From ce77595a35d3ce817c05b5c10b5659e015b21643 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Wed, 24 Feb 2016 15:13:07 -0500 Subject: [PATCH 01/64] Added shader program and window initialization (based on OpenGL tutorial skeleton codes --- .gitignore | 5 +- CMakeLists.txt | 15 +++--- src/camera/camera.cpp | 1 + src/main.cpp | 40 ++++++++++++++ src/main.hpp | 16 ++++++ src/program.cpp | 93 +++++++++++++++++++++++++++++++++ src/program.hpp | 70 +++++++++++++++++++++++++ src/scene/scene.hpp | 2 +- src/shader.cpp | 118 ++++++++++++++++++++++++++++++++++++++++++ src/shader.hpp | 75 +++++++++++++++++++++++++++ src/utility.cpp | 3 ++ src/utility.hpp | 4 ++ src/viewer/viewer.cpp | 2 +- src/viewer/viewer.hpp | 4 -- 14 files changed, 434 insertions(+), 14 deletions(-) create mode 100644 src/program.cpp create mode 100644 src/program.hpp create mode 100644 src/shader.cpp create mode 100644 src/shader.hpp create mode 100644 src/utility.cpp create mode 100644 src/utility.hpp diff --git a/.gitignore b/.gitignore index f3219dd9..0effb0b4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,9 @@ -Thanda.xcodeproj -Thanda.build +Korra.xcodeproj +Korra.build DerivedData CMakeScripts CMakeFiles CMakeCache.txt cmake_install.cmake CMakeLists.txt.user +build*/ diff --git a/CMakeLists.txt b/CMakeLists.txt index 696f9493..7f33a36f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,11 +1,11 @@ ################## -# Thanda # +# Korra # ################## # credit - base CMake config : Yining Karl Li , edited CMake config: Akshay Shah & Debanshu Singh #name your project -project(Thanda) +project(Korra) cmake_minimum_required(VERSION 2.8) # set creates a variable @@ -69,11 +69,11 @@ elseif(WIN32) endif() if(MSVC) - set(COMPILER_FLAGS + set(COMPILER_FLAGS CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE - CMAKE_C_FLAGS + CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE ) @@ -81,6 +81,9 @@ endif() # Add source files you want to compile (.cpp) set(CORE_SRC + src/utility.cpp + src/shader.cpp + src/program.cpp src/main.cpp src/camera/camera.cpp src/viewer/viewer.cpp @@ -89,5 +92,5 @@ set(CORE_SRC src/geom/geom.cpp ) -add_executable(Thanda ${CORE_SRC}) -target_link_libraries(Thanda ${CORE_LIBS}) \ No newline at end of file +add_executable(Korra ${CORE_SRC}) +target_link_libraries(Korra ${CORE_LIBS}) diff --git a/src/camera/camera.cpp b/src/camera/camera.cpp index fb246b12..d0b5511a 100644 --- a/src/camera/camera.cpp +++ b/src/camera/camera.cpp @@ -4,3 +4,4 @@ // #include "camera.hpp" +#include "../geom/geom.hpp" diff --git a/src/main.cpp b/src/main.cpp index 2a00d1e4..d709ccd5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,11 +1,51 @@ +#include + #include "main.hpp" using namespace std; +// ---------------------- +// Constants +// ---------------------- + +const glm::vec2 SCREEN_SIZE(800, 600); + +// ---------------------- +// Globals +// ---------------------- + +GLFWwindow* gWindow = NULL; + int main() { + // Initialise GLFW + if( !glfwInit() ) + { + fprintf( stderr, "Failed to initialize GLFW\n" ); + getchar(); + return -1; + } + + glfwWindowHint(GLFW_SAMPLES, 4); // antiliasing 4 samples + glfwWindowHint(GLFW_RESIZABLE,GL_FALSE); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Get version 3.3 + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Make sure with don't use old profile + + // Open a window and create its OpenGL context + gWindow = glfwCreateWindow( 1024, 768, "Playground", NULL, NULL); + if( gWindow == NULL ){ + fprintf( stderr, "Failed to open GLFW window.\n" ); + getchar(); + glfwTerminate(); + return -1; + } + + glfwMakeContextCurrent(gWindow); + cout << "Hello world 2" << endl; return 0; } diff --git a/src/main.hpp b/src/main.hpp index fe1f64d5..6832b250 100644 --- a/src/main.hpp +++ b/src/main.hpp @@ -5,5 +5,21 @@ #ifndef main_hpp #define main_hpp +// GL libraries + +#include +#include +#include + +// C++ libraries + +#include + +// Custom + +#include "program.hpp" +#include "utility.hpp" + + #endif /* main_hpp */ diff --git a/src/program.cpp b/src/program.cpp new file mode 100644 index 00000000..6fe03e38 --- /dev/null +++ b/src/program.cpp @@ -0,0 +1,93 @@ +/* + tdogl::Program + + Copyright 2012 Thomas Dalling - http://tomdalling.com/ + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#include "program.hpp" +#include + +using namespace tdogl; + +Program::Program(const std::vector& shaders) : + _object(0) +{ + if(shaders.size() <= 0) + throw std::runtime_error("No shaders were provided to create the program"); + + //create the program object + _object = glCreateProgram(); + if(_object == 0) + throw std::runtime_error("glCreateProgram failed"); + + //attach all the shaders + for(unsigned i = 0; i < shaders.size(); ++i) + glAttachShader(_object, shaders[i].object()); + + //link the shaders together + glLinkProgram(_object); + + //detach all the shaders + for(unsigned i = 0; i < shaders.size(); ++i) + glDetachShader(_object, shaders[i].object()); + + //throw exception if linking failed + GLint status; + glGetProgramiv(_object, GL_LINK_STATUS, &status); + if (status == GL_FALSE) { + std::string msg("Program linking failure: "); + + GLint infoLogLength; + glGetProgramiv(_object, GL_INFO_LOG_LENGTH, &infoLogLength); + char* strInfoLog = new char[infoLogLength + 1]; + glGetProgramInfoLog(_object, infoLogLength, NULL, strInfoLog); + msg += strInfoLog; + delete[] strInfoLog; + + glDeleteProgram(_object); _object = 0; + throw std::runtime_error(msg); + } +} + +Program::~Program() { + //might be 0 if ctor fails by throwing exception + if(_object != 0) glDeleteProgram(_object); +} + +GLuint Program::object() const { + return _object; +} + +GLint Program::attrib(const GLchar* attribName) const { + if(!attribName) + throw std::runtime_error("attribName was NULL"); + + GLint attrib = glGetAttribLocation(_object, attribName); + if(attrib == -1) + throw std::runtime_error(std::string("Program attribute not found: ") + attribName); + + return attrib; +} + +GLint Program::uniform(const GLchar* uniformName) const { + if(!uniformName) + throw std::runtime_error("uniformName was NULL"); + + GLint uniform = glGetUniformLocation(_object, uniformName); + if(uniform == -1) + throw std::runtime_error(std::string("Program uniform not found: ") + uniformName); + + return uniform; +} diff --git a/src/program.hpp b/src/program.hpp new file mode 100644 index 00000000..537043cc --- /dev/null +++ b/src/program.hpp @@ -0,0 +1,70 @@ +/* + tdogl::Program + + Copyright 2012 Thomas Dalling - http://tomdalling.com/ + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#pragma once + +#include "shader.hpp" +#include + +namespace tdogl { + + /** + Represents an OpenGL program made by linking shaders. + */ + class Program { + public: + /** + Creates a program by linking a list of tdogl::Shader objects + + @param shaders The shaders to link together to make the program + + @throws std::exception if an error occurs. + + @see tdogl::Shader + */ + Program(const std::vector& shaders); + ~Program(); + + + /** + @result The program's object id, as returned from glCreateProgram + */ + GLuint object() const; + + + /** + @result The attribute index for the given name, as returned from glGetAttribLocation. + */ + GLint attrib(const GLchar* attribName) const; + + + /** + @result The uniform index for the given name, as returned from glGetUniformLocation. + */ + GLint uniform(const GLchar* uniformName) const; + + + private: + GLuint _object; + + //copying disabled + Program(const Program&); + const Program& operator=(const Program&); + }; + +} diff --git a/src/scene/scene.hpp b/src/scene/scene.hpp index 84022a43..3d6723cf 100644 --- a/src/scene/scene.hpp +++ b/src/scene/scene.hpp @@ -1,3 +1,3 @@ // // scene.hpp -// Thanda \ No newline at end of file +// Thanda diff --git a/src/shader.cpp b/src/shader.cpp new file mode 100644 index 00000000..e34390b8 --- /dev/null +++ b/src/shader.cpp @@ -0,0 +1,118 @@ +/* + tdogl::Shader + + Copyright 2012 Thomas Dalling - http://tomdalling.com/ + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#include "shader.hpp" +#include +#include +#include +#include +#include + +using namespace tdogl; + +Shader::Shader(const std::string& shaderCode, GLenum shaderType) : + _object(0), + _refCount(NULL) +{ + //create the shader object + _object = glCreateShader(shaderType); + if(_object == 0) + throw std::runtime_error("glCreateShader failed"); + + //set the source code + const char* code = shaderCode.c_str(); + glShaderSource(_object, 1, (const GLchar**)&code, NULL); + + //compile + glCompileShader(_object); + + //throw exception if compile error occurred + GLint status; + glGetShaderiv(_object, GL_COMPILE_STATUS, &status); + if (status == GL_FALSE) { + std::string msg("Compile failure in shader:\n"); + + GLint infoLogLength; + glGetShaderiv(_object, GL_INFO_LOG_LENGTH, &infoLogLength); + char* strInfoLog = new char[infoLogLength + 1]; + glGetShaderInfoLog(_object, infoLogLength, NULL, strInfoLog); + msg += strInfoLog; + delete[] strInfoLog; + + glDeleteShader(_object); _object = 0; + throw std::runtime_error(msg); + } + + _refCount = new unsigned; + *_refCount = 1; +} + +Shader::Shader(const Shader& other) : + _object(other._object), + _refCount(other._refCount) +{ + _retain(); +} + +Shader::~Shader() { + //_refCount will be NULL if constructor failed and threw an exception + if(_refCount) _release(); +} + +GLuint Shader::object() const { + return _object; +} + +Shader& Shader::operator = (const Shader& other) { + _release(); + _object = other._object; + _refCount = other._refCount; + _retain(); + return *this; +} + +Shader Shader::shaderFromFile(const std::string& filePath, GLenum shaderType) { + //open file + std::ifstream f; + f.open(filePath.c_str(), std::ios::in | std::ios::binary); + if(!f.is_open()){ + throw std::runtime_error(std::string("Failed to open file: ") + filePath); + } + + //read whole file into stringstream buffer + std::stringstream buffer; + buffer << f.rdbuf(); + + //return new shader + Shader shader(buffer.str(), shaderType); + return shader; +} + +void Shader::_retain() { + assert(_refCount); + *_refCount += 1; +} + +void Shader::_release() { + assert(_refCount && *_refCount > 0); + *_refCount -= 1; + if(*_refCount == 0){ + glDeleteShader(_object); _object = 0; + delete _refCount; _refCount = NULL; + } +} diff --git a/src/shader.hpp b/src/shader.hpp new file mode 100644 index 00000000..4a7c64f6 --- /dev/null +++ b/src/shader.hpp @@ -0,0 +1,75 @@ +/* + tdogl::Shader + + Copyright 2012 Thomas Dalling - http://tomdalling.com/ + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + */ + +#pragma once + +#include +#include + +namespace tdogl { + + /** + Represents a compiled OpenGL shader. + */ + class Shader { + public: + + /** + Creates a shader from a text file. + + @param filePath The path to the text file containing the shader source. + @param shaderType Same as the argument to glCreateShader. For example GL_VERTEX_SHADER + or GL_FRAGMENT_SHADER. + + @throws std::exception if an error occurs. + */ + static Shader shaderFromFile(const std::string& filePath, GLenum shaderType); + + + /** + Creates a shader from a string of shader source code. + + @param shaderCode The source code for the shader. + @param shaderType Same as the argument to glCreateShader. For example GL_VERTEX_SHADER + or GL_FRAGMENT_SHADER. + + @throws std::exception if an error occurs. + */ + Shader(const std::string& shaderCode, GLenum shaderType); + + + /** + @result The shader's object ID, as returned from glCreateShader + */ + GLuint object() const; + + // tdogl::Shader objects can be copied and assigned because they are reference counted + // like a shared pointer + Shader(const Shader& other); + Shader& operator =(const Shader& other); + ~Shader(); + + private: + GLuint _object; + unsigned* _refCount; + + void _retain(); + void _release(); + }; + +} diff --git a/src/utility.cpp b/src/utility.cpp new file mode 100644 index 00000000..5523ac1d --- /dev/null +++ b/src/utility.cpp @@ -0,0 +1,3 @@ +#include "utility.hpp" + + diff --git a/src/utility.hpp b/src/utility.hpp new file mode 100644 index 00000000..35c732bb --- /dev/null +++ b/src/utility.hpp @@ -0,0 +1,4 @@ +#ifndef UTILITY_HPP +#define UTILITY_HPP + +#endif UTILITY_HPP diff --git a/src/viewer/viewer.cpp b/src/viewer/viewer.cpp index 60d1868a..dbe52e7e 100644 --- a/src/viewer/viewer.cpp +++ b/src/viewer/viewer.cpp @@ -1,5 +1,5 @@ // // viewer.cpp -// Thanda #include "viewer.hpp" + diff --git a/src/viewer/viewer.hpp b/src/viewer/viewer.hpp index abb78a17..3b380e09 100644 --- a/src/viewer/viewer.hpp +++ b/src/viewer/viewer.hpp @@ -1,7 +1,3 @@ -// -// viewer.hpp -// Thanda - #ifndef viewer_hpp #define viewer_hpp From 3d048cbe3b6578a735ffd052decb7f9fd3e58f5f Mon Sep 17 00:00:00 2001 From: Trung Le Date: Fri, 26 Feb 2016 13:35:08 -0500 Subject: [PATCH 02/64] Fixed CmakeLists.txt typo to properly include OSX frameworks --- CMakeLists.txt | 14 +- nuparu/include/GLFW/COPYING.txt | 22 - nuparu/include/GLFW/glfw3.h | 1684 ++++++++++++++++++++--------- nuparu/include/GLFW/glfw3native.h | 242 +++-- nuparu/lib/osx/libglfw3.a | Bin 169936 -> 176728 bytes src/main.cpp | 32 +- 6 files changed, 1381 insertions(+), 613 deletions(-) delete mode 100644 nuparu/include/GLFW/COPYING.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f33a36f..6a1af131 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,10 +50,14 @@ set(CORE_LIBS ${GLFW_LIBRARY} ${GLUT_LIBRARY} ${GLEW_LIBRARY} ${JSONCPP} ${OPENG # OSX-specific hacks/fixes if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - #Link IOKit because this is where we get GL stuff for OSX - set(COREVIDEO "-framework CoreVideo") - set(IOKIT "-framework IOKit") - set(CORELIBS ${CORELIBS} ${IOKIT} ${COREVIDEO}) + find_library(COCOA Cocoa) + find_library(COREVIDEO CoreVideo) + find_library(IOKIT IOKit) + find_library(COREFOUNDATION CoreFoundation) + find_library(CARBON_FRAMEWORK Carbon) + + set(CORE_LIBS ${CORE_LIBS} "-framework Cocoa -framework IOKit -framework CoreFoundation -framework CoreVideo -framework Carbon") + endif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") # Linux specific hacks/fixes @@ -82,8 +86,6 @@ endif() # Add source files you want to compile (.cpp) set(CORE_SRC src/utility.cpp - src/shader.cpp - src/program.cpp src/main.cpp src/camera/camera.cpp src/viewer/viewer.cpp diff --git a/nuparu/include/GLFW/COPYING.txt b/nuparu/include/GLFW/COPYING.txt deleted file mode 100644 index b30c7015..00000000 --- a/nuparu/include/GLFW/COPYING.txt +++ /dev/null @@ -1,22 +0,0 @@ -Copyright (c) 2002-2006 Marcus Geelnard -Copyright (c) 2006-2010 Camilla Berglund - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any damages -arising from the use of this software. - -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it -freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not - claim that you wrote the original software. If you use this software - in a product, an acknowledgment in the product documentation would - be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and must not - be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source - distribution. - diff --git a/nuparu/include/GLFW/glfw3.h b/nuparu/include/GLFW/glfw3.h index 009fa755..68d005dd 100644 --- a/nuparu/include/GLFW/glfw3.h +++ b/nuparu/include/GLFW/glfw3.h @@ -1,5 +1,5 @@ /************************************************************************* - * GLFW 3.1 - www.glfw.org + * GLFW 3.2 - www.glfw.org * A library for OpenGL, window and input *------------------------------------------------------------------------ * Copyright (c) 2002-2006 Marcus Geelnard @@ -38,32 +38,45 @@ extern "C" { * Doxygen documentation *************************************************************************/ -/*! @defgroup context Context handling +/*! @file glfw3.h + * @brief The header of the GLFW 3 API. * - * This is the reference documentation for context related functions. For more - * information, see the @ref context. + * This is the header file of the GLFW 3 API. It defines all its types and + * declares all its functions. + * + * For more information about how to use this file, see @ref build_include. + */ +/*! @defgroup context Context reference + * + * This is the reference documentation for OpenGL and OpenGL ES context related + * functions. For more task-oriented information, see the @ref context_guide. + */ +/*! @defgroup vulkan Vulkan reference + * + * This is the reference documentation for Vulkan related functions and types. + * For more task-oriented information, see the @ref vulkan_guide. */ -/*! @defgroup init Initialization, version and errors +/*! @defgroup init Initialization, version and error reference * * This is the reference documentation for initialization and termination of - * the library, version management and error handling. For more information, - * see the @ref intro. + * the library, version management and error handling. For more task-oriented + * information, see the @ref intro_guide. */ -/*! @defgroup input Input handling +/*! @defgroup input Input reference * * This is the reference documentation for input related functions and types. - * For more information, see the @ref input. + * For more task-oriented information, see the @ref input_guide. */ -/*! @defgroup monitor Monitor handling +/*! @defgroup monitor Monitor reference * * This is the reference documentation for monitor related functions and types. - * For more information, see the @ref monitor. + * For more task-oriented information, see the @ref monitor_guide. */ -/*! @defgroup window Window handling +/*! @defgroup window Window reference * * This is the reference documentation for window related functions and types, - * including creation, deletion and event polling. For more information, see - * the @ref window. + * including creation, deletion and event polling. For more task-oriented + * information, see the @ref window_guide. */ @@ -111,7 +124,7 @@ extern "C" { /* Include the chosen client API headers. */ -#if defined(__APPLE_CC__) +#if defined(__APPLE__) #if defined(GLFW_INCLUDE_GLCOREARB) #include #if defined(GLFW_INCLUDE_GLEXT) @@ -142,13 +155,15 @@ extern "C" { #elif defined(GLFW_INCLUDE_ES3) #include #if defined(GLFW_INCLUDE_GLEXT) - #include + #include #endif #elif defined(GLFW_INCLUDE_ES31) #include #if defined(GLFW_INCLUDE_GLEXT) - #include + #include #endif + #elif defined(GLFW_INCLUDE_VULKAN) + #include #elif !defined(GLFW_INCLUDE_NONE) #include #if defined(GLFW_INCLUDE_GLEXT) @@ -165,7 +180,7 @@ extern "C" { * version of the GLFW library. _GLFW_BUILD_DLL is defined by the GLFW * configuration header when compiling the DLL version of the library. */ - #error "You may not have both GLFW_DLL and _GLFW_BUILD_DLL defined" + #error "You must not have both GLFW_DLL and _GLFW_BUILD_DLL defined" #endif /* GLFWAPI is used to declare public API functions for export @@ -204,14 +219,32 @@ extern "C" { * backward-compatible. * @ingroup init */ -#define GLFW_VERSION_MINOR 1 +#define GLFW_VERSION_MINOR 2 /*! @brief The revision number of the GLFW library. * * This is incremented when a bug fix release is made that does not contain any * API changes. * @ingroup init */ -#define GLFW_VERSION_REVISION 1 +#define GLFW_VERSION_REVISION 0 +/*! @} */ + +/*! @name Boolean values + * @{ */ +/*! @brief One. + * + * One. Seriously. You don't _need_ to use this symbol in your code. It's + * just semantic sugar for the number 1. You can use `1` or `true` or `_True` + * or `GL_TRUE` or whatever you want. + */ +#define GLFW_TRUE 1 +/*! @brief Zero. + * + * Zero. Seriously. You don't _need_ to use this symbol in your code. It's + * just just semantic sugar for the number 0. You can use `0` or `false` or + * `_False` or `GL_FALSE` or whatever you want. + */ +#define GLFW_FALSE 0 /*! @} */ /*! @name Key and button actions @@ -388,6 +421,7 @@ extern "C" { #define GLFW_KEY_RIGHT_ALT 346 #define GLFW_KEY_RIGHT_SUPER 347 #define GLFW_KEY_MENU 348 + #define GLFW_KEY_LAST GLFW_KEY_MENU /*! @} */ @@ -467,12 +501,11 @@ extern "C" { * @{ */ /*! @brief GLFW has not been initialized. * - * This occurs if a GLFW function was called that may not be called unless the + * This occurs if a GLFW function was called that must not be called unless the * library is [initialized](@ref intro_init). * - * @par Analysis - * Application programmer error. Initialize GLFW before calling any function - * that requires initialization. + * @analysis Application programmer error. Initialize GLFW before calling any + * function that requires initialization. */ #define GLFW_NOT_INITIALIZED 0x00010001 /*! @brief No context is current for this thread. @@ -481,9 +514,8 @@ extern "C" { * current OpenGL or OpenGL ES context but no context is current on the calling * thread. One such function is @ref glfwSwapInterval. * - * @par Analysis - * Application programmer error. Ensure a context is current before calling - * functions that require a current context. + * @analysis Application programmer error. Ensure a context is current before + * calling functions that require a current context. */ #define GLFW_NO_CURRENT_CONTEXT 0x00010002 /*! @brief One of the arguments to the function was an invalid enum value. @@ -492,8 +524,7 @@ extern "C" { * requesting [GLFW_RED_BITS](@ref window_hints_fb) with @ref * glfwGetWindowAttrib. * - * @par Analysis - * Application programmer error. Fix the offending call. + * @analysis Application programmer error. Fix the offending call. */ #define GLFW_INVALID_ENUM 0x00010003 /*! @brief One of the arguments to the function was an invalid value. @@ -504,37 +535,31 @@ extern "C" { * Requesting a valid but unavailable OpenGL or OpenGL ES version will instead * result in a @ref GLFW_VERSION_UNAVAILABLE error. * - * @par Analysis - * Application programmer error. Fix the offending call. + * @analysis Application programmer error. Fix the offending call. */ #define GLFW_INVALID_VALUE 0x00010004 /*! @brief A memory allocation failed. * * A memory allocation failed. * - * @par Analysis - * A bug in GLFW or the underlying operating system. Report the bug to our - * [issue tracker](https://github.com/glfw/glfw/issues). + * @analysis A bug in GLFW or the underlying operating system. Report the bug + * to our [issue tracker](https://github.com/glfw/glfw/issues). */ #define GLFW_OUT_OF_MEMORY 0x00010005 -/*! @brief GLFW could not find support for the requested client API on the - * system. +/*! @brief GLFW could not find support for the requested API on the system. * - * GLFW could not find support for the requested client API on the system. If - * emitted by functions other than @ref glfwCreateWindow, no supported client - * API was found. + * GLFW could not find support for the requested API on the system. * - * @par Analysis - * The installed graphics driver does not support the requested client API, or - * does not support it via the chosen context creation backend. Below are - * a few examples. + * @analysis The installed graphics driver does not support the requested + * API, or does not support it via the chosen context creation backend. + * Below are a few examples. * * @par * Some pre-installed Windows graphics drivers do not support OpenGL. AMD only - * supports OpenGL ES via EGL, while Nvidia and Intel only supports it via + * supports OpenGL ES via EGL, while Nvidia and Intel only support it via * a WGL or GLX extension. OS X does not provide OpenGL ES at all. The Mesa * EGL, OpenGL and OpenGL ES libraries do not interface with the Nvidia binary - * driver. + * driver. Older graphics drivers do not support Vulkan. */ #define GLFW_API_UNAVAILABLE 0x00010006 /*! @brief The requested OpenGL or OpenGL ES version is not available. @@ -542,10 +567,9 @@ extern "C" { * The requested OpenGL or OpenGL ES version (including any requested context * or framebuffer hints) is not available on this machine. * - * @par Analysis - * The machine does not support your requirements. If your application is - * sufficiently flexible, downgrade your requirements and try again. - * Otherwise, inform the user that their machine does not match your + * @analysis The machine does not support your requirements. If your + * application is sufficiently flexible, downgrade your requirements and try + * again. Otherwise, inform the user that their machine does not match your * requirements. * * @par @@ -561,10 +585,9 @@ extern "C" { * A platform-specific error occurred that does not match any of the more * specific categories. * - * @par Analysis - * A bug or configuration error in GLFW, the underlying operating system or - * its drivers, or a lack of required resources. Report the issue to our - * [issue tracker](https://github.com/glfw/glfw/issues). + * @analysis A bug or configuration error in GLFW, the underlying operating + * system or its drivers, or a lack of required resources. Report the issue to + * our [issue tracker](https://github.com/glfw/glfw/issues). */ #define GLFW_PLATFORM_ERROR 0x00010008 /*! @brief The requested format is not supported or available. @@ -575,8 +598,7 @@ extern "C" { * If emitted when querying the clipboard, the contents of the clipboard could * not be converted to the requested format. * - * @par Analysis - * If emitted during window creation, one or more + * @analysis If emitted during window creation, one or more * [hard constraints](@ref window_hints_hard) did not match any of the * available pixel formats. If your application is sufficiently flexible, * downgrade your requirements and try again. Otherwise, inform the user that @@ -587,6 +609,14 @@ extern "C" { * the user, as appropriate. */ #define GLFW_FORMAT_UNAVAILABLE 0x00010009 +/*! @brief The specified window does not have an OpenGL or OpenGL ES context. + * + * A window that does not have an OpenGL or OpenGL ES context was passed to + * a function that requires it to have one. + * + * @analysis Application programmer error. Fix the offending call. + */ +#define GLFW_NO_WINDOW_CONTEXT 0x0001000A /*! @} */ #define GLFW_FOCUSED 0x00020001 @@ -596,6 +626,7 @@ extern "C" { #define GLFW_DECORATED 0x00020005 #define GLFW_AUTO_ICONIFY 0x00020006 #define GLFW_FLOATING 0x00020007 +#define GLFW_MAXIMIZED 0x00020008 #define GLFW_RED_BITS 0x00021001 #define GLFW_GREEN_BITS 0x00021002 @@ -623,7 +654,9 @@ extern "C" { #define GLFW_OPENGL_DEBUG_CONTEXT 0x00022007 #define GLFW_OPENGL_PROFILE 0x00022008 #define GLFW_CONTEXT_RELEASE_BEHAVIOR 0x00022009 +#define GLFW_CONTEXT_NO_ERROR 0x0002200A +#define GLFW_NO_API 0 #define GLFW_OPENGL_API 0x00030001 #define GLFW_OPENGL_ES_API 0x00030002 @@ -701,14 +734,37 @@ extern "C" { * Generic function pointer used for returning client API function pointers * without forcing a cast from a regular pointer. * + * @sa @ref context_glext + * @sa glfwGetProcAddress + * + * @since Added in version 3.0. + * @ingroup context */ typedef void (*GLFWglproc)(void); +/*! @brief Vulkan API function pointer type. + * + * Generic function pointer used for returning Vulkan API function pointers + * without forcing a cast from a regular pointer. + * + * @sa @ref vulkan_proc + * @sa glfwGetInstanceProcAddress + * + * @since Added in version 3.2. + * + * @ingroup vulkan + */ +typedef void (*GLFWvkproc)(void); + /*! @brief Opaque monitor object. * * Opaque monitor object. * + * @see @ref monitor_object + * + * @since Added in version 3.0. + * * @ingroup monitor */ typedef struct GLFWmonitor GLFWmonitor; @@ -717,6 +773,10 @@ typedef struct GLFWmonitor GLFWmonitor; * * Opaque window object. * + * @see @ref window_object + * + * @since Added in version 3.0. + * * @ingroup window */ typedef struct GLFWwindow GLFWwindow; @@ -725,6 +785,10 @@ typedef struct GLFWwindow GLFWwindow; * * Opaque cursor object. * + * @see @ref cursor_object + * + * @since Added in version 3.1. + * * @ingroup cursor */ typedef struct GLFWcursor GLFWcursor; @@ -736,8 +800,11 @@ typedef struct GLFWcursor GLFWcursor; * @param[in] error An [error code](@ref errors). * @param[in] description A UTF-8 encoded string describing the error. * + * @sa @ref error_handling * @sa glfwSetErrorCallback * + * @since Added in version 3.0. + * * @ingroup init */ typedef void (* GLFWerrorfun)(int,const char*); @@ -752,8 +819,11 @@ typedef void (* GLFWerrorfun)(int,const char*); * @param[in] ypos The new y-coordinate, in screen coordinates, of the * upper-left corner of the client area of the window. * + * @sa @ref window_pos * @sa glfwSetWindowPosCallback * + * @since Added in version 3.0. + * * @ingroup window */ typedef void (* GLFWwindowposfun)(GLFWwindow*,int,int); @@ -766,8 +836,12 @@ typedef void (* GLFWwindowposfun)(GLFWwindow*,int,int); * @param[in] width The new width, in screen coordinates, of the window. * @param[in] height The new height, in screen coordinates, of the window. * + * @sa @ref window_size * @sa glfwSetWindowSizeCallback * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. + * * @ingroup window */ typedef void (* GLFWwindowsizefun)(GLFWwindow*,int,int); @@ -778,8 +852,12 @@ typedef void (* GLFWwindowsizefun)(GLFWwindow*,int,int); * * @param[in] window The window that the user attempted to close. * + * @sa @ref window_close * @sa glfwSetWindowCloseCallback * + * @since Added in version 2.5. + * @glfw3 Added window handle parameter. + * * @ingroup window */ typedef void (* GLFWwindowclosefun)(GLFWwindow*); @@ -790,8 +868,12 @@ typedef void (* GLFWwindowclosefun)(GLFWwindow*); * * @param[in] window The window whose content needs to be refreshed. * + * @sa @ref window_refresh * @sa glfwSetWindowRefreshCallback * + * @since Added in version 2.5. + * @glfw3 Added window handle parameter. + * * @ingroup window */ typedef void (* GLFWwindowrefreshfun)(GLFWwindow*); @@ -801,11 +883,14 @@ typedef void (* GLFWwindowrefreshfun)(GLFWwindow*); * This is the function signature for window focus callback functions. * * @param[in] window The window that gained or lost input focus. - * @param[in] focused `GL_TRUE` if the window was given input focus, or - * `GL_FALSE` if it lost it. + * @param[in] focused `GLFW_TRUE` if the window was given input focus, or + * `GLFW_FALSE` if it lost it. * + * @sa @ref window_focus * @sa glfwSetWindowFocusCallback * + * @since Added in version 3.0. + * * @ingroup window */ typedef void (* GLFWwindowfocusfun)(GLFWwindow*,int); @@ -816,11 +901,14 @@ typedef void (* GLFWwindowfocusfun)(GLFWwindow*,int); * functions. * * @param[in] window The window that was iconified or restored. - * @param[in] iconified `GL_TRUE` if the window was iconified, or `GL_FALSE` - * if it was restored. + * @param[in] iconified `GLFW_TRUE` if the window was iconified, or + * `GLFW_FALSE` if it was restored. * + * @sa @ref window_iconify * @sa glfwSetWindowIconifyCallback * + * @since Added in version 3.0. + * * @ingroup window */ typedef void (* GLFWwindowiconifyfun)(GLFWwindow*,int); @@ -834,8 +922,11 @@ typedef void (* GLFWwindowiconifyfun)(GLFWwindow*,int); * @param[in] width The new width, in pixels, of the framebuffer. * @param[in] height The new height, in pixels, of the framebuffer. * + * @sa @ref window_fbsize * @sa glfwSetFramebufferSizeCallback * + * @since Added in version 3.0. + * * @ingroup window */ typedef void (* GLFWframebuffersizefun)(GLFWwindow*,int,int); @@ -851,8 +942,12 @@ typedef void (* GLFWframebuffersizefun)(GLFWwindow*,int,int); * @param[in] mods Bit field describing which [modifier keys](@ref mods) were * held down. * + * @sa @ref input_mouse_button * @sa glfwSetMouseButtonCallback * + * @since Added in version 1.0. + * @glfw3 Added window handle and modifier mask parameters. + * * @ingroup input */ typedef void (* GLFWmousebuttonfun)(GLFWwindow*,int,int,int); @@ -862,11 +957,16 @@ typedef void (* GLFWmousebuttonfun)(GLFWwindow*,int,int,int); * This is the function signature for cursor position callback functions. * * @param[in] window The window that received the event. - * @param[in] xpos The new x-coordinate, in screen coordinates, of the cursor. - * @param[in] ypos The new y-coordinate, in screen coordinates, of the cursor. + * @param[in] xpos The new cursor x-coordinate, relative to the left edge of + * the client area. + * @param[in] ypos The new cursor y-coordinate, relative to the top edge of the + * client area. * + * @sa @ref cursor_pos * @sa glfwSetCursorPosCallback * + * @since Added in version 3.0. Replaces `GLFWmouseposfun`. + * * @ingroup input */ typedef void (* GLFWcursorposfun)(GLFWwindow*,double,double); @@ -876,11 +976,14 @@ typedef void (* GLFWcursorposfun)(GLFWwindow*,double,double); * This is the function signature for cursor enter/leave callback functions. * * @param[in] window The window that received the event. - * @param[in] entered `GL_TRUE` if the cursor entered the window's client - * area, or `GL_FALSE` if it left it. + * @param[in] entered `GLFW_TRUE` if the cursor entered the window's client + * area, or `GLFW_FALSE` if it left it. * + * @sa @ref cursor_enter * @sa glfwSetCursorEnterCallback * + * @since Added in version 3.0. + * * @ingroup input */ typedef void (* GLFWcursorenterfun)(GLFWwindow*,int); @@ -893,8 +996,11 @@ typedef void (* GLFWcursorenterfun)(GLFWwindow*,int); * @param[in] xoffset The scroll offset along the x-axis. * @param[in] yoffset The scroll offset along the y-axis. * + * @sa @ref scrolling * @sa glfwSetScrollCallback * + * @since Added in version 3.0. Replaces `GLFWmousewheelfun`. + * * @ingroup input */ typedef void (* GLFWscrollfun)(GLFWwindow*,double,double); @@ -910,8 +1016,12 @@ typedef void (* GLFWscrollfun)(GLFWwindow*,double,double); * @param[in] mods Bit field describing which [modifier keys](@ref mods) were * held down. * + * @sa @ref input_key * @sa glfwSetKeyCallback * + * @since Added in version 1.0. + * @glfw3 Added window handle, scancode and modifier mask parameters. + * * @ingroup input */ typedef void (* GLFWkeyfun)(GLFWwindow*,int,int,int,int); @@ -923,8 +1033,12 @@ typedef void (* GLFWkeyfun)(GLFWwindow*,int,int,int,int); * @param[in] window The window that received the event. * @param[in] codepoint The Unicode code point of the character. * + * @sa @ref input_char * @sa glfwSetCharCallback * + * @since Added in version 2.4. + * @glfw3 Added window handle parameter. + * * @ingroup input */ typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int); @@ -941,8 +1055,11 @@ typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int); * @param[in] mods Bit field describing which [modifier keys](@ref mods) were * held down. * + * @sa @ref input_char * @sa glfwSetCharModsCallback * + * @since Added in version 3.1. + * * @ingroup input */ typedef void (* GLFWcharmodsfun)(GLFWwindow*,unsigned int,int); @@ -955,8 +1072,11 @@ typedef void (* GLFWcharmodsfun)(GLFWwindow*,unsigned int,int); * @param[in] count The number of dropped files. * @param[in] paths The UTF-8 encoded file and/or directory path names. * + * @sa @ref path_drop * @sa glfwSetDropCallback * + * @since Added in version 3.1. + * * @ingroup input */ typedef void (* GLFWdropfun)(GLFWwindow*,int,const char**); @@ -968,8 +1088,11 @@ typedef void (* GLFWdropfun)(GLFWwindow*,int,const char**); * @param[in] monitor The monitor that was connected or disconnected. * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. * + * @sa @ref monitor_event * @sa glfwSetMonitorCallback * + * @since Added in version 3.0. + * * @ingroup monitor */ typedef void (* GLFWmonitorfun)(GLFWmonitor*,int); @@ -978,6 +1101,12 @@ typedef void (* GLFWmonitorfun)(GLFWmonitor*,int); * * This describes a single video mode. * + * @sa @ref monitor_modes + * @sa glfwGetVideoMode glfwGetVideoModes + * + * @since Added in version 1.0. + * @glfw3 Added refresh rate member. + * * @ingroup monitor */ typedef struct GLFWvidmode @@ -1006,8 +1135,11 @@ typedef struct GLFWvidmode * * This describes the gamma ramp for a monitor. * + * @sa @ref monitor_gamma * @sa glfwGetGammaRamp glfwSetGammaRamp * + * @since Added in version 3.0. + * * @ingroup monitor */ typedef struct GLFWgammaramp @@ -1027,6 +1159,11 @@ typedef struct GLFWgammaramp } GLFWgammaramp; /*! @brief Image data. + * + * @sa @ref cursor_custom + * + * @since Added in version 2.1. + * @glfw3 Removed format and bytes-per-pixel members. */ typedef struct GLFWimage { @@ -1057,29 +1194,24 @@ typedef struct GLFWimage * succeeds, you should call @ref glfwTerminate before the application exits. * * Additional calls to this function after successful initialization but before - * termination will return `GL_TRUE` immediately. + * termination will return `GLFW_TRUE` immediately. * - * @return `GL_TRUE` if successful, or `GL_FALSE` if an + * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an * [error](@ref error_handling) occurred. * - * @remarks __OS X:__ This function will change the current directory of the + * @errors Possible errors include @ref GLFW_PLATFORM_ERROR. + * + * @remark @osx This function will change the current directory of the * application to the `Contents/Resources` subdirectory of the application's * bundle, if present. This can be disabled with a * [compile-time option](@ref compile_options_osx). * - * @remarks __X11:__ If the `LC_CTYPE` category of the current locale is set to - * `"C"` then the environment's locale will be applied to that category. This - * is done because character input will not function when `LC_CTYPE` is set to - * `"C"`. If another locale was set before this function was called, it will - * be left untouched. - * - * @par Thread Safety - * This function may only be called from the main thread. + * @thread_safety This function must only be called from the main thread. * * @sa @ref intro_init * @sa glfwTerminate * - * @since Added in GLFW 1.0. + * @since Added in version 1.0. * * @ingroup init */ @@ -1097,21 +1229,21 @@ GLFWAPI int glfwInit(void); * call this function, as it is called by @ref glfwInit before it returns * failure. * - * @remarks This function may be called before @ref glfwInit. + * @errors Possible errors include @ref GLFW_PLATFORM_ERROR. * - * @warning No window's context may be current on another thread when this - * function is called. + * @remark This function may be called before @ref glfwInit. * - * @par Reentrancy - * This function may not be called from a callback. + * @warning The contexts of any remaining windows must not be current on any + * other thread when this function is called. * - * @par Thread Safety - * This function may only be called from the main thread. + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref intro_init * @sa glfwInit * - * @since Added in GLFW 1.0. + * @since Added in version 1.0. * * @ingroup init */ @@ -1123,22 +1255,22 @@ GLFWAPI void glfwTerminate(void); * library. It is intended for when you are using GLFW as a shared library and * want to ensure that you are using the minimum required version. * - * Any or all of the version arguments may be `NULL`. This function always - * succeeds. + * Any or all of the version arguments may be `NULL`. * * @param[out] major Where to store the major version number, or `NULL`. * @param[out] minor Where to store the minor version number, or `NULL`. * @param[out] rev Where to store the revision number, or `NULL`. * - * @remarks This function may be called before @ref glfwInit. + * @errors None. * - * @par Thread Safety - * This function may be called from any thread. + * @remark This function may be called before @ref glfwInit. + * + * @thread_safety This function may be called from any thread. * * @sa @ref intro_version * @sa glfwGetVersionString * - * @since Added in GLFW 1.0. + * @since Added in version 1.0. * * @ingroup init */ @@ -1149,28 +1281,27 @@ GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev); * This function returns the compile-time generated * [version string](@ref intro_version_string) of the GLFW library binary. It * describes the version, platform, compiler and any platform-specific - * compile-time options. + * compile-time options. It should not be confused with the OpenGL or OpenGL + * ES version string, queried with `glGetString`. * * __Do not use the version string__ to parse the GLFW library version. The - * @ref glfwGetVersion function already provides the version of the running - * library binary. + * @ref glfwGetVersion function provides the version of the running library + * binary in numerical format. * - * This function always succeeds. + * @return The ASCII encoded GLFW version string. * - * @return The GLFW version string. + * @errors None. * - * @remarks This function may be called before @ref glfwInit. + * @remark This function may be called before @ref glfwInit. * - * @par Pointer Lifetime - * The returned string is static and compile-time generated. + * @pointer_lifetime The returned string is static and compile-time generated. * - * @par Thread Safety - * This function may be called from any thread. + * @thread_safety This function may be called from any thread. * * @sa @ref intro_version * @sa glfwGetVersion * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup init */ @@ -1196,14 +1327,15 @@ GLFWAPI const char* glfwGetVersionString(void); * callback. * @return The previously set callback, or `NULL` if no callback was set. * - * @remarks This function may be called before @ref glfwInit. + * @errors None. * - * @par Thread Safety - * This function may only be called from the main thread. + * @remark This function may be called before @ref glfwInit. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref error_handling * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup init */ @@ -1212,26 +1344,27 @@ GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun); /*! @brief Returns the currently connected monitors. * * This function returns an array of handles for all currently connected - * monitors. + * monitors. The primary monitor is always first in the returned array. If no + * monitors were found, this function returns `NULL`. * * @param[out] count Where to store the number of monitors in the returned * array. This is set to zero if an error occurred. - * @return An array of monitor handles, or `NULL` if an - * [error](@ref error_handling) occurred. + * @return An array of monitor handles, or `NULL` if no monitors were found or + * if an [error](@ref error_handling) occurred. * - * @par Pointer Lifetime - * The returned array is allocated and freed by GLFW. You should not free it - * yourself. It is guaranteed to be valid only until the monitor configuration - * changes or the library is terminated. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * - * @par Thread Safety - * This function may only be called from the main thread. + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is guaranteed to be valid only until the + * monitor configuration changes or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref monitor_monitors * @sa @ref monitor_event * @sa glfwGetPrimaryMonitor * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup monitor */ @@ -1240,18 +1373,22 @@ GLFWAPI GLFWmonitor** glfwGetMonitors(int* count); /*! @brief Returns the primary monitor. * * This function returns the primary monitor. This is usually the monitor - * where elements like the Windows task bar or the OS X menu bar is located. + * where elements like the task bar or global menu bar are located. * - * @return The primary monitor, or `NULL` if an [error](@ref error_handling) - * occurred. + * @return The primary monitor, or `NULL` if no monitors were found or if an + * [error](@ref error_handling) occurred. * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @remark The primary monitor is always first in the array returned by @ref + * glfwGetMonitors. * * @sa @ref monitor_monitors * @sa glfwGetMonitors * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup monitor */ @@ -1269,12 +1406,14 @@ GLFWAPI GLFWmonitor* glfwGetPrimaryMonitor(void); * @param[out] xpos Where to store the monitor x-coordinate, or `NULL`. * @param[out] ypos Where to store the monitor y-coordinate, or `NULL`. * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref monitor_properties * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup monitor */ @@ -1299,15 +1438,16 @@ GLFWAPI void glfwGetMonitorPos(GLFWmonitor* monitor, int* xpos, int* ypos); * @param[out] heightMM Where to store the height, in millimetres, of the * monitor's display area, or `NULL`. * - * @remarks __Windows:__ The OS calculates the returned physical size from the + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @remark @win32 calculates the returned physical size from the * current resolution and system DPI instead of querying the monitor EDID data. * - * @par Thread Safety - * This function may only be called from the main thread. + * @thread_safety This function must only be called from the main thread. * * @sa @ref monitor_properties * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup monitor */ @@ -1323,17 +1463,17 @@ GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* monitor, int* widthMM, int* * @return The UTF-8 encoded name of the monitor, or `NULL` if an * [error](@ref error_handling) occurred. * - * @par Pointer Lifetime - * The returned string is allocated and freed by GLFW. You should not free it - * yourself. It is valid until the specified monitor is disconnected or the - * library is terminated. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * - * @par Thread Safety - * This function may only be called from the main thread. + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified monitor is + * disconnected or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref monitor_properties * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup monitor */ @@ -1350,15 +1490,13 @@ GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* monitor); * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * - * @bug __X11:__ This callback is not yet called on monitor configuration - * changes. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * - * @par Thread Safety - * This function may only be called from the main thread. + * @thread_safety This function must only be called from the main thread. * * @sa @ref monitor_event * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup monitor */ @@ -1377,21 +1515,21 @@ GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun cbfun); * @return An array of video modes, or `NULL` if an * [error](@ref error_handling) occurred. * - * @par Pointer Lifetime - * The returned array is allocated and freed by GLFW. You should not free it - * yourself. It is valid until the specified monitor is disconnected, this - * function is called again for that monitor or the library is terminated. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. * - * @par Thread Safety - * This function may only be called from the main thread. + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified monitor is + * disconnected, this function is called again for that monitor or the library + * is terminated. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref monitor_modes * @sa glfwGetVideoMode * - * @since Added in GLFW 1.0. - * - * @par - * __GLFW 3:__ Changed to return an array of modes for a specific monitor. + * @since Added in version 1.0. + * @glfw3 Changed to return an array of modes for a specific monitor. * * @ingroup monitor */ @@ -1407,18 +1545,19 @@ GLFWAPI const GLFWvidmode* glfwGetVideoModes(GLFWmonitor* monitor, int* count); * @return The current mode of the monitor, or `NULL` if an * [error](@ref error_handling) occurred. * - * @par Pointer Lifetime - * The returned array is allocated and freed by GLFW. You should not free it - * yourself. It is valid until the specified monitor is disconnected or the - * library is terminated. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. * - * @par Thread Safety - * This function may only be called from the main thread. + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified monitor is + * disconnected or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref monitor_modes * @sa glfwGetVideoModes * - * @since Added in GLFW 3.0. Replaces `glfwGetDesktopMode`. + * @since Added in version 3.0. Replaces `glfwGetDesktopMode`. * * @ingroup monitor */ @@ -1433,12 +1572,14 @@ GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor); * @param[in] monitor The monitor whose gamma ramp to set. * @param[in] gamma The desired exponent. * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref monitor_gamma * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup monitor */ @@ -1452,18 +1593,19 @@ GLFWAPI void glfwSetGamma(GLFWmonitor* monitor, float gamma); * @return The current gamma ramp, or `NULL` if an * [error](@ref error_handling) occurred. * - * @par Pointer Lifetime - * The returned structure and its arrays are allocated and freed by GLFW. You - * should not free them yourself. They are valid until the specified monitor - * is disconnected, this function is called again for that monitor or the - * library is terminated. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. * - * @par Thread Safety - * This function may only be called from the main thread. + * @pointer_lifetime The returned structure and its arrays are allocated and + * freed by GLFW. You should not free them yourself. They are valid until the + * specified monitor is disconnected, this function is called again for that + * monitor or the library is terminated. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref monitor_gamma * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup monitor */ @@ -1478,20 +1620,22 @@ GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* monitor); * @param[in] monitor The monitor whose gamma ramp to set. * @param[in] ramp The gamma ramp to use. * - * @remarks Gamma ramp sizes other than 256 are not supported by all platforms + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark Gamma ramp sizes other than 256 are not supported by all platforms * or graphics hardware. * - * @remarks __Windows:__ The gamma ramp size must be 256. + * @remark @win32 The gamma ramp size must be 256. * - * @par Pointer Lifetime - * The specified gamma ramp is copied before this function returns. + * @pointer_lifetime The specified gamma ramp is copied before this function + * returns. * - * @par Thread Safety - * This function may only be called from the main thread. + * @thread_safety This function must only be called from the main thread. * * @sa @ref monitor_gamma * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup monitor */ @@ -1502,13 +1646,14 @@ GLFWAPI void glfwSetGammaRamp(GLFWmonitor* monitor, const GLFWgammaramp* ramp); * This function resets all window hints to their * [default values](@ref window_hints_values). * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref window_hints * @sa glfwWindowHint * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup window */ @@ -1521,20 +1666,26 @@ GLFWAPI void glfwDefaultWindowHints(void); * glfwWindowHint or @ref glfwDefaultWindowHints, or until the library is * terminated. * - * @param[in] target The [window hint](@ref window_hints) to set. - * @param[in] hint The new value of the window hint. + * This function does not check whether the specified hint values are valid. + * If you set hints to invalid values this will instead be reported by the next + * call to @ref glfwCreateWindow. * - * @par Thread Safety - * This function may only be called from the main thread. + * @param[in] hint The [window hint](@ref window_hints) to set. + * @param[in] value The new value of the window hint. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref window_hints * @sa glfwDefaultWindowHints * - * @since Added in GLFW 3.0. Replaces `glfwOpenWindowHint`. + * @since Added in version 3.0. Replaces `glfwOpenWindowHint`. * * @ingroup window */ -GLFWAPI void glfwWindowHint(int target, int hint); +GLFWAPI void glfwWindowHint(int hint, int value); /*! @brief Creates a window and its associated context. * @@ -1551,8 +1702,8 @@ GLFWAPI void glfwWindowHint(int target, int hint); * requested, as not all parameters and hints are * [hard constraints](@ref window_hints_hard). This includes the size of the * window, especially for full screen windows. To query the actual attributes - * of the created window, framebuffer and context, use queries like @ref - * glfwGetWindowAttrib and @ref glfwGetWindowSize. + * of the created window, framebuffer and context, see @ref + * glfwGetWindowAttrib, @ref glfwGetWindowSize and @ref glfwGetFramebufferSize. * * To create a full screen window, you need to specify the monitor the window * will cover. If no monitor is specified, windowed mode will be used. Unless @@ -1595,50 +1746,60 @@ GLFWAPI void glfwWindowHint(int target, int hint); * @return The handle of the created window, or `NULL` if an * [error](@ref error_handling) occurred. * - * @remarks __Windows:__ Window creation will fail if the Microsoft GDI - * software OpenGL implementation is the only one available. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM, @ref GLFW_INVALID_VALUE, @ref GLFW_API_UNAVAILABLE, @ref + * GLFW_VERSION_UNAVAILABLE, @ref GLFW_FORMAT_UNAVAILABLE and @ref + * GLFW_PLATFORM_ERROR. * - * @remarks __Windows:__ If the executable has an icon resource named - * `GLFW_ICON,` it will be set as the icon for the window. If no such icon is - * present, the `IDI_WINLOGO` icon will be used instead. + * @remark @win32 Window creation will fail if the Microsoft GDI software + * OpenGL implementation is the only one available. * - * @remarks __Windows:__ The context to share resources with may not be current - * on any other thread. + * @remark @win32 If the executable has an icon resource named `GLFW_ICON,` + * it will be set as the icon for the window. If no such icon is present, the + * `IDI_WINLOGO` icon will be used instead. * - * @remarks __OS X:__ The GLFW window has no icon, as it is not a document + * @remark @win32 The context to share resources with must not be current on + * any other thread. + * + * @remark @osx The GLFW window has no icon, as it is not a document * window, but the dock icon will be the same as the application bundle's icon. * For more information on bundles, see the * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/) * in the Mac Developer Library. * - * @remarks __OS X:__ The first time a window is created the menu bar is - * populated with common commands like Hide, Quit and About. The About entry - * opens a minimal about dialog with information from the application's bundle. - * The menu bar can be disabled with a + * @remark @osx The first time a window is created the menu bar is populated + * with common commands like Hide, Quit and About. The About entry opens + * a minimal about dialog with information from the application's bundle. The + * menu bar can be disabled with a * [compile-time option](@ref compile_options_osx). * - * @remarks __OS X:__ On OS X 10.10 and later the window frame will not be - * rendered at full resolution on Retina displays unless the - * `NSHighResolutionCapable` key is enabled in the application bundle's - * `Info.plist`. For more information, see + * @remark @osx On OS X 10.10 and later the window frame will not be rendered + * at full resolution on Retina displays unless the `NSHighResolutionCapable` + * key is enabled in the application bundle's `Info.plist`. For more + * information, see * [High Resolution Guidelines for OS X](https://developer.apple.com/library/mac/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html) - * in the Mac Developer Library. + * in the Mac Developer Library. The GLFW test and example programs use + * a custom `Info.plist` template for this, which can be found as + * `CMake/MacOSXBundleInfo.plist.in` in the source tree. * - * @remarks __X11:__ There is no mechanism for setting the window icon yet. + * @remark @x11 There is no mechanism for setting the window icon yet. * - * @remarks __X11:__ Some window managers will not respect the placement of + * @remark @x11 Some window managers will not respect the placement of * initially hidden windows. * - * @par Reentrancy - * This function may not be called from a callback. + * @remark @x11 Due to the asynchronous nature of X11, it may take a moment for + * a window to reach its requested state. This means you may not be able to + * query the final size, position or other attributes directly after window + * creation. * - * @par Thread Safety - * This function may only be called from the main thread. + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref window_creation * @sa glfwDestroyWindow * - * @since Added in GLFW 3.0. Replaces `glfwOpenWindow`. + * @since Added in version 3.0. Replaces `glfwOpenWindow`. * * @ingroup window */ @@ -1654,19 +1815,20 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, const char* title, G * * @param[in] window The window to destroy. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * * @note The context of the specified window must not be current on any other * thread when this function is called. * - * @par Reentrancy - * This function may not be called from a callback. + * @reentrancy This function must not be called from a callback. * - * @par Thread Safety - * This function may only be called from the main thread. + * @thread_safety This function must only be called from the main thread. * * @sa @ref window_creation * @sa glfwCreateWindow * - * @since Added in GLFW 3.0. Replaces `glfwCloseWindow`. + * @since Added in version 3.0. Replaces `glfwCloseWindow`. * * @ingroup window */ @@ -1679,12 +1841,14 @@ GLFWAPI void glfwDestroyWindow(GLFWwindow* window); * @param[in] window The window to query. * @return The value of the close flag. * - * @par Thread Safety - * This function may be called from any thread. Access is not synchronized. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. * * @sa @ref window_close * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup window */ @@ -1699,12 +1863,14 @@ GLFWAPI int glfwWindowShouldClose(GLFWwindow* window); * @param[in] window The window whose flag to change. * @param[in] value The new value. * - * @par Thread Safety - * This function may be called from any thread. Access is not synchronized. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. * * @sa @ref window_close * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup window */ @@ -1718,15 +1884,18 @@ GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* window, int value); * @param[in] window The window whose title to change. * @param[in] title The UTF-8 encoded window title. * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. * - * @sa @ref window_title + * @remark @osx The window title will not be updated until the next time you + * process events. * - * @since Added in GLFW 1.0. + * @thread_safety This function must only be called from the main thread. * - * @par - * __GLFW 3:__ Added window handle parameter. + * @sa @ref window_title + * + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. * * @ingroup window */ @@ -1746,13 +1915,15 @@ GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title); * @param[out] ypos Where to store the y-coordinate of the upper-left corner of * the client area, or `NULL`. * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref window_pos * @sa glfwSetWindowPos * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup window */ @@ -1774,16 +1945,16 @@ GLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos); * @param[in] xpos The x-coordinate of the upper-left corner of the client area. * @param[in] ypos The y-coordinate of the upper-left corner of the client area. * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref window_pos * @sa glfwGetWindowPos * - * @since Added in GLFW 1.0. - * - * @par - * __GLFW 3:__ Added window handle parameter. + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. * * @ingroup window */ @@ -1804,21 +1975,96 @@ GLFWAPI void glfwSetWindowPos(GLFWwindow* window, int xpos, int ypos); * @param[out] height Where to store the height, in screen coordinates, of the * client area, or `NULL`. * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref window_size * @sa glfwSetWindowSize * - * @since Added in GLFW 1.0. - * - * @par - * __GLFW 3:__ Added window handle parameter. + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. * * @ingroup window */ GLFWAPI void glfwGetWindowSize(GLFWwindow* window, int* width, int* height); +/*! @brief Sets the size limits of the specified window. + * + * This function sets the size limits of the client area of the specified + * window. If the window is full screen or not resizable, this function does + * nothing. + * + * The size limits are applied immediately and may cause the window to be + * resized. + * + * @param[in] window The window to set limits for. + * @param[in] minwidth The minimum width, in screen coordinates, of the client + * area, or `GLFW_DONT_CARE`. + * @param[in] minheight The minimum height, in screen coordinates, of the + * client area, or `GLFW_DONT_CARE`. + * @param[in] maxwidth The maximum width, in screen coordinates, of the client + * area, or `GLFW_DONT_CARE`. + * @param[in] maxheight The maximum height, in screen coordinates, of the + * client area, or `GLFW_DONT_CARE`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @remark If you set size limits and an aspect ratio that conflict, the + * results are undefined. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_sizelimits + * @sa glfwSetWindowAspectRatio + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight); + +/*! @brief Sets the aspect ratio of the specified window. + * + * This function sets the required aspect ratio of the client area of the + * specified window. If the window is full screen or not resizable, this + * function does nothing. + * + * The aspect ratio is specified as a numerator and a denominator and both + * values must be greater than zero. For example, the common 16:9 aspect ratio + * is specified as 16 and 9, respectively. + * + * If the numerator and denominator is set to `GLFW_DONT_CARE` then the aspect + * ratio limit is disabled. + * + * The aspect ratio is applied immediately and may cause the window to be + * resized. + * + * @param[in] window The window to set limits for. + * @param[in] numer The numerator of the desired aspect ratio, or + * `GLFW_DONT_CARE`. + * @param[in] denom The denominator of the desired aspect ratio, or + * `GLFW_DONT_CARE`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. + * + * @remark If you set size limits and an aspect ratio that conflict, the + * results are undefined. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_sizelimits + * @sa glfwSetWindowSizeLimits + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* window, int numer, int denom); + /*! @brief Sets the size of the client area of the specified window. * * This function sets the size, in screen coordinates, of the client area of @@ -1836,16 +2082,16 @@ GLFWAPI void glfwGetWindowSize(GLFWwindow* window, int* width, int* height); * @param[in] width The desired width of the specified window. * @param[in] height The desired height of the specified window. * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref window_size * @sa glfwGetWindowSize * - * @since Added in GLFW 1.0. - * - * @par - * __GLFW 3:__ Added window handle parameter. + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. * * @ingroup window */ @@ -1866,13 +2112,15 @@ GLFWAPI void glfwSetWindowSize(GLFWwindow* window, int width, int height); * @param[out] height Where to store the height, in pixels, of the framebuffer, * or `NULL`. * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref window_fbsize * @sa glfwSetFramebufferSizeCallback * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup window */ @@ -1902,12 +2150,14 @@ GLFWAPI void glfwGetFramebufferSize(GLFWwindow* window, int* width, int* height) * @param[out] bottom Where to store the size, in screen coordinates, of the * bottom edge of the window frame, or `NULL`. * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref window_size * - * @since Added in GLFW 3.1. + * @since Added in version 3.1. * * @ingroup window */ @@ -1924,16 +2174,17 @@ GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* window, int* left, int* top, int * * @param[in] window The window to iconify. * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref window_iconify * @sa glfwRestoreWindow + * @sa glfwMaximizeWindow * - * @since Added in GLFW 2.1. - * - * @par - * __GLFW 3:__ Added window handle parameter. + * @since Added in version 2.1. + * @glfw3 Added window handle parameter. * * @ingroup window */ @@ -1942,27 +2193,51 @@ GLFWAPI void glfwIconifyWindow(GLFWwindow* window); /*! @brief Restores the specified window. * * This function restores the specified window if it was previously iconified - * (minimized). If the window is already restored, this function does nothing. + * (minimized) or maximized. If the window is already restored, this function + * does nothing. * * If the specified window is a full screen window, the resolution chosen for * the window is restored on the selected monitor. * * @param[in] window The window to restore. * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_iconify + * @sa glfwIconifyWindow + * @sa glfwMaximizeWindow + * + * @since Added in version 2.1. + * @glfw3 Added window handle parameter. + * + * @ingroup window + */ +GLFWAPI void glfwRestoreWindow(GLFWwindow* window); + +/*! @brief Maximizes the specified window. + * + * This function maximizes the specified window if it was previously not + * maximized. If the window is already maximized, this function does nothing. + * + * If the specified window is a full screen window, this function does nothing. + * + * @param[in] window The window to maximize. + * * @par Thread Safety * This function may only be called from the main thread. * * @sa @ref window_iconify * @sa glfwIconifyWindow + * @sa glfwRestoreWindow * - * @since Added in GLFW 2.1. - * - * @par - * __GLFW 3:__ Added window handle parameter. + * @since Added in GLFW 3.2. * * @ingroup window */ -GLFWAPI void glfwRestoreWindow(GLFWwindow* window); +GLFWAPI void glfwMaximizeWindow(GLFWwindow* window); /*! @brief Makes the specified window visible. * @@ -1972,13 +2247,15 @@ GLFWAPI void glfwRestoreWindow(GLFWwindow* window); * * @param[in] window The window to make visible. * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref window_hide * @sa glfwHideWindow * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup window */ @@ -1992,18 +2269,48 @@ GLFWAPI void glfwShowWindow(GLFWwindow* window); * * @param[in] window The window to hide. * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref window_hide * @sa glfwShowWindow * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup window */ GLFWAPI void glfwHideWindow(GLFWwindow* window); +/*! @brief Brings the specified window to front and sets input focus. + * + * This function brings the specified window to front and sets input focus. + * The window should already be visible and not iconified. + * + * By default, both windowed and full screen mode windows are focused when + * initially created. Set the [GLFW_FOCUSED](@ref window_hints_wnd) to disable + * this behavior. + * + * __Do not use this function__ to steal focus from other applications unless + * you are certain that is what the user wants. Focus stealing can be + * extremely disruptive. + * + * @param[in] window The window to give input focus. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref window_focus + * + * @since Added in version 3.2. + * + * @ingroup window + */ +GLFWAPI void glfwFocusWindow(GLFWwindow* window); + /*! @brief Returns the monitor that the window uses for full screen mode. * * This function returns the handle of the monitor that the specified window is @@ -2013,12 +2320,13 @@ GLFWAPI void glfwHideWindow(GLFWwindow* window); * @return The monitor, or `NULL` if the window is in windowed mode or an error * occurred. * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref window_monitor * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup window */ @@ -2035,12 +2343,22 @@ GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window); * @return The value of the attribute, or zero if an * [error](@ref error_handling) occurred. * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @remark Framebuffer related hints are not window attributes. See @ref + * window_attribs_fb for more information. + * + * @remark Zero is a valid value for many window and context related + * attributes so you cannot use a return value of zero as an indication of + * errors. However, this function should not fail as long as it is passed + * valid arguments and the library has been [initialized](@ref intro_init). + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref window_attribs * - * @since Added in GLFW 3.0. Replaces `glfwGetWindowParam` and + * @since Added in version 3.0. Replaces `glfwGetWindowParam` and * `glfwGetGLVersion`. * * @ingroup window @@ -2056,13 +2374,15 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* window, int attrib); * @param[in] window The window whose pointer to set. * @param[in] pointer The new value. * - * @par Thread Safety - * This function may be called from any thread. Access is not synchronized. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. * * @sa @ref window_userptr * @sa glfwGetWindowUserPointer * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup window */ @@ -2075,13 +2395,15 @@ GLFWAPI void glfwSetWindowUserPointer(GLFWwindow* window, void* pointer); * * @param[in] window The window whose pointer to return. * - * @par Thread Safety - * This function may be called from any thread. Access is not synchronized. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. * * @sa @ref window_userptr * @sa glfwSetWindowUserPointer * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup window */ @@ -2099,12 +2421,13 @@ GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* window); * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref window_pos * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup window */ @@ -2122,15 +2445,14 @@ GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* window, GLFWwindow * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref window_size * - * @since Added in GLFW 1.0. - * - * @par - * __GLFW 3:__ Added window handle parameter. Updated callback signature. + * @since Added in version 1.0. + * @glfw3 Added window handle parameter and return value. * * @ingroup window */ @@ -2153,18 +2475,17 @@ GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwind * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * - * @remarks __OS X:__ Selecting Quit from the application menu will - * trigger the close callback for all windows. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * - * @par Thread Safety - * This function may only be called from the main thread. + * @remark @osx Selecting Quit from the application menu will trigger the close + * callback for all windows. * - * @sa @ref window_close + * @thread_safety This function must only be called from the main thread. * - * @since Added in GLFW 2.5. + * @sa @ref window_close * - * @par - * __GLFW 3:__ Added window handle parameter. Updated callback signature. + * @since Added in version 2.5. + * @glfw3 Added window handle parameter and return value. * * @ingroup window */ @@ -2186,15 +2507,14 @@ GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* window, GLFWwi * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * - * @sa @ref window_refresh + * @thread_safety This function must only be called from the main thread. * - * @since Added in GLFW 2.5. + * @sa @ref window_refresh * - * @par - * __GLFW 3:__ Added window handle parameter. Updated callback signature. + * @since Added in version 2.5. + * @glfw3 Added window handle parameter and return value. * * @ingroup window */ @@ -2216,12 +2536,13 @@ GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* window, GL * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref window_focus * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup window */ @@ -2238,12 +2559,13 @@ GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwi * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref window_iconify * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup window */ @@ -2260,12 +2582,13 @@ GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* window, GL * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref window_fbsize * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup window */ @@ -2289,16 +2612,17 @@ GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window * * Event processing is not required for joystick input to work. * - * @par Reentrancy - * This function may not be called from a callback. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. * - * @par Thread Safety - * This function may only be called from the main thread. + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref events * @sa glfwWaitEvents * - * @since Added in GLFW 1.0. + * @since Added in version 1.0. * * @ingroup window */ @@ -2332,16 +2656,17 @@ GLFWAPI void glfwPollEvents(void); * * Event processing is not required for joystick input to work. * - * @par Reentrancy - * This function may not be called from a callback. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. * - * @par Thread Safety - * This function may only be called from the main thread. + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref events * @sa glfwPollEvents * - * @since Added in GLFW 2.5. + * @since Added in version 2.5. * * @ingroup window */ @@ -2356,13 +2681,15 @@ GLFWAPI void glfwWaitEvents(void); * of threads in applications that do not create windows, use your threading * library of choice. * - * @par Thread Safety - * This function may be called from any thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function may be called from any thread. * * @sa @ref events * @sa glfwWaitEvents * - * @since Added in GLFW 3.1. + * @since Added in version 3.1. * * @ingroup window */ @@ -2378,12 +2705,14 @@ GLFWAPI void glfwPostEmptyEvent(void); * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or * `GLFW_STICKY_MOUSE_BUTTONS`. * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. + * + * @thread_safety This function must only be called from the main thread. * * @sa glfwSetInputMode * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup input */ @@ -2404,37 +2733,96 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); * and unlimited cursor movement. This is useful for implementing for * example 3D camera controls. * - * If the mode is `GLFW_STICKY_KEYS`, the value must be either `GL_TRUE` to - * enable sticky keys, or `GL_FALSE` to disable it. If sticky keys are + * If the mode is `GLFW_STICKY_KEYS`, the value must be either `GLFW_TRUE` to + * enable sticky keys, or `GLFW_FALSE` to disable it. If sticky keys are * enabled, a key press will ensure that @ref glfwGetKey returns `GLFW_PRESS` * the next time it is called even if the key had been released before the * call. This is useful when you are only interested in whether keys have been * pressed but not when or in which order. * * If the mode is `GLFW_STICKY_MOUSE_BUTTONS`, the value must be either - * `GL_TRUE` to enable sticky mouse buttons, or `GL_FALSE` to disable it. If - * sticky mouse buttons are enabled, a mouse button press will ensure that @ref - * glfwGetMouseButton returns `GLFW_PRESS` the next time it is called even if - * the mouse button had been released before the call. This is useful when you - * are only interested in whether mouse buttons have been pressed but not when - * or in which order. + * `GLFW_TRUE` to enable sticky mouse buttons, or `GLFW_FALSE` to disable it. + * If sticky mouse buttons are enabled, a mouse button press will ensure that + * @ref glfwGetMouseButton returns `GLFW_PRESS` the next time it is called even + * if the mouse button had been released before the call. This is useful when + * you are only interested in whether mouse buttons have been pressed but not + * when or in which order. * * @param[in] window The window whose input mode to set. * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or * `GLFW_STICKY_MOUSE_BUTTONS`. * @param[in] value The new value of the specified input mode. * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. * * @sa glfwGetInputMode * - * @since Added in GLFW 3.0. Replaces `glfwEnable` and `glfwDisable`. + * @since Added in version 3.0. Replaces `glfwEnable` and `glfwDisable`. * * @ingroup input */ GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value); +/*! @brief Returns the localized name of the specified printable key. + * + * This function returns the localized name of the specified printable key. + * This is intended for displaying key bindings to the user. + * + * If the key is `GLFW_KEY_UNKNOWN`, the scancode is used instead, otherwise + * the scancode is ignored. If a non-printable key or (if the key is + * `GLFW_KEY_UNKNOWN`) a scancode that maps to a non-printable key is + * specified, this function returns `NULL`. + * + * This behavior allows you to pass in the arguments passed to the + * [key callback](@ref input_key) without modification. + * + * The printable keys are: + * - `GLFW_KEY_APOSTROPHE` + * - `GLFW_KEY_COMMA` + * - `GLFW_KEY_MINUS` + * - `GLFW_KEY_PERIOD` + * - `GLFW_KEY_SLASH` + * - `GLFW_KEY_SEMICOLON` + * - `GLFW_KEY_EQUAL` + * - `GLFW_KEY_LEFT_BRACKET` + * - `GLFW_KEY_RIGHT_BRACKET` + * - `GLFW_KEY_BACKSLASH` + * - `GLFW_KEY_WORLD_1` + * - `GLFW_KEY_WORLD_2` + * - `GLFW_KEY_0` to `GLFW_KEY_9` + * - `GLFW_KEY_A` to `GLFW_KEY_Z` + * - `GLFW_KEY_KP_0` to `GLFW_KEY_KP_9` + * - `GLFW_KEY_KP_DECIMAL` + * - `GLFW_KEY_KP_DIVIDE` + * - `GLFW_KEY_KP_MULTIPLY` + * - `GLFW_KEY_KP_SUBTRACT` + * - `GLFW_KEY_KP_ADD` + * - `GLFW_KEY_KP_EQUAL` + * + * @param[in] key The key to query, or `GLFW_KEY_UNKNOWN`. + * @param[in] scancode The scancode of the key to query. + * @return The localized name of the key, or `NULL`. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the next call to @ref + * glfwGetKeyName, or until the library is terminated. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref input_key_name + * + * @since Added in version 3.2. + * + * @ingroup input + */ +GLFWAPI const char* glfwGetKeyName(int key, int scancode); + /*! @brief Returns the last reported state of a keyboard key for the specified * window. * @@ -2454,20 +2842,22 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value); * The [modifier key bit masks](@ref mods) are not key tokens and cannot be * used with this function. * + * __Do not use this function__ to implement [text input](@ref input_char). + * * @param[in] window The desired window. * @param[in] key The desired [keyboard key](@ref keys). `GLFW_KEY_UNKNOWN` is * not a valid key for this function. * @return One of `GLFW_PRESS` or `GLFW_RELEASE`. * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. * - * @sa @ref input_key + * @thread_safety This function must only be called from the main thread. * - * @since Added in GLFW 1.0. + * @sa @ref input_key * - * @par - * __GLFW 3:__ Added window handle parameter. + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. * * @ingroup input */ @@ -2488,15 +2878,15 @@ GLFWAPI int glfwGetKey(GLFWwindow* window, int key); * @param[in] button The desired [mouse button](@ref buttons). * @return One of `GLFW_PRESS` or `GLFW_RELEASE`. * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_ENUM. * - * @sa @ref input_mouse_button + * @thread_safety This function must only be called from the main thread. * - * @since Added in GLFW 1.0. + * @sa @ref input_mouse_button * - * @par - * __GLFW 3:__ Added window handle parameter. + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. * * @ingroup input */ @@ -2526,13 +2916,15 @@ GLFWAPI int glfwGetMouseButton(GLFWwindow* window, int button); * @param[out] ypos Where to store the cursor y-coordinate, relative to the to * top edge of the client area, or `NULL`. * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref cursor_pos * @sa glfwSetCursorPos * - * @since Added in GLFW 3.0. Replaces `glfwGetMousePos`. + * @since Added in version 3.0. Replaces `glfwGetMousePos`. * * @ingroup input */ @@ -2561,17 +2953,19 @@ GLFWAPI void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos); * @param[in] ypos The desired y-coordinate, relative to the top edge of the * client area. * - * @remarks __X11:__ Due to the asynchronous nature of a modern X desktop, it - * may take a moment for the window focus event to arrive. This means you will - * not be able to set the cursor position directly after window creation. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. * - * @par Thread Safety - * This function may only be called from the main thread. + * @remark @x11 Due to the asynchronous nature of X11, it may take a moment for + * the window focus event to arrive. This means you may not be able to set the + * cursor position directly after window creation. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref cursor_pos * @sa glfwGetCursorPos * - * @since Added in GLFW 3.0. Replaces `glfwSetMousePos`. + * @since Added in version 3.0. Replaces `glfwSetMousePos`. * * @ingroup input */ @@ -2583,9 +2977,9 @@ GLFWAPI void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos); * glfwSetCursor. The cursor can be destroyed with @ref glfwDestroyCursor. * Any remaining cursors are destroyed by @ref glfwTerminate. * - * The pixels are 32-bit little-endian RGBA, i.e. eight bits per channel. They - * are arranged canonically as packed sequential rows, starting from the - * top-left corner. + * The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight + * bits per channel. They are arranged canonically as packed sequential rows, + * starting from the top-left corner. * * The cursor hotspot is specified in pixels, relative to the upper-left corner * of the cursor image. Like all other coordinate systems in GLFW, the X-axis @@ -2594,24 +2988,24 @@ GLFWAPI void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos); * @param[in] image The desired cursor image. * @param[in] xhot The desired x-coordinate, in pixels, of the cursor hotspot. * @param[in] yhot The desired y-coordinate, in pixels, of the cursor hotspot. - * * @return The handle of the created cursor, or `NULL` if an * [error](@ref error_handling) occurred. * - * @par Pointer Lifetime - * The specified image data is copied before this function returns. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. * - * @par Reentrancy - * This function may not be called from a callback. + * @pointer_lifetime The specified image data is copied before this function + * returns. * - * @par Thread Safety - * This function may only be called from the main thread. + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref cursor_object * @sa glfwDestroyCursor * @sa glfwCreateStandardCursor * - * @since Added in GLFW 3.1. + * @since Added in version 3.1. * * @ingroup input */ @@ -2623,20 +3017,20 @@ GLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot) * a window with @ref glfwSetCursor. * * @param[in] shape One of the [standard shapes](@ref shapes). - * * @return A new cursor ready to use or `NULL` if an * [error](@ref error_handling) occurred. * - * @par Reentrancy - * This function may not be called from a callback. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. * - * @par Thread Safety - * This function may only be called from the main thread. + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref cursor_object * @sa glfwCreateCursor * - * @since Added in GLFW 3.1. + * @since Added in version 3.1. * * @ingroup input */ @@ -2650,16 +3044,17 @@ GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape); * * @param[in] cursor The cursor object to destroy. * - * @par Reentrancy - * This function may not be called from a callback. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. * - * @par Thread Safety - * This function may only be called from the main thread. + * @reentrancy This function must not be called from a callback. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref cursor_object * @sa glfwCreateCursor * - * @since Added in GLFW 3.1. + * @since Added in version 3.1. * * @ingroup input */ @@ -2679,12 +3074,14 @@ GLFWAPI void glfwDestroyCursor(GLFWcursor* cursor); * @param[in] cursor The cursor to set, or `NULL` to switch back to the default * arrow cursor. * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref cursor_object * - * @since Added in GLFW 3.1. + * @since Added in version 3.1. * * @ingroup input */ @@ -2720,15 +3117,14 @@ GLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor); * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * - * @sa @ref input_key + * @thread_safety This function must only be called from the main thread. * - * @since Added in GLFW 1.0. + * @sa @ref input_key * - * @par - * __GLFW 3:__ Added window handle parameter. Updated callback signature. + * @since Added in version 1.0. + * @glfw3 Added window handle parameter and return value. * * @ingroup input */ @@ -2759,15 +3155,14 @@ GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun cbfun); * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * - * @sa @ref input_char + * @thread_safety This function must only be called from the main thread. * - * @since Added in GLFW 2.4. + * @sa @ref input_char * - * @par - * __GLFW 3:__ Added window handle parameter. Updated callback signature. + * @since Added in version 2.4. + * @glfw3 Added window handle parameter and return value. * * @ingroup input */ @@ -2794,12 +3189,13 @@ GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun cbfun); * @return The previously set callback, or `NULL` if no callback was set or an * error occurred. * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref input_char * - * @since Added in GLFW 3.1. + * @since Added in version 3.1. * * @ingroup input */ @@ -2822,15 +3218,14 @@ GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmods * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. * - * @sa @ref input_mouse_button + * @thread_safety This function must only be called from the main thread. * - * @since Added in GLFW 1.0. + * @sa @ref input_mouse_button * - * @par - * __GLFW 3:__ Added window handle parameter. Updated callback signature. + * @since Added in version 1.0. + * @glfw3 Added window handle parameter and return value. * * @ingroup input */ @@ -2849,12 +3244,13 @@ GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* window, GLFWmo * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref cursor_pos * - * @since Added in GLFW 3.0. Replaces `glfwSetMousePosCallback`. + * @since Added in version 3.0. Replaces `glfwSetMousePosCallback`. * * @ingroup input */ @@ -2872,12 +3268,13 @@ GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* window, GLFWcursor * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref cursor_enter * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup input */ @@ -2898,12 +3295,13 @@ GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* window, GLFWcu * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref scrolling * - * @since Added in GLFW 3.0. Replaces `glfwSetMouseWheelCallback`. + * @since Added in version 3.0. Replaces `glfwSetMouseWheelCallback`. * * @ingroup input */ @@ -2925,12 +3323,13 @@ GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun cb * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref path_drop * - * @since Added in GLFW 3.1. + * @since Added in version 3.1. * * @ingroup input */ @@ -2941,14 +3340,16 @@ GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* window, GLFWdropfun cbfun); * This function returns whether the specified joystick is present. * * @param[in] joy The [joystick](@ref joysticks) to query. - * @return `GL_TRUE` if the joystick is present, or `GL_FALSE` otherwise. + * @return `GLFW_TRUE` if the joystick is present, or `GLFW_FALSE` otherwise. * - * @par Thread Safety - * This function may only be called from the main thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref joystick * - * @since Added in GLFW 3.0. Replaces `glfwGetJoystickParam`. + * @since Added in version 3.0. Replaces `glfwGetJoystickParam`. * * @ingroup input */ @@ -2959,22 +3360,28 @@ GLFWAPI int glfwJoystickPresent(int joy); * This function returns the values of all axes of the specified joystick. * Each element in the array is a value between -1.0 and 1.0. * + * Querying a joystick slot with no device present is not an error, but will + * cause this function to return `NULL`. Call @ref glfwJoystickPresent to + * check device presence. + * * @param[in] joy The [joystick](@ref joysticks) to query. * @param[out] count Where to store the number of axis values in the returned * array. This is set to zero if an error occurred. * @return An array of axis values, or `NULL` if the joystick is not present. * - * @par Pointer Lifetime - * The returned array is allocated and freed by GLFW. You should not free it - * yourself. It is valid until the specified joystick is disconnected, this - * function is called again for that joystick or the library is terminated. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. * - * @par Thread Safety - * This function may only be called from the main thread. + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified joystick is + * disconnected, this function is called again for that joystick or the library + * is terminated. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref joystick_axis * - * @since Added in GLFW 3.0. Replaces `glfwGetJoystickPos`. + * @since Added in version 3.0. Replaces `glfwGetJoystickPos`. * * @ingroup input */ @@ -2985,25 +3392,29 @@ GLFWAPI const float* glfwGetJoystickAxes(int joy, int* count); * This function returns the state of all buttons of the specified joystick. * Each element in the array is either `GLFW_PRESS` or `GLFW_RELEASE`. * + * Querying a joystick slot with no device present is not an error, but will + * cause this function to return `NULL`. Call @ref glfwJoystickPresent to + * check device presence. + * * @param[in] joy The [joystick](@ref joysticks) to query. * @param[out] count Where to store the number of button states in the returned * array. This is set to zero if an error occurred. * @return An array of button states, or `NULL` if the joystick is not present. * - * @par Pointer Lifetime - * The returned array is allocated and freed by GLFW. You should not free it - * yourself. It is valid until the specified joystick is disconnected, this - * function is called again for that joystick or the library is terminated. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. * - * @par Thread Safety - * This function may only be called from the main thread. + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified joystick is + * disconnected, this function is called again for that joystick or the library + * is terminated. * - * @sa @ref joystick_button + * @thread_safety This function must only be called from the main thread. * - * @since Added in GLFW 2.2. + * @sa @ref joystick_button * - * @par - * __GLFW 3:__ Changed to return a dynamic array. + * @since Added in version 2.2. + * @glfw3 Changed to return a dynamic array. * * @ingroup input */ @@ -3015,21 +3426,27 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int joy, int* count); * The returned string is allocated and freed by GLFW. You should not free it * yourself. * + * Querying a joystick slot with no device present is not an error, but will + * cause this function to return `NULL`. Call @ref glfwJoystickPresent to + * check device presence. + * * @param[in] joy The [joystick](@ref joysticks) to query. * @return The UTF-8 encoded name of the joystick, or `NULL` if the joystick * is not present. * - * @par Pointer Lifetime - * The returned string is allocated and freed by GLFW. You should not free it - * yourself. It is valid until the specified joystick is disconnected, this - * function is called again for that joystick or the library is terminated. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. * - * @par Thread Safety - * This function may only be called from the main thread. + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the specified joystick is + * disconnected, this function is called again for that joystick or the library + * is terminated. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref joystick_name * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup input */ @@ -3043,16 +3460,18 @@ GLFWAPI const char* glfwGetJoystickName(int joy); * @param[in] window The window that will own the clipboard contents. * @param[in] string A UTF-8 encoded string. * - * @par Pointer Lifetime - * The specified string is copied before this function returns. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. * - * @par Thread Safety - * This function may only be called from the main thread. + * @pointer_lifetime The specified string is copied before this function + * returns. + * + * @thread_safety This function must only be called from the main thread. * * @sa @ref clipboard * @sa glfwGetClipboardString * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup input */ @@ -3061,25 +3480,28 @@ GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string); /*! @brief Returns the contents of the clipboard as a string. * * This function returns the contents of the system clipboard, if it contains - * or is convertible to a UTF-8 encoded string. + * or is convertible to a UTF-8 encoded string. If the clipboard is empty or + * if its contents cannot be converted, `NULL` is returned and a @ref + * GLFW_FORMAT_UNAVAILABLE error is generated. * * @param[in] window The window that will request the clipboard contents. * @return The contents of the clipboard as a UTF-8 encoded string, or `NULL` * if an [error](@ref error_handling) occurred. * - * @par Pointer Lifetime - * The returned string is allocated and freed by GLFW. You should not free it - * yourself. It is valid until the next call to @ref + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_PLATFORM_ERROR. + * + * @pointer_lifetime The returned string is allocated and freed by GLFW. You + * should not free it yourself. It is valid until the next call to @ref * glfwGetClipboardString or @ref glfwSetClipboardString, or until the library * is terminated. * - * @par Thread Safety - * This function may only be called from the main thread. + * @thread_safety This function must only be called from the main thread. * * @sa @ref clipboard * @sa glfwSetClipboardString * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup input */ @@ -3098,12 +3520,14 @@ GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window); * @return The current value, in seconds, or zero if an * [error](@ref error_handling) occurred. * - * @par Thread Safety - * This function may be called from any thread. Access is not synchronized. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. * * @sa @ref time * - * @since Added in GLFW 1.0. + * @since Added in version 1.0. * * @ingroup input */ @@ -3117,16 +3541,18 @@ GLFWAPI double glfwGetTime(void); * * @param[in] time The new value, in seconds. * - * @remarks The upper limit of the timer is calculated as + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_INVALID_VALUE. + * + * @remark The upper limit of the timer is calculated as * floor((264 - 1) / 109) and is due to implementations * storing nanoseconds in 64 bits. The limit may be increased in the future. * - * @par Thread Safety - * This function may only be called from the main thread. + * @thread_safety This function must only be called from the main thread. * * @sa @ref time * - * @since Added in GLFW 2.2. + * @since Added in version 2.2. * * @ingroup input */ @@ -3145,16 +3571,22 @@ GLFWAPI void glfwSetTime(double time); * whether a context performs this flush by setting the * [GLFW_CONTEXT_RELEASE_BEHAVIOR](@ref window_hints_ctx) window hint. * + * The specified window must have an OpenGL or OpenGL ES context. Specifying + * a window without a context will generate a @ref GLFW_NO_WINDOW_CONTEXT + * error. + * * @param[in] window The window whose context to make current, or `NULL` to * detach the current context. * - * @par Thread Safety - * This function may be called from any thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_NO_WINDOW_CONTEXT and @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function may be called from any thread. * * @sa @ref context_current * @sa glfwGetCurrentContext * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup context */ @@ -3168,13 +3600,14 @@ GLFWAPI void glfwMakeContextCurrent(GLFWwindow* window); * @return The window whose context is current, or `NULL` if no window's * context is current. * - * @par Thread Safety - * This function may be called from any thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. * * @sa @ref context_current * @sa glfwMakeContextCurrent * - * @since Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup context */ @@ -3182,22 +3615,33 @@ GLFWAPI GLFWwindow* glfwGetCurrentContext(void); /*! @brief Swaps the front and back buffers of the specified window. * - * This function swaps the front and back buffers of the specified window. If - * the swap interval is greater than zero, the GPU driver waits the specified - * number of screen updates before swapping the buffers. + * This function swaps the front and back buffers of the specified window when + * rendering with OpenGL or OpenGL ES. If the swap interval is greater than + * zero, the GPU driver waits the specified number of screen updates before + * swapping the buffers. + * + * The specified window must have an OpenGL or OpenGL ES context. Specifying + * a window without a context will generate a @ref GLFW_NO_WINDOW_CONTEXT + * error. + * + * This function does not apply to Vulkan. If you are rendering with Vulkan, + * see `vkQueuePresentKHR` instead. * * @param[in] window The window whose buffers to swap. * - * @par Thread Safety - * This function may be called from any thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_NO_WINDOW_CONTEXT and @ref GLFW_PLATFORM_ERROR. + * + * @remark __EGL:__ The context of the specified window must be current on the + * calling thread. + * + * @thread_safety This function may be called from any thread. * * @sa @ref buffer_swap * @sa glfwSwapInterval * - * @since Added in GLFW 1.0. - * - * @par - * __GLFW 3:__ Added window handle parameter. + * @since Added in version 1.0. + * @glfw3 Added window handle parameter. * * @ingroup window */ @@ -3205,11 +3649,11 @@ GLFWAPI void glfwSwapBuffers(GLFWwindow* window); /*! @brief Sets the swap interval for the current context. * - * This function sets the swap interval for the current context, i.e. the - * number of screen updates to wait from the time @ref glfwSwapBuffers was - * called before swapping the buffers and returning. This is sometimes called - * _vertical synchronization_, _vertical retrace synchronization_ or just - * _vsync_. + * This function sets the swap interval for the current OpenGL or OpenGL ES + * context, i.e. the number of screen updates to wait from the time @ref + * glfwSwapBuffers was called before swapping the buffers and returning. This + * is sometimes called _vertical synchronization_, _vertical retrace + * synchronization_ or just _vsync_. * * Contexts that support either of the `WGL_EXT_swap_control_tear` and * `GLX_EXT_swap_control_tear` extensions also accept negative swap intervals, @@ -3221,25 +3665,30 @@ GLFWAPI void glfwSwapBuffers(GLFWwindow* window); * A context must be current on the calling thread. Calling this function * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error. * + * This function does not apply to Vulkan. If you are rendering with Vulkan, + * see the present mode of your swapchain instead. + * * @param[in] interval The minimum number of screen updates to wait for * until the buffers are swapped by @ref glfwSwapBuffers. * - * @remarks This function is not called during context creation, leaving the + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_NO_CURRENT_CONTEXT and @ref GLFW_PLATFORM_ERROR. + * + * @remark This function is not called during context creation, leaving the * swap interval set to whatever is the default on that platform. This is done * because some swap interval extensions used by GLFW do not allow the swap * interval to be reset to zero once it has been set to a non-zero value. * - * @remarks Some GPU drivers do not honor the requested swap interval, either + * @remark Some GPU drivers do not honor the requested swap interval, either * because of a user setting that overrides the application's request or due to * bugs in the driver. * - * @par Thread Safety - * This function may be called from any thread. + * @thread_safety This function may be called from any thread. * * @sa @ref buffer_swap * @sa glfwSwapBuffers * - * @since Added in GLFW 1.0. + * @since Added in version 1.0. * * @ingroup context */ @@ -3248,9 +3697,9 @@ GLFWAPI void glfwSwapInterval(int interval); /*! @brief Returns whether the specified extension is available. * * This function returns whether the specified - * [client API extension](@ref context_glext) is supported by the current - * OpenGL or OpenGL ES context. It searches both for OpenGL and OpenGL ES - * extension and platform-specific context creation API extensions. + * [API extension](@ref context_glext) is supported by the current OpenGL or + * OpenGL ES context. It searches both for client API extension and context + * creation API extensions. * * A context must be current on the calling thread. Calling this function * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error. @@ -3260,16 +3709,24 @@ GLFWAPI void glfwSwapInterval(int interval); * frequently. The extension strings will not change during the lifetime of * a context, so there is no danger in doing this. * + * This function does not apply to Vulkan. If you are using Vulkan, see @ref + * glfwGetRequiredInstanceExtensions, `vkEnumerateInstanceExtensionProperties` + * and `vkEnumerateDeviceExtensionProperties` instead. + * * @param[in] extension The ASCII encoded name of the extension. - * @return `GL_TRUE` if the extension is available, or `GL_FALSE` otherwise. + * @return `GLFW_TRUE` if the extension is available, or `GLFW_FALSE` + * otherwise. * - * @par Thread Safety - * This function may be called from any thread. + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_NO_CURRENT_CONTEXT, @ref GLFW_INVALID_VALUE and @ref + * GLFW_PLATFORM_ERROR. + * + * @thread_safety This function may be called from any thread. * * @sa @ref context_glext * @sa glfwGetProcAddress * - * @since Added in GLFW 1.0. + * @since Added in version 1.0. * * @ingroup context */ @@ -3278,40 +3735,243 @@ GLFWAPI int glfwExtensionSupported(const char* extension); /*! @brief Returns the address of the specified function for the current * context. * - * This function returns the address of the specified + * This function returns the address of the specified OpenGL or OpenGL ES * [core or extension function](@ref context_glext), if it is supported * by the current context. * * A context must be current on the calling thread. Calling this function * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error. * + * This function does not apply to Vulkan. If you are rendering with Vulkan, + * see @ref glfwGetInstanceProcAddress, `vkGetInstanceProcAddr` and + * `vkGetDeviceProcAddr` instead. + * * @param[in] procname The ASCII encoded name of the function. - * @return The address of the function, or `NULL` if the function is - * unavailable or an [error](@ref error_handling) occurred. + * @return The address of the function, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_NO_CURRENT_CONTEXT and @ref GLFW_PLATFORM_ERROR. * - * @remarks The addresses of a given function is not guaranteed to be the same + * @remark The address of a given function is not guaranteed to be the same * between contexts. * - * @remarks This function may return a non-`NULL` address despite the + * @remark This function may return a non-`NULL` address despite the * associated version or extension not being available. Always check the - * context version or extension string presence first. + * context version or extension string first. * - * @par Pointer Lifetime - * The returned function pointer is valid until the context is destroyed or the - * library is terminated. + * @pointer_lifetime The returned function pointer is valid until the context + * is destroyed or the library is terminated. * - * @par Thread Safety - * This function may be called from any thread. + * @thread_safety This function may be called from any thread. * * @sa @ref context_glext * @sa glfwExtensionSupported * - * @since Added in GLFW 1.0. + * @since Added in version 1.0. * * @ingroup context */ GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname); +/*! @brief Returns whether the Vulkan loader has been found. + * + * This function returns whether the Vulkan loader has been found. This check + * is performed by @ref glfwInit. + * + * The availability of a Vulkan loader does not by itself guarantee that window + * surface creation or even device creation is possible. Call @ref + * glfwGetRequiredInstanceExtensions to check whether the extensions necessary + * for Vulkan surface creation are available and @ref + * glfwGetPhysicalDevicePresentationSupport to check whether a queue family of + * a physical device supports image presentation. + * + * @return `GLFW_TRUE` if Vulkan is available, or `GLFW_FALSE` otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref vulkan_support + * + * @since Added in version 3.2. + * + * @ingroup vulkan + */ +GLFWAPI int glfwVulkanSupported(void); + +/*! @brief Returns the Vulkan instance extensions required by GLFW. + * + * This function returns an array of names of Vulkan instance extensions required + * by GLFW for creating Vulkan surfaces for GLFW windows. If successful, the + * list will always contains `VK_KHR_surface`, so if you don't require any + * additional extensions you can pass this list directly to the + * `VkInstanceCreateInfo` struct. + * + * If Vulkan is not available on the machine, this function returns `NULL` and + * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported + * to check whether Vulkan is available. + * + * If Vulkan is available but no set of extensions allowing window surface + * creation was found, this function returns `NULL`. You may still use Vulkan + * for off-screen rendering and compute work. + * + * @param[out] count Where to store the number of extensions in the returned + * array. This is set to zero if an error occurred. + * @return An array of ASCII encoded extension names, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_API_UNAVAILABLE. + * + * @remarks Additional extensions may be required by future versions of GLFW. + * You should check if any extensions you wish to enable are already in the + * returned array, as it is an error to specify an extension more than once in + * the `VkInstanceCreateInfo` struct. + * + * @pointer_lifetime The returned array is allocated and freed by GLFW. You + * should not free it yourself. It is guaranteed to be valid only until the + * library is terminated. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref vulkan_ext + * @sa glfwCreateWindowSurface + * + * @since Added in version 3.2. + * + * @ingroup vulkan + */ +GLFWAPI const char** glfwGetRequiredInstanceExtensions(unsigned int* count); + +#if defined(VK_VERSION_1_0) + +/*! @brief Returns the address of the specified Vulkan instance function. + * + * This function returns the address of the specified Vulkan core or extension + * function for the specified instance. If instance is set to `NULL` it can + * return any function exported from the Vulkan loader, including at least the + * following functions: + * + * - `vkEnumerateInstanceExtensionProperties` + * - `vkEnumerateInstanceLayerProperties` + * - `vkCreateInstance` + * - `vkGetInstanceProcAddr` + * + * If Vulkan is not available on the machine, this function returns `NULL` and + * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported + * to check whether Vulkan is available. + * + * This function is equivalent to calling `vkGetInstanceProcAddr` with + * a platform-specific query of the Vulkan loader as a fallback. + * + * @param[in] instance The Vulkan instance to query, or `NULL` to retrieve + * functions related to instance creation. + * @param[in] procname The ASCII encoded name of the function. + * @return The address of the function, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref + * GLFW_API_UNAVAILABLE. + * + * @pointer_lifetime The returned function pointer is valid until the library + * is terminated. + * + * @thread_safety This function may be called from any thread. + * + * @sa @ref vulkan_proc + * + * @since Added in version 3.2. + * + * @ingroup vulkan + */ +GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, const char* procname); + +/*! @brief Returns whether the specified queue family can present images. + * + * This function returns whether the specified queue family of the specified + * physical device supports presentation to the platform GLFW was built for. + * + * If Vulkan or the required window surface creation instance extensions are + * not available on the machine, or if the specified instance was not created + * with the required extensions, this function returns `GLFW_FALSE` and + * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported + * to check whether Vulkan is available and @ref + * glfwGetRequiredInstanceExtensions to check what instance extensions are + * required. + * + * @param[in] instance The instance that the physical device belongs to. + * @param[in] device The physical device that the queue family belongs to. + * @param[in] queuefamily The index of the queue family to query. + * @return `GLFW_TRUE` if the queue family supports presentation, or + * `GLFW_FALSE` otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_API_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR. + * + * @thread_safety This function may be called from any thread. For + * synchronization details of Vulkan objects, see the Vulkan specification. + * + * @sa @ref vulkan_present + * + * @since Added in version 3.2. + * + * @ingroup vulkan + */ +GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily); + +/*! @brief Creates a Vulkan surface for the specified window. + * + * This function creates a Vulkan surface for the specified window. + * + * If the Vulkan loader was not found at initialization, this function returns + * `VK_ERROR_INITIALIZATION_FAILED` and generates a @ref GLFW_API_UNAVAILABLE + * error. Call @ref glfwVulkanSupported to check whether the Vulkan loader was + * found. + * + * If the required window surface creation instance extensions are not + * available or if the specified instance was not created with these extensions + * enabled, this function returns `VK_ERROR_EXTENSION_NOT_PRESENT` and + * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref + * glfwGetRequiredInstanceExtensions to check what instance extensions are + * required. + * + * The window surface must be destroyed before the specified Vulkan instance. + * It is the responsibility of the caller to destroy the window surface. GLFW + * does not destroy it for you. Call `vkDestroySurfaceKHR` to destroy the + * surface. + * + * @param[in] instance The Vulkan instance to create the surface in. + * @param[in] window The window to create the surface for. + * @param[in] allocator The allocator to use, or `NULL` to use the default + * allocator. + * @param[out] surface Where to store the handle of the surface. This is set + * to `VK_NULL_HANDLE` if an error occurred. + * @return `VK_SUCCESS` if successful, or a Vulkan error code if an + * [error](@ref error_handling) occurred. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref + * GLFW_API_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR. + * + * @remarks If an error occurs before the creation call is made, GLFW returns + * the Vulkan error code most appropriate for the error. Appropriate use of + * @ref glfwVulkanSupported and @ref glfwGetRequiredInstanceExtensions should + * elminiate almost all occurences of these errors. + * + * @thread_safety This function may be called from any thread. For + * synchronization details of Vulkan objects, see the Vulkan specification. + * + * @sa @ref vulkan_surface + * @sa glfwGetRequiredInstanceExtensions + * + * @since Added in version 3.2. + * + * @ingroup vulkan + */ +GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface); + +#endif /*VK_VERSION_1_0*/ + /************************************************************************* * Global definition cleanup diff --git a/nuparu/include/GLFW/glfw3native.h b/nuparu/include/GLFW/glfw3native.h index b3ce7482..9fa955e9 100644 --- a/nuparu/include/GLFW/glfw3native.h +++ b/nuparu/include/GLFW/glfw3native.h @@ -1,5 +1,5 @@ /************************************************************************* - * GLFW 3.1 - www.glfw.org + * GLFW 3.2 - www.glfw.org * A library for OpenGL, window and input *------------------------------------------------------------------------ * Copyright (c) 2002-2006 Marcus Geelnard @@ -38,20 +38,30 @@ extern "C" { * Doxygen documentation *************************************************************************/ +/*! @file glfw3native.h + * @brief The header of the native access functions. + * + * This is the header file of the native access functions. See @ref native for + * more information. + */ /*! @defgroup native Native access * * **By using the native access functions you assert that you know what you're * doing and how to fix problems caused by using them. If you don't, you * shouldn't be using them.** * - * Before the inclusion of @ref glfw3native.h, you must define exactly one - * window system API macro and exactly one context creation API macro. Failure - * to do this will cause a compile-time error. + * Before the inclusion of @ref glfw3native.h, you may define exactly one + * window system API macro and zero or more context creation API macros. + * + * The chosen backends must match those the library was compiled for. Failure + * to do this will cause a link-time error. * * The available window API macros are: * * `GLFW_EXPOSE_NATIVE_WIN32` * * `GLFW_EXPOSE_NATIVE_COCOA` * * `GLFW_EXPOSE_NATIVE_X11` + * * `GLFW_EXPOSE_NATIVE_WAYLAND` + * * `GLFW_EXPOSE_NATIVE_MIR` * * The available context API macros are: * * `GLFW_EXPOSE_NATIVE_WGL` @@ -86,20 +96,23 @@ extern "C" { #elif defined(GLFW_EXPOSE_NATIVE_X11) #include #include -#else - #error "No window API selected" +#elif defined(GLFW_EXPOSE_NATIVE_WAYLAND) + #include +#elif defined(GLFW_EXPOSE_NATIVE_MIR) + #include #endif #if defined(GLFW_EXPOSE_NATIVE_WGL) /* WGL is declared by windows.h */ -#elif defined(GLFW_EXPOSE_NATIVE_NSGL) +#endif +#if defined(GLFW_EXPOSE_NATIVE_NSGL) /* NSGL is declared by Cocoa.h */ -#elif defined(GLFW_EXPOSE_NATIVE_GLX) +#endif +#if defined(GLFW_EXPOSE_NATIVE_GLX) #include -#elif defined(GLFW_EXPOSE_NATIVE_EGL) +#endif +#if defined(GLFW_EXPOSE_NATIVE_EGL) #include -#else - #error "No context API selected" #endif @@ -114,11 +127,10 @@ extern "C" { * of the specified monitor, or `NULL` if an [error](@ref error_handling) * occurred. * - * @par Thread Safety - * This function may be called from any thread. Access is not synchronized. + * @thread_safety This function may be called from any thread. Access is not + * synchronized. * - * @par History - * Added in GLFW 3.1. + * @since Added in version 3.1. * * @ingroup native */ @@ -130,11 +142,10 @@ GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* monitor); * `\\.\DISPLAY1\Monitor0`) of the specified monitor, or `NULL` if an * [error](@ref error_handling) occurred. * - * @par Thread Safety - * This function may be called from any thread. Access is not synchronized. + * @thread_safety This function may be called from any thread. Access is not + * synchronized. * - * @par History - * Added in GLFW 3.1. + * @since Added in version 3.1. * * @ingroup native */ @@ -145,11 +156,10 @@ GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* monitor); * @return The `HWND` of the specified window, or `NULL` if an * [error](@ref error_handling) occurred. * - * @par Thread Safety - * This function may be called from any thread. Access is not synchronized. + * @thread_safety This function may be called from any thread. Access is not + * synchronized. * - * @par History - * Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup native */ @@ -162,11 +172,10 @@ GLFWAPI HWND glfwGetWin32Window(GLFWwindow* window); * @return The `HGLRC` of the specified window, or `NULL` if an * [error](@ref error_handling) occurred. * - * @par Thread Safety - * This function may be called from any thread. Access is not synchronized. + * @thread_safety This function may be called from any thread. Access is not + * synchronized. * - * @par History - * Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup native */ @@ -179,11 +188,10 @@ GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* window); * @return The `CGDirectDisplayID` of the specified monitor, or * `kCGNullDirectDisplay` if an [error](@ref error_handling) occurred. * - * @par Thread Safety - * This function may be called from any thread. Access is not synchronized. + * @thread_safety This function may be called from any thread. Access is not + * synchronized. * - * @par History - * Added in GLFW 3.1. + * @since Added in version 3.1. * * @ingroup native */ @@ -194,11 +202,10 @@ GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* monitor); * @return The `NSWindow` of the specified window, or `nil` if an * [error](@ref error_handling) occurred. * - * @par Thread Safety - * This function may be called from any thread. Access is not synchronized. + * @thread_safety This function may be called from any thread. Access is not + * synchronized. * - * @par History - * Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup native */ @@ -211,11 +218,10 @@ GLFWAPI id glfwGetCocoaWindow(GLFWwindow* window); * @return The `NSOpenGLContext` of the specified window, or `nil` if an * [error](@ref error_handling) occurred. * - * @par Thread Safety - * This function may be called from any thread. Access is not synchronized. + * @thread_safety This function may be called from any thread. Access is not + * synchronized. * - * @par History - * Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup native */ @@ -228,11 +234,10 @@ GLFWAPI id glfwGetNSGLContext(GLFWwindow* window); * @return The `Display` used by GLFW, or `NULL` if an * [error](@ref error_handling) occurred. * - * @par Thread Safety - * This function may be called from any thread. Access is not synchronized. + * @thread_safety This function may be called from any thread. Access is not + * synchronized. * - * @par History - * Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup native */ @@ -243,11 +248,10 @@ GLFWAPI Display* glfwGetX11Display(void); * @return The `RRCrtc` of the specified monitor, or `None` if an * [error](@ref error_handling) occurred. * - * @par Thread Safety - * This function may be called from any thread. Access is not synchronized. + * @thread_safety This function may be called from any thread. Access is not + * synchronized. * - * @par History - * Added in GLFW 3.1. + * @since Added in version 3.1. * * @ingroup native */ @@ -258,11 +262,10 @@ GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* monitor); * @return The `RROutput` of the specified monitor, or `None` if an * [error](@ref error_handling) occurred. * - * @par Thread Safety - * This function may be called from any thread. Access is not synchronized. + * @thread_safety This function may be called from any thread. Access is not + * synchronized. * - * @par History - * Added in GLFW 3.1. + * @since Added in version 3.1. * * @ingroup native */ @@ -273,11 +276,10 @@ GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* monitor); * @return The `Window` of the specified window, or `None` if an * [error](@ref error_handling) occurred. * - * @par Thread Safety - * This function may be called from any thread. Access is not synchronized. + * @thread_safety This function may be called from any thread. Access is not + * synchronized. * - * @par History - * Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup native */ @@ -290,15 +292,116 @@ GLFWAPI Window glfwGetX11Window(GLFWwindow* window); * @return The `GLXContext` of the specified window, or `NULL` if an * [error](@ref error_handling) occurred. * - * @par Thread Safety - * This function may be called from any thread. Access is not synchronized. + * @thread_safety This function may be called from any thread. Access is not + * synchronized. * - * @par History - * Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup native */ GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* window); + +/*! @brief Returns the `GLXWindow` of the specified window. + * + * @return The `GLXWindow` of the specified window, or `None` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.2. + * + * @ingroup native + */ +GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_WAYLAND) +/*! @brief Returns the `struct wl_display*` used by GLFW. + * + * @return The `struct wl_display*` used by GLFW, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.2. + * + * @ingroup native + */ +GLFWAPI struct wl_display* glfwGetWaylandDisplay(void); + +/*! @brief Returns the `struct wl_output*` of the specified monitor. + * + * @return The `struct wl_output*` of the specified monitor, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.2. + * + * @ingroup native + */ +GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* monitor); + +/*! @brief Returns the main `struct wl_surface*` of the specified window. + * + * @return The main `struct wl_surface*` of the specified window, or `NULL` if + * an [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.2. + * + * @ingroup native + */ +GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* window); +#endif + +#if defined(GLFW_EXPOSE_NATIVE_MIR) +/*! @brief Returns the `MirConnection*` used by GLFW. + * + * @return The `MirConnection*` used by GLFW, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.2. + * + * @ingroup native + */ +GLFWAPI MirConnection* glfwGetMirDisplay(void); + +/*! @brief Returns the Mir output ID of the specified monitor. + * + * @return The Mir output ID of the specified monitor, or zero if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.2. + * + * @ingroup native + */ +GLFWAPI int glfwGetMirMonitor(GLFWmonitor* monitor); + +/*! @brief Returns the `MirSurface*` of the specified window. + * + * @return The `MirSurface*` of the specified window, or `NULL` if an + * [error](@ref error_handling) occurred. + * + * @thread_safety This function may be called from any thread. Access is not + * synchronized. + * + * @since Added in version 3.2. + * + * @ingroup native + */ +GLFWAPI MirSurface* glfwGetMirWindow(GLFWwindow* window); #endif #if defined(GLFW_EXPOSE_NATIVE_EGL) @@ -307,11 +410,10 @@ GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* window); * @return The `EGLDisplay` used by GLFW, or `EGL_NO_DISPLAY` if an * [error](@ref error_handling) occurred. * - * @par Thread Safety - * This function may be called from any thread. Access is not synchronized. + * @thread_safety This function may be called from any thread. Access is not + * synchronized. * - * @par History - * Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup native */ @@ -322,11 +424,10 @@ GLFWAPI EGLDisplay glfwGetEGLDisplay(void); * @return The `EGLContext` of the specified window, or `EGL_NO_CONTEXT` if an * [error](@ref error_handling) occurred. * - * @par Thread Safety - * This function may be called from any thread. Access is not synchronized. + * @thread_safety This function may be called from any thread. Access is not + * synchronized. * - * @par History - * Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup native */ @@ -337,11 +438,10 @@ GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* window); * @return The `EGLSurface` of the specified window, or `EGL_NO_SURFACE` if an * [error](@ref error_handling) occurred. * - * @par Thread Safety - * This function may be called from any thread. Access is not synchronized. + * @thread_safety This function may be called from any thread. Access is not + * synchronized. * - * @par History - * Added in GLFW 3.0. + * @since Added in version 3.0. * * @ingroup native */ diff --git a/nuparu/lib/osx/libglfw3.a b/nuparu/lib/osx/libglfw3.a index 92ac6608d345b0b79def5a7efbeb5627791b1004..48d246458190450cfc685b080841c9900a15be8d 100644 GIT binary patch literal 176728 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

%9@&YpDg+AH$d(QucZ^xy*q*CbR2ytPS0U^jr4Y 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

xrZ^^(;V(X~GAR71 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 diff --git a/src/main.cpp b/src/main.cpp index d709ccd5..88ec71f3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -35,7 +35,7 @@ int main() glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Make sure with don't use old profile // Open a window and create its OpenGL context - gWindow = glfwCreateWindow( 1024, 768, "Playground", NULL, NULL); + gWindow = glfwCreateWindow( SCREEN_SIZE.x, SCREEN_SIZE.y, "Trungle Fluid", NULL, NULL); if( gWindow == NULL ){ fprintf( stderr, "Failed to open GLFW window.\n" ); getchar(); @@ -45,7 +45,35 @@ int main() glfwMakeContextCurrent(gWindow); - cout << "Hello world 2" << endl; + // Initialize GLEW + if (glewInit() != GLEW_OK) { + fprintf(stderr, "Failed to initialize GLEW\n"); + getchar(); + glfwTerminate(); + return -1; + } + + // Ensure we can capture the escape key being pressed below + glfwSetInputMode(gWindow, GLFW_STICKY_KEYS, GL_TRUE); + + // Dark blue background + glClearColor(0.0f, 0.0f, 0.4f, 0.0f); + + do{ + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + // Swap buffers + glfwSwapBuffers(gWindow); + glfwPollEvents(); + + } + // Check if the ESC key was pressed or the window was closed + while( glfwGetKey(gWindow, GLFW_KEY_ESCAPE ) != GLFW_PRESS && + glfwWindowShouldClose(gWindow) == 0 ); + + // Close OpenGL window and terminate GLFW + glfwTerminate(); + return 0; } From 0ff6662576ce2b1e8c48ce31cea36503fd3432fb Mon Sep 17 00:00:00 2001 From: Trung Le Date: Fri, 26 Feb 2016 23:21:41 -0500 Subject: [PATCH 03/64] Added ShaderProgram. Added Viewer. Added Box geometry --- CMakeLists.txt | 7 +- src/camera/camera.cpp | 1 - src/geom/geom.cpp | 5 -- src/geom/geom.hpp | 9 --- src/geometry/box.cpp | 139 ++++++++++++++++++++++++++++++++++++++ src/geometry/box.h | 21 ++++++ src/geometry/geometry.cpp | 58 ++++++++++++++++ src/geometry/geometry.h | 65 ++++++++++++++++++ src/glsl/frag_bare.glsl | 12 ++++ src/glsl/vert_bare.glsl | 18 +++++ src/loadShaders.cpp | 107 +++++++++++++++++++++++++++++ src/loadShaders.h | 8 +++ src/main.cpp | 71 ++----------------- src/main.hpp | 16 ----- src/program.cpp | 93 ------------------------- src/program.hpp | 70 ------------------- src/scene/scene.cpp | 31 +++++++-- src/scene/scene.h | 19 ++++++ src/scene/scene.hpp | 3 - src/shader.cpp | 118 -------------------------------- src/shader.hpp | 75 -------------------- src/shaderProgram.cpp | 60 ++++++++++++++++ src/shaderProgram.h | 25 +++++++ src/viewer.cpp | 105 ++++++++++++++++++++++++++++ src/viewer.h | 41 +++++++++++ src/viewer/viewer.cpp | 5 -- src/viewer/viewer.hpp | 5 -- 27 files changed, 714 insertions(+), 473 deletions(-) delete mode 100644 src/geom/geom.cpp delete mode 100644 src/geom/geom.hpp create mode 100644 src/geometry/box.cpp create mode 100644 src/geometry/box.h create mode 100644 src/geometry/geometry.cpp create mode 100644 src/geometry/geometry.h create mode 100644 src/glsl/frag_bare.glsl create mode 100644 src/glsl/vert_bare.glsl create mode 100644 src/loadShaders.cpp create mode 100644 src/loadShaders.h delete mode 100644 src/program.cpp delete mode 100644 src/program.hpp create mode 100644 src/scene/scene.h delete mode 100644 src/scene/scene.hpp delete mode 100644 src/shader.cpp delete mode 100644 src/shader.hpp create mode 100644 src/shaderProgram.cpp create mode 100644 src/shaderProgram.h create mode 100644 src/viewer.cpp create mode 100644 src/viewer.h delete mode 100644 src/viewer/viewer.cpp delete mode 100644 src/viewer/viewer.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a1af131..9e87391f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,13 +85,16 @@ endif() # Add source files you want to compile (.cpp) set(CORE_SRC + src/loadShaders.cpp src/utility.cpp src/main.cpp + src/shaderProgram.cpp + src/viewer.cpp src/camera/camera.cpp - src/viewer/viewer.cpp src/fluidSolver/fluidSolver.cpp src/scene/scene.cpp - src/geom/geom.cpp + src/geometry/geometry.cpp + src/geometry/box.cpp ) add_executable(Korra ${CORE_SRC}) diff --git a/src/camera/camera.cpp b/src/camera/camera.cpp index d0b5511a..fb246b12 100644 --- a/src/camera/camera.cpp +++ b/src/camera/camera.cpp @@ -4,4 +4,3 @@ // #include "camera.hpp" -#include "../geom/geom.hpp" diff --git a/src/geom/geom.cpp b/src/geom/geom.cpp deleted file mode 100644 index c4438fdc..00000000 --- a/src/geom/geom.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// -// geom.cpp -// Thanda - -#include "geom.hpp" diff --git a/src/geom/geom.hpp b/src/geom/geom.hpp deleted file mode 100644 index 91e658f7..00000000 --- a/src/geom/geom.hpp +++ /dev/null @@ -1,9 +0,0 @@ -// -// geom.hpp -// Thanda - -#ifndef geom_hpp -#define geom_hpp - - -#endif /* geom_hpp */ diff --git a/src/geometry/box.cpp b/src/geometry/box.cpp new file mode 100644 index 00000000..eefa15b4 --- /dev/null +++ b/src/geometry/box.cpp @@ -0,0 +1,139 @@ +#include "box.h" + +Box::Box( + ViewMode viewMode + ) +{ + m_viewMode = viewMode; + InitPositions(); + InitIndices(); +} + +void +Box::InitPositions() +{ + //Front face + //UR + m_positions.push_back(glm::vec3(0.5f, 0.5f, 0.5f)); + //LR + m_positions.push_back(glm::vec3(0.5f, -0.5f, 0.5f)); + //LL + m_positions.push_back(glm::vec3(-0.5f, -0.5f, 0.5f)); + //UL + m_positions.push_back(glm::vec3(-0.5f, 0.5f, 0.5f)); + + //Right face + //UR + m_positions.push_back(glm::vec3(0.5f, 0.5f, -0.5f)); + //LR + m_positions.push_back(glm::vec3(0.5f, -0.5f, -0.5f)); + //LL + m_positions.push_back(glm::vec3(0.5f, -0.5f, 0.5f)); + //UL + m_positions.push_back(glm::vec3(0.5f, 0.5f, 0.5f)); + + //Left face + //UR + m_positions.push_back(glm::vec3(-0.5f, 0.5f, 0.5f)); + //LR + m_positions.push_back(glm::vec3(-0.5f, -0.5f, 0.5f)); + //LL + m_positions.push_back(glm::vec3(-0.5f, -0.5f, -0.5f)); + //UL + m_positions.push_back(glm::vec3(-0.5f, 0.5f, -0.5f)); + + //Back face + //UR + m_positions.push_back(glm::vec3(-0.5f, 0.5f, -0.5f)); + //LR + m_positions.push_back(glm::vec3(-0.5f, -0.5f, -0.5f)); + //LL + m_positions.push_back(glm::vec3(0.5f, -0.5f, -0.5f)); + //UL + m_positions.push_back(glm::vec3(0.5f, 0.5f, -0.5f)); + + //Top face + //UR + m_positions.push_back(glm::vec3(0.5f, 0.5f, -0.5f)); + //LR + m_positions.push_back(glm::vec3(0.5f, 0.5f, 0.5f)); + //LL + m_positions.push_back(glm::vec3(-0.5f, 0.5f, 0.5f)); + //UL + m_positions.push_back(glm::vec3(-0.5f, 0.5f, -0.5f)); + + //Bottom face + //UR + m_positions.push_back(glm::vec3(0.5f, -0.5f, 0.5f)); + //LR + m_positions.push_back(glm::vec3(0.5f, -0.5f, -0.5f)); + //LL + m_positions.push_back(glm::vec3(-0.5f, -0.5f, -0.5f)); + //UL + m_positions.push_back(glm::vec3(-0.5f, -0.5f, 0.5f)); +} + +void +Box::InitNormals() +{ + +} + +void +Box::InitColors() +{ + +} + +void +Box::InitIndices() +{ + // Two triangles for each faces + for(int i = 0; i < 6; i++){ + m_indices.push_back(i * 4); + m_indices.push_back(i * 4 + 1); + m_indices.push_back(i * 4 + 2); + m_indices.push_back(i * 4); + m_indices.push_back(i * 4 + 2); + m_indices.push_back(i * 4 + 3); + } +} + +void +Box::Create() +{ + glGenVertexArrays(1, &m_vao); + glBindVertexArray(m_vao); + + // @todo: Is it faster to allocate all the buffers ahead of time? + + // // -- Position + // glGenBuffers(1, &m_posBuffer); + // glBindBuffer(GL_ARRAY_BUFFER, m_posBuffer); + // glBufferData(GL_ARRAY_BUFFER, m_positions.size() * sizeof(glm::vec3), (void*)&(m_positions[0]), GL_STATIC_DRAW); + // glBindBuffer(GL_ARRAY_BUFFER, NULL); + + // // @todo: + // // glGenBuffers(1, &m_norBuffer); + + // // @todo: + // // glGenBuffers(1, &m_colBuffer); + + // // -- Index + // glGenBuffers(1, &m_idxBuffer); + // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_idxBuffer); + // glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indices.size() * sizeof(unsigned int), (void*)&(m_indices[0]), GL_STATIC_DRAW); + // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NULL); + + // Draw a triangle + static const GLfloat g_vertex_buffer_data[] = { + -1.0f, -1.0f, 0.0f, + 1.0f, -1.0f, 0.0f, + 0.0f, 1.0f, 0.0f + }; + glBindBuffer(GL_ARRAY_BUFFER, m_posBuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW); + + glBindVertexArray(0); + +} diff --git a/src/geometry/box.h b/src/geometry/box.h new file mode 100644 index 00000000..0c13eec4 --- /dev/null +++ b/src/geometry/box.h @@ -0,0 +1,21 @@ +#ifndef BOX_H +#define BOX_H + +#include "geometry.h" + +class Box : public Geometry +{ +public: + Box(ViewMode viewMode = Shaded); + virtual ~Box() {}; + virtual void Create(); + +protected: + virtual void InitPositions(); + virtual void InitNormals(); + virtual void InitColors(); + virtual void InitIndices(); +}; + + +#endif diff --git a/src/geometry/geometry.cpp b/src/geometry/geometry.cpp new file mode 100644 index 00000000..a20329b1 --- /dev/null +++ b/src/geometry/geometry.cpp @@ -0,0 +1,58 @@ +#include "geometry.h" + +GLenum +Geometry::DrawMode() const +{ + switch (m_viewMode) { + case WireFrame: + return GL_LINES; + case Shaded: + default: + return GL_TRIANGLES; + } +} + +int +Geometry::ElementCount() const +{ + return m_indices.size(); +} + +void +Geometry::Bind() const +{ + glBindVertexArray(m_vao); + glEnableVertexAttribArray(0); + glBindBuffer(GL_ARRAY_BUFFER, m_posBuffer); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0); + glBindBuffer(GL_ARRAY_BUFFER, NULL); +} + +void +Geometry::SetLocalTransformation( + const glm::mat4 localTrans + ) +{ + +} + +void +Geometry::SetGlobalTransformation( + const glm::mat4 + ) +{ + +} + +glm::mat4 +Geometry::GetLocalTransformation() const +{ + return m_localTransform; +} + +glm::mat4 +Geometry::GetGlobalTransformation() const +{ + return m_globalTransform; +} + diff --git a/src/geometry/geometry.h b/src/geometry/geometry.h new file mode 100644 index 00000000..15f28f2a --- /dev/null +++ b/src/geometry/geometry.h @@ -0,0 +1,65 @@ +#ifndef GEOMETRY_H +#define GEOMETRY_H + +// GL libraries + +#include +#include +#include + +// C++ + +#include + +using namespace std; + +typedef enum { + WireFrame, + Shaded +} ViewMode; + +class Geometry +{ +public: + Geometry () { m_viewMode = Shaded; }; + virtual void Create() = 0; + + // -- OpenGL helpers + virtual GLenum DrawMode() const; + virtual int ElementCount() const; + + // Bind the vertex array object for this geometry + virtual void Bind() const; + + // -- Transformations + virtual void SetLocalTransformation(const glm::mat4); + virtual void SetGlobalTransformation(const glm::mat4); + virtual glm::mat4 GetLocalTransformation() const; + virtual glm::mat4 GetGlobalTransformation() const; + +protected: + ViewMode m_viewMode; + + // Data + vector m_positions; + vector m_normals; + vector m_colors; + vector m_indices; + + // Transformation + glm::mat4 m_localTransform; + glm::mat4 m_globalTransform; + + // Vertex buffers + GLuint m_posBuffer; + GLuint m_norBuffer; + GLuint m_colBuffer; + + // Index buffer + GLuint m_idxBuffer; + + // Vertex array object + GLuint m_vao; +}; + +#endif diff --git a/src/glsl/frag_bare.glsl b/src/glsl/frag_bare.glsl new file mode 100644 index 00000000..53911af1 --- /dev/null +++ b/src/glsl/frag_bare.glsl @@ -0,0 +1,12 @@ +#version 150 core + +precision highp float; +precision highp int; +layout(std140, column_major) uniform; + +out vec4 color; + +void main() +{ + color = vec4(0.0, 1.0, 1.0, 1.0); +} diff --git a/src/glsl/vert_bare.glsl b/src/glsl/vert_bare.glsl new file mode 100644 index 00000000..b05f2014 --- /dev/null +++ b/src/glsl/vert_bare.glsl @@ -0,0 +1,18 @@ +#version 150 core +#define POSITION_LOCATION 0 + +precision highp float; +precision highp int; +layout(std140, column_major) uniform; + +uniform mat4 u_model; +uniform mat4 u_viewProj; + +in vec3 a_position; + +void main() +{ + //vec4 position = u_viewProj * u_model * vec4(a_position, 1.0); + vec4 position = vec4(a_position, 1.0); + gl_Position = position; +} diff --git a/src/loadShaders.cpp b/src/loadShaders.cpp new file mode 100644 index 00000000..6fafe206 --- /dev/null +++ b/src/loadShaders.cpp @@ -0,0 +1,107 @@ +// @todo: Properly credit OpenGL Tutorials + +#include +#include +#include +#include +#include + +#include + +#include "loadShaders.h" + +GLuint +LoadShaders( + const char* vertFilePath, + const char* fragFilePath + ) +{ + GLuint vertexShaderID = glCreateShader(GL_VERTEX_SHADER); + GLuint fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER); + + // Read vertex shader code from file + std::string vertexShaderCode; + std::ifstream vertexShaderStream(vertFilePath, std::ios::in); + + if (vertexShaderStream.is_open()) { + std::string line = " "; + while (getline(vertexShaderStream, line)) { + vertexShaderCode += "\n" + line; + } + vertexShaderStream.close(); + } else { + printf("Can't open"); + getchar(); + return 0; + } + + // Read fragment shader code from file + std::string fragmentShaderCode; + std::ifstream fragmentShaderStream(fragFilePath, std::ios::in); + if (fragmentShaderStream.is_open()) { + std::string line = " "; + while(getline(fragmentShaderStream, line)) { + fragmentShaderCode += "\n" + line; + } + + fragmentShaderStream.close(); + } + + GLint result = GL_FALSE; + int infoLogLength; + + // Compile vertex shader + printf("Compiling vertex shader: %s\n", vertFilePath); + char const* vertexSourcePointer = vertexShaderCode.c_str(); + glShaderSource(vertexShaderID, 1, &vertexSourcePointer, NULL); + glCompileShader(vertexShaderID); + + // Check vertex shader + glGetShaderiv(vertexShaderID, GL_COMPILE_STATUS, &result); + glGetShaderiv(vertexShaderID, GL_INFO_LOG_LENGTH, &infoLogLength); + + if (infoLogLength > 0) { + std::vector vertexShaderErrorMessage(infoLogLength + 1); + glGetShaderInfoLog(vertexShaderID, infoLogLength, NULL, &vertexShaderErrorMessage[0]); + printf("%s\n", &vertexShaderErrorMessage[0]); + } + + // Compile fragment shader + printf("Compiling fragment shader: %s\n", fragFilePath); + char const * fragmentSourcePointer = fragmentShaderCode.c_str(); + glShaderSource(fragmentShaderID, 1, &fragmentSourcePointer, NULL); + glCompileShader(fragmentShaderID); + + // Check fragment shader + glGetShaderiv(fragmentShaderID, GL_COMPILE_STATUS, &result); + glGetShaderiv(fragmentShaderID, GL_INFO_LOG_LENGTH, &infoLogLength); + if (infoLogLength > 0) { + std::vector fragmentShaderErrorMessage(infoLogLength + 1); + glGetShaderInfoLog(fragmentShaderID, infoLogLength, NULL, &fragmentShaderErrorMessage[0]); + printf("%s\n", &fragmentShaderErrorMessage[0]); + } + + // Link the program + printf("Linking program\n"); + GLuint programID = glCreateProgram(); + glAttachShader(programID, vertexShaderID); + glAttachShader(programID, fragmentShaderID); + glLinkProgram(programID); + + // Check the program + glGetProgramiv(programID, GL_LINK_STATUS, &result); + glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &infoLogLength); + if (infoLogLength > 0) { + std::vector programErrorMessage(infoLogLength + 1); + glGetProgramInfoLog(programID, infoLogLength, NULL, &programErrorMessage[0]); + printf("%s\n", &programErrorMessage[0]); + } + + glDetachShader(programID, vertexShaderID); + glDetachShader(programID, fragmentShaderID); + + glDeleteShader(vertexShaderID); + glDeleteShader(fragmentShaderID); + + return programID; +} diff --git a/src/loadShaders.h b/src/loadShaders.h new file mode 100644 index 00000000..dd5309c0 --- /dev/null +++ b/src/loadShaders.h @@ -0,0 +1,8 @@ +#ifndef loadShader_h +#define loadShader_h + +#include + +GLuint LoadShaders(const char* vertex_file_path, const char* fragment_file_path); + +#endif /* loadShader_h */ diff --git a/src/main.cpp b/src/main.cpp index 88ec71f3..973ed277 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,78 +1,15 @@ #include #include "main.hpp" +#include "viewer.h" using namespace std; - -// ---------------------- -// Constants -// ---------------------- - -const glm::vec2 SCREEN_SIZE(800, 600); - -// ---------------------- -// Globals -// ---------------------- - -GLFWwindow* gWindow = NULL; - int main() { - // Initialise GLFW - if( !glfwInit() ) - { - fprintf( stderr, "Failed to initialize GLFW\n" ); - getchar(); - return -1; - } - - glfwWindowHint(GLFW_SAMPLES, 4); // antiliasing 4 samples - glfwWindowHint(GLFW_RESIZABLE,GL_FALSE); - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Get version 3.3 - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Make sure with don't use old profile - - // Open a window and create its OpenGL context - gWindow = glfwCreateWindow( SCREEN_SIZE.x, SCREEN_SIZE.y, "Trungle Fluid", NULL, NULL); - if( gWindow == NULL ){ - fprintf( stderr, "Failed to open GLFW window.\n" ); - getchar(); - glfwTerminate(); - return -1; - } - - glfwMakeContextCurrent(gWindow); - - // Initialize GLEW - if (glewInit() != GLEW_OK) { - fprintf(stderr, "Failed to initialize GLEW\n"); - getchar(); - glfwTerminate(); - return -1; - } - - // Ensure we can capture the escape key being pressed below - glfwSetInputMode(gWindow, GLFW_STICKY_KEYS, GL_TRUE); - - // Dark blue background - glClearColor(0.0f, 0.0f, 0.4f, 0.0f); - - do{ - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - // Swap buffers - glfwSwapBuffers(gWindow); - glfwPollEvents(); - - } - // Check if the ESC key was pressed or the window was closed - while( glfwGetKey(gWindow, GLFW_KEY_ESCAPE ) != GLFW_PRESS && - glfwWindowShouldClose(gWindow) == 0 ); - - // Close OpenGL window and terminate GLFW - glfwTerminate(); + Viewer view(800, 600); + view.Render(); + view.CleanUp(); return 0; } diff --git a/src/main.hpp b/src/main.hpp index 6832b250..fe1f64d5 100644 --- a/src/main.hpp +++ b/src/main.hpp @@ -5,21 +5,5 @@ #ifndef main_hpp #define main_hpp -// GL libraries - -#include -#include -#include - -// C++ libraries - -#include - -// Custom - -#include "program.hpp" -#include "utility.hpp" - - #endif /* main_hpp */ diff --git a/src/program.cpp b/src/program.cpp deleted file mode 100644 index 6fe03e38..00000000 --- a/src/program.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - tdogl::Program - - Copyright 2012 Thomas Dalling - http://tomdalling.com/ - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#include "program.hpp" -#include - -using namespace tdogl; - -Program::Program(const std::vector& shaders) : - _object(0) -{ - if(shaders.size() <= 0) - throw std::runtime_error("No shaders were provided to create the program"); - - //create the program object - _object = glCreateProgram(); - if(_object == 0) - throw std::runtime_error("glCreateProgram failed"); - - //attach all the shaders - for(unsigned i = 0; i < shaders.size(); ++i) - glAttachShader(_object, shaders[i].object()); - - //link the shaders together - glLinkProgram(_object); - - //detach all the shaders - for(unsigned i = 0; i < shaders.size(); ++i) - glDetachShader(_object, shaders[i].object()); - - //throw exception if linking failed - GLint status; - glGetProgramiv(_object, GL_LINK_STATUS, &status); - if (status == GL_FALSE) { - std::string msg("Program linking failure: "); - - GLint infoLogLength; - glGetProgramiv(_object, GL_INFO_LOG_LENGTH, &infoLogLength); - char* strInfoLog = new char[infoLogLength + 1]; - glGetProgramInfoLog(_object, infoLogLength, NULL, strInfoLog); - msg += strInfoLog; - delete[] strInfoLog; - - glDeleteProgram(_object); _object = 0; - throw std::runtime_error(msg); - } -} - -Program::~Program() { - //might be 0 if ctor fails by throwing exception - if(_object != 0) glDeleteProgram(_object); -} - -GLuint Program::object() const { - return _object; -} - -GLint Program::attrib(const GLchar* attribName) const { - if(!attribName) - throw std::runtime_error("attribName was NULL"); - - GLint attrib = glGetAttribLocation(_object, attribName); - if(attrib == -1) - throw std::runtime_error(std::string("Program attribute not found: ") + attribName); - - return attrib; -} - -GLint Program::uniform(const GLchar* uniformName) const { - if(!uniformName) - throw std::runtime_error("uniformName was NULL"); - - GLint uniform = glGetUniformLocation(_object, uniformName); - if(uniform == -1) - throw std::runtime_error(std::string("Program uniform not found: ") + uniformName); - - return uniform; -} diff --git a/src/program.hpp b/src/program.hpp deleted file mode 100644 index 537043cc..00000000 --- a/src/program.hpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - tdogl::Program - - Copyright 2012 Thomas Dalling - http://tomdalling.com/ - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#pragma once - -#include "shader.hpp" -#include - -namespace tdogl { - - /** - Represents an OpenGL program made by linking shaders. - */ - class Program { - public: - /** - Creates a program by linking a list of tdogl::Shader objects - - @param shaders The shaders to link together to make the program - - @throws std::exception if an error occurs. - - @see tdogl::Shader - */ - Program(const std::vector& shaders); - ~Program(); - - - /** - @result The program's object id, as returned from glCreateProgram - */ - GLuint object() const; - - - /** - @result The attribute index for the given name, as returned from glGetAttribLocation. - */ - GLint attrib(const GLchar* attribName) const; - - - /** - @result The uniform index for the given name, as returned from glGetUniformLocation. - */ - GLint uniform(const GLchar* uniformName) const; - - - private: - GLuint _object; - - //copying disabled - Program(const Program&); - const Program& operator=(const Program&); - }; - -} diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 32300ca9..27759f42 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -1,5 +1,28 @@ -// -// scene.cpp -// Thanda +#include "scene.h" -#include "scene.hpp" +Scene::Scene() +{ +} + +Scene::~Scene() +{ +} + +void +Scene::InitFromTestScene() +{ + m_box = Box(); + m_box.Create(); +} + +void +Scene::InitFromJson() +{ + +} + +const Geometry& +Scene::RootGeometry() const +{ + return m_box; +} diff --git a/src/scene/scene.h b/src/scene/scene.h new file mode 100644 index 00000000..e2fca543 --- /dev/null +++ b/src/scene/scene.h @@ -0,0 +1,19 @@ +#ifndef SCENE_H +#define SCENE_H + +#include "../geometry/box.h" + +class Scene +{ +public: + Scene(); + ~Scene(); + virtual void InitFromTestScene(); + virtual void InitFromJson(); + virtual const Geometry& RootGeometry() const; + +protected: + Box m_box; +}; + +#endif diff --git a/src/scene/scene.hpp b/src/scene/scene.hpp deleted file mode 100644 index 3d6723cf..00000000 --- a/src/scene/scene.hpp +++ /dev/null @@ -1,3 +0,0 @@ -// -// scene.hpp -// Thanda diff --git a/src/shader.cpp b/src/shader.cpp deleted file mode 100644 index e34390b8..00000000 --- a/src/shader.cpp +++ /dev/null @@ -1,118 +0,0 @@ -/* - tdogl::Shader - - Copyright 2012 Thomas Dalling - http://tomdalling.com/ - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#include "shader.hpp" -#include -#include -#include -#include -#include - -using namespace tdogl; - -Shader::Shader(const std::string& shaderCode, GLenum shaderType) : - _object(0), - _refCount(NULL) -{ - //create the shader object - _object = glCreateShader(shaderType); - if(_object == 0) - throw std::runtime_error("glCreateShader failed"); - - //set the source code - const char* code = shaderCode.c_str(); - glShaderSource(_object, 1, (const GLchar**)&code, NULL); - - //compile - glCompileShader(_object); - - //throw exception if compile error occurred - GLint status; - glGetShaderiv(_object, GL_COMPILE_STATUS, &status); - if (status == GL_FALSE) { - std::string msg("Compile failure in shader:\n"); - - GLint infoLogLength; - glGetShaderiv(_object, GL_INFO_LOG_LENGTH, &infoLogLength); - char* strInfoLog = new char[infoLogLength + 1]; - glGetShaderInfoLog(_object, infoLogLength, NULL, strInfoLog); - msg += strInfoLog; - delete[] strInfoLog; - - glDeleteShader(_object); _object = 0; - throw std::runtime_error(msg); - } - - _refCount = new unsigned; - *_refCount = 1; -} - -Shader::Shader(const Shader& other) : - _object(other._object), - _refCount(other._refCount) -{ - _retain(); -} - -Shader::~Shader() { - //_refCount will be NULL if constructor failed and threw an exception - if(_refCount) _release(); -} - -GLuint Shader::object() const { - return _object; -} - -Shader& Shader::operator = (const Shader& other) { - _release(); - _object = other._object; - _refCount = other._refCount; - _retain(); - return *this; -} - -Shader Shader::shaderFromFile(const std::string& filePath, GLenum shaderType) { - //open file - std::ifstream f; - f.open(filePath.c_str(), std::ios::in | std::ios::binary); - if(!f.is_open()){ - throw std::runtime_error(std::string("Failed to open file: ") + filePath); - } - - //read whole file into stringstream buffer - std::stringstream buffer; - buffer << f.rdbuf(); - - //return new shader - Shader shader(buffer.str(), shaderType); - return shader; -} - -void Shader::_retain() { - assert(_refCount); - *_refCount += 1; -} - -void Shader::_release() { - assert(_refCount && *_refCount > 0); - *_refCount -= 1; - if(*_refCount == 0){ - glDeleteShader(_object); _object = 0; - delete _refCount; _refCount = NULL; - } -} diff --git a/src/shader.hpp b/src/shader.hpp deleted file mode 100644 index 4a7c64f6..00000000 --- a/src/shader.hpp +++ /dev/null @@ -1,75 +0,0 @@ -/* - tdogl::Shader - - Copyright 2012 Thomas Dalling - http://tomdalling.com/ - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#pragma once - -#include -#include - -namespace tdogl { - - /** - Represents a compiled OpenGL shader. - */ - class Shader { - public: - - /** - Creates a shader from a text file. - - @param filePath The path to the text file containing the shader source. - @param shaderType Same as the argument to glCreateShader. For example GL_VERTEX_SHADER - or GL_FRAGMENT_SHADER. - - @throws std::exception if an error occurs. - */ - static Shader shaderFromFile(const std::string& filePath, GLenum shaderType); - - - /** - Creates a shader from a string of shader source code. - - @param shaderCode The source code for the shader. - @param shaderType Same as the argument to glCreateShader. For example GL_VERTEX_SHADER - or GL_FRAGMENT_SHADER. - - @throws std::exception if an error occurs. - */ - Shader(const std::string& shaderCode, GLenum shaderType); - - - /** - @result The shader's object ID, as returned from glCreateShader - */ - GLuint object() const; - - // tdogl::Shader objects can be copied and assigned because they are reference counted - // like a shared pointer - Shader(const Shader& other); - Shader& operator =(const Shader& other); - ~Shader(); - - private: - GLuint _object; - unsigned* _refCount; - - void _retain(); - void _release(); - }; - -} diff --git a/src/shaderProgram.cpp b/src/shaderProgram.cpp new file mode 100644 index 00000000..e4192762 --- /dev/null +++ b/src/shaderProgram.cpp @@ -0,0 +1,60 @@ +#include "shaderProgram.h" +#include "loadShaders.h" + +ShaderProgram::ShaderProgram( + const char* vertFilePath, + const char* fragFilePath + ) +{ + m_programID = LoadShaders(vertFilePath, fragFilePath); +} + +void ShaderProgram::Render( + const Geometry* geo + ) +{ + glUseProgram(m_programID); + + // GLuint VertexArrayID = 0; + // glGenVertexArrays(1, &VertexArrayID); + // glBindVertexArray(VertexArrayID); + + // // Draw a triangle + // static const GLfloat g_vertex_buffer_data[] = { + // -1.0f, -1.0f, 0.0f, + // 1.0f, -1.0f, 0.0f, + // 0.0f, 1.0f, 0.0f + // }; + + // // Identify vertex buffer + // GLuint vertexbuffer; + + + // // Generate one and put the result in vertexbuffer + // glGenBuffers(1, &vertexbuffer); + + // glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); + // // Give vertices to openGL + // glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW); + + // // First attribute buffer + // glEnableVertexAttribArray(0); + // glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); + // glVertexAttribPointer( + // 0, // Attribute 0, no reason just need to match the layout in the shader + // 3, // size + // GL_FLOAT, // type + // GL_FALSE, // normalized? + // 0, // stride + // (void*)0 // array buffer offset + // ); + + // glPointSize(5.0f); + + // // Draw the triangle! + // glDrawArrays(GL_TRIANGLES, 0, 3); // vertex 3, count 3 -> 1 triangle + // glDisableVertexAttribArray(0); + geo->Bind(); + // glDrawElements(GL_POINTS, geo.ElementCount(), GL_UNSIGNED_SHORT, NULL); + glDrawArrays(GL_TRIANGLES, 0, 3); // vertex 3, count 3 -> 1 triangle +} diff --git a/src/shaderProgram.h b/src/shaderProgram.h new file mode 100644 index 00000000..29410797 --- /dev/null +++ b/src/shaderProgram.h @@ -0,0 +1,25 @@ +#ifndef SHADER_PROGRAM_H +#define SHADER_PROGRAM_H + +#include "loadShaders.h" +#include "geometry/geometry.h" + +class ShaderProgram +{ + +public: + ShaderProgram() {} + ShaderProgram( + const char* vertFilePath, + const char* fragFilePath + ); + + void Render( + const Geometry* geo + ); + +private: + GLuint m_programID; +}; + +#endif diff --git a/src/viewer.cpp b/src/viewer.cpp new file mode 100644 index 00000000..375f15b1 --- /dev/null +++ b/src/viewer.cpp @@ -0,0 +1,105 @@ +// +// viewer.cpp + +#include "viewer.h" + +Viewer::Viewer() +{ + screenSize.x = 800; + screenSize.y = 600; + + this->Init(); + +} + +Viewer::Viewer( + int width, + int height + ) +{ + screenSize.x = width; + screenSize.y = height; + + this->Init(); +} + + +void +Viewer::Init() +{ + // Initialise GLFW + if( !glfwInit() ) + { + fprintf( stderr, "Failed to initialize GLFW\n" ); + getchar(); + } + + glfwWindowHint(GLFW_SAMPLES, 4); // antiliasing 4 samples + glfwWindowHint(GLFW_RESIZABLE,GL_FALSE); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Get version 3.3 + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Make sure with don't use old profile + + // Open a window and create its OpenGL context + m_window = glfwCreateWindow( screenSize.x, screenSize.y, "Trung's Fluid Sim", NULL, NULL); + if( m_window == NULL ){ + fprintf( stderr, "Failed to open GLFW window.\n" ); + getchar(); + this->CleanUp(); + } + + glfwMakeContextCurrent(m_window); + + // Initialize GLEW + + // *N.B*: Strange we have to do this to make glGenVertexArrays to work? + // + // GLEW obtains information on the supported extensions from the graphics driver. Experimental or pre-release drivers, however, might not report every available extension through the standard mechanism, in which case GLEW will report it unsupported. To circumvent this situation, the glewExperimental global switch can be turned on by setting it to GL_TRUE before calling glewInit(), which ensures that all extensions with valid entry points will be exposed. + glewExperimental = GL_TRUE; + if (glewInit() != GLEW_OK) { + fprintf(stderr, "Failed to initialize GLEW\n"); + getchar(); + this->CleanUp(); + } + + // Ensure we can capture the escape key being pressed below + glfwSetInputMode(m_window, GLFW_STICKY_KEYS, GL_TRUE); + + // Create and compile GLSL program from the shaders + m_program = ShaderProgram("../src/glsl/vert_bare.glsl", "../src/glsl/frag_bare.glsl"); + + // Initialize scene + m_scene = Scene(); + m_scene.InitFromTestScene(); + + // White background + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); +} + + +void +Viewer::Render() +{ + do { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + m_program.Render(&m_scene.RootGeometry()); + + // Swap buffers + glfwSwapBuffers(m_window); + glfwPollEvents(); + } + // Check if the ESC key was pressed or the window was closed + while( + glfwGetKey(m_window, GLFW_KEY_ESCAPE ) != GLFW_PRESS && + glfwWindowShouldClose(m_window) == 0 ); +} + +void +Viewer::CleanUp() +{ + // Close OpenGL window and terminate GLFW + glfwTerminate(); +} + diff --git a/src/viewer.h b/src/viewer.h new file mode 100644 index 00000000..2a81e184 --- /dev/null +++ b/src/viewer.h @@ -0,0 +1,41 @@ +#ifndef viewer_hpp +#define viewer_hpp + +// C++ libraries + +#include + +// GL libraries + +#include +#include +#include + +#include "shaderProgram.h" +#include "scene/scene.h" + +class Viewer +{ +public: + glm::vec2 screenSize; + + Viewer(); + Viewer( + int width = 800, + int height = 600 + ); + + virtual void Render(); + virtual void CleanUp(); + +private: + void + Init(); + + GLFWwindow* m_window = NULL; + ShaderProgram m_program; + Scene m_scene; + +}; + +#endif /* viewer_hpp */ diff --git a/src/viewer/viewer.cpp b/src/viewer/viewer.cpp deleted file mode 100644 index dbe52e7e..00000000 --- a/src/viewer/viewer.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// -// viewer.cpp - -#include "viewer.hpp" - diff --git a/src/viewer/viewer.hpp b/src/viewer/viewer.hpp deleted file mode 100644 index 3b380e09..00000000 --- a/src/viewer/viewer.hpp +++ /dev/null @@ -1,5 +0,0 @@ -#ifndef viewer_hpp -#define viewer_hpp - - -#endif /* viewer_hpp */ From 894ed0ec200bbbbe11664762b39802cc5027989e Mon Sep 17 00:00:00 2001 From: Trung Le Date: Sat, 27 Feb 2016 02:16:16 -0500 Subject: [PATCH 04/64] Added enable and disable attributes --- src/geometry/box.cpp | 56 ++++++++++++++++++++------------------- src/geometry/geometry.cpp | 17 +++++++++--- src/geometry/geometry.h | 5 ++-- src/shaderProgram.cpp | 46 +++----------------------------- src/viewer.cpp | 2 -- 5 files changed, 49 insertions(+), 77 deletions(-) diff --git a/src/geometry/box.cpp b/src/geometry/box.cpp index eefa15b4..188650f0 100644 --- a/src/geometry/box.cpp +++ b/src/geometry/box.cpp @@ -107,33 +107,35 @@ Box::Create() // @todo: Is it faster to allocate all the buffers ahead of time? - // // -- Position - // glGenBuffers(1, &m_posBuffer); - // glBindBuffer(GL_ARRAY_BUFFER, m_posBuffer); - // glBufferData(GL_ARRAY_BUFFER, m_positions.size() * sizeof(glm::vec3), (void*)&(m_positions[0]), GL_STATIC_DRAW); - // glBindBuffer(GL_ARRAY_BUFFER, NULL); - - // // @todo: - // // glGenBuffers(1, &m_norBuffer); - - // // @todo: - // // glGenBuffers(1, &m_colBuffer); - - // // -- Index - // glGenBuffers(1, &m_idxBuffer); - // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_idxBuffer); - // glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indices.size() * sizeof(unsigned int), (void*)&(m_indices[0]), GL_STATIC_DRAW); - // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NULL); - - // Draw a triangle - static const GLfloat g_vertex_buffer_data[] = { - -1.0f, -1.0f, 0.0f, - 1.0f, -1.0f, 0.0f, - 0.0f, 1.0f, 0.0f - }; - glBindBuffer(GL_ARRAY_BUFFER, m_posBuffer); - glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW); + // -- Position - glBindVertexArray(0); + glGenBuffers(1, &m_posBuffer); + glBindBuffer(GL_ARRAY_BUFFER, m_posBuffer); + glBufferData( + GL_ARRAY_BUFFER, + sizeof(m_positions), + m_positions, + GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, NULL); + + // @todo: + // glGenBuffers(1, &m_norBuffer); + + // @todo: + // glGenBuffers(1, &m_colBuffer); + + // -- Index + + glGenBuffers(1, &m_idxBuffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_idxBuffer); + glBufferData( + GL_ELEMENT_ARRAY_BUFFER, + sizeof(m_indices), + m_indices, + GL_STATIC_DRAW + ); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NULL); + + glBindVertexArray(NULL); } diff --git a/src/geometry/geometry.cpp b/src/geometry/geometry.cpp index a20329b1..224fec1e 100644 --- a/src/geometry/geometry.cpp +++ b/src/geometry/geometry.cpp @@ -12,20 +12,31 @@ Geometry::DrawMode() const } } -int +GLsizei Geometry::ElementCount() const { - return m_indices.size(); + return 0; } void -Geometry::Bind() const +Geometry::EnableVertexAttributes() const { glBindVertexArray(m_vao); glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, m_posBuffer); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_idxBuffer); +} + +void +Geometry::DisableVertexAttributes() const +{ + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NULL); + + glDisableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, NULL); + glBindVertexArray(NULL); } void diff --git a/src/geometry/geometry.h b/src/geometry/geometry.h index 15f28f2a..a727dc13 100644 --- a/src/geometry/geometry.h +++ b/src/geometry/geometry.h @@ -26,10 +26,11 @@ class Geometry // -- OpenGL helpers virtual GLenum DrawMode() const; - virtual int ElementCount() const; + virtual GLsizei ElementCount() const; // Bind the vertex array object for this geometry - virtual void Bind() const; + virtual void EnableVertexAttributes() const; + virtual void DisableVertexAttributes() const; // -- Transformations virtual void SetLocalTransformation(const glm::mat4); diff --git a/src/shaderProgram.cpp b/src/shaderProgram.cpp index e4192762..d409499c 100644 --- a/src/shaderProgram.cpp +++ b/src/shaderProgram.cpp @@ -14,47 +14,7 @@ void ShaderProgram::Render( ) { glUseProgram(m_programID); - - // GLuint VertexArrayID = 0; - // glGenVertexArrays(1, &VertexArrayID); - // glBindVertexArray(VertexArrayID); - - // // Draw a triangle - // static const GLfloat g_vertex_buffer_data[] = { - // -1.0f, -1.0f, 0.0f, - // 1.0f, -1.0f, 0.0f, - // 0.0f, 1.0f, 0.0f - // }; - - // // Identify vertex buffer - // GLuint vertexbuffer; - - - // // Generate one and put the result in vertexbuffer - // glGenBuffers(1, &vertexbuffer); - - // glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); - // // Give vertices to openGL - // glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW); - - // // First attribute buffer - // glEnableVertexAttribArray(0); - // glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer); - // glVertexAttribPointer( - // 0, // Attribute 0, no reason just need to match the layout in the shader - // 3, // size - // GL_FLOAT, // type - // GL_FALSE, // normalized? - // 0, // stride - // (void*)0 // array buffer offset - // ); - - // glPointSize(5.0f); - - // // Draw the triangle! - // glDrawArrays(GL_TRIANGLES, 0, 3); // vertex 3, count 3 -> 1 triangle - // glDisableVertexAttribArray(0); - geo->Bind(); - // glDrawElements(GL_POINTS, geo.ElementCount(), GL_UNSIGNED_SHORT, NULL); - glDrawArrays(GL_TRIANGLES, 0, 3); // vertex 3, count 3 -> 1 triangle + geo->EnableVertexAttributes(); + glDrawElements(geo->DrawMode(), geo->ElementCount(), GL_UNSIGNED_SHORT, NULL); + geo->DisableVertexAttributes(); } diff --git a/src/viewer.cpp b/src/viewer.cpp index 375f15b1..b1517bb2 100644 --- a/src/viewer.cpp +++ b/src/viewer.cpp @@ -23,7 +23,6 @@ Viewer::Viewer( this->Init(); } - void Viewer::Init() { @@ -77,7 +76,6 @@ Viewer::Init() glClearColor(0.0f, 0.0f, 0.0f, 0.0f); } - void Viewer::Render() { From b85e3eafea6c6b9b1c34d4c4d537214438516b8d Mon Sep 17 00:00:00 2001 From: Trung Le Date: Sat, 27 Feb 2016 02:22:50 -0500 Subject: [PATCH 05/64] Finished Box class --- src/geometry/box.cpp | 8 ++++---- src/geometry/geometry.cpp | 2 +- src/geometry/geometry.h | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/geometry/box.cpp b/src/geometry/box.cpp index 188650f0..ec3ca60e 100644 --- a/src/geometry/box.cpp +++ b/src/geometry/box.cpp @@ -113,8 +113,8 @@ Box::Create() glBindBuffer(GL_ARRAY_BUFFER, m_posBuffer); glBufferData( GL_ARRAY_BUFFER, - sizeof(m_positions), - m_positions, + m_positions.size() * sizeof(glm::vec3), + &m_positions[0], GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, NULL); @@ -130,8 +130,8 @@ Box::Create() glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_idxBuffer); glBufferData( GL_ELEMENT_ARRAY_BUFFER, - sizeof(m_indices), - m_indices, + m_indices.size() * sizeof(GLushort), + &m_indices[0], GL_STATIC_DRAW ); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NULL); diff --git a/src/geometry/geometry.cpp b/src/geometry/geometry.cpp index 224fec1e..bd7d657e 100644 --- a/src/geometry/geometry.cpp +++ b/src/geometry/geometry.cpp @@ -15,7 +15,7 @@ Geometry::DrawMode() const GLsizei Geometry::ElementCount() const { - return 0; + return m_indices.size(); } void diff --git a/src/geometry/geometry.h b/src/geometry/geometry.h index a727dc13..93616fa9 100644 --- a/src/geometry/geometry.h +++ b/src/geometry/geometry.h @@ -45,7 +45,7 @@ class Geometry vector m_positions; vector m_normals; vector m_colors; - vector m_indices; + vector m_indices; // Transformation glm::mat4 m_localTransform; From 9fbe608c37322462fdffe1f5129f6e3dff0d7919 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Sat, 27 Feb 2016 16:21:10 -0500 Subject: [PATCH 06/64] Added jsconcpp as submodule --- .gitmodules | 3 +++ src/thirdparty/jsoncpp | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 src/thirdparty/jsoncpp diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..234a8024 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/thirdparty/jsoncpp"] + path = src/thirdparty/jsoncpp + url = git@github.com:open-source-parsers/jsoncpp.git diff --git a/src/thirdparty/jsoncpp b/src/thirdparty/jsoncpp new file mode 160000 index 00000000..b4d2b658 --- /dev/null +++ b/src/thirdparty/jsoncpp @@ -0,0 +1 @@ +Subproject commit b4d2b658416b8087f89599daac9b9c36c6c56616 From 4d75985676c336a0c7a2fd7da7881b01b011be42 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Sat, 27 Feb 2016 16:21:44 -0500 Subject: [PATCH 07/64] Updated README. Added Camera class. Added wireframe view for box geometry --- src/camera/camera.cpp | 62 +++++++++++++++++++++++++++++++++++---- src/camera/camera.h | 41 ++++++++++++++++++++++++++ src/camera/camera.hpp | 10 ------- src/geometry/box.cpp | 47 ++++++++++++++++++----------- src/geometry/box.h | 3 -- src/geometry/geometry.cpp | 27 ++++++++++++----- src/geometry/geometry.h | 21 +++++++------ src/glsl/vert_bare.glsl | 3 +- src/main.cpp | 2 +- src/mathConstants.h | 7 +++++ src/scene/scene.cpp | 25 ++++++++++++++++ src/scene/scene.h | 2 ++ src/shaderProgram.cpp | 40 ++++++++++++++++++++++--- src/shaderProgram.h | 8 ++++- src/viewer.cpp | 18 +++++++----- src/viewer.h | 22 ++++++++++---- 16 files changed, 265 insertions(+), 73 deletions(-) create mode 100644 src/camera/camera.h delete mode 100644 src/camera/camera.hpp create mode 100644 src/mathConstants.h diff --git a/src/camera/camera.cpp b/src/camera/camera.cpp index fb246b12..90844872 100644 --- a/src/camera/camera.cpp +++ b/src/camera/camera.cpp @@ -1,6 +1,58 @@ -// -// camera.cpp -// Thanda -// +#include "camera.h" +#include "../mathConstants.h" + +Camera::Camera(): + Camera(800, 600) +{ +} + +Camera::Camera( + float width, + float height + ) : m_width(width), + m_height(height), + m_fov(45), + m_eye(glm::vec3(0.0f, 0.0f, -12.0f)), + m_target(glm::vec3(0.0f)), + m_up(glm::vec3(0.0f, 1.0f, 0.0f)), + m_right(glm::vec3(1.0f, 0.0f, 0.0f)), + m_nearClip(1.0f), + m_farClip(1000.0f) +{ + m_forward = m_target - m_eye; + RecomputeAttributes(); +} + +void +Camera::RecomputeAttributes() +{ + m_view = glm::lookAt( + m_eye, + m_target, + m_up + ); + + m_perspective = glm::perspective( + m_fov, + m_width / m_height, + m_nearClip, + m_farClip + ); +} + +glm::mat4 +Camera::GetViewProj() const +{ + return m_perspective * m_view; +} + +void +Camera::RotateAboutRight( + float deg + ) +{ + +} + + -#include "camera.hpp" diff --git a/src/camera/camera.h b/src/camera/camera.h new file mode 100644 index 00000000..3c2b56b5 --- /dev/null +++ b/src/camera/camera.h @@ -0,0 +1,41 @@ +#ifndef CAMERA_H +#define CAMERA_H + +#include +#include + +class Camera +{ +public: + Camera(); + Camera(float width, float height); + + // -- Camera attributes + glm::mat4 GetViewProj() const; + void RecomputeAttributes(); + + // -- Camera movement + void RotateAboutRight(float deg); + void RotateAboutUp(float deg); + void Zoom(float amount); + +protected: + + // -- Attributes + float m_width; + float m_height; + float m_fov; + glm::vec3 m_eye; + glm::vec3 m_target; + glm::vec3 m_forward; + glm::vec3 m_up; + glm::vec3 m_right; + float m_nearClip; + float m_farClip; + + glm::mat4 m_view; + glm::mat4 m_perspective; +}; + + +#endif /* CAMERA_H */ diff --git a/src/camera/camera.hpp b/src/camera/camera.hpp deleted file mode 100644 index 59ad2b12..00000000 --- a/src/camera/camera.hpp +++ /dev/null @@ -1,10 +0,0 @@ -// -// camera.hpp -// Thanda -// - -#ifndef camera_hpp -#define camera_hpp - - -#endif /* camera_hpp */ diff --git a/src/geometry/box.cpp b/src/geometry/box.cpp index ec3ca60e..fcfaf82a 100644 --- a/src/geometry/box.cpp +++ b/src/geometry/box.cpp @@ -1,17 +1,9 @@ #include "box.h" -Box::Box( - ViewMode viewMode - ) -{ - m_viewMode = viewMode; - InitPositions(); - InitIndices(); -} - void Box::InitPositions() { + m_positions.clear(); //Front face //UR m_positions.push_back(glm::vec3(0.5f, 0.5f, 0.5f)); @@ -88,20 +80,41 @@ Box::InitColors() void Box::InitIndices() { - // Two triangles for each faces - for(int i = 0; i < 6; i++){ - m_indices.push_back(i * 4); - m_indices.push_back(i * 4 + 1); - m_indices.push_back(i * 4 + 2); - m_indices.push_back(i * 4); - m_indices.push_back(i * 4 + 2); - m_indices.push_back(i * 4 + 3); + m_indices.clear(); + if (m_drawMode == DrawMode_Wireframe) + { + // Square for each face + for(int i = 0; i < 6; i++){ + m_indices.push_back(i * 4); + m_indices.push_back(i * 4 + 1); + m_indices.push_back(i * 4 + 1); + m_indices.push_back(i * 4 + 2); + m_indices.push_back(i * 4 + 2); + m_indices.push_back(i * 4 + 3); + m_indices.push_back(i * 4 + 3); + m_indices.push_back(i * 4); + } + } + else if (m_drawMode == DrawMode_Shaded) + { + // Two triangles for each faces + for(int i = 0; i < 6; i++){ + m_indices.push_back(i * 4); + m_indices.push_back(i * 4 + 1); + m_indices.push_back(i * 4 + 2); + m_indices.push_back(i * 4); + m_indices.push_back(i * 4 + 2); + m_indices.push_back(i * 4 + 3); + } } } void Box::Create() { + InitPositions(); + InitIndices(); + glGenVertexArrays(1, &m_vao); glBindVertexArray(m_vao); diff --git a/src/geometry/box.h b/src/geometry/box.h index 0c13eec4..7202afa8 100644 --- a/src/geometry/box.h +++ b/src/geometry/box.h @@ -6,8 +6,6 @@ class Box : public Geometry { public: - Box(ViewMode viewMode = Shaded); - virtual ~Box() {}; virtual void Create(); protected: @@ -17,5 +15,4 @@ class Box : public Geometry virtual void InitIndices(); }; - #endif diff --git a/src/geometry/geometry.cpp b/src/geometry/geometry.cpp index bd7d657e..ea56e1b0 100644 --- a/src/geometry/geometry.cpp +++ b/src/geometry/geometry.cpp @@ -1,12 +1,20 @@ #include "geometry.h" +void +Geometry::SetDrawMode( + DrawMode drawMode + ) +{ + m_drawMode = drawMode; +} + GLenum -Geometry::DrawMode() const +Geometry::GLDrawMode() const { - switch (m_viewMode) { - case WireFrame: + switch(m_drawMode) { + case DrawMode_Wireframe: return GL_LINES; - case Shaded: + case DrawMode_Shaded: default: return GL_TRIANGLES; } @@ -22,10 +30,13 @@ void Geometry::EnableVertexAttributes() const { glBindVertexArray(m_vao); + + // Enable vertex attributes glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, m_posBuffer); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0); + // Bind element buffer glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_idxBuffer); } @@ -41,18 +52,18 @@ Geometry::DisableVertexAttributes() const void Geometry::SetLocalTransformation( - const glm::mat4 localTrans + const glm::mat4& localTrans ) { - + m_localTransform = localTrans; } void Geometry::SetGlobalTransformation( - const glm::mat4 + const glm::mat4& globalTrans ) { - + m_globalTransform = globalTrans; } glm::mat4 diff --git a/src/geometry/geometry.h b/src/geometry/geometry.h index 93616fa9..25e0bcbe 100644 --- a/src/geometry/geometry.h +++ b/src/geometry/geometry.h @@ -6,26 +6,29 @@ #include #include #include +#include // C++ #include -using namespace std; +#include "../mathConstants.h" typedef enum { - WireFrame, - Shaded -} ViewMode; + DrawMode_Wireframe, + DrawMode_Shaded +} DrawMode; + +using namespace std; class Geometry { public: - Geometry () { m_viewMode = Shaded; }; virtual void Create() = 0; // -- OpenGL helpers - virtual GLenum DrawMode() const; + virtual void SetDrawMode(DrawMode); + virtual GLenum GLDrawMode() const; virtual GLsizei ElementCount() const; // Bind the vertex array object for this geometry @@ -33,13 +36,13 @@ class Geometry virtual void DisableVertexAttributes() const; // -- Transformations - virtual void SetLocalTransformation(const glm::mat4); - virtual void SetGlobalTransformation(const glm::mat4); + virtual void SetLocalTransformation(const glm::mat4&); + virtual void SetGlobalTransformation(const glm::mat4&); virtual glm::mat4 GetLocalTransformation() const; virtual glm::mat4 GetGlobalTransformation() const; protected: - ViewMode m_viewMode; + DrawMode m_drawMode = DrawMode_Shaded; // Data vector m_positions; diff --git a/src/glsl/vert_bare.glsl b/src/glsl/vert_bare.glsl index b05f2014..ee583dd9 100644 --- a/src/glsl/vert_bare.glsl +++ b/src/glsl/vert_bare.glsl @@ -12,7 +12,6 @@ in vec3 a_position; void main() { - //vec4 position = u_viewProj * u_model * vec4(a_position, 1.0); - vec4 position = vec4(a_position, 1.0); + vec4 position = u_viewProj * u_model * vec4(a_position, 1.0); gl_Position = position; } diff --git a/src/main.cpp b/src/main.cpp index 973ed277..af31e58a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,7 +8,7 @@ using namespace std; int main() { Viewer view(800, 600); - view.Render(); + view.Update(); view.CleanUp(); return 0; diff --git a/src/mathConstants.h b/src/mathConstants.h new file mode 100644 index 00000000..0e075437 --- /dev/null +++ b/src/mathConstants.h @@ -0,0 +1,7 @@ +// Math constants + +static const float PI = 3.14159265358979323846f; +static const float TWO_PI = 2 * PI; +static const float INV_PI = 1.0f / PI; +static const float DEG2RAD = PI / 180.f; +static const float RAD2DEG = 180.f / PI; diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 27759f42..f87a37f2 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -12,6 +12,25 @@ void Scene::InitFromTestScene() { m_box = Box(); + + glm::mat4 rot = glm::rotate( + glm::mat4(1.0f), + 0.02f, + glm::vec3(1.0f, 1.0f, 0.0f) + ); + + glm::mat4 scale = glm::scale( + glm::mat4(1.0f), + glm::vec3(2.5f, 2.5f, 2.5f) + ); + + glm::mat4 translate = glm::translate( + glm::mat4(1.0f), + glm::vec3(-1.0f, 0.0f, 0.0f) + ); + + m_box.SetLocalTransformation(translate * rot * scale); + m_box.SetDrawMode(DrawMode_Wireframe); m_box.Create(); } @@ -21,6 +40,12 @@ Scene::InitFromJson() } +void +Scene::Update() +{ + +} + const Geometry& Scene::RootGeometry() const { diff --git a/src/scene/scene.h b/src/scene/scene.h index e2fca543..47e3ea49 100644 --- a/src/scene/scene.h +++ b/src/scene/scene.h @@ -10,6 +10,8 @@ class Scene ~Scene(); virtual void InitFromTestScene(); virtual void InitFromJson(); + + virtual void Update(); virtual const Geometry& RootGeometry() const; protected: diff --git a/src/shaderProgram.cpp b/src/shaderProgram.cpp index d409499c..9576baf4 100644 --- a/src/shaderProgram.cpp +++ b/src/shaderProgram.cpp @@ -7,14 +7,46 @@ ShaderProgram::ShaderProgram( ) { m_programID = LoadShaders(vertFilePath, fragFilePath); + + m_unifModel = glGetUniformLocation(m_programID, "u_model"); + m_unifViewProj = glGetUniformLocation(m_programID, "u_viewProj"); } void ShaderProgram::Render( - const Geometry* geo + const Camera& camera, + const Geometry& geo ) { glUseProgram(m_programID); - geo->EnableVertexAttributes(); - glDrawElements(geo->DrawMode(), geo->ElementCount(), GL_UNSIGNED_SHORT, NULL); - geo->DisableVertexAttributes(); + + // Enable attributes + geo.EnableVertexAttributes(); + + // Set uniforms + + if (m_unifModel != -1) { + glUniformMatrix4fv( + m_unifModel, + 1, + GL_FALSE, + &(geo.GetLocalTransformation()[0][0]) + ); + } + + if (m_unifModel != -1) { + glUniformMatrix4fv( + m_unifViewProj, + 1, + GL_FALSE, + &(camera.GetViewProj()[0][0]) + ); + } + // Render + glDrawElements( + geo.GLDrawMode(), + geo.ElementCount(), + GL_UNSIGNED_SHORT, NULL + ); + + geo.DisableVertexAttributes(); } diff --git a/src/shaderProgram.h b/src/shaderProgram.h index 29410797..172e433b 100644 --- a/src/shaderProgram.h +++ b/src/shaderProgram.h @@ -3,6 +3,7 @@ #include "loadShaders.h" #include "geometry/geometry.h" +#include "camera/camera.h" class ShaderProgram { @@ -15,11 +16,16 @@ class ShaderProgram ); void Render( - const Geometry* geo + const Camera&, + const Geometry& ); private: GLuint m_programID; + + // Uniform locations + int m_unifModel; + int m_unifViewProj; }; #endif diff --git a/src/viewer.cpp b/src/viewer.cpp index b1517bb2..1fc759fd 100644 --- a/src/viewer.cpp +++ b/src/viewer.cpp @@ -5,8 +5,8 @@ Viewer::Viewer() { - screenSize.x = 800; - screenSize.y = 600; + m_width = 800; + m_height = 600; this->Init(); @@ -17,8 +17,8 @@ Viewer::Viewer( int height ) { - screenSize.x = width; - screenSize.y = height; + m_width = width; + m_height = height; this->Init(); } @@ -41,7 +41,7 @@ Viewer::Init() glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Make sure with don't use old profile // Open a window and create its OpenGL context - m_window = glfwCreateWindow( screenSize.x, screenSize.y, "Trung's Fluid Sim", NULL, NULL); + m_window = glfwCreateWindow( m_width, m_height, "Trung's Fluid Sim", NULL, NULL); if( m_window == NULL ){ fprintf( stderr, "Failed to open GLFW window.\n" ); getchar(); @@ -72,17 +72,21 @@ Viewer::Init() m_scene = Scene(); m_scene.InitFromTestScene(); + // Initialize camera + m_camera = Camera(m_width, m_height); + // White background glClearColor(0.0f, 0.0f, 0.0f, 0.0f); } void -Viewer::Render() +Viewer::Update() { do { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - m_program.Render(&m_scene.RootGeometry()); + m_scene.Update(); + m_program.Render(m_camera, m_scene.RootGeometry()); // Swap buffers glfwSwapBuffers(m_window); diff --git a/src/viewer.h b/src/viewer.h index 2a81e184..d4316c82 100644 --- a/src/viewer.h +++ b/src/viewer.h @@ -13,28 +13,38 @@ #include "shaderProgram.h" #include "scene/scene.h" +#include "camera/camera.h" class Viewer { public: - glm::vec2 screenSize; - Viewer(); Viewer( int width = 800, int height = 600 ); - virtual void Render(); + virtual void Update(); virtual void CleanUp(); -private: - void - Init(); + float Width() { + return m_width; + } + + float Height() { + return m_height; + } + +protected: + void Init(); + + float m_width; + float m_height; GLFWwindow* m_window = NULL; ShaderProgram m_program; Scene m_scene; + Camera m_camera; }; From 50ca6234df4ecddb8d55284ed8f88e5e97cd80f7 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Sat, 27 Feb 2016 16:34:46 -0500 Subject: [PATCH 08/64] Updated CMakeLists.txt to include jsoncpp and also include search directories for other header files --- CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e87391f..0d6b9028 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,11 @@ include_directories( ${NUPARU}/src ) +# include source directories +include_directories( + src/ +) + # Add path for pre-compiled libraries here (we will later link them with our compiled source) # Add Nuparu library to path for OSX, linux and windows if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") @@ -85,6 +90,7 @@ endif() # Add source files you want to compile (.cpp) set(CORE_SRC + src/thirdparty/jsoncpp/dist/jsoncpp.cpp src/loadShaders.cpp src/utility.cpp src/main.cpp From 44c157e15f7abb8f0ea7c198efe34275fd43699a Mon Sep 17 00:00:00 2001 From: Trung Le Date: Sat, 27 Feb 2016 16:35:23 -0500 Subject: [PATCH 09/64] [README] - Updated README to include links to other thirdparty codes used --- README.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0b3a5bf2..e2f4c8d7 100644 --- a/README.md +++ b/README.md @@ -13,4 +13,13 @@ In the body of the pull request, include a link to your repository. Submit on canvas with a direct link to your pull request on GitHub -And you're done! \ No newline at end of file +And you're done! + + +# Third Party + +## glfw, [Website](http://www.glfw.org/) +## glew, [Website](http://glew.sourceforge.net/) +## glm, [Website](http://glm.g-truc.net/0.9.7/index.html) +## opengl-tutorial.org, [Website](http://www.opengl-tutorial.org/) +## jsoncpp, [Github](https://github.com/open-source-parsers/jsoncpp) From 7ee9c9ce30071886a9ddfb59eb6466d523c3d661 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Sat, 27 Feb 2016 17:54:57 -0500 Subject: [PATCH 10/64] [scene][geo] - Parse json into scene view. Added transformation for geo --- src/camera/camera.cpp | 2 +- src/fluidSolver/fluidSolver.cpp | 7 +-- src/geometry/geometry.cpp | 33 ++++++++++++ src/geometry/geometry.h | 15 ++++-- src/scene/scene.cpp | 93 +++++++++++++++++++++++++-------- src/scene/scene.h | 6 +-- src/scene/scene.json | 24 ++++----- src/viewer.cpp | 3 +- 8 files changed, 132 insertions(+), 51 deletions(-) diff --git a/src/camera/camera.cpp b/src/camera/camera.cpp index 90844872..5e4d166b 100644 --- a/src/camera/camera.cpp +++ b/src/camera/camera.cpp @@ -1,5 +1,5 @@ #include "camera.h" -#include "../mathConstants.h" +#include Camera::Camera(): Camera(800, 600) diff --git a/src/fluidSolver/fluidSolver.cpp b/src/fluidSolver/fluidSolver.cpp index 9c9a1663..f846ecea 100644 --- a/src/fluidSolver/fluidSolver.cpp +++ b/src/fluidSolver/fluidSolver.cpp @@ -1,6 +1 @@ -// -// fluidSolver.cpp -// Thanda - - -#include "fluidSolver.hpp" +#include "fluidSolver.h" diff --git a/src/geometry/geometry.cpp b/src/geometry/geometry.cpp index ea56e1b0..2c6c5667 100644 --- a/src/geometry/geometry.cpp +++ b/src/geometry/geometry.cpp @@ -50,6 +50,39 @@ Geometry::DisableVertexAttributes() const glBindVertexArray(NULL); } +void +Geometry::Translate( + const float& x, + const float& y, + const float& z + ) +{ + m_localTransform = glm::translate(m_localTransform, glm::vec3(x, y, z)); +} + +void +Geometry::Rotate( + const float& radx, + const float& rady, + const float& radz + ) +{ + m_localTransform = glm::rotate(m_localTransform, radx, glm::vec3(1.0f, 0.0f, 0.0f)); + m_localTransform = glm::rotate(m_localTransform, rady, glm::vec3(0.0f, 1.0f, 0.0f)); + m_localTransform = glm::rotate(m_localTransform, radz, glm::vec3(0.0f, 0.0f, 1.0f)); +} + +void +Geometry::Scale( + const float& scalex, + const float& scaley, + const float& scalez + ) +{ + m_localTransform = glm::scale(m_localTransform, glm::vec3(scalex, scaley, scalez)); +} + + void Geometry::SetLocalTransformation( const glm::mat4& localTrans diff --git a/src/geometry/geometry.h b/src/geometry/geometry.h index 25e0bcbe..8982acef 100644 --- a/src/geometry/geometry.h +++ b/src/geometry/geometry.h @@ -12,7 +12,9 @@ #include -#include "../mathConstants.h" +// Custom + +#include typedef enum { DrawMode_Wireframe, @@ -36,10 +38,13 @@ class Geometry virtual void DisableVertexAttributes() const; // -- Transformations - virtual void SetLocalTransformation(const glm::mat4&); - virtual void SetGlobalTransformation(const glm::mat4&); - virtual glm::mat4 GetLocalTransformation() const; - virtual glm::mat4 GetGlobalTransformation() const; + void Translate(const float& x, const float& y, const float& z); + void Rotate(const float& radx, const float& rady, const float& radz); + void Scale(const float& scaleX, const float& scaleY, const float& scaleZ); + void SetLocalTransformation(const glm::mat4&); + void SetGlobalTransformation(const glm::mat4&); + glm::mat4 GetLocalTransformation() const; + glm::mat4 GetGlobalTransformation() const; protected: DrawMode m_drawMode = DrawMode_Shaded; diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index f87a37f2..0b129015 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -1,3 +1,8 @@ +#include +#include +#include +#include + #include "scene.h" Scene::Scene() @@ -11,43 +16,85 @@ Scene::~Scene() void Scene::InitFromTestScene() { - m_box = Box(); - - glm::mat4 rot = glm::rotate( - glm::mat4(1.0f), - 0.02f, - glm::vec3(1.0f, 1.0f, 0.0f) - ); - - glm::mat4 scale = glm::scale( - glm::mat4(1.0f), - glm::vec3(2.5f, 2.5f, 2.5f) - ); - - glm::mat4 translate = glm::translate( - glm::mat4(1.0f), - glm::vec3(-1.0f, 0.0f, 0.0f) - ); - - m_box.SetLocalTransformation(translate * rot * scale); - m_box.SetDrawMode(DrawMode_Wireframe); - m_box.Create(); + m_box = Box(); + + glm::mat4 rot = glm::rotate( + glm::mat4(1.0f), + 0.02f, + glm::vec3(1.0f, 1.0f, 0.0f) + ); + + glm::mat4 scale = glm::scale( + glm::mat4(1.0f), + glm::vec3(2.5f, 2.5f, 2.5f) + ); + + glm::mat4 translate = glm::translate( + glm::mat4(1.0f), + glm::vec3(-1.0f, 0.0f, 0.0f) + ); + + m_box.SetLocalTransformation(translate * rot * scale); + m_box.SetDrawMode(DrawMode_Wireframe); + m_box.Create(); } -void +bool Scene::InitFromJson() { + // -- Open file + std::string jsonText; + std::string line; + std::ifstream jsonFile("../src/scene/scene.json"); + if (jsonFile.is_open() == false) { + std::cout << "JSON file not open." << endl; + return false; + } + while (std::getline(jsonFile, line)) { + jsonText += line; + } + // -- Parse json + Json::Value root; + Json::Reader reader; + bool success = reader.parse(jsonText, root, false); + if (!success) + { + std::cout << "Parsing failed." << endl; + + // report to the user the failure and their locations in the document. + std::cout << reader.getFormatedErrorMessages() << endl; + return false; + } + + // -- Get container information + const Json::Value containerDim = root["containerDim"]; + + m_box = Box(); + m_box.Scale( + containerDim["scaleX"].asFloat(), + containerDim["scaleY"].asFloat(), + containerDim["scaleZ"].asFloat() + ); + m_box.SetDrawMode(DrawMode_Wireframe); + m_box.Create(); + + return true; } void Scene::Update() { +#ifdef ANIMATE + // Rotate bounding box + m_box.Rotate(0.02f, 0.01f, 0.02); +#endif + } const Geometry& Scene::RootGeometry() const { - return m_box; + return m_box; } diff --git a/src/scene/scene.h b/src/scene/scene.h index 47e3ea49..5532edff 100644 --- a/src/scene/scene.h +++ b/src/scene/scene.h @@ -1,15 +1,15 @@ #ifndef SCENE_H #define SCENE_H -#include "../geometry/box.h" +#include class Scene { public: Scene(); ~Scene(); - virtual void InitFromTestScene(); - virtual void InitFromJson(); + void InitFromTestScene(); + bool InitFromJson(); virtual void Update(); virtual const Geometry& RootGeometry() const; diff --git a/src/scene/scene.json b/src/scene/scene.json index 789b4a42..b2c62a66 100644 --- a/src/scene/scene.json +++ b/src/scene/scene.json @@ -1,13 +1,13 @@ { - "containerDim" : { - "scaleX" : 5.0, - "scaleY" : 5.0, - "scaleZ" : 5.0 - }, - "particleDim" : { - "boundX" : 3.7, - "boundY" : 3.7, - "boundZ" : 3.7 - }, - "particleSeparation" : 0.1 -} \ No newline at end of file + "containerDim" : { + "scaleX" : 5.0, + "scaleY" : 5.0, + "scaleZ" : 5.0 + }, + "particleDim" : { + "boundX" : 3.7, + "boundY" : 3.7, + "boundZ" : 3.7 + }, + "particleSeparation" : 0.1 +} diff --git a/src/viewer.cpp b/src/viewer.cpp index 1fc759fd..e5a05f43 100644 --- a/src/viewer.cpp +++ b/src/viewer.cpp @@ -70,7 +70,8 @@ Viewer::Init() // Initialize scene m_scene = Scene(); - m_scene.InitFromTestScene(); + // m_scene.InitFromTestScene(); + m_scene.InitFromJson(); // Initialize camera m_camera = Camera(m_width, m_height); From f2c596531281097e4345ce9b5a685e8fec83e726 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Sat, 27 Feb 2016 23:42:50 -0500 Subject: [PATCH 11/64] [fluidsolver] - Added barebone code for fluid solver --- src/fluidSolver/fluidSolver.cpp | 43 ++++++++++++++++++++++++++ src/fluidSolver/fluidSolver.h | 53 +++++++++++++++++++++++++++++++++ src/fluidSolver/fluidSolver.hpp | 9 ------ src/mathConstants.h | 6 ++++ src/scene/scene.h | 1 + 5 files changed, 103 insertions(+), 9 deletions(-) create mode 100644 src/fluidSolver/fluidSolver.h delete mode 100644 src/fluidSolver/fluidSolver.hpp diff --git a/src/fluidSolver/fluidSolver.cpp b/src/fluidSolver/fluidSolver.cpp index f846ecea..1067a102 100644 --- a/src/fluidSolver/fluidSolver.cpp +++ b/src/fluidSolver/fluidSolver.cpp @@ -1 +1,44 @@ #include "fluidSolver.h" + +FluidSolver::FluidSolver( + const glm::vec3& containerDim, + const glm::vec3& particleDim, + const float& separation + ) : m_containerDim(containerDim), + m_particleDim(particleDim), + m_separation(separation) +{ + // -- Initialize all particles + for (float x = 0; x < m_particleDim.x; x += m_separation) + { + for (float y = 0; y < m_particleDim.y; y += m_separation) + { + for (float z = 0; z < m_particleDim.z; z += m_separation) + { + m_particles.push_back( + new FluidParticle(glm::vec3(x, y, z)) + ); + } + } + } + + // -- Initalize gravity + m_gravity = GRAVITY; +} + +FluidSolver::~FluidSolver() +{ + for (auto &particle : m_particles) + { + if (particle != nullptr) + { + delete particle; + } + } +} + +void +FluidSolver::Update() +{ + +} diff --git a/src/fluidSolver/fluidSolver.h b/src/fluidSolver/fluidSolver.h new file mode 100644 index 00000000..8bcef1f4 --- /dev/null +++ b/src/fluidSolver/fluidSolver.h @@ -0,0 +1,53 @@ +#ifndef FLUIDSOLVER_H +#define FLUIDSOLVER_H + +#include +#include +#include + +class FluidParticle +{ +public: + FluidParticle() {}; + FluidParticle(const glm::vec3& position) : m_position(position) {}; + + void Translate( + const glm::vec3& offset + ) + { + m_position += offset; + } + + const glm::vec3& Position() const + { + return m_position; + } +private: + glm::vec3 m_position; +}; + + +class FluidSolver +{ +public: + FluidSolver( + const glm::vec3& containerDim, + const glm::vec3& particleDim, + const float& separation + ); + ~FluidSolver(); + void Update(); +private: + + // -- Particle info + glm::vec3 m_containerDim; + glm::vec3 m_particleDim; + float m_separation; + + // -- Forces + float m_gravity; + + std::vector m_particles; +}; + +#endif /* FLUIDSOLVER_H */ diff --git a/src/fluidSolver/fluidSolver.hpp b/src/fluidSolver/fluidSolver.hpp deleted file mode 100644 index 6429c4e3..00000000 --- a/src/fluidSolver/fluidSolver.hpp +++ /dev/null @@ -1,9 +0,0 @@ -// -// fluidSolver.hpp -// Thanda - -#ifndef fluidSolver_hpp -#define fluidSolver_hpp - - -#endif /* fluidSolver_hpp */ diff --git a/src/mathConstants.h b/src/mathConstants.h index 0e075437..08d33aba 100644 --- a/src/mathConstants.h +++ b/src/mathConstants.h @@ -1,3 +1,6 @@ +#ifndef MATH_CONSTANTS_H +#define MATH_CONSTANTS_H + // Math constants static const float PI = 3.14159265358979323846f; @@ -5,3 +8,6 @@ static const float TWO_PI = 2 * PI; static const float INV_PI = 1.0f / PI; static const float DEG2RAD = PI / 180.f; static const float RAD2DEG = 180.f / PI; +static const float GRAVITY = -9.8f; + +#endif diff --git a/src/scene/scene.h b/src/scene/scene.h index 5532edff..acdeccfd 100644 --- a/src/scene/scene.h +++ b/src/scene/scene.h @@ -2,6 +2,7 @@ #define SCENE_H #include +#include class Scene { From c3b325715b5fc08820bd7559288df855b137fc2c Mon Sep 17 00:00:00 2001 From: Trung Le Date: Sun, 28 Feb 2016 00:16:56 -0500 Subject: [PATCH 12/64] [fluidparticle] - Implemeted advection --- src/fluidSolver/fluidSolver.cpp | 52 ++++++++++++++++++++++++++++++--- src/fluidSolver/fluidSolver.h | 46 ++++++++++++++++------------- 2 files changed, 73 insertions(+), 25 deletions(-) diff --git a/src/fluidSolver/fluidSolver.cpp b/src/fluidSolver/fluidSolver.cpp index 1067a102..75b8ca6e 100644 --- a/src/fluidSolver/fluidSolver.cpp +++ b/src/fluidSolver/fluidSolver.cpp @@ -1,5 +1,48 @@ #include "fluidSolver.h" +// ---------------------------------------------------- // +// FluidParticle +// ---------------------------------------------------- // +FluidParticle::FluidParticle() +{ + +} + +FluidParticle::FluidParticle( + const glm::vec3& pos, + const glm::vec3& vel, + const glm::vec3& accel + ) :m_pos(pos), m_vel(vel), m_accel(accel) +{ + +} + +void +FluidParticle::Update() +{ + // @todo: We're just doing Euler integration for now + m_vel = m_vel + m_accel; + m_pos = m_pos + m_vel; +} + +void +FluidParticle::AddForce( + const glm::vec3& force + ) +{ + m_accel += force; +} + +const glm::vec3& +FluidParticle::Position() const +{ + return m_pos; +} + + +// ---------------------------------------------------- // +// FluidSolver +// ---------------------------------------------------- // FluidSolver::FluidSolver( const glm::vec3& containerDim, const glm::vec3& particleDim, @@ -16,14 +59,15 @@ FluidSolver::FluidSolver( for (float z = 0; z < m_particleDim.z; z += m_separation) { m_particles.push_back( - new FluidParticle(glm::vec3(x, y, z)) + new FluidParticle( + glm::vec3(x, y, z), + glm::vec3(0.f), + glm::vec3(0.0f, GRAVITY, 0.0f) + ) ); } } } - - // -- Initalize gravity - m_gravity = GRAVITY; } FluidSolver::~FluidSolver() diff --git a/src/fluidSolver/fluidSolver.h b/src/fluidSolver/fluidSolver.h index 8bcef1f4..234a5aaf 100644 --- a/src/fluidSolver/fluidSolver.h +++ b/src/fluidSolver/fluidSolver.h @@ -5,28 +5,35 @@ #include #include +// ---------------------------------------------------- // +// FluidParticle +// ---------------------------------------------------- // class FluidParticle { public: - FluidParticle() {}; - FluidParticle(const glm::vec3& position) : m_position(position) {}; - - void Translate( - const glm::vec3& offset - ) - { - m_position += offset; - } - - const glm::vec3& Position() const - { - return m_position; - } -private: - glm::vec3 m_position; + FluidParticle(); + FluidParticle( + const glm::vec3& pos, + const glm::vec3& vel, + const glm::vec3& accel + ); + + virtual void Update(); + void AddForce(const glm::vec3& force); + + // Getters/setters + const glm::vec3& Position() const; + +protected: + glm::vec3 m_pos; + glm::vec3 m_vel; + glm::vec3 m_accel; }; +// ---------------------------------------------------- // +// FluidSolver +// ---------------------------------------------------- // class FluidSolver { public: @@ -36,17 +43,14 @@ class FluidSolver const float& separation ); ~FluidSolver(); - void Update(); -private: + virtual void Update(); +protected: // -- Particle info glm::vec3 m_containerDim; glm::vec3 m_particleDim; float m_separation; - // -- Forces - float m_gravity; - std::vector m_particles; }; From 9c1dce52ec188c9b2fa0aa929ac51714b71198b8 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Sun, 28 Feb 2016 00:49:43 -0500 Subject: [PATCH 13/64] [fluidparticle] - Implemented advection using Euler integration --- src/fluidSolver/fluidSolver.cpp | 20 ++++++++++++++++---- src/fluidSolver/fluidSolver.h | 2 ++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/fluidSolver/fluidSolver.cpp b/src/fluidSolver/fluidSolver.cpp index 75b8ca6e..2d3ba223 100644 --- a/src/fluidSolver/fluidSolver.cpp +++ b/src/fluidSolver/fluidSolver.cpp @@ -20,9 +20,7 @@ FluidParticle::FluidParticle( void FluidParticle::Update() { - // @todo: We're just doing Euler integration for now - m_vel = m_vel + m_accel; - m_pos = m_pos + m_vel; + Advect(); } void @@ -40,6 +38,17 @@ FluidParticle::Position() const } +// -- Private -- // +void +FluidParticle::Advect() +{ + // @todo: We're just doing Euler integration for now + // RK2 soon + m_vel = m_vel + m_accel; + m_pos = m_pos + m_vel; + +} + // ---------------------------------------------------- // // FluidSolver // ---------------------------------------------------- // @@ -84,5 +93,8 @@ FluidSolver::~FluidSolver() void FluidSolver::Update() { - + for (auto &particle : m_particles) + { + particle->Update(); + } } diff --git a/src/fluidSolver/fluidSolver.h b/src/fluidSolver/fluidSolver.h index 234a5aaf..5e32dc37 100644 --- a/src/fluidSolver/fluidSolver.h +++ b/src/fluidSolver/fluidSolver.h @@ -28,6 +28,8 @@ class FluidParticle glm::vec3 m_pos; glm::vec3 m_vel; glm::vec3 m_accel; + + void Advect(); }; From 45c89f747ca192e3ca3e9e4abf6f58fcd3e4d5b5 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Sun, 28 Feb 2016 00:50:44 -0500 Subject: [PATCH 14/64] [geometry] - Removed setters for transformations and updated test scene --- src/geometry/box.h | 2 +- src/geometry/geometry.cpp | 17 ----------------- src/geometry/geometry.h | 2 -- src/scene/scene.cpp | 36 +++++++++--------------------------- src/scene/scene.h | 2 +- 5 files changed, 11 insertions(+), 48 deletions(-) diff --git a/src/geometry/box.h b/src/geometry/box.h index 7202afa8..47d43e8c 100644 --- a/src/geometry/box.h +++ b/src/geometry/box.h @@ -1,7 +1,7 @@ #ifndef BOX_H #define BOX_H -#include "geometry.h" +#include class Box : public Geometry { diff --git a/src/geometry/geometry.cpp b/src/geometry/geometry.cpp index 2c6c5667..d22d634c 100644 --- a/src/geometry/geometry.cpp +++ b/src/geometry/geometry.cpp @@ -82,23 +82,6 @@ Geometry::Scale( m_localTransform = glm::scale(m_localTransform, glm::vec3(scalex, scaley, scalez)); } - -void -Geometry::SetLocalTransformation( - const glm::mat4& localTrans - ) -{ - m_localTransform = localTrans; -} - -void -Geometry::SetGlobalTransformation( - const glm::mat4& globalTrans - ) -{ - m_globalTransform = globalTrans; -} - glm::mat4 Geometry::GetLocalTransformation() const { diff --git a/src/geometry/geometry.h b/src/geometry/geometry.h index 8982acef..622fd1cc 100644 --- a/src/geometry/geometry.h +++ b/src/geometry/geometry.h @@ -41,8 +41,6 @@ class Geometry void Translate(const float& x, const float& y, const float& z); void Rotate(const float& radx, const float& rady, const float& radz); void Scale(const float& scaleX, const float& scaleY, const float& scaleZ); - void SetLocalTransformation(const glm::mat4&); - void SetGlobalTransformation(const glm::mat4&); glm::mat4 GetLocalTransformation() const; glm::mat4 GetGlobalTransformation() const; diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 0b129015..9bc6bc12 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -16,27 +16,9 @@ Scene::~Scene() void Scene::InitFromTestScene() { - m_box = Box(); - - glm::mat4 rot = glm::rotate( - glm::mat4(1.0f), - 0.02f, - glm::vec3(1.0f, 1.0f, 0.0f) - ); - - glm::mat4 scale = glm::scale( - glm::mat4(1.0f), - glm::vec3(2.5f, 2.5f, 2.5f) - ); - - glm::mat4 translate = glm::translate( - glm::mat4(1.0f), - glm::vec3(-1.0f, 0.0f, 0.0f) - ); - - m_box.SetLocalTransformation(translate * rot * scale); - m_box.SetDrawMode(DrawMode_Wireframe); - m_box.Create(); + m_testBox = Box(); + m_testBox.SetDrawMode(DrawMode_Wireframe); + m_testBox.Create(); } bool @@ -70,14 +52,14 @@ Scene::InitFromJson() // -- Get container information const Json::Value containerDim = root["containerDim"]; - m_box = Box(); - m_box.Scale( + m_testBox = Box(); + m_testBox.Scale( containerDim["scaleX"].asFloat(), containerDim["scaleY"].asFloat(), containerDim["scaleZ"].asFloat() ); - m_box.SetDrawMode(DrawMode_Wireframe); - m_box.Create(); + m_testBox.SetDrawMode(DrawMode_Wireframe); + m_testBox.Create(); return true; } @@ -88,7 +70,7 @@ Scene::Update() #ifdef ANIMATE // Rotate bounding box - m_box.Rotate(0.02f, 0.01f, 0.02); + m_testBox.Rotate(0.02f, 0.01f, 0.02); #endif } @@ -96,5 +78,5 @@ Scene::Update() const Geometry& Scene::RootGeometry() const { - return m_box; + return m_testBox; } diff --git a/src/scene/scene.h b/src/scene/scene.h index acdeccfd..50db1f40 100644 --- a/src/scene/scene.h +++ b/src/scene/scene.h @@ -16,7 +16,7 @@ class Scene virtual const Geometry& RootGeometry() const; protected: - Box m_box; + Box m_testBox; }; #endif From fc00d6a0ee972ac493479b1fc17744d85a0d0556 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Sun, 28 Feb 2016 01:17:10 -0500 Subject: [PATCH 15/64] Created fluidGeo for drawing fluids. Moved Json parser to utility. --- src/geometry/box.cpp | 6 --- src/geometry/box.h | 2 +- src/geometry/fluidGeo.cpp | 68 +++++++++++++++++++++++++++++++++ src/geometry/fluidGeo.h | 21 ++++++++++ src/scene/scene.cpp | 80 +++++++++++++++++++-------------------- src/scene/scene.h | 10 +++-- src/utility.cpp | 35 ++++++++++++++++- src/utility.h | 13 +++++++ src/utility.hpp | 4 -- src/viewer.cpp | 9 +++-- 10 files changed, 188 insertions(+), 60 deletions(-) create mode 100644 src/geometry/fluidGeo.cpp create mode 100644 src/geometry/fluidGeo.h create mode 100644 src/utility.h delete mode 100644 src/utility.hpp diff --git a/src/geometry/box.cpp b/src/geometry/box.cpp index fcfaf82a..76a76fea 100644 --- a/src/geometry/box.cpp +++ b/src/geometry/box.cpp @@ -131,12 +131,6 @@ Box::Create() GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, NULL); - // @todo: - // glGenBuffers(1, &m_norBuffer); - - // @todo: - // glGenBuffers(1, &m_colBuffer); - // -- Index glGenBuffers(1, &m_idxBuffer); diff --git a/src/geometry/box.h b/src/geometry/box.h index 47d43e8c..88764d45 100644 --- a/src/geometry/box.h +++ b/src/geometry/box.h @@ -1,7 +1,7 @@ #ifndef BOX_H #define BOX_H -#include +#include class Box : public Geometry { diff --git a/src/geometry/fluidGeo.cpp b/src/geometry/fluidGeo.cpp new file mode 100644 index 00000000..fa133ed5 --- /dev/null +++ b/src/geometry/fluidGeo.cpp @@ -0,0 +1,68 @@ +#include + +FluidGeo::FluidGeo( + const vector& positions + ) +{ + UpdatePositions(positions); +} + +GLenum +FluidGeo::GLDrawMode() +{ + return GL_POINTS; +} + +void +FluidGeo::UpdatePositions( + const std::vector& positions + ) +{ + m_positions = positions; +} + +void +FluidGeo::InitIndices() +{ + int len = m_positions.size(); + for (int i = 0; i < len; ++i) + { + m_indices.push_back(i); + } +} + +void +FluidGeo::Create() +{ + InitIndices(); + + glGenVertexArrays(1, &m_vao); + glBindVertexArray(m_vao); + + // @todo: Is it faster to allocate all the buffers ahead of time? + + // -- Position + + glGenBuffers(1, &m_posBuffer); + glBindBuffer(GL_ARRAY_BUFFER, m_posBuffer); + glBufferData( + GL_ARRAY_BUFFER, + m_positions.size() * sizeof(glm::vec3), + &m_positions[0], + GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, NULL); + + // -- Index + + glGenBuffers(1, &m_idxBuffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_idxBuffer); + glBufferData( + GL_ELEMENT_ARRAY_BUFFER, + m_indices.size() * sizeof(GLushort), + &m_indices[0], + GL_STATIC_DRAW + ); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NULL); + + glBindVertexArray(NULL); +} diff --git a/src/geometry/fluidGeo.h b/src/geometry/fluidGeo.h new file mode 100644 index 00000000..750d4b38 --- /dev/null +++ b/src/geometry/fluidGeo.h @@ -0,0 +1,21 @@ +#ifndef FLUID_GEO_H +#define GLUID_GEO_H + +#include +#include + +class FluidGeo : public Geometry +{ +public: + FluidGeo(const vector& positions); + + virtual void Create(); + virtual GLenum GLDrawMode(); + + void UpdatePositions(const std::vector& positions); + +protected: + virtual void InitIndices(); +}; + +#endif diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 9bc6bc12..73f3abeb 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -1,67 +1,63 @@ -#include -#include -#include -#include +#include +#include -#include "scene.h" - -Scene::Scene() +Scene::Scene() : + m_testBox(nullptr), + m_fluidContainer(nullptr), + m_fluidGeo(nullptr), + m_fluidSolver(nullptr) { } Scene::~Scene() { + if (m_testBox != nullptr) { + delete m_testBox; + } + + if (m_fluidContainer != nullptr) { + delete m_fluidContainer; + } + + if (m_fluidGeo != nullptr) { + delete m_fluidGeo; + } + + if (m_fluidSolver != nullptr) { + delete m_fluidSolver; + } } void Scene::InitFromTestScene() { - m_testBox = Box(); - m_testBox.SetDrawMode(DrawMode_Wireframe); - m_testBox.Create(); + m_testBox = new Box(); + m_testBox->SetDrawMode(DrawMode_Wireframe); + m_testBox->Create(); } -bool -Scene::InitFromJson() +void +Scene::InitFromJson(const char* filepath) { - // -- Open file - std::string jsonText; - std::string line; - std::ifstream jsonFile("../src/scene/scene.json"); - if (jsonFile.is_open() == false) { - std::cout << "JSON file not open." << endl; - return false; - } - while (std::getline(jsonFile, line)) { - jsonText += line; - } - - // -- Parse json + // -- Parse JSON Json::Value root; - Json::Reader reader; - bool success = reader.parse(jsonText, root, false); - if (!success) - { - std::cout << "Parsing failed." << endl; - - // report to the user the failure and their locations in the document. - std::cout << reader.getFormatedErrorMessages() << endl; - return false; + bool success = ParseJson(filepath, root); + if (!success) { + // Failed parsing JSON + return; } // -- Get container information const Json::Value containerDim = root["containerDim"]; - m_testBox = Box(); - m_testBox.Scale( + m_testBox = new Box(); + m_testBox->Scale( containerDim["scaleX"].asFloat(), containerDim["scaleY"].asFloat(), containerDim["scaleZ"].asFloat() ); - m_testBox.SetDrawMode(DrawMode_Wireframe); - m_testBox.Create(); - - return true; + m_testBox->SetDrawMode(DrawMode_Wireframe); + m_testBox->Create(); } void @@ -70,12 +66,12 @@ Scene::Update() #ifdef ANIMATE // Rotate bounding box - m_testBox.Rotate(0.02f, 0.01f, 0.02); + m_testBox->Rotate(0.02f, 0.01f, 0.02); #endif } -const Geometry& +const Geometry* Scene::RootGeometry() const { return m_testBox; diff --git a/src/scene/scene.h b/src/scene/scene.h index 50db1f40..0487a530 100644 --- a/src/scene/scene.h +++ b/src/scene/scene.h @@ -2,6 +2,7 @@ #define SCENE_H #include +#include #include class Scene @@ -10,13 +11,16 @@ class Scene Scene(); ~Scene(); void InitFromTestScene(); - bool InitFromJson(); + void InitFromJson(const char* filepath); virtual void Update(); - virtual const Geometry& RootGeometry() const; + virtual const Geometry* RootGeometry() const; protected: - Box m_testBox; + Box* m_testBox; + Box* m_fluidContainer; + FluidGeo* m_fluidGeo; + FluidSolver* m_fluidSolver; }; #endif diff --git a/src/utility.cpp b/src/utility.cpp index 5523ac1d..f8b82af6 100644 --- a/src/utility.cpp +++ b/src/utility.cpp @@ -1,3 +1,36 @@ -#include "utility.hpp" +#include + +bool +ParseJson( + const char* filepath, + Json::Value& root + ) +{ + // -- Open file + std::string jsonText; + std::string line; + std::ifstream jsonFile(filepath); + if (jsonFile.is_open() == false) { + std::cout << "JSON file not open." << std::endl; + return false; + } + while (std::getline(jsonFile, line)) { + jsonText += line; + } + + // -- Parse json + Json::Reader reader; + bool success = reader.parse(jsonText, root, false); + if (!success) + { + std::cout << "Parsing failed." << std::endl; + + // report to the user the failure and their locations in the document. + std::cout << reader.getFormatedErrorMessages() << std::endl; + return false; + } + + return true; +} diff --git a/src/utility.h b/src/utility.h new file mode 100644 index 00000000..fab6acca --- /dev/null +++ b/src/utility.h @@ -0,0 +1,13 @@ +#ifndef UTILITY_H +#define UTILITY_H + +#include +#include +#include +#include + +// This file includes all the helpers for our fluid solvers + +bool ParseJson(const char* filepath, Json::Value& root); + +#endif UTILITY_H diff --git a/src/utility.hpp b/src/utility.hpp deleted file mode 100644 index 35c732bb..00000000 --- a/src/utility.hpp +++ /dev/null @@ -1,4 +0,0 @@ -#ifndef UTILITY_HPP -#define UTILITY_HPP - -#endif UTILITY_HPP diff --git a/src/viewer.cpp b/src/viewer.cpp index e5a05f43..e1f2a0d5 100644 --- a/src/viewer.cpp +++ b/src/viewer.cpp @@ -70,8 +70,11 @@ Viewer::Init() // Initialize scene m_scene = Scene(); - // m_scene.InitFromTestScene(); - m_scene.InitFromJson(); +#define TEST_SCENE +#ifdef TEST_SCENE + m_scene.InitFromTestScene(); +#endif + m_scene.InitFromJson("../src/scene/scene.json"); // Initialize camera m_camera = Camera(m_width, m_height); @@ -87,7 +90,7 @@ Viewer::Update() glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); m_scene.Update(); - m_program.Render(m_camera, m_scene.RootGeometry()); + m_program.Render(m_camera, *(m_scene.RootGeometry())); // Swap buffers glfwSwapBuffers(m_window); From d072522b08b7204ef178c9ed3d5f6265f364604e Mon Sep 17 00:00:00 2001 From: Trung Le Date: Sun, 28 Feb 2016 02:07:16 -0500 Subject: [PATCH 16/64] Finished particle viewer --- src/fluidSolver/fluidSolver.cpp | 25 +++++-- src/fluidSolver/fluidSolver.h | 3 + src/geometry/fluidGeo.h | 2 +- src/geometry/geometry.cpp | 2 + src/geometry/geometry.h | 3 +- src/glsl/vert_bare.glsl | 1 + src/scene/scene.cpp | 122 +++++++++++++++++++++++--------- src/scene/scene.h | 11 ++- src/shaderProgram.cpp | 11 ++- src/shaderProgram.h | 8 ++- src/viewer.cpp | 30 ++++---- src/viewer.h | 8 +-- 12 files changed, 156 insertions(+), 70 deletions(-) diff --git a/src/fluidSolver/fluidSolver.cpp b/src/fluidSolver/fluidSolver.cpp index 2d3ba223..142231b5 100644 --- a/src/fluidSolver/fluidSolver.cpp +++ b/src/fluidSolver/fluidSolver.cpp @@ -61,17 +61,20 @@ FluidSolver::FluidSolver( m_separation(separation) { // -- Initialize all particles - for (float x = 0; x < m_particleDim.x; x += m_separation) + float midX = m_particleDim.x / 2.f; + float midY = m_particleDim.y / 2.f; + float midZ = m_particleDim.z / 2.f; + for (float x = -midX; x < midX; x += m_separation) { - for (float y = 0; y < m_particleDim.y; y += m_separation) + for (float y = -midY; y < midY; y += m_separation) { - for (float z = 0; z < m_particleDim.z; z += m_separation) + for (float z = -midZ; z < midX; z += m_separation) { m_particles.push_back( new FluidParticle( glm::vec3(x, y, z), - glm::vec3(0.f), - glm::vec3(0.0f, GRAVITY, 0.0f) + glm::vec3(0.f, 0.0001f, 0.0f), + glm::vec3(0.0f, GRAVITY * 0.0001f, 0.0f) ) ); } @@ -90,6 +93,18 @@ FluidSolver::~FluidSolver() } } +const std::vector +FluidSolver::ParticlePositions() +{ + std::vector positions; + for (auto &particle : m_particles) + { + positions.push_back(particle->Position()); + } + + return positions; +} + void FluidSolver::Update() { diff --git a/src/fluidSolver/fluidSolver.h b/src/fluidSolver/fluidSolver.h index 5e32dc37..ab1e20ab 100644 --- a/src/fluidSolver/fluidSolver.h +++ b/src/fluidSolver/fluidSolver.h @@ -45,6 +45,9 @@ class FluidSolver const float& separation ); ~FluidSolver(); + const std::vector ParticlePositions(); + + // Called every frame virtual void Update(); protected: diff --git a/src/geometry/fluidGeo.h b/src/geometry/fluidGeo.h index 750d4b38..f6764049 100644 --- a/src/geometry/fluidGeo.h +++ b/src/geometry/fluidGeo.h @@ -7,7 +7,7 @@ class FluidGeo : public Geometry { public: - FluidGeo(const vector& positions); + FluidGeo(const vector& positions); virtual void Create(); virtual GLenum GLDrawMode(); diff --git a/src/geometry/geometry.cpp b/src/geometry/geometry.cpp index d22d634c..254a5bf2 100644 --- a/src/geometry/geometry.cpp +++ b/src/geometry/geometry.cpp @@ -14,6 +14,8 @@ Geometry::GLDrawMode() const switch(m_drawMode) { case DrawMode_Wireframe: return GL_LINES; + case DrawMode_Vertex: + return GL_POINTS; case DrawMode_Shaded: default: return GL_TRIANGLES; diff --git a/src/geometry/geometry.h b/src/geometry/geometry.h index 622fd1cc..936f2849 100644 --- a/src/geometry/geometry.h +++ b/src/geometry/geometry.h @@ -18,7 +18,8 @@ typedef enum { DrawMode_Wireframe, - DrawMode_Shaded + DrawMode_Shaded, + DrawMode_Vertex } DrawMode; using namespace std; diff --git a/src/glsl/vert_bare.glsl b/src/glsl/vert_bare.glsl index ee583dd9..7bf5c92a 100644 --- a/src/glsl/vert_bare.glsl +++ b/src/glsl/vert_bare.glsl @@ -14,4 +14,5 @@ void main() { vec4 position = u_viewProj * u_model * vec4(a_position, 1.0); gl_Position = position; + gl_PointSize = 1.0f; } diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 73f3abeb..2882731e 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -2,30 +2,20 @@ #include Scene::Scene() : + Scene(800, 600) +{} + +Scene::Scene( + const float& screenWidth, + const float& screenHeight + ) : m_testBox(nullptr), m_fluidContainer(nullptr), m_fluidGeo(nullptr), m_fluidSolver(nullptr) { -} - -Scene::~Scene() -{ - if (m_testBox != nullptr) { - delete m_testBox; - } - - if (m_fluidContainer != nullptr) { - delete m_fluidContainer; - } - - if (m_fluidGeo != nullptr) { - delete m_fluidGeo; - } - - if (m_fluidSolver != nullptr) { - delete m_fluidSolver; - } + // Initialize camera + m_camera = new Camera(screenWidth, screenHeight); } void @@ -48,31 +38,97 @@ Scene::InitFromJson(const char* filepath) } // -- Get container information - const Json::Value containerDim = root["containerDim"]; + const Json::Value containerDimJson = root["containerDim"]; - m_testBox = new Box(); - m_testBox->Scale( - containerDim["scaleX"].asFloat(), - containerDim["scaleY"].asFloat(), - containerDim["scaleZ"].asFloat() - ); - m_testBox->SetDrawMode(DrawMode_Wireframe); - m_testBox->Create(); + glm::vec3 containerDim( + containerDimJson["scaleX"].asFloat(), + containerDimJson["scaleY"].asFloat(), + containerDimJson["scaleZ"].asFloat() + ); + + // Create geometry for the container + m_fluidContainer = new Box(); + m_fluidContainer->Scale(containerDim.x, containerDim.y, containerDim.z); + m_fluidContainer->SetDrawMode(DrawMode_Wireframe); + m_fluidContainer->Create(); + + // -- Get particle information + const Json::Value particleDimJson = root["particleDim"]; + glm::vec3 particleDim( + particleDimJson["boundX"].asFloat(), + particleDimJson["boundY"].asFloat(), + particleDimJson["boundZ"].asFloat() + ); + + const float separation = root["particleSeparation"].asFloat(); + + // -- Initialize fluid solvers + m_fluidSolver = new FluidSolver( + containerDim, + particleDim, + separation + ); + + // -- Initialize fluid geo + m_fluidGeo = new FluidGeo( + m_fluidSolver->ParticlePositions() + ); + m_fluidGeo->Create(); } void Scene::Update() { - -#ifdef ANIMATE +#ifdef TEST_SCENE // Rotate bounding box m_testBox->Rotate(0.02f, 0.01f, 0.02); + return; #endif + // -- Update fluid solver + m_fluidSolver->Update(); + m_fluidGeo->UpdatePositions( + m_fluidSolver->ParticlePositions() + ); + m_fluidGeo->Create(); } -const Geometry* -Scene::RootGeometry() const +void +Scene::Draw( + const ShaderProgram& prog + ) const { - return m_testBox; +#ifdef TEST_SCENE + prog.Draw(*m_camera, *m_testBox); + return; +#endif + glPointSize(5.0f); + + // @todo: do scene graph traversal here + prog.Draw(*m_camera, *m_fluidContainer); + prog.Draw(*m_camera, *m_fluidGeo); +} + +void +Scene::CleanUp() +{ + if (m_camera != nullptr) { + delete m_camera; + } + + if (m_testBox != nullptr) { + delete m_testBox; + } + + if (m_fluidContainer != nullptr) { + delete m_fluidContainer; + } + + if (m_fluidGeo != nullptr) { + delete m_fluidGeo; + } + + if (m_fluidSolver != nullptr) { + delete m_fluidSolver; + } } diff --git a/src/scene/scene.h b/src/scene/scene.h index 0487a530..f7e63211 100644 --- a/src/scene/scene.h +++ b/src/scene/scene.h @@ -1,6 +1,7 @@ #ifndef SCENE_H #define SCENE_H +#include #include #include #include @@ -9,14 +10,20 @@ class Scene { public: Scene(); - ~Scene(); + Scene( + const float& screenWidth, + const float& screenHeight + ); + void InitFromTestScene(); void InitFromJson(const char* filepath); virtual void Update(); - virtual const Geometry* RootGeometry() const; + virtual void Draw(const ShaderProgram&) const; + virtual void CleanUp(); protected: + Camera* m_camera; Box* m_testBox; Box* m_fluidContainer; FluidGeo* m_fluidGeo; diff --git a/src/shaderProgram.cpp b/src/shaderProgram.cpp index 9576baf4..a1b5465d 100644 --- a/src/shaderProgram.cpp +++ b/src/shaderProgram.cpp @@ -12,10 +12,11 @@ ShaderProgram::ShaderProgram( m_unifViewProj = glGetUniformLocation(m_programID, "u_viewProj"); } -void ShaderProgram::Render( +void +ShaderProgram::Draw( const Camera& camera, const Geometry& geo - ) + ) const { glUseProgram(m_programID); @@ -50,3 +51,9 @@ void ShaderProgram::Render( geo.DisableVertexAttributes(); } + +void +ShaderProgram::CleanUp() +{ + glDeleteProgram(m_programID); +} diff --git a/src/shaderProgram.h b/src/shaderProgram.h index 172e433b..ce3845e1 100644 --- a/src/shaderProgram.h +++ b/src/shaderProgram.h @@ -15,12 +15,14 @@ class ShaderProgram const char* fragFilePath ); - void Render( + virtual void Draw( const Camera&, const Geometry& - ); + ) const; -private: + virtual void CleanUp(); + +protected: GLuint m_programID; // Uniform locations diff --git a/src/viewer.cpp b/src/viewer.cpp index e1f2a0d5..c2c2638f 100644 --- a/src/viewer.cpp +++ b/src/viewer.cpp @@ -4,18 +4,13 @@ #include "viewer.h" Viewer::Viewer() -{ - m_width = 800; - m_height = 600; - - this->Init(); - -} + : Viewer(800, 600) +{} Viewer::Viewer( int width, int height - ) + ) : m_window(nullptr), m_program(nullptr), m_scene(nullptr) { m_width = width; m_height = height; @@ -66,18 +61,14 @@ Viewer::Init() glfwSetInputMode(m_window, GLFW_STICKY_KEYS, GL_TRUE); // Create and compile GLSL program from the shaders - m_program = ShaderProgram("../src/glsl/vert_bare.glsl", "../src/glsl/frag_bare.glsl"); + m_program = new ShaderProgram("../src/glsl/vert_bare.glsl", "../src/glsl/frag_bare.glsl"); // Initialize scene - m_scene = Scene(); -#define TEST_SCENE + m_scene = new Scene(m_width, m_height); #ifdef TEST_SCENE - m_scene.InitFromTestScene(); + m_scene->InitFromTestScene(); #endif - m_scene.InitFromJson("../src/scene/scene.json"); - - // Initialize camera - m_camera = Camera(m_width, m_height); + m_scene->InitFromJson("../src/scene/scene.json"); // White background glClearColor(0.0f, 0.0f, 0.0f, 0.0f); @@ -89,8 +80,8 @@ Viewer::Update() do { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - m_scene.Update(); - m_program.Render(m_camera, *(m_scene.RootGeometry())); + m_scene->Update(); + m_scene->Draw(*m_program); // Swap buffers glfwSwapBuffers(m_window); @@ -105,6 +96,9 @@ Viewer::Update() void Viewer::CleanUp() { + m_program->CleanUp(); + m_scene->CleanUp(); + // Close OpenGL window and terminate GLFW glfwTerminate(); } diff --git a/src/viewer.h b/src/viewer.h index d4316c82..0d5edc6a 100644 --- a/src/viewer.h +++ b/src/viewer.h @@ -41,11 +41,9 @@ class Viewer float m_width; float m_height; - GLFWwindow* m_window = NULL; - ShaderProgram m_program; - Scene m_scene; - Camera m_camera; - + GLFWwindow* m_window; + ShaderProgram* m_program; + Scene* m_scene; }; #endif /* viewer_hpp */ From 449a00762d4cec0ed78b12d83ca44dcd051bc804 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Sun, 28 Feb 2016 02:52:26 -0500 Subject: [PATCH 17/64] [input] - Added keyboard control --- CMakeLists.txt | 6 +- src/camera/camera.cpp | 30 ++++++++- src/fluidSolver/fluidSolver.cpp | 3 +- src/input/keyboardControl.cpp | 110 ++++++++++++++++++++++++++++++++ src/input/keyboardControl.h | 28 ++++++++ src/scene/scene.cpp | 66 +++++++++++++++---- src/scene/scene.h | 11 +++- src/viewer.cpp | 52 +++++++++------ src/viewer.h | 8 ++- 9 files changed, 269 insertions(+), 45 deletions(-) create mode 100644 src/input/keyboardControl.cpp create mode 100644 src/input/keyboardControl.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0d6b9028..9be95e1b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,12 +95,14 @@ set(CORE_SRC src/utility.cpp src/main.cpp src/shaderProgram.cpp + src/input/keyboardControl.cpp src/viewer.cpp src/camera/camera.cpp - src/fluidSolver/fluidSolver.cpp - src/scene/scene.cpp src/geometry/geometry.cpp src/geometry/box.cpp + src/geometry/fluidGeo.cpp + src/fluidSolver/fluidSolver.cpp + src/scene/scene.cpp ) add_executable(Korra ${CORE_SRC}) diff --git a/src/camera/camera.cpp b/src/camera/camera.cpp index 5e4d166b..bdc10934 100644 --- a/src/camera/camera.cpp +++ b/src/camera/camera.cpp @@ -51,8 +51,34 @@ Camera::RotateAboutRight( float deg ) { - + deg *= DEG2RAD; + glm::mat4 rotation = glm::rotate(glm::mat4(1.0f), deg, m_up); + m_target = m_target - m_eye; + m_target = glm::vec3(rotation * glm::vec4(m_target, 1)); + m_target = m_target + m_eye; + RecomputeAttributes(); } +void +Camera::RotateAboutUp( + float deg + ) +{ + deg *= DEG2RAD; + glm::mat4 rotation = glm::rotate(glm::mat4(1.0f), deg, m_right); + m_target = m_target - m_eye; + m_target = glm::vec3(rotation * glm::vec4(m_target, 1)); + m_target = m_target + m_eye; + RecomputeAttributes(); +} - +void +Camera::Zoom( + float amount + ) +{ + glm::vec3 translation = m_forward * amount; + m_eye += translation; + m_target += translation; + RecomputeAttributes(); +} diff --git a/src/fluidSolver/fluidSolver.cpp b/src/fluidSolver/fluidSolver.cpp index 142231b5..c27a7573 100644 --- a/src/fluidSolver/fluidSolver.cpp +++ b/src/fluidSolver/fluidSolver.cpp @@ -37,7 +37,6 @@ FluidParticle::Position() const return m_pos; } - // -- Private -- // void FluidParticle::Advect() @@ -74,7 +73,7 @@ FluidSolver::FluidSolver( new FluidParticle( glm::vec3(x, y, z), glm::vec3(0.f, 0.0001f, 0.0f), - glm::vec3(0.0f, GRAVITY * 0.0001f, 0.0f) + glm::vec3(0.0f, GRAVITY * 0.0000f, 0.0f) ) ); } diff --git a/src/input/keyboardControl.cpp b/src/input/keyboardControl.cpp new file mode 100644 index 00000000..7ddd1934 --- /dev/null +++ b/src/input/keyboardControl.cpp @@ -0,0 +1,110 @@ +#include "keyboardControl.h" + +KeyboardControl::KeyboardControl( + GLFWwindow* window + ) : m_window(window) +{ + // Ensure we can capture key presses + glfwSetInputMode(m_window, GLFW_STICKY_KEYS, GL_TRUE); +} + +bool +KeyboardControl::KeyPressed( + const KeyCode& keyCode + ) const +{ + return glfwGetKey(m_window, keyCode) == GLFW_PRESS; +} + +// glm::mat4 ViewMatrix; +// glm::mat4 ProjectionMatrix; + +// glm::mat4 getViewMatrix(){ +// return ViewMatrix; +// } +// glm::mat4 getProjectionMatrix(){ +// return ProjectionMatrix; +// } + + +// // Initial position : on +Z +// glm::vec3 position = glm::vec3( 0, 0, 5 ); +// // Initial horizontal angle : toward -Z +// float horizontalAngle = 3.14f; +// // Initial vertical angle : none +// float verticalAngle = 0.0f; +// // Initial Field of View +// float initialFoV = 45.0f; + +// float speed = 3.0f; // 3 units / second +// float mouseSpeed = 0.005f; + +// void +// computeMatricesFromInputs(){ + +// // glfwGetTime is called only once, the first time this function is called +// static double lastTime = glfwGetTime(); + +// // Compute time difference between current and last frame +// double currentTime = glfwGetTime(); +// float deltaTime = float(currentTime - lastTime); + +// // Get mouse position +// double xpos, ypos; +// glfwGetCursorPos(window, &xpos, &ypos); + +// // Reset mouse position for next frame +// glfwSetCursorPos(window, 1024/2, 768/2); + +// // Compute new orientation +// horizontalAngle += mouseSpeed * float(1024/2 - xpos ); +// verticalAngle += mouseSpeed * float( 768/2 - ypos ); + +// // Direction : Spherical coordinates to Cartesian coordinates conversion +// glm::vec3 direction( +// cos(verticalAngle) * sin(horizontalAngle), +// sin(verticalAngle), +// cos(verticalAngle) * cos(horizontalAngle) +// ); + +// // Right vector +// glm::vec3 right = glm::vec3( +// sin(horizontalAngle - 3.14f/2.0f), +// 0, +// cos(horizontalAngle - 3.14f/2.0f) +// ); + +// // Up vector +// glm::vec3 up = glm::cross( right, direction ); + +// // Move forward +// if (glfwGetKey( window, GLFW_KEY_UP ) == GLFW_PRESS){ +// position += direction * deltaTime * speed; +// } +// // Move backward +// if (glfwGetKey( window, GLFW_KEY_DOWN ) == GLFW_PRESS){ +// position -= direction * deltaTime * speed; +// } +// // Strafe right +// if (glfwGetKey( window, GLFW_KEY_RIGHT ) == GLFW_PRESS){ +// position += right * deltaTime * speed; +// } +// // Strafe left +// if (glfwGetKey( window, GLFW_KEY_LEFT ) == GLFW_PRESS){ +// position -= right * deltaTime * speed; +// } + +// float FoV = initialFoV;// - 5 * glfwGetMouseWheel(); // Now GLFW 3 requires setting up a callback for this. It's a bit too complicated for this beginner's tutorial, so it's disabled instead. + +// // Projection matrix : 45∞ Field of View, 4:3 ratio, display range : 0.1 unit <-> 100 units +// ProjectionMatrix = glm::perspective(FoV, 4.0f / 3.0f, 0.1f, 100.0f); +// // Camera matrix +// ViewMatrix = glm::lookAt( +// position, // Camera is here +// position+direction, // and looks here : at the same position, plus "direction" +// up // Head is up (set to 0,-1,0 to look upside-down) +// ); + +// // For the next frame, the "last time" will be "now" +// lastTime = currentTime; +// } diff --git a/src/input/keyboardControl.h b/src/input/keyboardControl.h new file mode 100644 index 00000000..dcc634b2 --- /dev/null +++ b/src/input/keyboardControl.h @@ -0,0 +1,28 @@ +#ifndef KEYBOARD_CONTROL_H +#define KEYBOARD_CONTROL_H + +#include + +typedef enum { + Key_Escape = GLFW_KEY_ESCAPE, + Key_W = GLFW_KEY_W, + Key_A = GLFW_KEY_A, + Key_S = GLFW_KEY_S, + Key_D = GLFW_KEY_D, + Key_Up = GLFW_KEY_UP, + Key_Down = GLFW_KEY_DOWN, + Key_Left = GLFW_KEY_LEFT, + Key_Right = GLFW_KEY_RIGHT +} KeyCode; + +class KeyboardControl +{ +public: + KeyboardControl(GLFWwindow*); + bool KeyPressed(const KeyCode&) const; + +private: + GLFWwindow* m_window; +}; + +#endif diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 2882731e..3c1db8a6 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -70,27 +70,24 @@ Scene::InitFromJson(const char* filepath) ); // -- Initialize fluid geo - m_fluidGeo = new FluidGeo( - m_fluidSolver->ParticlePositions() - ); + m_fluidGeo = new FluidGeo(m_fluidSolver->ParticlePositions()); m_fluidGeo->Create(); } void -Scene::Update() +Scene::Update( + const KeyboardControl* kc + ) { + UpdateCamera(kc); + #ifdef TEST_SCENE // Rotate bounding box m_testBox->Rotate(0.02f, 0.01f, 0.02); return; #endif - // -- Update fluid solver - m_fluidSolver->Update(); - m_fluidGeo->UpdatePositions( - m_fluidSolver->ParticlePositions() - ); - m_fluidGeo->Create(); + UpdateFluidSolver(); } void @@ -102,11 +99,8 @@ Scene::Draw( prog.Draw(*m_camera, *m_testBox); return; #endif - glPointSize(5.0f); - // @todo: do scene graph traversal here - prog.Draw(*m_camera, *m_fluidContainer); - prog.Draw(*m_camera, *m_fluidGeo); + DrawFluidSolver(prog); } void @@ -132,3 +126,47 @@ Scene::CleanUp() delete m_fluidSolver; } } + +// ----- Private ------ // + +void +Scene::UpdateCamera( + const KeyboardControl* kc + ) +{ + float rotateAmt = 10.0f; + float zoomAmt = 0.01f; + if (kc->KeyPressed(Key_Down)) { + m_camera->RotateAboutUp(rotateAmt); + } else if (kc->KeyPressed(Key_Up)) { + m_camera->RotateAboutUp(-rotateAmt); + } else if (kc->KeyPressed(Key_Left)) { + m_camera->RotateAboutRight(rotateAmt); + } else if (kc->KeyPressed(Key_Right)) { + m_camera->RotateAboutRight(-rotateAmt); + } else if (kc->KeyPressed(Key_W)){ + m_camera->Zoom(zoomAmt); + } else if (kc->KeyPressed(Key_S)){ + m_camera->Zoom(-zoomAmt); + } +} + +void +Scene::UpdateFluidSolver() +{ + m_fluidSolver->Update(); + m_fluidGeo->UpdatePositions(m_fluidSolver->ParticlePositions()); + m_fluidGeo->Create(); +} + +void +Scene::DrawFluidSolver( + const ShaderProgram& prog + ) const +{ + glPointSize(5.0f); + + // @todo: do scene graph traversal here + prog.Draw(*m_camera, *m_fluidContainer); + prog.Draw(*m_camera, *m_fluidGeo); +} diff --git a/src/scene/scene.h b/src/scene/scene.h index f7e63211..3f6079af 100644 --- a/src/scene/scene.h +++ b/src/scene/scene.h @@ -5,6 +5,7 @@ #include #include #include +#include class Scene { @@ -18,7 +19,7 @@ class Scene void InitFromTestScene(); void InitFromJson(const char* filepath); - virtual void Update(); + virtual void Update(const KeyboardControl*); virtual void Draw(const ShaderProgram&) const; virtual void CleanUp(); @@ -28,6 +29,14 @@ class Scene Box* m_fluidContainer; FluidGeo* m_fluidGeo; FluidSolver* m_fluidSolver; + + // -- Update helpers + void UpdateCamera(const KeyboardControl*); + void UpdateFluidSolver(); + + // -- Draw helpers + void DrawFluidSolver(const ShaderProgram& prog) const; + }; #endif diff --git a/src/viewer.cpp b/src/viewer.cpp index c2c2638f..fdbc2431 100644 --- a/src/viewer.cpp +++ b/src/viewer.cpp @@ -1,6 +1,3 @@ -// -// viewer.cpp - #include "viewer.h" Viewer::Viewer() @@ -21,21 +18,25 @@ Viewer::Viewer( void Viewer::Init() { - // Initialise GLFW + // -- Initialise GLFW if( !glfwInit() ) { fprintf( stderr, "Failed to initialize GLFW\n" ); getchar(); } - glfwWindowHint(GLFW_SAMPLES, 4); // antiliasing 4 samples + // antiliasing 4 samples + glfwWindowHint(GLFW_SAMPLES, 4); glfwWindowHint(GLFW_RESIZABLE,GL_FALSE); - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // Get version 3.3 + // Get version 3.3 + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); - glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // To make MacOS happy; should not be needed - glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // Make sure with don't use old profile + // To make MacOS happy; should not be needed + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); + // Make sure with don't use old profile + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - // Open a window and create its OpenGL context + // -- Open a window and create its OpenGL context m_window = glfwCreateWindow( m_width, m_height, "Trung's Fluid Sim", NULL, NULL); if( m_window == NULL ){ fprintf( stderr, "Failed to open GLFW window.\n" ); @@ -45,7 +46,7 @@ Viewer::Init() glfwMakeContextCurrent(m_window); - // Initialize GLEW + // -- Initialize GLEW // *N.B*: Strange we have to do this to make glGenVertexArrays to work? // @@ -57,40 +58,44 @@ Viewer::Init() this->CleanUp(); } - // Ensure we can capture the escape key being pressed below - glfwSetInputMode(m_window, GLFW_STICKY_KEYS, GL_TRUE); + // -- Initialize inputs + m_keyboard = new KeyboardControl(m_window); - // Create and compile GLSL program from the shaders + // -- Initialize shader program m_program = new ShaderProgram("../src/glsl/vert_bare.glsl", "../src/glsl/frag_bare.glsl"); - // Initialize scene + // -- Initialize scene m_scene = new Scene(m_width, m_height); + + // Have a test scene here in case our graphics pipeline is broken #ifdef TEST_SCENE m_scene->InitFromTestScene(); -#endif +#else m_scene->InitFromJson("../src/scene/scene.json"); +#endif - // White background + // -- Clear background glClearColor(0.0f, 0.0f, 0.0f, 0.0f); } void Viewer::Update() { - do { + do + { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - m_scene->Update(); + m_scene->Update(m_keyboard); m_scene->Draw(*m_program); // Swap buffers glfwSwapBuffers(m_window); glfwPollEvents(); } - // Check if the ESC key was pressed or the window was closed while( - glfwGetKey(m_window, GLFW_KEY_ESCAPE ) != GLFW_PRESS && - glfwWindowShouldClose(m_window) == 0 ); + m_keyboard->KeyPressed(Key_Escape) == false && + glfwWindowShouldClose(m_window) == 0 + ); } void @@ -99,6 +104,11 @@ Viewer::CleanUp() m_program->CleanUp(); m_scene->CleanUp(); + // *N.B*: Deleting resources in reverse order of creation + delete m_scene; + delete m_program; + delete m_keyboard; + // Close OpenGL window and terminate GLFW glfwTerminate(); } diff --git a/src/viewer.h b/src/viewer.h index 0d5edc6a..f8976d66 100644 --- a/src/viewer.h +++ b/src/viewer.h @@ -11,9 +11,10 @@ #include #include -#include "shaderProgram.h" -#include "scene/scene.h" -#include "camera/camera.h" +#include +#include +#include +#include class Viewer { @@ -42,6 +43,7 @@ class Viewer float m_height; GLFWwindow* m_window; + KeyboardControl* m_keyboard; ShaderProgram* m_program; Scene* m_scene; }; From 0711b1faad60579b65c723d1fc93177870f873fd Mon Sep 17 00:00:00 2001 From: Trung Le Date: Sun, 28 Feb 2016 03:18:43 -0500 Subject: [PATCH 18/64] [fluidsolver] - Removed FluidParticle class (too slow). Added shaders for transform feedback --- src/camera/camera.cpp | 6 +- src/fluidSolver/fluidSolver.cpp | 86 ++---------------------- src/fluidSolver/fluidSolver.h | 37 +--------- src/geometry/fluidGeo.cpp | 2 +- src/geometry/fluidGeo.h | 2 +- src/glsl/particle_draw.frag | 12 ++++ src/glsl/particle_draw.vert | 28 ++++++++ src/glsl/particle_emit.frag | 11 +++ src/glsl/particle_emit.vert | 44 ++++++++++++ src/glsl/{frag_bare.glsl => simple.frag} | 0 src/glsl/{vert_bare.glsl => simple.vert} | 1 - src/scene/scene.cpp | 6 +- src/viewer.cpp | 2 +- 13 files changed, 111 insertions(+), 126 deletions(-) create mode 100644 src/glsl/particle_draw.frag create mode 100644 src/glsl/particle_draw.vert create mode 100644 src/glsl/particle_emit.frag create mode 100644 src/glsl/particle_emit.vert rename src/glsl/{frag_bare.glsl => simple.frag} (100%) rename src/glsl/{vert_bare.glsl => simple.vert} (92%) diff --git a/src/camera/camera.cpp b/src/camera/camera.cpp index bdc10934..ee241549 100644 --- a/src/camera/camera.cpp +++ b/src/camera/camera.cpp @@ -26,6 +26,8 @@ Camera::Camera( void Camera::RecomputeAttributes() { + m_forward = m_target - m_eye; + m_view = glm::lookAt( m_eye, m_target, @@ -54,7 +56,7 @@ Camera::RotateAboutRight( deg *= DEG2RAD; glm::mat4 rotation = glm::rotate(glm::mat4(1.0f), deg, m_up); m_target = m_target - m_eye; - m_target = glm::vec3(rotation * glm::vec4(m_target, 1)); + m_target = glm::vec3(rotation * glm::vec4(m_target, 1.0f)); m_target = m_target + m_eye; RecomputeAttributes(); } @@ -67,7 +69,7 @@ Camera::RotateAboutUp( deg *= DEG2RAD; glm::mat4 rotation = glm::rotate(glm::mat4(1.0f), deg, m_right); m_target = m_target - m_eye; - m_target = glm::vec3(rotation * glm::vec4(m_target, 1)); + m_target = glm::vec3(rotation * glm::vec4(m_target, 1.0f)); m_target = m_target + m_eye; RecomputeAttributes(); } diff --git a/src/fluidSolver/fluidSolver.cpp b/src/fluidSolver/fluidSolver.cpp index c27a7573..f3a8b647 100644 --- a/src/fluidSolver/fluidSolver.cpp +++ b/src/fluidSolver/fluidSolver.cpp @@ -1,56 +1,5 @@ #include "fluidSolver.h" -// ---------------------------------------------------- // -// FluidParticle -// ---------------------------------------------------- // -FluidParticle::FluidParticle() -{ - -} - -FluidParticle::FluidParticle( - const glm::vec3& pos, - const glm::vec3& vel, - const glm::vec3& accel - ) :m_pos(pos), m_vel(vel), m_accel(accel) -{ - -} - -void -FluidParticle::Update() -{ - Advect(); -} - -void -FluidParticle::AddForce( - const glm::vec3& force - ) -{ - m_accel += force; -} - -const glm::vec3& -FluidParticle::Position() const -{ - return m_pos; -} - -// -- Private -- // -void -FluidParticle::Advect() -{ - // @todo: We're just doing Euler integration for now - // RK2 soon - m_vel = m_vel + m_accel; - m_pos = m_pos + m_vel; - -} - -// ---------------------------------------------------- // -// FluidSolver -// ---------------------------------------------------- // FluidSolver::FluidSolver( const glm::vec3& containerDim, const glm::vec3& particleDim, @@ -69,46 +18,21 @@ FluidSolver::FluidSolver( { for (float z = -midZ; z < midX; z += m_separation) { - m_particles.push_back( - new FluidParticle( - glm::vec3(x, y, z), - glm::vec3(0.f, 0.0001f, 0.0f), - glm::vec3(0.0f, GRAVITY * 0.0000f, 0.0f) - ) + m_particlePositions.push_back( + glm::vec3(x, y, z) ); } } } } -FluidSolver::~FluidSolver() -{ - for (auto &particle : m_particles) - { - if (particle != nullptr) - { - delete particle; - } - } -} - -const std::vector -FluidSolver::ParticlePositions() +const std::vector& +FluidSolver::ParticlePositions() const { - std::vector positions; - for (auto &particle : m_particles) - { - positions.push_back(particle->Position()); - } - - return positions; + return m_particlePositions; } void FluidSolver::Update() { - for (auto &particle : m_particles) - { - particle->Update(); - } } diff --git a/src/fluidSolver/fluidSolver.h b/src/fluidSolver/fluidSolver.h index ab1e20ab..a643a5d2 100644 --- a/src/fluidSolver/fluidSolver.h +++ b/src/fluidSolver/fluidSolver.h @@ -5,37 +5,6 @@ #include #include -// ---------------------------------------------------- // -// FluidParticle -// ---------------------------------------------------- // -class FluidParticle -{ -public: - FluidParticle(); - FluidParticle( - const glm::vec3& pos, - const glm::vec3& vel, - const glm::vec3& accel - ); - - virtual void Update(); - void AddForce(const glm::vec3& force); - - // Getters/setters - const glm::vec3& Position() const; - -protected: - glm::vec3 m_pos; - glm::vec3 m_vel; - glm::vec3 m_accel; - - void Advect(); -}; - - -// ---------------------------------------------------- // -// FluidSolver -// ---------------------------------------------------- // class FluidSolver { public: @@ -44,8 +13,8 @@ class FluidSolver const glm::vec3& particleDim, const float& separation ); - ~FluidSolver(); - const std::vector ParticlePositions(); + + const std::vector& ParticlePositions() const; // Called every frame virtual void Update(); @@ -56,7 +25,7 @@ class FluidSolver glm::vec3 m_particleDim; float m_separation; - std::vector m_particles; + std::vector m_particlePositions; }; #endif /* FLUIDSOLVER_H */ diff --git a/src/geometry/fluidGeo.cpp b/src/geometry/fluidGeo.cpp index fa133ed5..cfccf243 100644 --- a/src/geometry/fluidGeo.cpp +++ b/src/geometry/fluidGeo.cpp @@ -8,7 +8,7 @@ FluidGeo::FluidGeo( } GLenum -FluidGeo::GLDrawMode() +FluidGeo::GLDrawMode() const { return GL_POINTS; } diff --git a/src/geometry/fluidGeo.h b/src/geometry/fluidGeo.h index f6764049..9de2a990 100644 --- a/src/geometry/fluidGeo.h +++ b/src/geometry/fluidGeo.h @@ -10,7 +10,7 @@ class FluidGeo : public Geometry FluidGeo(const vector& positions); virtual void Create(); - virtual GLenum GLDrawMode(); + virtual GLenum GLDrawMode() const; void UpdatePositions(const std::vector& positions); diff --git a/src/glsl/particle_draw.frag b/src/glsl/particle_draw.frag new file mode 100644 index 00000000..d13b1814 --- /dev/null +++ b/src/glsl/particle_draw.frag @@ -0,0 +1,12 @@ +#version 150 core +precision highp float; +precision highp int; + +uniform vec4 u_color; + +out vec4 color; + +void main() +{ + color = u_color; +} diff --git a/src/glsl/particle_draw.vert b/src/glsl/particle_draw.vert new file mode 100644 index 00000000..efd8c4d7 --- /dev/null +++ b/src/glsl/particle_draw.vert @@ -0,0 +1,28 @@ +#version 150 core + +#define POSITION_LOCATION 0 +#define VELOCITY_LOCATION 1 +#define SPAWNTIME_LOCATION 2 +#define LIFETIME_LOCATION 3 + +precision highp float; +precision highp int; + +uniform float u_time; +uniform vec2 u_acceleration; + +layout(location = POSITION_LOCATION) in vec2 a_position; +layout(location = VELOCITY_LOCATION) in vec2 a_velocity; +layout(location = SPAWNTIME_LOCATION) in float a_spawntime; +layout(location = LIFETIME_LOCATION) in float a_lifetime; + +void main() +{ + float deltaTime = u_time - a_spawntime; + if (deltaTime < a_lifetime) { + gl_Position = vec4(a_position, 0.0, 1.0); + } else { + gl_Position = vec4(-100.0, -100.0, 0.0, 1.0); + } + gl_PointSize = 10.0; +} diff --git a/src/glsl/particle_emit.frag b/src/glsl/particle_emit.frag new file mode 100644 index 00000000..25961f4e --- /dev/null +++ b/src/glsl/particle_emit.frag @@ -0,0 +1,11 @@ +#version 150 core + +precision highp float; +precision highp int; + +out vec4 color; + +void main() +{ + color = vec4(1.0); +} diff --git a/src/glsl/particle_emit.vert b/src/glsl/particle_emit.vert new file mode 100644 index 00000000..263eeeaf --- /dev/null +++ b/src/glsl/particle_emit.vert @@ -0,0 +1,44 @@ +#version 150 core +#define POSITION_LOCATION 0 +#define VELOCITY_LOCATION 1 +#define SPAWNTIME_LOCATION 2 +#define LIFETIME_LOCATION 3 +#define ID_LOCATION 4 + +precision highp float; +precision highp int; +precision highp sampler3D; + +uniform float u_time; +uniform vec2 u_acceleration; + +layout(location = POSITION_LOCATION) in vec2 a_position; +layout(location = VELOCITY_LOCATION) in vec2 a_velocity; +layout(location = SPAWNTIME_LOCATION) in float a_spawntime; +layout(location = LIFETIME_LOCATION) in float a_lifetime; +layout(location = ID_LOCATION) in float a_ID; + +out vec2 v_position; +out vec2 v_velocity; +out float v_spawntime; +out float v_lifetime; + +float rand(vec2 co){ + return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); +} + +void main() +{ + if (a_spawntime == 0.0 || (u_time - a_spawntime > a_lifetime) || a_position.y < -0.5) { + // Generate a new particle + v_position = vec2(0.0, 0.0); + v_velocity = vec2(rand(vec2(a_ID, 0.0)) - 0.5, rand(vec2(a_ID, a_ID))); + v_spawntime = u_time; + v_lifetime = 5000.0; + } else { + v_velocity = a_velocity + 0.01 * u_acceleration; + v_position = a_position + 0.01 * v_velocity; + v_spawntime = a_spawntime; + v_lifetime = a_lifetime; + } +} diff --git a/src/glsl/frag_bare.glsl b/src/glsl/simple.frag similarity index 100% rename from src/glsl/frag_bare.glsl rename to src/glsl/simple.frag diff --git a/src/glsl/vert_bare.glsl b/src/glsl/simple.vert similarity index 92% rename from src/glsl/vert_bare.glsl rename to src/glsl/simple.vert index 7bf5c92a..ee583dd9 100644 --- a/src/glsl/vert_bare.glsl +++ b/src/glsl/simple.vert @@ -14,5 +14,4 @@ void main() { vec4 position = u_viewProj * u_model * vec4(a_position, 1.0); gl_Position = position; - gl_PointSize = 1.0f; } diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 3c1db8a6..e9b58aa2 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -134,7 +134,7 @@ Scene::UpdateCamera( const KeyboardControl* kc ) { - float rotateAmt = 10.0f; + float rotateAmt = 20.0f; float zoomAmt = 0.01f; if (kc->KeyPressed(Key_Down)) { m_camera->RotateAboutUp(rotateAmt); @@ -155,8 +155,6 @@ void Scene::UpdateFluidSolver() { m_fluidSolver->Update(); - m_fluidGeo->UpdatePositions(m_fluidSolver->ParticlePositions()); - m_fluidGeo->Create(); } void @@ -164,8 +162,6 @@ Scene::DrawFluidSolver( const ShaderProgram& prog ) const { - glPointSize(5.0f); - // @todo: do scene graph traversal here prog.Draw(*m_camera, *m_fluidContainer); prog.Draw(*m_camera, *m_fluidGeo); diff --git a/src/viewer.cpp b/src/viewer.cpp index fdbc2431..0f28330c 100644 --- a/src/viewer.cpp +++ b/src/viewer.cpp @@ -62,7 +62,7 @@ Viewer::Init() m_keyboard = new KeyboardControl(m_window); // -- Initialize shader program - m_program = new ShaderProgram("../src/glsl/vert_bare.glsl", "../src/glsl/frag_bare.glsl"); + m_program = new ShaderProgram("../src/glsl/simple.vert", "../src/glsl/simple.frag"); // -- Initialize scene m_scene = new Scene(m_width, m_height); From 281128a0850d7c2a322de9c33e28ef54f0e6b422 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Sun, 28 Feb 2016 11:13:54 -0500 Subject: [PATCH 19/64] Added FluidParticle. Split vao creation and vao update into separate calls --- src/fluidSolver/fluidSolver.cpp | 53 ++++++++++++++++++++++++++++++--- src/fluidSolver/fluidSolver.h | 30 +++++++++++++++++-- src/geometry/box.cpp | 27 +---------------- src/geometry/fluidGeo.cpp | 45 ++++++++++++++++++---------- src/geometry/fluidGeo.h | 12 ++++++++ src/geometry/geometry.cpp | 30 +++++++++++++++++++ src/geometry/geometry.h | 1 + 7 files changed, 149 insertions(+), 49 deletions(-) diff --git a/src/fluidSolver/fluidSolver.cpp b/src/fluidSolver/fluidSolver.cpp index f3a8b647..2c1b4e05 100644 --- a/src/fluidSolver/fluidSolver.cpp +++ b/src/fluidSolver/fluidSolver.cpp @@ -1,5 +1,30 @@ #include "fluidSolver.h" +// ---------------------------------------------------- // +// FluidParticle +// ---------------------------------------------------- // +FluidParticle::FluidParticle() +{ + +} + +FluidParticle::FluidParticle( + const glm::vec3& pos, + const glm::vec3& vel + ) :m_pos(pos), m_vel(vel) +{ + +} + +const glm::vec3& +FluidParticle::Position() const +{ + return m_pos; +} + +// ---------------------------------------------------- // +// FluidSolver +// ---------------------------------------------------- // FluidSolver::FluidSolver( const glm::vec3& containerDim, const glm::vec3& particleDim, @@ -18,18 +43,38 @@ FluidSolver::FluidSolver( { for (float z = -midZ; z < midX; z += m_separation) { - m_particlePositions.push_back( - glm::vec3(x, y, z) + m_particles.push_back( + new FluidParticle( + glm::vec3(x, y, z), + glm::vec3(0.f, 0.0001f, 0.0f) + ) ); } } } } -const std::vector& +FluidSolver::~FluidSolver() +{ + for (auto &particle : m_particles) + { + if (particle != nullptr) + { + delete particle; + } + } +} + +const std::vector FluidSolver::ParticlePositions() const { - return m_particlePositions; + std::vector positions; + for (auto &particle : m_particles) + { + positions.push_back(particle->Position()); + } + + return positions; } void diff --git a/src/fluidSolver/fluidSolver.h b/src/fluidSolver/fluidSolver.h index a643a5d2..afa96a44 100644 --- a/src/fluidSolver/fluidSolver.h +++ b/src/fluidSolver/fluidSolver.h @@ -5,6 +5,29 @@ #include #include +// ---------------------------------------------------- // +// FluidParticle +// ---------------------------------------------------- // +class FluidParticle +{ +public: + FluidParticle(); + FluidParticle( + const glm::vec3& pos, + const glm::vec3& vel + ); + + // Getters/setters + const glm::vec3& Position() const; + +protected: + glm::vec3 m_pos; + glm::vec3 m_vel; +}; + +// ---------------------------------------------------- // +// FluidSolver +// ---------------------------------------------------- // class FluidSolver { public: @@ -14,18 +37,19 @@ class FluidSolver const float& separation ); - const std::vector& ParticlePositions() const; + ~FluidSolver(); + const std::vector ParticlePositions() const; - // Called every frame virtual void Update(); protected: + // -- Particle info glm::vec3 m_containerDim; glm::vec3 m_particleDim; float m_separation; - std::vector m_particlePositions; + std::vector m_particles; }; #endif /* FLUIDSOLVER_H */ diff --git a/src/geometry/box.cpp b/src/geometry/box.cpp index 76a76fea..60ede6b0 100644 --- a/src/geometry/box.cpp +++ b/src/geometry/box.cpp @@ -116,33 +116,8 @@ Box::Create() InitIndices(); glGenVertexArrays(1, &m_vao); - glBindVertexArray(m_vao); - - // @todo: Is it faster to allocate all the buffers ahead of time? - - // -- Position - glGenBuffers(1, &m_posBuffer); - glBindBuffer(GL_ARRAY_BUFFER, m_posBuffer); - glBufferData( - GL_ARRAY_BUFFER, - m_positions.size() * sizeof(glm::vec3), - &m_positions[0], - GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, NULL); - - // -- Index - glGenBuffers(1, &m_idxBuffer); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_idxBuffer); - glBufferData( - GL_ELEMENT_ARRAY_BUFFER, - m_indices.size() * sizeof(GLushort), - &m_indices[0], - GL_STATIC_DRAW - ); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NULL); - - glBindVertexArray(NULL); + UpdateVAO(); } diff --git a/src/geometry/fluidGeo.cpp b/src/geometry/fluidGeo.cpp index cfccf243..6a4a1138 100644 --- a/src/geometry/fluidGeo.cpp +++ b/src/geometry/fluidGeo.cpp @@ -2,7 +2,7 @@ FluidGeo::FluidGeo( const vector& positions - ) + ) : m_useVao2(false) { UpdatePositions(positions); } @@ -37,24 +37,15 @@ FluidGeo::Create() InitIndices(); glGenVertexArrays(1, &m_vao); - glBindVertexArray(m_vao); - - // @todo: Is it faster to allocate all the buffers ahead of time? - - // -- Position - glGenBuffers(1, &m_posBuffer); - glBindBuffer(GL_ARRAY_BUFFER, m_posBuffer); - glBufferData( - GL_ARRAY_BUFFER, - m_positions.size() * sizeof(glm::vec3), - &m_positions[0], - GL_STATIC_DRAW); - glBindBuffer(GL_ARRAY_BUFFER, NULL); + glGenBuffers(1, &m_idxBuffer); - // -- Index + // -- Secondary vao to ping-pong transform feedback + glGenVertexArrays(1, &m_vao2); + glGenBuffers(1, &m_posBuffer2); - glGenBuffers(1, &m_idxBuffer); + // Share the same m_idxBuffer + glBindVertexArray(m_vao); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_idxBuffer); glBufferData( GL_ELEMENT_ARRAY_BUFFER, @@ -63,6 +54,28 @@ FluidGeo::Create() GL_STATIC_DRAW ); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NULL); + glBindVertexArray(NULL); + + glBindVertexArray(m_vao2); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_idxBuffer); + glBindVertexArray(NULL); + + UpdateVAO(); +} + +void +FluidGeo::UpdateVAO() +{ + GLuint curvao = m_useVao2 ? m_vao2 : m_vao; + GLuint curposBuffer = m_useVao2 ? m_posBuffer2 : m_posBuffer; + + glBindBuffer(GL_ARRAY_BUFFER, m_posBuffer); + glBufferData( + GL_ARRAY_BUFFER, + m_positions.size() * sizeof(glm::vec3), + &m_positions[0], + GL_DYNAMIC_COPY); + glBindBuffer(GL_ARRAY_BUFFER, NULL); glBindVertexArray(NULL); } diff --git a/src/geometry/fluidGeo.h b/src/geometry/fluidGeo.h index 9de2a990..734612e0 100644 --- a/src/geometry/fluidGeo.h +++ b/src/geometry/fluidGeo.h @@ -10,12 +10,24 @@ class FluidGeo : public Geometry FluidGeo(const vector& positions); virtual void Create(); + virtual void UpdateVAO(); virtual GLenum GLDrawMode() const; void UpdatePositions(const std::vector& positions); protected: virtual void InitIndices(); + + // -- Secondary vao to ping-pong transform feedback + + // Which vao we are using + bool m_useVao2; + + // Vertex buffers + GLuint m_posBuffer2; + + // Vertex array object + GLuint m_vao2; }; #endif diff --git a/src/geometry/geometry.cpp b/src/geometry/geometry.cpp index 254a5bf2..89fd706a 100644 --- a/src/geometry/geometry.cpp +++ b/src/geometry/geometry.cpp @@ -96,3 +96,33 @@ Geometry::GetGlobalTransformation() const return m_globalTransform; } +void +Geometry::UpdateVAO() +{ + glBindVertexArray(m_vao); + + // @todo: Is it faster to allocate all the buffers ahead of time? + + // -- Position + + glBindBuffer(GL_ARRAY_BUFFER, m_posBuffer); + glBufferData( + GL_ARRAY_BUFFER, + m_positions.size() * sizeof(glm::vec3), + &m_positions[0], + GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, NULL); + + // -- Index + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_idxBuffer); + glBufferData( + GL_ELEMENT_ARRAY_BUFFER, + m_indices.size() * sizeof(GLushort), + &m_indices[0], + GL_STATIC_DRAW + ); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NULL); + + glBindVertexArray(NULL); +} diff --git a/src/geometry/geometry.h b/src/geometry/geometry.h index 936f2849..07ebd1b4 100644 --- a/src/geometry/geometry.h +++ b/src/geometry/geometry.h @@ -28,6 +28,7 @@ class Geometry { public: virtual void Create() = 0; + virtual void UpdateVAO(); // -- OpenGL helpers virtual void SetDrawMode(DrawMode); From 69a51cb0171b1d0a84625ab01bb83ffc9c35f702 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Sun, 28 Feb 2016 11:53:01 -0500 Subject: [PATCH 20/64] [shaderProgram] - Added shader programs for transform feedbacks --- CMakeLists.txt | 4 ++- src/geometry/fluidGeo.cpp | 33 ++++++++++++++---- src/geometry/fluidGeo.h | 5 ++- src/glsl/particle_draw.vert | 13 +++---- src/glsl/particle_emit.vert | 17 ++++------ src/scene/scene.cpp | 18 +++++++--- src/scene/scene.h | 11 ++++-- src/shaderProgram/particleDrawProgram.cpp | 41 +++++++++++++++++++++++ src/shaderProgram/particleDrawProgram.h | 20 +++++++++++ src/shaderProgram/particleEmitProgram.cpp | 41 +++++++++++++++++++++++ src/shaderProgram/particleEmitProgram.h | 20 +++++++++++ src/{ => shaderProgram}/shaderProgram.cpp | 1 - src/{ => shaderProgram}/shaderProgram.h | 1 - src/viewer.cpp | 30 ++++++++++++++++- src/viewer.h | 15 ++++++++- 15 files changed, 230 insertions(+), 40 deletions(-) create mode 100644 src/shaderProgram/particleDrawProgram.cpp create mode 100644 src/shaderProgram/particleDrawProgram.h create mode 100644 src/shaderProgram/particleEmitProgram.cpp create mode 100644 src/shaderProgram/particleEmitProgram.h rename src/{ => shaderProgram}/shaderProgram.cpp (97%) rename src/{ => shaderProgram}/shaderProgram.h (95%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9be95e1b..52556b2b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,7 +94,9 @@ set(CORE_SRC src/loadShaders.cpp src/utility.cpp src/main.cpp - src/shaderProgram.cpp + src/shaderProgram/shaderProgram.cpp + src/shaderProgram/particleEmitProgram.cpp + src/shaderProgram/particleDrawProgram.cpp src/input/keyboardControl.cpp src/viewer.cpp src/camera/camera.cpp diff --git a/src/geometry/fluidGeo.cpp b/src/geometry/fluidGeo.cpp index 6a4a1138..f68bbec2 100644 --- a/src/geometry/fluidGeo.cpp +++ b/src/geometry/fluidGeo.cpp @@ -22,13 +22,20 @@ FluidGeo::UpdatePositions( } void -FluidGeo::InitIndices() +FluidGeo::EnableVertexAttributes() const { - int len = m_positions.size(); - for (int i = 0; i < len; ++i) - { - m_indices.push_back(i); - } + GLuint curvao = m_useVao2 ? m_vao2 : m_vao; + GLuint curposBuffer = m_useVao2 ? m_posBuffer2 : m_posBuffer; + + glBindVertexArray(curvao); + + // Enable vertex attributes + glEnableVertexAttribArray(0); + glBindBuffer(GL_ARRAY_BUFFER, curposBuffer); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0); + + // Bind element buffer + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_idxBuffer); } void @@ -69,7 +76,8 @@ FluidGeo::UpdateVAO() GLuint curvao = m_useVao2 ? m_vao2 : m_vao; GLuint curposBuffer = m_useVao2 ? m_posBuffer2 : m_posBuffer; - glBindBuffer(GL_ARRAY_BUFFER, m_posBuffer); + glBindVertexArray(curvao); + glBindBuffer(GL_ARRAY_BUFFER, curposBuffer); glBufferData( GL_ARRAY_BUFFER, m_positions.size() * sizeof(glm::vec3), @@ -79,3 +87,14 @@ FluidGeo::UpdateVAO() glBindVertexArray(NULL); } + +// ------ Private ------ // +void +FluidGeo::InitIndices() +{ + int len = m_positions.size(); + for (int i = 0; i < len; ++i) + { + m_indices.push_back(i); + } +} diff --git a/src/geometry/fluidGeo.h b/src/geometry/fluidGeo.h index 734612e0..bdfe643e 100644 --- a/src/geometry/fluidGeo.h +++ b/src/geometry/fluidGeo.h @@ -10,9 +10,12 @@ class FluidGeo : public Geometry FluidGeo(const vector& positions); virtual void Create(); - virtual void UpdateVAO(); virtual GLenum GLDrawMode() const; + // Override to update transform feedback buffers + virtual void EnableVertexAttributes() const; + virtual void UpdateVAO(); + void UpdatePositions(const std::vector& positions); protected: diff --git a/src/glsl/particle_draw.vert b/src/glsl/particle_draw.vert index efd8c4d7..8074682b 100644 --- a/src/glsl/particle_draw.vert +++ b/src/glsl/particle_draw.vert @@ -1,20 +1,15 @@ #version 150 core -#define POSITION_LOCATION 0 -#define VELOCITY_LOCATION 1 -#define SPAWNTIME_LOCATION 2 -#define LIFETIME_LOCATION 3 - precision highp float; precision highp int; uniform float u_time; uniform vec2 u_acceleration; -layout(location = POSITION_LOCATION) in vec2 a_position; -layout(location = VELOCITY_LOCATION) in vec2 a_velocity; -layout(location = SPAWNTIME_LOCATION) in float a_spawntime; -layout(location = LIFETIME_LOCATION) in float a_lifetime; +in vec2 a_position; +in vec2 a_velocity; +in float a_spawntime; +in float a_lifetime; void main() { diff --git a/src/glsl/particle_emit.vert b/src/glsl/particle_emit.vert index 263eeeaf..615b0dbd 100644 --- a/src/glsl/particle_emit.vert +++ b/src/glsl/particle_emit.vert @@ -1,22 +1,17 @@ #version 150 core -#define POSITION_LOCATION 0 -#define VELOCITY_LOCATION 1 -#define SPAWNTIME_LOCATION 2 -#define LIFETIME_LOCATION 3 -#define ID_LOCATION 4 precision highp float; precision highp int; -precision highp sampler3D; +layout(std140, column_major) uniform; uniform float u_time; uniform vec2 u_acceleration; -layout(location = POSITION_LOCATION) in vec2 a_position; -layout(location = VELOCITY_LOCATION) in vec2 a_velocity; -layout(location = SPAWNTIME_LOCATION) in float a_spawntime; -layout(location = LIFETIME_LOCATION) in float a_lifetime; -layout(location = ID_LOCATION) in float a_ID; +in vec2 a_position; +in vec2 a_velocity; +in float a_spawntime; +in float a_lifetime; +in float a_ID; out vec2 v_position; out vec2 v_velocity; diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index e9b58aa2..7957c440 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -1,6 +1,8 @@ #include #include +// #define TEST_SCENE + Scene::Scene() : Scene(800, 600) {} @@ -99,8 +101,15 @@ Scene::Draw( prog.Draw(*m_camera, *m_testBox); return; #endif +} - DrawFluidSolver(prog); +void +Scene::DrawTransformFeedback( + const ShaderProgram& progUpdate, + const ShaderProgram& progDraw + ) const +{ + DrawFluidSolver(progUpdate, progDraw); } void @@ -159,10 +168,9 @@ Scene::UpdateFluidSolver() void Scene::DrawFluidSolver( - const ShaderProgram& prog + const ShaderProgram& progUpdate, + const ShaderProgram& progDraw ) const { - // @todo: do scene graph traversal here - prog.Draw(*m_camera, *m_fluidContainer); - prog.Draw(*m_camera, *m_fluidGeo); + } diff --git a/src/scene/scene.h b/src/scene/scene.h index 3f6079af..10f73d9b 100644 --- a/src/scene/scene.h +++ b/src/scene/scene.h @@ -1,7 +1,7 @@ #ifndef SCENE_H #define SCENE_H -#include +#include #include #include #include @@ -21,6 +21,10 @@ class Scene virtual void Update(const KeyboardControl*); virtual void Draw(const ShaderProgram&) const; + virtual void DrawTransformFeedback( + const ShaderProgram& progUpdate, + const ShaderProgram& progDraw + ) const; virtual void CleanUp(); protected: @@ -35,7 +39,10 @@ class Scene void UpdateFluidSolver(); // -- Draw helpers - void DrawFluidSolver(const ShaderProgram& prog) const; + void DrawFluidSolver( + const ShaderProgram& progUpdate, + const ShaderProgram& progDraw + ) const; }; diff --git a/src/shaderProgram/particleDrawProgram.cpp b/src/shaderProgram/particleDrawProgram.cpp new file mode 100644 index 00000000..6c1b8774 --- /dev/null +++ b/src/shaderProgram/particleDrawProgram.cpp @@ -0,0 +1,41 @@ +#include + +void +ParticleDrawProgram::Draw( + const Camera& camera, + const Geometry& geo + ) const +{ + glUseProgram(m_programID); + + // Enable attributes + geo.EnableVertexAttributes(); + + // Set uniforms + + if (m_unifModel != -1) { + glUniformMatrix4fv( + m_unifModel, + 1, + GL_FALSE, + &(geo.GetLocalTransformation()[0][0]) + ); + } + + if (m_unifModel != -1) { + glUniformMatrix4fv( + m_unifViewProj, + 1, + GL_FALSE, + &(camera.GetViewProj()[0][0]) + ); + } + // Render + glDrawElements( + geo.GLDrawMode(), + geo.ElementCount(), + GL_UNSIGNED_SHORT, NULL + ); + + geo.DisableVertexAttributes(); +} diff --git a/src/shaderProgram/particleDrawProgram.h b/src/shaderProgram/particleDrawProgram.h new file mode 100644 index 00000000..38dcdbdd --- /dev/null +++ b/src/shaderProgram/particleDrawProgram.h @@ -0,0 +1,20 @@ +#ifndef PARTICLE_DRAW_PROGRAM_H +#define PARTICLE_DRAW_PROGRAM_H + +#include + +class ParticleDrawProgram : public ShaderProgram +{ +public: + ParticleDrawProgram( + const char* vertFilePath, + const char* fragFilePath + ) : ShaderProgram(vertFilePath, fragFilePath) {} + + virtual void Draw( + const Camera&, + const Geometry& + ) const; +}; + +#endif diff --git a/src/shaderProgram/particleEmitProgram.cpp b/src/shaderProgram/particleEmitProgram.cpp new file mode 100644 index 00000000..ad1f4ad8 --- /dev/null +++ b/src/shaderProgram/particleEmitProgram.cpp @@ -0,0 +1,41 @@ +#include + +void +ParticleEmitProgram::Draw( + const Camera& camera, + const Geometry& geo + ) const +{ + glUseProgram(m_programID); + + // Enable attributes + geo.EnableVertexAttributes(); + + // Set uniforms + + if (m_unifModel != -1) { + glUniformMatrix4fv( + m_unifModel, + 1, + GL_FALSE, + &(geo.GetLocalTransformation()[0][0]) + ); + } + + if (m_unifModel != -1) { + glUniformMatrix4fv( + m_unifViewProj, + 1, + GL_FALSE, + &(camera.GetViewProj()[0][0]) + ); + } + // Render + glDrawElements( + geo.GLDrawMode(), + geo.ElementCount(), + GL_UNSIGNED_SHORT, NULL + ); + + geo.DisableVertexAttributes(); +} diff --git a/src/shaderProgram/particleEmitProgram.h b/src/shaderProgram/particleEmitProgram.h new file mode 100644 index 00000000..7a37357f --- /dev/null +++ b/src/shaderProgram/particleEmitProgram.h @@ -0,0 +1,20 @@ +#ifndef PARTICLE_EMIT_PROGRAM_H +#define PARTICLE_EMIT_PROGRAM_H + +#include + +class ParticleEmitProgram : public ShaderProgram +{ +public: + ParticleEmitProgram( + const char* vertFilePath, + const char* fragFilePath + ) : ShaderProgram(vertFilePath, fragFilePath) {} + + virtual void Draw( + const Camera&, + const Geometry& + ) const; +}; + +#endif diff --git a/src/shaderProgram.cpp b/src/shaderProgram/shaderProgram.cpp similarity index 97% rename from src/shaderProgram.cpp rename to src/shaderProgram/shaderProgram.cpp index a1b5465d..45ff1113 100644 --- a/src/shaderProgram.cpp +++ b/src/shaderProgram/shaderProgram.cpp @@ -1,5 +1,4 @@ #include "shaderProgram.h" -#include "loadShaders.h" ShaderProgram::ShaderProgram( const char* vertFilePath, diff --git a/src/shaderProgram.h b/src/shaderProgram/shaderProgram.h similarity index 95% rename from src/shaderProgram.h rename to src/shaderProgram/shaderProgram.h index ce3845e1..81fc3724 100644 --- a/src/shaderProgram.h +++ b/src/shaderProgram/shaderProgram.h @@ -9,7 +9,6 @@ class ShaderProgram { public: - ShaderProgram() {} ShaderProgram( const char* vertFilePath, const char* fragFilePath diff --git a/src/viewer.cpp b/src/viewer.cpp index 0f28330c..e2a13fe7 100644 --- a/src/viewer.cpp +++ b/src/viewer.cpp @@ -7,7 +7,14 @@ Viewer::Viewer() Viewer::Viewer( int width, int height - ) : m_window(nullptr), m_program(nullptr), m_scene(nullptr) + ) : m_window(nullptr), +#ifdef TEST_SCENE + m_program(nullptr), +#else + m_programEmit(nullptr), + m_programDraw(nullptr), +#endif + m_scene(nullptr) { m_width = width; m_height = height; @@ -62,7 +69,12 @@ Viewer::Init() m_keyboard = new KeyboardControl(m_window); // -- Initialize shader program +#if TEST_SCENE m_program = new ShaderProgram("../src/glsl/simple.vert", "../src/glsl/simple.frag"); +#else + m_programEmit = new ParticleEmitProgram("../src/glsl/particle_emit.vert", "../src/glsl/particle_emit.frag"); + m_programDraw = new ParticleDrawProgram("../src/glsl/particle_draw.vert", "../src/glsl/particle_draw.frag"); +#endif // -- Initialize scene m_scene = new Scene(m_width, m_height); @@ -86,7 +98,13 @@ Viewer::Update() glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); m_scene->Update(m_keyboard); + +#ifdef TEST_SCENE m_scene->Draw(*m_program); +#else + m_scene->Draw(*m_programEmit); + m_scene->Draw(*m_programDraw); +#endif // Swap buffers glfwSwapBuffers(m_window); @@ -101,12 +119,22 @@ Viewer::Update() void Viewer::CleanUp() { +#ifdef TEST_SCENE m_program->CleanUp(); +#else + m_programEmit->CleanUp(); + m_programDraw->CleanUp(); +#endif m_scene->CleanUp(); // *N.B*: Deleting resources in reverse order of creation delete m_scene; +#ifdef TEST_SCENE delete m_program; +#else + delete m_programEmit; + delete m_programDraw; +#endif delete m_keyboard; // Close OpenGL window and terminate GLFW diff --git a/src/viewer.h b/src/viewer.h index f8976d66..31e6e983 100644 --- a/src/viewer.h +++ b/src/viewer.h @@ -1,6 +1,8 @@ #ifndef viewer_hpp #define viewer_hpp +// #define TEST_SCENE + // C++ libraries #include @@ -11,7 +13,13 @@ #include #include -#include +#ifdef TEST_SCENE +#include +#else +#include +#include +#endif + #include #include #include @@ -44,7 +52,12 @@ class Viewer GLFWwindow* m_window; KeyboardControl* m_keyboard; +#ifdef TEST_SCENE ShaderProgram* m_program; +#else + ParticleEmitProgram* m_programEmit; + ParticleDrawProgram* m_programDraw; +#endif Scene* m_scene; }; From 6959741f3f271b2519ca723b56e4cd551320a35e Mon Sep 17 00:00:00 2001 From: Trung Le Date: Sun, 28 Feb 2016 17:59:28 -0500 Subject: [PATCH 21/64] Checkpoint --- src/camera/camera.cpp | 23 +++ src/camera/camera.h | 2 + src/fluidSolver/fluidSolver.cpp | 30 +++- src/fluidSolver/fluidSolver.h | 7 +- src/geometry/fluidGeo.cpp | 83 +++++++++- src/geometry/fluidGeo.h | 24 ++- src/geometry/geometry.cpp | 1 - src/geometry/geometry.h | 12 +- src/glsl/particle_draw.frag | 3 +- src/glsl/particle_draw.vert | 20 +-- src/glsl/particle_emit.frag | 2 +- src/glsl/particle_emit.vert | 28 ++-- src/input/keyboardControl.cpp | 181 +++++++++++----------- src/scene/scene.cpp | 50 ++++-- src/scene/scene.h | 10 +- src/shaderProgram/particleDrawProgram.cpp | 32 +++- src/shaderProgram/particleDrawProgram.h | 7 +- src/shaderProgram/particleEmitProgram.cpp | 90 +++++++++-- src/shaderProgram/particleEmitProgram.h | 18 ++- src/shaderProgram/shaderProgram.cpp | 2 +- src/viewer.cpp | 3 +- 21 files changed, 451 insertions(+), 177 deletions(-) diff --git a/src/camera/camera.cpp b/src/camera/camera.cpp index ee241549..f8492b25 100644 --- a/src/camera/camera.cpp +++ b/src/camera/camera.cpp @@ -27,6 +27,7 @@ void Camera::RecomputeAttributes() { m_forward = m_target - m_eye; + m_right = glm::cross(m_forward, m_up); m_view = glm::lookAt( m_eye, @@ -84,3 +85,25 @@ Camera::Zoom( m_target += translation; RecomputeAttributes(); } + +void +Camera::TranslateAlongRight( + float amt + ) +{ + glm::vec3 translation = m_right * amt; + m_eye += translation; + m_target += translation; + RecomputeAttributes(); +} + +void +Camera::TranslateAlongUp( + float amt + ) +{ + glm::vec3 translation = m_up * amt; + m_eye += translation; + m_target += translation; + RecomputeAttributes(); +} diff --git a/src/camera/camera.h b/src/camera/camera.h index 3c2b56b5..122694a5 100644 --- a/src/camera/camera.h +++ b/src/camera/camera.h @@ -18,6 +18,8 @@ class Camera void RotateAboutRight(float deg); void RotateAboutUp(float deg); void Zoom(float amount); + void TranslateAlongRight(float amount); + void TranslateAlongUp(float amount); protected: diff --git a/src/fluidSolver/fluidSolver.cpp b/src/fluidSolver/fluidSolver.cpp index 2c1b4e05..62cbf1ce 100644 --- a/src/fluidSolver/fluidSolver.cpp +++ b/src/fluidSolver/fluidSolver.cpp @@ -16,12 +16,6 @@ FluidParticle::FluidParticle( } -const glm::vec3& -FluidParticle::Position() const -{ - return m_pos; -} - // ---------------------------------------------------- // // FluidSolver // ---------------------------------------------------- // @@ -77,6 +71,30 @@ FluidSolver::ParticlePositions() const return positions; } +const std::vector +FluidSolver::ParticleVelocities() const +{ + std::vector velocities; + for (auto &particle : m_particles) + { + velocities.push_back(particle->Velocity()); + } + + return velocities; +} + +const std::vector +FluidSolver::ParticleSpawnTimes() const +{ + std::vector spawnTimes; + for (auto &particle : m_particles) + { + spawnTimes.push_back(particle->SpawnTime()); + } + + return spawnTimes; +} + void FluidSolver::Update() { diff --git a/src/fluidSolver/fluidSolver.h b/src/fluidSolver/fluidSolver.h index afa96a44..bccc7c9b 100644 --- a/src/fluidSolver/fluidSolver.h +++ b/src/fluidSolver/fluidSolver.h @@ -18,11 +18,14 @@ class FluidParticle ); // Getters/setters - const glm::vec3& Position() const; + const glm::vec3& Position() const { return m_pos; } + const glm::vec3& Velocity() const { return m_vel; } + const float& SpawnTime() const { return m_spawnTime; } protected: glm::vec3 m_pos; glm::vec3 m_vel; + float m_spawnTime; }; // ---------------------------------------------------- // @@ -39,6 +42,8 @@ class FluidSolver ~FluidSolver(); const std::vector ParticlePositions() const; + const std::vector ParticleVelocities() const; + const std::vector ParticleSpawnTimes() const; virtual void Update(); diff --git a/src/geometry/fluidGeo.cpp b/src/geometry/fluidGeo.cpp index f68bbec2..1fcb49d9 100644 --- a/src/geometry/fluidGeo.cpp +++ b/src/geometry/fluidGeo.cpp @@ -1,10 +1,17 @@ #include +#include +#include FluidGeo::FluidGeo( - const vector& positions - ) : m_useVao2(false) + const vector& positions, + const vector& velocities, + const vector& spawnTimes + ) : + m_velocities(velocities), + m_spawnTimes(spawnTimes), + m_useVao2(true) { - UpdatePositions(positions); + m_positions = positions; } GLenum @@ -14,11 +21,27 @@ FluidGeo::GLDrawMode() const } void -FluidGeo::UpdatePositions( - const std::vector& positions - ) +FluidGeo::ToggleVao() { - m_positions = positions; + m_useVao2 = !m_useVao2; +} + +GLuint +FluidGeo::PosBuffer() const +{ + return !m_useVao2 ? m_posBuffer2 : m_posBuffer; +} + +GLuint +FluidGeo::VelBuffer() const +{ + return !m_useVao2 ? m_velBuffer2 : m_velBuffer; +} + +GLuint +FluidGeo::SpawnTimeBuffer() const +{ + return !m_useVao2 ? m_spawnTimeBuffer2 : m_spawnTimeBuffer; } void @@ -26,18 +49,40 @@ FluidGeo::EnableVertexAttributes() const { GLuint curvao = m_useVao2 ? m_vao2 : m_vao; GLuint curposBuffer = m_useVao2 ? m_posBuffer2 : m_posBuffer; + GLuint curvelBuffer = m_useVao2 ? m_velBuffer2 : m_velBuffer; + GLuint curspawntimeBuffer = m_useVao2 ? m_spawnTimeBuffer2 : m_spawnTimeBuffer; glBindVertexArray(curvao); // Enable vertex attributes - glEnableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, curposBuffer); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0); + glBindBuffer(GL_ARRAY_BUFFER, curvelBuffer); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)0); + + glBindBuffer(GL_ARRAY_BUFFER, curspawntimeBuffer); + glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, 0, (void*)0); + + glEnableVertexAttribArray(0); + glEnableVertexAttribArray(1); + glEnableVertexAttribArray(2); + // Bind element buffer glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_idxBuffer); } +void +FluidGeo::DisableVertexAttributes() const +{ + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NULL); + glDisableVertexAttribArray(0); + glDisableVertexAttribArray(1); + glDisableVertexAttribArray(2); + glBindBuffer(GL_ARRAY_BUFFER, NULL); + glBindVertexArray(NULL); +} + void FluidGeo::Create() { @@ -45,11 +90,14 @@ FluidGeo::Create() glGenVertexArrays(1, &m_vao); glGenBuffers(1, &m_posBuffer); + glGenBuffers(1, &m_velBuffer); glGenBuffers(1, &m_idxBuffer); // -- Secondary vao to ping-pong transform feedback glGenVertexArrays(1, &m_vao2); glGenBuffers(1, &m_posBuffer2); + glGenBuffers(1, &m_velBuffer2); + glGenBuffers(1, &m_spawnTimeBuffer2); // Share the same m_idxBuffer glBindVertexArray(m_vao); @@ -65,6 +113,7 @@ FluidGeo::Create() glBindVertexArray(m_vao2); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_idxBuffer); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NULL); glBindVertexArray(NULL); UpdateVAO(); @@ -75,6 +124,8 @@ FluidGeo::UpdateVAO() { GLuint curvao = m_useVao2 ? m_vao2 : m_vao; GLuint curposBuffer = m_useVao2 ? m_posBuffer2 : m_posBuffer; + GLuint curvelBuffer = m_useVao2 ? m_velBuffer2 : m_velBuffer; + GLuint curspawntimeBuffer = m_useVao2 ? m_spawnTimeBuffer2 : m_spawnTimeBuffer; glBindVertexArray(curvao); glBindBuffer(GL_ARRAY_BUFFER, curposBuffer); @@ -85,6 +136,22 @@ FluidGeo::UpdateVAO() GL_DYNAMIC_COPY); glBindBuffer(GL_ARRAY_BUFFER, NULL); + glBindBuffer(GL_ARRAY_BUFFER, curvelBuffer); + glBufferData( + GL_ARRAY_BUFFER, + m_velocities.size() * sizeof(glm::vec3), + &m_velocities[0], + GL_DYNAMIC_COPY); + glBindBuffer(GL_ARRAY_BUFFER, NULL); + + glBindBuffer(GL_ARRAY_BUFFER, curspawntimeBuffer); + glBufferData( + GL_ARRAY_BUFFER, + m_spawnTimes.size() * sizeof(float), + &m_spawnTimes[0], + GL_DYNAMIC_COPY); + glBindBuffer(GL_ARRAY_BUFFER, NULL); + glBindVertexArray(NULL); } diff --git a/src/geometry/fluidGeo.h b/src/geometry/fluidGeo.h index bdfe643e..5d78acef 100644 --- a/src/geometry/fluidGeo.h +++ b/src/geometry/fluidGeo.h @@ -1,5 +1,5 @@ #ifndef FLUID_GEO_H -#define GLUID_GEO_H +#define FLUID_GEO_H #include #include @@ -7,20 +7,32 @@ class FluidGeo : public Geometry { public: - FluidGeo(const vector& positions); + FluidGeo( + const vector& positions, + const vector& velocities, + const vector& spawnTimes + ); virtual void Create(); virtual GLenum GLDrawMode() const; // Override to update transform feedback buffers virtual void EnableVertexAttributes() const; + virtual void DisableVertexAttributes() const; virtual void UpdateVAO(); - void UpdatePositions(const std::vector& positions); + void ToggleVao(); + + GLuint PosBuffer() const; + GLuint VelBuffer() const; + GLuint SpawnTimeBuffer() const; protected: virtual void InitIndices(); + vector m_velocities; + vector m_spawnTimes; + // -- Secondary vao to ping-pong transform feedback // Which vao we are using @@ -29,6 +41,12 @@ class FluidGeo : public Geometry // Vertex buffers GLuint m_posBuffer2; + GLuint m_velBuffer; + GLuint m_velBuffer2; + + GLuint m_spawnTimeBuffer; + GLuint m_spawnTimeBuffer2; + // Vertex array object GLuint m_vao2; }; diff --git a/src/geometry/geometry.cpp b/src/geometry/geometry.cpp index 89fd706a..2b8c31dc 100644 --- a/src/geometry/geometry.cpp +++ b/src/geometry/geometry.cpp @@ -46,7 +46,6 @@ void Geometry::DisableVertexAttributes() const { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NULL); - glDisableVertexAttribArray(0); glBindBuffer(GL_ARRAY_BUFFER, NULL); glBindVertexArray(NULL); diff --git a/src/geometry/geometry.h b/src/geometry/geometry.h index 07ebd1b4..916ee127 100644 --- a/src/geometry/geometry.h +++ b/src/geometry/geometry.h @@ -39,6 +39,14 @@ class Geometry virtual void EnableVertexAttributes() const; virtual void DisableVertexAttributes() const; + // -- Color + void SetColor(const glm::vec4& color) { + m_color = color; + }; + const glm::vec4& GetColor() const { + return m_color; + }; + // -- Transformations void Translate(const float& x, const float& y, const float& z); void Rotate(const float& radx, const float& rady, const float& radz); @@ -50,9 +58,9 @@ class Geometry DrawMode m_drawMode = DrawMode_Shaded; // Data + glm::vec4 m_color = glm::vec4(1.0); + vector m_positions; - vector m_normals; - vector m_colors; vector m_indices; // Transformation diff --git a/src/glsl/particle_draw.frag b/src/glsl/particle_draw.frag index d13b1814..3ac92e1c 100644 --- a/src/glsl/particle_draw.frag +++ b/src/glsl/particle_draw.frag @@ -1,4 +1,5 @@ -#version 150 core +#version 330 core + precision highp float; precision highp int; diff --git a/src/glsl/particle_draw.vert b/src/glsl/particle_draw.vert index 8074682b..93cd76a8 100644 --- a/src/glsl/particle_draw.vert +++ b/src/glsl/particle_draw.vert @@ -1,23 +1,23 @@ -#version 150 core +#version 330 core precision highp float; precision highp int; +uniform mat4 u_model; +uniform mat4 u_viewProj; uniform float u_time; -uniform vec2 u_acceleration; -in vec2 a_position; -in vec2 a_velocity; -in float a_spawntime; -in float a_lifetime; +layout(location = 0) in vec3 a_position; +layout(location = 1) in vec3 a_velocity; +layout(location = 2) in float a_spawntime; void main() { + float life_time = 5000.0; float deltaTime = u_time - a_spawntime; - if (deltaTime < a_lifetime) { - gl_Position = vec4(a_position, 0.0, 1.0); + if (deltaTime < life_time) { + gl_Position = u_viewProj * u_model * vec4(a_position, 1.0); } else { - gl_Position = vec4(-100.0, -100.0, 0.0, 1.0); + gl_Position = u_viewProj * u_model * vec4(-100.0, -100.0, 0.0, 1.0); } - gl_PointSize = 10.0; } diff --git a/src/glsl/particle_emit.frag b/src/glsl/particle_emit.frag index 25961f4e..af247290 100644 --- a/src/glsl/particle_emit.frag +++ b/src/glsl/particle_emit.frag @@ -1,4 +1,4 @@ -#version 150 core +#version 330 core precision highp float; precision highp int; diff --git a/src/glsl/particle_emit.vert b/src/glsl/particle_emit.vert index 615b0dbd..23e8aabf 100644 --- a/src/glsl/particle_emit.vert +++ b/src/glsl/particle_emit.vert @@ -1,22 +1,19 @@ -#version 150 core +#version 330 core precision highp float; precision highp int; layout(std140, column_major) uniform; uniform float u_time; -uniform vec2 u_acceleration; +uniform vec3 u_accel; -in vec2 a_position; -in vec2 a_velocity; -in float a_spawntime; -in float a_lifetime; -in float a_ID; +layout(location = 0) in vec3 a_position; +layout(location = 1) in vec3 a_velocity; +layout(location = 2) in float a_spawntime; -out vec2 v_position; -out vec2 v_velocity; +out vec3 v_position; +out vec3 v_velocity; out float v_spawntime; -out float v_lifetime; float rand(vec2 co){ return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); @@ -24,16 +21,15 @@ float rand(vec2 co){ void main() { - if (a_spawntime == 0.0 || (u_time - a_spawntime > a_lifetime) || a_position.y < -0.5) { + float life_time = 5000.0; + if (a_spawntime == 0.0 || (u_time - a_spawntime > life_time) || a_position.y < -0.5) { // Generate a new particle - v_position = vec2(0.0, 0.0); - v_velocity = vec2(rand(vec2(a_ID, 0.0)) - 0.5, rand(vec2(a_ID, a_ID))); + v_position = vec3(0.0, 0.0, 0.0); + v_velocity = a_velocity; v_spawntime = u_time; - v_lifetime = 5000.0; } else { - v_velocity = a_velocity + 0.01 * u_acceleration; + v_velocity = a_velocity + 0.01 * u_accel; v_position = a_position + 0.01 * v_velocity; v_spawntime = a_spawntime; - v_lifetime = a_lifetime; } } diff --git a/src/input/keyboardControl.cpp b/src/input/keyboardControl.cpp index 7ddd1934..08259f92 100644 --- a/src/input/keyboardControl.cpp +++ b/src/input/keyboardControl.cpp @@ -1,8 +1,8 @@ #include "keyboardControl.h" KeyboardControl::KeyboardControl( - GLFWwindow* window - ) : m_window(window) + GLFWwindow* window + ) : m_window(window) { // Ensure we can capture key presses glfwSetInputMode(m_window, GLFW_STICKY_KEYS, GL_TRUE); @@ -10,101 +10,94 @@ KeyboardControl::KeyboardControl( bool KeyboardControl::KeyPressed( - const KeyCode& keyCode - ) const + const KeyCode& keyCode + ) const { - return glfwGetKey(m_window, keyCode) == GLFW_PRESS; + return glfwGetKey(m_window, keyCode) == GLFW_PRESS; } -// glm::mat4 ViewMatrix; -// glm::mat4 ProjectionMatrix; - -// glm::mat4 getViewMatrix(){ -// return ViewMatrix; -// } -// glm::mat4 getProjectionMatrix(){ -// return ProjectionMatrix; -// } - - -// // Initial position : on +Z -// glm::vec3 position = glm::vec3( 0, 0, 5 ); -// // Initial horizontal angle : toward -Z -// float horizontalAngle = 3.14f; -// // Initial vertical angle : none -// float verticalAngle = 0.0f; -// // Initial Field of View -// float initialFoV = 45.0f; - -// float speed = 3.0f; // 3 units / second -// float mouseSpeed = 0.005f; // void -// computeMatricesFromInputs(){ - -// // glfwGetTime is called only once, the first time this function is called -// static double lastTime = glfwGetTime(); - -// // Compute time difference between current and last frame -// double currentTime = glfwGetTime(); -// float deltaTime = float(currentTime - lastTime); - -// // Get mouse position -// double xpos, ypos; -// glfwGetCursorPos(window, &xpos, &ypos); - -// // Reset mouse position for next frame -// glfwSetCursorPos(window, 1024/2, 768/2); - -// // Compute new orientation -// horizontalAngle += mouseSpeed * float(1024/2 - xpos ); -// verticalAngle += mouseSpeed * float( 768/2 - ypos ); - -// // Direction : Spherical coordinates to Cartesian coordinates conversion -// glm::vec3 direction( -// cos(verticalAngle) * sin(horizontalAngle), -// sin(verticalAngle), -// cos(verticalAngle) * cos(horizontalAngle) -// ); - -// // Right vector -// glm::vec3 right = glm::vec3( -// sin(horizontalAngle - 3.14f/2.0f), -// 0, -// cos(horizontalAngle - 3.14f/2.0f) -// ); - -// // Up vector -// glm::vec3 up = glm::cross( right, direction ); - -// // Move forward -// if (glfwGetKey( window, GLFW_KEY_UP ) == GLFW_PRESS){ -// position += direction * deltaTime * speed; -// } -// // Move backward -// if (glfwGetKey( window, GLFW_KEY_DOWN ) == GLFW_PRESS){ -// position -= direction * deltaTime * speed; -// } -// // Strafe right -// if (glfwGetKey( window, GLFW_KEY_RIGHT ) == GLFW_PRESS){ -// position += right * deltaTime * speed; -// } -// // Strafe left -// if (glfwGetKey( window, GLFW_KEY_LEFT ) == GLFW_PRESS){ -// position -= right * deltaTime * speed; -// } - -// float FoV = initialFoV;// - 5 * glfwGetMouseWheel(); // Now GLFW 3 requires setting up a callback for this. It's a bit too complicated for this beginner's tutorial, so it's disabled instead. - -// // Projection matrix : 45∞ Field of View, 4:3 ratio, display range : 0.1 unit <-> 100 units -// ProjectionMatrix = glm::perspective(FoV, 4.0f / 3.0f, 0.1f, 100.0f); -// // Camera matrix -// ViewMatrix = glm::lookAt( -// position, // Camera is here -// position+direction, // and looks here : at the same position, plus "direction" -// up // Head is up (set to 0,-1,0 to look upside-down) -// ); - -// // For the next frame, the "last time" will be "now" -// lastTime = currentTime; +// KeyboardControl::MatricesFromInputs( +// glm::mat4& view, +// glm::mat4& proj +// ) +// { +// // Initial position : on +Z +// glm::vec3 position = glm::vec3( 0, 0, 5 ); +// // Initial horizontal angle : toward -Z +// float horizontalAngle = 3.14f; +// // Initial vertical angle : none +// float verticalAngle = 0.0f; +// // Initial Field of View +// float initialFoV = 45.0f; + +// float speed = 3.0f; // 3 units / second +// float mouseSpeed = 0.005f; + +// // glfwGetTime is called only once, the first time this function is called +// static double lastTime = glfwGetTime(); + +// // Compute time difference between current and last frame +// double currentTime = glfwGetTime(); +// float deltaTime = float(currentTime - lastTime); + +// // Get mouse position +// double xpos, ypos; +// glfwGetCursorPos(window, &xpos, &ypos); + +// // Reset mouse position for next frame +// glfwSetCursorPos(window, 1024/2, 768/2); + +// // Compute new orientation +// horizontalAngle += mouseSpeed * float(1024/2 - xpos ); +// verticalAngle += mouseSpeed * float( 768/2 - ypos ); + +// // Direction : Spherical coordinates to Cartesian coordinates conversion +// glm::vec3 direction( +// cos(verticalAngle) * sin(horizontalAngle), +// sin(verticalAngle), +// cos(verticalAngle) * cos(horizontalAngle) +// ); + +// // Right vector +// glm::vec3 right = glm::vec3( +// sin(horizontalAngle - 3.14f/2.0f), +// 0, +// cos(horizontalAngle - 3.14f/2.0f) +// ); + +// // Up vector +// glm::vec3 up = glm::cross( right, direction ); + +// // Move forward +// if (glfwGetKey( window, GLFW_KEY_UP ) == GLFW_PRESS){ +// position += direction * deltaTime * speed; +// } +// // Move backward +// if (glfwGetKey( window, GLFW_KEY_DOWN ) == GLFW_PRESS){ +// position -= direction * deltaTime * speed; +// } +// // Strafe right +// if (glfwGetKey( window, GLFW_KEY_RIGHT ) == GLFW_PRESS){ +// position += right * deltaTime * speed; +// } +// // Strafe left +// if (glfwGetKey( window, GLFW_KEY_LEFT ) == GLFW_PRESS){ +// position -= right * deltaTime * speed; +// } + +// float FoV = initialFoV;// - 5 * glfwGetMouseWheel(); // Now GLFW 3 requires setting up a callback for this. It's a bit too complicated for this beginner's tutorial, so it's disabled instead. + +// // Projection matrix : 45∞ Field of View, 4:3 ratio, display range : 0.1 unit <-> 100 units +// proj = glm::perspective(FoV, 4.0f / 3.0f, 0.1f, 100.0f); +// // Camera matrix +// view = glm::lookAt( +// position, // Camera is here +// position+direction, // and looks here : at the same position, plus "direction" +// up // Head is up (set to 0,-1,0 to look upside-down) +// ); + +// // For the next frame, the "last time" will be "now" +// lastTime = currentTime; // } diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 7957c440..801355f5 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -52,6 +52,7 @@ Scene::InitFromJson(const char* filepath) m_fluidContainer = new Box(); m_fluidContainer->Scale(containerDim.x, containerDim.y, containerDim.z); m_fluidContainer->SetDrawMode(DrawMode_Wireframe); + m_fluidContainer->SetColor(glm::vec4(0.0f, 1.0f, 1.0f, 1.0f)); m_fluidContainer->Create(); // -- Get particle information @@ -72,7 +73,11 @@ Scene::InitFromJson(const char* filepath) ); // -- Initialize fluid geo - m_fluidGeo = new FluidGeo(m_fluidSolver->ParticlePositions()); + m_fluidGeo = new FluidGeo( + m_fluidSolver->ParticlePositions(), + m_fluidSolver->ParticleVelocities(), + m_fluidSolver->ParticleSpawnTimes() + ); m_fluidGeo->Create(); } @@ -105,8 +110,8 @@ Scene::Draw( void Scene::DrawTransformFeedback( - const ShaderProgram& progUpdate, - const ShaderProgram& progDraw + const ParticleEmitProgram& progUpdate, + const ParticleDrawProgram& progDraw ) const { DrawFluidSolver(progUpdate, progDraw); @@ -145,32 +150,55 @@ Scene::UpdateCamera( { float rotateAmt = 20.0f; float zoomAmt = 0.01f; - if (kc->KeyPressed(Key_Down)) { + if (kc->KeyPressed(Key_Up)) { m_camera->RotateAboutUp(rotateAmt); - } else if (kc->KeyPressed(Key_Up)) { + } + if (kc->KeyPressed(Key_Down)) { m_camera->RotateAboutUp(-rotateAmt); - } else if (kc->KeyPressed(Key_Left)) { + } + + if (kc->KeyPressed(Key_Left)) { m_camera->RotateAboutRight(rotateAmt); - } else if (kc->KeyPressed(Key_Right)) { + } + + if (kc->KeyPressed(Key_Right)) { m_camera->RotateAboutRight(-rotateAmt); - } else if (kc->KeyPressed(Key_W)){ + } + + if (kc->KeyPressed(Key_W)){ m_camera->Zoom(zoomAmt); - } else if (kc->KeyPressed(Key_S)){ + } + + if (kc->KeyPressed(Key_S)){ m_camera->Zoom(-zoomAmt); } + + if (kc->KeyPressed(Key_D)){ + m_camera->TranslateAlongRight(zoomAmt); + } + + if (kc->KeyPressed(Key_A)){ + m_camera->TranslateAlongRight(-zoomAmt); + } } void Scene::UpdateFluidSolver() { m_fluidSolver->Update(); + m_fluidGeo->ToggleVao(); } void Scene::DrawFluidSolver( - const ShaderProgram& progUpdate, - const ShaderProgram& progDraw + const ParticleEmitProgram& progUpdate, + const ParticleDrawProgram& progDraw ) const { + // -- Draw boundary + progDraw.Draw(*m_camera, *m_fluidContainer); + // -- Draw particles + progUpdate.Draw(m_camera, m_fluidGeo); + progDraw.Draw(*m_camera, *m_fluidGeo); } diff --git a/src/scene/scene.h b/src/scene/scene.h index 10f73d9b..137b3a44 100644 --- a/src/scene/scene.h +++ b/src/scene/scene.h @@ -2,6 +2,8 @@ #define SCENE_H #include +#include +#include #include #include #include @@ -22,8 +24,8 @@ class Scene virtual void Update(const KeyboardControl*); virtual void Draw(const ShaderProgram&) const; virtual void DrawTransformFeedback( - const ShaderProgram& progUpdate, - const ShaderProgram& progDraw + const ParticleEmitProgram& progUpdate, + const ParticleDrawProgram& progDraw ) const; virtual void CleanUp(); @@ -40,8 +42,8 @@ class Scene // -- Draw helpers void DrawFluidSolver( - const ShaderProgram& progUpdate, - const ShaderProgram& progDraw + const ParticleEmitProgram& progUpdate, + const ParticleDrawProgram& progDraw ) const; }; diff --git a/src/shaderProgram/particleDrawProgram.cpp b/src/shaderProgram/particleDrawProgram.cpp index 6c1b8774..3cc37136 100644 --- a/src/shaderProgram/particleDrawProgram.cpp +++ b/src/shaderProgram/particleDrawProgram.cpp @@ -1,5 +1,15 @@ #include +ParticleDrawProgram::ParticleDrawProgram( + const char* vertFilePath, + const char* fragFilePath +) : ShaderProgram(vertFilePath, fragFilePath) +{ + // Get uniform locations + m_unifTime = glGetUniformLocation(m_programID, "u_time"); + m_unifColor = glGetUniformLocation(m_programID, "u_color"); +} + void ParticleDrawProgram::Draw( const Camera& camera, @@ -22,7 +32,7 @@ ParticleDrawProgram::Draw( ); } - if (m_unifModel != -1) { + if (m_unifViewProj != -1) { glUniformMatrix4fv( m_unifViewProj, 1, @@ -30,6 +40,26 @@ ParticleDrawProgram::Draw( &(camera.GetViewProj()[0][0]) ); } + + // @todo: maybe we only need to pass delta time through + static float lastTime = glfwGetTime(); + float currentTime = glfwGetTime(); + float deltaTime = float(currentTime - lastTime); + + if (m_unifTime != -1) { + glUniform1f( + m_unifTime, currentTime + ); + } + + if (m_unifColor != -1) { + glUniform4fv( + m_unifColor, + 1, + &(geo.GetColor()[0]) + ); + } + // Render glDrawElements( geo.GLDrawMode(), diff --git a/src/shaderProgram/particleDrawProgram.h b/src/shaderProgram/particleDrawProgram.h index 38dcdbdd..06ffd3aa 100644 --- a/src/shaderProgram/particleDrawProgram.h +++ b/src/shaderProgram/particleDrawProgram.h @@ -9,12 +9,17 @@ class ParticleDrawProgram : public ShaderProgram ParticleDrawProgram( const char* vertFilePath, const char* fragFilePath - ) : ShaderProgram(vertFilePath, fragFilePath) {} + ); virtual void Draw( const Camera&, const Geometry& ) const; + +protected: + // Uniform locations + int m_unifTime; + int m_unifColor; }; #endif diff --git a/src/shaderProgram/particleEmitProgram.cpp b/src/shaderProgram/particleEmitProgram.cpp index ad1f4ad8..e7d4a24b 100644 --- a/src/shaderProgram/particleEmitProgram.cpp +++ b/src/shaderProgram/particleEmitProgram.cpp @@ -1,24 +1,67 @@ #include +#include +#include +#include + +ParticleEmitProgram::ParticleEmitProgram( + const char* vertFilePath, + const char* fragFilePath +) : ShaderProgram(vertFilePath, fragFilePath) +{ + const GLchar* varyings[] = { + "a_position", "a_velocity", "a_spawntime" + }; + + glTransformFeedbackVaryings( + m_programID, + sizeof(varyings), + varyings, + GL_SEPARATE_ATTRIBS + ); + + glLinkProgram(m_programID); + + // Get uniform locations + m_unifTime = glGetUniformLocation(m_programID, "u_time"); + m_unifAccel = glGetUniformLocation(m_programID, "u_accel"); + + glGenTransformFeedbacks(2, &m_transformFeedback[0]); +} + +void +ParticleEmitProgram::ToggleVao() +{ + m_curr = (m_curr + 1) % 2; +} void ParticleEmitProgram::Draw( - const Camera& camera, - const Geometry& geo + const Camera* camera, + const FluidGeo* geo ) const { + glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, m_transformFeedback[m_curr]); + glUseProgram(m_programID); // Enable attributes - geo.EnableVertexAttributes(); + geo->EnableVertexAttributes(); + + // Set transform feedback buffer + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, geo->PosBuffer()); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, geo->VelBuffer()); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 2, geo->SpawnTimeBuffer()); - // Set uniforms + // Turn off rasterization - we are not drawing + glEnable(GL_RASTERIZER_DISCARD); + // -- Set uniforms if (m_unifModel != -1) { glUniformMatrix4fv( m_unifModel, 1, GL_FALSE, - &(geo.GetLocalTransformation()[0][0]) + &(geo->GetLocalTransformation()[0][0]) ); } @@ -27,15 +70,40 @@ ParticleEmitProgram::Draw( m_unifViewProj, 1, GL_FALSE, - &(camera.GetViewProj()[0][0]) + &(camera->GetViewProj()[0][0]) + ); + } + + // @todo: maybe we only need to pass delta time through + static float lastTime = glfwGetTime(); + float currentTime = glfwGetTime(); + float deltaTime = float(currentTime - lastTime); + + if (m_unifTime != -1) { + glUniform1f( + m_unifTime, currentTime ); } + + if (m_unifAccel != -1) { + glUniform3f( + m_unifAccel, 0.0f, -0.001f, 0.0f + ); + } + // Render - glDrawElements( - geo.GLDrawMode(), - geo.ElementCount(), - GL_UNSIGNED_SHORT, NULL + glBeginTransformFeedback(geo->GLDrawMode()); + glDrawTransformFeedback( + geo->GLDrawMode(), + m_transformFeedback[m_curr] ); + glEndTransformFeedback(); + + // Turn on rasterization - we are done + glDisable(GL_RASTERIZER_DISCARD); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, NULL); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, NULL); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 2, NULL); - geo.DisableVertexAttributes(); + geo->DisableVertexAttributes(); } diff --git a/src/shaderProgram/particleEmitProgram.h b/src/shaderProgram/particleEmitProgram.h index 7a37357f..869c5803 100644 --- a/src/shaderProgram/particleEmitProgram.h +++ b/src/shaderProgram/particleEmitProgram.h @@ -3,18 +3,30 @@ #include +class FluidGeo; + class ParticleEmitProgram : public ShaderProgram { public: ParticleEmitProgram( const char* vertFilePath, const char* fragFilePath - ) : ShaderProgram(vertFilePath, fragFilePath) {} + ); virtual void Draw( - const Camera&, - const Geometry& + const Camera*, + const FluidGeo* ) const; + + virtual void ToggleVao(); + +protected: + GLuint m_transformFeedback[2]; + int m_curr = 0; + + // Uniform locations + int m_unifTime; + int m_unifAccel; }; #endif diff --git a/src/shaderProgram/shaderProgram.cpp b/src/shaderProgram/shaderProgram.cpp index 45ff1113..6e643c2c 100644 --- a/src/shaderProgram/shaderProgram.cpp +++ b/src/shaderProgram/shaderProgram.cpp @@ -33,7 +33,7 @@ ShaderProgram::Draw( ); } - if (m_unifModel != -1) { + if (m_unifViewProj != -1) { glUniformMatrix4fv( m_unifViewProj, 1, diff --git a/src/viewer.cpp b/src/viewer.cpp index e2a13fe7..e1d23cab 100644 --- a/src/viewer.cpp +++ b/src/viewer.cpp @@ -102,8 +102,7 @@ Viewer::Update() #ifdef TEST_SCENE m_scene->Draw(*m_program); #else - m_scene->Draw(*m_programEmit); - m_scene->Draw(*m_programDraw); + m_scene->DrawTransformFeedback(*m_programEmit, *m_programDraw); #endif // Swap buffers From 5bb41ef57ff4f630496918580a34822530ff20d8 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Sun, 28 Feb 2016 19:03:39 -0500 Subject: [PATCH 22/64] Checkpoint --- src/glsl/particle_draw.vert | 2 +- src/scene/scene.cpp | 6 ++-- src/scene/scene.h | 4 +-- src/shaderProgram/particleDrawProgram.cpp | 2 +- src/shaderProgram/particleEmitProgram.cpp | 36 ++++++++++------------- src/shaderProgram/particleEmitProgram.h | 6 ++-- 6 files changed, 24 insertions(+), 32 deletions(-) diff --git a/src/glsl/particle_draw.vert b/src/glsl/particle_draw.vert index 93cd76a8..1044f90f 100644 --- a/src/glsl/particle_draw.vert +++ b/src/glsl/particle_draw.vert @@ -13,7 +13,7 @@ layout(location = 2) in float a_spawntime; void main() { - float life_time = 5000.0; + float life_time = 50000000.0; float deltaTime = u_time - a_spawntime; if (deltaTime < life_time) { gl_Position = u_viewProj * u_model * vec4(a_position, 1.0); diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 801355f5..73fabbd1 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -112,7 +112,7 @@ void Scene::DrawTransformFeedback( const ParticleEmitProgram& progUpdate, const ParticleDrawProgram& progDraw - ) const + ) { DrawFluidSolver(progUpdate, progDraw); } @@ -186,19 +186,19 @@ void Scene::UpdateFluidSolver() { m_fluidSolver->Update(); - m_fluidGeo->ToggleVao(); } void Scene::DrawFluidSolver( const ParticleEmitProgram& progUpdate, const ParticleDrawProgram& progDraw - ) const + ) { // -- Draw boundary progDraw.Draw(*m_camera, *m_fluidContainer); // -- Draw particles progUpdate.Draw(m_camera, m_fluidGeo); + m_fluidGeo->ToggleVao(); progDraw.Draw(*m_camera, *m_fluidGeo); } diff --git a/src/scene/scene.h b/src/scene/scene.h index 137b3a44..646cbc4f 100644 --- a/src/scene/scene.h +++ b/src/scene/scene.h @@ -26,7 +26,7 @@ class Scene virtual void DrawTransformFeedback( const ParticleEmitProgram& progUpdate, const ParticleDrawProgram& progDraw - ) const; + ); virtual void CleanUp(); protected: @@ -44,7 +44,7 @@ class Scene void DrawFluidSolver( const ParticleEmitProgram& progUpdate, const ParticleDrawProgram& progDraw - ) const; + ); }; diff --git a/src/shaderProgram/particleDrawProgram.cpp b/src/shaderProgram/particleDrawProgram.cpp index 3cc37136..e1aa85fe 100644 --- a/src/shaderProgram/particleDrawProgram.cpp +++ b/src/shaderProgram/particleDrawProgram.cpp @@ -60,7 +60,7 @@ ParticleDrawProgram::Draw( ); } - // Render + Render glDrawElements( geo.GLDrawMode(), geo.ElementCount(), diff --git a/src/shaderProgram/particleEmitProgram.cpp b/src/shaderProgram/particleEmitProgram.cpp index e7d4a24b..cd9048c1 100644 --- a/src/shaderProgram/particleEmitProgram.cpp +++ b/src/shaderProgram/particleEmitProgram.cpp @@ -25,13 +25,7 @@ ParticleEmitProgram::ParticleEmitProgram( m_unifTime = glGetUniformLocation(m_programID, "u_time"); m_unifAccel = glGetUniformLocation(m_programID, "u_accel"); - glGenTransformFeedbacks(2, &m_transformFeedback[0]); -} - -void -ParticleEmitProgram::ToggleVao() -{ - m_curr = (m_curr + 1) % 2; + glGenTransformFeedbacks(1, &m_transformFeedback); } void @@ -40,21 +34,10 @@ ParticleEmitProgram::Draw( const FluidGeo* geo ) const { - glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, m_transformFeedback[m_curr]); + glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, m_transformFeedback); glUseProgram(m_programID); - // Enable attributes - geo->EnableVertexAttributes(); - - // Set transform feedback buffer - glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, geo->PosBuffer()); - glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, geo->VelBuffer()); - glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 2, geo->SpawnTimeBuffer()); - - // Turn off rasterization - we are not drawing - glEnable(GL_RASTERIZER_DISCARD); - // -- Set uniforms if (m_unifModel != -1) { glUniformMatrix4fv( @@ -65,7 +48,7 @@ ParticleEmitProgram::Draw( ); } - if (m_unifModel != -1) { + if (m_unifViewProj != -1) { glUniformMatrix4fv( m_unifViewProj, 1, @@ -91,11 +74,22 @@ ParticleEmitProgram::Draw( ); } + // Enable attributes + geo->EnableVertexAttributes(); + + // Set transform feedback buffer + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, geo->PosBuffer()); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, geo->VelBuffer()); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 2, geo->SpawnTimeBuffer()); + + // Turn off rasterization - we are not drawing + glEnable(GL_RASTERIZER_DISCARD); + // Render glBeginTransformFeedback(geo->GLDrawMode()); glDrawTransformFeedback( geo->GLDrawMode(), - m_transformFeedback[m_curr] + m_transformFeedback ); glEndTransformFeedback(); diff --git a/src/shaderProgram/particleEmitProgram.h b/src/shaderProgram/particleEmitProgram.h index 869c5803..d0d2b45d 100644 --- a/src/shaderProgram/particleEmitProgram.h +++ b/src/shaderProgram/particleEmitProgram.h @@ -18,11 +18,9 @@ class ParticleEmitProgram : public ShaderProgram const FluidGeo* ) const; - virtual void ToggleVao(); - protected: - GLuint m_transformFeedback[2]; - int m_curr = 0; + + GLuint m_transformFeedback; // Uniform locations int m_unifTime; From 8c5fb0c1f6b3742c534c9c6701b94ce5a8759a98 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Sun, 28 Feb 2016 20:27:32 -0500 Subject: [PATCH 23/64] Merged drawprogram and emitprogram into one --- src/geometry/fluidGeo.cpp | 6 +- src/glsl/particle_draw.vert | 2 +- src/glsl/particle_emit.vert | 6 +- src/scene/scene.cpp | 32 +++-- src/scene/scene.h | 19 +-- src/shaderProgram/particleDrawProgram.cpp | 71 ---------- src/shaderProgram/particleDrawProgram.h | 25 ---- src/shaderProgram/particleEmitProgram.cpp | 157 ++++++++++++++++------ src/shaderProgram/particleEmitProgram.h | 44 ++++-- src/viewer.cpp | 31 ++--- src/viewer.h | 9 -- 11 files changed, 195 insertions(+), 207 deletions(-) delete mode 100644 src/shaderProgram/particleDrawProgram.cpp delete mode 100644 src/shaderProgram/particleDrawProgram.h diff --git a/src/geometry/fluidGeo.cpp b/src/geometry/fluidGeo.cpp index 1fcb49d9..1ab0d5b5 100644 --- a/src/geometry/fluidGeo.cpp +++ b/src/geometry/fluidGeo.cpp @@ -29,19 +29,19 @@ FluidGeo::ToggleVao() GLuint FluidGeo::PosBuffer() const { - return !m_useVao2 ? m_posBuffer2 : m_posBuffer; + return m_useVao2 ? m_posBuffer2 : m_posBuffer; } GLuint FluidGeo::VelBuffer() const { - return !m_useVao2 ? m_velBuffer2 : m_velBuffer; + return m_useVao2 ? m_velBuffer2 : m_velBuffer; } GLuint FluidGeo::SpawnTimeBuffer() const { - return !m_useVao2 ? m_spawnTimeBuffer2 : m_spawnTimeBuffer; + return m_useVao2 ? m_spawnTimeBuffer2 : m_spawnTimeBuffer; } void diff --git a/src/glsl/particle_draw.vert b/src/glsl/particle_draw.vert index 1044f90f..d749f990 100644 --- a/src/glsl/particle_draw.vert +++ b/src/glsl/particle_draw.vert @@ -18,6 +18,6 @@ void main() if (deltaTime < life_time) { gl_Position = u_viewProj * u_model * vec4(a_position, 1.0); } else { - gl_Position = u_viewProj * u_model * vec4(-100.0, -100.0, 0.0, 1.0); + gl_Position = u_viewProj * u_model * vec4(0.0, 0.0, 0.0, 1.0); } } diff --git a/src/glsl/particle_emit.vert b/src/glsl/particle_emit.vert index 23e8aabf..4f8fde9d 100644 --- a/src/glsl/particle_emit.vert +++ b/src/glsl/particle_emit.vert @@ -21,15 +21,15 @@ float rand(vec2 co){ void main() { - float life_time = 5000.0; + float life_time = 500000.0; if (a_spawntime == 0.0 || (u_time - a_spawntime > life_time) || a_position.y < -0.5) { // Generate a new particle v_position = vec3(0.0, 0.0, 0.0); v_velocity = a_velocity; v_spawntime = u_time; } else { - v_velocity = a_velocity + 0.01 * u_accel; - v_position = a_position + 0.01 * v_velocity; + v_velocity = a_velocity + u_accel; + v_position = a_position + v_velocity; v_spawntime = a_spawntime; } } diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 73fabbd1..29c9c95e 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -29,7 +29,9 @@ Scene::InitFromTestScene() } void -Scene::InitFromJson(const char* filepath) +Scene::InitFromJson( + const char* filepath + ) { // -- Parse JSON Json::Value root; @@ -83,7 +85,8 @@ Scene::InitFromJson(const char* filepath) void Scene::Update( - const KeyboardControl* kc + const KeyboardControl* kc, + ParticleEmitProgram& progEmit ) { UpdateCamera(kc); @@ -94,7 +97,7 @@ Scene::Update( return; #endif - UpdateFluidSolver(); + UpdateFluidSolver(progEmit); } void @@ -110,11 +113,11 @@ Scene::Draw( void Scene::DrawTransformFeedback( - const ParticleEmitProgram& progUpdate, - const ParticleDrawProgram& progDraw + const ShaderProgram& prog, + ParticleEmitProgram& progEmit ) { - DrawFluidSolver(progUpdate, progDraw); + DrawFluidSolver(prog, progEmit); } void @@ -183,22 +186,25 @@ Scene::UpdateCamera( } void -Scene::UpdateFluidSolver() +Scene::UpdateFluidSolver( + ParticleEmitProgram& progEmit + ) { m_fluidSolver->Update(); + + // Advect particle? + progEmit.Emit(m_fluidGeo); } void Scene::DrawFluidSolver( - const ParticleEmitProgram& progUpdate, - const ParticleDrawProgram& progDraw + const ShaderProgram& prog, + ParticleEmitProgram& progEmit ) { // -- Draw boundary - progDraw.Draw(*m_camera, *m_fluidContainer); + prog.Draw(*m_camera, *m_fluidContainer); // -- Draw particles - progUpdate.Draw(m_camera, m_fluidGeo); - m_fluidGeo->ToggleVao(); - progDraw.Draw(*m_camera, *m_fluidGeo); + progEmit.Draw(m_camera, m_fluidGeo); } diff --git a/src/scene/scene.h b/src/scene/scene.h index 646cbc4f..b6c056e8 100644 --- a/src/scene/scene.h +++ b/src/scene/scene.h @@ -3,7 +3,6 @@ #include #include -#include #include #include #include @@ -21,11 +20,14 @@ class Scene void InitFromTestScene(); void InitFromJson(const char* filepath); - virtual void Update(const KeyboardControl*); + virtual void Update( + const KeyboardControl*, + ParticleEmitProgram& prog + ); virtual void Draw(const ShaderProgram&) const; virtual void DrawTransformFeedback( - const ParticleEmitProgram& progUpdate, - const ParticleDrawProgram& progDraw + const ShaderProgram& prog, + ParticleEmitProgram& progEmit ); virtual void CleanUp(); @@ -38,14 +40,15 @@ class Scene // -- Update helpers void UpdateCamera(const KeyboardControl*); - void UpdateFluidSolver(); + void UpdateFluidSolver( + ParticleEmitProgram& prog + ); // -- Draw helpers void DrawFluidSolver( - const ParticleEmitProgram& progUpdate, - const ParticleDrawProgram& progDraw + const ShaderProgram& prog, + ParticleEmitProgram& progEmit ); - }; #endif diff --git a/src/shaderProgram/particleDrawProgram.cpp b/src/shaderProgram/particleDrawProgram.cpp deleted file mode 100644 index e1aa85fe..00000000 --- a/src/shaderProgram/particleDrawProgram.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include - -ParticleDrawProgram::ParticleDrawProgram( - const char* vertFilePath, - const char* fragFilePath -) : ShaderProgram(vertFilePath, fragFilePath) -{ - // Get uniform locations - m_unifTime = glGetUniformLocation(m_programID, "u_time"); - m_unifColor = glGetUniformLocation(m_programID, "u_color"); -} - -void -ParticleDrawProgram::Draw( - const Camera& camera, - const Geometry& geo - ) const -{ - glUseProgram(m_programID); - - // Enable attributes - geo.EnableVertexAttributes(); - - // Set uniforms - - if (m_unifModel != -1) { - glUniformMatrix4fv( - m_unifModel, - 1, - GL_FALSE, - &(geo.GetLocalTransformation()[0][0]) - ); - } - - if (m_unifViewProj != -1) { - glUniformMatrix4fv( - m_unifViewProj, - 1, - GL_FALSE, - &(camera.GetViewProj()[0][0]) - ); - } - - // @todo: maybe we only need to pass delta time through - static float lastTime = glfwGetTime(); - float currentTime = glfwGetTime(); - float deltaTime = float(currentTime - lastTime); - - if (m_unifTime != -1) { - glUniform1f( - m_unifTime, currentTime - ); - } - - if (m_unifColor != -1) { - glUniform4fv( - m_unifColor, - 1, - &(geo.GetColor()[0]) - ); - } - - Render - glDrawElements( - geo.GLDrawMode(), - geo.ElementCount(), - GL_UNSIGNED_SHORT, NULL - ); - - geo.DisableVertexAttributes(); -} diff --git a/src/shaderProgram/particleDrawProgram.h b/src/shaderProgram/particleDrawProgram.h deleted file mode 100644 index 06ffd3aa..00000000 --- a/src/shaderProgram/particleDrawProgram.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef PARTICLE_DRAW_PROGRAM_H -#define PARTICLE_DRAW_PROGRAM_H - -#include - -class ParticleDrawProgram : public ShaderProgram -{ -public: - ParticleDrawProgram( - const char* vertFilePath, - const char* fragFilePath - ); - - virtual void Draw( - const Camera&, - const Geometry& - ) const; - -protected: - // Uniform locations - int m_unifTime; - int m_unifColor; -}; - -#endif diff --git a/src/shaderProgram/particleEmitProgram.cpp b/src/shaderProgram/particleEmitProgram.cpp index cd9048c1..598ed3f9 100644 --- a/src/shaderProgram/particleEmitProgram.cpp +++ b/src/shaderProgram/particleEmitProgram.cpp @@ -4,53 +4,130 @@ #include ParticleEmitProgram::ParticleEmitProgram( - const char* vertFilePath, - const char* fragFilePath -) : ShaderProgram(vertFilePath, fragFilePath) + const char* vertEmitFilepath, + const char* fragEmitFilepath, + const char* vertDrawFilepath, + const char* fragDrawFilepath + ) { + // -- Load emit program + m_programEmit = LoadShaders(vertEmitFilepath, fragEmitFilepath); + const GLchar* varyings[] = { "a_position", "a_velocity", "a_spawntime" }; glTransformFeedbackVaryings( - m_programID, + m_programEmit, sizeof(varyings), varyings, GL_SEPARATE_ATTRIBS ); - glLinkProgram(m_programID); + glLinkProgram(m_programEmit); + + // -- Emit program uniforms + m_unifEmitTime = glGetUniformLocation(m_programEmit, "u_time"); + m_unifEmitAccel = glGetUniformLocation(m_programEmit, "u_accel"); + + // -- Load draw program + m_programDraw = LoadShaders(vertDrawFilepath, fragDrawFilepath); - // Get uniform locations - m_unifTime = glGetUniformLocation(m_programID, "u_time"); - m_unifAccel = glGetUniformLocation(m_programID, "u_accel"); + // -- Draw program uniforms + m_unifDrawModel = glGetUniformLocation(m_programDraw, "u_model"); + m_unifDrawViewProj = glGetUniformLocation(m_programDraw, "u_viewProj"); + m_unifDrawTime = glGetUniformLocation(m_programDraw, "u_time"); + m_unifDrawColor = glGetUniformLocation(m_programDraw, "u_color"); - glGenTransformFeedbacks(1, &m_transformFeedback); + // -- Generate two transform feedback buffers for ping-pong'ing + glGenTransformFeedbacks(2, &m_TFBuffers[0]); +} + +void +ParticleEmitProgram::Emit( + FluidGeo* geo + ) +{ + glUseProgram(m_programEmit); + + // -- Setup uniforms + // @todo: maybe we only need to pass delta time through + static float lastTime = glfwGetTime(); + float currentTime = glfwGetTime(); + float deltaTime = float(currentTime - lastTime); + + if (m_unifEmitTime != -1) { + glUniform1f( + m_unifEmitTime, currentTime + ); + } + + if (m_unifEmitAccel != -1) { + glUniform3f( + m_unifEmitAccel, 0.0f, -10.1f, 0.0f + ); + } + + // -- Bind transform feedback buffer + glBindTransformFeedback( + GL_TRANSFORM_FEEDBACK, + m_TFBuffers[m_curTFBuffer] + ); + + // -- Enable attributes + geo->EnableVertexAttributes(); + + // Toggle the vao we're using so that we can store the output of + // transform feedback into the secondary vao + geo->ToggleVao(); + + // Set transform feedback buffer + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, geo->PosBuffer()); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, geo->VelBuffer()); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 2, geo->SpawnTimeBuffer()); + + // Turn off rasterization - we are not drawing + glEnable(GL_RASTERIZER_DISCARD); + + // Render + glBeginTransformFeedback(geo->GLDrawMode()); + glDrawArrays(geo->GLDrawMode(), 0, geo->ElementCount()); + glEndTransformFeedback(); + + // Turn on rasterization - we are done + glDisable(GL_RASTERIZER_DISCARD); + glUseProgram(NULL); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, NULL); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, NULL); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 2, NULL); + glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, NULL); + geo->DisableVertexAttributes(); } void ParticleEmitProgram::Draw( const Camera* camera, const FluidGeo* geo - ) const + ) { - glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, m_transformFeedback); + glUseProgram(m_programDraw); - glUseProgram(m_programID); + // Enable attributes + geo->EnableVertexAttributes(); // -- Set uniforms - if (m_unifModel != -1) { + if (m_unifDrawModel != -1) { glUniformMatrix4fv( - m_unifModel, + m_unifDrawModel, 1, GL_FALSE, &(geo->GetLocalTransformation()[0][0]) ); } - if (m_unifViewProj != -1) { + if (m_unifDrawViewProj != -1) { glUniformMatrix4fv( - m_unifViewProj, + m_unifDrawViewProj, 1, GL_FALSE, &(camera->GetViewProj()[0][0]) @@ -62,42 +139,38 @@ ParticleEmitProgram::Draw( float currentTime = glfwGetTime(); float deltaTime = float(currentTime - lastTime); - if (m_unifTime != -1) { + if (m_unifDrawTime != -1) { glUniform1f( - m_unifTime, currentTime + m_unifDrawTime, currentTime ); } - if (m_unifAccel != -1) { - glUniform3f( - m_unifAccel, 0.0f, -0.001f, 0.0f + if (m_unifDrawColor != -1) { + glUniform4fv( + m_unifDrawColor, + 1, + &(geo->GetColor()[0]) ); } - // Enable attributes - geo->EnableVertexAttributes(); - - // Set transform feedback buffer - glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, geo->PosBuffer()); - glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, geo->VelBuffer()); - glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 2, geo->SpawnTimeBuffer()); - - // Turn off rasterization - we are not drawing - glEnable(GL_RASTERIZER_DISCARD); - // Render - glBeginTransformFeedback(geo->GLDrawMode()); - glDrawTransformFeedback( + glDrawElements( geo->GLDrawMode(), - m_transformFeedback + geo->ElementCount(), + GL_UNSIGNED_SHORT, NULL ); - glEndTransformFeedback(); + // glDrawTransformFeedback(geo->GLDrawMode(), m_TFBuffers[m_curTFBuffer]); + geo->DisableVertexAttributes(); - // Turn on rasterization - we are done - glDisable(GL_RASTERIZER_DISCARD); - glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, NULL); - glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, NULL); - glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 2, NULL); + // -- Toggle transform feedback buffer + m_curTFBuffer = (m_curTFBuffer + 1) % 2; +} - geo->DisableVertexAttributes(); +void +ParticleEmitProgram::CleanUp() +{ + glDeleteProgram(m_programEmit); + glDeleteProgram(m_programDraw); } + + diff --git a/src/shaderProgram/particleEmitProgram.h b/src/shaderProgram/particleEmitProgram.h index d0d2b45d..d38f2a81 100644 --- a/src/shaderProgram/particleEmitProgram.h +++ b/src/shaderProgram/particleEmitProgram.h @@ -1,30 +1,52 @@ #ifndef PARTICLE_EMIT_PROGRAM_H #define PARTICLE_EMIT_PROGRAM_H -#include +#include +#include +#include class FluidGeo; -class ParticleEmitProgram : public ShaderProgram +class ParticleEmitProgram { public: ParticleEmitProgram( - const char* vertFilePath, - const char* fragFilePath + const char* vertEmitFilepath, + const char* fragEmitFilepath, + const char* vertDrawFilepath, + const char* fragDrawFilepath ); + // First pass + virtual void Emit( + FluidGeo* + ); + + // Second pass virtual void Draw( const Camera*, const FluidGeo* - ) const; - -protected: + ); - GLuint m_transformFeedback; + virtual void CleanUp(); - // Uniform locations - int m_unifTime; - int m_unifAccel; +protected: + GLuint m_programEmit; + GLuint m_programDraw; + + // Transform feedback buffer + GLuint m_TFBuffers[2]; + char m_curTFBuffer = 0; + + // Uniform locations for emit program + int m_unifEmitTime; + int m_unifEmitAccel; + + // Uniform locations for draw program + int m_unifDrawTime; + int m_unifDrawColor; + int m_unifDrawModel; + int m_unifDrawViewProj; }; #endif diff --git a/src/viewer.cpp b/src/viewer.cpp index e1d23cab..8b6917dc 100644 --- a/src/viewer.cpp +++ b/src/viewer.cpp @@ -8,12 +8,8 @@ Viewer::Viewer( int width, int height ) : m_window(nullptr), -#ifdef TEST_SCENE m_program(nullptr), -#else m_programEmit(nullptr), - m_programDraw(nullptr), -#endif m_scene(nullptr) { m_width = width; @@ -69,12 +65,13 @@ Viewer::Init() m_keyboard = new KeyboardControl(m_window); // -- Initialize shader program -#if TEST_SCENE m_program = new ShaderProgram("../src/glsl/simple.vert", "../src/glsl/simple.frag"); -#else - m_programEmit = new ParticleEmitProgram("../src/glsl/particle_emit.vert", "../src/glsl/particle_emit.frag"); - m_programDraw = new ParticleDrawProgram("../src/glsl/particle_draw.vert", "../src/glsl/particle_draw.frag"); -#endif + m_programEmit = new ParticleEmitProgram( + "../src/glsl/particle_emit.vert", + "../src/glsl/particle_emit.frag", + "../src/glsl/particle_draw.vert", + "../src/glsl/particle_draw.frag" + ); // -- Initialize scene m_scene = new Scene(m_width, m_height); @@ -97,12 +94,12 @@ Viewer::Update() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - m_scene->Update(m_keyboard); + m_scene->Update(m_keyboard, *m_programEmit); #ifdef TEST_SCENE m_scene->Draw(*m_program); #else - m_scene->DrawTransformFeedback(*m_programEmit, *m_programDraw); + m_scene->DrawTransformFeedback(*m_program, *m_programEmit); #endif // Swap buffers @@ -118,22 +115,14 @@ Viewer::Update() void Viewer::CleanUp() { -#ifdef TEST_SCENE m_program->CleanUp(); -#else m_programEmit->CleanUp(); - m_programDraw->CleanUp(); -#endif m_scene->CleanUp(); - // *N.B*: Deleting resources in reverse order of creation + // *N.B*: Should delete resources in reverse order of creation delete m_scene; -#ifdef TEST_SCENE - delete m_program; -#else delete m_programEmit; - delete m_programDraw; -#endif + delete m_program; delete m_keyboard; // Close OpenGL window and terminate GLFW diff --git a/src/viewer.h b/src/viewer.h index 31e6e983..f26d9b12 100644 --- a/src/viewer.h +++ b/src/viewer.h @@ -13,13 +13,8 @@ #include #include -#ifdef TEST_SCENE #include -#else #include -#include -#endif - #include #include #include @@ -52,12 +47,8 @@ class Viewer GLFWwindow* m_window; KeyboardControl* m_keyboard; -#ifdef TEST_SCENE ShaderProgram* m_program; -#else ParticleEmitProgram* m_programEmit; - ParticleDrawProgram* m_programDraw; -#endif Scene* m_scene; }; From 415831766461e678d209590683b8346ecfb3f46c Mon Sep 17 00:00:00 2001 From: Trung Le Date: Tue, 1 Mar 2016 00:01:35 -0500 Subject: [PATCH 24/64] Advect particle. Generally refactoring and code cleaning --- CMakeLists.txt | 3 +- README.md | 2 +- src/camera/camera.cpp | 2 +- src/fluidSolver/fluidSolver.h | 2 +- src/geometry/box.cpp | 15 ++ src/geometry/box.h | 2 + src/geometry/fluidGeo.cpp | 17 +- src/geometry/fluidGeo.h | 4 +- src/geometry/geometry.cpp | 5 + src/geometry/geometry.h | 5 + ...article_emit.frag => particle_advect.frag} | 0 ...article_emit.vert => particle_advect.vert} | 14 +- src/glsl/particle_draw.frag | 10 +- src/glsl/particle_draw.vert | 21 +- src/loadShaders.cpp | 94 ++++--- src/loadShaders.h | 2 + src/scene/scene.cpp | 17 +- src/scene/scene.h | 10 +- src/scene/scene.json | 12 +- src/shaderProgram/particleAdvectProgram.cpp | 237 ++++++++++++++++++ ...eEmitProgram.h => particleAdvectProgram.h} | 31 ++- src/shaderProgram/particleEmitProgram.cpp | 176 ------------- src/viewer.cpp | 17 +- src/viewer.h | 4 +- 24 files changed, 403 insertions(+), 299 deletions(-) rename src/glsl/{particle_emit.frag => particle_advect.frag} (100%) rename src/glsl/{particle_emit.vert => particle_advect.vert} (52%) create mode 100644 src/shaderProgram/particleAdvectProgram.cpp rename src/shaderProgram/{particleEmitProgram.h => particleAdvectProgram.h} (54%) delete mode 100644 src/shaderProgram/particleEmitProgram.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 52556b2b..d9437e1a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -95,8 +95,7 @@ set(CORE_SRC src/utility.cpp src/main.cpp src/shaderProgram/shaderProgram.cpp - src/shaderProgram/particleEmitProgram.cpp - src/shaderProgram/particleDrawProgram.cpp + src/shaderProgram/particleAdvectProgram.cpp src/input/keyboardControl.cpp src/viewer.cpp src/camera/camera.cpp diff --git a/README.md b/README.md index e2f4c8d7..5a5dfc3a 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,9 @@ Submit on canvas with a direct link to your pull request on GitHub And you're done! - # Third Party +## CIS277 Camera code ## glfw, [Website](http://www.glfw.org/) ## glew, [Website](http://glew.sourceforge.net/) ## glm, [Website](http://glm.g-truc.net/0.9.7/index.html) diff --git a/src/camera/camera.cpp b/src/camera/camera.cpp index f8492b25..9dea616d 100644 --- a/src/camera/camera.cpp +++ b/src/camera/camera.cpp @@ -12,7 +12,7 @@ Camera::Camera( ) : m_width(width), m_height(height), m_fov(45), - m_eye(glm::vec3(0.0f, 0.0f, -12.0f)), + m_eye(glm::vec3(0.0f, 0.0f, -20.0f)), m_target(glm::vec3(0.0f)), m_up(glm::vec3(0.0f, 1.0f, 0.0f)), m_right(glm::vec3(1.0f, 0.0f, 0.0f)), diff --git a/src/fluidSolver/fluidSolver.h b/src/fluidSolver/fluidSolver.h index bccc7c9b..256a1970 100644 --- a/src/fluidSolver/fluidSolver.h +++ b/src/fluidSolver/fluidSolver.h @@ -25,7 +25,7 @@ class FluidParticle protected: glm::vec3 m_pos; glm::vec3 m_vel; - float m_spawnTime; + float m_spawnTime = 0.0f; }; // ---------------------------------------------------- // diff --git a/src/geometry/box.cpp b/src/geometry/box.cpp index 60ede6b0..0f7dc298 100644 --- a/src/geometry/box.cpp +++ b/src/geometry/box.cpp @@ -1,5 +1,20 @@ #include "box.h" +const glm::vec3 +Box::GetMinBoundary() const +{ + // @todo: use global transformation when building a scene graph + return m_translation - m_scale; +} + +const glm::vec3 +Box::GetMaxBoundary() const +{ + // @todo: use global transformation when building a scene graph + return m_translation + m_scale; +} + + void Box::InitPositions() { diff --git a/src/geometry/box.h b/src/geometry/box.h index 88764d45..41224b5a 100644 --- a/src/geometry/box.h +++ b/src/geometry/box.h @@ -7,6 +7,8 @@ class Box : public Geometry { public: virtual void Create(); + virtual const glm::vec3 GetMinBoundary() const; + virtual const glm::vec3 GetMaxBoundary() const; protected: virtual void InitPositions(); diff --git a/src/geometry/fluidGeo.cpp b/src/geometry/fluidGeo.cpp index 1ab0d5b5..ae0ef88c 100644 --- a/src/geometry/fluidGeo.cpp +++ b/src/geometry/fluidGeo.cpp @@ -9,7 +9,7 @@ FluidGeo::FluidGeo( ) : m_velocities(velocities), m_spawnTimes(spawnTimes), - m_useVao2(true) + m_useVao2(false) { m_positions = positions; } @@ -67,15 +67,11 @@ FluidGeo::EnableVertexAttributes() const glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glEnableVertexAttribArray(2); - - // Bind element buffer - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_idxBuffer); } void FluidGeo::DisableVertexAttributes() const { - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NULL); glDisableVertexAttribArray(0); glDisableVertexAttribArray(1); glDisableVertexAttribArray(2); @@ -88,10 +84,12 @@ FluidGeo::Create() { InitIndices(); + glGenBuffers(1, &m_idxBuffer); + glGenVertexArrays(1, &m_vao); glGenBuffers(1, &m_posBuffer); glGenBuffers(1, &m_velBuffer); - glGenBuffers(1, &m_idxBuffer); + glGenBuffers(1, &m_spawnTimeBuffer); // -- Secondary vao to ping-pong transform feedback glGenVertexArrays(1, &m_vao2); @@ -116,11 +114,14 @@ FluidGeo::Create() glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NULL); glBindVertexArray(NULL); - UpdateVAO(); + UpdateVao(); + ToggleVao(); + UpdateVao(); + ToggleVao(); } void -FluidGeo::UpdateVAO() +FluidGeo::UpdateVao() { GLuint curvao = m_useVao2 ? m_vao2 : m_vao; GLuint curposBuffer = m_useVao2 ? m_posBuffer2 : m_posBuffer; diff --git a/src/geometry/fluidGeo.h b/src/geometry/fluidGeo.h index 5d78acef..1373fb6c 100644 --- a/src/geometry/fluidGeo.h +++ b/src/geometry/fluidGeo.h @@ -19,7 +19,9 @@ class FluidGeo : public Geometry // Override to update transform feedback buffers virtual void EnableVertexAttributes() const; virtual void DisableVertexAttributes() const; - virtual void UpdateVAO(); + + // Vao -> vertex array object + virtual void UpdateVao(); void ToggleVao(); diff --git a/src/geometry/geometry.cpp b/src/geometry/geometry.cpp index 2b8c31dc..e80bca68 100644 --- a/src/geometry/geometry.cpp +++ b/src/geometry/geometry.cpp @@ -59,6 +59,7 @@ Geometry::Translate( ) { m_localTransform = glm::translate(m_localTransform, glm::vec3(x, y, z)); + m_translation += glm::vec3(x, y, z); } void @@ -71,6 +72,8 @@ Geometry::Rotate( m_localTransform = glm::rotate(m_localTransform, radx, glm::vec3(1.0f, 0.0f, 0.0f)); m_localTransform = glm::rotate(m_localTransform, rady, glm::vec3(0.0f, 1.0f, 0.0f)); m_localTransform = glm::rotate(m_localTransform, radz, glm::vec3(0.0f, 0.0f, 1.0f)); + + m_orientation *= glm::quat(glm::vec3(radx, rady, radz)); } void @@ -81,6 +84,8 @@ Geometry::Scale( ) { m_localTransform = glm::scale(m_localTransform, glm::vec3(scalex, scaley, scalez)); + + m_scale += glm::vec3(scalex, scaley, scalez); } glm::mat4 diff --git a/src/geometry/geometry.h b/src/geometry/geometry.h index 916ee127..9ddd4a11 100644 --- a/src/geometry/geometry.h +++ b/src/geometry/geometry.h @@ -7,6 +7,8 @@ #include #include #include +#include +#include // C++ @@ -66,6 +68,9 @@ class Geometry // Transformation glm::mat4 m_localTransform; glm::mat4 m_globalTransform; + glm::vec3 m_translation; + glm::quat m_orientation; + glm::vec3 m_scale; // Vertex buffers GLuint m_posBuffer; diff --git a/src/glsl/particle_emit.frag b/src/glsl/particle_advect.frag similarity index 100% rename from src/glsl/particle_emit.frag rename to src/glsl/particle_advect.frag diff --git a/src/glsl/particle_emit.vert b/src/glsl/particle_advect.vert similarity index 52% rename from src/glsl/particle_emit.vert rename to src/glsl/particle_advect.vert index 4f8fde9d..85b362c9 100644 --- a/src/glsl/particle_emit.vert +++ b/src/glsl/particle_advect.vert @@ -21,15 +21,7 @@ float rand(vec2 co){ void main() { - float life_time = 500000.0; - if (a_spawntime == 0.0 || (u_time - a_spawntime > life_time) || a_position.y < -0.5) { - // Generate a new particle - v_position = vec3(0.0, 0.0, 0.0); - v_velocity = a_velocity; - v_spawntime = u_time; - } else { - v_velocity = a_velocity + u_accel; - v_position = a_position + v_velocity; - v_spawntime = a_spawntime; - } + v_velocity = a_velocity + vec3(0.0, 0.01, 0.0);// u_time * u_accel + vec3(0.0, 0.1, 0.0); + v_position = a_position + u_time * v_velocity + vec3(0.0, 0.1, 0.0); + v_spawntime = a_spawntime; } diff --git a/src/glsl/particle_draw.frag b/src/glsl/particle_draw.frag index 3ac92e1c..02d40f99 100644 --- a/src/glsl/particle_draw.frag +++ b/src/glsl/particle_draw.frag @@ -4,10 +4,18 @@ precision highp float; precision highp int; uniform vec4 u_color; +uniform vec3 u_minBoundary; +uniform vec3 u_maxBoundary; + +in vec3 v_position; out vec4 color; void main() { - color = u_color; + if (v_position.y < u_minBoundary.y) { + color = vec4(0.0, -v_position.y * 0.1, 1.0, 1.0); + } else { + color = u_color; + } } diff --git a/src/glsl/particle_draw.vert b/src/glsl/particle_draw.vert index d749f990..9a4b8c0e 100644 --- a/src/glsl/particle_draw.vert +++ b/src/glsl/particle_draw.vert @@ -11,13 +11,20 @@ layout(location = 0) in vec3 a_position; layout(location = 1) in vec3 a_velocity; layout(location = 2) in float a_spawntime; +out vec3 v_position; + +float rand(vec2 co){ + return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); +} + void main() { - float life_time = 50000000.0; - float deltaTime = u_time - a_spawntime; - if (deltaTime < life_time) { - gl_Position = u_viewProj * u_model * vec4(a_position, 1.0); - } else { - gl_Position = u_viewProj * u_model * vec4(0.0, 0.0, 0.0, 1.0); - } + float r = rand(vec2(a_position.y, gl_VertexID)); + vec3 position = vec3( + a_position.x * r * (u_time * u_time), + a_position.y - (u_time * u_time * u_time) + r * u_time * u_time * u_time * u_time * -abs(sin(a_position.x)), + a_position.z * r * (u_time * u_time) + ); + gl_Position = u_viewProj * u_model * vec4(position, 1.0); + v_position = position; } diff --git a/src/loadShaders.cpp b/src/loadShaders.cpp index 6fafe206..f2ff10e7 100644 --- a/src/loadShaders.cpp +++ b/src/loadShaders.cpp @@ -11,79 +11,63 @@ #include "loadShaders.h" GLuint -LoadShaders( - const char* vertFilePath, - const char* fragFilePath +CreateShader( + const char* filepath, + const GLenum shaderType ) { - GLuint vertexShaderID = glCreateShader(GL_VERTEX_SHADER); - GLuint fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER); + GLuint shaderId = glCreateShader(shaderType); - // Read vertex shader code from file - std::string vertexShaderCode; - std::ifstream vertexShaderStream(vertFilePath, std::ios::in); + // Read shader code from file + std::string shaderCode; + std::ifstream shaderStream(filepath, std::ios::in); - if (vertexShaderStream.is_open()) { + if (shaderStream.is_open()) { std::string line = " "; - while (getline(vertexShaderStream, line)) { - vertexShaderCode += "\n" + line; + while (getline(shaderStream, line)) { + shaderCode += "\n" + line; } - vertexShaderStream.close(); + shaderStream.close(); } else { printf("Can't open"); getchar(); return 0; } - // Read fragment shader code from file - std::string fragmentShaderCode; - std::ifstream fragmentShaderStream(fragFilePath, std::ios::in); - if (fragmentShaderStream.is_open()) { - std::string line = " "; - while(getline(fragmentShaderStream, line)) { - fragmentShaderCode += "\n" + line; - } - - fragmentShaderStream.close(); - } - GLint result = GL_FALSE; int infoLogLength; - // Compile vertex shader - printf("Compiling vertex shader: %s\n", vertFilePath); - char const* vertexSourcePointer = vertexShaderCode.c_str(); - glShaderSource(vertexShaderID, 1, &vertexSourcePointer, NULL); - glCompileShader(vertexShaderID); + // Compile shader + printf("Compiling %s shader: %s\n", shaderType == GL_VERTEX_SHADER ? "vertex" : "fragment", filepath); + char const* sourcePointer = shaderCode.c_str(); + glShaderSource(shaderId, 1, &sourcePointer, NULL); + glCompileShader(shaderId); - // Check vertex shader - glGetShaderiv(vertexShaderID, GL_COMPILE_STATUS, &result); - glGetShaderiv(vertexShaderID, GL_INFO_LOG_LENGTH, &infoLogLength); + // Check shader + glGetShaderiv(shaderId, GL_COMPILE_STATUS, &result); + glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &infoLogLength); if (infoLogLength > 0) { - std::vector vertexShaderErrorMessage(infoLogLength + 1); - glGetShaderInfoLog(vertexShaderID, infoLogLength, NULL, &vertexShaderErrorMessage[0]); - printf("%s\n", &vertexShaderErrorMessage[0]); + std::vector shaderErrorMessage(infoLogLength + 1); + glGetShaderInfoLog(shaderId, infoLogLength, NULL, &shaderErrorMessage[0]); + printf("%s\n", &shaderErrorMessage[0]); } - // Compile fragment shader - printf("Compiling fragment shader: %s\n", fragFilePath); - char const * fragmentSourcePointer = fragmentShaderCode.c_str(); - glShaderSource(fragmentShaderID, 1, &fragmentSourcePointer, NULL); - glCompileShader(fragmentShaderID); + return shaderId; +} - // Check fragment shader - glGetShaderiv(fragmentShaderID, GL_COMPILE_STATUS, &result); - glGetShaderiv(fragmentShaderID, GL_INFO_LOG_LENGTH, &infoLogLength); - if (infoLogLength > 0) { - std::vector fragmentShaderErrorMessage(infoLogLength + 1); - glGetShaderInfoLog(fragmentShaderID, infoLogLength, NULL, &fragmentShaderErrorMessage[0]); - printf("%s\n", &fragmentShaderErrorMessage[0]); - } +GLuint +LinkProgram( + GLuint vertexShaderID, + GLuint fragmentShaderID + ) +{ + GLint result = GL_FALSE; + int infoLogLength; // Link the program - printf("Linking program\n"); GLuint programID = glCreateProgram(); + printf("Linking program\n"); glAttachShader(programID, vertexShaderID); glAttachShader(programID, fragmentShaderID); glLinkProgram(programID); @@ -105,3 +89,15 @@ LoadShaders( return programID; } + +GLuint +LoadShaders( + const char* vertFilePath, + const char* fragFilePath + ) +{ + GLuint vertexShaderID = CreateShader(vertFilePath, GL_VERTEX_SHADER); + GLuint fragmentShaderID = CreateShader(fragFilePath, GL_FRAGMENT_SHADER); + + return LinkProgram(vertexShaderID, fragmentShaderID); +} diff --git a/src/loadShaders.h b/src/loadShaders.h index dd5309c0..a364b504 100644 --- a/src/loadShaders.h +++ b/src/loadShaders.h @@ -3,6 +3,8 @@ #include +GLuint CreateShader(const char* filepath, const GLenum shaderType); +GLuint LinkProgram(GLuint vertexShaderID, GLuint fragmentShaderID); GLuint LoadShaders(const char* vertex_file_path, const char* fragment_file_path); #endif /* loadShader_h */ diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 29c9c95e..2ce43b52 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -80,13 +80,14 @@ Scene::InitFromJson( m_fluidSolver->ParticleVelocities(), m_fluidSolver->ParticleSpawnTimes() ); + m_fluidGeo->SetColor(glm::vec4(0.0f, 0.0f, 1.0f, 1.0f)); m_fluidGeo->Create(); } void Scene::Update( const KeyboardControl* kc, - ParticleEmitProgram& progEmit + ParticleAdvectProgram& progAdvect ) { UpdateCamera(kc); @@ -97,7 +98,7 @@ Scene::Update( return; #endif - UpdateFluidSolver(progEmit); + UpdateFluidSolver(progAdvect); } void @@ -114,10 +115,10 @@ Scene::Draw( void Scene::DrawTransformFeedback( const ShaderProgram& prog, - ParticleEmitProgram& progEmit + ParticleAdvectProgram& progAdvect ) { - DrawFluidSolver(prog, progEmit); + DrawFluidSolver(prog, progAdvect); } void @@ -187,24 +188,24 @@ Scene::UpdateCamera( void Scene::UpdateFluidSolver( - ParticleEmitProgram& progEmit + ParticleAdvectProgram& progAdvect ) { m_fluidSolver->Update(); // Advect particle? - progEmit.Emit(m_fluidGeo); + progAdvect.Advect(m_fluidGeo); } void Scene::DrawFluidSolver( const ShaderProgram& prog, - ParticleEmitProgram& progEmit + ParticleAdvectProgram& progAdvect ) { // -- Draw boundary prog.Draw(*m_camera, *m_fluidContainer); // -- Draw particles - progEmit.Draw(m_camera, m_fluidGeo); + progAdvect.Draw(m_camera, m_fluidGeo, m_fluidContainer); } diff --git a/src/scene/scene.h b/src/scene/scene.h index b6c056e8..f5323cbd 100644 --- a/src/scene/scene.h +++ b/src/scene/scene.h @@ -2,7 +2,7 @@ #define SCENE_H #include -#include +#include #include #include #include @@ -22,12 +22,12 @@ class Scene virtual void Update( const KeyboardControl*, - ParticleEmitProgram& prog + ParticleAdvectProgram& prog ); virtual void Draw(const ShaderProgram&) const; virtual void DrawTransformFeedback( const ShaderProgram& prog, - ParticleEmitProgram& progEmit + ParticleAdvectProgram& progAdvect ); virtual void CleanUp(); @@ -41,13 +41,13 @@ class Scene // -- Update helpers void UpdateCamera(const KeyboardControl*); void UpdateFluidSolver( - ParticleEmitProgram& prog + ParticleAdvectProgram& prog ); // -- Draw helpers void DrawFluidSolver( const ShaderProgram& prog, - ParticleEmitProgram& progEmit + ParticleAdvectProgram& progAdvect ); }; diff --git a/src/scene/scene.json b/src/scene/scene.json index b2c62a66..33b9ac80 100644 --- a/src/scene/scene.json +++ b/src/scene/scene.json @@ -1,13 +1,13 @@ { "containerDim" : { - "scaleX" : 5.0, - "scaleY" : 5.0, - "scaleZ" : 5.0 + "scaleX" : 10.0, + "scaleY" : 10.0, + "scaleZ" : 10.0 }, "particleDim" : { - "boundX" : 3.7, - "boundY" : 3.7, - "boundZ" : 3.7 + "boundX" : 8.7, + "boundY" : 8.7, + "boundZ" : 8.7 }, "particleSeparation" : 0.1 } diff --git a/src/shaderProgram/particleAdvectProgram.cpp b/src/shaderProgram/particleAdvectProgram.cpp new file mode 100644 index 00000000..0e0d77ab --- /dev/null +++ b/src/shaderProgram/particleAdvectProgram.cpp @@ -0,0 +1,237 @@ +#include +#include +#include +#include + +ParticleAdvectProgram::ParticleAdvectProgram( + const char* vertAdvectFilepath, + const char* fragAdvectFilepath, + const char* vertDrawFilepath, + const char* fragDrawFilepath + ) +{ + // -- Load advect program + GLuint vertShaderID = CreateShader(vertAdvectFilepath, GL_VERTEX_SHADER); + GLuint fragShaderID = CreateShader(fragAdvectFilepath, GL_FRAGMENT_SHADER); + GLuint m_programAdvect = glCreateProgram(); + glAttachShader(m_programAdvect, vertShaderID); + glAttachShader(m_programAdvect, fragShaderID); + + const GLchar* varyings[] = { + "v_position", "v_velocity", "v_spawntime" + }; + + glTransformFeedbackVaryings( + m_programAdvect, + sizeof(varyings), + varyings, + GL_SEPARATE_ATTRIBS + ); + + glDetachShader(m_programAdvect, vertShaderID); + glDetachShader(m_programAdvect, fragShaderID); + + glDeleteShader(vertShaderID); + glDeleteShader(fragShaderID); + + // -- Advect program uniforms + m_unifAdvectTime = glGetUniformLocation(m_programAdvect, "u_time"); + m_unifAdvectAccel = glGetUniformLocation(m_programAdvect, "u_accel"); + + // -- Load draw program + m_programDraw = LoadShaders(vertDrawFilepath, fragDrawFilepath); + + // -- Draw program uniforms + m_unifDrawModel = glGetUniformLocation(m_programDraw, "u_model"); + m_unifDrawViewProj = glGetUniformLocation(m_programDraw, "u_viewProj"); + m_unifDrawTime = glGetUniformLocation(m_programDraw, "u_time"); + m_unifDrawColor = glGetUniformLocation(m_programDraw, "u_color"); + m_unifDrawMinBoundary = glGetUniformLocation(m_programDraw, "u_minBoundary"); + m_unifDrawMaxBoundary = glGetUniformLocation(m_programDraw, "u_maxBoundary"); + + // -- Generate two transform feedback buffers for ping-pong'ing + glGenTransformFeedbacks(2, &m_TFBuffers[0]); +} + +void +ParticleAdvectProgram::Advect( + FluidGeo* fluidGeo + ) +{ + glUseProgram(m_programAdvect); + + // -- Setup uniforms + + // @todo: maybe we only need to pass delta time through + static float lastTime = glfwGetTime(); + float currentTime = glfwGetTime(); + float deltaTime = float(currentTime - lastTime); + + if (m_unifAdvectTime != -1) { + glUniform1f( + m_unifAdvectTime, deltaTime + ); + } + + if (m_unifAdvectAccel != -1) { + glUniform3f( + m_unifAdvectAccel, 0.0f, -10.1f, 0.0f + ); + } + + // -- Bind transform feedback buffer + if (m_isFirstDraw) { + glBindTransformFeedback( + GL_TRANSFORM_FEEDBACK, + m_TFBuffers[(m_curTFBuffer + 1) & 0x1] + ); + + fluidGeo->ToggleVao(); + fluidGeo->UpdateVao(); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, fluidGeo->PosBuffer()); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, fluidGeo->VelBuffer()); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 2, fluidGeo->SpawnTimeBuffer()); + + fluidGeo->ToggleVao(); + + glBindTransformFeedback( + GL_TRANSFORM_FEEDBACK, + m_TFBuffers[m_curTFBuffer] + ); + fluidGeo->UpdateVao(); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, fluidGeo->PosBuffer()); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, fluidGeo->VelBuffer()); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 2, fluidGeo->SpawnTimeBuffer()); + } else { + glBindTransformFeedback( + GL_TRANSFORM_FEEDBACK, + m_TFBuffers[m_curTFBuffer] + ); + } + + // -- Enable attributes + fluidGeo->EnableVertexAttributes(); + + // Toggle the vao we're using so that we can store the output of + // transform feedback into the secondary vao + fluidGeo->ToggleVao(); + + // Set transform feedback buffer + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, fluidGeo->PosBuffer()); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, fluidGeo->VelBuffer()); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 2, fluidGeo->SpawnTimeBuffer()); + + // Turn off rasterization - we are not drawing + glEnable(GL_RASTERIZER_DISCARD); + + // Render + glBeginTransformFeedback(fluidGeo->GLDrawMode()); + glDrawArrays(fluidGeo->GLDrawMode(), 0, fluidGeo->ElementCount()); + glEndTransformFeedback(); + + // Turn on rasterization - we are done + glDisable(GL_RASTERIZER_DISCARD); + glUseProgram(NULL); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, NULL); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, NULL); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 2, NULL); + fluidGeo->DisableVertexAttributes(); + + fluidGeo->ToggleVao(); +} + +void +ParticleAdvectProgram::Draw( + const Camera* camera, + const FluidGeo* fluidGeo, + const Box* container + ) +{ + glUseProgram(m_programDraw); + + // -- Set uniforms + if (m_unifDrawModel != -1) { + glUniformMatrix4fv( + m_unifDrawModel, + 1, + GL_FALSE, + &(fluidGeo->GetLocalTransformation()[0][0]) + ); + } + + if (m_unifDrawViewProj != -1) { + glUniformMatrix4fv( + m_unifDrawViewProj, + 1, + GL_FALSE, + &(camera->GetViewProj()[0][0]) + ); + } + + // @todo: maybe we only need to pass delta time through + static float lastTime = glfwGetTime(); + float currentTime = glfwGetTime(); + float deltaTime = float(currentTime - lastTime); + + if (m_unifDrawTime != -1) { + glUniform1f( + m_unifDrawTime, currentTime + ); + } + + if (m_unifDrawColor != -1) { + glUniform4fv( + m_unifDrawColor, + 1, + &(fluidGeo->GetColor()[0]) + ); + } + + if (m_unifDrawMinBoundary != -1) { + glm::vec3 minBoundary = container->GetMinBoundary(); + glUniform3f( + m_unifDrawMinBoundary, + minBoundary.x, + minBoundary.y, + minBoundary.z + ); + } + + if (m_unifDrawMaxBoundary != -1) { + glm::vec3 maxBoundary = container->GetMaxBoundary(); + glUniform3f( + m_unifDrawMaxBoundary, + maxBoundary.x, + maxBoundary.y, + maxBoundary.z + ); + } + // Enable attributes + fluidGeo->EnableVertexAttributes(); + + // Render + if (m_isFirstDraw) + { + glDrawArrays(fluidGeo->GLDrawMode(), 0, fluidGeo->ElementCount()); + // m_isFirstDraw = false; + } + else + { + glDrawTransformFeedback( + fluidGeo->GLDrawMode(), + m_TFBuffers[m_curTFBuffer]); + } + fluidGeo->DisableVertexAttributes(); + + // -- Toggle transform feedback buffer + m_curTFBuffer = (m_curTFBuffer + 1) & 0x1; +} + +void +ParticleAdvectProgram::CleanUp() +{ + glDeleteProgram(m_programAdvect); + glDeleteProgram(m_programDraw); +} + + diff --git a/src/shaderProgram/particleEmitProgram.h b/src/shaderProgram/particleAdvectProgram.h similarity index 54% rename from src/shaderProgram/particleEmitProgram.h rename to src/shaderProgram/particleAdvectProgram.h index d38f2a81..546a02c6 100644 --- a/src/shaderProgram/particleEmitProgram.h +++ b/src/shaderProgram/particleAdvectProgram.h @@ -1,52 +1,59 @@ -#ifndef PARTICLE_EMIT_PROGRAM_H -#define PARTICLE_EMIT_PROGRAM_H +#ifndef PARTICLE_ADVECT_PROGRAM_H +#define PARTICLE_ADVECT_PROGRAM_H #include #include #include +#include class FluidGeo; -class ParticleEmitProgram +class ParticleAdvectProgram { public: - ParticleEmitProgram( - const char* vertEmitFilepath, - const char* fragEmitFilepath, + ParticleAdvectProgram( + const char* vertAdvectFilepath, + const char* fragAdvectFilepath, const char* vertDrawFilepath, const char* fragDrawFilepath ); // First pass - virtual void Emit( + virtual void Advect( FluidGeo* ); // Second pass virtual void Draw( const Camera*, - const FluidGeo* + const FluidGeo*, + const Box* ); virtual void CleanUp(); protected: - GLuint m_programEmit; + GLuint m_programAdvect; GLuint m_programDraw; // Transform feedback buffer GLuint m_TFBuffers[2]; char m_curTFBuffer = 0; - // Uniform locations for emit program - int m_unifEmitTime; - int m_unifEmitAccel; + // Uniform locations for advect program + int m_unifAdvectTime; + int m_unifAdvectAccel; // Uniform locations for draw program int m_unifDrawTime; int m_unifDrawColor; int m_unifDrawModel; int m_unifDrawViewProj; + int m_unifDrawMinBoundary; + int m_unifDrawMaxBoundary; + + // First draw + bool m_isFirstDraw = true; }; #endif diff --git a/src/shaderProgram/particleEmitProgram.cpp b/src/shaderProgram/particleEmitProgram.cpp deleted file mode 100644 index 598ed3f9..00000000 --- a/src/shaderProgram/particleEmitProgram.cpp +++ /dev/null @@ -1,176 +0,0 @@ -#include -#include -#include -#include - -ParticleEmitProgram::ParticleEmitProgram( - const char* vertEmitFilepath, - const char* fragEmitFilepath, - const char* vertDrawFilepath, - const char* fragDrawFilepath - ) -{ - // -- Load emit program - m_programEmit = LoadShaders(vertEmitFilepath, fragEmitFilepath); - - const GLchar* varyings[] = { - "a_position", "a_velocity", "a_spawntime" - }; - - glTransformFeedbackVaryings( - m_programEmit, - sizeof(varyings), - varyings, - GL_SEPARATE_ATTRIBS - ); - - glLinkProgram(m_programEmit); - - // -- Emit program uniforms - m_unifEmitTime = glGetUniformLocation(m_programEmit, "u_time"); - m_unifEmitAccel = glGetUniformLocation(m_programEmit, "u_accel"); - - // -- Load draw program - m_programDraw = LoadShaders(vertDrawFilepath, fragDrawFilepath); - - // -- Draw program uniforms - m_unifDrawModel = glGetUniformLocation(m_programDraw, "u_model"); - m_unifDrawViewProj = glGetUniformLocation(m_programDraw, "u_viewProj"); - m_unifDrawTime = glGetUniformLocation(m_programDraw, "u_time"); - m_unifDrawColor = glGetUniformLocation(m_programDraw, "u_color"); - - // -- Generate two transform feedback buffers for ping-pong'ing - glGenTransformFeedbacks(2, &m_TFBuffers[0]); -} - -void -ParticleEmitProgram::Emit( - FluidGeo* geo - ) -{ - glUseProgram(m_programEmit); - - // -- Setup uniforms - // @todo: maybe we only need to pass delta time through - static float lastTime = glfwGetTime(); - float currentTime = glfwGetTime(); - float deltaTime = float(currentTime - lastTime); - - if (m_unifEmitTime != -1) { - glUniform1f( - m_unifEmitTime, currentTime - ); - } - - if (m_unifEmitAccel != -1) { - glUniform3f( - m_unifEmitAccel, 0.0f, -10.1f, 0.0f - ); - } - - // -- Bind transform feedback buffer - glBindTransformFeedback( - GL_TRANSFORM_FEEDBACK, - m_TFBuffers[m_curTFBuffer] - ); - - // -- Enable attributes - geo->EnableVertexAttributes(); - - // Toggle the vao we're using so that we can store the output of - // transform feedback into the secondary vao - geo->ToggleVao(); - - // Set transform feedback buffer - glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, geo->PosBuffer()); - glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, geo->VelBuffer()); - glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 2, geo->SpawnTimeBuffer()); - - // Turn off rasterization - we are not drawing - glEnable(GL_RASTERIZER_DISCARD); - - // Render - glBeginTransformFeedback(geo->GLDrawMode()); - glDrawArrays(geo->GLDrawMode(), 0, geo->ElementCount()); - glEndTransformFeedback(); - - // Turn on rasterization - we are done - glDisable(GL_RASTERIZER_DISCARD); - glUseProgram(NULL); - glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, NULL); - glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, NULL); - glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 2, NULL); - glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, NULL); - geo->DisableVertexAttributes(); -} - -void -ParticleEmitProgram::Draw( - const Camera* camera, - const FluidGeo* geo - ) -{ - glUseProgram(m_programDraw); - - // Enable attributes - geo->EnableVertexAttributes(); - - // -- Set uniforms - if (m_unifDrawModel != -1) { - glUniformMatrix4fv( - m_unifDrawModel, - 1, - GL_FALSE, - &(geo->GetLocalTransformation()[0][0]) - ); - } - - if (m_unifDrawViewProj != -1) { - glUniformMatrix4fv( - m_unifDrawViewProj, - 1, - GL_FALSE, - &(camera->GetViewProj()[0][0]) - ); - } - - // @todo: maybe we only need to pass delta time through - static float lastTime = glfwGetTime(); - float currentTime = glfwGetTime(); - float deltaTime = float(currentTime - lastTime); - - if (m_unifDrawTime != -1) { - glUniform1f( - m_unifDrawTime, currentTime - ); - } - - if (m_unifDrawColor != -1) { - glUniform4fv( - m_unifDrawColor, - 1, - &(geo->GetColor()[0]) - ); - } - - // Render - glDrawElements( - geo->GLDrawMode(), - geo->ElementCount(), - GL_UNSIGNED_SHORT, NULL - ); - // glDrawTransformFeedback(geo->GLDrawMode(), m_TFBuffers[m_curTFBuffer]); - geo->DisableVertexAttributes(); - - // -- Toggle transform feedback buffer - m_curTFBuffer = (m_curTFBuffer + 1) % 2; -} - -void -ParticleEmitProgram::CleanUp() -{ - glDeleteProgram(m_programEmit); - glDeleteProgram(m_programDraw); -} - - diff --git a/src/viewer.cpp b/src/viewer.cpp index 8b6917dc..222e9a31 100644 --- a/src/viewer.cpp +++ b/src/viewer.cpp @@ -9,7 +9,7 @@ Viewer::Viewer( int height ) : m_window(nullptr), m_program(nullptr), - m_programEmit(nullptr), + m_programAdvect(nullptr), m_scene(nullptr) { m_width = width; @@ -66,9 +66,9 @@ Viewer::Init() // -- Initialize shader program m_program = new ShaderProgram("../src/glsl/simple.vert", "../src/glsl/simple.frag"); - m_programEmit = new ParticleEmitProgram( - "../src/glsl/particle_emit.vert", - "../src/glsl/particle_emit.frag", + m_programAdvect = new ParticleAdvectProgram( + "../src/glsl/particle_advect.vert", + "../src/glsl/particle_advect.frag", "../src/glsl/particle_draw.vert", "../src/glsl/particle_draw.frag" ); @@ -77,6 +77,7 @@ Viewer::Init() m_scene = new Scene(m_width, m_height); // Have a test scene here in case our graphics pipeline is broken +// #define TEST_SCENE #ifdef TEST_SCENE m_scene->InitFromTestScene(); #else @@ -94,12 +95,12 @@ Viewer::Update() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - m_scene->Update(m_keyboard, *m_programEmit); + m_scene->Update(m_keyboard, *m_programAdvect); #ifdef TEST_SCENE m_scene->Draw(*m_program); #else - m_scene->DrawTransformFeedback(*m_program, *m_programEmit); + m_scene->DrawTransformFeedback(*m_program, *m_programAdvect); #endif // Swap buffers @@ -116,12 +117,12 @@ void Viewer::CleanUp() { m_program->CleanUp(); - m_programEmit->CleanUp(); + m_programAdvect->CleanUp(); m_scene->CleanUp(); // *N.B*: Should delete resources in reverse order of creation delete m_scene; - delete m_programEmit; + delete m_programAdvect; delete m_program; delete m_keyboard; diff --git a/src/viewer.h b/src/viewer.h index f26d9b12..ca6107c1 100644 --- a/src/viewer.h +++ b/src/viewer.h @@ -14,7 +14,7 @@ #include #include -#include +#include #include #include #include @@ -48,7 +48,7 @@ class Viewer GLFWwindow* m_window; KeyboardControl* m_keyboard; ShaderProgram* m_program; - ParticleEmitProgram* m_programEmit; + ParticleAdvectProgram* m_programAdvect; Scene* m_scene; }; From 6e2d08221a2b236b198d04ead6481d7569d09f19 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Tue, 1 Mar 2016 00:19:07 -0500 Subject: [PATCH 25/64] updated link to jsoncpp --- CMakeLists.txt | 1 - nuparu/include/GLFW/COPYING.txt | 22 + nuparu/include/GLFW/glfw3.h | 1684 +++++++++-------------------- nuparu/include/GLFW/glfw3native.h | 242 ++--- src/main.cpp | 3 +- src/main.hpp | 9 - 6 files changed, 606 insertions(+), 1355 deletions(-) create mode 100644 nuparu/include/GLFW/COPYING.txt delete mode 100644 src/main.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d9437e1a..c2d4d9a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -90,7 +90,6 @@ endif() # Add source files you want to compile (.cpp) set(CORE_SRC - src/thirdparty/jsoncpp/dist/jsoncpp.cpp src/loadShaders.cpp src/utility.cpp src/main.cpp diff --git a/nuparu/include/GLFW/COPYING.txt b/nuparu/include/GLFW/COPYING.txt new file mode 100644 index 00000000..b30c7015 --- /dev/null +++ b/nuparu/include/GLFW/COPYING.txt @@ -0,0 +1,22 @@ +Copyright (c) 2002-2006 Marcus Geelnard +Copyright (c) 2006-2010 Camilla Berglund + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any damages +arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it +freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would + be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not + be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source + distribution. + diff --git a/nuparu/include/GLFW/glfw3.h b/nuparu/include/GLFW/glfw3.h index 68d005dd..009fa755 100644 --- a/nuparu/include/GLFW/glfw3.h +++ b/nuparu/include/GLFW/glfw3.h @@ -1,5 +1,5 @@ /************************************************************************* - * GLFW 3.2 - www.glfw.org + * GLFW 3.1 - www.glfw.org * A library for OpenGL, window and input *------------------------------------------------------------------------ * Copyright (c) 2002-2006 Marcus Geelnard @@ -38,45 +38,32 @@ extern "C" { * Doxygen documentation *************************************************************************/ -/*! @file glfw3.h - * @brief The header of the GLFW 3 API. +/*! @defgroup context Context handling * - * This is the header file of the GLFW 3 API. It defines all its types and - * declares all its functions. - * - * For more information about how to use this file, see @ref build_include. - */ -/*! @defgroup context Context reference - * - * This is the reference documentation for OpenGL and OpenGL ES context related - * functions. For more task-oriented information, see the @ref context_guide. - */ -/*! @defgroup vulkan Vulkan reference - * - * This is the reference documentation for Vulkan related functions and types. - * For more task-oriented information, see the @ref vulkan_guide. + * This is the reference documentation for context related functions. For more + * information, see the @ref context. */ -/*! @defgroup init Initialization, version and error reference +/*! @defgroup init Initialization, version and errors * * This is the reference documentation for initialization and termination of - * the library, version management and error handling. For more task-oriented - * information, see the @ref intro_guide. + * the library, version management and error handling. For more information, + * see the @ref intro. */ -/*! @defgroup input Input reference +/*! @defgroup input Input handling * * This is the reference documentation for input related functions and types. - * For more task-oriented information, see the @ref input_guide. + * For more information, see the @ref input. */ -/*! @defgroup monitor Monitor reference +/*! @defgroup monitor Monitor handling * * This is the reference documentation for monitor related functions and types. - * For more task-oriented information, see the @ref monitor_guide. + * For more information, see the @ref monitor. */ -/*! @defgroup window Window reference +/*! @defgroup window Window handling * * This is the reference documentation for window related functions and types, - * including creation, deletion and event polling. For more task-oriented - * information, see the @ref window_guide. + * including creation, deletion and event polling. For more information, see + * the @ref window. */ @@ -124,7 +111,7 @@ extern "C" { /* Include the chosen client API headers. */ -#if defined(__APPLE__) +#if defined(__APPLE_CC__) #if defined(GLFW_INCLUDE_GLCOREARB) #include #if defined(GLFW_INCLUDE_GLEXT) @@ -155,15 +142,13 @@ extern "C" { #elif defined(GLFW_INCLUDE_ES3) #include #if defined(GLFW_INCLUDE_GLEXT) - #include + #include #endif #elif defined(GLFW_INCLUDE_ES31) #include #if defined(GLFW_INCLUDE_GLEXT) - #include + #include #endif - #elif defined(GLFW_INCLUDE_VULKAN) - #include #elif !defined(GLFW_INCLUDE_NONE) #include #if defined(GLFW_INCLUDE_GLEXT) @@ -180,7 +165,7 @@ extern "C" { * version of the GLFW library. _GLFW_BUILD_DLL is defined by the GLFW * configuration header when compiling the DLL version of the library. */ - #error "You must not have both GLFW_DLL and _GLFW_BUILD_DLL defined" + #error "You may not have both GLFW_DLL and _GLFW_BUILD_DLL defined" #endif /* GLFWAPI is used to declare public API functions for export @@ -219,32 +204,14 @@ extern "C" { * backward-compatible. * @ingroup init */ -#define GLFW_VERSION_MINOR 2 +#define GLFW_VERSION_MINOR 1 /*! @brief The revision number of the GLFW library. * * This is incremented when a bug fix release is made that does not contain any * API changes. * @ingroup init */ -#define GLFW_VERSION_REVISION 0 -/*! @} */ - -/*! @name Boolean values - * @{ */ -/*! @brief One. - * - * One. Seriously. You don't _need_ to use this symbol in your code. It's - * just semantic sugar for the number 1. You can use `1` or `true` or `_True` - * or `GL_TRUE` or whatever you want. - */ -#define GLFW_TRUE 1 -/*! @brief Zero. - * - * Zero. Seriously. You don't _need_ to use this symbol in your code. It's - * just just semantic sugar for the number 0. You can use `0` or `false` or - * `_False` or `GL_FALSE` or whatever you want. - */ -#define GLFW_FALSE 0 +#define GLFW_VERSION_REVISION 1 /*! @} */ /*! @name Key and button actions @@ -421,7 +388,6 @@ extern "C" { #define GLFW_KEY_RIGHT_ALT 346 #define GLFW_KEY_RIGHT_SUPER 347 #define GLFW_KEY_MENU 348 - #define GLFW_KEY_LAST GLFW_KEY_MENU /*! @} */ @@ -501,11 +467,12 @@ extern "C" { * @{ */ /*! @brief GLFW has not been initialized. * - * This occurs if a GLFW function was called that must not be called unless the + * This occurs if a GLFW function was called that may not be called unless the * library is [initialized](@ref intro_init). * - * @analysis Application programmer error. Initialize GLFW before calling any - * function that requires initialization. + * @par Analysis + * Application programmer error. Initialize GLFW before calling any function + * that requires initialization. */ #define GLFW_NOT_INITIALIZED 0x00010001 /*! @brief No context is current for this thread. @@ -514,8 +481,9 @@ extern "C" { * current OpenGL or OpenGL ES context but no context is current on the calling * thread. One such function is @ref glfwSwapInterval. * - * @analysis Application programmer error. Ensure a context is current before - * calling functions that require a current context. + * @par Analysis + * Application programmer error. Ensure a context is current before calling + * functions that require a current context. */ #define GLFW_NO_CURRENT_CONTEXT 0x00010002 /*! @brief One of the arguments to the function was an invalid enum value. @@ -524,7 +492,8 @@ extern "C" { * requesting [GLFW_RED_BITS](@ref window_hints_fb) with @ref * glfwGetWindowAttrib. * - * @analysis Application programmer error. Fix the offending call. + * @par Analysis + * Application programmer error. Fix the offending call. */ #define GLFW_INVALID_ENUM 0x00010003 /*! @brief One of the arguments to the function was an invalid value. @@ -535,31 +504,37 @@ extern "C" { * Requesting a valid but unavailable OpenGL or OpenGL ES version will instead * result in a @ref GLFW_VERSION_UNAVAILABLE error. * - * @analysis Application programmer error. Fix the offending call. + * @par Analysis + * Application programmer error. Fix the offending call. */ #define GLFW_INVALID_VALUE 0x00010004 /*! @brief A memory allocation failed. * * A memory allocation failed. * - * @analysis A bug in GLFW or the underlying operating system. Report the bug - * to our [issue tracker](https://github.com/glfw/glfw/issues). + * @par Analysis + * A bug in GLFW or the underlying operating system. Report the bug to our + * [issue tracker](https://github.com/glfw/glfw/issues). */ #define GLFW_OUT_OF_MEMORY 0x00010005 -/*! @brief GLFW could not find support for the requested API on the system. +/*! @brief GLFW could not find support for the requested client API on the + * system. * - * GLFW could not find support for the requested API on the system. + * GLFW could not find support for the requested client API on the system. If + * emitted by functions other than @ref glfwCreateWindow, no supported client + * API was found. * - * @analysis The installed graphics driver does not support the requested - * API, or does not support it via the chosen context creation backend. - * Below are a few examples. + * @par Analysis + * The installed graphics driver does not support the requested client API, or + * does not support it via the chosen context creation backend. Below are + * a few examples. * * @par * Some pre-installed Windows graphics drivers do not support OpenGL. AMD only - * supports OpenGL ES via EGL, while Nvidia and Intel only support it via + * supports OpenGL ES via EGL, while Nvidia and Intel only supports it via * a WGL or GLX extension. OS X does not provide OpenGL ES at all. The Mesa * EGL, OpenGL and OpenGL ES libraries do not interface with the Nvidia binary - * driver. Older graphics drivers do not support Vulkan. + * driver. */ #define GLFW_API_UNAVAILABLE 0x00010006 /*! @brief The requested OpenGL or OpenGL ES version is not available. @@ -567,9 +542,10 @@ extern "C" { * The requested OpenGL or OpenGL ES version (including any requested context * or framebuffer hints) is not available on this machine. * - * @analysis The machine does not support your requirements. If your - * application is sufficiently flexible, downgrade your requirements and try - * again. Otherwise, inform the user that their machine does not match your + * @par Analysis + * The machine does not support your requirements. If your application is + * sufficiently flexible, downgrade your requirements and try again. + * Otherwise, inform the user that their machine does not match your * requirements. * * @par @@ -585,9 +561,10 @@ extern "C" { * A platform-specific error occurred that does not match any of the more * specific categories. * - * @analysis A bug or configuration error in GLFW, the underlying operating - * system or its drivers, or a lack of required resources. Report the issue to - * our [issue tracker](https://github.com/glfw/glfw/issues). + * @par Analysis + * A bug or configuration error in GLFW, the underlying operating system or + * its drivers, or a lack of required resources. Report the issue to our + * [issue tracker](https://github.com/glfw/glfw/issues). */ #define GLFW_PLATFORM_ERROR 0x00010008 /*! @brief The requested format is not supported or available. @@ -598,7 +575,8 @@ extern "C" { * If emitted when querying the clipboard, the contents of the clipboard could * not be converted to the requested format. * - * @analysis If emitted during window creation, one or more + * @par Analysis + * If emitted during window creation, one or more * [hard constraints](@ref window_hints_hard) did not match any of the * available pixel formats. If your application is sufficiently flexible, * downgrade your requirements and try again. Otherwise, inform the user that @@ -609,14 +587,6 @@ extern "C" { * the user, as appropriate. */ #define GLFW_FORMAT_UNAVAILABLE 0x00010009 -/*! @brief The specified window does not have an OpenGL or OpenGL ES context. - * - * A window that does not have an OpenGL or OpenGL ES context was passed to - * a function that requires it to have one. - * - * @analysis Application programmer error. Fix the offending call. - */ -#define GLFW_NO_WINDOW_CONTEXT 0x0001000A /*! @} */ #define GLFW_FOCUSED 0x00020001 @@ -626,7 +596,6 @@ extern "C" { #define GLFW_DECORATED 0x00020005 #define GLFW_AUTO_ICONIFY 0x00020006 #define GLFW_FLOATING 0x00020007 -#define GLFW_MAXIMIZED 0x00020008 #define GLFW_RED_BITS 0x00021001 #define GLFW_GREEN_BITS 0x00021002 @@ -654,9 +623,7 @@ extern "C" { #define GLFW_OPENGL_DEBUG_CONTEXT 0x00022007 #define GLFW_OPENGL_PROFILE 0x00022008 #define GLFW_CONTEXT_RELEASE_BEHAVIOR 0x00022009 -#define GLFW_CONTEXT_NO_ERROR 0x0002200A -#define GLFW_NO_API 0 #define GLFW_OPENGL_API 0x00030001 #define GLFW_OPENGL_ES_API 0x00030002 @@ -734,37 +701,14 @@ extern "C" { * Generic function pointer used for returning client API function pointers * without forcing a cast from a regular pointer. * - * @sa @ref context_glext - * @sa glfwGetProcAddress - * - * @since Added in version 3.0. - * @ingroup context */ typedef void (*GLFWglproc)(void); -/*! @brief Vulkan API function pointer type. - * - * Generic function pointer used for returning Vulkan API function pointers - * without forcing a cast from a regular pointer. - * - * @sa @ref vulkan_proc - * @sa glfwGetInstanceProcAddress - * - * @since Added in version 3.2. - * - * @ingroup vulkan - */ -typedef void (*GLFWvkproc)(void); - /*! @brief Opaque monitor object. * * Opaque monitor object. * - * @see @ref monitor_object - * - * @since Added in version 3.0. - * * @ingroup monitor */ typedef struct GLFWmonitor GLFWmonitor; @@ -773,10 +717,6 @@ typedef struct GLFWmonitor GLFWmonitor; * * Opaque window object. * - * @see @ref window_object - * - * @since Added in version 3.0. - * * @ingroup window */ typedef struct GLFWwindow GLFWwindow; @@ -785,10 +725,6 @@ typedef struct GLFWwindow GLFWwindow; * * Opaque cursor object. * - * @see @ref cursor_object - * - * @since Added in version 3.1. - * * @ingroup cursor */ typedef struct GLFWcursor GLFWcursor; @@ -800,11 +736,8 @@ typedef struct GLFWcursor GLFWcursor; * @param[in] error An [error code](@ref errors). * @param[in] description A UTF-8 encoded string describing the error. * - * @sa @ref error_handling * @sa glfwSetErrorCallback * - * @since Added in version 3.0. - * * @ingroup init */ typedef void (* GLFWerrorfun)(int,const char*); @@ -819,11 +752,8 @@ typedef void (* GLFWerrorfun)(int,const char*); * @param[in] ypos The new y-coordinate, in screen coordinates, of the * upper-left corner of the client area of the window. * - * @sa @ref window_pos * @sa glfwSetWindowPosCallback * - * @since Added in version 3.0. - * * @ingroup window */ typedef void (* GLFWwindowposfun)(GLFWwindow*,int,int); @@ -836,12 +766,8 @@ typedef void (* GLFWwindowposfun)(GLFWwindow*,int,int); * @param[in] width The new width, in screen coordinates, of the window. * @param[in] height The new height, in screen coordinates, of the window. * - * @sa @ref window_size * @sa glfwSetWindowSizeCallback * - * @since Added in version 1.0. - * @glfw3 Added window handle parameter. - * * @ingroup window */ typedef void (* GLFWwindowsizefun)(GLFWwindow*,int,int); @@ -852,12 +778,8 @@ typedef void (* GLFWwindowsizefun)(GLFWwindow*,int,int); * * @param[in] window The window that the user attempted to close. * - * @sa @ref window_close * @sa glfwSetWindowCloseCallback * - * @since Added in version 2.5. - * @glfw3 Added window handle parameter. - * * @ingroup window */ typedef void (* GLFWwindowclosefun)(GLFWwindow*); @@ -868,12 +790,8 @@ typedef void (* GLFWwindowclosefun)(GLFWwindow*); * * @param[in] window The window whose content needs to be refreshed. * - * @sa @ref window_refresh * @sa glfwSetWindowRefreshCallback * - * @since Added in version 2.5. - * @glfw3 Added window handle parameter. - * * @ingroup window */ typedef void (* GLFWwindowrefreshfun)(GLFWwindow*); @@ -883,14 +801,11 @@ typedef void (* GLFWwindowrefreshfun)(GLFWwindow*); * This is the function signature for window focus callback functions. * * @param[in] window The window that gained or lost input focus. - * @param[in] focused `GLFW_TRUE` if the window was given input focus, or - * `GLFW_FALSE` if it lost it. + * @param[in] focused `GL_TRUE` if the window was given input focus, or + * `GL_FALSE` if it lost it. * - * @sa @ref window_focus * @sa glfwSetWindowFocusCallback * - * @since Added in version 3.0. - * * @ingroup window */ typedef void (* GLFWwindowfocusfun)(GLFWwindow*,int); @@ -901,14 +816,11 @@ typedef void (* GLFWwindowfocusfun)(GLFWwindow*,int); * functions. * * @param[in] window The window that was iconified or restored. - * @param[in] iconified `GLFW_TRUE` if the window was iconified, or - * `GLFW_FALSE` if it was restored. + * @param[in] iconified `GL_TRUE` if the window was iconified, or `GL_FALSE` + * if it was restored. * - * @sa @ref window_iconify * @sa glfwSetWindowIconifyCallback * - * @since Added in version 3.0. - * * @ingroup window */ typedef void (* GLFWwindowiconifyfun)(GLFWwindow*,int); @@ -922,11 +834,8 @@ typedef void (* GLFWwindowiconifyfun)(GLFWwindow*,int); * @param[in] width The new width, in pixels, of the framebuffer. * @param[in] height The new height, in pixels, of the framebuffer. * - * @sa @ref window_fbsize * @sa glfwSetFramebufferSizeCallback * - * @since Added in version 3.0. - * * @ingroup window */ typedef void (* GLFWframebuffersizefun)(GLFWwindow*,int,int); @@ -942,12 +851,8 @@ typedef void (* GLFWframebuffersizefun)(GLFWwindow*,int,int); * @param[in] mods Bit field describing which [modifier keys](@ref mods) were * held down. * - * @sa @ref input_mouse_button * @sa glfwSetMouseButtonCallback * - * @since Added in version 1.0. - * @glfw3 Added window handle and modifier mask parameters. - * * @ingroup input */ typedef void (* GLFWmousebuttonfun)(GLFWwindow*,int,int,int); @@ -957,16 +862,11 @@ typedef void (* GLFWmousebuttonfun)(GLFWwindow*,int,int,int); * This is the function signature for cursor position callback functions. * * @param[in] window The window that received the event. - * @param[in] xpos The new cursor x-coordinate, relative to the left edge of - * the client area. - * @param[in] ypos The new cursor y-coordinate, relative to the top edge of the - * client area. + * @param[in] xpos The new x-coordinate, in screen coordinates, of the cursor. + * @param[in] ypos The new y-coordinate, in screen coordinates, of the cursor. * - * @sa @ref cursor_pos * @sa glfwSetCursorPosCallback * - * @since Added in version 3.0. Replaces `GLFWmouseposfun`. - * * @ingroup input */ typedef void (* GLFWcursorposfun)(GLFWwindow*,double,double); @@ -976,14 +876,11 @@ typedef void (* GLFWcursorposfun)(GLFWwindow*,double,double); * This is the function signature for cursor enter/leave callback functions. * * @param[in] window The window that received the event. - * @param[in] entered `GLFW_TRUE` if the cursor entered the window's client - * area, or `GLFW_FALSE` if it left it. + * @param[in] entered `GL_TRUE` if the cursor entered the window's client + * area, or `GL_FALSE` if it left it. * - * @sa @ref cursor_enter * @sa glfwSetCursorEnterCallback * - * @since Added in version 3.0. - * * @ingroup input */ typedef void (* GLFWcursorenterfun)(GLFWwindow*,int); @@ -996,11 +893,8 @@ typedef void (* GLFWcursorenterfun)(GLFWwindow*,int); * @param[in] xoffset The scroll offset along the x-axis. * @param[in] yoffset The scroll offset along the y-axis. * - * @sa @ref scrolling * @sa glfwSetScrollCallback * - * @since Added in version 3.0. Replaces `GLFWmousewheelfun`. - * * @ingroup input */ typedef void (* GLFWscrollfun)(GLFWwindow*,double,double); @@ -1016,12 +910,8 @@ typedef void (* GLFWscrollfun)(GLFWwindow*,double,double); * @param[in] mods Bit field describing which [modifier keys](@ref mods) were * held down. * - * @sa @ref input_key * @sa glfwSetKeyCallback * - * @since Added in version 1.0. - * @glfw3 Added window handle, scancode and modifier mask parameters. - * * @ingroup input */ typedef void (* GLFWkeyfun)(GLFWwindow*,int,int,int,int); @@ -1033,12 +923,8 @@ typedef void (* GLFWkeyfun)(GLFWwindow*,int,int,int,int); * @param[in] window The window that received the event. * @param[in] codepoint The Unicode code point of the character. * - * @sa @ref input_char * @sa glfwSetCharCallback * - * @since Added in version 2.4. - * @glfw3 Added window handle parameter. - * * @ingroup input */ typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int); @@ -1055,11 +941,8 @@ typedef void (* GLFWcharfun)(GLFWwindow*,unsigned int); * @param[in] mods Bit field describing which [modifier keys](@ref mods) were * held down. * - * @sa @ref input_char * @sa glfwSetCharModsCallback * - * @since Added in version 3.1. - * * @ingroup input */ typedef void (* GLFWcharmodsfun)(GLFWwindow*,unsigned int,int); @@ -1072,11 +955,8 @@ typedef void (* GLFWcharmodsfun)(GLFWwindow*,unsigned int,int); * @param[in] count The number of dropped files. * @param[in] paths The UTF-8 encoded file and/or directory path names. * - * @sa @ref path_drop * @sa glfwSetDropCallback * - * @since Added in version 3.1. - * * @ingroup input */ typedef void (* GLFWdropfun)(GLFWwindow*,int,const char**); @@ -1088,11 +968,8 @@ typedef void (* GLFWdropfun)(GLFWwindow*,int,const char**); * @param[in] monitor The monitor that was connected or disconnected. * @param[in] event One of `GLFW_CONNECTED` or `GLFW_DISCONNECTED`. * - * @sa @ref monitor_event * @sa glfwSetMonitorCallback * - * @since Added in version 3.0. - * * @ingroup monitor */ typedef void (* GLFWmonitorfun)(GLFWmonitor*,int); @@ -1101,12 +978,6 @@ typedef void (* GLFWmonitorfun)(GLFWmonitor*,int); * * This describes a single video mode. * - * @sa @ref monitor_modes - * @sa glfwGetVideoMode glfwGetVideoModes - * - * @since Added in version 1.0. - * @glfw3 Added refresh rate member. - * * @ingroup monitor */ typedef struct GLFWvidmode @@ -1135,11 +1006,8 @@ typedef struct GLFWvidmode * * This describes the gamma ramp for a monitor. * - * @sa @ref monitor_gamma * @sa glfwGetGammaRamp glfwSetGammaRamp * - * @since Added in version 3.0. - * * @ingroup monitor */ typedef struct GLFWgammaramp @@ -1159,11 +1027,6 @@ typedef struct GLFWgammaramp } GLFWgammaramp; /*! @brief Image data. - * - * @sa @ref cursor_custom - * - * @since Added in version 2.1. - * @glfw3 Removed format and bytes-per-pixel members. */ typedef struct GLFWimage { @@ -1194,24 +1057,29 @@ typedef struct GLFWimage * succeeds, you should call @ref glfwTerminate before the application exits. * * Additional calls to this function after successful initialization but before - * termination will return `GLFW_TRUE` immediately. + * termination will return `GL_TRUE` immediately. * - * @return `GLFW_TRUE` if successful, or `GLFW_FALSE` if an + * @return `GL_TRUE` if successful, or `GL_FALSE` if an * [error](@ref error_handling) occurred. * - * @errors Possible errors include @ref GLFW_PLATFORM_ERROR. - * - * @remark @osx This function will change the current directory of the + * @remarks __OS X:__ This function will change the current directory of the * application to the `Contents/Resources` subdirectory of the application's * bundle, if present. This can be disabled with a * [compile-time option](@ref compile_options_osx). * - * @thread_safety This function must only be called from the main thread. + * @remarks __X11:__ If the `LC_CTYPE` category of the current locale is set to + * `"C"` then the environment's locale will be applied to that category. This + * is done because character input will not function when `LC_CTYPE` is set to + * `"C"`. If another locale was set before this function was called, it will + * be left untouched. + * + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref intro_init * @sa glfwTerminate * - * @since Added in version 1.0. + * @since Added in GLFW 1.0. * * @ingroup init */ @@ -1229,21 +1097,21 @@ GLFWAPI int glfwInit(void); * call this function, as it is called by @ref glfwInit before it returns * failure. * - * @errors Possible errors include @ref GLFW_PLATFORM_ERROR. + * @remarks This function may be called before @ref glfwInit. * - * @remark This function may be called before @ref glfwInit. + * @warning No window's context may be current on another thread when this + * function is called. * - * @warning The contexts of any remaining windows must not be current on any - * other thread when this function is called. + * @par Reentrancy + * This function may not be called from a callback. * - * @reentrancy This function must not be called from a callback. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref intro_init * @sa glfwInit * - * @since Added in version 1.0. + * @since Added in GLFW 1.0. * * @ingroup init */ @@ -1255,22 +1123,22 @@ GLFWAPI void glfwTerminate(void); * library. It is intended for when you are using GLFW as a shared library and * want to ensure that you are using the minimum required version. * - * Any or all of the version arguments may be `NULL`. + * Any or all of the version arguments may be `NULL`. This function always + * succeeds. * * @param[out] major Where to store the major version number, or `NULL`. * @param[out] minor Where to store the minor version number, or `NULL`. * @param[out] rev Where to store the revision number, or `NULL`. * - * @errors None. - * - * @remark This function may be called before @ref glfwInit. + * @remarks This function may be called before @ref glfwInit. * - * @thread_safety This function may be called from any thread. + * @par Thread Safety + * This function may be called from any thread. * * @sa @ref intro_version * @sa glfwGetVersionString * - * @since Added in version 1.0. + * @since Added in GLFW 1.0. * * @ingroup init */ @@ -1281,27 +1149,28 @@ GLFWAPI void glfwGetVersion(int* major, int* minor, int* rev); * This function returns the compile-time generated * [version string](@ref intro_version_string) of the GLFW library binary. It * describes the version, platform, compiler and any platform-specific - * compile-time options. It should not be confused with the OpenGL or OpenGL - * ES version string, queried with `glGetString`. + * compile-time options. * * __Do not use the version string__ to parse the GLFW library version. The - * @ref glfwGetVersion function provides the version of the running library - * binary in numerical format. + * @ref glfwGetVersion function already provides the version of the running + * library binary. * - * @return The ASCII encoded GLFW version string. + * This function always succeeds. * - * @errors None. + * @return The GLFW version string. * - * @remark This function may be called before @ref glfwInit. + * @remarks This function may be called before @ref glfwInit. * - * @pointer_lifetime The returned string is static and compile-time generated. + * @par Pointer Lifetime + * The returned string is static and compile-time generated. * - * @thread_safety This function may be called from any thread. + * @par Thread Safety + * This function may be called from any thread. * * @sa @ref intro_version * @sa glfwGetVersion * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup init */ @@ -1327,15 +1196,14 @@ GLFWAPI const char* glfwGetVersionString(void); * callback. * @return The previously set callback, or `NULL` if no callback was set. * - * @errors None. + * @remarks This function may be called before @ref glfwInit. * - * @remark This function may be called before @ref glfwInit. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref error_handling * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup init */ @@ -1344,27 +1212,26 @@ GLFWAPI GLFWerrorfun glfwSetErrorCallback(GLFWerrorfun cbfun); /*! @brief Returns the currently connected monitors. * * This function returns an array of handles for all currently connected - * monitors. The primary monitor is always first in the returned array. If no - * monitors were found, this function returns `NULL`. + * monitors. * * @param[out] count Where to store the number of monitors in the returned * array. This is set to zero if an error occurred. - * @return An array of monitor handles, or `NULL` if no monitors were found or - * if an [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * @return An array of monitor handles, or `NULL` if an + * [error](@ref error_handling) occurred. * - * @pointer_lifetime The returned array is allocated and freed by GLFW. You - * should not free it yourself. It is guaranteed to be valid only until the - * monitor configuration changes or the library is terminated. + * @par Pointer Lifetime + * The returned array is allocated and freed by GLFW. You should not free it + * yourself. It is guaranteed to be valid only until the monitor configuration + * changes or the library is terminated. * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref monitor_monitors * @sa @ref monitor_event * @sa glfwGetPrimaryMonitor * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup monitor */ @@ -1373,22 +1240,18 @@ GLFWAPI GLFWmonitor** glfwGetMonitors(int* count); /*! @brief Returns the primary monitor. * * This function returns the primary monitor. This is usually the monitor - * where elements like the task bar or global menu bar are located. - * - * @return The primary monitor, or `NULL` if no monitors were found or if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * where elements like the Windows task bar or the OS X menu bar is located. * - * @thread_safety This function must only be called from the main thread. + * @return The primary monitor, or `NULL` if an [error](@ref error_handling) + * occurred. * - * @remark The primary monitor is always first in the array returned by @ref - * glfwGetMonitors. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref monitor_monitors * @sa glfwGetMonitors * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup monitor */ @@ -1406,14 +1269,12 @@ GLFWAPI GLFWmonitor* glfwGetPrimaryMonitor(void); * @param[out] xpos Where to store the monitor x-coordinate, or `NULL`. * @param[out] ypos Where to store the monitor y-coordinate, or `NULL`. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref monitor_properties * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup monitor */ @@ -1438,16 +1299,15 @@ GLFWAPI void glfwGetMonitorPos(GLFWmonitor* monitor, int* xpos, int* ypos); * @param[out] heightMM Where to store the height, in millimetres, of the * monitor's display area, or `NULL`. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @remark @win32 calculates the returned physical size from the + * @remarks __Windows:__ The OS calculates the returned physical size from the * current resolution and system DPI instead of querying the monitor EDID data. * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref monitor_properties * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup monitor */ @@ -1463,17 +1323,17 @@ GLFWAPI void glfwGetMonitorPhysicalSize(GLFWmonitor* monitor, int* widthMM, int* * @return The UTF-8 encoded name of the monitor, or `NULL` if an * [error](@ref error_handling) occurred. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @pointer_lifetime The returned string is allocated and freed by GLFW. You - * should not free it yourself. It is valid until the specified monitor is - * disconnected or the library is terminated. + * @par Pointer Lifetime + * The returned string is allocated and freed by GLFW. You should not free it + * yourself. It is valid until the specified monitor is disconnected or the + * library is terminated. * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref monitor_properties * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup monitor */ @@ -1490,13 +1350,15 @@ GLFWAPI const char* glfwGetMonitorName(GLFWmonitor* monitor); * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * @bug __X11:__ This callback is not yet called on monitor configuration + * changes. * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref monitor_event * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup monitor */ @@ -1515,21 +1377,21 @@ GLFWAPI GLFWmonitorfun glfwSetMonitorCallback(GLFWmonitorfun cbfun); * @return An array of video modes, or `NULL` if an * [error](@ref error_handling) occurred. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. + * @par Pointer Lifetime + * The returned array is allocated and freed by GLFW. You should not free it + * yourself. It is valid until the specified monitor is disconnected, this + * function is called again for that monitor or the library is terminated. * - * @pointer_lifetime The returned array is allocated and freed by GLFW. You - * should not free it yourself. It is valid until the specified monitor is - * disconnected, this function is called again for that monitor or the library - * is terminated. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref monitor_modes * @sa glfwGetVideoMode * - * @since Added in version 1.0. - * @glfw3 Changed to return an array of modes for a specific monitor. + * @since Added in GLFW 1.0. + * + * @par + * __GLFW 3:__ Changed to return an array of modes for a specific monitor. * * @ingroup monitor */ @@ -1545,19 +1407,18 @@ GLFWAPI const GLFWvidmode* glfwGetVideoModes(GLFWmonitor* monitor, int* count); * @return The current mode of the monitor, or `NULL` if an * [error](@ref error_handling) occurred. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @pointer_lifetime The returned array is allocated and freed by GLFW. You - * should not free it yourself. It is valid until the specified monitor is - * disconnected or the library is terminated. + * @par Pointer Lifetime + * The returned array is allocated and freed by GLFW. You should not free it + * yourself. It is valid until the specified monitor is disconnected or the + * library is terminated. * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref monitor_modes * @sa glfwGetVideoModes * - * @since Added in version 3.0. Replaces `glfwGetDesktopMode`. + * @since Added in GLFW 3.0. Replaces `glfwGetDesktopMode`. * * @ingroup monitor */ @@ -1572,14 +1433,12 @@ GLFWAPI const GLFWvidmode* glfwGetVideoMode(GLFWmonitor* monitor); * @param[in] monitor The monitor whose gamma ramp to set. * @param[in] gamma The desired exponent. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref monitor_gamma * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup monitor */ @@ -1593,19 +1452,18 @@ GLFWAPI void glfwSetGamma(GLFWmonitor* monitor, float gamma); * @return The current gamma ramp, or `NULL` if an * [error](@ref error_handling) occurred. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @pointer_lifetime The returned structure and its arrays are allocated and - * freed by GLFW. You should not free them yourself. They are valid until the - * specified monitor is disconnected, this function is called again for that - * monitor or the library is terminated. + * @par Pointer Lifetime + * The returned structure and its arrays are allocated and freed by GLFW. You + * should not free them yourself. They are valid until the specified monitor + * is disconnected, this function is called again for that monitor or the + * library is terminated. * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref monitor_gamma * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup monitor */ @@ -1620,22 +1478,20 @@ GLFWAPI const GLFWgammaramp* glfwGetGammaRamp(GLFWmonitor* monitor); * @param[in] monitor The monitor whose gamma ramp to set. * @param[in] ramp The gamma ramp to use. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @remark Gamma ramp sizes other than 256 are not supported by all platforms + * @remarks Gamma ramp sizes other than 256 are not supported by all platforms * or graphics hardware. * - * @remark @win32 The gamma ramp size must be 256. + * @remarks __Windows:__ The gamma ramp size must be 256. * - * @pointer_lifetime The specified gamma ramp is copied before this function - * returns. + * @par Pointer Lifetime + * The specified gamma ramp is copied before this function returns. * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref monitor_gamma * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup monitor */ @@ -1646,14 +1502,13 @@ GLFWAPI void glfwSetGammaRamp(GLFWmonitor* monitor, const GLFWgammaramp* ramp); * This function resets all window hints to their * [default values](@ref window_hints_values). * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref window_hints * @sa glfwWindowHint * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup window */ @@ -1666,26 +1521,20 @@ GLFWAPI void glfwDefaultWindowHints(void); * glfwWindowHint or @ref glfwDefaultWindowHints, or until the library is * terminated. * - * This function does not check whether the specified hint values are valid. - * If you set hints to invalid values this will instead be reported by the next - * call to @ref glfwCreateWindow. + * @param[in] target The [window hint](@ref window_hints) to set. + * @param[in] hint The new value of the window hint. * - * @param[in] hint The [window hint](@ref window_hints) to set. - * @param[in] value The new value of the window hint. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_INVALID_ENUM. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref window_hints * @sa glfwDefaultWindowHints * - * @since Added in version 3.0. Replaces `glfwOpenWindowHint`. + * @since Added in GLFW 3.0. Replaces `glfwOpenWindowHint`. * * @ingroup window */ -GLFWAPI void glfwWindowHint(int hint, int value); +GLFWAPI void glfwWindowHint(int target, int hint); /*! @brief Creates a window and its associated context. * @@ -1702,8 +1551,8 @@ GLFWAPI void glfwWindowHint(int hint, int value); * requested, as not all parameters and hints are * [hard constraints](@ref window_hints_hard). This includes the size of the * window, especially for full screen windows. To query the actual attributes - * of the created window, framebuffer and context, see @ref - * glfwGetWindowAttrib, @ref glfwGetWindowSize and @ref glfwGetFramebufferSize. + * of the created window, framebuffer and context, use queries like @ref + * glfwGetWindowAttrib and @ref glfwGetWindowSize. * * To create a full screen window, you need to specify the monitor the window * will cover. If no monitor is specified, windowed mode will be used. Unless @@ -1746,60 +1595,50 @@ GLFWAPI void glfwWindowHint(int hint, int value); * @return The handle of the created window, or `NULL` if an * [error](@ref error_handling) occurred. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_INVALID_ENUM, @ref GLFW_INVALID_VALUE, @ref GLFW_API_UNAVAILABLE, @ref - * GLFW_VERSION_UNAVAILABLE, @ref GLFW_FORMAT_UNAVAILABLE and @ref - * GLFW_PLATFORM_ERROR. + * @remarks __Windows:__ Window creation will fail if the Microsoft GDI + * software OpenGL implementation is the only one available. * - * @remark @win32 Window creation will fail if the Microsoft GDI software - * OpenGL implementation is the only one available. + * @remarks __Windows:__ If the executable has an icon resource named + * `GLFW_ICON,` it will be set as the icon for the window. If no such icon is + * present, the `IDI_WINLOGO` icon will be used instead. * - * @remark @win32 If the executable has an icon resource named `GLFW_ICON,` - * it will be set as the icon for the window. If no such icon is present, the - * `IDI_WINLOGO` icon will be used instead. + * @remarks __Windows:__ The context to share resources with may not be current + * on any other thread. * - * @remark @win32 The context to share resources with must not be current on - * any other thread. - * - * @remark @osx The GLFW window has no icon, as it is not a document + * @remarks __OS X:__ The GLFW window has no icon, as it is not a document * window, but the dock icon will be the same as the application bundle's icon. * For more information on bundles, see the * [Bundle Programming Guide](https://developer.apple.com/library/mac/documentation/CoreFoundation/Conceptual/CFBundles/) * in the Mac Developer Library. * - * @remark @osx The first time a window is created the menu bar is populated - * with common commands like Hide, Quit and About. The About entry opens - * a minimal about dialog with information from the application's bundle. The - * menu bar can be disabled with a + * @remarks __OS X:__ The first time a window is created the menu bar is + * populated with common commands like Hide, Quit and About. The About entry + * opens a minimal about dialog with information from the application's bundle. + * The menu bar can be disabled with a * [compile-time option](@ref compile_options_osx). * - * @remark @osx On OS X 10.10 and later the window frame will not be rendered - * at full resolution on Retina displays unless the `NSHighResolutionCapable` - * key is enabled in the application bundle's `Info.plist`. For more - * information, see + * @remarks __OS X:__ On OS X 10.10 and later the window frame will not be + * rendered at full resolution on Retina displays unless the + * `NSHighResolutionCapable` key is enabled in the application bundle's + * `Info.plist`. For more information, see * [High Resolution Guidelines for OS X](https://developer.apple.com/library/mac/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html) - * in the Mac Developer Library. The GLFW test and example programs use - * a custom `Info.plist` template for this, which can be found as - * `CMake/MacOSXBundleInfo.plist.in` in the source tree. + * in the Mac Developer Library. * - * @remark @x11 There is no mechanism for setting the window icon yet. + * @remarks __X11:__ There is no mechanism for setting the window icon yet. * - * @remark @x11 Some window managers will not respect the placement of + * @remarks __X11:__ Some window managers will not respect the placement of * initially hidden windows. * - * @remark @x11 Due to the asynchronous nature of X11, it may take a moment for - * a window to reach its requested state. This means you may not be able to - * query the final size, position or other attributes directly after window - * creation. + * @par Reentrancy + * This function may not be called from a callback. * - * @reentrancy This function must not be called from a callback. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref window_creation * @sa glfwDestroyWindow * - * @since Added in version 3.0. Replaces `glfwOpenWindow`. + * @since Added in GLFW 3.0. Replaces `glfwOpenWindow`. * * @ingroup window */ @@ -1815,20 +1654,19 @@ GLFWAPI GLFWwindow* glfwCreateWindow(int width, int height, const char* title, G * * @param[in] window The window to destroy. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * * @note The context of the specified window must not be current on any other * thread when this function is called. * - * @reentrancy This function must not be called from a callback. + * @par Reentrancy + * This function may not be called from a callback. * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref window_creation * @sa glfwCreateWindow * - * @since Added in version 3.0. Replaces `glfwCloseWindow`. + * @since Added in GLFW 3.0. Replaces `glfwCloseWindow`. * * @ingroup window */ @@ -1841,14 +1679,12 @@ GLFWAPI void glfwDestroyWindow(GLFWwindow* window); * @param[in] window The window to query. * @return The value of the close flag. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. + * @par Thread Safety + * This function may be called from any thread. Access is not synchronized. * * @sa @ref window_close * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup window */ @@ -1863,14 +1699,12 @@ GLFWAPI int glfwWindowShouldClose(GLFWwindow* window); * @param[in] window The window whose flag to change. * @param[in] value The new value. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. + * @par Thread Safety + * This function may be called from any thread. Access is not synchronized. * * @sa @ref window_close * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup window */ @@ -1884,18 +1718,15 @@ GLFWAPI void glfwSetWindowShouldClose(GLFWwindow* window, int value); * @param[in] window The window whose title to change. * @param[in] title The UTF-8 encoded window title. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @remark @osx The window title will not be updated until the next time you - * process events. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref window_title * - * @since Added in version 1.0. - * @glfw3 Added window handle parameter. + * @since Added in GLFW 1.0. + * + * @par + * __GLFW 3:__ Added window handle parameter. * * @ingroup window */ @@ -1915,15 +1746,13 @@ GLFWAPI void glfwSetWindowTitle(GLFWwindow* window, const char* title); * @param[out] ypos Where to store the y-coordinate of the upper-left corner of * the client area, or `NULL`. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref window_pos * @sa glfwSetWindowPos * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup window */ @@ -1945,16 +1774,16 @@ GLFWAPI void glfwGetWindowPos(GLFWwindow* window, int* xpos, int* ypos); * @param[in] xpos The x-coordinate of the upper-left corner of the client area. * @param[in] ypos The y-coordinate of the upper-left corner of the client area. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref window_pos * @sa glfwGetWindowPos * - * @since Added in version 1.0. - * @glfw3 Added window handle parameter. + * @since Added in GLFW 1.0. + * + * @par + * __GLFW 3:__ Added window handle parameter. * * @ingroup window */ @@ -1975,95 +1804,20 @@ GLFWAPI void glfwSetWindowPos(GLFWwindow* window, int xpos, int ypos); * @param[out] height Where to store the height, in screen coordinates, of the * client area, or `NULL`. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref window_size * @sa glfwSetWindowSize * - * @since Added in version 1.0. - * @glfw3 Added window handle parameter. + * @since Added in GLFW 1.0. * - * @ingroup window - */ -GLFWAPI void glfwGetWindowSize(GLFWwindow* window, int* width, int* height); - -/*! @brief Sets the size limits of the specified window. - * - * This function sets the size limits of the client area of the specified - * window. If the window is full screen or not resizable, this function does - * nothing. - * - * The size limits are applied immediately and may cause the window to be - * resized. - * - * @param[in] window The window to set limits for. - * @param[in] minwidth The minimum width, in screen coordinates, of the client - * area, or `GLFW_DONT_CARE`. - * @param[in] minheight The minimum height, in screen coordinates, of the - * client area, or `GLFW_DONT_CARE`. - * @param[in] maxwidth The maximum width, in screen coordinates, of the client - * area, or `GLFW_DONT_CARE`. - * @param[in] maxheight The maximum height, in screen coordinates, of the - * client area, or `GLFW_DONT_CARE`. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @remark If you set size limits and an aspect ratio that conflict, the - * results are undefined. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_sizelimits - * @sa glfwSetWindowAspectRatio - * - * @since Added in version 3.2. - * - * @ingroup window - */ -GLFWAPI void glfwSetWindowSizeLimits(GLFWwindow* window, int minwidth, int minheight, int maxwidth, int maxheight); - -/*! @brief Sets the aspect ratio of the specified window. - * - * This function sets the required aspect ratio of the client area of the - * specified window. If the window is full screen or not resizable, this - * function does nothing. - * - * The aspect ratio is specified as a numerator and a denominator and both - * values must be greater than zero. For example, the common 16:9 aspect ratio - * is specified as 16 and 9, respectively. - * - * If the numerator and denominator is set to `GLFW_DONT_CARE` then the aspect - * ratio limit is disabled. - * - * The aspect ratio is applied immediately and may cause the window to be - * resized. - * - * @param[in] window The window to set limits for. - * @param[in] numer The numerator of the desired aspect ratio, or - * `GLFW_DONT_CARE`. - * @param[in] denom The denominator of the desired aspect ratio, or - * `GLFW_DONT_CARE`. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_INVALID_VALUE and @ref GLFW_PLATFORM_ERROR. - * - * @remark If you set size limits and an aspect ratio that conflict, the - * results are undefined. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_sizelimits - * @sa glfwSetWindowSizeLimits - * - * @since Added in version 3.2. + * @par + * __GLFW 3:__ Added window handle parameter. * * @ingroup window */ -GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* window, int numer, int denom); +GLFWAPI void glfwGetWindowSize(GLFWwindow* window, int* width, int* height); /*! @brief Sets the size of the client area of the specified window. * @@ -2082,16 +1836,16 @@ GLFWAPI void glfwSetWindowAspectRatio(GLFWwindow* window, int numer, int denom); * @param[in] width The desired width of the specified window. * @param[in] height The desired height of the specified window. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref window_size * @sa glfwGetWindowSize * - * @since Added in version 1.0. - * @glfw3 Added window handle parameter. + * @since Added in GLFW 1.0. + * + * @par + * __GLFW 3:__ Added window handle parameter. * * @ingroup window */ @@ -2112,15 +1866,13 @@ GLFWAPI void glfwSetWindowSize(GLFWwindow* window, int width, int height); * @param[out] height Where to store the height, in pixels, of the framebuffer, * or `NULL`. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref window_fbsize * @sa glfwSetFramebufferSizeCallback * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup window */ @@ -2150,14 +1902,12 @@ GLFWAPI void glfwGetFramebufferSize(GLFWwindow* window, int* width, int* height) * @param[out] bottom Where to store the size, in screen coordinates, of the * bottom edge of the window frame, or `NULL`. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref window_size * - * @since Added in version 3.1. + * @since Added in GLFW 3.1. * * @ingroup window */ @@ -2174,17 +1924,16 @@ GLFWAPI void glfwGetWindowFrameSize(GLFWwindow* window, int* left, int* top, int * * @param[in] window The window to iconify. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref window_iconify * @sa glfwRestoreWindow - * @sa glfwMaximizeWindow * - * @since Added in version 2.1. - * @glfw3 Added window handle parameter. + * @since Added in GLFW 2.1. + * + * @par + * __GLFW 3:__ Added window handle parameter. * * @ingroup window */ @@ -2193,51 +1942,27 @@ GLFWAPI void glfwIconifyWindow(GLFWwindow* window); /*! @brief Restores the specified window. * * This function restores the specified window if it was previously iconified - * (minimized) or maximized. If the window is already restored, this function - * does nothing. + * (minimized). If the window is already restored, this function does nothing. * * If the specified window is a full screen window, the resolution chosen for * the window is restored on the selected monitor. * * @param[in] window The window to restore. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_iconify - * @sa glfwIconifyWindow - * @sa glfwMaximizeWindow - * - * @since Added in version 2.1. - * @glfw3 Added window handle parameter. - * - * @ingroup window - */ -GLFWAPI void glfwRestoreWindow(GLFWwindow* window); - -/*! @brief Maximizes the specified window. - * - * This function maximizes the specified window if it was previously not - * maximized. If the window is already maximized, this function does nothing. - * - * If the specified window is a full screen window, this function does nothing. - * - * @param[in] window The window to maximize. - * * @par Thread Safety * This function may only be called from the main thread. * * @sa @ref window_iconify * @sa glfwIconifyWindow - * @sa glfwRestoreWindow * - * @since Added in GLFW 3.2. + * @since Added in GLFW 2.1. + * + * @par + * __GLFW 3:__ Added window handle parameter. * * @ingroup window */ -GLFWAPI void glfwMaximizeWindow(GLFWwindow* window); +GLFWAPI void glfwRestoreWindow(GLFWwindow* window); /*! @brief Makes the specified window visible. * @@ -2247,15 +1972,13 @@ GLFWAPI void glfwMaximizeWindow(GLFWwindow* window); * * @param[in] window The window to make visible. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref window_hide * @sa glfwHideWindow * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup window */ @@ -2269,48 +1992,18 @@ GLFWAPI void glfwShowWindow(GLFWwindow* window); * * @param[in] window The window to hide. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref window_hide * @sa glfwShowWindow * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup window */ GLFWAPI void glfwHideWindow(GLFWwindow* window); -/*! @brief Brings the specified window to front and sets input focus. - * - * This function brings the specified window to front and sets input focus. - * The window should already be visible and not iconified. - * - * By default, both windowed and full screen mode windows are focused when - * initially created. Set the [GLFW_FOCUSED](@ref window_hints_wnd) to disable - * this behavior. - * - * __Do not use this function__ to steal focus from other applications unless - * you are certain that is what the user wants. Focus stealing can be - * extremely disruptive. - * - * @param[in] window The window to give input focus. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref window_focus - * - * @since Added in version 3.2. - * - * @ingroup window - */ -GLFWAPI void glfwFocusWindow(GLFWwindow* window); - /*! @brief Returns the monitor that the window uses for full screen mode. * * This function returns the handle of the monitor that the specified window is @@ -2320,13 +2013,12 @@ GLFWAPI void glfwFocusWindow(GLFWwindow* window); * @return The monitor, or `NULL` if the window is in windowed mode or an error * occurred. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref window_monitor * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup window */ @@ -2343,22 +2035,12 @@ GLFWAPI GLFWmonitor* glfwGetWindowMonitor(GLFWwindow* window); * @return The value of the attribute, or zero if an * [error](@ref error_handling) occurred. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. - * - * @remark Framebuffer related hints are not window attributes. See @ref - * window_attribs_fb for more information. - * - * @remark Zero is a valid value for many window and context related - * attributes so you cannot use a return value of zero as an indication of - * errors. However, this function should not fail as long as it is passed - * valid arguments and the library has been [initialized](@ref intro_init). - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref window_attribs * - * @since Added in version 3.0. Replaces `glfwGetWindowParam` and + * @since Added in GLFW 3.0. Replaces `glfwGetWindowParam` and * `glfwGetGLVersion`. * * @ingroup window @@ -2374,15 +2056,13 @@ GLFWAPI int glfwGetWindowAttrib(GLFWwindow* window, int attrib); * @param[in] window The window whose pointer to set. * @param[in] pointer The new value. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. + * @par Thread Safety + * This function may be called from any thread. Access is not synchronized. * * @sa @ref window_userptr * @sa glfwGetWindowUserPointer * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup window */ @@ -2395,15 +2075,13 @@ GLFWAPI void glfwSetWindowUserPointer(GLFWwindow* window, void* pointer); * * @param[in] window The window whose pointer to return. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. + * @par Thread Safety + * This function may be called from any thread. Access is not synchronized. * * @sa @ref window_userptr * @sa glfwSetWindowUserPointer * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup window */ @@ -2421,13 +2099,12 @@ GLFWAPI void* glfwGetWindowUserPointer(GLFWwindow* window); * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref window_pos * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup window */ @@ -2445,14 +2122,15 @@ GLFWAPI GLFWwindowposfun glfwSetWindowPosCallback(GLFWwindow* window, GLFWwindow * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref window_size * - * @since Added in version 1.0. - * @glfw3 Added window handle parameter and return value. + * @since Added in GLFW 1.0. + * + * @par + * __GLFW 3:__ Added window handle parameter. Updated callback signature. * * @ingroup window */ @@ -2475,17 +2153,18 @@ GLFWAPI GLFWwindowsizefun glfwSetWindowSizeCallback(GLFWwindow* window, GLFWwind * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @remark @osx Selecting Quit from the application menu will trigger the close - * callback for all windows. + * @remarks __OS X:__ Selecting Quit from the application menu will + * trigger the close callback for all windows. * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref window_close * - * @since Added in version 2.5. - * @glfw3 Added window handle parameter and return value. + * @since Added in GLFW 2.5. + * + * @par + * __GLFW 3:__ Added window handle parameter. Updated callback signature. * * @ingroup window */ @@ -2507,14 +2186,15 @@ GLFWAPI GLFWwindowclosefun glfwSetWindowCloseCallback(GLFWwindow* window, GLFWwi * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref window_refresh * - * @since Added in version 2.5. - * @glfw3 Added window handle parameter and return value. + * @since Added in GLFW 2.5. + * + * @par + * __GLFW 3:__ Added window handle parameter. Updated callback signature. * * @ingroup window */ @@ -2536,13 +2216,12 @@ GLFWAPI GLFWwindowrefreshfun glfwSetWindowRefreshCallback(GLFWwindow* window, GL * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref window_focus * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup window */ @@ -2559,13 +2238,12 @@ GLFWAPI GLFWwindowfocusfun glfwSetWindowFocusCallback(GLFWwindow* window, GLFWwi * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref window_iconify * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup window */ @@ -2582,13 +2260,12 @@ GLFWAPI GLFWwindowiconifyfun glfwSetWindowIconifyCallback(GLFWwindow* window, GL * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref window_fbsize * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup window */ @@ -2612,17 +2289,16 @@ GLFWAPI GLFWframebuffersizefun glfwSetFramebufferSizeCallback(GLFWwindow* window * * Event processing is not required for joystick input to work. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @reentrancy This function must not be called from a callback. + * @par Reentrancy + * This function may not be called from a callback. * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref events * @sa glfwWaitEvents * - * @since Added in version 1.0. + * @since Added in GLFW 1.0. * * @ingroup window */ @@ -2656,17 +2332,16 @@ GLFWAPI void glfwPollEvents(void); * * Event processing is not required for joystick input to work. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. + * @par Reentrancy + * This function may not be called from a callback. * - * @reentrancy This function must not be called from a callback. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref events * @sa glfwPollEvents * - * @since Added in version 2.5. + * @since Added in GLFW 2.5. * * @ingroup window */ @@ -2681,15 +2356,13 @@ GLFWAPI void glfwWaitEvents(void); * of threads in applications that do not create windows, use your threading * library of choice. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function may be called from any thread. + * @par Thread Safety + * This function may be called from any thread. * * @sa @ref events * @sa glfwWaitEvents * - * @since Added in version 3.1. + * @since Added in GLFW 3.1. * * @ingroup window */ @@ -2705,14 +2378,12 @@ GLFWAPI void glfwPostEmptyEvent(void); * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or * `GLFW_STICKY_MOUSE_BUTTONS`. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_INVALID_ENUM. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa glfwSetInputMode * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup input */ @@ -2733,96 +2404,37 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); * and unlimited cursor movement. This is useful for implementing for * example 3D camera controls. * - * If the mode is `GLFW_STICKY_KEYS`, the value must be either `GLFW_TRUE` to - * enable sticky keys, or `GLFW_FALSE` to disable it. If sticky keys are + * If the mode is `GLFW_STICKY_KEYS`, the value must be either `GL_TRUE` to + * enable sticky keys, or `GL_FALSE` to disable it. If sticky keys are * enabled, a key press will ensure that @ref glfwGetKey returns `GLFW_PRESS` * the next time it is called even if the key had been released before the * call. This is useful when you are only interested in whether keys have been * pressed but not when or in which order. * * If the mode is `GLFW_STICKY_MOUSE_BUTTONS`, the value must be either - * `GLFW_TRUE` to enable sticky mouse buttons, or `GLFW_FALSE` to disable it. - * If sticky mouse buttons are enabled, a mouse button press will ensure that - * @ref glfwGetMouseButton returns `GLFW_PRESS` the next time it is called even - * if the mouse button had been released before the call. This is useful when - * you are only interested in whether mouse buttons have been pressed but not - * when or in which order. + * `GL_TRUE` to enable sticky mouse buttons, or `GL_FALSE` to disable it. If + * sticky mouse buttons are enabled, a mouse button press will ensure that @ref + * glfwGetMouseButton returns `GLFW_PRESS` the next time it is called even if + * the mouse button had been released before the call. This is useful when you + * are only interested in whether mouse buttons have been pressed but not when + * or in which order. * * @param[in] window The window whose input mode to set. * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS` or * `GLFW_STICKY_MOUSE_BUTTONS`. * @param[in] value The new value of the specified input mode. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa glfwGetInputMode * - * @since Added in version 3.0. Replaces `glfwEnable` and `glfwDisable`. + * @since Added in GLFW 3.0. Replaces `glfwEnable` and `glfwDisable`. * * @ingroup input */ GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value); -/*! @brief Returns the localized name of the specified printable key. - * - * This function returns the localized name of the specified printable key. - * This is intended for displaying key bindings to the user. - * - * If the key is `GLFW_KEY_UNKNOWN`, the scancode is used instead, otherwise - * the scancode is ignored. If a non-printable key or (if the key is - * `GLFW_KEY_UNKNOWN`) a scancode that maps to a non-printable key is - * specified, this function returns `NULL`. - * - * This behavior allows you to pass in the arguments passed to the - * [key callback](@ref input_key) without modification. - * - * The printable keys are: - * - `GLFW_KEY_APOSTROPHE` - * - `GLFW_KEY_COMMA` - * - `GLFW_KEY_MINUS` - * - `GLFW_KEY_PERIOD` - * - `GLFW_KEY_SLASH` - * - `GLFW_KEY_SEMICOLON` - * - `GLFW_KEY_EQUAL` - * - `GLFW_KEY_LEFT_BRACKET` - * - `GLFW_KEY_RIGHT_BRACKET` - * - `GLFW_KEY_BACKSLASH` - * - `GLFW_KEY_WORLD_1` - * - `GLFW_KEY_WORLD_2` - * - `GLFW_KEY_0` to `GLFW_KEY_9` - * - `GLFW_KEY_A` to `GLFW_KEY_Z` - * - `GLFW_KEY_KP_0` to `GLFW_KEY_KP_9` - * - `GLFW_KEY_KP_DECIMAL` - * - `GLFW_KEY_KP_DIVIDE` - * - `GLFW_KEY_KP_MULTIPLY` - * - `GLFW_KEY_KP_SUBTRACT` - * - `GLFW_KEY_KP_ADD` - * - `GLFW_KEY_KP_EQUAL` - * - * @param[in] key The key to query, or `GLFW_KEY_UNKNOWN`. - * @param[in] scancode The scancode of the key to query. - * @return The localized name of the key, or `NULL`. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @pointer_lifetime The returned string is allocated and freed by GLFW. You - * should not free it yourself. It is valid until the next call to @ref - * glfwGetKeyName, or until the library is terminated. - * - * @thread_safety This function must only be called from the main thread. - * - * @sa @ref input_key_name - * - * @since Added in version 3.2. - * - * @ingroup input - */ -GLFWAPI const char* glfwGetKeyName(int key, int scancode); - /*! @brief Returns the last reported state of a keyboard key for the specified * window. * @@ -2842,22 +2454,20 @@ GLFWAPI const char* glfwGetKeyName(int key, int scancode); * The [modifier key bit masks](@ref mods) are not key tokens and cannot be * used with this function. * - * __Do not use this function__ to implement [text input](@ref input_char). - * * @param[in] window The desired window. * @param[in] key The desired [keyboard key](@ref keys). `GLFW_KEY_UNKNOWN` is * not a valid key for this function. * @return One of `GLFW_PRESS` or `GLFW_RELEASE`. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_INVALID_ENUM. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref input_key * - * @since Added in version 1.0. - * @glfw3 Added window handle parameter. + * @since Added in GLFW 1.0. + * + * @par + * __GLFW 3:__ Added window handle parameter. * * @ingroup input */ @@ -2878,15 +2488,15 @@ GLFWAPI int glfwGetKey(GLFWwindow* window, int key); * @param[in] button The desired [mouse button](@ref buttons). * @return One of `GLFW_PRESS` or `GLFW_RELEASE`. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_INVALID_ENUM. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref input_mouse_button * - * @since Added in version 1.0. - * @glfw3 Added window handle parameter. + * @since Added in GLFW 1.0. + * + * @par + * __GLFW 3:__ Added window handle parameter. * * @ingroup input */ @@ -2916,15 +2526,13 @@ GLFWAPI int glfwGetMouseButton(GLFWwindow* window, int button); * @param[out] ypos Where to store the cursor y-coordinate, relative to the to * top edge of the client area, or `NULL`. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref cursor_pos * @sa glfwSetCursorPos * - * @since Added in version 3.0. Replaces `glfwGetMousePos`. + * @since Added in GLFW 3.0. Replaces `glfwGetMousePos`. * * @ingroup input */ @@ -2953,19 +2561,17 @@ GLFWAPI void glfwGetCursorPos(GLFWwindow* window, double* xpos, double* ypos); * @param[in] ypos The desired y-coordinate, relative to the top edge of the * client area. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. + * @remarks __X11:__ Due to the asynchronous nature of a modern X desktop, it + * may take a moment for the window focus event to arrive. This means you will + * not be able to set the cursor position directly after window creation. * - * @remark @x11 Due to the asynchronous nature of X11, it may take a moment for - * the window focus event to arrive. This means you may not be able to set the - * cursor position directly after window creation. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref cursor_pos * @sa glfwGetCursorPos * - * @since Added in version 3.0. Replaces `glfwSetMousePos`. + * @since Added in GLFW 3.0. Replaces `glfwSetMousePos`. * * @ingroup input */ @@ -2977,9 +2583,9 @@ GLFWAPI void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos); * glfwSetCursor. The cursor can be destroyed with @ref glfwDestroyCursor. * Any remaining cursors are destroyed by @ref glfwTerminate. * - * The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight - * bits per channel. They are arranged canonically as packed sequential rows, - * starting from the top-left corner. + * The pixels are 32-bit little-endian RGBA, i.e. eight bits per channel. They + * are arranged canonically as packed sequential rows, starting from the + * top-left corner. * * The cursor hotspot is specified in pixels, relative to the upper-left corner * of the cursor image. Like all other coordinate systems in GLFW, the X-axis @@ -2988,24 +2594,24 @@ GLFWAPI void glfwSetCursorPos(GLFWwindow* window, double xpos, double ypos); * @param[in] image The desired cursor image. * @param[in] xhot The desired x-coordinate, in pixels, of the cursor hotspot. * @param[in] yhot The desired y-coordinate, in pixels, of the cursor hotspot. + * * @return The handle of the created cursor, or `NULL` if an * [error](@ref error_handling) occurred. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. + * @par Pointer Lifetime + * The specified image data is copied before this function returns. * - * @pointer_lifetime The specified image data is copied before this function - * returns. + * @par Reentrancy + * This function may not be called from a callback. * - * @reentrancy This function must not be called from a callback. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref cursor_object * @sa glfwDestroyCursor * @sa glfwCreateStandardCursor * - * @since Added in version 3.1. + * @since Added in GLFW 3.1. * * @ingroup input */ @@ -3017,20 +2623,20 @@ GLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot) * a window with @ref glfwSetCursor. * * @param[in] shape One of the [standard shapes](@ref shapes). + * * @return A new cursor ready to use or `NULL` if an * [error](@ref error_handling) occurred. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * @par Reentrancy + * This function may not be called from a callback. * - * @reentrancy This function must not be called from a callback. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref cursor_object * @sa glfwCreateCursor * - * @since Added in version 3.1. + * @since Added in GLFW 3.1. * * @ingroup input */ @@ -3044,17 +2650,16 @@ GLFWAPI GLFWcursor* glfwCreateStandardCursor(int shape); * * @param[in] cursor The cursor object to destroy. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @reentrancy This function must not be called from a callback. + * @par Reentrancy + * This function may not be called from a callback. * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref cursor_object * @sa glfwCreateCursor * - * @since Added in version 3.1. + * @since Added in GLFW 3.1. * * @ingroup input */ @@ -3074,14 +2679,12 @@ GLFWAPI void glfwDestroyCursor(GLFWcursor* cursor); * @param[in] cursor The cursor to set, or `NULL` to switch back to the default * arrow cursor. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref cursor_object * - * @since Added in version 3.1. + * @since Added in GLFW 3.1. * * @ingroup input */ @@ -3117,14 +2720,15 @@ GLFWAPI void glfwSetCursor(GLFWwindow* window, GLFWcursor* cursor); * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref input_key * - * @since Added in version 1.0. - * @glfw3 Added window handle parameter and return value. + * @since Added in GLFW 1.0. + * + * @par + * __GLFW 3:__ Added window handle parameter. Updated callback signature. * * @ingroup input */ @@ -3155,14 +2759,15 @@ GLFWAPI GLFWkeyfun glfwSetKeyCallback(GLFWwindow* window, GLFWkeyfun cbfun); * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref input_char * - * @since Added in version 2.4. - * @glfw3 Added window handle parameter and return value. + * @since Added in GLFW 2.4. + * + * @par + * __GLFW 3:__ Added window handle parameter. Updated callback signature. * * @ingroup input */ @@ -3189,13 +2794,12 @@ GLFWAPI GLFWcharfun glfwSetCharCallback(GLFWwindow* window, GLFWcharfun cbfun); * @return The previously set callback, or `NULL` if no callback was set or an * error occurred. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref input_char * - * @since Added in version 3.1. + * @since Added in GLFW 3.1. * * @ingroup input */ @@ -3218,14 +2822,15 @@ GLFWAPI GLFWcharmodsfun glfwSetCharModsCallback(GLFWwindow* window, GLFWcharmods * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref input_mouse_button * - * @since Added in version 1.0. - * @glfw3 Added window handle parameter and return value. + * @since Added in GLFW 1.0. + * + * @par + * __GLFW 3:__ Added window handle parameter. Updated callback signature. * * @ingroup input */ @@ -3244,13 +2849,12 @@ GLFWAPI GLFWmousebuttonfun glfwSetMouseButtonCallback(GLFWwindow* window, GLFWmo * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref cursor_pos * - * @since Added in version 3.0. Replaces `glfwSetMousePosCallback`. + * @since Added in GLFW 3.0. Replaces `glfwSetMousePosCallback`. * * @ingroup input */ @@ -3268,13 +2872,12 @@ GLFWAPI GLFWcursorposfun glfwSetCursorPosCallback(GLFWwindow* window, GLFWcursor * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref cursor_enter * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup input */ @@ -3295,13 +2898,12 @@ GLFWAPI GLFWcursorenterfun glfwSetCursorEnterCallback(GLFWwindow* window, GLFWcu * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref scrolling * - * @since Added in version 3.0. Replaces `glfwSetMouseWheelCallback`. + * @since Added in GLFW 3.0. Replaces `glfwSetMouseWheelCallback`. * * @ingroup input */ @@ -3323,13 +2925,12 @@ GLFWAPI GLFWscrollfun glfwSetScrollCallback(GLFWwindow* window, GLFWscrollfun cb * @return The previously set callback, or `NULL` if no callback was set or the * library had not been [initialized](@ref intro_init). * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref path_drop * - * @since Added in version 3.1. + * @since Added in GLFW 3.1. * * @ingroup input */ @@ -3340,16 +2941,14 @@ GLFWAPI GLFWdropfun glfwSetDropCallback(GLFWwindow* window, GLFWdropfun cbfun); * This function returns whether the specified joystick is present. * * @param[in] joy The [joystick](@ref joysticks) to query. - * @return `GLFW_TRUE` if the joystick is present, or `GLFW_FALSE` otherwise. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * @return `GL_TRUE` if the joystick is present, or `GL_FALSE` otherwise. * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref joystick * - * @since Added in version 3.0. Replaces `glfwGetJoystickParam`. + * @since Added in GLFW 3.0. Replaces `glfwGetJoystickParam`. * * @ingroup input */ @@ -3360,28 +2959,22 @@ GLFWAPI int glfwJoystickPresent(int joy); * This function returns the values of all axes of the specified joystick. * Each element in the array is a value between -1.0 and 1.0. * - * Querying a joystick slot with no device present is not an error, but will - * cause this function to return `NULL`. Call @ref glfwJoystickPresent to - * check device presence. - * * @param[in] joy The [joystick](@ref joysticks) to query. * @param[out] count Where to store the number of axis values in the returned * array. This is set to zero if an error occurred. * @return An array of axis values, or `NULL` if the joystick is not present. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. - * - * @pointer_lifetime The returned array is allocated and freed by GLFW. You - * should not free it yourself. It is valid until the specified joystick is - * disconnected, this function is called again for that joystick or the library - * is terminated. + * @par Pointer Lifetime + * The returned array is allocated and freed by GLFW. You should not free it + * yourself. It is valid until the specified joystick is disconnected, this + * function is called again for that joystick or the library is terminated. * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref joystick_axis * - * @since Added in version 3.0. Replaces `glfwGetJoystickPos`. + * @since Added in GLFW 3.0. Replaces `glfwGetJoystickPos`. * * @ingroup input */ @@ -3392,29 +2985,25 @@ GLFWAPI const float* glfwGetJoystickAxes(int joy, int* count); * This function returns the state of all buttons of the specified joystick. * Each element in the array is either `GLFW_PRESS` or `GLFW_RELEASE`. * - * Querying a joystick slot with no device present is not an error, but will - * cause this function to return `NULL`. Call @ref glfwJoystickPresent to - * check device presence. - * * @param[in] joy The [joystick](@ref joysticks) to query. * @param[out] count Where to store the number of button states in the returned * array. This is set to zero if an error occurred. * @return An array of button states, or `NULL` if the joystick is not present. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. + * @par Pointer Lifetime + * The returned array is allocated and freed by GLFW. You should not free it + * yourself. It is valid until the specified joystick is disconnected, this + * function is called again for that joystick or the library is terminated. * - * @pointer_lifetime The returned array is allocated and freed by GLFW. You - * should not free it yourself. It is valid until the specified joystick is - * disconnected, this function is called again for that joystick or the library - * is terminated. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref joystick_button * - * @since Added in version 2.2. - * @glfw3 Changed to return a dynamic array. + * @since Added in GLFW 2.2. + * + * @par + * __GLFW 3:__ Changed to return a dynamic array. * * @ingroup input */ @@ -3426,27 +3015,21 @@ GLFWAPI const unsigned char* glfwGetJoystickButtons(int joy, int* count); * The returned string is allocated and freed by GLFW. You should not free it * yourself. * - * Querying a joystick slot with no device present is not an error, but will - * cause this function to return `NULL`. Call @ref glfwJoystickPresent to - * check device presence. - * * @param[in] joy The [joystick](@ref joysticks) to query. * @return The UTF-8 encoded name of the joystick, or `NULL` if the joystick * is not present. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_INVALID_ENUM and @ref GLFW_PLATFORM_ERROR. - * - * @pointer_lifetime The returned string is allocated and freed by GLFW. You - * should not free it yourself. It is valid until the specified joystick is - * disconnected, this function is called again for that joystick or the library - * is terminated. + * @par Pointer Lifetime + * The returned string is allocated and freed by GLFW. You should not free it + * yourself. It is valid until the specified joystick is disconnected, this + * function is called again for that joystick or the library is terminated. * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref joystick_name * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup input */ @@ -3460,18 +3043,16 @@ GLFWAPI const char* glfwGetJoystickName(int joy); * @param[in] window The window that will own the clipboard contents. * @param[in] string A UTF-8 encoded string. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. + * @par Pointer Lifetime + * The specified string is copied before this function returns. * - * @pointer_lifetime The specified string is copied before this function - * returns. - * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref clipboard * @sa glfwGetClipboardString * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup input */ @@ -3480,28 +3061,25 @@ GLFWAPI void glfwSetClipboardString(GLFWwindow* window, const char* string); /*! @brief Returns the contents of the clipboard as a string. * * This function returns the contents of the system clipboard, if it contains - * or is convertible to a UTF-8 encoded string. If the clipboard is empty or - * if its contents cannot be converted, `NULL` is returned and a @ref - * GLFW_FORMAT_UNAVAILABLE error is generated. + * or is convertible to a UTF-8 encoded string. * * @param[in] window The window that will request the clipboard contents. * @return The contents of the clipboard as a UTF-8 encoded string, or `NULL` * if an [error](@ref error_handling) occurred. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_PLATFORM_ERROR. - * - * @pointer_lifetime The returned string is allocated and freed by GLFW. You - * should not free it yourself. It is valid until the next call to @ref + * @par Pointer Lifetime + * The returned string is allocated and freed by GLFW. You should not free it + * yourself. It is valid until the next call to @ref * glfwGetClipboardString or @ref glfwSetClipboardString, or until the library * is terminated. * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref clipboard * @sa glfwSetClipboardString * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup input */ @@ -3520,14 +3098,12 @@ GLFWAPI const char* glfwGetClipboardString(GLFWwindow* window); * @return The current value, in seconds, or zero if an * [error](@ref error_handling) occurred. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. + * @par Thread Safety + * This function may be called from any thread. Access is not synchronized. * * @sa @ref time * - * @since Added in version 1.0. + * @since Added in GLFW 1.0. * * @ingroup input */ @@ -3541,18 +3117,16 @@ GLFWAPI double glfwGetTime(void); * * @param[in] time The new value, in seconds. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_INVALID_VALUE. - * - * @remark The upper limit of the timer is calculated as + * @remarks The upper limit of the timer is calculated as * floor((264 - 1) / 109) and is due to implementations * storing nanoseconds in 64 bits. The limit may be increased in the future. * - * @thread_safety This function must only be called from the main thread. + * @par Thread Safety + * This function may only be called from the main thread. * * @sa @ref time * - * @since Added in version 2.2. + * @since Added in GLFW 2.2. * * @ingroup input */ @@ -3571,22 +3145,16 @@ GLFWAPI void glfwSetTime(double time); * whether a context performs this flush by setting the * [GLFW_CONTEXT_RELEASE_BEHAVIOR](@ref window_hints_ctx) window hint. * - * The specified window must have an OpenGL or OpenGL ES context. Specifying - * a window without a context will generate a @ref GLFW_NO_WINDOW_CONTEXT - * error. - * * @param[in] window The window whose context to make current, or `NULL` to * detach the current context. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_NO_WINDOW_CONTEXT and @ref GLFW_PLATFORM_ERROR. - * - * @thread_safety This function may be called from any thread. + * @par Thread Safety + * This function may be called from any thread. * * @sa @ref context_current * @sa glfwGetCurrentContext * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup context */ @@ -3600,14 +3168,13 @@ GLFWAPI void glfwMakeContextCurrent(GLFWwindow* window); * @return The window whose context is current, or `NULL` if no window's * context is current. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. + * @par Thread Safety + * This function may be called from any thread. * * @sa @ref context_current * @sa glfwMakeContextCurrent * - * @since Added in version 3.0. + * @since Added in GLFW 3.0. * * @ingroup context */ @@ -3615,33 +3182,22 @@ GLFWAPI GLFWwindow* glfwGetCurrentContext(void); /*! @brief Swaps the front and back buffers of the specified window. * - * This function swaps the front and back buffers of the specified window when - * rendering with OpenGL or OpenGL ES. If the swap interval is greater than - * zero, the GPU driver waits the specified number of screen updates before - * swapping the buffers. - * - * The specified window must have an OpenGL or OpenGL ES context. Specifying - * a window without a context will generate a @ref GLFW_NO_WINDOW_CONTEXT - * error. - * - * This function does not apply to Vulkan. If you are rendering with Vulkan, - * see `vkQueuePresentKHR` instead. + * This function swaps the front and back buffers of the specified window. If + * the swap interval is greater than zero, the GPU driver waits the specified + * number of screen updates before swapping the buffers. * * @param[in] window The window whose buffers to swap. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_NO_WINDOW_CONTEXT and @ref GLFW_PLATFORM_ERROR. - * - * @remark __EGL:__ The context of the specified window must be current on the - * calling thread. - * - * @thread_safety This function may be called from any thread. + * @par Thread Safety + * This function may be called from any thread. * * @sa @ref buffer_swap * @sa glfwSwapInterval * - * @since Added in version 1.0. - * @glfw3 Added window handle parameter. + * @since Added in GLFW 1.0. + * + * @par + * __GLFW 3:__ Added window handle parameter. * * @ingroup window */ @@ -3649,11 +3205,11 @@ GLFWAPI void glfwSwapBuffers(GLFWwindow* window); /*! @brief Sets the swap interval for the current context. * - * This function sets the swap interval for the current OpenGL or OpenGL ES - * context, i.e. the number of screen updates to wait from the time @ref - * glfwSwapBuffers was called before swapping the buffers and returning. This - * is sometimes called _vertical synchronization_, _vertical retrace - * synchronization_ or just _vsync_. + * This function sets the swap interval for the current context, i.e. the + * number of screen updates to wait from the time @ref glfwSwapBuffers was + * called before swapping the buffers and returning. This is sometimes called + * _vertical synchronization_, _vertical retrace synchronization_ or just + * _vsync_. * * Contexts that support either of the `WGL_EXT_swap_control_tear` and * `GLX_EXT_swap_control_tear` extensions also accept negative swap intervals, @@ -3665,30 +3221,25 @@ GLFWAPI void glfwSwapBuffers(GLFWwindow* window); * A context must be current on the calling thread. Calling this function * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error. * - * This function does not apply to Vulkan. If you are rendering with Vulkan, - * see the present mode of your swapchain instead. - * * @param[in] interval The minimum number of screen updates to wait for * until the buffers are swapped by @ref glfwSwapBuffers. * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_NO_CURRENT_CONTEXT and @ref GLFW_PLATFORM_ERROR. - * - * @remark This function is not called during context creation, leaving the + * @remarks This function is not called during context creation, leaving the * swap interval set to whatever is the default on that platform. This is done * because some swap interval extensions used by GLFW do not allow the swap * interval to be reset to zero once it has been set to a non-zero value. * - * @remark Some GPU drivers do not honor the requested swap interval, either + * @remarks Some GPU drivers do not honor the requested swap interval, either * because of a user setting that overrides the application's request or due to * bugs in the driver. * - * @thread_safety This function may be called from any thread. + * @par Thread Safety + * This function may be called from any thread. * * @sa @ref buffer_swap * @sa glfwSwapBuffers * - * @since Added in version 1.0. + * @since Added in GLFW 1.0. * * @ingroup context */ @@ -3697,9 +3248,9 @@ GLFWAPI void glfwSwapInterval(int interval); /*! @brief Returns whether the specified extension is available. * * This function returns whether the specified - * [API extension](@ref context_glext) is supported by the current OpenGL or - * OpenGL ES context. It searches both for client API extension and context - * creation API extensions. + * [client API extension](@ref context_glext) is supported by the current + * OpenGL or OpenGL ES context. It searches both for OpenGL and OpenGL ES + * extension and platform-specific context creation API extensions. * * A context must be current on the calling thread. Calling this function * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error. @@ -3709,24 +3260,16 @@ GLFWAPI void glfwSwapInterval(int interval); * frequently. The extension strings will not change during the lifetime of * a context, so there is no danger in doing this. * - * This function does not apply to Vulkan. If you are using Vulkan, see @ref - * glfwGetRequiredInstanceExtensions, `vkEnumerateInstanceExtensionProperties` - * and `vkEnumerateDeviceExtensionProperties` instead. - * * @param[in] extension The ASCII encoded name of the extension. - * @return `GLFW_TRUE` if the extension is available, or `GLFW_FALSE` - * otherwise. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_NO_CURRENT_CONTEXT, @ref GLFW_INVALID_VALUE and @ref - * GLFW_PLATFORM_ERROR. + * @return `GL_TRUE` if the extension is available, or `GL_FALSE` otherwise. * - * @thread_safety This function may be called from any thread. + * @par Thread Safety + * This function may be called from any thread. * * @sa @ref context_glext * @sa glfwGetProcAddress * - * @since Added in version 1.0. + * @since Added in GLFW 1.0. * * @ingroup context */ @@ -3735,243 +3278,40 @@ GLFWAPI int glfwExtensionSupported(const char* extension); /*! @brief Returns the address of the specified function for the current * context. * - * This function returns the address of the specified OpenGL or OpenGL ES + * This function returns the address of the specified * [core or extension function](@ref context_glext), if it is supported * by the current context. * * A context must be current on the calling thread. Calling this function * without a current context will cause a @ref GLFW_NO_CURRENT_CONTEXT error. * - * This function does not apply to Vulkan. If you are rendering with Vulkan, - * see @ref glfwGetInstanceProcAddress, `vkGetInstanceProcAddr` and - * `vkGetDeviceProcAddr` instead. - * * @param[in] procname The ASCII encoded name of the function. - * @return The address of the function, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_NO_CURRENT_CONTEXT and @ref GLFW_PLATFORM_ERROR. + * @return The address of the function, or `NULL` if the function is + * unavailable or an [error](@ref error_handling) occurred. * - * @remark The address of a given function is not guaranteed to be the same + * @remarks The addresses of a given function is not guaranteed to be the same * between contexts. * - * @remark This function may return a non-`NULL` address despite the + * @remarks This function may return a non-`NULL` address despite the * associated version or extension not being available. Always check the - * context version or extension string first. + * context version or extension string presence first. * - * @pointer_lifetime The returned function pointer is valid until the context - * is destroyed or the library is terminated. + * @par Pointer Lifetime + * The returned function pointer is valid until the context is destroyed or the + * library is terminated. * - * @thread_safety This function may be called from any thread. + * @par Thread Safety + * This function may be called from any thread. * * @sa @ref context_glext * @sa glfwExtensionSupported * - * @since Added in version 1.0. + * @since Added in GLFW 1.0. * * @ingroup context */ GLFWAPI GLFWglproc glfwGetProcAddress(const char* procname); -/*! @brief Returns whether the Vulkan loader has been found. - * - * This function returns whether the Vulkan loader has been found. This check - * is performed by @ref glfwInit. - * - * The availability of a Vulkan loader does not by itself guarantee that window - * surface creation or even device creation is possible. Call @ref - * glfwGetRequiredInstanceExtensions to check whether the extensions necessary - * for Vulkan surface creation are available and @ref - * glfwGetPhysicalDevicePresentationSupport to check whether a queue family of - * a physical device supports image presentation. - * - * @return `GLFW_TRUE` if Vulkan is available, or `GLFW_FALSE` otherwise. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. - * - * @thread_safety This function may be called from any thread. - * - * @sa @ref vulkan_support - * - * @since Added in version 3.2. - * - * @ingroup vulkan - */ -GLFWAPI int glfwVulkanSupported(void); - -/*! @brief Returns the Vulkan instance extensions required by GLFW. - * - * This function returns an array of names of Vulkan instance extensions required - * by GLFW for creating Vulkan surfaces for GLFW windows. If successful, the - * list will always contains `VK_KHR_surface`, so if you don't require any - * additional extensions you can pass this list directly to the - * `VkInstanceCreateInfo` struct. - * - * If Vulkan is not available on the machine, this function returns `NULL` and - * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported - * to check whether Vulkan is available. - * - * If Vulkan is available but no set of extensions allowing window surface - * creation was found, this function returns `NULL`. You may still use Vulkan - * for off-screen rendering and compute work. - * - * @param[out] count Where to store the number of extensions in the returned - * array. This is set to zero if an error occurred. - * @return An array of ASCII encoded extension names, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_API_UNAVAILABLE. - * - * @remarks Additional extensions may be required by future versions of GLFW. - * You should check if any extensions you wish to enable are already in the - * returned array, as it is an error to specify an extension more than once in - * the `VkInstanceCreateInfo` struct. - * - * @pointer_lifetime The returned array is allocated and freed by GLFW. You - * should not free it yourself. It is guaranteed to be valid only until the - * library is terminated. - * - * @thread_safety This function may be called from any thread. - * - * @sa @ref vulkan_ext - * @sa glfwCreateWindowSurface - * - * @since Added in version 3.2. - * - * @ingroup vulkan - */ -GLFWAPI const char** glfwGetRequiredInstanceExtensions(unsigned int* count); - -#if defined(VK_VERSION_1_0) - -/*! @brief Returns the address of the specified Vulkan instance function. - * - * This function returns the address of the specified Vulkan core or extension - * function for the specified instance. If instance is set to `NULL` it can - * return any function exported from the Vulkan loader, including at least the - * following functions: - * - * - `vkEnumerateInstanceExtensionProperties` - * - `vkEnumerateInstanceLayerProperties` - * - `vkCreateInstance` - * - `vkGetInstanceProcAddr` - * - * If Vulkan is not available on the machine, this function returns `NULL` and - * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported - * to check whether Vulkan is available. - * - * This function is equivalent to calling `vkGetInstanceProcAddr` with - * a platform-specific query of the Vulkan loader as a fallback. - * - * @param[in] instance The Vulkan instance to query, or `NULL` to retrieve - * functions related to instance creation. - * @param[in] procname The ASCII encoded name of the function. - * @return The address of the function, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref - * GLFW_API_UNAVAILABLE. - * - * @pointer_lifetime The returned function pointer is valid until the library - * is terminated. - * - * @thread_safety This function may be called from any thread. - * - * @sa @ref vulkan_proc - * - * @since Added in version 3.2. - * - * @ingroup vulkan - */ -GLFWAPI GLFWvkproc glfwGetInstanceProcAddress(VkInstance instance, const char* procname); - -/*! @brief Returns whether the specified queue family can present images. - * - * This function returns whether the specified queue family of the specified - * physical device supports presentation to the platform GLFW was built for. - * - * If Vulkan or the required window surface creation instance extensions are - * not available on the machine, or if the specified instance was not created - * with the required extensions, this function returns `GLFW_FALSE` and - * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref glfwVulkanSupported - * to check whether Vulkan is available and @ref - * glfwGetRequiredInstanceExtensions to check what instance extensions are - * required. - * - * @param[in] instance The instance that the physical device belongs to. - * @param[in] device The physical device that the queue family belongs to. - * @param[in] queuefamily The index of the queue family to query. - * @return `GLFW_TRUE` if the queue family supports presentation, or - * `GLFW_FALSE` otherwise. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_API_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR. - * - * @thread_safety This function may be called from any thread. For - * synchronization details of Vulkan objects, see the Vulkan specification. - * - * @sa @ref vulkan_present - * - * @since Added in version 3.2. - * - * @ingroup vulkan - */ -GLFWAPI int glfwGetPhysicalDevicePresentationSupport(VkInstance instance, VkPhysicalDevice device, uint32_t queuefamily); - -/*! @brief Creates a Vulkan surface for the specified window. - * - * This function creates a Vulkan surface for the specified window. - * - * If the Vulkan loader was not found at initialization, this function returns - * `VK_ERROR_INITIALIZATION_FAILED` and generates a @ref GLFW_API_UNAVAILABLE - * error. Call @ref glfwVulkanSupported to check whether the Vulkan loader was - * found. - * - * If the required window surface creation instance extensions are not - * available or if the specified instance was not created with these extensions - * enabled, this function returns `VK_ERROR_EXTENSION_NOT_PRESENT` and - * generates a @ref GLFW_API_UNAVAILABLE error. Call @ref - * glfwGetRequiredInstanceExtensions to check what instance extensions are - * required. - * - * The window surface must be destroyed before the specified Vulkan instance. - * It is the responsibility of the caller to destroy the window surface. GLFW - * does not destroy it for you. Call `vkDestroySurfaceKHR` to destroy the - * surface. - * - * @param[in] instance The Vulkan instance to create the surface in. - * @param[in] window The window to create the surface for. - * @param[in] allocator The allocator to use, or `NULL` to use the default - * allocator. - * @param[out] surface Where to store the handle of the surface. This is set - * to `VK_NULL_HANDLE` if an error occurred. - * @return `VK_SUCCESS` if successful, or a Vulkan error code if an - * [error](@ref error_handling) occurred. - * - * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref - * GLFW_API_UNAVAILABLE and @ref GLFW_PLATFORM_ERROR. - * - * @remarks If an error occurs before the creation call is made, GLFW returns - * the Vulkan error code most appropriate for the error. Appropriate use of - * @ref glfwVulkanSupported and @ref glfwGetRequiredInstanceExtensions should - * elminiate almost all occurences of these errors. - * - * @thread_safety This function may be called from any thread. For - * synchronization details of Vulkan objects, see the Vulkan specification. - * - * @sa @ref vulkan_surface - * @sa glfwGetRequiredInstanceExtensions - * - * @since Added in version 3.2. - * - * @ingroup vulkan - */ -GLFWAPI VkResult glfwCreateWindowSurface(VkInstance instance, GLFWwindow* window, const VkAllocationCallbacks* allocator, VkSurfaceKHR* surface); - -#endif /*VK_VERSION_1_0*/ - /************************************************************************* * Global definition cleanup diff --git a/nuparu/include/GLFW/glfw3native.h b/nuparu/include/GLFW/glfw3native.h index 9fa955e9..b3ce7482 100644 --- a/nuparu/include/GLFW/glfw3native.h +++ b/nuparu/include/GLFW/glfw3native.h @@ -1,5 +1,5 @@ /************************************************************************* - * GLFW 3.2 - www.glfw.org + * GLFW 3.1 - www.glfw.org * A library for OpenGL, window and input *------------------------------------------------------------------------ * Copyright (c) 2002-2006 Marcus Geelnard @@ -38,30 +38,20 @@ extern "C" { * Doxygen documentation *************************************************************************/ -/*! @file glfw3native.h - * @brief The header of the native access functions. - * - * This is the header file of the native access functions. See @ref native for - * more information. - */ /*! @defgroup native Native access * * **By using the native access functions you assert that you know what you're * doing and how to fix problems caused by using them. If you don't, you * shouldn't be using them.** * - * Before the inclusion of @ref glfw3native.h, you may define exactly one - * window system API macro and zero or more context creation API macros. - * - * The chosen backends must match those the library was compiled for. Failure - * to do this will cause a link-time error. + * Before the inclusion of @ref glfw3native.h, you must define exactly one + * window system API macro and exactly one context creation API macro. Failure + * to do this will cause a compile-time error. * * The available window API macros are: * * `GLFW_EXPOSE_NATIVE_WIN32` * * `GLFW_EXPOSE_NATIVE_COCOA` * * `GLFW_EXPOSE_NATIVE_X11` - * * `GLFW_EXPOSE_NATIVE_WAYLAND` - * * `GLFW_EXPOSE_NATIVE_MIR` * * The available context API macros are: * * `GLFW_EXPOSE_NATIVE_WGL` @@ -96,23 +86,20 @@ extern "C" { #elif defined(GLFW_EXPOSE_NATIVE_X11) #include #include -#elif defined(GLFW_EXPOSE_NATIVE_WAYLAND) - #include -#elif defined(GLFW_EXPOSE_NATIVE_MIR) - #include +#else + #error "No window API selected" #endif #if defined(GLFW_EXPOSE_NATIVE_WGL) /* WGL is declared by windows.h */ -#endif -#if defined(GLFW_EXPOSE_NATIVE_NSGL) +#elif defined(GLFW_EXPOSE_NATIVE_NSGL) /* NSGL is declared by Cocoa.h */ -#endif -#if defined(GLFW_EXPOSE_NATIVE_GLX) +#elif defined(GLFW_EXPOSE_NATIVE_GLX) #include -#endif -#if defined(GLFW_EXPOSE_NATIVE_EGL) +#elif defined(GLFW_EXPOSE_NATIVE_EGL) #include +#else + #error "No context API selected" #endif @@ -127,10 +114,11 @@ extern "C" { * of the specified monitor, or `NULL` if an [error](@ref error_handling) * occurred. * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. + * @par Thread Safety + * This function may be called from any thread. Access is not synchronized. * - * @since Added in version 3.1. + * @par History + * Added in GLFW 3.1. * * @ingroup native */ @@ -142,10 +130,11 @@ GLFWAPI const char* glfwGetWin32Adapter(GLFWmonitor* monitor); * `\\.\DISPLAY1\Monitor0`) of the specified monitor, or `NULL` if an * [error](@ref error_handling) occurred. * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. + * @par Thread Safety + * This function may be called from any thread. Access is not synchronized. * - * @since Added in version 3.1. + * @par History + * Added in GLFW 3.1. * * @ingroup native */ @@ -156,10 +145,11 @@ GLFWAPI const char* glfwGetWin32Monitor(GLFWmonitor* monitor); * @return The `HWND` of the specified window, or `NULL` if an * [error](@ref error_handling) occurred. * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. + * @par Thread Safety + * This function may be called from any thread. Access is not synchronized. * - * @since Added in version 3.0. + * @par History + * Added in GLFW 3.0. * * @ingroup native */ @@ -172,10 +162,11 @@ GLFWAPI HWND glfwGetWin32Window(GLFWwindow* window); * @return The `HGLRC` of the specified window, or `NULL` if an * [error](@ref error_handling) occurred. * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. + * @par Thread Safety + * This function may be called from any thread. Access is not synchronized. * - * @since Added in version 3.0. + * @par History + * Added in GLFW 3.0. * * @ingroup native */ @@ -188,10 +179,11 @@ GLFWAPI HGLRC glfwGetWGLContext(GLFWwindow* window); * @return The `CGDirectDisplayID` of the specified monitor, or * `kCGNullDirectDisplay` if an [error](@ref error_handling) occurred. * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. + * @par Thread Safety + * This function may be called from any thread. Access is not synchronized. * - * @since Added in version 3.1. + * @par History + * Added in GLFW 3.1. * * @ingroup native */ @@ -202,10 +194,11 @@ GLFWAPI CGDirectDisplayID glfwGetCocoaMonitor(GLFWmonitor* monitor); * @return The `NSWindow` of the specified window, or `nil` if an * [error](@ref error_handling) occurred. * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. + * @par Thread Safety + * This function may be called from any thread. Access is not synchronized. * - * @since Added in version 3.0. + * @par History + * Added in GLFW 3.0. * * @ingroup native */ @@ -218,10 +211,11 @@ GLFWAPI id glfwGetCocoaWindow(GLFWwindow* window); * @return The `NSOpenGLContext` of the specified window, or `nil` if an * [error](@ref error_handling) occurred. * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. + * @par Thread Safety + * This function may be called from any thread. Access is not synchronized. * - * @since Added in version 3.0. + * @par History + * Added in GLFW 3.0. * * @ingroup native */ @@ -234,10 +228,11 @@ GLFWAPI id glfwGetNSGLContext(GLFWwindow* window); * @return The `Display` used by GLFW, or `NULL` if an * [error](@ref error_handling) occurred. * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. + * @par Thread Safety + * This function may be called from any thread. Access is not synchronized. * - * @since Added in version 3.0. + * @par History + * Added in GLFW 3.0. * * @ingroup native */ @@ -248,10 +243,11 @@ GLFWAPI Display* glfwGetX11Display(void); * @return The `RRCrtc` of the specified monitor, or `None` if an * [error](@ref error_handling) occurred. * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. + * @par Thread Safety + * This function may be called from any thread. Access is not synchronized. * - * @since Added in version 3.1. + * @par History + * Added in GLFW 3.1. * * @ingroup native */ @@ -262,10 +258,11 @@ GLFWAPI RRCrtc glfwGetX11Adapter(GLFWmonitor* monitor); * @return The `RROutput` of the specified monitor, or `None` if an * [error](@ref error_handling) occurred. * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. + * @par Thread Safety + * This function may be called from any thread. Access is not synchronized. * - * @since Added in version 3.1. + * @par History + * Added in GLFW 3.1. * * @ingroup native */ @@ -276,10 +273,11 @@ GLFWAPI RROutput glfwGetX11Monitor(GLFWmonitor* monitor); * @return The `Window` of the specified window, or `None` if an * [error](@ref error_handling) occurred. * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. + * @par Thread Safety + * This function may be called from any thread. Access is not synchronized. * - * @since Added in version 3.0. + * @par History + * Added in GLFW 3.0. * * @ingroup native */ @@ -292,116 +290,15 @@ GLFWAPI Window glfwGetX11Window(GLFWwindow* window); * @return The `GLXContext` of the specified window, or `NULL` if an * [error](@ref error_handling) occurred. * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. + * @par Thread Safety + * This function may be called from any thread. Access is not synchronized. * - * @since Added in version 3.0. + * @par History + * Added in GLFW 3.0. * * @ingroup native */ GLFWAPI GLXContext glfwGetGLXContext(GLFWwindow* window); - -/*! @brief Returns the `GLXWindow` of the specified window. - * - * @return The `GLXWindow` of the specified window, or `None` if an - * [error](@ref error_handling) occurred. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.2. - * - * @ingroup native - */ -GLFWAPI GLXWindow glfwGetGLXWindow(GLFWwindow* window); -#endif - -#if defined(GLFW_EXPOSE_NATIVE_WAYLAND) -/*! @brief Returns the `struct wl_display*` used by GLFW. - * - * @return The `struct wl_display*` used by GLFW, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.2. - * - * @ingroup native - */ -GLFWAPI struct wl_display* glfwGetWaylandDisplay(void); - -/*! @brief Returns the `struct wl_output*` of the specified monitor. - * - * @return The `struct wl_output*` of the specified monitor, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.2. - * - * @ingroup native - */ -GLFWAPI struct wl_output* glfwGetWaylandMonitor(GLFWmonitor* monitor); - -/*! @brief Returns the main `struct wl_surface*` of the specified window. - * - * @return The main `struct wl_surface*` of the specified window, or `NULL` if - * an [error](@ref error_handling) occurred. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.2. - * - * @ingroup native - */ -GLFWAPI struct wl_surface* glfwGetWaylandWindow(GLFWwindow* window); -#endif - -#if defined(GLFW_EXPOSE_NATIVE_MIR) -/*! @brief Returns the `MirConnection*` used by GLFW. - * - * @return The `MirConnection*` used by GLFW, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.2. - * - * @ingroup native - */ -GLFWAPI MirConnection* glfwGetMirDisplay(void); - -/*! @brief Returns the Mir output ID of the specified monitor. - * - * @return The Mir output ID of the specified monitor, or zero if an - * [error](@ref error_handling) occurred. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.2. - * - * @ingroup native - */ -GLFWAPI int glfwGetMirMonitor(GLFWmonitor* monitor); - -/*! @brief Returns the `MirSurface*` of the specified window. - * - * @return The `MirSurface*` of the specified window, or `NULL` if an - * [error](@ref error_handling) occurred. - * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. - * - * @since Added in version 3.2. - * - * @ingroup native - */ -GLFWAPI MirSurface* glfwGetMirWindow(GLFWwindow* window); #endif #if defined(GLFW_EXPOSE_NATIVE_EGL) @@ -410,10 +307,11 @@ GLFWAPI MirSurface* glfwGetMirWindow(GLFWwindow* window); * @return The `EGLDisplay` used by GLFW, or `EGL_NO_DISPLAY` if an * [error](@ref error_handling) occurred. * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. + * @par Thread Safety + * This function may be called from any thread. Access is not synchronized. * - * @since Added in version 3.0. + * @par History + * Added in GLFW 3.0. * * @ingroup native */ @@ -424,10 +322,11 @@ GLFWAPI EGLDisplay glfwGetEGLDisplay(void); * @return The `EGLContext` of the specified window, or `EGL_NO_CONTEXT` if an * [error](@ref error_handling) occurred. * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. + * @par Thread Safety + * This function may be called from any thread. Access is not synchronized. * - * @since Added in version 3.0. + * @par History + * Added in GLFW 3.0. * * @ingroup native */ @@ -438,10 +337,11 @@ GLFWAPI EGLContext glfwGetEGLContext(GLFWwindow* window); * @return The `EGLSurface` of the specified window, or `EGL_NO_SURFACE` if an * [error](@ref error_handling) occurred. * - * @thread_safety This function may be called from any thread. Access is not - * synchronized. + * @par Thread Safety + * This function may be called from any thread. Access is not synchronized. * - * @since Added in version 3.0. + * @par History + * Added in GLFW 3.0. * * @ingroup native */ diff --git a/src/main.cpp b/src/main.cpp index af31e58a..4c8cead8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,6 @@ #include -#include "main.hpp" -#include "viewer.h" +#include using namespace std; diff --git a/src/main.hpp b/src/main.hpp deleted file mode 100644 index fe1f64d5..00000000 --- a/src/main.hpp +++ /dev/null @@ -1,9 +0,0 @@ -// -// main.hpp -// Thanda - -#ifndef main_hpp -#define main_hpp - -#endif /* main_hpp */ - From a16f49a2fd333805223602b22b9a432c3776c9b3 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Tue, 1 Mar 2016 00:59:36 -0500 Subject: [PATCH 26/64] Updated README --- README.md | 112 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 97 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 5a5dfc3a..980af87a 100644 --- a/README.md +++ b/README.md @@ -1,25 +1,107 @@ -# CIS563-FluidSolver +# Korra Fluid Solver, CIS 563, University of Pennsylvania, Spring 2016 -FluidSolver Submission guidelines: +## Overview -(Borrowed from CIS565 README) +__Korra__ is a cross-platform C++ fluid FLIP solver, designed and built for CIS563 course. __Korra__ uses OpenGL heavily for the graphics pipeline. It is also designed for GPU optimization for fluid advection. -If you have modified any of the CMakeLists.txt files at all (aside from the list of CORE_SRC), you must test that your project can build. Beware of any build issues. +Details will be updated as more features are coming soon. -Open a GitHub pull request so that we can see that you have finished. The title should be "Submission: YOUR NAME". +## Classes -In the body of the pull request, include a link to your repository. +The following are classes for the fluid solver. -Submit on canvas with a direct link to your pull request on GitHub +### Viewer +The _Viewer_ class contains all the OpenGL, glfw, glew, shader programs, and scene initialization. Once the the initialization is completed, the _Viewer_ kicks the simulation loop. The loop can be canceled by hitting the ESC key. -And you're done! +- __Note__: By defining **#define TEST_SCENE** in `viewer.h` and `scene.h`, the test scene would be used instead. -# Third Party +### Scene -## CIS277 Camera code -## glfw, [Website](http://www.glfw.org/) -## glew, [Website](http://glew.sourceforge.net/) -## glm, [Website](http://glm.g-truc.net/0.9.7/index.html) -## opengl-tutorial.org, [Website](http://www.opengl-tutorial.org/) -## jsoncpp, [Github](https://github.com/open-source-parsers/jsoncpp) +The _Scene_ class contains the _Camera_ and all other objects in the scene. The _Scene_ class takes in a json file in the `scene/` directory for initializing the fluid solver. In this case, it's the `scene.json` file. + +There are three objects of our interests: + +- *m_fluidContainer*, a _Box_ object that specifies the boundary of the fluid. +- *m_fluidGeo*, a _FluidGeo_ object that specifies the particles of the fluid. +- *m_fluidSolver*, a _FluidSolver_ object that gets updated every simulation frame to compute the fluid state. + +- __Note__: By defining **#define TEST_SCENE** in `viewer.h` and `scene.h`, the test scene would be used instead. + +### ShaderProgram + +The _ShaderProgram_ compiles the `glsl/simple.vert` vertex shader and the `glsl/simple.frag`. This program is used to draw simple geometry without any special shading. + +### ParticleAdvectProgram + +The _ParticleAdvectProgram_ compiles the `glsl/particle_advect.vert`, `glsl/particle_advect.frag`, `glsl/particle_draw.frag`, and `glsl/particle_draw.frag` into 2 programs: `m_programAdvect` and `m_programDraw`. + +This class is used to perform two render passes for transform feedback. In short, the first pass uses the `particle_advect.vert` to update particle positions but skipping the rasterization phase, passing its vertex attributes directly to the `partice_draw.vert` and `particle_draw.frag` for the actual drawing. This approaches allow us to parallelize the particle update using only the shaders. + +### Camera + +The _Camera_ class handles the view matrix. The _Camera_ class uses the _KeyboardControl_ class to handles user keyboard input. To control the _Camera_ movement, uses: + +- **W**: Pan up +- **S**: Pan down +- **A**: Pan left +- **D**: Pan right +- **Up**: Look up +- **Down**: Look down +- **Left**: Look left +- **Right**: Look right + +### Geometry + +The _Geometry_ is the base class for all the drawable geometry classes in a scene. It is not meant to be instanced directly but to be derived by new classes (etc. _Box_ and _FluidGeo_). _Geometry_ class contains all the information for transformations, vertex buffers, vertex arrays, index buffer, and drawing mode. + +Each new geometry instanced **NEEDS** to call Create() in order to properly initialize its data. + +This class supports the basic affine transformation: + +- Translate +- Rotate +- Scale + +This class supports three drawing mode: + +- Wireframe (GL_LINES) +- Shaded (GL_TRIANGLES) +- Vertex (GL_POINTS) + +### Box + +_Box_ is derived from _Geometry_. _Box_ represents a simple box geometry. + +### FluiGeo + +_FluiGeo_ is derived from _Geometry_. FluiGeo represents a collection of fluid particles. _FluidGeo_ also contains the particle positions and velocities. + +### FluidSolver + +_FluidSolver_ initializes all the particle information such as container boundary and particle separation. Its `Update()` function is called each frame to update the solver if needed. + +### KeyboardControl + +_KeyboardControl_ handles user keyboard input. + +## Code flow + +Starting with `main.cpp`, the _Viewer_ is initialized, then the simulation loop kicks in. For each frame, the _Scene_ calls its `Update()` and `Draw()` functions to update the state of the solver then render the result. + +## Extra features + +N/A + +## Camera control + +See Camera class section above. + +## Third Party + +- CIS277, Camera code +- glfw, [Website](http://www.glfw.org/) +- glew, [Website](http://glew.sourceforge.net/) +- glm, [Website](http://glm.g-truc.net/0.9.7/index.html) +- opengl-tutorial.org, [Website](http://www.opengl-tutorial.org/) +- jsoncpp, [Github](https://github.com/open-source-parsers/jsoncpp) From d83583dfc09ac103f47692c60e442fd6b60b4f62 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Mon, 21 Mar 2016 11:07:30 -0400 Subject: [PATCH 27/64] Update gitignore. Update vertex shader --- .gitignore | 1 + src/glsl/particle_draw.vert | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 0effb0b4..f534d84a 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ CMakeCache.txt cmake_install.cmake CMakeLists.txt.user build*/ +*.sublime* diff --git a/src/glsl/particle_draw.vert b/src/glsl/particle_draw.vert index 9a4b8c0e..013a8f9d 100644 --- a/src/glsl/particle_draw.vert +++ b/src/glsl/particle_draw.vert @@ -21,9 +21,9 @@ void main() { float r = rand(vec2(a_position.y, gl_VertexID)); vec3 position = vec3( - a_position.x * r * (u_time * u_time), - a_position.y - (u_time * u_time * u_time) + r * u_time * u_time * u_time * u_time * -abs(sin(a_position.x)), - a_position.z * r * (u_time * u_time) + a_position.x, + a_position.y - (u_time * u_time * u_time * u_time), + a_position.z ); gl_Position = u_viewProj * u_model * vec4(position, 1.0); v_position = position; From 8a80f069aa849f98a463904ac6ab0c24182163c4 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Tue, 22 Mar 2016 18:08:52 -0400 Subject: [PATCH 28/64] Updated to latest version of nuparu --- nuparu/include/json/config.h | 78 ++++++++++----------------------- nuparu/include/json/features.h | 6 --- nuparu/include/json/reader.h | 53 ++-------------------- nuparu/include/json/value.h | 37 +++++----------- nuparu/include/json/version.h | 8 ++-- nuparu/include/json/writer.h | 33 ++++---------- nuparu/lib/osx/libglfw3.a | Bin 176728 -> 169936 bytes 7 files changed, 48 insertions(+), 167 deletions(-) diff --git a/nuparu/include/json/config.h b/nuparu/include/json/config.h index 51027dd8..0dcd8ae6 100644 --- a/nuparu/include/json/config.h +++ b/nuparu/include/json/config.h @@ -5,7 +5,6 @@ #ifndef JSON_CONFIG_H_INCLUDED #define JSON_CONFIG_H_INCLUDED -#include /// If defined, indicates that json library is embedded in CppTL library. //# define JSON_IN_CPPTL 1 @@ -57,67 +56,34 @@ // Storages, and 64 bits integer support is disabled. // #define JSON_NO_INT64 1 -#if defined(_MSC_VER) // MSVC -# if _MSC_VER <= 1200 // MSVC 6 - // Microsoft Visual Studio 6 only support conversion from __int64 to double - // (no conversion from unsigned __int64). -# define JSON_USE_INT64_DOUBLE_CONVERSION 1 - // Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255' - // characters in the debug information) - // All projects I've ever seen with VS6 were using this globally (not bothering - // with pragma push/pop). -# pragma warning(disable : 4786) -# endif // MSVC 6 - -# if _MSC_VER >= 1500 // MSVC 2008 - /// Indicates that the following function is deprecated. -# define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) -# endif - -#endif // defined(_MSC_VER) - - -#ifndef JSON_HAS_RVALUE_REFERENCES - -#if defined(_MSC_VER) && _MSC_VER >= 1600 // MSVC >= 2010 -#define JSON_HAS_RVALUE_REFERENCES 1 -#endif // MSVC >= 2010 - -#ifdef __clang__ -#if __has_feature(cxx_rvalue_references) -#define JSON_HAS_RVALUE_REFERENCES 1 -#endif // has_feature - -#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) -#if defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus >= 201103L) -#define JSON_HAS_RVALUE_REFERENCES 1 -#endif // GXX_EXPERIMENTAL - -#endif // __clang__ || __GNUC__ - -#endif // not defined JSON_HAS_RVALUE_REFERENCES - -#ifndef JSON_HAS_RVALUE_REFERENCES -#define JSON_HAS_RVALUE_REFERENCES 0 +#if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC 6 +// Microsoft Visual Studio 6 only support conversion from __int64 to double +// (no conversion from unsigned __int64). +#define JSON_USE_INT64_DOUBLE_CONVERSION 1 +// Disable warning 4786 for VS6 caused by STL (identifier was truncated to '255' +// characters in the debug information) +// All projects I've ever seen with VS6 were using this globally (not bothering +// with pragma push/pop). +#pragma warning(disable : 4786) +#endif // if defined(_MSC_VER) && _MSC_VER < 1200 // MSVC 6 + +#if defined(_MSC_VER) && _MSC_VER >= 1500 // MSVC 2008 +/// Indicates that the following function is deprecated. +#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message)) +#elif defined(__clang__) && defined(__has_feature) +#if __has_feature(attribute_deprecated_with_message) +#define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) +#endif +#elif defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) +#define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) +#elif defined(__GNUC__) && (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) +#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) #endif - -#ifdef __clang__ -#elif defined __GNUC__ // not clang (gcc comes later since clang emulates gcc) -# if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) -# define JSONCPP_DEPRECATED(message) __attribute__ ((deprecated(message))) -# elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) -# define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__)) -# endif // GNUC version -#endif // __clang__ || __GNUC__ #if !defined(JSONCPP_DEPRECATED) #define JSONCPP_DEPRECATED(message) #endif // if !defined(JSONCPP_DEPRECATED) -#if __GNUC__ >= 6 -# define JSON_USE_INT64_DOUBLE_CONVERSION 1 -#endif - namespace Json { typedef int Int; typedef unsigned int UInt; diff --git a/nuparu/include/json/features.h b/nuparu/include/json/features.h index 1bb7bb61..78135478 100644 --- a/nuparu/include/json/features.h +++ b/nuparu/include/json/features.h @@ -44,12 +44,6 @@ class JSON_API Features { /// \c true if root must be either an array or an object value. Default: \c /// false. bool strictRoot_; - - /// \c true if dropped null placeholders are allowed. Default: \c false. - bool allowDroppedNullPlaceholders_; - - /// \c true if numeric object key are allowed. Default: \c false. - bool allowNumericKeys_; }; } // namespace Json diff --git a/nuparu/include/json/reader.h b/nuparu/include/json/reader.h index 70d9505b..e1bd6296 100644 --- a/nuparu/include/json/reader.h +++ b/nuparu/include/json/reader.h @@ -35,18 +35,6 @@ class JSON_API Reader { typedef char Char; typedef const Char* Location; - /** \brief An error tagged with where in the JSON text it was encountered. - * - * The offsets give the [start, limit) range of bytes within the text. Note - * that this is bytes, not codepoints. - * - */ - struct StructuredError { - ptrdiff_t offset_start; - ptrdiff_t offset_limit; - std::string message; - }; - /** \brief Constructs a Reader allowing all features * for parsing. */ @@ -123,38 +111,6 @@ class JSON_API Reader { */ std::string getFormattedErrorMessages() const; - /** \brief Returns a vector of structured erros encounted while parsing. - * \return A (possibly empty) vector of StructuredError objects. Currently - * only one error can be returned, but the caller should tolerate - * multiple - * errors. This can occur if the parser recovers from a non-fatal - * parse error and then encounters additional errors. - */ - std::vector getStructuredErrors() const; - - /** \brief Add a semantic error message. - * \param value JSON Value location associated with the error - * \param message The error message. - * \return \c true if the error was successfully added, \c false if the - * Value offset exceeds the document size. - */ - bool pushError(const Value& value, const std::string& message); - - /** \brief Add a semantic error message with extra context. - * \param value JSON Value location associated with the error - * \param message The error message. - * \param extra Additional JSON Value location to contextualize the error - * \return \c true if the error was successfully added, \c false if either - * Value offset exceeds the document size. - */ - bool pushError(const Value& value, const std::string& message, const Value& extra); - - /** \brief Return whether there are any errors. - * \return \c true if there are no errors to report \c false if - * errors have occurred. - */ - bool good() const; - private: enum TokenType { tokenEndOfStream = 0, @@ -268,7 +224,7 @@ class JSON_API CharReader { char const* beginDoc, char const* endDoc, Value* root, std::string* errs) = 0; - class JSON_API Factory { + class Factory { public: virtual ~Factory() {} /** \brief Allocate a CharReader via operator new(). @@ -321,9 +277,6 @@ class JSON_API CharReaderBuilder : public CharReader::Factory { the JSON value in the input string. - `"rejectDupKeys": false or true` - If true, `parse()` returns false when a key is duplicated within an object. - - `"allowSpecialFloats": false or true` - - If true, special float values (NaNs and infinities) are allowed - and their values are lossfree restorable. You can examine 'settings_` yourself to see the defaults. You can also write and read them just like any @@ -333,9 +286,9 @@ class JSON_API CharReaderBuilder : public CharReader::Factory { Json::Value settings_; CharReaderBuilder(); - ~CharReaderBuilder() override; + virtual ~CharReaderBuilder(); - CharReader* newCharReader() const override; + virtual CharReader* newCharReader() const; /** \return true if 'settings' are legal and consistent; * otherwise, indicate bad settings via 'invalid'. diff --git a/nuparu/include/json/value.h b/nuparu/include/json/value.h index b3f1ceb8..fe55bfde 100644 --- a/nuparu/include/json/value.h +++ b/nuparu/include/json/value.h @@ -40,10 +40,10 @@ namespace Json { class JSON_API Exception : public std::exception { public: Exception(std::string const& msg); - ~Exception() throw() override; - char const* what() const throw() override; + virtual ~Exception() throw(); + virtual char const* what() const throw(); protected: - std::string msg_; + std::string const msg_; }; /** Exceptions which the user cannot easily avoid. @@ -175,8 +175,11 @@ class JSON_API Value { typedef Json::LargestUInt LargestUInt; typedef Json::ArrayIndex ArrayIndex; - static const Value& null; ///< We regret this reference to a global instance; prefer the simpler Value(). - static const Value& nullRef; ///< just a kludge for binary-compatibility; same as null + static const Value& nullRef; +#if !defined(__ARMEL__) + /// \deprecated This exists for binary compatibility only. Use nullRef. + static const Value null; +#endif /// Minimum signed integer value that can be stored in a Json::Value. static const LargestInt minLargestInt; /// Maximum signed integer value that can be stored in a Json::Value. @@ -212,9 +215,6 @@ class JSON_API Value { CZString(ArrayIndex index); CZString(char const* str, unsigned length, DuplicationPolicy allocate); CZString(CZString const& other); -#if JSON_HAS_RVALUE_REFERENCES - CZString(CZString&& other); -#endif ~CZString(); CZString& operator=(CZString other); bool operator<(CZString const& other) const; @@ -297,15 +297,11 @@ Json::Value obj_value(Json::objectValue); // {} Value(bool value); /// Deep copy. Value(const Value& other); -#if JSON_HAS_RVALUE_REFERENCES - /// Move constructor - Value(Value&& other); -#endif ~Value(); /// Deep copy, then swap(other). /// \note Over-write existing comments. To preserve comments, use #swapPayload(). - Value& operator=(Value other); + Value &operator=(const Value &other); /// Swap everything. void swap(Value& other); /// Swap values but leave comments and source offsets in place. @@ -552,13 +548,6 @@ Json::Value obj_value(Json::objectValue); // {} iterator begin(); iterator end(); - // Accessors for the [start, limit) range of bytes within the JSON text from - // which this value was parsed, if any. - void setOffsetStart(ptrdiff_t start); - void setOffsetLimit(ptrdiff_t limit); - ptrdiff_t getOffsetStart() const; - ptrdiff_t getOffsetLimit() const; - private: void initBasic(ValueType type, bool allocated = false); @@ -595,11 +584,6 @@ Json::Value obj_value(Json::objectValue); // {} unsigned int allocated_ : 1; // Notes: if declared as bool, bitfield is useless. // If not allocated_, string_ must be null-terminated. CommentInfo* comments_; - - // [start, limit) byte offsets in the source JSON text from which this Value - // was extracted. - ptrdiff_t start_; - ptrdiff_t limit_; }; /** \brief Experimental and untested: represents an element of the "path" to @@ -745,7 +729,6 @@ class JSON_API ValueConstIterator : public ValueIteratorBase { typedef ValueConstIterator SelfType; ValueConstIterator(); - ValueConstIterator(ValueIterator const& other); private: /*! \internal Use by Value to create an iterator. @@ -795,7 +778,7 @@ class JSON_API ValueIterator : public ValueIteratorBase { typedef ValueIterator SelfType; ValueIterator(); - explicit ValueIterator(const ValueConstIterator& other); + ValueIterator(const ValueConstIterator& other); ValueIterator(const ValueIterator& other); private: diff --git a/nuparu/include/json/version.h b/nuparu/include/json/version.h index 6c570a1e..087fc638 100644 --- a/nuparu/include/json/version.h +++ b/nuparu/include/json/version.h @@ -3,10 +3,10 @@ #ifndef JSON_VERSION_H_INCLUDED # define JSON_VERSION_H_INCLUDED -# define JSONCPP_VERSION_STRING "1.7.0" -# define JSONCPP_VERSION_MAJOR 1 -# define JSONCPP_VERSION_MINOR 7 -# define JSONCPP_VERSION_PATCH 0 +# define JSONCPP_VERSION_STRING "0.10.5" +# define JSONCPP_VERSION_MAJOR 0 +# define JSONCPP_VERSION_MINOR 10 +# define JSONCPP_VERSION_PATCH 5 # define JSONCPP_VERSION_QUALIFIER # define JSONCPP_VERSION_HEXA ((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | (JSONCPP_VERSION_PATCH << 8)) diff --git a/nuparu/include/json/writer.h b/nuparu/include/json/writer.h index 49b15124..2da72b68 100644 --- a/nuparu/include/json/writer.h +++ b/nuparu/include/json/writer.h @@ -99,10 +99,6 @@ class JSON_API StreamWriterBuilder : public StreamWriter::Factory { Strictly speaking, this is not valid JSON. But when the output is being fed to a browser's Javascript, it makes for smaller output and the browser can handle the output just fine. - - "useSpecialFloats": false or true - - If true, outputs non-finite floating point values in the following way: - NaN values as "NaN", positive infinity as "Infinity", and negative infinity - as "-Infinity". You can examine 'settings_` yourself to see the defaults. You can also write and read them just like any @@ -112,12 +108,12 @@ class JSON_API StreamWriterBuilder : public StreamWriter::Factory { Json::Value settings_; StreamWriterBuilder(); - ~StreamWriterBuilder() override; + virtual ~StreamWriterBuilder(); /** * \throw std::exception if something goes wrong (e.g. invalid settings) */ - StreamWriter* newStreamWriter() const override; + virtual StreamWriter* newStreamWriter() const; /** \return true if 'settings' are legal and consistent; * otherwise, indicate bad settings via 'invalid'. @@ -158,29 +154,18 @@ class JSON_API FastWriter : public Writer { public: FastWriter(); - ~FastWriter() override {} + virtual ~FastWriter() {} void enableYAMLCompatibility(); - /** \brief Drop the "null" string from the writer's output for nullValues. - * Strictly speaking, this is not valid JSON. But when the output is being - * fed to a browser's Javascript, it makes for smaller output and the - * browser can handle the output just fine. - */ - void dropNullPlaceholders(); - - void omitEndingLineFeed(); - public: // overridden from Writer - std::string write(const Value& root) override; + virtual std::string write(const Value& root); private: void writeValue(const Value& value); std::string document_; bool yamlCompatiblityEnabled_; - bool dropNullPlaceholders_; - bool omitEndingLineFeed_; }; /** \brief Writes a Value in JSON format in a @@ -210,14 +195,14 @@ class JSON_API FastWriter : public Writer { class JSON_API StyledWriter : public Writer { public: StyledWriter(); - ~StyledWriter() override {} + virtual ~StyledWriter() {} public: // overridden from Writer /** \brief Serialize a Value in JSON format. * \param root Value to serialize. * \return String containing the JSON document that represents the root value. */ - std::string write(const Value& root) override; + virtual std::string write(const Value& root); private: void writeValue(const Value& value); @@ -238,8 +223,8 @@ class JSON_API StyledWriter : public Writer { ChildValues childValues_; std::string document_; std::string indentString_; - unsigned int rightMargin_; - unsigned int indentSize_; + int rightMargin_; + int indentSize_; bool addChildValues_; }; @@ -302,7 +287,7 @@ class JSON_API StyledStreamWriter { ChildValues childValues_; std::ostream* document_; std::string indentString_; - unsigned int rightMargin_; + int rightMargin_; std::string indentation_; bool addChildValues_ : 1; bool indented_ : 1; diff --git a/nuparu/lib/osx/libglfw3.a b/nuparu/lib/osx/libglfw3.a index 48d246458190450cfc685b080841c9900a15be8d..92ac6608d345b0b79def5a7efbeb5627791b1004 100644 GIT binary patch literal 169936 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

%9@&YpDg+AH$d(QucZ^xy*q*CbR2ytPS0U^jr4Y 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

xrZ^^(;V(X~GAR71 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 literal 176728 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 Date: Wed, 23 Mar 2016 02:43:56 -0400 Subject: [PATCH 29/64] Added SPHSolver. Updated color through vertex attribute. Added kernel function --- CMakeLists.txt | 1 + src/fluidSolver/SPHSolver.cpp | 75 +++++++++++++++++++++ src/fluidSolver/SPHSolver.h | 74 ++++++++++++++++++++ src/fluidSolver/fluidConstants.h | 5 ++ src/fluidSolver/fluidSolver.cpp | 20 +++++- src/fluidSolver/fluidSolver.h | 13 +++- src/fluidSolver/kernels.h | 26 +++++++ src/geometry/fluidGeo.cpp | 45 ++++++++++--- src/geometry/fluidGeo.h | 6 +- src/geometry/geometry.h | 20 +++--- src/glsl/particle_advect.vert | 13 +++- src/glsl/particle_draw.frag | 4 +- src/glsl/particle_draw.vert | 14 ++-- src/scene/scene.cpp | 7 +- src/scene/scene.h | 4 +- src/shaderProgram/particleAdvectProgram.cpp | 16 ++--- 16 files changed, 293 insertions(+), 50 deletions(-) create mode 100644 src/fluidSolver/SPHSolver.cpp create mode 100644 src/fluidSolver/SPHSolver.h create mode 100644 src/fluidSolver/fluidConstants.h create mode 100644 src/fluidSolver/kernels.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c2d4d9a5..f1e22002 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -102,6 +102,7 @@ set(CORE_SRC src/geometry/box.cpp src/geometry/fluidGeo.cpp src/fluidSolver/fluidSolver.cpp + src/fluidSolver/SPHSolver.cpp src/scene/scene.cpp ) diff --git a/src/fluidSolver/SPHSolver.cpp b/src/fluidSolver/SPHSolver.cpp new file mode 100644 index 00000000..3b7f1706 --- /dev/null +++ b/src/fluidSolver/SPHSolver.cpp @@ -0,0 +1,75 @@ +#include "SPHSolver.h" + +// ---------------------------------------------------- // +// SPHGrid +// ---------------------------------------------------- // +SPHGrid::SPHGrid( + std::vector particles, + int width, + int height, + int depth, + float cellSize + ) : m_width(width), m_height(height), m_depth(depth), m_cellSize(cellSize) +{ + cells.resize(m_width * m_height * m_depth); + + // for(FluidParticle* p : particles) { + // AddParticle(p); + // } +} + +void +SPHGrid::AddParticle( + FluidParticle* particle + ) +{ + glm::vec3 m_pos = particle->Position(); + int i = m_pos[0] / m_cellSize; + int j = m_pos[1] / m_cellSize; + int k = m_pos[2] / m_cellSize; + + if (i > m_width || j > m_height || k > m_depth) { + return; + } + + int idx = GetCellIdx(i, j, k); + cells[idx].push_back(particle); +} + +int +SPHGrid::GetCellIdx( + int i, + int j, + int k + ) +{ + return i + j * m_depth + k * m_width * m_height; +} + +// ---------------------------------------------------- // +// SPHSolver +// ---------------------------------------------------- // +SPHSolver::SPHSolver( + const glm::vec3& containerDim, + const glm::vec3& particleDim, + const float& separation + ) : FluidSolver(containerDim, particleDim, separation) +{ + m_grid = new SPHGrid( + m_particles, + 5, + 5, + 5, + 1.0f + ); +} + +SPHSolver::~SPHSolver() +{ + +} + +void SPHSolver::Update() +{ + +} diff --git a/src/fluidSolver/SPHSolver.h b/src/fluidSolver/SPHSolver.h new file mode 100644 index 00000000..0b4ce18b --- /dev/null +++ b/src/fluidSolver/SPHSolver.h @@ -0,0 +1,74 @@ +#ifndef SPHSOLVER_H +#define SPHSOLVER_H + +#include + +// ---------------------------------------------------- // +// SPHGrid +// ---------------------------------------------------- // +class SPHGrid +{ +public: + SPHGrid( + std::vector particles, + int width, + int height, + int depth, + float cellSize + ); + void AddParticle(FluidParticle* particle); + std::vector GetParticles(int i, int j, int k); + std::vector GetNeighborParticles(int i, int j, int k); + +private: + + int m_width; // i + int m_height; // j + int m_depth; // k + int m_cellSize; + + // -- cells + std::vector> cells; + + // Return the cell index + int GetCellIdx(int i, int j, int k); + int GetTopCellIdx(int& i, int& j, int& k); + +}; + + +// ---------------------------------------------------- // +// SPHSolver +// ---------------------------------------------------- // +class SPHSolver : public FluidSolver +{ +public: + SPHSolver( + const glm::vec3& containerDim, + const glm::vec3& particleDim, + const float& separation + ); + + virtual ~SPHSolver(); + + virtual void Update(); + void AddParticle(FluidParticle*); + +protected: + + SPHGrid* m_grid; + + // -- Particle info + float m_stiffness; + float m_viscosity; + float m_radius; + float m_mas; + float m_restDensity; + float m_timeStep; + + void SearchNeighbors(); + void CalculateDensity(); + void CalculatePressure(); +}; + +#endif /* SPHSOLVER_H */ diff --git a/src/fluidSolver/fluidConstants.h b/src/fluidSolver/fluidConstants.h new file mode 100644 index 00000000..3d4ef185 --- /dev/null +++ b/src/fluidSolver/fluidConstants.h @@ -0,0 +1,5 @@ +#ifndef FLUID_CONSTANTS_H +#define FLUID_CONSTANTS_H + + +#endif diff --git a/src/fluidSolver/fluidSolver.cpp b/src/fluidSolver/fluidSolver.cpp index 62cbf1ce..ebf9b659 100644 --- a/src/fluidSolver/fluidSolver.cpp +++ b/src/fluidSolver/fluidSolver.cpp @@ -10,8 +10,9 @@ FluidParticle::FluidParticle() FluidParticle::FluidParticle( const glm::vec3& pos, - const glm::vec3& vel - ) :m_pos(pos), m_vel(vel) + const glm::vec3& vel, + const glm::vec4& col + ) :m_pos(pos), m_vel(vel), m_col(col) { } @@ -40,7 +41,8 @@ FluidSolver::FluidSolver( m_particles.push_back( new FluidParticle( glm::vec3(x, y, z), - glm::vec3(0.f, 0.0001f, 0.0f) + glm::vec3(0.f, 0.0001f, 0.0f), + glm::vec4(0.f, 0.f, 1.0f, 1.0f) ) ); } @@ -95,6 +97,18 @@ FluidSolver::ParticleSpawnTimes() const return spawnTimes; } +const std::vector +FluidSolver::ParticleColors() const +{ + std::vector colors; + for (auto &particle : m_particles) + { + colors.push_back(particle->Color()); + } + + return colors; +} + void FluidSolver::Update() { diff --git a/src/fluidSolver/fluidSolver.h b/src/fluidSolver/fluidSolver.h index 256a1970..ff43ec21 100644 --- a/src/fluidSolver/fluidSolver.h +++ b/src/fluidSolver/fluidSolver.h @@ -14,17 +14,25 @@ class FluidParticle FluidParticle(); FluidParticle( const glm::vec3& pos, - const glm::vec3& vel + const glm::vec3& vel, + const glm::vec4& col ); // Getters/setters const glm::vec3& Position() const { return m_pos; } const glm::vec3& Velocity() const { return m_vel; } + const glm::vec4& Color() const { return m_col; } + void SetColor(const glm::vec4& color) { m_col = color; } + const float& Density() const { return m_density; } + const float& Pressure() const { return m_pressure; } const float& SpawnTime() const { return m_spawnTime; } protected: glm::vec3 m_pos; glm::vec3 m_vel; + glm::vec4 m_col; + float m_density = 1.0f; + float m_pressure = 1.0f; float m_spawnTime = 0.0f; }; @@ -40,10 +48,11 @@ class FluidSolver const float& separation ); - ~FluidSolver(); + virtual ~FluidSolver(); const std::vector ParticlePositions() const; const std::vector ParticleVelocities() const; const std::vector ParticleSpawnTimes() const; + const std::vector ParticleColors() const; virtual void Update(); diff --git a/src/fluidSolver/kernels.h b/src/fluidSolver/kernels.h new file mode 100644 index 00000000..eb5e27d1 --- /dev/null +++ b/src/fluidSolver/kernels.h @@ -0,0 +1,26 @@ +#ifndef KERNELS_H +#define KERNELS_H + +#define _USE_MATH_DEFINES +#include + +inline float KernelPoly6(float x, float h) +{ + if (x < 0 || x > h) { + return 0; + } + + return (315 * pow(pow(h, 2) - pow(x, 2), 3)) / (64 * M_PI * pow(h, 9)); +} + +inline float KernelSpiky(float x, float h) +{ + +} + +inline float KernelViscous(float x, float h) +{ + +} + +#endif diff --git a/src/geometry/fluidGeo.cpp b/src/geometry/fluidGeo.cpp index ae0ef88c..35d157fa 100644 --- a/src/geometry/fluidGeo.cpp +++ b/src/geometry/fluidGeo.cpp @@ -5,13 +5,15 @@ FluidGeo::FluidGeo( const vector& positions, const vector& velocities, - const vector& spawnTimes + const vector& spawnTimes, + const vector& colors ) : m_velocities(velocities), m_spawnTimes(spawnTimes), m_useVao2(false) { m_positions = positions; + m_colors = colors; } GLenum @@ -44,6 +46,12 @@ FluidGeo::SpawnTimeBuffer() const return m_useVao2 ? m_spawnTimeBuffer2 : m_spawnTimeBuffer; } +GLuint +FluidGeo::ColBuffer() const +{ + return m_useVao2 ? m_colBuffer2 : m_colBuffer; +} + void FluidGeo::EnableVertexAttributes() const { @@ -51,30 +59,36 @@ FluidGeo::EnableVertexAttributes() const GLuint curposBuffer = m_useVao2 ? m_posBuffer2 : m_posBuffer; GLuint curvelBuffer = m_useVao2 ? m_velBuffer2 : m_velBuffer; GLuint curspawntimeBuffer = m_useVao2 ? m_spawnTimeBuffer2 : m_spawnTimeBuffer; + GLuint curColBuffer = m_useVao2 ? m_colBuffer2 : m_colBuffer; glBindVertexArray(curvao); // Enable vertex attributes glBindBuffer(GL_ARRAY_BUFFER, curposBuffer); - glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0); + glVertexAttribPointer(POSITION_LOCATION, 3, GL_FLOAT, GL_FALSE, 0, (void*)0); glBindBuffer(GL_ARRAY_BUFFER, curvelBuffer); - glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)0); + glVertexAttribPointer(VELOCITY_LOCATION, 3, GL_FLOAT, GL_FALSE, 0, (void*)0); glBindBuffer(GL_ARRAY_BUFFER, curspawntimeBuffer); - glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, 0, (void*)0); + glVertexAttribPointer(SPAWNTIME_LOCATION, 1, GL_FLOAT, GL_FALSE, 0, (void*)0); + + glBindBuffer(GL_ARRAY_BUFFER, curColBuffer); + glVertexAttribPointer(COLOR_LOCATION, 4, GL_FLOAT, GL_FALSE, 0, (void*)0); - glEnableVertexAttribArray(0); - glEnableVertexAttribArray(1); - glEnableVertexAttribArray(2); + glEnableVertexAttribArray(POSITION_LOCATION); + glEnableVertexAttribArray(VELOCITY_LOCATION); + glEnableVertexAttribArray(SPAWNTIME_LOCATION); + glEnableVertexAttribArray(COLOR_LOCATION); } void FluidGeo::DisableVertexAttributes() const { - glDisableVertexAttribArray(0); - glDisableVertexAttribArray(1); - glDisableVertexAttribArray(2); + glDisableVertexAttribArray(POSITION_LOCATION); + glDisableVertexAttribArray(VELOCITY_LOCATION); + glDisableVertexAttribArray(SPAWNTIME_LOCATION); + glDisableVertexAttribArray(COLOR_LOCATION); glBindBuffer(GL_ARRAY_BUFFER, NULL); glBindVertexArray(NULL); } @@ -90,12 +104,14 @@ FluidGeo::Create() glGenBuffers(1, &m_posBuffer); glGenBuffers(1, &m_velBuffer); glGenBuffers(1, &m_spawnTimeBuffer); + glGenBuffers(1, &m_colBuffer); // -- Secondary vao to ping-pong transform feedback glGenVertexArrays(1, &m_vao2); glGenBuffers(1, &m_posBuffer2); glGenBuffers(1, &m_velBuffer2); glGenBuffers(1, &m_spawnTimeBuffer2); + glGenBuffers(1, &m_colBuffer2); // Share the same m_idxBuffer glBindVertexArray(m_vao); @@ -127,6 +143,7 @@ FluidGeo::UpdateVao() GLuint curposBuffer = m_useVao2 ? m_posBuffer2 : m_posBuffer; GLuint curvelBuffer = m_useVao2 ? m_velBuffer2 : m_velBuffer; GLuint curspawntimeBuffer = m_useVao2 ? m_spawnTimeBuffer2 : m_spawnTimeBuffer; + GLuint curColBuffer = m_useVao2 ? m_colBuffer2 : m_colBuffer; glBindVertexArray(curvao); glBindBuffer(GL_ARRAY_BUFFER, curposBuffer); @@ -153,6 +170,14 @@ FluidGeo::UpdateVao() GL_DYNAMIC_COPY); glBindBuffer(GL_ARRAY_BUFFER, NULL); + glBindBuffer(GL_ARRAY_BUFFER, curColBuffer); + glBufferData( + GL_ARRAY_BUFFER, + m_colors.size() * sizeof(glm::vec4), + &m_colors[0], + GL_DYNAMIC_COPY); + glBindBuffer(GL_ARRAY_BUFFER, NULL); + glBindVertexArray(NULL); } diff --git a/src/geometry/fluidGeo.h b/src/geometry/fluidGeo.h index 1373fb6c..0e80fbbe 100644 --- a/src/geometry/fluidGeo.h +++ b/src/geometry/fluidGeo.h @@ -10,7 +10,8 @@ class FluidGeo : public Geometry FluidGeo( const vector& positions, const vector& velocities, - const vector& spawnTimes + const vector& spawnTimes, + const vector& colors ); virtual void Create(); @@ -28,6 +29,7 @@ class FluidGeo : public Geometry GLuint PosBuffer() const; GLuint VelBuffer() const; GLuint SpawnTimeBuffer() const; + GLuint ColBuffer() const; protected: virtual void InitIndices(); @@ -49,6 +51,8 @@ class FluidGeo : public Geometry GLuint m_spawnTimeBuffer; GLuint m_spawnTimeBuffer2; + GLuint m_colBuffer2; + // Vertex array object GLuint m_vao2; }; diff --git a/src/geometry/geometry.h b/src/geometry/geometry.h index 9ddd4a11..1fe28647 100644 --- a/src/geometry/geometry.h +++ b/src/geometry/geometry.h @@ -9,15 +9,22 @@ #include #include #include - // C++ #include +#include // Custom #include + +#define POSITION_LOCATION 0 +#define VELOCITY_LOCATION 1 +#define SPAWNTIME_LOCATION 2 +#define COLOR_LOCATION 3 + + typedef enum { DrawMode_Wireframe, DrawMode_Shaded, @@ -42,11 +49,8 @@ class Geometry virtual void DisableVertexAttributes() const; // -- Color - void SetColor(const glm::vec4& color) { - m_color = color; - }; - const glm::vec4& GetColor() const { - return m_color; + void SetColors(const vector& colors) { + m_colors = colors; }; // -- Transformations @@ -59,10 +63,8 @@ class Geometry protected: DrawMode m_drawMode = DrawMode_Shaded; - // Data - glm::vec4 m_color = glm::vec4(1.0); - vector m_positions; + vector m_colors; vector m_indices; // Transformation diff --git a/src/glsl/particle_advect.vert b/src/glsl/particle_advect.vert index 85b362c9..d0eb5658 100644 --- a/src/glsl/particle_advect.vert +++ b/src/glsl/particle_advect.vert @@ -1,4 +1,8 @@ #version 330 core +#define POSITION_LOCATION 0 +#define VELOCITY_LOCATION 1 +#define SPAWNTIME_LOCATION 2 +#define COLOR_LOCATION 3 precision highp float; precision highp int; @@ -7,13 +11,15 @@ layout(std140, column_major) uniform; uniform float u_time; uniform vec3 u_accel; -layout(location = 0) in vec3 a_position; -layout(location = 1) in vec3 a_velocity; -layout(location = 2) in float a_spawntime; +layout(location = POSITION_LOCATION) in vec3 a_position; +layout(location = VELOCITY_LOCATION) in vec3 a_velocity; +layout(location = SPAWNTIME_LOCATION) in float a_spawntime; +layout(location = COLOR_LOCATION) in vec4 a_color; out vec3 v_position; out vec3 v_velocity; out float v_spawntime; +out vec4 v_color; float rand(vec2 co){ return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); @@ -24,4 +30,5 @@ void main() v_velocity = a_velocity + vec3(0.0, 0.01, 0.0);// u_time * u_accel + vec3(0.0, 0.1, 0.0); v_position = a_position + u_time * v_velocity + vec3(0.0, 0.1, 0.0); v_spawntime = a_spawntime; + v_color = a_color; } diff --git a/src/glsl/particle_draw.frag b/src/glsl/particle_draw.frag index 02d40f99..93ad060c 100644 --- a/src/glsl/particle_draw.frag +++ b/src/glsl/particle_draw.frag @@ -3,11 +3,11 @@ precision highp float; precision highp int; -uniform vec4 u_color; uniform vec3 u_minBoundary; uniform vec3 u_maxBoundary; in vec3 v_position; +in vec4 v_color; out vec4 color; @@ -16,6 +16,6 @@ void main() if (v_position.y < u_minBoundary.y) { color = vec4(0.0, -v_position.y * 0.1, 1.0, 1.0); } else { - color = u_color; + color = v_color; } } diff --git a/src/glsl/particle_draw.vert b/src/glsl/particle_draw.vert index 013a8f9d..69b627ed 100644 --- a/src/glsl/particle_draw.vert +++ b/src/glsl/particle_draw.vert @@ -1,4 +1,8 @@ #version 330 core +#define POSITION_LOCATION 0 +#define VELOCITY_LOCATION 1 +#define SPAWNTIME_LOCATION 2 +#define COLOR_LOCATION 3 precision highp float; precision highp int; @@ -7,11 +11,13 @@ uniform mat4 u_model; uniform mat4 u_viewProj; uniform float u_time; -layout(location = 0) in vec3 a_position; -layout(location = 1) in vec3 a_velocity; -layout(location = 2) in float a_spawntime; +layout(location = POSITION_LOCATION) in vec3 a_position; +layout(location = VELOCITY_LOCATION) in vec3 a_velocity; +layout(location = SPAWNTIME_LOCATION) in float a_spawntime; +layout(location = COLOR_LOCATION) in vec4 a_color; out vec3 v_position; +out vec4 v_color; float rand(vec2 co){ return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); @@ -19,7 +25,6 @@ float rand(vec2 co){ void main() { - float r = rand(vec2(a_position.y, gl_VertexID)); vec3 position = vec3( a_position.x, a_position.y - (u_time * u_time * u_time * u_time), @@ -27,4 +32,5 @@ void main() ); gl_Position = u_viewProj * u_model * vec4(position, 1.0); v_position = position; + v_color = a_color; } diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 2ce43b52..cb2fb85c 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -54,7 +54,6 @@ Scene::InitFromJson( m_fluidContainer = new Box(); m_fluidContainer->Scale(containerDim.x, containerDim.y, containerDim.z); m_fluidContainer->SetDrawMode(DrawMode_Wireframe); - m_fluidContainer->SetColor(glm::vec4(0.0f, 1.0f, 1.0f, 1.0f)); m_fluidContainer->Create(); // -- Get particle information @@ -68,7 +67,7 @@ Scene::InitFromJson( const float separation = root["particleSeparation"].asFloat(); // -- Initialize fluid solvers - m_fluidSolver = new FluidSolver( + m_fluidSolver = new SPHSolver( containerDim, particleDim, separation @@ -78,9 +77,9 @@ Scene::InitFromJson( m_fluidGeo = new FluidGeo( m_fluidSolver->ParticlePositions(), m_fluidSolver->ParticleVelocities(), - m_fluidSolver->ParticleSpawnTimes() + m_fluidSolver->ParticleSpawnTimes(), + m_fluidSolver->ParticleColors() ); - m_fluidGeo->SetColor(glm::vec4(0.0f, 0.0f, 1.0f, 1.0f)); m_fluidGeo->Create(); } diff --git a/src/scene/scene.h b/src/scene/scene.h index f5323cbd..419ed425 100644 --- a/src/scene/scene.h +++ b/src/scene/scene.h @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include class Scene @@ -36,7 +36,7 @@ class Scene Box* m_testBox; Box* m_fluidContainer; FluidGeo* m_fluidGeo; - FluidSolver* m_fluidSolver; + SPHSolver* m_fluidSolver; // -- Update helpers void UpdateCamera(const KeyboardControl*); diff --git a/src/shaderProgram/particleAdvectProgram.cpp b/src/shaderProgram/particleAdvectProgram.cpp index 0e0d77ab..af2d0f8f 100644 --- a/src/shaderProgram/particleAdvectProgram.cpp +++ b/src/shaderProgram/particleAdvectProgram.cpp @@ -18,7 +18,7 @@ ParticleAdvectProgram::ParticleAdvectProgram( glAttachShader(m_programAdvect, fragShaderID); const GLchar* varyings[] = { - "v_position", "v_velocity", "v_spawntime" + "v_position", "v_velocity", "v_spawntime", "v_color" }; glTransformFeedbackVaryings( @@ -45,7 +45,6 @@ ParticleAdvectProgram::ParticleAdvectProgram( m_unifDrawModel = glGetUniformLocation(m_programDraw, "u_model"); m_unifDrawViewProj = glGetUniformLocation(m_programDraw, "u_viewProj"); m_unifDrawTime = glGetUniformLocation(m_programDraw, "u_time"); - m_unifDrawColor = glGetUniformLocation(m_programDraw, "u_color"); m_unifDrawMinBoundary = glGetUniformLocation(m_programDraw, "u_minBoundary"); m_unifDrawMaxBoundary = glGetUniformLocation(m_programDraw, "u_maxBoundary"); @@ -91,6 +90,7 @@ ParticleAdvectProgram::Advect( glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, fluidGeo->PosBuffer()); glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, fluidGeo->VelBuffer()); glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 2, fluidGeo->SpawnTimeBuffer()); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 3, fluidGeo->ColBuffer()); fluidGeo->ToggleVao(); @@ -102,6 +102,8 @@ ParticleAdvectProgram::Advect( glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, fluidGeo->PosBuffer()); glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, fluidGeo->VelBuffer()); glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 2, fluidGeo->SpawnTimeBuffer()); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 3, fluidGeo->ColBuffer()); + } else { glBindTransformFeedback( GL_TRANSFORM_FEEDBACK, @@ -120,6 +122,7 @@ ParticleAdvectProgram::Advect( glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, fluidGeo->PosBuffer()); glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, fluidGeo->VelBuffer()); glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 2, fluidGeo->SpawnTimeBuffer()); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 3, fluidGeo->ColBuffer()); // Turn off rasterization - we are not drawing glEnable(GL_RASTERIZER_DISCARD); @@ -135,6 +138,7 @@ ParticleAdvectProgram::Advect( glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, NULL); glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, NULL); glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 2, NULL); + glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 3, NULL); fluidGeo->DisableVertexAttributes(); fluidGeo->ToggleVao(); @@ -179,14 +183,6 @@ ParticleAdvectProgram::Draw( ); } - if (m_unifDrawColor != -1) { - glUniform4fv( - m_unifDrawColor, - 1, - &(fluidGeo->GetColor()[0]) - ); - } - if (m_unifDrawMinBoundary != -1) { glm::vec3 minBoundary = container->GetMinBoundary(); glUniform3f( From 410f1216362d07d20326d1d004f0d6379b383c76 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Wed, 23 Mar 2016 03:59:46 -0400 Subject: [PATCH 30/64] Search for neighbor particles --- src/fluidSolver/SPHSolver.cpp | 103 ++++++++++++++++++++++++++-------- src/fluidSolver/SPHSolver.h | 22 +++++--- src/fluidSolver/fluidSolver.h | 3 + src/geometry/box.cpp | 4 +- src/glsl/particle_draw.vert | 2 +- src/scene/scene.cpp | 1 + src/scene/scene.json | 6 +- 7 files changed, 105 insertions(+), 36 deletions(-) diff --git a/src/fluidSolver/SPHSolver.cpp b/src/fluidSolver/SPHSolver.cpp index 3b7f1706..08d37432 100644 --- a/src/fluidSolver/SPHSolver.cpp +++ b/src/fluidSolver/SPHSolver.cpp @@ -4,36 +4,56 @@ // SPHGrid // ---------------------------------------------------- // SPHGrid::SPHGrid( - std::vector particles, - int width, - int height, - int depth, - float cellSize - ) : m_width(width), m_height(height), m_depth(depth), m_cellSize(cellSize) + const std::vector& particles, + const int width, + const int height, + const int depth, + const float radius, + const glm::vec3& gridMin, + const glm::vec3& gridMax, + const bool useGrid + ) : m_width(width), m_height(height), m_depth(depth), + m_radius(radius), m_gridMin(gridMin), m_gridMax(gridMax), + m_useGrid(useGrid) { - cells.resize(m_width * m_height * m_depth); + if (useGrid) { + m_cells.resize(m_width * m_height * m_depth); + } else { + // Grid size is only 1 + m_cells.resize(1); + } - // for(FluidParticle* p : particles) { - // AddParticle(p); - // } + for(FluidParticle* p : particles) { + AddParticle(p); + } } +// Add particle to grid based on its position void SPHGrid::AddParticle( FluidParticle* particle ) { - glm::vec3 m_pos = particle->Position(); - int i = m_pos[0] / m_cellSize; - int j = m_pos[1] / m_cellSize; - int k = m_pos[2] / m_cellSize; - - if (i > m_width || j > m_height || k > m_depth) { - return; + if (!m_useGrid) { + m_cells[0].push_back(particle); } - int idx = GetCellIdx(i, j, k); - cells[idx].push_back(particle); + // @todo + // glm::vec3 m_pos = particle->Position(); + // int i = m_pos[0] / m_radius; + // int j = m_pos[1] / m_radius; + // int k = m_pos[2] / m_radius; + + // if (i > m_width || j > m_height || k > m_depth) { + // return; + // } + + // int idx = GetCellIdx(i, j, k); + // if (idx > m_cells.size()) { + // std::cout << idx << std::endl; + // return; + // } + // m_cells[idx].push_back(particle); } int @@ -46,6 +66,31 @@ SPHGrid::GetCellIdx( return i + j * m_depth + k * m_width * m_height; } + +std::vector +SPHGrid::GetNeighborParticles( + FluidParticle* particle + ) +{ + std::vector neighbors; + + // @todo: Use naive neighbor search O(n*n) + if (!m_useGrid) { + for (FluidParticle* neighbor : m_cells[0]) { + if (glm::distance(neighbor->Position(), particle->Position()) < m_radius && + neighbor != particle) { + + // Change color of this neighbor + neighbor->SetColor(glm::vec4(1.0f, 0.0f, 0.0f, 1.0f)); + neighbors.push_back(neighbor); + } else { + neighbor->SetColor(glm::vec4(0.0f, 1.0f, 1.0f, 1.0f)); + } + } + } + + return neighbors; +} // ---------------------------------------------------- // // SPHSolver // ---------------------------------------------------- // @@ -60,16 +105,30 @@ SPHSolver::SPHSolver( 5, 5, 5, - 1.0f + 1.0f, + -containerDim, + containerDim, + false ); + + // Initialize random seed + srand (time(NULL)); } SPHSolver::~SPHSolver() { - + delete m_grid; } -void SPHSolver::Update() +void +SPHSolver::Update() { + SearchNeighbors(); +} +void +SPHSolver::SearchNeighbors() +{ + int randIdx = rand() % m_particles.size(); + m_grid->GetNeighborParticles(m_particles[randIdx]); } diff --git a/src/fluidSolver/SPHSolver.h b/src/fluidSolver/SPHSolver.h index 0b4ce18b..f0e5dcd8 100644 --- a/src/fluidSolver/SPHSolver.h +++ b/src/fluidSolver/SPHSolver.h @@ -10,25 +10,31 @@ class SPHGrid { public: SPHGrid( - std::vector particles, - int width, - int height, - int depth, - float cellSize + const std::vector& particles, + const int width, + const int height, + const int depth, + const float radius, + const glm::vec3& gridMin, + const glm::vec3& gridMax, + const bool useGrid // Use grid to speed up neighbor search ); void AddParticle(FluidParticle* particle); std::vector GetParticles(int i, int j, int k); - std::vector GetNeighborParticles(int i, int j, int k); + std::vector GetNeighborParticles(FluidParticle*); private: int m_width; // i int m_height; // j int m_depth; // k - int m_cellSize; + float m_radius; // radius of particle influence + glm::vec3 m_gridMin; + glm::vec3 m_gridMax; + bool m_useGrid; // -- cells - std::vector> cells; + std::vector> m_cells; // Return the cell index int GetCellIdx(int i, int j, int k); diff --git a/src/fluidSolver/fluidSolver.h b/src/fluidSolver/fluidSolver.h index ff43ec21..a5d12313 100644 --- a/src/fluidSolver/fluidSolver.h +++ b/src/fluidSolver/fluidSolver.h @@ -4,6 +4,9 @@ #include #include #include +#include +#include +#include // ---------------------------------------------------- // // FluidParticle diff --git a/src/geometry/box.cpp b/src/geometry/box.cpp index 0f7dc298..7e28009c 100644 --- a/src/geometry/box.cpp +++ b/src/geometry/box.cpp @@ -4,14 +4,14 @@ const glm::vec3 Box::GetMinBoundary() const { // @todo: use global transformation when building a scene graph - return m_translation - m_scale; + return m_translation - m_scale / 2.0f; } const glm::vec3 Box::GetMaxBoundary() const { // @todo: use global transformation when building a scene graph - return m_translation + m_scale; + return m_translation + m_scale / 2.0f; } diff --git a/src/glsl/particle_draw.vert b/src/glsl/particle_draw.vert index 69b627ed..db5487bb 100644 --- a/src/glsl/particle_draw.vert +++ b/src/glsl/particle_draw.vert @@ -27,7 +27,7 @@ void main() { vec3 position = vec3( a_position.x, - a_position.y - (u_time * u_time * u_time * u_time), + a_position.y,// - (u_time * u_time * u_time * u_time), a_position.z ); gl_Position = u_viewProj * u_model * vec4(position, 1.0); diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index cb2fb85c..53efebc7 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -206,5 +206,6 @@ Scene::DrawFluidSolver( prog.Draw(*m_camera, *m_fluidContainer); // -- Draw particles + m_fluidGeo->SetColors(m_fluidSolver->ParticleColors()); progAdvect.Draw(m_camera, m_fluidGeo, m_fluidContainer); } diff --git a/src/scene/scene.json b/src/scene/scene.json index 33b9ac80..be29843d 100644 --- a/src/scene/scene.json +++ b/src/scene/scene.json @@ -5,9 +5,9 @@ "scaleZ" : 10.0 }, "particleDim" : { - "boundX" : 8.7, - "boundY" : 8.7, - "boundZ" : 8.7 + "boundX" : 3.7, + "boundY" : 3.7, + "boundZ" : 3.7 }, "particleSeparation" : 0.1 } From 7f2d75478067f4a29bbb9052f8ab711c76f4e1e6 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Wed, 23 Mar 2016 11:17:11 -0400 Subject: [PATCH 31/64] Added linear test of particle neighbor search --- src/fluidSolver/SPHSolver.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/fluidSolver/SPHSolver.cpp b/src/fluidSolver/SPHSolver.cpp index 08d37432..511387f0 100644 --- a/src/fluidSolver/SPHSolver.cpp +++ b/src/fluidSolver/SPHSolver.cpp @@ -66,7 +66,6 @@ SPHGrid::GetCellIdx( return i + j * m_depth + k * m_width * m_height; } - std::vector SPHGrid::GetNeighborParticles( FluidParticle* particle @@ -129,6 +128,8 @@ SPHSolver::Update() void SPHSolver::SearchNeighbors() { - int randIdx = rand() % m_particles.size(); - m_grid->GetNeighborParticles(m_particles[randIdx]); + static int i = 0; + m_grid->GetNeighborParticles(m_particles[i]); + i = (i + 1) % m_particles.size(); } + From c436184f53b0bde69704e5ce34823c2e74bc5d24 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Wed, 23 Mar 2016 13:28:26 -0400 Subject: [PATCH 32/64] Buggy incomplete submission before deadline --- CMakeLists.txt | 2 + README.md | 12 ++++ src/fluidSolver/SPHGrid.cpp | 97 ++++++++++++++++++++++++++++ src/fluidSolver/SPHGrid.h | 52 +++++++++++++++ src/fluidSolver/SPHKernels.h | 53 ++++++++++++++++ src/fluidSolver/SPHSolver.cpp | 101 ++++-------------------------- src/fluidSolver/SPHSolver.h | 41 +----------- src/fluidSolver/fluidParticle.cpp | 19 ++++++ src/fluidSolver/fluidParticle.h | 37 +++++++++++ src/fluidSolver/fluidSolver.cpp | 17 ----- src/fluidSolver/fluidSolver.h | 31 +-------- src/fluidSolver/kernels.h | 26 -------- src/main.cpp | 50 +++++++++++++++ 13 files changed, 335 insertions(+), 203 deletions(-) create mode 100644 src/fluidSolver/SPHGrid.cpp create mode 100644 src/fluidSolver/SPHGrid.h create mode 100644 src/fluidSolver/SPHKernels.h create mode 100644 src/fluidSolver/fluidParticle.cpp create mode 100644 src/fluidSolver/fluidParticle.h delete mode 100644 src/fluidSolver/kernels.h diff --git a/CMakeLists.txt b/CMakeLists.txt index f1e22002..89962841 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,7 +101,9 @@ set(CORE_SRC src/geometry/geometry.cpp src/geometry/box.cpp src/geometry/fluidGeo.cpp + src/fluidSolver/fluidParticle.cpp src/fluidSolver/fluidSolver.cpp + src/fluidSolver/SPHGrid.cpp src/fluidSolver/SPHSolver.cpp src/scene/scene.cpp ) diff --git a/README.md b/README.md index 980af87a..4ad57e94 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,17 @@ # Korra Fluid Solver, CIS 563, University of Pennsylvania, Spring 2016 +## Update March 23 (Sprint 1) +I implemented the SPH Solver with naive neighbor search. When you started running the code, you'll see a test particle iterating through the grid and shows all the neighbors in red color. However, this neighbor search isn't using a working uniform grid yet. + +I have a simple test suite in main.cpp to test Poly6. Currently spiky kernel and viscous kernel are not tested. By uncommenting `#define UNIT_TEST`, there should be print out of the kernel unit testing. + +### Incomplete features: +Uniform grid. +Compute density. +Compute pressure. +Full unit testing. +OpenVDB. + ## Overview __Korra__ is a cross-platform C++ fluid FLIP solver, designed and built for CIS563 course. __Korra__ uses OpenGL heavily for the graphics pipeline. It is also designed for GPU optimization for fluid advection. diff --git a/src/fluidSolver/SPHGrid.cpp b/src/fluidSolver/SPHGrid.cpp new file mode 100644 index 00000000..ef1d5213 --- /dev/null +++ b/src/fluidSolver/SPHGrid.cpp @@ -0,0 +1,97 @@ +#include "SPHGrid.h" + +// ---------------------------------------------------- // +// SPHGrid +// ---------------------------------------------------- // +SPHGrid::SPHGrid( + const std::vector& particles, + const int width, + const int height, + const int depth, + const float radius, + const glm::vec3& gridMin, + const glm::vec3& gridMax, + const bool useGrid + ) : m_width(width), m_height(height), m_depth(depth), + m_radius(radius), m_gridMin(gridMin), m_gridMax(gridMax), + m_useGrid(useGrid) +{ + if (useGrid) { + m_cells.resize(m_width * m_height * m_depth); + } else { + // Grid size is only 1 + m_cells.resize(1); + } + + for(FluidParticle* p : particles) { + AddParticle(p); + } +} + +// Add particle to grid based on its position +void +SPHGrid::AddParticle( + FluidParticle* particle + ) +{ + if (!m_useGrid) { + + // No grid + m_cells[0].push_back(particle); + } else { + + // Add to grid based on its position + glm::vec3 m_pos = particle->Position(); + int i = m_pos[0] / m_radius; + int j = m_pos[1] / m_radius; + int k = m_pos[2] / m_radius; + + if (i > m_width || j > m_height || k > m_depth) { + return; + } + + int idx = GetCellIdx(i, j, k); + if (idx > m_cells.size()) { + std::cout << idx << std::endl; + return; + } + m_cells[idx].push_back(particle); + } +} + +int +SPHGrid::GetCellIdx( + int i, + int j, + int k + ) +{ + return i + j * m_depth + k * m_width * m_height; +} + +std::vector +SPHGrid::GetNeighborParticles( + FluidParticle* particle + ) +{ + std::vector neighbors; + + // @todo: Use naive neighbor search + if (!m_useGrid) { + for (FluidParticle* neighbor : m_cells[0]) { + if (glm::distance(neighbor->Position(), particle->Position()) < m_radius && + neighbor != particle) { + + // Change color of this neighbor + neighbor->SetColor(glm::vec4(1.0f, 0.0f, 0.0f, 1.0f)); + neighbors.push_back(neighbor); + } else { + neighbor->SetColor(glm::vec4(0.0f, 1.0f, 1.0f, 1.0f)); + } + } + } else { + + } + + return neighbors; +} diff --git a/src/fluidSolver/SPHGrid.h b/src/fluidSolver/SPHGrid.h new file mode 100644 index 00000000..61cc3e63 --- /dev/null +++ b/src/fluidSolver/SPHGrid.h @@ -0,0 +1,52 @@ +#ifndef SPHGRID_H +#define SPHGRID_H + +#include +#include +#include +#include +#include + +#include + + +// ---------------------------------------------------- // +// SPHGrid +// ---------------------------------------------------- // +class SPHGrid +{ +public: + SPHGrid( + const std::vector& particles, + const int width, + const int height, + const int depth, + const float radius, + const glm::vec3& gridMin, + const glm::vec3& gridMax, + const bool useGrid // Use grid to speed up neighbor search + ); + void AddParticle(FluidParticle* particle); + std::vector GetParticles(int i, int j, int k); + std::vector GetNeighborParticles(FluidParticle*); + +private: + + int m_width; // i + int m_height; // j + int m_depth; // k + float m_radius; // radius of particle influence + glm::vec3 m_gridMin; + glm::vec3 m_gridMax; + bool m_useGrid; + + // -- cells + std::vector> m_cells; + + // Return the cell index + int GetCellIdx(int i, int j, int k); + int GetTopCellIdx(int& i, int& j, int& k); + +}; + +#endif diff --git a/src/fluidSolver/SPHKernels.h b/src/fluidSolver/SPHKernels.h new file mode 100644 index 00000000..b967e51c --- /dev/null +++ b/src/fluidSolver/SPHKernels.h @@ -0,0 +1,53 @@ +#ifndef KERNELS_H +#define KERNELS_H + +#define _USE_MATH_DEFINES +#include + +inline float KernelPoly6(float x, float h) +{ + if (x < 0 || x > h) { + return 0; + } + + if (h <= 0) { + return 0; + } + + return (315 * pow(pow(h, 2) - pow(x, 2), 3)) / (64 * M_PI * pow(h, 9)); +} + +inline float KernelSpiky(float x, float h) +{ + if (x < 0 || x > h) { + return 0; + } + + if (h <= 0) { + return 0; + } + + return (15 * pow(h - x, 3)) / (M_PI * pow(h, 6)); +} + +inline float KernelViscous(float x, float h) +{ + if (x <= 0 || x > h) { + return 0; + } + + if (h <= 0) { + return 0; + } + + return (15 * + ( + -(pow(x, 3) / (2 * pow(h, 3))) + + pow(x, 2) / (pow(h, 2)) + + h / (2 * x) - + 1 + )) / + (2 * M_PI * pow(h, 3)); +} + +#endif diff --git a/src/fluidSolver/SPHSolver.cpp b/src/fluidSolver/SPHSolver.cpp index 511387f0..9fb029dd 100644 --- a/src/fluidSolver/SPHSolver.cpp +++ b/src/fluidSolver/SPHSolver.cpp @@ -1,95 +1,5 @@ #include "SPHSolver.h" -// ---------------------------------------------------- // -// SPHGrid -// ---------------------------------------------------- // -SPHGrid::SPHGrid( - const std::vector& particles, - const int width, - const int height, - const int depth, - const float radius, - const glm::vec3& gridMin, - const glm::vec3& gridMax, - const bool useGrid - ) : m_width(width), m_height(height), m_depth(depth), - m_radius(radius), m_gridMin(gridMin), m_gridMax(gridMax), - m_useGrid(useGrid) -{ - if (useGrid) { - m_cells.resize(m_width * m_height * m_depth); - } else { - // Grid size is only 1 - m_cells.resize(1); - } - - for(FluidParticle* p : particles) { - AddParticle(p); - } -} - -// Add particle to grid based on its position -void -SPHGrid::AddParticle( - FluidParticle* particle - ) -{ - if (!m_useGrid) { - m_cells[0].push_back(particle); - } - - // @todo - // glm::vec3 m_pos = particle->Position(); - // int i = m_pos[0] / m_radius; - // int j = m_pos[1] / m_radius; - // int k = m_pos[2] / m_radius; - - // if (i > m_width || j > m_height || k > m_depth) { - // return; - // } - - // int idx = GetCellIdx(i, j, k); - // if (idx > m_cells.size()) { - // std::cout << idx << std::endl; - // return; - // } - // m_cells[idx].push_back(particle); -} - -int -SPHGrid::GetCellIdx( - int i, - int j, - int k - ) -{ - return i + j * m_depth + k * m_width * m_height; -} - -std::vector -SPHGrid::GetNeighborParticles( - FluidParticle* particle - ) -{ - std::vector neighbors; - - // @todo: Use naive neighbor search O(n*n) - if (!m_useGrid) { - for (FluidParticle* neighbor : m_cells[0]) { - if (glm::distance(neighbor->Position(), particle->Position()) < m_radius && - neighbor != particle) { - - // Change color of this neighbor - neighbor->SetColor(glm::vec4(1.0f, 0.0f, 0.0f, 1.0f)); - neighbors.push_back(neighbor); - } else { - neighbor->SetColor(glm::vec4(0.0f, 1.0f, 1.0f, 1.0f)); - } - } - } - - return neighbors; -} // ---------------------------------------------------- // // SPHSolver // ---------------------------------------------------- // @@ -123,6 +33,8 @@ void SPHSolver::Update() { SearchNeighbors(); + CalculateDensity(); + CalculatePressure(); } void @@ -133,3 +45,12 @@ SPHSolver::SearchNeighbors() i = (i + 1) % m_particles.size(); } +void +SPHSolver::CalculateDensity() +{ +} + +void +SPHSolver::CalculatePressure() +{ +} diff --git a/src/fluidSolver/SPHSolver.h b/src/fluidSolver/SPHSolver.h index f0e5dcd8..37db260d 100644 --- a/src/fluidSolver/SPHSolver.h +++ b/src/fluidSolver/SPHSolver.h @@ -2,46 +2,7 @@ #define SPHSOLVER_H #include - -// ---------------------------------------------------- // -// SPHGrid -// ---------------------------------------------------- // -class SPHGrid -{ -public: - SPHGrid( - const std::vector& particles, - const int width, - const int height, - const int depth, - const float radius, - const glm::vec3& gridMin, - const glm::vec3& gridMax, - const bool useGrid // Use grid to speed up neighbor search - ); - void AddParticle(FluidParticle* particle); - std::vector GetParticles(int i, int j, int k); - std::vector GetNeighborParticles(FluidParticle*); - -private: - - int m_width; // i - int m_height; // j - int m_depth; // k - float m_radius; // radius of particle influence - glm::vec3 m_gridMin; - glm::vec3 m_gridMax; - bool m_useGrid; - - // -- cells - std::vector> m_cells; - - // Return the cell index - int GetCellIdx(int i, int j, int k); - int GetTopCellIdx(int& i, int& j, int& k); - -}; - +#include // ---------------------------------------------------- // // SPHSolver diff --git a/src/fluidSolver/fluidParticle.cpp b/src/fluidSolver/fluidParticle.cpp new file mode 100644 index 00000000..896159dc --- /dev/null +++ b/src/fluidSolver/fluidParticle.cpp @@ -0,0 +1,19 @@ +#include "FluidParticle.h" + +// ---------------------------------------------------- // +// FluidParticle +// ---------------------------------------------------- // +FluidParticle::FluidParticle() +{ + +} + +FluidParticle::FluidParticle( + const glm::vec3& pos, + const glm::vec3& vel, + const glm::vec4& col + ) :m_pos(pos), m_vel(vel), m_col(col) +{ + +} + diff --git a/src/fluidSolver/fluidParticle.h b/src/fluidSolver/fluidParticle.h new file mode 100644 index 00000000..cf9de837 --- /dev/null +++ b/src/fluidSolver/fluidParticle.h @@ -0,0 +1,37 @@ +#ifndef FLUID_PARTICLE_H +#define FLUID_PARTICLE_H + +#include + +// ---------------------------------------------------- // +// FluidParticle +// ---------------------------------------------------- // +class FluidParticle +{ +public: + FluidParticle(); + FluidParticle( + const glm::vec3& pos, + const glm::vec3& vel, + const glm::vec4& col + ); + + // Getters/setters + const glm::vec3& Position() const { return m_pos; } + const glm::vec3& Velocity() const { return m_vel; } + const glm::vec4& Color() const { return m_col; } + void SetColor(const glm::vec4& color) { m_col = color; } + const float& Density() const { return m_density; } + const float& Pressure() const { return m_pressure; } + const float& SpawnTime() const { return m_spawnTime; } + +protected: + glm::vec3 m_pos; + glm::vec3 m_vel; + glm::vec4 m_col; + float m_density = 1.0f; + float m_pressure = 1.0f; + float m_spawnTime = 0.0f; +}; + +#endif diff --git a/src/fluidSolver/fluidSolver.cpp b/src/fluidSolver/fluidSolver.cpp index ebf9b659..a6b79956 100644 --- a/src/fluidSolver/fluidSolver.cpp +++ b/src/fluidSolver/fluidSolver.cpp @@ -1,22 +1,5 @@ #include "fluidSolver.h" -// ---------------------------------------------------- // -// FluidParticle -// ---------------------------------------------------- // -FluidParticle::FluidParticle() -{ - -} - -FluidParticle::FluidParticle( - const glm::vec3& pos, - const glm::vec3& vel, - const glm::vec4& col - ) :m_pos(pos), m_vel(vel), m_col(col) -{ - -} - // ---------------------------------------------------- // // FluidSolver // ---------------------------------------------------- // diff --git a/src/fluidSolver/fluidSolver.h b/src/fluidSolver/fluidSolver.h index a5d12313..cee7a7f2 100644 --- a/src/fluidSolver/fluidSolver.h +++ b/src/fluidSolver/fluidSolver.h @@ -8,36 +8,7 @@ #include #include -// ---------------------------------------------------- // -// FluidParticle -// ---------------------------------------------------- // -class FluidParticle -{ -public: - FluidParticle(); - FluidParticle( - const glm::vec3& pos, - const glm::vec3& vel, - const glm::vec4& col - ); - - // Getters/setters - const glm::vec3& Position() const { return m_pos; } - const glm::vec3& Velocity() const { return m_vel; } - const glm::vec4& Color() const { return m_col; } - void SetColor(const glm::vec4& color) { m_col = color; } - const float& Density() const { return m_density; } - const float& Pressure() const { return m_pressure; } - const float& SpawnTime() const { return m_spawnTime; } - -protected: - glm::vec3 m_pos; - glm::vec3 m_vel; - glm::vec4 m_col; - float m_density = 1.0f; - float m_pressure = 1.0f; - float m_spawnTime = 0.0f; -}; +#include // ---------------------------------------------------- // // FluidSolver diff --git a/src/fluidSolver/kernels.h b/src/fluidSolver/kernels.h deleted file mode 100644 index eb5e27d1..00000000 --- a/src/fluidSolver/kernels.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef KERNELS_H -#define KERNELS_H - -#define _USE_MATH_DEFINES -#include - -inline float KernelPoly6(float x, float h) -{ - if (x < 0 || x > h) { - return 0; - } - - return (315 * pow(pow(h, 2) - pow(x, 2), 3)) / (64 * M_PI * pow(h, 9)); -} - -inline float KernelSpiky(float x, float h) -{ - -} - -inline float KernelViscous(float x, float h) -{ - -} - -#endif diff --git a/src/main.cpp b/src/main.cpp index 4c8cead8..84d74607 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,11 +1,61 @@ #include #include +#include +#include +#include + +#define UNIT_TEST using namespace std; +void TestKernels() +{ + float x[] = {0, 1.0f, 5.4f, 100.7f, -30.f, 36.f, -4.f}; + float h[] = {10.f, 1.0f, 2.4f, 140.23f, -22.f, -2.f, 20.f}; + float expectedPoly6[] = {0.f, 0.0f, 0.f, 100.7f, 0.f, 0.f, 0.f}; + + cout << "TestKernels" << endl; + + int testLen = 7; + for (int i = 0; i < testLen; ++i) { + cout << "KernelPoly6, x: " + to_string(x[i]) + ", h: " + to_string(h[i]) << endl; + float wPoly6 = KernelPoly6(x[i], h[i]); + cout << wPoly6 << endl; + +#ifdef TEST_SPIKY + cout << "KernelSpiky: x: " + to_string(x[i]) + ", h: " + to_string(h[i]) << endl; + float wSpiky = KernelSpiky(x[i], h[i]); + cout << wSpiky << endl; +#endif + +#ifdef TEST_VISCOUS + cout << "KernelViscous: x: " + to_string(x[i]) + ", h: " + to_string(h[i]) << endl; + float wViscous = KernelViscous(x[i], h[i]); + cout << wSpiky << endl; +#endif + } +} + +void TestSuite() +{ + cout << "------------------------" << endl; + cout << "UNIT TEST ENABLED" << endl; + + // @todo: NEED TO WRITE PROPER TEST SUITE! + bool passed = false; + TestKernels(); + + cout << "END UNIT TEST" << endl; + cout << "------------------------" << endl; +} + int main() { +#ifdef UNIT_TEST + TestSuite(); +#endif + Viewer view(800, 600); view.Update(); view.CleanUp(); From efa8def3a2f3d26a52d8b2513ba3481080d6ab1a Mon Sep 17 00:00:00 2001 From: Trung Le Date: Sat, 26 Mar 2016 13:22:54 -0400 Subject: [PATCH 33/64] Enabled TBB for neighbor search --- CMakeLists.txt | 3 +- src/fluidSolver/SPHGrid.cpp | 54 +++++++++++++++++------------------ src/fluidSolver/SPHGrid.h | 23 ++++++++------- src/fluidSolver/SPHSolver.cpp | 5 +--- src/main.cpp | 1 - 5 files changed, 42 insertions(+), 44 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 89962841..2b3750d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -42,6 +42,7 @@ find_package(OPENGL REQUIRED) find_package(GLEW) find_library(GLFW_LIBRARY "glfw3" HINTS ${GLFW_LIBRARY_DIR}) find_library(JSONCPP "jsoncpp") +find_library(TBB "tbb") add_definitions( -DTW_STATIC @@ -51,7 +52,7 @@ add_definitions( -D_CRT_SECURE_NO_WARNINGS ) -set(CORE_LIBS ${GLFW_LIBRARY} ${GLUT_LIBRARY} ${GLEW_LIBRARY} ${JSONCPP} ${OPENGL_LIBRARY} ) +set(CORE_LIBS ${GLFW_LIBRARY} ${GLUT_LIBRARY} ${GLEW_LIBRARY} ${JSONCPP} ${TBB} ${OPENGL_LIBRARY} ) # OSX-specific hacks/fixes if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") diff --git a/src/fluidSolver/SPHGrid.cpp b/src/fluidSolver/SPHGrid.cpp index ef1d5213..741cf1e6 100644 --- a/src/fluidSolver/SPHGrid.cpp +++ b/src/fluidSolver/SPHGrid.cpp @@ -5,18 +5,18 @@ // ---------------------------------------------------- // SPHGrid::SPHGrid( const std::vector& particles, - const int width, - const int height, - const int depth, - const float radius, const glm::vec3& gridMin, const glm::vec3& gridMax, + const float cellSize, const bool useGrid - ) : m_width(width), m_height(height), m_depth(depth), - m_radius(radius), m_gridMin(gridMin), m_gridMax(gridMax), + ) : m_width(1), m_height(1), m_depth(1), + m_cellSize(cellSize), m_gridMin(gridMin), m_gridMax(gridMax), m_useGrid(useGrid) { if (useGrid) { + m_width = ceil((m_gridMax.x - m_gridMin.x) / m_cellSize); + m_height = ceil((m_gridMax.y - m_gridMin.y) / m_cellSize); + m_depth = ceil((m_gridMax.z - m_gridMin.z) / m_cellSize); m_cells.resize(m_width * m_height * m_depth); } else { // Grid size is only 1 @@ -35,30 +35,31 @@ SPHGrid::AddParticle( ) { if (!m_useGrid) { - - // No grid + // No grid, push particle in normally m_cells[0].push_back(particle); } else { - - // Add to grid based on its position - glm::vec3 m_pos = particle->Position(); - int i = m_pos[0] / m_radius; - int j = m_pos[1] / m_radius; - int k = m_pos[2] / m_radius; - - if (i > m_width || j > m_height || k > m_depth) { - return; - } - - int idx = GetCellIdx(i, j, k); + // Add particle to grid based on its position + int idx = GetCellIdx(particle->Position()); if (idx > m_cells.size()) { - std::cout << idx << std::endl; + cout << "Error adding particle " + idx << endl; return; } m_cells[idx].push_back(particle); } } +int +SPHGrid::GetCellIdx( + const glm::vec3& position + ) +{ + int i = (position.x - m_gridMin.x) / m_cellSize; + int j = (position.y - m_gridMin.y) / m_cellSize; + int k = (position.z - m_gridMin.z) / m_cellSize; + + return this->GetCellIdx(i, j, k); +} + int SPHGrid::GetCellIdx( int i, @@ -74,21 +75,20 @@ SPHGrid::GetNeighborParticles( FluidParticle* particle ) { - std::vector neighbors; - // @todo: Use naive neighbor search if (!m_useGrid) { - for (FluidParticle* neighbor : m_cells[0]) { - if (glm::distance(neighbor->Position(), particle->Position()) < m_radius && + int len = m_cells[0].size(); + parallel_for(0, len, [&](int i) { + FluidParticle* neighbor = m_cells[0][i]; + if (glm::distance(neighbor->Position(), particle->Position()) < m_cellSize && neighbor != particle) { // Change color of this neighbor neighbor->SetColor(glm::vec4(1.0f, 0.0f, 0.0f, 1.0f)); - neighbors.push_back(neighbor); } else { neighbor->SetColor(glm::vec4(0.0f, 1.0f, 1.0f, 1.0f)); } - } + }); } else { } diff --git a/src/fluidSolver/SPHGrid.h b/src/fluidSolver/SPHGrid.h index 61cc3e63..044b4bea 100644 --- a/src/fluidSolver/SPHGrid.h +++ b/src/fluidSolver/SPHGrid.h @@ -1,14 +1,17 @@ #ifndef SPHGRID_H #define SPHGRID_H -#include -#include -#include #include #include +#include +#include +#include +#include #include +using namespace std; +using namespace tbb; // ---------------------------------------------------- // // SPHGrid @@ -18,12 +21,9 @@ class SPHGrid public: SPHGrid( const std::vector& particles, - const int width, - const int height, - const int depth, - const float radius, const glm::vec3& gridMin, const glm::vec3& gridMax, + const float cellSize, const bool useGrid // Use grid to speed up neighbor search ); void AddParticle(FluidParticle* particle); @@ -32,10 +32,10 @@ class SPHGrid private: - int m_width; // i - int m_height; // j - int m_depth; // k - float m_radius; // radius of particle influence + int m_width; // Uses i or x + int m_height; // Uses j or y + int m_depth; // Uses k or z + float m_cellSize; // radius of particle influence glm::vec3 m_gridMin; glm::vec3 m_gridMax; bool m_useGrid; @@ -44,6 +44,7 @@ class SPHGrid std::vector> m_cells; // Return the cell index + int GetCellIdx(const glm::vec3&); int GetCellIdx(int i, int j, int k); int GetTopCellIdx(int& i, int& j, int& k); diff --git a/src/fluidSolver/SPHSolver.cpp b/src/fluidSolver/SPHSolver.cpp index 9fb029dd..5b1ef083 100644 --- a/src/fluidSolver/SPHSolver.cpp +++ b/src/fluidSolver/SPHSolver.cpp @@ -11,12 +11,9 @@ SPHSolver::SPHSolver( { m_grid = new SPHGrid( m_particles, - 5, - 5, - 5, - 1.0f, -containerDim, containerDim, + 1.0f, false ); diff --git a/src/main.cpp b/src/main.cpp index 84d74607..15bd4037 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,7 +13,6 @@ void TestKernels() { float x[] = {0, 1.0f, 5.4f, 100.7f, -30.f, 36.f, -4.f}; float h[] = {10.f, 1.0f, 2.4f, 140.23f, -22.f, -2.f, 20.f}; - float expectedPoly6[] = {0.f, 0.0f, 0.f, 100.7f, 0.f, 0.f, 0.f}; cout << "TestKernels" << endl; From e327a11dff7d20ce3ac90d939c808120a55f9ec9 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Sat, 26 Mar 2016 18:19:41 -0400 Subject: [PATCH 34/64] Uniform grid search --- src/fluidSolver/SPHGrid.cpp | 56 +++++++++++++++++++++++++++++++---- src/fluidSolver/SPHGrid.h | 2 +- src/fluidSolver/SPHSolver.cpp | 2 +- 3 files changed, 53 insertions(+), 7 deletions(-) diff --git a/src/fluidSolver/SPHGrid.cpp b/src/fluidSolver/SPHGrid.cpp index 741cf1e6..03e03395 100644 --- a/src/fluidSolver/SPHGrid.cpp +++ b/src/fluidSolver/SPHGrid.cpp @@ -41,7 +41,8 @@ SPHGrid::AddParticle( // Add particle to grid based on its position int idx = GetCellIdx(particle->Position()); if (idx > m_cells.size()) { - cout << "Error adding particle " + idx << endl; + cerr << "Error adding particle " + idx << endl; + throw idx; return; } m_cells[idx].push_back(particle); @@ -53,9 +54,9 @@ SPHGrid::GetCellIdx( const glm::vec3& position ) { - int i = (position.x - m_gridMin.x) / m_cellSize; - int j = (position.y - m_gridMin.y) / m_cellSize; - int k = (position.z - m_gridMin.z) / m_cellSize; + int i = abs(position.x - m_gridMin.x) / m_cellSize; + int j = abs(position.y - m_gridMin.y) / m_cellSize; + int k = abs(position.z - m_gridMin.z) / m_cellSize; return this->GetCellIdx(i, j, k); } @@ -67,7 +68,24 @@ SPHGrid::GetCellIdx( int k ) { - return i + j * m_depth + k * m_width * m_height; + int idx = i + j * m_depth + k * m_width * m_height; + if (idx < 0) { + cerr << "Get cell idx is negative " + idx << endl; + throw idx; + } + return idx; +} + +glm::ivec3 +SPHGrid::GetCellCoord( + const glm::vec3& position + ) +{ + return glm::ivec3( + (position.x - m_gridMin.x) / m_cellSize, + (position.y - m_gridMin.y) / m_cellSize, + (position.z - m_gridMin.z) / m_cellSize + ); } std::vector @@ -75,6 +93,8 @@ SPHGrid::GetNeighborParticles( FluidParticle* particle ) { + vector neighbors; + // @todo: Use naive neighbor search if (!m_useGrid) { int len = m_cells[0].size(); @@ -91,6 +111,32 @@ SPHGrid::GetNeighborParticles( }); } else { + // -- Use grid to search neighbor + glm::ivec3 cellCoord = this->GetCellCoord(particle->Position()); + // Search in all 27 neighboring cells + for (int i = -1; i <= 1; ++i) { + for (int j = -1; j <= 1; ++j) { + for (int k = -1; k <= 1; ++k) { + + // Check if any neighbor is out of bound + if (cellCoord.x + i < 0 || cellCoord.y + j < 0 || cellCoord.z + k < 0) { + continue; + } + + int idx = this->GetCellIdx(i + cellCoord.x, j + cellCoord.y, k + cellCoord.z); + for(FluidParticle* neighbor : m_cells[idx]) { + if (glm::distance(neighbor->Position(), particle->Position()) < m_cellSize && + neighbor != particle) { + + // Change color of this neighbor + neighbor->SetColor(glm::vec4(1.0f, 0.0f, 0.0f, 1.0f)); + } else { + neighbor->SetColor(glm::vec4(0.0f, 1.0f, 1.0f, 1.0f)); + } + } + } + } + } } return neighbors; diff --git a/src/fluidSolver/SPHGrid.h b/src/fluidSolver/SPHGrid.h index 044b4bea..99ffda7b 100644 --- a/src/fluidSolver/SPHGrid.h +++ b/src/fluidSolver/SPHGrid.h @@ -46,7 +46,7 @@ class SPHGrid // Return the cell index int GetCellIdx(const glm::vec3&); int GetCellIdx(int i, int j, int k); - int GetTopCellIdx(int& i, int& j, int& k); + glm::ivec3 GetCellCoord(const glm::vec3&); }; diff --git a/src/fluidSolver/SPHSolver.cpp b/src/fluidSolver/SPHSolver.cpp index 5b1ef083..f57aa875 100644 --- a/src/fluidSolver/SPHSolver.cpp +++ b/src/fluidSolver/SPHSolver.cpp @@ -14,7 +14,7 @@ SPHSolver::SPHSolver( -containerDim, containerDim, 1.0f, - false + true ); // Initialize random seed From cfd71b1923924abfac34dc8351ea06dd0473edad Mon Sep 17 00:00:00 2001 From: Trung Le Date: Sun, 27 Mar 2016 11:43:52 -0400 Subject: [PATCH 35/64] API changes. --- src/fluidSolver/SPHGrid.cpp | 92 +++++++++++++-------- src/fluidSolver/SPHGrid.h | 9 +- src/fluidSolver/SPHSolver.cpp | 45 ++++++++-- src/fluidSolver/SPHSolver.h | 6 +- src/fluidSolver/fluidParticle.cpp | 9 ++ src/fluidSolver/fluidParticle.h | 17 +++- src/fluidSolver/fluidSolver.cpp | 10 ++- src/fluidSolver/fluidSolver.h | 4 +- src/geometry/fluidGeo.h | 4 + src/geometry/geometry.h | 6 +- src/mathConstants.h | 2 +- src/scene/scene.cpp | 29 +++---- src/scene/scene.h | 6 +- src/scene/scene.json | 9 +- src/shaderProgram/particleAdvectProgram.cpp | 6 +- src/shaderProgram/particleAdvectProgram.h | 1 + src/viewer.cpp | 5 +- 17 files changed, 179 insertions(+), 81 deletions(-) diff --git a/src/fluidSolver/SPHGrid.cpp b/src/fluidSolver/SPHGrid.cpp index 03e03395..1784e1c9 100644 --- a/src/fluidSolver/SPHGrid.cpp +++ b/src/fluidSolver/SPHGrid.cpp @@ -89,50 +89,72 @@ SPHGrid::GetCellCoord( } std::vector -SPHGrid::GetNeighborParticles( +SPHGrid::SearchNeighbors( + FluidParticle* particle + ) +{ + if (m_useGrid) { + return this->SearchNeighborsUniformGrid(particle); + } else { + return this->SearchNeighborsSimple(particle); + } +} + + +std::vector +SPHGrid::SearchNeighborsSimple( FluidParticle* particle ) { vector neighbors; - // @todo: Use naive neighbor search - if (!m_useGrid) { - int len = m_cells[0].size(); - parallel_for(0, len, [&](int i) { - FluidParticle* neighbor = m_cells[0][i]; - if (glm::distance(neighbor->Position(), particle->Position()) < m_cellSize && - neighbor != particle) { - - // Change color of this neighbor - neighbor->SetColor(glm::vec4(1.0f, 0.0f, 0.0f, 1.0f)); - } else { - neighbor->SetColor(glm::vec4(0.0f, 1.0f, 1.0f, 1.0f)); - } - }); - } else { + int len = m_cells[0].size(); + for (int i = 0; i < len; ++i) { + FluidParticle* neighbor = m_cells[0][i]; + if (glm::distance(neighbor->Position(), particle->Position()) < m_cellSize && + neighbor != particle) { - // -- Use grid to search neighbor - glm::ivec3 cellCoord = this->GetCellCoord(particle->Position()); - // Search in all 27 neighboring cells - for (int i = -1; i <= 1; ++i) { - for (int j = -1; j <= 1; ++j) { - for (int k = -1; k <= 1; ++k) { + // Change color of this neighbor + neighbor->SetColor(glm::vec4(1.0f, 0.0f, 0.0f, 1.0f)); + neighbors.push_back(neighbor); + } else { + neighbor->SetColor(glm::vec4(0.0f, 1.0f, 1.0f, 1.0f)); + } + }; - // Check if any neighbor is out of bound - if (cellCoord.x + i < 0 || cellCoord.y + j < 0 || cellCoord.z + k < 0) { - continue; - } + return neighbors; +} + +std::vector +SPHGrid::SearchNeighborsUniformGrid( + FluidParticle* particle + ) +{ + vector neighbors; + + // -- Use grid to search neighbor + glm::ivec3 cellCoord = this->GetCellCoord(particle->Position()); + // Search in all 27 neighboring cells + for (int i = -1; i <= 1; ++i) { + for (int j = -1; j <= 1; ++j) { + for (int k = -1; k <= 1; ++k) { + + // Check if any neighbor is out of bound + if (cellCoord.x + i < 0 || cellCoord.y + j < 0 || cellCoord.z + k < 0) { + continue; + } - int idx = this->GetCellIdx(i + cellCoord.x, j + cellCoord.y, k + cellCoord.z); - for(FluidParticle* neighbor : m_cells[idx]) { - if (glm::distance(neighbor->Position(), particle->Position()) < m_cellSize && - neighbor != particle) { + int idx = this->GetCellIdx(i + cellCoord.x, j + cellCoord.y, k + cellCoord.z); + glm::vec3 particlePosition = particle->Position(); + for(FluidParticle* neighbor : m_cells[idx]) { + if (glm::distance(neighbor->Position(), particlePosition) < m_cellSize && + neighbor != particle) { - // Change color of this neighbor - neighbor->SetColor(glm::vec4(1.0f, 0.0f, 0.0f, 1.0f)); - } else { - neighbor->SetColor(glm::vec4(0.0f, 1.0f, 1.0f, 1.0f)); - } + // Change color of this neighbor + neighbor->SetColor(glm::vec4(1.0f, 0.0f, 0.0f, 1.0f)); + neighbors.push_back(neighbor); + } else { + neighbor->SetColor(glm::vec4(0.0f, 1.0f, 1.0f, 1.0f)); } } } diff --git a/src/fluidSolver/SPHGrid.h b/src/fluidSolver/SPHGrid.h index 99ffda7b..069dd86c 100644 --- a/src/fluidSolver/SPHGrid.h +++ b/src/fluidSolver/SPHGrid.h @@ -27,8 +27,7 @@ class SPHGrid const bool useGrid // Use grid to speed up neighbor search ); void AddParticle(FluidParticle* particle); - std::vector GetParticles(int i, int j, int k); - std::vector GetNeighborParticles(FluidParticle*); + std::vector SearchNeighbors(FluidParticle*); private: @@ -40,14 +39,16 @@ class SPHGrid glm::vec3 m_gridMax; bool m_useGrid; - // -- cells std::vector> m_cells; - // Return the cell index + // -- Cell idx/coordinates int GetCellIdx(const glm::vec3&); int GetCellIdx(int i, int j, int k); glm::ivec3 GetCellCoord(const glm::vec3&); + // -- Neighbor search algorithms + std::vector SearchNeighborsSimple(FluidParticle*); + std::vector SearchNeighborsUniformGrid(FluidParticle*); }; #endif diff --git a/src/fluidSolver/SPHSolver.cpp b/src/fluidSolver/SPHSolver.cpp index f57aa875..9e3a4012 100644 --- a/src/fluidSolver/SPHSolver.cpp +++ b/src/fluidSolver/SPHSolver.cpp @@ -6,19 +6,17 @@ SPHSolver::SPHSolver( const glm::vec3& containerDim, const glm::vec3& particleDim, - const float& separation + const float separation, + const float cellSize ) : FluidSolver(containerDim, particleDim, separation) { m_grid = new SPHGrid( m_particles, -containerDim, containerDim, - 1.0f, + cellSize, true ); - - // Initialize random seed - srand (time(NULL)); } SPHSolver::~SPHSolver() @@ -27,18 +25,21 @@ SPHSolver::~SPHSolver() } void -SPHSolver::Update() +SPHSolver::Update( + const float deltaT + ) { SearchNeighbors(); CalculateDensity(); CalculatePressure(); + Advect(deltaT); } void SPHSolver::SearchNeighbors() { static int i = 0; - m_grid->GetNeighborParticles(m_particles[i]); + m_grid->SearchNeighbors(m_particles[i]); i = (i + 1) % m_particles.size(); } @@ -51,3 +52,33 @@ void SPHSolver::CalculatePressure() { } + +void +SPHSolver::Advect( + const float deltaT + ) +{ + for (FluidParticle* p : m_particles) { + + // Check boundary + glm::vec3 newForce(0.0f); + if (p->Position().x < m_minBoundary.x || p->Position().x > m_maxBoundary.x) { + newForce.x = -p->Velocity().x; + } + + if (p->Position().y < m_minBoundary.y) { + newForce.y = 0.2f; + } else if (p->Position().y > m_maxBoundary.y) { + newForce.y = -0.0f; + } + + if (p->Position().z < m_minBoundary.z || p->Position().z > m_maxBoundary.z) { + newForce.z = -p->Velocity().z; + } + + p->SetForce(newForce); + + // Update kinematic + p->Update(deltaT); + } +} diff --git a/src/fluidSolver/SPHSolver.h b/src/fluidSolver/SPHSolver.h index 37db260d..902762b9 100644 --- a/src/fluidSolver/SPHSolver.h +++ b/src/fluidSolver/SPHSolver.h @@ -13,12 +13,13 @@ class SPHSolver : public FluidSolver SPHSolver( const glm::vec3& containerDim, const glm::vec3& particleDim, - const float& separation + const float separation, + const float cellSize ); virtual ~SPHSolver(); - virtual void Update(); + virtual void Update(const float deltaT); void AddParticle(FluidParticle*); protected: @@ -36,6 +37,7 @@ class SPHSolver : public FluidSolver void SearchNeighbors(); void CalculateDensity(); void CalculatePressure(); + void Advect(const float deltaT); }; #endif /* SPHSOLVER_H */ diff --git a/src/fluidSolver/fluidParticle.cpp b/src/fluidSolver/fluidParticle.cpp index 896159dc..70bec0fd 100644 --- a/src/fluidSolver/fluidParticle.cpp +++ b/src/fluidSolver/fluidParticle.cpp @@ -17,3 +17,12 @@ FluidParticle::FluidParticle( } +void +FluidParticle::Update( + const float deltaT + ) +{ + glm::vec3 gravity(0.0f, GRAVITY, 0.0f); + m_vel = m_vel + (m_accel + gravity) * deltaT; + m_pos = m_pos + m_vel * deltaT * 0.1f; +} diff --git a/src/fluidSolver/fluidParticle.h b/src/fluidSolver/fluidParticle.h index cf9de837..2fa6aa70 100644 --- a/src/fluidSolver/fluidParticle.h +++ b/src/fluidSolver/fluidParticle.h @@ -1,7 +1,12 @@ #ifndef FLUID_PARTICLE_H #define FLUID_PARTICLE_H +#include + #include +#include + +using namespace std; // ---------------------------------------------------- // // FluidParticle @@ -16,18 +21,28 @@ class FluidParticle const glm::vec4& col ); - // Getters/setters + virtual void Update(const float deltaT); + + // -- Getters/setters const glm::vec3& Position() const { return m_pos; } + void SetPosition(const glm::vec3& pos) { m_pos = pos; } const glm::vec3& Velocity() const { return m_vel; } + void SetVelocity(const glm::vec3& vel) { m_vel = vel; } + const glm::vec3& Acceleration() const { return m_accel; } + void SetForce(const glm::vec3& force) { m_accel = force / 1.0f; } // Mass is 1 const glm::vec4& Color() const { return m_col; } void SetColor(const glm::vec4& color) { m_col = color; } const float& Density() const { return m_density; } + void SetDensity(float density) { m_density = density; } const float& Pressure() const { return m_pressure; } + void SetPressure(float pressure) { m_pressure = pressure; } const float& SpawnTime() const { return m_spawnTime; } + void SetSpawnTime(float spawnTime) { m_spawnTime = spawnTime; } protected: glm::vec3 m_pos; glm::vec3 m_vel; + glm::vec3 m_accel; glm::vec4 m_col; float m_density = 1.0f; float m_pressure = 1.0f; diff --git a/src/fluidSolver/fluidSolver.cpp b/src/fluidSolver/fluidSolver.cpp index a6b79956..6616ad51 100644 --- a/src/fluidSolver/fluidSolver.cpp +++ b/src/fluidSolver/fluidSolver.cpp @@ -24,13 +24,17 @@ FluidSolver::FluidSolver( m_particles.push_back( new FluidParticle( glm::vec3(x, y, z), - glm::vec3(0.f, 0.0001f, 0.0f), + glm::vec3(0.f, 0.0f, 0.0f), glm::vec4(0.f, 0.f, 1.0f, 1.0f) ) ); } } } + + // -- Set container boundary + m_minBoundary = -m_containerDim / 2.0f; + m_maxBoundary = m_containerDim / 2.0f; } FluidSolver::~FluidSolver() @@ -93,6 +97,8 @@ FluidSolver::ParticleColors() const } void -FluidSolver::Update() +FluidSolver::Update( + const float deltaT + ) { } diff --git a/src/fluidSolver/fluidSolver.h b/src/fluidSolver/fluidSolver.h index cee7a7f2..c4bca1a1 100644 --- a/src/fluidSolver/fluidSolver.h +++ b/src/fluidSolver/fluidSolver.h @@ -28,13 +28,15 @@ class FluidSolver const std::vector ParticleSpawnTimes() const; const std::vector ParticleColors() const; - virtual void Update(); + virtual void Update(const float deltaT); protected: // -- Particle info glm::vec3 m_containerDim; glm::vec3 m_particleDim; + glm::vec3 m_minBoundary; + glm::vec3 m_maxBoundary; float m_separation; std::vector m_particles; diff --git a/src/geometry/fluidGeo.h b/src/geometry/fluidGeo.h index 0e80fbbe..40fe3cda 100644 --- a/src/geometry/fluidGeo.h +++ b/src/geometry/fluidGeo.h @@ -26,10 +26,14 @@ class FluidGeo : public Geometry void ToggleVao(); + // Getters/Setters GLuint PosBuffer() const; GLuint VelBuffer() const; GLuint SpawnTimeBuffer() const; GLuint ColBuffer() const; + void SetVelocities(const vector& velocities) { + m_velocities = velocities; + } protected: virtual void InitIndices(); diff --git a/src/geometry/geometry.h b/src/geometry/geometry.h index 1fe28647..459b3a16 100644 --- a/src/geometry/geometry.h +++ b/src/geometry/geometry.h @@ -48,11 +48,15 @@ class Geometry virtual void EnableVertexAttributes() const; virtual void DisableVertexAttributes() const; - // -- Color + // Getters/Setters void SetColors(const vector& colors) { m_colors = colors; }; + void SetPositions(const std::vector& positions) { + m_positions = positions; + } + // -- Transformations void Translate(const float& x, const float& y, const float& z); void Rotate(const float& radx, const float& rady, const float& radz); diff --git a/src/mathConstants.h b/src/mathConstants.h index 08d33aba..b3f0440b 100644 --- a/src/mathConstants.h +++ b/src/mathConstants.h @@ -8,6 +8,6 @@ static const float TWO_PI = 2 * PI; static const float INV_PI = 1.0f / PI; static const float DEG2RAD = PI / 180.f; static const float RAD2DEG = 180.f / PI; -static const float GRAVITY = -9.8f; +static const float GRAVITY = -0.05f; #endif diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 53efebc7..33ddfb69 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -66,11 +66,14 @@ Scene::InitFromJson( const float separation = root["particleSeparation"].asFloat(); + const float cellSize = root["cellSize"].asFloat(); + // -- Initialize fluid solvers m_fluidSolver = new SPHSolver( containerDim, particleDim, - separation + separation, + cellSize ); // -- Initialize fluid geo @@ -85,19 +88,13 @@ Scene::InitFromJson( void Scene::Update( + const float deltaT, const KeyboardControl* kc, ParticleAdvectProgram& progAdvect ) { - UpdateCamera(kc); - -#ifdef TEST_SCENE - // Rotate bounding box - m_testBox->Rotate(0.02f, 0.01f, 0.02); - return; -#endif - - UpdateFluidSolver(progAdvect); + UpdateCamera(deltaT, kc); + UpdateFluidSolver(deltaT, progAdvect); } void @@ -105,10 +102,6 @@ Scene::Draw( const ShaderProgram& prog ) const { -#ifdef TEST_SCENE - prog.Draw(*m_camera, *m_testBox); - return; -#endif } void @@ -148,6 +141,7 @@ Scene::CleanUp() void Scene::UpdateCamera( + const float deltaT, const KeyboardControl* kc ) { @@ -187,13 +181,16 @@ Scene::UpdateCamera( void Scene::UpdateFluidSolver( + const float deltaT, ParticleAdvectProgram& progAdvect ) { - m_fluidSolver->Update(); + m_fluidSolver->Update(deltaT); + m_fluidGeo->SetVelocities(m_fluidSolver->ParticleVelocities()); + m_fluidGeo->SetPositions(m_fluidSolver->ParticlePositions()); // Advect particle? - progAdvect.Advect(m_fluidGeo); + progAdvect.Advect(deltaT, m_fluidGeo); } void diff --git a/src/scene/scene.h b/src/scene/scene.h index 419ed425..7e8c9269 100644 --- a/src/scene/scene.h +++ b/src/scene/scene.h @@ -21,6 +21,7 @@ class Scene void InitFromJson(const char* filepath); virtual void Update( + const float deltaT, const KeyboardControl*, ParticleAdvectProgram& prog ); @@ -39,8 +40,11 @@ class Scene SPHSolver* m_fluidSolver; // -- Update helpers - void UpdateCamera(const KeyboardControl*); + void UpdateCamera( + const float deltaT, + const KeyboardControl*); void UpdateFluidSolver( + const float deltaT, ParticleAdvectProgram& prog ); diff --git a/src/scene/scene.json b/src/scene/scene.json index be29843d..63cf1952 100644 --- a/src/scene/scene.json +++ b/src/scene/scene.json @@ -5,9 +5,10 @@ "scaleZ" : 10.0 }, "particleDim" : { - "boundX" : 3.7, - "boundY" : 3.7, - "boundZ" : 3.7 + "boundX" : 4.0, + "boundY" : 4.0, + "boundZ" : 4.0 }, - "particleSeparation" : 0.1 + "particleSeparation" : 0.2, + "cellSize" : 1.0 } diff --git a/src/shaderProgram/particleAdvectProgram.cpp b/src/shaderProgram/particleAdvectProgram.cpp index af2d0f8f..09925b5d 100644 --- a/src/shaderProgram/particleAdvectProgram.cpp +++ b/src/shaderProgram/particleAdvectProgram.cpp @@ -54,6 +54,7 @@ ParticleAdvectProgram::ParticleAdvectProgram( void ParticleAdvectProgram::Advect( + const float deltaTime, FluidGeo* fluidGeo ) { @@ -61,11 +62,6 @@ ParticleAdvectProgram::Advect( // -- Setup uniforms - // @todo: maybe we only need to pass delta time through - static float lastTime = glfwGetTime(); - float currentTime = glfwGetTime(); - float deltaTime = float(currentTime - lastTime); - if (m_unifAdvectTime != -1) { glUniform1f( m_unifAdvectTime, deltaTime diff --git a/src/shaderProgram/particleAdvectProgram.h b/src/shaderProgram/particleAdvectProgram.h index 546a02c6..08a70824 100644 --- a/src/shaderProgram/particleAdvectProgram.h +++ b/src/shaderProgram/particleAdvectProgram.h @@ -20,6 +20,7 @@ class ParticleAdvectProgram // First pass virtual void Advect( + const float deltaTime, FluidGeo* ); diff --git a/src/viewer.cpp b/src/viewer.cpp index 222e9a31..6d368d41 100644 --- a/src/viewer.cpp +++ b/src/viewer.cpp @@ -95,7 +95,10 @@ Viewer::Update() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - m_scene->Update(m_keyboard, *m_programAdvect); + static float lastTime = glfwGetTime(); + float currentTime = glfwGetTime(); + float deltaTime = float(currentTime - lastTime); + m_scene->Update(deltaTime, m_keyboard, *m_programAdvect); #ifdef TEST_SCENE m_scene->Draw(*m_program); From b5026e4b5a16471d0d8495bd10fe58cd9f9e160a Mon Sep 17 00:00:00 2001 From: Trung Le Date: Tue, 29 Mar 2016 20:20:49 -0400 Subject: [PATCH 36/64] Added easy logging. Updated fluid in the correct group order. Clamp pressure. Updated fluid constants (from json file) --- CMakeLists.txt | 3 +- README.md | 1 + src/camera/camera.cpp | 38 +- src/camera/camera.h | 4 +- src/fluidSolver/SPHGrid.cpp | 51 +- src/fluidSolver/SPHGrid.h | 2 + src/fluidSolver/SPHKernels.h | 19 +- src/fluidSolver/SPHSolver.cpp | 150 +- src/fluidSolver/SPHSolver.h | 32 +- src/fluidSolver/fluidConstants.h | 5 - src/fluidSolver/fluidParticle.cpp | 15 +- src/fluidSolver/fluidParticle.h | 11 +- src/fluidSolver/fluidSolver.cpp | 10 +- src/fluidSolver/fluidSolver.h | 5 +- src/input/keyboardControl.cpp | 86 - src/input/keyboardControl.h | 4 +- src/main.cpp | 32 +- src/mathConstants.h | 2 +- src/scene/scene.cpp | 28 +- src/scene/scene.h | 1 + src/scene/scene.json | 15 +- src/shaderProgram/particleAdvectProgram.cpp | 3 + src/thirdparty/easylogging++.h | 6663 +++++++++++++++++++ src/viewer.cpp | 4 +- 24 files changed, 6980 insertions(+), 204 deletions(-) delete mode 100644 src/fluidSolver/fluidConstants.h create mode 100755 src/thirdparty/easylogging++.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b3750d3..c1b26881 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -43,6 +43,7 @@ find_package(GLEW) find_library(GLFW_LIBRARY "glfw3" HINTS ${GLFW_LIBRARY_DIR}) find_library(JSONCPP "jsoncpp") find_library(TBB "tbb") +find_library(OPENVDB "openvdb") add_definitions( -DTW_STATIC @@ -52,7 +53,7 @@ add_definitions( -D_CRT_SECURE_NO_WARNINGS ) -set(CORE_LIBS ${GLFW_LIBRARY} ${GLUT_LIBRARY} ${GLEW_LIBRARY} ${JSONCPP} ${TBB} ${OPENGL_LIBRARY} ) +set(CORE_LIBS ${GLFW_LIBRARY} ${GLUT_LIBRARY} ${GLEW_LIBRARY} ${JSONCPP} ${TBB} ${OPENVDB} ${OPENGL_LIBRARY} ) # OSX-specific hacks/fixes if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") diff --git a/README.md b/README.md index 4ad57e94..13b1086e 100644 --- a/README.md +++ b/README.md @@ -117,3 +117,4 @@ See Camera class section above. - glm, [Website](http://glm.g-truc.net/0.9.7/index.html) - opengl-tutorial.org, [Website](http://www.opengl-tutorial.org/) - jsoncpp, [Github](https://github.com/open-source-parsers/jsoncpp) +- easylogging++, [Github](https://github.com/easylogging/easyloggingpp) diff --git a/src/camera/camera.cpp b/src/camera/camera.cpp index 9dea616d..233339b3 100644 --- a/src/camera/camera.cpp +++ b/src/camera/camera.cpp @@ -12,7 +12,7 @@ Camera::Camera( ) : m_width(width), m_height(height), m_fov(45), - m_eye(glm::vec3(0.0f, 0.0f, -20.0f)), + m_eye(glm::vec3(10.0f, 10.0f, -20.0f)), m_target(glm::vec3(0.0f)), m_up(glm::vec3(0.0f, 1.0f, 0.0f)), m_right(glm::vec3(1.0f, 0.0f, 0.0f)), @@ -23,6 +23,21 @@ Camera::Camera( RecomputeAttributes(); } +void +Camera::EnablePerspective( + bool enabled + ) +{ + m_isPerspective = enabled; + + if (!m_isPerspective) { + m_eye = glm::vec3(0.0, 0.0, 10.0); + } else { + m_eye = glm::vec3(10.0f, 10.0f, -20.0f); + } + RecomputeAttributes(); +} + void Camera::RecomputeAttributes() { @@ -35,18 +50,27 @@ Camera::RecomputeAttributes() m_up ); - m_perspective = glm::perspective( - m_fov, - m_width / m_height, - m_nearClip, - m_farClip + if (m_isPerspective) { + m_proj = glm::perspective( + m_fov, + m_width / m_height, + m_nearClip, + m_farClip + ); + } else { + m_proj = glm::ortho( + 0.0f, + m_width, + 0.0f, + m_height ); + } } glm::mat4 Camera::GetViewProj() const { - return m_perspective * m_view; + return m_proj * m_view; } void diff --git a/src/camera/camera.h b/src/camera/camera.h index 122694a5..a820c3d2 100644 --- a/src/camera/camera.h +++ b/src/camera/camera.h @@ -12,6 +12,7 @@ class Camera // -- Camera attributes glm::mat4 GetViewProj() const; + void EnablePerspective(bool enabled); void RecomputeAttributes(); // -- Camera movement @@ -36,7 +37,8 @@ class Camera float m_farClip; glm::mat4 m_view; - glm::mat4 m_perspective; + glm::mat4 m_proj; + bool m_isPerspective = true; }; diff --git a/src/fluidSolver/SPHGrid.cpp b/src/fluidSolver/SPHGrid.cpp index 1784e1c9..a6582694 100644 --- a/src/fluidSolver/SPHGrid.cpp +++ b/src/fluidSolver/SPHGrid.cpp @@ -18,6 +18,9 @@ SPHGrid::SPHGrid( m_height = ceil((m_gridMax.y - m_gridMin.y) / m_cellSize); m_depth = ceil((m_gridMax.z - m_gridMin.z) / m_cellSize); m_cells.resize(m_width * m_height * m_depth); + + LOG(INFO) << "Number of particles: " << particles.size() << endl; + LOG(INFO) << "Grid dim: " << m_width << ", " << m_height << ", " << m_depth << endl; } else { // Grid size is only 1 m_cells.resize(1); @@ -26,6 +29,21 @@ SPHGrid::SPHGrid( for(FluidParticle* p : particles) { AddParticle(p); } + +} + +void +InitializeVdb() +{ + // // -- Initialize openvdb + // openvdb::initialize(); + + // // Create an empty floating-point grid with background value 0. + // openvdb::FloatGrid::Ptr grid = openvdb::FloatGrid::create(); + + // // Get an accessor for coordinate-based access to voxels. + // openvdb::FloatGrid::Accessor accessor = grid->getAccessor(); + } // Add particle to grid based on its position @@ -40,9 +58,8 @@ SPHGrid::AddParticle( } else { // Add particle to grid based on its position int idx = GetCellIdx(particle->Position()); - if (idx > m_cells.size()) { - cerr << "Error adding particle " + idx << endl; - throw idx; + if (idx > m_cells.size() || idx < 0) { + LOG(WARNING) << "Error adding particle " + idx << endl; return; } m_cells[idx].push_back(particle); @@ -58,6 +75,14 @@ SPHGrid::GetCellIdx( int j = abs(position.y - m_gridMin.y) / m_cellSize; int k = abs(position.z - m_gridMin.z) / m_cellSize; + // -- Error checking + if (i < 0 || j < 0 || k < 0 || i >= m_width || j >= m_height || k >= m_depth) { + LOG(WARNING) << "WARNING GetCellIdx "<< endl; + LOG(WARNING) << "x: "<< position.x << ", y: " << position.y << ", z: " << position.z << endl; + LOG(WARNING) << "i: "<< i << ", j: " << j << ", k: " << k << endl; + return -1; + } + return this->GetCellIdx(i, j, k); } @@ -70,8 +95,7 @@ SPHGrid::GetCellIdx( { int idx = i + j * m_depth + k * m_width * m_height; if (idx < 0) { - cerr << "Get cell idx is negative " + idx << endl; - throw idx; + LOG(ERROR) << "Get cell idx is negative " + idx << endl; } return idx; } @@ -113,12 +137,7 @@ SPHGrid::SearchNeighborsSimple( FluidParticle* neighbor = m_cells[0][i]; if (glm::distance(neighbor->Position(), particle->Position()) < m_cellSize && neighbor != particle) { - - // Change color of this neighbor - neighbor->SetColor(glm::vec4(1.0f, 0.0f, 0.0f, 1.0f)); neighbors.push_back(neighbor); - } else { - neighbor->SetColor(glm::vec4(0.0f, 1.0f, 1.0f, 1.0f)); } }; @@ -140,7 +159,12 @@ SPHGrid::SearchNeighborsUniformGrid( for (int k = -1; k <= 1; ++k) { // Check if any neighbor is out of bound - if (cellCoord.x + i < 0 || cellCoord.y + j < 0 || cellCoord.z + k < 0) { + if (cellCoord.x + i < 0 || + cellCoord.y + j < 0 || + cellCoord.z + k < 0 || + cellCoord.x + i >= m_width || + cellCoord.y + j >= m_height || + cellCoord.z + k >= m_depth) { continue; } @@ -149,12 +173,7 @@ SPHGrid::SearchNeighborsUniformGrid( for(FluidParticle* neighbor : m_cells[idx]) { if (glm::distance(neighbor->Position(), particlePosition) < m_cellSize && neighbor != particle) { - - // Change color of this neighbor - neighbor->SetColor(glm::vec4(1.0f, 0.0f, 0.0f, 1.0f)); neighbors.push_back(neighbor); - } else { - neighbor->SetColor(glm::vec4(0.0f, 1.0f, 1.0f, 1.0f)); } } } diff --git a/src/fluidSolver/SPHGrid.h b/src/fluidSolver/SPHGrid.h index 069dd86c..8e2c1444 100644 --- a/src/fluidSolver/SPHGrid.h +++ b/src/fluidSolver/SPHGrid.h @@ -6,6 +6,8 @@ #include #include #include +#include +#include #include #include diff --git a/src/fluidSolver/SPHKernels.h b/src/fluidSolver/SPHKernels.h index b967e51c..a9af3834 100644 --- a/src/fluidSolver/SPHKernels.h +++ b/src/fluidSolver/SPHKernels.h @@ -3,6 +3,7 @@ #define _USE_MATH_DEFINES #include +#include inline float KernelPoly6(float x, float h) { @@ -14,7 +15,7 @@ inline float KernelPoly6(float x, float h) return 0; } - return (315 * pow(pow(h, 2) - pow(x, 2), 3)) / (64 * M_PI * pow(h, 9)); + return (315 * pow(pow(h, 2.0f) - pow(x, 2.0f), 3.0f)) / (64.0f * M_PI * pow(h, 9.0f)); } inline float KernelSpiky(float x, float h) @@ -27,7 +28,21 @@ inline float KernelSpiky(float x, float h) return 0; } - return (15 * pow(h - x, 3)) / (M_PI * pow(h, 6)); + return (15.0f * pow(h - x, 3.0f)) / (M_PI * pow(h, 6.0f)); +} + +inline glm::vec3 GradKernelSpiky(glm::vec3 x_vec, float x, float h) +{ + if (x <= 0 || x > h) { + return glm::vec3(0.0f); + } + + if (h <= 0) { + return glm::vec3(0.0f); + } + + float scalarPart = -45.0f * pow ((h - x), 2.0f) / (M_PI * pow(h, 6.0f)); + return scalarPart * x_vec; } inline float KernelViscous(float x, float h) diff --git a/src/fluidSolver/SPHSolver.cpp b/src/fluidSolver/SPHSolver.cpp index 9e3a4012..2dd7dc80 100644 --- a/src/fluidSolver/SPHSolver.cpp +++ b/src/fluidSolver/SPHSolver.cpp @@ -7,16 +7,21 @@ SPHSolver::SPHSolver( const glm::vec3& containerDim, const glm::vec3& particleDim, const float separation, - const float cellSize - ) : FluidSolver(containerDim, particleDim, separation) + const float cellSize, + const float stiffness, + const float viscosity, + const float mass, + const float restDensity, + const float timestep + ) : FluidSolver(containerDim, particleDim, separation, mass), + m_grid(nullptr), + m_cellSize(cellSize), + m_stiffness(stiffness), + m_viscosity(viscosity), + m_restDensity(restDensity), + m_timestep(timestep) { - m_grid = new SPHGrid( - m_particles, - -containerDim, - containerDim, - cellSize, - true - ); + srand(time(NULL)); } SPHSolver::~SPHSolver() @@ -29,56 +34,123 @@ SPHSolver::Update( const float deltaT ) { - SearchNeighbors(); - CalculateDensity(); - CalculatePressure(); - Advect(deltaT); + if (m_grid != nullptr) { + delete m_grid; + } + m_grid = new SPHGrid( + m_particles, + -m_containerDim, + m_containerDim, + m_cellSize, + true + ); + + std::vector neighbors; + for (FluidParticle* p : m_particles) { + neighbors.clear(); + neighbors = m_grid->SearchNeighbors(p); + this->CalculateDensity(p, neighbors); + this->CalculatePressure(p); + } + + for (FluidParticle* p : m_particles) { + neighbors.clear(); + neighbors = m_grid->SearchNeighbors(p); + this->CalculatePressureForceField(p, neighbors); + } + + for (FluidParticle* p : m_particles) { + this->UpdateDynamics(p, deltaT); + } } void -SPHSolver::SearchNeighbors() +SPHSolver::CalculateDensity( + FluidParticle* particle, + const std::vector& neighbors + ) { - static int i = 0; - m_grid->SearchNeighbors(m_particles[i]); - i = (i + 1) % m_particles.size(); + // -- Compute density over a kernel function of neighbors + float density = 0.f; + for (FluidParticle* neighbor : neighbors) { + float tempDensity = KernelPoly6(glm::distance(neighbor->Position(), particle->Position()), m_cellSize); + density += tempDensity; + } + density = m_mass * density; + particle->SetDensity(density); } void -SPHSolver::CalculateDensity() +SPHSolver::CalculatePressure( + FluidParticle* particle + ) { + float pressure = m_stiffness * (pow((particle->Density() / m_restDensity), 7.0f) - 1.0f); + + // Clamp from being negative + pressure = pressure < 0.0 ? 0.0 : pressure; + particle->SetPressure(pressure); } void -SPHSolver::CalculatePressure() +SPHSolver::CalculatePressureForceField( + FluidParticle* particle, + const std::vector& neighbors + ) { + // -- Compute pressure gradient + glm::vec3 pressureGrad; + for (FluidParticle* neighbor : neighbors) { + glm::vec3 r = neighbor->Position() - particle->Position(); + glm::vec3 kernelGrad = GradKernelSpiky(r, glm::distance(neighbor->Position(), particle->Position()), m_cellSize); + + glm::vec3 tempPressureForce = + ( + particle->Pressure() / (pow(particle->Density(), 2.0f)) + + neighbor->Pressure() / (pow(neighbor->Density(), 2.0f)) + ) * kernelGrad; + pressureGrad += tempPressureForce; + } + pressureGrad = pressureGrad * m_mass * m_mass; + // LOG(DEBUG) << "Pressure x: " << pressureGrad.x << ", y: " << pressureGrad.y << ", z: " << pressureGrad.z << endl; + particle->SetPressureForce(-pressureGrad); } void -SPHSolver::Advect( +SPHSolver::UpdateDynamics( + FluidParticle* particle, const float deltaT ) { - for (FluidParticle* p : m_particles) { - - // Check boundary - glm::vec3 newForce(0.0f); - if (p->Position().x < m_minBoundary.x || p->Position().x > m_maxBoundary.x) { - newForce.x = -p->Velocity().x; - } - - if (p->Position().y < m_minBoundary.y) { - newForce.y = 0.2f; - } else if (p->Position().y > m_maxBoundary.y) { - newForce.y = -0.0f; - } + particle->Update(deltaT); - if (p->Position().z < m_minBoundary.z || p->Position().z > m_maxBoundary.z) { - newForce.z = -p->Velocity().z; - } + // Check boundary + glm::vec3 newPosition = particle->Position(); + glm::vec3 newVel = particle->Velocity(); + if (particle->Position().x < m_minBoundary.x) { + newPosition.x = m_minBoundary.x + 0.0f; + newVel.x = -newVel.x * 0.2; + } else if (particle->Position().x > m_maxBoundary.x) { + newPosition.x = m_maxBoundary.x - 0.0f; + newVel.x = -newVel.x * 0.2; + } - p->SetForce(newForce); + if (particle->Position().y < m_minBoundary.y) { + newPosition.y = m_minBoundary.y + 0.0f; + newVel.y = -newVel.y * 0.2; + } else if (particle->Position().y > m_maxBoundary.y) { + newPosition.y = m_maxBoundary.y - 0.0f; + newVel.y = -newVel.y * 0.2f; + } - // Update kinematic - p->Update(deltaT); + if (particle->Position().z < m_minBoundary.z) { + newPosition.z = m_minBoundary.z + 0.0f; + newVel.z = -newVel.z * 0.2; + } else if (particle->Position().z > m_maxBoundary.z) { + newPosition.z = m_maxBoundary.z - 0.0f; + newVel.z = -newVel.z * 0.2; } + + particle->SetPosition(newPosition); + particle->SetVelocity(newVel); } diff --git a/src/fluidSolver/SPHSolver.h b/src/fluidSolver/SPHSolver.h index 902762b9..9f3c220a 100644 --- a/src/fluidSolver/SPHSolver.h +++ b/src/fluidSolver/SPHSolver.h @@ -3,6 +3,7 @@ #include #include +#include // ---------------------------------------------------- // // SPHSolver @@ -14,7 +15,12 @@ class SPHSolver : public FluidSolver const glm::vec3& containerDim, const glm::vec3& particleDim, const float separation, - const float cellSize + const float cellSize, + const float stiffness, + const float viscosity, + const float mass, + const float restDensity, + const float timestep ); virtual ~SPHSolver(); @@ -27,17 +33,27 @@ class SPHSolver : public FluidSolver SPHGrid* m_grid; // -- Particle info + float m_cellSize; float m_stiffness; float m_viscosity; - float m_radius; - float m_mas; float m_restDensity; - float m_timeStep; + float m_timestep; - void SearchNeighbors(); - void CalculateDensity(); - void CalculatePressure(); - void Advect(const float deltaT); + void CalculateDensity( + FluidParticle* particle, + const std::vector& neighbors + ); + void CalculatePressure( + FluidParticle* particle + ); + void CalculatePressureForceField( + FluidParticle* particle, + const std::vector& neighbors + ); + void UpdateDynamics( + FluidParticle* particle, + const float deltaT + ); }; #endif /* SPHSOLVER_H */ diff --git a/src/fluidSolver/fluidConstants.h b/src/fluidSolver/fluidConstants.h deleted file mode 100644 index 3d4ef185..00000000 --- a/src/fluidSolver/fluidConstants.h +++ /dev/null @@ -1,5 +0,0 @@ -#ifndef FLUID_CONSTANTS_H -#define FLUID_CONSTANTS_H - - -#endif diff --git a/src/fluidSolver/fluidParticle.cpp b/src/fluidSolver/fluidParticle.cpp index 70bec0fd..f8c4492d 100644 --- a/src/fluidSolver/fluidParticle.cpp +++ b/src/fluidSolver/fluidParticle.cpp @@ -19,10 +19,15 @@ FluidParticle::FluidParticle( void FluidParticle::Update( - const float deltaT - ) + const float deltaT + ) { - glm::vec3 gravity(0.0f, GRAVITY, 0.0f); - m_vel = m_vel + (m_accel + gravity) * deltaT; - m_pos = m_pos + m_vel * deltaT * 0.1f; + glm::vec3 gravity(0.0f, GRAVITY, 0.0f); + m_vel = m_vel + (m_accel + m_pressureForce + gravity) * deltaT; + // cout << "Position b4: " << m_pos.y << endl; + // cout << "Vel: " << m_vel.y << endl; + // cout << "Accel: " << m_accel.y << endl; + // cout << "Pressure force: " << m_pressureForce.y << endl; + m_pos = m_pos + m_vel * deltaT; + // cout << "Position after: " << m_pos.y << endl; } diff --git a/src/fluidSolver/fluidParticle.h b/src/fluidSolver/fluidParticle.h index 2fa6aa70..9e415183 100644 --- a/src/fluidSolver/fluidParticle.h +++ b/src/fluidSolver/fluidParticle.h @@ -29,13 +29,15 @@ class FluidParticle const glm::vec3& Velocity() const { return m_vel; } void SetVelocity(const glm::vec3& vel) { m_vel = vel; } const glm::vec3& Acceleration() const { return m_accel; } - void SetForce(const glm::vec3& force) { m_accel = force / 1.0f; } // Mass is 1 + void SetForce(const glm::vec3& force) { m_accel = force / 0.125f; } // Mass is 1 const glm::vec4& Color() const { return m_col; } void SetColor(const glm::vec4& color) { m_col = color; } const float& Density() const { return m_density; } void SetDensity(float density) { m_density = density; } - const float& Pressure() const { return m_pressure; } - void SetPressure(float pressure) { m_pressure = pressure; } + const float Pressure() const { return m_pressure; } + void SetPressure(const float pressure) { m_pressure = pressure; } + const glm::vec3& PressureForce() const { return m_pressureForce; } + void SetPressureForce(const glm::vec3& pressureForce) { m_pressureForce = pressureForce; } const float& SpawnTime() const { return m_spawnTime; } void SetSpawnTime(float spawnTime) { m_spawnTime = spawnTime; } @@ -44,8 +46,9 @@ class FluidParticle glm::vec3 m_vel; glm::vec3 m_accel; glm::vec4 m_col; + glm::vec3 m_pressureForce; + float m_pressure = 0.0f; float m_density = 1.0f; - float m_pressure = 1.0f; float m_spawnTime = 0.0f; }; diff --git a/src/fluidSolver/fluidSolver.cpp b/src/fluidSolver/fluidSolver.cpp index 6616ad51..e4b7fd13 100644 --- a/src/fluidSolver/fluidSolver.cpp +++ b/src/fluidSolver/fluidSolver.cpp @@ -6,10 +6,12 @@ FluidSolver::FluidSolver( const glm::vec3& containerDim, const glm::vec3& particleDim, - const float& separation + const float separation, + const float mass ) : m_containerDim(containerDim), m_particleDim(particleDim), - m_separation(separation) + m_separation(separation), + m_mass(mass) { // -- Initialize all particles float midX = m_particleDim.x / 2.f; @@ -24,8 +26,8 @@ FluidSolver::FluidSolver( m_particles.push_back( new FluidParticle( glm::vec3(x, y, z), - glm::vec3(0.f, 0.0f, 0.0f), - glm::vec4(0.f, 0.f, 1.0f, 1.0f) + glm::vec3(0.0f, 0.0f, 0.0f), + glm::vec4(0.3f, 0.3f, 1.0f, 1.0f) ) ); } diff --git a/src/fluidSolver/fluidSolver.h b/src/fluidSolver/fluidSolver.h index c4bca1a1..33bdbba0 100644 --- a/src/fluidSolver/fluidSolver.h +++ b/src/fluidSolver/fluidSolver.h @@ -8,6 +8,7 @@ #include #include +#include #include // ---------------------------------------------------- // @@ -19,7 +20,8 @@ class FluidSolver FluidSolver( const glm::vec3& containerDim, const glm::vec3& particleDim, - const float& separation + const float separation, + const float mass ); virtual ~FluidSolver(); @@ -38,6 +40,7 @@ class FluidSolver glm::vec3 m_minBoundary; glm::vec3 m_maxBoundary; float m_separation; + float m_mass; std::vector m_particles; }; diff --git a/src/input/keyboardControl.cpp b/src/input/keyboardControl.cpp index 08259f92..6c8c58e7 100644 --- a/src/input/keyboardControl.cpp +++ b/src/input/keyboardControl.cpp @@ -15,89 +15,3 @@ KeyboardControl::KeyPressed( { return glfwGetKey(m_window, keyCode) == GLFW_PRESS; } - - -// void -// KeyboardControl::MatricesFromInputs( -// glm::mat4& view, -// glm::mat4& proj -// ) -// { -// // Initial position : on +Z -// glm::vec3 position = glm::vec3( 0, 0, 5 ); -// // Initial horizontal angle : toward -Z -// float horizontalAngle = 3.14f; -// // Initial vertical angle : none -// float verticalAngle = 0.0f; -// // Initial Field of View -// float initialFoV = 45.0f; - -// float speed = 3.0f; // 3 units / second -// float mouseSpeed = 0.005f; - -// // glfwGetTime is called only once, the first time this function is called -// static double lastTime = glfwGetTime(); - -// // Compute time difference between current and last frame -// double currentTime = glfwGetTime(); -// float deltaTime = float(currentTime - lastTime); - -// // Get mouse position -// double xpos, ypos; -// glfwGetCursorPos(window, &xpos, &ypos); - -// // Reset mouse position for next frame -// glfwSetCursorPos(window, 1024/2, 768/2); - -// // Compute new orientation -// horizontalAngle += mouseSpeed * float(1024/2 - xpos ); -// verticalAngle += mouseSpeed * float( 768/2 - ypos ); - -// // Direction : Spherical coordinates to Cartesian coordinates conversion -// glm::vec3 direction( -// cos(verticalAngle) * sin(horizontalAngle), -// sin(verticalAngle), -// cos(verticalAngle) * cos(horizontalAngle) -// ); - -// // Right vector -// glm::vec3 right = glm::vec3( -// sin(horizontalAngle - 3.14f/2.0f), -// 0, -// cos(horizontalAngle - 3.14f/2.0f) -// ); - -// // Up vector -// glm::vec3 up = glm::cross( right, direction ); - -// // Move forward -// if (glfwGetKey( window, GLFW_KEY_UP ) == GLFW_PRESS){ -// position += direction * deltaTime * speed; -// } -// // Move backward -// if (glfwGetKey( window, GLFW_KEY_DOWN ) == GLFW_PRESS){ -// position -= direction * deltaTime * speed; -// } -// // Strafe right -// if (glfwGetKey( window, GLFW_KEY_RIGHT ) == GLFW_PRESS){ -// position += right * deltaTime * speed; -// } -// // Strafe left -// if (glfwGetKey( window, GLFW_KEY_LEFT ) == GLFW_PRESS){ -// position -= right * deltaTime * speed; -// } - -// float FoV = initialFoV;// - 5 * glfwGetMouseWheel(); // Now GLFW 3 requires setting up a callback for this. It's a bit too complicated for this beginner's tutorial, so it's disabled instead. - -// // Projection matrix : 45∞ Field of View, 4:3 ratio, display range : 0.1 unit <-> 100 units -// proj = glm::perspective(FoV, 4.0f / 3.0f, 0.1f, 100.0f); -// // Camera matrix -// view = glm::lookAt( -// position, // Camera is here -// position+direction, // and looks here : at the same position, plus "direction" -// up // Head is up (set to 0,-1,0 to look upside-down) -// ); - -// // For the next frame, the "last time" will be "now" -// lastTime = currentTime; -// } diff --git a/src/input/keyboardControl.h b/src/input/keyboardControl.h index dcc634b2..684db284 100644 --- a/src/input/keyboardControl.h +++ b/src/input/keyboardControl.h @@ -9,10 +9,12 @@ typedef enum { Key_A = GLFW_KEY_A, Key_S = GLFW_KEY_S, Key_D = GLFW_KEY_D, + Key_P = GLFW_KEY_P, Key_Up = GLFW_KEY_UP, Key_Down = GLFW_KEY_DOWN, Key_Left = GLFW_KEY_LEFT, - Key_Right = GLFW_KEY_RIGHT + Key_Right = GLFW_KEY_RIGHT, + Key_Space = GLFW_KEY_SPACE } KeyCode; class KeyboardControl diff --git a/src/main.cpp b/src/main.cpp index 15bd4037..5285fa35 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,8 +4,11 @@ #include #include #include +#include -#define UNIT_TEST +INITIALIZE_EASYLOGGINGPP + +// #define UNIT_TEST using namespace std; @@ -14,43 +17,44 @@ void TestKernels() float x[] = {0, 1.0f, 5.4f, 100.7f, -30.f, 36.f, -4.f}; float h[] = {10.f, 1.0f, 2.4f, 140.23f, -22.f, -2.f, 20.f}; - cout << "TestKernels" << endl; + LOG(INFO) << "TestKernels" << endl; int testLen = 7; for (int i = 0; i < testLen; ++i) { - cout << "KernelPoly6, x: " + to_string(x[i]) + ", h: " + to_string(h[i]) << endl; + LOG(INFO) << "KernelPoly6, x: " + to_string(x[i]) + ", h: " + to_string(h[i]) << endl; float wPoly6 = KernelPoly6(x[i], h[i]); - cout << wPoly6 << endl; + LOG(INFO) << wPoly6 << endl; #ifdef TEST_SPIKY - cout << "KernelSpiky: x: " + to_string(x[i]) + ", h: " + to_string(h[i]) << endl; + LOG(INFO) << "KernelSpiky: x: " + to_string(x[i]) + ", h: " + to_string(h[i]) << endl; float wSpiky = KernelSpiky(x[i], h[i]); - cout << wSpiky << endl; + LOG(INFO) << wSpiky << endl; #endif #ifdef TEST_VISCOUS - cout << "KernelViscous: x: " + to_string(x[i]) + ", h: " + to_string(h[i]) << endl; + LOG(INFO) << "KernelViscous: x: " + to_string(x[i]) + ", h: " + to_string(h[i]) << endl; float wViscous = KernelViscous(x[i], h[i]); - cout << wSpiky << endl; + LOG(INFO) << wSpiky << endl; #endif } } void TestSuite() { - cout << "------------------------" << endl; - cout << "UNIT TEST ENABLED" << endl; + LOG(INFO) << "------------------------" << endl; + LOG(INFO) << "UNIT TEST ENABLED" << endl; // @todo: NEED TO WRITE PROPER TEST SUITE! bool passed = false; TestKernels(); - cout << "END UNIT TEST" << endl; - cout << "------------------------" << endl; + LOG(INFO) << "END UNIT TEST" << endl; + LOG(INFO) << "------------------------" << endl; } -int main() -{ +int +main(int argv, char* argc[]) { + #ifdef UNIT_TEST TestSuite(); #endif diff --git a/src/mathConstants.h b/src/mathConstants.h index b3f0440b..dd8763c8 100644 --- a/src/mathConstants.h +++ b/src/mathConstants.h @@ -8,6 +8,6 @@ static const float TWO_PI = 2 * PI; static const float INV_PI = 1.0f / PI; static const float DEG2RAD = PI / 180.f; static const float RAD2DEG = 180.f / PI; -static const float GRAVITY = -0.05f; +static const float GRAVITY = -0.5f; #endif diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 33ddfb69..93b3198c 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -65,15 +65,24 @@ Scene::InitFromJson( ); const float separation = root["particleSeparation"].asFloat(); - const float cellSize = root["cellSize"].asFloat(); + const float stiffness = root["stiffness"].asFloat(); + const float viscosity = root["viscosity"].asFloat(); + const float mass = root["mass"].asFloat(); + const float restDensity = root["restDensity"].asFloat(); + const float timestep = root["timestep"].asFloat(); // -- Initialize fluid solvers m_fluidSolver = new SPHSolver( containerDim, particleDim, separation, - cellSize + cellSize, + stiffness, + viscosity, + mass, + restDensity, + timestep ); // -- Initialize fluid geo @@ -94,6 +103,13 @@ Scene::Update( ) { UpdateCamera(deltaT, kc); + + if (kc->KeyPressed(Key_Space)) { + m_paused = !m_paused; + } + if (m_paused) { + return; + } UpdateFluidSolver(deltaT, progAdvect); } @@ -145,6 +161,14 @@ Scene::UpdateCamera( const KeyboardControl* kc ) { + // Camera mode + if (kc->KeyPressed(Key_P)) { + static bool perspective = true; + perspective = !perspective; + m_camera->EnablePerspective(perspective); + } + + // Camera movement float rotateAmt = 20.0f; float zoomAmt = 0.01f; if (kc->KeyPressed(Key_Up)) { diff --git a/src/scene/scene.h b/src/scene/scene.h index 7e8c9269..1177d6e5 100644 --- a/src/scene/scene.h +++ b/src/scene/scene.h @@ -33,6 +33,7 @@ class Scene virtual void CleanUp(); protected: + bool m_paused = false; Camera* m_camera; Box* m_testBox; Box* m_fluidContainer; diff --git a/src/scene/scene.json b/src/scene/scene.json index 63cf1952..79a33d56 100644 --- a/src/scene/scene.json +++ b/src/scene/scene.json @@ -5,10 +5,15 @@ "scaleZ" : 10.0 }, "particleDim" : { - "boundX" : 4.0, - "boundY" : 4.0, - "boundZ" : 4.0 + "boundX" : 1.0, + "boundY" : 1.0, + "boundZ" : 1.0 }, - "particleSeparation" : 0.2, - "cellSize" : 1.0 + "stiffness": 1000, + "viscosity": 25, + "mass": 0.125, + "restDensity": 1000, + "cellSize": 0.100001, + "particleSeparation": 0.05, + "timestep": 0.001 } diff --git a/src/shaderProgram/particleAdvectProgram.cpp b/src/shaderProgram/particleAdvectProgram.cpp index 09925b5d..24471bde 100644 --- a/src/shaderProgram/particleAdvectProgram.cpp +++ b/src/shaderProgram/particleAdvectProgram.cpp @@ -50,6 +50,9 @@ ParticleAdvectProgram::ParticleAdvectProgram( // -- Generate two transform feedback buffers for ping-pong'ing glGenTransformFeedbacks(2, &m_TFBuffers[0]); + + // -- Set big point size + glPointSize(3.0f); } void diff --git a/src/thirdparty/easylogging++.h b/src/thirdparty/easylogging++.h new file mode 100755 index 00000000..f9a67feb --- /dev/null +++ b/src/thirdparty/easylogging++.h @@ -0,0 +1,6663 @@ +// +// Easylogging++ v9.80 +// Single-header only, cross-platform logging library for C++ applications +// +// Copyright (c) 2015 muflihun.com +// +// This library is released under the MIT Licence. +// http://easylogging.muflihun.com/licence.php +// +// easylogging@muflihun.com +// +// https://github.com/easylogging/easyloggingpp +// http://easylogging.muflihun.com +// http://muflihun.com +// +#ifndef EASYLOGGINGPP_H +#define EASYLOGGINGPP_H +// Compilers and C++0x/C++11 Evaluation +#if defined(__GNUC__) +# define ELPP_COMPILER_GCC 1 +# define ELPP_GCC_VERSION (__GNUC__ * 10000 \ + + __GNUC_MINOR__ * 100 \ + + __GNUC_PATCHLEVEL__) +# if defined(__GXX_EXPERIMENTAL_CXX0X__) +# define ELPP_CXX0X 1 +# elif(ELPP_GCC_VERSION >= 40801) +# define ELPP_CXX11 1 +# endif // defined(__GXX_EXPERIMENTAL_CXX0X__) +#endif // defined(__GNUC__) +// Visual C++ +#if defined(_MSC_VER) +# define ELPP_COMPILER_MSVC 1 +# define ELPP_CRT_DBG_WARNINGS 1 +# if (_MSC_VER == 1600) +# define ELPP_CXX0X 1 +# elif(_MSC_VER >= 1700) +# define ELPP_CXX11 1 +# endif // (_MSC_VER == 1600) +#endif // defined(_MSC_VER) +// Clang++ +#if defined(__clang__) && (__clang__ == 1) +# define ELPP_COMPILER_CLANG 1 +# define ELPP_CLANG_VERSION (__clang_major__ * 10000 \ + + __clang_minor__ * 100 \ + + __clang_patchlevel__) +# if (ELPP_CLANG_VERSION >= 30300) +# define ELPP_CXX11 1 +# endif // (ELPP_CLANG_VERSION >= 30300) +#endif // defined(__clang__) && (__clang__ == 1) +// MinGW +#if defined(__MINGW32__) || defined(__MINGW64__) +# define ELPP_MINGW 1 +#endif // defined(__MINGW32__) || defined(__MINGW64__) +// Cygwin +#if defined(__CYGWIN__) && (__CYGWIN__ == 1) +# define ELPP_CYGWIN 1 +#endif // defined(__CYGWIN__) && (__CYGWIN__ == 1) +// Intel C++ +#if defined(__INTEL_COMPILER) +# define ELPP_COMPILER_INTEL 1 +#endif +// Operating System Evaluation +// Windows +#if defined(_WIN32) || defined(_WIN64) +# define ELPP_OS_WINDOWS 1 +#endif // defined(_WIN32) || defined(_WIN64) +// Linux +#if (defined(__linux) || defined(__linux__)) +# define ELPP_OS_LINUX 1 +#endif // (defined(__linux) || defined(__linux__)) +// Mac +#if defined(__APPLE__) +# define ELPP_OS_MAC 1 +#endif // defined(__APPLE__) +// FreeBSD +#if defined(__FreeBSD__) +# define ELPP_OS_FREEBSD 1 +#endif +// Solaris +#if defined(__sun) +# define ELPP_OS_SOLARIS 1 +#endif +// Unix +#if ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_SOLARIS) && (!ELPP_OS_WINDOWS)) +# define ELPP_OS_UNIX 1 +#endif // ((ELPP_OS_LINUX || ELPP_OS_MAC || ELPP_OS_FREEBSD || ELPP_OS_SOLARIS) && (!ELPP_OS_WINDOWS)) +// Android +#if defined(__ANDROID__) +# define ELPP_OS_ANDROID 1 +#endif // defined(__ANDROID__) +// Evaluating Cygwin as *nix OS +#if !ELPP_OS_UNIX && !ELPP_OS_WINDOWS && ELPP_CYGWIN +# undef ELPP_OS_UNIX +# undef ELPP_OS_LINUX +# define ELPP_OS_UNIX 1 +# define ELPP_OS_LINUX 1 +#endif // !ELPP_OS_UNIX && !ELPP_OS_WINDOWS && ELPP_CYGWIN +#if !defined(ELPP_INTERNAL_DEBUGGING_OUT_INFO) +# define ELPP_INTERNAL_DEBUGGING_OUT_INFO std::cout +#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) +#if !defined(ELPP_INTERNAL_DEBUGGING_OUT_ERROR) +# define ELPP_INTERNAL_DEBUGGING_OUT_ERROR std::cerr +#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) +#if !defined(ELPP_INTERNAL_DEBUGGING_ENDL) +# define ELPP_INTERNAL_DEBUGGING_ENDL std::endl +#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) +#if !defined(ELPP_INTERNAL_DEBUGGING_MSG) +# define ELPP_INTERNAL_DEBUGGING_MSG(msg) msg +#endif // !defined(ELPP_INTERNAL_DEBUGGING_OUT) +// Internal Assertions and errors +#if !defined(ELPP_DISABLE_ASSERT) +# if (defined(ELPP_DEBUG_ASSERT_FAILURE)) +# define ELPP_ASSERT(expr, msg) if (!(expr)) { \ + std::stringstream internalInfoStream; internalInfoStream << msg; \ + ELPP_INTERNAL_DEBUGGING_OUT_ERROR \ + << "EASYLOGGING++ ASSERTION FAILED (LINE: " << __LINE__ << ") [" #expr << "] WITH MESSAGE \"" \ + << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << "\"" << ELPP_INTERNAL_DEBUGGING_ENDL; base::utils::abort(1, \ + "ELPP Assertion failure, please define ELPP_DEBUG_ASSERT_FAILURE"); } +# else +# define ELPP_ASSERT(expr, msg) if (!(expr)) { \ + std::stringstream internalInfoStream; internalInfoStream << msg; \ + ELPP_INTERNAL_DEBUGGING_OUT_ERROR\ + << "ASSERTION FAILURE FROM EASYLOGGING++ (LINE: " \ + << __LINE__ << ") [" #expr << "] WITH MESSAGE \"" << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << "\"" \ + << ELPP_INTERNAL_DEBUGGING_ENDL; } +# endif // (defined(ELPP_DEBUG_ASSERT_FAILURE)) +#else +# define ELPP_ASSERT(x, y) +#endif //(!defined(ELPP_DISABLE_ASSERT) +#if ELPP_COMPILER_MSVC +# define ELPP_INTERNAL_DEBUGGING_WRITE_PERROR \ + { char buff[256]; strerror_s(buff, 256, errno); \ + ELPP_INTERNAL_DEBUGGING_OUT_ERROR << ": " << buff << " [" << errno << "]";} (void)0 +#else +# define ELPP_INTERNAL_DEBUGGING_WRITE_PERROR \ + ELPP_INTERNAL_DEBUGGING_OUT_ERROR << ": " << strerror(errno) << " [" << errno << "]"; (void)0 +#endif // ELPP_COMPILER_MSVC +#if defined(ELPP_DEBUG_ERRORS) +# if !defined(ELPP_INTERNAL_ERROR) +# define ELPP_INTERNAL_ERROR(msg, pe) { \ + std::stringstream internalInfoStream; internalInfoStream << " " << msg; \ + ELPP_INTERNAL_DEBUGGING_OUT_ERROR \ + << "ERROR FROM EASYLOGGING++ (LINE: " << __LINE__ << ") " \ + << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) << ELPP_INTERNAL_DEBUGGING_ENDL; \ + if (pe) { ELPP_INTERNAL_DEBUGGING_OUT_ERROR << " "; ELPP_INTERNAL_DEBUGGING_WRITE_PERROR; }} (void)0 +# endif +#else +# undef ELPP_INTERNAL_INFO +# define ELPP_INTERNAL_ERROR(msg, pe) +#endif // defined(ELPP_DEBUG_ERRORS) +#if (defined(ELPP_DEBUG_INFO)) +# if !(defined(ELPP_INTERNAL_INFO_LEVEL)) +# define ELPP_INTERNAL_INFO_LEVEL 9 +# endif // !(defined(ELPP_INTERNAL_INFO_LEVEL)) +# if !defined(ELPP_INTERNAL_INFO) +# define ELPP_INTERNAL_INFO(lvl, msg) { if (lvl <= ELPP_INTERNAL_INFO_LEVEL) { \ + std::stringstream internalInfoStream; internalInfoStream << " " << msg; \ + ELPP_INTERNAL_DEBUGGING_OUT_INFO << ELPP_INTERNAL_DEBUGGING_MSG(internalInfoStream.str()) \ + << ELPP_INTERNAL_DEBUGGING_ENDL; }} +# endif +#else +# undef ELPP_INTERNAL_INFO +# define ELPP_INTERNAL_INFO(lvl, msg) +#endif // (defined(ELPP_DEBUG_INFO)) +#if defined(ELPP_STACKTRACE_ON_CRASH) +# if (ELPP_COMPILER_GCC && !ELPP_MINGW) +# define ELPP_STACKTRACE 1 +# else +# if ELPP_COMPILER_MSVC +# pragma message("Stack trace not available for this compiler") +# else +# warning "Stack trace not available for this compiler"; +# endif // ELPP_COMPILER_MSVC +# endif // ELPP_COMPILER_GCC +#endif // (defined(ELPP_STACKTRACE_ON_CRASH)) +// Miscellaneous macros +#define ELPP_UNUSED(x) (void)x +#if ELPP_OS_UNIX +// Log file permissions for unix-based systems +# define ELPP_LOG_PERMS S_IRUSR | S_IWUSR | S_IXUSR | S_IWGRP | S_IRGRP | S_IXGRP | S_IWOTH | S_IXOTH +#endif // ELPP_OS_UNIX +#if defined(ELPP_AS_DLL) && ELPP_COMPILER_MSVC +# if defined(ELPP_EXPORT_SYMBOLS) +# define ELPP_EXPORT __declspec(dllexport) +# else +# define ELPP_EXPORT __declspec(dllimport) +# endif // defined(ELPP_EXPORT_SYMBOLS) +#else +# define ELPP_EXPORT +#endif // defined(ELPP_AS_DLL) && ELPP_COMPILER_MSVC +// Some special functions that are VC++ specific +#undef STRTOK +#undef STRERROR +#undef STRCAT +#undef STRCPY +#if ELPP_CRT_DBG_WARNINGS +# define STRTOK(a, b, c) strtok_s(a, b, c) +# define STRERROR(a, b, c) strerror_s(a, b, c) +# define STRCAT(a, b, len) strcat_s(a, len, b) +# define STRCPY(a, b, len) strcpy_s(a, len, b) +#else +# define STRTOK(a, b, c) strtok(a, b) +# define STRERROR(a, b, c) strerror(c) +# define STRCAT(a, b, len) strcat(a, b) +# define STRCPY(a, b, len) strcpy(a, b) +#endif +// Compiler specific support evaluations +#if (!ELPP_MINGW && !ELPP_COMPILER_CLANG) || defined(ELPP_FORCE_USE_STD_THREAD) +# define ELPP_USE_STD_THREADING 1 +#endif // (!ELPP_MINGW && !ELPP_COMPILER_CLANG) || defined(ELPP_FORCE_USE_STD_THREAD) +#undef ELPP_FINAL +#if ELPP_COMPILER_INTEL || (ELPP_GCC_VERSION < 40702) +# define ELPP_FINAL +#else +# define ELPP_FINAL final +#endif // ELPP_COMPILER_INTEL || (ELPP_GCC_VERSION < 40702) +#if defined(ELPP_EXPERIMENTAL_ASYNC) +# define ELPP_ASYNC_LOGGING 1 +#else +# define ELPP_ASYNC_LOGGING 0 +#endif // defined(ELPP_EXPERIMENTAL_ASYNC) +#if defined(ELPP_THREAD_SAFE) || ELPP_ASYNC_LOGGING +# define ELPP_THREADING_ENABLED 1 +#endif // defined(ELPP_THREAD_SAFE) || ELPP_ASYNC_LOGGING +// Function macro ELPP_FUNC +#undef ELPP_FUNC +#if ELPP_COMPILER_MSVC // Visual C++ +# define ELPP_FUNC __FUNCSIG__ +#elif ELPP_COMPILER_GCC // GCC +# define ELPP_FUNC __PRETTY_FUNCTION__ +#elif ELPP_COMPILER_INTEL // Intel C++ +# define ELPP_FUNC __PRETTY_FUNCTION__ +#elif ELPP_COMPILER_CLANG // Clang++ +# define ELPP_FUNC __PRETTY_FUNCTION__ +#else +# if defined(__func__) +# define ELPP_FUNC __func__ +# else +# define ELPP_FUNC "" +# endif // defined(__func__) +#endif // defined(_MSC_VER) +#undef ELPP_VARIADIC_TEMPLATES_SUPPORTED +// Keep following line commented until features are fixed +#if ELPP_COMPILER_GCC || ELPP_COMPILER_CLANG || ELPP_COMPILER_INTEL || (ELPP_COMPILER_MSVC && _MSC_VER >= 1800) +# define ELPP_VARIADIC_TEMPLATES_SUPPORTED 1 +#endif // ELPP_COMPILER_GCC || ELPP_COMPILER_CLANG || ELPP_COMPILER_INTEL || (ELPP_COMPILER_MSVC && _MSC_VER >= 1800) +// Logging Enable/Disable macros +#if (!defined(ELPP_DISABLE_LOGS)) +# define ELPP_LOGGING_ENABLED 1 +#endif // (!defined(ELPP_DISABLE_LOGS)) +#if (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED) && ((defined(_DEBUG)) || (!defined(NDEBUG)))) +# define ELPP_DEBUG_LOG 1 +#else +# define ELPP_DEBUG_LOG 0 +#endif // (!defined(ELPP_DISABLE_DEBUG_LOGS) && (ELPP_LOGGING_ENABLED) && ((defined(_DEBUG)) || (!defined(NDEBUG)))) +#if (!defined(ELPP_DISABLE_INFO_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_INFO_LOG 1 +#else +# define ELPP_INFO_LOG 0 +#endif // (!defined(ELPP_DISABLE_INFO_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_WARNING_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_WARNING_LOG 1 +#else +# define ELPP_WARNING_LOG 0 +#endif // (!defined(ELPP_DISABLE_WARNING_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_ERROR_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_ERROR_LOG 1 +#else +# define ELPP_ERROR_LOG 0 +#endif // (!defined(ELPP_DISABLE_ERROR_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_FATAL_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_FATAL_LOG 1 +#else +# define ELPP_FATAL_LOG 0 +#endif // (!defined(ELPP_DISABLE_FATAL_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_TRACE_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_TRACE_LOG 1 +#else +# define ELPP_TRACE_LOG 0 +#endif // (!defined(ELPP_DISABLE_TRACE_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!defined(ELPP_DISABLE_VERBOSE_LOGS) && (ELPP_LOGGING_ENABLED)) +# define ELPP_VERBOSE_LOG 1 +#else +# define ELPP_VERBOSE_LOG 0 +#endif // (!defined(ELPP_DISABLE_VERBOSE_LOGS) && (ELPP_LOGGING_ENABLED)) +#if (!(ELPP_CXX0X || ELPP_CXX11)) +# error "Easylogging++ 9.0+ is only compatible with C++0x (or higher) compliant compiler" +#endif // (!(ELPP_CXX0X || ELPP_CXX11)) +// Headers +#if defined(ELPP_SYSLOG) +# include +#endif // defined(ELPP_SYSLOG) +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(ELPP_UNICODE) +# include +# if ELPP_OS_WINDOWS +# include +# endif // ELPP_OS_WINDOWS +#endif // defined(ELPP_UNICODE) +#if ELPP_STACKTRACE +# include +# include +#endif // ELPP_STACKTRACE +#if ELPP_OS_ANDROID +# include +#endif // ELPP_OS_ANDROID +#if ELPP_OS_UNIX +# include +# include +#elif ELPP_OS_WINDOWS +# include +# include +# if defined(WIN32_LEAN_AND_MEAN) +# if defined(ELPP_WINSOCK2) +# include +# else +# include +# endif // defined(ELPP_WINSOCK2) +# endif // defined(WIN32_LEAN_AND_MEAN) +#endif // ELPP_OS_UNIX +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if ELPP_THREADING_ENABLED +# if ELPP_USE_STD_THREADING +# include +# include +# else +# if ELPP_OS_UNIX +# include +# endif // ELPP_OS_UNIX +# endif // ELPP_USE_STD_THREADING +#endif // ELPP_THREADING_ENABLED +#if ELPP_ASYNC_LOGGING +# include +# include +# include +#endif // ELPP_ASYNC_LOGGING +#if defined(ELPP_STL_LOGGING) +// For logging STL based templates +# include +# include +# include +# include +# include +# include +# if defined(ELPP_LOG_STD_ARRAY) +# include +# endif // defined(ELPP_LOG_STD_ARRAY) +# if defined(ELPP_LOG_UNORDERED_MAP) +# include +# endif // defined(ELPP_LOG_UNORDERED_MAP) +# if defined(ELPP_LOG_UNORDERED_SET) +# include +# endif // defined(ELPP_UNORDERED_SET) +#endif // defined(ELPP_STL_LOGGING) +#if defined(ELPP_QT_LOGGING) +// For logging Qt based classes & templates +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include +#endif // defined(ELPP_QT_LOGGING) +#if defined(ELPP_BOOST_LOGGING) +// For logging boost based classes & templates +# include +# include +# include +# include +# include +# include +# include +# include +#endif // defined(ELPP_BOOST_LOGGING) +#if defined(ELPP_WXWIDGETS_LOGGING) +// For logging wxWidgets based classes & templates +# include +#endif // defined(ELPP_WXWIDGETS_LOGGING) +// Forward declarations +namespace el { +class Logger; +class LogMessage; +class PerformanceTrackingData; +class Loggers; +class Helpers; +template class Callback; +class LogDispatchCallback; +class PerformanceTrackingCallback; +class LogDispatchData; +namespace base { +class Storage; +class RegisteredLoggers; +class PerformanceTracker; +class MessageBuilder; +class Writer; +class PErrorWriter; +class LogDispatcher; +class DefaultLogBuilder; +class DefaultLogDispatchCallback; +#if ELPP_ASYNC_LOGGING +class AsyncLogDispatchCallback; +class AsyncDispatchWorker; +#endif // ELPP_ASYNC_LOGGING +class DefaultPerformanceTrackingCallback; +} // namespace base +} // namespace el +/// @brief Easylogging++ entry namespace +namespace el { +/// @brief Namespace containing base/internal functionality used by Easylogging++ +namespace base { +/// @brief Data types used by Easylogging++ +namespace type { +#undef ELPP_LITERAL +#undef ELPP_STRLEN +#undef ELPP_COUT +#if defined(ELPP_UNICODE) +# define ELPP_LITERAL(txt) L##txt +# define ELPP_STRLEN wcslen +# if defined ELPP_CUSTOM_COUT +# define ELPP_COUT ELPP_CUSTOM_COUT +# else +# define ELPP_COUT std::wcout +# endif // defined ELPP_CUSTOM_COUT +typedef wchar_t char_t; +typedef std::wstring string_t; +typedef std::wstringstream stringstream_t; +typedef std::wfstream fstream_t; +typedef std::wostream ostream_t; +#else +# define ELPP_LITERAL(txt) txt +# define ELPP_STRLEN strlen +# if defined ELPP_CUSTOM_COUT +# define ELPP_COUT ELPP_CUSTOM_COUT +# else +# define ELPP_COUT std::cout +# endif // defined ELPP_CUSTOM_COUT +typedef char char_t; +typedef std::string string_t; +typedef std::stringstream stringstream_t; +typedef std::fstream fstream_t; +typedef std::ostream ostream_t; +#endif // defined(ELPP_UNICODE) +#if defined(ELPP_CUSTOM_COUT_LINE) +# define ELPP_COUT_LINE(logLine) ELPP_CUSTOM_COUT_LINE(logLine) +#else +# define ELPP_COUT_LINE(logLine) logLine << std::flush +#endif // defined(ELPP_CUSTOM_COUT_LINE) +typedef unsigned short EnumType; +typedef std::shared_ptr StoragePointer; +typedef int VerboseLevel; +typedef std::shared_ptr LogDispatchCallbackPtr; +typedef std::shared_ptr PerformanceTrackingCallbackPtr; +} // namespace type +/// @brief Internal helper class that prevent copy constructor for class +/// +/// @detail When using this class simply inherit it privately +class NoCopy { +protected: + NoCopy(void) {} +private: + NoCopy(const NoCopy&); + NoCopy& operator=(const NoCopy&); +}; +/// @brief Internal helper class that makes all default constructors private. +/// +/// @detail This prevents initializing class making it static unless an explicit constructor is declared. +/// When using this class simply inherit it privately +class StaticClass { +private: + StaticClass(void); + StaticClass(const StaticClass&); + StaticClass& operator=(const StaticClass&); +}; +} // namespace base +/// @brief Represents enumeration for severity level used to determine level of logging +/// +/// @detail With Easylogging++, developers may disable or enable any level regardless of +/// what the severity is. Or they can choose to log using hierarchical logging flag +enum class Level : base::type::EnumType { + /// @brief Generic level that represents all the levels. Useful when setting global configuration for all levels + Global = 1, + /// @brief Information that can be useful to back-trace certain events - mostly useful than debug logs. + Trace = 2, + /// @brief Informational events most useful for developers to debug application + Debug = 4, + /// @brief Severe error information that will presumably abort application + Fatal = 8, + /// @brief Information representing errors in application but application will keep running + Error = 16, + /// @brief Useful when application has potentially harmful situtaions + Warning = 32, + /// @brief Information that can be highly useful and vary with verbose logging level. + Verbose = 64, + /// @brief Mainly useful to represent current progress of application + Info = 128, + /// @brief Represents unknown level + Unknown = 1010 +}; +/// @brief Static class that contains helper functions for el::Level +class LevelHelper : base::StaticClass { +public: + /// @brief Represents minimum valid level. Useful when iterating through enum. + static const base::type::EnumType kMinValid = static_cast(Level::Trace); + /// @brief Represents maximum valid level. This is used internally and you should not need it. + static const base::type::EnumType kMaxValid = static_cast(Level::Info); + /// @brief Casts level to int, useful for iterating through enum. + static base::type::EnumType castToInt(Level level) { + return static_cast(level); + } + /// @brief Casts int(ushort) to level, useful for iterating through enum. + static Level castFromInt(base::type::EnumType l) { + return static_cast(l); + } + /// @brief Converts level to associated const char* + /// @return Upper case string based level. + static const char* convertToString(Level level) { + // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. + if (level == Level::Global) return "GLOBAL"; + if (level == Level::Debug) return "DEBUG"; + if (level == Level::Info) return "INFO"; + if (level == Level::Warning) return "WARNING"; + if (level == Level::Error) return "ERROR"; + if (level == Level::Fatal) return "FATAL"; + if (level == Level::Verbose) return "VERBOSE"; + if (level == Level::Trace) return "TRACE"; + return "UNKNOWN"; + } + /// @brief Converts from levelStr to Level + /// @param levelStr Upper case string based level. + /// Lower case is also valid but providing upper case is recommended. + static Level convertFromString(const char* levelStr) { + if ((strcmp(levelStr, "GLOBAL") == 0) || (strcmp(levelStr, "global") == 0)) + return Level::Global; + if ((strcmp(levelStr, "DEBUG") == 0) || (strcmp(levelStr, "debug") == 0)) + return Level::Debug; + if ((strcmp(levelStr, "INFO") == 0) || (strcmp(levelStr, "info") == 0)) + return Level::Info; + if ((strcmp(levelStr, "WARNING") == 0) || (strcmp(levelStr, "warning") == 0)) + return Level::Warning; + if ((strcmp(levelStr, "ERROR") == 0) || (strcmp(levelStr, "error") == 0)) + return Level::Error; + if ((strcmp(levelStr, "FATAL") == 0) || (strcmp(levelStr, "fatal") == 0)) + return Level::Fatal; + if ((strcmp(levelStr, "VERBOSE") == 0) || (strcmp(levelStr, "verbose") == 0)) + return Level::Verbose; + if ((strcmp(levelStr, "TRACE") == 0) || (strcmp(levelStr, "trace") == 0)) + return Level::Trace; + return Level::Unknown; + } + /// @brief Applies specified function to each level starting from startIndex + /// @param startIndex initial value to start the iteration from. This is passed as pointer and + /// is left-shifted so this can be used inside function (fn) to represent current level. + /// @param fn function to apply with each level. This bool represent whether or not to stop iterating through levels. + static inline void forEachLevel(base::type::EnumType* startIndex, const std::function& fn) { + base::type::EnumType lIndexMax = LevelHelper::kMaxValid; + do { + if (fn()) { + break; + } + *startIndex = static_cast(*startIndex << 1); + } while (*startIndex <= lIndexMax); + } +}; +/// @brief Represents enumeration of ConfigurationType used to configure or access certain aspect +/// of logging +enum class ConfigurationType : base::type::EnumType { + /// @brief Determines whether or not corresponding level and logger of logging is enabled + /// You may disable all logs by using el::Level::Global + Enabled = 1, + /// @brief Whether or not to write corresponding log to log file + ToFile = 2, + /// @brief Whether or not to write corresponding level and logger log to standard output. + /// By standard output meaning termnal, command prompt etc + ToStandardOutput = 4, + /// @brief Determines format of logging corresponding level and logger. + Format = 8, + /// @brief Determines log file (full path) to write logs to for correponding level and logger + Filename = 16, + /// @brief Specifies milliseconds width. Width can be within range (1-6) + MillisecondsWidth = 32, + /// @brief Determines whether or not performance tracking is enabled. + /// + /// @detail This does not depend on logger or level. Performance tracking always uses 'performance' logger + PerformanceTracking = 64, + /// @brief Specifies log file max size. + /// + /// @detail If file size of corresponding log file (for corresponding level) is >= specified size, log file will + /// be truncated and re-initiated. + MaxLogFileSize = 128, + /// @brief Specifies number of log entries to hold until we flush pending log data + LogFlushThreshold = 256, + /// @brief Represents unknown configuration + Unknown = 1010 +}; +/// @brief Static class that contains helper functions for el::ConfigurationType +class ConfigurationTypeHelper : base::StaticClass { +public: + /// @brief Represents minimum valid configuration type. Useful when iterating through enum. + static const base::type::EnumType kMinValid = static_cast(ConfigurationType::Enabled); + /// @brief Represents maximum valid configuration type. This is used internally and you should not need it. + static const base::type::EnumType kMaxValid = static_cast(ConfigurationType::MaxLogFileSize); + /// @brief Casts configuration type to int, useful for iterating through enum. + static base::type::EnumType castToInt(ConfigurationType configurationType) { + return static_cast(configurationType); + } + /// @brief Casts int(ushort) to configurationt type, useful for iterating through enum. + static ConfigurationType castFromInt(base::type::EnumType c) { + return static_cast(c); + } + /// @brief Converts configuration type to associated const char* + /// @returns Upper case string based configuration type. + static const char* convertToString(ConfigurationType configurationType) { + // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. + if (configurationType == ConfigurationType::Enabled) return "ENABLED"; + if (configurationType == ConfigurationType::Filename) return "FILENAME"; + if (configurationType == ConfigurationType::Format) return "FORMAT"; + if (configurationType == ConfigurationType::ToFile) return "TO_FILE"; + if (configurationType == ConfigurationType::ToStandardOutput) return "TO_STANDARD_OUTPUT"; + if (configurationType == ConfigurationType::MillisecondsWidth) return "MILLISECONDS_WIDTH"; + if (configurationType == ConfigurationType::PerformanceTracking) return "PERFORMANCE_TRACKING"; + if (configurationType == ConfigurationType::MaxLogFileSize) return "MAX_LOG_FILE_SIZE"; + if (configurationType == ConfigurationType::LogFlushThreshold) return "LOG_FLUSH_THRESHOLD"; + return "UNKNOWN"; + } + /// @brief Converts from configStr to ConfigurationType + /// @param configStr Upper case string based configuration type. + /// Lower case is also valid but providing upper case is recommended. + static ConfigurationType convertFromString(const char* configStr) { + if ((strcmp(configStr, "ENABLED") == 0) || (strcmp(configStr, "enabled") == 0)) + return ConfigurationType::Enabled; + if ((strcmp(configStr, "TO_FILE") == 0) || (strcmp(configStr, "to_file") == 0)) + return ConfigurationType::ToFile; + if ((strcmp(configStr, "TO_STANDARD_OUTPUT") == 0) || (strcmp(configStr, "to_standard_output") == 0)) + return ConfigurationType::ToStandardOutput; + if ((strcmp(configStr, "FORMAT") == 0) || (strcmp(configStr, "format") == 0)) + return ConfigurationType::Format; + if ((strcmp(configStr, "FILENAME") == 0) || (strcmp(configStr, "filename") == 0)) + return ConfigurationType::Filename; + if ((strcmp(configStr, "MILLISECONDS_WIDTH") == 0) || (strcmp(configStr, "milliseconds_width") == 0)) + return ConfigurationType::MillisecondsWidth; + if ((strcmp(configStr, "PERFORMANCE_TRACKING") == 0) || (strcmp(configStr, "performance_tracking") == 0)) + return ConfigurationType::PerformanceTracking; + if ((strcmp(configStr, "MAX_LOG_FILE_SIZE") == 0) || (strcmp(configStr, "max_log_file_size") == 0)) + return ConfigurationType::MaxLogFileSize; + if ((strcmp(configStr, "LOG_FLUSH_THRESHOLD") == 0) || (strcmp(configStr, "log_flush_threshold") == 0)) + return ConfigurationType::LogFlushThreshold; + return ConfigurationType::Unknown; + } + /// @brief Applies specified function to each configuration type starting from startIndex + /// @param startIndex initial value to start the iteration from. This is passed by pointer and is left-shifted + /// so this can be used inside function (fn) to represent current configuration type. + /// @param fn function to apply with each configuration type. + /// This bool represent whether or not to stop iterating through configurations. + static inline void forEachConfigType(base::type::EnumType* startIndex, const std::function& fn) { + base::type::EnumType cIndexMax = ConfigurationTypeHelper::kMaxValid; + do { + if (fn()) { + break; + } + *startIndex = static_cast(*startIndex << 1); + } while (*startIndex <= cIndexMax); + } +}; +/// @brief Flags used while writing logs. This flags are set by user +enum class LoggingFlag : base::type::EnumType { + /// @brief Makes sure we have new line for each container log entry + NewLineForContainer = 1, + /// @brief Makes sure if -vmodule is used and does not specifies a module, then verbose + /// logging is allowed via that module. + AllowVerboseIfModuleNotSpecified = 2, + /// @brief When handling crashes by default, detailed crash reason will be logged as well + LogDetailedCrashReason = 4, + /// @brief Allows to disable application abortion when logged using FATAL level + DisableApplicationAbortOnFatalLog = 8, + /// @brief Flushes log with every log-entry (performance sensative) - Disabled by default + ImmediateFlush = 16, + /// @brief Enables strict file rolling + StrictLogFileSizeCheck = 32, + /// @brief Make terminal output colorful for supported terminals + ColoredTerminalOutput = 64, + /// @brief Supports use of multiple logging in same macro, e.g, CLOG(INFO, "default", "network") + MultiLoggerSupport = 128, + /// @brief Disables comparing performance tracker's checkpoints + DisablePerformanceTrackingCheckpointComparison = 256, + /// @brief Disable VModules + DisableVModules = 512, + /// @brief Disable VModules extensions + DisableVModulesExtensions = 1024, + /// @brief Enables hierarchical logging + HierarchicalLogging = 2048, + /// @brief Creates logger automatically when not available + CreateLoggerAutomatically = 4096, + /// @brief Adds spaces b/w logs that separated by left-shift operator + AutoSpacing = 8192, + /// @brief Preserves time format and does not convert it to sec, hour etc (performance tracking only) + FixedTimeFormat = 16384 +}; +namespace base { +/// @brief Namespace containing constants used internally. +namespace consts { + // Level log values - These are values that are replaced in place of %level format specifier + static const base::type::char_t* kInfoLevelLogValue = ELPP_LITERAL("INFO "); + static const base::type::char_t* kDebugLevelLogValue = ELPP_LITERAL("DEBUG"); + static const base::type::char_t* kWarningLevelLogValue = ELPP_LITERAL("WARN "); + static const base::type::char_t* kErrorLevelLogValue = ELPP_LITERAL("ERROR"); + static const base::type::char_t* kFatalLevelLogValue = ELPP_LITERAL("FATAL"); + static const base::type::char_t* kVerboseLevelLogValue = ELPP_LITERAL("VER"); + static const base::type::char_t* kTraceLevelLogValue = ELPP_LITERAL("TRACE"); + static const base::type::char_t* kInfoLevelShortLogValue = ELPP_LITERAL("I"); + static const base::type::char_t* kDebugLevelShortLogValue = ELPP_LITERAL("D"); + static const base::type::char_t* kWarningLevelShortLogValue = ELPP_LITERAL("W"); + static const base::type::char_t* kErrorLevelShortLogValue = ELPP_LITERAL("E"); + static const base::type::char_t* kFatalLevelShortLogValue = ELPP_LITERAL("F"); + static const base::type::char_t* kVerboseLevelShortLogValue = ELPP_LITERAL("V"); + static const base::type::char_t* kTraceLevelShortLogValue = ELPP_LITERAL("T"); + // Format specifiers - These are used to define log format + static const base::type::char_t* kAppNameFormatSpecifier = ELPP_LITERAL("%app"); + static const base::type::char_t* kLoggerIdFormatSpecifier = ELPP_LITERAL("%logger"); + static const base::type::char_t* kThreadIdFormatSpecifier = ELPP_LITERAL("%thread"); + static const base::type::char_t* kSeverityLevelFormatSpecifier = ELPP_LITERAL("%level"); + static const base::type::char_t* kSeverityLevelShortFormatSpecifier = ELPP_LITERAL("%levshort"); + static const base::type::char_t* kDateTimeFormatSpecifier = ELPP_LITERAL("%datetime"); + static const base::type::char_t* kLogFileFormatSpecifier = ELPP_LITERAL("%file"); + static const base::type::char_t* kLogFileBaseFormatSpecifier = ELPP_LITERAL("%fbase"); + static const base::type::char_t* kLogLineFormatSpecifier = ELPP_LITERAL("%line"); + static const base::type::char_t* kLogLocationFormatSpecifier = ELPP_LITERAL("%loc"); + static const base::type::char_t* kLogFunctionFormatSpecifier = ELPP_LITERAL("%func"); + static const base::type::char_t* kCurrentUserFormatSpecifier = ELPP_LITERAL("%user"); + static const base::type::char_t* kCurrentHostFormatSpecifier = ELPP_LITERAL("%host"); + static const base::type::char_t* kMessageFormatSpecifier = ELPP_LITERAL("%msg"); + static const base::type::char_t* kVerboseLevelFormatSpecifier = ELPP_LITERAL("%vlevel"); + static const char* kDateTimeFormatSpecifierForFilename = "%datetime"; + // Date/time + static const char* kDays[7] = { "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" }; + static const char* kDaysAbbrev[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; + static const char* kMonths[12] = { "January", "February", "March", "Apri", "May", "June", "July", "August", + "September", "October", "November", "December" }; + static const char* kMonthsAbbrev[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; + static const char* kDefaultDateTimeFormat = "%Y-%M-%d %H:%m:%s,%g"; + static const char* kDefaultDateTimeFormatInFilename = "%Y-%M-%d_%H-%m"; + static const int kYearBase = 1900; + static const char* kAm = "AM"; + static const char* kPm = "PM"; + // Miscellaneous constants + static const char* kDefaultLoggerId = "default"; + static const char* kPerformanceLoggerId = "performance"; + static const char* kSysLogLoggerId = "syslog"; + static const char* kNullPointer = "nullptr"; + static const char kFormatSpecifierChar = '%'; +#if ELPP_VARIADIC_TEMPLATES_SUPPORTED + static const char kFormatSpecifierCharValue = 'v'; +#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED + static const unsigned int kMaxLogPerContainer = 100; + static const unsigned int kMaxLogPerCounter = 100000; + static const unsigned int kDefaultMillisecondsWidth = 3; + static const base::type::VerboseLevel kMaxVerboseLevel = 9; + static const char* kUnknownUser = "user"; + static const char* kUnknownHost = "unknown-host"; +#if defined(ELPP_DEFAULT_LOG_FILE) + static const char* kDefaultLogFile = ELPP_DEFAULT_LOG_FILE; +#else +# if ELPP_OS_UNIX +# if ELPP_OS_ANDROID + static const char* kDefaultLogFile = "logs/myeasylog.log"; +# else + static const char* kDefaultLogFile = "logs/myeasylog.log"; +# endif // ELPP_OS_ANDROID +# elif ELPP_OS_WINDOWS + static const char* kDefaultLogFile = "logs\\myeasylog.log"; +# endif // ELPP_OS_UNIX +#endif // defined(ELPP_DEFAULT_LOG_FILE) +#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) + static const char* kDefaultLogFileParam = "--default-log-file"; +#endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) +#if defined(ELPP_LOGGING_FLAGS_FROM_ARG) + static const char* kLoggingFlagsParam = "--logging-flags"; +#endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG) +#if ELPP_OS_WINDOWS + static const char* kFilePathSeperator = "\\"; +#else + static const char* kFilePathSeperator = "/"; +#endif // ELPP_OS_WINDOWS + static const char* kValidLoggerIdSymbols = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._"; + static const char* kConfigurationComment = "##"; + static const char* kConfigurationLevel = "*"; + static const char* kConfigurationLoggerId = "--"; + static const std::size_t kSourceFilenameMaxLength = 100; + static const std::size_t kSourceLineMaxLength = 10; + static const Level kPerformanceTrackerDefaultLevel = Level::Info; + const struct { + double value; + const base::type::char_t* unit; + } kTimeFormats[] = { + { 1000.0f, ELPP_LITERAL("mis") }, + { 1000.0f, ELPP_LITERAL("ms") }, + { 60.0f, ELPP_LITERAL("seconds") }, + { 60.0f, ELPP_LITERAL("minutes") }, + { 24.0f, ELPP_LITERAL("hours") }, + { 7.0f, ELPP_LITERAL("days") } + }; + static const int kTimeFormatsCount = sizeof(kTimeFormats) / sizeof(kTimeFormats[0]); + const struct { + int numb; + const char* name; + const char* brief; + const char* detail; + } kCrashSignals[] = { + // NOTE: Do not re-order, if you do please check CrashHandler(bool) constructor and CrashHandler::setHandler(..) + { SIGABRT, "SIGABRT", "Abnormal termination", + "Program was abnormally terminated." }, + { SIGFPE, "SIGFPE", "Erroneous arithmetic operation", + "Arithemetic operation issue such as division by zero or operation resulting in overflow." }, + { SIGILL, "SIGILL", "Illegal instruction", + "Generally due to a corruption in the code or to an attempt to execute data."}, + { SIGSEGV, "SIGSEGV", "Invalid access to memory", + "Program is trying to read an invalid (unallocated, deleted or corrupted) or inaccessible memory." }, + { SIGINT, "SIGINT", "Interactive attention signal", + "Interruption generated (generally) by user or operating system." }, + }; + static const int kCrashSignalsCount = sizeof(kCrashSignals) / sizeof(kCrashSignals[0]); +} // namespace consts +} // namespace base +typedef std::function PreRollOutCallback; +namespace base { +static inline void defaultPreRollOutCallback(const char*, std::size_t) {} +/// @brief Enum to represent timestamp unit +enum class TimestampUnit : base::type::EnumType { + Microsecond = 0, Millisecond = 1, Second = 2, Minute = 3, Hour = 4, Day = 5 +}; +/// @brief Format flags used to determine specifiers that are active for performance improvements. +enum class FormatFlags : base::type::EnumType { + DateTime = 1<<1, LoggerId = 1<<2, File = 1<<3, Line = 1<<4, Location = 1<<5, Function = 1<<6, + User = 1<<7, Host = 1<<8, LogMessage = 1<<9, VerboseLevel = 1<<10, AppName = 1<<11, ThreadId = 1<<12, + Level = 1<<13, FileBase = 1<<14, LevelShort = 1<<15 +}; +/// @brief A milliseconds width class containing actual width and offset for date/time +class MillisecondsWidth { +public: + MillisecondsWidth(void) { init(base::consts::kDefaultMillisecondsWidth); } + explicit MillisecondsWidth(int width) { init(width); } + bool operator==(const MillisecondsWidth& msWidth) { return m_width == msWidth.m_width && m_offset == msWidth.m_offset; } + int m_width; unsigned int m_offset; +private: + void init(int width) { + if (width < 1 || width > 6) { + width = base::consts::kDefaultMillisecondsWidth; + } + m_width = width; + switch (m_width) { + case 3: m_offset = 1000; break; + case 4: m_offset = 100; break; + case 5: m_offset = 10; break; + case 6: m_offset = 1; break; + default: m_offset = 1000; break; + } + } +}; +/// @brief Namespace containing utility functions/static classes used internally +namespace utils { +/// @brief Deletes memory safely and points to null +template +static inline +typename std::enable_if::value, void>::type +safeDelete(T*& pointer) { + if (pointer == nullptr) + return; + delete pointer; + pointer = nullptr; +} +/// @brief Gets value of const char* but if it is nullptr, a string nullptr is returned +static inline const char* charPtrVal(const char* pointer) { + return pointer == nullptr ? base::consts::kNullPointer : pointer; +} +/// @brief Aborts application due with user-defined status +static inline void abort(int status, const std::string& reason = std::string()) { + // Both status and reason params are there for debugging with tools like gdb etc + ELPP_UNUSED(status); + ELPP_UNUSED(reason); +#if defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG) + // Ignore msvc critical error dialog - break instead (on debug mode) + _asm int 3 +#else + ::abort(); +#endif // defined(ELPP_COMPILER_MSVC) && defined(_M_IX86) && defined(_DEBUG) +} +/// @brief Bitwise operations for C++11 strong enum class. This casts e into Flag_T and returns value after bitwise operation +/// Use these function as

flag = bitwise::Or(MyEnum::val1, flag);
+namespace bitwise { +template +static inline base::type::EnumType And(Enum e, base::type::EnumType flag) { + return static_cast(flag) & static_cast(e); +} +template +static inline base::type::EnumType Not(Enum e, base::type::EnumType flag) { + return static_cast(flag) & ~(static_cast(e)); +} +template +static inline base::type::EnumType Or(Enum e, base::type::EnumType flag) { + return static_cast(flag) | static_cast(e); +} +} // namespace bitwise +template +static inline void addFlag(Enum e, base::type::EnumType* flag) { + *flag = base::utils::bitwise::Or(e, *flag); +} +template +static inline void removeFlag(Enum e, base::type::EnumType* flag) { + *flag = base::utils::bitwise::Not(e, *flag); +} +template +static inline bool hasFlag(Enum e, base::type::EnumType flag) { + return base::utils::bitwise::And(e, flag) > 0x0; +} +} // namespace utils +namespace threading { +#if ELPP_THREADING_ENABLED +# if !ELPP_USE_STD_THREADING +namespace internal { +/// @brief A mutex wrapper for compiler that dont yet support std::mutex +class Mutex : base::NoCopy { +public: + Mutex(void) { +# if ELPP_OS_UNIX + pthread_mutex_init(&m_underlyingMutex, nullptr); +# elif ELPP_OS_WINDOWS + InitializeCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + virtual ~Mutex(void) { +# if ELPP_OS_UNIX + pthread_mutex_destroy(&m_underlyingMutex); +# elif ELPP_OS_WINDOWS + DeleteCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + inline void lock(void) { +# if ELPP_OS_UNIX + pthread_mutex_lock(&m_underlyingMutex); +# elif ELPP_OS_WINDOWS + EnterCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + inline bool try_lock(void) { +# if ELPP_OS_UNIX + return (pthread_mutex_trylock(&m_underlyingMutex) == 0); +# elif ELPP_OS_WINDOWS + return TryEnterCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + + inline void unlock(void) { +# if ELPP_OS_UNIX + pthread_mutex_unlock(&m_underlyingMutex); +# elif ELPP_OS_WINDOWS + LeaveCriticalSection(&m_underlyingMutex); +# endif // ELPP_OS_UNIX + } + +private: +# if ELPP_OS_UNIX + pthread_mutex_t m_underlyingMutex; +# elif ELPP_OS_WINDOWS + CRITICAL_SECTION m_underlyingMutex; +# endif // ELPP_OS_UNIX +}; +/// @brief Scoped lock for compiler that dont yet support std::lock_guard +template +class ScopedLock : base::NoCopy { +public: + explicit ScopedLock(M& mutex) { + m_mutex = &mutex; + m_mutex->lock(); + } + + virtual ~ScopedLock(void) { + m_mutex->unlock(); + } +private: + M* m_mutex; + ScopedLock(void); +}; +} // namespace internal +/// @brief Gets ID of currently running threading in windows systems. On unix, nothing is returned. +static inline std::string getCurrentThreadId(void) { + std::stringstream ss; +# if (ELPP_OS_WINDOWS) + ss << GetCurrentThreadId(); +# endif // (ELPP_OS_WINDOWS) + return ss.str(); +} +static inline void msleep(int) { + // No implementation for non std::thread version +} +typedef base::threading::internal::Mutex Mutex; +typedef base::threading::internal::ScopedLock ScopedLock; +# else +/// @brief Gets ID of currently running threading using std::this_thread::get_id() +static inline std::string getCurrentThreadId(void) { + std::stringstream ss; + ss << std::this_thread::get_id(); + return ss.str(); +} +static inline void msleep(int ms) { + // Only when async logging enabled - this is because async is strict on compiler +#if ELPP_ASYNC_LOGGING + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); +#endif // ELPP_ASYNC_LOGGING +} +typedef std::mutex Mutex; +typedef std::lock_guard ScopedLock; +# endif // !ELPP_USE_STD_THREADING +#else +namespace internal { +/// @brief Mutex wrapper used when multi-threading is disabled. +class NoMutex : base::NoCopy { +public: + NoMutex(void) {} + inline void lock(void) {} + inline bool try_lock(void) { return true; } + inline void unlock(void) {} +}; +/// @brief Lock guard wrapper used when multi-threading is disabled. +template +class NoScopedLock : base::NoCopy { +public: + explicit NoScopedLock(Mutex&) { + } + virtual ~NoScopedLock(void) { + } +private: + NoScopedLock(void); +}; +} // namespace internal +static inline std::string getCurrentThreadId(void) { + return std::string(); +} +static inline void msleep(int) { + // No custom implementation +} +typedef base::threading::internal::NoMutex Mutex; +typedef base::threading::internal::NoScopedLock ScopedLock; +#endif // ELPP_THREADING_ENABLED +/// @brief Base of thread safe class, this class is inheritable-only +class ThreadSafe { +public: + virtual inline void acquireLock(void) ELPP_FINAL { m_mutex.lock(); } + virtual inline void releaseLock(void) ELPP_FINAL { m_mutex.unlock(); } + virtual inline base::threading::Mutex& lock(void) ELPP_FINAL { return m_mutex; } +protected: + ThreadSafe(void) {} + virtual ~ThreadSafe(void) {} +private: + base::threading::Mutex m_mutex; +}; +} // namespace threading +namespace utils { +class File : base::StaticClass { +public: + /// @brief Creates new out file stream for specified filename. + /// @return Pointer to newly created fstream or nullptr + static base::type::fstream_t* newFileStream(const std::string& filename) { + base::type::fstream_t *fs = new base::type::fstream_t(filename.c_str(), + base::type::fstream_t::out | base::type::fstream_t::app); +#if defined(ELPP_UNICODE) + std::locale elppUnicodeLocale(""); +#if ELPP_OS_WINDOWS + std::locale elppUnicodeLocaleWindows(elppUnicodeLocale, new std::codecvt_utf8_utf16); + elppUnicodeLocale = elppUnicodeLocaleWindows; +#endif + fs->imbue(elppUnicodeLocale); +#endif // defined(ELPP_UNICODE) + if (fs->is_open()) { + fs->flush(); + } else { + base::utils::safeDelete(fs); + ELPP_INTERNAL_ERROR("Bad file [" << filename << "]", true); + } + return fs; + } + + /// @brief Gets size of file provided in stream + static std::size_t getSizeOfFile(base::type::fstream_t* fs) { + if (fs == nullptr) { + return 0; + } + std::streampos currPos = fs->tellg(); + fs->seekg(0, fs->end); + std::size_t size = static_cast(fs->tellg()); + fs->seekg(currPos); + return size; + } + + /// @brief Determines whether or not provided path exist in current file system + static inline bool pathExists(const char* path, bool considerFile = false) { + if (path == nullptr) { + return false; + } +#if ELPP_OS_UNIX + ELPP_UNUSED(considerFile); + struct stat st; + return (stat(path, &st) == 0); +#elif ELPP_OS_WINDOWS + DWORD fileType = GetFileAttributesA(path); + if (fileType == INVALID_FILE_ATTRIBUTES) { + return false; + } + return considerFile ? true : ((fileType & FILE_ATTRIBUTE_DIRECTORY) == 0 ? false : true); +#endif // ELPP_OS_UNIX + } + + /// @brief Creates specified path on file system + /// @param path Path to create. + static bool createPath(const std::string& path) { + if (path.empty()) { + return false; + } + if (base::utils::File::pathExists(path.c_str())) { + return true; + } + int status = -1; + + char* currPath = const_cast(path.c_str()); + std::string builtPath = std::string(); +#if ELPP_OS_UNIX + if (path[0] == '/') { + builtPath = "/"; + } + currPath = STRTOK(currPath, base::consts::kFilePathSeperator, 0); +#elif ELPP_OS_WINDOWS + // Use secure functions API + char* nextTok_ = nullptr; + currPath = STRTOK(currPath, base::consts::kFilePathSeperator, &nextTok_); + ELPP_UNUSED(nextTok_); +#endif // ELPP_OS_UNIX + while (currPath != nullptr) { + builtPath.append(currPath); + builtPath.append(base::consts::kFilePathSeperator); +#if ELPP_OS_UNIX + status = mkdir(builtPath.c_str(), ELPP_LOG_PERMS); + currPath = STRTOK(nullptr, base::consts::kFilePathSeperator, 0); +#elif ELPP_OS_WINDOWS + status = _mkdir(builtPath.c_str()); + currPath = STRTOK(nullptr, base::consts::kFilePathSeperator, &nextTok_); +#endif // ELPP_OS_UNIX + } + if (status == -1) { + ELPP_INTERNAL_ERROR("Error while creating path [" << path << "]", true); + return false; + } + return true; + } + /// @brief Extracts path of filename with leading slash + static std::string extractPathFromFilename(const std::string& fullPath, + const char* seperator = base::consts::kFilePathSeperator) { + if ((fullPath == "") || (fullPath.find(seperator) == std::string::npos)) { + return fullPath; + } + std::size_t lastSlashAt = fullPath.find_last_of(seperator); + if (lastSlashAt == 0) { + return std::string(seperator); + } + return fullPath.substr(0, lastSlashAt + 1); + } + /// @brief builds stripped filename and puts it in buff + static void buildStrippedFilename(const char* filename, char buff[], + std::size_t limit = base::consts::kSourceFilenameMaxLength) { + std::size_t sizeOfFilename = strlen(filename); + if (sizeOfFilename >= limit) { + filename += (sizeOfFilename - limit); + if (filename[0] != '.' && filename[1] != '.') { // prepend if not already + filename += 3; // 3 = '..' + STRCAT(buff, "..", limit); + } + } + STRCAT(buff, filename, limit); + } + /// @brief builds base filename and puts it in buff + static void buildBaseFilename(const std::string& fullPath, char buff[], + std::size_t limit = base::consts::kSourceFilenameMaxLength, + const char* seperator = base::consts::kFilePathSeperator) { + const char *filename = fullPath.c_str(); + std::size_t lastSlashAt = fullPath.find_last_of(seperator); + filename += lastSlashAt ? lastSlashAt+1 : 0; + std::size_t sizeOfFilename = strlen(filename); + if (sizeOfFilename >= limit) { + filename += (sizeOfFilename - limit); + if (filename[0] != '.' && filename[1] != '.') { // prepend if not already + filename += 3; // 3 = '..' + STRCAT(buff, "..", limit); + } + } + STRCAT(buff, filename, limit); + } +}; +/// @brief String utilities helper class used internally. You should not use it. +class Str : base::StaticClass { +public: + /// @brief Checks if character is digit. Dont use libc implementation of it to prevent locale issues. + static inline bool isDigit(char c) { + return c >= '0' && c <= '9'; + } + + /// @brief Matches wildcards, '*' and '?' only supported. + static bool wildCardMatch(const char* str, const char* pattern) { + while (*pattern) { + switch (*pattern) { + case '?': + if (!*str) + return false; + ++str; + ++pattern; + break; + case '*': + if (wildCardMatch(str, pattern + 1)) + return true; + if (*str && wildCardMatch(str + 1, pattern)) + return true; + return false; + break; + default: + if (*str++ != *pattern++) + return false; + break; + } + } + return !*str && !*pattern; + } + + /// @brief Trims string from start + /// @param [in,out] str String to trim + static inline std::string& ltrim(std::string& str) { + str.erase(str.begin(), std::find_if(str.begin(), str.end(), std::not1(std::ptr_fun(&std::isspace)))); + return str; + } + + /// @brief Trim string from end + /// @param [in,out] str String to trim + static inline std::string& rtrim(std::string& str) { + str.erase(std::find_if(str.rbegin(), str.rend(), std::not1(std::ptr_fun(&std::isspace))).base(), str.end()); + return str; + } + + /// @brief Trims string from left and right + /// @param [in,out] str String to trim + static inline std::string& trim(std::string& str) { + return ltrim(rtrim(str)); + } + + /// @brief Determines whether or not str starts with specified string + /// @param str String to check + /// @param start String to check against + /// @return Returns true if starts with specified string, false otherwise + static inline bool startsWith(const std::string& str, const std::string& start) { + return (str.length() >= start.length()) && (str.compare(0, start.length(), start) == 0); + } + + /// @brief Determines whether or not str ends with specified string + /// @param str String to check + /// @param end String to check against + /// @return Returns true if ends with specified string, false otherwise + static inline bool endsWith(const std::string& str, const std::string& end) { + return (str.length() >= end.length()) && (str.compare(str.length() - end.length(), end.length(), end) == 0); + } + + /// @brief Replaces all instances of replaceWhat with 'replaceWith'. Original variable is changed for performance. + /// @param [in,out] str String to replace from + /// @param replaceWhat Character to replace + /// @param replaceWith Character to replace with + /// @return Modified version of str + static inline std::string& replaceAll(std::string& str, char replaceWhat, char replaceWith) { + std::replace(str.begin(), str.end(), replaceWhat, replaceWith); + return str; + } + + /// @brief Replaces all instances of 'replaceWhat' with 'replaceWith'. (String version) Replaces in place + /// @param str String to replace from + /// @param replaceWhat Character to replace + /// @param replaceWith Character to replace with + /// @return Modified (original) str + static inline std::string& replaceAll(std::string& str, const std::string& replaceWhat, // NOLINT + const std::string& replaceWith) { + if (replaceWhat == replaceWith) + return str; + std::size_t foundAt = std::string::npos; + while ((foundAt = str.find(replaceWhat, foundAt + 1)) != std::string::npos) { + str.replace(foundAt, replaceWhat.length(), replaceWith); + } + return str; + } + + static void replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, // NOLINT + const base::type::string_t& replaceWith) { + std::size_t foundAt = base::type::string_t::npos; + while ((foundAt = str.find(replaceWhat, foundAt + 1)) != base::type::string_t::npos) { + if (foundAt > 0 && str[foundAt - 1] == base::consts::kFormatSpecifierChar) { + str.erase(foundAt > 0 ? foundAt - 1 : 0, 1); + ++foundAt; + } else { + str.replace(foundAt, replaceWhat.length(), replaceWith); + return; + } + } + } +#if defined(ELPP_UNICODE) + static void replaceFirstWithEscape(base::type::string_t& str, const base::type::string_t& replaceWhat, // NOLINT + const std::string& replaceWith) { + replaceFirstWithEscape(str, replaceWhat, base::type::string_t(replaceWith.begin(), replaceWith.end())); + } +#endif // defined(ELPP_UNICODE) + /// @brief Converts string to uppercase + /// @param str String to convert + /// @return Uppercase string + static inline std::string& toUpper(std::string& str) { + std::transform(str.begin(), str.end(), str.begin(), ::toupper); + return str; + } + + /// @brief Compares cstring equality - uses strcmp + static inline bool cStringEq(const char* s1, const char* s2) { + if (s1 == nullptr && s2 == nullptr) return true; + if (s1 == nullptr || s2 == nullptr) return false; + return strcmp(s1, s2) == 0; + } + + /// @brief Compares cstring equality (case-insensitive) - uses toupper(char) + /// Dont use strcasecmp because of CRT (VC++) + static bool cStringCaseEq(const char* s1, const char* s2) { + if (s1 == nullptr && s2 == nullptr) return true; + if (s1 == nullptr || s2 == nullptr) return false; + if (strlen(s1) != strlen(s2)) return false; + while (*s1 != '\0' && *s2 != '\0') { + if (::toupper(*s1) != ::toupper(*s2)) return false; + ++s1; + ++s2; + } + return true; + } + + /// @brief Returns true if c exist in str + static inline bool contains(const char* str, char c) { + for (; *str; ++str) { + if (*str == c) + return true; + } + return false; + } + + static inline char* convertAndAddToBuff(std::size_t n, int len, char* buf, const char* bufLim, bool zeroPadded = true) { + char localBuff[10] = ""; + char* p = localBuff + sizeof(localBuff) - 2; + if (n > 0) { + for (; n > 0 && p > localBuff && len > 0; n /= 10, --len) + *--p = static_cast(n % 10 + '0'); + } else { + *--p = '0'; + --len; + } + if (zeroPadded) + while (p > localBuff && len-- > 0) *--p = static_cast('0'); + return addToBuff(p, buf, bufLim); + } + + static inline char* addToBuff(const char* str, char* buf, const char* bufLim) { + while ((buf < bufLim) && ((*buf = *str++) != '\0')) + ++buf; + return buf; + } + + static inline char* clearBuff(char buff[], std::size_t lim) { + STRCPY(buff, "", lim); + ELPP_UNUSED(lim); // For *nix we dont have anything using lim in above STRCPY macro + return buff; + } + + /// @brief Converst wchar* to char* + /// NOTE: Need to free return value after use! + static char* wcharPtrToCharPtr(const wchar_t* line) { + std::size_t len_ = wcslen(line) + 1; + char* buff_ = static_cast(malloc(len_ + 1)); +# if ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS) + std::wcstombs(buff_, line, len_); +# elif ELPP_OS_WINDOWS + std::size_t convCount_ = 0; + mbstate_t mbState_; + ::memset(static_cast(&mbState_), 0, sizeof(mbState_)); + wcsrtombs_s(&convCount_, buff_, len_, &line, len_, &mbState_); +# endif // ELPP_OS_UNIX || (ELPP_OS_WINDOWS && !ELPP_CRT_DBG_WARNINGS) + return buff_; + } +}; +/// @brief Operating System helper static class used internally. You should not use it. +class OS : base::StaticClass { +public: +#if ELPP_OS_WINDOWS + /// @brief Gets environment variables for Windows based OS. + /// We are not using getenv(const char*) because of CRT deprecation + /// @param varname Variable name to get environment variable value for + /// @return If variable exist the value of it otherwise nullptr + static const char* getWindowsEnvironmentVariable(const char* varname) { + const DWORD bufferLen = 50; + static char buffer[bufferLen]; + if (GetEnvironmentVariableA(varname, buffer, bufferLen)) { + return buffer; + } + return nullptr; + } +#endif // ELPP_OS_WINDOWS +#if ELPP_OS_ANDROID + /// @brief Reads android property value + static inline std::string getProperty(const char* prop) { + char propVal[PROP_VALUE_MAX + 1]; + int ret = __system_property_get(prop, propVal); + return ret == 0 ? std::string() : std::string(propVal); + } + + /// @brief Reads android device name + static std::string getDeviceName(void) { + std::stringstream ss; + std::string manufacturer = getProperty("ro.product.manufacturer"); + std::string model = getProperty("ro.product.model"); + if (manufacturer.empty() || model.empty()) { + return std::string(); + } + ss << manufacturer << "-" << model; + return ss.str(); + } +#endif // ELPP_OS_ANDROID + + /// @brief Runs command on terminal and returns the output. + /// + /// @detail This is applicable only on unix based systems, for all other OS, an empty string is returned. + /// @param command Bash command + /// @return Result of bash output or empty string if no result found. + static const std::string getBashOutput(const char* command) { +#if (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN) + if (command == nullptr) { + return std::string(); + } + FILE* proc = nullptr; + if ((proc = popen(command, "r")) == nullptr) { + ELPP_INTERNAL_ERROR("\nUnable to run command [" << command << "]", true); + return std::string(); + } + char hBuff[4096]; + if (fgets(hBuff, sizeof(hBuff), proc) != nullptr) { + pclose(proc); + if (hBuff[strlen(hBuff) - 1] == '\n') { + hBuff[strlen(hBuff) - 1] = '\0'; + } + return std::string(hBuff); + } + return std::string(); +#else + ELPP_UNUSED(command); + return std::string(); +#endif // (ELPP_OS_UNIX && !ELPP_OS_ANDROID && !ELPP_CYGWIN) + } + + /// @brief Gets environment variable. This is cross-platform and CRT safe (for VC++) + /// @param variableName Environment variable name + /// @param defaultVal If no environment variable or value found the value to return by default + /// @param alternativeBashCommand If environment variable not found what would be alternative bash command + /// in order to look for value user is looking for. E.g, for 'user' alternative command will 'whoami' + static std::string getEnvironmentVariable(const char* variableName, const char* defaultVal, const char* alternativeBashCommand = nullptr) { +#if ELPP_OS_UNIX + const char* val = getenv(variableName); +#elif ELPP_OS_WINDOWS + const char* val = getWindowsEnvironmentVariable(variableName); +#endif // ELPP_OS_UNIX + if ((val == nullptr) || ((strcmp(val, "") == 0))) { +#if ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH) + // Try harder on unix-based systems + std::string valBash = base::utils::OS::getBashOutput(alternativeBashCommand); + if (valBash.empty()) { + return std::string(defaultVal); + } else { + return valBash; + } +#elif ELPP_OS_WINDOWS || ELPP_OS_UNIX + ELPP_UNUSED(alternativeBashCommand); + return std::string(defaultVal); +#endif // ELPP_OS_UNIX && defined(ELPP_FORCE_ENV_VAR_FROM_BASH) + } + return std::string(val); + } + /// @brief Gets current username. + static inline std::string currentUser(void) { +#if ELPP_OS_UNIX && !ELPP_OS_ANDROID + return getEnvironmentVariable("USER", base::consts::kUnknownUser, "whoami"); +#elif ELPP_OS_WINDOWS + return getEnvironmentVariable("USERNAME", base::consts::kUnknownUser); +#elif ELPP_OS_ANDROID + ELPP_UNUSED(base::consts::kUnknownUser); + return std::string("android"); +#else + return std::string(); +#endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID + } + + /// @brief Gets current host name or computer name. + /// + /// @detail For android systems this is device name with its manufacturer and model seperated by hyphen + static inline std::string currentHost(void) { +#if ELPP_OS_UNIX && !ELPP_OS_ANDROID + return getEnvironmentVariable("HOSTNAME", base::consts::kUnknownHost, "hostname"); +#elif ELPP_OS_WINDOWS + return getEnvironmentVariable("COMPUTERNAME", base::consts::kUnknownHost); +#elif ELPP_OS_ANDROID + ELPP_UNUSED(base::consts::kUnknownHost); + return getDeviceName(); +#else + return std::string(); +#endif // ELPP_OS_UNIX && !ELPP_OS_ANDROID + } + /// @brief Whether or not terminal supports colors + static inline bool termSupportsColor(void) { + std::string term = getEnvironmentVariable("TERM", ""); + return term == "xterm" || term == "xterm-color" || term == "xterm-256color" || + term == "screen" || term == "linux" || term == "cygwin"; + } +}; +extern std::string s_currentUser; +extern std::string s_currentHost; +extern bool s_termSupportsColor; +#define ELPP_INITI_BASIC_DECLR \ + namespace el {\ + namespace base {\ + namespace utils {\ + std::string s_currentUser = el::base::utils::OS::currentUser(); \ + std::string s_currentHost = el::base::utils::OS::currentHost(); \ + bool s_termSupportsColor = el::base::utils::OS::termSupportsColor(); \ + }\ + }\ + } +/// @brief Contains utilities for cross-platform date/time. This class make use of el::base::utils::Str +class DateTime : base::StaticClass { +public: + /// @brief Cross platform gettimeofday for Windows and unix platform. This can be used to determine current millisecond. + /// + /// @detail For unix system it uses gettimeofday(timeval*, timezone*) and for Windows, a seperate implementation is provided + /// @param [in,out] tv Pointer that gets updated + static void gettimeofday(struct timeval* tv) { +#if ELPP_OS_WINDOWS + if (tv != nullptr) { +# if ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS) + const unsigned __int64 delta_ = 11644473600000000Ui64; +# else + const unsigned __int64 delta_ = 11644473600000000ULL; +# endif // ELPP_COMPILER_MSVC || defined(_MSC_EXTENSIONS) + const double secOffSet = 0.000001; + const unsigned long usecOffSet = 1000000; + FILETIME fileTime; + GetSystemTimeAsFileTime(&fileTime); + unsigned __int64 present = 0; + present |= fileTime.dwHighDateTime; + present = present << 32; + present |= fileTime.dwLowDateTime; + present /= 10; // mic-sec + // Subtract the difference + present -= delta_; + tv->tv_sec = static_cast(present * secOffSet); + tv->tv_usec = static_cast(present % usecOffSet); + } +#else + ::gettimeofday(tv, nullptr); +#endif // ELPP_OS_WINDOWS + } + + /// @brief Gets current date and time with milliseconds. + /// @param format User provided date/time format + /// @param msWidth A pointer to base::MillisecondsWidth from configuration (non-null) + /// @returns string based date time in specified format. + static inline std::string getDateTime(const char* format, const base::MillisecondsWidth* msWidth) { + struct timeval currTime; + gettimeofday(&currTime); + struct ::tm timeInfo; + buildTimeInfo(&currTime, &timeInfo); + const int kBuffSize = 30; + char buff_[kBuffSize] = ""; + parseFormat(buff_, kBuffSize, format, &timeInfo, static_cast(currTime.tv_usec / msWidth->m_offset), msWidth); + return std::string(buff_); + } + + /// @brief Formats time to get unit accordingly, units like second if > 1000 or minutes if > 60000 etc + static base::type::string_t formatTime(unsigned long long time, base::TimestampUnit timestampUnit) { + double result = static_cast(time); + base::type::EnumType start = static_cast(timestampUnit); + const base::type::char_t* unit = base::consts::kTimeFormats[start].unit; + for (base::type::EnumType i = start; i < base::consts::kTimeFormatsCount - 1; ++i) { + if (result <= base::consts::kTimeFormats[i].value) { + break; + } + result /= base::consts::kTimeFormats[i].value; + unit = base::consts::kTimeFormats[i + 1].unit; + } + base::type::stringstream_t ss; + ss << result << " " << unit; + return ss.str(); + } + + /// @brief Gets time difference in milli/micro second depending on timestampUnit + static inline unsigned long long getTimeDifference(const struct timeval& endTime, const struct timeval& startTime, base::TimestampUnit timestampUnit) { + if (timestampUnit == base::TimestampUnit::Microsecond) { + return static_cast(static_cast(1000000 * endTime.tv_sec + endTime.tv_usec) - + static_cast(1000000 * startTime.tv_sec + startTime.tv_usec)); + } else { + return static_cast((((endTime.tv_sec - startTime.tv_sec) * 1000000) + (endTime.tv_usec - startTime.tv_usec)) / 1000); + } + } + +private: + static inline struct ::tm* buildTimeInfo(struct timeval* currTime, struct ::tm* timeInfo) { +#if ELPP_OS_UNIX + time_t rawTime = currTime->tv_sec; + ::localtime_r(&rawTime, timeInfo); + return timeInfo; +#else +# if ELPP_COMPILER_MSVC + ELPP_UNUSED(currTime); + time_t t; + _time64(&t); + localtime_s(timeInfo, &t); + return timeInfo; +# else + // For any other compilers that don't have CRT warnings issue e.g, MinGW or TDM GCC- we use different method + time_t rawTime = currTime->tv_sec; + struct tm* tmInf = localtime(&rawTime); + *timeInfo = *tmInf; + return timeInfo; +# endif // ELPP_COMPILER_MSVC +#endif // ELPP_OS_UNIX + } + static char* parseFormat(char* buf, std::size_t bufSz, const char* format, const struct tm* tInfo, + std::size_t msec, const base::MillisecondsWidth* msWidth) { + const char* bufLim = buf + bufSz; + for (; *format; ++format) { + if (*format == base::consts::kFormatSpecifierChar) { + switch (*++format) { + case base::consts::kFormatSpecifierChar: // Escape + break; + case '\0': // End + --format; + break; + case 'd': // Day + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mday, 2, buf, bufLim); + continue; + case 'a': // Day of week (short) + buf = base::utils::Str::addToBuff(base::consts::kDaysAbbrev[tInfo->tm_wday], buf, bufLim); + continue; + case 'A': // Day of week (long) + buf = base::utils::Str::addToBuff(base::consts::kDays[tInfo->tm_wday], buf, bufLim); + continue; + case 'M': // month + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_mon + 1, 2, buf, bufLim); + continue; + case 'b': // month (short) + buf = base::utils::Str::addToBuff(base::consts::kMonthsAbbrev[tInfo->tm_mon], buf, bufLim); + continue; + case 'B': // month (long) + buf = base::utils::Str::addToBuff(base::consts::kMonths[tInfo->tm_mon], buf, bufLim); + continue; + case 'y': // year (two digits) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 2, buf, bufLim); + continue; + case 'Y': // year (four digits) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_year + base::consts::kYearBase, 4, buf, bufLim); + continue; + case 'h': // hour (12-hour) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour % 12, 2, buf, bufLim); + continue; + case 'H': // hour (24-hour) + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_hour, 2, buf, bufLim); + continue; + case 'm': // minute + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_min, 2, buf, bufLim); + continue; + case 's': // second + buf = base::utils::Str::convertAndAddToBuff(tInfo->tm_sec, 2, buf, bufLim); + continue; + case 'z': // milliseconds + case 'g': + buf = base::utils::Str::convertAndAddToBuff(msec, msWidth->m_width, buf, bufLim); + continue; + case 'F': // AM/PM + buf = base::utils::Str::addToBuff((tInfo->tm_hour >= 12) ? base::consts::kPm : base::consts::kAm, buf, bufLim); + continue; + default: + continue; + } + } + if (buf == bufLim) break; + *buf++ = *format; + } + return buf; + } +}; +/// @brief Command line arguments for application if specified using el::Helpers::setArgs(..) or START_EASYLOGGINGPP(..) +class CommandLineArgs { +public: + CommandLineArgs(void) { + setArgs(0, static_cast(nullptr)); + } + CommandLineArgs(int argc, const char** argv) { + setArgs(argc, argv); + } + CommandLineArgs(int argc, char** argv) { + setArgs(argc, argv); + } + virtual ~CommandLineArgs(void) {} + /// @brief Sets arguments and parses them + inline void setArgs(int argc, const char** argv) { + setArgs(argc, const_cast(argv)); + } + /// @brief Sets arguments and parses them + inline void setArgs(int argc, char** argv) { + m_params.clear(); + m_paramsWithValue.clear(); + if (argc == 0 || argv == nullptr) { + return; + } + m_argc = argc; + m_argv = argv; + for (int i = 1; i < m_argc; ++i) { + const char* v = (strstr(m_argv[i], "=")); + if (v != nullptr && strlen(v) > 0) { + std::string key = std::string(m_argv[i]); + key = key.substr(0, key.find_first_of('=')); + if (hasParamWithValue(key.c_str())) { + ELPP_INTERNAL_INFO(1, "Skipping [" << key << "] arg since it already has value [" + << getParamValue(key.c_str()) << "]"); + } else { + m_paramsWithValue.insert(std::make_pair(key, std::string(v + 1))); + } + } + if (v == nullptr) { + if (hasParam(m_argv[i])) { + ELPP_INTERNAL_INFO(1, "Skipping [" << m_argv[i] << "] arg since it already exists"); + } else { + m_params.push_back(std::string(m_argv[i])); + } + } + } + } + /// @brief Returns true if arguments contain paramKey with a value (seperated by '=') + inline bool hasParamWithValue(const char* paramKey) const { + return m_paramsWithValue.find(std::string(paramKey)) != m_paramsWithValue.end(); + } + /// @brief Returns value of arguments + /// @see hasParamWithValue(const char*) + inline const char* getParamValue(const char* paramKey) const { + return m_paramsWithValue.find(std::string(paramKey))->second.c_str(); + } + /// @brief Return true if arguments has a param (not having a value) i,e without '=' + inline bool hasParam(const char* paramKey) const { + return std::find(m_params.begin(), m_params.end(), std::string(paramKey)) != m_params.end(); + } + /// @brief Returns true if no params available. This exclude argv[0] + inline bool empty(void) const { + return m_params.empty() && m_paramsWithValue.empty(); + } + /// @brief Returns total number of arguments. This exclude argv[0] + inline std::size_t size(void) const { + return m_params.size() + m_paramsWithValue.size(); + } + inline friend base::type::ostream_t& operator<<(base::type::ostream_t& os, const CommandLineArgs& c) { + for (int i = 1; i < c.m_argc; ++i) { + os << ELPP_LITERAL("[") << c.m_argv[i] << ELPP_LITERAL("]"); + if (i < c.m_argc - 1) { + os << ELPP_LITERAL(" "); + } + } + return os; + } + +private: + int m_argc; + char** m_argv; + std::map m_paramsWithValue; + std::vector m_params; +}; +/// @brief Abstract registry (aka repository) that provides basic interface for pointer repository specified by T_Ptr type. +/// +/// @detail Most of the functions are virtual final methods but anything implementing this abstract class should implement +/// unregisterAll() and deepCopy(const AbstractRegistry&) and write registerNew() method according to container +/// and few more methods; get() to find element, unregister() to unregister single entry. +/// Please note that this is thread-unsafe and should also implement thread-safety mechanisms in implementation. +template +class AbstractRegistry : public base::threading::ThreadSafe { +public: + typedef typename Container::iterator iterator; + typedef typename Container::const_iterator const_iterator; + + /// @brief Default constructor + AbstractRegistry(void) {} + + /// @brief Move constructor that is useful for base classes + AbstractRegistry(AbstractRegistry&& sr) { + if (this == &sr) { + return; + } + unregisterAll(); + m_list = std::move(sr.m_list); + } + + bool operator==(const AbstractRegistry& other) { + if (size() != other.size()) { + return false; + } + for (std::size_t i = 0; i < m_list.size(); ++i) { + if (m_list.at(i) != other.m_list.at(i)) { + return false; + } + } + return true; + } + + bool operator!=(const AbstractRegistry& other) { + if (size() != other.size()) { + return true; + } + for (std::size_t i = 0; i < m_list.size(); ++i) { + if (m_list.at(i) != other.m_list.at(i)) { + return true; + } + } + return false; + } + + /// @brief Assignment move operator + AbstractRegistry& operator=(AbstractRegistry&& sr) { + if (this == &sr) { + return *this; + } + unregisterAll(); + m_list = std::move(sr.m_list); + return *this; + } + + virtual ~AbstractRegistry(void) { + } + + /// @return Iterator pointer from start of repository + virtual inline iterator begin(void) ELPP_FINAL { + return m_list.begin(); + } + + /// @return Iterator pointer from end of repository + virtual inline iterator end(void) ELPP_FINAL { + return m_list.end(); + } + + + /// @return Constant iterator pointer from start of repository + virtual inline const_iterator cbegin(void) const ELPP_FINAL { + return m_list.cbegin(); + } + + /// @return End of repository + virtual inline const_iterator cend(void) const ELPP_FINAL { + return m_list.cend(); + } + + /// @return Whether or not repository is empty + virtual inline bool empty(void) const ELPP_FINAL { + return m_list.empty(); + } + + /// @return Size of repository + virtual inline std::size_t size(void) const ELPP_FINAL { + return m_list.size(); + } + + /// @brief Returns underlying container by reference + virtual inline Container& list(void) ELPP_FINAL { + return m_list; + } + + /// @brief Returns underlying container by constant reference. + virtual inline const Container& list(void) const ELPP_FINAL { + return m_list; + } + + /// @brief Unregisters all the pointers from current repository. + virtual void unregisterAll(void) = 0; + +protected: + virtual void deepCopy(const AbstractRegistry&) = 0; + void reinitDeepCopy(const AbstractRegistry& sr) { + unregisterAll(); + deepCopy(sr); + } + +private: + Container m_list; +}; + +/// @brief A pointer registry mechanism to manage memory and provide search functionalities. (non-predicate version) +/// +/// @detail NOTE: This is thread-unsafe implementation (although it contains lock function, it does not use these functions) +/// of AbstractRegistry. Any implementation of this class should be +/// explicitly (by using lock functions) +template +class Registry : public AbstractRegistry> { +public: + typedef typename Registry::iterator iterator; + typedef typename Registry::const_iterator const_iterator; + + Registry(void) {} + + /// @brief Copy constructor that is useful for base classes. Try to avoid this constructor, use move constructor. + Registry(const Registry& sr) : AbstractRegistry>() { + if (this == &sr) { + return; + } + this->reinitDeepCopy(sr); + } + + /// @brief Assignment operator that unregisters all the existing registeries and deeply copies each of repo element + /// @see unregisterAll() + /// @see deepCopy(const AbstractRegistry&) + Registry& operator=(const Registry& sr) { + if (this == &sr) { + return *this; + } + this->reinitDeepCopy(sr); + return *this; + } + + virtual ~Registry(void) { + unregisterAll(); + } + +protected: + virtual inline void unregisterAll(void) ELPP_FINAL { + if (!this->empty()) { + for (auto&& curr : this->list()) { + base::utils::safeDelete(curr.second); + } + this->list().clear(); + } + } + + /// @brief Registers new registry to repository. + virtual inline void registerNew(const T_Key& uniqKey, T_Ptr* ptr) ELPP_FINAL { + unregister(uniqKey); + this->list().insert(std::make_pair(uniqKey, ptr)); + } + + /// @brief Unregisters single entry mapped to specified unique key + inline void unregister(const T_Key& uniqKey) { + T_Ptr* existing = get(uniqKey); + if (existing != nullptr) { + base::utils::safeDelete(existing); + this->list().erase(uniqKey); + } + } + + /// @brief Gets pointer from repository. If none found, nullptr is returned. + inline T_Ptr* get(const T_Key& uniqKey) { + iterator it = this->list().find(uniqKey); + return it == this->list().end() + ? nullptr + : it->second; + } + +private: + virtual inline void deepCopy(const AbstractRegistry>& sr) ELPP_FINAL { + for (const_iterator it = sr.cbegin(); it != sr.cend(); ++it) { + registerNew(it->first, new T_Ptr(*it->second)); + } + } +}; + +/// @brief A pointer registry mechanism to manage memory and provide search functionalities. (predicate version) +/// +/// @detail NOTE: This is thread-unsafe implementation of AbstractRegistry. Any implementation of this class +/// should be made thread-safe explicitly +template +class RegistryWithPred : public AbstractRegistry> { +public: + typedef typename RegistryWithPred::iterator iterator; + typedef typename RegistryWithPred::const_iterator const_iterator; + + RegistryWithPred(void) { + } + + virtual ~RegistryWithPred(void) { + unregisterAll(); + } + + /// @brief Copy constructor that is useful for base classes. Try to avoid this constructor, use move constructor. + RegistryWithPred(const RegistryWithPred& sr) : AbstractRegistry>() { + if (this == &sr) { + return; + } + this->reinitDeepCopy(sr); + } + + /// @brief Assignment operator that unregisters all the existing registeries and deeply copies each of repo element + /// @see unregisterAll() + /// @see deepCopy(const AbstractRegistry&) + RegistryWithPred& operator=(const RegistryWithPred& sr) { + if (this == &sr) { + return *this; + } + this->reinitDeepCopy(sr); + return *this; + } + + friend inline base::type::ostream_t& operator<<(base::type::ostream_t& os, const RegistryWithPred& sr) { + for (const_iterator it = sr.list().begin(); it != sr.list().end(); ++it) { + os << ELPP_LITERAL(" ") << **it << ELPP_LITERAL("\n"); + } + return os; + } + +protected: + virtual inline void unregisterAll(void) ELPP_FINAL { + if (!this->empty()) { + for (auto&& curr : this->list()) { + base::utils::safeDelete(curr); + } + this->list().clear(); + } + } + + virtual void unregister(T_Ptr*& ptr) ELPP_FINAL { + if (ptr) { + iterator iter = this->begin(); + for (; iter != this->end(); ++iter) { + if (ptr == *iter) { + break; + } + } + if (iter != this->end() && *iter != nullptr) { + this->list().erase(iter); + base::utils::safeDelete(*iter); + } + } + } + + virtual inline void registerNew(T_Ptr* ptr) ELPP_FINAL { + this->list().push_back(ptr); + } + + /// @brief Gets pointer from repository with speicifed arguments. Arguments are passed to predicate + /// in order to validate pointer. + template + inline T_Ptr* get(const T& arg1, const T2 arg2) { + iterator iter = std::find_if(this->list().begin(), this->list().end(), Pred(arg1, arg2)); + if (iter != this->list().end() && *iter != nullptr) { + return *iter; + } + return nullptr; + } + +private: + virtual inline void deepCopy(const AbstractRegistry>& sr) { + for (const_iterator it = sr.list().begin(); it != sr.list().end(); ++it) { + registerNew(new T_Ptr(**it)); + } + } +}; + +} // namespace utils +} // namespace base +/// @brief Base of Easylogging++ friendly class +/// +/// @detail After inheriting this class publicly, implement pure-virtual function `void log(std::ostream&) const` +class Loggable { +public: + virtual ~Loggable(void) {} + virtual void log(el::base::type::ostream_t&) const = 0; +private: + friend inline el::base::type::ostream_t& operator<<(el::base::type::ostream_t& os, const Loggable& loggable) { + loggable.log(os); + return os; + } +}; +namespace base { +/// @brief Represents log format containing flags and date format. This is used internally to start initial log +class LogFormat : public Loggable { +public: + LogFormat(void) : + m_level(Level::Unknown), + m_userFormat(base::type::string_t()), + m_format(base::type::string_t()), + m_dateTimeFormat(std::string()), + m_flags(0x0) { + } + + LogFormat(Level level, const base::type::string_t& format) + : m_level(level), m_userFormat(format) { + parseFromFormat(m_userFormat); + } + + LogFormat(const LogFormat& logFormat) { + m_level = logFormat.m_level; + m_userFormat = logFormat.m_userFormat; + m_format = logFormat.m_format; + m_dateTimeFormat = logFormat.m_dateTimeFormat; + m_flags = logFormat.m_flags; + } + + LogFormat(LogFormat&& logFormat) { + m_level = std::move(logFormat.m_level); + m_userFormat = std::move(logFormat.m_userFormat); + m_format = std::move(logFormat.m_format); + m_dateTimeFormat = std::move(logFormat.m_dateTimeFormat); + m_flags = std::move(logFormat.m_flags); + } + + LogFormat& operator=(const LogFormat& logFormat) { + m_level = logFormat.m_level; + m_userFormat = logFormat.m_userFormat; + m_dateTimeFormat = logFormat.m_dateTimeFormat; + m_flags = logFormat.m_flags; + return *this; + } + + virtual ~LogFormat(void) { + } + + inline bool operator==(const LogFormat& other) { + return m_level == other.m_level && m_userFormat == other.m_userFormat && m_format == other.m_format && + m_dateTimeFormat == other.m_dateTimeFormat && m_flags == other.m_flags; + } + + /// @brief Updates format to be used while logging. + /// @param userFormat User provided format + void parseFromFormat(const base::type::string_t& userFormat) { + // We make copy because we will be changing the format + // i.e, removing user provided date format from original format + // and then storing it. + base::type::string_t formatCopy = userFormat; + m_flags = 0x0; + auto conditionalAddFlag = [&](const base::type::char_t* specifier, base::FormatFlags flag) { + std::size_t foundAt = base::type::string_t::npos; + while ((foundAt = formatCopy.find(specifier, foundAt + 1)) != base::type::string_t::npos){ + if (foundAt > 0 && formatCopy[foundAt - 1] == base::consts::kFormatSpecifierChar) { + if (hasFlag(flag)) { + // If we already have flag we remove the escape chars so that '%%' is turned to '%' + // even after specifier resolution - this is because we only replaceFirst specifier + formatCopy.erase(foundAt > 0 ? foundAt - 1 : 0, 1); + ++foundAt; + } + } else { + if (!hasFlag(flag)) addFlag(flag); + } + } + }; + conditionalAddFlag(base::consts::kAppNameFormatSpecifier, base::FormatFlags::AppName); + conditionalAddFlag(base::consts::kSeverityLevelFormatSpecifier, base::FormatFlags::Level); + conditionalAddFlag(base::consts::kSeverityLevelShortFormatSpecifier, base::FormatFlags::LevelShort); + conditionalAddFlag(base::consts::kLoggerIdFormatSpecifier, base::FormatFlags::LoggerId); + conditionalAddFlag(base::consts::kThreadIdFormatSpecifier, base::FormatFlags::ThreadId); + conditionalAddFlag(base::consts::kLogFileFormatSpecifier, base::FormatFlags::File); + conditionalAddFlag(base::consts::kLogFileBaseFormatSpecifier, base::FormatFlags::FileBase); + conditionalAddFlag(base::consts::kLogLineFormatSpecifier, base::FormatFlags::Line); + conditionalAddFlag(base::consts::kLogLocationFormatSpecifier, base::FormatFlags::Location); + conditionalAddFlag(base::consts::kLogFunctionFormatSpecifier, base::FormatFlags::Function); + conditionalAddFlag(base::consts::kCurrentUserFormatSpecifier, base::FormatFlags::User); + conditionalAddFlag(base::consts::kCurrentHostFormatSpecifier, base::FormatFlags::Host); + conditionalAddFlag(base::consts::kMessageFormatSpecifier, base::FormatFlags::LogMessage); + conditionalAddFlag(base::consts::kVerboseLevelFormatSpecifier, base::FormatFlags::VerboseLevel); + // For date/time we need to extract user's date format first + std::size_t dateIndex = std::string::npos; + if ((dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier)) != std::string::npos) { + while (dateIndex > 0 && formatCopy[dateIndex - 1] == base::consts::kFormatSpecifierChar) { + dateIndex = formatCopy.find(base::consts::kDateTimeFormatSpecifier, dateIndex + 1); + } + if (dateIndex != std::string::npos) { + addFlag(base::FormatFlags::DateTime); + updateDateFormat(dateIndex, formatCopy); + } + } + m_format = formatCopy; + updateFormatSpec(); + } + + inline Level level(void) const { + return m_level; + } + + inline const base::type::string_t& userFormat(void) const { + return m_userFormat; + } + + inline const base::type::string_t& format(void) const { + return m_format; + } + + inline const std::string& dateTimeFormat(void) const { + return m_dateTimeFormat; + } + + inline base::type::EnumType flags(void) const { + return m_flags; + } + + inline bool hasFlag(base::FormatFlags flag) const { + return base::utils::hasFlag(flag, m_flags); + } + + virtual void log(el::base::type::ostream_t& os) const { + os << m_format; + } + +protected: + /// @brief Updates date time format if available in currFormat. + /// @param index Index where %datetime, %date or %time was found + /// @param [in,out] currFormat current format that is being used to format + virtual void updateDateFormat(std::size_t index, base::type::string_t& currFormat) ELPP_FINAL { + if (hasFlag(base::FormatFlags::DateTime)) { + index += ELPP_STRLEN(base::consts::kDateTimeFormatSpecifier); + } + const base::type::char_t* ptr = currFormat.c_str() + index; + if ((currFormat.size() > index) && (ptr[0] == '{')) { + // User has provided format for date/time + ++ptr; + int count = 1; // Start by 1 in order to remove starting brace + std::stringstream ss; + for (; *ptr; ++ptr, ++count) { + if (*ptr == '}') { + ++count; // In order to remove ending brace + break; + } + ss << *ptr; + } + currFormat.erase(index, count); + m_dateTimeFormat = ss.str(); + } else { + // No format provided, use default + if (hasFlag(base::FormatFlags::DateTime)) { + m_dateTimeFormat = std::string(base::consts::kDefaultDateTimeFormat); + } + } + } + + /// @brief Updates %level from format. This is so that we dont have to do it at log-writing-time. It uses m_format and m_level + virtual void updateFormatSpec(void) ELPP_FINAL { + // Do not use switch over strongly typed enums because Intel C++ compilers dont support them yet. + if (m_level == Level::Debug) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kDebugLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kDebugLevelShortLogValue); + } else if (m_level == Level::Info) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kInfoLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kInfoLevelShortLogValue); + } else if (m_level == Level::Warning) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kWarningLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kWarningLevelShortLogValue); + } else if (m_level == Level::Error) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kErrorLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kErrorLevelShortLogValue); + } else if (m_level == Level::Fatal) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kFatalLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kFatalLevelShortLogValue); + } else if (m_level == Level::Verbose) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kVerboseLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kVerboseLevelShortLogValue); + } else if (m_level == Level::Trace) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelFormatSpecifier, + base::consts::kTraceLevelLogValue); + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kSeverityLevelShortFormatSpecifier, + base::consts::kTraceLevelShortLogValue); + } + if (hasFlag(base::FormatFlags::User)) { + std::string s = base::utils::s_currentUser; + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentUserFormatSpecifier, + base::utils::s_currentUser); + } + if (hasFlag(base::FormatFlags::Host)) { + base::utils::Str::replaceFirstWithEscape(m_format, base::consts::kCurrentHostFormatSpecifier, + base::utils::s_currentHost); + } + // Ignore Level::Global and Level::Unknown + } + + inline void addFlag(base::FormatFlags flag) { + base::utils::addFlag(flag, &m_flags); + } + +private: + Level m_level; + base::type::string_t m_userFormat; + base::type::string_t m_format; + std::string m_dateTimeFormat; + base::type::EnumType m_flags; + friend class el::Logger; // To resolve loggerId format specifier easily +}; +} // namespace base +/// @brief Resolving function for format specifier +typedef std::function FormatSpecifierValueResolver; +/// @brief User-provided custom format specifier +/// @see el::Helpers::installCustomFormatSpecifier +/// @see FormatSpecifierValueResolver +class CustomFormatSpecifier { +public: + CustomFormatSpecifier(const char* formatSpecifier, const FormatSpecifierValueResolver& resolver) : + m_formatSpecifier(formatSpecifier), m_resolver(resolver) {} + inline const char* formatSpecifier(void) const { return m_formatSpecifier; } + inline const FormatSpecifierValueResolver& resolver(void) const { return m_resolver; } + inline bool operator==(const char* formatSpecifier) { + return strcmp(m_formatSpecifier, formatSpecifier) == 0; + } + +private: + const char* m_formatSpecifier; + FormatSpecifierValueResolver m_resolver; +}; +/// @brief Represents single configuration that has representing level, configuration type and a string based value. +/// +/// @detail String based value means any value either its boolean, integer or string itself, it will be embedded inside quotes +/// and will be parsed later. +/// +/// Consider some examples below: +/// * el::Configuration confEnabledInfo(el::Level::Info, el::ConfigurationType::Enabled, "true"); +/// * el::Configuration confMaxLogFileSizeInfo(el::Level::Info, el::ConfigurationType::MaxLogFileSize, "2048"); +/// * el::Configuration confFilenameInfo(el::Level::Info, el::ConfigurationType::Filename, "/var/log/my.log"); +class Configuration : public Loggable { +public: + Configuration(const Configuration& c) : + m_level(c.m_level), + m_configurationType(c.m_configurationType), + m_value(c.m_value) { + } + + Configuration& operator=(const Configuration& c) { + m_level = c.m_level; + m_configurationType = c.m_configurationType; + m_value = c.m_value; + return *this; + } + + virtual ~Configuration(void) { + } + + /// @brief Full constructor used to sets value of configuration + Configuration(Level level, ConfigurationType configurationType, const std::string& value) : + m_level(level), + m_configurationType(configurationType), + m_value(value) { + } + + /// @brief Gets level of current configuration + inline Level level(void) const { + return m_level; + } + + /// @brief Gets configuration type of current configuration + inline ConfigurationType configurationType(void) const { + return m_configurationType; + } + + /// @brief Gets string based configuration value + inline const std::string& value(void) const { + return m_value; + } + + /// @brief Set string based configuration value + /// @param value Value to set. Values have to be std::string; For boolean values use "true", "false", for any integral values + /// use them in quotes. They will be parsed when configuring + inline void setValue(const std::string& value) { + m_value = value; + } + + virtual inline void log(el::base::type::ostream_t& os) const { + os << LevelHelper::convertToString(m_level) + << ELPP_LITERAL(" ") << ConfigurationTypeHelper::convertToString(m_configurationType) + << ELPP_LITERAL(" = ") << m_value.c_str(); + } + + /// @brief Used to find configuration from configuration (pointers) repository. Avoid using it. + class Predicate { + public: + Predicate(Level level, ConfigurationType configurationType) : + m_level(level), + m_configurationType(configurationType) { + } + + inline bool operator()(const Configuration* conf) const { + return ((conf != nullptr) && (conf->level() == m_level) && (conf->configurationType() == m_configurationType)); + } + + private: + Level m_level; + ConfigurationType m_configurationType; + }; + +private: + Level m_level; + ConfigurationType m_configurationType; + std::string m_value; +}; + +/// @brief Thread-safe Configuration repository +/// +/// @detail This repository represents configurations for all the levels and configuration type mapped to a value. +class Configurations : public base::utils::RegistryWithPred { +public: + /// @brief Default constructor with empty repository + Configurations(void) : + m_configurationFile(std::string()), + m_isFromFile(false) { + } + + /// @brief Constructor used to set configurations using configuration file. + /// @param configurationFile Full path to configuration file + /// @param useDefaultsForRemaining Lets you set the remaining configurations to default. + /// @param base If provided, this configuration will be based off existing repository that this argument is pointing to. + /// @see parseFromFile(const std::string&, Configurations* base) + /// @see setRemainingToDefault() + Configurations(const std::string& configurationFile, bool useDefaultsForRemaining = true, Configurations* base = nullptr) : + m_configurationFile(configurationFile), + m_isFromFile(false) { + parseFromFile(configurationFile, base); + if (useDefaultsForRemaining) { + setRemainingToDefault(); + } + } + + virtual ~Configurations(void) { + } + + /// @brief Parses configuration from file. + /// @param configurationFile Full path to configuration file + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration file. + /// @return True if successfully parsed, false otherwise. You may define 'ELPP_DEBUG_ASSERT_FAILURE' to make sure you + /// do not proceed without successful parse. + inline bool parseFromFile(const std::string& configurationFile, Configurations* base = nullptr) { + // We initial assertion with true because if we have assertion diabled, we want to pass this + // check and if assertion is enabled we will have values re-assigned any way. + bool assertionPassed = true; + ELPP_ASSERT((assertionPassed = base::utils::File::pathExists(configurationFile.c_str(), true)), + "Configuration file [" << configurationFile << "] does not exist!"); + if (!assertionPassed) { + return false; + } + bool success = Parser::parseFromFile(configurationFile, this, base); + m_isFromFile = success; + return success; + } + + /// @brief Parse configurations from configuration string. + /// + /// @detail This configuration string has same syntax as configuration file contents. Make sure all the necessary + /// new line characters are provided. + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration text. + /// @return True if successfully parsed, false otherwise. You may define 'ELPP_DEBUG_ASSERT_FAILURE' to make sure you + /// do not proceed without successful parse. + inline bool parseFromText(const std::string& configurationsString, Configurations* base = nullptr) { + bool success = Parser::parseFromText(configurationsString, this, base); + if (success) { + m_isFromFile = false; + } + return success; + } + + /// @brief Sets configuration based-off an existing configurations. + /// @param base Pointer to existing configurations. + inline void setFromBase(Configurations* base) { + if (base == nullptr || base == this) { + return; + } + base::threading::ScopedLock scopedLock(base->lock()); + for (Configuration*& conf : base->list()) { + set(conf); + } + } + + /// @brief Determines whether or not specified configuration type exists in the repository. + /// + /// @detail Returns as soon as first level is found. + /// @param configurationType Type of configuration to check existence for. + bool hasConfiguration(ConfigurationType configurationType) { + base::type::EnumType lIndex = LevelHelper::kMinValid; + bool result = false; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + if (hasConfiguration(LevelHelper::castFromInt(lIndex), configurationType)) { + result = true; + } + return result; + }); + return result; + } + + /// @brief Determines whether or not specified configuration type exists for specified level + /// @param level Level to check + /// @param configurationType Type of configuration to check existence for. + inline bool hasConfiguration(Level level, ConfigurationType configurationType) { + base::threading::ScopedLock scopedLock(lock()); +#if ELPP_COMPILER_INTEL + // We cant specify template types here, Intel C++ throws compilation error + // "error: type name is not allowed" + return RegistryWithPred::get(level, configurationType) != nullptr; +#else + return RegistryWithPred::get(level, configurationType) != nullptr; +#endif // ELPP_COMPILER_INTEL + } + + /// @brief Sets value of configuration for specified level. + /// + /// @detail Any existing configuration for specified level will be replaced. Also note that configuration types + /// ConfigurationType::MillisecondsWidth and ConfigurationType::PerformanceTracking will be ignored if not set for + /// Level::Global because these configurations are not dependant on level. + /// @param level Level to set configuration for (el::Level). + /// @param configurationType Type of configuration (el::ConfigurationType) + /// @param value A string based value. Regardless of what the data type of configuration is, it will always be string + /// from users' point of view. This is then parsed later to be used internally. + /// @see Configuration::setValue(const std::string& value) + /// @see el::Level + /// @see el::ConfigurationType + inline void set(Level level, ConfigurationType configurationType, const std::string& value) { + base::threading::ScopedLock scopedLock(lock()); + unsafeSet(level, configurationType, value); // This is not unsafe anymore as we have locked mutex + if (level == Level::Global) { + unsafeSetGlobally(configurationType, value, false); // Again this is not unsafe either + } + } + + /// @brief Sets single configuration based on other single configuration. + /// @see set(Level level, ConfigurationType configurationType, const std::string& value) + inline void set(Configuration* conf) { + if (conf == nullptr) { + return; + } + set(conf->level(), conf->configurationType(), conf->value()); + } + + inline Configuration* get(Level level, ConfigurationType configurationType) { + base::threading::ScopedLock scopedLock(lock()); + return RegistryWithPred::get(level, configurationType); + } + + /// @brief Sets configuration for all levels. + /// @param configurationType Type of configuration + /// @param value String based value + /// @see Configurations::set(Level level, ConfigurationType configurationType, const std::string& value) + inline void setGlobally(ConfigurationType configurationType, const std::string& value) { + setGlobally(configurationType, value, false); + } + + /// @brief Clears repository so that all the configurations are unset + inline void clear(void) { + base::threading::ScopedLock scopedLock(lock()); + unregisterAll(); + } + + /// @brief Gets configuration file used in parsing this configurations. + /// + /// @detail If this repository was set manually or by text this returns empty string. + inline const std::string& configurationFile(void) const { + return m_configurationFile; + } + + /// @brief Sets configurations to "factory based" configurations. + void setToDefault(void) { + setGlobally(ConfigurationType::Enabled, std::string("true"), true); +#if !defined(ELPP_NO_DEFAULT_LOG_FILE) + setGlobally(ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile), true); +#else + ELPP_UNUSED(base::consts::kDefaultLogFile); +#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE) + setGlobally(ConfigurationType::ToFile, std::string("true"), true); + setGlobally(ConfigurationType::ToStandardOutput, std::string("true"), true); + setGlobally(ConfigurationType::MillisecondsWidth, std::string("3"), true); + setGlobally(ConfigurationType::PerformanceTracking, std::string("true"), true); + setGlobally(ConfigurationType::MaxLogFileSize, std::string("0"), true); + setGlobally(ConfigurationType::LogFlushThreshold, std::string("0"), true); + + setGlobally(ConfigurationType::Format, std::string("%datetime %level [%logger] %msg"), true); + set(Level::Debug, ConfigurationType::Format, std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg")); + // INFO and WARNING are set to default by Level::Global + set(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + set(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + set(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg")); + set(Level::Trace, ConfigurationType::Format, std::string("%datetime %level [%logger] [%func] [%loc] %msg")); + } + + /// @brief Lets you set the remaining configurations to default. + /// + /// @detail By remaining, it means that the level/type a configuration does not exist for. + /// This function is useful when you want to minimize chances of failures, e.g, if you have a configuration file that sets + /// configuration for all the configurations except for Enabled or not, we use this so that ENABLED is set to default i.e, + /// true. If you dont do this explicitley (either by calling this function or by using second param in Constructor + /// and try to access a value, an error is thrown + void setRemainingToDefault(void) { + base::threading::ScopedLock scopedLock(lock()); + unsafeSetIfNotExist(Level::Global, ConfigurationType::Enabled, std::string("true")); +#if !defined(ELPP_NO_DEFAULT_LOG_FILE) + unsafeSetIfNotExist(Level::Global, ConfigurationType::Filename, std::string(base::consts::kDefaultLogFile)); +#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE) + unsafeSetIfNotExist(Level::Global, ConfigurationType::ToFile, std::string("true")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::ToStandardOutput, std::string("true")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::MillisecondsWidth, std::string("3")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::PerformanceTracking, std::string("true")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::MaxLogFileSize, std::string("0")); + unsafeSetIfNotExist(Level::Global, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + unsafeSetIfNotExist(Level::Debug, ConfigurationType::Format, + std::string("%datetime %level [%logger] [%user@%host] [%func] [%loc] %msg")); + // INFO and WARNING are set to default by Level::Global + unsafeSetIfNotExist(Level::Error, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + unsafeSetIfNotExist(Level::Fatal, ConfigurationType::Format, std::string("%datetime %level [%logger] %msg")); + unsafeSetIfNotExist(Level::Verbose, ConfigurationType::Format, std::string("%datetime %level-%vlevel [%logger] %msg")); + unsafeSetIfNotExist(Level::Trace, ConfigurationType::Format, std::string("%datetime %level [%logger] [%func] [%loc] %msg")); + } + + /// @brief Parser used internally to parse configurations from file or text. + /// + /// @detail This class makes use of base::utils::Str. + /// You should not need this unless you are working on some tool for Easylogging++ + class Parser : base::StaticClass { + public: + /// @brief Parses configuration from file. + /// @param configurationFile Full path to configuration file + /// @param sender Sender configurations pointer. Usually 'this' is used from calling class + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration file. + /// @return True if successfully parsed, false otherwise. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make sure you + /// do not proceed without successful parse. + static bool parseFromFile(const std::string& configurationFile, Configurations* sender, Configurations* base = nullptr) { + sender->setFromBase(base); + std::ifstream fileStream_(configurationFile.c_str(), std::ifstream::in); + ELPP_ASSERT(fileStream_.is_open(), "Unable to open configuration file [" << configurationFile << "] for parsing."); + bool parsedSuccessfully = false; + std::string line = std::string(); + Level currLevel = Level::Unknown; + std::string currConfigStr = std::string(); + std::string currLevelStr = std::string(); + while (fileStream_.good()) { + std::getline(fileStream_, line); + parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender); + ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line); + } + return parsedSuccessfully; + } + + /// @brief Parse configurations from configuration string. + /// + /// @detail This configuration string has same syntax as configuration file contents. Make sure all the necessary + /// new line characters are provided. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make sure you + /// do not proceed without successful parse (This is recommended) + /// @param configurationsString + /// @param sender Sender configurations pointer. Usually 'this' is used from calling class + /// @param base Configurations to base new configuration repository off. This value is used when you want to use + /// existing Configurations to base all the values and then set rest of configuration via configuration text. + /// @return True if successfully parsed, false otherwise. + static bool parseFromText(const std::string& configurationsString, Configurations* sender, Configurations* base = nullptr) { + sender->setFromBase(base); + bool parsedSuccessfully = false; + std::stringstream ss(configurationsString); + std::string line = std::string(); + Level currLevel = Level::Unknown; + std::string currConfigStr = std::string(); + std::string currLevelStr = std::string(); + while (std::getline(ss, line)) { + parsedSuccessfully = parseLine(&line, &currConfigStr, &currLevelStr, &currLevel, sender); + ELPP_ASSERT(parsedSuccessfully, "Unable to parse configuration line: " << line); + } + return parsedSuccessfully; + } + + private: + friend class el::Loggers; + static void ignoreComments(std::string* line) { + std::size_t foundAt = 0; + std::size_t quotesStart = line->find("\""); + std::size_t quotesEnd = std::string::npos; + if (quotesStart != std::string::npos) { + quotesEnd = line->find("\"", quotesStart + 1); + while (quotesEnd != std::string::npos && line->at(quotesEnd - 1) == '\\') { + // Do not erase slash yet - we will erase it in parseLine(..) while loop + quotesEnd = line->find("\"", quotesEnd + 2); + } + } + if ((foundAt = line->find(base::consts::kConfigurationComment)) != std::string::npos) { + if (foundAt < quotesEnd) { + foundAt = line->find(base::consts::kConfigurationComment, quotesEnd + 1); + } + *line = line->substr(0, foundAt); + } + } + static inline bool isLevel(const std::string& line) { + return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLevel)); + } + + static inline bool isComment(const std::string& line) { + return base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationComment)); + } + + static inline bool isConfig(const std::string& line) { + std::size_t assignment = line.find('='); + return line != "" && + (line[0] >= 65 || line[0] <= 90 || line[0] >= 97 || line[0] <= 122) && + (assignment != std::string::npos) && + (line.size() > assignment); + } + + static bool parseLine(std::string* line, std::string* currConfigStr, std::string* currLevelStr, Level* currLevel, Configurations* conf) { + ConfigurationType currConfig = ConfigurationType::Unknown; + std::string currValue = std::string(); + *line = base::utils::Str::trim(*line); + if (isComment(*line)) return true; + ignoreComments(line); + *line = base::utils::Str::trim(*line); + if (line->empty()) { + // Comment ignored + return true; + } + if (isLevel(*line)) { + if (line->size() <= 2) { + return true; + } + *currLevelStr = line->substr(1, line->size() - 2); + *currLevelStr = base::utils::Str::toUpper(*currLevelStr); + *currLevelStr = base::utils::Str::trim(*currLevelStr); + *currLevel = LevelHelper::convertFromString(currLevelStr->c_str()); + return true; + } + if (isConfig(*line)) { + std::size_t assignment = line->find('='); + *currConfigStr = line->substr(0, assignment); + *currConfigStr = base::utils::Str::toUpper(*currConfigStr); + *currConfigStr = base::utils::Str::trim(*currConfigStr); + currConfig = ConfigurationTypeHelper::convertFromString(currConfigStr->c_str()); + currValue = line->substr(assignment + 1); + currValue = base::utils::Str::trim(currValue); + std::size_t quotesStart = currValue.find("\"", 0); + std::size_t quotesEnd = std::string::npos; + if (quotesStart != std::string::npos) { + quotesEnd = currValue.find("\"", quotesStart + 1); + while (quotesEnd != std::string::npos && currValue.at(quotesEnd - 1) == '\\') { + currValue = currValue.erase(quotesEnd - 1, 1); + quotesEnd = currValue.find("\"", quotesEnd + 2); + } + } + if (quotesStart != std::string::npos && quotesEnd != std::string::npos) { + // Quote provided - check and strip if valid + ELPP_ASSERT((quotesStart < quotesEnd), "Configuration error - No ending quote found in [" + << currConfigStr << "]"); + ELPP_ASSERT((quotesStart + 1 != quotesEnd), "Empty configuration value for [" << currConfigStr << "]"); + if ((quotesStart != quotesEnd) && (quotesStart + 1 != quotesEnd)) { + // Explicit check in case if assertion is disabled + currValue = currValue.substr(quotesStart + 1, quotesEnd - 1); + } + } + } + ELPP_ASSERT(*currLevel != Level::Unknown, "Unrecognized severity level [" << *currLevelStr << "]"); + ELPP_ASSERT(currConfig != ConfigurationType::Unknown, "Unrecognized configuration [" << *currConfigStr << "]"); + if (*currLevel == Level::Unknown || currConfig == ConfigurationType::Unknown) { + return false; // unrecognizable level or config + } + conf->set(*currLevel, currConfig, currValue); + return true; + } + }; + +private: + std::string m_configurationFile; + bool m_isFromFile; + friend class el::Loggers; + + /// @brief Unsafely sets configuration if does not already exist + void unsafeSetIfNotExist(Level level, ConfigurationType configurationType, const std::string& value) { + Configuration* conf = RegistryWithPred::get(level, configurationType); + if (conf == nullptr) { + unsafeSet(level, configurationType, value); + } + } + + /// @brief Thread unsafe set + void unsafeSet(Level level, ConfigurationType configurationType, const std::string& value) { + Configuration* conf = RegistryWithPred::get(level, configurationType); + if (conf == nullptr) { + registerNew(new Configuration(level, configurationType, value)); + } else { + conf->setValue(value); + } + if (level == Level::Global) { + unsafeSetGlobally(configurationType, value, false); + } + } + + /// @brief Sets configurations for all levels including Level::Global if includeGlobalLevel is true + /// @see Configurations::setGlobally(ConfigurationType configurationType, const std::string& value) + void setGlobally(ConfigurationType configurationType, const std::string& value, bool includeGlobalLevel) { + if (includeGlobalLevel) { + set(Level::Global, configurationType, value); + } + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + set(LevelHelper::castFromInt(lIndex), configurationType, value); + return false; // Do not break lambda function yet as we need to set all levels regardless + }); + } + + /// @brief Sets configurations (Unsafely) for all levels including Level::Global if includeGlobalLevel is true + /// @see Configurations::setGlobally(ConfigurationType configurationType, const std::string& value) + void unsafeSetGlobally(ConfigurationType configurationType, const std::string& value, bool includeGlobalLevel) { + if (includeGlobalLevel) { + unsafeSet(Level::Global, configurationType, value); + } + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + unsafeSet(LevelHelper::castFromInt(lIndex), configurationType, value); + return false; // Do not break lambda function yet as we need to set all levels regardless + }); + } +}; + +namespace base { +typedef std::shared_ptr FileStreamPtr; +typedef std::map LogStreamsReferenceMap; +/// @brief Configurations with data types. +/// +/// @detail el::Configurations have string based values. This is whats used internally in order to read correct configurations. +/// This is to perform faster while writing logs using correct configurations. +/// +/// This is thread safe and final class containing non-virtual destructor (means nothing should inherit this class) +class TypedConfigurations : public base::threading::ThreadSafe { +public: + /// @brief Constructor to initialize (construct) the object off el::Configurations + /// @param configurations Configurations pointer/reference to base this typed configurations off. + /// @param logStreamsReference Use ELPP->registeredLoggers()->logStreamsReference() + TypedConfigurations(Configurations* configurations, base::LogStreamsReferenceMap* logStreamsReference) { + m_configurations = configurations; + m_logStreamsReference = logStreamsReference; + build(m_configurations); + } + + TypedConfigurations(const TypedConfigurations& other) { + this->m_configurations = other.m_configurations; + this->m_logStreamsReference = other.m_logStreamsReference; + build(m_configurations); + } + + virtual ~TypedConfigurations(void) { + } + + const Configurations* configurations(void) const { + return m_configurations; + } + + inline bool enabled(Level level) { + return getConfigByVal(level, &m_enabledMap, "enabled"); + } + + inline bool toFile(Level level) { + return getConfigByVal(level, &m_toFileMap, "toFile"); + } + + inline const std::string& filename(Level level) { + return getConfigByRef(level, &m_filenameMap, "filename"); + } + + inline bool toStandardOutput(Level level) { + return getConfigByVal(level, &m_toStandardOutputMap, "toStandardOutput"); + } + + inline const base::LogFormat& logFormat(Level level) { + return getConfigByRef(level, &m_logFormatMap, "logFormat"); + } + + inline const base::MillisecondsWidth& millisecondsWidth(Level level = Level::Global) { + return getConfigByRef(level, &m_millisecondsWidthMap, "millisecondsWidth"); + } + + inline bool performanceTracking(Level level = Level::Global) { + return getConfigByVal(level, &m_performanceTrackingMap, "performanceTracking"); + } + + inline base::type::fstream_t* fileStream(Level level) { + return getConfigByRef(level, &m_fileStreamMap, "fileStream").get(); + } + + inline std::size_t maxLogFileSize(Level level) { + return getConfigByVal(level, &m_maxLogFileSizeMap, "maxLogFileSize"); + } + + inline std::size_t logFlushThreshold(Level level) { + return getConfigByVal(level, &m_logFlushThresholdMap, "logFlushThreshold"); + } + +private: + Configurations* m_configurations; + std::map m_enabledMap; + std::map m_toFileMap; + std::map m_filenameMap; + std::map m_toStandardOutputMap; + std::map m_logFormatMap; + std::map m_millisecondsWidthMap; + std::map m_performanceTrackingMap; + std::map m_fileStreamMap; + std::map m_maxLogFileSizeMap; + std::map m_logFlushThresholdMap; + base::LogStreamsReferenceMap* m_logStreamsReference; + + friend class el::Helpers; + friend class el::base::MessageBuilder; + friend class el::base::Writer; + friend class el::base::DefaultLogDispatchCallback; + friend class el::base::LogDispatcher; + + template + inline Conf_T getConfigByVal(Level level, const std::map* confMap, const char* confName) { + base::threading::ScopedLock scopedLock(lock()); + return unsafeGetConfigByVal(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope + } + + template + inline Conf_T& getConfigByRef(Level level, std::map* confMap, const char* confName) { + base::threading::ScopedLock scopedLock(lock()); + return unsafeGetConfigByRef(level, confMap, confName); // This is not unsafe anymore - mutex locked in scope + } + + template + inline Conf_T unsafeGetConfigByVal(Level level, const std::map* confMap, const char* confName) { + ELPP_UNUSED(confName); + typename std::map::const_iterator it = confMap->find(level); + if (it == confMap->end()) { + try { + return confMap->at(Level::Global); + } catch (...) { + ELPP_INTERNAL_ERROR("Unable to get configuration [" << confName << "] for level [" + << LevelHelper::convertToString(level) << "]" + << std::endl << "Please ensure you have properly configured logger.", false); + return Conf_T(); + } + } + return it->second; + } + + template + inline Conf_T& unsafeGetConfigByRef(Level level, std::map* confMap, const char* confName) { + ELPP_UNUSED(confName); + typename std::map::iterator it = confMap->find(level); + if (it == confMap->end()) { + try { + return confMap->at(Level::Global); + } catch (...) { + ELPP_INTERNAL_ERROR("Unable to get configuration [" << confName << "] for level [" + << LevelHelper::convertToString(level) << "]" + << std::endl << "Please ensure you have properly configured logger.", false); + } + } + return it->second; + } + + template + void setValue(Level level, const Conf_T& value, std::map* confMap, bool includeGlobalLevel = true) { + // If map is empty and we are allowed to add into generic level (Level::Global), do it! + if (confMap->empty() && includeGlobalLevel) { + confMap->insert(std::make_pair(Level::Global, value)); + return; + } + // If same value exist in generic level already, dont add it to explicit level + typename std::map::iterator it = confMap->find(Level::Global); + if (it != confMap->end() && it->second == value) { + return; + } + // Now make sure we dont double up values if we really need to add it to explicit level + it = confMap->find(level); + if (it == confMap->end()) { + // Value not found for level, add new + confMap->insert(std::make_pair(level, value)); + } else { + // Value found, just update value + confMap->at(level) = value; + } + } + + void build(Configurations* configurations) { + base::threading::ScopedLock scopedLock(lock()); + auto getBool = [] (std::string boolStr) -> bool { // Pass by value for trimming + base::utils::Str::trim(boolStr); + return (boolStr == "TRUE" || boolStr == "true" || boolStr == "1"); + }; + std::vector withFileSizeLimit; + for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) { + Configuration* conf = *it; + // We cannot use switch on strong enums because Intel C++ dont support them yet + if (conf->configurationType() == ConfigurationType::Enabled) { + setValue(conf->level(), getBool(conf->value()), &m_enabledMap); + } else if (conf->configurationType() == ConfigurationType::ToFile) { + setValue(conf->level(), getBool(conf->value()), &m_toFileMap); + } else if (conf->configurationType() == ConfigurationType::ToStandardOutput) { + setValue(conf->level(), getBool(conf->value()), &m_toStandardOutputMap); + } else if (conf->configurationType() == ConfigurationType::Filename) { + // We do not yet configure filename but we will configure in another + // loop. This is because if file cannot be created, we will force ToFile + // to be false. Because configuring logger is not necessarily performance + // sensative operation, we can live with another loop; (by the way this loop + // is not very heavy either) + } else if (conf->configurationType() == ConfigurationType::Format) { + setValue(conf->level(), base::LogFormat(conf->level(), + base::type::string_t(conf->value().begin(), conf->value().end())), &m_logFormatMap); + } else if (conf->configurationType() == ConfigurationType::MillisecondsWidth) { + setValue(Level::Global, + base::MillisecondsWidth(static_cast(getULong(conf->value()))), &m_millisecondsWidthMap); + } else if (conf->configurationType() == ConfigurationType::PerformanceTracking) { + setValue(Level::Global, getBool(conf->value()), &m_performanceTrackingMap); + } else if (conf->configurationType() == ConfigurationType::MaxLogFileSize) { + setValue(conf->level(), static_cast(getULong(conf->value())), &m_maxLogFileSizeMap); +#if !defined(ELPP_NO_DEFAULT_LOG_FILE) + withFileSizeLimit.push_back(conf); +#endif // !defined(ELPP_NO_DEFAULT_LOG_FILE) + } else if (conf->configurationType() == ConfigurationType::LogFlushThreshold) { + setValue(conf->level(), static_cast(getULong(conf->value())), &m_logFlushThresholdMap); + } + } + // As mentioned early, we will now set filename configuration in separate loop to deal with non-existent files + for (Configurations::const_iterator it = configurations->begin(); it != configurations->end(); ++it) { + Configuration* conf = *it; + if (conf->configurationType() == ConfigurationType::Filename) { + insertFile(conf->level(), conf->value()); + } + } + for (std::vector::iterator conf = withFileSizeLimit.begin(); + conf != withFileSizeLimit.end(); ++conf) { + // This is not unsafe as mutex is locked in currect scope + unsafeValidateFileRolling((*conf)->level(), base::defaultPreRollOutCallback); + } + } + + unsigned long getULong(std::string confVal) { + bool valid = true; + base::utils::Str::trim(confVal); + valid = !confVal.empty() && std::find_if(confVal.begin(), confVal.end(), + [](char c) { return !base::utils::Str::isDigit(c); }) == confVal.end(); + if (!valid) { + valid = false; + ELPP_ASSERT(valid, "Configuration value not a valid integer [" << confVal << "]"); + return 0; + } + return atol(confVal.c_str()); + } + + std::string resolveFilename(const std::string& filename) { + std::string resultingFilename = filename; + std::size_t dateIndex = std::string::npos; + std::string dateTimeFormatSpecifierStr = std::string(base::consts::kDateTimeFormatSpecifierForFilename); + if ((dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str())) != std::string::npos) { + while (dateIndex > 0 && resultingFilename[dateIndex - 1] == base::consts::kFormatSpecifierChar) { + dateIndex = resultingFilename.find(dateTimeFormatSpecifierStr.c_str(), dateIndex + 1); + } + if (dateIndex != std::string::npos) { + const char* ptr = resultingFilename.c_str() + dateIndex; + // Goto end of specifier + ptr += dateTimeFormatSpecifierStr.size(); + std::string fmt; + if ((resultingFilename.size() > dateIndex) && (ptr[0] == '{')) { + // User has provided format for date/time + ++ptr; + int count = 1; // Start by 1 in order to remove starting brace + std::stringstream ss; + for (; *ptr; ++ptr, ++count) { + if (*ptr == '}') { + ++count; // In order to remove ending brace + break; + } + ss << *ptr; + } + resultingFilename.erase(dateIndex + dateTimeFormatSpecifierStr.size(), count); + fmt = ss.str(); + } else { + fmt = std::string(base::consts::kDefaultDateTimeFormatInFilename); + } + base::MillisecondsWidth msWidth(3); + std::string now = base::utils::DateTime::getDateTime(fmt.c_str(), &msWidth); + base::utils::Str::replaceAll(now, '/', '-'); // Replace path element since we are dealing with filename + base::utils::Str::replaceAll(resultingFilename, dateTimeFormatSpecifierStr, now); + } + } + return resultingFilename; + } + + void insertFile(Level level, const std::string& fullFilename) { + std::string resolvedFilename = resolveFilename(fullFilename); + if (resolvedFilename.empty()) { + std::cerr << "Could not load empty file for logging, please re-check your configurations for level [" + << LevelHelper::convertToString(level) << "]"; + } + std::string filePath = base::utils::File::extractPathFromFilename(resolvedFilename, base::consts::kFilePathSeperator); + if (filePath.size() < resolvedFilename.size()) { + base::utils::File::createPath(filePath); + } + auto create = [&](Level level) { + base::LogStreamsReferenceMap::iterator filestreamIter = m_logStreamsReference->find(resolvedFilename); + base::type::fstream_t* fs = nullptr; + if (filestreamIter == m_logStreamsReference->end()) { + // We need a completely new stream, nothing to share with + fs = base::utils::File::newFileStream(resolvedFilename); + m_filenameMap.insert(std::make_pair(level, resolvedFilename)); + m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(fs))); + m_logStreamsReference->insert(std::make_pair(resolvedFilename, base::FileStreamPtr(m_fileStreamMap.at(level)))); + } else { + // Woops! we have an existing one, share it! + m_filenameMap.insert(std::make_pair(level, filestreamIter->first)); + m_fileStreamMap.insert(std::make_pair(level, base::FileStreamPtr(filestreamIter->second))); + fs = filestreamIter->second.get(); + } + if (fs == nullptr) { + // We display bad file error from newFileStream() + ELPP_INTERNAL_ERROR("Setting [TO_FILE] of [" + << LevelHelper::convertToString(level) << "] to FALSE", false); + setValue(level, false, &m_toFileMap); + } + }; + // If we dont have file conf for any level, create it for Level::Global first + // otherwise create for specified level + create(m_filenameMap.empty() && m_fileStreamMap.empty() ? Level::Global : level); + } + + bool unsafeValidateFileRolling(Level level, const PreRollOutCallback& PreRollOutCallback) { + base::type::fstream_t* fs = unsafeGetConfigByRef(level, &m_fileStreamMap, "fileStream").get(); + if (fs == nullptr) { + return true; + } + std::size_t maxLogFileSize = unsafeGetConfigByVal(level, &m_maxLogFileSizeMap, "maxLogFileSize"); + std::size_t currFileSize = base::utils::File::getSizeOfFile(fs); + if (maxLogFileSize != 0 && currFileSize >= maxLogFileSize) { + std::string fname = unsafeGetConfigByRef(level, &m_filenameMap, "filename"); + ELPP_INTERNAL_INFO(1, "Truncating log file [" << fname << "] as a result of configurations for level [" + << LevelHelper::convertToString(level) << "]"); + fs->close(); + PreRollOutCallback(fname.c_str(), currFileSize); + fs->open(fname, std::fstream::out | std::fstream::trunc); + return true; + } + return false; + } + + bool validateFileRolling(Level level, const PreRollOutCallback& PreRollOutCallback) { + base::threading::ScopedLock scopedLock(lock()); + return unsafeValidateFileRolling(level, PreRollOutCallback); + } +}; +/// @brief Class that keeps record of current line hit for occasional logging +class HitCounter { +public: + HitCounter(void) : + m_filename(""), + m_lineNumber(0), + m_hitCounts(0) { + } + + HitCounter(const char* filename, unsigned long int lineNumber) : + m_filename(filename), + m_lineNumber(lineNumber), + m_hitCounts(0) { + } + + HitCounter(const HitCounter& hitCounter) : + m_filename(hitCounter.m_filename), + m_lineNumber(hitCounter.m_lineNumber), + m_hitCounts(hitCounter.m_hitCounts) { + } + + HitCounter& operator=(const HitCounter& hitCounter) { + m_filename = hitCounter.m_filename; + m_lineNumber = hitCounter.m_lineNumber; + m_hitCounts = hitCounter.m_hitCounts; + return *this; + } + + virtual ~HitCounter(void) { + } + + /// @brief Resets location of current hit counter + inline void resetLocation(const char* filename, unsigned long int lineNumber) { + m_filename = filename; + m_lineNumber = lineNumber; + } + + /// @brief Validates hit counts and resets it if necessary + inline void validateHitCounts(std::size_t n) { + if (m_hitCounts >= base::consts::kMaxLogPerCounter) { + m_hitCounts = (n >= 1 ? base::consts::kMaxLogPerCounter % n : 0); + } + ++m_hitCounts; + } + + inline const char* filename(void) const { + return m_filename; + } + + inline unsigned long int lineNumber(void) const { + return m_lineNumber; + } + + inline std::size_t hitCounts(void) const { + return m_hitCounts; + } + + inline void increment(void) { + ++m_hitCounts; + } + + class Predicate { + public: + Predicate(const char* filename, unsigned long int lineNumber) + : m_filename(filename), + m_lineNumber(lineNumber) { + } + inline bool operator()(const HitCounter* counter) { + return ((counter != nullptr) && + (strcmp(counter->m_filename, m_filename) == 0) && + (counter->m_lineNumber == m_lineNumber)); + } + + private: + const char* m_filename; + unsigned long int m_lineNumber; + }; + +private: + const char* m_filename; + unsigned long int m_lineNumber; + std::size_t m_hitCounts; +}; +/// @brief Repository for hit counters used across the application +class RegisteredHitCounters : public base::utils::RegistryWithPred { +public: + /// @brief Validates counter for every N, i.e, registers new if does not exist otherwise updates original one + /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned + bool validateEveryN(const char* filename, unsigned long int lineNumber, std::size_t n) { + base::threading::ScopedLock scopedLock(lock()); + base::HitCounter* counter = get(filename, lineNumber); + if (counter == nullptr) { + registerNew(counter = new base::HitCounter(filename, lineNumber)); + } + counter->validateHitCounts(n); + bool result = (n >= 1 && counter->hitCounts() != 0 && counter->hitCounts() % n == 0); + return result; + } + + /// @brief Validates counter for hits >= N, i.e, registers new if does not exist otherwise updates original one + /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned + bool validateAfterN(const char* filename, unsigned long int lineNumber, std::size_t n) { + base::threading::ScopedLock scopedLock(lock()); + base::HitCounter* counter = get(filename, lineNumber); + if (counter == nullptr) { + registerNew(counter = new base::HitCounter(filename, lineNumber)); + } + // Do not use validateHitCounts here since we do not want to reset counter here + // Note the >= instead of > because we are incrementing + // after this check + if (counter->hitCounts() >= n) + return true; + counter->increment(); + return false; + } + + /// @brief Validates counter for hits are <= n, i.e, registers new if does not exist otherwise updates original one + /// @return True if validation resulted in triggering hit. Meaning logs should be written everytime true is returned + bool validateNTimes(const char* filename, unsigned long int lineNumber, std::size_t n) { + base::threading::ScopedLock scopedLock(lock()); + base::HitCounter* counter = get(filename, lineNumber); + if (counter == nullptr) { + registerNew(counter = new base::HitCounter(filename, lineNumber)); + } + counter->increment(); + // Do not use validateHitCounts here since we do not want to reset counter here + if (counter->hitCounts() <= n) + return true; + return false; + } + + /// @brief Gets hit counter registered at specified position + inline const base::HitCounter* getCounter(const char* filename, unsigned long int lineNumber) { + base::threading::ScopedLock scopedLock(lock()); + return get(filename, lineNumber); + } +}; +/// @brief Action to be taken for dispatching +enum class DispatchAction : base::type::EnumType { + None = 1, NormalLog = 2, SysLog = 4 +}; +} // namespace base +template +class Callback : protected base::threading::ThreadSafe { +public: + Callback(void) : m_enabled(true) {} + inline bool enabled(void) const { return m_enabled; } + inline void setEnabled(bool enabled) { + base::threading::ScopedLock scopedLock(lock()); + m_enabled = enabled; + } +protected: + virtual void handle(const T* handlePtr) = 0; +private: + bool m_enabled; +}; +class LogDispatchData { +public: + LogDispatchData() : m_logMessage(nullptr), m_dispatchAction(base::DispatchAction::None) {} + inline const LogMessage* logMessage(void) const { return m_logMessage; } + inline base::DispatchAction dispatchAction(void) const { return m_dispatchAction; } +private: + LogMessage* m_logMessage; + base::DispatchAction m_dispatchAction; + friend class base::LogDispatcher; + + inline void setLogMessage(LogMessage* logMessage) { m_logMessage = logMessage; } + inline void setDispatchAction(base::DispatchAction dispatchAction) { m_dispatchAction = dispatchAction; } +}; +class LogDispatchCallback : public Callback { +private: + friend class base::LogDispatcher; +}; +class PerformanceTrackingCallback : public Callback { +private: + friend class base::PerformanceTracker; +}; +class LogBuilder : base::NoCopy { +public: + virtual ~LogBuilder(void) { ELPP_INTERNAL_INFO(3, "Destroying log builder...")} + virtual base::type::string_t build(const LogMessage* logMessage, bool appendNewLine) const = 0; + void convertToColoredOutput(base::type::string_t* logLine, Level level) { + if (!base::utils::s_termSupportsColor) return; + const base::type::char_t* resetColor = ELPP_LITERAL("\x1b[0m"); + if (level == Level::Error || level == Level::Fatal) + *logLine = ELPP_LITERAL("\x1b[31m") + *logLine + resetColor; + else if (level == Level::Warning) + *logLine = ELPP_LITERAL("\x1b[33m") + *logLine + resetColor; + } +private: + friend class el::base::DefaultLogDispatchCallback; +}; +typedef std::shared_ptr LogBuilderPtr; +/// @brief Represents a logger holding ID and configurations we need to write logs +/// +/// @detail This class does not write logs itself instead its used by writer to read configuations from. +class Logger : public base::threading::ThreadSafe, public Loggable { +public: + Logger(const std::string& id, base::LogStreamsReferenceMap* logStreamsReference) : + m_id(id), + m_typedConfigurations(nullptr), + m_parentApplicationName(std::string()), + m_isConfigured(false), + m_logStreamsReference(logStreamsReference) { + initUnflushedCount(); + } + + Logger(const std::string& id, const Configurations& configurations, base::LogStreamsReferenceMap* logStreamsReference) : + m_id(id), + m_typedConfigurations(nullptr), + m_parentApplicationName(std::string()), + m_isConfigured(false), + m_logStreamsReference(logStreamsReference) { + initUnflushedCount(); + configure(configurations); + } + + Logger(const Logger& logger) { + base::utils::safeDelete(m_typedConfigurations); + m_id = logger.m_id; + m_typedConfigurations = logger.m_typedConfigurations; + m_parentApplicationName = logger.m_parentApplicationName; + m_isConfigured = logger.m_isConfigured; + m_configurations = logger.m_configurations; + m_unflushedCount = logger.m_unflushedCount; + m_logStreamsReference = logger.m_logStreamsReference; + } + + Logger& operator=(const Logger& logger) { + base::utils::safeDelete(m_typedConfigurations); + m_id = logger.m_id; + m_typedConfigurations = logger.m_typedConfigurations; + m_parentApplicationName = logger.m_parentApplicationName; + m_isConfigured = logger.m_isConfigured; + m_configurations = logger.m_configurations; + m_unflushedCount = logger.m_unflushedCount; + m_logStreamsReference = logger.m_logStreamsReference; + return *this; + } + + virtual ~Logger(void) { + base::utils::safeDelete(m_typedConfigurations); + } + + virtual inline void log(el::base::type::ostream_t& os) const { + os << m_id.c_str(); + } + + /// @brief Configures the logger using specified configurations. + void configure(const Configurations& configurations) { + m_isConfigured = false; // we set it to false in case if we fail + initUnflushedCount(); + if (m_typedConfigurations != nullptr) { + Configurations* c = const_cast(m_typedConfigurations->configurations()); + if (c->hasConfiguration(Level::Global, ConfigurationType::Filename)) { + // This check is definitely needed for cases like ELPP_NO_DEFAULT_LOG_FILE + flush(); + } + } + base::threading::ScopedLock scopedLock(lock()); + if (m_configurations != configurations) { + m_configurations.setFromBase(const_cast(&configurations)); + } + base::utils::safeDelete(m_typedConfigurations); + m_typedConfigurations = new base::TypedConfigurations(&m_configurations, m_logStreamsReference); + resolveLoggerFormatSpec(); + m_isConfigured = true; + } + + /// @brief Reconfigures logger using existing configurations + inline void reconfigure(void) { + ELPP_INTERNAL_INFO(1, "Reconfiguring logger [" << m_id << "]"); + configure(m_configurations); + } + + inline const std::string& id(void) const { + return m_id; + } + + inline const std::string& parentApplicationName(void) const { + return m_parentApplicationName; + } + + inline void setParentApplicationName(const std::string& parentApplicationName) { + m_parentApplicationName = parentApplicationName; + } + + inline Configurations* configurations(void) { + return &m_configurations; + } + + inline base::TypedConfigurations* typedConfigurations(void) { + return m_typedConfigurations; + } + + static inline bool isValidId(const std::string& id) { + for (std::string::const_iterator it = id.begin(); it != id.end(); ++it) { + if (!base::utils::Str::contains(base::consts::kValidLoggerIdSymbols, *it)) { + return false; + } + } + return true; + } + /// @brief Flushes logger to sync all log files for all levels + inline void flush(void) { + ELPP_INTERNAL_INFO(3, "Flushing logger [" << m_id << "] all levels"); + base::threading::ScopedLock scopedLock(lock()); + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + flush(LevelHelper::castFromInt(lIndex), nullptr); + return false; + }); + } + + inline void flush(Level level, base::type::fstream_t* fs) { + if (fs == nullptr && m_typedConfigurations->toFile(level)) { + fs = m_typedConfigurations->fileStream(level); + } + if (fs != nullptr) { + fs->flush(); + m_unflushedCount.find(level)->second = 0; + } + } + + inline bool isFlushNeeded(Level level) { + return ++m_unflushedCount.find(level)->second >= m_typedConfigurations->logFlushThreshold(level); + } + + inline LogBuilder* logBuilder(void) const { + return m_logBuilder.get(); + } + + inline void setLogBuilder(const LogBuilderPtr& logBuilder) { + m_logBuilder = logBuilder; + } + + inline bool enabled(Level level) const { + return m_typedConfigurations->enabled(level); + } + +#if ELPP_VARIADIC_TEMPLATES_SUPPORTED +# define LOGGER_LEVEL_WRITERS_SIGNATURES(FUNCTION_NAME)\ + template \ + inline void FUNCTION_NAME(const char*, const T&, const Args&...);\ + template \ + inline void FUNCTION_NAME(const T&); + + template + inline void verbose(int, const char*, const T&, const Args&...); + + template + inline void verbose(int, const T&); + + LOGGER_LEVEL_WRITERS_SIGNATURES(info) + LOGGER_LEVEL_WRITERS_SIGNATURES(debug) + LOGGER_LEVEL_WRITERS_SIGNATURES(warn) + LOGGER_LEVEL_WRITERS_SIGNATURES(error) + LOGGER_LEVEL_WRITERS_SIGNATURES(fatal) + LOGGER_LEVEL_WRITERS_SIGNATURES(trace) +# undef LOGGER_LEVEL_WRITERS_SIGNATURES +#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED +private: + std::string m_id; + base::TypedConfigurations* m_typedConfigurations; + base::type::stringstream_t m_stream; + std::string m_parentApplicationName; + bool m_isConfigured; + Configurations m_configurations; + std::map m_unflushedCount; + base::LogStreamsReferenceMap* m_logStreamsReference; + LogBuilderPtr m_logBuilder; + + friend class el::LogMessage; + friend class el::Loggers; + friend class el::Helpers; + friend class el::base::RegisteredLoggers; + friend class el::base::DefaultLogDispatchCallback; + friend class el::base::MessageBuilder; + friend class el::base::Writer; + friend class el::base::PErrorWriter; + friend class el::base::Storage; + friend class el::base::PerformanceTracker; + friend class el::base::LogDispatcher; + + Logger(void); + +#if ELPP_VARIADIC_TEMPLATES_SUPPORTED + template + void log_(Level, int, const char*, const T&, const Args&...); + + template + inline void log_(Level, int, const T&); + + template + void log(Level, const char*, const T&, const Args&...); + + template + inline void log(Level, const T&); +#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED + + void initUnflushedCount(void) { + m_unflushedCount.clear(); + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + m_unflushedCount.insert(std::make_pair(LevelHelper::castFromInt(lIndex), 0)); + return false; + }); + } + + inline base::type::stringstream_t& stream(void) { + return m_stream; + } + + void resolveLoggerFormatSpec(void) const { + base::type::EnumType lIndex = LevelHelper::kMinValid; + LevelHelper::forEachLevel(&lIndex, [&](void) -> bool { + base::LogFormat* logFormat = + const_cast(&m_typedConfigurations->logFormat(LevelHelper::castFromInt(lIndex))); + base::utils::Str::replaceFirstWithEscape(logFormat->m_format, base::consts::kLoggerIdFormatSpecifier, m_id); + return false; + }); + } +}; +namespace base { +/// @brief Loggers repository +class RegisteredLoggers : public base::utils::Registry { +public: + explicit RegisteredLoggers(const LogBuilderPtr& defaultLogBuilder) : + m_defaultLogBuilder(defaultLogBuilder) { + m_defaultConfigurations.setToDefault(); + } + + virtual ~RegisteredLoggers(void) { + flushAll(); + } + + inline void setDefaultConfigurations(const Configurations& configurations) { + base::threading::ScopedLock scopedLock(lock()); + m_defaultConfigurations.setFromBase(const_cast(&configurations)); + } + + inline Configurations* defaultConfigurations(void) { + return &m_defaultConfigurations; + } + + Logger* get(const std::string& id, bool forceCreation = true) { + base::threading::ScopedLock scopedLock(lock()); + Logger* logger_ = base::utils::Registry::get(id); + if (logger_ == nullptr && forceCreation) { + bool validId = Logger::isValidId(id); + if (!validId) { + ELPP_ASSERT(validId, "Invalid logger ID [" << id << "]. Not registering this logger."); + return nullptr; + } + logger_ = new Logger(id, m_defaultConfigurations, &m_logStreamsReference); + logger_->m_logBuilder = m_defaultLogBuilder; + registerNew(id, logger_); + } + return logger_; + } + + bool remove(const std::string& id) { + if (id == "default") { + return false; + } + Logger* logger = base::utils::Registry::get(id); + if (logger != nullptr) { + unregister(logger); + } + return true; + } + + inline bool has(const std::string& id) { + return get(id, false) != nullptr; + } + + inline void unregister(Logger*& logger) { + base::threading::ScopedLock scopedLock(lock()); + base::utils::Registry::unregister(logger->id()); + } + + inline base::LogStreamsReferenceMap* logStreamsReference(void) { + return &m_logStreamsReference; + } + + inline void flushAll(void) { + ELPP_INTERNAL_INFO(1, "Flushing all log files"); + base::threading::ScopedLock scopedLock(lock()); + for (base::LogStreamsReferenceMap::iterator it = m_logStreamsReference.begin(); + it != m_logStreamsReference.end(); ++it) { + if (it->second.get() == nullptr) continue; + it->second->flush(); + } + } + +private: + LogBuilderPtr m_defaultLogBuilder; + Configurations m_defaultConfigurations; + base::LogStreamsReferenceMap m_logStreamsReference; + friend class el::base::Storage; +}; +/// @brief Represents registries for verbose logging +class VRegistry : base::NoCopy, public base::threading::ThreadSafe { +public: + explicit VRegistry(base::type::VerboseLevel level, base::type::EnumType* pFlags) : m_level(level), m_pFlags(pFlags) { + } + + /// @brief Sets verbose level. Accepted range is 0-9 + inline void setLevel(base::type::VerboseLevel level) { + base::threading::ScopedLock scopedLock(lock()); + if (level < 0) + m_level = 0; + else if (level > 9) + m_level = base::consts::kMaxVerboseLevel; + else + m_level = level; + } + + inline base::type::VerboseLevel level(void) const { + return m_level; + } + + inline void clearModules(void) { + base::threading::ScopedLock scopedLock(lock()); + m_modules.clear(); + } + + void setModules(const char* modules) { + base::threading::ScopedLock scopedLock(lock()); + auto addSuffix = [](std::stringstream& ss, const char* sfx, const char* prev) { + if (prev != nullptr && base::utils::Str::endsWith(ss.str(), std::string(prev))) { + std::string chr(ss.str().substr(0, ss.str().size() - strlen(prev))); + ss.str(std::string("")); + ss << chr; + } + if (base::utils::Str::endsWith(ss.str(), std::string(sfx))) { + std::string chr(ss.str().substr(0, ss.str().size() - strlen(sfx))); + ss.str(std::string("")); + ss << chr; + } + ss << sfx; + }; + auto insert = [&](std::stringstream& ss, base::type::VerboseLevel level) { + if (!base::utils::hasFlag(LoggingFlag::DisableVModulesExtensions, *m_pFlags)) { + addSuffix(ss, ".h", nullptr); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".c", ".h"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".cpp", ".c"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".cc", ".cpp"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".cxx", ".cc"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".-inl.h", ".cxx"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".hxx", ".-inl.h"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".hpp", ".hxx"); + m_modules.insert(std::make_pair(ss.str(), level)); + addSuffix(ss, ".hh", ".hpp"); + } + m_modules.insert(std::make_pair(ss.str(), level)); + }; + bool isMod = true; + bool isLevel = false; + std::stringstream ss; + int level = -1; + for (; *modules; ++modules) { + switch (*modules) { + case '=': + isLevel = true; + isMod = false; + break; + case ',': + isLevel = false; + isMod = true; + if (!ss.str().empty() && level != -1) { + insert(ss, level); + ss.str(std::string("")); + level = -1; + } + break; + default: + if (isMod) { + ss << *modules; + } else if (isLevel) { + if (isdigit(*modules)) { + level = static_cast(*modules) - 48; + } + } + break; + } + } + if (!ss.str().empty() && level != -1) { + insert(ss, level); + } + } + + bool allowed(base::type::VerboseLevel vlevel, const char* file) { + base::threading::ScopedLock scopedLock(lock()); + if (m_modules.empty() || file == nullptr) { + return vlevel <= m_level; + } else { + std::map::iterator it = m_modules.begin(); + for (; it != m_modules.end(); ++it) { + if (base::utils::Str::wildCardMatch(file, it->first.c_str())) { + return vlevel <= it->second; + } + } + if (base::utils::hasFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified, *m_pFlags)) { + return true; + } + return false; + } + } + + inline const std::map& modules(void) const { + return m_modules; + } + + void setFromArgs(const base::utils::CommandLineArgs* commandLineArgs) { + if (commandLineArgs->hasParam("-v") || commandLineArgs->hasParam("--verbose") || + commandLineArgs->hasParam("-V") || commandLineArgs->hasParam("--VERBOSE")) { + setLevel(base::consts::kMaxVerboseLevel); + } else if (commandLineArgs->hasParamWithValue("--v")) { + setLevel(atoi(commandLineArgs->getParamValue("--v"))); + } else if (commandLineArgs->hasParamWithValue("--V")) { + setLevel(atoi(commandLineArgs->getParamValue("--V"))); + } else if ((commandLineArgs->hasParamWithValue("-vmodule")) && vModulesEnabled()) { + setModules(commandLineArgs->getParamValue("-vmodule")); + } else if (commandLineArgs->hasParamWithValue("-VMODULE") && vModulesEnabled()) { + setModules(commandLineArgs->getParamValue("-VMODULE")); + } + } + + /// @brief Whether or not vModules enabled + inline bool vModulesEnabled(void) { + return !base::utils::hasFlag(LoggingFlag::DisableVModules, *m_pFlags); + } + +private: + base::type::VerboseLevel m_level; + base::type::EnumType* m_pFlags; + std::map m_modules; +}; +} // namespace base +class LogMessage { +public: + LogMessage(Level level, const std::string& file, unsigned long int line, const std::string& func, + base::type::VerboseLevel verboseLevel, Logger* logger) : + m_level(level), m_file(file), m_line(line), m_func(func), + m_verboseLevel(verboseLevel), m_logger(logger), m_message(std::move(logger->stream().str())) { + } + inline Level level(void) const { return m_level; } + inline const std::string& file(void) const { return m_file; } + inline unsigned long int line(void) const { return m_line; } // NOLINT + inline const std::string& func(void) const { return m_func; } + inline base::type::VerboseLevel verboseLevel(void) const { return m_verboseLevel; } + inline Logger* logger(void) const { return m_logger; } + inline const base::type::string_t& message(void) const { return m_message; } +private: + Level m_level; + std::string m_file; + unsigned long int m_line; + std::string m_func; + base::type::VerboseLevel m_verboseLevel; + Logger* m_logger; + base::type::string_t m_message; +}; +namespace base { +#if ELPP_ASYNC_LOGGING +class AsyncLogItem { +public: + explicit AsyncLogItem(const LogMessage& logMessage, const LogDispatchData& data, const base::type::string_t& logLine) + : m_logMessage(logMessage), m_dispatchData(data), m_logLine(logLine) {} + virtual ~AsyncLogItem() {} + inline LogMessage* logMessage(void) { return &m_logMessage; } + inline LogDispatchData* data(void) { return &m_dispatchData; } + inline base::type::string_t logLine(void) { return m_logLine; } +private: + LogMessage m_logMessage; + LogDispatchData m_dispatchData; + base::type::string_t m_logLine; +}; +class AsyncLogQueue : public base::threading::ThreadSafe { +public: + virtual ~AsyncLogQueue() { + ELPP_INTERNAL_INFO(6, "~AsyncLogQueue"); + } + + inline AsyncLogItem next(void) { + base::threading::ScopedLock scopedLock(lock()); + AsyncLogItem result = m_queue.front(); + m_queue.pop(); + return result; + } + + inline void push(const AsyncLogItem& item) { + base::threading::ScopedLock scopedLock(lock()); + m_queue.push(item); + } + inline void pop(void) { + base::threading::ScopedLock scopedLock(lock()); + m_queue.pop(); + } + inline AsyncLogItem front(void) { + base::threading::ScopedLock scopedLock(lock()); + return m_queue.front(); + } + inline bool empty(void) { + base::threading::ScopedLock scopedLock(lock()); + return m_queue.empty(); + } +private: + std::queue m_queue; +}; +class IWorker { +public: + virtual ~IWorker() {} + virtual void start() = 0; +}; +#endif // ELPP_ASYNC_LOGGING +/// @brief Easylogging++ management storage +class Storage : base::NoCopy, public base::threading::ThreadSafe { +public: +#if ELPP_ASYNC_LOGGING + Storage(const LogBuilderPtr& defaultLogBuilder, base::IWorker* asyncDispatchWorker) : +#else + explicit Storage(const LogBuilderPtr& defaultLogBuilder) : +#endif // ELPP_ASYNC_LOGGING + m_registeredHitCounters(new base::RegisteredHitCounters()), + m_registeredLoggers(new base::RegisteredLoggers(defaultLogBuilder)), + m_flags(0x0), + m_vRegistry(new base::VRegistry(0, &m_flags)), +#if ELPP_ASYNC_LOGGING + m_asyncLogQueue(new base::AsyncLogQueue()), + m_asyncDispatchWorker(asyncDispatchWorker), +#endif // ELPP_ASYNC_LOGGING + m_preRollOutCallback(base::defaultPreRollOutCallback) { + // Register default logger + m_registeredLoggers->get(std::string(base::consts::kDefaultLoggerId)); + // Register performance logger and reconfigure format + Logger* performanceLogger = m_registeredLoggers->get(std::string(base::consts::kPerformanceLoggerId)); + performanceLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%datetime %level %msg")); + performanceLogger->reconfigure(); +#if defined(ELPP_SYSLOG) + // Register syslog logger and reconfigure format + Logger* sysLogLogger = m_registeredLoggers->get(std::string(base::consts::kSysLogLoggerId)); + sysLogLogger->configurations()->setGlobally(ConfigurationType::Format, std::string("%level: %msg")); + sysLogLogger->reconfigure(); +#else + ELPP_UNUSED(base::consts::kSysLogLoggerId); +#endif // defined(ELPP_SYSLOG) + addFlag(LoggingFlag::AllowVerboseIfModuleNotSpecified); +#if ELPP_ASYNC_LOGGING + installLogDispatchCallback(std::string("AsyncLogDispatchCallback")); +#else + installLogDispatchCallback(std::string("DefaultLogDispatchCallback")); +#endif // ELPP_ASYNC_LOGGING + installPerformanceTrackingCallback(std::string("DefaultPerformanceTrackingCallback")); + ELPP_INTERNAL_INFO(1, "Easylogging++ has been initialized"); +#if ELPP_ASYNC_LOGGING + m_asyncDispatchWorker->start(); +#endif // ELPP_ASYNC_LOGGING + } + + virtual ~Storage(void) { + ELPP_INTERNAL_INFO(4, "Destroying storage"); +#if ELPP_ASYNC_LOGGING + ELPP_INTERNAL_INFO(5, "Replacing log dispatch callback to synchronous"); + uninstallLogDispatchCallback(std::string("AsyncLogDispatchCallback")); + installLogDispatchCallback(std::string("DefaultLogDispatchCallback")); + ELPP_INTERNAL_INFO(5, "Destroying asyncDispatchWorker"); + base::utils::safeDelete(m_asyncDispatchWorker); + ELPP_INTERNAL_INFO(5, "Destroying asyncLogQueue"); + base::utils::safeDelete(m_asyncLogQueue); +#endif // ELPP_ASYNC_LOGGING + ELPP_INTERNAL_INFO(5, "Destroying registeredHitCounters"); + base::utils::safeDelete(m_registeredHitCounters); + ELPP_INTERNAL_INFO(5, "Destroying registeredLoggers"); + base::utils::safeDelete(m_registeredLoggers); + ELPP_INTERNAL_INFO(5, "Destroying vRegistry"); + base::utils::safeDelete(m_vRegistry); + } + + inline bool validateEveryNCounter(const char* filename, unsigned long int lineNumber, std::size_t occasion) { + return hitCounters()->validateEveryN(filename, lineNumber, occasion); + } + + inline bool validateAfterNCounter(const char* filename, unsigned long int lineNumber, std::size_t n) { // NOLINT + return hitCounters()->validateAfterN(filename, lineNumber, n); + } + + inline bool validateNTimesCounter(const char* filename, unsigned long int lineNumber, std::size_t n) { // NOLINT + return hitCounters()->validateNTimes(filename, lineNumber, n); + } + + inline base::RegisteredHitCounters* hitCounters(void) const { + return m_registeredHitCounters; + } + + inline base::RegisteredLoggers* registeredLoggers(void) const { + return m_registeredLoggers; + } + + inline base::VRegistry* vRegistry(void) const { + return m_vRegistry; + } + +#if ELPP_ASYNC_LOGGING + inline base::AsyncLogQueue* asyncLogQueue(void) const { + return m_asyncLogQueue; + } +#endif // ELPP_ASYNC_LOGGING + + inline const base::utils::CommandLineArgs* commandLineArgs(void) const { + return &m_commandLineArgs; + } + + inline void addFlag(LoggingFlag flag) { + base::utils::addFlag(flag, &m_flags); + } + + inline void removeFlag(LoggingFlag flag) { + base::utils::removeFlag(flag, &m_flags); + } + + inline bool hasFlag(LoggingFlag flag) const { + return base::utils::hasFlag(flag, m_flags); + } + + inline base::type::EnumType flags(void) const { + return m_flags; + } + + inline void setFlags(base::type::EnumType flags) { + m_flags = flags; + } + + inline void setPreRollOutCallback(const PreRollOutCallback& callback) { + m_preRollOutCallback = callback; + } + + inline void unsetPreRollOutCallback(void) { + m_preRollOutCallback = base::defaultPreRollOutCallback; + } + + inline PreRollOutCallback& preRollOutCallback(void) { + return m_preRollOutCallback; + } + + inline bool hasCustomFormatSpecifier(const char* formatSpecifier) { + base::threading::ScopedLock scopedLock(lock()); + return std::find(m_customFormatSpecifiers.begin(), m_customFormatSpecifiers.end(), + formatSpecifier) != m_customFormatSpecifiers.end(); + } + + inline void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) { + if (hasCustomFormatSpecifier(customFormatSpecifier.formatSpecifier())) { + return; + } + base::threading::ScopedLock scopedLock(lock()); + m_customFormatSpecifiers.push_back(customFormatSpecifier); + } + + inline bool uninstallCustomFormatSpecifier(const char* formatSpecifier) { + base::threading::ScopedLock scopedLock(lock()); + std::vector::iterator it = std::find(m_customFormatSpecifiers.begin(), + m_customFormatSpecifiers.end(), formatSpecifier); + if (it != m_customFormatSpecifiers.end() && strcmp(formatSpecifier, it->formatSpecifier()) == 0) { + m_customFormatSpecifiers.erase(it); + return true; + } + return false; + } + + const std::vector* customFormatSpecifiers(void) const { + return &m_customFormatSpecifiers; + } + + inline void setLoggingLevel(Level level) { + m_loggingLevel = level; + } + + template + inline bool installLogDispatchCallback(const std::string& id) { + return installCallback(id, &m_logDispatchCallbacks); + } + + template + inline void uninstallLogDispatchCallback(const std::string& id) { + uninstallCallback(id, &m_logDispatchCallbacks); + } + template + inline T* logDispatchCallback(const std::string& id) { + return callback(id, &m_logDispatchCallbacks); + } + + template + inline bool installPerformanceTrackingCallback(const std::string& id) { + return installCallback(id, &m_performanceTrackingCallbacks); + } + + template + inline void uninstallPerformanceTrackingCallback(const std::string& id) { + uninstallCallback(id, &m_performanceTrackingCallbacks); + } + + template + inline T* performanceTrackingCallback(const std::string& id) { + return callback(id, &m_performanceTrackingCallbacks); + } +private: + base::RegisteredHitCounters* m_registeredHitCounters; + base::RegisteredLoggers* m_registeredLoggers; + base::type::EnumType m_flags; + base::VRegistry* m_vRegistry; +#if ELPP_ASYNC_LOGGING + base::AsyncLogQueue* m_asyncLogQueue; + base::IWorker* m_asyncDispatchWorker; +#endif // ELPP_ASYNC_LOGGING + base::utils::CommandLineArgs m_commandLineArgs; + PreRollOutCallback m_preRollOutCallback; + std::map m_logDispatchCallbacks; + std::map m_performanceTrackingCallbacks; + std::vector m_customFormatSpecifiers; + Level m_loggingLevel; + + friend class el::Helpers; + friend class el::base::DefaultLogDispatchCallback; + friend class el::LogBuilder; + friend class el::base::MessageBuilder; + friend class el::base::Writer; + friend class el::base::PerformanceTracker; + friend class el::base::LogDispatcher; + + void setApplicationArguments(int argc, char** argv) { + m_commandLineArgs.setArgs(argc, argv); + m_vRegistry->setFromArgs(commandLineArgs()); + // default log file +#if !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) + if (m_commandLineArgs.hasParamWithValue(base::consts::kDefaultLogFileParam)) { + Configurations c; + c.setGlobally(ConfigurationType::Filename, std::string(m_commandLineArgs.getParamValue(base::consts::kDefaultLogFileParam))); + registeredLoggers()->setDefaultConfigurations(c); + for (base::RegisteredLoggers::iterator it = registeredLoggers()->begin(); + it != registeredLoggers()->end(); ++it) { + it->second->configure(c); + } + } +#endif // !defined(ELPP_DISABLE_LOG_FILE_FROM_ARG) +#if defined(ELPP_LOGGING_FLAGS_FROM_ARG) + if (m_commandLineArgs.hasParamWithValue(base::consts::kLoggingFlagsParam)) { + m_flags = atoi(m_commandLineArgs.getParamValue(base::consts::kLoggingFlagsParam)); + } +#endif // defined(ELPP_LOGGING_FLAGS_FROM_ARG) + } + + inline void setApplicationArguments(int argc, const char** argv) { + setApplicationArguments(argc, const_cast(argv)); + } + + template + inline bool installCallback(const std::string& id, std::map* mapT) { + if (mapT->find(id) == mapT->end()) { + mapT->insert(std::make_pair(id, TPtr(new T()))); + return true; + } + return false; + } + + template + inline void uninstallCallback(const std::string& id, std::map* mapT) { + if (mapT->find(id) != mapT->end()) { + mapT->erase(id); + } + } + + template + inline T* callback(const std::string& id, std::map* mapT) { + typename std::map::iterator iter = mapT->find(id); + if (iter != mapT->end()) { + return static_cast(iter->second.get()); + } + return nullptr; + } +}; +extern ELPP_EXPORT base::type::StoragePointer elStorage; +#define ELPP el::base::elStorage +class DefaultLogDispatchCallback : public LogDispatchCallback { +protected: + void handle(const LogDispatchData* data) { + m_data = data; + dispatch(std::move(m_data->logMessage()->logger()->logBuilder()->build(m_data->logMessage(), + m_data->dispatchAction() == base::DispatchAction::NormalLog))); + } +private: + const LogDispatchData* m_data; + void dispatch(base::type::string_t&& logLine) { + if (m_data->dispatchAction() == base::DispatchAction::NormalLog) { + if (m_data->logMessage()->logger()->m_typedConfigurations->toFile(m_data->logMessage()->level())) { + base::type::fstream_t* fs = m_data->logMessage()->logger()->m_typedConfigurations->fileStream(m_data->logMessage()->level()); + if (fs != nullptr) { + fs->write(logLine.c_str(), logLine.size()); + if (fs->fail()) { + ELPP_INTERNAL_ERROR("Unable to write log to file [" + << m_data->logMessage()->logger()->m_typedConfigurations->filename(m_data->logMessage()->level()) << "].\n" + << "Few possible reasons (could be something else):\n" << " * Permission denied\n" + << " * Disk full\n" << " * Disk is not writable", true); + } else { + if (ELPP->hasFlag(LoggingFlag::ImmediateFlush) || (m_data->logMessage()->logger()->isFlushNeeded(m_data->logMessage()->level()))) { + m_data->logMessage()->logger()->flush(m_data->logMessage()->level(), fs); + } + } + } else { + ELPP_INTERNAL_ERROR("Log file for [" << LevelHelper::convertToString(m_data->logMessage()->level()) << "] " + << "has not been configured but [TO_FILE] is configured to TRUE. [Logger ID: " + << m_data->logMessage()->logger()->id() << "]", false); + } + } + if (m_data->logMessage()->logger()->m_typedConfigurations->toStandardOutput(m_data->logMessage()->level())) { + if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput)) + m_data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, m_data->logMessage()->level()); + ELPP_COUT << ELPP_COUT_LINE(logLine); + } + } +#if defined(ELPP_SYSLOG) + else if (m_data->dispatchAction() == base::DispatchAction::SysLog) { + // Determine syslog priority + int sysLogPriority = 0; + if (m_data->logMessage()->level() == Level::Fatal) + sysLogPriority = LOG_EMERG; + else if (m_data->logMessage()->level() == Level::Error) + sysLogPriority = LOG_ERR; + else if (m_data->logMessage()->level() == Level::Warning) + sysLogPriority = LOG_WARNING; + else if (m_data->logMessage()->level() == Level::Info) + sysLogPriority = LOG_INFO; + else if (m_data->logMessage()->level() == Level::Debug) + sysLogPriority = LOG_DEBUG; + else + sysLogPriority = LOG_NOTICE; +# if defined(ELPP_UNICODE) + char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str()); + syslog(sysLogPriority, "%s", line); + free(line); +# else + syslog(sysLogPriority, "%s", logLine.c_str()); +# endif + } +#endif // defined(ELPP_SYSLOG) + } +}; +#if ELPP_ASYNC_LOGGING +class AsyncLogDispatchCallback : public LogDispatchCallback { +protected: + void handle(const LogDispatchData* data) { + base::type::string_t logLine = data->logMessage()->logger()->logBuilder()->build(data->logMessage(), data->dispatchAction() == base::DispatchAction::NormalLog); + if (data->dispatchAction() == base::DispatchAction::NormalLog && data->logMessage()->logger()->typedConfigurations()->toStandardOutput(data->logMessage()->level())) { + if (ELPP->hasFlag(LoggingFlag::ColoredTerminalOutput)) + data->logMessage()->logger()->logBuilder()->convertToColoredOutput(&logLine, data->logMessage()->level()); + ELPP_COUT << ELPP_COUT_LINE(logLine); + } + // Save resources and only queue if we want to write to file otherwise just ignore handler + if (data->logMessage()->logger()->typedConfigurations()->toFile(data->logMessage()->level())) { + ELPP->asyncLogQueue()->push(AsyncLogItem(*(data->logMessage()), *data, logLine)); + } + } +}; +class AsyncDispatchWorker : public base::IWorker, public base::threading::ThreadSafe { +public: + AsyncDispatchWorker() { + setContinueRunning(false); + } + + virtual ~AsyncDispatchWorker() { + setContinueRunning(false); + ELPP_INTERNAL_INFO(6, "Stopping dispatch worker - Cleaning log queue"); + clean(); + ELPP_INTERNAL_INFO(6, "Log queue cleaned"); + } + + inline bool clean() { + std::mutex m; + std::unique_lock lk(m); + cv.wait(lk, []{ return !ELPP->asyncLogQueue()->empty(); }); + emptyQueue(); + lk.unlock(); + cv.notify_one(); + return ELPP->asyncLogQueue()->empty(); + } + + inline void emptyQueue() { + while (!ELPP->asyncLogQueue()->empty()) { + AsyncLogItem data = ELPP->asyncLogQueue()->next(); + handle(&data); + base::threading::msleep(100); + } + } + + virtual inline void start() { + base::threading::msleep(5000); // Wait extra few seconds + setContinueRunning(true); + std::thread t1(&AsyncDispatchWorker::runner, this); + t1.join(); + } + + void handle(AsyncLogItem* logItem) { + LogDispatchData* data = logItem->data(); + LogMessage* logMessage = logItem->logMessage(); + Logger* logger = logMessage->logger(); + base::TypedConfigurations* conf = logger->typedConfigurations(); + base::type::string_t logLine = logItem->logLine(); + if (data->dispatchAction() == base::DispatchAction::NormalLog) { + if (conf->toFile(logMessage->level())) { + base::type::fstream_t* fs = conf->fileStream(logMessage->level()); + if (fs != nullptr) { + fs->write(logLine.c_str(), logLine.size()); + if (fs->fail()) { + ELPP_INTERNAL_ERROR("Unable to write log to file [" + << conf->filename(logMessage->level()) << "].\n" + << "Few possible reasons (could be something else):\n" << " * Permission denied\n" + << " * Disk full\n" << " * Disk is not writable", true); + } else { + if (ELPP->hasFlag(LoggingFlag::ImmediateFlush) || (logger->isFlushNeeded(logMessage->level()))) { + logger->flush(logMessage->level(), fs); + } + } + } else { + ELPP_INTERNAL_ERROR("Log file for [" << LevelHelper::convertToString(logMessage->level()) << "] " + << "has not been configured but [TO_FILE] is configured to TRUE. [Logger ID: " << logger->id() << "]", false); + } + } + } +# if defined(ELPP_SYSLOG) + else if (data->dispatchAction() == base::DispatchAction::SysLog) { + // Determine syslog priority + int sysLogPriority = 0; + if (logMessage->level() == Level::Fatal) + sysLogPriority = LOG_EMERG; + else if (logMessage->level() == Level::Error) + sysLogPriority = LOG_ERR; + else if (logMessage->level() == Level::Warning) + sysLogPriority = LOG_WARNING; + else if (logMessage->level() == Level::Info) + sysLogPriority = LOG_INFO; + else if (logMessage->level() == Level::Debug) + sysLogPriority = LOG_DEBUG; + else + sysLogPriority = LOG_NOTICE; +# if defined(ELPP_UNICODE) + char* line = base::utils::Str::wcharPtrToCharPtr(logLine.c_str()); + syslog(sysLogPriority, "%s", line); + free(line); +# else + syslog(sysLogPriority, "%s", logLine.c_str()); +# endif + } +# endif // defined(ELPP_SYSLOG) + } + + void run() { + while (continueRunning()) { + emptyQueue(); + base::threading::msleep(10); // 10ms + } + } + + static void* runner(void *context) { + static_cast(context)->run(); + return NULL; + } + + void setContinueRunning(bool value) { + base::threading::ScopedLock scopedLock(m_continueRunningMutex); + m_continueRunning = value; + } + bool continueRunning(void) { + return m_continueRunning; + } +private: + std::condition_variable cv; + bool m_continueRunning; + base::threading::Mutex m_continueRunningMutex; +}; +#endif // ELPP_ASYNC_LOGGING +} // namespace base +namespace base { +class DefaultLogBuilder : public LogBuilder { +public: + base::type::string_t build(const LogMessage* logMessage, bool appendNewLine) const { + base::TypedConfigurations* tc = logMessage->logger()->typedConfigurations(); + const base::LogFormat* logFormat = &tc->logFormat(logMessage->level()); + base::type::string_t logLine = logFormat->format(); + char buff[base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength] = ""; + const char* bufLim = buff + sizeof(buff); + if (logFormat->hasFlag(base::FormatFlags::AppName)) { + // App name + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kAppNameFormatSpecifier, + logMessage->logger()->parentApplicationName()); + } + if (logFormat->hasFlag(base::FormatFlags::ThreadId)) { + // Thread ID + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kThreadIdFormatSpecifier, + base::threading::getCurrentThreadId()); + } + if (logFormat->hasFlag(base::FormatFlags::DateTime)) { + // DateTime + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kDateTimeFormatSpecifier, + base::utils::DateTime::getDateTime(logFormat->dateTimeFormat().c_str(), + &tc->millisecondsWidth(logMessage->level()))); + } + if (logFormat->hasFlag(base::FormatFlags::Function)) { + // Function + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFunctionFormatSpecifier, logMessage->func()); + } + if (logFormat->hasFlag(base::FormatFlags::File)) { + // File + char* buf = base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength); + base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff); + buf = base::utils::Str::addToBuff(buff, buf, bufLim); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::FileBase)) { + // FileBase + char* buf = base::utils::Str::clearBuff(buff, base::consts::kSourceFilenameMaxLength); + base::utils::File::buildBaseFilename(logMessage->file(), buff); + buf = base::utils::Str::addToBuff(buff, buf, bufLim); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogFileBaseFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::Line)) { + // Line + char* buf = base::utils::Str::clearBuff(buff, base::consts::kSourceLineMaxLength); + buf = base::utils::Str::convertAndAddToBuff(logMessage->line(), + base::consts::kSourceLineMaxLength, buf, bufLim, false); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLineFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::Location)) { + // Location + char* buf = base::utils::Str::clearBuff(buff, + base::consts::kSourceFilenameMaxLength + base::consts::kSourceLineMaxLength); + base::utils::File::buildStrippedFilename(logMessage->file().c_str(), buff); + buf = base::utils::Str::addToBuff(buff, buf, bufLim); + buf = base::utils::Str::addToBuff(":", buf, bufLim); + buf = base::utils::Str::convertAndAddToBuff(logMessage->line(), + base::consts::kSourceLineMaxLength, buf, bufLim, false); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kLogLocationFormatSpecifier, std::string(buff)); + } + if (logMessage->level() == Level::Verbose && logFormat->hasFlag(base::FormatFlags::VerboseLevel)) { + // Verbose level + char* buf = base::utils::Str::clearBuff(buff, 1); + buf = base::utils::Str::convertAndAddToBuff(logMessage->verboseLevel(), 1, buf, bufLim, false); + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kVerboseLevelFormatSpecifier, std::string(buff)); + } + if (logFormat->hasFlag(base::FormatFlags::LogMessage)) { + // Log message + base::utils::Str::replaceFirstWithEscape(logLine, base::consts::kMessageFormatSpecifier, logMessage->message()); + } +#if !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS) + for (std::vector::const_iterator it = ELPP->customFormatSpecifiers()->begin(); + it != ELPP->customFormatSpecifiers()->end(); ++it) { + std::string fs(it->formatSpecifier()); + base::type::string_t wcsFormatSpecifier(fs.begin(), fs.end()); + base::utils::Str::replaceFirstWithEscape(logLine, wcsFormatSpecifier, std::string(it->resolver()())); + } +#endif // !defined(ELPP_DISABLE_CUSTOM_FORMAT_SPECIFIERS) + if (appendNewLine) logLine += ELPP_LITERAL("\n"); + return logLine; + } +}; +/// @brief Dispatches log messages +class LogDispatcher : base::NoCopy { +public: + LogDispatcher(bool proceed, LogMessage&& logMessage, base::DispatchAction dispatchAction) : + m_proceed(proceed), + m_logMessage(std::move(logMessage)), + m_dispatchAction(std::move(dispatchAction)) { + } + + void dispatch(void) { + if (m_proceed && m_dispatchAction == base::DispatchAction::None) { + m_proceed = false; + } + if (!m_proceed) { + return; + } + // We minimize the time of ELPP's lock - this lock is released after log is written + base::threading::ScopedLock scopedLock(ELPP->lock()); + base::TypedConfigurations* tc = m_logMessage.logger()->m_typedConfigurations; + if (ELPP->hasFlag(LoggingFlag::StrictLogFileSizeCheck)) { + tc->validateFileRolling(m_logMessage.level(), ELPP->preRollOutCallback()); + } + LogDispatchCallback* callback = nullptr; + LogDispatchData data; + for (const std::pair& h + : ELPP->m_logDispatchCallbacks) { + callback = h.second.get(); + if (callback != nullptr && callback->enabled()) { + data.setLogMessage(&m_logMessage); + data.setDispatchAction(m_dispatchAction); + callback->acquireLock(); + callback->handle(&data); + callback->releaseLock(); + } + } + } + +private: + bool m_proceed; + LogMessage m_logMessage; + base::DispatchAction m_dispatchAction; +}; +#if defined(ELPP_STL_LOGGING) +/// @brief Workarounds to write some STL logs +/// +/// @detail There is workaround needed to loop through some stl containers. In order to do that, we need iterable containers +/// of same type and provide iterator interface and pass it on to writeIterator(). +/// Remember, this is passed by value in constructor so that we dont change original containers. +/// This operation is as expensive as Big-O(std::min(class_.size(), base::consts::kMaxLogPerContainer)) +namespace workarounds { +/// @brief Abstract IterableContainer template that provides interface for iterable classes of type T +template +class IterableContainer { +public: + typedef typename Container::iterator iterator; + typedef typename Container::const_iterator const_iterator; + IterableContainer(void) {} + virtual ~IterableContainer(void) {} + iterator begin(void) { return getContainer().begin(); } + iterator end(void) { return getContainer().end(); } +private: + virtual Container& getContainer(void) = 0; +}; +/// @brief Implements IterableContainer and provides iterable std::priority_queue class +template, typename Comparator = std::less> +class IterablePriorityQueue : public IterableContainer, public std::priority_queue { +public: + IterablePriorityQueue(std::priority_queue queue_) { + std::size_t count_ = 0; + while (++count_ < base::consts::kMaxLogPerContainer && !queue_.empty()) { + this->push(queue_.top()); + queue_.pop(); + } + } +private: + inline Container& getContainer(void) { + return this->c; + } +}; +/// @brief Implements IterableContainer and provides iterable std::queue class +template> +class IterableQueue : public IterableContainer, public std::queue { +public: + IterableQueue(std::queue queue_) { + std::size_t count_ = 0; + while (++count_ < base::consts::kMaxLogPerContainer && !queue_.empty()) { + this->push(queue_.front()); + queue_.pop(); + } + } +private: + inline Container& getContainer(void) { + return this->c; + } +}; +/// @brief Implements IterableContainer and provides iterable std::stack class +template> +class IterableStack : public IterableContainer, public std::stack { +public: + IterableStack(std::stack stack_) { + std::size_t count_ = 0; + while (++count_ < base::consts::kMaxLogPerContainer && !stack_.empty()) { + this->push(stack_.top()); + stack_.pop(); + } + } +private: + inline Container& getContainer(void) { + return this->c; + } +}; +} // namespace workarounds +#endif // defined(ELPP_STL_LOGGING) +// Log message builder +class MessageBuilder { +public: + MessageBuilder(void) : m_logger(nullptr), m_containerLogSeperator(ELPP_LITERAL("")) {} + void initialize(Logger* logger) { + m_logger = logger; + m_containerLogSeperator = ELPP->hasFlag(LoggingFlag::NewLineForContainer) ? + ELPP_LITERAL("\n ") : ELPP_LITERAL(", "); + } + +# define ELPP_SIMPLE_LOG(LOG_TYPE)\ + inline MessageBuilder& operator<<(LOG_TYPE msg) {\ + m_logger->stream() << msg;\ + if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) {\ + m_logger->stream() << " ";\ + }\ + return *this;\ + } + + inline MessageBuilder& operator<<(const std::string& msg) { + return operator<<(msg.c_str()); + } + ELPP_SIMPLE_LOG(char) + ELPP_SIMPLE_LOG(bool) + ELPP_SIMPLE_LOG(signed short) + ELPP_SIMPLE_LOG(unsigned short) + ELPP_SIMPLE_LOG(signed int) + ELPP_SIMPLE_LOG(unsigned int) + ELPP_SIMPLE_LOG(signed long) + ELPP_SIMPLE_LOG(unsigned long) + ELPP_SIMPLE_LOG(float) + ELPP_SIMPLE_LOG(double) + ELPP_SIMPLE_LOG(char*) + ELPP_SIMPLE_LOG(const char*) + ELPP_SIMPLE_LOG(const void*) + ELPP_SIMPLE_LOG(long double) + inline MessageBuilder& operator<<(const std::wstring& msg) { + return operator<<(msg.c_str()); + } + inline MessageBuilder& operator<<(const wchar_t* msg) { + if (msg == nullptr) { + m_logger->stream() << base::consts::kNullPointer; + return *this; + } +# if defined(ELPP_UNICODE) + m_logger->stream() << msg; +# else + char* buff_ = base::utils::Str::wcharPtrToCharPtr(msg); + m_logger->stream() << buff_; + free(buff_); +# endif + if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) { + m_logger->stream() << " "; + } + return *this; + } + // ostream manipulators + inline MessageBuilder& operator<<(std::ostream& (*OStreamMani)(std::ostream&)) { + m_logger->stream() << OStreamMani; + return *this; + } +#define ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(temp) \ + template \ + inline MessageBuilder& operator<<(const temp& template_inst) { \ + return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ + } +#define ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(temp) \ + template \ + inline MessageBuilder& operator<<(const temp& template_inst) { \ + return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ + } +#define ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(temp) \ + template \ + inline MessageBuilder& operator<<(const temp& template_inst) { \ + return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ + } +#define ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(temp) \ + template \ + inline MessageBuilder& operator<<(const temp& template_inst) { \ + return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ + } +#define ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(temp) \ + template \ + inline MessageBuilder& operator<<(const temp& template_inst) { \ + return writeIterator(template_inst.begin(), template_inst.end(), template_inst.size()); \ + } + +#if defined(ELPP_STL_LOGGING) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::vector) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::list) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(std::deque) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::set) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(std::multiset) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::map) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::multimap) + template + inline MessageBuilder& operator<<(const std::queue& queue_) { + base::workarounds::IterableQueue iterableQueue_ = + static_cast >(queue_); + return writeIterator(iterableQueue_.begin(), iterableQueue_.end(), iterableQueue_.size()); + } + template + inline MessageBuilder& operator<<(const std::stack& stack_) { + base::workarounds::IterableStack iterableStack_ = + static_cast >(stack_); + return writeIterator(iterableStack_.begin(), iterableStack_.end(), iterableStack_.size()); + } + template + inline MessageBuilder& operator<<(const std::priority_queue& priorityQueue_) { + base::workarounds::IterablePriorityQueue iterablePriorityQueue_ = + static_cast >(priorityQueue_); + return writeIterator(iterablePriorityQueue_.begin(), iterablePriorityQueue_.end(), iterablePriorityQueue_.size()); + } + template + inline MessageBuilder& operator<<(const std::pair& pair_) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast(pair_.first)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast(pair_.second)); + m_logger->stream() << ELPP_LITERAL(")"); + return *this; + } + template + inline MessageBuilder& operator<<(const std::bitset& bitset_) { + m_logger->stream() << ELPP_LITERAL("["); + operator << (bitset_.to_string()); + m_logger->stream() << ELPP_LITERAL("]"); + return *this; + } +# if defined(ELPP_LOG_STD_ARRAY) + template + inline MessageBuilder& operator<<(const std::array& array) { + return writeIterator(array.begin(), array.end(), array.size()); + } +# endif // defined(ELPP_LOG_STD_ARRAY) +# if defined(ELPP_LOG_UNORDERED_MAP) + ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_map) + ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG(std::unordered_multimap) +# endif // defined(ELPP_LOG_UNORDERED_MAP) +# if defined(ELPP_LOG_UNORDERED_SET) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_set) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(std::unordered_multiset) +# endif // defined(ELPP_LOG_UNORDERED_SET) +#endif // defined(ELPP_STL_LOGGING) +#if defined(ELPP_QT_LOGGING) + inline MessageBuilder& operator<<(const QString& msg) { +# if defined(ELPP_UNICODE) + m_logger->stream() << msg.toStdWString(); +# else + m_logger->stream() << msg.toStdString(); +# endif // defined(ELPP_UNICODE) + return *this; + } + inline MessageBuilder& operator<<(const QByteArray& msg) { + return operator << (QString(msg)); + } + inline MessageBuilder& operator<<(const QStringRef& msg) { + return operator<<(msg.toString()); + } + inline MessageBuilder& operator<<(qint64 msg) { +# if defined(ELPP_UNICODE) + m_logger->stream() << QString::number(msg).toStdWString(); +# else + m_logger->stream() << QString::number(msg).toStdString(); +# endif // defined(ELPP_UNICODE) + return *this; + } + inline MessageBuilder& operator<<(quint64 msg) { +# if defined(ELPP_UNICODE) + m_logger->stream() << QString::number(msg).toStdWString(); +# else + m_logger->stream() << QString::number(msg).toStdString(); +# endif // defined(ELPP_UNICODE) + return *this; + } + inline MessageBuilder& operator<<(QChar msg) { + m_logger->stream() << msg.toLatin1(); + return *this; + } + inline MessageBuilder& operator<<(const QLatin1String& msg) { + m_logger->stream() << msg.latin1(); + return *this; + } + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QList) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QVector) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QQueue) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QSet) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QLinkedList) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(QStack) + template + inline MessageBuilder& operator<<(const QPair& pair_) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast(pair_.first)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast(pair_.second)); + m_logger->stream() << ELPP_LITERAL(")"); + return *this; + } + template + inline MessageBuilder& operator<<(const QMap& map_) { + m_logger->stream() << ELPP_LITERAL("["); + QList keys = map_.keys(); + typename QList::const_iterator begin = keys.begin(); + typename QList::const_iterator end = keys.end(); + int max_ = static_cast(base::consts::kMaxLogPerContainer); // to prevent warning + for (int index_ = 0; begin != end && index_ < max_; ++index_, ++begin) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast(*begin)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast(map_.value(*begin))); + m_logger->stream() << ELPP_LITERAL(")"); + m_logger->stream() << ((index_ < keys.size() -1) ? m_containerLogSeperator : ELPP_LITERAL("")); + } + if (begin != end) { + m_logger->stream() << ELPP_LITERAL("..."); + } + m_logger->stream() << ELPP_LITERAL("]"); + return *this; + } + template + inline MessageBuilder& operator<<(const QMultiMap& map_) { + operator << (static_cast>(map_)); + return *this; + } + template + inline MessageBuilder& operator<<(const QHash& hash_) { + m_logger->stream() << ELPP_LITERAL("["); + QList keys = hash_.keys(); + typename QList::const_iterator begin = keys.begin(); + typename QList::const_iterator end = keys.end(); + int max_ = static_cast(base::consts::kMaxLogPerContainer); // prevent type warning + for (int index_ = 0; begin != end && index_ < max_; ++index_, ++begin) { + m_logger->stream() << ELPP_LITERAL("("); + operator << (static_cast(*begin)); + m_logger->stream() << ELPP_LITERAL(", "); + operator << (static_cast(hash_.value(*begin))); + m_logger->stream() << ELPP_LITERAL(")"); + m_logger->stream() << ((index_ < keys.size() -1) ? m_containerLogSeperator : ELPP_LITERAL("")); + } + if (begin != end) { + m_logger->stream() << ELPP_LITERAL("..."); + } + m_logger->stream() << ELPP_LITERAL("]"); + return *this; + } + template + inline MessageBuilder& operator<<(const QMultiHash& multiHash_) { + operator << (static_cast>(multiHash_)); + return *this; + } +#endif // defined(ELPP_QT_LOGGING) +#if defined(ELPP_BOOST_LOGGING) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::vector) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::stable_vector) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::list) + ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG(boost::container::deque) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::map) + ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG(boost::container::flat_map) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::set) + ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG(boost::container::flat_set) +#endif // defined(ELPP_BOOST_LOGGING) + +/// @brief Macro used internally that can be used externally to make containers easylogging++ friendly +/// +/// @detail This macro expands to write an ostream& operator<< for container. This container is expected to +/// have begin() and end() methods that return respective iterators +/// @param ContainerType Type of container e.g, MyList from WX_DECLARE_LIST(int, MyList); in wxwidgets +/// @param SizeMethod Method used to get size of container. +/// @param ElementInstance Instance of element to be fed out. Insance name is "elem". See WXELPP_ENABLED macro +/// for an example usage +#define MAKE_CONTAINERELPP_FRIENDLY(ContainerType, SizeMethod, ElementInstance) \ + el::base::type::ostream_t& operator<<(el::base::type::ostream_t& ss, const ContainerType& container) {\ + const el::base::type::char_t* sep = ELPP->hasFlag(el::LoggingFlag::NewLineForContainer) ? \ + ELPP_LITERAL("\n ") : ELPP_LITERAL(", ");\ + ContainerType::const_iterator elem = container.begin();\ + ContainerType::const_iterator endElem = container.end();\ + std::size_t size_ = container.SizeMethod; \ + ss << ELPP_LITERAL("[");\ + for (std::size_t i = 0; elem != endElem && i < el::base::consts::kMaxLogPerContainer; ++i, ++elem) { \ + ss << ElementInstance;\ + ss << ((i < size_ - 1) ? sep : ELPP_LITERAL(""));\ + }\ + if (elem != endElem) {\ + ss << ELPP_LITERAL("...");\ + }\ + ss << ELPP_LITERAL("]");\ + return ss;\ + } +#if defined(ELPP_WXWIDGETS_LOGGING) + ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG(wxVector) +# define ELPP_WX_PTR_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), *(*elem)) +# define ELPP_WX_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), (*elem)) +# define ELPP_WX_HASH_MAP_ENABLED(ContainerType) MAKE_CONTAINERELPP_FRIENDLY(ContainerType, size(), \ + ELPP_LITERAL("(") << elem->first << ELPP_LITERAL(", ") << elem->second << ELPP_LITERAL(")") +#else +# define ELPP_WX_PTR_ENABLED(ContainerType) +# define ELPP_WX_ENABLED(ContainerType) +# define ELPP_WX_HASH_MAP_ENABLED(ContainerType) +#endif // defined(ELPP_WXWIDGETS_LOGGING) + // Other classes + template + ELPP_SIMPLE_LOG(const Class&) +#undef ELPP_SIMPLE_LOG +#undef ELPP_ITERATOR_CONTAINER_LOG_ONE_ARG +#undef ELPP_ITERATOR_CONTAINER_LOG_TWO_ARG +#undef ELPP_ITERATOR_CONTAINER_LOG_THREE_ARG +#undef ELPP_ITERATOR_CONTAINER_LOG_FOUR_ARG +#undef ELPP_ITERATOR_CONTAINER_LOG_FIVE_ARG +private: + Logger* m_logger; + const base::type::char_t* m_containerLogSeperator; + + template + inline MessageBuilder& writeIterator(Iterator begin_, Iterator end_, std::size_t size_) { + m_logger->stream() << ELPP_LITERAL("["); + for (std::size_t i = 0; begin_ != end_ && i < base::consts::kMaxLogPerContainer; ++i, ++begin_) { + operator << (*begin_); + m_logger->stream() << ((i < size_ - 1) ? m_containerLogSeperator : ELPP_LITERAL("")); + } + if (begin_ != end_) { + m_logger->stream() << ELPP_LITERAL("..."); + } + m_logger->stream() << ELPP_LITERAL("]"); + if (ELPP->hasFlag(LoggingFlag::AutoSpacing)) { + m_logger->stream() << " "; + } + return *this; + } +}; +/// @brief Writes nothing - Used when certain log is disabled +class NullWriter : base::NoCopy { +public: + NullWriter(void) {} + + // Null manipulator + inline NullWriter& operator<<(std::ostream& (*)(std::ostream&)) { + return *this; + } + + template + inline NullWriter& operator<<(const T&) { + return *this; + } +}; +/// @brief Main entry point of each logging +class Writer : base::NoCopy { +public: + Writer(Level level, const char* file, unsigned long int line, + const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog, + base::type::VerboseLevel verboseLevel = 0) : + m_level(level), m_file(file), m_line(line), m_func(func), m_verboseLevel(verboseLevel), + m_proceed(false), m_dispatchAction(dispatchAction) { + } + + virtual ~Writer(void) { + processDispatch(); + } + + template + inline Writer& operator<<(const T& log) { +#if ELPP_LOGGING_ENABLED + if (m_proceed) { + m_messageBuilder << log; + } +#endif // ELPP_LOGGING_ENABLED + return *this; + } + + inline Writer& operator<<(std::ostream& (*log)(std::ostream&)) { +#if ELPP_LOGGING_ENABLED + if (m_proceed) { + m_messageBuilder << log; + } +#endif // ELPP_LOGGING_ENABLED + return *this; + } + + Writer& construct(Logger* logger, bool needLock = true) { + m_logger = logger; + initializeLogger(logger->id(), false, needLock); + m_messageBuilder.initialize(m_logger); + return *this; + } + + Writer& construct(int count, const char* loggerIds, ...) { + if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) { + va_list loggersList; + va_start(loggersList, loggerIds); + const char* id = loggerIds; + for (int i = 0; i < count; ++i) { + m_loggerIds.push_back(std::string(id)); + id = va_arg(loggersList, const char*); + } + va_end(loggersList); + initializeLogger(m_loggerIds.at(0)); + } else { + initializeLogger(std::string(loggerIds)); + } + m_messageBuilder.initialize(m_logger); + return *this; + } +protected: + Level m_level; + const char* m_file; + const unsigned long int m_line; + const char* m_func; + base::type::VerboseLevel m_verboseLevel; + Logger* m_logger; + bool m_proceed; + base::MessageBuilder m_messageBuilder; + base::DispatchAction m_dispatchAction; + std::vector m_loggerIds; + friend class el::Helpers; + + void initializeLogger(const std::string& loggerId, bool lookup = true, bool needLock = true) { + if (lookup) { + m_logger = ELPP->registeredLoggers()->get(loggerId, ELPP->hasFlag(LoggingFlag::CreateLoggerAutomatically)); + } + if (m_logger == nullptr) { + ELPP->acquireLock(); + if (!ELPP->registeredLoggers()->has(std::string(base::consts::kDefaultLoggerId))) { + // Somehow default logger has been unregistered. Not good! Register again + ELPP->registeredLoggers()->get(std::string(base::consts::kDefaultLoggerId)); + } + ELPP->releaseLock(); // Need to unlock it for next writer + Writer(Level::Debug, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId) + << "Logger [" << loggerId << "] is not registered yet!"; + m_proceed = false; + } else { + if (needLock) { + m_logger->acquireLock(); // This should not be unlocked by checking m_proceed because + // m_proceed can be changed by lines below + } + if (ELPP->hasFlag(LoggingFlag::HierarchicalLogging)) { + m_proceed = m_level == Level::Verbose ? m_logger->enabled(m_level) : + LevelHelper::castToInt(m_level) >= LevelHelper::castToInt(ELPP->m_loggingLevel); + } else { + m_proceed = m_logger->enabled(m_level); + } + } + } + + void processDispatch() { +#if ELPP_LOGGING_ENABLED + if (ELPP->hasFlag(LoggingFlag::MultiLoggerSupport)) { + bool firstDispatched = false; + base::type::string_t logMessage; + std::size_t i = 0; + do { + if (m_proceed) { + if (firstDispatched) { + m_logger->stream() << logMessage; + } else { + firstDispatched = true; + if (m_loggerIds.size() > 1) { + logMessage = m_logger->stream().str(); + } + } + triggerDispatch(); + } else if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } + if (i + 1 < m_loggerIds.size()) { + initializeLogger(m_loggerIds.at(i + 1)); + } + } while (++i < m_loggerIds.size()); + } else { + if (m_proceed) { + triggerDispatch(); + } else if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } + } +#else + if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } +#endif // ELPP_LOGGING_ENABLED + } + + void triggerDispatch(void) { + if (m_proceed) { + base::LogDispatcher(m_proceed, LogMessage(m_level, m_file, m_line, m_func, m_verboseLevel, + m_logger), m_dispatchAction).dispatch(); + } + if (m_logger != nullptr) { + m_logger->stream().str(ELPP_LITERAL("")); + m_logger->releaseLock(); + } + if (m_proceed && m_level == Level::Fatal + && !ELPP->hasFlag(LoggingFlag::DisableApplicationAbortOnFatalLog)) { + base::Writer(Level::Warning, m_file, m_line, m_func).construct(1, base::consts::kDefaultLoggerId) + << "Aborting application. Reason: Fatal log at [" << m_file << ":" << m_line << "]"; + std::stringstream reasonStream; + reasonStream << "Fatal log at [" << m_file << ":" << m_line << "]" + << " If you wish to disable 'abort on fatal log' please use " + << "el::Helpers::addFlag(el::LoggingFlag::DisableApplicationAbortOnFatalLog)"; + base::utils::abort(1, reasonStream.str()); + } + m_proceed = false; + } +}; +class PErrorWriter : public base::Writer { +public: + PErrorWriter(Level level, const char* file, unsigned long int line, + const char* func, base::DispatchAction dispatchAction = base::DispatchAction::NormalLog, + base::type::VerboseLevel verboseLevel = 0) : + base::Writer(level, file, line, func, dispatchAction, verboseLevel) { + } + + virtual ~PErrorWriter(void) { + if (m_proceed) { +#if ELPP_COMPILER_MSVC + char buff[256]; + strerror_s(buff, 256, errno); + m_logger->stream() << ": " << buff << " [" << errno << "]"; +#else + m_logger->stream() << ": " << strerror(errno) << " [" << errno << "]"; +#endif + } + } +}; +} // namespace base +// Logging from Logger class. Why this is here? Because we have Storage and Writer class available +#if ELPP_VARIADIC_TEMPLATES_SUPPORTED + template + void Logger::log_(Level level, int vlevel, const char* s, const T& value, const Args&... args) { + base::MessageBuilder b; + b.initialize(this); + while (*s) { + if (*s == base::consts::kFormatSpecifierChar) { + if (*(s + 1) == base::consts::kFormatSpecifierChar) { + ++s; + } else { + if (*(s + 1) == base::consts::kFormatSpecifierCharValue) { + ++s; + b << value; + log_(level, vlevel, ++s, args...); + return; + } + } + } + b << *s++; + } + ELPP_INTERNAL_ERROR("Too many arguments provided. Unable to handle. Please provide more format specifiers", false); + } + template + inline void Logger::log_(Level level, int vlevel, const T& log) { + if (level == Level::Verbose) { + if (ELPP->vRegistry()->allowed(vlevel, __FILE__)) { + base::Writer(Level::Verbose, "FILE", 0, "FUNCTION", + base::DispatchAction::NormalLog, vlevel).construct(this, false) << log; + } else { + stream().str(ELPP_LITERAL("")); + } + } else { + base::Writer(level, "FILE", 0, "FUNCTION").construct(this, false) << log; + } + } + template + void Logger::log(Level level, const char* s, const T& value, const Args&... args) { + base::threading::ScopedLock scopedLock(lock()); + log_(level, 0, s, value, args...); + } + template + inline void Logger::log(Level level, const T& log) { + base::threading::ScopedLock scopedLock(lock()); + log_(level, 0, log); + } +# if ELPP_VERBOSE_LOG + template + inline void Logger::verbose(int vlevel, const char* s, const T& value, const Args&... args) { + base::threading::ScopedLock scopedLock(lock()); + log_(el::Level::Verbose, vlevel, s, value, args...); + } + template + inline void Logger::verbose(int vlevel, const T& log) { + base::threading::ScopedLock scopedLock(lock()); + log_(el::Level::Verbose, vlevel, log); + } +# else + template + inline void Logger::verbose(int, const char*, const T&, const Args&...) { + return; + } + template + inline void Logger::verbose(int, const T&) { + return; + } +# endif // ELPP_VERBOSE_LOG +# define LOGGER_LEVEL_WRITERS(FUNCTION_NAME, LOG_LEVEL)\ + template \ + inline void Logger::FUNCTION_NAME(const char* s, const T& value, const Args&... args) {\ + log(LOG_LEVEL, s, value, args...);\ + }\ + template \ + inline void Logger::FUNCTION_NAME(const T& value) {\ + log(LOG_LEVEL, value);\ + } +# define LOGGER_LEVEL_WRITERS_DISABLED(FUNCTION_NAME, LOG_LEVEL)\ + template \ + inline void Logger::FUNCTION_NAME(const char*, const T&, const Args&...) {\ + return;\ + }\ + template \ + inline void Logger::FUNCTION_NAME(const T&) {\ + return;\ + } + +# if ELPP_INFO_LOG + LOGGER_LEVEL_WRITERS(info, Level::Info) +# else + LOGGER_LEVEL_WRITERS_DISABLED(info, Level::Info) +# endif // ELPP_INFO_LOG +# if ELPP_DEBUG_LOG + LOGGER_LEVEL_WRITERS(debug, Level::Debug) +# else + LOGGER_LEVEL_WRITERS_DISABLED(debug, Level::Debug) +# endif // ELPP_DEBUG_LOG +# if ELPP_WARNING_LOG + LOGGER_LEVEL_WRITERS(warn, Level::Warning) +# else + LOGGER_LEVEL_WRITERS_DISABLED(warn, Level::Warning) +# endif // ELPP_WARNING_LOG +# if ELPP_ERROR_LOG + LOGGER_LEVEL_WRITERS(error, Level::Error) +# else + LOGGER_LEVEL_WRITERS_DISABLED(error, Level::Error) +# endif // ELPP_ERROR_LOG +# if ELPP_FATAL_LOG + LOGGER_LEVEL_WRITERS(fatal, Level::Fatal) +# else + LOGGER_LEVEL_WRITERS_DISABLED(fatal, Level::Fatal) +# endif // ELPP_FATAL_LOG +# if ELPP_TRACE_LOG + LOGGER_LEVEL_WRITERS(trace, Level::Trace) +# else + LOGGER_LEVEL_WRITERS_DISABLED(trace, Level::Trace) +# endif // ELPP_TRACE_LOG +# undef LOGGER_LEVEL_WRITERS +# undef LOGGER_LEVEL_WRITERS_DISABLED +#endif // ELPP_VARIADIC_TEMPLATES_SUPPORTED +#if ELPP_COMPILER_MSVC +# define ELPP_VARIADIC_FUNC_MSVC(variadicFunction, variadicArgs) variadicFunction variadicArgs +# define ELPP_VARIADIC_FUNC_MSVC_RUN(variadicFunction, ...) ELPP_VARIADIC_FUNC_MSVC(variadicFunction, (__VA_ARGS__)) +# define el_getVALength(...) ELPP_VARIADIC_FUNC_MSVC_RUN(el_resolveVALength, 0, ## __VA_ARGS__,\ + 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +#else +# if ELPP_COMPILER_CLANG +# define el_getVALength(...) el_resolveVALength(0, __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +# else +# define el_getVALength(...) el_resolveVALength(0, ## __VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0) +# endif // ELPP_COMPILER_CLANG +#endif // ELPP_COMPILER_MSVC +#define el_resolveVALength(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N +#define ELPP_WRITE_LOG(writer, level, dispatchAction, ...) \ + writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#define ELPP_WRITE_LOG_IF(writer, condition, level, dispatchAction, ...) if (condition) \ + writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#define ELPP_WRITE_LOG_EVERY_N(writer, occasion, level, dispatchAction, ...) \ + if (ELPP->validateEveryNCounter(__FILE__, __LINE__, occasion)) \ + writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#define ELPP_WRITE_LOG_AFTER_N(writer, n, level, dispatchAction, ...) \ + if (ELPP->validateAfterNCounter(__FILE__, __LINE__, n)) \ + writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#define ELPP_WRITE_LOG_N_TIMES(writer, n, level, dispatchAction, ...) \ + if (ELPP->validateNTimesCounter(__FILE__, __LINE__, n)) \ + writer(level, __FILE__, __LINE__, ELPP_FUNC, dispatchAction).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#undef ELPP_CURR_FILE_PERFORMANCE_LOGGER +#if defined(ELPP_PERFORMANCE_LOGGER) +# define ELPP_CURR_FILE_PERFORMANCE_LOGGER ELPP_PERFORMANCE_LOGGER +#else +# define ELPP_CURR_FILE_PERFORMANCE_LOGGER el::base::consts::kPerformanceLoggerId +#endif +class PerformanceTrackingData { +public: + enum class DataType : base::type::EnumType { + Checkpoint = 1, Complete = 2 + }; + // Do not use constructor, will run into multiple definition error, use init(PerformanceTracker*) + explicit PerformanceTrackingData(DataType dataType) : m_performanceTracker(nullptr), + m_dataType(dataType), m_file(""), m_line(0), m_func("") {} + inline const std::string* blockName(void) const; + inline const struct timeval* startTime(void) const; + inline const struct timeval* endTime(void) const; + inline const struct timeval* lastCheckpointTime(void) const; + inline const base::PerformanceTracker* performanceTracker(void) const { return m_performanceTracker; } + inline PerformanceTrackingData::DataType dataType(void) const { return m_dataType; } + inline bool firstCheckpoint(void) const { return m_firstCheckpoint; } + inline std::string checkpointId(void) const { return m_checkpointId; } + inline const char* file(void) const { return m_file; } + inline unsigned long int line(void) const { return m_line; } + inline const char* func(void) const { return m_func; } + inline const base::type::string_t* formattedTimeTaken() const { return &m_formattedTimeTaken; } + inline const std::string& loggerId(void) const; +private: + base::PerformanceTracker* m_performanceTracker; + base::type::string_t m_formattedTimeTaken; + PerformanceTrackingData::DataType m_dataType; + bool m_firstCheckpoint; + std::string m_checkpointId; + const char* m_file; + unsigned long int m_line; + const char* m_func; + inline void init(base::PerformanceTracker* performanceTracker, bool firstCheckpoint = false) { + m_performanceTracker = performanceTracker; + m_firstCheckpoint = firstCheckpoint; + } + + friend class el::base::PerformanceTracker; +}; +namespace base { +/// @brief Represents performanceTracker block of code that conditionally adds performance status to log +/// either when goes outside the scope of when checkpoint() is called +class PerformanceTracker : public base::threading::ThreadSafe, public Loggable { +public: + PerformanceTracker(const std::string& blockName, + base::TimestampUnit timestampUnit = base::TimestampUnit::Millisecond, + const std::string& loggerId = std::string(ELPP_CURR_FILE_PERFORMANCE_LOGGER), + bool scopedLog = true, Level level = base::consts::kPerformanceTrackerDefaultLevel) : + m_blockName(blockName), m_timestampUnit(timestampUnit), m_loggerId(loggerId), m_scopedLog(scopedLog), + m_level(level), m_hasChecked(false), m_lastCheckpointId(std::string()), m_enabled(false) { +#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + // We store it locally so that if user happen to change configuration by the end of scope + // or before calling checkpoint, we still depend on state of configuraton at time of construction + el::Logger* loggerPtr = ELPP->registeredLoggers()->get(loggerId, false); + m_enabled = loggerPtr != nullptr && loggerPtr->m_typedConfigurations->performanceTracking(m_level); + if (m_enabled) { + base::utils::DateTime::gettimeofday(&m_startTime); + } +#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + } + /// @brief Copy constructor + PerformanceTracker(const PerformanceTracker& t) : + m_blockName(t.m_blockName), m_timestampUnit(t.m_timestampUnit), m_loggerId(t.m_loggerId), m_scopedLog(t.m_scopedLog), + m_level(t.m_level), m_hasChecked(t.m_hasChecked), m_lastCheckpointId(t.m_lastCheckpointId), m_enabled(t.m_enabled), + m_startTime(t.m_startTime), m_endTime(t.m_endTime), m_lastCheckpointTime(t.m_lastCheckpointTime) { + } + virtual ~PerformanceTracker(void) { +#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + if (m_enabled) { + base::threading::ScopedLock scopedLock(lock()); + if (m_scopedLog) { + base::utils::DateTime::gettimeofday(&m_endTime); + base::type::string_t formattedTime = getFormattedTimeTaken(); + PerformanceTrackingData data(PerformanceTrackingData::DataType::Complete); + data.init(this); + data.m_formattedTimeTaken = formattedTime; + PerformanceTrackingCallback* callback = nullptr; + for (const std::pair& h + : ELPP->m_performanceTrackingCallbacks) { + callback = h.second.get(); + if (callback != nullptr && callback->enabled()) { + callback->acquireLock(); + callback->handle(&data); + callback->releaseLock(); + } + } + } + } +#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) + } + /// @brief A checkpoint for current performanceTracker block. + void checkpoint(const std::string& id = std::string(), const char* file = __FILE__, unsigned long int line = __LINE__, const char* func = "") { +#if !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + if (m_enabled) { + base::threading::ScopedLock scopedLock(lock()); + base::utils::DateTime::gettimeofday(&m_endTime); + base::type::string_t formattedTime = m_hasChecked ? getFormattedTimeTaken(m_lastCheckpointTime) : ELPP_LITERAL(""); + PerformanceTrackingData data(PerformanceTrackingData::DataType::Checkpoint); + data.init(this); + data.m_checkpointId = id; + data.m_file = file; + data.m_line = line; + data.m_func = func; + data.m_formattedTimeTaken = formattedTime; + PerformanceTrackingCallback* callback = nullptr; + for (const std::pair& h + : ELPP->m_performanceTrackingCallbacks) { + callback = h.second.get(); + if (callback != nullptr && callback->enabled()) { + callback->acquireLock(); + callback->handle(&data); + callback->releaseLock(); + } + } + base::utils::DateTime::gettimeofday(&m_lastCheckpointTime); + m_hasChecked = true; + m_lastCheckpointId = id; + } +#endif // !defined(ELPP_DISABLE_PERFORMANCE_TRACKING) && ELPP_LOGGING_ENABLED + ELPP_UNUSED(id); + ELPP_UNUSED(file); + ELPP_UNUSED(line); + ELPP_UNUSED(func); + } + inline Level level(void) const { return m_level; } +private: + std::string m_blockName; + base::TimestampUnit m_timestampUnit; + std::string m_loggerId; + bool m_scopedLog; + Level m_level; + bool m_hasChecked; + std::string m_lastCheckpointId; + bool m_enabled; + struct timeval m_startTime, m_endTime, m_lastCheckpointTime; + + PerformanceTracker(void); + + friend class el::PerformanceTrackingData; + friend class base::DefaultPerformanceTrackingCallback; + + const inline base::type::string_t getFormattedTimeTaken() const { + return getFormattedTimeTaken(m_startTime); + } + + const base::type::string_t getFormattedTimeTaken(struct timeval startTime) const { + if (ELPP->hasFlag(LoggingFlag::FixedTimeFormat)) { + base::type::stringstream_t ss; + ss << base::utils::DateTime::getTimeDifference(m_endTime, + startTime, m_timestampUnit) << " " << base::consts::kTimeFormats[static_cast(m_timestampUnit)].unit; + return ss.str(); + } + return base::utils::DateTime::formatTime(base::utils::DateTime::getTimeDifference(m_endTime, + startTime, m_timestampUnit), m_timestampUnit); + } + + virtual inline void log(el::base::type::ostream_t& os) const { + os << getFormattedTimeTaken(); + } +}; +class DefaultPerformanceTrackingCallback : public PerformanceTrackingCallback { +protected: + void handle(const PerformanceTrackingData* data) { + m_data = data; + base::type::stringstream_t ss; + if (m_data->dataType() == PerformanceTrackingData::DataType::Complete) { + ss << ELPP_LITERAL("Executed [") << m_data->blockName()->c_str() << ELPP_LITERAL("] in [") << *m_data->formattedTimeTaken() << ELPP_LITERAL("]"); + } else { + ss << ELPP_LITERAL("Performance checkpoint"); + if (!m_data->checkpointId().empty()) { + ss << ELPP_LITERAL(" [") << m_data->checkpointId().c_str() << ELPP_LITERAL("]"); + } + ss << ELPP_LITERAL(" for block [") << m_data->blockName()->c_str() << ELPP_LITERAL("] : [") << *m_data->performanceTracker(); + if (!ELPP->hasFlag(LoggingFlag::DisablePerformanceTrackingCheckpointComparison) && m_data->performanceTracker()->m_hasChecked) { + ss << ELPP_LITERAL(" ([") << *m_data->formattedTimeTaken() << ELPP_LITERAL("] from "); + if (m_data->performanceTracker()->m_lastCheckpointId.empty()) { + ss << ELPP_LITERAL("last checkpoint"); + } else { + ss << ELPP_LITERAL("checkpoint '") << m_data->performanceTracker()->m_lastCheckpointId.c_str() << ELPP_LITERAL("'"); + } + ss << ELPP_LITERAL(")]"); + } else { + ss << ELPP_LITERAL("]"); + } + } + el::base::Writer(m_data->performanceTracker()->level(), m_data->file(), m_data->line(), m_data->func()).construct(1, m_data->loggerId().c_str()) << ss.str(); + } +private: + const PerformanceTrackingData* m_data; +}; +} // namespace base +inline const std::string* PerformanceTrackingData::blockName() const { + return const_cast(&m_performanceTracker->m_blockName); +} +inline const struct timeval* PerformanceTrackingData::startTime() const { + return const_cast(&m_performanceTracker->m_startTime); +} +inline const struct timeval* PerformanceTrackingData::endTime() const { + return const_cast(&m_performanceTracker->m_endTime); +} +inline const struct timeval* PerformanceTrackingData::lastCheckpointTime() const { + return const_cast(&m_performanceTracker->m_lastCheckpointTime); +} +inline const std::string& PerformanceTrackingData::loggerId(void) const { return m_performanceTracker->m_loggerId; } +namespace base { +/// @brief Contains some internal debugging tools like crash handler and stack tracer +namespace debug { +class StackTrace : base::NoCopy { +public: + static const std::size_t kMaxStack = 64; + static const std::size_t kStackStart = 2; // We want to skip c'tor and StackTrace::generateNew() + class StackTraceEntry { + public: + StackTraceEntry(std::size_t index, const char* loc, const char* demang, const char* hex, const char* addr) { + m_index = index; + m_location = std::string(loc); + m_demangled = std::string(demang); + m_hex = std::string(hex); + m_addr = std::string(addr); + } + StackTraceEntry(std::size_t index, char* loc) { + m_index = index; + m_location = std::string(loc); + } + std::size_t m_index; + std::string m_location; + std::string m_demangled; + std::string m_hex; + std::string m_addr; + friend std::ostream& operator<<(std::ostream& ss, const StackTraceEntry& si) { + ss << "[" << si.m_index << "] " << si.m_location << (si.m_demangled.empty() ? "" : ":") << si.m_demangled + << (si.m_hex.empty() ? "" : "+") << si.m_hex << si.m_addr; + return ss; + } + + private: + StackTraceEntry(void); + }; + + StackTrace(void) { + generateNew(); + } + + virtual ~StackTrace(void) { + } + + inline std::vector& getLatestStack(void) { + return m_stack; + } + + friend inline std::ostream& operator<<(std::ostream& os, const StackTrace& st) { + std::vector::const_iterator it = st.m_stack.begin(); + while (it != st.m_stack.end()) { + os << " " << *it++ << "\n"; + } + return os; + } + +private: + std::vector m_stack; + + void generateNew(void) { +#if ELPP_STACKTRACE + m_stack.clear(); + void* stack[kMaxStack]; + std::size_t size = backtrace(stack, kMaxStack); + char** strings = backtrace_symbols(stack, size); + if (size > kStackStart) { // Skip StackTrace c'tor and generateNew + for (std::size_t i = kStackStart; i < size; ++i) { + char* mangName = nullptr; + char* hex = nullptr; + char* addr = nullptr; + for (char* c = strings[i]; *c; ++c) { + switch (*c) { + case '(': + mangName = c; + break; + case '+': + hex = c; + break; + case ')': + addr = c; + break; + } + } + // Perform demangling if parsed properly + if (mangName != nullptr && hex != nullptr && addr != nullptr && mangName < hex) { + *mangName++ = '\0'; + *hex++ = '\0'; + *addr++ = '\0'; + int status = 0; + char* demangName = abi::__cxa_demangle(mangName, 0, 0, &status); + // if demangling is successful, output the demangled function name + if (status == 0) { + // Success (see http://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-4.3/a01696.html) + StackTraceEntry entry(i - 1, strings[i], demangName, hex, addr); + m_stack.push_back(entry); + } else { + // Not successful - we will use mangled name + StackTraceEntry entry(i - 1, strings[i], mangName, hex, addr); + m_stack.push_back(entry); + } + free(demangName); + } else { + StackTraceEntry entry(i - 1, strings[i]); + m_stack.push_back(entry); + } + } + } + free(strings); +#else + ELPP_INTERNAL_INFO(1, "Stacktrace generation not supported for selected compiler"); +#endif // ELPP_STACKTRACE + } +}; +static std::string crashReason(int sig) { + std::stringstream ss; + bool foundReason = false; + for (int i = 0; i < base::consts::kCrashSignalsCount; ++i) { + if (base::consts::kCrashSignals[i].numb == sig) { + ss << "Application has crashed due to [" << base::consts::kCrashSignals[i].name << "] signal"; + if (ELPP->hasFlag(el::LoggingFlag::LogDetailedCrashReason)) { + ss << std::endl << + " " << base::consts::kCrashSignals[i].brief << std::endl << + " " << base::consts::kCrashSignals[i].detail; + } + foundReason = true; + } + } + if (!foundReason) { + ss << "Application has crashed due to unknown signal [" << sig << "]"; + } + return ss.str(); +} +/// @brief Logs reason of crash from sig +static void logCrashReason(int sig, bool stackTraceIfAvailable, Level level, const char* logger) { + std::stringstream ss; + ss << "CRASH HANDLED; "; + ss << crashReason(sig); +#if ELPP_STACKTRACE + if (stackTraceIfAvailable) { + ss << std::endl << " ======= Backtrace: =========" << std::endl << base::debug::StackTrace(); + } +#else + ELPP_UNUSED(stackTraceIfAvailable); +#endif // ELPP_STACKTRACE + ELPP_WRITE_LOG(el::base::Writer, level, base::DispatchAction::NormalLog, logger) << ss.str(); +} +static inline void crashAbort(int sig) { + base::utils::abort(sig); +} +/// @brief Default application crash handler +/// +/// @detail This function writes log using 'default' logger, prints stack trace for GCC based compilers and aborts program. +static inline void defaultCrashHandler(int sig) { + base::debug::logCrashReason(sig, true, Level::Fatal, base::consts::kDefaultLoggerId); + base::debug::crashAbort(sig); +} +/// @brief Handles unexpected crashes +class CrashHandler : base::NoCopy { +public: + typedef void (*Handler)(int); + + explicit CrashHandler(bool useDefault) { + if (useDefault) { + setHandler(defaultCrashHandler); + } + } + explicit CrashHandler(const Handler& cHandler) { + setHandler(cHandler); + } + void setHandler(const Handler& cHandler) { + m_handler = cHandler; +#if defined(ELPP_HANDLE_SIGABRT) + int i = 0; // SIGABRT is at base::consts::kCrashSignals[0] +#else + int i = 1; +#endif // defined(ELPP_HANDLE_SIGABRT) + for (; i < base::consts::kCrashSignalsCount; ++i) { + m_handler = signal(base::consts::kCrashSignals[i].numb, cHandler); + } + } + +private: + Handler m_handler; +}; +} // namespace debug +} // namespace base +extern base::debug::CrashHandler elCrashHandler; +#define MAKE_LOGGABLE(ClassType, ClassInstance, OutputStreamInstance) \ + el::base::type::ostream_t& operator<<(el::base::type::ostream_t& OutputStreamInstance, const ClassType& ClassInstance) +/// @brief Initializes syslog with process ID, options and facility. calls closelog() on d'tor +class SysLogInitializer { +public: + SysLogInitializer(const char* processIdent, int options = 0, int facility = 0) { +#if defined(ELPP_SYSLOG) + openlog(processIdent, options, facility); +#else + ELPP_UNUSED(processIdent); + ELPP_UNUSED(options); + ELPP_UNUSED(facility); +#endif // defined(ELPP_SYSLOG) + } + virtual ~SysLogInitializer(void) { +#if defined(ELPP_SYSLOG) + closelog(); +#endif // defined(ELPP_SYSLOG) + } +}; +#define ELPP_INITIALIZE_SYSLOG(id, opt, fac) el::SysLogInitializer elSyslogInit(id, opt, fac) +/// @brief Static helpers for developers +class Helpers : base::StaticClass { +public: + /// @brief Shares logging repository (base::Storage) + static inline void setStorage(base::type::StoragePointer storage) { + ELPP = storage; + } + /// @return Main storage repository + static inline base::type::StoragePointer storage() { + return ELPP; + } + /// @brief Sets application arguments and figures out whats active for logging and whats not. + static inline void setArgs(int argc, char** argv) { + ELPP->setApplicationArguments(argc, argv); + } + /// @copydoc setArgs(int argc, char** argv) + static inline void setArgs(int argc, const char** argv) { + ELPP->setApplicationArguments(argc, const_cast(argv)); + } + /// @brief Overrides default crash handler and installs custom handler. + /// @param crashHandler A functor with no return type that takes single int argument. + /// Handler is a typedef with specification: void (*Handler)(int) + static inline void setCrashHandler(const el::base::debug::CrashHandler::Handler& crashHandler) { + el::elCrashHandler.setHandler(crashHandler); + } + /// @brief Abort due to crash with signal in parameter + /// @param sig Crash signal + static inline void crashAbort(int sig, const char* sourceFile = "", unsigned int long line = 0) { + std::stringstream ss; + ss << base::debug::crashReason(sig).c_str(); + ss << " - [Called el::Helpers::crashAbort(" << sig << ")]"; + if (sourceFile != nullptr && strlen(sourceFile) > 0) { + ss << " - Source: " << sourceFile; + if (line > 0) + ss << ":" << line; + else + ss << " (line number not specified)"; + } + base::utils::abort(sig, ss.str()); + } + /// @brief Logs reason of crash as per sig + /// @param sig Crash signal + /// @param stackTraceIfAvailable Includes stack trace if available + /// @param level Logging level + /// @param logger Logger to use for logging + static inline void logCrashReason(int sig, bool stackTraceIfAvailable = false, + Level level = Level::Fatal, const char* logger = base::consts::kDefaultLoggerId) { + el::base::debug::logCrashReason(sig, stackTraceIfAvailable, level, logger); + } + /// @brief Installs pre rollout callback, this callback is triggered when log file is about to be rolled out + /// (can be useful for backing up) + static inline void installPreRollOutCallback(const PreRollOutCallback& callback) { + ELPP->setPreRollOutCallback(callback); + } + /// @brief Uninstalls pre rollout callback + static inline void uninstallPreRollOutCallback(void) { + ELPP->unsetPreRollOutCallback(); + } + /// @brief Installs post log dispatch callback, this callback is triggered when log is dispatched + template + static inline bool installLogDispatchCallback(const std::string& id) { + return ELPP->installLogDispatchCallback(id); + } + /// @brief Uninstalls log dispatch callback + template + static inline void uninstallLogDispatchCallback(const std::string& id) { + ELPP->uninstallLogDispatchCallback(id); + } + template + static inline T* logDispatchCallback(const std::string& id) { + return ELPP->logDispatchCallback(id); + } + /// @brief Installs post performance tracking callback, this callback is triggered when performance tracking is finished + template + static inline bool installPerformanceTrackingCallback(const std::string& id) { + return ELPP->installPerformanceTrackingCallback(id); + } + /// @brief Uninstalls post performance tracking handler + template + static inline void uninstallPerformanceTrackingCallback(const std::string& id) { + ELPP->uninstallPerformanceTrackingCallback(id); + } + template + static inline T* performanceTrackingCallback(const std::string& id) { + return ELPP->performanceTrackingCallback(id); + } + /// @brief Converts template to std::string - useful for loggable classes to log containers within log(std::ostream&) const + template + static std::string convertTemplateToStdString(const T& templ) { + el::Logger* logger = + ELPP->registeredLoggers()->get(el::base::consts::kDefaultLoggerId); + if (logger == nullptr) { + return std::string(); + } + base::MessageBuilder b; + b.initialize(logger); + logger->acquireLock(); + b << templ; +#if defined(ELPP_UNICODE) + std::string s = std::string(logger->stream().str().begin(), logger->stream().str().end()); +#else + std::string s = logger->stream().str(); +#endif // defined(ELPP_UNICODE) + logger->stream().str(ELPP_LITERAL("")); + logger->releaseLock(); + return s; + } + /// @brief Returns command line arguments (pointer) provided to easylogging++ + static inline const el::base::utils::CommandLineArgs* commandLineArgs(void) { + return ELPP->commandLineArgs(); + } + /// @brief Installs user defined format specifier and handler + static inline void installCustomFormatSpecifier(const CustomFormatSpecifier& customFormatSpecifier) { + ELPP->installCustomFormatSpecifier(customFormatSpecifier); + } + /// @brief Uninstalls user defined format specifier and handler + static inline bool uninstallCustomFormatSpecifier(const char* formatSpecifier) { + return ELPP->uninstallCustomFormatSpecifier(formatSpecifier); + } + /// @brief Returns true if custom format specifier is installed + static inline bool hasCustomFormatSpecifier(const char* formatSpecifier) { + return ELPP->hasCustomFormatSpecifier(formatSpecifier); + } + static inline void validateFileRolling(Logger* logger, Level level) { + if (logger == nullptr) return; + logger->m_typedConfigurations->validateFileRolling(level, ELPP->preRollOutCallback()); + } +}; +/// @brief Static helpers to deal with loggers and their configurations +class Loggers : base::StaticClass { +public: + /// @brief Gets existing or registers new logger + static inline Logger* getLogger(const std::string& identity, bool registerIfNotAvailable = true) { + base::threading::ScopedLock scopedLock(ELPP->lock()); + return ELPP->registeredLoggers()->get(identity, registerIfNotAvailable); + } + /// @brief Unregisters logger - use it only when you know what you are doing, you may unregister + /// loggers initialized / used by third-party libs. + static inline bool unregisterLogger(const std::string& identity) { + base::threading::ScopedLock scopedLock(ELPP->lock()); + return ELPP->registeredLoggers()->remove(identity); + } + /// @brief Whether or not logger with id is registered + static inline bool hasLogger(const std::string& identity) { + base::threading::ScopedLock scopedLock(ELPP->lock()); + return ELPP->registeredLoggers()->has(identity); + } + /// @brief Reconfigures specified logger with new configurations + static inline Logger* reconfigureLogger(Logger* logger, const Configurations& configurations) { + if (!logger) return nullptr; + logger->configure(configurations); + return logger; + } + /// @brief Reconfigures logger with new configurations after looking it up using identity + static inline Logger* reconfigureLogger(const std::string& identity, const Configurations& configurations) { + return Loggers::reconfigureLogger(Loggers::getLogger(identity), configurations); + } + /// @brief Reconfigures logger's single configuration + static inline Logger* reconfigureLogger(const std::string& identity, ConfigurationType configurationType, + const std::string& value) { + Logger* logger = Loggers::getLogger(identity); + if (logger == nullptr) { + return nullptr; + } + logger->configurations()->set(Level::Global, configurationType, value); + logger->reconfigure(); + return logger; + } + /// @brief Reconfigures all the existing loggers with new configurations + static inline void reconfigureAllLoggers(const Configurations& configurations) { + for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin(); + it != ELPP->registeredLoggers()->end(); ++it) { + Loggers::reconfigureLogger(it->second, configurations); + } + } + /// @brief Reconfigures single configuration for all the loggers + static inline void reconfigureAllLoggers(ConfigurationType configurationType, const std::string& value) { + reconfigureAllLoggers(Level::Global, configurationType, value); + } + /// @brief Reconfigures single configuration for all the loggers for specified level + static inline void reconfigureAllLoggers(Level level, ConfigurationType configurationType, + const std::string& value) { + for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->begin(); + it != ELPP->registeredLoggers()->end(); ++it) { + Logger* logger = it->second; + logger->configurations()->set(level, configurationType, value); + logger->reconfigure(); + } + } + /// @brief Sets default configurations. This configuration is used for future (and conditionally for existing) loggers + static inline void setDefaultConfigurations(const Configurations& configurations, bool reconfigureExistingLoggers = false) { + ELPP->registeredLoggers()->setDefaultConfigurations(configurations); + if (reconfigureExistingLoggers) { + Loggers::reconfigureAllLoggers(configurations); + } + } + /// @brief Returns current default + static inline const Configurations* defaultConfigurations(void) { + return ELPP->registeredLoggers()->defaultConfigurations(); + } + /// @brief Returns log stream reference pointer if needed by user + static inline const base::LogStreamsReferenceMap* logStreamsReference(void) { + return ELPP->registeredLoggers()->logStreamsReference(); + } + /// @brief Default typed configuration based on existing defaultConf + static base::TypedConfigurations defaultTypedConfigurations(void) { + return base::TypedConfigurations( + ELPP->registeredLoggers()->defaultConfigurations(), + ELPP->registeredLoggers()->logStreamsReference()); + } + /// @brief Populates all logger IDs in current repository. + /// @param [out] targetList List of fill up. + static inline std::vector* populateAllLoggerIds(std::vector* targetList) { + targetList->clear(); + for (base::RegisteredLoggers::iterator it = ELPP->registeredLoggers()->list().begin(); + it != ELPP->registeredLoggers()->list().end(); ++it) { + targetList->push_back(it->first); + } + return targetList; + } + /// @brief Sets configurations from global configuration file. + static void configureFromGlobal(const char* globalConfigurationFilePath) { + std::ifstream gcfStream(globalConfigurationFilePath, std::ifstream::in); + ELPP_ASSERT(gcfStream.is_open(), "Unable to open global configuration file [" << globalConfigurationFilePath + << "] for parsing."); + std::string line = std::string(); + std::stringstream ss; + Logger* logger = nullptr; + auto configure = [&](void) { + ELPP_INTERNAL_INFO(8, "Configuring logger: '" << logger->id() << "' with configurations \n" << ss.str() + << "\n--------------"); + Configurations c; + c.parseFromText(ss.str()); + logger->configure(c); + }; + while (gcfStream.good()) { + std::getline(gcfStream, line); + ELPP_INTERNAL_INFO(1, "Parsing line: " << line); + base::utils::Str::trim(line); + if (Configurations::Parser::isComment(line)) continue; + Configurations::Parser::ignoreComments(&line); + base::utils::Str::trim(line); + if (line.size() > 2 && base::utils::Str::startsWith(line, std::string(base::consts::kConfigurationLoggerId))) { + if (!ss.str().empty() && logger != nullptr) { + configure(); + } + ss.str(std::string("")); + line = line.substr(2); + base::utils::Str::trim(line); + if (line.size() > 1) { + ELPP_INTERNAL_INFO(1, "Getting logger: '" << line << "'"); + logger = getLogger(line); + } + } else { + ss << line << "\n"; + } + } + if (!ss.str().empty() && logger != nullptr) { + configure(); + } + } + /// @brief Configures loggers using command line arg. Ensure you have already set command line args, + /// @return False if invalid argument or argument with no value provided, true if attempted to configure logger. + /// If true is returned that does not mean it has been configured successfully, it only means that it + /// has attempeted to configure logger using configuration file provided in argument + static inline bool configureFromArg(const char* argKey) { +#if defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS) + ELPP_UNUSED(argKey); +#else + if (!Helpers::commandLineArgs()->hasParamWithValue(argKey)) { + return false; + } + configureFromGlobal(Helpers::commandLineArgs()->getParamValue(argKey)); +#endif // defined(ELPP_DISABLE_CONFIGURATION_FROM_PROGRAM_ARGS) + return true; + } + /// @brief Flushes all loggers for all levels - Be careful if you dont know how many loggers are registered + static inline void flushAll(void) { + ELPP->registeredLoggers()->flushAll(); + } + /// @brief Adds logging flag used internally. + static inline void addFlag(LoggingFlag flag) { + ELPP->addFlag(flag); + } + /// @brief Removes logging flag used internally. + static inline void removeFlag(LoggingFlag flag) { + ELPP->removeFlag(flag); + } + /// @brief Determines whether or not certain flag is active + static inline bool hasFlag(LoggingFlag flag) { + return ELPP->hasFlag(flag); + } + /// @brief Adds flag and removes it when scope goes out + class ScopedAddFlag { + public: + ScopedAddFlag(LoggingFlag flag) : m_flag(flag) { Loggers::addFlag(m_flag); } + ~ScopedAddFlag(void) { Loggers::removeFlag(m_flag); } + private: + LoggingFlag m_flag; + }; + /// @brief Removes flag and add it when scope goes out + class ScopedRemoveFlag { + public: + ScopedRemoveFlag(LoggingFlag flag) : m_flag(flag) { Loggers::removeFlag(m_flag); } + ~ScopedRemoveFlag(void) { Loggers::addFlag(m_flag); } + private: + LoggingFlag m_flag; + }; + /// @brief Sets hierarchy for logging. Needs to enable logging flag (HierarchicalLogging) + static inline void setLoggingLevel(Level level) { + ELPP->setLoggingLevel(level); + } + /// @brief Sets verbose level on the fly + static inline void setVerboseLevel(base::type::VerboseLevel level) { + ELPP->vRegistry()->setLevel(level); + } + /// @brief Gets current verbose level + static inline base::type::VerboseLevel verboseLevel(void) { + return ELPP->vRegistry()->level(); + } + /// @brief Sets vmodules as specified (on the fly) + static inline void setVModules(const char* modules) { + if (ELPP->vRegistry()->vModulesEnabled()) { + ELPP->vRegistry()->setModules(modules); + } + } + /// @brief Clears vmodules + static inline void clearVModules(void) { + ELPP->vRegistry()->clearModules(); + } +}; +class VersionInfo : base::StaticClass { +public: + /// @brief Current version number + static inline const std::string version(void) { return std::string("9.80"); } + /// @brief Release date of current version + static inline const std::string releaseDate(void) { return std::string("08-01-2015 0850hrs"); } +}; +} // namespace el +#undef VLOG_IS_ON +/// @brief Determines whether verbose logging is on for specified level current file. +#define VLOG_IS_ON(verboseLevel) (ELPP->vRegistry()->allowed(verboseLevel, __FILE__)) +#undef TIMED_BLOCK +#undef TIMED_SCOPE +#undef TIMED_FUNC +#undef ELPP_MIN_UNIT +#if defined(ELPP_PERFORMANCE_MICROSECONDS) +# define ELPP_MIN_UNIT el::base::TimestampUnit::Microsecond +#else +# define ELPP_MIN_UNIT el::base::TimestampUnit::Millisecond +#endif // (defined(ELPP_PERFORMANCE_MICROSECONDS)) +/// @brief Performance tracked scope. Performance gets written when goes out of scope using +/// 'performance' logger. +/// +/// @detail Please note in order to check the performance at a certain time you can use obj.checkpoint(); +/// @see el::base::PerformanceTracker +/// @see el::base::PerformanceTracker::checkpoint +// Note: Do not surround this definition with null macro because of obj instance +#define TIMED_SCOPE(obj, blockname) el::base::PerformanceTracker obj(blockname, ELPP_MIN_UNIT) +#define TIMED_BLOCK(obj, blockName) for (struct { int i; el::base::PerformanceTracker timer; } obj = { 0, \ + el::base::PerformanceTracker(blockName, ELPP_MIN_UNIT) }; obj.i < 1; ++obj.i) +/// @brief Performance tracked function. Performance gets written when goes out of scope using +/// 'performance' logger. +/// +/// @detail Please note in order to check the performance at a certain time you can use obj.checkpoint(); +/// @see el::base::PerformanceTracker +/// @see el::base::PerformanceTracker::checkpoint +#define TIMED_FUNC(obj) TIMED_SCOPE(obj, ELPP_FUNC) +#undef PERFORMANCE_CHECKPOINT +#undef PERFORMANCE_CHECKPOINT_WITH_ID +#define PERFORMANCE_CHECKPOINT(obj) obj.checkpoint(std::string(), __FILE__, __LINE__, ELPP_FUNC) +#define PERFORMANCE_CHECKPOINT_WITH_ID(obj, id) obj.checkpoint(id, __FILE__, __LINE__, ELPP_FUNC) +#undef ELPP_COUNTER +#undef ELPP_COUNTER_POS +/// @brief Gets hit counter for file/line +#define ELPP_COUNTER (ELPP->hitCounters()->getCounter(__FILE__, __LINE__)) +/// @brief Gets hit counter position for file/line, -1 if not registered yet +#define ELPP_COUNTER_POS (ELPP_COUNTER == nullptr ? -1 : ELPP_COUNTER->hitCounts()) +// Undef levels to support LOG(LEVEL) +#undef INFO +#undef WARNING +#undef DEBUG +#undef ERROR +#undef FATAL +#undef TRACE +#undef VERBOSE +// Undef existing +#undef CINFO +#undef CWARNING +#undef CDEBUG +#undef CFATAL +#undef CERROR +#undef CTRACE +#undef CVERBOSE +#undef CINFO_IF +#undef CWARNING_IF +#undef CDEBUG_IF +#undef CERROR_IF +#undef CFATAL_IF +#undef CTRACE_IF +#undef CVERBOSE_IF +#undef CINFO_EVERY_N +#undef CWARNING_EVERY_N +#undef CDEBUG_EVERY_N +#undef CERROR_EVERY_N +#undef CFATAL_EVERY_N +#undef CTRACE_EVERY_N +#undef CVERBOSE_EVERY_N +#undef CINFO_AFTER_N +#undef CWARNING_AFTER_N +#undef CDEBUG_AFTER_N +#undef CERROR_AFTER_N +#undef CFATAL_AFTER_N +#undef CTRACE_AFTER_N +#undef CVERBOSE_AFTER_N +#undef CINFO_N_TIMES +#undef CWARNING_N_TIMES +#undef CDEBUG_N_TIMES +#undef CERROR_N_TIMES +#undef CFATAL_N_TIMES +#undef CTRACE_N_TIMES +#undef CVERBOSE_N_TIMES +// Normal logs +#if ELPP_INFO_LOG +# define CINFO(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE(writer, dispatchAction, ...) ELPP_WRITE_LOG(writer, el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE(writer, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE(writer, vlevel, dispatchAction, ...) if (VLOG_IS_ON(vlevel)) writer(\ + el::Level::Verbose, __FILE__, __LINE__, ELPP_FUNC, dispatchAction, vlevel).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#else +# define CVERBOSE(writer, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// Conditional logs +#if ELPP_INFO_LOG +# define CINFO_IF(writer, condition_, dispatchAction, ...) \ + ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING_IF(writer, condition_, dispatchAction, ...)\ + ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG_IF(writer, condition_, dispatchAction, ...)\ + ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR_IF(writer, condition_, dispatchAction, ...)\ + ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL_IF(writer, condition_, dispatchAction, ...)\ + ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE_IF(writer, condition_, dispatchAction, ...)\ + ELPP_WRITE_LOG_IF(writer, (condition_), el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE_IF(writer, condition_, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE_IF(writer, condition_, vlevel, dispatchAction, ...) if (VLOG_IS_ON(vlevel) && (condition_)) writer( \ + el::Level::Verbose, __FILE__, __LINE__, ELPP_FUNC, dispatchAction, vlevel).construct(el_getVALength(__VA_ARGS__), __VA_ARGS__) +#else +# define CVERBOSE_IF(writer, condition_, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// Occasional logs +#if ELPP_INFO_LOG +# define CINFO_EVERY_N(writer, occasion, dispatchAction, ...)\ + ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING_EVERY_N(writer, occasion, dispatchAction, ...)\ + ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG_EVERY_N(writer, occasion, dispatchAction, ...)\ + ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR_EVERY_N(writer, occasion, dispatchAction, ...)\ + ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL_EVERY_N(writer, occasion, dispatchAction, ...)\ + ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE_EVERY_N(writer, occasion, dispatchAction, ...)\ + ELPP_WRITE_LOG_EVERY_N(writer, occasion, el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE_EVERY_N(writer, occasion, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE_EVERY_N(writer, occasion, vlevel, dispatchAction, ...)\ + CVERBOSE_IF(writer, ELPP->validateEveryNCounter(__FILE__, __LINE__, occasion), vlevel, dispatchAction, __VA_ARGS__) +#else +# define CVERBOSE_EVERY_N(writer, occasion, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// After N logs +#if ELPP_INFO_LOG +# define CINFO_AFTER_N(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING_AFTER_N(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG_AFTER_N(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR_AFTER_N(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL_AFTER_N(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE_AFTER_N(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_AFTER_N(writer, n, el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE_AFTER_N(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE_AFTER_N(writer, n, vlevel, dispatchAction, ...)\ + CVERBOSE_IF(writer, ELPP->validateAfterNCounter(__FILE__, __LINE__, n), vlevel, dispatchAction, __VA_ARGS__) +#else +# define CVERBOSE_AFTER_N(writer, n, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// N Times logs +#if ELPP_INFO_LOG +# define CINFO_N_TIMES(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Info, dispatchAction, __VA_ARGS__) +#else +# define CINFO_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_INFO_LOG +#if ELPP_WARNING_LOG +# define CWARNING_N_TIMES(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Warning, dispatchAction, __VA_ARGS__) +#else +# define CWARNING_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_WARNING_LOG +#if ELPP_DEBUG_LOG +# define CDEBUG_N_TIMES(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Debug, dispatchAction, __VA_ARGS__) +#else +# define CDEBUG_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_DEBUG_LOG +#if ELPP_ERROR_LOG +# define CERROR_N_TIMES(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Error, dispatchAction, __VA_ARGS__) +#else +# define CERROR_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_ERROR_LOG +#if ELPP_FATAL_LOG +# define CFATAL_N_TIMES(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Fatal, dispatchAction, __VA_ARGS__) +#else +# define CFATAL_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_FATAL_LOG +#if ELPP_TRACE_LOG +# define CTRACE_N_TIMES(writer, n, dispatchAction, ...)\ + ELPP_WRITE_LOG_N_TIMES(writer, n, el::Level::Trace, dispatchAction, __VA_ARGS__) +#else +# define CTRACE_N_TIMES(writer, n, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_TRACE_LOG +#if ELPP_VERBOSE_LOG +# define CVERBOSE_N_TIMES(writer, n, vlevel, dispatchAction, ...)\ + CVERBOSE_IF(writer, ELPP->validateNTimesCounter(__FILE__, __LINE__, n), vlevel, dispatchAction, __VA_ARGS__) +#else +# define CVERBOSE_N_TIMES(writer, n, vlevel, dispatchAction, ...) el::base::NullWriter() +#endif // ELPP_VERBOSE_LOG +// +// Custom Loggers - Requires (level, dispatchAction, loggerId/s) +// +// undef existing +#undef CLOG +#undef CLOG_VERBOSE +#undef CVLOG +#undef CLOG_IF +#undef CLOG_VERBOSE_IF +#undef CVLOG_IF +#undef CLOG_EVERY_N +#undef CVLOG_EVERY_N +#undef CLOG_AFTER_N +#undef CVLOG_AFTER_N +#undef CLOG_N_TIMES +#undef CVLOG_N_TIMES +// Normal logs +#define CLOG(LEVEL, ...)\ + C##LEVEL(el::base::Writer, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG(vlevel, ...) CVERBOSE(el::base::Writer, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +// Conditional logs +#define CLOG_IF(condition, LEVEL, ...)\ + C##LEVEL##_IF(el::base::Writer, condition, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG_IF(condition, vlevel, ...)\ + CVERBOSE_IF(el::base::Writer, condition, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +// Hit counts based logs +#define CLOG_EVERY_N(n, LEVEL, ...)\ + C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG_EVERY_N(n, vlevel, ...)\ + CVERBOSE_EVERY_N(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CLOG_AFTER_N(n, LEVEL, ...)\ + C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG_AFTER_N(n, vlevel, ...)\ + CVERBOSE_AFTER_N(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CLOG_N_TIMES(n, LEVEL, ...)\ + C##LEVEL##_N_TIMES(el::base::Writer, n, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CVLOG_N_TIMES(n, vlevel, ...)\ + CVERBOSE_N_TIMES(el::base::Writer, n, vlevel, el::base::DispatchAction::NormalLog, __VA_ARGS__) +// +// Default Loggers macro using CLOG(), CLOG_VERBOSE() and CVLOG() macros +// +// undef existing +#undef LOG +#undef VLOG +#undef LOG_IF +#undef VLOG_IF +#undef LOG_EVERY_N +#undef VLOG_EVERY_N +#undef LOG_AFTER_N +#undef VLOG_AFTER_N +#undef LOG_N_TIMES +#undef VLOG_N_TIMES +#undef ELPP_CURR_FILE_LOGGER_ID +#if defined(ELPP_DEFAULT_LOGGER) +# define ELPP_CURR_FILE_LOGGER_ID ELPP_DEFAULT_LOGGER +#else +# define ELPP_CURR_FILE_LOGGER_ID el::base::consts::kDefaultLoggerId +#endif +#undef ELPP_TRACE +#define ELPP_TRACE CLOG(TRACE, ELPP_CURR_FILE_LOGGER_ID) +// Normal logs +#define LOG(LEVEL) CLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG(vlevel) CVLOG(vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Conditional logs +#define LOG_IF(condition, LEVEL) CLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG_IF(condition, vlevel) CVLOG_IF(condition, vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Hit counts based logs +#define LOG_EVERY_N(n, LEVEL) CLOG_EVERY_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG_EVERY_N(n, vlevel) CVLOG_EVERY_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#define LOG_AFTER_N(n, LEVEL) CLOG_AFTER_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG_AFTER_N(n, vlevel) CVLOG_AFTER_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#define LOG_N_TIMES(n, LEVEL) CLOG_N_TIMES(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define VLOG_N_TIMES(n, vlevel) CVLOG_N_TIMES(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Generic PLOG() +#undef CPLOG +#undef CPLOG_IF +#undef PLOG +#undef PLOG_IF +#undef DCPLOG +#undef DCPLOG_IF +#undef DPLOG +#undef DPLOG_IF +#define CPLOG(LEVEL, ...)\ + C##LEVEL(el::base::PErrorWriter, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define CPLOG_IF(condition, LEVEL, ...)\ + C##LEVEL##_IF(el::base::PErrorWriter, condition, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define DCPLOG(LEVEL, ...)\ + if (ELPP_DEBUG_LOG) C##LEVEL(el::base::PErrorWriter, el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define DCPLOG_IF(condition, LEVEL, ...)\ + C##LEVEL##_IF(el::base::PErrorWriter, (ELPP_DEBUG_LOG) && (condition), el::base::DispatchAction::NormalLog, __VA_ARGS__) +#define PLOG(LEVEL) CPLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define PLOG_IF(condition, LEVEL) CPLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DPLOG(LEVEL) DCPLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DPLOG_IF(condition, LEVEL) DCPLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +// Generic SYSLOG() +#undef CSYSLOG +#undef CSYSLOG_IF +#undef CSYSLOG_EVERY_N +#undef CSYSLOG_AFTER_N +#undef CSYSLOG_N_TIMES +#undef SYSLOG +#undef SYSLOG_IF +#undef SYSLOG_EVERY_N +#undef SYSLOG_AFTER_N +#undef SYSLOG_N_TIMES +#undef DCSYSLOG +#undef DCSYSLOG_IF +#undef DCSYSLOG_EVERY_N +#undef DCSYSLOG_AFTER_N +#undef DCSYSLOG_N_TIMES +#undef DSYSLOG +#undef DSYSLOG_IF +#undef DSYSLOG_EVERY_N +#undef DSYSLOG_AFTER_N +#undef DSYSLOG_N_TIMES +#if defined(ELPP_SYSLOG) +# define CSYSLOG(LEVEL, ...)\ + C##LEVEL(el::base::Writer, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_IF(condition, LEVEL, ...)\ + C##LEVEL##_IF(el::base::Writer, condition, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_EVERY_N(n, LEVEL, ...) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_AFTER_N(n, LEVEL, ...) C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define CSYSLOG_N_TIMES(n, LEVEL, ...) C##LEVEL##_N_TIMES(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define SYSLOG(LEVEL) CSYSLOG(LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_IF(condition, LEVEL) CSYSLOG_IF(condition, LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_EVERY_N(n, LEVEL) CSYSLOG_EVERY_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_AFTER_N(n, LEVEL) CSYSLOG_AFTER_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define SYSLOG_N_TIMES(n, LEVEL) CSYSLOG_N_TIMES(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define DCSYSLOG(LEVEL, ...) if (ELPP_DEBUG_LOG) C##LEVEL(el::base::Writer, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DCSYSLOG_IF(condition, LEVEL, ...)\ + C##LEVEL##_IF(el::base::Writer, (ELPP_DEBUG_LOG) && (condition), el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DCSYSLOG_EVERY_N(n, LEVEL, ...)\ + if (ELPP_DEBUG_LOG) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DCSYSLOG_AFTER_N(n, LEVEL, ...)\ + if (ELPP_DEBUG_LOG) C##LEVEL##_AFTER_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DCSYSLOG_N_TIMES(n, LEVEL, ...)\ + if (ELPP_DEBUG_LOG) C##LEVEL##_EVERY_N(el::base::Writer, n, el::base::DispatchAction::SysLog, __VA_ARGS__) +# define DSYSLOG(LEVEL) DCSYSLOG(LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_IF(condition, LEVEL) DCSYSLOG_IF(condition, LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_EVERY_N(n, LEVEL) DCSYSLOG_EVERY_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_AFTER_N(n, LEVEL) DCSYSLOG_AFTER_N(n, LEVEL, el::base::consts::kSysLogLoggerId) +# define DSYSLOG_N_TIMES(n, LEVEL) DCSYSLOG_N_TIMES(n, LEVEL, el::base::consts::kSysLogLoggerId) +#else +# define CSYSLOG(LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_IF(condition, LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_EVERY_N(n, LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_AFTER_N(n, LEVEL, ...) el::base::NullWriter() +# define CSYSLOG_N_TIMES(n, LEVEL, ...) el::base::NullWriter() +# define SYSLOG(LEVEL) el::base::NullWriter() +# define SYSLOG_IF(condition, LEVEL) el::base::NullWriter() +# define SYSLOG_EVERY_N(n, LEVEL) el::base::NullWriter() +# define SYSLOG_AFTER_N(n, LEVEL) el::base::NullWriter() +# define SYSLOG_N_TIMES(n, LEVEL) el::base::NullWriter() +# define DCSYSLOG(LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_IF(condition, LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_EVERY_N(n, LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_AFTER_N(n, LEVEL, ...) el::base::NullWriter() +# define DCSYSLOG_N_TIMES(n, LEVEL, ...) el::base::NullWriter() +# define DSYSLOG(LEVEL) el::base::NullWriter() +# define DSYSLOG_IF(condition, LEVEL) el::base::NullWriter() +# define DSYSLOG_EVERY_N(n, LEVEL) el::base::NullWriter() +# define DSYSLOG_AFTER_N(n, LEVEL) el::base::NullWriter() +# define DSYSLOG_N_TIMES(n, LEVEL) el::base::NullWriter() +#endif // defined(ELPP_SYSLOG) +// +// Custom Debug Only Loggers - Requires (level, loggerId/s) +// +// undef existing +#undef DCLOG +#undef DCVLOG +#undef DCLOG_IF +#undef DCVLOG_IF +#undef DCLOG_EVERY_N +#undef DCVLOG_EVERY_N +#undef DCLOG_AFTER_N +#undef DCVLOG_AFTER_N +#undef DCLOG_N_TIMES +#undef DCVLOG_N_TIMES +// Normal logs +#define DCLOG(LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG(LEVEL, __VA_ARGS__) +#define DCLOG_VERBOSE(vlevel, ...) if (ELPP_DEBUG_LOG) CLOG_VERBOSE(vlevel, __VA_ARGS__) +#define DCVLOG(vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG(vlevel, __VA_ARGS__) +// Conditional logs +#define DCLOG_IF(condition, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_IF(condition, LEVEL, __VA_ARGS__) +#define DCVLOG_IF(condition, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_IF(condition, vlevel, __VA_ARGS__) +// Hit counts based logs +#define DCLOG_EVERY_N(n, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_EVERY_N(n, LEVEL, __VA_ARGS__) +#define DCVLOG_EVERY_N(n, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_EVERY_N(n, vlevel, __VA_ARGS__) +#define DCLOG_AFTER_N(n, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_AFTER_N(n, LEVEL, __VA_ARGS__) +#define DCVLOG_AFTER_N(n, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_AFTER_N(n, vlevel, __VA_ARGS__) +#define DCLOG_N_TIMES(n, LEVEL, ...) if (ELPP_DEBUG_LOG) CLOG_N_TIMES(n, LEVEL, __VA_ARGS__) +#define DCVLOG_N_TIMES(n, vlevel, ...) if (ELPP_DEBUG_LOG) CVLOG_N_TIMES(n, vlevel, __VA_ARGS__) +// +// Default Debug Only Loggers macro using CLOG(), CLOG_VERBOSE() and CVLOG() macros +// +// undef existing +#undef DLOG +#undef DVLOG +#undef DLOG_IF +#undef DVLOG_IF +#undef DLOG_EVERY_N +#undef DVLOG_EVERY_N +#undef DLOG_AFTER_N +#undef DVLOG_AFTER_N +#undef DLOG_N_TIMES +#undef DVLOG_N_TIMES +// Normal logs +#define DLOG(LEVEL) DCLOG(LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG(vlevel) DCVLOG(vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Conditional logs +#define DLOG_IF(condition, LEVEL) DCLOG_IF(condition, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG_IF(condition, vlevel) DCVLOG_IF(condition, vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Hit counts based logs +#define DLOG_EVERY_N(n, LEVEL) DCLOG_EVERY_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG_EVERY_N(n, vlevel) DCVLOG_EVERY_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#define DLOG_AFTER_N(n, LEVEL) DCLOG_AFTER_N(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG_AFTER_N(n, vlevel) DCVLOG_AFTER_N(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +#define DLOG_N_TIMES(n, LEVEL) DCLOG_N_TIMES(n, LEVEL, ELPP_CURR_FILE_LOGGER_ID) +#define DVLOG_N_TIMES(n, vlevel) DCVLOG_N_TIMES(n, vlevel, ELPP_CURR_FILE_LOGGER_ID) +// Check macros +#undef CCHECK +#undef CPCHECK +#undef CCHECK_EQ +#undef CCHECK_NE +#undef CCHECK_LT +#undef CCHECK_GT +#undef CCHECK_LE +#undef CCHECK_GE +#undef CCHECK_BOUNDS +#undef CCHECK_NOTNULL +#undef CCHECK_STRCASEEQ +#undef CCHECK_STRCASENE +#undef CHECK +#undef PCHECK +#undef CHECK_EQ +#undef CHECK_NE +#undef CHECK_LT +#undef CHECK_GT +#undef CHECK_LE +#undef CHECK_GE +#undef CHECK_BOUNDS +#undef CHECK_NOTNULL +#undef CHECK_STRCASEEQ +#undef CHECK_STRCASENE +#define CCHECK(condition, ...) CLOG_IF(!(condition), FATAL, __VA_ARGS__) << "Check failed: [" << #condition << "] " +#define CPCHECK(condition, ...) CPLOG_IF(!(condition), FATAL, __VA_ARGS__) << "Check failed: [" << #condition << "] " +#define CHECK(condition) CCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) +#define PCHECK(condition) CPCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) +#define CCHECK_EQ(a, b, ...) CCHECK(a == b, __VA_ARGS__) +#define CCHECK_NE(a, b, ...) CCHECK(a != b, __VA_ARGS__) +#define CCHECK_LT(a, b, ...) CCHECK(a < b, __VA_ARGS__) +#define CCHECK_GT(a, b, ...) CCHECK(a > b, __VA_ARGS__) +#define CCHECK_LE(a, b, ...) CCHECK(a <= b, __VA_ARGS__) +#define CCHECK_GE(a, b, ...) CCHECK(a >= b, __VA_ARGS__) +#define CCHECK_BOUNDS(val, min, max, ...) CCHECK(val >= min && val <= max, __VA_ARGS__) +#define CHECK_EQ(a, b) CCHECK_EQ(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_NE(a, b) CCHECK_NE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_LT(a, b) CCHECK_LT(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_GT(a, b) CCHECK_GT(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_LE(a, b) CCHECK_LE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_GE(a, b) CCHECK_GE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_BOUNDS(val, min, max) CCHECK_BOUNDS(val, min, max, ELPP_CURR_FILE_LOGGER_ID) +namespace el { +namespace base { +namespace utils { +template +static T* checkNotNull(T* ptr, const char* name, const char* loggers, ...) { + CLOG_IF(ptr == nullptr, FATAL, loggers) << "Check failed: [" << name << " != nullptr]"; + return ptr; +} +} // namespace utils +} // namespace base +} // namespace el +#define CCHECK_NOTNULL(ptr, ...) el::base::utils::checkNotNull(ptr, #ptr, __VA_ARGS__) +#define CCHECK_STREQ(str1, str2, ...) CLOG_IF(!el::base::utils::Str::cStringEq(str1, str2), FATAL, __VA_ARGS__) \ + << "Check failed: [" << #str1 << " == " << #str2 << "] " +#define CCHECK_STRNE(str1, str2, ...) CLOG_IF(el::base::utils::Str::cStringEq(str1, str2), FATAL, __VA_ARGS__) \ + << "Check failed: [" << #str1 << " != " << #str2 << "] " +#define CCHECK_STRCASEEQ(str1, str2, ...) CLOG_IF(!el::base::utils::Str::cStringCaseEq(str1, str2), FATAL, __VA_ARGS__) \ + << "Check failed: [" << #str1 << " == " << #str2 << "] " +#define CCHECK_STRCASENE(str1, str2, ...) CLOG_IF(el::base::utils::Str::cStringCaseEq(str1, str2), FATAL, __VA_ARGS__) \ + << "Check failed: [" << #str1 << " != " << #str2 << "] " +#define CHECK_NOTNULL(ptr) CCHECK_NOTNULL(ptr, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_STREQ(str1, str2) CCHECK_STREQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_STRNE(str1, str2) CCHECK_STRNE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_STRCASEEQ(str1, str2) CCHECK_STRCASEEQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define CHECK_STRCASENE(str1, str2) CCHECK_STRCASENE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#undef DCCHECK +#undef DCCHECK_EQ +#undef DCCHECK_NE +#undef DCCHECK_LT +#undef DCCHECK_GT +#undef DCCHECK_LE +#undef DCCHECK_GE +#undef DCCHECK_BOUNDS +#undef DCCHECK_NOTNULL +#undef DCCHECK_STRCASEEQ +#undef DCCHECK_STRCASENE +#undef DCPCHECK +#undef DCHECK +#undef DCHECK_EQ +#undef DCHECK_NE +#undef DCHECK_LT +#undef DCHECK_GT +#undef DCHECK_LE +#undef DCHECK_GE +#undef DCHECK_BOUNDS_ +#undef DCHECK_NOTNULL +#undef DCHECK_STRCASEEQ +#undef DCHECK_STRCASENE +#undef DPCHECK +#define DCCHECK(condition, ...) if (ELPP_DEBUG_LOG) CCHECK(condition, __VA_ARGS__) +#define DCCHECK_EQ(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_EQ(a, b, __VA_ARGS__) +#define DCCHECK_NE(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_NE(a, b, __VA_ARGS__) +#define DCCHECK_LT(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_LT(a, b, __VA_ARGS__) +#define DCCHECK_GT(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_GT(a, b, __VA_ARGS__) +#define DCCHECK_LE(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_LE(a, b, __VA_ARGS__) +#define DCCHECK_GE(a, b, ...) if (ELPP_DEBUG_LOG) CCHECK_GE(a, b, __VA_ARGS__) +#define DCCHECK_BOUNDS(val, min, max, ...) if (ELPP_DEBUG_LOG) CCHECK_BOUNDS(val, min, max, __VA_ARGS__) +#define DCCHECK_NOTNULL(ptr, ...) if (ELPP_DEBUG_LOG) CCHECK_NOTNULL(ptr, __VA_ARGS__) +#define DCCHECK_STREQ(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STREQ(str1, str2, __VA_ARGS__) +#define DCCHECK_STRNE(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STRNE(str1, str2, __VA_ARGS__) +#define DCCHECK_STRCASEEQ(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STRCASEEQ(str1, str2, __VA_ARGS__) +#define DCCHECK_STRCASENE(str1, str2, ...) if (ELPP_DEBUG_LOG) CCHECK_STRCASENE(str1, str2, __VA_ARGS__) +#define DCPCHECK(condition, ...) if (ELPP_DEBUG_LOG) CPCHECK(condition, __VA_ARGS__) +#define DCHECK(condition) DCCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_EQ(a, b) DCCHECK_EQ(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_NE(a, b) DCCHECK_NE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_LT(a, b) DCCHECK_LT(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_GT(a, b) DCCHECK_GT(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_LE(a, b) DCCHECK_LE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_GE(a, b) DCCHECK_GE(a, b, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_BOUNDS(val, min, max) DCCHECK_BOUNDS(val, min, max, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_NOTNULL(ptr) DCCHECK_NOTNULL(ptr, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_STREQ(str1, str2) DCCHECK_STREQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_STRNE(str1, str2) DCCHECK_STRNE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_STRCASEEQ(str1, str2) DCCHECK_STRCASEEQ(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define DCHECK_STRCASENE(str1, str2) DCCHECK_STRCASENE(str1, str2, ELPP_CURR_FILE_LOGGER_ID) +#define DPCHECK(condition) DCPCHECK(condition, ELPP_CURR_FILE_LOGGER_ID) +#if defined(ELPP_DISABLE_DEFAULT_CRASH_HANDLING) +# define ELPP_USE_DEF_CRASH_HANDLER false +#else +# define ELPP_USE_DEF_CRASH_HANDLER true +#endif // defined(ELPP_DISABLE_DEFAULT_CRASH_HANDLING) +#define ELPP_CRASH_HANDLER_INIT +#define ELPP_INIT_EASYLOGGINGPP(val)\ + ELPP_INITI_BASIC_DECLR\ + namespace el {\ + namespace base {\ + el::base::type::StoragePointer elStorage(val);\ + }\ + el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER);\ + } + +#if ELPP_ASYNC_LOGGING +# define INITIALIZE_EASYLOGGINGPP\ + ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()),\ + new el::base::AsyncDispatchWorker()))\ + +#else +# define INITIALIZE_EASYLOGGINGPP\ + ELPP_INIT_EASYLOGGINGPP(new el::base::Storage(el::LogBuilderPtr(new el::base::DefaultLogBuilder()))) +#endif // ELPP_ASYNC_LOGGING +#define INITIALIZE_NULL_EASYLOGGINGPP\ + ELPP_INITI_BASIC_DECLR\ + namespace el {\ + namespace base {\ + el::base::type::StoragePointer elStorage;\ + }\ + el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER);\ + } +// NOTE: no ELPP_INITI_BASIC_DECLR when sharing - causes double free corruption on external symbols +#define SHARE_EASYLOGGINGPP(initializedStorage)\ + namespace el {\ + namespace base {\ + el::base::type::StoragePointer elStorage(initializedStorage);\ + }\ + el::base::debug::CrashHandler elCrashHandler(ELPP_USE_DEF_CRASH_HANDLER);\ + } + +#if defined(ELPP_UNICODE) +# define START_EASYLOGGINGPP(argc, argv) el::Helpers::setArgs(argc, argv); std::locale::global(std::locale("")) +#else +# define START_EASYLOGGINGPP(argc, argv) el::Helpers::setArgs(argc, argv) +#endif // defined(ELPP_UNICODE) +#endif // EASYLOGGINGPP_H diff --git a/src/viewer.cpp b/src/viewer.cpp index 6d368d41..7ac53d9d 100644 --- a/src/viewer.cpp +++ b/src/viewer.cpp @@ -85,7 +85,7 @@ Viewer::Init() #endif // -- Clear background - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClearColor(0.2f, 0.2f, 0.2f, 1.0f); } void @@ -97,7 +97,7 @@ Viewer::Update() static float lastTime = glfwGetTime(); float currentTime = glfwGetTime(); - float deltaTime = float(currentTime - lastTime); + float deltaTime = float(currentTime - lastTime) * 0.01f; m_scene->Update(deltaTime, m_keyboard, *m_programAdvect); #ifdef TEST_SCENE From 67788870f4904cb09d4f5f1b700e9f76acc47e15 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Wed, 30 Mar 2016 01:48:01 -0400 Subject: [PATCH 37/64] Added CFL condition. Fixed pressure computation. Fixed pressure gradient --- src/camera/camera.cpp | 2 +- src/fluidSolver/SPHGrid.cpp | 27 +++++++++++++------ src/fluidSolver/SPHGrid.h | 5 ++-- src/fluidSolver/SPHKernels.h | 1 + src/fluidSolver/SPHSolver.cpp | 45 ++++++++++++++++++------------- src/fluidSolver/fluidParticle.cpp | 20 +++++++++----- src/fluidSolver/fluidSolver.h | 1 + src/main.cpp | 15 ++++++++++- src/mathConstants.h | 2 +- src/scene/scene.json | 6 ++--- src/viewer.cpp | 2 +- 11 files changed, 83 insertions(+), 43 deletions(-) diff --git a/src/camera/camera.cpp b/src/camera/camera.cpp index 233339b3..11f8264b 100644 --- a/src/camera/camera.cpp +++ b/src/camera/camera.cpp @@ -12,7 +12,7 @@ Camera::Camera( ) : m_width(width), m_height(height), m_fov(45), - m_eye(glm::vec3(10.0f, 10.0f, -20.0f)), + m_eye(glm::vec3(0.0f, 0.0f, -20.0f)), m_target(glm::vec3(0.0f)), m_up(glm::vec3(0.0f, 1.0f, 0.0f)), m_right(glm::vec3(1.0f, 0.0f, 0.0f)), diff --git a/src/fluidSolver/SPHGrid.cpp b/src/fluidSolver/SPHGrid.cpp index a6582694..c09c670b 100644 --- a/src/fluidSolver/SPHGrid.cpp +++ b/src/fluidSolver/SPHGrid.cpp @@ -19,17 +19,14 @@ SPHGrid::SPHGrid( m_depth = ceil((m_gridMax.z - m_gridMin.z) / m_cellSize); m_cells.resize(m_width * m_height * m_depth); - LOG(INFO) << "Number of particles: " << particles.size() << endl; - LOG(INFO) << "Grid dim: " << m_width << ", " << m_height << ", " << m_depth << endl; + // LOG(INFO) << "Number of particles: " << particles.size() << endl; + // LOG(INFO) << "Grid dim: " << m_width << ", " << m_height << ", " << m_depth << endl; } else { // Grid size is only 1 m_cells.resize(1); } - for(FluidParticle* p : particles) { - AddParticle(p); - } - + this->ResetGrid(particles); } void @@ -46,6 +43,16 @@ InitializeVdb() } +void +SPHGrid::ResetGrid( + const std::vector& particles + ) +{ + for(FluidParticle* p : particles) { + AddParticle(p); + } +} + // Add particle to grid based on its position void SPHGrid::AddParticle( @@ -62,7 +69,11 @@ SPHGrid::AddParticle( LOG(WARNING) << "Error adding particle " + idx << endl; return; } - m_cells[idx].push_back(particle); + + // If the particle isn't added to the cell yet, add it now + if (std::find(m_cells[idx].begin(), m_cells[idx].end(),particle) == m_cells[idx].end()) { + m_cells[idx].push_back(particle); + } } } @@ -171,7 +182,7 @@ SPHGrid::SearchNeighborsUniformGrid( int idx = this->GetCellIdx(i + cellCoord.x, j + cellCoord.y, k + cellCoord.z); glm::vec3 particlePosition = particle->Position(); for(FluidParticle* neighbor : m_cells[idx]) { - if (glm::distance(neighbor->Position(), particlePosition) < m_cellSize && + if (glm::distance(neighbor->Position(), particlePosition) < m_cellSize * 2 && neighbor != particle) { neighbors.push_back(neighbor); } diff --git a/src/fluidSolver/SPHGrid.h b/src/fluidSolver/SPHGrid.h index 8e2c1444..49251011 100644 --- a/src/fluidSolver/SPHGrid.h +++ b/src/fluidSolver/SPHGrid.h @@ -28,8 +28,9 @@ class SPHGrid const float cellSize, const bool useGrid // Use grid to speed up neighbor search ); - void AddParticle(FluidParticle* particle); - std::vector SearchNeighbors(FluidParticle*); + virtual void ResetGrid(const std::vector& particles); + virtual void AddParticle(FluidParticle* particle); + virtual std::vector SearchNeighbors(FluidParticle*); private: diff --git a/src/fluidSolver/SPHKernels.h b/src/fluidSolver/SPHKernels.h index a9af3834..a15ba2c6 100644 --- a/src/fluidSolver/SPHKernels.h +++ b/src/fluidSolver/SPHKernels.h @@ -4,6 +4,7 @@ #define _USE_MATH_DEFINES #include #include +#include inline float KernelPoly6(float x, float h) { diff --git a/src/fluidSolver/SPHSolver.cpp b/src/fluidSolver/SPHSolver.cpp index 2dd7dc80..242da9b6 100644 --- a/src/fluidSolver/SPHSolver.cpp +++ b/src/fluidSolver/SPHSolver.cpp @@ -21,6 +21,14 @@ SPHSolver::SPHSolver( m_restDensity(restDensity), m_timestep(timestep) { + m_grid = new SPHGrid( + m_particles, + -m_containerDim, + m_containerDim, + m_cellSize, + true + ); + srand(time(NULL)); } @@ -34,16 +42,7 @@ SPHSolver::Update( const float deltaT ) { - if (m_grid != nullptr) { - delete m_grid; - } - m_grid = new SPHGrid( - m_particles, - -m_containerDim, - m_containerDim, - m_cellSize, - true - ); + m_grid->ResetGrid(m_particles); std::vector neighbors; for (FluidParticle* p : m_particles) { @@ -73,7 +72,7 @@ SPHSolver::CalculateDensity( // -- Compute density over a kernel function of neighbors float density = 0.f; for (FluidParticle* neighbor : neighbors) { - float tempDensity = KernelPoly6(glm::distance(neighbor->Position(), particle->Position()), m_cellSize); + float tempDensity = KernelPoly6(glm::distance(neighbor->Position(), particle->Position()), m_cellSize * 2); density += tempDensity; } density = m_mass * density; @@ -85,10 +84,10 @@ SPHSolver::CalculatePressure( FluidParticle* particle ) { - float pressure = m_stiffness * (pow((particle->Density() / m_restDensity), 7.0f) - 1.0f); + float pressure = m_stiffness * (particle->Density() - m_restDensity); // Clamp from being negative - pressure = pressure < 0.0 ? 0.0 : pressure; + pressure = pressure < 0.0 ? 0 : pressure; particle->SetPressure(pressure); } @@ -101,19 +100,27 @@ SPHSolver::CalculatePressureForceField( // -- Compute pressure gradient glm::vec3 pressureGrad; for (FluidParticle* neighbor : neighbors) { - glm::vec3 r = neighbor->Position() - particle->Position(); - glm::vec3 kernelGrad = GradKernelSpiky(r, glm::distance(neighbor->Position(), particle->Position()), m_cellSize); - - glm::vec3 tempPressureForce = + glm::vec3 r = particle->Position() - neighbor->Position(); + float x = glm::distance(neighbor->Position(), particle->Position()); + // LOG(DEBUG) << "x: " << x << endl; + glm::vec3 kernelGrad = GradKernelSpiky(r, x, m_cellSize * 2); + // LOG(DEBUG) << "x: " << x << ", h: " << m_cellSize << endl; + // LOG(DEBUG) << "KernelGrad x: " << kernelGrad.x << ", y: " << kernelGrad.y << ", z: " << kernelGrad.z << endl; + + float tempPressureForce = ( particle->Pressure() / (pow(particle->Density(), 2.0f)) + neighbor->Pressure() / (pow(neighbor->Density(), 2.0f)) - ) * kernelGrad; - pressureGrad += tempPressureForce; + ); + // LOG(DEBUG) << "Temp pressure force: " << tempPressureForce << endl; + // LOG(DEBUG) << "Paricle pressure: " << particle->Pressure() << endl; + // LOG(DEBUG) << "Neighbor pressure: " << neighbor->Pressure() << endl; + pressureGrad += tempPressureForce * kernelGrad; } pressureGrad = pressureGrad * m_mass * m_mass; // LOG(DEBUG) << "Pressure x: " << pressureGrad.x << ", y: " << pressureGrad.y << ", z: " << pressureGrad.z << endl; particle->SetPressureForce(-pressureGrad); + particle->SetColor(glm::vec4(glm::abs(pressureGrad), 1.f)); } void diff --git a/src/fluidSolver/fluidParticle.cpp b/src/fluidSolver/fluidParticle.cpp index f8c4492d..570b88cd 100644 --- a/src/fluidSolver/fluidParticle.cpp +++ b/src/fluidSolver/fluidParticle.cpp @@ -22,12 +22,18 @@ FluidParticle::Update( const float deltaT ) { - glm::vec3 gravity(0.0f, GRAVITY, 0.0f); - m_vel = m_vel + (m_accel + m_pressureForce + gravity) * deltaT; - // cout << "Position b4: " << m_pos.y << endl; - // cout << "Vel: " << m_vel.y << endl; - // cout << "Accel: " << m_accel.y << endl; - // cout << "Pressure force: " << m_pressureForce.y << endl; + // @todo: HACK HERE! + float mass = 0.125; + float cellSize = 0.10001; + + glm::vec3 gravity(0.0f, mass * GRAVITY, 0.0f); + glm::vec3 totalForce = m_accel + m_pressureForce + gravity; + m_vel = m_vel + totalForce * deltaT; + + // -- Enforce CFL condition + float vMax = 0.4 * cellSize / deltaT; + if (glm::length(m_vel) > vMax) { + m_vel = glm::normalize(m_vel) * vMax; + } m_pos = m_pos + m_vel * deltaT; - // cout << "Position after: " << m_pos.y << endl; } diff --git a/src/fluidSolver/fluidSolver.h b/src/fluidSolver/fluidSolver.h index 33bdbba0..8863bbc5 100644 --- a/src/fluidSolver/fluidSolver.h +++ b/src/fluidSolver/fluidSolver.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include diff --git a/src/main.cpp b/src/main.cpp index 5285fa35..f59ac7ce 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -8,7 +8,7 @@ INITIALIZE_EASYLOGGINGPP -// #define UNIT_TEST +#define UNIT_TEST using namespace std; @@ -16,6 +16,15 @@ void TestKernels() { float x[] = {0, 1.0f, 5.4f, 100.7f, -30.f, 36.f, -4.f}; float h[] = {10.f, 1.0f, 2.4f, 140.23f, -22.f, -2.f, 20.f}; + glm::vec3 r[] = { + glm::vec3(1,0,0), + glm::vec3(0,1,0), + glm::vec3(0,0,1), + glm::vec3(1,0,0), + glm::vec3(0,1,0), + glm::vec3(0,1,0), + glm::vec3(0,0,1) + }; LOG(INFO) << "TestKernels" << endl; @@ -25,6 +34,10 @@ void TestKernels() float wPoly6 = KernelPoly6(x[i], h[i]); LOG(INFO) << wPoly6 << endl; + // LOG(INFO) << "Grad" << endl; + // glm::vec3 gradSpiky = GradKernelSpike(r, x, h); + // LOG(INFO) << "x: " << gradSpiky.x << ", y: " << gradSpiky.y << ", z: " << gradSpiky.z << endl; + #ifdef TEST_SPIKY LOG(INFO) << "KernelSpiky: x: " + to_string(x[i]) + ", h: " + to_string(h[i]) << endl; float wSpiky = KernelSpiky(x[i], h[i]); diff --git a/src/mathConstants.h b/src/mathConstants.h index dd8763c8..08d33aba 100644 --- a/src/mathConstants.h +++ b/src/mathConstants.h @@ -8,6 +8,6 @@ static const float TWO_PI = 2 * PI; static const float INV_PI = 1.0f / PI; static const float DEG2RAD = PI / 180.f; static const float RAD2DEG = 180.f / PI; -static const float GRAVITY = -0.5f; +static const float GRAVITY = -9.8f; #endif diff --git a/src/scene/scene.json b/src/scene/scene.json index 79a33d56..bba7aeee 100644 --- a/src/scene/scene.json +++ b/src/scene/scene.json @@ -1,8 +1,8 @@ { "containerDim" : { - "scaleX" : 10.0, - "scaleY" : 10.0, - "scaleZ" : 10.0 + "scaleX" : 3.0, + "scaleY" : 3.0, + "scaleZ" : 3.0 }, "particleDim" : { "boundX" : 1.0, diff --git a/src/viewer.cpp b/src/viewer.cpp index 7ac53d9d..d0a49b2b 100644 --- a/src/viewer.cpp +++ b/src/viewer.cpp @@ -98,7 +98,7 @@ Viewer::Update() static float lastTime = glfwGetTime(); float currentTime = glfwGetTime(); float deltaTime = float(currentTime - lastTime) * 0.01f; - m_scene->Update(deltaTime, m_keyboard, *m_programAdvect); + m_scene->Update(0.01f, m_keyboard, *m_programAdvect); #ifdef TEST_SCENE m_scene->Draw(*m_program); From dde8ca13e586974073eade8de0fab3b3ba7d77ee Mon Sep 17 00:00:00 2001 From: Trung Le Date: Wed, 30 Mar 2016 03:02:56 -0400 Subject: [PATCH 38/64] Added z-curve indexing and viscosity force --- src/camera/camera.cpp | 2 +- src/fluidSolver/SPHGrid.cpp | 47 ++++++++++++++++++++++----- src/fluidSolver/SPHKernels.h | 5 +++ src/fluidSolver/SPHSolver.cpp | 53 +++++++++++++++++++------------ src/fluidSolver/SPHSolver.h | 5 +++ src/fluidSolver/fluidParticle.cpp | 4 +-- src/fluidSolver/fluidParticle.h | 35 ++++++++++---------- src/scene/scene.json | 4 +-- src/viewer.cpp | 4 +-- 9 files changed, 108 insertions(+), 51 deletions(-) diff --git a/src/camera/camera.cpp b/src/camera/camera.cpp index 11f8264b..bde9343d 100644 --- a/src/camera/camera.cpp +++ b/src/camera/camera.cpp @@ -12,7 +12,7 @@ Camera::Camera( ) : m_width(width), m_height(height), m_fov(45), - m_eye(glm::vec3(0.0f, 0.0f, -20.0f)), + m_eye(glm::vec3(0.0f, 0.0f, -5.0f)), m_target(glm::vec3(0.0f)), m_up(glm::vec3(0.0f, 1.0f, 0.0f)), m_right(glm::vec3(1.0f, 0.0f, 0.0f)), diff --git a/src/fluidSolver/SPHGrid.cpp b/src/fluidSolver/SPHGrid.cpp index c09c670b..8ecc20d2 100644 --- a/src/fluidSolver/SPHGrid.cpp +++ b/src/fluidSolver/SPHGrid.cpp @@ -66,7 +66,7 @@ SPHGrid::AddParticle( // Add particle to grid based on its position int idx = GetCellIdx(particle->Position()); if (idx > m_cells.size() || idx < 0) { - LOG(WARNING) << "Error adding particle " + idx << endl; + // LOG(WARNING) << "Error adding particle " + idx << endl; return; } @@ -88,13 +88,15 @@ SPHGrid::GetCellIdx( // -- Error checking if (i < 0 || j < 0 || k < 0 || i >= m_width || j >= m_height || k >= m_depth) { - LOG(WARNING) << "WARNING GetCellIdx "<< endl; - LOG(WARNING) << "x: "<< position.x << ", y: " << position.y << ", z: " << position.z << endl; - LOG(WARNING) << "i: "<< i << ", j: " << j << ", k: " << k << endl; + // LOG(WARNING) << "WARNING GetCellIdx "<< endl; + // LOG(WARNING) << "x: "<< position.x << ", y: " << position.y << ", z: " << position.z << endl; + // LOG(WARNING) << "i: "<< i << ", j: " << j << ", k: " << k << endl; return -1; } - return this->GetCellIdx(i, j, k); + int idx = this->GetCellIdx(i, j, k); + // LOG(DEBUG) << "i: "<< i << ", j: " << j << ", k: " << k << " -> " << idx << endl; + return idx; } int @@ -104,9 +106,38 @@ SPHGrid::GetCellIdx( int k ) { - int idx = i + j * m_depth + k * m_width * m_height; - if (idx < 0) { - LOG(ERROR) << "Get cell idx is negative " + idx << endl; + // int idx = i + j * m_depth + k * m_width * m_height; + // if (idx < 0) { + // LOG(ERROR) << "Get cell idx is negative " + idx << endl; + // } + + // -- Uses z-curve indexing + // See https://en.wikipedia.org/wiki/Z-order_curve + int i_temp = i; + int j_temp = j; + int k_temp = k; + int idx = 0; + int digit = 0; + while (i_temp != 0 || j_temp != 0 || k_temp != 0) { + // Interleave bits + char i_bit = i_temp & 0x1; + idx = idx | (i_bit << digit); + digit++; + i_temp = i_temp >> 1; + + char j_bit = j_temp & 0x1; + idx = idx | (j_bit << digit); + digit++; + j_temp = j_temp >> 1; + + char k_bit = k_temp & 0x1; + idx = idx | (k_bit << digit); + digit++; + k_temp = k_temp >> 1; + } + if (idx < 0 || idx > m_cells.size()) { + // LOG(ERROR) << "Get cell idx is negative " + idx << endl; + return -1; } return idx; } diff --git a/src/fluidSolver/SPHKernels.h b/src/fluidSolver/SPHKernels.h index a15ba2c6..aa7b6ac9 100644 --- a/src/fluidSolver/SPHKernels.h +++ b/src/fluidSolver/SPHKernels.h @@ -46,6 +46,11 @@ inline glm::vec3 GradKernelSpiky(glm::vec3 x_vec, float x, float h) return scalarPart * x_vec; } +inline float LaplacianKernelViscous(float x, float h) +{ + return 45.0f * (h - x) / (M_PI * pow(h, 6.0f)); +} + inline float KernelViscous(float x, float h) { if (x <= 0 || x > h) { diff --git a/src/fluidSolver/SPHSolver.cpp b/src/fluidSolver/SPHSolver.cpp index 242da9b6..66250e0f 100644 --- a/src/fluidSolver/SPHSolver.cpp +++ b/src/fluidSolver/SPHSolver.cpp @@ -19,7 +19,8 @@ SPHSolver::SPHSolver( m_stiffness(stiffness), m_viscosity(viscosity), m_restDensity(restDensity), - m_timestep(timestep) + m_timestep(timestep), + m_kernelRadius(cellSize * 2.0f) { m_grid = new SPHGrid( m_particles, @@ -56,6 +57,7 @@ SPHSolver::Update( neighbors.clear(); neighbors = m_grid->SearchNeighbors(p); this->CalculatePressureForceField(p, neighbors); + this->CalculateViscosityForceField(p, neighbors); } for (FluidParticle* p : m_particles) { @@ -72,7 +74,7 @@ SPHSolver::CalculateDensity( // -- Compute density over a kernel function of neighbors float density = 0.f; for (FluidParticle* neighbor : neighbors) { - float tempDensity = KernelPoly6(glm::distance(neighbor->Position(), particle->Position()), m_cellSize * 2); + float tempDensity = KernelPoly6(glm::distance(neighbor->Position(), particle->Position()), m_kernelRadius); density += tempDensity; } density = m_mass * density; @@ -98,31 +100,42 @@ SPHSolver::CalculatePressureForceField( ) { // -- Compute pressure gradient - glm::vec3 pressureGrad; + glm::vec3 pressureGrad(0.0f); for (FluidParticle* neighbor : neighbors) { glm::vec3 r = particle->Position() - neighbor->Position(); float x = glm::distance(neighbor->Position(), particle->Position()); - // LOG(DEBUG) << "x: " << x << endl; - glm::vec3 kernelGrad = GradKernelSpiky(r, x, m_cellSize * 2); - // LOG(DEBUG) << "x: " << x << ", h: " << m_cellSize << endl; - // LOG(DEBUG) << "KernelGrad x: " << kernelGrad.x << ", y: " << kernelGrad.y << ", z: " << kernelGrad.z << endl; + glm::vec3 kernelGrad = GradKernelSpiky(r, x, m_kernelRadius); float tempPressureForce = ( particle->Pressure() / (pow(particle->Density(), 2.0f)) + neighbor->Pressure() / (pow(neighbor->Density(), 2.0f)) ); - // LOG(DEBUG) << "Temp pressure force: " << tempPressureForce << endl; - // LOG(DEBUG) << "Paricle pressure: " << particle->Pressure() << endl; - // LOG(DEBUG) << "Neighbor pressure: " << neighbor->Pressure() << endl; pressureGrad += tempPressureForce * kernelGrad; } - pressureGrad = pressureGrad * m_mass * m_mass; - // LOG(DEBUG) << "Pressure x: " << pressureGrad.x << ", y: " << pressureGrad.y << ", z: " << pressureGrad.z << endl; - particle->SetPressureForce(-pressureGrad); - particle->SetColor(glm::vec4(glm::abs(pressureGrad), 1.f)); + pressureGrad = -pressureGrad * m_mass * m_mass; + particle->SetPressureForce(pressureGrad); + // particle->SetColor(glm::vec4(glm::abs(pressureGrad), 1.f)); } +void +SPHSolver::CalculateViscosityForceField( + FluidParticle* particle, + const std::vector& neighbors + ) +{ + glm::vec3 viscosityForce(0.0f); + for (FluidParticle* neighbor : neighbors) { + + float laplacianKernelViscous = LaplacianKernelViscous(glm::distance(particle->Position(), neighbor->Position()), m_kernelRadius); + glm::vec3 tempVisForce = (neighbor->Velocity() - particle->Velocity()) * laplacianKernelViscous / neighbor->Density(); + viscosityForce += tempVisForce; + } + viscosityForce = viscosityForce * m_mass * m_viscosity; + particle->SetViscosityForce(viscosityForce); +} + + void SPHSolver::UpdateDynamics( FluidParticle* particle, @@ -136,26 +149,26 @@ SPHSolver::UpdateDynamics( glm::vec3 newVel = particle->Velocity(); if (particle->Position().x < m_minBoundary.x) { newPosition.x = m_minBoundary.x + 0.0f; - newVel.x = -newVel.x * 0.2; + newVel.x = -newVel.x * 0.3; } else if (particle->Position().x > m_maxBoundary.x) { newPosition.x = m_maxBoundary.x - 0.0f; - newVel.x = -newVel.x * 0.2; + newVel.x = -newVel.x * 0.3; } if (particle->Position().y < m_minBoundary.y) { newPosition.y = m_minBoundary.y + 0.0f; - newVel.y = -newVel.y * 0.2; + newVel.y = -newVel.y * 0.3; } else if (particle->Position().y > m_maxBoundary.y) { newPosition.y = m_maxBoundary.y - 0.0f; - newVel.y = -newVel.y * 0.2f; + newVel.y = -newVel.y * 0.3f; } if (particle->Position().z < m_minBoundary.z) { newPosition.z = m_minBoundary.z + 0.0f; - newVel.z = -newVel.z * 0.2; + newVel.z = -newVel.z * 0.3; } else if (particle->Position().z > m_maxBoundary.z) { newPosition.z = m_maxBoundary.z - 0.0f; - newVel.z = -newVel.z * 0.2; + newVel.z = -newVel.z * 0.3; } particle->SetPosition(newPosition); diff --git a/src/fluidSolver/SPHSolver.h b/src/fluidSolver/SPHSolver.h index 9f3c220a..81fbae69 100644 --- a/src/fluidSolver/SPHSolver.h +++ b/src/fluidSolver/SPHSolver.h @@ -38,6 +38,7 @@ class SPHSolver : public FluidSolver float m_viscosity; float m_restDensity; float m_timestep; + float m_kernelRadius; void CalculateDensity( FluidParticle* particle, @@ -50,6 +51,10 @@ class SPHSolver : public FluidSolver FluidParticle* particle, const std::vector& neighbors ); + void CalculateViscosityForceField( + FluidParticle* particle, + const std::vector& neighbors + ); void UpdateDynamics( FluidParticle* particle, const float deltaT diff --git a/src/fluidSolver/fluidParticle.cpp b/src/fluidSolver/fluidParticle.cpp index 570b88cd..ca2206bd 100644 --- a/src/fluidSolver/fluidParticle.cpp +++ b/src/fluidSolver/fluidParticle.cpp @@ -26,8 +26,8 @@ FluidParticle::Update( float mass = 0.125; float cellSize = 0.10001; - glm::vec3 gravity(0.0f, mass * GRAVITY, 0.0f); - glm::vec3 totalForce = m_accel + m_pressureForce + gravity; + glm::vec3 gravity(0.0f, GRAVITY, 0.0f); + glm::vec3 totalForce = m_accel + m_pressureForce + m_viscosityForce + gravity; m_vel = m_vel + totalForce * deltaT; // -- Enforce CFL condition diff --git a/src/fluidSolver/fluidParticle.h b/src/fluidSolver/fluidParticle.h index 9e415183..22b7790f 100644 --- a/src/fluidSolver/fluidParticle.h +++ b/src/fluidSolver/fluidParticle.h @@ -24,22 +24,24 @@ class FluidParticle virtual void Update(const float deltaT); // -- Getters/setters - const glm::vec3& Position() const { return m_pos; } - void SetPosition(const glm::vec3& pos) { m_pos = pos; } - const glm::vec3& Velocity() const { return m_vel; } - void SetVelocity(const glm::vec3& vel) { m_vel = vel; } - const glm::vec3& Acceleration() const { return m_accel; } - void SetForce(const glm::vec3& force) { m_accel = force / 0.125f; } // Mass is 1 - const glm::vec4& Color() const { return m_col; } - void SetColor(const glm::vec4& color) { m_col = color; } - const float& Density() const { return m_density; } - void SetDensity(float density) { m_density = density; } - const float Pressure() const { return m_pressure; } - void SetPressure(const float pressure) { m_pressure = pressure; } - const glm::vec3& PressureForce() const { return m_pressureForce; } - void SetPressureForce(const glm::vec3& pressureForce) { m_pressureForce = pressureForce; } - const float& SpawnTime() const { return m_spawnTime; } - void SetSpawnTime(float spawnTime) { m_spawnTime = spawnTime; } + inline const glm::vec3& Position() const { return m_pos; } + inline void SetPosition(const glm::vec3& pos) { m_pos = pos; } + inline const glm::vec3& Velocity() const { return m_vel; } + inline void SetVelocity(const glm::vec3& vel) { m_vel = vel; } + inline const glm::vec3& Acceleration() const { return m_accel; } + inline void SetForce(const glm::vec3& force) { m_accel = force / 0.125f; } // Mass is 1 + inline const glm::vec4& Color() const { return m_col; } + inline void SetColor(const glm::vec4& color) { m_col = color; } + inline const float& Density() const { return m_density; } + inline void SetDensity(float density) { m_density = density; } + inline const float Pressure() const { return m_pressure; } + inline void SetPressure(const float pressure) { m_pressure = pressure; } + inline const glm::vec3& PressureForce() const { return m_pressureForce; } + inline void SetPressureForce(const glm::vec3& pressureForce) { m_pressureForce = pressureForce; } + inline const glm::vec3& ViscosityForce() const { return m_viscosityForce; } + inline void SetViscosityForce(const glm::vec3& viscosityForce) { m_viscosityForce = viscosityForce; } + inline const float& SpawnTime() const { return m_spawnTime; } + inline void SetSpawnTime(float spawnTime) { m_spawnTime = spawnTime; } protected: glm::vec3 m_pos; @@ -47,6 +49,7 @@ class FluidParticle glm::vec3 m_accel; glm::vec4 m_col; glm::vec3 m_pressureForce; + glm::vec3 m_viscosityForce; float m_pressure = 0.0f; float m_density = 1.0f; float m_spawnTime = 0.0f; diff --git a/src/scene/scene.json b/src/scene/scene.json index bba7aeee..37710fec 100644 --- a/src/scene/scene.json +++ b/src/scene/scene.json @@ -9,8 +9,8 @@ "boundY" : 1.0, "boundZ" : 1.0 }, - "stiffness": 1000, - "viscosity": 25, + "stiffness": 100, + "viscosity": 0.1, "mass": 0.125, "restDensity": 1000, "cellSize": 0.100001, diff --git a/src/viewer.cpp b/src/viewer.cpp index d0a49b2b..8807b57e 100644 --- a/src/viewer.cpp +++ b/src/viewer.cpp @@ -97,8 +97,8 @@ Viewer::Update() static float lastTime = glfwGetTime(); float currentTime = glfwGetTime(); - float deltaTime = float(currentTime - lastTime) * 0.01f; - m_scene->Update(0.01f, m_keyboard, *m_programAdvect); + float deltaTime = float(currentTime - lastTime); + m_scene->Update(deltaTime, m_keyboard, *m_programAdvect); #ifdef TEST_SCENE m_scene->Draw(*m_program); From bc69b371893b45089d41c53d9a35db35fe0a9ae7 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Wed, 30 Mar 2016 03:19:32 -0400 Subject: [PATCH 39/64] [SRPINT 2] - Updated README and tweak parameters for the solver --- README.md | 21 ++++++++++++++------- src/fluidSolver/SPHSolver.cpp | 12 ++++++------ src/fluidSolver/fluidParticle.cpp | 2 +- src/scene/scene.json | 4 ++-- 4 files changed, 23 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 13b1086e..d970cf6b 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,24 @@ # Korra Fluid Solver, CIS 563, University of Pennsylvania, Spring 2016 +## Update March 30 (Sprint 2) + +### Completed features: +Pressure force field +Viscosity force field (change value inside scene.json) +Press Space to pause simulation +z-index sorting + +Currently my simulation is running on the CPU still so it's pretty slow. + +### Incomplete features: +OpenVDB +TBB + ## Update March 23 (Sprint 1) I implemented the SPH Solver with naive neighbor search. When you started running the code, you'll see a test particle iterating through the grid and shows all the neighbors in red color. However, this neighbor search isn't using a working uniform grid yet. I have a simple test suite in main.cpp to test Poly6. Currently spiky kernel and viscous kernel are not tested. By uncommenting `#define UNIT_TEST`, there should be print out of the kernel unit testing. -### Incomplete features: -Uniform grid. -Compute density. -Compute pressure. -Full unit testing. -OpenVDB. - ## Overview __Korra__ is a cross-platform C++ fluid FLIP solver, designed and built for CIS563 course. __Korra__ uses OpenGL heavily for the graphics pipeline. It is also designed for GPU optimization for fluid advection. diff --git a/src/fluidSolver/SPHSolver.cpp b/src/fluidSolver/SPHSolver.cpp index 66250e0f..f386b4b2 100644 --- a/src/fluidSolver/SPHSolver.cpp +++ b/src/fluidSolver/SPHSolver.cpp @@ -149,26 +149,26 @@ SPHSolver::UpdateDynamics( glm::vec3 newVel = particle->Velocity(); if (particle->Position().x < m_minBoundary.x) { newPosition.x = m_minBoundary.x + 0.0f; - newVel.x = -newVel.x * 0.3; + newVel.x = -newVel.x * 0.1; } else if (particle->Position().x > m_maxBoundary.x) { newPosition.x = m_maxBoundary.x - 0.0f; - newVel.x = -newVel.x * 0.3; + newVel.x = -newVel.x * 0.1; } if (particle->Position().y < m_minBoundary.y) { newPosition.y = m_minBoundary.y + 0.0f; - newVel.y = -newVel.y * 0.3; + newVel.y = -newVel.y * 0.1; } else if (particle->Position().y > m_maxBoundary.y) { newPosition.y = m_maxBoundary.y - 0.0f; - newVel.y = -newVel.y * 0.3f; + newVel.y = -newVel.y * 0.1f; } if (particle->Position().z < m_minBoundary.z) { newPosition.z = m_minBoundary.z + 0.0f; - newVel.z = -newVel.z * 0.3; + newVel.z = -newVel.z * 0.1; } else if (particle->Position().z > m_maxBoundary.z) { newPosition.z = m_maxBoundary.z - 0.0f; - newVel.z = -newVel.z * 0.3; + newVel.z = -newVel.z * 0.1; } particle->SetPosition(newPosition); diff --git a/src/fluidSolver/fluidParticle.cpp b/src/fluidSolver/fluidParticle.cpp index ca2206bd..8e033096 100644 --- a/src/fluidSolver/fluidParticle.cpp +++ b/src/fluidSolver/fluidParticle.cpp @@ -26,7 +26,7 @@ FluidParticle::Update( float mass = 0.125; float cellSize = 0.10001; - glm::vec3 gravity(0.0f, GRAVITY, 0.0f); + glm::vec3 gravity(0.0f, mass * GRAVITY, 0.0f); glm::vec3 totalForce = m_accel + m_pressureForce + m_viscosityForce + gravity; m_vel = m_vel + totalForce * deltaT; diff --git a/src/scene/scene.json b/src/scene/scene.json index 37710fec..4c991946 100644 --- a/src/scene/scene.json +++ b/src/scene/scene.json @@ -9,11 +9,11 @@ "boundY" : 1.0, "boundZ" : 1.0 }, - "stiffness": 100, + "stiffness": 10, "viscosity": 0.1, "mass": 0.125, "restDensity": 1000, "cellSize": 0.100001, - "particleSeparation": 0.05, + "particleSeparation": 0.06, "timestep": 0.001 } From fb8657c7ff44a0cb6700132e2bf3d3c71ee3b2eb Mon Sep 17 00:00:00 2001 From: Trung Le Date: Wed, 30 Mar 2016 03:27:11 -0400 Subject: [PATCH 40/64] [SPRINT 2] - Added TBB parallel_for to update particles --- README.md | 6 +++--- src/fluidSolver/SPHSolver.cpp | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d970cf6b..d340cdfc 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,11 @@ Viscosity force field (change value inside scene.json) Press Space to pause simulation z-index sorting -Currently my simulation is running on the CPU still so it's pretty slow. +Currently my simulation is running on the CPU still so it's pretty slow. The only tbb call is in SPHSolver::Update (in SPHSolver.cpp), that update the particle velocity & position using parallel_for (not even sure if this is worth any extra credits); ### Incomplete features: -OpenVDB -TBB +- OpenVDB +- TBB ## Update March 23 (Sprint 1) I implemented the SPH Solver with naive neighbor search. When you started running the code, you'll see a test particle iterating through the grid and shows all the neighbors in red color. However, this neighbor search isn't using a working uniform grid yet. diff --git a/src/fluidSolver/SPHSolver.cpp b/src/fluidSolver/SPHSolver.cpp index f386b4b2..52ed7cd9 100644 --- a/src/fluidSolver/SPHSolver.cpp +++ b/src/fluidSolver/SPHSolver.cpp @@ -46,6 +46,7 @@ SPHSolver::Update( m_grid->ResetGrid(m_particles); std::vector neighbors; + int len = m_particles.size(); for (FluidParticle* p : m_particles) { neighbors.clear(); neighbors = m_grid->SearchNeighbors(p); @@ -60,9 +61,10 @@ SPHSolver::Update( this->CalculateViscosityForceField(p, neighbors); } - for (FluidParticle* p : m_particles) { + parallel_for(0, len, [&](int i) { + FluidParticle* p = m_particles[i]; this->UpdateDynamics(p, deltaT); - } + }); } void From 927fc3388b6739fa5183799349803946ffda9870 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Thu, 31 Mar 2016 00:44:51 -0400 Subject: [PATCH 41/64] [SPRINT 3] - Cached all neighbors for each particles. Used TBB to parallelize computation in main update function --- Debug/Korra | Bin 0 -> 1190984 bytes logs/myeasylog.log | 38 +++++++++++++++ src/fluidSolver/SPHGrid.cpp | 61 ++++++++++++------------ src/fluidSolver/SPHGrid.h | 12 +++-- src/fluidSolver/SPHKernels.h | 12 +++-- src/fluidSolver/SPHSolver.cpp | 75 +++++++++++++++++++++--------- src/fluidSolver/SPHSolver.h | 9 ++-- src/fluidSolver/fluidParticle.cpp | 5 +- src/fluidSolver/fluidParticle.h | 19 +++++++- src/scene/scene.json | 6 +-- 10 files changed, 166 insertions(+), 71 deletions(-) create mode 100755 Debug/Korra create mode 100644 logs/myeasylog.log diff --git a/Debug/Korra b/Debug/Korra new file mode 100755 index 0000000000000000000000000000000000000000..6127ad1b80e36543b935f5537ac76e18ea11f2ed GIT binary patch literal 1190984 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

11g->$u=ydrDG>@3rC8Fp|=_zAjF)>qm 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�x*~F9vCI}4*(3EiMrFbtTRn-@t^#?dJe0skGhxD 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%0AYY9ZBL) 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�K#XM8bZRU{@+g3P0R9;&RsRg&!MJ}O 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

WslS-TwaG5VIg<@`=q7+k6gYU?gIC7RZwA0>k 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 NJR~Av!H8f~ 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'v@K}c5 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 BZOQ-u literal 0 HcmV?d00001 diff --git a/logs/myeasylog.log b/logs/myeasylog.log new file mode 100644 index 00000000..1618e183 --- /dev/null +++ b/logs/myeasylog.log @@ -0,0 +1,38 @@ +2016-03-30 17:05:18,899 INFO [default] ------------------------ + +2016-03-30 17:05:18,900 INFO [default] UNIT TEST ENABLED + +2016-03-30 17:05:18,900 INFO [default] TestKernels + +2016-03-30 17:05:18,900 INFO [default] KernelPoly6, x: 0.000000, h: 10.000000 + +2016-03-30 17:05:18,900 INFO [default] 0.00156668 + +2016-03-30 17:05:18,900 INFO [default] KernelPoly6, x: 1.000000, h: 1.000000 + +2016-03-30 17:05:18,900 INFO [default] 0 + +2016-03-30 17:05:18,901 INFO [default] KernelPoly6, x: 5.400000, h: 2.400000 + +2016-03-30 17:05:18,901 INFO [default] 0 + +2016-03-30 17:05:18,901 INFO [default] KernelPoly6, x: 100.699997, h: 140.229996 + +2016-03-30 17:05:18,901 INFO [default] 6.45454e-08 + +2016-03-30 17:05:18,901 INFO [default] KernelPoly6, x: -30.000000, h: -22.000000 + +2016-03-30 17:05:18,901 INFO [default] 0 + +2016-03-30 17:05:18,901 INFO [default] KernelPoly6, x: 36.000000, h: -2.000000 + +2016-03-30 17:05:18,901 INFO [default] 0 + +2016-03-30 17:05:18,901 INFO [default] KernelPoly6, x: -4.000000, h: 20.000000 + +2016-03-30 17:05:18,901 INFO [default] 0 + +2016-03-30 17:05:18,901 INFO [default] END UNIT TEST + +2016-03-30 17:05:18,901 INFO [default] ------------------------ + diff --git a/src/fluidSolver/SPHGrid.cpp b/src/fluidSolver/SPHGrid.cpp index 8ecc20d2..028bfc30 100644 --- a/src/fluidSolver/SPHGrid.cpp +++ b/src/fluidSolver/SPHGrid.cpp @@ -88,14 +88,10 @@ SPHGrid::GetCellIdx( // -- Error checking if (i < 0 || j < 0 || k < 0 || i >= m_width || j >= m_height || k >= m_depth) { - // LOG(WARNING) << "WARNING GetCellIdx "<< endl; - // LOG(WARNING) << "x: "<< position.x << ", y: " << position.y << ", z: " << position.z << endl; - // LOG(WARNING) << "i: "<< i << ", j: " << j << ", k: " << k << endl; return -1; } int idx = this->GetCellIdx(i, j, k); - // LOG(DEBUG) << "i: "<< i << ", j: " << j << ", k: " << k << " -> " << idx << endl; return idx; } @@ -106,11 +102,13 @@ SPHGrid::GetCellIdx( int k ) { - // int idx = i + j * m_depth + k * m_width * m_height; - // if (idx < 0) { - // LOG(ERROR) << "Get cell idx is negative " + idx << endl; - // } - +#define USE_Z_CURVE +#ifndef USE_Z_CURVE + int idx = i + j * m_depth + k * m_width * m_height; + if (idx < 0) { + LOG(ERROR) << "Get cell idx is negative " + idx << endl; + } +#else // -- Uses z-curve indexing // See https://en.wikipedia.org/wiki/Z-order_curve int i_temp = i; @@ -139,6 +137,7 @@ SPHGrid::GetCellIdx( // LOG(ERROR) << "Get cell idx is negative " + idx << endl; return -1; } +#endif return idx; } @@ -154,47 +153,48 @@ SPHGrid::GetCellCoord( ); } -std::vector -SPHGrid::SearchNeighbors( +void +SPHGrid::UpdateNeighbors( FluidParticle* particle ) { + particle->ResetNeighborCount(); if (m_useGrid) { - return this->SearchNeighborsUniformGrid(particle); + this->UpdateNeighborsUniformGrid(particle); } else { - return this->SearchNeighborsSimple(particle); + this->UpdateNeighborsSimple(particle); } } -std::vector -SPHGrid::SearchNeighborsSimple( +void +SPHGrid::UpdateNeighborsSimple( FluidParticle* particle ) { - vector neighbors; + size_t len = m_cells[0].size(); - int len = m_cells[0].size(); - for (int i = 0; i < len; ++i) { + for (size_t i = 0; i < len; ++i) { FluidParticle* neighbor = m_cells[0][i]; if (glm::distance(neighbor->Position(), particle->Position()) < m_cellSize && neighbor != particle) { - neighbors.push_back(neighbor); - } - }; - return neighbors; + if(!particle->AddNeighbor(neighbor)) { + // Can't add more neighbors to limit, stop loop + break; + } + } + } } -std::vector -SPHGrid::SearchNeighborsUniformGrid( +void +SPHGrid::UpdateNeighborsUniformGrid( FluidParticle* particle ) { - vector neighbors; - // -- Use grid to search neighbor glm::ivec3 cellCoord = this->GetCellCoord(particle->Position()); + // Search in all 27 neighboring cells for (int i = -1; i <= 1; ++i) { for (int j = -1; j <= 1; ++j) { @@ -211,16 +211,19 @@ SPHGrid::SearchNeighborsUniformGrid( } int idx = this->GetCellIdx(i + cellCoord.x, j + cellCoord.y, k + cellCoord.z); + glm::vec3 particlePosition = particle->Position(); for(FluidParticle* neighbor : m_cells[idx]) { if (glm::distance(neighbor->Position(), particlePosition) < m_cellSize * 2 && neighbor != particle) { - neighbors.push_back(neighbor); + + if(!particle->AddNeighbor(neighbor)) { + // Can't add more neighbors to limit, stop loop + break; + } } } } } } - - return neighbors; } diff --git a/src/fluidSolver/SPHGrid.h b/src/fluidSolver/SPHGrid.h index 49251011..89f5638f 100644 --- a/src/fluidSolver/SPHGrid.h +++ b/src/fluidSolver/SPHGrid.h @@ -30,7 +30,9 @@ class SPHGrid ); virtual void ResetGrid(const std::vector& particles); virtual void AddParticle(FluidParticle* particle); - virtual std::vector SearchNeighbors(FluidParticle*); + virtual void UpdateNeighbors( + FluidParticle* + ); private: @@ -50,8 +52,12 @@ class SPHGrid glm::ivec3 GetCellCoord(const glm::vec3&); // -- Neighbor search algorithms - std::vector SearchNeighborsSimple(FluidParticle*); - std::vector SearchNeighborsUniformGrid(FluidParticle*); + void UpdateNeighborsSimple( + FluidParticle* + ); + void UpdateNeighborsUniformGrid( + FluidParticle* + ); }; #endif diff --git a/src/fluidSolver/SPHKernels.h b/src/fluidSolver/SPHKernels.h index aa7b6ac9..f9b9db71 100644 --- a/src/fluidSolver/SPHKernels.h +++ b/src/fluidSolver/SPHKernels.h @@ -15,8 +15,9 @@ inline float KernelPoly6(float x, float h) if (h <= 0) { return 0; } - - return (315 * pow(pow(h, 2.0f) - pow(x, 2.0f), 3.0f)) / (64.0f * M_PI * pow(h, 9.0f)); + /// @todo: cache h2 and x2 + float result = (315 * pow(h * h - x * x, 3.0f)) / (64.0f * M_PI * pow(h, 9.0f)); + return result < 0.0f ? 0.0f : result; } inline float KernelSpiky(float x, float h) @@ -29,7 +30,8 @@ inline float KernelSpiky(float x, float h) return 0; } - return (15.0f * pow(h - x, 3.0f)) / (M_PI * pow(h, 6.0f)); + float result = (15.0f * pow(h - x, 3.0f)) / (M_PI * pow(h, 6.0f)); + return result < 0.0 ? 0.0f : result; } inline glm::vec3 GradKernelSpiky(glm::vec3 x_vec, float x, float h) @@ -42,8 +44,8 @@ inline glm::vec3 GradKernelSpiky(glm::vec3 x_vec, float x, float h) return glm::vec3(0.0f); } - float scalarPart = -45.0f * pow ((h - x), 2.0f) / (M_PI * pow(h, 6.0f)); - return scalarPart * x_vec; + float scalarPart = -45.0f * (h - x) * (h - x) / (M_PI * pow(h, 6.0f)); + return scalarPart * (x_vec) / x; } inline float LaplacianKernelViscous(float x, float h) diff --git a/src/fluidSolver/SPHSolver.cpp b/src/fluidSolver/SPHSolver.cpp index 52ed7cd9..84faac8f 100644 --- a/src/fluidSolver/SPHSolver.cpp +++ b/src/fluidSolver/SPHSolver.cpp @@ -45,37 +45,71 @@ SPHSolver::Update( { m_grid->ResetGrid(m_particles); - std::vector neighbors; +#define USE_TBB +#ifdef USE_TBB + // -- Search all neighbors int len = m_particles.size(); + parallel_for(0, len, [&](int i) { + FluidParticle* p = m_particles[i]; + m_grid->UpdateNeighbors(p); + }); + + // -- Calculate density & pressures + parallel_for(0, len, [&](int i) { + FluidParticle* p = m_particles[i]; + this->CalculateDensity(p); + this->CalculatePressure(p); + }); + + // -- Calculate force field + parallel_for(0, len, [&](int i) { + FluidParticle* p = m_particles[i]; + this->CalculatePressureForceField(p); + this->CalculateViscosityForceField(p); + }); + + // -- Update particles + parallel_for(0, len, [&](int i) { + FluidParticle* p = m_particles[i]; + this->UpdateDynamics(p, deltaT); + }); + +#else + + // -- Search all neighbors for (FluidParticle* p : m_particles) { - neighbors.clear(); - neighbors = m_grid->SearchNeighbors(p); - this->CalculateDensity(p, neighbors); + m_grid->UpdateNeighbors(p); + } + + // -- Calculate density & pressures + for (FluidParticle* p : m_particles) { + this->CalculateDensity(p); this->CalculatePressure(p); } + // -- Calculate force field for (FluidParticle* p : m_particles) { - neighbors.clear(); - neighbors = m_grid->SearchNeighbors(p); - this->CalculatePressureForceField(p, neighbors); - this->CalculateViscosityForceField(p, neighbors); + this->CalculatePressureForceField(p); + this->CalculateViscosityForceField(p); } - parallel_for(0, len, [&](int i) { - FluidParticle* p = m_particles[i]; + // -- Update particles + for (FluidParticle* p : m_particles) { this->UpdateDynamics(p, deltaT); - }); + } + +#endif } void SPHSolver::CalculateDensity( - FluidParticle* particle, - const std::vector& neighbors + FluidParticle* particle ) { // -- Compute density over a kernel function of neighbors float density = 0.f; - for (FluidParticle* neighbor : neighbors) { + for (size_t i = 0; i < particle->neighborsCount; ++i) { + FluidParticle* neighbor = particle->neighbors[i]; float tempDensity = KernelPoly6(glm::distance(neighbor->Position(), particle->Position()), m_kernelRadius); density += tempDensity; } @@ -97,13 +131,13 @@ SPHSolver::CalculatePressure( void SPHSolver::CalculatePressureForceField( - FluidParticle* particle, - const std::vector& neighbors + FluidParticle* particle ) { // -- Compute pressure gradient glm::vec3 pressureGrad(0.0f); - for (FluidParticle* neighbor : neighbors) { + for (size_t i = 0; i < particle->neighborsCount; ++i) { + FluidParticle* neighbor = particle->neighbors[i]; glm::vec3 r = particle->Position() - neighbor->Position(); float x = glm::distance(neighbor->Position(), particle->Position()); glm::vec3 kernelGrad = GradKernelSpiky(r, x, m_kernelRadius); @@ -122,13 +156,12 @@ SPHSolver::CalculatePressureForceField( void SPHSolver::CalculateViscosityForceField( - FluidParticle* particle, - const std::vector& neighbors + FluidParticle* particle ) { glm::vec3 viscosityForce(0.0f); - for (FluidParticle* neighbor : neighbors) { - + for (size_t i = 0; i < particle->neighborsCount; ++i) { + FluidParticle* neighbor = particle->neighbors[i]; float laplacianKernelViscous = LaplacianKernelViscous(glm::distance(particle->Position(), neighbor->Position()), m_kernelRadius); glm::vec3 tempVisForce = (neighbor->Velocity() - particle->Velocity()) * laplacianKernelViscous / neighbor->Density(); viscosityForce += tempVisForce; diff --git a/src/fluidSolver/SPHSolver.h b/src/fluidSolver/SPHSolver.h index 81fbae69..fa69b404 100644 --- a/src/fluidSolver/SPHSolver.h +++ b/src/fluidSolver/SPHSolver.h @@ -41,19 +41,16 @@ class SPHSolver : public FluidSolver float m_kernelRadius; void CalculateDensity( - FluidParticle* particle, - const std::vector& neighbors + FluidParticle* particle ); void CalculatePressure( FluidParticle* particle ); void CalculatePressureForceField( - FluidParticle* particle, - const std::vector& neighbors + FluidParticle* particle ); void CalculateViscosityForceField( - FluidParticle* particle, - const std::vector& neighbors + FluidParticle* particle ); void UpdateDynamics( FluidParticle* particle, diff --git a/src/fluidSolver/fluidParticle.cpp b/src/fluidSolver/fluidParticle.cpp index 8e033096..a6b60fd9 100644 --- a/src/fluidSolver/fluidParticle.cpp +++ b/src/fluidSolver/fluidParticle.cpp @@ -22,11 +22,10 @@ FluidParticle::Update( const float deltaT ) { - // @todo: HACK HERE! - float mass = 0.125; + // @todo: HACK HERE, should read cell size from json! float cellSize = 0.10001; - glm::vec3 gravity(0.0f, mass * GRAVITY, 0.0f); + glm::vec3 gravity(0.0f, MASS * GRAVITY, 0.0f); glm::vec3 totalForce = m_accel + m_pressureForce + m_viscosityForce + gravity; m_vel = m_vel + totalForce * deltaT; diff --git a/src/fluidSolver/fluidParticle.h b/src/fluidSolver/fluidParticle.h index 22b7790f..56397a1e 100644 --- a/src/fluidSolver/fluidParticle.h +++ b/src/fluidSolver/fluidParticle.h @@ -8,6 +8,9 @@ using namespace std; +#define NEIGHBORS_MAX 1000 +#define MASS 0.125f + // ---------------------------------------------------- // // FluidParticle // ---------------------------------------------------- // @@ -29,7 +32,7 @@ class FluidParticle inline const glm::vec3& Velocity() const { return m_vel; } inline void SetVelocity(const glm::vec3& vel) { m_vel = vel; } inline const glm::vec3& Acceleration() const { return m_accel; } - inline void SetForce(const glm::vec3& force) { m_accel = force / 0.125f; } // Mass is 1 + inline void SetForce(const glm::vec3& force) { m_accel = force / MASS; } inline const glm::vec4& Color() const { return m_col; } inline void SetColor(const glm::vec4& color) { m_col = color; } inline const float& Density() const { return m_density; } @@ -42,6 +45,20 @@ class FluidParticle inline void SetViscosityForce(const glm::vec3& viscosityForce) { m_viscosityForce = viscosityForce; } inline const float& SpawnTime() const { return m_spawnTime; } inline void SetSpawnTime(float spawnTime) { m_spawnTime = spawnTime; } + inline void ResetNeighborCount() { neighborsCount = 0; } + + // @return true if successful + inline bool AddNeighbor(FluidParticle* neighbor) { + if (neighborsCount < NEIGHBORS_MAX) { + neighbors[neighborsCount++] = neighbor; + return true; + } else { + return false; + } + } + + FluidParticle* neighbors[NEIGHBORS_MAX]; + size_t neighborsCount = 0; protected: glm::vec3 m_pos; diff --git a/src/scene/scene.json b/src/scene/scene.json index 4c991946..16adb327 100644 --- a/src/scene/scene.json +++ b/src/scene/scene.json @@ -9,11 +9,11 @@ "boundY" : 1.0, "boundZ" : 1.0 }, - "stiffness": 10, - "viscosity": 0.1, + "stiffness": 1000, + "viscosity": 0.0, "mass": 0.125, "restDensity": 1000, "cellSize": 0.100001, - "particleSeparation": 0.06, + "particleSeparation": 0.05, "timestep": 0.001 } From 34eeb4e3665b9067b5a1af43f65808bf02cc8f87 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Thu, 31 Mar 2016 17:32:13 -0400 Subject: [PATCH 42/64] [SPRINT 3] - Updated gitignore, remove Debug folder, updated README --- .gitignore | 2 ++ Debug/Korra | Bin 1190984 -> 0 bytes README.md | 13 +++++++++++-- 3 files changed, 13 insertions(+), 2 deletions(-) delete mode 100755 Debug/Korra diff --git a/.gitignore b/.gitignore index f534d84a..c6fdfbd3 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,6 @@ CMakeCache.txt cmake_install.cmake CMakeLists.txt.user build*/ +Debug/ *.sublime* +*.log diff --git a/Debug/Korra b/Debug/Korra deleted file mode 100755 index 6127ad1b80e36543b935f5537ac76e18ea11f2ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1190984 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

11g->$u=ydrDG>@3rC8Fp|=_zAjF)>qm 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�x*~F9vCI}4*(3EiMrFbtTRn-@t^#?dJe0skGhxD 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%0AYY9ZBL) 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�K#XM8bZRU{@+g3P0R9;&RsRg&!MJ}O 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

WslS-TwaG5VIg<@`=q7+k6gYU?gIC7RZwA0>k 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 NJR~Av!H8f~ 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'v@K}c5 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 BZOQ-u diff --git a/README.md b/README.md index d340cdfc..9f612e0c 100644 --- a/README.md +++ b/README.md @@ -21,10 +21,17 @@ I have a simple test suite in main.cpp to test Poly6. Currently spiky kernel and ## Overview -__Korra__ is a cross-platform C++ fluid FLIP solver, designed and built for CIS563 course. __Korra__ uses OpenGL heavily for the graphics pipeline. It is also designed for GPU optimization for fluid advection. +__Korra__ is a cross-platform C++ fluid SPH solver, designed and built for CIS563 course at the University of Pennsylvania, Sprin 2016, instructed by [Debanshu Singh](http://www.debanshusingh.com/). __Korra__ uses OpenGL heavily for the graphics pipeline. It is also designed for GPU optimization for fluid advection. Details will be updated as more features are coming soon. +## Compiling + +- Clone using git clone --recursive https://github.com/trungtle/KorraFluidSolver + +- If you forgot to call --recursive, you can recover the submodules by calling git submodule update --init --recursive + + ## Classes The following are classes for the fluid solver. @@ -124,4 +131,6 @@ See Camera class section above. - glm, [Website](http://glm.g-truc.net/0.9.7/index.html) - opengl-tutorial.org, [Website](http://www.opengl-tutorial.org/) - jsoncpp, [Github](https://github.com/open-source-parsers/jsoncpp) -- easylogging++, [Github](https://github.com/easylogging/easyloggingpp) +- easylogging++, [Github](https://github.com/easylogging/easyloggingpp +) +- nanogui, [Github](https://github.com/wjakob/nanogui) From b559e17d0271158a9b05af48e2f2745ec6b7e34c Mon Sep 17 00:00:00 2001 From: Trung Le Date: Thu, 31 Mar 2016 17:41:01 -0400 Subject: [PATCH 43/64] [SPRINT 3] - Added LICENSE.md --- LICENSE.md | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 7 ++++ 2 files changed, 105 insertions(+) create mode 100644 LICENSE.md diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 00000000..2ef1ee83 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,98 @@ +The MIT License + +Copyright (c) 2016 [Trung Le](http://www.trungtuanle.com/), [Debanshu Singh](http://www.debanshusingh.com/) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +Third-Party Code +============================= + +Korra fluid solver includes the following third-party code. + +## easylogging++ + +https://github.com/easylogging/easyloggingpp + +The MIT License (MIT) + +Copyright (c) 2015 muflihun.com + +http://github.com/easylogging/ +http://easylogging.muflihun.com +http://muflihun.com + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +## nanogui + +https://github.com/wjakob/nanogui + +Copyright (c) 2015 Wenzel Jakob , All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder 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 HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, 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. + +You are under no obligation whatsoever to provide any bug fixes, patches, or +upgrades to the features, functionality or performance of the source code +("Enhancements") to anyone; however, if you choose to make your Enhancements +available either publicly, or directly to the author of this software, without +imposing a separate written license agreement for such Enhancements, then you +hereby grant the following license: a non-exclusive, royalty-free perpetual +license to install, use, modify, prepare derivative works, incorporate into +other computer software, distribute, and sublicense such enhancements or +derivative works thereof, in binary and source code form. + + diff --git a/README.md b/README.md index 9f612e0c..0017e8c4 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,8 @@ Details will be updated as more features are coming soon. - If you forgot to call --recursive, you can recover the submodules by calling git submodule update --init --recursive +- Go to the top directory with CMakeFile + ## Classes @@ -123,6 +125,10 @@ N/A See Camera class section above. +## LICENSE + +See LICENSE.md for the MIT License. + ## Third Party - CIS277, Camera code @@ -131,6 +137,7 @@ See Camera class section above. - glm, [Website](http://glm.g-truc.net/0.9.7/index.html) - opengl-tutorial.org, [Website](http://www.opengl-tutorial.org/) - jsoncpp, [Github](https://github.com/open-source-parsers/jsoncpp) +- tbb, [Download](https://www.threadingbuildingblocks.org/) - easylogging++, [Github](https://github.com/easylogging/easyloggingpp ) - nanogui, [Github](https://github.com/wjakob/nanogui) From 695f867e7e695138bd21a4b14212d0f0fa29f612 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Thu, 31 Mar 2016 17:46:30 -0400 Subject: [PATCH 44/64] [SPRINT 3] - Added instruction for compiling and building the code --- README.md | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 0017e8c4..900aa844 100644 --- a/README.md +++ b/README.md @@ -27,11 +27,27 @@ Details will be updated as more features are coming soon. ## Compiling -- Clone using git clone --recursive https://github.com/trungtle/KorraFluidSolver - -- If you forgot to call --recursive, you can recover the submodules by calling git submodule update --init --recursive - -- Go to the top directory with CMakeFile +1. Clone using git clone --recursive https://github.com/trungtle/KorraFluidSolver + +If you forgot to call --recursive, you can recover the submodules by calling git submodule update --init --recursive + +2. Cd to directory (note, this is for Unix build) +``` +mkdir build +cd build +cmake -G "Unix Makefiles" ../ +``` +or +``` +cmake -G "Xcode" ../ +``` + +then + +``` +./Korra +``` +to launch the application. ## Classes From be4996c63490953e1cde580f831570cb6f9c4945 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Fri, 1 Apr 2016 19:04:27 -0400 Subject: [PATCH 45/64] [SPRINT 3] - Added GUI for tweaking parameters --- CMakeLists.txt | 24 ++- src/fluidSolver/SPHSolver.cpp | 32 ++- src/fluidSolver/SPHSolver.h | 15 +- src/fluidSolver/fluidParticle.cpp | 4 +- src/fluidSolver/fluidParticle.h | 6 +- src/fluidSolver/fluidSolver.cpp | 6 +- src/fluidSolver/fluidSolver.h | 1 - src/input/keyboardControl.h | 6 + src/main.cpp | 27 ++- src/scene/scene.cpp | 64 ++++-- src/scene/scene.h | 11 +- src/scene/scene.json | 5 +- src/shaderProgram/particleAdvectProgram.cpp | 2 +- src/thirdparty/nanogui | 1 + src/viewer.cpp | 222 ++++++++++++++++---- src/viewer.h | 29 ++- 16 files changed, 359 insertions(+), 96 deletions(-) create mode 160000 src/thirdparty/nanogui diff --git a/CMakeLists.txt b/CMakeLists.txt index c1b26881..a747c07d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,11 @@ project(Korra) cmake_minimum_required(VERSION 2.8) +# include source directories +include_directories( + src/ +) + # set creates a variable set (NUPARU "nuparu") # add include and src directories to path @@ -16,11 +21,6 @@ include_directories( ${NUPARU}/src ) -# include source directories -include_directories( - src/ -) - # Add path for pre-compiled libraries here (we will later link them with our compiled source) # Add Nuparu library to path for OSX, linux and windows if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") @@ -31,6 +31,17 @@ elseif(WIN32) set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} ${NUPARU}/lib/win) endif() +# set creates a variable +set (THIRDPARTY "src/thirdparty") +# add include and src directories to path +include_directories( + ${THIRDPARTY}/nanogui/include + ${THIRDPARTY}/nanogui/src +) + +# NanoGUI library path +set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} ${THIRDPARTY}/nanogui) + #set include variables set(GLFW_INCLUDE_DIR ${NUPARU}/include ) set(GLEW_INCLUDE_DIR ${NUPARU}/include ) @@ -44,6 +55,7 @@ find_library(GLFW_LIBRARY "glfw3" HINTS ${GLFW_LIBRARY_DIR}) find_library(JSONCPP "jsoncpp") find_library(TBB "tbb") find_library(OPENVDB "openvdb") +find_library(NANOGUI "nanogui") add_definitions( -DTW_STATIC @@ -53,7 +65,7 @@ add_definitions( -D_CRT_SECURE_NO_WARNINGS ) -set(CORE_LIBS ${GLFW_LIBRARY} ${GLUT_LIBRARY} ${GLEW_LIBRARY} ${JSONCPP} ${TBB} ${OPENVDB} ${OPENGL_LIBRARY} ) +set(CORE_LIBS ${GLFW_LIBRARY} ${GLUT_LIBRARY} ${GLEW_LIBRARY} ${JSONCPP} ${TBB} ${OPENVDB} ${NANOGUI} ${OPENGL_LIBRARY} ) # OSX-specific hacks/fixes if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") diff --git a/src/fluidSolver/SPHSolver.cpp b/src/fluidSolver/SPHSolver.cpp index 84faac8f..c42a92bd 100644 --- a/src/fluidSolver/SPHSolver.cpp +++ b/src/fluidSolver/SPHSolver.cpp @@ -11,15 +11,13 @@ SPHSolver::SPHSolver( const float stiffness, const float viscosity, const float mass, - const float restDensity, - const float timestep + const float restDensity ) : FluidSolver(containerDim, particleDim, separation, mass), m_grid(nullptr), m_cellSize(cellSize), m_stiffness(stiffness), m_viscosity(viscosity), m_restDensity(restDensity), - m_timestep(timestep), m_kernelRadius(cellSize * 2.0f) { m_grid = new SPHGrid( @@ -38,6 +36,28 @@ SPHSolver::~SPHSolver() delete m_grid; } +void +SPHSolver::SetConstant( + SPHConstantType type, + float value + ) +{ + switch(type) { + case SPHStiffness: + m_stiffness = value; + break; + case SPHViscosity: + m_viscosity = value; + break; + case SPHRestDensity: + m_restDensity = value; + break; + case SPHMass: + FluidParticle::mass = value; + break; + } +} + void SPHSolver::Update( const float deltaT @@ -113,7 +133,7 @@ SPHSolver::CalculateDensity( float tempDensity = KernelPoly6(glm::distance(neighbor->Position(), particle->Position()), m_kernelRadius); density += tempDensity; } - density = m_mass * density; + density = FluidParticle::mass * density; particle->SetDensity(density); } @@ -149,7 +169,7 @@ SPHSolver::CalculatePressureForceField( ); pressureGrad += tempPressureForce * kernelGrad; } - pressureGrad = -pressureGrad * m_mass * m_mass; + pressureGrad = -pressureGrad * FluidParticle::mass * FluidParticle::mass; particle->SetPressureForce(pressureGrad); // particle->SetColor(glm::vec4(glm::abs(pressureGrad), 1.f)); } @@ -166,7 +186,7 @@ SPHSolver::CalculateViscosityForceField( glm::vec3 tempVisForce = (neighbor->Velocity() - particle->Velocity()) * laplacianKernelViscous / neighbor->Density(); viscosityForce += tempVisForce; } - viscosityForce = viscosityForce * m_mass * m_viscosity; + viscosityForce = viscosityForce * FluidParticle::mass * m_viscosity; particle->SetViscosityForce(viscosityForce); } diff --git a/src/fluidSolver/SPHSolver.h b/src/fluidSolver/SPHSolver.h index fa69b404..567c7abb 100644 --- a/src/fluidSolver/SPHSolver.h +++ b/src/fluidSolver/SPHSolver.h @@ -5,6 +5,13 @@ #include #include +typedef enum { + SPHStiffness, + SPHViscosity, + SPHRestDensity, + SPHMass +} SPHConstantType; + // ---------------------------------------------------- // // SPHSolver // ---------------------------------------------------- // @@ -19,12 +26,15 @@ class SPHSolver : public FluidSolver const float stiffness, const float viscosity, const float mass, - const float restDensity, - const float timestep + const float restDensity ); virtual ~SPHSolver(); + void SetConstant( + SPHConstantType type, + float value + ); virtual void Update(const float deltaT); void AddParticle(FluidParticle*); @@ -37,7 +47,6 @@ class SPHSolver : public FluidSolver float m_stiffness; float m_viscosity; float m_restDensity; - float m_timestep; float m_kernelRadius; void CalculateDensity( diff --git a/src/fluidSolver/fluidParticle.cpp b/src/fluidSolver/fluidParticle.cpp index a6b60fd9..c193fa72 100644 --- a/src/fluidSolver/fluidParticle.cpp +++ b/src/fluidSolver/fluidParticle.cpp @@ -1,5 +1,7 @@ #include "FluidParticle.h" +float FluidParticle::mass = 0.125f; + // ---------------------------------------------------- // // FluidParticle // ---------------------------------------------------- // @@ -25,7 +27,7 @@ FluidParticle::Update( // @todo: HACK HERE, should read cell size from json! float cellSize = 0.10001; - glm::vec3 gravity(0.0f, MASS * GRAVITY, 0.0f); + glm::vec3 gravity(0.0f, FluidParticle::mass * GRAVITY, 0.0f); glm::vec3 totalForce = m_accel + m_pressureForce + m_viscosityForce + gravity; m_vel = m_vel + totalForce * deltaT; diff --git a/src/fluidSolver/fluidParticle.h b/src/fluidSolver/fluidParticle.h index 56397a1e..88d256ee 100644 --- a/src/fluidSolver/fluidParticle.h +++ b/src/fluidSolver/fluidParticle.h @@ -9,7 +9,6 @@ using namespace std; #define NEIGHBORS_MAX 1000 -#define MASS 0.125f // ---------------------------------------------------- // // FluidParticle @@ -17,6 +16,9 @@ using namespace std; class FluidParticle { public: + + static float mass; + FluidParticle(); FluidParticle( const glm::vec3& pos, @@ -32,7 +34,7 @@ class FluidParticle inline const glm::vec3& Velocity() const { return m_vel; } inline void SetVelocity(const glm::vec3& vel) { m_vel = vel; } inline const glm::vec3& Acceleration() const { return m_accel; } - inline void SetForce(const glm::vec3& force) { m_accel = force / MASS; } + inline void SetForce(const glm::vec3& force) { m_accel = force / FluidParticle::mass; } inline const glm::vec4& Color() const { return m_col; } inline void SetColor(const glm::vec4& color) { m_col = color; } inline const float& Density() const { return m_density; } diff --git a/src/fluidSolver/fluidSolver.cpp b/src/fluidSolver/fluidSolver.cpp index e4b7fd13..295e0403 100644 --- a/src/fluidSolver/fluidSolver.cpp +++ b/src/fluidSolver/fluidSolver.cpp @@ -10,8 +10,7 @@ FluidSolver::FluidSolver( const float mass ) : m_containerDim(containerDim), m_particleDim(particleDim), - m_separation(separation), - m_mass(mass) + m_separation(separation) { // -- Initialize all particles float midX = m_particleDim.x / 2.f; @@ -37,6 +36,9 @@ FluidSolver::FluidSolver( // -- Set container boundary m_minBoundary = -m_containerDim / 2.0f; m_maxBoundary = m_containerDim / 2.0f; + + // -- Set particle properties + FluidParticle::mass = mass; } FluidSolver::~FluidSolver() diff --git a/src/fluidSolver/fluidSolver.h b/src/fluidSolver/fluidSolver.h index 8863bbc5..ae145125 100644 --- a/src/fluidSolver/fluidSolver.h +++ b/src/fluidSolver/fluidSolver.h @@ -41,7 +41,6 @@ class FluidSolver glm::vec3 m_minBoundary; glm::vec3 m_maxBoundary; float m_separation; - float m_mass; std::vector m_particles; }; diff --git a/src/input/keyboardControl.h b/src/input/keyboardControl.h index 684db284..57cc4630 100644 --- a/src/input/keyboardControl.h +++ b/src/input/keyboardControl.h @@ -17,6 +17,12 @@ typedef enum { Key_Space = GLFW_KEY_SPACE } KeyCode; +typedef enum { + Key_Pressed = GLFW_PRESS, + Key_Released = GLFW_RELEASE, + Key_Repeat = GLFW_REPEAT +} KeyAction; + class KeyboardControl { public: diff --git a/src/main.cpp b/src/main.cpp index f59ac7ce..0bfdd1b9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -72,10 +72,29 @@ main(int argv, char* argc[]) { TestSuite(); #endif - Viewer view(800, 600); - view.Update(); - view.CleanUp(); - + nanogui::ref app; + + try { + nanogui::init(); + + { + app = new Viewer(1600, 900); + app->drawAll(); + app->setVisible(true); + nanogui::mainloop(); + } + + nanogui::shutdown(); + } catch (const std::runtime_error &e) { + std::string error_msg = std::string("Caught a fatal error: ") + std::string(e.what()); + #if defined(_WIN32) + MessageBoxA(nullptr, error_msg.c_str(), NULL, MB_ICONERROR | MB_OK); + #else + std::cerr << error_msg << endl; + #endif + app->CleanUp(); + return -1; + } return 0; } diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 93b3198c..d9210ad3 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -70,7 +70,6 @@ Scene::InitFromJson( const float viscosity = root["viscosity"].asFloat(); const float mass = root["mass"].asFloat(); const float restDensity = root["restDensity"].asFloat(); - const float timestep = root["timestep"].asFloat(); // -- Initialize fluid solvers m_fluidSolver = new SPHSolver( @@ -81,8 +80,7 @@ Scene::InitFromJson( stiffness, viscosity, mass, - restDensity, - timestep + restDensity ); // -- Initialize fluid geo @@ -95,18 +93,44 @@ Scene::InitFromJson( m_fluidGeo->Create(); } +void +Scene::SetConstant( + SPHConstantType type, + float value + ) +{ + if (m_fluidSolver) { + m_fluidSolver->SetConstant(type, value); + } +} + +void +Scene::ReadInputs( + KeyCode key, + KeyAction action + ) +{ + if (key == Key_Space && action == Key_Pressed) { + this->Pause(); + } + + if (action == Key_Repeat) { + UpdateCamera(key); + } +} + +void +Scene::Pause() +{ + m_paused = !m_paused; +} + void Scene::Update( const float deltaT, - const KeyboardControl* kc, ParticleAdvectProgram& progAdvect ) { - UpdateCamera(deltaT, kc); - - if (kc->KeyPressed(Key_Space)) { - m_paused = !m_paused; - } if (m_paused) { return; } @@ -118,6 +142,7 @@ Scene::Draw( const ShaderProgram& prog ) const { + prog.Draw(*m_camera, *m_testBox); } void @@ -157,12 +182,11 @@ Scene::CleanUp() void Scene::UpdateCamera( - const float deltaT, - const KeyboardControl* kc + KeyCode key ) { // Camera mode - if (kc->KeyPressed(Key_P)) { + if (key == Key_P) { static bool perspective = true; perspective = !perspective; m_camera->EnablePerspective(perspective); @@ -171,34 +195,34 @@ Scene::UpdateCamera( // Camera movement float rotateAmt = 20.0f; float zoomAmt = 0.01f; - if (kc->KeyPressed(Key_Up)) { + if (key == Key_Up) { m_camera->RotateAboutUp(rotateAmt); } - if (kc->KeyPressed(Key_Down)) { + if (key == Key_Down) { m_camera->RotateAboutUp(-rotateAmt); } - if (kc->KeyPressed(Key_Left)) { + if (key == Key_Left) { m_camera->RotateAboutRight(rotateAmt); } - if (kc->KeyPressed(Key_Right)) { + if (key == Key_Right) { m_camera->RotateAboutRight(-rotateAmt); } - if (kc->KeyPressed(Key_W)){ + if (key == Key_W){ m_camera->Zoom(zoomAmt); } - if (kc->KeyPressed(Key_S)){ + if (key == Key_S){ m_camera->Zoom(-zoomAmt); } - if (kc->KeyPressed(Key_D)){ + if (key == Key_D){ m_camera->TranslateAlongRight(zoomAmt); } - if (kc->KeyPressed(Key_A)){ + if (key == Key_A){ m_camera->TranslateAlongRight(-zoomAmt); } } diff --git a/src/scene/scene.h b/src/scene/scene.h index 1177d6e5..d7f8c753 100644 --- a/src/scene/scene.h +++ b/src/scene/scene.h @@ -19,10 +19,15 @@ class Scene void InitFromTestScene(); void InitFromJson(const char* filepath); + void Pause(); + void SetConstant( + SPHConstantType type, + float value + ); + virtual void ReadInputs(KeyCode key, KeyAction action); virtual void Update( const float deltaT, - const KeyboardControl*, ParticleAdvectProgram& prog ); virtual void Draw(const ShaderProgram&) const; @@ -41,9 +46,7 @@ class Scene SPHSolver* m_fluidSolver; // -- Update helpers - void UpdateCamera( - const float deltaT, - const KeyboardControl*); + void UpdateCamera(KeyCode key); void UpdateFluidSolver( const float deltaT, ParticleAdvectProgram& prog diff --git a/src/scene/scene.json b/src/scene/scene.json index 16adb327..31055541 100644 --- a/src/scene/scene.json +++ b/src/scene/scene.json @@ -9,11 +9,10 @@ "boundY" : 1.0, "boundZ" : 1.0 }, - "stiffness": 1000, + "stiffness": 10, "viscosity": 0.0, "mass": 0.125, "restDensity": 1000, "cellSize": 0.100001, - "particleSeparation": 0.05, - "timestep": 0.001 + "particleSeparation": 0.05 } diff --git a/src/shaderProgram/particleAdvectProgram.cpp b/src/shaderProgram/particleAdvectProgram.cpp index 24471bde..b4d8a94d 100644 --- a/src/shaderProgram/particleAdvectProgram.cpp +++ b/src/shaderProgram/particleAdvectProgram.cpp @@ -52,7 +52,7 @@ ParticleAdvectProgram::ParticleAdvectProgram( glGenTransformFeedbacks(2, &m_TFBuffers[0]); // -- Set big point size - glPointSize(3.0f); + glPointSize(5.0f); } void diff --git a/src/thirdparty/nanogui b/src/thirdparty/nanogui new file mode 160000 index 00000000..3c31c570 --- /dev/null +++ b/src/thirdparty/nanogui @@ -0,0 +1 @@ +Subproject commit 3c31c57087f3a140dfafbefd3bb27cc5ce6d05c7 diff --git a/src/viewer.cpp b/src/viewer.cpp index 8807b57e..abf881b4 100644 --- a/src/viewer.cpp +++ b/src/viewer.cpp @@ -1,13 +1,9 @@ #include "viewer.h" -Viewer::Viewer() - : Viewer(800, 600) -{} - Viewer::Viewer( int width, int height - ) : m_window(nullptr), + ) : nanogui::Screen(Eigen::Vector2i(width, height), "Korra Fluid Simulation"), m_program(nullptr), m_programAdvect(nullptr), m_scene(nullptr) @@ -39,16 +35,6 @@ Viewer::Init() // Make sure with don't use old profile glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); - // -- Open a window and create its OpenGL context - m_window = glfwCreateWindow( m_width, m_height, "Trung's Fluid Sim", NULL, NULL); - if( m_window == NULL ){ - fprintf( stderr, "Failed to open GLFW window.\n" ); - getchar(); - this->CleanUp(); - } - - glfwMakeContextCurrent(m_window); - // -- Initialize GLEW // *N.B*: Strange we have to do this to make glGenVertexArrays to work? @@ -61,9 +47,6 @@ Viewer::Init() this->CleanUp(); } - // -- Initialize inputs - m_keyboard = new KeyboardControl(m_window); - // -- Initialize shader program m_program = new ShaderProgram("../src/glsl/simple.vert", "../src/glsl/simple.frag"); m_programAdvect = new ParticleAdvectProgram( @@ -84,36 +67,197 @@ Viewer::Init() m_scene->InitFromJson("../src/scene/scene.json"); #endif - // -- Clear background - glClearColor(0.2f, 0.2f, 0.2f, 1.0f); + // -- Initialize GUI + InitGUI(); } void -Viewer::Update() +Viewer::InitGUI() { - do - { - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + Window* toolbox = new Window(this, "Bending parameters"); + toolbox->setPosition(Vector2i(20, 20)); + toolbox->setLayout(new GroupLayout()); + + // -- Diagnostics + Widget* widget = new Widget(toolbox); + GridLayout *layout = + new GridLayout( + Orientation::Horizontal, + 2, + Alignment::Middle, + 15, + 5); + layout->setColAlignment( + { Alignment::Maximum, Alignment::Fill }); + layout->setSpacing(0, 10); + widget->setLayout(layout); + + // FPS + new Label(widget, "FPS: ", "sans-bold"); + m_labelFps = new Label(widget, to_string(m_fps), "sans-bold"); + + // Paricle infos + + // -- Parameter widgets + widget = new Widget(toolbox); + layout = + new GridLayout( + Orientation::Horizontal, + 3, + Alignment::Middle, + 15, + 5); + layout->setColAlignment( + { Alignment::Maximum, Alignment::Fill }); + layout->setSpacing(0, 10); + widget->setLayout(layout); + + // Global constants + + new Label(widget, "Mass: ", "sans-bold"); + Slider *slider = new Slider(widget); + Label* label = new Label(widget, "0.125", "sans-bold"); + slider->setValue(0.0f); + slider->setCallback([this, label](float value) { + float mass = value + 0.125f; + m_scene->SetConstant(SPHMass, mass); + label->setCaption(to_string(mass)); + }); + + new Label(widget, "Stiffness: ", "sans-bold"); + slider = new Slider(widget); + label = new Label(widget, "1000", "sans-bold"); + slider->setValue(0.5f); + slider->setCallback([this, label](float value) { + float stiffness = value * 2000.0f; + m_scene->SetConstant(SPHStiffness, stiffness); + label->setCaption(to_string(stiffness)); + }); + + new Label(widget, "Viscosity: ", "sans-bold"); + slider = new Slider(widget); + label = new Label(widget, "0", "sans-bold"); + slider->setValue(0.0f); + slider->setCallback([this, label](float value) { + float viscosity = value * 100.0f; + m_scene->SetConstant(SPHViscosity, viscosity); + label->setCaption(to_string(viscosity)); + }); + + new Label(widget, "Rest Density: ", "sans-bold"); + slider = new Slider(widget); + label = new Label(widget, "1000", "sans-bold"); + slider->setValue(0.5f); + slider->setCallback([this, label](float value) { + float restDensity = value * 2000.0f; + m_scene->SetConstant(SPHRestDensity, restDensity); + label->setCaption(to_string(restDensity)); + }); + + new Label(widget, "Time step: ", "sans-bold"); + slider = new Slider(widget); + label = new Label(widget, "0.001", "sans-bold"); + slider->setValue(0.1f); + slider->setCallback([this, label](float value) { + m_timeStep = value / 100.0f + 0.001f; + label->setCaption(to_string(m_timeStep)); + }); + + // -- Coloring particles + // widget = new Widget(toolbox); + // widget->setLayout(new GroupLayout()); + // new Label(widget, "Combo box", "sans-bold"); + // ComboBox* new ComboBox(widget, { "Combo box item 1", "Combo box item 2", "Combo box item 3"}); + // slider->setCallback([this, label](float value) { + // m_timeStep = value / 100.0f + 0.001f; + // label->setCaption(to_string(m_timeStep)); + // }); + + + // -- Buttons + widget = new Widget(toolbox); + widget->setLayout(new GroupLayout()); + + // Pause + Button* b = new Button(widget, "Pause"); + b->setCallback([this] { + m_scene->Pause(); + }); + + // Replay + b = new Button(widget, "Replay"); + b->setCallback([this] { + this->ReplayScene(); + }); + + // Quit + b = new Button(widget, "Quit"); + b->setCallback([this] { + this->CleanUp(); + }); - static float lastTime = glfwGetTime(); - float currentTime = glfwGetTime(); - float deltaTime = float(currentTime - lastTime); - m_scene->Update(deltaTime, m_keyboard, *m_programAdvect); + performLayout(mNVGContext); +} + +void +Viewer::ReplayScene() +{ + if (m_scene != nullptr) { + delete m_scene; + } + m_scene = new Scene(m_width, m_height); + m_scene->InitFromJson("../src/scene/scene.json"); +} + +void +Viewer::draw( + NVGcontext *ctx + ) +{ + Screen::draw(ctx); +} + +void +Viewer::drawContents() +{ + static double lastTime = glfwGetTime(); + double currentTime = glfwGetTime(); + double deltaTime = double(currentTime - lastTime); + m_fps = 1.0 / deltaTime; + m_labelFps->setCaption(to_string(m_fps)); #ifdef TEST_SCENE - m_scene->Draw(*m_program); + m_scene->Draw(*m_program); #else - m_scene->DrawTransformFeedback(*m_program, *m_programAdvect); + m_scene->Update(m_timeStep, *m_programAdvect); + m_scene->DrawTransformFeedback(*m_program, *m_programAdvect); #endif - // Swap buffers - glfwSwapBuffers(m_window); - glfwPollEvents(); +} + +bool +Viewer::keyboardEvent( + int key, + int scancode, + int action, + int modifiers) +{ + if (Screen::keyboardEvent(key, scancode, action, modifiers)) + return true; + + // Quit + if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { + this->CleanUp(); + return true; } - while( - m_keyboard->KeyPressed(Key_Escape) == false && - glfwWindowShouldClose(m_window) == 0 - ); + + // Replay + if (key == GLFW_KEY_R && action == GLFW_PRESS) { + this->ReplayScene(); + } + + m_scene->ReadInputs((KeyCode)key, (KeyAction)action); + return false; } void @@ -127,7 +271,9 @@ Viewer::CleanUp() delete m_scene; delete m_programAdvect; delete m_program; - delete m_keyboard; + + setVisible(false); + nanogui::shutdown(); // Close OpenGL window and terminate GLFW glfwTerminate(); diff --git a/src/viewer.h b/src/viewer.h index ca6107c1..67da112a 100644 --- a/src/viewer.h +++ b/src/viewer.h @@ -12,6 +12,19 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(_WIN32) +#include +#endif +// #include +#include #include #include @@ -19,17 +32,20 @@ #include #include -class Viewer +using namespace nanogui; + +class Viewer :public nanogui::Screen { public: - Viewer(); Viewer( int width = 800, int height = 600 ); - virtual void Update(); virtual void CleanUp(); + virtual void draw(NVGcontext *ctx); + virtual void drawContents(); + virtual bool keyboardEvent(int key, int scancode, int action, int modifiers); float Width() { return m_width; @@ -41,12 +57,15 @@ class Viewer protected: void Init(); + void InitGUI(); + void ReplayScene(); float m_width; float m_height; + float m_timeStep = 0.001f; + float m_fps; + Label* m_labelFps; - GLFWwindow* m_window; - KeyboardControl* m_keyboard; ShaderProgram* m_program; ParticleAdvectProgram* m_programAdvect; Scene* m_scene; From d6cca9cb02601309426adb06615d70deadc3e7d8 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Fri, 1 Apr 2016 19:10:25 -0400 Subject: [PATCH 46/64] [SPRINT 3] - Added submodule for nanogui --- .gitmodules | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitmodules b/.gitmodules index 234a8024..391a0e8a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,7 @@ [submodule "src/thirdparty/jsoncpp"] path = src/thirdparty/jsoncpp url = git@github.com:open-source-parsers/jsoncpp.git + +[submodule "src/thirdparty/nanogui"] + path = src/thirdparty/nanogui + url = git@github.com:wjakob/nanogui.git From 782cd5b337786c52d918bf574e7ee06d624f6204 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Fri, 1 Apr 2016 19:27:24 -0400 Subject: [PATCH 47/64] [SPRINT 3] - Added installation instructions and install.sh --- README.md | 31 ++++++++++++++++++++----------- install.sh | 18 ++++++++++++++++++ 2 files changed, 38 insertions(+), 11 deletions(-) create mode 100644 install.sh diff --git a/README.md b/README.md index 900aa844..10c7b402 100644 --- a/README.md +++ b/README.md @@ -27,27 +27,36 @@ Details will be updated as more features are coming soon. ## Compiling -1. Clone using git clone --recursive https://github.com/trungtle/KorraFluidSolver +1. Clone using git clone --recursive git@github.com:trungtle/KorraFluidSolver.git -If you forgot to call --recursive, you can recover the submodules by calling git submodule update --init --recursive - -2. Cd to directory (note, this is for Unix build) +If you forgot to call --recursive, you can recover the submodules by ``` -mkdir build -cd build -cmake -G "Unix Makefiles" ../ +cd CIS563-FluidSolver (at top directory) +git submodule update --init --recursive ``` -or + +There should be 2 submodules for `jsoncpp` and `nanogui` + +2. Compile nanogui with cmake and make ``` -cmake -G "Xcode" ../ +cd src/thirdparty/nanogui +cmake -G "Unix Makefiles" . +make ``` -then +3. Compile Korra with cmake and make +``` +cd ../../../ (at top directory) +mkdir build +cd build +cmake -G "Unix Makefiles" ../ +make +4. Launch ``` ./Korra ``` -to launch the application. + ## Classes diff --git a/install.sh b/install.sh new file mode 100644 index 00000000..14c811d5 --- /dev/null +++ b/install.sh @@ -0,0 +1,18 @@ +#!/bin/sh +# clone submodules +git submodule update --init --recursive + +# compile nanogui +cd src/thirdparty/nanogui +cmake -G "Unix Makefiles" . +make + +# compile Korra +cd ../../../ +mkdir build +cd build +cmake -G "Unix Makefiles" ../ +make + +#launch +./Korra From 9a8f064d6c60f95b02a7ca8c9f29241924c80cb3 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Fri, 1 Apr 2016 19:28:33 -0400 Subject: [PATCH 48/64] [SPRINT 3] - Make install.sh executable --- install.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 install.sh diff --git a/install.sh b/install.sh old mode 100644 new mode 100755 From f54bdfd55cba5db738bc5535d9001ce8e5bd63fa Mon Sep 17 00:00:00 2001 From: Trung Le Date: Fri, 1 Apr 2016 19:32:21 -0400 Subject: [PATCH 49/64] [SPRINT 3] - Updated instruction for simple install.sh --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 10c7b402..e33805b3 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,12 @@ __Korra__ is a cross-platform C++ fluid SPH solver, designed and built for CIS56 Details will be updated as more features are coming soon. -## Compiling +## Automatic build (easy!) +``` +./install.sh +``` + +## Manual build (if you prefer doing it manually) 1. Clone using git clone --recursive git@github.com:trungtle/KorraFluidSolver.git From e0598e3fe8d3b3f75828428aa4946fac56da5d4c Mon Sep 17 00:00:00 2001 From: Trung Le Date: Sat, 2 Apr 2016 01:36:11 -0400 Subject: [PATCH 50/64] [SRPINT 3] - Added combo box to select particle colors --- src/fluidSolver/SPHSolver.cpp | 1 - src/fluidSolver/fluidParticle.cpp | 7 ++++--- src/fluidSolver/fluidParticle.h | 30 ++++++++++++++++++++++++++---- src/fluidSolver/fluidSolver.cpp | 8 +++++--- src/fluidSolver/fluidSolver.h | 11 +++++++---- src/viewer.cpp | 15 +++++++-------- 6 files changed, 49 insertions(+), 23 deletions(-) diff --git a/src/fluidSolver/SPHSolver.cpp b/src/fluidSolver/SPHSolver.cpp index c42a92bd..77f57fa1 100644 --- a/src/fluidSolver/SPHSolver.cpp +++ b/src/fluidSolver/SPHSolver.cpp @@ -171,7 +171,6 @@ SPHSolver::CalculatePressureForceField( } pressureGrad = -pressureGrad * FluidParticle::mass * FluidParticle::mass; particle->SetPressureForce(pressureGrad); - // particle->SetColor(glm::vec4(glm::abs(pressureGrad), 1.f)); } void diff --git a/src/fluidSolver/fluidParticle.cpp b/src/fluidSolver/fluidParticle.cpp index c193fa72..0c9d4e5d 100644 --- a/src/fluidSolver/fluidParticle.cpp +++ b/src/fluidSolver/fluidParticle.cpp @@ -1,6 +1,7 @@ #include "FluidParticle.h" float FluidParticle::mass = 0.125f; +SPHColor FluidParticle::colorType = SPHColorSimple; // ---------------------------------------------------- // // FluidParticle @@ -27,9 +28,9 @@ FluidParticle::Update( // @todo: HACK HERE, should read cell size from json! float cellSize = 0.10001; - glm::vec3 gravity(0.0f, FluidParticle::mass * GRAVITY, 0.0f); - glm::vec3 totalForce = m_accel + m_pressureForce + m_viscosityForce + gravity; - m_vel = m_vel + totalForce * deltaT; + glm::vec3 gravity(0.0f, GRAVITY, 0.0f); + m_force = (m_pressureForce + m_viscosityForce + gravity); + m_vel = m_vel + m_force * deltaT / FluidParticle::mass; // -- Enforce CFL condition float vMax = 0.4 * cellSize / deltaT; diff --git a/src/fluidSolver/fluidParticle.h b/src/fluidSolver/fluidParticle.h index 88d256ee..0a9332ee 100644 --- a/src/fluidSolver/fluidParticle.h +++ b/src/fluidSolver/fluidParticle.h @@ -10,6 +10,14 @@ using namespace std; #define NEIGHBORS_MAX 1000 +typedef enum { + SPHColorSimple, + SPHColorPressure, + SPHColorViscosity, + SPHColorVelocity, + SPHColorAllForces +} SPHColor; + // ---------------------------------------------------- // // FluidParticle // ---------------------------------------------------- // @@ -18,6 +26,7 @@ class FluidParticle public: static float mass; + static SPHColor colorType; FluidParticle(); FluidParticle( @@ -33,9 +42,22 @@ class FluidParticle inline void SetPosition(const glm::vec3& pos) { m_pos = pos; } inline const glm::vec3& Velocity() const { return m_vel; } inline void SetVelocity(const glm::vec3& vel) { m_vel = vel; } - inline const glm::vec3& Acceleration() const { return m_accel; } - inline void SetForce(const glm::vec3& force) { m_accel = force / FluidParticle::mass; } - inline const glm::vec4& Color() const { return m_col; } + inline const glm::vec4& Color() const { + switch(FluidParticle::colorType) { + case SPHColorSimple: + return m_col; + case SPHColorPressure: + return glm::vec4(glm::abs(m_pressureForce), 1.0f); + case SPHColorViscosity: + return glm::vec4(glm::abs(m_viscosityForce), 1.0f); + case SPHColorVelocity: + return glm::vec4(glm::abs(m_vel), 1.0f); + case SPHColorAllForces: + return glm::vec4(glm::abs(m_force), 1.0f); + default: + return m_col; + } + } inline void SetColor(const glm::vec4& color) { m_col = color; } inline const float& Density() const { return m_density; } inline void SetDensity(float density) { m_density = density; } @@ -65,10 +87,10 @@ class FluidParticle protected: glm::vec3 m_pos; glm::vec3 m_vel; - glm::vec3 m_accel; glm::vec4 m_col; glm::vec3 m_pressureForce; glm::vec3 m_viscosityForce; + glm::vec3 m_force; float m_pressure = 0.0f; float m_density = 1.0f; float m_spawnTime = 0.0f; diff --git a/src/fluidSolver/fluidSolver.cpp b/src/fluidSolver/fluidSolver.cpp index 295e0403..5f8a06e1 100644 --- a/src/fluidSolver/fluidSolver.cpp +++ b/src/fluidSolver/fluidSolver.cpp @@ -91,10 +91,12 @@ FluidSolver::ParticleSpawnTimes() const const std::vector FluidSolver::ParticleColors() const { + int len = m_particles.size(); std::vector colors; - for (auto &particle : m_particles) - { - colors.push_back(particle->Color()); + colors.resize(len); + for (int i = 0; i < len; ++i) { + FluidParticle* particle = m_particles[i]; + colors.insert(colors.begin() + i, particle->Color()); } return colors; diff --git a/src/fluidSolver/fluidSolver.h b/src/fluidSolver/fluidSolver.h index ae145125..76b4ad68 100644 --- a/src/fluidSolver/fluidSolver.h +++ b/src/fluidSolver/fluidSolver.h @@ -2,6 +2,7 @@ #define FLUIDSOLVER_H #include +#include #include #include #include @@ -12,6 +13,8 @@ #include #include +using namespace tbb; + // ---------------------------------------------------- // // FluidSolver // ---------------------------------------------------- // @@ -26,10 +29,10 @@ class FluidSolver ); virtual ~FluidSolver(); - const std::vector ParticlePositions() const; - const std::vector ParticleVelocities() const; - const std::vector ParticleSpawnTimes() const; - const std::vector ParticleColors() const; + virtual const std::vector ParticlePositions() const; + virtual const std::vector ParticleVelocities() const; + virtual const std::vector ParticleSpawnTimes() const; + virtual const std::vector ParticleColors() const; virtual void Update(const float deltaT); diff --git a/src/viewer.cpp b/src/viewer.cpp index abf881b4..94bfe160 100644 --- a/src/viewer.cpp +++ b/src/viewer.cpp @@ -164,14 +164,13 @@ Viewer::InitGUI() }); // -- Coloring particles - // widget = new Widget(toolbox); - // widget->setLayout(new GroupLayout()); - // new Label(widget, "Combo box", "sans-bold"); - // ComboBox* new ComboBox(widget, { "Combo box item 1", "Combo box item 2", "Combo box item 3"}); - // slider->setCallback([this, label](float value) { - // m_timeStep = value / 100.0f + 0.001f; - // label->setCaption(to_string(m_timeStep)); - // }); + widget = new Widget(toolbox); + widget->setLayout(new GroupLayout()); + new Label(widget, "Particle color", "sans-bold"); + ComboBox* cb = new ComboBox(widget, { "Simple", "Pressure", "Viscosity", "Velocity", "All forces"}); + cb->setCallback([this](int index) { + FluidParticle::colorType = (SPHColor)index; + }); // -- Buttons From 4fd8d003b0a5ee8cf62917de63c04d01022b009d Mon Sep 17 00:00:00 2001 From: Trung Le Date: Sat, 2 Apr 2016 03:36:24 -0400 Subject: [PATCH 51/64] [SPRINT 3] - Fixed FPS. Added info about number of particles in sim --- src/fluidSolver/fluidSolver.h | 4 ++++ src/scene/scene.h | 8 ++++++++ src/scene/scene.json | 6 +++--- src/viewer.cpp | 16 ++++++++++------ src/viewer.h | 2 +- 5 files changed, 26 insertions(+), 10 deletions(-) diff --git a/src/fluidSolver/fluidSolver.h b/src/fluidSolver/fluidSolver.h index 76b4ad68..2b0db35c 100644 --- a/src/fluidSolver/fluidSolver.h +++ b/src/fluidSolver/fluidSolver.h @@ -36,6 +36,10 @@ class FluidSolver virtual void Update(const float deltaT); + inline const uint NumParticles() const { + return m_particles.size(); + } + protected: // -- Particle info diff --git a/src/scene/scene.h b/src/scene/scene.h index d7f8c753..b920352e 100644 --- a/src/scene/scene.h +++ b/src/scene/scene.h @@ -37,6 +37,14 @@ class Scene ); virtual void CleanUp(); + inline const uint NumParticles() const { + if (m_fluidSolver) { + return m_fluidSolver->NumParticles(); + } else { + return 0; + } + } + protected: bool m_paused = false; Camera* m_camera; diff --git a/src/scene/scene.json b/src/scene/scene.json index 31055541..76dee50c 100644 --- a/src/scene/scene.json +++ b/src/scene/scene.json @@ -1,8 +1,8 @@ { "containerDim" : { - "scaleX" : 3.0, - "scaleY" : 3.0, - "scaleZ" : 3.0 + "scaleX" : 1.5, + "scaleY" : 2.0, + "scaleZ" : 1.5 }, "particleDim" : { "boundX" : 1.0, diff --git a/src/viewer.cpp b/src/viewer.cpp index 94bfe160..0b58ebd9 100644 --- a/src/viewer.cpp +++ b/src/viewer.cpp @@ -96,6 +96,9 @@ Viewer::InitGUI() new Label(widget, "FPS: ", "sans-bold"); m_labelFps = new Label(widget, to_string(m_fps), "sans-bold"); + new Label(widget, "Number of particles: ", "sans-bold"); + new Label(widget, to_string(m_scene->NumParticles()), "sans-bold"); + // Paricle infos // -- Parameter widgets @@ -126,10 +129,10 @@ Viewer::InitGUI() new Label(widget, "Stiffness: ", "sans-bold"); slider = new Slider(widget); - label = new Label(widget, "1000", "sans-bold"); - slider->setValue(0.5f); + label = new Label(widget, "10", "sans-bold"); + slider->setValue(0.f); slider->setCallback([this, label](float value) { - float stiffness = value * 2000.0f; + float stiffness = value * 1000.0f + 10.f; m_scene->SetConstant(SPHStiffness, stiffness); label->setCaption(to_string(stiffness)); }); @@ -139,7 +142,7 @@ Viewer::InitGUI() label = new Label(widget, "0", "sans-bold"); slider->setValue(0.0f); slider->setCallback([this, label](float value) { - float viscosity = value * 100.0f; + float viscosity = value * 10.0f; m_scene->SetConstant(SPHViscosity, viscosity); label->setCaption(to_string(viscosity)); }); @@ -167,7 +170,7 @@ Viewer::InitGUI() widget = new Widget(toolbox); widget->setLayout(new GroupLayout()); new Label(widget, "Particle color", "sans-bold"); - ComboBox* cb = new ComboBox(widget, { "Simple", "Pressure", "Viscosity", "Velocity", "All forces"}); + ComboBox* cb = new ComboBox(widget, { "Default", "Pressure", "Viscosity", "Velocity", "All forces"}); cb->setCallback([this](int index) { FluidParticle::colorType = (SPHColor)index; }); @@ -219,10 +222,11 @@ Viewer::draw( void Viewer::drawContents() { + // -- Update fps static double lastTime = glfwGetTime(); double currentTime = glfwGetTime(); double deltaTime = double(currentTime - lastTime); - m_fps = 1.0 / deltaTime; + m_fps = 1000.0 / deltaTime; m_labelFps->setCaption(to_string(m_fps)); #ifdef TEST_SCENE diff --git a/src/viewer.h b/src/viewer.h index 67da112a..6459d757 100644 --- a/src/viewer.h +++ b/src/viewer.h @@ -63,7 +63,7 @@ class Viewer :public nanogui::Screen float m_width; float m_height; float m_timeStep = 0.001f; - float m_fps; + uint m_fps; Label* m_labelFps; ShaderProgram* m_program; From 440e0b994c19007bda489f91449c27441be25d33 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Sat, 2 Apr 2016 03:37:36 -0400 Subject: [PATCH 52/64] [SPRINT 3] - Added instruction to launch Korra --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index e33805b3..de50301e 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,7 @@ Details will be updated as more features are coming soon. ``` ./install.sh ``` +The application `Korra` will be created in build folder. Simply run `./Korra` to launch. ## Manual build (if you prefer doing it manually) From 8f9882a4fcc5172798e07886a84ff3d6eb247021 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Wed, 6 Apr 2016 10:57:08 -0400 Subject: [PATCH 53/64] [SPRINT 3] - UPdated boundary conditions --- src/fluidSolver/SPHSolver.cpp | 24 ++++++++++++++++++------ src/scene/scene.json | 4 ++-- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/fluidSolver/SPHSolver.cpp b/src/fluidSolver/SPHSolver.cpp index 77f57fa1..61f5ba2c 100644 --- a/src/fluidSolver/SPHSolver.cpp +++ b/src/fluidSolver/SPHSolver.cpp @@ -203,26 +203,38 @@ SPHSolver::UpdateDynamics( glm::vec3 newVel = particle->Velocity(); if (particle->Position().x < m_minBoundary.x) { newPosition.x = m_minBoundary.x + 0.0f; - newVel.x = -newVel.x * 0.1; + newVel.x = -newVel.x * 0.5; + newVel.y *= 1.1; + newVel.z *= 1.1; } else if (particle->Position().x > m_maxBoundary.x) { newPosition.x = m_maxBoundary.x - 0.0f; - newVel.x = -newVel.x * 0.1; + newVel.x = -newVel.x * 0.5; + newVel.y *= 1.1; + newVel.z *= 1.1; } if (particle->Position().y < m_minBoundary.y) { newPosition.y = m_minBoundary.y + 0.0f; - newVel.y = -newVel.y * 0.1; + newVel.y = -newVel.y * 0.5; + newVel.x *= 1.1; + newVel.z *= 1.1; } else if (particle->Position().y > m_maxBoundary.y) { newPosition.y = m_maxBoundary.y - 0.0f; - newVel.y = -newVel.y * 0.1f; + newVel.y = -newVel.y * 0.5f; + newVel.x *= 1.1; + newVel.z *= 1.1; } if (particle->Position().z < m_minBoundary.z) { newPosition.z = m_minBoundary.z + 0.0f; - newVel.z = -newVel.z * 0.1; + newVel.z = -newVel.z * 0.5; + newVel.x *= 1.1; + newVel.y *= 1.1; } else if (particle->Position().z > m_maxBoundary.z) { newPosition.z = m_maxBoundary.z - 0.0f; - newVel.z = -newVel.z * 0.1; + newVel.z = -newVel.z * 0.5; + newVel.x *= 1.1; + newVel.y *= 1.1; } particle->SetPosition(newPosition); diff --git a/src/scene/scene.json b/src/scene/scene.json index 76dee50c..64aab3c7 100644 --- a/src/scene/scene.json +++ b/src/scene/scene.json @@ -1,8 +1,8 @@ { "containerDim" : { - "scaleX" : 1.5, + "scaleX" : 2.0, "scaleY" : 2.0, - "scaleZ" : 1.5 + "scaleZ" : 2.0 }, "particleDim" : { "boundX" : 1.0, From 7671dadfd4f5aeeef2505278b8eff534759bc233 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Fri, 8 Apr 2016 18:16:01 -0400 Subject: [PATCH 54/64] [SPRINT 3] - Check point: OpenVDB linked and initialized, added solid to intersect with --- CMakeLists.txt | 13 +++- src/fluidSolver/SPHGrid.cpp | 111 ++++++++++++++++++++++++------ src/fluidSolver/SPHGrid.h | 22 +++++- src/fluidSolver/SPHKernels.h | 16 ++--- src/fluidSolver/SPHSolver.cpp | 25 +++++-- src/fluidSolver/SPHSolver.h | 6 +- src/fluidSolver/fluidParticle.cpp | 1 + src/fluidSolver/fluidParticle.h | 2 + src/fluidSolver/fluidSolver.cpp | 3 + src/fluidSolver/fluidSolver.h | 2 + src/fluidSolver/geo/fluidGeo.bgeo | Bin 0 -> 20439 bytes src/geometry/box.cpp | 67 ++++++++++++++++++ src/geometry/box.h | 15 ++++ src/geometry/geometry.h | 9 +++ src/scene/scene.cpp | 12 ++++ 15 files changed, 264 insertions(+), 40 deletions(-) create mode 100644 src/fluidSolver/geo/fluidGeo.bgeo diff --git a/CMakeLists.txt b/CMakeLists.txt index a747c07d..2bc7d02e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -54,7 +54,10 @@ find_package(GLEW) find_library(GLFW_LIBRARY "glfw3" HINTS ${GLFW_LIBRARY_DIR}) find_library(JSONCPP "jsoncpp") find_library(TBB "tbb") +# link half for openvdb +find_library(HALF "half") find_library(OPENVDB "openvdb") +# find_library(PARTIO "partio") find_library(NANOGUI "nanogui") add_definitions( @@ -65,7 +68,7 @@ add_definitions( -D_CRT_SECURE_NO_WARNINGS ) -set(CORE_LIBS ${GLFW_LIBRARY} ${GLUT_LIBRARY} ${GLEW_LIBRARY} ${JSONCPP} ${TBB} ${OPENVDB} ${NANOGUI} ${OPENGL_LIBRARY} ) +set(CORE_LIBS ${GLFW_LIBRARY} ${GLUT_LIBRARY} ${GLEW_LIBRARY} ${JSONCPP} ${TBB} ${HALF} ${OPENVDB} ${NANOGUI} ${PARTIO} ${ZILB} ${OPENGL_LIBRARY} ) # OSX-specific hacks/fixes if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") @@ -124,3 +127,11 @@ set(CORE_SRC add_executable(Korra ${CORE_SRC}) target_link_libraries(Korra ${CORE_LIBS}) + +# Include zlib for osx +find_package( ZLIB REQUIRED ) +if ( ZLIB_FOUND ) + include_directories( ${ZLIB_INCLUDE_DIRS} ) + target_link_libraries( Korra ${ZLIB_LIBRARIES} ) +endif( ZLIB_FOUND ) + diff --git a/src/fluidSolver/SPHGrid.cpp b/src/fluidSolver/SPHGrid.cpp index 028bfc30..708cc5c4 100644 --- a/src/fluidSolver/SPHGrid.cpp +++ b/src/fluidSolver/SPHGrid.cpp @@ -1,5 +1,77 @@ #include "SPHGrid.h" +void +SPHGrid::InitializeVdb( + const std::vector& particles + ) +{ + openvdb::initialize(); + + openvdb::FloatGrid::Ptr grid = openvdb::FloatGrid::create(2.0); + + CoordBBox indexBB(Coord(-10, -10, -10), Coord(10, 10, 10)); + // CoordBBox indexBB(Coord(0, 0, 0), Coord(20, 20, 20)); + MakeWaterSurface(grid, FluidParticle::separation, indexBB, m_cellSize, particles); + + // specify dataset name + grid->setName("WaterSurface"); + + // save grid in the file + openvdb::io::File file("fluidGeo.vdb"); + openvdb::GridPtrVec grids; + grids.push_back(grid); + file.write(grids); + file.close(); +} + +void +SPHGrid::MakeWaterSurface( + FloatGrid::Ptr grid, + const float radius, + const CoordBBox& indexBB, + const double cellSize, + const std::vector& particles + ) +{ + double h = cellSize; + // radius = 5.0f; + h = 0.5; + typename FloatGrid::Accessor accessor = grid->getAccessor(); + + for (Int32 i = indexBB.min().x(); i <= indexBB.max().x(); ++i) { + for (Int32 j = indexBB.min().y(); j <= indexBB.max().y(); ++j) { + for (Int32 k = indexBB.min().z(); k <= indexBB.max().z(); ++k) { + + // transform point (i, j, k) of index space into world space + glm::vec3 vecP = GetWorlCoord(glm::ivec3(i, j, k)); + Vec3d p(vecP.x, vecP.y, vecP.z); + // Vec3d p(i * h, j * h, k * h); + + // compute level set function value + float distance = 0; + + // int cellIdx = GetCellIdx(i, j, k); + // std::vector list = m_cells[cellIdx]; + + // LOG(INFO) << "Cell idx: " << to_string(cellIdx) << " with # particles: " << to_string(list.size()) << endl; + + // for (FluidParticle* particle : particles) { + // float voxelToParticle = glm::distance(particle->Position(), vecP); + // // if (voxelToParticle < h * 2.0f) { + // distance += voxelToParticle - radius; + // // } + // } + distance = glm::length(vecP) - 1.0; + // LOG(INFO) << "voxel p: " << glm::to_string(vecP) << ", i: " << to_string(i) << ", j" << to_string(j) << ", k" << to_string(k) << ", distance: " << distance << endl; + // distance = sqrt(p.x() * p.x() + p.y() * p.y()) - radius; + accessor.setValue(Coord(i, j, k), distance); + } + } + } + + grid->setTransform(openvdb::math::Transform::createLinearTransform(h)); +} + // ---------------------------------------------------- // // SPHGrid // ---------------------------------------------------- // @@ -7,7 +79,7 @@ SPHGrid::SPHGrid( const std::vector& particles, const glm::vec3& gridMin, const glm::vec3& gridMax, - const float cellSize, + const double cellSize, const bool useGrid ) : m_width(1), m_height(1), m_depth(1), m_cellSize(cellSize), m_gridMin(gridMin), m_gridMax(gridMax), @@ -19,28 +91,14 @@ SPHGrid::SPHGrid( m_depth = ceil((m_gridMax.z - m_gridMin.z) / m_cellSize); m_cells.resize(m_width * m_height * m_depth); - // LOG(INFO) << "Number of particles: " << particles.size() << endl; - // LOG(INFO) << "Grid dim: " << m_width << ", " << m_height << ", " << m_depth << endl; } else { // Grid size is only 1 m_cells.resize(1); } this->ResetGrid(particles); -} - -void -InitializeVdb() -{ - // // -- Initialize openvdb - // openvdb::initialize(); - - // // Create an empty floating-point grid with background value 0. - // openvdb::FloatGrid::Ptr grid = openvdb::FloatGrid::create(); - - // // Get an accessor for coordinate-based access to voxels. - // openvdb::FloatGrid::Accessor accessor = grid->getAccessor(); + InitializeVdb(particles); } void @@ -82,9 +140,10 @@ SPHGrid::GetCellIdx( const glm::vec3& position ) { - int i = abs(position.x - m_gridMin.x) / m_cellSize; - int j = abs(position.y - m_gridMin.y) / m_cellSize; - int k = abs(position.z - m_gridMin.z) / m_cellSize; + glm::ivec3 coord = GetCellCoord(position); + int i = coord.x; + int j = coord.y; + int k = coord.z; // -- Error checking if (i < 0 || j < 0 || k < 0 || i >= m_width || j >= m_height || k >= m_depth) { @@ -153,6 +212,18 @@ SPHGrid::GetCellCoord( ); } +glm::vec3 +SPHGrid::GetWorlCoord( + const glm::ivec3& cellCoord + ) +{ + return glm::vec3( + cellCoord.x * m_cellSize + m_gridMin.x, + cellCoord.y * m_cellSize + m_gridMin.y, + cellCoord.z * m_cellSize + m_gridMin.z + ); +} + void SPHGrid::UpdateNeighbors( FluidParticle* particle @@ -214,7 +285,7 @@ SPHGrid::UpdateNeighborsUniformGrid( glm::vec3 particlePosition = particle->Position(); for(FluidParticle* neighbor : m_cells[idx]) { - if (glm::distance(neighbor->Position(), particlePosition) < m_cellSize * 2 && + if (glm::distance(neighbor->Position(), particlePosition) < m_cellSize * 2.0 && neighbor != particle) { if(!particle->AddNeighbor(neighbor)) { diff --git a/src/fluidSolver/SPHGrid.h b/src/fluidSolver/SPHGrid.h index 89f5638f..90fa230b 100644 --- a/src/fluidSolver/SPHGrid.h +++ b/src/fluidSolver/SPHGrid.h @@ -4,9 +4,12 @@ #include #include #include +#include +#include #include #include #include +#include #include #include @@ -14,6 +17,7 @@ using namespace std; using namespace tbb; +using namespace openvdb; // ---------------------------------------------------- // // SPHGrid @@ -25,7 +29,7 @@ class SPHGrid const std::vector& particles, const glm::vec3& gridMin, const glm::vec3& gridMax, - const float cellSize, + const double cellSize, const bool useGrid // Use grid to speed up neighbor search ); virtual void ResetGrid(const std::vector& particles); @@ -39,17 +43,29 @@ class SPHGrid int m_width; // Uses i or x int m_height; // Uses j or y int m_depth; // Uses k or z - float m_cellSize; // radius of particle influence + double m_cellSize; // radius of particle influence glm::vec3 m_gridMin; glm::vec3 m_gridMax; bool m_useGrid; std::vector> m_cells; + // -- Open vdb + void InitializeVdb(const std::vector& particles); + void MakeWaterSurface( + FloatGrid::Ptr grid, + const float radius, + const CoordBBox& indexBB, + const double cellSize, + const std::vector& particles + ); + + // -- Cell idx/coordinates int GetCellIdx(const glm::vec3&); int GetCellIdx(int i, int j, int k); - glm::ivec3 GetCellCoord(const glm::vec3&); + glm::ivec3 GetCellCoord(const glm::vec3& position); + glm::vec3 GetWorlCoord(const glm::ivec3& cellCoord); // -- Neighbor search algorithms void UpdateNeighborsSimple( diff --git a/src/fluidSolver/SPHKernels.h b/src/fluidSolver/SPHKernels.h index f9b9db71..f0464e9e 100644 --- a/src/fluidSolver/SPHKernels.h +++ b/src/fluidSolver/SPHKernels.h @@ -6,7 +6,7 @@ #include #include -inline float KernelPoly6(float x, float h) +inline double KernelPoly6(double x, double h) { if (x < 0 || x > h) { return 0; @@ -16,11 +16,11 @@ inline float KernelPoly6(float x, float h) return 0; } /// @todo: cache h2 and x2 - float result = (315 * pow(h * h - x * x, 3.0f)) / (64.0f * M_PI * pow(h, 9.0f)); + double result = (315 * pow(h * h - x * x, 3.0f)) / (64.0f * M_PI * pow(h, 9.0f)); return result < 0.0f ? 0.0f : result; } -inline float KernelSpiky(float x, float h) +inline double KernelSpiky(double x, double h) { if (x < 0 || x > h) { return 0; @@ -30,11 +30,11 @@ inline float KernelSpiky(float x, float h) return 0; } - float result = (15.0f * pow(h - x, 3.0f)) / (M_PI * pow(h, 6.0f)); + double result = (15.0f * pow(h - x, 3.0f)) / (M_PI * pow(h, 6.0f)); return result < 0.0 ? 0.0f : result; } -inline glm::vec3 GradKernelSpiky(glm::vec3 x_vec, float x, float h) +inline glm::vec3 GradKernelSpiky(glm::vec3 x_vec, double x, double h) { if (x <= 0 || x > h) { return glm::vec3(0.0f); @@ -45,15 +45,15 @@ inline glm::vec3 GradKernelSpiky(glm::vec3 x_vec, float x, float h) } float scalarPart = -45.0f * (h - x) * (h - x) / (M_PI * pow(h, 6.0f)); - return scalarPart * (x_vec) / x; + return scalarPart * (x_vec) / (float)x; } -inline float LaplacianKernelViscous(float x, float h) +inline double LaplacianKernelViscous(double x, double h) { return 45.0f * (h - x) / (M_PI * pow(h, 6.0f)); } -inline float KernelViscous(float x, float h) +inline double KernelViscous(double x, double h) { if (x <= 0 || x > h) { return 0; diff --git a/src/fluidSolver/SPHSolver.cpp b/src/fluidSolver/SPHSolver.cpp index 61f5ba2c..c9025103 100644 --- a/src/fluidSolver/SPHSolver.cpp +++ b/src/fluidSolver/SPHSolver.cpp @@ -7,24 +7,23 @@ SPHSolver::SPHSolver( const glm::vec3& containerDim, const glm::vec3& particleDim, const float separation, - const float cellSize, + const double cellSize, const float stiffness, const float viscosity, const float mass, const float restDensity ) : FluidSolver(containerDim, particleDim, separation, mass), m_grid(nullptr), - m_cellSize(cellSize), m_stiffness(stiffness), m_viscosity(viscosity), m_restDensity(restDensity), - m_kernelRadius(cellSize * 2.0f) + m_kernelRadius(cellSize * 2.0) { m_grid = new SPHGrid( m_particles, -m_containerDim, m_containerDim, - m_cellSize, + cellSize, true ); @@ -58,6 +57,22 @@ SPHSolver::SetConstant( } } +void +SPHSolver::CheckBoxIntersection( + Box* box + ) +{ + for (FluidParticle* particle : m_particles) { + glm::vec3 newVel; + bool intersected = false; + glm::vec3 newPos = box->Intersect(particle->Position(), intersected, newVec); + if (intersected) { + particle->SetPosition(newPos); + particle->SetVelocity(particle->Velocity() + newVel); + } + } +} + void SPHSolver::Update( const float deltaT @@ -145,7 +160,7 @@ SPHSolver::CalculatePressure( float pressure = m_stiffness * (particle->Density() - m_restDensity); // Clamp from being negative - pressure = pressure < 0.0 ? 0 : pressure; + // pressure = pressure < 0.0 ? 0 : pressure; particle->SetPressure(pressure); } diff --git a/src/fluidSolver/SPHSolver.h b/src/fluidSolver/SPHSolver.h index 567c7abb..a5053060 100644 --- a/src/fluidSolver/SPHSolver.h +++ b/src/fluidSolver/SPHSolver.h @@ -22,7 +22,7 @@ class SPHSolver : public FluidSolver const glm::vec3& containerDim, const glm::vec3& particleDim, const float separation, - const float cellSize, + const double cellSize, const float stiffness, const float viscosity, const float mass, @@ -36,6 +36,7 @@ class SPHSolver : public FluidSolver float value ); virtual void Update(const float deltaT); + void CheckBoxIntersection(Box* box); void AddParticle(FluidParticle*); protected: @@ -43,11 +44,10 @@ class SPHSolver : public FluidSolver SPHGrid* m_grid; // -- Particle info - float m_cellSize; float m_stiffness; float m_viscosity; float m_restDensity; - float m_kernelRadius; + double m_kernelRadius; void CalculateDensity( FluidParticle* particle diff --git a/src/fluidSolver/fluidParticle.cpp b/src/fluidSolver/fluidParticle.cpp index 0c9d4e5d..aa2e9235 100644 --- a/src/fluidSolver/fluidParticle.cpp +++ b/src/fluidSolver/fluidParticle.cpp @@ -2,6 +2,7 @@ float FluidParticle::mass = 0.125f; SPHColor FluidParticle::colorType = SPHColorSimple; +float FluidParticle::separation = 0.05f; // ---------------------------------------------------- // // FluidParticle diff --git a/src/fluidSolver/fluidParticle.h b/src/fluidSolver/fluidParticle.h index 0a9332ee..6bbc7b06 100644 --- a/src/fluidSolver/fluidParticle.h +++ b/src/fluidSolver/fluidParticle.h @@ -4,6 +4,7 @@ #include #include +#include #include using namespace std; @@ -27,6 +28,7 @@ class FluidParticle static float mass; static SPHColor colorType; + static float separation; FluidParticle(); FluidParticle( diff --git a/src/fluidSolver/fluidSolver.cpp b/src/fluidSolver/fluidSolver.cpp index 5f8a06e1..e34f62a2 100644 --- a/src/fluidSolver/fluidSolver.cpp +++ b/src/fluidSolver/fluidSolver.cpp @@ -12,6 +12,9 @@ FluidSolver::FluidSolver( m_particleDim(particleDim), m_separation(separation) { + // -- Store separation in particle class + FluidParticle::separation = m_separation; + // -- Initialize all particles float midX = m_particleDim.x / 2.f; float midY = m_particleDim.y / 2.f; diff --git a/src/fluidSolver/fluidSolver.h b/src/fluidSolver/fluidSolver.h index 2b0db35c..f73192ac 100644 --- a/src/fluidSolver/fluidSolver.h +++ b/src/fluidSolver/fluidSolver.h @@ -12,6 +12,7 @@ #include #include +#include using namespace tbb; @@ -49,6 +50,7 @@ class FluidSolver glm::vec3 m_maxBoundary; float m_separation; + // @todo: convert to shared pointers std::vector m_particles; }; diff --git a/src/fluidSolver/geo/fluidGeo.bgeo b/src/fluidSolver/geo/fluidGeo.bgeo new file mode 100644 index 0000000000000000000000000000000000000000..34215732d484a3b2c0e2da722f54685bfdf88564 GIT binary patch literal 20439 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�tpJ?2 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 VcLff!93G{~wF~)42cu literal 0 HcmV?d00001 diff --git a/src/geometry/box.cpp b/src/geometry/box.cpp index 7e28009c..01ac0499 100644 --- a/src/geometry/box.cpp +++ b/src/geometry/box.cpp @@ -14,6 +14,73 @@ Box::GetMaxBoundary() const return m_translation + m_scale / 2.0f; } +glm::vec3 +Box::Intersect( + const glm::vec3& position, + glm::vec3& newVelocity, + bool& intersected + ) +{ + glm::vec3 localPos = glm::vec3(glm::inverse(m_localTransform) * glm::vec4(position, 1.0f)); + if (localPos.x < 0.5f && localPos.x > -0.5f && + localPos.y < 0.5f && localPos.y > -0.5f && + localPos.z < 0.5f && localPos.z > -0.5f + ) + { + // Find the nearest face, the push it out that way + float minDis = 99999; + float dis[6] = { + fabs(localPos.x - 0.5f), + fabs(localPos.x + 0.5f), + fabs(localPos.y - 0.5f), + fabs(localPos.y + 0.5f), + fabs(localPos.z - 0.5f), + fabs(localPos.z + 0.5f) + }; + + BoxFace face = BoxFace_PosX; + for (char f = 0; f < (char)BoxFace_Max; ++f) { + if (dis[f] < minDis) { + minDis = dis[f]; + face = (BoxFace)f; + } + } + + switch(face) { + case BoxFace_PosX: + localPos.x = 0.5f; + newVelocity = glm::vec3(-1.0f, 0.0f, 0.0f); + break; + case BoxFace_NegX: + localPos.x = -0.5f; + newVelocity = glm::vec3(1.0f, 0.0f, 0.0f); + break; + case BoxFace_PosY: + localPos.y = 0.5f; + newVelocity = glm::vec3(0.0f, -1.0f, 0.0f); + break; + case BoxFace_NegY: + localPos.y = -0.5f; + newVelocity = glm::vec3(0.0f, 1.0f, 0.0f); + break; + case BoxFace_PosZ: + localPos.z = 0.5f; + newVelocity = glm::vec3(0.0f, 0.0f, -1.0f); + break; + case BoxFace_NegZ: + localPos.z = -0.5f; + newVelocity = glm::vec3(0.0f, 0.0f, 1.0f); + break; + } + + intersected = true; + // std::cout << "Intersected: " << " global: " << glm::to_string(position) << ", local: " << glm::to_string(localPos) << endl; + return glm::vec3(m_localTransform * glm::vec4(position, 1.0f)); + } + + return position; +} + void Box::InitPositions() diff --git a/src/geometry/box.h b/src/geometry/box.h index 41224b5a..8ec9c2ef 100644 --- a/src/geometry/box.h +++ b/src/geometry/box.h @@ -3,10 +3,25 @@ #include +typedef enum { + BoxFace_PosX, + BoxFace_NegX, + BoxFace_PosY, + BoxFace_NegY, + BoxFace_PosZ, + BoxFace_NegZ, + BoxFace_Max +} BoxFace; + class Box : public Geometry { public: virtual void Create(); + virtual glm::vec3 Intersect( + const glm::vec3& position, + glm::vec3& newVelocity, + bool& intersected + ); virtual const glm::vec3 GetMinBoundary() const; virtual const glm::vec3 GetMaxBoundary() const; diff --git a/src/geometry/geometry.h b/src/geometry/geometry.h index 459b3a16..23784036 100644 --- a/src/geometry/geometry.h +++ b/src/geometry/geometry.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -49,6 +50,12 @@ class Geometry virtual void DisableVertexAttributes() const; // Getters/Setters + void SetColor(const glm::vec4& color) { + int len = m_colors.size(); + for (int i = 0; i < len; ++i) { + m_colors[i] = color; + } + } void SetColors(const vector& colors) { m_colors = colors; }; @@ -57,6 +64,8 @@ class Geometry m_positions = positions; } + virtual glm::vec3 Intersect(const glm::vec3& position) {return position;} + // -- Transformations void Translate(const float& x, const float& y, const float& z); void Rotate(const float& radx, const float& rady, const float& radz); diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index d9210ad3..5e5d4d39 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -91,6 +91,14 @@ Scene::InitFromJson( m_fluidSolver->ParticleColors() ); m_fluidGeo->Create(); + + // -- Solid object + m_testBox = new Box(); + m_testBox->Translate(0.2f, -0.7f, 0.0f); + m_testBox->Scale(0.2f, 0.2f, 0.2f); + m_testBox->SetDrawMode(DrawMode_Wireframe); + m_testBox->SetColor(glm::vec4(0.8f, 0.3f, 0.2f, 1.0f)); + m_testBox->Create(); } void @@ -233,6 +241,7 @@ Scene::UpdateFluidSolver( ParticleAdvectProgram& progAdvect ) { + m_fluidSolver->CheckBoxIntersection(m_testBox); m_fluidSolver->Update(deltaT); m_fluidGeo->SetVelocities(m_fluidSolver->ParticleVelocities()); m_fluidGeo->SetPositions(m_fluidSolver->ParticlePositions()); @@ -253,4 +262,7 @@ Scene::DrawFluidSolver( // -- Draw particles m_fluidGeo->SetColors(m_fluidSolver->ParticleColors()); progAdvect.Draw(m_camera, m_fluidGeo, m_fluidContainer); + + // -- Draw solids + prog.Draw(*m_camera, *m_testBox); } From cec6bf5b6be98a9c9cbd3f88e319aef82c6f61f2 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Fri, 8 Apr 2016 22:39:26 -0400 Subject: [PATCH 55/64] [SPRINT 3] - Added 2 solide objects to scene --- CMakeLists.txt | 17 ++++- src/fluidSolver/SPHGrid.cpp | 38 +++++----- src/fluidSolver/SPHGrid.h | 3 + src/fluidSolver/SPHSolver.cpp | 30 ++++---- src/geometry/box.cpp | 112 +++++++++++++++++++++++----- src/geometry/box.h | 1 + src/geometry/geometry.cpp | 26 +++++++ src/geometry/geometry.h | 10 ++- src/glsl/simple.frag | 13 +++- src/glsl/simple.vert | 23 +++++- src/scene/scene.cpp | 35 ++++++--- src/scene/scene.h | 1 + src/shaderProgram/shaderProgram.cpp | 8 +- src/thirdparty/openvdb | 1 + src/thirdparty/openvdb_points_dev | 1 + 15 files changed, 244 insertions(+), 75 deletions(-) create mode 160000 src/thirdparty/openvdb create mode 160000 src/thirdparty/openvdb_points_dev diff --git a/CMakeLists.txt b/CMakeLists.txt index 2bc7d02e..ee9d00c7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,7 +33,7 @@ endif() # set creates a variable set (THIRDPARTY "src/thirdparty") -# add include and src directories to path +# add include and src directories to path for nanogui include_directories( ${THIRDPARTY}/nanogui/include ${THIRDPARTY}/nanogui/src @@ -42,6 +42,17 @@ include_directories( # NanoGUI library path set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} ${THIRDPARTY}/nanogui) +# add include and src directories to path for openvdb_points +include_directories( + ${THIRDPARTY}/ + ${THIRDPARTY}/openvdb_points_dev/ + ${THIRDPARTY}/openvdb_points_dev/openvdb_points/ + ${THIRDPARTY}/openvdb_points_dev/openvdb_points/tools/ +) + +# OpenVDB point library path +set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} ${THIRDPARTY}/openvdb_points_dev/lib) + #set include variables set(GLFW_INCLUDE_DIR ${NUPARU}/include ) set(GLEW_INCLUDE_DIR ${NUPARU}/include ) @@ -57,8 +68,8 @@ find_library(TBB "tbb") # link half for openvdb find_library(HALF "half") find_library(OPENVDB "openvdb") -# find_library(PARTIO "partio") find_library(NANOGUI "nanogui") +find_library(OPENVDB_POINT "openvdb_points") add_definitions( -DTW_STATIC @@ -68,7 +79,7 @@ add_definitions( -D_CRT_SECURE_NO_WARNINGS ) -set(CORE_LIBS ${GLFW_LIBRARY} ${GLUT_LIBRARY} ${GLEW_LIBRARY} ${JSONCPP} ${TBB} ${HALF} ${OPENVDB} ${NANOGUI} ${PARTIO} ${ZILB} ${OPENGL_LIBRARY} ) +set(CORE_LIBS ${GLFW_LIBRARY} ${GLUT_LIBRARY} ${GLEW_LIBRARY} ${JSONCPP} ${TBB} ${HALF} ${OPENVDB} ${OPENVDB_POINT} ${NANOGUI} ${ZILB} ${OPENGL_LIBRARY} ) # OSX-specific hacks/fixes if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") diff --git a/src/fluidSolver/SPHGrid.cpp b/src/fluidSolver/SPHGrid.cpp index 708cc5c4..3dc4a6bc 100644 --- a/src/fluidSolver/SPHGrid.cpp +++ b/src/fluidSolver/SPHGrid.cpp @@ -9,8 +9,8 @@ SPHGrid::InitializeVdb( openvdb::FloatGrid::Ptr grid = openvdb::FloatGrid::create(2.0); - CoordBBox indexBB(Coord(-10, -10, -10), Coord(10, 10, 10)); - // CoordBBox indexBB(Coord(0, 0, 0), Coord(20, 20, 20)); + // CoordBBox indexBB(Coord(-10, -10, -10), Coord(10, 10, 10)); + CoordBBox indexBB(Coord(0, 0, 0), Coord(20, 20, 20)); MakeWaterSurface(grid, FluidParticle::separation, indexBB, m_cellSize, particles); // specify dataset name @@ -35,7 +35,7 @@ SPHGrid::MakeWaterSurface( { double h = cellSize; // radius = 5.0f; - h = 0.5; + // h = 0.5; typename FloatGrid::Accessor accessor = grid->getAccessor(); for (Int32 i = indexBB.min().x(); i <= indexBB.max().x(); ++i) { @@ -45,26 +45,22 @@ SPHGrid::MakeWaterSurface( // transform point (i, j, k) of index space into world space glm::vec3 vecP = GetWorlCoord(glm::ivec3(i, j, k)); Vec3d p(vecP.x, vecP.y, vecP.z); - // Vec3d p(i * h, j * h, k * h); // compute level set function value - float distance = 0; - - // int cellIdx = GetCellIdx(i, j, k); - // std::vector list = m_cells[cellIdx]; - - // LOG(INFO) << "Cell idx: " << to_string(cellIdx) << " with # particles: " << to_string(list.size()) << endl; - - // for (FluidParticle* particle : particles) { - // float voxelToParticle = glm::distance(particle->Position(), vecP); - // // if (voxelToParticle < h * 2.0f) { - // distance += voxelToParticle - radius; - // // } - // } - distance = glm::length(vecP) - 1.0; - // LOG(INFO) << "voxel p: " << glm::to_string(vecP) << ", i: " << to_string(i) << ", j" << to_string(j) << ", k" << to_string(k) << ", distance: " << distance << endl; - // distance = sqrt(p.x() * p.x() + p.y() * p.y()) - radius; - accessor.setValue(Coord(i, j, k), distance); + float distance = 9999; + + int cellIdx = GetCellIdx(i, j, k); + std::vector list = m_cells[cellIdx]; + + for (FluidParticle* particle : list) { + float voxelToParticle = glm::distance(particle->Position(), vecP) - radius; + if (voxelToParticle < distance) { + distance = voxelToParticle; + } + } + if (distance < 9999) { + accessor.setValue(Coord(i, j, k), distance); + } } } } diff --git a/src/fluidSolver/SPHGrid.h b/src/fluidSolver/SPHGrid.h index 90fa230b..2bbff06f 100644 --- a/src/fluidSolver/SPHGrid.h +++ b/src/fluidSolver/SPHGrid.h @@ -10,6 +10,9 @@ #include #include #include +// #include +// #include +// #include #include #include diff --git a/src/fluidSolver/SPHSolver.cpp b/src/fluidSolver/SPHSolver.cpp index c9025103..78fd443d 100644 --- a/src/fluidSolver/SPHSolver.cpp +++ b/src/fluidSolver/SPHSolver.cpp @@ -65,10 +65,10 @@ SPHSolver::CheckBoxIntersection( for (FluidParticle* particle : m_particles) { glm::vec3 newVel; bool intersected = false; - glm::vec3 newPos = box->Intersect(particle->Position(), intersected, newVec); + glm::vec3 newPos = box->Intersect(particle->Position(), newVel, intersected); if (intersected) { particle->SetPosition(newPos); - particle->SetVelocity(particle->Velocity() + newVel); + particle->SetVelocity(particle->Velocity() + 2.0f * newVel); } } } @@ -160,7 +160,7 @@ SPHSolver::CalculatePressure( float pressure = m_stiffness * (particle->Density() - m_restDensity); // Clamp from being negative - // pressure = pressure < 0.0 ? 0 : pressure; + pressure = pressure < 0.0 ? 0 : pressure; particle->SetPressure(pressure); } @@ -219,37 +219,37 @@ SPHSolver::UpdateDynamics( if (particle->Position().x < m_minBoundary.x) { newPosition.x = m_minBoundary.x + 0.0f; newVel.x = -newVel.x * 0.5; - newVel.y *= 1.1; - newVel.z *= 1.1; + newVel.y *= 0.9; + newVel.z *= 0.9; } else if (particle->Position().x > m_maxBoundary.x) { newPosition.x = m_maxBoundary.x - 0.0f; newVel.x = -newVel.x * 0.5; - newVel.y *= 1.1; - newVel.z *= 1.1; + newVel.y *= 0.9; + newVel.z *= 0.9; } if (particle->Position().y < m_minBoundary.y) { newPosition.y = m_minBoundary.y + 0.0f; newVel.y = -newVel.y * 0.5; - newVel.x *= 1.1; - newVel.z *= 1.1; + newVel.x *= 0.9; + newVel.z *= 0.9; } else if (particle->Position().y > m_maxBoundary.y) { newPosition.y = m_maxBoundary.y - 0.0f; newVel.y = -newVel.y * 0.5f; - newVel.x *= 1.1; - newVel.z *= 1.1; + newVel.x *= 0.9; + newVel.z *= 0.9; } if (particle->Position().z < m_minBoundary.z) { newPosition.z = m_minBoundary.z + 0.0f; newVel.z = -newVel.z * 0.5; - newVel.x *= 1.1; - newVel.y *= 1.1; + newVel.x *= 0.9; + newVel.y *= 0.9; } else if (particle->Position().z > m_maxBoundary.z) { newPosition.z = m_maxBoundary.z - 0.0f; newVel.z = -newVel.z * 0.5; - newVel.x *= 1.1; - newVel.y *= 1.1; + newVel.x *= 0.9; + newVel.y *= 0.9; } particle->SetPosition(newPosition); diff --git a/src/geometry/box.cpp b/src/geometry/box.cpp index 01ac0499..e4b0edd6 100644 --- a/src/geometry/box.cpp +++ b/src/geometry/box.cpp @@ -1,5 +1,16 @@ #include "box.h" +Box::Box( + DrawMode drawMode + ) +{ + SetDrawMode(drawMode); + InitPositions(); + InitNormals(); + InitColors(); + InitIndices(); +} + const glm::vec3 Box::GetMinBoundary() const { @@ -48,34 +59,36 @@ Box::Intersect( switch(face) { case BoxFace_PosX: - localPos.x = 0.5f; - newVelocity = glm::vec3(-1.0f, 0.0f, 0.0f); + localPos.x = 0.6f; + newVelocity = glm::vec3(1.0f, 0.0f, 0.0f); break; case BoxFace_NegX: - localPos.x = -0.5f; - newVelocity = glm::vec3(1.0f, 0.0f, 0.0f); + localPos.x = -0.6f; + newVelocity = glm::vec3(-1.0f, 0.0f, 0.0f); break; case BoxFace_PosY: - localPos.y = 0.5f; - newVelocity = glm::vec3(0.0f, -1.0f, 0.0f); + localPos.y = 0.6f; + newVelocity = glm::vec3(0.0f, 1.0f, 0.0f); break; case BoxFace_NegY: - localPos.y = -0.5f; - newVelocity = glm::vec3(0.0f, 1.0f, 0.0f); + localPos.y = -0.6f; + newVelocity = glm::vec3(0.0f, -1.0f, 0.0f); break; case BoxFace_PosZ: - localPos.z = 0.5f; - newVelocity = glm::vec3(0.0f, 0.0f, -1.0f); + localPos.z = 0.6f; + newVelocity = glm::vec3(0.0f, 0.0f, 1.0f); break; case BoxFace_NegZ: - localPos.z = -0.5f; - newVelocity = glm::vec3(0.0f, 0.0f, 1.0f); + localPos.z = -0.6f; + newVelocity = glm::vec3(0.0f, 0.0f, -1.0f); break; } + newVelocity = glm::vec3(m_localTransform * glm::vec4(newVelocity, 0.0f)); intersected = true; - // std::cout << "Intersected: " << " global: " << glm::to_string(position) << ", local: " << glm::to_string(localPos) << endl; - return glm::vec3(m_localTransform * glm::vec4(position, 1.0f)); + + glm::vec3 positionWorld = glm::vec3(m_localTransform * glm::vec4(localPos, 1.0f)); + return positionWorld; } return position; @@ -150,13 +163,77 @@ Box::InitPositions() void Box::InitNormals() { + m_normals.clear(); + // Front face + m_normals.push_back(glm::vec3(0.0f, 0.0f, 1.0f)); + //LR + m_normals.push_back(glm::vec3(0.0f, 0.0f, 1.0f)); + //LL + m_normals.push_back(glm::vec3(0.0f, 0.0f, 1.0f)); + //UL + m_normals.push_back(glm::vec3(0.0f, 0.0f, 1.0f)); + //Right face + //UR + m_normals.push_back(glm::vec3(1.0f, 0.0f, 0.0f)); + //LR + m_normals.push_back(glm::vec3(1.0f, 0.0f, 0.0f)); + //LL + m_normals.push_back(glm::vec3(1.0f, 0.0f, 0.0f)); + //UL + m_normals.push_back(glm::vec3(1.0f, 0.0f, 0.0f)); + + //Left face + //UR + m_normals.push_back(glm::vec3(-1.0f, 0.0f, 0.0f)); + //LR + m_normals.push_back(glm::vec3(-1.0f, 0.0f, 0.0f)); + //LL + m_normals.push_back(glm::vec3(-1.0f, 0.0f, 0.0f)); + //UL + m_normals.push_back(glm::vec3(-1.0f, 0.0f, 0.0f)); + + //Back face + //UR + m_normals.push_back(glm::vec3(0.0f, 0.0f, -1.0f)); + //LR + m_normals.push_back(glm::vec3(0.0f, 0.0f, -1.0f)); + //LL + m_normals.push_back(glm::vec3(0.0f, 0.0f, -1.0f)); + //UL + m_normals.push_back(glm::vec3(0.0f, 0.0f, -1.0f)); + + //Top face + //UR + m_normals.push_back(glm::vec3(0.0f, 1.0f, 0.0f)); + //LR + m_normals.push_back(glm::vec3(0.0f, 1.0f, 0.0f)); + //LL + m_normals.push_back(glm::vec3(0.0f, 1.0f, 0.0f)); + //UL + m_normals.push_back(glm::vec3(0.0f, 1.0f, 0.0f)); + + //Bottom face + //UR + m_normals.push_back(glm::vec3(0.0f, -1.0f, 0.0f)); + //LR + m_normals.push_back(glm::vec3(0.0f, -1.0f, 0.0f)); + //LL + m_normals.push_back(glm::vec3(0.0f, -1.0f, 0.0f)); + //UL + m_normals.push_back(glm::vec3(0.0f, -1.0f, 0.0f)); } void Box::InitColors() { - + if (m_colors.size() == 0) { + // Set default color + int len = m_positions.size(); + for (int i = 0; i < len; ++i) { + m_colors.push_back(glm::vec4(0, 1.0f, 1.0f, 1.0f)); + } + } } void @@ -194,11 +271,10 @@ Box::InitIndices() void Box::Create() { - InitPositions(); - InitIndices(); - glGenVertexArrays(1, &m_vao); glGenBuffers(1, &m_posBuffer); + glGenBuffers(1, &m_norBuffer); + glGenBuffers(1, &m_colBuffer); glGenBuffers(1, &m_idxBuffer); UpdateVAO(); diff --git a/src/geometry/box.h b/src/geometry/box.h index 8ec9c2ef..d7a3c036 100644 --- a/src/geometry/box.h +++ b/src/geometry/box.h @@ -16,6 +16,7 @@ typedef enum { class Box : public Geometry { public: + Box(DrawMode mode); virtual void Create(); virtual glm::vec3 Intersect( const glm::vec3& position, diff --git a/src/geometry/geometry.cpp b/src/geometry/geometry.cpp index e80bca68..ff3523fc 100644 --- a/src/geometry/geometry.cpp +++ b/src/geometry/geometry.cpp @@ -38,6 +38,14 @@ Geometry::EnableVertexAttributes() const glBindBuffer(GL_ARRAY_BUFFER, m_posBuffer); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0); + glEnableVertexAttribArray(1); + glBindBuffer(GL_ARRAY_BUFFER, m_norBuffer); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)0); + + glEnableVertexAttribArray(2); + glBindBuffer(GL_ARRAY_BUFFER, m_colBuffer); + glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, 0, (void*)0); + // Bind element buffer glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_idxBuffer); } @@ -47,6 +55,8 @@ Geometry::DisableVertexAttributes() const { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, NULL); glDisableVertexAttribArray(0); + glDisableVertexAttribArray(1); + glDisableVertexAttribArray(2); glBindBuffer(GL_ARRAY_BUFFER, NULL); glBindVertexArray(NULL); } @@ -117,6 +127,22 @@ Geometry::UpdateVAO() GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, NULL); + glBindBuffer(GL_ARRAY_BUFFER, m_norBuffer); + glBufferData( + GL_ARRAY_BUFFER, + m_normals.size() * sizeof(glm::vec3), + &m_normals[0], + GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, NULL); + + glBindBuffer(GL_ARRAY_BUFFER, m_colBuffer); + glBufferData( + GL_ARRAY_BUFFER, + m_colors.size() * sizeof(glm::vec4), + &m_colors[0], + GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, NULL); + // -- Index glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_idxBuffer); diff --git a/src/geometry/geometry.h b/src/geometry/geometry.h index 23784036..8706ee72 100644 --- a/src/geometry/geometry.h +++ b/src/geometry/geometry.h @@ -51,9 +51,10 @@ class Geometry // Getters/Setters void SetColor(const glm::vec4& color) { - int len = m_colors.size(); + m_colors.clear(); + int len = m_positions.size(); for (int i = 0; i < len; ++i) { - m_colors[i] = color; + m_colors.push_back(color); } } void SetColors(const vector& colors) { @@ -64,6 +65,10 @@ class Geometry m_positions = positions; } + const GLuint PosBuffer() const { return m_posBuffer; } + const GLuint NorBuffer() const { return m_norBuffer; } + const GLuint ColBuffer() const { return m_colBuffer; } + virtual glm::vec3 Intersect(const glm::vec3& position) {return position;} // -- Transformations @@ -77,6 +82,7 @@ class Geometry DrawMode m_drawMode = DrawMode_Shaded; vector m_positions; + vector m_normals; vector m_colors; vector m_indices; diff --git a/src/glsl/simple.frag b/src/glsl/simple.frag index 53911af1..cdd05b5a 100644 --- a/src/glsl/simple.frag +++ b/src/glsl/simple.frag @@ -4,9 +4,20 @@ precision highp float; precision highp int; layout(std140, column_major) uniform; +in vec4 v_color; +in vec3 v_normal; +in vec3 v_lightVec; + out vec4 color; void main() { - color = vec4(0.0, 1.0, 1.0, 1.0); + vec4 diffuse = v_color; + float diffuseTerm = dot(normalize(v_normal), normalize(v_lightVec)); + diffuseTerm = clamp(diffuseTerm, 0, 1); + + float ambientTerm = 0.1; + float lightIntensity = diffuseTerm + ambientTerm; + + color = vec4(vec3(diffuse * lightIntensity), 1); } diff --git a/src/glsl/simple.vert b/src/glsl/simple.vert index ee583dd9..69fd5d70 100644 --- a/src/glsl/simple.vert +++ b/src/glsl/simple.vert @@ -1,5 +1,7 @@ -#version 150 core +#version 330 core #define POSITION_LOCATION 0 +#define NORMAL_LOCATION 1 +#define COLOR_LOCATION 2 precision highp float; precision highp int; @@ -8,10 +10,23 @@ layout(std140, column_major) uniform; uniform mat4 u_model; uniform mat4 u_viewProj; -in vec3 a_position; +layout(location = POSITION_LOCATION) in vec3 a_position; +layout(location = NORMAL_LOCATION) in vec3 a_normal; +layout(location = COLOR_LOCATION) in vec4 a_color; + +out vec4 v_color; +out vec3 v_normal; +out vec3 v_lightVec; + +const vec4 lightPos = vec4(2, 1, 1, 1); // Virtual light void main() { - vec4 position = u_viewProj * u_model * vec4(a_position, 1.0); - gl_Position = position; + vec4 modelPosition = u_model * vec4(a_position, 1.0); + gl_Position = u_viewProj * modelPosition; + + // Pass through frag shader + v_color = a_color; + v_normal = vec3(transpose(inverse(u_model)) * vec4(a_normal, 0.0)); + v_lightVec = (lightPos - modelPosition).xyz; } diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 5e5d4d39..b7d92579 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -12,6 +12,7 @@ Scene::Scene( const float& screenHeight ) : m_testBox(nullptr), + m_testBoxV(nullptr), m_fluidContainer(nullptr), m_fluidGeo(nullptr), m_fluidSolver(nullptr) @@ -23,7 +24,7 @@ Scene::Scene( void Scene::InitFromTestScene() { - m_testBox = new Box(); + m_testBox = new Box(DrawMode_Wireframe); m_testBox->SetDrawMode(DrawMode_Wireframe); m_testBox->Create(); } @@ -51,9 +52,8 @@ Scene::InitFromJson( ); // Create geometry for the container - m_fluidContainer = new Box(); + m_fluidContainer = new Box(DrawMode_Wireframe); m_fluidContainer->Scale(containerDim.x, containerDim.y, containerDim.z); - m_fluidContainer->SetDrawMode(DrawMode_Wireframe); m_fluidContainer->Create(); // -- Get particle information @@ -93,12 +93,17 @@ Scene::InitFromJson( m_fluidGeo->Create(); // -- Solid object - m_testBox = new Box(); - m_testBox->Translate(0.2f, -0.7f, 0.0f); - m_testBox->Scale(0.2f, 0.2f, 0.2f); - m_testBox->SetDrawMode(DrawMode_Wireframe); + m_testBox = new Box(DrawMode_Shaded); + m_testBox->Translate(0.0f, -1.0f, 0.1f); + m_testBox->Scale(1.0f, 0.2f, 0.9f); m_testBox->SetColor(glm::vec4(0.8f, 0.3f, 0.2f, 1.0f)); m_testBox->Create(); + + m_testBoxV = new Box(DrawMode_Shaded); + m_testBoxV->Translate(-1.0f, 0.0f, 0.0f); + m_testBoxV->Scale(0.2f, 1.0f, 0.9f); + m_testBoxV->SetColor(glm::vec4(0.3f, 0.8f, 0.2f, 1.0f)); + m_testBoxV->Create(); } void @@ -142,6 +147,13 @@ Scene::Update( if (m_paused) { return; } + // Rotate solid + m_testBox->Rotate(0.0f, deltaT * 100.0f, 0.0f); + m_testBox->Translate(0.0f, cos(deltaT) * 0.05f, 0.0f); + + m_testBoxV->Translate(cos(deltaT) * 0.05f, 0.0f, 0.0f); + + // Update solver UpdateFluidSolver(deltaT, progAdvect); } @@ -242,6 +254,7 @@ Scene::UpdateFluidSolver( ) { m_fluidSolver->CheckBoxIntersection(m_testBox); + m_fluidSolver->CheckBoxIntersection(m_testBoxV); m_fluidSolver->Update(deltaT); m_fluidGeo->SetVelocities(m_fluidSolver->ParticleVelocities()); m_fluidGeo->SetPositions(m_fluidSolver->ParticlePositions()); @@ -256,13 +269,15 @@ Scene::DrawFluidSolver( ParticleAdvectProgram& progAdvect ) { - // -- Draw boundary + // -- Draw container prog.Draw(*m_camera, *m_fluidContainer); + // -- Draw solids + prog.Draw(*m_camera, *m_testBox); + prog.Draw(*m_camera, *m_testBoxV); + // -- Draw particles m_fluidGeo->SetColors(m_fluidSolver->ParticleColors()); progAdvect.Draw(m_camera, m_fluidGeo, m_fluidContainer); - // -- Draw solids - prog.Draw(*m_camera, *m_testBox); } diff --git a/src/scene/scene.h b/src/scene/scene.h index b920352e..8899d202 100644 --- a/src/scene/scene.h +++ b/src/scene/scene.h @@ -49,6 +49,7 @@ class Scene bool m_paused = false; Camera* m_camera; Box* m_testBox; + Box* m_testBoxV; Box* m_fluidContainer; FluidGeo* m_fluidGeo; SPHSolver* m_fluidSolver; diff --git a/src/shaderProgram/shaderProgram.cpp b/src/shaderProgram/shaderProgram.cpp index 6e643c2c..18b9f05c 100644 --- a/src/shaderProgram/shaderProgram.cpp +++ b/src/shaderProgram/shaderProgram.cpp @@ -9,6 +9,11 @@ ShaderProgram::ShaderProgram( m_unifModel = glGetUniformLocation(m_programID, "u_model"); m_unifViewProj = glGetUniformLocation(m_programID, "u_viewProj"); + + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + glEnable(GL_CULL_FACE); + glCullFace(GL_FRONT); } void @@ -45,7 +50,8 @@ ShaderProgram::Draw( glDrawElements( geo.GLDrawMode(), geo.ElementCount(), - GL_UNSIGNED_SHORT, NULL + GL_UNSIGNED_SHORT, + NULL ); geo.DisableVertexAttributes(); diff --git a/src/thirdparty/openvdb b/src/thirdparty/openvdb new file mode 160000 index 00000000..c332c9c4 --- /dev/null +++ b/src/thirdparty/openvdb @@ -0,0 +1 @@ +Subproject commit c332c9c4bfc4471c8ce81f445af1ea6db36a08ca diff --git a/src/thirdparty/openvdb_points_dev b/src/thirdparty/openvdb_points_dev new file mode 160000 index 00000000..804a95f8 --- /dev/null +++ b/src/thirdparty/openvdb_points_dev @@ -0,0 +1 @@ +Subproject commit 804a95f8992cd02d963194399805d228efd773e8 From 1c9e15e83e9dd4d888f2725db81ec6d0d3fa83f1 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Fri, 8 Apr 2016 22:56:42 -0400 Subject: [PATCH 56/64] [SPRINT 3] - MESHED --- src/fluidSolver/SPHGrid.cpp | 26 ++++++++++++-------------- src/fluidSolver/SPHGrid.h | 10 +++++----- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/fluidSolver/SPHGrid.cpp b/src/fluidSolver/SPHGrid.cpp index 3dc4a6bc..8dba00fb 100644 --- a/src/fluidSolver/SPHGrid.cpp +++ b/src/fluidSolver/SPHGrid.cpp @@ -1,15 +1,15 @@ #include "SPHGrid.h" void -SPHGrid::InitializeVdb( - const std::vector& particles +SPHGrid::WriteVdb( + const std::vector& particles, + size_t frameNumber ) { openvdb::initialize(); - openvdb::FloatGrid::Ptr grid = openvdb::FloatGrid::create(2.0); + openvdb::FloatGrid::Ptr grid = openvdb::FloatGrid::create(10.0); - // CoordBBox indexBB(Coord(-10, -10, -10), Coord(10, 10, 10)); CoordBBox indexBB(Coord(0, 0, 0), Coord(20, 20, 20)); MakeWaterSurface(grid, FluidParticle::separation, indexBB, m_cellSize, particles); @@ -17,7 +17,7 @@ SPHGrid::InitializeVdb( grid->setName("WaterSurface"); // save grid in the file - openvdb::io::File file("fluidGeo.vdb"); + openvdb::io::File file(string("geo/fluidGeo") + to_string(frameNumber) + string(".vdb")); openvdb::GridPtrVec grids; grids.push_back(grid); file.write(grids); @@ -33,18 +33,14 @@ SPHGrid::MakeWaterSurface( const std::vector& particles ) { - double h = cellSize; - // radius = 5.0f; - // h = 0.5; - typename FloatGrid::Accessor accessor = grid->getAccessor(); + FloatGrid::Accessor accessor = grid->getAccessor(); for (Int32 i = indexBB.min().x(); i <= indexBB.max().x(); ++i) { for (Int32 j = indexBB.min().y(); j <= indexBB.max().y(); ++j) { for (Int32 k = indexBB.min().z(); k <= indexBB.max().z(); ++k) { // transform point (i, j, k) of index space into world space - glm::vec3 vecP = GetWorlCoord(glm::ivec3(i, j, k)); - Vec3d p(vecP.x, vecP.y, vecP.z); + glm::vec3 point = GetWorlCoord(glm::ivec3(i, j, k)); // compute level set function value float distance = 9999; @@ -53,7 +49,7 @@ SPHGrid::MakeWaterSurface( std::vector list = m_cells[cellIdx]; for (FluidParticle* particle : list) { - float voxelToParticle = glm::distance(particle->Position(), vecP) - radius; + float voxelToParticle = glm::distance(particle->Position(), point) - radius; if (voxelToParticle < distance) { distance = voxelToParticle; } @@ -65,7 +61,7 @@ SPHGrid::MakeWaterSurface( } } - grid->setTransform(openvdb::math::Transform::createLinearTransform(h)); + grid->setTransform(openvdb::math::Transform::createLinearTransform(cellSize)); } // ---------------------------------------------------- // @@ -94,7 +90,6 @@ SPHGrid::SPHGrid( this->ResetGrid(particles); - InitializeVdb(particles); } void @@ -105,6 +100,9 @@ SPHGrid::ResetGrid( for(FluidParticle* p : particles) { AddParticle(p); } + + static size_t frameNumber = 0; + WriteVdb(particles, frameNumber++); } // Add particle to grid based on its position diff --git a/src/fluidSolver/SPHGrid.h b/src/fluidSolver/SPHGrid.h index 2bbff06f..484594ff 100644 --- a/src/fluidSolver/SPHGrid.h +++ b/src/fluidSolver/SPHGrid.h @@ -4,15 +4,12 @@ #include #include #include +#include #include #include #include #include #include -#include -// #include -// #include -// #include #include #include @@ -54,7 +51,10 @@ class SPHGrid std::vector> m_cells; // -- Open vdb - void InitializeVdb(const std::vector& particles); + void WriteVdb( + const std::vector& particles, + size_t frameNumber + ); void MakeWaterSurface( FloatGrid::Ptr grid, const float radius, From 9f820bbf38ac2abc02e35a00c11f26a793f1ba10 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Sat, 9 Apr 2016 23:57:40 -0400 Subject: [PATCH 57/64] [SPRINT 3] - Added conditional flag to enable solids --- src/scene/scene.cpp | 57 ++++++++++++++++++++++++++++++--------------- src/scene/scene.h | 8 +++++++ 2 files changed, 46 insertions(+), 19 deletions(-) diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index b7d92579..3b345f4b 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -1,8 +1,6 @@ #include #include -// #define TEST_SCENE - Scene::Scene() : Scene(800, 600) {} @@ -93,17 +91,21 @@ Scene::InitFromJson( m_fluidGeo->Create(); // -- Solid object - m_testBox = new Box(DrawMode_Shaded); - m_testBox->Translate(0.0f, -1.0f, 0.1f); - m_testBox->Scale(1.0f, 0.2f, 0.9f); - m_testBox->SetColor(glm::vec4(0.8f, 0.3f, 0.2f, 1.0f)); - m_testBox->Create(); + if (m_enableTestBox1) { + m_testBox = new Box(DrawMode_Shaded); + m_testBox->Translate(0.0f, -1.0f, 0.1f); + m_testBox->Scale(1.0f, 0.2f, 0.9f); + m_testBox->SetColor(glm::vec4(0.8f, 0.3f, 0.2f, 1.0f)); + m_testBox->Create(); + } - m_testBoxV = new Box(DrawMode_Shaded); - m_testBoxV->Translate(-1.0f, 0.0f, 0.0f); - m_testBoxV->Scale(0.2f, 1.0f, 0.9f); - m_testBoxV->SetColor(glm::vec4(0.3f, 0.8f, 0.2f, 1.0f)); - m_testBoxV->Create(); + if (m_enableTestBox2) { + m_testBoxV = new Box(DrawMode_Shaded); + m_testBoxV->Translate(-1.0f, 0.0f, 0.0f); + m_testBoxV->Scale(0.2f, 1.0f, 0.9f); + m_testBoxV->SetColor(glm::vec4(0.3f, 0.8f, 0.2f, 1.0f)); + m_testBoxV->Create(); + } } void @@ -148,10 +150,14 @@ Scene::Update( return; } // Rotate solid - m_testBox->Rotate(0.0f, deltaT * 100.0f, 0.0f); - m_testBox->Translate(0.0f, cos(deltaT) * 0.05f, 0.0f); + if (m_enableTestBox1) { + m_testBox->Rotate(0.0f, deltaT * 100.0f, 0.0f); + m_testBox->Translate(0.0f, cos(deltaT) * 0.05f, 0.0f); + } - m_testBoxV->Translate(cos(deltaT) * 0.05f, 0.0f, 0.0f); + if (m_enableTestBox2) { + m_testBoxV->Translate(cos(deltaT) * 0.05f, 0.0f, 0.0f); + } // Update solver UpdateFluidSolver(deltaT, progAdvect); @@ -253,8 +259,16 @@ Scene::UpdateFluidSolver( ParticleAdvectProgram& progAdvect ) { - m_fluidSolver->CheckBoxIntersection(m_testBox); - m_fluidSolver->CheckBoxIntersection(m_testBoxV); + // -- Test box 1 + if (m_enableTestBox1) { + m_fluidSolver->CheckBoxIntersection(m_testBox); + } + + // -- Test box 2 + if (m_enableTestBox2) { + m_fluidSolver->CheckBoxIntersection(m_testBoxV); + } + m_fluidSolver->Update(deltaT); m_fluidGeo->SetVelocities(m_fluidSolver->ParticleVelocities()); m_fluidGeo->SetPositions(m_fluidSolver->ParticlePositions()); @@ -273,8 +287,13 @@ Scene::DrawFluidSolver( prog.Draw(*m_camera, *m_fluidContainer); // -- Draw solids - prog.Draw(*m_camera, *m_testBox); - prog.Draw(*m_camera, *m_testBoxV); + if (m_enableTestBox1) { + prog.Draw(*m_camera, *m_testBox); + } + + if (m_enableTestBox2) { + prog.Draw(*m_camera, *m_testBoxV); + } // -- Draw particles m_fluidGeo->SetColors(m_fluidSolver->ParticleColors()); diff --git a/src/scene/scene.h b/src/scene/scene.h index 8899d202..48fdc70f 100644 --- a/src/scene/scene.h +++ b/src/scene/scene.h @@ -24,6 +24,12 @@ class Scene SPHConstantType type, float value ); + void EnableTestBoxOne(bool enable) { + m_enableTestBox1 = enable; + } + void EnableTestBoxTwo(bool enable) { + m_enableTestBox2 = enable; + } virtual void ReadInputs(KeyCode key, KeyAction action); virtual void Update( @@ -48,6 +54,8 @@ class Scene protected: bool m_paused = false; Camera* m_camera; + bool m_enableTestBox1 = false; + bool m_enableTestBox2 = false; Box* m_testBox; Box* m_testBoxV; Box* m_fluidContainer; From 78a35237dfcecae40f3a8838423b8ac0475b795a Mon Sep 17 00:00:00 2001 From: Trung Le Date: Sun, 10 Apr 2016 00:26:41 -0400 Subject: [PATCH 58/64] [SPRINT 3] - Added buttons to add solids during real time --- src/geometry/box.cpp | 12 ++--- src/glsl/simple.vert | 2 +- src/scene/scene.cpp | 71 +++++++++++++++++++---------- src/scene/scene.h | 14 +++++- src/shaderProgram/shaderProgram.cpp | 2 +- src/viewer.cpp | 28 +++++++++--- src/viewer.h | 7 ++- 7 files changed, 94 insertions(+), 42 deletions(-) diff --git a/src/geometry/box.cpp b/src/geometry/box.cpp index e4b0edd6..88c16075 100644 --- a/src/geometry/box.cpp +++ b/src/geometry/box.cpp @@ -59,27 +59,27 @@ Box::Intersect( switch(face) { case BoxFace_PosX: - localPos.x = 0.6f; + localPos.x = 0.55f; newVelocity = glm::vec3(1.0f, 0.0f, 0.0f); break; case BoxFace_NegX: - localPos.x = -0.6f; + localPos.x = -0.55f; newVelocity = glm::vec3(-1.0f, 0.0f, 0.0f); break; case BoxFace_PosY: - localPos.y = 0.6f; + localPos.y = 0.55f; newVelocity = glm::vec3(0.0f, 1.0f, 0.0f); break; case BoxFace_NegY: - localPos.y = -0.6f; + localPos.y = -0.55f; newVelocity = glm::vec3(0.0f, -1.0f, 0.0f); break; case BoxFace_PosZ: - localPos.z = 0.6f; + localPos.z = 0.55f; newVelocity = glm::vec3(0.0f, 0.0f, 1.0f); break; case BoxFace_NegZ: - localPos.z = -0.6f; + localPos.z = -0.55f; newVelocity = glm::vec3(0.0f, 0.0f, -1.0f); break; } diff --git a/src/glsl/simple.vert b/src/glsl/simple.vert index 69fd5d70..d34c402a 100644 --- a/src/glsl/simple.vert +++ b/src/glsl/simple.vert @@ -18,7 +18,7 @@ out vec4 v_color; out vec3 v_normal; out vec3 v_lightVec; -const vec4 lightPos = vec4(2, 1, 1, 1); // Virtual light +const vec4 lightPos = vec4(7, 5, -3, 1); // Virtual light void main() { diff --git a/src/scene/scene.cpp b/src/scene/scene.cpp index 3b345f4b..1199b4c7 100644 --- a/src/scene/scene.cpp +++ b/src/scene/scene.cpp @@ -9,8 +9,8 @@ Scene::Scene( const float& screenWidth, const float& screenHeight ) : - m_testBox(nullptr), - m_testBoxV(nullptr), + m_testBoxOne(nullptr), + m_testBoxTwo(nullptr), m_fluidContainer(nullptr), m_fluidGeo(nullptr), m_fluidSolver(nullptr) @@ -22,9 +22,9 @@ Scene::Scene( void Scene::InitFromTestScene() { - m_testBox = new Box(DrawMode_Wireframe); - m_testBox->SetDrawMode(DrawMode_Wireframe); - m_testBox->Create(); + m_testBoxOne = new Box(DrawMode_Wireframe); + m_testBoxOne->SetDrawMode(DrawMode_Wireframe); + m_testBoxOne->Create(); } void @@ -92,19 +92,11 @@ Scene::InitFromJson( // -- Solid object if (m_enableTestBox1) { - m_testBox = new Box(DrawMode_Shaded); - m_testBox->Translate(0.0f, -1.0f, 0.1f); - m_testBox->Scale(1.0f, 0.2f, 0.9f); - m_testBox->SetColor(glm::vec4(0.8f, 0.3f, 0.2f, 1.0f)); - m_testBox->Create(); + CreateTestBoxOne(); } if (m_enableTestBox2) { - m_testBoxV = new Box(DrawMode_Shaded); - m_testBoxV->Translate(-1.0f, 0.0f, 0.0f); - m_testBoxV->Scale(0.2f, 1.0f, 0.9f); - m_testBoxV->SetColor(glm::vec4(0.3f, 0.8f, 0.2f, 1.0f)); - m_testBoxV->Create(); + CreateTestBoxTwo(); } } @@ -151,12 +143,12 @@ Scene::Update( } // Rotate solid if (m_enableTestBox1) { - m_testBox->Rotate(0.0f, deltaT * 100.0f, 0.0f); - m_testBox->Translate(0.0f, cos(deltaT) * 0.05f, 0.0f); + m_testBoxOne->Rotate(0.0f, deltaT * 200.0f, 0.0f); + m_testBoxOne->Translate(0.0f, cos(deltaT) * 0.05f, 0.0f); } if (m_enableTestBox2) { - m_testBoxV->Translate(cos(deltaT) * 0.05f, 0.0f, 0.0f); + m_testBoxTwo->Translate(cos(deltaT) * 0.05f, 0.0f, 0.0f); } // Update solver @@ -168,7 +160,7 @@ Scene::Draw( const ShaderProgram& prog ) const { - prog.Draw(*m_camera, *m_testBox); + prog.Draw(*m_camera, *m_testBoxOne); } void @@ -187,8 +179,12 @@ Scene::CleanUp() delete m_camera; } - if (m_testBox != nullptr) { - delete m_testBox; + if (m_testBoxOne != nullptr) { + delete m_testBoxOne; + } + + if (m_testBoxTwo != nullptr) { + delete m_testBoxTwo; } if (m_fluidContainer != nullptr) { @@ -261,12 +257,12 @@ Scene::UpdateFluidSolver( { // -- Test box 1 if (m_enableTestBox1) { - m_fluidSolver->CheckBoxIntersection(m_testBox); + m_fluidSolver->CheckBoxIntersection(m_testBoxOne); } // -- Test box 2 if (m_enableTestBox2) { - m_fluidSolver->CheckBoxIntersection(m_testBoxV); + m_fluidSolver->CheckBoxIntersection(m_testBoxTwo); } m_fluidSolver->Update(deltaT); @@ -288,11 +284,11 @@ Scene::DrawFluidSolver( // -- Draw solids if (m_enableTestBox1) { - prog.Draw(*m_camera, *m_testBox); + prog.Draw(*m_camera, *m_testBoxOne); } if (m_enableTestBox2) { - prog.Draw(*m_camera, *m_testBoxV); + prog.Draw(*m_camera, *m_testBoxTwo); } // -- Draw particles @@ -300,3 +296,28 @@ Scene::DrawFluidSolver( progAdvect.Draw(m_camera, m_fluidGeo, m_fluidContainer); } + +void +Scene::CreateTestBoxOne() +{ + if (m_testBoxOne == nullptr) { + m_testBoxOne = new Box(DrawMode_Shaded); + m_testBoxOne->Translate(0.0f, -1.0f, 0.1f); + m_testBoxOne->Scale(1.0f, 0.2f, 0.9f); + m_testBoxOne->SetColor(glm::vec4(0.8f, 0.3f, 0.2f, 1.0f)); + m_testBoxOne->Create(); + } + +} + +void +Scene::CreateTestBoxTwo() +{ + if (m_testBoxTwo == nullptr) { + m_testBoxTwo = new Box(DrawMode_Shaded); + m_testBoxTwo->Translate(-1.0f, 0.0f, 0.0f); + m_testBoxTwo->Scale(0.2f, 1.0f, 0.9f); + m_testBoxTwo->SetColor(glm::vec4(0.3f, 0.8f, 0.2f, 1.0f)); + m_testBoxTwo->Create(); + } +} diff --git a/src/scene/scene.h b/src/scene/scene.h index 48fdc70f..65baf2fa 100644 --- a/src/scene/scene.h +++ b/src/scene/scene.h @@ -26,9 +26,15 @@ class Scene ); void EnableTestBoxOne(bool enable) { m_enableTestBox1 = enable; + if (enable) { + CreateTestBoxOne(); + } } void EnableTestBoxTwo(bool enable) { m_enableTestBox2 = enable; + if (enable) { + CreateTestBoxTwo(); + } } virtual void ReadInputs(KeyCode key, KeyAction action); @@ -56,12 +62,16 @@ class Scene Camera* m_camera; bool m_enableTestBox1 = false; bool m_enableTestBox2 = false; - Box* m_testBox; - Box* m_testBoxV; + Box* m_testBoxOne; + Box* m_testBoxTwo; Box* m_fluidContainer; FluidGeo* m_fluidGeo; SPHSolver* m_fluidSolver; + // -- Test box helpers + void CreateTestBoxOne(); + void CreateTestBoxTwo(); + // -- Update helpers void UpdateCamera(KeyCode key); void UpdateFluidSolver( diff --git a/src/shaderProgram/shaderProgram.cpp b/src/shaderProgram/shaderProgram.cpp index 18b9f05c..b86ebf38 100644 --- a/src/shaderProgram/shaderProgram.cpp +++ b/src/shaderProgram/shaderProgram.cpp @@ -13,7 +13,7 @@ ShaderProgram::ShaderProgram( glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); glEnable(GL_CULL_FACE); - glCullFace(GL_FRONT); + glCullFace(GL_BACK); } void diff --git a/src/viewer.cpp b/src/viewer.cpp index 0b58ebd9..f0ab9135 100644 --- a/src/viewer.cpp +++ b/src/viewer.cpp @@ -6,7 +6,10 @@ Viewer::Viewer( ) : nanogui::Screen(Eigen::Vector2i(width, height), "Korra Fluid Simulation"), m_program(nullptr), m_programAdvect(nullptr), - m_scene(nullptr) + m_scene(nullptr), + m_labelFps(nullptr), + m_checkBoxSolid1(nullptr), + m_checkBoxSolid2(nullptr) { m_width = width; m_height = height; @@ -93,8 +96,8 @@ Viewer::InitGUI() widget->setLayout(layout); // FPS - new Label(widget, "FPS: ", "sans-bold"); - m_labelFps = new Label(widget, to_string(m_fps), "sans-bold"); + // new Label(widget, "FPS: ", "sans-bold"); + // m_labelFps = new Label(widget, to_string(m_fps), "sans-bold"); new Label(widget, "Number of particles: ", "sans-bold"); new Label(widget, to_string(m_scene->NumParticles()), "sans-bold"); @@ -166,16 +169,25 @@ Viewer::InitGUI() label->setCaption(to_string(m_timeStep)); }); + m_checkBoxSolid1 = new CheckBox(widget, "Test box 1"); + m_checkBoxSolid1->setCallback([this](bool state) { + m_scene->EnableTestBoxOne(state); + }); + + m_checkBoxSolid2 = new CheckBox(widget, "Test box 2"); + m_checkBoxSolid2->setCallback([this](bool state) { + m_scene->EnableTestBoxTwo(state); + }); + // -- Coloring particles widget = new Widget(toolbox); widget->setLayout(new GroupLayout()); new Label(widget, "Particle color", "sans-bold"); - ComboBox* cb = new ComboBox(widget, { "Default", "Pressure", "Viscosity", "Velocity", "All forces"}); + ComboBox* cb = new ComboBox(widget, { "Blue", "Pressure", "Viscosity", "Velocity", "All forces"}); cb->setCallback([this](int index) { FluidParticle::colorType = (SPHColor)index; }); - // -- Buttons widget = new Widget(toolbox); widget->setLayout(new GroupLayout()); @@ -209,6 +221,10 @@ Viewer::ReplayScene() } m_scene = new Scene(m_width, m_height); m_scene->InitFromJson("../src/scene/scene.json"); + + // -- Update scene based on current state of GUI + m_scene->EnableTestBoxOne(m_checkBoxSolid1->checked()); + m_scene->EnableTestBoxTwo(m_checkBoxSolid2->checked()); } void @@ -227,7 +243,7 @@ Viewer::drawContents() double currentTime = glfwGetTime(); double deltaTime = double(currentTime - lastTime); m_fps = 1000.0 / deltaTime; - m_labelFps->setCaption(to_string(m_fps)); + // m_labelFps->setCaption(to_string(m_fps)); #ifdef TEST_SCENE m_scene->Draw(*m_program); diff --git a/src/viewer.h b/src/viewer.h index 6459d757..f7689ca1 100644 --- a/src/viewer.h +++ b/src/viewer.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #if defined(_WIN32) #include @@ -64,11 +65,15 @@ class Viewer :public nanogui::Screen float m_height; float m_timeStep = 0.001f; uint m_fps; - Label* m_labelFps; ShaderProgram* m_program; ParticleAdvectProgram* m_programAdvect; Scene* m_scene; + + // -- GUI variables + Label* m_labelFps; + CheckBox* m_checkBoxSolid1; + CheckBox* m_checkBoxSolid2; }; #endif /* viewer_hpp */ From 116a3c3b9bc6a0e07ff517e3186de50c839a8592 Mon Sep 17 00:00:00 2001 From: Trung Le Date: Sun, 10 Apr 2016 02:49:50 -0400 Subject: [PATCH 59/64] [SPRINT 3] - Cache ijk grid index --- src/fluidSolver/SPHGrid.cpp | 10 +++++++++- src/fluidSolver/SPHGrid.h | 3 +++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/fluidSolver/SPHGrid.cpp b/src/fluidSolver/SPHGrid.cpp index 8dba00fb..c32e03d0 100644 --- a/src/fluidSolver/SPHGrid.cpp +++ b/src/fluidSolver/SPHGrid.cpp @@ -10,7 +10,7 @@ SPHGrid::WriteVdb( openvdb::FloatGrid::Ptr grid = openvdb::FloatGrid::create(10.0); - CoordBBox indexBB(Coord(0, 0, 0), Coord(20, 20, 20)); + CoordBBox indexBB(Coord(0, 0, 0), Coord(m_width, m_height, m_depth)); MakeWaterSurface(grid, FluidParticle::separation, indexBB, m_cellSize, particles); // specify dataset name @@ -164,6 +164,13 @@ SPHGrid::GetCellIdx( #else // -- Uses z-curve indexing // See https://en.wikipedia.org/wiki/Z-order_curve + + auto key = make_tuple(i, j, k); + if (m_idxCache.find(key) != m_idxCache.end()) { + // We already cached the index, return the value + return m_idxCache[key]; + } + int i_temp = i; int j_temp = j; int k_temp = k; @@ -190,6 +197,7 @@ SPHGrid::GetCellIdx( // LOG(ERROR) << "Get cell idx is negative " + idx << endl; return -1; } + m_idxCache[key] = idx; #endif return idx; } diff --git a/src/fluidSolver/SPHGrid.h b/src/fluidSolver/SPHGrid.h index 484594ff..1075c012 100644 --- a/src/fluidSolver/SPHGrid.h +++ b/src/fluidSolver/SPHGrid.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -48,7 +49,9 @@ class SPHGrid glm::vec3 m_gridMax; bool m_useGrid; + // -- Grid std::vector> m_cells; + std::map, int> m_idxCache; // -- Open vdb void WriteVdb( From 5caac1719013b6dbb6dd33f910c9f838029e605b Mon Sep 17 00:00:00 2001 From: Trung Le Date: Sun, 10 Apr 2016 15:25:12 -0400 Subject: [PATCH 60/64] [SPRINT 3] - Added screenshot savings --- CMakeLists.txt | 18 +- nuparu/include/openvdb/bin/vdb_print | Bin 0 -> 4751928 bytes nuparu/include/openvdb/bin/vdb_render | Bin 0 -> 3173836 bytes nuparu/include/openvdb/bin/vdb_view | Bin 0 -> 2572520 bytes .../include/houdini_utils/OP_NodeChain.h | 167 + .../include/houdini_utils/ParmFactory.h | 486 ++ .../openvdb/include/houdini_utils/geometry.h | 72 + .../openvdb/include/openvdb/Exceptions.h | 112 + nuparu/include/openvdb/include/openvdb/Grid.h | 1404 +++++ .../openvdb/include/openvdb/Metadata.h | 41 + .../openvdb/include/openvdb/Platform.h | 210 + .../openvdb/include/openvdb/PlatformConfig.h | 57 + .../include/openvdb/include/openvdb/Types.h | 504 ++ .../openvdb/include/openvdb/io/Archive.h | 228 + .../openvdb/include/openvdb/io/Compression.h | 619 +++ .../include/openvdb/include/openvdb/io/File.h | 276 + .../include/openvdb/io/GridDescriptor.h | 135 + .../openvdb/include/openvdb/io/Queue.h | 277 + .../openvdb/include/openvdb/io/Stream.h | 120 + .../openvdb/include/openvdb/io/TempFile.h | 81 + .../include/openvdb/include/openvdb/io/io.h | 277 + .../openvdb/include/openvdb/math/BBox.h | 467 ++ .../include/openvdb/math/ConjGradient.h | 1841 +++++++ .../openvdb/include/openvdb/math/Coord.h | 528 ++ .../openvdb/include/openvdb/math/DDA.h | 373 ++ .../include/openvdb/math/FiniteDifference.h | 2376 +++++++++ .../openvdb/include/openvdb/math/Hermite.h | 493 ++ .../include/openvdb/math/LegacyFrustum.h | 196 + .../openvdb/include/openvdb/math/Maps.h | 2685 ++++++++++ .../openvdb/include/openvdb/math/Mat.h | 1026 ++++ .../openvdb/include/openvdb/math/Mat3.h | 867 +++ .../openvdb/include/openvdb/math/Mat4.h | 1398 +++++ .../openvdb/include/openvdb/math/Math.h | 912 ++++ .../openvdb/include/openvdb/math/Operators.h | 2123 ++++++++ .../openvdb/include/openvdb/math/Proximity.h | 79 + .../include/openvdb/math/QuantizedUnitVec.h | 166 + .../openvdb/include/openvdb/math/Quat.h | 658 +++ .../openvdb/include/openvdb/math/Ray.h | 342 ++ .../openvdb/include/openvdb/math/Stats.h | 399 ++ .../openvdb/include/openvdb/math/Stencils.h | 1680 ++++++ .../openvdb/include/openvdb/math/Transform.h | 310 ++ .../openvdb/include/openvdb/math/Tuple.h | 212 + .../openvdb/include/openvdb/math/Vec2.h | 553 ++ .../openvdb/include/openvdb/math/Vec3.h | 661 +++ .../openvdb/include/openvdb/math/Vec4.h | 594 +++ .../include/openvdb/metadata/MetaMap.h | 251 + .../include/openvdb/metadata/Metadata.h | 397 ++ .../include/openvdb/metadata/StringMetadata.h | 75 + .../include/openvdb/include/openvdb/openvdb.h | 99 + .../include/openvdb/tools/ChangeBackground.h | 278 + .../openvdb/include/openvdb/tools/Clip.h | 413 ++ .../openvdb/include/openvdb/tools/Composite.h | 1107 ++++ .../openvdb/include/openvdb/tools/Dense.h | 608 +++ .../include/openvdb/tools/DenseSparseTools.h | 1259 +++++ .../include/openvdb/tools/Diagnostics.h | 1344 +++++ .../openvdb/include/openvdb/tools/Filter.h | 459 ++ .../include/openvdb/tools/GridOperators.h | 1090 ++++ .../include/openvdb/tools/GridTransformer.h | 1039 ++++ .../include/openvdb/tools/Interpolation.h | 1040 ++++ .../include/openvdb/tools/LevelSetAdvect.h | 569 ++ .../include/openvdb/tools/LevelSetFilter.h | 546 ++ .../include/openvdb/tools/LevelSetFracture.h | 346 ++ .../include/openvdb/tools/LevelSetMeasure.h | 567 ++ .../include/openvdb/tools/LevelSetMorph.h | 664 +++ .../include/openvdb/tools/LevelSetPlatonic.h | 494 ++ .../include/openvdb/tools/LevelSetRebuild.h | 353 ++ .../include/openvdb/tools/LevelSetSphere.h | 222 + .../include/openvdb/tools/LevelSetTracker.h | 650 +++ .../include/openvdb/tools/LevelSetUtil.h | 2554 +++++++++ .../include/openvdb/tools/MaskToLevelSet.h | 199 + .../include/openvdb/tools/MeshToVolume.h | 4032 ++++++++++++++ .../include/openvdb/tools/Morphology.h | 1097 ++++ .../include/openvdb/tools/MultiResGrid.h | 981 ++++ .../include/openvdb/tools/ParticleAtlas.h | 1052 ++++ .../openvdb/tools/ParticlesToLevelSet.h | 924 ++++ .../include/openvdb/tools/PointAdvect.h | 424 ++ .../include/openvdb/tools/PointIndexGrid.h | 1822 +++++++ .../include/openvdb/tools/PointMaskGrid.h | 283 + .../include/openvdb/tools/PointPartitioner.h | 1038 ++++ .../include/openvdb/tools/PointScatter.h | 437 ++ .../include/openvdb/tools/PoissonSolver.h | 774 +++ .../openvdb/include/openvdb/tools/Prune.h | 417 ++ .../include/openvdb/tools/RayIntersector.h | 701 +++ .../openvdb/include/openvdb/tools/RayTracer.h | 1102 ++++ .../include/openvdb/tools/SignedFloodFill.h | 311 ++ .../include/openvdb/tools/Statistics.h | 438 ++ .../openvdb/tools/TopologyToLevelSet.h | 284 + .../include/openvdb/tools/ValueTransformer.h | 707 +++ .../include/openvdb/tools/VectorTransformer.h | 158 + .../include/openvdb/tools/VelocityFields.h | 305 ++ .../include/openvdb/tools/VolumeAdvect.h | 571 ++ .../include/openvdb/tools/VolumeToMesh.h | 4690 +++++++++++++++++ .../include/openvdb/tools/VolumeToSpheres.h | 1034 ++++ .../include/openvdb/tree/InternalNode.h | 3201 +++++++++++ .../openvdb/include/openvdb/tree/Iterator.h | 290 + .../include/openvdb/tree/LeafManager.h | 846 +++ .../openvdb/include/openvdb/tree/LeafNode.h | 2219 ++++++++ .../include/openvdb/tree/LeafNodeBool.h | 1773 +++++++ .../include/openvdb/tree/LeafNodeMask.h | 1687 ++++++ .../include/openvdb/tree/NodeManager.h | 1128 ++++ .../openvdb/include/openvdb/tree/NodeUnion.h | 137 + .../openvdb/include/openvdb/tree/RootNode.h | 3411 ++++++++++++ .../openvdb/include/openvdb/tree/Tree.h | 2337 ++++++++ .../include/openvdb/tree/TreeIterator.h | 1405 +++++ .../include/openvdb/tree/ValueAccessor.h | 2666 ++++++++++ .../openvdb/include/openvdb/util/CpuTimer.h | 128 + .../openvdb/include/openvdb/util/Formats.h | 140 + .../openvdb/include/openvdb/util/MapsUtil.h | 321 ++ .../openvdb/include/openvdb/util/Name.h | 72 + .../openvdb/include/openvdb/util/NodeMasks.h | 1411 +++++ .../include/openvdb/util/NullInterrupter.h | 90 + .../openvdb/include/openvdb/util/PagedArray.h | 735 +++ .../openvdb/include/openvdb/util/Util.h | 166 + .../openvdb/include/openvdb/util/logging.h | 83 + .../include/openvdb/include/openvdb/version.h | 133 + .../openvdb_houdini/AttributeTransferUtil.h | 1495 ++++++ .../include/openvdb_houdini/GEO_PrimVDB.h | 834 +++ .../openvdb_houdini/GT_GEOPrimCollectVDB.h | 92 + .../include/openvdb_houdini/GU_PrimVDB.h | 355 ++ .../openvdb_houdini/GU_VDBPointTools.h | 434 ++ .../include/openvdb_houdini/GeometryUtil.h | 360 ++ .../include/openvdb_houdini/SOP_NodeVDB.h | 230 + .../openvdb_houdini/SOP_NodeVDBPoints.h | 272 + .../include/openvdb_houdini/UT_VDBTools.h | 203 + .../include/openvdb_houdini/UT_VDBUtils.h | 706 +++ .../openvdb/include/openvdb_houdini/Utils.h | 423 ++ .../openvdb/include/openvdb_points/Types.h | 65 + .../openvdb/include/openvdb_points/openvdb.h | 56 + .../openvdb_points/tools/AttributeArray.h | 1573 ++++++ .../openvdb_points/tools/AttributeGroup.h | 209 + .../openvdb_points/tools/AttributeSet.h | 405 ++ .../openvdb_points/tools/IndexFilter.h | 161 + .../openvdb_points/tools/IndexIterator.h | 347 ++ .../openvdb_points/tools/PointAttribute.h | 545 ++ .../openvdb_points/tools/PointConversion.h | 414 ++ .../include/openvdb_points/tools/PointCount.h | 277 + .../openvdb_points/tools/PointDataGrid.h | 920 ++++ .../include/openvdb_points/tools/PointGroup.h | 671 +++ .../include/openvdb_points/tools/PointLoad.h | 161 + .../openvdb/include/openvdb_points/version.h | 85 + .../include/openvdb/lib/libHalf.12.0.0.dylib | Bin 0 -> 277624 bytes ...ost_iostreams-clang-darwin61-mt-1_55.dylib | Bin 0 -> 103104 bytes .../openvdb/lib/libopenvdb.3.0.0.dylib | Bin 0 -> 2643180 bytes .../include/openvdb/lib/libopenvdb.3.0.dylib | Bin 0 -> 2643180 bytes .../openvdb/lib/libopenvdb.3.2.0.dylib | Bin 0 -> 2967516 bytes .../include/openvdb/lib/libopenvdb.3.2.dylib | Bin 0 -> 2967516 bytes nuparu/include/openvdb/lib/libopenvdb.a | Bin 0 -> 5135088 bytes nuparu/include/openvdb/lib/libopenvdb.dylib | Bin 0 -> 2967516 bytes .../include/openvdb/lib/libopenvdb_houdini.so | Bin 0 -> 566052 bytes .../openvdb/lib/libopenvdb_houdini.so.3.2.0 | Bin 0 -> 566052 bytes .../openvdb/lib/libopenvdb_points.0.2.0.dylib | Bin 0 -> 831568 bytes .../openvdb/lib/libopenvdb_points.0.2.dylib | Bin 0 -> 831568 bytes .../include/openvdb/lib/libopenvdb_points.a | Bin 0 -> 1263072 bytes .../openvdb/lib/libopenvdb_points.dylib | Bin 0 -> 831568 bytes nuparu/include/openvdb/lib/libz.1.2.7.dylib | Bin 0 -> 92364 bytes .../python/include/python2.7/pyopenvdb.h | 113 + .../openvdb/python/lib/python2.7/pyopenvdb.so | Bin 0 -> 11187560 bytes .../python/lib/python2.7/pyopenvdb.so.3.0 | Bin 0 -> 11187560 bytes .../python/lib/python2.7/pyopenvdb.so.3.2 | Bin 0 -> 11187560 bytes .../doc/openvdb/html/Archive_8h_source.html | 284 + .../doc/openvdb/html/BBox_8h_source.html | 563 ++ .../html/ChangeBackground_8h_source.html | 356 ++ .../doc/openvdb/html/Clip_8h_source.html | 549 ++ .../doc/openvdb/html/Composite_8h_source.html | 1267 +++++ .../openvdb/html/Compression_8h_source.html | 684 +++ .../openvdb/html/ConjGradient_8h_source.html | 1965 +++++++ .../doc/openvdb/html/Coord_8h_source.html | 686 +++ .../doc/openvdb/html/CpuTimer_8h_source.html | 206 + .../share/doc/openvdb/html/DDA_8h_source.html | 463 ++ .../html/DenseSparseTools_8h_source.html | 1353 +++++ .../doc/openvdb/html/Dense_8h_source.html | 636 +++ .../openvdb/html/Diagnostics_8h_source.html | 1380 +++++ .../openvdb/html/Exceptions_8h_source.html | 230 + .../doc/openvdb/html/File_8h_source.html | 322 ++ .../doc/openvdb/html/Filter_8h_source.html | 537 ++ .../html/FiniteDifference_8h_source.html | 2694 ++++++++++ .../doc/openvdb/html/Formats_8h_source.html | 227 + .../share/doc/openvdb/html/FreeSans.ttf | Bin 0 -> 22932 bytes .../html/GridDescriptor_8h_source.html | 230 + .../openvdb/html/GridOperators_8h_source.html | 1279 +++++ .../html/GridTransformer_8h_source.html | 1116 ++++ .../doc/openvdb/html/Grid_8h_source.html | 1383 +++++ .../openvdb/html/InternalNode_8h_source.html | 3255 ++++++++++++ .../openvdb/html/Interpolation_8h_source.html | 1082 ++++ .../doc/openvdb/html/Iterator_8h_source.html | 369 ++ .../openvdb/html/LeafManager_8h_source.html | 802 +++ .../openvdb/html/LeafNodeBool_8h_source.html | 1931 +++++++ .../openvdb/html/LeafNodeMask_8h_source.html | 1829 +++++++ .../doc/openvdb/html/LeafNode_8h_source.html | 2325 ++++++++ .../openvdb/html/LegacyFrustum_8h_source.html | 325 ++ .../html/LevelSetAdvect_8h_source.html | 651 +++ .../html/LevelSetFilter_8h_source.html | 622 +++ .../html/LevelSetFracture_8h_source.html | 457 ++ .../html/LevelSetMeasure_8h_source.html | 644 +++ .../openvdb/html/LevelSetMorph_8h_source.html | 758 +++ .../html/LevelSetPlatonic_8h_source.html | 482 ++ .../html/LevelSetRebuild_8h_source.html | 447 ++ .../html/LevelSetSphere_8h_source.html | 292 + .../html/LevelSetTracker_8h_source.html | 786 +++ .../openvdb/html/LevelSetUtil_8h_source.html | 2786 ++++++++++ .../doc/openvdb/html/MapsUtil_8h_source.html | 427 ++ .../doc/openvdb/html/Maps_8h_source.html | 2837 ++++++++++ .../html/MaskToLevelSet_8h_source.html | 294 ++ .../doc/openvdb/html/Mat3_8h_source.html | 938 ++++ .../doc/openvdb/html/Mat4_8h_source.html | 1492 ++++++ .../share/doc/openvdb/html/Mat_8h_source.html | 1097 ++++ .../doc/openvdb/html/Math_8h_source.html | 963 ++++ .../openvdb/html/MeshToVolume_8h_source.html | 663 +++ .../share/doc/openvdb/html/arrowdown.png | Bin 0 -> 243 bytes .../share/doc/openvdb/html/arrowright.png | Bin 0 -> 234 bytes .../openvdb/share/doc/openvdb/html/bc_s.png | Bin 0 -> 635 bytes .../openvdb/share/doc/openvdb/html/bdwn.png | Bin 0 -> 148 bytes .../openvdb/share/doc/openvdb/html/closed.png | Bin 0 -> 133 bytes .../openvdb/share/doc/openvdb/html/doc.png | Bin 0 -> 788 bytes .../share/doc/openvdb/html/doxygen.css | 1449 +++++ .../share/doc/openvdb/html/doxygen.png | Bin 0 -> 3844 bytes .../share/doc/openvdb/html/dynsections.js | 97 + .../share/doc/openvdb/html/folderclosed.png | Bin 0 -> 586 bytes .../share/doc/openvdb/html/folderopen.png | Bin 0 -> 619 bytes .../share/doc/openvdb/html/io_8h_source.html | 330 ++ .../openvdb/share/doc/openvdb/html/jquery.js | 68 + .../doc/openvdb/html/logging_8h_source.html | 176 + .../openvdb/share/doc/openvdb/html/nav_f.png | Bin 0 -> 134 bytes .../openvdb/share/doc/openvdb/html/nav_g.png | Bin 0 -> 95 bytes .../openvdb/share/doc/openvdb/html/nav_h.png | Bin 0 -> 97 bytes .../openvdb/share/doc/openvdb/html/open.png | Bin 0 -> 122 bytes .../share/doc/openvdb/html/search/all_0.html | 26 + .../share/doc/openvdb/html/search/all_0.js | 8 + .../share/doc/openvdb/html/search/all_1.html | 26 + .../share/doc/openvdb/html/search/all_1.js | 6 + .../share/doc/openvdb/html/search/all_10.html | 26 + .../share/doc/openvdb/html/search/all_10.js | 198 + .../share/doc/openvdb/html/search/all_11.html | 26 + .../share/doc/openvdb/html/search/all_11.js | 176 + .../share/doc/openvdb/html/search/all_12.html | 26 + .../share/doc/openvdb/html/search/all_12.js | 21 + .../share/doc/openvdb/html/search/all_13.html | 26 + .../share/doc/openvdb/html/search/all_13.js | 159 + .../share/doc/openvdb/html/search/all_14.html | 26 + .../share/doc/openvdb/html/search/all_14.js | 374 ++ .../share/doc/openvdb/html/search/all_15.html | 26 + .../share/doc/openvdb/html/search/all_15.js | 189 + .../share/doc/openvdb/html/search/all_16.html | 26 + .../share/doc/openvdb/html/search/all_16.js | 59 + .../share/doc/openvdb/html/search/all_17.html | 26 + .../share/doc/openvdb/html/search/all_17.js | 256 + .../share/doc/openvdb/html/search/all_18.html | 26 + .../share/doc/openvdb/html/search/all_18.js | 36 + .../share/doc/openvdb/html/search/all_19.html | 26 + .../share/doc/openvdb/html/search/all_19.js | 11 + .../share/doc/openvdb/html/search/all_1a.html | 26 + .../share/doc/openvdb/html/search/all_1a.js | 9 + .../share/doc/openvdb/html/search/all_1b.html | 26 + .../share/doc/openvdb/html/search/all_1b.js | 18 + .../share/doc/openvdb/html/search/all_1c.html | 26 + .../share/doc/openvdb/html/search/all_1c.js | 84 + .../share/doc/openvdb/html/search/all_2.html | 26 + .../share/doc/openvdb/html/search/all_2.js | 102 + .../share/doc/openvdb/html/search/all_3.html | 26 + .../share/doc/openvdb/html/search/all_3.js | 123 + .../share/doc/openvdb/html/search/all_4.html | 26 + .../share/doc/openvdb/html/search/all_4.js | 384 ++ .../share/doc/openvdb/html/search/all_5.html | 26 + .../share/doc/openvdb/html/search/all_5.js | 186 + .../share/doc/openvdb/html/search/all_6.html | 26 + .../share/doc/openvdb/html/search/all_6.js | 77 + .../share/doc/openvdb/html/search/all_7.html | 26 + .../share/doc/openvdb/html/search/all_7.js | 111 + .../share/doc/openvdb/html/search/all_8.html | 26 + .../share/doc/openvdb/html/search/all_8.js | 257 + .../share/doc/openvdb/html/search/all_9.html | 26 + .../share/doc/openvdb/html/search/all_9.js | 33 + .../share/doc/openvdb/html/search/all_a.html | 26 + .../share/doc/openvdb/html/search/all_a.js | 299 ++ .../share/doc/openvdb/html/search/all_b.html | 26 + .../share/doc/openvdb/html/search/all_b.js | 6 + .../share/doc/openvdb/html/search/all_c.html | 26 + .../share/doc/openvdb/html/search/all_c.js | 5 + .../share/doc/openvdb/html/search/all_d.html | 26 + .../share/doc/openvdb/html/search/all_d.js | 140 + .../share/doc/openvdb/html/search/all_e.html | 26 + .../share/doc/openvdb/html/search/all_e.js | 344 ++ .../share/doc/openvdb/html/search/all_f.html | 26 + .../share/doc/openvdb/html/search/all_f.js | 163 + .../doc/openvdb/html/search/classes_0.html | 26 + .../doc/openvdb/html/search/classes_0.js | 16 + .../doc/openvdb/html/search/classes_1.html | 26 + .../doc/openvdb/html/search/classes_1.js | 43 + .../doc/openvdb/html/search/classes_10.html | 26 + .../doc/openvdb/html/search/classes_10.js | 8 + .../doc/openvdb/html/search/classes_11.html | 26 + .../doc/openvdb/html/search/classes_11.js | 36 + .../doc/openvdb/html/search/classes_12.html | 26 + .../doc/openvdb/html/search/classes_12.js | 68 + .../doc/openvdb/html/search/classes_13.html | 26 + .../doc/openvdb/html/search/classes_13.js | 64 + .../doc/openvdb/html/search/classes_14.html | 26 + .../doc/openvdb/html/search/classes_14.js | 14 + .../doc/openvdb/html/search/classes_15.html | 26 + .../doc/openvdb/html/search/classes_15.js | 100 + .../doc/openvdb/html/search/classes_16.html | 26 + .../doc/openvdb/html/search/classes_16.js | 4 + .../doc/openvdb/html/search/classes_2.html | 26 + .../doc/openvdb/html/search/classes_2.js | 105 + .../doc/openvdb/html/search/classes_3.html | 26 + .../doc/openvdb/html/search/classes_3.js | 84 + .../doc/openvdb/html/search/classes_4.html | 26 + .../doc/openvdb/html/search/classes_4.js | 12 + .../doc/openvdb/html/search/classes_5.html | 26 + .../doc/openvdb/html/search/classes_5.js | 28 + .../doc/openvdb/html/search/classes_6.html | 26 + .../doc/openvdb/html/search/classes_6.js | 41 + .../doc/openvdb/html/search/classes_7.html | 26 + .../doc/openvdb/html/search/classes_7.js | 12 + .../doc/openvdb/html/search/classes_8.html | 26 + .../doc/openvdb/html/search/classes_8.js | 93 + .../doc/openvdb/html/search/classes_9.html | 26 + .../doc/openvdb/html/search/classes_9.js | 4 + .../doc/openvdb/html/search/classes_a.html | 26 + .../doc/openvdb/html/search/classes_a.js | 4 + .../doc/openvdb/html/search/classes_b.html | 26 + .../doc/openvdb/html/search/classes_b.js | 44 + .../doc/openvdb/html/search/classes_c.html | 26 + .../doc/openvdb/html/search/classes_c.js | 56 + .../doc/openvdb/html/search/classes_d.html | 26 + .../doc/openvdb/html/search/classes_d.js | 56 + .../doc/openvdb/html/search/classes_e.html | 26 + .../doc/openvdb/html/search/classes_e.js | 21 + .../doc/openvdb/html/search/classes_f.html | 26 + .../doc/openvdb/html/search/classes_f.js | 34 + .../share/doc/openvdb/html/search/close.png | Bin 0 -> 273 bytes .../doc/openvdb/html/search/defines_0.html | 26 + .../doc/openvdb/html/search/defines_0.js | 4 + .../doc/openvdb/html/search/defines_1.html | 26 + .../doc/openvdb/html/search/defines_1.js | 6 + .../doc/openvdb/html/search/defines_2.html | 26 + .../doc/openvdb/html/search/defines_2.js | 4 + .../doc/openvdb/html/search/defines_3.html | 26 + .../doc/openvdb/html/search/defines_3.js | 43 + .../doc/openvdb/html/search/defines_4.html | 26 + .../doc/openvdb/html/search/defines_4.js | 4 + .../doc/openvdb/html/search/defines_5.html | 26 + .../doc/openvdb/html/search/defines_5.js | 4 + .../doc/openvdb/html/search/defines_6.html | 26 + .../doc/openvdb/html/search/defines_6.js | 4 + .../doc/openvdb/html/search/enums_0.html | 26 + .../share/doc/openvdb/html/search/enums_0.js | 4 + .../doc/openvdb/html/search/enums_1.html | 26 + .../share/doc/openvdb/html/search/enums_1.js | 4 + .../doc/openvdb/html/search/enums_2.html | 26 + .../share/doc/openvdb/html/search/enums_2.js | 5 + .../doc/openvdb/html/search/enums_3.html | 26 + .../share/doc/openvdb/html/search/enums_3.js | 6 + .../doc/openvdb/html/search/enums_4.html | 26 + .../share/doc/openvdb/html/search/enums_4.js | 4 + .../doc/openvdb/html/search/enums_5.html | 26 + .../share/doc/openvdb/html/search/enums_5.js | 4 + .../doc/openvdb/html/search/enums_6.html | 26 + .../share/doc/openvdb/html/search/enums_6.js | 6 + .../doc/openvdb/html/search/enums_7.html | 26 + .../share/doc/openvdb/html/search/enums_7.js | 4 + .../doc/openvdb/html/search/enums_8.html | 26 + .../share/doc/openvdb/html/search/enums_8.js | 4 + .../doc/openvdb/html/search/enums_9.html | 26 + .../share/doc/openvdb/html/search/enums_9.js | 6 + .../doc/openvdb/html/search/enums_a.html | 26 + .../share/doc/openvdb/html/search/enums_a.js | 5 + .../doc/openvdb/html/search/enums_b.html | 26 + .../share/doc/openvdb/html/search/enums_b.js | 4 + .../doc/openvdb/html/search/enumvalues_0.html | 26 + .../doc/openvdb/html/search/enumvalues_0.js | 9 + .../doc/openvdb/html/search/enumvalues_1.html | 26 + .../doc/openvdb/html/search/enumvalues_1.js | 21 + .../openvdb/html/search/enumvalues_10.html | 26 + .../doc/openvdb/html/search/enumvalues_10.js | 9 + .../openvdb/html/search/enumvalues_11.html | 26 + .../doc/openvdb/html/search/enumvalues_11.js | 9 + .../openvdb/html/search/enumvalues_12.html | 26 + .../doc/openvdb/html/search/enumvalues_12.js | 4 + .../openvdb/html/search/enumvalues_13.html | 26 + .../doc/openvdb/html/search/enumvalues_13.js | 8 + .../openvdb/html/search/enumvalues_14.html | 26 + .../doc/openvdb/html/search/enumvalues_14.js | 7 + .../openvdb/html/search/enumvalues_15.html | 26 + .../doc/openvdb/html/search/enumvalues_15.js | 8 + .../doc/openvdb/html/search/enumvalues_2.html | 26 + .../doc/openvdb/html/search/enumvalues_2.js | 13 + .../doc/openvdb/html/search/enumvalues_3.html | 26 + .../doc/openvdb/html/search/enumvalues_3.js | 5 + .../doc/openvdb/html/search/enumvalues_4.html | 26 + .../doc/openvdb/html/search/enumvalues_4.js | 10 + .../doc/openvdb/html/search/enumvalues_5.html | 26 + .../doc/openvdb/html/search/enumvalues_5.js | 7 + .../doc/openvdb/html/search/enumvalues_6.html | 26 + .../doc/openvdb/html/search/enumvalues_6.js | 4 + .../doc/openvdb/html/search/enumvalues_7.html | 26 + .../doc/openvdb/html/search/enumvalues_7.js | 7 + .../doc/openvdb/html/search/enumvalues_8.html | 26 + .../doc/openvdb/html/search/enumvalues_8.js | 6 + .../doc/openvdb/html/search/enumvalues_9.html | 26 + .../doc/openvdb/html/search/enumvalues_9.js | 14 + .../doc/openvdb/html/search/enumvalues_a.html | 26 + .../doc/openvdb/html/search/enumvalues_a.js | 17 + .../doc/openvdb/html/search/enumvalues_b.html | 26 + .../doc/openvdb/html/search/enumvalues_b.js | 16 + .../doc/openvdb/html/search/enumvalues_c.html | 26 + .../doc/openvdb/html/search/enumvalues_c.js | 8 + .../doc/openvdb/html/search/enumvalues_d.html | 26 + .../doc/openvdb/html/search/enumvalues_d.js | 6 + .../doc/openvdb/html/search/enumvalues_e.html | 26 + .../doc/openvdb/html/search/enumvalues_e.js | 10 + .../doc/openvdb/html/search/enumvalues_f.html | 26 + .../doc/openvdb/html/search/enumvalues_f.js | 7 + .../doc/openvdb/html/search/files_0.html | 26 + .../share/doc/openvdb/html/search/files_0.js | 5 + .../doc/openvdb/html/search/files_1.html | 26 + .../share/doc/openvdb/html/search/files_1.js | 4 + .../doc/openvdb/html/search/files_10.html | 26 + .../share/doc/openvdb/html/search/files_10.js | 10 + .../doc/openvdb/html/search/files_11.html | 26 + .../share/doc/openvdb/html/search/files_11.js | 4 + .../doc/openvdb/html/search/files_12.html | 26 + .../share/doc/openvdb/html/search/files_12.js | 14 + .../doc/openvdb/html/search/files_2.html | 26 + .../share/doc/openvdb/html/search/files_2.js | 12 + .../doc/openvdb/html/search/files_3.html | 26 + .../share/doc/openvdb/html/search/files_3.js | 8 + .../doc/openvdb/html/search/files_4.html | 26 + .../share/doc/openvdb/html/search/files_4.js | 5 + .../doc/openvdb/html/search/files_5.html | 26 + .../share/doc/openvdb/html/search/files_5.js | 8 + .../doc/openvdb/html/search/files_6.html | 26 + .../share/doc/openvdb/html/search/files_6.js | 7 + .../doc/openvdb/html/search/files_7.html | 26 + .../share/doc/openvdb/html/search/files_7.js | 7 + .../doc/openvdb/html/search/files_8.html | 26 + .../share/doc/openvdb/html/search/files_8.js | 19 + .../doc/openvdb/html/search/files_9.html | 26 + .../share/doc/openvdb/html/search/files_9.js | 17 + .../doc/openvdb/html/search/files_a.html | 26 + .../share/doc/openvdb/html/search/files_a.js | 8 + .../doc/openvdb/html/search/files_b.html | 26 + .../share/doc/openvdb/html/search/files_b.js | 5 + .../doc/openvdb/html/search/files_c.html | 26 + .../share/doc/openvdb/html/search/files_c.js | 18 + .../doc/openvdb/html/search/files_d.html | 26 + .../share/doc/openvdb/html/search/files_d.js | 6 + .../doc/openvdb/html/search/files_e.html | 26 + .../share/doc/openvdb/html/search/files_e.js | 7 + .../doc/openvdb/html/search/files_f.html | 26 + .../share/doc/openvdb/html/search/files_f.js | 9 + .../doc/openvdb/html/search/functions_0.html | 26 + .../doc/openvdb/html/search/functions_0.js | 73 + .../doc/openvdb/html/search/functions_1.html | 26 + .../doc/openvdb/html/search/functions_1.js | 47 + .../doc/openvdb/html/search/functions_10.html | 26 + .../doc/openvdb/html/search/functions_10.js | 8 + .../doc/openvdb/html/search/functions_11.html | 26 + .../doc/openvdb/html/search/functions_11.js | 90 + .../doc/openvdb/html/search/functions_12.html | 26 + .../doc/openvdb/html/search/functions_12.js | 263 + .../doc/openvdb/html/search/functions_13.html | 26 + .../doc/openvdb/html/search/functions_13.js | 92 + .../doc/openvdb/html/search/functions_14.html | 26 + .../doc/openvdb/html/search/functions_14.js | 32 + .../doc/openvdb/html/search/functions_15.html | 26 + .../doc/openvdb/html/search/functions_15.js | 53 + .../doc/openvdb/html/search/functions_16.html | 26 + .../doc/openvdb/html/search/functions_16.js | 31 + .../doc/openvdb/html/search/functions_17.html | 26 + .../doc/openvdb/html/search/functions_17.js | 6 + .../doc/openvdb/html/search/functions_18.html | 26 + .../doc/openvdb/html/search/functions_18.js | 5 + .../doc/openvdb/html/search/functions_19.html | 26 + .../doc/openvdb/html/search/functions_19.js | 13 + .../doc/openvdb/html/search/functions_1a.html | 26 + .../doc/openvdb/html/search/functions_1a.js | 84 + .../doc/openvdb/html/search/functions_2.html | 26 + .../doc/openvdb/html/search/functions_2.js | 218 + .../doc/openvdb/html/search/functions_3.html | 26 + .../doc/openvdb/html/search/functions_3.js | 71 + .../doc/openvdb/html/search/functions_4.html | 26 + .../doc/openvdb/html/search/functions_4.js | 62 + .../doc/openvdb/html/search/functions_5.html | 26 + .../doc/openvdb/html/search/functions_5.js | 60 + .../doc/openvdb/html/search/functions_6.html | 26 + .../doc/openvdb/html/search/functions_6.js | 187 + .../doc/openvdb/html/search/functions_7.html | 26 + .../doc/openvdb/html/search/functions_7.js | 21 + .../doc/openvdb/html/search/functions_8.html | 26 + .../doc/openvdb/html/search/functions_8.js | 134 + .../doc/openvdb/html/search/functions_9.html | 26 + .../doc/openvdb/html/search/functions_9.js | 5 + .../doc/openvdb/html/search/functions_a.html | 26 + .../doc/openvdb/html/search/functions_a.js | 4 + .../doc/openvdb/html/search/functions_b.html | 26 + .../doc/openvdb/html/search/functions_b.js | 52 + .../doc/openvdb/html/search/functions_c.html | 26 + .../doc/openvdb/html/search/functions_c.js | 85 + .../doc/openvdb/html/search/functions_d.html | 26 + .../doc/openvdb/html/search/functions_d.js | 53 + .../doc/openvdb/html/search/functions_e.html | 26 + .../doc/openvdb/html/search/functions_e.js | 72 + .../doc/openvdb/html/search/functions_f.html | 26 + .../doc/openvdb/html/search/functions_f.js | 106 + .../share/doc/openvdb/html/search/mag_sel.png | Bin 0 -> 563 bytes .../doc/openvdb/html/search/namespaces_0.html | 26 + .../doc/openvdb/html/search/namespaces_0.js | 36 + .../doc/openvdb/html/search/namespaces_1.html | 26 + .../doc/openvdb/html/search/namespaces_1.js | 4 + .../doc/openvdb/html/search/namespaces_2.html | 26 + .../doc/openvdb/html/search/namespaces_2.js | 4 + .../doc/openvdb/html/search/nomatches.html | 12 + .../doc/openvdb/html/search/pages_0.html | 26 + .../share/doc/openvdb/html/search/pages_0.js | 4 + .../doc/openvdb/html/search/pages_1.html | 26 + .../share/doc/openvdb/html/search/pages_1.js | 4 + .../doc/openvdb/html/search/pages_2.html | 26 + .../share/doc/openvdb/html/search/pages_2.js | 6 + .../doc/openvdb/html/search/pages_3.html | 26 + .../share/doc/openvdb/html/search/pages_3.js | 4 + .../doc/openvdb/html/search/pages_4.html | 26 + .../share/doc/openvdb/html/search/pages_4.js | 4 + .../doc/openvdb/html/search/pages_5.html | 26 + .../share/doc/openvdb/html/search/pages_5.js | 4 + .../doc/openvdb/html/search/pages_6.html | 26 + .../share/doc/openvdb/html/search/pages_6.js | 4 + .../doc/openvdb/html/search/related_0.html | 26 + .../doc/openvdb/html/search/related_0.js | 8 + .../doc/openvdb/html/search/related_1.html | 26 + .../doc/openvdb/html/search/related_1.js | 4 + .../doc/openvdb/html/search/related_2.html | 26 + .../doc/openvdb/html/search/related_2.js | 4 + .../doc/openvdb/html/search/related_3.html | 26 + .../doc/openvdb/html/search/related_3.js | 13 + .../doc/openvdb/html/search/related_4.html | 26 + .../doc/openvdb/html/search/related_4.js | 4 + .../doc/openvdb/html/search/related_5.html | 26 + .../doc/openvdb/html/search/related_5.js | 4 + .../doc/openvdb/html/search/related_6.html | 26 + .../doc/openvdb/html/search/related_6.js | 4 + .../doc/openvdb/html/search/related_7.html | 26 + .../doc/openvdb/html/search/related_7.js | 4 + .../doc/openvdb/html/search/related_8.html | 26 + .../doc/openvdb/html/search/related_8.js | 6 + .../doc/openvdb/html/search/related_9.html | 26 + .../doc/openvdb/html/search/related_9.js | 5 + .../doc/openvdb/html/search/related_a.html | 26 + .../doc/openvdb/html/search/related_a.js | 7 + .../doc/openvdb/html/search/related_b.html | 26 + .../doc/openvdb/html/search/related_b.js | 18 + .../share/doc/openvdb/html/search/search.css | 271 + .../share/doc/openvdb/html/search/search.js | 791 +++ .../doc/openvdb/html/search/search_l.png | Bin 0 -> 604 bytes .../doc/openvdb/html/search/search_m.png | Bin 0 -> 158 bytes .../doc/openvdb/html/search/search_r.png | Bin 0 -> 612 bytes .../doc/openvdb/html/search/searchdata.js | 48 + .../doc/openvdb/html/search/typedefs_0.html | 26 + .../doc/openvdb/html/search/typedefs_0.js | 5 + .../doc/openvdb/html/search/typedefs_1.html | 26 + .../doc/openvdb/html/search/typedefs_1.js | 13 + .../doc/openvdb/html/search/typedefs_10.html | 26 + .../doc/openvdb/html/search/typedefs_10.js | 24 + .../doc/openvdb/html/search/typedefs_11.html | 26 + .../doc/openvdb/html/search/typedefs_11.js | 24 + .../doc/openvdb/html/search/typedefs_12.html | 26 + .../doc/openvdb/html/search/typedefs_12.js | 20 + .../doc/openvdb/html/search/typedefs_13.html | 26 + .../doc/openvdb/html/search/typedefs_13.js | 10 + .../doc/openvdb/html/search/typedefs_14.html | 26 + .../doc/openvdb/html/search/typedefs_14.js | 76 + .../doc/openvdb/html/search/typedefs_15.html | 26 + .../doc/openvdb/html/search/typedefs_15.js | 6 + .../doc/openvdb/html/search/typedefs_2.html | 26 + .../doc/openvdb/html/search/typedefs_2.js | 24 + .../doc/openvdb/html/search/typedefs_3.html | 26 + .../doc/openvdb/html/search/typedefs_3.js | 36 + .../doc/openvdb/html/search/typedefs_4.html | 26 + .../doc/openvdb/html/search/typedefs_4.js | 12 + .../doc/openvdb/html/search/typedefs_5.html | 26 + .../doc/openvdb/html/search/typedefs_5.js | 4 + .../doc/openvdb/html/search/typedefs_6.html | 26 + .../doc/openvdb/html/search/typedefs_6.js | 12 + .../doc/openvdb/html/search/typedefs_7.html | 26 + .../doc/openvdb/html/search/typedefs_7.js | 27 + .../doc/openvdb/html/search/typedefs_8.html | 26 + .../doc/openvdb/html/search/typedefs_8.js | 4 + .../doc/openvdb/html/search/typedefs_9.html | 26 + .../doc/openvdb/html/search/typedefs_9.js | 51 + .../doc/openvdb/html/search/typedefs_a.html | 26 + .../doc/openvdb/html/search/typedefs_a.js | 25 + .../doc/openvdb/html/search/typedefs_b.html | 26 + .../doc/openvdb/html/search/typedefs_b.js | 37 + .../doc/openvdb/html/search/typedefs_c.html | 26 + .../doc/openvdb/html/search/typedefs_c.js | 38 + .../doc/openvdb/html/search/typedefs_d.html | 26 + .../doc/openvdb/html/search/typedefs_d.js | 10 + .../doc/openvdb/html/search/typedefs_e.html | 26 + .../doc/openvdb/html/search/typedefs_e.js | 22 + .../doc/openvdb/html/search/typedefs_f.html | 26 + .../doc/openvdb/html/search/typedefs_f.js | 8 + .../doc/openvdb/html/search/variables_0.html | 26 + .../doc/openvdb/html/search/variables_0.js | 9 + .../doc/openvdb/html/search/variables_1.html | 26 + .../doc/openvdb/html/search/variables_1.js | 8 + .../doc/openvdb/html/search/variables_10.html | 26 + .../doc/openvdb/html/search/variables_10.js | 8 + .../doc/openvdb/html/search/variables_11.html | 26 + .../doc/openvdb/html/search/variables_11.js | 5 + .../doc/openvdb/html/search/variables_12.html | 26 + .../doc/openvdb/html/search/variables_12.js | 4 + .../doc/openvdb/html/search/variables_13.html | 26 + .../doc/openvdb/html/search/variables_13.js | 4 + .../doc/openvdb/html/search/variables_14.html | 26 + .../doc/openvdb/html/search/variables_14.js | 4 + .../doc/openvdb/html/search/variables_2.html | 26 + .../doc/openvdb/html/search/variables_2.js | 7 + .../doc/openvdb/html/search/variables_3.html | 26 + .../doc/openvdb/html/search/variables_3.js | 13 + .../doc/openvdb/html/search/variables_4.html | 26 + .../doc/openvdb/html/search/variables_4.js | 7 + .../doc/openvdb/html/search/variables_5.html | 26 + .../doc/openvdb/html/search/variables_5.js | 6 + .../doc/openvdb/html/search/variables_6.html | 26 + .../doc/openvdb/html/search/variables_6.js | 4 + .../doc/openvdb/html/search/variables_7.html | 26 + .../doc/openvdb/html/search/variables_7.js | 19 + .../doc/openvdb/html/search/variables_8.html | 26 + .../doc/openvdb/html/search/variables_8.js | 15 + .../doc/openvdb/html/search/variables_9.html | 26 + .../doc/openvdb/html/search/variables_9.js | 153 + .../doc/openvdb/html/search/variables_a.html | 26 + .../doc/openvdb/html/search/variables_a.js | 7 + .../doc/openvdb/html/search/variables_b.html | 26 + .../doc/openvdb/html/search/variables_b.js | 16 + .../doc/openvdb/html/search/variables_c.html | 26 + .../doc/openvdb/html/search/variables_c.js | 6 + .../doc/openvdb/html/search/variables_d.html | 26 + .../doc/openvdb/html/search/variables_d.js | 8 + .../doc/openvdb/html/search/variables_e.html | 26 + .../doc/openvdb/html/search/variables_e.js | 18 + .../doc/openvdb/html/search/variables_f.html | 26 + .../doc/openvdb/html/search/variables_f.js | 11 + .../share/doc/openvdb/html/splitbar.png | Bin 0 -> 308 bytes .../share/doc/openvdb/html/sync_off.png | Bin 0 -> 844 bytes .../share/doc/openvdb/html/sync_on.png | Bin 0 -> 834 bytes .../openvdb/share/doc/openvdb/html/tab_a.png | Bin 0 -> 136 bytes .../openvdb/share/doc/openvdb/html/tab_b.png | Bin 0 -> 164 bytes .../openvdb/share/doc/openvdb/html/tab_h.png | Bin 0 -> 164 bytes .../openvdb/share/doc/openvdb/html/tab_s.png | Bin 0 -> 167 bytes .../openvdb/share/doc/openvdb/html/tabs.css | 60 + src/camera/camera.h | 2 + src/fluidSolver/SPHGrid.cpp | 38 +- src/fluidSolver/SPHGrid.h | 4 + src/fluidSolver/SPHSolver.cpp | 122 +- src/fluidSolver/SPHSolver.h | 4 + src/fluidSolver/fluidSolver.cpp | 29 +- src/fluidSolver/fluidSolver.h | 3 + src/scene/scene.cpp | 10 + src/scene/scene.json | 14 +- src/shaderProgram/particleAdvectProgram.cpp | 49 +- src/shaderProgram/shaderProgram.cpp | 1 + src/utility.cpp | 3 + 663 files changed, 165994 insertions(+), 54 deletions(-) create mode 100755 nuparu/include/openvdb/bin/vdb_print create mode 100755 nuparu/include/openvdb/bin/vdb_render create mode 100755 nuparu/include/openvdb/bin/vdb_view create mode 100644 nuparu/include/openvdb/include/houdini_utils/OP_NodeChain.h create mode 100644 nuparu/include/openvdb/include/houdini_utils/ParmFactory.h create mode 100644 nuparu/include/openvdb/include/houdini_utils/geometry.h create mode 100644 nuparu/include/openvdb/include/openvdb/Exceptions.h create mode 100644 nuparu/include/openvdb/include/openvdb/Grid.h create mode 100644 nuparu/include/openvdb/include/openvdb/Metadata.h create mode 100644 nuparu/include/openvdb/include/openvdb/Platform.h create mode 100644 nuparu/include/openvdb/include/openvdb/PlatformConfig.h create mode 100644 nuparu/include/openvdb/include/openvdb/Types.h create mode 100644 nuparu/include/openvdb/include/openvdb/io/Archive.h create mode 100644 nuparu/include/openvdb/include/openvdb/io/Compression.h create mode 100644 nuparu/include/openvdb/include/openvdb/io/File.h create mode 100644 nuparu/include/openvdb/include/openvdb/io/GridDescriptor.h create mode 100644 nuparu/include/openvdb/include/openvdb/io/Queue.h create mode 100644 nuparu/include/openvdb/include/openvdb/io/Stream.h create mode 100644 nuparu/include/openvdb/include/openvdb/io/TempFile.h create mode 100644 nuparu/include/openvdb/include/openvdb/io/io.h create mode 100644 nuparu/include/openvdb/include/openvdb/math/BBox.h create mode 100644 nuparu/include/openvdb/include/openvdb/math/ConjGradient.h create mode 100644 nuparu/include/openvdb/include/openvdb/math/Coord.h create mode 100644 nuparu/include/openvdb/include/openvdb/math/DDA.h create mode 100644 nuparu/include/openvdb/include/openvdb/math/FiniteDifference.h create mode 100644 nuparu/include/openvdb/include/openvdb/math/Hermite.h create mode 100644 nuparu/include/openvdb/include/openvdb/math/LegacyFrustum.h create mode 100644 nuparu/include/openvdb/include/openvdb/math/Maps.h create mode 100644 nuparu/include/openvdb/include/openvdb/math/Mat.h create mode 100644 nuparu/include/openvdb/include/openvdb/math/Mat3.h create mode 100644 nuparu/include/openvdb/include/openvdb/math/Mat4.h create mode 100644 nuparu/include/openvdb/include/openvdb/math/Math.h create mode 100644 nuparu/include/openvdb/include/openvdb/math/Operators.h create mode 100644 nuparu/include/openvdb/include/openvdb/math/Proximity.h create mode 100644 nuparu/include/openvdb/include/openvdb/math/QuantizedUnitVec.h create mode 100644 nuparu/include/openvdb/include/openvdb/math/Quat.h create mode 100644 nuparu/include/openvdb/include/openvdb/math/Ray.h create mode 100644 nuparu/include/openvdb/include/openvdb/math/Stats.h create mode 100644 nuparu/include/openvdb/include/openvdb/math/Stencils.h create mode 100644 nuparu/include/openvdb/include/openvdb/math/Transform.h create mode 100644 nuparu/include/openvdb/include/openvdb/math/Tuple.h create mode 100644 nuparu/include/openvdb/include/openvdb/math/Vec2.h create mode 100644 nuparu/include/openvdb/include/openvdb/math/Vec3.h create mode 100644 nuparu/include/openvdb/include/openvdb/math/Vec4.h create mode 100644 nuparu/include/openvdb/include/openvdb/metadata/MetaMap.h create mode 100644 nuparu/include/openvdb/include/openvdb/metadata/Metadata.h create mode 100644 nuparu/include/openvdb/include/openvdb/metadata/StringMetadata.h create mode 100644 nuparu/include/openvdb/include/openvdb/openvdb.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/ChangeBackground.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/Clip.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/Composite.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/Dense.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/DenseSparseTools.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/Diagnostics.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/Filter.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/GridOperators.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/GridTransformer.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/Interpolation.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/LevelSetAdvect.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/LevelSetFilter.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/LevelSetFracture.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/LevelSetMeasure.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/LevelSetMorph.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/LevelSetPlatonic.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/LevelSetRebuild.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/LevelSetSphere.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/LevelSetTracker.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/LevelSetUtil.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/MaskToLevelSet.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/MeshToVolume.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/Morphology.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/MultiResGrid.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/ParticleAtlas.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/ParticlesToLevelSet.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/PointAdvect.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/PointIndexGrid.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/PointMaskGrid.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/PointPartitioner.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/PointScatter.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/PoissonSolver.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/Prune.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/RayIntersector.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/RayTracer.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/SignedFloodFill.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/Statistics.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/TopologyToLevelSet.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/ValueTransformer.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/VectorTransformer.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/VelocityFields.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/VolumeAdvect.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/VolumeToMesh.h create mode 100644 nuparu/include/openvdb/include/openvdb/tools/VolumeToSpheres.h create mode 100644 nuparu/include/openvdb/include/openvdb/tree/InternalNode.h create mode 100644 nuparu/include/openvdb/include/openvdb/tree/Iterator.h create mode 100644 nuparu/include/openvdb/include/openvdb/tree/LeafManager.h create mode 100644 nuparu/include/openvdb/include/openvdb/tree/LeafNode.h create mode 100644 nuparu/include/openvdb/include/openvdb/tree/LeafNodeBool.h create mode 100644 nuparu/include/openvdb/include/openvdb/tree/LeafNodeMask.h create mode 100644 nuparu/include/openvdb/include/openvdb/tree/NodeManager.h create mode 100644 nuparu/include/openvdb/include/openvdb/tree/NodeUnion.h create mode 100644 nuparu/include/openvdb/include/openvdb/tree/RootNode.h create mode 100644 nuparu/include/openvdb/include/openvdb/tree/Tree.h create mode 100644 nuparu/include/openvdb/include/openvdb/tree/TreeIterator.h create mode 100644 nuparu/include/openvdb/include/openvdb/tree/ValueAccessor.h create mode 100644 nuparu/include/openvdb/include/openvdb/util/CpuTimer.h create mode 100644 nuparu/include/openvdb/include/openvdb/util/Formats.h create mode 100644 nuparu/include/openvdb/include/openvdb/util/MapsUtil.h create mode 100644 nuparu/include/openvdb/include/openvdb/util/Name.h create mode 100644 nuparu/include/openvdb/include/openvdb/util/NodeMasks.h create mode 100644 nuparu/include/openvdb/include/openvdb/util/NullInterrupter.h create mode 100644 nuparu/include/openvdb/include/openvdb/util/PagedArray.h create mode 100644 nuparu/include/openvdb/include/openvdb/util/Util.h create mode 100644 nuparu/include/openvdb/include/openvdb/util/logging.h create mode 100644 nuparu/include/openvdb/include/openvdb/version.h create mode 100644 nuparu/include/openvdb/include/openvdb_houdini/AttributeTransferUtil.h create mode 100644 nuparu/include/openvdb/include/openvdb_houdini/GEO_PrimVDB.h create mode 100644 nuparu/include/openvdb/include/openvdb_houdini/GT_GEOPrimCollectVDB.h create mode 100644 nuparu/include/openvdb/include/openvdb_houdini/GU_PrimVDB.h create mode 100644 nuparu/include/openvdb/include/openvdb_houdini/GU_VDBPointTools.h create mode 100644 nuparu/include/openvdb/include/openvdb_houdini/GeometryUtil.h create mode 100644 nuparu/include/openvdb/include/openvdb_houdini/SOP_NodeVDB.h create mode 100644 nuparu/include/openvdb/include/openvdb_houdini/SOP_NodeVDBPoints.h create mode 100644 nuparu/include/openvdb/include/openvdb_houdini/UT_VDBTools.h create mode 100644 nuparu/include/openvdb/include/openvdb_houdini/UT_VDBUtils.h create mode 100644 nuparu/include/openvdb/include/openvdb_houdini/Utils.h create mode 100644 nuparu/include/openvdb/include/openvdb_points/Types.h create mode 100644 nuparu/include/openvdb/include/openvdb_points/openvdb.h create mode 100644 nuparu/include/openvdb/include/openvdb_points/tools/AttributeArray.h create mode 100644 nuparu/include/openvdb/include/openvdb_points/tools/AttributeGroup.h create mode 100644 nuparu/include/openvdb/include/openvdb_points/tools/AttributeSet.h create mode 100644 nuparu/include/openvdb/include/openvdb_points/tools/IndexFilter.h create mode 100644 nuparu/include/openvdb/include/openvdb_points/tools/IndexIterator.h create mode 100644 nuparu/include/openvdb/include/openvdb_points/tools/PointAttribute.h create mode 100644 nuparu/include/openvdb/include/openvdb_points/tools/PointConversion.h create mode 100644 nuparu/include/openvdb/include/openvdb_points/tools/PointCount.h create mode 100644 nuparu/include/openvdb/include/openvdb_points/tools/PointDataGrid.h create mode 100644 nuparu/include/openvdb/include/openvdb_points/tools/PointGroup.h create mode 100644 nuparu/include/openvdb/include/openvdb_points/tools/PointLoad.h create mode 100644 nuparu/include/openvdb/include/openvdb_points/version.h create mode 100755 nuparu/include/openvdb/lib/libHalf.12.0.0.dylib create mode 100755 nuparu/include/openvdb/lib/libboost_iostreams-clang-darwin61-mt-1_55.dylib create mode 100755 nuparu/include/openvdb/lib/libopenvdb.3.0.0.dylib create mode 100755 nuparu/include/openvdb/lib/libopenvdb.3.0.dylib create mode 100755 nuparu/include/openvdb/lib/libopenvdb.3.2.0.dylib create mode 100755 nuparu/include/openvdb/lib/libopenvdb.3.2.dylib create mode 100644 nuparu/include/openvdb/lib/libopenvdb.a create mode 100755 nuparu/include/openvdb/lib/libopenvdb.dylib create mode 100755 nuparu/include/openvdb/lib/libopenvdb_houdini.so create mode 100755 nuparu/include/openvdb/lib/libopenvdb_houdini.so.3.2.0 create mode 100755 nuparu/include/openvdb/lib/libopenvdb_points.0.2.0.dylib create mode 100755 nuparu/include/openvdb/lib/libopenvdb_points.0.2.dylib create mode 100644 nuparu/include/openvdb/lib/libopenvdb_points.a create mode 100755 nuparu/include/openvdb/lib/libopenvdb_points.dylib create mode 100755 nuparu/include/openvdb/lib/libz.1.2.7.dylib create mode 100644 nuparu/include/openvdb/python/include/python2.7/pyopenvdb.h create mode 100755 nuparu/include/openvdb/python/lib/python2.7/pyopenvdb.so create mode 100755 nuparu/include/openvdb/python/lib/python2.7/pyopenvdb.so.3.0 create mode 100755 nuparu/include/openvdb/python/lib/python2.7/pyopenvdb.so.3.2 create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/Archive_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/BBox_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/ChangeBackground_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/Clip_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/Composite_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/Compression_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/ConjGradient_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/Coord_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/CpuTimer_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/DDA_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/DenseSparseTools_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/Dense_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/Diagnostics_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/Exceptions_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/File_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/Filter_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/FiniteDifference_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/Formats_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/FreeSans.ttf create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/GridDescriptor_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/GridOperators_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/GridTransformer_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/Grid_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/InternalNode_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/Interpolation_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/Iterator_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/LeafManager_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/LeafNodeBool_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/LeafNodeMask_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/LeafNode_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/LegacyFrustum_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/LevelSetAdvect_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/LevelSetFilter_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/LevelSetFracture_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/LevelSetMeasure_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/LevelSetMorph_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/LevelSetPlatonic_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/LevelSetRebuild_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/LevelSetSphere_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/LevelSetTracker_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/LevelSetUtil_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/MapsUtil_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/Maps_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/MaskToLevelSet_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/Mat3_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/Mat4_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/Mat_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/Math_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/MeshToVolume_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/arrowdown.png create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/arrowright.png create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/bc_s.png create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/bdwn.png create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/closed.png create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/doc.png create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/doxygen.css create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/doxygen.png create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/dynsections.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/folderclosed.png create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/folderopen.png create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/io_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/jquery.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/logging_8h_source.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/nav_f.png create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/nav_g.png create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/nav_h.png create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/open.png create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_0.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_0.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_1.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_1.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_10.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_10.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_11.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_11.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_12.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_12.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_13.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_13.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_14.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_14.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_15.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_15.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_16.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_16.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_17.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_17.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_18.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_18.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_19.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_19.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_1a.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_1a.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_1b.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_1b.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_1c.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_1c.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_2.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_2.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_3.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_3.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_4.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_4.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_5.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_5.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_6.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_6.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_7.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_7.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_8.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_8.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_9.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_9.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_a.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_a.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_b.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_b.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_c.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_c.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_d.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_d.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_e.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_e.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_f.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/all_f.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_0.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_0.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_1.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_1.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_10.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_10.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_11.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_11.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_12.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_12.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_13.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_13.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_14.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_14.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_15.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_15.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_16.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_16.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_2.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_2.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_3.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_3.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_4.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_4.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_5.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_5.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_6.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_6.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_7.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_7.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_8.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_8.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_9.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_9.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_a.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_a.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_b.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_b.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_c.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_c.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_d.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_d.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_e.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_e.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_f.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/classes_f.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/close.png create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/defines_0.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/defines_0.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/defines_1.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/defines_1.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/defines_2.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/defines_2.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/defines_3.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/defines_3.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/defines_4.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/defines_4.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/defines_5.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/defines_5.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/defines_6.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/defines_6.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enums_0.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enums_0.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enums_1.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enums_1.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enums_2.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enums_2.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enums_3.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enums_3.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enums_4.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enums_4.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enums_5.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enums_5.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enums_6.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enums_6.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enums_7.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enums_7.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enums_8.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enums_8.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enums_9.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enums_9.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enums_a.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enums_a.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enums_b.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enums_b.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_0.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_0.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_1.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_1.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_10.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_10.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_11.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_11.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_12.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_12.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_13.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_13.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_14.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_14.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_15.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_15.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_2.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_2.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_3.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_3.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_4.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_4.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_5.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_5.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_6.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_6.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_7.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_7.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_8.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_8.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_9.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_9.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_a.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_a.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_b.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_b.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_c.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_c.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_d.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_d.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_e.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_e.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_f.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/enumvalues_f.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_0.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_0.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_1.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_1.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_10.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_10.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_11.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_11.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_12.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_12.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_2.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_2.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_3.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_3.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_4.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_4.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_5.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_5.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_6.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_6.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_7.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_7.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_8.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_8.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_9.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_9.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_a.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_a.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_b.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_b.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_c.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_c.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_d.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_d.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_e.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_e.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_f.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/files_f.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_0.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_0.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_1.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_1.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_10.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_10.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_11.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_11.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_12.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_12.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_13.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_13.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_14.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_14.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_15.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_15.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_16.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_16.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_17.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_17.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_18.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_18.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_19.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_19.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_1a.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_1a.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_2.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_2.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_3.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_3.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_4.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_4.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_5.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_5.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_6.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_6.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_7.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_7.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_8.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_8.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_9.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_9.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_a.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_a.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_b.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_b.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_c.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_c.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_d.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_d.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_e.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_e.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_f.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/functions_f.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/mag_sel.png create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/namespaces_0.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/namespaces_0.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/namespaces_1.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/namespaces_1.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/namespaces_2.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/namespaces_2.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/nomatches.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/pages_0.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/pages_0.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/pages_1.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/pages_1.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/pages_2.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/pages_2.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/pages_3.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/pages_3.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/pages_4.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/pages_4.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/pages_5.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/pages_5.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/pages_6.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/pages_6.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/related_0.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/related_0.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/related_1.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/related_1.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/related_2.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/related_2.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/related_3.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/related_3.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/related_4.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/related_4.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/related_5.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/related_5.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/related_6.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/related_6.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/related_7.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/related_7.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/related_8.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/related_8.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/related_9.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/related_9.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/related_a.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/related_a.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/related_b.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/related_b.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/search.css create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/search.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/search_l.png create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/search_m.png create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/search_r.png create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/searchdata.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_0.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_0.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_1.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_1.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_10.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_10.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_11.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_11.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_12.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_12.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_13.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_13.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_14.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_14.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_15.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_15.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_2.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_2.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_3.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_3.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_4.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_4.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_5.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_5.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_6.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_6.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_7.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_7.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_8.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_8.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_9.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_9.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_a.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_a.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_b.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_b.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_c.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_c.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_d.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_d.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_e.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_e.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_f.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/typedefs_f.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_0.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_0.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_1.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_1.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_10.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_10.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_11.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_11.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_12.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_12.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_13.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_13.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_14.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_14.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_2.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_2.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_3.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_3.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_4.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_4.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_5.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_5.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_6.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_6.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_7.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_7.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_8.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_8.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_9.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_9.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_a.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_a.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_b.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_b.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_c.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_c.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_d.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_d.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_e.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_e.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_f.html create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/search/variables_f.js create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/splitbar.png create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/sync_off.png create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/sync_on.png create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/tab_a.png create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/tab_b.png create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/tab_h.png create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/tab_s.png create mode 100644 nuparu/include/openvdb/share/doc/openvdb/html/tabs.css diff --git a/CMakeLists.txt b/CMakeLists.txt index ee9d00c7..fbd48c6d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,14 +44,14 @@ set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} ${THIRDPARTY}/nanogui) # add include and src directories to path for openvdb_points include_directories( - ${THIRDPARTY}/ - ${THIRDPARTY}/openvdb_points_dev/ - ${THIRDPARTY}/openvdb_points_dev/openvdb_points/ - ${THIRDPARTY}/openvdb_points_dev/openvdb_points/tools/ + /usr/local/include/openvdb/ + /usr/local/openvdb/ + /Library/Frameworks/Houdini.framework/Versions/15.0.347/Resources/toolkit/include/ ) # OpenVDB point library path -set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} ${THIRDPARTY}/openvdb_points_dev/lib) +set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} /usr/local/openvdb/lib) +set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} /Library/Frameworks/Houdini.framework/Versions/15.0.347/Libraries/) #set include variables set(GLFW_INCLUDE_DIR ${NUPARU}/include ) @@ -65,11 +65,10 @@ find_package(GLEW) find_library(GLFW_LIBRARY "glfw3" HINTS ${GLFW_LIBRARY_DIR}) find_library(JSONCPP "jsoncpp") find_library(TBB "tbb") -# link half for openvdb +find_library(NANOGUI "nanogui") find_library(HALF "half") find_library(OPENVDB "openvdb") -find_library(NANOGUI "nanogui") -find_library(OPENVDB_POINT "openvdb_points") +find_library(OPENVDB_POINTS "openvdb_points") add_definitions( -DTW_STATIC @@ -79,7 +78,7 @@ add_definitions( -D_CRT_SECURE_NO_WARNINGS ) -set(CORE_LIBS ${GLFW_LIBRARY} ${GLUT_LIBRARY} ${GLEW_LIBRARY} ${JSONCPP} ${TBB} ${HALF} ${OPENVDB} ${OPENVDB_POINT} ${NANOGUI} ${ZILB} ${OPENGL_LIBRARY} ) +set(CORE_LIBS ${GLFW_LIBRARY} ${GLUT_LIBRARY} ${GLEW_LIBRARY} ${JSONCPP} ${TBB} ${HALF} ${OPENVDB} ${OPENVDB_POINTS} ${NANOGUI} ${OPENGL_LIBRARY} ) # OSX-specific hacks/fixes if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") @@ -118,6 +117,7 @@ endif() # Add source files you want to compile (.cpp) set(CORE_SRC + nuparu/src/stb_image/stb_image_write.c src/loadShaders.cpp src/utility.cpp src/main.cpp diff --git a/nuparu/include/openvdb/bin/vdb_print b/nuparu/include/openvdb/bin/vdb_print new file mode 100755 index 0000000000000000000000000000000000000000..e4063275fb65299235ee319bb350f0e184dfec1d GIT binary patch literal 4751928 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�D{$%eF?+&D^>u;aZ-@6Sgmkoalcs=NJU`1)9 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 z∈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 z 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

XDSt;LoW^W zlf{{2o}X+}3JH)3sF>?GiOp$lw0KJ3Bfpo~7xroIxso{l)3yD%ES2Uc{W@ER@H3KD_U!A}Vwdb1rk1HRnktkibA{un+yE9{!j9BvimgnV zG%k6XSZd3B%%DgTn-=c$AWS4f2BAbZ?W|5!{z)GDgAkP_Tp@K|NZoS%mi~skzgjBp zA(_Yfna=rJV!8eNe!9%=-qHtYgn+yE5f5aCz1@+*@7c0)nj`M zT^)I?Wlz7^az{?3_*DP3iE?e5RVNp7x^REs8bY^6O=CYn+5urT;z{p z^HoA>TPUd#mD%iP@_cbBY5!(5Nz0zIQC((jkxSbwkBj`Z&Dz1Qa9P@Bi6*-Iwawbg zujEZJtD#7mqq(DcFb5TOys4U>sU)LvzG)Y%ia7KT=bOL|Iotd-V#D*YM9%z9c1yS;AeDFe64x~}I;V#juDH66E(Ckc^Vl9ul#9@puGlKwM4 zVdtJ^E+Pb4Yt;Xqq~#n>rgVc$Y^>{DlG*!-&+y6e$NZ`9gjW0OWo_l`KbCV1=S@zg zn>lYHD^n>B3oW*rD)u)41BQAJpqPJ=W(Ve)vcRG81(imNH@bNfCl<*zGbT35q1 zZ8m2>;j@lo!Nku20Hsnwo*i>p*oQLM0`IU_Qip=G zDe`%zLEtR;)3D<cZI9_$d zmFBCFOmr8rB(+x50ICJE3wp_N&PgvA-5+3lgHgXhJ-ZWh(ovl&|HZ%9x3l}Rr&E83 zbQ<_JY(eQydS3e_FH6s7N%Au6nSCyPXl!*v&EacNnL~vy{I2_m;m^g{UH4%vuKN7- zW~)fqTj}|_B*kDlc)oTqB%0r(=W9c!{(YDJonzS>q<;kLnS4y*H<-Jn_D9v1UYWiq zBxkA~sg-2R`Xfze6}`(#VE0j}g<)ditvtYxC`&x7`WG6UkVzEK7w`keNxtPE1*siN z4$k}Pe@jj_esHd2&R);LZ}7j57Yq0lep}b6KjY}n@i==G{3cZ%0>23k1_*zQO9fk>QM~(xj6nns-1>6g`n>zGpGF;?`7^E{>{ zdH;vKcY%+px)%R4kieke2?`n&G-%YIR)ZQ%1T{k@cm^gI1r#5s^+9cY6k$eCK!Qm$ zhvQh(z$#1Z^jVf+GpdZ5u}X%h}VKADvn$+NbJvI_+u5eN@yBh$nn>|i zyC**pPTE*J>{PigcU8z5H!WZ-lU9VRhDILl00!)8KX*voKnFcyFH&Qd*t@r)X{JI? zxZe?*#Z)jsU@GPi=&sk^I{oeNRf_98-|%=UOYNOfZLH4;f*@QJe0lF*o4)PB6jl2Z zH>w6#7bd6hS(dz3kq2RcBA2-g1vi&^{Dhtp-yc80d}iA`R9(riWg3FDB-8pvE}!#V;OD|jV`iTh1h=`{;_}|VGJT&am=!9XR?#B?nNkBt zMm5}&l7Im3$_()OWbx06=lg!)_xAQQ{NCi1aXwnw>A6z^ozM zc_WCG#ZES5G#l0~c_Q@QYqlDuP8d6Mip`^x5Mr`@8?q=chv_3Eki2{Pdlw|AO<=2Y>3dioJ#?M=vIhI*TfPV$(q zb)LQCzxjFgGuOzd_xpP1*$Te0WBZQq{a>sf5WV^vz*F=C{(rA8^%wTo85Pa{(fU%; zek_Ck9rUG6Q3ZTEeJSLBYG#fsP>1TWrPKR8qyAwc!ZFJ%l$kqQJ&+@#ZZ^AyAa}>t zB*~7^*5E#N1FYy?Db4_r(AuPdvAo6`+gedMX~jmgUQIqymrRnGSQ@%d+*48~ReUcO!*R`R7A zvw{GDz~$sf-Y>}?(8<5D3o$p>52Q}JbYz#Nv=Z$ihVv6?iU?q6f>g9aTA)rfgKoR* z(!ILmZ`YILuJYCT`9Y{m(8Q&RZ zho;j!>ZZ}?>^wq*XfbT&>3qSzrwRoP*7GK2C$Z9n4-1B1JHtWklnQ}DUi~Ffxbk*K zRin@4%kKZ+IXpzH`ZSc9JUgAo)o>bAyHss_xi16^ zz(k`2wR$;^YqKk-*`sD~_7+nSKll}oa|jO_hj=kDQ!DW;Y6{ezRYTRLV!yL~m04a%+7x(M%Oyg{{eQucBqifm!kj z)WOwBf>~b4iO_9i5&Mm?r-*o*(ewtS=g z;s5d5%-5mA|JU%_{)@jOza5{TxTmjcpYVVC-~UiQK6;Tr^lQo!^nAn#0qg2>YSC`W z4_Ua7y`)TzK2Kf(4NbKh&6WPTZ9dsrU`tz(&!UrROGovVsNOn?<&r20yX``~NbACi zzguA^WEVCyALT!>)5_SeB=(aTg*m33XJjY6MAdsmx}~ZU^yF63Rk)-j#c@U&PyN-Nz+ZV~fM(_y`W^;o{;P zo3j7e(aos;oq9nOFMed7s^FiqL=*;Tp4@@^fwSgOzc5DoxeYMI4Y0V;v?>cj++f6U z-zXrlGSaI(rQL6;C~~Y7=1H?sl7oF$B|bdDMP=jw0kU$aC|KJzQQmx{2!*C%+>^VO z{K*pCs>3?9>ewS`Bbs}X;})Hxi5yL|A76BRd#D3>PU0ID5TFZq8zTkHsY$oKSgg7= zOmVI50EkCtHdv}#A$D1v8mXe_RyVg>3i;?3Idr$k(WP4wMI#rA()^o;(_k!?Y2-p_ zWXL)lbN(eE3#ZiV(`>wra2hN|c_p$@^L!x7;NFDEpeR!+i(rFZNZ^%XM`*POf4g&{ z4mw8%eTV5F1^1>D;bMQ1_-uFgjVgR%l5S#NJBt_j+0`Af^*q_tJ&oh*qp(RV#1yvB zxf8|#XuUi(Jyi)VQP)pZ6v|l3OctY3Kx9|F&J*A@MJn4<6#!(y>))Xlz%V}>hSDti zdzr31=%}Ey8vD$2{q!^e*?Q*`$D6KiW+O=^VJis*#tX0-gL};4kA;8iYOggRahOc& zjK6AomRLPTz?Pm9QkVL>rWZHVo_>pz*Y#V`W2iwfq#$0kL9j4h^$-0J;gUN~9=k$% z!~Q7xx^!y)!}%1?8qBnl2g#tKJ81P_j2JwS7fOCgd#chOcuL55XL5Lm&^(BCO@4dO zXrA6XP?z#WdwGF~3IyMA?BQX%)OQ@!3ykV7rSIwU&QAL&JfMp}1I)`i6U-AYJ9XQ* zoy}w$w2Nqiz1m~PNf%rhhu+59d54*d~`sG zGNf73lsp9%En)bRN-}f4OrXQT;RR;-wn%>vV5d14>-yDZ-P=1&9|FSa1Y01}Z-Z3-}ypt9?ae22(-veQ@n|PT{A}HkKQf=Z$MuSx<8P?JfmDeVb7Pq|e zt`-B?rN8Y`(1GbR54mYvDahZ5kX7P01k3pULPbBFIEgChPC{;(;F+wFe}koif>Dz$ zp-L3u`O-3`O9=4#@`b$Hr3GD@@GG4}^`%UjGePP*%&qqzp310(LS@VrOAShfl%rcj zi7g{KLnoL{^N)9=cR-L;rX|kC+u~IYa03clg@8$Heq6p}ZQv&BN`JB+ z)YB1!E-DjHiGQe3`SGfc(G$vX)2r9)^dC$5%}7Kt>D#+!(yQ0(^zD-V=}zh8vRtqH zPU(Bo9t;H6JHM3lVhX7x$h**2Ij@qXJ!Bmk62k%h^pw6AyR~?QUuZ?VqPINo@wLxL z8LwuhFfbX#fl2mPYE;$u3pN9;TivDie|yjL9}0YUK&hJ#Y_u( zw&D9>&#;T*@bLL)Gw43zYmMgoKR1GVGI7E|_=)1{!VcH`~ zIK<>p#+f2StSOSKwlCJU|L}aS81k&Z_N8`UWz>r`28=ztGK9oAME10c_p4}pwW9U| zUo?;F0g_ncj^SdBvWmv-wQsAijS@DEl?pd{9n5*|d?9ISg#=%i&y_WNG|5LrzC>|L z%bG};+D{mZDd|M+8*XSJA6Z1HJhdwNAy+n&NIZiHgsvYqAP2ud4fhCXw0pYu14j00 z52ViYtAF?cWcxdztlFB%2JTx+b>#N*A1s9>ad!{_qxnoFQ7NEW*caJC5Ozs#{Nfn( zudpzQ!66{aqHBuvZ>SJ9G2T$agGLL_vH*QLE}E<00dYQh7z9i6{bdrOO-EH4c-xUO zb>j^sZti$P8E^8(;pL?v7KPXd5TRiS>V7<=0N0LG7x1C87R;lTV@WG31c?^vw5s}e z!#uL6cEua!W?maJueI)LykS0Xdpz&_&>5$8HSlUrGf09(N{o;%mn)TC_FJ_RXjfG) zcm8lZjf(aEi4OHsE{C2H>C7bt-A$S-yYgkP6UNzSs!hYPP*MU7w@Jq0J0)*pTM_c2 zg9Kk!f0Q^-p4N&awh2hIO1qyX9arX}0v%^IjdRDqIULv|o>40Mac8f!LLAv<(t~a! zO5Db(rFU<&-p-4RauUqJbS)3SdhMy{!J8%_dhk>lPIO~ZSLV9SxVnQ=29I6Yy)dip z@wCplqJoW-&biS!oScmC4J2}pr&Wde1}%5E+NjWdZOyzkxvz0`+hJz7=6ix3IIsC~ zl=;VF1rY2;`;V2E(m&s&hDaLoct6T@Y?WZyr3A?F^iOp9T1HBOocC~=06f(D8s6Fe zsg8J^P>0wgkOnyqsRSFHv!yP(^tz$xL6JPp$GR-(y>kai5|d#j*l;8!@Kl4cOwiEz zi7MtpXD>-}D5+&tJNq@Y2T5Knd<1@%>L;>Iw2PC@xzdy$XyliQK9Ck%D(xzvBCh|;2ZF}p zyJbpkS)s^vG6_KL-ar6{p1l~@UL=vQMFqT=vEKVpxXd}{*JlsN+0r0ceK^&NEn3T4 z@)$kC?kVpKpF^eMA4|oLz9JPba!BkJ#GPx-1j=IOO1WE>`=aA1Zje;RigJM-YgOgV zBujkE9F&r-r6j!pRrq$9#H~={&xBQT(m9uE6SeM8oTh5NvslWt7YV%}nVSELgeP%( zbuh*7F5Pkr{Vw#x>8{@uLE4IGBWXmRT4W7Z=+Zrdn|)$_G)rfq7z z{!9v28~dZ&G%m&zky#`WSMYQM0$LyAEZ$U8N(+A>Byn7=aB}Y+B14q7SuH1?KE{(L z0YB1C*&leccg1dM+^ZBQ{Qm=x6YU%LbNLTJucuTtex;GzGNcmoG z7UNZYqMwd)9W{JnyNll~i zVtexnVJuG!JSj{0R|%M>AYjJ6FC$=9l8&tHOK1LG;G_L}hV5A}v6uL{mbrjEd_LZH z!{zTrZbrac$h+7xDvyD^aEy0^f^Y5gAP#c*+@D>0*^o7I#4~MxnBvz9j{w1KQZgit zx%w%4R_<=K#obKWzH@D-{fTLxk06OzvzGf+ea}#P^!DoVPmEtSX@N?5^X}TS>HWv7 zk_m}V4)FGaT7G3;QevF9a+5K>b(3*yvsL9-RUcbb?Sdtbk`&1_t~|7G>Yjt-TM2`d zeEr-p;?IZzB=H1b>;6#fw@ewZS@J^~f`?l5CYoNg z@r!qbsJavEOlXTbqc<0#~ zM0WSEy<`Ciox+py7;jKn0L3lqdc6sWMtsAGjtP72OTq5t?+O3w&D1^0na8CS~V*llHmy7mQcV zf4<=QxJFDjWpq<>U#BqUVFCKNx5(laD}CS&4mJdPWNjKX2>NNo71ue*3Hv(2u`64J z(jRGp=60#?ICcPh_En!qWJ6_7;$r}U7*DMZG>Y82)hP>MNM}5`S=OlqB$VeLRfeiY zxsW;>o0MN&zSXD~85_qXYkn?te!_ys;Si)1H0sMagL0fF3#FXJ=M@UewQndkwa`qp z2+M^VC-JYr?W9~yIhC1)M4q&~vwhwYxmZp}k&)IE8;h$JaN%YTZs|(q%|)!UPy`Go zXH|5eMMdMtY%CtPKG6E|P-E{;w)c(-<1Lld0 zz^r5S2>eWa?sf#)oW(TL8-XKvmk}u92?Q8{NrhDUTRM)j|G6$KQqE-*$co6eDQxX< z7UJ-k!JO;-g^;esdlrLym+!?sa>Db*W;dS%qM^x&h0z}+D`@~OWYaI zszkuM!q265(_CR-yb944-;&5E8H-rh4HLkXVm`` zg48MbJb02+n@+Y6vM|r-85MPn=#pv(O&-!aI%;3JP_8SlwxUeO2}{0%1dpc8bINIq zIyn_ZE;w?=Rmr2yV#dP0UIo!m2hq+#1`M)QE3%}tbaPyzv3QZB;R0k=!ug5X9#0Zt zQ~p_WFZRuQQSR)df5^Hd%2qSyBIJ~v^p8oQl1}3Ve>q}$fgJM$Z~ZkE_b?V4<;~GU z%v4-RF|y7is?em_$FVDvl=GJ>#1z?WrgI%FlrA_|^1_AI`*OE>vE2TiA9AmU2TdI4 zz_iYEK!7_UEe;u>gqSC*>)=HcvNcN-GD0c0;Rp$7eFb6=;*z~J?Pzi~D#Tgu%sfL7 zNW^5d&N{-9ztdmyoag21=Ym{MJ+s^Mg*1PwW%S|(7m>KL)|VA3se?qPK_3uWXG9556Bqb$;Q& zS=Y8W?{em#oKSc5_v)VSZpr|GK5XD57nGT$_`_QIR!iW=n!l{oOy!1DO^IFoajMxK z*PdE#M>m5$_RI~a4K!~o9K7QgnCa$s3e8@e4ew`7krPnwcy%}Q(w*<@$^ILRM~mUD zAK&YE>@VY?G)CMZsbMd%%aC*$U*V6x!}wHuALE0kp1N5DTs6a=+BpQ&o*^>8w9kW5 zjx01)6xfpDZjYcnoC_aTnZ2;f+9znqjTmIsJC1Wr99Ijww!cSzO!4og?Zh@G55aY~ zy3Q?wADG;-|0~CdNM!Sp6jC+&#zJ4*KD)@bynCQnDZFXq;_4U|q02IiBUN!)D&Wtq|85DCRc>X^0XgC* zHM+N1$E8pM6u-f?v1Z7Ysv%9Va|ji~)~C*od#6jsyXEYsr4i{>Q^r&L?O;5R=2yJ^ zQ%A_Y7>1w)l9CFSZ;FiNshE7ufc(w~XaqM|C?yDipyro_=&{bG`BOHCUD`h$_#x7I zJ-V4PqYrWyM`j)v*E#}L$&9o{QtD}>=qvTQh(^X!=ux_ zbPw>@ZT<705&yaMN3!tY04GP+pFF73cq{o!Pk2C3tir>UPWGVR3}1e>JNR%OkH`Up zNoz~KM|DUY9C^sP9gDI!nC9hG@0piZZ%~)~Tt30!xt3U2(dRaj(j1JCPPk~)QZLTP z8}>9G&dch5PiJ!8~4aB7X-8v1AU3NbA{+dpL<-$jS#S=G6>kr=X{aLrhVEM z1L5vtrN`dLp5|y0I7QGGG|4oVD*M6@!m(@FrEYXOPN5*XbS*jLGoe0r*` zT2_w$qxz8P2)K%nsf}*uIoXa*GI$Zlbp6zr*5z6&)Isp7=F!S)PbMwY+c)E3cZ~EH&D_ zx2R0?&XHwto?3K|HDk&rKlbak^8a1O^+w#TxSTm7WB*3#U@vX-O_oq8Eu&xJj+Y*+ zPUCbLKL2-gYJb=9Y7Xi-jdP`#>OV( z_{wWGr}Blc7cz2yO8jU$MLt>>D?Mcz6SIJDnR1N+mssgA3G+)@=0!xoiZct@3A2OZ zu-I6fFXAr0=&T@8V~XM`Kz5v3X=S)B<&>K#Ma>vWIZlTqrxMoJt?eMfxW39~{OmlE zQSW+*?d8VqguMxH9w2SlaFS}*7dK>;_z&X4_I(AyH63%GbSc!lvs;Mku#8)tRAnry z;UJOoNI6>!rJh?VZISrNW5Ti0nbd{R?=3t8pB~32t$;u_Ft<>$X~(3v2EANk zLo>$EjK|>(1ujBe&X1>v;Ll7|j+W(<0Wli-aEQ-ld@$b(OrkfR z@dnvrQZjG3={FI?>#%K{Xz91j>9=LBOo(kb(p zyVVGw8Hq}}hk2s00p@LRi;D!s;Go$#4Q*6?nSirt2;K6nlexJ3MCoCJenea5&a5UdPEp;;6SKo$S(dve+b@Odf8stJ2A)$s5y% zyp5UoTJP10&Eu6e&X-3_YdTpC3*;-VoUrRgEg?X#B;N1@Z#t~W4Qpw5TEft(Z{-<^ zoyi{mJbx%AyN^|o)9{z-^4rQk68HJt3^i5v`Rz#i$2EliurvNu$tiwTp9@;|1r^3? zbi<(|RhZVDb9pE+)ZG=l>GCQajS@&)_x)c4KX#8|F zzt=Z$jM>gV#QZO>;I|4*mO@srozQoyg8(j*ozyy-m)40PBUE6+`v4O`^A4F+0CF$`|^oT`EYNm=8iEus=X2y;Qa7Ws`6Q&g_lVL*9Lmn)B ze<6XUPi{tL&LW*jX6NuZz3LG{N`P0l2K7f-3sRfopD#<*PRxSVK2$x5x+cL$QRcWCdALH{hvKZ zYr*t1$N+#Cg><}vUOD%594tgo-epx91HB3eNn&L}LV0J2yhDxU0hzf^%5fd|?|q!A z{|+I(di>Tq4a6r7V-a!*Tw)RL6l3aQYTRNj*2Sm>JpBVz%$eE6oKAe*GMSFK#_gX= zKVqe$rB8xXUY`z>Lg-U$*?1{d(^==0FI9geNcsbw}h*@C+WJ|8BFJVUAF{zb&K53((v;0^EfvW6^he^EMpwXoQbZHPZC%Uf2Pq>L%&AfeqgxYE_B@+HYu^H1;kW$KW zgKFmiHwhKxjzQ9yL0N!olP!oH(`CepHCn2N)#BW#_9vwaqUlkNJHOLmJAT@wpxY$e zy1P)9pdOSkMyH;bUBUzjt6L^javr)`8nXmY%#t77@lyg9mhfK%h#;$0enj?fB2^Z~@hk$RG-t`t@=|Kb2+5R|$P*;eT4Af6TUCvAgY?O&x?Ok6s+vpI zc~slztlgm+GEW-9u|o7NX$ZU}K4LJL6SRB3o2qG^Gg{ZQS}3(lUa6^7MV9{Px-P@I zkZ>-%0(=6uz~JX#E95fNTXY!@T}ygJvEaVyr3{^r3xf*-sg}TC++~q|i*t@nKRY}9 zSV?ci?L{&SPKi!2)N2(n&I!6zn2jAFa+^psU z3tIm%a7Yu4%a%_xiU*#j-Y;TZo6+={aem(MClJSd)6bHCt1d3$Ib-E##>Fh?z%1mL z@t`k1-7lrdSNbz)g&dYk%jU@*piZegg2ZY%#!4r3ZRccCNt0wg(xg63EPHyrRn2n0 zi`UYEbklxGQ^^yPIp4W;=l7{4PPLL2d{QjKS-Sn2tX6fSK21Bj8@J2-6(sml5@ZAK zO}2Rqlh&|~<;bky>KHXSTAbfYWURFHCt0Qbl`!dr+Ci zJ9hy9)ASQ{(;K8^9+1yetx~JDv+*de8<_dWgh|bs>b3LfSE@~Em6eW{BKDPzx+`Xm zZvPm<>6HTvAK#y7XUCDMUvDzI2yc^cL4D^P!sEF`sui(2CA}GoNi+iEz+-ABr%RUY z&Gj!MO3=BTUbNAz!TC}b{`u9a@FnR=8+7j19Oj(-g@^QzTi}rpVUfGTju3c6O+Omq`xi6J1F^Dp$37V{--UhTz0of<%j0 zals8xzqtQn>iwcOn1t&l8iVhyp%gJ(v*0qAtt(`v5+R9oBIW>c$++{8uBlnDwNS8t zunrbty8eQM9VB5cW_&=n)c!OD<(;By%sWFpHBC5J)poJwk|-#q3!g6q2@Xl&_ekNk zz7nqi%L>KchV_m$GT2+RvCl2QP-9!#K6jh4&sAK#<5z>PBnh%VkxT`wP-4_8nYtSF zs-3U%5^YI~C@e%2h+IUFk#&H}gh(3lZ?CYWnk6$N-PB0w*J|9=)YGMad}-X*Zs^eh zG&^<`BPh25*%c1VPnmpKWBtfLwmK6BrMJLY4R!g-hYzRzIRfeX{v=wuN^_J3IKLqx z@dIfJah8rt2xmhYU9$WNHWx z=^Pnzzjo zKQp6~p1IVcb#~Pr8Q}O zO-q&ZemC+O4TzRSRwN`g$R-qu)P*R*R_2W<~US0m-go1 zr)gi==3GUR#7|}L(WRA;Xdhv1&X!;qPbxGTQes61B9KsiFj#RVmdv_ z%1;Wn^O9;TsS>gY5_^}18r6ARzevxkV>6JSix9m!c1=-!@*IsHo;(&^WRY9?**dt= z*#7!Fm5s#LKhI1?9}L-%B9VSNMccuoc(ABIpjB;ML+yMaH4-9?24ff{C1#1-$STP{ z-zEsS)%oqcYI4czTwY~J64x*xk{?O;0sm$f{4cnucFpE8LFli8YX>S^b5U%7Qg!c) zVr>e=AgLRjUPLEv7u?c#wVmcBZd9NJuY@R|=!f&43I*M84C_XFJZIKdF`rrzg<05? z;No!AkFS_l+mZ(JYG-C%y(oP;%YH-!Y4L}`vG=oZ%mK#)znDxd8o!R9gDqY0OXkz@ zq7jyvPhi)_lb=@D#eCZ83?N!z*Hl57G)hU7Px>u;X@yefNqp9PO01SWl_8Q~(!b>PYfjVhv~k~x%0{0Uel{sGxdyrz~J ze7v{_^av#=#M*@RcbcAf=Y`(=$tGk}8}EsSqb9*d$4m&#yO@b_9j8ml$V6`6EJSZ} zp^_i!zV#Wh56N=K`S~=tXfcx&gvl zFBC*zjbhX6`Yq|o7gaBDHerCkd6k(pu*q&o$S=r(=c%tz z!b4I51h1zUrf1XBuH%glAw=;ro2p~zFuR_**e|mg-|DP^!4Ji5t6(Vz*}tpdVb*<7 zG1&-P(IuiqW<8UTD|Fo}+@BQOoEwf^)=dPy6GQesq4MXJ3Xt?A)Kf=>8{UZ+tf&Ra zW77D?Ih`yqrm?Jbi2%}kX#Opni9BVu4^ff>Zx_|Z=?l@dq^%X zL)Ky1k&ycYG(P4-z{JekJ{W!=dh@7Q?<+#FU=h4~zvrcmz;H>X{@A76svG_pdEB%O z+B@~{;f4*7z0i9qNFFKYIbrKrdZ>3tuOiplCe-gg>leX&a*SuI3^1Xu5biN#5 z)h(CyopHkho#0IqjD{ZxRj_d{7Qd~_y1GHDLuA%F2>6FC0e_D5^TYJ|`L7G(oKL7c`DMA!Qr4ea!!G!)N%$5F;)xMDLWnn8i;mG}I{L8|R+Y~&wc(p4_iN;L2J*hkYt>H_+(Ie0p8co+0>^&i5qn-u;l9AQ#q5}5c+NFhcJ1QnG4Ll*I$&oWm%x03k ztv8Z>ez~a?)*H?5U}e;9ZhhSx)NVd`ZP}gAUH<0r-4Fw}Afv}qY3c1f4$DEwq^L*0 z_cDuRQ_vDyDy|Biy;I6G>Scg2d}<<50{bM1mvvykM+fIrS?|g6*Zem3C;Icboshps z9*d2iU3CY-^d+LLvluP`nK%We458q^BvsJb(CqXGT5s3AeTHmdez`>fz_@qSLj&DaPZP5{rCS|hpg71-8rCK>ra`LtbaZe42*T5Ud_)zwvq z*Xe5id-8Nw_X_x)mTq`mY&e$cl09YlqQ~%6I4azg9(m(wRU_0N*A%ka0TUy;GZd>Y z@)wekgO3n9e|eW1kRf%q&{YY_C+M*XJ_wCYl8^!ksdYmxmXJaTX>vot5>hN7bWr6w zRYFQ6q{0m;mXIKQ?l#-*|GsVxS3pVv9W>UvYWC#srL>UHx4B%E&;Fs5#Li)T{nElfEUD z!dH5}nOoc3_(3{8Ps2Vlf9Saw@2n$X0AQbZ_y?U zBwBicqSBod9SOsHPdztiISNG+9*S;OgA9rqxAYbK%tli~X%>5vTbwjOkkN1Q8-VmOk^mAX@0n2;= z%lCOd$Ppj1lae#SU+zQuVx_-0TZ!-IQIEbKWG|HXProtbDKV zp1vRCiJxnC4AQ?x`XFM#cVqf~kb4Ob*-Irkv@evHPX3i$xYo_pN9wdokM7cx1w@;! zJI0aHl!q8@DwT@fkQOKx5Jhgg?9z_}XR<(Wm83aT1kpKO+cj)INBykp{U8Yvr|$=8 zo2&XN6!ck?mO zNd63j2~&=jV}O}*ckprwH@_`2BVNXuC*FV&E}omhcqE`QVekPup~lnl0T*L zr&5RRntEhx*q1VsoqJS|a8a_&jOEfUgcf#HlWFISv~u4a9lPRPKmCQ5hffd-a%ZUO z`2=K?S0cj`SI<@XR2;eUGe$N7+Br54NK(GlCf90Wj$ zP~E-qb#}`vCOwV12Zi=1n`-u=3owd4&tyD z59#)Fgmg-U74sRmG!#~L~Cp4m9Izpb0j6bOS6$kJ(CAGccPWVo7+q| zHe5>%FZbP&8^O!jqPqlwJs8`qJz!UV(GwEO_fjG~_Sb@sri|>gVy@aM&f(T)mLGv# z?X=HxZX9IV=7zc(oo+J@XbJdzbIcO#E_<{fq4 z&)d6A-X|0jwQp|31(?Ypm=VI_kRUK8T;#9kW0s`OO4G42&`4B>Q(9sGzH+8jmipSY?OzNN03+|+Eml~3PgVs8#j&v6a*|1m5 zq2X2aO-B8`e6^@Mu6X!B9Uk3y_*xpe%NH(`)ghvIoV8+w1c^ftJOZL&sg6D^r7`@l z52+RatlQ`jDy9EQ$c;j*6zNj)B3Y2lKdW}QRTzt(t=rzyxNQTFK@o+TtX0h)<)fq@ zX#S^P!^c?sDP|6>#^NVRrFG>m8g+6Fof#W;15oJ*9TV>Ghswhp`9_`WgsWq`R>#J6 z_%ZG6Xs&&`XQL{Z-NRZ$3m1m$u^kBb$F}6l8@$)p7N%9dj*!)lZ2&)ucUuJxYcU7E zX0G07`Y>Y{Mhq-)kbIVJPQ7YNM^mGIF=Ka1<3qh(3N%)B?^w}zx4_d_yrOQKzwr&@ zmNLE?F=gA1W!{E46@I)e6xeD6Tuic7G$->D*MpjK19Kup{)*a`Zh=N#jK$0AwxQ?U zgDAl8W^m3U0|g9E3S)*1AJ(kS33&r@S}KHu#5OSQxX5j(@H2_e+`!CogcK=iBlFcq zQZWz&5VTp1ax8awOF%N;si{?8ble&Dvk^}shr&g^6_8JWbY>y%g@HNel{B7Rf*Xlq z>j zUP!ggQPApmRzoc~=K;aVK*uwI+V^_~8`T$}OT0lxkVni>aI=>Qz)fqJ%!3MJ@oUx> zK#-RXWAP*X6{2)ZDKcXxox`AR2^}+b3rI3{OFriH_5+ef5G*0*$D-yN%dHSkBz9yu z;>Kailly1w_eREZ+eyYrX-jo^hg>WWC>(7UwTsN{-H`RXI&($PD9?WY-msOBB+0ld z5Ayou)*Ebea(fo_Y*f9FMxa6~vsDc1|4uc4s8t5_?Cx@5l~MmZ$1@W5E8=w58`pNM z+_Iu)q_FXg+IMYa1!v9aUN+8aFE+X@Dj1!xpFmBiDwEuKouF!bu>VfkoH9;{Dv)>h7PT2GaBT= zch(l6vsv3%))axz;}nwY#fKhrtlh8b+^d7UUU~AVZ`U7Lq(l!&mNMF2NwpGm7eUV7 z`PW%qqWn>)DdoJ6N|v5Xeg6EBkTLOEr6v>$@U9bTA<3CgdIsgy{?j9T%qOua!VXuS z_LzKgaGR{wzBXCMWlkCSl?6uMK*5~KlEwg7HHPW*5fhC$go9>MTRhz9)H^+$aw>N_ ziC}4P7n}BZ{EcX_>Q?4 z`O~^crEa@-r_V_yhm_hd$7?!K%I(2{I*KjidfXnUy~4J7m#^RI45lLb>&z0}YFUrF z^2=qW#c_f)bBXeo(~KymyE#O>_rs7LnjA70^33U5Q{trT7ldPx)%m6!T%FJ9 z$!hGsw{?#mC9U>h)>V)30taMmfk%8Oy_t2VcbDV4nM;yY9)BUR&MG>Huw7zEmISvD z1lZLJV1yu(XiY>3$Fa>v&JB+VHKQ$CMV^2Ua3LUR`VqEI=X|+h@+lP1su-tDo#I17 z&&-b;8MfFCoI%1frhbw%vh$+|1L_#{FEj2Kbyd}KiZf5W!;R6-0kpHe30h$^%qAEh z)3H$bTkQ8Eb?i~XL)r;28>%3VmjY_&vg{7h@=T6C&WfFMa%3y2EuldWo%Y=UIy9JE z!596A#cu%It@-Le0v4~kQNI_ZCJ*=MkKjF9uy_-pDH+q-{EVlVr*EpZIudu&#!mgG z!AgQelcT*zggS3i@+Y`k3lI5J-pz&=A{WwVul#VsmaO*T{wIB0qg4IOm)_w%fDYML z3091X^^^934Ky{fpK7&KpBW#}wMl?le?7z#L=?z=uYT+ zTpV7e+CziHB~8j{CQP|eD9a+@Fc5A!@CMu&x1ik|6kP3hD#c`TH<3oO#2%qzoq=6b zF+&FVoE`*cw||%UhrK3!VpF%xbX5vM#z+`Ib<|$X;jEt#oWCn#}|@&h^w2Fd=3U*STIdT!h;gw^6k-6 z8L=ocoH7ScCUr8aPlRwG8>IF&I0`axuz!AjvTx_{79agKejdI!W9lE0`!kK{*dfwr z3Hb$?Ta)`rPt=&j!maE9-i!3{2AmPV6O23hAUD~b?mXr*@yT-1TMRFx0d~i^2xbKy zC!ySOWe<9*8qQNt8tQ}#R6K5>izo+misWXN=HOd?Cs&6$J5eCY@;BkiyG^`4v65ja zi$@rCE&=Rr3%SuiRJ@?+a<%E}rd_}>D9k*!nRxma`uxkU)?beM3%vRE&GbF+?*J(i zH9cV?_7#QVc%>4GhOHBMRqIx@eQXiuWz3u_=T(=#Z`8lVss>wh;Kv?2P&GGApF*){ zp_EmEHg2eHW(n)_V+yOP=08=C>&tQO1p(+uwOv)hn9YUzkiLau^W}s>3GqA4_fR}& zv{HbmY8kJ~OQ0oawE5)!2A>2vUhFn6ww>5qgRyR|FlTSCpxo$^P^>YBOQ^%KxpMWi zXc|<7Vhh!&h>pQxZ0ZS5RZxZ;AibTCZ7=*{z>by$>~X^ac6CKy&deaEqlufLkRH6H z5DFZ!(&dK(Mt*&(6pQ=K`WN*JAq8HDej%jT3(+rxlzAcYLgSgZ+bg1f`@0YC9@gr5 z?qCz{CSD@b9BJ*Sd#C4Cc;xi@m4adA)^KL2(Bb;nN)bxNcj| z$>+G^2OAW!V0lI`-G&375p0Nu1D_E%8X69KMilY{J|l{G0-q5jJT-i{y#V;|Syml8 z2lxc++I%`0ux^t_$gWjAw{DXM)EIg?bBU;UG2h}Rc~0|IMLN`0vZZpHue$t$DCeE{ek4L9t{t0%0oC^YX?azRKDZTiGhuq80lR0w zJ}WnXC2g`7Bn5sIerjlC;f#V=Cl4J}G^6jVDUuAx) zQtccd$}N>anE49gg-(i$Q=TR6QxQv-MzA)>CR zH-|^^DjSj+pLJS)b|e4&FeCq^-?@RWE&pX~cPsx@;hy~W=(oJ(f)mTGa$mGFAV{MM zWyRC-U&;F|;7a*HC1w85l8PStr+&CRRle>}(rdF=?0^D|a`;`4n`xH~It2@xdd>v6; zF^HB7A$2O@B_a2iGPR6)(L1P^gRX>*-~tp+mZK78Wv=LWp`ww5aWOXrf^NLB*%k4~ zfe!-xT^n*0_fm)qQS*l0_#+C9# z6Ki%wP_=~u@iX^Amws7Q?83eI_Apj4{F zTY_WVHy-SXiwg-)i;Jmg7kTmOPh_66S$Q`%>`Q7Xmo|U0*cBmhkwXEvBPHlk-Z@Mg z-ryVwEhMxcFlTI$78ggjz}J%(2YDBPu{>Q7SKqXVE0BZ1gAy6<*rUieI7dUQ<3$&C zE;a-@S^~8nAT?IFk&M94%uzV&0uO+^>H-kysECRcj=V7CKpfu*a`YGE=r738-#>YX z;GeO$-E97V?tg-HmP|6I+X{AQnVK8hwLn(}C(!%}vn3U|9*zE@R_FGA%2v{-7lSGZ zzQAov<8}^hgg+g~7;7PJnm?svSAhF@wg5LBFY?M>!m*XnX%})$-Uvl4slHWvz;+C4|bPX03#aguJ*?*!R~E7{%!C{kmJRv-7&%KzLwnX?2XYhI<2BGCnvhhv~TtE zVp?_TVc)8>s;oNoux}MrI^xvBzO_()sfT@QvHnsI`_>Zur5^UJW%^4!>|3QON>LB{ zR#Shehka{}{!$P7*75pFJ?vXA)nDpi-+Gn)QV;vqN%~7Y>|3YlFZHl*t<_)huy38q zf2&UZ)7M5FqaO$?tqHAScfp=MUL{gnAX)XgPo8qE2CmN8g>7&yx)01zgXrC{9C#{+ zl7`m~!?&j~(w;GNo8Ag7M$>L&JUCPVe0WRlP?YiF?a^0dptqq4CEJtE^JPNf)W2l& zkM8}2M-b@o^FO3em!ChES1IeSQdZg@C-)I_)#n%t-gx>P--w@|EBafz(dT$Xg?s$` zjimsmSH$p9e$&s@bFxg1r9fugTAU#)Qo5 zuf7+4-n!?z^YiDu@!yM|5BwhJ|LgeqcL3S0#s3%a^V6UA(B0$bw`W5BkDvdS@$)CX z>v2e0SI1NT*d0IbBklk3^IiD)Up(@4`S~ZX9R1JZ=jRB&`+e~94?X;!#Lu6)9)5ld z|GKQ-S^Rv%1`k{Nj9e)MdDZ?rJM&CC(^Yi*XFqnC*52CLr_qSB`#%=1&_+`BE zDUl&Njbz7c*R(mGxgHtAB!2ANL;!)#n$7Wyg;^~(ZE!oXoK2)73W+G>n+-z^3To*D zAtFqE-4miy_*6~-l?+rUhX{6`A#0Ey*pa?|E0RxXC-pV0lk)lQ$2Y1fk-iv`MDly{ zQpl6?BYkN>B)@={V|gAcAw?2W#7h~^G6^Y>kP=>kJcAN4OhSh7Qp2-GLQDyfmy38_ zBrjtnWGqT^m(JcH?KB!LW7`zs`~{IP%8YVbuqX_VpgVFpI)c86Tz&-ARidsZS4UTt z@vPuk!ILT~O&~qqsvOT}4WBjI@5nCvclLP%W>tA}U(ic^NW}GOZ{*q=%?65u>!^V}-ypQ1>B(2ptC zBR?l{P_;dyhhNm4^5wkd+&>Enr<`_BIAgM-OEPY|nId*E+}Yx%rEJhwP5yl+qn#*9 zCa~hlo~)-XVJahoRhgvMRy!Ly*+Ur*J}b6l(*E8V^C!1*C6%39Sv%}>_FJRu8K=AZ ztr4_zR6KaP+HY01nJ2`vHm`v?AMGM#j9*t@pMDa({)9;r*Qv z;pohM*Tsi3bU`jY(3SrHABqg=fwq}++mObG<1{`T=Z@dW>l8j5=i$R~;KMVF-!AwN zUn($E!;xv5Ck|JtpFR{t(vFM6DWo`Jbci zf0$xd76z~ZKt4CRHDL8FvMY;_78i-YP6BvCYg0n}=@5jCg+ZTVk17iK5~z0ueIEsI zt<$@_#i(wP>z#UwDoZ};t5pg4^4YJv&ERHIYitn<42q&0Qg`t${|)t5jD~}?bS?Yv zbeH*Q;fB))+WQBs*5KeaYx&kUjBs19eEH3%1+6t(-v}7tH9>3T*0sE>EMIkVkGld} zcO-N9zujs^YarbEnQ?UUX945r)``Y(ty6jqS)&x=f}B+c?Q?T?DfZNFRc!8%HOY`Y z%WvP{x3&##cdP!pFIe7u^APJ(X~+xK4r%*q<*RSbyK8Glvin_IQ@d-T@&A@Lwh0pL zs_$uy`-8q{E@1ykG!X(r`xVU?5wP#*tv(p)87REmsx%jdVpB2W=-JB!odV@!i+ax( zIJ;SYgp2ZL?61#*)~yly>t=0#yKxc z&3?Nnl{zD6b;4B8v2Fh0i+yMXZSkQMyAQpaze`EJO!<$Os_JGBtlH@X# z!p)L9J(a@C<+EQP6{kDe`dbv~xw1!@)}r*{S$_-om1*q&nJ(F;wb7%50;c8n9({K0 zzxA-0U(fzKu0f6EGEP=!V*jl{-0)X)>eBY#o|V4FksJ>{^b+9W2Or-IKbC!K{P?&< zHTqca14YZ|f}nLRiaAwFglhul()Rfa?5aP2N86J7ss$qrFG(-@Zb!p6Dc&lv2YY=* zeRJ+hb5+V#zfp}=7enZbt$r7NYG8(+`Zo6YKDVo4SuV}?y4WxboRINZC_QGG%+`?f z&LcekRAt%pLuBZRew+^dxLth+{V0Kc%vE1PKMt!~eugF<1$E853FX+Qu8B8AH$H}L zgktJM0GARb**GeN+nLE%4PTE}s#ia7tF`f+_v%Hr)Kjf z5S2cUKCLl*NWh2fs3t0$7b&75x`FlQQJNwutPMhMusHpb(32g_AK{gOd07TMspBNq zZWI_oN)+^B16BuO!#IwbE8v5Me5PoKrWzg%(G+EOG$arUuI>p9@$+x&XQ)!?<@lS> z5ZZ0r_KTd|(GVFLO+)0ExHAno9OkB;T%>UMeN8`P03qjJkAD1c#jf;2ps8+O(DZ{( zaK%rljGH_%xRTFTCtN9zfZ7~2pi#kC0dLn5HjkB=?cz^iO_N;N za5?G%+6_V73PcF5D}s&AIphi3rT39YZrbBAf$!a)!Yd4|@~04dUS3Lv=SwG+&12Qn z{uCZp3DW))?$YV!qi3W1DeR}RX`6o^?_Ni|PIU68u;DpLu+gcNOm^wrk5CtT2e&@w zU|p7<8SX6OeNX-ruI!TUU5cd)m9Iha4Fmt=$nBb%j6a1dNYu%n!Wg$Pnuzg8Q zH43!N9~TDg!+{dEL+b z_%z9@KaKi#cnM`W=y}%My3QQ*Y{+=qh-w$cDy! z{nl^zLO6R;l}*MzA~Yv3^&RvQwv$XfZa-Nrw`Qu!BnLjGs-)9fpQ)@N$jZ*O+G z*KO!oWqlez8@>DuWAq!r@(n>_^oFYPM3pf*5h#BxU{t@xg?!wH8z_(S5f7HH3>wud z<@N&Hw9*>mHk(Qrxlr?mg`r+stE|rgs`yXg_N%P6(e_30{E%_)vy5ZmG4y#Obv(7 zT?}BB_+BzGZVULZajR~EllFb)!=uKALbDfvD+AWLD(fSx!&-y(wU3~UNlducHn4_X zCxg~;_UPO|`E%@rp0h^hq6r)*U&Y5NYqZ}U-MhS*4Y)PBw>>(aJzb&unQxEi7c6gO z$KK3Mu30)@iapQlXFB0C8WQ6Hq4-sX8nYrin<+He2U#dv-uUcQCzoS_({Mo$MA}^)y^pZ|wtuGg-1nKpq zT&JJ!tuGI&Y?=n%!~0hf=USCuqfak^HN=wB6AZ+vZwVWuS>qVtb9jE zK5u>LJuGfqpPr(qqzTEpufkNIRYVlIuTj#)|5+4>%yyuo225bDbq}` zFo=y1qaUtl`!sSYl6Top*WFl>6Xkxko0QC((4hPrAw);ckORWU?*cgJ zYY(rTQN|g}F(d6@nUaPM;%?fjIRBNUJRb0?J%*@nD(euXc?N7VUx_PYS^%BiFL>}( z`*jYzzCh`d6E$i!>Sd=9jNRlDVUb!%ULpVk8Lr>X5*WozdsqkCgBPc;Rh?d#ygsCj zgF`QE2oH)!i&M*4Zp;)H*=YNmHte-=aw5(q$cby68qAkduD`(T%3t92PAkf4?HRAn z^^4SsGOVwX7fmk|d67Wq%)ny$58#aYefTzG(+eDHo)X8UWh+MgCPm1DegT_N|GxSf zbyeaSUR!gkX7chGFeYk1*YqHRC(21N@i)knk12X zC0&2gRoOoUn#y-zrgX*%`OsK0@ATM|3BavGKm0;B#yqLPEN!4SbbiroN7z0jqC z_yVB_jNHsNyeQd?Y6kR7JzMvMZ|X-`4G36Rwp#3E7}ZeSET+S-P;6F#^XRVxo*8Lb z&9?Y~T$%zbxp7@2HkScokkfuZ>@jPTrg@eq3a z?48*pN8fZd(hisT6-RE=r&L$Rlt>Q?rEw3b2c~l~co*r4%dT&UUQK{VS6qgDOY{N) z3MHT^hn!>y5g^hPmucS;MKjIG`xd#J@*$C~xQzRjDAq_1RY-ua?n@Qn!}JGJ z)~)8&_2!_haO@ps^F}!K(z1uR>vi#f(|tHk+snED^crrYMggu<*8W@B9vO>syZJEE=oLV5z8V^<90G8Rq^N9Kp5@_>mVbU%{&CCi zWntZuzsLQbWa->RK5+t#-829A_hpKI%p=jiihnExa}@vhhbQEv^!KSk9$Eb3K`KF- zf7~t{Lu}@JkAIw^vT6SDhrE9!|5%|CY;-nACcE^@djVJ$^EF+UrTE8zbbU|!<1=0I zRc7UTpZ7HX_}m#8{_*LhFjd+99v>z^cJpJ%q4~$Vb@H$5!b~^UK&cb{uS-*w5bg1g z7f4ec%2E3{spunVfnw`Ua@%E>?hu^G0>Ly%qvR9$UfVTn^N8rmKW>*WkAK{7y371- zbY`b3dR7P0(4Tjy$VjL8lbgmBdi;S1SrXpk=_L85`NtP|)BNN7F9kz1|9JTvfkIy7 ze(8nBKb}6HFT4Nm{*4E;=`k-S{NovqODXG}=O0b;kHu6k{9_?cnUA3*o=jd`p$|kR zQ5c9#_UZlc4JSS+#KW3em>3ErkxO=d3xG-F&m=N#6&bfmnAn$8VFV6pmu|@}+QM2g zw;2|wb@GK~yoD77Z^<9v;W>ItAhld}+o9BQSAQgItufh78@D|nqiE-@4#&=c|7n8% z5!cYoivMYb_Oyy-3xJpomu@UR$?tnrnmCVKxS1|-YGxmtGhqeo$lq-{CUtJd@tJmX z+3&jTnTj*FOj|zMLt8ff#cNB_@Sv9TyKjD17ILRJA?(Cos>M^d3S=#XsGYIGt%>ZP z;Ro@o#u2W^Kf}G2`+)@fvxSc}1V8Tb8X0O@Tq|)C{5LY4$&`X;HSwlxv)*n==g0cK zoZ#hdey;i8y%j@FHF0EFJcxsM2KQ{=>>Y9ldPCpGWo1lQw2*c14jy3~PFigoidw7P zGtraqb#w0vli4CH*W>$+Iq22sQGrx*K&+5b&FS%NRqn8X=SfZBn90~~iF~BgZ9Y|F z`{{0Z+dJ7H8H@Y%^({~4sn%HM`C-FlS>F|&X?+fCe#|QTbz83l@G#27Aa!hh+Yl6qtuEv@d_9SGzf$@C#n8F$R z%sQ}qdeMxYv-T~oES}lNv@ay#y6Ah!-V@OeQTFp|lHJ4BOX2brkwb@);~?W_HPkbd zT!!(pDtWEMx?XR(pB2{GxD6{O7`W&>?bhQtHC7*>MNzvsKSwiPK8yvKThWGi;U2seZ1} z?1#G-JRXRU3B^W#XRim$ty6@^cq+xyXtiB6k3-ug+5-zO-lq_8)n(u7l9+hY!8uG} zWNBtE9P!MYZ&fXzHS?u4*U%b!Z+lFx_(#!}#?&fa7ban#Iti}}ztdh9?iQ~L4A!Fy z^k5CG#td*Ddy+g=10S>9+a?FCtIO!VRXHqZU0#tq(DvEa=gM4lx6x=z zT2qEuV=97;Q-X;QlR#8~z)mwZrp#E(p`{wbb8Zy!t?w9eea^7EE(s6CHVXGG(f(aQ zocJxAxbuU*yVQHD^T$Pcwb=O(>sGQeKZT83$XXe)US^}pOg3&Sg>=iAXB7sfxm-qh zL=F=ggWzs~*&ntGe0s&JIM(!IiP|Gj_kmH>tAp4cBklSxl-1xe20cez==MJUYu`Qt zBU2&dQ%Zt(p8EgTdl&Gis&nB#A%RQ)-BCf~J*m+KFD=wd8H1968Jv+BKn10(sBM*M z)uTd2XiX$xGMdfKF0_@F_UQTSU+v-a=qYW*q5>vdlAsm>C?K?QQDKi!xrhW%$@hEL z+H(u>R{NdP=lLI=hs@e*uf6u#YrX4T@BJl`KC#C7d4_b=R`-MN#tQawjt~vYtO0yA@cBcr*& zYWx*{$g8L)EEuD=YMMf34e@UZay3vBIx91le&VlHGSm1|+Bf$D1$R}Q24E1ir!8Qv z05}5XHsXi0CFe>aId-lMmbP7K>d8|#_y!{Fh6^3M;)902S>2=(ySTE|j}SoBc}2DS&wR~osn^{8X|E}e zotBwaKRlr7hk7evloWl7}W}h{_EFV;Ume>TDwzA4H=PG+FGp?+OaIQX4dSL#R3ZwR%kT}lQ_^60ogHq+q z6u+Xv^W(5O!?lJiXCvx$I+v0UPh7%4jFIU4{OIufR~(f1{G{~5Pq8SZZ7iq!nF-Cb zsRR2cb4fAOhwBuB+0)%utYl;J{ih4UlkY!0M_bGi|E1IE!>9Jdk0g%_Dm>)3L_+lhqYTlLSH+NGC*k!}5Z+pzw2rP#7#8>#T?EBGH{SjeP3lF^@ z8((r-;BG7%-iQf9kJg5L`?6?mkiv*JamWsz;bFq~jxz)UgsoDEalyBNVE-afPf-N# za>mxkR@&tx7GO;3zJ%O$XK?mRXb+mC!H}IZ+SIR?7D0v~F{%sOtqCvJuamtax23uk z4^fsx@9f^rT>8{Y@=Ho|*h9*MKu${@>Fe_p0b4(h(M8NxRiKJ_tUOM}?V}5I=ZRkk z%9JT-ek;!xBZ*>8)v?O;{Uzryk=dB9`LL4n#oii9Y{b|uBCq7*5ZzRD=14ny3^Ql0 z3flc)*rQDZg)+xCaGDSzYCwap@Z&`qo2GI_AUKkjK_HDT^uJt`m?>i+|4MR>U$}{Z zI?aG3-q=_chnnBaCyCZMp40@LMHp&AA8A*FpV4rv?LP|dgnpn$Tocq?921&vsyn0|6C}xEuyfLa;K@)S;-4VC3F4O^J_+KFAifCV zhcGJWx4vrKm~U>1Z96Coh+oLhgMM>IY)4W(Myqk=n2E_16<{Xi#^_SLxMS_=V~a-C z@+T+yRb}ZrRN-|n78IT{n#~oG!(nH@I>%?-kRM2qbFe`it0z48?qqsKs8usiz(2Ll^#1MPA^qB9)_wYM|t zspM8@q(qFh96?VJjh-Tco^W0f?9_1z{VKpK1@MUJCs(|;l=mKu`T_qAm@h)??{>aQ z$8rMH{D+(X+4ivG6!I6$|A=s+Da@ZC-xb9ECGkH?3jS}6*HVyw6sMP{k`(Vw7jI1$ zZ%P*@riGPd$V6C3dH{RUC`Sup3ou7EVo%8RXcD~iUBOy%5 zM-jFz`!yg&&{!4AHdZ5ni_ZYPBNZRgv?pLJi1vp{xWSE<$5NtuE7CtX`bT1`^NMZyr(ORv>7NDs5Cv*5EqS$EWxe_l_2FQm zyLbzUN0>QxE+#sHpw|5%Uo6!bww^%SRs$1Ck8s7#A8rA>-7_-z9hcEl*jh55SAD5? z)&Cq3vuo7XTsGxbYM&@NK-kRa0PHjbk~8cyIxcWtdQ70WwPOZhIGqcP&|0-K*UA|w7%x`iH9;#q1q z0XxPDCj@l8SuQfv<^Pb-=C3@VkJLHzDYI`)mcdtrx(z&vJEoebo~QAYtZCFz@5^1NaPBVW-2ckY zF_*UlHsahV!_&1-Kb4+=eVxSuW*O}s-kJ|}3bphcZDHs5SLLw= zTjTOczlNB=obWOn_^iN2aikYZt&(~8M{cDQ+uqBxucuJ;eK1q(V4sNjvGs_| zD?z?9X64=|nU!^F{!%Tbcx4%PB3~P_PCw%%@Y2UsUoo2j$>#07$Z-;Ke!^!V&_(3g zQJ8wvimR7z%eQP94^+lr;+KAlsC#lruuLRFmMv`#5YpKmP#OIZA1WfpWBArY9g|7> zD?X+DwQl>r0azW#+cG+q{@lAW{!j0mj!Qo2?sw&`Y&Utae>NBW+>38=F=SP2iOd}5 zykumoP_tw(y{KwH?3EE;kE`K!4Gt!AHK-7eZ9j~Z}95!jH|5{C@&)fTwf%~Q(} zl#s>)NkJSD!F3Si{y6BbpsS@rWpGUKN5Odg$Fllivn^xn-H6Z2zscGu^m1mpIDlfd zz93V+O&DFtI;%uti<4>`Hk3-)=@*=({l$=#h!R2ab*`4i_O~+p#gOfMy1$tA2fEnx z7kdqb@M|c9mm@t8f3Xe9wPOQHnf=FQ%!MawVteuV1PR+?(?mAW@+MG6{2X|`uc5$bEEZfQA z3>7a@`&SH~HcEL?>uEa5zk*MDS?iky_H#eYwaUjUOMg&&6ic7-%2EMnGct)~`Jevt zuirU8?RotaIi39C`oFM*N|9ZIpp-RmEnhy?o^$iLt7>G8Q}VRTX{7r_X^%M*4MV__XV#bt->qTh&gpEz{RMV})*;qX*w@g{n#guGURGHK!{B zV2lTT$hwJs1YOcmtCkTDOybMK-|Fwe)iR^jh!VA}++YNJnaX_%%QJg+sRZdf?WEk7 zhBVcEsMKHe|HU(;)-uklrI2H8k>JNV96^36$1T#ue2>#~C**Ce)3vV2Sxh>SBuVXz94;O{#(;w;eI1n^9 zy82mHhsT&#%YKo;8oBHBP$lGbTu^v7QK%3gsr{ctf2jGk8g+_V@nSRL^?V~S4ONO- zbs~!i`xL1W{`PZQBN|nT+LVXVFsT_VZ7QJ><*9R}W~kKEx-~?| zXe_IgnkH&o)1G(eGQIdgvrQSqa6V{*7#3=$trw~wcfDFDjt6c0#6o4$Tc5IC=&_5O z15?naH6>S#F-*JXs8wTlY$3I3Bx}uFwNu$YYP|~F^^&IX&$C{P`0rdVyB?A0|IGfl ztMNY7*!}JzA6s*>-?*#y?}Xkn?mFg=`gh=S`uFr@ z`uFS?dY5BqX#$CgfQ`kM(nEGn&WS_CcnQVOcv98&Ypmt-g$;OsLlZ{v0%BipB zb3ywpZ_|p({S4x+mHe)t^_~)LlJb4(s}%NkQ20P<=yd9QzNg!V71SnPXsE%6TQlo#(5jFx^^aO>T)AO68M}K|$r_!U&)sx|Cad^^M zxj9{p2kYoFlib1HzmI@Hv+b1Qx@WxR{beR=h1AWBlMw)CkFrfIq^+D z_tUy@SDEk#C-uefjh0_1sl`7=Ua?qMqpD2MI-S zi;`SCugpiZF^5sV9mrBOf<(gB1qbR%b2>aCPmuP)XZ{fNf{F#UJ>_E~4z4*>y@OV@ z$Kzam6S5w`Boc*7Gt8e61MD--3j#0@363+jCw!9ismcQt>#UoP$sq`s3^*CYOl9au z$uN4tDW7>>{F4Qf=<#?6*7kfnJg+pd+Rw+Re@)g{YScl|&&8+7^J2yKQtmb+SoC;e zKl0=(60ID0`C{IjMK@8b>b>ZuVo*@lfpLHK?vzy%NtQY=J~=+k=RYrQpYaboFY4YI z${}aY75$vH>Vml~0mul&J>#upPMg%QA75m;-s8kQW6C%#8Y)gBpAK%;3F7Ch0UiNQ7+oi+GIQD zEE;9>#dAch5T&E@qhIIepooXgmG`yAt5PKq4C^|Po$J!gsglTor7o#INa`e>6$^fQ zq1;Zr{R!$UrGCZ9=15;Nxb36`t~U1)!mDtvxw`qie0}B7S4D-ZZ(HrYTI}(I=0Z{D z+GBF<@hA3L3K-?A-|5eNuX$(RuGQv3>DRJlQt3)Q_~qn#rA{j&c<-RS=9;y;3)iT~ zVc1r!Y2KZ$uRQvysBq0~Yf`Vx=e6-Rd2KM)v?W8i?Jvqz%BS@^%(;K1B6ov30$H+# zW~5u@Ba5Wrceh&1ZrfrkRaS?Z;+;_Gf0lR&snCflyVt&7twK}u$Czz5xW|4I2YxRR9Jx(`YU{D z!F=m?`MTEB#bxwFPJ>52kfz75__WkR>zzFH5J%6jMEp$ zZ`drE6~%FTnr=tbD|8oOvAuUM2lH+tuKf$TT~zQeKb?N?rXUg;EoDiP$Zz z2NdM>Avg^~Bs!MKX^mgxmY8s}tm`>*$hbJYzJb6j3+&6RGfh3Cs(o$$<^^l={Z z?*8WVB0yu7eG<3zoDZZVbKbSQ;t}~&W9CHuM$G;Cn}e3Z;tO)cEVVZSBLnV`EgH@x z0A0j&q^WH8?}yyY{~kNi8N!vC zg@dfS>ggj@pr;%jNW-DrcScT9#?13%Jl17*Oj8iHAh+XK+#UK-^KD(DOhUekrczG&Y)z%u5McAS%ejzwNWyQa zE-UyQC}oTx?x~Ys)zsrw-9S~?R3Oitj%Md})kfh)saV`2f2!>beC8{d8SwOUnHrM$ z5?@4_6?8dwzL04ypzuZ&G3QeEBiy$|^)d*%&w?hhfF|kw6vxLvc`?*-D!#I=# z3nh0vwhm2#WrbqFw`W3|bA|s@Qsq=Xh8?Tq%6SYmlPH~M@uD#%mm^yIj}_$#24?Zq zJj&Vi?nt!r-w_E{YWyy|OV9J2GiUbL>qYdc4XiJrL=t$@V?~}+X_^)Qe^;7*_k|d} zuOw4gzAx5N8FKZaGdBa0iBk&{&XFU+Z$42aFljElm7k2+uCo;NMmS=KJm|H*sfpjj zUlEWFX;RYBM$Q~WmB&1!e+NFSe@}m0|DOG%{APR~ZACu1c)6?#7yrF7QesORaj|bs zOGu6To7^uPMAS%$94>|%mCmc|573Bq{anpOYo!O672$rrrn)?h|1D8)p z^C>Bf@^9!syf4!588CNCV2?3&RRs<=ZvyibSq?4pYLl?}Yf4wz;ff-X-ro$i68hS@ z=$9s;^M9Fd-kooa|0TX=m~;1O$xVi0S903(c`1Cj-+T`zD8HEyb?6?XAqr%KFEe5i z-yzt!E>n22F?x=+AGjNR=#k;ZgKsg7gf3XFc<`7Yu02)TgPj{Qede>GUy>(LLh2s# z>^*uWa(%fUBxfR?&l?jzla*2bQv3Bwtu9g0D_ zW{z|J)gn#Vo{A1V!XEGhP?MvNMtn_iO=KVCHK;RkjyfY3=rdB>USGs%rq+x$B+tlN zbw-|bnRKdLCR~TOJC1gUo5(04=H*yM=CCj<=t3bnthsVTIZK9#T}x6zH|^@|2>k*e zNo|fX^DG|7#18A^o@B`(6B=pWICPtC+vS@VznZlXD1o+6%W z=Hn)jL0w(=G#&{!CE;n9qL$C3E@Cef`QvkyY&S(Z9B_&OwTS(MC>YY!JDpsrGag=7 zmm-d5va>-F^8-V=L^zQ-J|$Pc%=Mj+RsUCc5w!6hBlZ?=)81`wNUtWO(4N?~p*UFS zaL&HGhszph?aQk#Q&QQy#_sHF>JmOv2PKt;`>}W`#zV>{tAO0W8QI}(AoDD<&sri zO;pgTnJy$oM(brNBf!*dT}g}&|weVtw#SqtYIwb*3`>Q(5U%Y>Gm zW{BMCi7}F}g(n9sSB7Gm`n-eifhR!K$)hjcuXDewXx1#p~B+8EK+1g~) z_ob?&L{j%8tHw!Hxm2-#^n+vos#m65It7sQg)LXTvgOiAfTRjWvA(QQ_2oWj)Elr~ zVzUQv2Ps?HbJBy8ZWWs5v|hO{)qSWYy?&D-RJ$h6@tJ=MHNTaQq-3mp^Vc9zyI9|^ zvAz&CI#F(KXFq=HdU+2}8f}NaCN$~{=50=o1ZsX6(hDdXWvItUjR$X2Bxec%21cmZ!q zyvoS*l19uU|3oW#nXK(+t!QAfqEdV1zSuD|3txREebY}r30<5F11R)u*xUwv3&K#j z&+Y1BPi=K^ksvm4ZyBz+^LMa4)o}10rH~yyUGXYVx4%%-?Jr#F_7|GEEdT@*amx-O z5(}S|P2aMZ`&|h5!t`yyw37j#{Tdd!(YF$Lx67p)Y8s$zuTVsT1V0c;LFm~|YH#dM zXu7D5VF5WCn1@k3@IdrH*GMyScVq5~k!I%RrsgK8DN*~8DHM}fj5IU1HZ>TbW$tWh zx@~M~Fw)G}*U78{O_FsWnb0d0;fQ2T?K)m!Zyy*9Qg>L9N@a4(0+Du_(MQQJut{yuI!6h$ zdZhWiyto zEPmX>Cxy&26g{1FCN95AXla#LMK-84=~1DlGvZ`w>nPr@Fxt%Z9BbX^>AR&$)6)gh zG(9cNll1geik?=cfY`!J%#dYDs251sJMQLKz9&gU-^&#Qk28*6r;a}f4@5Lgom-_{ zNQm7d`}RtWyBQ*~88iib9>}}FC7+Q{Cdp@`d8DSEd0mQnmQ!~u22|4FQ*@XG583n^ z`c|+sf}AJCM^k*Ro>qnZyd!Aki4-)LyVd#gbdL78xHF2|RmP3WxzXM0O1^y5zwr9> z^I!d4rWEy)oUZ({?%Wutf44`sD-63=;!`!P;#uu?)jV~G$R;(l!wX-<)SSI_=SE-b zOKUS@ngeHir;6jToxQuHlrq0FjLf4&05)U4)C%cqAf zHGjrTp)Ex2sHBc3a&C*%@eKVt@ErYn`q%XD*_ZIEU#sMeBQeN4^4}?Jgdpq{igi^+ zYcmCn%VaX*@~z%wd^R$S%glCj?&DOX#Ev&Gt>SZtAKF5>(J>%3K^;b_n4Z#nYp%d% z{kp1iz`V$~%!P6-{Uv?J?qs<{^Rt#E?U+t0#A!#j!bwcE`cG+fb!H237;|rqxeYI{ zi$CG!5V?u{tm)JAWQMb;y)&EoasMCbLBvWx1@NGtxz6ha30Z_;&qI*LznYBvm!bfut5eMgP%{e z_?5I^UwnX}81+(oJ6+sLQSl#H_$3{m;p1;-a^@M|ANt?;4FA;e8Rp-Xq#=Jt>GR1S z0HU7d51gJo$R7f93>fqrf9Qc*j+Q@kk7qb3(Tgs$yinjT=;mkaGb!-~_KGx5(?j|` zVZX1Zr?CXOxoTKjBR}@n|8lI7Nl;Z{*GCv@=U&Q`FF=**5pLIpC z8EE6@G(r{eQ-TUGbpT;X)X~pXdhHfo{Rixn5JB~{hEY{D+?!8TLByU$4#+(^m=Puf zCNT`*oPIPoH@1%E_rSa#s5(=Tcj@m5ngkXbqT?-|DecYGS?9fy+=9VtR*1BPx9``=TlX?3RUbY7tPg`6gg@&8jM(rnSPn#a|} zN|A@LL=w@$RTuVNcXb-YV=!0L8fd2hHU+}3CL=p3f}hYz0rO?2`70o_(; z&u`G&hl^gT8l>n;LiW?@;*04^mden@Uib^^OFo*Qc)yF#m$V?-T0o4+MdD$Cl2!U7 zN}5ss2J;=ZZ&`q$@Ly=&&EGk?NJROU0+R$ll(>XMRjjS{qQTq&NnwZ2QL&R zN~Nw*s>_-5jO0{hr&N)4TUfEqq*9`L{^*Q+WW%S4y>RCjvKOurd*K%OK=#_xFDRD;<0*< z9M85P=BXSSX^tsyyKr0$G;9g{zR&z#(GqBDaOdw8dSe7wP|e}4Q~X^%a&I5uaFdJ$ zEuDoAE9tvRb(G_KUUpzrKfhJ%w}Pa+olYdPNq|n|X^{<3UQD@(@(NK9O2oD_J(gO3{)inyl*}7Xhj*&muOK$A|n{7=h20u|H9JYEF zT~n&uk3G3~5DRo0@jnY_n@`m0iDA~3$Sh>ejnfA+KSFT)9(K`q@Rv-#AP3DgqFH}{ z3M($lApt&yafR$P1b}J zJZ(lEHz)M?*fbtX97FTKkae85<|Z}|$I?m>J8YWrBV^)s|H_a(;3t9jy8ExPz9GZ$ zwnR^o@sMKkC;6Zy%l{KkMPO0YXym`K!@a7{t%xp;R!DVzMW87<#BH!5`qhqdfyRvW z3w?i?1fIaI!VJfeGxZJfyMXZ17@iVvTkUEIbJ8tp^$JGUHubxNY0GqwZ;77UwY-_o zY+b4N0zq@La?fUaU-U}Q{X8IfwS?VoZJl>YgK`H50g&A zX#5*86(F8~h=+Sc15yGLfye@j{NC4%hrq|+t3(M|wTy?Bl`IQ*4^mmuPB1;Pft47~ zw8swT8gYqo=qD~?!lpo1RIQ`r0uB)y7f1+5`ceIEjvh;Y(Lz6+)&CC2@a3*I;>3x` z@pZl?Z;G#1<*m{n-b$HqS`N1n zSoAUKzb!w1 zz=!DVeM0_3i8n4~6%pVpT~;9hPLi_iEAhe0Pgx;f>N?qS#PBHjQ+AV;S9jKsCfI1-ab8K)MxG=EFTAayg|gw&LO=!IqfhOI~B zb5Ls;^}nQ=wOj0GZ~3uI9{E7uY~g_tWL>%SJf`9TWvszcpbes2|*@e0#Y?&#Hp(BTnTfQY#^Qo zN+~hQ)!&v)&^r>=r+!6%NaG?9Mff75LIyn}=hJCOi2b=+qBoGDzH^h$yDT~$$nM-k zVYC22&<-qSpR&uzNGtoE*sPhyd14Dxb^5&Qop_0B9?VZH(}iA%XDNJEH|mKw(lA3H z)F9g#Qs~VI&{`k!M{dIHDuOvhKV;KGnfwUkIgx~(Q5q#PI$sO#>2K6T;Ya=}SmIg( z@D5?3zD~6p|Bx4_bjJhK#R^|5jGksli1H={Cb@D{k|PlfXLZ>pu_R+sEJ@cK==*Gb zL@4SPvX~J!XkDrBrI6+>H&RX3_*j=> zN8IY-w4XFY!u+KAOEPUgAc{4u*9O~@Y5M_*PNKEiR1oXyffA``%3(}Ya9Tf5E)`0n zO$C{@9~dkZmCT*AD5pXqlW1)=6)3YGsFaE(T3}HsUk77dlG1tML;Q-X>AEhYn3VI; zbV*WnK=Mveam@!jVN_IJEA8)wtJ{K(qD*`&O+_I2%6UimqiVeYJ|k7c4<-v-3StIa zD_qJ-dR9uyZvLq79choZPx43o{QhqD1@*VN{-~)u&?*1uq(Z1WZ*niARQex&ojU#` zu8_k7-sDy;I*4|L{%2D*Z{mW+7l~&mTk(s0qJmi_1xb zoTQ3B338F*PaeCXC;sG#8-za*7Z-4Y@F(Y}gMT6w-MADn!FHyM=W{Q`W+cs}fXn4p z>M^I+oJ^Kw3FFGqdTCVE28Cqs?_CyIel5yJ0O{EImW(N6y|=xi|+R`LC! zcZaNSnMXs4*bm<%*G13O(z&uH#@q6e;gy@|cn(G7+#c zS@V&aM(5&dQUs;(;FIjTEQaP^sj%X*esW+06w*1Gp-D|M-KN+YwKN-nl@wbuMmjr+ zt@-4Kf6c>*6L|<>X+7TqjUXCUFj*_)!NDm%7BVc&-dvqxZ;I3G%`qwVCJcWhg#SN= zznLcd4Y*1N#^{m1xvmHPrd6W(b+snHCukm){6NzH4B>Bn+%tc}O2dFS5`W6&YM2w3 ztLZcvcV%)lVOjLBRin6?fOj_xOqjGE8C;FeyUFEhNO<<1%hkAGV#FV%YsK6=LqV9E z)SY*!=+4}{9X&>v8xLL9{|bCV7JDPUErYjtg>vT>ZpU9tayIwSE_2OO{EY&b-;PTG zOtduBEXn04P!=v{2F)~=bC7eaPZT#G*MlH2L^C^rW_;#Kn4PBta)jAQwQ(-FN>JD` z9);y;;=qxW{o%TvM{)g1j%cl;~ zWWA4Y@~}S-YW8QqgAnUq?{*&a#Q(f1t_(@~T{MQm|NMn`ljbAx3GhGCKp|CU9_D>v zcDZ&A!7?jx4WI7!zGuXh=u5FdH}L)5Ntz3~nzG-T1ph;mk7=v;pZyek!aH?HP434= zT;?pv|9q8l=N8!(@zW@>f$|bB!{lhbXIYZ(d6ojoT;Y0t;c`6#ei1T{C$m&w$f^#^ z(<(XZ+CG!vVF=THl*R1qSIkatvP*R=&E|H_*4$1C|4DSp*pY&15LCnV2Ycjq(zrsn z9Qt$(yOg_3M;;Yn)0T-2o5&iK(~V+xYlli)R~`TWqK=Ch6nN!3t&|!^h*4@ z@GL#@NU6uw12bqB!e}R^K0=2|mCtb=V?sor+!cgqD-J0|NE+NbRx5pBxA>X|lydMv zk&26hKx-ELg_<#BJ-tTj4-TmKsOrN07I<7G0K?w`mC!U~H3!7sVj6;)1LAMdENV&> zz43X-Z&!bCxA_v3FVo~}A4QtiAoP`ZM1l6CBT&6WrV84V znh;Oz=;%F1?h8p7EF4zM^#xF7UqoMk zhu8LDS(JG?QtB{PG96_kJ8V%lJsf*ys7v?~Ppm{vQ%io(SfT5w;Y#?drIk6{q2dlI zgwI-9>sEY+iV~@4ax1Q+Lintun$KDl^->{x)>6%9DUTXU_^hS1U>)i7BwAETMH3ZC z+rm|1TewCgQeBfJONDOOEk0duRj6+t=Jn-vimne?J~GZg(lpShA4qSqq+$EcrjBFN z@-)@Z#(aC%8M46O9J<`zgFhO{!{sKYv2_r0t#em!@I^= zG*F-+X@X}wD4zVEW`cJ&k7t?SMN{#D3Z{)y{wyUjN$bQtYUgK~Nv^$A&xva$xxh7( zG~(h^E%2~FF|n}~Lo*OkHaw93fq66S@ept!lNFT{(H@iysQ!x2juiMR`j=G~ z+t*MaRaNz|_T7}K$~=ZGaXPeEWre&G12%eVjZ07khQ*MdmXf7r0QhN6dM~pC=nkDN z`JlwsQA4FIzk}W1a`(5y{VjHX!CI=FNB^R>l0Bxr00#hdW$Um7KRE#^bE%pz%=7%# zLRkW%aVKpu=I;#V4|%!%fIL0`1871GOjO@+Q|cRTmQuPmqw(i-LxLS0r6dbK2kH-0 zof;r1_$ALtwji~7?>YAdI2VaIQ<)>TDl%_TGjJVOy&DS24t{dVr7 zNzq^FU0xkSTrNX*etNvXQt$Gi8RzGpg=i*`0_&9luI9q?dBB`ISpM1<@s}kW%8#0) zy9tHg9A>=*r*9f7N|JC%xD}hgjU7%W%)*9h$6mQ^1QDCzS(H}CN-Ao z*XIqF^*0*NQ5Et!sVFiU2dWAgg;WeM8uL{r$u(uTIWGuUI6q{ff~}PbH25u{POcSb z@T)+BUkwGVa#zM8z#TLR60ag|h*SATBV}@Ip@v-0Q9BU}0sB#D9A8zHzo!emhc#Ee zHE3c$Zrxf;Cy1CKulmwCEc7N?Dzj_Gr_*C=yq3SR z4lVI|DSaXqcr}NI>?QJ$^QwVJ;6K#jDHw3G!JeeMzfpBB!AkVP4z}X5vB4}&Nz5W> zrCvJB!V4r?atG5Izav$;)h%V9gy(CN7^&(6$>(6pLkc|N(llt#RX^4edAnF+oyus5 z*r5Su3yJPgs`wTQglI6qz*_^hojFLf%&Zx7usVBOj`&tyRyKH-%KH3eQ#pG zt}ZLEuI=wVTwP(^UnJX$JKomn3#>5%{NC-=XZtObwCcg?ll|7He7{#cT-~3{B8?qwhVe`%gK@V3X*H+usqyB;R7PnM{)oAiI zkQBz;mzpMR0&;?DA0j3o$tL`A;x`C(`O=uBrop+aQdEh!V>8-TkSO2NIzpkt#3NjU zRG4?8{->11bgy1oh-fGgnrOTY*xIQHc#XsM)8es7D< z2=5^%m;jQ`i|k>C-w3x`3kCMP`;0OBOjYz-3Xc5VR=+U@ZyNXN5LbtWtn+z4P+A9L z_5~cje(yFqUD3JLxAdUAc+gItcQpZ3jPTmdb>4O((r#`T)Pi4l*RnxmMxJ{sjr`ud zej~Ei*SW!OE)*wkzjqHlszzi}*YZKjV{aU>M;}?6AT8HK?%5%th>fR^o3^jeTfy&b zGG?FPo~$%5Tge2;?wU@S13*4f+p7k=hmF~odsdo+)^E=m#NJThk=U|)@A~ND?pj)r+DPGE zzxOq$MwxL$6EaOk{SqDzc;Cl>&1XcG6SB=e=r#YKO$VCusxDOZ`>0>vwaho7=(9)X>IZ~UBctiIaA6yj@-g(1 zoJUe;Qz@BHsZ6#c_oy$noDjk8D^njFDr^pTKdgQ%Y_g64?+TyoFUy7QH5!kRQ;ma? z@2iMww2Bf~T^o%}qF=Hm%z=F+rbP5k%JVeyDwj8K`KV@H<#H^S^OrWsnB}sP%LSTk zl}i%5PgtaxR=NBNmra^wmCF;jY*P%YJZscX=hHJ27D!UbVArx>=`sZ6$>b8>xIha| zPs-ibQI?{gGr2k%C$l;XFw*{yr_j3zSgBxLu^Hq`?MK`T=icK)RI^Lmpn$bg$rP^A zqN*ri$U!9yrkfd6&9_Q{$+#pVZJ%JNe@zkFNpP5bjYRk09mazTn2qcRA5|aBK@_;! zh}|cE)wv-vyvII_GpJ2kNa_8kf|?#In09N>8o!e0w-Zqy9PX-eB)CTrja|eE+LAT& zdsiEceHBHKL}NzdVUcutS?s+OD(1>68;x&Cd5N5#M&oOg{oX@HV><=m5g2v`k063A zjwvjO5hZP@8o7vvB!q~fY@!z_m0TrXzcoo^B)!TANrFvbC8RUAbb_x*ieZ`L7TIT(0Y=GJ$bGY5|kki!<|;g-_`Q4kCeq>{X4QFv^*@K zgrSC?ZT+AClJFVoWCAC1t~}}sE6^O#1YI6?17}~K+1;p-?AkjDmzq$ z{?_$J96n9tV_zWtF7mPFLHbkONGa(kyb}5@B_DI6Ben*fW}KL9%N`JxJ50()QD1*& zil(oR5&AlUS|4&Ruz&z43!p`zUFJK@hfe{AWJy)h@}_L~(E4XKo}3WkN1=`(@uW~; zBgK~@9_AL^FVYkM2}c4=G9=&%4H|BHGdv;h5u<*j*ua`9MxlG+1Z@FbmCsT1CTm0H zL1I$LQQ0vd4Sy2u(VDb~O%k!j3?>2Z(13Nruz)qW(l=|Azajnu+6S!rOMKS7Wj^cv z@}QTFUah*0tt5NfTw8S-2SAFyM*sn(sEqFRV|YC$Y~6-GK?KZ{sCqx(ttqKKea3fu z-e_6%z!}&0yfx)Dk*xEBXl1wZM!%`@jrq;FA{5i-yt%NNG6~{q)i#EjZI9TucoK)0 zjr8}#w_rvWt@wQXm=oIh7$jQ>S#3G*7=NIGQJys8VqXQLG-h1jvjXMb)|w*ik0EB# zn1gKWh+vIoj|Wd?jX(MEfBswpknziRbPWAm&DAHH?_|w)Y*-m~!9Qd8F9_-BcN@K}YvhXFlA zV$`CrcQD{5{vDVM-p--j@r}D&MQ4R7_K&lE}ZMYE-3aRHb_)q=5vr{m?kGmgUUxUQ<$i?2MDWqsyubW*qMw zSzbf->F+RsTcU4v98dl|m5Mt0Qb!?M0Dcr-m$3JZs^SWsJ7LCv3ZCmf>${bdoEd;7diH5RKh7=$SRjxXx*9_MvFV+;;l^LZzwCg35t za(92$%4L18UcZy{`W4Cg^-Hf`Uw8Fd#U((k+X=F6$IdWh-3n%SWZm+5T(Zx(Tp|0~ zp%`V1@%uE9?P@9Q=sf4QN0=cmQ?KYFiF1i9+L+eB909x5fGFSjX^sHLCO*evz&Lo=d3>t|3@P+dc#{2^K&p!X~CsZlS(b;?$OsbrIo>V!l z6P44?GgMCNMCJ7J43*QmC@QC)mz0RQNXAKq8&pm|&rmt76P44?GgMCNM5*}m43*P5 zQ91p*QuP5063b24Ok4p`Ic-!br;S;G_YP~7)4(aEnw+@Qm%Cp)kR-4_qyU=4kE92q zejg)3u$SoOe?%+0rpcLx=HSx_D5Ba5{WRsn6#c~TdZM3HU^K=LW&Yxn&qqJ~J^G}& zlB4M-`OtWpe&X`8=%-U}`y~1avQjifE8bJ&Q+WnJW#E@=_({@F6XqzN)Q=Vkn)i`H zPU6<(Hv6oH$|QDO?lOsMrQ+8ivQkKYwacZ2@n4^%()8@xx|U4-8mQ1akYT@l=^4mPYn?hQX$368$$E*@QXH zANQPs$7TRFBT&mPVjUIXo?<>lA$cfrrYa){5!A8kRd@vnftw&QO^*49h$#FODnxzQ ze5v}Y{tA_$wfYQjrVxkbuCfwiDGD39yDdjLnJlsEdKnb7-KD76{pp{E` zttLWz-ZqeHvmd7-OJ(BqdDl>J2)*l*I!d0;`yv%4n2@02%NagviHu*56d{_N6w$sq zFwpw*kTk}-jJeBoc%U_0%iyiP1OmGZotm(=eLKQ;IrxkeEs%-!2$dFe#7nR6NcOV!NY?}U0o6`e2v32*u0}846|Ff z0Vsy|5;vd7(@Yp-TZ45~f4_HG^^GE4wJz}kB;yhM%L!|p>i4d%9%U7xF5uGVeW@Cp zX$wqRh2AyQ=j#;8RNbViw5-nCX4tnutI5}8>`wrbNg#rx0)_M z3;|T)EYXSIk^m*%uiFnwIOo#(elnM#t`*o^5!HAlQH?bl8ehRd)p5ICM)FOtk0!B? zCW&}dzJVh9NacK{$h#hqvC2O3d$(2x0^UyclhOD)=q)yje5TR(2p1x6n9HxvyVhuI zRD~v^F-GC&V~rjXw{a)ydp`jmH7K8B;*I^X0mSULelJmkV#|i}O~b9(bL66VxR5KF z0<|8LD!k}})(i3gWT?hOkyos#{Vk1%{MKj?(JdgN?bYY`t&vcnE+X>F5^kQNM=xBAc}&FWJ)L|kr72EQ6VFlX&s3> zF&k+19;_~dG`pVm)u;L^o|-6y<3WDMV#rpF6WMaZzE7~ul40CY78Obd0u0W%(rP>; zhdbD395;2k1%+-%+Mp5!WS|>u6)a9R=OMnC!$!W_ML9S>RP`pDALc>;v-+rO_OsbU z^J*y_3BVZ!Q_Qzi{!=Wez%ZLYD^F@!tk1iW3P)_HwJ1FLG=mQqV_w7$6i+*vk68}N zV&hC;(A85sJz<+5pAfVjUKZqw;5me`4wrT?&M6XzY-1AHO#Uj!CB7wvY`!PgEJSx? zgKz;vGffc9G&<;f6$~S<2$w#oa!(0{N#d5@?&5RBq-9RP+~vb!7qk-F4imYLz*VsG zbF4oJ7+ETx&ImjIh~GZVu>Mtm%x|B6!U((SNP*uD8~pK~P!Ps+vw1t5(B81QS&#@@ zHCVV2AOwlrsgQ`Cj7Tk51SIlph(XzPe(R(Fa5tJufgkHkzqh0MGRq5c`DO}(2(D;LhjhYafxk+%xEngJoK^#V79-|8o1)=dU8((w_je9A;CNw5gMZ##@MnS%$ zaU+GJu|`nh^zU|VI-6BTZuSgzWSRJ~C;Xvv!Jt4F0O0*$(muw8YH{&s_~v+ zvgZ0^&3Mc}Z^FhLrKu7BF7qAg+!{Q&iH|`%6(E;ppoIToSAs+-fGuO7lK+sdq9~Av zw+BH>f>g0UA>?deB96qe6+}NEOD8J?Su(m*uVTcmDlQ^o50NFKUkfQoOVv40I1%#| zpCrtf)ZjF*pTpLq2H+@S?tpM}0U=_Z=WFRheDY|m(!-0?TehuiS0qX7-Z9HK1s=uu zbXIavf$!oB0OSgXaycJNlz4QgOq#OobZZHKY#}eNQL8|$pv0~ zm(;jkb;d$+0j-QALJ;y2A+lW1bU#XM$dtSmZCDMSa z`415mZ3C&Aq^d_yX7=(I9@4Uz_1mn-EA^|Y`d``4-%-?YjG^Rto$Xhi#U-RcN0AyS zgYyxTk-TfwGu0PE*)8(6l6M@RiN;|-vziouYrgkI`|4by@gV14cZEc9{8{rq4q_ zkq-a|T~rqATpudkG{)3D_-cn|h3LHsYg^5GT|>!dMRH6qOGH>NeuVcDadXEv>sMt4 z_B}QA)ajq{6oc@!6o9(-@X?+OF@oUZtnVd0&~!7>2vF2w_RKMh7~yc3W3$KDJpt<+ zwS!Sb&OTPj88~^5=6{|Lgd)NqHdQ{%lrj5NuzA251&mb!A&&?yf);(5{lwAQY|Q?( zIxFf0$gmt}A7Njlq94@l%)yA_UH#MH0q^Ag5%aGYEmYrE z5qUCtFZs%R-u=;k@!9!572ktf)VX;hUMJPx4iI(O{0KmsXHA-qc-op!>%6kHN8U~j zHUPP?UPK=Ov%{#LDO(d7HH`WlBzss`yo8v0%Y3A6Slq~A?8?r-Z{0cH-;lt{!r$;V zAZI+YjSuiNHOknn^M@c&AH{Tr{s(+eD=&2B*nsaGHb# z-{H5^LPm+}tHKQ*$9w$d7H1l-@|jy`5i!5+9sy{<6jZomv!%wUj)hu=oIVyFSH}Y6 z+n6PrUIcek#iQ!_0q4d?R4g4c}}(If-xH zp!9k1&3~qzdg@c2`U3c-?j1*9)Gia6!<<{pkIuYRBo$WgOZuhk>V1~sv!?a;?lorr ztheBgv0%pQibPN`yG~VHHz44>&ST7;$`yuNg5|Lir6XQJ;yTg%>5?EMDWNv;$b0N7 zgts58i^UXkC=v&-uh=Jz&6QVa4ne)F1yq+lPw|zOj(ygR;I8$??4NSC;a4vkhe) ztg*$`b*EX=O3cWk=zI{FwFI8TB->2_X9gO`#{!Mj;QVPfs1D0gD=sVMw+0hJN-~}$ zHEGA2aY-+SKCNq<$I@k2o#r>ELN(ks4E326mt}7l=_hH8taoA-n2WM3M94DGi5m4a z3>W{Zkh#Xy|M|Q#N~()yl>5BX%Bnpx2#IuEdCe&iYla6tCAysue%bVn?kM_vQXdFI z6RuFNs4DYS(AOEp>@wc#tDwK*joHPjd|kO=A4^H%0WQ~R_MT_Qq8SniHpvH7?l5Rg zffVP+YfOipL+;3uIk^ZsTZnY09b;q?oA?qwW$>)X%PNCjo6exu6y$85WlKa8ok4E` zCCQ*iusU~n-02~bB-HX)AbuqpxRtQj{19gHx&c1#D->TzV@3t_+O)ytd|ZAOL6e{U z&ADOz>iJj5UcP83ReX`Gs55{s3ea=F`-;)%1)C)vXY>(k+F%TmjrjLz;|@+w&cET= z!{ne@8|x<)Fzj%0ju68-uWvePezw0O^V37_P3EVk+?yiq*7I{UC7By5?D0q zOQ{Y6_}FJpms|ENwMlxB48(&)`8>o|TjNK`CHBR7PG$`dRKGlA6C}W%FjN^U3SHd3 zQP^sp(vn(HDHf#^i~!0UUIw7RP7x$zhx7Y2OrHI`9L;!|Hu3r_dfq6iUoNDMQAE-v z+8Eci8OOOrZWX(?iqp5GDA}5#D|8s9ON&HvtxQy^j)UM6dleB+U^_tY;Fniz`8>| zeiq4}suO~-VQZMoP;Qlzcq2zW?^~FAS)JVc1~*$d3o3J~I>_~xTwgNiRSgKn7Prde z@+`OCN&Gv{qKpcnXmOFB-?|Cx7ijZah@>T?UM1YXjm1i|ku`rItDiN*Kj?tDr*o4L zZt;8DXIyXj^8JGjnk}8LW1@`7-;66QpT|FFo7vp?nh|b;CY~`AbIDL_c~MZuU%D4N zE5WTvt#=Zu`pxfG`WtTdr_Y~coY9k9{Hf6P=Zr5JteDiU^SeOlHbKx7rx!DEJLImb zzrF^#1uxd=q##3|WYjNYdLnkvW1VP?$tRkz(t&`(H}mpt_8U{P>^J(0{e~Lcx(?L* z>xn+-eW~iUAx&Zh@VyyD6tDyM){FuQQ_HKz1+CFyI#ZQ#-e%d;1Tp%QC}&>I_vYyS zkheIR7c!BD20;n0JLfn+3On)@c7SF%g+G$;!0GwxPM?Y4V#vBd3{I*h`>X;mvd|%( z!93i+v^`NGO~p1*Ax0 zOQeAL+SoE*L%7TzGKp@$X@W~UQ4cWXlZuj%L`+Ddjq=+pe=Fu&i7+$`wqQHpBR%?xm#G0mHKt&P3AF>r6ky=^51^ornKGH6i_7eG7 zI+zyDE2@5Z%g7I~9qm1(vqbP7D^HlXf{-V3XoOi`M2>GNQo&etvd6E0{2mQ2F$bZlZU4v8bQ}L#$ur>un zx%`71Z}#+(U~GCxPW0=Ts+GuG)lUB)=SQVME4VH8Bd1sONi9B4?yO)r5`;c2mF2mA z$gOSe)qrK24I05*?VGKq5TQZ=e|Pxr{{A$6u6w7AH=*1?JQcPsez2>A6(n*%v>jkX zfC0&VUB0<`(1xIQ&(srxm}kb0^qO+AOL5C7EqN{!IhI&MPe8s={{n@|StzsgjmmoJ zl-rJ*j#-oI@^Iym)s+o2Y$cKq59{_WDFe6d+}Q?v9zBp-$ucd}j_}*aLKWd}UBb|E zal6IwDoCYtKkJP8EmajqUV(0-KW zi_LErY4|dp*-N`N$!MVBx-o5qZ8Nt^S53GLsCuHFnVBZ#Zns zyo75C63As6Up_xKdJ@hc@>Ia=L<~^mZ`hN#ie1C`qvmH;(4)482T>mW#>UM3d>B2x z?w_^zbeV{y&};$+&iAqCYN-?qW0z1LG(9bq9<~H;VkbI+suVVoeLaM>5=Il5OE|Zq z#V1cG7D_x?Irwni3Yu4eV0_>twZ^_;->mUvDNURR_t+g>RriFDQnxuD^*J?1_DT>2M5@2- z4yNJIkyX9}tp!!5v!=B>3Jz>e<41ay{*Ek*e!vg`?8ZA z0kg`6)gY?_%>`AbG`v*%M!|tCE`C3OgS-w{-|9xl)JUf z2csV(2dp|2aQZPObI#BClhIzz*K5Y1Do_CGtn8hH&SPf1$M@;<%>tpEct->19GU~5 z8ZvuV?vW?t#LTrudH_oPTG#X+)V|iqZRgxHef^E1kwwOFzM@$p%Nu-v+BJaa+rV%M zbSDtn&RZyI0PUXw(43JyLZzZ0CUtG=e*UD-8{$QZ^3ZRg;3v$x^)HxrkoWmKkm)FU zm`|pO&ZgDav`Ch0dw! z4WjD9zMQm3n0WTHo6i7Ntxe9^Al8dWMKA!J(C!>+f+{Xju|kX@D_8=LAq<&{8bW=s zR1{egQ}wzcR<7%BP1Wm)*icF+>?J4?=Dd#DN0ZBkK#=wswBmU z>EgI_F_f9{yI}oNnq|@d4@*WV5 z)R*@YC4A5Og-(>ay+kLIpN}1u&cJ&s&<5S=rq~{BjlbNZxMVgK6FT28S3?>?QA(C+`l`g~qb! z8F!^J)LLJbTo5VCefcNl6l(@&w-fAxV-dw&PLo|7wYI+|WDQ}s!D{d;ehxY*~@==GNw z?cYJK$9U}1==ICGL`aiKuh+?oTzWkqPb0}VB(~om;_Kp{L9b8g^$(}l^Pd0m^!mp4 z|GDV(g?!WhLwfz%9=rb^qSrs%nFQ}7y?)QGRL%d=>;KQ_^*?=tb>vgpK|2@5a z;_A;!uZKT8<-d_$KV>)DU;LNM22PPc0^`5bN7AiS-w!w1wjJTZKTXS}Fv8>`h7CKFYr6UD{)rv~#F0 z@BcJ#JCOJ9Vrd@8`|})rl$oHjSNxH2Omb;`ifs|Ay1=8&s{SH(tqE{0y;^m&m6j3x z39v#SPi3EhG)9~MousY*)FsA$Tq@f?bq$BEuCe@6*BJh(>oWHmvp;o>7*^fK=ucfE ztyR~U{Hbd!{?s)Ff9g8R-tQjxN>b1B@%-O;Fx32JNvQCz!PZehZV)(clrWz1E3z*6 z6_J$uirh$kMP4Dla@xpmk^F|u$>aRyy;uU?(iF0kiz^OV*L%=sRC!3Z>w)qlzC(q0 zju2;|)A&|Fl>9Q2zbi-I!`#>K z&e_7Qp^C}k-$jOD;kL3WdI_n8a7&Tmd8$Z+%vI46RV1*%s^~z9Ztfv8$v=a0ljK9m zL_?c{r7vcMP9y=$+Y&nQchH@BV@2B?XVED+&*tWL(+AWYDx_+W z{89R=mmorw{z}5?qJKgDciC}rQ}kEMd(vNdl>TbDY!;qX`m2+8mMkiw8&dkKTTxgc z+_cyeY5i5uo?5Jeuw>6aN}XQ*L3>iVDavbj0`?VF=5!5tgq(0w!;1t)s?tMyr6F2W$^DUf##IUXala3_6n~uyN zZ=7~xc4P)T3sc$6jG3nhoVPAlyJ!6y!P3n}{4a>AvgZra#9-{;aFs7QE8kh-H`+=l z%dxMg_^}_OZ*D`{(9YDs#Rp`%g)eT3;A}14Js~Xf>))g@Vn>VBc1h#cP;3V25(oKe zXZUlXUkTYWh7KX#202ew$XQ)g6tbt4hy=r#$|(pu3HdD^N=bbonpp{^mf^pAFzfrm z)^zm21=whaer;=XHVz9-9TQUeQ=sTHvG@Nm-3z=)-=}7re_uo&i6eLP_w1E6-ht+Y zL9cjOQ28d1O@?Z6x$2Wv(&GP9Ew(yi_R8lSZSRO2J=zp9KPHhif*m!*-7*wn4p1Ty zwqy?O3t90ZuAZ%Awx!~SR1`}^ty}ReDoUiH$*l-dQ7#n#u6pE~R1B7iO1I)1DnyQu z3OGKHib|0L4<=yBxNH6!lRf4Sbw4&UXI=;yQrSwIgiNBuL5B60# z)j(T@PF8Kc9ztw8*1q{-&&b;9i;E=mEdRuobr>2t5RFXmgpIf{H91|YG~p*c(kXX= zzao+mHUGWupQ43s+Qc7+)6NY#>sjg3D`jfO+Eo`9kwkH&ps3oKjfmWWg=;(Vf~9*y zrmpqXUY=zx`xW}s=CWm!tVo;bu`mb6Pm!2g+E$jsh+vv^ABx^^+C=q2iK8^!#2^2m z59Gb#9&F4!UY&iddK2#omXdR+jeVWO2X1^7^1aJrvi8Y%vx(p6W3C(I$tTC~$`Mh@ zYghqw+m92F&!)~h^yaHoES!&qG`aF%G!|7uNOHQONaOO|ExqInOs%KfAE54QWWGBg zQqPuNxjqu3is`k8a_&=#Db8-Jt_^?hd9Rk_5S_(mqVlv5YZNGZLf?}9Jem2JUp>~J z$MA0L{XL$l>J!dQpi;LyzVi(8MM7YO*ZL|>oG}a=2j6*^#U-n*&7V<@&4ceeTu-&t zg0EtXXT|_B!-Zli`!C21>Xo>6wwtXq(+)WkUX$~qWZ1!01iJ`aM|wo=N(kLz3?A@x zU=dnJiUNjPCHmHVgOj!p{FdumL*+2$GANh)R?2VKyl-OIoO)Bx{LZb^-7R%NbLw;w z<9b+Zl}RS(-CK1jlm_@v^6lnCF9=#c26f>GBMzpPm!cc51Cj#!g5zi$-Opq4i!04Y z#MGn5EtSy=JUsi1Lbf+Ad zRdM~Fg+LYsZ&1*Hpi7M!du-(T&}CJEfw3J zEw$R>t70n{QDg&}4QL@~i>R%D__)hbiSkC9{eQn_?rwHNu=x1(_xS(v`E2gox$`)4 z=FFKhXU?3#{+iPC1|WH@P#>gM1UjGgsUp@g65ir?u| z*M0fm+6>oy6(m-rT;O|&+9phY*kqh1WetW(tz7Q>QvR9`==1xjEt*)xIs$FE6rq@ZdX;mM|+o+pk zuOVO)jUFCW2muUs6`MUTBN_x&v;j}}@$48l0slJ$Juk7xJQeo{DFtqbAB*`>2l6rJ zjwsivOe9AK;|P{@=AQ_`w%E~#O5?w!@DF^_fcH>@bXMSGJNcJ?g!=Bi*e(>0T&?39 zMgvpnC#>}7sOb7D(cnXi+^o{|Dn^}G;Uh@#a%uee7BN#M6k zSBZ3`<{iMM+`AOGKUT?v>Fw02rpZDeVO@^IZ9dV5NW6bK8q|&PXCi4b_9YTNF%-BR zQdASH9bN!-Aa3L&Ua$PZF@1X)03PFSaI*?lie81Vg5fY2Qc9k+t)BRdrJRokPEGb^ z;6xa2v@;0e9kCORja)_k*LcRO0WqoeuPFT>ey0HR%kVoaziIVR2kdLrPoNcka}-?i zV*RihFBp7zvawZJ@8r(DSUSPN0v3Qs@kgLOziP#F;sA}4{ z>f!&82#SU{m`t0g>Pd*_(q$%AF?hY2$;_Ep$Kd5szTg13Ub)&;+2Wq^lw5bKRb3~E z?smnB5P)Gt@I(xY!mL1l9(N``0s9M{G9VHghVOU`Ru87e+d1lSIv$*>N`UWHmZMc| zKzm1;{$ereLhEQTjs3ksw$R1kbMk$eZi z0b7(Ok|OJGA`4yUfFyX7aTTV-p}BGQfJpp8{4Rh!juXG^M8s%FzjqJ?*q0*o{Jj*A z-NAt!_AVZ5e3pU7Vh&63RbB>PmBl~uQ(x*rOqh@a`+ChQT%Pb0`?{fN-xo7WW4bFg z2$0i!_Lsd{Ie*J25@Jw9X>H)j;C05z; zgC7U0a6geY?`0H-_4@`tjy!<{!ax28uyV4m@I+hX;!aHSFTF=(4Rks-5=1f$Tq{v% zbzRasYy4`!k=rgDlQZGKeWFP?+Q>ULJkw#l(F)7&wmnuW|6ID+dcVEoFKI+B$igL!vF88+hhc9ojLST@WyUtv>@Iha zi@_eo-Tg@AhpAq=BS~B))r|gKHw7ZeDsZR-55xA`7(5-9zwqFhFHxWB-$q$Z0-pmT ze)udUl-oth|4io6&)S_!|N0Jlxkq=$s(2@X#<;?XX&(%t9ek$bQ=A9G8PfmO_m_OR zNrZ&6zV7`cBVWceX#4|i_);^BTHth&`_8dM=KUodUV!df-6Z($zrUoP-85*HaG3e_ zZ+3sl&FH^$XHsH`@LzX-iS114U*2DG>f6$-`MAFX=U&C72@DKE5Z7u_{-n+Wql9^u zQOy%>@uSpumsHA3-&*t{`}jj~s{_kCI%(wI^YlXP~Y_6iB;d-jucHB^>F| zw6$ljcWS)}Ve9eX7tncB3S#QIb5ywNJ-DT- zCjR4!6*mKCs+2jZlnID1#nX-TEQV{mPsQwzFt)1kD!~jU7$35(U`;T+gB+RXjGqE^ zXDuapy`*%ZRyBZWal{-k#jxKrJ56k`7tQ{BteT-E#JbpYa*b;Y$J;S>+Xw8mravvR zEnJU^NrOy)srdy<|=8N)oD;^ZV3$c;r(P|0*>({{bt9ZG|sId2T6!92cXgRw4nqB$AH zgO_KQYz*d_rvVXHMp*lLXtZux9nG7j>{1l_pjW|&vcK=K)Ow#j@(=Y{x? z5%EARLV9!_AN)hGMum`!5}-=<&|>wC5B?$9A_=gxB@E?CWW$s!zgYC?ZTTzF>!C>XPhYrn2IjLmJfVaxjo?{` zXQMA%T`Q%*b)vbHc=Csb^y-HBgzHIl83|l>v~+wCSk|5HmXRRA*~7{hyjxt z|Mr44wLd`FLb+Vs6F-^qwXr#4eCg(O(2qf~-VFU%qF#qz{+H9sqwGHKA=Vu3`TWsr z!rS`99`kg^!NdopCcGKX#5V+7@gV*e$M5i&V|BOyq=-LJpdDL|SDw`kAtiksp$?kx z%fdQvGNDO_KMmfu{sp}8KTfK}Wh23(0pzr4MsqxSQmxVKt@%9kfdlbVFZ+|D>P@YB z(^16R>^jVXxex$ov3wE8*zUYK_7ct(pqUd$)ZtG5WO}T`CT2e;Go#0>Cg?E_KG(89 z2Xc#6hVX@ZKEw1Av(c?hQ2RtY6_8Lcj)UG9cYtM!9C5-hf`zg1jKgPWt!4|S2=f;gbaXNSN;Zai&;%t;{-mfmuGJSuCloc#bECS?K%$$Vn4zw z2<;pNj+)hND#NnoBQQ9WA#^sK)tl9%v-ppFW;lWX&xWlDOwi5lh!6sc#5-ZWgVLj9 zE&EF;SP$*5gSvTv(zmMqdONc}dAzd(%G$SaUQzmMi&p&w>XRX&37-Mb4|fq9+CbYF z)T&n@VT?@49wCCv829z;VIr7}bwNaGtOx>Szw z+3$jXr3=&Bv#(8iaMNSSg-~m-J*j@}6Ca)G&)oQE9MQD3kfr4Tzx3gPU$YN|U)W8z z8N(l(=}ot3ty0?j^?EBu&X1`68lGGrAKG8Iu;x4}Z1%)ee{F2jzRS_R^I%QE_TdJV zsmI42${}-8`V?u)nqQ^bB23OBEs~L>&3h7{V-$UYOyKS#lLG@@$(6?o1YEKyf^4gJYS+vXvHjCyKT9QSF5Vl8;C>U!y5qLP*NN3Q$MPvs2iti=& zZ@(>n=J};ORN)!4loNIE5^m<6@9 zt1g>*6Gw2OuW~lE`it=n&#^A-87WEsAw+;MBG$U6l$0qhsI;nkBp%W{=4Y+yPQ2k- zr}i|%y<6vd?CM)@fxd)IOGPqf66-vM1Og!K*>J?U-TLi+9JZBfRV#nW4uQ@nt(C(6 z*5ETB$yzCbRP~xVU@nWp5smu|{_A0E*_fl@rw&9J{2_vQueceHw=41HdL?AP!Ij|X zf>$E;+Yr1J+INS(iIaZj=wkcg&^LqF85-@jZw-Bu7CNJKxq-!o;mtt7SG#ZiQxt23 zWrHu^eW7c8#@HDiV+En`8Pzp-*d0h`M7Yyu8XA~n_SAaW%s%!MGuT3f8gRcxOqx)E z0Ro|7J|gY;Iyp~&GkRrlnd1!}A34=7V71|H=aE2gQR>9559y8r6pG56vAggM0att~ z{ujq@7xq9AC~$)EXt81P=rCaiH; zY5E%XaeRCZzZLkc!EfB|MB;kZR{T=*1 z!tYc3CL^x*zC_|Q{LaJgYW#kP-&Fi&;dko(MB*I$et@4FzefE2g5MVWcHp-h;@La+ z@!E?%X!sZms=ILY(epU#62Wg2-YE%2QQqr>^y&T0_1*vM{hxaB{X_PDUIyX*`}coRuzaokpa0SL9~5a9 z|L6bwzX1Qwu1v!6weZgwx0+hUZ@K?Ain5gKzjY4X4Smd@^-3WlaV_R> zGzEOR(E$CO=;J$wW+?qRc4?sH%iz~-_4C7_FM*;P_nV;h=8K32N*~1kvhaLb|8wVNO|8#(-Dg&PF2@0YhjL%|K6!yNW|(?V zF%T@oO{4SzgSt9cQO{6&=4 z!{V3`zF_1+aew#`FN7cp5#9|yFiyz4^hfi%*PtCqU0Agcu~1L;UG;(s zR5%gtDjrvPRwK_ND$h=9DDuE_&jKXE9V77bzErA;ic37=94?odQI#FET7z5cU{u^? zqN9=<<*B0zaK}w;(QDA7h%Np=ShecmqVsath!q58`N_y;Jt+xRgb=}I7%KfD_;q2F z)lss^XX`WAc_tf@Y=nY1yn+B(O_E@zHJO>rqVQ9wtFi%>y;7fbv?`0Ox1K>bR)G${ z8HdKX4%S9E64*f#mzLa}ylSV88aEgg@3Uo(Oiphl&AV~{8g2V;0szznm2*~Sk~ zF!|Xk`Byffo1NSESchby?uqWNv|JDKKW z0<*|Y6IQrgunL9U0L-Ws2#yR^Kz7FTlT~_F)IC+v)v1d5wW;XWC{F?NgH%OQ=s;xV zu4@2KYbO3d>;xHHhY*i$go_G0p~>N~i?~g`>p^0nQ}%Pjf_U!&PS*V%Z)VSPAHxG_ ze94@%7g_6J$H=(xIDQ~lNmzBIpTRFr)LMdP#(rEmw4HO z6DvVhS<6?=TPh1Gf0={CVtltCY$Dy+u1+9L)Q>j!+MwJKp!5ADh+>xeVXrQe+ z+IP`B7+g%9A$pMMG2cXQb(Y^@!Op_2bz`g^NKIDQ7%xdyv?AU&O+=A*|KGkoe*l9}!Sc1%hyN#UW4yle z)BiQ{cFnUSgKr^kkB|b6Sl<4>_3yv5@7?nZyW|M<32HpT=_cmYDw(t+xi}T(Gp{S= z*5~mm<5d|MTW~T?zlU^tF-k8~lv4?L2mOyNuYDD}jN+4#ZqVy6|m0K=U$;o%p z5c}HuPF>qixbG(varRkn!z>QdQS1Djl0V@8v~ouw{ASXjtZh%}i(=ap>z{)iv?^|# zRUU*Yo_;Y`y7A8{UakZr-V6G%b!nFz3PwK_N!501O1FUUxaPwRF4*oH1x&07bZJf~ zcY^W>IUZcB5UbM>DqO)LMcxmq1ze8;l%&RH&2&n$UQVY=x~Ihg zOPb}wDk}p>`GjUIr%ae6J~RtCRI`wyO|$q3KrW%t@5~?oHgP+zz2@>67vgwvozH;N zU98hgw>yE|U>+(eSs-%27%Z_?eMSY9mz=H2X zDU@QjU+~#s{OMNiz#!d)ac!NSgZp00Q>Q<{Ro$!b;(I#IRJm}J&t>(5-~eph_q!Cf z5M9WtcTBg7ea(@~LvcWnS-J^NATJUa)|_wyA>%iFi&g-Kx#>70Smt*!T<6*(EsUpi zxZe2yQMTE7p(Ht6$G4wQM?&Jb0LRogTrtqNQ6nRHCJSQ;$u2sBuz)N@aZ%+{8vz1@Ib%R|Jf- zB~#d`upKnejr0@^;sq!FL33i5)3cK7Jd+)sM4ktxU7p+Q)#_(<^;9Mt!7j;2s0v8$ z1@?(j@>taOhouTf**NHziJ!Lm4NzWiAt;aDLn`;JP$1U!wJe_A2Zn;4u%7+?UK;W#jR#CdvaC8L4;Ni= z;N-03jNrI_3~{&~ZL$B%;38jrO9#&itQs+V;V^lqKYZ$*JuKr9YE9DVQ`}jid&oL( zV5w%=qM_zMQ8uu4U*fg0l08#4t;}P+dkGJ2ATv+>%g=#jWD5)0=@H^sL;BP+6J^qngpS z3gdJsdG;a>pi@A@i}jNIfu10MFZKR79lSxW-1?>NfP%1Wv7E6Lsyak*1~-yl1H7l6 zlllpK|F(@F$A?yP(IfO^7Qa% z5D1jnIPUlX2xP5pTn3$WQ?%4VQ-B4yb}wlR6@9}n~|QEg(l0-NZ%Fk;DFO+s|VB5jfv7A zr|e0R@*?-EIJNtXlYKlmfIl^)PeE5i-A?j~sJkm4@L@FPmoAi6k0A`4jLXF&?nL3O zE&`&~{%27irU+YvM>2@;)I@0RIW*Vz8H$a-{^!BgSXGp#}Z+vd!Oo9~RF*`$m#${gF#xQv6!o7s>RUDp) zW6ytbV-|ab|EwIe3qB-@Q9z+!Jp9rO#QWehrJ|t*^{QlBaH_;_{3(P_aK6hFOib^# zn6gW6l@3Rrq)w^>^K935BN9!u+uBAXx^^xR0{SmZibNyZPgsP6?8BaDL>qZ1sn2Pb z9)F6_dCyKD8y~fPf3Bj(U*p*hJ&unJMCY{1 zci1fk_pwf9j~SaG6^)Pm28Xw7x#;Hz5>Y!ABTveIlkhCab70z=tg@NNDZ(_xvn}6^ zcm}^SvKWyD9Lg`qGsU+|i;^C{0L@Z*q^QthmHh~B0F9J5_2&OX=xik2iQ0*z=huHv8*Sw zWhm+UxNo((0r`-HM&*gM!B66OOmv)$q0fHTI}0|?)xRN&G;{3x&jLp^dlDm@@=xyk zHOCj18MCo~`^H)wx^p`FC5R;U3c{n+k8oD*cLeiLEyNctArN<6SNQeNs{V?J3A>s^ zVj5o)(ZAYFF)>tODO2MfS1h!~sV=ErqS8wB(dvcB zB5*~kYg>agt-+8Tj8-o~=&<{ppICprN5)pf@`Bh&MWRN?qTzrA;CP3z@e8ncjVQ)0iq;TnCN?^kVNn5snd25>Tp)cx9dfl)+ z){9SLbsT3CpwTL-ydUUQ0xq#*IRKp92i9*9j=cum6Pe@uGsqmV4F_rSUXWRA9%e1I zdJ832UdYIuU~Ei>@Bmu>VWM=^c{3QJI#1GIN4Fd6O23P7hUz=y?0X8P277y2<&^Dd zm2;={TL6F_-pl2r+MbpQ)r~-QcQv6xJJ{G7jM%|w^%jI$yK7NW(F4hA5w|i=no!#uGg`+G_fsHDV8Ia^kEe9X3X-9<{V~?_i8UYT zV<#guJ8naPtU=YC~1VmSWfPxlTZX`-j;Jsl8P4uf*Gd z-?G6wt%q5sS+tDgrifYaHjvSZCasMK$Ie#7&n%Nz(FDS^k4 zuBK!b{&lD1N3IVg1?w z^=qV!-@=N2AQdmQkk~E=cSu=(f0R?n0+zdXl_NL~#r0!#m{G1$eJxeCAX#h&#vn`j z$7yPviSyZ{kq`K7we&v*tc*!(BdU#s?5~+OcS{yG>kcnvtaWv33d_f^na+-FR*cAWz6;X>R@58G zNzcnAThZG_s(S_2v8v?7Ou7%@*l850jn%R4>iIQlT7r{|tf^epbbFhc;`j_qNKIkZ z^sFFWE=cH$ceUvK;i-y6nnh=~$^R7c$1%A?tzjxnyi9ealsJebiYqcC_@14>mDbnC zGCMSL>`dtmhk{3XW6vUiO?o4SaI6!!D2679yn-2JLH>Haq7bv_!I~5nWsqp8G{3jg z*c9^^K2Q_!A)Xd7u+l}ow;mw@rD)MMvc$0=2p`tO$?Vxqfd^Q4(jMc&xp*^s{`y`# zkmg0?jIBW#l>WlAL)HZZcPk=@nf|Cvh0LrQNujeCT2WB z5Z+XpznzjSOfdWoJ`jRlY>F$@lB0N+2;-tP8d0W&n^;!XYwaL@3!mcgZTrk-z%$~WBwsYU8bt&7wf00o>Kwq2z9!!Nb2 zP%o4;FSS{rqRX1NFnIKu-dVdm+8x6SwSuxH?A}~lI7z#wv}uwyIdbtNU7NF-g5~!; zuW&a1Dh2aYD421+uT{Y;nvS&POQw7eeR|>e*m;n&)p|<3AG{^`uJl3Xb%oj$Yj$bl z8h2^k%Pjue!GF!fdp#1vT^j7Zl)C5f+xVVRuVmJT^&S14pg?Awo6O3U(q38ZU5(uK zc{N+#k8Xmhe^Cuc`q1^4TAvUh_wWa813Xwts1E5v{6W>wH9CyqMMyX2urwe%QSMG( z%P^r@&tPLGTDqUF(Nb}j5G`%y8xg~-;b3#3T(7Ag5UE=8B0ntWzW{7NfeqG4&l3r~ zU!ys8zp+N|(g;+*xHIVw*DeO_S%jpAwnwe^lPEGf`pvq_E6PnjIY7bNcL``b`uJN5mp}TU+?8~ z6fXMn7Hk}lddT~4oeKV>+%%h$Ex)6M{-ViAhyQ}it*BoJ5;k53$SXGSe8sOE#UhPp zVuPoKdgVrI+%IuZO?zcG=Yd8f-{M#XW11liZsXi=v_L|K#}GcrBA_ahPcXZI!0R5`XkaJjOFRxz_r|g zgbuR2KwDP27#H+)#;rc_PPI^*Ea3wF{IrT@zK4o}$gC|JyO{NKK?N8DPsGuAvY zC)TSAr3ZdJNAG+7$~X0iu{Bg`J~;t33(2PV5v=}Tz1KRmnE#zn%q7X@O* z!Lo$+Y&ZQBN>F^ZUPrWAJdz$h#nsgj&P(SjXu1nD`>cNLL&F(6V(AJR4C|d%oz$|^ zTE+~OuZ~Mw+RHpXd$DyTKE*FqZ%x+G$Q%bRi&{ha*wZtL+`}|yF!uCRY@@ZAu{{{u zJr!GPbzxQLyN4F=8MXGDm^Ocoj~$$dxuA7ERrQQT3K;heHHnc@Dti@s0sWLftQveQ$->aQNkaF8&2r3 z^uy?y(Zi(2o|90SsF>4c@5g>ds1KRF;j7kH5pA8oXG^lS6S&VfDQQxoHVInLoXVt8>vY)FU~9Dwm)8e6K|6+qnl~-s-Nt1`5W{hN z-M7$?0knnIl>kO|CTS=Ii5LAU`ko+0EX2c`!88q&dcV>L;Q_{Ml+A^|isY?gxZyf>n(GF2Fq&cQF_6 zs%ZFVhYbz_AQKCGU`8e%AaaMN)Ct2yNqD~89ZglkzBE}Mf>2HrShIU+?aqrkz_q0!v^~3 z<`o#ZBVD?D0=5(WervquP&g=Giz@?k;~1|&5AMjUB;d<+I9jFnaC~w+=s0U+s&hfR z<0rw@ZEmJKha;BQI4*(Hh`{c;1hgpTT`ot|9G&l2)zMR+`sbi-T;A0f-UFKnsr?>G zZxuV(@3`>D8ake1NPOs#!y8xk&Z6Ty$V!20%fh(Gm(v0q|Ix`_+xj12B)M)Ga?!D0 z@MyhqP*$*y%y(GSln>ZjIv@h^0!k#m@tL*$x>V`G2$rY>BE`i?uk^11WAWDcQqDhO zeDETBs#S3pjK5@8U<{rjbVBFvwC=wV@`xiUo8-ixnr}G>!S`{5D(Mer6C04?@47h- z_=5^#Q*bxR3_c6r@vZBE)hT>khP~cY{T{hzh9F$8g6ZW!2o`u&Vb1!u0c34Nd|Bj6WpU`&ng zBz#HvEzhG?)z}{|8%L%Wd}BG;4H;PZePCd--*5z`=;jdfPDq>KqL<%*jC}?YSkHsM zz`96DO<{0LRnt!EeiH?mMYkXaU+i@KuaDBpgo;Djds%s^q0!P0A#$>*AEui68^V}b zVsDA zOHitPkCLLluQR@Ie^P2@NLxQ~JtOylc#J>$1S;A)%)T7xG$(TT0wEuU-<7Lz)zjYs zKL&9BNY1Ccsa+WKF`@%Hh$|bc$rrOZkP|uco|H-7$sWf9vO9kAq*~}(GU(rLPCzBu zxMFBaa2IL|{t6-zsG>!oGHELlYXq?+^;evEWq4z%QSNAIL{NJfr^SUb>7E74Neaj~ zuWX9H+Dcr|Bfk?rNI)**8il|dvM$NWkx^U0DsE|GvIIAUwM=S z{Om*kQq%FeNMu^A}HouuuIKyO>k>;13`>%s%gqkO^` zt9A?^3>QUT$H2@(JcnE!@e(e2gKwWxcPRH&(PN3FE ziRRk05^uwIzxj9`+TuU+aW=_U|78asu5Z!qeMQPxQI3rx>_2hqz?XPKE=A$@DVO=f zMcq&r4tXAn2kB|~(W+Wdvvn+CHNz}&e%FWP0OwmD6O3*1pizQsNW74{#2|seR0AV- z%1VI0mf+>s<(N{f)qo^^__ziLD3ZX=_kEdGWG#wB)`yst{FBxEgvEx7Y8aPP_RV3O zflHO5E_7f48w~&kf`MQwk+0|wt-4z$;&EwzyrW}cUGW-~W8x?op=`vf5Ea8Jv>!Vd zDzrNlYPLh@oa|Z0qd+Ft!wVQs25*44ZMUH>m=->!M)R8RBMO zxO$xW#s|Nl-tttk38`e^$toEicCwpO$!0KwVT8Ojt=~h*pndp0Y4WTrTa4<(_>LS) z>}1jEI)q3i(dw5|aS=PNq54(E!HT*OPhWV16Pi({FU-})+Q1O_$F${b@rQ%^r{;j0 z%KcLZQ~prZeHN|$G?9~jHQ(?Wk9&pU6}iEIAF9xe1+{pHTI3NTPL+>6T7+LDnEkOb zTF}I#iR`uD&!=Nsl?lnkPpq5)hRNk|j$T>JOCz*p5xuh6;d6+RtsADL&C4 zupqmXW`;MR6v|Tl>$R%ivrp;|bkr*kI22dVMr`26>Nh!BvjVf}9{p{-@{=zW`RrT+ zkIm3NGlL9WT^sR^Ub!vxIfMUB((c%R-=5`hsZ9`!IvSGN9oxbMmaa-*UuJXHS(Kf(~j!D%~nr*k9`l5kj>y?OA$sfx++H1IT8HnID>xjpwfpw49pR$)UqwA6s$`pQ5&*7Hz`1v+Il@C?YTG|kW%?xG`VfvXQ%3(%mQJ4hR(FM zl}p`>O4dzh)2#bw)IEb?GZEU;Dd+-lU*FAb3sq^__kBV3@!QXU}7 zQw7kX;#T0I*10Nfe%pdhV7&2&OO+rGl&}rcEUMkxm}(v@au`>+f>mOFZUcd-11j#} z56y9+glKgx{!0WESt0)LsfT>(gZ~ndh7h+K{lJ_FT*&)4(|KWvgojzpe4(&RYGfS+`A-O|4 z9buot>^)e|4nLi`;i3zOB1&vYqTP++rt)e&_I4Z&`f zBvwR4)=Cs{EE{Uim|Vl6(c2J@R$0K|^5gN@nt!*%-wtMu_$J1a^sOm~k7nl!6yXI- zuZP3@427}hMOn$IlDWKb6tEDtxHwfQ^oJckVdvesY?HO7uzaS zfeX(&_`~_`Rv4HaFfjF@FnnyyK3!noXG@j8f;p@%tSiM;#c5W!=qLqI4H76cRI=u9 z(If@YWX1~`Ze@J^whnr@&(*D-LCV2sUCoh-4@|5QVGKYo z8MW?KHH~IXF4ly&`%w#;crD|4r`0~quKiUM)G3N-7ln0NFHgl2o&ppoSV>7JsIMx# zFx3pi4PfD>y2Gg&mh>Xu`qhdx&_6k8<2+^_Y`gcT&SUPE^Oz#lTMJpNDVhYXPnanW zGZYVTGE>__oU-z@CL&WxMPWci0hJ3C8EG4e7!Yxd|BkZ7ZjO)?sDYv*0`7|$P}yb{ zkW1i>7|`V?0Lh4plW4q4V#Yo=d&A&zG#N2 zrdSFB0P75Vh@DMP@U55n7NcZ?G~ywlwX9fYB$KR|RX?AGe0#{_kZ?IdG@-6+~@Wi8PILC%L}usLLX;?RQ;#dZsl?TY;Y zpP|KVWJ!lhwLji6d+TGIDvk!)EbAnIjaxT>XS8@bZs{p2**#^IuQK7B(ud;QQTR^K zVl&aAwERe6H=e0Jgy~j{iy)qFs4kbr!_@~{UKYmv=c1e~3*VNX8^2W1M^YaP+f(W- zome{9+Ss0IKNSm!Bj3-A2cPwsfqabog!QN&EsA>c^N6i7<2KZeH)xH3XI?V#HyBD| zl=#db2rpqxMRL;enfqkaGU!5(T@t$u10uei%?J8-+vs0sliF>&i%HN<kN**4R>z z*wc3&N@7hyV&I54NrBJt`$$`gt_-}#?h@33uDF_m;TfF0xCOiZFix|Xu?iW_@IG<)akW3LVO)LLIPY>c#jB9BjsVn$BZ9K&`P_CpQ zCgzMoX{eiMBJAyOjeCn3+u!jZwzsAUwFE|zT87)i@?+4-a78Man^Ljg1DCPCfz`&| zmT87qRRnY%N)W=T{4rXTbVZDsS_&)1$2>8D6 zJU6BSpZV)@JoL&h+*35a5v;?|f=VFPm*ZGnIs6J~v(_KJp#yt*;w!m;#zcte2>*!rytUH=zNWcYeWkVjT zN3BzSK?q{ML2?ELyjMElA2IrmSM-<*2F7E(=83S%3(mpipEvr#-h9aJJ^oA>fy27i z`oj}ClvV#Duv|Aaz&rhq{^~7(qu?SSFMb;5Ctm(qxrXqqk%>&l4$g*8{kMMFRMzpf z^~FBNUw!8ITy9`ht|ELFO?xH--vB|Y{vo-733pP_d2Qy^Ey^6Cb-n`w|HNkCZ(#oX zBsG6-mHG3-wD}WzsGL7}F=uK5&G&~fm!{2sGJob+n_-5=qolUHAU<8d-@b%7)a=OA z&Io~xlVDaMVT@d^410mqKNfa2LOCgjmNo)=5aoi6M4|NqEOPqDEFen!c&AkZt|HA%phTvtWz}dGRxNH?ZAJ3_2=+~L^?UGR$K`khCZs+ zb3SN~L|&cpQ0!5ZMAGTII{+eAwb7tzgQ>teCKV zjzpxN80nkxE5cg!RYEh;7#Yyw2r9YnzB}Rp!K<-T+K`WJ z|KJbbE%bAP&%C(MSF%2XcysHaKFena9H}eaUJ-}p~Ula zIzMlvpYU8Y5`UFG5(Sk7ZBUt>KZ#F_$NSZI%tWagj|bDnczAq1Ch)a~(sssUtDOr6P-=Xn=+~B)3NYcP-R8)Z-aF#IUlpGFy!`eP$LqZw#I z&wl9(SLHihKoT}Sd|06{%o}gP>h48T8I_Bu7u|S4Y;ZDT@)#4cW3FY4i!l*9#?P1n z#-M?c>jK6UGRAGk6fmZkF(EtVc*eLH6Hzf*)qM=K_Pb%<&Hw_~Xy49o;yA&|S-Bp^ zpX2A${!1*zuTbf63;b)IihpKdhSjQ0MKxX{&I`I>e%#0D3C-%0n^l*EzW|(of(GI7 z5!Yn<2I;vl$}k+hO1IOAGh3Ly;mtnSWR1MAH<#Fbaz)DX*2Ey_nPqQOtqJr65!J^! z`?^%Up`&eaG5AW2H+^4|{k@<1?g?F#73}7dJ5XVPHSQ)HH|`2Jtg8?LO-9V445v9V z+t9Pkkxm0J8J#^1IUrFsCxM3eA$(82+-q3%R%eu5x%DEhvVXQ)IvFIY-P;`mlR>7M zSX*#DvFS5sdQWCy$HVYI=ri^kE@Qb`h`L3B|9^}w`nY0zrUmYB8M3MG$<3Mp3CXlSD)zME7|PVhJI0I2A#hCFi0NTSO%}p^XG5W|Kv!v1^|!13o8w011=~n%+iQ%W{otmw_O~* zahnr3fGiI=fhU@+*Kv&BXN-rA@+Ro;=-Rh57ZnGN2QI*@;72UUtYT}4tc!PjA?fREnohgrwk6j9S zQtNBdqt2e4QGlzJ3bJs^FQy;jE;M6L)?NK{Q{Pf~m(^j`2@M{nW4>O9bAX*2prn1y z5vuP{qZjJDo(Wsu(G%#-wOxN_h)m7Tne<<=gN`I|nYBp7UjpmTUxFcJB8pTtEZ2|C z8S_?b%6#VU9|M(ZmCpb)pV<|jeh+k*a*Qubvv1sh3S`Cyz)v?s93=3}_vbsS@Ny4R zr=@9G z0mG_Ci_ho*84EuHVN)UpH|YJ});H|b9XQ4?7+)X)dy&s@%&!tyS|T+npobR_cwd*d zJT=2Qwydssg!t5!t*+efta(?vw-|3VILdYaC-b(qJ3r3bjRwUepNHF%hLf#O!(VdIW|9}V>+^iXh# zZg}_#B#m$(?DEXNqOfLkA>0}iK+mWfcVZbZqCoH8j0(Yg?kvFSFpvv%^S-^%Nk%+b zBMRq_F7y_Bj!e3Fm*PzT9rmhZP3sa(^95TiuPdy1=RZT5^t8O@34N5~t&umNF7_@O z0)51M!JD~)2-l4j90zV~+1th!z#sxG+Op?+3Zb-2&DX;NFGZ*B^_@9pFNkE!-dr5_ zHlJZ05}52C)66%V?WSi8Gq4Kk#=)!N$EU6LMnw}W*vg`HMSlK!GIW^J>0s#|Rj#eJ@{v)aZiW=Fe zTQBE%R;}vK*q-6HpW~Zqdd)j6Yxb_r3Ak$B4SkTa_uV%0SIg@xzhzZ$ueNLrM%R=R zBteQFW*6t@gjVkl?o)$4OF+L4dZ zY)c_Tl(g$H;3toC2@f4q+N4$g2U}6`D*0^d#LL6>#L!3-67PZ+)#*5WeRF23dM8_z z)K7i0_HdG-9TcZB!2Z@1YToLPC~GMGWzG-cA3=tc6@&?x8cf|TxDc(Xq?aNYP?&%Z z9Z+uCl^OMF1a->tFb8LFj3=Hc!Z}<%&C7E2{!N^%9Zj6aIi|GSVR>42Pu~1tg*6^f z)nyn?+cD5EhTxDFsp;~_9!_0T!zpW6yMqWK4IGK4{JE&}bvWY0)3sXF@6aQd*SMc9 zH`AAya|$RQ71v=5CFtE!dA!6(7P$daQrcnm5ptNltNNyV=AnSRZ&d)h=OR$J9;GE$ zR&jy-O5FeeE*J_+Q# z)Tcbq0gmB|cwVzf)(V8;MRXwAYiYEt=6swfFI4U*)(vbY7_;lde@i_qB|GW^=&e1X zn;#X(!8O91#{2tr_!u$l-W9*$PFTL-%m zC4glg^$GC#Oa_wTAAa*f>@T~g`~U?sir};>7T~6^!zY{ z0qJN}e?hSTORT1zCt-1oyKr$NUD|sL5{XiwwDEh|sG` z!5k+5)!@rq9i;S`*!s96Jn;O$KA5)n`hn>*w})t7f8_G6cS0>b4d`ynl?@4qcpbH> zqfl!6lqCNl-D3$BjUYC`KAq=8fAV$~_6T6iXg>fJ4H7^Oc0nST^G4!7!tq%D zfX^7#tJejt2GGg!{ndNZ;DtMy)OL+Bt9|PO_l8gWaY=6tm{ zjfF#iu+xEVKs#d>H|zbp8=Tf#uw`!VqgKzaOVwBFnYO7gLi#zZpCdXQ{zJwe&TFDg zSk&#;ZIc3@wqm{7Px8F=ktcz{bo=76e*gT+F;!*;8gbCG&!rH~-v@&(jX;=tKi3zY z^}ZTU=h)g*YY0yI2myku>H;T_*f75J$`X*qKS+_r?8Z1sfCWrvB$Y9R@5cBgSm1(7 z^x`I?c5jUkZz;y^ZCMT<@0~4+h<*+NK7BMOKH~2e^Oqb5UVt78VuYj2r-2XDiBbK1 zFfO_8q^t=N1u|h{|H|BW_sssLPj(Z29=^C}`eX6qF^ti$lL$0pege=Nuz28A$>+bfdx7i#~@Df2J3`}30HO>7jtj(-QRGMed8jHsnCZ*&-CZ*a)( zBPjD$nR!+|_RBDGZdzDY@{v~cK4vwDMGs%(vc-YY+7y5Cg@Z1ZwVa#Nm9q;mKffqc zRk~;B}|W_-=o(AV{Dz2blyeZh&b>F2F< zqYQ2!ZH;l+3whCF28%uB*ufsN%|dM79I0Bd**SW~O;1q>>DNr3oD#;5@Cm5fmV;wu@$Kr6nU1HOF5 zD(F?_U_)5&bdqtPa%;}MchG*~=XjrStgmvF!cTp0N9F#UDVN&)2QkQJK;#*Y;WiZb z8IA?4afwq%1dX%sZSxPnx+wUkV z`8bIE&V*Hr57fBcKY{{itG^EAuRv|w(%g(fZ_s1rc+5+(Jvd1l?*f(rc@^^1pkc0A zdH0+@XjJ~J?)RKK=(2*@JzD7<*2dhg-4s8ozoiN< zN=?mUBe$@kr2dz=fvT-u0uKG}>qn~p)zkaqJJJ7M@AyXgU(S`{0%SLK0beL#kDd>~ z4ZFi5a5s*Ip_Of{gS1`JJoR|p{DIEv$$IHHz7ADg0SGPbgVw1Kf!KOdH`mm(J8;j3 zt%Ul33onwo97&j#+1;|vU4w*C9ybOC?eB7Cm+FxRI{^EK0&+9%A)pRX$&X7}yKbHj z&>Yiov&P+&;7(8#kHZ^O#S#gHIHpg* zpXUYWjrT%2dhe_xxr@gR%1h^@V)BqVj^pZX%*QD>pa5Mj`r;4JB=3AV{A`8^lBkUp zKTOjX6E5nDmm#^1Wp4c^;GF&;S)McfL$aYW9^%w0e}Qk%84rQ&@R%XltvX|tsx5=Z zwI&?-$}Tgn8G??vW(c_BHJzMg_y@s<{|tQan6uPtcK^*Ab~-kD48DuHxDzr^T})6! zUCaRV{VZPZ@_EhBj-HmNw`K(~iRMM2F!t6Q)b2eW?+EZ5gRL+=0ViactJK)%so$CF zt!a*3kEyWM#?O4+Dcd6{jWOm>YK-6a&VRWtD;4z?Y3!#%lNv6i+fS;CQPUy%;_H!C z7AxDb{f@ZOa;f7_)Nn!L!c754N22Hw?;N@b+2B&fx)7V^nLj39sfz=+ur;YK_99G$ zF`u@It9wet<;lW<2T>UxJgmytJ6~a}Wxb6%n;JYV4W7`)&>9PE!~>Y$=L^l+$OA$) z+6aVpl&WH(k#$ph!SNl5qbG@@CyAq{bIJhHpSBF1JU&MAKgH=R4l--x@mQg?)?Cx9 z6uRsjPyMGDEs4OLu;|ZkwEB3I5|;<67=4uuOw9po+E702i#}FL+?0HZl5GXO1wfo=|juaIYGFP~ZwNnVq%|#PGpfDq2|D2SN)=ZkM)%_YVp}Lu(+C_I<(?S`JAS zyQOFEtodP*B=(oCn7@GKyKB~gdF`#w3G}XcJ+v)n?|Vu8_L|VD9JHZjb#OQI?|Um- za;Bu~&qJ$<@ikPxKe#6euoTGY;}@{GvHpaAY4SPe@5{=u;#2zAk1k45Yquv0(7u16 zk6nV~t@>E~4CWAhEYz?dQv5eB>)O9*gXL&~9@g;Sb~rA_vAQ;~k~A*^(JI3IPSvsy zxKjPsR=-gIO+u4-46M3vB^&k|t3F5Xq@~9iu*q-s_nUV)je43PnfLu!R!vl>?@|U& zH=gX&wcplHy=>(|7euqq+#t}S9O>x`M)Vrx%(F0ULI-kkE>fJFn-%Qg zGY7+?Y9av5=xXapWQDL*W}KDZkmZP9IjI&bsISv&7MOarQT8%6glKfe{5o_fb{WSc z%jjg?f&!Ge)2#@3jRLdmWm-Cw)rqB3c0QGL4PZ@)25$*Hxio9ly+B_O@6wHmWV>Le zbt1~3ym9AX3dbQ}CZ(UH$2{N;Yx7PO=O(+kW1SmGGUeN-wP;TzT#zjD$-|sW^Etf9!<<{J-sEAyFz1d_Z}KqbPEc?1 zFz4Q^-sEA+FGN&s0S35#`s2_yTF=0T1u@g zFj>0E!6&@S!FCS&XJfZmf7#y)V=etoUC+ualr%In7`{EVk>;#H`_)otI0pR4sCSSc zd}u@VAQvFQQHV ziSqna?H2!EB+nn+FG=o6dH$BxnE#XK|I_68)#oQAq<<~XKZvydC(pN$=XXGVcx3Xt zzv$m7&-d<+0rg$T^GBnB{}y?E?Rk*rx8Yx#`8!RX?}@{=wtdCBeaJAr+P-4T_l00> z`-;52NfzVy-zCpOPV%%X&risLJl_vwcUXD;!mpI)-vzqAl|2723Q+R=FA@5-^86^2 zL3!ghg_-iax}OTdys`&T*HSwhhINJ8)H(3Wc+*PikgYsq$FW`A#Qw~k&@oKL4=Xn+ zAYij*VtdBGv0B{xklmc%-bCty3m;r~$AY0825PAUK77#e>lmX=;mWK$RPv0F98|E! z40sdz12fRwX#{dn+Q9C*F)$bJJ@5`wl|XkKBnjkpMZkq8$`5o01cBT<1kS?qEXL$B zCLe)fJc}7q$e2O|ym)#UGng@h5h%yAoH06M7`O({YZw^Am@zP#n^19(aB9^zVASkjHeqJ^Gg^-u z!OVU81u)`%`V?0d{5zl31ILY2#?&FAdStFGHU2?E0x113MLuGIeA}m88v^gCY(&xU<&@NU=a)JLS=gLpwnjnLrem^JF=ygz4xR zoGN2_b!ulz#(601+1Ft>Z~@`(+IsxSu3WUTwZlS#FT{FllsW4{d%d+0gKbpQd!eki zhBahgXs@^0?5OyRSJ|$`Nc~b407&lFh6bO6@}ST<$u4g$Qea>CBqO|7a;{9tE@K;9$B19EpvvHNgQA#EHZ; z!P6v&QNJd5B7)(OuFC!HDQkG7-+{gLf1*Q>%Oz-BtM7YP@3$6*&unTK@~eJA4f!W{ z9IvV@$wPTntC17^Q)+xj7XYpFa2!z48BnCV%V<4bXxBTh8o9sXkl_=Vgm6adde^3h zi&Q~2J)kN7f*$fUwm=;yv|&im!#Ro`&awNi;T)ldbCUFM4(Q=E^xq-$5PgX_l26&wy@qT?2IESjJ}9}%#z$JsUV}IB@O_#4$YY6Ka#SvpGIiV7D78Q?(Qzk z3Uoo?-DUqcbhnqo1Xeu*BCxZ^hCOII!(ImVh&<7b`ybl+WA-`L0=IO@{6twvllC)o zORK(x_B)s_c!d}&l6WT%?sw3tJ_8J%6?7XduH|c!_Btov9vLt0c>5Siofsjt;g$P0 zrkcZC9-P5|e)Qr-kI^;X9F`AleLfYYj6euhvy5@3Vxa7Gc^#HHD&Omf!MxV%*zSRA zqpl?lT3G|Hd+JJ4qFkgelM-_I+5^qNO`yh@d`$2#)CDHm`1k*s_Cs3riAuYfzIokN zzm?*T&}kMMV;@sk^O!%UC4^^CkNjD~J?8ygeh*MZJ3Q=jYDK?WDqJ7k2)Uh9|&b3qn^oN&9p-i^i0*j?rK-a{gOh!XE7KU?UH4 z=aR)fyR0(&fYf3`R^uu zd1_+To`t6xp8xLI2de;3!r%@b2V5rc{5Q{a@%*=VDA%fP!@$MG8IgDwp;z^Nr*&hZ z7Z~h;`dEjGt&v3HBCpX>@iEOSKtz=vXgGK7HN~LBYt|5RL&Jj|o^YS%yk;#?R~qhv zd%v>tOQPDvQIC-ga`J+lR%;ipCT(CZ2&naxtoGOu&Y@wi<04Pk{~Z2}IrgF?Dfyc{ z28fEuNlN~D=1!54e-&SQcoI=GC!v9;=h2WKR|d(^UKrh|qrH0t{c^Oo56LAO#s7r< z)+c(ugTd1?x5F@~w2o3+E)bgZ53txQeGGK8KYnbc z{J`|Iw;Kq#O=P>6UcWUvG|QO_c^`i%e*waM-rd(LDb-1D)ktr(If&irE1-=$*SA%k z`zHPYy4y|V$##OBY-06b5Ci*X30sV5I^8O_?S4`Q|DX6ilUC^aMre13V6#T~$m4`&GhJP=Z#)dpLxtWGDH_ z#Rthx4%TnSk$a5GV(^nn>?E5t#35LTU@zQfgC{%~+hMgtA9%>;!b23-Nb(THQQG4n zp0IaA4tR(Y|Hga{Q!X_feH|VGxV3pd$pT1+@DO$lR~g#9+@{XtA*ZD5qmt_l#Xs1A zVCP51KkhtUVKtq95Sw!QgW?}}1+6$yD*BC&R$9r$TL!JzdU&$#b2Z+55r&3i1yZtY5XO}=KoB2r2Y3?57p9i{v!R?%3tvMmHg$~ z`g8bQXBTj(Lst=Ah)G9wS>Zz-^h*1rhJFyp5Ao;l!Er8()x-I7xB+?mX3Ca(|3-E=DE}huMpTk;7kn-p7gi5~%c0S_I;S|ZH{5f<-_$!?uG@r}_JFQw~ zGK-$c0m2|VW5!=J-M z8<>vBq5L_NspMbTgtP5je`cL#(RTKES`%(&v<=Ep1m!slF!5Zl)A|LzsWVz1KAQCB z@MN3(1x#}?w<{xd8rz@4(vwh_XvOHCLbde?Bu@Ers8#8S-T|qKW~M6gw5ez%%2U8x zk*Y`#I0l)iEBP6o)-UmIC5B;^bq_*Z9)yd^uW$9|aNMV$9gtBmG^ z-I%bh7*`q|nd3LFD=&iqZ~YFJ*F2}RBtEO3chIElStoi2UGALK(_1p9>nyEgcv4G1kypKHRUaVWOLGwPy1wr{z2ECT?b-MB`Zy=^gPO3dfxSu{ z4W5b?vURQMDP)7if7NI_!GfRn8{=_)ZEOTbg&I9C0o2dd#%p{o(|&Wk)v5-#qjplH1$H7Zu6P_<)h)Tjgk5TZD{wAdz4)C7YcsSf!;^PU*3>wAHV)6-5Qj0471GT;*yNP;rlA<)U&` z^8Mbm_T0inZO{2m|M>DenZ5SfYp=bo?|ScVQNY@3q7CocY7F1%_igtZ!?y=~iGVRY zVfr?iMr0EK0f}d8`dYYY@%vWzjmQd#o`9QJT4PMN`6x3Nioa79Dmf6a_L-{syKwWcazLePY*Ex`j@ z!sT0X2&tEaBgk4>e~KQa&Vl@msbPwpM|Uy6S(Lv%h;f~nkBwcV2~0YFUp^i^wwDD< z$XsDs+XB|RSf{o6?Yo{r8U@dq^UjwK#Q}tK)xMkCoiE>01=8nBNSB|NI$xHl*Yx@FC(uf90j;3&zD=h zGT!+ziU;k_mymp5kt{{dqduv|?{?QpCF(<8NPlQY8}pjg&|A`F4m{!wl0LLPQuqt2J*>hMaPFAsb|0lUZULtHpZN>XofzRbjLisYT! zBYnQy%jc#2m~-@`w`?PA2G=sUOaOA&!GmE(9?DQ(I+oT ze7MQ*qEvi1ylD7HrumWAQ5@0S%h3;cO|JOI8;o>o*oYS(UDZH7`1)C5CwI{?!r`(ZO753Xx%0h5Mcw9A51eD9)LvaUv@jr?`RT#Kyft?E5*& zTg+lbV~F~wvQGJydCOS*6}^$UOWe5~#kK77CN;j=z6S+HlunW8@nxf4C=I_eIbWDX zz&&I7dNMH3A^dk1!>A=_4>$^WuzDh->XfqN-63t999q9UT;3Ax)wY!5CfG(G9c>@9 z277UwtV~yR;nai!Mn+QmD?G%B5-dNIbE2%(p82{>gMbs(+^LFRG^I@VMKVG|&8O)< z183Bq!hO)0($jq?sn~I8-ilHG0X@LRx>$zIsDE4C4Z9=p5(#h71RcB?fVzaNH?;G2 z0>iE4G0_+cFuWyzWz>A059FECrmCe>^_}Ets8aI;3h@ab^=p0@2-b*_}nI#gAd{?0SXtMBu?EX`M&r0Ov#cRu9;_Kz4%<-4%zfkYi; za{6i1-@~~rh4*batNo*!c$B;(dl;&e&2|e#Q@PK}!j}=M0+Fj(gB8!rZE6P;wXLi> zl0WI)tOl4CmbDhgC$k!=o62?=5OSvWbmIvb&kVP$>cZ4)Rv~@8{y3W;S%75+h6#H4 zr+jV^L;h82Kg|CHH2nttkGCHbyvZPfjJ^^39lZ$K!*EuCXQgG_u=&u^9$co38DY29 zFI1VqKH!~3{Ufp%oM}p)h#@^9B4r=9HE1v1E19f%OhhRZbRJb1B6_u_2wX8@0y~P z?oaB1>0UKy;jVb-`u^zm$q?>}hp_LD-bhB7WHjaRCa;9Z=p`8pi^@PV;ZMR{@zD1D z(Tm9t?uv)F?~h^)^`}b75Y+v71^6&M!j$$v@W30v@&jP(M}zU*prTg=3(K&ZIOp1Y zfMI9rMwaKG(qttfr)&AY8b4#j{EK*?Ilc?=z(FM*I7kJaQ`jCws)E7}`51|mm?-W? zfU(LU0b&#dWz+nu9C-Rqr(jR<#hc-_~WZG&{^-@koDe2q|^B0Yd^{0j~h+_slq3MQ1{B;UHqh7aJ;uJmv4gqwQEW%k5l;L_oOLV{E5TT0)^K3y>{7Gb#C{5gOpMH ziLzAN_1N~{L3{kML_UzhANM@yA-}ub2h$(grZZ{iuRh8hv-8tsp7F|fSnpFjkkjKs zuI>?jH2(MoDUCla7$GZU)I5PZ6aS|0$NG<@@)Z6!`~&Xn9)H};g)%ErZ-PHwk7=#s z{jGBvf9%KS75>Xr_G4&KiYG6w*w2M0QD!<5@-=;Y&lN8S@USM8CHezNB*@Prc^twB z8#9HB9}ACL6-=B)sW1!&MWtIpMO#>F7REt=S|{8TY-#31K~}a457W_0&9>+;)69(~frZuGgMPIFrk?<=qpsrR`{{ElDFaYRO_RRqXLX zW{VTVG(TF#Umc!;RlsX0L+yK$eRk`D4Jg;mDJ3;1lf7QE6-Dj_w+r7udZ6Aj~GM?^!YW~t>N7Wi@bTMeS9P7u9 zXSRGk7{D2`^{Od~4MdddRz*b-8&5x|4S=^A(|^fwU6l7R_!RzA%a@vVO<{S9c|o8U zmw9RkzZJon81y7$ZN+%scSBkADO1n#O{u6ZntGaVaIczfLHi~OZi~K^>^ctp2xY&& zE?E$^)`xw|s?P7v8)q9cNAR8gyk!_O1Ckt!b-yOMR|@KEOvlO!1TH#9yA^p&ZP(_I zGrFgU|MB$nzWe%Lkw(}76z_C%3(H?NFNhRhD7P3ymIX_e2CY#GtjHqFe2}`fxrIB= z%4-g+!V;*J+T}28l55!NGb6I9A=n*vGk9DOCKHO4#kpP=V9yK{Ad^yQzL!?pfjLkZ zO|%CTUc6ty;;PGzE0gF=8`;9}(t;&80;-v31?JP5dD5DDcAS-Gcd>6M6#pzO(vUp9 zDqbJPV<0;ouMfY}ULWR)*9Ru6<%piF{t?U|4^lk$W6M&?C}+tCP8twa<M|UTi@?T z_pQML{ML6XlV{oa_T7cDS3NQsZB1+90PBWIf5Sw7BE%*TRTx%h(7B->nmXPbp1n%I zw~pWB`MiO;?i22hZ5HlhqCMIVapDm$arc^z9`ruoK3%L&i>_oPlezd5Hg+LvMabF! zp~_A+re6i<7MW)N1J^==VP2TWhQ=VdgE{S;Ju*HeEdj)u#aP02GV693fs&&*=*KQrZIpDB`^ zmTllEcnIU=`s6LJiJ7=2Q1n{P`Ws=2i_?utALG`>3ato{^_0Ig0?BJ=TE}z3GYn|9P8rZg zscVX%092kyyR+Z^USYsmZPpR|DgI7@-`Y(o;8YB>Z!EU9;Mff5iCMBr_Wdxs{-}_tC5K*= zjV~Ham^;gcH|m6-M{2{qT_utmxG-YP9CE_v6*FM;<2-=?VY^&>Y|u9V>|e#}s@Sc& z<{H^b@3@BwP-gW$LhQWrID1B|30iCQ&KYLu&r69Q%@7~iC2Qj}wy}>N`WRZrZ=~xvoj^tw?NW)6}t4b61OIwKlk{aU|Z>6E` zT9zf&;Al37;%^la#OtEs^Z;Fe8*E%JX;X;*qTuz8{~~P119@gkKLi2p%C0O{)tNnLdVj^(`h5t_Hu_VC3ho>yXxCA@3yDD#Y{pytlf#JC~iE)k`4!Z*OMW+3&!T?hkviisoVb;A>6o0co*|&6ui-Mc^ zfVIheV3X*P9L7hd8Gox&-JAVPJeURXlEjKAALMksz+bXfuZXaHR}(lU!_WY3If{wy z-rz4;uhvAvmUQ@%s2P8+({&~OU8Tu;q!(GOM?qbnJNpI$2M22hZSp{%HSLGBqx*}z z)K++u6h7Ya1UyAFc!~&kLY^(qDS0;as)bk1!o#Pa-0|IVzI!a_XZa@~zHsrs+x;39 z%L(9oe3t0j=fZyl`~~vQ6-pHHR73u3ek_3fpM?KRDe%7~R!c(uNZwnfQj#2>N#2o3 z-kM2{$s|W)k^)!cj~Xh=R7#S$3Nfr@^Q|$k5KJA;raH}!%6^?hEF-` z3x1)vPz_n1gzam7&l1CFt@5Q?S0j#-&jY-}74O@$Ctxgyb_Gissu=poFU7W5oA2u^ z{$W0t)z%t4oWCtqC|Q;}9(6}b?_2BMLn~4x>YJkPn)(CsaROn``aquiX|Q{#^aqG5 zp;ci-iD=t>yS#u?Ma8NKJC7HW#ip45KAS>wD-Ysgh9p4EQ4d>Ud6bblagG^u7N08x z+}qh>)Tj!#)c0?Y=lqeJ#YLtXcS7oE!M$$Cxu;Brn?Lxqf;=U8k8P(G2p^Fi=&uWjxp z3|Fe1&O)jGxk?JTZ;+42INg>qU0bQX4eR1U_Npys;SL3V_;KSRmpsnym-Waa6+u)5Be|yKoW9+dCsu{fJOg*j_T9 zPyJK=vi~(ab~~y!*WCY}+9wJR5Hd5`9X$ z-?%ZyrbC_*d=e28%5&Ju9^JFbDVkB$9&?4L~vFv&t%CCe*(Q{q7lTUx0Pa97MHq{;M$R2Q_t?4IU;|ZVew3XVy!-jZm{1fHdtHY$L z0Ym~L6?l_6Kf$F3Zzjt#jTqf@9H814dTGfR-t4==+G@=e$Xd6vLsgA6dn8GqRNcW& z5o@yYhvU!P*7~ZxR-Ba@ z-_e24E1^s!_vvj;gpQ=4Kc243RK6N z>5D7lW-FJ0`{RYtDb`#+x%=b!M(kI5%u!S)Qi+shuc%d*c$6phY#D(4OADhHd7t7F zaHsY!&Bup+Wpp@0D=_+(EYrj*ypU5$ig*RC-3L|oBn9-R$<6)Zc|1>E?iJ48N#R-w zLwhG~q+iDre`AN7+NrKq(%r#9pKSLk6UD~-jS^n z+*nAeP^CmQrV|$6U4y>n=qC3low3yj^<7c7M5Y~-{R{x2t;IP#iG6GHZ2vr;=uapk9#j!2zij!$MC8Kq?(35o_BvMDWpTLZ1 z%LO20og9p%f-ZaH^k^0+c7~LzQ!nNbyHQu+9{xZW0K@>NV0L?Q;f;!SCQ*D9i*IpC zECqm2AyECY5>8;>^lw@AZ}6&4ZvUq3o30Y~(Ad9iLBPEQ0rxVvA!7fwQJJc2M9B1^ zHc;tt(c9+>E^*mcv{e(E`7XpJfit^}x;K9W zD#83S&775OFL_EnpJ+?K`8-uQGRJBDVD|Jd;?1&fWpYH>>#o-1&DnCsebIAgf%L=8 zJ2Y_8%~CoYkG8dHm(`l>J0NR@ez)_uGgMVB>#8eFZOd9P#*e?pyotOA+1N3&mgQf~ zv-j2EXlZoc6&Y=wto17*MQu;qzbziLanpfmaUYe0CEgUhP$kj3X^Ngr(iv2SllKWL z`ZjSvTU8!`OACwLmtO>?{#qV~qwune_Q^0rw; zMh40Ao5Aweqva|1>WpSS4i6Lo?^7S^`Pd({HhJ=9PbNv5Uy=PHjWzPu>!yke@z~8$ zWiD$s2j1quw-E|;YSbZr#VXH;)ze#iM5>U#3ab#6{0WjH{DkLfc`P}%N=}*NGGLf72FM10<)9l&R!!lefMkIsGK3*2}@;NMl8%ekC%AubMKEcma;$Miv<6=q&0h+Og`B*!9Pj%HmS($$A5Oegg*~_L;eKo z{>V6E_um(pY|U|g<3}Ce6^zgLQMY~i+T*akp4(xs%D=F)T+{dxB|rMW_|br6BzBK8 zCcc_K&NQ~Ui}vDL(4n40|ku0he}}#OZ2GpGKY8=#J2K4}ajDu`f?}JQo{f zgq1#T8XuLyX?&;I(`QHUhd%pSx|YI6pWkv^Y{$sOM^&7#>`=iVq_wm!R~{kjQSi|; zArW$35O%n`;01QsviO@N|8myPWob)!#>68(y8U*u9uv>zIZb|eI_ZR+q~~h7v5(jE zM(^iOI=|{H{yl5JY6_<54LOaEM{h7 zPre?fwWy7GCd<}p$^kr+s!sxR=H&t%!4H*xwtU zAQHAO|0-`Ki-n&c<%Q1t^)oVm_*UDIwiyzG-IPBT_vSyt^$|!SUd=SY9OLmxe5!rQ zd4WR-JnMnG9S@%5bX8f@qHT6tw>+GxNrO{P;Z($vG!Nr=Fy&Lvi+?fa7{@&x8rJD0 z-2QlYzOf8}sJ2@&>bEc*Dn}g@T|9W2Ixmhsl{OzDU}bS}-G3D4EtBr$MkB`qzVpZvsu!eQs9qH6<>69>oy`NXh5D{o-<6iUa_1}F z$Hm4VXe|`kv@<;48F@yB7g$Pp>v!cJjqmE*_KLMoYPB61RcewRuS$JY@@z*&RKB{` zTD^96$!g^c4Rx{A@!f^`u2|odmaM*WwfEJq^`sJtKV8VjV~gbD!F3_Wpypfg2KbD(0dh$Qs)%>b3FB z!lG|;Z&FW%tuMKMkX-w5X@!!bypISZY$e?SEEH7+rhA9okG9I4(sw|BB7;PDd)3@< zeh15u3_Gw~_dp3S)^BfXwPH_^KP@J^5tnd6EYAX(*sAz?w?OsRo-Kv^yPoXs$3#ryBlFMO+t()~NEX{i)ZyR4E$w3*;KM%4S3sD~)?1tBA(^0;O?JY?)vWCPe78tRiyoJ|E9jM1v+7#Rt8@-m|8Z&P6+H8zG_b({l z8@4>9hh&$&XLvxp0HFL3I$S*y2Jj6T-}k6+q0z_d*NmB zyQB}lDbtY=#n$+)*1e_dsu|9iJkTTFgOrST+p_+1rK86EWBAqGU63!z$-)rqyUkW` z2dZW}-8@Lg6u%C)5*lE9g&{?2LjZ>CP$`-M7#IISK~$7O z(NT3j`nlJ0`IxHhW0jz`JoS=5XN2F4nmx(v-zm#7VRCTLfPz&;t2KFvp$hye4Egu=5AuSt*r&+AQP!wGA}ac}&a!GTFP_GP1KiviENt@~(N68ZfJ2q%fXG z@7-hYruTz#M(!2_FFe@Rc7D*VFOYAAjmKQ0?m=FCaZVZRr#7kB!l@^oDmz@*q}-wI zz%MHLi#Ru$wo?)Xbz-+rU<;jA@Q_$7KzNk}OX3(aq3_b$I?YpSFs ztrt3olpUFG72A;~RH(28s1;vl`Mr-|`0x_Z>I%)e=E{&KOTK>>%Q8_^BtYQFJ7jJ% z)A`729rCQ$NHXz*4K{p@95Kf9wfqVnxo?wVB0mm(Axxbo^|i-BeLZ)HzFzneSG9jB zB>dyW%VgZS`G3np#+SJf6Mgm!2idrifRBX*OGi z6bq>2+EuO~yxp5piqz-_-B)TWTwT-}{gz6yDP`)=mhoHfLY=#B&Qx1(44H`Pl%9!< zC~EDTV>0%}^xsi8TT9S&0EqI}UU#T1>T7FK4%;Ptxk^L&kuFzhtV~JwAno~Ve+luf zaO$$WwXZ{g1a9uJ)H$}vZ!XKwacPPBuc)B!HcpiE{sCbVBe3h8+xiq++fdc-Qwr2* z5ql3E_pYP6aBXTSK0^v)i<=k(=?scNGb7f)VNO+_Qu{Qk)E+sr&_alpS+FRYpYJC^J6) z)O>w~`abJ?T|ujmc6ZKSmi%hg_fKf4tGF=C^O1P!y_7(IG0V@|M&##E{B4=oUd2pn zuVbe5_0_3q-A+oTHRN=EK+k6{HJ@lVJs=Z`aMD@G|JUfbJIdTGt$I6nU}K^irNk3m+q3e+I+K01 z$B18#n~%CfU}{TbOR@W+U&S^M@qWZ=ZW~b=vG#CkQtp1UF9+t@aa$5^Yv@KSMKkwn_TC5O%-c9&7X;#q_ zo;TC{!Rthy=^*+{2hnG0!41)N^qEp|6tZ3Krkn^xW-p^r6@J6$S8uPR%TL&SS!D+L87K zKFI0*IIAR2AAQ&sV~WbYDQHk<;4F0pF3@M-PR_uWF_J>I54ntdE9PUhN$Z#+xed8GJbAz2})U>c7F>b2!zc ztYT99zV`Mo!IdefbRDVBW)H&zAKxAZl;k+}FmI#tcS3uZi?Bh?hW|O^k9>2CHENE+ zlxyZ=7L$cn-Oy2^$yk=AQRji=MIceiL`R7Ar_kU1ZaeQ652gqnJ*70C zvrG+C(5{&;JhB5!QgU>Lew2I&;#T){9&p%IOLc_!srIV-fQ6@#>5oW>69$m!RSKD2 z12R3#@>drRZuaw91n(?u%@`8npX#V6g&4742xia)Tskv1G8gVQ)`Ca}u)L*|LZT-b zqHz1tFL6nx)Jc{pMLLarPU5?1>~lOfEtLxMY^n4R&u)(_l)bFNy@esN(ROX{TodSC zAGNj$y7|MHEE0U3M(0ecsTWL>xyKV8D+Kvh-9cbd%`78k$uQgXO3m%rN^ay)^d8CT zStfy^Qd!@XtTM@JN@b0ZtX`7E{?RWIajIUay6KQpQWv^z^-9-G2c42EoYB`SWj7Ue zO1@~+8<19_(+E!2))Xv1evpf=J*#k-4*4s=VPw$jH}fk?eU_=wn27Zbn9P^)0kEI> z{W9~Lg`jq4*7fwBL28)O6+_M%B$M4|f|`E$1gL4m`WWm~;WG5nm_Cj#;-%Jl9o$r- zG=iEgmn|i-6{Bu~K=}5k=@&hG6bCvNQUOTN@=|%@^WdYtMXLAt`5$<_j}`H$gy6S& zN!{bkDWp;uEoU zg5SY`xVOCzSMJq;nDSM_RUL?2shp{3=OtF0v068b!HubCUgIkTha&kn6!}3u4n^_> zoyO6NUgN!%%0Y4-=WHr?dQ`YV9$;ufW;;>sg8I@agfA zrS&8-6jW)}v`y*7V zq{6=r)&_QI{zfkThCG_q2dtNrece-Hm}`AV{Xx4x*qy=rH{5M2gg+*xt72l8_24@^ zXm4RB82k6oiq9%G?1)&6vDr>)VCSDvvnhaAl*>v)?H!Ezeyl<(HlKpJ zAK<9(*b8OIjrvQ~3QY|;@3|dYJBT<^jjwUPSdj`dQn4b>#^OR)kzdr;9$(VebHAdm z7hWyb^n5FRO%acM7x zhGGgB{W_BlH^sf1^0s=KSRyr1#4AiGuS~b+5ESS#Kc|fTcGfrl{wxNAoFl5jHNQ(0 zPW}k6t=z(9mHiWsk?DEeuJo)0t6&6m+c?)V6@1-z=30Bev)0wdGxv3n#ANIP?<-WP zT>C&-x!ck`5EDUJtDv59XlH+Kp*E5ZXPF3AGG70Hec;Fn&p@!S!ZQ#|uUD}Tl--nV zA4qX!Ac%FZ7zi#`27*vd(fcE@zl%5H-R>7?xk!+JW?67p&N{mFokao+-{8(Y^gN_y z>ZM)W$n!xwPto5w@DY0%oJom4k;osiISX0%P3Zso6Zx0+CsO=e3J2~?>a+1rSVYI? zpOiro9qTXaaa2oL2FLf8wQJ$A{FCdd4671At#&!R>bb%AO%%C&7d>1CuzkG$XUv4kb@x*L7I={(sDIsY>ZiZ(w#C1xu77E} z`ZNAYsW|$_WJwF^HPVR{+R^3Wv~t4}PXkW`w@7I~JVJ&YSkxu3*r@NxF%`BiSdbs_ zeQeYVmqX1@2oGO<@}gf2IaL*9siAbg!;6yBFP$^O%kg`<47=THB}c+eiZD8@4;ZqR zE8~60ln;~VkDCRVnA4xIeQRmRS6D%Q^aE*1Y_d>t_7{?OtM+J@l;uHZVx~14Si_=+ z2;#C)#ML)ia<9Xk9y?zcKNkpXl^V~@Io}W3W0_XFmFZ9e5Ocv{iYT1?z{whlbk@Az+Z( z1guqV$B6*47nrp+MJMdRlSx2P%w@9mI1M40@jw)hb~VMHa_4kzPsZ7LU8W;5zCk7b zX;F>z^T~f+kfw`%_~)1Z{6-PQS0evGV+s&p_i0N(f!m6Cs;*SRi2`o znXv;YAxr!C>sj&i22K+F@B`WMm6Mb0=!aK{et5HV(CeI$)(=+!0@wq=9OR|N{mx&6 zW<1~y;OgGRPvQnaK0#zga+3@QiTCbR$`eexwE)*b6nVwv&F&PnC<`U!@kTwww+s&j z1seEAL4{h}xm1?3KGgpE<_&B)crmEN6^uinz!<&4=pBz1P3{y+5$DGF*{{!d`fjsyv=MJU`wEGJNDBqwgH$$XZ@4bPfCQx4s{s?Ii{NP~rbdwj7KVW&l&lEr967L#lu ziLK%f<9(Y#&!_xeh)Y=lr`o3|-HWT+nv_Ub^f0baX|11enM!N@j0;IS!wNK_+`ZV$UusTMKDMJJ zj7nYt+6a^JT=<0xM)B`$cU_I3XRUF+*t3%-E-*vRm5s5c`^K^oPBD5%7DUS?pZ9@j zl#XmHGfRJx9KCk(0C^UcqLHTPmnUDYZz&pCE)^xu z^YEANf(~^JDOZ~gd^}ujj&v5ZHF>`2(@;ig zXvJUF_fjt#vALX?Q#vMo%7x6as+P4z@2&XgC(D>Io8QD#{dCk~qxYI<#pEwa$tJ0O zhtd1B=;@PBm5k+*u}d;KO!)#W?3h@|YqZ~JTuOqRPKjqpNFCA16LaghZSr@>xJ%VR zo0HKSCV!ob9@lfY8>5$s=5XwKd3i=ien;=*Vc;pF_i`g9Y#C#QD5e;rw@UURqxUi+ zR<5$Yz%O4eNDP!)spYFGzYo8Z)&1RT^nTrlovR**$^j2r5Z^+p;EK5UX3;=+!~IMf~b3bpJ_zw(RN>%M`IVezxtl~$~>wvo{9XI z-PJap8TD(s`$K%piZoiIPE!4YRG*Y!nQ555Am{UUXFeY-PZ!HBqMC3W#byM4W<0&Q z?ntNTDd~cVr#N=t1<6TcTFuuvg%arqVq`#Nx{O=EIKx+~JAz;K2YHIBXeh0cgKSab z{}x_WB;ZB#DsWTQmn6TWlFHx8`6{XWt#l{pm@2~23c?&!eTCv>O4}?GC1ZsL)Ng-O zIVk8}oWD4&=IB+EuaKLYD>xFiL@%Cvp}ylp*cv@+@|nSshhLJ`tK%v2zQ?gL zeXkhG<|u5bNrGoAg*DOQEy26ybEv;A(vVKSh!1nUuNmsgQN$%(kUEgoZJ_hI*}yBFmMC8ThaHNKT;D z6A7!7d_LQI!yC~Ha^t~m6!pSMmHHLun<^MwLQS(5434u~O*3bY%x_y$YKE0gr0#FB z1jIlm!$@`XEl&%DO$o>id8gA0Budqu4nPx7lW zIu^mk5nQu>U`=$O=zOu7awrUF^(E3xcLy!RX_~A#?KDkN?n{yjbx@AFoO?AtD0oTn zB%Uq2@jfwq4!MdauBVmA*ow~A!Z|AY@#8|D|DRx)r|rWBgsJc&vb8_koA_#4H)N!` zS#Gw#=wL}epyRbu5-?@&g)-1io(n4?R&~XRNc7=p5?%C-u}sdU&(BZ|G#=ac8R&C{ zFN;1`Ab0FM)&8TP%3*9O-0yrB3iHJr;+&QX+Egxl8Aw%+I6H$@u9;r z7QZL5$N%j1zR%Cree?X$L96Tjr2Rvr1itNQ^{3VTXme+Ab>sXbhO$+JR-eyJa;T8k z*ItxMt9wi1^Z2Kg{rDg{RZ`vV3qAL#ne!%hKTdmZbDp9{%UF?$9$f`JDn?AWk5_Wu zj&DAEJbLu)KL|Z4mQmnt8G5uI8EM3gmLOm2MV9;LU(wz3OGNn!?sbYJMLR$p@bZ>B z80!@lR2EhGB2CMyD$zu#eQ$Aw?)0cr^cFng)T&t?vEB{Y9r|t)HLB>HAw0o7U&;IG z_C{_?pRY~b4&%1x^G&H+R5}KgJ+F3xyn8vfy`ES5KyJ_Dw$Jlw7sxH@ii7$+FMB|0 z_Eb7NT`hJ19!*^#QfC8tgx1Paw3fhy)5F$mG0I}R0F&<|UV&IecBDz_%>0P0uOe4D zefJvoi;n_mI7O`vcmQhk7LXbvhgy9Ad?SlmeIP}xMub}Jqo~!&$=53KREk2a_MQA? zeXFU}FHSB?mZjiz(4$vJ69+)()iXVM)%jLFUhL?$_Ok9!jS&hh0u-;cR1^dQL>MNf{!5+8z)sJw9X$sbRo~B^c=l{raZwx#N zR*I!CUMZp|*anJ0!6M;s&ALxgG^`X&yrB>(UT?fx00@vnpn1^gAKQ)-0oo@q1*hvsHk8b^X04!}4nglqf1!8fxkl;i zC2Gag{2Zj=Z!$58-c@VhN3sT-?jfk%pL*18_n(HWGYJL>x%%;kP_dB|y{mHd*X4uIyT6p@7fQ(<_mjFn=-pc(QN8yedsUkb zPSa%XWvH<7RK`}5y**S$$I=EAhzPexR>lUjU=ilHor~seX z*FlC)g1fd~?Ca1cB!PV$oQ1j;<<0ncZNMw?)$zyk7rsaN%iR-vAq9(2I`%?iU-e#t zCTBV+QmqCtkND>ctVtPq*1smx?%=}FczS2DBldFQS^N$6o*x`TpJ{v@`mDORm^l)c z0-L{?rp%P2Ll`5Q%=`nQ z3xSUJ0;unlD!5VKgW6CcO>95$a7X~u;BvYUC{9D9*R?DrI5>|XRJ zy3=y>O}bpvd`5U^lKPbFzKf(*XQ8~768*ZTk!RXBSBx-yW4l&=RU)d64ESnFt1mUt z0vcob0^O_2oMA;~|43f-nWi(E)TqMhZq9fT(PG01a7t+o+aakQrCO?Qi7nulme5LZ zbOfB2&a-slDdx2FpRg9FR=9a1IFMR~^po;j`*>HS%+Gz9ci+>yqQIfbA*hzC@HrI& zPu;`qmj<}M`4))d3u=g(%cV_ly`wy++dEXV8=f*RIT?nW<-L(7>CYhWsJ8mV+pE{LBit}|AqS=3Jp3yjzRG@!Nq0x8^ zDexhw#q1v_HX1J@!;VSitn98v<2mYHKFNKl(b!epOG9Mk8;u{+#x$4sMOm3{PdXfI zsZ^_zYZ-ZRuU02lwK}=#)z~6WSyCt&J4>3VHfS%D*><0JK*q%p^i^rrS;gGVlLF|u z6z*w5M25n++*_C`*?08BCl2wG{$6#pKuZ;dN~>pm4wW33Cqyz*%ROkJXm^}(ZIHIT zzeJQPW3yCinxr+An8`e;T4y^_1XG%_u>IX$RP)b=wZa3FeCnSLkEOTpL>X9QLexme zL4IWdnq&2nIw=&IJd78eCGw(s$Bk^WU&_qcR}uzwfAmyc`5CHmLa$$0p|>~?4HFc# z%G53vaMqWK)S8hm6S~oBLrd%j>C_!wDtijVFlMzop;a+7t4^0dQI5PTq82MAZv>s$ za)H+*pkUFz$qJgQ*9gDRL1THjvEr#41u466fjK8vpajxG?1IJ({c_u}=!&lQR+)G{b24d+_v zT|L}nkcXQLQqZ60W!`fSHyNa$zsxd|%bcI^%&7Og(znZpU; z%Trum<)`B*d0WBnYcgh@uArp~M~2$SJ4NiSdnwE*Af43uA_3oFW9Aml-XYFq;$(q6 z@piwk9sXoiT}?w<)eY1q%_0(Usc9`UX+Yifqg9N8e0F|*RlthWn)tv`p_Gg2wio$J z68;i5#kJB1TSGjwepjeut?7KDL-L$}wLT%NL9R&u6SX9%#BZ(RE|9;TFOH#Z*mSp` z|4-GT@sO!mVSvH)yOihFhBzU{!zHhTOAa{Kb)c9ts36@2Q}oU9LhE;W*WQ$rk@5oA<{Hs;*G^ z`^aD4w$y*g*1DYq2eyj-xL<7TWe`&B43+FP+m^L0jlPX^WWrr$+sYjNg6Ry)*DnZ? zM#S{(;gVJ|r8Cr$8pmMCQZgmdI+>}G#5OnUmf^DB`((Pqp^|vOccl8Uu*EzEe9KM8 zUy%<_)@baejybg?`o}a_MfXhI8;wmO@3c{a+D}BIw&-1?=V;w?e*Nw zf1yd*EVq^1F3@DN-1g;mktUku_KVy$X_8rPPv^E(5zO+gQ7S{-a#8Z2K5 zS2h*wY3C%(l|3_mU$P=Cugaz>DV&P3)ZNHObw^b1Y?3T6m1w>m&@c;+Cn0uuTnvT4##=Aj6Y|}1@c;6{l?qAI*tJr#|bfTogH;m~E z7!7NU_>dAM9XU&@KAHvJ@|Bdm{l;L+QmRM%wli41&xl z2-+iG!so*n1aOCyzUaC{DTO`2Njn94`B6)1>C16J0ys=O9KD2jJ&mAOadfc=V$Y&ka6K2MNC7onM+Cw=tU^ zr*51ZKaC%^RP*BwS5CYxahTcs3lM?>i=kSLQKj!W#-BtK8xnA+bm1>$l=Q%2F2|PtRag`~10NkS} zOh{=_6zL))4jZhSpxF^5)}VWV1wm)j0@eb!%Y65e0O)Vs@@%e7rv16=W7>Q3shy1{ zCxkt;r*`~k4?RcwZ?=b)SuquH$QiXrV*msI3p7cSSXUUJd@blqEe`pP8uf$0|2XdZ zQIU7z1RX(-mAC^n1#4}{I*9Z`j>=^B%=#10%l6nsY?6pQyidUXX1{>_tpNdhT%|c< zh`%BB@01VN_m!FUJr$;XU$3B#ie9Z6%~q1VZLO_3hbcR*yAm4V?*D8xk_xa^BH?7yxC}XHLBeE>%Uf zG4*oO4)pT1)RZdzj6#DE+-*umupV?q0w;6YpX&TGpQ!=Te(6WD-)AbWx@^6dGv0Lr zDvoHdDxCF z2>CXG7t1a%>idARhOA8{QB7ZHBb~p`w1-#Z?=|fqz4F10Ib3&@1xq%{vc|t(h*ci6 zYUTv3=&!B76Q&*QYLA`cd%b#Cz>bvq?NN^gd~2((4cLM1eyraDzRlHrP2#LWv|VD= z)RHW;mdHT*4psN$u2?Ty-$A>k)^N(?M3w$(x1EzztHcR^xoTCpYE}6jd~S(JJr1$Y zuVp&(d^eO;pEC6<->{16qN%6(2KTDLb>B@4;I`;n$3&A0GP7C;o!+7 zuZl+`aY&Zl*1lL#!RbMP;mG_go4Hj`X8Ruw#D8 z`p-T8u>V#r4Uicf38q9|52Zw2>qO-BP?pGRort_1$`X04iz4!RsH{xHRnkt(rV)8P zlqK?77e(atP?pGRod`7_$`X04iz4!Rs8ZFz8pL;~keQffBl6m$L|&V6ydE9aBCj4_ z{DDR%t~T>=Q6n+u1Tzvz=EoXOmyqmm`4a8yLn%d2A-DYcPeY)>);__X3WuceCz@wW zKNf!y$&`$lD8)_qZ1~eZBG2mk{bTVbAxL5w{E6F7!=G0EWj#8)bYdj)voA|=7B70`dhv>#PD*4Wk% zNqIqL5#Ybd3T#lozoyA|v>J3sfPc;M0AT%64g5#727GI(&kMk{gv_q)5wN2$1zeJ#{E`czU@eBP2XxV4k7n_Lc3!% zeJ_)-7I?gX=EHZ|3M78YcCMr_MBVU1sNLF%E98qC!{b(9vHGB0WCiBfLkq3IqxR5Z zD=^RF6oqYko#UV=e1TG<&@F|DCMQL-v)0R~yGOjPv>CIPX?Ju}xtGSV4o?aP*C`Sd^iOvTFn&b9j9VhxS zaXIHfwwK?uZ+kp$Pur!u-P@1ILJV13O#A1uasy6b7r!&$bkm+D%h&I8?-Out?lRD+ zK7F9$?=vu{#N}n-k}Vvj!T8RyFjRWUMpiMj*C;@6DWX_xp`S6^nSi6iS)m8E%gzE;Dz6I@Mt zmv#P!EyjVfbeCETJ^L}R5KEvMtnHSzuNSLhh()ED4|-AJonXg72@geMjlT!qVzWp$jmEj$2)|)ASJSuFXlzu8CZn;A#IeU3H6-rjNlt%H0FN4!sJ>>F z{j&xDv)}$>BbU0R1L@O1d*&>;i4PQXMPs1$bjh*;twH-mc>y$3L!!tAdtz5x10ugY z3_x@{fM`ecrG9%bSf~ex{4#}GtIzP;6AJ^rkE*-*?J32=5pjyrfC!ca$l|C5chwcl zi8GiHR1k=y03#U^k||(Bl0j6!NJiQNLo->;zJt{jpk_ByzWNLP{!c2va~iJqBI`UZ-XBbE^-&XO+P2Vyg%x1vK6Pg!G z$Qd$Rbc+^fR(SL&8Xqu*zl`PiBDw$+!VSJ`oKZBodP4E>pp62D_M=OKOc*qWG(TLP zq@5GQ@#UrzusLIb?161*U~`(>GZF294a@}q%_IRdlc?apt3Vj}M7aD36$?@zObWIX ze}(RfO3kc*^^S>ZJYc162SnsP93?`|&$9m_%g9#o?FKo8NBzz@hW&^vGQV@#>4TiA zqeXrvZ1Cehy(o-cXnY5h(B81MRe%UvHCVEVMFuwmgvVQFI{k~-NHMS4n^0hPs5!C&X|;wAAM z;g(z@_~dRkJ|2(P^dHZ69w@r*!`OYcHI7X*F1p3XeBPEq&!fwjtcY{sc{NbU8X*w3?~cf-Yf#6nq`)}VcN4~p_;aG>j_KW zv=?}*6u)8W_J~`lU?%Qs1aKng!y|U+arJ zY{Y&nE5rA`5vw6#`tB>Op5*U8_KE7dSm4 z!Z5yO-Y~nqwU`IgqGTY#e%}dn+dT1d&#fq)yx_RP!>Bdt#o2Mh{y|=GLO^8peCV)x zFWySO2(17Uu)&SrrS^y)5-R>?VZ^@eJm8{2DI@mcW@%0Mf|>@chA@+|5$ERfit+J{ zcr{Q=J$cHBcwhRmCv2-}&6$6^-B}}xG2!iAAtF{>^s0>RI%8xpNUzQ*OXa})&^ec- za^M{595mdenvunQQaS!q&VW=7oLyZen98|1l`|3rqFYg5M`)_`n?ncQ2%gnMXJAhS z2qY0G;~$zb07of+E@hySe^6Hu6o`lQgMcLgsv#?ai}S!3l-Ok}h!|U@PG$(1$gpxf zi)hdFl!ysEaq-$aq&O{A7lGkKtPQ3_V;bAw_G3SX?XeB4qlmQ=#LZg>5$jSFQi17I za(JQIWZTMig_FeY9ln%PV4m+~P8~Q3-R+tue4C8no5X5YU~wF4VKg1>z{3`S0;8>< za;vOS8+5=M{4oJkI1ZY;#fvSSYS*jo4)AZWO|!NTDkND(Z&FLs>y+A%X@0F8SX|ds zEc;$|`2%BtVRqzE&eeGyoumy^%bdyi(sY{Jm=JXlx`Hyg=xogpam_J201fsK1PSM1xjA-4s+DVq%VMvP=kl zQqh%0%{)hC_$$P?lfQKTutBdVL_cEY9wq?Gb)+!}9pgJXff<|DU(NX6J4qegQ}URA zU4;QYXyMG?@d7({5$N|PBoQRP!vTA+O8|@-&orL=Mw%nM&^O$NE5(5^UYm+UY!qW= zdOQ=sBc5T8T3+(9wGK0aZ}1Ni7HtKnT7%f73{MAt$sx^~S-;(mY^YyR)%ChA{$y#g zo1yr52i9D70k@zAN!(-8QW|IC1Q@pp)%T00Wht25B43L$I^Wu4G!9@jt3hFL&G)_R zd?VjzJjnUiULcWbf6n-ifEixdun+mLwO0;`;dcFM*%B(WXl29R;QBXfcMim=yfEzC zR)l6(by4Yza8ar8G=j~?nkIf7Jdq9ngV23Cus&42dAOx(NQIF|#VpFdGvx-cfRt%R z@+=@rL`W|#LVF3jIr+8v711n5@+)j|Mc|^V*iRg-abxB$)LEg=Z!5B9kaMN7G@7<64<&ZLwKe{3hiN-y{jwCl zB9UP`)onLSXFw4MTKxSEh>QdF?}cs**fWG^H0|evSai-Q0^TO-z+L!8@s52R7Q zc5W^jXgbvo4h;Clb&XiBp|nsvrGMm!=so`Y_f6l2(I1*l;e%kz(c9Izd5Z~~`^ErX z{w*|)Yv$Nv=Zi<$QMK-XFR>qwrS0TkV<9)z3+p3bC5`&~Wov??h7rF5WDg68mk@Px z8ISY>n+JKcUFjM4?Yrg^kQ6Nof5W>hIpb-u_*^rwTt^Hm1SWS-XXPxTUi{s2R(2>( z{GK7Q=2i(j*R1y!=dgGI*6x3flf9V)lawP8e@EvOUmttwV@0^nZc0PbR=4G%v+}Ml z;R#I%9oTq%xh@oOs?U-5Ip@e}aE^qb*y*>`L`I2|q}Fcz3}2sh9;{Kmbs!Q&;D{x%HEOvX zXr%QReCXp~fy@l}jWpz~3spmaz8nY1vpERTV_mK%!D|(49=t6DIUXW)BFNDm-rSUaYwSth`W*0P+Iz07QKv$! z(b+w@=&+8Zl3{n;H7c#pZ`y`wPwML1Ys~yfM}Z&L0~tq`3Zr7?G?g*Bd%!ok*qAwy zI~2DB%A+$%MSKG7QIY)VlmH}2u`7z@JI+v{?fd9tPm*~g@!Q&2^pmd7mrrR5L4B+l zP?tGR@g*k}oAxchuJy)D{D9;o&&)hW((l(&M_eUpc8w={Ts`d_5qJ5mR{(4ztgX{_ z%2o6eRNNXNLIj7lW()5|4(Dlq6@G5OB7l9X;_Oq%tcq519`vv*y4PC}ev< zMy)`D1ucr`!~Hsx01FzdT4u~|_wn2J7TT2@;98P)niZ4KrO4CTMikpR?TOR;)wEF5qoMebV_swE>&~!9bHlM`IJ0R%=P-KPgGTy{i*9bV`c^4HTzTFX~xW+ zDm}WF;haKB{If38VO+n@jzuyg9BhKwE|fI{t|=?U9aq6{=rQDpOqu%!7=6uz71f40 zGKfv|gaPj}BeF_GXl=DrnCGS-XZs9iG%x80t)oaugjSr1deh?#k7*ADTfUwZ{}K|o zmmsmZw6X`lfHsiakb#V9)oaxTkMi;8S=iiu>bGW#1XGW{0`}6Qeq_-jnNhd;0)#Qt z9Pn*08ht>s#II?6#GceAf=l<APJW#PASAZmj|4_ph7NQ3NPK5ChbL3Wvf zB?mBDAZ;sHfz@ZMA9K!#$!-YSy~6#Ul(lHoi_^0(3!gy8_sS#Z_SzJ_h^98e<89Ht zW^Iih^&rtN)?;$@Eozg5oM>6d8P!iID+*rR?~5{4yrns{ErAA6Eb;`5ER)P-?|`idtJQRhR+1Ac;vzhR3O#p3>Tjm0oPo9xGZw zyr2nSCZN@zR#98Q`!kM}sK`y5{J+1wpP5Vowpx46d(QiR^Z87k=h^qY_S$Q&z1G@m z`>#Zm_)#Tf_oMWx+W}*%ts}3uvC#LeG@`>`_++mlMkeE**y~dt#?9!f<;K>FeUJfA z^A1+5=ri#p$$nqv}V%hU3ZCcX@tuGrJCU z4~JShjayDcs!gI5;+K4q6KpzOV}vKBWa<9KlizEsP>OsYzciO#H*62_yUE-$V#AA<0rwuAO&wBi}6 z>()L2;jL-8tR~Z+jGyTJeEMq0`ls{{?VLOnH;LDpv1f!DSC{k-c52-SaX5y*nFwFQ zDcjlHiHW``*d*U~bqOcn?(3dafOmSMBZ0=hNag+PJ7)B;?-(Nc4%4`&)0hD~!!4Or zk4L{XvQ<_B-3w8ouom<4qjPmQpJMK7{)oTvZYq z7>*+^g*2nAUxrj!Gxnmd`NCDRf0BFI-u1md|0cu6uzP_FQlgiH+#)Ep_>@3q{Xq|M z^61j*OXi-arVg8X?8y_$=j`2Ep5q>`Q!?w*$5c&Z5qb_#^_hQ;JA<;f#O{wIE~C{` zJ2_>NpgP{L^Qv<%Mv?b=v}okhsW@4!3DgoZ!|*#kmL2oiC82nvY!ojC=>;j%*`<2H zyzT6=P)nq&CLFIRLmV}hqx&^w_@UyKr<$_TusnQGB)0qoW#$<_zl?jTE{1pvlGR^x zE7LDZV%+D%2)rTS&6BjcXH4Z}X`ZbNID0<%pzZ=osa_{HmKUP0*OqG021X-cwL5^a(~Aq~gG{*kG9t3ow?)#qv)Z|&4)Vuc=$ zRFJ~E$b_$IAN?V=rG>ACAYPh9ar_b%h266O>NvheBkDsgF}kFEba|mSM0wVE&I6R9 zzq!tEZrmPM3aMFd?T5(nqSLq%?(OBCPU=%Dyj2*3agX|XF0g4>tzK!SToC&D)Kx5- z>HWMpG9N;IX`3hHYm@nypSxMPu^Oo&6}kxTM=x#a@#m}H1xEIP-_+E-SffI+{QZTN zzr^fPTHGT}xz=~xyJXn4)KeN%T`2P?FV*u=cG-yPCc@0I9?LrB zWUlUhvbu$YNp3?VvrX@_I_$>1H}=%hw^KtdG>W5hZKMM>jhFz|jEHS?t>k>B1tT3*$^>(GC!=&Z3o=kQ;plXDezUgR5O36T4B#w#@DvJU2O3yxa>XB zbp)3^T&Z@kS8rTKa=Dpmdke}JIW;eIbmY{6a8=~gK(O&A#MLEutxHTgwEz)XcU%1{ zMJ>koBDf53B$u&mL|%6OsSM2dry}(_pNo3=#~xMx;zS_Aw4VhdHmuu}zX6Y$iaCkP z77EFbKflQ|rMtDozA)g`G|d_Oe#;pHL*2=mmKA~|&E~qQKuybz;EgBp%?l-zZDa%% z=g0Qru!wTi#1+VG^Z;JMlm}Ct5{PH!>4Fkt#!tG_Tu8%St4fOe8QY6PIwQIa;pXO!Qeo{dZuQu@#R;xx z4dp@C=T9iFDc?ax$Gyf|7KB5i$RC<)$ekEitcsi*gCWwwi|f)7Lj}5bN|pK}f~ffkp&+v1bLTG>PSPmB;~?U04id#oHM{OGyrbTFfk5a#DtXpI1!O%`>;ngDOkSv1cjou|zTK21F zWB>K1cfDzR$B}Ze#>D`DYdYL3G5>gs``aLy)zR(1y);H6gr20mbf=Y`6_*EVtp8X2Wej<0uxHG}O|MgWU&r^pI+<5jkgfe$O5Mu9FWmft zb|W@!x|dJ`$^l2T_VgfJmrdI(4X+qGF66`Ui+^CRZx0Rpu6C@Uf!{O2`GG&Q7q`Ds zijog8?qck;jG)@v2I_KOPHlWsn6+e_JL$B5*)B1$0O(@x3&moKF8(xd-#!?k!d|&7 zKxcTlsZ6~B_91r5nZTy9l1!{1k%{#Ko6H@6Qr0rbDMp+0|)Uq4@YK0Cj@7uo(Ezy8N? ze|~+KD)RaDKLH}~>v2+v5Wn@*Kj7B~)cwo(^?8M#pI`qb9OC~Ze*Kt1|E2u;lYngJ z?ZE#be%&3JLGKK|{z`Ai|Kr#H%lP%tV>2AlKjznmk@o-h{Q6ZTpO#<$bM1d8zka9q zS>#eU*Z+L{`px_OH}UJ|aWdGO!M{)D*Ke=MVCT?pA0qub=O&i1@MIRBv-}w5dw$}!ZNu2Ok(qp;w=BUuX+ggaO9r#;WqSp?*e*q6; zM(8zwrVa;}d|sc|jym`HqJTBKnyOfLGCP+6N$ZWJ{fr?vVBsW<_Op`pTJymDtaYII z#5j;oV;g9`*`b?nECbCqhJoh0%>TwL(0n6?HSrh)ns21F<{Ohh^NmHI`Nkm7eD|>j zyqbMw<7?aKJij-$h1*^)4G;dalN=+7D~|ofXyd7;WL z#4j0N6Q7Bt;Ki+B*BpW60N44roJ>RmT>l<`^ThvyPGQOq!&dJ|a8SqJlF{4YhtD56~_l_V*Bu#II|Ru&U7@*-)HBh`w=r2pS|b|U)#sE<2rk} z!qAf{?XfRzL|mOdCf7ejpJQ=rb+ZmOZ@c@+b;@2x;gAajn~tC|&ior$SK{)b09G_b znf)Z@I8!+qRa3+~m2Jxg$BlJ*UmUo9-UIms_Liz5_)A|t(6D_lfy?#J=(8S&2{rnx zX-p02vzFzbI!uw$XRX?mJ}Y4KS*tWz$ZPajN0HY^4M(HT%9C#2szT*&HL%Kg7a=ulX`Vqv})$v@z}$t z1KHlMmP9O$C$2tcXpo+5sL=e$(^DZpppxd80lZ{+=_qiz{`VU`<;%-ed#a zsU7tgRbo{)2k~-Gw9t!%GPW_ZcysGk#{+I%s}4b7VkASn+B|GPeE9cza+o!7Coc}! zn<|4h?jf8{Ce2KG<}GLV4(v4$TVYQz1`iEBeF zs%@J}`B~W@Jfl>5wZmEE1(vZEJjeWMjL~0y9{s4m-uycaygRiuJ;Bd-;?Xqq@gd(pm>iWdr`k2zRlh%2d-;tZ2wgZJ)9 z>fX-wpXd!@t=x;zUi&>{$5o5i5*&M5#QE$wc5fzKZ#Jpqc~<_%;D03aIx3 zzDhu;0$Tk5hk$YgK)p?tFB4FqfNDSBNCG4`$vSsuYBX0Xpw*@bHtF8N9Cym`mCjb^ z^-BCI8*f5LYkzPo4n&t7RGj~K`pdca{SWE&*KEe!@>khgFM>_wR2GWA97aricH*MS z!07txD~sg{m_OO}p2Dp`5LN+r^79<)YV{I(Sw>f9#Q2CFDTZhGm>WaQCV`7L$=Pa5Aq zACGMH=_?wV=twT%UR`Y0u1#E5o_n91;`cW3cJbPj68UQ%kRRgWZkeKMvJEzhICvGT z(1SIF6^C+zgM)e-6|akgvB=n0hE@zACnR9#;xjZin)nj0Xo}$g%v;_{RG^A)A)Z^_ zD2`LWg|8lhQTw1sehSk9V)LL$=NAPhwS|IT8Z_%PLaGZCG`7HojVYKlLV*Dj81MrF zv-ZYCRJif6AxrWdJ4iE|{EN49O}4ZC&5aD-Ng~*0W{wU>?i@+XD8b-^vmgvgaQmG`Xddw*Xi z%Rfrid8VvWFaa2EuJC))P%8flzdoF0h7rBz3x*Lr1xA!(Ti2EP{>fCCKmNr(h;^kzopZ_yPb*j! z$?xjfm{aEC!m&zIV78w7Q@7A^JMkOlnn@DGo#8P#y(T&BY*;l$h$?~2_uqN%KN_~;Rt(TpC5@yaeHL3$uOJ&J6L{^y6L z+6i&VfE&q@5u1c)`};I;|BgmCt2 z?se?qNcJ@)bFX6`$H!On!2x-vah194^mpdE_F&W3O`>Z{Q@Xta!;;v6OpA)V=m;GL z7d?Ue#SS0Yni@?!k5lYJsrmYneP}PFwQJRGxg;mpw2uCs=|)Bwqc(ciWBNa_LOm>1 zk3zAYxu;qItpq$IQYoeXAu`0^OyKaXI8W@ho?0qrA8B}_)ACn2@!jI* zfu3g+r>nWYSQ8brL2^5$f6Ynu-qzg}J39Ba?s|%V8a<7}Mdp`0ef%8<(eMt-wVebAz*Q7_j78Zhz8ewRCo)vm*h=dMLUXj*^aCF zw&U2|cJ$+a{PxVkdZo85Z+}8t9xcnXB^}H_EgkUR0$eK2s0SCyNUP6!W+tnsn1ijK z<4#~kJssWG+V9BX#$`@Don5bc?IE4`SdyR3&GBYBM^DzZN{!dD-sAprI;CvUdf(-^ z%zyYG=I zf~jJzqfMNLcQL+CPKPDwb+Gwd?5I$WIl&$7Y4iET2>#Txod%vKv_|mhs#d;j)>8i} z_Fb-}7auOj?maAjWxCL`CVpp^?sRW3+0aK zued49t}u4PQ`5l9tY>OFm}cU0M;j!*<56$Ak%-@M*;%evw=j{Hro z?U6+70!~1w4c;5+l8oz9wQXM9pvy+XJL9jzc+~B6-4(p?Y%=vlnFF%>PmH^N@PU@q z%>MHq;!6YDKKzfB`TkDpGk|J-2i`~cQg2qx#?_bR$WHp=OT#-l9~6#1Yp`ZpC}RqZ zZ8S1gEhs~{qJYf;YXc)jMI_#CXYk?VVTEaJLE2=sjLt$>3Q@QS|C@i6L{jin}F!Wy6X~No7m< zxh>^F0$k$>$g85P^g#`C>P2rkTxAo?R)VSZ-YcpJ(?j^kk^q-`;bcafyi-+si@g(s zkXr~Dh5f2qm}c^tO>~5V3@%bp_QP&~Ltvr!7{2!K`#y4u_tA%jx5YK9G3hW1V0L|? z%vPg9O_f%4Twi!{ZO09JF#TY-R-0_GmpODJAf{FVdF=laLsPu$5^Q%V!Ux*|D3bx3 zF_Y~DTemvt?ryj4N7091lTN~oOVnIJ-hzfyGhaFTiClozQND#FwFs=9*He<0F3;tc zv%oD?;;Hqd6nwoVwWk-jS^lUml(oamK-rfi6z>uI64KVr*xuLfNzix+6N=B`mcMne z0oM%+erfHjlJrr%>%EB$1i$12W)3sMbCw;QJu<}&DWkmDKG*IgA%rO`QbuWQl(rX- zSqG-SnA;zv3Lwg;DGSWhO&U5XV5KGnWs)Cqy*rzmlFep_aRZ(^ny)dMb6@T|-kCY@ z-QyWkY$G+Fa;DpY?bsikj^KM5zxn)1Nv69;mhjuium611_77io7*s0-(3bKFOl#Gb zTUfYWDX&laq4>m7xQ%dP!V@{pRh;gX^to zE|@drf|hONi1FN0_a+f1-H;2}%V?6uPFDqS20@B4|25G2n&-bJjX?)9Fhe=BvU{ZQ z!u%THiiEX)I7%;-w|@H&9xK!sBRKR%kU#@nn+f(lvMFOZDVf}PUJnRrQWp3HcdV&kN3)LL;JRPCTUXI-pICVca!X~bcn{KkKmsf zx$Q?#3}=b>vOw?%Q28xtgIjrfzakIMY1(6I!Yq}2_Z^!s7*Be?<*BmB?Q~qOYTNEA;%q;SUqr+dvB`V-Fts)5RS>QD=AWNP zj*lH6UKYI%MgA1Mb z+)JIr*x4kX?Pt46&*Z#%J7hSf7nv6& zTcWpO6WHnp62Cn28vjuzuTK2t_2w~eqaMk59uNH`=XLR)<(fBH`s*(_ZyWy==EU#M z1Ct!Qm_)J?(k^ogEi343D<3tQXziCeSSF=@w*HX3IzK-B2`qs(q%F?h!in11@R!SZLQZR2c~Mj+WB8gM? z9}0Dod?)lU$SGGt-2)%cTX5ckN}L$_O{DE~%$YXiMlRv)c_ri~>WMLDDS21D#tAv^ zOZ5JG{)OFztvsZAdOFzjOI{uK00Pww0y*>El;AaY{qhA!(cbfoANfv(KTIF0{YQL* z7N{J4Vqu+TQb={2e{7*1C9Kzm7Ow zO4Bj^k4k?h>`r!YS*k7fG}GG20tBv-2}He%i&53JgwWo zrqe-VfAGvI$XO@4d7+RxQTR8dN3TAu;2y)NhTLW~JryE)feOqgw~>;neTjYUQu=8H z3~MK1*w#=3I|k-)tR^Lk6l1bOm?0Rkiquqv5segij-eE>gHmC_1UF8j1eR-tg{vkb zO+JobHQcLvi+zOKEL;;qJ#+uimUgb^x`tx#`$4~sBth!ie1d3SvdytvwvT0q8Qx$u zr3`~-vsAs?EIdnJ8Fmw`yy&dzn&^d&JB$E>V6Evw_|KkJr)sUn%PKr}9dlS{=KYB0)oEX!MT<8yNbH_8}N-UP;4JB1--YF~Ce6 zV7g+aiGgHlu-KN&TrmJN6Xt#RDaSo7J7H{p!13pTP0yLQ8z3h=(Z2d<{K1`;+n=rd z`oqn*Ri`0lRDZJLdQf^oc07yI6F5@tYXxe@M)=g`07uHbG<{j| zdbTfy$c0Rg%uEW-yOX9fi{2s=)cwrN#NZYM>!F92($rn1f3>4h!aBQ1pW&O)p#5NB1SwUlsmc%TIIi z*qt!Szi9dC7@FD%)&4EZPs?I(f@5xs^4suqwEXY8oBsn`ZE)EsC+U9Wwc+^W*`fFf zwIUpEZsFk%pq!_12iZ(Im?8F(Li@x%^*d$o38kHIw-J)VR4_qcD&`aDuh*G%`k%vB z71wvZ;h|Pb?Lt+X7xf9{Bk% zvzR&bN#QnkmrNV-Lnr?|1G7@Y)239ZKxWJe(lHA+V{{m2v)p+MJR6^`S3BnyYp*k*uPIdxG$jHI?9BW|0jxwJ7LX8)SH=U{g#%-{fEJ zk{fKgK*8hr2db1&VV2+RX${2nAri^C8+NF<&f6O%73=@uK z#>Wm0yUnenvsMMoV#SQ|s)?7pyg%e`X@m7!_yPF`jC`qOJ#qt8nj<$@a66Ir@LR!e z4ZkUydwTwj-#7VP!SDPJdGc%Ew~*gU{C>5ir{_L?kMRrg{Q|!?_`S>TD#8xf+S79+ zzvK9Qjo&x<&ER)EzazHw^n8imm-$um>)`h+zm5Ff;rAfy*$e!1P0vAed=iuD8m?-) z57}2Mzj1t@20s?zcP_t=`2|UP7{5w>(-_ASNpl9jv-rKp?=^mMM7oLJ6Qtk3PyAjX z>2t?h`}zOX`q+nxGCI&c`q(=`-2Zlc?6KLld?tPD|7!T3JZP8jFZ=WV0r=ltm}$#r zg1=VWW=Z`&r#^g~cqyX~A5?!W@|cq|n~Xm4m`|l2U;aN_KVDlPNPkNGxPh;BY@ZXp z{}Hu><3(8OIPqDStT|IoeUAe0}J3fBF0vnZFYZip#kB8l+_sw}E;4nU%xXFCrJRVnc5)8yDxEWbq>3AK% z3pS3eu`+Al*kdX8(ZP~H>PTAw+rwKe@%l+mF&3FAl>-&BiWI~l6) zc1d$-)UUTF#HwGV6P16g-(bQ@)&&9;USsk0D1fO1u*=`9%GC zT%A$Bsxc29N;dCKlVC+X0qREFreB1gDFt~0O*Rwly=M-6*p6g6VOyM7p$2)KCc!4} zDrIsj6Yo)%RgS7YZ+BakdhgxEd#Zr}z*}bOa{EjlAlI30<>Y?(o}pNOG`yJd$+lHG zz4H@NB_>V(CXq-=O1^AkM*EF zyD9^-Z>2Zsq)b`2$LGs2a1#Pm{2)T^OLSZvYwF0|TSDDiAqS`m1bu zRWvMH(buvS4ewXcZIovl^W|(srqC0}EGvg7PwzVZJz6R>_&RSoT9v4*7$*#wyhsY# z51yBHys%)apkTZS0+T2I%9p#(v2XJrO*oTt&LZ!rB9*ein~K4mfl80J+_h@oB|Q7s zJB-Itt^Ogi@#TIOMwBBT6`1KuAJ^%Qp6!mi9^G5QL>%)&9#vS=#M`7I*)QI1x`Nv8czL|9-9q(!*yo=9z8LQ%x)Y~ z{kDJ=sZ@;{A03Vcni{^7BVTCO*%A6z^+yb#X3REnq*NlV9s|sWz&} zRM?TO%2c!>eQ=Jv0`qdCNpPx0qY;`%7Ad_8o|1qx_LbBe0_>rYnKkY-4vm;AkD}jl zkdb;Pu}9$)v`&qb7UxALacZPg6gSEpnY%hQBK-kS?dgac&|;sod-byXlN;-)eOrP;|| z`cj7fzkPpxGn3G^i-C??UCg4>)0vGL zqNpJ@ish0h3M--&TBL1Z<)5#xd*9C~Y*sPGfjalqdnP9JADU4zC@Z`&NPkfrh+9~hTVl3yuaqja_+HZJ+p-l7*>%FL>t)2Z)qDQqlD_)R zEdODwWVS|i99!-4n zVghUd|3(3pQ&YFDJIi$I;at3wyKFXCrdwflS$!I5qUhG0;t4azN4LmfyG4$E-BJ|Y zS|TI-*hAtkxlP=+TMfrgK?SgI^-u-H&?2}(SB1INr5bC^WF1lkL~iYRo&c{kI=ZVz_mNCp z|2cXA42yGNsAjqUlIi+gAEgc9>7A}u|4AU*;N3qrGhL_dI-x^C!MJ?8ovyw7w&rX9 z^Iua_ztprg`d7s}?p&RsjDjsiQ+qB9^iMB0-x>QBrSJc3=&^k9Knkn`K$C2vri`5- zr&FET8O4~18@$;M@+mK<%&{fQHJn%v#u-4a8W-9^!@u0sGdd7@RD`@QAv{FX4st!d zxT_}EcHNLrV^4l;a3&BTfiN8xDH%LhefM$HR?yjPTgguLj1pjgX|b2VGILSjD!Odkvy+M$rF`VoPJ2&g9^|*zK<|&<6Im$I<_d>);%D!0#GttaoB!FBymKc zDdSd^k-MKU2I~xpNab{u>eekf!#%+W7gT$==drq~52vqxv_$m|GBkr@l}9}LXPQK| zgaLabzQsF?+#_4N!ohnEk|PJ1(r)nnogvnNRYJ?A80@Z1Z{tZlhLC1My z6XbU&JAUaminth)bY=AxD%clUeA$#dXOSk%;b87!r)pbt9}wVUX9TD7*Ex-^eC*^K z$F<(c>X5K%CqWr*Y(GQu9@=W^SBt%KZ4;xyx|}AyZ<7dxydRJxbtI#~s+5jcX^6>t zAQ4ugaq)E-@ygNt3c7=WY?_dt#_#xPWY((i1%fsHhneWrsS~J@?xb{!2hVh^{*6!v zg;9Ab4w({PR;ILxgHODh_PA=aTe+CYm)n;kbNbSu{8Lrmz1P}$@8oH$?I~1aHbyCo z`T7E4RFhkIPQMbLrBa*bazBmVsc)GUe7e!Rg0yn`vFZa_O@oH}MS*T#Au4q=!(;q$ z=o}&J7>2qqX5$bxw?#{w!~_ro;WfvNUj;$dI`N6fs5_Ij9&iH+TrE-65iL&YOMCz~ zjgJnb_r{NmCxpHaiH7kHM8ah4JJke?!&r*V((zBS|3anI zdv;{U&bFv_I!}mKF8#{%&zu(>Zx8#16Jv|%q$n0nne7P(bs^#&v-C}IFpt5(94ijy z{a|t7S{6fZ@@)n5yYM%!so!S9@n{DF>Fq;DSSeW>;9d%!f5S$@={mZRzMnG}VF-5! z6%V>3I8014nh988{CkXwB>tc^?2&5zdHd5VJh)VoeUyQB;-PxN!?-YNXkS3BHre}K zO7@2qqHz7K6iN)jYnh&Kj~VV<*E7Ifz53TMK+S$n`!PUQJt1&x@RrTYFhJ+;I^kFn zY6Op>6T2L*PsGPxF`IY7$8JLu{~kWh77^G9AFm};_;`t)pC2E;x=GzndiTdHKHkmK zpkqJ&E_EEk)637H9ftaN2W!mY8{mbertW#)G)%Dk4X*;##~kqJ^&e+w7W^ATd%bsV z8#xX0ET(MZzlJH})U{#COb2$R7xwJDu1m5!;AL`@EAcJh%$M&vzfV{fHPlxQy>LixZEze3Hw5Q%h){QDWByLE+n z#sIGMpF#A~Y|@ypM+p@XwXn0^ahzr2^c-hYc$#@ksL$o7PmHY`%po zrd`SA1--8=y|4BDYqEI}Z=a~&l;#&jbUhEi`nPr2!JDlRJ9s7y8r*|bSMFW0v+dZC^WfuI8hmxM&1uxp zHYYw1XaFNTh(x|R+Ei$7B=d)>lM4OUj^5W+|25gXk+hi?ZX1Er4 zx8ps5Av$TK86q|2!M!QhJJ2LpaUlUxNAFl=($_Om3i6^Rn~CrQPjkW^J&UOP)k9M0qrOeF3iGCBY3gpOTGdGfjNz zXayJ@_=J9qeTqi-h~QnVhUg@e8ZyN;1Pof_jVDn?2W}v6x@r0*Z-XMtHg$we{&(Rv z*w1wbPvF)rbhF58;8vqxi<91)yr+(_$glC8$#?ofwX5|CFxy>N3r#E`uxTyxGlDpiuVrCC|?amtri`T}q1qwNeE*{@M| zlQ#V=RXo;IywoGHUl4A&yuWfdkeRD;x2(*MO`*8qs*V-qJUiB=$~`D1^$v4TCH?Jk zyU)Zwf^g)0ib$<8FfQW_#^4*a=FTNmx^AH%*_8<@g#n)en&C9t6Sb} z_FeSE`w#nW2+}iDYEF#G%rHMMebQ8is?Yns!qrjLjQ5~ikG zNs_wT0Nz~Catz;g(>v=jOCsIMQ~TwAi2P|5m!vn*rb*Y?p)@6qQ;EhEAPJ816O`tR z^ye2lq|2qwF?nWrZRSo@0&yqTs{E*U{diOKci$QCyJNrh+ZFV1>`FL$52 zJ9v<04LMV5pq=9W5_9UkGu7Nz2oPkdsNHtapv{*Qcap_njZ8b=wkyScLbvj{KdQFg z#cNVF%{$-96lNftB0?<~p{`V9yRvt-D^DBhzR=(;>26sgmL!5dbIy*xhj zh}QJKCtRK}B&1V)y~1T9@9;Mvl5!$!91d6%(A#K2eta+2=RK0$T)sW<9u^07@dxJ^? z)IH;?bN4bzX!kQHa+0;%^qH(R?h%r;UHTGWlr<6S>KX5}w2f?ST4M|&&Dp{zHlIL3 zb;?gjk2Ac(oxCl+#u?lJ7%=9P{Y~HgEF$U7{mJNuEr{HK5qJ4w5_;pgG$ObRBoUw@ zT!NYvG4*WB9nW3lU!wfQ|9pOR%Ikdm)7OtTvT*!4c6Mf%)rUo`D&rsfi0kfJI0~uJ?OCv%!Z5l52b*rxa+Ik2 z%aJG!ggv7D*F7LvMAC3|S9y{AZthR-5G6flqljDO$aXkvK11dc(h(l@2xs>5pv67w zLM@UjcX*e+!ojrQBb&7jtS6zK&zTIhEfKb|k;GNSbyZt~O_E}vju`L-XjEeA;s;rQ z)e0JQ%;AURdG9V)Im^y16-%>cI5Df#>1h{Bvxk+ZaGNSuY0hG%CB2oT<(=*6mgoi2 zI3j}_UmjdmyO;|?2XU)dx^MwvlO@`~pO{mTJDiG|$sAlZd9mslOa)AWJJNJ+@)F18 zGC+!-MhZ>nKW-?o4#8=rSeJ38S}PYoG-?2c39voS4H|2Jv-E+!b00f9-EFbW>VNa@ znn{VutBzm<1VxR&_WO4p0hGjVdC$IVM&Rz%b_5`QWdsX+SoC<`r$AO5wk47H4sYWN2tkSVULx3T9)%C@>J|d6OJi(nhMU%G z^+xWxHCnM-y(zXeOzTZvhpAe+h0{NcE5c|&!ksKTz37OntJmN)`IM}FP~dS zAbAYM9!6bSnJHoruj>8z6lK?wsi{SM@>_bHsC*d&HtIOK9NdiYz^ z5?zu4HcE5?0*xP3nAft(5A(J#o=q>UHrL({bwZ&mkeBX}5UaqA^A@$-bI^ddAhrdqk>W4=;siSj^hU zfI+tEC9BCw4=1+-mn~HqE^PKCi0_*1=T#y$Rqw}kV_&)(C;>>u+$`py0A#Ra@*3rza^=j4UyYX)$T`@E%%kq=-LQrK7!m zGrgbFLUqAg$O{*6J>x6fx_5fo+{s-%E8W;;hQpn+5jA() zrqU5Rjz%-t_G+m!7+vOG@fp(CWz>Pa>yep1(nox@{-t1Q{r>H%{u#3Yzl*EryWd4i zA3sO`s{a-J!xKy6+>*RrPBvtC*yurydoDBg%u>fh0qvg1@HI8=8C(#smMc7*_#QR! zF~E(?mIFS=!O^kUhkY~i!QpI?e-3DK`Vd@A7x(4oI8uq?xC$o`(apc z+q0$P->_k|8j+>q!Wa2d!8kmNzZrhKJM3P-0@YqzJF+KI^+9wJPs4N~^AC9E+zCID zpEOExLD0@G%{}OTmS19(R0p#3-EpS?KSUVoW1A^6b~kr7^zI8*W#KguU2nGj(4)}s zJT>9p>}zg$Fu|rh^I5b1o7%HWcpOIk;m;3`KKrCkfX6QDU%dFgyZ&enKJ4ce+xpW7 z^cnB^>&E9ri{MEZ!0HUH^f~YM+3@A&UBL%>#}*ib@vY#JwXahhn_*86Egx+0*#SG# zYF~Gz)otXSq7G-;RFCHi#A;z#&_YUQ1VR}p3o$!11iklurat)S!~fiz{ug-#zh$*4 z2re5v-WZ`^7rU4IvBe*ET)Df=`h$9*4}2W=tIP)^b1%d;gFTuO|Jk>TO5Tg_8UCnG zdqR&^(0YG<=(RU}?|fbKn|}#@FqE>K=-0;V{LZv5P}{bjuf z#h*9!E+2b6KLZr*`DNNI6i2FFi=I~-TxRz>nep8n5lanwyjOofsqSc_)VBEdS?1Sk zvAeS?Lk_>B_L%#4Lh(O_O{-+@@GV*uai?kf0+WxScjGFy!heolN#p*SSx>|6^;nEU zp!YB`pdI+7_eRl1S(2MZ9?}=TGb7FdvN?U|B@2+Z^uhm#dm)t+aOKy=*ay@W`#CHU zSkY}knYmRZ^?Q&lBfrwhYo<3#jdcetBZ43NSj*xqX3_nklQ2HqIiKtrf6{V$tUC+se$uOd(vkv|{1FBdzzZ&-F5BV2Fx6n@PJfiNE{F?N;q7 z7wVG%2{!+YBG?>`;G2O-eavSD3%aCfv1hcYhTyr)QlJyGk%xj9ih}F82w%gcI6ptc{*-?lx{xF{~K6|AqVBZh(54``&hB{hbEI zZ@9hwGRZ5ynNQTjZ!a?#ZqX}ly5YnZUPWC-405a5Z0S87S$Tgz4IRNwXyFpRoSuTU z(Bq>}qngga?H|}U8v?2AaFNqEN|)XQm$f>LUHRdBBUC$TVM$*73e%}5{LIo$skZU( zs?uEJ3SPX^h_+zUZ`G-`?E{_0?fI4p1jnvZWb@OSoq5qYx{ly^r}3?iEsAbhgGXkh z&B|b=suLXhg46g~Ho6bJy(IXZb^O+U5d6-VF8(2AewN#jo?VN&u#^?+W9WIjn}9a- zRUK#X(s4;Rc*9djRic7tWZ)@X5{pzlIejy8Ei{z5Hk4)=5^$Fzz3YL7geOEq+^Y>4 z+74clc<&)3&JeZ_*{yvN87d z-cKkRs0z4exey1=IyG-r3Dm)I8W2~J(j8ZyYO|QK0Er#gnU8~SUC{{=WiIoTm z*}@9FyGuvu0il^}%D2AUA4;0~Pt!7E$8UpINqp)s79Frz3q@)v?-b+MV#fQ$>|u*B z4T#!ew)U<@-1~YPJnsrk$AaKZAF3aT%I@8$M@W_F(;5;P_H)G)6>BN2cg>GXe-xzt zKrNSkRnbzO(2xuD?iw+n`qQj35~ctz-ER7GWo|<*Hu04U`qy2Tt@|olcPE4C{q2XQ zZUtrPmNRM9y*qW!Hg(79Rq+s8v66bzZN(o3a{&2{wgH%a7<8!yU7GWR%bB$g?xEf? zYabyYjW?Go5`Mw36|+syHW3p)sQwna>dM>rl* z_-_IvxT@EWsLduy&IV0D4nV4_8EfV2>8x>YSGpQ^={6p2vy#JIxd}xx^<<=Uv?)K{ z{Aa-_QP~sd2bFKb4TQPQz%{Ljqd;r(O5?Tn;c}8fa53*7O=tVE#M=`xjx@SeV_r^3 zn+E(S$$~4-6Ntp2Wsr7nN==E~M90?QaA&$0MeSTvns=!&lB?XNT$<8~d{vS0Rj%oI zytc({R-fXvH`#8*YZs997OHLW;%pgc$SrCJV(r+E)ev||e132wN@%yx)^v;4ZS3zV z8$_u!c~#SF6Ir=me@I+NI5&s`KEW+C;t3cFU6y&AZSB7mQHG&da9by+44aUv=}JRA z?V%C4KO+4WZ-7ldGB zDtF~~Au@Diyuu9_T!pv2Hqc2N^tIlmC?8Exvi?oc=3-CV6h(V?nu1p-hxc<^$!g({ zfS(zgyKV&H2j`4Dq*R&tGmc=PwOc?=4rcu8|yQ zf1*q!tWY|%sZ9M3ZJM2~*GQ(UNFt6%0uhT=k{rDu6Cxedzuh8n(=5%9Y*V9^pQ>>O zQcu4Eiq*JJ-Ov{wQSTBCBdA+m+-eWzr$k@zNr7VI$lksWnJuZpavJJi@H1@%_0QKZ zP7c`vRz9nGCK0J))D+^XY+Op54ZSoT5PHi>4I-I3uT^zV<@-=f3HkCfWukB1{xA`L z_9MJ%Q(0=BEs&`p9@6`h(ik?3%&To0;tb>Vfa0`IRl$q1aZBiycPnwE>(KkZD_xXy zrbX6RcaDuW^Y&`RPs+xd$u!l*o5?gm@hAB8R8vo1{UwHjhdb`^0_!SU_N;^}CQ)+A~fcN8%TEVtKLsF35s8{8v!sEuW`xdds_$XV`5jtA`GUlTBqU zVovyT8awhM!4+NUL&hbWHp}!;f8m}djkvN`{%bD$INSYClf+)rUDNVj_?g$yaz}zS z8`;T@OBBpS4Eh*5)}HF=hlLa8KZeZBzn@opZG512?1MtAlSy&?@<)wcAIamkvGt8Lex8t0z>adpV-6l+?xho5Umw_dgSF2n%5;RFDp)e#o|Xs{)&~UJGkB*BH=(S0V}+n&D@n+CVpw;Pje11s(OJ=-t{=r8(G? zYB2Meo2fo|UTsSUnO7h8&a0=?r?cJrOpq0S7#w>e2gf{cO!&oQYPa}x1RZSek6)Tk z8;litc0Pe!?@V89u#5S$)q8?ygI%+PGFg;T)ICI{w3jw0b*|!b=2L2&xEdK=vS#dz zLqm-u!dHI>rAzFi?oP*0XU#O&a^nJF%NQS7t^`-cSXAk&gi8GuSf>6AsZBl4KVe4( zfvPFNAl9$$(`uh{-?!06`1Vr{T2XsAYY7LNT7^p9k{dPmE?{C@i8d)6y@cBqOR*bV zV&sRmZ$n3}L2w+2zv?wzt9q{+@oH`B9g+foXU_ApLK8OMk5&{rqHD z{v79xSv!J5bRif>pQ?iY(BzA-=sGM9wiJ;GH_JR~65ONwWm8EJks8e~N+ zNgtiXKlE~1Vw~WLj^zTR^UOvJb)CyvJHrEOo~m~8yUdip#cpGJiK?g8=|52mQokU% zUPiuDJMyn7`G>doSPTKvF<$pTtngwtaM|uf;Y3Xty!+lL2dIK8$$INgTsWYv`LEFj z9XCjOXWbiV-Wc5tmjET{Begz7;;%T3Yt+6qW+8LX*sOaua@o6`bptPdsydwiCl19H zOJUWxQtdl^=3RZ@O%sC6-xO7F$Fi`_>$k3MwB`@J>m3CAt!f%6`7Z0{TiNyV6|;W6 zp0j?UGu8U3i*U0GXjvq|TAH)|nf0^C>%r_yM@M~r!T4~2zI}}O)11~BgLs3C3t?U* zVMpX00)8|-rS)J}D6)F`k5a#+ zB%#LZhB402JLt#G_%Sn_7?VX0LvXVE2X^Y<$LrChz3rWKN2Iwc3d~lQq>sR0YM1!& zCiwAd)4p*2vtldy;K&_W{L8_Qc?KTbmD!)A#6l+99#+Avy{q7pXS)xZ&&&!4C&>!9 zC4(he0sHpHUk{VL>!Gn&%a-pi{!i)9N-uwzSr5Ad!WEW$R>dA~8Ht2HDdD&HSKfoo z=NX(aoiP*IxO>Xz*;i%hkL;bh!-?-&>hkj}bpd_Y9IQ?q-Vc3zdxuEkT7y5|2)k#E z3RgYV2YEIbMWbxPMm&#f2mm8b&?z+Tx18kyX>YMDH-|=l>_rB*8p-T(T z4ksQqABpo22h2hzyCrrkP8nQoW#qM-7yL48i5<>zQh+sSY2%LS=?{e-mOdH1F2^>~ zL*g%{1N;hlR>fazdzDizUC!3^&hRehkt;?md}7*Pjvas)xE&cio+m3$S#(GqMkQr~ zLix|KShm*0b(V*#U-7g|nZYIvFb8j1NtEJzjN-Kpv^JLH)y7}f^4InX_u&PKxowWW zXd#PDfL(Pr!t~{`&RL7x3z;|t_5?-2e?Y35_{KJGP)+=m##c_)7UlOK>0vt8XSl)qZt$aJy!i&U2`((*-*;j3^zw=+G;jAy(Kq5kC7aJ&mJF|r?r z6HR4-QZl0W2(v@cyKc0F)ZIu|6;w>n1J!)Uj`b>qlqjU$54k`gr3z{FLm~<(R|p+6 zxlU3@g+i+RkaC5LQb@fYvadp_71C-$f=xH+rFY!p{_P7Oz;piX3n%~FK;?_Yq5MCl zzqH^VLUDILJy3is)bO`FR@h+E5mZwXPwQ$#_Q?kYLiN`lRGhawkH08RJ`qXa(UIHc z!zVk%m}SKC!;RH}0B5vVzwuTd>+~*aDq)KqJ*e1lc{MF57(KV{iKaEtgF&LU)S&R- zrY9WwmNo`n+4<&d?eyb^+xSrZX?d}s)|mswtSL2d&md?jgP@zu5QCt>8}|}+<{~M+Q`l8m{>8`7qFMWZ zpPM!C7#O#MRJSnU?r^U@$lZgqwKEcc(K{!CcrzuSd_{ah<}(uEf_<~d!MqF}!% zJ9y8%?_oaQ_U!z#U$E_RD}T~2e~Ho@s>Uqw)A;v2-2NL1>v!M7k5z%_hSJyD^aAhj zY(2HfC(LA_Kr0GK(K=;_12kv;GFY!<)9uiOTAfFvQ;XFmR@zP`LL( zE8Io+1593a`EvI;y@dyh-&Zp^aX7NU7|3n#uJ{$Z9hv9#cpW1ocfKI^5PXAY8Q}5>_=Rox1^E9zH=Qn7uUiO5yla zWRw>p!;|aY+F~k>?h7nfP4OF+Sxr-K3s`-ER(%rvGUr>0A;<<`D36Y~<3~7Mb;~f- zy?8ylWfqg7VB_7i*m~z7Db%Q0qHES9#`D(oqAUzoF++o~g$z$XWIY}c0rpNX;j7w5 ze+aKt__RT%R#pX8EKc#nN6Y0D=g;UaP7&gx5j(+5jV^5;T~X*gD>DLt2Lk&`c8AKM3&lzNwCBawQh_w#ChAy~@8 zgJ~^l!?-H)bJr<1f|qt?J8sBD)+6s z^lwIBc75)P{|)cRaU_>j8;Y4IBEmE8D9sgyJ*9VWBmH_XRVXxZ(JBBRR8?Ja3{n1)sX%@wAM3qWV=wxhP`$^ z4X@Ru&wKLKZtg7Nkq_$d7{DXHy?LO%aFJ`2i1H{JPm~g*10Q&N!@|<2KCy=*@DKS& zwE*Dp@eH9d2DO9?z9}0boJ*JF^3SFneigxGk2h{F3eMXIWZXm^g~r#myC)%ft>XF@Hb2X?P&xmEBCE?d>OEzt5}@WxSm zwQzK8J16lr&aVz|+P}m#Bj96Fd{tYzICV9sxgaz@S{A6TZyyk9;U&0iW#cyNx(5*j z82(Z-|Gt9-hDXGhab_dFhJdq+pZ8Tr+ku4Vo z0s#bVHlv&X8v8m$x!!Wq53z9YFf+}pi8|-N03L%H*j-T2;fe9h2}wZaM|V{>Xz`)leT~)leQFdyxYA?d4ym_e=IZKL_xJYVYtzih#OB? zncg>NzjtP`po^LjcedA6b<0tFsPrtitV=St*TV5XO6R)lGLgl%yqj**Go>Wyq%RN3 z^vkcero}5LDl2L+y-*`Cq19~F!R~vh1`u^71giW7TankeV%Pex~i_~ z-I*rlVupJKu$^S}e+Xtuc?&TCvy*A)c}*f?ZD7BdP^`Q=PpAuJR~B!xGpMk5UWV{7pLB+Z`}g#8 zCl)&+I<;EocWNEioC+3?E(s0`mCPSq(GmiyCNiDgW}-2NMl+q1E<2ruWT#Ue?s<1H ziC}4P2imid@yo1trcXLe8*KUwBf`2SeY&}MzRta&T>R0f<;AIO`*i0!ltt0UC?1{|wM2WyzJ79BuZOc*Q`XOT&hEr}RdezGd9;(rG5{ob&_pNSQgC5on20_ zZ8ua6sW{=i_!f@;B|IFefggc>Qs2gnaIzMHc=U7O)A6zg3}QiSb_Az2#S zGHS!cFhY)muPH$MmYL7iaJvy3~3xN4iw;@o21;l^m^6PRo@wL&X`%?$(t zWI7hEdYS!xw2?hZWMmfsPIE29@j^fiUDoaZW}%7X(#>?8ojBpd=vE92!owjtUAqEw zcm%m>o(doqp9#1-ij6`77H?p%X*WtuAD$^sc+VCr*-B`S#&lkQ@zinj59;FGsavVC zPycDKks#6J*kBT2&fA*)HpgKjBj3}z)BI%gYcx7jex!LzPJ3}blU3JPv)X4~#ov3k z_Eo})af!XvUa*0tM)xwUR`tE(1G=^fs7+U+Ue{rmT#OvPoAG%mb1zQ192!Qo?0xfx znuqdC*V7dqE|(}h-qnOHLCm>&R1OJ;f$-CT@Jm4P#a+(unsov110B)aMP#r|v466$ z-XHs?VulRQ_m&Zy+x`sw%AB_y#raJ7#HMbW}FK zPerP>$4+9zV$5*Ld;)k-C$su^2$#9lq^=W=f=np(zgnCg)_1(+h26!k!$+sj`gM9= zrZF8mM2%L+_i1`ZdQbJlj9DVm!5-lC=+Ml7GXgju@kbxz-g(UDbc7Qpyv*=I8en(4 z^I=xtaZ<)DRPLa6nBja2E1*8OK*f`n_=vK$Pb4>QwAH*E@b0sr-hcAU@h{P9@4HBa-^)3Z7)AvTDjhFOHhcy)+hVhM_GMX0Pv&JXpNR+=HF+t z|G>Wkq)ldfVk^FfW&uBgMo1coAJ3~<$LicKmZ3jmCS80>UDX@Grk7deV3U5_oM8`Y z7i8&GI1!`q8@$z0RyWS6V7-38U|H>=M@tIw^Ssd@8U3kqYbzMT1#lzkVI;9g8Ve)T z?=%0y$(mpX1;}(OxLz-z_L^X4zW#5_SD=@P-Ij^%BDNsN*tbzvU1$>UL#rlhpX zp-(D=0!Oa#`D4;Ib*NYzEjB%6UkEA5gxD8C$}=JMg^*F15WUcNCi3>`*zfPn$Gb;- zT@!a~&8JP__)9g3vwNzyB#WX)#&)VV~gjAB8nzlk^l+;6i})HD(o-{ z2+G9^`TyRv_MS@!cx%t;^M6hrn7wX$t+m(XUGMv@_o9Ih;}tTO!)JMr3t}}+=bA^Wc62)Y zI^Qqjb4JKKEmXB!k57H{{i-9KCSC8$A81j?1O*z#rW?Ze41+)(!uSk>(Kv+h8CJlP z@flXclkpkWpQj!ltkp9WHzM(6-T4T>Z$a2BF}evY8_cgQFPKyI*V^C( z#-#&qD4O0q zx!#dJmiybS@r%7T6dAEs7`?RbQ?QK!&vK$Q({rZv%{UUQV>yBUCSkZM_X67PX)ZJ0 zrxVc!UK&ju_M;-bXRh$U7gfL+tVuYPX}oqae((TGk4ust{4;6D;Rg?vw;8Gr z=g0@4ZP@Kg@`G>xdFp)pUHHLwUj4QB!E#nA6CfXPE7&+gX7maWIc|%u&hc3WTPw?4 z2lnmVKlzNH_1&PjCp$BULOZOxBGy+t1Q)75ffn&o$<{KeMGm2K4zd$E1BVwbZ$}kO zdAao1y3$$-Hj4T&Bk1HS9bRFV0Iz_9$8}@^eh7r8sPW0g(t6Okg06X{;928PBp9?7 zOW)?43xj3gF%0(9NSWHvv?L8NXS?P8^ul(lpY$JbHm~YR-wlcd{+hyHR2_cUmx_yA zPg+AwDb?~2HDn$`c)&#-DlxiP&NGD6BG3ue@n~Z3lT{)Na*j_me!genBOd}gicfNv zyRz6piWy0y?|lst@KKs2!5& zJJ#s0T_z*R=QYAW_Sb%5Jah$d0)q284CJ0@%*tB<^RTaeU!K2qf6EUz3g}+Q zDhUpZToQmCDaMzw&Jnc04X&5e0#fsRb4C_w_HhI(-sK+)MlTFxc{&{AZV8r`F9Tnu ziiP~i30cVgIeNs7t#`)GSq;8p4Zg}Nz zGckN8i=&4ujvlf&dgM$hmi1>mgKdvpeEwdvwPcgor`?jD;Y+q$yI(VSWpaG=d)Y0m zk^7MFFKn_eo~@a>B|@5$JjUrv?NKgq1V8P?9BZa-0zai@hoSp%DnmDsFZ^6zW#f_P zA-(@#=&HqJWq&f2?ZXkOxUwQI2v=61T?$Db9vFpVYmzAPehMlaJFZkq@y+=eb7Hb2 zxo^5@&U~rAw00f9>rj2C$XT^(E8ppKXq(ILUR}Ao6W?%bMf4;1@rSC8b()mQPp@2F zLT+XKk?6;6hgF4geEbO+9D4;nDfqdZ-#3&)0c!s2$2IP1?Yi_{0S_nV&(={q$)7Em z=mHcEWFU)_U+ac0eVaGfo@V&6rVsCMLY_lyThNo%4J=x{aR07sS`eKnFxmSfh7SdumO6-6}(gZ-cufcoKiIYHy``pGxE z^hg1ynTKVEKvb{13~hiU8x@~k0HRfbXYNI11D?ovd6YOWFC*GLWDUZiYHI{e=?Z)4 zEE&J8W#+|&4H=&Jb>rs&f_mi`c^O7u5OTqVApRK5U!IK zA@>wEMZqSk5VX7wk56cw`@Le(Wxr9=n{@>?mn!|Ht_}9tm+`*f8RkQ>Hq!7_cl^X+ z9%Bb>o;@?_PIKmxQZ;(1d)LNfd+qOvxlGU9!av@ydzHDeosSICbV%~ zX>f39K}JS&SS4_i(K_>S4{2rpQwdYgSOl)We!pu4C$9%^Iy^>S4_qr(^12&6=QN>S4{AsAKA3&3Zt` z)We!psbli6X3gW*tdd{8wN~fo2MkPeY?J6=K-M<~gp12a2DW zR*srX*ORXAr3`8sQaK3Qp2js+#lRz)6uOZOe@&x*pc?QY4Os&b&THHs-7FLS2%=HP z&-d7tEB&eXql3@)CDFy_Jyh!8^M{ZWmAb-GSHd2rwqKei-?5r?M0lY)P>!RPx-bysWGJ+}+MI5|GQ|7U8!I`S4dceo(t|7Z9-c#^MOe105gmR-!U zJ0U*bA2!&D@%cRr_t(bfpX6nY&;OOsH^Jxc;C;a}oOLsajrt>bG;lfXR&=A$iadTU zCneI2DkFKYF(TcdA1k_1Sw$W{kvyW2Zlw|`l~8}lEsXL?hiMN@${l#!2o3$>k@KNA&4#wG2qmd^%4(Df2+kdZAmTN_-(tKO=c0KHY7B zN_;v`J&DhgIE5ck=`ZBz=hJyQ{poH?obu{P`GrpTMNWDBd^%61&s<(d^2|?U=!}~6 zTo&Wwjqw_ayrnp-6`AH?jE8bJ®k?y&wmkth`J#Y4F}9L6XOE#(>H8D#QDn?pwv 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�s@tTCh*qfA@zPsh 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≥q6_ZpBvS{G75a9}5Jmhnm2m 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 z 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ðWIpXiq6x9M#EUTJ)U2I+HoT`IrQ_^^@RxRm^l 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

01L;sK*IGcG4$KWJjn*<3fMddf}Q8eH*M&uWJ( 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

?qaP^Uaa_}DC!+@)+qJi{&GjJ?CU50 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

Zs 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 z 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%0A}oBmcPnu~U6Cl^HhD&0_R!lL=(J)s(jY<+gy`t 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�-Lv;={IMnD+i$kprEp@2fq2)l#+B<--_v=*Hb^ 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

==8d9SfZ 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

@6cTVo zlr-O+ocfp;KyTFCC6mqkz}dXv?9OJ#Zw(8LW0$HWSc;!THc^=Kdo?AX zN+&;L$b9%tioaAdgZDXB2;E`j0i!0+cu{h9p!I>qmMqgiQEhNdLLFPjp@@DqX& zhJVuh3e&dFh6&DO_-T&V-W0K>vp3u0C|GYODR5YRCpClRXD>$k#NMn?K?Qyv|Mch7 zLmIz-af;u^KV9or@lQ|FuO|J|KPR`pU3b;bNcl5XgcmF}v3IpDYN`@;#^UYOqY$G!{s67F9HIWydxFyjZ*bl9u< zYk$H+yw>6V-{m1OO7gsYZYKBd;K2og2bfu5gS98k1`AvjEWrp%y~PNNw=xMPtr0Q^ z|4(rl@XMS}=r17;9L6V-$qfEqHB9mUG6_2J|G!oIKe=GHPQ?H7*50?4ZeHGz|9|6) z^Z$d=`2PWE{QsG0{Qt?C{}(UO6`#fY=hL$k^UsjAXZ}SVbwd81&aupYHvdn)6Y~E^ z(~KDr@V!(1|Jxn-{|h_k|08X~+cE#~&tm@RZ5s362ef4z|Lx5GFNGKFocXU&uRe?U z|E|jQFEIZ=T{FjeLu%j2eE3Y}pO3x>^G|mBVnV+cE#!Mtwo%|IkXB zFJjg+pXJlexo>ICqtx`dng4Hp(N_FWx8lUrccuEzWd7eIlQ93Zw;l68M<~B${&}h8 zJ&1)J{(nds|9?pu|9?&z|KG>u|K&)IewEAuCD#pEsYA z|A!_`srsD!fB(+;|46F}KbK&%(_ zzvPDh=OX++nKSwS)N8x6I)ne0H|AEw|H}*b|H&@@&#NT=ud|_(6o}iC$Rqs3SxxEu zKM%tHv$b*#vDExOtVEjPO!)uhn*Z13OF5fpu<-w+IQ+lV@-nH4|6i8m|4HtY|3|+i zCRW%4bRL-I|5!Ylm#@^;Z6}OJBX(Yj4<1%8)`M{ssEIHQZHazq{H@AyMp_Q`E+P{z z_;b>DZ<=rovO`P{>LQ`Sl#pMz;geKs)aR5wErTD}PgzS= z3$YZ}&}0bPc1oL0^^=3OeO!f(u7z?=ADEmb&uqaE)rqflM8yY5#n+l5DnX>6sFbE4 z`#BOu5<3OK&yjGu_#jJM`_OwuUC}|*BOc7YMM zZ=twe2$8D!SO9M<*y)c>tWR_!zT4FKjM+T<@FE#iPGytuTS9z+PgXTz^Lp73$gN29 zr^!EwYZ`s8$t|Oc0-Zo2O0(d&ohY_2$~PU{P7b@)nUiN^T4tW@(zguz%)`n3&gZeu zJQmblcf3vJyIoc%49g(T?_^)oem^k}vTR1jV`vsIUssB`rzfZfIeYT2)Zp$VqKo_h z!2TVoE^*=Rmpf?+0??S3pk^ZmCwnYr8*CW#N1_B zH;VHF_S+}@6av?H#bC+Lb)a)q=Da)?InRBaD+s6Z<>@QCC@r8+&xj+X0rvQRu>LGo zAi`eLm?b8X826}JlSY+x=%=z_6A?o)w3>irT@ix?IQ+ruvliuH&IL2hsW zS zt5ymJ&kKxM{nWEm$#bDGOFTZTT4{*P17p_5v@t0Y)2AEvXFzaWQn<>sNU(*g!d0#c zSGj836ee0!Xow|{no}%1i;TU!V6nhOA3HBX3p0gpLSc&DsYjv9Y$AUm1V~6?5&=Y0~}w#fQ;F z@}ZsgS0-JxjGT2DHX}#EC+ot`Q-x!dd0B~`@X)vVbowU5?IH?NI3qPT)`pxmq=Q=- zc-v1d*TSDX3qwW8=8w*i3(_l@`BVNPDDt#M3XXg~>o{1sn8l(^L)(ar6xpaz+npD( zD45$sJnA)Kl?811c<}+g0Pc&v@om~OKs>PreUE`jSQ znyaTZzP%Ot?_?25^gHu}r5m+(0gUt-;@^=3)!tt!d29w{i>R5ipap2|=b@_5Q(RTo%edwI)tRGnj; zS~f>MtUAqWU6JE0Q&C>33OFg2h%G^V|omdzntYIx)k$ zT0;O&qAL^#Mkvoj5qchVV!6Okh=3EDNfqlQm0v;q6rCS2=uy~A0X*DQxElVVj%pwy zM?!;BpQtK0)*K^ZRfRi-QoSC;RB=>lO|7&0zAUIoiTWUT$CoE@t?p2$qI9Llx;6_V zM0FWo$8y5s_&N?_QCH4lQCE&)QCCi4QCALPQCH4kiREeIh4Axkm;ZLczCCax8xAwt7EBMLm$ke*F&^44N zO&Sp`DD;@P*zRhLY;O$#2&r;jcF1Q|RC_S?@QOAyH?lp~o4>=GzunaWYlf;W7TCBu zkiXs&y}C=HpU>PF|1EIhiVRbcV+!$_8+h_%Z=^=)7sfL;qVJ&S%#vO6e-#X_F2*VI z!Thzs{G-v)U1&gbMAu{+JTKC%tiZ+{8I9N<$Zw4-%_-X$e#RM2tD?Fhf4{eEi`P=n zhDj4J88xE&_LULg1K+=jWdvsT4%p%yu=z-=Yv^k#{Q&73+m?CCWD=ZAG#f)FOpU;j{v?Mdk@V!^a%r0wAbpp|xr?ja$18c9x47PYEa!3l;w*Zi zGGm=|?ZU<7?&H}!)-M)^SM`<Sbi3>=zpeqvj;aH9@OI1L=<(kQHL3{Mdf)MZ^QZjLSJXq)!~WSlf2y8ab(+itZhY1A z#1{!ObJuciA%-p0=!gb6Ay#9FJTs=wv5vXcbesLf&0v)Bk;ZQP<)NP;|FY?wU-ZDk z;C<5MU+;Dz|4Pe&R|8=fMMBc`XPF|f*%Wv}5g0zX91rxj*_ry_O!?Q-^XK(JS); zWovOS>qc3Y3e4w^3a?r_m=8#d__gG_xniWj}YwU#q>sy!k&1>uZa_(JOJ&NVfb@-3r!Vtv$261OMPTRddhArsXBGqWxlf8`%k@Cnv?pT`G1E10q?YP z@Z+8zh~Ai|oM>GAOD%$^Hja_k`2Yb17XR?g0F`&4qgyZ+q zsvhf#{@GZF9CN~=c&tbIn^UU;)+0p@14i>}Oy4YPY=QN(a2jP>swN2M>b2%n@#rgC zU*(6V?d7#5R{P2}R}HbgS@u!YAg}eq9ADYqstdiQV&Hhr<5u7~L@{Yb^ekuiQuu?f z3}$NVbvc_@+PYXmJr*ATnM;cIOH+&Y1_`@6f{22U}FtFgBr^etQnbsGcWJfr=h(@X*$DCN`&+Kv)nsUKf*}*fo%j*nj&ugb7z7WIoFx zU7u+F_Y4_OW!cs3+JjvLcz79Al#RFdJ;ub4YlAj=gE6iewO}7{03!Ap)aBMj!}ss} z#Ax06Wuv9wug2?mHD37~ub!##IyyO85F*Gp9m{d*JxAj><;=Q>CE8Z8K-iN*GJ9tAvx1J1%w1Q%g3To}6sf)acL4WPi9!NeH%Msn;mC7FHJ zQody0UYZmq!bBKEiO80=aIjz$ig`}TV(_#Z(8kzyyz^CISllBT$9(;TycuWJPF&&3J5Bufr5Wp(aU4ro9n^dK6%SdGGWsvtD~h14T97eV2VKvAI2;pUwVD0b|N`VjC8 znfO3Ok!Q$CvPu;v4S8@ppT~M!Z&md6xSA?@cw7yd^#JxqSV8>w<}+UR)qix9ukxRG z0ZC8>rIh%s&%+6uV}$mh6c&|(B}iKe_3 z?MAS{Lnad&QyBl=YIuK#`UfK`xC^#P56?d+KByrVGc%x|yj6P;4y8Bf15`XNM(|pR z1>3sTxZl~w-j69|3KXLEb>e?uqdjiCTK{49%B>-ou=N=GV9ORr<1u2D+ZbSX%~~;C zYletF9_!@ez1GkYGeo5ESXUfB$Ld?)wfrS>JeH>bXQHr#(*oA=J-r76w!x)--mybO zk7jfmvchLn?`DO}8F?PpdllD$i6ttS8Niu}a%}#H4dzL%H!8}^ON?0n>cI*)oR{Uy zyu^*gtJsi{M;uWEiB=M{Zlc^4FgPjxK<5Q)>`E)Q;X61gdIPV1hXd{w35)NI9nKKr z!r!#ZaF6K$*!0ElZuRZo>RP^(jUPzLbRT0&HiIBh8A>azRnB_@he~J_w5PC@G4? zkxnNn<=~}W2StfugA9tombzW9Az};s0JJA6uY!`iN}_`1kKSxd1Vxd$E;T)$Y0O(q)S)YPzK;Bl4OUU&*u}*w|4BbbW)h!%TanB)xxh*-0SPYt`jU8j&)|N{} z-vOIP87i>?AdF?GMw zq-92_iH|dTstcBme^PCITQAqylMtDhi0cz%R6czROkVze&so5% zMMhgy%S@1krB;>~P08pPWUO=Wh36sQb=`_X{g6cMskj&D4onm~dk!Y< z*53do?g?#$EtK8b@{K^oW?X^@zt523MC{4TuoP>oZ>Jx|@ zWxxFlydVn*6@)Q zz7k|18gtl2T(9ISd+`w;A|*u5sZ)^$aY;i5?>Y3vr~U0$5p80Ec=i zfKxpcz_FeR;9PH2u9CM0pV0Tet)Bo1n>Qc70{jZ`E5@$`Kj_DK%kYDML*^auhnQtr zL?@q7KGtn`$D?B;>;4`vU@YjD7$)!t`ko0h|AQFL43mGf_ovR+_ zbC^Gv`)^^LAHB{T_lU=}5%hkeQK#fG^)O%woIVm{_fASKDgbDT)mkMR0(a3pD6Cm}`LhJ5KbYuP~k{VC~+%qJ{a){FZP6S60 z8nAIBIQ#9(KxjyCMm!A3yZj~BpZ5F<@0*o3gZMNl0lYfr4fax~lrP0}mvikf?RmJ( z@3_sY@<0j~^pMvvz==6ouJzi2kAY;y9&)o)TXX8S9tE!5ZT!uREd_G)P2soKoPiT_ z6u_;$mG>_qCw5b{Ra?-KH+z;pW0bE>r&kdBHF?YdY(tK?Be8+EHMh|+02%im0#otfp$)oTLBjqG_vEI)R92Y7%CjQkcpj5P3% zO-19}@c-mC)>Txw%`y3)NVTr@){IPJzc&ASf@xO)(}GJ~Z&}kbwfUc6sp)VRD!u*f z;a$0t-KO+X=sg_BKrl$Dv0%A?cT66JdBIsi^GD=+xiRc_-f0kvRILJLh@dLHd_a5= znw{lZr7eh(^~1ZB_bI_zxh10lm5IJXeH3gMRt5kqq}B>A`f9wj(0KL=)hFynH);zO za#euI`EIATFjU`DavS9j)VFlwD&aQPx@nNVwW=lLECB-0Bz_s=R@8r(Q?>O}*y-T{ zP`?%Qy@tCZa5=KOHuk#BWol=1_Hm^5)&$oaVGr#h5h!#El~9jUR$DK}ns8+*%*_ps9`r!R=@3YBMuKiz_>ar@dVreEpbQTA{`OIdu7nXG!loy6}W;qt=&jA|^-#Hk|@-WP3>D}m#D)=DlkFHxK)+EMn;P=eE6zbQh7VWy`!S3G2a=m|L% z3{#s^P-qhN^z{eMv6?_akJNt_p)&{ zvH{B9f^(02==4C&TJLdfLd+&QIK;+V?*X|^`68pd4le7=@)bDc0j@iX!9ukdz*~f~ z76_}l7RTx3#^3FmXLCi^hWq7ke*um)b{@wl4O?)W<}lT z$JGo&6+hul6m9l_ug5Vy+w3=ZnY_@tD{Va(ljp`e*mWHBsuh8RC5;75oOg7jKbw&Z ziYply1;e7)8VFX5hhjbpB|^yx%uFb+;oWUsmIp3;iQFfbRZ{#%CV*_ z`~+`0bRb!PH609=h-m4Ws&q|%Qm;l(uV-`dId(!i{emkyrQ;%uEs70*@+tRNZsSe3 z2I6@|8on4S`=Si5>BcGo6_*myN$k5TcQBpbSXqFt&e@B<#}+Wb+*wBVq1WYRu!Ou_ z*ayl65RmSn^MmnFp03V-JRcHf_dM^=ns5%kn&RHBozJU-5Eg}2@+n~)N9!U~TC&ujfC*rj8wa4~9v z5uL;#5|r8Cek3<>JmE2>4TfGX($oyYL898qnHKRl8U^{pd8>#iebDZ}(IL!VqZatE zg1A$ipLkt26;|ZWDTbzeQAO^YLMYD{&pNT(oI+Y0+yYCY?bkP_px}#im+HY;0*uEF zktza3-XSRK6m7vzu~mkia#6)G+JYY=4DID&&8nc>4XlYE9)x2G>Hdlxubh{3MzsYH zn|6s~fC}ds)Gvk3vVqC<7o_wn`&UaxvV#`45ABicA%$cQ63NKu$`o`|z?_kV2@1j- zAu{v8M_cZRr%7bd&lfZA^s`iDGLhH|v(-yzx$9QET?4D5C- z>xdr{if~wned=#V&}3|hU3}{l*4`$pyyQ$=ZqGF!CxeY zv{w`r57=W=k=Pg}c_`y&BsIGAVh2K#js1xbnlyYsA%hKn1L%zkNU@9oyOpu zUQEV?exs!wzPGdQml&h)bTZ|YtGU>|Shd8*&KIamBY$Z)3M=g=Lk zn*Kx4cRMz-H*H7tAI59GFcSDY#nY+ul?O7xA=UC9eDqZfT@NVvG6Qy$@(a@cXEQI( z1Zl?Q{i>l6td(V%fmoWoW3fjU=%ZJyh!AYZjRYUY#^Z#?gRK+oPc_nJjwUCtli}Yk z|B6U1e0nx(SG=K}yJ5z;I*upB^w#ukooS_~=0`)@!7pLg@c4v235o6z+cVszh2JHp zO~9~fb#4mQ(NijMYX|%d`H#y1XWV+53BL-$JaaKAxAa`|ICFHCIUQ#NYoR*h1J7+V zbkIMT)=vK*kNOAFxQ#KKQM~~tRIaswTL;zgRQtv``3T@#`|3G42uv%k7^|D3fJJKp z$EU`J_mxdXLVrNhVZoLgZWs8K`ruxlt036jXJq)|I+&IB>&z$pyTwXjYZGWtr z7w}%f=GZx|%du$GqSg-MH4SC&3qI$!euP#}Y=+vuh3XLU8UH9ZZ-N2W4`5YwW9;^q z%aGZ=3{*uX(FllQJVk?{(38(V2{eCvCN!lCe-VT;AUi(Vh(Q+I+{!AFCx}6Qxp5Pw zBbHsPpLH;A8oPK&zMKA|*(`GCrS?(b@S{1i?k?>|9nG8TgRz5H|m>7#0%kg=K z`aj0vX1-c0`FN=WDeRLWfd@)84*L|kf!P~-kqxcG$ihyQbRD-{@HQ-$-7|(2G!84y zwpX*P4YvL~O5xueeLy{L#bBy$#&lBGA>P4e90Jz&pfzu;WGn26$mSn%(`J~zu3XJt zaVlKrzJ6i@7Wdfk_PcY{GOwCPXAbcW&l9y|{W! z@TkVJ;>@QqL-pNi^kSG31okxA58q1HK7!mG@w;RFDf^_AJIS{Bcx8F7-z z2$apkU0C&@tQv9DwFR5PglU<}4zDY#LBdE5J_B;j_h-4_L$%jl`CnX0IyK}LTzg>> z(kJX$q)_uBG%a(+>D2Sl5lI$f-YM`xG34-CFabIXI@#Zm-qaiLR_P4$?=^}mZzw|fuH-iOq|sLz5m=Jf!^XX8!=msPR5W!ifO99(igS^nmp^P4&-Qax1vj) zdC#&H>HwYR-EbN~{#IMQ8|j*~rpa=5NW1;7`QGt+zQskyYf`*6flvyzZR zvtbJ{*Hd#)yYC{rBS5yI1wCJy89W}!{>%k`|f1g#Xb`Tll2WB>&9J$ z{8*?DY!$K%OVWkJ+CH!`y?>vst*morc#4YWT~_QVK8TFEdAFimsQOh!xYos+J@XWh z4zG8>cR>hO)MR_AduMR2 z=-f{s`WmM(H3tr_aqo-d1o~r4LtAt9y_2GkD4FFQUKRWlO$2&R6^U<>er63W%5jGx zZNYtNzGbMIF%e3YDst)H&JuZ#E z2(227uc7+3;HQZOOMyhETfk%4mH3yIp6kKoW1-9`2nxIA3GD1OFPr3`9@r%qiWRRj zf^NP~5L97i_L8F#1SAvIuYr{KZ(g1^uz7=>*^G75aOY|H!}7I0?)-Vfifi0JwabB` z@b+<8A^1vQXftX-P$6hu5NqS|<}ojVA`0&BOolqu zUMPBFmSF^QdnVdTU)5l;@dr`dGQU~uF)lw?@+SfKx{S4#I%qG2*-LFDhS zA01};Kh1>zEMg}^t;tZE6UvJ?ds2Wt_&N?rj$|CR{{WTILs&b0nFh)uxWAP@Khg>^ z0`S9Dxe6mPDch3g6Q~d6k-4e6mi}>9)S=LwQcc&oR7xBBX zijMrQ!HOJy_dMnXzxxNIA|Z?X?MwLGQ0N=ycL!bf_4(bGbN^@YyZ=C+{u}w-*K>s|FtPD4dC`;03T)AF?+o{umcJaKT%v76a}k)YEyNLpPskpjxKG=SGFkji&hA zsqnQ40X$f|z{_4^r@MYLQ_K^uC|22Vs#E%QOKs=UW`Ju+MeYYvC6 z!@2F}bFlNIq=xU@o$^`eJh_PQJ)cj%F@3BteEdiw@L6tjuJDNM?zGC8&a7tpEy1Yw z?boKIjm6~K=fUZav(BI9f*J}Z^l7L zuG(v~n;$$MX=qJ5ADJyNZD}#LA*OFbTEw;b#bOE0D(gP=Wy)m?7~sj369CyXws1|` zzrkZ{g?&)pfccv^nAlrx;>JuLuv_oZ>aG4r*F5z;qgouupccLiQMi9KJQK$|fd7ns%#k#2*ckXA zdY;0|v~!q-j{9dE0vy80WfM;HnlS>{vZ|AJY_jTTN2=Vqtq4ZIVp=r^2$1P@%`!0S z(0x(zK=GFqbcOC6ni0GJpN9>`Y19PBs&^MNW~P_Ns9OFzoY)dc~^42>s32JzmXEa)iLKo@S;}Ne*UHA z|3#_!e^zS#7pCTaKQ;g9AsmVfM?Cwx9a0lmX8$=~I^W;;to?k)+|;TcQ}cZ!=X))L z5-I%>_wvlDwxa_>QnU9Xb}t9R=$8MuT{zGAX=dp*74yy;$?kG z-1feyU6H-u36@{pq@9~N;}+Z*@KwE%g*yYBP1BdN0rrg?@AvKesm_UC=lRZM3>V2Z zaWJvYZW6hVGv^_F_29G!a~?}8mO5M#*^%qacxu!j>x_x*XFV=m?w6uu)-RG--=Jna zT#&q@g-0PO4W2UptJ*G^(T_RP?O#R*sM()xX@Qz0=#CqQ^_-92nL^&F9eqsD2jQX8 z%syd|{noSs=-Cti-XIf{tUwSptdOJ;z70zfDmZu+t51WuIN(+1E1qS1`i*Bh8~@XU~l&WNi7-L*ULUCacm2lS7a5j;mX;SB~C$-s-jRxKl-p0QI^hBT$Cxmxaz< zk-!B6%v8O1#z+$vQb?Kx;Mn*UO5|}^7;)#)hzun1@WsUgZaLEfOjB>sAz1i89Kjml zF9AS7gB>Hr zY9iCbctMZWK5IN{-4^YMLelWx8~z}8%Aj{qiVRHPR44hNtLU6P%0&ygQuGCfw2kn) zHT`&c0{bM`8tvb{Jl~|O<^IgpcuQKDYl$*{mI9OYJSQG!MS~b|G6@2|Wx6V)^O|=6 zg8$+u2YwQ*LL1t3RjZ~I0Vci@i939{7N24R)4@O|G;#YZBu&J=NW>|Wtjw&vv;$0)U0Hi69g8^89 z)f;d^4TUMB_zp(KZ&eCsybm`n9Q=MqH&^Ty#t4en;cp|6ZapFqWQA~%nmQ%n zZiwenc`vwTyzZBR?*-qC7v4>P0xInPG!#0HvzFE>Mz&4T-BTD{hX8%UVXeBiBqK18 zH?&D2LXZJ}YfxSE7?h6(u@MiNNx|b^$b<*AOG#$G=Sia?ccSMdek&x4KD*=aPX_zb zDkPt6o6o$~BNBmY2Tp&2x6$W^^d)!`0_;cdhD0A|{QtS<1>5cdp)8Dz98?zz;CCl< z4RZ0ziq{#9Vw1j2-w!)RZLnj6tEV<*e!ovXIR=MvS=};mdf3B_L!H=##yaF$hg|U6 z*TIs#*waF;m69v(=*-L zK=r1Vh?-Y)06<7>T$;c|id-iJVznRBZN13dqrLI&H^&qkd%$Y*_QE9wR131e9Q?LP zyYC+aUYY^$*5H}$_$oKK^7twRZ$EB?R2{rToBKG@BIgFb^?kZq3v9VA$%^(&3r35o5fc}O6>29qZebeBfqP5Ouemr8%|4W1N7Bcf6k0y>2-hj zIdxx^tUFd9P%>r^VYzrb2-OS$i7_Kr;jl!By$hLaq&m7ercgI8!;IJ%I4*&2%}Ia1 zYW+;tXNTWB05*56C9{vvu6)?em# zH8I?TXY^?_fpZ8WJ_h4?87-w@qcgqF3!Fw#6Q40#7UJQD5>mS9G?-&;#gML>`d{g0 z)83-*yLvY)u=(O}o{TcMyf-=9ALs*ISm%dTKDN`A6>qn!c)Pn?@je4933qILGFQd2 zr9H+~ys?Ht1&|%-nXKR7-~Y$j<39`i!M^sF`5N~4akur^JF#9tEw9}Au@Gai$)6Z+Dju58tW)$@ z+2fZN>uS(qs1^b7kH8-P3L(aq=;l|p$N$5R)piT^_+Nun(wS%~E^&uo$E$|Y7U3HQ zf(Sz0jo+Y-VuX2xGj_C^-%QN4XW7LZ_HF2~DwTC80i=5bz6ELQ_ppbI z*2#4kX!Ot`^!v&5_N@OBOE&HoNz3RRxFF>dn3quTd(CI&710K#cqR$n>4Tbf?n`Az z%He9w0*aN-?s^cNMpRG0W(#&@WY6K9ydgzv?U5l*}p^1Z~d!io2Mz7KZHa2|*U z&ZLIyez+2@SL>oU)%Ba%FUF1$Z(_OPLlm66<4x=@s7PE1D|{5HjfFZ0kuDJd^yjFy7S=ce*~dX*Z#vi^6AU`hn*s+zPkS~ zB5D#TlSpiT>jtzZ;X-&WA)oF-cse4K0c7kvPJ&#;+mz;dclevDLl$w^41QF`Hh z)RJjccOz$sm(2RfMpF!aETJh$atYmnhp{_XuEj1x0m`>mvPN`0>0MYFI3T@&yhO@w zcHv=oSfW*X-}$#%)m^pf%bN33oVY88V{kPbgj##_24E%)@K{5sMV|t-JqaxbRU*}f z+c9nKZ2%qSVG&(@h3j~SHX}HNii5e&mm!R};B|b>e?H{AjuD;1=j)tT5E5t^J}-m> zi3jnu`1vvv%e<%YwdDDb^Li{_2R~n@UbX5c82~rAABPgF^yGd(*Pes|=!bAhoc*I6 zik{@-c4OuSWfNSho`HOBv+n}klvUd}4qPO95bK{R zidRD7JZ)X@_rUGY3uPQkXGVF!(#1}Isuz?_2|8yDi#|t~$6vukDz)T8vt6#EC+50V zy%wd2Q?e0A3d2{rH53L%CAqyDrQ#Bta!3pv@NJupNtf4j>5pt=ntX=-!rhqHqrZG! zAB*Yy>eZOeSFOQfQ;kMJGz2>`xS$YU?Q{Ko>Q9y32sj|S7An4ToQ~6so3adHUkW*3 zY*sSW$?2YlIsb!0$t#`ARM*3VZ#qHZhnJ6qGnU~AkHgHsvW>Ux-}A%ilJ_Wtz6Bu1 zCUvepo_zp0cG*v%0(>Qf!p1(3ZT~_dvhVmn@gE6~!(>*{c$rwX{WD61Xy0|0eH-&s zNg>MB7>l#VB9`JKKN5C6kEq;9GY(>J8N0VOcLZz-2H&~wp(k!9;5LAdntyc=Eq2%D z-UOPXTfOe$V^UV7p4k*{sT6~6l#OcDUirRKXZ;f{CmCw+D1&e*(SF4R>@r3Bf!jw~ zS+%h{D&;BmEbf~?Ku~Xne*@T__ zr$Gs*l{@?8B=D9n@x%~P3ZDElnetoh&I8gR8Mq?>MUXE=X^KE;rXLNW(CoH;XwG@B{rN)z$$s2`-b0s~_?d?C3l~AN@*VuT z@xX#=l(}zgp$=;xLzC?QGotJI5YxV0tl%`h{P6_p>b?6qQv>6 zjx9zw#}*@;V~Y`G^4S5;j7@DD-G zT>eGoF5)Af!~f7ygz$|I{vo(T0!+4yp^!w@F^E5WuT>$;sE3*vV=UM#kGU;;MCRIf z@GmmA75^zEj?Cpv1^z|mw&6eHbmKW$#bFHDpFDAC4GhKtjaf5OowtbQ0p&e7{lvCc z?#Ib_OPajL)k;|p_NkEB8zZpuz=Yc!7>h%gBt2H=H%Eo;#`*Afbn{XaRTnrhMejMc zObXzV@}y4ZIV=3e+&XFYf+jq~k;24iCgj&>PsjZn!Rw~&y+c0SiotFlM-b|IjJJ+~ z62vsQ&1Z@TAis6-vOG@LRD0Yn0g2+UXL7jv^9*ToUtrZ%ul+p>GnLbeNylL;eOy-TRQgVUGqs7Aw;GLs{V5hghrC&m&#fq)+NQSI;~c z-|EE`lHBM$_{dOUY#JYfW4HOtD~fd9B7)fPdTH$yMPu_CdP>$Rw^>~jLa(FOuSK!>~w&ijNR@7de$L?O%GTsjBg*t2sYNmy7RWM z>s5}gI&kegz6W*lyF7mQG^WOPQwMy6C1e*4Z#Ut7wv2D97Ul&=nNr%^Y#EYX2XfOJ zoq(to{u>$=J%oSk2wN`DjkbZ}C#IEvHo6@#sktQpl?k>E@w$0_YJw?*O6nKL9C=Ht z{=KA|4RRC@wAT-TKZ)<9_iulP{>?!DzK8p6>|biwPmNuf8t!9aS91U39AtWm#)2kt zh=W5p#ESMf@OTA%#2_BPjm0D{mBdd=0HKC38ggD_6sNZz#hcrYVy_gwIO91yA;9lE zo_hc;CC^vm32nSIdI*0Q=%8{|fpO0Tf7jQ-gP4UYZ=A(<;gQ4mdj(M>qJswf(ZD421oCTckR z*)LkItwp}p^-z(6_8=*<_Rf$6oS7Vv5G%R(9x9sQ;L47_J1{c&-y=Bt^yMNRR~=Y32R6slkL$kn+W@4w{d1#&cGaJqh^Kk*vqp zxeYaQh;n{ot9{NFbShhDWA;`;zpGAhgOMPPvQ8)HWAqO)g3h9?@Q+85>5De728(tW zvC!9a*1>;o{8&c+iTEo|<(l1mz!8f;F1HZz3V3NuqU%e5G-U19s0+Zw^3Cus?B}t_fFn`6KrEx$C zm^FxX#{cW^KVJsVtwamse;PdN;fBe9=S%d+Wc3=!b|$vg?dg}*YhwCGE;_=EXw~;1 zZ}c|&ORh><^<>74cjAN>(W<}CI1m1{U!$~Y7eAbZe`)j|h;(}TdJwO38;VC!oX8`k zpe)bAdH}~f!Me)7Gey;yi1*|7EBwv`Z!-wL!T4Q*-{0}$ zWydU}FTn3~3~>(f^u=!get*L6Dg0{jdl|pSk+u%MH}SLaOCNvkxBge_b9CR(S)U_* z;eSS-V{UVz?yscJ@xL10KU<&Zb4q{4fbj1>p+9qcvhIHq-sE} zC24{o*7ER1IY!Z{?*lC>x8_mHm-4PP>Ga503+l5+w1U9_g)cx-pEX}1V8R#XhJ-T; zYlx4p!YlOEkSq(Yc!8c5UOpp-Ci-gih1B)>v$Q~~&ShD+S08NhQm>#F`fP>`P5ONnM$4nondTZ+sTtU%BzNh z--pzvk@~^Sd_tZCrR*^xvWk+?f{v0>yu;|0)N)~IH@aJpgrkeg9M0%=F}}R%!LeG1 z#4^5!NR4ms&g9qzZw0_OuE8HB#?;Y4d`7AcOy!WqMonxm-hvw5TTsJWg)^+}T`H$t z^csPhm7Ce>H(*RS_pzAE^BB>H6n-zp%^*M#r?r}gzltvO=q^d}`4K=bLJrZtB1?^O@#R*2l zcr#d%gz{eUDj$?tcrg6LaXbWsEG7@)N8KuY?NWAPB^&d?smNykR1&NVAp+;u_6U`J z3G8Sw%6=Ep7^Ii*8iW%aNpwQB*jmX3*-ItCE?Z|^W@-PGs0*H0ocio-sx0>2HjyM+ zi2=X?c2v|;a7uon=p&T=lYmak_vf^Hmowi`j-wH=AG!Y%=yFD+e`h063?VYx^izULndSSe2yO`?c9zpF zv-HFc`CCwY>{O;{S?IJ~!FCEhL~jISR1XA3H?snAb4(ka9&ed@*b zFq@G~Gb34%6nYE_<*6)NYhR6jFBcLGE=Gt~8LiU0hY~}^Ey3Z9y`vAIknO2s;bJWj zFq!;3-pqbCFUJFE&Pe2(wZ#4dq;iaV_aa9#A#S%V_yo!&5xW)7cIS0CDf}$-Vl^ty z(86@JhAU215-$O7TPSM&4CJ|Bfw!M#Qw4BDa|lO*`gyYJE%aB2i&e?EFMUD{1gN$zS36 ziyRZ^u3=*X_ww%9>eDIw3=WS4)a|q>9&-|Ir-|-w;0B%N2yb9aa6PSsTZaJrzjbvz z%{fm56yr^XfD-x(J;z;iQU|IeO_kU=!njB!?7t}iFgIzrH`oTPUyr>p5Fy5-w56i7; z%M6}DwKlUX9~c>LPJYVcC-@2CCoFIHgu?dAid~nl3M>A4h5aVIuo?NjstE6mrf%d# zGtc9S+=zPTq$j;z>iuD|rP7IUpWg{wic57Ob~ZT%GyC-P5|fqNKRH)11N-KZ(_z(z z(&3j%coPzG@p}wUEGMb;G088$US8GC64(`{LG8$J=nsJFQu>Nj2fJz2kK(=RAjEX* z5qP-_|Ln=sokR1C_hF~nKELEM4q%nj0G(!^X*LxWDS0GWj2q*C#F{{l*0?gaDxZ*J zKy8xsVGhWWhRG$0!N>swNXsFgz=}50pmW)=P*5s{|B8sW8Li2Pm8e!VQ`0Wls`vWg zjpiefqqoXYha7dxK`pu{HzGI$5A&d06|evW2)_w#eY<__dD5-N$&rCS6I4RCe2@f5 zw|o!>wQHn&Lbpaf*DeP@J3@5}IXZNUpU}uY0Z zpp9-lBtKf%1Vk307>S{%O_BMnQguyK9{?noEnDt@88_*?96_Q_;*6?_q0 zqSFF;Ss}-oGnx0?0V3188BYMOE-v`v+(^h z2{QPJt4>VU@~#suM?%7QAD*19?dNMp``{C!&79V9-bVQA<{aM24ggznCdO~b?U0B1BzbQ`smoEbP_#aFLi8xw%?IKsBN~HJ@3!J#LtmI?6Wh_ z1i)k=>vm)ie)Hn@o;?O4?3)uZ2&@fd@QuXHAV=}%oGE7ir;rF&;xQ){F9DYi!NWHt zgV=8mJPK79i)&CW_TCFwJS7tMoW%~7{-cClQW6QMp=8(@Om3Ba=shOm1*)WT9`DQs zaN%dqe^zjbul{g1$MZN1$Y1KWPJdL&SWKx#IzB3=e`*Ez6H0)&mnD~)7m6@|wfo|) zl)FBiy7}b-)_bAQ4BYHK{lY(@Nyugi*l`KgE{Sk+0eAXyS=! zOTACP8z4*IFaViY<8Tjk>Vec7981V{t{`aymdbW z0&LJzPEkO+5I#lE!tls_e{x3PDWNz9#%vUh&FTXMx^)>41jleNH?S250(a1t=?Vh-B4p$G;IQmS2A16(j94%$ zb`hWE^|FHeo9sVP={fQisBBY(Lh`i}BpTQgJ z*06k-XChU^dNNGvAciR4D1|TtRP48&9*l+Sr84}p_nby#1!EK$RxCiaUc3|WKI1)K zWn&0csbp&<+0!%eNsm-91rr0y$^V9OWsK9!)dAS-$~VLHgq-dPkDTIqq5}wn_wCE> zO>jL|b)B#d2{~*T=tMey@`d=f1+%vk{(W2M#TVhF3cK*heS^ITo<}%V%B!9z?6oL<|`B_*hWS2D=6Lv|%cA2FZTaA`nggLN61R(SGqEzkY~^80c=+%^(s%=-ug!-GBD~b` zr7WMH;B{wXV6f~LwB;!20=Pg9HqA$e>J!xR=`BZIuPmsI8y!M*GS}pA6|0~ z{58e{N4L7;BXZ>*Jl$e@J8^KiF$){Ce_Lw9QqOrYwOw#)^&>=LQh;i~t^h3XE>M4c z9_(;w)qlf61ZIpt?0Cu;E+bnQ36I4CjV8af6uNeP>#;ICK#YI$D2TD*^Qp)OvRwu) zQqo}CU%;sw370szBjLda@ehZIt>9gK)^H#~xEAVwc(4E*FP7nWQ5CSm{xzm2YFR-( z7b^l1EmLWw`bc;YvPiok;f1MSO)402f|2kNgt$PcEleG@AI+*qJb&C_pL4Jm=T@CP zAA>|J5-~wOon@8&;JfVP%2ug*mHksm+V55sn~QRB2+5E3Y<%&VUNo7z?vU6v8O~2UOfdL#1O(&b-dA?*l8gn4@PiM9Kr)&{paH3;LT)=8azpZ z6V_{4S41vd6HpPkUd_S7F6@G($>U;`Q;v&O&Rup44ZsK=g+$JAu~eu$q6yF=;bv6m z1e;R9Iwu$jw;+@nt|jQhGD&U)m+)wDcAE^>68j&r`z-zAU>PDd=7qjUXit&^D{nvq zkBbXb`VdBnQTF3%(<E^QD*__C`FTry@1Es>*4y=ZO6XG#ArWtG*MgtJ>rz zW~~;6lPMAmQXbA;M_|kEl-O$Fp~>&qJmY)xEJpCoe5t!bV{_6P`y?B}TS;t4+f=pr zv9P7DIa%#bct$M{n1Kj_{dnY1XVhy{@-Kv&+sU;BPc&sY(M=(0%qg|6LCAG127oMXK?DvWdthLDAPM81EkHoG@{UE} zoOAZ|C-u(Q7iQK>80oa0eH)?JS!#w2=+LMgNE^GA74Nu<6+deuu~U$4yknn^@0htP zci*ba;CK`_fYo6|xkin(R5={UqC3GhvZTc*No_a<-_GPWxjD=UD|6B=LVu$nXDFsf z%?}o{TzGop!&)ix-};njuX?rZL;)YWWp7ep(i3~F3JXDcS%vG6hGN)bRQ59Bp7kta z?cb(S*!MGRrjuib6eBX7?n2Fi72SyBB%R&R9}MrNUnYlF+PYYkyp&13?DDtCPa6%< zKI-`uYHB+{)wD<&SUROcO&6rq^e)0tWyqs}jk^i&Y9oC7or*-7rRQ|W{|SnZVR4Dr zpWGp7V&!TmrR@ew{HHUN8=VA&X=8m2vqKEW!IaVPAp&6;jf+)SMq?1d(QHsqAOn$n z4KvCn{azHS=Ii~xNJ3F2$yLcTS2<}MjM;+^lsue|=GiX%Q~DA85t62qUVRcuvc`qT z?nYXHH)eJbS8G{Eu?H_5z6ft-zY!1OfiyYn=NiyXa(~vWkbPePBDNrcka_!d1w9Z1 zqRnJ}`>xP10rO$JtFA0_5}Kur53{z^;x#{+W`43Tf$$mV3R`e9p7!ndr@Hcei3U_l zH=co#tOo^ek?z1Q>CN>(@f#qMMHg4HJo^!_`DAjN z`zpYp{Ew!vuMzv7cqZk+>H7nf$%b=EpcC*_K^cUVC=J0;2FBR42G`p`fVii4c-G|?7qUu+7&R( zbZ)P!z;|druS=INXv^#15DKfpFl+wf+H6$Q9&+~iM!w% zG~r%+u2$^<$nDvF$oESoMsI-(SMC3>KQXT_`9!;>4whQ>XycpqXno3CiI#&Y=dEgh zdRS22TM`lg?p;FYuaci%c{v0hEQ5Kn5_u@p-6y-x#mJVHp7n#toETqHgAzLL2i1Ow zLp6s#NIN3KD4m<5Q`0KMA8;uzGK}m_NH=G*G@wF!Y+3r=iV3BC5Whi2*2dR}SIjL$ zysdn58p7Vi$LclpgX*f+$dv)w6vzm`0V=@2I%&Zot`BH3$F;%yQI95I2Grsc_F7W? zS%RdU`=jI!2@sjx{bub_B(xXel99F?NK8Qajkc7CE8fyse$g&JIYN8vzn`C-vh%+} zeptsAGc@~UnAwLVbu8nxHS9x^^Z|~>ae1I6aU{l0DEKGGy9rs22tLg$`%WqQ$l#N( z|2f`gd`eNPF|h_$KZzLeg_}_rbhQd_H5T_Vw?!AK%<7DQTFe5_M+Los|v|t9pkfR z6zZ_(02ZsaI-i4gS-2LRftiMrQ=~;0+vRv-Y`8R*zz7=&92Yf4AKOI$2_A;dV# zC{Va4t)i22P|?N6tSuk6l=bvL1(*bXjG=Smmgy!uC8GF~kb)CBhELfz*E8gqb?^ay>=_qnk1ZVT>Z|HC~wldRJ3pN$b96gdJ<#X64w4ykw8 z75Sh)W@*ksH3B!wYnLO?Y(IczCPv^e5^@9%Nd}IzB|dyiZyH=DKb9lP(z_S1HT&(i<+W35$U)TEN2%6eTJN&& zlv;M#x3e`>tH-B_8iZe&wbbr|PqESJt=V3~H_)<(J#n(bd!Wdt84Gid5qm^3w#i=1 z*uIP%l#E?y*RyRHyHE4@jM)9~ExDdgjLyvHLS8rtk;o$y`(>0$P60(cgCM*6ClsaD z3&q9u6MiTn-{gEs!q4%qI@1yUMZ6GYfq{*9`Ea((1vC-^Y@{d$2()bb!FWxB6KDUb z7xY-)#T8@_B(=kKk}rZ9NxWGlTL!YQ$bRUfgOFPVYs*1svLUTQN6nj-(bIgn5yS+T zSo<|3c4*onyAK*8C!Rg zz}k^$ek|KzF`MVw6FeIGTy)f6n~2{a?ZCHKo+24_ zcC+9MxGxdJTEfb zVYS9!0J; zF5rm}{8+IMlCnRg_}!*<>kGUQfZxs3ji%sUf2Op_;dh&ZWV3t0AbjqijrMGCrOPrE z|LdH`exj%K+uMSZ*Nz?c?o|9Dc+4D3gqHZ&60>|~yxttw8ee4w_W~WvSuHr`uisTT z@ZecEnXTVisQ18G?y<%+o>(T1LsR|x=HPukp!e0_-Nv&3*-1pHy6rfN<>MvBvS3y&W-S%+T(zd5o{mruZkxPQPJk9jE^zPzgU zzy;3U9%DGh6`9oz;YuBb@yTAnIEtE2VlPe57xs8g0Wdn)%S!Ff0()y|1=qa0Ff(Fa zR+PD_ySrH7&q3X|GS3*^7oba?&tbj9X-|lx-8f_~aYVdqAy&Y5_w~x4;lO-sTCOX% zU@MHvCM+J({uRNW8gIxA8Oc@YQRJdM!(dOnYDh-#WZ@66+Z#J*pLb9l*e?kE{Kjs3 zH$?kR>A^aP6xfL5D#!8UNj3EEll(xB^dM%CVWvwOiug zbTc8SRZhT?{tv(e9I^kePl8Xv7f)*6)R2T1^?{T1EB8}u!Vgq19Uj;uNPGJJ=i1XH zJRUy`Civ^aquoC8bKucs{pbI;)-U{CazIoAU>6REgRpM}p2Xe^>{rxy9BiC&kI7ku9QxC)?1^gqX*eW+JP zEK6HHtW1=KpvSD%!?nuJXwrVx+wI_^n^$8!gk<&Y;9g)8a6b5Wdz5GoFV5di?Qx3~ z(wQ%X=tx)d_>UrAo{0CmrRNeYqobXHTG54 zfZmWByJvh?`p=h-O?DL62Ns=O{UF)Zft{=TIp#|mB}|jIIr}ZQu}Sok4%t8015lU~ zinzPV_q(>o-=8NCF0hMGGAwKvCY771fEFG}oumWXb^rG(o*%IHbqzTKXG#6W$BX^O36|S<6H+!P^TD~B_LIB7#5gDa_GmLuoIhFH zRR;;YvRO_qAA-Ef`uG-}Us$C}kAkG20P&n}Prog3PcSZ*X)!4tWgHI}aH#2={A$bd zDXYq*f(hhdn4hpf!tJ8c!I`2;IWrHX$aFy|SF5-QEV++Un-_6#Z{tOEuzMq?wR$>mm`_0F=S@fUv7`K8!IXVp_dpGsUh@ROCMMT2f0HZJl$z?JK zSZ7rnhn+UR;Up`;eQIrJpi*}MPs#@BXDt?hLM0LE;|VM(;}@vmIK>4#)a-E`QUTtAgP7x?F# zEoVrWEh+o3%D-63ekgL&jwtL!E4(q}f&na@XP_F92Q4Pl+R-0VTKYVjIuJYc2MKyT zp7ko|?HN2fM|x7_ld5Kd0CGc~qgM^43ojmQRkddNa7KlJyBQ=rv{Jei0Kf9=4ZvHE z@z^thzA~P`VvZbHq=EHX^%7L5*B|H(pE;RozH1{k@MC!6OZb!#m=4jG0bepZ4k=99 zwI<-xA%tfJnUuOV;%&WZTk^A7pC)T}Y`|~xCpb`q8|phCc7GGD0N;ES$GL@Akq&`g z8;225kMqT*(Fin6_Gz=%>#jWk!qeYT*P_ViMj?nI_b=jHt|jg-8O zd6lA0`|NGVE!Q^wIkgg19*ru6IytSd8}Qv3O`N5)q537NdU>Gw(M({K1~QOQsGe$t zJx^uqOw2~11<0_J84|>7qAKKJ#*vsMYWJ!_2z^Mc+(On4fEFP?sdEzk{gJL}B@r;I zVkSOLV?i9rL_vo(f--9PLi0!)pHYSUF};w@NQ1oIj(O{xyf<*RAn#0-cS?HRN0`?P z3%N4O04CA%L+y&DV_^&_Cgd|nN})-}0mf1&E9Ojzs!59qEke^7OQnJaBHDnozLZE^ zmMTM;PEpr=hz&1qkWMtGuY++F_ejTrb}-&pyig6OJW#?cmHH>?C9Gsz)k;>0bGZ#{ zOf8@>hd*?O8X%Ey2>&I50-6wi_*5;Q=HtIaqyeO?4C@SY#+yCn6HMnZpRK{e3^O?x z2P}mM0OjsPy=O`J@Xtm#S*5!VcYw-?Xd>^1n9>>p2Wj#39rj-k$>QuBNM(>~ zCvy>(`SE^!G#X6ly5X%c<<64fov-?3co!n;BGg-BdsIsnsbf7W_^`t(!B`0HW|7o- zR85QQ#i9~b+C~yZs>7PjkdLLg9YLWX9zJIPKLXA@@O5A>|d+&52vSpn(2**`7Fo5o}*ISm}nKJ$NfxUl|JyV4g+*I z;(e1|_f2{WJMRttNsqAvzxL$TPqf)GAwbFdjl@1{vUVf1$AeLFlx-3Xru6V~DbG$5|SO2+-4tw%hW`H6nLu~%AA!d8G?V4Lmz05`CBA?gJd z%Y3v-`*dvQ2Bcz>I3dxbUeB>wS!kqL&U^9OXhE`RzXYp=JQj;?-@2=N&BZrqC0cN? zYSk|Y(L}3$fcg~e>aNYFwQd0KS4@yT=ZCWCrp1+(^MTXCS}70A*tM@qU(NJVdpf;f z4)$U&ca z1pSIPi#k*3q0i8{W7ttA>-DPr`w@>`jYsERI1Zm}-yW$l3j~?$Bjbts_8`Q=tBwq{ z^&r!O&EscCLfy~Hu0)kgKHYwvg(w}W@lv4FVEa|!535Rxla;=?TPj_EDUEFT$x2r+ z9t4UbP`|xJ{i1#{D$anl@CTKrmdj zCR4Shh4BJ{a}n>W{~)ay%tLM$kB-ft)Oky-fPtUw%TzO%!}h8Q>JDN|2=v65tX->c z;XNYHGuR0OCxG(B{c0sV;T=rAcPDED7uTT_o+vE>TZCEKKm_``N~{tA_aRt|*ltzR z6xLM8nhsR<64!cV;FsG`uy+ zOdW5H%GS*;L8jy$+rS>1G6PHq3RfJD-{@N#y*Fyv4H#J=5AuV49iOr`y;ZP~}lKuE@>j3HLQrBSCww;lE(2(b3_k zh>hoJc;I8Km8Gm1&>!oiswh7!vRnA!WaqPaXfu6 zmo1m~BGpAR$s-WWtC}+X+RE10DWf9Qd#NhPKVN*Eh6 zQtsL@HR7u}oIAA-g&F2e&2|ZrMDIY)5uM4HU@uC0PGIzSM5972Jci%Vjl@kuit&mW zQR4I<(XS}|mO?7n#F9QQRa-1?RK#SUyfUM-!6FZr?3d32bgP;S9cya@Zst=o$7W8YW!k&y2} zE0vDk;le@Px|0P2=gy)UumO95l<1taM0tp4YlKqhcFmfXtce+ z?{K*buft4!240EFYuz#l8LUH)LlV6WQzP~vyASa1b-=&QLABfVmJp$j1lLXwTw_mx zVjo01qgb<`7%1E>djmel<`cIRUe)70dbfZUcqPON)+F>unLyWX;dYxj9%rJr;9RFM zT9}4SQ8>Dnetf~aYKv)@SBFybYCZcj#(YMi)b?9&?4M~kW&_8FUzkjd3cpTA2OB%$ z7dyKj&`q09z^m7r8=K1DY`W&QJU zs8>4rXgsSj$3ut&jsDG0r*OrBI4+qk*iyWm*fQKfmfL|V!xgHuKjetgSY4C`?W3E7 z*n6822(>?2B2Pi5@Pdd^+9b{D9Q(K0MPj$pb`>$_v?p+5z*|+1gA{LljZcQ$P z9^W$RhdAdIy#~QeI22H~+DkUF9(AbPXJL8=mHx=)cJix^9e%RhnvH3AU2g0L48^YP zKtB`peRK0s>YJW#H-3-~;q#IJHnHzC%WLV|Y~U)r{vZZt+#_} zp(7|^4od*R3!+o);N5Y&i*bzLH)hq9TQiE$nP%yf#XT|T%PWA0HFq$U0-yOPjj-rd zpOsD3{6?@AQwv^9qy0ILBddV*@t2633;ouO-6(LJ>@$1&T(7;z^4u%=f5u3Ecxynz zwTXh*SxNlExeu2ZU0c~CuB_;ldV{V1sIJ-9-Lt+#&ukS8fqC$%#sXH4TBrVkEr|Xa zg)%VcU3k1r`Xe*%%?APFHDB{2@B_iwqpZ9i`YcZoSn0m6vyGSpwW<288@iQ;KMK5{ zn;P0X_kEEkN$9L05Dr!Kp(cdyTp&} zz>nWa`+S*ilC5ZmBY#ZdUmAYQ6?jDEc48@kB5C+hy+5@It{Y>nkk`Zt2u>0!;JyTw za0P7NA7?#`q}D^xkV=;CEdD#TkXj}A%fxzkIG4DBB_FF|++K%7#GfefoBS)nTKF2l z8R-luBj{Lh8XZg0AKqs;?6ZD6Sg6adlGFw0gKfg)RpEEfzQRCAFcy^>5M#&V?556|(ALn%=Q}q9^ zcQ)WrR9E9qARz%mCs+_v&_ttxpTuG%MM_q7WfnGw7OJhHwpOaG79_jI3P^Ah$uMq9 z-&b4O>RZ}sOW$fsTLt_;vjmcW7J{gtR0LF*uvR`LAe#Jt=iZt9NC36%+yDFgpMRbw zJ2Q9g+>dk5J@=e*&pEd#<9jx*O#8tXB^z61k*m$vbLt`HLrCYm57I6cJvpaDa(f;( z62~EOHiqswSTh#sgWsnwV@<7Qm+cbHA1q1~Sn)(Y|2ZA?Ry@89N0b(`ZIfBt!ucW` z&Fm18vfp{PotTC7!uj2N*(i@E*ZZwIaIy)s_~v{pB^fmih^}P*-@@);*1@9EoR8_p zJBfj5{Z~0^({d=TZ=c`V=3LsSbkr2sF`EMC&Zj`X1vArw(xTf&(eDP;Z{yc%D)XfN z#GmGyO^wit4p~-<_eF}}napf+&fyMFw(4s6qEJH$1LN@9R&#tSom+3Vy>1q-H=psq zjP`jAs&46~|5n}2UcT&AQia#O+R@aK$dfgU-a?cJO_$W&r1kG8@CErDfX9q zzQ%sl+UwFIVGHeE1dxneCc~yq1&&;*ZW+PVdN9W_hN~kNkQy6X6g^ZveYI&tnvI$p zWMX3Pn&3q1NVE6>^V>`P5^gG@d%H#*6|=@nB9-Xc&G;^Q+xx5EP6lZ$)$6MQ&|U?g zy+;MmnfjAn*(>IJD7G?2%_hbPViDa|1#%8 zF~P0+n$-f9SnEL&i5&3c5rML4{Ccf%-_3#?dKoY`Y&VNvGipyJDcK$~HYS@OfupMW z$#;eFT`e2WP2nA_dl7}6SB3>5_1sB=I}N+EFLS*A>GKb+6TP;{rt(qGKlmF>$c#Nm zK#OnQB3s7CJpX`i2xxH$ncRRDYM1t37kiNN51yh~qU^E$#dR zU6y?4*kn28`3C~%wC{eO_T5+HyX5%?L)`!f^zVE!WuAZFHs)D*@jFp2-l4M}w+rur zNumL%l{z{9pi@(dB-ssT*UWv!`3F*wa=XuQ+r{|@oxa~n@$UHt7rAYB&p+Trm-7## zf@Mr#Y5ZcHU)t6xg{X>t?iRxN2Vc*GSyeJmRk9)l_!B8q_`9pQI$z^QJH?OWPQ-c6 zKiDi7;+}u7SfG&WzflUWhlF(pCR48EiSrL8aY3j!$NN&wKM*;Xw9T&~J%~9K=O0|C zo!<9w&76-ahAGkn!IdTvI98TKkOB-*gNQE8A4-Ua#bW(E#QIq2zbuX<)hoczE$QcQg z$Yg&(ec#b>de_`zW0!pl;Io|5fg^B{N@F>+3nhRZs5kAbu~yF8<73}?TU^ZGjahER zL)VF_o=-;ScMCc(iMSdlq~h>Nz#@}EwJsB2bzZA7_=zOElz)O8B(2OA*&8|Qs&5d~ z)Gooa@|I0VpV0I?Bl<^LtfS$O6>3x~s$f`f#`BR)YSzBxJVOJSy)E(>TP~O9hf7C4 zQt1tiqd$OG$vs1%lNxbKXW&Omh^L@^CEXbi(vmWrODrHvzR?d zB1+pcP;LrpoTjUC-TJ=Gh=;i&P% zNIV9AIp@86VG*ni%FFO0pgvRPnN5;l+33O$n;rId+2q(88qx}u8__2isi0jzPb8>5VfKfxmlgiV++E>S`AbX*_0gz7}Vi-V7jtSTD zJ2aeY!9W(>shlp+^16wi9bMV9aTrN3q?72CCsBy z0XgLOM4lSe89rRHK^-pLxMVlhc9$nY&=zu45j(9|k-9xWBCKIJH$a}On7sc>Hd-P8 z_!pr`icqOQPo#Y9nq-RLTw*{vzpvWiR$(l8F?u-9n7b9o*s&}MwbsOU6=Fo~i~l`e z!^c>%7c=x`W65)uNbAbh8PUHeKwid?!om_o4R_Z(g4~0wl4R^%eQ%T{E=qD#C@J-dz8_Y27)P zH-pU~d#DiNbLU`j-p85JY&(dKuilq6X6U@DhWZEZ<0ArpXxc<0us;;iw6>#Vo`P2U ziyCVFc~1yV`r23ds^87?*QqB!*ZKw>LB5}-;ASrufScBGnFr;@l8x38AjnO-vE<2q z3Q^iCOU&4?s~OaT!Sf~_1W6_yEW~};enP$x1Z$luMPjxY%PE&g4|aG3>buKUh=)#+ zevEC(*+VvV9vdsl+9eRXuV}novPX1SZwIZv${uE46nAp{I-2^B2ztJvM3XDec>Qwg z_18H$c_n#usu$7-Od@5risSO1sRj@=%b>2t5=bWNU)k=EcR%Kx?t0zp?W+$qLT%gO}}9T8|TY9E$f%vmXP zHccXAO%VtU2LTg3dC-H7H}S2Ry*kC~m8b9d$ND3S)S8#{Qbyb3EqdKrN|MvUPilN+ zJyQPuYDzhi*Lstw_Zt(0j45|`nNTdiT_@CFvNNId49dOfYLDN21;2?qs1UeqCTJ@rC|k5uhdJ`H!tcvu{mx)_TL;H zt=sOMfild*M^5`rcunV(@-IJGze0q33;%M(!f!fL$pO-e-!g#0GC#6L++qA~{2z(u z;Oe7>i{8MrW-U|kNKpQ?b~j7mv?}UN`}9QVe8@AqS@q#_TK@p4HJjp0Sk}%-EqhEO z-V4=zt3`SW--6bg!D6Tep7WiwsL`!(v%6$Z%nq#sM=BPx!E!b1^rBEKyuQ%1{p$-MZ=KV~it<|CkSzgj z1s2orMdEuF8-^7^gmeOj>~i}4UO+-y+C-iwpmGL%3E5v}zqWk(r4-Pt7^nRi?}H=8 z6o$_ZS%?Fp$T+I%{e+QDpD-)|b&Q%<7;v0!0yXT4$bJH5BDAf=nZ`iW)I%$b+BqZx zWI7fs+d@c_aFlJ_Q0X2L%-R6N@g_hGT^8veslQ+=;mX*sFNWJN6$utYboLw#(7_>m z?{IGc4jhbGRnmE&=KZ5s&U`_R;cF35{&1XEtop^tR)!zCDZOrID z4OVI#njGm#CahHJ6W_soF;u!&?#iY*&kqLpnwl_fjz?O>nuG{;eQCty?P&eh8PPcx zGo(1%DIqz%{Ym-Dn}7Jz(IWxO1w)4mab7 zH}Gnh^DQtmfqIy^Err3@%q@C8U5M^>nYg0oHMxT4AD3=6FlMa~r?>BcrMwO`OCuremi` zqb21(XnJ$vMCpkdvsefXbj>^A-rj&S0)*UfM<3+wI_9&QgRx;-7+y#N?2dCi%nCeC zs|tIRKIqMAI47~ioq-EfJZZ6uD8n;eapI&P>HVa9wf?ewKERuApUr*(e|$)pn7s)LbO1ij zi%lQ0zQCFx=8vpuutmWuJ$4{4KS`g0u}BdEz0f&9c6$jn z6`e&qz1vUmCp&S@ml8*-vZ#(3R*n=f%OCL_&Z19S0vdh0pp%hm7vir>EHLpoE+&u8w0Cm%+<`tdN=YdxwWs1r)*vm9r zCy`4hkxP;g0P;rjY=g+gi*V5eN_=^2S zZa$yM7hFnT&YZ6ren3>iPu&Ro^bPRi#)Y4kX}C@j_<6Y`DHDE6vP)lBQ%*K5OeyLibik`A@?Nw(Z~E0PJWadQw7|AIf!8$Pbtmw`sZ2R? zO#@zc0}KET!fP7Wa^U5&?=PgsKC7lgu0i{L1qUAG3U!1+ z-m*-LpsL~d0xJO<(<#u5q<~^Xd!8s0pX$d%TXao5`a|m+5Vp^ld|%y|!g+x|aOpqA z4m>`^XN~D+2NrSZgB>y3eDz^%KZrStGAvH{M>{eANqH`0V=z}1pgB%te`v%V3yj*^ zlu21&A?sbbg^*G~?sRHb3PpR%nNcqsV&?wF1f@n&Q6PjVjri<5pM7PHkB!GfPZuAp z{Ub&f%^WcMizCLB%C2xqB61fQ;!ZXjLA!npgoL47=||&@0X;3;H1pWIsTxQu9$zZU!ABTgHPCXiL}*-lr~P`OTOX}Znn^+Lva z&{ZEBAD7a{($5^_w=!+_&xH?!F`?eut9q~6J}hIiI!IBV+-Wl z&#sSsj7}a;AA9EWvLdAGW8-{by875}%hyOTZ!MP&H8~w8eM)`o;&}Ra{7dw)8-DQ* z>0_J0K@~FLK*+vicTh(3i;xOTqM7uz!Bonz*24Xk?WsJ;v@bQ~_^p#oT!c}b4FY1;>bX0-V%Jp1NzPNh)H@c1k4s z|2S&#(;rVDa;T;ZS+7*kuPS~*mElKysI<6o(i)W`F&7`DbGe)gTHMj{L=z`)4Pu2S zW)Rjx*YbqwRwbgKW;s;w^DRY>dk}6VG0H>t+9Jtn=br3KrVJn@fjcndL-$46fkJYf z(%){UOa6IJNh|DFIloGctLJ-~{#M#0`r9RBHxWDIJE4@Jky*a;LL+m~;f~BVqCevu zI^2=i#!eiWlhWZzsm2l_R<)S%tvMC;^&=UW>qkNjug}e|;D;y&@?-JBXV27+IYZuB zzdQRapC!*qeVfY%qP~@;DD|z}pnsR*&7S+}F7NJZZ}QhI7f8~*CKR~-x(|)}aNw_y$-_4Kz`>?;MXTwM)rpQOskm&FmJN`?F3F;%$!c^YEv>s;8m*x+k# z^i}Ubi>q)W5mx$Jftm|E0P>m(K(v*jx>Y!`w(?{GhY51@6XfV8$k8uu+?x;`+7txsHug_Bx2s?K+ zQ4Ticg$FUlTB(}k7bw|PsD6;HP))uUeX6gz=}_dLo_{D*6*94(7hw9>u2xj+-_VK* zwM!GJLn9(+Of3?{eoaw@_Aa?vXZ)pi*1VXYB>O$r%u^u=%Inrbybi|m!l%@|Sp9b1 zL61LfUtPT-k3O_FL=K=2KPbFjx<0#l#cKWVfIfwS1g@CgL&Du00y& z^D7{&M%;g?nlzms?5uAvkvA9FpR+0+wXeuw|F7n3MSOdgC}JqRtyqx$J(B36kX;%= z;J{R@6*lRCwMrPm&)WEPyz<5u2GqcgjRW;yO|P`WJD8e*1>%DfjLkx_tLE)lOL!Z5 zNUU^3=(tZVwPN!la@_?YNvJnZ$r};tPIzW6 zu1@kApTkCTJdnCUf*ata=o?Z7dnx*cl;K{A+-Q00!}7=<(nPh$%%qrhs@10D4dgTA zjZ>2H8S=*epnL{wlkyqe28dZIX?@j6&y;5Yhl8zixTN@P(Gun6lScr><>>MmMQM1c z8-bUXqHhFVUW&dEczG#ulLjwOG@I>;W<{=OrugB1vwTL6eO7Iu^dIp|E>^8vv~+fd zlFry$Jf3v6@5jBfuC#97neXdZ=^t^+qke0vQTu_&P?_=*Y^#`(1fVCvqmF=zx$@KG z)(0L*=Re)^-Kt3RQy%z}ROHG7*T~nvdflI-;Z4r&UY}APScox2 zNMs&)5S8`qoH5&JwDPGxGiR3HG=gVwVnCW0tj4qSx$iau)ma zzMQ|3UjC+K^p!3YCS+eDfw{)w~=o%_ts?y117=2|&6Q zI{kUEplkPAF%c0k?knQrby4;&Y1sx0-2{w8yjF_z(ypuSmOf zxFlojeOwjb$^(5z`Ocd-3XX5$D8zvu z$>_vFqeL7Sg*dRUeFfZ0o#I$9>ELvRy=HtH^1#4R@aCha&Tk+N3`B0sF??3eD0Q`G z8aYD_!DJjFiR5nNHOPAuNkEA&!Jcbx;H5u$ z43{33d;0U8wS->u$vgTL=bn7-*wE}aKZ9kSd z?!>+WiP2{l(R zKh6$+jIM2`Gx(RX27aqu%r|60B~J9l7k=zx;m6up9-I7SN{lC@Z{#PoLWcI$b&8Y6 z97I}|GzTf?L3&Y#vOr;%7=64=)zAhlyexf!!Hp^TT?|9QEVz-q?U{-CU_o~-oWbO|X)R)REH ziD{M?$d7=3?;m<+*^kITi!n*lJcU>2TE3eJi9sGD((3=m;31o$_x!hXQ`LVb^-I#X zNph2UU{7p(F>e-WQRbPlj=>IZ#NuQ6z#+bI> z@Yz?OWX^9KT|jiidx?wp%@;(q?0glJ#ckEJF)DNS7c3+@QBYuGW@BS!;a1AJ$?33{ zBNioJ6!D^n=fPYtC`)Dt^1|f5dt&+v;%ni&4|sW`68LzaUrJE{z?r_OK-qGfnJ<%C>}fDe8yvt_q40_5%>mvT}0s&>PnW;QNN zJI&$`BR}ZW-`Ewde~Nsh{Zn4X9vJ_WZ^%bt36*k0a%GS>Q&jlkrN3dSW|o(f|HXe& z41)i&)C0b3zCNIKTLJ&`8Q@RN_Z0kxr{FK1N%mOfuXIKV{{1!lN6L=p^d_Tb#nDIL zH9Z0&*MJ2A+5U_`Ns@o)1pl=j{0#+vU>N?MwTTsjQ)ZH0fRI|Cj%jk$)t#VzKvWe9 z>T5EgzQ9}0Oo8`E-#NdSb$j*8_;h|w#aqTIKrN7`;meaxd~c&>FF^Gz7Zg*Wh39+m4f?}^`#BS7 zFpEz0w|C1cy+9zGr;1&nhUfN0*>|ED;5eByfjSo}}a_Q}X%7pY7P= zO8nWanD6xWQD;mpEZeNVrP*jn^t6|wu3v4D7Kdi%@5zdX2Bk|E>~ z>~Ee^5C5Ql=BVTQXJ&Zl5nMufYR=5s zOU!iUY^%m!h2Vg5c5J1Oers2B`|0uRd49WE;ODpN1ag~m%;~NAw;70+iudKo6Cgfip^JmZY#X^7LXX1&oJyZ(q@mV01 zWEWHl#pUZHDuq_aV_yOpCY?mZmM+~%)FrM0eu%4apW==)2<#9ch)H}&o9`18h1CNm z6!x8T+LHlk&snrT42$^0c5xQ{Z;>B8XHmhDEG#1gOXlOhtMG$_6n^mVZ^Vypa88}H zT0D8dk42yWmI{Goo~vukeqf4$!XoFlXsA3-UOVt8A02YZ%A}uW`s&JoD{>qj_>^+k zrK61=!L7uJ_m0IJPkbDI-75JYNpL@%zwU#aLzgxLi``CyFBVnWPPXp7_PY&#ctJ+P z?+-yvngWZns%d|w{h#Kqj!#dJaTPuJvd|OtAoOIY&=d6}^yG}_irJc&42Z_L6H4;A zXgzm|jx@M*1RZM9+dCaUU3?K0EA-?_MNfE0!$<_YeFm>AB`%F|D%HKPBxP1Dv zID5lAhW`;_GK?el(vYQI(+lnPO_@f`^&T-nJ>ZwQC~n~$&WAq}(C>=x%4Y!9IiamPo@n@QP=wVX)*(Xwu*Ew!44Rw{Pxq!6x zxg_=AQK$#Vsc-{*vPVAzUz7O2!@mn3O58>tjd&=0ND_}j%rtNEWfG5aaKokH3i=-L z5Pe7&`XL<^hks2!rexBOLE7IxdU#ZoCLS6r;_ScUQ#kuyGdN=e6!D-Fns^LPYrji8 z=BB8gH2i*veka6(Z@Ll>@R`3%`ti^Dhrd4%X8oA{;otf%q{A-$;g@p+-?9C}KZL}G z>`VHQ>3_;UJXR%|9Q>!>gB;`i`}nWL`*-;Quw46xPgfa|{^957{0q@hEC29k)o0p= z`FGqO*SWe*W!UWumdzFWl8*<8bkm3_w5rc(&}E6n*ZD44j^!V|s?&FN+IL@&?>ztT znfvw;Bc)08uYgP${^3VlN$}_LLivYR=*A^7IB~MAAYe^ zq%D)(B~>Q^t?>4@gEf_FH_> zro31=Ozt(;24Z9LLiUXlDlj;VzgOh9FAS6=W)}NLOv#yfs(-{+^Jn(+m)%=1($(9Vj_xMTu7v8igZlx#W#s}m9&g~W$hWN8eY}##!Mk)9*(db4Iw+dfYWYXJ#NYZ^`SCp` zzv=w5-&*ar_QiKWp7+F^?$NDz0c)=>x-GBl4P*Qp{<5upWBk@YS!=);-|8#d=rbxd zasU+PE&9ructB;m+HX{>mLTJVailfIT$@UnQo;DnqF~RqfVI!3irv?Fae7TEu1BzVgM0p6MQj2wM?TD}G9@E!TbnDamT;>tzP(4tdY!l?q2kw%%FB;C zLxZVTAp12BOtq)cT@3n{Wp6NX*70SdFsZ1AZO-18O@x50MP^SDSNp8Z0c#iDF3o=X zjwi8Z<4vgNA+UyCC;Zlh_V^rM*-OYiFInSr?C}UfYj{{=jnB8o7nH?~P_s3@z#d*JFipiH(n}hDn1Ron?{h666n}Um?GsTd%VXNdIx^!z7Kz0G zS*Wl~NNnd+hz&dU2PZM1j=8?%up)L{U$z$=Z+-b0(~$M$DKh;}tuGhcNwZ{qd4QdI zs7FXqkF@pWM3o`AzBEl!`4@WY%XR8Ay}kswKQ3_|tTOC&n&l(=l1GFBr4iSgbyAUmNzT3cka(x;2UTS@L{2_=gyQi!+%eS(6IzJ~tX&U)LuP=X3sO-n> z!Y%Gsxl*Tn$s01}X^kYJK_k?E;0|m9nCsgVyWIj?eRC_uE;>g|+0j-j}Q|=g3|;Y1jQIxxU;@ z_0r#IT)p}D6_noIeu4PSKy^d0$^lrR4^T6&X*5iuxPlf5_sB`vL(>e@2kTeEh5nLk z$|KiuxpFA>wIQqWCeym^R??C(NqDlcXe;^yS(;!dZ7|NnynJX zn9&)xW`)cB_Kc$7oDS(pSOw3%IMzFhcuJ8(s#M^q z71{FC+B8QgPmjVs3rsPROAkEdkKL2)o5Q}RtQ_?M3%iozUJt3LVTav-P?xnMLaY-J zVn$Koj-YmK3~tyODsGCDbgW=`F*`V4LgZJmxRr2Fz>|OoEG7cAT}qZatfFEa&R9LX zKw7=weURlY>%)ntQVUH|&=~GpLcal$QPU=rIW}W})9(Qhj2H$#E_xh4UGt{A0Y&<6$pN;AozpUC~e5-s!?;7tXgD#oXnkGUB|1$kaRGF*`@7p=8)A(eO3@#AQb+l ztm{O5Id7Ii=)8^>qerr%6YDMt4A?>Dv@iB zg5`|vD<8}Icea(hSI?MvhqZloWPo4h_m+smgfeOh7Loe;{R#8|wA z!~9PpMo>Qg5*M*CzJt(#hg4j|L$KG4uD1R210;lAPPgnmL93?^Bj3a@Yb{@`<*T^0 zR(<7C4!%aq0)&p_$n?_OnX{-5ISD!xs#guiB6Y zQ&j$i9)CAOeWv-l+qge2b7ZOvyB$Y9vM*WQTiAk>{vfK$QvBU{bp2TTU2UiD#-)9? zpZg?#ciHw7fA^9NLzB+mJxhYHUDxr2=I?sz?8ogwt^3tkQYZ4ijCooY>Ur()cehGY z()hc5(gMZbJ-SV|%f959PTyBc9>w2PyLnvxt~W0_^LKetfu}#1pz}+QtYk$ak`-Om zsiOYLJpGduDZu}k7edmdwa#IFG=KLdcbdODIhNw@=6+M4ko%6Xa!LNK2M1s>3-j{0 zKxmr0FX8XTK-VR0#@r--w_vO0@8)xr`50X0ZH`MQ<5SQX6!~J)vyornLhc&+yik(X zVn~{tkRef;$U(SDm4j5{{{J9Nc`CPlj^_~C03||OBH~&tM7WH@uQVI?y(z>jZNp?w zm5vkNjuelOGfZb2#B}Ma%~~ zrJ^TVN8mU8W#28DSvY&JzwG9cnfbGe{ADu+&pO4luOs{B$hO4Eo%9ttv`F=hIbmE4hKu| zx0vd!ZI&N6Z~xj&^0QTb9Qk31_pdz^v~t!el$vu`*pA3c>{)Q|7RsuXID9wz2|uG^ z+(Sf8x=$z-q8$O1_-iaL%$irxRA=JUU9oD4W%jcx;+EOR4m^zTP>;!uP4DbUSaA^V zA-eL~l)`0`CFoLBKjUEdSBzTZ(_ zX3-5WM9hGW@Cg99>QX{BHzw|r+p#XJ1^!8RJJaqL!q}+!x=~GmI_X)W9KSaFf>1qm zOu+3^`&uKX4)@p1C{O&rw6ncLq)QSj%Ugws7jqQw(+bovc=>A4bnU>r!8=UPjyiaD zpbnYzWPoPQX%c&j=R0V~GeS+A+kyMC93Fw>xZy!7)aZCOOn04fNZ%CDllmNUWKK0h zx!wbX8%~1`3~jvuRy}Etl*x}p-!p?2`o9klOnuRhLD3tK$^14HVtUTyu^b`bS!ZR< zWNXtp;ETW4%{S*AF{<$OT!}#Lv#^2Po#%`0JU!5JC$D>qw;z04Saa{pBf?ty>kgHS z0y$85uBa>ySnXr&nFK$Mew=O0{W_C_;i_kAHDs-B9nPaQi{p1EgBy`$rn3DAJco-q zTe6tpsBi956Kc`|w7mF*z@A0<&F>_a@MF~2!f!_ z(Dc8NujZj0d9D+^kQw(G<|BiT{=yC{G$&aHuaFgfBcIuZ zvrZduqy7bS2cbVb7((l-w+T$zWL69Wrv-cgYZa&+u$&Oty@8UTt!*vA;uYg9J#u61 z2i{iTD{c<;JdEqIY51GRS~@XkXRFZFi4k_-IbKl^T3Q8xK>gUBG?xY@Q1J)qDOFyK zE!K%>G)$(p6(WX6MoSwy!K*WVg8XYR8G}(V<{nVE*~@Q#9jB7!=+?XATXO@}-+a+` z?)Jss$rhx%8l>C^Qg#O^4HqfL*$?j!Lg$?k!AV^2dRWP#)=)VeT!QQcUI|zok1@BDZ}=`y`pTYf`gEeK!y?e08Pb%m`9)U=ws(3+&?9|$A+DGDmuYP?J+G;x@iW*oYq+Ny1O zPvHgJY^ZoQI|%ir)uZVOy;xjPfVZCC4!i=wEwd{g4_f=C%*X1S2u>LMx;Gg7ChrQ9 zcNpucY#96|?=aYP(}pJR(hYteGuz%WPkuRQU0ulk0RD^kAI$$y{=uWG%lS9`*1G6U zkTJ5@+85op_J#!M0Z`ig#aW=}uQz6SqLV zWOTOm;MC0Mj^WsneU5~l#8#ZdFrNs>5B1!uAg)5g*N005(Bpdg*Ayl$rnoDM^%Bso@TcWy zF9bQ+x^W(x+QvqeijTUPConY*)B7O4cvf8!|wX;+!CodX9YFqS0PU7Tg zF&C`B^Nk^a#r!Yie?I@!yqYGOuOYQJ>B|It5#JD*Dah|~`*a^^rL}Xv`Iy@_=PAJD zwKMjVl>baQnVg@HP9PWEmLgr6xSdc%mcB?VRGwt_giDLQ)a%P)E_l%lB_~zOQ%2Q~ zsE$L(E~OR%g#$Sk7S}pL-+pywP}HWKT4U5o7`@}Q$f$iWl|#3x zRoO&Y4T&p7qXBmHZ4n(r!@ndH>92sHlv%${fffizN;D3jsksmZgAC?)-e=OkOnzm~dqQ&pI9FL~t%qhZs(8C`!kmi6dbbnG?r@Hb zZ|lHEIj|A$EEa5?ct43FxA5uoK1f6x6Xbt1;shk zEcvF8Hxw)(y*LgzuvmT;$`4`jpnG-k>iEanAF^r~A66^kB}Hz;@`(=;k8 zbGj*`_S36+XITxUtL<9(x~zfMSVCXG{#<+~=NothOi_~;ad(@wC1L2AlO@@;(mGbd z8(3|G!V9w|1}n-*Q90e#TYtkhnYphd1N2}p9M&D-R>+vZ@pYAC0WK#x)r?EZqY6xwYtqKg5 ztq%_y8|ym*b?~!$`=syx8B4SGQ`G(Yjjz{B=sC)WcMC=v*iTv$*)CKf3<&L~WIUqF z1;et!vqos#ig3txpt*E4mx}t*dUyX6<9PN{=WHh{j%t^^<{f(|aa#~%HzP8pvdxq@Jt<$Cwc%H*IaItQlEd$MX%bmXwr}*wRgwQPQ-^QoI_o147S7TJ z$Z5+9qm$@I+qpVVNsj1p!NaTwbN(&omtqd03vqrVZ%K=hrrMuLkD~A0D*iUwfIs-0 zr}l-0;OFi3_Zas4Ut{mdJC6$ui?+``&$G`y8!#*T>|>nA^*TF9ud{>HV7;!+<9f|( z+k(OnJ?;TB{yuu#en0=XkFp-VSX=Q!6z*t-Yif~w*N>IjujZ#9Xz6C<=~3Y6|2+Hb zm+nYyUrsT0uP$B9sqSap3j;XCZ%kdu!v%BX!I*l9rLQA@q_1cFR9#*FZZgF88&e-_ z773AuHB*er_1Q#p+}u3HII>=+Z2l{$FL#?_Y<_i$@rs*Rxtl8eLxP|T?u zD1U&n;w$$T@sbneE7x;#U>!GmCFMm)8O}|!;3-jo%Z*w&G9e5YZ}sOeCmvU$YK9G@$b|lN5?;ROW#GozXE)4p0~Wk+dPY;lra2QMed-?)>RH z;X8?SL0lDD_+Bq)47dZRpk0pavF)cGiufejP0wmXRCX*9eq`8$HC_-KIjBnh0k)NSvo$41K{&=QEd2ztV*z2rr}F^z7YAImTW`f!R)^( zQX)t`^%zLLG84)1u=3Mbp(pt$~}@-da)7atFlEnuy+1e>Akb= zHA1+v6(9BrjPbN);l-LM?%uV*_>O+wxwdHxwDOpi@wJEm@$Wp(cBN~#bsZuV>bhf} zXZyBH6)`*dC8Wc1WOIh!Ahe~aBD*b(L74jlc|#}%#Gg9l|C(`Nt@y8f0{`-zExgTQ^zgUQ=3Q@Bdngn<3sjp{!N3KbJhnycEwoaY7N3o{U#7t;Zer!Dg6Hyf7r96j6rQ?@t6-%>?(N%(@jlTGfo)Uqw8a-Vy?uZid=4*dT6(dP_u<`ics)txgzPpga zBwhLL98bPG!;|ln|LwD~DJ=(FsI!)jE!z)W; zoaSG78_e9QWc-n1@)t;^*}C<;vKuJW4U%%PAm$F{PI`mSr@_MitW37|{>d(G0?VY)qSr#8*FdRnSM<6> z9uZS3Ulw{TqUZ*Npe z3wf3J{HA;@^m>EL0a-zaUN5!|8T1+6cg$9bdxAEw-I;EfazeTTq|K2Ci>z|7{ z4tmW=lWFwJ(|7#s`lIPPet;H4a0D7M^@1PC1Nx4i>g&i~>g!p*Q`au~4&e{}cj!BU zIQDgZ~Kh;j;mBW1mx>9dR&^K$LD(V_$-eed-}F#MGXEFB-vB9oxdwZk|Q2T_AM7< zGEb!5Cn-TBp`0*q#1N#=s zfA&i0AoZ($sS^^k#!=eVMR!Ggnf#qTFW7o0hmj|05@zPIi|*P#?Cj&&KX8&ZaVaAC zi3;rNwC$j4Fo#7YhM)h;`BOoT)fNl!Dlikf2ih&HfU<#*oi}$QRkrdOLsc~D;j1sJf%!e#S3GL1i+2=Q>bO`AZkw}BRY-96iU;F|3x*uF z)~xd_E{jthy#LRBO zW4?49CKa*E9%0mFla9R|&USR3yhfY&{DHS@-H?O~=lqvS-K~c{pP`r2)xQGq2LIf% z)&uHEoh6RBhrohH=5({vfz)PbG3fEP z3ozU(2&jm#xinBZak-tdNI}kDw|B#KeyX>f&Tcaf1sDuhTa#;`5OqdW#9Ms|+b`%N z>R&!@pGJ~tMxo+`Tr{RntyGP zpH1?!Rel`#*&{!PIS?u4y&UY;leGC0GAIyRe* zDzoO;Ebf$Lb4>PuT7OHo()mAYgPQRaD3lDl9@z+Vz6Uj@zj6e$_4?(t1&bnQv5BcR z$4M3{u+O+2_HJVC0|@_9j04B^fl0>L=rzL&slfd2td#Yk*% zcA^_+%ZioQm)XYebcnXlZ+AAd!kcn0BjCI#_w3oft{S0cOX3x$l_$nca>(|-cYb3g ze-nN0TI`!SlNX!##ca;O5iT-qjDhavtm?O%E3a>3C;1vexYUQS!cL}NN;>o*YoF04 zurL@~qGTzHWnQ=}Y07C(f{NI0#C+RJ6YG;aMIe&JY#oA?(yL4(W9~$-j6Ejs>)Fj7 zegz0Z3RE2wl-nsIcx{$qkNwuK#h`zgaAIQov2Z>h@i#Q9-xl?|N&RkBzmEFdqka$Z zo72EDm~W2E2W*j5m}M^-XI=k(K4&jst6@)GEX>>JFJa(!W>VjvsW-1o86=V~N9@KMC>g4aQh3P=`}fbyn6$pb30Nl@{?dA- zzO+%0BFFdsPq~V^=w~`%(68Kte!o)**J%3|j8e3IA06hSN?sFf=7Wvu9MJ#g75%cA zW7Hl3+USeY6#d4%%^JqOA%s8Z5Mwdtas~^wLi=Wp-~lHnVOX2WSR1a_AU4zZi9cQ`j*IR z1h6peUhEj*3;t`~%l7VA^rP~;(0fK~ljldgXFLSuS@y(b4|uG*$Nra~^}11WF@2_t zBIPcLKG-Wi5=xaFsZjT-+{k!!@0KZX!qUrpA!uzfYPOK7$}HlBS`ufG-1>R<0UgMu z)*kM|3G#55mXS?~fW)qTS?r;T*L!=q=v-s3i)b*cS9T-Llhq7T%?!S`_5^JL#$z7e zLAv-V8gp+Z-KQ8<#jnY!13i@EDa2-rt0Zc!{Fb<9r0qF3M$Xsv8D?y>YY$`09Ux7# zpWLE!MA3GkyWzLYw%4qN_}loN?oqz-9DDr7CP>nRf7sK%yjZ!x zY}=6D|1FVUc>Pb=HxTmSNo{YUI^P`LE}d^~y_3dExU7z+UTsg z!p8)8huGU46U00j8cl{93&?OpWgvV!E_4Ir#PuDb53u%e#FlOt!NINz#-3CUvAeM^ zBjO8bBRZP6#ad)_WEeLp_W4Ch(Hm`aG5KjdJHw*{=~A;OQD7ay(CiR~W=(`l*~*}G zij2-Iy_i|bj8vgbSTC1BVriQw8B{a}qvko#fQ4+|E6IgyexktJZ;;%2(HcHpEm~%5 z)-tDNjBq;bX-n4?vGbRM5-eTs%3>9;K4SGb9qA@7xo$1WT(_8t-GxFZL4`%hSr_z{ zu6Hw+uHTEUr_0i%$5X9M|JeG4_lYcF9LY$009n8yi*Y7%?)w7G=x72wdcujRYDPdB z9sZqVUE80%0X5Ck10BQuY=Y7V$#-Jm(Mcbhq5Q%GQ+&PqRlTZ*j%`B7@~nDq$ZEes zouU`27g~X(O`4-=gLaQ`W`%^sYjY|wQW4YbTJ^l!xsj_gpP$zAKNM-P(W&Ag5s*An ze<=p#jms3;V{ji(*7)bD1B#V3{_Zqu{D5bS9{_vGF?;8wRM^d}@C*v;dOrGaYeUdF z$n;SM_KNh=<}ASTLRdDA{zMI>(`ra#u9V209*&TU)#25uKVyYed#*&h&AddBFs>s# z?fYRxNnY(#@EcwY)gZ=|qC`vQ&vH}+#;-RS(KrLGeZ4Bip;dh&zAJamcG$Q)>-AIs z+`E0xt2`g+!vt=XukXRZW)UaSnb{5Gn-`YhA{@Fs&_*gnDfHud<`%q z8J(+n=)&mqU)!0{0VNkIX6JEXc7V6_M#wtsJa?Y>fz6ql2qrT6UeHw(RImlyekeulph8@H!$D_m&Q+~l!0>R9(<@i$kd@i$ke^EV>M zX#PgZjE7Km8Z`x8nP1|qRFgPQUbLR)Jq}cl1HH%I>an-`=&eI8mvdCg_DGAj^21Po z-A2s}GI)2*Ou=G!962rxdMfYr zqA(n5I3=`qIG_KrY$olF@6MgGQx*uUi_{8nd93f>1*p;OcXwYVi$wQ2St7ct;OOq* zLlR7oKRJ9z6xT%i!yQ-F3Gv01b>c9A7Tk4Wbh%{Z*A_dFZnW)3KP z(cH`qg}tu%@=@pf?_ScNca5IlW>kbYqxRt>Hv?)P)!EniBW;(Z?lII{!l^7(&%2!; zadlSk(>liGW`4(m=4O7m;9tPa409`-NMRzMX6mEnyl2v6zg4?vxA0QytcRXcH=4l_ zA4a9$b$SetjjmPN>n)Z1!~REedt+bBC%jB5UsbS+-`nJka+=y)L62pE1pMLm7T11n z4fwQG4S#wVi3W$lrTe|@bS|cFW69)=(Ic6DZ#$gh^*Clt4}Un5A`FxvMSajq~l3()``L?DzeMlF}4G_rbIYUouawq6qqtP8jr4 zH=*A`m9V=$eLd!jq&}#mNpwLIn+i!3tF-@zEk3m(sDp{uSrPQ2I1kEri1XkFoty{N zXbfS<;eA3@t8yMxW0@R9TTKPmc~G*8PQF3Ld=Jit3rH}v^PtQaoCmjy4ViWx6co|U zgAdAG(s@w!gxNKa51a@{dAB-WYgh&$0RepfdOjwG9{e{Rfee_!R&5n zEs^HMq*5?Ct+ENc7Uw~g_Y3c-yoZ=mtOBjS=bOyjCz1iRymS%g!JC!y;B(~0d9b9b z^I$@p2fuw~?D|z%Y2Jg<$8oXiOYj~{n3z>W-xlw|X8hPg)-JUk8B41=`49eC`w#9n zX5&vK{(~!E9~+YXgNib_{)0E;KRAQJ()J%d*?({tP_&;C=tP&xw9N|79-$^|xJp#W zpX5Kd@!0-@6JG_7`mV))usFq!)IRyD5SUCC!oF)=55j5`&kwVaAR{gjN^+eaj$}oc zvr_1Anllzdl=L91lpIm@iR(dlV;L#B5a&vH%ND>m(vD(JVZsU%IVNbm1Z`(~58)My z#WWT5c_)GwDn^1(_p}#(?$edHOGSSkA(bap+&>@PnYR1q&Dt2K=?I_Mg|g%P(FAF~ z>s$!x<1{95vwPzaU(!?%HYYk91q}zTb83sqnOqA>QN(K>XlcvYv;}Tx)<$2nBRA5I zciIbD-u04qG)0JdvfT41m)J4M^?=wDPsv*)iOW~7p#E1KDE+&wprVMr@ZKzvWYk@N z>a%Sw@HA@9mFby;2ZFkDcVSxxSLNRf8^U`LKb5xs5tuL`@{j3jzbEu{&{O())^fRa zqVFHtj16={RRAuqEMT*UG>{Unj|)?oxp(_E5E|3{6-U@ZO3^E~l2#Y!$}hU_2-C z-Ggyx>Q58_Lov_J^+S@Esp6al^0!Jh&3Q_&g zXDq$8ou4f6#dbZif0m-CK_zwwMa3hVLj)d@0^KHwS2nCw-zuo;S(N6|=i~WfJ3Eu$ zi!PVxfZ!`p97m|R5WcH3K`jT6o&2$1IW|4*yU``5XQcEmrqEL=ItD$3YDONGlP{Ic z)@WKMv=o|`MoSr6wDxRCfybaVG3=hHwy)dU_9}e>3Tx?vk+PE+t>3h$Kai=gb#?_Z$FSH|ZgII9& zNAG&&pBc@i1!HShhflXwh%5Z;thm!Vx-~mi(@iV`dB~1Wv6>Tk$RgjS+s4v*RPCqp zop|HVQQr7$Hn%`gTf8cq1D6O}FX~c}3ZzPB5Q_xFkNEK%2YCn<7-W#`RKiIjIPxd) z-?O7H^Ua-X790$pF4QdzUM44s6ShyqzdT*-H>}NkFFOlmrKYtuav)T8IPymhqAELN zO?oN(jndVUKYBOizfL0v`$t&LuH_TQOnI_?kkI9?=v00x-X;Y{{+Q_56nIHmPI!xo zX8QkIRKzYXig@)O#+d@P6)hY)W~L3$Ubv%c177oXrOpf;~R|5EFl2?7lSd5 zymrmkc!idcQfZpI{ptQfSIH&)7AtQ7Mc1dHsK<@l8FOS0oV~H+24`&$$s2Q1r+p86O{gmCZ_41 zhNkpSLp=RciKlYe@PB`vyXG5VlrlH2ECJJN7OibN@eBfaMo~jj_}j^ zkgTt8B-?q=YMeLjSCNxCjr?cWlu|f<0aKC+V-%J#-#Y7au2uS+*nH%R3eGUHULqDD zR|1%j$uKyCX6jqXNRT zsr&QQJ!}iHz-WBG}oMJNZgL)z>TWdJLLkDR# z=FZ_m>$Uj%J*@R&iN8N+5B!@_Z%ag6#cxE;>8Qsl!0f=EQ{bZyrjiiX zeGkxk_5(ib2=Ac3D!6}(GIL1w8Y12|D|?>~y&N`t(GR=(t@T!m@jxFcknWSzEpoox z3ATpz%E%v@TphPXk=na2-jO+}4FOn=i5>A(dM)5~>9bB;T-Wc$X0B*!u&; zAd!00JvZ=KcH(wbNXb2C@V4JZ{dPs^8IiJuu*9dSW zC)?u{w6IU0IdKZlIf=gfc1wINl`g5oauVcBM_FeKGluY3FCcFkbMKMm80xGwiffGM zZ6w&WGD{Fe2Dxz13>M8$99Hn2F~VWZ)}^{Kr>!b-cCkWwh1U+EEHbI39I0IEh$79- zDl(}fB|Nrl*`iO&mi3}MX8T{ybvd&^wR|d^S=8gq7VBnde*(8w=PT*Wx}U+3j$vGo z*|EHPa;0M zIs!>(uRIwwGUoET2=)CMTOMigse}P3XlBYZM|yFjQqKohNgLoqd}Zq*ze1ZKkJ<** z#nmq}T3BkfN_aVIJtjzmI8~j%?5#sY`ot+|{J72`fr+hxJsm4F@v6EvMYss5b$~Xm ztIU;#VWicd3D?_Z@mrB|-J^_B{GDmN=Fu-Agz#zXtLjLjot+LQQLtA)&)!GDZsd3s zA0*M81SP*IJP26|s^081YQ&QO&>J=1m5FHmeOzqj1z^cH&5P!Wq)$;?F{hSq9$ zx%CrHcQ(n3AP7xp2-m$O~B9ELHn|p23Y&@ny!Ng7o`bdmGY1 z)W_yGLT=e$d@a$`4-^{Xm)#>Apx>Jb_yg9-5N-zekHFpo9lV5-xPSfU>D@0yjV#MS74i&I|%A%>19}rwAd^UuQW#t+lT7@F))n^nXpx!}nxL4=x&f zmI$*l2`(rWGT7u)P+SK5y2uYK(0(g6pN3?}52|788{|Iw#L?u3cMl&F`GH*UW5<#o zcXU?rr3 z&SnY^!u%dbexS1~`=3gF5ZE49eo*s3^sK36I8A=Is*C(kk|{q#+edX09$G(9c%Z)- z!b9KxLE&K;Y@!k=jw(F-OuzjU!oxJ3^B)i%7y->sYkAk{#dPub`(p|ZeDs-x2flHh zC`uLHMR*WgK*i|PktsuXnDOvu6dvY7qLf;9kjbaVXV#@zC#5&*QwtC8{QNUE>+m4e ztWR#w{VWfkC_GH2(IPz1flk810;Y>AJn#}`PS4kr{4gR-ez+)2emFBte(3MX4{}7` zN*}++mmKa#luDBvm_$ZxGYx^u7N(nqv`%v+2k!M&=Ymr^Ne;Xb#*s^SYD#i&;~i-I zYDH`{seKA?ET2&w}!fW*pdK#O91jbIEFKoMF80^MRJ1F+LY4OvlWJ@xl;=fd(J31J_ zUbhOx(ZNWk_%G_<6Y*Y}Cic3W1E{%l0t&!iiWVjp9C)5Q(w10@$qgx9&5ma=Cg*UNN!*$}9|IOWk$X`)9G&<=TO?R0etRQRJe`@tyPDBFM58n~0%bP&P7foPOk5_3#lC z<05MC_A+A2#ev^Bo1Hd%M#QGXBwq!atgd9K#r^{)_0DRneSuf_uO*F`8s9tEp}a+^ zE|6{632LJjw|uEe$jtM6g>Fqg^EO1B|0d06Utco8S2np|<`)C@%#w+LvRQ>Q&-1Zs zd#$f5(0680Y+RmiL`Bxj{=V2X+)d)3o!B&P(4NQ6hra0(b$%j^#!!ahn`-6Y7siTK zJ}Gy$>O2?{_f}=yD9#Vt>-6}rfFLzj443>|hq_l~Ud?ln`#iwCf^aHdzP=)bX$6OR zMx7}QaF*o3pgpfZgvG4!z>UJN*fCWrnpgJz&Fpo~fy6;9f>-MHZv+&|qVkEGR2Obd zcHwrpOD`>#((tUy_9-(EJn?`3qYQgKyH>^;zWR$Vu&bHn+$WGnf5$Q5-+D$;&=F|> zY;gwn!$Nm8TMcRSPBNbA=|3f-Y-o2t{}MA4{pkE^U;~TRO)33c=Q=)GA%bb@y|FZB zoUwG`xNgec<|p!7Fjw2#uqUhoi*)nO3J||?=Sf-#8|eZ;;%q$7A4~<@0xV}t4?)=6 zZ~a$t@iS@{bf=$lRtoN?Er8Zj!~PF@?*boHb?pHs5=^4O6BH!ALyc{ydJEQAreMt> zGjIkbfFg>Is4dsmtyT&%f)dQc$&?%pN77bqX|J^OVM}}U7F(hC026|lfEELI5u}xu z${9x`AVNT8zW;yiGmiwYKH7fwe&5aSH#z(4v)_B|wbx#2?X{4>oIjNR?cw+jn5pC= zYj&n!cGjWn1&DXfI~%)gM8d!{R|4MjVKq70+))VmhRw>;=3b5vC{vcA5Azjha|x7I zl!XI;i?q3C%R8&Yd$Bf`S8J>&8$tlr=6=ZeOfk!zBLF%tVYG2aOn?y2QbHM?0)%)9 z5aOwTF2=6_9;h@xPY=a{7~!-zHK%c0>`Dw3SzaJE@wSKwz=_T4OoH(b{V%?oxX5bV z0RkDSpA*m~#_~QM+5Gr@T=}q9Fr)MR zUZ5%?oH^@KD5D$*^;L!c8M_ulmVWcnGPRO{xSF#O7qQlO6eKXin&Ap;ira|n@m5&o z8)smkakKIy!Sa&Ou;(+qtJVC`351e`3NCP=py~qv=1Rbft7)ONR29JIk{10kJ`mxX z+sHZfYAw@>aGb-3zF8ki;=S-jeKFj67k#mVeNn!t0f6y|{Pb|2R1GrL7qA9g^MzDv zp#r(B!5^oKVE00Ej$67G{Wg(JRt@p%83A6Pe%roe4|}HYH^({UPWyp1VZ33^=cZ~^ zT}@1bkUcsdn1_MNd{d15c&*8ke0FVFzV$$n=at%FUg~QBp&*U!X{o)?8r#q7*-?AG zb-HIhGuEErwMOQ7J(9V$2=ad26VvQlu@^;V(y85g!UUQd#o=rz!U1avR*|ofr-iH1 zOM9erdxTv4q?@gSpbKa@1{6ZFLry>Pnn^DseX2^2yS@iOHgd2*Err6M7aCCwu*ZSm z&`cJo0$vMG7y|}}sfj8Unj0K`Kz33cz4nlGk&{tBpN0WP|Q`Z6!xfoA(WjA}m0a%h&BFd-cKf z`pPc6`UbtJd)9f)eOh4s!3~}ct-8b9G^ouCH6~XLT7?$eA$j(Cwd%e4!A)LsX;30y z@!>tKdULXMP%G{x*kg{YkK<~^Bz&{|F#UEl0=aZ)nwHnOu;;IOp#dxKQB!1AV=_1a<*{ia?4n}b#Es@% zN1*r{#DK8s#koPBSsm8x5xLOWfJ@5A_Iz*Q4sYT1l$2PJO^q%7jc@u3*X#CGCnV1H znH%Geq9#1S#Egd#L?K>t1KxbOQ2LFsf54(QLmq;nyO*3&#}#9KjV;E;-GRb&fx?6K z=o8QYdql5v8}z5ptsMWx9o-tS*I#%fvMSHBG4v~UTCM7Eb>UvGXN%X8Q3u7wVbU5X z8~1r&Pz84_t6O1M-#ciFchKeoExm$Ym3RoZt2ZWBc?Z23+1cyht2}Rd%`IpK1|ii> zf8ky|*_vDxdLz=+YwF#4@`Y~aJ>4FYt1^g<5%}*66t*Lhok1z7aWo27A+lHZltv+Z z6t}`Rty^Wf!~Vh+pXYcebpMig-}L;&@iDW)L|r783R|P6#GDazx+eRu^f=Q zFL5s5Bl}c`WGNX~KP?x!0CMZ9;Utu60<2aL;nKft5WD4vPG=sOF1uYHwoqcMh7AH} z5p>8zG}3b|8UQ0Q@e>XDNX6=50rmuHNTEn=_7muKw%2;Y3clY2IqJG;O}NXO?YVxR z7M*~+$yOMT!9Anw^-3e7UHQ}RFs!SeZ!+vjN?QuQGz(R$< zTD7_Pcn9v%<{rU~%wa1ytJ>Us2z!T#i8^iW8+`E`(&qjJ0nq8(GqdP)*{OYv(1Zmm zVQa8zIa>-ND?*9TM9T39fitqVY30yx;Z1S}R5q7y<3vyL;G4Zg8NC6uDt+{9{n>Ke ze9=!8a_op{mR4G)=2-lZf3p7rxswhZ^&4>R195nK{|63w2Tn+{^9op1S^TZnkLGVJ z%}ODPxBJKex%*v^nOSzJn^53!SzNdS4HnlEP2J_i-T7NreLR2bn3slwO~>JMRRJh6 zoUWLUKm=+!KLFRZ57rIK2~ucw}{X7R;sP8%Oa^IEv=d z@u&?8?iN0*-UsJp_$JmU5T3F_1Z-G8YcT$y(Hjih5$M^4!7jM!wcZDQ9tenVCVX+QKIyv(g z@2tDD%WbNn5#UbzBosWC-i~aV^1##Xf zO37EEp#PJhvw$b=KwmHbi_we#uH%J-e}MXZ!%)qDx(a<5s?q8$)~!+HKp3-5k@krT z@6hJNV6SJpX6FI1b*n$s_&>Q7uzt29aeBJOS&IYa2WkFWWEj|@bJ0LdA92)k2Exv_ zfdjne9i=!jv6-e(<~46Ax2oGs!wa|W%{UAX$8!lc3>=U#s>Ve<&fEcW25>NN5sfGp z@>C6ihG9g(h#d^;-0~XWEa_W>BRS!R@DZ?Nis??7z}dGcBTsujp<(~PG!_RuLonUc z|6ij;KOpD5UsmV9=v*A9`dhX6BJ3A6Rf8kaP!38@##e`ril=x*C3mgQdY}wkQ{5U_ zo(tCZA=eCzZaq*AL$ZGBfl`+ML$e!9-&|{Kk+qPNhG$FdBvP(6;@& zR!!LF*<3r!`iAFlZH3qRPM*)Rr}iSRDFhte3lQXZ4ih3xv(Iy~hPB_Zs350W4nd6yIT6;>}8J;m^wfS|YdaBB2^)aj)FnC)- zuP080ey=qQWrfxy3Kji`15CiPD_AlN`A)0rHw^iT>iP^rK9~ddaz*Y+nY=FKR%=xj zBs=_Yg$=6;gTXTh;?6xyu;TPUEaF^|D9VYm?bcEj>3L5Jyk^RTT1yG}x@R9w z6QJc~R#7(Iwt3B(uxGtCcD*^V0kz;faR@B*M%1NiW19gLsoA>oZ_Sn(e>GnN)O;0l zzD~}}7uXQ)Y=My=^K=^L>6AK+^ORp#z06JR|fvO?=O+JkD@oi*u_2py@6!(XEV;QpM8Ns z2wMkhS@|pd3hnzSf2HNevB$rHl7nD{YmdJLHso4hL#`Pb%{w5J$Y{i;h>dp}k56o% zB&{J$Y;<)Mbtg8?<8sLKAA{BGY|KiOp*OybZP}$avLr5!7~hXac3dGMHTIhdYR9HG zvh>pwv7v?uLjT~Ku+sjwBrf@GH?!R`v3t>xm|I0`;1!#DPhw+J@8c31o;QvrHnPT} zJ3K&MlYk&LJyW}dd;TsSFZh*H%pDeb=HsgZcc1B ztml~4fU!7mW?nRXFI&@!tXa*TBeg@l))=7lTUYzQPrA@+RRNz*Z1;IOYR~mrXW_V6 z3kJqCVD$HE`*^LH1zy}ov@gcKV&KL&?xt{4vnDn=uf2&5*;Uy4#IP2E*qxN=ZMzCr z2?X^cwg5q)mw;iQzoFC2AQjf*egwS3Y96jF^A1~aBSyCNtYKmky7n}$XH)GdUQc^% zp%=cF?fGCBU`L@rX}|erf6F^3`s?4o3sA)4M0sq}XzSTM>WbS6ZnF9cR^#9J29j_^ zhIhbcF&Zx)Meq7du&a=3_;z75u}A;D3uxjIWP_D`-SYy_;ufIA#g@&+K`$H;M2e4r zos9_3TJTKFiA&H{@LPaRCq9W5BM61_>g8xRf~{WD_bf&W_2j_`vu_a^pc*U;c_TXa zFmKjb$SAb7Iy z1x&W{kXpYB=|EXc1Q0g7$_dwnpje1%g-^!c;>LD`S3YWq85XoBP)f!IAUq*XF(~fy3I| z7ZAXKVQ#BL#I?CkBR~RR?h^DD~jFO0O*fo4_sWRq_FjB(IB_FF$=4r)*cLaBaPw?Zk5;728@2-iou{I(Ax=L_RWgu)R- zkO+k%h#(OPAqZ*!O=QgtNyf)wF@TU!2eaCN-eGq))}G}Z_9*$GwWkuB^znMu))sg+ zYxaFWCV`YudKg^T1EAWCbRb3KV1R3(T_Vm`{7iu^Z{r~_^pL%bkuQQ)wx!X^uW&x0 zb>f>biYfSk%0x>XWc-o!??&E)^^o@>yJqNCm@we8#G0AcKD*s#Uo=Fw#H^XuE;|!A zq&*)9WJtb!c4ict`sjy2*+^S~-_=@qa6Xa6?Pg6QeBS`- zYXJ2%Jme{;uL0Bx4n-^`+C53%-2`M&>-Bk7W3@n9qRl-ZNCD{L9R$dWdqW~xz*&<3 z5XeRZfG&szvZz)R5PynaXoP-`bL9bS=@7M#+UJt18T{9iK4C#PE|Ns?Q7zsoz zTcgjqvR^j6=(DaX@Od_B^M1wxW-TzgBHL#G_7~fEJ~SogWN|lP`X1KJW*wv=RyfCy z$z&HZdWkFzsDrDN{yjJ2dU|BLrdxBxZmWHEiydsqhZ`dA$)SgJ%eC%`uSAiU&Lwv3 z3Hj!%Um>{IYu}ZdkG>UQqnb;0D=3g9xraRy7b$C=Sp17~*L$iLXwe_ytM1vVMdu*k z^V|v9e^{dS)y@R?12V<#p$nONjg5fJec_!~c+I`qvJC*v4lObPD685Ez6cn2mDh6z z!g9XKhQ^R`@SC_y53!rpa4D0L(Q?qK0JXs9%>9B_<7ePgdVerOk4WtgOYLe8g^w9* zqvB~sh$Yh3U&6kKrc_&Zv4VHB=uJQp@%{LR*~#7?)mG+JK#H^FT^MH2Uc}kY-GUA~ zIcSF8d>*L8UE9?#G|2^{B;H$b=HkVjfrn^DfK~>ODMxSf6~Pb&I1nr^t3JOKg!NnV z(hfHe?0^JNqJkX|0lFc`0Nqe&Is_@88wW9<8-g5gRe|EmhaNQ!ZZXbiMk{F0paj2C z{L1kgj34yme3kft5hBYGa6|~RY=V{FtRAnMzDejH(S4v0hNT4zQ_}^~p?ctvai;1x z?mqpc0GuFhA_OMjZw?o{KY3ap&%-(bO4x+$f9yJI;v-(q2J8bH%x1;EX@Ox;D6s*@ zn?5)Tt7*pydJ#nE%a*SJ_#Mlc>X;ze0gH{`yH&${!Z{#tt4$2>W_Uz zzR*cy3LlYa0pncomb|#F1n1{jh;|CNI=LDjS_eP1s6 zF?R$}5KsaxERvFWk79zO5RpPapznx?4m$076(U;s4iJ&>Yr5kf-dC&c0P<;940ub7oZ4ofzyh3>$SFBrgd7hWFdG7+q%zdP8BRlh3cAf|- z9yk4U95IliUk3mAtm%-jqW~Vv?4pkdoH$)AkL*B8UOu_{Nwa!&xAg!XV!#^6HUv*5 zPCWx$88j1X3Boco*0B2D@*NnqIoX=Hx^S(z0S1I1NB_df)i0-HYgTnf! z_H456&DG|9fSsquU8?N%cOP$c9wk>(@IAOqU>uU0EYK~(JFW<`JMTil`J;+`JSg`6 z&As56&<>rFWM`L;5nq62=Xq9X^Wr20@opE<3RF)0j+|Odis(nQ#6gx}XTYF^)n4I4 zU(HQT=28lJQ3B4V8?<>%+!f#g;DigdGt_djdYGK2eYsr z1cuhx;FWvLH+|OaMY`uN+T5oIB9J@gt|u*s9WnYFiK$+k+YVlB^v8JfdhUK!i_SyH z=NX5sA&L-49c?Z~AP0NHW}a|9c5msxg+gqd**>0oMr{76?;^RbXZ!^z8iy&d-2+KCyo^@QXq(1PTxP&8=fhHMHr? zKsq|kysHD7!K|>m*8!_S@&Sy$9deOk=m3Ea+v4@Sf|yt6z!C>>qp%%PhXoM&z$Kwo z{Vb$FfOU5hXi}R1%x$`RfKaY4K-^w!{>6D>19yU5xStMpBp_fDU*Q~OLkr|h?rtE> zOPr#119<^G2Om=rC(Om@^>mo4w1=KiJAof`KE(WNbHL}3N7dt6Xh9M@M5mWLmPdTj$H&QFyoP0$2q)yH`4{+mJeTD z*-W5t2Z-RIh=?CRudA)lV3KyxTY%Xx1YYUu{Bk^$C$irFmcf43=lCicqX&uBEphGB z%_rIY;MRp#@GJCNeoegQGgm6S_ERD?E|Qg8aPA@jygKh9&vG;rheF6aszLe$+MHCN zGZnzS0DdZJldH~`=Y;|3z<4qlfW0PX%j=}<2B*YkZO~kuQ{5;Y#;U(3?!~Gfgl{pd zG4?5b7qAcM^Va%Z92<~U9183LO=iskbx^KfLw2ZAGcpg?ujQN9Y^z_#0H%w0MECfi zcKbY9ujAMpeHH<)M;HQH^!M@tPeH$y&sPHRK7tUcVop!HHkVrdy7>`+jBQHwhBDrC zbC!VnwH!R)W+X+WzV2YIARoCnZh5HgsC5@ZfogHw_wXu!cIOhHP95iq9>wW0*( z#;Qu3=~+3D4}m$J$5F_FAE>mXfe8W)Ix!Sx3ag>I7b6M@F z+Pr%ahSGDnX5U4=9Og#=5xg@6gkNw32#*DNNl=}~ggGQO&?o9_D$kQy{RL|M)c){N z(?p>A*PX`zx_JudYz1^CfR1FZtW4-!Wi2ciw%{xPi-BJ?uGo^T0DJb>Hf-Z#*OK0H z=LZBO6sPjT~PEv!(Ms^zgvRPobSCPL^_&K zfLJg5G=Zk>2X?Z#M;+{OlR=U%6wqwhg?-=!byj%QXnCvDzCRa@oNH12hA42OHg_Dt z%Ihhv>P1OTxI-VXN&~|lC17a{ze02X;0vSoi(pr5aZh-;u?)gSoMUPKL=V@NRYKfz z5%Re6PicU_JaJ{(6&)~(liUG$4z6;zGTu(z3Y3YE_v*6pf&0t|k&$T;7nwgKDl+V6 zdLW`(F`0shGW4$>g$y;!Kr0ZE8v;ghLqJIqDx#mdJVW86Kf?+>x?t8U;*FUwy#YUo zu80^1-N0(1vlIY?^Vsv~i)w@sYtBJ`Zfnwt8(P^=t$1#0i98=}Wh=GfM_Nnxyrx6D zW{cL>jqR&qS$$}=vTV}@_Ga1+A&!=-MI9ffmcMO(Ff~WfdpWlCtGO9=+cSIEqaQD) zqFEh=bzZ=}y+HhR^jQBcO41rOO4pA#{r_AR181w|2mL>qgvaUqAgK81gbMr!%H|cN~pKy^Y_l z%)%cjyNd%-`Y43WPi${-mloa1p|&uFk<|s6v5sH;-$m%I8t(}hXMoxa_`wj`nrhb7 zwozc6ZjH^e?uAI<1zwv2O>VaK&|jF=O@El^%>3{^v&Bg0A84>C4!caS9|?MqfZ z0b=Gg=iz6#R<}9d;<6&>OCM=|HB(+U`it-cqdb1=CMsM6Z__Otj>dtay0#!o-(px* zX>Cc}Wo&BSx~~kIP(CZ`{A^fX<@JWmiHkiGu!*#wRzhU@{AF0vQ1*e)p91!`(dwEG zTph4c9YTKduhrJAxaRsgtiWzaOjnvaPXMdPA{teSy`roV3PB|d3`L-r0dRNn{T39i z%s^Q=BTD_eUxSKq#VfB&p1=sj)#j~Om)M%Iu{OgNYY+kw6O8(|i!sV_!4muX1ub{dXKER&&)m8hJ4~KF zEMJT8Xc!Ym#xfx>ISYXXz(QHLSupB0=gZV1G8cC6)n38J%E3tC+$Hg?ZB_3>_TT%| zZ;@xTva6 zW@BU(7_zTHE8kY$j356iw$)*`ZibEU`qk_em&Q%{%{6p4mKfk%_!mV6sOAya)*weJ z_dU!F_Rj9D>`2VXub?xHF#2#Dd%?}yviUz)Oolz96sBrRbD)EPD^VP)@Qn9!?i*-W z#`eg4&Iz>x+Vq0lIYxQIn$U@DmF2llLXf(vHrRO48>*v_H6j$d?7Lo zkLmfFsLKqtqb5JEIPVWw70KHYyH)Zh?z@ZJxPq&?dvvIStH#uNjdeCrmxV zu%TgRM!8QX{E!}Tqm93Wk^t#i!*d8!&cWST^`Wu>aev@Pr4gK!x$?-m%0?uN<>NC( zP6`6qFcjs{iq88hG3rM*ekb{k3U52LRPQQS9Lk12$Y}=f38@2|A9eDvn z0bAHO2{o}I%++pQ`}_A}7C-f|1U2`WW6*j7M04bM98lacymKlGe6$Pj1mfD{2#*IH zIpZ+`kKDE>H8(+VJU$*zzXhjnP+KvTQ)pAMQ*Ccqi93G!pK>AV19*Q>lLGH(DKa8v z;LYh6(%V#@tITc(^nzZB9Lm>b??jipbC$9d>Ovgl<#6&r`d3@F7wOuyWzSZyT{tL4 zXkHweRWQj8Oc%I3N53#|d7j@BxV%7%(8XxAjbOEX_2qe$jeC!@gm>gOis(XHww}5( zE!*>|ts5`L#N2o}kkF02!DzX$Hz*UZfF254=@+} z)9WpJioK1U@hh=vu;0jB&oK&Qc@${NR`HhVgZt4jl=zx=&a>xmAXefaNH}ml*iw|! zR!IzF%eb2bZMb?!yt*AL(IKi5Y{YfF25G6#*s?1xkeg`JnM=+>hKUfQ_Y{3!2?V19 zC&?V^CHfqYgN;{~gC}2BYOIIBt?!odW2rHyQ?NHIUzZYU`@$~v!2^c2qFK-JmX*)B zvfNvK7#R)gK83kZ^;(6v)+IZ04d~c(GL*l!01V-J#!T@Y? z2MkIh+OjA63rwt=b_VRl5g7Ut82S?!`WH+cLU_}bb%Fzq?m`W31s9bw1uPalQ*C2s zUVd4Aqf`TbnOGgk;CI2aD{FTi-dvQ!wPNtRh3M zKcQrncVt!QeKZl^JykfwY5bKpv@Bl_$GSoX)OyQNHDkGqn2hD3%tL6=M=@={+vs(ULby+ zFdoXG0-#RKD=Fyg^Xhd(2Q7dufl%ytM+0a%k^?{$WcJ>EeF}l(!df?g6926UMT0ul zJGmX$H%+}rE3PWm`s&4Vs>&O6fZ7Cr=v#o{QqY-z&<@mspaRgMG=Sz*9TO@Qxs}}B zbN|V{e_%kku*7R!21OR!2f7XFSo@(ij#Y*k9G^ybbTtm58-HNkEeTlVUUNHypRm;M zJQ>F#N$@uAfH_?JPwU65(;^Fz1!;ks;0n^LSxz=&q zU;l|4`7>ms;#Aa($jrUjDsexRWIi#NIv4=hHD(;C+&wF()1BOozJMP*Kc1pJM1ke}+)ylgz`p z%TO6TypiHeuXoy0kKsO8@!VJ^&6bxliuCJ;d80wI_3twd$5BYi8%wl{5R;` zj+g)L^ls=6pP$~F2j%=O-}#6MDLzFAqCzkdiTCe%>U85|1f&@ zWUygBDZRU89oP5&&*|Mfa7{lMz56S$8vl9p?p2epfc_rz?)j_#N%U^}TF|>Y@GonB z??Lb0{bC9_`@A)l2;$@E-JwXHp?8OlOQDqiIrMHhuPS)06CqT4mKVwreM#PA(KpHG zaba9*2!R>|TU*^fHLNFFsn+SzxpAR$;~6@4>KY>ohCE2T;B!9nZN25~-jK|8!Ba1E z!}ay=>QHS3H7WK5pib!R^-I!x-d=ws&DSd;s?WX`pSuObrlSLB4cLwInR{IOFO0Az zg7iNjHbU5nZ6mPN@`4090=0j0Q&#&&TUS$8w_m#(fW?Y*s5l)eNry_)q4IQSa5|K2 zx8hM)M(9FMx-8#|P(|}Y539QHO(Qna0ErIAy(4)pc};1wu&ytbAS}qO34K9=xcI#$Gyp;PI#(WdVp_u&a9W@VZMnv?);Rd8 zF>viMeLPoQEw`1*;Ny99Vpw;0`kQxvdbyc#2WYik|6vZmOlzQbI-mLa80*H}n9OZZ zr|NL-dO`_Kz6{#!sa(*~a(<~lq$9YT|3qH$0*FG9m-Lboj>t>u`SB9SOInYXm%zVu zupgu)0=>u!Y02Aha^*x|GEE6gK&E#&F}MbBKp1X=n!B&Z3_iFlgAXpu;Dgc(KIpIT0X@J&5keW0`2^VDlJQyC z;Nri%{@vuM^^fJCH9V@;|7foNFjeQJGKZlvp^;IPbUeurI5pU=Ue4W4C0YFmrd zV%`(q!|dtrU~Y@9DA9`ZPBSNwv&D6$dHY=EB@I&uC79*#jr5%sdh+SeS8&%+3VKZ$ zMbjmj%vPU|s^~iro`uT+qq$h)KFU*jKLFwus$=wjk~{rQTuOkWQeH~ndb;;XHlYh# z20p*h=E?|o@RWYTK(B(9e6}XXTA#(m8d=i7!FMh z%Yz{0yZmmAhJ3d#65~F(2cELlhF$~{(tr$a{7T4NP+!t{^!FsRgOgUG7ZUNXiWmj# zbw^IH64yIR-K!|UuX13DARHTQ;l2w%FE|il<~zu-hcGdw7tm-8IQH-bK!Lc({4l1O zxA@K22-EWZZul1aKg^(&Lcv~E(Pp&93pEuOGNr-a zf?xkve!&>UdDqm12*a)#Ofu1LZc{g119k;@se0tYUcoc*|AdhD8BT{@Ung)9yfx8Z z^y@|$7-*OW88u!Lo+l;{YPrtD$5`vG_{k_F3!l(F=ea|+q7<2!;CXKHFCzJnt#NKz z$d$n_5aPB)j`X^B0REZpg*xN(wv|<$e{$ZkKyGJpdsdk*5QhHyKUrY9ox(f);Prd;6Ez{r1YJhJ3MQivTPyuGrAE^Vl4*|os#2EyE zzF5Igvlu)JIs(Ul@&w#4AAqz>!S{`Nje_{59VqccH1#X^-HcxfAEn@3@JGg46h>Q% zK898YtTE-)RNQOB3xK9X1P8D_N6Bf!a)~scV1*QPk{z(?g;G9%n;U9>H(xcZXZH$W z1U>HP7m>)YACU;6LR{3Q4odU{#B-~h2`U<|2c_Vdpri4^yADujg&W=0()uG@&r=sK zSA=bsq`SW~z77G%u3@+(GIMZFa1d{R6Geos|A59swiH-X%aqiazOI*0x>R{#fKi`1Eg=Kjs zvpo%n3%;Jap zm!}*yLgXGg(`#K@s@qd@;f4@c@7}?p78W0fA*6OMowOlEftv!c+V|**Tln(vQ}73O@#L!z6O}3T-xl5jocf z?5pVh4fdyZLMtTWybV{p;~1aby`-m?7UXV<(_;!;FAsKZ_-zrqG@~8G$7;vaySlgG z3t4SwQuXK4e@dqAw~kZy?2#O0)}1JkQ8K3!V7YnBhw6q5i8;MM!LUS${Y_bH5jNHu zS87;SVnu8So}PlYj7Ug^57ytl7-wT)Z&jMdVFM*zA? z*_PU11-27Yx^C*fWm{?6L|=R@It;$~lW+(fMn>%B?e6gh`U00W2Vez|?Q~?v+bcWX z-ec}~7y3B1{rcvvO7xcYnAh^=8w! zf{ew+e`>y|sAxU8PT^x^-(Ozr>wt@)dIZ4#1or)(6=ZzHXRz=8@CbD}gMI%OVGVT# znu<%}Vb~38B=<$=#z8=WP>16;sH>P!>Q^UDQtO+Ex%Z5Q>;{~KfOS_pbaQ5FMso7dZ(_2iYHTcJ2gJh4?&yII!&0v zg)M1DmY6n0pQ!b$>_Nak88yjqG01Pyzj5j=W$*J)16k&vV|fe}Q}x>%zfIp{)AC&7gsUp(poyS)mILgm)wtr*S<1vk5M~*<9d|PM= z6@v<%ss!$1ROlAI7e9s7px!Z#!LpL4np5wUqDb^q4q=Ar2lKuBsmj!QDc=V_MePFl zUd;EQPc^&m!9m3M2Vv;5%^27Qy5T*e3M1Pq; zrTp~%%Lun=7=_}C>&48AZ6WQ&?B|#PC<-p9Y8QG0olXla&fv_&cC4>Kwr0DDB$u;n>`KV3F5SjKE|TW zyu6zex|lgUy;v2l4##a~J zke5If0?AHFGgcYtgM*>{9-5?Y5%EkNrYsB><633zdS$d+Ylup#`@LjXOW6~{JRS5V zx{xUA&R-ReV=vJku`;y;gTe=sbW4o<+U()@pFt|!U5h=R&4t?*Dpgz;-uMST(PrmL z12PVY-^@Naq4{#2fI?-zf|NsIOo%3Xcbpt|`gpw!meCjSUlad@@lS3{rTsvEF8^)l z^jqoCQEs41aK8koxacKdo3#ID?(^cmZJqdk=IhgyXdnhm!2TPc1QTaKgABT?YN(@J zM)4&W0GO-8eJ#e)XqMU&Q#e6uSdCJ|HySq|*yA$n;joLU>`A_bQgQdr^{Wis@~szQ zDW7+If65~jy(7y?$C@Aex7Yk>Sn6vZCvJsDc2{5Uwkp$$dk@Lge$QW~{=kxL9S+cL zfXZ;4+hb)?lAUr!fNz+Xl}>ebw&!FHPG~r3tFxKvW-wQ>krh8YcRQTJj7)hk&F1qnns~boI&W4kE`M=LuARuN-zT0)oArpGZXSdxk1{CFSvqEUGNp zC(+yaA-PC2i?YYLi+Lhah;ntt;+*k_CA%qrgh$IS>ffdr39$!_(_WW92C@e4?llr4 zb=#rMu@_R4uo~@UlyEr0 zR5fAe$LS>JgEQYmuA||BCTYV`^bs)&5Hl3I{Z{Yi0E?glc^}4v>loF|AqSkFgZ{;4 zcQJ@@-a=9O_UJWLVB)1P+t+)Tul<2jpsx>#JSd4nuLbOjr=x;^DkaVxt{eZ+8_fyi zI^9R;e74B-NOkG2WrVExq(EhWVCr`pZVa@e3C`2(lqF%qIvIR4G(QK;57ruX-ulG7 z*`xMn%$*u8lr8Yxk7diKRlDcw^x!RG;+im0a{Qbd(<#sICU4Ci$>2SyQN+%eBbcU) znC9M-5bm2-#@O3*?O#s3w~!Gha{PvXHI{UY|B!)&LHg{`+=bDsd#yV@PXXEYTG4yx zW)nWMK)#7CCtt;{7e0Rxzu)1PgANSFk6q?@z`air8>M*rHf}e^MwOtyU?z-472VI++dl3Y%|eM~l@rKKO@ZizL9(mM|2S$YuudhwsZ(2rKGG9gH#O zZI;LE?R>;$J9zLfHoFu5$yJWc=FJEG#b$TmKjRGZPdNlYnEMT+B{ZGEq1dOfYGx?; zjA$;9KLlwhj>&TWPUJIL{2@;#c|$nYg1K#u!bt@SZf|frgf>9t4XhLxiSnKk!}md+ z{58WGfufp&XJ+{KTw4|yc4_|8QSzT>1Lo{z7G=(B$3q-F)WqLkK!hgut1|Lfb^g<7 z3-CifZrOl40Fi~dZsTiaU@&6Zb?eD84p6|p9NQF@YoBWPG> zGM0ToEbcKkkaOm)PnSU)%-rvUQ*ALStn4oXHS}1YH-ldX&fYfg>%dfopN(g;`$OL0 zY}zk3?EYTq!Yjt)ezT3-!^xdKoD2)%_v0f&rHN^L3{BkSx2`TTcq<8P#+#+JSC@^) zJ5Fy+ew-UU8+pY_81uoN_wOMQOSgBUoX?Km-ozhp-`=w~bQT*aOko)8DDI9A%ua9P zTj8C#NH=ZrA9EWvXwgY0aSS0QX>(5fA@PdVPzSJ1P3m$^8)!K6yN*z9_pW6Iz}e)y zCuFbEt%kBNdM$7#wj~yN$cPi;i1r90 z1*L?(8Xv<*e1|BkwoH&o=-fDOFv+;mBG+=RLca&2sx_R$)}lAg$RB|Df|e05xzFBv zzJB0&IH39^ipsPPx)A7hKctP%!3O~5Lxs{jwGF4B5(k9Uz>F-9aG8LO!v+D$#CQAv z&t}Bo!~hZt^V^p(0*%dyUc8I!Sd(|u~<8XkjOiJ!M@Ja8hXcybe`Sm7RbJf25iF^Pw812RoZrQy?-flyNz54$f! zEs1-(&*JUfXR%KPUflT{nPT`KJ)iq9T#Em%<`dd|S@;kpw9(&7a4cefy}Sk*e+5)x z8o#q&*1Sx=1Z(QjJ6Va^?BAlnScP9?7tK|pQ!9z46AI#srB^s##{bg|4UPX7Lb6r} zqClGbVpwp_Ra-V5Q7|LbK%jQ@jjqQWZq34%kiVTt^Z6-!xv@uoV87OLreSU*zGPv? zz?XDojY(-Blh0f``Y4o7e3}B#IPD*%U^6}oFR7^!q35%CZ2K7*coc;H5@OYPCxu7< zUihB(RSlG`4|}qVJAY^_wWnUUdrT1VqdPrw6q4NCo1I++ApCsULvyGc3^U*y6Q@|3X6AusD`r`gC_b zXSF+50-k!J_2fF;R4a!|DPZn&&ia^6^%mUNdnZT#Se@blBZVAgpHAV&_-|we-A!A; zA4Qnin>L^Zn|2hj(D!ur!Ou2-EU7#Enfqktn$IfjKHrk|zXYP=M@H@;5*>lEg2YTd zN)zxe0c#-8AK`W_^hNNSsoffGPnG=}MHBq}_G`dbv@c9vx#;T@1mkxALehgt^pqaV z-{B>VrPW~mls(I$fYe|%BG#RMMOXY|#&ajZ!u+2$o-J?_<&Nh{dW^FBjAlDiN9*qR z%kEQ?{gIoFP$OEy{m2`?3;)u)lGbn=<0iRrLW^h(S250uf8F;et--?&7vf(Q{s$mk zki8!yn{`vsD2fu9V*;}*g*5`sd5UzIzQqq3Ge8=1$bn1?#U7wu|7=+pvrP&r_(Y;k z$oX`OL-+7Iu{-fd@cW6IMaPTZ6(u!}-w9(mVL(j7-JqVR0-fI9(|rAx_zlOe9ls6u zZNYCles8~@Odi4Sv;)axIes(o^BznlufgvQ{F?CXe#AB4_Yi)?h+B)_EBL*RUvXD5 zc@BPG#_v-6-o&pHzr*8iD$IFv>NMC~A1(;%bG3$rlK>U7(-xK&P$L~4(9z)t@{9eY-!7qFM zdA|PNt^aXBN(UpO|M5=aKS}?iELHcX(*O8hJ>EY&Kh@{Vcu$}G&l&Fl>AFATc$3=s zC9nag9W8ndwz+_HRmpOqCK!UPj&2ZXiq`NTa9OoIhdROJcdg0BN9GH_KKn#n7!*+S zt4Qj%=Sl=D2g96^P)1P=@$kJ!>qbnLhgZBnFAOgqkU|svww6-rhW)3sKx-&qS-7Vk ze~k&OlSH!5WBZ!rXfv=Q5m~_Y7rVuVa7@rkR#qaG443QhR=P6mzQIVnUcR!~eY+fQ zP~>8{Nck1K4OM<6@iq`|mCCIo-r!YIH^i%?QY-*_=UkFpBJhEHS;RbGT3Q1M2mPR| z9Wd9Ct7PUwL&q?u7uU3c9`N5l|SsPDBZ~_Fp+4x8Cr%nvdQ2Zq5 z7~>Gzji1z$Uo{f+KDa(@)Fz;YAIcg{}SJH-1dOsz; zCq*ju-an`Q*%-ow=%1Y!z8~{9EOjXDHh-O;L?3PW|9E}0|2S9R{gddU$@oS9IG=NT z|39tYH|CsAJf7qS|DV3{Hm7m!{{nsGX=e!*`jq<0mrJfssIQzYADARtB@NXFPX(#Z z*DO+Bsmm?Gr8zlb7_hGm`Wm*p7J+QWCU#0O1c$a!Vwm;<#zdMcAr+T2uqW2=eegPB zK5hZ9II{pbk>{Lmo!J+g@MM@9dQ|0UMV^H!&mQMY5-he;@^44+iSw9d`|EDo)!5#R5AkndWK<7ijBaNIq~@4@iAv9k z;w(fe`VY4dtKtc*`_#GFn% zyXk8}QusmmnFdszqeUOYjN(e#VB9pq`to&HRWqzHwGh$Gk`EVu4-XHd?`8{yBLmDf z;fUilL~S;G=$~9PFF;n=kOTGbQ2`1$cw?Ljw!&wqpD99`pg+C{bqZ^tex@)Mf|n&g z$&LZbzNJ7|2Zm?N_~1ZX9zzY^&d@{2Hr{PR|N6{;M#Quk!nc_o_fJlX`Q2JK30$Nq zY)@R0s%QlbuBYiMT$d3sf#IndADqd%YRm5<^D{Iu5mdL??)F-fahpwag@d;k?w= zf4;(gkzLsIVt*vYd#0HieDPc?4_+9zde>zqy;8@5R{&)f#{*Ulvkhd6-|w*dytNYe}}EL>6~ zS&kd#7>PB(!p@{J-zuMwBAi0B~{_lg&5)=0|3a%A)i2scF~M2-TLqZv7xnS(lXaUMh<1dmcst_qlk0)*ZKwf?4a zLqxju7%4K)X97y-mLHrT>6Rbtpl*$nPw3W&le^`>(2i2wLXIBY;wLn62^Cr=--ZUm zOB5Q(rlBR@fH{rjeiKH`#lCiB64F`TFCNT;Rmz4oC$~Keq7@m$X2FUEHRFFoB%(-< z{?3hBq@q5@a$s%OqZCq|_ix#oFU z{ZeP)boRG*Ka#p?d=Wj^pkejOQqDD3GVjG>h%Db`JTZ99!K%(=B?hEs^XKRV#;`bh z4B0H}@3LI)&7S+rD%|z}hYWr)H>Z|s^|2E!Lqd-60X(@}yRX-t_Qq@C9bDETZ{zqI zRvm9~2lD|iHOX5FdZwot^OSxG)AxKAdPmv1ITG`|PAF6>BP8esCz#Od?8Z+Bs_trB zKAF?VdBViiV447~z};W#OjZ@>&k=!lM}Y8qtE>YRcY3v!nMHaenHwrhMdC0|2rqH% zMYtgSR=paP?X#$wD94(k^b^T&0HnQfq!_IGJlq27!qLaTG36-M1I|(tzsnA|JP38n zwtCOOS^s!EhyzXz+JL3m#6fX+5CQA**9u|i(!M>#gTUTUnfA?e(D`?+6ziNPkO)`k zu_hKS0+kQOLl+(_==1U*4myLbM-}G6Mihv%cN2>zN8zMT`J4~*6vR}U+sB+>gMN4Sns8RGjJ390`Es?60+Gc?6@@R3`XuD&7FSjiL=YN z!ZA+UoTuCsvxgnAqS7%l%0{^3i%51FTp}4$mof36yMDeP*(wflO0&2fD_E+_Vz^b3 z%v!_+L%sIJhNmmo9{}*7F$fkw)*F!>9~!w3XjCtD?qr7uRYx(l{O0OU(Y##%VARhR zJ9TV}RdIEXCZ3tq#8n8#X}AHqQX-(Z7m~LM9~773{f&y%VO5;mqoDPuOQl)wrg1xd z7cz5KcpA~1|3Ap*)$vQQR?(gKQcM`02_OFrVh0IP!YvA57sjXfg_s_(4?xNYG$j;oL70t!*sLX3V%S##Kp=*L zd4!z+5V)tF0BKuC%;%sh7zMb_fJuPj7~i}?@o%=fk| zLX}>E+3DYA`VH4)r|-;Tp(?!uv(tAn{V%$u-w(a8RC)=f(ifpUxS6uenacFUxRg%y z*l$wjUvA}o^E^Kl|M91m^fxgTvC0BKCmuz7z&y?o0m2q6VT{Qn5KZuBkui0_!M+UH zxIwriJCcE9cZMPs$lfXA)4Ex)@I?PsPS-fzCN znkH~~-9NB(?jSvfmCr$1It_!=bG?h~(wTzOAchwj3(hD&oHA2ba4h^M=?&{%o=TGA zu@lgsAyc~dV{p-DFw`yZ7>a8;Kw8bgJUO12T?(5X58uGUQ#sJF4BJP3>k7~yT0<^k zK)5Hz1ADeQYe8dc%TB9o+*BEUGdI*5?jsU;bcCWU8(P_TD7-`B_AqV+b|#p)Jc1Pu zpFl{OY-RKf#c*kamu9|H7V{Il_-qIcmGgqO3?+RPE|{Zj#6-zfDs3m%hIS`PnCLWj zgbFc^RKNH+68Yf<*s6nn$Yk)OPCYrQK>oqgE{?Y~L#xeMIG}xIt_@4Q_?gUkLDyPF z3C9HX4e~**U|8U-pylQw*zM99{u>(+h%pXgPbp)xl4M~lIvx)+n)KFU=-LJBM=S9F zGXBMTB#K4WNJTzu(UqVgB@K@KC0x3(=wLT@EIJe+{y~`73EI_fj|3n@mqQ&84;BFN zVkN|js({_j&#^pF%d@0&MXnQFqS8wBvFHM1k#@zRO_^Y0CKz^uvFIX%xIw5ROmm9i zfvvll^vlI#Q4?vixtJtEk+2Dh>3pl=n-8&*D>|j>Rn8A2Y5zM_YyrxJ5RxCA2k^yj z`OsvZx>syQ9AuTr4yiL*%9I!lw@KQv`OMqcQ3ig{nS|H%Z^h5x(`-#Z_a7Xqipc&n z5y;VT{Aipz2hLuEFPj*Zm>NQY<7dO=yrrM)3qqY$iZ|iqG<7ffK1mS=dW`5toZF*nIbmk>3%5J znJfub+=2*-i%V4cFlLHT&SUD(D&e0XjPss!#9XL4Awp#gSThsfCkggAXP{r0ly~=~ zCxv;OCskR@=-kM$p9)d}2aI?Po-!#b2!_rjQp|2=1D?uCE~u*dn4CEh-v-Xbveg=< zfOJ)-{M4$|qHtP8gh9;1)$1~B`JEhFEjm2?9fxOpk6*|Lkx}bC8k?Wh*x$1uyp=@q zuB%NQe&B%AX`G_YCzMgk2uw!=hkXEYC>iw{mHcDJO?Pwcz!T%PjNqmYkTPzyj8-t| zl(8unE8u5E|AlXfTiC8!P!aAH7h?pqWed5a4zvooP9!iO3%4Tzg2+NlY$1{`ZfOYu zhFx?T3g?=0Zoa1|=RlNMb79oeUb+RL#D!{w4eZgV-AJ3blNIlNo)s^3kk~BPcz!OEI+%Fy3< z*qw^IrRMqNEZ15}e1T-L{%yYunN+dsI}&EMoL5ws_{4cZg#{r!r^3xhLpJQGDtjd$ z742rM^UF*M=lcv>+346s#fZ$tyHK-WMc+blqRtc0A58Bnd(zV@ZC$8JUd*IEcKM4W zsLj@RU-kSHHFe#sYFZ!-thl>JO<&EbX)D6<-^;+avT?WKT^)o6tX3$}s<^mE{`XLP z0)#@$d9PK{B&yX^O507A_=Y=`8{7n?S#y0ovx5)E$&}gfBLZQWjmuS7W@8A#@!r6o z00sj28fKJ3`b-q7*6a7*O^>21l55jxu65J65VH>-$a%N~&2v2Xr}QJvK}ecXaos&A z$(|VIu_5N=6!k9SZY}#L&fpaz-^QEOe^eX~q{(ML*8q2t`m<++od@qi#12GoWVWnQ zqX&dQu(^%jzD_tYWtflPU3F!No6xFgdxy1&T8#k2+;p0`>B3}$zl5%^1-Ikrd2W5-G>e~3nv5=<~>AW+SVIgPnk9*MHFhmJb6*f4bDD1bd zEQU(AXMOO#AqRM$n)m60g)jzsfs9}HzTq&0jn~tmqOD)3mzuGI+q6Iz_Pd`pBfYk) z84d(N^CyG-#GXZ#z2)=de)db<&L2=g1!KsAOH$ywSU_~Js1Zx(=<}XB4{bxok2~iFn?fW z6ve7Y&V&u?0hWfzPL8k4K07g?@bAH10}rf=uQ8vPQ;7LG`Nn}@Ry)+09B(wX3~6py zBUb{R=GPz`rdD@Koz&1v8Ux#{iCwUARM?JDfZ99fqkDfAA=}aYQT(mc5Lvwf*7C(j z=qx$+05)!bDF=!MdeSVcEdLP9=&Dj9r@0UVtJ_LSmk&q1w$ z95aYJeld-ByJoQ%A3`h8s2kBJr>&CZEW5UpB-E*XdwMA?{egr!MakdbjTWv*r(>nX zoSz^q!Z;2e2;M}ACU~6V%VDc+F{~w>2(NO9-c4ZBEFr_-vaE`R-++p0kXc(caWU&D zLuLsO%j+>`pu>3* z%}mX}xd?Lx&c+iXfEk!lib@aH@oYA$$sLQNb3F>g4u@kbzWsma^^G8-s%3>u5Ju+*t zGX$R!W7S)S6XzRnSEsGGlK~$F@5w7~7ArSEgf|oE>Z%=C1!Y`HVRg z_?F(UYvQk7eGGo!^N2(q!PwhSDk%XJ@iKz!?%G>Y`$bLs;codAzP2k)c)%6+IVVW< zDK-`WBCRi(;9FN(V!hUo- z4ow4|N}aEu77nF}ViE&6A3x#%i5`wCnfNIpki(gSCrA56$r5vFmkUex=<##yAJ1@^ zdodqaRUlAfK0e$daS4sU02;}v0RVM;{ZO*8)s1sj0yG-7;)*d4k~%@V$rnIPB-x>o zEdg3s;QUFadPQh?H4sg2aO2QX>((W7P+x6^umEb7e-4funzq0hg2ssC6X$qDgfXwe zU;4d}w_;gL1j)`~%q!4Vn7pAhw{l{mwruGgKrC>0?Q$&NmE*41&hzXGorH7nNhr5I zQ7~j{0?JXdn!vQ=$@os>lB6>bfPI?g5QM`Uf|sQBUv1fm+A_`45<1sN#)yi+yG%gU z7->#%Cfp=k%ZFQe^%zt$o8a7r7P1RY4FYgrUZ`86N_Eo_qV<)3Z+79h_t9hv58 zj1*qvWqwgl0qh*i;%JN%9W>Y;qUs8`8TghcQYfR@>2yJ?OqlxQc7>h4ATIGUm12Rj zj&I*5@RctkGH4J$5&9*I)H*RX@+eYkWKnFSOKOe7%#SJw*yDT%M(9j>ayuq8QZP@nWHV6xaOwgTv2A@ymN;>(-Mf-!Hla0u*oY2{_-{br?e#ime zkYC!{ZU;(fZ>0OKPO35ilxVZ{36qxvkhYBpL9$7rYuz zA+m8eEpVO&O#p8-qIAF!RP-vb5B9klrMf%~;Q#_d>siubs>}NWs@p z^()^}Y{EBHFtgq=?Rj^?Kdn8-9FOmze*fnmk8bC}o59;)-Uv14}gFjHvkSA z<9`-ULT?VvA2@^oJ_b1ddX%1wmzZ?ah?tEN}XR81C&ZH}`|K@4Ss)mb_5A=@Je)od13!1ifwWZtm7}j-IAK-*; z3GD|oafv+E9VEJsSBkzSzaTw5x^NBM^-_wC9BaMoAOClOSCH4lA3fT5T>7;{H#dQV zr9Q&N29aRs#ixmWNH&*4hbc1a#oXXj!@B&mO*}AHyzR#s|8G z1NPlGHSTe4Tn)F9XK_<&(o%ZgnYiq4B&g=9KH!; zJGG{(Ml&7ykmWl(OUBT<=M?~R&cm190`VBd=5~Wfv3@BQ`->1RP zyF$NDFD`}OQ}Cwrn{@NferXlwvj(jSSd)2t!yyNX^w3R(Rp;P$(71O}`zwvY34%C9 zcwmQNrP-Ns1-k=+nT_m$4^lIZ4P}4gyxW?rv)(skAY&ireG@*@Q1LlzR`_3ln?05x6&loo5GIDd)al zCT-}i*iuOGTfk{~ro#50vA5Y zF8NzNJi!llc-n(4QpSo7Na-N#TaJf6`J#B3>R!b66?q4WMAqk-mH3m@+|Od|ie-#TF*|QV90sJ6-!D~p2Hc!AAS3ZT z{>462YM@IQl!Oq^wG3gL^J-7WshOW3bz(db^*sS1PH8CUQ8v!FaG?w)^ar8por0k8 z{1Ou@^(-#vr5b*Vb2+p$ZLvS@{1BUvk-L>^N_wRHx{S3i?L_$`bkS~#b*jcnjgwS? z&5%~Hz*tliCMZO;qnjy$JEmfVQla3&Hf+07CF6rdAP2P-RB2rdg(Z@QV)uJaDrnjz z(xPZ7A9j?@fU-_G-=NImCWC4ZLY(bbba**L<=c@dNW$b;T1ls+CeP;zX%F+QWlNX~Z;qNSDle^&` zOIrIBn>z?6aWQIcB=gn+-+l>VY3U-c@>!7?K!7}S=NplsbkRjYRirc551A7tuk{;6 zIpG!3wIJw~rBeX6Uh~n_0={C@A5hHMBZ)MqMQd1u3XPUSy^P4AT(#b{QS13Jx?v?g zJK1lWmxV57eb zSAQ?Bh4{4;JJN^1*X9vKz&Y}i_OS@G-{#jIc**eW3vxUIy@q#^?c-TDbwAcp-8EP% zSRHDKKklGE&S`@HvzE=JX?<5P%?>~(68F10Yhk@petb+*2FwNAqR`HdQM$StVc6$1 zO(cS;*olYgkKh+%c~8d6VJ04&SR>Wu}EMkyavp8rYhniV$M_%7pVsPxM$9Ol9Q;AICVQv zA}OOBP$dRPH11Hd!sgf#!;1X)06b4K+RmxPCMbL542fEh6%}59W;2%J z|1;Q{fm!|dp44TlGVH}_BIJRI=+BRK#TT<1H&Mmyo6w`6%Ng(9BvMyNNEB_nKuUN$ zdk;O0DI+UbB_wR?*_h=RjXM6&okp|)I*9l$5oFDT`NOA&<e-noXf|(gSkf93BC!^y)bu^6 zZv2?OuwY62Lt>T5u2r6iB z63O8(E$t`$Xsa!)+S1lmT7||7n&FZJQ3#>}Qstt;8Ajz!f|ulf*WTyMB?PMd`t|vq z|NrwmnRCuQd!N1c+H0@9*4k^W4c2n}j{SMv+A}UAznY{VbGdv&XOsgpd>xIx!Nh(e z{~^0o*SULQR{ma+-)yoL%Q)Dt=^XV!GTlkE6Yv6)T~bRn+O8!%bp6Yr>p7R~iFM)Y zACoVHjVHG6H||vv0vg_b0Sw$E<9nQ}Jm|DX)%`V)^64CF_057g@G=nYTVEmrrx#qa znis3~8Q*3N6UomGHu*h8ZodZWmOUV?fLV|>+m+I?*wO{m3l^*S7$5rm&h7j>xui)l zA!$;dKB^hA#HeOD&x1D6f^^gNBFIrDE}zbA-8cKZ$vf3bTJT>%1)DtrQMXlp2MILY z>Tb-DxY_{kfMm!9-cQNIFv%J#^FgMC4)-H7wnSdWhi=RM>UT+#UPuY)#aIEJS}1id z^mA@6&ZDIQV9qA$1YnxJajdH3zHDUpxI|i|7ApHe-s^7Q>0K-QS=L;yUO>M(T9o0@ z(97i$CrC$~qKoA-@u8KZ)2o~4+%qTg+Wz$zmHsVemoSf#E=Xq2BAxSZ)KJ5rDqzIr zH9An=(@GxEUltDitH;YnRB8J!>Vv8V`?vbzyC7m3sSrCK2DmC^)bxq61 zQ&X{wK55UMp}N;1X{Sh83_-*VnAX zF&lR}^`t(R!tg?gWjS9Gp6}DQ_*K7jGp-m;%Y%e&BNm7>95k=+7Rg@qB6Ld9-fziI zG*eY_3>8V^W%GH$Xx7T1QZvy1-Ot6SQ(nxsFP0YrofmQaA~iw1)f2Nn8Jwwa_ma%J z!A<>MO`L<5sP}5(B)LznO}|$d{hEHSFnX1|e?n#htth54rN0t?D4f6AWf%Lbp!Fr` z)Z5Y`tBOtAR(peLowkfvNeh(rw+sE3_G$k(Y~IM8X2CaCLnW|e2$}h%_BCG;fpw=n zo@5`lU@Z2LvPJFVwL{q_ZsfuU_6vupU=ge9sBV z^VjVSt$&Ao302 zp^m1?@|{zfLa}!5l)j?8ux@Oz=aMCP8$Bo4BPr27qTRVml7*F}LZfzqyr&zkE1;X-SxGXTGxC$#HS<^f8F8G`W<)ysQ%N@ zJ+Ne=zoWA^1g+`Ib!R^xj^DCecIYzlvegsMp*(wu(kadW&h&T}=gU==ADFT%6zlL# z87REeZ+S0GOWs3|vdWXg>Ad8cPOjuPRp&VyUIeaC6CIXOsEiZHT`Bh}qj zxF$UX#r&uM@jkPz zVh{8t=2K&`I18JS99*yZag2G@mNJ-EA7|#(TItgS>nW9_#chRS?`Gkc4UP$ZF_{`Q zew|GR8$08dboLtnJFBx`*Lzb|DC}ZBwc37OE9{yoD3eAhc{P|<>Kr0*ajE@=LaEGr zO0L7C{OCB;N2EY^d)1J=DJy$BH5Fxh5ol-+WZ{-C1OhE6;}Tg=qkb0j5=1FK3SpUKUJz$X@rA^MTC*feqJ%LOse} z!P`D)fbiy(t_aBW$`*U->3ZH87bx&!%&Fr@5J6eTk?72vRMy)if{n2i!7QdC)hz}2 z1zCvv^C`Zt1!^G#@3mo!bv8ZiJl=!YHVB(D8Y<&c2ho`p)v35h!makOQ-DZ()-aZW zko9W`k`j!4R58T}o6&lv7RQSwPm_794~KqUaC2@r{=IIp?K3H49Um%x=~?-nZ>juF z9T%?M9x+%^2c*tR;~)Dxvcv?9rOoQ7so?sp7`_H?ZfFU0_pcoi^z2bng7Dq3#sN}2 zwNCkkv>^E#J}Nh5n>m*@eO{P#PZ0!6+;FW^;RmAkUK%gFAr$wQ!a(ofJo zuj^J>`+nq^pk>hBsecRCZjBs=3;%%BIkG;5&27QhDrw&;wU7nb0a*7z_~s8a?zIO`W0;JiXpQz;`+X{3X`U zo$2*+n_55L$yz_zgOl~sKbao63utLL&TZEE2Q2Wnx2J*!;`-?ja}{$1`t~vAH8UEj zdT<9B7lip18IwZgD%ne_y;YI3u{xwcQ=nPK5#uxe1q{(`13cXy@EV#kzN{kWPidud{q3oVGZ=c7gd7hcBA zp}4+%esi1M{Zyry=7Syg@xfVh`Jmsl6Lt^s#3-^*3MjYnRr>M8Z7{yI0<)Cj%k zkY%-GU$hvWDd-8#JS@k@PrY196lq9dFdTW?Y))*YbL)bwn}Q|lg3q{MM(6w@3U2A< zqtEGX9_RC{lq%fr)sCi?R8LvM=q*Hv*l}r%O;-Q97~i!aWnUp)N%1GWl*V4w-0RRI zVGFI}2=EvkA;YFl?Tvm}-7U&IM!A%WmV9OHXAkHmWhddYl;)6 zqs`){%kOdOmvB%~-P;${Q7$hA(?}&6_F!Tcz3sE)SqvAJ^pis_%JllG0JK*DXm6JQ zis>T9d6v!mP`1#GnvIMT#!cfa|9)lX6S6A3q2ZN965PWRiUqvhRR8pPh3E$Z&|?1( zBvA|-+6#ELKn?zlesgak*(2DiX0;r*lR`kQ$P2c%ILReC*++b|OH8WraXib+55?qm z>Nm|6u*6&kl89&jQy&SGgT^CkjQg(_{!N)bCF45-`%mHU}&L_Bs z1kq)jN(mkN;booum|X}srQYRA7oL$Z&+0<8Bs<~e21!%0&L{XA@3b%Z4NkkPp?7yG zUmZ>qx-lD^JWi{p zP+0goX|4S%%031DK9+EcvT%-pvpX#u$qI&7?M6S8>k+^!w>W<(?S0ZFw>kSTGA5472Dap!^ zSD7Bk01m|JNwQR0Gm1ikH;D0SqOE)IY0yai421Kl3y~EYLWz!Uq4F(ZgQ<{lRx@d=bO&%RIfh*$w_!FWFH zLJ4368iH2dI5Yq4@$nnp78fyiW0qU-u(jVFEIq##o!^b<#3UqYpooeiCjyIH3Y8on zpUx#(!cBCwUMYVBIp|pdj2)4)|GKycfRtiNdE-W;PiT5iBlZL>)<#uig&L`?2xuhZ z`S3mCN(Pq@&lU^yQ7C55lVy{PZvB5$Ed+{AdZm6tX7JU4jId zqfz6qSdT7l96hAK{+LdSVE;59=mUa4Mv<{rR8k<=?&_}Nt1Zvv z@2*WQI8Uj_kXHpCgEF$y266P+pghiEb5xqu(+=j{Rf0d$*4SNkx9KM}`n;am!6Epi z^=PaZ!ik*KiEeelAwfAv&BTTWDl36>yBI$bkHTNhe9tTEk-1)Z34RRJ{~`0tB1y1p zbWxbi4(nz%IrfGJC&6+f_Gd;aWEB$G_Hef;$BW{F>eUf8sWP|_qh<~ne6u;nwuT27 zHB>yAJngG|b-W6(1Iil!xwwsC05LhnTg&g@s7|#1yEjnnq779SIQ4s`uA@edqqCHi z-t1mAt0H|n)sXrNwANO2q`Od@j~Bvh8WoU3h)>|DQJt~F#RKYa>Bhy=SleBm2oYPv zRYlY^Maq_Fq9i0FU+{Sf6{}N2mpTWk1pD(Kun~p`cpbZa4t0uzaG-hoDGFDv72+bE0yNWQH_9fo;YWNsS_F{J4Y%F!SUR)pB6V=;#)~OG}sc%A5znZg!+fkTb(O4vRUb$mh z^=l8A{TjJ8a>YwPC5~te4&D%4w>#*;tb7D-zQQ&Qw$85b;*CDQQX}AClDRyQDoWk~YR>h| zj+A;UsvEoc>bNnMEQ_^adfS6nfZFgy>V3C_YvueFamr!cRvLP$t_E8r#s zXk&#Jsx@{iGs_lIq^OO|*G*^3yiGt>ucpmvl;io!8huiDo~BlPF*}BGT!y+2y&oaJ zO`kjhq_M@^7yD*^V@Ta)g!4eXx$c9YdB-5$3U;jR{>UPAE`~C)l+pGQs+FXZNV13UlNn!GkCeZ@no{=CGu_G5=PQp18B^}; zGND*`kDO3-WM@L@8C1~nJD2b=pTx%lKS{Y-Un>d@Zjsg6(<1A*%qgR2^Z=u;Z@}!) zL+X5B)z_F#yO?Oqq0vky$(No^h3V;(H@eG71WQAB%yfJa?SF-QE#8kt&F>fy)-~Cm zu9?k!rFHKh;g5#ZGlx1Px}%Qx$V^sz1FKV(m$ix;7cJE9bj&zDFyMdm0U3+fHy z?1@?7wctqQA~skqhn-#+jz`uN1ug%&B6cIz;h)>qJ^FcRwO9nzV0_MoQ;Y8@4;Ea( z*jKxY$;OQORKVpgzY+Mn}2G<kR*{98@l1LJtPEc0}#jS05x=3 zq=U5nf~|xJ@ry2wv|=g}DuL+i=?c)H!Ibi^^`d6J6L2>dDFr_)UU#GBIKG-X%cVbp z_lRIk4WxF+n9lMtp5i{dztU_^K1>^P`cH$E8iyuFdyxs-)P~d#aaRnN?Unmr?V8Bd zG}`@sxb{$1dkK${-u}=wqUdO1+uKF160Ep1-cQ;KHqg|_=Txhu`po!%t_=dznmgF8 z7N3S}j4U}$#efX*3QvVILTuIAH=ic+P$IQ;>?!pf3Qjyb0)tw=MDcLOhnIe!a=CeQ#g_f}CTKK(5{pT2 zcW=q_gye+i$_AysgTWUTOw*BYNitl1IQkVvEXoY$n=QbDI+@j9f^ezRJt|w^C@926 z`8SJFeRIcKB9dG9;eRx0>Z7R>nZ|VNbZNAt{FJ6Qr%sTbs4cXQ2(2jJ`n1cCR0U^xu>0i)&|L>C_xRRieF0+Umxt&_v?JK*BiroQIt zj~)97y`SV&>o2;;0q%VJZ1xlQ-$Tm8>`mB+X>8ECvE#$$m$+5yR;6`*Df>^%oEzs? zmcNS-$f^cg^z<)`r+->YLEEAQnf7-|4|;Na);tIrE-=x-c3`>Lh&EzS5KA7yI}d= zsi!HOZCQsp{%@{?jFvszI8;p0cM6cP>=g%oKBK7Sb@>E0b6y{yZu-`|s#E&Tc~z&7 zXONp`n43qZkVjHr@4Z1Z;#3p;Q^nsohmpgwD{3(5sbyyP*&nVr)YvmpYPQC9_H5mf zhEK;DnL_3%syt^`b~Hub^TpbFPPvRMet|2o{7}^s0?@ph;k}EAaW_-vE*AK^L5RDG zxdP$4261JTyK4y7A+g3bZifZqp$Zki;``w)@MtUDZd-OjZFc=@acis-S=tSOai$a+A*fk(MQ z{h)w1){Bu-H9TEl1z=;k1e%cqP<&{A5@o_OzU#aY)b43;dH!=TLXAZG^H zv}=f*S=n|JooUE>;*h?Nzjd41Z_Fx3EnC@p?0b#pSiEaQ~2dv=_HR zN+!2dn(xtw==Wc!;2ZW2h4z@siJjy%M=MTU;)6Dg9>Pl)XwfI;HI5!3^hpsVhqVry zHy4G>fu*84wAjjNKpPmrf~7~ux`;$s|M&JrmKy^{v7DjbsM+oGPM&Th7OLi(Nikkg z@mj7;dOg|tJm>s`{dv+vZ58~4qHap~dQ?frt@bZImqXu=)URH)AfsQUpZUH|t6zPl zpHzILe)VIO?&?=xobQrj4$AASR}ER?nIM-I2hqAJ{pv$f_Ot6(hf?fV`qd{-&_bzJ z1B>VGWm1u&Uu^|MB9+`mF45Qg_~uWkU#&n$*ZBEw)34rhp!dI`Uu^~tRmg(_VQc7p zzmgHX7}9`=^H|PWF_=pE<{G%!@;y~22CXj#<+!VpgSh#k3L6B1r9x$=;}(;b5`Ms_B9oCWNZ>=aUkftUn~kZnfre|2(BO5kei5eFIhBy zwpeNpS{Kta&s0usI1~xu$|7x>eL4z@;q3${QO?YDS5cEx#F*`r$nlqV`S~f2rw}1j zQwFb7D&^M{f1tkMM;)lNvI)`}Yb&UQi%(xJ;eu9nv^>$oiCjZi)rnbyIqxc-P{pc5 z6wWMv3Vy!7qnAAd_i@jIF1l-XZ1l48(kTNdoWh;Ag7q}r4it=Ql|FVmUGmRfEUmEO z(j=vp)%caAkCk?bK6VM&gNTudolwf~$UNV9;gR|1WJh|9*e`g8PIe@=u2+oA&*)_3 ztHu%{PPGIRTk|WeYeq6K*NlW3UQ^($R7o!pDv#d_S#o>Nq1j+lfQ1MK$7k?p^)|0eQ4Y-Cp~k63{dJQWKY7MQc4BP zR$pRwk-u(F^1Ccta~=514tmx0Ls}!7&r81emVfqhr%9zseIz(`Pm!xZ(zmA`C(5=YmsVYY=S0I2O14gx?jeMEsCUhf`UN{b;IS7T1@Hy-|Q<&wK_J! zfuF8E77Ki(kd>>Wk?osSA^Gw+@>8i~KRl{R)<0WAtbMHmI|mzl?Tx+FYy7hr6Y2OL`iA3^9`*e9VVhK$={l-CJ=V(Y>FIef=yzKwURW$ zpS7=b6r=~U6{P9+q965DH?>6%HP#B&pdeMqWEK4F=S&~gP^IM*dp5M3LhZ6dr&u}4!>q5X(l$$8$wyxDR2IQuu>m_1LvUs1OP;&mv|GjdAZ>gu<99@^~iKkcik zm-VC%?aQMF&~G0S-Y#1|UA=4=Z>tk+(SvS-)dywy7*vS5)5lL1f7(gk`Enl@Tlg2;~xt>ewgN3AjW?B~^-qlj;x6^a;2Z!70#mq)T(7`Dp72pE`tCE=6i ztWm-b-qOY+iK_3sFrWr@Z1eH5j<1|xMRqVX1M|hFCKR86lvd5##q3Rn;`7AnMud#} zEH$~rweCDR;8&Zb3DRQIbs}C!pf5{TgqSMoY*{P0T_8*qVfNfeHquT&6 zPbICJdg3z`dBEXNQq+POep|FY1zz$9pty7`kMSuDFLfjEa#Qq;z{^e1Hv%s=MQ*a- zIl@@6BaA8e^gk?*(PN)kTO|EQ9Fq${O}J=jY_gKZ*eg7iH1@Yt@4N}-Ejzuw zjurmlH$Lh&w;HtvMTW|ipV&(g(~#HLr4}A_b}Qt;&ZgM0 z;yyt>+g_D2>N~B`IPd&5D3t}SsvKG^Iwt&Y-X#nx7Y(udfQI{ zIZLQEw($C`-qxl0#2y{|Vz9iW3O(gXLC!v((k~pp4aTlLRnAW(rz44@KUWNQ4RtR1 zY$5`g?KM0-C~pv$+h6q6eH}9k&bX4SZSa7?(PJ%4IeV22CbBqns_M2wfDfi93GOy;9NLkM3(U6Oic%;jZPI+D|;@bUYT*L^B^@_Q;U6ehQm>l`! zpFLKy{WtP(EVe%wcY*`C78qCYIfw19H1Vu`On9bHJS+l$vUG6lGWy(8C+IB;T-+;V ztUi6sbcz_Eq>fkozu8Qyq&==R#)m)ih=cz(C~2e8Xqvk5X4_x{*J)4JMc%L;4B@&Q!;DSF{iArQ+XUk&olW%;c+_nYrEEqnIKxu>pYQ@jW_%#|l2c&)nOsm= z{s3Hlw>p6yyX{zN$cd~D^ni;p*vJtNuE}>9E}7l%c_cHu+@;B z=QqF+MzN}pT|s4?@gmTz+i2Db%!6#>vCMHN_8LfxKC7^&o?`3=^n|gC+@|SWrFBc` z0Csyyi(U5ZWyK^xJ%oK5^1^j|)*e_OWOv1-R^$_OZ9DA)NbVEN8`{MjLnc(}1b2Ml z$378$texet$zQI-ctY|<|Jzc?(7vipaq^gVNbAz(9ThxCFX~VhDC`m=j<=~A+MtD( zvE78uEBRdvJt8c)l1G#sT8wcGJx~K*wASaGI;Wq=D1XT>%Hj>Ngc|HlPWrG;i1f`BX#=>-Eh12mEh(eWrhl zw_~}ld=p_=ABNCBAZx2DEB%o!AqB}wkR>ZI%~AvT5%ABXM(bf2XfY0Hnyv5(UCYh6 zkT}hSL{|O(8a!l^^Emdoo%%i0FG;QKz4PoM(lh<*BriRTj#C?3^*JTxl2c`Hh34=` z0skhY5_RoE=Bg56)BArK9+s zpoOKFv#DSr-iek1!!ZlPF%w@=7Eg|3y#(1P{i2u`#XJwl_;*i-h*&CYi zLY|jO?vzSQl(feO<3ITrZ65pqK`Y~P_5Hf3g?e)a!qAwyGGsP~W;RGc9)mL*<*w|N zGDK+f3q|=qH;TePFEaY(&o}yp=VN)}|9P<_4Ke!q=NC)b5DCSK92A`Sk=n3&F>EH- ztBTI>Gr48If!<{j>aa zjG9-3D&VW59Bdq)9Neomh$l|iI{SRs7Z%Wg{XEN4=VZH zM5db?Sp*t6FZcaOkw`3k_lQ9YUI|R>4st0MnXj80#YJY5xX57n`QRa;gHXX2r!)kc znxI6uQ0)wsd>H*vr~byrIQ}tGNc+dU0@~pp^Ia(#_70P(78_(e+ zLLNVHGGT-gFAe zwqn1>wk|F8dy<$B`aQdRuswz4jYef73vy{8OLJ)v`76~IisZ3hd5giRf94gX*xF%; z$VO|Yc+#KIKEkLy^+>D-#nbxK&ic4v=9Q0XsQIj)$-c2~c)!x=V|><+3e^K$oj%On zX&&w`^-e!`<}v}Nul$PA!s(~YOz4MjY0>ne0)0=vjQAS#dX@19WTRbqiMspWGCU8i zk!04KDE2?ksfT~jKXlZw{X=s+1PN}TJVn3DvhR9|qL(?+Q}kxmJ;Hx9qb31{xuN+12^uGsc765D(Ft!jav->MVHT@oMR@x=%J)o;y} z&G|rl;0Z9+rR7b=_$Hs34?6ilr{%`@<$@dV&w$ofzTD>|crS_jJ@@(I;lJ{8#R>Ph zs1)AgGeIiJE~pevNa-{xg_p@=Kf))bom9n^FWX7gA+7>`h^ukG;^uP*>|mjYX?)3= z?-Lb;)dMFK_ML3nlLKkjS+$iD;{?)2III3qfqK`~wq`#t#eia=9cQoJ^%J%NkJ8mPhphbg{oWGAGCd4(z>6HG z2R>yShUsXdOK_`jBA&d6oroY73$)zXA`B|u9 zl)riGaK)5oaW)rDIGO6>A70PAoc zp9V#V?Jyu8qA!M+D0syOuJgr5AnDB&fI&s}DJr7rhD${>P3ek?_~QO`J)t6Aey-Su z@r+)OJ_{9r)F0{U?obf{x}qZI$fA=&MM_+H@A7Awdgx(N{MkRH9#7B6L0#qQE+DOa zE=@gn6zTzTI%^CPho&EbuW5YX;Xj2B`A(y|A|47K(!}E}W|}+sa*40N_!PkgzC1l=1QhY06PkFOp4EPb zcvNMmo;3UciM}VqgEB`F5Ad14Tz=$#>Yu*mc9`{}`ltJ41EeW1SL$sF%%^cE=uSJ6 zBm9o;pZ*3UK5Pv=c=!LFfBNLrG)w%`UxFOteLSbt;Iec+Sg!rkFIO4T{^>n+{srjN ztI1>kQWeua)TWZHj^UqvlghB$_R=pLncuuaq?<-eu~mKcBl=sSp|&q2%hCMPAL~@^ z=B#p`lX9+q`ZZ%R{^{@S?w#wOzL^A(1n;4=_D{b+XFsNY`eRP1fYfOXJ*87qw(^?p zs4mozCEGv!1gS{*oU91AZsTtV&SZgLfwb}r+1^>;twxj@$N z-!}>r3i6}U3(;j^rZV)LTY0kjfBt4J}32CnV~h&cM2=)MvvWHR>yeDxIqI3B;vW3jKk+IgJ7 z;~-ysgY#Iy;}DL_VTpbe4 zaJBp+UgGclto-<%lizIq*>A4$oBI;GAkTXecK6uUo&j^O57kxq8^-uI{N-Ey#`vv) z@?^jmpY)Y)@EMgGIB<&d8-3+XJfPxUn{cl-u!dfz{N@*|@%g^;myms4GRNmz;}L{j&sy7k4c=pDg*IPq!95>gaTy|*U|c0ip-_BKic{d=v1zER=FpooV&h!JSwxk%t=CY z+3h77%2IK?w(lW9X&Ncv#6Qv5kJ*KQQ|eu=bm18p^Qq`@v)9Xu-+j!!iq$~R9tL{S9w^KzM_?~Xe1}Bdj z|AfM_zWj}={WL#L{1fi<`m$+PW_=mkAW$fHR92Mq`qKD)o``>P0~gkkS#C**e{ZJ?W@9O)pbGh>YGTrUDASP)wCdV1!V?K8N|vh-y8V~np*57 z)p@{C&MU%s1qCVVCs6^)x#he=eO?iI+w#6-jvk@P5n$aTr|{b?>VH;-!#DFjg(s04 zC14j@Wn+T?n(zZ-CmJ=vH82zWn2$_uky_tYn_gM#2S?6DPcPfdSJc2n&Tz7SmF#7k zlOoMgDx)tWxJzTKz5AUf@ni0f?oknBv%VL)K7UErv!`Qg);CwnWPE?Ed@x{zM$O!9e6a^jy^#O!@Sm;!Py5qn7(1YC)ILtD!`7uZ z(-Zlnkq3|lD_8t-aKsWz{~E=qTfrglkw(pzW$@!OmF^KoUwxji1nIG!+pE-a67c#Q zG6mxgsRU7FSoa8>FlxJzE_x*uEz_uNRkhv2z8dd7kcU_!`laQ?nxUMsuwH1DQPZaK z-Cdkq!wtR`(Nn>G91aIVBD_K($Q3=StRZ;<-{&G*uw2Btoy#W_;&Mnn0*M{>W)>B> zot_T~$E%exM7rb7SLx3lwVNhwnU!pg1BmIb8=k_7iUNPnG zPb+M@143Dx8i%)Xg#X*w?e+36aTyyEJBWtVrs6WT!Cp5y+V=gd>rK~> z#^2r8soZ&4<<@hb=I;hxoZ;^t-vUFE&ENf!1mW+7Q$q81?+;bkkJ*JAol*r-r#18q z8S|_zEa0`v-vy;9S^V8A(gG#c*XW8U|BFuLuai8Azq`)KBh8P&z1y*bU_O{03H|KKX~F;wqvj!P)zQ_vX{ z`{GkP$gglAcMf?z4?ZYa0!fn-IwVRIIS7}ja*%2~@Ke&1mvr)Yp2KJZln8B#h-UES9lBE1U)#4WuSfED1qK6ggmCX6} zEkPnjp$d2TS)4!0z4RP@7ULC);H#{b$H;J8!u9!`=vb!k6(UE8rJu!mc_>}}63Uaj z%qh>Y0NgjTye1_On5=1x_yorDG6{c*mBF2TpOIxPDS8W5uen^p1k}rliHgC;Ma1%W zWX+TxW^@+BLX8ggbtHV^_0f@V=zZ91cFYe5|8-GASi-)wzL^PyE*R@&hFw`_B@` z_i4fxL?Sc@c4{>NM&QNaH~r-|7f&y`Z;-$I`qJs%`-=VL_Y9hGO3=ER?3<(8QYUxP zSLl#C(HBxgmslMxUmZEqKb$WNG=6*?wfl$jl_KNEa^jYM_&qo@%b~)4P6`C>#;k*Y z){H(BG_PDB8<#480W;oSs^%&oRtgq>Pfv4YN^~#oF~tNi_}KNS4LFvcbFt`vw0RtQP!hHp+zC;W|yu@4dH_PS82Ax#xnh+%kLfg@IBQ(e%QWL2)5WCr_Kfv4D9Yrs~= zqFdIL3&b4=Rx!(uJlO+v1^Xzv2F{Ybv7Q3E(fYUpK7hj`8cqh4zPkVo5aB4`3_Q_5%OVQ4WMIH5Zbs)r%q5$6?e~AB8;}b zuB~(w1A~flSyf5EY#(P$C)jfA6OS=VCW8zbTd84lRdNuI<_wPay>^jbV!76n|d4$eaNJMD{c4 zGvF;Jbz1&6ylUQA(dT4FeTMnQSfih?0tb0yduFzqne-GPyyu&phjl4avfJyPSW zId3a)l{ANY9mbV9X!x7QnL5#Dc~ofZRJj#+j#qq$DAe?r7B`IRMRRFj3Kf2!fv?Jo zaV0ttjfK_A3=_j7r=5EJ%eFtj5_46^d=31A_b_IC4&(|DI5%L;z_Fzzwsl%!YeB&L z*cW?enlJH=2gC~yb~j+Yg)eJ&9~Z+qI0WYvJYCZ;uf#2JLRLKuz7SN&C_MX1PPC7LM zYvJQ*PuN^nYVPZApASFQBQF z65lb<5GF`2k_BM8O)k&bELx z8AA;$z1sZdn%E9DH?|cA0hKvnqWO)hWd4VHB@~)hg95EBmClS{JjR*RKN6N8_BO+@ zqk9ZRQsj6nyX{NY2q@S;>6v(&!PvoIysHDV`{ISC`+E_%5?bI6#0SnmNHlsi5RnTL zsUE>z{w+nRAkPoJO5P z|Cgi15ac8}yIs5dAb?hp+Ho@TlEfP8y&8J1rTsQs*12Wm zZAy-r^y>WlcYFt=cF z7R&9^eW^QW?%W@I%xRlF59OrW&iG>a{xcP1vZs+ed)QjKkgEPi@h$&vMA$qJxG+yX0iLWsruBbCwqWy5j z5lqgSkf{solV1`nZc5$9j|vqk(nsnJp2S1v2_Pe3ikj??N?qAsq3GWrDoRI!=ah!q zJCPCf44DcnioG2X2czd9jFw7#PCn(v=OpYXHp=q0kMou#jJK86BowOu^N+CAOcX>> zn|5lAQ7d8mj@2Tgc6BC)ZdFp*M48J|6NF}gT?ft*9mMi~ODNR)fuVeJ0pAqpTa%>m zkx-|GNAd7D1{hDt0i#ybjMrZj--FYrMB@NU!SN^@b%%1~%YJCT-KH-zRnCTflHiU*w_sj+#^DYt@QMNyFA);zXI$0IB#J4HU|ujAd~6 ze-_$UcF5dhK0J-P*tTwF{1$E^-+{)&+Im!)pI8qIkRsp6?&3ASp^JwUFw!}u43z=- z)nlwNhjf|pjl7JUZZ?5jiJjf-JLv;Pi?~9jdZml$QG#AUG z`35moy6HRw82kf6KFyGCp#d)(;-pKrr zik45~p||s}gooHt0Z(4!uHhOiclfP9OW7*FRrylcp|n25isJ%K{gijnNHD1 zgPFsq3+$5^V%U&rSyWG^a!j39ofjcYu)4E%tEsWYPGRSiVi449e~X8%?T>E~{|{|Y z9(vAILxR)zNjv+ON3k#eCVL_7`BZR0+FtlP*IxLaolmui9gR)wXe>83sq?8`54OIA z0t>z3fneeT^ospcm#ZDn^S`LA%E92tXwDb`_WYa^s(ub}EZeL+4+>rW*R>Zub9rVv zZIZEjRoN=eHa~j}jM^l>aoY+WE?6fI#%)74>FdaC`g(R!T^)X!42k{5Z4Wk!aKpok zlZ>i$o=HAqbMqwQqjfrE^UI{Z(ruEl`L#*Lt4?Cox_lnUzj+P0NIbBCE61uI;KcN* z{l&cGT=lAT+#FcT&0a}aEh)peX+|T|jVQSlMy(v8JIS~`F`neaB;(!&&+=?j`8m*+ zY%5pC))*}Sn1{#0E@4A*Y>hvkrJrJp-}O%#qx==5T&?m5%kfuWWM0x5-~N|frbp%o zFNGRvN_#1&P<%IKVQbg;hiBmbP5irgV%PZRY%x11_%gs(dr5z5V0r3T>1-Z5C!Q_j ztx6Fb)&0*NsU!Runp5A&R*iQ)|LmG$`!i$>(O)KXIYf~X+2-g;Q*n0Acw0Om=QR@O zG7l72!VDoKT|uxxgM4o`o`&OxZ0vmuaNCurE8B4RkupZey+L|hui2G z+{P~}cKrJ|okQIjHERXN)@9n$(mCsmzAD#AJS~@v*M5F@P*MgbN_t_`NZ>Z*$k*t# zi`ma3*pWEJr_%~0&9kz@_3WFR@$AbU7S0FA+ArA`Jp-X9eI8x;KOK)w?w!)I+PeX^ ziuIi1Y3b6h9R9_HFXcLUuyz4ob>5tYZ&?Pu=Vah}s)nyRZw^RtHEOm3renZ%Qe`LD zqA$bPlR^$`+3qQ_AAA_x2fhR;&Pkt6y4m1DnZ2y3kxo#iV?v}j|1Jm0=Va_d6uiYA zUjthT2z5Hb9^XZX9A8?ac5;`%&Z2)^!W$a+&*A;Qh0psg?F4iBoQuz2uRju>FOjtW zAD{Ptn#aKBpN;DTTZPYZ#8);xulNP{yi^2j-{6%gR?94W$`JR?+gA*ao%Zii; zlHYa|Bwvw>5JOjfBW721tTuJD&N++DQ z&PnLMQ6=nd5d9N5ZT?%~iEdcFhu?k}&ImtM)#BmiLvA)G_NYO4q&9e@t^5n;L!QmB zFOp^RVY0mnzoeO_ZKS{-y-LnkCFjTT5tygju%WAJ5iW|9SC`2L56h2yg?tCrbKOb8 zf$iiwP)r`V*}%;K`dHN}DU`ge#mT$%h`cS5mkw0zk-XB24KQC-Ey97d^S@H>?{r7E zYN)fONP{uHy;*p%W zGnm%DJFGJ2yV~2I6xz~M>1oYk5N7?Eyy1AXLHv6&{+KxjxQaj3KjB|~cp5IrLxV2g+II$NTj@NQ0Um zzhF}WBJ}fDllY(bp zzf6Nyoy4lw1kdH|E^?75D16&Wu7Z^cJwd#x*92ix+#D2y<)c-v39fzso^}h4oGIkY zl^0eDj;<6OZA6dWOCmT{qc=*&!B9fpTVPHtd(Q3N|3{=%r)>Eh-4Pr*JA z-3qi+Y{muigw0q{={ew%T4UBC7cpJ_DTnr|;6TD)RC%c+bv67+E)CA|_xl0N+{$!3 zjHB`wNT!$hRqK!QEa#(6(2g$(Vvgw4;OP%){%1wHy$@XL;3n{@G+n~$`c5vNA!T}0u1b&a3R|KVB=VqUvG$n82n<{VWh`j41?_1kpz(rv|LOC%U3&cy1jluZX2)IW zJ95tB{w3NDrRyll=0{w8#~1(BmA>PxWg17IA-7$yO&-v9B=vRVUVT0Lu(}?h?-2gr z|AxLJgriwUCN_ZBMP)BTn3Gr!?pHhaO5u?_LgA4kPh|8RQY;-Fpi_K?ti|ZN6Ct#PEA`t*<7F+9Rw-loOoJlV@7iVtSb!_=MZ$NzLhdOw~+ zhKQ38^`It_g=Tcc*<$Qz z1{M-gJkeG-c#oM_m+Y00SdHC!HA_IteEsCrPer{FKUnqV<12b5lBZ%{kT+araX&9c zno7(10xMqL`kkKE#LmKwwdRcZR^{X7+C)d8xpV*>;UKe_+f;>>k6V=wB@P!3K5VX8 z_d%~U=H5iITd%!%OMH;8pS=31sMnewtZ^z7qh)r5D)d*6;ANCw3He ztigT}GZnreJ$uQmX%Q!PNlkmrwd;2GT5BGbnhw*d-TC^-tDlN`t^L7Tr>08tT~>DD z6%ppvW5)YYBY{TxdDOn*`t9aC2`c1y-Klj!U1I&)jFngz#INp1qze4;Di302H{me{ z0`PEr#KVlbv$-S20Qt;bdE357_TAX==De{c`-8_Nb+;aRL3ufC{1GUkvTZ^#+Go#G zPfJx$RvjspM&`3MSVVR!u)?e@U}qF7cZ^x<%xmXBCgvJ3@fy;{ zlI^0S=zu9MZ4`4CflRE)G3i~-+7K|8ib=1}d{~?GJ|-r;6p%?{Yh78LYx|2|e#bKE5yUKCX53#Q ze3qqf$G!_{#=Q)UJ(>GO9w=%Bh=Jus@^7ffa!HXruMQeiE8ESC5C_o9mbBbqC?kn! zDs~VQCQV+a)3%Xj#U-1iUYPrOy=c^2N+k{*OuMsbf>(yi{tnKcsiH(UzVte|SdR(8 z6nJXV#v!87dV3aMcB&y^u^1WKRgxxzi1{V^#MevhfqGLJ8DW-b*4{uiZ1c?8o8-%~ zo7`@HbD?bS$E8X!eY57tw{pSRv0QH~8~01^bXNdc?~4`CD$N5yOVR5^9x-3Ml_od@ zylgX*Zs0}fNmn7o)vRDA-&8l5`R7t<)xY0^<%ATduNSK6D_QBcW<2LBY4Te)Huy?b z=!ue`e`2AnsX^Lwm#JX?icY1?sh`)ATbqDKXz|+P0sdA4IOx5qDYBeiTui7!57&e&~luNyG zgnWOMOh=Ac<@`i33p6J$PzUEs3R9uV4a!; z?HZZPq4;8DP|SID%9dRk^%phCyNi8`e0SD;*tfg}P8+kn1zrWpfM3CsG8dx)^E3D( z=C{P*2%gKaCZQs=1$Z11M)xo-S60C@cT|%ZmU?{r{H0`qS$Ve_ z&3M?Hv4|)fJ$*BG%A>#gBB8hb?h6H#{N3v{Dq+q~OqTdPQ05wJ@CGxK7<*P@?8*Pm zYQOoU3ixIINjO};`K(xo*9+^U!*bSu8^n=m*+FWcX&dFKNH2oz@Jo>8r4JL#Yc$M(l zpLjzZO^!dRQQHJMC9fpxFJ|=f=`~zr8uq>K#o{P12fvu=yu6WO;BUMI*uJ3 ze1{+5g>1i$Lw_mH3*2YKDtUg`ea53qo@LKk_GrgBd*Dxo%uPnk_vjs=n&aJXeudZ6k~*8@Fpq0yxfdgCh0!K zT4wW$MA`4592X%rZ#qhy=Biu8zaeXnxG{RZwxU&ayHqJ|V~q~xKmD|zPGqFueX7uG!&_`>N$t^N_Us0HH~ z_!nWxkh{8#IdW+eKP&{6o=02P*lWQ_k;o(>bkwl4jsNwhUD~qS_b;q(GZx_w#M#;l zIG&K$b+W)k7vjidPcdmhN0S6RQBzVVP)@KD9a@~VMB9?RB`klAE!lapI%dt^EPFB? z!BWb0VNcd2?Pz6CAhsj0EQ4&%XppEUjGIyWL+TYhPlG&S1$kALT^W)tJ+eRfjqqI^ z_I`#+OoI!4E6=U=7Sin^9+HJ0is08|w~~??EqQhy+9N6TF4~V#*4_lBN82OD-mO{2 z-eZ)lccFfV(J1evx*DgtNBNZK(>m*Whned`vW|SL3W|Agt36;L&J`F9a}*{O>9l;( zm@B2SM~CAbtecW&TJ8NHNoD-GQgKys6NS~=OnO%Nr=n?=?i4T_`yNz%uOcd7(3Q=< z<*N#eM>ZO<1cR-;k6vDYLidftu7a7{Ve@*Ln=%1br}?^bS~uA0>prJc=@h#^RVpl( z;}n`Dw&LuA=TF+%XC22y2C0Ia6&22Pm~$QCTn9PVV&^)*xhky~$0|%TZ@(&J`u{;B zqT7p}so<>)>DFH{fTwHv?}}>zl4jCj#}GLvfQ4IZB_g;As&CXD5SDCi4@mp9k7t2x zMnt%=g=z!SVa8sk7TE!In6Y2NItw%Qh`#p!lfItzq`sd0tXwnmldNk0_qi1UXrBJF zTfkz}rG#lVy_^kT3`SM=1}|sc3nR1%Frf<3jL<7Q9Ki@ZaaI;1bQ2Td15gs;vC;}W zF3b>vV7>vnV}EO==7APEJkTQHfo{p62U+~kZ-H!vXEADj<32n5&;s|_;az^MpW%nZ zW^FNklnacS>zEgc9g=g{CqYwPek+F`nvlg0U7pPki6^+`hvb_Hoa*>yq5Dl?hoqX+ zdGaE8p8GgZJq~mq`>4l0&ZD~|9g8bE?Hrd2fXx8AjhgwCcDbSznkzykGGju3x$2f9_@Q&N_#pyL8M7o@4-5W3)SkA(4Q=}@+>mOf2zi~jp{&-b z{p_R|!3sn^4IuiR?E>OcvqQ8aGC9o-Won;#RSr9(Cfw23A%FwT;xBD)CCt!@3~ZVs z?`md9Y+-Ymp~?(1R3^Tk=<*zH=-Lc7bW-|_%MLm9DSk+|4Tgt!+fS*7EPm+13_ryF zkmiSox6MX2{E%R2R=n-c%nm)uRA$PaPav_C+Sh+F(|1NXICRZ)!D4tEIX=uUtN-K% zy(rXU-3A9Oi`=jOOE!mgCw3Ri+$jqL;RDnPaY?+N_#)-SwomI`FN;L?I$0vRtKi#N z!RpA;I9kpEdy6g=#dfN7V!PuMdKlk+XPp>bAz$O!Sts=A@H{)~#OPw@y|Yf}n0FFd z&ivX{Yzf2Otnj9niKkJtI{U_IW*xb2%ofxEXG&ox?_uO~nVW&o7k9r(xS0cK`w-4c zQP}R7BX`No3_eqX-m#O1n^6(SjM|6N+zhAj|QExgoA=DZc^<`7mYSvrnclV#s(rMOjPrS>JYvsab$ zweNpZWkx~r;jeO?s{FHOY~XZcqh=<9uUxCJ@#Q4!f~muwzKA3_7Of0*jP|XP4>tS^ zbTyXD*x-wG6h!;;PJ36$yGP|6U&LjynWn3#G4@Kq@4B{poU3q!>Nb_nEta=w{n@Qf zsnkh0SmhL*O;Pk`#AGv*W^-(7H?#IBN^&|4^GY`q*Q$elYpFoWs98+18DGSGR+n3E z&T_JH^I9sGaes^wS<0c_aIeCTd`_;S96CiOoOY&@(0{N>5OYoDGg;I6q0$wi8@gge z5sCkwPz-f2(K;)JUSz2BVED^FWMm@em>bxhQk>BXYPN~nng)LAxXjSW;O1NN1o1gh zvlNdsKf8zWC<-B5*A68-(eHy|vPtk&W>IPic?c2IInP8V&nc2;eC~Va)!57NMf*~h zug;fO81brVj7!94&3Wj?Lu{!aUtVOIf4IW8$Zgq9Ya*W<;ji}bKl4``m@D7aKJ zRQ7})_vV1eG|{6uA+N(e{1a6sb)rLa(gjbXAbxM=(t460J{i&@9^yug=)u$> zp+b4A#Vb@|xey*sbBs%&poKO@_5#-0_k3UW7&Kuv?iY=y2tP^>z2G5DY8L8i|3&(G zTD`uW-N4n{BOZ86Q;xnr!zhE7c|4efqJ2p&tCipO!_E!qp^G>Gz zXu;qcvT}T0a`>}ylt_+CvJ$Qu&onN}N*F5%#aRjGe3b+vE1^FLv86KHd69dDKP-KW zROQZf7%~3-E%TkPA35Kd)zRNbm;sdK9riRj#ax%^nLF1dM}Nujb;-dBeEFQLDIVY< z2Y+O~cRtUI^Uo-&J%0SJbNPiz5-vq9a9Un*AushY;y<>;cp7SFiAi_uq z`5|n~4tp^ahy|WF%p9xMM&u2KLbjF?5v)k^x{yQBW|6|>D~4?3QEc_7p(R$CuPJlloXN6K1WjI z*e_9?rR}dc1y`9CFclA^&X_beY;MizPuRMyC{&(bN_up^G)4K}1+7z4cc}L0NMt3! zcvYq~&w-Q@%nSDIb(zu+)XK9yZth5UvK!o;CK2yX!*gR!{Xy$0;{T!d-6-SrQS`@p za~ZBb+fXet{+;oW^PwKs`@{93L=+97XjHQ|cl*%XN_jyrD3@bp@5(-C5y>(SJ)*sn z91uFK@!6^#R!Zy;0WDW&|Er+Buhxl+bxfr>qX?gA^eY5<%-a9Wcei8&qw}T|aI)G< zULx4Jj_cJ>h1FV3)ci~yPeB6YpqwLy5h{X30duYW@@)iJ7L{u!Nrmm@T;ZCHhUjtW z!~vP;#T<%ZMHlJubL4BKX4d-+E2N28^7W_LZ%CWVjyuMF12)0xa7nfbEBlEWV_;to z^6`zzT$auSwHE`OHfpv2ouC-Y{=6n_EK8&7^ywg<(Oz^q zFL^s80y2Rf`sHYVg%AMWeCqfEfrb$_hVjm;_yIoj5#CUjr$Qt+$rNR5I+R$teB0>1BV9Cbl zMOi^&v*8uSe1;8WnmN@+s}6+YU&o)ats}BS!gooC?M%p8LRzZBcJsZSJtKuS5x4Tg z?&dn5xy+bz7IjINV;>TbRc@@xX4W!)`QgY}{_?iS>7*5;u8)1_iJnD>IZ67u-_sWT z3hAEIr94MZ^m`6Rhw$7zHJlGmznI$|sUdtKdY+k(mnX|hlhU9;>LebzrTX%lpX%)- z%0tjxDrl9BZ1!OAop3Rt_Aq9N-2mZb+{iLUOyFqM3XV(6Fj_~nD_y)nEvPV#3Q^x? zA#=VGy_(J)CEIgkOAXT>XwLb-Z?isd3K_Cr%1U`Z?`H>^P`>QptuNNz!>IWtXn-}D zOgeNzCm(8?tI96OYuKU0trAI^|4VIw0yGsM~CWr z_?QNXc`NB`BR51(ll!=@ST+u+c1psziCjdEVm4=U@L{#!1L$9Uyz&a}=k6;m8t8-882#5wVF}dB+6HyNkvxhhOS2>2gA-?(2g+8d{Rue_V&-=6 zi})}jUR74B1m>(;UHvxkVK#&CTdWavRsH?U4RgfL2;obN7*}8IFK>$W4)%KLHK|+R zL4e=#m!k{AjuI)pSYGI5-x=jkpyA0)fDbl@D$|E6|32HHbMbs(* zte$aDBO(vA%>TRgKJ&=Ks@2zuzjji-|I z(>*+8HcwTI7&Zx|C3+dE@^^&KW-}#gSGUcSM#ck3wPa}1Td%*-JjlDV|4tU&!&t3z z>0|fxP{kX~@{?$s|ET2OX@#>CvkKI%A^k2_-%OftlmA8X>bz>q7XAgP-)p1k=S6l58`d&M=fb zX@(MqwJ?-7E~&8`=hka_GL)|0wsP4TV3F&pYo_6^O|z74YB!b=`law!>{sHdRp@It z5Br#U$l)iyn$_2QC9SXFJZz4>CUYM4f=|ayri!!NBuA#i-R_N<^RR9J%x346+ca+$ zlJl@_S{*LHa7xnrq*8ON0Qem7`5k2jW7jnIoX?rC@i2?Jn;HJ{66t@Ak=3qn&iUBu zC4WZ$(bxIdbS(90gYt5Z#-1$)M}~e}+b5;DOKJP}uV9i3cPYc%T`wZc-Wl{$m{jSn zb1wf9QJ1@{;2}9q@*Ml;?%d@+;K5HO!9#b*oQgYtdj6C6OO_d*6}z8?Wcf?gs-;TM zCVG+nUn}a1zx?K8#b1({RsZAoOWr!CGO-ggdhwSVqMwhyJV)`7n!h|#-E02xRCTZU z%agc=zm%xIPXU*Igt`0@R3MkRlnJew%PXN1!d&LktB>I?0mEnGFF~hSbNb{hFY3ix zo|NS+ksoX}>kgdPgPBZx5;IAsa+%4q!D9yXUxb+ic$%5hI~V6KAonLSlYI2?%%ouD zzh_JNIA&6^eV1&#nMrvwC8vOH%p@;`nH{2w|eQcOHUvC&Vhs ze$q%c_`3$vQ)_DrQpN zXBkF$ml~dCCVRizA@3A3DLJQ{m}VyBX&g@(cJOT~V9jaG@4-y6Q1xIY`83g!nOq`Y zvj9FAVvT)@e?9nzp8O>8@)t00RPypV5xnJkk;Dnk|22OKzk`Vo!E_-otS;!a|iwqVdf5b5S`C&B*~TEHb<^uE-ddC@G#rX}L}by7|T#sC?r*ru47f z_^a;c-OW$!A7?#hFQs$6TrkSfri=^59zy0m^&O*>b?V=0w09U46~5bgjM@Z4Dc3*D z9J++r-3jlG)X9|_=%W!ufz^yXy$CV9`8Eq0PO$*2+) zQQXJK<{~0L6Oo57m~tx;$hq<(@!7cldAVhDiD<_`c*nXnV;ZffT8wXcnMS|w?p~h0 zWqxSD@i*Od*BY8=^193F^sNkXUxDJg&(C=~7YnhDO=HU# zu%4F)6^bGKPl`?pJYy?OdhXmeQ^IT<$)XxmPew zzjKd_O6{Z*}tkoUx0wDI) z9Adqayz%Ju(sKUVqBqE0wlt&h$0QSyLm!jxHeW?!d5i?%Jo||}T8S`TSgGGbz><@N z{>ZDU+*d!ASDuW#DfiWq6If)9R|`Of9_GGp;(e;sZI`FDwvNtA)14Vt3oiAq{!n8X z@(d%oupgkBw^r~mXBo1Vp2Y{&y`$uRY&8EF!fGxGNH?QSJwTxgNpKFjjqOgIx@U2} zg5}I#b!vstIGHQu3_|QK?`RwfWnkrp$6A-(&Sm823Lg|Tor1hBPQ7~L2lE^7nYAZ zu~I+lW&uu2PVKs=qh?|eX^SLtbX};RD}|iXzE}E9)TPp(eLebX6R+xTh0ggLiW7~g zK^royCOL|}qHch0phD+1tkKBOq#DyKjj6`|{yx<>J0@8IR!w=0eGRc={GZ>~Jx6_| zs?Y5+Ur(ZsP%eT(vneqijqqQFsZF`+`FJW2yNHisE9E2S*H^;r-YsLRq2)$%8={N9 zPZi&merFB0qcX9fEX`@kB4lN>6xfX}#G@g%F*(!UxK;1qi|jm0y*Fd`N;Wue!5c9o zD55zW1lO4HvQfYILlAWZxZ`S1jg~QD=4nJ1@Iv%Y)>e^r`HYsUONi>ri(7MElqIg0 zC(NtdnYwfav;RuxFW(lZNva}VzmU`mNZ`1<)LdhKjgShVQ5n?xj0a_eq5K9_5PVqI zq9E;&TJG0rL3^o2f<2)h^{C}8bzGy*<2Xk2DgdG1>mxad3uRg%xEb8qxBaSc0y^H2 zEg`kDXd~$Nl^_1ahsXfJ> zFxcnWR(qm-q~~7wu=XgQJ+{E-QCgrFz>**=zT1@UGg&qkq`zUe9_wZ5Y7~dpCMIjG7K>^RPB6+>lx`4E654 z*Q;;d^%*tqdiQSjS;|R;&%=4u_l%k?sn%hwbvq8k#vj;_AT-ZZGRFeAoDp9M`2=jP zX7m)I`#jCYg2VJmCq1>|b#D}+c%03B(0)jIt>qlk*C#rp!n)2vU-33yai>5Dkc(-!Ib!@8fhUFR*D_w?@1hC_YfProgi)e7`%K zc1@(F_+6jpMYuRoLqVMdOh)}mX{3h(;hc=EY2~1;Z`g~zVO#dJ^b4J-(sz-*DYeEo z?4`Qx{r0{j@b_6S(hPu*ZfBtQU2m#2wI=-6x_$j_xzU??rW-%!jg8OOA1s&Fq74rg zx05KHp_DWn&EhpA_UoS0EGA}OD-pTeDl;7p6u0<2AJ!5dopJPgp7O^01dV>mXgowM zK2gDls;H)e`H)6=HX2)y7Z75rX~Z5eJpfB8Ne4i$TeZ)Uw@l{BewXn0ZV683fwz9JzZ!k#N-@(6*BT`W;tDbbk!2YQ_wwAHjj z?>C`AT|e`O95u=f+`7w9(%2#3BE_!dcgw_T;BHjWQMMh7VF9heP;@ za_UPHNlm^yn~la3)OvrPCS0vXqrtP+v({)Fz)jUk8C9e415S_ls$xQ)jK;U*!L#3J ze2p7;bXg;Fcy!K`$O?ib8(aIALt~cnQL4*jJi=(!F?y+{|69|?OL(Q0BVv-sE3sBj zb?AUTbDmUZR7_b+#9w8>h)JgGT+_ZgGv+gW$tw;Z zMR`V9so~G|A~Uu^J;c2E_(~L$$nY6*9`G~9D{0{bdW(wN-jX(>!IwY>7^OT2;1#y& zZ7q`dSb38}?~qJ1JFSS0a^y{~^_DmNGADk z>+`zHn~*}@)B5rDVtpQJn*7$sNS?-k6dB1=+%H;4+35y}>?Rgr6|$*d9`%RSC8TI< z3SmXJieTZI8d!?%;q`Lyzn45iplB&CKg_r*sg#Z83p+F740~Le6`e+MFy>#V*Lh&z zU&Lo1o4+Y&f6a_dEDCtmbD&76)RZG9GbU&n9mR(u;JcfNiiw4V(7j|?H+sxWXaai| z5>u+izNtKDCC!#(xu2k4UVFl5Vmb!w38x0^OU{zGlcrU(Jm_hzvErdiF@ivr7PMMI za-br!pT;aRV2@KqExYkUK>KdPUe(|nZD_aO^WE~={QXZVCht*++Y84nHcS=X1d36}0yW`)nXvJw+4%S4u5vs_jNcEUi_JhjIr zJ7~>@_&{jHS!1zmpJ8O=o}@Vok@Gla6)2YSFT(ahyh90QU=1UKD`5l$E3@`CS^bcX z|IKig20bGfZn588XGA|_6-*A!=^TQ`C5e+Ltj2m49}7)2o;IWY%W!xKzFvG{KQ7KP z^Pl$HcUB<9^x9)9^U?6{*D*0ScJGG}0>`PI9`=%mp1)f!kNi$+6 zxWkvlA8g$*)2R^r2}xU5iEFT-yFliOq3=plhjs<69m<6uV_2Q%xwyRckhw>B##hu9 z&OOXCrgF}pba3w1l80e0Y*$fMczv>1^N(y;f}S0rvMRnieD2^XzAKqKsEY4!KkAb? zxg#AnxmcE3ySgaV5g-&Kr#?mOf?b=;X5v1#jTxQCA}v$i`PDbrOuA!tTz7nw@J9m# zWwkvvZh5=CXCK7;!f{C#@z_CxfCo9?+pNEEKFZqBT6q>V02a@O_DX?WZMm;XG5)nf zyf96N0sTY#p8d7OUU=x(t&-WkbvQ!r&=BIVQW(1ip(BoMB1yP}JA0`V>3PoxzN&+- z*-K@7yq;a`BH-cGs8TjhTYc8_h-ZT_VS_cLo?6&P>_?>CKwVyA02MjRL*^PXPK9%eWSsi-IYfO9Pat;5D444WqxAuH zceD@$3%~4an*=<@0%yfggcq_HHYG1dFv3e2Y4W-3wHo8)WM{9bvN}Qt3lRy8eDYnYnoNuOH7`Ab2$DGt9;ADLt4AePAK8-Y5>_xtvT{ z{^BRB#JT6CwZ_9y&V3UY04kN1^ji#u`sOck?1eOQp~1u{$ff4^TEX9>~bO9_5$%!m*3*E9~Mt_ zoTTqJUwqZcTyJ^3+$>&i|3scG3Cz7(^71LmRrpzs4cRkG2yN~;P&>+JkB5X`v(}Hi z=oFtl2Kqgv-S6qBJ>F*@!}hWkLB&ic`1`ekeD)nhSQHzvQ$=b{;0>fJvY6RZ8k}={ z^eHxmHBUME;Fi@$MZV=0<6%YWxy9~Vvo=r^+`)KPK>IPf-IEA#Db)lv{go^-2_xIEai#Iw^&uzET`Svw)%w0WMhh- z*zK18e(Kll)nfN(;|Iz`BD)$16<)C2xR(Ix8_c0P9ouS?6Tz|YV))eiopm6Q&sI=K z+~qEynfVmJr*F!7y5-G&^R`Fudg!zE;QkP#@ma}vJLE>XYcE%x4(*8YbQn8!h(9)Z zxE&Fs@x>+&nHw8i=8px3&-KN8WpkCw#m@2|QodrG0f?9TlLT59A;6Ix5A zm4X0XLAh-(I9dL{pc(f@ajoV7J}An1=ugV z4p{ z+Tk=sA?A9Pq7Z!Dr6`+gkN4xj+~;|z_6Wc2M;hH+i{y2Bk>^DtR+ynEY|CUko!idE z;1@N2%n#`N5w1c|1cclQK@kvgD+EOtBGm9rB8nT4N=#rnz_{p*SnW_>)r}3c$M~uq z5V=C_VM0v?`8-e87J0T9vDpxlpcNG&AvI@%rn(ye%x?E-V`MeW6?XX=_9MU}gD~qn zXjBVo31C1l?s%|WioC!kYCIH$#h)&191BJHg;123jC3whsezaJ6BH%E1{sPXE%kby zC1D#bqq`H8r=cWI3sIr@6Si2>p(v`Z^KIXUMT)AZO@u2YHlvN3#AsU0cc=<^GGs-) zP-JCmhO8VkS`;E%5?2aI$Dvj!keRreK% z!t;2-8Z=;AE0(_l{MrJzbh4mq7u&6d@k4*sDPT_?qxKUyA5&`jCC*tP7)9csRrra! z{#}U0b=LF-TSV;kX6&@wc5R~ zhRkR|4auhc`SjCEtQC`4De=OPkRq2hAbmp8Vxymq`JOKHTI+oWhZ{5WaDmW6rtjj| zv@;5==|AIkQ7klqoqTvXT`KXsYBWAV_2QW~|0U0qg+}yO>T$}V{0*L(CL^j$B0L`& zQLVeJS?=`&Q1920%j;QFJIk8$7}%&R-d${bmmtl)hFwHy_F3<`ftrmvQHs>qq30O* zm&DWQ&<^lY8_LTp!9Be1Iq(vf-0v}QYpk`h!?+0+L9Vke6uj(AY>_#w@X_+$lT)*1 z!h8+y1~$FM|D2U`2yLjbZ;+zjGNNDR`-G?`niwXbU_R@<8@Q~#lI}RiQ%k^l3gWm0K4SKpYMOuCye#qSWqInb&Gk+<+GJfU!#5Q0!KkUo=)%*}xs2=#N zErNU)M$m5T2qt+g|5UmmoeUOtLfWN6X^;o@K3_BR^@*159%o3l79qJ&tO`gWxJ%#% z&%4G1L=}Nj=KgX|JB*<1zsKk4fO>aW#7Ws!ZWeD~RX`wz_QIM%2oZ@mR>NV;DbL%= z2|c8li71RV%SQz17_uDa$FQX}7C_u0&KWXeufM=Hdf@otpDC~-+E%1SJ-F?&YaTrs6mHHerVVk`YzzM}Ziu(K8oYUQ~M+! znsC6D2?5#bcUrf-ux0xIxNsezVE|tY&aP*EdlnjS3XrXOtmJQk zitLV-*KMOE&mUa#m{qejm;NBeYxmhhr41qRY~xHpih?YU>49G$Tww7}no8rm#Q%UG z)z*3Ki6GIO<(}tav+}LIM&sAPMS*}&CeP;Bt@%ddhb$dE&Zgv+-;@87Zi_V1n$iHs zk&H$HA_Z3Ll>+a?5+T>86bQ7(m2zZ|4!*GoF-P4RRwgBQ$}Jy=FQi2ULiH2E%JCd4 zd0)UfZ(APJPTMa)bHVT>0x^jLO`w@0f3~KqXnC`+Zu<%N&48lc)3UwLYd6Sl2d+i! zmaW}hdx_rZoO&eCT@st-nQKjFPc~<E6XT9#XuPgC-{$eyfE+~SYFn@#a za?FY7f2ow3#rf^XR-^xiC!goW71+FR=l4v6%tyI{RnhbwI=@ABD1VpE`=Il%fXf5Q zKs1Net52DwY*(Q57>`C#^`1m-_AE8lpS;%Y8tYYdE;Zr`{Ph|u0nHM=A(2m>rt$VY zssOz^Qk4D;Uqe>~yWnOxo;5R%mr79?L+qP{*yA}of`CH^ekcFjy@{pvYC7w^Lit(t zg)l1D#6jz2=V+8cY7OXOg%E=1GHyZpu`POUtCn{tFOD_lxDJP3fh{I>0%KVd-D@kf z@LDn9Ej2ynLAiLfqOw$njtmfCfMsQsi071flQU)1T?c0=pJ|CRqGIz35F*NR$#K^wQqw#2$TxNe^5PpA(-AG zW5LgPGVP@b^J2HcEGdzzgeDm|?>V7fZBVbJMU=;3=A7{-l##Ysk)oy8dMKX`kK(nS z_s)Aw^ba}tt(Ai*!*6?8MKD>Z7COm3dfpq74;NTvymfUc!QyS$yvR;5F@Tv=V~2+e zpBB9g$_5C8?$P;CTy&(97r*qq$meZiT&@fhEb6#8~o;z8ANmPT<6YF@U%(FdhW&ja$c z5tSXb&%@2XtJfDoh#R?ccZaZIjdSFM^*0ypZg0@hW?KU-^f#lSZ6_yn;(nlK5OPEJ9-v*8pe!fdoC@84hdc-5+DKkR3(NCV7-BA z(5RRF0Fg-$XKMc3@42SDwsdZ#-!rG8wrFm-*E79x&e1jYjY6Bl+i)qQ{xbV>x1jKo z5DlFC$+E~9QJS~itJ38zwCv38sHCx_dE0UwxA!}tVAAv1k zL>_EECM z^hQ!jBo+=aa|&%F>pD(tQ zpTWOZdW@pC3X?u>tt@^yVi#F|yRDCt)GlaFhiRV>j9pihIEFs@6!Bp^C88d5xk5eiLTBM zC2=jErqAb!4O-|>vu=f2J@}doZziXrvGK{)$u3k@ikbZO*hdpzv=y3mm9{!{#y*^-DofMH5FaWDSZ z;8$A0y91uGaKC_+7f6{fE3Z1QmC4-P>ij{bk)XTkqqrB$me&h^0>oOhub_sPL)S7| zP(L`PON(;sAF9Tb*OtvaTbg?4+|#QruAGyzKQ`?%<-FXM*xaXa0nw$4o(z|*qFR&bu}M5`7~ZlnsJC?d=;ZG&|wn! zDu!dp6R@#-vVs+rl29HHpB;R{!KZ=>b!GCG)>zjt{a9vMFPk}m5JFLdzb68e+IJQ? ze;Y#Ssj;PhAOuWjcNA!qHbD?O64wfyboq}Aye404&f8fERMmk)7U`3Q#toRX3LJD? zvxSADz*|*l)X7#a9^C?xsR4+qm5wRG#JEpVWqj@ydmih)4tb2Nl*_8&8)*J z-pAIj|BqG+8`{js;&w-M9Y^6jjp4`(f|D_2ZDT6?J0baIgJV8UDe^bh9NI#@a;Eqe zW|OvTe2L9C0@GJ9y^pPwRyc$BELe5T7Q9Q(TPwYisc?mN#`IPe^W+f6_UL6@H&2jj zm{jC`{oFaS$nJH(_+`v{hH;KawA5IjF&`#GM1!)8Qr=4oto6e-o1U(j zZ1%r~FNK3{Im(P-;btjTE*U_ZGaNuCo}(l%+o)g7P4(TJCDkvg>q!&aS=bWEnDcS< zf%Vl5WQ-G9jrTe~Y=JEB;$JBmeaX`>m71g?#)_bhZ#!S^v-n#cOd_UMO&zwugz2q&iVcTbhVSm`B0tPT z{UfBcsVzAdFQs!ipG$sQR4gaqC?1-`HDD7I1~XMl?XB6)sKl7BqRfS#_mBDL-azkf zJefvs74eajU#`fc2uGAObbnP}q#ao1JXg2B(3roSF8S_WDy`7x&_sWYQ*hyDjb-nW zuiaR-Vx+W-9a^0*GFUd@AsfmTJU7pKO7Pr*fG2ovkx?gF`s3<@G&krQ23$BXvv^m-`Te{H;=YEJ0!f;Ud$b~7!w1G);(vo1VIw-} z`QrTW5RCWbE5EhZ+wx8+BALX6%o^4g1)sakA~ijVjAd&$uW8)1n~qW9tG>He94CNS z#fDDs!1>14I0#!U6h`ebh!xkynLi<@oyNR^^SGOve`R}2bUb`%8jlWku4N(Hx- z6MTj8h+EC8_C1(*caUG~@!3MbajJPLPOf(%CpG8`eua7589BLv^@{Duyj?qUYF zQt|1RBQo)3t8M5iD6A-KP}LAElG%|8eGegAMZ2?mxW64+mPqyQ__5d+H7p-g0EldLLOcino!Lec|`h4ORsT zI+dM?Plgfv%gHbF`r=90#w!GMR-To{&Ozt=QOJRcW+)iSiq{)K8^{iVYRv3^>D^;08@!;}#h~b$;BYy7B^cU4E!=7Z zEy*Bg-k3hIQnOd7&YtT}cDS)Iw``5d+*f7US8|)) z{~QDtGin633qPh>EnkS<^OV{@)kjED-RgHz1VbaM<@{KIpl#ks+T3VI-HsDlb`*O1 z#neWz=0R`2sM;(d#qW2YY#4Q1yH3U&t8>5gI%3O(0jn?&n~QvyRyZq}s<>K1K5W-4 zMsrZNBjUB@D~lCNX=uHcvP;39+nlGmmlX{QV|Efv@3H?JV+Y?aLf{p5GIw2>yM69m zN!;Co0(#=Hq8|Db` zN-iN}v*v#SzdM}WyZf^# z|I6?GxAD8*IW^5ud@{f5A@Be1`Q24t`n3FR!72Yte)kVcnLzdz!0)~?^q<7O(or5lTPzc1|=XV#9JM0Iv8}&EB4I;MI=RZyRu~sp&botzb;&T&OKKGV&W)y)tEM91}-`eSI+1VdW z>pmj+VXN)k@U|CIQ@BZWz8`x*U%y`q&*$s+s8|E^s}o}Y^yAw2?3W_{Mos6p-qBV= zcPoMT6j=X*;^!-QV%zy8`1Le7_JH?7&UtRmY5)25WNc>#H|Q%KBGNZS>6?=DO=;54Am-!RWL0%Ba7Ns{Ph$lV=^$b@dX(fHi|FEF3TMHu36mWuYb!ld@TX1UL}sRLT{k#WfJhm4gjc# zwq@27y~)I=M1mJ^?C_qwQkEQ#GVWS0D3cp0+lUgTAjJv(v2MKjSp-nOXK&{CbzA7Q z#0-sRIeL{=p?4X4GKo)wYXUTn?QBguq&>1ll`EU3X2K^_)WmM6AmmWesNW%}*sydT ze--a0JS=j@=se;Wj2Y!U1-X;eviC(J*dARsx6~vMjX ze?|GniWsevMI>GJF*)=>&8ttXkNPnDFu2%G9Z2RmT7PqDAjAoe3EiUWs4~eVIVPwa zTb*aAKdrCo249)7>i8COHX@!|d%zEwnmtga=h@l$drEfxmS^Yh5qkbg1Xm0X4q|UF zaf?53_U?MfK#C@zWs;{|+w_U^wN+;)l#3hcT1OTp@Ha-T_Oz`<%c(eP58_>xU$<{y zs46@AB+zwseL;d6I>xN4#V>%GeD5S)lg~5qG=$H}HdDxe^Z4;Hh%#C-sSMws0l$Ls z*u%W53;0MIJ1g*Izt0+W)@DDp2mR@nm~<9g5GxsF7yE`Ktf%po62$tYrlac6xz}*s zhX`*4ob?f{(5+8O-5(B?b!e;8zap)y!*W!da-h)MyGbP4=&%sm4E@;)GL9^fFDQ}C z(D&2~M{w#xW5o<-RrFaHUV}vKDS)VKU-)v?#o1Uq;sJx%E#;`$eV>}$t!ly8L%h8;{9poR>HBm}|Lm~xR{5qE5O+C+wHrG7OkFQRgq@l?@8#gfbOZs;6VF6kff%ksjfnfC3hT&Ym0$vJl0!>#C7<-L6Y z*$*W%=c_&M%9W5iV@wIsy9yl$^`)HHAYpt%Q&$OUN5cvH>l{bD#jbbmKD8!94?X%Bkq zH);tV%@Q7$T3p|sp;63V1tg<>FH^yc-BIQ|b(9?WWf+vPD)RGJaEfWRQ#( zcCXHefl}*^#K9DjgRkOW^7s)iQ;Hgx&t0^q>Vjp#{H|1IPMOaL$~;mm1!n5`V`3piD~n&!)FcS~TJl-s^V{DAf-3rQ z=m&{9$(Y+!qh3z&OXg{0?(ymyyh;ws1%nQFID)L{)F%b81wx^#pgA3Adw)lF!SKS= zGen%2^=3{3yw+cg`tL&u7|B50T+n?40@yMqg=SrOh0&uJB0l-VKCnF54+@O-O5^u5 z?{tmvP1`7O08Kre-wb|fekG0X3fHsVlYq9isSEsL0Lqe0VSxQH#8ECp}-KKV6E{ zOWPXd+t|-k0>=d0{)BIk7zwH`;cK{&euS?~_o41U^(}vVTxd$@dl1U<-lOuU$t24 zdw4!i2XtGhzA71*|1JtsuN6SY>MdOYsmRd89Qkx$c{;`tD)8{pKKs&gFQ-r7Plah7 zuFS9!Ljj>!&;AqukfX@WL8|fnACacy{H?h2or4Yi=*9pnT@-3R9x^B*KHUv3D-E|PFh#8=;rl}0vr7e}BIz^DJF`vpE{ z73FVE+$qJ^#fLj%3MC3C?I`W59k<-jy$xT=X+x8)zi`%=+`8ZVn7S{`)SWC-P_kwT z!ph=tFQya3z-ocLr13Zts@hUQ?H*{`y|sENA`(C z;!x8Orp;Po*4@tfJHoVYp_Dzc{&u+@O4IA_`K-SruVMX_?>+5u-_AAhC{2(##E4I1 zJTDYaWnwmV=o2!H5;m`yElpg47!BpRE(_RabupytZ~1nv+p^6Pd#lBZ2ed${#lQb>(J%$bp7czu z-?AV2SM@)Cim<|;%K!YV!(7V$vHs_$M2$=9ZK(?>wkU%?7DRB z^ZTFYZ0XXQS+6jotBHMI5o0`W#hD!S;2~2+`JZ2}>9O`duO4ETL5nd3!Zhwv_@7^) zi1B&U6IAU|?E6q4-yg6rGB9%?K&>C|Ig(%nUy2CZq@-o!P$-aO0jmx%-z- z+=+UAp+O~*Bk6V;KGBchO~iXkM8L(8G&@I*mgZ0Ne4e+nfiWkZl9-QY;wfD(&D3Zj zP5|xG-uSub;cjH>a3jUhGJ8inwtV0sLGaHl;Hze)+o(M8E6P?kr=P3kxqNk=V2R%D$PHjSD|pJ#32G*1UnPo<9iM+adb=%)>ur98b7AMd*$B^XQco$DxA`_T0Qg-6lNKjQejOWQI3HNEH z+OqR|uUVHWoCQD%J~BCWse*T^cuRc*Ys4Ul;GO%l(exk{ywhsK%R3dM!!1?F4Wa-Y zyHiY!b>Bm$NyyGh-u6%4NEFZPn}0$)PXT@|E;^S%(K8{Keh&Pwxy#1$6wgcAl`wmV zzTFUh7Rc2Ofd{9uH~BLL??K+Is7fLWGi~Snn#kum&;@6;}Q39 zyfT7&yxDymt<2sYS965Sr^#dG<7$fVc)UCge_YKF9x?~hxQU%%2B)g4cg(fdwkJ|K1coVL18slUey$M3BIBi@oVB2aXX(Au1T+8 zFND686Rs(}etpJxO+V=~{%7dV5tm=g#{X1gF&2(IQ7r&-2P}_uMjQxfw+un+y@dFj z_S%CjuXQ9WAG?sFbLC6c28rO56K#`T&${DdqJC}^oBiCtpV;JwPt@WLm;XcCgJEsq zEbPv+!9PDSUK1E)M85}0Cl6wisNKxyxJ&|B?Zm~6%Oop9CN7g8F~+-+DJ-Ex1SAZ+ z8|!nLvkNXM`As=AuIOJn>sNba9p%I&D^8Mh89FYLb5s2NRB~vK#LFbH#M$^bm;TN( zTr#njZb8&!R!#2+pNyHHEM++>9fHYBz6&HDT*26b(QwY-UvgW{{wynxp0`tu;Vb{Q zaP#}TaZX(_ny$S*<~7O&9&7G<|27cIt!9X4KV$wBnV_*j-;~SM1@p}O_EhIQ<$z1} z)<*qE^?s!Zu$+t7I?*zpcF>g-?Y>$>I!hyS>81-}1o(r+z)kY2M*Q z<_b%QkaC=o*x{KMvGus&?kg>}9_Na!$GKwban?e!4Db|85>N3AmF7f#e7x|0N@8Z8m3FGs{vRwY+^Y`&z(oE}*YLUaCX)q&k z?+T4#4P%zf*7A0xDVF^{S{=4M>O7N@x99BlJzcWbXP<)1)*1)T$pm{hGzlR*i-)@& zDf#_?$ZhEVzGm9zQ&e;4=A*0hh7ROt(;oCB>FNl1D<(*n#;_PiVcU6qSl?OzsB&~3h$_6+1NNNTnse?dq{ z9DxQ%-## zHe!m?+Q}7@cuw}ewkeQmqYgR3q6Dp>p68X>FlE}iN$}%l?J}ZrU`#3aihs$94dj@Q_ z^q0UdGmlKX09Tly@6xY_-^un%QBAMvotlZp{NJc9_W!bU(OPpsdM3%y^P2k$j`Sbao9$ z_yCmR$pPE|^rdJ#(uK#Ib|(xz^(5=5^m{1cT_?xGFWuxLj~#lG%yjh~d%9sjIdYUR3Ds*DY~O11wv zn2w*A`TK?GAj)9!4!M*k@t==F1?&N2xmNr|=$q-)dc-={Ux?-jzPR-o{E8lmiy)VL z9l~wmdN3pdOaWpBn7`#AL!~t^MflVkm<^i6)?n_y86gOhj-_Ey*8~B%5m5lmpC2gvkrg#yf{vt{9@vr+DWz>7* z#VP#Dq5mM#NxADms@ZF49z}B^zfUj;mvY)$#=J|$7S?6fivf7dpzxR@_GDYA{HJ;6 zt*D5QPdKR1BgwgnozJv5{9So3)Lr&Sp&uq^D4QL5uQ{oS@?LN(F9OEY-w5|4CZ`$t zl@&FY@c;xAXl}>HxpP_oPyl{O;iA+nY*V!tZ*1O}x9Ew0eF&;8#l8 z)BIlG_Zq*_eW}!O{7&X~7Qff|b@BUvU%`G3hV#3Z->>*R%{6zna6r*B$r^|?s)X1j=#;&RGm09dr`GU@y%O09c;o>)A09pw4o%rBwUmpk&+k+V zjQS!ei*wM4S0#fzP-$vq+rDl&ZH77uAq&RDQLQRA%r?PWwyK&`$_-z}Q~9ch`-D?& zZ{@0H_vu`oFwBv1$$2_YqjcOno{&#_t995so`{;~HC5C+ttdvkbG)!zS{Y(Ij6;Su z#g4|Fr|d0C^9gpjUZhG^Ar=>=HLG-bD=bg=_Mm+}Tucq$Ulp){5pF{~9Rz%|+H&cS zmQM|c%+~TGY)09{c9SRdlvj;~-$&}xCibBnaz&nm%aw^rWR(y`3mX+}c*W=rUoHzv zx6$2378zaa_1)3!V|>M!U&gA5%xZi|$c}IL=FHfJzXiZ#T*LpC9#gG3z2z}gb?6ou z(&YH*t=4ADur_0cwFdRb>wOw0oy}2frJ^Kg63#CsmSKStv?f-5s*m^2?f&s_YuqS4 zt`;9Hne|lYy>O(;-}UFTCmJt!q3nqUL~dvNs?wX%ZsXVWY3z%Z|I6DK{q#QmaE=IS@6 zs-;qeSUn6@UmkNUM)g_-fp_JfOd6<(vETH}5z{^9`P@Mv^H|*M=|N zrK+lUbRXqQL1sMKmA!Scx1H{7Tshl?B}p4^WsYib7)xxs9(tN)$yuZl#E)id*6p>q{9)kgk_AB#m5tal51;;U$f0QoF_=_xhIw}=B~2)hSVWyl zv@q`uC0GcZorIGekI25INQn+i&-h89p&SOHhVNwgp;VikRib}KZNUO!=4>UmnU%;< z>$|mXR(O%FuswN3x}ufhpgO}}ar{B42~5wJNufLB>`}*yBK!=GO@{R8q#J$q)tpXJ zwx6LZO_8^cd0i3qPT4wyh`-g>@g!HD2ozK8mI5XA3I|s&%de`7(Dysb(txl3FXnBs zu&qwUA%Dc4>RyLEmXhHqep_UM^E-~&dkVj^`33pq!vBA~d|(HN-pk&fvHoYs2d>N1 z{h9Ww|I6L)ey8r1e}vzuQD1#Ze&>Jp6QAb%2g~wjIR7C&hJ^z>_9nd! ziXSt3#wbxep1m9zm<>?>6=mUFT?$6_3{X#87mUB%0QD5MSFPC1rMj@n&sW&5atoVP z$^m99`^9URdrCEt&-75P-}}$a%{oKX`)}PlG3DbsfiNfAiR7umG1&c2$}KTdx$}eV zni<$rC7ce2n)`p03%_i}Yse^z-veA(PO|G`hF`$HS>4Xz_<J?uMyCK8+$QQ_x#$Y~=>WwczfSfPXD_GGEalkt^j)77o@de`k4qB6!9Iji{ z@z>l|b*#sZC*rB@hpm1dH zX9|_*Rsczm>Q(@8P`5^^S9I&5?{xbD(2mpH;)@>Ll2U%Wvp@1jE@inBHO=(E5K_G!Rl56km*}KM=yY3X)X+-`l^6zt21s?db-y1opBi%1;%^0%5?2MUwiwL?vQD%^lbuv)5h-yb~l9}%=FZi zMLqM2o9MKCiRAD3toR-6o8$m!*ePhJr$$K88@4dvPU*+*1XtbZxUyBoQ3i^Uo6b0a zuMqFAb*07>c~=Vs?@j>;^r?Nhly>=ymODzkb*cPtaXOLRyr4L~719VU^(D4xuxx!r zY!f5Wt_;4(`F}i;i@*U)d+Q1?O%5y6?PKd>vQ6nFdSEYg+XiXBD}&J6Y`g#6Gr+`; zkwNTn@@N7uX_C5K8AQ-N_tkH4fGKueS_Z+|P)#fw*$m1k{=3W+`?yEQ#F1#`#C?n4 z@)10IQ8I|V&afw_!n&`4a@l(~N%10)xP7a1aO5kmWhD}rq2xJvl07zZ@BTybo{$8Y z$2%VfxWq*Zo)TUZXgP3@8~IrrexDzVo%9P;#(g5y$i+t;q)o2?zoi6Rd@MTOK1&G$ zr1n7SsT$Aww`^HmCiR}BXa*;HPYO;47?ISZEg+|R{=UdVt!Kz+@t$?-mGc<1p_)GoLVrQiD&mH@uR<_^L%uMbX;1aivNjf!KWL| zpOROMw|yd*o7bnYB-L&ZwEmL z$9pl_wvM>pp({Xv&;*|LP-$GAWMv};?U1ylreCu7{dQ%yphU)|>B z-zoVwF3Zi|RUn1x{OUG0f0yL{Rk!@Ru?tJ*SGVc>CA5c=C|jLdB)%(ft_4R6aaGG{_w_U*zn-LHSp;H) z*lAbGmRayNuHQ7s2*BBhOM?;PNr8S}EV zpp~@&mZtwZSLxUA#z^5xanB=Ek@aMZszWeD$Fe9lV|P>rV~>tv;aXMAKWEowK~@-} z_?Sus{lK!E)h$f3!l4ajrWufGu75Vz{1& z+dai2XStrlp9Kb6oeNJ-b3K>#ow1pWGHiKtB9}i&%U}A$zirIkUikNQMKAsy{w;<46)3&*Jl`n#)Qw+@&Dr8?c!n<(vn;$==NHS1sSfm}51L=x%kELLiUFFL-W(oq z?hw?YX@Z77e*C|&buN%rGxG&VOE+l-x?6jd?$Wu3-5~J9V!;^)!f7{iMSamfmEW{) zm0d|{5-Wi?q?4TP`54n4I~;QhE^Jw$bTBUkYI|bs)6}#F|DK^Y@T--OW1?0CtiJ{9 z^WZ^@`g~Gg+*6aFo~_Q?u-L}3!>b!MS4UpY5BJACf3iRVO&H5YRX6O9Y*T6PNZM>x zrVoth0VX^#SIC-bmE>=f5;BH|W_hSCl~+Vb*%%t7_6x=`N;;E}wb3@AM5$IW+D<(k zej`~XnGSa$R16%&{Nk5n3J^Edo=ZHvRA^wAH#M$E{Ub6d+uP}*YOFbI(Eeemjg)%Y zliBTp*Jv46RJSibTt>C9D}V)W@GUb+@WW)(|CxmdW=w$CQ_C2w7Pc@Roy3Jk3%@02 z+k&wNs<}Xne|0g$So8T}$OqZ3hKp2r*!Gvn)Qv}nyI;qnqqvhll!;yNu7TKC5Fxr8 zb3iUq0P4kR)Qh@+H=JKGJ*j1d@VTrAWLl#0s_Ns>g?ysg6^}M$ZyU0=5%)G8UBsO% z5U%qGwjUk3N1}gx!#VBu)FixFBZDMZq{IZJ65MFyH{VvAf0wFyjq@Xw^@wlj)FR47 z2`Mj~J9!hZ{WMv2-7B|{hORQzq3Vp5OHQESHpy6auYB9kQGq<@Xu;PFZzhhCtJRvs z_8*DW$hyhWaiy6Q=r{oy=Z=B%F87J2l=XkyIcJyyBbOFwD?dI zBPfa^Tmb7|FU$SFz+QWxtl-*J*O zfDs-*CRbgoD%26I6wu?*4k~nS+q1XL?rl8U$z6817SV?#Dtj!vNK}h=KB$Ikk@Je$ zeUALiPBlc*m?s94(Yab>Sa~H0qAo7e`6G;!BsmZ2O{=>95ANCTsg79p=}su6G5~9q z%(BCkN{^yn49eRVWd=pQaURoU$%oD~f&DFT5^OLM)48faSt)4foS}+&!`a9+aRRx8 ztD5%}wdY8F2b#;YHR^AMb=8~v^sF_a1O*a;A(TgEZ%6ih3A5UWj?TPi^UV9iDUzUc z)ZQMAEzD``!_p8rl_U+>_hr5LiLs?`=+OHU(NQZ1%pyS`FoZ9(j(VNW{&(T#b-&ui z6}T-EbkoSJ;8rWi8Yi6!Y07CD`K6-Y^DcR%wChSL;w*6~5HyzECsS%qt0LFQBmlXu zlLQoz#SCmQStM;~88>FE6)$zj>=uMuH|HeYGjpZfJ!|sAQz>q!RL6>PxgKj({rq&mOO=5T%}_jWEhc1babxpWsZ3#sTTvJ2@vi2g9V zFHFn~uWIXky5z-@)h}KCvhdSZYvNFS{R}nj`;)F|p=#jB8++7rW=>5nbDwxv0lrll zcMZ>aBRr%+lSq5yX+6GwkK&UoE^+6*6IGsMjUGzXc1ue9t2>k%-3;Y9V|}@Nju=h` zQ;kM|1n$*noU8BEXpG=K(H{y5G6<5dlMmG<{SJ!N^Yw=lGEh{L7t?s}GlZ6pX}Ui^}V9t1(q=304oj>cgD^8uc9SC+UL?U8LYnXV{=Xw2kk z%oL^|{1v()Ex3-Wa})n`S1wY?Ky~ECm!&KBD!e7%{$H!!+&U$2;& z+3CgFq^`tYG+kUNmw)8>z1E>SSxY~oJ zs-;?XO9z`Vnij=e{%;dosHzJzM2@vd5;Bf$EU$ zz()@a%zMvke0S_{qq3$0oy>&c*BW#E9oHH+H&3|MH12E_cJfSpaQyGdkY|?Qs3up2 zoFhQaoR=4Q295<`Rq?5ap>QB}VJRlvo()F*{{de{?CG&TQ~q&7#kg4+so)v8ts2M1 zJbqbhwgabl0ZZK6yVW#;`-FY}d2{#uk+H0q0B-CFQlY`(uOZjJu0B8coP3bkpJ1Yj z;K;)vD&#WxoCV)tDDEZT=`!v?ZZGXezCV0=Vm&fkz5io>V$T?ET)OT(V`}?*rg19L zN`IH2<4y)8B|tcaPeA9>Zrj@^+~o zCUbgfQ3EA(@2|8^@0Vn#=E@IgM|_M(=jNJXX%&$lxKt7!BkWGZwC|MCK!wz#>fF7R zWK`{AYl)0(pFGC>%H2ZT-z86OL!@`9NoGUKi01lr>d1ij6vzt^u(GC0)tN`_DRXGM zJ!Kyrf{NRL0cP${w(6N-u=YsOpMsC@uth zWGy=Lmm(X9D=zb9CzyXA7`vujQFdioH`4OM@k8hn>Jd^F7_|zQ15CwuYJ;~yPM4)hXPDRH)Nk!-Kv9WB*VyUN?3YY}HNz%C~OH7-{b`*aMIb=eI z>9a`l)KQoC49T0vyLB^m%U3SOz#lkLY`8z{AdaUwF6lKYFmtq}a%dIvD|h zqKv>Jzvw*zD5>9YYS+>Pdt{+ckHB^6vCk3caPFa*=@B?UMj3&R)CV#GH?#DNd}-?; zz<<2^S6WIL-Qttu)KEaNYfcf z5e}l+8K7Ikw0_6AS=I86bCa~DZtc_@QG?=F<}7v&qz7qMX^^? zs&EPv@ff$#-QQA_UN5F67Iyny)9cts=M`5S=xkpjMWw}Dx%eRTQpvA`o`|i8W+=Wv zCDMDl|G~n{XhsdM^+l-rS$+DJbu+?(kAGgqw>|lagJ^9ZdPk~T3H*#Dg6vwIq0}Ov{1(OFK8*30rEmvU z6e9$sxww-AzHq+HRiHgueG+%3l_`;LWGXzqlV!77T3MFBf;qTqaIp zz(x_}fIv(BV{fXV)lGAL-hv(L%N#+5An6^pn|&c>B&iOaZ3$#yq4Sepiupx&c@2c7 zKhionYG1QNB7@ghVJ5)z<)1@hN7EKMhte3O8D*bOLWFTu@~3+h`>Q;SDMho>z_>!T zA|gA=NHeE27|WJk4`CtTuq)AgTWyEM$mBW1Ig@n&spdVcax@vP zTAJXDl_@8ih;z9ifOK)d#?;615r)NbnEtumPCDY`^W2}-@o~;6|i^OAZj>KcE zNIb%L`#~sQjkJSz$r4R6nw<_8#R`#|xC8ToN#!Pvyo5gUUZoWS2hDI+#!>HWv^yFvTHQNdF# zEf0#P(k%faxQ2D97r%=a;SM-sB?LcQ`3K4RpVItpd$;ukZ}jlH`KHw#-W|+WZF2eD zjiou;+B5=5KkYeAox4pqXviUTzkn%RSthDH_YPIy4KP|JUJND)3g^f-?*~ zjexw{cuD_12mGtQ2>9cprfzOoUY67M3Ok$^Iu_?h;P(aPrg{Z7q=G}&`Rwxv`0=z^ z%mL0re3nE&;bXl+xWvRUy0BL`N73_1`Abvii$9*@0Y(phS=s$rX!r6IUYYIN%Jbv) zg%$a0`gtog{(N9smy}pz2Lrl{eok8_MQA3j)0_e9d7(Gr-bz-$)NcpoiKD<$I&no! z3}0cgY~tZiG6?vy_=nt3f0c4t`AYm3h7UIDs`A3eD*k}o-lP$G{3DuSzYzKbt#_U8 zKAb5%+>A&;8j)G$WIRPvjs1Owzew+Q4g~BgSisv#@gf`ayce1*E@y=)XER&m`~e<< zXeP>tK_IC4TcK8%No4p*xQHI@pUQkkH_~`>yu8jC&(DZI3xHP!nce-W=+6-u zfIzPCV%-0lG|;upkp);S2Bvwn|26aKnoiSrxZS*ZIw9~Lev!AbG&hN8!yJaKig3Z0 ziC&SF7ku3OxD-&N`(NlxdW7}5z*sh>S{V&t$E?>w99WBA(TxAB7rXc0zKr>SWc9`H zZm>zF$b;QcqC31~{`P8*S4kn=`BF|ta^RbpzdM(FA@~YAF7%^E8$XucCBDOESYEMj z$X_f(F#N1?P%Uis7B&KPbDqr)-D2A3wvL25kB$7jUp@BP*F6PK)!}>agBbJ_f$z zP{JH}o4eoAkuhZXi2o;`u%}hX?y5BC*%7+jZ!FXM!!-UK?8cSITSTd1X9kJZ`1e`p z^N#StnaQQ;dm7)AyoNshDPX-Ik}bw_1g#3%SIc&fO%aCq*t!Nhe+k`0(l7TN>Ge9;8Q{1!Z^ggln6SPj#CwLHkN7DX1Vl{2sN1e-29r zRvgfvOpjh-D#(_W_lcdbn%*ou+8wVLfgJQ}Stb{%Mfg-xhkp?NW6ED|9pSG+{hj++ zN@eUjewE1^euX7fO3T`VxZ?#_#7%1MI9&yM{6V+&_Z+WNg){i?8ZYTL@+z z01AjAKMH3UmGC18f0^%n_daLlObAqMZ=d^o-* z0)+T0s%#}@Al|3Hz|Eqs-wW)LH*&0#7uTgJvm&C5Wz{(KGY=2F@%O|HWQ;HQsV_Y6 zt2Zft8gc*+bAG-BpRa@hu-W*ZCs3gtW~pyH>ip!bPGEQA^^EJLbe3uo0?Y@Zfl@aXV> zW|Yr-XgQCG_Ps1>R2fniT8#5Awy!Q!#Wx8!)tXSnNZpQhoXkV+hj66 z%w(Gq$*2jDRxa|kCgYo}S1s%mEed7x5f;rri+jmN<>0{*>q33;#__HTeLoS_-wdk_ zQNtS0TQ&$!Z+HZBK5uk-u_dQt*kR&&W!ri!{JG$M#>d1N5@yShgnw9hzf{tHDDu;f zDEviBywQY#0X&vm2^-VT5A3OD@2z)i3z8duQQl6b7J*o0}!KomC z+>mFe!9l#ji3eN3W``GNR2aI4|JXIHe3|H&ANMr>At zI}_1zeVV9DZo>bj12|A@#t+2q8*mlehSgBt$ia$q4D?!SLqG)Qi}j=NQa{nFJhm26 zCO_fn>wI-H#YBY-B?mE=u3Un-g4rRb_{*E95l~YJG}XhT6Z(UK(){{wV&bj%hizgl zK&q2bnmkZ0&=yH{euvVft%VxyQ}j9s%)tM`L-t3|7c4@o8SR;b2;&;r077-7o)v|& zj71zKD7f)~rR@FL7QHbuV*YfKnWiAqHFL85Y9l4@V`e3((;;IgatpJK&r4>a%C4wV zP$#n$Mh)W8e=!qvZ!4-_D61C_R6m*t!UO%UA)}yrvK7W0nejwoHVQ33hDFR^5wi)h zkl!(k#LTMQBMTw)RsBIh*DjOU_mb`bvVs<#k7U7R#J}`u4_S@jKw9M-f1JILbPcp| z7YG`Xy=X7vBPIzC9Kj~ovo4oeZ{t)!))!^gDfX;?5=lZ5Mn!;;Q1V0FvfWq}%OwYL z6k<}S5wbT}2gPzZRRmSqg39Nk;S8llK`pT?*VdCCh`PeE4CyyTdIKRUEDwo3thXns#wu|r*TlwD0U9&-kM2u1%M5eI8{PWLOy|~Luf#(SF*&#ftb=#~zD+^B{Y3e=oQrS@ z4?Otyc2MbqCekoO7p>87iWV_)G|y|g%;%|6AyNlVzSwA*B}CHIG6%%U-~0oV3;Bqz$a*ZsS!1vN)qFw+-Qedzp)f%*c6+)M|9mbr@W7{EMJ;x;orZAlo$q_^{1DsjCf}Ve zumqp-%8modV`4&pjQ1?YCTpT{$MT6v-#*ug_gg^Cr&aG)Zt7A9b^)~ef%lLA7b4uU z1`(_FE4N}9)eiPt6v{360P_FTfrk+w(JPN4J@9EdV$4aK86SOXy48aNW~^` zLZV6C-eR?4l@ZM{zx!@R3ldHHBldF06K2t=t-Gn)9K?xMq6I~=RSO7Ft5tWQK1sPc zD^Ji>H-Psn6WD<_lugGyIw+s>fztvUx|kicM#D_e*C*|zo?-TaIoOMXXobBO@;kK` zo4}e8z-v>x005)uq5l?@eBX`?e`Uocv3DU}b_2%aAu?#Txn8~y{fajVU8aHYSJ1gL z*-uFu@c++GyVjUjgAgSMQl7*!()HOS~-w4 z1Nt`{-fKq8H+C|jw;Azc84NHMhR3vaxxxNvt#oj2I#Ea^^RhJJUW z(H}{olhJboI~Wq^MrqGE3?7eQRH%fmWjwlJSsGG|*OvkP%^q0&ivEohP4m>Vq?4s; zjb)9hb06z3yS{dme#^1`F0oT_S047R-vuiJ(SLNb8T=XLdFbq2sy?kocJ@+V`2HH6 z*d^r8V5wo5lxE5prvuLD@g8q{MRDQ5lI7lD+*#6_jMX)N;&$!s#^|Hyku5(`n4M>; z-bfX_Npv1}iH{MTM~6v*i^FgfFNA%?;rsKlW7kXku;hF2Y(P<%6I{G0-RmjO>%h~0 z^YgM;;1w_K5W*CU8&5m~OsaF|Q49w?j{8wNzIX_OxeJZ9w-~S0^43`DNyXQ1tcP=DAp)Y3`B0P z(S+#O6T~fvR}p+i9~PhmUI}^yZxU))mrxHTKzQ zB;`B+^iKsDmBw3(Av>-nHX6O2B+a`O_ar}n(~6jXVsUt44)pjIQ$NH!kLp%{Yoeim zy2H2zJ9lf2bf0VQpwu5Z)p;`JM{)QuOv789vD^6;ySW|xwCel!%|}t+L)pf$`F03j znEQS%`*8HjhgxAJd`$0U%H5wN^Cg<(? zCj|JR3@ZIpz(ce2Q_-GNDXuai#KzzX)y84OI>kVKfh_PW^bJaw!4g350_aqVys-Uv z_r)=SPn%v{94_mN&NPZng}E&T);|o0gl7-JQsCA9L?s{E_1mC0tKL`L=gRzU2s}0_oOPEs?9Kx>-Q#_>5iqPu)*rsDQ*r1^|68iApuJ^(_l36kyTV04 zuh=DApM2VOHMolHTO}4U6(<5sU-|C)sBz;~Mj{5`CnyA$i@cCmhbW?MgTCdc*DJqbOs7SKFj z7_YYS#QK?GoK570X{pIC2%j#XZ^xi+GrhX>G`xX~6Jd@#4RS^*SB$fz&;x9E-3aNi zpw3gzafGA;f|Kc0#>z5*9#rN8dd$T`pvUQuRD0onjg*g&(1l;77$;t_K~V$cmDuqkl$8M2*j)^YNDCpE|&g zhrHq83G@&IC&7O}ZyfmXng8`b z{+hr)8-C0d@Ti-80!xVtf)nk*D%g@-1skr_mx@no1q3IF74VRSC0qeh`(v($b;Hy8iiPmx+B7Jq9!Y;zJozsbMiT?ze6;Ed=DDI@4uUmG1u z&>!j_w|T>l4-(Ynj|u7m^ubPHMf9R}=wqJO`NEF~{J|Xvqx*Ub*QOxPUV%JE{9PsT z$oUKPq?6!}yP-q+vZCbo(H9cP^9Q`y;E(kun*Ha713WPwt@cWgCg$VG;rYC1Uq>MAU_K`GCOCl- z@J`^bss9aqnD

9POwt9CW6or+MF;H0Y`K?)>D+PGpMeQu5>QO4aABVKN+*1+G`a zOT|ang?&2i`#{)L6qVlyEL#`ohnHS_xde*t>Ck4(k$0n)RAEgm$0^%D+&`ET%hDn- zC;rpp)mkL79fl}W@l|sham6~WVm9ECvS+_ON*c5mzTe5^fc$tuwOgA6BO6bnYvys5 zq*NF{bg}J!Gu$-|Y8_Zq>YY&i_$=1j)_;-WXyN!USFNiBy@+#J&D|f!0nem5)R~9lK-seEnIl_5 z3JS*lomzcl8#>pdwro~&o7A@~Fhc;1Tiv7Vrn|T7W>=SEC9ANy7aE3IVi{b+&|45C zxTZ^R&9?q^FurTOLf?W`P2qnyksJD}+CGyWkuB7_QXlMU4x6|YIPfF!iV<8T2XiFG za8=-Pgoa1v1Y3(rR;gN`Ua7d16BGKbG2CDssHYt;e!GfaxSI;d-mVr`#jI8nNJVkG zyfmiUPtQv@X)TcJs{qhG0ib;y0_Y-{rK@B4%p=s5Q7SfJoS^J9QgiQjO z?uSa~90VoWqlQN~;3RNpSx@H6X1)*(p6mR(qWiEFdPk@ha6A1VaJJpBvwf-KE&UI2 z4Fn7OUG6_nsPv}dP`ShV59Xo?slEr{(Bh7Bp!Rn1`wx%@4lM>Ek?GJvoYMaLd=KLO zgL}~|%KNf@#g~EaJtait^8r5d{sWO9dH;b-zYxcLJR&r<02Ng0{sX+jF$44BB+`kI zf;3aK*mO6yNF>1h2doSCA7oR;X1o7Dmc@*QjwC0&|A2tD<$KYV?*`^e+<)*H#Uce=moMk$5L>;io?3nxn;f z!0FBZ5F7UTVpzgOj&&}mh73nAlz|Il5F#V_yCQ!)e!+>UM~1IH4#1}w7XXfc3o8w$ zqg@aJ=$>j-PaCPF?;IVzYbQ<2z#C(^rG>5$6g{5+ncv-ziP7V#CmR*}&jKt`DO9wI zz-qr%rSKC8cnR;RyhvIA496oEU)K{(P4zseR^Gh{yHC*c3?=vqS}eWcAStL(h@xPy z;26&Zo5ZaB)OZUG#O!V4XLuPO?+ju`kBRV_+F@UVSLyO&flgw?Q91^G7+pMh^=r{x zI3dsyzVK>?_3vUl!@StozlqZLH$%yJX{MbRp^fFt=5B$`36oMT zhh5ugwMG4L<7auKQ>EHpfg@YpsdOefnS<=ZnVMZw_IH)E@QGwv>KgTQ=j zZ8qPWe1mkfN1Ipcfs+CJYk1d1;MKKi{zkQFuj+t8_tl8`F^*}FPjk$v5N&Czu9Pp( z17+jAnd@DZ!#cOsR4yPql_fR7R%hi$%B(^7s)Xs$VHg5#n^WY3HG3~zjDU$rT1_OD z9eog}IoCDEpXV$pukGZj#EY_Id9W3#xThfsVEDv6=e0fr!#iL!fwSPG*Bbv5>Y7$t zL=qx3IW=c(krR`6#5T+jyo~ai)_>b(sf{lW_WQ{&eU5obO%rUQ3G zWL-LUwo)VV>yovY@PoK=frbwDG%BHI*o&DfNM~(3LaZr-&>$2N%fyEqbXXJLliI6u ztzKF3p6}KlE>f$@vXSpP~F zu<8a(r#+Zxm_x%botQ2$ow5?sDQ#GXlL#yg+;1lQlSum~?5}AJsZ{(0BZ75}=hGE) z@LsGx)|dRzpeoFvIK4+okCK?lf^Wd;WV6y9A*{5sWSsO#hXQ*2O9gOvE~gq?9d(q_ z>V{D#iC!NdAw|QojIPN@(KpNMJ$!TAioO39|1jBhB{EDHm=92K=qGI_tfnJMwEP?y zg)QW#urC)Z{HBd!AHY`pHwKU_^8%bIrPzB~{#OQ3ZlG$@tAsxi2!EoT)dFx@#nq~Q zL9Add$TLo}s{NNn{{~X4)SBUPT8p4zK=G8v&=7MU#a=x7uHMkAj z>>i$p>AtnVk>bTTV7VUb^deu_-;}ND?xt*&FqM{nKLJ7@8Fo2AXc?-9~rHMa?16^N1GXm6*2gLC< zfEsj}yMu)M0!Ingh6nu6-vU(;Z!U;Vb4P&o<|CJTtrKGAhXC&SY$4zWi`QAH=!#Nf z7g_X&c#kbuT{S}E9MjoOj3>>B4-{){(FJH@O8?PdA;v+I1DQw!Mb+xqFJZpuE7-?( zHPqn08I88e_k|AG+6(t8iQ^Bcx(yjka7P>WRm6%>;T~)+umMf=e^0cU)hF=_=vqyn zRy>H~YT7v1F*3KS@G+?(3YH5OcyUx~oIaiNke=Y;&E8zP{D>Qc2wMy>(27Ae5)KB! zOb5IH?u=X1tme9#oW|{sk_es{sYDn%OvW06+o!?|$#occ2)4IBvHvpV!GmB=hdyCb z*Q%OF^j>AzT6sR>TK~B#j>$A>QE~3^Q_iVk-Cqwwyx!M?Io}F~##0SuZfmwTJZ-C- zPgg*8TSZ&KjA_g-k;|1@&@YSY9YS^gHIMr(h*;7)K_>o@MDZv05cqAPC=Qd6SMjUAY9_| zkm5#g6v%{w@|&__-BZV#4$pPi_ z=I>?=I7R?2H_Xuoa-TTn)9bzA0b4P=APr!5j9b90fX9gnw@3CtuNT8P4oBQ6xPXes zEjAHlP)a0jwurd5I*kiusBtcyHva(T_^W{TLbVVK`az}Qr`WnsSE?6!vkhj-gkRplV)5D z+{0NNWF!4CW^=tzlmmw5Ibi{W*tj?!o|h+%M8Z#|;l^)oxFV1Dgu~LQY@9dzq>Oqk zk8x_@zA~4P*A~RZ;jLH?AdDX5n&T;Q%^5eMa@^l0y7Y0LiQ2fA@N{9R;qfk&lWpyN zQd~s7&g5rZN_NGEtOhSneAr98bg$STL%Kh)L53hrUn|WZD~$|68is)VcdC$>qprY< zMQ`99NLq2&+Cf^lHp%s zKOy0Vq9pvp3*jeU06!T8_*rQNS!rYl;fIh^_$_f1tfM3IY2gz1vd!@K(KZHmXS94e zac&&PDd(sfR4J-K=m4*}z!$-`jFRiE^A^28)qrge0$x)9uLl7y7?lY_t|@@mgMb%& zjd5eAO#!?f1iYs7=iW_!(1h0%Jc|G?m;QJ*dhF6F^7!=Xj|({9lTT1bP{_Nhs039G zp3k!aV8e9s)MKXr!H6_I5h_0QH&L`xu!{FjY1cYkl_Rp}c>adR*%NioOA}q%h@QBY z29NGgBgPS5wf~$1{GrYwbc>Vr(MAgVeL5883O#6!5jfx*JZZiXx>u-_c@|>5vug+m z5#(+o#7U3I|EQlFV(R(E7$HUyULeSn23&fEOTRYVg@eafriqW*fx*LaruCZf!@;BS zrgfij<=`9oPVbS#2WZz~#~j!c`+ngKeU58OBE-7U+Qsa!PHO;Oyb8w02k#2^fWseD*h=f{K~E zxKFz;+pG1?6JD8c_EQZRpdTuur+M`O2n56M(HIb^_;9cwHNb^Ul0Q!z{mEuRrB(YR zIte@l*YgR{zujh+|C#qW{0@PDtnklvfuM%Mbg~Ct$(na9O<504l*dNmXC&mY=x4g? zo62J^!%%i4kG)@nTk_bEtq{sf2|SpZz_^g(v0&WNx6hVt0O0sDCTTja4VH+&~~Y&~#LxJ)?c z(+BQ7&Jq0~NCi})M>t)&f~l0QHGumqY%V=Z)qkY&`mJ+R7zsmk)))8{^Fj*LA1~3h zL~8DF-^EMOoHC>k5ul5fY(;$AG8eTq6jdT&9gWQaK{Nh!3hXGZC>#gqpV*Ll;CG)% zBtQ2$YghFjqG^sY+$3?xufh(7ZJTpG1RR6g;NT?0G?n8~6Dz`)?WQODkH&UXe922O zY&k?z@|%PR`UAmFh%(#|9||dM1YxzpkQfgaO2^|GJRrp##xFE+44z(S;ZY4jdulws zAi5QS5Kv<|6!3Gs0Xgm=a4WF`7P^OWm{jlL{W=lS3n4L>1A~0XzOWq-NUjy~+a2hV zd(P`@g&r=#uMp!(`e&2hvR#zlEtP)gsBG*>_0kaWm!hd7ns?-2(X?hxq4 zjvbPol;N^eWeGf1HL8(q>BagjLohJ63;{K~rHiu|Kk#zE9}N*MeVY7C&;PV(uj5mf z#&03M?Sc#x-?Aw}e9ITe-&wqxxxez7&aSpPcjYod65XqV0M}i4M482#r}0AG(-h#I z4P#8^@@Ort$lh#sWpng)EM#*{_{>qA;ZGH6j4ysA)dINp*iH2U313f zN%5^87m!-=TWIVH0WP1W1h;!a`ZKMwbi=HcR`9P za3kgy@>@d91RenLp$R}pD=E4aIMPsh4jhLOIeHQ~dJ;K$I!g+Of65a0OW1|(f2Hcn z*hAwA90a+NHCHxE0V}I{94;uQ=s~_U7(tXr##hFUe_8pB2{b(=}~Qm?(+!o*U-~mjp$X4Io~JA{qX3E7z3o%s8~gk~gm^ zU!H+JwABO-LLPpIyq;a2UA}w}V#_0~fv>FwOCJYVKIY>9%6Jt&Hh#68e00bp3tPLr zwU^}K)bdd$k|*V(t<4reaX>5zB*m|OLsoucli_HDTvT)6_A_pevD?*$f%&`vNGlQd z-zp}x^W*L14J!7Vi}W+J;^+0@={WzZxI_@&ZdVIp$leyux97)BbdgUl@L|INRk0}8 zq$e80W(cO%%5#y@pTFCy0_@l*pdMJ$YjyuFOij;x+TeJ@)3LKFN9@JFL~QseYUyy( zF^i87b$+<7yPP`-5%W4@xUD<)ouV`dq2jSe=Z@0dyPTsm_iktO{!XAjOpH_osrxl# zha-7h4@mU*vx4NKxQ#w#`~#T4JMip4K9E9NeE_zj1D2 ze}?_W|Kk1(uubgG&}{%QO$0TSo%L2x8sKm!Iunm1zfCDo7bnsXPF6!|B#f*GHwpcWM4=wXm@ia?P_<+{Iqf z!xx?m#;z?^h@WEI)ctX)v5%;vufj=RwtpZ>37=sOS+iF+kPob89JGYp$6+ED1 z`sXl(#rCUVVTGx}rJ~f>u(AEc8Z2>N zAkX9t`?vuRnh~ZRN4MB+YdH@)kwn$I{n|)`P-!K0hT#9TdbEn|v9vdi0D{=Bu-(#K zlGYT7LW$!89&ba!NpVJ!{whoMgX?RgK8N7-HY&lFL;>c0pvO>Gzp+EX@r@md?ZB@& zI$_^XZU=^9JFvfPIk=Zf!LdN41Gh8mQzP54ALu<4y!lX6=T@*C=#712y5iE(hl-~@ zO-avh1(VT=AOv?pS_Nr4*a?X3i#M~uRXJQJa**pX6*)F5vxefUax)lDb~cttcvNnt z5I4CmxIlV$A}^4wg?l4iv;?ewLOun+rFW_%^l&;4%?{oWYeElj5egnD+k^M+v=}Zq zET`HY{2pl&+k>O#_JBjDcC6ic&kiYT->Rs&ZmH)$clT*a{;mN~pp zzXLl)m!6d&rdj3aw-(GAUQm7gTD!9@5%jA_D<%YM(F>&{Sy6A!z>Zj zj6!zi(uX@CkaxOjM`XcC;$v`*clW!LFB{hZZ(+Nt>d-7R4;eIU?>^0aMB_eVVk%RBPctWczk-&=xHOH%EMEikeyi1!4V;8`T5Y`j^bt_CDz#qWr zWq7W9Zz{`@hczG!wXtztt=>Dcni=t_&aCBI!MXx$p&?($PT#C#`!;7Q-P1QI-F=&& zspH<9!=V03clV|ohV`d+TUyc_4pqZpCL}s^)h-T#A$!M7UHHZp z*#&3}2bkb%T^kPZ4RndL;qrXOWuDLA7T)kfJkQdHL$r^py!GMG0v`eG;^6=z?sCwF z_r(*31jGB|3EjToICUQ}2+txsi||zOR58ZmF~;NZjlmx=MoRu~b^W}(zHdU`7Rg|p z)g^70#A7c`5#(OB0Gi;Iu~EDJLNid>|NJm%|I;sN|C1wLh5b)YoBfYcv5r&&wm-sP z$F%W*xzLQVElrpK6WU zz?1Fz8y=2BciP)9leD*42egE}&F#!Y&90jP_z&R}D5+B_mUlb?AIcFJ za^!P40%v0c@)G<|JNP$P@K*%<0Ym=-+9s@kxHTtX)rh47P=~5@+4b$9evqOU0rl0X zP@iwD&nn?PM3}=7;s+DvaJBx=gm)K0TjI3XpA(+#@q0pa$}K7TC4GjlVTJY_E`htv z**-nh)UEniUi)wReBl*B(BOb=k`w;Qm5O};s%sfhOt?y(Yx!$98(RuD?s&Qki)|EO z0`tV*G~rLaR3DZDjwcWDtI_o?Eh|qSmIrBS9tBzqz#Fs-8RATYIFZNgF!WJ*ZbuaA zh;GLo7uce#!dj)c)`ja{vcNgzWg~sDC?T7lJqjBY+)=2F&4cb96dQ3A+s# zLg$@`^+552yfuN}mVS^n?s<9fl1u+B&zW6=d*n?U?$V#k5+CU5v_aO%^k8?MbJ}Gy zmlK?>!m)W-)B4Pe$PZs$_O$FS@*VwB!VQr76^9?^!Mw0Kwa&{qJo6eDj5R3$eWIn} z<2%}$4n4KKX^O=moMi~h(^tVrSeB=d+ni{5dLPzBxD%<4oi=f$`dl97d&0fX1mm4o zSf`Ax!!8Qw%M5T{6LvdhxWc|y@iX?!85Sz}nq3-@iphye zz6f(CP|3HPpFQB4F=6yd-9Wq1t4Ul5eh^n>mf-eN2y8wnVgg@m^L>nA#g7NGBa6fDrzY5|duR2;N9Z&InfHP0*1V zla4?poUmTE--Vdm zwKWA9yfkCny9lUvd0MoYJxfXR8W-QE(PR+@?;Pb z0k3fHCRg}s>;&f$V4x!V1r?EW!=fURrgTI_Tw!-p2B?S=KV$bpxkfHX--e2S)SqZN zZ&DEgT~Lurxag!%kz9-3Tl|@%9&(rjfA&wQM<%Whw4tsrohL{`OB2)spQIi@PIHaH zjzrQA;%fpQ@bRC*hp`{0;6n=W5crTF9s@AbIwl^KN#Y@(ZxIjfuTG#J>?j==Ncu4` zm45V*{tJSKhn7j=A;CiD1-rh1^Mcv9ZO=9Wf_R`4l6d%Wj$yUmBpz#%#DfigoZkFM zJdo!^;sJcdUn)QHuiCqx-w0;?r1tLfu7h;o1bg?AkLV)(P8=UgGx1ZL_bPMqHJ)|h{`V&CZfc?0E$TB4$g(KGHvWJlC$o3|nt-(mGSvRza9GuX6u--LI& zz55-kNZ7FSFx#aMysTaRW)v^&-J73Jv3H-2i1zmGWvrkIc+BIXYMGvGJIq2vMK79# z=mR5bQ(-nck!E(HA_4ddQ7G34w$`{8Khoa47;oGTga>M4h#}(^at&{}m{904b^{Bi ztP5%^10Q-HUwY57)p#JykF1=~n|=etg<-LgrZ_HcI8i;@pN413e01wG#^@u)syIJ# z1?w`xMPPDo9PbH_%<$K!mHv;X0VEM<(IDm#eCXfWzml_{?!tm7AZ};W%Eq@n*JowEZqo{62_IJX+&u z{g%3HukzfQl8U6C5^wH$^FF`g@EG_|Ig(MPG9k>+wiIiP{-`Um<20YP8Gc%PxtlxW zt3>2yy>s1&Zxf~Ff6j#Hck7K_B9uKzVsF?^{<UW0ZXVtkpcz#8;A=GLyzN2j|A-^1SLJ#BQlJ{lXL)%aMgjdtpzvkD`MuU;FS zrH{_W1}?|^%+`nZa2M9Qum!@+h&B-qcP}>nFa@7+U(tAg@Vom{d1n&ur2mg`IYNTRu1U!J~z1KH&uSCqv1a#kI_^qyz0zyoW^Mk^=Q zmm7IZk1*YxSYH+`kn77rJgxb7!x;LnO4_f2>iWW^y`WX!3*-&cNSX=N4a*_-h?~Fr z+Z68mG70tY%SB3+pNKBv6PJ9C_i3fKsoKr=AZ!Z5RIPNX3c3Pe28=l1d$(|(|HqKj zLZ4FHPaME~NHBJRfE4Q|ybKiL756Ff{vIK>E$oiOfrH4#=X*GMQlY#)<{fNJ1t4$# z0TmEP<>Hf$l*atrZjPHd7_8f$vjZ}a8W1@hv{vAn2pT$v&4gP=i7aXa7%2M<_G z2(>+8FLzjY`8u4kdRUIZhJg3MPU7nBxZ#zxph*H6gIr6{ZvaWDXdz_|KibQ%FpGr^CzVJX=!r+3j&i-@p+bQ6Fo5SIn zIYZ!y|86<}!%^A@6@Z32gArqt3UUpY32vyD)LhA0cScV~kZt|Im3D#i3$~yXF);oM z%;fWsykJX|yE)`a%FEy^rZ(8t`JuD%qa6q|3y-w6^1OGZFY!5=#eDlVo%n3x3q=a}w1r1R*L0m0(}f=>$jsg$lN6%S!uX5z1|=$h6p>Sjd6^6g;o zhtc(=5_})bGM_!)6mo_@&9xD+vWcW`}`zh2WTsy zm(XgTJ_^S9@FY`<4@@i^@eRwN;TA0cDg-6f)&Afkm5Lv6@WV5O>=A~;^1fm^{n3~1 z6Jj|!(7ptT)bKMRfT9fjQPK$|)Cu8~D+%wLN~lHD_9)KP5cd^7g0=9ESd$|e%92G@ zq*Y2qt4Q}qPP73pu*C?R2keIdV?IcPlOzJE0v8ojN6$q0cn}-$p!Pc+$B_w-Lo5U& zcHz}FDl!E<@9ztj3t9oL^yO=_Ybzb#?56nvqNMLMh3WEljF$oF{0a zDPOYI6X|c@U*+$%zRQL0Wc=M&7nZgY5tUN>`%W1ZOlw`)o>qAmVzK9bn^y9-&7V7oTVv-rC`#2FiZx0tOI{M}+Rjmh5) zK}380?nkV^;_u33diLl|79uLTFHzCVb`_0Cq#2Q@NC18ZB1qENS|cAnlE3SQH_6|5 zi-;kTzk8!6q0prVS-Av%_rqoQ(tG|0SD}*7w4gwd6Z~Bn=sLq*{BeT6tN)GU@2c_S zeDqdXhvRh3d@f`LIj(St1N&ESArJL`hm<6m3zEhgKj?J|Dd2unq(G?h_SF%}=J>u=1??~42E(T?nn+EH?U zY8x)JwPCKTKcj4zz3x3Hsr&3i-LYN*N?I8g5P%C8WKO6NAkoU4rjUX`;y!n=_yyTm zh*6;NgB5C>|169CH>&VN1yQ)g&%!vQ(21UdpM~-YvcR@kERX)dFdJRg!9(5O;<6T{+yblD zTs(Ym&}Z|p3WbjW@D1`@L&-0bG7I=t4dlDx5f`mO;t`YH`?Pvf{Q&O722}g#o)_&j z1P9uYpIM6oeH#d_Ea}nRu{_pAv`4$q2}T(W6R>U1Us(QY#(Qwqp&cfOXMYZ4biJF_ zO#7?LJh{i+`W@+>++*&G9&vhUFb_`HUmSLUof6(4wWHuS-G%q%Ov|3p*IjsL-ZbZo z9CzWPeW#zR>Ng|#mcaJdIql>X(uGgp-59)8;6mRT|Ap?sD51CVtA|j#doW7LR(|zM z@%|_bo^Qh&Y-$z)?#k?A0Ie2yLdBgAZY}VPaEjp5))?IebFRW=OP6lTDj7OIC0_51i*mkAoHm{8t25j8Dpaq9I5vQYu=EimFf% z&TiKldk2upA(NhI+K@sC4z`I?vgZ`PBI|ou*7sM`ms)fU7$VGoxc_tjx$H-9L0uc0 z#n+K0tO@@Fyp4s064-*T8(Qb7WY1znu+xK`6GTt(F)(A5w&U2R4susMS`>SdYHF52 zxi$=erA1LP@l;0verldd4Bi;AQdMWC3|>A!*x}$+rK4WGxDrNhmjQC>%sMJvyc0+0 zTO=vQ)5s|j+2v(mnW4Pqt2L|_On({DDqpf{u}q|^L(fd`Oh=$?gYX6s0;Vu#rGv~2X|#z$*Qcd%bR&Hs@Tvk$JIS^ziHKOfzJ z5Ww#2A^LP5!lZ?>q&IM!;PYrJf$AR3@FBT1P;^S$(&)`yK3bC_H&TCMrvP7Wy)W}H zOtV$RT|ZKjfnMDqTyw_;>z<{ELP79?Eg;~j9+`>eqJc4p{5{nul@TLzWgsLRoa&kn zUQ$w8TGI|*srv`$FW`s(Dr8_>l-V(Xn_cxsU^7!6+%`3`t&2-L<_dl`)fM^7L7coE zIJprx*%>&gm^e8~uh>PpXI+7Ti^e{OiER0HVC5?EEu|&IFkspSpir?64@y|TrqLlB z&LHE)ZI?*y&4=k}l|`?JElp}}{U}WqE+T*-;9Y!~hCXgX#X0~kBBB`##DqRLkYTGc znul)CuAg?Y!uEYTQ2N@;mv#}wqMzb}#wIwDi0no`Am@M=qbgw6QpMuT)bWDyTS5VLH`a zBUg-TvS6d=);$}5WmS6dOI~d~M>wIs1ZSl1P^I-~HebdzW33iJW1F)RZ_wp6!*UXD z(C0P7`X}B7k%7_xnD96B!ha6_`{KVp{y{`HJAjST z7i&lGqBR6};cR3_jxUpW$GEi8@nSH5GTxRCuFaL>0-OA;pjz8A2yzSzpngqwfP#Qb zScsAaoXK8g9u~j`q%pGo0u#n-vSU}-$`X^|bW(L^>|9I~kt!D>0pC@LuQl12D0Y8k zLRS`AT7q%l>A_XRf)?ZNz=kAzQ|HgjFX&Rus~z|0L+*H+=7d9qZ@u7jI#N8JlpNNa z)y*DdaUcr~9w7u=vCBeRv`vq7rGCSKx$*kI5yuuQQ7lJH*u;1Sk7hsCWaLp;mMA8$ z$0aLZ<#7Ili^)8|bxXND6Ac&k!nnE&8#{V`H&$MF5C>Pn6FiL4oCw1Ds&cy{cmKTU z5wArs@)D7Cp){mnnW_%G_@#ZsRgl99fUDtUB5_^CV6oC830}$MV);g?Araeos^JYk zlp`-!-8AT6T-<3E;^mS`eyCACc-bhv7USWCJ%Ez$S&aXM_@9gaazss`BrHF4n|zFs z58A$P#$Z3s*EhxW(%S9=>I-Juj1K@VtDWK3S^ir^XrS>1!r`b!wk1cFMzb1JktQGX z^@=a+9;?J zvFN{vW43g^`oY0?!755ipj3iYw77nbXCuh=bV^O=ka0xFo z#W~C3#-li8iDzT5wF{6M*R&;Keu5YjWnS!PCvugM^2l*%+*x)d@{|io#nQUUJTZ7j zK+QK+BUcJ}EC9>!G6`hCA#Jm^U@G2%+dFCD`|;xcIS5U#^|WH`xW0gp^51Ch=hJEE z=OF=+M2eDu9F@)KSZEH(JcXMO=|5kq1BPKmHeN-8pvegP$yjD0o4mj|xGf!b-lHCD zzWTzInoreU=co1&R{qX14ZN7&i+<6O(Uq5BlKPSECmp97ZKlg$y414Fn1RN@cnmtd zwpz@X^BBWP!iw59;^TDlV;??(%Ltw{zkjd<%l$rZj?krq8b`q)uE%hau15Brrq0}r zbTHgcZxc(lx_)Qn>hc|6X~B0s*I3$11A{efWm>#eN;DGdwHmtRcsX|9Ud02!z}0J2 z95l!~qXN&X$(ygc7(%NpxMHe}>TxOuml1Ahez+kPZvs}FjQzkS$Iv`6+t0^`ly|kC zANSB6L@Jnjms(;;;56*2&GXEY=$qe!vk~k5D@XvO{%}7_fB2u>f3+DW9Gh{%QKM}Z z_g{Uewrqt!3v$MTYUC@(8GH8oyVR|`3hFYJ6#`cAMxG0rH(>n^#IazDutdnR_S2UB z@I4nMkJlzDdsh{#!bR#ApMhQCM7J_|1wJnC&JShsz;ontNUnTdoG+f{{u~J+2b9V4 z>bYgX$LfhnX_I53OW9ICQ90TqL$G_xE3$_7_x>TgYZ%h2~j6_;Vn`^yrg%cG9@w^!I6o|W7Uot zy9L7&peA;$S5x;GyMACEEEx8X4N1{GzSPG5sr3S37hoPH zn>VU0LaYcOj>`VqN9qLohWgmg?V|Gb_p9AliV3zZECMsTP+>`i9%zV44g0dJ&I(@!kOY<%{^i`Nr!t$~c`f6HKA`grz(F>)5j^2b} zU#-)aivvA4N1_pZB5WGN94ix+ev^`xe)eI({R6i1ll_4Uu<1$McUSmN$D^IOCtDVJ z1i%))q*E*}E&7#0pDp+zUptG|4&W>9vy<>GNW%A$Bz(`4@D=yj0aAD>725!&Q^58w zFSUa$fgh;WiRYG(TdLpoG^shsBg9iR{c>izV^HndlgE{(~iqExY zpNP+YXW0Lb&&@#1Q{eN5e{Ba_fzP}`%#P3LM}W`ExY2gyuZ&@}wBd_A-N%PB)81sV z{D{}_z91z;^2sMb@)fB_4htYR8cTZepTck0pTVsEDEOgxRp(jYXTS<|IfA_u7?W8Xa4!j(}%SOQ-Rg*_* z1&_21|Kxm-W((LCCfRZr$u@vrl1$Tfgn&QVfRr1U@)!#N^E4Scb)}8uq7brSISV|@ zAC`i22iN1d8vzG*Al<9}v$Oy9cQdNEDvD5>H~KKu;iE=?6sE7+$_6!lKa94~VN@ z0Z%)rLuQhkS^I^R#L<<+(OOqzS0+6^mP2lo@QWcfd2^*bpzujV9&9x1L&Zlpxxc#t zu0+iJ-KEz4?m}!&#Qny?KJ;y#m-0dhakufw_jd5XCH8jLK2KeP-d8!G**U2i#~a+( zNp~1Z#cm5Pf6$|A8cZ&9Tx%@Yj7bN{W~?ZNaK`kCq0C-vA*RJYrO;mCOo$9dsS}mN zZU%pnN`r0ofA@l!Tbb~WaZ>&QJJVl3CtBatQG-HFCmvT3F=t`!*c-g`faHHxB-;D< z1QRy_uVQW2iMT2BRqXb{_egrZE=jLP!WsdnG30taNw05`0RwM01A5*g0`>}jGe}7S zy&9DIenGGE_=#<5={nMDZbjD%1Z@PSY}o`X1^yk~Dbs8rHAa#xpAyl4v4UuCA(rk2 zav|jgGiB*Ipsy^Ac+73^$1JdkKiKn524z0D3(q|WIEaiAfxp1ZI->N4c-c>sM$XA7 zQ>2ZZkaj)OZbe$Leg1^GNw3#)4sZp5w|Jhtr_gI`vZvH=F4Um7$7(yK)!3YY#uM}Y z)AxN_^m_2K|Ab!u0n!d3>&UkABbK~lYWI%h9p^&|LL31NnS6OJKOpbOm(L*u@_BKg zc%C5dAb;?GL*C(q;aNQCvVnS1_GO5QJM#{RlX)TVh@K$uNZC&$Tolygi_=kaxU8i9%B9PV14;E@{a3-y*~CMHhNr;q{o+8^!Q?n9$WX#zs+s%Hz3KD zxNYellO#D{k!060Dmz(Q>P?&;WEirP1f4T{r;ELacjiha6@(5dZOZKDk(k(;LXVT) z90WbS^#ppHdY}BA|8#$CiKN;Vq>^Oz{ri-4+A-Bd&ci$37fy%iqCB3~q`) zs#E(rfLhXB-O^l+6>Ko-7yS}95=e<7+jfHN3iYMVznNd~-bhVer>Y%<8gwty669G>l0NPQoZfMKW z0xK@2@~7rmh|8rSty%faTBIqO8KG~-PMP&|fSE48oboF>Gji{$PhMJ)5s97${eq(3 za~Ah-hDSPtl=WSVRoeR9j+V&otoT}O`dq#Ed2MYZo~13L*HLYH{mkv6LO4?|o)5Kn zi|opZH)w%*@V$;Q*t7SnnilEB^I6kAZEe%u%(dEK)^r%H+M6!FoboF>bM3ur&6VHp;_zv%EVVqM(fQU|t~ zoycR}0)YF%S1-V*n}<7825@8cGWI7NAD(#QKC?RG3K6-V}lFJqbd0v46K-mSmD(pjE&xT)Z~reGZ_(_bgCF+OX_gotWcHcQ3%t>Mq)fb>n# z@eR*FtWdiRPsf|kybbdlh|0bKqEX)X0Qx58JBnk`&@$m+ppn*%wEO`2x(R)~Ce(Y8 z%)^`Ego+HVau%KH2j7oE!ygvpF!_3HtV_c^kjbWPb^@LBuvyAq@yhaT3c|`$McH%0 zlMyHzOL41g?7etHV`1AL$AzOpL8frIGV4p|)}RKKXoS#AymL5>hw;AH0j($>kO|rz z(M=Gmc~i)j@hqreUFa?(N7vKpgq9EA&}|03#VJ7|^6JYml$6?%w)QV(DrAp z#vPWZ#H5`xc#E+x=~ANrX8*fz$IXq%;R`QQ`OshBmJU{94ro`7tzc2Lz9aQGKHsP#A* zZA)|-v-$MjBxW;buAFYr4$#EBOHh8%->1P*!DT^J6{)Z*ccojOzSNal=hpA8cIB>+ z6Yo{@SE$Gd>XK0X1+!>8iB2X|v)_@E?k^ooI{SVHlk3~)@0L@4myp-RN)JCgx>)>; zTOmvk!_c3d7^Wk%kd_MARlk$5D@>AS?>YU z0sH`<3bbDAh;_oh?V*=lCqJPtAY=oFQu?@z(_`gPK17Z3z*_;sO>jQjS4o;;iOq#zWD0)DaeKzzstzdk>Q%To1gB^i-p6%&Wngz?#@*bwLJ4x_<#vN z3d&yr9luNrtt+^z96G!B-(Bw3{vjNUX)lt4c581_g(6UOTLghgcJ?%kW2*6w^k z(sLvU2O5nZVS+$axe@-pk2}5bI@17F3Dp9tqGRD18G8Bi(QUA)3E=Pf_yAb2Ct!$E zo|ZP`6E=MIc#Hz2{sq-Sq?rMsDfaf=6h0|JI!gxhIo}NEd9euCTaAlFO?f|Ln$NG~ z;840bZzb>HL>z^a|1TE&^KeHI>@ zknYzN(FZ@qvD-qOGeF#E(dUG-3A8>^j{o23|r;r5W(Z!k&ut_d!hA>Z5zKASl_T<<2TR5c?LYvJY)X{ zG}e`hJJ3gzkt59cAP=_65BQR02a3hJC^s-#yqjhI6n(Ro`L0*nq*R)&)!ho)hTIiXVV|Xf_ zMP{m3!Bx9&r8ACjTGc7DHH5na>uQtt)9e3f!cY>#<`YwBRA2f4?IUbw(6xc@oBB`T zVWxhJGJ6nPslWD#kQoKrNO%38sx2RCHIbdLm24Ka-OcKomKv3+yn>6EjUS`Aog7=O z=I+NRXw7ad;)+*$^{iE?t$v*T2ZZ%I;POr2(K&3vBbJw9XgdMenuZm7IdBwvmMg&HKps-mi2b%Yz;h>keAx@A>hK`wUtTW)cbc3IQ7xH(IvWn zioeO9W@yfib{13z5931lw!jrou*L!%ACt|9TeA>xR7AjivW)1(+tAxAe~H?DT!AH< z24`1eyy4fxM|djq(cr-ZVg2KEc!hS!sz5$ogfH(rAs7zUnwbCQn?xP|5aN9-OKXKT zS}U~C>frZd8%EM}@0~J|mYw8pFo~5VS=}8GET&aZwFLRChtv(_;hJcrf}0YocK2(E z)oyO=jO2QU?Mby<)aG{*i<%mqUS%9Qnh8$GzPb%RacQGn7dAv0CtKGrcVQF>Pbl2f zRA6blq74To-)Lz|wvQ{=w_e|-!#=DPRxt~4T_Swtae)ggf+3VqX`lu1IyzVpQ<4-Y z#kq=fWsbE(>Z)zUx|S+4>er6nw&|;l5fUWk?Lc3xL)x(sg92iEHI5ZQwx?Iq%L>L# z3H=iFQjS#3&vw?0*h!;D#wTgyzT(DuD78@a_;c~yV!Vr|(TtyH))8(j>x_-~hz(e0yflx-PF5gUy|)o= z%Rg^Jf~7kT7!Lg*h!UZUNuisae@hn?D9>$Df)Nb1v{`yNj)Sm|B73^b+yOQ(L))Bm z=sMNa88?mtTV0)>$P+Tl&adT>!GpzS$oW)pVIDyl%rq9XG- z$b9xUpMA||j`{3mK84f`7OrEoDdVxD_!yp#rewcWgjfLHs6Gz1>;g&ut?e8^vIya1 z$7Bwu&;?gwk1xFVE1?5q$$o|Vp0o)s+2D4|dU9in#0ja%jFB^Vn1`}~)DUi^DLPdHy*x`Kd4zW?kIGnK|dQ-RMoR5K5lp^z$imtTWDiyb3UI=z5gxHBE`0baRFd6 zfZa;PeB`#cq7{-W!k$Q(O_xrRD-!#e6n^MqbMn@g-hTo=beWAGf`cn%Hr)eZ!T(sC znVa0u_HV-tiDq(>*Nz*qwN{*$N2z2>hXD4DTb_g^Qf2!bz zWZS^-z`yrv;={%dU76&EaQ-CuA)G(qpd9=VvDD_@`&+X^6`0DHvfqc}vX$^&P=)ho zOyB7VXVewbh{fP>c%7M>tN)~GxhTws_8d59E^?v$nTXgM+1q92ZY~heMiMK;HQ^q# zbpqv|+PR90MCVE_5uJszZ<}*GB&i*ih0?a7OWDEQVx53ZX@u*gCN&DmliYY#B*sdCFwi z2|Q~txJ{cF{BRWEGGqpx0K4KoGaTJrAsZDS*9>L9AM#Plt%w8Ki zGkqiOYgQ_P#13h?1wCTi&fTSK@S6h= z$gA24Ad-=`TP*Y$6bihbzR?wocM0@FoV4CzTsh-VCd@Lc(R|^l#(0)EUfG%s<1JWP zbt=0G0%FD{WN0JJTrsF<7Gsxg%X>HSLWl-KZY@en`%YTuC1h19=0ok^ zp&F^AJ}9I@c`GOz8oMGJf&ZTn48<|g+6#tO)7)N+gS+qu2E~5~JRoSeCJLO)aXfxE0-N~%5dT%bw36A16B?6=Ovs|N%|vR#7%T?=pZ&zabtH~ zg8tCWgBkiDLfg|Hu5J(R<4!7(>}QAD)1NsLHsW%@q|s;%3D2Me;jtTy#*K?mnzCfV z#^BLZqtUpLi5L_`%7I1;XIMtRuQG9IWrFDRC(%KqxzURoA2f?Rv9m3jlgPR6OR(Ns zaNa`MmfKcxuofOzNk7ePEa}`D!Y5|Ht+|-e6rPZ!lgI zD7Wu>YS1M;#GV({00=Od`;iuY9-rn$Xfc3GU25&PwpAAqv9l7uik+T#g*mRA{KPs; z{tLDJII+Q$#{?h5X+Mr=k2p^Ki-^H@*-~7}6mDAz2UA>Q3mEh=rZHSACCa#z0XepS z(-t5=u?2kn3@1BBI?ey+;05fXzch8OTl{A```7O>-$mCf6Ig4$V^#;Ar5gjN%fyZG z=oIHVrf2G0XNsOo@k6G-tiCE{o8lMaBZa>+G!J_+cAHT zJV~EqJlrG??y~BEGt&I&1@Raap+PcRkx=OGs^Jm6@ZPchAI{@I|H04DC#I+3kpC=k z%nb!-9{HH!hP|=Vtv4K58uwu?N;Zqa9O!QBGUnAE;NlL)&$!2}RjJ_dFwd4E?5(Z6 z^xy0Vt^)(~AcUib{1>#3#|_7DyCwYBT|oUTtsyl(C8>o42o4YZ3V{0<3AzprDm{N?S+Px{M4!GTv|nRTA`&>5)L!JZy4 zdCxr9-)r3U1xA>RR{Hj|6xy0I5XX(-#%j z1>WPtE!auU8cK_v3nEEe@cP2~H@p+bC^5sbqwJOWF}1#|{ta(0pd*f#=~jWh zmpY=wQUFWyEqFxpqRzH1{fWBQR7L_N)x^jDB2CfzeztpJ`ZjHkF=so_PEN_UriGeB z&kRa#vn=uN|54tJqS>M2iHx|XRG%HsEnVuj-HFT>eBdoIU+>1}8j&-nfI{WQuJBx_ zZ;pQ|3=fICcnJoMlw&9y)bx7KNQ>yoc787wL(++sOrSbJxQ*f`|AKxyS)|>&ZCf zaN~>2{Q2i?Wjh=B!zyWF!Fl_m$RBd*xgT`k=0c1*w?uS!@)TifoOEGX7{sN&`oyB?{lXGn84YvXKxT6-NpFfy>YbmVa#5@wO z4lnPuut&TK`T~5nTu~COK;7ehA!e`IMMo!>3an?P{-pGZ-|z`bb-+>->lt;~IuTGM z#id$ZN93`rU=rjMb(czeSZ}Lsqy&xoGw~4!T`{Yq;R`ej-<0@sGz`y_a1G()rB%L` z`3f2pgY>wxDt!#WbRYRA@}IN_qSUa%3YF%z+5WX;CQ z>2Xo&8{h|UKo@-Gh;{vl+jlcyxnlC!&I-cMp{jPgJ$yoI>|}i0*B=$sJN86$#E+m2 z*W!g>{M)TqMs?OIZ#IF2=%aH~XqDO%hSAHkR6 z^jO~G{iDTe;^*<+GkviXUmqacD}5fN#6JTCAfHc=&%jr!OniC(Z#~k-@V^9;W`3-^ z+0Igds6sgN4g%^}rCH7)#G3sB1`R7VSpzm!<2)kVgn&18ZbnNJDQ}S}z$hO>MJkj& zk}oGdNqYHy36msD4>Lvh3>LqLDFsEGKmy&Z{qkj_yn^p@!pq^q?B;8*@nA2j=}XFM zPso~sMKmgTk#Z3E><#>rtYM@!VhZ;A^n+y(c|5x&BF@Xko(qdW3?RpY2rlt=E!%&5 zs0l;Iw2IALiASv58b4XW$zYWT5~+7`n;(dsPl_spwOvdouM-0rfd9B#6#V;^g^CaO zCU(_g6WffV4NtkO1kQ1CbT!+00Iw)Y;E!U~ z4xkNtG#5RqTCy1aY+l!}6MHl-0`tgcFY1`mH{`6vG;B^lTyx`%1GwciIED#!oXYHSj1!$6nTES@E6>O%b$XH65=_l{N~luGpJd;WzT0nADb?4|)|f z_}fhie)f-Rx(YVN&P=d~73SkC2%RGbOlLSYx7n^De(z`7f$|yoq!%5MbbK~xy(Im7 z_IsNT^`@AU{*9B`1&9AuB;T-|Y*J2c;jJS3_BGyehp8i(G21QO_Ah1|8XoS9U%=r? z_O336D+7!N;d05;W{+Y|(>&0-x8@IQx;yyC6u!@XsQym#^du7K-zz^nD}a__?gg~# zh`-yHKNBa^#Zj zeeumz*oQMDr61dcqy&K}{E9wHyS57X4fk&!)|Uc$@-un)%|B)3H{8E1kl(2MRW5sd zL~8Px8}rm8SG2^O?=89ex4r|;R_}LP3~d&Z`?sr&cw7d<%TZ`5_IN^<^V!oWGn%}v zwflO`xvhs-G~LY6msd*v3zV+*#0&1@-YDsF@{q&b$Ibe3zp77B;Z@uVL=TrM7CZaBEafQri25!_k*Rey8*$so478kG|xq z_ufl%_3A_D%OCvlW73zDRY+f6sC1;EFVEAkp)b$Uu%R!{APjvex5}SnT>ci~a?f=I z#HFleLtK6ZBmqA7yU>#g_jdvJ`Q563p1k}Jda}AJJ=u8h;%>ww){8%cn4}|Jh)K~_ zXBz*z5tB^0A>_=V#rq9#{h`DpnLdb^6p*|rU(WjzlajEOgohH7^5s(nMRXx1`6$F> zFTTGYF-ekc#N@N2&JmO1qvKFwvVmCAWa-J_1@z=O1@z?U1@vV9EIkRYdGabmi?tt2 zN%p$73ndAVScwhPYA8usow^sW7fO<_kq8{WS@!JEbQ(>4KprrID&Jo24gpA^wo0(38h}^<&VJU+79t zp4x?;l=az#n0)oah)Egv!xNLb%}mV^lk~tRCJ8}I@;^&V5`_2f6O+6cVp0-xB_@gS zzQp#HB_<`Q5|dq$DlsWZ&yl1`Ov?8>F)81knVTgh5B>H#`KH9Aq&(y9EHNo>{dvog zgUhG@;_ExtbR#C&sJao8WKDG^CL1I-8({q=_SpIS>qb9x-T%0MjVu*~N9CV43gfNN zY9&T@{-4=zn0~d<#&}C-yY3g(Pt{KM>ADK81SQDfluWuzBi6O(^L-P zd*j{i)2liB*u}PctEhK(H%OCJ)GySK_ua7#eRz`g^rBwD;%A_W7osMN(qVXs9PD+K zzb6B4J+i}2PHIh;Vh?G?BIazN*Y>o`8D1I1T#Xzq7@wjBcpj}nU`kLpkTc~+3&a>k2LK&X7Q*@T$ znOrJ2AH7>P3fd5^P{JlP&P}rlxa`Jzy^Hv~IQgv7WL#|HyfRae$?XfhGC6N7I5ag2}qx!QmCv^#hVu5Ex%wQh!wPRzk7rGAk+C493~QMybUT(>00>|$jS2y zKQ-LNCUc5z06BbE`28BbXIA>{3aqSer1R2ruWp6FQfcLva$697(|R&gvsMW_7HmV# z;`7Pi%pWHIlNa%yDXit8gLEs>)q@mzKM~%>=a8^AYM9Mk{P2W_pH*$Oj3*QvepPje z)e<3yvV-Ig4vzvJEuSUE)pCP@Xmy3v@+pmw>LjAlYU!sD(h7;_YqgYUAqs@PqV#eB z3vU_&g=)L6npj&!oV+V&@Kn&?si2X)lI~Kv69^x_?+&1={GlwE2Hj>$g^#%xc@nJ;U^HB7)Xt}plMy?2>S3Yf+kFMpqr`C zi=dcBf_d7QR%uKv`uGoOh`8~tVxJ%w-)5Sm7b z?TH5d%QUT4(?IW!1-|(kG9{NvChuFzp>_q?=O$;cY-s&W@waI4h-ef(Y-g~WkcACq zY0Y95K`W<7fnD)Jd*W8VH3jScWo~i6w~ig5AfPGijH{T zU*r9JjSQT`D*l=yv7?CKw!O@Jo%>n5IK+nKpzmYeoe`R{hAQxXY--WLu9jMUY~rHs zVgmw!cq6u8VHbP z8Qsr!>-Q!3ezSAR&3IG+GM%DV?doejy{h*flOG4jI#KY2nu#~Ctv6E*=;M=2pgW`4 zyK$X>A|>V>q0@yl;-sF&@4^)kcLHG4x5GTgcYt3cVthwsHh}LU69aeF1ZuK@!P&qt zG1p;)n(<#{yZ#B)#@*v&W|$CWQf85vGhP{53q3Ocg#r@6z~+z@-Gh`t-ay3y9ihOA zpcQ5K^(`1!6?S%zs?&W?WSjEF zVsR;!+s}Pi4h34RxyP8DPkQXMH}2|OljNm-NiFA^0fWDvE5{-lY+5M#>L1t|$p? zivQT3R=2L9uKdkV;CZMz8Ff=`Vl(#QZ8#<@9Lw~~t|p+V1w25s5f)-!gV#_u71 zQ)WeI&q6fJlcS4G2E9p* zBdut82a(blO399+UA}_Io?TPg<@g9(iE}u=%3OydyY={MXCA|WCxgi_POP7_ zT8`44NKK;*PZ+$2i5h*kTH4?r;M!~6kiB@U6(|unj^9Gm{mW_@3W<$>h1Uhk8nUlr zd0n`yHTzo2>!M{vjJqaACvw7)WwqJY(|B!NCKsgi>#@9cENk^&xhPL4uM=cD2>v^5 ztlJD+kyX@*x+7v^4M(nj@t*W4z(?V!E2|JAVo0kPQKQI=t)?R=*8!}S3c?ltaxw12 zzvnG%73sq+yanox9dYZpLuoq|ojMWC&odP?0RI`OUbZ+ZWA#V@$ty(Tfg-KhzoFZO zUSw@I_VzqPs_UoB<1SL6C)qt#;tKL+R&qJs&J^l9s!_H5y{F!2yW^kaw#r1TRipjj z!LUwMkt2&N4H^qg#~X4PuhLpU^C4f0qmf0CEL zJ66j}1fbbv&n%$XxpN{r3Yu*k^4t(7s!O*=I1~^4qALq(9tFNH3yM2jn1lY!ysq-SuKXvs zlJ~8AfBmt;UV7^5ce9&iXe%)ipvZHg+QR}wa`cVjT255NcCIm`FQ*UagD{?NCoBOl zgx4_fmrxmvt7-~hE_Xid3J>r%WE7pnm7NqoO$?*NA&}v%5oZlL7q;~3xGwu=7-KIp z?#HK9o%@ZdsFSwa(p6!cjs@K@A<^=3$6O@J{5;Q!xL2N!vt#;Mw84%|>P z<1<~)=dAx5We`vfIGuZc3nR&^%TnK^t6BL$g40_jexjd_0a!>g@Bp2TD}Rvs!^5ej z|IDGm;Z$SIJUi%KRs+JAag?@C(O-N^dT=PP%}SPl*t+ab*~b6guR!XTcBGHX)i?v? z6o zBpU`NWJc9KtJ~o_>P!O(fpElVWVsxxA!sB_)QtP#|9D0J2ApNW3((0B_!0O(OGc(x z+!+d%KYCtXUS#}{GmVwez!0We^m*4I@30bDqKm!@Rn{YOARR zN>9f(h)wQC#aL(Y+OT_9HC)Z0dudHE690G1^~eoD_pTaj=0)7Qsu&WXENFHU7KGd} z74C!b9(cZfqEN6QSG}l(0~_jXXxqLacTz(*u%&*u`?YgNVAfs`qP)E2hoK`Z3lg7e1l6{mx(E{fqMYv>Va=cQwQ#jIy=>bA#+$; z6u6?Q{-~M92S!)d_nvu7U}VjVK3Qkse@q{PzMearvf}I0<%WLb022-Dj13r0zGG+h z9ZtTAnSF+n5A&xXS&=)lCO*q$t95J3GMy1zQRduD8*-6f8wMA^xvgp0Zk8!s2 zXYIs-^~q)>haz;tjD9v|fxEyf=OWe@hD~1B)4}wL!SwDlb zFp-eESc(k%#frXc{Jy!1WqyKzJ)9<><#kpmo42hYXHr98gEeM@GoguEI8VF-i@TY+ zg4P)PY`0`*>!uHzEi?XRzWST_s+9RUGCyCiA^h2biJBFQU7a*cGc9)kd71>5?sBeGx!-9;5p9iHAO z&?hp)h8ZT5!eXyrmG;9VB0n2xx>-nUNcR$#GPj1<;FZn2JF#(l+xrq5(|-UMbt5(k z@JCm8$kH1Vnpq$CgT8->pmKMv*tIKe3%L)8Wjk)(_na-Z?PTq@y;5bDuXn#I?~b@Z zUM^|L$`%%X3ZL5UlnQr7Yv5r0u#h_%lz!c+F#M!5L+(iM`Gk&eptJszkb5G>&3YIZ zQ^4qN*Y^pz)5}6!GfJK`unaXmTVP#)|rh zEh8wXldB~Sn2!lQ0kY#(^WId1Kxp`+`|7K?ReBXE>Q5Z5HZSXs4FxvW9~BC8)R%_> zE3M>w&P(hlVlXq}JRNEKbFav(Kk)*J_?4hMY11h8i8qw`c9zN}YrKr(Vw@zhB7HaG za62WvsFxz+4wLJmZOUkp9&Pz|(8L3@4y(e!z&g<4^Pt6r2z8{uz%xV$Qv9W>jE=xs zcqYz-MYI)u3+Qyh!?c(nrfBPy&~Ab&Lr(Y!MvHn%c0heD0K=FD8@_y^<$&5$)MHN3 zD5qqHH6NGlcr-U3P{FpjL!68+glt{l{R$WgxjGK1S&Uu*{6w%ZoN?B+z1nlujs~nG zhMc{aO9W^_PI}f(2}pO{C2ASbZBWaI$&>nrlNSwk8w6-V$?^SXCi@NuC!>RBhLWKH zGu1@nwyG#x!1C2V5#$s)l|+n3yf5s$&h+kx?0YUcaKG)C)aFE~LoKuida^k3{?E)f zh{spMa+N-QUv%ITFUYK<2NsNPK|U?eB#C5D)>&N=Z2WUWWY&AdU>PehnSCaGot?ab zl`&t2gCHwozHTtdD_9xIR3ny^F+aO9La_B({_q|gxxiai%d;9dV706xz=5GY>03 z_5H$b7;bfIeMQinR2F#NN|xm?3a2!gPw(mnPzXj1AQ@)l!VNqHp~wgl6oeupNKg=p z5J-sW=Lu_WNG3Ig#Q-5=4rcWOL&I-ru0Js}{3pT>tv^PvNuN+)ZGBl_iUrLe!;LMNbnx``AvjY!a<%S@0ZsM>(X*p%wk2Xhlqo8np5X=M%BUcr~M# zg&$NJ+ES452RFRl_{uc6y^TAk1>FXLfv~IASVGB@I>O1bh6G(T%Mwafp8yW&=nVoH z(mQ$83BB{^B8t`vHd&(ZvC7v1g=kk?rwBy*Qa8`NP4S0ZD`{{mLxET8YYR}xw)%73 zfS?YzCTfVia7+7Z|-v({Kf2-pXW7})&6Mtl2dyaOSw#l5T z99t)IXKohR3_7Na8|&V@zf2T32W}bdn8g10VT5&mnMg!W!Ouc7%D18#yot?Ep38qL zFuR-cSM%tB4{VcRPoF5~mBP#voBfC}R}e;-7<3ha;_mr^EN*ZnHDl`^sGkJXPm)8P zf%-{6z2Z<}nP@$63)F)w>OMKUor{Pen&6)FB&; z_%rL|x@qG!E9kbU-MQpRZOK?$Z`>|~j*PbiUEew#Uj;=noePuoJ$gGc9w4|N87k(G zk9Lq7tbjaf5jhJ)$im_SfeH6piHG!c!h+%rfx3BCVgau~+MW>p2z-Ji!9WC!08!-% zayn~iAmB`T7$8*>PCk&niS3L~^B$Z@hnzQkf6`XtX<)B9H}(|Z@Pb^i8;Fr$lZ|#Y zu-v)R@D$*Rh3}q{RCq4?ylMysc~&k`6r1vtcqklS_DgNTecA-Gr-qu}WLxEbZf9LP zr9X|Sb8nU^{%j@0vTLf1e~hOQ6*&)zy%tjriv1EzV4LoSX z-1lgTuv`uO!A425khmiUCp{iU6sh=N**v}6Y z%Kd&4!3Fj4t>_q3@*BXfil68l4CaU4TDX=Uj1RwqodQvY1Vle!T+j(mWFwI7jV(9U z`5+L+4kRc&b#8vC3}%mFe}KiUdmzw;td0dz$quoEMKWfsJK;X|NRa#sPOD+gw1K+O zAj6%86UOri^t@B|6tw8GZbFtE_?zV_^f+ZSz*MW_y7PrluXWBlAoBFNg|J| z=<)}&QLw}nBk7CZIL-hM2OC$E$-0wucJGUz1vd<|*Ddkege?GB%Qlt++EP`?JBsN^ z%hyOItDWmtLZ|fO^-wfp&k#a{oW*em3ZSp#sb|w_IPxRtDXfC4pzGxvv9N&K{v&8+Y_>+tCXu-FTGf zMUK9O_+=^*bPAAjeX`=O0*#!WmNagsCC?sN_pnp9st_I-myW1AP}&d^OF7<~Kc!PH zcRt@l7zQ86cKZ$cG=?IMbSJDTU+ZkNlYNnspLS&3vsu}hTi4mNCU)GYBgdyJ)BP#D zOZodYjyjzWqa6N8Q)yffel* zYI&EPrQ6+>!txJy-W0B-GohIwhm+b65pJ=PUpl;FE12K8XO_rlUslPLLpnI~TzDdl zD*y>)R27!bh~H0(N^t%#HzlMV@5zd{Wms{kQqQEQ{zF>|va4>93|fPelmlLvzB-%d zIg3S*O9`AmH(GP&3D(C*agQ^$NvQ2e&F$d*Wm^vr@j|Zr2F1Yh?#68mhPIQ@EZ9Gs z2~V`W+PiVbso3EF!Qa=mqj%74meUYajh?2Rw}S2>a}ql1I7W9-@~Xg0XVQ-auZNP+ zVKbBP!b8q0VfTiLVBjUI zEI2)z^MMFI1Qv%xToO?Q(*mTgSy|{1K}8^av`-VrhR+~5XPP?a z>7esgo%1p$n>w*A{z{#b0!;~Bkt!z5X!kxXpev@|UCm9=Rw$wsQ%;de5ix`8TLsx; ziM#;@h#>rJ{PT}4j-KUA()oh-{!{uM1Yritthvj0vyG72PTmd$p^iPUU+fM&{xgETR)f73 z?-w%s_(FVv>m^6s%P8SWu|Ed$x%2gy<~$pm^+%Cc6u^tKw1hT>-5|RNDlXRup5&Z8 zYnPk_ii%*pce^_4BuvEUHzw%STYhg!Ss|20z3sPG`gNkB<*naI#FK2 zXV!!RGpg&$W>y6QlWJz1Q0LwvxH`Ta6Gi%$J3sja#UB^lT+Ni|mTD&fO1KMI)e$_!9$O7J&61U16saJcX!Z@R;y5Ve09yh zJx-$_C&v|<%lVF=COi3fH-yxW$utP50Dhy@!)zzI5`^T3z({@wl%$~w@xOx~;F1P# z($iS&r3=oa1#*LhnD+3)bVbEBbc59dnTx%sSG^z4qA#Wqc6)0P{keXgRoS#s8fsOx ztQ?@v`&LRTt;z>h4v^=Soz|7lTSxn`N1IrwKEBGB?pO#rGiOJPqvfiIxHUsS{NeV) zvU8NUUB-6SD%l(hxHiIH>O#Md(^JxnPTM^-n!KSbbt3)iw*GyTWHqhSt{?PnaOAu) zeek2gwZ4Adtapl`pq9wL*s*rwMb9uIk>Z$_k}Dw41Hr|`BZ`~&IE0Vw@U&}-V`(he zXZQ0FqSF>T+HQP8cNcP#;MMd*aTwrEEkhwkwH|ZnQ7e0#ND{U=m)OAU_%stK>Q{vu^$Y7mZTVokIE4%3@OzEPZP$BvW+PIBrWLBGS zY!X-(yJ<+PNWH&0vyuRE?=Q~mO<+n*{W#kl4K=?e)<1tf6?+!0M513{*<$F6lXw7n zrY-(fBrqV}GvX9QGB!xzWpDgcS({tE(_|S5sB0g^oMFDaL*OTZ?@0G6)bL{L`k>3< zXe<=f7s?9sDTa@%svj`(0%_{eGtU`*Ma_(Y^Rw-qC)XFYq|Xjq!KTqhtpb6$^=rFR zD0^@G_tE6nY4s%Z@*+v9BNTD|z$K5OIxc3E4B42TYBY0h1B>XQxvYx4p}H1@p8*mW zMu626s)n1>JK~~H*=;@L#&7%;_N><`8D+iT$bNpttTW_7 zay14)uG_+nQW6~A+iH}fVY~We)Cp-tC{Z|Leis#rhO>5%4R@c@P|E9R1 ztw*z6V<#cBP_}BjLtIBFuouFg*4M56Li=qU)D)2`#&1R4q03K~8N{GSyi@edf-YR* zFO)dz2W_?kyQjb$_^gdFsmaINNi^u3gzU@d2+|?hSX0kX5|C~+EhkVrhr6rhLv0gr zzm<Xj(DX zB+(19IrvE1=A43=;o%3@K?Fz(n5ItxXxU!G=g~e3Ao- zUq)z7ZCM!NqUkrpt=1zv4{=1v;}Ralt5q`-3gcJfc#XI?r9lnWQ2{@skwzv%)dT$T z3;wzUV9DQOgM(FFbW%{dqk#?Z>^GxGAy{*|h=u&9TVrhlB_a^di zjFJnzYd!KN={l@Oo){_`qZ||)A0auKQ&{K*(?v%V1<#C*D2W83Bg(8saadHBBv_Xm zKcb|z`OSlE4cmJ+tJL0lWP>Pyw{0t_bFUh~#9TE3By?3NjFziPp?u_@!-t?-Z(d6V zZQbxj@z$Ut@6Lq~1*K#tp3|gFIoR540!(-!t{u{f!eZOb zl4x;ybx?BY*+*_5ir%A$wl4?41i?u<$H4)?Igo$Nm)AsG46@oA@ceRHm3*wS2klnu zjiuTuf!d=nXS;8&Z7ppL7KN&7=3HJAsyRSL+r7hJE~;K@5ZC%lXK0QYq=V1+!{q}& z@ZjoT!z;Z*%?b|b+vX^Y`h&#)Z1x9CoCiPha6g5K^;3?=e3t-2KLLh*0u24iCJzyK z!_*uyak`6|-BMXpUJqC-!F;vNyGwdk_ii>fw%T52b!1|#fMtEc?T$EEhIAtb19BpD=A;a=~tSB4=fEc${w{e~MrqRMzVu7H@6ydXUxLw%)N5#I9WLy)XLs{otC06}{=h!Ikm1YOP21HXiIfxln((VZ|`M zHni=JznyKc7AW8}4VugQ7XJ$91;N)>j6?Tc0n|JBJ6Y)L^KbnG9kc;m3Zd+HhXSbg zZ3R#RnWde_Wf4d*R3kvifA@-tL7f}C;!gHWCs<)sj;yqf4pzHQ zFGm8Qozz0m0BA)HK#N8m7Ag(7mD$#P|HQFKSB|JD7q7&cg{d$UQ!8k66?wi>u5j7 zQE7nxX+Zvy1heO%9#Ap&vtCl)p<2U#NZj|No~N4R-i>yv=NSzsE;((NBufNri_S3W zOFJ8Pp4zsvcd%zt4~})e4E9Xu;SsKX&j;lIY5eNd0wSVS?z{q<^Ztm_JCd9UKbcl| zCsQ@1-QXwV*&Rtki!i`%obdwEu}5Yh?qcRP=ccB5|#>Dlh( zLc4RJ{eGyT-9L{q^!S5Cob(aq;Zu{TOgzvC#hF|0L8~&<&V8@SmiFDC5r#j=_-mNR zl6L(fN7w8Z37gs*rS|1&1xPwm$+BjI7J(ziIQ6^BP*i!BiVvrEzen2t-_yHe;F5kcdiQIv7XNYd?lFB? zE`JYt_m+l#5WU-c5A^O2_*byMccXXDZq7nypI;my=-`9t-Tku_Kz(|*=Ey7p`R_yT z*2qDJSo!5eGuazR1-H#_hbiaceUdIXYG^k0nAAq`GsOOJ_<_q=wozQ$e8&$PA z{M7PmAU3^X;fG_GKkU3=w2WS53Q+8T^zYGriLw<}Us8c7QJZTNA+!_VTo<%|lsjH@ zar<`o0rb;yp~_rnKrU333)SR8gL9!S`e|4`*iTRPyWyu$J&SPDqptiFyZurd5*^2a zm&);9p27-}r|@{>DW_C<%5h$vvYE)UQl3%g_HoXo6Gu6*cgs>Ul~(L>5mse8GdjJO z6|%N^&o&CU=I2Y`HFuujoCc@NK{%Aht%xb!Us>9A7N4r<;Vlua)PSxqHzooqB(c2t>X^BEFd6Aa9mYPjo6_~&)$KNnmO@$_g-!*<+*&*_hFZ%Kl z#NZ+?L6-`XJY~xB*JI%}*|qj<2st~%o5H}B=VW>~Jw7K#FMHsdcKPmb)1PfC)E04? zG;*vH+l7Rf3s^ELB8Ztf!5lhKO5yUATywl{pA;k4udKV*3+S}BD`=r1Kk$H+z-S_aT1#*{_UEs;a-Xn^{NG3Zts&+CedSsO+hp1LO zI8@gqZ?47zQrc<~yVb}sTf5G``rC;I8m8A4#n0mqKP+;lGNKrc=aDvb0C+P`wDHCx z^!wMpqQG4U-(&$~TVsEld?{@urpsb;u@!i^o_DG0Bw@3i%O;a8yYT*~uD6dYzj`H))#$GpTxLawY6*sOb2o91*+#S_(*Z%{v%?;^Q%$(aOd$E9M;&0c4Pg@O0yW>NTnou z?)(jNT6H}IEqJ9fWD;ezWPMqoK4Xe7g6<9qE|-16+;G$3%jeH4igKwfo7wHtkBqUB zQOSy7+#Qb&!ibI{JZ_eu`PFF;KYHeM+%dup9aje4lht(lBhrBJ!mHs~dv{jChdQbl z20;qCZ4z1?%W26 zDeLhD#<$z}W#LtMf(2%wm}E>y4nW0b07DO&5Y7pVu8~#3@4`(QdPrT}{fZah22E*8 zNHux>q02UCN85F8nMJg zTn6^Ku_#u{^~);%sz~fS8JGbGw??_#M-kLC5HazM!f{6!*d8ns0X!UcY^#evTV-~P zY5pzM$VQf1583eyP6&?IF@@!cVH(EFYT80;La3Es2vp#~n%znS0$jL{`}G#?JF*e9 z<4oir5b?|uh`Q-j>02W0!Zgf8FcmbgJzkv=4?ft*G4dCYnBYEqD&u((;=^(3tm9Q( zt9)8%Uas zeOv%rMkw}G&`&4Q;azkW2=AR)hewR5uNE{Q=)7b#-2*CMCL@hA0rw#=YzKHl5y-2R z8Z(PA{Hc#U43wvPf{~GS8Th^`ILSc#yzP|Oi>98#?`nQo{GWw)h4s8OCq~;!{E}8j z-O)95qG-387l3AfGXAj+Cz8*mB^qg?5Jig5GBeTSETwe!a{0pS@6HRh`^1|{7@>!q z_!Nok=id)H*RkIGtdY-&+i6Kn) zYq~qDQtJsId&aOzv#BUZK4%(Oabp)gz@oTbuXgpXu z`S*_sPKn(HqO3}f8q%7M@w*jWf--)1_;FUNjrINNOlv=uvbcKcvGV(!ULxc`NsnTr zgXKYlHtF-7_2gPlt~O_VH_PlaZI6zyEG^-qd2KLxSYJ>Moy`Rb{oc#pbcYn(kG zJ-N+rv4iSB3BWhhaQ)OH;y~3iH*7aGq2xdJC~7s>;N&=$Z~{;b5_`mce4KuvE9j(vRC%s-8PE1 zXc%Sp-%HqkiC)3}EBON*67Jxc`W;P>HN=dMWj-(WUB%sgv^tVZxqN1|%;OP7*Qn5v zTEsnnH&eQP^1l>XQQIuO!PfujcrJy#M^xC|3HUou2M^Z|aWs=3#fd zGmB+x`yO4qB3-KOalTOIyRDM3?T*iM|CaO8e^>kXUxvcvoPE}Z+sFUatv=?*lKSE8 zv$-;!ANo-2{Pz)6U@H+P_0 zGb8I=YU!<{6|2?LV$5gb3~tq-UipUie^e3_FFK3`RlVqgGKR_dPBa;w7jlzQr}y)T z0u;)?Q2NyXg-Ff|m>7F?EcHZlHJQ3FG2$mE|3V1Xad0p87M@=|^kjO|U(HYnXGyx9 z9iPN|&?aI%ra^{_EooYTm^DkEnDuP#SL5$VF2Qk!H=_PTCrUG&#nTix)yCn|>7rWI)uHH(eD#jx7>Omh)bC0KCuj5f7tP7z z{q)YiFe_=bxm(~}G*3U_X0nxE2RN=8#j{NOR?+V%d$g8isPEUvd*!1n3iHlz#;ONA z+M0c@RehjGiv)Be|6qBqd9*hBUM25?9~C75{az{W!yaw*-(!OW@$bVP=W2W4YPQF{ zcH8UXYw?fo_EA@(gT>V-lvKQuv7nQBUoAC_?C9-c#?gIHhV%NIf2ij`?<*#srT#`Y z;cs++`Wrn-{Eha}W18=7v?A|r)LGAQCTn|B_G0Hbj7rF5_KTf&bKXX;;9y?3X}l2s zRL1u?q-(K0a%Ol_T#br; znDkS2w@;Z4*R{e*6i=h*9sAvneMa1i4&r-h`Zh?P{Fl7*(JzYg(Thk@oE{_2M~@Tl zqUZA7GyOTL`((aH1^5a<(z_ePo^u!VLxvDly}a+yZxLY(`*M^JBN45f;P5>vFrD)~ zssMdqz8tX#D_$lJpYUiBrURykobORBVjmdCZwf>G zbTZR`3Y;v^F8-qSdOxNj2oI9se6*F0Kwrr%0J%I02yTwwBua4pg5h$&m*mx7FT9Rc z{W-H-c)dt9=FDp0^=x_d7Ynba$g96rctzt9b!oFyAS5pqs%hT~VzxH#{M34Albp0! zFu&Wc3#M-+xB7}V7@OGs-Eb@M9F2t}x>J|UR)dTeV9t6TzXofNx$ly-kDPN;Dl3aF z#=f3&E|ov;3ZqN-1E1Jmsi1%1x8(hmDw;F~-5#r@;Xc%RC$sfne-DLIX&AabsmEmw z+`elf&T&wI98CJ>_FHE|1dme%-a+a5)aiM@Z(Y2UZj?I!g^o&DQSs9a`@^RP<(>mN zaZ^!W&pRvqQ2$bZm6f=S)~0)KX!OmC=DJ`m!}@-tWT(jakrF_)UEL|-;`&5}gxU9f zlV0T=11l)~&vHp!@nJUUXAVg^%-aQlrH)6n4C6=2yY~z{b_~p>_9O3*$O>;ye;%b? znui#IXB*$=eVh91;gdS!pGM747!(CNA3V!kx_^;$Pzl%HgoJb^|I*tF&TBb&6B_q& z?O*NlLf!9=v~$aocVF!v^&Hiq7j5t_i{@7=tf!vV>uufZbtOzp-zON|x+Te{+@&p`n2OLaV4<;LCkb zvBmXLPBOHmEPd&$84czz|FlUpO;Sy2@D}HioDRyGCtM~wfaYJ*Pb8ff@o8)whAy^S zx+~R(2>RVC;($d=H`%8D^GnKQIUC|jI&S1ef+bPWO;uq=t#o*}P*4~{Hw^dp+r%%*y}n%$C{Sw2wnL#9U*tV5E?o_m|oqAhIa=|@ODV2 zTn*dqk?>7uehtl!)!WIE4e8qpM{Ox{XNJokJ8!U*t)o`w&wp<4Of);ym0h(GI}## z?#kcJ0@Xd6=q*4k@K^xU^Ta9VtNf1T^9B4K<5x+8YxxPv5_-?q_iw+<@NSdbHf_Ib zfIu=&NJN;HmnjDiyTP!rFB|NCsxhB@Huhyc8~d_a{p^gL$a(h~+nISIWW+SGP4lze zs@Y~Y$U`B0u8gJKK3g90N3hw1irQz(Dpfi)`Q3%)n>^%?WD7Jv`(`haP=iLcN{~Ok zFEJrj(04l}#+kcCAG5c~qkXo=L;l)l@8-X74cljnX1e^f&)&~}iL;&GD_w}W)o6|b zzdJUJjf_I^a`@ix z^LsVifxPguwtERhwZ={;fG5_HD-4+&f8h}R{u5DWcB?jfZU+xFv_B~o_<{)By2;xN zeIFECZ0|+51;ObLmbAGJ5pI>hU}8Fg?!(nGK+)ugMHR9<^X&;P1Bf!%yExeMQ3+YI z<(?+kZQqeHaBJ;rEAD0=&uka_Mh$#Q$#n;Lk%?6nh`h-3rGCXfPC3=BJcZQXXf^$S zpzWR@vCTetR01w$w;dq2}o_B&a!Ut7YocOoWe4bH0PHXI8*Zl`2J zQmp`qRFo1wFEv^s`40cTzFLt<{FIcOAaq>0VNte3R$(N-s9H@YOKa(k_rN#~*R)Ik zQ})%{PY>>W66ZfZqNx1%BI~B#|3cc-96kV;_m*q(%(k0_N{Gr;U`8oV;4+Pkxh4h5 z^f$1>SKUgST)tc*^LwCmQL zAtt}JbU<~1ttZ}g&&@9|15o+)$r{;gHT_i6-32nk}f(cMrBu$TxBmyE!19dT`Bb+erQs$>UGc{*Q<6A~R-SIf>HA0yf^7e>2Hkl;sudgd4;xvNwzVaI$E+Lyxu~P8?WH<+>va)bE`UUFF44CeQxX9X0H8O&y4{rNX^#ox9bK5RU92v|7(-Nv&GH$DD% zE)$PE$v&f`o!O&wSNxUiGpX>SY&uGfSWS15H+3`ra=Vh%biKq)^y8Ekv6?6tR=CFJ>l+;-HE-+Tq0K_!i0`(*+ zs=56=*C$m5ir|8FTpU%0rs`I*7)6sNC3a%RD7^iycyAj-R_R;93mWob^Oi#yDf9aA z@iczt@Vkg#2fvN{p69oX-)nDYG6(q`yEl`m;WwRMXkR9CCBGZ_&Ewmh#5M7|mtQ4u zYxzCL?~nW{_h&LE^ZPWv^ZC8PZ#TaK{7T;8VmZJ0{C>#qA%4GvvU{4}pZT4{0DKy= zG)*kTZ}Iy+zfVKk4B*4@cRwFCH$82`z2{x z`8~_e<5xKUa=!i_E#E(VXCL^`UFG{D0pfpzeE(-VvUPtf`Tqat@&4WRY@hS`6iBagRFnvLtYiP|2+v+t)0i&0t5tqtPT5txU0TjtRj5%W8=ghRYXut6J9J zzajq#)+}rF-$w9;qK=eH%5!)dX8ii`27fwOYux(rhF8C!tzP|%;28GKDME4?iHP+O z1|3A>u=GE+3LNyUEXFD4df_TLz0tt1ovD?RRzmW`?~J;aK*iLN|Em#rv0`>4&>6eJ zc2*bSAH$#O)iBNQlh7gM5Zi^H)SX{-DfB*EpVgu-+9^-?NtkRIt1PRO5L!s}j3O4Z zJ9vp~EL~=IJ4s}AQSJ9<_b~G-djB$4^GK}oOGJKteytN()8#_E1k`#Wol;1( zlEV8((KpiZZT#fp`~PYEt*^iMk;hZyS^uk#y48Dl)c*i|)bBj6DC}eEqfXLXA5kB* zP`)`)w@P&Zdkmz$ycUR;dr5MD)VwK@ z?;@E;r`>zvN3Wv`(XRcAk|>SAKHf}YhWm|qQ9k@!k|#~_59~szpX(<)Y08~4=LKD; zm1qMPo<}$(Zh@&(hI%sZcmbfIYQJ6X&MJg+;_3q%`@+tv?NrdF0 zQ*!9{#t4K=Sb*@x>s>9QQ2MovQn*^jqpEw>@#glM-NA!2mt}L#Sm3q8m6N!4ZL#pd zsARkWR4?tX|Z4@u#>4Uacbd6AX4n;GTG(O_hh?cDU4q#c*x$`LoRik>iom({5N2GWD_dM|t?t`2Nh4C@&gNV&yp#Vcq znSC&V9W?jqVIq3GUMv7r*3uqOkr- z8njlL!fHNQVJmd|_Y0d^DR-OMeDgQ7QUh5Yav|6){rAG8S8Kgr%e7QHG2#6?0W&Ax ziS$`QF}S7S!V+_pd%vzV#K3X?CX|a*ebV6*CP*lo-;+GqPV)O>j$Xh%TH|Yju_G%6 zmD?{xe*mh>=qENF=wUVeJMWDLVADNAV0kV7ynEOtrFr6&{uS@@m+X}Z{E^NH;5UDHL>#D8Dm~kKau0|wK>v9a==R(i8j3&Mjs6T6y(rPkfNPp!1v}e zP*AGD+C+P}lhzca2b)&y9$DC`(|qDwa$IO~w34G$a$t$BS4s{d2*DE~C^rQ>L;;4f zl5R~8YPX&eiVXTpL5Xff-~?&6BCvzHG*Um&tvlCr$-&TGX1Ya=ZrzekbZe1ZB$==D z;EY8yQkq6fKF1~ZB@qXs(`x^?HiL9l?vDT2`{$duE8-@D`p7UTbXR?o2HMGHJ^v=mld~ zSvZE$EcfrST>rIj?(@qvbCC?$R_}&?%`Vql4xex{31y6r8{r!i$kDShzK7bQ-Yv^o zV&wvhL}{%yd@2MACPES9-_TGC9Z$Z1H9&Ov>OhE0eY+Y>pj=uo5^MU3?DXo8>8(W3yZO0$JaWr?A%>cQIMI@)V-(h|d6x zb;%pDJOyxlZO#|YKJOKQeD~yH1=*Fr_=5|e+F>N@=OJtMlkybyd3OvDNO`cC0y#_1 zlj4QLuoigW{CM_A>5}0vpjJ}kjg#cbp>-4c6g?&?<+3g>A`bta3!`Vo7ev|)_V7JR z!;GJNNi=!J8<$8K4+@u}aQuyvH-pg+^p}3tEx5!zUk~?EdnEH@UEuA>Tb2)ydYcpn zaC7&J*y9vOwxkX_mmIwYat~?U9kG6Ml3W}SRoAUvxpaW%#X5uu?U<90M)>24ZoYIH zu8)qX&uG}_uZPK!-5q+_GAb)z8T8R%FJOU4sV91a|03CzVwtvyF<4;(X}t%H%YWFmr8RcNqyh` zmZ^6R&y<{XDO7OSGO0m%Hn;LcYJ$Jft;A?9P1H~0cWReZ<$ko;Op{iWv9@!-mj)I4 zMFDP|d`g|k^yqzVp>&`TQ3Qj~8jTpNEjGYTUJigDR^vMR4gdsqzgHkV>ue8ubcHsI zM$FX_t8AAy*{N`GJF+a@mz!3e0QBekNkg%{M8fv)YqBc+^cpNo|C*#ftxNjdCHeFk zEKI*!((mb8SpHk+d1cGjU^aaP?crw0R&Rx*7bI&u3Ut{&;NCL=RSb zjb23nR#hz}d%wI;h7eX&e2Wy;c#3XELPKpikuS0*Tufqv$g4o)#?Qi$KJD?(l3fy= zpo#W*V>sZ;lzd|@w8CNWy~q=URERjIM&x1ve+I=2q^$PZGFA(Z3VC2&<@>rQ+CVAH z(L{(c{2Am;B}h$ssaZ$^|42YGjs)I*VrirIM|rwemwf`_K|p(|&@NQ=-Fpq_&*iDz zd!PMmobk00N#Y5_zcIm6Sj)8PGQ8}aeO;SSNKZl`og);|9;>qS7>*U*+uwRyi0gxj z_>>A=-T|O^J*AsWlw0by$|6+;BHVBo!Xqm&2P9aU?P#RdArOnjnJU4~F&0T(aw=r! z^kLEDk7P$(R?9!{=jY43URz71VLIB$#~X-`I4?zJt(r%jCfVJZ>_^RfvfCRag(PNB z1W3+2fmZ2!gki@-xmc&+$nJ4cX>ZJPg=j#8!}8>Nu84=zW(+D-zokeD3zY4 zyJl05%vZbCC!_WDax+@p5`M*R(CQW@GDlBk`9Ft+&shxQA@KQ#?1qgW1fN$5k~#!F zClV|08ItEGhtKEEmGN!&_PGW=-{5%|_?(pbH4|3QU+{zTuJ=8eMQ5w5?$lfsuuL42 z)kIJR^G*uCUh(N6^x_qR$f?BoKa5^foQ^vwkeWeZDP5c9aC*>dKb{tWkpw#X_5Uwy zoj*udQ@ae(gdv82?}C3-cIy2?h-$fbw%XgJU?*nq$_|IGHNEZLE~lc*ICidVO8S|p*EMV(I&6Q9xEDh+Sxv>naB#?s1G}#DN@%6^ z$g#D}n`;|hDUO%oULai}jxwxAhSfH|)39CR-jKLy?4+1cJP01ZXADU*D<%5NmAJg& zrBz;PE9DbjUN**t>H4xBp``P0(VJK;m@2bUl*Th_pC;d6B4p{D4Fe z+{C&w@fVnh9kDx@xvWh8;fa;w^`v2S&I}HSe>2xeNl8&o zaCa?Kucm?Rutb(_dt$JkyFD?Cko+OcoCkJ^BrgRJ5=&4=G;8&NQ?*DxF-qTdQ8-om(ME`<0v6GRj2=Dj&U5_!4o$G+9plOSce*tTfZ9 zbtbALC8Oat$$DhIAw{etdda~9WMn0`E%fX zi-xA2)Y}K`-V65#a)j1_8vYj&rDn5}nXWcuPljX^!3=`x1|AG+;-TE+O_LZid71_Z z@4Hf0yWTFBA#PXq#WH!E8aF+f6IYv@Dy}v;-|$>=GQ&S5kuR>+3XKOYf4VxU&<}Rx zgROqBJ+X~Ye!3Q98A(jn0ugPVa)eIT0&j+%mWM`0=@dz09_vfFUQbQ1^co^WT)m`R z(>E|v66HnBAzQ;266SoW9dRBsolv1R1J){ukJkimc#ldZcj&!~a+4x?yz!_aD zI5impg9A}&5>K6!r2>ZD#?MGSyS&Mg=2TM4t~M~CXQcGk!MQA3tLauqZNrGju39UB zQzJnbf_Y^1`V3?FE*xVkaZ&C&hkd@M&Xfq1k#CndHV~wZ{TYq$0l^z94Ut<>(vbU` z%wY|OlwJ*qvNCcVf&SX`H@x4=2R%mLWRid2xNSdMI{bz=OX_roUfHcFCrPw{NpBV5 z)Y%63CCeD8=o)EP5fyQlxsnmI9(hof)ZUefuG47-v*Jdy=wP zxl-=l6~*z16gN<+V@LUtnQN``6_TZ1V+~44BPhunkQLt6%D0s||14Ntlin(-O*Qya z(MM~ZUnAwZiv?ehOxOR}y>-a6ibjedUb^K?HDSRg-q%f75z^Hr+)5f@1OK*{W~r6B zlZz$R8<|hx4Uw>0h>m?!Ok^S6Ma@Gh>OpdW=O1dep;LdA8%b^J2vhPxN%|+ksZR-+ z?W{~4ZJr;aru)w{H4QU0_32jACO$I~TGMo?=^-8XmD0FU-p#@N_p=O&bca6u%dRc` z4*An;F74iTOqz6^nMy5jkd*i*e<~;Y391U_`oO2j4y#ZmQ)eSZ1YwN|6DiiL*CF|~>3ZEKg=?C94Ou8sO!I9W6R-+T?|J?i{fwoAv?)X1 zIg4Htxw85hul$?@ZogeWNkDL>nrPScw&E;PM)p3H{WIfS&yT!VdU@uDc@c^5(E3z2*4 zr_8`nesZStwcShb?0SCk`N`(k8a3^U2u<6#x{C3AN~odi@s%q|A8>s2mZ(fe&aIE< z&#iAF+`$%Z?IGvZt&v&n`E%=9P4;-Vb1Re9DMy3JN0t};C1`!~(!o|uT_^IPF@vwS zW`;Yjw{C46bG>cdwNgmS^UNjo58`=GsR+sqzC6!cc`-R@jX3~kyanbA;0^Xi@>8_# zQ2q$~{K1n_OHRw4x6J$3;P3k9FXiWOI=GtS{Lnd{6#^sPI%K2F&9HxBZaDZTM^?g2 z(ka3%Zd}PA8}`m)a?lfY&DTxhR`074Pze*99fe)K))DD1R!F6b{lcbb7g+x@WX6rX zfGe+n(V@+uwncK>lG=o+yjM??BdD;zK8o}QqH$7YK{HF{(EgkAj|}%r`5{?sA1Tbo znYL(dHOLRvMn(HbAzvD7_bw@o>CB9)Ej%kpLTw+Q2#;>Rytaqcd_#M9x4iicVO(}+ z#@WqnLt2~G=oN&=MmJGLhqWF_OMX{h%!)7oQmdHTA6Ky@|wfsUnKdf{`UMbklwZzKYIO0XVi?% zKAmRi!-7^R;{h$BL;HY}YAz#e%Zk{f6K4E#^W8y~k2F4=Qg-dfX`jlE8ef|jao!eI zuQREctE2)LnR#0%4Bf8*+~5$UIMz(d#RyIWdSq|unGyey?MgM?>P3qWMw8cd2&P_t zI*{xR{o;>UV7xx6uW)=L!a zD(XXK>yZfyrJiysU=jR0P3I;ovRypwQGEKZWr{w+^jpP5G*d{UO~glB!*_rcs{_aN z3v)jlFqR3<@jvK$WhB5!dVl;a>3`x#SxL#Er+tnYkWrKwm?b54n*oI0ySykkkQsRH z2WAF7tFMQhfllvhG&4H`>q#gx@RVkd8Mw8IN`EmzQ00ty{#YP8(RBD%e?5wg&dR4? zy7Jk(f@pI%6?k}Eu&dzKWdpS}5pB&r@8|k@NNa>5X!SNz1g&9Nzv0ztEpK?AO<|^Q2VuG3+Jz{o3&VEF#Hc_LnAJ6 zf}Gy}W$#B|+-e&&4-`?lUBLu3oy|@4G zyZOG%IcGoDUVH7e*Is+=wY%WSuGuEC#H?gUN@EA{%$RRe_{Kyy*;f9=yl&#i7xVH# z`47bx(Mb)cm#Q2P+^9pJC2N-0Vb-?+WAzrSy1~G*lFxz}OR`BOn+rBF%epf88T3}6 zI{HiN2yGo&ZrnbX7n}$55C+Wny04(I18B3XKZ6*>PA=B@2&e*I68fhBLi`n;&cw#J zRf>dQb5+zGrL>u2YLxl&?f@ggg~_%?^AoWgrpGI-d~c{T*2$ew?(~Ga@0J9Nx2Dd7 zQj;s^A0dTEIvFqUpHmN4l+asV;mp^{{8N}8tdFnn6X6QbK3Trv|9iov9^d5*y6i8-MZ%ttRptoNO^*{AuRn~O~OZ--*m}PnR z^)pi9GZWk0&m;f9W=X5|MP)W?3xq^C$;^WdmD`+FOIR5${OjrMXlkqGXuqd$A&p<-9exVFT#I9Dx| z{c}XqZz#}*o{7zk)c%vS7OFxGa7|_n#5xY+iSDB)q5KpoOgiE#L?gZ%FyV`fV+tMw znIK#KgkDVMN?O+=7ySc2<`C6uxM0o2p-Z3p_lBAbelGrs+Km&*PZm|A*jd7at|4-}lw= zk+rTm0Y2KT|Br?L&iciAD%Bwp0Qg-)x8U8k5KmHX4&-+%Vn82fTOA}k3EWnmAzxQL zGAbvS8*uG%=;r)3^i7HCOVH0xfv*%Xr`PvfLW6)}-ey7;^X`xjiw{mKg4t@?t==8l zt$}8Z*FW4k-okS;VmaZ=tU*ex7nBQ>3Sou`MT4_`KGFEN5vXw5-^n^~8fKw9W&V&+ z!iWcU!e;5(Hc4dT%aKppj28-+_ITlI zd*kKjfbU;Guh3pm9Bu<+YCYw^mvNuM28eXrzrRwbtV637ko|D=GYd9H;i;=!!Ai{- z6n=sW&a!cC$mo|LfmP7LMb}a(MRA?8({J8`Ro5PCgRng+`#y5;Gb%ohTjwl;q|ju2 zf;zCy`HWKRSiJLj1D@0XVI8{xk_CmBUTy{*ck3HVc!{YYf1E3pA2I8yyC7p6*WT+# zsk+@zP#@VBXl_TUsd2)XUFC3$mkdtWRvsRGy}XHn)5qs|lqScOfH^NM^p}4S{0{GR zko?~sf2Tr4!+RVya8XRVFR*Aye_v*NeGqyjHMla?UtA9no>jJr1)X^(K(ZUOO5za8 z6(?_eJ-RxbpLVlic!Bjxce6AyIK2a>saawY{2DAjDX`MNmheX##&u7@j#fQrmbh7gBfPrU3)=GZ-K6u)*bswDEXZmU8C>C`FJfO8J!x`wdI( zYS$J)7_RejDL);IWT__CxDnKXJKi_^xQhZgNy1bK)C^Z zKLkWVtMz_bU{~Kt+iHiTiWvht1v~(APSC>@e$*NSGem5tBGjr>P`hgj2iY(-Q5UzT zk2L$WV8f94&`_>>E`(5tkWyJ74Ut(O%r^MOT|hH3hkHmd@n9TYXQz_QO93P3icQfq`yvzYH+8@L@Xk(1-XXkHxdZm^h%%?vF(Z?3TwOg?M9F zL*z_`VYj;oPoL=lSA|18x6kyKU?yG>RCytBioZ+$CAgnZx(>zYN&7>U_Y0(^lrvOs3D#v{v>}i1hB6YvR+t;JSloCnYRK8%S#=LZ?N+7!jr)e zxg71Jh5Pe*C}n=V7H)C*TtW%Ks3lMN;6FM8fBGUQKkBM)TBiZ z=W5}@E;;a(VQU!~dHYR72V zl>64=f73o}TD9N@YT@;`3T^!gv9A_hat!=iZ$&^9dsvMl@zOZKr#!j_T8$v_>CYY5 zLbXKs>dO5X72oTIvDF1&i6Q@T9hznB+a-oQQEWUQI8PMBiNyO)M`DMU{SB#3#Cb}P z2)stJpXllW~E}NOqCU+#Uf0$gqhS9Oh=qLY6a(%*`nz zBnu(-mHF3-ZGZuq`9LuRUBTrNSNe<_N(0&r?t z;{l{yNtSxHOuL>e)k&L+u<#w&+`y9w`N zX9C0O7QP!or^agLA~j06C$pO0$gupjC0EOmD>6z}GfLKE$)or{$v4PS&d4lzFvE<9 z?63-SSmb_E6Xs#4fs85&Gf)oV*hxlc#bgn5haIFZBEn_*S$2AEr~il~-!lUb8+Hid zkVTo3C1HpM2;+7}nq?1W&d|@vu)%E$G8wKveZMS0JW#?#GIhVq5^iN!coD0_4%S)% zQwMBJ<3Cz87A3?Yx8PR2dyDe*h@VI@%bx1m1}pU+a4oS6oxn`E%LbERvF?4Z= z<)C6@e@5y{4AFAkTVq7nCf)nEY?khwiLA3wZH<*^7J$qmAW-aveq{@B@X~=pRGhC9 z?~9tC|CBZTmb{XKhBd7gk!7zGw!y@FgyTvtppSS{dqH@|g|IuV$+C?56{HtD1GHSr zGGs#Df1xHD)LwAqhV%z4O{TBNO#dCG*JH*!c7t_^Op)(E#nI7ULN%Z*RK*DQ-MY$m z>jo^z*J`&uEjm(p<=uVCqheTKO6m3~P|ZRlbWU6+*Ai(qbN0q0_F1JBgmzNZ7f(Nywf-zk`T5FhRr|lIXiS zrpO#!laY!baaaPRF5eYkaK;m0+25s`0YM71!2k_;OctFD-A!G-jyM4&AlM<6$7O0J zRBqHKSzoU5INf9eC~sS!J^-blo5Yll;lNR0E)ow6*h)s4WmjaD`WJ+;70kg_%p&r{ zye(THzq4C01#%)L>U-B@HvtF+=)EN9O{ZlLPe4E==1uFvj#%gm4vvG{CZ)xUpJ=tBE!EWD{TS#WTm$< z9Ag;)TC_RWZrZ@_*D6v$$}%Z=@%SgpY!MbUoKzFOAnex&TnN~$jsP@qNdh(yqV?%g6FrU#0+CU~verT&^Q9a2 zuE2;{2WsFQFh+$1$&BlMO&{&%mY_8sMjve2!}y8wR^a0Rh<8QxY%!rNJE zn>_pmZ4zo}7Wgtgvi5I;z8)UFFTgQzcVY}Gs|NZwTBN=UmVb~nE8LJOZEY8BWA|`` zeM$Ww40`Bi#0nilSJ{UJ>8QXyB#BdkMb=+wu_V)_RDxcn$31Y2{{`ZuCG5vzM5poE zOBPy;M5Kd1;hBLi#~y<=o6>j+t?1aR224+yVOYENU>rjx+RUdR&i~;UU=8IiA|c%9 z@+*s45?zPKs`t^>l7GJB>IpN&?II(Mmqe(%$=*}*sc+p5>|Xm7Pcv4=Bh0*MxQk*L zR=~-M$_stwxMk4UrSJ>OzFg~hp>`dB6jx9B$d`--%6C=9eBoAiWmiaJuqbGHGCPL!$ z-_ElUk=xi%_|ec}72;TDTwK}?%HDGQCrJRj&jxt)NaMDB6)=};l52F)IUfR74$u~Q zC<8vbvzAKuFGgoAZ3{p9>)=t+NoIfTsa)#x7xt>qU(HAouLPKx{k6+l&fuFxGtghx zu^GO5}z8o}*j)@Roe2OwVHA->N> zOtT0Sh-tK5JcRX+ZNHl_{x$6K$-_|eNK;R$wzfj&%B|~wc`tTH62S=pb3!rf=;m_& z&E|hya7V2U1JL)aIb5#VJh1D`FUS89p;rRtqfi)r*PZx2-x4>A?YYeH@vr2!sLvTJBU`shH@7`98Qi6g+7%G9;;a+P{+HhhRQ!6V4z!L1f4m18Z{HUDCNVeDZ~mYI_r@mpjMIGOt9WxMw151aG~6F~ zKd4|vDN6KC@psq@lj~~AqQ*KRQd`%ot+lkf>YIGIo;4Mkt3@-$7HV+!9d0OM^{BP- z*MuPcJ0#~z-!0z0AE)jYGaf0#RLo)mV%|mq2I?Sdt+PJFX;_d4&su;yX5%5q<5Z~N zeegeWVH?r_${`Q=%}PNZxBHBrR*|sES4$cZCJ)Mtk5Gd@vKPLLNGF%f6eg1=ppWV` zWYF!A$kNKE;=e~pB#j;MS$Ip!6YbE)gFbU;iafIW19sb>j~&5zX?Q2S;E%KffwQGW ziHl(n)GmGO1bsX!;PbiGbI&x3MqWtKUj}{55ct5|mTjdfkVpo7R3A)FORKLj7K%@2 zN`hq7X7b*V-5$yxn4n_m`CC|IrOKxmi}~rw{p41u`CH6SV*Vzr;Ghd;DV*U&BGONs z^i6pV?@Hu5f@VZx#DESjEzRI#DgMLr1g$>v$CA4|lj1HQA1oP`#xH6^KKA~k-+Wlm z5AHM=+22>bCX0CX2;w;s>@0~#j$fEVoq&EktJ~!-ODlgJe=bEl|An^<{A0d@fm<>N zOGt9bUP6zi z`zys?%pc2<_)O+V6sRn1gUZbO4n8p+e1W8+Rk`%7FM{tAroII`nAuSlw!iK6E@YY0rSW195+M(zaC4EZ`}H9 zyt;uAycF;{pSe(cnBRd+Hwk7W2SQiC34(!J5b@d@FYtqKAan_)lROBo#nX@EfX7wV zsS9(euz7g3hLa450{z{D8~=I9CHlM3_i^x}MLW1j>)E2cc~Ac*SKa#FH#$Ji+W;jz zoJ*EXf22ze>@-SFQC;gXU)Fnc9&hnBsqle}GL`D1AaR^UsYjxC;`HqHGBE{-ze5J} z>N_|cMz`lxO>r0IyoGEqBEjV2#)=i!JdUaJ=yB@`NDa_{qx9r7ls@B^NafL+qt+=N zeS3KO)mWn%Phge-LXjK}k^XB;1Gl#DPMA z$hTqO65J7OM$6Y~2RCay*J=wgT8=B*<7j!e84t^rcUE0X*$}72k;_nFq7x^yD$ZJ2 z94fp0ZTq`O55P)?_9k2?n}2oVJP+(=^-pd7L^09m3+jN zqK}t{F2}1myf}QQqH?*0eo?CLP3TBh^t^$Xs>59 zc&)q?rpqrM5)JOxRdqEa`jY?sLmb^|X912HRNR~bp;xK?Q1ls08JhDTP~^I+rsNjp z)FBYIhP3_XEx<+qcT@)ogIIjJd||KKoe=xWJjxVmvPxl?A_O`tC?V4sfmPMZgQt7+ zPowcuwEXIo?hdY;#2K(hI@n|f_LPB`SZKmq0L39UM5edWD0tG19=%0S(5HfeG~52JP|=XJLB82EP9ip3(`!z^*a!Fn^h(8 zm)-x;KOyHxh`9g6eFxvZhk9fUN|yHSJ9zaIfROE*P&0b`d=j_JPjuhGaOCkDWw#>{ zEsJ5AFq^Brf1Pi_J=S|MfJJrSz>oP-c5Gh(9?~1(1t1A)u}F}*?_iQlKN~xYRe;4h zRb-Ps`47`x$-cOu-TMwUi3EGB$;@Pw1@5H|Dt+I<8L})ly4QLG?C=HZDZ)UwYrnlOsN zHYkVio_XKFWB8VeyM8uYM%nN-`3srm0s>R$z%Ft7G$^?3eFsmm0uEfJZ&pQHsTDgX zRnbZr2tc>roek*oC{F_Oyq(5|dN49m{v))OjvslR{vNz>iE5U;@G&Vw-gj{J5@Mla z@BtQH50;jTs(yJVzKm{9{SXgKE;l+k;ZS2E*U1Pw)xzpR-gi)n>Ir`_o~iLj`4Q`k zV`>#IYz&#57)s}Fc@*$~a{GG0fObqH%+O%g zm)o}<4HN=z=)we)+DQJE$e&DJaD3|F=D^R%v8^=%qj2$XNHg;Q7nBr+w_Y=HhU4SITd6FD15<4ynw z(}^a2nv3{&vp*a9k_cbYFyu4HRvj1Lm7+M54#FEkmrg$88Z;Nq9rYnlGeLeBQrs}4 zPe;qoyih#6j?(xyP3wuRS9q{C9@#{GFAO{1g;WOflC4nk!+!-*S|7r8#y{ZpO`&es zNMh?!Vk2lMakh0UOjAfz5=?Fax>%8&`+4RDfn7iY77+N=-)`Su+mf_AM0}2qw_tF{ z^)$aV_Uz_jxq7QBu zt%`Q2(fVk-7*21nZjM28g`?nh`VWDr^+u{cZTnj{s>0>ZLIOSptbdK;+&~biY)GLW zn=i&)5D!1~_lkqcmGH{|&1V$Av+v;!m8VfLgX)FBVWKajBBlCqBv5N{FK;zIAVEUM zkWSOOT&+Qr4|}eN0V3xeLN|agaV@#jFMvj6ptYnCSZxP}iI>~;yC$tgjksRVFk4Vc zZ9YVlu5D^a{0Y3?5_P1SP@IQT8npn*%S)9{$5(^6GYAhCNC%G&c(@uOx%`5QT-rto zkD|IMc0!F)=}`?9i$#pkO~(J#1&t06cq@6LV3fkK6G9H(kqRNrC5q5Rss0df*i|U= zSB4LFQf4#*Glt21JAFlTccH5PO^tr+miSTTe+u*2MrHoOGD26rMhX8_AaWqCKrd+Z z_4c>AedYevLM6$Oo^yGL_v^yvc;a}~V5BcJZ7troDf*4M}v7~v9lU;a9^ zW=L-9lA5Q9Pi6j+@F922yUJtz@l}JPSch@4thu(rjnnQ$hUfvClJq6fL}B~^kmgLa zHdx}WsA}k-*5E~%zc_pdrqQP$3TSxGQ~PQUqTx-ddT|ELr?0WT0~?}-3bGKBLI7Nj zHdJsB4{OHAvdD@Ax8Zp0g`_;-21Ed6(aUDu;s%wOpOoCHE6l>dY_5em5_(A<;8pnv zBn>OZd$C%3W1pI9;mK}NDfEh({y-^WO0`}ss1VHOfl{mngN0BpA3OjZWWAa*tWWK= zeLSUmkx4TilDr8(!2Xl0X?3!xR$!}jrNo-2_GQwf+PX}w`mmFyMtlKv@psV>=pSkY z-i$?Zmv<4zL4`7Zqka?^#7nC(|J81SP+BLJXlB3b(5VM}y+qF@GB z1H$J%IxYPbuUN~Tgx`+uLJzSeI7Gtj+_%zC6RrlprM5X>BTAegKSxGk`HCpHGdiU2KQS|RmBk`H!SXNz(4e2Ua&8!74XF3`WrO# zf^}6zoADShZ|i|sDcHkeqyGrn!zn2XC%FwAC0hsb@x^%Z1P82TgEP!}U^MVquveS% z{Q3td0OYaF8jr%Ty-8tP-x}H}N}P@o*`}#mNSIjv^^+1>YW!!l5B3xNH67a{`qVn% zk0lC4RIb(+l4PJ>GcHKJxRmA24f&F7aLsHVYx8JZwA*EYKCWRA(S_3aX$M{Zs=0B zqBnX5r|!@5>rf(B#ngfU2#z}OMnpmTWE(CpVp z?+)ky0AOu;gD3(l2dOUs>WJYKe*DI_v8`QEc_j*H6oTQtta#smVTHj<{5qC9S0mxo zlRil(nfYPJgQ1~RuScIk>E^A*t`zrku<{cfQ~@}v>oHp>k?9BqlF=|<`Br%T2!?Uk z)Ze!S0b0Zh=6?%N&67e{{iNsqb(=2=h$B3u*wU z!7~KZtUld8AlG^#wfX^A4)E|yCPnt_EPQ5ah{71XfLrC(md)fd^lX=G@%5xnlcbgZ zu3HYc{uxsghKrpJ1V0xfKBq=`oFI<;*ojF6r#YyUtc`$E5Fp5^E_gPH4dYu2mxDAuCPnH~8>z{K`N^-5RE#Oa z>*MEQ0S_*54mVAe$7%%edNFqI&T;v4n1KiCIi4_mA+y6r{5|9T^24F4&|@KtaFkhr zGEpZ+^*6zs#f2?_CP+-kl&k4C6(+i7_cz9;&ww9>qgPLQHgOh)F&cJ0fo90h0eWNN zbhbqFndxuD8fOPoymp792jDow`B0){ve|t;wb5tx+luZ5Yk)Xsje{@-87?l|K4nrw zqv+0yu^Ey@3#fR^T$@nbS&_I|By=LWjP(cU6wynQ+Q75HBOE2EhqCzQWH zj)n1(FeCBn-^8n!mjf`y!~P#e&h4`U}B4y{;4d(|oWP9XrSR5ON|X>F}d{ za)RY=6%|9&>WHz2y-~VRq=Wzzh^)oNPz`TA-(&gl=`)53gq&@c2MLgB zlMH`fEm`~op0?oc>lE1b&}>EQwV7ba}82<$l|y#MjENFICJKP?gml(U(Z@8m*~#(E=vqM0bAhh6KE0b$l<7 zcTZD5{|G>YD&HE5GOX_acW8|Yy%r=-F)Iuy1_ktCXVCT(z18$#0d@fUYImWI>^NGo4E zsk6F(TQhx=iF=ryp-6*=x9{71-%j_RQdo@!p(%xrZz$rkYc&QApO3E---s`@dh471gk+`vqY4nuC+-fyJGImj(2-zVP-=eQ}tW=jZbd`}Dk{ zV!54+so=&R`JnXewN~wQL=7M6RC!Yy`T;kG${SbUais#rj3-#P3dJ$*<;Bt#PmFlA z6i-N5Y9BoNs9ek&Du~2af$#nS^X7`st7$~!6lWk#;N}+$%0}F6OwUjt4iAC2nF3$P zA@Ic~@g<(Xmk0;G?D+n6y!a-*@csW2zKkCiqTPC!znbx5feSq>A9QgzzTVyej(Jx} zvh_A$#sI>mHewUA4qF(TtSx_Kqq8sH!)AOAP)B>F!Bqn<$_bqdM*|lf;u=nDKb(QF zMY+faQXC%hTqMTA;D)@5uu;0SCA5;`ulE|1OLmvEE<`f2yNXh|zcD`k-;k*#{}TL8 z`se!@0+Eb1>hV3O!5+{%L}dr9cUNc4hsW#Ne|B?P-wwW%r+!g=`w;=^@%r|GO`-{o zzWw?X(*V0`qi2Wp3#P;o#TtlS=-dC!GQYgOJ%ns0)3@*Yo5V|6-~KaJWb50nWNzU8 z6{O;nv*wa7sBf>riaFgM{}O%sQ?L9}`gZbOh`11Mu(u^xLUjKHEX2M&iE)K{he44(f52l0o8d5rS7;oXFq87F&G6ENNIJz^D zFe7tC>*|UcB$P{RJ3Z9xRLD{wd0{TFe>@;};2s4gk@A-GdSHADpt&aDW{?9xO*4e! zgWB_c<;{h%G5Pku~QF!`b1q$f_s`;__f@)p_VgsVc#-I8c^z;WXN4{~5 zL+-X0fR52osgNRMra44KCLB;WuMztB_t7Lz?L0z(V}{S8a`X zY8DZbXkH8odQZ&}YC29ouOqEkJbjSocO|4&F~Yvlbga@a zD{~X(S}c{QSbVaS5eC#V*D(~VN-a`rN0&(Td=MAfI{JAJ-l?GH(~uhat|^tGn&T;v zD*7WQRMC5CCB|CU*toN)L2YeNt9C+5FK8nX6#98$%|;#&vcX0mw9!<}3mREH@f>oCbj>v_Qi0BD)aWM|Ey>^mun;Y2 zv^*=M(!4)a0|@?3tr$Q!*Md#51F=rQ9_VAKG*9V!P_nHs|148so{FcwT&;>73LTK+ z4+?WZOh(7OqcMC8TkB2xOK82x?J`sfSWrMCt|O54lY%R>9+xVfiw4$0dV{8adGN+s z;n1a`W;K}CfoP}Txiu@RKIn8{i?g4wylQbLw4rrLXg~Hl4uo4fRc7v2R4wk0*sAEE z&}R<7qCid`cVNrZnvS0g`Iz(d^`jt0+xqY24z+fv9YOZ+5A@&Mvecut{`-Ev!%$pU zmV!v}Yg}K@t7)y}YJxPWtGMls>H)0w4GNRjgJ>n;{!7*15NK2Vm$m+q`p&6yjnnkN zOUBSV?C(}{TFFRHJ%Zop2{#mOJqp_|pYixA%IB)SETDhl18I&lr(EHNAy;(btx#Up zy;X#vZ=DCy8&Wl;Utx}Pn$mHE5$uOy83Sgm(=iXA+`7S4Q~DOdQKP)cDtIQN$oBmN)TudEQwJ80D}rLbC!j*m$zhIEPY^eZvSnu7*WeY>GY{8@5^`6UK=i!Ow-zVZ23dG@&vED-t zvCiX@__DvnllZc~#gq85zxn+C313+6q1{;T5eZoD@qzW8c%;{RP{*BYy?69k477IE zdqCX@*LxRXfN{9qgu#BI_1*v|>+JR36-Y*2GwNJty*Kp5%=LvhzkJl{i$G_+mqZQr zdT+m|?4Y%~9(hv!U8O(VEc>e81^&tPUgyQE`1tjnUxYjMRp)x0*@-5>|7gAU5u5+z z*LxAFI8S0>-wdrJfQPOkTco8#nqPYDYZq&{XYRPl9h-GbcKMIb?(@W#^b%EBCB>K??>Z`_sG zVcWxHn(-14hBYs3(z)iv6YJg~#dsFuiA@wQbTUJ*iQ?^pXMa5V<5_`c1)dt7c#Ktb z?^rxX;d2yMsP;xTtfJ0>*&v0qVWNw+SyR!ou$vU?NiHn?dM~Ugd2EimfThm* z3hPS&td%SCQEyo>hgtVn$3Qwb8Rsd#{aGA{QzEonKYneAlv66p#b#&>_uN8vMl~!hXHXl47QoZ(GA}#8r6P(E0VKnuP9Ig8k-&!t4Lxv5V#Hf+RAG%} z4cMlpkv8`5xYzw=4kEZNz6vl0^h*N9m3^UtsldK;zw>?O!0y(Eb?DjflA`HHnKZO3 zWk}EMw073d>GKyfI}cUJxfok>B|h`<5_v4ompTp!VNR}p)93TPl|G->JAFQ{IDI~^ zTgLf3rJ71xp3TcVgXdFT*}#)`e&h1)5C)*08`mwzwIp##8s5zFcSydPCli^j3(}#f zg*C+S!fI=zm&@nU6NBRgYRZp`w6sDGi5#BHXJ9%Hnf9 zfPf#5W?Ao0%M~c!Lxa9$Fm`F=u=z74M64Nz&N)a6F5{xGFX={gJ#s(qW~bC$sq;g_k(?FYQSb@QyU5D>A=ww8=g;k_AWhF) zJ)D%D`={xdCrgdt;=JjFX?i|e((@pW$;y~g{WdUkVu~I#9M{o-QoElGf-Xtn=LGcJ zT@mD5g#)6Gwk72=piRz$fkrZzc9JD%7>7G1jXIu&K|`eBx-1$F{Wybu<$2(4l7f+3 zp6J{m-Qq|JPWLD2*PW){PMP!zIurDZK+^BcuiErGbDl%Lok&JD{es?8^n2(e^m`q0 z3;KNxSq1w_kEavU?^aL->Gw=g_etpYQ*MLl4WL^$&NsA2ufGsQC9UOXk1y6fy}l?* zL5(ST{WX%4UhDGH^jeK%ZRxe`d7$y}TaZ)s59cG?hbep{!1@`NeBR=2o1Vvvoc>|@ zw%DhM+Y=E5da2XNorSjo^ zF&+i>XI4$a{U~mJ@;Cr4R_vd36WEOvAn_|QuK~;R852u+2Ur)}?R6R~qx$0_uLKKB zOhm)9YWg_}GF2aSZtDK8u}=NU{L_^Aoyr?SWtsPXm2pXiEe1i_^A?EtS{Y@Vm?JHZJ7g~!J~6X&gA2n3%@R{{f}{hA9d z_;(Q*k^NuRqZ=zIxN@`!k=|x=aFw^nxbT$rU z^Ztw3n26$4MCAqi%@Oy{OoRlc;!GFx|05e!y`aPxQi9#65^e}E0B?}Z7~)QaxRJ-> zvWyWW9#mz)XlO&F$q zwfkBDp9ps7>(tJ3mB9pEW( zPq}RRVxm(mA6-%~rN{KB{D3q3DTN*7JKCj~4cMTj2Z=&3dXSiN-UZIw!RA&o7&IjO zM~Dwo#SSJmsu3n!3fzbh_cr}U+(_At{v)E&f5c(I?(5XIa?oG&A8`ZyN8rW={O2X> zu_?11ry!ez|A?k6FA8vWMgI}q)ND#MtKGmbQPT$##HiC%4?_UsozmvzPBK|x^4KX*^9OzQb?tk_e zGik2lHM^g#8hIt-g;2-d&nsV|j9fz6z+NX%tClZO?Ev>+)8m?^n*KlIXY}dQ z98&VPs5*#>$w^B7D08Ps$-kJNXR5H{nvNr%AnF;jK-0KFOZXZ%>tB@Fu{1L-B<*&l=GeRnv>Ci_` zCVk-JOVh`qf0{meN~pgE`hab9XpTp}XD+Ry)#;zJOr<&joMNFg$M8N2rUKu38S)$x zb4@e!d33t|F^_w-?_Wf4pSs_31+@$%%iiMg8*I~w-ioJti^!k|@%!waf=w*o_zM0+ zuKbqhpRb{RmJx8Lg^{a373`n6Y;c<;T{b_@;=kNK&EhB6su29-+vF$WgZ!iq`HA=< zKe;fxc$#D;Md2vk$VpBM*W*p_k%cxN!QPGFBcl4W{Nd0Gw|2lMu7N+A)oy%b(91U{ zGD?1ewY20X7diYSe7K_W4KNcdT>dF$vcKPnnTc2ojdPeuzSwVZ_{po!lAj!j?skK# ztVLf>21g0+RKPyC!2@O@@MU&itC|DdlH<5YPe*^^tj|V)?H?$V-9+xOGgI=;;?s_| zn_qqNcH2R;S&uxW$i!Jc2R21?TdKE511Ec&PEVaFA6NVUg21U=9JRVMQ| z&OedlA5VZY{A2#H;twhQA^NH21pLEZMz`S~Jskc)e=@RvB-*}M{~YdnJoga&LmLCh zJ@DCvdt~#Auj+s1%}byrIidfVe$Z1L?|i8Wo5pOh%GS|1%v}5iU!NS+5?Ia2aJQ zNHZBIm`NxvV49hB8dKu72O`?~pE;Wq)Nu$S{Vg&*Tl6ayA}Sh@s_6S|D!Me4=F(I} z0_w$xp!SB)TKV{q{%1Z$Fim94vR==p{m(o#g&o;(|7B7^sL>d|?5q-e8QsqAi3fy_ zjZRMZpScke4Z{p>K1%;HbB=rr8=;sr8_%rq=rN{^GKP(Y;;Kmv$2yr65NdB6>otdW z@*Ce96~F{{dopHHRP3_P;25PtybbKK?k>JJy$|ixmAlMApUn5e7Rs-{m$`92VsCzseTdJ zs^3-P!lNuSqm@_PRFEj~dYwIUmi% zfwnr=Ix03*7gW`OdWgxcy)JkFYA)9D5x88{w|Mp4IPKZ!G46R4*35|U=N|&qpw$VF z{&izyo?5;NYn@g4$UIomtK}>3u|gl|HbxedM-_jgKC-|VS%?K&vHe+S3_a6R-l$^9 zgPW5xw5)C9!7O^lJ(6RA!hhbs*>}YydnfX5wjM+3`1$287?~%YUuXk>`6Yrx|1SS# zpFa$+IKPaW&6l$7f_r56H+x?sNX;)#$@HoDrMJi?dEhyCKPhuQ^jpc&XEBpeb`LpF z26O$UEK9JtEqFiC{8H8?-%%9nF!et$pEJLF^jUg-nSw-F^UGZ}FsCAioL?5nt6&o4y;<l0? zOuWhYW%=y%{Bq@k#6rg>IHL$-5Y8|2d*I9H_S(64U@n>D^Y z8b1+T!KYt;WUODGc#EdrdoUDPbU~!Do&2U~{{%P@(_3EJXz(*XlGZ2;TN4S|I}J zWdJU|f8>swpx2m07ur4;5|6lp*84zp zOm&mX3iD@dF}CaKVwxe;*Xi}IgI{rf@+epn!)O|3|4_5kQ+UjWT~r2%<-t1Q2I}A} zg-}d02DV}uw`L;dtVfCy5BTI^f8V-hf6rK`G`Sel4IGx+`xQ{FVY#5p;3gk(!c=V( z+z}-0_1t0Mz4UO_jN&-+Tz;{bQ3k@9C$vG26jK{S3=LD~qy0dfQhgfRZ$46Ft$b3b z+fup{rFx%WKhR!Wv))BcM}qW%#IT((ViZRQ>NtwT=}!des-6 zhT$+!+~IQg7sO{;8y3769w+zo{e>vkp@SC(C2<$77m(U8oF#^tQ;EAmP9~gYX+5*ZTH&0qs3(4Nl`atefs)vyn z%;~i5?+eEi*y8aZi~g==Ec}##pn4_pEhN&+--rOJI-I~>d4!aYT~QjjWi5q5tr zL4kN&ARk5Y(N#V=<3W~u=;v8n=TV32DAj*N>A*xEzgZ#1dW~U#C zoCTd-pW=|0s{3#*Of7W$<_Nbz;QtPN$ouBMs(kqEvoIl^Q2+4iQy`M#{oi8RKCyf_ z9C;ug-i}27F8T1i`vI2le~VcO^5L=Hw9!8-770@F;Ut-UwxfSIRb-Pg%wh29lL}Xx zL<0D~WhVH)J)2@hTK{l{EKA5fZ{Ynz@?p0&`L>`~M<@$3pCccxN~Gn(aY&RUAC9nr zNigCCiYQOZgdpgN@Zgg_e|1J1F!%qEK zN+@luVOeCI)`G_taM3N*$?)4lbj^p6HJXaUi}vs5>mMB6bMy* z%$whY=j-@s`1V7aAeP{|aITG`#CcK3H;u|;?~$3s8Zc$RJ^N1hmk(X0CKq!#?Q`TC zzy7usJ_7lMlLd0F^_$m0zNv?NL%;MTy7bsD8l_Ky3ylVA07yEeohW%1!yAL}&; z2jiCjj>7ifsJuHHh6^)bm?`V;G-*g?-Mddv_oP(ai6Vg|eG(@W3V%<+CPAP?pX9bz z?+g;>E5u|-DFM2ZVi>?-#(Fz=mc##>GL*NbOLA-eMNFL~);;=Gf5pk;vpR>{<&qOgwaqHfV zMci$JL$pt<_a4F~gW=72-EKf$&lT!L0r$oH#5vt!Eq}2Fr_MV3G~p^8GLpksr3hVP z2$RP6IhbSS{g$p_$OnG-Q&<#x>p^?o**?7!WJ3_wR%1tt#SUvd z0D(wO?{J1m|G3t-0aBa?-7s3C+W`Sih6iTq-dTVELnvuY0tDb)eshtA#v+8*w^pPI zt%=-19uSM%fd`7gggozlkq|9zwaz9mB1w(61}D0iii%cx9i(TW16C&iZcWXQVlMCY z!P^U_09Iw~;`{Waq=O8h6*3YAMKV}KyiZcSXq0Rb9G=Cyqg%XsOhi4J)g3!f4UYp+ z22`qfmpKN6ywY48eg-IBy}|k+>ck;ydGPqS`^21ev~bcR91~a?GRVWvHA6-L@=`z^ z=lOkmB4ZOz7lq2JPLID7GP_VWTfWa^p~ZNWg)%Vy5xR>uH5fFSd3}Gl>b0qA#w6g$ zF4uNeg=KsnUZd4x?r-&DU z6wsBt_6WaK+por?spMvGL79^k)FquX|U8E`ysydXKqK(?fHG7u=+^ z8bgv;%@f#-GaXdKb76q~%|Z<~R%rUSVSvt77@>t|NOl9_TPgu2itG+;E*s<3mnKKK zy?V1Bl{)yNxy8Y?TF=JexT?KD zyLd4)geaKpV_=K*CCnQ;y3w1obL=QoWAN$UvCzfeViyBTo!I_-VR#=ATR|lfoKn$@ zGF0aSE#u|QoMITTKRE-Jmq_kiP=bla7+sQwS?D6#9Oi~)gm{YUtscqJJ>14Dzi5nkr5f{v%t6-7)VViZkW8x_k_9^=!n1}bk zl9mR_J<cac`bSZwxn2jy8An>L01$9h24Q4wst0RD~xt73!quo?JD& zOY!FKQllSnwC>^pS$L>~_Xs+v)Wz|a6pmgT z8lb-#6A9sb7k=9@gQF2sp^xWt3y=Uxa%!VfRdGF#P2U0p5lN#8c=yk5GRCZ`e%q$- zU9^G$4nf)V@hYKLoLa}~oq-5d+0;0*X!9=T$i^OPMm0)?uRy$`Yj3O6YM8H0OE&_mrzTm3p zuuiz$U3Mr0n1j|Yq1QqmpA!C{KPcPj%5w*s1-(7_hqJrVI)}Q@{G*~1Yc@N57#s-) zW-?Cr810BEH-H^z=?N)*6tf932omTC5`Z60exbmg zC@?$m63JxeT8#jN%=OL3_yoT_m`Efr`0%zXv7P5{vc`K`etSWee+$2bQwYg#UEsHQ zpxuiB9;E&vl32uTu30?YqEsg?*R)q_FF9_$~U~=C>eP>&GbO1pIap zXTvnV1$RCHzr|6IEPmKNzr6xOg8cRlDt%AHZ+##co8ML-9jWNM@1C6BPXCB5dU2nO z-x%~hkPoNG!dG*6DkjJ3atcKAU483Yyvp2A0rbhkgn~P9S!9d8>Cw*Y&wVqKjiDp1 zEl#3DadvycM++ZaYds6loD?u~->u3D)}eA-B!eb-F}8DZB(Kmp{xNGy)&M1}WDTUN zyQ|p5@mTmjSO{rwHrE=Lh4zApifgF+=5uZYVdj3h0F)#Ze}~{r2(K9VrihG1a*&4A zX-1J|F6_Ys)*Nt8lqeF%st43C9)Eb{`WBKhN{xuv+{qT4n-+`7#TsPnO^WM_Bh4s9 z=@n^`4_+USueo^8qp8x%I~V`6@jny)Rfytp1`n=t@E9c@Vub)%Pm?aMUhj&!HGS7U z?KvAZ>ne^c8%}c`%U@7|L{@i%gWpS7DC48hLir%{_~Nw*c+k)X51s|Yqh3Dd;sJ+a zeUK7W^DC?Br$xho|ApvB+$jXi*|->Mv0E-3yEn;oU z)S@K6v1kse`bw_o|1+H7H;L8^`fX=1gwzIa0lY{|Z3>-=%q?{dk~6p-090b>I8TE2QAuv&pbZ}7&PHqa;$dZa!X zL`r03I)#KPPIDfNf6Q560-&y6y+d{6l7Gpq1(yPcEYpiJiG5=Nfe-rH03PapGN41} zW}#8)Z1=6VmAwb8#ZWIw8UV}w>m#f&X01bf+w}(QU$~(Jl@GNhK7EzS#m7O&nZjG# zWL!A0&8i)}Hwo`m4c@1)498}_3_TN}gSZJ**%dmKr=&A>H0(@>gwW@FXo@|9$si7R z{QU(8E+elM0mp=3s9-QM1qy`T-(X98c_rLYQ#@mqm;pYfd2c>!!VVPeaJ_JiDanTHx=pKEZg;=F}3*kzm zbJ=x!;f?-dfl)iPVTzQG(VT?ivh@b^s@{N9MuahHIgWISa`h;7rbVjf$X31j{^4dG zue3zV1>42iw8Aa$iL>Uo=8Tf7+lwUy?!K@Sx8L2JMWSYZOUBe!WFHcS`;HjQ6iM8j6V#|}Ru%XM=Ho^l7(i0~bN zZo-4ECe87)P;LtoV1CE`*j&*PV-fR$*5+F8lB6lWCL0zMi*mPF0}*z>I}00-A?Opm zzrrd9isms$gZm;xR_h^=EijkP!*@}q_%$&!wppX`#FYcW$KHT>grl-1{$hIoQ>EAr zz;!8r32ze;0X_%bChIa$ai}R1-p8?sJK(J&yp4eObsJu!Nx{3Ec2`hdq~M*x0MOdA zc(#QXm`)I{-645R6>f;jlCQQtNA800F(Zb}@(<>Y{#2?L{jtF?~N% z!?b$h#W=5xn~vY{-S={jQ=Yu1eno5>t1A$n8hMkfMl1Rn9UNeL9_rbjjycr7|sXX!Sk6o z7MO!F7dqR1eW_w}_MhbWK*VzsiuX&eW@7Uu3?=7$AlCW+@O+>=|5aw658Mpvfz9H6 z>CNJN;0EpBR%{qyA8)@F{RC#g-Fmx(b<)?akf*+&wz;PfT7)Lr<=E%GUcg+6^SanN zPZT)gfys#J8y>LFYwV`$(yA$ih1f5_AMca)7vi$IJzlX<_|4e%P zuU6U0w)AM5fY~JZHy|IZtlrviFQkX$GTXSzK2A6Xu_A;tE8CyhQ^0ov#}+miR?ztG zp2o!YGPkDphKgrmWsA!M4vP!R$1y|d!t%!82YO@XA+C%r+WL9BT@>ueq0$?mCNoLs z$3Fyx*{qEW8E9|oBL&bPC2NzG5CyX2V*2_0JJ0NSZ;2ev^b>)lESld;~pihrMdng=BtVAB6 z{KkO-pdO;!%r?#=-a;^Vg76?=&A9^%)@7x9M?9>*3sVH?|74@jc=edwIoM5JRykjb zEdiQfxbT72b8Dz1b-wOKUiKjR2Rq!a9+UlZK+`t{KM-aE_9iy~cAT}qxy3pOCkj>~ zH?h=kwvAm%e#qkAX?*j*-9NzhfHc1Qr15=m8sFzieDee*cq1N;Q<@WEddAUgOhfIZ zRR2k0+Q!F8@QuCOLTlCt+@MhJU>#PmlO8x8>(IW!xPVxnn~nAJFdl$!7>G==k&UDg zfoy3XazbPSRDtYq?I1f-UONH4|2OHo>yd1%R}t%9guYipFXLc(8ZiBT^t~6fdJ<0Z z{Gn`23;KTZOVM|o2KtWHQ8m`9(Lp}RydC7H$^3zVQx3xRNT zA8g_$Hc`q#$oj=B@GyT^3exRghvzN??0+BW_7@`!UpC@pKiW9)AVZMzz9u_u^KogL zm=+C~*uu1I#YPBc6PqZZwc>vnq_jM3P%d^9l!yzcMi>O?`x_~%HKMpjS7T4Vo8N@? zIa}Zs9yuUi;=HtGJh4*DG?cfYuofQ~=cT`xKaNG*^bI=l_6huPU=mu2KZ9XicLf4)|p_R|uoPOz{V+9^;99 z_8hyS^0{mw^+J0+k|Ccmc}n~_v3$CHf4jnu67iseQu3*|nK8>>nArc%;{WOK@Nu15 zjKu)Q!vZYFKr~$z%V)P0^4a5U`MmgDK2!5IQhtU}Fp#5Q4@l=7LHrmZM?aS#TjHdu z4wC5C+kq3`Ayp&&mfc8&KoabI%kU%(3StDAO?-#snZV0uBu^AN@f}j;Ct~d|XgVNP zB992Fynh)fav3SIL5=Rr7ZeE};2BFmL6K>C3R9yL~>&Dr~@T)#{H{C1i@Uxtlb5XYb^_#}TWmjPv} z9neh^0egghM5H7st^iHmE%ASgCH#fv$rgq)9k1y&1U@9pS(0cA=k}Kc4is!>?o8V>|S7 z?!p{LEc-$t-Sw?hOu?=hWST*I>uGTl#=yrn*radi@Dv+UrJpl6@w7l5KmRx)_g~ zZ|&Y}f4pQL>RtVUr)^PAlTjuj2A+V-21+_0`m zj7iTI*(TMFSHZ7x|8D~OdT#P8LtX>?;y4VINNmE| z8*&@J2PNh7R^ENC%-GtIv)j`&mJu7X-kI22n~f+aG($6;Y}Ox;CxA0TIDo*tX}@ca zX3S$I%Qavxw1&cxCgv5hWz1W}{ILkPhU8l?^>)AghSIAR6d;!SonDlVtt`f+!uldi zLR4>ftMtXuBL(`HSH*D$-CJb{%bMsRTwA3_*T(asP=Z<=@*8;@RG$2D%df)x=#Q4a z_tMf%(fD~V8G73)5C?^DiV}BWtHPM>EzF@g>vp*|Mt2n?*XWA~hY_gOS4NK(=uhuQ zufq??^zAg=)dSUr$<{;<7xdb!uUNY~e}%p_8t;(5me=j&$uGD3D$HN;qZM}5IK<*M zhojD=X7Gv*PL12_8d;(dsL|I%lLh+Jg-%^H#?!rJ)~+l=dWx>}<<3zDmKvGs3`JEMa>epO9W#Tj!w zVxgU0b26@L+tbl&{?O%%?&!cB@#gPx1L|d(JkfyG>}TeoI1T4j#BhK$OVV2K9j4i3 zy^6F@u%S;u9*nnUBL;hF%ngH1>|o1W_;}wMiP-?Tak_#SUnMC`5N^Zn!sPtV=@2DbbYL-eC- ztH8XcCc19d^cYA!r{_l3wrF#KE;dks9DUwx#Q3fPebF6w@#=es4**2C2OD{Hs*TOK zx^AbZCSJAu0vIdsTwC~gphceF`74^jy*hJI1a|Sy=Ek=;_3i6=_cF533zd9wtD*Ghaz!W+Uw|l)hGr1f zLZFaN3Z7+XHg>nc$OEqA?Zyo@y4PX1l@`r#q7lS+k*Ww;)f!JpG3^UH92xeNLjC2tK>OG5 zPU`|-z;6gD$FXpDO)7>K1rT$Xs^!aY;7EC_Bi_+8_Ka6MK$WbvdsoHdGwMfPIRL9qCLBGf}L-gT`FMgI~M=52NrRxzOc67O0%; z2UwoRqz?P3RRzBW=QV^>W8I7ZQDA?kz=2MI6(XO*ofvQZ_Ik2RkTe+ce$ z+KuE+eGp9E!Dw9J06r&=?6q2>5nn6f)taHjoI3_0B)MdOxEKt#-7AkN9AM_6xvc2| zM9y_V;<{|Rkhl^Z`qtCtQi<$mZ%toH$t+`g8Iw=^1;pT)_yK>$^Jgl5F6Yk=@k0}e zjzD0%N+u)_uYn`L6Suw%@zx*l;dJ0LjKKCZa_zF#02-W1;z!b|mZ=1olbLD^A~>+` zk`bE-|M%p(7}(b{5zIW6B`(B#%0bHv_VB$IMdO{_ZvBAo7`|3ds2Kc6BN9d)G zlhy=$q~_6O3Aoc~V{Z?8LvA_4$`FTGariaE0B-$wX%koi3pp`!q;2C~XA~A~&|+Cn zj{&6TIBCqXskhV4aB4k_48M*jpZS{5*Xr{|p{vBHB({4Rq5|ggJg($~adx`{!C(&+ z?A3I2SM`QvBYC+p<8BNA+<^kiZQ3&V&4=7@R|i5CWH5dvW_)lCq3}!TQ>8ssu6FSu zIDyuTY8T=0D}QKF!sPw(NZsYY9_U}XfV;R#xQmX%Z#32;7zZqV`up-j7*XYltK4&i z$L-u-ird}Aq1Gr4wa&#AqV_m~L7X|J-r6((1G{P(4tW*(jH|_=Qp|D0sdX3F4!Tmj z6O#e`Qng{2+a)c`3y6i|?$v4Wz4qA~_-j?Bo6%m_YE+KSri(_` zs`(z$!YS=aXXqYgywfA2vH(EcbpTtOY9Vnu0AS|F?75@$mD9a zY@I;NH!lQu?7KKL2~3I#Kf1{B=m8jH0B?y1W!!(IB-#_%^^aTojr+d}2mnR0VKVTF zL#~K@Or+*OTcS+->s*0NCeO;pGZ8}M`E-?zmja&iVuY+RYOfZK!58&reFPW%i;s{RkV#$C)~}jZICI*}(bT6GcQ_lCTESMnvA+!XA~pb=x%e(0@V zq2moBIgcbIukhV3oG7*Y_t~j$Q>t0Xw-o(`?{fRnS(sg6>wL_vn9qz6^BLh4_n=ah zzEH^wwx$P5mi%E5=~LuI{($afAn9Y$r;6zjnQiVT(z1MF3H60|q)=UvJhqUSL%*|( z8s5$k+oBCe*f5g!xVKN#F43NyRy}mMJxqIh(0|?lclqC7e}5t)`v=(H-$OG;um}Cs zNaikQ4_bXhdr-I@`In_%9ovJRc7+DfNi59~?LmX=C8Zb6iJ5Y8xVduSr`dywxHfY> zzy(6q-srKba_57aS$AL7ze$#NKB$U%=L1#7xp4AVvgd>2$cj2klo_?9CAxyt1hQ@- zD~>w@t_=U|rHPGqgk;}$1`W&}41Nh-iCZ7Uo!j+^KyVmW6AL*JaG(%OlA%{I%Z0{z z=YgnQ+fBaDdB8Pl3TQ;zvJYT7fo$9#Hm17H+`PZ7tq8f{Iv1A%pmb@x=I!Zb7Zv8* z?DBnO8O%X|`w%fw_m)3XznS&_;92(U->o=joKtwuXJXyI1Bk)8!*Ev37uNj`Ls$?yG1pe| zX=QzDD;kP#ppdwkIpF?Pk8h}K7QR7 zqw*P}_Oj2;t)KIa7Zo>|7l^WRhF#C$yCd-HBbwZQfygR$om0AE*H<%=4%u~mb2#;} z!`Ssw{Ba4tUULWsa!IOdW}WgLv%dMWFzYvT?WDMMbz>;{lc`H`bL!*IKmUAw4(|Mn zQ`TzkyeHuj&&$o#L&7X4xpX1Lj&GF|KWcYJDOJ(``1w2*T`&tb?A0Es#onn7=G@O8d@6sw>p9(`=Fe}d_U`l($E5BD zYDNE|nt^CKc%s5FaE;0=*xiC83YNR^gRV2TXqvcLMM8opS4a(_*k!SJ%ji-kB(aN% zfz$y-+_GPYqWD(X43EC^i&MmLLJ`AUI^%c-M0cfAIV6CGt08dmXL>TXfzU@;ZFgU} zqL1o%hzQ8Eyuu@sIHMT?t$ZPB4TP~*q*C~#wN&yLdawjbl1OJ{rpvTP%jNxYweZZ6{+2>1|~+j!`}X&Q$cd zoCD6r%u$yt=*qPf~7#%XDVIPW2 zd#!1`jG6@S7PYw@6m5Yv_ctfP5v&sPc3dQM+W$CUf@WpGf0;L%IKSA7Mw;y;g`7J7($Hid{05P+aOf@ez4O0nU6 z65C#Iiz2b@C%+=GEzt=B273u7o~IH-Vw+uGzr41zNjR@-n~MK$5!AM^W7`XApJS#& z!L)ClYiF{9@rh8%s+iVa@dfa{qJ&6RWu25SfQONOu_5Euc}VF4Y)SD1ptC=dCxABh z>p|Iv%cYL>ddOB^OCOW#hSvdGZ9VY-u!2sIT}%a(2m10OjsRq18nrh98M-8_{9uwq znZikkBLMk;Z~ZgJ1aOXmI`Idv2B?ns`~e6C9ZK4f{Q*c-_fA#K^9LXvE&G!obwm8> zGe+N7*`v}O`~f_9lmLa}OXx*6snex@|xS+5!3g0R-iM>@?2v`3KnQwo+V)DOUIO z+yok)e}J}%5pn0A@eeR|KTJ}^z>MX6+!;DB?oHOLZ}~PsyF=5}6;3THWtR zj%UMun|@Zf_ABx{bG1+jAI&?=J;3=C)9wN4=j7Z2^d}YPK}KyW^A>gJnU$24$#n9p zOecNttNVMa^mnWKrbuN}e?4$^3+Qu7_Ai2yc<_^olVetz60LG@ew5O&;K!ouX8lF^ z15jYFmR`uo&DW&}Z z{E&v}R-QkAelh}&T{~9Vqa!9qbtU(~{})wq{s5#|uXXJY;LBtyb~v{`^5}PadEAT@ z|0VLc!~C-)k<YaI-xu=F_M?kr>f79wi<$Z(`Dgnz2Ut%Y zw~%tOR8QA`nmn!r3NQSV(g}cndI|qPXq5WYE!{hN<`}4~5~jPWM@Jmghwd(*0}g66 zE~-4hfrFYNAd}PJbel#W&O$w=D(10Jb4Y@PqHo?l&#+MUknveqs1My+b0yoeP#?Nu z$@Ew#zM*6=uwoD3>Jlj;sF-QfyhDmN_fj~Qp-p3g4(iC zJCjU~22#hzN&W7aoLeAc)-{w70hPM%-T)^m+|!%-_r!Pf?|`)aJ!2QY&~*8l|GY+( zJ-aT$;7e}3|J!6$_#5gRt@&GdqBdD?2vehTVYmr)v+~>WIL(1ub@GN4* zthf~3joJwFuk(aUAt^lkUG1)Di8QV6Pjdc>)LIIEauyrU&z}e~uLq2d!k^Hb`z%GE z@5)~hKDpjkFLHYx%PR3u+x0GC|9Y3P-59deP-11D%#b1%OMfhC) ziVgyJioiqe+uTx_Adey0Bsm^K@{)d53`vVT!;pY>kH(NZL@~{feEj_!Lt>NK-d~X{ zO~$KIjv>jOr}F(3T_Il=hQt+ygqrT%%)n>#S9C0u^Zga67S5#wp*2_2Q8}<*p?;H+ zcXWS6%g^)L%lRvMTRs7Qq83c^CqJQ-=1+De59d$rCD$V}?p@Lz9eHS|pc3V;=xM6> z_!FA-TG#x^&&WP1e}XVUoaO0D7#$VmD!Pc`d(qK6(6#-$MnM<8b1%ukng z4mD(APN$|g1H1@+wiHe`RC*Yz+duA9_E)g9@LX@DV0HJoYZ#jQv5XauMDgI(iqkUp zD?a}5E~>`)XaF!nG@*2Iu+5I5>kKi#iQr6Y{UC&Mxg~Qv@s6aF$ zf!fcwl|5C&8QYiV4(;OxXI&^t45x7I-iotg&Rq>qA*D|b=&1`X(1C=`I8DYYciij9 zNEhd_wzx%P<%CL7d3XS($nGM(s$)Uds9g9qek9hpG; z>njd#A1xFVNhCguZhvLF_E9qlvIB_sLc@vQr-^v*6eGbL&BEEWlX%Kg>LwBZlS0C> zY{D)86c64#!cQ5sC$Y7-k6IkDPca`WV4aQHZjvOSFcKF;oT(+AYI2+Vl)$w!b!29G z-QVhj2^l4B_otFzj}hlT6$A47E6NF7L@u8h&ujVl_9VvB1zRl{Pg>ivi}!Gx`=-Ce zeUA#yHYl$@L)F*r8-{T26}j6iJw5mJ!{_sJeLpcrqT;<9s@ufw8SJAVT>B|NyF&r~ zM)$!p`6he)scN+Wvg@C>f5o2LnphvPcFD~jY=2zt&;3fYd)=F8P_EUBr9CXClsFlp zKbG_rfler12W7Z?9xPcc3nc!ONf*wbzv{(gJRnwa#C^-X;aG|~7yc@U|5~R?aXxMC z#36#Wm28v?#}*z%svLDD<0}8%m?iu`?tZuO&8z>YE@(y&b`zR-*L-ZV7RIdG7D-TN+9Z}{C(bR-ZSy1c zfG<%t-Ok^Jl-}yzbDG%C5+_haAvw%`7WS8`o`dd-14JIwMBCbib{1_bHE7(6sc2kH zXgu!8s47i)$gl2rFmbBfpJx5RMWv`;Kk4XqQLOZc9*j}Oml<19V%muL>_PLIg9yG z*A{!_zR&LZ(LhJn&-e*)v=n~6DiMO zXd&O#NedRK0id@<`%L6AZp2iQF5s>w8FhMlfs8O>y&kh28M5pSKRwFn2VCsa4>(&r zYkl5VeTV&m~ zG#>1(-5W5L#uwJLlolkgxLZ;u^S3S~-m3L8(I+CoHAeF1Omp>esop6;@G?i*j5zle z5b?owq!!-r))tFFo>(}99}-u&net~Tt2&8W=J|Ka+TV@$+uuzT&OmJRVmx597P98m z(y)8}AVKE?#y<)Qv{jM^g;CTJG-Qe?l<*p6&B4wnW2w&BY5+#PTxn!_(lxs7{dCv3 z{Y^FOWop=6WxUuw#2cc)7mVcdzT({?1YVYDs@$uG))Ms%>vVOBlWT?Sc%phd(Y6;7 z=~R*&Riu&!raW#vlNr)YC2B~OkF*~W+Ba}9m+g`TJey`DoE*pEfRH_a;Ew&8Tl2Y^xx?%KD?@cettEJqlLp7>!sz({Tb%q4X1x^1adp??*NlL>pSqX}#jT;G=)DogMHK`8iUe%NXOMtxb^iq2 zt@*g~v~R+J*vkduqPO7FzrqqSd0kf>k{FW-c^#X170$y*j?BCY|6wGD=+`W+eOxgj zM)FwFk?N(6V?1zbiim-__@KUlK8ucI2OxK909aD`6PNqsGTEnXNg)$hR^h68E*g9? zCvj^}l-MuPneRcfx{R|6UDLCdCKTFMzoY7wsiWf73n=rjf2F4A7m3BisMSi5ET-LBkMEXKpQ`ZhvKWOS77FUP0zv-^9P4_~1ZEfF_~ z_vp^;FKZ8RRFlL8Cc4^F@5_98s@366LF?fZNJO4`XX1 z`7JgY^fB2>b|G!5a8DGNB>m$<+P?Y&KAW?zj`-#)r2^LrewVJnI|(rf)u~mCTQ9rg zPC>F_Z41p-Da@th3u6CwS@oq8m*tk31)bO+J%y636ZDOqh%zvOR3&a91WlnB`AdQB znW0cZ^^`RoAnO)oO+iKhjKI&2!jEVp@NH>M-Rmoc;>Ijo_d?8?Cy?U);bg(E>N#AO zVoUeb*r{u3_CA5Y4!dS}DB9~G0k|}ClI!T#>B}(@N6ypGQ328;c3FYplq?vY>6_N{ z*QCeS(cL)2pc`F{TL*PFO<5LpKw27~O!O($1DO`+;I?!TW17I?#ey4*WW!#yibnD+ zg*jQ?*VV)FzLa1R1q$8OEKv^E4g**P*AA6mK!=ZYG~K7uKcn_$+Ryes&+Ptka#WGg zJh~MPMAsi#lb)L~ffR-ByYoxo8#v7P|V?BCY zlQ|432=6u8?|rTkFZUI3p=T5?!A0RLE;z!DD6xa3BHfo7cq2I;tlu5ln?=d z36r%neIwtf_{wK*|A(L2{ssQ_Mbul4Jl7}c9g>lZ=GGg10ffF4BJ|zo{B} ziLL1qB_GM)8}7f;H0U|`eJSBA@8rW15B5=bLSbwpd8>RAJaM!7w%V|QRN8OY5@~=9 z+e?Vdol;haj8q=}X6j-gI@re2kIK|X8c z0pN9eOP5XKfz>JTz|KsoBQ-%J_MRkQjppS0>OsTzX8o){`emL~1fxqp8&OBZ{^ogR*cjSHpvM3GnaIiU$u%)XC9aE)`SPC}sf0VC&KJKd9}y4~Xcx?)R8s0k)ri zQ{MvoRDL0fAVXP*_H!HX=Jq&d?pqO9-H7wOg7h6stdh{?kas@{D)c#+hd<>e`f){r zwbv9Q`77QsK(QvVHT7@uE)zYM2Y1#93NRj6rF3L~wib^v6l5AN&NQxh28sNhgSc7w zji;vxYMbH?&9s`nUCTiP>ARN#gurr!A{V6gqrRf4bLZ4{o`=5)Yznx!Cr}imbb`+9 z;vue7bG8CZgW5Ey$1?R~D&02$8tP5*>M60YMTEA&VTsgM9dW|o$(0mruqbxvKum;R9r>OK~xJ`%tLk6gjADs>`%JWwxF z@*bo1WRlb@g4As-@TqBbJ=u?&VyYv$Xkt_9gHLo@AMxwqq!@CMXz;CyljWMK=9yNx zzJH8L@gP`eBez*xT-gfNQ;T(NA+!(k$Z5md0`lWNi9p(YNlB$4sv_MIv5R{LZK3Xz z`Z_hzy_8g330BYh9ty&o+PCtV8lRQ=7#!()4kz#SL<VIW4&lsZJpn z?Jphq`-S1T_y8gn$6fWnkW7^)4`)JZwJMR%zP;DVs3*fWE}9`>AGIn$PD37=hyC;MjP!lt$JA6eqfPez}A zl$L(WG--W)zjVYO%mI5FI_ZrjIUc;D1>OqwIuv+^fWm*2zrzzr6dof9puzz3078lXo{89C{nHy!-lretCCSax_K|R+9sWqQYZH zMc&QVee&*0`)GN$bgiAd`wYq}&NV0RRs-1R9ym^Nw7grAyc5pbBx`xMDyQV#JhkKR zefC=yD!I1yUok{IYkBto^{o9@^x_#Aj6nS}pIzF0)BJpCH=tm&mv-x(&tQhKR|pl1 zASdKHvb3A39Z9=4t4p+eX}6k+jqb^LE$2(Sf1|49NxOfola#dkC+%AWjXsyF;!flF zFll#g|9)P1HyzH%yLmjSyj#YWk$1~;J9#%rp1fN!v+{0UI+k~<@?SvSO?~&WV+3Ol zxanWDaF`?!>dGgzyql-?@@|sgj_dfz%DZ{)BiPauS$FHwyYA)5x{EzoH{XHHY~@F*3SUY$MAogkpOtm$;5G|2c@@`&zg8BandG|?l^=R^L8A6Bh?x&G2 zBkwx8y!$1VD2Hu7m%RH%+Rw-|^4-jaM6xZyk}@9LiJg_Or1o4%&6jsy=llBqguMGL z!bAJz-NJy@J_|YhJo4^eus~Ygt*G(+LXAIzy!-US@UoFS0j-}lnupB;V_eNe>6(#N7z_Z6@Ang{=_N3i^CIbP{?Mm7$6hup-KbyR} zfcZJPy!#i@rN3L={SdW}-u@VW`+tYL`^f_4C@b%NkJgZP|Jm+9-VK-XDe`V+^;6{C zalY10-aSQqQt^N8zZ^mq!%24b^?#?l`#+B;@BS@Ki@dw`aC!G5CWJ9#%HJCb*k%odR2sr%f4qE7C+{6Vzr zlXp+2tq$egH<6K*cQ3vANb+tROpD@a+__(ra>b)zvq-G6`r?>H;P&!Gt-rii#p6~l zDt^o~G>P^m&gb6Do`x3D-o)8Fpg*)1wxy;~%qlX`beEYrKH!%l_T8eu+&+ZNj>x{( zB7=5Bp7uE}Z9>pZq`(V*nB?eByytEzGIv$OVUYsU5KcsO4HSAieY_I5W&1CMMe@FV z572MDAG1+4>S;J5Xb8=~T^7Yx2Sd=8w0%e*Z|d zz9{x5#edn6*7pVFHIH>0PkbQt;#*%8A4sn+T0MGwaWJOWSEBbmX|7NL`M+mzAFEMA z9>wCmh;V*U$f{Z#v%<^Fnd;8Ms1*|{Sy)+M?)xPl>a)oG^)6XZ&I|`ST+qlemX68l zR1LytkeQRmWz6FY`A_2W&Ec2ik)`KO8Y5kI%Qjj4W}4KXs>rHzweOe{Piq76|8=<3^OwY$)0>iR)4X~GOIt*#|q`I z_ss44nJ&vI>cV&Omxvr?JS7mQ!YEjvD3M+d$(~HXP4H(WA1Xe2o!?+fH zRr;zeOhmG!EKEq3ef{U%lC|ItK8xp4{?ax2Eur91%$9iocp5U++o*|%MAzd9{&Rn|MB+{m z%%PeeKmN8<91}egBD8-*O4|?1=Y|uI5jT*@W!?u^cotCJ+(~i;Xsc&bBK}+=m0+PB z2Z`=g5il@bQNb)CPVylth{`s~(^5Cmwo>kN1>cIEB`M1Ly!bD9kThv#wd9F&1nmVx zTpEC9oxmIK7Y1UmYW+tVUJ-dLuJE^`b&5yn^b7j59fhpkhaPq0i%8p@3tdf2u6Au^~bAM`D`#!Y}9vA zuhN%bq_25(759Tg&CBsovf@M6E6OcrLh)_q}$EOa{kKXHzh;zbVN*e!YfpA zmP;kK>$^prQh%fps5WkoC}qHjA^Nk|@mW+SqRrjJ91W2ICVs$!Y~~=Jjaws15B~&P zUXbnLm!YaTYa9(#i^ z$TMhN);<(mX&SLsU_{=6O{Voxh10mze z1H;z`KVBgnH|7k7mIikiv8H|N%;o!~_K-bh@P&}I&4{hrw?5cv#9OUbhBPsgZ7YX} zn%x}~3I$UkBc3w%y&SS06|N%`bVF9!h`-jhYRIaZcMdt@4y{W8sFTQZq6L<7Ee{2l zl&Ri$fEfxl7;_{V2=6jDtGQ-t5lV2=d4^??q3Qd`aPXip=XD6^<({Wzvu4Z4m>v7u z+?uMowu)=0FTJ|V=~--At4zLMv*}O;Lzk~EDyRq(_tC`1ZOB?zv$?2dQ&Fh<&QSLq zip9nyin&~9b6sO}!+X*0>rChB&guSPYeVW`Qu!52ooXIwBxJ1@Z#)GX_@WFA9D5V| zA5G`Ctbzj64mg)}$@alitY>t?=DbeqjCTK9&C0^y zhQu$u1tg$lyn8AX6fc4@;Yc)OnvB}z(#c?&1GZ_zS2>Y_(2&5i$}xGwo6Bi&m~0%T-AnH_Mndt*5Gapf;!7V8)et~Q-e zX#w82jrwCjvmQv4B^8EKfg}Y)jQR!`D#nOg{l{5IO@(NIy##@kLwPKagU{mFUP}u@m-^#1a-~ml^jQU-$4&W)H zew%O0_F3vqZ%%d<}_GHEXj#)(y+>zX{HYU&0HVIKC18 z8_v|e2waxAojKq(gK+0k@$TC4Ri61^6VSXH|fXsw+f-_ z!1={8_ZyN0pYk&}SsC?;(TF|Y&TBtkUQ)AtWW<`dQ2D$&6nDO_WT1pRB~;eHS{9FO zrsSWm_~oDK#rY~n%A?9ZS8Dm^LFK?w0ynzFURn{AgZxdGMsPfOd zm0YM45!p+b_~(eoS~GU+wI54Awfr-6xcpOsr6d2`B;qpMX>=U_oYewZER^MnydYAr zOt2Yk^_%aU8p+n)tPHeW{Oq$8IOOneJ9+5d&+g9wo1>q6TaVoHEuM>teil0+Cl7Tl zylzhq0ggo+D2f1UG(2Nn@czu%{f77Td7`Ie%V8o;e#Du$h_eg4%R+baT6ssHc&=pR z3)d7sPSy#MCHyZ;x#EAzj>!MM##$h9Rhwsl;CHou@Q14&tE$~6fz77ye7>m&zmA7{ zv*db_cNEYmuc)X%!^Y&ZJp3Z(QLTx>Ety%~N4^*BXw$BX(w^g2H z+TSbW{WNR2bc?ruwY)b^!Tq*MF`P4mAiyec8;0R!z`nnuD8!sq0*}q3ho|ZVeIliTWhpAteE|%Q#BI( zy=pfifQ$>J+2Q$SU3fyoUM`^Ou6T=N&rQ-vy1%joKC6Eu52RU7Lx~+y)0w1D@Po<$ z@b#5_LuG0XD-AQazVa9|*izY*^Ig>sA`-IRS{|SWLOF<)uAW6MZ_kr`s&r72Q zl}zNDe?ICedh6HETfbgv{d(lquSaIVT1CTNt<_0tt%_&#Qfn2M(Os=om(R9R|H1al ztdeuhNMq@^38v;`^_n5E6`*$)Z(xeb;XWa-%AX%*CFMH=D=I@+5aS0MWzChrYi7M? zQpqdeo+tZU$Ce;Twx_%DFOm(;IzjSgt`^+pV`unMdI*06JM5%!iR{(vj=Lz&OP}zB z?gsgEe%rTa+J2lokt3xgf%-kD@8jP;jb3x7)iJ%6ix+*L1--_;BuB50aG#~=b#{w`wu?j1)a*2TgtZx{TYA=YxArRnnMTP{4rR?Xb>#cDY}L{UemIA~kDG`)871O>T+WKa;{R#|=nnp0~m;nRcQlejE5I^?t4Y|z zzFAD?5edB#vL-&p5O{nsgo{B2!sE)26@Dsa-&Pkzl8uMPxpMURGWUL*&SfYu`>|Ex z(@q4++AExU%d?U@Plgdoeq_vRkoZox&gVTc=z-^?G`^pJG}a3Cahv-Hz$wk{2Jlkf zRSW@8p$zvpRaC=w)rG7B*;tb^w@A!q>(!ZC<=3teAPU(v0&gK_L?G-89Tv6TH0>V? zoQ0gyOG3`Iff->ZHf%=730*QHW)Wy}Q%S5lMw6n&Py&0VLQ>-FgW2G7kIA4Jpa6n> zch698Zzb{rN8rZ}hJqhfn!E`J*}X!+w<|BOneUaALrD~1vLR}(SDwn7P!zj6q|$I! zAhQ5vWY2eEwwZI(0z_S5*QNy~%%`%zfO|p@3cx@u9N_AvMkHW0PVChB$Lx!v`(BC- z+H{4b>tq0?)HI2U zhw7Sb)lpOls=-UMLx}xjJmHECXoYc8fS3a z@%UsxUZi=*i>EB|ihmyD^+pDH?eLJ->xR=KA9+ozbI(|j#az?XTq?|Uw|*4N<>52I zTr%0HeN;0qa@4_4LPVB70hGMy50xos##K(`P(&^_P`>hX6NGiDpss$Vh1(qkufdj( z^=xGz_^RRD=)+=cl*6!CCM3KdGnop7%sgC{ifni%RF}#iF_i@pdsdK`k^|{Li5im? zDxT3!R0C;s?xQWBfwy(n1^3yG>_c`D_^OeexyW{;`)2St5?&8klOM4as%11J91xWc zA4ba#OSskeGRAe~tGH>^=&TBKhVN#%ywkn%uo`iIR|S-1vNvlWw?v&gN2sWdB^ya9 zDfr>ojaq)}cF?_XxnQ?8_awfO>Y=b(2y8DgECV6ygLNO&Y?&Ui_SI~i95NF zY5$u5V%RBM6LR`rVA?+vunRe(E(tr=t|@YExS+_n_7Vi;5;eO-VKT9(1(P*Jy03+r zg2~YD0h2LQanosk2Q1c9`4z?|uw+9)U#rBHO2yu?zZ?p-RgSj1Ti#5ssT{_%DRf7g zOm^_O$^j%P^wu3`bk4)=@EL%`{$o6932%I=sFUKZ0Rb(CRH!!91v3DCOINUA~1X3ZL~5j0S&&U^MtE1f#)UA(#yK3w8--u*)v+L#RwIQ~efR zg0C3$Z}H1<#i)N-J+v70&+~A&j8PEGA0@{_Fq2-=2&Nt(oFJG-&N`YuSdCvSr;;&n zU!po~?Zf|&z%ysGa(+y^4?>2XunTiOX78K!_~PMvv;3B6-&7cU&Y1HuSxDBbR$`ns zZt7|!M^LrV?aJCNlf?aO{=urXvEl+JOJBnFKXaM4+L-fQX<+h$BJQ6qA_}KxT&PD6 zM;fx?^G&P8L^I0^?kB}`{I@QSG|Q%r@N+x-^=1fg&1S>?i3Y`$N!u=PCN^#q0y{)E)wRp!q<=FeG}+6heAYH~3jsr@I=6jp~)a(%xu>-xTB>RGr{WdjvfR~8j>q&SC)G5ecE0ppD61rOlx(;dFkIzY&e0BL; z$IxZp$bPXW{*vu;&m?x6cTeYwtz0tS+ne*8-6Uys$Si4MXrwPHn(ym3z7{*3z0bJ5VH@sc?Wr?dE zabg4U($@e@ZkQV8h*MoTu$VJN%nnBPt&b0Cz0%TEBi4)fyL2CbiV?%9;yfW`r#C8+ z#yK+ZHS-& zOBkJ+Dqz`DqUoi+)uHarZUkHs`oyfM;*gOX2(^`qce!Nm$>h&DP9=m&i|3klWoaSG z4s&(@PuR;@6Blu5*|@oz6@LtzW;TA%O*Jbtij;y*5;zs?k z);%V_l)J>Ut494OfL%?4-Yd@41xEdTC~Z4gK$iZrUynPiIR4EiRm*D zRG~R{JuiI@gdo^+4uKqV^9y)f=8RCQFmp>M+`FQ#(73Jh%(pku42;tGR!gJHJ^#S4 zGoq;RgD#DmP>uPq92LX1Ek{Muep1edPJigsBQfip%biw;~`Y=3J7i?Iul9J4=|m=ADY2ujO2WI38Mdt{-P8s3okP2 zYj^@q=9`Tlb{l8ceyq-TO4|U6gt|ef?xX6rzl>LJQNKnDr3brmJ?FhvqvrPlt<{x1 ziv1@(VWjr0iwtTRXX(Z`I|4Y>?)E3tq0gwTrCP?5dLX5J{vOr^k4=Xr!lwIa;+H&{ z$cMWWLk_DJ8nu&X5s$rH0x1E0wLUBQLh0kyUtEiA3@O5=d9-m$;W+!+5rJ{Z2R~7# z{CN%FSB+d-+;9D{2(O_PWOpZ`MjR;5%8l zn&(Rq@%G*3zPLu=9=9vsMID;L-cc5MdO>l_I#*GTs)bS52=RUQpe%|4N709}Cq9s& z81p@facyaD@HM>Qm9JPin$Ze>#Uev3#8f8!lr?cRUhuQSS}w~ zelP&`+73SeL7eZAF>!c;ra_}y5_0Z8y4;U}4))&D{WO1^`3cvvAD zT#xdL1q?GNw73z;*b(;o%hx#ix-xdh$4=X zaWIt;=feK4_2}eoK$y~`PK;Dk`b?s<%cd@6WN*1AbFiJQvdy|sX^E^s0ZFhO(xKFN zL2w!TE{9m_5U6bjqgpld9rb%)=4Sa72h5Fc!`plWMxJlQ7ly4v=FGJsfVEc6T%!cB z*5AyTFQ}*BJ|igtBP68BrSkitk^CLM;b286s>{FN(F|TINK5cnIG8f(=kNem8eo{P zvH*ah6uzmL4lgdyB?EKzq-?Bm=W8d0U;OJ&tJV%Nlc z?{zyDL(q_HRQVwTuqEH`9#GCvrO7L$C$HydliYO`W%N zOrC7q92!JC%!OT34g;FOmq~cphX5B723NjihZk|g9Hu_&2p3_^6M3>;WQ7meFy3vL zLKN;_e+R!~t6ge3RRta@c)?aX)Tlp=$rEyi(mj0=`9-8VXtqh=j|{(jS%L7%XDZ|d zrje8T!H?yufwc@b2Eb zikDM);?7dGw0nmn9Gl+2Oi2D$cohh98f3DX-ADV$?c{25(@WL~ckV$cW4BTJvP`CP*ZjQw3C@;l>~9I?ocoB(g`_CQ znG;`y^1J&$LJ-(^Xm-`Bn#ow^K1|D@x~j#xa311`qC2lJkKkkQ&Ij&n3eOHN7GwGse{H%R5`Wc0#q$hUCppuu!wtMWJM%X!0i|-oT1U{Bi#xJwQEH25F7K>H?DzYfOm@x|j#1_r8%) z%+dO~H>eJwDL@d&>LRu`EB&=1iS1Tc|E;|Sm%SiQrGbq`(n9we@#h@gX0-b9~$%bPb}i`$n=*9>P9&oTQ- zsNb^{{bSaP^dNgaWnglBh(^qwv_WT>48}VWXp{I{4HYMc$|lH&&$g$R2JbCo#Dna6 z17*_(SM`(T;AcAHay9)888y;dMxMS&54+;l%#e@E`(9?eXL#csAmcs0{dm)-GT5pM z=4O-vn-2an(wE#kSH7F5(4mL+LQfp&4j3E9ZnD6 zPEeLZxS+Tb3D_suR~Fj$KtPdM?~lSwS*wEAlvEx!qhD}bX=Txjiz9Rw@_Cr6gf>u@$iOXa>kRN6* zB1dl+>ApgmPYv1k6o%~E1HwmR%UD99F#dHk*QBUA`&8M5Pzl8QsPMCzn7Ox7#?)*l z=eZKic*eT#GH1RktmkZ1y-o)s*D`uUqKYZemL~8Da)!eZXf>Y{EPFgH&Q=xb!t){7 zB1`m2*^bmy8gojX%0X+P9Jrv|%b+$8SNA1u642YJ3%62LT9$mx260Fy?GBx`5X!I+ zc1k9|9mi3aH5G!jkoq$A`ReyWcP_usIGGZ+R-VB?Guz)Tf`-y3${*yVP3cqkBTY^8 znu>c(g?&v0RoXuHF7@SR*EwE0$w=~hIDG^z2@ZV-oiR&syXI#h zpJii8D*Mj3NIOPUxP6ZvFvDv*8M z(ND^>Zqnpo{4ldlj!#H9qZYJ+v1sxDVhIS@Riq)>Tv#gb7PW3DEsk5afhD_%rev}K zMz(@4K^+n#LLTPK)8yfg1?n5^Vp&dZg;!`zjbsVEl{uh#v(NqT@jQB=?t7t+Gq)ED zQdSNc@J{AoNk(!lU@VtfHqqAIk;W|`{mEHDQ;zXcUM&&1+={{}` zW`IfYx+low*YY8+_IRlcU2R?}fZyzHHS+Xt506*65Ee_rqu^7a8P4<)LR*$}N}Pu! zREf-0_4GZR?j01dBb#dObvsw~X)*%^vm=9XINq(Pd~m@Z3aT4B*F^?LoC!=sllzDH zMYwn6Q~C5z+He1T|9!pSx2RY{?2g$NZoEuZ@FJABTSb-Dv<?p%AzLs8ntrwTs~Wb4l1L(mNaEkXsdT$Ghz?#hsL5o94XDr}l=BQ(1_5EeW2KfsSQXS;pqqY$R`*2E6iK_jIWhy@NkO8d zyiOEK4e-gnq@)c!UNsi!2f~#XYYr3{_rJ}MLbI2c6w-#v)|$p`#Tvehr8_CtVk~{? zLg^PmiW(7C2xro`Rb)$}JDF$4Mi)kdvC#pe=BMPv9UkM(wWABU*f`W!y}77PSw$L4 z5$7W*+f*30Zy3$O+%P%-(&-wA^9Na#{ISSDQlwuChiqK;VZlbzl6UJ8xZ$o;D41QV zx^k$YPCu~VigtDXsP3{Z=Aq_L-Ex^py4_r}KM<-rXw15ZcOHbF-o(fk6(stEbjXCT zwGRn!aj0%b>MC{({DhjH*^z+S9teF`io1hZ!okwwTcO!c4Uma=x|jrku{R%sc$W)? zan!t9fu?eCl*X6XiLRh3g^gri|C+LJ7IM!MD@Zq)QcJB(#Nr9Pv1?fAt3WU&I7zLs z+1H$XZRy~;t3dh)KZxKGvA$CxnI(}SI}{ZlQf`SrZ7(EW`}RhRAgeXX*|dde+?ATvRcDHLZTTXzv?2`b*2w z-l3I=J#-Q9p3*0e{uYibEi$W{_b2x1{nkl;neB4grEC}Io{B>v_XmgbAF;9oF-k1* zPQA1|e`4?soNC8Uo040CzYkCf8UFR0GLN_h5 zr%^nn*yv>z&mJ?l&IHu10YtX}!_dnyKxivp;ZXx<&n$p;8q*P9uB}CsN0`=daEnoUE?Y2epQod=M(xL*kvonf zs2*TwCq6oIS1nzOJ3T4OR6(ReFjYq8#O7}&b3$6s;0Zxm-LL&fX#11u{=K=O0Z0Iu z)=E3bn~*4f@`pPZ7l0T&eN=y0lIhuUn84+(8wU>gv z)F8W@QH?4O7Pk@;uG7rTqKz4{KB{?pVB_0GW|yRhzsxR9S?vF+Xv?~@AN)ej67sD^WsVNcMr%Xrehf;KFWTw(cST?4_Cr>wx z;?QbB4Hwt17AGD|hsk`XXA%ons~jb`^K#@_rXw=ChISGw^n(8wq#}2JF?;Yh2fD(= z)o2tRPL9}RBCI-|o4&d3P_{}$BtKv0W{7L4(OgTN?p;fv)BGS1lX{j;94-r@_d?WH zsoJMgTb=&+ri!P21b%iM^!M-LXD>m*iF)8+{A{I4U*++$mkrPC&ZF_OrBdhf^Rvqt z+tK*hG7V}|F!Jz|I7K=wLkl; z{Om6m{de-SS6{#e`bY4y@Bhbt6F=L+ZJoQBKR$l09Y1^5uQJei?6x6-Adb$@j-+^w zpDjKsgHrzY@Uv)o{tW|x#rCCE)gy|VB^sn}{O}}Z@DS=UaCdl0&>SA}Ijnn*tU7V`*^r$R#r7Z|+$FO%z2Zwg-~ZQKj&XcGV-PNs1-S zb6Y9A5gm2+F)ZPP-v7D$R<)$@3nDoU>34Jdj{kf!7zOFYz=U@>J9$YDVm!)KB-gV& z=}1%@od$CHD1v@%LYy`vg5stY?{Ykzs~(Hx5pVINqVe01=*y#Hmf%2%6Va3=qjn}R zMP^CkJIFs9RczG8$TFXYTI{h>c&P3AA(R(O$Podv;H~r}dGP7?pVFb%&p&x}v7&4; zXC9L4dZV^TpH-AB$hoy^0HrogTDV!|Uj^c!Q3!z%Vd0$vN$Hce%$=zJ5Svk7F<3lccdkP_7KbU`v2Tz*_C@V_by}*fdR08*c3+q75*f5JYUxxt zqwo>;5{_@O?{p;EX_Ivu>?5C#R1^x7k5rK?<@rc8k}1F;d)|wPzf>KN_CzdD@t8_Y z9r5hl)CvI{xq<$X?39jueoT`rgxtSfD8J44N#1@m`(bWI#5u$822Z}A!kPofHBW&s z*m4o4yr5#+@P^bmEWz-GxjG-=C;1#B>5k+#btX^UkLdhD24sepU^Ce3!)y3&bp43O&>G7A>EDtow=$+4DsjaoqS)J$LE`kk^}DjEMAu=kZ)sJVQi>Rnzf&bPxl*+ug|7=b!q*om%&hSB+3Hc@>k{>-@b!s2g0E{nE}w!m&6LlItS^Hv${=e- zlD;5szK@QtrCtvYQ6tnbYCi$mJi;G8GNR64W;?#v?WIxkje?qyuMnSLu0r4Lc4p(K z=ozy!LC-KP9iV4YGwAuD`Yd`zvNi37j?gpwj6%=C9<)Qx2L(N6<%`Z>H{)c4wu=7ZI_C>IBcfrm$Xenn8{x{+0 z>w%*q;b#HOS>o;FbK>U-8RD2*e{P5fv|3ZSs#s6%hiY>3^P!&P>&g9)#p%sVGr8h~ zQxtB{i~0!*t}gy@6n){HCku)yI^G6Zn{e%bbita`TwMI%}jbk0eLrmEUstjam0iidj={j#yv2 zgS5LPEn-cbPGnv5Y%W)^Ya_v(6{shz5&Bbe@32mZb0YS4!MtFoh;<-h2YD3zAIZ0` zfF2Q8c}-$B)#UeiE-@nL08DsVbm#=71;4M@&s(2n6e?k`PC(`FzB@Wq#6tFcC1RD& zNGiT8pzxnRh7uq~zpT1)B-UJKUtxzvDl$0;m83i5_7BB`W-}o3%IzOJZrS#i`vw z)_o=E??js$SlG5Gt|SLFCkD5zY~0ojH;S4+kQ-dkrE&lLs;yC|G)&<5(N?iVQI-Sh z6mcN$B(M%kJ5CYD{+cMz5SUTDl>h(xN<9Dn6=Th%jGu`^O<2b7+}GDlz=GpjLTqoU zD+6K#x=YZT9U>U91cG}{y^Xsg^$ZUl&_o;*B{`i&t&QH6b1qy*wpsH@mx@08?VK>5 zloc~)&QPFK@lWaF5vN9&eDoK`PB3b1inG8WG`L@!B|UgY>G}EgTNsV?Q-{Av6Kb(4 z271L8Qkmbt9CaL&^z~Y&d3)|mjHvgvJ?FXqEDd-=kQF{;TDTNQtENY)6{>pH3 zDdZqM6hu1@L8tDsbjm)ykRd;u^IBirSv5`M6-(HeUx!%(7}NNGGcrt(n1g z7!`G9^mTs$8-Q_rc10`B$gF0?DJbJ~unE##&}`shUW0!Yt=vBMWm*?1emY&hhW~#4 zGqWEQzR5WQW%iAv7`Mdiarj4upK9WT&DU35RAMhGni|@pvK>LenG$0K7@V1MY#NTI zZ?yYDR6+U2erl&A0`*4nA__&E2UUU`^=t$nl72kt66g3nxH?|tz{?AK9WRIE)8fCQ zW>pEN4VG%kqGkGE98l%HTatQ{Ys&#ZRBP|U6kj>|NSqE0`Vs~C5lc#b`s$xv z`sWyage?7hP9E0zUIyzJwZG9bF)Zd(ix^1_FSEa8d^;pn^>wcF)p<}QI9F!Im$YceFT!!0s>T@3LC+vEyk?B%H%aS0itbq947ux;w-amrAg6f z2A{^P^*Cq+p9*kNb6pG*P&q(tQGPQ$sSKmc$YO38AoDXKQ1D9Xd(wPO^Dy`KGm1J* z#>wFL;*R<;<(v6^IL+4w^yTIIMbpOkzrF1U-`?6k^X+tBHA~hs0bn6E^H3dAQ({dE zXqZ+>>V8yhKEX;6dwfa6zKR{Pz9Q>?4+T>8$oee(h5C8=w-E~`kM{V!NO@&@R+ORH z9I5#(^c%mxQ)p$c(C-})oBg^sTmLfZle`l8E%k#f^4!92>Q8h*_7p2Vp7p#+{O- zMq1p1zS4+>7+#;HpRS^gI~8@@*&%hbPm#Ip-K$__x~uAsb$tLrPRG@DvG;!sOg z{(Cbqt}^f-it_ejYc;;s{s7g>=ZkNZ?+@_v1TTB3uNtyA?|mNst<&i$dO6kj@2wO6 zy^0Tw3O#ZbNFM5RR#H4#q>s_`MhXk9s;!gtMedjL-%Cg}{#A74wjqE%VU-Tw6w$tM zL-$JpVQx<)kjaI=Wnn&Gk;03s}+BP)iRxsv+2G=Rz5zM zxqUu!9TJP|o^PKg+SCO_-4}fhWpYL-qPqUW=co&b;DSX-wZI8Zxh=g!g*_PDdB!P&j7r6Xls# z9L0?p_C$5&)!=pH6Aj)gG8t$Q{`27R2DRoP^p(QM#OwioKC!3BTuQsJGh``Y^AbJv z9Cu1B?=BM=#ul<1{oVC3EbJKv=T8lK!a~H1Uu-gFokBIz2!LK5b$ag&6CL>nV&n)y z#Rh-g1Zsuq+|q)&q})O!VBNPV!9mo%b}z#9_`rx`N($6*?kww1# zbLOL3hgeErT;oWa5$E25bWdKS7TTC=i)$MqI1QrD4E7XPnR((o-k2A5z?M5uuDr#~4Y$ zj4|GPMQ>RdaSF>LPUSNTxv`=l?AWUrUD$fnoe4&xt-EO}Y#sEb1{XY%PjkY@IoFnE z!v{P-XZ&1C#2&FrxR$ARwvQZ;IA2g+<8~MqoTK>hmon@iegIpgH+JMV>+k`%wpf33 z$uTmj-aRrg6Dp2nj9pCYC8K^Q`g9)9%L*p?doRY)$qUW8*Ujq91&IN2s6yJ+Pi7`D zG!s@lag8KiEsjxu_IZL8yxXP3sWHS5>)iAJ`HoTl?QRIt*+ANhF8X zw$N;qf`cd7+p-bF8YUA>IT!c5K|0OOUnk%WN>@e#Z0*FiF~cR4ZMa1Wa0jGEMG4hu zB!)xiM8(8*{beZw!N$b-QE*#ZQ>1N8Vq1E+6dX$6fN9V6D|bn)K&N^R*QLbE ztzLt!sf~q4?55#?x7S<3C@5(5b^7b{>zIfW=Q<1=#O}oz%@MlaUwSnqJH==Era1jI z$-9p3#@R^6(ABtgPBLTc>?fvxrKR2Jxy^J>rG#BvaP1Z~XU6V$+0s@>>|2MIOk zKFza)k>W-Ym(mFrR`ZlmMfwwBul5UqWjZ1^BGdR_(`%Xzz7F}p_^auGiboK8%DWL2 z6^(CdGc#vRK_2K53`_>0gH6i>TR zFLJf^7flO;pYqk(&)1uw`oX#`S8Km59b@B5nF(H)k_O98_5)IH8MymX!fPXG*Q>R) zSfxaTZ3bPfZG!H}4i_Xx^c<<=|*ssS&ABV6$cl+NGCZybtZcQo-H?8)V)`buX3TuF7Tc z2JoIfSq<jRlBHa%QI$c9O5;B@v?p8hVE74o~LI51sRK9!}fUnc}@@gSq1xI-pxJD#zy_uQrXzg{*X0^*yTnxw_AaWFn}(YygaWx(k2Gxd_1sEX#A#J;^QdLJyD)A0zl~ zd$p_Qv@x~D(a%Xni$pU9WuXHLFYPLU($+{&dzARds1RlCJ`L& zn>nkSVlLQv6A5eH>gr~^ffs7&eZ#OTEs?6-@oQOmO3+o<(#Py%Wt2Q+zumbz*_4gQ z5+xX3sg9D`Ff(7stf%o2nYDCtJe@vLBXlT!5xi;Sejhs)!rHa@C3v$Ke5FPX-|nM!ZdNtst-1X%n9Ykj zcC0Imv)`R&znio7yHGdC?yrz0hIUNoD2(@sCT6{v?xpdFa!|KZW*1^CexcSiY%C2S zl&wl%Of_|1vQ=dFM?a+epOO2+%O9U#zRu-W>l$$sZsGD>(q9b6jIBo`F{nD%pW4I; zgRtrv?FRe|UQb)sby}-p@$t(`~YQ$=* zUa1=-|2An#Y}a5+qx4wSi<%0~vvFMBb$U0LD(nbS@)@i$FgT^x3@v=WQ7VRf zNy(`gn3tNe!+b~?HQK0;f{lX+*S8SZwfdumr01)Wyd%_Ln>4;H)uAp@mOR$^P}c=i zx(V(G-3!pLp!?2X$^lT^|seIkAEk+cD?2@~@>nJUK^uje)@nQBq8p~}WAt#9x?rl_+94tQF zts4<3ofvZ%Viwhnkv(UPNQp9%s+_p_DsKKlQ8})ca#)@Ez3+|v%blS zi;(34t1UM*MKCk_$BBM7scHKH=~zWr;m)hP)}V?H(wx=M{q)|Sc z_A=c7KZi`*OHwbVjS$~K<;_oOb;Q0mo$hrd^!=jp0BX@VOLYgD$zT-C%RxDM!1coc zgv)?jniQsck{-XU%s|L}G&t@tt%HcJsQF8@bPDL+i69xoxcIt5NFS?ud**=*teo(T zW6q&6)Oe#n`hu78(e zK|=03&BwkM!M+90b}cV6U%&GU9b8?wUi2VuB{39*nLd(ggtS)(eITMu4{q;VG7>j_ zbNJoD??HZb6!B+%MYL@cKdrZ-H@<%j9pNw-IuWz2iDJgt+@8pe02VkCjYUc%aVBP4 z6G!+_)@*B{Z?-kjH`|(6ZK64T!T#)}5of_wk;KF~WSiq>yGGCCqIx|{ecxNcTO_$i z5B>2f!AXC~MN9Q4(ZsoVId8=2p+8=inHMEnp|^Sy*rHedh+l2r0Ljhm3UR*ogn2C5 zrblv-$3uU~MIHQy3h^dS5c*3l+Qol`MVvnt0QQJGArQ-2oORSyA{Q*dDT}o?JDvUN zP11*8SFG24?N?WIkR{GO8a7GJ#K}<-V!1XMBeX2OTKl!0_18@>a*2A+GMy6|{p`hZ1|!rh%O z)L>5^NPU|~y_KbB>QR~g2758(5qW8WcwJ&zdv5t;zSG=i2)j-layerRhw6ry>775G zx%NK#-?TSRFBFn)=JY=lHa-$;x~h+0h*{T0 zul*v?onYev&^j}yt2Jym$D!W~gyo6i8VGBZTV|hKjPF+HLxAv<7U1Xm6 zcM_=miGe37`}87)y&bc+me4}n@M@yRBPlI1t0@E%y}9Lvq{L53X~%Un@g~p^llW07 zox3q`#iL1JwPs-yXSta04OVOEjaNip)G{HaSejW!hjwfz3^x6OqH^#>pFqDKxsc7x z#YBLZKh@S;W_iwFC3Yd}p@=F^c$uD~Hz8&En;5N^x9}!scAGT5{S{)cu_fI@8-!Jx zHNMv1bno%q9dS>=)CzpG z-F(OqH(ch=C%j*}r{?!>XSe>%rhlj6Pe}dC4f~MvdHS%!eQ^3bi5|Y8X1IJZG*Lr5 z^a2gBr9D18HqckWBhY0Kef&%qK-eKn)%zc26lZlE#jkW7#eO+_@yByQ#sF#W@q7z# zS$iZqo{x3w?|JkWkCcz8zhSw>O1;XKRfZoi{fd4m*DpggL#B6TCfZxVnT03!qKnSj z(=#*4I1f!NH@(6=Q0l+@x5|{fN}IVN6oqIiCgP&enp-}ZpsL-dqzJP7se1uq_;en< zdSJ$u`A1cl==TF{%{iM--;3mno@Nj_Q z0Es^O!}B9EBa3$ytFvpNBzGD@mnD)*iO=Da9G>lhe901VdbD9~xy+znHRSgq&TpY# zD`8|Bb=?rC(c)bMXDAN5er~C(qSu)4aH-gUVB_t4$s6odds~znlm4(Nq|NJ`x;L5c zd^qITnIsgH^W;tE?Z1=7hE^88pwZY@CwmZ|A(m#{&d|%0wlK4V#^U3T3Cdr@!6Fv% zCPH-{(5{Q0Y&=y`*Yz7eI9kbML0DVG%#fELOqBaS9v7zpiUmuyME|j^tn7`UoN&Lj$twX=h8wu{?|AOe)V|60XEs zqsF{LV+$)Y`^hLgXG}cj$Q`*BHhquy(8Bx5>v@W!s=Fh-z_9n(7RSG(cu{-NmAdbz zFEM9W6mOT>GZil!E2tOUxf*UNJvy&_**=-WFwrU&<2d&FjpZ$5u?Hy{4f9in3t{NZ z9}0K0!gRg+Hsu^J^66%Ibyf9wGU}4zcRD){<~M}j3H(;`Tgz_~zjl6qeYdmob$&fi z@0IfVI=@jnJ3A-vo64`AxUUgb$8SEr0>bX$m*n?neg(TaJNxlFf?qkm$M|jI_Zq)< z_|16_>k5AV#_vD*{T@#5PyGJIZx8_ZER(R2Ir)8lKjrsXc%6~_D*2tp@2~viyh?I& zDZhaXaWC==;5U@t@A%!pZ#BOM`2C)=E&L>Fd;IdpU-I_9P*TFZ2H<-be|f{g){g^6hDStW4$7%O6l@taoDW~ieG zSu7D{pU)Hi*wxzVhgo4N3b1=POe$Dd$HxUzzh~wQ&1XEn{{E_+VInede+L_NdM> zoa7jhBa&DXfZxXVx!p}IzW+%bG>Nd=kXQ<1jeJqf#;Qq!&#+NwZON)70e8Gh$%(3R z@WC{b%X)Fo{41Mh9eHlCdEW3gqcX%({mU;NXpB+Vr8iY2&81f=h${wU2jIVYxz}y} zYqF_;ieOPP2aU3>e%N-GeZaI)G)vp-_o2pPOMdR9=Cq6&bI%a6dE-n1W2GV2XFE?a z>6c-qQ%@StHQB5Y$924CI+E#xZE?a}fags$3EuE_DU)0Ar>Ce(_r4_7s6OxCZCUEQ zH=3@eGT#cr21307nD@2{wlWLCnLZOnrN z2m)Uxclx(VKG-Jz&@SBM=c=$>=+mt!?L_;C;msjQ>JDH?rK;!vRm6doq<7bCwq0(; zX5ma82$raoLzHHTpC(~pJD7;nWkAOEz`*D*RUqz8=`XeERnZSsh^c5&wxTb0tLV6F zn&Yw+nL^hRp6e3v*iq!*l54 ze7OS;`!f&HJf6vk5zqOkA{FMIYiux7&+=QXrOYy(UF=2JNqoQl-bN}f2sRnx$;8YN zk;HiI=C!-y=eU#RvW1smsAGu}|AR+W+x&x^R&AafhFwfO`7k7!o6j#sr}zC{Oscby zI~L*(1&+DyqEr=GjoxA5^=1>NE^5?%M%A$JdZTElJ2?$!I|-G3Yrv=yB2|+n*A2yJ zg&Mw<l$mpkUTk(MIz8M}5G(YsN%3oaEY(W$G8ML^t1=a>Ne|BB zw>ZURwhAe}OdT(UX#DJy^v%|Z$E>hB9VZ^<2xA>QCEE?#&J?|WNnSk4 z@ZxbU=jqENUDTbvZ!aF&KL@IvZH@sg_9F*YuPPkfP){ZEOEUw}crz0<-D_im#-(uN z4(^HW=JDWHBv{F>jo%ae`hl}Y@;jE_7=HQV@xNYw(D1BQnXc#CA7JeNC(gI`%GUj{ zTzxogKl}@qvb2g`EF!IZjet0)}?BXQTQm3WID#vHTH5VT;U)o+)hD zKUUZm@(Y_?f^%Dr=)uBfypg27AJdABH`wA}e$q=!y_aWOYC17x|D8A`zZ2;b#8J4# zE4lVK3zlr<-ceMknfjey8Jl3iZ{G>uog|iJr&mD+mY3XmoaHCX{gxxR_emF~B`2`o z4p)|BhZlX%?gN62Iy}p9&S29PULL6CpSOecQ_Yj%>?_{d+IKX7hxiSM=vppaYh&)c zRq2TQVw)a<40)MvyvPjKwb_L1*_UFbMpH~VBj>2 z`|6T4?X;#KJ;Ju?(k`t!)iUEP%5kI3(L#LM6x~`Y<}QQDy41~?XuCy@ZrxH8-C7|h2(Nx#{NlV7 zG*V5YC8u(%Z#6gM1|)a6)zzKs=8B~lA;Bwiou}b?PKT*ACE{(HT`rJ6s=p%uYrcJ5qct9~Vt5 z07x^-yc6LCfYuKl%|eUd5fR;4S6!yD)=ZWYFhIlws5}8)OWpX6&T0TM?TLS&7r?N~ zm8fc#`>9OVo<2$&DwdnB---xi@cTc@OxI`fktCOJ0tp4<@9@-g?F!%S_qR<;U8-qq z_UAO8~rCeru4@o!2947BvD_{0vkA=HkIrI;SI=BxsxGn6S*otZ+_4vf&9#ZIWONp+ z71bP!mhh-iJydzz!6OaIuVaqf@+s>qc6z@(nkwvRA!PL>2pJ4bi?>?{n5dX`hPq@l z3in{vQsC7qd7|P!rXN^f0yM9`L0HUMfj{rb!(T6b4MBFfohL-Zs4rNj3*u_RlRo^8iJ>*V}7uaCXp>+z^(%h zD?XzOUpk_}I|kvry+x+B&v{kq0MBnJquXRU<}|4hK79RCYG{=VOnpVh0b~>8f3ECq z#cT~}o=Rb?g6+6_FPkLumT3YVZS1wHcGV380X~Qf$4A-LNW=3VL<-pusa@`+NNcca zFGX0?S^qJbHysqFezn-^)Vo{pXAm~Oi9fPQghJjpsx=PdSf$c2tL-p(*OAC-HR88q z)F*DYg3ijOIm=JucYHe$(l(qzu*UxjCVG8p6jjn4bApa%x<>zotAoO*%E6(O0qMBYRq0&4MtV7j2PAAR;)^P zh0fe;nz?=&zf*^q7JRhPJkkK*Rj>beT1|sS`bB|m1w^HeXLyYNGu;FZkBK1~+~y>< z-CFBPBZ;#>5aU9o;RO(6bHq7^-MaQ|09cPXYy3< z_R&A%k8QH`vJ_2lFRy<>LRU`ECt5WzfD7Pb{v?iO102og#nHSKEGd43wb)yE%iCgx zZ`&+9H3G;3EewpeF02-;V{L$&DgNR^HX2?zk{DltyIWC3)|2t34q-^h?Fj=Nawily zFnCokKVW(V`1CHdC)ncB)DChkBwS z$p)oRV(gN%WIufjEi+@1^~?$6)vr&7C3^e8EGVLGj?=m5Gy&u}Z~f8?OSF042~Q%S z#&9OjeT!3c~X#@f~CegIiCF4xP1j%!G6`)S1@SJ~pL$l!LAlgrOhgFc% zFwU*s>O&e&MsS8xB89z|q+HJknatAZ#+dJ1O>4h9!*GG#!s!J5bt;vtnRT_B$ z-$B7uCvgD1X`?8Ue5|y{h0v?%QYAXXhtPFnjGvLFpOGl)_NsLk1ax6QpN>%H#DMu1 zb!rfQr&ZQE^AO-Zu~tW=o_H@Ti7!Eff~^ycMx~T$VP7Od5Ozsl{1{_qjT(bpN&&I^ zE@e|S%Q>5alX=i+@hr=+-HRoDRm}t9eDE3wmgW0p7zi1!hO0Db;BA{Vb(2ja{M^Z= zO5XIxX5^(%muO-Fh|she^FkgffP?JSY){$(;4)NzTYQ3{MA{c>T4B?g>XS`N$zs}- zY+90gZO*;c`>)BSWxQ#@@Es*)6e29;Ei!Ju<0u8@Oc=u9XIadfa z9U*+h3DDtsa(@M93(#m)ye;rr9$e7z?xu167uV}OtjaBcjQ0Q z5$6`$344q^u|^H@Qb@e2)KMn4;^Oy@z;D&48Ie;7Cp5X$EDD(9rw2 zDdtsgoYD*-wN|w^mlzo&{dMsXqFmR&%38TUGi!rQToNvVAnn5>FO6=nX-qaA zVVd~V@d_||+;?b;o4nNmfs?)7Fwp&xS6Yb-q_XCooPPE9cijGo6m#SUu-vhJVn@WISaMdlE zQgWPOJedKITecCvCZ?Qq^(IN0u$85}L=t@up>WMPZ%bvbf*noDTF4~`iIq?CmOkFj zut9vH=bMUGdL;G>lJ||9D2th^a(ApPj8CPwp{foaY8TkCHdU@7S!z3T zP$l)FB#j2=$%P|7QrtQ-{_=cahT;cQo2vJR;+gN7E)P?=?n==Ml9~Dc_k#_{Ocm!) z4B_gQH_^U}o_M3}yCF!Qx9=^ak*?teo4s0fCsr!dJ0zFFE7ZH2PmX<3Okh6U#gsr5 zJ@Q2%#jD;7ldWRn-C0PQw)V3nFIUo)yr+&8OYN*n^|#NDQPWj^O#xfeGpoDS^b4Ya zgsEwgYP#J39)~HfdVe9#3dV~PmPEQ0w{*)tU1?ZclHPQiCS7ZX(v`8nD^C7n z*J1jAJhm$zxS7HXgl`s%Xu;z=y=VDnyRy~<1J#OgHR{Su25(9C)qbWouiUTlGrd`6 zdo%nl+ne8#*)ltOu{NET;y;`&u2FejUs@$5xB3efaFjpIG%x8*;hEJJXY{?~-DAbn zLAHHSF|KaEC1$CXv~*x3fgWQoF?CYFjg&c)%G^k~GpXFI9_D0()O&M6YRgEqhHJ~Z z3aPQgy*UZ>Cnn9k-6T|otX+aZSZzDIH)o8vGPt0o{mS4qEoWRA z3C>?9#_}Y4ME%1Q%rpCjbUrVqU^beLJn>~e`sB$mcL7Z7CES5GyA&C|q-Nyb@NiQ% z9|dn2?>Y}{u6O9o6$i%{_|{(^<_1JQ4@GLPloyvh-do4%Xnvy@0|;)5(IK(TH9+9WTzY{IoK<)7xYpY%l{-C9s@a~_N_xA%vOiNvK zbVeSu`YZR65yA5}y&as|_IB`uR;R{uYPLHy9m3XQNQ&Pdtj?@*>UOIekTS^Z7oK+x ziV;rMk5J-W64#ViO5Ytu8l6e{ZuOh3h-J<6Sl#IgUn6ftG++z#LmQGDFXiBZh%C42 z^+OfXH#uJHPkqFl4^nb_`?H;stMg?oCA9u68n&ZD+ojKBjdAagtm)7f8zUxbBHqzC zInvxZvZZm8Ig%hB1_j*P<<@qXI%N^q85!E#{b$%>rgbh`Mz+2C zWA$Sh5V<{K?rQBWdL6HJu+W3b0#uB%0bU>3d>{ zE80ZqFE(4R6{*{h0QiiWPc*X8%KLF~z|>53ZVfd{@7?CjLOT!&-v5r)scj_G^I4Oj zra9)$!EwknCAC#MgN@R$v0d`Q7>IXb+VXqg5Y!49^^?;NEbw~L82r_oUnZ98;AmoY znVguyavfy%^P=wg!_b#8(~{m(y+!zEBR=n_yI4D<=tw6I3$ChJzTEg*^YFKZ;$(#s z3@7JRG`F0JZXq+CS(mFGUH``1{%xAhO<56fxrmkGuO|gXdVl$xf3Y9U6l*ozRBQDX zsBR75IOJmeZGgsI<}-cAy$_Q3uJ}9ZzrFu4QE_LK5fBtL0{vBDw-I1d`&I9+X5X;l zE4SJaNSe=mjzGKj4$aJrz{R|41TNwU1Q>y9%7E}GREqbzB|a?RPbrWU5wR)e?Dm#D zj*^yW??r;Ga4URxUH-PTZk}OV^W^WeLXV|ZHZ#bFL(6Xerrom?)DDn`6;uJpV{U#gQ=KWnc2PpLW+0Z54 zP*ug)P5G>zNiR-Zc730g`o`dW=nu~)kUWNB51}q`4iqtfS9P}!MOppfwA8U(@>_bH zsQAi6f4k5dcY=z_sJ*(=ccae|J&|pRWiakF?UiNANn&m+tHUjE0pmk-Ke}*6SonD2 z5Gpe&@I3C~vf*m=Ufcyv-J%PVIR<02N!Fdgh&o4QOkV6J#vq!vm(njXY`rleT!$YBPu-X_439;)$pTmaH`5k?DUXX9A{$l-0~DQN%1WJ!AWEi@|N z{p@EqY@g#R1CuqVwqR(JHF}62r0h~Tr2!kI+5v$|ir(pL#ygz}^Y$>FjZX&~zXd_+ zl6)yVNoTuFwgR%S)Z5ud<{I(UwGf(Ow0CsWy<$bJa|M1N<4i|7T}xxk z&Z!K!;K&(QqmLS3*kR);&hAWm3>ajqKIfs|nt(X`#B{-NFmrF%J&mbvERLadU<<+9)rei3c6XzvhFPFW^qdszW9i zCFaRdWA_3Q?aWh!Ofbf6I7O083~CVK@IC6Q#Ew(-Oxp|1Zh+Ke_1+-)mOOBU>Ne<7!DW?$26dHp@djbXwON=^vS(>!)8>P+ZMsNu(I-FX3sC)!TzJrdL4 ziC>CVJr`{Jj?gkuQ${$*^^(sIa)lh%pXK=DQ2hd*K_18k8VoY4QM=bZC_}H{kGV|_ zow?5+N1ZKEH<;s(x&5Wv`g0xY4_@it6Mwv(mc(z2Rb9~*7Y}|2TXg~P;H+yqyqn>2 z&2?hzTx9pW#DDqwyKm=!Kp(cTliL*t1a7zdB|K>_CeF6b$s^p_?VYXel#b4|Zu}iq za(CV~%m!NDC>y@}c$n$d7s?{N*&9B@nW-Jn%=T(me)wN+JgSfUr;Nv;8V~@+MWEyzg@A+gs5b%S)dYs@{pEMlU-md@DFO<{K&7j$ms za{7pKP}tE8rewAf$9WNaS&LmeG(&%kzc+Kn8Gk}YaT{DWe_tFaMgFV8DM#Hqt98Gf zdtF&!(mkuZaBYv!FpFQiBhGn!oe2ZjTr&0w{`!kGRK4b;^MMy0D4Jz_Kx#!z9@48| z;k%TV^&5G#8$#gM4#c4`QTd`4 z_7))j*zq{byYFxVkMyBk#^2~;dej!an!%f+6rDGo9}|DR2fVV#?90E}pGO4&0%xr! zUs3~oTOh6DX0eDwrq{d_nO?gsqU#{1PxE*_Nvu}*CCy}r3}=5_Ts3B@7w7a3`&@3hQP>+_8`TR04a& z?+-@C`78;%i+1;fcCNQvtK`I2Vu`DfTHo-Fl|bTF+(8Z_-I~w6q}C0vX6@do*kK{H zzAI^WISKXrwKAYVLl5v6tejMY$7nl4{6UU0vFu#C_kmKw@O=&XC#G(39Hwrs{+=^A z+tFmrOR)Uv>Py+KR_$Ho4b0y>N|>j=^Vg4%^SEISKk$l!;@)xmD`RdcKrg@pu$4D6 z^ADfBJHp7=xAme_*|kBUE&hER`ut-2_Uz=!**BZ^$UcHT{wZo&C3~1}9RxVt>1Zvf z4yF^3!V3Pi?wd3Y)odd$kmNI$69y1$ZYn}p-^~@qUJ-57$ph2KZuB5C;w&idq=(2~ z`?fChBIaI7B}L_g$KR&5_)lT^z=}N#Jv6Vx0mR4LUzO6A*zvzom!hp791!I?^WY8ln=;l+W@C~4N$oX8J0CKa zge!i=Ik7~=`=60cYit2k3ZK~HHPuR>x#wMa>?Ty?;=VsL2X?uom}XoLYoa?`1Q$`4 z?<*myiZ;{lqtnj-J%<(GrccWrBalO)^*p_Wjj8oN09L0*x=D3YvA$}0M)YEDtH ziHcbY%P3oqRT!hd1_=W=Or53yufNq(Th01pK!Q#E*wjZjAI>)elj_T7(zpenWZsGh zw{KF++qYfpaM?Gx_qT5y{u_f+G?xvRl}a8-+@x?3_(wr*AFlAR>QvXW0p@MnLk079!b7VFf6b?1L1%tfT|Y0bqB(M_jGdCo|T>Y#k(10LG4dhk$F z;}f<(?aJAfM)ao5;)J;ij>@L`alB5>Cc9K`%#TRI-1|w~HMwM0Ws_+;wVVj?pC`FM4g zG_tvFEdEb@{@eJg;p)x#P}5Es?eEU|!wri6aC81r$tl074~3myg$>4Abh9@>a2L-K zYBFl6TLd^;K707Q->qg2$20a#?R-T#i#9`_k7L_i3ANN5*)WGY3ncURiZoQ}%4Kb| zHFOk43yqSq0+L_9#&oF;{>Ioik=>N$CZ3=a@lwNE?G=5I*7tfu8s00kR3SKVt0J2o z(QGZKn}>c4r_l|seqhn(jZJv7MffZfWA9>2xw)Ov+WFC+OCWS=WjvFRVR4HdW`ANjzQJKGu~nD1&1*A z`q3<-BD~Mo1_-#PyM0qQ|ESb3Vfdj3OW&;>J$?G*XJqF5 zjWC~>ja%IJw|S)JR_0WWn(gq`k8O6`{4`_nfze z_|#`ugnR;*TFE=bL~JpW^`Mylu*H}LY}Z8y9S?T>=gA86&uFDY7^Sv2HQz1!P9)R2C*AxiB{w+*>Jw@g0g z{q1!#C>M0E`!5PNbzh?bF;8SLy<4^$SSlz}_qB?q?gOcNj^1^92o?Xp6ud@Bz1L7- z(7~Ggit7~bHd%D323?wDlSJ3;T8N&#-ZF3Rqp^0HB8r56FbeoNCa5$osD3GMQ>ctR zetaZ=9GVBnfT=5C%Sa5fXlWkKM(<|eNv7px*->U6!G_g!FX#bqpc86E%qcCLj+W)zyrHa(1?d z-Crpk_negSaGR7IZp%$5nyFVny~msKou&_r{aMAPBf3H5Ee12{Y+w+S$zemY)hlyq z?s9EDZVqI3194`B_v_a*>{D7a<{XQNw`#zTlWxA^IDtqUTLx)=LaC`z<~REXZ6E_o zI#z!Ksx)s~e}f4dluJ`ukzZ4!v(7a=cWRp5CiTgwxzTou>kP@dlxmy3!L}hw)eyEB z;y+YF;4Se1!x1{6-C$eOQg4>6X)Bb`OkUMgU?MA?KfD_xyfnOI=X$^=xP^v41X~eC zVfQ;*#?MPhZzvYr*L|e*6Y6%GQ0Kso$lOs?JQSPN6qF+q!J{MBDBOUdhr(N5Jb)^w_BD$@`Wb~3dv(vuW+gu;BxILfbmJp~o_ zvNaY5&C}9^_u*+M*m6k}w9<7h+M^lb+ACeDr{W_u?uYcbTLC3%+(&NcQ~}LRsD9lVdTBgpku8v^VYtFtrCIH#`DG@vW!y$8?k9d+b2hG> zu6hHBqYO{)y_K$xbf!gX%)EWKw~06NHpP4D+-$s=On?Ww;{<+;+|gAq8pG{JF91WCYrct13EZgUurBZdw9Pw zYTVJJIBoX!XlIg}A7>=-1rV6gUy`PKMZ33zB&ly{@Ug8`NW7m|o9h&;@ub3F)0v8= z8|seQVjR9GpwjO_reCSsh(?RsrjquTYFE1Nq-6MmN(!RuTPC>|eNY)Pl47{|z38K_ zaO%YLpN@7I69nD=T6B5Dor~86b@sSe;v4ISe-%OEqwjB~jx{Q8HX!DrnU@ z-=KEBv}p#(z@qVpBI2EmT*U|Cb$aKMT)2GvG&5`UTE?q}B=rp@MEW&#AMn59ga2V4 z)xPmgr4ag~;M(B^*L)NU7mIdvMX`2+Vvy7~ypcqwZxn7>yc)`P>N*2j@JiAghJWpm zWVL~AEWv3SFsPbF;$j_O)2hNHvRaJd9^DYWL|xcn^#-Zr!(EVOpsN77#w>! z563)kO!&oQ+Gz3X7&>Tf$j!&Eh||M6A7JNo7VO$SW4*yH=F?6uOtitS*+Q8tN-1hM zQRZGPgHq=kl*-Mg)K>A8{vO-jIMh#aXa;?P+FNwy5Ts|IJBT&sgJ=XL>U62*EG<|#)|JbL~5)%ooXg4EAQu9uNdbJQby|DAh%2?R{XzFlthemXv%t6MLRCc@?L z?gRd$HUh)dx%v~A9#G4@*7ro*Anl!fXRK*kT_5bAO4G+`KPToqA8FX6_H8l?S%e*& zt*^(v{(5_Dk8fh4hgd&f&90x%oAvXhy!DekRIQ)ktLaf@0WFUuc+Fe?%=+2On=Rz#sIkv4 z7@t4Tw+}Fnn%6S3CvT8(A;3rVZ%9%5kkaAoQMj)}V)s zc|nh*JPdjq$QD5q|1_WJh8lo8%z!0k82q>*>V9vI5Uc88izCKlB)Dk12DRxed}Rqw zj$NXip8erR;})$4`$CcRGj2}(oRWkZFB!%-$3N`@KW0S}6SC;hbv$B;Yrv0}>Q?63 zJNvh>rj9ybw!Sod3|nO?<~vm!D>-3+RJz zus(HkH}vuCA7Y7b8~ovpjWb3>tG0AOo;M8g?5^u$k;l$o%*pmgUmo5keOW)_N2y|&}<-woz2Lr**I!u3!%Lhwju7k)dU=uEfL>YX8@iF*wr{7*7&#t=O7J0N^}ST~YV0#}uJUE60oU{0lhozypt7kLLwH9y{X4vYZrQ zZCbW)cL6kR#_G_01@&xho)+OGO}el1R64+~sB@k3RO<^IJnD$-d?GTmBXa-Ml{Y;! z{h7}nfF^h&YI>ZPR(!wlzyj<{%6oTa&TO00EZ8$4i*^OBas*5|n$F;G(Q0ND!aidlRDNZET(uCJE$&StmH74rv#r(Z8Frp;c>XkrG(Xs3022Jg<{3YYB$H5#&d8&$MuzT=A8)G9)gMVrD2$Bl z1cPT!R=yGg$Vb?vtb3_-8$I6|+4*E-=+?+Rc|G5Z!kwNEyt%>lyicfby*iTVanon1 zFx^WFTD?{+xiZXBsIL9rYrLIWl>Sp%qD}`eWaR%IWvd0*86#xpP3(hU6WuF2Y1E9vleO&mCM+u-XhbZe%&^i3dfM88yt4tY)$oy_HO)>Zk0+K1F!7-itKFnV#Q2m(l zf_OjcZKtl-D@FnH0C!s#c)fT7=~~=APQX3A$cYpIZihxWy+Rv{%0WVPNH*Cq*zMWp zhMf+Bgl}e$@Li2t1SAaJ5EKUHBVw>=EmA4tDg0~Lb=vHKVBfnX%Ftx6@0HZgNN~Sq zrDLmR9JZANrsV59{r6hz9cCOoHrv9TRQ(W$aF{#*b-ZZH3NYNBOOpNF*YUCdotN*w z^75Ude3|}}&+VKzIPW_GqB1Neg}RQ#h_>wSrT)(OK{-X!8CDz4ih46*lhy!xP^ zP#pfY3U7fV_5dbV&g9D-_?2sTus9u;$%#Xm-$>3T>}l*gIMb?nc15KpOL`qVyUxe# z`KqWp1tDfyATs>XNbCFDDsBwDjft7Mdmr|3O2XNylIZZqW!&7lt4HK+&`5tlg#VeJ z*V-Cw?L2@pPO;#)_iNpFAUYfa;wf#loV0BHpvTA-bVP<>jejPMlA7cXnEYJLxp(T* z6J!1^g6jemN(Q&4CE^yG?i9UvM&j}pv95g%-kU{TC&0Z1)AMQQ6t6(% zCRfchB~)B@D6oJ+<=HT&Rts2Nf{Zt{y!Af|FXO$pgmuB#9JAST%vp1Bs_T|xYJ0^K zNVhBoy@Cz5(_-s=iKI}YCaIdCk&NdpPncQzoOcfmWcHeEBlZsoyK%sL1ZYKt9Uqf zuScmS)h$_`f~&%S>jBM?5fNQ9=xp@6pW#da?Y^JzhhrN3t|r*1iwHxDx#zi&4>&+nNVzn7Hieprin6Op41ip@i(vpU zx%S@b2f$Gq=)J!csP3Z;Gf(vEFPy!V8o92K?R!dD+#Bwqf43Uaw?S(?Q%AN7WksA) zSxlp98e4*m2lKVj+&RUg5Y*vu0FT0rO+EC53uLqDCy%jCqKqIp1i_;p7LjJF(lHjk ze<(x?1pxmbnq&x-u~#LeYDP9hIF~NX=dVpW{3?R09&C88S8&lbAcKVoH94DGUn{|= zKGgcRfQ3(R)tekkXbY~ouR^V>+7fK|g8}52SwLk^^!V7GK(s2hrzF@Q$+b4YYi;7( zJpqp9?rE)mzE`s;7*_vDQ~?)q_x2tX^5<^ka4d=R#t~^j(zwv-jGcUnaYa4c9 zm)(;n!0=3X@m+%jhWn)p=L{aFQN7<`yR)%cBqXs7aO2{3V|9Q@JaHQ{%M&Rw)JF65 zI+^)!nI?ds&1RGnMQb;Pl=&u0t@>k=&bXh2IuX61PK$^136Lh1@m>~MeEx{$vqs}lG?3q~}NsKz1LERBO{@fiP$+)nyoq*e<#%dqBafcCaiQcS^va#isB?x+O9W0 zxo6Xkb-n7!nxCqFq1TS5y2+1w9#r{z*2Z@PS8ZZ;%{Z*9d{>NV-Wz`v&OGPM19wbhvdFUlF-p}fO!!KzT+q0%eq>ny!JbpxT2&_7X>GT>CjX8wxDpET<{OQy; zJDm!~?{gBt(%=|2>;J}{hvP_+PR9ltf6a)nu4(_faWU_;?)+imk1AI)hdSl0)Os*w zCL6wi)hX}rzwv_@?`z|%4@3$m{f7$BSez(3w4^Mr3X7(lVi zx3Fu~<@|4Ef3OlgL|ep}x7zr-X+_b}BQjFn_gfpC7{&P$$^^UTlk0Y3iV__P z(b;=D1wcoKlPkO>fEs%i;BG518wjv?J%WvWC^da_hW-fe5y6rzgm!977X=tk9WQ^o z*4dM~g*JBSKMgh-AetQSO(M*1Thd>{U0Q79n|hBlJzRGojn0%GYub_5UQPsNw^yuX z{i82L8eX8ij~T3(lo+7)f(H4a2;OB$H<|5j8D@DQJhQ} z5@jpcJMbmVLwO$T?1&DPOPNluiZJ5_p)!wz!$A1yz#DL9%JPoL(D2rP_vIHv1ow^% zwkmeKjrB%%PsI!wTIiJ%oZtSe{AK)4H*Bxud?kHCsM{6sO#q_7HCycdPw3H?L!C^f ztC!af{riW`sam(Ljf0tk&_{=#g`ufwfth=@B$}A}tesDvv)auMyk0N*2nbrOt~*S~ z#{7;nRDl|Q6Gl4o+N7z(Vr4UJH*<=3XW9w`T&Tq1+7t}l&}@*mhPnHVg2Je7P_J## ze1g1_IYUQcLsPM;_u{88VsU0TWnM#>)XA(q3BqOWuc>W^qaYI-@n0%Q_wPF1^0DvY zr{Vq6X8$sMDASmZ9i>JqAGt4iHKXuwk6Cww~=J*CG37x0C2PC z&{9{LnQtG@J_G*@kTRLoiH%s#1~A?ujU7tNIf+-ZZq>Rcl(WCZ%(-G|ZPm-c#%Ed8 zV2g%-?Lc2Ls#%hyPtioYOl1{lGr3{j2-asg>i{E{Jy2Q}DDZ}YfApl*tr@|XErFd^ z-(rbn+CjLxnzUW!J2q6oHUec@6?{T3p^f2Sd!hbsD^#GDiQScn?I5-&$XK^lM;5+U zO}X*a(L{4WNdVz*Nq~ne18Sm)6=v7M#&E)G_7@(I`d1r1&!@geY4D37H(nWXr;G`? zwbh};^TO++sue-?+twkY-pWO=TvH z4Izy}bk8e|S9{|Bb!Qw6nY6gmX9Cl09PpWdKs*lkOu&$I9PpV?#uNBV z7{(L$Oc=q_!iU=jfDfOQwTZKVPspt=p_3tJkseXE-t^pAqzBX(dOB~lOtR|X5Np;8 zc~SPaAy=SAvFC4$!BL+U6hWOMH;sH`Bs=m~EQQ)Fi#e6fH34UGUnf#h-8^T&;@aDr zBZpn-*4}!h6Jckg#t;Sh1Hin-SXSrG*Lz*YBj#ejS5-rUy!hLi-A$*+!VRL+)n18m ze17XiHXl0buisS==hWHt9b#yUz0;mEq{E1k8^`Y|tgU(_&gNvNcPtUoxDI++3~0Cg zlgW0YRpw!0dgCFtSI9lHD8$LvbZ^Aw`b7GKs&j?~69QwG{u$avfKk8&hqdrA z{Di%)RPjFaza~7R z|Gn(u%yOX}$8LIGtSKN!lghN>S^cl_egge3**;YN`-Fu{p8i)AW%R#*NK(_cNX07e zeQ)GP)&HVI$c@LRqW?YZo=>Fz)mmv>cD%zb;DXCFq9?<+vAa8wyN-fPts>`Pxb3Qr znTJN)FGh6T+~E<9o1>~52BM{-Xq`szlG-BdmP{?Xfa=BAHrNO*Fv;k0Ov1d()q5VU zZYE(;$Af{OAHPxxLa9fp?gjdLH{>erl~5APKdw~m5%*-8RydoRQg+luBJN7HZShek zR7UT4kh302(>#@$R1stLs%~m{3Xttx{EXkH5phgYhHo`0@4p!S#gq|VTy3?+t%PlC zs-{>TIws6x91pa{*y2~7zF6&&zIYYMBZ#@J zuR=LvV+%sZ$Ho?+GaefVHvEVq>9DN&gaWNUBE>=^Fx>zsh`&E3T_gl^5dW7~Q z!_8|1NxGLrVH|GW9lYUWzIoBP17Bf$7$;hltH#+GYJH<5+}x4+GE3nSA3lpC0kb`l z)fg8MNr}&f7vDEnl^XSyaP0cWG8!(c+s~+rscA2Lag?-1Sb4E1R+zS0E_+eFzcJ14v1udbbTDIVmAF~`3MKmr+;{U8xY>B=Z$tITUGW`u{-MA% z$mAB^c?r|U^|i3F=R*rCYL~Za#zx1{&^iRl9?wvPJ$t1}o%pAo1&b3xN%oh{Uwo6w zuWo)A;x8)NPvSVzwTmR_o&(Za#Qjv&vM^lP@$0VNn2IK0iF=S!dm~ruvESG@ zyoix=`~AQWL-n?{K0m)?!k9afeS(0~DzhT@#)l<5IcuYMHEMmS==4P0am>H?atwW1 zgAKYimnxQ`M?}DcDtIk_))K#AeEr;GOA129Q)FH7nQrV@xcQoT9X_f}%!3{pE3ehn zFgrE-b(rDw;^h zC@NaDF}UdaDmC$a87V~*O^S_Ft(`sp{%XRdaNC(ued1p8;HiF1a>K_4!tRK$JEth@ zUKKb!5k3yU)hr!XKliv2pgC>ov<&w^aBQ7^q4Z${&^lJm1Es|Fe$j-9u6T#Kh zZpdP~+Ra54b1)uok;R;epfs|WGqH^4FrJXxaU*z+XxO-mSLCs?tNG-)z{sl$3qTOA z6P3Jbx~*|AL2G9p>W@Q8V94RK_PZ9g21ukVJVv&386OKTAZX!bUIed9hfG9mUw z@XCbfr7OJH8f%(II}-Ev1212`xA1D{%$Hw*7mJtRrR^EPOVeNQ3i?WbmeApl)1*h#U1*?y3k$wtsP7S)jQone=)>gK3syk0z`HXNb!8=BY}sBI zdjN(3G3WTu#-231W?5&;=$adYO>eyq57E`TJ9gx#oMVZZd&!>GUI z_iG}=7X0L)ocuX0Rki?xrC_jCh z1!-UM(|sm9BR_p@a)#P}6#3~Q<^A~b)4O!?!{n!#SZ@g~Ir*u-D)Qy0KPor!)8}R5 z#R7O{`A3wW3by2r$EPAcE&BaGEDD#> zL-Lci9{H(Is?Hog)=hp&Y|LP5zdlhBj1QNerjk4-Kb4vsvPk{cm7id1=e0|IddQcb z-V%G7g9q|bd(0gk%afP>WVm)2B>4wEE*}z*ur%|W2QDSE_n)AhJpf`IEEeO!U@(fD z4dRzxky<(t9#TSzAteRz0a14hH+Xc`VOg@yn@U!c)wRwExAs0A^r&5J4tf-o!vMA*p>WnC zTqVzy9l@rEPG9@sd(FK%V$ zxOb2xU)%_Q6=t-YXZL>UqA(Cm*n zlZWLch!PRu*CQnx&IbxgspNQra0f=^6UE3g1VAYUQXb7fVzY7&S#-qAjSq9n@=+5{up7d4wAQPx=Jm!(Q zYI}So&uX64JgH)Q1V~SH#!uyQGM|&JUzRTNQWyQeWche;i<`MU4Ecu*lb+GUcZIzi zzPdMa97@^q@O@wBu}0?3V;!42k9B0;d8`mh>_YaU|IWB@UaPFj$*bV)91?i#6inbx zhQ+{}^$7z)g&S;^(89mlBUJ0i$@tYcf0H2wN>h-#XGuZb5w-5zp5`*{676>_yGtm^ zSz2gUsB;6^bzsS_e$jji2|J!Swv@^oTjJ~-@}JJm{k+SOr8q`9u-5jxk}Y92kk*+w z(%Vx@+q#?w3*P>qPRgkVebococE56?)m}2be$1%_@!%wP?y0-%)?MSeo&}SV;Zuz| zWBkUVQ#t&V?1(?sZAZ$;UY*`~q`ud{($^Q%k2#9+PzWC7mp6(OC{KX{mPh#Pjys z+y&+gH)^6@(>gB=bWg8i6dC&n;am6b?D+}kk*-j~!ickT0q$1_HonO~h1O^e8`thj zGWIuzaj%Q~?>~joz^IJ;^~cQWj3N{^SSKIu}=)LBvgB#Ld*gZ7tw1tPaJ8LmNiM5BT*3KUrb{^UJR45pG zB`NPo8-RUC!zvr~>d@>kodn@?a*0(~z&$e9|{9N12ULzkdhK6DG z{=@EhC@Z@ad&(y&wrJ!d>8QIP;LZv-yM}l8Ro__{u4(s@%2dDz*lwm)9Ab$;;= zckW5|_~FjZeKj%o?`xaeyZZmn*zY-0erd5L={-8I`%8UAWdP303Eo_nkX4jK>Abjd$x93S3geKYHK{&{yhaGdb^2KsqSiZ zYkmn)#T8f{f2YKO1K6ESF_QOXmOk;;-yzb?2{)|ElbI`U(6I z^zwVk_-s(aGd*lN1+38ctWbwxr1EV}&4Rn%{j$m~^hD}+(UVg}Pt1qt$q3OC^Cf!n znTE9sLWY=>Hnj34N^(#`3vY&w+~?B~_81Hu>9*cy@I_k?(1|mniTfVbhEN{9d|oV+ zr6+6{SbB1FhMqLMS3ToihzXkja%SO2aFxO3m7zfS4F~B?fH`CB`72E)l?>B5xTgV(CVPiddSmFDepBgtzvB ziUjy~?pqkA*#-IIP!TwX;G!i4FZZG%0=l6h$L>W%hWh+OhX41m(+-oU$17d4zDo=Kau*eamNq^W?J(^~g$Q|NZ!EI@NYHpL&?_8Iz?Re0HNA zUF0Rs`x;lsoZ-L*{9sHS^qFT6VLksX!>)7%J<~o-fU>b=GEO1f;_se>?^ z6Ul^0zYM!1| z|A{NQ;eV6)a zeBT~_gG6`tKk+Jwv;HUkAA9ctA60cOd?%2Q2}E~PsCW+=6}%O!RK{S<$c)Uu44{J2 zR;sO+YO6;IGeT=3fn+M%%@*1tm9}!~d9`n8wWqvQY_xz0U=lzffPz4ko5CJO<)RWU zO1}TI)}C9qh{vAup5OO<`Ta6`?X}lldtILOtmnS^rW(J?xi9^pv(g{x*Wp8L)TbNM zR^$$n0%zx!yeu*oX|1!KA02;U0V!dB?EVK{$i$!cp(&$Kxa4Xrb5ceT{kQk!&g!w_ zaxM@be@MMa{E3VCs^oq5KVVyR{E3NtUiv$hYtDM~Ta!jugAuI}jH$M{$gYH_y>)cJ z9#RyxZXXfB;JERFGQV|2z?-b<<*&G>pz17t#Vwwy9)9m5B~^y^hSI98e(x}-|L-EB zkfTTmOfCYA2T1qyDPtA6{T{bvzWN$j(%cT`wzsdo!M&~Iwl7DfieQ7M*wPFiz`O44 z`P^3Wu6uhLw?W?3w?^Go5}{1gDNZwN+ZgP%JY@Xk-C$!IE-(2WtZHJb1dr7LW%CstLs*ZLeTDHW?KIY19n*FgSSiNhqr z6hD{l;xJU7zb1$?pf4Yvz(@mRa{k_YB9Lq@3l@{P)MsuCm^<;NYw=qTyomKGPr}9f zff{<9^qW^$!wP)f*Wqo5M=1VWo1ehkILU-O%0d zZGj_iCgylB{OJzN$-)64K7pp*5Ssy_#da+ZVCj1lu{NNBc9i zO87bBQE}wsO-!jq;O^b-@^c4N^uq%=J4z}T;IFS#JIX+U0U`~0xJ;x$GD3rWFVTMn z&Zs+?0S(#@_jIQJFC}hE%TA2CkJ$_H3HQjb8FlZgyP@|cUL)ZGP0$|HS4fwTxk(3? z=is?jJm$2=&NvElKxWMMcbPHYvD~R`fi#cmu`zZA^Sj^}LnQ9}wKBsrGQ^hfe3m{5ZBd(L1-k z(47S@h4Xi}Z_cW7xh&QPRfXyA{7pp-R_6thrxr={87ViIaslf>MpG&JqK_wPX+usw zjk<@}x25oz6z=foyF5zv$sUHN@-g4_3qaN#oWBd;%LqjwbVI`!zksufVwzP$EO z{)C-b4e*(G0-I3#%xb8163by=$ez^GS@5)sXGR(py)ZSK(X()-I?5tQcE%G0M~V)L zynAN6MePUqzksIS%zrlhm4k0Ggdn4D#Gj`ZVQVPCHHZX-k{);(ib?69GK4`M@vNz< zSDC@S(49uzU;zeuvRH59xoDz-@$-tgA!yCtBbh+G5&tHIg7#A?Lqu`b!-7|g_^ITJ z+(|{}HR455%z7AEHqQ>rS8AJ(XD=?(l-0O7^@7`sItj}HZYoQ>OBsBdVrQ{3`sPLZ zfKohyQYaO>pkG7cA_{UjPhWcK%PIQOm5ZR{`M=A-I*+8Vj#2j~YG)++hV2@qu9ohl z*0=O;w^Hd!TW3&`{;3RmSgL=qvF(j+-(vT<{fga{>Qn9E%5i^tS*`7ubS62BXa*Dc zj}pVo!@tPsn;N$xr{AyS^!p*t-RQ7A#zP4hFBNpk-yJfGeb@je2k_=K<_g|i!JCcd z3iYOoX?Sym8(r0_-Yn^wzXzutpBcgr$XBYuB%lmZxD@=Jf)5=(T~1KGMf}ThAuBTU;Wk~DqJ1oi9E(1DUj{nsz2~ytyII~# z(T7j%PvSa_#6jX;QYeQ$Jj-p&-{pa#4YxbzmS4y`cO`g zgg*QshnouGxj6Nv#J>dZOWwxWY5H*gzNDfLcXO5X7^+X%%19{pGm$xz`RsA|I#%vO zeHRKQN%R7#$q7Nf1tC$wl7~TBlzknPyX7(BT{{Bb6@AdX|J;{;9}f;M*^Auw zIhKTU3)`(SGLt=y86VDk-k3l6peQXLI^`AF<`N?JV2hI-C``ajvlS(#xysdHUPY{* zXI6~+QAXww?6o%5$JgHI6R)fGMpyq3He1|q-Gm3csv&F#nazDsM`<7SsM*|eRBAN| zfEzD$KP7)rvQV|hyww9?O~cgKX4_+=?Jqd5D;OZ2xcQb(3^52wdeVHViC9D$!ps3W z)OhrFKxlp5Zs;T8xAt1(v#JYvHTy0IcnI02hVXk4yqVdLLr@y#$KIREs!pDChWFv} zs-j7!dI$Bc##8FM6xAeT^v2Hf;Y}IrjOt|6})8_(*u$m z)Z0KM&77Odg%ma(6#~d_#%AfTCPV&BmSQ*@!gtk#qge(JoklyqDeP7W7)? zyCCAZP;PO^EDIJd3Yw$lnjA*MVXJ~cmcgDh1zyLWriI#N^J|c6*zEgQ0Ik;dY7gNC(%_CS(L^eseIwb|#;Bz_t1lE;!oHWn-< z5K;BhW?&Ajd0JXCkk(k;teXoYK8*IX=BpBQVk{22V~INPI~{dmwnUv^vJAd2lT{JH z4RgPAAm?L?p>zQ4_UIWhf)fVna;=oZ!m24TpM-jV05virVy?`L?gvk)(bcrvty z@mqZ?UjaUFe(U=fQx;LYeFPNCffU>8H-9{U?wf-K`pr8llV@1@)=vs#t-5S6){Ex& zf#%JX{;A{ri4Z7FOm~3IpndZI<0UK_)EquiC+J!?+l{#cx7;^eQO!D!%!u{qIK+wH zLRmXQ89F5`Zga|MyW$pTHo|zal8s0E!(WMpGJu0^LF4Am7v@!w z(YULVZ}JYYmhcrf2T($K@Wd_h)owp8P<)uDU4~e7hlHfgJ~o6H?KNAj!^u`~_jtSj z_XsygthH{Qe(g0?-Qc6KO8pjFhK6P406^IqHkT%zRWKK6RnNW!93jQ^n}PY-!SeRs z%8nFh2$=gqxDF4oMt;LHEdKl6txw)XRne|$AgCzW zEo&$cawFW8N$E@oq^i!PHTS?9x4=YB2;t#t(z+hos849`{w8q8ZCFQ*ueb&hG0#Nw{V#8zF%8JUIsqCll zJuJie2j}vyNeztO!gjv$itN$OLsWw=FG`a4*nI2c^UTwA1g634%e7wo->f=4Pjg85 zu%ssp#xOZtfQvp|VDNrGidZb1pM)krTS$)kTL-Qg+^)#UImbE6yv5wgSQd5CkY4fUg)@g$IlAL2C40+e&K@S@-4EJwmE*Ka?G zDX9A`PTs@FAGlXXcE~y!YU);~5@M)8#O~r&Yt-v?t7Rpr$7I)3WCM5?s?_u2Aw_*h zIsTywws48lz<$y;i4+?#-%z2REfIHFf!3kLe3`0MEJ`2ouzZc%JNs&@G4(%Lwso5R zZ!-iTZxFo=cyj<@B5QXLgeCyCX^5UX$S(O>@d0yb&@Ks~cWEFzmN{}N`wGFbx=-~L zAFc-p=4G9FVbuLdFg29#&|?2vrHL4KsiDUN;M9pT;*D?n<-RX{z(QK0b-qLG0BjJO;M z8EhLfH+TkAi1-VLuYmXoh>w8y2Z(Qg_yyd6Kfo9ll=1i8AFv`=>nyF^vftcLyEUco zqTC2`*y!{;0>>O0GPIYTQqCN?PJB_W11(+{vIYqDJ%^C>5O~fq)>cUNhJC5;$QMFp zU%|Wiwc8XJD2I>k#7804RKR@QnfbUFhHN1MXyY~o3Zh&L5~c|_IL~L7oa--MngWFC z1{fs`2&W4m1d3O51cc~#g6{^4iM5!FN*A)4PEaRsJL{x~XamtA=|zzs_q3;eSK6;( zzl0PK=JEtNMKp4X2y#MufLC;0@(ij~z}5?3gHLU1m9O@aP>(rVAkuEm71$*KZv_9> zJAXjVsPO;v6T$!OoB{t|fd6$o1n_@8_#Xu4^q@6D?Jx|hM`}ZC$kPGz!E?RoGpAax4S@gDg*c{KJjMPc+%Ah^} z94X+u01~D~Rk*pXVyZmnkEAfEN~~bT)zi)sosj)dnLfTfIBR#ivSf;HrWT`5T#ljU z(x5MMx!`f+G5Q4N^8YmdGx)Ee#8~R&a)-W1tS0Ux-V4|lLSIE3difN-9W-}*9Q?i8 zHYWgJrP^uFllot&q>ytl`9yTnZ7I{WmFkPMp1LLWI+xA*(yA{F`ZAXbQKI_F=c$#u zoKMeI9fwwHX0kY#gpqT{VzeVm_{zI?w+r2yZkVkarZj=(VQ26-z`Hr1wvJRk1lNVF z1#|e+SBhu-KO>^{M%9~Z|8%-qC%&dY384zATVvg^dlN{`GHOU(?ks&wptw0Xi60dj zd?i~r$~j?8b~v|FmxKXqcAndo5*jKH{ejH`vAH5}y&kvsq66!hG8IT10+k5Eu}cvy z;i*`O$JdTjJZ{2_>R+#JUHC$ycug(-Wc|g8=h7w8n@_*xuu{$cQh` zl+dk8=%B2Nlfz}F0J@&tDl)~z|CG?@S2BiD=LqVQ(YNlA#z#XP8Xm)a@HoSGX3j8b z;q2ipszxEK-#|vFVE{?R!m0AbX6Id==il9)VK1c7GvoZ^v5FPb{8Lt!wew0S8WPtZDpQ#oOvM;Tn*S z;6;Vfq|O`lo5^08M%>zP6hzvu!EgRqP-(x_=6ZAXy`*ZlbTaMRNJQ@hGu7_z5-~rw zX3Mw|1Uze0*51yjtXAWfE-}t4)7bMacBa+(9WRc>pGVI(8v)72EuDh)z>xC+jYHsx zNHAHPe$|Srk8dupY-tZ+y!sN~^xH(`lUqf1b+T+JvyYI^cK6EIPwA+L z`6p;kg&H-ZPT(_a2U@~*q`u!iKM(>waNd>h?r%Kr)4_Y3`>7Ub2iH2b?Q3+s~}b3NM8 z#pZhD|FkyP_AUZ(^c0^28$ZJAvBz1bXlv~&wO0#k;Q=J^fmX3Q_+08T>i!6F?zfSH z)Ekl|DS3nx6Y1YIlU;lCof3+#rmD0Qu`qvf-e$Oz!+>@zC9;(9;Dt%3uY3H)gG;!% z{3E$B9_+tQU#~r^uNQWK8#{yjcpBffQqR_UCLZ;-pX z(>=zWGkha03a5}&Lju}5;2OU?iiE$|7Q zB*2&E2k$Y)HVz}Z@gC!mhWy2@=QGL(}uKN5Ve;&xA+o4BEy?b)ig zy|J7rnYB;(9h*OYEg;QAi|PJ z$yrC<9rJuU@z2Dc_p^{)S1z6Cw=`qTk_}&|;0Q6tc7W=OaA;_T8e-p6NkqsEu}f7F zyOV}k8A<#4G98qCTvNXWoige+$siMnPGOEk>NwX$z~fpSRN&ezCEX-c>Di~}FXx>@ z8dp5WI|$xpAcxa(0vOAJ^clQJ7YjVjWfLLa^PTp^lMl&!P52py1Mlo&t``;Fx{&qa zXYS;zAa=*Nmx9fU87367+k(B`jg_bUhi+wl4yyS*DPRL`Z()bFAXYeIHZe-pm*yXKC1jg0z z4}TeWM_+=d`4GfXdwpkEVWE=>UxD|7a5-s_T*)|Tsm?fOm7CGyZI!VLg;u4Mbog(B zX1Z(fZhpICxF^Kc_||+92T80`s*);ktv2PqtF-lQPOkVjYQ3=;Qb{ z$>GfhTivpok0{$JW$D29-BMP1u@2&Fe5+7shxvaEDy<-RZD%B^MqJJvBo9J*Y>Uup zEmZf&3X~H)!gg&(f?Gu#@GE*dXg#r9?KBv*+=MJo-DLTLo>;trTzdN$@dnt0mbg&{ zo-5QOf_Ra08iJy?b(8^@wAl#UD_XlW8$YJ#?PcJW;iP&Qozp= zIrO>C*_aTh>ZF1GO*eD=TLRUT-?)=fWD3AO_!?J%K?RI}+4#2vJd-3o74W3e@ox!a zex!JZYbVoUBmS_0%CWBrM7|~v*@W`0SV1H~XR}y91(Cx4X?T>46@PUNkN-LUeH=->}j4!p{PV%Ye!I6@D(4TiDj|s|7y`Yq~~(Xe+R? z@onG~2t2e+m)R)Tmm(WC38(?H3Q2DiINbqwQSwc<=mHq%?GJdl}#Q|c8!#Mm$HhTen;LG{Jch1f$SJC zGe_WOnC`KQWi>3Ox<{Ihj+;R~bu#>!_29}w{@d_#x4dI7i5f)#A`Lk6KSCgKQ3jvD;O0^G@aoVSu5*)LM_ckhqI zJxWa;0#%Qv_NSh_`Wd{&xs$cty!tyxMholjz=9<+jmoL-ah@D)8r@Cy-*PuuB~8zk zAEowNcQ%i{-=4s1sy+A0>^=9$**uE>%>b~hIaBn_+KLZD0`p+klp#R~`~8mok@T3JDZLfv zqB*S(BAc9iSdPp!9-Unv)W)A3L4tOyCRNUPjzU>=@g5=bMTzbd58d6~9G}S0$@iM$ z(R4}_9Xd~RK{|C;IwiUd$xG?OQaUytUm{N)r@wwVd6qIK!13yEi5Xj>thh~;Whn!L z#{DG{WNuZWxDmOh)2X;c4lpUucOHFLTHJWw(s!O;Qq-6@3j>Ok`@4#byvH7ruf(>m zGMRa6cI3A;?kH(rX)3*nWlMj2<{Q4o&r5`woV-Qs$4!-O+0vcH110`G33J7&kBV2A zdm0m+iud5;^-+Po^XR+M;uZI;a6c{n7D02Km?z+qW{><@r-kav+x__;HEu6yUt!LZ zdM#T9m8zt}ucy8%d0M%~cl+!ySFYMsyiy{&b;5#fW#g^_edp14rNt}nTbcfB0iTVx z$Y+E3rv0`g{H6IynYw1HIrAT68yHA&?8f)-8R+lyvQtH!YuWOe0`qZ3sHaMCMwlztuvzw4WWI7nAZW8VBg|FK2%JdDgvlwC zc7#+yw3_$KP%DUv)p%-y8)q}Pnx4$zyZ(6jtk@YGj%6>XRLxU%F;3q*#632KQD9Ds zp8KJ^XuB`|S|5&P%@n@bnirJ!J9sG=Gb_74W?#n>-LPv(iLV0TL1=)0mCxH`Ou3Z= zpOSi=2f<^cWtg2iNzcs(rGE2Mg;Em_A~wrKLXLHVV4ne0EQ8Y=zsgN9;F9@@Kququ zB!NKk{HjaJFLHL4Tk;-4?3c-UUhlS>4r4(seAFdA)hXxl{6FwX;Y>F@3YNA@HoJ&ONgNbIkbJ(w@NcraE^) zuMel+JreU>K5fmEYOHye$*(@(&mHs;9&>DoIk6Pbc+5VX$9l|rkdiTPUtBR;x@t@r z&2Pl~ME5ypDJ(uWUyOSTX&5Iy_uHu99IADH#C$(nzT_qlENP@`&liAvD+ zQWAO<%%y`_t3Zm%a{t{;?&AN573n;{of?Jx45q5-GnJsGJYLAap*(jkxLa1nQ>=_x zGU+KFRkbpnQY&MYUKyzGanrHecu5b&r=(JYA)sJPxm4O?UHj%<1z~gZlPBZe(v_S= zI!Bo}eIL!c?DSdcN)+p26N9{H z;*>%k5{zi_R!MZO^^|$?I$Oe2bpBWIN`O%)3l=xxtc2}OgJ4;qSn%Z<)#5CVbjicJ z0_8m3aV;F4_zhkQ)vc_-yMDgP%-5^(y?L>6OIiuW+ z9=E9sk-gVZf#T0o^7m&K%UmGnd1A4giVd?a!o&X;i{W()D2cp)bSFy;Z#>eX2iez$ zFrpG%UTmK#o!u-+FxRVPjGL3-rnyq$8*nL+3$eH!*@!17kZu#z=M{t-bwe>|rn)la>FdhRv%a^S zUS)qzXyWYeJ(s$7zxP{xf9&sVWV=@1n?%Xn#wf#tWx=-97(E4htvZrH!{n#;hZ(_1 z%7~=yQR?isF}>dg%n#&LLZ}Jpshn}#8SlTCk!|7MNr4OB(qkPL}vFbZLSG&Qeg6@agu&e)FTokFds2=LhVDpp&`~_AW|k zDvVmOmyoVyS=FqIaone$QLx8i4{%uQ0gOjwfZfIrw4zbzE@}n;?~bkuwykT-iZBs; zIc1C$H2%`4bu=dxeV_*@-!?cTGq4U@eQnNCRRQ9Y4wYy_C{0}DP z2{kcKxD)e)o|r(M%nX`-?QlKep={r3Hex*^^V{4R;f!86!nkV+?sX55hG)4cx)F$3zlzj_%zx`$T;iIFu>^2&Qd&E`(qE&H^us<;zfH-;l zv-S~UuDsiCG)^e#*Wkn5Odc9MY#-56&YwK2^ati%HYDAFY1;aS7=kwj?HARJJrS`G zfuSU>U^|hIh%ra<5)zomER9{L_Lqt1T;;t^t?L=NTD~y|RN&r?D6G*ei%t)r<1K*8DHj*Nz9v*^C zUFe^WzpaEr>EPqn+uh{=**v|o;~I@8ZR7T$&QsMEKSOQtbM+Q4UfRoo=BOGoHZ`@y z*QhPt2}q^t=m&SAaO2F!jr0C|+&J@d*FSBEnBT%)Ige3KZTXh6Kz1IcqHMY1Q?x8s zAeC9>`Q3CGse3FL!H4_|Zp(ymR~|1>WoH=%GhoEW*ZFsj-ef z1JH@Eah6a7M;e1BHIdow^t)Ztofl(Fle{8%nV4qxCr@ZqgR4`?PvVLLf@&5!7ju&w zA!W{cQ5m7vYlya8i1sO)`>(qo4!nxxJ!0rA%ljLY;e84FoPON1y;bJ@58sh?G&{{N zBspB1Hh0ih9pV4_zHF!e4gRmR>!0EON;Q!$@_!wZ!f|s*otXa<19c4lC-H+m(*Ko; zkL~}&(#z)Ga`}nc4@QHm3Dbave-3U?L94SE+z0bFIWOFa#}SfA5L7zvcjUPT_r4;; z6myL8pECn6i{O=n#wU}#-T5imSufwzFU>Rj3pgXtuN{7Ycp-RTr}%@ARfn@PQfA|s z-A3&ez(xCVK5(1B5j!u!wZh3;_P4heA=g>u;GPTfSi5ziud}VZ);h)t*63{%WLbmAoov1*=ip?tBO6Neyv_oHe?9$hB+Vu6pcDbs*Tzdwe@!))Qb}=vi z_Zl@QgJ{`3QR2EL4H!i0KKlB=VT#wQrtVK=u<!k`MYX#`dzxl1f-+PFyGpmD!Y8&bG2ew^ z*LJ(RQTi#+R7Ta4kEGH&z_X&)YY!7pDNdI|*8JDh>0d6DzU4%y^zUz0{m;+;*zJG3 z2mnp=r#^=AL`LvDRYUi#kmc8OXx|qS6WcZ^9S85G!fMV3aD3v{o;sfJ9FOcO413l= zfgjzH8u#6#h*NNmjQc~!VwDQY6-9oFP~?pKgzsik5Lyqh7VvkQt}B! zKZ*U!8BEltdaO0s{l1XhCs9-)SEs1%>KWN2w}j2v=hEBliryBA{C3D~qCCnY4jN2R z+xt@d?_Q3$*AM{#&G{QIs+%lA`|BA(8~v*$v^jqR!x0E=S(#bqZ+vMo+YgQbqo|;w z9Oyk4L|xAkY)D>xBK#M`VrRDkh(*v_HK)9&sHCy3i5~)B(CgLpdK2v}8V1~j?CQDB zZe(4Y*U|V<;pFXlH`Ql&K$w3HbmXz6cAhIcYUio4p>|fMcO1E&!lEJCDy`kVUv^fB zD6j=`ea~0lR)@;As*!a8zWaq90I*OT_zQv39z@L%N$fm5JVS>6hnqK*tMf8Hi8y39FE~$glMD&DJZp}4 zSvJ)RoYOS{aZQ7BoK*D;mjFlnnO8x)h81vLo*JS6DnTTPMMA4`#~;gr%d@^DIa7FRj>2awim{F2q~2!tDH|p@Qk`q zaMjZ_6=lxpF^tTq`wMW%#A&kAgsV{u$x|iqmKqnjZza!3`()-+@FwdA0+#TuX2NW# z-Q3BHKg;_@T`Ox&Hy2mT_x;ua8Be2bG1;d5H0i8?@m<8g>~ozmQ)Mkx%9!#C-pCU4 zj)L$LN^Fc=MbImhAQ|V}?`HHEuQGNubl4v#qvgHOWaUjC^Eea`5JX#)On6Exr@!4)1 zUEP~2xDiKJ0ZyDkguwJyxG*_`n~bMxdjI{;_`4E1#C3QrDAyjZX?`GxzpE^*Y=2jZ zD~DISw#DOhiSl@@!Bl7pG6oqE+4Q|Fh1 zWeak0w;-OT1Sn30`&Nf7C|$vgRDi0LUC2^_NZf5`4h4)l3DV9sM3ZG3vgW<0axWt{ zYg8J~+l>fDZ{fxi#4fVoe5mw_+aaoCyZK08d+gKKa}Mk4gBoa`V8Gqw zzmL~P$k8cO+g=%~$>#GH$za5#uiiy;8&UnWW~({#Su%ntuK#R?RbpZgCt;pENOn&T zP|`>zGeZix7uW>dUj!OQGh@z^d#Nvl>>qHEtqG_nCsOLVqm?3vfx^#sN~xQ&OJpJX z$Gd>Z`yb=sPvl_=&5zudh2{ryBWPYGA2k2J!R(e=QWa`6bcBNpuFqbN|Ec%}Uxe>z z#uwsykW6ffM8&~+vEBJ8qawH-Y>{*%p|WRnYNeic>eI1L>Vt@ zeoF99&^@oX%a}wy4=A3CpMnH43RV>UvAJtrw4h&mQVwoq-1k#Z+4BbN!97W*zn5*_ z+nX>V&Csg z?2@cGpuZ3yR_zI#iM-pWBM>6SV}vvoeFNu#CHL)30rrRhP(ykRr=qF;WidVbdUs^brO z1?};f);%R~iybf+dYw>-hb!b>8v-9We;9vvrisL(*5mtjPJ4pZXvViQw#qVpAIGL+ z!PJRE%W}igW!jUw{}S)n9Q9OM{@)(5|E3gNNQ%?E&_{!W%J@UtB1qw}?an%~U(L&d zi(N{0(t^}Q>gF|xgvAs|L@d`;g;2^cJ%-bO-PYs%<2eB&@a|{(l<*;bU zVhgRnQ|!zwuPB|FC6WPx7j8iikI3L{iAdQ0VhE#!!fY#!dpD7!0!rmoChL#|iX@2$ zaE-ANQQF43rgPESV$dqnkr`jwi#(}WH4=H!jmVSKO_n_A1QfR~`xZ)b#U0~IOO*C5 zR}!VGP~0}5sYRlMO%x8CatIoAa=MFAw~e6=+jq~!4`VUKlP7`9>d2GFG2~A>oURD9 z2{0^ie~DULQ{_p=MhIH?V+5_(9CL$HaJ5KSny6a4WJn$A;5h6fRPcI{vZU1T%f8_J zU$NkCJC^_Ek+ui;V?@Otqe+_VcFsuqW2o~;SOvkH?WEbc;(9HU@o;s#{3LFa?Hn~| z88^v*h)a4tnNfpgpy3jYl_ludu9W0OKqVD(Oi*?u;f8o9`_yyS%0AWXEdP~+#7g7a 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

Ixr?R_rw# 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�b0fzS4wzpC_0SzW?BO1dDK{aw=Ixy5R#qYp`b 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

uBA;oG{kQn-F&#t!_IQyY4Eu((#}1M_WE1w! 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

-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

yn|pqFK<>3Z%+QBV-R>N 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 z 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

yWh{G>X@J!wVw~slILGUt0uZGL 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 |2UPx=om(e%I=hAwy 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 z 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

H*h}cNp3rM&50l!|kkAA6-rVf%tvoQ8S7*N&( z7tXcd;TRy1R;)Lm7Pte?IU%4&LL8!7pbQ8@#YN{Hk^n6YqY6`j3GVp){Q5FQW|yZw zD=9V-v+e8B{RYhQXu`F{RTsR2d=(y31zud;K9&q=ph58A}VO@g8Cp$!pKtPjwjO}>76mDu@3N6dArxXRM&^Opyu$WEV-e7)(ah=0P zhVweBS@{nuQF4{PoN4b5>!aVGFs&D zyA;+H#wgBRu7eoNLE2YbfnH-b^tX^V?M>-Ud~Uj6wdd-f%Phu#EDPc_=ngM2dAcB_ z&PiS8M;94JMrs(98A}gSG4UQwh4!T4LV{7e^48k2A_1dD8H2E5+G)wCo7R`?r+HbN+e-blx}W&=i^ea zcO=Hcu|UN{wQ7D5nVxzVjP4?gTM`|{RMI%r+c0WCWp`&(ykArHZ2w1=rEOxg2jT`&{}8m!{% zoDGnjCUU(Bz(XxSqzz(b5}Q_(!^DGMH<;2E3C|{+MpF&JA2a;hEqV-#*CMOiBK{Do zZLj)Ec0f`6g6>HS=|1=ra}R4`wKM{xb6C;-%{Oo=fx`)I_vh%7G!(A>$m1-Al6)I# zrmlKQR>q?^AUHFtqkohPLDS3;ap61|W7};Ra1iT!aEmE-&8K@x6r6Bz^VvbQXr5cC zk3yT492vEW_E?K)X_rCDg^JUGiW07>7KmEHmPPe!d#* z(tM*;P1qwltF~$@D*h}`vDuaq`0jelmIv@E2~S)r`p3)c;-bcpn}4MWE?t@8udVWR z#NN{%w){u;9D2|WDoeZ>$w^T11;<+k9Zd0hOE`ikk!YZ8<08r`%0gQ|xY$SQw2<MAr1p~h8Kh?*+IN%B!2NLmqmKn91e8Co0egV8@j_}Ld3bb8(J|$MnwhGt^G{UT z2X?5=AJ@qvPtZ@twDiex;qpz9edNx-ndjgb7W0Z4ydbUc#50i!ggG*zAk#t=WLvnw zc7m+21nvoL#w>B33}3|0;gfj8C}57x$SqWu^;4p}7`Fb)SOpw2yu4-V5nd1Qd{AF6j=GwzKoD>!y?<8`0D3^~G5+;c-^asz zrBfO+Gqt~a!4?9!F=xg>H|G?s2n!Rh^ou5QY@x-AD>yQG5#ad?sFwMJxAfZ&mAtnz z$Co+8Ik}uTd-3rRauAe_DMmZznSALTKMs)@B@UFJ3j~Ko&m4w$&I}~Z2S*^*d%=&t zc;YEh@}xspony8LGxQjG9?1464%9_W6i>j``jU%QOoR0v<`ld%6M%{~Y{9cKv?0^mDtMz`)(Sx}H5|Ut(3qsVy7tV8~Qs7rA z`|iNa_YekwW0$_oH4mMZiUsyRbZAe1Tl00kbwU$I2ae!Cj;?5zQVe*eMKy31MnHkF zG->m_>Skn56p##gJJQosWf9vY;#ENZ4Xm1dFQ68_IL_NwUL%}AlhcxcgKxy2=$2+d zi_%{g6N|qVe$0nIz)JhVY}IyrQ`b=k%87gwWUj4{XPNU-p_GoZ(;}Y9q+C-JS5<|B z71JB{BkF~AzSP%EaKDeN&ntE`bQ4+(nD9a1FFeNl-P5+-m(>yH5q=OZafC!!#J+s_ EFXGW&j{pDw literal 0 HcmV?d00001 diff --git a/nuparu/include/openvdb/include/houdini_utils/OP_NodeChain.h b/nuparu/include/openvdb/include/houdini_utils/OP_NodeChain.h new file mode 100644 index 00000000..ff4107c4 --- /dev/null +++ b/nuparu/include/openvdb/include/houdini_utils/OP_NodeChain.h @@ -0,0 +1,167 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 OP_NodeChain.h +// +/// @author FX R&D Simulation team +// +/// @brief Utilities to collect a chain of adjacent nodes of a particular type +/// so that they can be cooked in a single step +/// @details For example, adjacent xform SOPs could be collapsed +/// by composing their transformation matrices into a single matrix. + +#ifndef HOUDINI_UTILS_OP_NODECHAIN_HAS_BEEN_INCLUDED +#define HOUDINI_UTILS_OP_NODECHAIN_HAS_BEEN_INCLUDED + +#include +#include // for CH_AutoEvaluateTime +#include +#include +#include // for fpreal +#include +#include +#include // for UT_VERSION_INT +#include +#include +#include +#if defined(PRODDEV_BUILD) || defined(DWREAL_IS_DOUBLE) + // OPENVDB_HOUDINI_API, which has no meaning in a DWA build environment but + // must at least exist, is normally defined by including openvdb/Platform.h. + // For DWA builds (i.e., if either PRODDEV_BUILD or DWREAL_IS_DOUBLE exists), + // that introduces an unwanted and unnecessary library dependency. + #ifndef OPENVDB_HOUDINI_API + #define OPENVDB_HOUDINI_API + #endif +#else + #include +#endif + + +namespace houdini_utils { + +/// @brief Return a list of adjacent, uncooked nodes of the given @c NodeType, +/// starting from @a startNode and traversing the network upstream +/// along input 0 connections. +/// @details The list is ordered from the topmost node to @a startNode. +/// @note Lock the inputs of the topmost node before cooking the chain. +template +inline std::vector +getNodeChain(OP_Context& context, NodeType* startNode, bool addInterest = true) +{ + struct Local { + /// Return the nearest upstream node to the given node, traversing + /// only input 0 connections and omitting bypassed nodes. + static inline OP_Node* nextInput( +#if (UT_VERSION_INT >= 0x0c0500aa) // 12.5.170 + fpreal now, +#else + fpreal /*now*/, +#endif + OP_Node* node) + { + OP_Node* input = node->getInput(0, /*mark_used=*/true); +#if (UT_VERSION_INT >= 0x0c0500aa) // 12.5.170 + while (input) { + OP_Node* passThrough = input->getPassThroughNode(now, /*mark_used=*/true); + if (!passThrough) break; + input = passThrough; + } +#else + while (input && input->getBypass()) { + input = input->getInput(0, /*mark_used=*/true); + } +#endif + return input; + } + }; // struct Local + + const fpreal now = context.getTime(); + + std::vector nodes; + for (OP_Node* node = startNode; node != NULL; node = Local::nextInput(now, node)) { + // Stop if the node does not need to cook. + if (!node->needToCook(context, /*query_only=*/true)) break; + + if (NodeType* candidate = dynamic_cast(node)) { + nodes.push_back(candidate); + } else { + // Stop if the node is not of the requested type, unless it is a SOP_NULL. + std::string opname = node->getName().toStdString().substr(0, 4); + if (opname == "null") continue; + break; // stop for all other node types + } + } + std::reverse(nodes.begin(), nodes.end()); + + if (addInterest && startNode != nodes[0] && nodes[0]->getInput(0)) { + startNode->addExtraInput(nodes[0]->getInput(0), OP_INTEREST_DATA); + } + + return nodes; +} + + +//////////////////////////////////////// + + +/// @brief Constructing an OP_EvalScope object allows one to temporarily +/// (for the duration of the current scope) set the evaluation context +/// and time for a node other than the one that is currently being cooked. +/// @internal Entire class is defined in header, so do *NOT* use *_API +class OP_EvalScope +{ +public: + OP_EvalScope(OP_Node& node, OP_Context& context): + mAutoEvaluator( + *OPgetDirector()->getCommandManager(), + context.getThread(), + context.getTime(), + node.getChannels()), + mDirector(OPgetDirector()), + mThread(context.getThread()) + { + mDirector->pushCwd(mThread, &node); + } + + ~OP_EvalScope() { mDirector->popCwd(mThread); } + +private: + CH_AutoEvaluateTime mAutoEvaluator; + OP_Director* mDirector; + int mThread; +}; + +} // namespace houdini_utils + +#endif // HOUDINI_UTILS_OP_NODECHAIN_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/houdini_utils/ParmFactory.h b/nuparu/include/openvdb/include/houdini_utils/ParmFactory.h new file mode 100644 index 00000000..6a90e7e9 --- /dev/null +++ b/nuparu/include/openvdb/include/houdini_utils/ParmFactory.h @@ -0,0 +1,486 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 ParmFactory.h +/// @author FX R&D OpenVDB team +/// +/// @brief A collection of factory methods and helper functions +/// to simplify Houdini plugin development and maintenance. + +#ifndef HOUDINI_UTILS_PARM_FACTORY_HAS_BEEN_INCLUDED +#define HOUDINI_UTILS_PARM_FACTORY_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#if defined(PRODDEV_BUILD) || defined(DWREAL_IS_DOUBLE) + // OPENVDB_HOUDINI_API, which has no meaning in a DWA build environment but + // must at least exist, is normally defined by including openvdb/Platform.h. + // For DWA builds (i.e., if either PRODDEV_BUILD or DWREAL_IS_DOUBLE exists), + // that introduces an unwanted and unnecessary library dependency. + #ifndef OPENVDB_HOUDINI_API + #define OPENVDB_HOUDINI_API + #endif +#else + #include +#endif +#include +#include +#include +#include + + +#ifdef SESI_OPENVDB + #ifdef OPENVDB_HOUDINI_API + #undef OPENVDB_HOUDINI_API + #define OPENVDB_HOUDINI_API + #endif +#endif + + +class GU_Detail; +class OP_OperatorTable; + +namespace houdini_utils { + +class ParmFactory; + + +/// @brief Parameter template list that is always terminated. +class OPENVDB_HOUDINI_API ParmList +{ +public: + typedef std::vector PrmTemplateVec; + + ParmList() {} + + bool empty() const { return mParmVec.empty(); } + size_t size() const { return mParmVec.size(); } + + void clear() { mParmVec.clear(); mSwitchers.clear(); } + + ParmList& add(const PRM_Template&); + ParmList& add(const ParmFactory&); + + ParmList& beginSwitcher(const std::string& token, const std::string& label = ""); + ParmList& endSwitcher(); + + ParmList& addFolder(const std::string& label); + + /// Return a heap-allocated copy of this list's array of parameters. + PRM_Template* get() const; + +private: + struct SwitcherInfo { size_t parmIdx; std::vector folders; }; + typedef std::vector SwitcherStack; + + void incFolderParmCount(); + SwitcherInfo* getCurrentSwitcher(); + + PrmTemplateVec mParmVec; + SwitcherStack mSwitchers; +}; + + +//////////////////////////////////////// + + +/// @class ParmFactory +/// @brief Helper class to simplify construction of PRM_Templates and +/// dynamic user interfaces. +/// +/// Usage example: +/// @code +/// houdini_utils::ParmList parms; +/// +/// parms.add(houdini_utils::ParmFactory(PRM_STRING, "group", "Group") +/// .setHelpText("Specify a subset of the input VDB grids to be processed.") +/// .setChoiceList(&houdini_utils::PrimGroupMenu)); +/// +/// parms.add(houdini_utils::ParmFactory(PRM_FLT_J, "tolerance", "Pruning Tolerance") +/// .setDefault(PRMzeroDefaults) +/// .setRange(PRM_RANGE_RESTRICTED, 0, PRM_RANGE_UI, 1)); +/// @endcode +class OPENVDB_HOUDINI_API ParmFactory +{ +public: + ParmFactory(PRM_Type, const std::string& token, const std::string& label); + ParmFactory(PRM_MultiType, const std::string& token, const std::string& label); + + // Settings + ParmFactory& setCallbackFunc(const PRM_Callback&); + + /// Specify a menu of values for this parameter. + ParmFactory& setChoiceList(const PRM_ChoiceList*); + /// @brief Specify a menu type and a list of token, label, token, label,... pairs + /// for this parameter. + /// @param typ specifies the menu behavior (toggle, replace, etc.) + /// @param items a list of token, label, token, label,... string pairs + ParmFactory& setChoiceListItems(PRM_ChoiceListType typ, const std::vector& items); + /// @brief Specify a menu type and a list of token, label, token, label,... pairs + /// for this parameter. + /// @param typ specifies the menu behavior (toggle, replace, etc.) + /// @param items a list of token, label, token, label,... string pairs + /// @note The @a items array must be null-terminated. + ParmFactory& setChoiceListItems(PRM_ChoiceListType typ, const char* const* items); + + +#if defined(GCC3) + #define IS_DEPRECATED __attribute__ ((deprecated)) +#elif defined(_MSC_VER) + #define IS_DEPRECATED __declspec(deprecated) +#else + #define IS_DEPRECATED +#endif + + /// @brief Specify a menu type and either a list of menu item labels or a list of + /// token, label, token, label,... pairs for this parameter. + /// @param typ specifies the menu behavior (toggle, replace, etc.) + /// @param items a list of menu item labels or token, label, token, label,... pairs + /// @param paired if @c false, treat all the elements of @a items as labels and assign + /// them numeric tokens starting from zero; otherwise, treat the elements of @a items + /// as token, label, token, label,... pairs + /// @deprecated Use setChoiceListItems() instead. Using unpaired items may mean + /// less typing now, but it prevents you from reordering or deleting entries later. + IS_DEPRECATED ParmFactory& setChoiceList(PRM_ChoiceListType typ, + const std::vector& items, bool paired = false); + /// @brief Specify a menu type and either a list of menu item labels or a list of + /// token, label, token, label,... pairs for this parameter. + /// @param typ specifies the menu behavior (toggle, replace, etc.) + /// @param items a list of menu item labels or token, label, token, label,... pairs + /// @param paired if @c false, treat all the elements of @a items as labels and assign + /// them numeric tokens starting from zero; otherwise, treat the elements of @a items + /// as token, label, token, label,... pairs + /// @note The @a items array must be null-terminated. + /// @deprecated Use setChoiceListItems() instead. Using unpaired items may mean + /// less typing now, but it prevents you from reordering or deleting entries later. + IS_DEPRECATED ParmFactory& setChoiceList(PRM_ChoiceListType typ, + const char* const* items, bool paired = false); + +#undef IS_DEPRECATED + + ParmFactory& setConditional(const PRM_ConditionalBase*); + + /// @brief Specify a default value for this parameter. + /// @details If the string is null, the floating-point value will be used + /// (but rounded if this parameter is integer-valued). + /// @note The string pointer must not point to a temporary. + ParmFactory& setDefault(fpreal, const char* = NULL, CH_StringMeaning = CH_STRING_LITERAL); + /// @brief Specify a default string value for this parameter. + ParmFactory& setDefault(const std::string&, CH_StringMeaning = CH_STRING_LITERAL); + /// @brief Specify default numeric values for the vector elements of this parameter + /// (assuming its vector size is > 1). + /// @details Floating-point values will be rounded if this parameter is integer-valued. + ParmFactory& setDefault(const std::vector&); + /// @brief Specify default values for the vector elements of this parameter + /// (assuming its vector size is > 1). + ParmFactory& setDefault(const std::vector&); + /// Specify a default value or values for this parameter. + ParmFactory& setDefault(const PRM_Default*); + + ParmFactory& setHelpText(const char*); + + ParmFactory& setParmGroup(int); + + /// Specify a range for this parameter's values. + ParmFactory& setRange( + PRM_RangeFlag minFlag, fpreal minVal, + PRM_RangeFlag maxFlag, fpreal maxVal); + /// @brief Specify range for the values of this parameter's vector elements + /// (assuming its vector size is > 1). + ParmFactory& setRange(const std::vector&); + /// Specify a range or ranges for this parameter's values. + ParmFactory& setRange(const PRM_Range*); + + /// Specify (@e key, @e value) pairs of spare data for this parameter. + ParmFactory& setSpareData(const std::map&); + /// Specify spare data for this parameter. + ParmFactory& setSpareData(const PRM_SpareData*); + + /// @brief Specify the list of parameters for each instance of a multiparm. + /// @note This setting is ignored for non-multiparm parameters. + /// @note Parameter name tokens should include a '#' character. + ParmFactory& setMultiparms(const ParmList&); + + /// Specify an extended type for this parameter. + ParmFactory& setTypeExtended(PRM_TypeExtended); + + /// @brief Specify the number of vector elements for this parameter. + /// @details (The default vector size is one element.) + ParmFactory& setVectorSize(int); + + /// Construct and return the parameter template. + PRM_Template get() const; + +private: + struct Impl; + boost::shared_ptr mImpl; + + // For internal use only, and soon to be removed: + ParmFactory& doSetChoiceList(PRM_ChoiceListType, const std::vector&, bool); + ParmFactory& doSetChoiceList(PRM_ChoiceListType, const char* const* items, bool); +}; + + +//////////////////////////////////////// + + +class OpPolicy; +typedef boost::shared_ptr OpPolicyPtr; + + +/// @brief Helper class to simplify operator registration +/// +/// Usage example: +/// @code +/// void +/// newPopOperator(OP_OperatorTable* table) +/// { +/// houdini_utils::ParmList parms; +/// +/// parms.add(houdini_utils::ParmFactory(PRM_STRING, "group", "Group") +/// .setHelpText("Specify a subset of the input VDB grids to be processed.") +/// .setChoiceList(&houdini_utils::PrimGroupMenu)); +/// +/// parms.add(...); +/// +/// ... +/// +/// houdini_utils::OpFactory(MyOpPolicy(), My Node", +/// POP_DW_MyNode::factory, parms, *table, houdini_utils::OpFactory::POP) +/// .addInput("Input geometry") // input 0 (required) +/// .addOptionalInput("Reference geometry"); // input 1 (optional) +/// } +/// @endcode +class OPENVDB_HOUDINI_API OpFactory +{ +public: + enum OpFlavor { SOP, POP, ROP, VOP, HDA }; + + /// @brief Return "SOP" for the SOP flavor, "POP" for the POP flavor, etc. + /// @details Useful in OpPolicy classes for constructing type and icon names. + static std::string flavorToString(OpFlavor); + + /// @brief Construct a factory that on destruction registers a new operator type. + /// @param english the operator's UI name, as it should appear in menus + /// @param ctor a factory function that creates operators of this type + /// @param parms the parameter template list for operators of this type + /// @param table the registry to which to add this operator type + /// @param flavor the operator's class (SOP, POP, etc.) + /// @details @c OpPolicyType specifies the type of OpPolicy to be used to control + /// the factory's behavior. The (unused) @c OpPolicyType argument is required + /// to enable the compiler to infer the type of the template argument + /// (there is no other way to invoke a templated constructor). + template + OpFactory(const OpPolicyType& /*unused*/, const std::string& english, + OP_Constructor ctor, ParmList& parms, OP_OperatorTable& table, OpFlavor flavor = SOP) + { + this->init(OpPolicyPtr(new OpPolicyType), english, ctor, parms, table, flavor); + } + + /// @note Factories initialized with this constructor use the DWAOpPolicy. + OpFactory(const std::string& english, OP_Constructor ctor, + ParmList& parms, OP_OperatorTable& table, OpFlavor flavor = SOP); + + /// Register the operator. + ~OpFactory(); + + /// @brief Return the new operator's flavor (SOP, POP, etc.). + /// @details This accessor is mainly for use by OpPolicy objects. + OpFlavor flavor() const; + /// @brief Return the new operator's flavor as a string ("SOP", "POP", etc.). + /// @details This accessor is mainly for use by OpPolicy objects. + std::string flavorString() const; + /// @brief Return the new operator's type name. + /// @details This accessor is mainly for use by OpPolicy objects. + const std::string& name() const; + /// @brief Return the new operator's UI name. + /// @details This accessor is mainly for use by OpPolicy objects. + const std::string& english() const; + /// @brief Return the new operator's icon name. + /// @details This accessor is mainly for use by OpPolicy objects. + const std::string& iconName() const; + /// @brief Return the new operator's help URL. + /// @details This accessor is mainly for use by OpPolicy objects. + const std::string& helpURL() const; + /// @brief Return the operator table with which this factory is associated. + /// @details This accessor is mainly for use by OpPolicy objects. + const OP_OperatorTable& table() const; + + /// @brief Construct a type name for this operator from the given English name + /// and add it as an alias. + /// @details For backward compatibility when an operator needs to be renamed, + /// add the old name as an alias. + OpFactory& addAlias(const std::string& english); + /// @brief Add an alias for this operator. + /// @details For backward compatibility when an operator needs to be renamed, + /// add the old name as an alias. + /// @note This variant takes an operator type name rather than an English name. + OpFactory& addAliasVerbatim(const std::string& name); + /// Add a required input with the given name. + OpFactory& addInput(const std::string& name); + /// Add an optional input with the given name. + OpFactory& addOptionalInput(const std::string& name); + /// @brief Set the maximum number of inputs allowed by this operator. + /// @note It is only necessary to set this limit if there are inputs + /// that have not been named with addInput() or addOptionalInput(). + OpFactory& setMaxInputs(unsigned = 9999); + /// Specify obsolete parameters to this operator. + OpFactory& setObsoleteParms(const ParmList&); + /// Add one or more local variables to this operator. + OpFactory& setLocalVariables(CH_LocalVariable*); + OpFactory& setFlags(unsigned); + +private: + OpFactory(const OpFactory&); + OpFactory& operator=(const OpFactory&); + + void init(OpPolicyPtr, const std::string& english, OP_Constructor, + ParmList&, OP_OperatorTable&, OpFlavor); + + struct Impl; + boost::shared_ptr mImpl; +}; + + +//////////////////////////////////////// + + +/// @brief An OpPolicy customizes the behavior of an OpFactory. +/// This base class specifies the required interface. +class OPENVDB_HOUDINI_API OpPolicy +{ +public: + OpPolicy() {} + virtual ~OpPolicy() {} + + /// @brief Return a type name for the operator defined by the given factory. + std::string getName(const OpFactory& factory) { return getName(factory, factory.english()); } + + /// @brief Convert an English name into a type name for the operator defined by + /// the given factory, and return the result. + /// @details In this base class implementation, the operator's type name is generated + /// by calling @c UT_String::forceValidVariableName() on the English name. + /// @note This function might be called (from OpFactory::addAlias(), for example) + /// with an English name other than the one returned by + /// factory.@link OpFactory::english() english()@endlink. + virtual std::string getName(const OpFactory& factory, const std::string& english); + + /// @brief Return an icon name for the operator defined by the given factory. + /// @details Return an empty string to use Houdini's default icon naming scheme. + virtual std::string getIconName(const OpFactory&) { return ""; } + + /// @brief Return a help URL for the operator defined by the given factory. + virtual std::string getHelpURL(const OpFactory&) { return ""; } +}; + +/// @brief Default policy for DWA operator types +class OPENVDB_HOUDINI_API DWAOpPolicy: public OpPolicy +{ +public: + /// @brief Return a type name for the operator defined by the given factory. + /// @details The operator's type name is generated from its English name + /// by prepending "DW_" and removing non-alphanumeric characters. + /// For example, "My Node" becomes "DW_MyNode". + virtual std::string getName(const OpFactory&, const std::string& english); + + /// @brief Return a help URL for the operator defined by the given factory. + virtual std::string getHelpURL(const OpFactory&); +}; + +/// @brief Default policies for DWA R&D operator types +/// +/// See http://mydw.anim.dreamworks.com/display/FX/Houdini+Plugin+and+HDA+Naming+Rules + +class DWALevel1RnDOpPolicy : public DWAOpPolicy +{ +public: + /// @brief Level 1: show-wide + virtual std::string getIconName(const OpFactory&) { return "DreamWorks_L1_RnD"; } +}; + +class DWALevel2RnDOpPolicy : public DWAOpPolicy +{ +public: + /// @brief Level 2: global + virtual std::string getIconName(const OpFactory&) { return "DreamWorks_L2_RnD"; } +}; + +class DWALevel3RnDOpPolicy : public DWAOpPolicy +{ +public: + /// @brief Level 3: depot, map, most stable + virtual std::string getIconName(const OpFactory&) { return "DreamWorks_L3_RnD"; } +}; + +//////////////////////////////////////// + + +/// @brief Helper class to manage input locking. +class OPENVDB_HOUDINI_API ScopedInputLock +{ +public: + ScopedInputLock(SOP_Node& node, OP_Context& context): mNode(&node) + { + if (mNode->lockInputs(context) >= UT_ERROR_ABORT) { + throw std::runtime_error("failed to lock inputs"); + } + } + + ~ScopedInputLock() { mNode->unlockInputs(); } + +private: + SOP_Node* mNode; +}; + + +//////////////////////////////////////// + + +// Extended group name drop-down menu incorporating "@= +#include +#include + +#if defined(PRODDEV_BUILD) || defined(DWREAL_IS_DOUBLE) || defined(SESI_OPENVDB) + // OPENVDB_HOUDINI_API, which has no meaning in a DWA build environment but + // must at least exist, is normally defined by including openvdb/Platform.h. + // For DWA builds (i.e., if either PRODDEV_BUILD or DWREAL_IS_DOUBLE exists), + // that introduces an unwanted and unnecessary library dependency. + #ifndef OPENVDB_HOUDINI_API + #define OPENVDB_HOUDINI_API + #endif +#else + #include +#endif + +namespace houdini_utils { + + +/// @brief Add geometry to the given GU_Detail to create a box with the given corners. +/// @param corners the eight corners of the box +/// @param color an optional color for the added geometry +/// @param shaded if false, generate a wireframe box; otherwise, generate a solid box +/// @param alpha an optional opacity for the added geometry +OPENVDB_HOUDINI_API void createBox(GU_Detail&, UT_Vector3 corners[8], + const UT_Vector3* color = NULL, bool shaded = false, float alpha = 1.0); + +} // namespace houdini_utils + +#endif // HOUDINI_UTILS_GEOMETRY_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/Exceptions.h b/nuparu/include/openvdb/include/openvdb/Exceptions.h new file mode 100644 index 00000000..c2a38386 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/Exceptions.h @@ -0,0 +1,112 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include +#include +#include + +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-2016 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/nuparu/include/openvdb/include/openvdb/Grid.h b/nuparu/include/openvdb/include/openvdb/Grid.h new file mode 100644 index 00000000..f43652bd --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/Grid.h @@ -0,0 +1,1404 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + +typedef tree::TreeBase TreeBase; + +template class Grid; // forward declaration + + +/// @brief Create a new grid of type @c GridType with a given background value. +/// +/// @note Calling createGrid(background) is equivalent to calling +/// GridType::create(background). +template +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() is equivalent to calling GridType::create(). +template +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 +inline typename Grid::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::Ptr createLevelSet( + Real voxelSize = 1.0, Real halfWidth = LEVEL_SET_HALF_WIDTH); + + +//////////////////////////////////////// + + +/// @brief Abstract base class for typed grids +class OPENVDB_API GridBase: public MetaMap +{ +public: + typedef boost::shared_ptr Ptr; + typedef boost::shared_ptr 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 + 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 + static typename GridType::Ptr grid(const GridBase::Ptr&); + template + static typename GridType::ConstPtr grid(const GridBase::ConstPtr&); + template + static typename GridType::ConstPtr constGrid(const GridBase::Ptr&); + template + 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(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; + +#ifndef OPENVDB_2_ABI_COMPATIBLE + /// @brief Clip this grid to the given world-space bounding box. + /// @details Voxels that lie outside the bounding box are set to the background. + /// @warning Clipping a level set will likely produce a grid that is + /// no longer a valid level set. + void clipGrid(const BBoxd&); + + /// @brief Clip this grid to the given index-space bounding box. + /// @details Voxels that lie outside the bounding box are set to the background. + /// @warning Clipping a level set will likely produce a grid that is + /// no longer a valid level set. + virtual void clip(const CoordBBox&) = 0; +#endif + + + // + // 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; +#ifndef OPENVDB_2_ABI_COMPATIBLE + /// Read all of this grid's data buffers that intersect the given index-space bounding box. + virtual void readBuffers(std::istream&, const CoordBBox&) = 0; + /// @brief Read all of this grid's data buffers that are not yet resident in memory + /// (because delayed loading is in effect). + /// @details If this grid was read from a memory-mapped file, this operation + /// disconnects the grid from the file. + /// @sa io::File::open, io::MappedFile + virtual void readNonresidentBuffers() const = 0; +#endif + /// 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 GridPtrVec; +typedef GridPtrVec::iterator GridPtrVecIter; +typedef GridPtrVec::const_iterator GridPtrVecCIter; +typedef boost::shared_ptr GridPtrVecPtr; + +typedef std::vector GridCPtrVec; +typedef GridCPtrVec::iterator GridCPtrVecIter; +typedef GridCPtrVec::const_iterator GridCPtrVecCIter; +typedef boost::shared_ptr GridCPtrVecPtr; + +typedef std::set GridPtrSet; +typedef GridPtrSet::iterator GridPtrSetIter; +typedef GridPtrSet::const_iterator GridPtrSetCIter; +typedef boost::shared_ptr GridPtrSetPtr; + +typedef std::set GridCPtrSet; +typedef GridCPtrSet::iterator GridCPtrSetIter; +typedef GridCPtrSet::const_iterator GridCPtrSetCIter; +typedef boost::shared_ptr 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 +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 +inline GridPtrT +findGridByName(const std::map& container, const Name& name) +{ + typedef std::map 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 +class Grid: public GridBase +{ +public: + typedef boost::shared_ptr Ptr; + typedef boost::shared_ptr ConstPtr; + + typedef _TreeType TreeType; + typedef typename _TreeType::Ptr TreePtrType; + typedef typename _TreeType::ConstPtr ConstTreePtrType; + typedef typename _TreeType::ValueType ValueType; + typedef typename _TreeType::BuildType BuildType; + + 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; + + typedef typename tree::ValueAccessor<_TreeType, true> Accessor; + typedef typename tree::ValueAccessor ConstAccessor; + typedef typename tree::ValueAccessor<_TreeType, false> UnsafeAccessor; + typedef typename tree::ValueAccessor ConstUnsafeAccessor; + + /// @brief ValueConverter::Type is the type of a grid having the same + /// hierarchy as this grid but a different value type, T. + /// + /// For example, FloatGrid::ValueConverter::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::Type". + template + struct ValueConverter { + typedef Grid::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&); + /// @brief Deep copy the metadata, transform and tree of another grid whose tree + /// configuration is the same as this grid's but whose value type is different. + /// Cast the other grid's values to this grid's value type. + /// @throw TypeError if the other grid's tree configuration doesn't match this grid's + /// or if this grid's ValueType is not constructible from the other grid's ValueType. + template + explicit 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. + explicit 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(); } + + /// @brief Return this grid's background value. + /// + /// @note Use tools::changeBackground to efficiently modify the background values. + const ValueType& background() const { return mTree->background(); } + + /// 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(); } + + /// @brief Return an accessor that provides random read and write access + /// to this grid's voxels. The accessor is safe in the sense that + /// it is registered by the tree of this grid. + Accessor getAccessor() { return Accessor(tree()); } + /// @brief Return an accessor that provides random read and write access + /// to this grid's voxels. The accessor is unsafe in the sense that + /// it is not registered by the tree of this grid. In some rare + /// cases this can give a performance advantage over a registered + /// accessor but it is unsafe if the tree topology is modified. + /// + /// @warning Only use this method if you're an expert and know the + /// risks of using an unregistered accessor (see tree/ValueAccessor.h) + Accessor getUnsafeAccessor() { return UnsafeAccessor(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()); } + //@} + /// @brief Return an accessor that provides random read-only access + /// to this grid's voxels. The accessor is unsafe in the sense that + /// it is not registered by the tree of this grid. In some rare + /// cases this can give a performance advantage over a registered + /// accessor but it is unsafe if the tree topology is modified. + /// + /// @warning Only use this method if you're an expert and know the + /// risks of using an unregistered accessor (see tree/ValueAccessor.h) + ConstAccessor getConstUnsafeAccessor() const { return ConstUnsafeAccessor(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 + /// @param sparse if false, active tiles are voxelized, i.e. only active voxels + /// are generated from the fill operation. Defaults to true. + /// @note If @a sparse is true 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, bool sparse = true); + + /// Reduce the memory footprint of this grid by increasing its sparseness. + virtual void pruneGrid(float tolerance = 0.0); + +#ifndef OPENVDB_2_ABI_COMPATIBLE + /// @brief Clip this grid to the given index-space bounding box. + /// @details Voxels that lie outside the bounding box are set to the background. + /// @warning Clipping a level set will likely produce a grid that is + /// no longer a valid level set. + virtual void clip(const CoordBBox&); +#endif + + /// @brief Efficiently merge another grid into this grid using one of several schemes. + /// @details This operation is primarily intended to combine grids that are mostly + /// non-overlapping (for example, intermediate grids from computations that are + /// parallelized across disjoint regions of space). + /// @warning This operation always empties the other grid. + void merge(Grid& other, MergePolicy policy = MERGE_ACTIVE_STATES); + + /// @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 the 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 + void topologyUnion(const Grid& other); + + /// @brief Intersect 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 only if the corresponding + /// value was already active AND if it is active in the other tree. Also, a + /// resulting value maps to a voxel if the corresponding value + /// already mapped to an active voxel in either of the two grids + /// and it maps to an active tile or voxel in the other grid. + /// + /// @note This operation can delete branches of this grid that overlap with + /// inactive tiles in the other grid. Also, because it can deactivate voxels, + /// it can create leaf nodes with no active values. Thus, it is recommended + /// to prune this grid after calling this method. + template + void topologyIntersection(const Grid& other); + + /// @brief Difference this grid's set of active values with the active values + /// of the other grid, whose value type may be different. + /// @details After this method is called, voxels in this grid will be active + /// only if they were active to begin with and if the corresponding voxels + /// in the other grid were inactive. + /// + /// @note This operation can delete branches of this grid that overlap with + /// active tiles in the other grid. Also, because it can deactivate voxels, + /// it can create leaf nodes with no active values. Thus, it is recommended + /// to prune this grid after calling this method. + template + void topologyDifference(const Grid& other); + + // + // 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&); +#ifndef OPENVDB_2_ABI_COMPATIBLE + /// Read all of this grid's data buffers that intersect the given index-space bounding box. + virtual void readBuffers(std::istream&, const CoordBBox&); + /// @brief Read all of this grid's data buffers that are not yet resident in memory + /// (because delayed loading is in effect). + /// @details If this grid was read from a memory-mapped file, this operation + /// disconnects the grid from the file. + /// @sa io::File::open, io::MappedFile + virtual void readNonresidentBuffers() const; +#endif + /// 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(grid) is equivalent to calling +/// GridBase::grid(grid). +template +inline typename GridType::Ptr +gridPtrCast(const GridBase::Ptr& grid) +{ + return GridBase::grid(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(grid) is equivalent to calling +/// GridBase::constGrid(grid). +template +inline typename GridType::ConstPtr +gridConstPtrCast(const GridBase::ConstPtr& grid) +{ + return GridBase::constGrid(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 +inline typename GridType::Ptr +deepCopyTypedGrid(const GridBase::ConstPtr& grid) +{ + if (!grid || !grid->isType()) return typename GridType::Ptr(); + return gridPtrCast(grid->deepCopyGrid()); +} + + +template +inline typename GridType::Ptr +deepCopyTypedGrid(const GridBase& grid) +{ + if (!grid.isType()) return typename GridType::Ptr(); + return gridPtrCast(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 +struct TreeAdapter +{ + typedef _TreeType TreeType; + typedef typename boost::remove_const::type NonConstTreeType; + typedef typename TreeType::Ptr TreePtrType; + typedef typename TreeType::ConstPtr ConstTreePtrType; + typedef typename NonConstTreeType::Ptr NonConstTreePtrType; + typedef Grid GridType; + typedef Grid NonConstGridType; + typedef typename GridType::Ptr GridPtrType; + typedef typename NonConstGridType::Ptr NonConstGridPtrType; + typedef typename GridType::ConstPtr ConstGridPtrType; + typedef typename TreeType::ValueType ValueType; + typedef typename tree::ValueAccessor AccessorType; + typedef typename tree::ValueAccessor ConstAccessorType; + typedef typename tree::ValueAccessor NonConstAccessorType; + + 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 +struct TreeAdapter > +{ + typedef _TreeType TreeType; + typedef typename boost::remove_const::type NonConstTreeType; + typedef typename TreeType::Ptr TreePtrType; + typedef typename TreeType::ConstPtr ConstTreePtrType; + typedef typename NonConstTreeType::Ptr NonConstTreePtrType; + typedef Grid GridType; + typedef Grid NonConstGridType; + typedef typename GridType::Ptr GridPtrType; + typedef typename NonConstGridType::Ptr NonConstGridPtrType; + typedef typename GridType::ConstPtr ConstGridPtrType; + typedef typename TreeType::ValueType ValueType; + typedef typename tree::ValueAccessor AccessorType; + typedef typename tree::ValueAccessor ConstAccessorType; + typedef typename tree::ValueAccessor NonConstAccessorType; + + 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 ValueAccessor types +template +struct TreeAdapter > +{ + typedef _TreeType TreeType; + typedef typename boost::remove_const::type NonConstTreeType; + typedef typename TreeType::Ptr TreePtrType; + typedef typename TreeType::ConstPtr ConstTreePtrType; + typedef typename NonConstTreeType::Ptr NonConstTreePtrType; + typedef Grid GridType; + typedef Grid NonConstGridType; + typedef typename GridType::Ptr GridPtrType; + typedef typename NonConstGridType::Ptr NonConstGridPtrType; + typedef typename GridType::ConstPtr ConstGridPtrType; + typedef typename TreeType::ValueType ValueType; + typedef typename tree::ValueAccessor AccessorType; + typedef typename tree::ValueAccessor ConstAccessorType; + typedef typename tree::ValueAccessor NonConstAccessorType; + + static TreeType& tree(TreeType& t) { return t; } + static TreeType& tree(GridType& g) { return g.tree(); } + static TreeType& tree(AccessorType& a) { return a.tree(); } + static const TreeType& tree(const TreeType& t) { return t; } + static const TreeType& tree(const GridType& g) { return g.tree(); } + static const TreeType& tree(const AccessorType& a) { return a.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 +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(grid); + } + return typename GridType::Ptr(); +} + + +template +inline typename GridType::ConstPtr +GridBase::grid(const GridBase::ConstPtr& grid) +{ + return boost::const_pointer_cast( + GridBase::grid(boost::const_pointer_cast(grid))); +} + + +template +inline typename GridType::ConstPtr +GridBase::constGrid(const GridBase::Ptr& grid) +{ + return boost::const_pointer_cast(GridBase::grid(grid)); +} + + +template +inline typename GridType::ConstPtr +GridBase::constGrid(const GridBase::ConstPtr& grid) +{ + return boost::const_pointer_cast( + GridBase::grid(boost::const_pointer_cast(grid))); +} + + +inline TreeBase::Ptr +GridBase::baseTreePtr() +{ + return boost::const_pointer_cast(this->constBaseTreePtr()); +} + + +inline void +GridBase::setTransform(math::Transform::Ptr xform) +{ + if (!xform) OPENVDB_THROW(ValueError, "Transform pointer is null"); + mTransform = xform; +} + + +//////////////////////////////////////// + + +template +inline Grid::Grid(): mTree(new TreeType) +{ +} + + +template +inline Grid::Grid(const ValueType &background): mTree(new TreeType(background)) +{ +} + + +template +inline Grid::Grid(TreePtrType tree): mTree(tree) +{ + if (!tree) OPENVDB_THROW(ValueError, "Tree pointer is null"); +} + + +template +inline Grid::Grid(const Grid& other): + GridBase(other), + mTree(boost::static_pointer_cast(other.mTree->copy())) +{ +} + + +template +template +inline Grid::Grid(const Grid& other): + GridBase(other), + mTree(new TreeType(other.constTree())) +{ +} + + +template +inline Grid::Grid(const Grid& other, ShallowCopy): + GridBase(other, ShallowCopy()), + mTree(other.mTree) +{ +} + + +template +inline Grid::Grid(const GridBase& other): + GridBase(other), + mTree(new TreeType) +{ +} + + +//static +template +inline typename Grid::Ptr +Grid::create() +{ + return Grid::create(zeroVal()); +} + + +//static +template +inline typename Grid::Ptr +Grid::create(const ValueType& background) +{ + return Ptr(new Grid(background)); +} + + +//static +template +inline typename Grid::Ptr +Grid::create(TreePtrType tree) +{ + return Ptr(new Grid(tree)); +} + + +//static +template +inline typename Grid::Ptr +Grid::create(const GridBase& other) +{ + return Ptr(new Grid(other)); +} + + +//////////////////////////////////////// + + +template +inline typename Grid::Ptr +Grid::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 +inline GridBase::Ptr +Grid::copyGrid(CopyPolicy treePolicy) const +{ + return this->copy(treePolicy); +} + + +//////////////////////////////////////// + + +template +inline void +Grid::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(tree); +} + + +template +inline void +Grid::newTree() +{ + mTree.reset(new TreeType(this->background())); +} + + +//////////////////////////////////////// + + +template +inline void +Grid::fill(const CoordBBox& bbox, const ValueType& value, bool active, bool sparse) +{ + tree().fill(bbox, value, active, sparse); +} + +template +inline void +Grid::pruneGrid(float tolerance) +{ + this->tree().prune(ValueType(zeroVal() + tolerance)); +} + +#ifndef OPENVDB_2_ABI_COMPATIBLE +template +inline void +Grid::clip(const CoordBBox& bbox) +{ + tree().clip(bbox); +} +#endif + + +template +inline void +Grid::merge(Grid& other, MergePolicy policy) +{ + tree().merge(other.tree(), policy); +} + + +template +template +inline void +Grid::topologyUnion(const Grid& other) +{ + tree().topologyUnion(other.tree()); +} + + +template +template +inline void +Grid::topologyIntersection(const Grid& other) +{ + tree().topologyIntersection(other.tree()); +} + + +template +template +inline void +Grid::topologyDifference(const Grid& other) +{ + tree().topologyDifference(other.tree()); +} + + +//////////////////////////////////////// + + +template +inline void +Grid::evalMinMax(ValueType& minVal, ValueType& maxVal) const +{ + tree().evalMinMax(minVal, maxVal); +} + + +template +inline CoordBBox +Grid::evalActiveVoxelBoundingBox() const +{ + CoordBBox bbox; + tree().evalActiveVoxelBoundingBox(bbox); + return bbox; +} + + +template +inline Coord +Grid::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 +inline void +Grid::readTopology(std::istream& is) +{ + tree().readTopology(is, saveFloatAsHalf()); +} + + +template +inline void +Grid::writeTopology(std::ostream& os) const +{ + tree().writeTopology(os, saveFloatAsHalf()); +} + + +template +inline void +Grid::readBuffers(std::istream& is) +{ + tree().readBuffers(is, saveFloatAsHalf()); +} + + +#ifndef OPENVDB_2_ABI_COMPATIBLE + +template +inline void +Grid::readBuffers(std::istream& is, const CoordBBox& bbox) +{ + tree().readBuffers(is, bbox, saveFloatAsHalf()); +} + + +template +inline void +Grid::readNonresidentBuffers() const +{ + tree().readNonresidentBuffers(); +} + +#endif // !OPENVDB_2_ABI_COMPATIBLE + + +template +inline void +Grid::writeBuffers(std::ostream& os) const +{ + tree().writeBuffers(os, saveFloatAsHalf()); +} + + +template +inline void +Grid::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 +inline typename GridType::Ptr +createGrid(const typename GridType::ValueType& background) +{ + return GridType::create(background); +} + + +template +inline typename GridType::Ptr +createGrid() +{ + return GridType::create(); +} + + +template +inline typename Grid::Ptr +createGrid(TreePtrType tree) +{ + typedef typename TreePtrType::element_type TreeType; + return Grid::create(tree); +} + + +template +typename GridType::Ptr +createLevelSet(Real voxelSize, Real halfWidth) +{ + typedef typename GridType::ValueType ValueType; + + // GridType::ValueType is required to be a floating-point scalar. + BOOST_STATIC_ASSERT(boost::is_floating_point::value); + + typename GridType::Ptr grid = GridType::create( + /*background=*/static_cast(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-2016 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/nuparu/include/openvdb/include/openvdb/Metadata.h b/nuparu/include/openvdb/include/openvdb/Metadata.h new file mode 100644 index 00000000..82a81ee3 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/Metadata.h @@ -0,0 +1,41 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include + +#endif // OPENVDB_METADATA_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/Platform.h b/nuparu/include/openvdb/include/openvdb/Platform.h new file mode 100644 index 00000000..d9fac2ff --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/Platform.h @@ -0,0 +1,210 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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" + +#define PRAGMA(x) _Pragma(#x) + +/// 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 + +/// Macro for determining if there are sufficient C++0x/C++11 features +#ifdef __INTEL_COMPILER + #ifdef __INTEL_CXX11_MODE__ + #define OPENVDB_HAS_CXX11 1 + #endif +#elif defined(__clang__) + #ifndef _LIBCPP_VERSION + #include + #endif + #ifdef _LIBCPP_VERSION + #define OPENVDB_HAS_CXX11 1 + #endif +#elif defined(__GXX_EXPERIMENTAL_CXX0X__) || (__cplusplus > 199711L) + #define OPENVDB_HAS_CXX11 1 +#elif defined(_MSC_VER) + #if (_MSC_VER >= 1700) + #define OPENVDB_HAS_CXX11 1 + #endif +#endif +#if defined(__GNUC__) && !OPENVDB_CHECK_GCC(4, 4) + // ICC uses GCC's standard library headers, so even if the ICC version + // is recent enough for C++11, the GCC version might not be. + #undef OPENVDB_HAS_CXX11 +#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 +/// 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, NodeType::LEVEL == 0 is a compile-time constant expression, +/// so for some template instantiations, the line below it is unreachable. +#if defined(__INTEL_COMPILER) + // Disable ICC remarks 111 ("statement is unreachable"), 128 ("loop is not reachable"), + // 185 ("dynamic initialization in unreachable code"), and 280 ("selector expression + // is constant"). + #define OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN \ + _Pragma("warning (push)") \ + _Pragma("warning (disable:111)") \ + _Pragma("warning (disable:128)") \ + _Pragma("warning (disable:185)") \ + _Pragma("warning (disable:280)") + #define OPENVDB_NO_UNREACHABLE_CODE_WARNING_END \ + _Pragma("warning (pop)") +#elif defined(__clang__) + #define OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN \ + PRAGMA(clang diagnostic push) \ + PRAGMA(clang diagnostic ignored "-Wunreachable-code") + #define OPENVDB_NO_UNREACHABLE_CODE_WARNING_END \ + PRAGMA(clang diagnostic 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 + using boost::math::round; +#endif + +/// Visual C++ uses _copysign() instead of copysign() +#ifdef _MSC_VER + #include + 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 +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 \_API to be exported. The \_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-2016 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/nuparu/include/openvdb/include/openvdb/PlatformConfig.h b/nuparu/include/openvdb/include/openvdb/PlatformConfig.h new file mode 100644 index 00000000..23276edb --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/PlatformConfig.h @@ -0,0 +1,57 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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-2016 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/nuparu/include/openvdb/include/openvdb/Types.h b/nuparu/include/openvdb/include/openvdb/Types.h new file mode 100644 index 00000000..0a2ec16d --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/Types.h @@ -0,0 +1,504 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + +// One-dimensional scalar types +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::Vec2 Vec2R; +typedef math::Vec2 Vec2I; +typedef math::Vec2 Vec2f; +typedef math::Vec2 Vec2H; +using math::Vec2i; +using math::Vec2s; +using math::Vec2d; + +// Three-dimensional vector types +typedef math::Vec3 Vec3R; +typedef math::Vec3 Vec3I; +typedef math::Vec3 Vec3f; +typedef math::Vec3 Vec3H; +using math::Vec3i; +using math::Vec3s; +using math::Vec3d; + +using math::Coord; +using math::CoordBBox; +typedef math::BBox BBoxd; + +// Four-dimensional vector types +typedef math::Vec4 Vec4R; +typedef math::Vec4 Vec4I; +typedef math::Vec4 Vec4f; +typedef math::Vec4 Vec4H; +using math::Vec4i; +using math::Vec4s; +using math::Vec4d; + +// Three-dimensional matrix types +typedef math::Mat3 Mat3R; + +// Four-dimensional matrix types +typedef math::Mat4 Mat4R; +typedef math::Mat4 Mat4d; +typedef math::Mat4 Mat4s; + +// Quaternions +typedef math::Quat QuatR; + +// Dummy type for a voxel with a binary mask value, e.g. the active state +class ValueMask {}; + + +//////////////////////////////////////// + + +/// @brief Integer wrapper, required to distinguish PointIndexGrid and +/// PointDataGrid from Int32Grid and Int64Grid +/// @note @c Kind is a dummy parameter used to create distinct types. +template +struct PointIndex +{ + BOOST_STATIC_ASSERT(boost::is_integral::value); + + typedef IntType_ IntType; + + PointIndex(IntType i = IntType(0)): mIndex(i) {} + + operator IntType() const { return mIndex; } + + /// Needed to support the (zeroVal() + val) idiom. + template + PointIndex operator+(T x) { return PointIndex(mIndex + IntType(x)); } + +private: + IntType mIndex; +}; + + +typedef PointIndex PointIndex32; +typedef PointIndex PointIndex64; + +typedef PointIndex PointDataIndex32; +typedef PointIndex PointDataIndex64; + + +//////////////////////////////////////// + + +template struct VecTraits { + static const bool IsVec = false; + static const int Size = 1; + typedef T ElementType; +}; +template struct VecTraits > { + static const bool IsVec = true; + static const int Size = 2; + typedef T ElementType; +}; +template struct VecTraits > { + static const bool IsVec = true; + static const int Size = 3; + typedef T ElementType; +}; +template struct VecTraits > { + static const bool IsVec = true; + static const int Size = 4; + typedef T ElementType; +}; + + +//////////////////////////////////////// + + +/// @brief CanConvertType::value is @c true if a value +/// of type @a ToType can be constructed from a value of type @a FromType. +/// +/// @note @c boost::is_convertible tests for implicit convertibility only. +/// What we want is the equivalent of C++11's @c std::is_constructible, +/// which allows for explicit conversions as well. Unfortunately, not all +/// compilers support @c std::is_constructible yet, so for now, types that +/// can only be converted explicitly have to be indicated with specializations +/// of this template. +template +struct CanConvertType { enum { value = boost::is_convertible::value }; }; + +// Specializations for vector types, which can be constructed from values +// of their own ValueTypes (or values that can be converted to their ValueTypes), +// but only explicitly +template struct CanConvertType > { enum { value = true }; }; +template struct CanConvertType > { enum { value = true }; }; +template struct CanConvertType > { enum { value = true }; }; +template struct CanConvertType, math::Vec2 > { enum {value = true}; }; +template struct CanConvertType, math::Vec3 > { enum {value = true}; }; +template struct CanConvertType, math::Vec4 > { enum {value = true}; }; +template +struct CanConvertType > { enum { value = CanConvertType::value }; }; +template +struct CanConvertType > { enum { value = CanConvertType::value }; }; +template +struct CanConvertType > { enum { value = CanConvertType::value }; }; +template<> struct CanConvertType { enum {value = true}; }; +template<> struct CanConvertType { enum {value = true}; }; +template +struct CanConvertType { enum {value = CanConvertType::value}; }; +template +struct CanConvertType { enum {value = CanConvertType::value}; }; + +//////////////////////////////////////// + + +// 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: +///

+///
Invariant +///
Does not transform (e.g., tuple, uvw, color) +/// +///
Covariant +///
Apply inverse-transpose transformation: @e w = 0, ignores translation +/// (e.g., gradient/normal) +/// +///
Covariant Normalize +///
Apply inverse-transpose transformation: @e w = 0, ignores translation, +/// vectors are renormalized (e.g., unit normal) +/// +///
Contravariant Relative +///
Apply "regular" transformation: @e w = 0, ignores translation +/// (e.g., displacement, velocity, acceleration) +/// +///
Contravariant Absolute +///
Apply "regular" transformation: @e w = 1, vector translates (e.g., position) +///
+enum VecType { + VEC_INVARIANT = 0, + VEC_COVARIANT, + VEC_COVARIANT_NORMALIZE, + VEC_CONTRAVARIANT_RELATIVE, + VEC_CONTRAVARIANT_ABSOLUTE +}; +enum { NUM_VEC_TYPES = VEC_CONTRAVARIANT_ABSOLUTE + 1 }; + + +/// Specify how grids should be merged during certain (typically multithreaded) operations. +///
+///
MERGE_ACTIVE_STATES +///
The output grid is active wherever any of the input grids is active. +/// +///
MERGE_NODES +///
The output grid's tree has a node wherever any of the input grids' trees +/// has a node, regardless of any active states. +/// +///
MERGE_ACTIVE_STATES_AND_NODES +///
The output grid is active wherever any of the input grids is active, +/// and its tree has a node wherever any of the input grids' trees has a node. +///
+enum MergePolicy { + MERGE_ACTIVE_STATES = 0, + MERGE_NODES, + MERGE_ACTIVE_STATES_AND_NODES +}; + + +//////////////////////////////////////// + + +template const char* typeNameAsString() { return typeid(T).name(); } +template<> inline const char* typeNameAsString() { return "bool"; } +template<> inline const char* typeNameAsString() { return "mask"; } +template<> inline const char* typeNameAsString() { return "float"; } +template<> inline const char* typeNameAsString() { return "double"; } +template<> inline const char* typeNameAsString() { return "int32"; } +template<> inline const char* typeNameAsString() { return "uint32"; } +template<> inline const char* typeNameAsString() { return "int64"; } +template<> inline const char* typeNameAsString() { return "vec2i"; } +template<> inline const char* typeNameAsString() { return "vec2s"; } +template<> inline const char* typeNameAsString() { return "vec2d"; } +template<> inline const char* typeNameAsString() { return "vec3i"; } +template<> inline const char* typeNameAsString() { return "vec3s"; } +template<> inline const char* typeNameAsString() { return "vec3d"; } +template<> inline const char* typeNameAsString() { return "string"; } +template<> inline const char* typeNameAsString() { return "mat4s"; } +template<> inline const char* typeNameAsString() { return "mat4d"; } +template<> inline const char* typeNameAsString() { return "ptidx32"; } +template<> inline const char* typeNameAsString() { return "ptidx64"; } +template<> inline const char* typeNameAsString() { return "ptdataidx32"; } +template<> inline const char* typeNameAsString() { return "ptdataidx64"; } + + +//////////////////////////////////////// + + +/// @brief This struct collects both input and output arguments to "grid combiner" functors +/// used with the tree::TypedGrid::combineExtended() and combine2Extended() methods. +/// AValueType and BValueType are the value types 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 args; +/// myCombineOp(args.setARef(aVal).setBRef(bVal).setAIsActive(true).setBIsActive(false)); +/// @endcode +template +class CombineArgs +{ +public: + typedef AValueType AValueT; + typedef BValueType BValueT; + + 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 AValueType& a, const BValueType& b, AValueType& result, + bool aOn = false, bool bOn = false) + : mAValPtr(&a) + , mBValPtr(&b) + , mResultValPtr(&result) + , mAIsActive(aOn) + , mBIsActive(bOn) + { + this->updateResultActive(); + } + + /// Use this constructor when the result value should be stored in this struct. + CombineArgs(const AValueType& a, const BValueType& b, bool aOn = false, bool bOn = false) + : mAValPtr(&a) + , mBValPtr(&b) + , mResultValPtr(&mResultVal) + , mAIsActive(aOn) + , mBIsActive(bOn) + { + this->updateResultActive(); + } + + /// Get the A input value. + const AValueType& a() const { return *mAValPtr; } + /// Get the B input value. + const BValueType& b() const { return *mBValPtr; } + //@{ + /// Get the output value. + const AValueType& result() const { return *mResultValPtr; } + AValueType& result() { return *mResultValPtr; } + //@} + + /// Set the output value. + CombineArgs& setResult(const AValueType& val) { *mResultValPtr = val; return *this; } + + /// Redirect the A value to a new external source. + CombineArgs& setARef(const AValueType& a) { mAValPtr = &a; return *this; } + /// Redirect the B value to a new external source. + CombineArgs& setBRef(const BValueType& b) { mBValPtr = &b; return *this; } + /// Redirect the result value to a new external destination. + CombineArgs& setResultRef(AValueType& 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 AValueType* mAValPtr; // pointer to input value from A grid + const BValueType* mBValPtr; // pointer to input value from B grid + AValueType mResultVal; // computed output value (unused if stored externally) + AValueType* 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 +struct SwappedCombineOp +{ + SwappedCombineOp(CombineOp& _op): op(_op) {} + + void operator()(CombineArgs& args) + { + CombineArgs 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: +///
+///
CP_NEW +///
Don't copy the member; default construct a new member object instead. +/// +///
CP_SHARE +///
Copy the shared pointer, so that the original and new objects share +/// the same member. +/// +///
CP_COPY +///
Create a deep copy of the member. +///
+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 {}; +// Dummy class that distinguishes constructors during file input +class PartialCreate {}; + +} // 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-2016 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/nuparu/include/openvdb/include/openvdb/io/Archive.h b/nuparu/include/openvdb/include/openvdb/io/Archive.h new file mode 100644 index 00000000..a195fc8b --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/io/Archive.h @@ -0,0 +1,228 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // 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; + + +/// Grid serializer/unserializer +class OPENVDB_API Archive +{ +public: + typedef boost::shared_ptr Ptr; + typedef boost::shared_ptr ConstPtr; + + static const uint32_t DEFAULT_COMPRESSION_FLAGS; + + Archive(); + virtual ~Archive(); + + /// @brief Return a copy of this archive. + virtual Ptr copy() const; + + /// @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 "./", 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 OpenVDB library includes support for the Blosc compressor. + static bool hasBloscCompression(); + + /// Return a bit mask specifying compression options for the data stream. + uint32_t compression() const { return mCompression; } + /// @brief Specify whether and how the data stream should be compressed. + /// @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 setCompression(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; } + + /// @brief Write the grids in the given container to this archive's output stream. + virtual void write(const GridCPtrVec&, const MetaMap& = MetaMap()) const {} + + /// @brief Return @c true if delayed loading is enabled. + /// @details If enabled, delayed loading can be disabled for individual files, + /// but not vice-versa. + /// @note Define the environment variable @c OPENVDB_DISABLE_DELAYED_LOAD + /// to disable delayed loading unconditionally. + static bool isDelayedLoadingEnabled(); + +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&); + + /// Read in and return the number of grids on the input stream. + static int32_t readGridCount(std::istream&); + + /// Populate the given grid from the input stream. + static void readGrid(GridBase::Ptr, const GridDescriptor&, std::istream&); +#ifndef OPENVDB_2_ABI_COMPATIBLE + /// @brief Populate the given grid from the input stream, but only where it + /// intersects the given world-space bounding box. + static void readGrid(GridBase::Ptr, const GridDescriptor&, std::istream&, const BBoxd&); + /// @brief Populate the given grid from the input stream, but only where it + /// intersects the given index-space bounding box. + static void readGrid(GridBase::Ptr, const GridDescriptor&, std::istream&, const CoordBBox&); +#endif + + typedef std::map 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 be 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-2016 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/nuparu/include/openvdb/include/openvdb/io/Compression.h b/nuparu/include/openvdb/include/openvdb/io/Compression.h new file mode 100644 index 00000000..405d61ac --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/io/Compression.h @@ -0,0 +1,619 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include // for negative() +#include "io.h" // for getDataCompression(), etc. +#include +#include +#include +#include +#include + + +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 +///
+///
COMPRESS_NONE +///
On write, don't compress data.
+/// On read, the input stream contains uncompressed data. +/// +///
COMPRESS_ZIP +///
When writing grids other than level sets or fog volumes, apply +/// ZLIB compression to internal and leaf node value buffers.
+/// When reading grids other than level sets or fog volumes, indicate that +/// the value buffers of internal and leaf nodes are ZLIB-compressed.
+/// ZLIB compresses well but is slow. +/// +///
COMPRESS_ACTIVE_MASK +///
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.
+/// On read, nodes might have been stored without inactive values. +/// Where necessary, reconstruct inactive values from available information. +/// +///
COMPRESS_BLOSC +///
When writing grids other than level sets or fog volumes, apply +/// Blosc compression to internal and leaf node value buffers.
+/// When reading grids other than level sets or fog volumes, indicate that +/// the value buffers of internal and leaf nodes are Blosc-compressed.
+/// Blosc is much faster than ZLIB and produces comparable file sizes. +///
+enum { + COMPRESS_NONE = 0, + COMPRESS_ZIP = 0x1, + COMPRESS_ACTIVE_MASK = 0x2, + COMPRESS_BLOSC = 0x4 +}; + +/// 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 + /*6*/ NO_MASK_AND_ALL_VALS // > 2 inactive vals, so no mask compression at all +}; + + +//////////////////////////////////////// + + +/// @brief RealToHalf and its specializations define a mapping from +/// floating-point data types to analogous half float types. +template +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 + static HalfT convert(const T& val) { return val; } +}; +template<> struct RealToHalf { + enum { isReal = true }; + typedef half HalfT; + static HalfT convert(float val) { return HalfT(val); } +}; +template<> struct RealToHalf { + enum { isReal = true }; + typedef half HalfT; + // A half can only be constructed from a float, so cast the value to a float first. + static HalfT convert(double val) { return HalfT(float(val)); } +}; +template<> struct RealToHalf { + enum { isReal = true }; + typedef Vec2H HalfT; + static HalfT convert(const Vec2s& val) { return HalfT(val); } +}; +template<> struct RealToHalf { + enum { isReal = true }; + typedef Vec2H HalfT; + // A half can only be constructed from a float, so cast the vector's elements to floats first. + static HalfT convert(const Vec2d& val) { return HalfT(Vec2s(val)); } +}; +template<> struct RealToHalf { + enum { isReal = true }; + typedef Vec3H HalfT; + static HalfT convert(const Vec3s& val) { return HalfT(val); } +}; +template<> struct RealToHalf { + enum { isReal = true }; + typedef Vec3H HalfT; + // A half can only be constructed from a float, so cast the vector's elements to floats first. + static HalfT convert(const Vec3d& val) { return HalfT(Vec3s(val)); } +}; + + +/// Return the given value truncated to 16-bit float precision. +template +inline T +truncateRealToHalf(const T& val) +{ + return T(RealToHalf::convert(val)); +} + + +//////////////////////////////////////// + + +OPENVDB_API void zipToStream(std::ostream&, const char* data, size_t numBytes); +OPENVDB_API void unzipFromStream(std::istream&, char* data, size_t numBytes); +OPENVDB_API void bloscToStream(std::ostream&, const char* data, size_t valSize, size_t numVals); +OPENVDB_API void bloscFromStream(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 compression whether and how the data is compressed (either COMPRESS_NONE, +/// COMPRESS_ZIP, COMPRESS_ACTIVE_MASK or COMPRESS_BLOSC) +/// @throw IoError if @a compression is COMPRESS_BLOSC but OpenVDB was compiled +/// without Blosc support. +/// @details This default implementation is instantiated only for types +/// whose size can be determined by the sizeof() operator. +template +inline void +readData(std::istream& is, T* data, Index count, uint32_t compression) +{ + if (compression & COMPRESS_BLOSC) { + bloscFromStream(is, reinterpret_cast(data), sizeof(T) * count); + } else if (compression & COMPRESS_ZIP) { + unzipFromStream(is, reinterpret_cast(data), sizeof(T) * count); + } else { + is.read(reinterpret_cast(data), sizeof(T) * count); + } +} + +/// Specialization for std::string input +template<> +inline void +readData(std::istream& is, std::string* data, Index count, uint32_t /*compression*/) +{ + 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 struct HalfReader; +/// Partial specialization for non-floating-point types (no half to float promotion) +template +struct HalfReader { + static inline void read(std::istream& is, T* data, Index count, uint32_t compression) { + readData(is, data, count, compression); + } +}; +/// Partial specialization for floating-point types +template +struct HalfReader { + typedef typename RealToHalf::HalfT HalfT; + static inline void read(std::istream& is, T* data, Index count, uint32_t compression) { + if (count < 1) return; + std::vector halfData(count); // temp buffer into which to read half float values + readData(is, reinterpret_cast(&halfData[0]), count, compression); + // 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 compression whether and how to compress the data (either COMPRESS_NONE, +/// COMPRESS_ZIP, COMPRESS_ACTIVE_MASK or COMPRESS_BLOSC) +/// @throw IoError if @a compression is COMPRESS_BLOSC but OpenVDB was compiled +/// without Blosc support. +/// @details This default implementation is instantiated only for types +/// whose size can be determined by the sizeof() operator. +template +inline void +writeData(std::ostream &os, const T *data, Index count, uint32_t compression) +{ + if (compression & COMPRESS_BLOSC) { + bloscToStream(os, reinterpret_cast(data), sizeof(T), count); + } else if (compression & COMPRESS_ZIP) { + zipToStream(os, reinterpret_cast(data), sizeof(T) * count); + } else { + os.write(reinterpret_cast(data), sizeof(T) * count); + } +} + +/// Specialization for std::string output +template<> +inline void +writeData(std::ostream& os, const std::string* data, Index count, + uint32_t /*compression*/) ///< @todo add compression +{ + 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 struct HalfWriter; +/// Partial specialization for non-floating-point types (no float to half quantization) +template +struct HalfWriter { + static inline void write(std::ostream& os, const T* data, Index count, uint32_t compression) { + writeData(os, data, count, compression); + } +}; +/// Partial specialization for floating-point types +template +struct HalfWriter { + typedef typename RealToHalf::HalfT HalfT; + static inline void write(std::ostream& os, const T* data, Index count, uint32_t compression) { + if (count < 1) return; + // Convert full float values to half float, then output the half float array. + std::vector halfData(count); + for (Index i = 0; i < count; ++i) halfData[i] = RealToHalf::convert(data[i]); + writeData(os, reinterpret_cast(&halfData[0]), count, compression); + } +}; +#ifdef _MSC_VER +/// Specialization to avoid double to float warnings in MSVC +template<> +struct HalfWriter { + typedef RealToHalf::HalfT HalfT; + static inline void write(std::ostream& os, const double* data, Index count, + uint32_t compression) + { + if (count < 1) return; + // Convert full float values to half float, then output the half float array. + std::vector halfData(count); + for (Index i = 0; i < count; ++i) halfData[i] = RealToHalf::convert(data[i]); + writeData(os, reinterpret_cast(&halfData[0]), count, compression); + } +}; +#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 +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 maskCompressed = compression & COMPRESS_ACTIVE_MASK; + + int8_t metadata = NO_MASK_AND_ALL_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(&metadata), /*bytes=*/1); + } + + ValueT background = zeroVal(); + if (const void* bgPtr = getGridBackgroundValuePtr(is)) { + background = *static_cast(bgPtr); + } + ValueT inactiveVal1 = background; + ValueT inactiveVal0 = + ((metadata == NO_MASK_OR_INACTIVE_VALS) ? background : math::negative(background)); + + if (metadata == NO_MASK_AND_ONE_INACTIVE_VAL || + metadata == MASK_AND_ONE_INACTIVE_VAL || + metadata == MASK_AND_TWO_INACTIVE_VALS) + { + // Read one of at most two distinct inactive values. + is.read(reinterpret_cast(&inactiveVal0), sizeof(ValueT)); + if (metadata == MASK_AND_TWO_INACTIVE_VALS) { + // Read the second of two distinct inactive values. + is.read(reinterpret_cast(&inactiveVal1), sizeof(ValueT)); + } + } + + MaskT selectionMask; + if (metadata == MASK_AND_NO_INACTIVE_VALS || + metadata == MASK_AND_ONE_INACTIVE_VAL || + metadata == MASK_AND_TWO_INACTIVE_VALS) + { + // 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 scopedTempBuf; + + Index tempCount = destCount; + if (maskCompressed && metadata != NO_MASK_AND_ALL_VALS + && 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::isReal, ValueT>::read(is, tempBuf, tempCount, compression); + } else { + readData(is, tempBuf, tempCount, compression); + } + + // 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 +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 maskCompress = compress & COMPRESS_ACTIVE_MASK; + + Index tempCount = srcCount; + ValueT* tempBuf = srcBuf; + boost::scoped_array scopedTempBuf; + + int8_t metadata = NO_MASK_AND_ALL_VALS; + + if (!maskCompress) { + os.write(reinterpret_cast(&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 background = zero; + if (const void* bgPtr = getGridBackgroundValuePtr(os)) { + background = *static_cast(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], math::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], math::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], math::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; + } + } + } else if (numUniqueInactiveVals > 2) { + metadata = NO_MASK_AND_ALL_VALS; + } + + os.write(reinterpret_cast(&metadata), /*bytes=*/1); + + if (metadata == NO_MASK_AND_ONE_INACTIVE_VAL || + metadata == MASK_AND_ONE_INACTIVE_VAL || + metadata == MASK_AND_TWO_INACTIVE_VALS) + { + if (!toHalf) { + // Write one of at most two distinct inactive values. + os.write(reinterpret_cast(&inactiveVal[0]), sizeof(ValueT)); + if (metadata == MASK_AND_TWO_INACTIVE_VALS) { + // Write the second of two distinct inactive values. + os.write(reinterpret_cast(&inactiveVal[1]), sizeof(ValueT)); + } + } else { + // Write one of at most two distinct inactive values. + ValueT truncatedVal = static_cast(truncateRealToHalf(inactiveVal[0])); + os.write(reinterpret_cast(&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(&truncatedVal), sizeof(ValueT)); + } + } + } + + if (metadata == NO_MASK_AND_ALL_VALS) { + // 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::isReal, ValueT>::write(os, tempBuf, tempCount, compress); + } else { + writeData(os, tempBuf, tempCount, compress); + } +} + +} // namespace io +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_IO_COMPRESSION_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/io/File.h b/nuparu/include/openvdb/include/openvdb/io/File.h new file mode 100644 index 00000000..90689685 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/io/File.h @@ -0,0 +1,276 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 "io.h" // for MappedFile::Notifier +#include "Archive.h" +#include "GridDescriptor.h" +#include +#include +#include +#include + + +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 NameMap; + typedef NameMap::const_iterator NameMapCIter; + + explicit File(const std::string& filename); + virtual ~File(); + + /// @brief Copy constructor + /// @details The copy will be closed and will not reference the same + /// file descriptor as the original. + File(const File& other); + /// @brief Assignment + /// @details After assignment, this File will be closed and will not + /// reference the same file descriptor as the source File. + File& operator=(const File& other); + + /// @brief Return a copy of this archive. + /// @details The copy will be closed and will not reference the same + /// file descriptor as the original. + virtual boost::shared_ptr copy() const; + + /// @brief Return the name of the file with which this archive is associated. + /// @details The file does not necessarily exist on disk yet. + const std::string& filename() const; + + /// @brief 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. + /// @details If @a delayLoad is true, map the file into memory and enable delayed loading + /// of grids, and if a notifier is provided, call it when the file gets unmapped. + /// @note Define the environment variable @c OPENVDB_DISABLE_DELAYED_LOAD to disable + /// delayed loading unconditionally. + /// @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. + /// @see setCopyMaxBytes + bool open(bool delayLoad = true, const MappedFile::Notifier& = MappedFile::Notifier()); + + /// Return @c true if the file has been opened for reading. + bool isOpen() const; + + /// Close the file once we are done reading from it. + void close(); + + /// @brief Return this file's current size on disk in bytes. + /// @throw IoError if the file size cannot be determined. + Index64 getSize() const; + + /// @brief Return the size in bytes above which this file will not be + /// automatically copied during delayed loading. + Index64 copyMaxBytes() const; + /// @brief If this file is opened with delayed loading enabled, make a private copy + /// of the file if its size in bytes is less than the specified value. + /// @details Making a private copy ensures that the file can't change on disk + /// before it has been fully read. + /// @warning If the file is larger than this size, it is the user's responsibility + /// to ensure that it does not change on disk before it has been fully read. + /// Undefined behavior and/or a crash might result otherwise. + /// @note Copying is enabled by default, but it can be disabled for individual files + /// by setting the maximum size to zero bytes. A default size limit can be specified + /// by setting the environment variable @c OPENVDB_DELAYED_LOAD_COPY_MAX_BYTES + /// to the desired number of bytes. + void setCopyMaxBytes(Index64 bytes); + + /// 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-const + /// 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&); +#ifndef OPENVDB_2_ABI_COMPATIBLE + /// @brief Read a grid, including its data blocks, but only where it + /// intersects the given world-space bounding box. + GridBase::Ptr readGrid(const Name&, const BBoxd&); +#endif + + /// @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. + virtual void write(const GridCPtrVec&, const MetaMap& = MetaMap()) const; + + /// @brief Write the grids in the given container to the file whose name + /// was given in the constructor. + template + 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: + /// 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; + + /// @brief Read a grid, including its data blocks, but only where it + /// intersects the given world-space bounding box. + GridBase::Ptr readGridByName(const Name&, const BBoxd&); + + /// 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; +#ifndef OPENVDB_2_ABI_COMPATIBLE + /// Read in and return the region of the grid specified by the given grid descriptor + /// that intersects the given world-space bounding box. + GridBase::Ptr readGrid(const GridDescriptor&, const BBoxd&) const; + /// Read in and return the region of the grid specified by the given grid descriptor + /// that intersects the given index-space bounding box. + GridBase::Ptr readGrid(const GridDescriptor&, const CoordBBox&) const; +#endif + + /// @brief 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; + + /// @brief Retrieve a grid from @c mNamedGrids. Return a null pointer + /// if @c mNamedGrids was not populated (because this file is random-access). + /// @throw KeyError if no grid with the given name exists in this file. + GridBase::Ptr retrieveCachedGrid(const Name&) const; + + void writeGrids(const GridCPtrVec&, const MetaMap&) const; + + MetaMap::Ptr fileMetadata(); + MetaMap::ConstPtr fileMetadata() const; + + const NameMap& gridDescriptors() const; + NameMap& gridDescriptors(); + + std::istream& inputStream() const; + + friend class ::TestFile; + friend class ::TestStream; + + struct Impl; + boost::scoped_ptr mImpl; +}; + + +//////////////////////////////////////// + + +inline void +File::write(const GridCPtrVec& grids, const MetaMap& meta) const +{ + this->writeGrids(grids, meta); +} + + +template +inline void +File::write(const GridPtrContainerT& container, const MetaMap& meta) const +{ + GridCPtrVec grids; + std::copy(container.begin(), container.end(), std::back_inserter(grids)); + this->writeGrids(grids, meta); +} + +} // namespace io +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_IO_FILE_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/io/GridDescriptor.h b/nuparu/include/openvdb/include/openvdb/io/GridDescriptor.h new file mode 100644 index 00000000..364a2b0d --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/io/GridDescriptor.h @@ -0,0 +1,135 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include +#include + +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-2016 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/nuparu/include/openvdb/include/openvdb/io/Queue.h b/nuparu/include/openvdb/include/openvdb/io/Queue.h new file mode 100644 index 00000000..7ac298ab --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/io/Queue.h @@ -0,0 +1,277 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 Queue.h +/// @author Peter Cucka + +#ifndef OPENVDB_IO_QUEUE_HAS_BEEN_INCLUDED +#define OPENVDB_IO_QUEUE_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include // for std::copy +#include // for std::back_inserter + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace io { + +class Archive; + +/// @brief Queue for asynchronous output of grids to files or streams +/// +/// @warning The queue holds shared pointers to grids. It is not safe +/// to modify a grid that has been placed in the queue. Instead, +/// make a deep copy of the grid (Grid::deepCopy()). +/// +/// @par Example: +/// @code +/// #include +/// #include +/// #include +/// #include +/// +/// using openvdb::io::Queue; +/// +/// struct MyNotifier +/// { +/// // Use a concurrent container, because queue callback functions +/// // must be thread-safe. +/// typedef tbb::concurrent_hash_map FilenameMap; +/// FilenameMap filenames; +/// +/// // Callback function that prints the status of a completed task. +/// void callback(Queue::Id id, Queue::Status status) +/// { +/// const bool ok = (status == Queue::SUCCEEDED); +/// FilenameMap::accessor acc; +/// if (filenames.find(acc, id)) { +/// std::cout << (ok ? "wrote " : "failed to write ") +/// << acc->second << std::endl; +/// filenames.erase(acc); +/// } +/// } +/// }; +/// +/// int main() +/// { +/// // Construct an object to receive notifications from the queue. +/// // The object's lifetime must exceed the queue's. +/// MyNotifier notifier; +/// +/// Queue queue; +/// +/// // Register the callback() method of the MyNotifier object +/// // to receive notifications of completed tasks. +/// queue.addNotifier(boost::bind(&MyNotifier::callback, ¬ifier, _1, _2)); +/// +/// // Queue grids for output (e.g., for each step of a simulation). +/// for (int step = 1; step <= 10; ++step) { +/// openvdb::FloatGrid::Ptr grid = ...; +/// +/// std::ostringstream os; +/// os << "mygrid." << step << ".vdb"; +/// const std::string filename = os.str(); +/// +/// Queue::Id id = queue.writeGrid(grid, openvdb::io::File(filename)); +/// +/// // Associate the filename with the ID of the queued task. +/// MyNotifier::FilenameMap::accessor acc; +/// notifier.filenames.insert(acc, id); +/// acc->second = filename; +/// } +/// } +/// @endcode +/// Output: +/// @code +/// wrote mygrid.1.vdb +/// wrote mygrid.2.vdb +/// wrote mygrid.4.vdb +/// wrote mygrid.3.vdb +/// ... +/// wrote mygrid.10.vdb +/// @endcode +/// Note that tasks do not necessarily complete in the order in which they were queued. +class OPENVDB_API Queue +{ +public: + /// Default maximum queue length (see setCapacity()) + static const Index32 DEFAULT_CAPACITY = 100; + /// @brief Default maximum time in seconds to wait to queue a task + /// when the queue is full (see setTimeout()) + static const Index32 DEFAULT_TIMEOUT = 120; // seconds + + /// ID number of a queued task or of a registered notification callback + typedef Index32 Id; + + /// Status of a queued task + enum Status { UNKNOWN, PENDING, SUCCEEDED, FAILED }; + + + /// Construct a queue with the given capacity. + explicit Queue(Index32 capacity = DEFAULT_CAPACITY); + /// Block until all queued tasks complete (successfully or unsuccessfully). + ~Queue(); + + /// @brief Return @c true if the queue is empty. + bool empty() const; + /// @brief Return the number of tasks currently in the queue. + Index32 size() const; + + /// @brief Return the maximum number of tasks allowed in the queue. + /// @details Once the queue has reached its maximum size, adding + /// a new task will block until an existing task has executed. + Index32 capacity() const; + /// Set the maximum number of tasks allowed in the queue. + void setCapacity(Index32); + + /// Return the maximum number of seconds to wait to queue a task when the queue is full. + Index32 timeout() const; + /// Set the maximum number of seconds to wait to queue a task when the queue is full. + void setTimeout(Index32 seconds = DEFAULT_TIMEOUT); + + /// @brief Return the status of the task with the given ID. + /// @note Querying the status of a task that has already completed + /// (whether successfully or not) removes the task from the status registry. + /// Subsequent queries of its status will return UNKNOWN. + Status status(Id) const; + + typedef boost::function Notifier; + /// @brief Register a function that will be called with a task's ID + /// and status when that task completes, whether successfully or not. + /// @return an ID that can be passed to removeNotifier() to deregister the function + /// @details When multiple notifiers are registered, they are called + /// in the order in which they were registered. + /// @warning Notifiers are called from worker threads, so they must be thread-safe + /// and their lifetimes must exceed that of the queue. They must also not call, + /// directly or indirectly, addNotifier(), removeNotifier() or clearNotifiers(), + /// as that can result in a deadlock. + Id addNotifier(Notifier); + /// Deregister the notifier with the given ID. + void removeNotifier(Id); + /// Deregister all notifiers. + void clearNotifiers(); + + /// @brief Queue a single grid for output to a file or stream. + /// @param grid the grid to be serialized + /// @param archive the io::File or io::Stream to which to output the grid + /// @param fileMetadata optional file-level metadata + /// @return an ID with which the status of the queued task can be queried + /// @throw RuntimeError if the task cannot be queued within the time limit + /// (see setTimeout()) because the queue is full + /// @par Example: + /// @code + /// openvdb::FloatGrid::Ptr grid = ...; + /// + /// openvdb::io::Queue queue; + /// + /// // Write the grid to the file mygrid.vdb. + /// queue.writeGrid(grid, openvdb::io::File("mygrid.vdb")); + /// + /// // Stream the grid to a binary string. + /// std::ostringstream ostr(std::ios_base::binary); + /// queue.writeGrid(grid, openvdb::io::Stream(ostr)); + /// @endcode + Id writeGrid(GridBase::ConstPtr grid, const Archive& archive, + const MetaMap& fileMetadata = MetaMap()); + + /// @brief Queue a container of grids for output to a file. + /// @param grids any iterable container of grid pointers + /// (e.g., a GridPtrVec or GridPtrSet) + /// @param archive the io::File or io::Stream to which to output the grids + /// @param fileMetadata optional file-level metadata + /// @return an ID with which the status of the queued task can be queried + /// @throw RuntimeError if the task cannot be queued within the time limit + /// (see setTimeout()) because the queue is full + /// @par Example: + /// @code + /// openvdb::FloatGrid::Ptr floatGrid = ...; + /// openvdb::BoolGrid::Ptr boolGrid = ...; + /// openvdb::GridPtrVec grids; + /// grids.push_back(floatGrid); + /// grids.push_back(boolGrid); + /// + /// openvdb::io::Queue queue; + /// + /// // Write the grids to the file mygrid.vdb. + /// queue.write(grids, openvdb::io::File("mygrid.vdb")); + /// + /// // Stream the grids to a (binary) string. + /// std::ostringstream ostr(std::ios_base::binary); + /// queue.write(grids, openvdb::io::Stream(ostr)); + /// @endcode + template + Id write(const GridPtrContainer& grids, const Archive& archive, + const MetaMap& fileMetadata = MetaMap()); + +private: + // Disallow copying of instances of this class. + Queue(const Queue&); + Queue& operator=(const Queue&); + + Id writeGridVec(const GridCPtrVec&, const Archive&, const MetaMap&); + + struct Impl; + boost::shared_ptr mImpl; +}; // class Queue + + +template +inline Queue::Id +Queue::write(const GridPtrContainer& container, + const Archive& archive, const MetaMap& metadata) +{ + GridCPtrVec grids; + std::copy(container.begin(), container.end(), std::back_inserter(grids)); + return this->writeGridVec(grids, archive, metadata); +} + +// Specialization for vectors of const Grid pointers; no copying necessary +template<> +inline Queue::Id +Queue::write(const GridCPtrVec& grids, + const Archive& archive, const MetaMap& metadata) +{ + return this->writeGridVec(grids, archive, metadata); +} + +} // namespace io +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_IO_QUEUE_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/io/Stream.h b/nuparu/include/openvdb/include/openvdb/io/Stream.h new file mode 100644 index 00000000..161e0c7f --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/io/Stream.h @@ -0,0 +1,120 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 "Archive.h" +#include +#include + + +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: + /// @brief Read grids from an input stream. + /// @details If @a delayLoad is true, map the contents of the input stream + /// into memory and enable delayed loading of grids. + /// @note Define the environment variable @c OPENVDB_DISABLE_DELAYED_LOAD + /// to disable delayed loading unconditionally. + explicit Stream(std::istream&, bool delayLoad = true); + + /// Construct an archive for stream output. + Stream(); + /// Construct an archive for output to the given stream. + explicit Stream(std::ostream&); + + Stream(const Stream&); + Stream& operator=(const Stream&); + + virtual ~Stream(); + + /// @brief Return a copy of this archive. + virtual Archive::Ptr copy() const; + + /// 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(); + + /// @brief Write the grids in the given container to this archive's output stream. + /// @throw ValueError if this archive was constructed without specifying an output stream. + virtual void write(const GridCPtrVec&, const MetaMap& = MetaMap()) const; + + /// @brief Write the grids in the given container to this archive's output stream. + /// @throw ValueError if this archive was constructed without specifying an output stream. + template + void write(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; + + + struct Impl; + boost::scoped_ptr mImpl; +}; + + +//////////////////////////////////////// + + +template +inline void +Stream::write(const GridPtrContainerT& container, const MetaMap& metadata) const +{ + GridCPtrVec grids; + std::copy(container.begin(), container.end(), std::back_inserter(grids)); + this->write(grids, metadata); +} + +} // namespace io +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_IO_STREAM_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/io/TempFile.h b/nuparu/include/openvdb/include/openvdb/io/TempFile.h new file mode 100644 index 00000000..94f52a4c --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/io/TempFile.h @@ -0,0 +1,81 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 TempFile.h + +#ifndef OPENVDB_IO_TEMPFILE_HAS_BEEN_INCLUDED +#define OPENVDB_IO_TEMPFILE_HAS_BEEN_INCLUDED + +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace io { + +/// Output stream to a unique temporary file +class OPENVDB_API TempFile: public std::ostream +{ +public: + /// @brief Create and open a unique file. + /// @details On UNIX systems, the file is created in the directory specified by + /// the environment variable @c OPENVDB_TEMP_DIR, if that variable is defined, + /// or else in the directory specified by @c TMPDIR, if that variable is defined. + /// Otherwise (and on non-UNIX systems), the file is created in the system default + /// temporary directory. + TempFile(); + ~TempFile(); + + /// Return the path to the temporary file. + const std::string& filename() const; + + /// Return @c true if the file is open for writing. + bool is_open() const; + + /// Close the file. + void close(); + +private: + struct TempFileImpl; + boost::scoped_ptr mImpl; +}; + +} // namespace io +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_IO_TEMPFILE_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/io/io.h b/nuparu/include/openvdb/include/openvdb/io/io.h new file mode 100644 index 00000000..f40f2032 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/io/io.h @@ -0,0 +1,277 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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_IO_HAS_BEEN_INCLUDED +#define OPENVDB_IO_IO_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include +#include // for std::ios_base +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + +class MetaMap; + +namespace io { + +/// @brief Container for metadata describing how to unserialize grids from and/or +/// serialize grids to a stream (which file format, compression scheme, etc. to use) +/// @details This class is mainly for internal use. +class OPENVDB_API StreamMetadata +{ +public: + typedef boost::shared_ptr Ptr; + typedef boost::shared_ptr ConstPtr; + + StreamMetadata(); + StreamMetadata(const StreamMetadata&); + explicit StreamMetadata(std::ios_base&); + ~StreamMetadata(); + + StreamMetadata& operator=(const StreamMetadata&); + + /// @brief Transfer metadata items directly to the given stream. + /// @todo Deprecate direct transfer; use StreamMetadata structs everywhere. + void transferTo(std::ios_base&) const; + + uint32_t fileVersion() const; + void setFileVersion(uint32_t); + + VersionId libraryVersion() const; + void setLibraryVersion(VersionId); + + uint32_t compression() const; + void setCompression(uint32_t); + + uint32_t gridClass() const; + void setGridClass(uint32_t); + + const void* backgroundPtr() const; + void setBackgroundPtr(const void*); + + bool halfFloat() const; + void setHalfFloat(bool); + + bool writeGridStats() const; + void setWriteGridStats(bool); + + //@{ + /// @brief Return a (reference to a) copy of the metadata of the grid currently + /// being read or written. + /// @details Some grid metadata might duplicate information returned by + /// gridClass(), backgroundPtr() and other accessors, but those values + /// are not guaranteed to be kept in sync. + MetaMap& gridMetadata(); + const MetaMap& gridMetadata() const; + //@} + + typedef std::map AuxDataMap; + //@{ + /// @brief Return a map that can be populated with arbitrary user data. + AuxDataMap& auxData(); + const AuxDataMap& auxData() const; + //@} + + /// Return a string describing this stream metadata. + std::string str() const; + +private: + struct Impl; + boost::scoped_ptr mImpl; +}; // class StreamMetadata + + +/// Write a description of the given metadata to an output stream. +std::ostream& operator<<(std::ostream&, const StreamMetadata&); + +std::ostream& operator<<(std::ostream&, const StreamMetadata::AuxDataMap&); + + +//////////////////////////////////////// + + +class File; + +/// @brief Handle to control the lifetime of a memory-mapped .vdb file +class OPENVDB_API MappedFile +{ +public: + typedef boost::shared_ptr Ptr; + + ~MappedFile(); + + /// Return the filename of the mapped file. + std::string filename() const; + + /// @brief Return a new stream buffer for the mapped file. + /// @details Typical usage is + /// @code + /// openvdb::io::MappedFile::Ptr mappedFile = ...; + /// boost::shared_ptr buf = mappedFile->createBuffer(); + /// std::istream istrm(buf.get()); + /// // Read from istrm... + /// @endcode + /// The buffer must persist as long as the stream is open. + boost::shared_ptr createBuffer() const; + + typedef boost::function Notifier; + /// @brief Register a function that will be called with this file's name + /// when the file is unmapped. + void setNotifier(const Notifier&); + /// Deregister the notifier. + void clearNotifier(); + +private: + friend class File; + + explicit MappedFile(const std::string& filename, bool autoDelete = false); + + MappedFile(const MappedFile&); // not copyable + MappedFile& operator=(const MappedFile&); // not copyable + + class Impl; + boost::scoped_ptr mImpl; +}; // class MappedFile + + +//////////////////////////////////////// + + +/// Return a string (possibly empty) describing the given system error code. +std::string getErrorString(int errorNum); + + +/// Return a string (possibly empty) describing the most recent system error. +std::string getErrorString(); + + +//////////////////////////////////////// + + +/// @brief Return the file format version number associated with the given input stream. +/// @sa File::setFormatVersion() +OPENVDB_API uint32_t getFormatVersion(std::ios_base&); + +/// @brief Return the (major, minor) library version number associated with the given input stream. +/// @sa File::setLibraryVersion() +OPENVDB_API VersionId getLibraryVersion(std::ios_base&); + +/// @brief Return a string of the form "./", giving the library +/// and file format version numbers associated with the given input stream. +OPENVDB_API std::string getVersion(std::ios_base&); + +/// 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); + +/// @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 true if floating-point values should be quantized to 16 bits when writing +/// to the given stream or promoted back from 16-bit to full precision when reading from it. +OPENVDB_API bool getHalfFloat(std::ios_base&); +/// @brief Specify whether floating-point values should be quantized to 16 bits when writing +/// to the given stream or promoted back from 16-bit to full precision when reading from it. +OPENVDB_API void setHalfFloat(std::ios_base&, bool); + +/// @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); + +/// @brief Return @c true if grid statistics (active voxel count and bounding box, etc.) +/// should be computed and stored as grid metadata when writing to the given stream. +OPENVDB_API bool getWriteGridStatsMetadata(std::ios_base&); +/// @brief Specify whether to compute grid statistics (active voxel count and bounding box, etc.) +/// and store them as grid metadata when writing to the given stream. +OPENVDB_API void setWriteGridStatsMetadata(std::ios_base&, bool writeGridStats); + +/// @brief Return a shared pointer to the memory-mapped file with which the given stream +/// is associated, or a null pointer if the stream is not associated with a memory-mapped file. +OPENVDB_API boost::shared_ptr getMappedFilePtr(std::ios_base&); +/// @brief Associate the given stream with (a shared pointer to) a memory-mapped file. +/// @note The shared pointer object (not just the io::MappedFile object to which it points) +/// must remain valid until the file is closed. +OPENVDB_API void setMappedFilePtr(std::ios_base&, boost::shared_ptr&); + +/// @brief Return a shared pointer to an object that stores metadata (file format, +/// compression scheme, etc.) for use when reading from or writing to the given stream. +OPENVDB_API boost::shared_ptr getStreamMetadataPtr(std::ios_base&); +/// @brief Associate the given stream with (a shared pointer to) an object that stores +/// metadata (file format, compression scheme, etc.) for use when reading from +/// or writing to the stream. +/// @details If @a transfer is true, copy metadata from the object directly to the stream +/// (for backward compatibility with older versions of the library). +/// @note The shared pointer object (not just the io::StreamMetadata object to which it points) +/// must remain valid until the file is closed. +OPENVDB_API void setStreamMetadataPtr(std::ios_base&, + boost::shared_ptr&, bool transfer = true); +/// @brief Dissociate the given stream from its metadata object (if it has one) +/// and return a shared pointer to the object. +OPENVDB_API boost::shared_ptr clearStreamMetadataPtr(std::ios_base&); + +} // namespace io +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_IO_IO_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/math/BBox.h b/nuparu/include/openvdb/include/openvdb/math/BBox.h new file mode 100644 index 00000000..15cc8bd6 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/math/BBox.h @@ -0,0 +1,467 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 math::isApproxEqual() and math::Tolerance() +#include "Vec3.h" +#include +#include // for min/max +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + +/// @brief Axis-aligned bounding box +template +class BBox +{ +public: + typedef Vec3T Vec3Type; + typedef Vec3T ValueType; + typedef Vec3T VectorType; + typedef typename Vec3Type::ValueType ElementType; + + /// @brief Default constructor creates an invalid BBox + BBox(); + + /// @brief Constructor based on a minimum and maximum point. + BBox(const Vec3T& xyzMin, const Vec3T& xyzMax); + + /// @brief Constructor based on a minimum and maximum point. + /// If sorted is false the points will be sorted by x,y,z component. + BBox(const Vec3T& xyzMin, const Vec3T& xyzMax, bool sorted); + + /// @brief Contruct a cubical BBox from a minimum coordinate and a + /// single edge length. + /// @note inclusive for integral ElementTypes + BBox(const Vec3T& xyzMin, const ElementType& length); + + /// @brief Constructor based on a raw array of six points. If + /// sorted is false the points will be sorted by x,y,z component. + explicit BBox(const ElementType* xyz, bool sorted = true); + + /// @brief Copy constructor + BBox(const BBox& other); + + /// @brief Sort the min/max by x,y,z component. + void sort(); + + /// @brief Return a const reference to the minimum point of the BBox + const Vec3T& min() const { return mMin; } + + /// @brief Return a const reference to the maximum point of the BBox + const Vec3T& max() const { return mMax; } + + /// @brief Return a non-const reference to the minimum point of the BBox + Vec3T& min() { return mMin; } + + /// @brief Return a non-const reference to the maximum point of the BBox + Vec3T& max() { return mMax; } + + /// @brief Return true if the two BBox'es are identical + bool operator==(const BBox& rhs) const; + + /// @brief Return true if the two BBox'es are not identical + bool operator!=(const BBox& rhs) const { return !(*this == rhs); } + + /// @brief Return true if the BBox is empty, i.e. has no + /// (positive) volume. + bool empty() const; + + /// @brief Return true if the BBox has a (positive) volume. + bool hasVolume() const { return !this->empty(); } + + /// @brief Return true if the BBox is valid, i.e. as a (positive) volume. + operator bool() const { return !this->empty(); } + + /// @brief Return true if the all components of mMin <= mMax, + /// i.e. the volume is not negative. + /// @note For floating point values a tolerance is used for this test. + bool isSorted() const; + + /// @brief Return the center point of the BBox + Vec3d getCenter() const; + + /// @brief Returns the extents of the BBox, i.e. the length per axis + /// for floating points values or number of grids per axis points + /// integral values. + /// @note inclusive for integral ElementTypes + Vec3T extents() const; + + /// @brief Return the volume spanned by this BBox. + ElementType volume() const { Vec3T e = this->extents(); return e[0] * e[1] * e[2]; } + + /// Return the index (0, 1 or 2) of the longest axis. + size_t maxExtent() const { return MaxIndex(mMax - mMin); } + + /// Return the index (0, 1 or 2) of the shortest axis. + size_t minExtent() const { return MinIndex(mMax - mMin); } + + /// Return @c true if point (x, y, z) is inside this bounding box. + bool isInside(const Vec3T& 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 Vec3T& 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 ElementTypes + void expand(const Vec3T& xyzMin, const ElementType& length); + + /// Translate this bounding box by \f$(t_x, t_y, t_z)\f$. + void translate(const Vec3T& t); + + /// Apply a map to this bounding box + template + BBox applyMap(const MapType& map) const; + + /// Apply the inverse of a map to this bounding box + template + BBox applyInverseMap(const MapType& map) const; + + /// 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: + Vec3T mMin, mMax; +}; // class BBox + + +//////////////////////////////////////// + + +template +inline +BBox::BBox(): + mMin( std::numeric_limits::max()), + mMax(-std::numeric_limits::max()) +{ +} + +template +inline +BBox::BBox(const Vec3T& xyzMin, const Vec3T& xyzMax): + mMin(xyzMin), mMax(xyzMax) +{ +} + +template +inline +BBox::BBox(const Vec3T& xyzMin, const Vec3T& xyzMax, bool sorted): + mMin(xyzMin), mMax(xyzMax) +{ + if (!sorted) this->sort(); +} + +template +inline +BBox::BBox(const Vec3T& xyzMin, const ElementType& length): + mMin(xyzMin), mMax(xyzMin) +{ + // min and max are inclusive for integral ElementType + const ElementType size = boost::is_integral::value ? length-1 : length; + mMax[0] += size; + mMax[1] += size; + mMax[2] += size; +} + +template +inline +BBox::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 +inline +BBox::BBox(const BBox& other): + mMin(other.mMin), mMax(other.mMax) +{ +} + + +//////////////////////////////////////// + + +template +inline bool +BBox::empty() const +{ + if (boost::is_integral::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 +inline bool +BBox::operator==(const BBox& rhs) const +{ + if (boost::is_integral::value) { + return mMin == rhs.min() && mMax == rhs.max(); + } else { + return math::isApproxEqual(mMin, rhs.min()) && math::isApproxEqual(mMax, rhs.max()); + } +} + + +template +inline void +BBox::sort() +{ + Vec3T tMin(mMin), tMax(mMax); + for (int i = 0; i < 3; ++i) { + mMin[i] = std::min(tMin[i], tMax[i]); + mMax[i] = std::max(tMin[i], tMax[i]); + } +} + + +template +inline bool +BBox::isSorted() const +{ + if (boost::is_integral::value) { + return (mMin[0] <= mMax[0] && mMin[1] <= mMax[1] && mMin[2] <= mMax[2]); + } else { + ElementType t = math::Tolerance::value(); + return (mMin[0] < (mMax[0] + t) && mMin[1] < (mMax[1] + t) && mMin[2] < (mMax[2] + t)); + } +} + + +template +inline Vec3d +BBox::getCenter() const +{ + return (Vec3d(mMin.asPointer()) + Vec3d(mMax.asPointer())) * 0.5; +} + + +template +inline Vec3T +BBox::extents() const +{ + if (boost::is_integral::value) { + return (mMax - mMin) + Vec3T(1, 1, 1); + } else { + return (mMax - mMin); + } +} + +//////////////////////////////////////// + + +template +inline bool +BBox::isInside(const Vec3T& xyz) const +{ + if (boost::is_integral::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 = math::Tolerance::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 +inline bool +BBox::isInside(const BBox& b) const +{ + if (boost::is_integral::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 = math::Tolerance::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 +inline bool +BBox::hasOverlap(const BBox& b) const +{ + if (boost::is_integral::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 = math::Tolerance::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 +inline void +BBox::expand(ElementType dx) +{ + dx = std::abs(dx); + for (int i = 0; i < 3; ++i) { + mMin[i] -= dx; + mMax[i] += dx; + } +} + + +template +inline void +BBox::expand(const Vec3T& xyz) +{ + for (int i = 0; i < 3; ++i) { + mMin[i] = std::min(mMin[i], xyz[i]); + mMax[i] = std::max(mMax[i], xyz[i]); + } +} + + +template +inline void +BBox::expand(const BBox& b) +{ + for (int i = 0; i < 3; ++i) { + mMin[i] = std::min(mMin[i], b.min()[i]); + mMax[i] = std::max(mMax[i], b.max()[i]); + } +} + +template +inline void +BBox::expand(const Vec3T& xyzMin, const ElementType& length) +{ + const ElementType size = boost::is_integral::value ? length-1 : length; + for (int i = 0; i < 3; ++i) { + mMin[i] = std::min(mMin[i], xyzMin[i]); + mMax[i] = std::max(mMax[i], xyzMin[i] + size); + } +} + + +template +inline void +BBox::translate(const Vec3T& dx) +{ + mMin += dx; + mMax += dx; +} + +template +template +inline BBox +BBox::applyMap(const MapType& map) const +{ + typedef Vec3 Vec3R; + BBox bbox; + bbox.expand(map.applyMap(Vec3R(mMin[0], mMin[1], mMin[2]))); + bbox.expand(map.applyMap(Vec3R(mMin[0], mMin[1], mMax[2]))); + bbox.expand(map.applyMap(Vec3R(mMin[0], mMax[1], mMin[2]))); + bbox.expand(map.applyMap(Vec3R(mMax[0], mMin[1], mMin[2]))); + bbox.expand(map.applyMap(Vec3R(mMax[0], mMax[1], mMin[2]))); + bbox.expand(map.applyMap(Vec3R(mMax[0], mMin[1], mMax[2]))); + bbox.expand(map.applyMap(Vec3R(mMin[0], mMax[1], mMax[2]))); + bbox.expand(map.applyMap(Vec3R(mMax[0], mMax[1], mMax[2]))); + return bbox; +} + +template +template +inline BBox +BBox::applyInverseMap(const MapType& map) const +{ + typedef Vec3 Vec3R; + BBox bbox; + bbox.expand(map.applyInverseMap(Vec3R(mMin[0], mMin[1], mMin[2]))); + bbox.expand(map.applyInverseMap(Vec3R(mMin[0], mMin[1], mMax[2]))); + bbox.expand(map.applyInverseMap(Vec3R(mMin[0], mMax[1], mMin[2]))); + bbox.expand(map.applyInverseMap(Vec3R(mMax[0], mMin[1], mMin[2]))); + bbox.expand(map.applyInverseMap(Vec3R(mMax[0], mMax[1], mMin[2]))); + bbox.expand(map.applyInverseMap(Vec3R(mMax[0], mMin[1], mMax[2]))); + bbox.expand(map.applyInverseMap(Vec3R(mMin[0], mMax[1], mMax[2]))); + bbox.expand(map.applyInverseMap(Vec3R(mMax[0], mMax[1], mMax[2]))); + return bbox; +} + +//////////////////////////////////////// + + +template +inline std::ostream& +operator<<(std::ostream& os, const BBox& 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-2016 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/nuparu/include/openvdb/include/openvdb/math/ConjGradient.h b/nuparu/include/openvdb/include/openvdb/math/ConjGradient.h new file mode 100644 index 00000000..6e752fef --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/math/ConjGradient.h @@ -0,0 +1,1841 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 ConjGradient.h +/// @authors D.J. Hill, Peter Cucka +/// @brief Preconditioned conjugate gradient solver (solves @e Ax = @e b using +/// the conjugate gradient method with one of a selection of preconditioners) + +#ifndef OPENVDB_MATH_CONJGRADIENT_HAS_BEEN_INCLUDED +#define OPENVDB_MATH_CONJGRADIENT_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include "Math.h" // for Abs(), isZero(), Max(), Sqrt() +#include +#include +#include +#include +#include // for std::lower_bound() +#include +#include // for std::isfinite() +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { +namespace pcg { + +typedef Index32 SizeType; + +typedef tbb::blocked_range SizeRange; + +template class Vector; + +template class SparseStencilMatrix; + +template class Preconditioner; +template class JacobiPreconditioner; +template class IncompleteCholeskyPreconditioner; + +/// Information about the state of a conjugate gradient solution +struct State { + bool success; + int iterations; + double relativeError; + double absoluteError; +}; + + +/// Return default termination conditions for a conjugate gradient solver. +template +inline State +terminationDefaults() +{ + State s; + s.success = false; + s.iterations = 50; + s.relativeError = 1.0e-6; + s.absoluteError = std::numeric_limits::epsilon() * 100.0; + return s; +} + + +//////////////////////////////////////// + + +/// @brief Solve @e Ax = @e b via the preconditioned conjugate gradient method. +/// +/// @param A a symmetric, positive-definite, @e N x @e N matrix +/// @param b a vector of size @e N +/// @param x a vector of size @e N +/// @param preconditioner a Preconditioner matrix +/// @param termination termination conditions given as a State object with the following fields: +///
+///
success +///
ignored +///
iterations +///
the maximum number of iterations, with or without convergence +///
relativeError +///
the relative error ||bA@f$\hat{x}@f$|| / ||b|| +/// that denotes convergence +///
absoluteError +///
the absolute error ||bA@f$\hat{x}@f$|| that denotes convergence +/// +/// @throw ArithmeticError if either @a x or @a b is not of the appropriate size. +template +inline State +solve( + const PositiveDefMatrix& A, + const Vector& b, + Vector& x, + Preconditioner& preconditioner, + const State& termination = terminationDefaults()); + + +/// @brief Solve @e Ax = @e b via the preconditioned conjugate gradient method. +/// +/// @param A a symmetric, positive-definite, @e N x @e N matrix +/// @param b a vector of size @e N +/// @param x a vector of size @e N +/// @param preconditioner a Preconditioner matrix +/// @param termination termination conditions given as a State object with the following fields: +///
+///
success +///
ignored +///
iterations +///
the maximum number of iterations, with or without convergence +///
relativeError +///
the relative error ||bA@f$\hat{x}@f$|| / ||b|| +/// that denotes convergence +///
absoluteError +///
the absolute error ||bA@f$\hat{x}@f$|| that denotes convergence +/// @param interrupter an object adhering to the util::NullInterrupter interface +/// with which computation can be interrupted +/// +/// @throw ArithmeticError if either @a x or @a b is not of the appropriate size. +/// @throw RuntimeError if the computation is interrupted. +template +inline State +solve( + const PositiveDefMatrix& A, + const Vector& b, + Vector& x, + Preconditioner& preconditioner, + Interrupter& interrupter, + const State& termination = terminationDefaults()); + + +//////////////////////////////////////// + + +/// Lightweight, variable-length vector +template +class Vector +{ +public: + typedef T ValueType; + typedef boost::shared_ptr Ptr; + + /// Construct an empty vector. + Vector(): mData(NULL), mSize(0) {} + /// Construct a vector of @a n elements, with uninitialized values. + Vector(SizeType n): mData(new T[n]), mSize(n) {} + /// Construct a vector of @a n elements and initialize each element to the given value. + Vector(SizeType n, const ValueType& val): mData(new T[n]), mSize(n) { this->fill(val); } + + ~Vector() { mSize = 0; delete[] mData; mData = NULL; } + + /// Deep copy the given vector. + Vector(const Vector&); + /// Deep copy the given vector. + Vector& operator=(const Vector&); + + /// Return the number of elements in this vector. + SizeType size() const { return mSize; } + /// Return @c true if this vector has no elements. + bool empty() const { return (mSize == 0); } + + /// @brief Reset this vector to have @a n elements, with uninitialized values. + /// @warning All of this vector's existing values will be lost. + void resize(SizeType n); + + /// Swap internal storage with another vector, which need not be the same size. + void swap(Vector& other) { std::swap(mData, other.mData); std::swap(mSize, other.mSize); } + + /// Set all elements of this vector to @a value. + void fill(const ValueType& value); + + //@{ + /// @brief Multiply each element of this vector by @a s. + template void scale(const Scalar& s); + template Vector& operator*=(const Scalar& s) { this->scale(s); return *this; } + //@} + + /// Return the dot product of this vector with the given vector, which must be the same size. + ValueType dot(const Vector&) const; + + /// Return the infinity norm of this vector. + ValueType infNorm() const; + /// Return the L2 norm of this vector. + ValueType l2Norm() const { return Sqrt(this->dot(*this)); } + + /// Return @c true if every element of this vector has a finite value. + bool isFinite() const; + + /// @brief Return @c true if this vector is equivalent to the given vector + /// to within the specified tolerance. + template + bool eq(const Vector& other, + ValueType eps = Tolerance::value()) const; + + /// Return a string representation of this vector. + std::string str() const; + + //@{ + /// @brief Return the value of this vector's ith element. + inline T& at(SizeType i) { return mData[i]; } + inline const T& at(SizeType i) const { return mData[i]; } + inline T& operator[](SizeType i) { return this->at(i); } + inline const T& operator[](SizeType i) const { return this->at(i); } + //@} + + //@{ + /// @brief Return a pointer to this vector's elements. + inline T* data() { return mData; } + inline const T* data() const { return mData; } + inline const T* constData() const { return mData; } + //@} + +private: + // Functor for use with tbb::parallel_for() + template struct ScaleOp; + struct DeterministicDotProductOp; + // Functors for use with tbb::parallel_reduce() + template struct EqOp; + struct InfNormOp; + struct IsFiniteOp; + + T* mData; + SizeType mSize; +}; + +typedef Vector VectorS; +typedef Vector VectorD; +//typedef Vector LinearVector; + + +//////////////////////////////////////// + + +/// @brief Sparse, square matrix representing a 3D stencil operator of size @a STENCIL_SIZE +/// @details The implementation is a variation on compressed row storage (CRS). +template +class SparseStencilMatrix +{ +public: + typedef ValueType_ ValueType; + typedef Vector VectorType; + typedef boost::shared_ptr Ptr; + + class ConstValueIter; + class ConstRow; + class RowEditor; + + static const ValueType sZeroValue; + + /// Construct an @a n x @a n matrix with at most @a STENCIL_SIZE nonzero elements per row. + SparseStencilMatrix(SizeType n); + + /// Deep copy the given matrix. + SparseStencilMatrix(const SparseStencilMatrix&); + + //@{ + /// Return the number of rows in this matrix. + SizeType numRows() const { return mNumRows; } + SizeType size() const { return mNumRows; } + //@} + + /// @brief Set the value at the given coordinates. + /// @warning It is not safe to set values in the same row simultaneously + /// from multiple threads. + void setValue(SizeType row, SizeType col, const ValueType&); + + //@{ + /// @brief Return the value at the given coordinates. + /// @warning It is not safe to get values from a row while another thread + /// is setting values in that row. + const ValueType& getValue(SizeType row, SizeType col) const; + const ValueType& operator()(SizeType row, SizeType col) const; + //@} + + /// Return a read-only view onto the given row of this matrix. + ConstRow getConstRow(SizeType row) const; + + /// Return a read/write view onto the given row of this matrix. + RowEditor getRowEditor(SizeType row); + + //@{ + /// @brief Multiply all elements in the matrix by @a s; + template void scale(const Scalar& s); + template + SparseStencilMatrix& operator*=(const Scalar& s) { this->scale(s); return *this; } + //@} + + /// @brief Multiply this matrix by @a inVec and return the result in @a resultVec. + /// @throw ArithmeticError if either @a inVec or @a resultVec is not of size @e N, + /// where @e N x @e N is the size of this matrix. + template + void vectorMultiply(const Vector& inVec, Vector& resultVec) const; + + /// @brief Multiply this matrix by the vector represented by the array @a inVec + /// and return the result in @a resultVec. + /// @warning Both @a inVec and @a resultVec must have at least @e N elements, + /// where @e N x @e N is the size of this matrix. + template + void vectorMultiply(const VecValueType* inVec, VecValueType* resultVec) const; + + /// @brief Return @c true if this matrix is equivalent to the given matrix + /// to within the specified tolerance. + template + bool eq(const SparseStencilMatrix& other, + ValueType eps = Tolerance::value()) const; + + /// Return @c true if every element of this matrix has a finite value. + bool isFinite() const; + + /// Return a string representation of this matrix. + std::string str() const; + +private: + struct RowData { + RowData(ValueType* v, SizeType* c, SizeType& s): mVals(v), mCols(c), mSize(s) {} + ValueType* mVals; SizeType* mCols; SizeType& mSize; + }; + + struct ConstRowData { + ConstRowData(const ValueType* v, const SizeType* c, const SizeType& s): + mVals(v), mCols(c), mSize(s) {} + const ValueType* mVals; const SizeType* mCols; const SizeType& mSize; + }; + + /// Base class for row accessors + template + class RowBase + { + public: + typedef DataType_ DataType; + + static SizeType capacity() { return STENCIL_SIZE; } + + RowBase(const DataType& data): mData(data) {} + + bool empty() const { return (mData.mSize == 0); } + const SizeType& size() const { return mData.mSize; } + + const ValueType& getValue(SizeType columnIdx, bool& active) const; + const ValueType& getValue(SizeType columnIdx) const; + + /// Return an iterator over the stored values in this row. + ConstValueIter cbegin() const; + + /// @brief Return @c true if this row is equivalent to the given row + /// to within the specified tolerance. + template + bool eq(const RowBase& other, + ValueType eps = Tolerance::value()) const; + + /// @brief Return the dot product of this row with the first + /// @a vecSize elements of @a inVec. + /// @warning @a inVec must have at least @a vecSize elements. + template + VecValueType dot(const VecValueType* inVec, SizeType vecSize) const; + + /// Return the dot product of this row with the given vector. + template + VecValueType dot(const Vector& inVec) const; + + /// Return a string representation of this row. + std::string str() const; + + protected: + friend class ConstValueIter; + + const ValueType& value(SizeType i) const { return mData.mVals[i]; } + SizeType column(SizeType i) const { return mData.mCols[i]; } + + /// @brief Return the array index of the first column index that is + /// equal to or greater than the given column index. + /// @note If @a columnIdx is larger than any existing column index, + /// the return value will point beyond the end of the array. + SizeType find(SizeType columnIdx) const; + + DataType mData; + }; + + typedef RowBase ConstRowBase; + +public: + /// Iterator over the stored values in a row of this matrix + class ConstValueIter + { + public: + const ValueType& operator*() const + { + if (mData.mSize == 0) return SparseStencilMatrix::sZeroValue; + return mData.mVals[mCursor]; + } + + SizeType column() const { return mData.mCols[mCursor]; } + + void increment() { mCursor++; } + ConstValueIter& operator++() { increment(); return *this; } + operator bool() const { return (mCursor < mData.mSize); } + + void reset() { mCursor = 0; } + + private: + friend class SparseStencilMatrix; + ConstValueIter(const RowData& d): mData(d.mVals, d.mCols, d.mSize), mCursor(0) {} + ConstValueIter(const ConstRowData& d): mData(d), mCursor(0) {} + + const ConstRowData mData; + SizeType mCursor; + }; + + + /// Read-only accessor to a row of this matrix + class ConstRow: public ConstRowBase + { + public: + ConstRow(const ValueType* valueHead, const SizeType* columnHead, const SizeType& rowSize); + }; // class ConstRow + + + /// Read/write accessor to a row of this matrix + class RowEditor: public RowBase<> + { + public: + RowEditor(ValueType* valueHead, SizeType* columnHead, SizeType& rowSize, SizeType colSize); + + /// Set the number of entries in this row to zero. + void clear(); + + /// @brief Set the value of the entry in the specified column. + /// @return the current number of entries stored in this row. + SizeType setValue(SizeType column, const ValueType& value); + + //@{ + /// @brief Scale all of the entries in this row. + template void scale(const Scalar&); + template + RowEditor& operator*=(const Scalar& s) { this->scale(s); return *this; } + //@} + + private: + const SizeType mNumColumns; // used only for bounds checking + }; // class RowEditor + +private: + // Functors for use with tbb::parallel_for() + struct MatrixCopyOp; + template struct VecMultOp; + template struct RowScaleOp; + + // Functors for use with tbb::parallel_reduce() + struct IsFiniteOp; + template struct EqOp; + + const SizeType mNumRows; + boost::scoped_array mValueArray; + boost::scoped_array mColumnIdxArray; + boost::scoped_array mRowSizeArray; +}; // class SparseStencilMatrix + + +//////////////////////////////////////// + + +/// Base class for conjugate gradient preconditioners +template +class Preconditioner +{ +public: + typedef T ValueType; + typedef boost::shared_ptr Ptr; + + template Preconditioner(const SparseStencilMatrix&) {} + virtual ~Preconditioner() {} + + virtual bool isValid() const { return true; } + + /// @brief Apply this preconditioner to a residue vector: + /// @e z = M−1r + /// @param r residue vector + /// @param[out] z preconditioned residue vector + virtual void apply(const Vector& r, Vector& z) = 0; +}; + + +//////////////////////////////////////// + + +namespace internal { + +// Functor for use with tbb::parallel_for() to copy data from one array to another +template +struct CopyOp +{ + CopyOp(const T* from_, T* to_): from(from_), to(to_) {} + + void operator()(const SizeRange& range) const { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) to[n] = from[n]; + } + + const T* from; + T* to; +}; + + +// Functor for use with tbb::parallel_for() to fill an array with a constant value +template +struct FillOp +{ + FillOp(T* data_, const T& val_): data(data_), val(val_) {} + + void operator()(const SizeRange& range) const { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) data[n] = val; + } + + T* data; + const T val; +}; + + +// Functor for use with tbb::parallel_for() that computes a * x + y +template +struct LinearOp +{ + LinearOp(const T& a_, const T* x_, const T* y_, T* out_): a(a_), x(x_), y(y_), out(out_) {} + + void operator()(const SizeRange& range) const { + if (isExactlyEqual(a, T(1))) { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) out[n] = x[n] + y[n]; + } else if (isExactlyEqual(a, T(-1))) { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) out[n] = -x[n] + y[n]; + } else { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) out[n] = a * x[n] + y[n]; + } + } + + const T a, *x, *y; + T* out; +}; + +} // namespace internal + + +//////////////////////////////////////// + + +inline std::ostream& +operator<<(std::ostream& os, const State& state) +{ + os << (state.success ? "succeeded with " : "") + << "rel. err. " << state.relativeError << ", abs. err. " << state.absoluteError + << " after " << state.iterations << " iteration" << (state.iterations == 1 ? "" : "s"); + return os; +} + + +//////////////////////////////////////// + + +template +inline +Vector::Vector(const Vector& other): mData(new T[other.mSize]), mSize(other.mSize) +{ + tbb::parallel_for(SizeRange(0, mSize), + internal::CopyOp(/*from=*/other.mData, /*to=*/mData)); +} + + +template +inline +Vector& Vector::operator=(const Vector& other) +{ + // Update the internal storage to the correct size + + if (mSize != other.mSize) { + mSize = other.mSize; + delete[] mData; + mData = new T[mSize]; + } + + // Deep copy the data + tbb::parallel_for(SizeRange(0, mSize), + internal::CopyOp(/*from=*/other.mData, /*to=*/mData)); + + return *this; +} + + +template +inline void +Vector::resize(SizeType n) +{ + if (n != mSize) { + if (mData) delete[] mData; + mData = new T[n]; + mSize = n; + } +} + + +template +inline void +Vector::fill(const ValueType& value) +{ + tbb::parallel_for(SizeRange(0, mSize), internal::FillOp(mData, value)); +} + + +template +template +struct Vector::ScaleOp +{ + ScaleOp(T* data_, const Scalar& s_): data(data_), s(s_) {} + + void operator()(const SizeRange& range) const { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) data[n] *= s; + } + + T* data; + const Scalar s; +}; + + +template +template +inline void +Vector::scale(const Scalar& s) +{ + tbb::parallel_for(SizeRange(0, mSize), ScaleOp(mData, s)); +} + + +template +struct Vector::DeterministicDotProductOp +{ + DeterministicDotProductOp(const T* a_, const T* b_, + const SizeType binCount_, const SizeType arraySize_, T* reducetmp_): + a(a_), b(b_), binCount(binCount_), arraySize(arraySize_), reducetmp(reducetmp_) {} + + void operator()(const SizeRange& range) const + { + + const SizeType binSize = arraySize / binCount; + + // Iterate over bins (array segments) + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { + const SizeType begin = n * binSize; + const SizeType end = (n == binCount-1) ? arraySize : begin + binSize; + + // Compute the partial sum for this array segment + T sum = zeroVal(); + for (SizeType i = begin; i < end; ++i) { + + sum += a[i] * b[i]; + } + // Store the partial sum + reducetmp[n] = sum; + } + } + + + const T* a; + const T* b; + const SizeType binCount; + const SizeType arraySize; + T* reducetmp; +}; + +template +inline T +Vector::dot(const Vector& other) const +{ + assert(this->size() == other.size()); + + const T* aData = this->data(); + const T* bData = other.data(); + + SizeType arraySize = this->size(); + + T result = zeroVal(); + + if (arraySize < 1024) { + + // Compute the dot product in serial for small arrays + + for (SizeType n = 0; n < arraySize; ++n) { + result += aData[n] * bData[n]; + } + + } else { + + // Compute the dot product by segmenting the arrays into + // a predetermined number of sub arrays in parallel and + // accumulate the finial result in series. + + const SizeType binCount = 100; + T partialSums[100]; + + tbb::parallel_for(SizeRange(0, binCount), + DeterministicDotProductOp(aData, bData, binCount, arraySize, partialSums)); + + for (SizeType n = 0; n < binCount; ++n) { + result += partialSums[n]; + } + } + + return result; +} + + +template +struct Vector::InfNormOp +{ + InfNormOp(const T* data_): data(data_) {} + + T operator()(const SizeRange& range, T maxValue) const + { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { + maxValue = Max(maxValue, Abs(data[n])); + } + return maxValue; + } + + static T join(T max1, T max2) { return Max(max1, max2); } + + const T* data; +}; + + +template +inline T +Vector::infNorm() const +{ + // Parallelize over the elements of this vector. + T result = tbb::parallel_reduce(SizeRange(0, this->size()), /*seed=*/zeroVal(), + InfNormOp(this->data()), InfNormOp::join); + return result; +} + + +template +struct Vector::IsFiniteOp +{ + IsFiniteOp(const T* data_): data(data_) {} + + bool operator()(const SizeRange& range, bool finite) const + { + if (finite) { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { + if (!std::isfinite(data[n])) return false; + } + } + return finite; + } + + static bool join(bool finite1, bool finite2) { return (finite1 && finite2); } + + const T* data; +}; + + +template +inline bool +Vector::isFinite() const +{ + // Parallelize over the elements of this vector. + bool finite = tbb::parallel_reduce(SizeRange(0, this->size()), /*seed=*/true, + IsFiniteOp(this->data()), IsFiniteOp::join); + return finite; +} + + +template +template +struct Vector::EqOp +{ + EqOp(const T* a_, const OtherValueType* b_, T e): a(a_), b(b_), eps(e) {} + + bool operator()(const SizeRange& range, bool equal) const + { + if (equal) { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { + if (!isApproxEqual(a[n], b[n], eps)) return false; + } + } + return equal; + } + + static bool join(bool eq1, bool eq2) { return (eq1 && eq2); } + + const T* a; + const OtherValueType* b; + const T eps; +}; + + +template +template +inline bool +Vector::eq(const Vector& other, ValueType eps) const +{ + if (this->size() != other.size()) return false; + bool equal = tbb::parallel_reduce(SizeRange(0, this->size()), /*seed=*/true, + EqOp(this->data(), other.data(), eps), EqOp::join); + return equal; +} + + +template +inline std::string +Vector::str() const +{ + std::ostringstream ostr; + ostr << "["; + std::string sep; + for (SizeType n = 0, N = this->size(); n < N; ++n) { + ostr << sep << (*this)[n]; + sep = ", "; + } + ostr << "]"; + return ostr.str(); +} + + +//////////////////////////////////////// + + +template +const ValueType SparseStencilMatrix::sZeroValue = zeroVal(); + + +template +inline +SparseStencilMatrix::SparseStencilMatrix(SizeType numRows) + : mNumRows(numRows) + , mValueArray(new ValueType[mNumRows * STENCIL_SIZE]) + , mColumnIdxArray(new SizeType[mNumRows * STENCIL_SIZE]) + , mRowSizeArray(new SizeType[mNumRows]) +{ + // Initialize the matrix to a null state by setting the size of each row to zero. + tbb::parallel_for(SizeRange(0, mNumRows), + internal::FillOp(mRowSizeArray.get(), /*value=*/0)); +} + + +template +struct SparseStencilMatrix::MatrixCopyOp +{ + MatrixCopyOp(const SparseStencilMatrix& from_, SparseStencilMatrix& to_): + from(&from_), to(&to_) {} + + void operator()(const SizeRange& range) const + { + const ValueType* fromVal = from->mValueArray.get(); + const SizeType* fromCol = from->mColumnIdxArray.get(); + ValueType* toVal = to->mValueArray.get(); + SizeType* toCol = to->mColumnIdxArray.get(); + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { + toVal[n] = fromVal[n]; + toCol[n] = fromCol[n]; + } + } + + const SparseStencilMatrix* from; SparseStencilMatrix* to; +}; + + +template +inline +SparseStencilMatrix::SparseStencilMatrix(const SparseStencilMatrix& other) + : mNumRows(other.mNumRows) + , mValueArray(new ValueType[mNumRows * STENCIL_SIZE]) + , mColumnIdxArray(new SizeType[mNumRows * STENCIL_SIZE]) + , mRowSizeArray(new SizeType[mNumRows]) +{ + SizeType size = mNumRows * STENCIL_SIZE; + + // Copy the value and column index arrays from the other matrix to this matrix. + tbb::parallel_for(SizeRange(0, size), MatrixCopyOp(/*from=*/other, /*to=*/*this)); + + // Copy the row size array from the other matrix to this matrix. + tbb::parallel_for(SizeRange(0, mNumRows), + internal::CopyOp(/*from=*/other.mRowSizeArray.get(), /*to=*/mRowSizeArray.get())); +} + + +template +inline void +SparseStencilMatrix::setValue(SizeType row, SizeType col, + const ValueType& val) +{ + assert(row < mNumRows); + this->getRowEditor(row).setValue(col, val); +} + + +template +inline const ValueType& +SparseStencilMatrix::getValue(SizeType row, SizeType col) const +{ + assert(row < mNumRows); + return this->getConstRow(row).getValue(col); +} + + +template +inline const ValueType& +SparseStencilMatrix::operator()(SizeType row, SizeType col) const +{ + return this->getValue(row,col); +} + + +template +template +struct SparseStencilMatrix::RowScaleOp +{ + RowScaleOp(SparseStencilMatrix& m, const Scalar& s_): mat(&m), s(s_) {} + + void operator()(const SizeRange& range) const + { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { + RowEditor row = mat->getRowEditor(n); + row.scale(s); + } + } + + SparseStencilMatrix* mat; + const Scalar s; +}; + + +template +template +inline void +SparseStencilMatrix::scale(const Scalar& s) +{ + // Parallelize over the rows in the matrix. + tbb::parallel_for(SizeRange(0, mNumRows), RowScaleOp(*this, s)); +} + + +template +template +struct SparseStencilMatrix::VecMultOp +{ + VecMultOp(const SparseStencilMatrix& m, const VecValueType* i, VecValueType* o): + mat(&m), in(i), out(o) {} + + void operator()(const SizeRange& range) const + { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { + ConstRow row = mat->getConstRow(n); + out[n] = row.dot(in, mat->numRows()); + } + } + + const SparseStencilMatrix* mat; + const VecValueType* in; + VecValueType* out; +}; + + +template +template +inline void +SparseStencilMatrix::vectorMultiply( + const Vector& inVec, Vector& resultVec) const +{ + if (inVec.size() != mNumRows) { + OPENVDB_THROW(ArithmeticError, "matrix and input vector have incompatible sizes (" + << mNumRows << "x" << mNumRows << " vs. " << inVec.size() << ")"); + } + if (resultVec.size() != mNumRows) { + OPENVDB_THROW(ArithmeticError, "matrix and result vector have incompatible sizes (" + << mNumRows << "x" << mNumRows << " vs. " << resultVec.size() << ")"); + } + + vectorMultiply(inVec.data(), resultVec.data()); +} + + +template +template +inline void +SparseStencilMatrix::vectorMultiply( + const VecValueType* inVec, VecValueType* resultVec) const +{ + // Parallelize over the rows in the matrix. + tbb::parallel_for(SizeRange(0, mNumRows), + VecMultOp(*this, inVec, resultVec)); +} + + +template +template +struct SparseStencilMatrix::EqOp +{ + EqOp(const SparseStencilMatrix& a_, + const SparseStencilMatrix& b_, ValueType e): + a(&a_), b(&b_), eps(e) {} + + bool operator()(const SizeRange& range, bool equal) const + { + if (equal) { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { + if (!a->getConstRow(n).eq(b->getConstRow(n), eps)) return false; + } + } + return equal; + } + + static bool join(bool eq1, bool eq2) { return (eq1 && eq2); } + + const SparseStencilMatrix* a; + const SparseStencilMatrix* b; + const ValueType eps; +}; + + +template +template +inline bool +SparseStencilMatrix::eq( + const SparseStencilMatrix& other, ValueType eps) const +{ + if (this->numRows() != other.numRows()) return false; + bool equal = tbb::parallel_reduce(SizeRange(0, this->numRows()), /*seed=*/true, + EqOp(*this, other, eps), EqOp::join); + return equal; +} + + +template +struct SparseStencilMatrix::IsFiniteOp +{ + IsFiniteOp(const SparseStencilMatrix& m): mat(&m) {} + + bool operator()(const SizeRange& range, bool finite) const + { + if (finite) { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { + const ConstRow row = mat->getConstRow(n); + for (ConstValueIter it = row.cbegin(); it; ++it) { + if (!std::isfinite(*it)) return false; + } + } + } + return finite; + } + + static bool join(bool finite1, bool finite2) { return (finite1 && finite2); } + + const SparseStencilMatrix* mat; +}; + + +template +inline bool +SparseStencilMatrix::isFinite() const +{ + // Parallelize over the rows of this matrix. + bool finite = tbb::parallel_reduce(SizeRange(0, this->numRows()), /*seed=*/true, + IsFiniteOp(*this), IsFiniteOp::join); + return finite; +} + + +template +inline std::string +SparseStencilMatrix::str() const +{ + std::ostringstream ostr; + for (SizeType n = 0, N = this->size(); n < N; ++n) { + ostr << n << ": " << this->getConstRow(n).str() << "\n"; + } + return ostr.str(); +} + + +template +inline typename SparseStencilMatrix::RowEditor +SparseStencilMatrix::getRowEditor(SizeType i) +{ + assert(i < mNumRows); + const SizeType head = i * STENCIL_SIZE; + return RowEditor(&mValueArray[head], &mColumnIdxArray[head], mRowSizeArray[i], mNumRows); +} + + +template +inline typename SparseStencilMatrix::ConstRow +SparseStencilMatrix::getConstRow(SizeType i) const +{ + assert(i < mNumRows); + const SizeType head = i * STENCIL_SIZE; // index for this row into main storage + return ConstRow(&mValueArray[head], &mColumnIdxArray[head], mRowSizeArray[i]); +} + + +template +template +inline SizeType +SparseStencilMatrix::RowBase::find(SizeType columnIdx) const +{ + if (this->empty()) return mData.mSize; + + // Get a pointer to the first column index that is equal to or greater than the given index. + // (This assumes that the data is sorted by column.) + const SizeType* colPtr = std::lower_bound(mData.mCols, mData.mCols + mData.mSize, columnIdx); + // Return the offset of the pointer from the beginning of the array. + return static_cast(colPtr - mData.mCols); +} + + +template +template +inline const ValueType& +SparseStencilMatrix::RowBase::getValue( + SizeType columnIdx, bool& active) const +{ + active = false; + SizeType idx = this->find(columnIdx); + if (idx < this->size() && this->column(idx) == columnIdx) { + active = true; + return this->value(idx); + } + return SparseStencilMatrix::sZeroValue; +} + +template +template +inline const ValueType& +SparseStencilMatrix::RowBase::getValue(SizeType columnIdx) const +{ + SizeType idx = this->find(columnIdx); + if (idx < this->size() && this->column(idx) == columnIdx) { + return this->value(idx); + } + return SparseStencilMatrix::sZeroValue; +} + + +template +template +inline typename SparseStencilMatrix::ConstValueIter +SparseStencilMatrix::RowBase::cbegin() const +{ + return ConstValueIter(mData); +} + + +template +template +template +inline bool +SparseStencilMatrix::RowBase::eq( + const RowBase& other, ValueType eps) const +{ + if (this->size() != other.size()) return false; + for (ConstValueIter it = cbegin(), oit = other.cbegin(); it || oit; ++it, ++oit) { + if (it.column() != oit.column()) return false; + if (!isApproxEqual(*it, *oit, eps)) return false; + } + return true; +} + + +template +template +template +inline VecValueType +SparseStencilMatrix::RowBase::dot( + const VecValueType* inVec, SizeType vecSize) const +{ + VecValueType result = zeroVal(); + for (SizeType idx = 0, N = std::min(vecSize, this->size()); idx < N; ++idx) { + result += static_cast(this->value(idx) * inVec[this->column(idx)]); + } + return result; +} + +template +template +template +inline VecValueType +SparseStencilMatrix::RowBase::dot( + const Vector& inVec) const +{ + return dot(inVec.data(), inVec.size()); +} + + +template +template +inline std::string +SparseStencilMatrix::RowBase::str() const +{ + std::ostringstream ostr; + std::string sep; + for (SizeType n = 0, N = this->size(); n < N; ++n) { + ostr << sep << "(" << this->column(n) << ", " << this->value(n) << ")"; + sep = ", "; + } + return ostr.str(); +} + + +template +inline +SparseStencilMatrix::ConstRow::ConstRow( + const ValueType* valueHead, const SizeType* columnHead, const SizeType& rowSize): + ConstRowBase(ConstRowData(const_cast(valueHead), + const_cast(columnHead), const_cast(rowSize))) +{ +} + + +template +inline +SparseStencilMatrix::RowEditor::RowEditor( + ValueType* valueHead, SizeType* columnHead, SizeType& rowSize, SizeType colSize): + RowBase<>(RowData(valueHead, columnHead, rowSize)), mNumColumns(colSize) +{ +} + + +template +inline void +SparseStencilMatrix::RowEditor::clear() +{ + // Note: since mSize is a reference, this modifies the underlying matrix. + RowBase<>::mData.mSize = 0; +} + + +template +inline SizeType +SparseStencilMatrix::RowEditor::setValue( + SizeType column, const ValueType& value) +{ + assert(column < mNumColumns); + + RowData& data = RowBase<>::mData; + + // Get the offset of the first column index that is equal to or greater than + // the column to be modified. + SizeType offset = this->find(column); + + if (offset < data.mSize && data.mCols[offset] == column) { + // If the column already exists, just update its value. + data.mVals[offset] = value; + return data.mSize; + } + + // Check that it is safe to add a new column. + assert(data.mSize < this->capacity()); + + if (offset >= data.mSize) { + // The new column's index is larger than any existing index. Append the new column. + data.mVals[data.mSize] = value; + data.mCols[data.mSize] = column; + } else { + // Insert the new column at the computed offset after shifting subsequent columns. + for (SizeType i = data.mSize; i > offset; --i) { + data.mVals[i] = data.mVals[i - 1]; + data.mCols[i] = data.mCols[i - 1]; + } + data.mVals[offset] = value; + data.mCols[offset] = column; + } + ++data.mSize; + + return data.mSize; +} + + +template +template +inline void +SparseStencilMatrix::RowEditor::scale(const Scalar& s) +{ + for (int idx = 0, N = this->size(); idx < N; ++idx) { + RowBase<>::mData.mVals[idx] *= s; + } +} + + +//////////////////////////////////////// + + +/// Diagonal preconditioner +template +class JacobiPreconditioner: public Preconditioner +{ +private: + struct InitOp; + struct ApplyOp; + +public: + typedef typename MatrixType::ValueType ValueType; + typedef Preconditioner BaseType; + typedef Vector VectorType; + typedef boost::shared_ptr Ptr; + + JacobiPreconditioner(const MatrixType& A): BaseType(A), mDiag(A.numRows()) + { + // Initialize vector mDiag with the values from the matrix diagonal. + tbb::parallel_for(SizeRange(0, A.numRows()), InitOp(A, mDiag.data())); + } + + virtual ~JacobiPreconditioner() {} + + virtual void apply(const Vector& r, Vector& z) + { + const SizeType size = mDiag.size(); + + assert(r.size() == z.size()); + assert(r.size() == size); + + tbb::parallel_for(SizeRange(0, size), ApplyOp(mDiag.data(), r.data(), z.data())); + } + + /// Return @c true if all values along the diagonal are finite. + bool isFinite() const { return mDiag.isFinite(); } + +private: + // Functor for use with tbb::parallel_for() + struct InitOp + { + InitOp(const MatrixType& m, ValueType* v): mat(&m), vec(v) {} + void operator()(const SizeRange& range) const { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { + const ValueType val = mat->getValue(n, n); + assert(!isApproxZero(val, ValueType(0.0001))); + vec[n] = static_cast(1.0 / val); + } + } + const MatrixType* mat; ValueType* vec; + }; + + // Functor for use with tbb::parallel_reduce() + struct ApplyOp + { + ApplyOp(const ValueType* x_, const ValueType* y_, ValueType* out_): + x(x_), y(y_), out(out_) {} + void operator()(const SizeRange& range) const { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) out[n] = x[n] * y[n]; + } + const ValueType *x, *y; ValueType* out; + }; + + // The Jacobi preconditioner is a diagonal matrix + VectorType mDiag; +}; // class JacobiPreconditioner + + +/// Preconditioner using incomplete Cholesky factorization +template +class IncompleteCholeskyPreconditioner: public Preconditioner +{ +private: + struct CopyToLowerOp; + struct TransposeOp; + +public: + typedef typename MatrixType::ValueType ValueType; + typedef Preconditioner BaseType; + typedef Vector VectorType; + typedef boost::shared_ptr Ptr; + typedef SparseStencilMatrix TriangularMatrix; + typedef typename TriangularMatrix::ConstRow TriangleConstRow; + typedef typename TriangularMatrix::RowEditor TriangleRowEditor; + + IncompleteCholeskyPreconditioner(const MatrixType& matrix) + : BaseType(matrix) + , mLowerTriangular(matrix.numRows()) + , mUpperTriangular(matrix.numRows()) + , mTempVec(matrix.numRows()) + { + // Size of matrix + const SizeType numRows = mLowerTriangular.numRows(); + + // Copy the upper triangular part to the lower triangular part. + tbb::parallel_for(SizeRange(0, numRows), CopyToLowerOp(matrix, mLowerTriangular)); + + // Build the Incomplete Cholesky Matrix + // + // Algorithm: + // + // for (k = 0; k < size; ++k) { + // A(k,k) = sqrt(A(k,k)); + // for (i = k +1, i < size; ++i) { + // if (A(i,k) == 0) continue; + // A(i,k) = A(i,k) / A(k,k); + // } + // for (j = k+1; j < size; ++j) { + // for (i = j; i < size; ++i) { + // if (A(i,j) == 0) continue; + // A(i,j) -= A(i,k)*A(j,k); + // } + // } + // } + + mPassedCompatibilityCondition = true; + + for (SizeType k = 0; k < numRows; ++k) { + + TriangleConstRow crow_k = mLowerTriangular.getConstRow(k); + ValueType diagonalValue = crow_k.getValue(k); + + // Test if the matrix build has failed. + if (diagonalValue < 1.e-5) { + mPassedCompatibilityCondition = false; + break; + } + + diagonalValue = Sqrt(diagonalValue); + + TriangleRowEditor row_k = mLowerTriangular.getRowEditor(k); + row_k.setValue(k, diagonalValue); + + // Exploit the fact that the matrix is symmetric. + typename MatrixType::ConstRow srcRow = matrix.getConstRow(k); + typename MatrixType::ConstValueIter citer = srcRow.cbegin(); + for ( ; citer; ++citer) { + SizeType ii = citer.column(); + if (ii < k+1) continue; // look above diagonal + + TriangleRowEditor row_ii = mLowerTriangular.getRowEditor(ii); + + row_ii.setValue(k, *citer / diagonalValue); + } + + // for (j = k+1; j < size; ++j) replaced by row iter below + citer.reset(); // k,j entries + for ( ; citer; ++citer) { + SizeType j = citer.column(); + if (j < k+1) continue; + + TriangleConstRow row_j = mLowerTriangular.getConstRow(j); + ValueType a_jk = row_j.getValue(k); // a_jk is non zero if a_kj is non zero + + // Entry (i,j) is non-zero if matrix(j,i) is nonzero + + typename MatrixType::ConstRow mask = matrix.getConstRow(j); + typename MatrixType::ConstValueIter maskIter = mask.cbegin(); + for ( ; maskIter; ++maskIter) { + SizeType i = maskIter.column(); + if (i < j) continue; + + TriangleConstRow crow_i = mLowerTriangular.getConstRow(i); + ValueType a_ij = crow_i.getValue(j); + ValueType a_ik = crow_i.getValue(k); + TriangleRowEditor row_i = mLowerTriangular.getRowEditor(i); + a_ij -= a_ik * a_jk; + + row_i.setValue(j, a_ij); + } + } + } + + // Build the transpose of the IC matrix: mUpperTriangular + tbb::parallel_for(SizeRange(0, numRows), + TransposeOp(matrix, mLowerTriangular, mUpperTriangular)); + } + + virtual ~IncompleteCholeskyPreconditioner() {} + + virtual bool isValid() const { return mPassedCompatibilityCondition; } + + virtual void apply(const Vector& rVec, Vector& zVec) + { + if (!mPassedCompatibilityCondition) { + OPENVDB_THROW(ArithmeticError, "invalid Cholesky decomposition"); + } + + // Solve mUpperTriangular * mLowerTriangular * rVec = zVec; + + SizeType size = mLowerTriangular.numRows(); + + zVec.fill(zeroVal()); + ValueType* zData = zVec.data(); + + if (size == 0) return; + + assert(rVec.size() == size); + assert(zVec.size() == size); + + // Allocate a temp vector + mTempVec.fill(zeroVal()); + ValueType* tmpData = mTempVec.data(); + const ValueType* rData = rVec.data(); + + // Solve mLowerTriangular * tmp = rVec; + for (SizeType i = 0; i < size; ++i) { + typename TriangularMatrix::ConstRow row = mLowerTriangular.getConstRow(i); + ValueType diagonal = row.getValue(i); + ValueType dot = row.dot(mTempVec); + tmpData[i] = (rData[i] - dot) / diagonal; + if (!std::isfinite(tmpData[i])) { + OPENVDB_LOG_DEBUG_RUNTIME("1 diagonal was " << diagonal); + OPENVDB_LOG_DEBUG_RUNTIME("1a diagonal " << row.getValue(i)); + } + } + + // Solve mUpperTriangular * zVec = tmp; + for (SizeType ii = 0; ii < size; ++ii) { + SizeType i = size - 1 - ii; + typename TriangularMatrix::ConstRow row = mUpperTriangular.getConstRow(i); + ValueType diagonal = row.getValue(i); + ValueType dot = row.dot(zVec); + zData[i] = (tmpData[i] - dot) / diagonal; + if (!std::isfinite(zData[i])) { + OPENVDB_LOG_DEBUG_RUNTIME("2 diagonal was " << diagonal); + } + } + } + + const TriangularMatrix& lowerMatrix() const { return mLowerTriangular; } + const TriangularMatrix& upperMatrix() const { return mUpperTriangular; } + +private: + // Functor for use with tbb::parallel_for() + struct CopyToLowerOp + { + CopyToLowerOp(const MatrixType& m, TriangularMatrix& l): mat(&m), lower(&l) {} + void operator()(const SizeRange& range) const { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { + typename TriangularMatrix::RowEditor outRow = lower->getRowEditor(n); + outRow.clear(); + typename MatrixType::ConstRow inRow = mat->getConstRow(n); + for (typename MatrixType::ConstValueIter it = inRow.cbegin(); it; ++it) { + if (it.column() > n) continue; // skip above diagonal + outRow.setValue(it.column(), *it); + } + } + } + const MatrixType* mat; TriangularMatrix* lower; + }; + + // Functor for use with tbb::parallel_for() + struct TransposeOp + { + TransposeOp(const MatrixType& m, const TriangularMatrix& l, TriangularMatrix& u): + mat(&m), lower(&l), upper(&u) {} + void operator()(const SizeRange& range) const { + for (SizeType n = range.begin(), N = range.end(); n < N; ++n) { + typename TriangularMatrix::RowEditor outRow = upper->getRowEditor(n); + outRow.clear(); + // Use the fact that matrix is symmetric. + typename MatrixType::ConstRow inRow = mat->getConstRow(n); + for (typename MatrixType::ConstValueIter it = inRow.cbegin(); it; ++it) { + const SizeType column = it.column(); + if (column < n) continue; // only set upper triangle + outRow.setValue(column, lower->getValue(column, n)); + } + } + } + const MatrixType* mat; const TriangularMatrix* lower; TriangularMatrix* upper; + }; + + TriangularMatrix mLowerTriangular; + TriangularMatrix mUpperTriangular; + Vector mTempVec; + bool mPassedCompatibilityCondition; +}; // class IncompleteCholeskyPreconditioner + + +//////////////////////////////////////// + + +namespace internal { + +/// Compute @e ax + @e y. +template +inline void +axpy(const T& a, const T* xVec, const T* yVec, T* resultVec, SizeType size) +{ + tbb::parallel_for(SizeRange(0, size), LinearOp(a, xVec, yVec, resultVec)); +} + +/// Compute @e ax + @e y. +template +inline void +axpy(const T& a, const Vector& xVec, const Vector& yVec, Vector& result) +{ + assert(xVec.size() == yVec.size()); + assert(xVec.size() == result.size()); + axpy(a, xVec.data(), yVec.data(), result.data(), xVec.size()); +} + + +/// Compute @e r = @e b − @e Ax. +template +inline void +computeResidual(const MatrixOperator& A, const VecValueType* x, + const VecValueType* b, VecValueType* r) +{ + // Compute r = A * x. + A.vectorMultiply(x, r); + // Compute r = b - r. + tbb::parallel_for(SizeRange(0, A.numRows()), LinearOp(-1.0, r, b, r)); +} + +/// Compute @e r = @e b − @e Ax. +template +inline void +computeResidual(const MatrixOperator& A, const Vector& x, const Vector& b, Vector& r) +{ + assert(x.size() == b.size()); + assert(x.size() == r.size()); + assert(x.size() == A.numRows()); + + computeResidual(A, x.data(), b.data(), r.data()); +} + +} // namespace internal + + +//////////////////////////////////////// + + +template +inline State +solve( + const PositiveDefMatrix& Amat, + const Vector& bVec, + Vector& xVec, + Preconditioner& precond, + const State& termination) +{ + util::NullInterrupter interrupter; + return solve(Amat, bVec, xVec, precond, interrupter, termination); +} + + +template +inline State +solve( + const PositiveDefMatrix& Amat, + const Vector& bVec, + Vector& xVec, + Preconditioner& precond, + Interrupter& interrupter, + const State& termination) +{ + typedef typename PositiveDefMatrix::ValueType ValueType; + typedef Vector VectorType; + + State result; + result.success = false; + result.iterations = 0; + result.relativeError = 0.0; + result.absoluteError = 0.0; + + const SizeType size = Amat.numRows(); + if (size == 0) { + OPENVDB_LOG_WARN("pcg::solve(): matrix has dimension zero"); + return result; + } + if (size != bVec.size()) { + OPENVDB_THROW(ArithmeticError, "A and b have incompatible sizes" + << size << "x" << size << " vs. " << bVec.size() << ")"); + } + if (size != xVec.size()) { + OPENVDB_THROW(ArithmeticError, "A and x have incompatible sizes" + << size << "x" << size << " vs. " << xVec.size() << ")"); + } + + // Temp vectors + VectorType zVec(size); // transformed residual (M^-1 r) + VectorType pVec(size); // search direction + VectorType qVec(size); // A * p + + // Compute norm of B (the source) + const ValueType tmp = bVec.infNorm(); + const ValueType infNormOfB = isZero(tmp) ? 1.f : tmp; + + // Compute rVec: residual = b - Ax. + VectorType rVec(size); // vector of residuals + + internal::computeResidual(Amat, xVec, bVec, rVec); + + assert(rVec.isFinite()); + + // Normalize the residual norm with the source norm and look for early out. + result.absoluteError = static_cast(rVec.infNorm()); + result.relativeError = static_cast(result.absoluteError / infNormOfB); + if (result.relativeError <= termination.relativeError) { + result.success = true; + return result; + } + + // Iterations of the CG solve + + ValueType rDotZPrev(1); // inner product of + + // Keep track of the minimum error to monitor convergence. + ValueType minL2Error = std::numeric_limits::max(); + ValueType l2Error; + + int iteration = 0; + for ( ; iteration < termination.iterations; ++iteration) { + + if (interrupter.wasInterrupted()) { + OPENVDB_THROW(RuntimeError, "conjugate gradient solver was interrupted"); + } + + OPENVDB_LOG_DEBUG_RUNTIME("pcg::solve() " << result); + + result.iterations = iteration + 1; + + // Apply preconditioner to residual + // z_{k} = M^-1 r_{k} + precond.apply(rVec, zVec); + + // + const ValueType rDotZ = rVec.dot(zVec); + assert(std::isfinite(rDotZ)); + + if (0 == iteration) { + // Initialize + pVec = zVec; + } else { + const ValueType beta = rDotZ / rDotZPrev; + // p = beta * p + z + internal::axpy(beta, pVec, zVec, /*result */pVec); + } + + // q_{k} = A p_{k} + Amat.vectorMultiply(pVec, qVec); + + // alpha = / + const ValueType pAp = pVec.dot(qVec); + assert(std::isfinite(pAp)); + + const ValueType alpha = rDotZ / pAp; + rDotZPrev = rDotZ; + + // x_{k} = x_{k-1} + alpha * p_{k} + internal::axpy(alpha, pVec, xVec, /*result=*/xVec); + + // r_{k} = r_{k-1} - alpha_{k-1} A p_{k} + internal::axpy(-alpha, qVec, rVec, /*result=*/rVec); + + // update tolerances + l2Error = rVec.l2Norm(); + minL2Error = Min(l2Error, minL2Error); + + result.absoluteError = static_cast(rVec.infNorm()); + result.relativeError = static_cast(result.absoluteError / infNormOfB); + + if (l2Error > 2 * minL2Error) { + // The solution started to diverge. + result.success = false; + break; + } + if (!std::isfinite(result.absoluteError)) { + // Total divergence of solution + result.success = false; + break; + } + if (result.absoluteError <= termination.absoluteError) { + // Convergence + result.success = true; + break; + } + if (result.relativeError <= termination.relativeError) { + // Convergence + result.success = true; + break; + } + } + OPENVDB_LOG_DEBUG_RUNTIME("pcg::solve() " << result); + + return result; +} + +} // namespace pcg +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_MATH_CONJGRADIENT_HAS_BEEN_INCLUDED + +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/math/Coord.h b/nuparu/include/openvdb/include/openvdb/math/Coord.h new file mode 100644 index 00000000..01a4893c --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/math/Coord.h @@ -0,0 +1,528 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 + +#include +#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) 32-bit integer coordinates +class Coord +{ +public: + typedef int32_t Int32; + typedef uint32_t Index32; + typedef Vec3 Vec3i; + typedef Vec3 Vec3I; + + typedef Int32 ValueType; + typedef std::numeric_limits 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; } + 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]; } + + /// @brief Return the smallest possible coordinate + static Coord min() { return Coord(Limits::min()); } + + /// @brief Return the largest possible coordinate + static Coord max() { return Coord(Limits::max()); } + + /// @brief Return @a xyz rounded to the closest integer coordinates + /// (cell centered conversion). + template static Coord round(const Vec3& xyz) + { + return Coord(Int32(Round(xyz[0])), Int32(Round(xyz[1])), Int32(Round(xyz[2]))); + } + /// @brief Return the largest integer coordinates that are not greater + /// than @a xyz (node centered conversion). + template static Coord floor(const Vec3& xyz) + { + return Coord(Int32(Floor(xyz[0])), Int32(Floor(xyz[1])), Int32(Floor(xyz[2]))); + } + + /// @brief Return the largest integer coordinates that are not greater + /// than @a xyz+1 (node centered conversion). + template static Coord ceil(const Vec3& xyz) + { + return Coord(Int32(Ceil(xyz[0])), Int32(Ceil(xyz[1])), Int32(Ceil(xyz[2]))); + } + + Coord& reset(Int32 x, Int32 y, Int32 z) + { + mVec[0] = x; mVec[1] = y; mVec[2] = z; + return *this; + } + Coord& reset(Int32 xyz) { return this->reset(xyz, xyz, xyz); } + + Coord& setX(Int32 x) { mVec[0] = x; return *this; } + Coord& setY(Int32 y) { mVec[1] = y; return *this; } + Coord& setZ(Int32 z) { mVec[2] = z; return *this; } + + Coord& offset(Int32 dx, Int32 dy, Int32 dz) + { + mVec[0]+=dx; mVec[1]+=dy; mVec[2]+=dz; + 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]<>=(size_t n) { mVec[0]>>=n; mVec[1]>>=n; mVec[2]>>=n; 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; return *this; } + Coord& operator|= (Int32 n) { mVec[0]|=n; mVec[1]|=n; mVec[2]|=n; 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() { return mVec[0]; } + Int32& y() { return mVec[1]; } + Int32& z() { return mVec[2]; } + Int32& operator[](size_t i) { assert(i < 3); return mVec[i]; } + + const Int32* data() const { return mVec; } + Int32* data() { return mVec; } + const Int32* asPointer() const { return mVec; } + Int32* asPointer() { 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); } + + /// 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())); + } + + /// Return true if any of the components of @a a are smaller than the + /// corresponding components of @a b. + static inline bool lessThan(const Coord& a, const Coord& b) + { + return (a[0] < b[0] || a[1] < b[1] || a[2] < b[2]); + } + + /// @brief Return the index (0, 1 or 2) with the smallest value. + size_t minIndex() const { return MinIndex(mVec); } + + /// @brief Return the index (0, 1 or 2) with the largest value. + size_t maxIndex() const { return MaxIndex(mVec); } + + void read(std::istream& is) { is.read(reinterpret_cast(mVec), sizeof(mVec)); } + void write(std::ostream& os) const + { + os.write(reinterpret_cast(mVec), sizeof(mVec)); + } + +private: + + Int32 mVec[3]; +}; // 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 Iterator over Coord domain covered by a CoordBBox + /// + /// @note If ZYX is true Z is the fastest moving coordinate, else + /// it is the X coordinate, i.e. XYZ traversal + template + class Iterator { + public: + /// @brief C-tor from a bounding box + Iterator(const CoordBBox &b) : mPos(b.min()), mMin(b.min()), mMax(b.max()) {} + /// @brief Increments iterator to point to the next coordinate + /// @note Stops a the last + 1 coordinate of the bounding box + /// as defined by the template parameter. + Iterator& operator++() { + ZYX ? this->next<2,1,0>() : this->next<0,1,2>(); + return *this; + } + /// @brief Return true if the iterator still points to a valid coordinate + operator bool() const { + return ZYX ? mPos[0] <= mMax[0] : mPos[2] <= mMax[2]; + } + /// @brief Return a const reference to the coordinate currently pointed to + const Coord& operator*() const { return mPos; } + private: + template + inline void next() { + if ( mPos[a] < mMax[a] ) {//by far this is the most common case + ++mPos[a]; + } else if ( mPos[b] < mMax[b] ) { + mPos[a] = mMin[a]; + ++mPos[b]; + } else if ( mPos[c] <= mMax[c] ) { + mPos[a] = mMin[a]; + mPos[b] = mMin[b]; + ++mPos[c]; + } + } + Coord mPos, mMin, mMax; + };// CoordBBox::Iterator + + /// @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() { mMin = Coord::max(); mMax = Coord::min(); } + 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]dim().minIndex(); } + + /// @brief Return the index (0, 1 or 2) of the longest axis. + size_t maxExtent() const { return this->dim().maxIndex(); } + + /// 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); + } + + /// Return a new instance that is expanded by the specified padding. + CoordBBox expandBy(ValueType padding) const + { + return CoordBBox(mMin.offsetBy(-padding),mMax.offsetBy(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; } + + //@{ + /// @brief Bit-wise operations performed on both the min and max members + CoordBBox operator>> (size_t n) const { return CoordBBox(mMin>>n, mMax>>n); } + CoordBBox operator<< (size_t n) const { return CoordBBox(mMin<>=(size_t n) { mMin >>= n; mMax >>= n; return *this; } + CoordBBox operator& (Coord::Int32 n) const { return CoordBBox(mMin & n, mMax & n); } + CoordBBox operator| (Coord::Int32 n) const { return CoordBBox(mMin | n, mMax | n); } + CoordBBox& operator&= (Coord::Int32 n) { mMin &= n; mMax &= n; return *this; } + CoordBBox& operator|= (Coord::Int32 n) { mMin |= n; mMax |= n; return *this; } + //@} + + /// 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 +inline Vec3::type> +operator+(const Vec3& v0, const Coord& v1) +{ + Vec3::type> result(v0); + result[0] += v1[0]; + result[1] += v1[1]; + result[2] += v1[2]; + return result; +} + +template +inline Vec3::type> +operator+(const Coord& v1, const Vec3& v0) +{ + Vec3::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 +inline Vec3::type> +operator-(const Vec3& v0, const Coord& v1) +{ + Vec3::type> result(v0); + result[0] -= v1[0]; + result[1] -= v1[1]; + result[2] -= v1[2]; + return result; +} + +template +inline Vec3::type> +operator-(const Coord& v1, const Vec3& v0) +{ + Vec3::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-2016 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/nuparu/include/openvdb/include/openvdb/math/DDA.h b/nuparu/include/openvdb/include/openvdb/math/DDA.h new file mode 100644 index 00000000..c4242b14 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/math/DDA.h @@ -0,0 +1,373 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 DDA.h +/// +/// @author Ken Museth +/// +/// @brief Digital Differential Analyzers specialized for VDB. + +#ifndef OPENVDB_MATH_DDA_HAS_BEEN_INCLUDED +#define OPENVDB_MATH_DDA_HAS_BEEN_INCLUDED + +#include "Coord.h" +#include "Math.h" +#include "Vec3.h" +#include // for std::ostream +#include // for std::numeric_limits::max() + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + +/// @brief A Digital Differential Analyzer specialized for OpenVDB grids +/// @note Conceptually similar to Bresenham's line algorithm applied +/// to a 3D Ray intersecting OpenVDB nodes or voxels. Log2Dim = 0 +/// corresponds to a voxel and Log2Dim a tree node of size 2^Log2Dim. +/// +/// @note The Ray template class is expected to have the following +/// methods: test(time), t0(), t1(), invDir(), and operator()(time). +/// See the example Ray class above for their definition. +template +class DDA +{ +public: + typedef typename RayT::RealType RealType; + typedef RealType RealT; + typedef typename RayT::Vec3Type Vec3Type; + typedef Vec3Type Vec3T; + + /// @brief uninitialized constructor + DDA() {} + + DDA(const RayT& ray) { this->init(ray); } + + DDA(const RayT& ray, RealT startTime) { this->init(ray, startTime); } + + DDA(const RayT& ray, RealT startTime, RealT maxTime) { this->init(ray, startTime, maxTime); } + + inline void init(const RayT& ray, RealT startTime, RealT maxTime) + { + assert(startTime <= maxTime); + static const int DIM = 1 << Log2Dim; + mT0 = startTime; + mT1 = maxTime; + const Vec3T &pos = ray(mT0), &dir = ray.dir(), &inv = ray.invDir(); + mVoxel = Coord::floor(pos) & (~(DIM-1)); + for (int axis = 0; axis < 3; ++axis) { + if (math::isZero(dir[axis])) {//handles dir = +/- 0 + mStep[axis] = 0;//dummy value + mNext[axis] = std::numeric_limits::max();//i.e. disabled! + mDelta[axis] = std::numeric_limits::max();//dummy value + } else if (inv[axis] > 0) { + mStep[axis] = DIM; + mNext[axis] = mT0 + (mVoxel[axis] + DIM - pos[axis]) * inv[axis]; + mDelta[axis] = mStep[axis] * inv[axis]; + } else { + mStep[axis] = -DIM; + mNext[axis] = mT0 + (mVoxel[axis] - pos[axis]) * inv[axis]; + mDelta[axis] = mStep[axis] * inv[axis]; + } + } + } + + inline void init(const RayT& ray) { this->init(ray, ray.t0(), ray.t1()); } + + inline void init(const RayT& ray, RealT startTime) { this->init(ray, startTime, ray.t1()); } + + /// @brief Increment the voxel index to next intersected voxel or node + /// and returns true if the step in time does not exceed maxTime. + inline bool step() + { + const int stepAxis = static_cast(math::MinIndex(mNext)); + mT0 = mNext[stepAxis]; + mNext[stepAxis] += mDelta[stepAxis]; + mVoxel[stepAxis] += mStep[stepAxis]; + return mT0 <= mT1; + } + + /// @brief Return the index coordinates of the next node or voxel + /// intersected by the ray. If Log2Dim = 0 the return value is the + /// actual signed coordinate of the voxel, else it is the origin + /// of the corresponding VDB tree node or tile. + /// @note Incurs no computational overhead. + inline const Coord& voxel() const { return mVoxel; } + + /// @brief Return the time (parameterized along the Ray) of the + /// first hit of a tree node of size 2^Log2Dim. + /// @details This value is initialized to startTime or ray.t0() + /// depending on the constructor used. + /// @note Incurs no computational overhead. + inline RealType time() const { return mT0; } + + /// @brief Return the maximum time (parameterized along the Ray). + inline RealType maxTime() const { return mT1; } + + /// @brief Return the time (parameterized along the Ray) of the + /// second (i.e. next) hit of a tree node of size 2^Log2Dim. + /// @note Incurs a (small) computational overhead. + inline RealType next() const { return math::Min(mT1, mNext[0], mNext[1], mNext[2]); } + + /// @brief Print information about this DDA for debugging. + /// @param os a stream to which to write textual information. + void print(std::ostream& os = std::cout) const + { + os << "Dim=" << (1< +#include "Math.h" +#include "Coord.h" +#include "Vec3.h" + +#include +#include + +#ifdef DWA_OPENVDB +#include +#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 integration 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 Implementation of nominally fifth-order finite-difference WENO +/// @details 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) and v5 = f(x+2dx), +/// return an 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 fifth-order in smooth regions: O(dx) <= error <=O(dx^5) +template +inline ValueType +WENO5(const ValueType& v1, const ValueType& v2, const ValueType& v3, + const ValueType& v4, const ValueType& v5, float scale2 = 0.01f) +{ + 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 static_cast(static_cast( + 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 +inline Real GodunovsNormSqrd(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 +OPENVDB_DEPRECATED inline Real GudonovsNormSqrd(bool isOutside, + Real dP_xm, Real dP_xp, + Real dP_ym, Real dP_yp, + Real dP_zm, Real dP_zp) +{ return GodunovsNormSqrd(isOutside, dP_xm, dP_xp, dP_ym, dP_yp, dP_zm, dP_zp); } + +template +inline Real +GodunovsNormSqrd(bool isOutside, const Vec3& gradient_m, const Vec3& gradient_p) +{ + return GodunovsNormSqrd(isOutside, + gradient_m[0], gradient_p[0], + gradient_m[1], gradient_p[1], + gradient_m[2], gradient_p[2]); +} + +template +OPENVDB_DEPRECATED inline Real GudonovsNormSqrd(bool isOutside, + const Vec3& gradient_m, + const Vec3& gradient_p) +{ + return GodunovsNormSqrd(isOutside, gradient_m, gradient_p); +} + + +#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(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.f / 12.f), + eps(1.0e-6f * scale2), + two(2.0), three(3.0), four(4.0), five(5.0), fourth(0.25), + A1 = F4(0.1f) / Pow2(C*Pow2(v1-two*v2+v3) + fourth*Pow2(v1-four*v2+three*v3) + eps), + A2 = F4(0.6f) / Pow2(C*Pow2(v2-two*v3+v4) + fourth*Pow2(v2-v4) + eps), + A3 = F4(0.3f) / 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 +GodunovsNormSqrd(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] +} + +OPENVDB_DEPRECATED inline float GudonovsNormSqrd(bool isOutside, + const simd::Float4& dP_m, + const simd::Float4& dP_p) +{ + return GodunovsNormSqrd(isOutside, dP_m, dP_p); +} +#endif + +template +struct D1 +{ + // random access version + template + static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk); + + template + static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk); + + template + static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk); + + // stencil access version + template + static typename Stencil::ValueType inX(const Stencil& S); + + template + static typename Stencil::ValueType inY(const Stencil& S); + + template + static typename Stencil::ValueType inZ(const Stencil& S); +}; + +template<> +struct D1 +{ + // the difference opperator + template + static ValueType difference(const ValueType& xp1, const ValueType& xm1) { + return xp1 - xm1; + } + + // random access version + template + 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 + 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 + 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 + static typename Stencil::ValueType inX(const Stencil& S) + { + return difference( S.template getValue< 1, 0, 0>(), S.template getValue<-1, 0, 0>()); + } + + template + static typename Stencil::ValueType inY(const Stencil& S) + { + return difference( S.template getValue< 0, 1, 0>(), S.template getValue< 0,-1, 0>()); + } + + template + 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 +{ + + // the difference opperator + template + static ValueType difference(const ValueType& xp1, const ValueType& xm1) { + return (xp1 - xm1)*ValueType(0.5); + } + + + // random access + template + 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 + 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 + 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 + static typename Stencil::ValueType inX(const Stencil& S) + { + return difference(S.template getValue< 1, 0, 0>(), S.template getValue<-1, 0, 0>()); + } + template + static typename Stencil::ValueType inY(const Stencil& S) + { + return difference(S.template getValue< 0, 1, 0>(), S.template getValue< 0,-1, 0>()); + } + + template + 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 +{ + + // the difference opperator + template + 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 + 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 + 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 + 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 + 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 + 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 + 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 +{ + + // the difference opperator + template + 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 + 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 + 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 + 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 + 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 + 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 + 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 +{ + + // the difference opperator + template + static ValueType difference(const ValueType& xp1, const ValueType& xp0) { + return xp1 - xp0; + } + + + // random access version + template + static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) + { + return difference(grid.getValue(ijk.offsetBy(1, 0, 0)), grid.getValue(ijk)); + } + + template + static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk) + { + return difference(grid.getValue(ijk.offsetBy(0, 1, 0)), grid.getValue(ijk)); + } + + template + 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 + static typename Stencil::ValueType inX(const Stencil& S) + { + return difference(S.template getValue< 1, 0, 0>(), S.template getValue< 0, 0, 0>()); + } + + template + static typename Stencil::ValueType inY(const Stencil& S) + { + return difference(S.template getValue< 0, 1, 0>(), S.template getValue< 0, 0, 0>()); + } + + template + 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 +{ + // the difference opperator + template + 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 + 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 + 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 + 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 + 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 + 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 + 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 +{ + + // the difference opperator + template + static ValueType difference(const ValueType& xp3, const ValueType& xp2, + const ValueType& xp1, const ValueType& xp0) + { + return static_cast(xp3/3.0 - 1.5*xp2 + 3.0*xp1 - 11.0*xp0/6.0); + } + + + // random access version + template + 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 + 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 + 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 + 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 + 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 + 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 +{ + + // the difference opperator + template + static ValueType difference(const ValueType& xm1, const ValueType& xm0) { + return -D1::difference(xm1, xm0); + } + + + // random access version + template + static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk) + { + return difference(grid.getValue(ijk.offsetBy(-1,0,0)), grid.getValue(ijk)); + } + + template + static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk) + { + return difference(grid.getValue(ijk.offsetBy(0,-1,0)), grid.getValue(ijk)); + } + + template + 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 + static typename Stencil::ValueType inX(const Stencil& S) + { + return difference(S.template getValue<-1, 0, 0>(), S.template getValue< 0, 0, 0>()); + } + + template + static typename Stencil::ValueType inY(const Stencil& S) + { + return difference(S.template getValue< 0,-1, 0>(), S.template getValue< 0, 0, 0>()); + } + + template + 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 +{ + + // the difference opperator + template + static ValueType difference(const ValueType& xm2, const ValueType& xm1, const ValueType& xm0) + { + return -D1::difference(xm2, xm1, xm0); + } + + + // random access version + template + 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 + 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 + 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 + 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 + 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 + 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 +{ + + // the difference opperator + template + static ValueType difference(const ValueType& xm3, const ValueType& xm2, + const ValueType& xm1, const ValueType& xm0) + { + return -D1::difference(xm3, xm2, xm1, xm0); + } + + // random access version + template + 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 + 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 + 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 + 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 + 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 + 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 +{ + // the difference operator + template + static ValueType difference(const ValueType& xp3, const ValueType& xp2, + const ValueType& xp1, const ValueType& xp0, + const ValueType& xm1, const ValueType& xm2) { + return WENO5(xp3, xp2, xp1, xp0, xm1) + - WENO5(xp2, xp1, xp0, xm1, xm2); + } + + + // random access version + template + 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 + 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 + 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 + static typename Stencil::ValueType inX(const Stencil& S) + { + + return static_cast(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 + static typename Stencil::ValueType inY(const Stencil& S) + { + return static_cast(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 + static typename Stencil::ValueType inZ(const Stencil& S) + { + return static_cast(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 +{ + + // the difference opperator + template + static ValueType difference(const ValueType& xp3, const ValueType& xp2, + const ValueType& xp1, const ValueType& xp0, + const ValueType& xm1, const ValueType& xm2) { + return WENO5(xp3 - xp2, xp2 - xp1, xp1 - xp0, xp0-xm1, xm1-xm2); + } + + // random access version + template + 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 + 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 + 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 + 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 + 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 + 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 +{ + + template + static ValueType difference(const ValueType& xm3, const ValueType& xm2, const ValueType& xm1, + const ValueType& xm0, const ValueType& xp1, const ValueType& xp2) + { + return -D1::difference(xm3, xm2, xm1, xm0, xp1, xp2); + } + + + // random access version + template + 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 + 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 + 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 + 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 + 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 + 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 +{ + template + static ValueType difference(const ValueType& xm3, const ValueType& xm2, const ValueType& xm1, + const ValueType& xm0, const ValueType& xp1, const ValueType& xp2) + { + return -D1::difference(xm3, xm2, xm1, xm0, xp1, xp2); + } + + // random access version + template + 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 + 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 + 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 + 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 + 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 + 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 D1Vec +{ + // random access version + template + static typename Accessor::ValueType::value_type + inX(const Accessor& grid, const Coord& ijk, int n) + { + return D1::inX(grid, ijk)[n]; + } + + template + static typename Accessor::ValueType::value_type + inY(const Accessor& grid, const Coord& ijk, int n) + { + return D1::inY(grid, ijk)[n]; + } + template + static typename Accessor::ValueType::value_type + inZ(const Accessor& grid, const Coord& ijk, int n) + { + return D1::inZ(grid, ijk)[n]; + } + + + // stencil access version + template + static typename Stencil::ValueType::value_type inX(const Stencil& S, int n) + { + return D1::inX(S)[n]; + } + + template + static typename Stencil::ValueType::value_type inY(const Stencil& S, int n) + { + return D1::inY(S)[n]; + } + + template + static typename Stencil::ValueType::value_type inZ(const Stencil& S, int n) + { + return D1::inZ(S)[n]; + } +}; + + +template<> +struct D1Vec +{ + + // random access version + template + static typename Accessor::ValueType::value_type + inX(const Accessor& grid, const Coord& ijk, int n) + { + return D1::difference( grid.getValue(ijk.offsetBy( 1, 0, 0))[n], + grid.getValue(ijk.offsetBy(-1, 0, 0))[n] ); + } + + template + static typename Accessor::ValueType::value_type + inY(const Accessor& grid, const Coord& ijk, int n) + { + return D1::difference( grid.getValue(ijk.offsetBy(0, 1, 0))[n], + grid.getValue(ijk.offsetBy(0,-1, 0))[n] ); + } + + template + static typename Accessor::ValueType::value_type + inZ(const Accessor& grid, const Coord& ijk, int n) + { + return D1::difference( grid.getValue(ijk.offsetBy(0, 0, 1))[n], + grid.getValue(ijk.offsetBy(0, 0,-1))[n] ); + } + + // stencil access version + template + static typename Stencil::ValueType::value_type inX(const Stencil& S, int n) + { + return D1::difference( S.template getValue< 1, 0, 0>()[n], + S.template getValue<-1, 0, 0>()[n] ); + } + + template + static typename Stencil::ValueType::value_type inY(const Stencil& S, int n) + { + return D1::difference( S.template getValue< 0, 1, 0>()[n], + S.template getValue< 0,-1, 0>()[n] ); + } + + template + static typename Stencil::ValueType::value_type inZ(const Stencil& S, int n) + { + return D1::difference( S.template getValue< 0, 0, 1>()[n], + S.template getValue< 0, 0,-1>()[n] ); + } +}; + +template<> +struct D1Vec +{ + + // random access version + template + static typename Accessor::ValueType::value_type + inX(const Accessor& grid, const Coord& ijk, int n) + { + return D1::difference( grid.getValue(ijk.offsetBy( 1, 0, 0))[n] , + grid.getValue(ijk.offsetBy(-1, 0, 0))[n] ); + } + + template + static typename Accessor::ValueType::value_type + inY(const Accessor& grid, const Coord& ijk, int n) + { + return D1::difference( grid.getValue(ijk.offsetBy(0, 1, 0))[n] , + grid.getValue(ijk.offsetBy(0,-1, 0))[n] ); + } + + template + static typename Accessor::ValueType::value_type + inZ(const Accessor& grid, const Coord& ijk, int n) + { + return D1::difference( grid.getValue(ijk.offsetBy(0, 0, 1))[n] , + grid.getValue(ijk.offsetBy(0, 0,-1))[n] ); + } + + + // stencil access version + template + static typename Stencil::ValueType::value_type inX(const Stencil& S, int n) + { + return D1::difference( S.template getValue< 1, 0, 0>()[n], + S.template getValue<-1, 0, 0>()[n] ); + } + + template + static typename Stencil::ValueType::value_type inY(const Stencil& S, int n) + { + return D1::difference( S.template getValue< 0, 1, 0>()[n], + S.template getValue< 0,-1, 0>()[n] ); + } + + template + static typename Stencil::ValueType::value_type inZ(const Stencil& S, int n) + { + return D1::difference( S.template getValue< 0, 0, 1>()[n], + S.template getValue< 0, 0,-1>()[n] ); + } +}; + + +template<> +struct D1Vec { + // typedef typename Accessor::ValueType::value_type value_type; + + + // random access version + template + static typename Accessor::ValueType::value_type + inX(const Accessor& grid, const Coord& ijk, int n) + { + return D1::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 + static typename Accessor::ValueType::value_type + inY(const Accessor& grid, const Coord& ijk, int n) + { + return D1::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 + static typename Accessor::ValueType::value_type + inZ(const Accessor& grid, const Coord& ijk, int n) + { + return D1::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 + static typename Stencil::ValueType::value_type inX(const Stencil& S, int n) + { + return D1::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 + static typename Stencil::ValueType::value_type inY(const Stencil& S, int n) + { + return D1::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 + static typename Stencil::ValueType::value_type inZ(const Stencil& S, int n) + { + return D1::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 +{ + //typedef typename Accessor::ValueType::value_type::value_type ValueType; + + // random access version + template + static typename Accessor::ValueType::value_type + inX(const Accessor& grid, const Coord& ijk, int n) + { + return D1::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 + static typename Accessor::ValueType::value_type + inY(const Accessor& grid, const Coord& ijk, int n) + { + return D1::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 + static typename Accessor::ValueType::value_type + inZ(const Accessor& grid, const Coord& ijk, int n) + { + return D1::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 + static typename Stencil::ValueType::value_type inX(const Stencil& S, int n) + { + return D1::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 + static typename Stencil::ValueType::value_type inY(const Stencil& S, int n) + { + return D1::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 + static typename Stencil::ValueType::value_type inZ(const Stencil& S, int n) + { + return D1::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 +struct D2 +{ + + template + static typename Accessor::ValueType inX(const Accessor& grid, const Coord& ijk); + template + static typename Accessor::ValueType inY(const Accessor& grid, const Coord& ijk); + template + static typename Accessor::ValueType inZ(const Accessor& grid, const Coord& ijk); + + // cross derivatives + template + static typename Accessor::ValueType inXandY(const Accessor& grid, const Coord& ijk); + + template + static typename Accessor::ValueType inXandZ(const Accessor& grid, const Coord& ijk); + + template + static typename Accessor::ValueType inYandZ(const Accessor& grid, const Coord& ijk); + + + // stencil access version + template + static typename Stencil::ValueType inX(const Stencil& S); + template + static typename Stencil::ValueType inY(const Stencil& S); + template + static typename Stencil::ValueType inZ(const Stencil& S); + + // cross derivatives + template + static typename Stencil::ValueType inXandY(const Stencil& S); + + template + static typename Stencil::ValueType inXandZ(const Stencil& S); + + template + static typename Stencil::ValueType inYandZ(const Stencil& S); +}; + +template<> +struct D2 +{ + + // the difference opperator + template + static ValueType difference(const ValueType& xp1, const ValueType& xp0, const ValueType& xm1) + { + return xp1 + xm1 - ValueType(2)*xp0; + } + + template + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 + 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 +{ + + // the difference opperator + template + 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 + 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 + 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 + 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 + 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 + static typename Accessor::ValueType inXandY(const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType ValueType; + typename Accessor::ValueType tmp1 = + D1::inX(grid, ijk.offsetBy(0, 1, 0)) - + D1::inX(grid, ijk.offsetBy(0,-1, 0)); + typename Accessor::ValueType tmp2 = + D1::inX(grid, ijk.offsetBy(0, 2, 0)) - + D1::inX(grid, ijk.offsetBy(0,-2, 0)); + return ValueType(2./3.)*tmp1 - ValueType(1./12.)*tmp2; + } + + template + static typename Accessor::ValueType inXandZ(const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType ValueType; + typename Accessor::ValueType tmp1 = + D1::inX(grid, ijk.offsetBy(0, 0, 1)) - + D1::inX(grid, ijk.offsetBy(0, 0,-1)); + typename Accessor::ValueType tmp2 = + D1::inX(grid, ijk.offsetBy(0, 0, 2)) - + D1::inX(grid, ijk.offsetBy(0, 0,-2)); + return ValueType(2./3.)*tmp1 - ValueType(1./12.)*tmp2; + } + + template + static typename Accessor::ValueType inYandZ(const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType ValueType; + typename Accessor::ValueType tmp1 = + D1::inY(grid, ijk.offsetBy(0, 0, 1)) - + D1::inY(grid, ijk.offsetBy(0, 0,-1)); + typename Accessor::ValueType tmp2 = + D1::inY(grid, ijk.offsetBy(0, 0, 2)) - + D1::inY(grid, ijk.offsetBy(0, 0,-2)); + return ValueType(2./3.)*tmp1 - ValueType(1./12.)*tmp2; + } + + + // stencil access version + template + 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 + 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 + 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 + 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 + 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 + 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 +{ + // the difference opperator + template + 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 + 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 + 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 + 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 + 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 + static typename Accessor::ValueType inXandY(const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType ValueT; + ValueT tmp1 = + D1::inX(grid, ijk.offsetBy(0, 1, 0)) - + D1::inX(grid, ijk.offsetBy(0,-1, 0)); + ValueT tmp2 = + D1::inX(grid, ijk.offsetBy(0, 2, 0)) - + D1::inX(grid, ijk.offsetBy(0,-2, 0)); + ValueT tmp3 = + D1::inX(grid, ijk.offsetBy(0, 3, 0)) - + D1::inX(grid, ijk.offsetBy(0,-3, 0)); + return ValueT(0.75*tmp1 - 0.15*tmp2 + 1./60*tmp3); + } + + template + static typename Accessor::ValueType inXandZ(const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType ValueT; + ValueT tmp1 = + D1::inX(grid, ijk.offsetBy(0, 0, 1)) - + D1::inX(grid, ijk.offsetBy(0, 0,-1)); + ValueT tmp2 = + D1::inX(grid, ijk.offsetBy(0, 0, 2)) - + D1::inX(grid, ijk.offsetBy(0, 0,-2)); + ValueT tmp3 = + D1::inX(grid, ijk.offsetBy(0, 0, 3)) - + D1::inX(grid, ijk.offsetBy(0, 0,-3)); + return ValueT(0.75*tmp1 - 0.15*tmp2 + 1./60*tmp3); + } + + template + static typename Accessor::ValueType inYandZ(const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType ValueT; + ValueT tmp1 = + D1::inY(grid, ijk.offsetBy(0, 0, 1)) - + D1::inY(grid, ijk.offsetBy(0, 0,-1)); + ValueT tmp2 = + D1::inY(grid, ijk.offsetBy(0, 0, 2)) - + D1::inY(grid, ijk.offsetBy(0, 0,-2)); + ValueT tmp3 = + D1::inY(grid, ijk.offsetBy(0, 0, 3)) - + D1::inY(grid, ijk.offsetBy(0, 0,-3)); + return ValueT(0.75*tmp1 - 0.15*tmp2 + 1./60*tmp3); + } + + + // stencil access version + template + 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 + 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 + 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 + 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 + 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 + 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-2016 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/nuparu/include/openvdb/include/openvdb/math/Hermite.h b/nuparu/include/openvdb/include/openvdb/math/Hermite.h new file mode 100644 index 00000000..97974ac9 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/math/Hermite.h @@ -0,0 +1,493 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2014 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 +#include +#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 + void setX(T offset, const Vec3&); + + template + void setY(T offset, const Vec3&); + + template + void setZ(T offset, const Vec3&); + //@} + + /// @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 Hermite operator+(const T&) const { return *this; } + template Hermite operator-(const T&) const { return *this; } + //@} + +private: + /// Helper function that quantizes a [0, 1) offset using 10-bits. + template + 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 +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 +inline void +Hermite::setX(T offset, const Vec3& n) +{ + mData &= ~MASK_XSLOT; // clear xslot + mData |= quantizeOffset(offset) << 20; + mXNormal = QuantizedUnitVec::pack(n); +} + +template +inline void +Hermite::setY(T offset, const Vec3& n) +{ + mData &= ~MASK_YSLOT; // clear yslot + mData |= quantizeOffset(offset) << 10; + mYNormal = QuantizedUnitVec::pack(n); +} + +template +inline void +Hermite::setZ(T offset, const Vec3& 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() { return math::Hermite(); } + + +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_MATH_HERMITE_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2014 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/nuparu/include/openvdb/include/openvdb/math/LegacyFrustum.h b/nuparu/include/openvdb/include/openvdb/math/LegacyFrustum.h new file mode 100644 index 00000000..b6b13c10 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/math/LegacyFrustum.h @@ -0,0 +1,196 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include // for Real typedef +#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" + Vec3i tmpMin, tmpMax; + is.read(reinterpret_cast(&tmpMin), sizeof(Vec3i::ValueType) * 3); + is.read(reinterpret_cast(&tmpMax), sizeof(Vec3i::ValueType) * 3); + + Coord tmpMinCoord(tmpMin); + Coord tmpMaxCoord(tmpMax); + + // set the extents + mExtents = CoordBBox(tmpMinCoord, tmpMaxCoord); + + // 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(&tmpW2C), + sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size); + is.read(reinterpret_cast(&mC2W), + sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size); + is.read(reinterpret_cast(&tmpC2S), + sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size); + is.read(reinterpret_cast(&tmpS2C), + sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size); + is.read(reinterpret_cast(&tmpWorldToLocal), + sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size); + is.read(reinterpret_cast(&mLocalToWorld), + sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size); + + is.read(reinterpret_cast(&tmpWindow[0]), sizeof(Real)); + is.read(reinterpret_cast(&tmpWindow[1]), sizeof(Real)); + is.read(reinterpret_cast(&tmpWindow[2]), sizeof(Real)); + is.read(reinterpret_cast(&tmpWindow[3]), sizeof(Real)); + is.read(reinterpret_cast(&tmpWindow[4]), sizeof(Real)); + is.read(reinterpret_cast(&tmpWindow[5]), sizeof(Real)); + + is.read(reinterpret_cast(&tmpPadding), sizeof(Real)); + + is.read(reinterpret_cast(&tmpS2U), + sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size); + is.read(reinterpret_cast(&mXYUnitToLocal), + sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size); + is.read(reinterpret_cast(&tmpXYLocalToUnit), + sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size); + is.read(reinterpret_cast(&mZUnitToLocal), + sizeof(Mat4d::value_type) * Mat4d::size * Mat4d::size); + is.read(reinterpret_cast(&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-2016 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/nuparu/include/openvdb/include/openvdb/math/Maps.h b/nuparu/include/openvdb/include/openvdb/math/Maps.h new file mode 100644 index 00000000..17655946 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/math/Maps.h @@ -0,0 +1,2685 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 "Math.h" +#include "Mat4.h" +#include "Vec3.h" +#include "BBox.h" +#include "Coord.h" +#include // for io::getFormatVersion() +#include +#include +#include +#include + +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 class CompoundMap; + +typedef CompoundMap UnitaryAndTranslationMap; +typedef CompoundMap, UnitaryMap> SpectralDecomposedMap; +typedef SpectralDecomposedMap SymmetricMap; +typedef CompoundMap FullyDecomposedMap; +typedef CompoundMap PolarDecomposedMap; + + +//////////////////////////////////////// + +/// Map traits + +template struct is_linear { static const bool value = false; }; +template<> struct is_linear { static const bool value = true; }; +template<> struct is_linear { static const bool value = true; }; +template<> struct is_linear { static const bool value = true; }; +template<> struct is_linear { static const bool value = true; }; +template<> struct is_linear { static const bool value = true; }; +template<> struct is_linear { static const bool value = true; }; +template<> struct is_linear { static const bool value = true; }; + +template struct is_linear > { + static const bool value = is_linear::value && is_linear::value; +}; + + +template struct is_uniform_scale { static const bool value = false; }; +template<> struct is_uniform_scale { static const bool value = true; }; + +template struct is_uniform_scale_translate { static const bool value = false; }; +template<> struct is_uniform_scale_translate { static const bool value = true; }; +template<> struct is_uniform_scale_translate { + static const bool value = true; +}; + + +template struct is_scale { static const bool value = false; }; +template<> struct is_scale { static const bool value = true; }; + +template struct is_scale_translate { static const bool value = false; }; +template<> struct is_scale_translate { static const bool value = true; }; + + +template struct is_uniform_diagonal_jacobian { + static const bool value = is_uniform_scale::value || is_uniform_scale_translate::value; +}; + +template struct is_diagonal_jacobian { + static const bool value = is_scale::value || is_scale_translate::value; +}; + + +//////////////////////////////////////// + +/// Utility methods + +/// @brief Create a SymmetricMap from a symmetric matrix. +/// Decomposes the map into Rotation Diagonal Rotation^T +OPENVDB_API boost::shared_ptr 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 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 createPolarDecomposedMap(const Mat3d& m); + + +/// @brief reduces an AffineMap to a ScaleMap or a ScaleTranslateMap when it can +OPENVDB_API boost::shared_ptr simplify(boost::shared_ptr 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 Ptr; + typedef boost::shared_ptr ConstPtr; + typedef Ptr (*MapFactory)(); + + virtual ~MapBase(){} + + virtual boost::shared_ptr 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 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; + + //@{ + /// @brief Apply the Inverse Jacobian Transpose of this map to a vector. + /// For a linear map this is equivalent to applying the transpose of + /// inverse map excluding translation. + virtual Vec3d applyIJT(const Vec3d& in) const = 0; + virtual Vec3d applyIJT(const Vec3d& in, const Vec3d& domainPos) const = 0; + //@} + + virtual Mat3d applyIJC(const Mat3d& m) const = 0; + virtual Mat3d applyIJC(const Mat3d& m, const Vec3d& v, const Vec3d& domainPos) const = 0; + + + virtual double determinant() const = 0; + virtual double determinant(const Vec3d&) const = 0; + + + //@{ + /// @brief Method to return the local size of a voxel. + /// When a location is specified as an argument, it is understood to be + /// be in the domain of the map (i.e. index space) + 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; + //@} + + //@{ + /// @brief Apply the Jacobian of this map to a vector. + /// For a linear map this is equivalent to applying the map excluding translation. + /// @warning Houdini 12.5 uses an earlier version of OpenVDB, and maps created + /// with that version lack a virtual table entry for this method. Do not call + /// this method from Houdini 12.5. + virtual Vec3d applyJacobian(const Vec3d& in) const = 0; + virtual Vec3d applyJacobian(const Vec3d& in, const Vec3d& domainPos) const = 0; + //@} + + //@{ + /// @brief Apply the InverseJacobian of this map to a vector. + /// For a linear map this is equivalent to applying the map inverse excluding translation. + /// @warning Houdini 12.5 uses an earlier version of OpenVDB, and maps created + /// with that version lack a virtual table entry for this method. Do not call + /// this method from Houdini 12.5. + virtual Vec3d applyInverseJacobian(const Vec3d& in) const = 0; + virtual Vec3d applyInverseJacobian(const Vec3d& in, const Vec3d& domainPos) const = 0; + //@} + + + //@{ + /// @brief Apply the Jacobian transpose of this map to a vector. + /// For a linear map this is equivalent to applying the transpose of the map + /// excluding translation. + /// @warning Houdini 12.5 uses an earlier version of OpenVDB, and maps created + /// with that version lack a virtual table entry for this method. Do not call + /// this method from Houdini 12.5. + virtual Vec3d applyJT(const Vec3d& in) const = 0; + virtual Vec3d applyJT(const Vec3d& in, const Vec3d& domainPos) const = 0; + //@} + + /// @brief Return a new map representing the inverse of this map. + /// @throw NotImplementedError if the map is a NonlinearFrustumMap. + /// @warning Houdini 12.5 uses an earlier version of OpenVDB, and maps created + /// with that version lack a virtual table entry for this method. Do not call + /// this method from Houdini 12.5. + virtual MapBase::Ptr inverseMap() const = 0; + +protected: + MapBase() {} + + template + static bool isEqualBase(const MapT& self, const MapBase& other) + { + return other.isType() && (self == *static_cast(&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 MapDictionary; + + 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* staticInstance(); + + static MapRegistry* mInstance; + + 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 Ptr; + typedef boost::shared_ptr 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)); } + + MapBase::Ptr inverseMap() const { return MapBase::Ptr(new AffineMap(mMatrixInv)); } + + 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 + { + Mat3d mat = mMatrix.getMat3(); + const double det = mat.det(); + if (isApproxEqual(det, double(0))) { + return false; + } else { + mat *= (1.f / pow(std::abs(det),1./3.)); + return isUnitary(mat); + } + } + + 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 { return mMatrix.transform3x3(in); } + + /// Return the Inverse Jacobian of the map applied to @a in. (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in, const Vec3d&) const { return applyInverseJacobian(in); } + /// Return the Inverse Jacobian of the map applied to @a in. (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in) const { return mMatrixInv.transform3x3(in); } + + /// Return the Jacobian Transpose of the map applied to @a in. + /// This tranforms range-space gradients to domain-space gradients + Vec3d applyJT(const Vec3d& in, const Vec3d&) const { return applyJT(in); } + /// Return the Jacobian Transpose of the map applied to @a in. + Vec3d applyJT(const Vec3d& in) const { + const double* m = mMatrix.asPointer(); + return Vec3d( m[ 0] * in[0] + m[ 1] * in[1] + m[ 2] * in[2], + m[ 4] * in[0] + m[ 5] * in[1] + m[ 6] * in[2], + m[ 8] * in[0] + m[ 9] * 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 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 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 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(affineMap); + } + MapBase::Ptr preScale(const Vec3d& s) const + { + AffineMap::Ptr affineMap = getAffineMap(); + affineMap->accumPreScale(s); + return boost::static_pointer_cast(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(affineMap); + } + MapBase::Ptr postScale(const Vec3d& s) const + { + AffineMap::Ptr affineMap = getAffineMap(); + affineMap->accumPostScale(s); + return boost::static_pointer_cast(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() { + Mat3d mat3 = mMatrix.getMat3(); + mDeterminant = mat3.det(); + + if (std::abs(mDeterminant) < (3.0 * math::Tolerance::value())) { + OPENVDB_THROW(ArithmeticError, + "Tried to initialize an affine transform from a nearly singular matrix"); + } + mMatrixInv = mMatrix.inverse(); + mJacobianInv = mat3.inverse().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 Ptr; + typedef boost::shared_ptr ConstPtr; + + ScaleMap(): MapBase(), 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){} + + 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 * math::Tolerance::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)); } + + MapBase::Ptr inverseMap() const { return MapBase::Ptr(new ScaleMap(mScaleValuesInverse)); } + + 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 = 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); } + + /// Return the Inverse Jacobian of the map applied to @a in. (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in, const Vec3d&) const { return applyInverseJacobian(in); } + /// Return the Inverse Jacobian of the map applied to @a in. (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in) const { return applyInverseMap(in); } + + /// Return the Jacobian Transpose of the map applied to @a in. + /// This tranforms range-space gradients to domain-space gradients + Vec3d applyJT(const Vec3d& in, const Vec3d&) const { return applyJT(in); } + /// Return the Jacobian Transpose of the map applied to @a in. + Vec3d applyJT(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(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 Ptr; + typedef boost::shared_ptr 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)); } + + MapBase::Ptr inverseMap() const + { + const Vec3d& invScale = getInvScale(); + return MapBase::Ptr(new UniformScaleMap( invScale[0])); + } + + 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 Ptr; + typedef boost::shared_ptr 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)); } + + MapBase::Ptr inverseMap() const { return MapBase::Ptr(new TranslationMap(-mTranslation)); } + + 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; } + + /// Return the Inverse Jacobian of the map applied to @a in. (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in, const Vec3d&) const { return applyInverseJacobian(in); } + /// Return the Inverse Jacobian of the map applied to @a in. (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in) const { return in; } + + + /// Return the Jacobian Transpose of the map applied to @a in. + /// This tranforms range-space gradients to domain-space gradients + Vec3d applyJT(const Vec3d& in, const Vec3d&) const { return applyJT(in); } + /// Return the Jacobian Transpose of the map applied to @a in. + Vec3d applyJT(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 + return mTranslation.eq(other.mTranslation); + } + + 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 Ptr; + typedef boost::shared_ptr 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 * math::Tolerance::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)); } + + MapBase::Ptr inverseMap() const + { + return MapBase::Ptr(new ScaleTranslateMap( + mScaleValuesInverse, -mScaleValuesInverse * mTranslation)); + } + + 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 = 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() ) * mScaleValuesInverse.x(), + (in.y() - mTranslation.y() ) * mScaleValuesInverse.y(), + (in.z() - mTranslation.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 in * mScaleValues; } + + /// Return the Inverse Jacobian of the map applied to @a in. (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in, const Vec3d&) const { return applyInverseJacobian(in); } + /// Return the Inverse Jacobian of the map applied to @a in. (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in) const { return in * mScaleValuesInverse; } + + /// Return the Jacobian Transpose of the map applied to @a in. + /// This tranforms range-space gradients to domain-space gradients + Vec3d applyJT(const Vec3d& in, const Vec3d&) const { return applyJT(in); } + /// Return the Jacobian Transpose of the map applied to @a in. + Vec3d applyJT(const Vec3d& in) const { return applyJacobian(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 Vec3d( + in.x() * mScaleValuesInverse.x(), + in.y() * mScaleValuesInverse.y(), + in.z() * mScaleValuesInverse.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(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 Ptr; + typedef boost::shared_ptr 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)); } + + MapBase::Ptr inverseMap() const + { + const Vec3d& scaleInv = getInvScale(); + const Vec3d& trans = getTranslation(); + return MapBase::Ptr(new UniformScaleTranslateMap(scaleInv[0], -scaleInv[0] * trans)); + } + + 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 Ptr; + typedef boost::shared_ptr 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)); } + + MapBase::Ptr inverseMap() const + { + return MapBase::Ptr(new UnitaryMap(mAffineMap.getMat4().inverse())); + } + + 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 mAffineMap.applyJacobian(in); } + + /// Return the Inverse Jacobian of the map applied to @a in. (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in, const Vec3d&) const { return applyInverseJacobian(in); } + /// Return the Inverse Jacobian of the map applied to @a in. (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in) const { return mAffineMap.applyInverseJacobian(in); } + + + /// Return the Jacobian Transpose of the map applied to @a in. + /// This tranforms range-space gradients to domain-space gradients + Vec3d applyJT(const Vec3d& in, const Vec3d&) const { return applyJT(in); } + /// Return the Jacobian Transpose of the map applied to @a in. + Vec3d applyJT(const Vec3d& in) const { + // The transpose of the unitary map is its inverse + return applyInverseMap(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(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(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. +/// First it will take a box of size (Lx X Ly X Lz) defined by a member data bounding box +/// and map it into a frustum with near plane (1 X Ly/Lx) and prescribed depth +/// Then this frustum is transformed by an internal second map: most often a uniform scale, +/// but other effects 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 Ptr; + typedef boost::shared_ptr 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(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( + 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)); } + + /// @brief Not implemented, since there is currently no map type that can + /// represent the inverse of a frustum + /// @throw NotImplementedError + MapBase::Ptr inverseMap() const + { + OPENVDB_THROW(NotImplementedError, + "inverseMap() is not implemented for NonlinearFrustumMap"); + } + 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 defined at @c isloc applied to @c in + Vec3d applyJacobian(const Vec3d& in, const Vec3d& isloc) const + { + // Move the center of the x-face of the bbox + // to the origin in index space. + Vec3d centered(isloc); + 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 Inverse Jacobian of the map applied to @a in. (i.e. inverse map with out translation) + Vec3d applyInverseJacobian(const Vec3d& in) const { return mSecondMap.applyInverseJacobian(in); } + /// Return the Inverse Jacobian defined at @c isloc of the map applied to @a in. + Vec3d applyInverseJacobian(const Vec3d& in, const Vec3d& isloc) const { + + // Move the center of the x-face of the bbox + // to the origin in index space. + Vec3d centered(isloc); + 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; + + + Vec3d out = mSecondMap.applyInverseJacobian(in); + + out.x() = (out.x() - scale2 * centered.x() * out.z() / mDepthOnLz) / scale; + out.y() = (out.y() - scale2 * centered.y() * out.z() / mDepthOnLz) / scale; + out.z() = out.z() / mDepthOnLz; + + return out; + } + + + + /// Return the Jacobian Transpose of the map applied to vector @c in at @c indexloc. + /// This tranforms range-space gradients to domain-space gradients. + /// + Vec3d applyJT(const Vec3d& in, const Vec3d& isloc) const { + const Vec3d tmp = mSecondMap.applyJT(in); + // Move the center of the x-face of the bbox + // to the origin in index space. + Vec3d centered(isloc); + 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; + + return Vec3d(scale * tmp.x(), + scale * tmp.y(), + scale2 * centered.x()* tmp.x() + + scale2 * centered.y()* tmp.y() + + mDepthOnLz * tmp.z()); + } + /// Return the Jacobian Transpose of the second map applied to @c in. + Vec3d applyJT(const Vec3d& in) const { + return mSecondMap.applyJT(in); + } + + /// 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(i) = 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(&mTaper), sizeof(double)); + is.read(reinterpret_cast(&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(&mTaper), sizeof(double)); + os.write(reinterpret_cast(&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 +class CompoundMap +{ +public: + typedef CompoundMap MyType; + + typedef boost::shared_ptr Ptr; + typedef boost::shared_ptr 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::value) { + return mAffineMap.isIdentity(); + } else { + return mFirstMap.isIdentity()&&mSecondMap.isIdentity(); + } + } + + bool isDiagonal() const { + if (is_linear::value) { + return mAffineMap.isDiagonal(); + } else { + return mFirstMap.isDiagonal()&&mSecondMap.isDiagonal(); + } + } + + AffineMap::Ptr getAffineMap() const + { + if (is_linear::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::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-2016 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/nuparu/include/openvdb/include/openvdb/math/Mat.h b/nuparu/include/openvdb/include/openvdb/math/Mat.h new file mode 100644 index 00000000..d413e77d --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/math/Mat.h @@ -0,0 +1,1026 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include +#include +#include +#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 +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& m) + { + ostr << m.str(); + return ostr; + } + + void write(std::ostream& os) const { + os.write(reinterpret_cast(&mm), sizeof(T)*SIZE*SIZE); + } + + void read(std::istream& is) { + is.read(reinterpret_cast(&mm), sizeof(T)*SIZE*SIZE); + } + + +protected: + T mm[SIZE*SIZE]; +}; + + +template class Quat; +template class Vec3; + +/// @brief Return the rotation matrix specified by the given quaternion. +/// @details The quaternion is normalized and used to construct the matrix. +/// Note that the matrix is transposed to match post-multiplication semantics. +template +MatType +rotation(const Quat &q, + typename MatType::value_type eps = static_cast(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 = T(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 Return a matrix for rotation by @a angle radians about the given @a axis. +/// @param axis The axis (one of X, Y, Z) to rotate about. +/// @param angle The rotation angle, in radians. +template +MatType +rotation(Axis axis, typename MatType::value_type angle) +{ + typedef typename MatType::value_type T; + T c = static_cast(cos(angle)); + T s = static_cast(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"); + } +} + + +/// @brief Return a matrix for rotation by @a angle radians about the given @a axis. +/// @note The axis must be a unit vector. +template +MatType +rotation(const Vec3 &_axis, typename MatType::value_type angle) +{ + typedef typename MatType::value_type T; + T txy, txz, tyz, sx, sy, sz; + + Vec3 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); +} + + +/// @brief Return the Euler angles composing the given rotation matrix. +/// @details 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 +Vec3 +eulerAngles( + const MatType& mat, + RotationOrder rotationOrder, + typename MatType::value_type eps = static_cast(1.0e-8)) +{ + typedef typename MatType::value_type ValueType; + typedef Vec3 V; + ValueType phi, theta, psi; + + switch(rotationOrder) + { + case XYZ_ROTATION: + if (isApproxEqual(mat[2][0], ValueType(1.0), eps)) { + theta = ValueType(M_PI_2); + phi = ValueType(0.5 * atan2(mat[1][2], mat[1][1])); + psi = phi; + } else if (isApproxEqual(mat[2][0], ValueType(-1.0), eps)) { + theta = ValueType(-M_PI_2); + phi = ValueType(0.5 * atan2(mat[1][2], mat[1][1])); + psi = -phi; + } else { + psi = ValueType(atan2(-mat[1][0],mat[0][0])); + phi = ValueType(atan2(-mat[2][1],mat[2][2])); + theta = ValueType(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 = ValueType(M_PI_2); + phi = ValueType(0.5 * atan2(mat[0][1], mat[0][0])); + psi = phi; + } else if (isApproxEqual(mat[1][2], ValueType(-1.0), eps)) { + theta = ValueType(-M_PI/2); + phi = ValueType(0.5 * atan2(mat[0][1],mat[2][1])); + psi = -phi; + } else { + psi = ValueType(atan2(-mat[0][2], mat[2][2])); + phi = ValueType(atan2(-mat[1][0], mat[1][1])); + theta = ValueType(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 = ValueType(M_PI_2); + phi = ValueType(0.5 * atan2(mat[2][0], mat[2][2])); + psi = phi; + } else if (isApproxEqual(mat[0][1], ValueType(-1.0), eps)) { + theta = ValueType(-M_PI/2); + phi = ValueType(0.5 * atan2(mat[2][0], mat[1][0])); + psi = -phi; + } else { + psi = ValueType(atan2(-mat[2][1], mat[1][1])); + phi = ValueType(atan2(-mat[0][2], mat[0][0])); + theta = ValueType(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 = ValueType(0.0); + phi = ValueType(0.5 * atan2(mat[1][2], mat[1][1])); + psi = phi; + } else if (isApproxEqual(mat[0][0], ValueType(-1.0), eps)) { + theta = ValueType(M_PI); + psi = ValueType(0.5 * atan2(mat[2][1], -mat[1][1])); + phi = - psi; + } else { + psi = ValueType(atan2(mat[2][0], -mat[1][0])); + phi = ValueType(atan2(mat[0][2], mat[0][1])); + theta = ValueType(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 = ValueType(0.0); + phi = ValueType(0.5 * atan2(mat[0][1], mat[0][0])); + psi = phi; + } else if (isApproxEqual(mat[2][2], ValueType(-1.0), eps)) { + theta = ValueType(M_PI); + phi = ValueType(0.5 * atan2(mat[0][1], mat[0][0])); + psi = -phi; + } else { + psi = ValueType(atan2(mat[0][2], mat[1][2])); + phi = ValueType(atan2(mat[2][0], -mat[2][1])); + theta = ValueType(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 = ValueType(-M_PI_2); + phi = ValueType(0.5 * atan2(-mat[1][0], mat[0][0])); + psi = phi; + } else if (isApproxEqual(mat[2][1], ValueType(-1.0), eps)) { + theta = ValueType(M_PI_2); + phi = ValueType(0.5 * atan2(mat[1][0], mat[0][0])); + psi = -phi; + } else { + psi = ValueType(atan2(mat[0][1], mat[1][1])); + phi = ValueType(atan2(mat[2][0], mat[2][2])); + theta = ValueType(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 = ValueType(-M_PI_2); + phi = ValueType(0.5 * atan2(-mat[1][0], mat[1][1])); + psi = phi; + } else if (isApproxEqual(mat[0][2], ValueType(-1.0), eps)) { + theta = ValueType(M_PI_2); + phi = ValueType(0.5 * atan2(mat[2][1], mat[2][0])); + psi = -phi; + } else { + psi = ValueType(atan2(mat[1][2], mat[2][2])); + phi = ValueType(atan2(mat[0][1], mat[0][0])); + theta = ValueType(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 = ValueType(M_PI_2); + psi = ValueType(0.5 * atan2(mat[2][1], mat[2][2])); + phi = -psi; + } else if (isApproxEqual(mat[1][0], ValueType(1.0), eps)) { + theta = ValueType(-M_PI_2); + psi = ValueType(0.5 * atan2(- mat[2][1], mat[2][2])); + phi = psi; + } else { + psi = ValueType(atan2(mat[2][0], mat[0][0])); + phi = ValueType(atan2(mat[1][2], mat[1][1])); + theta = ValueType(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 Return a rotation matrix that maps @a v1 onto @a v2 +/// about the cross product of @a v1 and @a v2. +template +MatType +rotation( + const Vec3& _v1, + const Vec3& _v2, + typename MatType::value_type eps=1.0e-8) +{ + typedef typename MatType::value_type T; + Vec3 v1(_v1); + Vec3 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 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 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 a matrix that scales by @a s. +template +MatType +scale(const Vec3& s) +{ + // Gets identity, then sets top 3 diagonal + // Inefficient by 3 sets. + + MatType result; + result.setIdentity(); + result[0][0] = s[0]; + result[1][1] = s[1]; + result[2][2] = s[2]; + + return result; +} + + +/// Return a Vec3 representing the lengths of the passed matrix's upper 3x3's rows. +template +Vec3 +getScale(const MatType &mat) +{ + typedef Vec3 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()); +} + + +/// @brief Return a copy of the given matrix with its upper 3x3 rows normalized. +/// @details This can be geometrically interpreted as a matrix with no scaling +/// along its major axes. +template +MatType +unit(const MatType &mat, typename MatType::value_type eps = 1.0e-8) +{ + Vec3 dud; + return unit(mat, eps, dud); +} + + +/// @brief Return a copy of the given matrix with its upper 3x3 rows normalized, +/// and return the length of each of these rows in @a scaling. +/// @details This can be geometrically interpretted as a matrix with no scaling +/// along its major axes, and the scaling in the input vector +template +MatType +unit( + const MatType &in, + typename MatType::value_type eps, + Vec3& scaling) +{ + typedef typename MatType::value_type T; + MatType result(in); + + for (int i(0); i < 3; i++) { + try { + const Vec3 u( + Vec3(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 @a axis0 by a fraction of @a axis1. +/// @param axis0 The fixed axis of the shear. +/// @param axis1 The shear axis. +/// @param shear The shear factor. +template +MatType +shear(Axis axis0, Axis axis1, typename MatType::value_type shear) +{ + int index0 = static_cast(axis0); + int index1 = static_cast(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 +MatType +skew(const Vec3 &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; +} + + +/// @brief Return an orientation matrix such that z points along @a direction, +/// and y is along the @a direction / @a vertical plane. +template +MatType +aim(const Vec3& direction, + const Vec3& vertical) +{ + typedef typename MatType::value_type T; + Vec3 forward(direction.unit()); + Vec3 horizontal(vertical.unit().cross(forward).unit()); + Vec3 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; +} + +/// @brief This function snaps a specific axis to a specific direction, +/// preserving scaling. +/// @details It does this using minimum energy, thus posing a unique solution if +/// basis & direction aren't parallel. +/// @note @a direction need not be unit. +template +inline MatType +snapMatBasis(const MatType& source, Axis axis, const Vec3& direction) +{ + typedef typename MatType::value_type T; + + Vec3 unitDir(direction.unit()); + Vec3 ourUnitAxis(source.row(axis).unit()); + + // Are the two parallel? + T parallel = unitDir.dot(ourUnitAxis); + + // Already snapped! + if (isApproxEqual(parallel, T(1.0))) return source; + + if (isApproxEqual(parallel, T(-1.0))) { + OPENVDB_THROW(ValueError, "Cannot snap to inverse axis"); + } + + // Find angle between our basis and the one specified + T angleBetween(angle(unitDir, ourUnitAxis)); + // Caclulate axis to rotate along + Vec3 rotationAxis = unitDir.cross(ourUnitAxis); + + MatType rotation; + rotation.setToRotation(rotationAxis, angleBetween); + + return source * rotation; +} + +/// @brief Write 0s along Mat4's last row and column, and a 1 on its diagonal. +/// @details Useful initialization when we're initializing just the 3x3 block. +template +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; +} + + +/// @brief Solve for A=B*B, given A. +/// @details Denman-Beavers square root iteration +template +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 +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; + } +} + + +/// @brief Determine if a matrix is an identity matrix. +template +inline bool +isIdentity(const MatType& m) +{ + return m.eq(MatType::identity()); +} + + +/// @brief Determine if a matrix is invertible. +template +inline bool +isInvertible(const MatType& m) +{ + typedef typename MatType::ValueType value_type; + return !isApproxEqual(m.det(), (value_type)0); +} + + +/// @brief Determine if a matrix is symmetric. +/// @details This implicitly uses math::isApproxEqual() to determine equality. +template +inline bool +isSymmetric(const MatType& m) +{ + return m.eq(m.transpose()); +} + + +/// Determine if a matrix is unitary (i.e., rotation or reflection). +template +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()); +} + + +/// Determine if a matrix is diagonal. +template +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)); +} + + +/// Return the @f$L_\infty@f$ norm of an N x N matrix. +template +typename MatType::ValueType +lInfinityNorm(const MatType& matrix) +{ + int n = MatType::size; + typename MatType::ValueType norm = 0; + + for( int j = 0; j +typename MatType::ValueType +lOneNorm(const MatType& matrix) +{ + int n = MatType::size; + typename MatType::ValueType norm = 0; + + for( int i = 0; i +bool +polarDecomposition(const MatType& input, MatType& unitary, + MatType& positive_hermitian, unsigned int MAX_ITERATIONS=100) +{ + unitary = input; + MatType new_unitary(input); + MatType unitary_inv; + + if (fabs(unitary.det()) < math::Tolerance::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 > math::Tolerance::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-2016 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/nuparu/include/openvdb/include/openvdb/math/Mat3.h b/nuparu/include/openvdb/include/openvdb/math/Mat3.h new file mode 100644 index 00000000..1b64e883 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/math/Mat3.h @@ -0,0 +1,867 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include +#include +#include +#include "Vec3.h" +#include "Mat.h" + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + +template class Vec3; +template class Mat4; +template class Quat; + +/// @class Mat3 Mat3.h +/// @brief 3x3 matrix class. +template +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 &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 + Mat3(Source a, Source b, Source c, + Source d, Source e, Source f, + Source g, Source h, Source i) + { + MyBase::mm[0] = static_cast(a); + MyBase::mm[1] = static_cast(b); + MyBase::mm[2] = static_cast(c); + MyBase::mm[3] = static_cast(d); + MyBase::mm[4] = static_cast(e); + MyBase::mm[5] = static_cast(f); + MyBase::mm[6] = static_cast(g); + MyBase::mm[7] = static_cast(h); + MyBase::mm[8] = static_cast(i); + } // constructor1Test + + /// Construct matrix from rows or columns vectors (defaults to rows + /// for historical reasons) + template + Mat3(const Vec3 &v1, const Vec3 &v2, const Vec3 &v3, bool rows = true) + { + if (rows) { + this->setRows(v1, v2, v3); + } else { + this->setColumns(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 + 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 + explicit Mat3(const Mat3 &m) + { + for (int i=0; i<3; ++i) { + for (int j=0; j<3; ++j) { + MyBase::mm[i*3 + j] = static_cast(m[i][j]); + } + } + } + + /// Conversion from Mat4 (copies top left) + explicit Mat3(const Mat4 &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& identity() { + return sIdentity; + } + + /// Predefined constant for zero matrix + static const Mat3& zero() { + return sZero; + } + + /// Set ith row to vector v + void setRow(int i, const Vec3 &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 row(int i) const + { + // assert(i>=0 && i<3); + return Vec3((*this)(i,0), (*this)(i,1), (*this)(i,2)); + } // rowColumnTest + + /// Set jth column to vector v + void setCol(int j, const Vec3& 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 col(int j) const + { + // assert(j>=0 && j<3); + return Vec3((*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 rows of "this" matrix to the vectors v1, v2, v3 + void setRows(const Vec3 &v1, const Vec3 &v2, const Vec3 &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]; + } // setRows + + /// Set the columns of "this" matrix to the vectors v1, v2, v3 + void setColumns(const Vec3 &v1, const Vec3 &v2, const Vec3 &v3) + { + MyBase::mm[0] = v1[0]; + MyBase::mm[1] = v2[0]; + MyBase::mm[2] = v3[0]; + MyBase::mm[3] = v1[1]; + MyBase::mm[4] = v2[1]; + MyBase::mm[5] = v3[1]; + MyBase::mm[6] = v1[2]; + MyBase::mm[7] = v2[2]; + MyBase::mm[8] = v3[2]; + } // setColumns + + /// Set the rows of "this" matrix to the vectors v1, v2, v3 + OPENVDB_DEPRECATED void setBasis(const Vec3 &v1, const Vec3 &v2, const Vec3 &v3) + { + this->setRows(v1, v2, v3); + } + + /// Set diagonal and symmetric triangular components + void setSymmetric(const Vec3 &vdiag, const Vec3 &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 &vdiag, const Vec3 &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 &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 &q) + {*this = rotation >(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 &axis, T angle) + {*this = rotation >(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 + const Mat3& operator=(const Mat3 &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 operator-() const + { + return Mat3( + -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 + const Mat3& 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 + const Mat3 &operator+=(const Mat3 &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 + const Mat3 &operator-=(const Mat3 &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 + const Mat3 &operator*=(const Mat3 &m1) + { + Mat3 m0(*this); + const T* s0 = m0.asPointer(); + const S* s1 = m1.asPointer(); + + MyBase::mm[0] = static_cast(s0[0] * s1[0] + + s0[1] * s1[3] + + s0[2] * s1[6]); + MyBase::mm[1] = static_cast(s0[0] * s1[1] + + s0[1] * s1[4] + + s0[2] * s1[7]); + MyBase::mm[2] = static_cast(s0[0] * s1[2] + + s0[1] * s1[5] + + s0[2] * s1[8]); + + MyBase::mm[3] = static_cast(s0[3] * s1[0] + + s0[4] * s1[3] + + s0[5] * s1[6]); + MyBase::mm[4] = static_cast(s0[3] * s1[1] + + s0[4] * s1[4] + + s0[5] * s1[7]); + MyBase::mm[5] = static_cast(s0[3] * s1[2] + + s0[4] * s1[5] + + s0[5] * s1[8]); + + MyBase::mm[6] = static_cast(s0[6] * s1[0] + + s0[7] * s1[3] + + s0[8] * s1[6]); + MyBase::mm[7] = static_cast(s0[6] * s1[1] + + s0[7] * s1[4] + + s0[8] * s1[7]); + MyBase::mm[8] = static_cast(s0[6] * s1[2] + + s0[7] * s1[5] + + s0[8] * s1[8]); + + return *this; + } + + /// @brief Return the cofactor matrix of "this" + Mat3 cofactor() const + { + return Mat3( + MyBase::mm[4] * MyBase::mm[8] - MyBase::mm[5] * MyBase::mm[7], + MyBase::mm[5] * MyBase::mm[6] - MyBase::mm[3] * MyBase::mm[8], + MyBase::mm[3] * MyBase::mm[7] - MyBase::mm[4] * MyBase::mm[6], + MyBase::mm[2] * MyBase::mm[7] - MyBase::mm[1] * MyBase::mm[8], + MyBase::mm[0] * MyBase::mm[8] - MyBase::mm[2] * MyBase::mm[6], + MyBase::mm[1] * MyBase::mm[6] - MyBase::mm[0] * MyBase::mm[7], + MyBase::mm[1] * MyBase::mm[5] - MyBase::mm[2] * MyBase::mm[4], + MyBase::mm[2] * MyBase::mm[3] - MyBase::mm[0] * MyBase::mm[5], + MyBase::mm[0] * MyBase::mm[4] - MyBase::mm[1] * MyBase::mm[3]); + } + + /// returns adjoint of "this", i.e. the transpose of the cofactor of "this" + Mat3 adjoint() const + { + return Mat3( + 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( + 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 ArithmeticError if singular + Mat3 inverse(T tolerance = 0) const + { + Mat3 inv(this->adjoint()); + + const 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,T(0.0),tolerance)) { + OPENVDB_THROW(ArithmeticError, "Inversion of singular 3x3 matrix"); + } + return inv * (T(1)/det); + } // invertTest + + /// Determinant of matrix + T det() const + { + const T co00 = MyBase::mm[4]*MyBase::mm[8] - MyBase::mm[5]*MyBase::mm[7]; + const T co10 = MyBase::mm[5]*MyBase::mm[6] - MyBase::mm[3]*MyBase::mm[8]; + const T co20 = MyBase::mm[3]*MyBase::mm[7] - MyBase::mm[4]*MyBase::mm[6]; + return MyBase::mm[0]*co00 + MyBase::mm[1]*co10 + MyBase::mm[2]*co20; + } // 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 &direction) + { + return snapMatBasis(*this, axis, direction); + } + + /// Return the transformed vector by "this" matrix. + /// This function is equivalent to post-multiplying the matrix. + template + Vec3 transform(const Vec3 &v) const + { + return static_cast< Vec3 >(v * *this); + } // xformVectorTest + + /// Return the transformed vector by transpose of "this" matrix. + /// This function is equivalent to pre-multiplying the matrix. + template + Vec3 pretransform(const Vec3 &v) const + { + return static_cast< Vec3 >(*this * v); + } // xformTVectorTest + + + /// Treats diag as a diagonal matrix and returns the + /// multiplication of "this" with diag (from the right). + Mat3 timesDiagonal(const Vec3& diag) const + { + Mat3 ret(*this); + + ret.mm[0] *= diag(0); + ret.mm[1] *= diag(1); + ret.mm[2] *= diag(2); + ret.mm[3] *= diag(0); + ret.mm[4] *= diag(1); + ret.mm[5] *= diag(2); + ret.mm[6] *= diag(0); + ret.mm[7] *= diag(1); + ret.mm[8] *= diag(2); + return ret; + } + +private: + static const Mat3 sIdentity; + static const Mat3 sZero; +}; // class Mat3 + + +template +const Mat3 Mat3::sIdentity = Mat3(1, 0, 0, + 0, 1, 0, + 0, 0, 1); + +template +const Mat3 Mat3::sZero = Mat3(0, 0, 0, + 0, 0, 0, + 0, 0, 0); + +/// @relates Mat3 +/// @brief Equality operator, does exact floating point comparisons +template +bool operator==(const Mat3 &m0, const Mat3 &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 +bool operator!=(const Mat3 &m0, const Mat3 &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 +Mat3::type> operator*(S scalar, const Mat3 &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 +Mat3::type> operator*(const Mat3 &m, S scalar) +{ + Mat3::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 +Mat3::type> operator+(const Mat3 &m0, const Mat3 &m1) +{ + Mat3::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 +Mat3::type> operator-(const Mat3 &m0, const Mat3 &m1) +{ + Mat3::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 +Mat3::type>operator*(const Mat3 &m0, const Mat3 &m1) +{ + Mat3::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 +inline Vec3::type> +operator*(const Mat3 &_m, const Vec3 &_v) +{ + MT const *m = _m.asPointer(); + return Vec3::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 +inline Vec3::type> +operator*(const Vec3 &_v, const Mat3 &_m) +{ + MT const *m = _m.asPointer(); + return Vec3::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 +inline Vec3 &operator *= (Vec3 &_v, const Mat3 &_m) +{ + Vec3 mult = _v * _m; + _v = mult; + return _v; +} + +/// Returns outer product of v1, v2, i.e. v1 v2^T if v1 and v2 are +/// column vectors, e.g. M = Mat3f::outerproduct(v1,v2); +template +Mat3 outerProduct(const Vec3& v1, const Vec3& v2) +{ + return Mat3(v1[0]*v2[0], v1[0]*v2[1], v1[0]*v2[2], + v1[1]*v2[0], v1[1]*v2[1], v1[1]*v2[2], + v1[2]*v2[0], v1[2]*v2[1], v1[2]*v2[2]); +}// outerProduct + +typedef Mat3 Mat3s; +typedef Mat3 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 +Mat3 powLerp(const Mat3 &m1, const Mat3 &m2, T t) +{ + Mat3 x = m1.inverse() * m2; + powSolve(x, x, t); + Mat3 m = m1 * x; + return m; +} + + +namespace { + template + void pivot(int i, int j, Mat3& S, Vec3& D, Mat3& Q) + { + const int& n = Mat3::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*math::Tolerance::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 +bool diagonalizeSymmetricMatrix(const Mat3& input, Mat3& Q, Vec3& D, + unsigned int MAX_ITERATIONS=250) +{ + /// use Givens rotation matrix to eliminate off-diagonal entries. + /// initialize the rotation matrix as idenity + Q = Mat3::identity(); + int n = Mat3::size; // should be 3 + + /// temp matrix. Assumed to be symmetric + Mat3 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) < math::Tolerance::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*math::Tolerance::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-2016 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/nuparu/include/openvdb/include/openvdb/math/Mat4.h b/nuparu/include/openvdb/include/openvdb/math/Mat4.h new file mode 100644 index 00000000..34f7d7fc --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/math/Mat4.h @@ -0,0 +1,1398 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include +#include +#include +#include +#include +#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 class Vec4; + + +/// @class Mat4 Mat4.h +/// @brief 4x4 -matrix class. +template +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 + Mat4(Source *a) + { + for (int 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 + 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] = T(a); + MyBase::mm[ 1] = T(b); + MyBase::mm[ 2] = T(c); + MyBase::mm[ 3] = T(d); + + MyBase::mm[ 4] = T(e); + MyBase::mm[ 5] = T(f); + MyBase::mm[ 6] = T(g); + MyBase::mm[ 7] = T(h); + + MyBase::mm[ 8] = T(i); + MyBase::mm[ 9] = T(j); + MyBase::mm[10] = T(k); + MyBase::mm[11] = T(l); + + MyBase::mm[12] = T(m); + MyBase::mm[13] = T(n); + MyBase::mm[14] = T(o); + MyBase::mm[15] = T(p); + } + + /// Construct matrix from rows or columns vectors (defaults to rows + /// for historical reasons) + template + Mat4(const Vec4 &v1, const Vec4 &v2, + const Vec4 &v3, const Vec4 &v4, bool rows = true) + { + if (rows) { + this->setRows(v1, v2, v3); + } else { + this->setColumns(v1, v2, v3); + } + } + + /// 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 + explicit Mat4(const Mat4 &m) + { + const Source *src = m.asPointer(); + + for (int i=0; i<16; ++i) { + MyBase::mm[i] = static_cast(src[i]); + } + } + + /// Predefined constant for identity matrix + static const Mat4& identity() { + return sIdentity; + } + + /// Predefined constant for zero matrix + static const Mat4& zero() { + return sZero; + } + + /// Set ith row to vector v + void setRow(int i, const Vec4 &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 row(int i) const + { + // assert(i>=0 && i<3); + return Vec4((*this)(i,0), (*this)(i,1), (*this)(i,2), (*this)(i,3)); + } + + /// Set jth column to vector v + void setCol(int j, const Vec4& 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 col(int j) const + { + // assert(j>=0 && j<4); + return Vec4((*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 rows of "this" matrix to the vectors v1, v2, v3, v4 + void setRows(const Vec4 &v1, const Vec4 &v2, + const Vec4 &v3, const Vec4 &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 the columns of "this" matrix to the vectors v1, v2, v3, v4 + void setColumns(const Vec4 &v1, const Vec4 &v2, + const Vec4 &v3, const Vec4 &v4) + { + MyBase::mm[ 0] = v1[0]; + MyBase::mm[ 1] = v2[0]; + MyBase::mm[ 2] = v3[0]; + MyBase::mm[ 3] = v4[0]; + + MyBase::mm[ 4] = v1[1]; + MyBase::mm[ 5] = v2[1]; + MyBase::mm[ 6] = v3[1]; + MyBase::mm[ 7] = v4[1]; + + MyBase::mm[ 8] = v1[2]; + MyBase::mm[ 9] = v2[2]; + MyBase::mm[10] = v3[2]; + MyBase::mm[11] = v4[2]; + + MyBase::mm[12] = v1[3]; + MyBase::mm[13] = v2[3]; + MyBase::mm[14] = v3[3]; + MyBase::mm[15] = v4[3]; + } + + /// Set the rows of "this" matrix to the vectors v1, v2, v3, v4 + OPENVDB_DEPRECATED void setBasis(const Vec4 &v1, const Vec4 &v2, + const Vec4 &v3, const Vec4 &v4) + { + this->setRows(v1, v2, v3, v4); + } + + + // 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 &m) + { + for (int i = 0; i < 3; i++) + for (int j=0; j < 3; j++) + MyBase::mm[i*4+j] = m[i][j]; + } + + Mat3 getMat3() const + { + Mat3 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 getTranslation() const + { + return Vec3(MyBase::mm[12], MyBase::mm[13], MyBase::mm[14]); + } + + void setTranslation(const Vec3 &t) + { + MyBase::mm[12] = t[0]; + MyBase::mm[13] = t[1]; + MyBase::mm[14] = t[2]; + } + + /// Assignment operator + template + const Mat4& operator=(const Mat4 &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 operator-() const + { + return Mat4( + -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 + const Mat4& 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 + const Mat4 &operator+=(const Mat4 &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 + const Mat4 &operator-=(const Mat4 &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 + const Mat4 &operator*=(const Mat4 &m1) + { + Mat4 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(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(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(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(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( + 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& 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 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 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 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(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 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; + } + + /// 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 + void setToTranslation(const Vec3& 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 + void preTranslate(const Vec3& tr) + { + Vec3 tmp(tr.x(), tr.y(), tr.z()); + Mat4 Tr = Mat4::translation(tmp); + + *this = Tr * (*this); + + } + + /// Right multiplies by the specified translation matrix, i.e. (*this) * Trans + template + void postTranslate(const Vec3& tr) + { + Vec3 tmp(tr.x(), tr.y(), tr.z()); + Mat4 Tr = Mat4::translation(tmp); + + *this = (*this) * Tr; + + } + + + /// Sets the matrix to a matrix that scales by v + template + void setToScale(const Vec3& 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 + void preScale(const Vec3& 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 + void postScale(const Vec3& 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 >(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& axis, T angle) {*this = rotation >(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& v1, const Vec3& v2) {*this = rotation >(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(cos(angle)); + T s = -static_cast(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(cos(angle)); + T s = -static_cast(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 >(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(axis0); + int index1 = static_cast(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(axis0); + int index1 = static_cast(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 + Vec4 transform(const Vec4 &v) const + { + return static_cast< Vec4 >(v * *this); + } + + /// Transform a Vec3 by post-multiplication, without homogenous division. + template + Vec3 transform(const Vec3 &v) const + { + return static_cast< Vec3 >(v * *this); + } + + /// Transform a Vec4 by pre-multiplication. + template + Vec4 pretransform(const Vec4 &v) const + { + return static_cast< Vec4 >(*this * v); + } + + /// Transform a Vec3 by pre-multiplication, without homogenous division. + template + Vec3 pretransform(const Vec3 &v) const + { + return static_cast< Vec3 >(*this * v); + } + + /// Transform a Vec3 by post-multiplication, doing homogenous divison. + template + Vec3 transformH(const Vec3 &p) const + { + T0 w; + + // w = p * (*this).col(3); + w = static_cast(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(static_cast((p[0] * MyBase::mm[ 0] + p[1] * MyBase::mm[ 4] + + p[2] * MyBase::mm[ 8] + MyBase::mm[12]) / w), + static_cast((p[0] * MyBase::mm[ 1] + p[1] * MyBase::mm[ 5] + + p[2] * MyBase::mm[ 9] + MyBase::mm[13]) / w), + static_cast((p[0] * MyBase::mm[ 2] + p[1] * MyBase::mm[ 6] + + p[2] * MyBase::mm[10] + MyBase::mm[14]) / w)); + } + + return Vec3(0, 0, 0); + } + + /// Transform a Vec3 by pre-multiplication, doing homogenous division. + template + Vec3 pretransformH(const Vec3 &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(static_cast((p[0] * MyBase::mm[ 0] + p[1] * MyBase::mm[ 1] + + p[2] * MyBase::mm[ 2] + MyBase::mm[ 3]) / w), + static_cast((p[0] * MyBase::mm[ 4] + p[1] * MyBase::mm[ 5] + + p[2] * MyBase::mm[ 6] + MyBase::mm[ 7]) / w), + static_cast((p[0] * MyBase::mm[ 8] + p[1] * MyBase::mm[ 9] + + p[2] * MyBase::mm[10] + MyBase::mm[11]) / w)); + } + + return Vec3(0, 0, 0); + } + + /// Transform a Vec3 by post-multiplication, without translation. + template + Vec3 transform3x3(const Vec3 &v) const + { + return Vec3( + static_cast(v[0] * MyBase::mm[ 0] + v[1] * MyBase::mm[ 4] + v[2] * MyBase::mm[ 8]), + static_cast(v[0] * MyBase::mm[ 1] + v[1] * MyBase::mm[ 5] + v[2] * MyBase::mm[ 9]), + static_cast(v[0] * MyBase::mm[ 2] + v[1] * MyBase::mm[ 6] + v[2] * MyBase::mm[10])); + } + + +private: + bool invert(Mat4 &inverse, T tolerance) const; + + T det2(const Mat4 &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 &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 sIdentity; + static const Mat4 sZero; +}; // class Mat4 + + +template +const Mat4 Mat4::sIdentity = Mat4(1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + +template +const Mat4 Mat4::sZero = Mat4(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 +bool operator==(const Mat4 &m0, const Mat4 &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 +bool operator!=(const Mat4 &m0, const Mat4 &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 +Mat4::type> operator*(S scalar, const Mat4 &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 +Mat4::type> operator*(const Mat4 &m, S scalar) +{ + Mat4::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 +inline Vec4::type> +operator*(const Mat4 &_m, + const Vec4 &_v) +{ + MT const *m = _m.asPointer(); + return Vec4::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 +inline Vec4::type> +operator*(const Vec4 &_v, + const Mat4 &_m) +{ + MT const *m = _m.asPointer(); + return Vec4::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 +inline Vec3::type> +operator*(const Mat4 &_m, + const Vec3 &_v) +{ + MT const *m = _m.asPointer(); + return Vec3::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 +inline Vec3::type> +operator*(const Vec3 &_v, + const Mat4 &_m) +{ + MT const *m = _m.asPointer(); + return Vec3::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 +Mat4::type> +operator+(const Mat4 &m0, const Mat4 &m1) +{ + Mat4::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 +Mat4::type> +operator-(const Mat4 &m0, const Mat4 &m1) +{ + Mat4::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 +Mat4::type> +operator*(const Mat4 &m0, const Mat4 &m1) +{ + Mat4::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 +Vec3 transformNormal(const Mat4 &m, const Vec3 &n) +{ + return Vec3( + static_cast(m[0][0]*n[0] + m[0][1]*n[1] + m[0][2]*n[2]), + static_cast(m[1][0]*n[0] + m[1][1]*n[1] + m[1][2]*n[2]), + static_cast(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 +bool Mat4::invert(Mat4 &inverse, T tolerance) const +{ + Mat4 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 +inline bool isAffine(const Mat4& m) { + return (m.col(3) == Vec4(0, 0, 0, 1)); +} + +template +inline bool hasTranslation(const Mat4& m) { + return (m.row(3) != Vec4(0, 0, 0, 1)); +} + + +typedef Mat4 Mat4s; +typedef Mat4 Mat4d; + +#if DWREAL_IS_DOUBLE == 1 +typedef Mat4d Mat4f; +#else +typedef Mat4s Mat4f; +#endif // DWREAL_IS_DOUBLE + +} // namespace math + + +template<> inline math::Mat4s zeroVal() { return math::Mat4s::identity(); } +template<> inline math::Mat4d zeroVal() { return math::Mat4d::identity(); } + +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_UTIL_MAT4_H_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/math/Math.h b/nuparu/include/openvdb/include/openvdb/math/Math.h new file mode 100644 index 00000000..c2213295 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/math/Math.h @@ -0,0 +1,912 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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.h +/// @brief General-purpose arithmetic and comparison routines, most of which +/// accept arbitrary value types (or at least arbitrary numeric value types) + +#ifndef OPENVDB_MATH_HAS_BEEN_INCLUDED +#define OPENVDB_MATH_HAS_BEEN_INCLUDED + +#include +#include // for std::max() +#include // for floor(), ceil() and sqrt() +#include // for pow(), fabs() etc +#include // for srand(), abs(int) +#include // for std::numeric_limits::max() +#include +#include +#include +#include // boost::math::isfinite +#include // for boost::random::mt19937 +#include +#include +#include // for BOOST_VERSION +#include +#include + + +// Compile pragmas + +#define PRAGMA(x) _Pragma(#x) + +// 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)") +#elif defined(__clang__) + #define OPENVDB_NO_FP_EQUALITY_WARNING_BEGIN \ + PRAGMA(clang diagnostic push) \ + PRAGMA(clang diagnostic ignored "-Wfloat-equal") + #define OPENVDB_NO_FP_EQUALITY_WARNING_END \ + PRAGMA(clang diagnostic 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 { + +/// @brief Return the value of type T that corresponds to zero. +/// @note A zeroVal() specialization must be defined for each @c ValueType T +/// that cannot be constructed using the form @c T(0). For example, @c std::string(0) +/// treats 0 as @c NULL and throws a @c std::logic_error. +template inline T zeroVal() { return T(0); } +/// Return the @c std::string value that corresponds to zero. +template<> inline std::string zeroVal() { return ""; } +/// Return the @c bool value that corresponds to zero. +template<> inline bool zeroVal() { return false; } + +/// @todo These won't be needed if we eliminate StringGrids. +//@{ +/// @brief Needed to support the (zeroVal() + val) 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; } +//@} + + +namespace math { + +/// @brief Return the unary negation of the given value. +/// @note A negative() specialization must be defined for each ValueType T +/// for which unary negation is not defined. +template inline T negative(const T& val) { return T(-val); } +/// Return the negation of the given boolean. +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; } + + +//@{ +/// Tolerance for floating-point comparison +template struct Tolerance { static T value() { return zeroVal(); } }; +template<> struct Tolerance { static float value() { return 1e-8f; } }; +template<> struct Tolerance { static double value() { return 1e-15; } }; +//@} + +//@{ +/// Delta for small floating-point offsets +template struct Delta { static T value() { return zeroVal(); } }; +template<> struct Delta { static float value() { return 1e-5f; } }; +template<> struct Delta { static double value() { return 1e-9; } }; +//@} + + +// ==========> Random Values <================== + +/// @brief Simple generator of random numbers over the range [0, 1) +/// @details Thread-safe as long as each thread has its own Rand01 instance +template +class Rand01 +{ +private: + EngineType mEngine; + boost::uniform_01 mRand; + +public: + typedef FloatType ValueType; + + /// @brief Initialize the generator. + /// @param engine random number generator + Rand01(const EngineType& engine): mEngine(engine) {} + + /// @brief Initialize the generator. + /// @param seed seed value for the random number generator + Rand01(unsigned int seed): mEngine(static_cast(seed)) {} + + /// Set the seed value for the random number generator + void setSeed(unsigned int seed) + { + mEngine.seed(static_cast(seed)); + } + + /// Return a const reference to the random number generator. + const EngineType& engine() const { return mEngine; } + + /// Return a uniformly distributed random number in the range [0, 1). + FloatType operator()() { return mRand(mEngine); } +}; + +typedef Rand01 Random01; + + +/// @brief Simple random integer generator +/// @details Thread-safe as long as each thread has its own RandInt instance +template +class RandInt +{ +private: +#if BOOST_VERSION >= 104700 + typedef boost::random::uniform_int_distribution Distr; +#else + typedef boost::uniform_int Distr; +#endif + EngineType mEngine; + Distr mRand; + +public: + /// @brief Initialize the generator. + /// @param engine random number generator + /// @param imin,imax generate integers that are uniformly distributed over [imin, imax] + RandInt(const EngineType& engine, IntType imin, IntType imax): + mEngine(engine), + mRand(std::min(imin, imax), std::max(imin, imax)) + {} + + /// @brief Initialize the generator. + /// @param seed seed value for the random number generator + /// @param imin,imax generate integers that are uniformly distributed over [imin, imax] + RandInt(unsigned int seed, IntType imin, IntType imax): + mEngine(static_cast(seed)), + mRand(std::min(imin, imax), std::max(imin, imax)) + {} + + /// Change the range over which integers are distributed to [imin, imax]. + void setRange(IntType imin, IntType imax) + { + mRand = Distr(std::min(imin, imax), std::max(imin, imax)); + } + + /// Set the seed value for the random number generator + void setSeed(unsigned int seed) + { + mEngine.seed(static_cast(seed)); + } + + /// Return a const reference to the random number generator. + const EngineType& engine() const { return mEngine; } + + /// Return a randomly-generated integer in the current range. + IntType operator()() { return mRand(mEngine); } + + /// @brief Return a randomly-generated integer in the new range [imin, imax], + /// without changing the current range. + IntType operator()(IntType imin, IntType imax) + { + const IntType lo = std::min(imin, imax), hi = std::max(imin, imax); +#if BOOST_VERSION >= 104700 + return mRand(mEngine, typename Distr::param_type(lo, hi)); +#else + return Distr(lo, hi)(mEngine); +#endif + } +}; + +typedef RandInt RandomInt; + + +// ==========> Clamp <================== + +/// Return @a x clamped to [@a min, @a max] +template +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 +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 +inline bool +ClampTest01(Type &x) +{ + if (x >= Type(0) && x <= Type(1)) return false; + x = x < Type(0) ? Type(0) : Type(1); + return true; +} + +/// @brief Return 0 if @a x < @a 0, 1 if @a x > 1 or else @f$(3-2x)x^2@f$. +template +inline Type +SmoothUnitStep(Type x) +{ + return x > 0 ? x < 1 ? (3-2*x)*x*x : Type(1) : Type(0); +} + +/// @brief Return 0 if @a x < @a min, 1 if @a x > @a max or else @f$(3-2t)t^2@f$, +/// where @f$t = (x-min)/(max-min)@f$. +template +inline Type +SmoothUnitStep(Type x, Type min, Type max) +{ + assert(min < max); + return SmoothUnitStep((x-min)/(max-min)); +} + + +// ==========> Absolute Value <================== + + +//@{ +/// Return the absolute value of the given quantity. +inline int32_t Abs(int32_t i) { return abs(i); } +inline int64_t Abs(int64_t i) +{ +#ifdef _MSC_VER + return (i < int64_t(0) ? -i : i); +#else + return labs(i); +#endif +} +inline float Abs(float x) { return fabsf(x); } +inline double Abs(double x) { return fabs(x); } +inline long double Abs(long double x) { return fabsl(x); } +inline uint32_t Abs(uint32_t i) { return i; } +inline uint64_t Abs(uint64_t i) { return i; } +inline bool Abs(bool b) { return b; } +// On OSX size_t and uint64_t are different types +#if defined(__APPLE__) || defined(MACOSX) +inline size_t Abs(size_t i) { return i; } +#endif +//@} + + +//////////////////////////////////////// + + +// ==========> Value Comparison <================== + + +/// Return @c true if @a x is exactly equal to zero. +template +inline bool +isZero(const Type& x) +{ + OPENVDB_NO_FP_EQUALITY_WARNING_BEGIN + return x == zeroVal(); + OPENVDB_NO_FP_EQUALITY_WARNING_END +} + + +/// @brief Return @c true if @a x is equal to zero to within +/// the default floating-point comparison tolerance. +template +inline bool +isApproxZero(const Type& x) +{ + const Type tolerance = Type(zeroVal() + Tolerance::value()); + return !(x > tolerance) && !(x < -tolerance); +} + +/// Return @c true if @a x is equal to zero to within the given tolerance. +template +inline bool +isApproxZero(const Type& x, const Type& tolerance) +{ + return !(x > tolerance) && !(x < -tolerance); +} + + +/// Return @c true if @a x is less than zero. +template +inline bool +isNegative(const Type& x) { return x < zeroVal(); } + +/// Return @c false, since @c bool values are never less than zero. +template<> inline bool isNegative(const bool&) { return false; } + + +/// Return @c true if @a x is finite. +template +inline bool +isFinite(const Type& x) { return boost::math::isfinite(x); } + + +/// @brief Return @c true if @a a is equal to @a b to within +/// the default floating-point comparison tolerance. +template +inline bool +isApproxEqual(const Type& a, const Type& b) +{ + const Type tolerance = Type(zeroVal() + Tolerance::value()); + return !(Abs(a - b) > tolerance); +} + + +/// Return @c true if @a a is equal to @a b to within the given tolerance. +template +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(const T& a, const T& b) { return a == b; } \ + template<> inline bool isApproxEqual(const T& a, const T& b, const T&) { return a == b; } \ + /**/ + +OPENVDB_EXACT_IS_APPROX_EQUAL(bool) +OPENVDB_EXACT_IS_APPROX_EQUAL(std::string) + + +/// @brief Return @c true if @a a is larger than @a b to within +/// the given tolerance, i.e., if @a b - @a a < @a tolerance. +template +inline bool +isApproxLarger(const Type& a, const Type& b, const Type& tolerance) +{ + return (b - a < tolerance); +} + + +/// @brief Return @c true if @a a is exactly equal to @a b. +template +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 +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(&aFloatValue); + return foi->int32Value; +} + + +inline int64_t +doubleToInt64(const double aDoubleValue) +{ + union DoubleOrInt64 { double doubleValue; int64_t int64Value; }; + const DoubleOrInt64* dol = reinterpret_cast(&aDoubleValue); + return dol->int64Value; +} + + +// aUnitsInLastPlace is the allowed difference between the least significant digits +// of the numbers' floating point representation +// Please read the reference 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 @f$ x^2 @f$. +template +inline Type Pow2(Type x) { return x*x; } + +/// Return @f$ x^3 @f$. +template +inline Type Pow3(Type x) { return x*x*x; } + +/// Return @f$ x^4 @f$. +template +inline Type Pow4(Type x) { return Pow2(Pow2(x)); } + +/// Return @f$ x^n @f$. +template +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 @f$ b^e @f$. +inline float +Pow(float b, float e) +{ + assert( b >= 0.0f && "Pow(float,float): base is negative" ); + return powf(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 +inline const Type& +Max(const Type& a, const Type& b) +{ + return std::max(a,b) ; +} + +/// Return the maximum of three values +template +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 +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 +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 +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 +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 +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 +inline const Type& +Min(const Type& a, const Type& b) { return std::min(a, b); } + +/// Return the minimum of three values +template +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 +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 +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 +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 +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 +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)); +} + + +// ============> Exp <================== + +/// Return @f$ e^x @f$. +template +inline Type Exp(const Type& x) { return std::exp(x); } + + +//////////////////////////////////////// + + +/// Return the sign of the given value as an integer (either -1, 0 or 1). +template +inline int Sign(const Type &x) { return (zeroVal() < x) - (x < zeroVal()); } + + +/// @brief Return @c true if @a a and @a b have different signs. +/// @note Zero is considered a positive number. +template +inline bool +SignChange(const Type& a, const Type& b) +{ + return ( (a()) ^ (b()) ); +} + + +/// @brief Return @c true if the interval [@a a, @a b] includes zero, +/// i.e., if either @a a or @a b is zero or if they have different signs. +template +inline bool +ZeroCrossing(const Type& a, const Type& b) +{ + return a * b <= zeroVal(); +} + + +//@{ +/// Return the square root of a floating-point value. +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 the cube root of a floating-point value. +inline float Cbrt(float x) { return boost::math::cbrt(x); } +inline double Cbrt(double x) { return boost::math::cbrt(x); } +inline long double Cbrt(long double x) { return boost::math::cbrt(x); } +//@} + + +//@{ +/// Return the remainder of @a x / @a y. +inline int Mod(int x, int y) { return (x % y); } +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); } +template inline Type Remainder(Type x, Type y) { return Mod(x,y); } +//@} + + +//@{ +/// Return @a x rounded up to the nearest integer. +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); } +//@} +/// Return @a x rounded up to the nearest multiple of @a base. +template +inline Type +RoundUp(Type x, Type base) +{ + Type remainder = Remainder(x, base); + return remainder ? x-remainder+base : x; +} + + +//@{ +/// Return @a x rounded down to the nearest integer. +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); } +//@} +/// Return @a x rounded down to the nearest multiple of @a base. +template +inline Type +RoundDown(Type x, Type base) +{ + Type remainder = Remainder(x, base); + return remainder ? x-remainder : x; +} + + +//@{ +/// Return @a x rounded to the nearest integer. +inline float Round(float x) { return RoundDown(x + 0.5f); } +inline double Round(double x) { return RoundDown(x + 0.5); } +inline long double Round(long double x) { return RoundDown(x + 0.5l); } +//@} + + +/// Return the euclidean remainder of @a x. +/// Note unlike % operator this will always return a positive result +template +inline Type +EuclideanRemainder(Type x) { return x - RoundDown(x); } + + +/// Return the integer part of @a x. +template +inline Type +IntegerPart(Type x) +{ + return (x > 0 ? RoundDown(x) : RoundUp(x)); +} + +/// Return the fractional part of @a x. +template +inline Type +FractionalPart(Type x) { return Mod(x,Type(1)); } + + +//@{ +/// Return the floor of @a x. +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 the ceiling of @a x. +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 @a x if it is greater or equal in magnitude than @a delta. Otherwise, return zero. +template +inline Type Chop(Type x, Type delta) { return (Abs(x) < delta ? zeroVal() : x); } + + +/// Return @a x truncated to the given number of decimal digits. +template +inline Type +Truncate(Type x, unsigned int digits) +{ + Type tenth = Pow(10,digits); + return RoundDown(x*tenth+0.5)/tenth; +} + + +//////////////////////////////////////// + + +/// Return the inverse of @a x. +template +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 +struct promote { + typedef typename boost::numeric::conversion_traits::supertype type; +}; + + +/// @brief Return the index [0,1,2] of the smallest value in a 3D vector. +/// @note This methods assumes operator[] exists and avoids branching. +/// @details If two components of the input vector are equal and smaller than the +/// third component, the largest index of the two is always returned. +/// If all three vector components are equal the largest index, i.e. 2, is +/// returned. In other words the return value corresponds to the largest index +/// of the of the smallest vector components. +template +size_t +MinIndex(const Vec3T& v) +{ +#ifndef _MSC_VER // Visual C++ doesn't guarantee thread-safe initialization of local statics + static +#endif + const size_t hashTable[8] = { 2, 1, 9, 1, 2, 9, 0, 0 };//9 is a dummy value + const size_t hashKey = + ((v[0] < v[1]) << 2) + ((v[0] < v[2]) << 1) + (v[1] < v[2]);// ?*4+?*2+?*1 + return hashTable[hashKey]; +} + + +/// @brief Return the index [0,1,2] of the largest value in a 3D vector. +/// @note This methods assumes operator[] exists and avoids branching. +/// @details If two components of the input vector are equal and larger than the +/// third component, the largest index of the two is always returned. +/// If all three vector components are equal the largest index, i.e. 2, is +/// returned. In other words the return value corresponds to the largest index +/// of the largest vector components. +template +size_t +MaxIndex(const Vec3T& v) +{ +#ifndef _MSC_VER // Visual C++ doesn't guarantee thread-safe initialization of local statics + static +#endif + const size_t hashTable[8] = { 2, 1, 9, 1, 2, 9, 0, 0 };//9 is a dummy value + const size_t hashKey = + ((v[0] > v[1]) << 2) + ((v[0] > v[2]) << 1) + (v[1] > v[2]);// ?*4+?*2+?*1 + return hashTable[hashKey]; +} + +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_MATH_MATH_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/math/Operators.h b/nuparu/include/openvdb/include/openvdb/math/Operators.h new file mode 100644 index 00000000..61aa4bc0 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/math/Operators.h @@ -0,0 +1,2123 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 struct is_vec3d { static const bool value = false; }; +template<> struct is_vec3d { static const bool value = true; }; + +template struct is_double { static const bool value = false; }; +template<> struct is_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 +struct MapAdapter { + MapAdapter(const MapType& m): map(m) {} + + template + inline ResultType + result(const AccessorType& grid, const Coord& ijk) { return OpType::result(map, grid, ijk); } + + template + 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 +struct ISOpMagnitude { + template + static inline double result(const AccessorType& grid, const Coord& ijk) { + return double(OpType::result(grid, ijk).length()); + } + + template + 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 +struct OpMagnitude { + template + static inline double result(const MapT& map, const AccessorType& grid, const Coord& ijk) { + return double(OpType::result(map, grid, ijk).length()); + } + + template + 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 +struct ReturnValue { + typedef typename T::ValueType ValueType; + typedef math::Vec3 Vec3Type; +}; + +} // namespace internal + +// ---- Operators defined in index space + + +//@{ +/// @brief Gradient operators defined in index space of various orders +template +struct ISGradient +{ + // random access version + template static Vec3 + result(const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType ValueType; + typedef Vec3 Vec3Type; + return Vec3Type( D1::inX(grid, ijk), + D1::inY(grid, ijk), + D1::inZ(grid, ijk) ); + } + + // stencil access version + template static Vec3 + result(const StencilT& stencil) + { + typedef typename StencilT::ValueType ValueType; + typedef Vec3 Vec3Type; + return Vec3Type( D1::inX(stencil), + D1::inY(stencil), + D1::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 +struct BIAS_SCHEME { + static const DScheme FD = FD_1ST; + static const DScheme BD = BD_1ST; + + template + struct ISStencil { + typedef SevenPointStencil StencilType; + }; +}; + +template<> struct BIAS_SCHEME +{ + static const DScheme FD = FD_1ST; + static const DScheme BD = BD_1ST; + + template + struct ISStencil { + typedef SevenPointStencil StencilType; + }; +}; + +template<> struct BIAS_SCHEME +{ + static const DScheme FD = FD_2ND; + static const DScheme BD = BD_2ND; + + template + struct ISStencil { + typedef ThirteenPointStencil StencilType; + }; +}; +template<> struct BIAS_SCHEME +{ + static const DScheme FD = FD_3RD; + static const DScheme BD = BD_3RD; + + template + struct ISStencil { + typedef NineteenPointStencil StencilType; + }; +}; +template<> struct BIAS_SCHEME +{ + static const DScheme FD = FD_WENO5; + static const DScheme BD = BD_WENO5; + + template + struct ISStencil { + typedef NineteenPointStencil StencilType; + }; +}; +template<> struct BIAS_SCHEME +{ + static const DScheme FD = FD_HJWENO5; + static const DScheme BD = BD_HJWENO5; + + template + struct ISStencil { + typedef NineteenPointStencil StencilType; + }; +}; + + +//@{ +/// @brief Biased Gradient Operators, using upwinding defined by the @c Vec3Bias input + +template +struct ISGradientBiased +{ + static const DScheme FD = BIAS_SCHEME::FD; + static const DScheme BD = BIAS_SCHEME::BD; + + // random access version + template + static Vec3 + result(const Accessor& grid, const Coord& ijk, const Vec3Bias& V) + { + typedef typename Accessor::ValueType ValueType; + typedef Vec3 Vec3Type; + + return Vec3Type(V[0]<0 ? D1::inX(grid,ijk) : D1::inX(grid,ijk), + V[1]<0 ? D1::inY(grid,ijk) : D1::inY(grid,ijk), + V[2]<0 ? D1::inZ(grid,ijk) : D1::inZ(grid,ijk) ); + } + + // stencil access version + template + static Vec3 + result(const StencilT& stencil, const Vec3Bias& V) + { + typedef typename StencilT::ValueType ValueType; + typedef Vec3 Vec3Type; + + return Vec3Type(V[0]<0 ? D1::inX(stencil) : D1::inX(stencil), + V[1]<0 ? D1::inY(stencil) : D1::inY(stencil), + V[2]<0 ? D1::inZ(stencil) : D1::inZ(stencil) ); + } +}; + + +template +struct ISGradientNormSqrd +{ + static const DScheme FD = BIAS_SCHEME::FD; + static const DScheme BD = BIAS_SCHEME::BD; + + + // random access version + template + static typename Accessor::ValueType + result(const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType ValueType; + typedef math::Vec3 Vec3Type; + + Vec3Type up = ISGradient::result(grid, ijk); + Vec3Type down = ISGradient::result(grid, ijk); + return math::GodunovsNormSqrd(grid.getValue(ijk)>0, down, up); + } + + // stencil access version + template + static typename StencilT::ValueType + result(const StencilT& stencil) + { + typedef typename StencilT::ValueType ValueType; + typedef math::Vec3 Vec3Type; + + Vec3Type up = ISGradient::result(stencil); + Vec3Type down = ISGradient::result(stencil); + return math::GodunovsNormSqrd(stencil.template getValue<0, 0, 0>()>0, down, up); + } +}; + +#ifdef DWA_OPENVDB // for SIMD - note will do the computations in float +template<> +struct ISGradientNormSqrd +{ + // random access version + template + static typename Accessor::ValueType result(const Accessor& grid, const Coord& ijk) + { + struct GetValue + { + const Accessor& acc; + GetValue(const Accessor& acc_): acc(acc_) {} + // Return the grid value at ijk converted to simd::Float4::value_type (= float). + inline simd::Float4::value_type operator()(const Coord& ijk_) { + return static_cast(acc.getValue(ijk_)); + } + } + valueAt(grid); + + // SSE optimized + const simd::Float4 + v1(valueAt(ijk.offsetBy(-2, 0, 0)) - valueAt(ijk.offsetBy(-3, 0, 0)), + valueAt(ijk.offsetBy( 0,-2, 0)) - valueAt(ijk.offsetBy( 0,-3, 0)), + valueAt(ijk.offsetBy( 0, 0,-2)) - valueAt(ijk.offsetBy( 0, 0,-3)), 0), + v2(valueAt(ijk.offsetBy(-1, 0, 0)) - valueAt(ijk.offsetBy(-2, 0, 0)), + valueAt(ijk.offsetBy( 0,-1, 0)) - valueAt(ijk.offsetBy( 0,-2, 0)), + valueAt(ijk.offsetBy( 0, 0,-1)) - valueAt(ijk.offsetBy( 0, 0,-2)), 0), + v3(valueAt(ijk ) - valueAt(ijk.offsetBy(-1, 0, 0)), + valueAt(ijk ) - valueAt(ijk.offsetBy( 0,-1, 0)), + valueAt(ijk ) - valueAt(ijk.offsetBy( 0, 0,-1)), 0), + v4(valueAt(ijk.offsetBy( 1, 0, 0)) - valueAt(ijk ), + valueAt(ijk.offsetBy( 0, 1, 0)) - valueAt(ijk ), + valueAt(ijk.offsetBy( 0, 0, 1)) - valueAt(ijk ), 0), + v5(valueAt(ijk.offsetBy( 2, 0, 0)) - valueAt(ijk.offsetBy( 1, 0, 0)), + valueAt(ijk.offsetBy( 0, 2, 0)) - valueAt(ijk.offsetBy( 0, 1, 0)), + valueAt(ijk.offsetBy( 0, 0, 2)) - valueAt(ijk.offsetBy( 0, 0, 1)), 0), + v6(valueAt(ijk.offsetBy( 3, 0, 0)) - valueAt(ijk.offsetBy( 2, 0, 0)), + valueAt(ijk.offsetBy( 0, 3, 0)) - valueAt(ijk.offsetBy( 0, 2, 0)), + valueAt(ijk.offsetBy( 0, 0, 3)) - valueAt(ijk.offsetBy( 0, 0, 2)), 0), + down = math::WENO5(v1, v2, v3, v4, v5), + up = math::WENO5(v6, v5, v4, v3, v2); + + return math::GodunovsNormSqrd(grid.getValue(ijk)>0, down, up); + } + + // stencil access version + template + static typename StencilT::ValueType result(const StencilT& s) + { + typedef simd::Float4::value_type F4Val; + + // SSE optimized + const simd::Float4 + v1(F4Val(s.template getValue<-2, 0, 0>()) - F4Val(s.template getValue<-3, 0, 0>()), + F4Val(s.template getValue< 0,-2, 0>()) - F4Val(s.template getValue< 0,-3, 0>()), + F4Val(s.template getValue< 0, 0,-2>()) - F4Val(s.template getValue< 0, 0,-3>()), 0), + v2(F4Val(s.template getValue<-1, 0, 0>()) - F4Val(s.template getValue<-2, 0, 0>()), + F4Val(s.template getValue< 0,-1, 0>()) - F4Val(s.template getValue< 0,-2, 0>()), + F4Val(s.template getValue< 0, 0,-1>()) - F4Val(s.template getValue< 0, 0,-2>()), 0), + v3(F4Val(s.template getValue< 0, 0, 0>()) - F4Val(s.template getValue<-1, 0, 0>()), + F4Val(s.template getValue< 0, 0, 0>()) - F4Val(s.template getValue< 0,-1, 0>()), + F4Val(s.template getValue< 0, 0, 0>()) - F4Val(s.template getValue< 0, 0,-1>()), 0), + v4(F4Val(s.template getValue< 1, 0, 0>()) - F4Val(s.template getValue< 0, 0, 0>()), + F4Val(s.template getValue< 0, 1, 0>()) - F4Val(s.template getValue< 0, 0, 0>()), + F4Val(s.template getValue< 0, 0, 1>()) - F4Val(s.template getValue< 0, 0, 0>()), 0), + v5(F4Val(s.template getValue< 2, 0, 0>()) - F4Val(s.template getValue< 1, 0, 0>()), + F4Val(s.template getValue< 0, 2, 0>()) - F4Val(s.template getValue< 0, 1, 0>()), + F4Val(s.template getValue< 0, 0, 2>()) - F4Val(s.template getValue< 0, 0, 1>()), 0), + v6(F4Val(s.template getValue< 3, 0, 0>()) - F4Val(s.template getValue< 2, 0, 0>()), + F4Val(s.template getValue< 0, 3, 0>()) - F4Val(s.template getValue< 0, 2, 0>()), + F4Val(s.template getValue< 0, 0, 3>()) - F4Val(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::GodunovsNormSqrd(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 +struct ISLaplacian +{ + // random access version + template + static typename Accessor::ValueType result(const Accessor& grid, const Coord& ijk); + + // stencil access version + template + static typename StencilT::ValueType result(const StencilT& stencil); +}; + + +template<> +struct ISLaplacian +{ + // random access version + template + 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 + 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 +{ + // random access version + template + static typename Accessor::ValueType result(const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType ValueT; + return static_cast( + (-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 + static typename StencilT::ValueType result(const StencilT& stencil) + { + typedef typename StencilT::ValueType ValueT; + return static_cast( + (-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 +{ + // random access version + template + static typename Accessor::ValueType result(const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType ValueT; + return static_cast( + (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 + static typename StencilT::ValueType result(const StencilT& stencil) + { + typedef typename StencilT::ValueType ValueT; + return static_cast( + (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 +struct ISDivergence +{ + // random access version + template static typename Accessor::ValueType::value_type + result(const Accessor& grid, const Coord& ijk) + { + return D1Vec::inX(grid, ijk, 0) + + D1Vec::inY(grid, ijk, 1) + + D1Vec::inZ(grid, ijk, 2); + } + + // stencil access version + template static typename StencilT::ValueType::value_type + result(const StencilT& stencil) + { + return D1Vec::inX(stencil, 0) + + D1Vec::inY(stencil, 1) + + D1Vec::inZ(stencil, 2); + } +}; +//@} + + +//@{ +/// Curl operator defined in index space using various first derivative schemes +template +struct ISCurl +{ + // random access version + template + static typename Accessor::ValueType result(const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType Vec3Type; + return Vec3Type( D1Vec::inY(grid, ijk, 2) - //dw/dy - dv/dz + D1Vec::inZ(grid, ijk, 1), + D1Vec::inZ(grid, ijk, 0) - //du/dz - dw/dx + D1Vec::inX(grid, ijk, 2), + D1Vec::inX(grid, ijk, 1) - //dv/dx - du/dy + D1Vec::inY(grid, ijk, 0) ); + } + + // stencil access version + template + static typename StencilT::ValueType result(const StencilT& stencil) + { + typedef typename StencilT::ValueType Vec3Type; + return Vec3Type( D1Vec::inY(stencil, 2) - //dw/dy - dv/dz + D1Vec::inZ(stencil, 1), + D1Vec::inZ(stencil, 0) - //du/dz - dw/dx + D1Vec::inX(stencil, 2), + D1Vec::inX(stencil, 1) - //dv/dx - du/dy + D1Vec::inY(stencil, 0) ); + } +}; +//@} + + +//@{ +/// Compute the mean curvature in index space +template +struct ISMeanCurvature +{ + /// @brief random access version + /// @return true if the gradient is none-zero, in which case the + /// mean curvature is computed as 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 + static bool result(const Accessor& grid, const Coord& ijk, + typename Accessor::ValueType& alpha, + typename Accessor::ValueType& beta) + { + typedef typename Accessor::ValueType ValueType; + + const ValueType Dx = D1::inX(grid, ijk); + const ValueType Dy = D1::inY(grid, ijk); + const ValueType Dz = D1::inZ(grid, ijk); + + const ValueType Dx2 = Dx*Dx; + const ValueType Dy2 = Dy*Dy; + const ValueType Dz2 = Dz*Dz; + const ValueType normGrad = Dx2 + Dy2 + Dz2; + if (normGrad <= math::Tolerance::value()) { + alpha = beta = 0; + return false; + } + + const ValueType Dxx = D2::inX(grid, ijk); + const ValueType Dyy = D2::inY(grid, ijk); + const ValueType Dzz = D2::inZ(grid, ijk); + + const ValueType Dxy = D2::inXandY(grid, ijk); + const ValueType Dyz = D2::inYandZ(grid, ijk); + const ValueType Dxz = D2::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(normGrad))); // * 1/dx + return true; + } + + /// @brief stencil access version + /// @return true if the gradient is none-zero, in which case the + /// mean curvature is computed as 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 + static bool result(const StencilT& stencil, + typename StencilT::ValueType& alpha, + typename StencilT::ValueType& beta) + { + typedef typename StencilT::ValueType ValueType; + const ValueType Dx = D1::inX(stencil); + const ValueType Dy = D1::inY(stencil); + const ValueType Dz = D1::inZ(stencil); + + const ValueType Dx2 = Dx*Dx; + const ValueType Dy2 = Dy*Dy; + const ValueType Dz2 = Dz*Dz; + const ValueType normGrad = Dx2 + Dy2 + Dz2; + if (normGrad <= math::Tolerance::value()) { + alpha = beta = 0; + return false; + } + + const ValueType Dxx = D2::inX(stencil); + const ValueType Dyy = D2::inY(stencil); + const ValueType Dzz = D2::inZ(stencil); + + const ValueType Dxy = D2::inXandY(stencil); + const ValueType Dyz = D2::inYandZ(stencil); + const ValueType Dxz = D2::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(normGrad))); // * 1/dx + return true; + } +}; + +//////////////////////////////////////////////////////// + +// --- 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 +struct Gradient +{ + // random access version + template + static typename internal::ReturnValue::Vec3Type + result(const MapType& map, const Accessor& grid, const Coord& ijk) + { + typedef typename internal::ReturnValue::Vec3Type Vec3Type; + + Vec3d iGradient( ISGradient::result(grid, ijk) ); + return Vec3Type(map.applyIJT(iGradient, ijk.asVec3d())); + } + + // stencil access version + template + static typename internal::ReturnValue::Vec3Type + result(const MapType& map, const StencilT& stencil) + { + typedef typename internal::ReturnValue::Vec3Type Vec3Type; + + Vec3d iGradient( ISGradient::result(stencil) ); + return Vec3Type(map.applyIJT(iGradient, stencil.getCenterCoord().asVec3d())); + } +}; + +// Partial template specialization of Gradient +// translation, any order +template +struct Gradient +{ + // random access version + template + static typename internal::ReturnValue::Vec3Type + result(const TranslationMap&, const Accessor& grid, const Coord& ijk) + { + return ISGradient::result(grid, ijk); + } + + // stencil access version + template + static typename internal::ReturnValue::Vec3Type + result(const TranslationMap&, const StencilT& stencil) + { + return ISGradient::result(stencil); + } +}; + +/// Full template specialization of Gradient +/// uniform scale, 2nd order +template<> +struct Gradient +{ + // random access version + template + static typename internal::ReturnValue::Vec3Type + result(const UniformScaleMap& map, const Accessor& grid, const Coord& ijk) + { + typedef typename internal::ReturnValue::ValueType ValueType; + typedef typename internal::ReturnValue::Vec3Type Vec3Type; + + Vec3Type iGradient( ISGradient::result(grid, ijk) ); + ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); + return iGradient * inv2dx; + } + + // stencil access version + template + static typename internal::ReturnValue::Vec3Type + result(const UniformScaleMap& map, const StencilT& stencil) + { + typedef typename internal::ReturnValue::ValueType ValueType; + typedef typename internal::ReturnValue::Vec3Type Vec3Type; + + Vec3Type iGradient( ISGradient::result(stencil) ); + ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); + return iGradient * inv2dx; + } +}; + +/// Full template specialization of Gradient +/// uniform scale translate, 2nd order +template<> +struct Gradient +{ + // random access version + template + static typename internal::ReturnValue::Vec3Type + result(const UniformScaleTranslateMap& map, const Accessor& grid, const Coord& ijk) + { + typedef typename internal::ReturnValue::ValueType ValueType; + typedef typename internal::ReturnValue::Vec3Type Vec3Type; + + Vec3Type iGradient( ISGradient::result(grid, ijk) ); + ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); + return iGradient * inv2dx; + } + + // stencil access version + template + static typename internal::ReturnValue::Vec3Type + result(const UniformScaleTranslateMap& map, const StencilT& stencil) + { + typedef typename internal::ReturnValue::ValueType ValueType; + typedef typename internal::ReturnValue::Vec3Type Vec3Type; + + Vec3Type iGradient( ISGradient::result(stencil) ); + ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); + return iGradient * inv2dx; + } +}; + +/// Full template specialization of Gradient +/// scale, 2nd order +template<> +struct Gradient +{ + // random access version + template + static typename internal::ReturnValue::Vec3Type + result(const ScaleMap& map, const Accessor& grid, const Coord& ijk) + { + typedef typename internal::ReturnValue::ValueType ValueType; + typedef typename internal::ReturnValue::Vec3Type Vec3Type; + + Vec3Type iGradient( ISGradient::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 + static typename internal::ReturnValue::Vec3Type + result(const ScaleMap& map, const StencilT& stencil) + { + typedef typename internal::ReturnValue::ValueType ValueType; + typedef typename internal::ReturnValue::Vec3Type Vec3Type; + + Vec3Type iGradient( ISGradient::result(stencil) ); + return Vec3Type(ValueType(iGradient[0] * map.getInvTwiceScale()[0]), + ValueType(iGradient[1] * map.getInvTwiceScale()[1]), + ValueType(iGradient[2] * map.getInvTwiceScale()[2]) ); + } +}; + +/// Full template specialization of Gradient +/// scale translate, 2nd order +template<> +struct Gradient +{ + // random access version + template + static typename internal::ReturnValue::Vec3Type + result(const ScaleTranslateMap& map, const Accessor& grid, const Coord& ijk) + { + typedef typename internal::ReturnValue::ValueType ValueType; + typedef typename internal::ReturnValue::Vec3Type Vec3Type; + + Vec3Type iGradient( ISGradient::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 + static typename internal::ReturnValue::Vec3Type + result(const ScaleTranslateMap& map, const StencilT& stencil) + { + typedef typename internal::ReturnValue::ValueType ValueType; + typedef typename internal::ReturnValue::Vec3Type Vec3Type; + + Vec3Type iGradient( ISGradient::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 +struct GradientBiased +{ + // random access version + template static math::Vec3 + result(const MapType& map, const Accessor& grid, const Coord& ijk, + const Vec3& V) + { + typedef typename Accessor::ValueType ValueType; + typedef math::Vec3 Vec3Type; + + Vec3d iGradient( ISGradientBiased::result(grid, ijk, V) ); + return Vec3Type(map.applyIJT(iGradient, ijk.asVec3d())); + } + + // stencil access version + template static math::Vec3 + result(const MapType& map, const StencilT& stencil, + const Vec3& V) + { + typedef typename StencilT::ValueType ValueType; + typedef math::Vec3 Vec3Type; + + Vec3d iGradient( ISGradientBiased::result(stencil, V) ); + return Vec3Type(map.applyIJT(iGradient, stencil.getCenterCoord().asVec3d())); + } +}; +//@} + + +//////////////////////////////////////////////////////// + +// Computes |Grad[Phi]| using upwinding +template +struct GradientNormSqrd +{ + static const DScheme FD = BIAS_SCHEME::FD; + static const DScheme BD = BIAS_SCHEME::BD; + + + // random access version + template + static typename Accessor::ValueType + result(const MapType& map, const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType ValueType; + typedef math::Vec3 Vec3Type; + + Vec3Type up = Gradient::result(map, grid, ijk); + Vec3Type down = Gradient::result(map, grid, ijk); + return math::GodunovsNormSqrd(grid.getValue(ijk)>0, down, up); + } + + // stencil access version + template + static typename StencilT::ValueType + result(const MapType& map, const StencilT& stencil) + { + typedef typename StencilT::ValueType ValueType; + typedef math::Vec3 Vec3Type; + + Vec3Type up = Gradient::result(map, stencil); + Vec3Type down = Gradient::result(map, stencil); + return math::GodunovsNormSqrd(stencil.template getValue<0, 0, 0>()>0, down, up); + } +}; + +/// Partial template specialization of GradientNormSqrd +template +struct GradientNormSqrd +{ + // random access version + template + 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::result(grid, ijk); + } + + // stencil access version + template + 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::result(stencil); + } +}; + +/// Partial template specialization of GradientNormSqrd +template +struct GradientNormSqrd +{ + // random access version + template + 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::result(grid, ijk); + } + + // stencil access version + template + 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::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 +struct Divergence +{ + // random access version + template static typename Accessor::ValueType::value_type + result(const MapType& map, const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType::value_type ValueType; + + ValueType div(0); + for (int i=0; i < 3; i++) { + Vec3d vec( D1Vec::inX(grid, ijk, i), + D1Vec::inY(grid, ijk, i), + D1Vec::inZ(grid, ijk, i) ); + div += ValueType(map.applyIJT(vec, ijk.asVec3d())[i]); + } + return div; + } + + // stencil access version + template static typename StencilT::ValueType::value_type + result(const MapType& map, const StencilT& stencil) + { + typedef typename StencilT::ValueType::value_type ValueType; + + ValueType div(0); + for (int i=0; i < 3; i++) { + Vec3d vec( D1Vec::inX(stencil, i), + D1Vec::inY(stencil, i), + D1Vec::inZ(stencil, i) ); + div += ValueType(map.applyIJT(vec, stencil.getCenterCoord().asVec3d())[i]); + } + return div; + } +}; + +/// Partial template specialization of Divergence +/// translation, any scheme +template +struct Divergence +{ + // random access version + template static typename Accessor::ValueType::value_type + result(const TranslationMap&, const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType::value_type ValueType; + + ValueType div(0); + div =ISDivergence::result(grid, ijk); + return div; + } + + // stencil access version + template static typename StencilT::ValueType::value_type + result(const TranslationMap&, const StencilT& stencil) + { + typedef typename StencilT::ValueType::value_type ValueType; + + ValueType div(0); + div =ISDivergence::result(stencil); + return div; + } +}; + +/// Partial template specialization of Divergence +/// uniform scale, any scheme +template +struct Divergence +{ + // random access version + template static typename Accessor::ValueType::value_type + result(const UniformScaleMap& map, const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType::value_type ValueType; + + ValueType div(0); + + div =ISDivergence::result(grid, ijk); + ValueType invdx = ValueType(map.getInvScale()[0]); + return div * invdx; + } + + // stencil access version + template static typename StencilT::ValueType::value_type + result(const UniformScaleMap& map, const StencilT& stencil) + { + typedef typename StencilT::ValueType::value_type ValueType; + + ValueType div(0); + + div =ISDivergence::result(stencil); + ValueType invdx = ValueType(map.getInvScale()[0]); + return div * invdx; + } +}; + +/// Partial template specialization of Divergence +/// uniform scale and translation, any scheme +template +struct Divergence +{ + // random access version + template static typename Accessor::ValueType::value_type + result(const UniformScaleTranslateMap& map, const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType::value_type ValueType; + + ValueType div(0); + + div =ISDivergence::result(grid, ijk); + ValueType invdx = ValueType(map.getInvScale()[0]); + return div * invdx; + } + + // stencil access version + template static typename StencilT::ValueType::value_type + result(const UniformScaleTranslateMap& map, const StencilT& stencil) + { + typedef typename StencilT::ValueType::value_type ValueType; + + ValueType div(0); + + div =ISDivergence::result(stencil); + ValueType invdx = ValueType(map.getInvScale()[0]); + return div * invdx; + } +}; + +/// Full template specialization of Divergence +/// uniform scale 2nd order +template<> +struct Divergence +{ + // random access version + template static typename Accessor::ValueType::value_type + result(const UniformScaleMap& map, const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType::value_type ValueType; + + ValueType div(0); + div =ISDivergence::result(grid, ijk); + ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); + return div * inv2dx; + } + + // stencil access version + template static typename StencilT::ValueType::value_type + result(const UniformScaleMap& map, const StencilT& stencil) + { + typedef typename StencilT::ValueType::value_type ValueType; + + ValueType div(0); + div =ISDivergence::result(stencil); + ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); + return div * inv2dx; + } +}; + +/// Full template specialization of Divergence +/// uniform scale translate 2nd order +template<> +struct Divergence +{ + // random access version + template static typename Accessor::ValueType::value_type + result(const UniformScaleTranslateMap& map, const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType::value_type ValueType; + + ValueType div(0); + + div =ISDivergence::result(grid, ijk); + ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); + return div * inv2dx; + } + + // stencil access version + template static typename StencilT::ValueType::value_type + result(const UniformScaleTranslateMap& map, const StencilT& stencil) + { + typedef typename StencilT::ValueType::value_type ValueType; + + ValueType div(0); + + div =ISDivergence::result(stencil); + ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); + return div * inv2dx; + } +}; + +/// Partial template specialization of Divergence +/// scale, any scheme +template +struct Divergence +{ + // random access version + template static typename Accessor::ValueType::value_type + result(const ScaleMap& map, const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType::value_type ValueType; + + ValueType div = ValueType( + D1Vec::inX(grid, ijk, 0) * (map.getInvScale()[0]) + + D1Vec::inY(grid, ijk, 1) * (map.getInvScale()[1]) + + D1Vec::inZ(grid, ijk, 2) * (map.getInvScale()[2])); + return div; + } + + // stencil access version + template static typename StencilT::ValueType::value_type + result(const ScaleMap& map, const StencilT& stencil) + { + typedef typename StencilT::ValueType::value_type ValueType; + + ValueType div(0); + div = ValueType( + D1Vec::inX(stencil, 0) * (map.getInvScale()[0]) + + D1Vec::inY(stencil, 1) * (map.getInvScale()[1]) + + D1Vec::inZ(stencil, 2) * (map.getInvScale()[2]) ); + return div; + } +}; + +/// Partial template specialization of Divergence +/// scale translate, any scheme +template +struct Divergence +{ + // random access version + template static typename Accessor::ValueType::value_type + result(const ScaleTranslateMap& map, const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType::value_type ValueType; + + ValueType div = ValueType( + D1Vec::inX(grid, ijk, 0) * (map.getInvScale()[0]) + + D1Vec::inY(grid, ijk, 1) * (map.getInvScale()[1]) + + D1Vec::inZ(grid, ijk, 2) * (map.getInvScale()[2])); + return div; + } + + // stencil access version + template static typename StencilT::ValueType::value_type + result(const ScaleTranslateMap& map, const StencilT& stencil) + { + typedef typename StencilT::ValueType::value_type ValueType; + + ValueType div(0); + div = ValueType( + D1Vec::inX(stencil, 0) * (map.getInvScale()[0]) + + D1Vec::inY(stencil, 1) * (map.getInvScale()[1]) + + D1Vec::inZ(stencil, 2) * (map.getInvScale()[2]) ); + return div; + } +}; + +/// Full template specialization Divergence +/// scale 2nd order +template<> +struct Divergence +{ + // random access version + template static typename Accessor::ValueType::value_type + result(const ScaleMap& map, const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType::value_type ValueType; + + ValueType div = ValueType( + D1Vec::inX(grid, ijk, 0) * (map.getInvTwiceScale()[0]) + + D1Vec::inY(grid, ijk, 1) * (map.getInvTwiceScale()[1]) + + D1Vec::inZ(grid, ijk, 2) * (map.getInvTwiceScale()[2]) ); + return div; + } + + // stencil access version + template static typename StencilT::ValueType::value_type + result(const ScaleMap& map, const StencilT& stencil) + { + typedef typename StencilT::ValueType::value_type ValueType; + + ValueType div = ValueType( + D1Vec::inX(stencil, 0) * (map.getInvTwiceScale()[0]) + + D1Vec::inY(stencil, 1) * (map.getInvTwiceScale()[1]) + + D1Vec::inZ(stencil, 2) * (map.getInvTwiceScale()[2]) ); + return div; + } +}; + +/// Full template specialization of Divergence +/// scale and translate, 2nd order +template<> +struct Divergence +{ + // random access version + template static typename Accessor::ValueType::value_type + result(const ScaleTranslateMap& map, const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType::value_type ValueType; + + ValueType div = ValueType( + D1Vec::inX(grid, ijk, 0) * (map.getInvTwiceScale()[0]) + + D1Vec::inY(grid, ijk, 1) * (map.getInvTwiceScale()[1]) + + D1Vec::inZ(grid, ijk, 2) * (map.getInvTwiceScale()[2]) ); + return div; + } + + // stencil access version + template static typename StencilT::ValueType::value_type + result(const ScaleTranslateMap& map, const StencilT& stencil) + { + typedef typename StencilT::ValueType::value_type ValueType; + + ValueType div = ValueType( + D1Vec::inX(stencil, 0) * (map.getInvTwiceScale()[0]) + + D1Vec::inY(stencil, 1) * (map.getInvTwiceScale()[1]) + + D1Vec::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 +struct Curl +{ + // random access version + template 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::inX(grid, ijk, i), + D1Vec::inY(grid, ijk, i), + D1Vec::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 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::inX(stencil, i), + D1Vec::inY(stencil, i), + D1Vec::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 + } +}; + +/// Partial template specialization of Curl +template +struct Curl +{ + // random access version + template 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::result(grid, ijk) * ValueType(map.getInvScale()[0]); + } + + // Stencil access version + template static typename StencilT::ValueType + result(const UniformScaleMap& map, const StencilT& stencil) + { + typedef typename StencilT::ValueType Vec3Type; + typedef typename Vec3Type::value_type ValueType; + return ISCurl::result(stencil) * ValueType(map.getInvScale()[0]); + } +}; + +/// Partial template specialization of Curl +template +struct Curl +{ + // random access version + template 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::result(grid, ijk) * ValueType(map.getInvScale()[0]); + } + + // stencil access version + template static typename StencilT::ValueType + result(const UniformScaleTranslateMap& map, const StencilT& stencil) + { + typedef typename StencilT::ValueType Vec3Type; + typedef typename Vec3Type::value_type ValueType; + + return ISCurl::result(stencil) * ValueType(map.getInvScale()[0]); + } +}; + +/// Full template specialization of Curl +template<> +struct Curl +{ + // random access version + template 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::result(grid, ijk) * ValueType(map.getInvTwiceScale()[0]); + } + + // stencil access version + template static typename StencilT::ValueType + result(const UniformScaleMap& map, const StencilT& stencil) + { + typedef typename StencilT::ValueType Vec3Type; + typedef typename Vec3Type::value_type ValueType; + + return ISCurl::result(stencil) * ValueType(map.getInvTwiceScale()[0]); + } +}; + +/// Full template specialization of Curl +template<> +struct Curl +{ + // random access version + template 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::result(grid, ijk) * ValueType(map.getInvTwiceScale()[0]); + } + + // stencil access version + template static typename StencilT::ValueType + result(const UniformScaleTranslateMap& map, const StencilT& stencil) + { + typedef typename StencilT::ValueType Vec3Type; + typedef typename Vec3Type::value_type ValueType; + + return ISCurl::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 +struct Laplacian +{ + // random access version + template + 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::inX(grid, ijk); + ValueType iddy = D2::inY(grid, ijk); + ValueType iddz = D2::inZ(grid, ijk); + + ValueType iddxy = D2::inXandY(grid, ijk); + ValueType iddyz = D2::inYandZ(grid, ijk); + ValueType iddxz = D2::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::value) { + d2_rs = map.applyIJC(d2_is); + } else { + // compute the first derivatives with 2nd order accuracy. + Vec3d d1_is(static_cast(D1::inX(grid, ijk)), + static_cast(D1::inY(grid, ijk)), + static_cast(D1::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 + 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::inX(stencil); + ValueType iddy = D2::inY(stencil); + ValueType iddz = D2::inZ(stencil); + + ValueType iddxy = D2::inXandY(stencil); + ValueType iddyz = D2::inYandZ(stencil); + ValueType iddxz = D2::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::value) { + d2_rs = map.applyIJC(d2_is); + } else { + // compute the first derivatives with 2nd order accuracy. + Vec3d d1_is(D1::inX(stencil), + D1::inY(stencil), + D1::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 +struct Laplacian +{ + // random access version + template + static typename Accessor::ValueType result(const TranslationMap&, + const Accessor& grid, const Coord& ijk) + { + return ISLaplacian::result(grid, ijk); + } + + // stencil access version + template + static typename StencilT::ValueType result(const TranslationMap&, const StencilT& stencil) + { + return ISLaplacian::result(stencil); + } +}; + + +// The Laplacian is invariant to rotation or reflection. +template +struct Laplacian +{ + // random access version + template + static typename Accessor::ValueType result(const UnitaryMap&, + const Accessor& grid, const Coord& ijk) + { + return ISLaplacian::result(grid, ijk); + } + + // stencil access version + template + static typename StencilT::ValueType result(const UnitaryMap&, const StencilT& stencil) + { + return ISLaplacian::result(stencil); + } +}; + + +template +struct Laplacian +{ + // random access version + template 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::result(grid, ijk) * invdxdx; + } + + // stencil access version + template static typename StencilT::ValueType + result(const UniformScaleMap& map, const StencilT& stencil) + { + typedef typename StencilT::ValueType ValueType; + ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]); + return ISLaplacian::result(stencil) * invdxdx; + } +}; + + +template +struct Laplacian +{ + // random access version + template 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::result(grid, ijk) * invdxdx; + } + + // stencil access version + template static typename StencilT::ValueType + result(const UniformScaleTranslateMap& map, const StencilT& stencil) + { + typedef typename StencilT::ValueType ValueType; + ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]); + return ISLaplacian::result(stencil) * invdxdx; + } +}; + + +template +struct Laplacian +{ + // random access version + template 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::inX(grid, ijk); + ValueType iddy = D2::inY(grid, ijk); + ValueType iddz = D2::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 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::inX(stencil); + ValueType iddy = D2::inY(stencil); + ValueType iddz = D2::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 +struct Laplacian +{ + // random access version + template 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::inX(grid, ijk); + ValueType iddy = D2::inY(grid, ijk); + ValueType iddz = D2::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 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::inX(stencil); + ValueType iddy = D2::inY(stencil); + ValueType iddz = D2::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 +struct CPT +{ + // random access version + template static math::Vec3 + result(const MapType& map, const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType ValueType; + typedef Vec3 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::result(map, grid, ijk)); + if (is_linear::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 static math::Vec3 + result(const MapType& map, const StencilT& stencil) + { + typedef typename StencilT::ValueType ValueType; + typedef Vec3 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::result(map, stencil)); + if (is_linear::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 +struct CPT_RANGE +{ + // random access version + template static Vec3 + result(const MapType& map, const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType ValueType; + typedef Vec3 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::result(map, grid, ijk); + Vec3d result = map.applyMap(ijk.asVec3d()) - vectorFromSurface; + + return Vec3Type(result); + } + + // stencil access version + template static Vec3 + result(const MapType& map, const StencilT& stencil) + { + typedef typename StencilT::ValueType ValueType; + typedef Vec3 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::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 +struct MeanCurvature +{ + /// @brief random access version + /// @return true if the gradient is none-zero, in which case the + /// mean curvature is computed as 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 + static bool compute(const MapType& map, const Accessor& grid, const Coord& ijk, + double& alpha, double& beta) + { + typedef typename Accessor::ValueType ValueType; + + // compute the gradient in index and world space + Vec3d d1_is(static_cast(D1::inX(grid, ijk)), + static_cast(D1::inY(grid, ijk)), + static_cast(D1::inZ(grid, ijk))), d1_ws; + if (is_linear::value) {//resolved at compiletime + d1_ws = map.applyIJT(d1_is); + } else { + d1_ws = map.applyIJT(d1_is, ijk.asVec3d()); + } + const double Dx2 = d1_ws(0)*d1_ws(0); + const double Dy2 = d1_ws(1)*d1_ws(1); + const double Dz2 = d1_ws(2)*d1_ws(2); + const double normGrad = Dx2 + Dy2 + Dz2; + if (normGrad <= math::Tolerance::value()) { + alpha = beta = 0; + return false; + } + + // all the second derivatives in index space + ValueType iddx = D2::inX(grid, ijk); + ValueType iddy = D2::inY(grid, ijk); + ValueType iddz = D2::inZ(grid, ijk); + + ValueType iddxy = D2::inXandY(grid, ijk); + ValueType iddyz = D2::inYandZ(grid, ijk); + ValueType iddxz = D2::inXandZ(grid, ijk); + + // second derivatives in index space + Mat3d d2_is(iddx, iddxy, iddxz, + iddxy, iddy, iddyz, + iddxz, iddyz, iddz); + + // convert second derivatives to world space + Mat3d d2_ws; + if (is_linear::value) {//resolved at compiletime + d2_ws = map.applyIJC(d2_is); + } else { + d2_ws = map.applyIJC(d2_is, d1_is, ijk.asVec3d()); + } + + // assemble the nominator and denominator for mean curvature + alpha = (Dx2*(d2_ws(1,1)+d2_ws(2,2))+Dy2*(d2_ws(0,0)+d2_ws(2,2)) + +Dz2*(d2_ws(0,0)+d2_ws(1,1)) + -2*(d1_ws(0)*(d1_ws(1)*d2_ws(0,1)+d1_ws(2)*d2_ws(0,2)) + +d1_ws(1)*d1_ws(2)*d2_ws(1,2))); + beta = std::sqrt(normGrad); // * 1/dx + return true; + } + + template + static typename Accessor::ValueType result(const MapType& map, + const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType ValueType; + double alpha, beta; + return compute(map, grid, ijk, alpha, beta) ? + ValueType(alpha/(2. *math::Pow3(beta))) : 0; + } + + template + static typename Accessor::ValueType normGrad(const MapType& map, + const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType ValueType; + double alpha, beta; + return compute(map, grid, ijk, alpha, beta) ? + ValueType(alpha/(2. *math::Pow2(beta))) : 0; + } + + /// @brief stencil access version + /// @return true if the gradient is none-zero, in which case the + /// mean curvature is computed as 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 + static bool compute(const MapType& map, const StencilT& stencil, + double& alpha, double& beta) + { + typedef typename StencilT::ValueType ValueType; + + // compute the gradient in index and world space + Vec3d d1_is(D1::inX(stencil), + D1::inY(stencil), + D1::inZ(stencil) ), d1_ws; + if (is_linear::value) {//resolved at compiletime + d1_ws = map.applyIJT(d1_is); + } else { + d1_ws = map.applyIJT(d1_is, stencil.getCenterCoord().asVec3d()); + } + const double Dx2 = d1_ws(0)*d1_ws(0); + const double Dy2 = d1_ws(1)*d1_ws(1); + const double Dz2 = d1_ws(2)*d1_ws(2); + const double normGrad = Dx2 + Dy2 + Dz2; + if (normGrad <= math::Tolerance::value()) { + alpha = beta = 0; + return false; + } + + // all the second derivatives in index space + ValueType iddx = D2::inX(stencil); + ValueType iddy = D2::inY(stencil); + ValueType iddz = D2::inZ(stencil); + + ValueType iddxy = D2::inXandY(stencil); + ValueType iddyz = D2::inYandZ(stencil); + ValueType iddxz = D2::inXandZ(stencil); + + // second derivatives in index space + Mat3d d2_is(iddx, iddxy, iddxz, + iddxy, iddy, iddyz, + iddxz, iddyz, iddz); + + // convert second derivatives to world space + Mat3d d2_ws; + if (is_linear::value) {//resolved at compiletime + d2_ws = map.applyIJC(d2_is); + } else { + d2_ws = map.applyIJC(d2_is, d1_is, stencil.getCenterCoord().asVec3d()); + } + + // for return + alpha = (Dx2*(d2_ws(1,1)+d2_ws(2,2))+Dy2*(d2_ws(0,0)+d2_ws(2,2)) + +Dz2*(d2_ws(0,0)+d2_ws(1,1)) + -2*(d1_ws(0)*(d1_ws(1)*d2_ws(0,1)+d1_ws(2)*d2_ws(0,2)) + +d1_ws(1)*d1_ws(2)*d2_ws(1,2))); + beta = std::sqrt(normGrad); // * 1/dx + return true; + } + + template + static typename StencilT::ValueType + result(const MapType& map, const StencilT stencil) + { + typedef typename StencilT::ValueType ValueType; + double alpha, beta; + return compute(map, stencil, alpha, beta) ? + ValueType(alpha/(2*math::Pow3(beta))) : 0; + } + + template + static typename StencilT::ValueType normGrad(const MapType& map, const StencilT stencil) + { + typedef typename StencilT::ValueType ValueType; + double alpha, beta; + return compute(map, stencil, alpha, beta) ? + ValueType(alpha/(2*math::Pow2(beta))) : 0; + } +}; + + +template +struct MeanCurvature +{ + // random access version + template + static typename Accessor::ValueType result(const TranslationMap&, + const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType ValueType; + + ValueType alpha, beta; + return ISMeanCurvature::result(grid, ijk, alpha, beta) ? + ValueType(alpha /(2*math::Pow3(beta))) : 0; + } + + template + static typename Accessor::ValueType normGrad(const TranslationMap&, + const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType ValueType; + + ValueType alpha, beta; + return ISMeanCurvature::result(grid, ijk, alpha, beta) ? + ValueType(alpha/(2*math::Pow2(beta))) : 0; + } + + // stencil access version + template + static typename StencilT::ValueType result(const TranslationMap&, const StencilT& stencil) + { + typedef typename StencilT::ValueType ValueType; + + ValueType alpha, beta; + return ISMeanCurvature::result(stencil, alpha, beta) ? + ValueType(alpha /(2*math::Pow3(beta))) : 0; + } + + template + static typename StencilT::ValueType normGrad(const TranslationMap&, const StencilT& stencil) + { + typedef typename StencilT::ValueType ValueType; + + ValueType alpha, beta; + return ISMeanCurvature::result(stencil, alpha, beta) ? + ValueType(alpha/(2*math::Pow2(beta))) : 0; + } +}; + + +template +struct MeanCurvature +{ + // random access version + template + static typename Accessor::ValueType result(const UniformScaleMap& map, + const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType ValueType; + + ValueType alpha, beta; + if (ISMeanCurvature::result(grid, ijk, alpha, beta)) { + ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); + return ValueType(alpha*inv2dx/math::Pow3(beta)); + } + return 0; + } + + template + static typename Accessor::ValueType normGrad(const UniformScaleMap& map, + const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType ValueType; + + ValueType alpha, beta; + if (ISMeanCurvature::result(grid, ijk, alpha, beta)) { + ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]); + return ValueType(alpha*invdxdx/(2*math::Pow2(beta))); + } + return 0; + } + + // stencil access version + template + static typename StencilT::ValueType result(const UniformScaleMap& map, const StencilT& stencil) + { + typedef typename StencilT::ValueType ValueType; + + ValueType alpha, beta; + if (ISMeanCurvature::result(stencil, alpha, beta)) { + ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); + return ValueType(alpha*inv2dx/math::Pow3(beta)); + } + return 0; + } + + template + static typename StencilT::ValueType normGrad(const UniformScaleMap& map, const StencilT& stencil) + { + typedef typename StencilT::ValueType ValueType; + + ValueType alpha, beta; + if (ISMeanCurvature::result(stencil, alpha, beta)) { + ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]); + return ValueType(alpha*invdxdx/(2*math::Pow2(beta))); + } + return 0; + } +}; + + +template +struct MeanCurvature +{ + // random access version + template static typename Accessor::ValueType + result(const UniformScaleTranslateMap& map, const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType ValueType; + + ValueType alpha, beta; + if (ISMeanCurvature::result(grid, ijk, alpha, beta)) { + ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); + return ValueType(alpha*inv2dx/math::Pow3(beta)); + } + return 0; + } + + template static typename Accessor::ValueType + normGrad(const UniformScaleTranslateMap& map, const Accessor& grid, const Coord& ijk) + { + typedef typename Accessor::ValueType ValueType; + + ValueType alpha, beta; + if (ISMeanCurvature::result(grid, ijk, alpha, beta)) { + ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]); + return ValueType(alpha*invdxdx/(2*math::Pow2(beta))); + } + return 0; + } + + // stencil access version + template static typename StencilT::ValueType + result(const UniformScaleTranslateMap& map, const StencilT& stencil) + { + typedef typename StencilT::ValueType ValueType; + + ValueType alpha, beta; + if (ISMeanCurvature::result(stencil, alpha, beta)) { + ValueType inv2dx = ValueType(map.getInvTwiceScale()[0]); + return ValueType(alpha*inv2dx/math::Pow3(beta)); + } + return 0; + } + + template static typename StencilT::ValueType + normGrad(const UniformScaleTranslateMap& map, const StencilT& stencil) + { + typedef typename StencilT::ValueType ValueType; + + ValueType alpha, beta; + if (ISMeanCurvature::result(stencil, alpha, beta)) { + ValueType invdxdx = ValueType(map.getInvScaleSqr()[0]); + return ValueType(alpha*invdxdx/(2*math::Pow2(beta))); + } + return 0; + } +}; + + +/// @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 Map-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 + GenericMap(const GridType& g): mMap(g.transform().baseMap()) {} + + GenericMap(const Transform& t): mMap(t.baseMap()) {} + GenericMap(MapBase::Ptr map): mMap(boost::const_pointer_cast(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-2016 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/nuparu/include/openvdb/include/openvdb/math/Proximity.h b/nuparu/include/openvdb/include/openvdb/math/Proximity.h new file mode 100644 index 00000000..37110936 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/math/Proximity.h @@ -0,0 +1,79 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 + + +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, +/// return the point on @c abc closest to @c p and the corresponding barycentric coordinates. +/// +/// @details Algorithms 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); + + +/// @brief Closest Point on Line Segment to Point. Given segment @c ab and point @c p, +/// return the point on @c ab closest to @c p and @c t the parametric distance to @c b. +/// +/// @param a The segment's first vertex point. +/// @param b The segment's second vertex point. +/// @param p Point to compute the closest point on @c ab for. +/// @param t Parametric distance to @c b. +OPENVDB_API Vec3d +closestPointOnSegmentToPoint( + const Vec3d& a, const Vec3d& b, const Vec3d& p, double& t); + +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_MESH_TO_VOLUME_UTIL_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/math/QuantizedUnitVec.h b/nuparu/include/openvdb/include/openvdb/math/QuantizedUnitVec.h new file mode 100644 index 00000000..09a81ad8 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/math/QuantizedUnitVec.h @@ -0,0 +1,166 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include +#include "Vec3.h" +#include + +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 + static uint16_t pack(const Vec3& 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 +inline uint16_t +QuantizedUnitVec::pack(const Vec3& vec) +{ + if (math::isZero(vec)) return 0; + + 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 = static_cast((x * w)); + uint16_t ybits = static_cast((y * w)); + + // 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 = static_cast(127 - xbits); + ybits = static_cast(127 - ybits); + } + + // Pack components into their respective slots. + data = static_cast(data | (xbits << 7)); + data = static_cast(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 = static_cast((data & MASK_XSLOT) >> 7); + uint16_t ybits = static_cast(data & MASK_YSLOT); + + // Check if the complement components where stored and revert. + if ((xbits + ybits) > 126) { + xbits = static_cast(127 - xbits); + ybits = static_cast(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 = static_cast((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-2016 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/nuparu/include/openvdb/include/openvdb/math/Quat.h b/nuparu/include/openvdb/include/openvdb/math/Quat.h new file mode 100644 index 00000000..93248f1d --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/math/Quat.h @@ -0,0 +1,658 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include + +#include "Mat.h" +#include "Mat3.h" +#include "Math.h" +#include "Vec3.h" +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + +template class Quat; + +/// Linear interpolation between the two quaternions +template +Quat slerp(const Quat &q1, const Quat &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 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(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 +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 &axis, T angle) + { + // assert( REL_EQ(axis.length(), 1.) ); + + T s = T(sin(angle*T(0.5))); + + mm[0] = axis.x() * s; + mm[1] = axis.y() * s; + mm[2] = axis.z() * s; + + mm[3] = T(cos(angle*T(0.5))); + + } + + /// Constructor given rotation as axis and angle + Quat(math::Axis axis, T angle) + { + T s = T(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] = T(cos(angle*T(0.5))); + } + + /// Constructor given a rotation matrix + template + Quat(const Mat3 &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(T(2.0) * acos(mm[3])); + + } else { + + return T(0.0); + } + } + + /// Return axis of rotation + Vec3 axis() const + { + T sqrLength = mm[0]*mm[0] + mm[1]*mm[1] + mm[2]*mm[2]; + + if ( sqrLength > 1.0e-8 ) { + + T invLength = T(T(1)/sqrt(sqrLength)); + + return Vec3( mm[0]*invLength, mm[1]*invLength, mm[2]*invLength ); + } else { + + return Vec3(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& axis, T angle) + { + + T s = T(sin(angle*T(0.5))); + + mm[0] = axis.x() * s; + mm[1] = axis.y() * s; + mm[2] = axis.z() * s; + + mm[3] = T(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 eulerAngles(RotationOrder rotationOrder) const + { return math::eulerAngles(Mat3(*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(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(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 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(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(mm[0]/scalar, mm[1]/scalar, mm[2]/scalar, mm[3]/scalar); + } + + /// Negation operator, e.g. q = -q; + Quat operator-() const + { return Quat(-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& omega) const + { + return Quat( +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 = T(1.0e-8)) + { + T d = T(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 = T(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(-mm[0], -mm[1], -mm[2], mm[3]); + } + + /// Return rotated vector by "this" quaternion + Vec3 rotateVector(const Vec3 &v) const + { + Mat3 m(*this); + return m.transform(v); + } + + /// Predefined constants, e.g. Quat q = Quat::identity(); + static Quat zero() { return Quat(0,0,0,0); } + static Quat identity() { return Quat(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 +Quat operator*(S scalar, const Quat &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 +Mat3 slerp(const Mat3 &m1, const Mat3 &m2, T t) +{ + typedef Mat3 MatType; + + Quat q1(m1); + Quat q2(m2); + + if (q1.dot(q2) < 0) q2 *= -1; + + Quat qslerp = slerp(q1, q2, static_cast(t)); + MatType m = rotation(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 +Mat3 bezLerp(const Mat3 &m1, const Mat3 &m2, + const Mat3 &m3, const Mat3 &m4, + T t) +{ + Mat3 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 Quats; +typedef Quat Quatd; + +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif //OPENVDB_MATH_QUAT_H_HAS_BEEN_INCLUDED + +// --------------------------------------------------------------------------- +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/math/Ray.h b/nuparu/include/openvdb/include/openvdb/math/Ray.h new file mode 100644 index 00000000..00bc0e27 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/math/Ray.h @@ -0,0 +1,342 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 Ray.h +/// +/// @author Ken Museth +/// +/// @brief A Ray class. + +#ifndef OPENVDB_MATH_RAY_HAS_BEEN_INCLUDED +#define OPENVDB_MATH_RAY_HAS_BEEN_INCLUDED + +#include "Math.h" +#include "Vec3.h" +#include "Transform.h" +#include // for std::ostream +#include +#include // for std::numeric_limits::max() + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + +template +class Ray +{ +public: + BOOST_STATIC_ASSERT(boost::is_floating_point::value); + typedef RealT RealType; + typedef Vec3 Vec3Type; + typedef Vec3Type Vec3T; + struct TimeSpan { + RealT t0, t1; + /// @brief Default constructor + TimeSpan() {} + /// @brief Constructor + TimeSpan(RealT _t0, RealT _t1) : t0(_t0), t1(_t1) {} + /// @brief Set both times + inline void set(RealT _t0, RealT _t1) { t0=_t0; t1=_t1; } + /// @brief Get both times + inline void get(RealT& _t0, RealT& _t1) const { _t0=t0; _t1=t1; } + /// @brief Return @c true if t1 is larger than t0 by at least eps. + inline bool valid(RealT eps=math::Delta::value()) const { return (t1-t0)>eps; } + /// @brief Return the midpoint of the ray. + inline RealT mid() const { return 0.5*(t0 + t1); } + /// @brief Multiplies both times + inline void scale(RealT s) {assert(s>0); t0*=s; t1*=s; } + /// @brief Return @c true if time is inclusive + inline bool test(RealT t) const { return (t>=t0 && t<=t1); } + }; + + Ray(const Vec3Type& eye = Vec3Type(0,0,0), + const Vec3Type& direction = Vec3Type(1,0,0), + RealT t0 = math::Delta::value(), + RealT t1 = std::numeric_limits::max()) + : mEye(eye), mDir(direction), mInvDir(1/mDir), mTimeSpan(t0, t1) + { + } + + inline void setEye(const Vec3Type& eye) { mEye = eye; } + + inline void setDir(const Vec3Type& dir) + { + mDir = dir; + mInvDir = 1/mDir; + } + + inline void setMinTime(RealT t0) { assert(t0>0); mTimeSpan.t0 = t0; } + + inline void setMaxTime(RealT t1) { assert(t1>0); mTimeSpan.t1 = t1; } + + inline void setTimes(RealT t0 = math::Delta::value(), + RealT t1 = std::numeric_limits::max()) + { + assert(t0>0 && t1>0); + mTimeSpan.set(t0, t1); + } + + inline void scaleTimes(RealT scale) { mTimeSpan.scale(scale); } + + inline void reset(const Vec3Type& eye, + const Vec3Type& direction, + RealT t0 = math::Delta::value(), + RealT t1 = std::numeric_limits::max()) + { + this->setEye(eye); + this->setDir(direction); + this->setTimes(t0, t1); + } + + inline const Vec3T& eye() const {return mEye;} + + inline const Vec3T& dir() const {return mDir;} + + inline const Vec3T& invDir() const {return mInvDir;} + + inline RealT t0() const {return mTimeSpan.t0;} + + inline RealT t1() const {return mTimeSpan.t1;} + + /// @brief Return the position along the ray at the specified time. + inline Vec3R operator()(RealT time) const { return mEye + mDir * time; } + + /// @brief Return the starting point of the ray. + inline Vec3R start() const { return (*this)(mTimeSpan.t0); } + + /// @brief Return the endpoint of the ray. + inline Vec3R end() const { return (*this)(mTimeSpan.t1); } + + /// @brief Return the midpoint of the ray. + inline Vec3R mid() const { return (*this)(mTimeSpan.mid()); } + + /// @brief Return @c true if t0 is strictly less than t1. + OPENVDB_DEPRECATED inline bool test() const { return mTimeSpan.valid(RealT(0)); } + + /// @brief Return @c true if t1 is larger than t0 by at least eps. + inline bool valid(RealT eps=math::Delta::value()) const + { + return mTimeSpan.valid(eps); + } + + /// @brief Return @c true if @a time is within t0 and t1, both inclusive. + inline bool test(RealT time) const { return mTimeSpan.test(time); } + + /// @brief Return a new Ray that is transformed with the specified map. + /// @param map the map from which to construct the new Ray. + /// @warning Assumes a linear map and a normalized direction. + /// @details The requirement that the direction is normalized + /// follows from the transformation of t0 and t1 - and that fact that + /// we want applyMap and applyInverseMap to be inverse operations. + template + inline Ray applyMap(const MapType& map) const + { + assert(map.isLinear()); + assert(math::isRelOrApproxEqual(mDir.length(), RealT(1), Tolerance::value(), Delta::value())); + const Vec3T eye = map.applyMap(mEye); + const Vec3T dir = map.applyJacobian(mDir); + const RealT length = dir.length(); + return Ray(eye, dir/length, length*mTimeSpan.t0, length*mTimeSpan.t1); + } + + /// @brief Return a new Ray that is transformed with the inverse of the specified map. + /// @param map the map from which to construct the new Ray by inverse mapping. + /// @warning Assumes a linear map and a normalized direction. + /// @details The requirement that the direction is normalized + /// follows from the transformation of t0 and t1 - and that fact that + /// we want applyMap and applyInverseMap to be inverse operations. + template + inline Ray applyInverseMap(const MapType& map) const + { + assert(map.isLinear()); + assert(math::isRelOrApproxEqual(mDir.length(), RealT(1), Tolerance::value(), Delta::value())); + const Vec3T eye = map.applyInverseMap(mEye); + const Vec3T dir = map.applyInverseJacobian(mDir); + const RealT length = dir.length(); + return Ray(eye, dir/length, length*mTimeSpan.t0, length*mTimeSpan.t1); + } + + /// @brief Return a new ray in world space, assuming the existing + /// ray is represented in the index space of the specified grid. + template + inline Ray indexToWorld(const GridType& grid) const + { + return this->applyMap(*(grid.transform().baseMap())); + } + + /// @brief Return a new ray in the index space of the specified + /// grid, assuming the existing ray is represented in world space. + template + inline Ray worldToIndex(const GridType& grid) const + { + return this->applyInverseMap(*(grid.transform().baseMap())); + } + + /// @brief Return true if this ray intersects the specified sphere. + /// @param center The center of the sphere in the same space as this ray. + /// @param radius The radius of the sphere in the same units as this ray. + /// @param t0 The first intersection point if an intersection exists. + /// @param t1 The second intersection point if an intersection exists. + /// @note If the return value is true, i.e. a hit, and t0 = + /// this->t0() or t1 == this->t1() only one true intersection exist. + inline bool intersects(const Vec3T& center, RealT radius, RealT& t0, RealT& t1) const + { + const Vec3T origin = mEye - center; + const RealT A = mDir.lengthSqr(); + const RealT B = 2 * mDir.dot(origin); + const RealT C = origin.lengthSqr() - radius * radius; + const RealT D = B * B - 4 * A * C; + + if (D < 0) return false; + + const RealT Q = RealT(-0.5)*(B<0 ? (B + Sqrt(D)) : (B - Sqrt(D))); + + t0 = Q / A; + t1 = C / Q; + + if (t0 > t1) std::swap(t0, t1); + if (t0 < mTimeSpan.t0) t0 = mTimeSpan.t0; + if (t1 > mTimeSpan.t1) t1 = mTimeSpan.t1; + return t0 <= t1; + } + + /// @brief Return true if this ray intersects the specified sphere. + /// @param center The center of the sphere in the same space as this ray. + /// @param radius The radius of the sphere in the same units as this ray. + inline bool intersects(const Vec3T& center, RealT radius) const + { + RealT t0, t1; + return this->intersects(center, radius, t0, t1)>0; + } + + /// @brief Return true if this ray intersects the specified sphere. + /// @note For intersection this ray is clipped to the two intersection points. + /// @param center The center of the sphere in the same space as this ray. + /// @param radius The radius of the sphere in the same units as this ray. + inline bool clip(const Vec3T& center, RealT radius) + { + RealT t0, t1; + const bool hit = this->intersects(center, radius, t0, t1); + if (hit) mTimeSpan.set(t0, t1); + return hit; + } + + /// @brief Return true if the Ray intersects the specified + /// axisaligned bounding box. + /// @param bbox Axis-aligned bounding box in the same space as the Ray. + /// @param t0 If an intersection is detected this is assigned + /// the time for the first intersection point. + /// @param t1 If an intersection is detected this is assigned + /// the time for the second intersection point. + template + inline bool intersects(const BBoxT& bbox, RealT& t0, RealT& t1) const + { + mTimeSpan.get(t0, t1); + for (int i = 0; i < 3; ++i) { + RealT a = (bbox.min()[i] - mEye[i]) * mInvDir[i]; + RealT b = (bbox.max()[i] - mEye[i]) * mInvDir[i]; + if (a > b) std::swap(a, b); + if (a > t0) t0 = a; + if (b < t1) t1 = b; + if (t0 > t1) return false; + } + return true; + } + + /// @brief Return true if this ray intersects the specified bounding box. + /// @param bbox Axis-aligned bounding box in the same space as this ray. + template + inline bool intersects(const BBoxT& bbox) const + { + RealT t0, t1; + return this->intersects(bbox, t0, t1); + } + + /// @brief Return true if this ray intersects the specified bounding box. + /// @note For intersection this ray is clipped to the two intersection points. + /// @param bbox Axis-aligned bounding box in the same space as this ray. + template + inline bool clip(const BBoxT& bbox) + { + RealT t0, t1; + const bool hit = this->intersects(bbox, t0, t1); + if (hit) mTimeSpan.set(t0, t1); + return hit; + } + + /// @brief Return true if the Ray intersects the plane specified + /// by a normal and distance from the origin. + /// @param normal Normal of the plane. + /// @param distance Distance of the plane to the origin. + /// @param t Time of intersection, if one exists. + inline bool intersects(const Vec3T& normal, RealT distance, RealT& t) const + { + const RealT cosAngle = mDir.dot(normal); + if (math::isApproxZero(cosAngle)) return false;//parallel + t = (distance - mEye.dot(normal))/cosAngle; + return this->test(t); + } + + /// @brief Return true if the Ray intersects the plane specified + /// by a normal and point. + /// @param normal Normal of the plane. + /// @param point Point in the plane. + /// @param t Time of intersection, if one exists. + inline bool intersects(const Vec3T& normal, const Vec3T& point, RealT& t) const + { + return this->intersects(normal, point.dot(normal), t); + } + +private: + Vec3T mEye, mDir, mInvDir; + TimeSpan mTimeSpan; +}; // end of Ray class + +/// @brief Output streaming of the Ray class. +/// @note Primarily intended for debugging. +template +inline std::ostream& operator<<(std::ostream& os, const Ray& r) +{ + os << "eye=" << r.eye() << " dir=" << r.dir() << " 1/dir="< // for ostringstream +#include +#include +#include +#include +#include +#include // for std::less +#include "Math.h" + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + +/// @brief Templated class to compute the minimum and maximum values. +template > +class MinMax +{ +public: + + /// @brief Constructor + MinMax(const ValueType &min, const ValueType &max) : mMin(min), mMax(max) + { + } + + /// Add a single sample. + inline void add(const ValueType &val, const Less &less = Less()) + { + if (less(val, mMin)) mMin = val; + if (less(mMax, val)) mMax = val; + } + + /// Return the minimum value. + inline const ValueType& min() const { return mMin; } + + /// Return the maximum value. + inline const ValueType& max() const { return mMax; } + + /// Add the samples from the other Stats instance. + inline void add(const MinMax& other, const Less &less = Less()) + { + if (less(other.mMin, mMin)) mMin = other.mMin; + if (less(mMax, other.mMax)) mMax = other.mMax; + } + + /// @brief Print MinMax 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 << "MinMax "; + if (!name.empty()) os << "for \"" << name << "\" "; + os << " Min=" << mMin << ", Max=" << mMax << std::endl; + strm << os.str(); + } + +protected: + + ValueType mMin, mMax; +};//end MinMax + +/// @brief This class computes the minimum and maximum values of a population +/// of floating-point values. +class Extrema +{ +public: + + /// @brief Constructor + /// @warning The min/max values are initiated to extreme values + Extrema() + : mSize(0) + , mMin(std::numeric_limits::max()) + , mMax(-mMin) + { + } + + /// Add a single sample. + void add(double val) + { + ++mSize; + mMin = std::min(val, mMin); + mMax = std::max(val, mMax); + } + + /// Add @a n samples with constant value @a val. + void add(double val, uint64_t n) + { + mSize += n; + mMin = std::min(val, mMin); + mMax = std::max(val, mMax); + } + + /// Return the size of the population, i.e., the total number of samples. + inline uint64_t size() const { return mSize; } + + /// Return the minimum value. + inline double min() const { return mMin; } + + /// Return the maximum value. + inline double max() const { return mMax; } + + /// Return the range defined as the maximum value minus the minimum value. + inline double range() const { return mMax - mMin; } + + /// Add the samples from the other Stats instance. + void add(const Extrema& other) + { + if (other.mSize > 0) this->join(other); + } + + /// @brief Print extrema 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 << "Extrema "; + if (!name.empty()) os << "for \"" << name << "\" "; + if (mSize>0) { + os << "with " << mSize << " samples:\n" + << " Min=" << mMin + << ", Max=" << mMax + << ", Range="<< this->range() << std::endl; + } else { + os << ": no samples were added." << std::endl; + } + strm << os.str(); + } + +protected: + + inline void join(const Extrema& other) + { + assert(other.mSize > 0); + mSize += other.mSize; + mMin = std::min(mMin, other.mMin); + mMax = std::max(mMax, other.mMax); + } + + uint64_t mSize; + double mMin, mMax; +};//end Extrema + + +/// @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 Extrema +{ +public: + Stats() + : Extrema() + , mAvg(0.0) + , mAux(0.0) + { + } + + /// Add a single sample. + void add(double val) + { + Extrema::add(val); + 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) + { + const double denom = 1.0/double(mSize + n); + const double delta = val - mAvg; + mAvg += denom * delta * double(n); + mAux += denom * delta * delta * double(mSize) * double(n); + Extrema::add(val, n); + } + + /// Add the samples from the other Stats instance. + void add(const Stats& other) + { + if (other.mSize > 0) { + const double denom = 1.0/double(mSize + other.mSize); + const double delta = other.mAvg - mAvg; + mAvg += denom * delta * double(other.mSize); + mAux += other.mAux + denom * delta * delta * double(mSize) * double(other.mSize); + Extrema::join(other); + } + } + + //@{ + /// Return the arithmetic mean, i.e. average, value. + inline double avg() const { return mAvg; } + inline double mean() const { return mAvg; } + //@} + + //@{ + /// @brief Return the population variance. + /// @note The unbiased sample variance = population variance * + //num/(num-1) + inline double var() const { return mSize<2 ? 0.0 : mAux/double(mSize); } + inline double variance() const { return this->var(); } + //@} + + //@{ + /// @brief Return the standard deviation (=Sqrt(variance)) as + /// defined from the (biased) population variance. + inline double std() const { return sqrt(this->var()); } + inline double stdDev() const { return this->std(); } + //@} + + /// @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 << "\" "; + if (mSize>0) { + os << "with " << mSize << " samples:\n" + << " Min=" << mMin + << ", Max=" << mMax + << ", Ave=" << mAvg + << ", Std=" << this->stdDev() + << ", Var=" << this->variance() << std::endl; + } else { + os << ": no samples were added." << std::endl; + } + strm << os.str(); + } + +protected: + using Extrema::mSize; + using Extrema::mMin; + using Extrema::mMax; + double mAvg, mAux; +}; // 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) + { + if ( mMax <= mMin ) { + OPENVDB_THROW(ValueError, "Histogram: expected min < max"); + } else if ( numBins == 0 ) { + OPENVDB_THROW(ValueError, "Histogram: expected at least one bin"); + } + for (size_t i=0; imMax) 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. + inline size_t numBins() const { return mBins.size(); } + /// Return the lower bound of this histogram's value range. + inline double min() const { return mMin; } + /// Return the upper bound of this histogram's value range. + inline double max() const { return mMax; } + /// Return the minimum value in the nth bin. + inline double min(int n) const { return mMin+n/mDelta; } + /// Return the maximum value in the nth bin. + inline double max(int n) const { return mMin+(n+1)/mDelta; } + /// Return the number of samples in the nth bin. + inline uint64_t count(int n) const { return mBins[n]; } + /// Return the population size, i.e., the total number of samples. + inline 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 << "\" "; + if (mSize > 0) { + os << "with " << mSize << " samples:\n"; + os << "==============================================================\n"; + os << "|| # | Min | Max | Frequency | % ||\n"; + os << "==============================================================\n"; + for (int i = 0, e = int(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"; + } else { + os << ": no samples were added." << std::endl; + } + strm << os.str(); + } + +private: + uint64_t mSize; + double mMin, mMax, mDelta; + std::vector mBins; +}; + +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_MATH_STATS_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/math/Stencils.h b/nuparu/include/openvdb/include/openvdb/math/Stencils.h new file mode 100644 index 00000000..dcdc7508 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/math/Stencils.h @@ -0,0 +1,1680 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +/// +/// @brief Defines various finite difference stencils by means of the +/// "curiously recurring template pattern" on a BaseStencil +/// that caches stencil values and stores a ValueAccessor for +/// fast lookup. + +#ifndef OPENVDB_MATH_STENCILS_HAS_BEEN_INCLUDED +#define OPENVDB_MATH_STENCILS_HAS_BEEN_INCLUDED + +#include +#include +#include // for Pow2, needed by WENO and Godunov +#include // for Real +#include // for Coord +#include // for WENO5 and GodunovsNormSqrd +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + + +//////////////////////////////////////// + +template +class BaseStencil +{ +public: + typedef GridT GridType; + typedef typename GridT::TreeType TreeType; + typedef typename GridT::ValueType ValueType; + typedef tree::ValueAccessor AccessorType; + typedef std::vector BufferType; + typedef typename BufferType::iterator IterType; + + /// @brief Initialize the stencil buffer with the values of voxel (i, j, k) + /// and its neighbors. + /// @param ijk Index coordinates of stencil center + inline void moveTo(const Coord& ijk) + { + mCenter = ijk; + mStencil[0] = mCache.getValue(ijk); + static_cast(*this).init(mCenter); + } + + /// @brief Initialize the stencil buffer with the values of voxel (i, j, k) + /// and its neighbors. The method also takes a value of the center + /// element of the stencil, assuming it is already known. + /// @param ijk Index coordinates of stnecil center + /// @param centerValue Value of the center element of the stencil + inline void moveTo(const Coord& ijk, const ValueType& centerValue) + { + mCenter = ijk; + mStencil[0] = centerValue; + static_cast(*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 + inline void moveTo(const IterType& iter) + { + mCenter = iter.getCoord(); + mStencil[0] = *iter; + static_cast(*this).init(mCenter); + } + + /// @brief Initialize the stencil buffer with the values of voxel (x, y, z) + /// and its neighbors. + /// @param xyz Floating point voxel coordinates of stencil center + /// @details This method will check to see if it is necessary to + /// update the stencil based on the cached index coordinates of + /// the center point. + inline void moveTo(const Vec3R& xyz) + { + Coord ijk = openvdb::Coord::floor(xyz); + if (ijk != mCenter) this->moveTo(ijk); + } + + /// @brief Return the value from the stencil buffer with linear + /// offset pos. + /// + /// @note The default (@a pos = 0) corresponds to the first element + /// which is typically the center point of the stencil. + inline const ValueType& getValue(unsigned int pos = 0) const + { + assert(pos < mStencil.size()); + return mStencil[pos]; + } + + /// @brief Return the value at the specified location relative to the center of the stencil + template + inline const ValueType& getValue() const + { + return mStencil[static_cast(*this).template pos()]; + } + + /// @brief Set the value at the specified location relative to the center of the stencil + template + inline void setValue(const ValueType& value) + { + mStencil[static_cast(*this).template pos()] = value; + } + + /// @brief Return the size of the stencil buffer. + inline int size() { return mStencil.size(); } + + /// @brief Return the median value of the current stencil. + inline ValueType median() const + { + BufferType 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]; + } + + /// @brief Return the mean value of the current stencil. + inline ValueType mean() const + { + ValueType sum = 0.0; + for (int n = 0, s = int(mStencil.size()); n < s; ++n) sum += mStencil[n]; + return sum / mStencil.size(); + } + + /// @brief Return the smallest value in the stencil buffer. + inline ValueType min() const + { + IterType iter = std::min_element(mStencil.begin(), mStencil.end()); + return *iter; + } + + /// @brief Return the largest value in the stencil buffer. + inline ValueType max() const + { + IterType iter = std::max_element(mStencil.begin(), mStencil.end()); + return *iter; + } + + /// @brief Return the coordinates of the center point of the stencil. + inline const Coord& getCenterCoord() const { return mCenter; } + + /// @brief Return the value at the center of the stencil + inline const ValueType& getCenterValue() const { return mStencil[0]; } + + /// @brief Return true if the center of the stencil intersects the + /// iso-contour specified by the isoValue + inline bool intersects(const ValueType &isoValue = zeroVal()) 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)) ; + } + + /// @brief Return a const reference to the grid from which this + /// stencil was constructed. + inline const GridType& grid() const { return *mGrid; } + + /// @brief Return a const reference to the ValueAccessor + /// associated with this Stencil. + inline const AccessorType& accessor() const { return mCache; } + +protected: + // Constructor is protected to prevent direct instantiation. + BaseStencil(const GridType& grid, int size) + : mGrid(&grid) + , mCache(grid.tree()) + , mStencil(size) + , mCenter(Coord::max()) + { + } + + const GridType* mGrid; + AccessorType mCache; + BufferType mStencil; + Coord mCenter; + +}; // BaseStencil class + + +//////////////////////////////////////// + + +namespace { // anonymous namespace for stencil-layout map + + // the seven point stencil + template 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 +class SevenPointStencil: public BaseStencil, GridT, IsSafe> +{ + typedef SevenPointStencil SelfT; + typedef BaseStencil BaseType; +public: + typedef GridT GridType; + typedef typename GridT::TreeType TreeType; + typedef typename GridT::ValueType ValueType; + + static const int SIZE = 7; + + SevenPointStencil(const GridT& grid): BaseType(grid, SIZE) {} + + /// Return linear offset for the specified stencil point relative to its center + template + unsigned int pos() const { return SevenPt::idx; } + +private: + inline void init(const Coord& 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 friend class BaseStencil; // allow base class to call init() + using BaseType::mCache; + using BaseType::mStencil; +};// SevenPointStencil class + + +//////////////////////////////////////// + + +namespace { // anonymous namespace for stencil-layout map + + // the eight point box stencil + template struct BoxPt {}; + template<> struct BoxPt< 0, 0, 0> { enum { idx = 0 }; }; + template<> struct BoxPt< 0, 0, 1> { enum { idx = 1 }; }; + template<> struct BoxPt< 0, 1, 1> { enum { idx = 2 }; }; + template<> struct BoxPt< 0, 1, 0> { enum { idx = 3 }; }; + template<> struct BoxPt< 1, 0, 0> { enum { idx = 4 }; }; + template<> struct BoxPt< 1, 0, 1> { enum { idx = 5 }; }; + template<> struct BoxPt< 1, 1, 1> { enum { idx = 6 }; }; + template<> struct BoxPt< 1, 1, 0> { enum { idx = 7 }; }; +} + +template +class BoxStencil: public BaseStencil, GridT, IsSafe> +{ + typedef BoxStencil SelfT; + typedef BaseStencil BaseType; +public: + typedef GridT GridType; + typedef typename GridT::TreeType TreeType; + typedef typename GridT::ValueType ValueType; + + static const int SIZE = 8; + + BoxStencil(const GridType& grid): BaseType(grid, SIZE) {} + + /// Return linear offset for the specified stencil point relative to its center + template + unsigned int pos() const { return BoxPt::idx; } + + /// @brief Return true if the center of the stencil intersects the + /// iso-contour specified by the isoValue + inline bool intersects(const ValueType &isoValue = zeroVal()) const + { + const bool less = mStencil[0] < isoValue; + return (less ^ (mStencil[1] < isoValue)) || + (less ^ (mStencil[2] < isoValue)) || + (less ^ (mStencil[3] < isoValue)) || + (less ^ (mStencil[4] < isoValue)) || + (less ^ (mStencil[5] < isoValue)) || + (less ^ (mStencil[6] < isoValue)) || + (less ^ (mStencil[7] < isoValue)) ; + } + + /// @brief Return the trilinear interpolation at the normalized position. + /// @param xyz Floating point coordinate position. + /// @warning It is assumed that the stencil has already been moved + /// to the relevant voxel position, e.g. using moveTo(xyz). + /// @note Trilinear interpolation kernal reads as: + /// v000 (1-u)(1-v)(1-w) + v001 (1-u)(1-v)w + v010 (1-u)v(1-w) + v011 (1-u)vw + /// + v100 u(1-v)(1-w) + v101 u(1-v)w + v110 uv(1-w) + v111 uvw + inline ValueType interpolation(const math::Vec3& xyz) const + { + const Real u = xyz[0] - BaseType::mCenter[0]; assert(u>=0 && u<=1); + const Real v = xyz[1] - BaseType::mCenter[1]; assert(v>=0 && v<=1); + const Real w = xyz[2] - BaseType::mCenter[2]; assert(w>=0 && w<=1); + + ValueType V = BaseType::template getValue<0,0,0>(); + ValueType A = static_cast(V + (BaseType::template getValue<0,0,1>() - V) * w); + V = BaseType::template getValue< 0, 1, 0>(); + ValueType B = static_cast(V + (BaseType::template getValue<0,1,1>() - V) * w); + ValueType C = static_cast(A + (B - A) * v); + + V = BaseType::template getValue<1,0,0>(); + A = static_cast(V + (BaseType::template getValue<1,0,1>() - V) * w); + V = BaseType::template getValue<1,1,0>(); + B = static_cast(V + (BaseType::template getValue<1,1,1>() - V) * w); + ValueType D = static_cast(A + (B - A) * v); + + return static_cast(C + (D - C) * u); + } + + /// @brief Return the gradient in world space of the trilinear interpolation kernel. + /// @param xyz Floating point coordinate position. + /// @warning It is assumed that the stencil has already been moved + /// to the relevant voxel position, e.g. using moveTo(xyz). + /// @note Computed as partial derivatives of the trilinear interpolation kernel: + /// v000 (1-u)(1-v)(1-w) + v001 (1-u)(1-v)w + v010 (1-u)v(1-w) + v011 (1-u)vw + /// + v100 u(1-v)(1-w) + v101 u(1-v)w + v110 uv(1-w) + v111 uvw + inline math::Vec3 gradient(const math::Vec3& xyz) const + { + const Real u = xyz[0] - BaseType::mCenter[0]; assert(u>=0 && u<=1); + const Real v = xyz[1] - BaseType::mCenter[1]; assert(v>=0 && v<=1); + const Real w = xyz[2] - BaseType::mCenter[2]; assert(w>=0 && w<=1); + + ValueType D[4]={BaseType::template getValue<0,0,1>()-BaseType::template getValue<0,0,0>(), + BaseType::template getValue<0,1,1>()-BaseType::template getValue<0,1,0>(), + BaseType::template getValue<1,0,1>()-BaseType::template getValue<1,0,0>(), + BaseType::template getValue<1,1,1>()-BaseType::template getValue<1,1,0>()}; + + // Z component + ValueType A = static_cast(D[0] + (D[1]- D[0]) * v); + ValueType B = static_cast(D[2] + (D[3]- D[2]) * v); + math::Vec3 grad(zeroVal(), + zeroVal(), + static_cast(A + (B - A) * u)); + + D[0] = static_cast(BaseType::template getValue<0,0,0>() + D[0] * w); + D[1] = static_cast(BaseType::template getValue<0,1,0>() + D[1] * w); + D[2] = static_cast(BaseType::template getValue<1,0,0>() + D[2] * w); + D[3] = static_cast(BaseType::template getValue<1,1,0>() + D[3] * w); + + // X component + A = static_cast(D[0] + (D[1] - D[0]) * v); + B = static_cast(D[2] + (D[3] - D[2]) * v); + + grad[0] = B - A; + + // Y component + A = D[1] - D[0]; + B = D[3] - D[2]; + + grad[1] = static_cast(A + (B - A) * u); + + return BaseType::mGrid->transform().baseMap()->applyIJT(grad, xyz); + } + +private: + inline void init(const Coord& ijk) + { + BaseType::template setValue< 0, 0, 1>(mCache.getValue(ijk.offsetBy( 0, 0, 1))); + BaseType::template setValue< 0, 1, 1>(mCache.getValue(ijk.offsetBy( 0, 1, 1))); + BaseType::template setValue< 0, 1, 0>(mCache.getValue(ijk.offsetBy( 0, 1, 0))); + BaseType::template setValue< 1, 0, 0>(mCache.getValue(ijk.offsetBy( 1, 0, 0))); + BaseType::template setValue< 1, 0, 1>(mCache.getValue(ijk.offsetBy( 1, 0, 1))); + BaseType::template setValue< 1, 1, 1>(mCache.getValue(ijk.offsetBy( 1, 1, 1))); + BaseType::template setValue< 1, 1, 0>(mCache.getValue(ijk.offsetBy( 1, 1, 0))); + } + + template friend class BaseStencil; // allow base class to call init() + using BaseType::mCache; + using BaseType::mStencil; +};// BoxStencil class + + +//////////////////////////////////////// + + +namespace { // anonymous namespace for stencil-layout map + + // the dense point stencil + template 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 +class SecondOrderDenseStencil + : public BaseStencil, GridT, IsSafe > +{ + typedef SecondOrderDenseStencil SelfT; + typedef BaseStencil BaseType; +public: + typedef GridT GridType; + typedef typename GridT::TreeType TreeType; + typedef typename GridType::ValueType ValueType; + + 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 + unsigned int pos() const { return DensePt::idx; } + +private: + inline void init(const Coord& ijk) + { + 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 friend class BaseStencil; // allow base class to call init() + using BaseType::mCache; + using BaseType::mStencil; +};// SecondOrderDenseStencil class + + +//////////////////////////////////////// + + +namespace { // anonymous namespace for stencil-layout map + + // the dense point stencil + template 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 +class ThirteenPointStencil + : public BaseStencil, GridT, IsSafe> +{ + typedef ThirteenPointStencil SelfT; + typedef BaseStencil BaseType; +public: + typedef GridT GridType; + typedef typename GridT::TreeType TreeType; + typedef typename GridType::ValueType ValueType; + + 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 + unsigned int pos() const { return ThirteenPt::idx; } + +private: + inline void init(const Coord& ijk) + { + 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 friend class BaseStencil; // allow base class to call init() + using BaseType::mCache; + using BaseType::mStencil; +};// ThirteenPointStencil class + + +//////////////////////////////////////// + + +namespace { // anonymous namespace for stencil-layout map + + // the 4th-order dense point stencil + template 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 +class FourthOrderDenseStencil + : public BaseStencil, GridT, IsSafe> +{ + typedef FourthOrderDenseStencil SelfT; + typedef BaseStencil BaseType; +public: + typedef GridT GridType; + typedef typename GridT::TreeType TreeType; + typedef typename GridType::ValueType ValueType; + + 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 + unsigned int pos() const { return FourthDensePt::idx; } + +private: + inline void init(const Coord& 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 friend class BaseStencil; // allow base class to call init() + using BaseType::mCache; + using BaseType::mStencil; +};// FourthOrderDenseStencil class + + +//////////////////////////////////////// + + +namespace { // anonymous namespace for stencil-layout map + + // the dense point stencil + template 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 +class NineteenPointStencil + : public BaseStencil, GridT, IsSafe> +{ + typedef NineteenPointStencil SelfT; + typedef BaseStencil BaseType; +public: + typedef GridT GridType; + typedef typename GridT::TreeType TreeType; + typedef typename GridType::ValueType ValueType; + + 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 + unsigned int pos() const { return NineteenPt::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 friend class BaseStencil; // allow base class to call init() + using BaseType::mCache; + using BaseType::mStencil; +};// NineteenPointStencil class + + +//////////////////////////////////////// + + +namespace { // anonymous namespace for stencil-layout map + + // the 4th-order dense point stencil + template 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 +class SixthOrderDenseStencil + : public BaseStencil, GridT, IsSafe> +{ + typedef SixthOrderDenseStencil SelfT; + typedef BaseStencil BaseType; +public: + typedef GridT GridType; + typedef typename GridT::TreeType TreeType; + typedef typename GridType::ValueType ValueType; + + 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 + unsigned int pos() const { return SixthDensePt::idx; } + +private: + inline void init(const Coord& 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 friend class BaseStencil; // allow base class to call init() + using BaseType::mCache; + using BaseType::mStencil; +};// SixthOrderDenseStencil class + + +////////////////////////////////////////////////////////////////////// + + +/// 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 +class GradStencil : public BaseStencil, GridT, IsSafe> +{ + typedef GradStencil SelfT; + typedef BaseStencil BaseType; +public: + typedef GridT GridType; + typedef typename GridT::TreeType TreeType; + typedef typename GridType::ValueType ValueType; + + 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 Godunov'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::GodunovsNormSqrd(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 math::Vec3 gradient() const + { + return math::Vec3(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 math::Vec3 gradient(const math::Vec3& V) const + { + return math::Vec3( + 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 typename BaseType::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 math::Vec3 cpt() + { + const Coord& ijk = BaseType::getCenterCoord(); + const ValueType d = ValueType(mStencil[0] * 0.5 * mInvDx2); // distance in voxels / (2dx^2) + return math::Vec3(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 friend class BaseStencil; // allow base class to call init() + using BaseType::mCache; + using BaseType::mStencil; + const ValueType mInv2Dx, mInvDx2; +}; // GradStencil class + +//////////////////////////////////////// + + +/// @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 +class WenoStencil: public BaseStencil, GridT, IsSafe> +{ + typedef WenoStencil SelfT; + typedef BaseStencil BaseType; +public: + typedef GridT GridType; + typedef typename GridT::TreeType TreeType; + typedef typename GridType::ValueType ValueType; + + 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 Godunov'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 typename BaseType::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::GodunovsNormSqrd(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 static_cast( + mInvDx2*math::GodunovsNormSqrd(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 math::Vec3 gradient(const math::Vec3& V) const + { + const typename BaseType::BufferType& v = mStencil; + return 2*mInv2Dx * math::Vec3( + 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 math::Vec3 gradient() const + { + return mInv2Dx * math::Vec3(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 typename BaseType::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 friend class BaseStencil; // allow base class to call init() + using BaseType::mCache; + using BaseType::mStencil; + const ValueType mDx2, mInv2Dx, mInvDx2; +}; // WenoStencil class + + +////////////////////////////////////////////////////////////////////// + + +template +class CurvatureStencil: public BaseStencil, GridT, IsSafe> +{ + typedef CurvatureStencil SelfT; + typedef BaseStencil BaseType; +public: + typedef GridT GridType; + typedef typename GridT::TreeType TreeType; + typedef typename GridT::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; + return this->meanCurvature(alpha, beta) ? ValueType(alpha*mInv2Dx/math::Pow3(beta)) : 0; + } + + /// 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; + return this->meanCurvature(alpha, beta) ? ValueType(alpha*mInvDx2/(2*math::Pow2(beta))) : 0; + } + + /// 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 gradient() + { + return math::Vec3( + 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 bool 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 + normGrad = Dx2 + Dy2 + Dz2; + if (normGrad <= math::Tolerance::value()) { + alpha = beta = 0; + return false; + } + const Real + 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(normGrad); // * 1/dx + return true; + } + + template friend class BaseStencil; // allow base class to call init() + using BaseType::mCache; + using BaseType::mStencil; + const ValueType mInv2Dx, mInvDx2; +}; // CurvatureStencil class + + +////////////////////////////////////////////////////////////////////// + + +/// @brief Dense stencil of a given width +template +class DenseStencil: public BaseStencil, GridT, IsSafe> +{ + typedef DenseStencil SelfT; + typedef BaseStencil BaseType; +public: + typedef GridT GridType; + typedef typename GridT::TreeType TreeType; + typedef typename GridType::ValueType ValueType; + + DenseStencil(const GridType& grid, int halfWidth) + : BaseType(grid, /*size=*/math::Pow3(2 * halfWidth + 1)) + , mHalfWidth(halfWidth) + { + assert(halfWidth>0); + } + + inline const ValueType& getCenterValue() const { return mStencil[(mStencil.size()-1)>>1]; } + + /// @brief Initialize the stencil buffer with the values of voxel (x, y, z) + /// and its neighbors. + inline void moveTo(const Coord& ijk) + { + BaseType::mCenter = ijk; + this->init(ijk); + } + /// @brief Initialize the stencil buffer with the values of voxel + /// (x, y, z) and its neighbors. + template + inline void moveTo(const IterType& iter) + { + BaseType::mCenter = iter.getCoord(); + this->init(BaseType::mCenter); + } + +private: + /// Initialize the stencil buffer centered at (i, j, k). + /// @warning The center point is NOT at mStencil[0] for this DenseStencil! + inline void init(const Coord& ijk) + { + int n = 0; + for (Coord p=ijk.offsetBy(-mHalfWidth), q=ijk.offsetBy(mHalfWidth); p[0] <= q[0]; ++p[0]) { + for (p[1] = ijk[1]-mHalfWidth; p[1] <= q[1]; ++p[1]) { + for (p[2] = ijk[2]-mHalfWidth; p[2] <= q[2]; ++p[2]) { + mStencil[n++] = mCache.getValue(p); + } + } + } + } + + template friend class BaseStencil; // allow base class to call init() + using BaseType::mCache; + using BaseType::mStencil; + const int mHalfWidth; +};// DenseStencil class + + +} // end math namespace +} // namespace OPENVDB_VERSION_NAME +} // end openvdb namespace + +#endif // OPENVDB_MATH_STENCILS_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/math/Transform.h b/nuparu/include/openvdb/include/openvdb/math/Transform.h new file mode 100644 index 00000000..df37d0e4 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/math/Transform.h @@ -0,0 +1,310 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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_TRANSFORM_HAS_BEEN_INCLUDED +#define OPENVDB_MATH_TRANSFORM_HAS_BEEN_INCLUDED + +#include "Maps.h" +#include +#include + +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. +/// @see Transform::worldToIndex(const BBoxd&) const +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 Ptr; + typedef boost::shared_ptr 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));} + //@} + + //@{ + /// @brief Apply this transformation to the given index-space bounding box. + /// @return an axis-aligned world-space bounding box + BBoxd indexToWorld(const CoordBBox&) const; + BBoxd indexToWorld(const BBoxd&) const; + //@} + //@{ + /// @brief Apply the inverse of this transformation to the given world-space bounding box. + /// @return an axis-aligned index-space bounding box + BBoxd worldToIndex(const BBoxd&) const; + CoordBBox worldToIndexCellCentered(const BBoxd&) const; + CoordBBox worldToIndexNodeCentered(const BBoxd&) const; + //@} + + //@{ + /// 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::Ptr map(); + template typename MapType::ConstPtr map() const; + template 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 +inline typename MapType::Ptr +Transform::map() +{ + if (mMap->type() == MapType::mapType()) { + return boost::static_pointer_cast(mMap); + } + return typename MapType::Ptr(); +} + + +template +inline typename MapType::ConstPtr +Transform::map() const +{ + return boost::const_pointer_cast( + const_cast(this)->map()); +} + + +template +inline typename MapType::ConstPtr +Transform::constMap() const +{ + return map(); +} + + +//////////////////////////////////////// + + +/// Helper function used internally by processTypedMap() +template +inline void +doProcessTypedMap(Transform& transform, OpType& op) +{ + ResolvedMapType& resolvedMap = *transform.map(); +#ifdef _MSC_VER + op.operator()(resolvedMap); +#else + op.template operator()(resolvedMap); +#endif +} + +/// Helper function used internally by processTypedMap() +template +inline void +doProcessTypedMap(const Transform& transform, OpType& op) +{ + const ResolvedMapType& resolvedMap = *transform.map(); +#ifdef _MSC_VER + op.operator()(resolvedMap); +#else + op.template operator()(resolvedMap); +#endif +} + + +/// @brief Utility function that, given a generic map pointer, +/// calls a functor on the fully-resoved map +/// +/// Usage: +/// @code +/// struct Foo { +/// template +/// void operator()(const MapT& map) const { blah } +/// }; +/// +/// processTypedMap(myMap, Foo()); +/// @endcode +/// +/// @return @c false if the grid type is unknown or unhandled. +template +bool +processTypedMap(TransformType& transform, OpType& op) +{ + using namespace openvdb; + + const Name mapType = transform.mapType(); + if (mapType == UniformScaleMap::mapType()) { + doProcessTypedMap(transform, op); + + } else if (mapType == UniformScaleTranslateMap::mapType()) { + doProcessTypedMap(transform, op); + + } else if (mapType == ScaleMap::mapType()) { + doProcessTypedMap(transform, op); + + } else if (mapType == ScaleTranslateMap::mapType()) { + doProcessTypedMap(transform, op); + + } else if (mapType == UnitaryMap::mapType()) { + doProcessTypedMap(transform, op); + + } else if (mapType == AffineMap::mapType()) { + doProcessTypedMap(transform, op); + + } else if (mapType == TranslationMap::mapType()) { + doProcessTypedMap(transform, op); + + } else if (mapType == NonlinearFrustumMap::mapType()) { + doProcessTypedMap(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-2016 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/nuparu/include/openvdb/include/openvdb/math/Tuple.h b/nuparu/include/openvdb/include/openvdb/math/Tuple.h new file mode 100644 index 00000000..f46248a0 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/math/Tuple.h @@ -0,0 +1,212 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include +#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 +class Tuple { +public: + typedef T value_type; + typedef T ValueType; + + static const int 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 + explicit Tuple(Tuple const &src) { + enum { COPY_END = (SIZE < src_size ? SIZE : src_size) }; + + for (int i = 0; i < COPY_END; ++i) { + mm[i] = src[i]; + } + for (int i = COPY_END; 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 + 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(&mm), sizeof(T)*SIZE); + } + void read(std::istream& is) { + is.read(reinterpret_cast(&mm), sizeof(T)*SIZE); + } + +protected: + T mm[SIZE]; +}; + + +//////////////////////////////////////// + + +/// @return true if t0 < t1, comparing components in order of significance. +template +bool +operator<(const Tuple& t0, const Tuple& t1) +{ + for (int 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 +bool +operator>(const Tuple& t0, const Tuple& t1) +{ + for (int 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 the absolute value of the given Tuple. +template +Tuple +Abs(const Tuple& t) +{ + Tuple result; + for (int i = 0; i < SIZE; ++i) result[i] = math::Abs(t[i]); + return result; +} + + +//////////////////////////////////////// + + +/// Write a Tuple to an output stream +template +std::ostream& operator<<(std::ostream& ostr, const Tuple& classname) +{ + ostr << classname.str(); + return ostr; +} + +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_MATH_TUPLE_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/math/Vec2.h b/nuparu/include/openvdb/include/openvdb/math/Vec2.h new file mode 100644 index 00000000..71a1d726 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/math/Vec2.h @@ -0,0 +1,553 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include +#include "Math.h" +#include "Tuple.h" + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + +template class Mat2; + +template +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 two 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 + Vec2(Source *a) + { + this->mm[0] = a[0]; + this->mm[1] = a[1]; + } // trivial + + /// Conversion constructor + template + explicit Vec2(const Tuple<2, Source> &t) + { + this->mm[0] = static_cast(t[0]); + this->mm[1] = static_cast(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& init(T x=0, T y=0) + { + this->mm[0] = x; this->mm[1] = y; + return *this; + } + + /// Set "this" vector to zero + const Vec2& setZero() + { + this->mm[0] = 0; this->mm[1] = 0; + return *this; + } + + /// Assignment operator + template + const Vec2& operator=(const Vec2 &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 &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 &v) const { return !(*this==v); } + + /// Test if "this" vector is equivalent to vector v with tolerance of eps + bool eq(const Vec2 &v, T eps = static_cast(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 operator-() const {return Vec2(-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 + const Vec2& add(const Vec2 &v1, const Vec2 &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 + const Vec2& sub(const Vec2 &v1, const Vec2 &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 + const Vec2& scale(T0 scalar, const Vec2 &v) + { + this->mm[0] = scalar * v[0]; + this->mm[1] = scalar * v[1]; + + return *this; + } + + template + const Vec2 &div(T0 scalar, const Vec2 &v) + { + this->mm[0] = v[0] / scalar; + this->mm[1] = v[1] / scalar; + + return *this; + } + + /// Dot product + T dot(const Vec2 &v) const { return this->mm[0]*v[0] + this->mm[1]*v[1]; } // trivial + + /// Length of the vector + T length() const + { + return static_cast(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]); } + + /// Return a reference to itsef after the exponent has been + /// applied to all the vector components. + inline const Vec2& exp() + { + this->mm[0] = std::exp(this->mm[0]); + this->mm[1] = std::exp(this->mm[1]); + return *this; + } + + /// Return the sum of all the vector components. + inline T sum() const + { + return this->mm[0] + 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 unit(T eps=0) const + { + T d; + return unit(eps, d); + } + + /// return normalized this and length, throws if null vector + Vec2 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 + const Vec2 &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 + const Vec2 &operator*=(const Vec2 &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 + const Vec2 &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 + const Vec2 &operator/=(const Vec2 &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 + const Vec2 &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 + const Vec2 &operator+=(const Vec2 &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 + const Vec2 &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 + const Vec2 &operator-=(const Vec2 &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 &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 projection(const Vec2 &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 getArbPerpendicular() const { return Vec2(-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 zero() { return Vec2(0, 0); } +}; + + +/// Returns V, where \f$V_i = v_i * scalar\f$ for \f$i \in [0, 1]\f$ +template +inline Vec2::type> operator*(S scalar, const Vec2 &v) +{ + return v * scalar; +} + +/// Returns V, where \f$V_i = v_i * scalar\f$ for \f$i \in [0, 1]\f$ +template +inline Vec2::type> operator*(const Vec2 &v, S scalar) +{ + Vec2::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 +inline Vec2::type> operator*(const Vec2 &v0, const Vec2 &v1) +{ + Vec2::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 +inline Vec2::type> operator/(S scalar, const Vec2 &v) +{ + return Vec2::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 +inline Vec2::type> operator/(const Vec2 &v, S scalar) +{ + Vec2::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 +inline Vec2::type> operator/(const Vec2 &v0, const Vec2 &v1) +{ + Vec2::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 +inline Vec2::type> operator+(const Vec2 &v0, const Vec2 &v1) +{ + Vec2::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 +inline Vec2::type> operator+(const Vec2 &v, S scalar) +{ + Vec2::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 +inline Vec2::type> operator-(const Vec2 &v0, const Vec2 &v1) +{ + Vec2::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 +inline Vec2::type> operator-(const Vec2 &v, S scalar) +{ + Vec2::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 +inline T angle(const Vec2 &v1, const Vec2 &v2) +{ + T c = v1.dot(v2); + return acos(c); +} + +template +inline bool +isApproxEqual(const Vec2& a, const Vec2& b) +{ + return a.eq(b); +} +template +inline bool +isApproxEqual(const Vec2& a, const Vec2& b, const Vec2& eps) +{ + return isApproxEqual(a.x(), b.x(), eps.x()) && + isApproxEqual(a.y(), b.y(), eps.y()); +} + +template +inline bool +isFinite(const Vec2& v) +{ + return isFinite(v[0]) && isFinite(v[1]); +} + +/// Return @c true if all components are exactly equal to zero. +template +inline bool +isZero(const Vec2& v) +{ + return isZero(v[0]) && isZero(v[1]); +} + +template +inline Vec2 +Abs(const Vec2& v) +{ + return Vec2(Abs(v[0]), Abs(v[1])); +} + +/// Orthonormalize vectors v1 and v2 and store back the resulting basis +/// e.g. Vec2f::orthonormalize(v1,v2); +template +inline void orthonormalize(Vec2 &v1, Vec2 &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 +inline Vec2 minComponent(const Vec2 &v1, const Vec2 &v2) +{ + return Vec2( + std::min(v1.x(), v2.x()), + std::min(v1.y(), v2.y())); +} + +/// Return component-wise maximum of the two vectors. +template +inline Vec2 maxComponent(const Vec2 &v1, const Vec2 &v2) +{ + return Vec2( + std::max(v1.x(), v2.x()), + std::max(v1.y(), v2.y())); +} + +/// @brief Return a vector with the exponent applied to each of +/// the components of the input vector. +template +inline Vec2 Exp(Vec2 v) { return v.exp(); } + +typedef Vec2 Vec2i; +typedef Vec2 Vec2ui; +typedef Vec2 Vec2s; +typedef Vec2 Vec2d; + +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_MATH_VEC2_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/math/Vec3.h b/nuparu/include/openvdb/include/openvdb/math/Vec3.h new file mode 100644 index 00000000..9b4a8a85 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/math/Vec3.h @@ -0,0 +1,661 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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_VEC3_HAS_BEEN_INCLUDED +#define OPENVDB_MATH_VEC3_HAS_BEEN_INCLUDED + +#include +#include +#include "Math.h" +#include "Tuple.h" + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + +template class Mat3; + +template +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 + Vec3(Source *a) + { + this->mm[0] = a[0]; + this->mm[1] = a[1]; + this->mm[2] = a[2]; + } + + /// @brief Construct a Vec3 from a 3-Tuple with a possibly different value type. + /// @details Type conversion warnings are suppressed. + template + explicit Vec3(const Tuple<3, Source> &v) + { + this->mm[0] = static_cast(v[0]); + this->mm[1] = static_cast(v[1]); + this->mm[2] = static_cast(v[2]); + } + + /// @brief Construct a Vec3 from another Vec3 with a possibly different value type. + /// @details Type conversion warnings are suppressed. + template + Vec3(const Vec3& v) + { + this->mm[0] = static_cast(v[0]); + this->mm[1] = static_cast(v[1]); + this->mm[2] = static_cast(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& 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& setZero() + { + this->mm[0] = 0; this->mm[1] = 0; this->mm[2] = 0; + return *this; + } + + /// @brief Assignment operator + /// @details Type conversion warnings are not suppressed. + template + const Vec3& operator=(const Vec3 &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]; + + return *this; + } + + /// Test if "this" vector is equivalent to vector v with tolerance of eps + bool eq(const Vec3 &v, T eps = static_cast(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 operator-() const { return Vec3(-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 + const Vec3& add(const Vec3 &v1, const Vec3 &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 + const Vec3& sub(const Vec3 &v1, const Vec3 &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 + const Vec3& scale(T0 scale, const Vec3 &v) + { + this->mm[0] = scale * v[0]; + this->mm[1] = scale * v[1]; + this->mm[2] = scale * v[2]; + + return *this; + } + + template + const Vec3 &div(T0 scale, const Vec3 &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 &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(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 cross(const Vec3 &v) const + { + return Vec3(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& cross(const Vec3 &v1, const Vec3 &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 + const Vec3 &operator*=(S scalar) + { + this->mm[0] = static_cast(this->mm[0] * scalar); + this->mm[1] = static_cast(this->mm[1] * scalar); + this->mm[2] = static_cast(this->mm[2] * scalar); + return *this; + } + + /// Returns v0, where \f$v0_i *= v1_i\f$ for \f$i \in [0, 2]\f$ + template + const Vec3 &operator*=(const Vec3 &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 + const Vec3 &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 + const Vec3 &operator/=(const Vec3 &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 + const Vec3 &operator+=(S scalar) + { + this->mm[0] = static_cast(this->mm[0] + scalar); + this->mm[1] = static_cast(this->mm[1] + scalar); + this->mm[2] = static_cast(this->mm[2] + scalar); + return *this; + } + + /// Returns v0, where \f$v0_i += v1_i\f$ for \f$i \in [0, 2]\f$ + template + const Vec3 &operator+=(const Vec3 &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 + const Vec3 &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 + const Vec3 &operator-=(const Vec3 &v1) + { + this->mm[0] -= v1[0]; + this->mm[1] -= v1[1]; + this->mm[2] -= v1[2]; + return *this; + } + + /// Return a reference to itsef after the exponent has been + /// applied to all the vector components. + inline const Vec3& exp() + { + this->mm[0] = std::exp(this->mm[0]); + this->mm[1] = std::exp(this->mm[1]); + this->mm[2] = std::exp(this->mm[2]); + return *this; + } + + /// Return the sum of all the vector components. + inline T sum() const + { + return this->mm[0] + this->mm[1] + this->mm[2]; + } + + /// 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 unit(T eps=0) const + { + T d; + return unit(eps, d); + } + + /// return normalized this and length, throws if null vector + Vec3 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 &onto, T eps = static_cast(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 projection(const Vec3 &onto, T eps = static_cast(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 getArbPerpendicular() const + { + Vec3 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(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(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 zero() { return Vec3(0, 0, 0); } +}; + + +/// Equality operator, does exact floating point comparisons +template +inline bool operator==(const Vec3 &v0, const Vec3 &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 +inline bool operator!=(const Vec3 &v0, const Vec3 &v1) { return !(v0==v1); } + +/// Returns V, where \f$V_i = v_i * scalar\f$ for \f$i \in [0, 2]\f$ +template +inline Vec3::type> operator*(S scalar, const Vec3 &v) { return v*scalar; } + +/// Returns V, where \f$V_i = v_i * scalar\f$ for \f$i \in [0, 2]\f$ +template +inline Vec3::type> operator*(const Vec3 &v, S scalar) +{ + Vec3::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 +inline Vec3::type> operator*(const Vec3 &v0, const Vec3 &v1) +{ + Vec3::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 +inline Vec3::type> operator/(S scalar, const Vec3 &v) +{ + return Vec3::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 +inline Vec3::type> operator/(const Vec3 &v, S scalar) +{ + Vec3::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 +inline Vec3::type> operator/(const Vec3 &v0, const Vec3 &v1) +{ + Vec3::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 +inline Vec3::type> operator+(const Vec3 &v0, const Vec3 &v1) +{ + Vec3::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 +inline Vec3::type> operator+(const Vec3 &v, S scalar) +{ + Vec3::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 +inline Vec3::type> operator-(const Vec3 &v0, const Vec3 &v1) +{ + Vec3::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 +inline Vec3::type> operator-(const Vec3 &v, S scalar) +{ + Vec3::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 +inline T angle(const Vec3 &v1, const Vec3 &v2) +{ + Vec3 c = v1.cross(v2); + return static_cast(atan2(c.length(), v1.dot(v2))); +} + +template +inline bool +isApproxEqual(const Vec3& a, const Vec3& b) +{ + return a.eq(b); +} +template +inline bool +isApproxEqual(const Vec3& a, const Vec3& b, const Vec3& eps) +{ + return isApproxEqual(a.x(), b.x(), eps.x()) && + isApproxEqual(a.y(), b.y(), eps.y()) && + isApproxEqual(a.z(), b.z(), eps.z()); +} + +template +inline bool +isFinite(const Vec3& v) +{ + return isFinite(v[0]) && isFinite(v[1]) && isFinite(v[2]); +} + +/// Return @c true if all components are exactly equal to zero. +template +inline bool +isZero(const Vec3& v) +{ + return isZero(v[0]) && isZero(v[1]) && isZero(v[2]); +} + +template +inline Vec3 +Abs(const Vec3& v) +{ + return Vec3(Abs(v[0]), Abs(v[1]), Abs(v[2])); +} + +/// Orthonormalize vectors v1, v2 and v3 and store back the resulting +/// basis e.g. Vec3d::orthonormalize(v1,v2,v3); +template +inline void orthonormalize(Vec3 &v1, Vec3 &v2, Vec3 &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 +inline Vec3 minComponent(const Vec3 &v1, const Vec3 &v2) +{ + return Vec3( + 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 +inline Vec3 maxComponent(const Vec3 &v1, const Vec3 &v2) +{ + return Vec3( + std::max(v1.x(), v2.x()), + std::max(v1.y(), v2.y()), + std::max(v1.z(), v2.z())); +} + +/// @brief Return a vector with the exponent applied to each of +/// the components of the input vector. +template +inline Vec3 Exp(Vec3 v) { return v.exp(); } + +typedef Vec3 Vec3i; +typedef Vec3 Vec3ui; +typedef Vec3 Vec3s; +typedef Vec3 Vec3d; + +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_MATH_VEC3_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/math/Vec4.h b/nuparu/include/openvdb/include/openvdb/math/Vec4.h new file mode 100644 index 00000000..64ac29fb --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/math/Vec4.h @@ -0,0 +1,594 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include +#include "Math.h" +#include "Tuple.h" +#include "Vec3.h" + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace math { + +template class Mat3; + +template +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 four arguments, e.g. Vec4f v(1,2,3,4); + 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 + 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 + explicit Vec4(const Tuple<4, Source> &v) + { + this->mm[0] = static_cast(v[0]); + this->mm[1] = static_cast(v[1]); + this->mm[2] = static_cast(v[2]); + this->mm[3] = static_cast(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 getVec3() const { return Vec3(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& 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& setZero() + { + this->mm[0] = 0; this->mm[1] = 0; this->mm[2] = 0; this->mm[3] = 0; + return *this; + } + + /// Assignment operator + template + const Vec4& operator=(const Vec4 &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 &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 operator-() const + { + return Vec4( + -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 + const Vec4& add(const Vec4 &v1, const Vec4 &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 + const Vec4& sub(const Vec4 &v1, const Vec4 &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 + const Vec4& scale(T0 scale, const Vec4 &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 + const Vec4 &div(T0 scalar, const Vec4 &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 &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]); + } + + /// Return a reference to itsef after the exponent has been + /// applied to all the vector components. + inline const Vec4& exp() + { + this->mm[0] = std::exp(this->mm[0]); + this->mm[1] = std::exp(this->mm[1]); + this->mm[2] = std::exp(this->mm[2]); + this->mm[3] = std::exp(this->mm[3]); + return *this; + } + + /// Return the sum of all the vector components. + inline T sum() const + { + return this->mm[0] + this->mm[1] + this->mm[2] + 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 unit(T eps=0) const + { + T d; + return unit(eps, d); + } + + /// return normalized this and length, throws if null vector + Vec4 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 + const Vec4 &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 + const Vec4 &operator*=(const Vec4 &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 + const Vec4 &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 + const Vec4 &operator/=(const Vec4 &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 + const Vec4 &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 + const Vec4 &operator+=(const Vec4 &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 + const Vec4 &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 + const Vec4 &operator-=(const Vec4 &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 zero() { return Vec4(0, 0, 0, 0); } + static Vec4 origin() { return Vec4(0, 0, 0, 1); } +}; + +/// Equality operator, does exact floating point comparisons +template +inline bool operator==(const Vec4 &v0, const Vec4 &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 +inline bool operator!=(const Vec4 &v0, const Vec4 &v1) { return !(v0==v1); } + +/// Returns V, where \f$V_i = v_i * scalar\f$ for \f$i \in [0, 3]\f$ +template +inline Vec4::type> operator*(S scalar, const Vec4 &v) +{ return v*scalar; } + +/// Returns V, where \f$V_i = v_i * scalar\f$ for \f$i \in [0, 3]\f$ +template +inline Vec4::type> operator*(const Vec4 &v, S scalar) +{ + Vec4::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 +inline Vec4::type> operator*(const Vec4 &v0, + const Vec4 &v1) +{ + Vec4::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 +inline Vec4::type> operator/(S scalar, const Vec4 &v) +{ + return Vec4::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 +inline Vec4::type> operator/(const Vec4 &v, S scalar) +{ + Vec4::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 +inline Vec4::type> operator/(const Vec4 &v0, + const Vec4 &v1) +{ + Vec4::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 +inline Vec4::type> operator+(const Vec4 &v0, const Vec4 &v1) +{ + Vec4::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 +inline Vec4::type> operator+(const Vec4 &v, S scalar) +{ + Vec4::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 +inline Vec4::type> operator-(const Vec4 &v0, const Vec4 &v1) +{ + Vec4::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 +inline Vec4::type> operator-(const Vec4 &v, S scalar) +{ + Vec4::type> result(v); + result -= scalar; + return result; +} + +template +inline bool +isApproxEqual(const Vec4& a, const Vec4& b) +{ + return a.eq(b); +} +template +inline bool +isApproxEqual(const Vec4& a, const Vec4& b, const Vec4& eps) +{ + return isApproxEqual(a[0], b[0], eps[0]) && + isApproxEqual(a[1], b[1], eps[1]) && + isApproxEqual(a[2], b[2], eps[2]) && + isApproxEqual(a[3], b[3], eps[3]); +} + +template +inline bool +isFinite(const Vec4& v) +{ + return isFinite(v[0]) && isFinite(v[1]) && isFinite(v[2]) && isFinite(v[3]); +} + +/// Return @c true if all components are exactly equal to zero. +template +inline bool +isZero(const Vec4& v) +{ + return isZero(v[0]) && isZero(v[1]) && isZero(v[2]) && isZero(v[3]); +} + +template +inline Vec4 +Abs(const Vec4& v) +{ + return Vec4(Abs(v[0]), Abs(v[1]), Abs(v[2]), Abs(v[3])); +} + +/// @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 +inline Vec4 minComponent(const Vec4 &v1, const Vec4 &v2) +{ + return Vec4( + 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 +inline Vec4 maxComponent(const Vec4 &v1, const Vec4 &v2) +{ + return Vec4( + std::max(v1.x(), v2.x()), + std::max(v1.y(), v2.y()), + std::max(v1.z(), v2.z()), + std::max(v1.w(), v2.w())); +} + +/// @brief Return a vector with the exponent applied to each of +/// the components of the input vector. +template +inline Vec4 Exp(Vec4 v) { return v.exp(); } + +typedef Vec4 Vec4i; +typedef Vec4 Vec4ui; +typedef Vec4 Vec4s; +typedef Vec4 Vec4d; + +} // namespace math +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_MATH_VEC4_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/metadata/MetaMap.h b/nuparu/include/openvdb/include/openvdb/metadata/MetaMap.h new file mode 100644 index 00000000..455b31a8 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/metadata/MetaMap.h @@ -0,0 +1,251 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + +/// Container that maps names (strings) to values of arbitrary types +class OPENVDB_API MetaMap +{ +public: + typedef boost::shared_ptr Ptr; + typedef boost::shared_ptr ConstPtr; + + typedef std::map MetadataMap; + typedef MetadataMap::iterator MetaIterator; + typedef MetadataMap::const_iterator ConstMetaIterator; + ///< @todo this should really iterate over a map of Metadata::ConstPtrs + + MetaMap() {} + MetaMap(const MetaMap& other); + 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 a deep copy of another map to this map. + MetaMap& operator=(const MetaMap&); + + /// Unserialize metadata from the given stream. + void readMeta(std::istream&); + /// Serialize metadata to the given stream. + void writeMeta(std::ostream&) const; + + /// @brief Insert a new metadata field or overwrite the value of an existing field. + /// @details If a field with the given name doesn't already exist, add a new field. + /// Otherwise, if the new value's type is the same as the existing field's value type, + /// overwrite the existing value with new value. + /// @throw TypeError if a field with the given name already exists, but its value type + /// is not the same as the new value's + /// @throw ValueError if the given field name is empty. + void insertMeta(const Name&, const Metadata& value); + /// @brief Deep copy all of the metadata fields from the given map into this map. + /// @throw TypeError if any field in the given map has the same name as + /// but a different value type than one of this map's fields. + void insertMeta(const MetaMap&); + + /// Remove the given metadata field if it exists. + void removeMeta(const Name&); + + //@{ + /// @brief Return a pointer to the metadata with the given name. + /// If no such field exists, return a null pointer. + Metadata::Ptr operator[](const Name&); + Metadata::ConstPtr operator[](const Name&) const; + //@} + + //@{ + /// @brief Return a pointer to a TypedMetadata object of type @c T and with the given name. + /// If no such field exists or if there is a type mismatch, return a null pointer. + template typename T::Ptr getMetadata(const Name&); + template typename T::ConstPtr getMetadata(const Name&) const; + //@} + + /// @brief Return a reference to the value of type @c T stored in the given metadata field. + /// @throw LookupError if no field with the given name exists. + /// @throw TypeError if the given field is not of type @c T. + template T& metaValue(const Name&); + template const T& metaValue(const 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(); } + + /// Return a string describing this metadata map. Prefix each line with @a indent. + std::string str(const std::string& indent = "") const; + + /// Return @c true if the given map is equivalent to this map. + bool operator==(const MetaMap& other) const; + /// Return @c true if the given map is different from this map. + bool operator!=(const MetaMap& other) const { return !(*this == other); } + +private: + /// @brief 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 TypedMetadata::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 +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(iter->second); + } // else + return typename T::Ptr(); +} + +template +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(iter->second); + } // else + return typename T::ConstPtr(); +} + + +//////////////////////////////////////// + + +template +inline typename TypedMetadata::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::Ptr m; + if (iter->second->typeName() == TypedMetadata::staticTypeName()) { + m = boost::static_pointer_cast, Metadata>(iter->second); + } + if (!m) OPENVDB_THROW(TypeError, "Invalid type for metadata " << name); + return m; +} + + +//////////////////////////////////////// + + +template +inline T& +MetaMap::metaValue(const Name &name) +{ + typename TypedMetadata::Ptr m = getValidTypedMetadata(name); + return m->value(); +} + + +template +inline const T& +MetaMap::metaValue(const Name &name) const +{ + typename TypedMetadata::Ptr m = getValidTypedMetadata(name); + return m->value(); +} + +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_METADATA_METAMAP_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/metadata/Metadata.h b/nuparu/include/openvdb/include/openvdb/metadata/Metadata.h new file mode 100644 index 00000000..96c44ef3 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/metadata/Metadata.h @@ -0,0 +1,397 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include +#include +#include // for math::isZero() +#include +#include +#include +#include + + +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 Ptr; + typedef boost::shared_ptr ConstPtr; + + Metadata() {} + 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 the given metadata into this metadata. + virtual void copy(const Metadata& other) = 0; + + /// Return a textual representation of this 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 @c true if the given metadata is equivalent to this metadata. + bool operator==(const Metadata& other) const; + /// Return @c true if the given metadata is different from this metadata. + bool operator!=(const Metadata& other) const { return !(*this == other); } + + /// Return the size of this metadata in bytes. + virtual Index32 size() const = 0; + + /// Unserialize this metadata from a stream. + void read(std::istream&); + /// Serialize this metadata to a stream. + void write(std::ostream&) const; + + /// Create new metadata of the given type. + static Metadata::Ptr createMetadata(const Name& typeName); + + /// Return @c true if the given type is known by the metadata type registry. + static bool isRegisteredType(const Name& typeName); + + /// Clear out the metadata registry. + static void clearRegistry(); + + /// 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); + +protected: + /// Read the size of the metadata from a stream. + static Index32 readSize(std::istream&); + /// Write the size of the metadata to a stream. + void writeSize(std::ostream&) const; + + /// Read the metadata from a stream. + virtual void readValue(std::istream&, Index32 numBytes) = 0; + /// Write the metadata to a stream. + virtual void writeValue(std::ostream&) const = 0; + +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 ""; } + 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 ""; } + 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 +class TypedMetadata: public Metadata +{ +public: + typedef boost::shared_ptr > Ptr; + typedef boost::shared_ptr > ConstPtr; + + TypedMetadata(); + TypedMetadata(const T& value); + TypedMetadata(const TypedMetadata& other); + virtual ~TypedMetadata(); + + virtual Name typeName() const; + virtual Metadata::Ptr copy() const; + virtual void copy(const Metadata& other); + virtual std::string str() const; + virtual bool asBool() const; + virtual Index32 size() const { return static_cast(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(); } + + /// Create new metadata of this type. + static Metadata::Ptr createMetadata(); + + static void registerType(); + static void unregisterType(); + static bool isRegisteredType(); + +protected: + virtual void readValue(std::istream&, Index32 numBytes); + 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(&n), sizeof(Index32)); +} + + +inline Index32 +Metadata::readSize(std::istream& is) +{ + Index32 n = 0; + is.read(reinterpret_cast(&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 +inline +TypedMetadata::TypedMetadata() : mValue(T()) +{ +} + +template +inline +TypedMetadata::TypedMetadata(const T &value) : mValue(value) +{ +} + +template +inline +TypedMetadata::TypedMetadata(const TypedMetadata &other) : + Metadata(), + mValue(other.mValue) +{ +} + +template +inline +TypedMetadata::~TypedMetadata() +{ +} + +template +inline Name +TypedMetadata::typeName() const +{ + return TypedMetadata::staticTypeName(); +} + +template +inline void +TypedMetadata::setValue(const T& val) +{ + mValue = val; +} + +template +inline T& +TypedMetadata::value() +{ + return mValue; +} + +template +inline const T& +TypedMetadata::value() const +{ + return mValue; +} + +template +inline Metadata::Ptr +TypedMetadata::copy() const +{ + Metadata::Ptr metadata(new TypedMetadata()); + metadata->copy(*this); + return metadata; +} + +template +inline void +TypedMetadata::copy(const Metadata &other) +{ + const TypedMetadata* t = dynamic_cast*>(&other); + if (t == NULL) OPENVDB_THROW(TypeError, "Incompatible type during copy"); + mValue = t->mValue; +} + + +template +inline void +TypedMetadata::readValue(std::istream& is, Index32 /*numBytes*/) +{ + //assert(this->size() == numBytes); + is.read(reinterpret_cast(&mValue), this->size()); +} + +template +inline void +TypedMetadata::writeValue(std::ostream& os) const +{ + os.write(reinterpret_cast(&mValue), this->size()); +} + +template +inline std::string +TypedMetadata::str() const +{ + std::ostringstream ostr; + ostr << mValue; + return ostr.str(); +} + +template +inline bool +TypedMetadata::asBool() const +{ + return !math::isZero(mValue); +} + +template +inline Metadata::Ptr +TypedMetadata::createMetadata() +{ + Metadata::Ptr ret(new TypedMetadata()); + return ret; +} + +template +inline void +TypedMetadata::registerType() +{ + Metadata::registerType(TypedMetadata::staticTypeName(), + TypedMetadata::createMetadata); +} + +template +inline void +TypedMetadata::unregisterType() +{ + Metadata::unregisterType(TypedMetadata::staticTypeName()); +} + +template +inline bool +TypedMetadata::isRegisteredType() +{ + return Metadata::isRegisteredType(TypedMetadata::staticTypeName()); +} + + +template<> +inline std::string +TypedMetadata::str() const +{ + return (mValue ? "true" : "false"); +} + + +inline std::ostream& +operator<<(std::ostream& ostr, const Metadata& metadata) +{ + ostr << metadata.str(); + return ostr; +} + + +typedef TypedMetadata BoolMetadata; +typedef TypedMetadata DoubleMetadata; +typedef TypedMetadata FloatMetadata; +typedef TypedMetadata Int32Metadata; +typedef TypedMetadata Int64Metadata; +typedef TypedMetadata Vec2DMetadata; +typedef TypedMetadata Vec2IMetadata; +typedef TypedMetadata Vec2SMetadata; +typedef TypedMetadata Vec3DMetadata; +typedef TypedMetadata Vec3IMetadata; +typedef TypedMetadata Vec3SMetadata; +typedef TypedMetadata Mat4SMetadata; +typedef TypedMetadata Mat4DMetadata; + +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_METADATA_METADATA_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/metadata/StringMetadata.h b/nuparu/include/openvdb/include/openvdb/metadata/StringMetadata.h new file mode 100644 index 00000000..53030cf1 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/metadata/StringMetadata.h @@ -0,0 +1,75 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + +typedef TypedMetadata StringMetadata; + + +template <> +inline Index32 +StringMetadata::size() const +{ + return Index32(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(&mValue[0]), this->size()); +} + +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_METADATA_STRINGMETADATA_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/openvdb.h b/nuparu/include/openvdb/include/openvdb/openvdb.h new file mode 100644 index 00000000..ef29f51f --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/openvdb.h @@ -0,0 +1,99 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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_OPENVDB_HAS_BEEN_INCLUDED +#define OPENVDB_OPENVDB_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::Type MaskTree; +typedef tree::Tree4::Type BoolTree; +typedef tree::Tree4::Type FloatTree; +typedef tree::Tree4::Type DoubleTree; +typedef tree::Tree4::Type Int32Tree; +typedef tree::Tree4::Type UInt32Tree; +typedef tree::Tree4::Type Int64Tree; +typedef tree::Tree4::Type Vec2ITree; +typedef tree::Tree4::Type Vec2STree; +typedef tree::Tree4::Type Vec2DTree; +typedef tree::Tree4::Type Vec3ITree; +typedef tree::Tree4::Type Vec3STree; +typedef tree::Tree4::Type Vec3DTree; +typedef tree::Tree4::Type StringTree; +typedef MaskTree TopologyTree; +typedef Vec3STree Vec3fTree; +typedef Vec3DTree Vec3dTree; +typedef FloatTree ScalarTree; +typedef Vec3fTree VectorTree; + +/// Common grid types +typedef Grid MaskGrid; +typedef Grid BoolGrid; +typedef Grid FloatGrid; +typedef Grid DoubleGrid; +typedef Grid Int32Grid; +typedef Grid Int64Grid; +typedef Grid Vec3IGrid; +typedef Grid Vec3SGrid; +typedef Grid Vec3DGrid; +typedef Grid StringGrid; +typedef Vec3SGrid Vec3fGrid; +typedef Vec3DGrid Vec3dGrid; +typedef FloatGrid ScalarGrid; +typedef Vec3fGrid VectorGrid; +typedef MaskGrid TopologyGrid; + +/// 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_OPENVDB_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/ChangeBackground.h b/nuparu/include/openvdb/include/openvdb/tools/ChangeBackground.h new file mode 100644 index 00000000..3e1e61d0 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/ChangeBackground.h @@ -0,0 +1,278 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 ChangeBackground.h +/// +/// @brief Efficient multi-threaded replacement of the background +/// values in tree. +/// +/// @author Ken Museth + +#ifndef OPENVDB_TOOLS_ChangeBACKGROUND_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_ChangeBACKGROUND_HAS_BEEN_INCLUDED + +#include // for isNegative and negative +#include // for Index typedef +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Replace the background value in all the nodes of a tree. +/// @details The sign of the background value is preserved, and only +/// inactive values equal to the old background value are replaced. +/// +/// @note If a LeafManager is used the cached leaf nodes are reused, +/// resulting in slightly better overall performance. +/// +/// @param tree Tree (or LeafManager) that will have its background value changed +/// @param background the new background value +/// @param threaded enable or disable threading (threading is enabled by default) +/// @param grainSize used to control the threading granularity (default is 32) +template +inline void +changeBackground( + TreeOrLeafManagerT& tree, + const typename TreeOrLeafManagerT::ValueType& background, + bool threaded = true, + size_t grainSize = 32); + + +/// @brief Replace the background value in all the nodes of a floating-point tree +/// containing a symmetric narrow-band level set. +/// @details All inactive values will be set to +| @a halfWidth | if outside +/// and -| @a halfWidth | if inside, where @a halfWidth is half the width +/// of the symmetric narrow band. +/// +/// @note This method is faster than changeBackground since it does not +/// perform tests to see if inactive values are equal to the old background value. +/// @note If a LeafManager is used the cached leaf nodes are reused, +/// resulting in slightly better overall performance. +/// +/// @param tree Tree (or LeafManager) that will have its background value changed +/// @param halfWidth half of the width of the symmetric narrow band +/// @param threaded enable or disable threading (threading is enabled by default) +/// @param grainSize used to control the threading granularity (default is 32) +/// +/// @throw ValueError if @a halfWidth is negative (as defined by math::isNegative) +template +inline void +changeLevelSetBackground( + TreeOrLeafManagerT& tree, + const typename TreeOrLeafManagerT::ValueType& halfWidth, + bool threaded = true, + size_t grainSize = 32); + + +/// @brief Replace the background values in all the nodes of a floating-point tree +/// containing a possibly asymmetric narrow-band level set. +/// @details All inactive values will be set to +| @a outsideWidth | if outside +/// and -| @a insideWidth | if inside, where @a outsideWidth is the outside +/// width of the narrow band and @a insideWidth is its inside width. +/// +/// @note This method is faster than changeBackground since it does not +/// perform tests to see if inactive values are equal to the old background value. +/// @note If a LeafManager is used the cached leaf nodes are reused, +/// resulting in slightly better overall performance. +/// +/// @param tree Tree (or LeafManager) that will have its background value changed +/// @param outsideWidth The width of the outside of the narrow band +/// @param insideWidth The width of the inside of the narrow band +/// @param threaded enable or disable threading (threading is enabled by default) +/// @param grainSize used to control the threading granularity (default is 32) +/// +/// @throw ValueError if @a outsideWidth is negative or @a insideWidth is +/// not negative (as defined by math::isNegative) +template +inline void +changeAsymmetricLevelSetBackground( + TreeOrLeafManagerT& tree, + const typename TreeOrLeafManagerT::ValueType& outsideWidth, + const typename TreeOrLeafManagerT::ValueType& insideWidth, + bool threaded = true, + size_t grainSize = 32); + + +////////////////////////////////////////////////////// + + +// Replaces the background value in a Tree of any type. +template +class ChangeBackgroundOp +{ +public: + typedef typename TreeOrLeafManagerT::ValueType ValueT; + typedef typename TreeOrLeafManagerT::RootNodeType RootT; + typedef typename TreeOrLeafManagerT::LeafNodeType LeafT; + + + ChangeBackgroundOp(const TreeOrLeafManagerT& tree, const ValueT& newValue) + : mOldValue(tree.root().background()) + , mNewValue(newValue) + { + } + void operator()(RootT& root) const + { + for (typename RootT::ValueOffIter it = root.beginValueOff(); it; ++it) this->set(it); + root.setBackground(mNewValue, false); + } + void operator()(LeafT& node) const + { + for (typename LeafT::ValueOffIter it = node.beginValueOff(); it; ++it) this->set(it); + } + template + void operator()(NodeT& node) const + { + typename NodeT::NodeMaskType mask = node.getValueOffMask(); + for (typename NodeT::ValueOnIter it(mask.beginOn(), &node); it; ++it) this->set(it); + } +private: + + template + inline void set(IterT& iter) const + { + if (math::isApproxEqual(*iter, mOldValue)) { + iter.setValue(mNewValue); + } else if (math::isApproxEqual(*iter, math::negative(mOldValue))) { + iter.setValue(math::negative(mNewValue)); + } + } + const ValueT mOldValue, mNewValue; +};// ChangeBackgroundOp + + +// Replaces the background value in a Tree assumed to represent a +// level set. It is generally faster than ChangeBackgroundOp. +// Note that is follows the sign-convention that outside is positive +// and inside is negative! +template +class ChangeLevelSetBackgroundOp +{ +public: + typedef typename TreeOrLeafManagerT::ValueType ValueT; + typedef typename TreeOrLeafManagerT::RootNodeType RootT; + typedef typename TreeOrLeafManagerT::LeafNodeType LeafT; + + /// @brief Constructor for asymmetric narrow-bands + ChangeLevelSetBackgroundOp(const ValueT& outside, const ValueT& inside) + : mOutside(outside) + , mInside(inside) + { + if (math::isNegative(mOutside)) { + OPENVDB_THROW(ValueError, + "ChangeLevelSetBackgroundOp: the outside value cannot be negative!"); + } + if (!math::isNegative(mInside)) { + OPENVDB_THROW(ValueError, + "ChangeLevelSetBackgroundOp: the inside value must be negative!"); + } + } + void operator()(RootT& root) const + { + for (typename RootT::ValueOffIter it = root.beginValueOff(); it; ++it) this->set(it); + root.setBackground(mOutside, false); + } + void operator()(LeafT& node) const + { + for(typename LeafT::ValueOffIter it = node.beginValueOff(); it; ++it) this->set(it); + } + template + void operator()(NodeT& node) const + { + typedef typename NodeT::ValueOffIter IterT; + for (IterT it(node.getChildMask().beginOff(), &node); it; ++it) this->set(it); + } +private: + + template + inline void set(IterT& iter) const + { + //this is safe since we know ValueType is_floating_point + ValueT& v = const_cast(*iter); + v = v < 0 ? mInside : mOutside; + } + const ValueT mOutside, mInside; +};// ChangeLevelSetBackgroundOp + + +template +inline void +changeBackground( + TreeOrLeafManagerT& tree, + const typename TreeOrLeafManagerT::ValueType& background, + bool threaded, + size_t grainSize) +{ + tree::NodeManager linearTree(tree); + ChangeBackgroundOp op(tree, background); + linearTree.foreachTopDown(op, threaded, grainSize); +} + + +template +inline void +changeAsymmetricLevelSetBackground( + TreeOrLeafManagerT& tree, + const typename TreeOrLeafManagerT::ValueType& outsideValue, + const typename TreeOrLeafManagerT::ValueType& insideValue, + bool threaded, + size_t grainSize) +{ + tree::NodeManager linearTree(tree); + ChangeLevelSetBackgroundOp op(outsideValue, insideValue); + linearTree.foreachTopDown(op, threaded, grainSize); +} + + +// If the narrow-band is symmetric only one background value is required +template +inline void +changeLevelSetBackground( + TreeOrLeafManagerT& tree, + const typename TreeOrLeafManagerT::ValueType& background, + bool threaded, + size_t grainSize) +{ + changeAsymmetricLevelSetBackground( + tree, background, math::negative(background), threaded, grainSize); +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_CHANGEBACKGROUND_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/Clip.h b/nuparu/include/openvdb/include/openvdb/tools/Clip.h new file mode 100644 index 00000000..c3f09d49 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/Clip.h @@ -0,0 +1,413 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 Clip.h +/// +/// @brief Functions to clip a grid against a bounding box or against +/// another grid's active voxel topology + +#ifndef OPENVDB_TOOLS_CLIP_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_CLIP_HAS_BEEN_INCLUDED + +#include // for ValueMask +#include +#include // for isNegative +#include +#include "GridTransformer.h" // for resampleToMatch() +#include +#include +#include +#include +#include +#include "Prune.h" + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Clip the given grid against a world-space bounding box +/// and return a new grid containing the result. +/// @warning Clipping a level set will likely produce a grid that is +/// no longer a valid level set. +template OPENVDB_STATIC_SPECIALIZATION +inline typename GridType::Ptr clip(const GridType& grid, const BBoxd&); + +/// @brief Clip a grid against the active voxels of another grid +/// and return a new grid containing the result. +/// @param grid the grid to be clipped +/// @param mask a grid whose active voxels form a boolean clipping mask +/// @details The mask grid need not have the same transform as the source grid. +/// Also, if the mask grid is a level set, consider using tools::sdfInteriorMask +/// to construct a new mask comprising the interior (rather than the narrow band) +/// of the level set. +/// @warning Clipping a level set will likely produce a grid that is +/// no longer a valid level set. +template OPENVDB_STATIC_SPECIALIZATION +inline typename GridType::Ptr clip(const GridType& grid, const Grid& mask); + + +//////////////////////////////////////// + + +namespace clip_internal { + + +//////////////////////////////////////// + + +template +class MaskInteriorVoxels +{ +public: + typedef typename TreeT::ValueType ValueT; + typedef typename TreeT::LeafNodeType LeafNodeT; + + MaskInteriorVoxels(const TreeT& tree): mAcc(tree) {} + + template + void operator()(LeafNodeType &leaf, size_t /*leafIndex*/) const + { + const LeafNodeT *refLeaf = mAcc.probeConstLeaf(leaf.origin()); + if (refLeaf) { + typename LeafNodeType::ValueOffIter iter = leaf.beginValueOff(); + for ( ; iter; ++iter) { + const Index pos = iter.pos(); + leaf.setActiveState(pos, math::isNegative(refLeaf->getValue(pos))); + } + } + } + +private: + tree::ValueAccessor mAcc; +}; + + +//////////////////////////////////////// + + +template +class CopyLeafNodes +{ +public: + typedef typename TreeT::template ValueConverter::Type MaskTreeT; + typedef tree::LeafManager MaskLeafManagerT; + + CopyLeafNodes(const TreeT& tree, const MaskLeafManagerT& leafNodes); + + void run(bool threaded = true); + + typename TreeT::Ptr tree() const { return mNewTree; } + + CopyLeafNodes(CopyLeafNodes&, tbb::split); + void operator()(const tbb::blocked_range&); + void join(const CopyLeafNodes& rhs) { mNewTree->merge(*rhs.mNewTree); } + +private: + const MaskTreeT* mClipMask; + const TreeT* mTree; + const MaskLeafManagerT* mLeafNodes; + typename TreeT::Ptr mNewTree; +}; + + +template +CopyLeafNodes::CopyLeafNodes(const TreeT& tree, const MaskLeafManagerT& leafNodes) + : mTree(&tree) + , mLeafNodes(&leafNodes) + , mNewTree(new TreeT(mTree->background())) +{ +} + + +template +CopyLeafNodes::CopyLeafNodes(CopyLeafNodes& rhs, tbb::split) + : mTree(rhs.mTree) + , mLeafNodes(rhs.mLeafNodes) + , mNewTree(new TreeT(mTree->background())) +{ +} + + +template +void +CopyLeafNodes::run(bool threaded) +{ + if (threaded) tbb::parallel_reduce(mLeafNodes->getRange(), *this); + else (*this)(mLeafNodes->getRange()); +} + + +template +void +CopyLeafNodes::operator()(const tbb::blocked_range& range) +{ + typedef typename TreeT::LeafNodeType LeafT; + typedef typename MaskTree::LeafNodeType MaskLeafT; + typename MaskLeafT::ValueOnCIter it; + + tree::ValueAccessor acc(*mNewTree); + tree::ValueAccessor refAcc(*mTree); + + for (size_t n = range.begin(); n != range.end(); ++n) { + const MaskLeafT& maskLeaf = mLeafNodes->leaf(n); + const Coord& ijk = maskLeaf.origin(); + const LeafT* refLeaf = refAcc.probeConstLeaf(ijk); + + LeafT* newLeaf = acc.touchLeaf(ijk); + + if (refLeaf) { + for (it = maskLeaf.cbeginValueOn(); it; ++it) { + const Index pos = it.pos(); + newLeaf->setValueOnly(pos, refLeaf->getValue(pos)); + newLeaf->setActiveState(pos, refLeaf->isValueOn(pos)); + } + } else { + typename TreeT::ValueType value; + bool isActive = refAcc.probeValue(ijk, value); + + for (it = maskLeaf.cbeginValueOn(); it; ++it) { + const Index pos = it.pos(); + newLeaf->setValueOnly(pos, value); + newLeaf->setActiveState(pos, isActive); + } + } + } +} + + +//////////////////////////////////////// + + +struct BoolSampler +{ + static const char* name() { return "bin"; } + static int radius() { return 2; } + static bool mipmap() { return false; } + static bool consistent() { return true; } + + template + static bool sample(const TreeT& inTree, + const Vec3R& inCoord, typename TreeT::ValueType& result) + { + Coord ijk; + ijk[0] = int(std::floor(inCoord[0])); + ijk[1] = int(std::floor(inCoord[1])); + ijk[2] = int(std::floor(inCoord[2])); + return inTree.probeValue(ijk, result); + } +}; + + +//////////////////////////////////////// + + +// Convert a grid of one type to a grid of another type +template +struct ConvertGrid +{ + typedef typename FromGridT::Ptr FromGridPtrT; + typedef typename ToGridT::Ptr ToGridPtrT; + ToGridPtrT operator()(const FromGridPtrT& grid) { return ToGridPtrT(new ToGridT(*grid)); } +}; + +// Partial specialization that avoids copying when +// the input and output grid types are the same +template +struct ConvertGrid +{ + typedef typename GridT::Ptr GridPtrT; + GridPtrT operator()(const GridPtrT& grid) { return grid; } +}; + + +//////////////////////////////////////// + + +// Convert a grid of arbitrary type to a mask grid and return a pointer to the new grid. +template +inline typename boost::disable_if, + typename GridT::template ValueConverter::Type::Ptr>::type +convertToMaskGrid(const GridT& grid) +{ + typedef typename GridT::template ValueConverter::Type MaskGridT; + typedef typename MaskGridT::Ptr MaskGridPtrT; + + // Convert the input grid to a boolean mask grid (with the same tree configuration). + MaskGridPtrT mask = MaskGridT::create(/*background=*/false); + mask->topologyUnion(grid); + mask->setTransform(grid.constTransform().copy()); + return mask; +} + +// Overload that avoids any processing if the input grid is already a mask grid +template +inline typename boost::enable_if, + typename GridT::Ptr>::type +convertToMaskGrid(const GridT& grid) +{ + return grid.copy(); // shallow copy +} + + +//////////////////////////////////////// + + +template +inline typename GridType::Ptr +doClip(const GridType& grid, const typename GridType::template ValueConverter::Type& aMask) +{ + typedef typename GridType::TreeType TreeT; + typedef typename GridType::TreeType::template ValueConverter::Type MaskTreeT; + + const GridClass gridClass = grid.getGridClass(); + const TreeT& tree = grid.tree(); + + MaskTreeT mask(false); + mask.topologyUnion(tree); + + if (gridClass == GRID_LEVEL_SET) { + tree::LeafManager leafNodes(mask); + leafNodes.foreach(MaskInteriorVoxels(tree)); + + tree::ValueAccessor acc(tree); + + typename MaskTreeT::ValueAllIter iter(mask); + iter.setMaxDepth(MaskTreeT::ValueAllIter::LEAF_DEPTH - 1); + + for ( ; iter; ++iter) { + iter.setActiveState(math::isNegative(acc.getValue(iter.getCoord()))); + } + } + + mask.topologyIntersection(aMask.constTree()); + + typename GridType::Ptr outGrid; + { + // Copy voxel values and states. + tree::LeafManager leafNodes(mask); + CopyLeafNodes maskOp(tree, leafNodes); + maskOp.run(); + outGrid = GridType::create(maskOp.tree()); + } + { + // Copy tile values and states. + tree::ValueAccessor refAcc(tree); + tree::ValueAccessor maskAcc(mask); + + typename TreeT::ValueAllIter it(outGrid->tree()); + it.setMaxDepth(TreeT::ValueAllIter::LEAF_DEPTH - 1); + for ( ; it; ++it) { + Coord ijk = it.getCoord(); + + if (maskAcc.isValueOn(ijk)) { + typename TreeT::ValueType value; + bool isActive = refAcc.probeValue(ijk, value); + + it.setValue(value); + if (!isActive) it.setValueOff(); + } + } + } + + outGrid->setTransform(grid.transform().copy()); + if (gridClass != GRID_LEVEL_SET) outGrid->setGridClass(gridClass); + + return outGrid; +} + +} // namespace clip_internal + + +//////////////////////////////////////// + + +template +OPENVDB_STATIC_SPECIALIZATION +inline typename GridType::Ptr +clip(const GridType& grid, const BBoxd& bbox) +{ + typedef typename GridType::template ValueConverter::Type MaskGridT; + + // Transform the world-space bounding box into the source grid's index space. + Vec3d idxMin, idxMax; + math::calculateBounds(grid.constTransform(), bbox.min(), bbox.max(), idxMin, idxMax); + CoordBBox region(Coord::floor(idxMin), Coord::floor(idxMax)); + // Construct a boolean mask grid that is true inside the index-space bounding box + // and false everywhere else. + MaskGridT clipMask(/*background=*/false); + clipMask.fill(region, /*value=*/true, /*active=*/true); + + return clip_internal::doClip(grid, clipMask); +} + + +template +OPENVDB_STATIC_SPECIALIZATION +inline typename GridType1::Ptr +clip(const GridType1& grid1, const Grid& grid2) +{ + typedef typename GridType1::template ValueConverter::Type MaskGridT1; + typedef typename MaskGridT1::Ptr MaskGridPtrT1; + + typedef Grid GridType2; + typedef typename GridType2::template ValueConverter::Type MaskGridT2; + typedef typename MaskGridT2::Ptr MaskGridPtrT2; + + // Convert the mask grid to a boolean grid with the same tree configuration. + MaskGridPtrT2 maskGrid = clip_internal::convertToMaskGrid( grid2 ); + + // Resample the boolean mask grid into the source grid's index space. + if (grid1.constTransform() != maskGrid->constTransform()) { + MaskGridPtrT2 resampledMask = MaskGridT2::create(/*background=*/false); + resampledMask->setTransform(grid1.constTransform().copy()); + tools::resampleToMatch(*maskGrid, *resampledMask); + tools::prune(resampledMask->tree()); + maskGrid = resampledMask; + } + + // Convert the bool mask grid to a bool grid of the same configuration as the source grid. + MaskGridPtrT1 clipMask = + clip_internal::ConvertGrid()( maskGrid ); + + // Clip the source grid against the boolean mask grid. + return clip_internal::doClip(grid1, *clipMask); +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_CLIP_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/Composite.h b/nuparu/include/openvdb/include/openvdb/tools/Composite.h new file mode 100644 index 00000000..fe4f6fd3 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/Composite.h @@ -0,0 +1,1107 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +/// +/// @authors Peter Cucka, Mihai Alden + +#ifndef OPENVDB_TOOLS_COMPOSITE_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_COMPOSITE_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include // for isExactlyEqual() +#include "ValueTransformer.h" // for transformValues() +#include "Prune.h"// for prune +#include "SignedFloodFill.h" // for signedFloodFill() +#include + +#include +#include +#include +#include +#include + + +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 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 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 OPENVDB_STATIC_SPECIALIZATION +inline void csgDifference(GridOrTreeT& a, GridOrTreeT& b, bool prune = true); + +/// @brief Threaded CSG union operation that produces a new grid or tree from +/// immutable inputs. +/// @return The CSG union of the @a and @b level set inputs. +template OPENVDB_STATIC_SPECIALIZATION +inline typename GridOrTreeT::Ptr csgUnionCopy(const GridOrTreeT& a, const GridOrTreeT& b); +/// @brief Threaded CSG intersection operation that produces a new grid or tree from +/// immutable inputs. +/// @return The CSG intersection of the @a and @b level set inputs. +template OPENVDB_STATIC_SPECIALIZATION +inline typename GridOrTreeT::Ptr csgIntersectionCopy(const GridOrTreeT& a, const GridOrTreeT& b); +/// @brief Threaded CSG difference operation that produces a new grid or tree from +/// immutable inputs. +/// @return The CSG difference of the @a and @b level set inputs. +template OPENVDB_STATIC_SPECIALIZATION +inline typename GridOrTreeT::Ptr csgDifferenceCopy(const GridOrTreeT& a, const GridOrTreeT& b); + +/// @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 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 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 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 OPENVDB_STATIC_SPECIALIZATION +inline void compMul(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 OPENVDB_STATIC_SPECIALIZATION +inline void compDiv(GridOrTreeT& a, GridOrTreeT& b); + +/// Copy the active voxels of B into A. +template 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 inline +const typename boost::disable_if_c::IsVec, T>::type& // = T if T is not a vector type +min(const T& a, const T& b) { return std::min(a, b); } + +template inline +const typename boost::disable_if_c::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 inline +const typename boost::enable_if_c::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 inline +const typename boost::enable_if_c::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))); +} + + +template inline +typename boost::disable_if, T>::type // = T if T is not an integer type +divide(const T& a, const T& b) { return a / b; } + +template inline +typename boost::enable_if, T>::type // = T if T is an integer type +divide(const T& a, const T& b) +{ + const T zero(0); + if (b != zero) return a / b; + if (a == zero) return 0; + return (a > 0 ? std::numeric_limits::max() : -std::numeric_limits::max()); +} + +// If b is true, return a / 1 = a. +// If b is false and a is true, return 1 / 0 = inf = MAX_BOOL = 1 = a. +// If b is false and a is false, return 0 / 0 = NaN = 0 = a. +inline bool divide(bool a, bool /*b*/) { return a; } + + +enum CSGOperation { CSG_UNION, CSG_INTERSECTION, CSG_DIFFERENCE }; + +template +struct BuildPrimarySegment +{ + typedef typename TreeType::ValueType ValueType; + typedef typename TreeType::Ptr TreePtrType; + typedef typename TreeType::LeafNodeType LeafNodeType; + typedef typename LeafNodeType::NodeMaskType NodeMaskType; + typedef typename TreeType::RootNodeType RootNodeType; + typedef typename RootNodeType::NodeChainType NodeChainType; + typedef typename boost::mpl::at >::type InternalNodeType; + + BuildPrimarySegment(const TreeType& lhs, const TreeType& rhs) + : mSegment(new TreeType(lhs.background())) + , mLhsTree(&lhs) + , mRhsTree(&rhs) + { + } + + void operator()() const + { + std::vector leafNodes; + + { + std::vector internalNodes; + mLhsTree->getNodes(internalNodes); + + ProcessInternalNodes op(internalNodes, *mRhsTree, *mSegment, leafNodes); + tbb::parallel_reduce(tbb::blocked_range(0, internalNodes.size()), op); + } + + ProcessLeafNodes op(leafNodes, *mRhsTree, *mSegment); + tbb::parallel_reduce(tbb::blocked_range(0, leafNodes.size()), op); + } + + TreePtrType& segment() { return mSegment; } + +private: + + struct ProcessInternalNodes { + + ProcessInternalNodes(std::vector& lhsNodes, const TreeType& rhsTree, + TreeType& outputTree, std::vector& outputLeafNodes) + : mLhsNodes(lhsNodes.empty() ? NULL : &lhsNodes.front()) + , mRhsTree(&rhsTree) + , mLocalTree(mRhsTree->background()) + , mOutputTree(&outputTree) + , mLocalLeafNodes() + , mOutputLeafNodes(&outputLeafNodes) + { + } + + ProcessInternalNodes(ProcessInternalNodes& other, tbb::split) + : mLhsNodes(other.mLhsNodes) + , mRhsTree(other.mRhsTree) + , mLocalTree(mRhsTree->background()) + , mOutputTree(&mLocalTree) + , mLocalLeafNodes() + , mOutputLeafNodes(&mLocalLeafNodes) + { + } + + void join(ProcessInternalNodes& other) + { + mOutputTree->merge(*other.mOutputTree); + mOutputLeafNodes->insert(mOutputLeafNodes->end(), + other.mOutputLeafNodes->begin(), other.mOutputLeafNodes->end()); + } + + void operator()(const tbb::blocked_range& range) + { + tree::ValueAccessor rhsAcc(*mRhsTree); + tree::ValueAccessor outputAcc(*mOutputTree); + + std::vector tmpLeafNodes; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + const InternalNodeType& lhsNode = *mLhsNodes[n]; + const Coord& ijk = lhsNode.origin(); + const InternalNodeType * rhsNode = rhsAcc.template probeConstNode(ijk); + + if (rhsNode) { + lhsNode.getNodes(*mOutputLeafNodes); + } else { + if (Operation == CSG_INTERSECTION) { + if (rhsAcc.getValue(ijk) < ValueType(0.0)) { + tmpLeafNodes.clear(); + lhsNode.getNodes(tmpLeafNodes); + for (size_t i = 0, I = tmpLeafNodes.size(); i < I; ++i) { + outputAcc.addLeaf(new LeafNodeType(*tmpLeafNodes[i])); + } + } + } else { // Union & Difference + if (!(rhsAcc.getValue(ijk) < ValueType(0.0))) { + tmpLeafNodes.clear(); + lhsNode.getNodes(tmpLeafNodes); + for (size_t i = 0, I = tmpLeafNodes.size(); i < I; ++i) { + outputAcc.addLeaf(new LeafNodeType(*tmpLeafNodes[i])); + } + } + } + } + } // end range loop + } + + InternalNodeType const * const * const mLhsNodes; + TreeType const * const mRhsTree; + TreeType mLocalTree; + TreeType * const mOutputTree; + + std::vector mLocalLeafNodes; + std::vector * const mOutputLeafNodes; + }; // struct ProcessInternalNodes + + struct ProcessLeafNodes { + + ProcessLeafNodes(std::vector& lhsNodes, const TreeType& rhsTree, TreeType& output) + : mLhsNodes(lhsNodes.empty() ? NULL : &lhsNodes.front()) + , mRhsTree(&rhsTree) + , mLocalTree(mRhsTree->background()) + , mOutputTree(&output) + { + } + + ProcessLeafNodes(ProcessLeafNodes& other, tbb::split) + : mLhsNodes(other.mLhsNodes) + , mRhsTree(other.mRhsTree) + , mLocalTree(mRhsTree->background()) + , mOutputTree(&mLocalTree) + { + } + + void join(ProcessLeafNodes& rhs) { mOutputTree->merge(*rhs.mOutputTree); } + + void operator()(const tbb::blocked_range& range) + { + tree::ValueAccessor rhsAcc(*mRhsTree); + tree::ValueAccessor outputAcc(*mOutputTree); + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + const LeafNodeType& lhsNode = *mLhsNodes[n]; + const Coord& ijk = lhsNode.origin(); + + const LeafNodeType* rhsNodePt = rhsAcc.probeConstLeaf(ijk); + + if (rhsNodePt) { // combine overlapping nodes + + LeafNodeType* outputNode = outputAcc.touchLeaf(ijk); + ValueType * outputData = outputNode->buffer().data(); + NodeMaskType& outputMask = outputNode->getValueMask(); + + const ValueType * lhsData = lhsNode.buffer().data(); + const NodeMaskType& lhsMask = lhsNode.getValueMask(); + + const ValueType * rhsData = rhsNodePt->buffer().data(); + const NodeMaskType& rhsMask = rhsNodePt->getValueMask(); + + if (Operation == CSG_INTERSECTION) { + for (Index pos = 0; pos < LeafNodeType::SIZE; ++pos) { + const bool fromRhs = lhsData[pos] < rhsData[pos]; + outputData[pos] = fromRhs ? rhsData[pos] : lhsData[pos]; + outputMask.set(pos, fromRhs ? rhsMask.isOn(pos) : lhsMask.isOn(pos)); + } + } else if (Operation == CSG_DIFFERENCE){ + for (Index pos = 0; pos < LeafNodeType::SIZE; ++pos) { + const ValueType rhsVal = math::negative(rhsData[pos]); + const bool fromRhs = lhsData[pos] < rhsVal; + outputData[pos] = fromRhs ? rhsVal : lhsData[pos]; + outputMask.set(pos, fromRhs ? rhsMask.isOn(pos) : lhsMask.isOn(pos)); + } + } else { // Union + for (Index pos = 0; pos < LeafNodeType::SIZE; ++pos) { + const bool fromRhs = lhsData[pos] > rhsData[pos]; + outputData[pos] = fromRhs ? rhsData[pos] : lhsData[pos]; + outputMask.set(pos, fromRhs ? rhsMask.isOn(pos) : lhsMask.isOn(pos)); + } + } + + } else { + if (Operation == CSG_INTERSECTION) { + if (rhsAcc.getValue(ijk) < ValueType(0.0)) { + outputAcc.addLeaf(new LeafNodeType(lhsNode)); + } + } else { // Union & Difference + if (!(rhsAcc.getValue(ijk) < ValueType(0.0))) { + outputAcc.addLeaf(new LeafNodeType(lhsNode)); + } + } + } + } // end range loop + } + + LeafNodeType const * const * const mLhsNodes; + TreeType const * const mRhsTree; + TreeType mLocalTree; + TreeType * const mOutputTree; + }; // struct ProcessLeafNodes + + TreePtrType mSegment; + TreeType const * const mLhsTree; + TreeType const * const mRhsTree; +}; // struct BuildPrimarySegment + + +template +struct BuildSecondarySegment +{ + typedef typename TreeType::ValueType ValueType; + typedef typename TreeType::Ptr TreePtrType; + typedef typename TreeType::LeafNodeType LeafNodeType; + typedef typename LeafNodeType::NodeMaskType NodeMaskType; + typedef typename TreeType::RootNodeType RootNodeType; + typedef typename RootNodeType::NodeChainType NodeChainType; + typedef typename boost::mpl::at >::type InternalNodeType; + + BuildSecondarySegment(const TreeType& lhs, const TreeType& rhs) + : mSegment(new TreeType(lhs.background())) + , mLhsTree(&lhs) + , mRhsTree(&rhs) + { + } + + void operator()() const + { + std::vector leafNodes; + + { + std::vector internalNodes; + mRhsTree->getNodes(internalNodes); + + ProcessInternalNodes op(internalNodes, *mLhsTree, *mSegment, leafNodes); + tbb::parallel_reduce(tbb::blocked_range(0, internalNodes.size()), op); + } + + ProcessLeafNodes op(leafNodes, *mLhsTree, *mSegment); + tbb::parallel_reduce(tbb::blocked_range(0, leafNodes.size()), op); + } + + TreePtrType& segment() { return mSegment; } + +private: + + struct ProcessInternalNodes { + + ProcessInternalNodes(std::vector& rhsNodes, const TreeType& lhsTree, + TreeType& outputTree, std::vector& outputLeafNodes) + : mRhsNodes(rhsNodes.empty() ? NULL : &rhsNodes.front()) + , mLhsTree(&lhsTree) + , mLocalTree(mLhsTree->background()) + , mOutputTree(&outputTree) + , mLocalLeafNodes() + , mOutputLeafNodes(&outputLeafNodes) + { + } + + ProcessInternalNodes(ProcessInternalNodes& other, tbb::split) + : mRhsNodes(other.mRhsNodes) + , mLhsTree(other.mLhsTree) + , mLocalTree(mLhsTree->background()) + , mOutputTree(&mLocalTree) + , mLocalLeafNodes() + , mOutputLeafNodes(&mLocalLeafNodes) + { + } + + void join(ProcessInternalNodes& other) + { + mOutputTree->merge(*other.mOutputTree); + mOutputLeafNodes->insert(mOutputLeafNodes->end(), + other.mOutputLeafNodes->begin(), other.mOutputLeafNodes->end()); + } + + void operator()(const tbb::blocked_range& range) + { + tree::ValueAccessor lhsAcc(*mLhsTree); + tree::ValueAccessor outputAcc(*mOutputTree); + + std::vector tmpLeafNodes; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + const InternalNodeType& rhsNode = *mRhsNodes[n]; + const Coord& ijk = rhsNode.origin(); + const InternalNodeType * lhsNode = lhsAcc.template probeConstNode(ijk); + + if (lhsNode) { + rhsNode.getNodes(*mOutputLeafNodes); + } else { + if (Operation == CSG_INTERSECTION) { + if (lhsAcc.getValue(ijk) < ValueType(0.0)) { + tmpLeafNodes.clear(); + rhsNode.getNodes(tmpLeafNodes); + for (size_t i = 0, I = tmpLeafNodes.size(); i < I; ++i) { + outputAcc.addLeaf(new LeafNodeType(*tmpLeafNodes[i])); + } + } + } else if (Operation == CSG_DIFFERENCE) { + if (lhsAcc.getValue(ijk) < ValueType(0.0)) { + tmpLeafNodes.clear(); + rhsNode.getNodes(tmpLeafNodes); + for (size_t i = 0, I = tmpLeafNodes.size(); i < I; ++i) { + LeafNodeType* outputNode = new LeafNodeType(*tmpLeafNodes[i]); + outputNode->negate(); + outputAcc.addLeaf(outputNode); + } + } + } else { // Union + if (!(lhsAcc.getValue(ijk) < ValueType(0.0))) { + tmpLeafNodes.clear(); + rhsNode.getNodes(tmpLeafNodes); + for (size_t i = 0, I = tmpLeafNodes.size(); i < I; ++i) { + outputAcc.addLeaf(new LeafNodeType(*tmpLeafNodes[i])); + } + } + } + } + } // end range loop + } + + InternalNodeType const * const * const mRhsNodes; + TreeType const * const mLhsTree; + TreeType mLocalTree; + TreeType * const mOutputTree; + + std::vector mLocalLeafNodes; + std::vector * const mOutputLeafNodes; + }; // struct ProcessInternalNodes + + struct ProcessLeafNodes { + + ProcessLeafNodes(std::vector& rhsNodes, const TreeType& lhsTree, TreeType& output) + : mRhsNodes(rhsNodes.empty() ? NULL : &rhsNodes.front()) + , mLhsTree(&lhsTree) + , mLocalTree(mLhsTree->background()) + , mOutputTree(&output) + { + } + + ProcessLeafNodes(ProcessLeafNodes& rhs, tbb::split) + : mRhsNodes(rhs.mRhsNodes) + , mLhsTree(rhs.mLhsTree) + , mLocalTree(mLhsTree->background()) + , mOutputTree(&mLocalTree) + { + } + + void join(ProcessLeafNodes& rhs) { mOutputTree->merge(*rhs.mOutputTree); } + + void operator()(const tbb::blocked_range& range) + { + tree::ValueAccessor lhsAcc(*mLhsTree); + tree::ValueAccessor outputAcc(*mOutputTree); + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + const LeafNodeType& rhsNode = *mRhsNodes[n]; + const Coord& ijk = rhsNode.origin(); + + const LeafNodeType* lhsNode = lhsAcc.probeConstLeaf(ijk); + + if (!lhsNode) { + if (Operation == CSG_INTERSECTION) { + if (lhsAcc.getValue(ijk) < ValueType(0.0)) { + outputAcc.addLeaf(new LeafNodeType(rhsNode)); + } + } else if (Operation == CSG_DIFFERENCE) { + if (lhsAcc.getValue(ijk) < ValueType(0.0)) { + LeafNodeType* outputNode = new LeafNodeType(rhsNode); + outputNode->negate(); + outputAcc.addLeaf(outputNode); + } + } else { // Union + if (!(lhsAcc.getValue(ijk) < ValueType(0.0))) { + outputAcc.addLeaf(new LeafNodeType(rhsNode)); + } + } + } + } // end range loop + } + + LeafNodeType const * const * const mRhsNodes; + TreeType const * const mLhsTree; + TreeType mLocalTree; + TreeType * const mOutputTree; + }; // struct ProcessLeafNodes + + TreePtrType mSegment; + TreeType const * const mLhsTree; + TreeType const * const mRhsTree; +}; // struct BuildSecondarySegment + + +template +inline typename TreeType::Ptr +doCSGCopy(const TreeType& lhs, const TreeType& rhs) +{ + BuildPrimarySegment primary(lhs, rhs); + BuildSecondarySegment secondary(lhs, rhs); + + // Exploiting nested parallelism + tbb::task_group tasks; + tasks.run(primary); + tasks.run(secondary); + tasks.wait(); + + primary.segment()->merge(*secondary.segment()); + + // The leafnode (level = 0) sign is set in the segment construction. + tools::signedFloodFill(*primary.segment(), /*threaded=*/true, /*grainSize=*/1, /*minLevel=*/1); + + return primary.segment(); +} + + +//////////////////////////////////////// + + +template +struct GridOrTreeConstructor +{ + typedef typename TreeType::Ptr TreeTypePtr; + static TreeTypePtr construct(const TreeType&, TreeTypePtr& tree) { return tree; } +}; + + +template +struct GridOrTreeConstructor > +{ + typedef Grid GridType; + typedef typename Grid::Ptr GridTypePtr; + typedef typename TreeType::Ptr TreeTypePtr; + + static GridTypePtr construct(const GridType& grid, TreeTypePtr& tree) { + GridTypePtr maskGrid(GridType::create(tree)); + maskGrid->setTransform(grid.transform().copy()); + maskGrid->insertMeta(grid); + return maskGrid; + } +}; + + +//////////////////////////////////////// + + +} // namespace composite + + +template +OPENVDB_STATIC_SPECIALIZATION inline void +compMax(GridOrTreeT& aTree, GridOrTreeT& bTree) +{ + typedef TreeAdapter Adapter; + typedef typename Adapter::TreeType TreeT; + typedef typename TreeT::ValueType ValueT; + struct Local { + static inline void op(CombineArgs& args) { + args.setResult(composite::max(args.a(), args.b())); + } + }; + Adapter::tree(aTree).combineExtended(Adapter::tree(bTree), Local::op, /*prune=*/false); +} + + +template +OPENVDB_STATIC_SPECIALIZATION inline void +compMin(GridOrTreeT& aTree, GridOrTreeT& bTree) +{ + typedef TreeAdapter Adapter; + typedef typename Adapter::TreeType TreeT; + typedef typename TreeT::ValueType ValueT; + struct Local { + static inline void op(CombineArgs& args) { + args.setResult(composite::min(args.a(), args.b())); + } + }; + Adapter::tree(aTree).combineExtended(Adapter::tree(bTree), Local::op, /*prune=*/false); +} + + +template +OPENVDB_STATIC_SPECIALIZATION inline void +compSum(GridOrTreeT& aTree, GridOrTreeT& bTree) +{ + typedef TreeAdapter Adapter; + typedef typename Adapter::TreeType TreeT; + struct Local { + static inline void op(CombineArgs& args) { + args.setResult(args.a() + args.b()); + } + }; + Adapter::tree(aTree).combineExtended(Adapter::tree(bTree), Local::op, /*prune=*/false); +} + + +template +OPENVDB_STATIC_SPECIALIZATION inline void +compMul(GridOrTreeT& aTree, GridOrTreeT& bTree) +{ + typedef TreeAdapter Adapter; + typedef typename Adapter::TreeType TreeT; + struct Local { + static inline void op(CombineArgs& args) { + args.setResult(args.a() * args.b()); + } + }; + Adapter::tree(aTree).combineExtended(Adapter::tree(bTree), Local::op, /*prune=*/false); +} + + +template +OPENVDB_STATIC_SPECIALIZATION inline void +compDiv(GridOrTreeT& aTree, GridOrTreeT& bTree) +{ + typedef TreeAdapter Adapter; + typedef typename Adapter::TreeType TreeT; + struct Local { + static inline void op(CombineArgs& args) { + args.setResult(composite::divide(args.a(), args.b())); + } + }; + Adapter::tree(aTree).combineExtended(Adapter::tree(bTree), Local::op, /*prune=*/false); +} + + +//////////////////////////////////////// + + +template +struct CompReplaceOp +{ + TreeT* const aTree; + + CompReplaceOp(TreeT& _aTree): aTree(&_aTree) {} + + /// @note fill operation is not thread safe + 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 acc(*aTree); + for (typename TreeT::LeafCIter::LeafNodeT::ValueOnCIter iter = + leafIter->cbeginValueOn(); iter; ++iter) + { + acc.setValue(iter.getCoord(), *iter); + } + } +}; + + +template +OPENVDB_STATIC_SPECIALIZATION inline void +compReplace(GridOrTreeT& aTree, const GridOrTreeT& bTree) +{ + typedef TreeAdapter 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 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, /*threaded=*/false); + + // 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 +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(math::negative(mAOutside)), + mBOutside(bTree.background()), + mBInside(math::negative(mBOutside)) + { + const ValueT zero = zeroVal(); + 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 +struct CsgUnionVisitor: public CsgVisitorBase +{ + typedef TreeType TreeT; + typedef typename TreeT::ValueType ValueT; + typedef typename TreeT::LeafNodeType::ChildAllIter ChildIterT; + + enum { STOP = CsgVisitorBase::STOP }; + + CsgUnionVisitor(const TreeT& a, const TreeT& b): CsgVisitorBase(a, b) {} + + /// Don't process nodes that are at different tree levels. + template + inline int operator()(AIterT&, BIterT&) { return 0; } + + /// Process root and internal nodes. + template + inline int operator()(IterT& aIter, IterT& bIter) + { + ValueT aValue = zeroVal(); + typename IterT::ChildNodeType* aChild = aIter.probeChild(aValue); + if (!aChild && aValue < zeroVal()) { + // A is an inside tile. Leave it alone and stop traversing this branch. + return STOP; + } + + ValueT bValue = zeroVal(); + typename IterT::ChildNodeType* bChild = bIter.probeChild(bValue); + if (!bChild && bValue < zeroVal()) { + // 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()) { + // 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 +struct CsgIntersectVisitor: public CsgVisitorBase +{ + typedef TreeType TreeT; + typedef typename TreeT::ValueType ValueT; + typedef typename TreeT::LeafNodeType::ChildAllIter ChildIterT; + + enum { STOP = CsgVisitorBase::STOP }; + + CsgIntersectVisitor(const TreeT& a, const TreeT& b): CsgVisitorBase(a, b) {} + + /// Don't process nodes that are at different tree levels. + template + inline int operator()(AIterT&, BIterT&) { return 0; } + + /// Process root and internal nodes. + template + inline int operator()(IterT& aIter, IterT& bIter) + { + ValueT aValue = zeroVal(); + typename IterT::ChildNodeType* aChild = aIter.probeChild(aValue); + if (!aChild && !(aValue < zeroVal())) { + // A is an outside tile. Leave it alone and stop traversing this branch. + return STOP; + } + + ValueT bValue = zeroVal(); + typename IterT::ChildNodeType* bChild = bIter.probeChild(bValue); + if (!bChild && !(bValue < zeroVal())) { + // 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()) { + // 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 +struct CsgDiffVisitor: public CsgVisitorBase +{ + typedef TreeType TreeT; + typedef typename TreeT::ValueType ValueT; + typedef typename TreeT::LeafNodeType::ChildAllIter ChildIterT; + + enum { STOP = CsgVisitorBase::STOP }; + + CsgDiffVisitor(const TreeT& a, const TreeT& b): CsgVisitorBase(a, b) {} + + /// Don't process nodes that are at different tree levels. + template + inline int operator()(AIterT&, BIterT&) { return 0; } + + /// Process root and internal nodes. + template + inline int operator()(IterT& aIter, IterT& bIter) + { + ValueT aValue = zeroVal(); + typename IterT::ChildNodeType* aChild = aIter.probeChild(aValue); + if (!aChild && !(aValue < zeroVal())) { + // A is an outside tile. Leave it alone and stop traversing this branch. + return STOP; + } + + ValueT bValue = zeroVal(); + typename IterT::ChildNodeType* bChild = bIter.probeChild(bValue); + if (!bChild && bValue < zeroVal()) { + // 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()) { + // 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 = math::negative(bValue); + if (aValue < bValue) { // a = max(a, -b) + aIter.setValue(bValue); + aIter.setValueOn(bIter.isValueOn()); + } + return 0; + } +}; + + +//////////////////////////////////////// + + +template +OPENVDB_STATIC_SPECIALIZATION inline void +csgUnion(GridOrTreeT& a, GridOrTreeT& b, bool prune) +{ + typedef TreeAdapter Adapter; + typedef typename Adapter::TreeType TreeT; + TreeT &aTree = Adapter::tree(a), &bTree = Adapter::tree(b); + CsgUnionVisitor visitor(aTree, bTree); + aTree.visit2(bTree, visitor); + if (prune) tools::pruneLevelSet(aTree); +} + +template +OPENVDB_STATIC_SPECIALIZATION inline void +csgIntersection(GridOrTreeT& a, GridOrTreeT& b, bool prune) +{ + typedef TreeAdapter Adapter; + typedef typename Adapter::TreeType TreeT; + TreeT &aTree = Adapter::tree(a), &bTree = Adapter::tree(b); + CsgIntersectVisitor visitor(aTree, bTree); + aTree.visit2(bTree, visitor); + if (prune) tools::pruneLevelSet(aTree); +} + +template +OPENVDB_STATIC_SPECIALIZATION inline void +csgDifference(GridOrTreeT& a, GridOrTreeT& b, bool prune) +{ + typedef TreeAdapter Adapter; + typedef typename Adapter::TreeType TreeT; + TreeT &aTree = Adapter::tree(a), &bTree = Adapter::tree(b); + CsgDiffVisitor visitor(aTree, bTree); + aTree.visit2(bTree, visitor); + if (prune) tools::pruneLevelSet(aTree); +} + + +template +OPENVDB_STATIC_SPECIALIZATION inline typename GridOrTreeT::Ptr +csgUnionCopy(const GridOrTreeT& a, const GridOrTreeT& b) +{ + typedef TreeAdapter Adapter; + typedef typename Adapter::TreeType::Ptr TreePtrT; + + TreePtrT output = composite::doCSGCopy( + Adapter::tree(a), Adapter::tree(b)); + + return composite::GridOrTreeConstructor::construct(a, output); +} + + +template +OPENVDB_STATIC_SPECIALIZATION inline typename GridOrTreeT::Ptr +csgIntersectionCopy(const GridOrTreeT& a, const GridOrTreeT& b) +{ + typedef TreeAdapter Adapter; + typedef typename Adapter::TreeType::Ptr TreePtrT; + + TreePtrT output = composite::doCSGCopy( + Adapter::tree(a), Adapter::tree(b)); + + return composite::GridOrTreeConstructor::construct(a, output); +} + + +template +OPENVDB_STATIC_SPECIALIZATION inline typename GridOrTreeT::Ptr +csgDifferenceCopy(const GridOrTreeT& a, const GridOrTreeT& b) +{ + typedef TreeAdapter Adapter; + typedef typename Adapter::TreeType::Ptr TreePtrT; + + TreePtrT output = composite::doCSGCopy( + Adapter::tree(a), Adapter::tree(b)); + + return composite::GridOrTreeConstructor::construct(a, output); +} + + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_COMPOSITE_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/Dense.h b/nuparu/include/openvdb/include/openvdb/tools/Dense.h new file mode 100644 index 00000000..e4c091ec --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/Dense.h @@ -0,0 +1,608 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include +#include +#include +#include +#include +#include +#include +#include "Prune.h" + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @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 +void +copyToDense( + const GridOrTreeT& sparse, + DenseT& 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 +void +copyFromDense( + const DenseT& dense, + GridOrTreeT& sparse, + const typename GridOrTreeT::ValueType& tolerance, + bool serial = false); + + +//////////////////////////////////////// + +/// We currently support the following two 3D memory layouts for dense +/// volumes: XYZ, i.e. x is the fastest moving index, and ZYX, i.e. z +/// is the fastest moving index. The ZYX memory layout leads to nested +/// for-loops of the order x, y, z, which we find to be the most +/// intuitive. Hence, ZYX is the layout used throughout VDB. However, +/// other data structures, e.g. Houdini and Maya, employ the XYZ +/// layout. Clearly a dense volume with the ZYX layout converts more +/// efficiently to a VDB, but we support both for convenience. +enum MemoryLayout { LayoutXYZ, LayoutZYX }; + +/// @brief Base class for Dense which is defined below. +/// @note The constructor of this class is protected to prevent direct +/// instantiation. +template class DenseBase; + +/// @brief Partial template specialization of DenseBase. +/// @note ZYX is the memory-layout in VDB. It leads to nested +/// for-loops of the order x, y, z which we find to be the most intuitive. +template +class DenseBase +{ +public: + /// @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. + /// + /// @warning The input coordinates are assume to be relative to + /// the grid's origin, i.e. minimum of its index bounding box! + inline size_t coordToOffset(size_t i, size_t j, size_t k) const { return i*mX + j*mY + k; } + + /// @brief Return the local coordinate corresponding to the specified linear offset. + /// + /// @warning The returned coordinate is relative to the origin of this + /// grid's bounding box so add dense.origin() to get absolute coordinates. + inline Coord offsetToLocalCoord(size_t n) const + { + const size_t x = n / mX; + n -= mX*x; + const size_t y = n / mY; + return Coord(Coord::ValueType(x), Coord::ValueType(y), Coord::ValueType(n - mY*y)); + } + + /// @brief Return the stride of the array in the x direction ( = dimY*dimZ). + /// @note This method is required by both CopyToDense and CopyFromDense. + inline 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. + inline size_t yStride() const { return mY; } + + /// @brief Return the stride of the array in the z direction ( = 1). + /// @note This method is required by both CopyToDense and CopyFromDense. + static size_t zStride() { return 1; } + +protected: + /// Protected constructor so as to prevent direct instantiation + DenseBase(const CoordBBox& bbox) : mBBox(bbox), mY(bbox.dim()[2]), mX(mY*bbox.dim()[1]) {} + + const CoordBBox mBBox;//signed coordinates of the domain represented by the grid + const size_t mY, mX;//strides in the y and x direction +};// end of DenseBase + +/// @brief Partial template specialization of DenseBase. +/// @note This is the memory-layout employed in Houdini and Maya. It leads +/// to nested for-loops of the order z, y, x. +template +class DenseBase +{ +public: + /// @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. + /// + /// @warning The input coordinates are assume to be relative to + /// the grid's origin, i.e. minimum of its index bounding box! + inline size_t coordToOffset(size_t i, size_t j, size_t k) const { return i + j*mY + k*mZ; } + + /// @brief Return the index coordinate corresponding to the specified linear offset. + /// + /// @warning The returned coordinate is relative to the origin of this + /// grid's bounding box so add dense.origin() to get absolute coordinates. + inline Coord offsetToLocalCoord(size_t n) const + { + const size_t z = n / mZ; + n -= mZ*z; + const size_t y = n / mY; + return Coord(Coord::ValueType(n - mY*y), Coord::ValueType(y), Coord::ValueType(z)); + } + + /// @brief Return the stride of the array in the x direction ( = 1). + /// @note This method is required by both CopyToDense and CopyFromDense. + static size_t xStride() { return 1; } + + /// @brief Return the stride of the array in the y direction ( = dimX). + /// @note This method is required by both CopyToDense and CopyFromDense. + inline size_t yStride() const { return mY; } + + /// @brief Return the stride of the array in the y direction ( = dimX*dimY). + /// @note This method is required by both CopyToDense and CopyFromDense. + inline size_t zStride() const { return mZ; } + +protected: + /// Protected constructor so as to prevent direct instantiation + DenseBase(const CoordBBox& bbox) : mBBox(bbox), mY(bbox.dim()[0]), mZ(mY*bbox.dim()[1]) {} + + const CoordBBox mBBox;//signed coordinates of the domain represented by the grid + const size_t mY, mZ;//strides in the y and z direction +};// end of DenseBase + +/// @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 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 allows for the 3D memory layout to be +/// defined by the MemoryLayout template parameter (see above for definition). +/// The default memory layout is ZYX since that's the layout used by OpenVDB grids. +template +class Dense : public DenseBase +{ +public: + typedef ValueT ValueType; + typedef DenseBase BaseT; + typedef boost::shared_ptr Ptr; + typedef boost::shared_ptr ConstPtr; + + /// @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. + /// @note The min and max coordinates of the bounding box are inclusive. + Dense(const CoordBBox& bbox) : BaseT(bbox) { this->init(); } + + /// @brief Construct a dense grid with a given range of coordinates and initial value + /// + /// @param bbox the bounding box of the (signed) coordinate range of this grid + /// @param value the initial value of the grid. + /// @throw ValueError if the bounding box is empty. + /// @note The min and max coordinates of the bounding box are inclusive. + Dense(const CoordBBox& bbox, const ValueT& value) : BaseT(bbox) + { + this->init(); + this->fill(value); + } + + /// @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. + /// @note The min and max coordinates of the bounding box are inclusive. + Dense(const CoordBBox& bbox, ValueT* data) : BaseT(bbox), mData(data) + { + if (BaseT::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. + /// @note The @a min coordinate is inclusive, and the max coordinate will be + /// @a min + @a dim - 1. + Dense(const Coord& dim, const Coord& min = Coord(0)) + : BaseT(CoordBBox(min, min+dim.offsetBy(-1))) + { + this->init(); + } + + /// @brief Return the memory layout for this grid (see above for definitions). + static MemoryLayout memoryLayout() { return Layout; } + + /// @brief Return a raw pointer to this grid's value array. + /// @note This method is required by CopyToDense. + inline ValueT* data() { return mData; } + + /// @brief Return a raw pointer to this grid's value array. + /// @note This method is required by CopyFromDense. + inline 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. + inline const CoordBBox& bbox() const { return BaseT::mBBox; } + + /// Return the grid's origin in index coordinates. + inline const Coord& origin() const { return BaseT::mBBox.min(); } + + /// @brief Return the number of voxels contained in this grid. + inline Index64 valueCount() const { return BaseT::mBBox.volume(); } + + /// @brief Set the value of the voxel at the given array offset. + inline void setValue(size_t offset, const ValueT& value) { mData[offset] = value; } + + /// @brief Return a const reference to the value of the voxel at the given array offset. + const ValueT& getValue(size_t offset) const { return mData[offset]; } + + /// @brief Return a non-const reference to the value of the voxel at the given array offset. + ValueT& getValue(size_t offset) { 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. + inline void setValue(size_t i, size_t j, size_t k, const ValueT& value) + { + mData[BaseT::coordToOffset(i,j,k)] = value; + } + + /// @brief Return a const reference to the value of the voxel at unsigned index coordinates (i, j, k). + /// @note This is somewhat slower than using an array offset. + inline const ValueT& getValue(size_t i, size_t j, size_t k) const + { + return mData[BaseT::coordToOffset(i,j,k)]; + } + + /// @brief Return a non-const reference to the value of the voxel at unsigned index coordinates (i, j, k). + /// @note This is somewhat slower than using an array offset. + inline ValueT& getValue(size_t i, size_t j, size_t k) + { + return mData[BaseT::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. + inline void setValue(const Coord& xyz, const ValueT& value) + { + mData[this->coordToOffset(xyz)] = value; + } + + /// @brief Return a const reference to the value of the voxel at the given signed coordinates. + /// @note This is slower than using either an array offset or unsigned index coordinates. + inline const ValueT& getValue(const Coord& xyz) const + { + return mData[this->coordToOffset(xyz)]; + } + + /// @brief Return a non-const reference to the value of the voxel at the given signed coordinates. + /// @note This is slower than using either an array offset or unsigned index coordinates. + inline ValueT& getValue(const Coord& xyz) + { + return mData[this->coordToOffset(xyz)]; + } + + /// @brief Fill this grid with a constant value. + inline 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 + /// 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(const Coord& xyz) const + { + assert(BaseT::mBBox.isInside(xyz)); + return BaseT::coordToOffset(size_t(xyz[0]-BaseT::mBBox.min()[0]), + size_t(xyz[1]-BaseT::mBBox.min()[1]), + size_t(xyz[2]-BaseT::mBBox.min()[2])); + } + + /// @brief Return the global coordinate corresponding to the specified linear offset. + inline Coord offsetToCoord(size_t n) const + { + return this->offsetToLocalCoord(n) + BaseT::mBBox.min(); + } + + /// @brief Return the memory footprint of this Dense grid in bytes. + inline Index64 memUsage() const + { + return sizeof(*this) + BaseT::mBBox.volume() * sizeof(ValueType); + } + + /// @brief Output a human-readable description of this grid to the + /// specified stream. + void print(const std::string& name = "", std::ostream& os = std::cout) const + { + const Coord dim = BaseT::mBBox.dim(); + os << "Dense Grid"; + if (!name.empty()) os << " \"" << name << "\""; + util::printBytes(os, this->memUsage(), ":\n Memory footprint: "); + os << " Dimensions of grid : " << dim[0] << " x " << dim[1] << " x " << dim[2] << "\n"; + os << " Number of voxels: " << util::formattedInt(this->valueCount()) << "\n"; + os << " Bounding box of voxels: " << BaseT::mBBox << "\n"; + os << " Memory layout: " << (Layout == LayoutZYX ? "ZYX (" : "XYZ (dis") + << "similar to VDB)\n"; + } + +private: + + /// @brief Private method to initialize the dense value array. + void init() + { + if (BaseT::mBBox.empty()) { + OPENVDB_THROW(ValueError, "can't construct a dense grid with an empty bounding box"); + } + mArray.reset(new ValueT[BaseT::mBBox.volume()]); + mData = mArray.get(); + } + + boost::scoped_array mArray; + ValueT* mData;//raw c-style pointer to values +};// end of Dense + +//////////////////////////////////////// + + +/// @brief Copy an OpenVDB tree into an existing dense grid. +/// +/// @note Only voxels that intersect the dense grid's bounding box are copied +/// from the OpenVDB tree. But both active and inactive voxels are copied, +/// so all existing values in the dense grid are overwritten, regardless of +/// the OpenVDB tree's topology. +template > +class CopyToDense +{ +public: + typedef _DenseT DenseT; + typedef _TreeT TreeT; + typedef typename TreeT::ValueType ValueT; + + CopyToDense(const TreeT& tree, DenseT& 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; + DenseT* mDense; +};// CopyToDense + + +// Convenient wrapper function for the CopyToDense class +template +void +copyToDense(const GridOrTreeT& sparse, DenseT& dense, bool serial) +{ + typedef TreeAdapter Adapter; + typedef typename Adapter::TreeType TreeT; + + CopyToDense 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 > +class CopyFromDense +{ +public: + typedef _DenseT DenseT; + typedef _TreeT TreeT; + typedef typename TreeT::ValueType ValueT; + typedef typename TreeT::LeafNodeType LeafT; + typedef tree::ValueAccessor AccessorT; + + CopyFromDense(const DenseT& dense, TreeT& tree, const ValueT& tolerance) + : mDense(&dense), + mTree(&tree), + mBlocks(NULL), + mTolerance(tolerance), + mAccessor(tree.empty() ? NULL : new AccessorT(tree)) + { + } + CopyFromDense(const CopyFromDense& other) + : mDense(other.mDense), + mTree(other.mTree), + mBlocks(other.mBlocks), + mTolerance(other.mTolerance), + mAccessor(other.mAccessor.get() == NULL ? NULL : new AccessorT(*mTree)) + { + } + + /// @brief Copy values from the dense grid to the sparse tree. + void copy(bool serial = false) + { + mBlocks = new std::vector(); + const CoordBBox& bbox = mDense->bbox(); + // Pre-process: Construct a list of blocks aligned 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)); + mBlocks->push_back(Block(sub)); + } + } + } + + // Multi-threaded process: Convert dense grid into leaf nodes and tiles + if (serial) { + (*this)(tbb::blocked_range(0, mBlocks->size())); + } else { + tbb::parallel_for(tbb::blocked_range(0, mBlocks->size()), *this); + } + + // Post-process: Insert leaf nodes and tiles into the tree, and prune the tiles only! + tree::ValueAccessor acc(*mTree); + for (size_t m=0, size = mBlocks->size(); m &r) const + { + assert(mBlocks); + LeafT* leaf = new LeafT(); + + for (size_t m=r.begin(), n=0, end = r.end(); m != end; ++m, ++n) { + + Block& block = (*mBlocks)[m]; + const CoordBBox &bbox = block.bbox; + + if (mAccessor.get() == NULL) {//i.e. empty target tree + leaf->fill(mTree->background(), false); + } else {//account for existing leaf nodes in the target tree + if (const LeafT* target = mAccessor->probeConstLeaf(bbox.min())) { + (*leaf) = (*target); + } else { + ValueT value = zeroVal(); + bool state = mAccessor->probeValue(bbox.min(), value); + leaf->fill(value, state); + } + } + + leaf->copyFromDense(bbox, *mDense, mTree->background(), mTolerance); + + if (!leaf->isConstant(block.tile.first, block.tile.second, mTolerance)) { + leaf->setOrigin(bbox.min() & (~(LeafT::DIM - 1))); + block.leaf = leaf; + leaf = new LeafT(); + } + }// loop over blocks + + delete leaf; + } + +private: + struct Block { + CoordBBox bbox; + LeafT* leaf; + std::pair tile; + Block(const CoordBBox& b) : bbox(b), leaf(NULL) {} + }; + + const DenseT* mDense; + TreeT* mTree; + std::vector* mBlocks; + ValueT mTolerance; + boost::scoped_ptr mAccessor; +};// CopyFromDense + + +// Convenient wrapper function for the CopyFromDense class +template +void +copyFromDense(const DenseT& dense, GridOrTreeT& sparse, + const typename GridOrTreeT::ValueType& tolerance, bool serial) +{ + typedef TreeAdapter Adapter; + typedef typename Adapter::TreeType TreeT; + + CopyFromDense 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-2016 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/nuparu/include/openvdb/include/openvdb/tools/DenseSparseTools.h b/nuparu/include/openvdb/include/openvdb/tools/DenseSparseTools.h new file mode 100644 index 00000000..c8ec5e75 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/DenseSparseTools.h @@ -0,0 +1,1259 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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_DENSESPARSETOOLS_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_DENSESPARSETOOLS_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include +#include "Dense.h" + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Selectively extract and transform data from a dense grid, producing a +/// sparse tree with leaf nodes only (e.g. create a tree from the square +/// of values greater than a cutoff.) +/// @param dense A dense grid that acts as a data source +/// @param functor A functor that selects and transforms data for output +/// @param background The background value of the resulting sparse grid +/// @param threaded Option to use threaded or serial code path +/// @return @c Ptr to tree with the valuetype and configuration defined +/// by typedefs in the @c functor. +/// @note To achieve optimal sparsity consider calling the prune() +/// method on the result. +/// @note To simply copy the all the data from a Dense grid to a +/// OpenVDB Grid, use tools::copyFromDense() for better performance. +/// +/// The type of the sparse tree is determined by the specified OtpType +/// functor by means of the typedef OptType::ResultTreeType +/// +/// The OptType function is responsible for the the transformation of +/// dense grid data to sparse grid data on a per-voxel basis. +/// +/// Only leaf nodes with active values will be added to the sparse grid. +/// +/// The OpType must struct that defines a the minimal form +/// @code +/// struct ExampleOp +/// { +/// typedef DesiredTreeType ResultTreeType; +/// +/// template +/// void OpType::operator() (const DenseValueType a, const IndexOrCoord& ijk, +/// ResultTreeType::LeafNodeType* leaf); +/// }; +/// @endcode +/// +/// For example, to generate a tree with valuesOn +/// at locations greater than a given maskvalue +/// @code +/// template +/// class Rule +/// { +/// public: +/// // Standard tree type (e.g. MaskTree or FloatTree in openvdb.h) +/// typedef typename openvdb::tree::Tree4::Type ResultTreeType; +/// +/// typedef typename ResultTreeType::LeafNodeType ResultLeafNodeType; +/// typedef typename ResultTreeType::ValueType ResultValueType; +/// +/// typedef float DenseValueType; +/// +/// typedef vdbmath::Coord::ValueType Index; +/// +/// Rule(const DenseValueType& value): mMaskValue(value){}; +/// +/// template +/// void operator()(const DenseValueType& a, const IndexOrCoord& offset, +/// ResultLeafNodeType* leaf) const +/// { +/// if (a > mMaskValue) { +/// leaf->setValueOn(offset, a); +/// } +/// } +/// +/// private: +/// const DenseValueType mMaskValue; +/// }; +/// @endcode +template +typename OpType::ResultTreeType::Ptr +extractSparseTree(const DenseType& dense, const OpType& functor, + const typename OpType::ResultValueType& background, + bool threaded = true); + +/// This struct that aids template resolution of a new tree type +/// has the same configuration at TreeType, but the ValueType from +/// DenseType. +template struct DSConverter { + typedef typename DenseType::ValueType ValueType; + + typedef typename TreeType::template ValueConverter::Type Type; +}; + + +/// @brief Copy data from the intersection of a sparse tree and a dense input grid. +/// The resulting tree has the same configuration as the sparse tree, but holds +/// the data type specified by the dense input. +/// @param dense A dense grid that acts as a data source +/// @param mask The active voxels and tiles intersected with dense define iteration mask +/// @param background The background value of the resulting sparse grid +/// @param threaded Option to use threaded or serial code path +/// @return @c Ptr to tree with the same configuration as @c mask but of value type +/// defined by @c dense. +template +typename DSConverter::Type::Ptr +extractSparseTreeWithMask(const DenseType& dense, + const MaskTreeType& mask, + const typename DenseType::ValueType& background, + bool threaded = true); + + +/// Apply a point-wise functor to the intersection of a dense grid and a given bounding box +/// @param dense A dense grid to be transformed +/// @param bbox Index space bounding box, define region where the transformation is applied +/// @param op A functor that acts on the dense grid value type +/// @param parallel Used to select multithreaded or single threaded +/// Minimally, the @c op class has to support a @c operator() method, +/// @code +/// // Square values in a grid +/// struct Op +/// { +/// ValueT operator()(const ValueT& in) const +/// { +/// // do work +/// ValueT result = in * in; +/// +/// return result; +/// } +/// }; +/// @endcode +/// NB: only Dense grids with memory layout zxy are supported +template +void transformDense(Dense& dense, + const openvdb::CoordBBox& bbox, const OpType& op, bool parallel=true); + +/// We currrently support the following operations when compositing sparse +/// data into a dense grid. +enum DSCompositeOp { + DS_OVER, DS_ADD, DS_SUB, DS_MIN, DS_MAX, DS_MULT, DS_SET +}; + +/// @brief Composite data from a sparse tree into a dense array of the same value type. +/// @param dense Dense grid to be altered by the operation +/// @param source Sparse data to composite into @c dense +/// @param alpha Sparse Alpha mask used in compositing operations. +/// @param beta Constant multiplier on src +/// @param strength Constant multiplier on alpha +/// @param threaded Enable threading for this operation. +template +void compositeToDense(Dense& dense, + const TreeT& source, + const TreeT& alpha, + const typename TreeT::ValueType beta, + const typename TreeT::ValueType strength, + bool threaded = true); + + +/// @brief Functor-based class used to extract data that satisfies some +/// criteria defined by the embedded @c OpType functor. The @c extractSparseTree +/// function wraps this class. +template +class SparseExtractor +{ + +public: + + typedef openvdb::math::Coord::ValueType Index; + + typedef typename DenseType::ValueType DenseValueType; + typedef typename OpType::ResultTreeType ResultTreeType; + typedef typename ResultTreeType::ValueType ResultValueType; + typedef typename ResultTreeType::LeafNodeType ResultLeafNodeType; + typedef typename ResultTreeType::template ValueConverter::Type MaskTree; + + typedef tbb::blocked_range3d Range3d; + + +private: + + const DenseType& mDense; + const OpType& mFunctor; + const ResultValueType mBackground; + const openvdb::math::CoordBBox mBBox; + const Index mWidth; + typename ResultTreeType::Ptr mMask; + openvdb::math::Coord mMin; + + +public: + + SparseExtractor(const DenseType& dense, const OpType& functor, + const ResultValueType background) : + mDense(dense), mFunctor(functor), + mBackground(background), + mBBox(dense.bbox()), + mWidth(ResultLeafNodeType::DIM), + mMask( new ResultTreeType(mBackground)) + {} + + + SparseExtractor(const DenseType& dense, + const openvdb::math::CoordBBox& bbox, + const OpType& functor, + const ResultValueType background) : + mDense(dense), mFunctor(functor), + mBackground(background), + mBBox(bbox), + mWidth(ResultLeafNodeType::DIM), + mMask( new ResultTreeType(mBackground)) + { + // mBBox must be inside the coordinate rage of the dense grid + if (!dense.bbox().isInside(mBBox)) { + OPENVDB_THROW(ValueError, "Data extraction window out of bound"); + } + } + + + SparseExtractor(SparseExtractor& other, tbb::split): + mDense(other.mDense), mFunctor(other.mFunctor), + mBackground(other.mBackground), mBBox(other.mBBox), + mWidth(other.mWidth), + mMask(new ResultTreeType(mBackground)), + mMin(other.mMin) + {} + + typename ResultTreeType::Ptr extract(bool threaded = true) { + + + // Construct 3D range of leaf nodes that + // intersect mBBox. + + // Snap the bbox to nearest leaf nodes min and max + + openvdb::math::Coord padded_min = mBBox.min(); + openvdb::math::Coord padded_max = mBBox.max(); + + + padded_min &= ~(mWidth - 1); + padded_max &= ~(mWidth - 1); + + padded_max[0] += mWidth - 1; + padded_max[1] += mWidth - 1; + padded_max[2] += mWidth - 1; + + + // number of leaf nodes in each direction + // division by leaf width, e.g. 8 in most cases + + const Index xleafCount = ( padded_max.x() - padded_min.x() + 1 ) / mWidth; + const Index yleafCount = ( padded_max.y() - padded_min.y() + 1 ) / mWidth; + const Index zleafCount = ( padded_max.z() - padded_min.z() + 1 ) / mWidth; + + mMin = padded_min; + + + Range3d leafRange(0, xleafCount, 1, + 0, yleafCount, 1, + 0, zleafCount, 1); + + + // Iterate over the leafnodes applying *this as a functor. + if (threaded) { + tbb::parallel_reduce(leafRange, *this); + } else { + (*this)(leafRange); + } + + return mMask; + } + + + void operator()(const Range3d& range) { + + ResultLeafNodeType* leaf = NULL; + + // Unpack the range3d item. + const Index imin = range.pages().begin(); + const Index imax = range.pages().end(); + + const Index jmin = range.rows().begin(); + const Index jmax = range.rows().end(); + + const Index kmin = range.cols().begin(); + const Index kmax = range.cols().end(); + + + // loop over all the candidate leafs. Adding only those with 'true' values + // to the tree + + for (Index i = imin; i < imax; ++i) { + for (Index j = jmin; j < jmax; ++j) { + for (Index k = kmin; k < kmax; ++k) { + + // Calculate the origin of candidate leaf + const openvdb::math::Coord origin = + mMin + openvdb::math::Coord(mWidth * i, + mWidth * j, + mWidth * k ); + + if (leaf == NULL) { + leaf = new ResultLeafNodeType(origin, mBackground); + } else { + leaf->setOrigin(origin); + leaf->fill(mBackground); + leaf->setValuesOff(); + } + + // The bounding box for this leaf + + openvdb::math::CoordBBox localBBox = leaf->getNodeBoundingBox(); + + // Shrink to the intersection with mBBox (i.e. the dense + // volume) + + localBBox.intersect(mBBox); + + // Early out for non-intersecting leafs + + if (localBBox.empty()) continue; + + + const openvdb::math::Coord start = localBBox.getStart(); + const openvdb::math::Coord end = localBBox.getEnd(); + + // Order the looping to respect the memory layout in + // the Dense source + + if (mDense.memoryLayout() == openvdb::tools::LayoutZYX) { + + openvdb::math::Coord ijk; + Index offset; + const DenseValueType* dp; + for (ijk[0] = start.x(); ijk[0] < end.x(); ++ijk[0] ) { + for (ijk[1] = start.y(); ijk[1] < end.y(); ++ijk[1] ) { + for (ijk[2] = start.z(), + offset = ResultLeafNodeType::coordToOffset(ijk), + dp = &mDense.getValue(ijk); + ijk[2] < end.z(); ++ijk[2], ++offset, ++dp) { + + mFunctor(*dp, offset, leaf); + } + } + } + + } else { + + openvdb::math::Coord ijk; + const DenseValueType* dp; + for (ijk[2] = start.z(); ijk[2] < end.z(); ++ijk[2]) { + for (ijk[1] = start.y(); ijk[1] < end.y(); ++ijk[1]) { + for (ijk[0] = start.x(), + dp = &mDense.getValue(ijk); + ijk[0] < end.x(); ++ijk[0], ++dp) { + + mFunctor(*dp, ijk, leaf); + + } + } + } + } + + // Only add non-empty leafs (empty is defined as all inactive) + + if (!leaf->isEmpty()) { + mMask->addLeaf(*leaf); + leaf = NULL; + } + + } + } + } + + // Clean up an unused leaf. + + if (leaf != NULL) delete leaf; + } + + void join(SparseExtractor& rhs) { + mMask->merge(*rhs.mMask); + } +}; // class SparseExtractor + + +template +typename OpType::ResultTreeType::Ptr +extractSparseTree(const DenseType& dense, const OpType& functor, + const typename OpType::ResultValueType& background, + bool threaded) +{ + + // Construct the mask using a parallel reduce pattern. + // Each thread computes disjoint mask-trees. The join merges + // into a single tree. + + SparseExtractor extractor(dense, functor, background); + + return extractor.extract(threaded); +} + + +/// @brief Functor-based class used to extract data from a dense grid, at +/// the index-space intersection with a supplied mask in the form of a sparse tree. +/// The @c extractSparseTreeWithMask function wraps this class. +template +class SparseMaskedExtractor +{ +public: + + typedef typename DSConverter::Type _ResultTreeType; + typedef _ResultTreeType ResultTreeType; + typedef typename ResultTreeType::LeafNodeType ResultLeafNodeType; + typedef typename ResultTreeType::ValueType ResultValueType; + typedef ResultValueType DenseValueType; + + typedef typename ResultTreeType::template ValueConverter::Type MaskTree; + typedef typename MaskTree::LeafCIter MaskLeafCIter; + typedef std::vector MaskLeafVec; + + + SparseMaskedExtractor(const DenseType& dense, + const ResultValueType& background, + const MaskLeafVec& leafVec + ): + mDense(dense), mBackground(background), mBBox(dense.bbox()), + mLeafVec(leafVec), + mResult(new ResultTreeType(mBackground)) + {} + + + + SparseMaskedExtractor(const SparseMaskedExtractor& other, tbb::split): + mDense(other.mDense), mBackground(other.mBackground), mBBox(other.mBBox), + mLeafVec(other.mLeafVec), mResult( new ResultTreeType(mBackground)) + {} + + typename ResultTreeType::Ptr extract(bool threaded = true) { + + tbb::blocked_range range(0, mLeafVec.size()); + + if (threaded) { + tbb::parallel_reduce(range, *this); + } else { + (*this)(range); + } + + return mResult; + } + + + // Used in looping over leaf nodes in the masked grid + // and using the active mask to select data to + void operator()(const tbb::blocked_range& range) { + + ResultLeafNodeType* leaf = NULL; + + + // loop over all the candidate leafs. Adding only those with 'true' values + // to the tree + + for (size_t idx = range.begin(); idx < range.end(); ++ idx) { + + const typename MaskTree::LeafNodeType* maskLeaf = mLeafVec[idx]; + + // The bounding box for this leaf + + openvdb::math::CoordBBox localBBox = maskLeaf->getNodeBoundingBox(); + + // Shrink to the intersection with the dense volume + + localBBox.intersect(mBBox); + + // Early out if there was no intersection + + if (localBBox.empty()) continue; + + // Reset or allocate the target leaf + + if (leaf == NULL) { + leaf = new ResultLeafNodeType(maskLeaf->origin(), mBackground); + } else { + leaf->setOrigin(maskLeaf->origin()); + leaf->fill(mBackground); + leaf->setValuesOff(); + } + + + // Iterate over the intersecting bounding box + // copying active values to the result tree + + const openvdb::math::Coord start = localBBox.getStart(); + const openvdb::math::Coord end = localBBox.getEnd(); + + + openvdb::math::Coord ijk; + + if (mDense.memoryLayout() == openvdb::tools::LayoutZYX + && maskLeaf->isDense()) { + + Index offset; + const DenseValueType* src; + for (ijk[0] = start.x(); ijk[0] < end.x(); ++ijk[0] ) { + for (ijk[1] = start.y(); ijk[1] < end.y(); ++ijk[1] ) { + for (ijk[2] = start.z(), + offset = ResultLeafNodeType::coordToOffset(ijk), + src = &mDense.getValue(ijk); + ijk[2] < end.z(); ++ijk[2], ++offset, ++src) { + + // copy into leaf + leaf->setValueOn(offset, *src); + } + + } + } + + } else { + + Index offset; + for (ijk[0] = start.x(); ijk[0] < end.x(); ++ijk[0] ) { + for (ijk[1] = start.y(); ijk[1] < end.y(); ++ijk[1] ) { + for (ijk[2] = start.z(), + offset = ResultLeafNodeType::coordToOffset(ijk); + ijk[2] < end.z(); ++ijk[2], ++offset) { + + if (maskLeaf->isValueOn(offset)) { + const ResultValueType denseValue = mDense.getValue(ijk); + leaf->setValueOn(offset, denseValue); + } + } + } + } + } + // Only add non-empty leafs (empty is defined as all inactive) + + if (!leaf->isEmpty()) { + mResult->addLeaf(*leaf); + leaf = NULL; + } + } + + // Clean up an unused leaf. + + if (leaf != NULL) delete leaf; + } + + void join(SparseMaskedExtractor& rhs) { + mResult->merge(*rhs.mResult); + } + + +private: + const DenseType& mDense; + const ResultValueType mBackground; + const openvdb::math::CoordBBox& mBBox; + const MaskLeafVec& mLeafVec; + + typename ResultTreeType::Ptr mResult; + +}; // class SparseMaskedExtractor + + +/// @brief a simple utility class used by @c extractSparseTreeWithMask +template +struct ExtractAll +{ + typedef _ResultTreeType ResultTreeType; + typedef typename ResultTreeType::LeafNodeType ResultLeafNodeType; + + template inline void + operator()(const DenseValueType& a, const CoordOrIndex& offset, ResultLeafNodeType* leaf) const + { + leaf->setValueOn(offset, a); + } +}; + + +template +typename DSConverter::Type::Ptr +extractSparseTreeWithMask(const DenseType& dense, + const MaskTreeType& maskProxy, + const typename DenseType::ValueType& background, + bool threaded) +{ + typedef SparseMaskedExtractor LeafExtractor; + typedef typename LeafExtractor::DenseValueType DenseValueType; + typedef typename LeafExtractor::ResultTreeType ResultTreeType; + typedef typename LeafExtractor::MaskLeafVec MaskLeafVec; + typedef typename LeafExtractor::MaskTree MaskTree; + typedef typename LeafExtractor::MaskLeafCIter MaskLeafCIter; + typedef ExtractAll ExtractionRule; + + // Use Mask tree to hold the topology + + MaskTree maskTree(maskProxy, false, TopologyCopy()); + + // Construct an array of pointers to the mask leafs. + + const size_t leafCount = maskTree.leafCount(); + MaskLeafVec leafarray(leafCount); + MaskLeafCIter leafiter = maskTree.cbeginLeaf(); + for (size_t n = 0; n != leafCount; ++n, ++leafiter) { + leafarray[n] = leafiter.getLeaf(); + } + + + // Extract the data that is masked leaf nodes in the mask. + + LeafExtractor leafextractor(dense, background, leafarray); + typename ResultTreeType::Ptr resultTree = leafextractor.extract(threaded); + + + // Extract data that is masked by tiles in the mask. + + + // Loop over the mask tiles, extracting the data into new trees. + // These trees will be leaf-orthogonal to the leafTree (i.e. no leaf + // nodes will overlap). Merge these trees into the result. + + typename MaskTreeType::ValueOnCIter tileIter(maskProxy); + tileIter.setMaxDepth(MaskTreeType::ValueOnCIter::LEAF_DEPTH - 1); + + // Return the leaf tree if the mask had no tiles + + if (!tileIter) return resultTree; + + ExtractionRule allrule; + + // Loop over the tiles in series, but the actual data extraction + // is in parallel. + + CoordBBox bbox; + for ( ; tileIter; ++tileIter) { + + // Find the intersection of the tile with the dense grid. + + tileIter.getBoundingBox(bbox); + bbox.intersect(dense.bbox()); + + if (bbox.empty()) continue; + + SparseExtractor copyData(dense, bbox, allrule, background); + typename ResultTreeType::Ptr fromTileTree = copyData.extract(threaded); + resultTree->merge(*fromTileTree); + } + + return resultTree; +} + + +/// @brief Class that applies a functor to the index space intersection +/// of a prescribed bounding box and the dense grid. +/// NB: This class only supports DenseGrids with ZYX memory layout. +template +class DenseTransformer +{ +public: + + typedef _ValueT ValueT; + typedef Dense DenseT; + typedef openvdb::math::Coord::ValueType IntType; + typedef tbb::blocked_range2d RangeType; + + +private: + + DenseT& mDense; + const OpType& mOp; + openvdb::math::CoordBBox mBBox; + +public: + DenseTransformer(DenseT& dense, + const openvdb::math::CoordBBox& bbox, + const OpType& functor): + mDense(dense), mOp(functor), mBBox(dense.bbox()) + { + // The iteration space is the intersection of the + // input bbox and the index-space covered by the dense grid + mBBox.intersect(bbox); + } + + DenseTransformer(const DenseTransformer& other) : + mDense(other.mDense), mOp(other.mOp), mBBox(other.mBBox) {} + + void apply(bool threaded = true) { + + // Early out if the iteration space is empty + + if (mBBox.empty()) return; + + + const openvdb::math::Coord start = mBBox.getStart(); + const openvdb::math::Coord end = mBBox.getEnd(); + + // The iteration range only the slower two directions. + const RangeType range(start.x(), end.x(), 1, + start.y(), end.y(), 1); + + if (threaded) { + tbb::parallel_for(range, *this); + } else { + (*this)(range); + } + } + + void operator()(const RangeType& range) const { + + // The stride in the z-direction. + // Note: the bbox is [inclusive, inclusive] + + const size_t zlength = size_t(mBBox.max().z() - mBBox.min().z() + 1); + + const IntType imin = range.rows().begin(); + const IntType imax = range.rows().end(); + const IntType jmin = range.cols().begin(); + const IntType jmax = range.cols().end(); + + + openvdb::math::Coord xyz(imin, jmin, mBBox.min().z()); + for (xyz[0] = imin; xyz[0] != imax; ++xyz[0]) { + for (xyz[1] = jmin; xyz[1] != jmax; ++xyz[1]) { + + mOp.transform(mDense, xyz, zlength); + } + } + } +}; // class DenseTransformer + + +/// @brief a wrapper struct used to avoid unnecessary computation of +/// memory access from @c Coord when all offsets are guaranteed to be +/// within the dense grid. +template +struct ContiguousOp +{ + ContiguousOp(const PointWiseOp& op) : mOp(op){} + + typedef Dense DenseT; + inline void transform(DenseT& dense, openvdb::math::Coord& ijk, size_t size) const + { + ValueT* dp = const_cast(&dense.getValue(ijk)); + + for (size_t offset = 0; offset < size; ++offset) { + dp[offset] = mOp(dp[offset]); + } + } + + const PointWiseOp mOp; +}; + + +/// Apply a point-wise functor to the intersection of a dense grid and a given bounding box +template +void +transformDense(Dense& dense, + const openvdb::CoordBBox& bbox, + const PointwiseOpT& functor, bool parallel) +{ + typedef ContiguousOp OpT; + + // Convert the Op so it operates on a contiguous line in memory + + OpT op(functor); + + // Apply to the index space intersection in the dense grid + DenseTransformer transformer(dense, bbox, op); + transformer.apply(parallel); +} + + +template +class SparseToDenseCompositor +{ + +public: + typedef _TreeT TreeT; + typedef typename TreeT::ValueType ValueT; + typedef typename TreeT::LeafNodeType LeafT; + typedef typename TreeT::template ValueConverter::Type MaskTreeT; + typedef typename MaskTreeT::LeafNodeType MaskLeafT; + typedef Dense DenseT; + typedef openvdb::math::Coord::ValueType Index; + typedef tbb::blocked_range3d Range3d; + + SparseToDenseCompositor(DenseT& dense, const TreeT& source, const TreeT& alpha, + const ValueT beta, const ValueT strength) : + mDense(dense), mSource(source), mAlpha(alpha), mBeta(beta), mStrength(strength) + {} + + SparseToDenseCompositor(const SparseToDenseCompositor& other): + mDense(other.mDense), mSource(other.mSource), mAlpha(other.mAlpha), + mBeta(other.mBeta), mStrength(other.mStrength) {} + + + + void sparseComposite(bool threaded) { + + const ValueT beta = mBeta; + const ValueT strenght = mStrength; + + // construct a tree that defines the iteration space + + MaskTreeT maskTree(mSource, false /*background*/, openvdb::TopologyCopy()); + maskTree.topologyUnion(mAlpha); + + // Composite regions that are represented by leafnodes in either mAlpha or mSource + // Parallelize over bool-leafs + + openvdb::tree::LeafManager maskLeafs(maskTree); + maskLeafs.foreach(*this, threaded); + + // Composite regions that are represented by tiles + // Parallelize within each tile. + + typename MaskTreeT::ValueOnCIter citer = maskTree.cbeginValueOn(); + citer.setMaxDepth(MaskTree::ValueOnCIter::LEAF_DEPTH - 1); + + if (!citer) return; + + typename tree::ValueAccessor alphaAccessor(mAlpha); + typename tree::ValueAccessor sourceAccessor(mSource); + + for (; citer; ++citer) { + + const openvdb::math::Coord org = citer.getCoord(); + + // Early out if both alpha and source are zero in this tile. + + const ValueT alphaValue = alphaAccessor.getValue(org); + const ValueT sourceValue = sourceAccessor.getValue(org); + + if (openvdb::math::isZero(alphaValue) && + openvdb::math::isZero(sourceValue) ) continue; + + // Compute overlap of tile with the dense grid + + openvdb::math::CoordBBox localBBox = citer.getBoundingBox(); + localBBox.intersect(mDense.bbox()); + + // Early out if there is no intersection + + if (localBBox.empty()) continue; + + // Composite the tile-uniform values into the dense grid. + compositeFromTile(mDense, localBBox, sourceValue, + alphaValue, beta, strenght, threaded); + } + } + + // Composites leaf values where the alpha values are active. + // Used in sparseComposite + void inline operator()(const MaskLeafT& maskLeaf, size_t /*i*/) const + { + + typedef UniformLeaf ULeaf; + openvdb::math::CoordBBox localBBox = maskLeaf.getNodeBoundingBox(); + localBBox.intersect(mDense.bbox()); + + // Early out for non-overlapping leafs + + if (localBBox.empty()) return; + + const openvdb::math::Coord org = maskLeaf.origin(); + const LeafT* alphaLeaf = mAlpha.probeLeaf(org); + const LeafT* sourceLeaf = mSource.probeLeaf(org); + + if (!sourceLeaf) { + + // Create a source leaf proxy with the correct value + ULeaf uniformSource(mSource.getValue(org)); + + if (!alphaLeaf) { + + // Create an alpha leaf proxy with the correct value + ULeaf uniformAlpha(mAlpha.getValue(org)); + + compositeFromLeaf(mDense, localBBox, uniformSource, uniformAlpha, + mBeta, mStrength); + } else { + + compositeFromLeaf(mDense, localBBox, uniformSource, *alphaLeaf, + mBeta, mStrength); + } + } else { + if (!alphaLeaf) { + + // Create an alpha leaf proxy with the correct value + ULeaf uniformAlpha(mAlpha.getValue(org)); + + compositeFromLeaf(mDense, localBBox, *sourceLeaf, uniformAlpha, + mBeta, mStrength); + } else { + + compositeFromLeaf(mDense, localBBox, *sourceLeaf, *alphaLeaf, + mBeta, mStrength); + } + } + } + // i.e. it assumes that all valueOff Alpha voxels have value 0. + + template + inline static void compositeFromLeaf(DenseT& dense, const openvdb::math::CoordBBox& bbox, + const LeafT1& source, const LeafT2& alpha, + const ValueT beta, const ValueT strength) + { + typedef openvdb::math::Coord::ValueType IntType; + + const ValueT sbeta = strength * beta; + openvdb::math::Coord ijk = bbox.min(); + + + if (alpha.isDense() /*all active values*/) { + + // Optimal path for dense alphaLeaf + const IntType size = bbox.max().z() + 1 - bbox.min().z(); + + for (ijk[0] = bbox.min().x(); ijk[0] < bbox.max().x() + 1; ++ijk[0]) { + for (ijk[1] = bbox.min().y(); ijk[1] < bbox.max().y() + 1; ++ijk[1]) { + + ValueT* d = const_cast(&dense.getValue(ijk)); + const ValueT* a = &alpha.getValue(ijk); + const ValueT* s = &source.getValue(ijk); + + for (IntType idx = 0; idx < size; ++idx) { + d[idx] = CompositeMethod::apply(d[idx], a[idx], s[idx], + strength, beta, sbeta); + } + } + } + } else { + + // AlphaLeaf has non-active cells. + + for (ijk[0] = bbox.min().x(); ijk[0] < bbox.max().x() + 1; ++ijk[0]) { + for (ijk[1] = bbox.min().y(); ijk[1] < bbox.max().y() + 1; ++ijk[1]) { + for (ijk[2] = bbox.min().z(); ijk[2] < bbox.max().z() + 1; ++ijk[2]) { + + if (alpha.isValueOn(ijk)) { + + dense.setValue(ijk, + CompositeMethod::apply(dense.getValue(ijk), + alpha.getValue(ijk), source.getValue(ijk), + strength, beta, sbeta) + ); + } + } + } + } + } + } + + inline static void compositeFromTile(DenseT& dense, openvdb::math::CoordBBox& bbox, + const ValueT& sourceValue, const ValueT& alphaValue, + const ValueT& beta, const ValueT& strength, + bool threaded) + { + + typedef UniformTransformer TileTransformer; + TileTransformer functor(sourceValue, alphaValue, beta, strength); + + // Transform the data inside the bbox according to the TileTranformer. + + transformDense(dense, bbox, functor, threaded); + + } + + + void denseComposite(bool threaded) + { + /// Construct a range that corresponds to the + /// bounding box of the dense volume + const openvdb::math::CoordBBox& bbox = mDense.bbox(); + + Range3d range(bbox.min().x(), bbox.max().x(), LeafT::DIM, + bbox.min().y(), bbox.max().y(), LeafT::DIM, + bbox.min().z(), bbox.max().z(), LeafT::DIM); + + // Iterate over the range, compositing into + // the dense grid using value accessors for + // sparse the grids. + if (threaded) { + tbb::parallel_for(range, *this); + } else { + (*this)(range); + } + + } + + // Composites a dense region using value accessors + // into a dense grid + void inline operator()(const Range3d& range) const + { + // Use value accessors to alpha and source + + typename tree::ValueAccessor alphaAccessor(mAlpha); + typename tree::ValueAccessor sourceAccessor(mSource); + + const ValueT strength = mStrength; + const ValueT beta = mBeta; + const ValueT sbeta = strength * beta; + + // Unpack the range3d item. + const Index imin = range.pages().begin(); + const Index imax = range.pages().end(); + + const Index jmin = range.rows().begin(); + const Index jmax = range.rows().end(); + + const Index kmin = range.cols().begin(); + const Index kmax = range.cols().end(); + + openvdb::Coord ijk; + for (ijk[0] = imin; ijk[0] < imax; ++ijk[0]) { + for (ijk[1] = jmin; ijk[1] < jmax; ++ijk[1]) { + for (ijk[2] = kmin; ijk[2] < kmax; ++ijk[2]) { + const ValueT d_old = mDense.getValue(ijk); + const ValueT& alpha = alphaAccessor.getValue(ijk); + const ValueT& src = sourceAccessor.getValue(ijk); + + mDense.setValue(ijk, CompositeMethod::apply(d_old, alpha, src, + strength, beta, sbeta)); + } + } + } + + } + + +private: + + // Internal class that wraps the templated composite method + // for use when both alpha and source are uniform over + // a prescribed bbox (e.g. a tile). + class UniformTransformer + { + public: + UniformTransformer(const ValueT& source, const ValueT& alpha, const ValueT& _beta, + const ValueT& _strength) : + mSource(source), mAlpha(alpha), mBeta(_beta), + mStrength(_strength), mSBeta(_strength * _beta) + {} + + ValueT operator()(const ValueT& input) const + { + return CompositeMethod::apply(input, mAlpha, mSource, + mStrength, mBeta, mSBeta); + } + + private: + const ValueT mSource; const ValueT mAlpha; const ValueT mBeta; + const ValueT mStrength; const ValueT mSBeta; + }; + + + // Simple Class structure that mimics a leaf + // with uniform values. Holds LeafT::DIM copies + // of a value in an array. + struct Line { ValueT mValues[LeafT::DIM]; }; + class UniformLeaf : private Line + { + public: + typedef typename LeafT::ValueType ValueT; + + typedef Line BaseT; + UniformLeaf(const ValueT& value) : BaseT(init(value)) {} + + static const BaseT init(const ValueT& value) { + BaseT tmp; + for (openvdb::Index i = 0; i < LeafT::DIM; ++i) { + tmp.mValues[i] = value; + } + return tmp; + } + + bool isDense() const { return true; } + bool isValueOn(openvdb::math::Coord&) const { return true; } + + inline const ValueT& getValue(const openvdb::math::Coord& ) const + {return BaseT::mValues[0];} + }; + +private: + DenseT& mDense; + const TreeT& mSource; + const TreeT& mAlpha; + ValueT mBeta; + ValueT mStrength; +}; // class SparseToDenseCompositor + + +namespace ds +{ + //@{ + /// @brief Point wise methods used to apply various compositing operations. + template + struct OpOver + { + static inline ValueT apply(const ValueT u, const ValueT alpha, + const ValueT v, + const ValueT strength, + const ValueT beta, + const ValueT /*sbeta*/) + { return (u + strength * alpha * (beta * v - u)); } + }; + + + template + struct OpAdd + { + static inline ValueT apply(const ValueT u, const ValueT alpha, + const ValueT v, + const ValueT /*strength*/, + const ValueT /*beta*/, + const ValueT sbeta) + { return (u + sbeta * alpha * v); } + }; + + template + struct OpSub + { + static inline ValueT apply(const ValueT u, const ValueT alpha, + const ValueT v, + const ValueT /*strength*/, + const ValueT /*beta*/, + const ValueT sbeta) + { return (u - sbeta * alpha * v); } + }; + + template + struct OpMin + { + static inline ValueT apply(const ValueT u, const ValueT alpha, + const ValueT v, + const ValueT s /*trength*/, + const ValueT beta, + const ValueT /*sbeta*/) + { return ( ( 1 - s * alpha) * u + s * alpha * std::min(u, beta * v) ); } + }; + + + template + struct OpMax + { + static inline ValueT apply(const ValueT u, const ValueT alpha, + const ValueT v, + const ValueT s/*trength*/, + const ValueT beta, + const ValueT /*sbeta*/) + { return ( ( 1 - s * alpha ) * u + s * alpha * std::min(u, beta * v) ); } + }; + + template + struct OpMult + { + static inline ValueT apply(const ValueT u, const ValueT alpha, + const ValueT v, + const ValueT s/*trength*/, + const ValueT /*beta*/, + const ValueT sbeta) + { return ( ( 1 + alpha * (sbeta * v - s)) * u ); } + }; + //@} + + //@{ + /// Translator that converts an enum to compositing functor types + template + struct CompositeFunctorTranslator{}; + + template + struct CompositeFunctorTranslator{ typedef OpOver OpT; }; + + template + struct CompositeFunctorTranslator{ typedef OpAdd OpT; }; + + template + struct CompositeFunctorTranslator{ typedef OpSub OpT; }; + + template + struct CompositeFunctorTranslator{ typedef OpMin OpT; }; + + template + struct CompositeFunctorTranslator{ typedef OpMax OpT; }; + + template + struct CompositeFunctorTranslator{ typedef OpMult OpT; }; + //@} + +} // namespace ds + + +template +void compositeToDense( + Dense& dense, + const TreeT& source, const TreeT& alpha, + const typename TreeT::ValueType beta, + const typename TreeT::ValueType strength, + bool threaded) +{ + typedef typename TreeT::ValueType ValueT; + typedef ds::CompositeFunctorTranslator Translator; + typedef typename Translator::OpT Method; + + if (openvdb::math::isZero(strength)) return; + + SparseToDenseCompositor tool(dense, source, alpha, beta, strength); + + if (openvdb::math::isZero(alpha.background()) && + openvdb::math::isZero(source.background())) + { + // Use the sparsity of (alpha U source) as the iteration space. + tool.sparseComposite(threaded); + } else { + // Use the bounding box of dense as the iteration space. + tool.denseComposite(threaded); + } +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif //OPENVDB_TOOLS_DENSESPARSETOOLS_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/Diagnostics.h b/nuparu/include/openvdb/include/openvdb/tools/Diagnostics.h new file mode 100644 index 00000000..68bc4853 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/Diagnostics.h @@ -0,0 +1,1344 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 Diagnostics.h +/// +/// @author Ken Museth +/// +/// @brief Various diagnostic tools to identify potential issues with +/// for example narrow-band level sets or fog volumes +/// +#ifndef OPENVDB_TOOLS_DIAGNOSTICS_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_DIAGNOSTICS_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +//////////////////////////////////////////////////////////////////////////////// + +/// @brief Perform checks on a grid to see if it is a valid symmetric, +/// narrow-band level set. +/// +/// @param grid Grid to be checked +/// @param number Number of the checks to be performed (see below) +/// @return string with a message indicating the nature of the +/// issue. If no issue is detected the return string is empty. +/// +/// @details @a number refers to the following ordered list of +/// checks - always starting from the top. +/// Fast checks +/// 1: value type is floating point +/// 2: has level set class type +/// 3: has uniform scale +/// 4: background value is positive and n*dx +/// +/// Slower checks +/// 5: no active tiles +/// 6: all the values are finite, i.e not NaN or infinite +/// 7: active values in range between +-background +/// 8: abs of inactive values = background, i.e. assuming a symmetric +/// narrow band! +/// +/// Relatively slow check (however multithreaded) +/// 9: norm gradient is close to one, i.e. satisfied the Eikonal equation. +template +std::string +checkLevelSet(const GridType& grid, size_t number=9); + +//////////////////////////////////////////////////////////////////////////////// + +/// @brief Perform checks on a grid to see if it is a valid fog volume. +/// +/// @param grid Grid to be checked +/// @param number Number of the checks to be performed (see below) +/// @return string with a message indicating the nature of the +/// issue. If no issue is detected the return string is empty. +/// +/// @details @a number refers to the following ordered list of +/// checks - always starting from the top. +/// Fast checks +/// 1: value type is floating point +/// 2: has FOG volume class type +/// 3: background value is zero +/// +/// Slower checks +/// 4: all the values are finite, i.e not NaN or infinite +/// 5: inactive values are zero +/// 6: active values are in the range [0,1] +template +std::string +checkFogVolume(const GridType& grid, size_t number=6); + +//////////////////////////////////////////////////////////////////////////////// + +/// @brief Threaded method to find unique inactive values. +/// +/// @param grid A VDB volume. +/// @param values List of unique inactive values, returned by this method. +/// @param numValues Number of values to look for. +/// @return @c false if the @a grid has more than @a numValues inactive values. +template +bool +uniqueInactiveValues(const GridType& grid, + std::vector& values, size_t numValues); + + +//////////////////////////////////////////////////////////////////////////////// + +/// @brief Checks NaN values +template +struct CheckNan +{ + typedef typename VecTraits::ElementType ElementType; + typedef TreeIterT TileIterT; + typedef typename tree::IterTraits + ::template NodeConverter::Type VoxelIterT; + + /// @brief Default constructor + CheckNan() {} + + /// Return true if the scalar value is NaN + inline bool operator()(const ElementType& v) const { return boost::math::isnan(v); } + + /// @brief This allows for vector values to be checked component-wise + template + inline typename boost::enable_if_c::IsVec, bool>::type + operator()(const T& v) const + { + for (int i=0; i::Size; ++i) if ((*this)(v[i])) return true;//should unroll + return false; + } + + /// @brief Return true if the tile at the iterator location is NaN + bool operator()(const TreeIterT &iter) const { return (*this)(*iter); } + + /// @brief Return true if the voxel at the iterator location is NaN + bool operator()(const VoxelIterT &iter) const { return (*this)(*iter); } + + /// @brief Return a string describing a failed check. + std::string str() const { return "NaN"; } + +};// CheckNan + +//////////////////////////////////////////////////////////////////////////////// + +/// @brief Checks for infinite values, e.g. 1/0 or -1/0 +template +struct CheckInf +{ + typedef typename VecTraits::ElementType ElementType; + typedef TreeIterT TileIterT; + typedef typename tree::IterTraits + ::template NodeConverter::Type VoxelIterT; + + /// @brief Default constructor + CheckInf() {} + + /// Return true if the value is infinite + inline bool operator()(const ElementType& v) const { return boost::math::isinf(v); } + + /// Return true if any of the vector components are infinite. + template inline typename boost::enable_if_c::IsVec, bool>::type + operator()(const T& v) const + { + for (int i=0; i::Size; ++i) if ((*this)(v[i])) return true; + return false; + } + + /// @brief Return true if the tile at the iterator location is infinite + bool operator()(const TreeIterT &iter) const { return (*this)(*iter); } + + /// @brief Return true if the tile at the iterator location is infinite + bool operator()(const VoxelIterT &iter) const { return (*this)(*iter); } + + /// @brief Return a string describing a failed check. + std::string str() const { return "infinite"; } +};// CheckInf + +//////////////////////////////////////////////////////////////////////////////// + +/// @brief Checks for both NaN and inf values, i.e. any value that is not finite. +template +struct CheckFinite +{ + typedef typename VecTraits::ElementType ElementType; + typedef TreeIterT TileIterT; + typedef typename tree::IterTraits + ::template NodeConverter::Type VoxelIterT; + + /// @brief Default constructor + CheckFinite() {} + + /// Return true if the value is NOT finite, i.e. it's NaN or infinite + inline bool operator()(const ElementType& v) const { return !boost::math::isfinite(v); } + + /// Return true if any of the vector components are NaN or infinite. + template + inline typename boost::enable_if_c::IsVec, bool>::type + operator()(const T& v) const { + for (int i=0; i::Size; ++i) if ((*this)(v[i])) return true; + return false; + } + + /// @brief Return true if the tile at the iterator location is NaN or infinite. + bool operator()(const TreeIterT &iter) const { return (*this)(*iter); } + + /// @brief Return true if the tile at the iterator location is NaN or infinite. + bool operator()(const VoxelIterT &iter) const { return (*this)(*iter); } + + /// @brief Return a string describing a failed check. + std::string str() const { return "not finite"; } +};// CheckFinite + +//////////////////////////////////////////////////////////////////////////////// + +/// @brief Check that the magnitude of a value, a, is close to a fixed +/// magnitude, b, given a fixed tolerance c. That is | |a| - |b| | <= c +template +struct CheckMagnitude +{ + typedef typename VecTraits::ElementType ElementType; + typedef TreeIterT TileIterT; + typedef typename tree::IterTraits + ::template NodeConverter::Type VoxelIterT; + + /// @brief Default constructor + CheckMagnitude(const ElementType& a, + const ElementType& t = math::Tolerance::value()) + : absVal(math::Abs(a)), tolVal(math::Abs(t)) + { + } + + /// Return true if the magnitude of the value is not approximately + /// equal to totVal. + inline bool operator()(const ElementType& v) const + { + return math::Abs(math::Abs(v) - absVal) > tolVal; + } + + /// Return true if any of the vector components are infinite. + template inline typename boost::enable_if_c::IsVec, bool>::type + operator()(const T& v) const + { + for (int i=0; i::Size; ++i) if ((*this)(v[i])) return true; + return false; + } + + /// @brief Return true if the tile at the iterator location is infinite + bool operator()(const TreeIterT &iter) const { return (*this)(*iter); } + + /// @brief Return true if the tile at the iterator location is infinite + bool operator()(const VoxelIterT &iter) const { return (*this)(*iter); } + + /// @brief Return a string describing a failed check. + std::string str() const + { + std::ostringstream ss; + ss << "not equal to +/-"< +struct CheckRange +{ + typedef typename VecTraits::ElementType ElementType; + typedef TreeIterT TileIterT; + typedef typename tree::IterTraits + ::template NodeConverter::Type VoxelIterT; + + // @brief Constructor taking a range to be tested against. + CheckRange(const ElementType& _min, const ElementType& _max) : minVal(_min), maxVal(_max) + { + if (minVal > maxVal) { + OPENVDB_THROW(ValueError, "CheckRange: Invalid range (min > max)"); + } + } + + /// Return true if the value is smaller than min or larger than max. + inline bool operator()(const ElementType& v) const + { + return (MinInclusive ? vmaxVal : v>=maxVal); + } + + /// Return true if any of the vector components are out of range. + template + inline typename boost::enable_if_c::IsVec, bool>::type + operator()(const T& v) const { + for (int i=0; i::Size; ++i) if ((*this)(v[i])) return true; + return false; + } + + /// @brief Return true if the voxel at the iterator location is out of range. + bool operator()(const TreeIterT &iter) const { return (*this)(*iter); } + + /// @brief Return true if the tile at the iterator location is out of range. + bool operator()(const VoxelIterT &iter) const { return (*this)(*iter); } + + /// @brief Return a string describing a failed check. + std::string str() const + { + std::ostringstream ss; + ss << "outside the value range " << (MinInclusive ? "[" : "]") + << minVal << "," << maxVal << (MaxInclusive ? "]" : "["); + return ss.str(); + } + + const ElementType minVal, maxVal; +};// CheckRange + +//////////////////////////////////////////////////////////////////////////////// + +/// @brief Checks a value against a minimum +template +struct CheckMin +{ + typedef typename VecTraits::ElementType ElementType; + typedef TreeIterT TileIterT; + typedef typename tree::IterTraits + ::template NodeConverter::Type VoxelIterT; + + // @brief Constructor taking a minimum to be tested against. + CheckMin(const ElementType& _min) : minVal(_min) {} + + /// Return true if the value is smaller than min. + inline bool operator()(const ElementType& v) const { return v + inline typename boost::enable_if_c::IsVec, bool>::type + operator()(const T& v) const { + for (int i=0; i::Size; ++i) if ((*this)(v[i])) return true; + return false; + } + + /// @brief Return true if the voxel at the iterator location is smaller than min. + bool operator()(const TreeIterT &iter) const { return (*this)(*iter); } + + /// @brief Return true if the tile at the iterator location is smaller than min. + bool operator()(const VoxelIterT &iter) const { return (*this)(*iter); } + + /// @brief Return a string describing a failed check. + std::string str() const + { + std::ostringstream ss; + ss << "smaller than "< +struct CheckMax +{ + typedef typename VecTraits::ElementType ElementType; + typedef TreeIterT TileIterT; + typedef typename tree::IterTraits + ::template NodeConverter::Type VoxelIterT; + + /// @brief Constructor taking a maximum to be tested against. + CheckMax(const ElementType& _max) : maxVal(_max) {} + + /// Return true if the value is larger than max. + inline bool operator()(const ElementType& v) const { return v>maxVal; } + + /// Return true if any of the vector components are larger than max. + template + inline typename boost::enable_if_c::IsVec, bool>::type + operator()(const T& v) const { + for (int i=0; i::Size; ++i) if ((*this)(v[i])) return true; + return false; + } + + /// @brief Return true if the tile at the iterator location is larger than max. + bool operator()(const TreeIterT &iter) const { return (*this)(*iter); } + + /// @brief Return true if the voxel at the iterator location is larger than max. + bool operator()(const VoxelIterT &iter) const { return (*this)(*iter); } + + /// @brief Return a string describing a failed check. + std::string str() const + { + std::ostringstream ss; + ss << "larger than "<//math::WENO5_BIAS> +struct CheckNormGrad +{ + typedef typename GridT::ValueType ValueType; + BOOST_STATIC_ASSERT(boost::is_floating_point::value); + typedef TreeIterT TileIterT; + typedef typename tree::IterTraits + ::template NodeConverter::Type VoxelIterT; + typedef typename GridT::ConstAccessor AccT; + + /// @brief Constructor taking a grid and a range to be tested against. + CheckNormGrad(const GridT& grid, const ValueType& _min, const ValueType& _max) + : acc(grid.getConstAccessor()) + , invdx2(ValueType(1.0/math::Pow2(grid.voxelSize()[0]))) + , minVal2(_min*_min) + , maxVal2(_max*_max) + { + if ( !grid.hasUniformVoxels() ) { + OPENVDB_THROW(ValueError, "CheckNormGrad: The transform must have uniform scale"); + } + if (_min > _max) { + OPENVDB_THROW(ValueError, "CheckNormGrad: Invalid range (min > max)"); + } + } + + CheckNormGrad(const CheckNormGrad& other) + : acc(other.acc.tree()) + , invdx2(other.invdx2) + , minVal2(other.minVal2) + , maxVal2(other.maxVal2) + { + } + + /// Return true if the value is smaller than min or larger than max. + inline bool operator()(const ValueType& v) const { return vmaxVal2; } + + /// @brief Return true if zero is outside the range. + /// @note We assume that the norm of the gradient of a tile is always zero. + inline bool operator()(const TreeIterT&) const { return (*this)(ValueType(0)); } + + /// @brief Return true if the norm of the gradient at a voxel + /// location of the iterator is out of range. + inline bool operator()(const VoxelIterT &iter) const + { + const Coord ijk = iter.getCoord(); + return (*this)(invdx2 * math::ISGradientNormSqrd::result(acc, ijk)); + } + + /// @brief Return a string describing a failed check. + std::string str() const + { + std::ostringstream ss; + ss << "outside the range of NormGrad ["< >//math::GradStencil +struct CheckEikonal +{ + typedef typename GridT::ValueType ValueType; + BOOST_STATIC_ASSERT(boost::is_floating_point::value); + typedef TreeIterT TileIterT; + typedef typename tree::IterTraits + ::template NodeConverter::Type VoxelIterT; + + /// @brief Constructor taking a grid and a range to be tested against. + CheckEikonal(const GridT& grid, const ValueType& _min, const ValueType& _max) + : stencil(grid), minVal(_min), maxVal(_max) + { + if ( !grid.hasUniformVoxels() ) { + OPENVDB_THROW(ValueError, "CheckEikonal: The transform must have uniform scale"); + } + if (minVal > maxVal) { + OPENVDB_THROW(ValueError, "CheckEikonal: Invalid range (min > max)"); + } + } + + CheckEikonal(const CheckEikonal& other) + : stencil(other.stencil.grid()), minVal(other.minVal), maxVal(other.maxVal) + { + } + + /// Return true if the value is smaller than min or larger than max. + inline bool operator()(const ValueType& v) const { return vmaxVal; } + + /// @brief Return true if zero is outside the range. + /// @note We assume that the norm of the gradient of a tile is always zero. + inline bool operator()(const TreeIterT&) const { return (*this)(ValueType(0)); } + + /// @brief Return true if the norm of the gradient at a + /// zero-crossing voxel location of the iterator is out of range. + inline bool operator()(const VoxelIterT &iter) const + { + stencil.moveTo(iter); + if (!stencil.zeroCrossing()) return false; + return (*this)(stencil.normSqGrad()); + } + + /// @brief Return a string describing a failed check. + std::string str() const + { + std::ostringstream ss; + ss << "outside the range of NormGrad ["< +struct CheckDivergence +{ + typedef typename GridT::ValueType ValueType; + typedef typename VecTraits::ElementType ElementType; + BOOST_STATIC_ASSERT(boost::is_floating_point::value); + typedef TreeIterT TileIterT; + typedef typename tree::IterTraits + ::template NodeConverter::Type VoxelIterT; + typedef typename GridT::ConstAccessor AccT; + + /// @brief Constructor taking a grid and a range to be tested against. + CheckDivergence(const GridT& grid, + const ValueType& _min, + const ValueType& _max) + : acc(grid.getConstAccessor()) + , invdx(ValueType(1.0/grid.voxelSize()[0])) + , minVal(_min) + , maxVal(_max) + { + if ( !grid.hasUniformVoxels() ) { + OPENVDB_THROW(ValueError, "CheckDivergence: The transform must have uniform scale"); + } + if (minVal > maxVal) { + OPENVDB_THROW(ValueError, "CheckDivergence: Invalid range (min > max)"); + } + } + /// Return true if the value is smaller than min or larger than max. + inline bool operator()(const ElementType& v) const { return vmaxVal; } + + /// @brief Return true if zero is outside the range. + /// @note We assume that the divergence of a tile is always zero. + inline bool operator()(const TreeIterT&) const { return (*this)(ElementType(0)); } + + /// @brief Return true if the divergence at a voxel location of + /// the iterator is out of range. + inline bool operator()(const VoxelIterT &iter) const + { + const Coord ijk = iter.getCoord(); + return (*this)(invdx * math::ISDivergence::result(acc, ijk)); + } + + /// @brief Return a string describing a failed check. + std::string str() const + { + std::ostringstream ss; + ss << "outside the range of divergence ["< +class Diagnose +{ + public: + typedef typename GridT::template ValueConverter::Type MaskType; + + Diagnose(const GridT& grid) : mGrid(&grid), mMask(new MaskType()), mCount(0) + { + mMask->setTransform(grid.transformPtr()->copy()); + } + + template + std::string check(const CheckT& check, + bool updateMask = false, + bool checkVoxels = true, + bool checkTiles = true, + bool checkBackground = true) + { + typename MaskType::TreeType* mask = updateMask ? &(mMask->tree()) : NULL; + CheckValues cc(mask, mGrid, check); + std::ostringstream ss; + if (checkBackground) ss << cc.checkBackground(); + if (checkTiles) ss << cc.checkTiles(); + if (checkVoxels) ss << cc.checkVoxels(); + mCount += cc.mCount; + return ss.str(); + } + + //@{ + /// @brief Return a boolean mask of all the values + /// (i.e. tiles and/or voxels) that have failed one or + /// more checks. + typename MaskType::ConstPtr mask() const { return mMask; } + typename MaskType::Ptr mask() { return mMask; } + //@} + + /// @brief Return the number of values (i.e. background, tiles or + /// voxels) that have failed one or more checks. + Index64 valueCount() const { return mMask->activeVoxelCount(); } + + /// @brief Return total number of failed checks + /// @note If only one check was performed and the mask was updated + /// failureCount equals valueCount. + Index64 failureCount() const { return mCount; } + + /// @brief Return a const reference to the grid + const GridT& grid() const { return *mGrid; } + + /// @brief Clear the mask and error counter + void clear() { mMask = new MaskType(); mCount = 0; } + +private: + // disallow copy construction and copy by assignment! + Diagnose(const Diagnose&);// not implemented + Diagnose& operator=(const Diagnose&);// not implemented + + const GridT* mGrid; + typename MaskType::Ptr mMask; + Index64 mCount; + + /// @brief Private class that performs the multithreaded checks + template + struct CheckValues + { + typedef typename MaskType::TreeType MaskT; + typedef typename GridT::TreeType::LeafNodeType LeafT; + typedef typename tree::LeafManager LeafManagerT; + const bool mOwnsMask; + MaskT* mMask; + const GridT* mGrid; + const CheckT mCheck; + Index64 mCount; + + CheckValues(MaskT* mask, const GridT* grid, const CheckT& check) + : mOwnsMask(false) + , mMask(mask) + , mGrid(grid) + , mCheck(check) + , mCount(0) + { + } + CheckValues(CheckValues& other, tbb::split) + : mOwnsMask(true) + , mMask(other.mMask ? new MaskT() : NULL) + , mGrid(other.mGrid) + , mCheck(other.mCheck) + , mCount(0) + { + } + ~CheckValues() { if (mOwnsMask) delete mMask; } + + std::string checkBackground() + { + std::ostringstream ss; + if (mCheck(mGrid->background())) { + ++mCount; + ss << "Background is " + mCheck.str() << std::endl; + } + return ss.str(); + } + + std::string checkTiles() + { + std::ostringstream ss; + const Index64 n = mCount; + typename CheckT::TileIterT i(mGrid->tree()); + for (i.setMaxDepth(GridT::TreeType::RootNodeType::LEVEL - 1); i; ++i) { + if (mCheck(i)) { + ++mCount; + if (mMask) mMask->fill(i.getBoundingBox(), true, true); + } + } + if (const Index64 m = mCount - n) { + ss << m << " tile" << (m==1 ? " is " : "s are ") + mCheck.str() << std::endl; + } + return ss.str(); + } + + std::string checkVoxels() + { + std::ostringstream ss; + LeafManagerT leafs(mGrid->tree()); + const Index64 n = mCount; + tbb::parallel_reduce(leafs.leafRange(), *this); + if (const Index64 m = mCount - n) { + ss << m << " voxel" << (m==1 ? " is " : "s are ") + mCheck.str() << std::endl; + } + return ss.str(); + } + + void operator()(const typename LeafManagerT::LeafRange& r) + { + typedef typename CheckT::VoxelIterT VoxelIterT; + if (mMask) { + for (typename LeafManagerT::LeafRange::Iterator i=r.begin(); i; ++i) { + typename MaskT::LeafNodeType* maskLeaf = NULL; + for (VoxelIterT j = tree::IterTraits::begin(*i); j; ++j) { + if (mCheck(j)) { + ++mCount; + if (maskLeaf == NULL) maskLeaf = mMask->touchLeaf(j.getCoord()); + maskLeaf->setValueOn(j.pos(), true); + } + } + } + } else { + for (typename LeafManagerT::LeafRange::Iterator i=r.begin(); i; ++i) { + for (VoxelIterT j = tree::IterTraits::begin(*i); j; ++j) { + if (mCheck(j)) ++mCount; + } + } + } + } + void join(const CheckValues& other) + { + if (mMask) mMask->merge(*(other.mMask), openvdb::MERGE_ACTIVE_STATES_AND_NODES); + mCount += other.mCount; + } + };//End of private class CheckValues + +};// End of public class Diagnose + + +//////////////////////////////////////////////////////////////////////////////// + +/// @brief Class that performs various types of checks on narrow-band level sets. +/// +/// @note The most common usage is to simply call CheckLevelSet::check() +template +class CheckLevelSet +{ +public: + typedef typename GridType::ValueType ValueType; + typedef typename GridType::template ValueConverter::Type MaskType; + + CheckLevelSet(const GridType& grid) : mDiagnose(grid) {} + + //@{ + /// @brief Return a boolean mask of all the values + /// (i.e. tiles and/or voxels) that have failed one or + /// more checks. + typename MaskType::ConstPtr mask() const { return mDiagnose.mask(); } + typename MaskType::Ptr mask() { return mDiagnose.mask(); } + //@} + + /// @brief Return the number of values (i.e. background, tiles or + /// voxels) that have failed one or more checks. + Index64 valueCount() const { return mDiagnose.valueCount(); } + + /// @brief Return total number of failed checks + /// @note If only one check was performed and the mask was updated + /// failureCount equals valueCount. + Index64 failureCount() const { return mDiagnose.failureCount(); } + + /// @brief Return a const reference to the grid + const GridType& grid() const { return mDiagnose.grid(); } + + /// @brief Clear the mask and error counter + void clear() { mDiagnose.clear(); } + + /// @brief Return a nonempty message if the grid's value type is a floating point. + /// + /// @note No run-time overhead + static std::string checkValueType() + { + static const bool test = boost::is_floating_point::value; + return test ? "" : "Value type is not floating point\n"; + } + + /// @brief Return message if the grid's class is a level set. + /// + /// @note Small run-time overhead + std::string checkClassType() const + { + const bool test = mDiagnose.grid().getGridClass() == GRID_LEVEL_SET; + return test ? "" : "Class type is not \"GRID_LEVEL_SET\"\n"; + } + + /// @brief Return a nonempty message if the grid's transform does not have uniform scaling. + /// + /// @note Small run-time overhead + std::string checkTransform() const + { + return mDiagnose.grid().hasUniformVoxels() ? "" : "Does not have uniform voxels\n"; + } + + /// @brief Return a nonempty message if the background value is larger than or + /// equal to the halfWidth*voxelSize. + /// + /// @note Small run-time overhead + std::string checkBackground(Real halfWidth = LEVEL_SET_HALF_WIDTH) const + { + const Real w = mDiagnose.grid().background() / mDiagnose.grid().voxelSize()[0]; + if (w < halfWidth) { + std::ostringstream ss; + ss << "The background value ("<< mDiagnose.grid().background()<<") is less than " + << halfWidth << " voxel units\n"; + return ss.str(); + } + return ""; + } + + /// @brief Return a nonempty message if the grid has no active tile values. + /// + /// @note Medium run-time overhead + std::string checkTiles() const + { + const bool test = mDiagnose.grid().tree().hasActiveTiles(); + return test ? "Has active tile values\n" : ""; + } + + /// @brief Return a nonempty message if any of the values are not finite. i.e. NaN or inf. + /// + /// @note Medium run-time overhead + std::string checkFinite(bool updateMask = false) + { + CheckFinite c; + return mDiagnose.check(c, updateMask, /*voxel*/true, /*tiles*/true, /*background*/true); + } + + /// @brief Return a nonempty message if the active voxel values are out-of-range. + /// + /// @note Medium run-time overhead + std::string checkRange(bool updateMask = false) + { + const ValueType& background = mDiagnose.grid().background(); + CheckRange c(-background, background); + return mDiagnose.check(c, updateMask, /*voxel*/true, /*tiles*/false, /*background*/false); + } + + /// @brief Return a nonempty message if the the inactive values do not have a + /// magnitude equal to the background value. + /// + /// @note Medium run-time overhead + std::string checkInactiveValues(bool updateMask = false) + { + const ValueType& background = mDiagnose.grid().background(); + CheckMagnitude c(background); + return mDiagnose.check(c, updateMask, /*voxel*/true, /*tiles*/true, /*background*/false); + } + + /// @brief Return a nonempty message if the norm of the gradient of the + /// active voxels is out of the range minV to maxV. + /// + /// @note Significant run-time overhead + std::string checkEikonal(bool updateMask = false, ValueType minV = 0.5, ValueType maxV = 1.5) + { + CheckEikonal c(mDiagnose.grid(), minV, maxV); + return mDiagnose.check(c, updateMask, /*voxel*/true, /*tiles*/false, /*background*/false); + } + + /// @brief Return a nonempty message if an error or issue is detected. Only + /// runs tests with a number lower than or equal to n, where: + /// + /// Fast checks + /// 1: value type is floating point + /// 2: has level set class type + /// 3: has uniform scale + /// 4: background value is positive and n*dx + /// + /// Slower checks + /// 5: no active tiles + /// 6: all the values are finite, i.e not NaN or infinite + /// 7: active values in range between +-background + /// 8: abs of inactive values = background, i.e. assuming a symmetric narrow band! + /// + /// Relatively slow check (however multi-threaded) + /// 9: norm of gradient at zero-crossings is one, i.e. satisfied the Eikonal equation. + std::string check(size_t n=9, bool updateMask = false) + { + std::string str = this->checkValueType(); + if (str.empty() && n>1) str = this->checkClassType(); + if (str.empty() && n>2) str = this->checkTransform(); + if (str.empty() && n>3) str = this->checkBackground(); + if (str.empty() && n>4) str = this->checkTiles(); + if (str.empty() && n>5) str = this->checkFinite(updateMask); + if (str.empty() && n>6) str = this->checkRange(updateMask); + if (str.empty() && n>7) str = this->checkInactiveValues(updateMask); + if (str.empty() && n>8) str = this->checkEikonal(updateMask); + return str; + } + +private: + // disallow copy construction and copy by assignment! + CheckLevelSet(const CheckLevelSet&);// not implemented + CheckLevelSet& operator=(const CheckLevelSet&);// not implemented + + // Member data + Diagnose mDiagnose; +};// CheckLevelSet + +template +std::string +checkLevelSet(const GridType& grid, size_t n) +{ + CheckLevelSet c(grid); + return c.check(n, false); +} + +//////////////////////////////////////////////////////////////////////////////// + +/// @brief Class that performs various types of checks on fog volumes. +/// +/// @note The most common usage is to simply call CheckFogVolume::check() +template +class CheckFogVolume +{ +public: + typedef typename GridType::ValueType ValueType; + typedef typename GridType::template ValueConverter::Type MaskType; + + CheckFogVolume(const GridType& grid) : mDiagnose(grid) {} + + //@{ + /// @brief Return a boolean mask of all the values + /// (i.e. tiles and/or voxels) that have failed one or + /// more checks. + typename MaskType::ConstPtr mask() const { return mDiagnose.mask(); } + typename MaskType::Ptr mask() { return mDiagnose.mask(); } + //@} + + /// @brief Return the number of values (i.e. background, tiles or + /// voxels) that have failed one or more checks. + Index64 valueCount() const { return mDiagnose.valueCount(); } + + /// @brief Return total number of failed checks + /// @note If only one check was performed and the mask was updated + /// failureCount equals valueCount. + Index64 failureCount() const { return mDiagnose.failureCount(); } + + /// @brief Return a const reference to the grid + const GridType& grid() const { return mDiagnose.grid(); } + + /// @brief Clear the mask and error counter + void clear() { mDiagnose.clear(); } + + /// @brief Return a nonempty message if the grid's value type is a floating point. + /// + /// @note No run-time overhead + static std::string checkValueType() + { + static const bool test = boost::is_floating_point::value; + return test ? "" : "Value type is not floating point"; + } + + /// @brief Return a nonempty message if the grid's class is a level set. + /// + /// @note Small run-time overhead + std::string checkClassType() const + { + const bool test = mDiagnose.grid().getGridClass() == GRID_FOG_VOLUME; + return test ? "" : "Class type is not \"GRID_LEVEL_SET\""; + } + + /// @brief Return a nonempty message if the background value is not zero. + /// + /// @note Small run-time overhead + std::string checkBackground() const + { + if (!math::isApproxZero(mDiagnose.grid().background())) { + std::ostringstream ss; + ss << "The background value ("<< mDiagnose.grid().background()<<") is not zero"; + return ss.str(); + } + return ""; + } + + /// @brief Return a nonempty message if any of the values are not finite. i.e. NaN or inf. + /// + /// @note Medium run-time overhead + std::string checkFinite(bool updateMask = false) + { + CheckFinite c; + return mDiagnose.check(c, updateMask, /*voxel*/true, /*tiles*/true, /*background*/true); + } + + /// @brief Return a nonempty message if any of the inactive values are not zero. + /// + /// @note Medium run-time overhead + std::string checkInactiveValues(bool updateMask = false) + { + CheckMagnitude c(0); + return mDiagnose.check(c, updateMask, /*voxel*/true, /*tiles*/true, /*background*/true); + } + + /// @brief Return a nonempty message if the active voxel values + /// are out-of-range, i.e. not in the range [0,1]. + /// + /// @note Medium run-time overhead + std::string checkRange(bool updateMask = false) + { + CheckRange c(0, 1); + return mDiagnose.check(c, updateMask, /*voxel*/true, /*tiles*/true, /*background*/false); + } + + /// @brief Return a nonempty message if an error or issue is detected. Only + /// runs tests with a number lower than or equal to n, where: + /// + /// Fast checks + /// 1: value type is floating point + /// 2: has FOG volume class type + /// 3: background value is zero + /// + /// Slower checks + /// 4: all the values are finite, i.e not NaN or infinite + /// 5: inactive values are zero + /// 6: active values are in the range [0,1] + std::string check(size_t n=6, bool updateMask = false) + { + std::string str = this->checkValueType(); + if (str.empty() && n>1) str = this->checkClassType(); + if (str.empty() && n>2) str = this->checkBackground(); + if (str.empty() && n>3) str = this->checkFinite(updateMask); + if (str.empty() && n>4) str = this->checkInactiveValues(updateMask); + if (str.empty() && n>5) str = this->checkRange(updateMask); + return str; + } + +private: + // disallow copy construction and copy by assignment! + CheckFogVolume(const CheckFogVolume&);// not implemented + CheckFogVolume& operator=(const CheckFogVolume&);// not implemented + + // Member data + Diagnose mDiagnose; +};// CheckFogVolume + +template +std::string +checkFogVolume(const GridType& grid, size_t n) +{ + CheckFogVolume c(grid); + return c.check(n, false); +} + + +//////////////////////////////////////////////////////////////////////////////// + +// Internal utility objects and implementation details + + +namespace diagnostics_internal { + + +template +class InactiveVoxelValues +{ +public: + typedef tree::LeafManager LeafArray; + typedef typename TreeType::ValueType ValueType; + typedef std::set SetType; + + InactiveVoxelValues(LeafArray&, size_t numValues); + + void runParallel(); + void runSerial(); + + void getInactiveValues(SetType&) const; + + inline InactiveVoxelValues(const InactiveVoxelValues&, tbb::split); + inline void operator()(const tbb::blocked_range&); + inline void join(const InactiveVoxelValues&); + +private: + LeafArray& mLeafArray; + SetType mInactiveValues; + size_t mNumValues; +};// InactiveVoxelValues + +template +InactiveVoxelValues::InactiveVoxelValues(LeafArray& leafs, size_t numValues) + : mLeafArray(leafs) + , mInactiveValues() + , mNumValues(numValues) +{ +} + +template +inline +InactiveVoxelValues::InactiveVoxelValues( + const InactiveVoxelValues& rhs, tbb::split) + : mLeafArray(rhs.mLeafArray) + , mInactiveValues() + , mNumValues(rhs.mNumValues) +{ +} + +template +void +InactiveVoxelValues::runParallel() +{ + tbb::parallel_reduce(mLeafArray.getRange(), *this); +} + + +template +void +InactiveVoxelValues::runSerial() +{ + (*this)(mLeafArray.getRange()); +} + + +template +inline void +InactiveVoxelValues::operator()(const tbb::blocked_range& range) +{ + typename TreeType::LeafNodeType::ValueOffCIter iter; + + for (size_t n = range.begin(); n < range.end() && !tbb::task::self().is_cancelled(); ++n) { + for (iter = mLeafArray.leaf(n).cbeginValueOff(); iter; ++iter) { + mInactiveValues.insert(iter.getValue()); + } + + if (mInactiveValues.size() > mNumValues) { + tbb::task::self().cancel_group_execution(); + } + } +} + +template +inline void +InactiveVoxelValues::join(const InactiveVoxelValues& rhs) +{ + mInactiveValues.insert(rhs.mInactiveValues.begin(), rhs.mInactiveValues.end()); +} + +template +inline void +InactiveVoxelValues::getInactiveValues(SetType& values) const +{ + values.insert(mInactiveValues.begin(), mInactiveValues.end()); +} + + +//////////////////////////////////////// + + +template +class InactiveTileValues +{ +public: + typedef tree::IteratorRange IterRange; + typedef typename TreeType::ValueType ValueType; + typedef std::set SetType; + + InactiveTileValues(size_t numValues); + + void runParallel(IterRange&); + void runSerial(IterRange&); + + void getInactiveValues(SetType&) const; + + inline InactiveTileValues(const InactiveTileValues&, tbb::split); + inline void operator()(IterRange&); + inline void join(const InactiveTileValues&); + +private: + SetType mInactiveValues; + size_t mNumValues; +}; + + +template +InactiveTileValues::InactiveTileValues(size_t numValues) + : mInactiveValues() + , mNumValues(numValues) +{ +} + +template +inline +InactiveTileValues::InactiveTileValues( + const InactiveTileValues& rhs, tbb::split) + : mInactiveValues() + , mNumValues(rhs.mNumValues) +{ +} + +template +void +InactiveTileValues::runParallel(IterRange& range) +{ + tbb::parallel_reduce(range, *this); +} + + +template +void +InactiveTileValues::runSerial(IterRange& range) +{ + (*this)(range); +} + + +template +inline void +InactiveTileValues::operator()(IterRange& range) +{ + for (; range && !tbb::task::self().is_cancelled(); ++range) { + typename TreeType::ValueOffCIter iter = range.iterator(); + for (; iter; ++iter) { + mInactiveValues.insert(iter.getValue()); + } + + if (mInactiveValues.size() > mNumValues) { + tbb::task::self().cancel_group_execution(); + } + } +} + +template +inline void +InactiveTileValues::join(const InactiveTileValues& rhs) +{ + mInactiveValues.insert(rhs.mInactiveValues.begin(), rhs.mInactiveValues.end()); +} + +template +inline void +InactiveTileValues::getInactiveValues(SetType& values) const +{ + values.insert(mInactiveValues.begin(), mInactiveValues.end()); +} + +} // namespace diagnostics_internal + + +//////////////////////////////////////// + + +template +bool +uniqueInactiveValues(const GridType& grid, + std::vector& values, size_t numValues) +{ + + typedef typename GridType::TreeType TreeType; + typedef typename GridType::ValueType ValueType; + typedef std::set SetType; + + SetType uniqueValues; + + { // Check inactive voxels + TreeType& tree = const_cast(grid.tree()); + tree::LeafManager leafs(tree); + diagnostics_internal::InactiveVoxelValues voxelOp(leafs, numValues); + voxelOp.runParallel(); + voxelOp.getInactiveValues(uniqueValues); + } + + // Check inactive tiles + if (uniqueValues.size() <= numValues) { + typename TreeType::ValueOffCIter iter(grid.tree()); + iter.setMaxDepth(TreeType::ValueAllIter::LEAF_DEPTH - 1); + diagnostics_internal::InactiveTileValues tileOp(numValues); + + tree::IteratorRange range(iter); + tileOp.runParallel(range); + + tileOp.getInactiveValues(uniqueValues); + } + + values.clear(); + values.reserve(uniqueValues.size()); + + typename SetType::iterator it = uniqueValues.begin(); + for ( ; it != uniqueValues.end(); ++it) { + values.push_back(*it); + } + + return values.size() <= numValues; +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_DIAGNOSTICS_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/Filter.h b/nuparu/include/openvdb/include/openvdb/tools/Filter.h new file mode 100644 index 00000000..9bc124b8 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/Filter.h @@ -0,0 +1,459 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +/// +/// @brief Filtering of VDB volumes. Note that only the values in the +/// grid are changed, not its topology! All operations can optionally +/// be masked with another grid that acts as an alpha-mask. + +#ifndef OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_FILTER_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Interpolation.h" + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Volume filtering (e.g., diffusion) with optional alpha masking +/// +/// @note Only the values in the grid are changed, not its topology! +template::Type, + typename InterruptT = util::NullInterrupter> +class Filter +{ +public: + typedef GridT GridType; + typedef MaskT MaskType; + typedef typename GridType::TreeType TreeType; + typedef typename TreeType::LeafNodeType LeafType; + typedef typename GridType::ValueType ValueType; + typedef typename MaskType::ValueType AlphaType; + typedef typename tree::LeafManager LeafManagerType; + typedef typename LeafManagerType::LeafRange RangeType; + typedef typename LeafManagerType::BufferType BufferType; + BOOST_STATIC_ASSERT(boost::is_floating_point::value); + + /// Constructor + /// @param grid Grid to be filtered. + /// @param interrupt Optional interrupter. + Filter(GridT& grid, InterruptT* interrupt = NULL) + : mGrid(&grid) + , mTask(0) + , mInterrupter(interrupt) + , mMask(NULL) + , mGrainSize(1) + , mMinMask(0) + , mMaxMask(1) + , mInvertMask(false) + { + } + + /// @brief Shallow copy constructor called by tbb::parallel_for() + /// threads during filtering. + /// @param other The other Filter from which to copy. + Filter(const Filter& other) + : mGrid(other.mGrid) + , mTask(other.mTask) + , mInterrupter(other.mInterrupter) + , mMask(other.mMask) + , mGrainSize(other.mGrainSize) + , mMinMask(other.mMinMask) + , mMaxMask(other.mMaxMask) + , mInvertMask(other.mInvertMask) + { + } + + /// @return the grain-size used for multi-threading + int getGrainSize() const { return mGrainSize; } + /// @brief Set the grain-size used for multi-threading. + /// @note A grain size of 0 or less disables multi-threading! + void setGrainSize(int grainsize) { mGrainSize = grainsize; } + + /// @brief Return the minimum value of the mask to be used for the + /// derivation of a smooth alpha value. + AlphaType minMask() const { return mMinMask; } + /// @brief Return the maximum value of the mask to be used for the + /// derivation of a smooth alpha value. + AlphaType maxMask() const { return mMaxMask; } + /// @brief Define the range for the (optional) scalar mask. + /// @param min Minimum value of the range. + /// @param max Maximum value of the range. + /// @details Mask values outside the range are clamped to zero or one, and + /// values inside the range map smoothly to 0->1 (unless the mask is inverted). + /// @throw ValueError if @a min is not smaller than @a max. + void setMaskRange(AlphaType min, AlphaType max) + { + if (!(min < max)) OPENVDB_THROW(ValueError, "Invalid mask range (expects min < max)"); + mMinMask = min; + mMaxMask = max; + } + + /// @brief Return true if the mask is inverted, i.e. min->max in the + /// original mask maps to 1->0 in the inverted alpha mask. + bool isMaskInverted() const { return mInvertMask; } + /// @brief Invert the optional mask, i.e. min->max in the original + /// mask maps to 1->0 in the inverted alpha mask. + void invertMask(bool invert=true) { mInvertMask = invert; } + + /// @brief One iteration of a fast separable mean-value (i.e. box) filter. + /// @param width The width of the mean-value filter is 2*width+1 voxels. + /// @param iterations Number of times the mean-value filter is applied. + /// @param mask Optional alpha mask. + void mean(int width = 1, int iterations = 1, const MaskType* mask = NULL); + + /// @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%! + /// @param width The width of the mean-value filter is 2*width+1 voxels. + /// @param iterations Number of times the mean-value filter is applied. + /// @param mask Optional alpha mask. + void gaussian(int width = 1, int iterations = 1, const MaskType* mask = NULL); + + /// @brief One iteration of a median-value filter + /// + /// @note This filter is not separable and is hence relatively slow! + /// @param width The width of the mean-value filter is 2*width+1 voxels. + /// @param iterations Number of times the mean-value filter is applied. + /// @param mask Optional alpha mask. + void median(int width = 1, int iterations = 1, const MaskType* mask = NULL); + + /// Offsets (i.e. adds) a constant value to all active voxels. + /// @param offset Offset in the same units as the grid. + /// @param mask Optional alpha mask. + void offset(ValueType offset, const MaskType* mask = NULL); + + /// @brief Used internally by tbb::parallel_for() + /// @param range Range of LeafNodes over which to multi-thread. + /// + /// @warning Never call this method directly! + void operator()(const RangeType& range) const + { + if (mTask) mTask(const_cast(this), range); + else OPENVDB_THROW(ValueError, "task is undefined - call median(), mean(), etc."); + } + +private: + typedef typename TreeType::LeafNodeType LeafT; + typedef typename LeafT::ValueOnIter VoxelIterT; + typedef typename LeafT::ValueOnCIter VoxelCIterT; + typedef typename tree::LeafManager::BufferType BufferT; + typedef typename RangeType::Iterator LeafIterT; + typedef tools::AlphaMask AlphaMaskT; + + void cook(LeafManagerType& leafs); + + template + struct Avg { + Avg(const GridT* grid, Int32 w): acc(grid->tree()), width(w), frac(1.f/float(2*w+1)) {} + inline ValueType operator()(Coord xyz); + typename GridT::ConstAccessor acc; + const Int32 width; + const float frac; + }; + + // Private filter methods called by tbb::parallel_for threads + template + void doBox( const RangeType& r, Int32 w); + void doBoxX(const RangeType& r, Int32 w) { this->doBox >(r,w); } + void doBoxZ(const RangeType& r, Int32 w) { this->doBox >(r,w); } + void doBoxY(const RangeType& r, Int32 w) { this->doBox >(r,w); } + void doMedian(const RangeType&, int); + void doOffset(const RangeType&, ValueType); + /// @return true if the process was interrupted + bool wasInterrupted(); + + GridType* mGrid; + typename boost::function mTask; + InterruptT* mInterrupter; + const MaskType* mMask; + int mGrainSize; + AlphaType mMinMask, mMaxMask; + bool mInvertMask; +}; // end of Filter class + + +//////////////////////////////////////// + + +namespace filter_internal { +// Helper function for Filter::Avg::operator() +template static inline void accum(T& sum, T addend) { sum += addend; } +// Overload for bool ValueType +inline void accum(bool& sum, bool addend) { sum = sum || addend; } +} + + +template +template +inline typename GridT::ValueType +Filter::Avg::operator()(Coord xyz) +{ + ValueType sum = zeroVal(); + Int32 &i = xyz[Axis], j = i + width; + for (i -= width; i <= j; ++i) filter_internal::accum(sum, acc.getValue(xyz)); + return static_cast(sum * frac); +} + + +//////////////////////////////////////// + + +template +inline void +Filter::mean(int width, int iterations, const MaskType* mask) +{ + mMask = mask; + + if (mInterrupter) mInterrupter->start("Applying mean filter"); + + const int w = std::max(1, width); + + LeafManagerType leafs(mGrid->tree(), 1, mGrainSize==0); + + for (int i=0; iwasInterrupted(); ++i) { + mTask = boost::bind(&Filter::doBoxX, _1, _2, w); + this->cook(leafs); + + mTask = boost::bind(&Filter::doBoxY, _1, _2, w); + this->cook(leafs); + + mTask = boost::bind(&Filter::doBoxZ, _1, _2, w); + this->cook(leafs); + } + + if (mInterrupter) mInterrupter->end(); +} + + +template +inline void +Filter::gaussian(int width, int iterations, const MaskType* mask) +{ + mMask = mask; + + if (mInterrupter) mInterrupter->start("Applying Gaussian filter"); + + const int w = std::max(1, width); + + LeafManagerType leafs(mGrid->tree(), 1, mGrainSize==0); + + for (int i=0; iwasInterrupted(); ++n) { + mTask = boost::bind(&Filter::doBoxX, _1, _2, w); + this->cook(leafs); + + mTask = boost::bind(&Filter::doBoxY, _1, _2, w); + this->cook(leafs); + + mTask = boost::bind(&Filter::doBoxZ, _1, _2, w); + this->cook(leafs); + } + } + + if (mInterrupter) mInterrupter->end(); +} + + +template +inline void +Filter::median(int width, int iterations, const MaskType* mask) +{ + mMask = mask; + + if (mInterrupter) mInterrupter->start("Applying median filter"); + + LeafManagerType leafs(mGrid->tree(), 1, mGrainSize==0); + + mTask = boost::bind(&Filter::doMedian, _1, _2, std::max(1, width)); + for (int i=0; iwasInterrupted(); ++i) this->cook(leafs); + + if (mInterrupter) mInterrupter->end(); +} + + +template +inline void +Filter::offset(ValueType value, const MaskType* mask) +{ + mMask = mask; + + if (mInterrupter) mInterrupter->start("Applying offset"); + + LeafManagerType leafs(mGrid->tree(), 0, mGrainSize==0); + + mTask = boost::bind(&Filter::doOffset, _1, _2, value); + this->cook(leafs); + + if (mInterrupter) mInterrupter->end(); +} + + +//////////////////////////////////////// + + +/// Private method to perform the task (serial or threaded) and +/// subsequently swap the leaf buffers. +template +inline void +Filter::cook(LeafManagerType& leafs) +{ + if (mGrainSize>0) { + tbb::parallel_for(leafs.leafRange(mGrainSize), *this); + } else { + (*this)(leafs.leafRange()); + } + leafs.swapLeafBuffer(1, mGrainSize==0); +} + + +/// One dimensional convolution of a separable box filter +template +template +inline void +Filter::doBox(const RangeType& range, Int32 w) +{ + this->wasInterrupted(); + AvgT avg(mGrid, w); + if (mMask) { + typename AlphaMaskT::FloatType a, b; + AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask); + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + BufferT& buffer = leafIter.buffer(1); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + const Coord xyz = iter.getCoord(); + if (alpha(xyz, a, b)) { + buffer.setValue(iter.pos(), ValueType(b*(*iter) + a*avg(xyz))); + } + } + } + } else { + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + BufferT& buffer = leafIter.buffer(1); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + buffer.setValue(iter.pos(), avg(iter.getCoord())); + } + } + } +} + + +/// Performs simple but slow median-value diffusion +template +inline void +Filter::doMedian(const RangeType& range, int width) +{ + this->wasInterrupted(); + typename math::DenseStencil stencil(*mGrid, width);//creates local cache! + if (mMask) { + typename AlphaMaskT::FloatType a, b; + AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask); + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + BufferT& buffer = leafIter.buffer(1); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + if (alpha(iter.getCoord(), a, b)) { + stencil.moveTo(iter); + buffer.setValue(iter.pos(), ValueType(b*(*iter) + a*stencil.median())); + } + } + } + } else { + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + BufferT& buffer = leafIter.buffer(1); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + stencil.moveTo(iter); + buffer.setValue(iter.pos(), stencil.median()); + } + } + } +} + + +/// Offsets the values by a constant +template +inline void +Filter::doOffset(const RangeType& range, ValueType offset) +{ + this->wasInterrupted(); + if (mMask) { + typename AlphaMaskT::FloatType a, b; + AlphaMaskT alpha(*mGrid, *mMask, mMinMask, mMaxMask, mInvertMask); + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) { + if (alpha(iter.getCoord(), a, b)) iter.setValue(ValueType(*iter + a*offset)); + } + } + } else { + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) { + iter.setValue(*iter + offset); + } + } + } +} + + +template +inline bool +Filter::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-2016 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/nuparu/include/openvdb/include/openvdb/tools/GridOperators.h b/nuparu/include/openvdb/include/openvdb/tools/GridOperators.h new file mode 100644 index 00000000..a5b94a7d --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/GridOperators.h @@ -0,0 +1,1090 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief VectorToScalarConverter::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::Type is equivalent to DoubleGrid. +template struct VectorToScalarConverter { + typedef typename VectorGridType::ValueType::value_type VecComponentValueT; + typedef typename VectorGridType::template ValueConverter::Type Type; +}; + +/// @brief ScalarToVectorConverter::Type is the type of a grid +/// having the same tree configuration as ScalarGridType but value type Vec3 +/// where T is ScalarGridType::ValueType. +/// @details For example, ScalarToVectorConverter::Type is equivalent to Vec3DGrid. +template struct ScalarToVectorConverter { + typedef math::Vec3 VectorValueT; + typedef typename ScalarGridType::template ValueConverter::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) +/// @details When a mask grid is specified, the solution is calculated only in +/// the intersection of the mask active topology and the input active topology +/// independent of the transforms associated with either grid. +/// @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 inline +typename ScalarToVectorConverter::Type::Ptr +cpt(const GridType& grid, bool threaded, InterruptT* interrupt); + +template inline +typename ScalarToVectorConverter::Type::Ptr +cpt(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt); + +template inline +typename ScalarToVectorConverter::Type::Ptr +cpt(const GridType& grid, bool threaded = true) +{ + return cpt(grid, threaded, NULL); +} + +template inline +typename ScalarToVectorConverter::Type::Ptr +cpt(const GridType& grid, const MaskT& mask, bool threaded = true) +{ + return cpt(grid, mask, threaded, NULL); +} + + +/// @brief Compute the curl of the given vector-valued grid. +/// @return a new vector-valued grid +/// @details When a mask grid is specified, the solution is calculated only in +/// the intersection of the mask active topology and the input active topology +/// independent of the transforms associated with either grid. +template inline +typename GridType::Ptr +curl(const GridType& grid, bool threaded, InterruptT* interrupt); + +template inline +typename GridType::Ptr +curl(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt); + +template inline +typename GridType::Ptr +curl(const GridType& grid, bool threaded = true) +{ + return curl(grid, threaded, NULL); +} + +template inline +typename GridType::Ptr +curl(const GridType& grid, const MaskT& mask, bool threaded = true) +{ + return curl(grid, mask, 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) +/// @details When a mask grid is specified, the solution is calculated only in +/// the intersection of the mask active topology and the input active topology +/// independent of the transforms associated with either grid. +template inline +typename VectorToScalarConverter::Type::Ptr +divergence(const GridType& grid, bool threaded, InterruptT* interrupt); + +template inline +typename VectorToScalarConverter::Type::Ptr +divergence(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt); + +template inline +typename VectorToScalarConverter::Type::Ptr +divergence(const GridType& grid, bool threaded = true) +{ + return divergence(grid, threaded, NULL); +} + +template inline +typename VectorToScalarConverter::Type::Ptr +divergence(const GridType& grid, const MaskT& mask, bool threaded = true) +{ + return divergence(grid, mask, 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) +/// @details When a mask grid is specified, the solution is calculated only in +/// the intersection of the mask active topology and the input active topology +/// independent of the transforms associated with either grid. +template inline +typename ScalarToVectorConverter::Type::Ptr +gradient(const GridType& grid, bool threaded, InterruptT* interrupt); + +template inline +typename ScalarToVectorConverter::Type::Ptr +gradient(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt); + +template inline +typename ScalarToVectorConverter::Type::Ptr +gradient(const GridType& grid, bool threaded = true) +{ + return gradient(grid, threaded, NULL); +} + +template inline +typename ScalarToVectorConverter::Type::Ptr +gradient(const GridType& grid, const MaskT& mask, bool threaded = true) +{ + return gradient(grid, mask, threaded, NULL); +} + + +/// @brief Compute the Laplacian of the given scalar grid. +/// @return a new scalar grid +/// @details When a mask grid is specified, the solution is calculated only in +/// the intersection of the mask active topology and the input active topology +/// independent of the transforms associated with either grid. +template inline +typename GridType::Ptr +laplacian(const GridType& grid, bool threaded, InterruptT* interrupt); + +template inline +typename GridType::Ptr +laplacian(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt); + +template inline +typename GridType::Ptr +laplacian(const GridType& grid, bool threaded = true) +{ + return laplacian(grid, threaded, NULL); +} + +template inline +typename GridType::Ptr +laplacian(const GridType& grid, const MaskT mask, bool threaded = true) +{ + return laplacian(grid, mask, threaded, NULL); +} + + +/// @brief Compute the mean curvature of the given grid. +/// @return a new grid +/// @details When a mask grid is specified, the solution is calculated only in +/// the intersection of the mask active topology and the input active topology +/// independent of the transforms associated with either grid. +template inline +typename GridType::Ptr +meanCurvature(const GridType& grid, bool threaded, InterruptT* interrupt); + +template inline +typename GridType::Ptr +meanCurvature(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt); + +template inline +typename GridType::Ptr +meanCurvature(const GridType& grid, bool threaded = true) +{ + return meanCurvature(grid, threaded, NULL); +} + +template inline +typename GridType::Ptr +meanCurvature(const GridType& grid, const MaskT& mask, bool threaded = true) +{ + return meanCurvature(grid, mask, 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) +/// @details When a mask grid is specified, the solution is calculated only in +/// the intersection of the mask active topology and the input active topology +/// independent of the transforms associated with either grid. +template inline +typename VectorToScalarConverter::Type::Ptr +magnitude(const GridType& grid, bool threaded, InterruptT* interrupt); + +template inline +typename VectorToScalarConverter::Type::Ptr +magnitude(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt); + +template inline +typename VectorToScalarConverter::Type::Ptr +magnitude(const GridType& grid, bool threaded = true) +{ + return magnitude(grid, threaded, NULL); +} + +template inline +typename VectorToScalarConverter::Type::Ptr +magnitude(const GridType& grid, const MaskT& mask, bool threaded = true) +{ + return magnitude(grid, mask, threaded, NULL); +} + + +/// @brief Normalize the vectors of the given vector-valued grid. +/// @return a new vector-valued grid +/// @details When a mask grid is specified, the solution is calculated only in +/// the intersection of the mask active topology and the input active topology +/// independent of the transforms associated with either grid. +template inline +typename GridType::Ptr +normalize(const GridType& grid, bool threaded, InterruptT* interrupt); + +template inline +typename GridType::Ptr +normalize(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt); + +template inline +typename GridType::Ptr +normalize(const GridType& grid, bool threaded = true) +{ + return normalize(grid, threaded, NULL); +} + +template inline +typename GridType::Ptr +normalize(const GridType& grid, const MaskT& mask, bool threaded = true) +{ + return normalize(grid, mask, threaded, NULL); +} + + +//////////////////////////////////////// + + +namespace gridop { + +/// @brief ToMaskGrid::Type is the type of a grid having the same +/// tree hierarchy as grid type T but a value equal to its active state. +/// @details For example, ToMaskGrid::Type is equivalent to MaskGrid. +template +struct ToMaskGrid { + typedef Grid::Type> Type; +}; + + +/// @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 MaskGridType, + 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 LeafManagerT; + + GridOperator(const InGridT& grid, const MaskGridType* mask, const MapT& map, + InterruptT* interrupt = NULL): + mAcc(grid.getConstAccessor()), mMap(map), mInterrupt(interrupt), mMask(mask) + { + } + + 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.tree().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.tree(), backg, TopologyCopy())); + + + // create grid with output tree and unit transform + typename OutGridT::Ptr result(new OutGridT(tree)); + + // Modify the solution area if a mask was supplied. + if (mMask) { + result->topologyIntersection(*mMask); + } + + // 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; + const MaskGridType* mMask; +}; // end of GridOperator class + +} // namespace gridop + + +//////////////////////////////////////// + + +/// @brief Compute the closest-point transform of a scalar grid. +template< + typename InGridT, + typename MaskGridType = typename gridop::ToMaskGrid::Type, + typename InterruptT = util::NullInterrupter> +class Cpt +{ +public: + typedef InGridT InGridType; + typedef typename ScalarToVectorConverter::Type OutGridType; + + Cpt(const InGridType& grid, InterruptT* interrupt = NULL): + mInputGrid(grid), mInterrupt(interrupt), mMask(NULL) + { + } + + Cpt(const InGridType& grid, const MaskGridType& mask, InterruptT* interrupt = NULL): + mInputGrid(grid), mInterrupt(interrupt), mMask(&mask) + { + } + + typename OutGridType::Ptr process(bool threaded = true, bool useWorldTransform = true) + { + Functor functor(mInputGrid, mMask, threaded, useWorldTransform, mInterrupt); + processTypedMap(mInputGrid.transform(), functor); + if (functor.mOutputGrid) functor.mOutputGrid->setVectorType(VEC_CONTRAVARIANT_ABSOLUTE); + return functor.mOutputGrid; + } + +private: + struct IsOpT + { + template + static typename OutGridType::ValueType + result(const MapT& map, const AccT& acc, const Coord& xyz) + { + return math::CPT::result(map, acc, xyz); + } + }; + struct WsOpT + { + template + static typename OutGridType::ValueType + result(const MapT& map, const AccT& acc, const Coord& xyz) + { + return math::CPT_RANGE::result(map, acc, xyz); + } + }; + struct Functor + { + Functor(const InGridType& grid, const MaskGridType* mask, + bool threaded, bool worldspace, InterruptT* interrupt) + : mThreaded(threaded) + , mWorldSpace(worldspace) + , mInputGrid(grid) + , mInterrupt(interrupt) + , mMask(mask) + {} + + template + void operator()(const MapT& map) + { + if (mWorldSpace) { + gridop::GridOperator + op(mInputGrid, mMask, map, mInterrupt); + mOutputGrid = op.process(mThreaded); // cache the result + } else { + gridop::GridOperator + op(mInputGrid, mMask, 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 MaskGridType* mMask; + }; + const InGridType& mInputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; +}; // end of Cpt class + + +//////////////////////////////////////// + + +/// @brief Compute the curl of a vector grid. +template< + typename GridT, + typename MaskGridType = typename gridop::ToMaskGrid::Type, + typename InterruptT = util::NullInterrupter> +class Curl +{ +public: + typedef GridT InGridType; + typedef GridT OutGridType; + + Curl(const GridT& grid, InterruptT* interrupt = NULL): + mInputGrid(grid), mInterrupt(interrupt), mMask(NULL) + { + } + + Curl(const GridT& grid, const MaskGridType& mask, InterruptT* interrupt = NULL): + mInputGrid(grid), mInterrupt(interrupt), mMask(&mask) + { + } + + typename GridT::Ptr process(bool threaded = true) + { + Functor functor(mInputGrid, mMask, threaded, mInterrupt); + processTypedMap(mInputGrid.transform(), functor); + if (functor.mOutputGrid) functor.mOutputGrid->setVectorType(VEC_COVARIANT); + return functor.mOutputGrid; + } + +private: + struct Functor + { + Functor(const GridT& grid, const MaskGridType* mask, + bool threaded, InterruptT* interrupt): + mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt), mMask(mask) {} + + template + void operator()(const MapT& map) + { + typedef math::Curl OpT; + gridop::GridOperator + op(mInputGrid, mMask, map, mInterrupt); + mOutputGrid = op.process(mThreaded); // cache the result + } + + const bool mThreaded; + const GridT& mInputGrid; + typename GridT::Ptr mOutputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; + }; // Private Functor + + const GridT& mInputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; +}; // end of Curl class + + +//////////////////////////////////////// + + +/// @brief Compute the divergence of a vector grid. +template< + typename InGridT, + typename MaskGridType = typename gridop::ToMaskGrid::Type, + typename InterruptT = util::NullInterrupter> +class Divergence +{ +public: + typedef InGridT InGridType; + typedef typename VectorToScalarConverter::Type OutGridType; + + Divergence(const InGridT& grid, InterruptT* interrupt = NULL): + mInputGrid(grid), mInterrupt(interrupt), mMask(NULL) + { + } + + Divergence(const InGridT& grid, const MaskGridType& mask, InterruptT* interrupt = NULL): + mInputGrid(grid), mInterrupt(interrupt), mMask(&mask) + { + } + + typename OutGridType::Ptr process(bool threaded = true) + { + if (mInputGrid.getGridClass() == GRID_STAGGERED) { + Functor functor(mInputGrid, mMask, threaded, mInterrupt); + processTypedMap(mInputGrid.transform(), functor); + return functor.mOutputGrid; + } else { + Functor functor(mInputGrid, mMask, threaded, mInterrupt); + processTypedMap(mInputGrid.transform(), functor); + return functor.mOutputGrid; + } + } + +protected: + template + struct Functor + { + Functor(const InGridT& grid, const MaskGridType* mask, + bool threaded, InterruptT* interrupt): + mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt), mMask(mask) {} + + template + void operator()(const MapT& map) + { + typedef math::Divergence OpT; + gridop::GridOperator + op(mInputGrid, mMask, map, mInterrupt); + mOutputGrid = op.process(mThreaded); // cache the result + } + + const bool mThreaded; + const InGridType& mInputGrid; + typename OutGridType::Ptr mOutputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; + }; // Private Functor + + const InGridType& mInputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; +}; // end of Divergence class + + +//////////////////////////////////////// + + +/// @brief Compute the gradient of a scalar grid. +template< + typename InGridT, + typename MaskGridType = typename gridop::ToMaskGrid::Type, + typename InterruptT = util::NullInterrupter> +class Gradient +{ +public: + typedef InGridT InGridType; + typedef typename ScalarToVectorConverter::Type OutGridType; + + Gradient(const InGridT& grid, InterruptT* interrupt = NULL): + mInputGrid(grid), mInterrupt(interrupt), mMask(NULL) + { + } + + Gradient(const InGridT& grid, const MaskGridType& mask, InterruptT* interrupt = NULL): + mInputGrid(grid), mInterrupt(interrupt), mMask(&mask) + { + } + + typename OutGridType::Ptr process(bool threaded = true) + { + Functor functor(mInputGrid, mMask, threaded, mInterrupt); + processTypedMap(mInputGrid.transform(), functor); + if (functor.mOutputGrid) functor.mOutputGrid->setVectorType(VEC_COVARIANT); + return functor.mOutputGrid; + } + +protected: + struct Functor + { + Functor(const InGridT& grid, const MaskGridType* mask, + bool threaded, InterruptT* interrupt): + mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt), mMask(mask) {} + + template + void operator()(const MapT& map) + { + typedef math::Gradient OpT; + gridop::GridOperator + op(mInputGrid, mMask, map, mInterrupt); + mOutputGrid = op.process(mThreaded); // cache the result + } + + const bool mThreaded; + const InGridT& mInputGrid; + typename OutGridType::Ptr mOutputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; + }; // Private Functor + + const InGridT& mInputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; +}; // end of Gradient class + + +//////////////////////////////////////// + + +template< + typename GridT, + typename MaskGridType = typename gridop::ToMaskGrid::Type, + typename InterruptT = util::NullInterrupter> +class Laplacian +{ +public: + typedef GridT InGridType; + typedef GridT OutGridType; + + Laplacian(const GridT& grid, InterruptT* interrupt = NULL): + mInputGrid(grid), mInterrupt(interrupt), mMask(NULL) + { + } + + Laplacian(const GridT& grid, const MaskGridType& mask, InterruptT* interrupt = NULL): + mInputGrid(grid), mInterrupt(interrupt), mMask(&mask) + { + } + + typename GridT::Ptr process(bool threaded = true) + { + Functor functor(mInputGrid, mMask, threaded, mInterrupt); + processTypedMap(mInputGrid.transform(), functor); + if (functor.mOutputGrid) functor.mOutputGrid->setVectorType(VEC_COVARIANT); + return functor.mOutputGrid; + } + +protected: + struct Functor + { + Functor(const GridT& grid, const MaskGridType* mask, bool threaded, InterruptT* interrupt): + mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt), mMask(mask) {} + + template + void operator()(const MapT& map) + { + typedef math::Laplacian OpT; + gridop::GridOperator + op(mInputGrid, mMask, map); + mOutputGrid = op.process(mThreaded); // cache the result + } + + const bool mThreaded; + const GridT& mInputGrid; + typename GridT::Ptr mOutputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; + }; // Private Functor + + const GridT& mInputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; +}; // end of Laplacian class + + +//////////////////////////////////////// + + +template< + typename GridT, + typename MaskGridType = typename gridop::ToMaskGrid::Type, + typename InterruptT = util::NullInterrupter> +class MeanCurvature +{ +public: + typedef GridT InGridType; + typedef GridT OutGridType; + + MeanCurvature(const GridT& grid, InterruptT* interrupt = NULL): + mInputGrid(grid), mInterrupt(interrupt), mMask(NULL) + { + } + + MeanCurvature(const GridT& grid, const MaskGridType& mask, InterruptT* interrupt = NULL): + mInputGrid(grid), mInterrupt(interrupt), mMask(&mask) + { + } + + typename GridT::Ptr process(bool threaded = true) + { + Functor functor(mInputGrid, mMask, threaded, mInterrupt); + processTypedMap(mInputGrid.transform(), functor); + if (functor.mOutputGrid) functor.mOutputGrid->setVectorType(VEC_COVARIANT); + return functor.mOutputGrid; + } + +protected: + struct Functor + { + Functor(const GridT& grid, const MaskGridType* mask, bool threaded, InterruptT* interrupt): + mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt), mMask(mask) {} + + template + void operator()(const MapT& map) + { + typedef math::MeanCurvature OpT; + gridop::GridOperator + op(mInputGrid, mMask, map); + mOutputGrid = op.process(mThreaded); // cache the result + } + + const bool mThreaded; + const GridT& mInputGrid; + typename GridT::Ptr mOutputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; + }; // Private Functor + + const GridT& mInputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; +}; // end of MeanCurvature class + + +//////////////////////////////////////// + + +template< + typename InGridT, + typename MaskGridType = typename gridop::ToMaskGrid::Type, + typename InterruptT = util::NullInterrupter> +class Magnitude +{ +public: + typedef InGridT InGridType; + typedef typename VectorToScalarConverter::Type OutGridType; + + Magnitude(const InGridType& grid, InterruptT* interrupt = NULL): + mInputGrid(grid), mInterrupt(interrupt), mMask(NULL) + { + } + + Magnitude(const InGridType& grid, const MaskGridType& mask, InterruptT* interrupt = NULL): + mInputGrid(grid), mInterrupt(interrupt), mMask(&mask) + { + } + + typename OutGridType::Ptr process(bool threaded = true) + { + Functor functor(mInputGrid, mMask, threaded, mInterrupt); + processTypedMap(mInputGrid.transform(), functor); + return functor.mOutputGrid; + } + +protected: + struct OpT + { + template + static typename OutGridType::ValueType + result(const MapT&, const AccT& acc, const Coord& xyz) { return acc.getValue(xyz).length();} + }; + struct Functor + { + Functor(const InGridT& grid, const MaskGridType* mask, + bool threaded, InterruptT* interrupt): + mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt), mMask(mask) {} + + template + void operator()(const MapT& map) + { + gridop::GridOperator + op(mInputGrid, mMask, map); + mOutputGrid = op.process(mThreaded); // cache the result + } + + const bool mThreaded; + const InGridType& mInputGrid; + typename OutGridType::Ptr mOutputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; + }; // Private Functor + + const InGridType& mInputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; +}; // end of Magnitude class + + +//////////////////////////////////////// + + +template< + typename GridT, + typename MaskGridType = typename gridop::ToMaskGrid::Type, + typename InterruptT = util::NullInterrupter> +class Normalize +{ +public: + typedef GridT InGridType; + typedef GridT OutGridType; + + Normalize(const GridT& grid, InterruptT* interrupt = NULL): + mInputGrid(grid), mInterrupt(interrupt), mMask(NULL) + { + } + + Normalize(const GridT& grid, const MaskGridType& mask, InterruptT* interrupt = NULL): + mInputGrid(grid), mInterrupt(interrupt), mMask(&mask) + { + } + + typename GridT::Ptr process(bool threaded = true) + { + Functor functor(mInputGrid, mMask, threaded, mInterrupt); + processTypedMap(mInputGrid.transform(), functor); + if (typename GridT::Ptr outGrid = functor.mOutputGrid) { + const VecType vecType = mInputGrid.getVectorType(); + if (vecType == VEC_COVARIANT) { + outGrid->setVectorType(VEC_COVARIANT_NORMALIZE); + } else { + outGrid->setVectorType(vecType); + } + } + return functor.mOutputGrid; + } + +protected: + struct OpT + { + template + 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, const MaskGridType* mask, bool threaded, InterruptT* interrupt): + mThreaded(threaded), mInputGrid(grid), mInterrupt(interrupt), mMask(mask) {} + + template + void operator()(const MapT& map) + { + gridop::GridOperator + op(mInputGrid, mMask,map); + mOutputGrid = op.process(mThreaded); // cache the result + } + + const bool mThreaded; + const GridT& mInputGrid; + typename GridT::Ptr mOutputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; + }; // Private Functor + + const GridT& mInputGrid; + InterruptT* mInterrupt; + const MaskGridType* mMask; +}; // end of Normalize class + + +//////////////////////////////////////// + + +template inline +typename ScalarToVectorConverter::Type::Ptr +cpt(const GridType& grid, bool threaded, InterruptT* interrupt) +{ + Cpt::Type, InterruptT> op(grid, interrupt); + return op.process(threaded); +} + +template inline +typename ScalarToVectorConverter::Type::Ptr +cpt(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt) +{ + Cpt op(grid, mask, interrupt); + return op.process(threaded); +} + +template inline +typename GridType::Ptr +curl(const GridType& grid, bool threaded, InterruptT* interrupt) +{ + Curl::Type, InterruptT> op(grid, interrupt); + return op.process(threaded); +} + +template inline +typename GridType::Ptr +curl(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt) +{ + Curl op(grid, mask, interrupt); + return op.process(threaded); +} + +template inline +typename VectorToScalarConverter::Type::Ptr +divergence(const GridType& grid, bool threaded, InterruptT* interrupt) +{ + Divergence::Type, InterruptT> + op(grid, interrupt); + return op.process(threaded); +} + +template inline +typename VectorToScalarConverter::Type::Ptr +divergence(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt) +{ + Divergence op(grid, mask, interrupt); + return op.process(threaded); +} + +template inline +typename ScalarToVectorConverter::Type::Ptr +gradient(const GridType& grid, bool threaded, InterruptT* interrupt) +{ + Gradient::Type, InterruptT> + op(grid, interrupt); + return op.process(threaded); +} + +template inline +typename ScalarToVectorConverter::Type::Ptr +gradient(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt) +{ + Gradient op(grid, mask, interrupt); + return op.process(threaded); +} + +template inline +typename GridType::Ptr +laplacian(const GridType& grid, bool threaded, InterruptT* interrupt) +{ + Laplacian::Type, InterruptT> + op(grid, interrupt); + return op.process(threaded); +} + +template inline +typename GridType::Ptr +laplacian(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt) +{ + Laplacian op(grid, mask, interrupt); + return op.process(threaded); +} + +template inline +typename GridType::Ptr +meanCurvature(const GridType& grid, bool threaded, InterruptT* interrupt) +{ + MeanCurvature::Type, InterruptT> + op(grid, interrupt); + return op.process(threaded); +} + +template inline +typename GridType::Ptr +meanCurvature(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt) +{ + MeanCurvature op(grid, mask, interrupt); + return op.process(threaded); +} + +template inline +typename VectorToScalarConverter::Type::Ptr +magnitude(const GridType& grid, bool threaded, InterruptT* interrupt) +{ + Magnitude::Type, InterruptT> + op(grid, interrupt); + return op.process(threaded); +} + +template inline +typename VectorToScalarConverter::Type::Ptr +magnitude(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt) +{ + Magnitude op(grid, mask, interrupt); + return op.process(threaded); +} + +template inline +typename GridType::Ptr +normalize(const GridType& grid, bool threaded, InterruptT* interrupt) +{ + Normalize::Type, InterruptT> + op(grid, interrupt); + return op.process(threaded); +} + +template inline +typename GridType::Ptr +normalize(const GridType& grid, const MaskT& mask, bool threaded, InterruptT* interrupt) +{ + Normalize op(grid, mask, interrupt); + return op.process(threaded); +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_GRID_OPERATORS_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/GridTransformer.h b/nuparu/include/openvdb/include/openvdb/tools/GridTransformer.h new file mode 100644 index 00000000..b23f94ab --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/GridTransformer.h @@ -0,0 +1,1039 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +/// @author Peter Cucka + +#ifndef OPENVDB_TOOLS_GRIDTRANSFORMER_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_GRIDTRANSFORMER_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include // for isApproxEqual() +#include +#include "ChangeBackground.h" +#include "Interpolation.h" +#include "LevelSetRebuild.h" // for doLevelSetRebuild() +#include "SignedFloodFill.h" // for signedFloodFill +#include "Prune.h" // for pruneLevelSet + +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(*src, *dest, interrupter); +/// @endcode +template +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(*src, *dest); +/// @endcode +template +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 +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 +class TileSampler: public PointSampler { +public: + TileSampler(const CoordBBox&, const typename TreeT::ValueType&, bool) {} +}; + +/// @brief For point sampling, tree traversal is less expensive than testing +/// bounding box membership. +template +class TileSampler: public StaggeredPointSampler { +public: + 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(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 Ptr; + typedef boost::function 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 void setInterrupter(InterrupterType&); + + template + void transformGrid(const Transformer&, + const GridT& inGrid, GridT& outGrid) const; + +protected: + template + void applyTransform(const Transformer&, const GridT& inGrid, GridT& outGrid) const; + + bool interrupt() const { return mInterrupt && mInterrupt(); } + +private: + template + static void transformBBox(const Transformer&, const CoordBBox& inBBox, + const InTreeT& inTree, OutTreeT& outTree, const InterruptFunc&, + const Sampler& = Sampler()); + + template + 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(inGrid, outGrid); +/// @endcode +/// or +/// @code +/// Vec3R pivot = ..., scale = ..., rotate = ..., translate = ...; +/// GridTransformer transformer(pivot, scale, rotate, translate); +/// transformer.transformGrid(inGrid, outGrid); +/// @endcode +class GridTransformer: public GridResampler +{ +public: + typedef boost::shared_ptr 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 + 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. +template +inline bool +decompose(const math::Mat4& m, math::Vec3& scale, + math::Vec3& rotate, math::Vec3& translate) +{ + if (!math::isAffine(m)) return false; + + // This is the translation in world space + translate = m.getTranslation(); + // Extract translation. + const math::Mat3 xform = m.getMat3(); + + const math::Vec3 unsignedScale( + (math::Vec3(1, 0, 0) * xform).length(), + (math::Vec3(0, 1, 0) * xform).length(), + (math::Vec3(0, 0, 1) * xform).length()); + + const bool hasUniformScale = unsignedScale.eq(math::Vec3(unsignedScale[0])); + + bool hasRotation = false; + bool validDecomposition = false; + + T minAngle = std::numeric_limits::max(); + + // If the transformation matrix contains a reflection, + // test different negative scales to find a decomposition + // that favors the optimal resampling algorithm. + for (size_t n = 0; n < 8; ++n) { + + const math::Vec3 signedScale( + n & 0x1 ? -unsignedScale.x() : unsignedScale.x(), + n & 0x2 ? -unsignedScale.y() : unsignedScale.y(), + n & 0x4 ? -unsignedScale.z() : unsignedScale.z()); + + // Extract scale and potentially reflection. + const math::Mat3 mat = xform * math::scale >(signedScale).inverse(); + if (mat.det() < T(0.0)) continue; // Skip if mat contains a reflection. + + const math::Vec3 tmpAngle = math::eulerAngles(mat, math::XYZ_ROTATION); + + const math::Mat3 rebuild = + math::rotation >(math::Vec3(1, 0, 0), tmpAngle.x()) * + math::rotation >(math::Vec3(0, 1, 0), tmpAngle.y()) * + math::rotation >(math::Vec3(0, 0, 1), tmpAngle.z()) * + math::scale >(signedScale); + + if (xform.eq(rebuild)) { + + const T maxAngle = std::max(std::abs(tmpAngle[0]), + std::max(std::abs(tmpAngle[1]), std::abs(tmpAngle[2]))); + + if (!(minAngle < maxAngle)) { // Update if less or equal. + + minAngle = maxAngle; + rotate = tmpAngle; + scale = signedScale; + + hasRotation = !rotate.eq(math::Vec3::zero()); + validDecomposition = true; + + if (hasUniformScale || !hasRotation) { + // Current decomposition is optimal. + break; + } + } + } + } + + if (!validDecomposition || (hasRotation && !hasUniformScale)) { + // The decomposition is invalid if the transformation matrix contains shear. + // 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 +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(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(xform, inGrid, outGrid); + } +} + + +template +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(), + /*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(inGrid, outGrid, interrupter); +} + + +template +inline void +resampleToMatch(const GridType& inGrid, GridType& outGrid) +{ + util::NullInterrupter interrupter; + resampleToMatch(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 +void +GridResampler::setInterrupter(InterrupterType& interrupter) +{ + mInterrupt = boost::bind(&InterrupterType::wasInterrupted, + /*this=*/&interrupter, /*percent=*/-1); +} + + +template +void +GridResampler::transformGrid(const Transformer& xform, + const GridT& inGrid, GridT& outGrid) const +{ + tools::changeBackground(outGrid.tree(), inGrid.background()); + applyTransform(xform, inGrid, outGrid); +} + + +template +void +GridTransformer::transformGrid(const GridT& inGrid, GridT& outGrid) const +{ + tools::changeBackground(outGrid.tree(), inGrid.background()); + + if (!Sampler::mipmap() || mMipLevels == Vec3i::zero()) { + // Skip the mipmapping step. + const MatrixTransform xform(mTransform); + applyTransform(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(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(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(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(xform, *tempGrid, outGrid); + } else { + outGrid.setTree(tempGrid->treePtr()); + } + } +} + + +//////////////////////////////////////// + + +template +class GridResampler::RangeProcessor +{ +public: + typedef typename TreeT::LeafCIter LeafIterT; + typedef typename TreeT::ValueAllCIter TileIterT; + typedef typename tree::IteratorRange LeafRange; + typedef typename tree::IteratorRange TileRange; + typedef typename tree::ValueAccessor InTreeAccessor; + typedef typename tree::ValueAccessor 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->origin(), i->origin() + 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(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(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 +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 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) { + tools::pruneLevelSet(outTree); + tools::signedFloodFill(outTree); + } +} + + +//////////////////////////////////////// + + +//static +template +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; + + // 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-2016 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/nuparu/include/openvdb/include/openvdb/tools/Interpolation.h b/nuparu/include/openvdb/include/openvdb/tools/Interpolation.h new file mode 100644 index 00000000..81decbad --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/Interpolation.h @@ -0,0 +1,1040 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +/// 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 +#include +#include // for OPENVDB_VERSION_NAME +#include // for round() +#include // for SmoothUnitStep +#include // for Transform +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Provises a unified interface for sampling, i.e. interpolation. +/// @details Order = 0: closest point +/// Order = 1: tri-linear +/// Order = 2: tri-quadratic +/// Staggered: Set to true for MAC grids +template +struct Sampler +{ + BOOST_STATIC_ASSERT(Order < 3); + static const char* name(); + static int radius(); + static bool mipmap(); + static bool consistent(); + static bool staggered(); + static size_t order(); + + /// @brief Sample @a inTree at the floating-point index coordinate @a inCoord + /// and store the result in @a result. + /// + /// @return @c true if the sampled value is active. + template + static bool sample(const TreeT& inTree, const Vec3R& inCoord, + typename TreeT::ValueType& result); + + /// @brief Sample @a inTree at the floating-point index coordinate @a inCoord. + /// + /// @return the reconstructed value + template + static typename TreeT::ValueType sample(const TreeT& inTree, const Vec3R& inCoord); +}; + +//////////////////////////////////////// Non-Staggered Samplers + +// 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. Consider using the GridSampler below instead. + +struct PointSampler +{ + static const char* name() { return "point"; } + static int radius() { return 0; } + static bool mipmap() { return false; } + static bool consistent() { return true; } + static bool staggered() { return false; } + static size_t order() { return 0; } + + /// @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 + static bool sample(const TreeT& inTree, const Vec3R& inCoord, + typename TreeT::ValueType& result); + + /// @brief Sample @a inTree at the nearest neighbor to @a inCoord + /// @return the reconstructed value + template + static typename TreeT::ValueType sample(const TreeT& inTree, const Vec3R& inCoord); +}; + + +struct BoxSampler +{ + static const char* name() { return "box"; } + static int radius() { return 1; } + static bool mipmap() { return true; } + static bool consistent() { return true; } + static bool staggered() { return false; } + static size_t order() { return 1; } + + /// @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 + 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 + static typename TreeT::ValueType sample(const TreeT& inTree, const Vec3R& inCoord); + + /// @brief Import all eight values from @a inTree to support + /// tri-linear interpolation. + template + static inline void getValues(ValueT (&data)[N][N][N], const TreeT& inTree, Coord ijk); + + /// @brief Import all eight values from @a inTree to support + /// tri-linear interpolation. + /// @return @c true if any of the eight values are active + template + static inline bool probeValues(ValueT (&data)[N][N][N], const TreeT& inTree, Coord ijk); + + /// @brief Find the minimum and maximum values of the eight cell + /// values in @ data. + template + static inline void extrema(ValueT (&data)[N][N][N], ValueT& vMin, ValueT& vMax); + + /// @return the tri-linear interpolation with the unit cell coordinates @a uvw + template + 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; } + static bool staggered() { return false; } + static size_t order() { return 2; } + + /// @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 + static bool sample(const TreeT& inTree, const Vec3R& inCoord, + typename TreeT::ValueType& result); + + /// @brief Triquadratically reconstruct @a inTree at to @a inCoord. + /// @return the reconstructed value + template + static typename TreeT::ValueType sample(const TreeT& inTree, const Vec3R& inCoord); + + template + static inline ValueT triquadraticInterpolation(ValueT (&data)[N][N][N], const Vec3R& uvw); +}; + + +//////////////////////////////////////// Staggered Samplers + + +// 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; } + static bool staggered() { return true; } + static size_t order() { return 0; } + + /// @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 + static bool sample(const TreeT& inTree, const Vec3R& inCoord, + typename TreeT::ValueType& result); + + /// @brief Sample @a inTree at the nearest neighbor to @a inCoord + /// @return the reconstructed value + template + static typename TreeT::ValueType sample(const TreeT& inTree, const Vec3R& inCoord); +}; + + +struct StaggeredBoxSampler +{ + static const char* name() { return "box"; } + static int radius() { return 1; } + static bool mipmap() { return true; } + static bool consistent() { return false; } + static bool staggered() { return true; } + static size_t order() { return 1; } + + /// @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 + 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 + static typename TreeT::ValueType sample(const TreeT& inTree, const Vec3R& inCoord); +}; + + +struct StaggeredQuadraticSampler +{ + static const char* name() { return "quadratic"; } + static int radius() { return 1; } + static bool mipmap() { return true; } + static bool consistent() { return false; } + static bool staggered() { return true; } + static size_t order() { return 2; } + + /// @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 + static bool sample(const TreeT& inTree, const Vec3R& inCoord, + typename TreeT::ValueType& result); + + /// @brief Triquadratically reconstruct @a inTree at to @a inCoord. + /// @return the reconstructed value + template + static typename TreeT::ValueType sample(const TreeT& inTree, const Vec3R& inCoord); +}; + + +//////////////////////////////////////// GridSampler + + +/// @brief Class that provides the interface for continuous sampling +/// of values in a tree. +/// +/// @details Since trees support only discrete voxel sampling, TreeSampler +/// must be used to sample arbitrary continuous points in (world or +/// index) space. +/// +/// @warning This implementation of the GridSampler stores a pointer +/// to a Tree for value access. While this is thread-safe it is +/// uncached and hence slow compared to using a +/// ValueAccessor. Consequently it is normally advisable to use the +/// template specialization below that employs a +/// ValueAccessor. However, care must be taken when dealing with +/// multi-threading (see warning below). +template +class GridSampler +{ +public: + typedef boost::shared_ptr Ptr; + typedef typename GridOrTreeType::ValueType ValueType; + typedef typename TreeAdapter::GridType GridType; + typedef typename TreeAdapter::TreeType TreeType; + typedef typename TreeAdapter::AccessorType AccessorType; + + /// @param grid a grid to be sampled + explicit GridSampler(const GridType& grid) + : mTree(&(grid.tree())), mTransform(&(grid.transform())) {} + + /// @param tree a tree to be sampled, or a ValueAccessor for the tree + /// @param transform is used when sampling world space locations. + GridSampler(const TreeType& tree, const math::Transform& transform) + : mTree(&tree), mTransform(&transform) {} + + const math::Transform& transform() const { return *mTransform; } + + /// @brief Sample a point in index space in the grid. + /// @param x Fractional x-coordinate of point in index-coordinates of grid + /// @param y Fractional y-coordinate of point in index-coordinates of grid + /// @param z Fractional z-coordinate of point in index-coordinates of grid + template + ValueType sampleVoxel(const RealType& x, const RealType& y, const RealType& z) const + { + return this->isSample(Vec3d(x,y,z)); + } + + /// @brief Sample value in integer index space + /// @param i Integer x-coordinate in index space + /// @param j Integer y-coordinate in index space + /// @param k Integer x-coordinate in index space + ValueType sampleVoxel(typename Coord::ValueType i, + typename Coord::ValueType j, + typename Coord::ValueType k) const + { + return this->isSample(Coord(i,j,k)); + } + + /// @brief Sample value in integer index space + /// @param ijk the location in index space + ValueType isSample(const Coord& ijk) const { return mTree->getValue(ijk); } + + /// @brief Sample in fractional index space + /// @param ispoint the location in index space + ValueType isSample(const Vec3d& ispoint) const + { + ValueType result = zeroVal(); + 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(); + SamplerType::sample(*mTree, mTransform->worldToIndex(wspoint), result); + return result; + } + +private: + const TreeType* mTree; + const math::Transform* mTransform; +}; // class GridSampler + + +/// @brief Specialization of GridSampler for construction from a ValueAccessor type +/// +/// @note This version should normally be favored over the one above +/// that takes a Grid or Tree. The reason is this version uses a +/// ValueAccessor that performs fast (cached) access where the +/// tree-based flavor performs slower (uncached) access. +/// +/// @warning Since this version stores a pointer to an (externally +/// allocated) value accessor it is not threadsafe. Hence each thread +/// should have its own instance of a GridSampler constructed from a +/// local ValueAccessor. Alternatively the Grid/Tree-based GridSampler +/// is threadsafe, but also slower. +template +class GridSampler, SamplerType> +{ +public: + typedef boost::shared_ptr Ptr; + typedef typename TreeT::ValueType ValueType; + typedef TreeT TreeType; + typedef Grid GridType; + typedef typename tree::ValueAccessor AccessorType; + + /// @param acc a ValueAccessor to be sampled + /// @param transform is used when sampling world space locations. + GridSampler(const AccessorType& acc, + const math::Transform& transform) + : mAccessor(&acc), mTransform(&transform) {} + + const math::Transform& transform() const { return *mTransform; } + + /// @brief Sample a point in index space in the grid. + /// @param x Fractional x-coordinate of point in index-coordinates of grid + /// @param y Fractional y-coordinate of point in index-coordinates of grid + /// @param z Fractional z-coordinate of point in index-coordinates of grid + template + ValueType sampleVoxel(const RealType& x, const RealType& y, const RealType& z) const + { + return this->isSample(Vec3d(x,y,z)); + } + + /// @brief Sample value in integer index space + /// @param i Integer x-coordinate in index space + /// @param j Integer y-coordinate in index space + /// @param k Integer x-coordinate in index space + ValueType sampleVoxel(typename Coord::ValueType i, + typename Coord::ValueType j, + typename Coord::ValueType k) const + { + return this->isSample(Coord(i,j,k)); + } + + /// @brief Sample value in integer index space + /// @param ijk the location in index space + ValueType isSample(const Coord& ijk) const { return mAccessor->getValue(ijk); } + + /// @brief Sample in fractional index space + /// @param ispoint the location in index space + ValueType isSample(const Vec3d& ispoint) const + { + ValueType result = zeroVal(); + SamplerType::sample(*mAccessor, 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(); + SamplerType::sample(*mAccessor, mTransform->worldToIndex(wspoint), result); + return result; + } + +private: + const AccessorType* mAccessor;//not thread-safe! + const math::Transform* mTransform; +};//Specialization of GridSampler + + +//////////////////////////////////////// DualGridSampler + + +/// @brief This is a simple convenience class that allows for sampling +/// from a source grid into the index space of a target grid. At +/// construction the source and target grids are checked for alignment +/// which potentially renders interpolation unnecessary. Else +/// interpolation is performed according to the templated Sampler +/// type. +/// +/// @warning For performance reasons the check for alignment of the +/// two grids is only performed at construction time! +template +class DualGridSampler +{ +public: + typedef typename GridOrTreeT::ValueType ValueType; + typedef typename TreeAdapter::GridType GridType; + typedef typename TreeAdapter::TreeType TreeType; + typedef typename TreeAdapter::AccessorType AccessorType; + + /// @brief Grid and transform constructor. + /// @param sourceGrid Source grid. + /// @param targetXform Transform of the target grid. + DualGridSampler(const GridType& sourceGrid, + const math::Transform& targetXform) + : mSourceTree(&(sourceGrid.tree())) + , mSourceXform(&(sourceGrid.transform())) + , mTargetXform(&targetXform) + , mAligned(targetXform == *mSourceXform) + { + } + /// @brief Tree and transform constructor. + /// @param sourceTree Source tree. + /// @param sourceXform Transform of the source grid. + /// @param targetXform Transform of the target grid. + DualGridSampler(const TreeType& sourceTree, + const math::Transform& sourceXform, + const math::Transform& targetXform) + : mSourceTree(&sourceTree) + , mSourceXform(&sourceXform) + , mTargetXform(&targetXform) + , mAligned(targetXform == sourceXform) + { + } + /// @brief Return the value of the source grid at the index + /// coordinates, ijk, relative to the target grid (or its tranform). + inline ValueType operator()(const Coord& ijk) const + { + if (mAligned) return mSourceTree->getValue(ijk); + const Vec3R world = mTargetXform->indexToWorld(ijk); + return SamplerT::sample(*mSourceTree, mSourceXform->worldToIndex(world)); + } + /// @brief Return true if the two grids are aligned. + inline bool isAligned() const { return mAligned; } +private: + const TreeType* mSourceTree; + const math::Transform* mSourceXform; + const math::Transform* mTargetXform; + const bool mAligned; +};// DualGridSampler + +/// @brief Specialization of DualGridSampler for construction from a ValueAccessor type. +template +class DualGridSampler, SamplerT> +{ + public: + typedef typename TreeT::ValueType ValueType; + typedef TreeT TreeType; + typedef Grid GridType; + typedef typename tree::ValueAccessor AccessorType; + + /// @brief ValueAccessor and transform constructor. + /// @param sourceAccessor ValueAccessor into the source grid. + /// @param sourceXform Transform for the source grid. + /// @param targetXform Transform for the target grid. + DualGridSampler(const AccessorType& sourceAccessor, + const math::Transform& sourceXform, + const math::Transform& targetXform) + : mSourceAcc(&sourceAccessor) + , mSourceXform(&sourceXform) + , mTargetXform(&targetXform) + , mAligned(targetXform == sourceXform) + { + } + /// @brief Return the value of the source grid at the index + /// coordinates, ijk, relative to the target grid. + inline ValueType operator()(const Coord& ijk) const + { + if (mAligned) return mSourceAcc->getValue(ijk); + const Vec3R world = mTargetXform->indexToWorld(ijk); + return SamplerT::sample(*mSourceAcc, mSourceXform->worldToIndex(world)); + } + /// @brief Return true if the two grids are aligned. + inline bool isAligned() const { return mAligned; } +private: + const AccessorType* mSourceAcc; + const math::Transform* mSourceXform; + const math::Transform* mTargetXform; + const bool mAligned; +};//Specialization of DualGridSampler + +//////////////////////////////////////// AlphaMask + + +// Class to derive the normalized alpha mask +template +class AlphaMask +{ +public: + BOOST_STATIC_ASSERT(boost::is_floating_point::value); + typedef GridT GridType; + typedef MaskT MaskType; + typedef SamplerT SamlerType; + typedef FloatT FloatType; + + AlphaMask(const GridT& grid, const MaskT& mask, FloatT min, FloatT max, bool invert) + : mAcc(mask.tree()) + , mSampler(mAcc, mask.transform() , grid.transform()) + , mMin(min) + , mInvNorm(1/(max-min)) + , mInvert(invert) + { + assert(min < max); + } + + inline bool operator()(const Coord& xyz, FloatT& a, FloatT& b) const + { + a = math::SmoothUnitStep( (mSampler(xyz) - mMin) * mInvNorm );//smooth mapping to 0->1 + b = 1 - a; + if (mInvert) std::swap(a,b); + return a>0; + } + +protected: + typedef typename MaskType::ConstAccessor AccT; + AccT mAcc; + tools::DualGridSampler mSampler; + const FloatT mMin, mInvNorm; + const bool mInvert; +};// AlphaMask + +//////////////////////////////////////// + +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 + + +//////////////////////////////////////// PointSampler + + +template +inline bool +PointSampler::sample(const TreeT& inTree, const Vec3R& inCoord, + typename TreeT::ValueType& result) +{ + return inTree.probeValue(Coord(local_util::roundVec3(inCoord)), result); +} + +template +inline typename TreeT::ValueType +PointSampler::sample(const TreeT& inTree, const Vec3R& inCoord) +{ + return inTree.getValue(Coord(local_util::roundVec3(inCoord))); +} + + +//////////////////////////////////////// BoxSampler + +template +inline void +BoxSampler::getValues(ValueT (&data)[N][N][N], const TreeT& inTree, Coord ijk) +{ + 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] -= 1; + data[0][1][0] = inTree.getValue(ijk); // i, j+1, k + + ijk[0] += 1; + ijk[1] -= 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] -= 1; + data[1][1][0] = inTree.getValue(ijk); // i+1, j+1, k +} + +template +inline bool +BoxSampler::probeValues(ValueT (&data)[N][N][N], const TreeT& inTree, Coord ijk) +{ + bool hasActiveValues = false; + 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] -= 1; + hasActiveValues |= inTree.probeValue(ijk, data[0][1][0]); // i, j+1, k + + ijk[0] += 1; + ijk[1] -= 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] -= 1; + hasActiveValues |= inTree.probeValue(ijk, data[1][1][0]); // i+1, j+1, k + + return hasActiveValues; +} + +template +inline void +BoxSampler::extrema(ValueT (&data)[N][N][N], ValueT& vMin, ValueT &vMax) +{ + vMin = vMax = data[0][0][0]; + vMin = math::Min(vMin, data[0][0][1]); + vMax = math::Max(vMax, data[0][0][1]); + vMin = math::Min(vMin, data[0][1][0]); + vMax = math::Max(vMax, data[0][1][0]); + vMin = math::Min(vMin, data[0][1][1]); + vMax = math::Max(vMax, data[0][1][1]); + vMin = math::Min(vMin, data[1][0][0]); + vMax = math::Max(vMax, data[1][0][0]); + vMin = math::Min(vMin, data[1][0][1]); + vMax = math::Max(vMax, data[1][0][1]); + vMin = math::Min(vMin, data[1][1][0]); + vMax = math::Max(vMax, data[1][1][0]); + vMin = math::Min(vMin, data[1][1][1]); + vMax = math::Max(vMax, data[1][1][1]); +} + + +template +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 +inline bool +BoxSampler::sample(const TreeT& inTree, const Vec3R& inCoord, + typename TreeT::ValueType& result) +{ + typedef typename TreeT::ValueType ValueT; + + const Vec3i inIdx = local_util::floorVec3(inCoord); + const Vec3R uvw = inCoord - inIdx; + + // Retrieve the values of the eight voxels surrounding the + // fractional source coordinates. + ValueT data[2][2][2]; + + const bool hasActiveValues = BoxSampler::probeValues(data, inTree, Coord(inIdx)); + + result = BoxSampler::trilinearInterpolation(data, uvw); + + return hasActiveValues; +} + + +template +inline typename TreeT::ValueType +BoxSampler::sample(const TreeT& inTree, const Vec3R& inCoord) +{ + typedef typename TreeT::ValueType ValueT; + + const Vec3i inIdx = local_util::floorVec3(inCoord); + const Vec3R uvw = inCoord - inIdx; + + // Retrieve the values of the eight voxels surrounding the + // fractional source coordinates. + ValueT data[2][2][2]; + + BoxSampler::getValues(data, inTree, Coord(inIdx)); + + return BoxSampler::trilinearInterpolation(data, uvw); +} + + +//////////////////////////////////////// QuadraticSampler + +template +inline ValueT +QuadraticSampler::triquadraticInterpolation(ValueT (&data)[N][N][N], const Vec3R& uvw) +{ + /// @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 = &data[dx][dy][0]; + const ValueT + az = static_cast(0.5 * (vz[0] + vz[2]) - vz[1]), + bz = static_cast(0.5 * (vz[2] - vz[0])), + cz = static_cast(vz[1]); + vy[dy] = static_cast(uvw.z() * (uvw.z() * az + bz) + cz); + }//loop over y + // 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(0.5 * (vy[0] + vy[2]) - vy[1]), + by = static_cast(0.5 * (vy[2] - vy[0])), + cy = static_cast(vy[1]); + vx[dx] = static_cast(uvw.y() * (uvw.y() * ay + by) + cy); + }//loop over x + // 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(0.5 * (vx[0] + vx[2]) - vx[1]), + bx = static_cast(0.5 * (vx[2] - vx[0])), + cx = static_cast(vx[1]); + return static_cast(uvw.x() * (uvw.x() * ax + bx) + cx); +} + +template +inline bool +QuadraticSampler::sample(const TreeT& inTree, const Vec3R& inCoord, + typename TreeT::ValueType& result) +{ + typedef typename TreeT::ValueType ValueT; + + const Vec3i inIdx = local_util::floorVec3(inCoord), inLoIdx = inIdx - Vec3i(1, 1, 1); + const Vec3R uvw = inCoord - inIdx; + + // Retrieve the values of the 27 voxels surrounding the + // fractional source coordinates. + bool active = false; + ValueT data[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), data[dx][dy][dz])) active = true; + } + } + } + + result = QuadraticSampler::triquadraticInterpolation(data, uvw); + + return active; +} + +template +inline typename TreeT::ValueType +QuadraticSampler::sample(const TreeT& inTree, const Vec3R& inCoord) +{ + typedef typename TreeT::ValueType ValueT; + + const Vec3i inIdx = local_util::floorVec3(inCoord), inLoIdx = inIdx - Vec3i(1, 1, 1); + const Vec3R uvw = inCoord - inIdx; + + // Retrieve the values of the 27 voxels surrounding the + // fractional source coordinates. + ValueT data[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) { + data[dx][dy][dz] = inTree.getValue(Coord(ix, iy, iz)); + } + } + } + + return QuadraticSampler::triquadraticInterpolation(data, uvw); +} + + +//////////////////////////////////////// StaggeredPointSampler + + +template +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(inTree, inCoord + Vec3R(0.5, 0, 0), tempX) || active; + active = PointSampler::sample(inTree, inCoord + Vec3R(0, 0.5, 0), tempY) || active; + active = PointSampler::sample(inTree, inCoord + Vec3R(0, 0, 0.5), tempZ) || active; + + result.x() = tempX.x(); + result.y() = tempY.y(); + result.z() = tempZ.z(); + + return active; +} + +template +inline typename TreeT::ValueType +StaggeredPointSampler::sample(const TreeT& inTree, const Vec3R& inCoord) +{ + typedef typename TreeT::ValueType ValueT; + + const ValueT tempX = PointSampler::sample(inTree, inCoord + Vec3R(0.5, 0.0, 0.0)); + const ValueT tempY = PointSampler::sample(inTree, inCoord + Vec3R(0.0, 0.5, 0.0)); + const ValueT tempZ = PointSampler::sample(inTree, inCoord + Vec3R(0.0, 0.0, 0.5)); + + return ValueT(tempX.x(), tempY.y(), tempZ.z()); +} + + +//////////////////////////////////////// StaggeredBoxSampler + + +template +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(); + bool active = false; + + active = BoxSampler::sample(inTree, inCoord + Vec3R(0.5, 0, 0), tempX) || active; + active = BoxSampler::sample(inTree, inCoord + Vec3R(0, 0.5, 0), tempY) || active; + active = BoxSampler::sample(inTree, inCoord + Vec3R(0, 0, 0.5), tempZ) || active; + + result.x() = tempX.x(); + result.y() = tempY.y(); + result.z() = tempZ.z(); + + return active; +} + +template +inline typename TreeT::ValueType +StaggeredBoxSampler::sample(const TreeT& inTree, const Vec3R& inCoord) +{ + typedef typename TreeT::ValueType ValueT; + + const ValueT tempX = BoxSampler::sample(inTree, inCoord + Vec3R(0.5, 0.0, 0.0)); + const ValueT tempY = BoxSampler::sample(inTree, inCoord + Vec3R(0.0, 0.5, 0.0)); + const ValueT tempZ = BoxSampler::sample(inTree, inCoord + Vec3R(0.0, 0.0, 0.5)); + + return ValueT(tempX.x(), tempY.y(), tempZ.z()); +} + + +//////////////////////////////////////// StaggeredQuadraticSampler + + +template +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(inTree, inCoord + Vec3R(0.5, 0, 0), tempX) || active; + active = QuadraticSampler::sample(inTree, inCoord + Vec3R(0, 0.5, 0), tempY) || active; + active = QuadraticSampler::sample(inTree, inCoord + Vec3R(0, 0, 0.5), tempZ) || active; + + result.x() = tempX.x(); + result.y() = tempY.y(); + result.z() = tempZ.z(); + + return active; +} + +template +inline typename TreeT::ValueType +StaggeredQuadraticSampler::sample(const TreeT& inTree, const Vec3R& inCoord) +{ + typedef typename TreeT::ValueType ValueT; + + const ValueT tempX = QuadraticSampler::sample(inTree, inCoord + Vec3R(0.5, 0.0, 0.0)); + const ValueT tempY = QuadraticSampler::sample(inTree, inCoord + Vec3R(0.0, 0.5, 0.0)); + const ValueT tempZ = QuadraticSampler::sample(inTree, inCoord + Vec3R(0.0, 0.0, 0.5)); + + return ValueT(tempX.x(), tempY.y(), tempZ.z()); +} + +//////////////////////////////////////// Sampler + +template <> +struct Sampler<0, false> : public PointSampler {}; + +template <> +struct Sampler<1, false> : public BoxSampler {}; + +template <> +struct Sampler<2, false> : public QuadraticSampler {}; + +template <> +struct Sampler<0, true> : public StaggeredPointSampler {}; + +template <> +struct Sampler<1, true> : public StaggeredBoxSampler {}; + +template <> +struct Sampler<2, true> : public StaggeredQuadraticSampler {}; + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_INTERPOLATION_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/LevelSetAdvect.h b/nuparu/include/openvdb/include/openvdb/tools/LevelSetAdvect.h new file mode 100644 index 00000000..2139315a --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/LevelSetAdvect.h @@ -0,0 +1,569 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include +#include +#include "LevelSetTracker.h" +#include "VelocityFields.h" // for EnrightField +#include +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @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, ValueType 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. Finally we will assume that the +/// functor method is NOT threadsafe (typically uses a ValueAccessor) +/// and that its lightweight enough that we can copy it per thread. +/// +/// 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 InterruptT = util::NullInterrupter> +class LevelSetAdvection +{ +public: + typedef GridT GridType; + typedef LevelSetTracker TrackerT; + typedef typename TrackerT::LeafRange LeafRange; + typedef typename TrackerT::LeafType LeafType; + typedef typename TrackerT::BufferType BufferType; + typedef typename TrackerT::ValueType ValueType; + 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 grain size of 0 or less disables multi-threading! + void setGrainSize(int grainsize) { mTracker.setGrainSize(grainsize); } + + /// Advect the level set from its current time, time0, to its + /// 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(ValueType time0, ValueType time1); + +private: + + // disallow copy construction and copy by assinment! + LevelSetAdvection(const LevelSetAdvection&);// not implemented + LevelSetAdvection& operator=(const LevelSetAdvection&);// not implemented + + // This templated private struct implements all the level set magic. + template + struct Advect + { + /// Main constructor + Advect(LevelSetAdvection& parent); + /// Shallow copy constructor called by tbb::parallel_for() threads + Advect(const Advect& other); + /// Destructor + virtual ~Advect() { if (mIsMaster) this->clearField(); } + /// Advect the level set from its current time, time0, to its final time, time1. + /// @return number of CFL iterations + size_t advect(ValueType time0, ValueType time1); + /// Used internally by tbb::parallel_for() + void operator()(const LeafRange& r) const + { + if (mTask) mTask(const_cast(this), r); + else OPENVDB_THROW(ValueError, "task is undefined - don\'t call this method directly"); + } + /// method calling tbb + void cook(const char* msg, size_t swapBuffer = 0); + /// Sample field and return the CFL time step + typename GridT::ValueType sampleField(ValueType time0, ValueType time1); + template void sample(const LeafRange& r, ValueType t0, ValueType t1); + inline void sampleXformed(const LeafRange& r, ValueType t0, ValueType t1) + { + this->sample(r, t0, t1); + } + inline void sampleAligned(const LeafRange& r, ValueType t0, ValueType t1) + { + this->sample(r, t0, t1); + } + void clearField(); + // Convex combination of Phi and a forward Euler advection steps: + // Phi(result) = alpha * Phi(phi) + (1-alpha) * (Phi(0) - dt * Speed(speed)*|Grad[Phi(0)]|); + template + void euler(const LeafRange&, ValueType, Index, Index); + inline void euler01(const LeafRange& r, ValueType t) {this->euler<0,1>(r, t, 0, 1);} + inline void euler12(const LeafRange& r, ValueType t) {this->euler<1,2>(r, t, 1, 1);} + inline void euler34(const LeafRange& r, ValueType t) {this->euler<3,4>(r, t, 1, 2);} + inline void euler13(const LeafRange& r, ValueType t) {this->euler<1,3>(r, t, 1, 2);} + + LevelSetAdvection& mParent; + VectorType* mVelocity; + size_t* mOffsets; + const MapT* mMap; + typename boost::function mTask; + const bool mIsMaster; + }; // end of private Advect struct + + template + size_t advect1(ValueType time0, ValueType time1); + + template + size_t advect2(ValueType time0, ValueType time1); + + template + size_t advect3(ValueType time0, ValueType 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; + +};//end of LevelSetAdvection + +template +inline size_t +LevelSetAdvection::advect(ValueType time0, ValueType time1) +{ + switch (mSpatialScheme) { + case math::FIRST_BIAS: + return this->advect1(time0, time1); + case math::SECOND_BIAS: + return this->advect1(time0, time1); + case math::THIRD_BIAS: + return this->advect1(time0, time1); + case math::WENO5_BIAS: + return this->advect1(time0, time1); + case math::HJWENO5_BIAS: + return this->advect1(time0, time1); + default: + OPENVDB_THROW(ValueError, "Spatial difference scheme not supported!"); + } + return 0; +} + +template +template +inline size_t +LevelSetAdvection::advect1(ValueType time0, ValueType time1) +{ + switch (mTemporalScheme) { + case math::TVD_RK1: + return this->advect2(time0, time1); + case math::TVD_RK2: + return this->advect2(time0, time1); + case math::TVD_RK3: + return this->advect2(time0, time1); + default: + OPENVDB_THROW(ValueError, "Temporal integration scheme not supported!"); + } + return 0; +} + +template +template +inline size_t +LevelSetAdvection::advect2(ValueType time0, ValueType time1) +{ + const math::Transform& trans = mTracker.grid().transform(); + if (trans.mapType() == math::UniformScaleMap::mapType()) { + return this->advect3(time0, time1); + } else if (trans.mapType() == math::UniformScaleTranslateMap::mapType()) { + return this->advect3(time0, time1); + } else if (trans.mapType() == math::UnitaryMap::mapType()) { + return this->advect3(time0, time1); + } else if (trans.mapType() == math::TranslationMap::mapType()) { + return this->advect3(time0, time1); + } else { + OPENVDB_THROW(ValueError, "MapType not supported!"); + } + return 0; +} + +template +template +inline size_t +LevelSetAdvection::advect3(ValueType time0, ValueType time1) +{ + Advect tmp(*this); + return tmp.advect(time0, time1); +} + + +/////////////////////////////////////////////////////////////////////// + + +template +template +inline +LevelSetAdvection:: +Advect:: +Advect(LevelSetAdvection& parent) + : mParent(parent) + , mVelocity(NULL) + , mOffsets(NULL) + , mMap(parent.mTracker.grid().transform().template constMap().get()) + , mTask(0) + , mIsMaster(true) +{ +} + +template +template +inline +LevelSetAdvection:: +Advect:: +Advect(const Advect& other) + : mParent(other.mParent) + , mVelocity(other.mVelocity) + , mOffsets(other.mOffsets) + , mMap(other.mMap) + , mTask(other.mTask) + , mIsMaster(false) +{ +} + +template +template +inline size_t +LevelSetAdvection:: +Advect:: +advect(ValueType time0, ValueType time1) +{ + //util::CpuTimer timer; + size_t countCFL = 0; + if ( math::isZero(time0 - time1) ) return countCFL; + const bool isForward = time0 < time1; + while ((isForward ? time0time1) && mParent.mTracker.checkInterrupter()) { + /// Make sure we have enough temporal auxiliary buffers + //timer.start( "\nallocate buffers" ); + mParent.mTracker.leafs().rebuildAuxBuffers(TemporalScheme == math::TVD_RK3 ? 2 : 1); + //timer.stop(); + + const ValueType dt = this->sampleField(time0, time1); + if ( math::isZero(dt) ) break;//V is essentially zero so terminate + + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN //switch is resolved at compile-time + switch(TemporalScheme) { + 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(&Advect::euler01, _1, _2, dt); + + // Cook and swap buffer 0 and 1 such that Phi_t1(0) and Phi_t0(1) + this->cook("Advecting level set using TVD_RK1", 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(&Advect::euler01, _1, _2, dt); + + // Cook and swap buffer 0 and 1 such that Phi_t1(0) and Phi_t0(1) + this->cook("Advecting level set using TVD_RK1 (step 1 of 2)", 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(&Advect::euler12, _1, _2, dt); + + // Cook and swap buffer 0 and 1 such that Phi_t2(0) and Phi_t1(1) + this->cook("Advecting level set using TVD_RK1 (step 2 of 2)", 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(&Advect::euler01, _1, _2, dt); + + // Cook and swap buffer 0 and 1 such that Phi_t1(0) and Phi_t0(1) + this->cook("Advecting level set using TVD_RK3 (step 1 of 3)", 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(&Advect::euler34, _1, _2, dt); + + // Cook and swap buffer 0 and 2 such that Phi_t2(0) and Phi_t1(2) + this->cook("Advecting level set using TVD_RK3 (step 2 of 3)", 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(&Advect::euler13, _1, _2, dt); + + // Cook and swap buffer 0 and 2 such that Phi_t3(0) and Phi_t2(2) + this->cook("Advecting level set using TVD_RK3 (step 3 of 3)", 2); + break; + default: + OPENVDB_THROW(ValueError, "Temporal integration scheme not supported!"); + }//end of compile-time resolved switch + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + + time0 += isForward ? dt : -dt; + ++countCFL; + mParent.mTracker.leafs().removeAuxBuffers(); + this->clearField(); + /// Track the narrow band + mParent.mTracker.track(); + }//end wile-loop over time + return countCFL;//number of CLF propagation steps +} + +template +template +inline typename GridT::ValueType +LevelSetAdvection:: +Advect:: +sampleField(ValueType time0, ValueType time1) +{ + const int grainSize = mParent.mTracker.getGrainSize(); + const size_t leafCount = mParent.mTracker.leafs().leafCount(); + if (leafCount==0) return ValueType(0.0); + + // Compute the pre-fix sum of offsets to active voxels + size_t size=0, voxelCount=mParent.mTracker.leafs().getPreFixSum(mOffsets, size, grainSize); + + // Sample the velocity field + if (mParent.mField.transform() == mParent.mTracker.grid().transform()) { + mTask = boost::bind(&Advect::sampleAligned, _1, _2, time0, time1); + } else { + mTask = boost::bind(&Advect::sampleXformed, _1, _2, time0, time1); + } + assert(voxelCount != mParent.mTracker.grid().activeVoxelCount()); + mVelocity = new VectorType[ voxelCount ]; + this->cook("Sampling advection field"); + + // Find the extrema of the magnitude of the velocities + ValueType maxAbsV = 0; + VectorType* v = mVelocity; + for (size_t i=0; ilengthSqr())); + + // Compute the CFL number + if (math::isApproxZero(maxAbsV, math::Delta::value())) return ValueType(0); +#ifndef _MSC_VER // Visual C++ doesn't guarantee thread-safe initialization of local statics + static +#endif + const ValueType CFL = (TemporalScheme == math::TVD_RK1 ? ValueType(0.3) : + TemporalScheme == math::TVD_RK2 ? ValueType(0.9) : + ValueType(1.0))/math::Sqrt(ValueType(3.0)); + const ValueType dt = math::Abs(time1 - time0), dx = mParent.mTracker.voxelSize(); + return math::Min(dt, ValueType(CFL*dx/math::Sqrt(maxAbsV))); +} + +template +template +template +inline void +LevelSetAdvection:: +Advect:: +sample(const LeafRange& range, ValueType time0, ValueType time1) +{ + const bool isForward = time0 < time1; + typedef typename LeafType::ValueOnCIter VoxelIterT; + const MapT& map = *mMap; + const FieldT field( mParent.mField ); + mParent.mTracker.checkInterrupter(); + for (typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + VectorType* vel = mVelocity + mOffsets[ leafIter.pos() ]; + for (VoxelIterT iter = leafIter->cbeginValueOn(); iter; ++iter, ++vel) { + const VectorType v = Aligned ? field(iter.getCoord(), time0) ://resolved at compile time + field(map.applyMap(iter.getCoord().asVec3d()), time0); + *vel = isForward ? v : -v; + } + } +} + +template +template +inline void +LevelSetAdvection:: +Advect:: +clearField() +{ + delete [] mOffsets; + delete [] mVelocity; + mOffsets = NULL; + mVelocity = NULL; +} + +template +template +inline void +LevelSetAdvection:: +Advect:: +cook(const char* msg, size_t swapBuffer) +{ + mParent.mTracker.startInterrupter( msg ); + + const int grainSize = mParent.mTracker.getGrainSize(); + const LeafRange range = mParent.mTracker.leafs().leafRange(grainSize); + + grainSize == 0 ? (*this)(range) : tbb::parallel_for(range, *this); + + mParent.mTracker.leafs().swapLeafBuffer(swapBuffer, grainSize == 0); + + mParent.mTracker.endInterrupter(); +} + +// Convex combination of Phi and a forward Euler advection steps: +// Phi(result) = alpha * Phi(phi) + (1-alpha) * (Phi(0) - dt * V.Grad(0)); +template +template +template +inline void +LevelSetAdvection:: +Advect:: +euler(const LeafRange& range, ValueType dt, Index phiBuffer, Index resultBuffer) +{ + typedef math::BIAS_SCHEME SchemeT; + typedef typename SchemeT::template ISStencil::StencilType StencilT; + typedef typename LeafType::ValueOnCIter VoxelIterT; + typedef math::GradientBiased GradT; + + static const ValueType Alpha = ValueType(Nominator)/ValueType(Denominator); + static const ValueType Beta = ValueType(1) - Alpha; + + mParent.mTracker.checkInterrupter(); + const MapT& map = *mMap; + StencilT stencil(mParent.mTracker.grid()); + for (typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + const VectorType* vel = mVelocity + mOffsets[ leafIter.pos() ]; + const ValueType* phi = leafIter.buffer(phiBuffer).data(); + ValueType* result = leafIter.buffer(resultBuffer).data(); + for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter, ++vel) { + const Index i = voxelIter.pos(); + stencil.moveTo(voxelIter); + const ValueType a = stencil.getValue() - dt * vel->dot(GradT::result(map, stencil, *vel)); + result[i] = Nominator ? Alpha * phi[i] + Beta * a : a; + }//loop over active voxels in the leaf of the mask + }//loop over leafs of the level set +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_LEVEL_SET_ADVECT_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/LevelSetFilter.h b/nuparu/include/openvdb/include/openvdb/tools/LevelSetFilter.h new file mode 100644 index 00000000..4e9a864b --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/LevelSetFilter.h @@ -0,0 +1,546 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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). +/// All these operations can optionally be masked with another grid that +/// acts as an alpha-mask. + +#ifndef OPENVDB_TOOLS_LEVELSETFILTER_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_LEVELSETFILTER_HAS_BEEN_INCLUDED + +#include +#include +#include "LevelSetTracker.h" +#include "Interpolation.h" + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Filtering (e.g. diffusion) of narrow-band level sets. An +/// optional scalar field can be used to produce a (smooth) alpha mask +/// for the filtering. +/// +/// @note This class performs proper interface tracking which allows +/// for unrestricted surface deformations +template::Type, + typename InterruptT = util::NullInterrupter> +class LevelSetFilter : public LevelSetTracker +{ +public: + typedef LevelSetTracker BaseType; + typedef GridT GridType; + typedef MaskT MaskType; + typedef typename GridType::TreeType TreeType; + typedef typename TreeType::ValueType ValueType; + typedef typename MaskType::ValueType AlphaType; + BOOST_STATIC_ASSERT(boost::is_floating_point::value); + + /// @brief Main constructor from a grid + /// @param grid The level set to be filtered. + /// @param interrupt Optional interrupter. + LevelSetFilter(GridType& grid, InterruptT* interrupt = NULL) + : BaseType(grid, interrupt) + , mMinMask(0) + , mMaxMask(1) + , mInvertMask(false) + { + } + /// @brief Default destructor + virtual ~LevelSetFilter() {} + + /// @brief Return the minimum value of the mask to be used for the + /// derivation of a smooth alpha value. + AlphaType minMask() const { return mMinMask; } + /// @brief Return the maximum value of the mask to be used for the + /// derivation of a smooth alpha value. + AlphaType maxMask() const { return mMaxMask; } + /// @brief Define the range for the (optional) scalar mask. + /// @param min Minimum value of the range. + /// @param max Maximum value of the range. + /// @details Mask values outside the range maps to alpha values of + /// respectfully zero and one, and values inside the range maps + /// smoothly to 0->1 (unless of course the mask is inverted). + /// @throw ValueError if @a min is not smaller than @a max. + void setMaskRange(AlphaType min, AlphaType max) + { + if (!(min < max)) OPENVDB_THROW(ValueError, "Invalid mask range (expects min < max)"); + mMinMask = min; + mMaxMask = max; + } + + /// @brief Return true if the mask is inverted, i.e. min->max in the + /// original mask maps to 1->0 in the inverted alpha mask. + bool isMaskInverted() const { return mInvertMask; } + /// @brief Invert the optional mask, i.e. min->max in the original + /// mask maps to 1->0 in the inverted alpha mask. + void invertMask(bool invert=true) { mInvertMask = invert; } + + /// @brief One iteration of mean-curvature flow of the level set. + /// @param mask Optional alpha mask. + void meanCurvature(const MaskType* mask = NULL) + { + Filter f(this, mask); f.meanCurvature(); + } + + /// @brief One iteration of Laplacian flow of the level set. + /// @param mask Optional alpha mask. + void laplacian(const MaskType* mask = NULL) + { + Filter f(this, mask); f.laplacian(); + } + + /// @brief One iteration of a fast separable Gaussian filter. + /// @param width Width of the Gaussian kernel in voxel units. + /// @param mask Optional alpha mask. + /// + /// @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, const MaskType* mask = NULL) + { + Filter f(this, mask); f.gaussian(width); + } + + /// @brief Offset the level set by the specified (world) distance. + /// @param offset Value of the offset. + /// @param mask Optional alpha mask. + void offset(ValueType offset, const MaskType* mask = NULL) + { + Filter f(this, mask); f.offset(offset); + } + + /// @brief One iteration of median-value flow of the level set. + /// @param width Width of the median-value kernel in voxel units. + /// @param mask Optional alpha mask. + /// + /// @warning This filter is not separable and is hence relatively + /// slow! + void median(int width = 1, const MaskType* mask = NULL) + { + Filter f(this, mask); f.median(width); + } + + /// @brief One iteration of mean-value flow of the level set. + /// @param width Width of the mean-value kernel in voxel units. + /// @param mask Optional alpha mask. + /// + /// @note This filter is separable so it's fast! + void mean(int width = 1, const MaskType* mask = NULL) + { + Filter f(this, mask); f.mean(width); + } + +private: + // disallow copy construction and copy by assignment! + LevelSetFilter(const LevelSetFilter&);// not implemented + LevelSetFilter& operator=(const LevelSetFilter&);// not implemented + + // Private struct that implements all the filtering. + struct Filter + { + typedef typename TreeType::LeafNodeType LeafT; + typedef typename LeafT::ValueOnIter VoxelIterT; + typedef typename LeafT::ValueOnCIter VoxelCIterT; + typedef typename tree::LeafManager::BufferType BufferT; + typedef typename tree::LeafManager::LeafRange LeafRange; + typedef typename LeafRange::Iterator LeafIterT; + typedef tools::AlphaMask AlphaMaskT; + + Filter(LevelSetFilter* parent, const MaskType* mask) : mParent(parent), mMask(mask) {} + virtual ~Filter() {} + + void box(int width); + void median(int width); + void mean(int width); + void gaussian(int width); + void laplacian(); + void meanCurvature(); + void offset(ValueType value); + void operator()(const LeafRange& r) const + { + if (mTask) mTask(const_cast(this), r); + else OPENVDB_THROW(ValueError, "task is undefined - don\'t call this method directly"); + } + void cook(bool swap) + { + const int n = mParent->getGrainSize(); + if (n>0) { + tbb::parallel_for(mParent->leafs().leafRange(n), *this); + } else { + (*this)(mParent->leafs().leafRange()); + } + if (swap) mParent->leafs().swapLeafBuffer(1, n==0); + } + + template + struct Avg { + Avg(const GridT& grid, Int32 w) : + acc(grid.tree()), width(w), frac(1/ValueType(2*w+1)) {} + inline ValueType operator()(Coord xyz) + { + ValueType sum = zeroVal(); + Int32& i = xyz[Axis], j = i + width; + for (i -= width; i <= j; ++i) sum += acc.getValue(xyz); + return sum*frac; + } + typename GridT::ConstAccessor acc; + const Int32 width; + const ValueType frac; + }; + + template + void box( const LeafRange& r, Int32 w); + + void boxX(const LeafRange& r, Int32 w) { this->box >(r,w); } + void boxZ(const LeafRange& r, Int32 w) { this->box >(r,w); } + void boxY(const LeafRange& r, Int32 w) { this->box >(r,w); } + + void median(const LeafRange&, int); + void meanCurvature(const LeafRange&); + void laplacian(const LeafRange&); + void offset(const LeafRange&, ValueType); + + LevelSetFilter* mParent; + const MaskType* mMask; + typename boost::function mTask; + }; // end of private Filter struct + + AlphaType mMinMask, mMaxMask; + bool mInvertMask; + +}; // end of LevelSetFilter class + + +//////////////////////////////////////// + +template +inline void +LevelSetFilter:: +Filter::median(int width) +{ + mParent->startInterrupter("Median-value flow of level set"); + + mParent->leafs().rebuildAuxBuffers(1, mParent->getGrainSize()==0); + + mTask = boost::bind(&Filter::median, _1, _2, std::max(1, width)); + this->cook(true); + + mParent->track(); + + mParent->endInterrupter(); +} + +template +inline void +LevelSetFilter:: +Filter::mean(int width) +{ + mParent->startInterrupter("Mean-value flow of level set"); + + this->box(width); + + mParent->endInterrupter(); +} + +template +inline void +LevelSetFilter:: +Filter::gaussian(int width) +{ + mParent->startInterrupter("Gaussian flow of level set"); + + for (int n=0; n<4; ++n) this->box(width); + + mParent->endInterrupter(); +} + +template +inline void +LevelSetFilter:: +Filter::box(int width) +{ + mParent->leafs().rebuildAuxBuffers(1, mParent->getGrainSize()==0); + + width = std::max(1, width); + + mTask = boost::bind(&Filter::boxX, _1, _2, width); + this->cook(true); + + mTask = boost::bind(&Filter::boxY, _1, _2, width); + this->cook(true); + + mTask = boost::bind(&Filter::boxZ, _1, _2, width); + this->cook(true); + + mParent->track(); +} + +template +inline void +LevelSetFilter:: +Filter::meanCurvature() +{ + mParent->startInterrupter("Mean-curvature flow of level set"); + + mParent->leafs().rebuildAuxBuffers(1, mParent->getGrainSize()==0); + + mTask = boost::bind(&Filter::meanCurvature, _1, _2); + this->cook(true); + + mParent->track(); + + mParent->endInterrupter(); +} + +template +inline void +LevelSetFilter:: +Filter::laplacian() +{ + mParent->startInterrupter("Laplacian flow of level set"); + + mParent->leafs().rebuildAuxBuffers(1, mParent->getGrainSize()==0); + + mTask = boost::bind(&Filter::laplacian, _1, _2); + this->cook(true); + + mParent->track(); + + mParent->endInterrupter(); +} + +template +inline void +LevelSetFilter:: +Filter::offset(ValueType value) +{ + mParent->startInterrupter("Offsetting level set"); + + mParent->leafs().removeAuxBuffers();// no auxiliary buffers required + + const ValueType CFL = ValueType(0.5) * mParent->voxelSize(), offset = openvdb::math::Abs(value); + ValueType dist = 0.0; + while (offset-dist > ValueType(0.001)*CFL && mParent->checkInterrupter()) { + const ValueType delta = openvdb::math::Min(offset-dist, CFL); + dist += delta; + + mTask = boost::bind(&Filter::offset, _1, _2, copysign(delta, value)); + this->cook(false); + + mParent->track(); + } + + mParent->endInterrupter(); +} + + +///////////////////////// PRIVATE METHODS ////////////////////// + +/// Performs parabolic mean-curvature diffusion +template +inline void +LevelSetFilter:: +Filter::meanCurvature(const LeafRange& range) +{ + mParent->checkInterrupter(); + //const float CFL = 0.9f, dt = CFL * mDx * mDx / 6.0f; + const ValueType dx = mParent->voxelSize(), dt = math::Pow2(dx) / ValueType(3.0); + math::CurvatureStencil stencil(mParent->grid(), dx); + if (mMask) { + typename AlphaMaskT::FloatType a, b; + AlphaMaskT alpha(mParent->grid(), *mMask, mParent->minMask(), + mParent->maxMask(), mParent->isMaskInverted()); + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + ValueType* buffer = leafIter.buffer(1).data(); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + if (alpha(iter.getCoord(), a, b)) { + stencil.moveTo(iter); + const ValueType phi0 = *iter, phi1 = phi0 + dt*stencil.meanCurvatureNormGrad(); + buffer[iter.pos()] = b * phi0 + a * phi1; + } + } + } + } else { + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + ValueType* buffer = leafIter.buffer(1).data(); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + stencil.moveTo(iter); + buffer[iter.pos()] = *iter + 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 +inline void +LevelSetFilter:: +Filter::laplacian(const LeafRange& range) +{ + mParent->checkInterrupter(); + //const float CFL = 0.9f, half_dt = CFL * mDx * mDx / 12.0f; + const ValueType dx = mParent->voxelSize(), dt = math::Pow2(dx) / ValueType(6.0); + math::GradStencil stencil(mParent->grid(), dx); + if (mMask) { + typename AlphaMaskT::FloatType a, b; + AlphaMaskT alpha(mParent->grid(), *mMask, mParent->minMask(), + mParent->maxMask(), mParent->isMaskInverted()); + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + ValueType* buffer = leafIter.buffer(1).data(); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + if (alpha(iter.getCoord(), a, b)) { + stencil.moveTo(iter); + const ValueType phi0 = *iter, phi1 = phi0 + dt*stencil.laplacian(); + buffer[iter.pos()] = b * phi0 + a * phi1; + } + } + } + } else { + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + ValueType* buffer = leafIter.buffer(1).data(); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + stencil.moveTo(iter); + buffer[iter.pos()] = *iter + dt*stencil.laplacian(); + } + } + } +} + +/// Offsets the values by a constant +template +inline void +LevelSetFilter:: +Filter::offset(const LeafRange& range, ValueType offset) +{ + mParent->checkInterrupter(); + if (mMask) { + typename AlphaMaskT::FloatType a, b; + AlphaMaskT alpha(mParent->grid(), *mMask, mParent->minMask(), + mParent->maxMask(), mParent->isMaskInverted()); + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) { + if (alpha(iter.getCoord(), a, b)) iter.setValue(*iter + a*offset); + } + } + } else { + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + for (VoxelIterT iter = leafIter->beginValueOn(); iter; ++iter) { + iter.setValue(*iter + offset); + } + } + } +} + +/// Performs simple but slow median-value diffusion +template +inline void +LevelSetFilter:: +Filter::median(const LeafRange& range, int width) +{ + mParent->checkInterrupter(); + typename math::DenseStencil stencil(mParent->grid(), width);//creates local cache! + if (mMask) { + typename AlphaMaskT::FloatType a, b; + AlphaMaskT alpha(mParent->grid(), *mMask, mParent->minMask(), + mParent->maxMask(), mParent->isMaskInverted()); + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + ValueType* buffer = leafIter.buffer(1).data(); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + if (alpha(iter.getCoord(), a, b)) { + stencil.moveTo(iter); + buffer[iter.pos()] = b * (*iter) + a * stencil.median(); + } + } + } + } else { + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + ValueType* buffer = leafIter.buffer(1).data(); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + stencil.moveTo(iter); + buffer[iter.pos()] = stencil.median(); + } + } + } +} + +/// One dimensional convolution of a separable box filter +template +template +inline void +LevelSetFilter:: +Filter::box(const LeafRange& range, Int32 w) +{ + mParent->checkInterrupter(); + AvgT avg(mParent->grid(), w); + if (mMask) { + typename AlphaMaskT::FloatType a, b; + AlphaMaskT alpha(mParent->grid(), *mMask, mParent->minMask(), + mParent->maxMask(), mParent->isMaskInverted()); + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + ValueType* buffer = leafIter.buffer(1).data(); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + const Coord xyz = iter.getCoord(); + if (alpha(xyz, a, b)) buffer[iter.pos()] = b * (*iter)+ a * avg(xyz); + } + } + } else { + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + ValueType* buffer = leafIter.buffer(1).data(); + for (VoxelCIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + buffer[iter.pos()] = avg(iter.getCoord()); + } + } + } +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_LEVELSETFILTER_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/LevelSetFracture.h b/nuparu/include/openvdb/include/openvdb/tools/LevelSetFracture.h new file mode 100644 index 00000000..db73ee08 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/LevelSetFracture.h @@ -0,0 +1,346 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include +#include + +#include "Composite.h" // for csgIntersectionCopy() and csgDifferenceCopy() +#include "GridTransformer.h" // for resampleToMatch() +#include "LevelSetUtil.h" // for sdfSegmentation() + +#include +#include + +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Level set fracturing +template +class LevelSetFracture +{ +public: + typedef std::vector Vec3sList; + typedef std::vector QuatsList; + typedef std::list 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 level_set_fracture_internal { + + +template +struct FindMinMaxVoxelValue { + + typedef typename LeafNodeType::ValueType ValueType; + + FindMinMaxVoxelValue(const std::vector& nodes) + : minValue(std::numeric_limits::max()) + , maxValue(-minValue) + , mNodes(nodes.empty() ? NULL : &nodes.front()) + { + } + + FindMinMaxVoxelValue(FindMinMaxVoxelValue& rhs, tbb::split) + : minValue(std::numeric_limits::max()) + , maxValue(-minValue) + , mNodes(rhs.mNodes) + { + } + + void operator()(const tbb::blocked_range& range) { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + const ValueType* data = mNodes[n]->buffer().data(); + for (Index i = 0; i < LeafNodeType::SIZE; ++i) { + minValue = std::min(minValue, data[i]); + maxValue = std::max(maxValue, data[i]); + } + } + } + + void join(FindMinMaxVoxelValue& rhs) { + minValue = std::min(minValue, rhs.minValue); + maxValue = std::max(maxValue, rhs.maxValue); + } + + ValueType minValue, maxValue; + + LeafNodeType const * const * const mNodes; +}; // struct FindMinMaxVoxelValue + + +} // namespace level_set_fracture_internal + + +//////////////////////////////////////// + + +template +LevelSetFracture::LevelSetFracture(InterruptType* interrupter) + : mInterrupter(interrupter) + , mFragments() +{ +} + + +template +void +LevelSetFracture::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); + + // Since there is no scaling, use the generic resampler instead of + // the more expensive level set rebuild tool. + if (mInterrupter != NULL) { + + if (hasInstanceRotations) { + doResampleToMatch(cutterGrid, instCutterGrid, *mInterrupter); + } else { + doResampleToMatch(cutterGrid, instCutterGrid, *mInterrupter); + } + } else { + util::NullInterrupter interrupter; + if (hasInstanceRotations) { + doResampleToMatch(cutterGrid, instCutterGrid, interrupter); + } else { + doResampleToMatch(cutterGrid, instCutterGrid, interrupter); + } + } + + if (wasInterrupted(percent)) break; + + 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 +bool +LevelSetFracture::isValidFragment(GridType& grid) const +{ + typedef typename GridType::TreeType::LeafNodeType LeafNodeType; + + if (grid.tree().leafCount() < 9) { + + std::vector nodes; + grid.tree().getNodes(nodes); + + Index64 activeVoxelCount = 0; + + for (size_t n = 0, N = nodes.size(); n < N; ++n) { + activeVoxelCount += nodes[n]->onVoxelCount(); + } + + if (activeVoxelCount < 27) return false; + + level_set_fracture_internal::FindMinMaxVoxelValue op(nodes); + tbb::parallel_reduce(tbb::blocked_range(0, nodes.size()), op); + + if ((op.minValue < 0) == (op.maxValue < 0)) return false; + } + + return true; +} + + +template +void +LevelSetFracture::segmentFragments(GridPtrList& grids) const +{ + GridPtrList newFragments; + + for (GridPtrListIter it = grids.begin(); it != grids.end(); ++it) { + + std::vector segments; + segmentSDF(*(*it), segments); + + for (size_t n = 0, N = segments.size(); n < N; ++n) { + newFragments.push_back(segments[n]); + } + } + + grids.swap(newFragments); +} + + +template +void +LevelSetFracture::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; + + GridPtr fragment = csgIntersectionCopy(*grid, cutter); + if (!isValidFragment(*fragment)) continue; + + GridPtr residual = csgDifferenceCopy(*grid, cutter); + 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-2016 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/nuparu/include/openvdb/include/openvdb/tools/LevelSetMeasure.h b/nuparu/include/openvdb/include/openvdb/tools/LevelSetMeasure.h new file mode 100644 index 00000000..f7880182 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/LevelSetMeasure.h @@ -0,0 +1,567 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 LevelSetMeasure.h + +#ifndef OPENVDB_TOOLS_LEVELSETMEASURE_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_LEVELSETMEASURE_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include +#include //for Pi +#include +#include +#include +#include +#include +#include +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Return the surface area of a narrow-band level set. +/// +/// @param grid a scalar, floating-point grid with one or more disjoint, +/// closed isosurfaces at the given @a isovalue +/// @param useWorldSpace if true the area is computed in +/// world space units, else in voxel units. +/// +/// @throw TypeError if @a grid is not scalar or not floating-point or not a level set. +template +inline Real +levelSetArea(const GridType& grid, bool useWorldSpace = true); + +/// @brief Return the volume of a narrow-band level set surface. +/// +/// @param grid a scalar, floating-point grid with one or more disjoint, +/// closed isosurfaces at the given @a isovalue +/// @param useWorldSpace if true the volume is computed in +/// world space units, else in voxel units. +/// +/// @throw TypeError if @a grid is not scalar or not floating-point or not a level set. +template +inline Real +levelSetVolume(const GridType& grid, bool useWorldSpace = true); + +/// @brief Compute the surface area and volume of a narrow-band level set. +/// +/// @param grid a scalar, floating-point grid with one or more disjoint, +/// closed isosurfaces at the given @a isovalue +/// @param area surface area of the level set +/// @param volume volume of the level set surface +/// @param useWorldSpace if true the area and volume are computed in +/// world space units, else in voxel units. +/// +/// @throw TypeError if @a grid is not scalar or not floating-point or not a level set. +template +inline void +levelSetMeasure(const GridType& grid, Real& area, Real& volume, bool useWorldSpace = true); + +/// @brief Compute the surface area and volume of a narrow-band level set. +/// +/// @param grid a scalar, floating-point grid with one or more disjoint, +/// closed isosurfaces at the given @a isovalue +/// @param area surface area of the level set +/// @param volume volume of the level set surface +/// @param avgCurvature average mean curvature of the level set surface +/// @param useWorldSpace if true the area, volume and curvature are computed in +/// world space units, else in voxel units. +/// +/// @throw TypeError if @a grid is not scalar or not floating-point or not a level set. +template +inline void +levelSetMeasure(const GridType& grid, Real& area, Real& volume, Real& avgCurvature, + bool useWorldSpace = true); + +/// @brief Smeared-out and continuous Dirac Delta function. +template +class DiracDelta +{ +public: + DiracDelta(RealT eps) : mC(0.5/eps), mD(2*boost::math::constants::pi()*mC), mE(eps) {} + inline RealT operator()(RealT phi) const { return math::Abs(phi) > mE ? 0 : mC*(1+cos(mD*phi)); } +private: + const RealT mC, mD, mE; +}; + + +/// @brief Multi-threaded computation of surface area, volume and +/// average mean-curvature for narrow band level sets. +/// +/// @details To reduce the risk of round-off errors (primarily due to +/// catastrophic cancellation) and guarantee determinism during +/// multi-threading this class is implemented using parallel_for, and +/// delayed reduction of a sorted list. +template +class LevelSetMeasure +{ +public: + typedef GridT GridType; + typedef typename GridType::TreeType TreeType; + typedef typename TreeType::ValueType ValueType; + typedef typename tree::LeafManager ManagerType; + BOOST_STATIC_ASSERT(boost::is_floating_point::value); + + /// @brief Main constructor from a grid + /// @param grid The level set to be measured. + /// @param interrupt Optional interrupter. + /// @throw RuntimeError if the grid is not a level set. + LevelSetMeasure(const GridType& grid, InterruptT* interrupt = NULL); + + LevelSetMeasure(ManagerType& leafs, Real Dx, InterruptT* interrupt); + + /// @brief Re-initialize using the specified grid. + void reinit(const GridType& grid); + + /// @brief Re-initialize using the specified LeafManager and voxelSize. + void reinit(ManagerType& leafs, Real dx); + + /// @brief Destructor + virtual ~LevelSetMeasure() {} + + /// @return the grain-size used for multi-threading + int getGrainSize() const { return mGrainSize; } + + /// @brief Set the grain-size used for multi-threading. + /// @note A grain size of 0 or less disables multi-threading! + void setGrainSize(int grainsize) { mGrainSize = grainsize; } + + /// @brief Compute the surface area and volume of the level + /// set. Use the last argument to specify the result in world or + /// voxel units. + /// @note This method is faster (about 3x) then the measure method + /// below that also computes the average mean-curvature. + void measure(Real& area, Real& volume, bool useWorldUnits = true); + + /// @brief Compute the surface area, volume, and average + /// mean-curvature of the level set. Use the last argument to + /// specify the result in world or voxel units. + /// @note This method is slower (about 3x) then the measure method + /// above that only computes the area and volume. + void measure(Real& area, Real& volume, Real& avgMeanCurvature, bool useWorldUnits = true); + +private: + // disallow copy construction and copy by assignment! + LevelSetMeasure(const LevelSetMeasure&);// not implemented + LevelSetMeasure& operator=(const LevelSetMeasure&);// not implemented + + const TreeType* mTree; + ManagerType* mLeafs; + InterruptT* mInterrupter; + double mDx; + double* mArray; + int mGrainSize; + + // @brief Return false if the process was interrupted + bool checkInterrupter(); + + typedef typename TreeType::LeafNodeType LeafT; + typedef typename LeafT::ValueOnCIter VoxelCIterT; + typedef typename ManagerType::LeafRange LeafRange; + typedef typename LeafRange::Iterator LeafIterT; + + struct Measure2 + { + Measure2(LevelSetMeasure* parent) : mParent(parent), mAcc(*mParent->mTree) + { + if (parent->mGrainSize>0) { + tbb::parallel_for(parent->mLeafs->leafRange(parent->mGrainSize), *this); + } else { + (*this)(parent->mLeafs->leafRange()); + } + } + Measure2(const Measure2& other) : mParent(other.mParent), mAcc(*mParent->mTree) {} + void operator()(const LeafRange& range) const; + LevelSetMeasure* mParent; + typename GridT::ConstAccessor mAcc; + }; + struct Measure3 + { + Measure3(LevelSetMeasure* parent) : mParent(parent), mAcc(*mParent->mTree) + { + if (parent->mGrainSize>0) { + tbb::parallel_for(parent->mLeafs->leafRange(parent->mGrainSize), *this); + } else { + (*this)(parent->mLeafs->leafRange()); + } + } + Measure3(const Measure3& other) : mParent(other.mParent), mAcc(*mParent->mTree) {} + void operator()(const LeafRange& range) const; + LevelSetMeasure* mParent; + typename GridT::ConstAccessor mAcc; + }; + inline double reduce(double* first, double scale) + { + double* last = first + mLeafs->leafCount(); + tbb::parallel_sort(first, last);//reduces catastrophic cancellation + Real sum = 0.0; + while(first != last) sum += *first++; + return scale * sum; + } + +}; // end of LevelSetMeasure class + + +template +inline +LevelSetMeasure::LevelSetMeasure(const GridType& grid, InterruptT* interrupt) + : mTree(&(grid.tree())) + , mLeafs(NULL) + , mInterrupter(interrupt) + , mDx(grid.voxelSize()[0]) + , mArray(NULL) + , mGrainSize(1) +{ + if (!grid.hasUniformVoxels()) { + OPENVDB_THROW(RuntimeError, + "The transform must have uniform scale for the LevelSetMeasure to function"); + } + if (grid.getGridClass() != GRID_LEVEL_SET) { + OPENVDB_THROW(RuntimeError, + "LevelSetMeasure only supports level sets;" + " try setting the grid class to \"level set\""); + } +} + + +template +inline +LevelSetMeasure::LevelSetMeasure( + ManagerType& leafs, Real dx, InterruptT* interrupt) + : mTree(&(leafs.tree())) + , mLeafs(&leafs) + , mInterrupter(interrupt) + , mDx(dx) + , mArray(NULL) + , mGrainSize(1) +{ +} + +template +inline void +LevelSetMeasure::reinit(const GridType& grid) +{ + if (!grid.hasUniformVoxels()) { + OPENVDB_THROW(RuntimeError, + "The transform must have uniform scale for the LevelSetMeasure to function"); + } + if (grid.getGridClass() != GRID_LEVEL_SET) { + OPENVDB_THROW(RuntimeError, + "LevelSetMeasure only supports level sets;" + " try setting the grid class to \"level set\""); + } + mTree = &(grid.tree()); + mLeafs = NULL; + mDx = grid.voxelSize()[0]; +} + + +template +inline void +LevelSetMeasure::reinit(ManagerType& leafs, Real dx) +{ + mLeafs = &leafs; + mTree = &(leafs.tree()); + mDx = dx; +} + +//////////////////////////////////////// + + +template +inline void +LevelSetMeasure::measure(Real& area, Real& volume, bool useWorldUnits) +{ + if (mInterrupter) mInterrupter->start("Measuring level set"); + + + const bool newLeafs = mLeafs == NULL; + if (newLeafs) mLeafs = new ManagerType(*mTree); + const size_t leafCount = mLeafs->leafCount(); + if (leafCount == 0) { + area = volume = 0; + return; + } + mArray = new double[2*leafCount]; + + Measure2 m(this); + + const double dx = useWorldUnits ? mDx : 1.0; + area = this->reduce(mArray, math::Pow2(dx)); + volume = this->reduce(mArray + leafCount, math::Pow3(dx) / 3.0); + + if (newLeafs) { + delete mLeafs; + mLeafs = NULL; + } + delete [] mArray; + + if (mInterrupter) mInterrupter->end(); +} + + +template +inline void +LevelSetMeasure::measure(Real& area, Real& volume, + Real& avgMeanCurvature, + bool useWorldUnits) +{ + if (mInterrupter) mInterrupter->start("Measuring level set"); + + const bool newLeafs = mLeafs == NULL; + if (newLeafs) mLeafs = new ManagerType(*mTree); + const size_t leafCount = mLeafs->leafCount(); + if (leafCount == 0) { + area = volume = avgMeanCurvature = 0; + return; + } + mArray = new double[3*leafCount]; + + Measure3 m(this); + + const double dx = useWorldUnits ? mDx : 1.0; + area = this->reduce(mArray, math::Pow2(dx)); + volume = this->reduce(mArray + leafCount, math::Pow3(dx) / 3.0); + avgMeanCurvature = this->reduce(mArray + 2*leafCount, dx/area); + + if (newLeafs) { + delete mLeafs; + mLeafs = NULL; + } + delete [] mArray; + + if (mInterrupter) mInterrupter->end(); +} + + +///////////////////////// PRIVATE METHODS ////////////////////// + + +template +inline bool +LevelSetMeasure::checkInterrupter() +{ + if (util::wasInterrupted(mInterrupter)) { + tbb::task::self().cancel_group_execution(); + return false; + } + return true; +} + +template +inline void +LevelSetMeasure:: +Measure2::operator()(const LeafRange& range) const +{ + typedef math::Vec3 Vec3T; + typedef math::ISGradient Grad; + mParent->checkInterrupter(); + const Real invDx = 1.0/mParent->mDx; + const DiracDelta DD(1.5); + const size_t leafCount = mParent->mLeafs->leafCount(); + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + Real sumA = 0, sumV = 0;//reduce risk of catastrophic cancellation + for (VoxelCIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) { + const Real dd = DD(invDx * (*voxelIter)); + if (dd > 0.0) { + const Coord p = voxelIter.getCoord(); + const Vec3T g = invDx*Grad::result(mAcc, p);//voxel units + sumA += dd * g.dot(g); + sumV += dd * (g[0]*p[0]+g[1]*p[1]+g[2]*p[2]); + } + } + double* v = mParent->mArray + leafIter.pos(); + *v = sumA; + v += leafCount; + *v = sumV; + } +} + +template +inline void +LevelSetMeasure:: +Measure3::operator()(const LeafRange& range) const +{ + typedef math::Vec3 Vec3T; + typedef math::ISGradient Grad; + typedef math::ISMeanCurvature Curv; + mParent->checkInterrupter(); + const Real invDx = 1.0/mParent->mDx; + const DiracDelta DD(1.5); + ValueType alpha, beta; + const size_t leafCount = mParent->mLeafs->leafCount(); + for (LeafIterT leafIter=range.begin(); leafIter; ++leafIter) { + Real sumA = 0, sumV = 0, sumC = 0;//reduce risk of catastrophic cancellation + for (VoxelCIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) { + const Real dd = DD(invDx * (*voxelIter)); + if (dd > 0.0) { + const Coord p = voxelIter.getCoord(); + const Vec3T g = invDx*Grad::result(mAcc, p);//voxel units + const Real dA = dd * g.dot(g); + sumA += dA; + sumV += dd * (g[0]*p[0]+g[1]*p[1]+g[2]*p[2]); + Curv::result(mAcc, p, alpha, beta); + sumC += dA * alpha/(2*math::Pow2(beta))*invDx; + } + } + double* v = mParent->mArray + leafIter.pos(); + *v = sumA; + v += leafCount; + *v = sumV; + v += leafCount; + *v = sumC; + } +} + +//////////////////////////////////////// + +template +inline typename boost::enable_if, Real>::type +doLevelSetArea(const GridT& grid, bool useWorldSpace) +{ + Real area, volume; + LevelSetMeasure m(grid); + m.measure(area, volume, useWorldSpace); + return area; +} + +template +inline typename boost::disable_if, Real>::type +doLevelSetArea(const GridT&, bool) +{ + OPENVDB_THROW(TypeError, + "level set area is supported only for scalar, floating-point grids"); +} + +template +inline Real +levelSetArea(const GridT& grid, bool useWorldSpace) +{ + return doLevelSetArea(grid, useWorldSpace); +} + +//////////////////////////////////////// + +template +inline typename boost::enable_if, Real>::type +doLevelSetVolume(const GridT& grid, bool useWorldSpace) +{ + Real area, volume; + LevelSetMeasure m(grid); + m.measure(area, volume, useWorldSpace); + return volume; +} + +template +inline typename boost::disable_if, Real>::type +doLevelSetVolume(const GridT&, bool) +{ + OPENVDB_THROW(TypeError, + "level set volume is supported only for scalar, floating-point grids"); +} + +template +inline Real +levelSetVolume(const GridT& grid, bool useWorldSpace) +{ + return doLevelSetVolume(grid, useWorldSpace); +} + +//////////////////////////////////////// + +template +inline typename boost::enable_if >::type +doLevelSetMeasure(const GridT& grid, Real& area, Real& volume, bool useWorldSpace) +{ + LevelSetMeasure m(grid); + m.measure(area, volume, useWorldSpace); +} + +template +inline typename boost::disable_if >::type +doLevelSetMeasure(const GridT&, Real&, Real&, bool) +{ + OPENVDB_THROW(TypeError, + "level set measure is supported only for scalar, floating-point grids"); +} + +template +inline void +levelSetMeasure(const GridT& grid, Real& area, Real& volume, bool useWorldSpace) +{ + doLevelSetMeasure(grid, area, volume, useWorldSpace); +} + +//////////////////////////////////////// + +template +inline typename boost::enable_if >::type +doLevelSetMeasure(const GridT& grid, Real& area, Real& volume, Real& avgCurvature, + bool useWorldSpace) +{ + LevelSetMeasure m(grid); + m.measure(area, volume, avgCurvature, useWorldSpace); +} + +template +inline typename boost::disable_if >::type +doLevelSetMeasure(const GridT&, Real&, Real&, Real&, bool) +{ + OPENVDB_THROW(TypeError, + "level set measure is supported only for scalar, floating-point grids"); +} + +template +inline void +levelSetMeasure(const GridT& grid, Real& area, Real& volume, Real& avgCurvature, bool useWorldSpace) +{ + doLevelSetMeasure(grid, area, volume, avgCurvature, useWorldSpace); +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_LEVELSETMEASURE_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/LevelSetMorph.h b/nuparu/include/openvdb/include/openvdb/tools/LevelSetMorph.h new file mode 100644 index 00000000..618d5bea --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/LevelSetMorph.h @@ -0,0 +1,664 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 LevelSetMorph.h +/// +/// @brief Shape morphology of level sets. Morphing from a source +/// narrow-band level sets to a target narrow-band level set. + +#ifndef OPENVDB_TOOLS_LEVEL_SET_MORPH_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_LEVEL_SET_MORPH_HAS_BEEN_INCLUDED + +#include "LevelSetTracker.h" +#include "Interpolation.h" // for BoxSampler, etc. +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + + +/// @brief Shape morphology of level sets. Morphing from a source +/// narrow-band level sets to a target narrow-band level set. +/// +/// @details +/// 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., they incur no computational overhead). +template +class LevelSetMorphing +{ +public: + typedef GridT GridType; + typedef typename GridT::TreeType TreeType; + typedef LevelSetTracker TrackerT; + typedef typename TrackerT::LeafRange LeafRange; + typedef typename TrackerT::LeafType LeafType; + typedef typename TrackerT::BufferType BufferType; + typedef typename TrackerT::ValueType ValueType; + + /// Main constructor + LevelSetMorphing(GridT& sourceGrid, + const GridT& targetGrid, + InterruptT* interrupt = NULL) + : mTracker(sourceGrid, interrupt) + , mTarget(&targetGrid) + , mMask(NULL) + , mSpatialScheme(math::HJWENO5_BIAS) + , mTemporalScheme(math::TVD_RK2) + , mMinMask(0) + , mDeltaMask(1) + , mInvertMask(false) + { + } + + virtual ~LevelSetMorphing() {} + + /// Redefine the target level set + void setTarget(const GridT& targetGrid) { mTarget = &targetGrid; } + + /// Define the alpha mask + void setAlphaMask(const GridT& maskGrid) { mMask = &maskGrid; } + + /// Return the spatial finite-difference scheme + math::BiasedGradientScheme getSpatialScheme() const { return mSpatialScheme; } + /// Set the spatial finite-difference scheme + void setSpatialScheme(math::BiasedGradientScheme scheme) { mSpatialScheme = scheme; } + + /// Return the temporal integration scheme + math::TemporalIntegrationScheme getTemporalScheme() const { return mTemporalScheme; } + /// Set the temporal integration scheme + void setTemporalScheme(math::TemporalIntegrationScheme scheme) { mTemporalScheme = scheme; } + + /// Return the spatial finite-difference scheme + math::BiasedGradientScheme getTrackerSpatialScheme() const + { + return mTracker.getSpatialScheme(); + } + /// 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(); + } + /// Set the temporal integration 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(); } + /// Set the number of normalizations performed per track or normalize call. + void setNormCount(int n) { mTracker.setNormCount(n); } + + /// Return the grain size used for multithreading + int getGrainSize() const { return mTracker.getGrainSize(); } + /// @brief Set the grain size used for multithreading. + /// @note A grain size of 0 or less disables multithreading! + void setGrainSize(int grainsize) { mTracker.setGrainSize(grainsize); } + + /// @brief Return the minimum value of the mask to be used for the + /// derivation of a smooth alpha value. + ValueType minMask() const { return mMinMask; } + + /// @brief Return the maximum value of the mask to be used for the + /// derivation of a smooth alpha value. + ValueType maxMask() const { return mDeltaMask + mMinMask; } + + /// @brief Define the range for the (optional) scalar mask. + /// @param min Minimum value of the range. + /// @param max Maximum value of the range. + /// @details Mask values outside the range maps to alpha values of + /// respectfully zero and one, and values inside the range maps + /// smoothly to 0->1 (unless of course the mask is inverted). + /// @throw ValueError if @a min is not smaller than @a max. + void setMaskRange(ValueType min, ValueType max) + { + if (!(min < max)) OPENVDB_THROW(ValueError, "Invalid mask range (expects min < max)"); + mMinMask = min; + mDeltaMask = max-min; + } + + /// @brief Return true if the mask is inverted, i.e. min->max in the + /// original mask maps to 1->0 in the inverted alpha mask. + bool isMaskInverted() const { return mInvertMask; } + /// @brief Invert the optional mask, i.e. min->max in the original + /// mask maps to 1->0 in the inverted alpha mask. + void invertMask(bool invert=true) { mInvertMask = invert; } + + /// @brief Advect the level set from its current time, @a time0, to its + /// final time, @a time1. If @a time0 > @a time1, perform backward advection. + /// + /// @return the number of CFL iterations used to advect from @a time0 to @a time1 + size_t advect(ValueType time0, ValueType time1); + +private: + + // disallow copy construction and copy by assignment! + LevelSetMorphing(const LevelSetMorphing&);// not implemented + LevelSetMorphing& operator=(const LevelSetMorphing&);// not implemented + + template + size_t advect1(ValueType time0, ValueType time1); + + template + size_t advect2(ValueType time0, ValueType time1); + + template + size_t advect3(ValueType time0, ValueType time1); + + TrackerT mTracker; + const GridT *mTarget, *mMask; + math::BiasedGradientScheme mSpatialScheme; + math::TemporalIntegrationScheme mTemporalScheme; + ValueType mMinMask, mDeltaMask; + bool mInvertMask; + + // This templated private class implements all the level set magic. + template + struct Morph + { + /// Main constructor + Morph(LevelSetMorphing& parent); + /// Shallow copy constructor called by tbb::parallel_for() threads + Morph(const Morph& other); + /// Shallow copy constructor called by tbb::parallel_reduce() threads + Morph(Morph& other, tbb::split); + /// destructor + virtual ~Morph() {} + /// Advect the level set from its current time, time0, to its final time, time1. + /// @return number of CFL iterations + size_t advect(ValueType time0, ValueType time1); + /// Used internally by tbb::parallel_for() + void operator()(const LeafRange& r) const + { + if (mTask) mTask(const_cast(this), r); + else OPENVDB_THROW(ValueError, "task is undefined - don\'t call this method directly"); + } + /// Used internally by tbb::parallel_reduce() + void operator()(const LeafRange& 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 Morph& other) { mMaxAbsS = math::Max(mMaxAbsS, other.mMaxAbsS); } + + /// Enum to define the type of multithreading + 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 sampleSpeed(ValueType time0, ValueType time1, Index speedBuffer); + void sampleXformedSpeed(const LeafRange& r, Index speedBuffer); + void sampleAlignedSpeed(const LeafRange& r, Index speedBuffer); + + // Convex combination of Phi and a forward Euler advection steps: + // Phi(result) = alpha * Phi(phi) + (1-alpha) * (Phi(0) - dt * Speed(speed)*|Grad[Phi(0)]|); + template + void euler(const LeafRange&, ValueType, Index, Index, Index); + inline void euler01(const LeafRange& r, ValueType t, Index s) {this->euler<0,1>(r,t,0,1,s);} + inline void euler12(const LeafRange& r, ValueType t) {this->euler<1,2>(r, t, 1, 1, 2);} + inline void euler34(const LeafRange& r, ValueType t) {this->euler<3,4>(r, t, 1, 2, 3);} + inline void euler13(const LeafRange& r, ValueType t) {this->euler<1,3>(r, t, 1, 2, 3);} + + typedef typename boost::function FuncType; + LevelSetMorphing* mParent; + ValueType mMinAbsS, mMaxAbsS; + const MapT* mMap; + FuncType mTask; + }; // end of private Morph struct + +};//end of LevelSetMorphing + +template +inline size_t +LevelSetMorphing::advect(ValueType time0, ValueType time1) +{ + switch (mSpatialScheme) { + case math::FIRST_BIAS: + return this->advect1(time0, time1); + //case math::SECOND_BIAS: + //return this->advect1(time0, time1); + //case math::THIRD_BIAS: + //return this->advect1(time0, time1); + //case math::WENO5_BIAS: + //return this->advect1(time0, time1); + case math::HJWENO5_BIAS: + return this->advect1(time0, time1); + default: + OPENVDB_THROW(ValueError, "Spatial difference scheme not supported!"); + } + return 0; +} + +template +template +inline size_t +LevelSetMorphing::advect1(ValueType time0, ValueType time1) +{ + switch (mTemporalScheme) { + case math::TVD_RK1: + return this->advect2(time0, time1); + case math::TVD_RK2: + return this->advect2(time0, time1); + case math::TVD_RK3: + return this->advect2(time0, time1); + default: + OPENVDB_THROW(ValueError, "Temporal integration scheme not supported!"); + } + return 0; +} + +template +template +inline size_t +LevelSetMorphing::advect2(ValueType time0, ValueType time1) +{ + const math::Transform& trans = mTracker.grid().transform(); + if (trans.mapType() == math::UniformScaleMap::mapType()) { + return this->advect3(time0, time1); + } else if (trans.mapType() == math::UniformScaleTranslateMap::mapType()) { + return this->advect3( + time0, time1); + } else if (trans.mapType() == math::UnitaryMap::mapType()) { + return this->advect3(time0, time1); + } else if (trans.mapType() == math::TranslationMap::mapType()) { + return this->advect3(time0, time1); + } else { + OPENVDB_THROW(ValueError, "MapType not supported!"); + } + return 0; +} + +template +template +inline size_t +LevelSetMorphing::advect3(ValueType time0, ValueType time1) +{ + Morph tmp(*this); + return tmp.advect(time0, time1); +} + + +/////////////////////////////////////////////////////////////////////// + +template +template +inline +LevelSetMorphing:: +Morph:: +Morph(LevelSetMorphing& parent) + : mParent(&parent) + , mMinAbsS(ValueType(1e-6)) + , mMap(parent.mTracker.grid().transform().template constMap().get()) + , mTask(0) +{ +} + +template +template +inline +LevelSetMorphing:: +Morph:: +Morph(const Morph& other) + : mParent(other.mParent) + , mMinAbsS(other.mMinAbsS) + , mMaxAbsS(other.mMaxAbsS) + , mMap(other.mMap) + , mTask(other.mTask) +{ +} + +template +template +inline +LevelSetMorphing:: +Morph:: +Morph(Morph& other, tbb::split) + : mParent(other.mParent) + , mMinAbsS(other.mMinAbsS) + , mMaxAbsS(other.mMaxAbsS) + , mMap(other.mMap) + , mTask(other.mTask) +{ +} + +template +template +inline size_t +LevelSetMorphing:: +Morph:: +advect(ValueType time0, ValueType time1) +{ + // Make sure we have enough temporal auxiliary buffers for the time + // integration AS WELL AS an extra buffer with the speed function! + static const Index auxBuffers = 1 + (TemporalScheme == math::TVD_RK3 ? 2 : 1); + size_t countCFL = 0; + while (time0 < time1 && mParent->mTracker.checkInterrupter()) { + mParent->mTracker.leafs().rebuildAuxBuffers(auxBuffers); + + const ValueType dt = this->sampleSpeed(time0, time1, auxBuffers); + if ( math::isZero(dt) ) break;//V is essentially zero so terminate + + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN //switch is resolved at compile-time + switch(TemporalScheme) { + case math::TVD_RK1: + // Perform one explicit Euler step: t1 = t0 + dt + // Phi_t1(1) = Phi_t0(0) - dt * Speed(2) * |Grad[Phi(0)]| + mTask = boost::bind(&Morph::euler01, _1, _2, dt, /*speed*/2); + + // 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 * Speed(2) * |Grad[Phi(0)]| + mTask = boost::bind(&Morph::euler01, _1, _2, dt, /*speed*/2); + + // 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 * Speed(2) * |Grad[Phi(0)]|) + mTask = boost::bind(&Morph::euler12, _1, _2, dt); + + // 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 * Speed(3) * |Grad[Phi(0)]| + mTask = boost::bind(&Morph::euler01, _1, _2, dt, /*speed*/3); + + // 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 * Speed(3) * |Grad[Phi(0)]|) + mTask = boost::bind(&Morph::euler34, _1, _2, dt); + + // 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 * Speed(3) * |Grad[Phi(0)]|) + mTask = boost::bind(&Morph::euler13, _1, _2, dt); + + // 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!"); + }//end of compile-time resolved switch + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + + time0 += dt; + ++countCFL; + mParent->mTracker.leafs().removeAuxBuffers(); + + // Track the narrow band + mParent->mTracker.track(); + }//end wile-loop over time + + return countCFL;//number of CLF propagation steps +} + +template +template +inline typename GridT::ValueType +LevelSetMorphing:: +Morph:: +sampleSpeed(ValueType time0, ValueType time1, Index speedBuffer) +{ + mMaxAbsS = mMinAbsS; + const size_t leafCount = mParent->mTracker.leafs().leafCount(); + if (leafCount==0 || time0 >= time1) return ValueType(0); + + const math::Transform& xform = mParent->mTracker.grid().transform(); + if (mParent->mTarget->transform() == xform && + (mParent->mMask == NULL || mParent->mMask->transform() == xform)) { + mTask = boost::bind(&Morph::sampleAlignedSpeed, _1, _2, speedBuffer); + } else { + mTask = boost::bind(&Morph::sampleXformedSpeed, _1, _2, speedBuffer); + } + this->cook(PARALLEL_REDUCE); + if (math::isApproxEqual(mMinAbsS, mMaxAbsS)) return ValueType(0);//speed is essentially zero + static const ValueType CFL = (TemporalScheme == math::TVD_RK1 ? ValueType(0.3) : + TemporalScheme == math::TVD_RK2 ? ValueType(0.9) : + ValueType(1.0))/math::Sqrt(ValueType(3.0)); + const ValueType dt = math::Abs(time1 - time0), dx = mParent->mTracker.voxelSize(); + return math::Min(dt, ValueType(CFL*dx/mMaxAbsS)); +} + +template +template +inline void +LevelSetMorphing:: +Morph:: +sampleXformedSpeed(const LeafRange& range, Index speedBuffer) +{ + typedef typename LeafType::ValueOnCIter VoxelIterT; + typedef tools::GridSampler SamplerT; + const MapT& map = *mMap; + mParent->mTracker.checkInterrupter(); + + typename GridT::ConstAccessor targetAcc = mParent->mTarget->getAccessor(); + SamplerT target(targetAcc, mParent->mTarget->transform()); + if (mParent->mMask == NULL) { + for (typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + ValueType* speed = leafIter.buffer(speedBuffer).data(); + bool isZero = true; + for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) { + ValueType& s = speed[voxelIter.pos()]; + s -= target.wsSample(map.applyMap(voxelIter.getCoord().asVec3d())); + if (!math::isApproxZero(s)) isZero = false; + mMaxAbsS = math::Max(mMaxAbsS, math::Abs(s)); + } + if (isZero) speed[0] = std::numeric_limits::max();//tag first voxel + } + } else { + const ValueType min = mParent->mMinMask, invNorm = 1.0f/(mParent->mDeltaMask); + const bool invMask = mParent->isMaskInverted(); + typename GridT::ConstAccessor maskAcc = mParent->mMask->getAccessor(); + SamplerT mask(maskAcc, mParent->mMask->transform()); + for (typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + ValueType* speed = leafIter.buffer(speedBuffer).data(); + bool isZero = true; + for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) { + const Vec3R xyz = map.applyMap(voxelIter.getCoord().asVec3d());//world space + const ValueType a = math::SmoothUnitStep((mask.wsSample(xyz)-min)*invNorm); + ValueType& s = speed[voxelIter.pos()]; + s -= target.wsSample(xyz); + s *= invMask ? 1 - a : a; + if (!math::isApproxZero(s)) isZero = false; + mMaxAbsS = math::Max(mMaxAbsS, math::Abs(s)); + } + if (isZero) speed[0] = std::numeric_limits::max();//tag first voxel + } + } +} + +template +template +inline void +LevelSetMorphing:: +Morph:: +sampleAlignedSpeed(const LeafRange& range, Index speedBuffer) +{ + typedef typename LeafType::ValueOnCIter VoxelIterT; + mParent->mTracker.checkInterrupter(); + + typename GridT::ConstAccessor target = mParent->mTarget->getAccessor(); + + if (mParent->mMask == NULL) { + for (typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + ValueType* speed = leafIter.buffer(speedBuffer).data(); + bool isZero = true; + for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) { + ValueType& s = speed[voxelIter.pos()]; + s -= target.getValue(voxelIter.getCoord()); + if (!math::isApproxZero(s)) isZero = false; + mMaxAbsS = math::Max(mMaxAbsS, math::Abs(s)); + } + if (isZero) speed[0] = std::numeric_limits::max();//tag first voxel + } + } else { + const ValueType min = mParent->mMinMask, invNorm = 1.0f/(mParent->mDeltaMask); + const bool invMask = mParent->isMaskInverted(); + typename GridT::ConstAccessor mask = mParent->mMask->getAccessor(); + for (typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + ValueType* speed = leafIter.buffer(speedBuffer).data(); + bool isZero = true; + for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) { + const Coord ijk = voxelIter.getCoord();//index space + const ValueType a = math::SmoothUnitStep((mask.getValue(ijk)-min)*invNorm); + ValueType& s = speed[voxelIter.pos()]; + s -= target.getValue(ijk); + s *= invMask ? 1 - a : a; + if (!math::isApproxZero(s)) isZero = false; + mMaxAbsS = math::Max(mMaxAbsS, math::Abs(s)); + } + if (isZero) speed[0] = std::numeric_limits::max();//tag first voxel + } + } +} + +template +template +inline void +LevelSetMorphing:: +Morph:: +cook(ThreadingMode mode, size_t swapBuffer) +{ + mParent->mTracker.startInterrupter("Morphing level set"); + + const int grainSize = mParent->mTracker.getGrainSize(); + const LeafRange range = mParent->mTracker.leafs().leafRange(grainSize); + + if (mParent->mTracker.getGrainSize()==0) { + (*this)(range); + } else if (mode == PARALLEL_FOR) { + tbb::parallel_for(range, *this); + } else if (mode == PARALLEL_REDUCE) { + tbb::parallel_reduce(range, *this); + } else { + throw std::runtime_error("Undefined threading mode"); + } + + mParent->mTracker.leafs().swapLeafBuffer(swapBuffer, grainSize == 0); + + mParent->mTracker.endInterrupter(); +} + +template +template +template +inline void +LevelSetMorphing:: +Morph:: +euler(const LeafRange& range, ValueType dt, + Index phiBuffer, Index resultBuffer, Index speedBuffer) +{ + typedef math::BIAS_SCHEME SchemeT; + typedef typename SchemeT::template ISStencil::StencilType StencilT; + typedef typename LeafType::ValueOnCIter VoxelIterT; + typedef math::GradientNormSqrd NumGrad; + + static const ValueType Alpha = ValueType(Nominator)/ValueType(Denominator); + static const ValueType Beta = ValueType(1) - Alpha; + + mParent->mTracker.checkInterrupter(); + const MapT& map = *mMap; + StencilT stencil(mParent->mTracker.grid()); + + for (typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + const ValueType* speed = leafIter.buffer(speedBuffer).data(); + if (math::isExactlyEqual(speed[0], std::numeric_limits::max())) continue; + const ValueType* phi = leafIter.buffer(phiBuffer).data(); + ValueType* result = leafIter.buffer(resultBuffer).data(); + for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) { + const Index n = voxelIter.pos(); + if (math::isApproxZero(speed[n])) continue; + stencil.moveTo(voxelIter); + const ValueType v = stencil.getValue() - dt * speed[n] * NumGrad::result(map, stencil); + result[n] = Nominator ? Alpha * phi[n] + Beta * v : v; + }//loop over active voxels in the leaf of the mask + }//loop over leafs of the level set +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_LEVEL_SET_MORPH_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/LevelSetPlatonic.h b/nuparu/include/openvdb/include/openvdb/tools/LevelSetPlatonic.h new file mode 100644 index 00000000..09b1bc3b --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/LevelSetPlatonic.h @@ -0,0 +1,494 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 LevelSetPlatonic.h +/// +/// @brief Generate a narrow-band level sets of the five platonic solids. +/// +/// @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_LEVELSETPLATONIC_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_LEVELSETPLATONIC_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 platonic solid. +/// +/// @param faceCount number of faces of the platonic solid, i.e. 4, 6, 8, 12 or 20 +/// @param scale scale of the platonic solid in world units +/// @param center center of the platonic solid 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 +/// +/// @details Faces: TETRAHEDRON=4, CUBE=6, OCTAHEDRON=8, DODECAHEDRON=12, ICOSAHEDRON=20 +/// +/// @note @c GridType::ValueType must be a floating-point scalar. +template +typename GridType::Ptr +createLevelSetPlatonic(int faceCount,// 4, 6, 8, 12 or 20 + float scale = 1.0f, + const Vec3f& center = Vec3f(0.0f), + float voxelSize = 0.1f, + 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 platonic solid. +/// +/// @param faceCount number of faces of the platonic solid, i.e. 4, 6, 8, 12 or 20 +/// @param scale scale of the platonic solid in world units +/// @param center center of the platonic solid in world units +/// @param voxelSize voxel size in world units +/// @param halfWidth half the width of the narrow band, in voxel units +/// +/// @details Faces: TETRAHEDRON=4, CUBE=6, OCTAHEDRON=8, DODECAHEDRON=12, ICOSAHEDRON=20 +/// +/// @note @c GridType::ValueType must be a floating-point scalar. +template +typename GridType::Ptr +createLevelSetPlatonic(int faceCount,// 4, 6, 8, 12 or 20 + float scale = 1.0f, + const Vec3f& center = Vec3f(0.0f), + float voxelSize = 0.1f, + float halfWidth = float(LEVEL_SET_HALF_WIDTH)) +{ + return createLevelSetPlatonic(faceCount, + scale, + center, + voxelSize, + halfWidth); +} + +//////////////////////////////////////////////////////////////////////////////// + +/// @brief Return a grid of type @c GridType containing a narrow-band level set +/// representation of a tetrahedron. +/// +/// @param scale scale of the platonic solid in world units +/// @param center center of the platonic solid 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. +template +typename GridType::Ptr +createLevelSetTetrahedron(float scale = 1.0f, + const Vec3f& center = Vec3f(0.0f), + float voxelSize = 0.1f, + float halfWidth = float(LEVEL_SET_HALF_WIDTH), + InterruptT* interrupt = NULL) +{ + return createLevelSetPlatonic(4, scale, center, + voxelSize, halfWidth, interrupt); +} + +/// @brief Return a grid of type @c GridType containing a narrow-band level set +/// representation of a tetrahedron. +/// +/// @param scale scale of the platonic solid in world units +/// @param center center of the platonic solid 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. +template +typename GridType::Ptr +createLevelSetTetrahedron(float scale = 1.0f, + const Vec3f& center = Vec3f(0.0f), + float voxelSize = 0.1f, + float halfWidth = float(LEVEL_SET_HALF_WIDTH)) +{ + return createLevelSetPlatonic(4, scale, center, + voxelSize, halfWidth); +} + +//////////////////////////////////////////////////////////////////////////////// + +/// @brief Return a grid of type @c GridType containing a narrow-band level set +/// representation of a cube. +/// +/// @param scale scale of the platonic solid in world units +/// @param center center of the platonic solid 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. +template +typename GridType::Ptr +createLevelSetCube(float scale = 1.0f, + const Vec3f& center = Vec3f(0.0f), + float voxelSize = 0.1f, + float halfWidth = float(LEVEL_SET_HALF_WIDTH), + InterruptT* interrupt = NULL) +{ + return createLevelSetPlatonic(6, scale, center, + voxelSize, halfWidth, interrupt); +} + +/// @brief Return a grid of type @c GridType containing a narrow-band level set +/// representation of a cube. +/// +/// @param scale scale of the platonic solid in world units +/// @param center center of the platonic solid 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. +template +typename GridType::Ptr +createLevelSetCube(float scale = 1.0f, + const Vec3f& center = Vec3f(0.0f), + float voxelSize = 0.1f, + float halfWidth = float(LEVEL_SET_HALF_WIDTH)) +{ + return createLevelSetPlatonic(6, scale, center, + voxelSize, halfWidth); +} + +//////////////////////////////////////////////////////////////////////////////// + +/// @brief Return a grid of type @c GridType containing a narrow-band level set +/// representation of an octahedron. +/// +/// @param scale scale of the platonic solid in world units +/// @param center center of the platonic solid 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. +template +typename GridType::Ptr +createLevelSetOctahedron(float scale = 1.0f, + const Vec3f& center = Vec3f(0.0f), + float voxelSize = 0.1f, + float halfWidth = float(LEVEL_SET_HALF_WIDTH), + InterruptT* interrupt = NULL) +{ + return createLevelSetPlatonic(8, scale, center, + voxelSize, halfWidth, interrupt); +} + +/// @brief Return a grid of type @c GridType containing a narrow-band level set +/// representation of an octahedron. +/// +/// @param scale scale of the platonic solid in world units +/// @param center center of the platonic solid 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. +template +typename GridType::Ptr +createLevelSetOctahedron(float scale = 1.0f, + const Vec3f& center = Vec3f(0.0f), + float voxelSize = 0.1f, + float halfWidth = float(LEVEL_SET_HALF_WIDTH)) +{ + return createLevelSetPlatonic(8, scale, center, + voxelSize, halfWidth); +} + +//////////////////////////////////////////////////////////////////////////////// + +/// @brief Return a grid of type @c GridType containing a narrow-band level set +/// representation of a dodecahedron. +/// +/// @param scale scale of the platonic solid in world units +/// @param center center of the platonic solid 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. +template +typename GridType::Ptr +createLevelSetDodecahedron(float scale = 1.0f, + const Vec3f& center = Vec3f(0.0f), + float voxelSize = 0.1f, + float halfWidth = float(LEVEL_SET_HALF_WIDTH), + InterruptT* interrupt = NULL) +{ + return createLevelSetPlatonic(12, scale, center, + voxelSize, halfWidth, interrupt); +} + +/// @brief Return a grid of type @c GridType containing a narrow-band level set +/// representation of a dodecahedron. +/// +/// @param scale scale of the platonic solid in world units +/// @param center center of the platonic solid 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. +template +typename GridType::Ptr +createLevelSetDodecahedron(float scale = 1.0f, + const Vec3f& center = Vec3f(0.0f), + float voxelSize = 0.1f, + float halfWidth = float(LEVEL_SET_HALF_WIDTH)) +{ + return createLevelSetPlatonic(12, scale, center, + voxelSize, halfWidth); +} + +//////////////////////////////////////////////////////////////////////////////// + +/// @brief Return a grid of type @c GridType containing a narrow-band level set +/// representation of an icosahedron. +/// +/// @param scale scale of the platonic solid in world units +/// @param center center of the platonic solid 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. +template +typename GridType::Ptr +createLevelSetIcosahedron(float scale = 1.0f, + const Vec3f& center = Vec3f(0.0f), + float voxelSize = 0.1f, + float halfWidth = float(LEVEL_SET_HALF_WIDTH), + InterruptT* interrupt = NULL) +{ + return createLevelSetPlatonic(20, scale, center, + voxelSize, halfWidth, interrupt); +} + +/// @brief Return a grid of type @c GridType containing a narrow-band level set +/// representation of an icosahedron. +/// +/// @param scale scale of the platonic solid in world units +/// @param center center of the platonic solid 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. +template +typename GridType::Ptr +createLevelSetIcosahedron(float scale = 1.0f, + const Vec3f& center = Vec3f(0.0f), + float voxelSize = 0.1f, + float halfWidth = float(LEVEL_SET_HALF_WIDTH)) +{ + return createLevelSetPlatonic(20, scale, center, + voxelSize, halfWidth); +} + +//////////////////////////////////////////////////////////////////////////////// + +template +typename GridType::Ptr +createLevelSetPlatonic(int faceCount,float scale, const 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::value); + + const math::Transform::Ptr xform = math::Transform::createLinearTransform( voxelSize ); + + std::vector vtx; + std::vector tri; + std::vector qua; + + if (faceCount == 4) {// Tetrahedron + + vtx.push_back( Vec3f( 0.0f, 1.0f, 0.0f) ); + vtx.push_back( Vec3f(-0.942810297f, -0.333329707f, 0.0f) ); + vtx.push_back( Vec3f( 0.471405149f, -0.333329707f, 0.816497624) ); + vtx.push_back( Vec3f( 0.471405149f, -0.333329707f, -0.816497624f) ); + + tri.push_back( Vec3I(0, 2, 3) ); + tri.push_back( Vec3I(0, 3, 1) ); + tri.push_back( Vec3I(0, 1, 2) ); + tri.push_back( Vec3I(1, 3, 2) ); + + } else if (faceCount == 6) {// Cube + + vtx.push_back( Vec3f(-0.5f, -0.5f, -0.5f) ); + vtx.push_back( Vec3f( 0.5f, -0.5f, -0.5f) ); + vtx.push_back( Vec3f( 0.5f, -0.5f, 0.5f) ); + vtx.push_back( Vec3f(-0.5f, -0.5f, 0.5f) ); + vtx.push_back( Vec3f(-0.5f, 0.5f, -0.5f) ); + vtx.push_back( Vec3f( 0.5f, 0.5f, -0.5f) ); + vtx.push_back( Vec3f( 0.5f, 0.5f, 0.5f) ); + vtx.push_back( Vec3f(-0.5f, 0.5f, 0.5f) ); + + qua.push_back( Vec4I(1, 0, 4, 5) ); + qua.push_back( Vec4I(2, 1, 5, 6) ); + qua.push_back( Vec4I(3, 2, 6, 7) ); + qua.push_back( Vec4I(0, 3, 7, 4) ); + qua.push_back( Vec4I(2, 3, 0, 1) ); + qua.push_back( Vec4I(5, 4, 7, 6) ); + + } else if (faceCount == 8) {// Octahedron + + vtx.push_back( Vec3f( 0.0f, 0.0f, -1.0f) ); + vtx.push_back( Vec3f( 1.0f, 0.0f, 0.0f) ); + vtx.push_back( Vec3f( 0.0f, 0.0f, 1.0f) ); + vtx.push_back( Vec3f(-1.0f, 0.0f, 0.0f) ); + vtx.push_back( Vec3f( 0.0f,-1.0f, 0.0f) ); + vtx.push_back( Vec3f( 0.0f, 1.0f, 0.0f) ); + + tri.push_back( Vec3I(0, 4, 3) ); + tri.push_back( Vec3I(0, 1, 4) ); + tri.push_back( Vec3I(1, 2, 4) ); + tri.push_back( Vec3I(2, 3, 4) ); + tri.push_back( Vec3I(0, 3, 5) ); + tri.push_back( Vec3I(0, 5, 1) ); + tri.push_back( Vec3I(1, 5, 2) ); + tri.push_back( Vec3I(2, 5, 3) ); + + } else if (faceCount == 12) {// Dodecahedron + + vtx.push_back( Vec3f( 0.354437858f, 0.487842113f, -0.789344311f) ); + vtx.push_back( Vec3f( 0.573492587f, -0.186338872f, -0.78934437f) ); + vtx.push_back( Vec3f( 0.0f, -0.603005826f, -0.78934443f) ); + vtx.push_back( Vec3f(-0.573492587f, -0.186338872f, -0.78934437f) ); + vtx.push_back( Vec3f(-0.354437858f, 0.487842113f, -0.789344311f) ); + vtx.push_back( Vec3f(-0.573492587f, 0.789345026f, -0.186338797f) ); + vtx.push_back( Vec3f(-0.927930415f, -0.301502913f, -0.186338872f) ); + vtx.push_back( Vec3f( 0.0f, -0.975683928f, -0.186338902f) ); + vtx.push_back( Vec3f( 0.927930415f, -0.301502913f, -0.186338872f) ); + vtx.push_back( Vec3f( 0.573492587f, 0.789345026f, -0.186338797f) ); + vtx.push_back( Vec3f( 0.0f, 0.975683868f, 0.186338902f) ); + vtx.push_back( Vec3f(-0.927930415f, 0.301502913f, 0.186338872f) ); + vtx.push_back( Vec3f(-0.573492587f, -0.789345026f, 0.186338797f) ); + vtx.push_back( Vec3f( 0.573492587f, -0.789345026f, 0.186338797f) ); + vtx.push_back( Vec3f( 0.927930415f, 0.301502913f, 0.186338872f) ); + vtx.push_back( Vec3f( 0.0f, 0.603005826f, 0.78934443f) ); + vtx.push_back( Vec3f( 0.573492587f, 0.186338872f, 0.78934437f) ); + vtx.push_back( Vec3f( 0.354437858f, -0.487842113f, 0.789344311f) ); + vtx.push_back( Vec3f(-0.354437858f, -0.487842113f, 0.789344311f) ); + vtx.push_back( Vec3f(-0.573492587f, 0.186338872f, 0.78934437f) ); + + qua.push_back( Vec4I(0, 1, 2, 3) ); + tri.push_back( Vec3I(0, 3, 4) ); + qua.push_back( Vec4I(0, 4, 5, 10) ); + tri.push_back( Vec3I(0, 10, 9) ); + qua.push_back( Vec4I(0, 9, 14, 8) ); + tri.push_back( Vec3I(0, 8, 1) ); + qua.push_back( Vec4I(1, 8, 13, 7) ); + tri.push_back( Vec3I(1, 7, 2) ); + qua.push_back( Vec4I(2, 7, 12, 6) ); + tri.push_back( Vec3I(2, 6, 3) ); + qua.push_back( Vec4I(3, 6, 11, 5) ); + tri.push_back( Vec3I(3, 5, 4) ); + qua.push_back( Vec4I(5, 11, 19, 15) ); + tri.push_back( Vec3I(5, 15, 10) ); + qua.push_back( Vec4I(6, 12, 18, 19) ); + tri.push_back( Vec3I(6, 19, 11) ); + qua.push_back( Vec4I(7, 13, 17, 18) ); + tri.push_back( Vec3I(7, 18, 12) ); + qua.push_back( Vec4I(8, 14, 16, 17) ); + tri.push_back( Vec3I(8, 17, 13) ); + qua.push_back( Vec4I(9, 10, 15, 16) ); + tri.push_back( Vec3I(9, 16, 14) ); + qua.push_back( Vec4I(15, 19, 18, 17) ); + tri.push_back( Vec3I(15, 17, 16) ); + + } else if (faceCount == 20) {// Icosahedron + + vtx.push_back( Vec3f(0.0f, 0.0f, -1.0f) ); + vtx.push_back( Vec3f(0.0f, 0.894427359f, -0.447213143f) ); + vtx.push_back( Vec3f(0.850650847f, 0.276393682f, -0.447213203f) ); + vtx.push_back( Vec3f(0.525731206f, -0.723606944f, -0.447213262f) ); + vtx.push_back( Vec3f(-0.525731206f, -0.723606944f, -0.447213262f) ); + vtx.push_back( Vec3f(-0.850650847f, 0.276393682f, -0.447213203f) ); + vtx.push_back( Vec3f(-0.525731206f, 0.723606944f, 0.447213262f) ); + vtx.push_back( Vec3f(-0.850650847f, -0.276393682f, 0.447213203f) ); + vtx.push_back( Vec3f(0.0f, -0.894427359f, 0.447213143f) ); + vtx.push_back( Vec3f(0.850650847f, -0.276393682f, 0.447213203f) ); + vtx.push_back( Vec3f(0.525731206f, 0.723606944f, 0.447213262f) ); + vtx.push_back( Vec3f(0.0f, 0.0f, 1.0f) ); + + tri.push_back( Vec3I( 2, 0, 1) ); + tri.push_back( Vec3I( 3, 0, 2) ); + tri.push_back( Vec3I( 4, 0, 3) ); + tri.push_back( Vec3I( 5, 0, 4) ); + tri.push_back( Vec3I( 1, 0, 5) ); + tri.push_back( Vec3I( 6, 1, 5) ); + tri.push_back( Vec3I( 7, 5, 4) ); + tri.push_back( Vec3I( 8, 4, 3) ); + tri.push_back( Vec3I( 9, 3, 2) ); + tri.push_back( Vec3I(10, 2, 1) ); + tri.push_back( Vec3I(10, 1, 6) ); + tri.push_back( Vec3I( 6, 5, 7) ); + tri.push_back( Vec3I( 7, 4, 8) ); + tri.push_back( Vec3I( 8, 3, 9) ); + tri.push_back( Vec3I( 9, 2, 10) ); + tri.push_back( Vec3I( 6, 11, 10) ); + tri.push_back( Vec3I(10, 11, 9) ); + tri.push_back( Vec3I( 9, 11, 8) ); + tri.push_back( Vec3I( 8, 11, 7) ); + tri.push_back( Vec3I( 7, 11, 6) ); + + } else { + OPENVDB_THROW(RuntimeError, "Invalid face count"); + } + + // Apply scale and translation to all the vertices + for ( size_t i = 0; i( *xform, vtx, tri, qua, halfWidth ); +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_LEVELSETPLATONIC_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/LevelSetRebuild.h b/nuparu/include/openvdb/include/openvdb/tools/LevelSetRebuild.h new file mode 100644 index 00000000..9b1c7ddc --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/LevelSetRebuild.h @@ -0,0 +1,353 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +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 +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 +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 +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& pointsOut, + const math::Transform& xform) + : mPointsIn(pointsIn) + , mPointsOut(&pointsOut) + , mXform(xform) + { + } + + void runParallel() + { + tbb::parallel_for(tbb::blocked_range(0, mPointsOut->size()), *this); + } + + void runSerial() + { + (*this)(tbb::blocked_range(0, mPointsOut->size())); + } + + inline void operator()(const tbb::blocked_range& range) const + { + for (size_t n = range.begin(); n < range.end(); ++n) { + (*mPointsOut)[n] = Vec3s(mXform.worldToIndex(mPointsIn[n])); + } + } + +private: + const PointList& mPointsIn; + std::vector * const mPointsOut; + const math::Transform& mXform; +}; + + +class PrimCpy +{ +public: + PrimCpy(const PolygonPoolList& primsIn, const std::vector& indexList, + std::vector& primsOut) + : mPrimsIn(primsIn) + , mIndexList(indexList) + , mPrimsOut(&primsOut) + { + } + + void runParallel() + { + tbb::parallel_for(tbb::blocked_range(0, mIndexList.size()), *this); + } + + void runSerial() + { + (*this)(tbb::blocked_range(0, mIndexList.size())); + } + + inline void operator()(const tbb::blocked_range& range) const + { + openvdb::Vec4I quad; + quad[3] = openvdb::util::INVALID_IDX; + std::vector& 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& mIndexList; + std::vector * 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 +inline typename boost::enable_if, +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); + mesher(grid); + + math::Transform::Ptr transform = (xform != NULL) ? xform->copy() : grid.transform().copy(); + + std::vector 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 primitives; + + { // Copy primitives. + PolygonPoolList& polygonPoolList = mesher.polygonPoolList(); + + size_t numPrimitives = 0; + std::vector 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(); + } + + QuadAndTriangleDataAdapter mesh(points, primitives); + + if (interrupter) { + return meshToVolume(*interrupter, mesh, *transform, exBandWidth, inBandWidth, + DISABLE_RENORMALIZATION, NULL); + } + + return meshToVolume(mesh, *transform, exBandWidth, inBandWidth, + DISABLE_RENORMALIZATION, NULL); +} + + +/// @internal This overload is enabled only for grids that do not have a scalar, +/// floating-point ValueType. +template +inline typename boost::disable_if, +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 +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 +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( + grid, isovalue, exBandWidth, inBandWidth, xform, NULL); +} + + +template +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( + grid, isovalue, halfWidth, halfWidth, xform, NULL); +} + + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_LEVELSETREBUILD_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/LevelSetSphere.h b/nuparu/include/openvdb/include/openvdb/tools/LevelSetSphere.h new file mode 100644 index 00000000..2b87d1a9 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/LevelSetSphere.h @@ -0,0 +1,222 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include +#include +#include +#include +#include +#include "SignedFloodFill.h" + +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::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::Ptr +createLevelSetSphere(float radius, const openvdb::Vec3f& center, float voxelSize, + float halfWidth = float(LEVEL_SET_HALF_WIDTH)) +{ + return createLevelSetSphere(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 +class LevelSetSphere +{ +public: + typedef typename GridT::ValueType ValueT; + typedef typename math::Vec3 Vec3T; + BOOST_STATIC_ASSERT(boost::is_floating_point::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 than + /// 1.5*voxelSize, i.e. the sphere is smaller than 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(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 a 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 consistent signed distances outside the narrow-band + tools::signedFloodFill(mGrid->tree()); + + if (mInterrupt) mInterrupt->end(); + } + + const ValueT mRadius; + const Vec3T mCenter; + InterruptT* mInterrupt; + typename GridT::Ptr mGrid; +};// LevelSetSphere + + +//////////////////////////////////////// + + +template +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::value); + + typedef typename GridType::ValueType ValueT; + LevelSetSphere 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-2016 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/nuparu/include/openvdb/include/openvdb/tools/LevelSetTracker.h b/nuparu/include/openvdb/include/openvdb/tools/LevelSetTracker.h new file mode 100644 index 00000000..1d4acd8a --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/LevelSetTracker.h @@ -0,0 +1,650 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ChangeBackground.h"// for changeLevelSetBackground +#include "Morphology.h"//for dilateActiveValues +#include "Prune.h"// for pruneLevelSet + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Performs multi-threaded interface tracking of narrow band level sets +template +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 LeafManagerType; // leafs + buffers + typedef typename LeafManagerType::LeafRange LeafRange; + typedef typename LeafManagerType::BufferType BufferType; + typedef typename TreeType::template ValueConverter::Type MaskTreeType; + BOOST_STATIC_ASSERT(boost::is_floating_point::value); + + /// Lightweight struct that stores the state of the LevelSetTracker + struct State { + State(math::BiasedGradientScheme s = math::HJWENO5_BIAS, + math::TemporalIntegrationScheme t = math::TVD_RK1, + int n = static_cast(LEVEL_SET_HALF_WIDTH), int g = 1) + : spatialScheme(s), temporalScheme(t), normCount(n), grainSize(g) {} + math::BiasedGradientScheme spatialScheme; + math::TemporalIntegrationScheme temporalScheme; + int normCount;// Number of iterations of normalization + int grainSize; + }; + + /// @brief Main constructor + /// @throw RuntimeError if the grid is not a level set + LevelSetTracker(GridT& grid, InterruptT* interrupt = NULL); + + virtual ~LevelSetTracker() { delete mLeafs; } + + /// @brief Iterative normalization, i.e. solving the Eikonal equation + /// @note The mask it optional and by default it is ignored. + template + void normalize(const MaskType* mask); + + /// @brief Iterative normalization, i.e. solving the Eikonal equation + void normalize() { this->normalize(NULL); } + + /// @brief Track the level set interface, i.e. rebuild and normalize the + /// narrow band of the level set. + void track(); + + /// @brief Remove voxels that are outside the narrow band. (substep of track) + void prune(); + + /// @brief Fast but approximate dilation of the narrow band - one + /// layer at a time. Normally we recommend using the resize method below + /// which internally calls dilate (or erode) with the correct + /// number of @a iterations to achieve the desired half voxel width + /// of the narrow band (3 is recomended for most level set applications). + /// + /// @note Since many level set applications perform + /// interface-tracking, which in turn rebuilds the narrow-band + /// accurately, this dilate method can often be used with a + /// single iterations of low-order re-normalization. This + /// effectively allows very narrow bands to be created from points + /// or polygons (e.g. with a half voxel width of 1), followed by a + /// fast but approximate dilation (typically with a half voxel + /// width of 3). This can be significantly faster than generating + /// the final width of the narrow band from points or polygons. + void dilate(int iterations = 1); + + /// @brief Erodes the width of the narrow-band and update the background values + /// @throw ValueError if @a iterations is larger than the current half-width. + void erode(int iterations = 1); + + /// @brief Resize the width of the narrow band, i.e. perform + /// dilation and renormalization or erosion as required. + bool resize(Index halfWidth = static_cast(LEVEL_SET_HALF_WIDTH)); + + /// @brief Return the half width of the narrow band in floating-point voxel units. + ValueType getHalfWidth() const { return mGrid->background()/mDx; } + + /// @brief Return the state of the tracker (see struct defined above) + State getState() const { return mState; } + + /// @brief Set the state of the tracker (see struct defined above) + void setState(const State& s) { mState =s; } + + /// @return the spatial finite difference scheme + math::BiasedGradientScheme getSpatialScheme() const { return mState.spatialScheme; } + + /// @brief Set the spatial finite difference scheme + void setSpatialScheme(math::BiasedGradientScheme scheme) { mState.spatialScheme = scheme; } + + /// @return the temporal integration scheme + math::TemporalIntegrationScheme getTemporalScheme() const { return mState.temporalScheme; } + + /// @brief Set the spatial finite difference scheme + void setTemporalScheme(math::TemporalIntegrationScheme scheme) { mState.temporalScheme = scheme;} + + /// @return The number of normalizations performed per track or + /// normalize call. + int getNormCount() const { return mState.normCount; } + + /// @brief Set the number of normalizations performed per track or + /// normalize call. + void setNormCount(int n) { mState.normCount = n; } + + /// @return the grain-size used for multi-threading + int getGrainSize() const { return mState.grainSize; } + + /// @brief Set the grain-size used for multi-threading. + /// @note A grainsize of 0 or less disables multi-threading! + void setGrainSize(int grainsize) { mState.grainSize = 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; } + + LeafManagerType& leafs() { return *mLeafs; } + + const LeafManagerType& leafs() const { return *mLeafs; } + +private: + + // disallow copy construction and copy by assignment! + LevelSetTracker(const LevelSetTracker&);// not implemented + LevelSetTracker& operator=(const LevelSetTracker&);// not implemented + + // Private class to perform multi-threaded trimming of + // voxels that are too far away from the zero-crossing. + struct Trim + { + Trim(LevelSetTracker& tracker) : mTracker(tracker) {} + void trim(); + void operator()(const LeafRange& r) const; + LevelSetTracker& mTracker; + };// Trim + + // Private struct to perform multi-threaded normalization + template + struct Normalizer + { + typedef math::BIAS_SCHEME SchemeT; + typedef typename SchemeT::template ISStencil::StencilType StencilT; + typedef typename MaskT::LeafNodeType MaskLeafT; + typedef typename MaskLeafT::ValueOnCIter MaskIterT; + typedef typename LeafType::ValueOnCIter VoxelIterT; + Normalizer(LevelSetTracker& tracker, const MaskT* mask); + void normalize(); + void operator()(const LeafRange& r) const {mTask(const_cast(this), r);} + void cook(const char* msg, int swapBuffer=0); + template + void euler(const LeafRange& range, Index phiBuffer, Index resultBuffer); + inline void euler01(const LeafRange& r) {this->euler<0,1>(r, 0, 1);} + inline void euler12(const LeafRange& r) {this->euler<1,2>(r, 1, 1);} + inline void euler34(const LeafRange& r) {this->euler<3,4>(r, 1, 2);} + inline void euler13(const LeafRange& r) {this->euler<1,3>(r, 1, 2);} + template + void eval(StencilT& stencil, const ValueType* phi, ValueType* result, Index n) const; + LevelSetTracker& mTracker; + const MaskT* mMask; + const ValueType mDt, mInvDx; + typename boost::function mTask; + }; // Normalizer struct + + template + void normalize1(const MaskT* mask); + + template + void normalize2(const MaskT* mask); + + // 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! + GridType* mGrid; + LeafManagerType* mLeafs; + InterruptT* mInterrupter; + const ValueType mDx; + State mState; +}; // end of LevelSetTracker class + +template +LevelSetTracker:: +LevelSetTracker(GridT& grid, InterruptT* interrupt): + mGrid(&grid), + mLeafs(new LeafManagerType(grid.tree())), + mInterrupter(interrupt), + mDx(static_cast(grid.voxelSize()[0])), + mState() +{ + 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 expected a level set, got a grid of class \"" + + grid.gridClassToString(grid.getGridClass()) + + "\" [hint: Grid::setGridClass(openvdb::GRID_LEVEL_SET)]"); + } +} + +template +inline void +LevelSetTracker:: +prune() +{ + this->startInterrupter("Pruning Level Set"); + + // Prune voxels that are too far away from the zero-crossing + Trim t(*this); + t.trim(); + + // Remove inactive nodes from tree + tools::pruneLevelSet(mGrid->tree()); + + // The tree topology has changes so rebuild the list of leafs + mLeafs->rebuildLeafArray(); + this->endInterrupter(); +} + +template +inline void +LevelSetTracker:: +track() +{ + // Dilate narrow-band (this also rebuilds the leaf array!) + tools::dilateActiveValues( *mLeafs, 1, tools::NN_FACE, tools::IGNORE_TILES); + + // Compute signed distances in dilated narrow-band + this->normalize(); + + // Remove voxels that are outside the narrow band + this->prune(); +} + +template +inline void +LevelSetTracker:: +dilate(int iterations) +{ + if (this->getNormCount() == 0) { + for (int i=0; i < iterations; ++i) { + tools::dilateActiveValues( *mLeafs, 1, tools::NN_FACE, tools::IGNORE_TILES); + tools::changeLevelSetBackground(this->leafs(), mDx + mGrid->background()); + } + } else { + for (int i=0; i < iterations; ++i) { + MaskTreeType mask0(mGrid->tree(), false, TopologyCopy()); + tools::dilateActiveValues( *mLeafs, 1, tools::NN_FACE, tools::IGNORE_TILES); + tools::changeLevelSetBackground(this->leafs(), mDx + mGrid->background()); + MaskTreeType mask(mGrid->tree(), false, TopologyCopy()); + mask.topologyDifference(mask0); + this->normalize(&mask); + } + } +} + +template +inline void +LevelSetTracker:: +erode(int iterations) +{ + tools::erodeVoxels(*mLeafs, iterations); + mLeafs->rebuildLeafArray(); + const ValueType background = mGrid->background() - iterations*mDx; + tools::changeLevelSetBackground(this->leafs(), background); +} + +template +inline bool +LevelSetTracker:: +resize(Index halfWidth) +{ + const int wOld = static_cast(math::RoundDown(this->getHalfWidth())); + const int wNew = static_cast(halfWidth); + if (wOld < wNew) { + this->dilate(wNew - wOld); + } else if (wOld > wNew) { + this->erode(wOld - wNew); + } + return wOld != wNew; +} + +template +inline void +LevelSetTracker:: +startInterrupter(const char* msg) +{ + if (mInterrupter) mInterrupter->start(msg); +} + +template +inline void +LevelSetTracker:: +endInterrupter() +{ + if (mInterrupter) mInterrupter->end(); +} + +template +inline bool +LevelSetTracker:: +checkInterrupter() +{ + if (util::wasInterrupted(mInterrupter)) { + tbb::task::self().cancel_group_execution(); + return false; + } + return true; +} + +template +template +inline void +LevelSetTracker:: +normalize(const MaskT* mask) +{ + switch (this->getSpatialScheme()) { + case math::FIRST_BIAS: + this->normalize1(mask); break; + case math::SECOND_BIAS: + this->normalize1(mask); break; + case math::THIRD_BIAS: + this->normalize1(mask); break; + case math::WENO5_BIAS: + this->normalize1(mask); break; + case math::HJWENO5_BIAS: + this->normalize1(mask); break; + default: + OPENVDB_THROW(ValueError, "Spatial difference scheme not supported!"); + } +} + +template +template +inline void +LevelSetTracker:: +normalize1(const MaskT* mask) +{ + switch (this->getTemporalScheme()) { + case math::TVD_RK1: + this->normalize2(mask); break; + case math::TVD_RK2: + this->normalize2(mask); break; + case math::TVD_RK3: + this->normalize2(mask); break; + default: + OPENVDB_THROW(ValueError, "Temporal integration scheme not supported!"); + } +} + +template +template +inline void +LevelSetTracker:: +normalize2(const MaskT* mask) +{ + Normalizer tmp(*this, mask); + tmp.normalize(); +} + +//////////////////////////////////////////////////////////////////////////// + +template +inline void +LevelSetTracker:: +Trim::trim() +{ + const int grainSize = mTracker.getGrainSize(); + const LeafRange range = mTracker.leafs().leafRange(grainSize); + + if (grainSize>0) { + tbb::parallel_for(range, *this); + } else { + (*this)(range); + } +} + +/// Prunes away voxels that have moved outside the narrow band +template +inline void +LevelSetTracker:: +Trim::operator()(const LeafRange& range) const +{ + typedef typename LeafType::ValueOnIter VoxelIterT; + mTracker.checkInterrupter(); + const ValueType gamma = mTracker.mGrid->background(); + + for (typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + LeafType &leaf = *leafIter; + 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 +template +inline +LevelSetTracker:: +Normalizer:: +Normalizer(LevelSetTracker& tracker, const MaskT* mask) + : mTracker(tracker) + , mMask(mask) + , mDt(tracker.voxelSize()*(TemporalScheme == math::TVD_RK1 ? 0.3f : + TemporalScheme == math::TVD_RK2 ? 0.9f : 1.0f)) + , mInvDx(1.0f/tracker.voxelSize()) + , mTask(0) +{ +} + +template +template +inline void +LevelSetTracker:: +Normalizer:: +normalize() +{ + /// Make sure we have enough temporal auxiliary buffers + mTracker.mLeafs->rebuildAuxBuffers(TemporalScheme == math::TVD_RK3 ? 2 : 1); + + for (int n=0, e=mTracker.getNormCount(); n < e; ++n) { + + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + switch(TemporalScheme) {//switch is resolved at compile-time + case math::TVD_RK1: + // Perform one explicit Euler step: t1 = t0 + dt + // Phi_t1(0) = Phi_t0(0) - dt * VdotG_t0(1) + mTask = boost::bind(&Normalizer::euler01, _1, _2); + + // Cook and swap buffer 0 and 1 such that Phi_t1(0) and Phi_t0(1) + this->cook("Normalizing level set using TVD_RK1", 1); + break; + case math::TVD_RK2: + // Perform one explicit Euler step: t1 = t0 + dt + // Phi_t1(1) = Phi_t0(0) - dt * VdotG_t0(1) + mTask = boost::bind(&Normalizer::euler01, _1, _2); + + // Cook and swap buffer 0 and 1 such that Phi_t1(0) and Phi_t0(1) + this->cook("Normalizing level set using TVD_RK1 (step 1 of 2)", 1); + + // Convex combine explicit 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::euler12, _1, _2); + + // Cook and swap buffer 0 and 1 such that Phi_t2(0) and Phi_t1(1) + this->cook("Normalizing level set using TVD_RK1 (step 2 of 2)", 1); + break; + case math::TVD_RK3: + // Perform one explicit Euler step: t1 = t0 + dt + // Phi_t1(1) = Phi_t0(0) - dt * VdotG_t0(1) + mTask = boost::bind(&Normalizer::euler01, _1, _2); + + // Cook and swap buffer 0 and 1 such that Phi_t1(0) and Phi_t0(1) + this->cook("Normalizing level set using TVD_RK3 (step 1 of 3)", 1); + + // Convex combine explicit 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::euler34, _1, _2); + + // Cook and swap buffer 0 and 2 such that Phi_t2(0) and Phi_t1(2) + this->cook("Normalizing level set using TVD_RK3 (step 2 of 3)", 2); + + // Convex combine explicit 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::euler13, _1, _2); + + // Cook and swap buffer 0 and 2 such that Phi_t3(0) and Phi_t2(2) + this->cook("Normalizing level set using TVD_RK3 (step 3 of 3)", 2); + break; + default: + OPENVDB_THROW(ValueError, "Temporal integration scheme not supported!"); + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + mTracker.mLeafs->removeAuxBuffers(); +} + +/// Private method to perform the task (serial or threaded) and +/// subsequently swap the leaf buffers. +template +template +inline void +LevelSetTracker:: +Normalizer:: +cook(const char* msg, int swapBuffer) +{ + mTracker.startInterrupter( msg ); + + const int grainSize = mTracker.getGrainSize(); + const LeafRange range = mTracker.leafs().leafRange(grainSize); + + grainSize>0 ? tbb::parallel_for(range, *this) : (*this)(range); + + mTracker.leafs().swapLeafBuffer(swapBuffer, grainSize==0); + + mTracker.endInterrupter(); +} + +template +template +template +inline void +LevelSetTracker:: +Normalizer:: +eval(StencilT& stencil, const ValueType* phi, ValueType* result, Index n) const +{ + typedef typename math::ISGradientNormSqrd GradientT; + static const ValueType alpha = ValueType(Nominator)/ValueType(Denominator); + static const ValueType beta = ValueType(1) - alpha; + + const ValueType normSqGradPhi = GradientT::result(stencil); + const ValueType phi0 = stencil.getValue(); + ValueType v = phi0 / ( math::Sqrt(math::Pow2(phi0) + normSqGradPhi) + + math::Tolerance::value() ); + v = phi0 - mDt * v * (math::Sqrt(normSqGradPhi) * mInvDx - 1.0f); + result[n] = Nominator ? alpha * phi[n] + beta * v : v; +} + +template +template +template +inline void +LevelSetTracker:: +Normalizer:: +euler(const LeafRange& range, Index phiBuffer, Index resultBuffer) +{ + typedef typename LeafType::ValueOnCIter VoxelIterT; + + mTracker.checkInterrupter(); + + StencilT stencil(mTracker.grid()); + + for (typename LeafRange::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + const ValueType* phi = leafIter.buffer(phiBuffer).data(); + ValueType* result = leafIter.buffer(resultBuffer).data(); + if (mMask == NULL) { + for (VoxelIterT iter = leafIter->cbeginValueOn(); iter; ++iter) { + stencil.moveTo(iter); + this->eval(stencil, phi, result, iter.pos()); + }//loop over active voxels in the leaf of the level set + } else if (const MaskLeafT* mask = mMask->probeLeaf(leafIter->origin())) { + const ValueType* phi0 = leafIter->buffer().data(); + for (MaskIterT iter = mask->cbeginValueOn(); iter; ++iter) { + const Index i = iter.pos(); + stencil.moveTo(iter.getCoord(), phi0[i]); + this->eval(stencil, phi, result, i); + }//loop over active voxels in the leaf of the mask + } + }//loop over leafs of the level set +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_LEVEL_SET_TRACKER_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/LevelSetUtil.h b/nuparu/include/openvdb/include/openvdb/tools/LevelSetUtil.h new file mode 100644 index 00000000..97a91b87 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/LevelSetUtil.h @@ -0,0 +1,2554 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 utility methods that operate primarily +/// or exclusively on level set grids. +/// +/// @author Mihai Alden + + +#ifndef OPENVDB_TOOLS_LEVEL_SET_UTIL_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_LEVEL_SET_UTIL_HAS_BEEN_INCLUDED + +#include "MeshToVolume.h" // for traceExteriorBoundaries +#include "SignedFloodFill.h" // for signedFloodFillWithValues + +#include +#include + +#include +#include +#include +#include + +#include + +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 +inline typename GridType::ValueType lsutilGridMax() +{ + return std::numeric_limits::max(); +} + +template +inline typename GridType::ValueType lsutilGridZero() +{ + return zeroVal(); +} + +} // 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 +inline void +sdfToFogVolume( + GridType& grid, + typename GridType::ValueType cutoffDistance = lsutilGridMax()); + + +/// @brief Threaded method to construct a boolean mask that represents interior regions +/// in a signed distance field. +/// +/// @return A shared pointer to either a boolean grid or tree with the same tree +/// configuration and potentially transform as the input @c volume and whose active +/// and @c true values correspond to the interior of the input signed distance field. +/// +/// @param volume Signed distance field / level set volume. +/// @param isovalue Threshold below which values are considered part of the +/// interior region. +template +inline typename GridOrTreeType::template ValueConverter::Type::Ptr +sdfInteriorMask( + const GridOrTreeType& volume, + typename GridOrTreeType::ValueType isovalue = lsutilGridZero()); + + +/// @brief Extracts the interior regions of a signed distance field and topologically enclosed +/// (watertight) regions of value greater than the @a isovalue (cavities) that can arise +/// as the result of CSG union operations between different shapes where at least one of +/// the shapes has a concavity that is capped. +/// +/// For example the enclosed region of a capped bottle would include the walls and +/// the interior cavity. +/// +/// @return A shared pointer to either a boolean grid or tree with the same tree configuration +/// and potentially transform as the input @c volume and whose active and @c true values +/// correspond to the interior and enclosed regions in the input signed distance field. +/// +/// @param volume Signed distance field / level set volume. +/// @param isovalue Threshold below which values are considered part of the interior region. +/// @param fillMask Optional boolean tree, when provided enclosed cavity regions that are not +/// completely filled by this mask are ignored. +/// +/// For instance if the fill mask does not completely fill the bottle in the +/// previous example only the walls and cap are returned and the interior +/// cavity will be ignored. +template +inline typename GridOrTreeType::template ValueConverter::Type::Ptr +extractEnclosedRegion(const GridOrTreeType& volume, + typename GridOrTreeType::ValueType isovalue = lsutilGridZero(), + const typename TreeAdapter::TreeType::template ValueConverter::Type* fillMask = NULL); + + +/// @brief Return a mask of the voxels that intersect the implicit surface with the given @a isovalue. +/// +/// @param volume Signed distance field / level set volume. +/// @param isovalue The crossing point that is considered the surface. +template +inline typename GridOrTreeType::template ValueConverter::Type::Ptr +extractIsosurfaceMask(const GridOrTreeType& volume, typename GridOrTreeType::ValueType isovalue); + + +/// @brief Return a mask for each connected component of the given grid's active voxels. +/// +/// @param volume Input grid or tree +/// @param masks Output set of disjoint active topology masks sorted in descending order +/// based on the active voxel count. +template +inline void +extractActiveVoxelSegmentMasks(const GridOrTreeType& volume, + std::vector::Type::Ptr>& masks); + + +/// @brief Separates disjoint active topology components into distinct grids or trees. +/// +/// @details Supports volumes with active tiles. +/// +/// @param volume Input grid or tree +/// @param segments Output set of disjoint active topology components sorted in +/// descending order based on the active voxel count. +template +inline void +segmentActiveVoxels(const GridOrTreeType& volume, std::vector& segments); + + +/// @brief Separates disjoint SDF surfaces into distinct grids or trees. +/// +/// @details Supports asymmetric interior / exterior narrowband widths and +/// SDF volumes with dense interior regions. +/// +/// @param volume Input signed distance field / level set volume +/// @param segments Output set of disjoint SDF surfaces found in @a volume sorted in +/// descending order based on the surface intersecting voxel count. +template +inline void +segmentSDF(const GridOrTreeType& volume, std::vector& segments); + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + +// Internal utility objects and implementation details + + +namespace level_set_util_internal { + + +template +struct MaskInteriorVoxels { + + typedef typename LeafNodeType::ValueType ValueType; + typedef tree::LeafNode BoolLeafNodeType; + + MaskInteriorVoxels( + ValueType isovalue, const LeafNodeType ** nodes, BoolLeafNodeType ** maskNodes) + : mNodes(nodes), mMaskNodes(maskNodes), mIsovalue(isovalue) + { + } + + void operator()(const tbb::blocked_range& range) const { + + BoolLeafNodeType * maskNodePt = NULL; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + mMaskNodes[n] = NULL; + const LeafNodeType& node = *mNodes[n]; + + if (!maskNodePt) { + maskNodePt = new BoolLeafNodeType(node.origin(), false); + } else { + maskNodePt->setOrigin(node.origin()); + } + + const ValueType* values = &node.getValue(0); + for (Index i = 0; i < LeafNodeType::SIZE; ++i) { + if (values[i] < mIsovalue) maskNodePt->setValueOn(i, true); + } + + if (maskNodePt->onVoxelCount() > 0) { + mMaskNodes[n] = maskNodePt; + maskNodePt = NULL; + } + } + + if (maskNodePt) delete maskNodePt; + } + + LeafNodeType const * const * const mNodes; + BoolLeafNodeType ** const mMaskNodes; + ValueType const mIsovalue; +}; // MaskInteriorVoxels + + +template +struct MaskInteriorTiles { + + typedef typename TreeType::ValueType ValueType; + + MaskInteriorTiles(ValueType isovalue, const TreeType& tree, InternalNodeType ** maskNodes) + : mTree(&tree), mMaskNodes(maskNodes), mIsovalue(isovalue) { } + + void operator()(const tbb::blocked_range& range) const { + tree::ValueAccessor acc(*mTree); + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + typename InternalNodeType::ValueAllIter it = mMaskNodes[n]->beginValueAll(); + for (; it; ++it) { + if (acc.getValue(it.getCoord()) < mIsovalue) { + it.setValue(true); + it.setValueOn(true); + } + } + } + } + + TreeType const * const mTree; + InternalNodeType ** const mMaskNodes; + ValueType const mIsovalue; +}; // MaskInteriorTiles + + +template +struct PopulateTree { + + typedef typename TreeType::ValueType ValueType; + typedef typename TreeType::LeafNodeType LeafNodeType; + + PopulateTree(TreeType& tree, LeafNodeType** leafnodes, + const size_t * nodexIndexMap, ValueType background) + : mNewTree(background) + , mTreePt(&tree) + , mNodes(leafnodes) + , mNodeIndexMap(nodexIndexMap) + { + } + + PopulateTree(PopulateTree& rhs, tbb::split) + : mNewTree(rhs.mNewTree.background()) + , mTreePt(&mNewTree) + , mNodes(rhs.mNodes) + , mNodeIndexMap(rhs.mNodeIndexMap) + { + } + + void operator()(const tbb::blocked_range& range) { + + tree::ValueAccessor acc(*mTreePt); + + if (mNodeIndexMap) { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + for (size_t i = mNodeIndexMap[n], I = mNodeIndexMap[n + 1]; i < I; ++i) { + if (mNodes[i] != NULL) acc.addLeaf(mNodes[i]); + } + } + } else { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + acc.addLeaf(mNodes[n]); + } + } + } + + void join(PopulateTree& rhs) { mTreePt->merge(*rhs.mTreePt); } + +private: + TreeType mNewTree; + TreeType * const mTreePt; + LeafNodeType ** const mNodes; + size_t const * const mNodeIndexMap; +}; // PopulateTree + + +/// @brief Negative active values are set @c 0, everything else is set to @c 1. +template +struct LabelBoundaryVoxels { + + typedef typename LeafNodeType::ValueType ValueType; + typedef tree::LeafNode CharLeafNodeType; + + LabelBoundaryVoxels( + ValueType isovalue, const LeafNodeType ** nodes, CharLeafNodeType ** maskNodes) + : mNodes(nodes), mMaskNodes(maskNodes), mIsovalue(isovalue) + { + } + + void operator()(const tbb::blocked_range& range) const { + + CharLeafNodeType * maskNodePt = NULL; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + mMaskNodes[n] = NULL; + const LeafNodeType& node = *mNodes[n]; + + if (!maskNodePt) { + maskNodePt = new CharLeafNodeType(node.origin(), 1); + } else { + maskNodePt->setOrigin(node.origin()); + } + + typename LeafNodeType::ValueOnCIter it; + for (it = node.cbeginValueOn(); it; ++it) { + maskNodePt->setValueOn(it.pos(), ((*it - mIsovalue) < 0.0) ? 0 : 1); + } + + if (maskNodePt->onVoxelCount() > 0) { + mMaskNodes[n] = maskNodePt; + maskNodePt = NULL; + } + } + + if (maskNodePt) delete maskNodePt; + } + + LeafNodeType const * const * const mNodes; + CharLeafNodeType ** const mMaskNodes; + ValueType const mIsovalue; +}; // LabelBoundaryVoxels + + +template +struct FlipRegionSign { + typedef typename LeafNodeType::ValueType ValueType; + + FlipRegionSign(LeafNodeType ** nodes) : mNodes(nodes) { } + + void operator()(const tbb::blocked_range& range) const { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + ValueType* values = const_cast(&mNodes[n]->getValue(0)); + for (Index i = 0; i < LeafNodeType::SIZE; ++i) { + values[i] = values[i] < 0 ? 1 : -1; + } + } + } + + LeafNodeType ** const mNodes; +}; // FlipRegionSign + + +template +struct FindMinVoxelValue { + + typedef typename LeafNodeType::ValueType ValueType; + + FindMinVoxelValue(LeafNodeType const * const * const leafnodes) + : minValue(std::numeric_limits::max()) + , mNodes(leafnodes) + { + } + + FindMinVoxelValue(FindMinVoxelValue& rhs, tbb::split) + : minValue(std::numeric_limits::max()) + , mNodes(rhs.mNodes) + { + } + + void operator()(const tbb::blocked_range& range) { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + const ValueType* data = mNodes[n]->buffer().data(); + for (Index i = 0; i < LeafNodeType::SIZE; ++i) { + minValue = std::min(minValue, data[i]); + } + } + } + + void join(FindMinVoxelValue& rhs) { minValue = std::min(minValue, rhs.minValue); } + + ValueType minValue; + + LeafNodeType const * const * const mNodes; +}; // FindMinVoxelValue + + +template +struct FindMinTileValue { + + typedef typename InternalNodeType::ValueType ValueType; + + FindMinTileValue(InternalNodeType const * const * const nodes) + : minValue(std::numeric_limits::max()) + , mNodes(nodes) + { + } + + FindMinTileValue(FindMinTileValue& rhs, tbb::split) + : minValue(std::numeric_limits::max()) + , mNodes(rhs.mNodes) + { + } + + void operator()(const tbb::blocked_range& range) { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + typename InternalNodeType::ValueAllCIter it = mNodes[n]->beginValueAll(); + for (; it; ++it) { + minValue = std::min(minValue, *it); + } + } + } + + void join(FindMinTileValue& rhs) { minValue = std::min(minValue, rhs.minValue); } + + ValueType minValue; + + InternalNodeType const * const * const mNodes; +}; // FindMinTileValue + + +template +struct SDFVoxelsToFogVolume { + + typedef typename LeafNodeType::ValueType ValueType; + + SDFVoxelsToFogVolume(LeafNodeType ** nodes, ValueType cutoffDistance) + : mNodes(nodes), mWeight(ValueType(1.0) / cutoffDistance) + { + } + + void operator()(const tbb::blocked_range& range) const { + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + LeafNodeType& node = *mNodes[n]; + node.setValuesOff(); + + ValueType* values = node.buffer().data(); + for (Index i = 0; i < LeafNodeType::SIZE; ++i) { + values[i] = values[i] > ValueType(0.0) ? ValueType(0.0) : values[i] * mWeight; + if (values[i] > ValueType(0.0)) node.setValueOn(i); + } + + if (node.onVoxelCount() == 0) { + delete mNodes[n]; + mNodes[n] = NULL; + } + } + } + + LeafNodeType ** const mNodes; + ValueType const mWeight; +}; // SDFVoxelsToFogVolume + + +template +struct SDFTilesToFogVolume { + + SDFTilesToFogVolume(const TreeType& tree, InternalNodeType ** nodes) + : mTree(&tree), mNodes(nodes) { } + + void operator()(const tbb::blocked_range& range) const { + + typedef typename TreeType::ValueType ValueType; + tree::ValueAccessor acc(*mTree); + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + typename InternalNodeType::ValueAllIter it = mNodes[n]->beginValueAll(); + for (; it; ++it) { + if (acc.getValue(it.getCoord()) < ValueType(0.0)) { + it.setValue(ValueType(1.0)); + it.setValueOn(true); + } + } + } + } + + TreeType const * const mTree; + InternalNodeType ** const mNodes; +}; // SDFTilesToFogVolume + + +template +struct FillMaskBoundary { + + typedef typename TreeType::ValueType ValueType; + typedef typename TreeType::LeafNodeType LeafNodeType; + typedef typename TreeType::template ValueConverter::Type BoolTreeType; + typedef typename BoolTreeType::LeafNodeType BoolLeafNodeType; + + FillMaskBoundary(const TreeType& tree, ValueType isovalue, const BoolTreeType& fillMask, + const BoolLeafNodeType ** fillNodes, BoolLeafNodeType ** newNodes) + : mTree(&tree), mFillMask(&fillMask), mFillNodes(fillNodes), mNewNodes(newNodes), mIsovalue(isovalue) + { + } + + void operator()(const tbb::blocked_range& range) const { + + tree::ValueAccessor maskAcc(*mFillMask); + tree::ValueAccessor distAcc(*mTree); + + boost::scoped_array valueMask(new char[BoolLeafNodeType::SIZE]); + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + mNewNodes[n] = NULL; + const BoolLeafNodeType& node = *mFillNodes[n]; + const Coord& origin = node.origin(); + + const bool denseNode = node.isDense(); + + // possible early out if the fill mask is dense + if (denseNode) { + + int denseNeighbors = 0; + + const BoolLeafNodeType* neighborNode = maskAcc.probeConstLeaf(origin.offsetBy(-1, 0, 0)); + if (neighborNode && neighborNode->isDense()) ++denseNeighbors; + + neighborNode = maskAcc.probeConstLeaf(origin.offsetBy(BoolLeafNodeType::DIM, 0, 0)); + if (neighborNode && neighborNode->isDense()) ++denseNeighbors; + + neighborNode = maskAcc.probeConstLeaf(origin.offsetBy(0, -1, 0)); + if (neighborNode && neighborNode->isDense()) ++denseNeighbors; + + neighborNode = maskAcc.probeConstLeaf(origin.offsetBy(0, BoolLeafNodeType::DIM, 0)); + if (neighborNode && neighborNode->isDense()) ++denseNeighbors; + + neighborNode = maskAcc.probeConstLeaf(origin.offsetBy(0, 0, -1)); + if (neighborNode && neighborNode->isDense()) ++denseNeighbors; + + neighborNode = maskAcc.probeConstLeaf(origin.offsetBy(0, 0, BoolLeafNodeType::DIM)); + if (neighborNode && neighborNode->isDense()) ++denseNeighbors; + + if (denseNeighbors == 6) continue; + } + + // rest value mask + memset(valueMask.get(), 0, sizeof(char) * BoolLeafNodeType::SIZE); + + const typename TreeType::LeafNodeType* distNode = distAcc.probeConstLeaf(origin); + + // check internal voxel neighbors + + bool earlyTermination = false; + + if (!denseNode) { + if (distNode) { + evalInternalNeighborsP(valueMask.get(), node, *distNode); + evalInternalNeighborsN(valueMask.get(), node, *distNode); + } else if (distAcc.getValue(origin) > mIsovalue) { + earlyTermination = evalInternalNeighborsP(valueMask.get(), node); + if (!earlyTermination) earlyTermination = evalInternalNeighborsN(valueMask.get(), node); + } + } + + // check external voxel neighbors + + if (!earlyTermination) { + evalExternalNeighborsX(valueMask.get(), node, maskAcc, distAcc); + evalExternalNeighborsX(valueMask.get(), node, maskAcc, distAcc); + evalExternalNeighborsY(valueMask.get(), node, maskAcc, distAcc); + evalExternalNeighborsY(valueMask.get(), node, maskAcc, distAcc); + evalExternalNeighborsZ(valueMask.get(), node, maskAcc, distAcc); + evalExternalNeighborsZ(valueMask.get(), node, maskAcc, distAcc); + } + + // Export marked boundary voxels. + + int numBoundaryValues = 0; + for (Index i = 0, I = BoolLeafNodeType::SIZE; i < I; ++i) { + numBoundaryValues += valueMask[i] == 1; + } + + if (numBoundaryValues > 0) { + mNewNodes[n] = new BoolLeafNodeType(origin, false); + for (Index i = 0, I = BoolLeafNodeType::SIZE; i < I; ++i) { + if (valueMask[i] == 1) mNewNodes[n]->setValueOn(i); + } + } + } + } + +private: + + // Check internal voxel neighbors in positive {x, y, z} directions. + + void evalInternalNeighborsP(char* valueMask, const BoolLeafNodeType& node, const LeafNodeType& distNode) const { + + for (Index x = 0; x < BoolLeafNodeType::DIM; ++x) { + const Index xPos = x << (2 * BoolLeafNodeType::LOG2DIM); + for (Index y = 0; y < BoolLeafNodeType::DIM; ++y) { + const Index yPos = xPos + (y << BoolLeafNodeType::LOG2DIM); + for (Index z = 0; z < BoolLeafNodeType::DIM - 1; ++z) { + const Index pos = yPos + z; + + if (valueMask[pos] != 0 || !node.isValueOn(pos)) continue; + + if (!node.isValueOn(pos + 1) && distNode.getValue(pos + 1) > mIsovalue) { + valueMask[pos] = 1; + } + } + } + } + + for (Index x = 0; x < BoolLeafNodeType::DIM; ++x) { + const Index xPos = x << (2 * BoolLeafNodeType::LOG2DIM); + for (Index y = 0; y < BoolLeafNodeType::DIM - 1; ++y) { + const Index yPos = xPos + (y << BoolLeafNodeType::LOG2DIM); + for (Index z = 0; z < BoolLeafNodeType::DIM; ++z) { + const Index pos = yPos + z; + + if (valueMask[pos] != 0 || !node.isValueOn(pos)) continue; + + if (!node.isValueOn(pos + BoolLeafNodeType::DIM) && + distNode.getValue(pos + BoolLeafNodeType::DIM) > mIsovalue) { + valueMask[pos] = 1; + } + } + } + } + + for (Index x = 0; x < BoolLeafNodeType::DIM - 1; ++x) { + const Index xPos = x << (2 * BoolLeafNodeType::LOG2DIM); + for (Index y = 0; y < BoolLeafNodeType::DIM; ++y) { + const Index yPos = xPos + (y << BoolLeafNodeType::LOG2DIM); + for (Index z = 0; z < BoolLeafNodeType::DIM; ++z) { + const Index pos = yPos + z; + + if (valueMask[pos] != 0 || !node.isValueOn(pos)) continue; + + if (!node.isValueOn(pos + BoolLeafNodeType::DIM * BoolLeafNodeType::DIM) && + distNode.getValue(pos + BoolLeafNodeType::DIM * BoolLeafNodeType::DIM) > mIsovalue) { + valueMask[pos] = 1; + } + } + } + } + } + + bool evalInternalNeighborsP(char* valueMask, const BoolLeafNodeType& node) const { + + for (Index x = 0; x < BoolLeafNodeType::DIM; ++x) { + const Index xPos = x << (2 * BoolLeafNodeType::LOG2DIM); + for (Index y = 0; y < BoolLeafNodeType::DIM; ++y) { + const Index yPos = xPos + (y << BoolLeafNodeType::LOG2DIM); + for (Index z = 0; z < BoolLeafNodeType::DIM - 1; ++z) { + const Index pos = yPos + z; + + if (node.isValueOn(pos) && !node.isValueOn(pos + 1)) { + valueMask[pos] = 1; + return true; + } + } + } + } + + for (Index x = 0; x < BoolLeafNodeType::DIM; ++x) { + const Index xPos = x << (2 * BoolLeafNodeType::LOG2DIM); + for (Index y = 0; y < BoolLeafNodeType::DIM - 1; ++y) { + const Index yPos = xPos + (y << BoolLeafNodeType::LOG2DIM); + for (Index z = 0; z < BoolLeafNodeType::DIM; ++z) { + const Index pos = yPos + z; + + if (node.isValueOn(pos) && !node.isValueOn(pos + BoolLeafNodeType::DIM)) { + valueMask[pos] = 1; + return true; + } + } + } + } + + for (Index x = 0; x < BoolLeafNodeType::DIM - 1; ++x) { + const Index xPos = x << (2 * BoolLeafNodeType::LOG2DIM); + for (Index y = 0; y < BoolLeafNodeType::DIM; ++y) { + const Index yPos = xPos + (y << BoolLeafNodeType::LOG2DIM); + for (Index z = 0; z < BoolLeafNodeType::DIM; ++z) { + const Index pos = yPos + z; + + if (node.isValueOn(pos) && + !node.isValueOn(pos + BoolLeafNodeType::DIM * BoolLeafNodeType::DIM)) { + valueMask[pos] = 1; + return true; + } + } + } + } + + return false; + } + + // Check internal voxel neighbors in negative {x, y, z} directions. + + void evalInternalNeighborsN(char* valueMask, const BoolLeafNodeType& node, const LeafNodeType& distNode) const { + + for (Index x = 0; x < BoolLeafNodeType::DIM; ++x) { + const Index xPos = x << (2 * BoolLeafNodeType::LOG2DIM); + for (Index y = 0; y < BoolLeafNodeType::DIM; ++y) { + const Index yPos = xPos + (y << BoolLeafNodeType::LOG2DIM); + for (Index z = 1; z < BoolLeafNodeType::DIM; ++z) { + const Index pos = yPos + z; + + if (valueMask[pos] != 0 || !node.isValueOn(pos)) continue; + + if (!node.isValueOn(pos - 1) && distNode.getValue(pos - 1) > mIsovalue) { + valueMask[pos] = 1; + } + } + } + } + + for (Index x = 0; x < BoolLeafNodeType::DIM; ++x) { + const Index xPos = x << (2 * BoolLeafNodeType::LOG2DIM); + for (Index y = 1; y < BoolLeafNodeType::DIM; ++y) { + const Index yPos = xPos + (y << BoolLeafNodeType::LOG2DIM); + for (Index z = 0; z < BoolLeafNodeType::DIM; ++z) { + const Index pos = yPos + z; + + if (valueMask[pos] != 0 || !node.isValueOn(pos)) continue; + + if (!node.isValueOn(pos - BoolLeafNodeType::DIM) && + distNode.getValue(pos - BoolLeafNodeType::DIM) > mIsovalue) { + valueMask[pos] = 1; + } + } + } + } + + for (Index x = 1; x < BoolLeafNodeType::DIM; ++x) { + const Index xPos = x << (2 * BoolLeafNodeType::LOG2DIM); + for (Index y = 0; y < BoolLeafNodeType::DIM; ++y) { + const Index yPos = xPos + (y << BoolLeafNodeType::LOG2DIM); + for (Index z = 0; z < BoolLeafNodeType::DIM; ++z) { + const Index pos = yPos + z; + + if (valueMask[pos] != 0 || !node.isValueOn(pos)) continue; + + if (!node.isValueOn(pos - BoolLeafNodeType::DIM * BoolLeafNodeType::DIM) && + distNode.getValue(pos - BoolLeafNodeType::DIM * BoolLeafNodeType::DIM) > mIsovalue) { + valueMask[pos] = 1; + } + } + } + } + } + + + bool evalInternalNeighborsN(char* valueMask, const BoolLeafNodeType& node) const { + + for (Index x = 0; x < BoolLeafNodeType::DIM; ++x) { + const Index xPos = x << (2 * BoolLeafNodeType::LOG2DIM); + for (Index y = 0; y < BoolLeafNodeType::DIM; ++y) { + const Index yPos = xPos + (y << BoolLeafNodeType::LOG2DIM); + for (Index z = 1; z < BoolLeafNodeType::DIM; ++z) { + const Index pos = yPos + z; + + if (node.isValueOn(pos) && !node.isValueOn(pos - 1)) { + valueMask[pos] = 1; + return true; + } + } + } + } + + for (Index x = 0; x < BoolLeafNodeType::DIM; ++x) { + const Index xPos = x << (2 * BoolLeafNodeType::LOG2DIM); + for (Index y = 1; y < BoolLeafNodeType::DIM; ++y) { + const Index yPos = xPos + (y << BoolLeafNodeType::LOG2DIM); + for (Index z = 0; z < BoolLeafNodeType::DIM; ++z) { + const Index pos = yPos + z; + + if (node.isValueOn(pos) && !node.isValueOn(pos - BoolLeafNodeType::DIM)) { + valueMask[pos] = 1; + return true; + } + } + } + } + + for (Index x = 1; x < BoolLeafNodeType::DIM; ++x) { + const Index xPos = x << (2 * BoolLeafNodeType::LOG2DIM); + for (Index y = 0; y < BoolLeafNodeType::DIM; ++y) { + const Index yPos = xPos + (y << BoolLeafNodeType::LOG2DIM); + for (Index z = 0; z < BoolLeafNodeType::DIM; ++z) { + const Index pos = yPos + z; + + if (node.isValueOn(pos) && + !node.isValueOn(pos - BoolLeafNodeType::DIM * BoolLeafNodeType::DIM)) { + valueMask[pos] = 1; + return true; + } + } + } + } + + return false; + } + + + // Check external voxel neighbors + + // If UpWind is true check the X+ oriented node face, else the X- oriented face. + template + void evalExternalNeighborsX(char* valueMask, const BoolLeafNodeType& node, + const tree::ValueAccessor& maskAcc, + const tree::ValueAccessor& distAcc) const { + + const Coord& origin = node.origin(); + Coord ijk(0, 0, 0), nijk; + int step = -1; + + if (UpWind) { + step = 1; + ijk[0] = int(BoolLeafNodeType::DIM) - 1; + } + + const Index xPos = ijk[0] << (2 * int(BoolLeafNodeType::LOG2DIM)); + + for (ijk[1] = 0; ijk[1] < int(BoolLeafNodeType::DIM); ++ijk[1]) { + const Index yPos = xPos + (ijk[1] << int(BoolLeafNodeType::LOG2DIM)); + + for (ijk[2] = 0; ijk[2] < int(BoolLeafNodeType::DIM); ++ijk[2]) { + const Index pos = yPos + ijk[2]; + + if (valueMask[pos] == 0 && node.isValueOn(pos)) { + + nijk = origin + ijk.offsetBy(step, 0, 0); + + if (!maskAcc.isValueOn(nijk) && distAcc.getValue(nijk) > mIsovalue) { + valueMask[pos] = 1; + } + } + } + } + } + + // If UpWind is true check the Y+ oriented node face, else the Y- oriented face. + template + void evalExternalNeighborsY(char* valueMask, const BoolLeafNodeType& node, + const tree::ValueAccessor& maskAcc, + const tree::ValueAccessor& distAcc) const { + + const Coord& origin = node.origin(); + Coord ijk(0, 0, 0), nijk; + int step = -1; + + if (UpWind) { + step = 1; + ijk[1] = int(BoolLeafNodeType::DIM) - 1; + } + + const Index yPos = ijk[1] << int(BoolLeafNodeType::LOG2DIM); + + for (ijk[0] = 0; ijk[0] < int(BoolLeafNodeType::DIM); ++ijk[0]) { + const Index xPos = yPos + (ijk[0] << (2 * int(BoolLeafNodeType::LOG2DIM))); + + for (ijk[2] = 0; ijk[2] < int(BoolLeafNodeType::DIM); ++ijk[2]) { + const Index pos = xPos + ijk[2]; + + if (valueMask[pos] == 0 && node.isValueOn(pos)) { + + nijk = origin + ijk.offsetBy(0, step, 0); + if (!maskAcc.isValueOn(nijk) && distAcc.getValue(nijk) > mIsovalue) { + valueMask[pos] = 1; + } + } + } + } + } + + // If UpWind is true check the Z+ oriented node face, else the Z- oriented face. + template + void evalExternalNeighborsZ(char* valueMask, const BoolLeafNodeType& node, + const tree::ValueAccessor& maskAcc, + const tree::ValueAccessor& distAcc) const { + + const Coord& origin = node.origin(); + Coord ijk(0, 0, 0), nijk; + int step = -1; + + if (UpWind) { + step = 1; + ijk[2] = int(BoolLeafNodeType::DIM) - 1; + } + + for (ijk[0] = 0; ijk[0] < int(BoolLeafNodeType::DIM); ++ijk[0]) { + const Index xPos = ijk[0] << (2 * int(BoolLeafNodeType::LOG2DIM)); + + for (ijk[1] = 0; ijk[1] < int(BoolLeafNodeType::DIM); ++ijk[1]) { + const Index pos = ijk[2] + xPos + (ijk[1] << int(BoolLeafNodeType::LOG2DIM)); + + if (valueMask[pos] == 0 && node.isValueOn(pos)) { + + nijk = origin + ijk.offsetBy(0, 0, step); + if (!maskAcc.isValueOn(nijk) && distAcc.getValue(nijk) > mIsovalue) { + valueMask[pos] = 1; + } + } + } + } + } + + ////////// + + TreeType const * const mTree; + BoolTreeType const * const mFillMask; + BoolLeafNodeType const * const * const mFillNodes; + BoolLeafNodeType ** const mNewNodes; + ValueType const mIsovalue; +}; // FillMaskBoundary + + +/// @brief Constructs a memory light char tree that represents the exterior region with @c +1 +/// and the interior regions with @c -1. +template +inline typename TreeType::template ValueConverter::Type::Ptr +computeEnclosedRegionMask(const TreeType& tree, typename TreeType::ValueType isovalue, + const typename TreeType::template ValueConverter::Type* fillMask) +{ + typedef typename TreeType::LeafNodeType LeafNodeType; + typedef typename TreeType::RootNodeType RootNodeType; + typedef typename RootNodeType::NodeChainType NodeChainType; + typedef typename boost::mpl::at >::type InternalNodeType; + + typedef typename TreeType::template ValueConverter::Type CharTreeType; + typedef typename CharTreeType::LeafNodeType CharLeafNodeType; + typedef typename CharTreeType::RootNodeType CharRootNodeType; + typedef typename CharRootNodeType::NodeChainType CharNodeChainType; + + typedef typename TreeType::template ValueConverter::Type BoolTreeType; + typedef typename BoolTreeType::LeafNodeType BoolLeafNodeType; + + ///// + + const TreeType* treePt = &tree; + + size_t numLeafNodes = 0, numInternalNodes = 0; + + std::vector nodes; + std::vector leafnodeCount; + + { + // compute the prefix sum of the leafnode count in each internal node. + std::vector internalNodes; + treePt->getNodes(internalNodes); + + numInternalNodes = internalNodes.size(); + + leafnodeCount.push_back(0); + for (size_t n = 0; n < numInternalNodes; ++n) { + leafnodeCount.push_back(leafnodeCount.back() + internalNodes[n]->leafCount()); + } + + numLeafNodes = leafnodeCount.back(); + + // extract all leafnodes + nodes.reserve(numLeafNodes); + + for (size_t n = 0; n < numInternalNodes; ++n) { + internalNodes[n]->getNodes(nodes); + } + } + + // create mask leafnodes + boost::scoped_array maskNodes(new CharLeafNodeType*[numLeafNodes]); + + tbb::parallel_for(tbb::blocked_range(0, numLeafNodes), + LabelBoundaryVoxels(isovalue, &nodes[0], maskNodes.get())); + + // create mask grid + typename CharTreeType::Ptr maskTree(new CharTreeType(1)); + + PopulateTree populate(*maskTree, maskNodes.get(), &leafnodeCount[0], 1); + tbb::parallel_reduce(tbb::blocked_range(0, numInternalNodes), populate); + + // optionally evaluate the fill mask + + std::vector extraMaskNodes; + + if (fillMask) { + + std::vector fillMaskNodes; + fillMask->getNodes(fillMaskNodes); + + boost::scoped_array boundaryMaskNodes(new BoolLeafNodeType*[fillMaskNodes.size()]); + + tbb::parallel_for(tbb::blocked_range(0, fillMaskNodes.size()), + FillMaskBoundary(tree, isovalue, *fillMask, &fillMaskNodes[0], boundaryMaskNodes.get())); + + tree::ValueAccessor maskAcc(*maskTree); + + for (size_t n = 0, N = fillMaskNodes.size(); n < N; ++n) { + + if (boundaryMaskNodes[n] == NULL) continue; + + const BoolLeafNodeType& boundaryNode = *boundaryMaskNodes[n]; + const Coord& origin = boundaryNode.origin(); + + CharLeafNodeType* maskNodePt = maskAcc.probeLeaf(origin); + + if (!maskNodePt) { + maskNodePt = maskAcc.touchLeaf(origin); + extraMaskNodes.push_back(maskNodePt); + } + + char* data = maskNodePt->buffer().data(); + + typename BoolLeafNodeType::ValueOnCIter it = boundaryNode.cbeginValueOn(); + for (; it; ++it) { + if (data[it.pos()] != 0) data[it.pos()] = -1; + } + + delete boundaryMaskNodes[n]; + } + } + + // eliminate enclosed regions + tools::traceExteriorBoundaries(*maskTree); + + // flip voxel sign to negative inside and positive outside. + tbb::parallel_for(tbb::blocked_range(0, numLeafNodes), + FlipRegionSign(maskNodes.get())); + + if (!extraMaskNodes.empty()) { + tbb::parallel_for(tbb::blocked_range(0, extraMaskNodes.size()), + FlipRegionSign(&extraMaskNodes[0])); + } + + // propagate sign information into tile region + tools::signedFloodFill(*maskTree); + + return maskTree; +} // computeEnclosedRegionMask() + + +template +inline typename TreeType::template ValueConverter::Type::Ptr +computeInteriorMask(const TreeType& tree, typename TreeType::ValueType iso) +{ + typedef typename TreeType::LeafNodeType LeafNodeType; + typedef typename TreeType::RootNodeType RootNodeType; + typedef typename RootNodeType::NodeChainType NodeChainType; + typedef typename boost::mpl::at >::type InternalNodeType; + + typedef typename TreeType::template ValueConverter::Type BoolTreeType; + typedef typename BoolTreeType::LeafNodeType BoolLeafNodeType; + typedef typename BoolTreeType::RootNodeType BoolRootNodeType; + typedef typename BoolRootNodeType::NodeChainType BoolNodeChainType; + typedef typename boost::mpl::at >::type BoolInternalNodeType; + + ///// + size_t numLeafNodes = 0, numInternalNodes = 0; + + std::vector nodes; + std::vector leafnodeCount; + + { + // compute the prefix sum of the leafnode count in each internal node. + std::vector internalNodes; + tree.getNodes(internalNodes); + + numInternalNodes = internalNodes.size(); + + leafnodeCount.push_back(0); + for (size_t n = 0; n < numInternalNodes; ++n) { + leafnodeCount.push_back(leafnodeCount.back() + internalNodes[n]->leafCount()); + } + + numLeafNodes = leafnodeCount.back(); + + // extract all leafnodes + nodes.reserve(numLeafNodes); + + for (size_t n = 0; n < numInternalNodes; ++n) { + internalNodes[n]->getNodes(nodes); + } + } + + // create mask leafnodes + boost::scoped_array maskNodes(new BoolLeafNodeType*[numLeafNodes]); + + tbb::parallel_for(tbb::blocked_range(0, numLeafNodes), + MaskInteriorVoxels(iso, &nodes[0], maskNodes.get())); + + + // create mask grid + typename BoolTreeType::Ptr maskTree(new BoolTreeType(false)); + + PopulateTree populate(*maskTree, maskNodes.get(), &leafnodeCount[0], false); + tbb::parallel_reduce(tbb::blocked_range(0, numInternalNodes), populate); + + + // evaluate tile values + std::vector internalMaskNodes; + maskTree->getNodes(internalMaskNodes); + + tbb::parallel_for(tbb::blocked_range(0, internalMaskNodes.size()), + MaskInteriorTiles(iso, tree, &internalMaskNodes[0])); + + tree::ValueAccessor acc(tree); + + typename BoolTreeType::ValueAllIter it(*maskTree); + it.setMaxDepth(BoolTreeType::ValueAllIter::LEAF_DEPTH - 2); + + for ( ; it; ++it) { + if (acc.getValue(it.getCoord()) < iso) { + it.setValue(true); + it.setActiveState(true); + } + } + + return maskTree; +} // computeInteriorMask() + + +template +struct MaskIsovalueCrossingVoxels +{ + typedef typename InputTreeType::ValueType InputValueType; + typedef typename InputTreeType::LeafNodeType InputLeafNodeType; + typedef typename InputTreeType::template ValueConverter::Type BoolTreeType; + typedef typename BoolTreeType::LeafNodeType BoolLeafNodeType; + + MaskIsovalueCrossingVoxels( + const InputTreeType& inputTree, + const std::vector& inputLeafNodes, + BoolTreeType& maskTree, + InputValueType iso) + : mInputAccessor(inputTree) + , mInputNodes(!inputLeafNodes.empty() ? &inputLeafNodes.front() : NULL) + , mMaskTree(false) + , mMaskAccessor(maskTree) + , mIsovalue(iso) + { + } + + MaskIsovalueCrossingVoxels(MaskIsovalueCrossingVoxels& rhs, tbb::split) + : mInputAccessor(rhs.mInputAccessor.tree()) + , mInputNodes(rhs.mInputNodes) + , mMaskTree(false) + , mMaskAccessor(mMaskTree) + , mIsovalue(rhs.mIsovalue) + { + } + + void operator()(const tbb::blocked_range& range) { + + const InputValueType iso = mIsovalue; + Coord ijk(0, 0, 0); + + BoolLeafNodeType* maskNodePt = NULL; + + for (size_t n = range.begin(); mInputNodes && (n != range.end()); ++n) { + + const InputLeafNodeType& node = *mInputNodes[n]; + + if (!maskNodePt) maskNodePt = new BoolLeafNodeType(node.origin(), false); + else maskNodePt->setOrigin(node.origin()); + + bool collectedData = false; + + for (typename InputLeafNodeType::ValueOnCIter it = node.cbeginValueOn(); it; ++it) { + + bool isUnder = *it < iso; + + ijk = it.getCoord(); + + ++ijk[2]; + bool signChange = isUnder != (mInputAccessor.getValue(ijk) < iso); // +z edge + --ijk[2]; + + if (!signChange) { + --ijk[2]; + signChange = isUnder != (mInputAccessor.getValue(ijk) < iso); // -z edge + ++ijk[2]; + } + + if (!signChange) { + ++ijk[1]; + signChange = isUnder != (mInputAccessor.getValue(ijk) < iso); // +y edge + --ijk[1]; + } + + if (!signChange) { + --ijk[1]; + signChange = isUnder != (mInputAccessor.getValue(ijk) < iso); // -y edge + ++ijk[1]; + } + + if (!signChange) { + ++ijk[0]; + signChange = isUnder != (mInputAccessor.getValue(ijk) < iso); // +x edge + --ijk[0]; + } + + if (!signChange) { + --ijk[0]; + signChange = isUnder != (mInputAccessor.getValue(ijk) < iso); // -x edge + ++ijk[0]; + } + + if (signChange) { + collectedData = true; + maskNodePt->setValueOn(it.pos(), true); + } + } + + if (collectedData) { + mMaskAccessor.addLeaf(maskNodePt); + maskNodePt = NULL; + } + } + + if (maskNodePt) delete maskNodePt; + } + + void join(MaskIsovalueCrossingVoxels& rhs) { + mMaskAccessor.tree().merge(rhs.mMaskAccessor.tree()); + } + +private: + tree::ValueAccessor mInputAccessor; + InputLeafNodeType const * const * const mInputNodes; + + BoolTreeType mMaskTree; + tree::ValueAccessor mMaskAccessor; + + InputValueType mIsovalue; +}; // MaskIsovalueCrossingVoxels + + +//////////////////////////////////////// + + +template +struct NodeMaskSegment +{ + typedef boost::shared_ptr Ptr; + typedef typename NodeType::NodeMaskType NodeMaskType; + + NodeMaskSegment() : connections(), mask(false), origin(0,0,0), visited(false) {} + + std::vector connections; + NodeMaskType mask; + Coord origin; + bool visited; +}; // struct NodeMaskSegment + + +template +inline void +nodeMaskSegmentation(const NodeType& node, + std::vector::Ptr>& segments) +{ + typedef typename NodeType::NodeMaskType NodeMaskType; + typedef NodeMaskSegment NodeMaskSegmentType; + typedef typename NodeMaskSegmentType::Ptr NodeMaskSegmentTypePtr; + + NodeMaskType nodeMask(node.getValueMask()); + std::deque indexList; + + while (!nodeMask.isOff()) { + + NodeMaskSegmentTypePtr segment(new NodeMaskSegmentType()); + segment->origin = node.origin(); + + NodeMaskType& mask = segment->mask; + + indexList.push_back(nodeMask.findFirstOn()); + nodeMask.setOff(indexList.back()); // mark as visited + Coord ijk(0, 0, 0); + + while (!indexList.empty()) { + + const Index pos = indexList.back(); + indexList.pop_back(); + + if (mask.isOn(pos)) continue; + mask.setOn(pos); + + ijk = NodeType::offsetToLocalCoord(pos); + + Index npos = pos - 1; + if (ijk[2] != 0 && nodeMask.isOn(npos)) { + nodeMask.setOff(npos); + indexList.push_back(npos); + } + + npos = pos + 1; + if (ijk[2] != (NodeType::DIM - 1) && nodeMask.isOn(npos)) { + nodeMask.setOff(npos); + indexList.push_back(npos); + } + + npos = pos - NodeType::DIM; + if (ijk[1] != 0 && nodeMask.isOn(npos)) { + nodeMask.setOff(npos); + indexList.push_back(npos); + } + + npos = pos + NodeType::DIM; + if (ijk[1] != (NodeType::DIM - 1) && nodeMask.isOn(npos)) { + nodeMask.setOff(npos); + indexList.push_back(npos); + } + + npos = pos - NodeType::DIM * NodeType::DIM; + if (ijk[0] != 0 && nodeMask.isOn(npos)) { + nodeMask.setOff(npos); + indexList.push_back(npos); + } + + npos = pos + NodeType::DIM * NodeType::DIM; + if (ijk[0] != (NodeType::DIM - 1) && nodeMask.isOn(npos)) { + nodeMask.setOff(npos); + indexList.push_back(npos); + } + + } + + segments.push_back(segment); + } +} + + +template +struct SegmentNodeMask +{ + typedef NodeMaskSegment NodeMaskSegmentType; + typedef typename NodeMaskSegmentType::Ptr NodeMaskSegmentTypePtr; + typedef typename std::vector NodeMaskSegmentVector; + + SegmentNodeMask(std::vector& nodes, NodeMaskSegmentVector* nodeMaskArray) + : mNodes(!nodes.empty() ? &nodes.front() : NULL) + , mNodeMaskArray(nodeMaskArray) + { + } + + void operator()(const tbb::blocked_range& range) const { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + NodeType& node = *mNodes[n]; + nodeMaskSegmentation(node, mNodeMaskArray[n]); + + // hack origin data to store array offset + Coord& origin = const_cast(node.origin()); + origin[0] = static_cast(n); + } + } + + NodeType * const * const mNodes; + NodeMaskSegmentVector * const mNodeMaskArray; +}; // struct SegmentNodeMask + + +template +struct ConnectNodeMaskSegments +{ + typedef typename NodeType::NodeMaskType NodeMaskType; + typedef NodeMaskSegment NodeMaskSegmentType; + typedef typename NodeMaskSegmentType::Ptr NodeMaskSegmentTypePtr; + typedef typename std::vector NodeMaskSegmentVector; + + ConnectNodeMaskSegments(const TreeType& tree, NodeMaskSegmentVector* nodeMaskArray) + : mTree(&tree) + , mNodeMaskArray(nodeMaskArray) + { + } + + void operator()(const tbb::blocked_range& range) const { + + tree::ValueAccessor acc(*mTree); + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + NodeMaskSegmentVector& segments = mNodeMaskArray[n]; + if (segments.empty()) continue; + + std::vector > connections(segments.size()); + + Coord ijk = segments[0]->origin; + + const NodeType* node = acc.template probeConstNode(ijk); + if (!node) continue; + + // get neighbour nodes + + ijk[2] += NodeType::DIM; + const NodeType* nodeZUp = acc.template probeConstNode(ijk); + ijk[2] -= (NodeType::DIM + NodeType::DIM); + const NodeType* nodeZDown = acc.template probeConstNode(ijk); + ijk[2] += NodeType::DIM; + + ijk[1] += NodeType::DIM; + const NodeType* nodeYUp = acc.template probeConstNode(ijk); + ijk[1] -= (NodeType::DIM + NodeType::DIM); + const NodeType* nodeYDown = acc.template probeConstNode(ijk); + ijk[1] += NodeType::DIM; + + ijk[0] += NodeType::DIM; + const NodeType* nodeXUp = acc.template probeConstNode(ijk); + ijk[0] -= (NodeType::DIM + NodeType::DIM); + const NodeType* nodeXDown = acc.template probeConstNode(ijk); + ijk[0] += NodeType::DIM; + + const Index startPos = node->getValueMask().findFirstOn(); + for (Index pos = startPos; pos < NodeMaskType::SIZE; ++pos) { + + if (!node->isValueOn(pos)) continue; + + ijk = NodeType::offsetToLocalCoord(pos); + Index npos = 0; + + if (ijk[2] == 0) { + npos = pos + (NodeType::DIM - 1); + if (nodeZDown && nodeZDown->isValueOn(npos)) { + NodeMaskSegmentType* nsegment = + findNodeMaskSegment(mNodeMaskArray[getNodeOffset(*nodeZDown)], npos); + const Index idx = findNodeMaskSegmentIndex(segments, pos); + connections[idx].insert(nsegment); + } + } else if (ijk[2] == (NodeType::DIM - 1)) { + npos = pos - (NodeType::DIM - 1); + if (nodeZUp && nodeZUp->isValueOn(npos)) { + NodeMaskSegmentType* nsegment = + findNodeMaskSegment(mNodeMaskArray[getNodeOffset(*nodeZUp)], npos); + const Index idx = findNodeMaskSegmentIndex(segments, pos); + connections[idx].insert(nsegment); + } + } + + if (ijk[1] == 0) { + npos = pos + (NodeType::DIM - 1) * NodeType::DIM; + if (nodeYDown && nodeYDown->isValueOn(npos)) { + NodeMaskSegmentType* nsegment = + findNodeMaskSegment(mNodeMaskArray[getNodeOffset(*nodeYDown)], npos); + const Index idx = findNodeMaskSegmentIndex(segments, pos); + connections[idx].insert(nsegment); + } + } else if (ijk[1] == (NodeType::DIM - 1)) { + npos = pos - (NodeType::DIM - 1) * NodeType::DIM; + if (nodeYUp && nodeYUp->isValueOn(npos)) { + NodeMaskSegmentType* nsegment = + findNodeMaskSegment(mNodeMaskArray[getNodeOffset(*nodeYUp)], npos); + const Index idx = findNodeMaskSegmentIndex(segments, pos); + connections[idx].insert(nsegment); + } + } + + if (ijk[0] == 0) { + npos = pos + (NodeType::DIM - 1) * NodeType::DIM * NodeType::DIM; + if (nodeXDown && nodeXDown->isValueOn(npos)) { + NodeMaskSegmentType* nsegment = + findNodeMaskSegment(mNodeMaskArray[getNodeOffset(*nodeXDown)], npos); + const Index idx = findNodeMaskSegmentIndex(segments, pos); + connections[idx].insert(nsegment); + } + } else if (ijk[0] == (NodeType::DIM - 1)) { + npos = pos - (NodeType::DIM - 1) * NodeType::DIM * NodeType::DIM; + if (nodeXUp && nodeXUp->isValueOn(npos)) { + NodeMaskSegmentType* nsegment = + findNodeMaskSegment(mNodeMaskArray[getNodeOffset(*nodeXUp)], npos); + const Index idx = findNodeMaskSegmentIndex(segments, pos); + connections[idx].insert(nsegment); + } + } + } + + for (size_t i = 0, I = connections.size(); i < I; ++i) { + + typename std::set::iterator + it = connections[i].begin(), end = connections[i].end(); + + std::vector& segmentConnections = segments[i]->connections; + segmentConnections.reserve(connections.size()); + for (; it != end; ++it) { + segmentConnections.push_back(*it); + } + } + } // end range loop + } + +private: + + static inline size_t getNodeOffset(const NodeType& node) { + return static_cast(node.origin()[0]); + } + + static inline NodeMaskSegmentType* + findNodeMaskSegment(NodeMaskSegmentVector& segments, Index pos) + { + NodeMaskSegmentType* segment = NULL; + + for (size_t n = 0, N = segments.size(); n < N; ++n) { + if (segments[n]->mask.isOn(pos)) { + segment = segments[n].get(); + break; + } + } + + return segment; + } + + static inline Index + findNodeMaskSegmentIndex(NodeMaskSegmentVector& segments, Index pos) + { + for (Index n = 0, N = Index(segments.size()); n < N; ++n) { + if (segments[n]->mask.isOn(pos)) return n; + } + return Index(-1); + } + + TreeType const * const mTree; + NodeMaskSegmentVector * const mNodeMaskArray; +}; // struct ConnectNodeMaskSegments + + +template +struct MaskSegmentGroup +{ + typedef typename TreeType::LeafNodeType LeafNodeType; + typedef typename TreeType::Ptr TreeTypePtr; + typedef NodeMaskSegment NodeMaskSegmentType; + + MaskSegmentGroup(const std::vector& segments) + : mSegments(!segments.empty() ? &segments.front() : NULL) + , mTree(new TreeType(false)) + { + } + + MaskSegmentGroup(const MaskSegmentGroup& rhs, tbb::split) + : mSegments(rhs.mSegments) + , mTree(new TreeType(false)) + { + } + + TreeTypePtr& mask() { return mTree; } + + void join(MaskSegmentGroup& rhs) { mTree->merge(*rhs.mTree); } + + void operator()(const tbb::blocked_range& range) { + + tree::ValueAccessor acc(*mTree); + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + NodeMaskSegmentType& segment = *mSegments[n]; + LeafNodeType* node = acc.touchLeaf(segment.origin); + node->getValueMask() |= segment.mask; + } + } + +private: + NodeMaskSegmentType * const * const mSegments; + TreeTypePtr mTree; +}; // struct MaskSegmentGroup + + +//////////////////////////////////////// + + +template +struct ExpandLeafNodeRegion +{ + typedef typename TreeType::ValueType ValueType; + typedef typename TreeType::LeafNodeType LeafNodeType; + typedef typename LeafNodeType::NodeMaskType NodeMaskType; + + typedef typename TreeType::template ValueConverter::Type BoolTreeType; + typedef typename BoolTreeType::LeafNodeType BoolLeafNodeType; + + ///// + + ExpandLeafNodeRegion(const TreeType& distTree, BoolTreeType& maskTree, std::vector& maskNodes) + : mDistTree(&distTree) + , mMaskTree(&maskTree) + , mMaskNodes(!maskNodes.empty() ? &maskNodes.front() : NULL) + , mNewMaskTree(false) + { + } + + ExpandLeafNodeRegion(const ExpandLeafNodeRegion& rhs, tbb::split) + : mDistTree(rhs.mDistTree) + , mMaskTree(rhs.mMaskTree) + , mMaskNodes(rhs.mMaskNodes) + , mNewMaskTree(false) + { + } + + BoolTreeType& newMaskTree() { return mNewMaskTree; } + + void join(ExpandLeafNodeRegion& rhs) { mNewMaskTree.merge(rhs.mNewMaskTree); } + + void operator()(const tbb::blocked_range& range) { + + typedef LeafNodeType NodeType; + + tree::ValueAccessor distAcc(*mDistTree); + tree::ValueAccessor maskAcc(*mMaskTree); + tree::ValueAccessor newMaskAcc(mNewMaskTree); + + NodeMaskType maskZUp, maskZDown, maskYUp, maskYDown, maskXUp, maskXDown; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + BoolLeafNodeType& maskNode = *mMaskNodes[n]; + if (maskNode.isEmpty()) continue; + + Coord ijk = maskNode.origin(), nijk; + + const LeafNodeType* distNode = distAcc.probeConstLeaf(ijk); + if (!distNode) continue; + + const ValueType *dataZUp = NULL, *dataZDown = NULL, + *dataYUp = NULL, *dataYDown = NULL, + *dataXUp = NULL, *dataXDown = NULL; + + ijk[2] += NodeType::DIM; + getData(ijk, distAcc, maskAcc, maskZUp, dataZUp); + ijk[2] -= (NodeType::DIM + NodeType::DIM); + getData(ijk, distAcc, maskAcc, maskZDown, dataZDown); + ijk[2] += NodeType::DIM; + + ijk[1] += NodeType::DIM; + getData(ijk, distAcc, maskAcc, maskYUp, dataYUp); + ijk[1] -= (NodeType::DIM + NodeType::DIM); + getData(ijk, distAcc, maskAcc, maskYDown, dataYDown); + ijk[1] += NodeType::DIM; + + ijk[0] += NodeType::DIM; + getData(ijk, distAcc, maskAcc, maskXUp, dataXUp); + ijk[0] -= (NodeType::DIM + NodeType::DIM); + getData(ijk, distAcc, maskAcc, maskXDown, dataXDown); + ijk[0] += NodeType::DIM; + + for (typename BoolLeafNodeType::ValueOnIter it = maskNode.beginValueOn(); it; ++it) { + + const Index pos = it.pos(); + const ValueType val = std::abs(distNode->getValue(pos)); + + ijk = BoolLeafNodeType::offsetToLocalCoord(pos); + nijk = ijk + maskNode.origin(); + + if (dataZUp && ijk[2] == (BoolLeafNodeType::DIM - 1)) { + const Index npos = pos - (NodeType::DIM - 1); + if (maskZUp.isOn(npos) && std::abs(dataZUp[npos]) > val) { + newMaskAcc.setValueOn(nijk.offsetBy(0, 0, 1)); + } + } else if (dataZDown && ijk[2] == 0) { + const Index npos = pos + (NodeType::DIM - 1); + if (maskZDown.isOn(npos) && std::abs(dataZDown[npos]) > val) { + newMaskAcc.setValueOn(nijk.offsetBy(0, 0, -1)); + } + } + + if (dataYUp && ijk[1] == (BoolLeafNodeType::DIM - 1)) { + const Index npos = pos - (NodeType::DIM - 1) * NodeType::DIM; + if (maskYUp.isOn(npos) && std::abs(dataYUp[npos]) > val) { + newMaskAcc.setValueOn(nijk.offsetBy(0, 1, 0)); + } + } else if (dataYDown && ijk[1] == 0) { + const Index npos = pos + (NodeType::DIM - 1) * NodeType::DIM; + if (maskYDown.isOn(npos) && std::abs(dataYDown[npos]) > val) { + newMaskAcc.setValueOn(nijk.offsetBy(0, -1, 0)); + } + } + + if (dataXUp && ijk[0] == (BoolLeafNodeType::DIM - 1)) { + const Index npos = pos - (NodeType::DIM - 1) * NodeType::DIM * NodeType::DIM; + if (maskXUp.isOn(npos) && std::abs(dataXUp[npos]) > val) { + newMaskAcc.setValueOn(nijk.offsetBy(1, 0, 0)); + } + } else if (dataXDown && ijk[0] == 0) { + const Index npos = pos + (NodeType::DIM - 1) * NodeType::DIM * NodeType::DIM; + if (maskXDown.isOn(npos) && std::abs(dataXDown[npos]) > val) { + newMaskAcc.setValueOn(nijk.offsetBy(-1, 0, 0)); + } + } + + } // end value on loop + } // end range loop + } + +private: + + static inline void + getData(const Coord& ijk, tree::ValueAccessor& distAcc, + tree::ValueAccessor& maskAcc, NodeMaskType& mask, const ValueType*& data) + { + const LeafNodeType* node = distAcc.probeConstLeaf(ijk); + if (node) { + data = node->buffer().data(); + mask = node->getValueMask(); + const BoolLeafNodeType* maskNodePt = maskAcc.probeConstLeaf(ijk); + if (maskNodePt) mask -= maskNodePt->getValueMask(); + } + } + + TreeType const * const mDistTree; + BoolTreeType * const mMaskTree; + BoolLeafNodeType ** const mMaskNodes; + + BoolTreeType mNewMaskTree; +}; // struct ExpandLeafNodeRegion + + +template +struct FillLeafNodeVoxels +{ + typedef typename TreeType::ValueType ValueType; + typedef typename TreeType::LeafNodeType LeafNodeType; + typedef typename LeafNodeType::NodeMaskType NodeMaskType; + typedef tree::LeafNode BoolLeafNodeType; + + FillLeafNodeVoxels(const TreeType& tree, std::vector& maskNodes) + : mTree(&tree), mMaskNodes(!maskNodes.empty() ? &maskNodes.front() : NULL) + { + } + + void operator()(const tbb::blocked_range& range) const { + + tree::ValueAccessor distAcc(*mTree); + + std::vector indexList; + indexList.reserve(NodeMaskType::SIZE); + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + BoolLeafNodeType& maskNode = *mMaskNodes[n]; + + const LeafNodeType * distNode = distAcc.probeConstLeaf(maskNode.origin()); + if (!distNode) continue; + + NodeMaskType mask(distNode->getValueMask()); + NodeMaskType& narrowbandMask = maskNode.getValueMask(); + + for (Index pos = narrowbandMask.findFirstOn(); pos < NodeMaskType::SIZE; ++pos) { + if (narrowbandMask.isOn(pos)) indexList.push_back(pos); + } + + mask -= narrowbandMask; // bitwise difference + narrowbandMask.setOff(); + + const ValueType* data = distNode->buffer().data(); + Coord ijk(0, 0, 0); + + while (!indexList.empty()) { + + const Index pos = indexList.back(); + indexList.pop_back(); + + if (narrowbandMask.isOn(pos)) continue; + narrowbandMask.setOn(pos); + + const ValueType dist = std::abs(data[pos]); + + ijk = LeafNodeType::offsetToLocalCoord(pos); + + Index npos = pos - 1; + if (ijk[2] != 0 && mask.isOn(npos) && std::abs(data[npos]) > dist) { + mask.setOff(npos); + indexList.push_back(npos); + } + + npos = pos + 1; + if (ijk[2] != (LeafNodeType::DIM - 1) && mask.isOn(npos) && std::abs(data[npos]) > dist) { + mask.setOff(npos); + indexList.push_back(npos); + } + + npos = pos - LeafNodeType::DIM; + if (ijk[1] != 0 && mask.isOn(npos) && std::abs(data[npos]) > dist) { + mask.setOff(npos); + indexList.push_back(npos); + } + + npos = pos + LeafNodeType::DIM; + if (ijk[1] != (LeafNodeType::DIM - 1) && mask.isOn(npos) && std::abs(data[npos]) > dist) { + mask.setOff(npos); + indexList.push_back(npos); + } + + npos = pos - LeafNodeType::DIM * LeafNodeType::DIM; + if (ijk[0] != 0 && mask.isOn(npos) && std::abs(data[npos]) > dist) { + mask.setOff(npos); + indexList.push_back(npos); + } + + npos = pos + LeafNodeType::DIM * LeafNodeType::DIM; + if (ijk[0] != (LeafNodeType::DIM - 1) && mask.isOn(npos) && std::abs(data[npos]) > dist) { + mask.setOff(npos); + indexList.push_back(npos); + } + } // end flood fill loop + } // end range loop + } + + TreeType const * const mTree; + BoolLeafNodeType ** const mMaskNodes; +}; // FillLeafNodeVoxels + + +template +struct ExpandNarrowbandMask +{ + typedef typename TreeType::template ValueConverter::Type BoolTreeType; + typedef typename BoolTreeType::LeafNodeType BoolLeafNodeType; + typedef typename BoolTreeType::Ptr BoolTreeTypePtr; + + ExpandNarrowbandMask(const TreeType& tree, std::vector& segments) + : mTree(&tree), mSegments(!segments.empty() ? &segments.front() : NULL) + { + } + + void operator()(const tbb::blocked_range& range) const { + + const TreeType& distTree = *mTree; + std::vector nodes; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + BoolTreeType& narrowBandMask = *mSegments[n]; + + BoolTreeType candidateMask(narrowBandMask, false, TopologyCopy()); + + while (true) { + + nodes.clear(); + candidateMask.getNodes(nodes); + if (nodes.empty()) break; + + const tbb::blocked_range nodeRange(0, nodes.size()); + + tbb::parallel_for(nodeRange, FillLeafNodeVoxels(distTree, nodes)); + + narrowBandMask.topologyUnion(candidateMask); + + ExpandLeafNodeRegion op(distTree, narrowBandMask, nodes); + tbb::parallel_reduce(nodeRange, op); + + if (op.newMaskTree().empty()) break; + + candidateMask.clear(); + candidateMask.merge(op.newMaskTree()); + } // end expand loop + } // end range loop + } + + TreeType const * const mTree; + BoolTreeTypePtr * const mSegments; +}; // ExpandNarrowbandMask + + +template +struct FloodFillSign +{ + typedef typename TreeType::Ptr TreeTypePtr; + typedef typename TreeType::ValueType ValueType; + typedef typename TreeType::LeafNodeType LeafNodeType; + typedef typename TreeType::RootNodeType RootNodeType; + typedef typename RootNodeType::NodeChainType NodeChainType; + typedef typename boost::mpl::at >::type InternalNodeType; + + FloodFillSign(const TreeType& tree, std::vector& segments) + : mTree(&tree) + , mSegments(!segments.empty() ? &segments.front() : NULL) + , mMinValue(ValueType(0.0)) + { + ValueType minSDFValue = std::numeric_limits::max(); + + { + std::vector nodes; + tree.getNodes(nodes); + + if (!nodes.empty()) { + FindMinTileValue minOp(&nodes[0]); + tbb::parallel_reduce(tbb::blocked_range(0, nodes.size()), minOp); + minSDFValue = std::min(minSDFValue, minOp.minValue); + } + } + + if (minSDFValue > ValueType(0.0)) { + std::vector nodes; + tree.getNodes(nodes); + if (!nodes.empty()) { + FindMinVoxelValue minOp(&nodes[0]); + tbb::parallel_reduce(tbb::blocked_range(0, nodes.size()), minOp); + minSDFValue = std::min(minSDFValue, minOp.minValue); + } + } + + mMinValue = minSDFValue; + } + + void operator()(const tbb::blocked_range& range) const { + const ValueType interiorValue = -std::abs(mMinValue); + const ValueType exteriorValue = std::abs(mTree->background()); + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + tools::signedFloodFillWithValues(*mSegments[n], exteriorValue, interiorValue); + } + } + +private: + + TreeType const * const mTree; + TreeTypePtr * const mSegments; + ValueType mMinValue; +}; // FloodFillSign + + +template +struct MaskedCopy +{ + typedef typename TreeType::Ptr TreeTypePtr; + typedef typename TreeType::ValueType ValueType; + typedef typename TreeType::LeafNodeType LeafNodeType; + + typedef typename TreeType::template ValueConverter::Type BoolTreeType; + typedef typename BoolTreeType::Ptr BoolTreeTypePtr; + typedef typename BoolTreeType::LeafNodeType BoolLeafNodeType; + + MaskedCopy(const TreeType& tree, std::vector& segments, std::vector& masks) + : mTree(&tree) + , mSegments(!segments.empty() ? &segments.front() : NULL) + , mMasks(!masks.empty() ? &masks.front() : NULL) + { + } + + void operator()(const tbb::blocked_range& range) const { + + std::vector nodes; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + const BoolTreeType& mask = *mMasks[n]; + + nodes.clear(); + mask.getNodes(nodes); + + Copy op(*mTree, nodes); + tbb::parallel_reduce(tbb::blocked_range(0, nodes.size()), op); + mSegments[n] = op.outputTree(); + } + } + +private: + + struct Copy { + Copy(const TreeType& inputTree, std::vector& maskNodes) + : mInputTree(&inputTree) + , mMaskNodes(!maskNodes.empty() ? &maskNodes.front() : NULL) + , mOutputTreePtr(new TreeType(inputTree.background())) + { + } + + Copy(const Copy& rhs, tbb::split) + : mInputTree(rhs.mInputTree) + , mMaskNodes(rhs.mMaskNodes) + , mOutputTreePtr(new TreeType(mInputTree->background())) + { + } + + TreeTypePtr& outputTree() { return mOutputTreePtr; } + + void join(Copy& rhs) { mOutputTreePtr->merge(*rhs.mOutputTreePtr); } + + void operator()(const tbb::blocked_range& range) { + + tree::ValueAccessor inputAcc(*mInputTree); + tree::ValueAccessor outputAcc(*mOutputTreePtr); + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + const BoolLeafNodeType& maskNode = *mMaskNodes[n]; + if (maskNode.isEmpty()) continue; + + const Coord& ijk = maskNode.origin(); + + const LeafNodeType* inputNode = inputAcc.probeConstLeaf(ijk); + if (inputNode) { + + LeafNodeType* outputNode = outputAcc.touchLeaf(ijk); + + for (typename BoolLeafNodeType::ValueOnCIter it = maskNode.cbeginValueOn(); it; ++it) { + const Index idx = it.pos(); + outputNode->setValueOn(idx, inputNode->getValue(idx)); + } + } else { + const int valueDepth = inputAcc.getValueDepth(ijk); + if (valueDepth >= 0) { + outputAcc.addTile(TreeType::RootNodeType::LEVEL - valueDepth, + ijk, inputAcc.getValue(ijk), true); + } + } + } + } + + private: + TreeType const * const mInputTree; + BoolLeafNodeType const * const * const mMaskNodes; + TreeTypePtr mOutputTreePtr; + }; // struct Copy + + TreeType const * const mTree; + TreeTypePtr * const mSegments; + BoolTreeTypePtr * const mMasks; +}; // MaskedCopy + + +//////////////////////////////////////// + + +template +struct ComputeActiveVoxelCount +{ + ComputeActiveVoxelCount(std::vector& segments, size_t *countArray) + : mSegments(!segments.empty() ? &segments.front() : NULL) + , mCountArray(countArray) + { + } + + void operator()(const tbb::blocked_range& range) const { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + mCountArray[n] = mSegments[n]->activeVoxelCount(); + } + } + + VolumePtrType * const mSegments; + size_t * const mCountArray; +}; + + +struct GreaterCount +{ + GreaterCount(const size_t *countArray) : mCountArray(countArray) {} + + inline bool operator() (const size_t& lhs, const size_t& rhs) const + { + return (mCountArray[lhs] > mCountArray[rhs]); + } + + size_t const * const mCountArray; +}; + +//////////////////////////////////////// + + +template +struct GridOrTreeConstructor +{ + typedef typename TreeType::Ptr TreeTypePtr; + typedef typename TreeType::template ValueConverter::Type::Ptr BoolTreePtrType; + + static BoolTreePtrType constructMask(const TreeType&, BoolTreePtrType& maskTree) { return maskTree; } + static TreeTypePtr construct(const TreeType&, TreeTypePtr& tree) { return tree; } +}; + + +template +struct GridOrTreeConstructor > +{ + typedef Grid GridType; + typedef typename Grid::Ptr GridTypePtr; + typedef typename TreeType::Ptr TreeTypePtr; + + typedef typename TreeType::template ValueConverter::Type BoolTreeType; + typedef typename BoolTreeType::Ptr BoolTreePtrType; + typedef Grid BoolGridType; + typedef typename BoolGridType::Ptr BoolGridPtrType; + + static BoolGridPtrType constructMask(const GridType& grid, BoolTreePtrType& maskTree) { + BoolGridPtrType maskGrid(BoolGridType::create(maskTree)); + maskGrid->setTransform(grid.transform().copy()); + return maskGrid; + } + + static GridTypePtr construct(const GridType& grid, TreeTypePtr& maskTree) { + GridTypePtr maskGrid(GridType::create(maskTree)); + maskGrid->setTransform(grid.transform().copy()); + maskGrid->insertMeta(grid); + return maskGrid; + } +}; + + +} // namespace level_set_util_internal + + +//////////////////////////////////////// + + +template +inline void +sdfToFogVolume(GridType& grid, typename GridType::ValueType cutoffDistance) +{ + typedef typename GridType::ValueType ValueType; + typedef typename GridType::TreeType TreeType; + typedef typename TreeType::LeafNodeType LeafNodeType; + typedef typename TreeType::RootNodeType RootNodeType; + typedef typename RootNodeType::NodeChainType NodeChainType; + typedef typename boost::mpl::at >::type InternalNodeType; + + ////////// + + TreeType& tree = grid.tree(); + + size_t numLeafNodes = 0, numInternalNodes = 0; + + std::vector nodes; + std::vector leafnodeCount; + + { + // Compute the prefix sum of the leafnode count in each internal node. + std::vector internalNodes; + tree.getNodes(internalNodes); + + numInternalNodes = internalNodes.size(); + + leafnodeCount.push_back(0); + for (size_t n = 0; n < numInternalNodes; ++n) { + leafnodeCount.push_back(leafnodeCount.back() + internalNodes[n]->leafCount()); + } + + numLeafNodes = leafnodeCount.back(); + + // Steal all leafnodes (Removes them from the tree and transfers ownership.) + nodes.reserve(numLeafNodes); + + for (size_t n = 0; n < numInternalNodes; ++n) { + internalNodes[n]->stealNodes(nodes, tree.background(), false); + } + + // Clamp cutoffDistance to min sdf value + ValueType minSDFValue = std::numeric_limits::max(); + + { + level_set_util_internal::FindMinTileValue minOp(&internalNodes[0]); + tbb::parallel_reduce(tbb::blocked_range(0, internalNodes.size()), minOp); + minSDFValue = std::min(minSDFValue, minOp.minValue); + } + + if (minSDFValue > ValueType(0.0)) { + level_set_util_internal::FindMinVoxelValue minOp(&nodes[0]); + tbb::parallel_reduce(tbb::blocked_range(0, nodes.size()), minOp); + minSDFValue = std::min(minSDFValue, minOp.minValue); + } + + cutoffDistance = -std::abs(cutoffDistance); + cutoffDistance = minSDFValue > cutoffDistance ? minSDFValue : cutoffDistance; + } + + // Transform voxel values and delete leafnodes that are uniformly zero after the transformation. + // (Positive values are set to zero with inactive state and negative values are remapped + // from zero to one with active state.) + tbb::parallel_for(tbb::blocked_range(0, nodes.size()), + level_set_util_internal::SDFVoxelsToFogVolume(&nodes[0], cutoffDistance)); + + // Populate a new tree with the remaining leafnodes + typename TreeType::Ptr newTree(new TreeType(ValueType(0.0))); + + level_set_util_internal::PopulateTree populate(*newTree, &nodes[0], &leafnodeCount[0], 0); + tbb::parallel_reduce(tbb::blocked_range(0, numInternalNodes), populate); + + // Transform tile values (Negative valued tiles are set to 1.0 with active state.) + std::vector internalNodes; + newTree->getNodes(internalNodes); + + tbb::parallel_for(tbb::blocked_range(0, internalNodes.size()), + level_set_util_internal::SDFTilesToFogVolume(tree, &internalNodes[0])); + + { + tree::ValueAccessor acc(tree); + + typename TreeType::ValueAllIter it(*newTree); + it.setMaxDepth(TreeType::ValueAllIter::LEAF_DEPTH - 2); + + for ( ; it; ++it) { + if (acc.getValue(it.getCoord()) < ValueType(0.0)) { + it.setValue(ValueType(1.0)); + it.setActiveState(true); + } + } + } + + // Insert missing root level tiles. (The new tree is constructed from the remaining leafnodes + // and will therefore not contain any root level tiles that may exist in the original tree.) + { + typename TreeType::ValueAllIter it(tree); + it.setMaxDepth(TreeType::ValueAllIter::ROOT_DEPTH); + for ( ; it; ++it) { + if (it.getValue() < ValueType(0.0)) { + newTree->addTile(TreeType::ValueAllIter::ROOT_LEVEL, it.getCoord(), ValueType(1.0), true); + } + } + } + + grid.setTree(newTree); + grid.setGridClass(GRID_FOG_VOLUME); +} + + +//////////////////////////////////////// + + +template +inline typename GridOrTreeType::template ValueConverter::Type::Ptr +sdfInteriorMask(const GridOrTreeType& volume, typename GridOrTreeType::ValueType isovalue) +{ + typedef typename TreeAdapter::TreeType TreeType; + const TreeType& tree = TreeAdapter::tree(volume); + + typedef typename TreeType::template ValueConverter::Type::Ptr BoolTreePtrType; + BoolTreePtrType mask = level_set_util_internal::computeInteriorMask(tree, isovalue); + + return level_set_util_internal::GridOrTreeConstructor::constructMask(volume, mask); +} + + +template +inline typename GridOrTreeType::template ValueConverter::Type::Ptr +extractEnclosedRegion(const GridOrTreeType& volume, + typename GridOrTreeType::ValueType isovalue, + const typename TreeAdapter::TreeType::template ValueConverter::Type* fillMask) +{ + typedef typename TreeAdapter::TreeType TreeType; + const TreeType& tree = TreeAdapter::tree(volume); + + typedef typename TreeType::template ValueConverter::Type::Ptr CharTreePtrType; + CharTreePtrType regionMask = level_set_util_internal::computeEnclosedRegionMask(tree, isovalue, fillMask); + + typedef typename TreeType::template ValueConverter::Type::Ptr BoolTreePtrType; + BoolTreePtrType mask = level_set_util_internal::computeInteriorMask(*regionMask, 0); + + return level_set_util_internal::GridOrTreeConstructor::constructMask(volume, mask); +} + + +//////////////////////////////////////// + + +template +inline typename GridOrTreeType::template ValueConverter::Type::Ptr +extractIsosurfaceMask(const GridOrTreeType& volume, typename GridOrTreeType::ValueType isovalue) +{ + typedef typename TreeAdapter::TreeType TreeType; + const TreeType& tree = TreeAdapter::tree(volume); + + std::vector nodes; + tree.getNodes(nodes); + + typedef typename TreeType::template ValueConverter::Type BoolTreeType; + typename BoolTreeType::Ptr mask(new BoolTreeType(false)); + + level_set_util_internal::MaskIsovalueCrossingVoxels op(tree, nodes, *mask, isovalue); + tbb::parallel_reduce(tbb::blocked_range(0, nodes.size()), op); + + return level_set_util_internal::GridOrTreeConstructor::constructMask(volume, mask); +} + + +//////////////////////////////////////// + + +template +inline void +extractActiveVoxelSegmentMasks(const GridOrTreeType& volume, + std::vector::Type::Ptr>& masks) +{ + typedef typename TreeAdapter::TreeType TreeType; + typedef typename TreeType::template ValueConverter::Type BoolTreeType; + typedef typename BoolTreeType::Ptr BoolTreePtrType; + typedef typename BoolTreeType::LeafNodeType BoolLeafNodeType; + + typedef level_set_util_internal::NodeMaskSegment NodeMaskSegmentType; + typedef typename NodeMaskSegmentType::Ptr NodeMaskSegmentPtrType; + typedef typename std::vector NodeMaskSegmentPtrVector; + typedef typename std::vector NodeMaskSegmentRawPtrVector; + + ///// + + const TreeType& tree = TreeAdapter::tree(volume); + + BoolTreeType topologyMask(tree, false, TopologyCopy()); + + if (topologyMask.hasActiveTiles()) { + topologyMask.voxelizeActiveTiles(); + } + + std::vector leafnodes; + topologyMask.getNodes(leafnodes); + + if (leafnodes.empty()) return; + + // 1. Split node masks into disjoint segments + // Note: The LeafNode origin coord is modified to record the 'leafnodes' array offset. + + boost::scoped_array nodeSegmentArray(new NodeMaskSegmentPtrVector[leafnodes.size()]); + + tbb::parallel_for(tbb::blocked_range(0, leafnodes.size()), + level_set_util_internal::SegmentNodeMask(leafnodes, nodeSegmentArray.get())); + + + // 2. Compute segment connectivity + + tbb::parallel_for(tbb::blocked_range(0, leafnodes.size()), + level_set_util_internal::ConnectNodeMaskSegments( + topologyMask, nodeSegmentArray.get())); + + topologyMask.clear(); + + size_t nodeSegmentCount = 0; + for (size_t n = 0, N = leafnodes.size(); n < N; ++n) { + nodeSegmentCount += nodeSegmentArray[n].size(); + } + + // 3. Group connected segments + + std::deque nodeSegmentGroups; + + NodeMaskSegmentType* nextSegment = nodeSegmentArray[0][0].get(); + while (nextSegment) { + + nodeSegmentGroups.push_back(NodeMaskSegmentRawPtrVector()); + + std::vector& segmentGroup = nodeSegmentGroups.back(); + segmentGroup.reserve(nodeSegmentCount); + + std::deque segmentQueue; + segmentQueue.push_back(nextSegment); + nextSegment = NULL; + + while (!segmentQueue.empty()) { + + NodeMaskSegmentType* segment = segmentQueue.back(); + segmentQueue.pop_back(); + + if (segment->visited) continue; + segment->visited = true; + + segmentGroup.push_back(segment); + + // queue connected segments + std::vector& connections = segment->connections; + for (size_t n = 0, N = connections.size(); n < N; ++n) { + if (!connections[n]->visited) segmentQueue.push_back(connections[n]); + } + } + + // find first unvisited segment + for (size_t n = 0, N = leafnodes.size(); n < N; ++n) { + NodeMaskSegmentPtrVector& nodeSegments = nodeSegmentArray[n]; + for (size_t i = 0, I = nodeSegments.size(); i < I; ++i) { + if (!nodeSegments[i]->visited) nextSegment = nodeSegments[i].get(); + } + } + } + + // 4. Mask segment groups + + if (nodeSegmentGroups.size() == 1) { + + BoolTreePtrType mask(new BoolTreeType(tree, false, TopologyCopy())); + + if (mask->hasActiveTiles()) { + mask->voxelizeActiveTiles(); + } + + masks.push_back( + level_set_util_internal::GridOrTreeConstructor::constructMask(volume, mask)); + + } else if (nodeSegmentGroups.size() > 1) { + + for (size_t n = 0, N = nodeSegmentGroups.size(); n < N; ++n) { + + NodeMaskSegmentRawPtrVector& segmentGroup = nodeSegmentGroups[n]; + + level_set_util_internal::MaskSegmentGroup op(segmentGroup); + tbb::parallel_reduce(tbb::blocked_range(0, segmentGroup.size()), op); + + masks.push_back( + level_set_util_internal::GridOrTreeConstructor::constructMask(volume, op.mask())); + } + } + + // 5. Sort segments in descending order based on the active voxel count. + + if (masks.size() > 1) { + const size_t segmentCount = masks.size(); + + boost::scoped_array segmentOrderArray(new size_t[segmentCount]); + boost::scoped_array voxelCountArray(new size_t[segmentCount]); + + for (size_t n = 0; n < segmentCount; ++n) { + segmentOrderArray[n] = n; + } + + tbb::parallel_for(tbb::blocked_range(0, segmentCount), + level_set_util_internal::ComputeActiveVoxelCount(masks, voxelCountArray.get())); + + size_t *begin = segmentOrderArray.get(); + tbb::parallel_sort(begin, begin + masks.size(), level_set_util_internal::GreaterCount(voxelCountArray.get())); + + std::vector orderedMasks; + orderedMasks.reserve(masks.size()); + + for (size_t n = 0; n < segmentCount; ++n) { + orderedMasks.push_back(masks[segmentOrderArray[n]]); + } + + masks.swap(orderedMasks); + } + +} // extractActiveVoxelSegmentMasks() + + +template +inline void +segmentActiveVoxels(const GridOrTreeType& volume, std::vector& segments) +{ + typedef typename TreeAdapter::TreeType TreeType; + typedef typename TreeType::Ptr TreePtrType; + typedef typename TreeType::template ValueConverter::Type BoolTreeType; + typedef typename BoolTreeType::Ptr BoolTreePtrType; + + const TreeType& inputTree = TreeAdapter::tree(volume); + + // 1. Segment active topology mask + std::vector maskSegmentArray; + extractActiveVoxelSegmentMasks(inputTree, maskSegmentArray); + + const size_t numSegments = maskSegmentArray.size(); + + if (numSegments < 2) { + // single segment early-out + TreePtrType segment(new TreeType(inputTree)); + segments.push_back( + level_set_util_internal::GridOrTreeConstructor::construct(volume, segment)); + return; + } + + const tbb::blocked_range segmentRange(0, numSegments); + + // 2. Export segments + std::vector outputSegmentArray(numSegments); + + tbb::parallel_for(segmentRange, + level_set_util_internal::MaskedCopy(inputTree, outputSegmentArray, maskSegmentArray)); + + for (size_t n = 0, N = numSegments; n < N; ++n) { + segments.push_back( + level_set_util_internal::GridOrTreeConstructor::construct(volume, outputSegmentArray[n])); + } +} + + +template +inline void +segmentSDF(const GridOrTreeType& volume, std::vector& segments) +{ + typedef typename TreeAdapter::TreeType TreeType; + typedef typename TreeType::Ptr TreePtrType; + typedef typename TreeType::template ValueConverter::Type BoolTreeType; + typedef typename BoolTreeType::Ptr BoolTreePtrType; + + const TreeType& inputTree = TreeAdapter::tree(volume); + + // 1. Mask zero crossing voxels + BoolTreePtrType mask = extractIsosurfaceMask(inputTree, lsutilGridZero()); + + // 2. Segment the zero crossing mask + std::vector maskSegmentArray; + extractActiveVoxelSegmentMasks(*mask, maskSegmentArray); + + const size_t numSegments = maskSegmentArray.size(); + + if (numSegments < 2) { + // single segment early-out + TreePtrType segment(new TreeType(inputTree)); + segments.push_back( + level_set_util_internal::GridOrTreeConstructor::construct(volume, segment)); + return; + } + + const tbb::blocked_range segmentRange(0, numSegments); + + + // 3. Expand zero crossing mask to capture sdf narrow band + tbb::parallel_for(segmentRange, + level_set_util_internal::ExpandNarrowbandMask(inputTree, maskSegmentArray)); + + // 4. Export sdf segments + std::vector outputSegmentArray(numSegments); + + tbb::parallel_for(segmentRange, + level_set_util_internal::MaskedCopy(inputTree, outputSegmentArray, maskSegmentArray)); + + tbb::parallel_for(segmentRange, + level_set_util_internal::FloodFillSign(inputTree, outputSegmentArray)); + + + for (size_t n = 0, N = numSegments; n < N; ++n) { + segments.push_back( + level_set_util_internal::GridOrTreeConstructor::construct(volume, outputSegmentArray[n])); + } +} + + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_LEVEL_SET_UTIL_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/MaskToLevelSet.h b/nuparu/include/openvdb/include/openvdb/tools/MaskToLevelSet.h new file mode 100644 index 00000000..391883fe --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/MaskToLevelSet.h @@ -0,0 +1,199 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 MaskToLevelSet.h +/// +/// @brief This tool generated a narrow band level set from the +/// interface between the active and inactive voxels of an +/// input grid. +/// +/// @par Example: +/// Combine with @c tools::createPointMaskGrid for fast point cloud to level set conversion. +/// +/// @author FX R&D OpenVDB team + +#ifndef OPENVDB_TOOLS_MASK_TO_LEVELSET_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_MASK_TO_LEVELSET_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include // for isNegative +#include +#include "LevelSetFilter.h" +#include "Morphology.h" // for erodeVoxels and dilateActiveValues +#include "SignedFloodFill.h" // for signedFloodFill + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Compute the narrow band level set to the interface +/// between active and inactive voxels in the input grid. +/// +/// @return a shared pointer to a new narrow band level set (SDF) of type @c float +/// +/// @param grid an incoming grid of arbitrary data type whose active voxels +/// are used in constructing the narrow band level set +/// @param halfWidth half of the narrow band level set width in voxel units +/// @param dilation amount of dilation in voxel units +/// @param erosion amount of erosion in voxel units +/// @param interrupt optional interrupter (see NullInterrupter.h) +/// +template +inline typename Grid::Type>::Ptr +maskToLevelSet(const GridT& grid, + int halfWidth = 3, + int dilation = 1, + int erosion = 1, + InterrupterT* interrupt = NULL); + +/// @brief Compute the narrow band level set to the interface +/// between active and inactive voxels in the input grid. +/// +/// @return a shared pointer to a new narrow band level set (SDF) of type @c float +/// +/// @param grid an incoming grid of arbitrary data type whose active voxels +/// are used in constructing the narrow band level set +/// @param halfWidth half of the narrow band level set width in voxel units +/// @param dilation amount of dilation in voxel units +/// @param erosion amount of erosion in voxel units +/// +/// @note This template specialized version uses first order upwinding and no interrupter. +/// C++03 compilers do not allow functions to have default template arguments - +/// hence this workaround. +/// +template +inline typename Grid::Type>::Ptr +maskToLevelSet(const GridT& grid, + int halfWidth = 3, + int dilation = 1, + int erosion = 1) +{ + typedef util::NullInterrupter T; + return maskToLevelSet(grid, halfWidth, dilation, erosion); +} + +namespace { + +template +struct DilateOp +{ + DilateOp(TreeT& t, int n) : tree(&t), size(n) {} + void operator()() const { + dilateActiveValues( *tree, size, tools::NN_FACE, tools::IGNORE_TILES); + } + TreeT* tree; + const int size; +}; + + +template +struct ErodeOp +{ + ErodeOp(TreeT& t, int n) : tree(&t), size(n) {} + void operator()() const { erodeVoxels( *tree, size); } + TreeT* tree; + const int size; +}; + +}// unnamed namespace + + +template +inline typename Grid::Type>::Ptr +maskToLevelSet(const GridT& grid, int halfWidth, int dilation, int erosion, InterrupterT* interrupt) +{ + typedef typename GridT::TreeType::template ValueConverter::Type MaskTreeT; + typedef typename GridT::TreeType::template ValueConverter::Type FloatTreeT; + typedef Grid FloatGridT; + + // Check input. + if ( halfWidth <= 0 ) { + OPENVDB_THROW(ValueError, "Narrow band width must be non-zero!"); + } + if ( !grid.hasUniformVoxels() ) { + OPENVDB_THROW(ValueError, "Non-uniform voxels are not supported!"); + } + + // background value = outside value + const float outside = static_cast(grid.voxelSize()[0]) * halfWidth; + + // Copy the topology into a MaskGrid. + MaskTreeT maskTree( grid.tree(), false/*background*/, openvdb::TopologyCopy() ); + + // Morphological closing operation. + dilateActiveValues( maskTree, dilation, tools::NN_FACE, tools::IGNORE_TILES); + erodeVoxels( maskTree, erosion); + + // Generate a volume with an implicit zero crossing at the boundary + // between active and inactive values in the input grid. + typename FloatTreeT::Ptr lsTree( new FloatTreeT(maskTree, + outside,// = inactive + -outside,//= active + openvdb::TopologyCopy()) ); + + tbb::task_group pool; + pool.run( ErodeOp< MaskTreeT >( maskTree, halfWidth ) ); + pool.run( DilateOp( *lsTree , halfWidth ) ); + pool.wait();// wait for both tasks to complete + + lsTree->topologyDifference( maskTree ); + tools::pruneLevelSet( *lsTree, /*threading=*/true); + + // Create a level set grid from the tree + typename FloatGridT::Ptr lsGrid = FloatGridT::create( lsTree ); + lsGrid->setTransform( grid.transform().copy() ); + lsGrid->setGridClass( openvdb::GRID_LEVEL_SET ); + + // Normalize and prune level set + LevelSetTracker tracker( *lsGrid, interrupt ); + tracker.setSpatialScheme( Scheme ); + tracker.setNormCount( 3 * halfWidth ); + tracker.normalize(); + tracker.prune(); + + return lsGrid; +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif //OPENVDB_TOOLS_MASK_TO_LEVELSET_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/MeshToVolume.h b/nuparu/include/openvdb/include/openvdb/tools/MeshToVolume.h new file mode 100644 index 00000000..8872fad2 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/MeshToVolume.h @@ -0,0 +1,4032 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 MeshToVolume.h +/// +/// @brief Convert polygonal meshes that consist of quads and/or triangles +/// into signed or unsigned distance field volumes. +/// +/// @note The signed distance field conversion 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 / polygon orientation. +/// +/// @author Mihai Alden + + +#ifndef OPENVDB_TOOLS_MESH_TO_VOLUME_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_MESH_TO_VOLUME_HAS_BEEN_INCLUDED + +#include +#include // for GodunovsNormSqrd +#include // for closestPointOnTriangleToPoint +#include +#include + +#include "ChangeBackground.h" +#include "Prune.h" // for pruneInactive and pruneLevelSet +#include "SignedFloodFill.h" // for signedFloodFillWithValues + +#include +#include +#include +#include +#include +#include +#include + +#include // const_max +#include // for isfinite +#include + +#include // for std::sort +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + + +//////////////////////////////////////// + + +/// @brief Mesh to volume conversion flags +enum MeshToVolumeFlags { + + /// Switch from the default signed distance field conversion that classifies + /// regions as either inside or outside the mesh boundary to a unsigned distance + /// field conversion that only computes distance values. This conversion type + /// does not require a closed watertight mesh. + UNSIGNED_DISTANCE_FIELD = 0x1, + + /// Disable the cleanup step that removes voxels created by self intersecting + /// portions of the mesh. + DISABLE_INTERSECTING_VOXEL_REMOVAL = 0x2, + + /// Disable the distance renormalization step that smooths out bumps caused + /// by self intersecting or overlapping portions of the mesh + DISABLE_RENORMALIZATION = 0x4, + + /// Disable the cleanup step that removes active voxels that exceed the + /// narrow band limits. (Only relevant for small limits) + DISABLE_NARROW_BAND_TRIMMING = 0x8 +}; + + +/// @brief Convert polygonal meshes that consist of quads and/or triangles into +/// signed or unsigned distance field volumes. +/// +/// @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. +/// +/// @interface MeshDataAdapter +/// Expected interface for the MeshDataAdapter class +/// @code +/// struct MeshDataAdapter { +/// size_t polygonCount() const; // Total number of polygons +/// size_t pointCount() const; // Total number of points +/// size_t vertexCount(size_t n) const; // Vertex count for polygon n +/// +/// // Return position pos in local grid index space for polygon n and vertex v +/// void getIndexSpacePoint(size_t n, size_t v, openvdb::Vec3d& pos) const; +/// }; +/// @endcode +/// +/// @param mesh mesh data access class that conforms to the MeshDataAdapter +/// interface +/// @param transform world-to-index-space transform +/// @param exteriorBandWidth exterior narrow band width in voxel units +/// @param interiorBandWidth interior narrow band width in voxel units +/// (set to std::numeric_limits::max() to fill object +/// interior with distance values) +/// @param flags optional conversion flags defined in @c MeshToVolumeFlags +/// @param polygonIndexGrid optional grid output that will contain the closest-polygon +/// index for each voxel in the narrow band region +template +inline typename GridType::Ptr +meshToVolume( + const MeshDataAdapter& mesh, + const math::Transform& transform, + float exteriorBandWidth = 3.0f, + float interiorBandWidth = 3.0f, + int flags = 0, + typename GridType::template ValueConverter::Type * polygonIndexGrid = NULL); + + +/// @brief Convert polygonal meshes that consist of quads and/or triangles into +/// signed or unsigned distance field volumes. +/// +/// @param interrupter a callback to interrupt the conversion process that conforms +/// to the util::NullInterrupter interface +/// @param mesh mesh data access class that conforms to the MeshDataAdapter +/// interface +/// @param transform world-to-index-space transform +/// @param exteriorBandWidth exterior narrow band width in voxel units +/// @param interiorBandWidth interior narrow band width in voxel units (set this value to +/// std::numeric_limits::max() to fill interior regions +/// with distance values) +/// @param flags optional conversion flags defined in @c MeshToVolumeFlags +/// @param polygonIndexGrid optional grid output that will contain the closest-polygon +/// index for each voxel in the active narrow band region +template +inline typename GridType::Ptr +meshToVolume( + Interrupter& interrupter, + const MeshDataAdapter& mesh, + const math::Transform& transform, + float exteriorBandWidth = 3.0f, + float interiorBandWidth = 3.0f, + int flags = 0, + typename GridType::template ValueConverter::Type * polygonIndexGrid = NULL); + + +//////////////////////////////////////// + + +/// @brief Contiguous quad and triangle data adapter class +/// +/// @details PointType and PolygonType must provide element access +/// through the square brackets operator. +/// @details Points are assumed to be in local grid index space. +/// @details The PolygonType tuple can have either three or four components +/// this property must be specified in a static member variable +/// named @c size, similar to the math::Tuple class. +/// @details A four component tuple can represent a quads or a triangle +/// if the fourth component set to @c util::INVALID_INDEX +template +struct QuadAndTriangleDataAdapter { + + QuadAndTriangleDataAdapter(const std::vector& points, + const std::vector& polygons) + : mPointArray(points.empty() ? NULL : &points[0]) + , mPointArraySize(points.size()) + , mPolygonArray(polygons.empty() ? NULL : &polygons[0]) + , mPolygonArraySize(polygons.size()) + { + } + + QuadAndTriangleDataAdapter(const PointType * pointArray, size_t pointArraySize, + const PolygonType* polygonArray, size_t polygonArraySize) + : mPointArray(pointArray) + , mPointArraySize(pointArraySize) + , mPolygonArray(polygonArray) + , mPolygonArraySize(polygonArraySize) + { + } + + size_t polygonCount() const { return mPolygonArraySize; } + size_t pointCount() const { return mPointArraySize; } + + /// @brief Vertex count for polygon @a n + size_t vertexCount(size_t n) const { + return (PolygonType::size == 3 || mPolygonArray[n][3] == util::INVALID_IDX) ? 3 : 4; + } + + /// @brief Returns position @a pos in local grid index space + /// for polygon @a n and vertex @a v + void getIndexSpacePoint(size_t n, size_t v, Vec3d& pos) const { + const PointType& p = mPointArray[mPolygonArray[n][int(v)]]; + pos[0] = double(p[0]); + pos[1] = double(p[1]); + pos[2] = double(p[2]); + } + +private: + PointType const * const mPointArray; + size_t const mPointArraySize; + PolygonType const * const mPolygonArray; + size_t const mPolygonArraySize; +}; // struct QuadAndTriangleDataAdapter + + +//////////////////////////////////////// + + +// Wrapper functions for the mesh to volume 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 +inline typename GridType::Ptr +meshToLevelSet( + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& 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 +inline typename GridType::Ptr +meshToLevelSet( + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& 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 +inline typename GridType::Ptr +meshToLevelSet( + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& triangles, + const std::vector& 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 +inline typename GridType::Ptr +meshToSignedDistanceField( + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& triangles, + const std::vector& 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 +inline typename GridType::Ptr +meshToUnsignedDistanceField( + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& triangles, + const std::vector& quads, + float bandWidth); + + +//////////////////////////////////////// + + +/// @brief Return a grid of type @c GridType containing a narrow-band level set +/// representation of a box. +/// +/// @param bbox a bounding box in world units +/// @param xform world-to-index-space transform +/// @param halfWidth half the width of the narrow band, in voxel units +template +inline typename GridType::Ptr +createLevelSetBox(const math::BBox& bbox, + const openvdb::math::Transform& xform, + typename VecType::ValueType halfWidth = LEVEL_SET_HALF_WIDTH); + + +//////////////////////////////////////// + + +/// @brief Traces the exterior voxel boundary of closed objects in the input +/// volume @a tree. Exterior voxels are marked with a negative sign, +/// voxels with a value below @c 0.75 are left unchanged and act as +/// the boundary layer. +/// +/// @note Does not propagate sign information into tile regions. +template +inline void +traceExteriorBoundaries(FloatTreeT& tree); + + +//////////////////////////////////////// + + +/// @brief Extracts and stores voxel edge intersection data from a mesh. +class MeshToVoxelEdgeData +{ +public: + + ////////// + + ///@brief Internal edge data type. + struct EdgeData { + EdgeData(float dist = 1.0) + : mXDist(dist), mYDist(dist), mZDist(dist) + , mXPrim(util::INVALID_IDX) + , mYPrim(util::INVALID_IDX) + , mZPrim(util::INVALID_IDX) + { + } + + //@{ + /// Required by several of the tree nodes + /// @note These methods don't perform meaningful operations. + bool operator< (const EdgeData&) const { return false; } + bool operator> (const EdgeData&) const { return false; } + template EdgeData operator+(const T&) const { return *this; } + template EdgeData operator-(const T&) const { return *this; } + EdgeData operator-() const { return *this; } + //@} + + bool operator==(const EdgeData& rhs) const + { + return mXPrim == rhs.mXPrim && mYPrim == rhs.mYPrim && mZPrim == rhs.mZPrim; + } + + float mXDist, mYDist, mZDist; + Index32 mXPrim, mYPrim, mZPrim; + }; + + typedef tree::Tree4::Type TreeType; + typedef tree::ValueAccessor Accessor; + + + ////////// + + + MeshToVoxelEdgeData(); + + + /// @brief Threaded method to extract voxel edge data, the closest + /// intersection point and corresponding primitive index, + /// from the given mesh. + /// + /// @param pointList List of points in grid index space, preferably unique + /// and shared by different polygons. + /// @param polygonList List of triangles and/or quads. + void convert(const std::vector& pointList, const std::vector& polygonList); + + + /// @brief Returns intersection points with corresponding primitive + /// indices for the given @c ijk voxel. + void getEdgeData(Accessor& acc, const Coord& ijk, + std::vector& points, std::vector& primitives); + + /// @return An accessor of @c MeshToVoxelEdgeData::Accessor type that + /// provides random read access to the internal tree. + Accessor getAccessor() { return Accessor(mTree); } + +private: + void operator=(const MeshToVoxelEdgeData&) {} + TreeType mTree; + class GenEdgeData; +}; + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// + + +// Internal utility objects and implementation details + +namespace mesh_to_volume_internal { + +template +struct TransformPoints { + + TransformPoints(const PointType* pointsIn, PointType* pointsOut, + const math::Transform& xform) + : mPointsIn(pointsIn), mPointsOut(pointsOut), mXform(&xform) + { + } + + void operator()(const tbb::blocked_range& range) const { + + Vec3d pos; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + const PointType& wsP = mPointsIn[n]; + pos[0] = double(wsP[0]); + pos[1] = double(wsP[1]); + pos[2] = double(wsP[2]); + + pos = mXform->worldToIndex(pos); + + PointType& isP = mPointsOut[n]; + isP[0] = typename PointType::value_type(pos[0]); + isP[1] = typename PointType::value_type(pos[1]); + isP[2] = typename PointType::value_type(pos[2]); + } + } + + PointType const * const mPointsIn; + PointType * const mPointsOut; + math::Transform const * const mXform; +}; // TransformPoints + + +template +struct Tolerance +{ + static ValueType epsilon() { return ValueType(1e-7); } + static ValueType minNarrowBandWidth() { return ValueType(1.0 + 1e-6); } +}; + + +//////////////////////////////////////// + + +template +class CombineLeafNodes +{ +public: + + typedef typename TreeType::template ValueConverter::Type Int32TreeType; + + typedef typename TreeType::LeafNodeType LeafNodeType; + typedef typename Int32TreeType::LeafNodeType Int32LeafNodeType; + + CombineLeafNodes(TreeType& lhsDistTree, Int32TreeType& lhsIdxTree, + LeafNodeType ** rhsDistNodes, Int32LeafNodeType ** rhsIdxNodes) + : mDistTree(&lhsDistTree) + , mIdxTree(&lhsIdxTree) + , mRhsDistNodes(rhsDistNodes) + , mRhsIdxNodes(rhsIdxNodes) + { + } + + void operator()(const tbb::blocked_range& range) const { + + tree::ValueAccessor distAcc(*mDistTree); + tree::ValueAccessor idxAcc(*mIdxTree); + + typedef typename LeafNodeType::ValueType DistValueType; + typedef typename Int32LeafNodeType::ValueType IndexValueType; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + const Coord& origin = mRhsDistNodes[n]->origin(); + + LeafNodeType* lhsDistNode = distAcc.probeLeaf(origin); + Int32LeafNodeType* lhsIdxNode = idxAcc.probeLeaf(origin); + + DistValueType* lhsDistData = lhsDistNode->buffer().data(); + IndexValueType* lhsIdxData = lhsIdxNode->buffer().data(); + + const DistValueType* rhsDistData = mRhsDistNodes[n]->buffer().data(); + const IndexValueType* rhsIdxData = mRhsIdxNodes[n]->buffer().data(); + + + for (Index32 offset = 0; offset < LeafNodeType::SIZE; ++offset) { + + if (rhsIdxData[offset] != Int32(util::INVALID_IDX)) { + + const DistValueType& lhsValue = lhsDistData[offset]; + const DistValueType& rhsValue = rhsDistData[offset]; + + if (rhsValue < lhsValue) { + lhsDistNode->setValueOn(offset, rhsValue); + lhsIdxNode->setValueOn(offset, rhsIdxData[offset]); + } else if (math::isExactlyEqual(rhsValue, lhsValue)) { + lhsIdxNode->setValueOn(offset, + std::min(lhsIdxData[offset], rhsIdxData[offset])); + } + } + } + + delete mRhsDistNodes[n]; + delete mRhsIdxNodes[n]; + } + } + +private: + + TreeType * const mDistTree; + Int32TreeType * const mIdxTree; + + LeafNodeType ** const mRhsDistNodes; + Int32LeafNodeType ** const mRhsIdxNodes; +}; // class CombineLeafNodes + + +//////////////////////////////////////// + + +template +struct StashOriginAndStoreOffset +{ + typedef typename TreeType::LeafNodeType LeafNodeType; + + StashOriginAndStoreOffset(std::vector& nodes, Coord* coordinates) + : mNodes(nodes.empty() ? NULL : &nodes[0]), mCoordinates(coordinates) + { + } + + void operator()(const tbb::blocked_range& range) const { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + Coord& origin = const_cast(mNodes[n]->origin()); + mCoordinates[n] = origin; + origin[0] = static_cast(n); + } + } + + LeafNodeType ** const mNodes; + Coord * const mCoordinates; +}; + + +template +struct RestoreOrigin +{ + typedef typename TreeType::LeafNodeType LeafNodeType; + + RestoreOrigin(std::vector& nodes, const Coord* coordinates) + : mNodes(nodes.empty() ? NULL : &nodes[0]), mCoordinates(coordinates) + { + } + + void operator()(const tbb::blocked_range& range) const { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + Coord& origin = const_cast(mNodes[n]->origin()); + origin[0] = mCoordinates[n][0]; + } + } + + LeafNodeType ** const mNodes; + Coord const * const mCoordinates; +}; + + +template +class ComputeNodeConnectivity +{ +public: + typedef typename TreeType::LeafNodeType LeafNodeType; + + ComputeNodeConnectivity(const TreeType& tree, const Coord* coordinates, + size_t* offsets, size_t numNodes, const CoordBBox& bbox) + : mTree(&tree) + , mCoordinates(coordinates) + , mOffsets(offsets) + , mNumNodes(numNodes) + , mBBox(bbox) + { + } + + void operator()(const tbb::blocked_range& range) const { + + size_t* offsetsNextX = mOffsets; + size_t* offsetsPrevX = mOffsets + mNumNodes; + size_t* offsetsNextY = mOffsets + mNumNodes * 2; + size_t* offsetsPrevY = mOffsets + mNumNodes * 3; + size_t* offsetsNextZ = mOffsets + mNumNodes * 4; + size_t* offsetsPrevZ = mOffsets + mNumNodes * 5; + + tree::ValueAccessor acc(*mTree); + Coord ijk; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + const Coord& origin = mCoordinates[n]; + offsetsNextX[n] = findNeighbourNode(acc, origin, Coord(LeafNodeType::DIM, 0, 0)); + offsetsPrevX[n] = findNeighbourNode(acc, origin, Coord(-LeafNodeType::DIM, 0, 0)); + offsetsNextY[n] = findNeighbourNode(acc, origin, Coord(0, LeafNodeType::DIM, 0)); + offsetsPrevY[n] = findNeighbourNode(acc, origin, Coord(0, -LeafNodeType::DIM, 0)); + offsetsNextZ[n] = findNeighbourNode(acc, origin, Coord(0, 0, LeafNodeType::DIM)); + offsetsPrevZ[n] = findNeighbourNode(acc, origin, Coord(0, 0, -LeafNodeType::DIM)); + } + } + + size_t findNeighbourNode(tree::ValueAccessor& acc, const Coord& start, const Coord& step) const { + + Coord ijk = start + step; + CoordBBox bbox(mBBox); + + while (bbox.isInside(ijk)) { + const LeafNodeType* node = acc.probeConstLeaf(ijk); + if (node) return static_cast(node->origin()[0]); + ijk += step; + } + + return boost::integer_traits::const_max; + } + + +private: + // Disallow assignment + ComputeNodeConnectivity& operator=(const ComputeNodeConnectivity&); + + TreeType const * const mTree; + Coord const * const mCoordinates; + size_t * const mOffsets; + + const size_t mNumNodes; + const CoordBBox mBBox; +}; // class ComputeNodeConnectivity + + +template +struct LeafNodeConnectivityTable { + + enum { INVALID_OFFSET = boost::integer_traits::const_max }; + + typedef typename TreeType::LeafNodeType LeafNodeType; + + LeafNodeConnectivityTable(TreeType& tree) + : mLeafNodes() + , mOffsets(NULL) + { + mLeafNodes.reserve(tree.leafCount()); + tree.getNodes(mLeafNodes); + + if (mLeafNodes.empty()) return; + + CoordBBox bbox; + tree.evalLeafBoundingBox(bbox); + + const tbb::blocked_range range(0, mLeafNodes.size()); + + // stash the leafnode origin coordinate and temporarily store the + // linear offset in the origin.x variable. + boost::scoped_array coordinates(new Coord[mLeafNodes.size()]); + tbb::parallel_for(range, StashOriginAndStoreOffset(mLeafNodes, coordinates.get())); + + // build the leafnode offset table + mOffsets.reset(new size_t[mLeafNodes.size() * 6]); + + + tbb::parallel_for(range, + ComputeNodeConnectivity(tree, coordinates.get(), mOffsets.get(), mLeafNodes.size(), bbox)); + + // restore the leafnode origin coordinate + tbb::parallel_for(range, RestoreOrigin(mLeafNodes, coordinates.get())); + } + + size_t size() const { return mLeafNodes.size(); } + + std::vector& nodes() { return mLeafNodes; } + const std::vector& nodes() const { return mLeafNodes; } + + + const size_t* offsetsNextX() const { return mOffsets.get(); } + const size_t* offsetsPrevX() const { return mOffsets.get() + mLeafNodes.size(); } + + const size_t* offsetsNextY() const { return mOffsets.get() + mLeafNodes.size() * 2; } + const size_t* offsetsPrevY() const { return mOffsets.get() + mLeafNodes.size() * 3; } + + const size_t* offsetsNextZ() const { return mOffsets.get() + mLeafNodes.size() * 4; } + const size_t* offsetsPrevZ() const { return mOffsets.get() + mLeafNodes.size() * 5; } + +private: + std::vector mLeafNodes; + boost::scoped_array mOffsets; +}; // struct LeafNodeConnectivityTable + + +template +class SweepExteriorSign +{ +public: + + enum Axis { X_AXIS = 0, Y_AXIS = 1, Z_AXIS = 2 }; + + typedef typename TreeType::ValueType ValueType; + typedef typename TreeType::LeafNodeType LeafNodeType; + typedef LeafNodeConnectivityTable ConnectivityTable; + + SweepExteriorSign(Axis axis, const std::vector& startNodeIndices, ConnectivityTable& connectivity) + : mStartNodeIndices(startNodeIndices.empty() ? NULL : &startNodeIndices[0]) + , mConnectivity(&connectivity) + , mAxis(axis) + { + } + + void operator()(const tbb::blocked_range& range) const { + + std::vector& nodes = mConnectivity->nodes(); + + // Z Axis + size_t idxA = 0, idxB = 1; + Index step = 1; + + const size_t* nextOffsets = mConnectivity->offsetsNextZ(); + const size_t* prevOffsets = mConnectivity->offsetsPrevZ(); + + if (mAxis == Y_AXIS) { + + idxA = 0; + idxB = 2; + step = LeafNodeType::DIM; + + nextOffsets = mConnectivity->offsetsNextY(); + prevOffsets = mConnectivity->offsetsPrevY(); + + } else if (mAxis == X_AXIS) { + + idxA = 1; + idxB = 2; + step = LeafNodeType::DIM * LeafNodeType::DIM; + + nextOffsets = mConnectivity->offsetsNextX(); + prevOffsets = mConnectivity->offsetsPrevX(); + } + + Coord ijk(0, 0, 0); + + int& a = ijk[idxA]; + int& b = ijk[idxB]; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + size_t startOffset = mStartNodeIndices[n]; + size_t lastOffset = startOffset; + + Index pos(0); + + for (a = 0; a < int(LeafNodeType::DIM); ++a) { + for (b = 0; b < int(LeafNodeType::DIM); ++b) { + + pos = LeafNodeType::coordToOffset(ijk); + size_t offset = startOffset; + + // sweep in +axis direction until a boundary voxel is hit. + while ( offset != ConnectivityTable::INVALID_OFFSET && + traceVoxelLine(*nodes[offset], pos, step) ) { + + lastOffset = offset; + offset = nextOffsets[offset]; + } + + // find last leafnode in +axis direction + offset = lastOffset; + while (offset != ConnectivityTable::INVALID_OFFSET) { + lastOffset = offset; + offset = nextOffsets[offset]; + } + + // sweep in -axis direction until a boundary voxel is hit. + offset = lastOffset; + pos += step * (LeafNodeType::DIM - 1); + while ( offset != ConnectivityTable::INVALID_OFFSET && + traceVoxelLine(*nodes[offset], pos, -step)) { + offset = prevOffsets[offset]; + } + } + } + } + } + + + bool traceVoxelLine(LeafNodeType& node, Index pos, Index step) const { + + ValueType* data = node.buffer().data(); + + bool isOutside = true; + + for (Index i = 0; i < LeafNodeType::DIM; ++i) { + + ValueType& dist = data[pos]; + + if (dist < ValueType(0.0)) { + isOutside = true; + } else { + // Boundary voxel check. (Voxel that intersects the surface) + if (!(dist > ValueType(0.75))) isOutside = false; + + if (isOutside) dist = ValueType(-dist); + } + + pos += step; + } + + return isOutside; + } + + +private: + size_t const * const mStartNodeIndices; + ConnectivityTable * const mConnectivity; + + const Axis mAxis; +}; // class SweepExteriorSign + + +template +inline void +seedFill(LeafNodeType& node) +{ + typedef typename LeafNodeType::ValueType ValueType; + typedef std::deque Queue; + + + ValueType* data = node.buffer().data(); + + // find seed points + Queue seedPoints; + for (Index pos = 0; pos < LeafNodeType::SIZE; ++pos) { + if (data[pos] < 0.0) seedPoints.push_back(pos); + } + + if (seedPoints.empty()) return; + + // clear sign information + for (Queue::iterator it = seedPoints.begin(); it != seedPoints.end(); ++it) { + ValueType& dist = data[*it]; + dist = -dist; + } + + // flood fill + + Coord ijk(0, 0, 0); + Index pos(0), nextPos(0); + + while (!seedPoints.empty()) { + + pos = seedPoints.back(); + seedPoints.pop_back(); + + ValueType& dist = data[pos]; + + if (!(dist < ValueType(0.0))) { + + dist = -dist; // flip sign + + ijk = LeafNodeType::offsetToLocalCoord(pos); + + if (ijk[0] != 0) { // i - 1, j, k + nextPos = pos - LeafNodeType::DIM * LeafNodeType::DIM; + if (data[nextPos] > ValueType(0.75)) seedPoints.push_back(nextPos); + } + + if (ijk[0] != (LeafNodeType::DIM - 1)) { // i + 1, j, k + nextPos = pos + LeafNodeType::DIM * LeafNodeType::DIM; + if (data[nextPos] > ValueType(0.75)) seedPoints.push_back(nextPos); + } + + if (ijk[1] != 0) { // i, j - 1, k + nextPos = pos - LeafNodeType::DIM; + if (data[nextPos] > ValueType(0.75)) seedPoints.push_back(nextPos); + } + + if (ijk[1] != (LeafNodeType::DIM - 1)) { // i, j + 1, k + nextPos = pos + LeafNodeType::DIM; + if (data[nextPos] > ValueType(0.75)) seedPoints.push_back(nextPos); + } + + if (ijk[2] != 0) { // i, j, k - 1 + nextPos = pos - 1; + if (data[nextPos] > ValueType(0.75)) seedPoints.push_back(nextPos); + } + + if (ijk[2] != (LeafNodeType::DIM - 1)) { // i, j, k + 1 + nextPos = pos + 1; + if (data[nextPos] > ValueType(0.75)) seedPoints.push_back(nextPos); + } + } + } +} // seedFill() + + +template +inline bool +scanFill(LeafNodeType& node) +{ + bool updatedNode = false; + + typedef typename LeafNodeType::ValueType ValueType; + ValueType* data = node.buffer().data(); + + Coord ijk(0, 0, 0); + + bool updatedSign = true; + while (updatedSign) { + + updatedSign = false; + + for (Index pos = 0; pos < LeafNodeType::SIZE; ++pos) { + + ValueType& dist = data[pos]; + + if (!(dist < ValueType(0.0)) && dist > ValueType(0.75)) { + + ijk = LeafNodeType::offsetToLocalCoord(pos); + + // i, j, k - 1 + if (ijk[2] != 0 && data[pos - 1] < ValueType(0.0)) { + updatedSign = true; + dist = ValueType(-dist); + + // i, j, k + 1 + } else if (ijk[2] != (LeafNodeType::DIM - 1) && data[pos + 1] < ValueType(0.0)) { + updatedSign = true; + dist = ValueType(-dist); + + // i, j - 1, k + } else if (ijk[1] != 0 && data[pos - LeafNodeType::DIM] < ValueType(0.0)) { + updatedSign = true; + dist = ValueType(-dist); + + // i, j + 1, k + } else if (ijk[1] != (LeafNodeType::DIM - 1) && data[pos + LeafNodeType::DIM] < ValueType(0.0)) { + updatedSign = true; + dist = ValueType(-dist); + + // i - 1, j, k + } else if (ijk[0] != 0 && data[pos - LeafNodeType::DIM * LeafNodeType::DIM] < ValueType(0.0)) { + updatedSign = true; + dist = ValueType(-dist); + + // i + 1, j, k + } else if (ijk[0] != (LeafNodeType::DIM - 1) && data[pos + LeafNodeType::DIM * LeafNodeType::DIM] < ValueType(0.0)) { + updatedSign = true; + dist = ValueType(-dist); + } + } + } // end value loop + + updatedNode |= updatedSign; + } // end update loop + + return updatedNode; +} // scanFill() + + +template +class SeedFillExteriorSign +{ +public: + typedef typename TreeType::ValueType ValueType; + typedef typename TreeType::LeafNodeType LeafNodeType; + + SeedFillExteriorSign(std::vector& nodes, bool* changedNodeMask) + : mNodes(nodes.empty() ? NULL : &nodes[0]) + , mChangedNodeMask(changedNodeMask) + { + } + + void operator()(const tbb::blocked_range& range) const { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + if (mChangedNodeMask[n]) { + //seedFill(*mNodes[n]); + mChangedNodeMask[n] = scanFill(*mNodes[n]); + } + } + } + + LeafNodeType ** const mNodes; + bool * const mChangedNodeMask; +}; + + +template +struct FillArray +{ + FillArray(ValueType* array, const ValueType v) : mArray(array), mValue(v) { } + + void operator()(const tbb::blocked_range& range) const { + const ValueType v = mValue; + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + mArray[n] = v; + } + } + + ValueType * const mArray; + const ValueType mValue; +}; + + +template +inline void +fillArray(ValueType* array, const ValueType val, const size_t length) +{ + const size_t grainSize = length / tbb::task_scheduler_init::default_num_threads(); + const tbb::blocked_range range(0, length, grainSize); + tbb::parallel_for(range, FillArray(array, val), tbb::simple_partitioner()); +} + + +template +class SyncVoxelMask +{ +public: + typedef typename TreeType::ValueType ValueType; + typedef typename TreeType::LeafNodeType LeafNodeType; + + SyncVoxelMask(std::vector& nodes, const bool* changedNodeMask, bool* changedVoxelMask) + : mNodes(nodes.empty() ? NULL : &nodes[0]) + , mChangedNodeMask(changedNodeMask) + , mChangedVoxelMask(changedVoxelMask) + { + } + + void operator()(const tbb::blocked_range& range) const { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + if (mChangedNodeMask[n]) { + bool* mask = &mChangedVoxelMask[n * LeafNodeType::SIZE]; + + ValueType* data = mNodes[n]->buffer().data(); + + for (Index pos = 0; pos < LeafNodeType::SIZE; ++pos) { + if (mask[pos]) { + data[pos] = ValueType(-data[pos]); + mask[pos] = false; + } + } + } + } + } + + LeafNodeType ** const mNodes; + bool const * const mChangedNodeMask; + bool * const mChangedVoxelMask; +}; + + +template +class SeedPoints +{ +public: + typedef typename TreeType::ValueType ValueType; + typedef typename TreeType::LeafNodeType LeafNodeType; + typedef LeafNodeConnectivityTable ConnectivityTable; + + SeedPoints(ConnectivityTable& connectivity, bool* changedNodeMask, bool* nodeMask, bool* changedVoxelMask) + : mConnectivity(&connectivity) + , mChangedNodeMask(changedNodeMask) + , mNodeMask(nodeMask) + , mChangedVoxelMask(changedVoxelMask) + { + } + + void operator()(const tbb::blocked_range& range) const { + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + if (!mChangedNodeMask[n]) { + + bool changedValue = false; + + changedValue |= processZ(n, /*firstFace=*/true); + changedValue |= processZ(n, /*firstFace=*/false); + + changedValue |= processY(n, /*firstFace=*/true); + changedValue |= processY(n, /*firstFace=*/false); + + changedValue |= processX(n, /*firstFace=*/true); + changedValue |= processX(n, /*firstFace=*/false); + + mNodeMask[n] = changedValue; + } + } + } + + + bool processZ(const size_t n, bool firstFace) const + { + const size_t offset = firstFace ? mConnectivity->offsetsPrevZ()[n] : mConnectivity->offsetsNextZ()[n]; + if (offset != ConnectivityTable::INVALID_OFFSET && mChangedNodeMask[offset]) { + + bool* mask = &mChangedVoxelMask[n * LeafNodeType::SIZE]; + + const ValueType* lhsData = mConnectivity->nodes()[n]->buffer().data(); + const ValueType* rhsData = mConnectivity->nodes()[offset]->buffer().data(); + + const Index lastOffset = LeafNodeType::DIM - 1; + const Index lhsOffset = firstFace ? 0 : lastOffset, rhsOffset = firstFace ? lastOffset : 0; + + Index tmpPos(0), pos(0); + bool changedValue = false; + + for (Index x = 0; x < LeafNodeType::DIM; ++x) { + tmpPos = x << (2 * LeafNodeType::LOG2DIM); + for (Index y = 0; y < LeafNodeType::DIM; ++y) { + pos = tmpPos + (y << LeafNodeType::LOG2DIM); + + if (lhsData[pos + lhsOffset] > ValueType(0.75)) { + if (rhsData[pos + rhsOffset] < ValueType(0.0)) { + changedValue = true; + mask[pos + lhsOffset] = true; + } + } + } + } + + return changedValue; + } + + return false; + } + + bool processY(const size_t n, bool firstFace) const + { + const size_t offset = firstFace ? mConnectivity->offsetsPrevY()[n] : mConnectivity->offsetsNextY()[n]; + if (offset != ConnectivityTable::INVALID_OFFSET && mChangedNodeMask[offset]) { + + bool* mask = &mChangedVoxelMask[n * LeafNodeType::SIZE]; + + const ValueType* lhsData = mConnectivity->nodes()[n]->buffer().data(); + const ValueType* rhsData = mConnectivity->nodes()[offset]->buffer().data(); + + const Index lastOffset = LeafNodeType::DIM * (LeafNodeType::DIM - 1); + const Index lhsOffset = firstFace ? 0 : lastOffset, rhsOffset = firstFace ? lastOffset : 0; + + Index tmpPos(0), pos(0); + bool changedValue = false; + + for (Index x = 0; x < LeafNodeType::DIM; ++x) { + tmpPos = x << (2 * LeafNodeType::LOG2DIM); + for (Index z = 0; z < LeafNodeType::DIM; ++z) { + pos = tmpPos + z; + + if (lhsData[pos + lhsOffset] > ValueType(0.75)) { + if (rhsData[pos + rhsOffset] < ValueType(0.0)) { + changedValue = true; + mask[pos + lhsOffset] = true; + } + } + } + } + + return changedValue; + } + + return false; + } + + bool processX(const size_t n, bool firstFace) const + { + const size_t offset = firstFace ? mConnectivity->offsetsPrevX()[n] : mConnectivity->offsetsNextX()[n]; + if (offset != ConnectivityTable::INVALID_OFFSET && mChangedNodeMask[offset]) { + + bool* mask = &mChangedVoxelMask[n * LeafNodeType::SIZE]; + + const ValueType* lhsData = mConnectivity->nodes()[n]->buffer().data(); + const ValueType* rhsData = mConnectivity->nodes()[offset]->buffer().data(); + + const Index lastOffset = LeafNodeType::DIM * LeafNodeType::DIM * (LeafNodeType::DIM - 1); + const Index lhsOffset = firstFace ? 0 : lastOffset, rhsOffset = firstFace ? lastOffset : 0; + + Index tmpPos(0), pos(0); + bool changedValue = false; + + for (Index y = 0; y < LeafNodeType::DIM; ++y) { + tmpPos = y << LeafNodeType::LOG2DIM; + for (Index z = 0; z < LeafNodeType::DIM; ++z) { + pos = tmpPos + z; + + if (lhsData[pos + lhsOffset] > ValueType(0.75)) { + if (rhsData[pos + rhsOffset] < ValueType(0.0)) { + changedValue = true; + mask[pos + lhsOffset] = true; + } + } + } + } + + return changedValue; + } + + return false; + } + + ConnectivityTable * const mConnectivity; + bool * const mChangedNodeMask; + bool * const mNodeMask; + bool * const mChangedVoxelMask; +}; + + +//////////////////////////////////////// + +template +struct ComputeIntersectingVoxelSign +{ + typedef typename TreeType::ValueType ValueType; + typedef typename TreeType::LeafNodeType LeafNodeType; + typedef typename TreeType::template ValueConverter::Type Int32TreeType; + typedef typename Int32TreeType::LeafNodeType Int32LeafNodeType; + + typedef std::pair, boost::shared_array > LocalData; + typedef tbb::enumerable_thread_specific LocalDataTable; + + ComputeIntersectingVoxelSign( + std::vector& distNodes, + const TreeType& distTree, + const Int32TreeType& indexTree, + const MeshDataAdapter& mesh) + : mDistNodes(distNodes.empty() ? NULL : &distNodes[0]) + , mDistTree(&distTree) + , mIndexTree(&indexTree) + , mMesh(&mesh) + , mLocalDataTable(new LocalDataTable()) + { + } + + + void operator()(const tbb::blocked_range& range) const { + + tree::ValueAccessor distAcc(*mDistTree); + tree::ValueAccessor idxAcc(*mIndexTree); + + ValueType nval; + CoordBBox bbox; + Index xPos(0), yPos(0); + Coord ijk, nijk, nodeMin, nodeMax; + Vec3d cp, xyz, nxyz, dir1, dir2; + + LocalData& localData = mLocalDataTable->local(); + + boost::shared_array& points = localData.first; + if (!points) points.reset(new Vec3d[LeafNodeType::SIZE * 2]); + + boost::shared_array& mask = localData.second; + if (!mask) mask.reset(new bool[LeafNodeType::SIZE]); + + + typename LeafNodeType::ValueOnCIter it; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + LeafNodeType& node = *mDistNodes[n]; + ValueType* data = node.buffer().data(); + + const Int32LeafNodeType* idxNode = idxAcc.probeConstLeaf(node.origin()); + const Int32* idxData = idxNode->buffer().data(); + + nodeMin = node.origin(); + nodeMax = nodeMin.offsetBy(LeafNodeType::DIM - 1); + + // reset computed voxel mask. + memset(mask.get(), 0, sizeof(bool) * LeafNodeType::SIZE); + + for (it = node.cbeginValueOn(); it; ++it) { + Index pos = it.pos(); + + ValueType& dist = data[pos]; + if (dist < 0.0 || dist > 0.75) continue; + + ijk = node.offsetToGlobalCoord(pos); + + xyz[0] = double(ijk[0]); + xyz[1] = double(ijk[1]); + xyz[2] = double(ijk[2]); + + + bbox.min() = Coord::maxComponent(ijk.offsetBy(-1), nodeMin); + bbox.max() = Coord::minComponent(ijk.offsetBy(1), nodeMax); + + bool flipSign = false; + + for (nijk[0] = bbox.min()[0]; nijk[0] <= bbox.max()[0] && !flipSign; ++nijk[0]) { + xPos = (nijk[0] & (LeafNodeType::DIM - 1u)) << (2 * LeafNodeType::LOG2DIM); + for (nijk[1] = bbox.min()[1]; nijk[1] <= bbox.max()[1] && !flipSign; ++nijk[1]) { + yPos = xPos + ((nijk[1] & (LeafNodeType::DIM - 1u)) << LeafNodeType::LOG2DIM); + for (nijk[2] = bbox.min()[2]; nijk[2] <= bbox.max()[2]; ++nijk[2]) { + pos = yPos + (nijk[2] & (LeafNodeType::DIM - 1u)); + + const Int32& polyIdx = idxData[pos]; + + if (polyIdx == Int32(util::INVALID_IDX) || !(data[pos] < -0.75)) continue; + + const Index pointIndex = pos * 2; + + if (!mask[pos]) { + + mask[pos] = true; + + nxyz[0] = double(nijk[0]); + nxyz[1] = double(nijk[1]); + nxyz[2] = double(nijk[2]); + + Vec3d& point = points[pointIndex]; + + point = closestPoint(nxyz, polyIdx); + + Vec3d& direction = points[pointIndex + 1]; + direction = nxyz - point; + direction.normalize(); + } + + dir1 = xyz - points[pointIndex]; + dir1.normalize(); + + if (points[pointIndex + 1].dot(dir1) > 0.0) { + flipSign = true; + break; + } + } + } + } + + if (flipSign) { + dist = -dist; + } else { + for (Int32 m = 0; m < 26; ++m) { + nijk = ijk + util::COORD_OFFSETS[m]; + + if (!bbox.isInside(nijk) && distAcc.probeValue(nijk, nval) && nval < -0.75) { + nxyz[0] = double(nijk[0]); + nxyz[1] = double(nijk[1]); + nxyz[2] = double(nijk[2]); + + cp = closestPoint(nxyz, idxAcc.getValue(nijk)); + + dir1 = xyz - cp; + dir1.normalize(); + + dir2 = nxyz - cp; + dir2.normalize(); + + if (dir2.dot(dir1) > 0.0) { + dist = -dist; + break; + } + } + } + } + + } // active voxel loop + } // leaf node loop + } + +private: + + Vec3d closestPoint(const Vec3d& center, Int32 polyIdx) const + { + Vec3d a, b, c, cp, uvw; + + const size_t polygon = size_t(polyIdx); + mMesh->getIndexSpacePoint(polygon, 0, a); + mMesh->getIndexSpacePoint(polygon, 1, b); + mMesh->getIndexSpacePoint(polygon, 2, c); + + cp = closestPointOnTriangleToPoint(a, c, b, center, uvw); + + if (4 == mMesh->vertexCount(polygon)) { + + mMesh->getIndexSpacePoint(polygon, 3, b); + + c = closestPointOnTriangleToPoint(a, b, c, center, uvw); + + if ((center - c).lengthSqr() < (center - cp).lengthSqr()) { + cp = c; + } + } + + return cp; + } + + + LeafNodeType ** const mDistNodes; + TreeType const * const mDistTree; + Int32TreeType const * const mIndexTree; + MeshDataAdapter const * const mMesh; + + boost::shared_ptr mLocalDataTable; +}; // ComputeIntersectingVoxelSign + + +//////////////////////////////////////// + + +template +inline void +maskNodeInternalNeighbours(const Index pos, bool (&mask)[26]) +{ + typedef LeafNodeType NodeT; + + const Coord ijk = NodeT::offsetToLocalCoord(pos); + + // Face adjacent neighbours + // i+1, j, k + mask[0] = ijk[0] != (NodeT::DIM - 1); + // i-1, j, k + mask[1] = ijk[0] != 0; + // i, j+1, k + mask[2] = ijk[1] != (NodeT::DIM - 1); + // i, j-1, k + mask[3] = ijk[1] != 0; + // i, j, k+1 + mask[4] = ijk[2] != (NodeT::DIM - 1); + // i, j, k-1 + mask[5] = ijk[2] != 0; + + // Edge adjacent neighbour + // i+1, j, k-1 + mask[6] = mask[0] && mask[5]; + // i-1, j, k-1 + mask[7] = mask[1] && mask[5]; + // i+1, j, k+1 + mask[8] = mask[0] && mask[4]; + // i-1, j, k+1 + mask[9] = mask[1] && mask[4]; + // i+1, j+1, k + mask[10] = mask[0] && mask[2]; + // i-1, j+1, k + mask[11] = mask[1] && mask[2]; + // i+1, j-1, k + mask[12] = mask[0] && mask[3]; + // i-1, j-1, k + mask[13] = mask[1] && mask[3]; + // i, j-1, k+1 + mask[14] = mask[3] && mask[4]; + // i, j-1, k-1 + mask[15] = mask[3] && mask[5]; + // i, j+1, k+1 + mask[16] = mask[2] && mask[4]; + // i, j+1, k-1 + mask[17] = mask[2] && mask[5]; + + // Corner adjacent neighbours + // i-1, j-1, k-1 + mask[18] = mask[1] && mask[3] && mask[5]; + // i-1, j-1, k+1 + mask[19] = mask[1] && mask[3] && mask[4]; + // i+1, j-1, k+1 + mask[20] = mask[0] && mask[3] && mask[4]; + // i+1, j-1, k-1 + mask[21] = mask[0] && mask[3] && mask[5]; + // i-1, j+1, k-1 + mask[22] = mask[1] && mask[2] && mask[5]; + // i-1, j+1, k+1 + mask[23] = mask[1] && mask[2] && mask[4]; + // i+1, j+1, k+1 + mask[24] = mask[0] && mask[2] && mask[4]; + // i+1, j+1, k-1 + mask[25] = mask[0] && mask[2] && mask[5]; +} + + +template +inline bool +checkNeighbours(const Index pos, const typename LeafNodeType::ValueType * data, bool (&mask)[26]) +{ + typedef LeafNodeType NodeT; + + // i, j, k - 1 + if (mask[5] && Compare::check(data[pos - 1])) return true; + // i, j, k + 1 + if (mask[4] && Compare::check(data[pos + 1])) return true; + // i, j - 1, k + if (mask[3] && Compare::check(data[pos - NodeT::DIM])) return true; + // i, j + 1, k + if (mask[2] && Compare::check(data[pos + NodeT::DIM])) return true; + // i - 1, j, k + if (mask[1] && Compare::check(data[pos - NodeT::DIM * NodeT::DIM])) return true; + // i + 1, j, k + if (mask[0] && Compare::check(data[pos + NodeT::DIM * NodeT::DIM])) return true; + // i+1, j, k-1 + if (mask[6] && Compare::check(data[pos + NodeT::DIM * NodeT::DIM])) return true; + // i-1, j, k-1 + if (mask[7] && Compare::check(data[pos - NodeT::DIM * NodeT::DIM - 1])) return true; + // i+1, j, k+1 + if (mask[8] && Compare::check(data[pos + NodeT::DIM * NodeT::DIM + 1])) return true; + // i-1, j, k+1 + if (mask[9] && Compare::check(data[pos - NodeT::DIM * NodeT::DIM + 1])) return true; + // i+1, j+1, k + if (mask[10] && Compare::check(data[pos + NodeT::DIM * NodeT::DIM + NodeT::DIM])) return true; + // i-1, j+1, k + if (mask[11] && Compare::check(data[pos - NodeT::DIM * NodeT::DIM + NodeT::DIM])) return true; + // i+1, j-1, k + if (mask[12] && Compare::check(data[pos + NodeT::DIM * NodeT::DIM - NodeT::DIM])) return true; + // i-1, j-1, k + if (mask[13] && Compare::check(data[pos - NodeT::DIM * NodeT::DIM - NodeT::DIM])) return true; + // i, j-1, k+1 + if (mask[14] && Compare::check(data[pos - NodeT::DIM + 1])) return true; + // i, j-1, k-1 + if (mask[15] && Compare::check(data[pos - NodeT::DIM - 1])) return true; + // i, j+1, k+1 + if (mask[16] && Compare::check(data[pos + NodeT::DIM + 1])) return true; + // i, j+1, k-1 + if (mask[17] && Compare::check(data[pos + NodeT::DIM - 1])) return true; + // i-1, j-1, k-1 + if (mask[18] && Compare::check(data[pos - NodeT::DIM * NodeT::DIM - NodeT::DIM - 1])) return true; + // i-1, j-1, k+1 + if (mask[19] && Compare::check(data[pos - NodeT::DIM * NodeT::DIM - NodeT::DIM + 1])) return true; + // i+1, j-1, k+1 + if (mask[20] && Compare::check(data[pos + NodeT::DIM * NodeT::DIM - NodeT::DIM + 1])) return true; + // i+1, j-1, k-1 + if (mask[21] && Compare::check(data[pos + NodeT::DIM * NodeT::DIM - NodeT::DIM - 1])) return true; + // i-1, j+1, k-1 + if (mask[22] && Compare::check(data[pos - NodeT::DIM * NodeT::DIM + NodeT::DIM - 1])) return true; + // i-1, j+1, k+1 + if (mask[23] && Compare::check(data[pos - NodeT::DIM * NodeT::DIM + NodeT::DIM + 1])) return true; + // i+1, j+1, k+1 + if (mask[24] && Compare::check(data[pos + NodeT::DIM * NodeT::DIM + NodeT::DIM + 1])) return true; + // i+1, j+1, k-1 + if (mask[25] && Compare::check(data[pos + NodeT::DIM * NodeT::DIM + NodeT::DIM - 1])) return true; + + return false; +} + + +template +inline bool +checkNeighbours(const Coord& ijk, AccessorType& acc, bool (&mask)[26]) +{ + for (Int32 m = 0; m < 26; ++m) { + if (!mask[m] && Compare::check(acc.getValue(ijk + util::COORD_OFFSETS[m]))) { + return true; + } + } + + return false; +} + + +template +struct ValidateIntersectingVoxels +{ + typedef typename TreeType::ValueType ValueType; + typedef typename TreeType::LeafNodeType LeafNodeType; + + struct IsNegative { static bool check(const ValueType v) { return v < ValueType(0.0); } }; + + ValidateIntersectingVoxels(TreeType& tree, std::vector& nodes) + : mTree(&tree) + , mNodes(nodes.empty() ? NULL : &nodes[0]) + { + } + + void operator()(const tbb::blocked_range& range) const + { + tree::ValueAccessor acc(*mTree); + bool neighbourMask[26]; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + LeafNodeType& node = *mNodes[n]; + ValueType* data = node.buffer().data(); + + typename LeafNodeType::ValueOnCIter it; + for (it = node.cbeginValueOn(); it; ++it) { + + const Index pos = it.pos(); + + ValueType& dist = data[pos]; + if (dist < 0.0 || dist > 0.75) continue; + + // Mask node internal neighbours + maskNodeInternalNeighbours(pos, neighbourMask); + + const bool hasNegativeNeighbour = + checkNeighbours(pos, data, neighbourMask) || + checkNeighbours(node.offsetToGlobalCoord(pos), acc, neighbourMask); + + if (!hasNegativeNeighbour) { + // push over boundary voxel distance + dist = ValueType(0.75) + Tolerance::epsilon(); + } + } + } + } + + TreeType * const mTree; + LeafNodeType ** const mNodes; +}; // ValidateIntersectingVoxels + + +template +struct RemoveSelfIntersectingSurface +{ + typedef typename TreeType::ValueType ValueType; + typedef typename TreeType::LeafNodeType LeafNodeType; + typedef typename TreeType::template ValueConverter::Type Int32TreeType; + + struct Comp { static bool check(const ValueType v) { return !(v > ValueType(0.75)); } }; + + RemoveSelfIntersectingSurface(std::vector& nodes, + TreeType& distTree, Int32TreeType& indexTree) + : mNodes(nodes.empty() ? NULL : &nodes[0]) + , mDistTree(&distTree) + , mIndexTree(&indexTree) + { + } + + void operator()(const tbb::blocked_range& range) const + { + tree::ValueAccessor distAcc(*mDistTree); + tree::ValueAccessor idxAcc(*mIndexTree); + bool neighbourMask[26]; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + LeafNodeType& distNode = *mNodes[n]; + ValueType* data = distNode.buffer().data(); + + typename Int32TreeType::LeafNodeType* idxNode = + idxAcc.probeLeaf(distNode.origin()); + + typename LeafNodeType::ValueOnCIter it; + for (it = distNode.cbeginValueOn(); it; ++it) { + + const Index pos = it.pos(); + + if (!(data[pos] > 0.75)) continue; + + // Mask node internal neighbours + maskNodeInternalNeighbours(pos, neighbourMask); + + const bool hasBoundaryNeighbour = + checkNeighbours(pos, data, neighbourMask) || + checkNeighbours(distNode.offsetToGlobalCoord(pos), distAcc, neighbourMask); + + if (!hasBoundaryNeighbour) { + distNode.setValueOff(pos); + idxNode->setValueOff(pos); + } + } + } + } + + LeafNodeType * * const mNodes; + TreeType * const mDistTree; + Int32TreeType * const mIndexTree; +}; // RemoveSelfIntersectingSurface + + +//////////////////////////////////////// + + +template +struct ReleaseChildNodes +{ + ReleaseChildNodes(NodeType ** nodes) : mNodes(nodes) {} + + void operator()(const tbb::blocked_range& range) const { + + typedef typename NodeType::NodeMaskType NodeMaskType; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + const_cast(mNodes[n]->getChildMask()).setOff(); + } + } + + NodeType ** const mNodes; +}; + + +template +inline void +releaseLeafNodes(TreeType& tree) +{ + typedef typename TreeType::RootNodeType RootNodeType; + typedef typename RootNodeType::NodeChainType NodeChainType; + typedef typename boost::mpl::at >::type InternalNodeType; + + std::vector nodes; + tree.getNodes(nodes); + + tbb::parallel_for(tbb::blocked_range(0, nodes.size()), + ReleaseChildNodes(nodes.empty() ? NULL : &nodes[0])); +} + + +template +struct StealUniqueLeafNodes +{ + typedef typename TreeType::LeafNodeType LeafNodeType; + + StealUniqueLeafNodes(TreeType& lhsTree, TreeType& rhsTree, + std::vector& overlappingNodes) + : mLhsTree(&lhsTree) + , mRhsTree(&rhsTree) + , mNodes(&overlappingNodes) + { + } + + void operator()() const { + + std::vector rhsLeafNodes; + + rhsLeafNodes.reserve(mRhsTree->leafCount()); + //mRhsTree->getNodes(rhsLeafNodes); + //releaseLeafNodes(*mRhsTree); + mRhsTree->stealNodes(rhsLeafNodes); + + tree::ValueAccessor acc(*mLhsTree); + + for (size_t n = 0, N = rhsLeafNodes.size(); n < N; ++n) { + if (!acc.probeLeaf(rhsLeafNodes[n]->origin())) { + acc.addLeaf(rhsLeafNodes[n]); + } else { + mNodes->push_back(rhsLeafNodes[n]); + } + } + } + +private: + TreeType * const mLhsTree; + TreeType * const mRhsTree; + std::vector * const mNodes; +}; + + +template +inline void +combineData(DistTreeType& lhsDist, IndexTreeType& lhsIdx, + DistTreeType& rhsDist, IndexTreeType& rhsIdx) +{ + typedef typename DistTreeType::LeafNodeType DistLeafNodeType; + typedef typename IndexTreeType::LeafNodeType IndexLeafNodeType; + + std::vector overlappingDistNodes; + std::vector overlappingIdxNodes; + + // Steal unique leafnodes + tbb::task_group tasks; + tasks.run(StealUniqueLeafNodes(lhsDist, rhsDist, overlappingDistNodes)); + tasks.run(StealUniqueLeafNodes(lhsIdx, rhsIdx, overlappingIdxNodes)); + tasks.wait(); + + // Combine overlapping leaf nodes + if (!overlappingDistNodes.empty() && !overlappingIdxNodes.empty()) { + tbb::parallel_for(tbb::blocked_range(0, overlappingDistNodes.size()), + CombineLeafNodes(lhsDist, lhsIdx, &overlappingDistNodes[0], &overlappingIdxNodes[0])); + } +} + +/// @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 (masks the mesh intersecting voxels) +/// @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 +struct VoxelizationData { + + typedef boost::scoped_ptr Ptr; + typedef typename TreeType::ValueType ValueType; + + typedef typename TreeType::template ValueConverter::Type Int32TreeType; + typedef typename TreeType::template ValueConverter::Type UCharTreeType; + + typedef tree::ValueAccessor FloatTreeAcc; + typedef tree::ValueAccessor Int32TreeAcc; + typedef tree::ValueAccessor UCharTreeAcc; + + + VoxelizationData() + : distTree(std::numeric_limits::max()) + , distAcc(distTree) + , indexTree(Int32(util::INVALID_IDX)) + , indexAcc(indexTree) + , primIdTree(MaxPrimId) + , primIdAcc(primIdTree) + , mPrimCount(0) + { + } + + TreeType distTree; + FloatTreeAcc distAcc; + + Int32TreeType indexTree; + Int32TreeAcc indexAcc; + + UCharTreeType primIdTree; + UCharTreeAcc primIdAcc; + + unsigned char getNewPrimId() { + + if (mPrimCount == MaxPrimId || primIdTree.leafCount() > 1000) { + mPrimCount = 0; + primIdTree.clear(); + } + + return mPrimCount++; + } + +private: + + enum { MaxPrimId = 100 }; + + unsigned char mPrimCount; +}; + + +template +class VoxelizePolygons +{ +public: + + typedef VoxelizationData VoxelizationDataType; + typedef tbb::enumerable_thread_specific DataTable; + + VoxelizePolygons(DataTable& dataTable, + const MeshDataAdapter& mesh, + Interrupter* interrupter = NULL) + : mDataTable(&dataTable) + , mMesh(&mesh) + , mInterrupter(interrupter) + { + } + + void operator()(const tbb::blocked_range& range) const { + + typename VoxelizationDataType::Ptr& dataPtr = mDataTable->local(); + if (!dataPtr) dataPtr.reset(new VoxelizationDataType()); + + Triangle prim; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + if (this->wasInterrupted()) { + tbb::task::self().cancel_group_execution(); + break; + } + + const size_t numVerts = mMesh->vertexCount(n); + + // rasterize triangles and quads. + if (numVerts == 3 || numVerts == 4) { + + prim.index = Int32(n); + + mMesh->getIndexSpacePoint(n, 0, prim.a); + mMesh->getIndexSpacePoint(n, 1, prim.b); + mMesh->getIndexSpacePoint(n, 2, prim.c); + + evalTriangle(prim, *dataPtr); + + if (numVerts == 4) { + mMesh->getIndexSpacePoint(n, 3, prim.b); + evalTriangle(prim, *dataPtr); + } + } + } + } + +private: + + bool wasInterrupted() const { return mInterrupter && mInterrupter->wasInterrupted(); } + + struct Triangle { Vec3d a, b, c; Int32 index; }; + + struct SubTask + { + enum { POLYGON_LIMIT = 1000 }; + + SubTask(const Triangle& prim, DataTable& dataTable, int subdivisionCount, size_t polygonCount) + : mLocalDataTable(&dataTable) + , mPrim(prim) + , mSubdivisionCount(subdivisionCount) + , mPolygonCount(polygonCount) + { + } + + void operator()() const + { + if (mSubdivisionCount <= 0 || mPolygonCount >= POLYGON_LIMIT) { + + typename VoxelizationDataType::Ptr& dataPtr = mLocalDataTable->local(); + if (!dataPtr) dataPtr.reset(new VoxelizationDataType()); + + voxelizeTriangle(mPrim, *dataPtr); + + } else { + spawnTasks(mPrim, *mLocalDataTable, mSubdivisionCount, mPolygonCount); + } + } + + DataTable * const mLocalDataTable; + Triangle const mPrim; + int const mSubdivisionCount; + size_t const mPolygonCount; + }; // struct SubTask + + inline static int evalSubdivisionCount(const Triangle& prim) + { + const double ax = prim.a[0], bx = prim.b[0], cx = prim.c[0]; + const double dx = std::max(ax, std::max(bx, cx)) - std::min(ax, std::min(bx, cx)); + + const double ay = prim.a[1], by = prim.b[1], cy = prim.c[1]; + const double dy = std::max(ay, std::max(by, cy)) - std::min(ay, std::min(by, cy)); + + const double az = prim.a[2], bz = prim.b[2], cz = prim.c[2]; + const double dz = std::max(az, std::max(bz, cz)) - std::min(az, std::min(bz, cz)); + + return int(std::max(dx, std::max(dy, dz)) / double(TreeType::LeafNodeType::DIM * 2)); + } + + void evalTriangle(const Triangle& prim, VoxelizationDataType& data) const + { + const size_t polygonCount = mMesh->polygonCount(); + const int subdivisionCount = polygonCount < SubTask::POLYGON_LIMIT ? evalSubdivisionCount(prim) : 0; + + if (subdivisionCount <= 0) { + voxelizeTriangle(prim, data); + } else { + spawnTasks(prim, *mDataTable, subdivisionCount, polygonCount); + } + } + + static void spawnTasks( + const Triangle& mainPrim, DataTable& dataTable, int subdivisionCount, size_t polygonCount) + { + subdivisionCount -= 1; + polygonCount *= 4; + + tbb::task_group tasks; + + const Vec3d ac = (mainPrim.a + mainPrim.c) * 0.5; + const Vec3d bc = (mainPrim.b + mainPrim.c) * 0.5; + const Vec3d ab = (mainPrim.a + mainPrim.b) * 0.5; + + Triangle prim; + prim.index = mainPrim.index; + + prim.a = mainPrim.a; + prim.b = ab; + prim.c = ac; + tasks.run(SubTask(prim, dataTable, subdivisionCount, polygonCount)); + + prim.a = ab; + prim.b = bc; + prim.c = ac; + tasks.run(SubTask(prim, dataTable, subdivisionCount, polygonCount)); + + prim.a = ab; + prim.b = mainPrim.b; + prim.c = bc; + tasks.run(SubTask(prim, dataTable, subdivisionCount, polygonCount)); + + prim.a = ac; + prim.b = bc; + prim.c = mainPrim.c; + tasks.run(SubTask(prim, dataTable, subdivisionCount, polygonCount)); + + tasks.wait(); + } + + static void voxelizeTriangle(const Triangle& prim, VoxelizationDataType& data) + { + std::deque coordList; + Coord ijk, nijk; + + ijk = Coord::floor(prim.a); + coordList.push_back(ijk); + + computeDistance(ijk, prim, data); + + unsigned char primId = data.getNewPrimId(); + data.primIdAcc.setValueOnly(ijk, primId); + + while (!coordList.empty()) { + ijk = coordList.back(); + coordList.pop_back(); + + for (Int32 i = 0; i < 26; ++i) { + nijk = ijk + util::COORD_OFFSETS[i]; + if (primId != data.primIdAcc.getValue(nijk)) { + data.primIdAcc.setValueOnly(nijk, primId); + if(computeDistance(nijk, prim, data)) coordList.push_back(nijk); + } + } + } + } + + static bool computeDistance(const Coord& ijk, const Triangle& prim, VoxelizationDataType& data) + { + Vec3d uvw, voxelCenter(ijk[0], ijk[1], ijk[2]); + + typedef typename TreeType::ValueType ValueType; + + const ValueType dist = ValueType((voxelCenter - + closestPointOnTriangleToPoint(prim.a, prim.c, prim.b, voxelCenter, uvw)).lengthSqr()); + + const ValueType oldDist = data.distAcc.getValue(ijk); + + if (dist < oldDist) { + data.distAcc.setValue(ijk, dist); + data.indexAcc.setValue(ijk, prim.index); + } else if (math::isExactlyEqual(dist, oldDist)) { + // makes reduction deterministic when different polygons + // produce the same distance value. + data.indexAcc.setValueOnly(ijk, std::min(prim.index, data.indexAcc.getValue(ijk))); + } + + return !(dist > 0.75); // true if the primitive intersects the voxel. + } + + DataTable * const mDataTable; + MeshDataAdapter const * const mMesh; + Interrupter * const mInterrupter; +}; // VoxelizePolygons + + +//////////////////////////////////////// + + +template +struct DiffLeafNodeMask +{ + typedef typename tree::ValueAccessor AccessorType; + typedef typename TreeType::LeafNodeType LeafNodeType; + + typedef typename TreeType::template ValueConverter::Type BoolTreeType; + typedef typename BoolTreeType::LeafNodeType BoolLeafNodeType; + + DiffLeafNodeMask(const TreeType& rhsTree, + std::vector& lhsNodes) + : mRhsTree(&rhsTree), mLhsNodes(lhsNodes.empty() ? NULL : &lhsNodes[0]) + { + } + + void operator()(const tbb::blocked_range& range) const { + + tree::ValueAccessor acc(*mRhsTree); + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + BoolLeafNodeType* lhsNode = mLhsNodes[n]; + const LeafNodeType* rhsNode = acc.probeConstLeaf(lhsNode->origin()); + + if (rhsNode) lhsNode->topologyDifference(*rhsNode, false); + } + } + +private: + TreeType const * const mRhsTree; + BoolLeafNodeType ** const mLhsNodes; +}; + + +template +struct UnionValueMasks +{ + UnionValueMasks(std::vector& nodesA, std::vector& nodesB) + : mNodesA(nodesA.empty() ? NULL : &nodesA[0]) + , mNodesB(nodesB.empty() ? NULL : &nodesB[0]) + { + } + + void operator()(const tbb::blocked_range& range) const { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + mNodesA[n]->topologyUnion(*mNodesB[n]); + } + } + +private: + LeafNodeTypeA ** const mNodesA; + LeafNodeTypeB ** const mNodesB; +}; + + +template +struct ConstructVoxelMask +{ + typedef typename TreeType::LeafNodeType LeafNodeType; + + typedef typename TreeType::template ValueConverter::Type BoolTreeType; + typedef typename BoolTreeType::LeafNodeType BoolLeafNodeType; + + ConstructVoxelMask(BoolTreeType& maskTree, const TreeType& tree, std::vector& nodes) + : mTree(&tree) + , mNodes(nodes.empty() ? NULL : &nodes[0]) + , mLocalMaskTree(false) + , mMaskTree(&maskTree) + { + } + + ConstructVoxelMask(ConstructVoxelMask& rhs, tbb::split) + : mTree(rhs.mTree) + , mNodes(rhs.mNodes) + , mLocalMaskTree(false) + , mMaskTree(&mLocalMaskTree) + { + } + + void operator()(const tbb::blocked_range& range) + { + typedef typename LeafNodeType::ValueOnCIter Iterator; + + tree::ValueAccessor acc(*mTree); + tree::ValueAccessor maskAcc(*mMaskTree); + + Coord ijk, nijk, localCorod; + Index pos, npos; + + for (size_t n = range.begin(); n != range.end(); ++n) { + + LeafNodeType& node = *mNodes[n]; + + CoordBBox bbox = node.getNodeBoundingBox(); + bbox.expand(-1); + + BoolLeafNodeType& maskNode = *maskAcc.touchLeaf(node.origin()); + + for (Iterator it = node.cbeginValueOn(); it; ++it) { + ijk = it.getCoord(); + pos = it.pos(); + + localCorod = LeafNodeType::offsetToLocalCoord(pos); + + if (localCorod[2] < int(LeafNodeType::DIM - 1)) { + npos = pos + 1; + if (!node.isValueOn(npos)) maskNode.setValueOn(npos); + } else { + nijk = ijk.offsetBy(0, 0, 1); + if (!acc.isValueOn(nijk)) maskAcc.setValueOn(nijk); + } + + if (localCorod[2] > 0) { + npos = pos - 1; + if (!node.isValueOn(npos)) maskNode.setValueOn(npos); + } else { + nijk = ijk.offsetBy(0, 0, -1); + if (!acc.isValueOn(nijk)) maskAcc.setValueOn(nijk); + } + + if (localCorod[1] < int(LeafNodeType::DIM - 1)) { + npos = pos + LeafNodeType::DIM; + if (!node.isValueOn(npos)) maskNode.setValueOn(npos); + } else { + nijk = ijk.offsetBy(0, 1, 0); + if (!acc.isValueOn(nijk)) maskAcc.setValueOn(nijk); + } + + if (localCorod[1] > 0) { + npos = pos - LeafNodeType::DIM; + if (!node.isValueOn(npos)) maskNode.setValueOn(npos); + } else { + nijk = ijk.offsetBy(0, -1, 0); + if (!acc.isValueOn(nijk)) maskAcc.setValueOn(nijk); + } + + if (localCorod[0] < int(LeafNodeType::DIM - 1)) { + npos = pos + LeafNodeType::DIM * LeafNodeType::DIM; + if (!node.isValueOn(npos)) maskNode.setValueOn(npos); + } else { + nijk = ijk.offsetBy(1, 0, 0); + if (!acc.isValueOn(nijk)) maskAcc.setValueOn(nijk); + } + + if (localCorod[0] > 0) { + npos = pos - LeafNodeType::DIM * LeafNodeType::DIM; + if (!node.isValueOn(npos)) maskNode.setValueOn(npos); + } else { + nijk = ijk.offsetBy(-1, 0, 0); + if (!acc.isValueOn(nijk)) maskAcc.setValueOn(nijk); + } + } + } + } + + void join(ConstructVoxelMask& rhs) { mMaskTree->merge(*rhs.mMaskTree); } + +private: + TreeType const * const mTree; + LeafNodeType ** const mNodes; + + BoolTreeType mLocalMaskTree; + BoolTreeType * const mMaskTree; +}; + + +/// @note The interior and exterior widths should be in world space units and squared. +template +struct ExpandNarrowband +{ + typedef typename TreeType::ValueType ValueType; + typedef typename TreeType::LeafNodeType LeafNodeType; + typedef typename LeafNodeType::NodeMaskType NodeMaskType; + typedef typename TreeType::template ValueConverter::Type Int32TreeType; + typedef typename Int32TreeType::LeafNodeType Int32LeafNodeType; + typedef typename TreeType::template ValueConverter::Type BoolTreeType; + typedef typename BoolTreeType::LeafNodeType BoolLeafNodeType; + + struct Fragment + { + Int32 idx, x, y, z; + ValueType dist; + + Fragment() : idx(0), x(0), y(0), z(0), dist(0.0) {} + + Fragment(Int32 idx_, Int32 x_, Int32 y_, Int32 z_, ValueType dist_) + : idx(idx_), x(x_), y(y_), z(z_), dist(dist_) + { + } + + bool operator<(const Fragment& rhs) const { return idx < rhs.idx; } + }; // struct Fragment + + //////////////////// + + ExpandNarrowband( + std::vector& maskNodes, + BoolTreeType& maskTree, + TreeType& distTree, + Int32TreeType& indexTree, + const MeshDataAdapter& mesh, + ValueType exteriorBandWidth, + ValueType interiorBandWidth, + ValueType voxelSize) + : mMaskNodes(maskNodes.empty() ? NULL : &maskNodes[0]) + , mMaskTree(&maskTree) + , mDistTree(&distTree) + , mIndexTree(&indexTree) + , mMesh(&mesh) + , mNewMaskTree(false) + , mDistNodes() + , mUpdatedDistNodes() + , mIndexNodes() + , mUpdatedIndexNodes() + , mExteriorBandWidth(exteriorBandWidth) + , mInteriorBandWidth(interiorBandWidth) + , mVoxelSize(voxelSize) + { + } + + ExpandNarrowband(const ExpandNarrowband& rhs, tbb::split) + : mMaskNodes(rhs.mMaskNodes) + , mMaskTree(rhs.mMaskTree) + , mDistTree(rhs.mDistTree) + , mIndexTree(rhs.mIndexTree) + , mMesh(rhs.mMesh) + , mNewMaskTree(false) + , mDistNodes() + , mUpdatedDistNodes() + , mIndexNodes() + , mUpdatedIndexNodes() + , mExteriorBandWidth(rhs.mExteriorBandWidth) + , mInteriorBandWidth(rhs.mInteriorBandWidth) + , mVoxelSize(rhs.mVoxelSize) + { + } + + void join(ExpandNarrowband& rhs) + { + mDistNodes.insert(mDistNodes.end(), rhs.mDistNodes.begin(), rhs.mDistNodes.end()); + mIndexNodes.insert(mIndexNodes.end(), rhs.mIndexNodes.begin(), rhs.mIndexNodes.end()); + + mUpdatedDistNodes.insert(mUpdatedDistNodes.end(), + rhs.mUpdatedDistNodes.begin(), rhs.mUpdatedDistNodes.end()); + + mUpdatedIndexNodes.insert(mUpdatedIndexNodes.end(), + rhs.mUpdatedIndexNodes.begin(), rhs.mUpdatedIndexNodes.end()); + + mNewMaskTree.merge(rhs.mNewMaskTree); + } + + void operator()(const tbb::blocked_range& range) + { + tree::ValueAccessor newMaskAcc(mNewMaskTree); + tree::ValueAccessor distAcc(*mDistTree); + tree::ValueAccessor indexAcc(*mIndexTree); + + std::vector fragments; + fragments.reserve(256); + + LeafNodeType * newDistNodePt = NULL; + Int32LeafNodeType * newIndexNodePt = NULL; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + BoolLeafNodeType& maskNode = *mMaskNodes[n]; + if (maskNode.isEmpty()) continue; + + // Setup local caches + + const Coord& origin = maskNode.origin(); + + LeafNodeType * distNodePt = distAcc.probeLeaf(origin); + Int32LeafNodeType * indexNodePt = indexAcc.probeLeaf(origin); + + assert(!distNodePt == !indexNodePt); + + bool usingNewNodes = false; + + if (!distNodePt && !indexNodePt) { + + const ValueType backgroundDist = distAcc.getValue(origin); + + if (!newDistNodePt && !newIndexNodePt) { + newDistNodePt = new LeafNodeType(origin, backgroundDist); + newIndexNodePt = new Int32LeafNodeType(origin, indexAcc.getValue(origin)); + } else { + + if ((backgroundDist < ValueType(0.0)) != + (newDistNodePt->getValue(0) < ValueType(0.0))) { + newDistNodePt->buffer().fill(backgroundDist); + } + + newDistNodePt->setOrigin(origin); + newIndexNodePt->setOrigin(origin); + } + + distNodePt = newDistNodePt; + indexNodePt = newIndexNodePt; + + usingNewNodes = true; + } + + + // Gather neighbour information + + CoordBBox bbox(Coord::max(), Coord::min()); + for (typename BoolLeafNodeType::ValueOnIter it = maskNode.beginValueOn(); it; ++it) { + bbox.expand(it.getCoord()); + } + + bbox.expand(1); + + gatherFragments(fragments, bbox, distAcc, indexAcc); + + + // Compute first voxel layer + + bbox = maskNode.getNodeBoundingBox(); + NodeMaskType mask; + bool updatedLeafNodes = false; + + for (typename BoolLeafNodeType::ValueOnIter it = maskNode.beginValueOn(); it; ++it) { + + const Coord ijk = it.getCoord(); + + if (updateVoxel(ijk, 5, fragments, *distNodePt, *indexNodePt, &updatedLeafNodes)) { + + for (Int32 i = 0; i < 6; ++i) { + const Coord nijk = ijk + util::COORD_OFFSETS[i]; + if (bbox.isInside(nijk)) { + mask.setOn(BoolLeafNodeType::coordToOffset(nijk)); + } else { + newMaskAcc.setValueOn(nijk); + } + } + + for (Int32 i = 6; i < 26; ++i) { + const Coord nijk = ijk + util::COORD_OFFSETS[i]; + if (bbox.isInside(nijk)) { + mask.setOn(BoolLeafNodeType::coordToOffset(nijk)); + } + } + } + } + + if (updatedLeafNodes) { + + // Compute second voxel layer + mask -= indexNodePt->getValueMask(); + + for (typename NodeMaskType::OnIterator it = mask.beginOn(); it; ++it) { + + const Index pos = it.pos(); + const Coord ijk = maskNode.origin() + LeafNodeType::offsetToLocalCoord(pos); + + if (updateVoxel(ijk, 6, fragments, *distNodePt, *indexNodePt)) { + for (Int32 i = 0; i < 6; ++i) { + newMaskAcc.setValueOn(ijk + util::COORD_OFFSETS[i]); + } + } + } + + // Export new distance values + if (usingNewNodes) { + distNodePt->topologyUnion(*indexNodePt); + + mDistNodes.push_back(distNodePt); + mIndexNodes.push_back(indexNodePt); + + newDistNodePt = NULL; + newIndexNodePt = NULL; + } else { + mUpdatedDistNodes.push_back(distNodePt); + mUpdatedIndexNodes.push_back(indexNodePt); + } + } + } // end leafnode loop + } + + ////////// + + BoolTreeType& newMaskTree() { return mNewMaskTree; } + + std::vector& newDistNodes() { return mDistNodes; } + std::vector& updatedDistNodes() { return mUpdatedDistNodes; } + + std::vector& newIndexNodes() { return mIndexNodes; } + std::vector& updatedIndexNodes() { return mUpdatedIndexNodes; } + +private: + + /// @note The output fragment list is ordered by the primitive index + void + gatherFragments(std::vector& fragments, const CoordBBox& bbox, + tree::ValueAccessor& distAcc, tree::ValueAccessor& indexAcc) + { + fragments.clear(); + const Coord nodeMin = bbox.min() & ~(LeafNodeType::DIM - 1); + const Coord nodeMax = bbox.max() & ~(LeafNodeType::DIM - 1); + + CoordBBox region; + Coord ijk; + + for (ijk[0] = nodeMin[0]; ijk[0] <= nodeMax[0]; ijk[0] += LeafNodeType::DIM) { + for (ijk[1] = nodeMin[1]; ijk[1] <= nodeMax[1]; ijk[1] += LeafNodeType::DIM) { + for (ijk[2] = nodeMin[2]; ijk[2] <= nodeMax[2]; ijk[2] += LeafNodeType::DIM) { + if (LeafNodeType* distleaf = distAcc.probeLeaf(ijk)) { + region.min() = Coord::maxComponent(bbox.min(), ijk); + region.max() = Coord::minComponent(bbox.max(), + ijk.offsetBy(LeafNodeType::DIM - 1)); + gatherFragments(fragments, region, *distleaf, *indexAcc.probeLeaf(ijk)); + } + } + } + } + + std::sort(fragments.begin(), fragments.end()); + } + + void + gatherFragments(std::vector& fragments, const CoordBBox& bbox, + const LeafNodeType& distLeaf, const Int32LeafNodeType& idxLeaf) const + { + const typename LeafNodeType::NodeMaskType& mask = distLeaf.getValueMask(); + const ValueType* distData = distLeaf.buffer().data(); + const Int32* idxData = idxLeaf.buffer().data(); + + for (int x = bbox.min()[0]; x <= bbox.max()[0]; ++x) { + const Index xPos = (x & (LeafNodeType::DIM - 1u)) << (2 * LeafNodeType::LOG2DIM); + for (int y = bbox.min()[1]; y <= bbox.max()[1]; ++y) { + const Index yPos = xPos + ((y & (LeafNodeType::DIM - 1u)) << LeafNodeType::LOG2DIM); + for (int z = bbox.min()[2]; z <= bbox.max()[2]; ++z) { + const Index pos = yPos + (z & (LeafNodeType::DIM - 1u)); + if (mask.isOn(pos)) { + fragments.push_back(Fragment(idxData[pos],x,y,z, std::abs(distData[pos]))); + } + } + } + } + } + + /// @note This method expects the fragment list to be ordered by the primitive index + /// to avoid redundant distance computations. + ValueType + computeDistance(const Coord& ijk, const Int32 manhattanLimit, + const std::vector& fragments, Int32& closestPrimIdx) const + { + Vec3d a, b, c, uvw, voxelCenter(ijk[0], ijk[1], ijk[2]); + double primDist, tmpDist, dist = std::numeric_limits::max(); + Int32 lastIdx = Int32(util::INVALID_IDX); + + for (size_t n = 0, N = fragments.size(); n < N; ++n) { + + const Fragment& fragment = fragments[n]; + if (lastIdx == fragment.idx) continue; + + const Int32 dx = std::abs(fragment.x - ijk[0]); + const Int32 dy = std::abs(fragment.y - ijk[1]); + const Int32 dz = std::abs(fragment.z - ijk[2]); + + const Int32 manhattan = dx + dy + dz; + if (manhattan > manhattanLimit) continue; + + lastIdx = fragment.idx; + + const size_t polygon = size_t(lastIdx); + + mMesh->getIndexSpacePoint(polygon, 0, a); + mMesh->getIndexSpacePoint(polygon, 1, b); + mMesh->getIndexSpacePoint(polygon, 2, c); + + primDist = (voxelCenter - + closestPointOnTriangleToPoint(a, c, b, voxelCenter, uvw)).lengthSqr(); + + // Split quad into a second triangle + if (4 == mMesh->vertexCount(polygon)) { + + mMesh->getIndexSpacePoint(polygon, 3, b); + + tmpDist = (voxelCenter - closestPointOnTriangleToPoint( + a, b, c, voxelCenter, uvw)).lengthSqr(); + + if (tmpDist < primDist) primDist = tmpDist; + } + + if (primDist < dist) { + dist = primDist; + closestPrimIdx = lastIdx; + } + } + + return ValueType(std::sqrt(dist)) * mVoxelSize; + } + + /// @note Returns true if the current voxel was updated and neighbouring + /// voxels need to be evaluated. + bool + updateVoxel(const Coord& ijk, const Int32 manhattanLimit, + const std::vector& fragments, + LeafNodeType& distLeaf, Int32LeafNodeType& idxLeaf, bool* updatedLeafNodes = NULL) + { + Int32 closestPrimIdx = 0; + const ValueType distance = computeDistance(ijk, manhattanLimit, fragments, closestPrimIdx); + + const Index pos = LeafNodeType::coordToOffset(ijk); + const bool inside = distLeaf.getValue(pos) < ValueType(0.0); + + bool activateNeighbourVoxels = false; + + if (!inside && distance < mExteriorBandWidth) { + if (updatedLeafNodes) *updatedLeafNodes = true; + activateNeighbourVoxels = (distance + mVoxelSize) < mExteriorBandWidth; + distLeaf.setValueOnly(pos, distance); + idxLeaf.setValueOn(pos, closestPrimIdx); + } else if (inside && distance < mInteriorBandWidth) { + if (updatedLeafNodes) *updatedLeafNodes = true; + activateNeighbourVoxels = (distance + mVoxelSize) < mInteriorBandWidth; + distLeaf.setValueOnly(pos, -distance); + idxLeaf.setValueOn(pos, closestPrimIdx); + } + + return activateNeighbourVoxels; + } + + ////////// + + BoolLeafNodeType ** const mMaskNodes; + BoolTreeType * const mMaskTree; + TreeType * const mDistTree; + Int32TreeType * const mIndexTree; + + MeshDataAdapter const * const mMesh; + + BoolTreeType mNewMaskTree; + + std::vector mDistNodes, mUpdatedDistNodes; + std::vector mIndexNodes, mUpdatedIndexNodes; + + const ValueType mExteriorBandWidth, mInteriorBandWidth, mVoxelSize; +}; // struct ExpandNarrowband + + +template +struct AddNodes { + typedef typename TreeType::LeafNodeType LeafNodeType; + + AddNodes(TreeType& tree, std::vector& nodes) + : mTree(&tree) , mNodes(&nodes) + { + } + + void operator()() const { + tree::ValueAccessor acc(*mTree); + std::vector& nodes = *mNodes; + for (size_t n = 0, N = nodes.size(); n < N; ++n) { + acc.addLeaf(nodes[n]); + } + } + + TreeType * const mTree; + std::vector * const mNodes; +}; // AddNodes + + +template +inline void +expandNarrowband( + TreeType& distTree, + Int32TreeType& indexTree, + BoolTreeType& maskTree, + std::vector& maskNodes, + const MeshDataAdapter& mesh, + typename TreeType::ValueType exteriorBandWidth, + typename TreeType::ValueType interiorBandWidth, + typename TreeType::ValueType voxelSize) +{ + ExpandNarrowband expandOp(maskNodes, maskTree, + distTree, indexTree, mesh, exteriorBandWidth, interiorBandWidth, voxelSize); + + tbb::parallel_reduce(tbb::blocked_range(0, maskNodes.size()), expandOp); + + tbb::parallel_for(tbb::blocked_range(0, expandOp.updatedIndexNodes().size()), + UnionValueMasks( + expandOp.updatedDistNodes(), expandOp.updatedIndexNodes())); + + tbb::task_group tasks; + tasks.run(AddNodes(distTree, expandOp.newDistNodes())); + tasks.run(AddNodes(indexTree, expandOp.newIndexNodes())); + tasks.wait(); + + maskTree.clear(); + maskTree.merge(expandOp.newMaskTree()); +} + + +//////////////////////////////////////// + + +// Transform values (sqrt, world space scaling and sign flip if sdf) +template +struct TransformValues +{ + typedef typename TreeType::LeafNodeType LeafNodeType; + typedef typename TreeType::ValueType ValueType; + + TransformValues(std::vector& nodes, + ValueType voxelSize, bool unsignedDist) + : mNodes(&nodes[0]) + , mVoxelSize(voxelSize) + , mUnsigned(unsignedDist) + { + } + + void operator()(const tbb::blocked_range& range) const { + + typename LeafNodeType::ValueOnIter iter; + + const bool udf = mUnsigned; + const ValueType w[2] = { -mVoxelSize, mVoxelSize }; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + for (iter = mNodes[n]->beginValueOn(); iter; ++iter) { + ValueType& val = const_cast(iter.getValue()); + val = w[udf || (val < ValueType(0.0))] * std::sqrt(std::abs(val)); + } + } + } + +private: + LeafNodeType * * const mNodes; + const ValueType mVoxelSize; + const bool mUnsigned; +}; + + +// Inactivate values outside the (exBandWidth, inBandWidth) range. +template +struct InactivateValues +{ + typedef typename TreeType::LeafNodeType LeafNodeType; + typedef typename TreeType::ValueType ValueType; + + InactivateValues(std::vector& nodes, + ValueType exBandWidth, ValueType inBandWidth) + : mNodes(nodes.empty() ? NULL : &nodes[0]) + , mExBandWidth(exBandWidth) + , mInBandWidth(inBandWidth) + { + } + + void operator()(const tbb::blocked_range& range) const { + + typename LeafNodeType::ValueOnIter iter; + const ValueType exVal = mExBandWidth; + const ValueType inVal = -mInBandWidth; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + for (iter = mNodes[n]->beginValueOn(); iter; ++iter) { + + ValueType& val = const_cast(iter.getValue()); + + const bool inside = val < ValueType(0.0); + + if (inside && !(val > inVal)) { + val = inVal; + iter.setValueOff(); + } else if (!inside && !(val < exVal)) { + val = exVal; + iter.setValueOff(); + } + } + } + } + +private: + LeafNodeType * * const mNodes; + const ValueType mExBandWidth, mInBandWidth; +}; + + +template +struct OffsetValues +{ + typedef typename TreeType::LeafNodeType LeafNodeType; + typedef typename TreeType::ValueType ValueType; + + OffsetValues(std::vector& nodes, ValueType offset) + : mNodes(nodes.empty() ? NULL : &nodes[0]), mOffset(offset) + { + } + + void operator()(const tbb::blocked_range& range) const { + + const ValueType offset = mOffset; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + typename LeafNodeType::ValueOnIter iter = mNodes[n]->beginValueOn(); + + for (; iter; ++iter) { + ValueType& val = const_cast(iter.getValue()); + val += offset; + } + } + } + +private: + LeafNodeType * * const mNodes; + const ValueType mOffset; +}; + + +template +struct Renormalize +{ + typedef typename TreeType::LeafNodeType LeafNodeType; + typedef typename TreeType::ValueType ValueType; + + Renormalize(const TreeType& tree, const std::vector& nodes, ValueType* buffer, ValueType voxelSize) + : mTree(&tree) + , mNodes(nodes.empty() ? NULL : &nodes[0]) + , mBuffer(buffer) + , mVoxelSize(voxelSize) + { + } + + void operator()(const tbb::blocked_range& range) const + { + typedef math::Vec3 Vec3Type; + + tree::ValueAccessor acc(*mTree); + + Coord ijk; + Vec3Type up, down; + + const ValueType dx = mVoxelSize, invDx = ValueType(1.0) / mVoxelSize; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + ValueType* bufferData = &mBuffer[n * LeafNodeType::SIZE]; + + typename LeafNodeType::ValueOnCIter iter = mNodes[n]->cbeginValueOn(); + for (; iter; ++iter) { + + const ValueType phi0 = *iter; + + ijk = iter.getCoord(); + + up[0] = acc.getValue(ijk.offsetBy(1, 0, 0)) - phi0; + up[1] = acc.getValue(ijk.offsetBy(0, 1, 0)) - phi0; + up[2] = acc.getValue(ijk.offsetBy(0, 0, 1)) - phi0; + + down[0] = phi0 - acc.getValue(ijk.offsetBy(-1, 0, 0)); + down[1] = phi0 - acc.getValue(ijk.offsetBy(0, -1, 0)); + down[2] = phi0 - acc.getValue(ijk.offsetBy(0, 0, -1)); + + const ValueType normSqGradPhi = math::GodunovsNormSqrd(phi0 > 0.0, down, up); + + const ValueType diff = math::Sqrt(normSqGradPhi) * invDx - ValueType(1.0); + const ValueType S = phi0 / (math::Sqrt(math::Pow2(phi0) + normSqGradPhi)); + + bufferData[iter.pos()] = phi0 - dx * S * diff; + } + } + } + +private: + TreeType const * const mTree; + LeafNodeType const * const * const mNodes; + ValueType * const mBuffer; + + const ValueType mVoxelSize; +}; + + +template +struct MinCombine +{ + typedef typename TreeType::LeafNodeType LeafNodeType; + typedef typename TreeType::ValueType ValueType; + + MinCombine(std::vector& nodes, const ValueType* buffer) + : mNodes(nodes.empty() ? NULL : &nodes[0]), mBuffer(buffer) + { + } + + void operator()(const tbb::blocked_range& range) const { + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + const ValueType* bufferData = &mBuffer[n * LeafNodeType::SIZE]; + + typename LeafNodeType::ValueOnIter iter = mNodes[n]->beginValueOn(); + + for (; iter; ++iter) { + ValueType& val = const_cast(iter.getValue()); + val = std::min(val, bufferData[iter.pos()]); + } + } + } + +private: + LeafNodeType * * const mNodes; + ValueType const * const mBuffer; +}; + + +} // mesh_to_volume_internal namespace + + +//////////////////////////////////////// + +// Utility method implementation + + +template +inline void +traceExteriorBoundaries(FloatTreeT& tree) +{ + typedef mesh_to_volume_internal::LeafNodeConnectivityTable ConnectivityTable; + + ConnectivityTable nodeConnectivity(tree); + + std::vector zStartNodes, yStartNodes, xStartNodes; + + for (size_t n = 0; n < nodeConnectivity.size(); ++n) { + if (ConnectivityTable::INVALID_OFFSET == nodeConnectivity.offsetsPrevX()[n]) { + xStartNodes.push_back(n); + } + + if (ConnectivityTable::INVALID_OFFSET == nodeConnectivity.offsetsPrevY()[n]) { + yStartNodes.push_back(n); + } + + if (ConnectivityTable::INVALID_OFFSET == nodeConnectivity.offsetsPrevZ()[n]) { + zStartNodes.push_back(n); + } + } + + typedef mesh_to_volume_internal::SweepExteriorSign SweepingOp; + + tbb::parallel_for(tbb::blocked_range(0, zStartNodes.size()), + SweepingOp(SweepingOp::Z_AXIS, zStartNodes, nodeConnectivity)); + + tbb::parallel_for(tbb::blocked_range(0, yStartNodes.size()), + SweepingOp(SweepingOp::Y_AXIS, yStartNodes, nodeConnectivity)); + + tbb::parallel_for(tbb::blocked_range(0, xStartNodes.size()), + SweepingOp(SweepingOp::X_AXIS, xStartNodes, nodeConnectivity)); + + const size_t numLeafNodes = nodeConnectivity.size(); + const size_t numVoxels = numLeafNodes * FloatTreeT::LeafNodeType::SIZE; + + boost::scoped_array changedNodeMaskA(new bool[numLeafNodes]); + boost::scoped_array changedNodeMaskB(new bool[numLeafNodes]); + boost::scoped_array changedVoxelMask(new bool[numVoxels]); + + memset(changedNodeMaskA.get(), 1, sizeof(bool) * numLeafNodes); + mesh_to_volume_internal::fillArray(changedVoxelMask.get(), false, numVoxels); + + const tbb::blocked_range nodeRange(0, numLeafNodes); + + bool nodesUpdated = false; + do { + tbb::parallel_for(nodeRange, mesh_to_volume_internal::SeedFillExteriorSign( + nodeConnectivity.nodes(), changedNodeMaskA.get())); + + tbb::parallel_for(nodeRange, mesh_to_volume_internal::SeedPoints(nodeConnectivity, + changedNodeMaskA.get(), changedNodeMaskB.get(), changedVoxelMask.get())); + + changedNodeMaskA.swap(changedNodeMaskB); + + nodesUpdated = false; + for (size_t n = 0; n < numLeafNodes; ++n) { + nodesUpdated |= changedNodeMaskA[n]; + if (nodesUpdated) break; + } + + if (nodesUpdated) { + tbb::parallel_for(nodeRange, mesh_to_volume_internal::SyncVoxelMask( + nodeConnectivity.nodes(), changedNodeMaskA.get(), changedVoxelMask.get())); + } + } while (nodesUpdated); + +} // void traceExteriorBoundaries() + + +//////////////////////////////////////// + + +template +inline typename GridType::Ptr +meshToVolume( + Interrupter& interrupter, + const MeshDataAdapter& mesh, + const math::Transform& transform, + float exteriorBandWidth, + float interiorBandWidth, + int flags, + typename GridType::template ValueConverter::Type * polygonIndexGrid) +{ + typedef typename GridType::Ptr GridTypePtr; + typedef typename GridType::TreeType TreeType; + typedef typename TreeType::LeafNodeType LeafNodeType; + typedef typename GridType::ValueType ValueType; + + typedef typename GridType::template ValueConverter::Type Int32GridType; + typedef typename Int32GridType::TreeType Int32TreeType; + + typedef typename TreeType::template ValueConverter::Type BoolTreeType; + + ////////// + + // Setup + + GridTypePtr distGrid(new GridType(std::numeric_limits::max())); + distGrid->setTransform(transform.copy()); + + ValueType exteriorWidth = ValueType(exteriorBandWidth); + ValueType interiorWidth = ValueType(interiorBandWidth); + + // Note: inf interior width is all right, this value makes the converter fill + // interior regions with distance values. + if (!boost::math::isfinite(exteriorWidth) || boost::math::isnan(interiorWidth)) { + std::stringstream msg; + msg << "Illegal narrow band width: exterior = " << exteriorWidth + << ", interior = " << interiorWidth; + OPENVDB_LOG_DEBUG(msg.str()); + return distGrid; + } + + const ValueType voxelSize = ValueType(transform.voxelSize()[0]); + + if (!boost::math::isfinite(voxelSize) || math::isZero(voxelSize)) { + std::stringstream msg; + msg << "Illegal transform, voxel size = " << voxelSize; + OPENVDB_LOG_DEBUG(msg.str()); + return distGrid; + } + + // Convert narrow band width from voxel units to world space units. + exteriorWidth *= voxelSize; + // Avoid the unit conversion if the interior band width is set to + // inf or std::numeric_limits::max(). + if (interiorWidth < std::numeric_limits::max()) { + interiorWidth *= voxelSize; + } + + const bool computeSignedDistanceField = (flags & UNSIGNED_DISTANCE_FIELD) == 0; + const bool removeIntersectingVoxels = (flags & DISABLE_INTERSECTING_VOXEL_REMOVAL) == 0; + const bool renormalizeValues = (flags & DISABLE_RENORMALIZATION) == 0; + const bool trimNarrowBand = (flags & DISABLE_NARROW_BAND_TRIMMING) == 0; + + Int32GridType* indexGrid = NULL; + + typename Int32GridType::Ptr temporaryIndexGrid; + + if (polygonIndexGrid) { + indexGrid = polygonIndexGrid; + } else { + temporaryIndexGrid.reset(new Int32GridType(Int32(util::INVALID_IDX))); + indexGrid = temporaryIndexGrid.get(); + } + + indexGrid->newTree(); + indexGrid->setTransform(transform.copy()); + + if (computeSignedDistanceField) { + distGrid->setGridClass(GRID_LEVEL_SET); + } else { + distGrid->setGridClass(GRID_UNKNOWN); + interiorWidth = ValueType(0.0); + } + + TreeType& distTree = distGrid->tree(); + Int32TreeType& indexTree = indexGrid->tree(); + + + ////////// + + // Voxelize mesh + + { + typedef mesh_to_volume_internal::VoxelizationData VoxelizationDataType; + typedef tbb::enumerable_thread_specific DataTable; + + DataTable data; + typedef mesh_to_volume_internal::VoxelizePolygons Voxelizer; + + const tbb::blocked_range polygonRange(0, mesh.polygonCount()); + + tbb::parallel_for(polygonRange, Voxelizer(data, mesh, &interrupter)); + + for (typename DataTable::iterator i = data.begin(); i != data.end(); ++i) { + VoxelizationDataType& dataItem = **i; + mesh_to_volume_internal::combineData( + distTree, indexTree, dataItem.distTree, dataItem.indexTree); + } + } + + // The progress estimates are based on the observed average time for a few different + // test cases and is only intended to provide some rough progression feedback to the user. + if (interrupter.wasInterrupted(30)) return distGrid; + + + ////////// + + // Classify interior and exterior regions + + if (computeSignedDistanceField) { + + // Determines the inside/outside state for the narrow band of voxels. + traceExteriorBoundaries(distTree); + + std::vector nodes; + nodes.reserve(distTree.leafCount()); + distTree.getNodes(nodes); + + const tbb::blocked_range nodeRange(0, nodes.size()); + + typedef mesh_to_volume_internal::ComputeIntersectingVoxelSign SignOp; + + tbb::parallel_for(nodeRange, SignOp(nodes, distTree, indexTree, mesh)); + + if (interrupter.wasInterrupted(45)) return distGrid; + + // Remove voxels created by self intersecting portions of the mesh. + if (removeIntersectingVoxels) { + + tbb::parallel_for(nodeRange, + mesh_to_volume_internal::ValidateIntersectingVoxels(distTree, nodes)); + + tbb::parallel_for(nodeRange, + mesh_to_volume_internal::RemoveSelfIntersectingSurface( + nodes, distTree, indexTree)); + + tools::pruneInactive(distTree, /*threading=*/true); + tools::pruneInactive(indexTree, /*threading=*/true); + } + } + + if (interrupter.wasInterrupted(50)) return distGrid; + + if (distTree.activeVoxelCount() == 0) { + distGrid.reset((new GridType(ValueType(0.0)))); + return distGrid; + } + + // Transform values (world space scaling etc.). + { + std::vector nodes; + nodes.reserve(distTree.leafCount()); + distTree.getNodes(nodes); + + tbb::parallel_for(tbb::blocked_range(0, nodes.size()), + mesh_to_volume_internal::TransformValues( + nodes, voxelSize, !computeSignedDistanceField)); + } + + // Propagate sign information into tile regions. + if (computeSignedDistanceField) { + distTree.root().setBackground(exteriorWidth, /*updateChildNodes=*/false); + tools::signedFloodFillWithValues(distTree, exteriorWidth, -interiorWidth); + } else { + tools::changeBackground(distTree, exteriorWidth); + } + + if (interrupter.wasInterrupted(54)) return distGrid; + + + ////////// + + // Expand the narrow band region + + const ValueType minBandWidth = voxelSize * ValueType(2.0); + + if (interiorWidth > minBandWidth || exteriorWidth > minBandWidth) { + + // Create the initial voxel mask. + BoolTreeType maskTree(false); + + { + std::vector nodes; + nodes.reserve(distTree.leafCount()); + distTree.getNodes(nodes); + + mesh_to_volume_internal::ConstructVoxelMask op(maskTree, distTree, nodes); + tbb::parallel_reduce(tbb::blocked_range(0, nodes.size()), op); + } + + // Progress estimation + unsigned maxIterations = std::numeric_limits::max(); + + float progress = 54.0f, step = 0.0f; + double estimated = + 2.0 * std::ceil((std::max(interiorWidth, exteriorWidth) - minBandWidth) / voxelSize); + + if (estimated < double(maxIterations)) { + maxIterations = unsigned(estimated); + step = 40.0f / float(maxIterations); + } + + std::vector maskNodes; + + unsigned count = 0; + while (true) { + + if (interrupter.wasInterrupted(int(progress))) return distGrid; + + const size_t maskNodeCount = maskTree.leafCount(); + if (maskNodeCount == 0) break; + + maskNodes.clear(); + maskNodes.reserve(maskNodeCount); + maskTree.getNodes(maskNodes); + + const tbb::blocked_range range(0, maskNodes.size()); + + tbb::parallel_for(range, + mesh_to_volume_internal::DiffLeafNodeMask(distTree, maskNodes)); + + mesh_to_volume_internal::expandNarrowband(distTree, indexTree, maskTree, maskNodes, + mesh, exteriorWidth, interiorWidth, voxelSize); + + if ((++count) >= maxIterations) break; + progress += step; + } + } + + if (interrupter.wasInterrupted(94)) return distGrid; + + if (!polygonIndexGrid) indexGrid->clear(); + + + ///////// + + // Renormalize distances to smooth out bumps caused by self intersecting + // and overlapping portions of the mesh and renormalize the level set. + + if (computeSignedDistanceField && renormalizeValues) { + + std::vector nodes; + nodes.reserve(distTree.leafCount()); + distTree.getNodes(nodes); + + boost::scoped_array buffer(new ValueType[LeafNodeType::SIZE * nodes.size()]); + + const ValueType offset = ValueType(0.8 * voxelSize); + + tbb::parallel_for(tbb::blocked_range(0, nodes.size()), + mesh_to_volume_internal::OffsetValues(nodes, -offset)); + + tbb::parallel_for(tbb::blocked_range(0, nodes.size()), + mesh_to_volume_internal::Renormalize( + distTree, nodes, buffer.get(), voxelSize)); + + tbb::parallel_for(tbb::blocked_range(0, nodes.size()), + mesh_to_volume_internal::MinCombine(nodes, buffer.get())); + + tbb::parallel_for(tbb::blocked_range(0, nodes.size()), + mesh_to_volume_internal::OffsetValues( + nodes, offset - mesh_to_volume_internal::Tolerance::epsilon())); + } + + if (interrupter.wasInterrupted(99)) return distGrid; + + + ///////// + + // Remove active voxels that exceed the narrow band limits + + if (trimNarrowBand && std::min(interiorWidth, exteriorWidth) < voxelSize * ValueType(4.0)) { + + std::vector nodes; + nodes.reserve(distTree.leafCount()); + distTree.getNodes(nodes); + + tbb::parallel_for(tbb::blocked_range(0, nodes.size()), + mesh_to_volume_internal::InactivateValues( + nodes, exteriorWidth, computeSignedDistanceField ? interiorWidth : exteriorWidth)); + + tools::pruneLevelSet( + distTree, exteriorWidth, computeSignedDistanceField ? -interiorWidth : -exteriorWidth); + } + + return distGrid; +} + + +template +inline typename GridType::Ptr +meshToVolume( + const MeshDataAdapter& mesh, + const math::Transform& transform, + float exteriorBandWidth, + float interiorBandWidth, + int flags, + typename GridType::template ValueConverter::Type * polygonIndexGrid) +{ + util::NullInterrupter nullInterrupter; + return meshToVolume(nullInterrupter, mesh, transform, + exteriorBandWidth, interiorBandWidth, flags, polygonIndexGrid); +} + + +//////////////////////////////////////// + + +/// @internal This overload is enabled only for grids with a scalar, floating-point ValueType. +template +inline typename boost::enable_if, +typename GridType::Ptr>::type +doMeshConversion( + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& triangles, + const std::vector& quads, + float exBandWidth, + float inBandWidth, + bool unsignedDistanceField = false) +{ + if (points.empty()) { + return typename GridType::Ptr(new GridType(typename GridType::ValueType(exBandWidth))); + } + + const size_t numPoints = points.size(); + boost::scoped_array indexSpacePoints(new Vec3s[numPoints]); + + // transform points to local grid index space + tbb::parallel_for(tbb::blocked_range(0, numPoints), + mesh_to_volume_internal::TransformPoints( + &points[0], indexSpacePoints.get(), xform)); + + const int conversionFlags = unsignedDistanceField ? UNSIGNED_DISTANCE_FIELD : 0; + + if (quads.empty()) { + + QuadAndTriangleDataAdapter + mesh(indexSpacePoints.get(), numPoints, &triangles[0], triangles.size()); + + return meshToVolume(mesh, xform, exBandWidth, inBandWidth, conversionFlags); + + } else if (triangles.empty()) { + + QuadAndTriangleDataAdapter + mesh(indexSpacePoints.get(), numPoints, &quads[0], quads.size()); + + return meshToVolume(mesh, xform, exBandWidth, inBandWidth, conversionFlags); + } + + // pack primitives + + const size_t numPrimitives = triangles.size() + quads.size(); + boost::scoped_array prims(new Vec4I[numPrimitives]); + + for (size_t n = 0, N = triangles.size(); n < N; ++n) { + const Vec3I& triangle = triangles[n]; + Vec4I& prim = prims[n]; + prim[0] = triangle[0]; + prim[1] = triangle[1]; + prim[2] = triangle[2]; + prim[3] = util::INVALID_IDX; + } + + const size_t offset = triangles.size(); + for (size_t n = 0, N = quads.size(); n < N; ++n) { + prims[offset + n] = quads[n]; + } + + QuadAndTriangleDataAdapter + mesh(indexSpacePoints.get(), numPoints, prims.get(), numPrimitives); + + return meshToVolume(mesh, xform, exBandWidth, inBandWidth, conversionFlags); +} + + +/// @internal This overload is enabled only for grids that do not have a scalar, +/// floating-point ValueType. +template +inline typename boost::disable_if, +typename GridType::Ptr>::type +doMeshConversion( + const math::Transform& /*xform*/, + const std::vector& /*points*/, + const std::vector& /*triangles*/, + const std::vector& /*quads*/, + float /*exBandWidth*/, + float /*inBandWidth*/, + bool /*unsignedDistanceField*/ = false) +{ + OPENVDB_THROW(TypeError, + "mesh to volume conversion is supported only for scalar floating-point grids"); +} + + +//////////////////////////////////////// + + +template +inline typename GridType::Ptr +meshToLevelSet( + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& triangles, + float halfWidth) +{ + std::vector quads(0); + return doMeshConversion(xform, points, triangles, quads, + halfWidth, halfWidth); +} + + +template +inline typename GridType::Ptr +meshToLevelSet( + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& quads, + float halfWidth) +{ + std::vector triangles(0); + return doMeshConversion(xform, points, triangles, quads, + halfWidth, halfWidth); +} + + +template +inline typename GridType::Ptr +meshToLevelSet( + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& triangles, + const std::vector& quads, + float halfWidth) +{ + return doMeshConversion(xform, points, triangles, quads, + halfWidth, halfWidth); +} + + +template +inline typename GridType::Ptr +meshToSignedDistanceField( + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& triangles, + const std::vector& quads, + float exBandWidth, + float inBandWidth) +{ + return doMeshConversion(xform, points, triangles, + quads, exBandWidth, inBandWidth); +} + + +template +inline typename GridType::Ptr +meshToUnsignedDistanceField( + const openvdb::math::Transform& xform, + const std::vector& points, + const std::vector& triangles, + const std::vector& quads, + float bandWidth) +{ + return doMeshConversion(xform, points, triangles, quads, + bandWidth, bandWidth, true); +} + + +//////////////////////////////////////////////////////////////////////////////// + + +// Required by several of the tree nodes +inline std::ostream& +operator<<(std::ostream& ostr, const MeshToVoxelEdgeData::EdgeData& rhs) +{ + ostr << "{[ " << rhs.mXPrim << ", " << rhs.mXDist << "]"; + ostr << " [ " << rhs.mYPrim << ", " << rhs.mYDist << "]"; + ostr << " [ " << rhs.mZPrim << ", " << rhs.mZDist << "]}"; + return ostr; +} + +// Required by math::Abs +inline MeshToVoxelEdgeData::EdgeData +Abs(const MeshToVoxelEdgeData::EdgeData& x) +{ + return x; +} + + +//////////////////////////////////////// + + +class MeshToVoxelEdgeData::GenEdgeData +{ +public: + + GenEdgeData( + const std::vector& pointList, + const std::vector& polygonList); + + void run(bool threaded = true); + + GenEdgeData(GenEdgeData& rhs, tbb::split); + inline void operator() (const tbb::blocked_range &range); + inline void join(GenEdgeData& rhs); + + inline TreeType& tree() { return mTree; } + +private: + void operator=(const GenEdgeData&) {} + + struct Primitive { Vec3d a, b, c, d; Int32 index; }; + + template + inline void voxelize(const Primitive&); + + template + inline bool evalPrimitive(const Coord&, const Primitive&); + + inline bool rayTriangleIntersection( const Vec3d& origin, const Vec3d& dir, + const Vec3d& a, const Vec3d& b, const Vec3d& c, double& t); + + + TreeType mTree; + Accessor mAccessor; + + const std::vector& mPointList; + const std::vector& mPolygonList; + + // Used internally for acceleration + typedef TreeType::ValueConverter::Type IntTreeT; + IntTreeT mLastPrimTree; + tree::ValueAccessor mLastPrimAccessor; +}; // class MeshToVoxelEdgeData::GenEdgeData + + +inline +MeshToVoxelEdgeData::GenEdgeData::GenEdgeData( + const std::vector& pointList, + const std::vector& polygonList) + : mTree(EdgeData()) + , mAccessor(mTree) + , mPointList(pointList) + , mPolygonList(polygonList) + , mLastPrimTree(Int32(util::INVALID_IDX)) + , mLastPrimAccessor(mLastPrimTree) +{ +} + + +inline +MeshToVoxelEdgeData::GenEdgeData::GenEdgeData(GenEdgeData& rhs, tbb::split) + : mTree(EdgeData()) + , mAccessor(mTree) + , mPointList(rhs.mPointList) + , mPolygonList(rhs.mPolygonList) + , mLastPrimTree(Int32(util::INVALID_IDX)) + , mLastPrimAccessor(mLastPrimTree) +{ +} + + +inline void +MeshToVoxelEdgeData::GenEdgeData::run(bool threaded) +{ + if (threaded) { + tbb::parallel_reduce(tbb::blocked_range(0, mPolygonList.size()), *this); + } else { + (*this)(tbb::blocked_range(0, mPolygonList.size())); + } +} + + +inline void +MeshToVoxelEdgeData::GenEdgeData::join(GenEdgeData& rhs) +{ + typedef TreeType::RootNodeType RootNodeType; + typedef RootNodeType::NodeChainType NodeChainType; + BOOST_STATIC_ASSERT(boost::mpl::size::value > 1); + typedef boost::mpl::at >::type InternalNodeType; + + Coord ijk; + Index offset; + + rhs.mTree.clearAllAccessors(); + + TreeType::LeafIter leafIt = rhs.mTree.beginLeaf(); + for ( ; leafIt; ++leafIt) { + ijk = leafIt->origin(); + + TreeType::LeafNodeType* lhsLeafPt = mTree.probeLeaf(ijk); + + if (!lhsLeafPt) { + + mAccessor.addLeaf(rhs.mAccessor.probeLeaf(ijk)); + InternalNodeType* node = rhs.mAccessor.getNode(); + node->stealNode(ijk, EdgeData(), false); + rhs.mAccessor.clear(); + + } else { + + TreeType::LeafNodeType::ValueOnCIter it = leafIt->cbeginValueOn(); + for ( ; it; ++it) { + + offset = it.pos(); + const EdgeData& rhsValue = it.getValue(); + + if (!lhsLeafPt->isValueOn(offset)) { + lhsLeafPt->setValueOn(offset, rhsValue); + } else { + + EdgeData& lhsValue = const_cast(lhsLeafPt->getValue(offset)); + + if (rhsValue.mXDist < lhsValue.mXDist) { + lhsValue.mXDist = rhsValue.mXDist; + lhsValue.mXPrim = rhsValue.mXPrim; + } + + if (rhsValue.mYDist < lhsValue.mYDist) { + lhsValue.mYDist = rhsValue.mYDist; + lhsValue.mYPrim = rhsValue.mYPrim; + } + + if (rhsValue.mZDist < lhsValue.mZDist) { + lhsValue.mZDist = rhsValue.mZDist; + lhsValue.mZPrim = rhsValue.mZPrim; + } + + } + } // end value iteration + } + } // end leaf iteration +} + + +inline void +MeshToVoxelEdgeData::GenEdgeData::operator()(const tbb::blocked_range &range) +{ + Primitive prim; + + for (size_t n = range.begin(); n < range.end(); ++n) { + + const Vec4I& verts = mPolygonList[n]; + + prim.index = Int32(n); + prim.a = Vec3d(mPointList[verts[0]]); + prim.b = Vec3d(mPointList[verts[1]]); + prim.c = Vec3d(mPointList[verts[2]]); + + if (util::INVALID_IDX != verts[3]) { + prim.d = Vec3d(mPointList[verts[3]]); + voxelize(prim); + } else { + voxelize(prim); + } + } +} + + +template +inline void +MeshToVoxelEdgeData::GenEdgeData::voxelize(const Primitive& prim) +{ + std::deque coordList; + Coord ijk, nijk; + + ijk = Coord::floor(prim.a); + coordList.push_back(ijk); + + evalPrimitive(ijk, prim); + + while (!coordList.empty()) { + + ijk = coordList.back(); + coordList.pop_back(); + + for (Int32 i = 0; i < 26; ++i) { + nijk = ijk + util::COORD_OFFSETS[i]; + + if (prim.index != mLastPrimAccessor.getValue(nijk)) { + mLastPrimAccessor.setValue(nijk, prim.index); + if(evalPrimitive(nijk, prim)) coordList.push_back(nijk); + } + } + } +} + + +template +inline bool +MeshToVoxelEdgeData::GenEdgeData::evalPrimitive(const Coord& ijk, const Primitive& prim) +{ + Vec3d uvw, org(ijk[0], ijk[1], ijk[2]); + bool intersecting = false; + double t; + + EdgeData edgeData; + mAccessor.probeValue(ijk, edgeData); + + // Evaluate first triangle + double dist = (org - + closestPointOnTriangleToPoint(prim.a, prim.c, prim.b, org, uvw)).lengthSqr(); + + if (rayTriangleIntersection(org, Vec3d(1.0, 0.0, 0.0), prim.a, prim.c, prim.b, t)) { + if (t < edgeData.mXDist) { + edgeData.mXDist = float(t); + edgeData.mXPrim = prim.index; + intersecting = true; + } + } + + if (rayTriangleIntersection(org, Vec3d(0.0, 1.0, 0.0), prim.a, prim.c, prim.b, t)) { + if (t < edgeData.mYDist) { + edgeData.mYDist = float(t); + edgeData.mYPrim = prim.index; + intersecting = true; + } + } + + if (rayTriangleIntersection(org, Vec3d(0.0, 0.0, 1.0), prim.a, prim.c, prim.b, t)) { + if (t < edgeData.mZDist) { + edgeData.mZDist = float(t); + edgeData.mZPrim = prim.index; + intersecting = true; + } + } + + if (IsQuad) { + // Split quad into a second triangle and calculate distance. + double secondDist = (org - + closestPointOnTriangleToPoint(prim.a, prim.d, prim.c, org, uvw)).lengthSqr(); + + if (secondDist < dist) dist = secondDist; + + if (rayTriangleIntersection(org, Vec3d(1.0, 0.0, 0.0), prim.a, prim.d, prim.c, t)) { + if (t < edgeData.mXDist) { + edgeData.mXDist = float(t); + edgeData.mXPrim = prim.index; + intersecting = true; + } + } + + if (rayTriangleIntersection(org, Vec3d(0.0, 1.0, 0.0), prim.a, prim.d, prim.c, t)) { + if (t < edgeData.mYDist) { + edgeData.mYDist = float(t); + edgeData.mYPrim = prim.index; + intersecting = true; + } + } + + if (rayTriangleIntersection(org, Vec3d(0.0, 0.0, 1.0), prim.a, prim.d, prim.c, t)) { + if (t < edgeData.mZDist) { + edgeData.mZDist = float(t); + edgeData.mZPrim = prim.index; + intersecting = true; + } + } + } + + if (intersecting) mAccessor.setValue(ijk, edgeData); + + return (dist < 0.86602540378443861); +} + + +inline bool +MeshToVoxelEdgeData::GenEdgeData::rayTriangleIntersection( + const Vec3d& origin, const Vec3d& dir, + const Vec3d& a, const Vec3d& b, const Vec3d& c, + double& t) +{ + // Check if ray is parallel with triangle + + Vec3d e1 = b - a; + Vec3d e2 = c - a; + Vec3d s1 = dir.cross(e2); + + double divisor = s1.dot(e1); + if (!(std::abs(divisor) > 0.0)) return false; + + // Compute barycentric coordinates + + double inv_divisor = 1.0 / divisor; + Vec3d d = origin - a; + double b1 = d.dot(s1) * inv_divisor; + + if (b1 < 0.0 || b1 > 1.0) return false; + + Vec3d s2 = d.cross(e1); + double b2 = dir.dot(s2) * inv_divisor; + + if (b2 < 0.0 || (b1 + b2) > 1.0) return false; + + // Compute distance to intersection point + + t = e2.dot(s2) * inv_divisor; + return (t < 0.0) ? false : true; +} + + +//////////////////////////////////////// + + +inline +MeshToVoxelEdgeData::MeshToVoxelEdgeData() + : mTree(EdgeData()) +{ +} + + +inline void +MeshToVoxelEdgeData::convert( + const std::vector& pointList, + const std::vector& polygonList) +{ + GenEdgeData converter(pointList, polygonList); + converter.run(); + + mTree.clear(); + mTree.merge(converter.tree()); +} + + +inline void +MeshToVoxelEdgeData::getEdgeData( + Accessor& acc, + const Coord& ijk, + std::vector& points, + std::vector& primitives) +{ + EdgeData data; + Vec3d point; + + Coord coord = ijk; + + if (acc.probeValue(coord, data)) { + + if (data.mXPrim != util::INVALID_IDX) { + point[0] = double(coord[0]) + data.mXDist; + point[1] = double(coord[1]); + point[2] = double(coord[2]); + + points.push_back(point); + primitives.push_back(data.mXPrim); + } + + if (data.mYPrim != util::INVALID_IDX) { + point[0] = double(coord[0]); + point[1] = double(coord[1]) + data.mYDist; + point[2] = double(coord[2]); + + points.push_back(point); + primitives.push_back(data.mYPrim); + } + + if (data.mZPrim != util::INVALID_IDX) { + point[0] = double(coord[0]); + point[1] = double(coord[1]); + point[2] = double(coord[2]) + data.mZDist; + + points.push_back(point); + primitives.push_back(data.mZPrim); + } + + } + + coord[0] += 1; + + if (acc.probeValue(coord, data)) { + + if (data.mYPrim != util::INVALID_IDX) { + point[0] = double(coord[0]); + point[1] = double(coord[1]) + data.mYDist; + point[2] = double(coord[2]); + + points.push_back(point); + primitives.push_back(data.mYPrim); + } + + if (data.mZPrim != util::INVALID_IDX) { + point[0] = double(coord[0]); + point[1] = double(coord[1]); + point[2] = double(coord[2]) + data.mZDist; + + points.push_back(point); + primitives.push_back(data.mZPrim); + } + } + + coord[2] += 1; + + if (acc.probeValue(coord, data)) { + if (data.mYPrim != util::INVALID_IDX) { + point[0] = double(coord[0]); + point[1] = double(coord[1]) + data.mYDist; + point[2] = double(coord[2]); + + points.push_back(point); + primitives.push_back(data.mYPrim); + } + } + + coord[0] -= 1; + + if (acc.probeValue(coord, data)) { + + if (data.mXPrim != util::INVALID_IDX) { + point[0] = double(coord[0]) + data.mXDist; + point[1] = double(coord[1]); + point[2] = double(coord[2]); + + points.push_back(point); + primitives.push_back(data.mXPrim); + } + + if (data.mYPrim != util::INVALID_IDX) { + point[0] = double(coord[0]); + point[1] = double(coord[1]) + data.mYDist; + point[2] = double(coord[2]); + + points.push_back(point); + primitives.push_back(data.mYPrim); + } + } + + + coord[1] += 1; + + if (acc.probeValue(coord, data)) { + + if (data.mXPrim != util::INVALID_IDX) { + point[0] = double(coord[0]) + data.mXDist; + point[1] = double(coord[1]); + point[2] = double(coord[2]); + + points.push_back(point); + primitives.push_back(data.mXPrim); + } + } + + coord[2] -= 1; + + if (acc.probeValue(coord, data)) { + + if (data.mXPrim != util::INVALID_IDX) { + point[0] = double(coord[0]) + data.mXDist; + point[1] = double(coord[1]); + point[2] = double(coord[2]); + + points.push_back(point); + primitives.push_back(data.mXPrim); + } + + if (data.mZPrim != util::INVALID_IDX) { + point[0] = double(coord[0]); + point[1] = double(coord[1]); + point[2] = double(coord[2]) + data.mZDist; + + points.push_back(point); + primitives.push_back(data.mZPrim); + } + } + + coord[0] += 1; + + if (acc.probeValue(coord, data)) { + + if (data.mZPrim != util::INVALID_IDX) { + point[0] = double(coord[0]); + point[1] = double(coord[1]); + point[2] = double(coord[2]) + data.mZDist; + + points.push_back(point); + primitives.push_back(data.mZPrim); + } + } +} + + +template +inline typename GridType::Ptr +createLevelSetBox(const math::BBox& bbox, + const openvdb::math::Transform& xform, + typename VecType::ValueType halfWidth) +{ + const Vec3s pmin = Vec3s(xform.worldToIndex(bbox.min())); + const Vec3s pmax = Vec3s(xform.worldToIndex(bbox.max())); + + Vec3s points[8]; + points[0] = Vec3s(pmin[0], pmin[1], pmin[2]); + points[1] = Vec3s(pmin[0], pmin[1], pmax[2]); + points[2] = Vec3s(pmax[0], pmin[1], pmax[2]); + points[3] = Vec3s(pmax[0], pmin[1], pmin[2]); + points[4] = Vec3s(pmin[0], pmax[1], pmin[2]); + points[5] = Vec3s(pmin[0], pmax[1], pmax[2]); + points[6] = Vec3s(pmax[0], pmax[1], pmax[2]); + points[7] = Vec3s(pmax[0], pmax[1], pmin[2]); + + Vec4I faces[6]; + faces[0] = Vec4I(0, 1, 2, 3); // bottom + faces[1] = Vec4I(7, 6, 5, 4); // top + faces[2] = Vec4I(4, 5, 1, 0); // front + faces[3] = Vec4I(6, 7, 3, 2); // back + faces[4] = Vec4I(0, 3, 7, 4); // left + faces[5] = Vec4I(1, 5, 6, 2); // right + + QuadAndTriangleDataAdapter mesh(points, 8, faces, 6); + + return meshToVolume(mesh, xform, halfWidth, halfWidth); +} + + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_MESH_TO_VOLUME_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/Morphology.h b/nuparu/include/openvdb/include/openvdb/tools/Morphology.h new file mode 100644 index 00000000..9fac07cb --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/Morphology.h @@ -0,0 +1,1097 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 Morphology.h +/// +/// @brief Implementation of morphological dilation and erosion. +/// +/// @note By design the morphological operations only change the +/// state of voxels, not their values. If one desires to +/// change the values of voxels that change state an efficient +/// technique is to construct a boolean mask by performing a +/// topology difference between the original and final grids. +/// +/// @todo Extend erosion with 18 and 26 neighbors (coming soon!) +/// +/// @author Ken Museth +/// + +#ifndef OPENVDB_TOOLS_MORPHOLOGY_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_MORPHOLOGY_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include +#include // for isApproxEqual() +#include +#include +#include +#include +#include +#include +#include +#include "Prune.h"// for pruneLevelSet +#include "ValueTransformer.h" // for foreach() + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Voxel topology of nearest neighbors +/// @details +///
+///
NN_FACE +///
face adjacency (6 nearest neighbors, defined as all neighbor +/// voxels connected along one of the primary axes) +/// +///
NN_FACE_EDGE +///
face and edge adjacency (18 nearest neighbors, defined as all +/// neighbor voxels connected along either one or two of the primary axes) +/// +///
NN_FACE_EDGE_VERTEX +///
face, edge and vertex adjacency (26 nearest neighbors, defined +/// as all neighbor voxels connected along either one, two or all +/// three of the primary axes) +///
+enum NearestNeighbors { NN_FACE = 6, NN_FACE_EDGE = 18, NN_FACE_EDGE_VERTEX = 26 }; + +/// @brief Different policies when dilating trees with active tiles +/// @details +///
+///
IGNORE_TILES +///
Active tiles are ignores, i.e. only active voxels are dilates. +/// +///
EXPAND_TILES +///
Active tiles are expanded into active voxels and then dilated. +/// +///
PRESERVE_TILES +///
Active tiles remain unchanged but they still contribute to the +/// dilation as if they were active voxels. +///
+enum TilePolicy { IGNORE_TILES, EXPAND_TILES, PRESERVE_TILES }; + +/// @brief Topologically dilate all active values (i.e. both voxels +/// and tiles) in a tree using one of three nearest neighbor +/// connectivity patterns. +/// @note This method is fully multi-threaded and support active tiles! +/// +/// @param tree tree to be dilated +/// @param iterations number of iterations to apply the dilation +/// @param nn connectivity pattern of the dilation: either +/// face-adjacent (6 nearest neighbors), face- and edge-adjacent +/// (18 nearest neighbors) or face-, edge- and vertex-adjacent (26 +/// nearest neighbors). +/// @param mode Defined the policy for handling active tiles +/// (see above for details) +/// +/// @note The values of any voxels are unchanged. +template OPENVDB_STATIC_SPECIALIZATION +inline void dilateActiveValues(TreeType& tree, + int iterations = 1, + NearestNeighbors nn = NN_FACE, + TilePolicy mode = PRESERVE_TILES); + +/// @brief Topologically dilate all active values (i.e. both voxels +/// and tiles) in a tree using one of three nearest neighbor +/// connectivity patterns. +/// +/// @warning Unlike the method above this one takes a LeafManger, +/// however (unlike dilateVoxels method below) it offers no performance +/// advantage over the one that takes a tree. Its merely included for +/// API compatability. The leaf nodes in the manger are updated +/// after the dilation, which incurres a (very small) overhead. +/// +/// @note This method is fully multi-threaded and support active tiles! +/// +/// @param manager Leaf node manager for the tree to be dilated +/// @param iterations number of iterations to apply the dilation +/// @param nn connectivity pattern of the dilation: either +/// face-adjacent (6 nearest neighbors), face- and edge-adjacent +/// (18 nearest neighbors) or face-, edge- and vertex-adjacent (26 +/// nearest neighbors). +/// @param mode Defined the policy for handling active tiles +/// (see above for details) +/// +/// @note The values of any voxels are unchanged. +template OPENVDB_STATIC_SPECIALIZATION +inline void dilateActiveValues(tree::LeafManager& manager, + int iterations = 1, + NearestNeighbors nn = NN_FACE, + TilePolicy mode = PRESERVE_TILES); + + +/// @brief Topologically dilate all leaf-level active voxels in a tree +/// using one of three nearest neighbor connectivity patterns. +/// @warning This method is NOT multi-threaded and ignores active tiles! +/// +/// @param tree tree to be dilated +/// @param iterations number of iterations to apply the dilation +/// @param nn connectivity pattern of the dilation: either +/// face-adjacent (6 nearest neighbors), face- and edge-adjacent +/// (18 nearest neighbors) or face-, edge- and vertex-adjacent (26 +/// nearest neighbors). +/// +/// @note The values of any voxels are unchanged. +template OPENVDB_STATIC_SPECIALIZATION +inline void dilateVoxels(TreeType& tree, + int iterations = 1, + NearestNeighbors nn = NN_FACE); + +/// @brief Topologically dilate all leaf-level active voxels in a tree +/// using one of three nearest neighbor connectivity patterns. +/// @warning This method is NOT multi-threaded and ignores active tiles! +/// +/// @param manager LeafManager containing the tree to be dilated. +/// @param iterations number of iterations to apply the dilation +/// @param nn connectivity pattern of the dilation: either +/// face-adjacent (6 nearest neighbors), face- and edge-adjacent +/// (18 nearest neighbors) or face-, edge- and vertex-adjacent (26 +/// nearest neighbors). +/// +/// @note The values of any voxels are unchanged. +template OPENVDB_STATIC_SPECIALIZATION +inline void dilateVoxels(tree::LeafManager& manager, + int iterations = 1, + NearestNeighbors nn = NN_FACE); + + +//@{ +/// @brief Topologically erode all leaf-level active voxels in the given tree. +/// @details That is, shrink the set of active voxels by @a iterations 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 OPENVDB_STATIC_SPECIALIZATION +inline void erodeVoxels(TreeType& tree, + int iterations=1, + NearestNeighbors nn = NN_FACE); + +template OPENVDB_STATIC_SPECIALIZATION +inline void erodeVoxels(tree::LeafManager& manager, + int iterations = 1, + NearestNeighbors nn = NN_FACE); +//@} + + +/// @brief Mark as active any inactive tiles or voxels in the given grid or tree +/// whose values are equal to @a value (optionally to within the given @a tolerance). +template +inline void activate( + GridOrTree&, + const typename GridOrTree::ValueType& value, + const typename GridOrTree::ValueType& tolerance = zeroVal() +); + + +/// @brief Mark as inactive any active tiles or voxels in the given grid or tree +/// whose values are equal to @a value (optionally to within the given @a tolerance). +template +inline void deactivate( + GridOrTree&, + const typename GridOrTree::ValueType& value, + const typename GridOrTree::ValueType& tolerance = zeroVal() +); + + +//////////////////////////////////////// + + +/// Mapping from a Log2Dim to a data type of size 2^Log2Dim bits +template struct DimToWord {}; +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 +class Morphology +{ +public: + typedef tree::LeafManager 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; } + + /// @brief Face-adjacent dilation pattern + void dilateVoxels6(); + /// @brief Face- and edge-adjacent dilation pattern. + void dilateVoxels18(); + /// @brief Face-, edge- and vertex-adjacent dilation pattern. + void dilateVoxels26(); + void dilateVoxels(int iterations = 1, NearestNeighbors nn = NN_FACE); + + /// @brief Face-adjacent erosion pattern. + void erodeVoxels6() { mSteps = 1; this->doErosion(NN_FACE); } + /// @brief Face- and edge-adjacent erosion pattern. + void erodeVoxels18() { mSteps = 1; this->doErosion(NN_FACE_EDGE); } + /// @brief Face-, edge- and vertex-adjacent erosion pattern. + void erodeVoxels26() { mSteps = 1; this->doErosion(NN_FACE_EDGE_VERTEX); } + void erodeVoxels(int iterations = 1, NearestNeighbors nn = NN_FACE) + { + mSteps = iterations; + this->doErosion(nn); + } + +protected: + + void doErosion(NearestNeighbors nn); + + typedef typename TreeType::LeafNodeType LeafType; + typedef typename LeafType::NodeMaskType MaskType; + typedef tree::ValueAccessor 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::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() { leaf = NULL; init = true; } + template + void scatter(AccessorType& acc, const Coord &xyz, int indx, Word mask) + { + 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); + } +#ifndef _MSC_VER // Visual C++ doesn't guarantee thread-safe initialization of local statics + static +#endif + const int N = (LEAF_DIM - 1)*(DY + DX*LEAF_DIM); + if (leaf) leaf->getValueMask().template getWord(indx-N) |= mask; + } + + template + 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); + } +#ifndef _MSC_VER // Visual C++ doesn't guarantee thread-safe initialization of local statics + static +#endif + const int N = (LEAF_DIM -1 )*(DY + DX*LEAF_DIM); + return leaf ? leaf->getValueMask().template getWord(indx-N) + : isOn ? ~Word(0) : Word(0); + } + };// Neighbor + + struct LeafCache + { + LeafCache(size_t n, TreeType& tree) : size(n), leafs(new LeafType*[n]), acc(tree) + { + onTile.setValuesOn(); + this->clear(); + } + ~LeafCache() { delete [] leafs; } + LeafType*& operator[](int offset) { return leafs[offset]; } + inline void clear() { for (size_t i=0; igetValueMask().template getWord(indx) |= mask; + } + template + inline void scatter(int n, int indx) + { + if (!leafs[n]) { + const Coord xyz = origin->offsetBy(DX*LEAF_DIM, DY*LEAF_DIM, DZ*LEAF_DIM); + leafs[n] = acc.probeLeaf(xyz); + if (!leafs[n]) leafs[n] = acc.isValueOn(xyz) ? &onTile : acc.touchLeaf(xyz); + } + this->scatter(n, indx - (LEAF_DIM - 1)*(DY + DX*LEAF_DIM)); + } + inline Word gather(int n, int indx) + { + assert(leafs[n]); + return leafs[n]->getValueMask().template getWord(indx); + } + template + inline Word gather(int n, int indx) + { + if (!leafs[n]) { + const Coord xyz = origin->offsetBy(DX*LEAF_DIM, DY*LEAF_DIM, DZ*LEAF_DIM); + leafs[n] = acc.probeLeaf(xyz); + if (!leafs[n]) leafs[n] = acc.isValueOn(xyz) ? &onTile : &offTile; + } + return this->gather(n, indx - (LEAF_DIM -1 )*(DY + DX*LEAF_DIM)); + } + // Scatters in the xy face-directions relative to leaf i1 + void scatterFacesXY(int x, int y, int i1, int n, int i2); + + // Scatters in the xy edge-directions relative to leaf i1 + void scatterEdgesXY(int x, int y, int i1, int n, int i2); + + Word gatherFacesXY(int x, int y, int i1, int n, int i2); + + Word gatherEdgesXY(int x, int y, int i1, int n, int i2); + + const Coord* origin; + size_t size; + LeafType** leafs; + LeafType onTile, offTile; + AccessorType acc; + Word mask; + };// LeafCache + + struct ErodeVoxelsOp { + typedef tbb::blocked_range RangeT; + ErodeVoxelsOp(std::vector& masks, ManagerType& manager) + : mTask(0), mSavedMasks(masks) , mManager(manager) {} + void runParallel(NearestNeighbors nn); + void operator()(const RangeT& r) const {mTask(const_cast(this), r);} + void erode6( const RangeT&) const; + void erode18(const RangeT&) const; + void erode26(const RangeT&) const; + private: + typedef typename boost::function FuncT; + FuncT mTask; + std::vector& mSavedMasks; + ManagerType& mManager; + };// ErodeVoxelsOp + + struct MaskManager { + MaskManager(std::vector& 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& 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& mMasks; + ManagerType& mManager; + bool mSaveMasks; + };// MaskManager + + struct UpdateMasks { + UpdateMasks(const std::vector& masks, ManagerType& manager) + : mMasks(masks), mManager(manager) {} + void update() { tbb::parallel_for(mManager.getRange(), *this); } + void operator()(const tbb::blocked_range& r) const { + for (size_t i=r.begin(); i& mMasks; + ManagerType& mManager; + }; + struct CopyMasks { + CopyMasks(std::vector& masks, const ManagerType& manager) + : mMasks(masks), mManager(manager) {} + void copy() { tbb::parallel_for(mManager.getRange(), *this); } + void operator()(const tbb::blocked_range& r) const { + for (size_t i=r.begin(); i& mMasks; + const ManagerType& mManager; + }; + void copyMasks(std::vector& a, const ManagerType& b) {CopyMasks c(a, b); c.copy();} +};// Morphology + + +template +inline void +Morphology::dilateVoxels(int iterations, NearestNeighbors nn) +{ + for (int i=0; idilateVoxels18(); + break; + case NN_FACE_EDGE_VERTEX: + this->dilateVoxels26(); + break; + default: + this->dilateVoxels6(); + } + } +} + + +template +inline void +Morphology::dilateVoxels6() +{ + /// @todo Currently operates only on leaf voxels; need to extend to tiles. + const int leafCount = static_cast(mManager->leafCount()); + + // Save the value masks of all leaf nodes. + std::vector savedMasks(leafCount); + this->copyMasks(savedMasks, *mManager); + LeafCache cache(7, mManager->tree()); + for (int leafIdx = 0; leafIdx < leafCount; ++leafIdx) { + const MaskType& oldMask = savedMasks[leafIdx];//original bit-mask of current leaf node + cache[0] = &mManager->leaf(leafIdx); + cache.setOrigin(cache[0]->origin()); + 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. + if (const Word w = oldMask.template getWord(n)) { + + // Dilate the current leaf in the +z and -z direction + cache.mask = Word(w | (w>>1) | (w<<1)); cache.scatter(0, n); + + // Dilate into neighbor leaf in the -z direction + if ( (cache.mask = Word(w<<(LEAF_DIM-1))) ) { + cache.template scatter< 0, 0,-1>(1, n); + } + // Dilate into neighbor leaf in the +z direction + if ( (cache.mask = Word(w>>(LEAF_DIM-1))) ) { + cache.template scatter< 0, 0, 1>(2, n); + } + // Dilate in the xy-face directions relative to the center leaf + cache.mask = w; cache.scatterFacesXY(x, y, 0, n, 3); + } + }// loop over y + }//loop over x + cache.clear(); + }//loop over leafs + + mManager->rebuildLeafArray(); +}//dilateVoxels6 + + +template +inline void +Morphology::dilateVoxels18() +{ + /// @todo Currently operates only on leaf voxels; need to extend to tiles. + const int leafCount = static_cast(mManager->leafCount()); + + // Save the value masks of all leaf nodes. + std::vector savedMasks(leafCount); + this->copyMasks(savedMasks, *mManager); + LeafCache cache(19, mManager->tree()); + Coord orig_mz, orig_pz;//origins of neighbor leaf nodes in the -z and +z directions + for (int leafIdx = 0; leafIdx < leafCount; ++leafIdx) { + const MaskType& oldMask = savedMasks[leafIdx];//original bit-mask of current leaf node + cache[0] = &mManager->leaf(leafIdx); + orig_mz = cache[0]->origin().offsetBy(0, 0, -LEAF_DIM); + orig_pz = cache[0]->origin().offsetBy(0, 0, LEAF_DIM); + for (int x = 0; x < LEAF_DIM; ++x ) { + for (int y = 0, n = (x << LEAF_LOG2DIM); y < LEAF_DIM; ++y, ++n) { + if (const Word w = oldMask.template getWord(n)) { + { + cache.mask = Word(w | (w>>1) | (w<<1)); + cache.setOrigin(cache[0]->origin()); + cache.scatter(0, n); + cache.scatterFacesXY(x, y, 0, n, 3); + cache.mask = w; + cache.scatterEdgesXY(x, y, 0, n, 3); + } + if ( (cache.mask = Word(w<<(LEAF_DIM-1))) ) { + cache.setOrigin(cache[0]->origin()); + cache.template scatter< 0, 0,-1>(1, n); + cache.setOrigin(orig_mz); + cache.scatterFacesXY(x, y, 1, n, 11); + } + if ( (cache.mask = Word(w>>(LEAF_DIM-1))) ) { + cache.setOrigin(cache[0]->origin()); + cache.template scatter< 0, 0, 1>(2, n); + cache.setOrigin(orig_pz); + cache.scatterFacesXY(x, y, 2, n, 15); + } + } + }// loop over y + }//loop over x + cache.clear(); + }//loop over leafs + + mManager->rebuildLeafArray(); +}// dilateVoxels18 + + +template +inline void +Morphology::dilateVoxels26() +{ + const int leafCount = static_cast(mManager->leafCount()); + + // Save the value masks of all leaf nodes. + std::vector savedMasks(leafCount); + this->copyMasks(savedMasks, *mManager); + LeafCache cache(27, mManager->tree()); + Coord orig_mz, orig_pz;//origins of neighbor leaf nodes in the -z and +z directions + for (int leafIdx = 0; leafIdx < leafCount; ++leafIdx) { + const MaskType& oldMask = savedMasks[leafIdx];//original bit-mask of current leaf node + cache[0] = &mManager->leaf(leafIdx); + orig_mz = cache[0]->origin().offsetBy(0, 0, -LEAF_DIM); + orig_pz = cache[0]->origin().offsetBy(0, 0, LEAF_DIM); + for (int x = 0; x < LEAF_DIM; ++x ) { + for (int y = 0, n = (x << LEAF_LOG2DIM); y < LEAF_DIM; ++y, ++n) { + if (const Word w = oldMask.template getWord(n)) { + { + cache.mask = Word(w | (w>>1) | (w<<1)); + cache.setOrigin(cache[0]->origin()); + cache.scatter(0, n); + cache.scatterFacesXY(x, y, 0, n, 3); + cache.scatterEdgesXY(x, y, 0, n, 3); + } + if ( (cache.mask = Word(w<<(LEAF_DIM-1))) ) { + cache.setOrigin(cache[0]->origin()); + cache.template scatter< 0, 0,-1>(1, n); + cache.setOrigin(orig_mz); + cache.scatterFacesXY(x, y, 1, n, 11); + cache.scatterEdgesXY(x, y, 1, n, 11); + } + if ( (cache.mask = Word(w>>(LEAF_DIM-1))) ) { + cache.setOrigin(cache[0]->origin()); + cache.template scatter< 0, 0, 1>(2, n); + cache.setOrigin(orig_pz); + cache.scatterFacesXY(x, y, 2, n, 19); + cache.scatterEdgesXY(x, y, 2, n, 19); + } + } + }// loop over y + }//loop over x + cache.clear(); + }//loop over leafs + + mManager->rebuildLeafArray(); +}// dilateVoxels26 + + +template +inline void +Morphology::LeafCache::scatterFacesXY(int x, int y, int i1, int n, int i2) +{ + // dilate current leaf or neighbor in the -x direction + if (x > 0) { + this->scatter(i1, n-LEAF_DIM); + } else { + this->template scatter<-1, 0, 0>(i2, n); + } + // dilate current leaf or neighbor in the +x direction + if (x < LEAF_DIM-1) { + this->scatter(i1, n+LEAF_DIM); + } else { + this->template scatter< 1, 0, 0>(i2+1, n); + } + // dilate current leaf or neighbor in the -y direction + if (y > 0) { + this->scatter(i1, n-1); + } else { + this->template scatter< 0,-1, 0>(i2+2, n); + } + // dilate current leaf or neighbor in the +y direction + if (y < LEAF_DIM-1) { + this->scatter(i1, n+1); + } else { + this->template scatter< 0, 1, 0>(i2+3, n); + } +} + + +template +inline void +Morphology::LeafCache::scatterEdgesXY(int x, int y, int i1, int n, int i2) +{ + if (x > 0) { + if (y > 0) { + this->scatter(i1, n-LEAF_DIM-1); + } else { + this->template scatter< 0,-1, 0>(i2+2, n-LEAF_DIM); + } + if (y < LEAF_DIM-1) { + this->scatter(i1, n-LEAF_DIM+1); + } else { + this->template scatter< 0, 1, 0>(i2+3, n-LEAF_DIM); + } + } else { + if (y < LEAF_DIM-1) { + this->template scatter<-1, 0, 0>(i2 , n+1); + } else { + this->template scatter<-1, 1, 0>(i2+7, n ); + } + if (y > 0) { + this->template scatter<-1, 0, 0>(i2 , n-1); + } else { + this->template scatter<-1,-1, 0>(i2+4, n ); + } + } + if (x < LEAF_DIM-1) { + if (y > 0) { + this->scatter(i1, n+LEAF_DIM-1); + } else { + this->template scatter< 0,-1, 0>(i2+2, n+LEAF_DIM); + } + if (y < LEAF_DIM-1) { + this->scatter(i1, n+LEAF_DIM+1); + } else { + this->template scatter< 0, 1, 0>(i2+3, n+LEAF_DIM); + } + } else { + if (y > 0) { + this->template scatter< 1, 0, 0>(i2+1, n-1); + } else { + this->template scatter< 1,-1, 0>(i2+6, n ); + } + if (y < LEAF_DIM-1) { + this->template scatter< 1, 0, 0>(i2+1, n+1); + } else { + this->template scatter< 1, 1, 0>(i2+5, n ); + } + } +} + + +template +inline void +Morphology::ErodeVoxelsOp::runParallel(NearestNeighbors nn) +{ + switch (nn) { + case NN_FACE_EDGE: + mTask = boost::bind(&ErodeVoxelsOp::erode18, _1, _2); + break; + case NN_FACE_EDGE_VERTEX: + mTask = boost::bind(&ErodeVoxelsOp::erode26, _1, _2); + break; + default: + mTask = boost::bind(&ErodeVoxelsOp::erode6, _1, _2); + } + tbb::parallel_for(mManager.getRange(), *this); +} + + +template +inline typename Morphology::Word +Morphology::LeafCache::gatherFacesXY(int x, int y, int i1, int n, int i2) +{ + // erode current leaf or neighbor in negative x-direction + Word w = x>0 ? this->gather(i1,n-LEAF_DIM) : this->template gather<-1,0,0>(i2, n); + + // erode current leaf or neighbor in positive x-direction + w = Word(w & (xgather(i1,n+LEAF_DIM):this->template gather<1,0,0>(i2+1,n))); + + // erode current leaf or neighbor in negative y-direction + w = Word(w & (y>0 ? this->gather(i1, n-1) : this->template gather<0,-1,0>(i2+2, n))); + + // erode current leaf or neighbor in positive y-direction + w = Word(w & (ygather(i1, n+1) : this->template gather<0,1,0>(i2+3, n))); + + return w; +} + + +template +inline typename Morphology::Word +Morphology::LeafCache::gatherEdgesXY(int x, int y, int i1, int n, int i2) +{ + Word w = ~Word(0); + + if (x > 0) { + w &= y > 0 ? this->gather(i1, n-LEAF_DIM-1) : + this->template gather< 0,-1, 0>(i2+2, n-LEAF_DIM); + w &= y < LEAF_DIM-1 ? this->gather(i1, n-LEAF_DIM+1) : + this->template gather< 0, 1, 0>(i2+3, n-LEAF_DIM); + } else { + w &= y < LEAF_DIM-1 ? this->template gather<-1, 0, 0>(i2 , n+1): + this->template gather<-1, 1, 0>(i2+7, n ); + w &= y > 0 ? this->template gather<-1, 0, 0>(i2 , n-1): + this->template gather<-1,-1, 0>(i2+4, n ); + } + if (x < LEAF_DIM-1) { + w &= y > 0 ? this->gather(i1, n+LEAF_DIM-1) : + this->template gather< 0,-1, 0>(i2+2, n+LEAF_DIM); + w &= y < LEAF_DIM-1 ? this->gather(i1, n+LEAF_DIM+1) : + this->template gather< 0, 1, 0>(i2+3, n+LEAF_DIM); + } else { + w &= y > 0 ? this->template gather< 1, 0, 0>(i2+1, n-1): + this->template gather< 1,-1, 0>(i2+6, n ); + w &= y < LEAF_DIM-1 ? this->template gather< 1, 0, 0>(i2+1, n+1): + this->template gather< 1, 1, 0>(i2+5, n ); + } + + return w; +} + + +template +inline void +Morphology::ErodeVoxelsOp::erode6(const RangeT& range) const +{ + LeafCache cache(7, mManager.tree()); + for (size_t leafIdx = range.begin(); leafIdx < range.end(); ++leafIdx) { + cache[0] = &mManager.leaf(leafIdx); + if (cache[0]->isEmpty()) continue; + cache.setOrigin(cache[0]->origin()); + MaskType& newMask = mSavedMasks[leafIdx];//original bit-mask of 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. + if (Word& w = newMask.template getWord(n)) { + + // erode in two z directions (this is first since it uses the original w) + w = Word(w & + (Word(w<<1 | (cache.template gather<0,0,-1>(1, n)>>(LEAF_DIM-1))) & + Word(w>>1 | (cache.template gather<0,0, 1>(2, n)<<(LEAF_DIM-1))))); + + w = Word(w & cache.gatherFacesXY(x, y, 0, n, 3)); + } + }// loop over y + }//loop over x + cache.clear(); + }//loop over leafs +} + + +template +inline void +Morphology::ErodeVoxelsOp::erode18(const RangeT&) const +{ + OPENVDB_THROW(NotImplementedError, "tools::erode18 is not implemented yet!"); +} + + +template +inline void +Morphology::ErodeVoxelsOp::erode26(const RangeT&) const +{ + OPENVDB_THROW(NotImplementedError, "tools::erode26 is not implemented yet!"); +} + + +template +inline void +Morphology::doErosion(NearestNeighbors nn) +{ + /// @todo Currently operates only on leaf voxels; need to extend to tiles. + const size_t leafCount = mManager->leafCount(); + + // Save the value masks of all leaf nodes. + std::vector savedMasks(leafCount); + this->copyMasks(savedMasks, *mManager); + UpdateMasks a(savedMasks, *mManager); + ErodeVoxelsOp erode(savedMasks, *mManager); + + for (int i = 0; i < mSteps; ++i) { + erode.runParallel(nn); + a.update(); + } + + tools::pruneLevelSet(mManager->tree()); +} + + +//////////////////////////////////////// + + +template +OPENVDB_STATIC_SPECIALIZATION inline void +dilateVoxels(tree::LeafManager& manager, int iterations, NearestNeighbors nn) +{ + if (iterations > 0 ) { + Morphology m(&manager); + m.dilateVoxels(iterations, nn); + } +} + +template +OPENVDB_STATIC_SPECIALIZATION inline void +dilateVoxels(TreeType& tree, int iterations, NearestNeighbors nn) +{ + if (iterations > 0 ) { + Morphology m(tree); + m.dilateVoxels(iterations, nn); + } +} + +template +OPENVDB_STATIC_SPECIALIZATION inline void +erodeVoxels(tree::LeafManager& manager, int iterations, NearestNeighbors nn) +{ + if (iterations > 0 ) { + Morphology m(&manager); + m.erodeVoxels(iterations, nn); + } +} + +template +OPENVDB_STATIC_SPECIALIZATION inline void +erodeVoxels(TreeType& tree, int iterations, NearestNeighbors nn) +{ + if (iterations > 0 ) { + Morphology m(tree); + m.erodeVoxels(iterations, nn); + } +} + + +//////////////////////////////////////// + + +namespace activation { + +template +class ActivationOp +{ +public: + typedef typename TreeType::ValueType ValueT; + + ActivationOp(bool state, const ValueT& val, const ValueT& tol) + : mActivate(state) + , mValue(val) + , mTolerance(tol) + {} + + void operator()(const typename TreeType::ValueOnIter& it) const + { + if (math::isApproxEqual(*it, mValue, mTolerance)) { + it.setValueOff(); + } + } + + void operator()(const typename TreeType::ValueOffIter& it) const + { + if (math::isApproxEqual(*it, mValue, mTolerance)) { + it.setActiveState(/*on=*/true); + } + } + + void operator()(const typename TreeType::LeafIter& lit) const + { + typedef typename TreeType::LeafNodeType LeafT; + LeafT& leaf = *lit; + if (mActivate) { + for (typename LeafT::ValueOffIter it = leaf.beginValueOff(); it; ++it) { + if (math::isApproxEqual(*it, mValue, mTolerance)) { + leaf.setValueOn(it.pos()); + } + } + } else { + for (typename LeafT::ValueOnIter it = leaf.beginValueOn(); it; ++it) { + if (math::isApproxEqual(*it, mValue, mTolerance)) { + leaf.setValueOff(it.pos()); + } + } + } + } + +private: + bool mActivate; + const ValueT mValue, mTolerance; +}; // class ActivationOp + +} // namespace activation + + +template +inline void +activate(GridOrTree& gridOrTree, const typename GridOrTree::ValueType& value, + const typename GridOrTree::ValueType& tolerance) +{ + typedef TreeAdapter Adapter; + typedef typename Adapter::TreeType TreeType; + + TreeType& tree = Adapter::tree(gridOrTree); + + activation::ActivationOp op(/*activate=*/true, value, tolerance); + + // Process all leaf nodes in parallel. + foreach(tree.beginLeaf(), op); + + // Process all other inactive values serially (because changing active states + // is not thread-safe unless no two threads modify the same node). + typename TreeType::ValueOffIter it = tree.beginValueOff(); + it.setMaxDepth(tree.treeDepth() - 2); + foreach(it, op, /*threaded=*/false); +} + + +template +inline void +deactivate(GridOrTree& gridOrTree, const typename GridOrTree::ValueType& value, + const typename GridOrTree::ValueType& tolerance) +{ + typedef TreeAdapter Adapter; + typedef typename Adapter::TreeType TreeType; + + TreeType& tree = Adapter::tree(gridOrTree); + + activation::ActivationOp op(/*activate=*/false, value, tolerance); + + // Process all leaf nodes in parallel. + foreach(tree.beginLeaf(), op); + + // Process all other active values serially (because changing active states + // is not thread-safe unless no two threads modify the same node). + typename TreeType::ValueOnIter it = tree.beginValueOn(); + it.setMaxDepth(tree.treeDepth() - 2); + foreach(it, op, /*threaded=*/false); +} + +/// @brief Class that performs multi-threaded dilation with support for active tiles. +/// @warning Dont use this class directly, instead call the function dilateActiveValues! +template +class DilationOp +{ + typedef typename TreeT::template ValueConverter::Type MaskT; + typedef tbb::enumerable_thread_specific PoolT; + typedef typename MaskT::LeafNodeType LeafT; + + // Very light-weight member data + const int mIter;// number of iterations + const tools::NearestNeighbors mNN;//enum to specify the dilation scheme + PoolT *mPool;// pointer to the thread-local pool of mask trees + LeafT **mLeafs;// raw array of pointers to leaf nodes + +public: + + DilationOp(TreeT &tree, int iterations, NearestNeighbors nn, TilePolicy mode) + : mIter(iterations), mNN(nn), mPool(NULL), mLeafs(NULL) + { + const size_t numLeafs = this->init( tree, mode ); + const size_t numThreads = size_t(tbb::task_scheduler_init::default_num_threads()); + const size_t grainSize = math::Max(size_t(1), numLeafs/(2*numThreads)); + + MaskT mask; + PoolT pool(mask);// Scoped thread-local storage of mask trees + mPool = &pool; + + tbb::parallel_for(tbb::blocked_range(mLeafs, mLeafs+numLeafs, grainSize), *this); + + delete [] mLeafs;// no more need for the array of leaf node pointers + + typedef typename PoolT::iterator IterT; + for (IterT it=pool.begin(); it!=pool.end(); ++it) mask.merge(*it);// fast stealing + + if (mode == PRESERVE_TILES) tools::prune(mask);//multithreaded + + tree.topologyUnion(mask);//multithreaded + } + + // This is required by tbb and should never be called directly + void operator()(const tbb::blocked_range &r) const + { + MaskT mask;// thread-local temporary mask tree + for (LeafT** it=r.begin(); it!=r.end(); ++it) mask.addLeaf( **it ); + tree::LeafManager manager(mask, r.begin(), r.end()); + tools::dilateVoxels(manager, mIter, mNN);// serial dilation of active voxels + mPool->local().merge(mask, MERGE_ACTIVE_STATES); + } +private: + + // Simple wrapper of a raw double-pointer to mimic a std container + struct MyArray { + typedef LeafT* value_type;//required by Tree::stealNodes + value_type* ptr; + MyArray(value_type* array) : ptr(array) {} + void push_back(value_type leaf) { *ptr++ = leaf; }//required by Tree::stealNodes + }; + + // Convert active tiles to leafs and de-construct the tree into a linear array of leafs. + size_t linearize(MaskT& mask, TilePolicy mode) + { + if (mode != IGNORE_TILES) mask.voxelizeActiveTiles();// light-weight since this is a mask tree + const size_t numLeafs = mask.leafCount(); + mLeafs = new LeafT*[numLeafs];// fast pre-allocation + MyArray tmp(mLeafs); + mask.stealNodes(tmp);// serializes the mask tree and leaves it empty + return numLeafs; + } + + template + typename boost::enable_if,size_t>::type init(T& tree, TilePolicy mode) + { + return this->linearize(tree, mode); + } + + template + typename boost::disable_if,size_t>::type init(const T& tree, TilePolicy mode) + { + MaskT mask(tree, false, true, TopologyCopy()); + return this->linearize(mask, mode); + } + +};// DilationOp + +template +OPENVDB_STATIC_SPECIALIZATION inline void +dilateActiveValues(TreeType& tree, int iterations, NearestNeighbors nn, TilePolicy mode) +{ + if (iterations > 0 ) DilationOp tmp(tree, iterations, nn, mode); +} + +template +OPENVDB_STATIC_SPECIALIZATION inline void +dilateActiveValues(tree::LeafManager& manager, + int iterations, + NearestNeighbors nn, + TilePolicy mode) +{ + if (iterations > 0 ) { + DilationOp tmp(manager.tree(), iterations, nn, mode); + manager.rebuildLeafArray(); + } +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_MORPHOLOGY_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/MultiResGrid.h b/nuparu/include/openvdb/include/openvdb/tools/MultiResGrid.h new file mode 100644 index 00000000..1a5fb57e --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/MultiResGrid.h @@ -0,0 +1,981 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 MultiResGrid.h +/// +/// @brief Ken Museth +/// +/// @warning This class is fairly new and as such has not seen a lot of +/// use in production. Please report any issues or request for new +/// features directly to ken.museth@dreamworks.com. +/// +/// @brief Multi-resolution grid that contains LoD sequences of trees +/// with powers of two refinements. +/// +/// @note While this class can arguably be used to implement a sparse +/// Multi-Grid solver it is currently intented as a means to +/// efficiently compute LoD levels for applications like rendering +/// +/// @note Prolongation means interpolation from coarse -> fine +/// @note Restriction means interpolation (or remapping) from fine -> coarse +/// + +#ifndef OPENVDB_TOOLS_MULTIRESGRID_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_MULTIRESGRID_HAS_BEEN_INCLUDED + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include + +class TestMultiResGrid; + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +template +class MultiResGrid: public MetaMap +{ +public: + + typedef boost::shared_ptr Ptr; + typedef boost::shared_ptr ConstPtr; + + typedef typename TreeType::ValueType ValueType; + typedef typename TreeType::ValueOnCIter ValueOnCIter; + typedef typename TreeType::ValueOnIter ValueOnIter; + typedef typename TreeType::Ptr TreePtr; + typedef typename TreeType::ConstPtr ConstTreePtr; + typedef typename Grid::Ptr GridPtr; + typedef typename Grid::ConstPtr ConstGridPtr; + + ////////////////////////////////////////////////////////////////////// + + /// @brief Constructor of empty grids + /// @param levels The number of trees in this MultiResGrid + /// @param background Background value + /// @param voxelSize Size of a (uniform voxel). Defaults to one. + /// @note The multiple grids are all empty. + MultiResGrid(size_t levels, ValueType background, double voxelSize = 1.0); + + /// @brief Given an initial high-resolution grid this constructor + /// generates all the coarser grids by means of restriction. + /// @param levels The number of trees in this MultiResGrid + /// @param grid High-resolution inout grid + /// @param useInjection Use restruction by injection, vs + /// full-weighting. It defaults to false and should rarely to used. + /// @note This constructor will perform a deep copy of the input + /// grid and use it as the highest level grid. + MultiResGrid(size_t levels, const Grid &grid, bool useInjection = false); + + /// @brief Given an initial high-resolution grid this constructor + /// generates all the coarser grids by means of restriction. + /// @param levels The number of trees in this MultiResGrid + /// @param grid High-resolution inout grid + /// @param useInjection Use restruction by injection, vs + /// full-weighting. It defaults to false and should rarely to used. + /// @note This constructor will steal the input input + /// grid and use it as the highest level grid. On output the grid + /// is empty. + MultiResGrid(size_t levels, GridPtr grid, bool useInjection = false); + + ////////////////////////////////////////////////////////////////////// + + /// @brief Return the number of levels, i.e. trees, in this MultiResGrid + /// @note level 0 is the finest level and numLevels()-1 is the coarsest + /// level. + size_t numLevels() const { return mTrees.size(); } + + /// @brief Return the level of the finest grid (always 0) + static size_t finestLevel() { return 0; } + + /// @brief Return the level of the coarest grid, i.e. numLevels()-1 + size_t coarsestLevel() const { return mTrees.size()-1; } + + ////////////////////////////////////////////////////////////////////// + + /// @brief Return a reference to the tree at the specified level + /// @param level The level of the tree to be returned + /// @note Level 0 is by definition the finest tree. + TreeType& tree(size_t level); + + /// @brief Return a const reference to the tree at the specified level + /// @param level The level of the tree to be returned + /// @note Level 0 is by definition the finest tree. + const TreeType& constTree(size_t level) const; + + /// @brief Return a shared pointer to the tree at the specified level + /// @param level The level of the tree to be returned + /// @note Level 0 is by definition the finest tree. + TreePtr treePtr(size_t level); + + /// @brief Return a const shared pointer to the tree at the specified level + /// @param level The level of the tree to be returned + /// @note Level 0 is by definition the finest tree. + ConstTreePtr constTreePtr(size_t level) const; + + /// @brief Return a reference to the tree at the finest level + TreeType& finestTree() { return *mTrees.front(); } + + /// @brief Return a const reference to the tree at the finest level + const TreeType& finestConstTree() const { return *mTrees.front(); } + + /// @brief Return a shared pointer to the tree at the finest level + TreePtr finestTreePtr() { return mTrees.front(); } + + /// @brief Return a const shared pointer to the tree at the finest level + ConstTreePtr finestConstTreePtr() const { return mTrees.front(); } + + /// @brief Return a reference to the tree at the coarsest level + TreeType& coarsestTree() { return *mTrees.back(); } + + /// @brief Return a const reference to the tree at the coarsest level + const TreeType& coarsestConstTree() const { return *mTrees.back(); } + + /// @brief Return a shared pointer to the tree at the coarsest level + TreePtr coarsestTreePtr() { return mTrees.back(); } + + /// @brief Return a const shared pointer to the tree at the coarsest level + ConstTreePtr coarsestConstTreePtr() const { return mTrees.back(); } + + ////////////////////////////////////////////////////////////////////// + + /// @brief Return a shared pointer to the grid at the specified integer level + /// @param level Integer level of the grid to be returned + /// @note Level 0 is by definition the finest grid. + GridPtr grid(size_t level); + + /// @brief Return a const shared pointer to the grid at the specified level + /// @param level The level of the grid to be returned + /// @note Level 0 is by definition the finest grid. + ConstGridPtr grid(size_t level) const; + + /// @brief Return a shared pointer to a new grid at the specified + /// floating-point level. + /// @param level Floating-point level of the grid to be returned + /// @param grainSize Grain size for the multi-threading + /// @details Interpolation of the specified order is performed + /// between the bracketing integer levels. + /// @note Level 0 is by definition the finest grid. + template + GridPtr createGrid(float level, size_t grainSize = 1) const; + + /// @brief Return a shared pointer to a vector of all the base + /// grids in this instance of the MultiResGrid. + /// @brief This method is useful for I/O + GridPtrVecPtr grids(); + + /// @brief Return a const shared pointer to a vector of all the base + /// grids in this instance of the MultiResGrid. + /// @brief This method is useful for I/O + GridCPtrVecPtr grids() const; + + ////////////////////////////////////////////////////////////////////// + + /// @brief Write all the grids to a file + /// @param filename Name of the files to write to + void write(const std::string &filename) const; + + ////////////////////////////////////////////////////////////////////// + + //@{ + /// @brief Return a reference to the finist grid's transform, which might be + /// shared with other grids. + /// @note Calling setTransform() on this grid invalidates all references + /// previously returned by this method. + /// @warning The transform is relative to the finest level (=0) grid! + math::Transform& transform() { return *mTransform; } + const math::Transform& transform() const { return *mTransform; } + const math::Transform& constTransform() const { return *mTransform; } + //@} + + ////////////////////////////////////////////////////////////////////// + + //@{ + /// @brief Return the floating-point index coordiate at out_level given + /// the index coordinate in_xyz at in_level. + static Vec3R xyz(const Coord& in_ijk, size_t in_level, size_t out_level); + static Vec3R xyz(const Vec3R& in_xyz, size_t in_level, size_t out_level); + static Vec3R xyz(const Vec3R& in_xyz, double in_level, double out_level); + //@} + + ////////////////////////////////////////////////////////////////////// + + //@{ + /// @brief Return the value at the specified coordinate position using + /// interpolation of the specified order into the tree at the out_level. + /// + /// @details First in_ijk is mapped from index space at in_level to + /// out_level, and then a value is interpolated from the tree at out_level. + /// + /// @param in_ijk Index coordinate position relative to tree at in_level + /// @param in_level Integer level of the input coordinate in_ijk + /// @param out_level Integer level of the interpolated value + template + ValueType sampleValue(const Coord& in_ijk, size_t in_level, size_t out_level) const; + template + ValueType sampleValue(const Vec3R& in_ijk, size_t in_level, size_t out_level) const; + //@} + + /// @brief Return the value at the specified integer coordinate position + /// and level using interpolation of the specified order. + /// @param ijk Integer coordinate position relative to the highest level (=0) grid + /// @param level Floating-point level from which to interpolate the value. + /// @brief Non-integer values of the level will use linear-interpolation + /// between the neighboring integer levels. + template + ValueType sampleValue(const Coord& ijk, double level) const; + + /// @brief Return the value at the specified floating-point coordinate position + /// and level using interpolation of the specified order. + /// @param xyz Floating-point coordinate position relative to the highest level grid + /// @param level Floating-point level from which to interpolate + /// the value. + /// @brief Non-integer values of the level will use linear-interpolation + /// between the neighboring integer levels. + template + ValueType sampleValue(const Vec3R& xyz, double level) const; + + ////////////////////////////////////////////////////////////////////// + + /// @brief Return the value at coordinate location in @a level tree + /// from the coarser tree at @a level+1 using trilinear interpolation + /// @param coords input coords relative to the fine tree at level + /// @param level The fine level to receive values from the coarser + /// level-1 + /// @note Prolongation means to interpolation from coarse -> fine + ValueType prolongate(const Coord& coords, const size_t level) const; + + + /// (coarse->fine) Poplulates all the active voxel values in a fine (@a level) tree + /// from the coarse (@a level+1) tree using linear interpolation + /// This transforms multiple values of the tree in parallel + void prolongateActiveVoxels(size_t destlevel, size_t grainSize = 1); + + ////////////////////////////////////////////////////////////////////// + + /// Populate a coordinate location in @a level (coarse) tree + /// from the @a level-1 (fine) tree using trilinear interpolation + /// input coords are relative to the mTree[level] (coarse) + /// @note Restriction means remapping from fine -> coarse + ValueType restrict(Coord ijk, const size_t level, bool useInjection = false) const; + + /// (fine->coarse) Poplulates all the active voxel values in the coarse (@a level) tree + /// from the fine (@a level-1) tree using trilinear interpolation. + /// For cell-centered data, this is equivalent to an average + /// For vertex-centered data this is equivalent to transfering the data + /// from the fine vertex directly above the coarse vertex. + /// This transforms multiple values of the tree in parallel + void restrictActiveVoxels(size_t destlevel, size_t grainSize = 1); + + /// Output a human-readable description of this MultiResGrid + void print(std::ostream& = std::cout, int verboseLevel = 1) const; + + /// @brief Return a string with the name of this MultiResGrid + std::string getName() const + { + if (Metadata::ConstPtr meta = (*this)[GridBase::META_GRID_NAME]) return meta->str(); + return ""; + } + + /// @brief Set the name of this MultiResGrid + void setName(const std::string& name) + { + this->removeMeta(GridBase::META_GRID_NAME); + this->insertMeta(GridBase::META_GRID_NAME, StringMetadata(name)); + } + + /// Return the class of volumetric data (level set, fog volume, etc.) stored in this grid. + GridClass getGridClass() const + { + typename StringMetadata::ConstPtr s = + this->getMetadata(GridBase::META_GRID_CLASS); + return s ? GridBase::stringToGridClass(s->value()) : GRID_UNKNOWN; + } + + /// Specify the class of volumetric data (level set, fog volume, etc.) stored in this grid. + void setGridClass(GridClass cls) + { + this->insertMeta(GridBase::META_GRID_CLASS, StringMetadata(GridBase::gridClassToString(cls))); + } + + /// Remove the setting specifying the class of this grid's volumetric data. + void clearGridClass() { this->removeMeta(GridBase::META_GRID_CLASS); } + +private: + + MultiResGrid(const MultiResGrid& other);//disallow copy construction + MultiResGrid& operator=(const MultiResGrid& other);//disallow copy assignment + + friend class ::TestMultiResGrid; + + // For optimal performance we disable registration of the ValueAccessor + typedef tree::ValueAccessor Accessor; + typedef tree::ValueAccessor ConstAccessor; + + void topDownRestrict(bool useInjection); + + inline void initMeta(); + + // Private struct that concurrently creates a mask of active voxel + // in a coarse tree from the active voxels in a fine tree + struct MaskOp; + + /// Private struct that performs multi-threaded restruction + struct RestrictOp; + + /// Private struct that performs multi-threaded prolongation + struct ProlongateOp; + + // Private struct that performs multi-threaded computation of grids a fraction levels + template + struct FractionOp; + + /// Private template struct that performs the actual multi-threading + template struct CookOp; + + // Array of shared pointer to trees, level 0 has the highest resolution. + std::vector mTrees; + // Shared point to a transform associated with the finest level grid + typename math::Transform::Ptr mTransform; +};// MultiResGrid + +template +MultiResGrid:: +MultiResGrid(size_t levels, ValueType background, double voxelSize) + : mTrees(levels) + , mTransform(math::Transform::createLinearTransform( voxelSize )) +{ + this->initMeta(); + for (size_t i=0; i +MultiResGrid:: +MultiResGrid(size_t levels, const Grid &grid, bool useInjection) + : MetaMap(grid) + , mTrees(levels) + , mTransform( grid.transform().copy() ) +{ + this->initMeta(); + mTrees[0].reset( new TreeType( grid.tree() ) );// deep copy input tree + mTrees[0]->voxelizeActiveTiles(); + this->topDownRestrict(useInjection); +} + +template +MultiResGrid:: +MultiResGrid(size_t levels, GridPtr grid, bool useInjection) + : MetaMap(*grid) + , mTrees(levels) + , mTransform( grid->transform().copy() ) +{ + this->initMeta(); + mTrees[0] = grid->treePtr();// steal tree from input grid + mTrees[0]->voxelizeActiveTiles(); + grid->newTree(); + this->topDownRestrict(useInjection); +} + +template +inline TreeType& MultiResGrid:: +tree(size_t level) +{ + assert( level < mTrees.size() ); + return *mTrees[level]; +} + +template +inline const TreeType& MultiResGrid:: +constTree(size_t level) const +{ + assert( level < mTrees.size() ); + return *mTrees[level]; +} + +template +inline typename TreeType::Ptr MultiResGrid:: +treePtr(size_t level) +{ + assert( level < mTrees.size() ); + return mTrees[level]; +} + +template +inline typename TreeType::ConstPtr MultiResGrid:: +constTreePtr(size_t level) const +{ + assert( level < mTrees.size() ); + return mTrees[level]; +} + +template +typename Grid::Ptr MultiResGrid:: +grid(size_t level) +{ + typename Grid::Ptr grid = Grid::create(this->treePtr(level)); + math::Transform::Ptr xform = mTransform->copy(); + if (level>0) xform->preScale( Real(1 << level) ); + grid->setTransform( xform ); + grid->insertMeta( *this->copyMeta() ); + grid->insertMeta( "MultiResGrid_Level", Int64Metadata(level)); + std::stringstream ss; + ss << this->getName() << "_level_" << level; + grid->setName( ss.str() ); + return grid; +} + +template +inline typename Grid::ConstPtr MultiResGrid:: +grid(size_t level) const +{ + return const_cast(this)->grid(level); +} + +template +template +typename Grid::Ptr MultiResGrid:: +createGrid(float level, size_t grainSize) const +{ + assert( level >= 0.0f && level <= float(mTrees.size()-1) ); + + typename Grid::Ptr grid(new Grid(this->constTree(0).background())); + math::Transform::Ptr xform = mTransform->copy(); + xform->preScale( math::Pow(2.0f, level) ); + grid->setTransform( xform ); + grid->insertMeta( *(this->copyMeta()) ); + grid->insertMeta( "MultiResGrid_Level", FloatMetadata(level) ); + std::stringstream ss; + ss << this->getName() << "_level_" << level; + grid->setName( ss.str() ); + + if ( size_t(floorf(level)) == size_t(ceilf(level)) ) { + grid->setTree( this->constTree( size_t(floorf(level))).copy() ); + } else { + FractionOp tmp(*this, grid->tree(), level, grainSize); + if ( grid->getGridClass() == GRID_LEVEL_SET ) { + signedFloodFill( grid->tree() ); + pruneLevelSet( grid->tree() );//only creates inactive tiles + } + } + + return grid; +} + +template +GridPtrVecPtr MultiResGrid:: +grids() +{ + GridPtrVecPtr grids( new GridPtrVec ); + for (size_t level=0; levelpush_back(this->grid(level)); + return grids; +} + +template +GridCPtrVecPtr MultiResGrid:: +grids() const +{ + GridCPtrVecPtr grids( new GridCPtrVec ); + for (size_t level=0; levelpush_back(this->grid(level)); + return grids; +} + +template +void MultiResGrid:: +write(const std::string &filename) const +{ + io::File file( filename ); + file.write( *this->grids() ); + file.close(); +} + +template +Vec3R MultiResGrid:: +xyz(const Coord& in_ijk, size_t in_level, size_t out_level) +{ + return Vec3R( in_ijk.data() ) * Real(1 << in_level) / Real(1 << out_level); +} + +template +Vec3R MultiResGrid:: +xyz(const Vec3R& in_xyz, size_t in_level, size_t out_level) +{ + return in_xyz * Real(1 << in_level) / Real(1 << out_level); +} + +template +Vec3R MultiResGrid:: +xyz(const Vec3R& in_xyz, double in_level, double out_level) +{ + return in_xyz * math::Pow(2.0, in_level - out_level); + +} + +template +template +typename TreeType::ValueType MultiResGrid:: +sampleValue(const Coord& in_ijk, size_t in_level, size_t out_level) const +{ + assert( in_level >= 0 && in_level < mTrees.size() ); + assert( out_level >= 0 && out_level < mTrees.size() ); + const ConstAccessor acc(*mTrees[out_level]);// has disabled registration! + return tools::Sampler::sample( acc, this->xyz(in_ijk, in_level, out_level) ); +} + +template +template +typename TreeType::ValueType MultiResGrid:: +sampleValue(const Vec3R& in_xyz, size_t in_level, size_t out_level) const +{ + assert( in_level >= 0 && in_level < mTrees.size() ); + assert( out_level >= 0 && out_level < mTrees.size() ); + const ConstAccessor acc(*mTrees[out_level]);// has disabled registration! + return tools::Sampler::sample( acc, this->xyz(in_xyz, in_level, out_level) ); +} + +template +template +typename TreeType::ValueType MultiResGrid:: +sampleValue(const Coord& ijk, double level) const +{ + assert( level >= 0.0 && level <= double(mTrees.size()-1) ); + const size_t level0 = size_t(floor(level)), level1 = size_t(ceil(level)); + const ValueType v0 = this->template sampleValue( ijk, 0, level0 ); + if ( level0 == level1 ) return v0; + assert( level1 - level0 == 1 ); + const ValueType v1 = this->template sampleValue( ijk, 0, level1 ); + const ValueType a = ValueType(level1 - level); + return a * v0 + (ValueType(1) - a) * v1; +} + +template +template +typename TreeType::ValueType MultiResGrid:: +sampleValue(const Vec3R& xyz, double level) const +{ + assert( level >= 0.0 && level <= double(mTrees.size()-1) ); + const size_t level0 = size_t(floor(level)), level1 = size_t(ceil(level)); + const ValueType v0 = this->template sampleValue( xyz, 0, level0 ); + if ( level0 == level1 ) return v0; + assert( level1 - level0 == 1 ); + const ValueType v1 = this->template sampleValue( xyz, 0, level1 ); + const ValueType a = ValueType(level1 - level); + return a * v0 + (ValueType(1) - a) * v1; +} + +template +typename TreeType::ValueType MultiResGrid:: +prolongate(const Coord& ijk, const size_t level) const +{ + assert( level+1 < mTrees.size() ); + const ConstAccessor acc(*mTrees[level + 1]);// has disabled registration! + return ProlongateOp::run(ijk, acc); +} + +template +void MultiResGrid:: +prolongateActiveVoxels(size_t destlevel, size_t grainSize) +{ + assert( destlevel < mTrees.size()-1 ); + TreeType &fineTree = *mTrees[ destlevel ]; + const TreeType &coarseTree = *mTrees[ destlevel+1 ]; + CookOp tmp( coarseTree, fineTree, grainSize ); + +}; + +template +typename TreeType::ValueType MultiResGrid:: +restrict(Coord ijk, const size_t destlevel, bool useInjection) const +{ + assert( destlevel > 0 && destlevel < mTrees.size() ); + const TreeType &fineTree = *mTrees[ destlevel-1 ]; + if ( useInjection ) return fineTree.getValue(ijk<<1); + const ConstAccessor acc( fineTree );// has disabled registration! + return RestrictOp::run( ijk, acc); +}; + +template +void MultiResGrid:: +restrictActiveVoxels(size_t destlevel, size_t grainSize) +{ + assert( destlevel > 0 && destlevel < mTrees.size() ); + const TreeType &fineTree = *mTrees[ destlevel-1 ]; + TreeType &coarseTree = *mTrees[ destlevel ]; + CookOp tmp( fineTree, coarseTree, grainSize ); +}; + +template +void MultiResGrid:: +print(std::ostream& os, int verboseLevel) const +{ + os << "MultiResGrid with " << mTrees.size() << " levels\n"; + for (size_t i=0; iprint(os, verboseLevel); + } + + if ( MetaMap::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 +void MultiResGrid:: +initMeta() +{ + const size_t levels = this->numLevels(); + if (levels < 2) { + OPENVDB_THROW(ValueError, "MultiResGrid: at least two levels are required"); + } + this->insertMeta("MultiResGrid_Levels", Int64Metadata( levels ) ); +} + +template +void MultiResGrid:: +topDownRestrict(bool useInjection) +{ + const bool isLevelSet = this->getGridClass() == GRID_LEVEL_SET; + for (size_t n=1; n> 1, *it ); + } + } else {// Restriction by full-weighting + MaskOp tmp(fineTree, coarseTree, 128); + this->restrictActiveVoxels(n, 64); + } + if ( isLevelSet ) { + tools::signedFloodFill( coarseTree ); + tools::pruneLevelSet( coarseTree );//only creates inactive tiles + } + }// loop over grid levels +} + +template +struct MultiResGrid::MaskOp +{ + typedef typename TreeType::template ValueConverter::Type MaskT; + typedef tbb::enumerable_thread_specific PoolType; + typedef tree::LeafManager ManagerT; + typedef typename ManagerT::LeafRange RangeT; + typedef typename ManagerT::LeafNodeType::ValueOnCIter VoxelIterT; + + MaskOp(const TreeType& fineTree, TreeType& coarseTree, size_t grainSize = 1) + : mPool(new PoolType( coarseTree ) )// empty coarse tree acts as examplar + { + assert( coarseTree.empty() ); + + // Create Mask of restruction performed on fineTree + MaskT mask(fineTree, false, true, TopologyCopy() ); + + // Muli-threaded dilation which also linearizes the tree to leaf nodes + tools::dilateActiveValues(mask, 1, NN_FACE_EDGE_VERTEX, EXPAND_TILES); + + // Restriction by injection using thread-local storage of coarse tree masks + ManagerT leafs( mask ); + tbb::parallel_for(leafs.leafRange( grainSize ), *this); + + // multithreaded union of thread-local coarse tree masks with the coarse tree + typedef typename PoolType::const_iterator IterT; + for (IterT it=mPool->begin(); it!=mPool->end(); ++it) coarseTree.topologyUnion( *it ); + delete mPool; + } + void operator()(const RangeT& range) const + { + Accessor coarseAcc( mPool->local() );// disabled registration + for (typename RangeT::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + for (VoxelIterT voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) { + Coord ijk = voxelIter.getCoord(); + if ( (ijk[2] & 1) || (ijk[1] & 1) || (ijk[0] & 1) ) continue;//no overlap + coarseAcc.setValueOn( ijk >> 1 );//injection from fine to coarse level + }//loop over active voxels in the fine tree + }// loop over leaf nodes in the fine tree + } + PoolType* mPool; +};// MaskOp + +template +template +struct MultiResGrid::FractionOp +{ + typedef typename TreeType::template ValueConverter::Type MaskT; + typedef tbb::enumerable_thread_specific PoolType; + typedef typename PoolType::iterator PoolIterT; + typedef tree::LeafManager Manager1; + typedef tree::LeafManager Manager2; + typedef typename Manager1::LeafRange Range1; + typedef typename Manager2::LeafRange Range2; + + FractionOp(const MultiResGrid& parent, + TreeType& midTree, + float level, + size_t grainSize = 1) + : mLevel( level ) + , mPool( NULL ) + , mTree0( &*(parent.mTrees[size_t(floorf(level))]) )//high-resolution + , mTree1( &*(parent.mTrees[size_t(ceilf(level))]) ) //low-resolution + { + assert( midTree.empty() ); + assert( mTree0 != mTree1 ); + + // Create a pool of thread-local masks + MaskT examplar( false ); + mPool = new PoolType( examplar ); + + {// create mask from re-mapping coarse tree to mid-level tree + tree::LeafManager manager( *mTree1 ); + tbb::parallel_for( manager.leafRange(grainSize), *this ); + } + + // Multi-threaded dilation of mask + tbb::parallel_for(tbb::blocked_range(mPool->begin(),mPool->end(),1), *this); + + // Union thread-local coarse tree masks into the coarse tree + for (PoolIterT it=mPool->begin(); it!=mPool->end(); ++it) midTree.topologyUnion( *it ); + delete mPool; + + {// Interpolate values into the static mid level tree + Manager2 manager( midTree ); + tbb::parallel_for(manager.leafRange(grainSize), *this); + } + } + void operator()(const Range1& range) const + { + typedef typename Manager1::LeafNodeType::ValueOnCIter VoxelIter; + // Let mLevel = level + frac, where + // level is integer part of mLevel and frac is the fractional part + // low-res voxel size in world units = dx1 = 2^(level + 1) + // mid-res voxel size in world units = dx = 2^(mLevel) = 2^(level + frac) + // low-res index -> world: ijk * dx1 + // world -> mid-res index: world / dx + // low-res index -> mid-res index: (ijk * dx1) / dx = ijk * scale where + // scale = dx1/dx = 2^(level+1)/2^(level+frac) = 2^(1-frac) + const float scale = math::Pow(2.0f, 1.0f - math::FractionalPart(mLevel)); + tree::ValueAccessor acc( mPool->local() );// disabled registration + for (typename Range1::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + for (VoxelIter voxelIter = leafIter->cbeginValueOn(); voxelIter; ++voxelIter) { + Coord ijk = voxelIter.getCoord(); + ijk[0] = int(math::Round(ijk[0] * scale)); + ijk[1] = int(math::Round(ijk[1] * scale)); + ijk[2] = int(math::Round(ijk[2] * scale)); + acc.setValueOn( ijk ); + }//loop over active voxels in the fine tree + }// loop over leaf nodes in the fine tree + } + void operator()(const tbb::blocked_range& range) const + { + for (PoolIterT it=range.begin(); it!=range.end(); ++it) { + tools::dilateVoxels( *it, 1, NN_FACE_EDGE_VERTEX); + } + } + void operator()(const Range2 &r) const + { + typedef typename TreeType::LeafNodeType::ValueOnIter VoxelIter; + // Let mLevel = level + frac, where + // level is integer part of mLevel and frac is the fractional part + // high-res voxel size in world units = dx0 = 2^(level) + // low-res voxel size in world units = dx1 = 2^(level+1) + // mid-res voxel size in world units = dx = 2^(mLevel) = 2^(level + frac) + // mid-res index -> world: ijk * dx + // world -> high-res index: world / dx0 + // world -> low-res index: world / dx1 + // mid-res index -> high-res index: (ijk * dx) / dx0 = ijk * scale0 where + // scale0 = dx/dx0 = 2^(level+frac)/2^(level) = 2^(frac) + // mid-res index -> low-res index: (ijk * dx) / dx1 = ijk * scale1 where + // scale1 = dx/dx1 = 2^(level+frac)/2^(level+1) = 2^(frac-1) + const float b = math::FractionalPart(mLevel), a = 1.0f - b; + const float scale0 = math::Pow( 2.0f, b ); + const float scale1 = math::Pow( 2.0f,-a ); + ConstAccessor acc0( *mTree0 ), acc1( *mTree1 ); + for (typename Range2::Iterator leafIter = r.begin(); leafIter; ++leafIter) { + for (VoxelIter voxelIter = leafIter->beginValueOn(); voxelIter; ++voxelIter) { + const Vec3R xyz = Vec3R( voxelIter.getCoord().data() );// mid level coord + const ValueType v0 = tools::Sampler::sample( acc0, xyz * scale0 ); + const ValueType v1 = tools::Sampler::sample( acc1, xyz * scale1 ); + voxelIter.setValue( ValueType(a*v0 + b*v1) ); + } + } + } + const float mLevel; + PoolType* mPool; + const TreeType *mTree0, *mTree1; +};// FractionOp + +template +template +struct MultiResGrid::CookOp +{ + typedef tree::LeafManager ManagerT; + typedef typename ManagerT::LeafRange RangeT; + CookOp(const TreeType& srcTree, TreeType& dstTree, size_t grainSize) : acc( srcTree ) + { + ManagerT leafs( dstTree ); + tbb::parallel_for( leafs.leafRange( grainSize ), *this ); + } + CookOp(const CookOp &other) : acc( other.acc.tree() ) {} + void operator()(const RangeT& range) const + { + typedef typename RangeT::Iterator LeafIterT; + typedef typename ManagerT::LeafNodeType::ValueOnIter VoxelIterT; + for (LeafIterT leaf = range.begin(); leaf; ++leaf) { + ValueType* phi = leaf.buffer(0).data();// avoids small overhead of out-of-core + for (VoxelIterT voxel = leaf->beginValueOn(); voxel; ++voxel) { + phi[ voxel.pos() ] = OperatorType::run(voxel.getCoord(), acc); + } + } + } + const ConstAccessor acc; +};// CookOp + +template +struct MultiResGrid::RestrictOp +{ + /// @brief Static method that performs restriction by full weighting + /// @param ijk Coordinate location on the coarse tree + /// @param acc ValueAccessor to the fine tree + static ValueType run(Coord ijk, const ConstAccessor &acc) + { + ijk <<= 1; + // Overlapping grid point + ValueType v = 8*acc.getValue(ijk); + // neighbors in one axial direction + v += 4*(acc.getValue(ijk.offsetBy(-1, 0, 0)) + acc.getValue(ijk.offsetBy( 1, 0, 0)) +// x + acc.getValue(ijk.offsetBy( 0,-1, 0)) + acc.getValue(ijk.offsetBy( 0, 1, 0)) +// y + acc.getValue(ijk.offsetBy( 0, 0,-1)) + acc.getValue(ijk.offsetBy( 0, 0, 1)));// z + // neighbors in two axial directions + v += 2*(acc.getValue(ijk.offsetBy(-1,-1, 0)) + acc.getValue(ijk.offsetBy(-1, 1, 0)) +// xy + acc.getValue(ijk.offsetBy( 1,-1, 0)) + acc.getValue(ijk.offsetBy( 1, 1, 0)) +// xy + acc.getValue(ijk.offsetBy(-1, 0,-1)) + acc.getValue(ijk.offsetBy(-1, 0, 1)) +// xz + acc.getValue(ijk.offsetBy( 1, 0,-1)) + acc.getValue(ijk.offsetBy( 1, 0, 1)) +// xz + acc.getValue(ijk.offsetBy( 0,-1,-1)) + acc.getValue(ijk.offsetBy( 0,-1, 1)) +// yz + acc.getValue(ijk.offsetBy( 0, 1,-1)) + acc.getValue(ijk.offsetBy( 0, 1, 1)));// yz + // neighbors in three axial directions + for (int i=-1; i<=1; i+=2) { + for (int j=-1; j<=1; j+=2) { + for (int k=-1; k<=1; k+=2) v += acc.getValue(ijk.offsetBy(i,j,k));// xyz + } + } + v *= ValueType(1.0f/64.0f); + return v; + } +};// RestrictOp + +template +struct MultiResGrid::ProlongateOp +{ + /// @brief Interpolate values from a coarse grid (acc) into the index space (ijk) of a fine grid + /// @param ijk Coordinate location on the fine tree + /// @param acc ValueAccessor to the coarse tree + static ValueType run(const Coord& ijk, const ConstAccessor &acc) + { + switch ( (ijk[0] & 1) | ((ijk[1] & 1) << 1) | ((ijk[2] & 1) << 2) ) { + case 0:// all even + return acc.getValue(ijk>>1); + case 1:// x is odd + return ValueType(0.5)*(acc.getValue(ijk.offsetBy(-1,0,0)>>1) + + acc.getValue(ijk.offsetBy( 1,0,0)>>1)); + case 2:// y is odd + return ValueType(0.5)*(acc.getValue(ijk.offsetBy(0,-1,0)>>1) + + acc.getValue(ijk.offsetBy(0, 1,0)>>1)); + case 3:// x&y are odd + return ValueType(0.25)*(acc.getValue(ijk.offsetBy(-1,-1,0)>>1) + + acc.getValue(ijk.offsetBy(-1, 1,0)>>1) + + acc.getValue(ijk.offsetBy( 1,-1,0)>>1) + + acc.getValue(ijk.offsetBy( 1, 1,0)>>1)); + case 4:// z is odd + return ValueType(0.5)*(acc.getValue(ijk.offsetBy(0,0,-1)>>1) + + acc.getValue(ijk.offsetBy(0,0, 1)>>1)); + case 5:// x&z are odd + return ValueType(0.25)*(acc.getValue(ijk.offsetBy(-1,0,-1)>>1) + + acc.getValue(ijk.offsetBy(-1,0, 1)>>1) + + acc.getValue(ijk.offsetBy( 1,0,-1)>>1) + + acc.getValue(ijk.offsetBy( 1,0, 1)>>1)); + case 6:// y&z are odd + return ValueType(0.25)*(acc.getValue(ijk.offsetBy(0,-1,-1)>>1) + + acc.getValue(ijk.offsetBy(0,-1, 1)>>1) + + acc.getValue(ijk.offsetBy(0, 1,-1)>>1) + + acc.getValue(ijk.offsetBy(0, 1, 1)>>1)); + } + // all are odd + ValueType v = zeroVal(); + for (int i=-1; i<=1; i+=2) { + for (int j=-1; j<=1; j+=2) { + for (int k=-1; k<=1; k+=2) v += acc.getValue(ijk.offsetBy(i,j,k)>>1);// xyz + } + } + return ValueType(0.125) * v; + } +};// ProlongateOp + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_MULTIRESGRID_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/ParticleAtlas.h b/nuparu/include/openvdb/include/openvdb/tools/ParticleAtlas.h new file mode 100644 index 00000000..d98e0b54 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/ParticleAtlas.h @@ -0,0 +1,1052 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 ParticleAtlas.h +/// +/// @brief Space-partitioning acceleration structure for particles, points with +/// radius. Partitions particle indices into voxels to accelerate range +/// and nearest neighbor searches. +/// +/// @note   This acceleration structure only stores integer offsets into an external particle +/// data structure that conforms to the ParticleArray interface.  +/// +/// @details Constructs and maintains a sequence of @c PointIndexGrids each of progressively +/// lower resolution. Particles are uniquely assigned to a particular resolution +/// level based on their radius. This strategy has proven efficient for accelerating +/// spatial queries on particle data sets with varying radii. +/// +/// @details The data structure automatically detects and adapts to particle data sets with +/// uniform radii. The construction is simplified and spatial queries pre-cache the +/// uniform particle radius to avoid redundant access calls to the +/// ParticleArray::getRadius method. +/// +/// @author Mihai Alden + + +#ifndef OPENVDB_TOOLS_PARTICLE_ATLAS_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_PARTICLE_ATLAS_HAS_BEEN_INCLUDED + +#include "PointIndexGrid.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + + +//////////////////////////////////////// + + +/// @brief Partition particles and performs range and nearest-neighbor searches. +/// +/// @interface ParticleArray +/// Expected interface for the ParticleArray container: +/// @code +/// template +/// struct ParticleArray +/// { +/// // The type used to represent world-space positions +/// typedef VectorType PosType; +/// typedef typename PosType::value_type ScalarType; +/// +/// // Return the number of particles in the array +/// size_t size() const; +/// +/// // Return the world-space position for the nth particle. +/// void getPos(size_t n, PosType& xyz) const; +/// +/// // Return the world-space radius for the nth particle. +/// void getRadius(size_t n, ScalarType& radius) const; +/// }; +/// @endcode +/// +/// @details Constructs a collection of @c PointIndexGrids of different resolutions +/// to accelerate spatial searches for particles with varying radius. +template +struct ParticleAtlas +{ + typedef boost::shared_ptr Ptr; + typedef boost::shared_ptr ConstPtr; + + typedef typename PointIndexGridType::Ptr PointIndexGridPtr; + typedef typename PointIndexGridType::ValueType IndexType; + + struct Iterator; + + ////////// + + ParticleAtlas() : mIndexGridArray(), mMinRadiusArray(), mMaxRadiusArray() {} + + /// @brief Partitions particle indices + /// + /// @param particles container conforming to the ParticleArray interface + /// @param minVoxelSize minimum voxel size limit + /// @param maxLevels maximum number of resolution levels + template + void construct(const ParticleArrayType& particles, double minVoxelSize, size_t maxLevels = 50); + + /// @brief Create a new @c ParticleAtlas from the given @a particles. + /// + /// @param particles container conforming to the ParticleArray interface + /// @param minVoxelSize minimum voxel size limit + /// @param maxLevels maximum number of resolution levels + template + static Ptr create(const ParticleArrayType& particles, double minVoxelSize, size_t maxLevels = 50); + + /// @brief Returns the number of resolution levels. + size_t levels() const { return mIndexGridArray.size(); } + /// @brief true if the container size is 0, false otherwise. + bool empty() const { return mIndexGridArray.empty(); } + + /// @brief Returns minimum particle radius for level @a n. + double minRadius(size_t n) const { return mMinRadiusArray[n]; } + /// @brief Returns maximum particle radius for level @a n. + double maxRadius(size_t n) const { return mMaxRadiusArray[n]; } + + /// @brief Returns the @c PointIndexGrid that represents the given level @a n. + PointIndexGridType& pointIndexGrid(size_t n) { return *mIndexGridArray[n]; } + /// @brief Returns the @c PointIndexGrid that represents the given level @a n. + const PointIndexGridType& pointIndexGrid(size_t n) const { return *mIndexGridArray[n]; } + +private: + // Disallow copying + ParticleAtlas(const ParticleAtlas&); + ParticleAtlas& operator=(const ParticleAtlas&); + + std::vector mIndexGridArray; + std::vector mMinRadiusArray, mMaxRadiusArray; +}; // struct ParticleAtlas + + +typedef ParticleAtlas ParticleIndexAtlas; + + +//////////////////////////////////////// + + +/// @brief Provides accelerated range and nearest-neighbor searches for +/// particles that are partitioned using the ParticleAtlas. +/// +/// @note Prefer to construct the iterator object once and reuse it +/// for subsequent queries. +template +struct ParticleAtlas::Iterator +{ + typedef typename PointIndexGridType::TreeType TreeType; + typedef tree::ValueAccessor ConstAccessor; + typedef boost::scoped_ptr ConstAccessorPtr; + + ///// + + /// @brief Construct an iterator from the given @a atlas. + explicit Iterator(const ParticleAtlas& atlas); + + /// @brief Clear the iterator and update it with the result of the given + /// world-space radial query. + /// @param center world-space center + /// @param radius world-space search radius + /// @param particles container conforming to the ParticleArray interface + template + void worldSpaceSearchAndUpdate(const Vec3d& center, double radius, const ParticleArrayType& particles); + + /// @brief Clear the iterator and update it with the result of the given + /// world-space radial query. + /// @param bbox world-space bounding box + /// @param particles container conforming to the ParticleArray interface + template + void worldSpaceSearchAndUpdate(const BBoxd& bbox, const ParticleArrayType& particles); + + /// @brief Returns the total number of resolution levels. + size_t levels() const { return mAtlas->levels(); } + + /// @brief Clear the iterator and update it with all particles that reside + /// at the given resolution @a level. + void updateFromLevel(size_t level); + + /// Reset the iterator to point to the first item. + void reset(); + + /// Return a const reference to the item to which this iterator is pointing. + const IndexType& operator*() const { return *mRange.first; } + + /// @{ + /// @brief Return @c true if this iterator is not yet exhausted. + bool test() const { return mRange.first < mRange.second || mIter != mRangeList.end(); } + operator bool() const { return this->test(); } + /// @} + + /// Advance iterator to next item. + void increment(); + + /// Advance iterator to next item. + void operator++() { this->increment(); } + + /// @brief Advance iterator to next item. + /// @return @c true if this iterator is not yet exhausted. + bool next(); + + /// Return the number of point indices in the iterator range. + size_t size() const; + + /// Return @c true if both iterators point to the same element. + bool operator==(const Iterator& p) const { return mRange.first == p.mRange.first; } + bool operator!=(const Iterator& p) const { return !this->operator==(p); } + +private: + Iterator(const Iterator& rhs); + Iterator& operator=(const Iterator& rhs); + + void clear(); + + typedef std::pair Range; + typedef std::deque RangeDeque; + typedef typename RangeDeque::const_iterator RangeDequeCIter; + typedef boost::scoped_array IndexArray; + + ParticleAtlas const * const mAtlas; + boost::scoped_array mAccessorList; + + // Primary index collection + Range mRange; + RangeDeque mRangeList; + RangeDequeCIter mIter; + // Secondary index collection + IndexArray mIndexArray; + size_t mIndexArraySize, mAccessorListSize; +}; // struct ParticleAtlas::Iterator + + +//////////////////////////////////////// + +// Internal operators and implementation details + + +namespace particle_atlas_internal { + + +template +struct ComputeExtremas { + typedef typename ParticleArrayT::PosType PosType; + typedef typename PosType::value_type ScalarType; + + ComputeExtremas(const ParticleArrayT& particles) + : particleArray(&particles) + , minRadius(std::numeric_limits::max()) + , maxRadius(-std::numeric_limits::max()) + { + } + + ComputeExtremas(ComputeExtremas& rhs, tbb::split) + : particleArray(rhs.particleArray) + , minRadius(std::numeric_limits::max()) + , maxRadius(-std::numeric_limits::max()) + { + } + + void operator()(const tbb::blocked_range& range) { + + ScalarType radius, tmpMin = minRadius, tmpMax = maxRadius; + + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + particleArray->getRadius(n, radius); + tmpMin = std::min(radius, tmpMin); + tmpMax = std::max(radius, tmpMax); + } + + minRadius = std::min(minRadius, tmpMin); + maxRadius = std::max(maxRadius, tmpMax); + } + + void join(const ComputeExtremas& rhs) { + minRadius = std::min(minRadius, rhs.minRadius); + maxRadius = std::max(maxRadius, rhs.maxRadius); + } + + ParticleArrayT const * const particleArray; + ScalarType minRadius, maxRadius; +}; // struct ComputeExtremas + + +template +struct SplittableParticleArray +{ + typedef boost::shared_ptr Ptr; + typedef boost::shared_ptr ConstPtr; + typedef ParticleArrayT ParticleArray; + + typedef typename ParticleArray::PosType PosType; + typedef typename PosType::value_type ScalarType; + + SplittableParticleArray(const ParticleArrayT& particles) + : mIndexMap(), mParticleArray(&particles), mSize(particles.size()) + { + updateExtremas(); + } + + SplittableParticleArray(const ParticleArrayT& particles, double minR, double maxR) + : mIndexMap(), mParticleArray(&particles), mSize(particles.size()) + { + mMinRadius = ScalarType(minR); + mMaxRadius = ScalarType(maxR); + } + + const ParticleArrayT& particleArray() const { return *mParticleArray; } + + size_t size() const { return mSize; } + + void getPos(size_t n, PosType& xyz) const { return mParticleArray->getPos(getGlobalIndex(n), xyz); } + void getRadius(size_t n, ScalarType& radius) const { return mParticleArray->getRadius(getGlobalIndex(n), radius); } + + ScalarType minRadius() const { return mMinRadius; } + ScalarType maxRadius() const { return mMaxRadius; } + + size_t getGlobalIndex(size_t n) const { return mIndexMap ? size_t(mIndexMap[n]) : n; } + + /// Move all particle indices that have a radius larger or equal to @a maxRadiusLimit + /// into a separate container. + Ptr split(ScalarType maxRadiusLimit) { + + if (mMaxRadius < maxRadiusLimit) return Ptr(); + + boost::scoped_array mask(new bool[mSize]); + + tbb::parallel_for(tbb::blocked_range(0, mSize), + MaskParticles(*this, mask, maxRadiusLimit)); + + Ptr output(new SplittableParticleArray(*this, mask)); + if (output->size() == 0) return Ptr(); + + size_t newSize = 0; + for (size_t n = 0, N = mSize; n < N; ++n) { + newSize += size_t(!mask[n]); + } + + boost::scoped_array newIndexMap(new PointIndex[newSize]); + + setIndexMap(newIndexMap, mask, false); + + mSize = newSize; + mIndexMap.swap(newIndexMap); + updateExtremas(); + + return output; + } + + +private: + // Disallow copying + SplittableParticleArray(const SplittableParticleArray&); + SplittableParticleArray& operator=(const SplittableParticleArray&); + + // Masked copy constructor + SplittableParticleArray(const SplittableParticleArray& other, const boost::scoped_array& mask) + : mIndexMap(), mParticleArray(&other.particleArray()), mSize(0) + { + for (size_t n = 0, N = other.size(); n < N; ++n) { + mSize += size_t(mask[n]); + } + + if (mSize != 0) { + mIndexMap.reset(new PointIndex[mSize]); + other.setIndexMap(mIndexMap, mask, true); + } + + updateExtremas(); + } + + struct MaskParticles { + MaskParticles(const SplittableParticleArray& particles, + const boost::scoped_array& mask, ScalarType radius) + : particleArray(&particles) + , particleMask(mask.get()) + , radiusLimit(radius) + { + } + + void operator()(const tbb::blocked_range& range) const { + const ScalarType maxRadius = radiusLimit; + ScalarType radius; + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + particleArray->getRadius(n, radius); + particleMask[n] = !(radius < maxRadius); + } + } + + SplittableParticleArray const * const particleArray; + bool * const particleMask; + ScalarType const radiusLimit; + }; // struct MaskParticles + + inline void updateExtremas() { + ComputeExtremas op(*this); + tbb::parallel_reduce(tbb::blocked_range(0, mSize), op); + mMinRadius = op.minRadius; + mMaxRadius = op.maxRadius; + } + + void setIndexMap(boost::scoped_array& newIndexMap, + const boost::scoped_array& mask, bool maskValue) const + { + if (mIndexMap.get() != NULL) { + const PointIndex* indices = mIndexMap.get(); + for (size_t idx = 0, n = 0, N = mSize; n < N; ++n) { + if (mask[n] == maskValue) newIndexMap[idx++] = indices[n]; + } + } else { + for (size_t idx = 0, n = 0, N = mSize; n < N; ++n) { + if (mask[n] == maskValue) newIndexMap[idx++] = PointIndex(n); + } + } + } + + + ////////// + + boost::scoped_array mIndexMap; + ParticleArrayT const * const mParticleArray; + size_t mSize; + ScalarType mMinRadius, mMaxRadius; +}; // struct SplittableParticleArray + + +template +struct RemapIndices { + + RemapIndices(const ParticleArrayType& particles, std::vector nodes) + : mParticles(&particles) + , mNodes(nodes.empty() ? NULL : &nodes.front()) + { + } + + void operator()(const tbb::blocked_range& range) const { + typedef typename PointIndexLeafNodeType::ValueType PointIndexType; + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + + PointIndexLeafNodeType& node = *mNodes[n]; + const size_t numIndices = node.indices().size(); + + if (numIndices > 0) { + PointIndexType* begin = &node.indices().front(); + const PointIndexType* end = begin + numIndices; + + while (begin < end) { + *begin = PointIndexType(mParticles->getGlobalIndex(*begin)); + ++begin; + } + } + } + } + + ParticleArrayType const * const mParticles; + PointIndexLeafNodeType * const * const mNodes; +}; // struct RemapIndices + + +template +struct RadialRangeFilter +{ + typedef typename ParticleArrayType::PosType PosType; + typedef typename PosType::value_type ScalarType; + + typedef std::pair Range; + typedef std::deque RangeDeque; + typedef std::deque IndexDeque; + + RadialRangeFilter(RangeDeque& ranges, IndexDeque& indices, const PosType& xyz, + ScalarType radius, const ParticleArrayType& particles, bool hasUniformRadius = false) + : mRanges(ranges) + , mIndices(indices) + , mCenter(xyz) + , mRadius(radius) + , mParticles(&particles) + , mHasUniformRadius(hasUniformRadius) + { + if (mHasUniformRadius) { + ScalarType uniformRadius; + mParticles->getRadius(0, uniformRadius); + mRadius = mRadius + uniformRadius; + mRadius *= mRadius; + } + } + + template + void filterLeafNode(const LeafNodeType& leaf) + { + const size_t numIndices = leaf.indices().size(); + if (numIndices > 0) { + const IndexT* begin = &leaf.indices().front(); + filterVoxel(leaf.origin(), begin, begin + numIndices); + } + } + + void filterVoxel(const Coord&, const IndexT* begin, const IndexT* end) + { + PosType pos; + + if (mHasUniformRadius) { + + const ScalarType searchRadiusSqr = mRadius; + + while (begin < end) { + mParticles->getPos(size_t(*begin), pos); + const ScalarType distSqr = (mCenter - pos).lengthSqr(); + if (distSqr < searchRadiusSqr) { + mIndices.push_back(*begin); + } + ++begin; + } + } else { + while (begin < end) { + const size_t idx = size_t(*begin); + mParticles->getPos(idx, pos); + + ScalarType radius; + mParticles->getRadius(idx, radius); + + ScalarType searchRadiusSqr = mRadius + radius; + searchRadiusSqr *= searchRadiusSqr; + + const ScalarType distSqr = (mCenter - pos).lengthSqr(); + + if (distSqr < searchRadiusSqr) { + mIndices.push_back(*begin); + } + + ++begin; + } + } + } + +private: + RadialRangeFilter(const RadialRangeFilter&); + RadialRangeFilter& operator=(const RadialRangeFilter&); + + RangeDeque& mRanges; + IndexDeque& mIndices; + PosType const mCenter; + ScalarType mRadius; + ParticleArrayType const * const mParticles; + bool const mHasUniformRadius; +}; // struct RadialRangeFilter + + +template +struct BBoxFilter +{ + typedef typename ParticleArrayType::PosType PosType; + typedef typename PosType::value_type ScalarType; + + typedef std::pair Range; + typedef std::deque RangeDeque; + typedef std::deque IndexDeque; + + BBoxFilter(RangeDeque& ranges, IndexDeque& indices, + const BBoxd& bbox, const ParticleArrayType& particles, bool hasUniformRadius = false) + : mRanges(ranges) + , mIndices(indices) + , mBBox(PosType(bbox.min()), PosType(bbox.max())) + , mCenter(mBBox.getCenter()) + , mParticles(&particles) + , mHasUniformRadius(hasUniformRadius) + , mUniformRadiusSqr(ScalarType(0.0)) + { + if (mHasUniformRadius) { + mParticles->getRadius(0, mUniformRadiusSqr); + mUniformRadiusSqr *= mUniformRadiusSqr; + } + } + + template + void filterLeafNode(const LeafNodeType& leaf) + { + const size_t numIndices = leaf.indices().size(); + if (numIndices > 0) { + const IndexT* begin = &leaf.indices().front(); + filterVoxel(leaf.origin(), begin, begin + numIndices); + } + } + + void filterVoxel(const Coord&, const IndexT* begin, const IndexT* end) + { + PosType pos; + + if (mHasUniformRadius) { + const ScalarType radiusSqr = mUniformRadiusSqr; + + while (begin < end) { + + mParticles->getPos(size_t(*begin), pos); + if (mBBox.isInside(pos)) { + mIndices.push_back(*begin++); + continue; + } + + const ScalarType distSqr = pointToBBoxDistSqr(pos); + if (!(distSqr > radiusSqr)) { + mIndices.push_back(*begin); + } + + ++begin; + } + + } else { + while (begin < end) { + + const size_t idx = size_t(*begin); + mParticles->getPos(idx, pos); + if (mBBox.isInside(pos)) { + mIndices.push_back(*begin++); + continue; + } + + ScalarType radius; + mParticles->getRadius(idx, radius); + const ScalarType distSqr = pointToBBoxDistSqr(pos); + if (!(distSqr > (radius * radius))) { + mIndices.push_back(*begin); + } + + ++begin; + } + } + } + +private: + BBoxFilter(const BBoxFilter&); + BBoxFilter& operator=(const BBoxFilter&); + + ScalarType pointToBBoxDistSqr(const PosType& pos) const + { + ScalarType distSqr = ScalarType(0.0); + + for (int i = 0; i < 3; ++i) { + + const ScalarType a = pos[i]; + + ScalarType b = mBBox.min()[i]; + if (a < b) { + ScalarType delta = b - a; + distSqr += delta * delta; + } + + b = mBBox.max()[i]; + if (a > b) { + ScalarType delta = a - b; + distSqr += delta * delta; + } + } + + return distSqr; + } + + RangeDeque& mRanges; + IndexDeque& mIndices; + math::BBox const mBBox; + PosType const mCenter; + ParticleArrayType const * const mParticles; + bool const mHasUniformRadius; + ScalarType mUniformRadiusSqr; +}; // struct BBoxFilter + + +} // namespace particle_atlas_internal + + +//////////////////////////////////////// + + +template +template +inline void +ParticleAtlas::construct( + const ParticleArrayType& particles, double minVoxelSize, size_t maxLevels) +{ + typedef typename particle_atlas_internal:: + SplittableParticleArray SplittableParticleArray; + typedef typename SplittableParticleArray::Ptr SplittableParticleArrayPtr; + typedef typename ParticleArrayType::ScalarType ScalarType; + + ///// + + particle_atlas_internal::ComputeExtremas extremas(particles); + tbb::parallel_reduce(tbb::blocked_range(0, particles.size()), extremas); + const double firstMin = extremas.minRadius; + const double firstMax = extremas.maxRadius; + const double firstVoxelSize = std::max(minVoxelSize, firstMin); + + if (!(firstMax < (firstVoxelSize * double(2.0))) && maxLevels > 1) { + + std::vector levels; + levels.push_back(SplittableParticleArrayPtr( + new SplittableParticleArray(particles, firstMin, firstMax))); + + std::vector voxelSizeArray; + voxelSizeArray.push_back(firstVoxelSize); + + for (size_t n = 0; n < maxLevels; ++n) { + + const double maxParticleRadius = double(levels.back()->maxRadius()); + const double particleRadiusLimit = voxelSizeArray.back() * double(2.0); + if (maxParticleRadius < particleRadiusLimit) break; + + SplittableParticleArrayPtr newLevel = levels.back()->split(ScalarType(particleRadiusLimit)); + if (!newLevel) break; + + levels.push_back(newLevel); + voxelSizeArray.push_back(double(newLevel->minRadius())); + } + + size_t numPoints = 0; + + typedef typename PointIndexGridType::TreeType PointIndexTreeType; + typedef typename PointIndexTreeType::LeafNodeType PointIndexLeafNodeType; + + std::vector nodes; + + for (size_t n = 0, N = levels.size(); n < N; ++n) { + + const SplittableParticleArray& particleArray = *levels[n]; + + numPoints += particleArray.size(); + + mMinRadiusArray.push_back(double(particleArray.minRadius())); + mMaxRadiusArray.push_back(double(particleArray.maxRadius())); + + PointIndexGridPtr grid = createPointIndexGrid(particleArray, voxelSizeArray[n]); + + nodes.clear(); + grid->tree().getNodes(nodes); + + tbb::parallel_for(tbb::blocked_range(0, nodes.size()), + particle_atlas_internal::RemapIndices + (particleArray, nodes)); + + mIndexGridArray.push_back(grid); + } + + } else { + mMinRadiusArray.push_back(firstMin); + mMaxRadiusArray.push_back(firstMax); + mIndexGridArray.push_back( + createPointIndexGrid(particles, firstVoxelSize)); + } +} + + +template +template +inline typename ParticleAtlas::Ptr +ParticleAtlas::create( + const ParticleArrayType& particles, double minVoxelSize, size_t maxLevels) +{ + Ptr ret(new ParticleAtlas()); + ret->construct(particles, minVoxelSize, maxLevels); + return ret; +} + + +//////////////////////////////////////// + +// ParticleAtlas::Iterator implementation + +template +inline +ParticleAtlas::Iterator::Iterator(const ParticleAtlas& atlas) + : mAtlas(&atlas) + , mAccessorList() + , mRange(static_cast(NULL), static_cast(NULL)) + , mRangeList() + , mIter(mRangeList.begin()) + , mIndexArray() + , mIndexArraySize(0) + , mAccessorListSize(atlas.levels()) +{ + if (mAccessorListSize > 0) { + mAccessorList.reset(new ConstAccessorPtr[mAccessorListSize]); + for (size_t n = 0, N = mAccessorListSize; n < N; ++n) { + mAccessorList[n].reset(new ConstAccessor(atlas.pointIndexGrid(n).tree())); + } + } +} + + +template +inline void +ParticleAtlas::Iterator::reset() +{ + mIter = mRangeList.begin(); + if (!mRangeList.empty()) { + mRange = mRangeList.front(); + } else if (mIndexArray) { + mRange.first = mIndexArray.get(); + mRange.second = mRange.first + mIndexArraySize; + } else { + mRange.first = static_cast(NULL); + mRange.second = static_cast(NULL); + } +} + + +template +inline void +ParticleAtlas::Iterator::increment() +{ + ++mRange.first; + if (mRange.first >= mRange.second && mIter != mRangeList.end()) { + ++mIter; + if (mIter != mRangeList.end()) { + mRange = *mIter; + } else if (mIndexArray) { + mRange.first = mIndexArray.get(); + mRange.second = mRange.first + mIndexArraySize; + } + } +} + + +template +inline bool +ParticleAtlas::Iterator::next() +{ + if (!this->test()) return false; + this->increment(); + return this->test(); +} + + +template +inline size_t +ParticleAtlas::Iterator::size() const +{ + size_t count = 0; + typename RangeDeque::const_iterator it = + mRangeList.begin(), end = mRangeList.end(); + + for ( ; it != end; ++it) { + count += it->second - it->first; + } + + return count + mIndexArraySize; +} + + +template +inline void +ParticleAtlas::Iterator::clear() +{ + mRange.first = static_cast(NULL); + mRange.second = static_cast(NULL); + mRangeList.clear(); + mIter = mRangeList.end(); + mIndexArray.reset(); + mIndexArraySize = 0; +} + + +template +inline void +ParticleAtlas::Iterator::updateFromLevel(size_t level) +{ + typedef typename PointIndexGridType::TreeType TreeType; + typedef typename TreeType::LeafNodeType LeafNodeType; + + this->clear(); + + if (mAccessorListSize > 0) { + const size_t levelIdx = std::min(mAccessorListSize - 1, level); + + const TreeType& tree = mAtlas->pointIndexGrid(levelIdx).tree(); + + + std::vector nodes; + tree.getNodes(nodes); + + for (size_t n = 0, N = nodes.size(); n < N; ++n) { + + const LeafNodeType& node = *nodes[n]; + const size_t numIndices = node.indices().size(); + + if (numIndices > 0) { + const IndexType* begin = &node.indices().front(); + mRangeList.push_back(Range(begin, (begin + numIndices))); + } + } + } + + this->reset(); +} + + +template +template +inline void +ParticleAtlas::Iterator::worldSpaceSearchAndUpdate( + const Vec3d& center, double radius, const ParticleArrayType& particles) +{ + typedef typename ParticleArrayType::PosType PosType; + typedef typename ParticleArrayType::ScalarType ScalarType; + + ///// + + this->clear(); + + std::deque filteredIndices; + std::vector searchRegions; + + const double iRadius = radius * double(1.0 / std::sqrt(3.0)); + + const Vec3d ibMin(center[0] - iRadius, center[1] - iRadius, center[2] - iRadius); + const Vec3d ibMax(center[0] + iRadius, center[1] + iRadius, center[2] + iRadius); + + const Vec3d bMin(center[0] - radius, center[1] - radius, center[2] - radius); + const Vec3d bMax(center[0] + radius, center[1] + radius, center[2] + radius); + + const PosType pos = PosType(center); + const ScalarType dist = ScalarType(radius); + + for (size_t n = 0, N = mAccessorListSize; n < N; ++n) { + + const double maxRadius = mAtlas->maxRadius(n); + const bool uniformRadius = math::isApproxEqual(mAtlas->minRadius(n), maxRadius); + + const openvdb::math::Transform& xform = mAtlas->pointIndexGrid(n).transform(); + + ConstAccessor& acc = *mAccessorList[n]; + + openvdb::CoordBBox inscribedRegion( + xform.worldToIndexCellCentered(ibMin), + xform.worldToIndexCellCentered(ibMax)); + + inscribedRegion.expand(-1); // erode by one voxel + + // collect indices that don't need to be tested + point_index_grid_internal::pointIndexSearch(mRangeList, acc, inscribedRegion); + + searchRegions.clear(); + + const openvdb::CoordBBox region( + xform.worldToIndexCellCentered(bMin - maxRadius), + xform.worldToIndexCellCentered(bMax + maxRadius)); + + inscribedRegion.expand(1); + point_index_grid_internal::constructExclusiveRegions(searchRegions, region, inscribedRegion); + + typedef particle_atlas_internal::RadialRangeFilter FilterType; + FilterType filter(mRangeList, filteredIndices, pos, dist, particles, uniformRadius); + + for (size_t i = 0, I = searchRegions.size(); i < I; ++i) { + point_index_grid_internal::filteredPointIndexSearch(filter, acc, searchRegions[i]); + } + } + + point_index_grid_internal::dequeToArray(filteredIndices, mIndexArray, mIndexArraySize); + + this->reset(); +} + + +template +template +inline void +ParticleAtlas::Iterator::worldSpaceSearchAndUpdate( + const BBoxd& bbox, const ParticleArrayType& particles) +{ + typedef typename ParticleArrayType::PosType PosType; + typedef typename ParticleArrayType::ScalarType ScalarType; + + ///// + + this->clear(); + + std::deque filteredIndices; + std::vector searchRegions; + + for (size_t n = 0, N = mAccessorListSize; n < N; ++n) { + + const double maxRadius = mAtlas->maxRadius(n); + const bool uniformRadius = math::isApproxEqual(mAtlas->minRadius(n), maxRadius); + const openvdb::math::Transform& xform = mAtlas->pointIndexGrid(n).transform(); + + ConstAccessor& acc = *mAccessorList[n]; + + openvdb::CoordBBox inscribedRegion( + xform.worldToIndexCellCentered(bbox.min()), + xform.worldToIndexCellCentered(bbox.max())); + + inscribedRegion.expand(-1); // erode by one voxel + + // collect indices that don't need to be tested + point_index_grid_internal::pointIndexSearch(mRangeList, acc, inscribedRegion); + + searchRegions.clear(); + + const openvdb::CoordBBox region( + xform.worldToIndexCellCentered(bbox.min() - maxRadius), + xform.worldToIndexCellCentered(bbox.max() + maxRadius)); + + inscribedRegion.expand(1); + point_index_grid_internal::constructExclusiveRegions(searchRegions, region, inscribedRegion); + + typedef particle_atlas_internal::BBoxFilter FilterType; + FilterType filter(mRangeList, filteredIndices, bbox, particles, uniformRadius); + + for (size_t i = 0, I = searchRegions.size(); i < I; ++i) { + point_index_grid_internal::filteredPointIndexSearch(filter, acc, searchRegions[i]); + } + } + + point_index_grid_internal::dequeToArray(filteredIndices, mIndexArray, mIndexArraySize); + + this->reset(); +} + + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_PARTICLE_ATLAS_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/ParticlesToLevelSet.h b/nuparu/include/openvdb/include/openvdb/tools/ParticlesToLevelSet.h new file mode 100644 index 00000000..5570f19a --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/ParticlesToLevelSet.h @@ -0,0 +1,924 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 converts particles (with position, radius and velocity) +/// into a signed distance field encoded as a narrow band level set. +/// Optionally, arbitrary attributes on the particles can be transferred +/// resulting in an additional attribute grid with the same topology +/// as the level set grid. +/// +/// @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 blobby. However it serves as a +/// great and fast starting point for subsequent level set surface +/// processing and convolution. +/// +/// 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: +/// typedef openvdb::Vec3R PosType; +/// +/// // Return the total number of particles in list. +/// // Always required! +/// size_t size() const; +/// +/// // Get the world space position of the nth particle. +/// // Required by ParticledToLevelSet::rasterizeSphere(*this,radius). +/// void getPos(size_t n, Vec3R& xyz) const; +/// +/// // Get the world space position and radius of the nth particle. +/// // Required by ParticledToLevelSet::rasterizeSphere(*this). +/// void getPosRad(size_t n, Vec3R& xyz, Real& rad) const; +/// +/// // Get the world space position, radius and velocity of the nth particle. +/// // Required by ParticledToLevelSet::rasterizeSphere(*this,radius). +/// void getPosRadVel(size_t n, Vec3R& xyz, Real& rad, Vec3R& vel) const; +/// +/// // Get the attribute of the nth particle. AttributeType is user-defined! +/// // Only required if attribute transfer is enabled in ParticlesToLevelSet. +/// void getAtt(size_t n, AttributeType& att) const; +/// }; +/// @endcode +/// +/// @note 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "Composite.h" // for csgUnion() +#include "PointMaskGrid.h" +#include "PointPartitioner.h" +#include "Morphology.h" // for {dilate|erode}Voxels +#include "Prune.h" +#include "SignedFloodFill.h" +#include "LevelSetTracker.h" +#include "MaskToLevelSet.h" + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +namespace { +// This is a simple type that combines a distance value and a particle +// attribute. It's required for attribute transfer which is performed +// in the ParticlesToLevelSet::Raster member class defined below. +template class BlindData; +}// unnamed namespace + + +template +class ParticlesToLevelSet +{ +public: + typedef typename boost::is_void::type DisableT; + typedef InterrupterT InterrupterType; + + typedef SdfGridT SdfGridType; + typedef typename SdfGridT::ValueType SdfType; + + typedef typename boost::mpl::if_::type AttType; + typedef typename SdfGridT::template ValueConverter::Type AttGridType; + + BOOST_STATIC_ASSERT(boost::is_floating_point::value); + + /// @brief Constructor using an exiting signed distance, + /// i.e. narrow band level set, grid. + /// + /// @param grid Level set grid in which particles are rasterized + /// @param interrupt Callback to interrupt a long-running process + /// + /// @note The input grid is assumed to be a valid level set and if + /// it already contains voxels (with SDF values) particles are unioned + /// onto the existing level set surface. However, if attribute transfer + /// is enabled, i.e. AttributeT != void, attributes are only + /// generated for voxels that overlap with particles, not the existing + /// voxels in the input grid (for which no attributes exist!). + /// + /// @details 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 also 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. + explicit ParticlesToLevelSet(SdfGridT& grid, InterrupterT* interrupt = NULL); + + /// Destructor + ~ParticlesToLevelSet() { delete mBlindGrid; } + + /// @brief This methods syncs up the level set and attribute grids + /// and therefore needs to be called before any of these grids are + /// used and after the last call to any of the rasterizer methods. + /// + /// @note Avoid calling this method more than once and only after + /// all the particles have been rasterized. It has no effect or + /// overhead if attribute transfer is disabled, i.e. AttributeT = + /// void and prune is false. + void finalize(bool prune = false); + + /// @brief Return a shared pointer to the grid containing the + /// (optional) attribute. + /// + /// @warning If attribute transfer was disabled, i.e. AttributeT = + /// void, or finalize() was not called the pointer is NULL! + typename AttGridType::Ptr attributeGrid() { return mAttGrid; } + + /// @brief Return the size of a voxel in world units + Real getVoxelSize() const { return mDx; } + + /// @brief Return the half-width of the narrow band in voxel units + Real getHalfWidth() const { return mHalfWidth; } + + /// @brief Return the smallest radius allowed in voxel units + Real getRmin() const { return mRmin; } + /// @brief Return the largest radius allowed in voxel units + Real getRmax() const { return mRmax; } + + /// @brief Return true if any particles were ignored due to their size + bool ignoredParticles() const { return mMinCount>0 || mMaxCount>0; } + /// @brief Return number of small particles that were ignore due to Rmin + size_t getMinCount() const { return mMinCount; } + /// @brief Return number of large particles that were ignore due to Rmax + size_t getMaxCount() const { return mMaxCount; } + + /// @brief set the smallest radius allowed in voxel units + void setRmin(Real Rmin) { mRmin = math::Max(Real(0),Rmin); } + /// @brief set the largest radius allowed in voxel units + void setRmax(Real Rmax) { mRmax = math::Max(mRmin,Rmax); } + + /// @brief Returns 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 Very fast generation of a level set from the active + /// mask of an input grid, e.g. a MaskGrid generated from points. + /// + /// @param grid Points with radius (no radius required). + /// @param dilationInVoxels Dilation in voxel units. + /// @param erosionInVoxels Erosion in voxel units. It is + /// recommended that erosionInVoxels <= dilationInVoxels. + template + void rasterizeMask(const GridT& grid, + const int dilationInVoxels = 1, + const int erosionInVoxels = 1); + + /// @brief Very fast generation of a level set from points + /// (e.g. particles without a radius). It employes a MaskGrid + /// an various bit-wise topology operations. + /// + /// @param points Points with radius (no radius required). + /// @param dilationInVoxels Dilation in voxel units. + /// @param erosionInVoxels Erosion in voxel units. It is + /// recommended that erosionInVoxels <= dilationInVoxels. + template + void rasterizePoints(const PointListT& points, + const int dilationInVoxels = 1, + const int erosionInVoxels = 1); + + /// @brief Rasterize a sphere per particle derived from their + /// position and radius. All spheres are CSG unioned. + /// + /// @param pa Particles with position and radius. + template + void rasterizeSpheres(const ParticleListT& pa); + + /// @brief Rasterize a sphere per particle derived from their + /// position and constant radius. All spheres are CSG unioned. + /// + /// @param pa Particles with position. + /// @param radius Constant particle radius in world units. + template + void rasterizeSpheres(const ParticleListT& pa, Real radius); + + /// @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! + template + void rasterizeTrails(const ParticleListT& pa, Real delta=1.0); + +private: + typedef BlindData BlindType; + typedef typename SdfGridT::template ValueConverter::Type BlindGridType; + + /// Class with multi-threaded implementation of particle rasterization + template struct Raster; + + SdfGridType* mSdfGrid; + typename AttGridType::Ptr mAttGrid; + BlindGridType* mBlindGrid; + InterrupterT* mInterrupter; + Real mDx, mHalfWidth; + Real mRmin, mRmax;//ignore particles outside this range of radii in voxel + size_t mMinCount, mMaxCount;//counters for ignored particles! + int mGrainSize; + +};//end of ParticlesToLevelSet class + +template +inline ParticlesToLevelSet:: +ParticlesToLevelSet(SdfGridT& grid, InterrupterT* interrupter) : + mSdfGrid(&grid), + mBlindGrid(NULL), + mInterrupter(interrupter), + mDx(grid.voxelSize()[0]), + mHalfWidth(grid.background()/mDx), + mRmin(1.5),// corresponds to the Nyquist grid sampling frequency + mRmax(100.0),// corresponds to a huge particle (probably too large!) + mMinCount(0), + mMaxCount(0), + mGrainSize(1) +{ + if (!mSdfGrid->hasUniformVoxels() ) { + OPENVDB_THROW(RuntimeError, + "ParticlesToLevelSet only supports uniform voxels!"); + } + if (mSdfGrid->getGridClass() != GRID_LEVEL_SET) { + OPENVDB_THROW(RuntimeError, + "ParticlesToLevelSet only supports level sets!" + "\nUse Grid::setGridClass(openvdb::GRID_LEVEL_SET)"); + } + + if (!DisableT::value) { + mBlindGrid = new BlindGridType(BlindType(grid.background())); + mBlindGrid->setTransform(mSdfGrid->transform().copy()); + } +} + +namespace { + +template struct DilationHandler +{ + DilationHandler(TreeT& t, int n) : tree(&t), size(n) {} + void operator()() const { dilateVoxels( *tree, size); } + TreeT* tree; + const int size; +}; +template struct ErosionHandler +{ + ErosionHandler(TreeT& t, int n) : tree(&t), size(n) {} + void operator()() const { erodeVoxels( *tree, size); } + TreeT* tree; + const int size; +}; + +} + +template +template +inline void ParticlesToLevelSet:: +rasterizeMask(const MaskGrid& maskGrid, int dilation, int erosion) +{ + // Generate a level set from the mask + mSdfGrid->setTree( maskToLevelSet + ( maskGrid, int(mHalfWidth), dilation, erosion, mInterrupter )->treePtr() ); +} + +template +template +inline void ParticlesToLevelSet:: +rasterizePoints(const PointListT& points, int dilation, int erosion) +{ + typedef typename SdfGridT::template ValueConverter::Type MaskGrid; + typedef typename MaskGrid::TreeType MaskTree; + typedef typename MaskTree::Ptr MaskTreePtr; + + // Generate a mask grid of the points + MaskGrid maskGrid(MaskTreePtr(new MaskTree(mSdfGrid->tree(), false, TopologyCopy()))); + maskGrid.setTransform( mSdfGrid->transform().copy() ); + PointMaskGrid pmg( maskGrid, mInterrupter ); + pmg.addPoints( points ); + // Generate a level set from the mask + this->rasterizeMask( maskGrid, dilation, erosion ); +} + +template +template +inline void ParticlesToLevelSet:: +rasterizeSpheres(const ParticleListT& pa) +{ + if (DisableT::value) { + Raster r(*this, mSdfGrid, pa); + r.rasterizeSpheres(); + } else { + Raster r(*this, mBlindGrid, pa); + r.rasterizeSpheres(); + } +} + +template +template +inline void ParticlesToLevelSet:: +rasterizeSpheres(const ParticleListT& pa, Real radius) +{ + if (DisableT::value) { + Raster r(*this, mSdfGrid, pa); + r.rasterizeSpheres(radius/mDx); + } else { + Raster r(*this, mBlindGrid, pa); + r.rasterizeSpheres(radius/mDx); + } +} + +template +template +inline void ParticlesToLevelSet:: +rasterizeTrails(const ParticleListT& pa, Real delta) +{ + if (DisableT::value) { + Raster r(*this, mSdfGrid, pa); + r.rasterizeTrails(delta); + } else { + Raster r(*this, mBlindGrid, pa); + r.rasterizeTrails(delta); + } +} + +template +inline void +ParticlesToLevelSet::finalize(bool prune) +{ + if (mBlindGrid==NULL) { + if (prune) tools::pruneLevelSet(mSdfGrid->tree()); + return; + } else { + if (prune) tools::prune(mBlindGrid->tree()); + } + + typedef typename SdfGridType::TreeType SdfTreeT; + typedef typename AttGridType::TreeType AttTreeT; + typedef typename BlindGridType::TreeType BlindTreeT; + // Use topology copy constructors since output grids have the same topology as mBlindDataGrid + const BlindTreeT& tree = mBlindGrid->tree(); + + // New level set tree + typename SdfTreeT::Ptr sdfTree(new SdfTreeT( + tree, tree.background().visible(), openvdb::TopologyCopy())); + + // Note this overwrites any existing attribute grids! + typename AttTreeT::Ptr attTree(new AttTreeT( + tree, tree.background().blind(), openvdb::TopologyCopy())); + mAttGrid = typename AttGridType::Ptr(new AttGridType(attTree)); + mAttGrid->setTransform(mBlindGrid->transform().copy()); + + // Extract the level set and IDs from mBlindDataGrid. We will + // explore the fact that by design active values always live + // at the leaf node level, i.e. level sets have no active tiles! + typedef typename BlindTreeT::LeafCIter LeafIterT; + typedef typename BlindTreeT::LeafNodeType LeafT; + typedef typename SdfTreeT::LeafNodeType SdfLeafT; + typedef typename AttTreeT::LeafNodeType AttLeafT; + for (LeafIterT n = tree.cbeginLeaf(); n; ++n) { + const LeafT& leaf = *n; + const openvdb::Coord xyz = leaf.origin(); + // Get leafnodes that were allocated during topology construction! + SdfLeafT* sdfLeaf = sdfTree->probeLeaf(xyz); + AttLeafT* attLeaf = attTree->probeLeaf(xyz); + // Use linear offset (vs coordinate) access for better performance! + typename LeafT::ValueOnCIter m=leaf.cbeginValueOn(); + if (!m) {//no active values in leaf node so copy everything + for (openvdb::Index k = 0; k!=LeafT::SIZE; ++k) { + const BlindType& v = leaf.getValue(k); + sdfLeaf->setValueOnly(k, v.visible()); + attLeaf->setValueOnly(k, v.blind()); + } + } else {//only copy active values (using flood fill for the inactive values) + for(; m; ++m) { + const openvdb::Index k = m.pos(); + const BlindType& v = *m; + sdfLeaf->setValueOnly(k, v.visible()); + attLeaf->setValueOnly(k, v.blind()); + } + } + } + + tools::signedFloodFill(*sdfTree);//required since we only transferred active voxels! + + if (mSdfGrid->empty()) { + mSdfGrid->setTree(sdfTree); + } else { + tools::csgUnion(mSdfGrid->tree(), *sdfTree, /*prune=*/true); + } +} + +/////////////////////////////////////////////////////////// + +template +template +struct ParticlesToLevelSet::Raster +{ + typedef typename boost::is_void::type DisableT; + typedef ParticlesToLevelSet ParticlesToLevelSetT; + typedef typename ParticlesToLevelSetT::SdfType SdfT;//type of signed distance values + typedef typename ParticlesToLevelSetT::AttType AttT;//type of particle attribute + typedef typename GridT::ValueType ValueT; + typedef typename GridT::Accessor AccessorT; + typedef typename GridT::TreeType TreeT; + typedef typename TreeT::LeafNodeType LeafNodeT; + typedef PointPartitioner PointPartitionerT; + + + /// @brief Main constructor + Raster(ParticlesToLevelSetT& parent, GridT* grid, const ParticleListT& particles) + : mParent(parent) + , mParticles(particles) + , mGrid(grid) + , mMap(*(mGrid->transform().baseMap())) + , mMinCount(0) + , mMaxCount(0) + , mIsCopy(false) + { + mPointPartitioner = new PointPartitionerT(); + mPointPartitioner->construct(particles, mGrid->transform()); + } + + /// @brief Copy constructor called by tbb threads + Raster(Raster& other, tbb::split) + : mParent(other.mParent) + , mParticles(other.mParticles) + , mGrid(new GridT(*other.mGrid, openvdb::ShallowCopy())) + , mMap(other.mMap) + , mMinCount(0) + , mMaxCount(0) + , mTask(other.mTask) + , mIsCopy(true) + , mPointPartitioner(other.mPointPartitioner) + { + mGrid->newTree(); + } + + virtual ~Raster() { + + // Copies construct temporary grids that have to be deleted + // but the original has ownership of the bucket array + if (mIsCopy) { + delete mGrid; + } else { + delete mPointPartitioner; + } + } + + /// @brief Rasterize a sphere per particle derived from their + /// position and radius. All spheres are CSG unioned. + void rasterizeSpheres() + { + mMinCount = mMaxCount = 0; + if (mParent.mInterrupter) { + mParent.mInterrupter->start("Rasterizing particles to level set using spheres"); + } + mTask = boost::bind(&Raster::rasterSpheres, _1, _2); + this->cook(); + if (mParent.mInterrupter) mParent.mInterrupter->end(); + } + /// @brief Rasterize a sphere per particle derived from their + /// position and constant radius. All spheres are CSG unioned. + /// @param radius constant radius of all particles in voxel units. + void rasterizeSpheres(Real radius) + { + mMinCount = radius < mParent.mRmin ? mParticles.size() : 0; + mMaxCount = radius > mParent.mRmax ? mParticles.size() : 0; + if (mMinCount>0 || mMaxCount>0) {//skipping all particles! + mParent.mMinCount = mMinCount; + mParent.mMaxCount = mMaxCount; + } else { + if (mParent.mInterrupter) { + mParent.mInterrupter->start( + "Rasterizing particles to level set using const spheres"); + } + mTask = boost::bind(&Raster::rasterFixedSpheres, _1, _2, SdfT(radius)); + this->cook(); + if (mParent.mInterrupter) mParent.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 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(Real delta=1.0) + { + mMinCount = mMaxCount = 0; + if (mParent.mInterrupter) { + mParent.mInterrupter->start("Rasterizing particles to level set using trails"); + } + mTask = boost::bind(&Raster::rasterTrails, _1, _2, SdfT(delta)); + this->cook(); + if (mParent.mInterrupter) mParent.mInterrupter->end(); + } + + /// @brief Kicks off the optionally multithreaded computation + void operator()(const tbb::blocked_range& r) + { + assert(mTask); + mTask(this, r); + mParent.mMinCount = mMinCount; + mParent.mMaxCount = mMaxCount; + } + + /// @brief Reguired by tbb::parallel_reduce + void join(Raster& other) + { + tools::csgUnion(*mGrid, *other.mGrid, /*prune=*/true); + mMinCount += other.mMinCount; + mMaxCount += other.mMaxCount; + } +private: + /// Disallow assignment since some of the members are references + Raster& operator=(const Raster&) { return *this; } + + /// @return true if the particle is too small or too large + bool ignoreParticle(SdfT R) + { + if (R < mParent.mRmin) {// below the cutoff radius + ++mMinCount; + return true; + } + if (R > mParent.mRmax) {// above the cutoff radius + ++mMaxCount; + return true; + } + return false; + } + /// @brief Reguired by tbb::parallel_reduce to multithreaded + /// rasterization of particles as spheres with variable radius + /// + /// @param r tbb's default range referring to the list of particles + void rasterSpheres(const tbb::blocked_range& r) + { + AccessorT acc = mGrid->getAccessor(); // local accessor + bool run = true; + const SdfT invDx = SdfT(1/mParent.mDx); + AttT att; + Vec3R pos; + Real rad; + + // Loop over buckets + for (size_t n = r.begin(), N = r.end(); n < N; ++n) { + // Loop over particles in bucket n. + typename PointPartitionerT::IndexIterator iter = mPointPartitioner->indices(n); + for ( ; run && iter; ++iter) { + const Index32& id = *iter; + mParticles.getPosRad(id, pos, rad); + const SdfT R = SdfT(invDx * rad);// in voxel units + if (this->ignoreParticle(R)) continue; + const Vec3R P = mMap.applyInverseMap(pos); + this->getAtt(id, att); + run = this->makeSphere(P, R, att, acc); + }//end loop over particles + }//end loop over buckets + } + + /// @brief Reguired by tbb::parallel_reduce to multithreaded + /// rasterization of particles as spheres with a fixed radius + /// + /// @param r tbb's default range referring to the list of particles + void rasterFixedSpheres(const tbb::blocked_range& r, SdfT R) + { + const SdfT + dx = static_cast(mParent.mDx), + w = static_cast(mParent.mHalfWidth); // in voxel units + AccessorT acc = mGrid->getAccessor(); // local accessor + const ValueT inside = -mGrid->background(); + const SdfT max = R + w;// maximum distance in voxel units + const SdfT max2 = math::Pow2(max);//square of maximum distance in voxel units + const SdfT min2 = math::Pow2(math::Max(SdfT(0), R - w));//square of minimum distance + ValueT v; + size_t count = 0; + AttT att; + Vec3R pos; + + // Loop over buckets + for (size_t n = r.begin(), N = r.end(); n < N; ++n) { + // Loop over particles in bucket n. + typename PointPartitionerT::IndexIterator iter = mPointPartitioner->indices(n); + for ( ; iter; ++iter) { + const Index32& id = *iter; + this->getAtt(id, att); + mParticles.getPos(id, pos); + const Vec3R P = mMap.applyInverseMap(pos); + 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)); + 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(mParent.mInterrupter)) { + tbb::task::self().cancel_group_execution(); + return; + } + SdfT x2 = static_cast(math::Pow2(c.x() - P[0])); + for (c.y() = a.y(); c.y() <= b.y(); ++c.y()) { + SdfT x2y2 = static_cast(x2 + math::Pow2(c.y() - P[1])); + for (c.z() = a.z(); c.z() <= b.z(); ++c.z()) { + SdfT x2y2z2 = static_cast( + x2y2 + math::Pow2(c.z()- P[2])); // square distance from c to P + if (x2y2z2 >= max2 || (!acc.probeValue(c,v) && v& r, SdfT delta) + { + AccessorT acc = mGrid->getAccessor(); // local accessor + bool run = true; + AttT att; + Vec3R pos, vel; + Real rad; + const Vec3R origin = mMap.applyInverseMap(Vec3R(0,0,0)); + const SdfT Rmin = SdfT(mParent.mRmin), invDx = SdfT(1/mParent.mDx); + + // Loop over buckets + for (size_t n = r.begin(), N = r.end(); n < N; ++n) { + // Loop over particles in bucket n. + typename PointPartitionerT::IndexIterator iter = mPointPartitioner->indices(n); + for ( ; run && iter; ++iter) { + const Index32& id = *iter; + mParticles.getPosRadVel(id, pos, rad, vel); + const SdfT R0 = SdfT(invDx*rad); + if (this->ignoreParticle(R0)) continue; + this->getAtt(id, att); + const Vec3R P0 = mMap.applyInverseMap(pos); + const Vec3R V = mMap.applyInverseMap(vel) - origin;//exclude translation + const SdfT speed = SdfT(V.length()), inv_speed = SdfT(1.0/speed); + const Vec3R Nrml = -V*inv_speed;// inverse normalized direction + Vec3R P = P0;// local position of instance + SdfT R = R0, d=0;// local radius and length of trail + for (size_t m=0; run && d <= speed ; ++m) { + run = this->makeSphere(P, R, att, acc); + P += 0.5*delta*R*Nrml;// adaptive offset along inverse velocity direction + d = SdfT((P-P0).length());// current length of trail + R = R0-(R0-Rmin)*d*inv_speed;// R = R0 -> mRmin(e.g. 1.5) + }//end loop over sphere instances + }//end loop over particles + }//end loop over buckets + } + + void cook() + { + // parallelize over the point buckets + const Index32 bucketCount = Index32(mPointPartitioner->size()); + + if (mParent.mGrainSize>0) { + tbb::parallel_reduce( + tbb::blocked_range(0, bucketCount, mParent.mGrainSize), *this); + } else { + (*this)(tbb::blocked_range(0, bucketCount)); + } + } + + /// @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 Euclidean signed distances measured in world + /// units). Also note we use the convention of positive distances + /// outside the surface and negative distances inside the surface. + bool makeSphere(const Vec3R &P, SdfT R, const AttT& att, AccessorT& acc) + { + const ValueT inside = -mGrid->background(); + const SdfT dx = SdfT(mParent.mDx), w = SdfT(mParent.mHalfWidth); + const SdfT max = R + w;// 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 SdfT max2 = math::Pow2(max);//square of maximum distance in voxel units + const SdfT min2 = math::Pow2(math::Max(SdfT(0), R - w));//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(mParent.mInterrupter)) { + tbb::task::self().cancel_group_execution(); + return false; + } + SdfT x2 = SdfT(math::Pow2(c.x() - P[0])); + for (c.y() = a.y(); c.y() <= b.y(); ++c.y()) { + SdfT x2y2 = SdfT(x2 + math::Pow2(c.y() - P[1])); + for (c.z() = a.z(); c.z() <= b.z(); ++c.z()) { + SdfT x2y2z2 = SdfT(x2y2 + math::Pow2(c.z()-P[2]));//square distance from c to P + if (x2y2z2 >= max2 || (!acc.probeValue(c,v) && v&)> FuncType; + + template + typename boost::enable_if::type + getAtt(size_t, AttT&) const {;} + + template + typename boost::disable_if::type + getAtt(size_t n, AttT& a) const { mParticles.getAtt(n, a); } + + template + typename boost::enable_if, ValueT>::type + Merge(T s, const AttT&) const { return s; } + + template + typename boost::disable_if, ValueT>::type + Merge(T s, const AttT& a) const { return ValueT(s,a); } + + ParticlesToLevelSetT& mParent; + const ParticleListT& mParticles;//list of particles + GridT* mGrid; + const math::MapBase& mMap; + size_t mMinCount, mMaxCount;//counters for ignored particles! + FuncType mTask; + const bool mIsCopy; + PointPartitionerT* mPointPartitioner; +};//end of Raster struct + + +///////////////////// YOU CAN SAFELY IGNORE THIS SECTION ///////////////////// + +namespace { + +// This is a simple type that combines a distance value and a particle +// attribute. It's required for attribute transfer which is defined in the +// Raster class above. +template +class BlindData +{ +public: + typedef VisibleT type; + typedef VisibleT VisibleType; + typedef BlindT BlindType; + + BlindData() {} + explicit BlindData(VisibleT v) : mVisible(v), mBlind(zeroVal()) {} + BlindData(VisibleT v, BlindT b) : mVisible(v), mBlind(b) {} + BlindData& operator=(const BlindData& rhs) + { + mVisible = rhs.mVisible; + mBlind = rhs.mBlind; + return *this; + } + const VisibleT& visible() const { return mVisible; } + const BlindT& blind() const { return mBlind; } + OPENVDB_NO_FP_EQUALITY_WARNING_BEGIN + bool operator==(const BlindData& rhs) const { return mVisible == rhs.mVisible; } + OPENVDB_NO_FP_EQUALITY_WARNING_END + bool operator< (const BlindData& rhs) const { return mVisible < rhs.mVisible; } + bool operator> (const BlindData& rhs) const { return mVisible > rhs.mVisible; } + BlindData operator+(const BlindData& rhs) const { return BlindData(mVisible + rhs.mVisible); } + BlindData operator+(const VisibleT& rhs) const { return BlindData(mVisible + rhs); } + BlindData operator-(const BlindData& rhs) const { return BlindData(mVisible - rhs.mVisible); } + BlindData operator-() const { return BlindData(-mVisible, mBlind); } + +protected: + VisibleT mVisible; + BlindT mBlind; +}; + +// Required by several of the tree nodes +template +inline std::ostream& operator<<(std::ostream& ostr, const BlindData& rhs) +{ + ostr << rhs.visible(); + return ostr; +} + +// Required by math::Abs +template +inline BlindData Abs(const BlindData& x) +{ + return BlindData(math::Abs(x.visible()), x.blind()); +} + +} // unnamed namespace + +////////////////////////////////////////////////////////////////////////////// + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_PARTICLES_TO_LEVELSET_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/PointAdvect.h b/nuparu/include/openvdb/include/openvdb/tools/PointAdvect.h new file mode 100644 index 00000000..80df623c --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/PointAdvect.h @@ -0,0 +1,424 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include // min +#include // Vec3 types and version number +#include // grid +#include +#include "Interpolation.h" // sampling +#include "VelocityFields.h" // VelocityIntegrator + +#include +#include // threading +#include // threading +#include // 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 +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 + 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(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 + +//////////////////////////////////////// + + +/// 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): +/// @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, + bool StaggeredVelocity = false, + typename InterrupterType = util::NullInterrupter> +class PointAdvect +{ +public: + typedef GridT GridType; + typedef PointListT PointListType; + typedef typename PointListT::value_type LocationType; + typedef VelocityIntegrator 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(0, mPoints->size()), *this); + } else { + (*this)(tbb::blocked_range(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 &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, + bool StaggeredVelocity = false, + typename CptGridType = GridT, + typename InterrupterType = util::NullInterrupter> +class ConstrainedPointAdvect +{ +public: + typedef GridT GridType; + typedef typename PointListT::value_type LocationType; + typedef VelocityIntegrator VelocityIntegratorType; + typedef ClosestPointProjector 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 size_t N = mPoints->size(); + + if (mThreaded) { + tbb::parallel_for(tbb::blocked_range(0, N), *this); + } else { + (*this)(tbb::blocked_range(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 &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-2016 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/nuparu/include/openvdb/include/openvdb/tools/PointIndexGrid.h b/nuparu/include/openvdb/include/openvdb/tools/PointIndexGrid.h new file mode 100644 index 00000000..c903017b --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/PointIndexGrid.h @@ -0,0 +1,1822 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 PointIndexGrid.h +/// +/// @brief Space-partitioning acceleration structure for points. Partitions +/// the points into voxels to accelerate range and nearest neighbor +/// searches. +/// +/// @note Leaf nodes store a single point-index array and the voxels are only +/// integer offsets into that array. The actual points are never stored +/// in the acceleration structure, only offsets into an external array. +/// +/// @author Mihai Alden + +#ifndef OPENVDB_TOOLS_POINT_INDEX_GRID_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_POINT_INDEX_GRID_HAS_BEEN_INCLUDED + + +#include +#include +#include +#include +#include +#include +#include "PointPartitioner.h" + +#include +#include +#include +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + +namespace tree { +template struct SameLeafConfig; // forward declaration +} + +namespace tools { + +template struct PointIndexLeafNode; // forward declaration + +/// Point index tree configured to match the default OpenVDB tree configuration +typedef tree::Tree, 4>, 5> > > PointIndexTree; + +/// Point index grid +typedef Grid PointIndexGrid; + + +//////////////////////////////////////// + + +/// @interface PointArray +/// Expected interface for the PointArray container: +/// @code +/// template +/// struct PointArray +/// { +/// // The type used to represent world-space point positions +/// typedef VectorType PosType; +/// +/// // Return the number of points in the array +/// size_t size() const; +/// +/// // Return the world-space position of the nth point in the array. +/// void getPos(size_t n, PosType& xyz) const; +/// }; +/// @endcode + + +//////////////////////////////////////// + + +/// @brief Partition points into a point index grid to accelerate range and +/// nearest-neighbor searches. +/// +/// @param points world-space point array conforming to the PointArray interface +/// @param voxelSize voxel size in world units +template +inline typename GridT::Ptr +createPointIndexGrid(const PointArrayT& points, double voxelSize); + + +/// @brief Partition points into a point index grid to accelerate range and +/// nearest-neighbor searches. +/// +/// @param points world-space point array conforming to the PointArray interface +/// @param xform world-to-index-space transform +template +inline typename GridT::Ptr +createPointIndexGrid(const PointArrayT& points, const math::Transform& xform); + + +/// @brief Return @c true if the given point index grid represents a valid partitioning +/// of the given point array. +/// +/// @param points world-space point array conforming to the PointArray interface +/// @param grid point index grid to validate +template +inline bool +isValidPartition(const PointArrayT& points, const GridT& grid); + + +/// Repartition the @a points if needed, otherwise return the input @a grid. +template +inline typename GridT::ConstPtr +getValidPointIndexGrid(const PointArrayT& points, const typename GridT::ConstPtr& grid); + +/// Repartition the @a points if needed, otherwise return the input @a grid. +template +inline typename GridT::Ptr +getValidPointIndexGrid(const PointArrayT& points, const typename GridT::Ptr& grid); + + +//////////////////////////////////////// + + +/// Accelerated range and nearest-neighbor searches for point index grids +template +struct PointIndexIterator +{ + typedef tree::ValueAccessor ConstAccessor; + typedef typename TreeType::LeafNodeType LeafNodeType; + typedef typename TreeType::ValueType ValueType; + + + PointIndexIterator(); + PointIndexIterator(const PointIndexIterator& rhs); + PointIndexIterator& operator=(const PointIndexIterator& rhs); + + + /// @brief Construct an iterator over the indices of the points contained in voxel (i, j, k). + /// @param ijk the voxel containing the points over which to iterate + /// @param acc an accessor for the grid or tree that holds the point indices + PointIndexIterator(const Coord& ijk, ConstAccessor& acc); + + + /// @brief Construct an iterator over the indices of the points contained in + /// the given bounding box. + /// @param bbox the bounding box of the voxels containing the points over which to iterate + /// @param acc an accessor for the grid or tree that holds the point indices + /// @note The range of the @a bbox is inclusive. Thus, a bounding box with + /// min = max is not empty but rather encloses a single voxel. + PointIndexIterator(const CoordBBox& bbox, ConstAccessor& acc); + + + /// @brief Clear the iterator and update it with the result of the given voxel query. + /// @param ijk the voxel containing the points over which to iterate + /// @param acc an accessor for the grid or tree that holds the point indices + void searchAndUpdate(const Coord& ijk, ConstAccessor& acc); + + + /// @brief Clear the iterator and update it with the result of the given voxel region query. + /// @param bbox the bounding box of the voxels containing the points over which to iterate + /// @param acc an accessor for the grid or tree that holds the point indices + /// @note The range of the @a bbox is inclusive. Thus, a bounding box with + /// min = max is not empty but rather encloses a single voxel. + void searchAndUpdate(const CoordBBox& bbox, ConstAccessor& acc); + + + /// @brief Clear the iterator and update it with the result of the given + /// index-space bounding box query. + /// @param bbox index-space bounding box + /// @param acc an accessor for the grid or tree that holds the point indices + /// @param points world-space point array conforming to the PointArray interface + /// @param xform linear, uniform-scale transform (i.e., cubical voxels) + template + void searchAndUpdate(const BBoxd& bbox, ConstAccessor& acc, + const PointArray& points, const math::Transform& xform); + + + /// @brief Clear the iterator and update it with the result of the given + /// index-space radial query. + /// @param center index-space center + /// @param radius index-space radius + /// @param acc an accessor for the grid or tree that holds the point indices + /// @param points world-space point array conforming to the PointArray interface + /// @param xform linear, uniform-scale transform (i.e., cubical voxels) + /// @param subvoxelAccuracy if true, check individual points against the search region, + /// otherwise return all points that reside in voxels that are inside + /// or intersect the search region + template + void searchAndUpdate(const Vec3d& center, double radius, ConstAccessor& acc, + const PointArray& points, const math::Transform& xform, bool subvoxelAccuracy = true); + + + /// @brief Clear the iterator and update it with the result of the given + /// world-space bounding box query. + /// @param bbox world-space bounding box + /// @param acc an accessor for the grid or tree that holds the point indices + /// @param points world-space point array conforming to the PointArray interface + /// @param xform linear, uniform-scale transform (i.e., cubical voxels) + template + void worldSpaceSearchAndUpdate(const BBoxd& bbox, ConstAccessor& acc, + const PointArray& points, const math::Transform& xform); + + + /// @brief Clear the iterator and update it with the result of the given + /// world-space radial query. + /// @param center world-space center + /// @param radius world-space radius + /// @param acc an accessor for the grid or tree that holds the point indices + /// @param points world-space point array conforming to the PointArray interface + /// @param xform linear, uniform-scale transform (i.e., cubical voxels) + /// @param subvoxelAccuracy if true, check individual points against the search region, + /// otherwise return all points that reside in voxels that are inside + /// or intersect the search region + template + void worldSpaceSearchAndUpdate(const Vec3d& center, double radius, ConstAccessor& acc, + const PointArray& points, const math::Transform& xform, bool subvoxelAccuracy = true); + + + /// Reset the iterator to point to the first item. + void reset(); + + /// Return a const reference to the item to which this iterator is pointing. + const ValueType& operator*() const { return *mRange.first; } + + /// @{ + /// @brief Return @c true if this iterator is not yet exhausted. + bool test() const { return mRange.first < mRange.second || mIter != mRangeList.end(); } + operator bool() const { return this->test(); } + /// @} + + /// Advance iterator to next item. + void increment(); + + /// Advance iterator to next item. + void operator++() { this->increment(); } + + + /// @brief Advance iterator to next item. + /// @return @c true if this iterator is not yet exhausted. + bool next(); + + /// Return the number of point indices in the iterator range. + size_t size() const; + + /// Return @c true if both iterators point to the same element. + bool operator==(const PointIndexIterator& p) const { return mRange.first == p.mRange.first; } + bool operator!=(const PointIndexIterator& p) const { return !this->operator==(p); } + + +private: + typedef std::pair Range; + typedef std::deque RangeDeque; + typedef typename RangeDeque::const_iterator RangeDequeCIter; + typedef boost::scoped_array IndexArray; + + void clear(); + + // Primary index collection + Range mRange; + RangeDeque mRangeList; + RangeDequeCIter mIter; + // Secondary index collection + IndexArray mIndexArray; + size_t mIndexArraySize; +}; // struct PointIndexIterator + + +/// @brief Selectively extract and filter point data using a custom filter operator. +/// +/// @par FilterType example: +/// @interface FilterType +/// @code +/// template +/// struct WeightedAverageAccumulator { +/// typedef T ValueType; +/// +/// WeightedAverageAccumulator(T const * const array, const T radius) +/// : mValues(array), mInvRadius(1.0/radius), mWeightSum(0.0), mValueSum(0.0) {} +/// +/// void reset() { mWeightSum = mValueSum = T(0.0); } +/// +/// // the following method is invoked by the PointIndexFilter +/// void operator()(const T distSqr, const size_t pointIndex) { +/// const T weight = T(1.0) - openvdb::math::Sqrt(distSqr) * mInvRadius; +/// mWeightSum += weight; +/// mValueSum += weight * mValues[pointIndex]; +/// } +/// +/// T result() const { return mWeightSum > T(0.0) ? mValueSum / mWeightSum : T(0.0); } +/// +/// private: +/// T const * const mValues; +/// const T mInvRadius; +/// T mWeightSum, mValueSum; +/// }; // struct WeightedAverageAccumulator +/// @endcode +template +struct PointIndexFilter +{ + typedef typename PointArray::PosType PosType; + typedef typename PosType::value_type ScalarType; + typedef tree::ValueAccessor ConstAccessor; + + /// @brief Constructor + /// @param points world-space point array conforming to the PointArray interface + /// @param tree a point index tree + /// @param xform linear, uniform-scale transform (i.e., cubical voxels) + PointIndexFilter(const PointArray& points, const TreeType& tree, const math::Transform& xform); + + /// Thread safe copy constructor + PointIndexFilter(const PointIndexFilter& rhs); + + /// @brief Perform a radial search query and apply the given filter + /// operator to the selected points. + /// @param center world-space center + /// @param radius world-space radius + /// @param op custom filter operator (see the FilterType example for interface details) + template + void searchAndApply(const PosType& center, ScalarType radius, FilterType& op); + +private: + PointArray const * const mPoints; + ConstAccessor mAcc; + const math::Transform mXform; + const ScalarType mInvVoxelSize; + PointIndexIterator mIter; +}; // struct PointIndexFilter + + +//////////////////////////////////////// + +// Internal operators and implementation details + + +namespace point_index_grid_internal { + +template +struct ValidPartitioningOp +{ + ValidPartitioningOp(tbb::atomic& hasChanged, + const PointArrayT& points, const math::Transform& xform) + : mPoints(&points) + , mTransform(&xform) + , mHasChanged(&hasChanged) + { + } + + template + void operator()(LeafT &leaf, size_t /*leafIndex*/) const + { + if ((*mHasChanged)) { + tbb::task::self().cancel_group_execution(); + return; + } + + typedef typename LeafT::IndexArray IndexArrayT; + typedef typename IndexArrayT::value_type IndexT; + typedef typename PointArrayT::PosType PosType; + + typename LeafT::ValueOnCIter iter; + Coord voxelCoord; + PosType point; + + const IndexT *begin = static_cast(NULL), *end = static_cast(NULL); + + for (iter = leaf.cbeginValueOn(); iter; ++iter) { + + if ((*mHasChanged)) break; + + voxelCoord = iter.getCoord(); + leaf.getIndices(iter.pos(), begin, end); + + while (begin < end) { + + mPoints->getPos(*begin, point); + if (voxelCoord != mTransform->worldToIndexCellCentered(point)) { + mHasChanged->fetch_and_store(true); + break; + } + + ++begin; + } + } + } + +private: + PointArrayT const * const mPoints; + math::Transform const * const mTransform; + tbb::atomic * const mHasChanged; +}; + + +template +struct PopulateLeafNodesOp +{ + typedef uint32_t IndexT; + typedef PointPartitioner Partitioner; + + PopulateLeafNodesOp(boost::scoped_array& leafNodes, + const Partitioner& partitioner) + : mLeafNodes(leafNodes.get()) + , mPartitioner(&partitioner) + { + } + + void operator()(const tbb::blocked_range& range) const { + + typedef typename Partitioner::VoxelOffsetType VoxelOffsetT; + + size_t maxPointCount = 0; + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + maxPointCount = std::max(maxPointCount, mPartitioner->indices(n).size()); + } + + const IndexT voxelCount = LeafNodeT::SIZE; + + // allocate histogram buffers + boost::scoped_array offsets(new VoxelOffsetT[maxPointCount]); + boost::scoped_array histogram(new IndexT[voxelCount]); + + VoxelOffsetT const * const voxelOffsets = mPartitioner->voxelOffsets().get(); + + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + + LeafNodeT* node = new LeafNodeT(); + node->setOrigin(mPartitioner->origin(n)); + + typename Partitioner::IndexIterator it = mPartitioner->indices(n); + + const size_t pointCount = it.size(); + IndexT const * const indices = &*it; + + // local copy of voxel offsets. + for (IndexT i = 0; i < pointCount; ++i) { + offsets[i] = voxelOffsets[ indices[i] ]; + } + + // compute voxel-offset histogram + memset(&histogram[0], 0, voxelCount * sizeof(IndexT)); + for (IndexT i = 0; i < pointCount; ++i) { + ++histogram[ offsets[i] ]; + } + + typename LeafNodeT::NodeMaskType& mask = node->getValueMask(); + typename LeafNodeT::Buffer& buffer = node->buffer(); + + // scan histogram (all-prefix-sums) + IndexT count = 0, startOffset; + for (int i = 0; i < int(voxelCount); ++i) { + if (histogram[i] > 0) { + startOffset = count; + count += histogram[i]; + histogram[i] = startOffset; + mask.setOn(i); + } + buffer.setValue(i, count); + } + + // allocate point-index array + node->indices().resize(pointCount); + typename LeafNodeT::ValueType * const orderedIndices = node->indices().data(); + + // rank and permute + for (IndexT i = 0; i < pointCount; ++i) { + orderedIndices[ histogram[ offsets[i] ]++ ] = indices[i]; + } + + mLeafNodes[n] = node; + } + } + + ////////// + + LeafNodeT* * const mLeafNodes; + Partitioner const * const mPartitioner; +}; + + +/// Construct a @c PointIndexTree +template +inline void +constructPointTree(TreeType& tree, const math::Transform& xform, const PointArray& points) +{ + typedef typename TreeType::LeafNodeType LeafType; + + boost::scoped_array leafNodes; + size_t leafNodeCount = 0; + + { + PointPartitioner partitioner; + partitioner.construct(points, xform, /*voxelOrder=*/false, /*recordVoxelOffsets=*/true); + + leafNodeCount = partitioner.size(); + leafNodes.reset(new LeafType*[leafNodeCount]); + + const tbb::blocked_range range(0, leafNodeCount); + tbb::parallel_for(range, PopulateLeafNodesOp(leafNodes, partitioner)); + } + + tree::ValueAccessor acc(tree); + for (size_t n = 0; n < leafNodeCount; ++n) { + acc.addLeaf(leafNodes[n]); + } +} + + +//////////////////////////////////////// + + +template +inline void +dequeToArray(const std::deque& d, boost::scoped_array& a, size_t& size) +{ + size = d.size(); + a.reset(new T[size]); + typename std::deque::const_iterator it = d.begin(), itEnd = d.end(); + T* item = a.get(); + for ( ; it != itEnd; ++it, ++item) *item = *it; +} + + +inline void +constructExclusiveRegions(std::vector& regions, + const CoordBBox& bbox, const CoordBBox& ibox) +{ + regions.clear(); + regions.reserve(6); + Coord cmin = ibox.min(); + Coord cmax = ibox.max(); + + // left-face bbox + regions.push_back(bbox); + regions.back().max().z() = cmin.z(); + + // right-face bbox + regions.push_back(bbox); + regions.back().min().z() = cmax.z(); + + --cmax.z(); // accounting for cell centered bucketing. + ++cmin.z(); + + // front-face bbox + regions.push_back(bbox); + CoordBBox* lastRegion = ®ions.back(); + lastRegion->min().z() = cmin.z(); + lastRegion->max().z() = cmax.z(); + lastRegion->max().x() = cmin.x(); + + // back-face bbox + regions.push_back(*lastRegion); + lastRegion = ®ions.back(); + lastRegion->min().x() = cmax.x(); + lastRegion->max().x() = bbox.max().x(); + + --cmax.x(); + ++cmin.x(); + + // bottom-face bbox + regions.push_back(*lastRegion); + lastRegion = ®ions.back(); + lastRegion->min().x() = cmin.x(); + lastRegion->max().x() = cmax.x(); + lastRegion->max().y() = cmin.y(); + + // top-face bbox + regions.push_back(*lastRegion); + lastRegion = ®ions.back(); + lastRegion->min().y() = cmax.y(); + lastRegion->max().y() = bbox.max().y(); +} + + +template +struct BBoxFilter +{ + typedef typename PointArray::PosType PosType; + typedef typename PosType::value_type ScalarType; + typedef std::pair Range; + typedef std::deque RangeDeque; + typedef std::deque IndexDeque; + + BBoxFilter(RangeDeque& ranges, IndexDeque& indices, const BBoxd& bbox, + const PointArray& points, const math::Transform& xform) + : mRanges(ranges) + , mIndices(indices) + , mRegion(bbox) + , mPoints(points) + , mMap(*xform.baseMap()) + { + } + + template + void filterLeafNode(const LeafNodeType& leaf) + { + typename LeafNodeType::ValueOnCIter iter; + const IndexT *begin = static_cast(NULL), *end = static_cast(NULL); + for (iter = leaf.cbeginValueOn(); iter; ++iter) { + leaf.getIndices(iter.pos(), begin, end); + filterVoxel(iter.getCoord(), begin, end); + } + } + + void filterVoxel(const Coord&, const IndexT* begin, const IndexT* end) + { + PosType vec; + + for (; begin < end; ++begin) { + mPoints.getPos(*begin, vec); + + if (mRegion.isInside(mMap.applyInverseMap(vec))) { + mIndices.push_back(*begin); + } + } + } + +private: + RangeDeque& mRanges; + IndexDeque& mIndices; + const BBoxd mRegion; + const PointArray& mPoints; + const math::MapBase& mMap; +}; + + +template +struct RadialRangeFilter +{ + typedef typename PointArray::PosType PosType; + typedef typename PosType::value_type ScalarType; + typedef std::pair Range; + typedef std::deque RangeDeque; + typedef std::deque IndexDeque; + + RadialRangeFilter(RangeDeque& ranges, IndexDeque& indices, const Vec3d& xyz, double radius, + const PointArray& points, const math::Transform& xform, + const double leafNodeDim, const bool subvoxelAccuracy) + : mRanges(ranges) + , mIndices(indices) + , mCenter(xyz) + , mWSCenter(xform.indexToWorld(xyz)) + , mVoxelDist1(ScalarType(0.0)) + , mVoxelDist2(ScalarType(0.0)) + , mLeafNodeDist1(ScalarType(0.0)) + , mLeafNodeDist2(ScalarType(0.0)) + , mWSRadiusSqr(ScalarType(radius * xform.voxelSize()[0])) + , mPoints(points) + , mSubvoxelAccuracy(subvoxelAccuracy) + { + const ScalarType voxelRadius = ScalarType(std::sqrt(3.0) * 0.5); + mVoxelDist1 = voxelRadius + ScalarType(radius); + mVoxelDist1 *= mVoxelDist1; + + if (radius > voxelRadius) { + mVoxelDist2 = ScalarType(radius) - voxelRadius; + mVoxelDist2 *= mVoxelDist2; + } + + const ScalarType leafNodeRadius = ScalarType(leafNodeDim * std::sqrt(3.0) * 0.5); + mLeafNodeDist1 = leafNodeRadius + ScalarType(radius); + mLeafNodeDist1 *= mLeafNodeDist1; + + if (radius > leafNodeRadius) { + mLeafNodeDist2 = ScalarType(radius) - leafNodeRadius; + mLeafNodeDist2 *= mLeafNodeDist2; + } + + mWSRadiusSqr *= mWSRadiusSqr; + } + + template + void filterLeafNode(const LeafNodeType& leaf) + { + { + const Coord& ijk = leaf.origin(); + PosType vec; + vec[0] = ScalarType(ijk[0]); + vec[1] = ScalarType(ijk[1]); + vec[2] = ScalarType(ijk[2]); + vec += ScalarType(LeafNodeType::DIM - 1) * 0.5; + vec -= mCenter; + + const ScalarType dist = vec.lengthSqr(); + if (dist > mLeafNodeDist1) return; + + if (mLeafNodeDist2 > 0.0 && dist < mLeafNodeDist2) { + const IndexT* begin = &leaf.indices().front(); + mRanges.push_back(Range(begin, begin + leaf.indices().size())); + return; + } + } + + typename LeafNodeType::ValueOnCIter iter; + const IndexT *begin = static_cast(NULL), *end = static_cast(NULL); + for (iter = leaf.cbeginValueOn(); iter; ++iter) { + leaf.getIndices(iter.pos(), begin, end); + filterVoxel(iter.getCoord(), begin, end); + } + } + + void filterVoxel(const Coord& ijk, const IndexT* begin, const IndexT* end) + { + PosType vec; + + { + vec[0] = mCenter[0] - ScalarType(ijk[0]); + vec[1] = mCenter[1] - ScalarType(ijk[1]); + vec[2] = mCenter[2] - ScalarType(ijk[2]); + + const ScalarType dist = vec.lengthSqr(); + if (dist > mVoxelDist1) return; + + if (!mSubvoxelAccuracy || (mVoxelDist2 > 0.0 && dist < mVoxelDist2)) { + if (!mRanges.empty() && mRanges.back().second == begin) { + mRanges.back().second = end; + } else { + mRanges.push_back(Range(begin, end)); + } + return; + } + } + + + while (begin < end) { + mPoints.getPos(*begin, vec); + vec = mWSCenter - vec; + + if (vec.lengthSqr() < mWSRadiusSqr) { + mIndices.push_back(*begin); + } + ++begin; + } + } + +private: + RangeDeque& mRanges; + IndexDeque& mIndices; + const PosType mCenter, mWSCenter; + ScalarType mVoxelDist1, mVoxelDist2, mLeafNodeDist1, mLeafNodeDist2, mWSRadiusSqr; + const PointArray& mPoints; + const bool mSubvoxelAccuracy; +}; // struct RadialRangeFilter + + +//////////////////////////////////////// + + +template +inline void +filteredPointIndexSearchVoxels(RangeFilterType& filter, + const LeafNodeType& leaf, const Coord& min, const Coord& max) +{ + typedef typename LeafNodeType::ValueType PointIndexT; + Index xPos(0), yPos(0), pos(0); + Coord ijk(0); + + const PointIndexT* dataPtr = &leaf.indices().front(); + PointIndexT beginOffset, endOffset; + + for (ijk[0] = min[0]; ijk[0] <= max[0]; ++ijk[0]) { + xPos = (ijk[0] & (LeafNodeType::DIM - 1u)) << (2 * LeafNodeType::LOG2DIM); + for (ijk[1] = min[1]; ijk[1] <= max[1]; ++ijk[1]) { + yPos = xPos + ((ijk[1] & (LeafNodeType::DIM - 1u)) << LeafNodeType::LOG2DIM); + for (ijk[2] = min[2]; ijk[2] <= max[2]; ++ijk[2]) { + pos = yPos + (ijk[2] & (LeafNodeType::DIM - 1u)); + + beginOffset = (pos == 0 ? PointIndexT(0) : leaf.getValue(pos - 1)); + endOffset = leaf.getValue(pos); + + if (endOffset > beginOffset) { + filter.filterVoxel(ijk, dataPtr + beginOffset, dataPtr + endOffset); + } + } + } + } +} + + +template +inline void +filteredPointIndexSearch(RangeFilterType& filter, ConstAccessor& acc, const CoordBBox& bbox) +{ + typedef typename ConstAccessor::TreeType::LeafNodeType LeafNodeType; + Coord ijk(0), ijkMax(0), ijkA(0), ijkB(0); + const Coord leafMin = bbox.min() & ~(LeafNodeType::DIM - 1); + const Coord leafMax = bbox.max() & ~(LeafNodeType::DIM - 1); + + for (ijk[0] = leafMin[0]; ijk[0] <= leafMax[0]; ijk[0] += LeafNodeType::DIM) { + for (ijk[1] = leafMin[1]; ijk[1] <= leafMax[1]; ijk[1] += LeafNodeType::DIM) { + for (ijk[2] = leafMin[2]; ijk[2] <= leafMax[2]; ijk[2] += LeafNodeType::DIM) { + + if (const LeafNodeType* leaf = acc.probeConstLeaf(ijk)) { + ijkMax = ijk; + ijkMax.offset(LeafNodeType::DIM - 1); + + // intersect leaf bbox with search region. + ijkA = Coord::maxComponent(bbox.min(), ijk); + ijkB = Coord::minComponent(bbox.max(), ijkMax); + + if (ijkA != ijk || ijkB != ijkMax) { + filteredPointIndexSearchVoxels(filter, *leaf, ijkA, ijkB); + } else { // leaf bbox is inside the search region + filter.filterLeafNode(*leaf); + } + } + } + } + } +} + + +//////////////////////////////////////// + + +template +inline void +pointIndexSearchVoxels(RangeDeque& rangeList, + const LeafNodeType& leaf, const Coord& min, const Coord& max) +{ + typedef typename LeafNodeType::ValueType PointIndexT; + typedef typename PointIndexT::IntType IntT; + typedef typename RangeDeque::value_type Range; + + Index xPos(0), pos(0), zStride = Index(max[2] - min[2]); + const PointIndexT* dataPtr = &leaf.indices().front(); + PointIndexT beginOffset(0), endOffset(0), + previousOffset(static_cast(leaf.indices().size() + 1u)); + Coord ijk(0); + + for (ijk[0] = min[0]; ijk[0] <= max[0]; ++ijk[0]) { + xPos = (ijk[0] & (LeafNodeType::DIM - 1u)) << (2 * LeafNodeType::LOG2DIM); + + for (ijk[1] = min[1]; ijk[1] <= max[1]; ++ijk[1]) { + pos = xPos + ((ijk[1] & (LeafNodeType::DIM - 1u)) << LeafNodeType::LOG2DIM); + pos += (min[2] & (LeafNodeType::DIM - 1u)); + + beginOffset = (pos == 0 ? PointIndexT(0) : leaf.getValue(pos - 1)); + endOffset = leaf.getValue(pos+zStride); + + if (endOffset > beginOffset) { + + if (beginOffset == previousOffset) { + rangeList.back().second = dataPtr + endOffset; + } else { + rangeList.push_back(Range(dataPtr + beginOffset, dataPtr + endOffset)); + } + + previousOffset = endOffset; + } + } + } +} + + +template +inline void +pointIndexSearch(RangeDeque& rangeList, ConstAccessor& acc, const CoordBBox& bbox) +{ + typedef typename ConstAccessor::TreeType::LeafNodeType LeafNodeType; + typedef typename LeafNodeType::ValueType PointIndexT; + typedef typename RangeDeque::value_type Range; + + Coord ijk(0), ijkMax(0), ijkA(0), ijkB(0); + const Coord leafMin = bbox.min() & ~(LeafNodeType::DIM - 1); + const Coord leafMax = bbox.max() & ~(LeafNodeType::DIM - 1); + + for (ijk[0] = leafMin[0]; ijk[0] <= leafMax[0]; ijk[0] += LeafNodeType::DIM) { + for (ijk[1] = leafMin[1]; ijk[1] <= leafMax[1]; ijk[1] += LeafNodeType::DIM) { + for (ijk[2] = leafMin[2]; ijk[2] <= leafMax[2]; ijk[2] += LeafNodeType::DIM) { + + if (const LeafNodeType* leaf = acc.probeConstLeaf(ijk)) { + ijkMax = ijk; + ijkMax.offset(LeafNodeType::DIM - 1); + + // intersect leaf bbox with search region. + ijkA = Coord::maxComponent(bbox.min(), ijk); + ijkB = Coord::minComponent(bbox.max(), ijkMax); + + if (ijkA != ijk || ijkB != ijkMax) { + pointIndexSearchVoxels(rangeList, *leaf, ijkA, ijkB); + } else { + // leaf bbox is inside the search region, add all indices. + const PointIndexT* begin = &leaf->indices().front(); + rangeList.push_back(Range(begin, (begin + leaf->indices().size()))); + } + } + } + } + } +} + + +} // namespace point_index_grid_internal + + +// PointIndexIterator implementation + +template +inline +PointIndexIterator::PointIndexIterator() + : mRange(static_cast(NULL), static_cast(NULL)) + , mRangeList() + , mIter(mRangeList.begin()) + , mIndexArray() + , mIndexArraySize(0) +{ +} + + +template +inline +PointIndexIterator::PointIndexIterator(const PointIndexIterator& rhs) + : mRange(rhs.mRange) + , mRangeList(rhs.mRangeList) + , mIter(mRangeList.begin()) + , mIndexArray() + , mIndexArraySize(rhs.mIndexArraySize) +{ + if (rhs.mIndexArray) { + mIndexArray.reset(new ValueType[mIndexArraySize]); + memcpy(mIndexArray.get(), rhs.mIndexArray.get(), mIndexArraySize * sizeof(ValueType)); + } +} + + +template +inline PointIndexIterator& +PointIndexIterator::operator=(const PointIndexIterator& rhs) +{ + if (&rhs != this) { + mRange = rhs.mRange; + mRangeList = rhs.mRangeList; + mIter = mRangeList.begin(); + mIndexArray.reset(); + mIndexArraySize = rhs.mIndexArraySize; + + if (rhs.mIndexArray) { + mIndexArray.reset(new ValueType[mIndexArraySize]); + memcpy(mIndexArray.get(), rhs.mIndexArray.get(), mIndexArraySize * sizeof(ValueType)); + } + } + return *this; +} + + +template +inline +PointIndexIterator::PointIndexIterator(const Coord& ijk, ConstAccessor& acc) + : mRange(static_cast(NULL), static_cast(NULL)) + , mRangeList() + , mIter(mRangeList.begin()) + , mIndexArray() + , mIndexArraySize(0) +{ + const LeafNodeType* leaf = acc.probeConstLeaf(ijk); + if (leaf && leaf->getIndices(ijk, mRange.first, mRange.second)) { + mRangeList.push_back(mRange); + mIter = mRangeList.begin(); + } +} + + +template +inline +PointIndexIterator::PointIndexIterator(const CoordBBox& bbox, ConstAccessor& acc) + : mRange(static_cast(NULL), static_cast(NULL)) + , mRangeList() + , mIter(mRangeList.begin()) + , mIndexArray() + , mIndexArraySize(0) +{ + point_index_grid_internal::pointIndexSearch(mRangeList, acc, bbox); + + if (!mRangeList.empty()) { + mIter = mRangeList.begin(); + mRange = mRangeList.front(); + } +} + + +template +inline void +PointIndexIterator::reset() +{ + mIter = mRangeList.begin(); + if (!mRangeList.empty()) { + mRange = mRangeList.front(); + } else if (mIndexArray) { + mRange.first = mIndexArray.get(); + mRange.second = mRange.first + mIndexArraySize; + } else { + mRange.first = static_cast(NULL); + mRange.second = static_cast(NULL); + } +} + + +template +inline void +PointIndexIterator::increment() +{ + ++mRange.first; + if (mRange.first >= mRange.second && mIter != mRangeList.end()) { + ++mIter; + if (mIter != mRangeList.end()) { + mRange = *mIter; + } else if (mIndexArray) { + mRange.first = mIndexArray.get(); + mRange.second = mRange.first + mIndexArraySize; + } + } +} + + +template +inline bool +PointIndexIterator::next() +{ + if (!this->test()) return false; + this->increment(); + return this->test(); +} + + +template +inline size_t +PointIndexIterator::size() const +{ + size_t count = 0; + typename RangeDeque::const_iterator it = mRangeList.begin(); + + for ( ; it != mRangeList.end(); ++it) { + count += it->second - it->first; + } + + return count + mIndexArraySize; +} + + +template +inline void +PointIndexIterator::clear() +{ + mRange.first = static_cast(NULL); + mRange.second = static_cast(NULL); + mRangeList.clear(); + mIter = mRangeList.end(); + mIndexArray.reset(); + mIndexArraySize = 0; +} + + +template +inline void +PointIndexIterator::searchAndUpdate(const Coord& ijk, ConstAccessor& acc) +{ + this->clear(); + const LeafNodeType* leaf = acc.probeConstLeaf(ijk); + if (leaf && leaf->getIndices(ijk, mRange.first, mRange.second)) { + mRangeList.push_back(mRange); + mIter = mRangeList.begin(); + } +} + + +template +inline void +PointIndexIterator::searchAndUpdate(const CoordBBox& bbox, ConstAccessor& acc) +{ + this->clear(); + point_index_grid_internal::pointIndexSearch(mRangeList, acc, bbox); + + if (!mRangeList.empty()) { + mIter = mRangeList.begin(); + mRange = mRangeList.front(); + } +} + + +template +template +inline void +PointIndexIterator::searchAndUpdate(const BBoxd& bbox, ConstAccessor& acc, + const PointArray& points, const math::Transform& xform) +{ + this->clear(); + + std::vector searchRegions; + CoordBBox region(Coord::round(bbox.min()), Coord::round(bbox.max())); + + const Coord dim = region.dim(); + const int minExtent = std::min(dim[0], std::min(dim[1], dim[2])); + + if (minExtent > 2) { + // collect indices that don't need to be tested + CoordBBox ibox = region; + ibox.expand(-1); + + point_index_grid_internal::pointIndexSearch(mRangeList, acc, ibox); + + // define regions for the filtered search + ibox.expand(1); + point_index_grid_internal::constructExclusiveRegions(searchRegions, region, ibox); + } else { + searchRegions.push_back(region); + } + + // filtered search + std::deque filteredIndices; + point_index_grid_internal::BBoxFilter + filter(mRangeList, filteredIndices, bbox, points, xform); + + for (size_t n = 0, N = searchRegions.size(); n < N; ++n) { + point_index_grid_internal::filteredPointIndexSearch(filter, acc, searchRegions[n]); + } + + point_index_grid_internal::dequeToArray(filteredIndices, mIndexArray, mIndexArraySize); + + this->reset(); +} + + +template +template +inline void +PointIndexIterator::searchAndUpdate(const Vec3d& center, double radius, + ConstAccessor& acc, const PointArray& points, const math::Transform& xform, + bool subvoxelAccuracy) +{ + this->clear(); + std::vector searchRegions; + + // bounding box + CoordBBox bbox( + Coord::round(Vec3d(center[0] - radius, center[1] - radius, center[2] - radius)), + Coord::round(Vec3d(center[0] + radius, center[1] + radius, center[2] + radius))); + bbox.expand(1); + + const double iRadius = radius * double(1.0 / std::sqrt(3.0)); + if (iRadius > 2.0) { + // inscribed box + CoordBBox ibox( + Coord::round(Vec3d(center[0] - iRadius, center[1] - iRadius, center[2] - iRadius)), + Coord::round(Vec3d(center[0] + iRadius, center[1] + iRadius, center[2] + iRadius))); + ibox.expand(-1); + + // collect indices that don't need to be tested + point_index_grid_internal::pointIndexSearch(mRangeList, acc, ibox); + + ibox.expand(1); + point_index_grid_internal::constructExclusiveRegions(searchRegions, bbox, ibox); + } else { + searchRegions.push_back(bbox); + } + + // filtered search + std::deque filteredIndices; + const double leafNodeDim = double(TreeType::LeafNodeType::DIM); + + typedef point_index_grid_internal::RadialRangeFilter FilterT; + + FilterT filter(mRangeList, filteredIndices, + center, radius, points, xform, leafNodeDim, subvoxelAccuracy); + + for (size_t n = 0, N = searchRegions.size(); n < N; ++n) { + point_index_grid_internal::filteredPointIndexSearch(filter, acc, searchRegions[n]); + } + + point_index_grid_internal::dequeToArray(filteredIndices, mIndexArray, mIndexArraySize); + + this->reset(); +} + + +template +template +inline void +PointIndexIterator::worldSpaceSearchAndUpdate(const BBoxd& bbox, ConstAccessor& acc, + const PointArray& points, const math::Transform& xform) +{ + this->searchAndUpdate( + BBoxd(xform.worldToIndex(bbox.min()), xform.worldToIndex(bbox.max())), acc, points, xform); +} + + +template +template +inline void +PointIndexIterator::worldSpaceSearchAndUpdate(const Vec3d& center, double radius, + ConstAccessor& acc, const PointArray& points, const math::Transform& xform, + bool subvoxelAccuracy) +{ + this->searchAndUpdate(xform.worldToIndex(center), + (radius / xform.voxelSize()[0]), acc, points, xform, subvoxelAccuracy); +} + + +//////////////////////////////////////// + +// PointIndexFilter implementation + +template +inline +PointIndexFilter::PointIndexFilter( + const PointArray& points, const TreeType& tree, const math::Transform& xform) + : mPoints(&points), mAcc(tree), mXform(xform), mInvVoxelSize(1.0/xform.voxelSize()[0]) +{ +} + + +template +inline +PointIndexFilter::PointIndexFilter(const PointIndexFilter& rhs) + : mPoints(rhs.mPoints) + , mAcc(rhs.mAcc.tree()) + , mXform(rhs.mXform) + , mInvVoxelSize(rhs.mInvVoxelSize) +{ +} + + +template +template +inline void +PointIndexFilter::searchAndApply( + const PosType& center, ScalarType radius, FilterType& op) +{ + if (radius * mInvVoxelSize < ScalarType(8.0)) { + mIter.searchAndUpdate(openvdb::CoordBBox( + mXform.worldToIndexCellCentered(center - radius), + mXform.worldToIndexCellCentered(center + radius)), mAcc); + } else { + mIter.worldSpaceSearchAndUpdate( + center, radius, mAcc, *mPoints, mXform, /*subvoxelAccuracy=*/false); + } + + const ScalarType radiusSqr = radius * radius; + ScalarType distSqr = 0.0; + PosType pos; + for (; mIter; ++mIter) { + mPoints->getPos(*mIter, pos); + pos -= center; + distSqr = pos.lengthSqr(); + + if (distSqr < radiusSqr) { + op(distSqr, *mIter); + } + } +} + + +//////////////////////////////////////// + + +template +inline typename GridT::Ptr +createPointIndexGrid(const PointArrayT& points, const math::Transform& xform) +{ + typename GridT::Ptr grid = GridT::create(typename GridT::ValueType(0)); + grid->setTransform(xform.copy()); + + if (points.size() > 0) { + point_index_grid_internal::constructPointTree( + grid->tree(), grid->transform(), points); + } + + return grid; +} + + +template +inline typename GridT::Ptr +createPointIndexGrid(const PointArrayT& points, double voxelSize) +{ + math::Transform::Ptr xform = math::Transform::createLinearTransform(voxelSize); + return createPointIndexGrid(points, *xform); +} + + +template +inline bool +isValidPartition(const PointArrayT& points, const GridT& grid) +{ + tree::LeafManager leafs(grid.tree()); + + size_t pointCount = 0; + for (size_t n = 0, N = leafs.leafCount(); n < N; ++n) { + pointCount += leafs.leaf(n).indices().size(); + } + + if (points.size() != pointCount) { + return false; + } + + tbb::atomic changed; + changed = false; + + point_index_grid_internal::ValidPartitioningOp + op(changed, points, grid.transform()); + + leafs.foreach(op); + + return !bool(changed); +} + + +template +inline typename GridT::ConstPtr +getValidPointIndexGrid(const PointArrayT& points, const typename GridT::ConstPtr& grid) +{ + if (isValidPartition(points, *grid)) { + return grid; + } + + return createPointIndexGrid(points, grid->transform()); +} + + +template +inline typename GridT::Ptr +getValidPointIndexGrid(const PointArrayT& points, const typename GridT::Ptr& grid) +{ + if (isValidPartition(points, *grid)) { + return grid; + } + + return createPointIndexGrid(points, grid->transform()); +} + + +//////////////////////////////////////// + + +template +struct PointIndexLeafNode : public tree::LeafNode +{ + typedef PointIndexLeafNode LeafNodeType; + typedef boost::shared_ptr Ptr; + + typedef T ValueType; + typedef std::vector IndexArray; + + + IndexArray& indices() { return mIndices; } + const IndexArray& indices() const { return mIndices; } + + bool getIndices(const Coord& ijk, const ValueType*& begin, const ValueType*& end) const; + bool getIndices(Index offset, const ValueType*& begin, const ValueType*& end) const; + + void setOffsetOn(Index offset, const ValueType& val); + void setOffsetOnly(Index offset, const ValueType& val); + + bool isEmpty(const CoordBBox& bbox) const; + +private: + IndexArray mIndices; + + //////////////////////////////////////// + + // The following methods had to be copied from the LeafNode class + // to make the derived PointIndexLeafNode class compatible with the tree structure. + +public: + typedef tree::LeafNode BaseLeaf; + typedef util::NodeMask NodeMaskType; + + using BaseLeaf::LOG2DIM; + using BaseLeaf::TOTAL; + using BaseLeaf::DIM; + using BaseLeaf::NUM_VALUES; + using BaseLeaf::NUM_VOXELS; + using BaseLeaf::SIZE; + using BaseLeaf::LEVEL; + + /// Default constructor + PointIndexLeafNode() : BaseLeaf(), mIndices() {} + + explicit + PointIndexLeafNode(const Coord& coords, const T& value = zeroVal(), bool active = false) + : BaseLeaf(coords, value, active) + , mIndices() + { + } + +#ifndef OPENVDB_2_ABI_COMPATIBLE + PointIndexLeafNode(PartialCreate, const Coord& coords, + const T& value = zeroVal(), bool active = false) + : BaseLeaf(PartialCreate(), coords, value, active) + , mIndices() + { + } +#endif + + /// Deep copy constructor + PointIndexLeafNode(const PointIndexLeafNode& rhs) : BaseLeaf(rhs), mIndices(rhs.mIndices) {} + + /// @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 + bool hasSameTopology(const PointIndexLeafNode* other) const { + return BaseLeaf::hasSameTopology(other); + } + + /// Check for buffer, state and origin equivalence. + bool operator==(const PointIndexLeafNode& other) const { return BaseLeaf::operator==(other); } + + bool operator!=(const PointIndexLeafNode& other) const { return !(other == *this); } + + template void merge(const PointIndexLeafNode& rhs) { + BaseLeaf::merge(rhs); + } + template void merge(const ValueType& tileValue, bool tileActive) { + BaseLeaf::template merge(tileValue, tileActive); + } + + template + void merge(const PointIndexLeafNode& other, + const ValueType& /*bg*/, const ValueType& /*otherBG*/) + { + BaseLeaf::template merge(other); + } + + void addLeaf(PointIndexLeafNode*) {} + template + void addLeafAndCache(PointIndexLeafNode*, AccessorT&) {} + + //@{ + /// @brief Return a pointer to this node. + PointIndexLeafNode* touchLeaf(const Coord&) { return this; } + template + PointIndexLeafNode* touchLeafAndCache(const Coord&, AccessorT&) { return this; } + + template + NodeT* probeNodeAndCache(const Coord&, AccessorT&) + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (!(boost::is_same::value)) return NULL; + return reinterpret_cast(this); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + PointIndexLeafNode* probeLeaf(const Coord&) { return this; } + template + PointIndexLeafNode* probeLeafAndCache(const Coord&, AccessorT&) { return this; } + //@} + + //@{ + /// @brief Return a @const pointer to this node. + const PointIndexLeafNode* probeConstLeaf(const Coord&) const { return this; } + template + const PointIndexLeafNode* probeConstLeafAndCache(const Coord&, AccessorT&) const {return this;} + template + const PointIndexLeafNode* probeLeafAndCache(const Coord&, AccessorT&) const { return this; } + const PointIndexLeafNode* probeLeaf(const Coord&) const { return this; } + template + const NodeT* probeConstNodeAndCache(const Coord&, AccessorT&) const + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (!(boost::is_same::value)) return NULL; + return reinterpret_cast(this); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + //@} + + + // I/O methods + + void readBuffers(std::istream& is, bool fromHalf = false); + void readBuffers(std::istream& is, const CoordBBox&, bool fromHalf = false); + void writeBuffers(std::ostream& os, bool toHalf = false) const; + + + Index64 memUsage() const; + + + //////////////////////////////////////// + + // Disable all write methods to avoid unintentional changes + // to the point-array offsets. + + void assertNonmodifiable() { + assert(false && "Cannot modify voxel values in a PointIndexTree."); + } + + void setActiveState(const Coord&, bool) { assertNonmodifiable(); } + void setActiveState(Index, bool) { assertNonmodifiable(); } + + void setValueOnly(const Coord&, const ValueType&) { assertNonmodifiable(); } + void setValueOnly(Index, const ValueType&) { assertNonmodifiable(); } + + void setValueOff(const Coord&) { assertNonmodifiable(); } + void setValueOff(Index) { assertNonmodifiable(); } + + void setValueOff(const Coord&, const ValueType&) { assertNonmodifiable(); } + void setValueOff(Index, const ValueType&) { assertNonmodifiable(); } + + void setValueOn(const Coord&) { assertNonmodifiable(); } + void setValueOn(Index) { assertNonmodifiable(); } + + void setValueOn(const Coord&, const ValueType&) { assertNonmodifiable(); } + void setValueOn(Index, const ValueType&) { assertNonmodifiable(); } + + void setValue(const Coord&, const ValueType&) { assertNonmodifiable(); } + + void setValuesOn() { assertNonmodifiable(); } + void setValuesOff() { assertNonmodifiable(); } + + template + void modifyValue(Index, const ModifyOp&) { assertNonmodifiable(); } + + template + void modifyValue(const Coord&, const ModifyOp&) { assertNonmodifiable(); } + + template + void modifyValueAndActiveState(const Coord&, const ModifyOp&) { assertNonmodifiable(); } + + void clip(const CoordBBox&, const ValueType&) { assertNonmodifiable(); } + + void fill(const CoordBBox&, const ValueType&, bool) { assertNonmodifiable(); } + void fill(const ValueType&) {} + void fill(const ValueType&, bool) { assertNonmodifiable(); } + + template + void setValueOnlyAndCache(const Coord&, const ValueType&, AccessorT&) {assertNonmodifiable();} + + template + void modifyValueAndActiveStateAndCache(const Coord&, const ModifyOp&, AccessorT&) { + assertNonmodifiable(); + } + + template + void setValueOffAndCache(const Coord&, const ValueType&, AccessorT&) { assertNonmodifiable(); } + + template + void setActiveStateAndCache(const Coord&, bool, AccessorT&) { assertNonmodifiable(); } + + void resetBackground(const ValueType&, const ValueType&) { assertNonmodifiable(); } + + void signedFloodFill(const ValueType&) { assertNonmodifiable(); } + void signedFloodFill(const ValueType&, const ValueType&) { assertNonmodifiable(); } + + void negate() { assertNonmodifiable(); } + +protected: + typedef typename BaseLeaf::ValueOn ValueOn; + typedef typename BaseLeaf::ValueOff ValueOff; + typedef typename BaseLeaf::ValueAll ValueAll; + typedef typename BaseLeaf::ChildOn ChildOn; + typedef typename BaseLeaf::ChildOff ChildOff; + typedef typename BaseLeaf::ChildAll ChildAll; + + typedef typename NodeMaskType::OnIterator MaskOnIterator; + typedef typename NodeMaskType::OffIterator MaskOffIterator; + typedef typename NodeMaskType::DenseIterator MaskDenseIterator; + + // During topology-only construction, access is needed + // to protected/private members of other template instances. + template friend struct PointIndexLeafNode; + + friend class tree::IteratorBase; + friend class tree::IteratorBase; + friend class tree::IteratorBase; + +public: + + + typedef typename BaseLeaf::template ValueIter< + MaskOnIterator, PointIndexLeafNode, const ValueType, ValueOn> ValueOnIter; + typedef typename BaseLeaf::template ValueIter< + MaskOnIterator, const PointIndexLeafNode, const ValueType, ValueOn> ValueOnCIter; + typedef typename BaseLeaf::template ValueIter< + MaskOffIterator, PointIndexLeafNode, const ValueType, ValueOff> ValueOffIter; + typedef typename BaseLeaf::template ValueIter< + MaskOffIterator,const PointIndexLeafNode,const ValueType,ValueOff> ValueOffCIter; + typedef typename BaseLeaf::template ValueIter< + MaskDenseIterator, PointIndexLeafNode, const ValueType, ValueAll> ValueAllIter; + typedef typename BaseLeaf::template ValueIter< + MaskDenseIterator,const PointIndexLeafNode,const ValueType,ValueAll> ValueAllCIter; + typedef typename BaseLeaf::template ChildIter< + MaskOnIterator, PointIndexLeafNode, ChildOn> ChildOnIter; + typedef typename BaseLeaf::template ChildIter< + MaskOnIterator, const PointIndexLeafNode, ChildOn> ChildOnCIter; + typedef typename BaseLeaf::template ChildIter< + MaskOffIterator, PointIndexLeafNode, ChildOff> ChildOffIter; + typedef typename BaseLeaf::template ChildIter< + MaskOffIterator, const PointIndexLeafNode, ChildOff> ChildOffCIter; + typedef typename BaseLeaf::template DenseIter< + PointIndexLeafNode, ValueType, ChildAll> ChildAllIter; + typedef typename BaseLeaf::template DenseIter< + const PointIndexLeafNode, const ValueType, ChildAll> ChildAllCIter; + +#define VMASK_ this->getValueMask() + ValueOnCIter cbeginValueOn() const { return ValueOnCIter(VMASK_.beginOn(), this); } + ValueOnCIter beginValueOn() const { return ValueOnCIter(VMASK_.beginOn(), this); } + ValueOnIter beginValueOn() { return ValueOnIter(VMASK_.beginOn(), this); } + ValueOffCIter cbeginValueOff() const { return ValueOffCIter(VMASK_.beginOff(), this); } + ValueOffCIter beginValueOff() const { return ValueOffCIter(VMASK_.beginOff(), this); } + ValueOffIter beginValueOff() { return ValueOffIter(VMASK_.beginOff(), this); } + ValueAllCIter cbeginValueAll() const { return ValueAllCIter(VMASK_.beginDense(), this); } + ValueAllCIter beginValueAll() const { return ValueAllCIter(VMASK_.beginDense(), this); } + ValueAllIter beginValueAll() { return ValueAllIter(VMASK_.beginDense(), this); } + + ValueOnCIter cendValueOn() const { return ValueOnCIter(VMASK_.endOn(), this); } + ValueOnCIter endValueOn() const { return ValueOnCIter(VMASK_.endOn(), this); } + ValueOnIter endValueOn() { return ValueOnIter(VMASK_.endOn(), this); } + ValueOffCIter cendValueOff() const { return ValueOffCIter(VMASK_.endOff(), this); } + ValueOffCIter endValueOff() const { return ValueOffCIter(VMASK_.endOff(), this); } + ValueOffIter endValueOff() { return ValueOffIter(VMASK_.endOff(), this); } + ValueAllCIter cendValueAll() const { return ValueAllCIter(VMASK_.endDense(), this); } + ValueAllCIter endValueAll() const { return ValueAllCIter(VMASK_.endDense(), this); } + ValueAllIter endValueAll() { return ValueAllIter(VMASK_.endDense(), this); } + + ChildOnCIter cbeginChildOn() const { return ChildOnCIter(VMASK_.endOn(), this); } + ChildOnCIter beginChildOn() const { return ChildOnCIter(VMASK_.endOn(), this); } + ChildOnIter beginChildOn() { return ChildOnIter(VMASK_.endOn(), this); } + ChildOffCIter cbeginChildOff() const { return ChildOffCIter(VMASK_.endOff(), this); } + ChildOffCIter beginChildOff() const { return ChildOffCIter(VMASK_.endOff(), this); } + ChildOffIter beginChildOff() { return ChildOffIter(VMASK_.endOff(), this); } + ChildAllCIter cbeginChildAll() const { return ChildAllCIter(VMASK_.beginDense(), this); } + ChildAllCIter beginChildAll() const { return ChildAllCIter(VMASK_.beginDense(), this); } + ChildAllIter beginChildAll() { return ChildAllIter(VMASK_.beginDense(), this); } + + ChildOnCIter cendChildOn() const { return ChildOnCIter(VMASK_.endOn(), this); } + ChildOnCIter endChildOn() const { return ChildOnCIter(VMASK_.endOn(), this); } + ChildOnIter endChildOn() { return ChildOnIter(VMASK_.endOn(), this); } + ChildOffCIter cendChildOff() const { return ChildOffCIter(VMASK_.endOff(), this); } + ChildOffCIter endChildOff() const { return ChildOffCIter(VMASK_.endOff(), this); } + ChildOffIter endChildOff() { return ChildOffIter(VMASK_.endOff(), this); } + ChildAllCIter cendChildAll() const { return ChildAllCIter(VMASK_.endDense(), this); } + ChildAllCIter endChildAll() const { return ChildAllCIter(VMASK_.endDense(), this); } + ChildAllIter endChildAll() { return ChildAllIter(VMASK_.endDense(), this); } +#undef VMASK_ +}; // struct PointIndexLeafNode + + +template +inline bool +PointIndexLeafNode::getIndices(const Coord& ijk, + const ValueType*& begin, const ValueType*& end) const +{ + return getIndices(LeafNodeType::coordToOffset(ijk), begin, end); +} + + +template +inline bool +PointIndexLeafNode::getIndices(Index offset, + const ValueType*& begin, const ValueType*& end) const +{ + if (this->isValueMaskOn(offset)) { + const ValueType* dataPtr = &mIndices.front(); + begin = dataPtr + (offset == 0 ? ValueType(0) : this->buffer()[offset - 1]); + end = dataPtr + this->buffer()[offset]; + return true; + } + return false; +} + + +template +inline void +PointIndexLeafNode::setOffsetOn(Index offset, const ValueType& val) +{ + this->buffer().setValue(offset, val); + this->setValueMaskOn(offset); +} + + +template +inline void +PointIndexLeafNode::setOffsetOnly(Index offset, const ValueType& val) +{ + this->buffer().setValue(offset, val); +} + + +template +inline bool +PointIndexLeafNode::isEmpty(const CoordBBox& bbox) const +{ + Index xPos, pos, zStride = Index(bbox.max()[2] - bbox.min()[2]); + Coord ijk; + + for (ijk[0] = bbox.min()[0]; ijk[0] <= bbox.max()[0]; ++ijk[0]) { + xPos = (ijk[0] & (DIM - 1u)) << (2 * LOG2DIM); + + for (ijk[1] = bbox.min()[1]; ijk[1] <= bbox.max()[1]; ++ijk[1]) { + pos = xPos + ((ijk[1] & (DIM - 1u)) << LOG2DIM); + pos += (bbox.min()[2] & (DIM - 1u)); + + if (this->buffer()[pos+zStride] > (pos == 0 ? T(0) : this->buffer()[pos - 1])) { + return false; + } + } + } + + return true; +} + + +template +inline void +PointIndexLeafNode::readBuffers(std::istream& is, bool fromHalf) +{ + BaseLeaf::readBuffers(is, fromHalf); + + Index64 numIndices = Index64(0); + is.read(reinterpret_cast(&numIndices), sizeof(Index64)); + + mIndices.resize(size_t(numIndices)); + is.read(reinterpret_cast(mIndices.data()), numIndices * sizeof(T)); +} + + +template +inline void +PointIndexLeafNode::readBuffers(std::istream& is, const CoordBBox& bbox, bool fromHalf) +{ + // Read and clip voxel values. + BaseLeaf::readBuffers(is, bbox, fromHalf); + + Index64 numIndices = Index64(0); + is.read(reinterpret_cast(&numIndices), sizeof(Index64)); + + const Index64 numBytes = numIndices * sizeof(T); + + if (bbox.hasOverlap(this->getNodeBoundingBox())) { + mIndices.resize(size_t(numIndices)); + is.read(reinterpret_cast(mIndices.data()), numBytes); + + /// @todo If any voxels were deactivated as a result of clipping in the call to + /// BaseLeaf::readBuffers(), the point index list will need to be regenerated. + } else { + // Read and discard voxel values. + boost::scoped_array buf(new char[numBytes]); + is.read(buf.get(), numBytes); + } + + // Reserved for future use + Index64 auxDataBytes = Index64(0); + is.read(reinterpret_cast(&auxDataBytes), sizeof(Index64)); + if (auxDataBytes > 0) { + // For now, read and discard any auxiliary data. + boost::scoped_array auxData(new char[auxDataBytes]); + is.read(auxData.get(), auxDataBytes); + } +} + + +template +inline void +PointIndexLeafNode::writeBuffers(std::ostream& os, bool toHalf) const +{ + BaseLeaf::writeBuffers(os, toHalf); + + Index64 numIndices = Index64(mIndices.size()); + os.write(reinterpret_cast(&numIndices), sizeof(Index64)); + os.write(reinterpret_cast(mIndices.data()), numIndices * sizeof(T)); + + // Reserved for future use + const Index64 auxDataBytes = Index64(0); + os.write(reinterpret_cast(&auxDataBytes), sizeof(Index64)); +} + + +template +inline Index64 +PointIndexLeafNode::memUsage() const +{ + return BaseLeaf::memUsage() + Index64((sizeof(T)*mIndices.capacity()) + sizeof(mIndices)); +} + +} // namespace tools + + +//////////////////////////////////////// + + +namespace tree { + +/// Helper metafunction used to implement LeafNode::SameConfiguration +/// (which, as an inner class, can't be independently specialized) +template +struct SameLeafConfig > +{ + static const bool value = true; +}; + +} // namespace tree +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_POINT_INDEX_GRID_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/PointMaskGrid.h b/nuparu/include/openvdb/include/openvdb/tools/PointMaskGrid.h new file mode 100644 index 00000000..bd896f2c --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/PointMaskGrid.h @@ -0,0 +1,283 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 PointMaskGrid.h +/// +/// @brief This tool produces a grid where every voxel that contains a +/// point is active. It employes thread-local storage for best performance. +/// +/// The @c PointListT template argument below refers to any class +/// with the following interface (see unittest/TestPointMaskGrid.cc +/// and SOP_OpenVDB_From_Particles.cc for practical examples): +/// @code +/// +/// class PointList { +/// ... +/// public: +/// +/// // Return the total number of particles in list. +/// size_t size() const; +/// +/// // Get the world space position of the nth particle. +/// void getPos(size_t n, Vec3R& xyz) const; +/// }; +/// @endcode +/// +/// @note See unittest/TestPointMaskGrid.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_POINT_MASK_GRID_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_POINT_MASK_GRID_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +// Forward declaration of main class +template +class PointMaskGrid; + +/// @brief Makes every voxel of the @c grid active if it contains a point. +/// +/// @param points points that active the voxels of @c grid +/// @param grid on out its voxels with points are active +template +inline void +pointMaskGrid(const PointListT& points, GridT& grid) +{ + PointMaskGrid tmp(grid, NULL); + tmp.addPoints(points); +} + +/// @brief Return a MaskGrid where each binary voxel value +/// is on if the voxel contains one (or more) points (i.e. +/// the 3D position of a point is closer to this voxel than +/// any other voxels). +/// +/// @param points points that active the voxels in the returned grid. +/// @param xform transform from world space to voxels in grid space. +template +inline MaskGrid::Ptr +createPointMaskGrid(const PointListT& points, const math::Transform& xform) +{ + MaskGrid::Ptr grid = createGrid( false ); + grid->setTransform( xform.copy() ); + pointMaskGrid( points, *grid ); + return grid; +} + +//////////////////////////////////////// + +/// @brief Makes every voxel of a grid active if it contains a point. +template +class PointMaskGrid +{ +public: + typedef typename GridT::ValueType ValueT; + + /// @brief Constructor from a grid and optional interrupter + /// + /// @param grid Grid whoes voxels will have their state activated by points. + /// @param interrupter Optional interrupter to prematurely terminate execution. + explicit PointMaskGrid(GridT& grid, InterrupterT* interrupter = NULL) + : mGrid(&grid) + , mInterrupter(interrupter) + { + } + + /// @brief Activates the state of any voxel in the input grid that contains a point. + /// + /// @param points List of points that active the voxels in the input grid. + /// @param grainSize Set the grain-size used for multi-threading. A value of 0 + /// disables multi-threading! + template + void addPoints(const PointListT& points, size_t grainSize = 1024) + { + if (mInterrupter) mInterrupter->start("PointMaskGrid: adding points"); + if (grainSize>0) { + typename GridT::Ptr examplar = mGrid->copy( CP_NEW ); + PoolType pool( *examplar );//thread local storage pool of grids + AddPoints tmp(points, pool, grainSize, *this ); + if ( this->interrupt() ) return; + ReducePool reducePool(pool, mGrid, size_t(0)); + } else { + const math::Transform& xform = mGrid->transform(); + typename GridT::Accessor acc = mGrid->getAccessor(); + Vec3R wPos; + for (size_t i = 0, n = points.size(); i < n; ++i) { + if ( this->interrupt() ) break; + points.getPos(i, wPos); + acc.setValueOn( xform.worldToIndexCellCentered( wPos ) ); + } + } + if (mInterrupter) mInterrupter->end(); + } + +private: + // Disallow copy construction and copy by assignment! + PointMaskGrid(const PointMaskGrid&);// not implemented + PointMaskGrid& operator=(const PointMaskGrid&);// not implemented + + bool interrupt() const + { + if (mInterrupter && util::wasInterrupted(mInterrupter)) { + tbb::task::self().cancel_group_execution(); + return true; + } + return false; + } + + // Private struct that implements concurrent thread-local + // insersion of points into a grid + typedef tbb::enumerable_thread_specific PoolType; + template struct AddPoints; + + // Private class that implements concurrent reduction of a thread-local pool + struct ReducePool; + + GridT* mGrid; + InterrupterT* mInterrupter; +};// PointMaskGrid + +// Private member class that implements concurrent thread-local +// insersion of points into a grid +template +template +struct PointMaskGrid::AddPoints +{ + AddPoints(const PointListT& points, + PoolType& pool, + size_t grainSize, + const PointMaskGrid& parent) + : mPoints(&points) + , mParent(&parent) + , mPool(&pool) + { + tbb::parallel_for(tbb::blocked_range(0, mPoints->size(), grainSize), *this); + } + void operator()(const tbb::blocked_range& range) const + { + if (mParent->interrupt()) return; + GridT& grid = mPool->local(); + const math::Transform& xform = grid.transform(); + typename GridT::Accessor acc = grid.getAccessor(); + Vec3R wPos; + for (size_t i=range.begin(), n=range.end(); i!=n; ++i) { + mPoints->getPos(i, wPos); + acc.setValueOn( xform.worldToIndexCellCentered( wPos ) ); + } + } + const PointListT* mPoints; + const PointMaskGrid* mParent; + PoolType* mPool; + +};// end of private member class AddPoints + +// Private member class that implements concurrent reduction of a thread-local pool +template +struct PointMaskGrid::ReducePool +{ + typedef std::vector VecT; + typedef typename VecT::iterator IterT; + typedef tbb::blocked_range RangeT; + + ReducePool(PoolType& pool, GridT* grid, size_t grainSize = 1) + : mOwnsGrid(false) + , mGrid(grid) + { + if ( grainSize == 0 ) { + typedef typename PoolType::const_iterator IterT; + for (IterT i=pool.begin(); i!=pool.end(); ++i) mGrid->topologyUnion( *i ); + } else { + VecT grids( pool.size() ); + typename PoolType::iterator i = pool.begin(); + for (size_t j=0; j != pool.size(); ++i, ++j) grids[j] = &(*i); + tbb::parallel_reduce( RangeT( grids.begin(), grids.end(), grainSize ), *this ); + } + } + + ReducePool(const ReducePool&, tbb::split) + : mOwnsGrid(true) + , mGrid(new GridT()) + { + } + + ~ReducePool() { if (mOwnsGrid) delete mGrid; } + + void operator()(const RangeT& r) + { + for (IterT i=r.begin(); i!=r.end(); ++i) mGrid->topologyUnion( *(*i) ); + } + + void join(ReducePool& other) { mGrid->topologyUnion(*other.mGrid); } + + const bool mOwnsGrid; + GridT* mGrid; +};// end of private member class ReducePool + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif //OPENVDB_TOOLS_POINT_MASK_GRID_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/PointPartitioner.h b/nuparu/include/openvdb/include/openvdb/tools/PointPartitioner.h new file mode 100644 index 00000000..9ada5210 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/PointPartitioner.h @@ -0,0 +1,1038 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 PointPartitioner.h +/// +/// @brief Spatially partitions points using a parallel radix-based +/// sorting algorithm. +/// +/// @details Performs a stable deterministic sort; partitioning the same +/// point sequence will produce the same result each time. +/// @details The algorithm is unbounded meaning that points may be +/// distributed anywhere in index space. +/// @details The actual points are never stored in the tool, only +/// offsets into an external array. +/// +/// @author Mihai Alden + +#ifndef OPENVDB_TOOLS_POINT_PARTITIONER_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_POINT_PARTITIONER_HAS_BEEN_INCLUDED + + +#include +#include + +#include +#include +#include +#include // std::pair +#include + +#include // boost::int_t::least +#include +#include +#include // boost::math::isfinite + +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + + +//////////////////////////////////////// + + +/// @brief Partitions points into @c BucketLog2Dim aligned buckets +/// using a parallel radix-based sorting algorithm. +/// +/// @interface PointArray +/// Expected interface for the PointArray container: +/// @code +/// template +/// struct PointArray +/// { +/// // The type used to represent world-space point positions +/// typedef VectorType PosType; +/// +/// // Return the number of points in the array +/// size_t size() const; +/// +/// // Return the world-space position of the nth point in the array. +/// void getPos(size_t n, PosType& xyz) const; +/// }; +/// @endcode +/// +/// @details Performs a stable deterministic sort; partitioning the same +/// point sequence will produce the same result each time. +/// @details The algorithm is unbounded meaning that points may be +/// distributed anywhere in index space. +/// @details The actual points are never stored in the tool, only +/// offsets into an external array. +/// @details @c BucketLog2Dim defines the bucket coordinate dimensions, +/// i.e. BucketLog2Dim = 3 corresponds to a bucket that spans +/// a (2^3)^3 = 8^3 voxel region. +template +class PointPartitioner +{ +public: + enum { LOG2DIM = BucketLog2Dim }; + + typedef boost::shared_ptr Ptr; + typedef boost::shared_ptr ConstPtr; + + typedef PointIndexType IndexType; + typedef typename boost::int_t<1 + (3 * BucketLog2Dim)>::least VoxelOffsetType; + typedef boost::scoped_array VoxelOffsetArray; + + class IndexIterator; + + ////////// + + PointPartitioner(); + + /// @brief Partitions point indices into @c BucketLog2Dim aligned buckets. + /// + /// @param points list of world space points. + /// @param xform world to index space transform. + /// @param voxelOrder sort point indices by local voxel offsets. + /// @param recordVoxelOffsets construct local voxel offsets + template + void construct(const PointArray& points, const math::Transform& xform, + bool voxelOrder = false, bool recordVoxelOffsets = false); + + + /// @brief Partitions point indices into @c BucketLog2Dim aligned buckets. + /// + /// @param points list of world space points. + /// @param xform world to index space transform. + /// @param voxelOrder sort point indices by local voxel offsets. + /// @param recordVoxelOffsets construct local voxel offsets + template + static Ptr create(const PointArray& points, const math::Transform& xform, + bool voxelOrder = false, bool recordVoxelOffsets = false); + + + /// @brief Returns the number of buckets. + size_t size() const { return mPageCount; } + + /// @brief true if the container size is 0, false otherwise. + bool empty() const { return mPageCount == 0; } + + /// @brief Removes all data and frees up memory. + void clear(); + + /// @brief Exchanges the content of the container by another. + void swap(PointPartitioner&); + + /// @brief Returns the point indices for bucket @a n + IndexIterator indices(size_t n) const; + + /// @brief Returns the coordinate-aligned bounding box for bucket @a n + CoordBBox getBBox(size_t n) const { + return CoordBBox::createCube(mPageCoordinates[n], (1u << BucketLog2Dim)); + } + + /// @brief Returns the origin coordinate for bucket @a n + const Coord& origin(size_t n) const { return mPageCoordinates[n]; } + + /// @brief Returns a list of @c LeafNode voxel offsets for the points. + /// @note The list is optionally constructed. + const VoxelOffsetArray& voxelOffsets() const { return mVoxelOffsets; } + +private: + // Disallow copying + PointPartitioner(const PointPartitioner&); + PointPartitioner& operator=(const PointPartitioner&); + + boost::scoped_array mPointIndices; + VoxelOffsetArray mVoxelOffsets; + + boost::scoped_array mPageOffsets; + boost::scoped_array mPageCoordinates; + IndexType mPageCount; +}; // class PointPartitioner + + +typedef PointPartitioner UInt32PointPartitioner; + + +template +class PointPartitioner::IndexIterator +{ +public: + typedef PointIndexType IndexType; + + IndexIterator(IndexType* begin = NULL, IndexType* end = NULL) + : mBegin(begin), mEnd(end), mItem(begin) {} + + /// @brief Rewind to first item. + void reset() { mItem = mBegin; } + + /// @brief Number of point indices in the iterator range. + size_t size() const { return mEnd - mBegin; } + + /// @brief Returns the item to which this iterator is currently pointing. + IndexType& operator*() { assert(mItem != NULL); return *mItem; } + const IndexType& operator*() const { assert(mItem != NULL); return *mItem; } + + /// @brief Return @c true if this iterator is not yet exhausted. + operator bool() const { return mItem < mEnd; } + bool test() const { return mItem < mEnd; } + + /// @brief Advance to the next item. + IndexIterator& operator++() { assert(this->test()); ++mItem; return *this; } + + /// @brief Advance to the next item. + bool next() { this->operator++(); return this->test(); } + bool increment() { this->next(); return this->test(); } + + /// @brief Equality operators + bool operator==(const IndexIterator& other) const { return mItem == other.mItem; } + bool operator!=(const IndexIterator& other) const { return !this->operator==(other); } + +private: + IndexType * const mBegin, * const mEnd; + IndexType * mItem; +}; // class PointPartitioner::IndexIterator + + +//////////////////////////////////////// +//////////////////////////////////////// + +// Implementation details + + +namespace point_partitioner_internal { + + +template +struct ComputePointOrderOp +{ + ComputePointOrderOp(PointIndexType* pointOrder, + const PointIndexType* bucketCounters, const PointIndexType* bucketOffsets) + : mPointOrder(pointOrder) + , mBucketCounters(bucketCounters) + , mBucketOffsets(bucketOffsets) + { + } + + void operator()(const tbb::blocked_range& range) const { + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + mPointOrder[n] += mBucketCounters[mBucketOffsets[n]]; + } + } + + PointIndexType * const mPointOrder; + PointIndexType const * const mBucketCounters; + PointIndexType const * const mBucketOffsets; +}; // struct ComputePointOrderOp + + +template +struct CreateOrderedPointIndexArrayOp +{ + CreateOrderedPointIndexArrayOp(PointIndexType* orderedIndexArray, + const PointIndexType* pointOrder, const PointIndexType* indices) + : mOrderedIndexArray(orderedIndexArray) + , mPointOrder(pointOrder) + , mIndices(indices) + { + } + + void operator()(const tbb::blocked_range& range) const { + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + mOrderedIndexArray[mPointOrder[n]] = mIndices[n]; + } + } + + PointIndexType * const mOrderedIndexArray; + PointIndexType const * const mPointOrder; + PointIndexType const * const mIndices; +}; // struct CreateOrderedPointIndexArrayOp + + +template +struct VoxelOrderOp +{ + typedef typename boost::int_t<1 + (3 * BucketLog2Dim)>::least VoxelOffsetType; + typedef boost::scoped_array VoxelOffsetArray; + typedef boost::scoped_array IndexArray; + + VoxelOrderOp(IndexArray& indices, const IndexArray& pages,const VoxelOffsetArray& offsets) + : mIndices(indices.get()) + , mPages(pages.get()) + , mVoxelOffsets(offsets.get()) + { + } + + void operator()(const tbb::blocked_range& range) const { + + PointIndexType pointCount = 0; + for (size_t n(range.begin()), N(range.end()); n != N; ++n) { + pointCount = std::max(pointCount, (mPages[n + 1] - mPages[n])); + } + + const PointIndexType voxelCount = 1 << (3 * BucketLog2Dim); + + // allocate histogram buffers + boost::scoped_array offsets(new VoxelOffsetType[pointCount]); + boost::scoped_array sortedIndices(new PointIndexType[pointCount]); + boost::scoped_array histogram(new PointIndexType[voxelCount]); + + for (size_t n(range.begin()), N(range.end()); n != N; ++n) { + + PointIndexType * const indices = mIndices + mPages[n]; + pointCount = mPages[n + 1] - mPages[n]; + + // local copy of voxel offsets. + for (PointIndexType i = 0; i < pointCount; ++i) { + offsets[i] = mVoxelOffsets[ indices[i] ]; + } + + // reset histogram + memset(&histogram[0], 0, voxelCount * sizeof(PointIndexType)); + + // compute histogram + for (PointIndexType i = 0; i < pointCount; ++i) { + ++histogram[ offsets[i] ]; + } + + PointIndexType count = 0, startOffset; + for (int i = 0; i < int(voxelCount); ++i) { + if (histogram[i] > 0) { + startOffset = count; + count += histogram[i]; + histogram[i] = startOffset; + } + } + + // sort indices based on voxel offset + for (PointIndexType i = 0; i < pointCount; ++i) { + sortedIndices[ histogram[ offsets[i] ]++ ] = indices[i]; + } + + memcpy(&indices[0], &sortedIndices[0], sizeof(PointIndexType) * pointCount); + } + } + + PointIndexType * const mIndices; + PointIndexType const * const mPages; + VoxelOffsetType const * const mVoxelOffsets; +}; // struct VoxelOrderOp + + +template +struct LeafNodeOriginOp +{ + typedef boost::scoped_array IndexArray; + typedef boost::scoped_array CoordArray; + + LeafNodeOriginOp(CoordArray& coordinates, + const IndexArray& indices, const IndexArray& pages, + const PointArray& points, const math::Transform& m, int log2dim) + : mCoordinates(coordinates.get()) + , mIndices(indices.get()) + , mPages(pages.get()) + , mPoints(&points) + , mXForm(m) + , mLog2Dim(log2dim) + { + } + + void operator()(const tbb::blocked_range& range) const { + + typedef typename PointArray::PosType PosType; + + const int mask = ~((1 << mLog2Dim) - 1); + Coord ijk; + PosType pos; + + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + + mPoints->getPos(mIndices[mPages[n]], pos); + + if (boost::math::isfinite(pos[0]) && + boost::math::isfinite(pos[1]) && + boost::math::isfinite(pos[2])) { + + ijk = mXForm.worldToIndexCellCentered(pos); + + ijk[0] &= mask; + ijk[1] &= mask; + ijk[2] &= mask; + + mCoordinates[n] = ijk; + } + } + } + + Coord * const mCoordinates; + PointIndexType const * const mIndices; + PointIndexType const * const mPages; + PointArray const * const mPoints; + math::Transform const mXForm; + int const mLog2Dim; +}; // struct LeafNodeOriginOp + + +//////////////////////////////////////// + + +template +struct Array +{ + typedef boost::shared_ptr Ptr; + + Array(size_t size) : mSize(size), mData(new T[size]) { } + + size_t size() const { return mSize; } + + T* data() { return mData.get(); } + const T* data() const { return mData.get(); } + + void clear() { mSize = 0; mData.reset(); } + +private: + size_t mSize; + boost::scoped_array mData; +}; // struct Array + + +template +struct MoveSegmentDataOp +{ + typedef Array Segment; + typedef typename Segment::Ptr SegmentPtr; + + MoveSegmentDataOp(std::vector& indexLists, SegmentPtr* segments) + : mIndexLists(&indexLists[0]), mSegments(segments) + { + } + + void operator()(const tbb::blocked_range& range) const { + for (size_t n(range.begin()), N(range.end()); n != N; ++n) { + PointIndexType* indices = mIndexLists[n]; + SegmentPtr& segment = mSegments[n]; + + tbb::parallel_for(tbb::blocked_range(0, segment->size()), + CopyData(indices, segment->data())); + + segment.reset(); // clear data + } + } + +private: + + struct CopyData + { + CopyData(PointIndexType* lhs, const PointIndexType* rhs) : mLhs(lhs), mRhs(rhs) { } + + void operator()(const tbb::blocked_range& range) const { + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + mLhs[n] = mRhs[n]; + } + } + + PointIndexType * const mLhs; + PointIndexType const * const mRhs; + }; + + PointIndexType * const * const mIndexLists; + SegmentPtr * const mSegments; +}; // struct MoveSegmentDataOp + + +template +struct MergeBinsOp +{ + typedef Array Segment; + typedef typename Segment::Ptr SegmentPtr; + + typedef std::pair IndexPair; + typedef std::deque IndexPairList; + typedef boost::shared_ptr IndexPairListPtr; + typedef std::map IndexPairListMap; + typedef boost::shared_ptr IndexPairListMapPtr; + + MergeBinsOp(IndexPairListMapPtr* bins, + SegmentPtr* indexSegments, + SegmentPtr* offsetSegments, + Coord* coords, + size_t numSegments) + : mBins(bins) + , mIndexSegments(indexSegments) + , mOffsetSegments(offsetSegments) + , mCoords(coords) + , mNumSegments(numSegments) + { + } + + void operator()(const tbb::blocked_range& range) const { + + std::vector data; + std::vector arrayOffsets; + + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + + const Coord& ijk = mCoords[n]; + size_t numIndices = 0; + + data.clear(); + + for (size_t i = 0, I = mNumSegments; i < I; ++i) { + + IndexPairListMap& idxMap = *mBins[i]; + typename IndexPairListMap::iterator iter = idxMap.find(ijk); + + if (iter != idxMap.end() && iter->second) { + IndexPairListPtr& idxListPtr = iter->second; + + data.push_back(&idxListPtr); + numIndices += idxListPtr->size(); + } + } + + if (data.empty() || numIndices == 0) continue; + + SegmentPtr& indexSegment = mIndexSegments[n]; + SegmentPtr& offsetSegment = mOffsetSegments[n]; + + indexSegment.reset(new Segment(numIndices)); + offsetSegment.reset(new Segment(numIndices)); + + arrayOffsets.clear(); + arrayOffsets.reserve(data.size()); + + for (size_t i = 0, count = 0, I = data.size(); i < I; ++i) { + arrayOffsets.push_back(PointIndexType(count)); + count += (*data[i])->size(); + } + + tbb::parallel_for(tbb::blocked_range(0, data.size()), + CopyData(&data[0], &arrayOffsets[0], indexSegment->data(), offsetSegment->data())); + } + } + +private: + + struct CopyData + { + CopyData(IndexPairListPtr** indexLists, + const PointIndexType* arrayOffsets, + PointIndexType* indices, + PointIndexType* offsets) + : mIndexLists(indexLists) + , mArrayOffsets(arrayOffsets) + , mIndices(indices) + , mOffsets(offsets) + { + } + + void operator()(const tbb::blocked_range& range) const { + + typedef typename IndexPairList::const_iterator CIter; + + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + + const PointIndexType arrayOffset = mArrayOffsets[n]; + PointIndexType* indexPtr = &mIndices[arrayOffset]; + PointIndexType* offsetPtr = &mOffsets[arrayOffset]; + + IndexPairListPtr& list = *mIndexLists[n]; + + for (CIter it = list->begin(), end = list->end(); it != end; ++it) { + const IndexPair& data = *it; + *indexPtr++ = data.first; + *offsetPtr++ = data.second; + } + + list.reset(); // clear data + } + } + + IndexPairListPtr * const * const mIndexLists; + PointIndexType const * const mArrayOffsets; + PointIndexType * const mIndices; + PointIndexType * const mOffsets; + }; // struct CopyData + + IndexPairListMapPtr * const mBins; + SegmentPtr * const mIndexSegments; + SegmentPtr * const mOffsetSegments; + Coord const * const mCoords; + size_t const mNumSegments; +}; // struct MergeBinsOp + + +template +struct BinPointIndicesOp +{ + typedef typename PointArray::PosType PosType; + typedef std::pair IndexPair; + typedef std::deque IndexPairList; + typedef boost::shared_ptr IndexPairListPtr; + typedef std::map IndexPairListMap; + typedef boost::shared_ptr IndexPairListMapPtr; + + BinPointIndicesOp(IndexPairListMapPtr* data, + const PointArray& points, + VoxelOffsetType* voxelOffsets, + const math::Transform& m, + Index binLog2Dim, + Index bucketLog2Dim, + size_t numSegments) + : mData(data) + , mPoints(&points) + , mVoxelOffsets(voxelOffsets) + , mXForm(m) + , mBinLog2Dim(binLog2Dim) + , mBucketLog2Dim(bucketLog2Dim) + , mNumSegments(numSegments) + { + } + + void operator()(const tbb::blocked_range& range) const { + + const Index log2dim = mBucketLog2Dim; + const Index log2dim2 = 2 * log2dim; + const Index bucketMask = (1u << log2dim) - 1u; + + const Index binLog2dim = mBinLog2Dim; + const Index binLog2dim2 = 2 * binLog2dim; + + const Index binMask = (1u << (log2dim + binLog2dim)) - 1u; + const Index invBinMask = ~binMask; + + IndexPairList * idxList = NULL; + Coord ijk(0, 0, 0), loc(0, 0, 0), binCoord(0, 0, 0), lastBinCoord(1, 2, 3); + PosType pos; + + PointIndexType bucketOffset = 0; + VoxelOffsetType voxelOffset = 0; + + const size_t numPoints = mPoints->size(); + const size_t segmentSize = numPoints / mNumSegments; + + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + + IndexPairListMapPtr& dataPtr = mData[n]; + if (!dataPtr) dataPtr.reset(new IndexPairListMap()); + IndexPairListMap& idxMap = *dataPtr; + + const bool isLastSegment = (n + 1) >= mNumSegments; + + const size_t start = n * segmentSize; + const size_t end = isLastSegment ? numPoints : (start + segmentSize); + + for (size_t i = start; i != end; ++i) { + + mPoints->getPos(i, pos); + + if (boost::math::isfinite(pos[0]) && + boost::math::isfinite(pos[1]) && + boost::math::isfinite(pos[2])) { + + ijk = mXForm.worldToIndexCellCentered(pos); + + if (mVoxelOffsets) { + loc[0] = ijk[0] & bucketMask; + loc[1] = ijk[1] & bucketMask; + loc[2] = ijk[2] & bucketMask; + voxelOffset = VoxelOffsetType((loc[0] << log2dim2) + (loc[1] << log2dim) + loc[2]); + } + + binCoord[0] = ijk[0] & invBinMask; + binCoord[1] = ijk[1] & invBinMask; + binCoord[2] = ijk[2] & invBinMask; + + ijk[0] &= binMask; + ijk[1] &= binMask; + ijk[2] &= binMask; + + ijk[0] >>= log2dim; + ijk[1] >>= log2dim; + ijk[2] >>= log2dim; + + bucketOffset = PointIndexType((ijk[0] << binLog2dim2) + (ijk[1] << binLog2dim) + ijk[2]); + + if (lastBinCoord != binCoord) { + lastBinCoord = binCoord; + IndexPairListPtr& idxListPtr = idxMap[lastBinCoord]; + if (!idxListPtr) idxListPtr.reset(new IndexPairList()); + idxList = idxListPtr.get(); + } + + idxList->push_back(IndexPair(PointIndexType(i), bucketOffset)); + if (mVoxelOffsets) mVoxelOffsets[i] = voxelOffset; + } + } + } + } + + IndexPairListMapPtr * const mData; + PointArray const * const mPoints; + VoxelOffsetType * const mVoxelOffsets; + math::Transform const mXForm; + Index const mBinLog2Dim; + Index const mBucketLog2Dim; + size_t const mNumSegments; +}; // struct BinPointIndicesOp + + +template +struct OrderSegmentsOp +{ + typedef boost::scoped_array IndexArray; + typedef typename Array::Ptr SegmentPtr; + + OrderSegmentsOp(SegmentPtr* indexSegments, SegmentPtr* offestSegments, + IndexArray* pageOffsetArrays, Index binVolume) + : mIndexSegments(indexSegments) + , mOffsetSegments(offestSegments) + , mPageOffsetArrays(pageOffsetArrays) + , mBinVolume(binVolume) + { + } + + void operator()(const tbb::blocked_range& range) const { + + const size_t bucketCountersSize = size_t(mBinVolume); + IndexArray bucketCounters(new PointIndexType[bucketCountersSize]); + + size_t maxSegmentSize = 0; + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + maxSegmentSize = std::max(maxSegmentSize, mIndexSegments[n]->size()); + } + + IndexArray bucketIndices(new PointIndexType[maxSegmentSize]); + + + for (size_t n = range.begin(), N = range.end(); n != N; ++n) { + + memset(bucketCounters.get(), 0, sizeof(PointIndexType) * bucketCountersSize); + + const size_t segmentSize = mOffsetSegments[n]->size(); + PointIndexType* offsets = mOffsetSegments[n]->data(); + + // Count the number of points per bucket and assign a local bucket index + // to each point. + for (size_t i = 0; i < segmentSize; ++i) { + bucketIndices[i] = bucketCounters[offsets[i]]++; + } + + PointIndexType nonemptyBucketCount = 0; + for (size_t i = 0; i < bucketCountersSize; ++i) { + nonemptyBucketCount += static_cast(bucketCounters[i] != 0); + } + + + IndexArray& pageOffsets = mPageOffsetArrays[n]; + pageOffsets.reset(new PointIndexType[nonemptyBucketCount + 1]); + pageOffsets[0] = nonemptyBucketCount + 1; // stores array size in first element + + // Compute bucket counter prefix sum + PointIndexType count = 0, idx = 1; + for (size_t i = 0; i < bucketCountersSize; ++i) { + if (bucketCounters[i] != 0) { + pageOffsets[idx] = bucketCounters[i]; + bucketCounters[i] = count; + count += pageOffsets[idx]; + ++idx; + } + } + + PointIndexType* indices = mIndexSegments[n]->data(); + const tbb::blocked_range segmentRange(0, segmentSize); + + // Compute final point order by incrementing the local bucket point index + // with the prefix sum offset. + tbb::parallel_for(segmentRange, ComputePointOrderOp( + bucketIndices.get(), bucketCounters.get(), offsets)); + + tbb::parallel_for(segmentRange, CreateOrderedPointIndexArrayOp( + offsets, bucketIndices.get(), indices)); + + mIndexSegments[n]->clear(); // clear data + } + } + + SegmentPtr * const mIndexSegments; + SegmentPtr * const mOffsetSegments; + IndexArray * const mPageOffsetArrays; + Index const mBinVolume; +}; // struct OrderSegmentsOp + + +//////////////////////////////////////// + + +/// @brief Segment points using one level of least significant digit radix bins. +template +inline void binAndSegment( + const PointArray& points, + const math::Transform& xform, + boost::scoped_array::Ptr>& indexSegments, + boost::scoped_array::Ptr>& offsetSegments, + size_t& segmentCount, + const Index binLog2Dim, + const Index bucketLog2Dim, + VoxelOffsetType* voxelOffsets = NULL) +{ + typedef std::pair IndexPair; + typedef std::deque IndexPairList; + typedef boost::shared_ptr IndexPairListPtr; + typedef std::map IndexPairListMap; + typedef boost::shared_ptr IndexPairListMapPtr; + + size_t numTasks = 1, numThreads = size_t(tbb::task_scheduler_init::default_num_threads()); + if (points.size() > (numThreads * 2)) numTasks = numThreads * 2; + else if (points.size() > numThreads) numTasks = numThreads; + + boost::scoped_array bins(new IndexPairListMapPtr[numTasks]); + + typedef BinPointIndicesOp BinOp; + + tbb::parallel_for(tbb::blocked_range(0, numTasks), + BinOp(bins.get(), points, voxelOffsets, xform, binLog2Dim, bucketLog2Dim, numTasks)); + + std::set uniqueCoords; + + for (size_t i = 0; i < numTasks; ++i) { + IndexPairListMap& idxMap = *bins[i]; + for (typename IndexPairListMap::iterator it = idxMap.begin(); it != idxMap.end(); ++it) { + uniqueCoords.insert(it->first); + } + } + + std::vector coords(uniqueCoords.begin(), uniqueCoords.end()); + uniqueCoords.clear(); + + segmentCount = coords.size(); + + typedef typename Array::Ptr SegmentPtr; + + indexSegments.reset(new SegmentPtr[segmentCount]); + offsetSegments.reset(new SegmentPtr[segmentCount]); + + typedef MergeBinsOp MergeOp; + + tbb::parallel_for(tbb::blocked_range(0, segmentCount), + MergeOp(bins.get(), indexSegments.get(), offsetSegments.get(), &coords[0], numTasks)); +} + + +template +inline void partition( + const PointArray& points, + const math::Transform& xform, + const Index bucketLog2Dim, + boost::scoped_array& pointIndices, + boost::scoped_array& pageOffsets, + PointIndexType& pageCount, + boost::scoped_array& voxelOffsets, + bool recordVoxelOffsets) +{ + if (recordVoxelOffsets) voxelOffsets.reset(new VoxelOffsetType[points.size()]); + else voxelOffsets.reset(); + + const Index binLog2Dim = 5u; + // note: Bins span a (2^(binLog2Dim + bucketLog2Dim))^3 voxel region, + // i.e. bucketLog2Dim = 3 and binLog2Dim = 5 corresponds to a + // (2^8)^3 = 256^3 voxel region. + + + size_t numSegments = 0; + + boost::scoped_array::Ptr> indexSegments; + boost::scoped_array::Ptr> offestSegments; + + binAndSegment(points, xform, + indexSegments, offestSegments, numSegments, binLog2Dim, bucketLog2Dim, voxelOffsets.get()); + + const tbb::blocked_range segmentRange(0, numSegments); + + typedef boost::scoped_array IndexArray; + boost::scoped_array pageOffsetArrays(new IndexArray[numSegments]); + + const Index binVolume = 1u << (3u * binLog2Dim); + + tbb::parallel_for(segmentRange, OrderSegmentsOp + (indexSegments.get(), offestSegments.get(), pageOffsetArrays.get(), binVolume)); + + indexSegments.reset(); + + pageCount = 0; + for (size_t n = 0; n < numSegments; ++n) { + pageCount += pageOffsetArrays[n][0] - 1; + } + + pageOffsets.reset(new PointIndexType[pageCount + 1]); + + PointIndexType count = 0; + for (size_t n = 0, idx = 0; n < numSegments; ++n) { + + PointIndexType* offsets = pageOffsetArrays[n].get(); + size_t size = size_t(offsets[0]); + + for (size_t i = 1; i < size; ++i) { + pageOffsets[idx++] = count; + count += offsets[i]; + } + } + + pageOffsets[pageCount] = count; + + pointIndices.reset(new PointIndexType[points.size()]); + + std::vector indexArray; + indexArray.reserve(numSegments); + + PointIndexType* index = pointIndices.get(); + for (size_t n = 0; n < numSegments; ++n) { + indexArray.push_back(index); + index += offestSegments[n]->size(); + } + + tbb::parallel_for(segmentRange, MoveSegmentDataOp(indexArray, offestSegments.get())); +} + + +} // namespace point_partitioner_internal + + +//////////////////////////////////////// + + +template +inline PointPartitioner::PointPartitioner() + : mPointIndices(NULL) + , mVoxelOffsets(NULL) + , mPageOffsets(NULL) + , mPageCoordinates(NULL) + , mPageCount(0) +{ +} + + +template +inline void +PointPartitioner::clear() +{ + mPageCount = 0; + mPointIndices.reset(); + mVoxelOffsets.reset(); + mPageOffsets.reset(); + mPageCoordinates.reset(); +} + + +template +inline void +PointPartitioner::swap(PointPartitioner& rhs) +{ + const IndexType tmpLhsPageCount = mPageCount; + mPageCount = rhs.mPageCount; + rhs.mPageCount = tmpLhsPageCount; + + mPointIndices.swap(rhs.mPointIndices); + mVoxelOffsets.swap(rhs.mVoxelOffsets); + mPageOffsets.swap(rhs.mPageOffsets); + mPageCoordinates.swap(rhs.mPageCoordinates); +} + + +template +inline typename PointPartitioner::IndexIterator +PointPartitioner::indices(size_t n) const +{ + assert(bool(mPointIndices) && bool(mPageCount)); + return IndexIterator( + mPointIndices.get() + mPageOffsets[n], + mPointIndices.get() + mPageOffsets[n + 1]); +} + + +template +template +inline void +PointPartitioner::construct(const PointArray& points, + const math::Transform& xform, bool voxelOrder, bool recordVoxelOffsets) +{ + point_partitioner_internal::partition(points, xform, BucketLog2Dim, + mPointIndices, mPageOffsets, mPageCount, mVoxelOffsets, (voxelOrder || recordVoxelOffsets)); + + const tbb::blocked_range pageRange(0, mPageCount); + mPageCoordinates.reset(new Coord[mPageCount]); + + tbb::parallel_for(pageRange, + point_partitioner_internal::LeafNodeOriginOp + (mPageCoordinates, mPointIndices, mPageOffsets, points, xform, BucketLog2Dim)); + + if (mVoxelOffsets && voxelOrder) { + tbb::parallel_for(pageRange, point_partitioner_internal::VoxelOrderOp< + IndexType, BucketLog2Dim>(mPointIndices, mPageOffsets, mVoxelOffsets)); + } + + if (mVoxelOffsets && !recordVoxelOffsets) { + mVoxelOffsets.reset(); + } +} + + +template +template +inline typename PointPartitioner::Ptr +PointPartitioner::create(const PointArray& points, const math::Transform& xform, + bool voxelOrder, bool recordVoxelOffsets) +{ + Ptr ret(new PointPartitioner()); + ret->construct(points, xform, voxelOrder, recordVoxelOffsets); + return ret; +} + + +//////////////////////////////////////// + + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + + +#endif // OPENVDB_TOOLS_POINT_PARTITIONER_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/PointScatter.h b/nuparu/include/openvdb/include/openvdb/tools/PointScatter.h new file mode 100644 index 00000000..6ba9e76b --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/PointScatter.h @@ -0,0 +1,437 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 PointScatter.h +/// +/// @brief We offer three different algorithms (each in its own class) +/// for scattering of point in active voxels: +/// +/// 1) UniformPointScatter. Has two modes: Either randomly distributes +/// a fixed number of points in the active voxels, or the user can +/// specify a fixed probability of having a points per unit of volume. +/// +/// 2) DenseUniformPointScatter. Randomly distributes points in active +/// voxels using a fixed number of points per voxel. +/// +/// 3) NonIniformPointScatter. Define the local probability of having +/// a point in a voxel as the product of a global density and the +/// value of the voxel itself. + +#ifndef OPENVDB_TOOLS_POINT_SCATTER_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_POINT_SCATTER_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// Forward declaration of base class +template +class BasePointScatter; + +/// @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 +class UniformPointScatter : public BasePointScatter +{ +public: + typedef BasePointScatter BaseT; + + UniformPointScatter(PointAccessorType& points, + Index64 pointCount, + RandomGenerator& randGen, + InterruptType* interrupt = NULL) + : BaseT(points, randGen, interrupt) + , mTargetPointCount(pointCount) + , mPointsPerVolume(0.0f) + { + } + UniformPointScatter(PointAccessorType& points, + float pointsPerVolume, + RandomGenerator& randGen, + InterruptType* interrupt = NULL) + : BaseT(points, randGen, interrupt) + , mTargetPointCount(0) + , mPointsPerVolume(pointsPerVolume) + { + } + + /// @brief This is the main functor method implementing the actual + /// scattering of points. + template + bool operator()(const GridT& grid) + { + mVoxelCount = grid.activeVoxelCount(); + if (mVoxelCount == 0) return false; + const Vec3d dim = grid.voxelSize(); + if (mPointsPerVolume>0) { + BaseT::start("Uniform scattering with fixed point density"); + mTargetPointCount = Index64(mPointsPerVolume*dim[0]*dim[1]*dim[2])*mVoxelCount; + } else if (mTargetPointCount>0) { + BaseT::start("Uniform scattering with fixed point count"); + mPointsPerVolume = mTargetPointCount/float(dim[0]*dim[1]*dim[2] * mVoxelCount); + } else { + return false; + } + + boost::scoped_array list(new Index64[mTargetPointCount]); + math::RandInt rand(BaseT::mRand01.engine(), 0, mVoxelCount-1); + for (Index64 i=0; i +class DenseUniformPointScatter : public BasePointScatter +{ +public: + typedef BasePointScatter BaseT; + + DenseUniformPointScatter(PointAccessorType& points, + float pointsPerVoxel, + RandomGenerator& randGen, + InterruptType* interrupt = NULL) + : BaseT(points, randGen, interrupt) + , mPointsPerVoxel(pointsPerVoxel) + { + } + + /// This is the main functor method implementing the actual scattering of points. + template + bool operator()(const GridT& grid) + { + typedef typename GridT::ValueOnCIter ValueIter; + if (mPointsPerVoxel < 1.0e-6) return false; + mVoxelCount = grid.activeVoxelCount(); + if (mVoxelCount == 0) return false; + BaseT::start("Dense uniform scattering with fixed point count"); + CoordBBox bbox; + const Vec3R offset(0.5, 0.5, 0.5); + + const int ppv = math::Floor(mPointsPerVoxel); + const double delta = mPointsPerVoxel - ppv; + const bool fractional = !math::isApproxZero(delta, 1.0e-6); + + for (ValueIter iter = grid.cbeginValueOn(); iter; ++iter) { + if (BaseT::interrupt()) return false; + if (iter.isVoxelValue()) {// a majority is expected to be voxels + const Vec3R dmin = iter.getCoord() - offset; + for (int n = 0; n != ppv; ++n) BaseT::addPoint(grid, dmin); + if (fractional && BaseT::getRand() < delta) BaseT::addPoint(grid, dmin); + } else {// tiles contain multiple (virtual) voxels + iter.getBoundingBox(bbox); + const Coord size(bbox.extents()); + const Vec3R dmin = bbox.min() - offset; + const double d = mPointsPerVoxel * iter.getVoxelCount(); + const int m = math::Floor(d); + for (int n = 0; n != m; ++n) BaseT::addPoint(grid, dmin, size); + if (BaseT::getRand() < d - m) BaseT::addPoint(grid, dmin, size); + } + }//loop over all the active voxels and tiles + + BaseT::end(); + return true; + } + + // 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 << "Dense uniformly scattered " << mPointCount << " points into " << mVoxelCount + << " active voxels in \"" << name << "\" corresponding to " + << mPointsPerVoxel << " points per voxel." << std::endl; + } + + float getPointsPerVoxel() const { return mPointsPerVoxel; } + +private: + using BaseT::mPointCount; + using BaseT::mVoxelCount; + float mPointsPerVoxel; +}; // class DenseUniformPointScatter + +/// @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 (called pointsPerVolume) 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 +class NonUniformPointScatter : public BasePointScatter +{ +public: + typedef BasePointScatter BaseT; + + NonUniformPointScatter(PointAccessorType& points, + float pointsPerVolume, + RandomGenerator& randGen, + InterruptType* interrupt = NULL) + : BaseT(points, randGen, interrupt) + , mPointsPerVolume(pointsPerVolume)//note this is merely a + //multiplier for the local point density + { + } + + /// This is the main functor method implementing the actual scattering of points. + template + bool operator()(const GridT& grid) + { + if (mPointsPerVolume <= 0.0f) return false; + mVoxelCount = grid.activeVoxelCount(); + if (mVoxelCount == 0) return false; + BaseT::start("Non-uniform scattering with local point density"); + const Vec3d dim = grid.voxelSize(); + const double volumePerVoxel = dim[0]*dim[1]*dim[2], + pointsPerVoxel = mPointsPerVolume * volumePerVoxel; + CoordBBox bbox; + const Vec3R offset(0.5, 0.5, 0.5); + for (typename GridT::ValueOnCIter iter = grid.cbeginValueOn(); iter; ++iter) { + if (BaseT::interrupt()) return false; + const double d = (*iter) * pointsPerVoxel * iter.getVoxelCount(); + const int n = int(d); + if (iter.isVoxelValue()) { // a majority is expected to be voxels + const Vec3R dmin =iter.getCoord() - offset; + for (int i = 0; i < n; ++i) BaseT::addPoint(grid, dmin); + if (BaseT::getRand() < (d - n)) BaseT::addPoint(grid, dmin); + } else { // tiles contain multiple (virtual) voxels + iter.getBoundingBox(bbox); + const Coord size(bbox.extents()); + const Vec3R dmin = bbox.min() - offset; + for (int i = 0; i < n; ++i) BaseT::addPoint(grid, dmin, size); + if (BaseT::getRand() < (d - n)) BaseT::addPoint(grid, dmin, size); + } + }//loop over all the active voxels and tiles + BaseT::end(); + return true; + } + + // 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-uniformly scattered " << mPointCount << " points into " << mVoxelCount + << " active voxels in \"" << name << "\"." << std::endl; + } + + float getPointPerVolume() const { return mPointsPerVolume; } + +private: + using BaseT::mPointCount; + using BaseT::mVoxelCount; + float mPointsPerVolume; + +}; // class NonUniformPointScatter + +/// Base class of all the point scattering classes defined above +template +class BasePointScatter +{ +public: + + Index64 getPointCount() const { return mPointCount; } + Index64 getVoxelCount() const { return mVoxelCount; } + +protected: + + /// This is a base class so the constructor is protected + BasePointScatter(PointAccessorType& points, + RandomGenerator& randGen, + InterruptType* interrupt = NULL) + : mPoints(points) + , mInterrupter(interrupt) + , mPointCount(0) + , mVoxelCount(0) + , mInterruptCount(0) + , mRand01(randGen) + { + } + + PointAccessorType& mPoints; + InterruptType* mInterrupter; + Index64 mPointCount; + Index64 mVoxelCount; + Index64 mInterruptCount; + math::Rand01 mRand01; + + inline void start(const char* name) + { + if (mInterrupter) mInterrupter->start(name); + } + + inline void end() + { + if (mInterrupter) mInterrupter->end(); + } + + inline bool interrupt() + { + //only check interrupter for every 32'th call + return !(mInterruptCount++ & ((1<<5)-1)) && util::wasInterrupted(mInterrupter); + } + + inline double getRand() { return mRand01(); } + + template + inline void addPoint(const GridT &grid, const Vec3R &dmin) + { + const Vec3R pos(dmin[0] + this->getRand(), + dmin[1] + this->getRand(), + dmin[2] + this->getRand()); + mPoints.add(grid.indexToWorld(pos)); + ++mPointCount; + } + + template + inline void addPoint(const GridT &grid, const Vec3R &dmin, const Coord &size) + { + const Vec3R pos(dmin[0] + size[0]*this->getRand(), + dmin[1] + size[1]*this->getRand(), + dmin[2] + size[2]*this->getRand()); + mPoints.add(grid.indexToWorld(pos)); + ++mPointCount; + } +};// class BasePointScatter + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_POINT_SCATTER_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/PoissonSolver.h b/nuparu/include/openvdb/include/openvdb/tools/PoissonSolver.h new file mode 100644 index 00000000..60a1f6d6 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/PoissonSolver.h @@ -0,0 +1,774 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 PoissonSolver.h +/// +/// @authors D.J. Hill, Peter Cucka +/// +/// @brief Solve Poisson's equation ∇2x = b +/// for x, where @e b is a vector comprising the values of all of the active voxels +/// in a grid. +/// +/// @par Example: +/// Solve for the pressure in a cubic tank of liquid, assuming uniform boundary conditions: +/// @code +/// FloatTree source(/*background=*/0.0f); +/// // Activate voxels to indicate that they contain liquid. +/// source.fill(CoordBBox(Coord(0, -10, 0), Coord(10, 0, 10)), /*value=*/0.0f); +/// +/// math::pcg::State state = math::pcg::terminationDefaults(); +/// FloatTree::Ptr solution = tools::poisson::solve(source, state); +/// @endcode +/// +/// @par Example: +/// Solve for the pressure, P, in a cubic tank of liquid that is open at the top. +/// Boundary conditions are P = 0 at the top, +/// ∂P/∂y = −1 at the bottom +/// and ∂P/∂x = 0 at the sides: +///
+///                P = 0
+///             +--------+ (N,0,N)
+///            /|       /|
+///   (0,0,0) +--------+ |
+///           | |      | | dP/dx = 0
+/// dP/dx = 0 | +------|-+
+///           |/       |/
+///  (0,-N,0) +--------+ (N,-N,N)
+///           dP/dy = -1
+/// 
+/// @code +/// const int N = 10; +/// DoubleTree source(/*background=*/0.0); +/// // Activate voxels to indicate that they contain liquid. +/// source.fill(CoordBBox(Coord(0, -N, 0), Coord(N, 0, N)), /*value=*/0.0); +/// +/// // C++11 +/// auto boundary = [](const openvdb::Coord& ijk, const openvdb::Coord& neighbor, +/// double& source, double& diagonal) +/// { +/// if (neighbor.x() == ijk.x() && neighbor.z() == ijk.z()) { +/// if (neighbor.y() < ijk.y()) source -= 1.0; +/// else diagonal -= 1.0; +/// } +/// }; +/// +/// math::pcg::State state = math::pcg::terminationDefaults(); +/// util::NullInterrupter interrupter; +/// +/// DoubleTree::Ptr solution = tools::poisson::solveWithBoundaryConditions( +/// source, boundary, state, interrupter); +/// @endcode + +#ifndef OPENVDB_TOOLS_POISSONSOLVER_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_POISSONSOLVER_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include + +#include "Morphology.h" // for erodeVoxels + +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { +namespace poisson { + +// This type should be at least as wide as math::pcg::SizeType. +typedef Int32 VIndex; + +/// The type of a matrix used to represent a three-dimensional Laplacian operator +typedef math::pcg::SparseStencilMatrix LaplacianMatrix; + + +//@{ +/// @brief Solve ∇2x = b for x, +/// where @e b is a vector comprising the values of all of the active voxels +/// in the input tree. +/// @return a new tree, with the same active voxel topology as the input tree, +/// whose voxel values are the elements of the solution vector x. +/// @details On input, the State object should specify convergence criteria +/// (minimum error and maximum number of iterations); on output, it gives +/// the actual termination conditions. +/// @details The solution is computed using the conjugate gradient method +/// with (where possible) incomplete Cholesky preconditioning, falling back +/// to Jacobi preconditioning. +/// @sa solveWithBoundaryConditions +template +inline typename TreeType::Ptr +solve(const TreeType&, math::pcg::State&); + +template +inline typename TreeType::Ptr +solve(const TreeType&, math::pcg::State&, Interrupter&); +//@} + + +//@{ +/// @brief Solve ∇2x = b for x +/// with user-specified boundary conditions, where @e b is a vector comprising +/// the values of all of the active voxels in the input tree or domain mask if provided +/// @return a new tree, with the same active voxel topology as the input tree, +/// whose voxel values are the elements of the solution vector x. +/// @details On input, the State object should specify convergence criteria +/// (minimum error and maximum number of iterations); on output, it gives +/// the actual termination conditions. +/// @details The solution is computed using the conjugate gradient method with +/// the specified type of preconditioner (default: incomplete Cholesky), +/// falling back to Jacobi preconditioning if necessary. +/// @details Each thread gets its own copy of the BoundaryOp, which should be +/// a functor of the form +/// @code +/// struct BoundaryOp { +/// typedef LaplacianMatrix::ValueType ValueType; +/// void operator()( +/// const Coord& ijk, // coordinates of a boundary voxel +/// const Coord& ijkNeighbor, // coordinates of an exterior neighbor of ijk +/// ValueType& source, // element of b corresponding to ijk +/// ValueType& diagonal // element of Laplacian matrix corresponding to ijk +/// ) const; +/// }; +/// @endcode +/// The functor is called for each of the exterior neighbors of each boundary voxel @ijk, +/// and it must specify a boundary condition for @ijk by modifying one or both of two +/// provided values: the entry in the source vector @e b corresponding to @ijk and +/// the weighting coefficient for @ijk in the Laplacian operator matrix. +/// +/// @sa solve +template +inline typename TreeType::Ptr +solveWithBoundaryConditions(const TreeType&, const BoundaryOp&, math::pcg::State&, Interrupter&); + +template +inline typename TreeType::Ptr +solveWithBoundaryConditionsAndPreconditioner(const TreeType&, const BoundaryOp&, + math::pcg::State&, Interrupter&); + +template +inline typename TreeType::Ptr +solveWithBoundaryConditionsAndPreconditioner(const TreeType&, const DomainTreeType&, const BoundaryOp&, + math::pcg::State&, Interrupter&); +//@} + + +/// @name Low-level functions +//@{ +// The following are low-level routines that can be used to assemble custom solvers. + +/// @brief Overwrite each active voxel in the given scalar tree +/// with a sequential index, starting from zero. +template +inline void populateIndexTree(VIndexTreeType&); + +/// @brief Iterate over the active voxels of the input tree and for each one +/// assign its index in the iteration sequence to the corresponding voxel +/// of an integer-valued output tree. +template +inline typename TreeType::template ValueConverter::Type::Ptr +createIndexTree(const TreeType&); + + +/// @brief Return a vector of the active voxel values of the scalar-valued @a source tree. +/// @details The nth element of the vector corresponds to the voxel whose value +/// in the @a index tree is @e n. +/// @param source a tree with a scalar value type +/// @param index a tree of the same configuration as @a source but with +/// value type VIndex that maps voxels to elements of the output vector +template +inline typename math::pcg::Vector::Ptr +createVectorFromTree( + const SourceTreeType& source, + const typename SourceTreeType::template ValueConverter::Type& index); + + +/// @brief Return a tree with the same active voxel topology as the @a index tree +/// but whose voxel values are taken from the the given vector. +/// @details The voxel whose value in the @a index tree is @e n gets assigned +/// the nth element of the vector. +/// @param index a tree with value type VIndex that maps voxels to elements of @a values +/// @param values a vector of values with which to populate the active voxels of the output tree +/// @param background the value for the inactive voxels of the output tree +template +inline typename VIndexTreeType::template ValueConverter::Type::Ptr +createTreeFromVector( + const math::pcg::Vector& values, + const VIndexTreeType& index, + const TreeValueType& background); + + +/// @brief Generate a sparse matrix of the index-space (Δx = 1) Laplacian operator +/// using second-order finite differences. +/// @details This construction assumes homogeneous Dirichlet boundary conditions +/// (exterior grid points are zero). +template +inline LaplacianMatrix::Ptr +createISLaplacian( + const typename BoolTreeType::template ValueConverter::Type& vectorIndexTree, + const BoolTreeType& interiorMask); + + +/// @brief Generate a sparse matrix of the index-space (Δx = 1) Laplacian operator +/// with user-specified boundary conditions using second-order finite differences. +/// @details Each thread gets its own copy of @a boundaryOp, which should be +/// a functor of the form +/// @code +/// struct BoundaryOp { +/// typedef LaplacianMatrix::ValueType ValueType; +/// void operator()( +/// const Coord& ijk, // coordinates of a boundary voxel +/// const Coord& ijkNeighbor, // coordinates of an exterior neighbor of ijk +/// ValueType& source, // element of source vector corresponding to ijk +/// ValueType& diagonal // element of Laplacian matrix corresponding to ijk +/// ) const; +/// }; +/// @endcode +/// The functor is called for each of the exterior neighbors of each boundary voxel @ijk, +/// and it must specify a boundary condition for @ijk by modifying one or both of two +/// provided values: an entry in the given @a source vector corresponding to @ijk and +/// the weighting coefficient for @ijk in the Laplacian matrix. +template +inline LaplacianMatrix::Ptr +createISLaplacianWithBoundaryConditions( + const typename BoolTreeType::template ValueConverter::Type& vectorIndexTree, + const BoolTreeType& interiorMask, + const BoundaryOp& boundaryOp, + typename math::pcg::Vector& source); + +//@} + + +//////////////////////////////////////// + + +namespace internal { + +/// @brief Functor for use with LeafManager::foreach() to populate an array +/// with per-leaf active voxel counts +template +struct LeafCountOp +{ + VIndex* count; + LeafCountOp(VIndex* count_): count(count_) {} + void operator()(const LeafType& leaf, size_t leafIdx) const { + count[leafIdx] = static_cast(leaf.onVoxelCount()); + } +}; + + +/// @brief Functor for use with LeafManager::foreach() to populate +/// active leaf voxels with sequential indices +template +struct LeafIndexOp +{ + const VIndex* count; + LeafIndexOp(const VIndex* count_): count(count_) {} + void operator()(LeafType& leaf, size_t leafIdx) const { + VIndex idx = (leafIdx == 0) ? 0 : count[leafIdx - 1]; + for (typename LeafType::ValueOnIter it = leaf.beginValueOn(); it; ++it) { + it.setValue(idx++); + } + } +}; + +} // namespace internal + + +template +inline void +populateIndexTree(VIndexTreeType& result) +{ + typedef typename VIndexTreeType::LeafNodeType LeafT; + typedef typename tree::LeafManager LeafMgrT; + + // Linearize the tree. + LeafMgrT leafManager(result); + const size_t leafCount = leafManager.leafCount(); + + // Count the number of active voxels in each leaf node. + boost::scoped_array perLeafCount(new VIndex[leafCount]); + VIndex* perLeafCountPtr = perLeafCount.get(); + leafManager.foreach(internal::LeafCountOp(perLeafCountPtr)); + + // The starting index for each leaf node is the total number + // of active voxels in all preceding leaf nodes. + for (size_t i = 1; i < leafCount; ++i) { + perLeafCount[i] += perLeafCount[i - 1]; + } + + // The last accumulated value should be the total of all active voxels. + assert(Index64(perLeafCount[leafCount-1]) == result.activeVoxelCount()); + + // Parallelize over the leaf nodes of the tree, storing a unique index + // in each active voxel. + leafManager.foreach(internal::LeafIndexOp(perLeafCountPtr)); +} + + +template +inline typename TreeType::template ValueConverter::Type::Ptr +createIndexTree(const TreeType& tree) +{ + typedef typename TreeType::template ValueConverter::Type VIdxTreeT; + + // Construct an output tree with the same active voxel topology as the input tree. + const VIndex invalidIdx = -1; + typename VIdxTreeT::Ptr result( + new VIdxTreeT(tree, /*background=*/invalidIdx, TopologyCopy())); + + // All active voxels are degrees of freedom, including voxels contained in active tiles. + result->voxelizeActiveTiles(); + + populateIndexTree(*result); + + return result; +} + + +//////////////////////////////////////// + + +namespace internal { + +/// @brief Functor for use with LeafManager::foreach() to populate a vector +/// with the values of a tree's active voxels +template +struct CopyToVecOp +{ + typedef typename SourceTreeType::template ValueConverter::Type VIdxTreeT; + typedef typename VIdxTreeT::LeafNodeType VIdxLeafT; + typedef typename SourceTreeType::LeafNodeType LeafT; + typedef typename SourceTreeType::ValueType TreeValueT; + typedef typename math::pcg::Vector VectorT; + + const SourceTreeType* tree; + VectorT* vector; + + CopyToVecOp(const SourceTreeType& t, VectorT& v): tree(&t), vector(&v) {} + + void operator()(const VIdxLeafT& idxLeaf, size_t /*leafIdx*/) const + { + VectorT& vec = *vector; + if (const LeafT* leaf = tree->probeLeaf(idxLeaf.origin())) { + // If a corresponding leaf node exists in the source tree, + // copy voxel values from the source node to the output vector. + for (typename VIdxLeafT::ValueOnCIter it = idxLeaf.cbeginValueOn(); it; ++it) { + vec[*it] = leaf->getValue(it.pos()); + } + } else { + // If no corresponding leaf exists in the source tree, + // fill the vector with a uniform value. + const TreeValueT& value = tree->getValue(idxLeaf.origin()); + for (typename VIdxLeafT::ValueOnCIter it = idxLeaf.cbeginValueOn(); it; ++it) { + vec[*it] = value; + } + } + } +}; + +} // namespace internal + + +template +inline typename math::pcg::Vector::Ptr +createVectorFromTree(const SourceTreeType& tree, + const typename SourceTreeType::template ValueConverter::Type& idxTree) +{ + typedef typename SourceTreeType::template ValueConverter::Type VIdxTreeT; + typedef tree::LeafManager VIdxLeafMgrT; + typedef typename math::pcg::Vector VectorT; + + // Allocate the vector. + const size_t numVoxels = idxTree.activeVoxelCount(); + typename VectorT::Ptr result(new VectorT(static_cast(numVoxels))); + + // Parallelize over the leaf nodes of the index tree, filling the output vector + // with values from corresponding voxels of the source tree. + VIdxLeafMgrT leafManager(idxTree); + leafManager.foreach(internal::CopyToVecOp(tree, *result)); + + return result; +} + + +//////////////////////////////////////// + + +namespace internal { + +/// @brief Functor for use with LeafManager::foreach() to populate a tree +/// with values from a vector +template +struct CopyFromVecOp +{ + typedef typename VIndexTreeType::template ValueConverter::Type OutTreeT; + typedef typename OutTreeT::LeafNodeType OutLeafT; + typedef typename VIndexTreeType::LeafNodeType VIdxLeafT; + typedef typename math::pcg::Vector VectorT; + + const VectorT* vector; + OutTreeT* tree; + + CopyFromVecOp(const VectorT& v, OutTreeT& t): vector(&v), tree(&t) {} + + void operator()(const VIdxLeafT& idxLeaf, size_t /*leafIdx*/) const + { + const VectorT& vec = *vector; + OutLeafT* leaf = tree->probeLeaf(idxLeaf.origin()); + assert(leaf != NULL); + for (typename VIdxLeafT::ValueOnCIter it = idxLeaf.cbeginValueOn(); it; ++it) { + leaf->setValueOnly(it.pos(), static_cast(vec[*it])); + } + } +}; + +} // namespace internal + + +template +inline typename VIndexTreeType::template ValueConverter::Type::Ptr +createTreeFromVector( + const math::pcg::Vector& vector, + const VIndexTreeType& idxTree, + const TreeValueType& background) +{ + typedef typename VIndexTreeType::template ValueConverter::Type OutTreeT; + typedef typename tree::LeafManager VIdxLeafMgrT; + + // Construct an output tree with the same active voxel topology as the index tree. + typename OutTreeT::Ptr result(new OutTreeT(idxTree, background, TopologyCopy())); + OutTreeT& tree = *result; + + // Parallelize over the leaf nodes of the index tree, populating voxels + // of the output tree with values from the input vector. + VIdxLeafMgrT leafManager(idxTree); + leafManager.foreach( + internal::CopyFromVecOp(vector, tree)); + + return result; +} + + +//////////////////////////////////////// + + +namespace internal { + +/// Constant boundary condition functor +template +struct DirichletOp { + inline void operator()( + const Coord&, const Coord&, ValueType&, ValueType& diag) const { diag -= 1; } +}; + + +/// Functor for use with LeafManager::foreach() to populate a sparse Laplacian matrix +template +struct ISLaplacianOp +{ + typedef typename BoolTreeType::template ValueConverter::Type VIdxTreeT; + typedef typename VIdxTreeT::LeafNodeType VIdxLeafT; + typedef LaplacianMatrix::ValueType ValueT; + typedef typename math::pcg::Vector VectorT; + + LaplacianMatrix* laplacian; + const VIdxTreeT* idxTree; + const BoolTreeType* interiorMask; + const BoundaryOp boundaryOp; + VectorT* source; + + ISLaplacianOp(LaplacianMatrix& m, const VIdxTreeT& idx, + const BoolTreeType& mask, const BoundaryOp& op, VectorT& src): + laplacian(&m), idxTree(&idx), interiorMask(&mask), boundaryOp(op), source(&src) {} + + void operator()(const VIdxLeafT& idxLeaf, size_t /*leafIdx*/) const + { + // Local accessors + typename tree::ValueAccessor interior(*interiorMask); + typename tree::ValueAccessor vectorIdx(*idxTree); + + Coord ijk; + VIndex column; + const ValueT diagonal = -6.f, offDiagonal = 1.f; + + // Loop over active voxels in this leaf. + for (typename VIdxLeafT::ValueOnCIter it = idxLeaf.cbeginValueOn(); it; ++it) { + assert(it.getValue() > -1); + const math::pcg::SizeType rowNum = static_cast(it.getValue()); + + LaplacianMatrix::RowEditor row = laplacian->getRowEditor(rowNum); + + ijk = it.getCoord(); + if (interior.isValueOn(ijk)) { + // The current voxel is an interior voxel. + // All of its neighbors are in the solution domain. + + // -x direction + row.setValue(vectorIdx.getValue(ijk.offsetBy(-1, 0, 0)), offDiagonal); + // -y direction + row.setValue(vectorIdx.getValue(ijk.offsetBy(0, -1, 0)), offDiagonal); + // -z direction + row.setValue(vectorIdx.getValue(ijk.offsetBy(0, 0, -1)), offDiagonal); + // diagonal + row.setValue(rowNum, diagonal); + // +z direction + row.setValue(vectorIdx.getValue(ijk.offsetBy(0, 0, 1)), offDiagonal); + // +y direction + row.setValue(vectorIdx.getValue(ijk.offsetBy(0, 1, 0)), offDiagonal); + // +x direction + row.setValue(vectorIdx.getValue(ijk.offsetBy(1, 0, 0)), offDiagonal); + + } else { + // The current voxel is a boundary voxel. + // At least one of its neighbors is outside the solution domain. + + ValueT modifiedDiagonal = 0.f; + + // -x direction + if (vectorIdx.probeValue(ijk.offsetBy(-1, 0, 0), column)) { + row.setValue(column, offDiagonal); + modifiedDiagonal -= 1; + } else { + boundaryOp(ijk, ijk.offsetBy(-1, 0, 0), source->at(rowNum), modifiedDiagonal); + } + // -y direction + if (vectorIdx.probeValue(ijk.offsetBy(0, -1, 0), column)) { + row.setValue(column, offDiagonal); + modifiedDiagonal -= 1; + } else { + boundaryOp(ijk, ijk.offsetBy(0, -1, 0), source->at(rowNum), modifiedDiagonal); + } + // -z direction + if (vectorIdx.probeValue(ijk.offsetBy(0, 0, -1), column)) { + row.setValue(column, offDiagonal); + modifiedDiagonal -= 1; + } else { + boundaryOp(ijk, ijk.offsetBy(0, 0, -1), source->at(rowNum), modifiedDiagonal); + } + // +z direction + if (vectorIdx.probeValue(ijk.offsetBy(0, 0, 1), column)) { + row.setValue(column, offDiagonal); + modifiedDiagonal -= 1; + } else { + boundaryOp(ijk, ijk.offsetBy(0, 0, 1), source->at(rowNum), modifiedDiagonal); + } + // +y direction + if (vectorIdx.probeValue(ijk.offsetBy(0, 1, 0), column)) { + row.setValue(column, offDiagonal); + modifiedDiagonal -= 1; + } else { + boundaryOp(ijk, ijk.offsetBy(0, 1, 0), source->at(rowNum), modifiedDiagonal); + } + // +x direction + if (vectorIdx.probeValue(ijk.offsetBy(1, 0, 0), column)) { + row.setValue(column, offDiagonal); + modifiedDiagonal -= 1; + } else { + boundaryOp(ijk, ijk.offsetBy(1, 0, 0), source->at(rowNum), modifiedDiagonal); + } + // diagonal + row.setValue(rowNum, modifiedDiagonal); + } + } // end loop over voxels + } +}; + +} // namespace internal + + +template +inline LaplacianMatrix::Ptr +createISLaplacian(const typename BoolTreeType::template ValueConverter::Type& idxTree, + const BoolTreeType& interiorMask) +{ + typedef LaplacianMatrix::ValueType ValueT; + math::pcg::Vector unused( + static_cast(idxTree.activeVoxelCount())); + internal::DirichletOp op; + return createISLaplacianWithBoundaryConditions(idxTree, interiorMask, op, unused); +} + + +template +inline LaplacianMatrix::Ptr +createISLaplacianWithBoundaryConditions( + const typename BoolTreeType::template ValueConverter::Type& idxTree, + const BoolTreeType& interiorMask, + const BoundaryOp& boundaryOp, + typename math::pcg::Vector& source) +{ + typedef typename BoolTreeType::template ValueConverter::Type VIdxTreeT; + typedef typename tree::LeafManager VIdxLeafMgrT; + + // The number of active voxels is the number of degrees of freedom. + const Index64 numDoF = idxTree.activeVoxelCount(); + + // Construct the matrix. + LaplacianMatrix::Ptr laplacianPtr( + new LaplacianMatrix(static_cast(numDoF))); + LaplacianMatrix& laplacian = *laplacianPtr; + + // Populate the matrix using a second-order, 7-point CD stencil. + VIdxLeafMgrT idxLeafManager(idxTree); + idxLeafManager.foreach(internal::ISLaplacianOp( + laplacian, idxTree, interiorMask, boundaryOp, source)); + + return laplacianPtr; +} + + +//////////////////////////////////////// + + +template +inline typename TreeType::Ptr +solve(const TreeType& inTree, math::pcg::State& state) +{ + util::NullInterrupter interrupter; + return solve(inTree, state, interrupter); +} + + +template +inline typename TreeType::Ptr +solve(const TreeType& inTree, math::pcg::State& state, Interrupter& interrupter) +{ + internal::DirichletOp boundaryOp; + return solveWithBoundaryConditions(inTree, boundaryOp, state, interrupter); +} + + +template +inline typename TreeType::Ptr +solveWithBoundaryConditions(const TreeType& inTree, const BoundaryOp& boundaryOp, + math::pcg::State& state, Interrupter& interrupter) +{ + typedef math::pcg::IncompleteCholeskyPreconditioner DefaultPrecondT; + return solveWithBoundaryConditionsAndPreconditioner( + inTree, boundaryOp, state, interrupter); +} + + +template +inline typename TreeType::Ptr +solveWithBoundaryConditionsAndPreconditioner(const TreeType& inTree, + const BoundaryOp& boundaryOp, math::pcg::State& state, Interrupter& interrupter) +{ + + return solveWithBoundaryConditionsAndPreconditioner(inTree /*source*/, inTree /*domain mask*/, + boundaryOp, state, interrupter); +} + +template +inline typename TreeType::Ptr +solveWithBoundaryConditionsAndPreconditioner(const TreeType& inTree, + const DomainTreeType& domainMask, + const BoundaryOp& boundaryOp, + math::pcg::State& state, Interrupter& interrupter) +{ + + typedef typename TreeType::ValueType TreeValueT; + typedef LaplacianMatrix::ValueType VecValueT; + typedef typename math::pcg::Vector VectorT; + typedef typename TreeType::template ValueConverter::Type VIdxTreeT; + typedef typename TreeType::template ValueConverter::Type MaskTreeT; + + // 1. Create a mapping from active voxels of the input tree to elements of a vector. + typename VIdxTreeT::ConstPtr idxTree = createIndexTree(domainMask); + + // 2. Populate a vector with values from the input tree. + typename VectorT::Ptr b = createVectorFromTree(inTree, *idxTree); + + // 3. Create a mask of the interior voxels of the input tree (from the densified index tree). + typename MaskTreeT::Ptr interiorMask( + new MaskTreeT(*idxTree, /*background=*/false, TopologyCopy())); + tools::erodeVoxels(*interiorMask, /*iterations=*/1, tools::NN_FACE); + + // 4. Create the Laplacian matrix. + LaplacianMatrix::Ptr laplacian = createISLaplacianWithBoundaryConditions( + *idxTree, *interiorMask, boundaryOp, *b); + + // 5. Solve the Poisson equation. + laplacian->scale(-1.0); // matrix is negative-definite; solve -M x = -b + b->scale(-1.0); + typename VectorT::Ptr x(new VectorT(b->size(), zeroVal())); + typename math::pcg::Preconditioner::Ptr precond( + new PreconditionerType(*laplacian)); + if (!precond->isValid()) { + precond.reset(new math::pcg::JacobiPreconditioner(*laplacian)); + } + + state = math::pcg::solve(*laplacian, *b, *x, *precond, interrupter, state); + + // 6. Populate the output tree with values from the solution vector. + /// @todo if (state.success) ... ? + return createTreeFromVector(*x, *idxTree, /*background=*/zeroVal()); +} + +} // namespace poisson +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_POISSONSOLVER_HAS_BEEN_INCLUDED + +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/Prune.h b/nuparu/include/openvdb/include/openvdb/tools/Prune.h new file mode 100644 index 00000000..1c6d48d8 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/Prune.h @@ -0,0 +1,417 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 Prune.h +/// +/// @brief Defined various multi-threaded utility functions for trees +/// +/// @author Ken Museth + +#ifndef OPENVDB_TOOLS_PRUNE_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_PRUNE_HAS_BEEN_INCLUDED + +#include +#include +#include + +#include // for isNegative and negative +#include // for Index typedef +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Reduce the memory footprint of a @a tree by replacing with tiles +/// any nodes whose values are all the same (optionally to within a tolerance) +/// and have the same active state. +/// +/// @note For trees with floating-point values a child node with (approximately) +/// constant values are replaced with a tile value corresponding to the average +/// of the extrema values in said child node. Else the first value encountered +/// in the child node is used. +/// +/// @param tree the tree to be pruned +/// @param tolerance tolerance within which values are considered to be equal +/// @param threaded enable or disable threading (threading is enabled by default) +/// @param grainSize used to control the threading granularity (default is 1) +template +inline void +prune(TreeT& tree, + typename TreeT::ValueType tolerance = zeroVal(), + bool threaded = true, + size_t grainSize = 1); + + +/// @brief Reduce the memory footprint of a @a tree by replacing with tiles +/// any non-leaf nodes whose values are all the same (optionally to within a tolerance) +/// and have the same active state. +/// +/// @param tree the tree to be pruned +/// @param tolerance tolerance within which values are considered to be equal +/// @param threaded enable or disable threading (threading is enabled by default) +/// @param grainSize used to control the threading granularity (default is 1) +template +inline void +pruneTiles(TreeT& tree, + typename TreeT::ValueType tolerance = zeroVal(), + bool threaded = true, + size_t grainSize = 1); + + +/// @brief Reduce the memory footprint of a @a tree by replacing with +/// background tiles any nodes whose values are all inactive. +/// +/// @param tree the tree to be pruned +/// @param threaded enable or disable threading (threading is enabled by default) +/// @param grainSize used to control the threading granularity (default is 1) +template +inline void +pruneInactive(TreeT& tree, bool threaded = true, size_t grainSize = 1); + + +/// @brief Reduce the memory footprint of a @a tree by replacing any nodes +/// whose values are all inactive with tiles of the given @a value. +/// +/// @param tree the tree to be pruned +/// @param value value assigned to inactive tiles created during pruning +/// @param threaded enable or disable threading (threading is enabled by default) +/// @param grainSize used to control the threading granularity (default is 1) +template +inline void +pruneInactiveWithValue( + TreeT& tree, + const typename TreeT::ValueType& value, + bool threaded = true, + size_t grainSize = 1); + + +/// @brief Reduce the memory footprint of a @a tree by replacing nodes +/// whose values are all inactive with inactive tiles having a value equal to +/// the first value encountered in the (inactive) child. +/// @details This method is faster than tolerance-based prune and +/// useful for narrow-band level set applications where inactive +/// values are limited to either an inside or an outside value. +/// +/// @param tree the tree to be pruned +/// @param threaded enable or disable threading (threading is enabled by default) +/// @param grainSize used to control the threading granularity (default is 1) +/// +/// @throw ValueError if the background of the @a tree is negative (as defined by math::isNegative) +template +inline void +pruneLevelSet(TreeT& tree, + bool threaded = true, + size_t grainSize = 1); + + +/// @brief Reduce the memory footprint of a @a tree by replacing nodes whose voxel values +/// are all inactive with inactive tiles having the value -| @a insideWidth | +/// if the voxel values are negative and | @a outsideWidth | otherwise. +/// +/// @details This method is faster than tolerance-based prune and +/// useful for narrow-band level set applications where inactive +/// values are limited to either an inside or an outside value. +/// +/// @param tree the tree to be pruned +/// @param outsideWidth the width of the outside of the narrow band +/// @param insideWidth the width of the inside of the narrow band +/// @param threaded enable or disable threading (threading is enabled by default) +/// @param grainSize used to control the threading granularity (default is 1) +/// +/// @throw ValueError if @a outsideWidth is negative or @a insideWidth is +/// not negative (as defined by math::isNegative). +template +inline void +pruneLevelSet(TreeT& tree, + const typename TreeT::ValueType& outsideWidth, + const typename TreeT::ValueType& insideWidth, + bool threaded = true, + size_t grainSize = 1); + + +//////////////////////////////////////////////// + + +template +class InactivePruneOp +{ +public: + typedef typename TreeT::ValueType ValueT; + typedef typename TreeT::RootNodeType RootT; + typedef typename TreeT::LeafNodeType LeafT; + BOOST_STATIC_ASSERT(RootT::LEVEL > TerminationLevel); + + InactivePruneOp(TreeT& tree) : mValue(tree.background()) + { + tree.clearAllAccessors();//clear cache of nodes that could be pruned + } + + InactivePruneOp(TreeT& tree, const ValueT& v) : mValue(v) + { + tree.clearAllAccessors();//clear cache of nodes that could be pruned + } + + // Nothing to do at the leaf node level + void operator()(LeafT&) const {} + // Prune the child nodes of the internal nodes + template + void operator()(NodeT& node) const + { + if (NodeT::LEVEL > TerminationLevel) { + for (typename NodeT::ChildOnIter it=node.beginChildOn(); it; ++it) { + if (it->isInactive()) node.addTile(it.pos(), mValue, false); + } + } + } + // Prune the child nodes of the root node + void operator()(RootT& root) const + { + for (typename RootT::ChildOnIter it = root.beginChildOn(); it; ++it) { + if (it->isInactive()) root.addTile(it.getCoord(), mValue, false); + } + root.eraseBackgroundTiles(); + } +private: + + const ValueT mValue; +};// InactivePruneOp + + +template +class TolerancePruneOp +{ +public: + typedef typename TreeT::ValueType ValueT; + typedef typename TreeT::RootNodeType RootT; + typedef typename TreeT::LeafNodeType LeafT; + BOOST_STATIC_ASSERT(RootT::LEVEL > TerminationLevel); + + TolerancePruneOp(TreeT& tree, const ValueT& t) : mTolerance(t) + { + tree.clearAllAccessors();//clear cache of nodes that could be pruned + } + + // Prune the child nodes of the root node + inline void operator()(RootT& root) const + { + ValueT value; + bool state; + for (typename RootT::ChildOnIter it = root.beginChildOn(); it; ++it) { + if (this->isConstant(*it, value, state)) root.addTile(it.getCoord(), value, state); + } + root.eraseBackgroundTiles(); + } + + // Prune the child nodes of the internal nodes + template + inline void operator()(NodeT& node) const + { + if (NodeT::LEVEL > TerminationLevel) { + ValueT value; + bool state; + for (typename NodeT::ChildOnIter it=node.beginChildOn(); it; ++it) { + if (this->isConstant(*it, value, state)) node.addTile(it.pos(), value, state); + } + } + } + + // Nothing to do at the leaf node level + inline void operator()(LeafT&) const {} + +private: + + // For floating-point value types set tile values to + // the mean of the extrema values of the constant node + template + inline + typename boost::enable_if, bool>::type + isConstant(const NodeT& node, ValueT& value, bool& state) const + { + ValueT tmp; + const bool test = node.isConstant(value, tmp, state, mTolerance); + if (test) value = ValueT(0.5f)*(value + tmp); + return test; + } + + // For non-floating-point value types set tile values to + // the first value encountered in the constant node + template + inline + typename boost::disable_if, bool>::type + isConstant(const NodeT& node, ValueT& value, bool& state) const + { + return node.isConstant(value, state, mTolerance); + } + + const ValueT mTolerance; +};// TolerancePruneOp + + +template +class LevelSetPruneOp +{ +public: + typedef typename TreeT::ValueType ValueT; + typedef typename TreeT::RootNodeType RootT; + typedef typename TreeT::LeafNodeType LeafT; + BOOST_STATIC_ASSERT(RootT::LEVEL > TerminationLevel); + + LevelSetPruneOp(TreeT& tree) + : mOutside(tree.background()) + , mInside(math::negative(mOutside)) + { + if (math::isNegative(mOutside)) { + OPENVDB_THROW(ValueError, + "LevelSetPruneOp: the background value cannot be negative!"); + } + tree.clearAllAccessors();//clear cache of nodes that could be pruned + } + LevelSetPruneOp(TreeT& tree, const ValueT& outside, const ValueT& inside) + : mOutside(outside) + , mInside(inside) + { + if (math::isNegative(mOutside)) { + OPENVDB_THROW(ValueError, + "LevelSetPruneOp: the outside value cannot be negative!"); + } + if (!math::isNegative(mInside)) { + OPENVDB_THROW(ValueError, + "LevelSetPruneOp: the inside value must be negative!"); + } + tree.clearAllAccessors();//clear cache of nodes that could be pruned + } + // Nothing to do at the leaf node level + void operator()(LeafT&) const {} + // Prune the child nodes of the internal nodes + template + void operator()(NodeT& node) const + { + if (NodeT::LEVEL > TerminationLevel) { + for (typename NodeT::ChildOnIter it=node.beginChildOn(); it; ++it) { + if (it->isInactive()) node.addTile(it.pos(), this->getTileValue(it), false); + } + } + } + // Prune the child nodes of the root node + void operator()(RootT& root) const + { + for (typename RootT::ChildOnIter it = root.beginChildOn(); it; ++it) { + if (it->isInactive()) root.addTile(it.getCoord(), this->getTileValue(it), false); + } + root.eraseBackgroundTiles(); + } + +private: + template + inline ValueT getTileValue(const IterT& iter) const + { + return math::isNegative(iter->getFirstValue()) ? mInside : mOutside; + } + + const ValueT mOutside, mInside; +};// LevelSetPruneOp + + +template +inline void +prune(TreeT& tree, typename TreeT::ValueType tol, bool threaded, size_t grainSize) +{ + tree::NodeManager nodes(tree); + TolerancePruneOp op(tree, tol); + nodes.foreachBottomUp(op, threaded, grainSize); +} + + +template +inline void +pruneTiles(TreeT& tree, typename TreeT::ValueType tol, bool threaded, size_t grainSize) +{ + tree::NodeManager nodes(tree); + TolerancePruneOp op(tree, tol); + nodes.foreachBottomUp(op, threaded, grainSize); +} + + +template +inline void +pruneInactive(TreeT& tree, bool threaded, size_t grainSize) +{ + tree::NodeManager nodes(tree); + InactivePruneOp op(tree); + nodes.foreachBottomUp(op, threaded, grainSize); +} + + +template +inline void +pruneInactiveWithValue(TreeT& tree, const typename TreeT::ValueType& v, + bool threaded, size_t grainSize) +{ + tree::NodeManager nodes(tree); + InactivePruneOp op(tree, v); + nodes.foreachBottomUp(op, threaded, grainSize); +} + + +template +inline void +pruneLevelSet(TreeT& tree, + const typename TreeT::ValueType& outside, + const typename TreeT::ValueType& inside, + bool threaded, + size_t grainSize) +{ + tree::NodeManager nodes(tree); + LevelSetPruneOp op(tree, outside, inside); + nodes.foreachBottomUp(op, threaded, grainSize); +} + + +template +inline void +pruneLevelSet(TreeT& tree, bool threaded, size_t grainSize) +{ + tree::NodeManager nodes(tree); + LevelSetPruneOp op(tree); + nodes.foreachBottomUp(op, threaded, grainSize); +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_PRUNE_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/RayIntersector.h b/nuparu/include/openvdb/include/openvdb/tools/RayIntersector.h new file mode 100644 index 00000000..0f177517 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/RayIntersector.h @@ -0,0 +1,701 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 RayIntersector.h +/// +/// @author Ken Museth +/// +/// @brief Accelerated intersection of a ray with a narrow-band level +/// set or a generic (e.g. density) volume. This will of course be +/// useful for respectively surface and volume rendering. +/// +/// @details This file defines two main classes, +/// LevelSetRayIntersector and VolumeRayIntersector, as well as the +/// three support classes LevelSetHDDA, VolumeHDDA and LinearSearchImpl. +/// The LevelSetRayIntersector is templated on the LinearSearchImpl class +/// and calls instances of the LevelSetHDDA class. The reason to split +/// level set ray intersection into three classes is twofold. First +/// LevelSetRayIntersector defines the public API for client code and +/// LinearSearchImpl defines the actual algorithm used for the +/// ray level-set intersection. In other words this design will allow +/// for the public API to be fixed while the intersection algorithm +/// can change without resolving to (slow) virtual methods. Second, +/// LevelSetHDDA, which implements a hierarchical Differential Digital +/// Analyzer, relies on partial template specialization, so it has to +/// be a standalone class (as opposed to a member class of +/// LevelSetRayIntersector). The VolumeRayIntersector is conceptually +/// much simpler than the LevelSetRayIntersector, and hence it only +/// depends on VolumeHDDA that implements the hierarchical +/// Differential Digital Analyzer. + + +#ifndef OPENVDB_TOOLS_RAYINTERSECTOR_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_RAYINTERSECTOR_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include +#include "Morphology.h" +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +// Helper class that implements the actual search of the zero-crossing +// of the level set along the direction of a ray. This particular +// implementation uses iterative linear search. +template +class LinearSearchImpl; + + +///////////////////////////////////// LevelSetRayIntersector ///////////////////////////////////// + + +/// @brief This class provides the public API for intersecting a ray +/// with a narrow-band level set. +/// +/// @details It wraps a SearchImplT with a simple public API and +/// performs the actual hierarchical tree node and voxel traversal. +/// +/// @warning Use the (default) copy-constructor to make sure each +/// computational thread has their own instance of this class. This is +/// important since the SearchImplT contains a ValueAccessor that is +/// not thread-safe. However copying is very efficient. +/// +/// @see tools/RayTracer.h for examples of intended usage. +/// +/// @todo Add TrilinearSearchImpl, as an alternative to LinearSearchImpl, +/// that performs analytical 3D trilinear intersection tests, i.e., solves +/// cubic equations. This is slower but also more accurate than the 1D +/// linear interpolation in LinearSearchImpl. +template, + int NodeLevel = GridT::TreeType::RootNodeType::ChildNodeType::LEVEL, + typename RayT = math::Ray > +class LevelSetRayIntersector +{ +public: + typedef GridT GridType; + typedef RayT RayType; + typedef typename RayT::RealType RealType; + typedef typename RayT::Vec3T Vec3Type; + typedef typename GridT::ValueType ValueT; + typedef typename GridT::TreeType TreeT; + + BOOST_STATIC_ASSERT( NodeLevel >= -1 && NodeLevel < int(TreeT::DEPTH)-1); + BOOST_STATIC_ASSERT(boost::is_floating_point::value); + + /// @brief Constructor + /// @param grid level set grid to intersect rays against. + /// @param isoValue optional iso-value for the ray-intersection. + LevelSetRayIntersector(const GridT& grid, const ValueT& isoValue = zeroVal()) + : mTester(grid, isoValue) + { + if (!grid.hasUniformVoxels() ) { + OPENVDB_THROW(RuntimeError, + "LevelSetRayIntersector only supports uniform voxels!"); + } + if (grid.getGridClass() != GRID_LEVEL_SET) { + OPENVDB_THROW(RuntimeError, + "LevelSetRayIntersector only supports level sets!" + "\nUse Grid::setGridClass(openvdb::GRID_LEVEL_SET)"); + } + } + + /// @brief Return the iso-value used for ray-intersections + const ValueT& getIsoValue() const { return mTester.getIsoValue(); } + + /// @brief Return @c true if the index-space ray intersects the level set. + /// @param iRay ray represented in index space. + bool intersectsIS(const RayType& iRay) const + { + if (!mTester.setIndexRay(iRay)) return false;//missed bbox + return math::LevelSetHDDA::test(mTester); + } + + /// @brief Return @c true if the index-space ray intersects the level set + /// @param iRay ray represented in index space. + /// @param iTime if an intersection was found it is assigned the time of the + /// intersection along the index ray. + bool intersectsIS(const RayType& iRay, RealType &iTime) const + { + if (!mTester.setIndexRay(iRay)) return false;//missed bbox + iTime = mTester.getIndexTime(); + return math::LevelSetHDDA::test(mTester); + } + + /// @brief Return @c true if the index-space ray intersects the level set. + /// @param iRay ray represented in index space. + /// @param xyz if an intersection was found it is assigned the + /// intersection point in index space, otherwise it is unchanged. + bool intersectsIS(const RayType& iRay, Vec3Type& xyz) const + { + if (!mTester.setIndexRay(iRay)) return false;//missed bbox + if (!math::LevelSetHDDA::test(mTester)) return false;//missed level set + mTester.getIndexPos(xyz); + return true; + } + + /// @brief Return @c true if the index-space ray intersects the level set. + /// @param iRay ray represented in index space. + /// @param xyz if an intersection was found it is assigned the + /// intersection point in index space, otherwise it is unchanged. + /// @param iTime if an intersection was found it is assigned the time of the + /// intersection along the index ray. + bool intersectsIS(const RayType& iRay, Vec3Type& xyz, RealType &iTime) const + { + if (!mTester.setIndexRay(iRay)) return false;//missed bbox + if (!math::LevelSetHDDA::test(mTester)) return false;//missed level set + mTester.getIndexPos(xyz); + iTime = mTester.getIndexTime(); + return true; + } + + /// @brief Return @c true if the world-space ray intersects the level set. + /// @param wRay ray represented in world space. + bool intersectsWS(const RayType& wRay) const + { + if (!mTester.setWorldRay(wRay)) return false;//missed bbox + return math::LevelSetHDDA::test(mTester); + } + + /// @brief Return @c true if the world-space ray intersects the level set. + /// @param wRay ray represented in world space. + /// @param wTime if an intersection was found it is assigned the time of the + /// intersection along the world ray. + bool intersectsWS(const RayType& wRay, RealType &wTime) const + { + if (!mTester.setWorldRay(wRay)) return false;//missed bbox + wTime = mTester.getWorldTime(); + return math::LevelSetHDDA::test(mTester); + } + + /// @brief Return @c true if the world-space ray intersects the level set. + /// @param wRay ray represented in world space. + /// @param world if an intersection was found it is assigned the + /// intersection point in world space, otherwise it is unchanged + bool intersectsWS(const RayType& wRay, Vec3Type& world) const + { + if (!mTester.setWorldRay(wRay)) return false;//missed bbox + if (!math::LevelSetHDDA::test(mTester)) return false;//missed level set + mTester.getWorldPos(world); + return true; + } + + /// @brief Return @c true if the world-space ray intersects the level set. + /// @param wRay ray represented in world space. + /// @param world if an intersection was found it is assigned the + /// intersection point in world space, otherwise it is unchanged. + /// @param wTime if an intersection was found it is assigned the time of the + /// intersection along the world ray. + bool intersectsWS(const RayType& wRay, Vec3Type& world, RealType &wTime) const + { + if (!mTester.setWorldRay(wRay)) return false;//missed bbox + if (!math::LevelSetHDDA::test(mTester)) return false;//missed level set + mTester.getWorldPos(world); + wTime = mTester.getWorldTime(); + return true; + } + + /// @brief Return @c true if the world-space ray intersects the level set. + /// @param wRay ray represented in world space. + /// @param world if an intersection was found it is assigned the + /// intersection point in world space, otherwise it is unchanged. + /// @param normal if an intersection was found it is assigned the normal + /// of the level set surface in world space, otherwise it is unchanged. + bool intersectsWS(const RayType& wRay, Vec3Type& world, Vec3Type& normal) const + { + if (!mTester.setWorldRay(wRay)) return false;//missed bbox + if (!math::LevelSetHDDA::test(mTester)) return false;//missed level set + mTester.getWorldPosAndNml(world, normal); + return true; + } + + /// @brief Return @c true if the world-space ray intersects the level set. + /// @param wRay ray represented in world space. + /// @param world if an intersection was found it is assigned the + /// intersection point in world space, otherwise it is unchanged. + /// @param normal if an intersection was found it is assigned the normal + /// of the level set surface in world space, otherwise it is unchanged. + /// @param wTime if an intersection was found it is assigned the time of the + /// intersection along the world ray. + bool intersectsWS(const RayType& wRay, Vec3Type& world, Vec3Type& normal, RealType &wTime) const + { + if (!mTester.setWorldRay(wRay)) return false;//missed bbox + if (!math::LevelSetHDDA::test(mTester)) return false;//missed level set + mTester.getWorldPosAndNml(world, normal); + wTime = mTester.getWorldTime(); + return true; + } + +private: + + mutable SearchImplT mTester; + +};// LevelSetRayIntersector + + +////////////////////////////////////// VolumeRayIntersector ////////////////////////////////////// + + +/// @brief This class provides the public API for intersecting a ray +/// with a generic (e.g. density) volume. +/// @details Internally it performs the actual hierarchical tree node traversal. +/// @warning Use the (default) copy-constructor to make sure each +/// computational thread has their own instance of this class. This is +/// important since it contains a ValueAccessor that is +/// not thread-safe and a CoordBBox of the active voxels that should +/// not be re-computed for each thread. However copying is very efficient. +/// @par Example: +/// @code +/// // Create an instance for the master thread +/// VolumeRayIntersector inter(grid); +/// // For each additional thread use the copy constructor. This +/// // amortizes the overhead of computing the bbox of the active voxels! +/// VolumeRayIntersector inter2(inter); +/// // Before each ray-traversal set the index ray. +/// iter.setIndexRay(ray); +/// // or world ray +/// iter.setWorldRay(ray); +/// // Now you can begin the ray-marching using consecutive calls to VolumeRayIntersector::march +/// double t0=0, t1=0;// note the entry and exit times are with respect to the INDEX ray +/// while ( inter.march(t0, t1) ) { +/// // perform line-integration between t0 and t1 +/// }} +/// @endcode +template > +class VolumeRayIntersector +{ +public: + typedef GridT GridType; + typedef RayT RayType; + typedef typename RayT::RealType RealType; + typedef typename GridT::TreeType::RootNodeType RootType; + typedef tree::Tree::Type> TreeT; + + BOOST_STATIC_ASSERT( NodeLevel >= 0 && NodeLevel < int(TreeT::DEPTH)-1); + + /// @brief Grid constructor + /// @param grid Generic grid to intersect rays against. + /// @param dilationCount The number of voxel dilations performed + /// on (a boolean copy of) the input grid. This allows the + /// intersector to account for the size of interpolation kernels + /// in client code. + /// @throw RuntimeError if the voxels of the grid are not uniform + /// or the grid is empty. + VolumeRayIntersector(const GridT& grid, int dilationCount = 0) + : mIsMaster(true) + , mTree(new TreeT(grid.tree(), false, TopologyCopy())) + , mGrid(&grid) + , mAccessor(*mTree) + { + if (!grid.hasUniformVoxels() ) { + OPENVDB_THROW(RuntimeError, + "VolumeRayIntersector only supports uniform voxels!"); + } + if ( grid.empty() ) { + OPENVDB_THROW(RuntimeError, "LinearSearchImpl does not supports empty grids"); + } + + // Dilate active voxels to better account for the size of interpolation kernels + tools::dilateVoxels(*mTree, dilationCount); + + mTree->root().evalActiveBoundingBox(mBBox, /*visit individual voxels*/false); + + mBBox.max().offset(1);//padding so the bbox of a node becomes (origin,origin + node_dim) + } + + /// @brief Grid and BBox constructor + /// @param grid Generic grid to intersect rays against. + /// @param bbox The axis-aligned bounding-box in the index space of the grid. + /// @warning It is assumed that bbox = (min, min + dim) where min denotes + /// to the smallest grid coordinates and dim are the integer length of the bbox. + /// @throw RuntimeError if the voxels of the grid are not uniform + /// or the grid is empty. + VolumeRayIntersector(const GridT& grid, const math::CoordBBox& bbox) + : mIsMaster(true) + , mTree(new TreeT(grid.tree(), false, TopologyCopy())) + , mGrid(&grid) + , mAccessor(*mTree) + , mBBox(bbox) + { + if (!grid.hasUniformVoxels() ) { + OPENVDB_THROW(RuntimeError, + "VolumeRayIntersector only supports uniform voxels!"); + } + if ( grid.empty() ) { + OPENVDB_THROW(RuntimeError, "LinearSearchImpl does not supports empty grids"); + } + } + + /// @brief Shallow copy constructor + /// @warning This copy constructor creates shallow copies of data + /// members of the instance passed as the argument. For + /// performance reasons we are not using shared pointers (their + /// mutex-lock impairs multi-threading). + VolumeRayIntersector(const VolumeRayIntersector& other) + : mIsMaster(false) + , mTree(other.mTree)//shallow copy + , mGrid(other.mGrid)//shallow copy + , mAccessor(*mTree)//initialize new (vs deep copy) + , mRay(other.mRay)//deep copy + , mTmax(other.mTmax)//deep copy + , mBBox(other.mBBox)//deep copy + { + } + + /// @brief Destructor + ~VolumeRayIntersector() { if (mIsMaster) delete mTree; } + + /// @brief Return @c false if the index ray misses the bbox of the grid. + /// @param iRay Ray represented in index space. + /// @warning Call this method (or setWorldRay) before the ray + /// traversal starts and use the return value to decide if further + /// marching is required. + inline bool setIndexRay(const RayT& iRay) + { + mRay = iRay; + const bool hit = mRay.clip(mBBox); + if (hit) mTmax = mRay.t1(); + return hit; + } + + /// @brief Return @c false if the world ray misses the bbox of the grid. + /// @param wRay Ray represented in world space. + /// @warning Call this method (or setIndexRay) before the ray + /// traversal starts and use the return value to decide if further + /// marching is required. + /// @details Since hit times are computed with respect to the ray + /// represented in index space of the current grid, it is + /// recommended that either the client code uses getIndexPos to + /// compute index position from hit times or alternatively keeps + /// an instance of the index ray and instead uses setIndexRay to + /// initialize the ray. + inline bool setWorldRay(const RayT& wRay) + { + return this->setIndexRay(wRay.worldToIndex(*mGrid)); + } + + inline typename RayT::TimeSpan march() + { + const typename RayT::TimeSpan t = mHDDA.march(mRay, mAccessor); + if (t.t1>0) mRay.setTimes(t.t1 + math::Delta::value(), mTmax); + return t; + } + + /// @brief Return @c true if the ray intersects active values, + /// i.e. either active voxels or tiles. Only when a hit is + /// detected are t0 and t1 updated with the corresponding entry + /// and exit times along the INDEX ray! + /// @note Note that t0 and t1 are only resolved at the node level + /// (e.g. a LeafNode with active voxels) as opposed to the individual + /// active voxels. + /// @param t0 If the return value > 0 this is the time of the + /// first hit of an active tile or leaf. + /// @param t1 If the return value > t0 this is the time of the + /// first hit (> t0) of an inactive tile or exit point of the + /// BBOX for the leaf nodes. + /// @warning t0 and t1 are computed with respect to the ray represented in + /// index space of the current grid, not world space! + inline bool march(RealType& t0, RealType& t1) + { + const typename RayT::TimeSpan t = this->march(); + t.get(t0, t1); + return t.valid(); + } + + /// @brief Generates a list of hits along the ray. + /// + /// @param list List of hits represented as time spans. + /// + /// @note ListType is a list of RayType::TimeSpan and is required to + /// have the two methods: clear() and push_back(). Thus, it could + /// be std::vector or + /// std::deque. + template + inline void hits(ListType& list) + { + mHDDA.hits(mRay, mAccessor, list); + } + + /// @brief Return the floating-point index position along the + /// current index ray at the specified time. + inline Vec3R getIndexPos(RealType time) const { return mRay(time); } + + /// @brief Return the floating-point world position along the + /// current index ray at the specified time. + inline Vec3R getWorldPos(RealType time) const { return mGrid->indexToWorld(mRay(time)); } + + inline RealType getWorldTime(RealType time) const + { + return time*mGrid->transform().baseMap()->applyJacobian(mRay.dir()).length(); + } + + /// @brief Return a const reference to the input grid. + const GridT& grid() const { return *mGrid; } + + /// @brief Return a const reference to the (potentially dilated) + /// bool tree used to accelerate the ray marching. + const TreeT& tree() const { return *mTree; } + + /// @brief Return a const reference to the BBOX of the grid + const math::CoordBBox& bbox() const { return mBBox; } + + /// @brief Print bbox, statistics, memory usage and other information. + /// @param os a stream to which to write textual information + /// @param verboseLevel 1: print bbox only; 2: include boolean tree + /// statistics; 3: include memory usage + void print(std::ostream& os = std::cout, int verboseLevel = 1) + { + if (verboseLevel>0) { + os << "BBox: " << mBBox << std::endl; + if (verboseLevel==2) { + mTree->print(os, 1); + } else if (verboseLevel>2) { + mTree->print(os, 2); + } + } + } + +private: + + typedef typename tree::ValueAccessor AccessorT; + + const bool mIsMaster; + TreeT* mTree; + const GridT* mGrid; + AccessorT mAccessor; + RayT mRay; + RealType mTmax; + math::CoordBBox mBBox; + math::VolumeHDDA mHDDA; + +};// VolumeRayIntersector + + +//////////////////////////////////////// LinearSearchImpl //////////////////////////////////////// + + +/// @brief Implements linear iterative search for an iso-value of +/// the level set along the direction of the ray. +/// +/// @note Since this class is used internally in +/// LevelSetRayIntersector (define above) and LevelSetHDDA (defined below) +/// client code should never interact directly with its API. This also +/// explains why we are not concerned with the fact that several of +/// its methods are unsafe to call unless roots were already detected. +/// +/// @details It is approximate due to the limited number of iterations +/// which can can be defined with a template parameter. However the default value +/// has proven surprisingly accurate and fast. In fact more iterations +/// are not guaranteed to give significantly better results. +/// +/// @warning Since the root-searching algorithm is approximate +/// (first-order) it is possible to miss intersections if the +/// iso-value is too close to the inside or outside of the narrow +/// band (typically a distance less than a voxel unit). +/// +/// @warning Since this class internally stores a ValueAccessor it is NOT thread-safe, +/// so make sure to give each thread its own instance. This of course also means that +/// the cost of allocating an instance should (if possible) be amortized over +/// as many ray intersections as possible. +template +class LinearSearchImpl +{ +public: + typedef math::Ray RayT; + typedef typename GridT::ValueType ValueT; + typedef typename GridT::ConstAccessor AccessorT; + typedef math::BoxStencil StencilT; + + /// @brief Constructor from a grid. + /// @throw RunTimeError if the grid is empty. + /// @throw ValueError if the isoValue is not inside the narrow-band. + LinearSearchImpl(const GridT& grid, const ValueT& isoValue = zeroVal()) + : mStencil(grid), + mIsoValue(isoValue), + mMinValue(isoValue - ValueT(2 * grid.voxelSize()[0])), + mMaxValue(isoValue + ValueT(2 * grid.voxelSize()[0])) + { + if ( grid.empty() ) { + OPENVDB_THROW(RuntimeError, "LinearSearchImpl does not supports empty grids"); + } + if (mIsoValue<= -grid.background() || + mIsoValue>= grid.background() ){ + OPENVDB_THROW(ValueError, "The iso-value must be inside the narrow-band!"); + } + grid.tree().root().evalActiveBoundingBox(mBBox, /*visit individual voxels*/false); + } + + /// @brief Return the iso-value used for ray-intersections + const ValueT& getIsoValue() const { return mIsoValue; } + + /// @brief Return @c false if the ray misses the bbox of the grid. + /// @param iRay Ray represented in index space. + /// @warning Call this method before the ray traversal starts. + inline bool setIndexRay(const RayT& iRay) + { + mRay = iRay; + return mRay.clip(mBBox);//did it hit the bbox + } + + /// @brief Return @c false if the ray misses the bbox of the grid. + /// @param wRay Ray represented in world space. + /// @warning Call this method before the ray traversal starts. + inline bool setWorldRay(const RayT& wRay) + { + mRay = wRay.worldToIndex(mStencil.grid()); + return mRay.clip(mBBox);//did it hit the bbox + } + + /// @brief Get the intersection point in index space. + /// @param xyz The position in index space of the intersection. + inline void getIndexPos(Vec3d& xyz) const { xyz = mRay(mTime); } + + /// @brief Get the intersection point in world space. + /// @param xyz The position in world space of the intersection. + inline void getWorldPos(Vec3d& xyz) const { xyz = mStencil.grid().indexToWorld(mRay(mTime)); } + + /// @brief Get the intersection point and normal in world space + /// @param xyz The position in world space of the intersection. + /// @param nml The surface normal in world space of the intersection. + inline void getWorldPosAndNml(Vec3d& xyz, Vec3d& nml) + { + this->getIndexPos(xyz); + mStencil.moveTo(xyz); + nml = mStencil.gradient(xyz); + nml.normalize(); + xyz = mStencil.grid().indexToWorld(xyz); + } + + /// @brief Return the time of intersection along the index ray. + inline RealT getIndexTime() const { return mTime; } + + /// @brief Return the time of intersection along the world ray. + inline RealT getWorldTime() const + { + return mTime*mStencil.grid().transform().baseMap()->applyJacobian(mRay.dir()).length(); + } + +private: + + /// @brief Initiate the local voxel intersection test. + /// @warning Make sure to call this method before the local voxel intersection test. + inline void init(RealT t0) + { + mT[0] = t0; + mV[0] = static_cast(this->interpValue(t0)); + } + + inline void setRange(RealT t0, RealT t1) { mRay.setTimes(t0, t1); } + + /// @brief Return a const reference to the ray. + inline const RayT& ray() const { return mRay; } + + /// @brief Return true if a node of the specified type exists at ijk. + template + inline bool hasNode(const Coord& ijk) + { + return mStencil.accessor().template probeConstNode(ijk) != NULL; + } + + /// @brief Return @c true if an intersection is detected. + /// @param ijk Grid coordinate of the node origin or voxel being tested. + /// @param time Time along the index ray being tested. + /// @warning Only if an intersection is detected is it safe to + /// call getIndexPos, getWorldPos and getWorldPosAndNml! + inline bool operator()(const Coord& ijk, RealT time) + { + ValueT V; + if (mStencil.accessor().probeValue(ijk, V) &&//within narrow band + V>mMinValue && V(this->interpValue(time)); + if (math::ZeroCrossing(mV[0], mV[1])) { + mTime = this->interpTime(); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + for (int n=0; Iterations>0 && n(this->interpValue(mTime)); + const int m = math::ZeroCrossing(mV[0], V) ? 1 : 0; + mV[m] = V; + mT[m] = mTime; + mTime = this->interpTime(); + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + return true; + } + mT[0] = mT[1]; + mV[0] = mV[1]; + } + return false; + } + + inline RealT interpTime() + { + assert(math::isApproxLarger(mT[1], mT[0], 1e-6)); + return mT[0]+(mT[1]-mT[0])*mV[0]/(mV[0]-mV[1]); + } + + inline RealT interpValue(RealT time) + { + const Vec3R pos = mRay(time); + mStencil.moveTo(pos); + return mStencil.interpolation(pos) - mIsoValue; + } + + template friend struct math::LevelSetHDDA; + + RayT mRay; + StencilT mStencil; + RealT mTime;//time of intersection + ValueT mV[2]; + RealT mT[2]; + const ValueT mIsoValue, mMinValue, mMaxValue; + math::CoordBBox mBBox; +};// LinearSearchImpl + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_RAYINTERSECTOR_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/RayTracer.h b/nuparu/include/openvdb/include/openvdb/tools/RayTracer.h new file mode 100644 index 00000000..ac035e57 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/RayTracer.h @@ -0,0 +1,1102 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 RayTracer.h +/// +/// @author Ken Museth +/// +/// @brief Defines two simple but multithreaded renders, a level-set +/// ray tracer and a volume render. To support these renders we also define +/// perspective and orthographic cameras (both designed to mimic a Houdini camera), +/// a Film class and some rather naive shaders. +/// +/// @note These classes are included mainly as reference implementations for +/// ray-tracing of OpenVDB volumes. In other words they are not intended for +/// production-quality rendering, but could be used for fast pre-visualization +/// or as a starting point for a more serious render. + +#ifndef OPENVDB_TOOLS_RAYTRACER_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_RAYTRACER_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef OPENVDB_TOOLS_RAYTRACER_USE_EXR +#include +#include +#include +#include +#include +#endif + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +// Forward declarations +class BaseCamera; +class BaseShader; + +/// @brief Ray-trace a volume. +template +inline void rayTrace(const GridT&, + const BaseShader&, + BaseCamera&, + size_t pixelSamples = 1, + unsigned int seed = 0, + bool threaded = true); + +/// @brief Ray-trace a volume using a given ray intersector. +template +inline void rayTrace(const GridT&, + const IntersectorT&, + const BaseShader&, + BaseCamera&, + size_t pixelSamples = 1, + unsigned int seed = 0, + bool threaded = true); + + +///////////////////////////////LEVEL SET RAY TRACER /////////////////////////////////////// + +/// @brief A (very) simple multithreaded ray tracer specifically for narrow-band level sets. +/// @details Included primarily as a reference implementation. +template > +class LevelSetRayTracer +{ +public: + typedef GridT GridType; + typedef typename IntersectorT::Vec3Type Vec3Type; + typedef typename IntersectorT::RayType RayType; + + /// @brief Constructor based on an instance of the grid to be rendered. + LevelSetRayTracer(const GridT& grid, + const BaseShader& shader, + BaseCamera& camera, + size_t pixelSamples = 1, + unsigned int seed = 0); + + /// @brief Constructor based on an instance of the intersector + /// performing the ray-intersections. + LevelSetRayTracer(const IntersectorT& inter, + const BaseShader& shader, + BaseCamera& camera, + size_t pixelSamples = 1, + unsigned int seed = 0); + + /// @brief Copy constructor + LevelSetRayTracer(const LevelSetRayTracer& other); + + /// @brief Destructor + ~LevelSetRayTracer(); + + /// @brief Set the level set grid to be ray-traced + void setGrid(const GridT& grid); + + /// @brief Set the intersector that performs the actual + /// intersection of the rays against the narrow-band level set. + void setIntersector(const IntersectorT& inter); + + /// @brief Set the shader derived from the abstract BaseShader class. + /// + /// @note The shader is not assumed to be thread-safe so each + /// thread will get its only deep copy. For instance it could + /// contains a ValueAccessor into another grid with auxiliary + /// shading information. Thus, make sure it is relatively + /// light-weight and efficient to copy (which is the case for ValueAccesors). + void setShader(const BaseShader& shader); + + /// @brief Set the camera derived from the abstract BaseCamera class. + void setCamera(BaseCamera& camera); + + /// @brief Set the number of pixel samples and the seed for + /// jittered sub-rays. A value larger than one implies + /// anti-aliasing by jittered super-sampling. + /// @throw ValueError if pixelSamples is equal to zero. + void setPixelSamples(size_t pixelSamples, unsigned int seed = 0); + + /// @brief Perform the actual (potentially multithreaded) ray-tracing. + void render(bool threaded = true) const; + + /// @brief Public method required by tbb::parallel_for. + /// @warning Never call it directly. + void operator()(const tbb::blocked_range& range) const; + +private: + const bool mIsMaster; + double* mRand; + IntersectorT mInter; + boost::scoped_ptr mShader; + BaseCamera* mCamera; + size_t mSubPixels; +};// LevelSetRayTracer + + +///////////////////////////////VOLUME RENDER /////////////////////////////////////// + +/// @brief A (very) simple multithreaded volume render specifically for scalar density. +/// @details Included primarily as a reference implementation. +/// @note It will only compile if the IntersectorT is templated on a Grid with a +/// floating-point voxel type. +template +class VolumeRender +{ +public: + + typedef typename IntersectorT::GridType GridType; + typedef typename IntersectorT::RayType RayType; + typedef typename GridType::ValueType ValueType; + typedef typename GridType::ConstAccessor AccessorType; + typedef tools::GridSampler SamplerType; + BOOST_STATIC_ASSERT(boost::is_floating_point::value); + + /// @brief Constructor taking an intersector and a base camera. + VolumeRender(const IntersectorT& inter, BaseCamera& camera); + + /// @brief Copy constructor which creates a thread-safe clone + VolumeRender(const VolumeRender& other); + + /// @brief Perform the actual (potentially multithreaded) volume rendering. + void render(bool threaded=true) const; + + /// @brief Set the camera derived from the abstract BaseCamera class. + void setCamera(BaseCamera& camera) { mCamera = &camera; } + + /// @brief Set the intersector that performs the actual + /// intersection of the rays against the volume. + void setIntersector(const IntersectorT& inter); + + /// @brief Set the vector components of a directional light source + /// @throw ArithmeticError if input is a null vector. + void setLightDir(Real x, Real y, Real z) { mLightDir = Vec3R(x,y,z).unit(); } + + /// @brief Set the color of the directional light source. + void setLightColor(Real r, Real g, Real b) { mLightColor = Vec3R(r,g,b); } + + /// @brief Set the integration step-size in voxel units for the primay ray. + void setPrimaryStep(Real primaryStep) { mPrimaryStep = primaryStep; } + + /// @brief Set the integration step-size in voxel units for the primay ray. + void setShadowStep(Real shadowStep) { mShadowStep = shadowStep; } + + /// @brief Set Scattering coefficients. + void setScattering(Real x, Real y, Real z) { mScattering = Vec3R(x,y,z); } + + /// @brief Set absorption coefficients. + void setAbsorption(Real x, Real y, Real z) { mAbsorption = Vec3R(x,y,z); } + + /// @brief Set parameter that imitates multi-scattering. A value + /// of zero implies no multi-scattering. + void setLightGain(Real gain) { mLightGain = gain; } + + /// @brief Set the cut-off value for density and transmittance. + void setCutOff(Real cutOff) { mCutOff = cutOff; } + + /// @brief Print parameters, statistics, memory usage and other information. + /// @param os a stream to which to write textual information + /// @param verboseLevel 1: print parameters only; 2: include grid + /// statistics; 3: include memory usage + void print(std::ostream& os = std::cout, int verboseLevel = 1); + + /// @brief Public method required by tbb::parallel_for. + /// @warning Never call it directly. + void operator()(const tbb::blocked_range& range) const; + +private: + + AccessorType mAccessor; + BaseCamera* mCamera; + boost::scoped_ptr mPrimary, mShadow; + Real mPrimaryStep, mShadowStep, mCutOff, mLightGain; + Vec3R mLightDir, mLightColor, mAbsorption, mScattering; +};//VolumeRender + +//////////////////////////////////////// FILM //////////////////////////////////////// + +/// @brief A simple class that allows for concurrent writes to pixels in an image, +/// background initialization of the image, and PPM or EXR file output. +class Film +{ +public: + /// @brief Floating-point RGBA components in the range [0, 1]. + /// @details This is our preferred representation for color processing. + struct RGBA + { + typedef float ValueT; + + RGBA() : r(0), g(0), b(0), a(1) {} + explicit RGBA(ValueT intensity) : r(intensity), g(intensity), b(intensity), a(1) {} + RGBA(ValueT _r, ValueT _g, ValueT _b, ValueT _a = static_cast(1.0)): + r(_r), g(_g), b(_b), a(_a) + {} + + RGBA operator* (ValueT scale) const { return RGBA(r*scale, g*scale, b*scale);} + RGBA operator+ (const RGBA& rhs) const { return RGBA(r+rhs.r, g+rhs.g, b+rhs.b);} + RGBA operator* (const RGBA& rhs) const { return RGBA(r*rhs.r, g*rhs.g, b*rhs.b);} + RGBA& operator+=(const RGBA& rhs) { r+=rhs.r; g+=rhs.g; b+=rhs.b, a+=rhs.a; return *this;} + + void over(const RGBA& rhs) + { + const float s = rhs.a*(1.0f-a); + r = a*r+s*rhs.r; + g = a*g+s*rhs.g; + b = a*b+s*rhs.b; + a = a + s; + } + + ValueT r, g, b, a; + }; + + + Film(size_t width, size_t height) + : mWidth(width), mHeight(height), mSize(width*height), mPixels(new RGBA[mSize]) + { + } + Film(size_t width, size_t height, const RGBA& bg) + : mWidth(width), mHeight(height), mSize(width*height), mPixels(new RGBA[mSize]) + { + this->fill(bg); + } + + const RGBA& pixel(size_t w, size_t h) const + { + assert(w < mWidth); + assert(h < mHeight); + return mPixels[w + h*mWidth]; + } + + RGBA& pixel(size_t w, size_t h) + { + assert(w < mWidth); + assert(h < mHeight); + return mPixels[w + h*mWidth]; + } + + void fill(const RGBA& rgb=RGBA(0)) { for (size_t i=0; i buffer(new unsigned char[3*mSize]); + unsigned char *tmp = buffer.get(), *q = tmp; + RGBA* p = mPixels.get(); + size_t n = mSize; + while (n--) { + *q++ = static_cast(255.0f*(*p ).r); + *q++ = static_cast(255.0f*(*p ).g); + *q++ = static_cast(255.0f*(*p++).b); + } + + std::ofstream os(name.c_str(), std::ios_base::binary); + if (!os.is_open()) { + std::cerr << "Error opening PPM file \"" << name << "\"" << std::endl; + return; + } + + os << "P6\n" << mWidth << " " << mHeight << "\n255\n"; + os.write((const char *)&(*tmp), 3*mSize*sizeof(unsigned char)); + } + +#ifdef OPENVDB_TOOLS_RAYTRACER_USE_EXR + void saveEXR(const std::string& fileName, size_t compression = 2, size_t threads = 8) + { + std::string name(fileName); + if (name.find_last_of(".") == std::string::npos) name.append(".exr"); + + if (threads>0) Imf::setGlobalThreadCount(threads); + Imf::Header header(mWidth, mHeight); + if (compression==0) header.compression() = Imf::NO_COMPRESSION; + if (compression==1) header.compression() = Imf::RLE_COMPRESSION; + if (compression>=2) header.compression() = Imf::ZIP_COMPRESSION; + header.channels().insert("R", Imf::Channel(Imf::FLOAT)); + header.channels().insert("G", Imf::Channel(Imf::FLOAT)); + header.channels().insert("B", Imf::Channel(Imf::FLOAT)); + header.channels().insert("A", Imf::Channel(Imf::FLOAT)); + + Imf::FrameBuffer framebuffer; + framebuffer.insert("R", Imf::Slice( Imf::FLOAT, (char *) &(mPixels[0].r), + sizeof (RGBA), sizeof (RGBA) * mWidth)); + framebuffer.insert("G", Imf::Slice( Imf::FLOAT, (char *) &(mPixels[0].g), + sizeof (RGBA), sizeof (RGBA) * mWidth)); + framebuffer.insert("B", Imf::Slice( Imf::FLOAT, (char *) &(mPixels[0].b), + sizeof (RGBA), sizeof (RGBA) * mWidth)); + framebuffer.insert("A", Imf::Slice( Imf::FLOAT, (char *) &(mPixels[0].a), + sizeof (RGBA), sizeof (RGBA) * mWidth)); + + Imf::OutputFile file(name.c_str(), header); + file.setFrameBuffer(framebuffer); + file.writePixels(mHeight); + } +#endif + + size_t width() const { return mWidth; } + size_t height() const { return mHeight; } + size_t numPixels() const { return mSize; } + const RGBA* pixels() const { return mPixels.get(); } + +private: + size_t mWidth, mHeight, mSize; + boost::scoped_array mPixels; +};// Film + + +//////////////////////////////////////// CAMERAS //////////////////////////////////////// + +/// Abstract base class for the perspective and orthographic cameras +class BaseCamera +{ +public: + BaseCamera(Film& film, const Vec3R& rotation, const Vec3R& translation, + double frameWidth, double nearPlane, double farPlane) + : mFilm(&film) + , mScaleWidth(frameWidth) + , mScaleHeight(frameWidth * double(film.height()) / double(film.width())) + { + assert(nearPlane > 0 && farPlane > nearPlane); + mScreenToWorld.accumPostRotation(math::X_AXIS, rotation[0] * M_PI / 180.0); + mScreenToWorld.accumPostRotation(math::Y_AXIS, rotation[1] * M_PI / 180.0); + mScreenToWorld.accumPostRotation(math::Z_AXIS, rotation[2] * M_PI / 180.0); + mScreenToWorld.accumPostTranslation(translation); + this->initRay(nearPlane, farPlane); + } + + virtual ~BaseCamera() {} + + Film::RGBA& pixel(size_t i, size_t j) { return mFilm->pixel(i, j); } + + size_t width() const { return mFilm->width(); } + size_t height() const { return mFilm->height(); } + + /// Rotate the camera so its negative z-axis points at xyz and its + /// y axis is in the plane of the xyz and up vectors. In other + /// words the camera will look at xyz and use up as the + /// horizontal direction. + void lookAt(const Vec3R& xyz, const Vec3R& up = Vec3R(0.0, 1.0, 0.0)) + { + const Vec3R orig = mScreenToWorld.applyMap(Vec3R(0.0)); + const Vec3R dir = orig - xyz; + try { + Mat4d xform = math::aim(dir, up); + xform.postTranslate(orig); + mScreenToWorld = math::AffineMap(xform); + this->initRay(mRay.t0(), mRay.t1()); + } catch (...) {} + } + + Vec3R rasterToScreen(double i, double j, double z) const + { + return Vec3R( (2 * i / double(mFilm->width()) - 1) * mScaleWidth, + (1 - 2 * j / double(mFilm->height())) * mScaleHeight, z ); + } + + /// @brief Return a Ray in world space given the pixel indices and + /// optional offsets in the range [0, 1]. An offset of 0.5 corresponds + /// to the center of the pixel. + virtual math::Ray getRay( + size_t i, size_t j, double iOffset = 0.5, double jOffset = 0.5) const = 0; + +protected: + void initRay(double t0, double t1) + { + mRay.setTimes(t0, t1); + mRay.setEye(mScreenToWorld.applyMap(Vec3R(0.0))); + mRay.setDir(mScreenToWorld.applyJacobian(Vec3R(0.0, 0.0, -1.0))); + } + + Film* mFilm; + double mScaleWidth, mScaleHeight; + math::Ray mRay; + math::AffineMap mScreenToWorld; +};// BaseCamera + + +class PerspectiveCamera: public BaseCamera +{ + public: + /// @brief Constructor + /// @param film film (i.e. image) defining the pixel resolution + /// @param rotation rotation in degrees of the camera in world space + /// (applied in x, y, z order) + /// @param translation translation of the camera in world-space units, + /// applied after rotation + /// @param focalLength focal length of the camera in mm + /// (the default of 50mm corresponds to Houdini's default camera) + /// @param aperture width in mm of the frame, i.e., the visible field + /// (the default 41.2136 mm corresponds to Houdini's default camera) + /// @param nearPlane depth of the near clipping plane in world-space units + /// @param farPlane depth of the far clipping plane in world-space units + /// + /// @details If no rotation or translation is provided, the camera is placed + /// at (0,0,0) in world space and points in the direction of the negative z axis. + PerspectiveCamera(Film& film, + const Vec3R& rotation = Vec3R(0.0), + const Vec3R& translation = Vec3R(0.0), + double focalLength = 50.0, + double aperture = 41.2136, + double nearPlane = 1e-3, + double farPlane = std::numeric_limits::max()) + : BaseCamera(film, rotation, translation, 0.5*aperture/focalLength, nearPlane, farPlane) + { + } + + virtual ~PerspectiveCamera() {} + + /// @brief Return a Ray in world space given the pixel indices and + /// optional offsets in the range [0,1]. An offset of 0.5 corresponds + /// to the center of the pixel. + virtual math::Ray getRay( + size_t i, size_t j, double iOffset = 0.5, double jOffset = 0.5) const + { + math::Ray ray(mRay); + Vec3R dir = BaseCamera::rasterToScreen(Real(i) + iOffset, Real(j) + jOffset, -1.0); + dir = BaseCamera::mScreenToWorld.applyJacobian(dir); + dir.normalize(); + ray.scaleTimes(1.0/dir.dot(ray.dir())); + ray.setDir(dir); + return ray; + } + + /// @brief Return the horizontal field of view in degrees given a + /// focal lenth in mm and the specified aperture in mm. + static double focalLengthToFieldOfView(double length, double aperture) + { + return 360.0 / M_PI * atan(aperture/(2.0*length)); + } + /// @brief Return the focal length in mm given a horizontal field of + /// view in degrees and the specified aperture in mm. + static double fieldOfViewToFocalLength(double fov, double aperture) + { + return aperture/(2.0*(tan(fov * M_PI / 360.0))); + } +};// PerspectiveCamera + + +class OrthographicCamera: public BaseCamera +{ +public: + /// @brief Constructor + /// @param film film (i.e. image) defining the pixel resolution + /// @param rotation rotation in degrees of the camera in world space + /// (applied in x, y, z order) + /// @param translation translation of the camera in world-space units, + /// applied after rotation + /// @param frameWidth width in of the frame in world-space units + /// @param nearPlane depth of the near clipping plane in world-space units + /// @param farPlane depth of the far clipping plane in world-space units + /// + /// @details If no rotation or translation is provided, the camera is placed + /// at (0,0,0) in world space and points in the direction of the negative z axis. + OrthographicCamera(Film& film, + const Vec3R& rotation = Vec3R(0.0), + const Vec3R& translation = Vec3R(0.0), + double frameWidth = 1.0, + double nearPlane = 1e-3, + double farPlane = std::numeric_limits::max()) + : BaseCamera(film, rotation, translation, 0.5*frameWidth, nearPlane, farPlane) + { + } + virtual ~OrthographicCamera() {} + + virtual math::Ray getRay( + size_t i, size_t j, double iOffset = 0.5, double jOffset = 0.5) const + { + math::Ray ray(mRay); + Vec3R eye = BaseCamera::rasterToScreen(Real(i) + iOffset, Real(j) + jOffset, 0.0); + ray.setEye(BaseCamera::mScreenToWorld.applyMap(eye)); + return ray; + } +};// OrthographicCamera + + +//////////////////////////////////////// SHADERS //////////////////////////////////////// + + +/// Abstract base class for the shaders +class BaseShader +{ +public: + typedef math::Ray RayT; + BaseShader() {} + virtual ~BaseShader() {} + /// @brief Defines the interface of the virtual function that returns a RGB color. + /// @param xyz World position of the intersection point. + /// @param nml Normal in world space at the intersection point. + /// @param dir Direction of the ray in world space. + virtual Film::RGBA operator()(const Vec3R& xyz, const Vec3R& nml, const Vec3R& dir) const = 0; + virtual BaseShader* copy() const = 0; +}; + + +/// @brief Shader that produces a simple matte. +/// +/// @details The color can either be constant (if GridT = +/// Film::RGBA which is the default) or defined in a separate Vec3 +/// color grid. Use SamplerType to define the order of interpolation +/// (default is zero order, i.e. closes-point). +template +class MatteShader: public BaseShader +{ +public: + MatteShader(const GridT& grid) : mAcc(grid.getAccessor()), mXform(&grid.transform()) {} + virtual ~MatteShader() {} + virtual Film::RGBA operator()(const Vec3R& xyz, const Vec3R&, const Vec3R&) const + { + typename GridT::ValueType v = zeroVal(); + SamplerType::sample(mAcc, mXform->worldToIndex(xyz), v); + return Film::RGBA( + static_cast(v[0]), + static_cast(v[1]), + static_cast(v[2])); + } + virtual BaseShader* copy() const { return new MatteShader(*this); } + +private: + typename GridT::ConstAccessor mAcc; + const math::Transform* mXform; +}; +// Template specialization using a constant color of the material. +template +class MatteShader: public BaseShader +{ +public: + MatteShader(const Film::RGBA& c = Film::RGBA(1.0f)): mRGBA(c) {} + virtual ~MatteShader() {} + virtual Film::RGBA operator()(const Vec3R&, const Vec3R&, const Vec3R&) const + { + return mRGBA; + } + virtual BaseShader* copy() const { return new MatteShader(*this); } + +private: + const Film::RGBA mRGBA; +}; + + +/// @brief Color shader that treats the surface normal (x, y, z) as an +/// RGB color. +/// +/// @details The color can either be constant (if GridT = +/// Film::RGBA which is the default) or defined in a separate Vec3 +/// color grid. Use SamplerType to define the order of interpolation +/// (default is zero order, i.e. closes-point). +template +class NormalShader: public BaseShader +{ +public: + NormalShader(const GridT& grid) : mAcc(grid.getAccessor()), mXform(&grid.transform()) {} + virtual ~NormalShader() {} + virtual Film::RGBA operator()(const Vec3R& xyz, const Vec3R& normal, const Vec3R&) const + { + typename GridT::ValueType v = zeroVal(); + SamplerType::sample(mAcc, mXform->worldToIndex(xyz), v); + return Film::RGBA(v[0]*(normal[0]+1.0f), v[1]*(normal[1]+1.0f), v[2]*(normal[2]+1.0f)); + } + virtual BaseShader* copy() const { return new NormalShader(*this); } + +private: + typename GridT::ConstAccessor mAcc; + const math::Transform* mXform; +}; +// Template specialization using a constant color of the material. +template +class NormalShader: public BaseShader +{ +public: + NormalShader(const Film::RGBA& c = Film::RGBA(1.0f)) : mRGBA(c*0.5f) {} + virtual ~NormalShader() {} + virtual Film::RGBA operator()(const Vec3R&, const Vec3R& normal, const Vec3R&) const + { + return mRGBA*Film::RGBA(normal[0]+1.0f, normal[1]+1.0f, normal[2]+1.0f); + } + virtual BaseShader* copy() const { return new NormalShader(*this); } + +private: + const Film::RGBA mRGBA; +}; + + +/// @brief Color shader that treats position (x, y, z) as an RGB color in a +/// cube defined from an axis-aligned bounding box in world space. +/// +/// @details The color can either be constant (if GridT = +/// Film::RGBA which is the default) or defined in a separate Vec3 +/// color grid. Use SamplerType to define the order of interpolation +/// (default is zero order, i.e. closes-point). +template +class PositionShader: public BaseShader +{ +public: + PositionShader(const math::BBox& bbox, const GridT& grid) + : mMin(bbox.min()) + , mInvDim(1.0/bbox.extents()) + , mAcc(grid.getAccessor()) + , mXform(&grid.transform()) + { + } + virtual ~PositionShader() {} + virtual Film::RGBA operator()(const Vec3R& xyz, const Vec3R&, const Vec3R&) const + { + typename GridT::ValueType v = zeroVal(); + SamplerType::sample(mAcc, mXform->worldToIndex(xyz), v); + const Vec3R rgb = (xyz - mMin)*mInvDim; + return Film::RGBA(v[0],v[1],v[2]) * Film::RGBA(rgb[0], rgb[1], rgb[2]); + } + virtual BaseShader* copy() const { return new PositionShader(*this); } + +private: + const Vec3R mMin, mInvDim; + typename GridT::ConstAccessor mAcc; + const math::Transform* mXform; +}; +// Template specialization using a constant color of the material. +template +class PositionShader: public BaseShader +{ +public: + PositionShader(const math::BBox& bbox, const Film::RGBA& c = Film::RGBA(1.0f)) + : mMin(bbox.min()), mInvDim(1.0/bbox.extents()), mRGBA(c) {} + virtual ~PositionShader() {} + virtual Film::RGBA operator()(const Vec3R& xyz, const Vec3R&, const Vec3R&) const + { + const Vec3R rgb = (xyz - mMin)*mInvDim; + return mRGBA*Film::RGBA(rgb[0], rgb[1], rgb[2]); + } + virtual BaseShader* copy() const { return new PositionShader(*this); } + +private: + const Vec3R mMin, mInvDim; + const Film::RGBA mRGBA; +}; + +/// @brief Simple diffuse Lambertian surface shader. +/// +/// @details The diffuse color can either be constant (if GridT = +/// Film::RGBA which is the default) or defined in a separate Vec3 +/// color grid. Lambertian implies that the (radiant) intensity is +/// directly proportional to the cosine of the angle between the +/// surface normal and the direction of the light source. Use +/// SamplerType to define the order of interpolation (default is +/// zero order, i.e. closes-point). +template +class DiffuseShader: public BaseShader +{ +public: + DiffuseShader(const GridT& grid): mAcc(grid.getAccessor()), mXform(&grid.transform()) {} + virtual ~DiffuseShader() {} + virtual Film::RGBA operator()(const Vec3R& xyz, const Vec3R& normal, const Vec3R& rayDir) const + { + typename GridT::ValueType v = zeroVal(); + SamplerType::sample(mAcc, mXform->worldToIndex(xyz), v); + // We take the abs of the dot product corresponding to having + // light sources at +/- rayDir, i.e., two-sided shading. + return Film::RGBA(v[0],v[1],v[2]) * math::Abs(normal.dot(rayDir)); + } + virtual BaseShader* copy() const { return new DiffuseShader(*this); } + +private: + typename GridT::ConstAccessor mAcc; + const math::Transform* mXform; +}; +// Template specialization using a constant color of the material. +template +class DiffuseShader: public BaseShader +{ +public: + DiffuseShader(const Film::RGBA& d = Film::RGBA(1.0f)): mRGBA(d) {} + virtual ~DiffuseShader() {} + virtual Film::RGBA operator()(const Vec3R&, const Vec3R& normal, const Vec3R& rayDir) const + { + // We assume a single directional light source at the camera, + // so the cosine of the angle between the surface normal and the + // direction of the light source becomes the dot product of the + // surface normal and inverse direction of the ray. We also ignore + // negative dot products, corresponding to strict one-sided shading. + //return mRGBA * math::Max(0.0, normal.dot(-rayDir)); + + // We take the abs of the dot product corresponding to having + // light sources at +/- rayDir, i.e., two-sided shading. + return mRGBA * math::Abs(normal.dot(rayDir)); + } + virtual BaseShader* copy() const { return new DiffuseShader(*this); } + +private: + const Film::RGBA mRGBA; +}; + +//////////////////////////////////////// RAYTRACER //////////////////////////////////////// + +template +inline void rayTrace(const GridT& grid, + const BaseShader& shader, + BaseCamera& camera, + size_t pixelSamples, + unsigned int seed, + bool threaded) +{ + LevelSetRayTracer > + tracer(grid, shader, camera, pixelSamples, seed); + tracer.render(threaded); +} + + +template +inline void rayTrace(const GridT&, + const IntersectorT& inter, + const BaseShader& shader, + BaseCamera& camera, + size_t pixelSamples, + unsigned int seed, + bool threaded) +{ + LevelSetRayTracer tracer(inter, shader, camera, pixelSamples, seed); + tracer.render(threaded); +} + + +//////////////////////////////////////// LevelSetRayTracer //////////////////////////////////////// + + +template +inline LevelSetRayTracer:: +LevelSetRayTracer(const GridT& grid, + const BaseShader& shader, + BaseCamera& camera, + size_t pixelSamples, + unsigned int seed) + : mIsMaster(true), + mRand(NULL), + mInter(grid), + mShader(shader.copy()), + mCamera(&camera) +{ + this->setPixelSamples(pixelSamples, seed); +} + +template +inline LevelSetRayTracer:: +LevelSetRayTracer(const IntersectorT& inter, + const BaseShader& shader, + BaseCamera& camera, + size_t pixelSamples, + unsigned int seed) + : mIsMaster(true), + mRand(NULL), + mInter(inter), + mShader(shader.copy()), + mCamera(&camera) +{ + this->setPixelSamples(pixelSamples, seed); +} + +template +inline LevelSetRayTracer:: +LevelSetRayTracer(const LevelSetRayTracer& other) : + mIsMaster(false), + mRand(other.mRand), + mInter(other.mInter), + mShader(other.mShader->copy()), + mCamera(other.mCamera), + mSubPixels(other.mSubPixels) +{ +} + +template +inline LevelSetRayTracer:: +~LevelSetRayTracer() +{ + if (mIsMaster) delete [] mRand; +} + +template +inline void LevelSetRayTracer:: +setGrid(const GridT& grid) +{ + assert(mIsMaster); + mInter = IntersectorT(grid); +} + +template +inline void LevelSetRayTracer:: +setIntersector(const IntersectorT& inter) +{ + assert(mIsMaster); + mInter = inter; +} + +template +inline void LevelSetRayTracer:: +setShader(const BaseShader& shader) +{ + assert(mIsMaster); + mShader.reset(shader.copy()); +} + +template +inline void LevelSetRayTracer:: +setCamera(BaseCamera& camera) +{ + assert(mIsMaster); + mCamera = &camera; +} + +template +inline void LevelSetRayTracer:: +setPixelSamples(size_t pixelSamples, unsigned int seed) +{ + assert(mIsMaster); + if (pixelSamples == 0) { + OPENVDB_THROW(ValueError, "pixelSamples must be larger than zero!"); + } + mSubPixels = pixelSamples - 1; + delete [] mRand; + if (mSubPixels > 0) { + mRand = new double[16]; + math::Rand01 rand(seed);//offsets for anti-aliaing by jittered super-sampling + for (size_t i=0; i<16; ++i) mRand[i] = rand(); + } else { + mRand = NULL; + } +} + +template +inline void LevelSetRayTracer:: +render(bool threaded) const +{ + tbb::blocked_range range(0, mCamera->height()); + threaded ? tbb::parallel_for(range, *this) : (*this)(range); +} + +template +inline void LevelSetRayTracer:: +operator()(const tbb::blocked_range& range) const +{ + const BaseShader& shader = *mShader; + Vec3Type xyz, nml; + const float frac = 1.0f / (1.0f + mSubPixels); + for (size_t j=range.begin(), n=0, je = range.end(); jwidth(); ipixel(i,j); + RayType ray = mCamera->getRay(i, j);//primary ray + Film::RGBA c = mInter.intersectsWS(ray, xyz, nml) ? shader(xyz, nml, ray.dir()) : bg; + for (size_t k=0; kgetRay(i, j, mRand[n & 15], mRand[(n+1) & 15]); + c += mInter.intersectsWS(ray, xyz, nml) ? shader(xyz, nml, ray.dir()) : bg; + }//loop over sub-pixels + bg = c*frac; + }//loop over image height + }//loop over image width +} + +//////////////////////////////////////// VolumeRender //////////////////////////////////////// + +template +inline VolumeRender:: +VolumeRender(const IntersectorT& inter, BaseCamera& camera) + : mAccessor(inter.grid().getConstAccessor()) + , mCamera(&camera) + , mPrimary(new IntersectorT(inter)) + , mShadow(new IntersectorT(inter)) + , mPrimaryStep(1.0) + , mShadowStep(3.0) + , mCutOff(0.005) + , mLightGain(0.2) + , mLightDir(Vec3R(0.3, 0.3, 0).unit()) + , mLightColor(0.7, 0.7, 0.7) + , mAbsorption(0.1) + , mScattering(1.5) +{ +} + +template +inline VolumeRender:: +VolumeRender(const VolumeRender& other) + : mAccessor(other.mAccessor) + , mCamera(other.mCamera) + , mPrimary(new IntersectorT(*(other.mPrimary))) + , mShadow(new IntersectorT(*(other.mShadow))) + , mPrimaryStep(other.mPrimaryStep) + , mShadowStep(other.mShadowStep) + , mCutOff(other.mCutOff) + , mLightGain(other.mLightGain) + , mLightDir(other.mLightDir) + , mLightColor(other.mLightColor) + , mAbsorption(other.mAbsorption) + , mScattering(other.mScattering) +{ +} + +template +inline void VolumeRender:: +print(std::ostream& os, int verboseLevel) +{ + if (verboseLevel>0) { + os << "\nPrimary step: " << mPrimaryStep + << "\nShadow step: " << mShadowStep + << "\nCutoff: " << mCutOff + << "\nLightGain: " << mLightGain + << "\nLightDir: " << mLightDir + << "\nLightColor: " << mLightColor + << "\nAbsorption: " << mAbsorption + << "\nScattering: " << mScattering << std::endl; + } + mPrimary->print(os, verboseLevel); +} + +template +inline void VolumeRender:: +setIntersector(const IntersectorT& inter) +{ + mPrimary.reset(new IntersectorT(inter)); + mShadow.reset( new IntersectorT(inter)); +} + +template +inline void VolumeRender:: +render(bool threaded) const +{ + tbb::blocked_range range(0, mCamera->height()); + threaded ? tbb::parallel_for(range, *this) : (*this)(range); +} + +template +inline void VolumeRender:: +operator()(const tbb::blocked_range& range) const +{ + SamplerType sampler(mAccessor, mShadow->grid().transform());//light-weight wrapper + + // Any variable prefixed with p (or s) means it's associated with a primary (or shadow) ray + const Vec3R extinction = -mScattering-mAbsorption, One(1.0); + const Vec3R albedo = mLightColor*mScattering/(mScattering+mAbsorption);//single scattering + const Real sGain = mLightGain;//in-scattering along shadow ray + const Real pStep = mPrimaryStep;//Integration step along primary ray in voxel units + const Real sStep = mShadowStep;//Integration step along shadow ray in voxel units + const Real cutoff = mCutOff;//Cutoff for density and transmittance + + // For the sake of completeness we show how to use two different + // methods (hits/march) in VolumeRayIntersector that produce + // segments along the ray that intersects active values. Comment out + // the line below to use VolumeRayIntersector::march instead of + // VolumeRayIntersector::hits. +#define USE_HITS +#ifdef USE_HITS + std::vector pTS, sTS; + //std::deque pTS, sTS; +#endif + + RayType sRay(Vec3R(0), mLightDir);//Shadow ray + for (size_t j=range.begin(), je = range.end(); jwidth(); ipixel(i, j); + bg.a = bg.r = bg.g = bg.b = 0; + RayType pRay = mCamera->getRay(i, j);// Primary ray + if( !mPrimary->setWorldRay(pRay)) continue; + Vec3R pTrans(1.0), pLumi(0.0); +#ifndef USE_HITS + Real pT0, pT1; + while (mPrimary->march(pT0, pT1)) { + for (Real pT = pStep*ceil(pT0/pStep); pT <= pT1; pT += pStep) { +#else + mPrimary->hits(pTS); + for (size_t k=0; kgetWorldPos(pT); + const Real density = sampler.wsSample(pPos); + if (density < cutoff) continue; + const Vec3R dT = math::Exp(extinction * density * pStep); + Vec3R sTrans(1.0); + sRay.setEye(pPos); + if( !mShadow->setWorldRay(sRay)) continue; +#ifndef USE_HITS + Real sT0, sT1; + while (mShadow->march(sT0, sT1)) { + for (Real sT = sStep*ceil(sT0/sStep); sT <= sT1; sT+= sStep) { +#else + mShadow->hits(sTS); + for (size_t l=0; lgetWorldPos(sT)); + if (d < cutoff) continue; + sTrans *= math::Exp(extinction * d * sStep/(1.0+sT*sGain)); + if (sTrans.lengthSqr()(pLumi[0]); + bg.g = static_cast(pLumi[1]); + bg.b = static_cast(pLumi[2]); + bg.a = static_cast(1.0f - pTrans.sum()/3.0f); + }//Horizontal pixel scan + }//Vertical pixel scan +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_RAYTRACER_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/SignedFloodFill.h b/nuparu/include/openvdb/include/openvdb/tools/SignedFloodFill.h new file mode 100644 index 00000000..d75ba1d9 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/SignedFloodFill.h @@ -0,0 +1,311 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 SignedFloodFill.h +/// +/// @brief Propagates the sign of distance values from the active +/// voxels in the narrow band to the inactive values outside the +/// narrow band. +/// +/// @author Ken Museth + +#ifndef OPENVDB_TOOLS_SIGNEDFLOODFILL_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_SIGNEDFLOODFILL_HAS_BEEN_INCLUDED + +#include +#include // for math::negative +#include // for Index typedef +#include +#include +#include + +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @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. +/// +/// @warning This method should only be used on closed, symmetric narrow-band level sets. +/// +/// @note If a LeafManager is used the cached leaf nodes are reused, +/// resulting in slightly better overall performance. +/// +/// @param tree Tree or LeafManager that will be flood filled. +/// @param threaded enable or disable threading (threading is enabled by default) +/// @param grainSize used to control the threading granularity (default is 1) +/// @param minLevel Specify the lowest tree level to process (leafnode level = 0) +/// +/// @throw TypeError if the ValueType of @a tree is not floating-point. +template +inline void +signedFloodFill(TreeOrLeafManagerT& tree, bool threaded = true, + size_t grainSize = 1, Index minLevel = 0); + + +/// @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 outsideWidth and interior values to @a insideWidth. Set the background value +/// of this tree to @a outsideWidth. +/// +/// @warning This method should only be used on closed, narrow-band level sets. +/// +/// @note If a LeafManager is used the cached leaf nodes are reused +/// resulting in slightly better overall performance. +/// +/// @param tree Tree or LeafManager that will be flood filled +/// @param outsideWidth the width of the outside of the narrow band +/// @param insideWidth the width of the inside of the narrow band +/// @param threaded enable or disable threading (threading is enabled by default) +/// @param grainSize used to control the threading granularity (default is 1) +/// @param minLevel Specify the lowest tree level to process (leafnode level = 0) +/// +/// @throw TypeError if the ValueType of @a tree is not floating-point. +template +inline void +signedFloodFillWithValues( + TreeOrLeafManagerT& tree, + const typename TreeOrLeafManagerT::ValueType& outsideWidth, + const typename TreeOrLeafManagerT::ValueType& insideWidth, + bool threaded = true, + size_t grainSize = 1, + Index minLevel = 0); + + +////////////////////////// Implementation of SignedFloodFill //////////////////////////// + + +template +class SignedFloodFillOp +{ +public: + typedef typename TreeOrLeafManagerT::ValueType ValueT; + typedef typename TreeOrLeafManagerT::RootNodeType RootT; + typedef typename TreeOrLeafManagerT::LeafNodeType LeafT; + BOOST_STATIC_ASSERT(boost::is_floating_point::value || boost::is_signed::value); + + SignedFloodFillOp(const TreeOrLeafManagerT& tree, Index minLevel = 0) + : mOutside(ValueT(math::Abs(tree.background()))) + , mInside(ValueT(math::negative(mOutside))) + , mMinLevel(minLevel) + { + } + + SignedFloodFillOp(ValueT outsideValue, ValueT insideValue, Index minLevel = 0) + : mOutside(ValueT(math::Abs(outsideValue))) + , mInside(ValueT(math::negative(math::Abs(insideValue)))) + , mMinLevel(minLevel) + { + } + + // Nothing to do at the leaf node level + void operator()(LeafT& leaf) const + { + if (LeafT::LEVEL < mMinLevel) return; + +#ifndef OPENVDB_2_ABI_COMPATIBLE + if (!leaf.allocate()) return;//this assures that the buffer is allocated and in-memory +#endif + const typename LeafT::NodeMaskType& valueMask = leaf.getValueMask(); + // WARNING: "Never do what you're about to see at home, we're what you call experts!" + typename LeafT::ValueType* buffer = + const_cast(&(leaf.getFirstValue())); + + const Index first = valueMask.findFirstOn(); + if (first < LeafT::SIZE) { + bool xInside = buffer[first]<0, yInside = xInside, zInside = xInside; + for (Index x = 0; x != (1 << LeafT::LOG2DIM); ++x) { + const Index x00 = x << (2 * LeafT::LOG2DIM); + if (valueMask.isOn(x00)) xInside = buffer[x00] < 0; // element(x, 0, 0) + yInside = xInside; + for (Index y = 0; y != (1 << LeafT::LOG2DIM); ++y) { + const Index xy0 = x00 + (y << LeafT::LOG2DIM); + if (valueMask.isOn(xy0)) yInside = buffer[xy0] < 0; // element(x, y, 0) + zInside = yInside; + for (Index z = 0; z != (1 << LeafT::LOG2DIM); ++z) { + const Index xyz = xy0 + z; // element(x, y, z) + if (valueMask.isOn(xyz)) { + zInside = buffer[xyz] < 0; + } else { + buffer[xyz] = zInside ? mInside : mOutside; + } + } + } + } + } else {// if no active voxels exist simply use the sign of the first value + leaf.fill(buffer[0] < 0 ? mInside : mOutside); + } + } + + // Prune the child nodes of the internal nodes + template + void operator()(NodeT& node) const + { + if (NodeT::LEVEL < mMinLevel) return; + // We assume the child nodes have already been flood filled! + const typename NodeT::NodeMaskType& childMask = node.getChildMask(); + // WARNING: "Never do what you're about to see at home, we're what you call experts!" + typename NodeT::UnionType* table = const_cast(node.getTable()); + + const Index first = childMask.findFirstOn(); + if (first < NodeT::NUM_VALUES) { + bool xInside = table[first].getChild()->getFirstValue()<0; + bool yInside = xInside, zInside = xInside; + for (Index x = 0; x != (1 << NodeT::LOG2DIM); ++x) { + const int x00 = x << (2 * NodeT::LOG2DIM); // offset for block(x, 0, 0) + if (childMask.isOn(x00)) xInside = table[x00].getChild()->getLastValue()<0; + yInside = xInside; + for (Index y = 0; y != (1 << NodeT::LOG2DIM); ++y) { + const Index xy0 = x00 + (y << NodeT::LOG2DIM); // offset for block(x, y, 0) + if (childMask.isOn(xy0)) yInside = table[xy0].getChild()->getLastValue()<0; + zInside = yInside; + for (Index z = 0; z != (1 << NodeT::LOG2DIM); ++z) { + const Index xyz = xy0 + z; // offset for block(x, y, z) + if (childMask.isOn(xyz)) { + zInside = table[xyz].getChild()->getLastValue()<0; + } else { + table[xyz].setValue(zInside ? mInside : mOutside); + } + } + } + } + } else {//no child nodes exist simply use the sign of the first tile value. + const ValueT v = table[0].getValue()<0 ? mInside : mOutside; + for (Index i = 0; i < NodeT::NUM_VALUES; ++i) table[i].setValue(v); + } + } + + // Prune the child nodes of the root node + void operator()(RootT& root) const + { + if (RootT::LEVEL < mMinLevel) return; + typedef typename RootT::ChildNodeType ChildT; + // Insert the child nodes into a map sorted according to their origin + std::map nodeKeys; + typename RootT::ChildOnIter it = root.beginChildOn(); + for (; it; ++it) nodeKeys.insert(std::pair(it.getCoord(), &(*it))); + static const Index DIM = RootT::ChildNodeType::DIM; + + // We employ a simple z-scanline algorithm that inserts inactive tiles with + // the inside value if they are sandwiched between inside child nodes only! + typename std::map::const_iterator b = nodeKeys.begin(), e = nodeKeys.end(); + if ( b == e ) return; + for (typename std::map::const_iterator a = b++; b != e; ++a, ++b) { + Coord d = b->first - a->first; // delta of neighboring coordinates + if (d[0]!=0 || d[1]!=0 || d[2]==Int32(DIM)) continue;// not same z-scanline or neighbors + const ValueT fill[] = { a->second->getLastValue(), b->second->getFirstValue() }; + if (!(fill[0] < 0) || !(fill[1] < 0)) continue; // scanline isn't inside + Coord c = a->first + Coord(0u, 0u, DIM); + for (; c[2] != b->first[2]; c[2] += DIM) root.addTile(c, mInside, false); + } + root.setBackground(mOutside, /*updateChildNodes=*/false); + } + +private: + const ValueT mOutside, mInside; + const Index mMinLevel; +};// SignedFloodFillOp + + +template +inline +typename boost::enable_if_c< + boost::is_floating_point::value || + boost::is_signed::value, void>::type +doSignedFloodFill(TreeOrLeafManagerT& tree, + typename TreeOrLeafManagerT::ValueType outsideValue, + typename TreeOrLeafManagerT::ValueType insideValue, + bool threaded, + size_t grainSize, + Index minLevel) +{ + tree::NodeManager nodes(tree); + SignedFloodFillOp op(outsideValue, insideValue, minLevel); + nodes.foreachBottomUp(op, threaded, grainSize); +} + +// Dummy (no-op) implementation for non-float types +template +inline +typename boost::disable_if_c< + boost::is_floating_point::value || + boost::is_signed::value, void>::type +doSignedFloodFill(TreeOrLeafManagerT&, + const typename TreeOrLeafManagerT::ValueType&, + const typename TreeOrLeafManagerT::ValueType&, + bool, + size_t, + Index) +{ + OPENVDB_THROW(TypeError, + "signedFloodFill is supported only for signed value grids"); +} + + +// If the narrow-band is symmetric and unchanged +template +inline void +signedFloodFillWithValues( + TreeOrLeafManagerT& tree, + const typename TreeOrLeafManagerT::ValueType& outsideValue, + const typename TreeOrLeafManagerT::ValueType& insideValue, + bool threaded, + size_t grainSize, + Index minLevel) +{ + doSignedFloodFill(tree, outsideValue, insideValue, threaded, grainSize, minLevel); +} + + +template +inline void +signedFloodFill(TreeOrLeafManagerT& tree, + bool threaded, + size_t grainSize, + Index minLevel) +{ + const typename TreeOrLeafManagerT::ValueType v = tree.root().background(); + doSignedFloodFill(tree, v, math::negative(v), threaded, grainSize, minLevel); +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_RESETBACKGROUND_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/Statistics.h b/nuparu/include/openvdb/include/openvdb/tools/Statistics.h new file mode 100644 index 00000000..f979c4af --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/Statistics.h @@ -0,0 +1,438 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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, extremas +/// (min/max) and statistics (mean, variance, etc.) of grid values + +#ifndef OPENVDB_TOOLS_STATISTICS_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_STATISTICS_HAS_BEEN_INCLUDED + +#include +#include +#include +#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 +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 extrema (min/max) of the +/// values of the voxels that are visited, or iterate over a vector-valued grid +/// and compute extrema 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 +inline math::Extrema +extrema(const IterT& iter, 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 +inline math::Stats +statistics(const IterT& iter, bool threaded = true); + +/// @brief Iterate over a grid and compute extrema (min/max) 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 void op(const IterT&, math::Stats&), +/// 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::Extrema& ex) +/// { +/// const float f = *iter; +/// if (f > 0.0) { +/// if (iter.isVoxelValue()) ex.add(f); +/// else ex.add(f, iter.getVoxelCount()); +/// } +/// } +/// }; +/// FloatGrid grid = ...; +/// math::Extrema stats = +/// tools::extrema(grid.cbeginValueOn(), Local::addIfPositive, /*threaded=*/true); +/// @endcode +template +inline math::Extrema +extrema(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 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 void op(const IterT&, math::Stats&), +/// 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 +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 +/// double result(Accessor&, const Coord&) +/// @param threaded if true, iterate over the grid in parallel +/// @note World-space operators, whose @c result() methods are of the form +/// double result(const Map&, Accessor&, const Coord&), 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 GradientOp; +/// // Wrap the operator with an adapter that computes the magnitude of the gradient. +/// typedef math::OpMagnitude MagnitudeOp; +/// // Wrap the operator with an adapter that associates a map with it. +/// typedef math::MapAdapter CompoundOp; +/// +/// if (MapType::Ptr map = grid.constTransform().constMap()) { +/// 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 DivergenceOp; +/// // Wrap the operator with an adapter that associates a map with it. +/// typedef math::MapAdapter CompoundOp; +/// +/// if (MapType::Ptr map = grid.constTransform().constMap()) { +/// 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 DivergenceOp; +/// +/// math::Stats stats = tools::opStatistics(grid.cbeginValueOn(), DivergenceOp()); +/// @endcode +template +inline math::Stats +opStatistics(const IterT& iter, const OperatorT& op = OperatorT(), bool threaded = true); + +/// @brief Same as opStatistics except it returns a math::Extrema vs a math::Stats +template +inline math::Extrema +opExtrema(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 +struct IterTraits { + typedef typename IterT::ValueType ValueType; +}; + +template +struct IterTraits > { + typedef typename tree::TreeValueIteratorBase::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 struct GetValImpl; + +template +struct GetValImpl { + static inline double get(const T& val) { return double(val); } +}; + +template +struct GetValImpl { + 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 +struct GetVal +{ + typedef typename IterTraits::ValueType ValueT; + typedef GetValImpl::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 +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); } + + StatsT stats; + ValueOp getValue; +}; + + +// Helper class to accumulate scalar voxel values or vector voxel magnitudes +// into a math::Histogram object +template +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 +struct MathOp +{ + typedef typename IterT::TreeT TreeT; + typedef typename TreeT::ValueType ValueT; + typedef typename tree::ValueAccessor ConstAccessor; + + // Each thread gets its own accessor and its own copy of the operator. + ConstAccessor mAcc; + OpT mOp; + StatsT mStats; + + template + 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 +inline math::Histogram +histogram(const IterT& iter, double vmin, double vmax, size_t numBins, bool threaded) +{ + typedef stats_internal::GetVal ValueOp; + ValueOp valOp; + stats_internal::HistOp op(valOp, vmin, vmax, numBins); + tools::accumulate(iter, op, threaded); + return op.hist; +} + +template +inline math::Extrema +extrema(const IterT& iter, bool threaded) +{ + stats_internal::GetVal valOp; + return extrema(iter, valOp, threaded); +} + +template +inline math::Stats +statistics(const IterT& iter, bool threaded) +{ + stats_internal::GetVal valOp; + return statistics(iter, valOp, threaded); +} + +template +inline math::Extrema +extrema(const IterT& iter, const ValueOp& valOp, bool threaded) +{ + stats_internal::StatsOp op(valOp); + tools::accumulate(iter, op, threaded); + return op.stats; +} + +template +inline math::Stats +statistics(const IterT& iter, const ValueOp& valOp, bool threaded) +{ + stats_internal::StatsOp op(valOp); + tools::accumulate(iter, op, threaded); + return op.stats; +} + + +template +inline math::Extrema +opExtrema(const IterT& iter, const OperatorT& op, bool threaded) +{ + stats_internal::MathOp func(iter, op); + tools::accumulate(iter, func, threaded); + return func.mStats; +} + +template +inline math::Stats +opStatistics(const IterT& iter, const OperatorT& op, bool threaded) +{ + stats_internal::MathOp 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-2016 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/nuparu/include/openvdb/include/openvdb/tools/TopologyToLevelSet.h b/nuparu/include/openvdb/include/openvdb/tools/TopologyToLevelSet.h new file mode 100644 index 00000000..1ae83f7a --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/TopologyToLevelSet.h @@ -0,0 +1,284 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 TopologyToLevelSet.h +/// +/// @brief This tool converts active grid topology to a into a signed +/// distance field encoded as a narrow band level set. +/// +/// @details The boundary between active and inactive voxels is treated +/// as the zero crossing for the level set. +/// +/// @par Example: +/// Combine with @c tools::createPointMaskGrid for fast point cloud to level set conversion. +/// +/// @author D.J. Hill + +#ifndef OPENVDB_TOOLS_TOPOLOGY_TO_LEVELSET_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_TOPOLOGY_TO_LEVELSET_HAS_BEEN_INCLUDED + +#include "LevelSetFilter.h" +#include "Morphology.h" // for {dilate|erode}Voxels +#include "Prune.h"// for pruneInactive +#include "SignedFloodFill.h" // for signedFloodFill + +#include +#include +#include // for math::BiasedGradientScheme +#include + +#include +#include + +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + + +/// @brief Compute the narrow-band signed distance to the boundary +/// between active and inactive voxels in the input grid. +/// +/// @return A shared pointer to a new signed distance field of type @c float +/// +/// @param grid Input grid of arbitrary type whose active voxels are used +/// in constructing the level set. +/// @param halfBandWidth Half the width of the narrow band, in voxel units +/// @param closingWidth Number of iterations used to first expand and then shrink +/// the filled voxel region. +/// This causes holes and valleys to be filled. +/// @param dilation Number of iterations used to expand the filled voxel region. +/// @param smoothingSteps Number of smoothing interations +template +inline typename GridType::template ValueConverter::Type::Ptr +topologyToLevelSet(const GridType& grid, int halfBandWidth = 3, int closingWidth = 1, + int dilation = 0, int smoothingSteps = 0); + + +/// @brief Compute the narrow-band signed distance to the boundary +/// between active and inactive voxels in the input grid. +/// +/// @return A shared pointer to a new signed distance field of type @c float +/// +/// @param grid Input grid of arbitrary type whose active voxels are used +/// in constructing the level set. +/// @param halfBandWidth Half the width of the narrow band, in voxel units +/// @param closingWidth Number of iterations used to first expand and then shrink +/// the filled voxel region. +/// This causes holes and valleys to be filled. +/// @param dilation Number of iterations used to expand the filled voxel region. +/// @param smoothingSteps Number of smoothing interations +/// @param interrupt Optional object adhering to the util::NullInterrupter interface. +template +inline typename GridType::template ValueConverter::Type::Ptr +topologyToLevelSet(const GridType& grid, int halfBandWidth = 3, int closingWidth = 1, + int dilation = 0, int smoothingSteps = 0, InterrupterType* interrupt = NULL); + + +//////////////////////////////////////// +//////////////////////////////////////// + +// Implementation details + + +namespace ttls_internal { + + +template +struct OffsetAndMinComp +{ + typedef typename TreeType::LeafNodeType LeafNodeType; + typedef typename TreeType::ValueType ValueType; + + OffsetAndMinComp(std::vector& lhsNodes, const TreeType& rhsTree, ValueType offset) + : mLhsNodes(lhsNodes.empty() ? NULL : &lhsNodes[0]), mRhsTree(&rhsTree), mOffset(offset) + { + } + + void operator()(const tbb::blocked_range& range) const + { + typedef typename LeafNodeType::ValueOnIter Iterator; + + tree::ValueAccessor rhsAcc(*mRhsTree); + const ValueType offset = mOffset; + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + + LeafNodeType& lhsNode = *mLhsNodes[n]; + const LeafNodeType * rhsNodePt = rhsAcc.probeConstLeaf(lhsNode.origin()); + if (!rhsNodePt) continue; + + for (Iterator it = lhsNode.beginValueOn(); it; ++it) { + ValueType& val = const_cast(it.getValue()); + val = std::min(val, offset + rhsNodePt->getValue(it.pos())); + } + } + } + +private: + LeafNodeType * * const mLhsNodes; + TreeType const * const mRhsTree; + ValueType const mOffset; +}; // struct OffsetAndMinComp + + +template +inline void +normalizeLevelSet(GridType& grid, const int halfWidthInVoxels, InterrupterType* interrupt = NULL) +{ + LevelSetFilter filter(grid, interrupt); + filter.setSpatialScheme(math::FIRST_BIAS); + filter.setNormCount(halfWidthInVoxels); + filter.normalize(); + filter.prune(); +} + + +template +inline void +smoothLevelSet(GridType& grid, int iterations, int halfBandWidthInVoxels, InterrupterType* interrupt = NULL) +{ + typedef typename GridType::ValueType ValueType; + typedef typename GridType::TreeType TreeType; + typedef typename TreeType::LeafNodeType LeafNodeType; + + GridType filterGrid(grid); + + LevelSetFilter filter(filterGrid, interrupt); + filter.setSpatialScheme(math::FIRST_BIAS); + + for (int n = 0; n < iterations; ++n) { + if (interrupt && interrupt->wasInterrupted()) break; + filter.mean(1); + } + + std::vector nodes; + grid.tree().getNodes(nodes); + + const ValueType offset = ValueType(double(0.5) * grid.transform().voxelSize()[0]); + + tbb::parallel_for(tbb::blocked_range(0, nodes.size()), + OffsetAndMinComp(nodes, filterGrid.tree(), -offset)); + + // Clean up any damanage that was done by the min operation + normalizeLevelSet(grid, halfBandWidthInVoxels, interrupt); +} + + +} // namespace ttls_internal + + +//////////////////////////////////////// + + +template +inline typename GridType::template ValueConverter::Type::Ptr +topologyToLevelSet(const GridType& grid, int halfBandWidth, int closingWidth, + int dilation, int smoothingSteps, InterrupterType* interrupt) +{ + typedef typename GridType::template ValueConverter::Type FloatGridType; + typedef typename FloatGridType::TreeType FloatTreeType; + typedef typename FloatTreeType::template ValueConverter::Type MaskTreeType; + + halfBandWidth = std::max(halfBandWidth, 1); + closingWidth = std::max(closingWidth, 0); + + MaskTreeType regionMask(grid.tree(), false, TopologyCopy()); + + // closing operation and padding + openvdb::tools::dilateVoxels(regionMask, closingWidth + dilation); + openvdb::tools::erodeVoxels(regionMask, closingWidth); + + + // Construct inside band mask + + MaskTreeType coreMask(regionMask); + openvdb::tools::erodeVoxels(coreMask, halfBandWidth); + + regionMask.topologyDifference(coreMask); + tools::pruneInactive(regionMask, /*threading=*/true); + + // Generate a volume with an implicit zero crossing at the boundary + // between active and inactive values in the input grid. + + const float width = float(grid.transform().voxelSize()[0] * double(halfBandWidth)); + + typename FloatTreeType::Ptr resultTree( + new FloatTreeType(regionMask, /*inactive=*/width, /*active=*/-width, openvdb::TopologyCopy())); + + // Construct outside band mask + openvdb::tools::dilateVoxels(regionMask, halfBandWidth); + regionMask.topologyDifference(coreMask); + tools::pruneInactive(regionMask, /*threading=*/true); + + // Activate outside band + resultTree->topologyUnion(regionMask); + + // Update interior sign + tools::signedFloodFill(*resultTree); + + // Embed the tree in a grid to define a transform and voxel size. + typename FloatGridType::Ptr resultGrid = FloatGridType::create(resultTree); + resultGrid->setTransform(grid.transform().copy()); + resultGrid->setGridClass(GRID_LEVEL_SET); + + // Use a PDE based scheme to propagate distance values from the + // implicit zero crossing. + ttls_internal::normalizeLevelSet(*resultGrid, 3*halfBandWidth, interrupt); + + // Optinal smooting operation + if (smoothingSteps > 0) { + ttls_internal::smoothLevelSet(*resultGrid, smoothingSteps, halfBandWidth, interrupt); + } + + return resultGrid; +} + + +template +inline typename GridType::template ValueConverter::Type::Ptr +topologyToLevelSet(const GridType& grid, int halfBandWidth, int closingWidth, int dilation, int smoothingSteps) +{ + util::NullInterrupter interrupt; + return topologyToLevelSet(grid, halfBandWidth, closingWidth, dilation, smoothingSteps, &interrupt); +} + + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif //OPENVDB_TOOLS_DENSESPARSETOOLS_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/ValueTransformer.h b/nuparu/include/openvdb/include/openvdb/tools/ValueTransformer.h new file mode 100644 index 00000000..7f51cd8b --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/ValueTransformer.h @@ -0,0 +1,707 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +/// +/// @author Peter Cucka +/// +/// 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. +/// +/// Finally, tools::setValueOnMin(), tools::setValueOnMax(), tools::setValueOnSum() +/// and tools::setValueOnMult() are wrappers around Tree::modifyValue() (or +/// ValueAccessor::modifyValue()) for some commmon in-place operations. +/// These are typically significantly faster than calling getValue() followed by setValue(). + +#ifndef OPENVDB_TOOLS_VALUETRANSFORMER_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_VALUETRANSFORMER_HAS_BEEN_INCLUDED + +#include // for std::min(), std::max() +#include +#include +#include +#include + + +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 void op(const IterT&), 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::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 +inline void foreach(const IterT& iter, XformOp& op, + bool threaded = true, bool shareOp = true); + +template +inline void foreach(const IterT& iter, const XformOp& op, + bool threaded = true, bool shareOp = true); + + +/// Iterate over a grid and at each step call op(iter, accessor) 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-const 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 +/// void op(const InIterT&, OutGridT::ValueAccessor&), +/// 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 +/// @param merge how to merge intermediate results from multiple threads (see Types.h) +/// +/// @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 +inline void transformValues(const InIterT& inIter, OutGridT& outGrid, + XformOp& op, bool threaded = true, bool shareOp = true, + MergePolicy merge = MERGE_ACTIVE_STATES); + +#ifndef _MSC_VER +template +inline void transformValues(const InIterT& inIter, OutGridT& outGrid, + const XformOp& op, bool threaded = true, bool shareOp = true, + MergePolicy merge = MERGE_ACTIVE_STATES); +#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 void join(XformOp&) +/// and a call method of the form void op(const IterT&), +/// 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 +inline void accumulate(const IterT& iter, XformOp& op, bool threaded = true); + + +/// @brief Set the value of the voxel at the given coordinates in @a tree to +/// the minimum of its current value and @a value, and mark the voxel as active. +/// @details This is typically significantly faster than calling getValue() +/// followed by setValueOn(). +/// @note @a TreeT can be either a Tree or a ValueAccessor. +template +inline void setValueOnMin(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value); + +/// @brief Set the value of the voxel at the given coordinates in @a tree to +/// the maximum of its current value and @a value, and mark the voxel as active. +/// @details This is typically significantly faster than calling getValue() +/// followed by setValueOn(). +/// @note @a TreeT can be either a Tree or a ValueAccessor. +template +inline void setValueOnMax(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value); + +/// @brief Set the value of the voxel at the given coordinates in @a tree to +/// the sum of its current value and @a value, and mark the voxel as active. +/// @details This is typically significantly faster than calling getValue() +/// followed by setValueOn(). +/// @note @a TreeT can be either a Tree or a ValueAccessor. +template +inline void setValueOnSum(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value); + +/// @brief Set the value of the voxel at the given coordinates in @a tree to +/// the product of its current value and @a value, and mark the voxel as active. +/// @details This is typically significantly faster than calling getValue() +/// followed by setValueOn(). +/// @note @a TreeT can be either a Tree or a ValueAccessor. +template +inline void setValueOnMult(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value); + + +//////////////////////////////////////// + + +namespace valxform { + +template +struct MinOp { + const ValueType val; + MinOp(const ValueType& v): val(v) {} + inline void operator()(ValueType& v) const { v = std::min(v, val); } +}; + +template +struct MaxOp { + const ValueType val; + MaxOp(const ValueType& v): val(v) {} + inline void operator()(ValueType& v) const { v = std::max(v, val); } +}; + +template +struct SumOp { + const ValueType val; + SumOp(const ValueType& v): val(v) {} + inline void operator()(ValueType& v) const { v += val; } +}; + +template +struct MultOp { + const ValueType val; + MultOp(const ValueType& v): val(v) {} + inline void operator()(ValueType& v) const { v *= val; } +}; + +} + + +template +inline void +setValueOnMin(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value) +{ + tree.modifyValue(xyz, valxform::MinOp(value)); +} + + +template +inline void +setValueOnMax(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value) +{ + tree.modifyValue(xyz, valxform::MaxOp(value)); +} + + +template +inline void +setValueOnSum(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value) +{ + tree.modifyValue(xyz, valxform::SumOp(value)); +} + + +template +inline void +setValueOnMult(TreeT& tree, const Coord& xyz, const typename TreeT::ValueType& value) +{ + tree.modifyValue(xyz, valxform::MultOp(value)); +} + + +//////////////////////////////////////// + + +namespace valxform { + +template +class SharedOpApplier +{ +public: + typedef typename tree::IteratorRange 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 +class CopyableOpApplier +{ +public: + typedef typename tree::IteratorRange 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 +inline void +foreach(const IterT& iter, XformOp& op, bool threaded, bool shared) +{ + if (shared) { + typename valxform::SharedOpApplier proc(iter, op); + proc.process(threaded); + } else { + typedef typename valxform::CopyableOpApplier Processor; + Processor proc(iter, op); + proc.process(threaded); + } +} + +template +inline void +foreach(const IterT& iter, const XformOp& op, bool threaded, bool /*shared*/) +{ + // Const ops are shared across threads, not copied. + typename valxform::SharedOpApplier proc(iter, op); + proc.process(threaded); +} + + +//////////////////////////////////////// + + +namespace valxform { + +template +class SharedOpTransformer +{ +public: + typedef typename InIterT::TreeT InTreeT; + typedef typename tree::IteratorRange IterRange; + typedef typename OutTreeT::ValueType OutValueT; + + SharedOpTransformer(const InIterT& inIter, OutTreeT& outTree, OpT& op, MergePolicy merge): + mIsRoot(true), + mInputIter(inIter), + mInputTree(inIter.getTree()), + mOutputTree(&outTree), + mOp(op), + mMergePolicy(merge) + { + if (static_cast(mInputTree) == static_cast(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())), + mOp(other.mOp), + mMergePolicy(other.mMergePolicy) + {} + + ~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 outAccessor(*mOutputTree); + for ( ; range; ++range) { + mOp(range.iterator(), outAccessor); + } + } + + void join(const SharedOpTransformer& other) + { + if (mOutputTree && other.mOutputTree) { + mOutputTree->merge(*other.mOutputTree, mMergePolicy); + } + } + +private: + bool mIsRoot; + InIterT mInputIter; + const InTreeT* mInputTree; + OutTreeT* mOutputTree; + OpT& mOp; + MergePolicy mMergePolicy; +}; // class SharedOpTransformer + + +template +class CopyableOpTransformer +{ +public: + typedef typename InIterT::TreeT InTreeT; + typedef typename tree::IteratorRange IterRange; + typedef typename OutTreeT::ValueType OutValueT; + + CopyableOpTransformer(const InIterT& inIter, OutTreeT& outTree, + const OpT& op, MergePolicy merge): + mIsRoot(true), + mInputIter(inIter), + mInputTree(inIter.getTree()), + mOutputTree(&outTree), + mOp(op), + mOrigOp(&op), + mMergePolicy(merge) + { + if (static_cast(mInputTree) == static_cast(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())), + mOp(*other.mOrigOp), + mOrigOp(other.mOrigOp), + mMergePolicy(other.mMergePolicy) + {} + + ~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 outAccessor(*mOutputTree); + for ( ; range; ++range) { + mOp(range.iterator(), outAccessor); + } + } + + void join(const CopyableOpTransformer& other) + { + if (mOutputTree && other.mOutputTree) { + mOutputTree->merge(*other.mOutputTree, mMergePolicy); + } + } + +private: + bool mIsRoot; + InIterT mInputIter; + const InTreeT* mInputTree; + OutTreeT* mOutputTree; + OpT mOp; // copy of original functor + OpT const * const mOrigOp; // pointer to original functor + MergePolicy mMergePolicy; +}; // class CopyableOpTransformer + +} // namespace valxform + + +//////////////////////////////////////// + + +template +inline void +transformValues(const InIterT& inIter, OutGridT& outGrid, XformOp& op, + bool threaded, bool shared, MergePolicy merge) +{ + typedef TreeAdapter Adapter; + typedef typename Adapter::TreeType OutTreeT; + if (shared) { + typedef typename valxform::SharedOpTransformer Processor; + Processor proc(inIter, Adapter::tree(outGrid), op, merge); + proc.process(threaded); + } else { + typedef typename valxform::CopyableOpTransformer Processor; + Processor proc(inIter, Adapter::tree(outGrid), op, merge); + proc.process(threaded); + } +} + +#ifndef _MSC_VER +template +inline void +transformValues(const InIterT& inIter, OutGridT& outGrid, const XformOp& op, + bool threaded, bool /*share*/, MergePolicy merge) +{ + typedef TreeAdapter Adapter; + typedef typename Adapter::TreeType OutTreeT; + // Const ops are shared across threads, not copied. + typedef typename valxform::SharedOpTransformer Processor; + Processor proc(inIter, Adapter::tree(outGrid), op, merge); + proc.process(threaded); +} +#endif + + +//////////////////////////////////////// + + +namespace valxform { + +template +class OpAccumulator +{ +public: + typedef typename tree::IteratorRange 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: + const bool mIsRoot; + const 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 +inline void +accumulate(const IterT& iter, XformOp& op, bool threaded) +{ + typename valxform::OpAccumulator proc(iter, op); + proc.process(threaded); +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_VALUETRANSFORMER_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/VectorTransformer.h b/nuparu/include/openvdb/include/openvdb/tools/VectorTransformer.h new file mode 100644 index 00000000..1a3e56a5 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/VectorTransformer.h @@ -0,0 +1,158 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 VectorTransformer.h + +#ifndef OPENVDB_TOOLS_VECTORTRANSFORMER_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_VECTORTRANSFORMER_HAS_BEEN_INCLUDED + +#include +#include +#include +#include "ValueTransformer.h" // for tools::foreach() +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Apply an affine transform to the voxel values of a vector-valued grid +/// in accordance with the grid's vector type (covariant, contravariant, etc.). +/// @throw TypeError if the grid is not vector-valued +template +inline void +transformVectors(GridType&, const Mat4d&); + + +//////////////////////////////////////// + + +// Functors for use with tools::foreach() to transform vector voxel values + +struct HomogeneousMatMul +{ + const Mat4d mat; + HomogeneousMatMul(const Mat4d& _mat): mat(_mat) {} + template void operator()(const TreeIterT& it) const + { + Vec3d v(*it); + it.setValue(mat.transformH(v)); + } +}; + +struct MatMul +{ + const Mat4d mat; + MatMul(const Mat4d& _mat): mat(_mat) {} + template + void operator()(const TreeIterT& it) const + { + Vec3d v(*it); + it.setValue(mat.transform3x3(v)); + } +}; + +struct MatMulNormalize +{ + const Mat4d mat; + MatMulNormalize(const Mat4d& _mat): mat(_mat) {} + template + void operator()(const TreeIterT& it) const + { + Vec3d v(*it); + v = mat.transform3x3(v); + v.normalize(); + it.setValue(v); + } +}; + + +/// @internal This overload is enabled only for scalar-valued grids. +template inline +typename boost::disable_if_c::IsVec, void>::type +doTransformVectors(GridType&, const Mat4d&) +{ + OPENVDB_THROW(TypeError, "tools::transformVectors() requires a vector-valued grid"); +} + +/// @internal This overload is enabled only for vector-valued grids. +template inline +typename boost::enable_if_c::IsVec, void>::type +doTransformVectors(GridType& grid, const Mat4d& mat) +{ + if (!grid.isInWorldSpace()) return; + + const VecType vecType = grid.getVectorType(); + switch (vecType) { + case VEC_COVARIANT: + case VEC_COVARIANT_NORMALIZE: + { + Mat4d invmat = mat.inverse(); + invmat = invmat.transpose(); + + if (vecType == VEC_COVARIANT_NORMALIZE) { + foreach(grid.beginValueAll(), MatMulNormalize(invmat)); + } else { + foreach(grid.beginValueAll(), MatMul(invmat)); + } + break; + } + + case VEC_CONTRAVARIANT_RELATIVE: + foreach(grid.beginValueAll(), MatMul(mat)); + break; + + case VEC_CONTRAVARIANT_ABSOLUTE: + foreach(grid.beginValueAll(), HomogeneousMatMul(mat)); + break; + + case VEC_INVARIANT: + break; + } +} + + +template +inline void +transformVectors(GridType& grid, const Mat4d& mat) +{ + doTransformVectors(grid, mat); +} + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_VECTORTRANSFORMER_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/VelocityFields.h b/nuparu/include/openvdb/include/openvdb/tools/VelocityFields.h new file mode 100644 index 00000000..7cb0539c --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/VelocityFields.h @@ -0,0 +1,305 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 VelocityFields.h +/// +/// @brief Defines two simple wrapper classes for advection velocity +/// fields as well as VelocitySampler and VelocityIntegrator +/// +/// +/// @details 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 in LevelSetAdvect.h. Thus, any +/// class with this API should work with LevelSetAdvection. +/// +/// @warning 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. + +#ifndef OPENVDB_TOOLS_VELOCITY_FIELDS_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_VELOCITY_FIELDS_HAS_BEEN_INCLUDED + +#include +#include +#include "Interpolation.h" // for Sampler, etc. +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Thin wrapper class for a velocity grid +/// @note Consider replacing BoxSampler with StaggeredBoxSampler +template +class DiscreteField +{ +public: + typedef typename VelGridT::ValueType VectorType; + typedef typename VectorType::ValueType ValueType; + BOOST_STATIC_ASSERT(boost::is_floating_point::value); + + DiscreteField(const VelGridT &vel) + : mAccessor(vel.tree()) + , mTransform(&vel.transform()) + { + } + + /// @brief Copy constructor + DiscreteField(const DiscreteField& other) + : mAccessor(other.mAccessor.tree()) + , mTransform(other.mTransform) + { + } + + /// @return const reference to the transform 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 + /// + /// @warning Not threadsafe since it uses a ValueAccessor! So use + /// one instance per thread (which is fine since its lightweight). + inline VectorType operator() (const Vec3d& xyz, ValueType/*dummy time*/) const + { + return Interpolator::sample(mAccessor, mTransform->worldToIndex(xyz)); + } + + /// @return the velocity at the coordinate space position ijk + /// + /// @warning Not threadsafe since it uses a ValueAccessor! So use + /// one instance per thread (which is fine since its lightweight). + inline VectorType operator() (const Coord& ijk, ValueType/*dummy time*/) const + { + return mAccessor.getValue(ijk); + } + +private: + const typename VelGridT::ConstAccessor mAccessor;//Not thread-safe + const math::Transform* mTransform; + +}; // end of DiscreteField + +/////////////////////////////////////////////////////////////////////// + +/// @brief Analytical, divergence-free and periodic velocity field +/// @note Primarily intended for debugging! +/// @warning This analytical velocity only produce meaningful values +/// in the unit box in world space. In other words make sure any level +/// set surface is fully enclosed in the axis aligned bounding box +/// spanning 0->1 in world units. +template +class EnrightField +{ +public: + typedef ScalarT ValueType; + typedef math::Vec3 VectorType; + BOOST_STATIC_ASSERT(boost::is_floating_point::value); + + EnrightField() {} + + /// @return const reference to the identity transform 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 + math::Transform transform() const { return math::Transform(); } + + /// @return the velocity in world units, evaluated at the world + /// position xyz and at the specified time + inline VectorType operator() (const Vec3d& xyz, ValueType time) const; + + /// @return the velocity at the coordinate space position ijk + inline VectorType operator() (const Coord& ijk, ValueType time) const + { + return (*this)(ijk.asVec3d(), time); + } +}; // end of EnrightField + +template +inline math::Vec3 +EnrightField::operator() (const Vec3d& xyz, ValueType time) const +{ + const ScalarT pi = boost::math::constants::pi(); + const ScalarT 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( + tr * ( ScalarT(2) * math::Pow2(sin(Px)) * a * c ), + tr * ( b * math::Pow2(sin(Py)) * c ), + tr * ( b * a * math::Pow2(sin(Pz)) )); +} + + +/////////////////////////////////////////////////////////////////////// + +/// Class to hold a Vec3 field interpreted 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 +class VelocitySampler +{ +public: + typedef typename GridT::ConstAccessor AccessorType; + typedef typename GridT::ValueType ValueType; + + /// @brief Constructor from a grid + VelocitySampler(const GridT& grid): + mGrid(&grid), + mAcc(grid.getAccessor()) + { + } + /// @brief Copy-constructor + VelocitySampler(const VelocitySampler& other): + mGrid(other.mGrid), + mAcc(mGrid->getAccessor()) + { + } + /// @brief Samples the velocity at world position onto result. Supports both + /// staggered (i.e. MAC) and collocated velocity grids. + /// + /// @return @c true if any one of the sampled values is active. + /// + /// @warning Not threadsafe since it uses a ValueAccessor! So use + /// one instance per thread (which is fine since its lightweight). + template + inline bool sample(const LocationType& world, ValueType& result) const + { + const Vec3R xyz = mGrid->worldToIndex(Vec3R(world[0], world[1], world[2])); + bool active = Sampler::sample(mAcc, xyz, result); + return active; + } + + /// @brief Samples the velocity at world position onto result. Supports both + /// staggered (i.e. MAC) and co-located velocity grids. + /// + /// @warning Not threadsafe since it uses a ValueAccessor! So use + /// one instance per thread (which is fine since its lightweight). + template + inline ValueType sample(const LocationType& world) const + { + const Vec3R xyz = mGrid->worldToIndex(Vec3R(world[0], world[1], world[2])); + return Sampler::sample(mAcc, xyz); + } + +private: + // holding the Grids for the transforms + const GridT* mGrid; // Velocity vector field + AccessorType mAcc; +};// end of VelocitySampler class + +/////////////////////////////////////////////////////////////////////// + +/// @brief Performs Runge-Kutta time integration of variable order in +/// a static velocity field. +/// +/// @note Note that the order of the velocity sampling is controlled +/// with the SampleOrder template parameter, which defaults +/// to one, i.e. a tri-linear interpolation kernel. +template +class VelocityIntegrator +{ +public: + typedef typename GridT::ValueType VecType; + typedef typename VecType::ValueType ElementType; + + VelocityIntegrator(const GridT& velGrid): + mVelSampler(velGrid) + { + } + /// @brief Variable order Runge-Kutta time integration for a single time step + /// + /// @param dt Time sub-step for the Runge-Kutte integrator of order OrderRK + /// @param world Location in world space coordinates (both input and output) + template + inline void rungeKutta(const ElementType dt, LocationType& world) const + { + BOOST_STATIC_ASSERT(OrderRK <= 4); + VecType P(static_cast(world[0]), + static_cast(world[1]), + static_cast(world[2])); + // Note the if-branching below is optimized away at compile time + if (OrderRK == 0) { + return;// do nothing + } else if (OrderRK == 1) { + VecType V0; + mVelSampler.sample(P, V0); + P = dt * V0; + } else if (OrderRK == 2) { + VecType V0, V1; + mVelSampler.sample(P, V0); + mVelSampler.sample(P + ElementType(0.5) * dt * V0, V1); + P = dt * V1; + } else if (OrderRK == 3) { + VecType V0, V1, V2; + mVelSampler.sample(P, V0); + mVelSampler.sample(P + ElementType(0.5) * dt * V0, V1); + mVelSampler.sample(P + dt * (ElementType(2.0) * V1 - V0), V2); + P = dt * (V0 + ElementType(4.0) * V1 + V2) * ElementType(1.0 / 6.0); + } else if (OrderRK == 4) { + VecType V0, V1, V2, V3; + mVelSampler.sample(P, V0); + mVelSampler.sample(P + ElementType(0.5) * dt * V0, V1); + mVelSampler.sample(P + ElementType(0.5) * dt * V1, V2); + mVelSampler.sample(P + dt * V2, V3); + P = dt * (V0 + ElementType(2.0) * (V1 + V2) + V3) * ElementType(1.0 / 6.0); + } + typedef typename LocationType::ValueType OutType; + world += LocationType(static_cast(P[0]), + static_cast(P[1]), + static_cast(P[2])); + } +private: + VelocitySampler mVelSampler; +};// end of VelocityIntegrator class + + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_VELOCITY_FIELDS_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/VolumeAdvect.h b/nuparu/include/openvdb/include/openvdb/tools/VolumeAdvect.h new file mode 100644 index 00000000..b29d3128 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/VolumeAdvect.h @@ -0,0 +1,571 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 VolumeAdvect.h +/// +/// @brief Sparse hyperbolic advection of volumes, e.g. a density or +/// velocity (vs a level set interface). + +#ifndef OPENVDB_TOOLS_VOLUME_ADVECT_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_VOLUME_ADVECT_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include +#include "Interpolation.h"// for Sampler +#include "VelocityFields.h" // for VelocityIntegrator +#include "Morphology.h"//for dilateActiveValues and dilateVoxels +#include "Prune.h"// for prune +#include "Statistics.h" // for extrema + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + + +namespace Scheme { + /// @brief Numerical advections schemes. + enum SemiLagrangian { SEMI, MID, RK3, RK4, MAC, BFECC }; + /// @brief Flux-limiters employed to stabalize the second-order + /// advection schemes MacCormack and BFECC. + enum Limiter { NO_LIMITER, CLAMP, REVERT }; +} + +/// @brief Performs advections of an arbitrary type of volume in a +/// static velocity field. The advections are performed by means +/// of various derivatives of Semi-Lagrangian integration, i.e. +/// backwards tracking along the hyperbolic characteristics +/// followed by interpolation. +/// +/// @note Optionally a limiter can be combined with the higher-order +/// integration schemes MacCormack and BFECC. There are two +/// types of limiters (CLAMP and REVERT) that supress +/// non-physical oscillations by means of either claminging or +/// reverting to a first-order schemes when the function is not +/// bounded by the cell values used for tri-linear interpolation. +/// +/// @verbatim The supported integrations schemes: +/// +/// ================================================================ +/// | Lable | Accuracy | Integration Scheme | Interpolations | +/// | |Time/Space| | velocity/volume | +/// ================================================================ +/// | SEMI | 1/1 | Semi-Lagrangian | 1/1 | +/// | MID | 2/1 | Mid-Point | 2/1 | +/// | RK3 | 3/1 | 3rd Order Runge-Kutta | 3/1 | +/// | RK4 | 4/1 | 4th Order Runge-Kutta | 4/1 | +/// | MAC | 2/2 | MacCormack | 2/2 | +/// | BFECC | 2/2 | BFECC | 3/2 | +/// ================================================================ +/// @endverbatim + +template +class VolumeAdvection +{ +public: + + /// @brief Constructor + /// + /// @param velGrid Velocity grid responsible for the (passive) advection. + /// @param interrupter Optional interrupter used to prematurely end computations. + /// + /// @note The velocity field is assumed to be constant for the duration of the + /// advection. + VolumeAdvection(const VelocityGridT& velGrid, InterrupterType* interrupter = NULL) + : mVelGrid(velGrid) + , mInterrupter(interrupter) + , mIntegrator( Scheme::SEMI ) + , mLimiter( Scheme::CLAMP ) + , mGrainSize( 128 ) + , mSubSteps( 1 ) + { + math::Extrema e = extrema(velGrid.cbeginValueAll(), /*threading*/true); + e.add(velGrid.background().length()); + mMaxVelocity = e.max(); + } + + virtual ~VolumeAdvection() + { + } + + /// @brief Return the spatial order of accuracy of the advection scheme + /// + /// @note This is the optimal order in smooth regions. In + /// non-smooth regions the flux-limiter will drop the order of + /// accuracy to add numerical dissipation. + int spatialOrder() const { return (mIntegrator == Scheme::MAC || + mIntegrator == Scheme::BFECC) ? 2 : 1; } + + /// @brief Return the temporal order of accuracy of the advection scheme + /// + /// @note This is the optimal order in smooth regions. In + /// non-smooth regions the flux-limiter will drop the order of + /// accuracy to add numerical dissipation. + int temporalOrder() const { + switch (mIntegrator) { + case Scheme::SEMI: return 1; + case Scheme::MID: return 2; + case Scheme::RK3: return 3; + case Scheme::RK4: return 4; + case Scheme::BFECC:return 2; + case Scheme::MAC: return 2; + } + return 0;//should never reach this point + } + + /// @brief Set the integrator (see details in the table above) + void setIntegrator(Scheme::SemiLagrangian integrator) { mIntegrator = integrator; } + + /// @brief Return the integrator (see details in the table above) + Scheme::SemiLagrangian getIntegrator() const { return mIntegrator; } + + /// @brief Set the limiter (see details above) + void setLimiter(Scheme::Limiter limiter) { mLimiter = limiter; } + + /// @brief Retrun the limiter (see details above) + Scheme::Limiter getLimiter() const { return mLimiter; } + + /// @brief Return @c true if a limiter will be applied based on + /// the current settings. + bool isLimiterOn() const { return this->spatialOrder()>1 && + mLimiter != Scheme::NO_LIMITER; } + + /// @return the grain-size used for multi-threading + /// @note A grainsize of 0 implies serial execution + size_t getGrainSize() const { return mGrainSize; } + + /// @brief Set the grain-size used for multi-threading + /// @note A grainsize of 0 disables multi-threading + /// @warning A small grainsize can degrade performance, + /// both in terms of time and memory footprint! + void setGrainSize(size_t grainsize) { mGrainSize = grainsize; } + + /// @return the number of sub-steps per integration (always larger + /// than or equal to 1). + int getSubSteps() const { return mSubSteps; } + + /// @brief Set the number of sub-steps per integration. + /// @note The only reason to increase the sub-step above its + /// default value of one is to reduce the memory footprint + /// due to significant dilation. Values smaller than 1 will + /// be clamped to 1! + void setSubSteps(int substeps) { mSubSteps = math::Max(1, substeps); } + + /// @brief Return the maximum magnitude of the velocity in the + /// advection velocity field defined during construction. + double getMaxVelocity() const { return mMaxVelocity; } + + /// @return Returns the maximum distance in voxel units of @a inGrid + /// that a particle can travel in the time-step @a dt when advected + /// in the velocity field defined during construction. + /// + /// @details This method is useful when dilating sparse volume + /// grids to pad boundary regions. Excessive dilation can be + /// computationally expensive so use this method to prevent + /// or warn against run-away computation. + /// + /// @throw RuntimeError if @a inGrid does not have uniform voxels. + template + int getMaxDistance(const VolumeGridT& inGrid, double dt) const + { + if (!inGrid.hasUniformVoxels()) { + OPENVDB_THROW(RuntimeError, "Volume grid does not have uniform voxels!"); + } + const double d = mMaxVelocity*math::Abs(dt)/inGrid.voxelSize()[0]; + return static_cast( math::RoundUp(d) ); + } + + /// @return Returns a new grid that is the result of passive advection + /// of all the active values the input grid by @a timeStep. + /// + /// @param inGrid The input grid to be advected (unmodified) + /// @param timeStep Time-step of the Runge-Kutta integrator. + /// + /// @details This method will advect all of the active values in + /// the input @a inGrid. To achieve this a + /// deep-copy is dilated to account for the material + /// transport. This dilation step can be slow for large + /// time steps @a dt or a velocity field with large magnitudes. + /// + /// @warning If the VolumeSamplerT is of higher order than one + /// (i.e. tri-linear interpolation) instabilities are + /// known to occure. To suppress those monotonicity + /// constrains or flux-limiters need to be applies. + /// + /// @throw RuntimeError if @a inGrid does not have uniform voxels. + template//only C++11 allows for a default argument + typename VolumeGridT::Ptr advect(const VolumeGridT& inGrid, double timeStep) + { + typename VolumeGridT::Ptr outGrid = inGrid.deepCopy(); + const double dt = timeStep/mSubSteps; + const int n = this->getMaxDistance(inGrid, dt); + dilateActiveValues( outGrid->tree(), n, NN_FACE, EXPAND_TILES); + this->template cook(*outGrid, inGrid, dt); + for (int step = 1; step < mSubSteps; ++step) { + typename VolumeGridT::Ptr tmpGrid = outGrid->deepCopy(); + dilateActiveValues( tmpGrid->tree(), n, NN_FACE, EXPAND_TILES); + this->template cook(*tmpGrid, *outGrid, dt); + outGrid.swap( tmpGrid ); + } + + return outGrid; + } + + /// @return Returns a new grid that is the result of + /// passive advection of the active values in @a inGrid + /// that intersect the active values in @c mask. The time + /// of the output grid is incremented by @a timeStep. + /// + /// @param inGrid The input grid to be advected (unmodified). + /// @param mask The mask of active values defining the active voxels + /// in @c inGrid on which to perform advection. Only + /// if a value is active in both grids will it be modified. + /// @param timeStep Time-step for a single Runge-Kutta integration step. + /// + /// + /// @details This method will advect all of the active values in + /// the input @a inGrid that intersects with the + /// active values in @a mask. To achieve this a + /// deep-copy is dilated to account for the material + /// transport and finally cropped to the intersection + /// with @a mask. The dilation step can be slow for large + /// time steps @a dt or fast moving velocity fields. + /// + /// @warning If the VolumeSamplerT is of higher order the one + /// (i.e. tri-linear interpolation) instabilities are + /// known to occure. To suppress those monotonicity + /// constrains or flux-limiters need to be applies. + /// + /// @throw RuntimeError if @a inGrid is not aligned with @a mask + /// or if its voxels are not uniform. + template//only C++11 allows for a default argument + typename VolumeGridT::Ptr advect(const VolumeGridT& inGrid, const MaskGridT& mask, double timeStep) + { + if (inGrid.transform() != mask.transform()) { + OPENVDB_THROW(RuntimeError, "Volume grid and mask grid are misaligned! Consider " + "resampling either of the two grids into the index space of the other."); + } + typename VolumeGridT::Ptr outGrid = inGrid.deepCopy(); + const double dt = timeStep/mSubSteps; + const int n = this->getMaxDistance(inGrid, dt); + dilateActiveValues( outGrid->tree(), n, NN_FACE, EXPAND_TILES); + outGrid->topologyIntersection( mask ); + pruneInactive( outGrid->tree(), mGrainSize>0, mGrainSize ); + this->template cook(*outGrid, inGrid, dt); + outGrid->topologyUnion( inGrid ); + + for (int step = 1; step < mSubSteps; ++step) { + typename VolumeGridT::Ptr tmpGrid = outGrid->deepCopy(); + dilateActiveValues( tmpGrid->tree(), n, NN_FACE, EXPAND_TILES); + tmpGrid->topologyIntersection( mask ); + pruneInactive( tmpGrid->tree(), mGrainSize>0, mGrainSize ); + this->template cook(*tmpGrid, *outGrid, dt); + tmpGrid->topologyUnion( inGrid ); + outGrid.swap( tmpGrid ); + } + return outGrid; + } + +private: + // disallow copy construction and copy by assignment! + VolumeAdvection(const VolumeAdvection&);// not implemented + VolumeAdvection& operator=(const VolumeAdvection&);// not implemented + + void start(const char* str) const + { + if (mInterrupter) mInterrupter->start(str); + } + void stop() const + { + if (mInterrupter) mInterrupter->end(); + } + bool interrupt() const + { + if (mInterrupter && util::wasInterrupted(mInterrupter)) { + tbb::task::self().cancel_group_execution(); + return true; + } + return false; + } + + template + void cook(VolumeGridT& outGrid, const VolumeGridT& inGrid, double dt) + { + switch (mIntegrator) { + case Scheme::SEMI: { + Advect adv(inGrid, *this); + adv.cook(outGrid, dt); + break; + } + case Scheme::MID: { + Advect adv(inGrid, *this); + adv.cook(outGrid, dt); + break; + } + case Scheme::RK3: { + Advect adv(inGrid, *this); + adv.cook(outGrid, dt); + break; + } + case Scheme::RK4: { + Advect adv(inGrid, *this); + adv.cook(outGrid, dt); + break; + } + case Scheme::BFECC: { + Advect adv(inGrid, *this); + adv.cook(outGrid, dt); + break; + } + case Scheme::MAC: { + Advect adv(inGrid, *this); + adv.cook(outGrid, dt); + break; + } + default: + OPENVDB_THROW(ValueError, "Spatial difference scheme not supported!"); + } + pruneInactive(outGrid.tree(), mGrainSize>0, mGrainSize); + } + + // Private class that implements the multi-threaded advection + template struct Advect; + + // Private member data of VolumeAdvection + const VelocityGridT& mVelGrid; + double mMaxVelocity; + InterrupterType* mInterrupter; + Scheme::SemiLagrangian mIntegrator; + Scheme::Limiter mLimiter; + size_t mGrainSize; + int mSubSteps; +};//end of VolumeAdvection class + +// Private class that implements the multi-threaded advection +template +template +struct VolumeAdvection::Advect +{ + typedef typename VolumeGridT::TreeType TreeT; + typedef typename VolumeGridT::ConstAccessor AccT; + typedef typename TreeT::ValueType ValueT; + typedef typename tree::LeafManager LeafManagerT; + typedef typename LeafManagerT::LeafNodeType LeafNodeT; + typedef typename LeafManagerT::LeafRange LeafRangeT; + typedef VelocityIntegrator VelocityIntegratorT; + typedef typename VelocityIntegratorT::ElementType RealT; + typedef typename TreeT::LeafNodeType::ValueOnIter VoxelIterT; + + Advect(const VolumeGridT& inGrid, const VolumeAdvection& parent) + : mTask(0) + , mInGrid(&inGrid) + , mVelocityInt(parent.mVelGrid) + , mParent(&parent) + { + } + inline void cook(const LeafRangeT& range) + { + if (mParent->mGrainSize > 0) { + tbb::parallel_for(range, *this); + } else { + (*this)(range); + } + } + void operator()(const LeafRangeT& range) const + { + assert(mTask); + mTask(const_cast(this), range); + } + void cook(VolumeGridT& outGrid, double time_step) + { + mParent->start("Advecting volume"); + LeafManagerT manager(outGrid.tree(), mParent->spatialOrder()==2 ? 1 : 0); + const LeafRangeT range = manager.leafRange(mParent->mGrainSize); + const RealT dt = static_cast(-time_step);//method of characteristics backtracks + if (mParent->mIntegrator == Scheme::MAC) { + mTask = boost::bind(&Advect::rk, _1, _2, dt, 0, mInGrid);//out[0]=forward + this->cook(range); + mTask = boost::bind(&Advect::rk, _1, _2,-dt, 1, &outGrid);//out[1]=backward + this->cook(range); + mTask = boost::bind(&Advect::mac, _1, _2);//out[0] = out[0] + (in[0] - out[1])/2 + this->cook(range); + } else if (mParent->mIntegrator == Scheme::BFECC) { + mTask = boost::bind(&Advect::rk, _1, _2, dt, 0, mInGrid);//out[0]=forward + this->cook(range); + mTask = boost::bind(&Advect::rk, _1, _2,-dt, 1, &outGrid);//out[1]=backward + this->cook(range); + mTask = boost::bind(&Advect::bfecc, _1, _2);//out[0] = (3*in[0] - out[1])/2 + this->cook(range); + mTask = boost::bind(&Advect::rk, _1, _2, dt, 1, &outGrid);//out[1]=forward + this->cook(range); + manager.swapLeafBuffer(1);// out[0] = out[1] + } else {// SEMI, MID, RK3 and RK4 + mTask = boost::bind(&Advect::rk, _1, _2, dt, 0, mInGrid);//forward + this->cook(range); + } + + if (mParent->spatialOrder()==2) manager.removeAuxBuffers(); + + mTask = boost::bind(&Advect::limiter, _1, _2, dt);// out[0] = limiter( out[0] ) + this->cook(range); + + mParent->stop(); + } + // Last step of the MacCormack scheme: out[0] = out[0] + (in[0] - out[1])/2 + void mac(const LeafRangeT& range) const + { + if (mParent->interrupt()) return; + assert( mParent->mIntegrator == Scheme::MAC ); + AccT acc = mInGrid->getAccessor(); + for (typename LeafRangeT::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + ValueT* out0 = leafIter.buffer( 0 ).data();// forward + const ValueT* out1 = leafIter.buffer( 1 ).data();// backward + const LeafNodeT* leaf = acc.probeConstLeaf( leafIter->origin() ); + if (leaf !=NULL) { + const ValueT* in0 = leaf->buffer().data(); + for (VoxelIterT voxelIter = leafIter->beginValueOn(); voxelIter; ++voxelIter) { + const Index i = voxelIter.pos(); + out0[i] += RealT(0.5) * ( in0[i] - out1[i] ); + } + } else { + for (VoxelIterT voxelIter = leafIter->beginValueOn(); voxelIter; ++voxelIter) { + const Index i = voxelIter.pos(); + out0[i] += RealT(0.5) * ( acc.getValue(voxelIter.getCoord()) - out1[i] ); + }//loop over active voxels + } + }//loop over leaf nodes + } + // Intermediate step in the BFECC scheme: out[0] = (3*in[0] - out[1])/2 + void bfecc(const LeafRangeT& range) const + { + if (mParent->interrupt()) return; + assert( mParent->mIntegrator == Scheme::BFECC ); + AccT acc = mInGrid->getAccessor(); + for (typename LeafRangeT::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + ValueT* out0 = leafIter.buffer( 0 ).data();// forward + const ValueT* out1 = leafIter.buffer( 1 ).data();// backward + const LeafNodeT* leaf = acc.probeConstLeaf(leafIter->origin()); + if (leaf !=NULL) { + const ValueT* in0 = leaf->buffer().data(); + for (VoxelIterT voxelIter = leafIter->beginValueOn(); voxelIter; ++voxelIter) { + const Index i = voxelIter.pos(); + out0[i] = RealT(0.5)*( RealT(3)*in0[i] - out1[i] ); + }//loop over active voxels + } else { + for (VoxelIterT voxelIter = leafIter->beginValueOn(); voxelIter; ++voxelIter) { + const Index i = voxelIter.pos(); + out0[i] = RealT(0.5)*( RealT(3)*acc.getValue(voxelIter.getCoord()) - out1[i] ); + }//loop over active voxels + } + }//loop over leaf nodes + } + // Semi-Lagrangian integration with Runge-Kutta of various orders (1->4) + void rk(const LeafRangeT& range, RealT dt, size_t n, const VolumeGridT* grid) const + { + if (mParent->interrupt()) return; + const math::Transform& xform = mInGrid->transform(); + AccT acc = grid->getAccessor(); + for (typename LeafRangeT::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + ValueT* phi = leafIter.buffer( n ).data(); + for (VoxelIterT voxelIter = leafIter->beginValueOn(); voxelIter; ++voxelIter) { + ValueT& value = phi[voxelIter.pos()]; + Vec3d wPos = xform.indexToWorld(voxelIter.getCoord()); + mVelocityInt.template rungeKutta(dt, wPos); + value = SamplerT::sample(acc, xform.worldToIndex(wPos)); + }//loop over active voxels + }//loop over leaf nodes + } + void limiter(const LeafRangeT& range, RealT dt) const + { + if (mParent->interrupt()) return; + const bool doLimiter = mParent->isLimiterOn(); + const bool doClamp = mParent->mLimiter == Scheme::CLAMP; + ValueT data[2][2][2], vMin, vMax; + const math::Transform& xform = mInGrid->transform(); + AccT acc = mInGrid->getAccessor(); + const ValueT backg = mInGrid->background(); + for (typename LeafRangeT::Iterator leafIter = range.begin(); leafIter; ++leafIter) { + ValueT* phi = leafIter.buffer( 0 ).data(); + for (VoxelIterT voxelIter = leafIter->beginValueOn(); voxelIter; ++voxelIter) { + ValueT& value = phi[voxelIter.pos()]; + + if ( doLimiter ) { + assert(OrderRK == 1); + Vec3d wPos = xform.indexToWorld(voxelIter.getCoord()); + mVelocityInt.template rungeKutta<1, Vec3d>(dt, wPos);// Explicit Euler + Vec3d iPos = xform.worldToIndex(wPos); + Coord ijk = Coord::floor( iPos ); + BoxSampler::getValues(data, acc, ijk); + BoxSampler::extrema(data, vMin, vMax); + if ( doClamp ) { + value = math::Clamp( value, vMin, vMax); + } else if (value < vMin || value > vMax ) { + iPos -= Vec3R(ijk[0], ijk[1], ijk[2]);//unit coordinates + value = BoxSampler::trilinearInterpolation( data, iPos ); + } + } + + if (math::isApproxEqual(value, backg, math::Delta::value())) { + value = backg; + leafIter->setValueOff( voxelIter.pos() ); + } + }//loop over active voxels + }//loop over leaf nodes + } + // Public member data of the private Advect class + + typename boost::function mTask; + const VolumeGridT* mInGrid; + const VelocityIntegratorT mVelocityInt;// lightweight! + const VolumeAdvection* mParent; +};// end of private member class Advect + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_VOLUME_ADVECT_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tools/VolumeToMesh.h b/nuparu/include/openvdb/include/openvdb/tools/VolumeToMesh.h new file mode 100644 index 00000000..f162941e --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/VolumeToMesh.h @@ -0,0 +1,4690 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 // for OPENVDB_HAS_CXX11 +#include +#include // for COORD_OFFSETS +#include // for ISGradient +#include // for dilateVoxels() +#include +#include "Prune.h" // for pruneInactive + +#include +#include +#include +#include +#include +#include +#include + +#include +#include // for auto_ptr/unique_ptr + + +////////// + + +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 +/// +/// @throw TypeError if @a grid does not have a scalar value type +template +inline void +volumeToMesh( + const GridType& grid, + std::vector& points, + std::vector& 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 triangle index list +/// @param quads output quad index list +/// @param isovalue determines which isosurface to mesh +/// @param adaptivity surface adaptivity threshold [0 to 1] +/// +/// @throw TypeError if @a grid does not have a scalar value type +template +inline void +volumeToMesh( + const GridType& grid, + std::vector& points, + std::vector& triangles, + std::vector& 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, POLYFLAG_SUBDIVIDED = 0x4}; + + +/// @brief Collection of quads and triangles +class PolygonPool +{ +public: + + inline PolygonPool(); + inline PolygonPool(const size_t numQuads, const size_t numTriangles); + + + inline void copy(const PolygonPool& rhs); + + inline void resetQuads(size_t size); + inline void clearQuads(); + + inline void resetTriangles(size_t size); + inline void clearTriangles(); + + + // polygon accessor methods + + const size_t& numQuads() const { return mNumQuads; } + + openvdb::Vec4I& quad(size_t n) { return mQuads[n]; } + const openvdb::Vec4I& quad(size_t n) const { return mQuads[n]; } + + + const size_t& numTriangles() const { return mNumTriangles; } + + 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]; } + + + // reduce the polygon containers, n has to + // be smaller than the current container size. + + inline bool trimQuads(const size_t n, bool reallocate = false); + inline bool trimTrinagles(const size_t n, bool reallocate = false); + +private: + // disallow copy by assignment + void operator=(const PolygonPool&) {} + + size_t mNumQuads, mNumTriangles; + boost::scoped_array mQuads; + boost::scoped_array mTriangles; + boost::scoped_array mQuadFlags, mTriangleFlags; +}; + + +/// @{ +/// @brief Point and primitive list types. +typedef boost::scoped_array PointList; +typedef boost::scoped_array 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); + + + ////////// + + // Mesh data accessors + + const size_t& pointListSize() const; + PointList& pointList(); + + const size_t& polygonPoolListSize() const; + PolygonPoolList& polygonPoolList(); + const PolygonPoolList& polygonPoolList() const; + + std::vector& pointFlags(); + const std::vector& pointFlags() const; + + + ////////// + + + /// @brief Main call + /// @note Call with scalar typed grid. + template + 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.) + void setRefGrid(const GridBase::ConstPtr& grid, double secAdaptivity = 0); + + + /// @param mask A boolean grid whose active topology defines the region to mesh. + /// @param invertMask Toggle to mesh the complement of the mask. + /// @note The mask's tree configuration has to match @c GridT's tree configuration. + void setSurfaceMask(const GridBase::ConstPtr& mask, bool invertMask = false); + + /// @param grid A scalar grid used as a spatial multiplier for the adaptivity threshold. + /// @note The grid's tree configuration has to match @c GridT's tree configuration. + void setSpatialAdaptivity(const GridBase::ConstPtr& grid); + + + /// @param tree A boolean tree whose active topology defines the adaptivity mask. + /// @note The tree configuration has to match @c GridT's tree configuration. + void setAdaptivityMask(const TreeBase::ConstPtr& tree); + + + /// @brief Subdivide volume and mesh into disjoint parts + /// @param partitions Number of partitions. + /// @param activePart Specific partition to mesh, 0 to @c partitions - 1. + void partition(unsigned partitions = 1, unsigned activePart = 0); + +private: + + PointList mPoints; + PolygonPoolList mPolygons; + + size_t mPointListSize, mSeamPointListSize, mPolygonPoolListSize; + double mIsovalue, mPrimAdaptivity, mSecAdaptivity; + + GridBase::ConstPtr mRefGrid, mSurfaceMaskGrid, mAdaptivityGrid; + TreeBase::ConstPtr mAdaptivityMaskTree; + + TreeBase::Ptr mRefSignTree, mRefIdxTree; + + bool mInvertSurfaceMask; + unsigned mPartitions, mActivePart; + + boost::scoped_array mQuantizedSeamPoints; + + std::vector mPointFlags; +}; + + +//////////////////////////////////////// + + +/// @brief Given a set of tangent elements, @c points with corresponding @c normals, +/// this method returns the intersection point of all tangent elements. +/// +/// @note Used to extract surfaces with sharp edges and corners from volume data, +/// see the following paper for details: "Feature Sensitive Surface +/// Extraction from Volume Data, Kobbelt et al. 2001". +inline Vec3d findFeaturePoint( + const std::vector& points, + const std::vector& normals) +{ + typedef math::Mat3d Mat3d; + + Vec3d avgPos(0.0); + + if (points.empty()) return avgPos; + + for (size_t n = 0, N = points.size(); n < N; ++n) { + avgPos += points[n]; + } + + avgPos /= double(points.size()); + + // Unique components of the 3x3 A^TA matrix, where A is + // the matrix of normals. + double m00=0,m01=0,m02=0, + m11=0,m12=0, + m22=0; + + // The rhs vector, A^Tb, where b = n dot p + Vec3d rhs(0.0); + + for (size_t n = 0, N = points.size(); n < N; ++n) { + + const Vec3d& n_ref = normals[n]; + + // A^TA + m00 += n_ref[0] * n_ref[0]; // diagonal + m11 += n_ref[1] * n_ref[1]; + m22 += n_ref[2] * n_ref[2]; + + m01 += n_ref[0] * n_ref[1]; // Upper-tri + m02 += n_ref[0] * n_ref[2]; + m12 += n_ref[1] * n_ref[2]; + + // A^Tb (centered around the origin) + rhs += n_ref * n_ref.dot(points[n] - avgPos); + } + + Mat3d A(m00,m01,m02, + m01,m11,m12, + m02,m12,m22); + + /* + // Inverse + const double det = A.det(); + if (det > 0.01) { + Mat3d A_inv = A.adjoint(); + A_inv *= (1.0 / det); + + return avgPos + A_inv * rhs; + } + */ + + // Compute the pseudo inverse + + math::Mat3d eigenVectors; + Vec3d eigenValues; + + diagonalizeSymmetricMatrix(A, eigenVectors, eigenValues, 300); + + Mat3d D = Mat3d::identity(); + + + double tolerance = std::max(std::abs(eigenValues[0]), std::abs(eigenValues[1])); + tolerance = std::max(tolerance, std::abs(eigenValues[2])); + tolerance *= 0.01; + + int clamped = 0; + for (int i = 0; i < 3; ++i ) { + if (std::abs(eigenValues[i]) < tolerance) { + D[i][i] = 0.0; + ++clamped; + } else { + D[i][i] = 1.0 / eigenValues[i]; + } + } + + // Assemble the pseudo inverse and calc. the intersection point + if (clamped < 3) { + Mat3d pseudoInv = eigenVectors * D * eigenVectors.transpose(); + return avgPos + pseudoInv * rhs; + } + + return avgPos; +} + + +//////////////////////////////////////// + + +// Internal utility methods +namespace internal { + +template +struct UniquePtr +{ +#ifdef OPENVDB_HAS_CXX11 + typedef std::unique_ptr type; +#else + typedef std::auto_ptr type; +#endif +}; + + +/// @brief Bit-flags used to classify cells. +enum { SIGNS = 0xFF, EDGES = 0xE00, INSIDE = 0x100, + XEDGE = 0x200, YEDGE = 0x400, ZEDGE = 0x800, SEAM = 0x1000}; + + +/// @brief Used to quickly determine if a given cell is adaptable. +const bool sAdaptable[256] = { + 1,1,1,1,1,0,1,1,1,1,0,1,1,1,1,1,1,1,0,1,0,0,0,1,0,1,0,1,0,1,0,1, + 1,0,1,1,0,0,1,1,0,0,0,1,0,0,1,1,1,1,1,1,0,0,1,1,0,1,0,1,0,0,0,1, + 1,0,0,0,1,0,1,1,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 1,0,1,1,1,0,1,1,0,0,0,0,1,0,1,1,1,1,1,1,1,0,1,1,0,0,0,0,0,0,0,1, + 1,0,0,0,0,0,0,0,1,1,0,1,1,1,1,1,1,1,0,1,0,0,0,0,1,1,0,1,1,1,0,1, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,1,1,0,1,0,0,0,1, + 1,0,0,0,1,0,1,0,1,1,0,0,1,1,1,1,1,1,0,0,1,0,0,0,1,1,0,0,1,1,0,1, + 1,0,1,0,1,0,1,0,1,0,0,0,1,0,1,1,1,1,1,1,1,0,1,1,1,1,0,1,1,1,1,1}; + + +/// @brief Contains the ambiguous face index for certain cell configuration. +const unsigned char sAmbiguousFace[256] = { + 0,0,0,0,0,5,0,0,0,0,5,0,0,0,0,0,0,0,1,0,0,5,1,0,4,0,0,0,4,0,0,0, + 0,1,0,0,2,0,0,0,0,1,5,0,2,0,0,0,0,0,0,0,2,0,0,0,4,0,0,0,0,0,0,0, + 0,0,2,2,0,5,0,0,3,3,0,0,0,0,0,0,6,6,0,0,6,0,0,0,0,0,0,0,0,0,0,0, + 0,1,0,0,0,0,0,0,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,4,0,4,3,0,3,0,0,0,5,0,0,0,0,0,0,0,1,0,3,0,0,0,0,0,0,0,0,0,0,0, + 6,0,6,0,0,0,0,0,6,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,4,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; + + +/// @brief Lookup table for different cell sign configurations. The first entry specifies +/// the total number of points that need to be generated inside a cell and the +/// remaining 12 entries indicate different edge groups. +const unsigned char sEdgeGroupTable[256][13] = { + {0,0,0,0,0,0,0,0,0,0,0,0,0},{1,1,0,0,1,0,0,0,0,1,0,0,0},{1,1,1,0,0,0,0,0,0,0,1,0,0}, + {1,0,1,0,1,0,0,0,0,1,1,0,0},{1,0,1,1,0,0,0,0,0,0,0,1,0},{1,1,1,1,1,0,0,0,0,1,0,1,0}, + {1,1,0,1,0,0,0,0,0,0,1,1,0},{1,0,0,1,1,0,0,0,0,1,1,1,0},{1,0,0,1,1,0,0,0,0,0,0,0,1}, + {1,1,0,1,0,0,0,0,0,1,0,0,1},{1,1,1,1,1,0,0,0,0,0,1,0,1},{1,0,1,1,0,0,0,0,0,1,1,0,1}, + {1,0,1,0,1,0,0,0,0,0,0,1,1},{1,1,1,0,0,0,0,0,0,1,0,1,1},{1,1,0,0,1,0,0,0,0,0,1,1,1}, + {1,0,0,0,0,0,0,0,0,1,1,1,1},{1,0,0,0,0,1,0,0,1,1,0,0,0},{1,1,0,0,1,1,0,0,1,0,0,0,0}, + {1,1,1,0,0,1,0,0,1,1,1,0,0},{1,0,1,0,1,1,0,0,1,0,1,0,0},{2,0,1,1,0,2,0,0,2,2,0,1,0}, + {1,1,1,1,1,1,0,0,1,0,0,1,0},{1,1,0,1,0,1,0,0,1,1,1,1,0},{1,0,0,1,1,1,0,0,1,0,1,1,0}, + {1,0,0,1,1,1,0,0,1,1,0,0,1},{1,1,0,1,0,1,0,0,1,0,0,0,1},{2,2,1,1,2,1,0,0,1,2,1,0,1}, + {1,0,1,1,0,1,0,0,1,0,1,0,1},{1,0,1,0,1,1,0,0,1,1,0,1,1},{1,1,1,0,0,1,0,0,1,0,0,1,1}, + {2,1,0,0,1,2,0,0,2,1,2,2,2},{1,0,0,0,0,1,0,0,1,0,1,1,1},{1,0,0,0,0,1,1,0,0,0,1,0,0}, + {1,1,0,0,1,1,1,0,0,1,1,0,0},{1,1,1,0,0,1,1,0,0,0,0,0,0},{1,0,1,0,1,1,1,0,0,1,0,0,0}, + {1,0,1,1,0,1,1,0,0,0,1,1,0},{2,2,2,1,1,1,1,0,0,1,2,1,0},{1,1,0,1,0,1,1,0,0,0,0,1,0}, + {1,0,0,1,1,1,1,0,0,1,0,1,0},{2,0,0,2,2,1,1,0,0,0,1,0,2},{1,1,0,1,0,1,1,0,0,1,1,0,1}, + {1,1,1,1,1,1,1,0,0,0,0,0,1},{1,0,1,1,0,1,1,0,0,1,0,0,1},{1,0,1,0,1,1,1,0,0,0,1,1,1}, + {2,1,1,0,0,2,2,0,0,2,1,2,2},{1,1,0,0,1,1,1,0,0,0,0,1,1},{1,0,0,0,0,1,1,0,0,1,0,1,1}, + {1,0,0,0,0,0,1,0,1,1,1,0,0},{1,1,0,0,1,0,1,0,1,0,1,0,0},{1,1,1,0,0,0,1,0,1,1,0,0,0}, + {1,0,1,0,1,0,1,0,1,0,0,0,0},{1,0,1,1,0,0,1,0,1,1,1,1,0},{2,1,1,2,2,0,2,0,2,0,1,2,0}, + {1,1,0,1,0,0,1,0,1,1,0,1,0},{1,0,0,1,1,0,1,0,1,0,0,1,0},{1,0,0,1,1,0,1,0,1,1,1,0,1}, + {1,1,0,1,0,0,1,0,1,0,1,0,1},{2,1,2,2,1,0,2,0,2,1,0,0,2},{1,0,1,1,0,0,1,0,1,0,0,0,1}, + {2,0,2,0,2,0,1,0,1,2,2,1,1},{2,2,2,0,0,0,1,0,1,0,2,1,1},{2,2,0,0,2,0,1,0,1,2,0,1,1}, + {1,0,0,0,0,0,1,0,1,0,0,1,1},{1,0,0,0,0,0,1,1,0,0,0,1,0},{2,1,0,0,1,0,2,2,0,1,0,2,0}, + {1,1,1,0,0,0,1,1,0,0,1,1,0},{1,0,1,0,1,0,1,1,0,1,1,1,0},{1,0,1,1,0,0,1,1,0,0,0,0,0}, + {1,1,1,1,1,0,1,1,0,1,0,0,0},{1,1,0,1,0,0,1,1,0,0,1,0,0},{1,0,0,1,1,0,1,1,0,1,1,0,0}, + {1,0,0,1,1,0,1,1,0,0,0,1,1},{1,1,0,1,0,0,1,1,0,1,0,1,1},{2,1,2,2,1,0,1,1,0,0,1,2,1}, + {2,0,1,1,0,0,2,2,0,2,2,1,2},{1,0,1,0,1,0,1,1,0,0,0,0,1},{1,1,1,0,0,0,1,1,0,1,0,0,1}, + {1,1,0,0,1,0,1,1,0,0,1,0,1},{1,0,0,0,0,0,1,1,0,1,1,0,1},{1,0,0,0,0,1,1,1,1,1,0,1,0}, + {1,1,0,0,1,1,1,1,1,0,0,1,0},{2,1,1,0,0,2,2,1,1,1,2,1,0},{2,0,2,0,2,1,1,2,2,0,1,2,0}, + {1,0,1,1,0,1,1,1,1,1,0,0,0},{2,2,2,1,1,2,2,1,1,0,0,0,0},{2,2,0,2,0,1,1,2,2,2,1,0,0}, + {2,0,0,1,1,2,2,1,1,0,2,0,0},{2,0,0,1,1,1,1,2,2,1,0,1,2},{2,2,0,2,0,2,2,1,1,0,0,2,1}, + {4,3,2,2,3,4,4,1,1,3,4,2,1},{3,0,2,2,0,1,1,3,3,0,1,2,3},{2,0,2,0,2,2,2,1,1,2,0,0,1}, + {2,1,1,0,0,1,1,2,2,0,0,0,2},{3,1,0,0,1,2,2,3,3,1,2,0,3},{2,0,0,0,0,1,1,2,2,0,1,0,2}, + {1,0,0,0,0,1,0,1,0,0,1,1,0},{1,1,0,0,1,1,0,1,0,1,1,1,0},{1,1,1,0,0,1,0,1,0,0,0,1,0}, + {1,0,1,0,1,1,0,1,0,1,0,1,0},{1,0,1,1,0,1,0,1,0,0,1,0,0},{2,1,1,2,2,2,0,2,0,2,1,0,0}, + {1,1,0,1,0,1,0,1,0,0,0,0,0},{1,0,0,1,1,1,0,1,0,1,0,0,0},{1,0,0,1,1,1,0,1,0,0,1,1,1}, + {2,2,0,2,0,1,0,1,0,1,2,2,1},{2,2,1,1,2,2,0,2,0,0,0,1,2},{2,0,2,2,0,1,0,1,0,1,0,2,1}, + {1,0,1,0,1,1,0,1,0,0,1,0,1},{2,2,2,0,0,1,0,1,0,1,2,0,1},{1,1,0,0,1,1,0,1,0,0,0,0,1}, + {1,0,0,0,0,1,0,1,0,1,0,0,1},{1,0,0,0,0,0,0,1,1,1,1,1,0},{1,1,0,0,1,0,0,1,1,0,1,1,0}, + {1,1,1,0,0,0,0,1,1,1,0,1,0},{1,0,1,0,1,0,0,1,1,0,0,1,0},{1,0,1,1,0,0,0,1,1,1,1,0,0}, + {2,2,2,1,1,0,0,1,1,0,2,0,0},{1,1,0,1,0,0,0,1,1,1,0,0,0},{1,0,0,1,1,0,0,1,1,0,0,0,0}, + {2,0,0,2,2,0,0,1,1,2,2,2,1},{2,1,0,1,0,0,0,2,2,0,1,1,2},{3,2,1,1,2,0,0,3,3,2,0,1,3}, + {2,0,1,1,0,0,0,2,2,0,0,1,2},{2,0,1,0,1,0,0,2,2,1,1,0,2},{2,1,1,0,0,0,0,2,2,0,1,0,2}, + {2,1,0,0,1,0,0,2,2,1,0,0,2},{1,0,0,0,0,0,0,1,1,0,0,0,1},{1,0,0,0,0,0,0,1,1,0,0,0,1}, + {1,1,0,0,1,0,0,1,1,1,0,0,1},{2,1,1,0,0,0,0,2,2,0,1,0,2},{1,0,1,0,1,0,0,1,1,1,1,0,1}, + {1,0,1,1,0,0,0,1,1,0,0,1,1},{2,1,1,2,2,0,0,1,1,1,0,1,2},{1,1,0,1,0,0,0,1,1,0,1,1,1}, + {2,0,0,1,1,0,0,2,2,2,2,2,1},{1,0,0,1,1,0,0,1,1,0,0,0,0},{1,1,0,1,0,0,0,1,1,1,0,0,0}, + {1,1,1,1,1,0,0,1,1,0,1,0,0},{1,0,1,1,0,0,0,1,1,1,1,0,0},{1,0,1,0,1,0,0,1,1,0,0,1,0}, + {1,1,1,0,0,0,0,1,1,1,0,1,0},{1,1,0,0,1,0,0,1,1,0,1,1,0},{1,0,0,0,0,0,0,1,1,1,1,1,0}, + {1,0,0,0,0,1,0,1,0,1,0,0,1},{1,1,0,0,1,1,0,1,0,0,0,0,1},{1,1,1,0,0,1,0,1,0,1,1,0,1}, + {1,0,1,0,1,1,0,1,0,0,1,0,1},{1,0,1,1,0,1,0,1,0,1,0,1,1},{2,2,2,1,1,2,0,2,0,0,0,2,1}, + {2,1,0,1,0,2,0,2,0,1,2,2,1},{2,0,0,2,2,1,0,1,0,0,1,1,2},{1,0,0,1,1,1,0,1,0,1,0,0,0}, + {1,1,0,1,0,1,0,1,0,0,0,0,0},{2,1,2,2,1,2,0,2,0,1,2,0,0},{1,0,1,1,0,1,0,1,0,0,1,0,0}, + {1,0,1,0,1,1,0,1,0,1,0,1,0},{1,1,1,0,0,1,0,1,0,0,0,1,0},{2,2,0,0,2,1,0,1,0,2,1,1,0}, + {1,0,0,0,0,1,0,1,0,0,1,1,0},{1,0,0,0,0,1,1,1,1,0,1,0,1},{2,1,0,0,1,2,1,1,2,2,1,0,1}, + {1,1,1,0,0,1,1,1,1,0,0,0,1},{2,0,2,0,2,1,2,2,1,1,0,0,2},{2,0,1,1,0,1,2,2,1,0,1,2,1}, + {4,1,1,3,3,2,4,4,2,2,1,4,3},{2,2,0,2,0,2,1,1,2,0,0,1,2},{3,0,0,1,1,2,3,3,2,2,0,3,1}, + {1,0,0,1,1,1,1,1,1,0,1,0,0},{2,2,0,2,0,1,2,2,1,1,2,0,0},{2,2,1,1,2,2,1,1,2,0,0,0,0}, + {2,0,1,1,0,2,1,1,2,2,0,0,0},{2,0,2,0,2,2,1,1,2,0,2,1,0},{3,1,1,0,0,3,2,2,3,3,1,2,0}, + {2,1,0,0,1,1,2,2,1,0,0,2,0},{2,0,0,0,0,2,1,1,2,2,0,1,0},{1,0,0,0,0,0,1,1,0,1,1,0,1}, + {1,1,0,0,1,0,1,1,0,0,1,0,1},{1,1,1,0,0,0,1,1,0,1,0,0,1},{1,0,1,0,1,0,1,1,0,0,0,0,1}, + {2,0,2,2,0,0,1,1,0,2,2,1,2},{3,1,1,2,2,0,3,3,0,0,1,3,2},{2,1,0,1,0,0,2,2,0,1,0,2,1}, + {2,0,0,1,1,0,2,2,0,0,0,2,1},{1,0,0,1,1,0,1,1,0,1,1,0,0},{1,1,0,1,0,0,1,1,0,0,1,0,0}, + {2,2,1,1,2,0,1,1,0,2,0,0,0},{1,0,1,1,0,0,1,1,0,0,0,0,0},{2,0,1,0,1,0,2,2,0,1,1,2,0}, + {2,1,1,0,0,0,2,2,0,0,1,2,0},{2,1,0,0,1,0,2,2,0,1,0,2,0},{1,0,0,0,0,0,1,1,0,0,0,1,0}, + {1,0,0,0,0,0,1,0,1,0,0,1,1},{1,1,0,0,1,0,1,0,1,1,0,1,1},{1,1,1,0,0,0,1,0,1,0,1,1,1}, + {2,0,2,0,2,0,1,0,1,1,1,2,2},{1,0,1,1,0,0,1,0,1,0,0,0,1},{2,2,2,1,1,0,2,0,2,2,0,0,1}, + {1,1,0,1,0,0,1,0,1,0,1,0,1},{2,0,0,2,2,0,1,0,1,1,1,0,2},{1,0,0,1,1,0,1,0,1,0,0,1,0}, + {1,1,0,1,0,0,1,0,1,1,0,1,0},{2,2,1,1,2,0,2,0,2,0,2,1,0},{2,0,2,2,0,0,1,0,1,1,1,2,0}, + {1,0,1,0,1,0,1,0,1,0,0,0,0},{1,1,1,0,0,0,1,0,1,1,0,0,0},{1,1,0,0,1,0,1,0,1,0,1,0,0}, + {1,0,0,0,0,0,1,0,1,1,1,0,0},{1,0,0,0,0,1,1,0,0,1,0,1,1},{1,1,0,0,1,1,1,0,0,0,0,1,1}, + {2,2,2,0,0,1,1,0,0,2,1,2,2},{2,0,1,0,1,2,2,0,0,0,2,1,1},{1,0,1,1,0,1,1,0,0,1,0,0,1}, + {2,1,1,2,2,1,1,0,0,0,0,0,2},{2,1,0,1,0,2,2,0,0,1,2,0,1},{2,0,0,2,2,1,1,0,0,0,1,0,2}, + {1,0,0,1,1,1,1,0,0,1,0,1,0},{1,1,0,1,0,1,1,0,0,0,0,1,0},{3,1,2,2,1,3,3,0,0,1,3,2,0}, + {2,0,1,1,0,2,2,0,0,0,2,1,0},{1,0,1,0,1,1,1,0,0,1,0,0,0},{1,1,1,0,0,1,1,0,0,0,0,0,0}, + {2,2,0,0,2,1,1,0,0,2,1,0,0},{1,0,0,0,0,1,1,0,0,0,1,0,0},{1,0,0,0,0,1,0,0,1,0,1,1,1}, + {2,2,0,0,2,1,0,0,1,1,2,2,2},{1,1,1,0,0,1,0,0,1,0,0,1,1},{2,0,1,0,1,2,0,0,2,2,0,1,1}, + {1,0,1,1,0,1,0,0,1,0,1,0,1},{3,1,1,3,3,2,0,0,2,2,1,0,3},{1,1,0,1,0,1,0,0,1,0,0,0,1}, + {2,0,0,2,2,1,0,0,1,1,0,0,2},{1,0,0,1,1,1,0,0,1,0,1,1,0},{2,1,0,1,0,2,0,0,2,2,1,1,0}, + {2,1,2,2,1,1,0,0,1,0,0,2,0},{2,0,1,1,0,2,0,0,2,2,0,1,0},{1,0,1,0,1,1,0,0,1,0,1,0,0}, + {2,1,1,0,0,2,0,0,2,2,1,0,0},{1,1,0,0,1,1,0,0,1,0,0,0,0},{1,0,0,0,0,1,0,0,1,1,0,0,0}, + {1,0,0,0,0,0,0,0,0,1,1,1,1},{1,1,0,0,1,0,0,0,0,0,1,1,1},{1,1,1,0,0,0,0,0,0,1,0,1,1}, + {1,0,1,0,1,0,0,0,0,0,0,1,1},{1,0,1,1,0,0,0,0,0,1,1,0,1},{2,1,1,2,2,0,0,0,0,0,1,0,2}, + {1,1,0,1,0,0,0,0,0,1,0,0,1},{1,0,0,1,1,0,0,0,0,0,0,0,1},{1,0,0,1,1,0,0,0,0,1,1,1,0}, + {1,1,0,1,0,0,0,0,0,0,1,1,0},{2,1,2,2,1,0,0,0,0,1,0,2,0},{1,0,1,1,0,0,0,0,0,0,0,1,0}, + {1,0,1,0,1,0,0,0,0,1,1,0,0},{1,1,1,0,0,0,0,0,0,0,1,0,0},{1,1,0,0,1,0,0,0,0,1,0,0,0}, + {0,0,0,0,0,0,0,0,0,0,0,0,0}}; + + +//////////////////////////////////////// + +inline bool +isPlanarQuad( + const Vec3d& p0, const Vec3d& p1, + const Vec3d& p2, const Vec3d& p3, + double epsilon = 0.001) +{ + // compute representative plane + Vec3d normal = (p2-p0).cross(p1-p3); + normal.normalize(); + const Vec3d centroid = (p0 + p1 + p2 + p3); + const double d = centroid.dot(normal) * 0.25; + + + // test vertice distance to plane + double absDist = std::abs(p0.dot(normal) - d); + if (absDist > epsilon) return false; + + absDist = std::abs(p1.dot(normal) - d); + if (absDist > epsilon) return false; + + absDist = std::abs(p2.dot(normal) - d); + if (absDist > epsilon) return false; + + absDist = std::abs(p3.dot(normal) - d); + if (absDist > epsilon) return false; + + return true; +} + + +//////////////////////////////////////// + + +/// @{ +/// @brief Utility methods for point quantization. + +enum { + MASK_FIRST_10_BITS = 0x000003FF, + MASK_DIRTY_BIT = 0x80000000, + MASK_INVALID_BIT = 0x40000000 +}; + +inline uint32_t +packPoint(const Vec3d& v) +{ + uint32_t data = 0; + + // values are expected to be in the [0.0 to 1.0] range. + assert(!(v.x() > 1.0) && !(v.y() > 1.0) && !(v.z() > 1.0)); + assert(!(v.x() < 0.0) && !(v.y() < 0.0) && !(v.z() < 0.0)); + + data |= (uint32_t(v.x() * 1023.0) & MASK_FIRST_10_BITS) << 20; + data |= (uint32_t(v.y() * 1023.0) & MASK_FIRST_10_BITS) << 10; + data |= (uint32_t(v.z() * 1023.0) & MASK_FIRST_10_BITS); + + return data; +} + +inline Vec3d +unpackPoint(uint32_t data) +{ + Vec3d v; + v.z() = double(data & MASK_FIRST_10_BITS) * 0.0009775171; + data = data >> 10; + v.y() = double(data & MASK_FIRST_10_BITS) * 0.0009775171; + data = data >> 10; + v.x() = double(data & MASK_FIRST_10_BITS) * 0.0009775171; + + return v; +} + +/// @} + +//////////////////////////////////////// + + +/// @brief General method that computes the cell-sign configuration at the given +/// @c ijk coordinate. +template +inline unsigned char +evalCellSigns(const AccessorT& accessor, const Coord& ijk, typename AccessorT::ValueType iso) +{ + unsigned signs = 0; + Coord coord = ijk; // i, j, k + if (accessor.getValue(coord) < iso) signs |= 1u; + coord[0] += 1; // i+1, j, k + if (accessor.getValue(coord) < iso) signs |= 2u; + coord[2] += 1; // i+1, j, k+1 + if (accessor.getValue(coord) < iso) signs |= 4u; + coord[0] = ijk[0]; // i, j, k+1 + if (accessor.getValue(coord) < iso) signs |= 8u; + coord[1] += 1; coord[2] = ijk[2]; // i, j+1, k + if (accessor.getValue(coord) < iso) signs |= 16u; + coord[0] += 1; // i+1, j+1, k + if (accessor.getValue(coord) < iso) signs |= 32u; + coord[2] += 1; // i+1, j+1, k+1 + if (accessor.getValue(coord) < iso) signs |= 64u; + coord[0] = ijk[0]; // i, j+1, k+1 + if (accessor.getValue(coord) < iso) signs |= 128u; + return uint8_t(signs); +} + + +/// @brief Leaf node optimized method that computes the cell-sign configuration +/// at the given local @c offset +template +inline unsigned char +evalCellSigns(const LeafT& leaf, const Index offset, typename LeafT::ValueType iso) +{ + unsigned char signs = 0; + + // i, j, k + if (leaf.getValue(offset) < iso) signs |= 1u; + + // i, j, k+1 + if (leaf.getValue(offset + 1) < iso) signs |= 8u; + + // i, j+1, k + if (leaf.getValue(offset + LeafT::DIM) < iso) signs |= 16u; + + // i, j+1, k+1 + if (leaf.getValue(offset + LeafT::DIM + 1) < iso) signs |= 128u; + + // i+1, j, k + if (leaf.getValue(offset + (LeafT::DIM * LeafT::DIM) ) < iso) signs |= 2u; + + // i+1, j, k+1 + if (leaf.getValue(offset + (LeafT::DIM * LeafT::DIM) + 1) < iso) signs |= 4u; + + // i+1, j+1, k + if (leaf.getValue(offset + (LeafT::DIM * LeafT::DIM) + LeafT::DIM) < iso) signs |= 32u; + + // i+1, j+1, k+1 + if (leaf.getValue(offset + (LeafT::DIM * LeafT::DIM) + LeafT::DIM + 1) < iso) signs |= 64u; + + return signs; +} + + +/// @brief Used to correct topological ambiguities related to two adjacent cells +/// that share an ambiguous face. +template +inline void +correctCellSigns(unsigned char& signs, unsigned char face, + const AccessorT& acc, Coord ijk, typename AccessorT::ValueType iso) +{ + if (face == 1) { + ijk[2] -= 1; + if (sAmbiguousFace[evalCellSigns(acc, ijk, iso)] == 3) signs = uint8_t(~signs); + } else if (face == 3) { + ijk[2] += 1; + if (sAmbiguousFace[evalCellSigns(acc, ijk, iso)] == 1) signs = uint8_t(~signs); + } else if (face == 2) { + ijk[0] += 1; + if (sAmbiguousFace[evalCellSigns(acc, ijk, iso)] == 4) signs = uint8_t(~signs); + } else if (face == 4) { + ijk[0] -= 1; + if (sAmbiguousFace[evalCellSigns(acc, ijk, iso)] == 2) signs = uint8_t(~signs); + } else if (face == 5) { + ijk[1] -= 1; + if (sAmbiguousFace[evalCellSigns(acc, ijk, iso)] == 6) signs = uint8_t(~signs); + } else if (face == 6) { + ijk[1] += 1; + if (sAmbiguousFace[evalCellSigns(acc, ijk, iso)] == 5) signs = uint8_t(~signs); + } +} + + +template +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 (!sAdaptable[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 +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]) { + leaf.setValueOnly(ijk, regionId); + } + } + } +} + + +// Note that we must use ValueType::value_type or else Visual C++ gets confused +// thinking that it is a constructor. +template +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 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 SignData +{ +public: + typedef typename TreeT::ValueType ValueT; + typedef tree::ValueAccessor AccessorT; + + typedef typename TreeT::template ValueConverter::Type IntTreeT; + typedef tree::ValueAccessor IntAccessorT; + + typedef typename TreeT::template ValueConverter::Type Int16TreeT; + typedef tree::ValueAccessor Int16AccessorT; + + ////////// + + + SignData(const TreeT& distTree, const LeafManagerT& leafs, ValueT iso); + + void run(bool threaded = true); + + typename Int16TreeT::Ptr signTree() const { return mSignTree; } + typename IntTreeT::Ptr idxTree() const { return mIdxTree; } + + ////////// + + SignData(SignData&, tbb::split); + void operator()(const tbb::blocked_range&); + void join(const SignData& rhs) + { + mSignTree->merge(*rhs.mSignTree); + mIdxTree->merge(*rhs.mIdxTree); + } + +private: + + const TreeT& mDistTree; + AccessorT mDistAcc; + + const LeafManagerT& mLeafs; + ValueT mIsovalue; + + typename Int16TreeT::Ptr mSignTree; + Int16AccessorT mSignAcc; + + typename IntTreeT::Ptr mIdxTree; + IntAccessorT mIdxAcc; + +}; + + +template +SignData::SignData(const TreeT& distTree, + const LeafManagerT& leafs, ValueT iso) + : mDistTree(distTree) + , mDistAcc(mDistTree) + , mLeafs(leafs) + , mIsovalue(iso) + , mSignTree(new Int16TreeT(0)) + , mSignAcc(*mSignTree) + , mIdxTree(new IntTreeT(int(util::INVALID_IDX))) + , mIdxAcc(*mIdxTree) +{ +} + + +template +SignData::SignData(SignData& rhs, tbb::split) + : mDistTree(rhs.mDistTree) + , mDistAcc(mDistTree) + , mLeafs(rhs.mLeafs) + , mIsovalue(rhs.mIsovalue) + , mSignTree(new Int16TreeT(0)) + , mSignAcc(*mSignTree) + , mIdxTree(new IntTreeT(int(util::INVALID_IDX))) + , mIdxAcc(*mIdxTree) +{ +} + + +template +void +SignData::run(bool threaded) +{ + if (threaded) tbb::parallel_reduce(mLeafs.getRange(), *this); + else (*this)(mLeafs.getRange()); +} + +template +void +SignData::operator()(const tbb::blocked_range& range) +{ + typedef typename Int16TreeT::LeafNodeType Int16LeafT; + typedef typename IntTreeT::LeafNodeType IntLeafT; + typename LeafManagerT::TreeType::LeafNodeType::ValueOnCIter iter; + unsigned char signs, face; + Coord ijk, coord; + + typename internal::UniquePtr::type signLeafPt(new Int16LeafT(ijk, 0)); + + for (size_t n = range.begin(); n != range.end(); ++n) { + + bool collectedData = false; + + coord = mLeafs.leaf(n).origin(); + + if (!signLeafPt.get()) signLeafPt.reset(new Int16LeafT(coord, 0)); + else signLeafPt->setOrigin(coord); + + const typename TreeT::LeafNodeType *leafPt = mDistAcc.probeConstLeaf(coord); + + coord.offset(TreeT::LeafNodeType::DIM - 1); + + for (iter = mLeafs.leaf(n).cbeginValueOn(); iter; ++iter) { + + ijk = iter.getCoord(); + + if (leafPt && ijk[0] < coord[0] && ijk[1] < coord[1] && ijk[2] < coord[2]) { + signs = evalCellSigns(*leafPt, iter.pos(), mIsovalue); + } else { + signs = evalCellSigns(mDistAcc, ijk, mIsovalue); + } + + if (signs != 0 && signs != 0xFF) { + Int16 flags = (signs & 0x1) ? INSIDE : 0; + + if (bool(signs & 0x1) != bool(signs & 0x2)) flags |= XEDGE; + if (bool(signs & 0x1) != bool(signs & 0x10)) flags |= YEDGE; + if (bool(signs & 0x1) != bool(signs & 0x8)) flags |= ZEDGE; + + face = internal::sAmbiguousFace[signs]; + if (face != 0) correctCellSigns(signs, face, mDistAcc, ijk, mIsovalue); + + flags = Int16(flags | Int16(signs)); + + signLeafPt->setValue(ijk, flags); + collectedData = true; + } + } + + if (collectedData) { + + IntLeafT* idxLeaf = mIdxAcc.touchLeaf(coord); + idxLeaf->topologyUnion(*signLeafPt); + typename IntLeafT::ValueOnIter it = idxLeaf->beginValueOn(); + for (; it; ++it) { + it.setValue(0); + } + + mSignAcc.addLeaf(signLeafPt.release()); + } + } +} + + +//////////////////////////////////////// + + +/// @brief Counts the total number of points per leaf, accounts for cells with multiple points. +class CountPoints +{ +public: + CountPoints(std::vector& pointList) : mPointList(pointList) {} + + template + void operator()(LeafNodeType &leaf, size_t leafIndex) const + { + size_t points = 0; + + typename LeafNodeType::ValueOnCIter iter = leaf.cbeginValueOn(); + for (; iter; ++iter) { + points += size_t(sEdgeGroupTable[(SIGNS & iter.getValue())][0]); + } + + mPointList[leafIndex] = points; + } + +private: + std::vector& mPointList; +}; + + +/// @brief Computes the point list indices for the index tree. +template +class MapPoints +{ +public: + typedef tree::ValueAccessor Int16AccessorT; + + MapPoints(std::vector& pointList, const Int16TreeT& signTree) + : mPointList(pointList) + , mSignAcc(signTree) + { + } + + template + void operator()(LeafNodeType &leaf, size_t leafIndex) const + { + size_t ptnIdx = mPointList[leafIndex]; + typename LeafNodeType::ValueOnIter iter = leaf.beginValueOn(); + + const typename Int16TreeT::LeafNodeType *signLeafPt = + mSignAcc.probeConstLeaf(leaf.origin()); + + for (; iter; ++iter) { + iter.setValue(static_cast(ptnIdx)); + unsigned signs = SIGNS & signLeafPt->getValue(iter.pos()); + ptnIdx += size_t(sEdgeGroupTable[signs][0]); + } + } + +private: + std::vector& mPointList; + Int16AccessorT mSignAcc; +}; + + +/// @brief Counts the total number of points per collapsed region +template +class CountRegions +{ +public: + typedef tree::ValueAccessor IntAccessorT; + typedef typename IntTreeT::LeafNodeType IntLeafT; + + CountRegions(IntTreeT& idxTree, std::vector& regions) + : mIdxAcc(idxTree) + , mRegions(regions) + { + } + + template + void operator()(LeafNodeType &leaf, size_t leafIndex) const + { + + size_t regions = 0; + + IntLeafT tmpLeaf(*mIdxAcc.probeConstLeaf(leaf.origin())); + + typename IntLeafT::ValueOnIter iter = tmpLeaf.beginValueOn(); + for (; iter; ++iter) { + if(iter.getValue() == 0) { + iter.setValueOff(); + regions += size_t(sEdgeGroupTable[(SIGNS & leaf.getValue(iter.pos()))][0]); + } + } + + int onVoxelCount = int(tmpLeaf.onVoxelCount()); + while (onVoxelCount > 0) { + ++regions; + iter = tmpLeaf.beginValueOn(); + int regionId = iter.getValue(); + for (; iter; ++iter) { + if (iter.getValue() == regionId) { + iter.setValueOff(); + --onVoxelCount; + } + } + } + + mRegions[leafIndex] = regions; + } + +private: + IntAccessorT mIdxAcc; + std::vector& mRegions; +}; + + +//////////////////////////////////////// + + +// @brief linear interpolation. +inline double evalRoot(double v0, double v1, double iso) { return (iso - v0) / (v1 - v0); } + + +/// @brief Extracts the eight corner values for leaf inclusive cells. +template +inline void +collectCornerValues(const LeafT& leaf, const Index offset, std::vector& values) +{ + values[0] = double(leaf.getValue(offset)); // i, j, k + values[3] = double(leaf.getValue(offset + 1)); // i, j, k+1 + values[4] = double(leaf.getValue(offset + LeafT::DIM)); // i, j+1, k + values[7] = double(leaf.getValue(offset + LeafT::DIM + 1)); // i, j+1, k+1 + values[1] = double(leaf.getValue(offset + (LeafT::DIM * LeafT::DIM))); // i+1, j, k + values[2] = double(leaf.getValue(offset + (LeafT::DIM * LeafT::DIM) + 1)); // i+1, j, k+1 + values[5] = double(leaf.getValue(offset + (LeafT::DIM * LeafT::DIM) + LeafT::DIM)); // i+1, j+1, k + values[6] = double(leaf.getValue(offset + (LeafT::DIM * LeafT::DIM) + LeafT::DIM + 1)); // i+1, j+1, k+1 +} + + +/// @brief Extracts the eight corner values for a cell starting at the given @ijk coordinate. +template +inline void +collectCornerValues(const AccessorT& acc, const Coord& ijk, std::vector& values) +{ + Coord 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 +} + + +/// @brief Computes the average cell point for a given edge group. +inline Vec3d +computePoint(const std::vector& values, unsigned char signs, + unsigned char edgeGroup, double iso) +{ + Vec3d avg(0.0, 0.0, 0.0); + int samples = 0; + + if (sEdgeGroupTable[signs][1] == edgeGroup) { // Edged: 0 - 1 + avg[0] += evalRoot(values[0], values[1], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][2] == edgeGroup) { // Edged: 1 - 2 + avg[0] += 1.0; + avg[2] += evalRoot(values[1], values[2], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][3] == edgeGroup) { // Edged: 3 - 2 + avg[0] += evalRoot(values[3], values[2], iso); + avg[2] += 1.0; + ++samples; + } + + if (sEdgeGroupTable[signs][4] == edgeGroup) { // Edged: 0 - 3 + avg[2] += evalRoot(values[0], values[3], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][5] == edgeGroup) { // Edged: 4 - 5 + avg[0] += evalRoot(values[4], values[5], iso); + avg[1] += 1.0; + ++samples; + } + + if (sEdgeGroupTable[signs][6] == edgeGroup) { // Edged: 5 - 6 + avg[0] += 1.0; + avg[1] += 1.0; + avg[2] += evalRoot(values[5], values[6], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][7] == edgeGroup) { // Edged: 7 - 6 + avg[0] += evalRoot(values[7], values[6], iso); + avg[1] += 1.0; + avg[2] += 1.0; + ++samples; + } + + if (sEdgeGroupTable[signs][8] == edgeGroup) { // Edged: 4 - 7 + avg[1] += 1.0; + avg[2] += evalRoot(values[4], values[7], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][9] == edgeGroup) { // Edged: 0 - 4 + avg[1] += evalRoot(values[0], values[4], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][10] == edgeGroup) { // Edged: 1 - 5 + avg[0] += 1.0; + avg[1] += evalRoot(values[1], values[5], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][11] == edgeGroup) { // Edged: 2 - 6 + avg[0] += 1.0; + avg[1] += evalRoot(values[2], values[6], iso); + avg[2] += 1.0; + ++samples; + } + + if (sEdgeGroupTable[signs][12] == edgeGroup) { // Edged: 3 - 7 + avg[1] += evalRoot(values[3], values[7], iso); + avg[2] += 1.0; + ++samples; + } + + if (samples > 1) { + double w = 1.0 / double(samples); + avg[0] *= w; + avg[1] *= w; + avg[2] *= w; + } + + return avg; +} + + +/// @brief Computes the average cell point for a given edge group, ignoring edge +/// samples present in the @c signsMask configuration. +inline int +computeMaskedPoint(Vec3d& avg, const std::vector& values, unsigned char signs, + unsigned char signsMask, unsigned char edgeGroup, double iso) +{ + avg = Vec3d(0.0, 0.0, 0.0); + int samples = 0; + + if (sEdgeGroupTable[signs][1] == edgeGroup + && sEdgeGroupTable[signsMask][1] == 0) { // Edged: 0 - 1 + avg[0] += evalRoot(values[0], values[1], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][2] == edgeGroup + && sEdgeGroupTable[signsMask][2] == 0) { // Edged: 1 - 2 + avg[0] += 1.0; + avg[2] += evalRoot(values[1], values[2], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][3] == edgeGroup + && sEdgeGroupTable[signsMask][3] == 0) { // Edged: 3 - 2 + avg[0] += evalRoot(values[3], values[2], iso); + avg[2] += 1.0; + ++samples; + } + + if (sEdgeGroupTable[signs][4] == edgeGroup + && sEdgeGroupTable[signsMask][4] == 0) { // Edged: 0 - 3 + avg[2] += evalRoot(values[0], values[3], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][5] == edgeGroup + && sEdgeGroupTable[signsMask][5] == 0) { // Edged: 4 - 5 + avg[0] += evalRoot(values[4], values[5], iso); + avg[1] += 1.0; + ++samples; + } + + if (sEdgeGroupTable[signs][6] == edgeGroup + && sEdgeGroupTable[signsMask][6] == 0) { // Edged: 5 - 6 + avg[0] += 1.0; + avg[1] += 1.0; + avg[2] += evalRoot(values[5], values[6], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][7] == edgeGroup + && sEdgeGroupTable[signsMask][7] == 0) { // Edged: 7 - 6 + avg[0] += evalRoot(values[7], values[6], iso); + avg[1] += 1.0; + avg[2] += 1.0; + ++samples; + } + + if (sEdgeGroupTable[signs][8] == edgeGroup + && sEdgeGroupTable[signsMask][8] == 0) { // Edged: 4 - 7 + avg[1] += 1.0; + avg[2] += evalRoot(values[4], values[7], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][9] == edgeGroup + && sEdgeGroupTable[signsMask][9] == 0) { // Edged: 0 - 4 + avg[1] += evalRoot(values[0], values[4], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][10] == edgeGroup + && sEdgeGroupTable[signsMask][10] == 0) { // Edged: 1 - 5 + avg[0] += 1.0; + avg[1] += evalRoot(values[1], values[5], iso); + ++samples; + } + + if (sEdgeGroupTable[signs][11] == edgeGroup + && sEdgeGroupTable[signsMask][11] == 0) { // Edged: 2 - 6 + avg[0] += 1.0; + avg[1] += evalRoot(values[2], values[6], iso); + avg[2] += 1.0; + ++samples; + } + + if (sEdgeGroupTable[signs][12] == edgeGroup + && sEdgeGroupTable[signsMask][12] == 0) { // Edged: 3 - 7 + avg[1] += evalRoot(values[3], values[7], iso); + avg[2] += 1.0; + ++samples; + } + + if (samples > 1) { + double w = 1.0 / double(samples); + avg[0] *= w; + avg[1] *= w; + avg[2] *= w; + } + + return samples; +} + + +/// @brief Computes the average cell point for a given edge group, by computing +/// convex weights based on the distance from the sample point @c p. +inline Vec3d +computeWeightedPoint(const Vec3d& p, const std::vector& values, + unsigned char signs, unsigned char edgeGroup, double iso) +{ + std::vector samples; + samples.reserve(8); + + std::vector weights; + weights.reserve(8); + + Vec3d avg(0.0, 0.0, 0.0); + + if (sEdgeGroupTable[signs][1] == edgeGroup) { // Edged: 0 - 1 + avg[0] = evalRoot(values[0], values[1], iso); + avg[1] = 0.0; + avg[2] = 0.0; + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][2] == edgeGroup) { // Edged: 1 - 2 + avg[0] = 1.0; + avg[1] = 0.0; + avg[2] = evalRoot(values[1], values[2], iso); + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][3] == edgeGroup) { // Edged: 3 - 2 + avg[0] = evalRoot(values[3], values[2], iso); + avg[1] = 0.0; + avg[2] = 1.0; + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][4] == edgeGroup) { // Edged: 0 - 3 + avg[0] = 0.0; + avg[1] = 0.0; + avg[2] = evalRoot(values[0], values[3], iso); + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][5] == edgeGroup) { // Edged: 4 - 5 + avg[0] = evalRoot(values[4], values[5], iso); + avg[1] = 1.0; + avg[2] = 0.0; + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][6] == edgeGroup) { // Edged: 5 - 6 + avg[0] = 1.0; + avg[1] = 1.0; + avg[2] = evalRoot(values[5], values[6], iso); + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][7] == edgeGroup) { // Edged: 7 - 6 + avg[0] = evalRoot(values[7], values[6], iso); + avg[1] = 1.0; + avg[2] = 1.0; + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][8] == edgeGroup) { // Edged: 4 - 7 + avg[0] = 0.0; + avg[1] = 1.0; + avg[2] = evalRoot(values[4], values[7], iso); + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][9] == edgeGroup) { // Edged: 0 - 4 + avg[0] = 0.0; + avg[1] = evalRoot(values[0], values[4], iso); + avg[2] = 0.0; + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][10] == edgeGroup) { // Edged: 1 - 5 + avg[0] = 1.0; + avg[1] = evalRoot(values[1], values[5], iso); + avg[2] = 0.0; + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][11] == edgeGroup) { // Edged: 2 - 6 + avg[0] = 1.0; + avg[1] = evalRoot(values[2], values[6], iso); + avg[2] = 1.0; + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + if (sEdgeGroupTable[signs][12] == edgeGroup) { // Edged: 3 - 7 + avg[0] = 0.0; + avg[1] = evalRoot(values[3], values[7], iso); + avg[2] = 1.0; + + samples.push_back(avg); + weights.push_back((avg-p).lengthSqr()); + } + + + double minWeight = std::numeric_limits::max(); + double maxWeight = -std::numeric_limits::max(); + + for (size_t i = 0, I = weights.size(); i < I; ++i) { + minWeight = std::min(minWeight, weights[i]); + maxWeight = std::max(maxWeight, weights[i]); + } + + const double offset = maxWeight + minWeight * 0.1; + for (size_t i = 0, I = weights.size(); i < I; ++i) { + weights[i] = offset - weights[i]; + } + + + double weightSum = 0.0; + for (size_t i = 0, I = weights.size(); i < I; ++i) { + weightSum += weights[i]; + } + + avg[0] = 0.0; + avg[1] = 0.0; + avg[2] = 0.0; + + if (samples.size() > 1) { + for (size_t i = 0, I = samples.size(); i < I; ++i) { + avg += samples[i] * (weights[i] / weightSum); + } + } else { + avg = samples.front(); + } + + return avg; +} + + +/// @brief Computes the average cell points defined by the sign configuration +/// @c signs and the given corner values @c values. +inline void +computeCellPoints(std::vector& points, + const std::vector& values, unsigned char signs, double iso) +{ + for (size_t n = 1, N = sEdgeGroupTable[signs][0] + 1; n < N; ++n) { + points.push_back(computePoint(values, signs, uint8_t(n), iso)); + } +} + + +/// @brief Given a sign configuration @c lhsSigns and an edge group @c groupId, +/// finds the corresponding edge group in a different sign configuration +/// @c rhsSigns. Returns -1 if no match is found. +inline int +matchEdgeGroup(unsigned char groupId, unsigned char lhsSigns, unsigned char rhsSigns) +{ + int id = -1; + for (size_t i = 1; i <= 12; ++i) { + if (sEdgeGroupTable[lhsSigns][i] == groupId && sEdgeGroupTable[rhsSigns][i] != 0) { + id = sEdgeGroupTable[rhsSigns][i]; + break; + } + } + return id; +} + + +/// @brief Computes the average cell points defined by the sign configuration +/// @c signs and the given corner values @c values. Combines data from +/// two different level sets to eliminate seam lines when meshing +/// fractured segments. +inline void +computeCellPoints(std::vector& points, std::vector& weightedPointMask, + const std::vector& lhsValues, const std::vector& rhsValues, + unsigned char lhsSigns, unsigned char rhsSigns, + double iso, size_t pointIdx, const boost::scoped_array& seamPoints) +{ + for (size_t n = 1, N = sEdgeGroupTable[lhsSigns][0] + 1; n < N; ++n) { + + int id = matchEdgeGroup(uint8_t(n), lhsSigns, rhsSigns); + + if (id != -1) { + + const unsigned char e = uint8_t(id); + uint32_t& quantizedPoint = seamPoints[pointIdx + (id - 1)]; + + if ((quantizedPoint & MASK_DIRTY_BIT) && !(quantizedPoint & MASK_INVALID_BIT)) { + Vec3d p = unpackPoint(quantizedPoint); + points.push_back(computeWeightedPoint(p, rhsValues, rhsSigns, e, iso)); + weightedPointMask.push_back(true); + } else { + points.push_back(computePoint(rhsValues, rhsSigns, e, iso)); + weightedPointMask.push_back(false); + } + + } else { + points.push_back(computePoint(lhsValues, lhsSigns, uint8_t(n), iso)); + weightedPointMask.push_back(false); + } + } +} + + +template +class GenPoints +{ +public: + typedef tree::ValueAccessor AccessorT; + + typedef typename TreeT::template ValueConverter::Type IntTreeT; + typedef tree::ValueAccessor IntAccessorT; + typedef tree::ValueAccessor IntCAccessorT; + + typedef typename TreeT::template ValueConverter::Type Int16TreeT; + typedef tree::ValueAccessor Int16CAccessorT; + + typedef boost::scoped_array QuantizedPointList; + + ////////// + + + GenPoints(const LeafManagerT& signLeafs, const TreeT& distTree, + IntTreeT& idxTree, PointList& points, std::vector& indices, + const math::Transform& xform, double iso); + + void run(bool threaded = true); + + void setRefData(const Int16TreeT* refSignTree = NULL, const TreeT* refDistTree = NULL, + IntTreeT* refIdxTree = NULL, const QuantizedPointList* seamPoints = NULL, + std::vector* mSeamPointMaskPt = NULL); + + ////////// + + + void operator()(const tbb::blocked_range&) const; + +private: + const LeafManagerT& mSignLeafs; + + AccessorT mDistAcc; + IntTreeT& mIdxTree; + + PointList& mPoints; + std::vector& mIndices; + const math::Transform& mTransform; + const double mIsovalue; + + // reference data + const Int16TreeT *mRefSignTreePt; + const TreeT* mRefDistTreePt; + const IntTreeT* mRefIdxTreePt; + const QuantizedPointList* mSeamPointsPt; + std::vector* mSeamPointMaskPt; +}; + + +template +GenPoints::GenPoints(const LeafManagerT& signLeafs, + const TreeT& distTree, IntTreeT& idxTree, PointList& points, + std::vector& indices, const math::Transform& xform, double iso) + : mSignLeafs(signLeafs) + , mDistAcc(distTree) + , mIdxTree(idxTree) + , mPoints(points) + , mIndices(indices) + , mTransform(xform) + , mIsovalue(iso) + , mRefSignTreePt(NULL) + , mRefDistTreePt(NULL) + , mRefIdxTreePt(NULL) + , mSeamPointsPt(NULL) + , mSeamPointMaskPt(NULL) +{ +} + + +template +void +GenPoints::run(bool threaded) +{ + if (threaded) tbb::parallel_for(mSignLeafs.getRange(), *this); + else (*this)(mSignLeafs.getRange()); +} + + +template +void +GenPoints::setRefData( + const Int16TreeT *refSignTree, + const TreeT *refDistTree, + IntTreeT* refIdxTree, + const QuantizedPointList* seamPoints, + std::vector* seamPointMask) +{ + mRefSignTreePt = refSignTree; + mRefDistTreePt = refDistTree; + mRefIdxTreePt = refIdxTree; + mSeamPointsPt = seamPoints; + mSeamPointMaskPt = seamPointMask; +} + + +template +void +GenPoints::operator()(const tbb::blocked_range& range) const +{ + typename IntTreeT::LeafNodeType::ValueOnIter iter; + unsigned char signs, refSigns; + Index offset; + Coord ijk, coord; + std::vector points(4); + std::vector weightedPointMask(4); + std::vector values(8), refValues(8); + + + IntAccessorT idxAcc(mIdxTree); + + // reference data accessors + boost::scoped_ptr refSignAcc; + if (mRefSignTreePt) refSignAcc.reset(new Int16CAccessorT(*mRefSignTreePt)); + + boost::scoped_ptr refIdxAcc; + if (mRefIdxTreePt) refIdxAcc.reset(new IntCAccessorT(*mRefIdxTreePt)); + + boost::scoped_ptr refDistAcc; + if (mRefDistTreePt) refDistAcc.reset(new AccessorT(*mRefDistTreePt)); + + + for (size_t n = range.begin(); n != range.end(); ++n) { + + coord = mSignLeafs.leaf(n).origin(); + + const typename TreeT::LeafNodeType *leafPt = mDistAcc.probeConstLeaf(coord); + typename IntTreeT::LeafNodeType *idxLeafPt = idxAcc.probeLeaf(coord); + + + // reference data leafs + const typename Int16TreeT::LeafNodeType *refSignLeafPt = NULL; + if (refSignAcc) refSignLeafPt = refSignAcc->probeConstLeaf(coord); + + const typename IntTreeT::LeafNodeType *refIdxLeafPt = NULL; + if (refIdxAcc) refIdxLeafPt = refIdxAcc->probeConstLeaf(coord); + + const typename TreeT::LeafNodeType *refDistLeafPt = NULL; + if (refDistAcc) refDistLeafPt = refDistAcc->probeConstLeaf(coord); + + + // generate cell points + size_t ptnIdx = mIndices[n]; + coord.offset(TreeT::LeafNodeType::DIM - 1); + + + + for (iter = idxLeafPt->beginValueOn(); iter; ++iter) { + + if(iter.getValue() != 0) continue; + + iter.setValue(static_cast(ptnIdx)); + iter.setValueOff(); + offset = iter.pos(); + ijk = iter.getCoord(); + + const bool inclusiveCell = ijk[0] < coord[0] && ijk[1] < coord[1] && ijk[2] < coord[2]; + + const Int16& flags = mSignLeafs.leaf(n).getValue(offset); + signs = uint8_t(SIGNS & flags); + refSigns = 0; + + if ((flags & SEAM) && refSignLeafPt && refIdxLeafPt) { + if (refSignLeafPt->isValueOn(offset)) { + refSigns = uint8_t(SIGNS & refSignLeafPt->getValue(offset)); + } + } + + + if (inclusiveCell) collectCornerValues(*leafPt, offset, values); + else collectCornerValues(mDistAcc, ijk, values); + + + points.clear(); + weightedPointMask.clear(); + + if (refSigns == 0) { + computeCellPoints(points, values, signs, mIsovalue); + } else { + + if (inclusiveCell) collectCornerValues(*refDistLeafPt, offset, refValues); + else collectCornerValues(*refDistAcc, ijk, refValues); + + computeCellPoints(points, weightedPointMask, values, refValues, signs, refSigns, + mIsovalue, refIdxLeafPt->getValue(offset), *mSeamPointsPt); + } + + + for (size_t i = 0, I = points.size(); i < I; ++i) { + + // offset by cell-origin + points[i][0] += double(ijk[0]); + points[i][1] += double(ijk[1]); + points[i][2] += double(ijk[2]); + + + points[i] = mTransform.indexToWorld(points[i]); + + mPoints[ptnIdx][0] = float(points[i][0]); + mPoints[ptnIdx][1] = float(points[i][1]); + mPoints[ptnIdx][2] = float(points[i][2]); + + if (mSeamPointMaskPt && !weightedPointMask.empty() && weightedPointMask[i]) { + (*mSeamPointMaskPt)[ptnIdx] = 1; + } + + ++ptnIdx; + } + } + + // generate collapsed region points + int onVoxelCount = int(idxLeafPt->onVoxelCount()); + while (onVoxelCount > 0) { + + iter = idxLeafPt->beginValueOn(); + int regionId = iter.getValue(), count = 0; + + Vec3d avg(0.0), point; + + for (; iter; ++iter) { + if (iter.getValue() != regionId) continue; + + iter.setValue(static_cast(ptnIdx)); + iter.setValueOff(); + --onVoxelCount; + + ijk = iter.getCoord(); + offset = iter.pos(); + + signs = uint8_t(SIGNS & mSignLeafs.leaf(n).getValue(offset)); + + if (ijk[0] < coord[0] && ijk[1] < coord[1] && ijk[2] < coord[2]) { + collectCornerValues(*leafPt, offset, values); + } else { + collectCornerValues(mDistAcc, ijk, values); + } + + points.clear(); + computeCellPoints(points, values, signs, mIsovalue); + + avg[0] += double(ijk[0]) + points[0][0]; + avg[1] += double(ijk[1]) + points[0][1]; + avg[2] += double(ijk[2]) + points[0][2]; + + ++count; + } + + + if (count > 1) { + double w = 1.0 / double(count); + avg[0] *= w; + avg[1] *= w; + avg[2] *= w; + } + + avg = mTransform.indexToWorld(avg); + + mPoints[ptnIdx][0] = float(avg[0]); + mPoints[ptnIdx][1] = float(avg[1]); + mPoints[ptnIdx][2] = float(avg[2]); + + ++ptnIdx; + } + } +} + + +//////////////////////////////////////// + + +template +class SeamWeights +{ +public: + typedef tree::ValueAccessor AccessorT; + + typedef typename TreeT::template ValueConverter::Type IntTreeT; + typedef tree::ValueAccessor IntAccessorT; + + typedef typename TreeT::template ValueConverter::Type Int16TreeT; + typedef tree::ValueAccessor Int16AccessorT; + + typedef boost::scoped_array QuantizedPointList; + + ////////// + + SeamWeights(const TreeT& distTree, const Int16TreeT& refSignTree, + IntTreeT& refIdxTree, QuantizedPointList& points, double iso); + + template + void operator()(LeafNodeType &signLeaf, size_t leafIndex) const; + +private: + AccessorT mDistAcc; + Int16AccessorT mRefSignAcc; + IntAccessorT mRefIdxAcc; + + QuantizedPointList& mPoints; + const double mIsovalue; +}; + + +template +SeamWeights::SeamWeights(const TreeT& distTree, const Int16TreeT& refSignTree, + IntTreeT& refIdxTree, QuantizedPointList& points, double iso) + : mDistAcc(distTree) + , mRefSignAcc(refSignTree) + , mRefIdxAcc(refIdxTree) + , mPoints(points) + , mIsovalue(iso) +{ +} + + +template +template +void +SeamWeights::operator()(LeafNodeType &signLeaf, size_t /*leafIndex*/) const +{ + Coord coord = signLeaf.origin(); + const typename Int16TreeT::LeafNodeType *refSignLeafPt = mRefSignAcc.probeConstLeaf(coord); + + if (!refSignLeafPt) return; + + const typename TreeT::LeafNodeType *distLeafPt = mDistAcc.probeConstLeaf(coord); + const typename IntTreeT::LeafNodeType *refIdxLeafPt = mRefIdxAcc.probeConstLeaf(coord); + + std::vector values(8); + unsigned char lhsSigns, rhsSigns; + Vec3d point; + Index offset; + + Coord ijk; + coord.offset(TreeT::LeafNodeType::DIM - 1); + + typename LeafNodeType::ValueOnCIter iter = signLeaf.cbeginValueOn(); + for (; iter; ++iter) { + + offset = iter.pos(); + ijk = iter.getCoord(); + + const bool inclusiveCell = ijk[0] < coord[0] && ijk[1] < coord[1] && ijk[2] < coord[2]; + + if ((iter.getValue() & SEAM) && refSignLeafPt->isValueOn(offset)) { + + lhsSigns = uint8_t(SIGNS & iter.getValue()); + rhsSigns = uint8_t(SIGNS & refSignLeafPt->getValue(offset)); + + + if (inclusiveCell) { + collectCornerValues(*distLeafPt, offset, values); + } else { + collectCornerValues(mDistAcc, ijk, values); + } + + + for (size_t n = 1, N = sEdgeGroupTable[lhsSigns][0] + 1; n < N; ++n) { + + int id = matchEdgeGroup(uint8_t(n), lhsSigns, rhsSigns); + + if (id != -1) { + + uint32_t& data = mPoints[refIdxLeafPt->getValue(offset) + (id - 1)]; + + if (!(data & MASK_DIRTY_BIT)) { + + int smaples = computeMaskedPoint( + point, values, lhsSigns, rhsSigns, uint8_t(n), mIsovalue); + + if (smaples > 0) data = packPoint(point); + else data = MASK_INVALID_BIT; + + data |= MASK_DIRTY_BIT; + } + } + } + } + } +} + + +//////////////////////////////////////// + + +template +class MergeVoxelRegions +{ +public: + typedef typename TreeT::ValueType ValueT; + typedef tree::ValueAccessor AccessorT; + + typedef typename TreeT::template ValueConverter::Type IntTreeT; + typedef tree::ValueAccessor IntAccessorT; + + typedef typename TreeT::template ValueConverter::Type BoolTreeT; + + typedef typename LeafManagerT::TreeType::template ValueConverter::Type Int16TreeT; + typedef tree::ValueAccessor Int16AccessorT; + + typedef typename TreeT::template ValueConverter::Type FloatTreeT; + typedef Grid FloatGridT; + + + ////////// + + MergeVoxelRegions(const LeafManagerT& signLeafs, const Int16TreeT& signTree, + const TreeT& distTree, IntTreeT& idxTree, ValueT iso, ValueT adaptivity); + + void run(bool threaded = true); + + void setSpatialAdaptivity( + const math::Transform& distGridXForm, const FloatGridT& adaptivityField); + + void setAdaptivityMask(const BoolTreeT* mask); + + void setRefData(const Int16TreeT* signTree, ValueT adaptivity); + + ////////// + + + void operator()(const tbb::blocked_range&) const; + +private: + + const LeafManagerT& mSignLeafs; + + const Int16TreeT& mSignTree; + Int16AccessorT mSignAcc; + + const TreeT& mDistTree; + AccessorT mDistAcc; + + IntTreeT& mIdxTree; + ValueT mIsovalue, mSurfaceAdaptivity, mInternalAdaptivity; + + const math::Transform* mTransform; + const FloatGridT* mAdaptivityGrid; + const BoolTreeT* mAdaptivityMask; + + const Int16TreeT* mRefSignTree; +}; + + +template +MergeVoxelRegions::MergeVoxelRegions( + const LeafManagerT& signLeafs, const Int16TreeT& signTree, + const TreeT& distTree, IntTreeT& idxTree, ValueT iso, ValueT adaptivity) + : mSignLeafs(signLeafs) + , mSignTree(signTree) + , mSignAcc(mSignTree) + , mDistTree(distTree) + , mDistAcc(mDistTree) + , mIdxTree(idxTree) + , mIsovalue(iso) + , mSurfaceAdaptivity(adaptivity) + , mInternalAdaptivity(adaptivity) + , mTransform(NULL) + , mAdaptivityGrid(NULL) + , mAdaptivityMask(NULL) + , mRefSignTree(NULL) +{ +} + + +template +void +MergeVoxelRegions::run(bool threaded) +{ + if (threaded) tbb::parallel_for(mSignLeafs.getRange(), *this); + else (*this)(mSignLeafs.getRange()); +} + + +template +void +MergeVoxelRegions::setSpatialAdaptivity( + const math::Transform& distGridXForm, const FloatGridT& adaptivityField) +{ + mTransform = &distGridXForm; + mAdaptivityGrid = &adaptivityField; +} + + +template +void +MergeVoxelRegions::setAdaptivityMask(const BoolTreeT* mask) +{ + mAdaptivityMask = mask; +} + +template +void +MergeVoxelRegions::setRefData(const Int16TreeT* signTree, ValueT adaptivity) +{ + mRefSignTree = signTree; + mInternalAdaptivity = adaptivity; +} + + +template +void +MergeVoxelRegions::operator()(const tbb::blocked_range& range) const +{ + typedef math::Vec3 Vec3T; + + typedef typename TreeT::LeafNodeType LeafT; + typedef typename IntTreeT::LeafNodeType IntLeafT; + typedef typename BoolTreeT::LeafNodeType BoolLeafT; + typedef typename LeafT::template ValueConverter::Type Vec3LeafT; + + const int LeafDim = LeafT::DIM; + + IntAccessorT idxAcc(mIdxTree); + + typename LeafManagerT::TreeType::LeafNodeType::ValueOnCIter iter; + + typedef typename tree::ValueAccessor FloatTreeCAccessorT; + boost::scoped_ptr adaptivityAcc; + if (mAdaptivityGrid) { + adaptivityAcc.reset(new FloatTreeCAccessorT(mAdaptivityGrid->tree())); + } + + typedef typename tree::ValueAccessor Int16TreeCAccessorT; + boost::scoped_ptr refAcc; + if (mRefSignTree) { + refAcc.reset(new Int16TreeCAccessorT(*mRefSignTree)); + } + + typedef typename tree::ValueAccessor BoolTreeCAccessorT; + boost::scoped_ptr maskAcc; + if (mAdaptivityMask) { + maskAcc.reset(new BoolTreeCAccessorT(*mAdaptivityMask)); + } + + + BoolLeafT mask; + Vec3LeafT gradients; + Coord ijk, end; + + for (size_t n = range.begin(); n != range.end(); ++n) { + + mask.setValuesOff(); + + const Coord& origin = mSignLeafs.leaf(n).origin(); + + ValueT adaptivity = (refAcc && !refAcc->probeConstLeaf(origin)) ? + mInternalAdaptivity : mSurfaceAdaptivity; + + IntLeafT& idxLeaf = *idxAcc.probeLeaf(origin); + + end[0] = origin[0] + LeafDim; + end[1] = origin[1] + LeafDim; + end[2] = origin[2] + LeafDim; + + // Mask off seam line adjacent voxels + if (maskAcc) { + const BoolLeafT* maskLeaf = maskAcc->probeConstLeaf(origin); + if (maskLeaf != NULL) { + typename BoolLeafT::ValueOnCIter it; + for (it = maskLeaf->cbeginValueOn(); it; ++it) { + mask.setActiveState(it.getCoord() & ~1u, true); + } + } + } + + // Set region adaptivity + LeafT adaptivityLeaf(origin, adaptivity); + if (mAdaptivityGrid) { + for (Index offset = 0; offset < LeafT::NUM_VALUES; ++offset) { + ijk = adaptivityLeaf.offsetToGlobalCoord(offset); + Vec3d xyz = mAdaptivityGrid->transform().worldToIndex( + mTransform->indexToWorld(ijk)); + ValueT tmpA = ValueT(adaptivityAcc->getValue(util::nearestCoord(xyz))); + adaptivityLeaf.setValueOnly(offset, tmpA * adaptivity); + } + } + + // Mask off ambiguous voxels + for (iter = mSignLeafs.leaf(n).cbeginValueOn(); iter; ++iter) { + unsigned char signs = static_cast(SIGNS & int(iter.getValue())); + if (!sAdaptable[signs] || sEdgeGroupTable[signs][0] > 1) { + mask.setActiveState(iter.getCoord() & ~1u, true); + } + } + + // Mask off topologically ambiguous 2x2x2 voxel sub-blocks + int dim = 2; + 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 (!mask.isValueOn(ijk) & isNonManifold(mDistAcc, ijk, mIsovalue, dim)) { + mask.setActiveState(ijk, true); + } + } + } + } + + // Compute the gradient for the remaining voxels + gradients.setValuesOff(); + for (iter = mSignLeafs.leaf(n).cbeginValueOn(); iter; ++iter) { + ijk = iter.getCoord(); + if(!mask.isValueOn(ijk & ~1u)) { + Vec3T dir(math::ISGradient::result(mDistAcc, ijk)); + dir.normalize(); + gradients.setValueOn(iter.pos(), dir); + } + } + + // Merge regions + int regionId = 1; + for ( ; dim <= LeafDim; dim = dim << 1) { + const unsigned coordMask = ~((dim << 1) - 1); + 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) { + + adaptivity = adaptivityLeaf.getValue(ijk); + + if (mask.isValueOn(ijk) || isNonManifold(mDistAcc, ijk, mIsovalue, dim) + || !isMergable(gradients, ijk, dim, adaptivity)) { + mask.setActiveState(ijk & coordMask, true); + } else { + mergeVoxels(idxLeaf, ijk, dim, regionId++); + } + } + } + } + } + } +} + + +//////////////////////////////////////// + + +// Constructs qudas +struct UniformPrimBuilder +{ + UniformPrimBuilder(): 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() + { + mPolygonPool->trimQuads(mIdx); + } + +private: + size_t mIdx; + PolygonPool* mPolygonPool; +}; + + +// Constructs qudas and triangles +struct AdaptivePrimBuilder +{ + AdaptivePrimBuilder() : mQuadIdx(0), mTriangleIdx(0), mPolygonPool(NULL) {} + + void init(const size_t upperBound, PolygonPool& polygonPool) + { + mPolygonPool = &polygonPool; + mPolygonPool->resetQuads(upperBound); + mPolygonPool->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]) { + mPolygonPool->quadFlags(mQuadIdx) = flags; + addQuad(verts, reverse); + } else if ( + verts[0] == verts[3] && + verts[1] != verts[2] && + verts[1] != verts[0] && + verts[2] != verts[0]) { + mPolygonPool->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]) { + mPolygonPool->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]) { + mPolygonPool->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]) { + mPolygonPool->triangleFlags(mTriangleIdx) = flags; + addTriangle(verts[0], verts[1], verts[2], reverse); + } + } + + + void done() + { + mPolygonPool->trimQuads(mQuadIdx, /*reallocate=*/true); + mPolygonPool->trimTrinagles(mTriangleIdx, /*reallocate=*/true); + } + +private: + + void addQuad(const Vec4I& verts, bool reverse) + { + if (!reverse) { + mPolygonPool->quad(mQuadIdx) = verts; + } else { + Vec4I& quad = mPolygonPool->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 = mPolygonPool->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; +}; + + +template +inline void +constructPolygons(Int16 flags, Int16 refFlags, const Vec4i& offsets, const Coord& ijk, + const SignAccT& signAcc, const IdxAccT& idxAcc, PrimBuilder& mesher, Index32 pointListSize) +{ + const Index32 v0 = idxAcc.getValue(ijk); + if (v0 == util::INVALID_IDX) return; + + char tag[2]; + tag[0] = (flags & SEAM) ? POLYFLAG_FRACTURE_SEAM : 0; + tag[1] = tag[0] | char(POLYFLAG_EXTERIOR); + + const bool isInside = flags & INSIDE; + + Coord coord; + openvdb::Vec4I quad; + unsigned char cell; + Index32 tmpIdx = 0; + + if (flags & XEDGE) { + + quad[0] = v0 + offsets[0]; + + // i, j-1, k + coord[0] = ijk[0]; + coord[1] = ijk[1] - 1; + coord[2] = ijk[2]; + + quad[1] = idxAcc.getValue(coord); + cell = uint8_t(SIGNS & signAcc.getValue(coord)); + if (sEdgeGroupTable[cell][0] > 1) { + tmpIdx = quad[1] + Index32(sEdgeGroupTable[cell][5] - 1); + if (tmpIdx < pointListSize) quad[1] = tmpIdx; + } + + // i, j-1, k-1 + coord[2] -= 1; + + quad[2] = idxAcc.getValue(coord); + cell = uint8_t(SIGNS & signAcc.getValue(coord)); + if (sEdgeGroupTable[cell][0] > 1) { + tmpIdx = quad[2] + Index32(sEdgeGroupTable[cell][7] - 1); + if (tmpIdx < pointListSize) quad[2] = tmpIdx; + } + + // i, j, k-1 + coord[1] = ijk[1]; + + quad[3] = idxAcc.getValue(coord); + cell = uint8_t(SIGNS & signAcc.getValue(coord)); + if (sEdgeGroupTable[cell][0] > 1) { + tmpIdx = quad[3] + Index32(sEdgeGroupTable[cell][3] - 1); + if (tmpIdx < pointListSize) quad[3] = tmpIdx; + } + + if (quad[1] != util::INVALID_IDX && + quad[2] != util::INVALID_IDX && quad[3] != util::INVALID_IDX) { + mesher.addPrim(quad, isInside, tag[bool(refFlags & XEDGE)]); + } + } + + + if (flags & YEDGE) { + + quad[0] = v0 + offsets[1]; + + // i, j, k-1 + coord[0] = ijk[0]; + coord[1] = ijk[1]; + coord[2] = ijk[2] - 1; + + quad[1] = idxAcc.getValue(coord); + cell = uint8_t(SIGNS & signAcc.getValue(coord)); + if (sEdgeGroupTable[cell][0] > 1) { + tmpIdx = quad[1] + Index32(sEdgeGroupTable[cell][12] - 1); + if (tmpIdx < pointListSize) quad[1] = tmpIdx; + } + + // i-1, j, k-1 + coord[0] -= 1; + + quad[2] = idxAcc.getValue(coord); + cell = uint8_t(SIGNS & signAcc.getValue(coord)); + if (sEdgeGroupTable[cell][0] > 1) { + tmpIdx = quad[2] + Index32(sEdgeGroupTable[cell][11] - 1); + if (tmpIdx < pointListSize) quad[2] = tmpIdx; + } + + // i-1, j, k + coord[2] = ijk[2]; + + quad[3] = idxAcc.getValue(coord); + cell = uint8_t(SIGNS & signAcc.getValue(coord)); + if (sEdgeGroupTable[cell][0] > 1) { + tmpIdx = quad[3] + Index32(sEdgeGroupTable[cell][10] - 1); + if (tmpIdx < pointListSize) quad[3] = tmpIdx; + } + + if (quad[1] != util::INVALID_IDX && + quad[2] != util::INVALID_IDX && quad[3] != util::INVALID_IDX) { + mesher.addPrim(quad, isInside, tag[bool(refFlags & YEDGE)]); + } + } + + if (flags & ZEDGE) { + + quad[0] = v0 + offsets[2]; + + // i, j-1, k + coord[0] = ijk[0]; + coord[1] = ijk[1] - 1; + coord[2] = ijk[2]; + + quad[1] = idxAcc.getValue(coord); + cell = uint8_t(SIGNS & signAcc.getValue(coord)); + if (sEdgeGroupTable[cell][0] > 1) { + tmpIdx = quad[1] + Index32(sEdgeGroupTable[cell][8] - 1); + if (tmpIdx < pointListSize) quad[1] = tmpIdx; + } + + // i-1, j-1, k + coord[0] -= 1; + + quad[2] = idxAcc.getValue(coord); + cell = uint8_t(SIGNS & signAcc.getValue(coord)); + if (sEdgeGroupTable[cell][0] > 1) { + tmpIdx = quad[2] + Index32(sEdgeGroupTable[cell][6] - 1); + if (tmpIdx < pointListSize) quad[2] = tmpIdx; + } + + // i-1, j, k + coord[1] = ijk[1]; + + quad[3] = idxAcc.getValue(coord); + cell = uint8_t(SIGNS & signAcc.getValue(coord)); + if (sEdgeGroupTable[cell][0] > 1) { + tmpIdx = quad[3] + Index32(sEdgeGroupTable[cell][2] - 1); + if (tmpIdx < pointListSize) quad[3] = tmpIdx; + } + + if (quad[1] != util::INVALID_IDX && + quad[2] != util::INVALID_IDX && quad[3] != util::INVALID_IDX) { + mesher.addPrim(quad, !isInside, tag[bool(refFlags & ZEDGE)]); + } + } +} + + +//////////////////////////////////////// + + +template +class GenPolygons +{ +public: + typedef typename LeafManagerT::TreeType::template ValueConverter::Type IntTreeT; + typedef typename LeafManagerT::TreeType::template ValueConverter::Type Int16TreeT; + + typedef tree::ValueAccessor IntAccessorT; + typedef tree::ValueAccessor Int16AccessorT; + + ////////// + + + GenPolygons(const LeafManagerT& signLeafs, const Int16TreeT& signTree, + const IntTreeT& idxTree, PolygonPoolList& polygons, Index32 pointListSize); + + void run(bool threaded = true); + + + void setRefSignTree(const Int16TreeT *r) { mRefSignTree = r; } + + ////////// + + + void operator()(const tbb::blocked_range&) const; + +private: + const LeafManagerT& mSignLeafs; + const Int16TreeT& mSignTree; + const IntTreeT& mIdxTree; + const PolygonPoolList& mPolygonPoolList; + const Index32 mPointListSize; + + const Int16TreeT *mRefSignTree; + }; + + +template +GenPolygons::GenPolygons(const LeafManagerT& signLeafs, + const Int16TreeT& signTree, const IntTreeT& idxTree, PolygonPoolList& polygons, + Index32 pointListSize) + : mSignLeafs(signLeafs) + , mSignTree(signTree) + , mIdxTree(idxTree) + , mPolygonPoolList(polygons) + , mPointListSize(pointListSize) + , mRefSignTree(NULL) +{ +} + +template +void +GenPolygons::run(bool threaded) +{ + if (threaded) tbb::parallel_for(mSignLeafs.getRange(), *this); + else (*this)(mSignLeafs.getRange()); +} + +template +void +GenPolygons::operator()( + const tbb::blocked_range& range) const +{ + typename LeafManagerT::TreeType::LeafNodeType::ValueOnCIter iter; + IntAccessorT idxAcc(mIdxTree); + Int16AccessorT signAcc(mSignTree); + + + PrimBuilder mesher; + size_t edgeCount; + Coord ijk, origin; + + + // reference data + boost::scoped_ptr refSignAcc; + if (mRefSignTree) refSignAcc.reset(new Int16AccessorT(*mRefSignTree)); + + + for (size_t n = range.begin(); n != range.end(); ++n) { + + origin = mSignLeafs.leaf(n).origin(); + + // Get an upper bound on the number of primitives. + edgeCount = 0; + iter = mSignLeafs.leaf(n).cbeginValueOn(); + for (; iter; ++iter) { + if (iter.getValue() & XEDGE) ++edgeCount; + if (iter.getValue() & YEDGE) ++edgeCount; + if (iter.getValue() & ZEDGE) ++edgeCount; + } + + if(edgeCount == 0) continue; + + mesher.init(edgeCount, mPolygonPoolList[n]); + + const typename Int16TreeT::LeafNodeType *signleafPt = signAcc.probeConstLeaf(origin); + const typename IntTreeT::LeafNodeType *idxLeafPt = idxAcc.probeConstLeaf(origin); + + if (!signleafPt || !idxLeafPt) continue; + + + const typename Int16TreeT::LeafNodeType *refSignLeafPt = NULL; + if (refSignAcc) refSignLeafPt = refSignAcc->probeConstLeaf(origin); + + Vec4i offsets; + + iter = mSignLeafs.leaf(n).cbeginValueOn(); + for (; iter; ++iter) { + ijk = iter.getCoord(); + + Int16 flags = iter.getValue(); + + if (!(flags & 0xE00)) continue; + + Int16 refFlags = 0; + if (refSignLeafPt) { + refFlags = refSignLeafPt->getValue(iter.pos()); + } + + offsets[0] = 0; + offsets[1] = 0; + offsets[2] = 0; + + const unsigned char cell = uint8_t(SIGNS & flags); + + if (sEdgeGroupTable[cell][0] > 1) { + offsets[0] = (sEdgeGroupTable[cell][1] - 1); + offsets[1] = (sEdgeGroupTable[cell][9] - 1); + offsets[2] = (sEdgeGroupTable[cell][4] - 1); + } + + if (ijk[0] > origin[0] && ijk[1] > origin[1] && ijk[2] > origin[2]) { + constructPolygons(flags, refFlags, offsets, ijk, + *signleafPt, *idxLeafPt, mesher, mPointListSize); + } else { + constructPolygons(flags, refFlags, offsets, ijk, + signAcc, idxAcc, mesher, mPointListSize); + } + } + + mesher.done(); + } +} + + +//////////////////////////////////////// + +// Masking and mesh partitioning + +struct PartOp +{ + + PartOp(size_t leafCount, size_t partitions, size_t activePart) + { + size_t leafSegments = leafCount / partitions; + mStart = leafSegments * activePart; + mEnd = activePart >= (partitions - 1) ? leafCount : mStart + leafSegments; + } + + template + void operator()(LeafNodeType &leaf, size_t leafIndex) const + { + if (leafIndex < mStart || leafIndex >= mEnd) leaf.setValuesOff(); + } + +private: + size_t mStart, mEnd; +}; + + +//////////////////////////////////////// + + +template +class PartGen +{ +public: + typedef tree::LeafManager LeafManagerT; + typedef typename SrcTreeT::template ValueConverter::Type BoolTreeT; + typedef tree::ValueAccessor BoolAccessorT; + + ////////// + + + PartGen(const LeafManagerT& leafs, size_t partitions, size_t activePart); + + void run(bool threaded = true); + + BoolTreeT& tree() { return mTree; } + + + ////////// + + PartGen(PartGen&, tbb::split); + void operator()(const tbb::blocked_range&); + void join(PartGen& rhs) { mTree.merge(rhs.mTree); } + +private: + const LeafManagerT& mLeafManager; + BoolTreeT mTree; + size_t mStart, mEnd; +}; + +template +PartGen::PartGen(const LeafManagerT& leafs, size_t partitions, size_t activePart) + : mLeafManager(leafs) + , mTree(false) + , mStart(0) + , mEnd(0) +{ + size_t leafCount = leafs.leafCount(); + size_t leafSegments = leafCount / partitions; + mStart = leafSegments * activePart; + mEnd = activePart >= (partitions - 1) ? leafCount : mStart + leafSegments; +} + +template +PartGen::PartGen(PartGen& rhs, tbb::split) + : mLeafManager(rhs.mLeafManager) + , mTree(false) + , mStart(rhs.mStart) + , mEnd(rhs.mEnd) +{ +} + + +template +void +PartGen::run(bool threaded) +{ + if (threaded) tbb::parallel_reduce(mLeafManager.getRange(), *this); + else (*this)(mLeafManager.getRange()); +} + + +template +void +PartGen::operator()(const tbb::blocked_range& range) +{ + Coord ijk; + BoolAccessorT acc(mTree); + + typedef typename BoolTreeT::LeafNodeType BoolLeafT; + typename SrcTreeT::LeafNodeType::ValueOnCIter iter; + + for (size_t n = range.begin(); n != range.end(); ++n) { + if (n < mStart || n >= mEnd) continue; + BoolLeafT* leaf = acc.touchLeaf(mLeafManager.leaf(n).origin()); + leaf->topologyUnion(mLeafManager.leaf(n)); + } +} + + +//////////////////////////////////////// + + +template +class GenSeamMask +{ +public: + typedef typename TreeT::template ValueConverter::Type BoolTreeT; + + ////////// + + GenSeamMask(const LeafManagerT& leafs, const TreeT& tree); + + void run(bool threaded = true); + + BoolTreeT& mask() { return mMaskTree; } + + ////////// + + GenSeamMask(GenSeamMask&, tbb::split); + void operator()(const tbb::blocked_range&); + void join(GenSeamMask& rhs) { mMaskTree.merge(rhs.mMaskTree); } + +private: + + const LeafManagerT& mLeafManager; + const TreeT& mTree; + + BoolTreeT mMaskTree; +}; + + +template +GenSeamMask::GenSeamMask(const LeafManagerT& leafs, const TreeT& tree) + : mLeafManager(leafs) + , mTree(tree) + , mMaskTree(false) +{ +} + + +template +GenSeamMask::GenSeamMask(GenSeamMask& rhs, tbb::split) + : mLeafManager(rhs.mLeafManager) + , mTree(rhs.mTree) + , mMaskTree(false) +{ +} + + +template +void +GenSeamMask::run(bool threaded) +{ + if (threaded) tbb::parallel_reduce(mLeafManager.getRange(), *this); + else (*this)(mLeafManager.getRange()); +} + + +template +void +GenSeamMask::operator()(const tbb::blocked_range& range) +{ + Coord ijk; + tree::ValueAccessor acc(mTree); + tree::ValueAccessor maskAcc(mMaskTree); + + typename LeafManagerT::TreeType::LeafNodeType::ValueOnCIter it; + + for (size_t n = range.begin(); n != range.end(); ++n) { + + it = mLeafManager.leaf(n).cbeginValueOn(); + + for (; it; ++it) { + + ijk = it.getCoord(); + + unsigned char rhsSigns = uint8_t(acc.getValue(ijk) & SIGNS); + + if (sEdgeGroupTable[rhsSigns][0] > 0) { + unsigned char lhsSigns = uint8_t(it.getValue() & SIGNS); + if (rhsSigns != lhsSigns) { + maskAcc.setValueOn(ijk); + } + } + } + } +} + + +//////////////////////////////////////// + + +template +class TagSeamEdges +{ +public: + typedef tree::ValueAccessor AccessorT; + + TagSeamEdges(const TreeT& tree) : mAcc(tree) {} + + template + void operator()(LeafNodeType &leaf, size_t/*leafIndex*/) const + { + const typename TreeT::LeafNodeType *maskLeaf = + mAcc.probeConstLeaf(leaf.origin()); + + if (!maskLeaf) return; + + typename LeafNodeType::ValueOnIter it = leaf.beginValueOn(); + + for (; it; ++it) { + + if (maskLeaf->isValueOn(it.pos())) { + it.setValue(it.getValue() | SEAM); + } + } + } + +private: + AccessorT mAcc; +}; + + + +template +struct MaskEdges +{ + typedef tree::ValueAccessor BoolAccessorT; + + MaskEdges(const BoolTreeT& valueMask) : mMaskAcc(valueMask) {} + + template + void operator()(LeafNodeType &leaf, size_t /*leafIndex*/) const + { + typename LeafNodeType::ValueOnIter it = leaf.beginValueOn(); + + const typename BoolTreeT::LeafNodeType * maskLeaf = + mMaskAcc.probeConstLeaf(leaf.origin()); + + if (maskLeaf) { + for (; it; ++it) { + if (!maskLeaf->isValueOn(it.pos())) { + it.setValue(0x1FF & it.getValue()); + } + } + } else { + for (; it; ++it) { + it.setValue(0x1FF & it.getValue()); + } + } + } + +private: + BoolAccessorT mMaskAcc; +}; + + +class FlagUsedPoints +{ +public: + ////////// + + FlagUsedPoints(const PolygonPoolList& polygons, size_t polyListCount, + std::vector& usedPointMask) + : mPolygons(polygons) + , mPolyListCount(polyListCount) + , mUsedPointMask(usedPointMask) + { + } + + void run(bool threaded = true) + { + if (threaded) { + tbb::parallel_for(tbb::blocked_range(0, mPolyListCount), *this); + } else { + (*this)(tbb::blocked_range(0, mPolyListCount)); + } + } + + ////////// + + void operator()(const tbb::blocked_range& range) const + { + // Concurrent writes to same memory address can occur, but + // all threads are writing the same value and char is atomic. + for (size_t n = range.begin(); n != range.end(); ++n) { + const PolygonPool& polygons = mPolygons[n]; + for (size_t i = 0; i < polygons.numQuads(); ++i) { + const Vec4I& quad = polygons.quad(i); + mUsedPointMask[quad[0]] = 1; + mUsedPointMask[quad[1]] = 1; + mUsedPointMask[quad[2]] = 1; + mUsedPointMask[quad[3]] = 1; + } + + for (size_t i = 0; i < polygons.numTriangles(); ++i) { + const Vec3I& triangle = polygons.triangle(i); + mUsedPointMask[triangle[0]] = 1; + mUsedPointMask[triangle[1]] = 1; + mUsedPointMask[triangle[2]] = 1; + } + } + } + + +private: + const PolygonPoolList& mPolygons; + size_t mPolyListCount; + std::vector& mUsedPointMask; +}; + +class RemapIndices +{ +public: + ////////// + + RemapIndices(PolygonPoolList& polygons, + size_t polyListCount, const std::vector& indexMap) + : mPolygons(polygons) + , mPolyListCount(polyListCount) + , mIndexMap(indexMap) + { + } + + void run(bool threaded = true) + { + if (threaded) { + tbb::parallel_for(tbb::blocked_range(0, mPolyListCount), *this); + } else { + (*this)(tbb::blocked_range(0, mPolyListCount)); + } + } + + ////////// + + void operator()(const tbb::blocked_range& range) const + { + for (size_t n = range.begin(); n != range.end(); ++n) { + PolygonPool& polygons = mPolygons[n]; + for (size_t i = 0; i < polygons.numQuads(); ++i) { + Vec4I& quad = polygons.quad(i); + quad[0] = mIndexMap[quad[0]]; + quad[1] = mIndexMap[quad[1]]; + quad[2] = mIndexMap[quad[2]]; + quad[3] = mIndexMap[quad[3]]; + } + + for (size_t i = 0; i < polygons.numTriangles(); ++i) { + Vec3I& triangle = polygons.triangle(i); + triangle[0] = mIndexMap[triangle[0]]; + triangle[1] = mIndexMap[triangle[1]]; + triangle[2] = mIndexMap[triangle[2]]; + } + } + } + + +private: + PolygonPoolList& mPolygons; + size_t mPolyListCount; + const std::vector& mIndexMap; +}; + + +class MovePoints +{ +public: + ////////// + + MovePoints( + internal::UniquePtr::type& newPointList, + const PointList& oldPointList, + const std::vector& indexMap, + const std::vector& usedPointMask) + : mNewPointList(newPointList) + , mOldPointList(oldPointList) + , mIndexMap(indexMap) + , mUsedPointMask(usedPointMask) + { + } + + void run(bool threaded = true) + { + if (threaded) { + tbb::parallel_for(tbb::blocked_range(0, mIndexMap.size()), *this); + } else { + (*this)(tbb::blocked_range(0, mIndexMap.size())); + } + } + + ////////// + + void operator()(const tbb::blocked_range& range) const + { + for (size_t n = range.begin(); n != range.end(); ++n) { + if (mUsedPointMask[n]) { + const size_t index = mIndexMap[n]; + mNewPointList.get()[index] = mOldPointList[n]; + } + } + } + +private: + internal::UniquePtr::type& mNewPointList; + const PointList& mOldPointList; + const std::vector& mIndexMap; + const std::vector& mUsedPointMask; +}; + + +//////////////////////////////////////// + + +template +class GenTopologyMask +{ +public: + typedef tree::LeafManager LeafManagerT; + typedef typename SrcTreeT::template ValueConverter::Type BoolTreeT; + typedef tree::ValueAccessor SrcAccessorT; + typedef tree::ValueAccessor BoolAccessorT; + typedef Grid BoolGridT; + + + ////////// + + + GenTopologyMask(const BoolGridT& mask, const LeafManagerT& srcLeafs, + const math::Transform& srcXForm, bool invertMask); + + void run(bool threaded = true); + + BoolTreeT& tree() { return mTree; } + + + ////////// + + GenTopologyMask(GenTopologyMask&, tbb::split); + + void operator()(const tbb::blocked_range&); + + void join(GenTopologyMask& rhs) { mTree.merge(rhs.mTree); } + +private: + + const BoolGridT& mMask; + const LeafManagerT& mLeafManager; + const math::Transform& mSrcXForm; + bool mInvertMask; + BoolTreeT mTree; +}; + + +template +GenTopologyMask::GenTopologyMask(const BoolGridT& mask, const LeafManagerT& srcLeafs, + const math::Transform& srcXForm, bool invertMask) + : mMask(mask) + , mLeafManager(srcLeafs) + , mSrcXForm(srcXForm) + , mInvertMask(invertMask) + , mTree(false) +{ +} + + +template +GenTopologyMask::GenTopologyMask(GenTopologyMask& rhs, tbb::split) + : mMask(rhs.mMask) + , mLeafManager(rhs.mLeafManager) + , mSrcXForm(rhs.mSrcXForm) + , mInvertMask(rhs.mInvertMask) + , mTree(false) +{ +} + + +template +void +GenTopologyMask::run(bool threaded) +{ + if (threaded) { + tbb::parallel_reduce(mLeafManager.getRange(), *this); + } else { + (*this)(mLeafManager.getRange()); + } +} + + +template +void +GenTopologyMask::operator()(const tbb::blocked_range& range) +{ + Coord ijk; + Vec3d xyz; + typedef typename BoolTreeT::LeafNodeType BoolLeafT; + const math::Transform& maskXForm = mMask.transform(); + tree::ValueAccessor maskAcc(mMask.tree()); + tree::ValueAccessor acc(mTree); + + typename SrcTreeT::LeafNodeType::ValueOnCIter iter; + for (size_t n = range.begin(); n != range.end(); ++n) { + + ijk = mLeafManager.leaf(n).origin(); + BoolLeafT* leaf = new BoolLeafT(ijk, false); + bool addLeaf = false; + + if (maskXForm == mSrcXForm) { + + const BoolLeafT* maskLeaf = maskAcc.probeConstLeaf(ijk); + + if (maskLeaf) { + + for (iter = mLeafManager.leaf(n).cbeginValueOn(); iter; ++iter) { + Index pos = iter.pos(); + if(maskLeaf->isValueOn(pos) != mInvertMask) { + leaf->setValueOn(pos); + addLeaf = true; + } + } + + } else if (maskAcc.isValueOn(ijk) != mInvertMask) { + leaf->topologyUnion(mLeafManager.leaf(n)); + addLeaf = true; + } + + } else { + for (iter = mLeafManager.leaf(n).cbeginValueOn(); iter; ++iter) { + ijk = iter.getCoord(); + xyz = maskXForm.worldToIndex(mSrcXForm.indexToWorld(ijk)); + if(maskAcc.isValueOn(util::nearestCoord(xyz)) != mInvertMask) { + leaf->setValueOn(iter.pos()); + addLeaf = true; + } + } + } + + if (addLeaf) acc.addLeaf(leaf); + else delete leaf; + } +} + + +//////////////////////////////////////// + + +template +class GenBoundaryMask +{ +public: + typedef typename SrcTreeT::template ValueConverter::Type IntTreeT; + typedef typename SrcTreeT::template ValueConverter::Type BoolTreeT; + typedef tree::LeafManager LeafManagerT; + + ////////// + + GenBoundaryMask(const LeafManagerT& leafs, const BoolTreeT&, const IntTreeT&); + + void run(bool threaded = true); + + BoolTreeT& tree() { return mTree; } + + ////////// + + GenBoundaryMask(GenBoundaryMask&, tbb::split); + void operator()(const tbb::blocked_range&); + void join(GenBoundaryMask& rhs) { mTree.merge(rhs.mTree); } + +private: + // This typedef is needed for Windows + typedef tree::ValueAccessor IntTreeAccessorT; + + bool neighboringLeaf(const Coord&, const IntTreeAccessorT&) const; + + const LeafManagerT& mLeafManager; + const BoolTreeT& mMaskTree; + const IntTreeT& mIdxTree; + BoolTreeT mTree; + CoordBBox mLeafBBox; +}; + + +template +GenBoundaryMask::GenBoundaryMask(const LeafManagerT& leafs, + const BoolTreeT& maskTree, const IntTreeT& auxTree) + : mLeafManager(leafs) + , mMaskTree(maskTree) + , mIdxTree(auxTree) + , mTree(false) +{ + mIdxTree.evalLeafBoundingBox(mLeafBBox); + mLeafBBox.expand(IntTreeT::LeafNodeType::DIM); +} + + +template +GenBoundaryMask::GenBoundaryMask(GenBoundaryMask& rhs, tbb::split) + : mLeafManager(rhs.mLeafManager) + , mMaskTree(rhs.mMaskTree) + , mIdxTree(rhs.mIdxTree) + , mTree(false) + , mLeafBBox(rhs.mLeafBBox) +{ +} + + +template +void +GenBoundaryMask::run(bool threaded) +{ + if (threaded) { + tbb::parallel_reduce(mLeafManager.getRange(), *this); + } else { + (*this)(mLeafManager.getRange()); + } +} + + +template +bool +GenBoundaryMask::neighboringLeaf(const Coord& ijk, const IntTreeAccessorT& acc) const +{ + if (acc.probeConstLeaf(ijk)) return true; + + const int dim = IntTreeT::LeafNodeType::DIM; + + // face adjacent neghbours + if (acc.probeConstLeaf(Coord(ijk[0] + dim, ijk[1], ijk[2]))) return true; + if (acc.probeConstLeaf(Coord(ijk[0] - dim, ijk[1], ijk[2]))) return true; + if (acc.probeConstLeaf(Coord(ijk[0], ijk[1] + dim, ijk[2]))) return true; + if (acc.probeConstLeaf(Coord(ijk[0], ijk[1] - dim, ijk[2]))) return true; + if (acc.probeConstLeaf(Coord(ijk[0], ijk[1], ijk[2] + dim))) return true; + if (acc.probeConstLeaf(Coord(ijk[0], ijk[1], ijk[2] - dim))) return true; + + // edge adjacent neighbors + if (acc.probeConstLeaf(Coord(ijk[0] + dim, ijk[1], ijk[2] - dim))) return true; + if (acc.probeConstLeaf(Coord(ijk[0] - dim, ijk[1], ijk[2] - dim))) return true; + if (acc.probeConstLeaf(Coord(ijk[0] + dim, ijk[1], ijk[2] + dim))) return true; + if (acc.probeConstLeaf(Coord(ijk[0] - dim, ijk[1], ijk[2] + dim))) return true; + if (acc.probeConstLeaf(Coord(ijk[0] + dim, ijk[1] + dim, ijk[2]))) return true; + if (acc.probeConstLeaf(Coord(ijk[0] - dim, ijk[1] + dim, ijk[2]))) return true; + if (acc.probeConstLeaf(Coord(ijk[0] + dim, ijk[1] - dim, ijk[2]))) return true; + if (acc.probeConstLeaf(Coord(ijk[0] - dim, ijk[1] - dim, ijk[2]))) return true; + if (acc.probeConstLeaf(Coord(ijk[0], ijk[1] - dim, ijk[2] + dim))) return true; + if (acc.probeConstLeaf(Coord(ijk[0], ijk[1] - dim, ijk[2] - dim))) return true; + if (acc.probeConstLeaf(Coord(ijk[0], ijk[1] + dim, ijk[2] + dim))) return true; + if (acc.probeConstLeaf(Coord(ijk[0], ijk[1] + dim, ijk[2] - dim))) return true; + + // corner adjacent neighbors + if (acc.probeConstLeaf(Coord(ijk[0] - dim, ijk[1] - dim, ijk[2] - dim))) return true; + if (acc.probeConstLeaf(Coord(ijk[0] - dim, ijk[1] - dim, ijk[2] + dim))) return true; + if (acc.probeConstLeaf(Coord(ijk[0] + dim, ijk[1] - dim, ijk[2] + dim))) return true; + if (acc.probeConstLeaf(Coord(ijk[0] + dim, ijk[1] - dim, ijk[2] - dim))) return true; + if (acc.probeConstLeaf(Coord(ijk[0] - dim, ijk[1] + dim, ijk[2] - dim))) return true; + if (acc.probeConstLeaf(Coord(ijk[0] - dim, ijk[1] + dim, ijk[2] + dim))) return true; + if (acc.probeConstLeaf(Coord(ijk[0] + dim, ijk[1] + dim, ijk[2] + dim))) return true; + if (acc.probeConstLeaf(Coord(ijk[0] + dim, ijk[1] + dim, ijk[2] - dim))) return true; + + return false; +} + + +template +void +GenBoundaryMask::operator()(const tbb::blocked_range& range) +{ + Coord ijk; + tree::ValueAccessor maskAcc(mMaskTree); + tree::ValueAccessor idxAcc(mIdxTree); + tree::ValueAccessor acc(mTree); + + typename SrcTreeT::LeafNodeType::ValueOnCIter iter; + + for (size_t n = range.begin(); n != range.end(); ++n) { + + const typename SrcTreeT::LeafNodeType& + leaf = mLeafManager.leaf(n); + + ijk = leaf.origin(); + + if (!mLeafBBox.isInside(ijk) || !neighboringLeaf(ijk, idxAcc)) continue; + + const typename BoolTreeT::LeafNodeType* + maskLeaf = maskAcc.probeConstLeaf(ijk); + + if (!maskLeaf || !leaf.hasSameTopology(maskLeaf)) { + acc.touchLeaf(ijk)->topologyUnion(leaf); + } + } +} + + +//////////////////////////////////////// + + +template +class GenTileMask +{ +public: + typedef typename TreeT::template ValueConverter::Type BoolTreeT; + + typedef typename TreeT::ValueType ValueT; + + ////////// + + GenTileMask(const std::vector& tiles, const TreeT& distTree, ValueT iso); + + void run(bool threaded = true); + + BoolTreeT& tree() { return mTree; } + + ////////// + + GenTileMask(GenTileMask&, tbb::split); + void operator()(const tbb::blocked_range&); + void join(GenTileMask& rhs) { mTree.merge(rhs.mTree); } + +private: + + const std::vector& mTiles; + const TreeT& mDistTree; + ValueT mIsovalue; + + BoolTreeT mTree; +}; + + +template +GenTileMask::GenTileMask( + const std::vector& tiles, const TreeT& distTree, ValueT iso) + : mTiles(tiles) + , mDistTree(distTree) + , mIsovalue(iso) + , mTree(false) +{ +} + + +template +GenTileMask::GenTileMask(GenTileMask& rhs, tbb::split) + : mTiles(rhs.mTiles) + , mDistTree(rhs.mDistTree) + , mIsovalue(rhs.mIsovalue) + , mTree(false) +{ +} + + +template +void +GenTileMask::run(bool threaded) +{ + if (threaded) tbb::parallel_reduce(tbb::blocked_range(0, mTiles.size()), *this); + else (*this)(tbb::blocked_range(0, mTiles.size())); +} + + +template +void +GenTileMask::operator()(const tbb::blocked_range& range) +{ + tree::ValueAccessor distAcc(mDistTree); + CoordBBox region, bbox; + Coord ijk, nijk; + bool processRegion = true; + ValueT value; + + + for (size_t n = range.begin(); n != range.end(); ++n) { + + const Vec4i& tile = mTiles[n]; + + bbox.min()[0] = tile[0]; + bbox.min()[1] = tile[1]; + bbox.min()[2] = tile[2]; + + bbox.max() = bbox.min(); + bbox.max().offset(tile[3]); + + const bool thisInside = (distAcc.getValue(bbox.min()) < mIsovalue); + const int thisDepth = distAcc.getValueDepth(bbox.min()); + + // eval x-edges + + ijk = bbox.max(); + nijk = ijk; + ++nijk[0]; + + processRegion = true; + if (thisDepth >= distAcc.getValueDepth(nijk)) { + processRegion = thisInside != (distAcc.getValue(nijk) < mIsovalue); + } + + + if (processRegion) { + region = bbox; + region.min()[0] = region.max()[0] = ijk[0]; + mTree.fill(region, true); + } + + + ijk = bbox.min(); + --ijk[0]; + + processRegion = true; + if (thisDepth >= distAcc.getValueDepth(ijk)) { + processRegion = !distAcc.probeValue(ijk, value) && thisInside != (value < mIsovalue); + } + + if (processRegion) { + region = bbox; + region.min()[0] = region.max()[0] = ijk[0]; + mTree.fill(region, true); + } + + + // eval y-edges + + ijk = bbox.max(); + nijk = ijk; + ++nijk[1]; + + processRegion = true; + if (thisDepth >= distAcc.getValueDepth(nijk)) { + processRegion = thisInside != (distAcc.getValue(nijk) < mIsovalue); + } + + if (processRegion) { + region = bbox; + region.min()[1] = region.max()[1] = ijk[1]; + mTree.fill(region, true); + } + + + ijk = bbox.min(); + --ijk[1]; + + processRegion = true; + if (thisDepth >= distAcc.getValueDepth(ijk)) { + processRegion = !distAcc.probeValue(ijk, value) && thisInside != (value < mIsovalue); + } + + if (processRegion) { + region = bbox; + region.min()[1] = region.max()[1] = ijk[1]; + mTree.fill(region, true); + } + + + // eval z-edges + + ijk = bbox.max(); + nijk = ijk; + ++nijk[2]; + + processRegion = true; + if (thisDepth >= distAcc.getValueDepth(nijk)) { + processRegion = thisInside != (distAcc.getValue(nijk) < mIsovalue); + } + + if (processRegion) { + region = bbox; + region.min()[2] = region.max()[2] = ijk[2]; + mTree.fill(region, true); + } + + ijk = bbox.min(); + --ijk[2]; + + processRegion = true; + if (thisDepth >= distAcc.getValueDepth(ijk)) { + processRegion = !distAcc.probeValue(ijk, value) && thisInside != (value < mIsovalue); + } + + if (processRegion) { + region = bbox; + region.min()[2] = region.max()[2] = ijk[2]; + mTree.fill(region, true); + } + + + ijk = bbox.min(); + --ijk[1]; + --ijk[2]; + + processRegion = true; + if (thisDepth >= distAcc.getValueDepth(ijk)) { + processRegion = !distAcc.probeValue(ijk, value) && thisInside != (value < mIsovalue); + } + + if (processRegion) { + region = bbox; + region.min()[1] = region.max()[1] = ijk[1]; + region.min()[2] = region.max()[2] = ijk[2]; + mTree.fill(region, true); + } + + + ijk = bbox.min(); + --ijk[0]; + --ijk[1]; + + processRegion = true; + if (thisDepth >= distAcc.getValueDepth(ijk)) { + processRegion = !distAcc.probeValue(ijk, value) && thisInside != (value < mIsovalue); + } + + if (processRegion) { + region = bbox; + region.min()[1] = region.max()[1] = ijk[1]; + region.min()[0] = region.max()[0] = ijk[0]; + mTree.fill(region, true); + } + + ijk = bbox.min(); + --ijk[0]; + --ijk[2]; + + processRegion = true; + if (thisDepth >= distAcc.getValueDepth(ijk)) { + processRegion = !distAcc.probeValue(ijk, value) && thisInside != (value < mIsovalue); + } + + if (processRegion) { + region = bbox; + region.min()[2] = region.max()[2] = ijk[2]; + region.min()[0] = region.max()[0] = ijk[0]; + mTree.fill(region, true); + } + } +} + + +//////////////////////////////////////// + + +template +inline void +tileData(const DistTreeT& distTree, SignTreeT& signTree, IdxTreeT& idxTree, double iso) +{ + typename DistTreeT::ValueOnCIter tileIter(distTree); + tileIter.setMaxDepth(DistTreeT::ValueOnCIter::LEAF_DEPTH - 1); + + if (!tileIter) return; // volume has no active tiles. + + size_t tileCount = 0; + for ( ; tileIter; ++tileIter) { + ++tileCount; + } + + std::vector tiles(tileCount); + + tileCount = 0; + tileIter = distTree.cbeginValueOn(); + tileIter.setMaxDepth(DistTreeT::ValueOnCIter::LEAF_DEPTH - 1); + + CoordBBox bbox; + for (; tileIter; ++tileIter) { + Vec4i& tile = tiles[tileCount++]; + tileIter.getBoundingBox(bbox); + tile[0] = bbox.min()[0]; + tile[1] = bbox.min()[1]; + tile[2] = bbox.min()[2]; + tile[3] = bbox.max()[0] - bbox.min()[0]; + } + + typename DistTreeT::ValueType isovalue = typename DistTreeT::ValueType(iso); + + GenTileMask tileMask(tiles, distTree, isovalue); + tileMask.run(); + + typedef typename DistTreeT::template ValueConverter::Type BoolTreeT; + typedef tree::LeafManager BoolLeafManagerT; + + BoolLeafManagerT leafs(tileMask.tree()); + + + internal::SignData op(distTree, leafs, isovalue); + op.run(); + + signTree.merge(*op.signTree()); + idxTree.merge(*op.idxTree()); +} + + +//////////////////////////////////////// + + +// Utility class for the volumeToMesh wrapper +class PointListCopy +{ +public: + PointListCopy(const PointList& pointsIn, std::vector& pointsOut) + : mPointsIn(pointsIn) , mPointsOut(pointsOut) + { + } + + void operator()(const tbb::blocked_range& range) const + { + for (size_t n = range.begin(); n < range.end(); ++n) { + mPointsOut[n] = mPointsIn[n]; + } + } + +private: + const PointList& mPointsIn; + std::vector& mPointsOut; +}; + + +// Checks if the isovalue is in proximity to the active voxel boundary. +template +inline bool +needsActiveVoxePadding(const LeafManagerT& leafs, double iso, double voxelSize) +{ + double interiorWidth = 0.0, exteriorWidth = 0.0; + { + typename LeafManagerT::TreeType::LeafNodeType::ValueOffCIter it; + bool foundInterior = false, foundExterior = false; + for (size_t n = 0, N = leafs.leafCount(); n < N; ++n) { + + for (it = leafs.leaf(n).cbeginValueOff(); it; ++it) { + double value = double(it.getValue()); + if (value < 0.0) { + interiorWidth = value; + foundInterior = true; + } else if (value > 0.0) { + exteriorWidth = value; + foundExterior = true; + } + + if (foundInterior && foundExterior) break; + } + + if (foundInterior && foundExterior) break; + } + + } + + double minDist = std::min(std::abs(interiorWidth - iso), std::abs(exteriorWidth - iso)); + return !(minDist > (2.0 * voxelSize)); +} + + +} // end namespace internal + + +//////////////////////////////////////// + + +inline +PolygonPool::PolygonPool() + : mNumQuads(0) + , mNumTriangles(0) + , mQuads(NULL) + , mTriangles(NULL) + , mQuadFlags(NULL) + , mTriangleFlags(NULL) +{ +} + + +inline +PolygonPool::PolygonPool(const size_t numQuads, const size_t numTriangles) + : mNumQuads(numQuads) + , mNumTriangles(numTriangles) + , mQuads(new openvdb::Vec4I[mNumQuads]) + , mTriangles(new openvdb::Vec3I[mNumTriangles]) + , mQuadFlags(new char[mNumQuads]) + , mTriangleFlags(new char[mNumTriangles]) +{ +} + + +inline void +PolygonPool::copy(const PolygonPool& rhs) +{ + resetQuads(rhs.numQuads()); + resetTriangles(rhs.numTriangles()); + + for (size_t i = 0; i < mNumQuads; ++i) { + mQuads[i] = rhs.mQuads[i]; + mQuadFlags[i] = rhs.mQuadFlags[i]; + } + + for (size_t i = 0; i < mNumTriangles; ++i) { + mTriangles[i] = rhs.mTriangles[i]; + mTriangleFlags[i] = rhs.mTriangleFlags[i]; + } +} + + +inline void +PolygonPool::resetQuads(size_t size) +{ + mNumQuads = size; + mQuads.reset(new openvdb::Vec4I[mNumQuads]); + mQuadFlags.reset(new char[mNumQuads]); +} + + +inline void +PolygonPool::clearQuads() +{ + mNumQuads = 0; + mQuads.reset(NULL); + mQuadFlags.reset(NULL); +} + + +inline void +PolygonPool::resetTriangles(size_t size) +{ + mNumTriangles = size; + mTriangles.reset(new openvdb::Vec3I[mNumTriangles]); + mTriangleFlags.reset(new char[mNumTriangles]); +} + + +inline void +PolygonPool::clearTriangles() +{ + mNumTriangles = 0; + mTriangles.reset(NULL); + mTriangleFlags.reset(NULL); +} + + +inline bool +PolygonPool::trimQuads(const size_t n, bool reallocate) +{ + if (!(n < mNumQuads)) return false; + + if (reallocate) { + + if (n == 0) { + mQuads.reset(NULL); + } else { + + boost::scoped_array quads(new openvdb::Vec4I[n]); + boost::scoped_array flags(new char[n]); + + for (size_t i = 0; i < n; ++i) { + quads[i] = mQuads[i]; + flags[i] = mQuadFlags[i]; + } + + mQuads.swap(quads); + mQuadFlags.swap(flags); + } + } + + mNumQuads = n; + return true; +} + + +inline bool +PolygonPool::trimTrinagles(const size_t n, bool reallocate) +{ + if (!(n < mNumTriangles)) return false; + + if (reallocate) { + + if (n == 0) { + mTriangles.reset(NULL); + } else { + + boost::scoped_array triangles(new openvdb::Vec3I[n]); + boost::scoped_array flags(new char[n]); + + for (size_t i = 0; i < n; ++i) { + triangles[i] = mTriangles[i]; + flags[i] = mTriangleFlags[i]; + } + + mTriangles.swap(triangles); + mTriangleFlags.swap(flags); + } + } + + mNumTriangles = n; + return true; +} + + +//////////////////////////////////////// + + +inline VolumeToMesh::VolumeToMesh(double isovalue, double adaptivity) + : mPoints(NULL) + , mPolygons() + , mPointListSize(0) + , mSeamPointListSize(0) + , mPolygonPoolListSize(0) + , mIsovalue(isovalue) + , mPrimAdaptivity(adaptivity) + , mSecAdaptivity(0.0) + , mRefGrid(GridBase::ConstPtr()) + , mSurfaceMaskGrid(GridBase::ConstPtr()) + , mAdaptivityGrid(GridBase::ConstPtr()) + , mAdaptivityMaskTree(TreeBase::ConstPtr()) + , mRefSignTree(TreeBase::Ptr()) + , mRefIdxTree(TreeBase::Ptr()) + , mInvertSurfaceMask(false) + , mPartitions(1) + , mActivePart(0) + , mQuantizedSeamPoints(NULL) + , mPointFlags(0) +{ +} + + +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) +{ + mRefGrid = grid; + mSecAdaptivity = secAdaptivity; + + // Clear out old auxiliary data + mRefSignTree = TreeBase::Ptr(); + mRefIdxTree = TreeBase::Ptr(); + mSeamPointListSize = 0; + mQuantizedSeamPoints.reset(NULL); +} + + +inline void +VolumeToMesh::setSurfaceMask(const GridBase::ConstPtr& mask, bool invertMask) +{ + mSurfaceMaskGrid = mask; + mInvertSurfaceMask = invertMask; +} + + +inline void +VolumeToMesh::setSpatialAdaptivity(const GridBase::ConstPtr& grid) +{ + mAdaptivityGrid = grid; +} + + +inline void +VolumeToMesh::setAdaptivityMask(const TreeBase::ConstPtr& tree) +{ + mAdaptivityMaskTree = tree; +} + + +inline void +VolumeToMesh::partition(unsigned partitions, unsigned activePart) +{ + mPartitions = std::max(partitions, unsigned(1)); + mActivePart = std::min(activePart, mPartitions-1); +} + + +inline std::vector& +VolumeToMesh::pointFlags() +{ + return mPointFlags; +} + + +inline const std::vector& +VolumeToMesh::pointFlags() const +{ + return mPointFlags; +} + + +template +inline void +VolumeToMesh::operator()(const GridT& distGrid) +{ + typedef typename GridT::TreeType DistTreeT; + typedef tree::LeafManager DistLeafManagerT; + typedef typename DistTreeT::ValueType DistValueT; + + typedef typename DistTreeT::template ValueConverter::Type BoolTreeT; + typedef tree::LeafManager BoolLeafManagerT; + typedef Grid BoolGridT; + + typedef typename DistTreeT::template ValueConverter::Type Int16TreeT; + typedef tree::LeafManager Int16LeafManagerT; + + typedef typename DistTreeT::template ValueConverter::Type IntTreeT; + typedef typename DistTreeT::template ValueConverter::Type FloatTreeT; + typedef Grid FloatGridT; + + + const openvdb::math::Transform& transform = distGrid.transform(); + const DistTreeT& distTree = distGrid.tree(); + const DistValueT isovalue = DistValueT(mIsovalue); + + typename Int16TreeT::Ptr signTreePt; + typename IntTreeT::Ptr idxTreePt; + typename BoolTreeT::Ptr pointMask; + + BoolTreeT valueMask(false), seamMask(false); + const bool adaptive = mPrimAdaptivity > 1e-7 || mSecAdaptivity > 1e-7; + bool maskEdges = false; + + + const BoolGridT * surfaceMask = NULL; + if (mSurfaceMaskGrid && mSurfaceMaskGrid->type() == BoolGridT::gridType()) { + surfaceMask = static_cast(mSurfaceMaskGrid.get()); + } + + const FloatGridT * adaptivityField = NULL; + if (mAdaptivityGrid && mAdaptivityGrid->type() == FloatGridT::gridType()) { + adaptivityField = static_cast(mAdaptivityGrid.get()); + } + + if (mAdaptivityMaskTree && mAdaptivityMaskTree->type() == BoolTreeT::treeType()) { + const BoolTreeT *adaptivityMaskPt = + static_cast(mAdaptivityMaskTree.get()); + seamMask.topologyUnion(*adaptivityMaskPt); + } + + + // Collect auxiliary data + { + DistLeafManagerT distLeafs(distTree); + + // Check if the isovalue is in proximity to the active voxel boundary. + bool padActiveVoxels = false; + int padVoxels = 3; + + if (distGrid.getGridClass() != GRID_LEVEL_SET) { + padActiveVoxels = true; + } else { + padActiveVoxels = internal::needsActiveVoxePadding(distLeafs, + mIsovalue, transform.voxelSize()[0]); + } + + // always pad the active region for small volumes (the performance hit is neglectable). + if (!padActiveVoxels) { + Coord dim; + distTree.evalActiveVoxelDim(dim); + int maxDim = std::max(std::max(dim[0], dim[1]), dim[2]); + if (maxDim < 1000) { + padActiveVoxels = true; + padVoxels = 1; + } + } + + if (surfaceMask || mPartitions > 1) { + + maskEdges = true; + + if (surfaceMask) { + + { // Mask + internal::GenTopologyMask masking( + *surfaceMask, distLeafs, transform, mInvertSurfaceMask); + masking.run(); + valueMask.merge(masking.tree()); + } + + if (mPartitions > 1) { // Partition + tree::LeafManager leafs(valueMask); + leafs.foreach(internal::PartOp(leafs.leafCount() , mPartitions, mActivePart)); + tools::pruneInactive(valueMask); + } + + } else { // Partition + + internal::PartGen partitioner(distLeafs, mPartitions, mActivePart); + partitioner.run(); + valueMask.merge(partitioner.tree()); + } + + { + if (padActiveVoxels) tools::dilateVoxels(valueMask, padVoxels); + BoolLeafManagerT leafs(valueMask); + + internal::SignData + signDataOp(distTree, leafs, isovalue); + signDataOp.run(); + + signTreePt = signDataOp.signTree(); + idxTreePt = signDataOp.idxTree(); + } + + { + internal::GenBoundaryMask boundary(distLeafs, valueMask, *idxTreePt); + boundary.run(); + + BoolLeafManagerT bleafs(boundary.tree()); + + internal::SignData + signDataOp(distTree, bleafs, isovalue); + signDataOp.run(); + + signTreePt->merge(*signDataOp.signTree()); + idxTreePt->merge(*signDataOp.idxTree()); + } + + } else { + + // Collect voxel-sign configurations + if (padActiveVoxels) { + + BoolTreeT regionMask(false); + regionMask.topologyUnion(distTree); + tools::dilateVoxels(regionMask, padVoxels); + + BoolLeafManagerT leafs(regionMask); + + internal::SignData + signDataOp(distTree, leafs, isovalue); + signDataOp.run(); + + signTreePt = signDataOp.signTree(); + idxTreePt = signDataOp.idxTree(); + } else { + + internal::SignData + signDataOp(distTree, distLeafs, isovalue); + signDataOp.run(); + + signTreePt = signDataOp.signTree(); + idxTreePt = signDataOp.idxTree(); + } + } + + } + + + // Collect auxiliary data from active tiles + internal::tileData(distTree, *signTreePt, *idxTreePt, static_cast(isovalue)); + + // Optionally collect auxiliary data from a reference level set. + Int16TreeT *refSignTreePt = NULL; + IntTreeT *refIdxTreePt = NULL; + const DistTreeT *refDistTreePt = NULL; + + if (mRefGrid && mRefGrid->type() == GridT::gridType()) { + + const GridT* refGrid = static_cast(mRefGrid.get()); + refDistTreePt = &refGrid->tree(); + + // Collect and cache auxiliary data from the reference grid. + if (!mRefSignTree && !mRefIdxTree) { + + DistLeafManagerT refDistLeafs(*refDistTreePt); + internal::SignData + signDataOp(*refDistTreePt, refDistLeafs, isovalue); + + signDataOp.run(); + + mRefSignTree = signDataOp.signTree(); + mRefIdxTree = signDataOp.idxTree(); + } + + // Get cached auxiliary data + if (mRefSignTree && mRefIdxTree) { + refSignTreePt = static_cast(mRefSignTree.get()); + refIdxTreePt = static_cast(mRefIdxTree.get()); + } + } + + + // Process auxiliary data + Int16LeafManagerT signLeafs(*signTreePt); + + if (maskEdges) { + signLeafs.foreach(internal::MaskEdges(valueMask)); + valueMask.clear(); + } + + + // Generate the seamline mask + if (refSignTreePt) { + internal::GenSeamMask seamOp(signLeafs, *refSignTreePt); + seamOp.run(); + + tools::dilateVoxels(seamOp.mask(), 3); + signLeafs.foreach(internal::TagSeamEdges(seamOp.mask())); + + seamMask.merge(seamOp.mask()); + } + + + std::vector regions(signLeafs.leafCount(), 0); + if (regions.empty()) { + mPointListSize = 0; + mPoints.reset(); + mPolygonPoolListSize = 0; + mPolygons.reset(); + mPointFlags.clear(); + return; + } + + if (adaptive) { + + internal::MergeVoxelRegions merge( + signLeafs, *signTreePt, distTree, *idxTreePt, isovalue, DistValueT(mPrimAdaptivity)); + + if (adaptivityField) { + merge.setSpatialAdaptivity(transform, *adaptivityField); + } + + if (refSignTreePt || mAdaptivityMaskTree) { + merge.setAdaptivityMask(&seamMask); + } + + if (refSignTreePt) { + merge.setRefData(refSignTreePt, DistValueT(mSecAdaptivity)); + } + + merge.run(); + + signLeafs.foreach(internal::CountRegions(*idxTreePt, regions)); + + } else { + signLeafs.foreach(internal::CountPoints(regions)); + } + + + { + 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; + } + } + + + // Generate the unique point list + mPoints.reset(new openvdb::Vec3s[mPointListSize]); + mPointFlags.clear(); + + // Generate seam line sample points + if (refSignTreePt && refIdxTreePt) { + + if (mSeamPointListSize == 0) { + + std::vector pointMap; + + { + Int16LeafManagerT refSignLeafs(*refSignTreePt); + pointMap.resize(refSignLeafs.leafCount(), 0); + + refSignLeafs.foreach(internal::CountPoints(pointMap)); + + size_t tmp = 0; + for (size_t n = 0, N = pointMap.size(); n < N; ++n) { + tmp = pointMap[n]; + pointMap[n] = mSeamPointListSize; + mSeamPointListSize += tmp; + } + } + + if (!pointMap.empty() && mSeamPointListSize != 0) { + + mQuantizedSeamPoints.reset(new uint32_t[mSeamPointListSize]); + memset(mQuantizedSeamPoints.get(), 0, sizeof(uint32_t) * mSeamPointListSize); + + typedef tree::LeafManager IntLeafManagerT; + + IntLeafManagerT refIdxLeafs(*refIdxTreePt); + refIdxLeafs.foreach(internal::MapPoints(pointMap, *refSignTreePt)); + } + } + + if (mSeamPointListSize != 0) { + signLeafs.foreach(internal::SeamWeights( + distTree, *refSignTreePt, *refIdxTreePt, mQuantizedSeamPoints, mIsovalue)); + } + } + + + internal::GenPoints + pointOp(signLeafs, distTree, *idxTreePt, mPoints, regions, transform, mIsovalue); + + + if (mSeamPointListSize != 0) { + mPointFlags.resize(mPointListSize); + pointOp.setRefData(refSignTreePt, refDistTreePt, refIdxTreePt, + &mQuantizedSeamPoints, &mPointFlags); + } + + pointOp.run(); + + + mPolygonPoolListSize = signLeafs.leafCount(); + mPolygons.reset(new PolygonPool[mPolygonPoolListSize]); + + + if (adaptive) { + + internal::GenPolygons + mesher(signLeafs, *signTreePt, *idxTreePt, mPolygons, Index32(mPointListSize)); + + mesher.setRefSignTree(refSignTreePt); + mesher.run(); + + } else { + + internal::GenPolygons + mesher(signLeafs, *signTreePt, *idxTreePt, mPolygons, Index32(mPointListSize)); + + mesher.setRefSignTree(refSignTreePt); + mesher.run(); + } + + // Clean up unused points, only necessary if masking and/or + // automatic mesh partitioning is enabled. + if ((surfaceMask || mPartitions > 1) && mPointListSize > 0) { + + // Flag used points + std::vector usedPointMask(mPointListSize, 0); + + internal::FlagUsedPoints flagPoints(mPolygons, mPolygonPoolListSize, usedPointMask); + flagPoints.run(); + + // Create index map + std::vector indexMap(mPointListSize); + size_t usedPointCount = 0; + for (size_t p = 0; p < mPointListSize; ++p) { + if (usedPointMask[p]) indexMap[p] = static_cast(usedPointCount++); + } + + if (usedPointCount < mPointListSize) { + + // move points + internal::UniquePtr::type + newPointList(new openvdb::Vec3s[usedPointCount]); + + internal::MovePoints movePoints(newPointList, mPoints, indexMap, usedPointMask); + movePoints.run(); + + mPointListSize = usedPointCount; + mPoints.reset(newPointList.release()); + + // update primitives + internal::RemapIndices remap(mPolygons, mPolygonPoolListSize, indexMap); + remap.run(); + } + } + + + // Subdivide nonplanar quads near the seamline edges + // todo: thread and clean up + if (refSignTreePt || refIdxTreePt || refDistTreePt) { + std::vector newPoints; + + for (size_t n = 0; n < mPolygonPoolListSize; ++n) { + + PolygonPool& polygons = mPolygons[n]; + + std::vector nonPlanarQuads; + nonPlanarQuads.reserve(polygons.numQuads()); + + for (size_t i = 0; i < polygons.numQuads(); ++i) { + + char& flags = polygons.quadFlags(i); + + if ((flags & POLYFLAG_FRACTURE_SEAM) && !(flags & POLYFLAG_EXTERIOR)) { + + openvdb::Vec4I& quad = polygons.quad(i); + + const bool edgePoly = mPointFlags[quad[0]] || mPointFlags[quad[1]] + || mPointFlags[quad[2]] || mPointFlags[quad[3]]; + + if (!edgePoly) continue; + + const Vec3s& p0 = mPoints[quad[0]]; + const Vec3s& p1 = mPoints[quad[1]]; + const Vec3s& p2 = mPoints[quad[2]]; + const Vec3s& p3 = mPoints[quad[3]]; + + if (!internal::isPlanarQuad(p0, p1, p2, p3, 1e-6f)) { + nonPlanarQuads.push_back(i); + } + } + } + + + if (!nonPlanarQuads.empty()) { + + PolygonPool tmpPolygons; + + tmpPolygons.resetQuads(polygons.numQuads() - nonPlanarQuads.size()); + tmpPolygons.resetTriangles(polygons.numTriangles() + 4 * nonPlanarQuads.size()); + + size_t triangleIdx = 0; + for (size_t i = 0; i < nonPlanarQuads.size(); ++i) { + + size_t& quadIdx = nonPlanarQuads[i]; + + openvdb::Vec4I& quad = polygons.quad(quadIdx); + char& quadFlags = polygons.quadFlags(quadIdx); + //quadFlags |= POLYFLAG_SUBDIVIDED; + + Vec3s centroid = (mPoints[quad[0]] + mPoints[quad[1]] + + mPoints[quad[2]] + mPoints[quad[3]]) * 0.25; + + size_t pointIdx = newPoints.size() + mPointListSize; + + newPoints.push_back(centroid); + + + { + Vec3I& triangle = tmpPolygons.triangle(triangleIdx); + + triangle[0] = quad[0]; + triangle[1] = static_cast(pointIdx); + triangle[2] = quad[3]; + + tmpPolygons.triangleFlags(triangleIdx) = quadFlags; + + if (mPointFlags[triangle[0]] || mPointFlags[triangle[2]]) { + tmpPolygons.triangleFlags(triangleIdx) |= POLYFLAG_SUBDIVIDED; + } + } + + ++triangleIdx; + + { + Vec3I& triangle = tmpPolygons.triangle(triangleIdx); + + triangle[0] = quad[0]; + triangle[1] = quad[1]; + triangle[2] = static_cast(pointIdx); + + tmpPolygons.triangleFlags(triangleIdx) = quadFlags; + + if (mPointFlags[triangle[0]] || mPointFlags[triangle[1]]) { + tmpPolygons.triangleFlags(triangleIdx) |= POLYFLAG_SUBDIVIDED; + } + } + + ++triangleIdx; + + { + Vec3I& triangle = tmpPolygons.triangle(triangleIdx); + + triangle[0] = quad[1]; + triangle[1] = quad[2]; + triangle[2] = static_cast(pointIdx); + + tmpPolygons.triangleFlags(triangleIdx) = quadFlags; + + if (mPointFlags[triangle[0]] || mPointFlags[triangle[1]]) { + tmpPolygons.triangleFlags(triangleIdx) |= POLYFLAG_SUBDIVIDED; + } + } + + + ++triangleIdx; + + { + Vec3I& triangle = tmpPolygons.triangle(triangleIdx); + + triangle[0] = quad[2]; + triangle[1] = quad[3]; + triangle[2] = static_cast(pointIdx); + + tmpPolygons.triangleFlags(triangleIdx) = quadFlags; + + if (mPointFlags[triangle[0]] || mPointFlags[triangle[1]]) { + tmpPolygons.triangleFlags(triangleIdx) |= POLYFLAG_SUBDIVIDED; + } + } + + ++triangleIdx; + + quad[0] = util::INVALID_IDX; + } + + + for (size_t i = 0; i < polygons.numTriangles(); ++i) { + tmpPolygons.triangle(triangleIdx) = polygons.triangle(i); + tmpPolygons.triangleFlags(triangleIdx) = polygons.triangleFlags(i); + ++triangleIdx; + } + + + size_t quadIdx = 0; + for (size_t i = 0; i < polygons.numQuads(); ++i) { + openvdb::Vec4I& quad = polygons.quad(i); + + if (quad[0] != util::INVALID_IDX) { + tmpPolygons.quad(quadIdx) = quad; + tmpPolygons.quadFlags(quadIdx) = polygons.quadFlags(i); + ++quadIdx; + } + } + + + polygons.copy(tmpPolygons); + } + + } + + + if (!newPoints.empty()) { + + size_t newPointCount = newPoints.size() + mPointListSize; + + internal::UniquePtr::type + newPointList(new openvdb::Vec3s[newPointCount]); + + for (size_t i = 0; i < mPointListSize; ++i) { + newPointList.get()[i] = mPoints[i]; + } + + for (size_t i = mPointListSize; i < newPointCount; ++i) { + newPointList.get()[i] = newPoints[i - mPointListSize]; + } + + mPointListSize = newPointCount; + mPoints.reset(newPointList.release()); + mPointFlags.resize(mPointListSize, 0); + } + } +} + + +//////////////////////////////////////// + + +/// @internal This overload is enabled only for grids with a scalar ValueType. +template +inline typename boost::enable_if, void>::type +doVolumeToMesh( + const GridType& grid, + std::vector& points, + std::vector& triangles, + std::vector& 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(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); + } + } +} + +/// @internal This overload is enabled only for grids that do not have a scalar ValueType. +template +inline typename boost::disable_if, void>::type +doVolumeToMesh( + const GridType&, + std::vector&, + std::vector&, + std::vector&, + double, + double) +{ + OPENVDB_THROW(TypeError, "volume to mesh conversion is supported only for scalar grids"); +} + + +template +inline void +volumeToMesh( + const GridType& grid, + std::vector& points, + std::vector& triangles, + std::vector& quads, + double isovalue, + double adaptivity) +{ + doVolumeToMesh(grid, points, triangles, quads, isovalue, adaptivity); +} + + +template +inline void +volumeToMesh( + const GridType& grid, + std::vector& points, + std::vector& quads, + double isovalue) +{ + std::vector triangles; + doVolumeToMesh(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-2016 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/nuparu/include/openvdb/include/openvdb/tools/VolumeToSpheres.h b/nuparu/include/openvdb/include/openvdb/tools/VolumeToSpheres.h new file mode 100644 index 00000000..fe94293d --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tools/VolumeToSpheres.h @@ -0,0 +1,1034 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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_SPHERES_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_VOLUME_TO_SPHERES_HAS_BEEN_INCLUDED + +#include +#include +#include // for erodeVoxels() + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include // std::numeric_limits + +////////// + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + + +/// @brief Threaded method to fill a closed level set or fog volume +/// with adaptively sized spheres. +/// +/// @param grid a scalar gird to fill with spheres. +/// +/// @param spheres a @c Vec4 array representing the spheres that returned by this +/// method. The first three components specify the sphere center +/// and the fourth is the radius. The spheres in this array are +/// ordered by radius, biggest to smallest. +/// +/// @param maxSphereCount no more than this number of spheres are generated. +/// +/// @param overlapping toggle to allow spheres to overlap/intersect +/// +/// @param minRadius determines the smallest sphere size in voxel units. +/// +/// @param maxRadius determines the largest sphere size in voxel units. +/// +/// @param isovalue the crossing point of the volume values that is considered +/// the surface. The zero default value works for signed distance +/// fields while fog volumes require a larger positive value, +/// 0.5 is a good initial guess. +/// +/// @param instanceCount how many interior points to consider for the sphere placement, +/// increasing this count increases the chances of finding optimal +/// sphere sizes. +/// +/// @param interrupter a pointer adhering to the util::NullInterrupter interface +/// +template +inline void +fillWithSpheres( + const GridT& grid, + std::vector& spheres, + int maxSphereCount, + bool overlapping = false, + float minRadius = 1.0, + float maxRadius = std::numeric_limits::max(), + float isovalue = 0.0, + int instanceCount = 10000, + InterrupterT* interrupter = NULL); + + +/// @brief @c fillWithSpheres method variant that automatically infers +/// the util::NullInterrupter. +template +inline void +fillWithSpheres( + const GridT& grid, + std::vector& spheres, + int maxSphereCount, + bool overlapping = false, + float minRadius = 1.0, + float maxRadius = std::numeric_limits::max(), + float isovalue = 0.0, + int instanceCount = 10000) +{ + fillWithSpheres(grid, spheres, + maxSphereCount, overlapping, minRadius, maxRadius, isovalue, instanceCount); +} + + +//////////////////////////////////////// + + +/// @brief Accelerated closest surface point queries for narrow band level sets. +/// Supports queries that originate at arbitrary world-space locations, is +/// not confined to the narrow band region of the input volume geometry. +template +class ClosestSurfacePoint +{ +public: + typedef typename GridT::TreeType TreeT; + typedef typename TreeT::template ValueConverter::Type IntTreeT; + typedef typename TreeT::template ValueConverter::Type Int16TreeT; + + + ClosestSurfacePoint(); + + + /// @brief Extracts the surface points and constructs a spatial acceleration structure. + /// + /// @param grid a scalar gird, level set or fog volume. + /// + /// @param isovalue the crossing point of the volume values that is considered + /// the surface. The zero default value works for signed distance + /// fields while fog volumes require a larger positive value, + /// 0.5 is a good initial guess. + /// + /// @param interrupter a pointer adhering to the util::NullInterrupter interface. + /// + template + void initialize(const GridT& grid, float isovalue = 0.0, InterrupterT* interrupter = NULL); + + + /// @brief @c initialize method variant that automatically infers + /// the util::NullInterrupter. + void initialize(const GridT& grid, float isovalue = 0.0); + + + + /// @brief Computes distance to closest surface. + /// + /// @param points search locations in world space. + /// + /// @param distances list of closest surface point distances, populated by this method. + /// + bool search(const std::vector& points, std::vector& distances); + + + /// @brief Performs closest point searches. + /// + /// @param points search locations in world space to be replaced by their closest + /// surface point. + /// + /// @param distances list of closest surface point distances, populated by this method. + /// + bool searchAndReplace(std::vector& points, std::vector& distances); + + + /// @{ + /// @brief Tree accessors + const IntTreeT& indexTree() const { return *mIdxTreePt; } + const Int16TreeT& signTree() const { return *mSignTreePt; } + /// @} + +private: + typedef typename IntTreeT::LeafNodeType IntLeafT; + typedef std::pair IndexRange; + + bool mIsInitialized; + std::vector mLeafBoundingSpheres, mNodeBoundingSpheres; + std::vector mLeafRanges; + std::vector mLeafNodes; + PointList mSurfacePointList; + size_t mPointListSize, mMaxNodeLeafs; + float mMaxRadiusSqr; + typename IntTreeT::Ptr mIdxTreePt; + typename Int16TreeT::Ptr mSignTreePt; + + bool search(std::vector&, std::vector&, bool transformPoints); +}; + + +//////////////////////////////////////// + + + + +// Internal utility methods + + +namespace internal { + +struct PointAccessor +{ + PointAccessor(std::vector& points) + : mPoints(points) + { + } + + void add(const Vec3R &pos) + { + mPoints.push_back(pos); + } +private: + std::vector& mPoints; +}; + + +template +class LeafBS +{ +public: + + LeafBS(std::vector& leafBoundingSpheres, + const std::vector& leafNodes, + const math::Transform& transform, + const PointList& surfacePointList); + + void run(bool threaded = true); + + + void operator()(const tbb::blocked_range&) const; + +private: + std::vector& mLeafBoundingSpheres; + const std::vector& mLeafNodes; + const math::Transform& mTransform; + const PointList& mSurfacePointList; +}; + +template +LeafBS::LeafBS( + std::vector& leafBoundingSpheres, + const std::vector& leafNodes, + const math::Transform& transform, + const PointList& surfacePointList) + : mLeafBoundingSpheres(leafBoundingSpheres) + , mLeafNodes(leafNodes) + , mTransform(transform) + , mSurfacePointList(surfacePointList) +{ +} + +template +void +LeafBS::run(bool threaded) +{ + if (threaded) { + tbb::parallel_for(tbb::blocked_range(0, mLeafNodes.size()), *this); + } else { + (*this)(tbb::blocked_range(0, mLeafNodes.size())); + } +} + +template +void +LeafBS::operator()(const tbb::blocked_range& range) const +{ + typename IntLeafT::ValueOnCIter iter; + Vec3s avg; + + for (size_t n = range.begin(); n != range.end(); ++n) { + + avg[0] = 0.0; + avg[1] = 0.0; + avg[2] = 0.0; + + int count = 0; + for (iter = mLeafNodes[n]->cbeginValueOn(); iter; ++iter) { + avg += mSurfacePointList[iter.getValue()]; + ++count; + } + + if (count > 1) avg *= float(1.0 / double(count)); + + float maxDist = 0.0; + + for (iter = mLeafNodes[n]->cbeginValueOn(); iter; ++iter) { + float tmpDist = (mSurfacePointList[iter.getValue()] - avg).lengthSqr(); + if (tmpDist > maxDist) maxDist = tmpDist; + } + + Vec4R& sphere = mLeafBoundingSpheres[n]; + + sphere[0] = avg[0]; + sphere[1] = avg[1]; + sphere[2] = avg[2]; + sphere[3] = maxDist * 2.0; // padded radius + } +} + + +class NodeBS +{ +public: + typedef std::pair IndexRange; + + NodeBS(std::vector& nodeBoundingSpheres, + const std::vector& leafRanges, + const std::vector& leafBoundingSpheres); + + inline void run(bool threaded = true); + + inline void operator()(const tbb::blocked_range&) const; + +private: + std::vector& mNodeBoundingSpheres; + const std::vector& mLeafRanges; + const std::vector& mLeafBoundingSpheres; +}; + +inline +NodeBS::NodeBS(std::vector& nodeBoundingSpheres, + const std::vector& leafRanges, + const std::vector& leafBoundingSpheres) + : mNodeBoundingSpheres(nodeBoundingSpheres) + , mLeafRanges(leafRanges) + , mLeafBoundingSpheres(leafBoundingSpheres) +{ +} + +inline void +NodeBS::run(bool threaded) +{ + if (threaded) { + tbb::parallel_for(tbb::blocked_range(0, mLeafRanges.size()), *this); + } else { + (*this)(tbb::blocked_range(0, mLeafRanges.size())); + } +} + +inline void +NodeBS::operator()(const tbb::blocked_range& range) const +{ + Vec3d avg, pos; + + for (size_t n = range.begin(); n != range.end(); ++n) { + + avg[0] = 0.0; + avg[1] = 0.0; + avg[2] = 0.0; + + int count = int(mLeafRanges[n].second) - int(mLeafRanges[n].first); + + for (size_t i = mLeafRanges[n].first; i < mLeafRanges[n].second; ++i) { + avg[0] += mLeafBoundingSpheres[i][0]; + avg[1] += mLeafBoundingSpheres[i][1]; + avg[2] += mLeafBoundingSpheres[i][2]; + } + + if (count > 1) avg *= float(1.0 / double(count)); + + + double maxDist = 0.0; + + for (size_t i = mLeafRanges[n].first; i < mLeafRanges[n].second; ++i) { + pos[0] = mLeafBoundingSpheres[i][0]; + pos[1] = mLeafBoundingSpheres[i][1]; + pos[2] = mLeafBoundingSpheres[i][2]; + + double tmpDist = (pos - avg).lengthSqr() + mLeafBoundingSpheres[i][3]; + if (tmpDist > maxDist) maxDist = tmpDist; + } + + Vec4R& sphere = mNodeBoundingSpheres[n]; + + sphere[0] = avg[0]; + sphere[1] = avg[1]; + sphere[2] = avg[2]; + sphere[3] = maxDist * 2.0; // padded radius + } +} + + + +//////////////////////////////////////// + + +template +class ClosestPointDist +{ +public: + typedef std::pair IndexRange; + + ClosestPointDist( + std::vector& instancePoints, + std::vector& instanceDistances, + const PointList& surfacePointList, + const std::vector& leafNodes, + const std::vector& leafRanges, + const std::vector& leafBoundingSpheres, + const std::vector& nodeBoundingSpheres, + size_t maxNodeLeafs, + bool transformPoints = false); + + + void run(bool threaded = true); + + + void operator()(const tbb::blocked_range&) const; + +private: + + void evalLeaf(size_t index, const IntLeafT& leaf) const; + void evalNode(size_t pointIndex, size_t nodeIndex) const; + + + std::vector& mInstancePoints; + std::vector& mInstanceDistances; + + const PointList& mSurfacePointList; + + const std::vector& mLeafNodes; + const std::vector& mLeafRanges; + const std::vector& mLeafBoundingSpheres; + const std::vector& mNodeBoundingSpheres; + + std::vector mLeafDistances, mNodeDistances; + + const bool mTransformPoints; + size_t mClosestPointIndex; +}; + + +template +ClosestPointDist::ClosestPointDist( + std::vector& instancePoints, + std::vector& instanceDistances, + const PointList& surfacePointList, + const std::vector& leafNodes, + const std::vector& leafRanges, + const std::vector& leafBoundingSpheres, + const std::vector& nodeBoundingSpheres, + size_t maxNodeLeafs, + bool transformPoints) + : mInstancePoints(instancePoints) + , mInstanceDistances(instanceDistances) + , mSurfacePointList(surfacePointList) + , mLeafNodes(leafNodes) + , mLeafRanges(leafRanges) + , mLeafBoundingSpheres(leafBoundingSpheres) + , mNodeBoundingSpheres(nodeBoundingSpheres) + , mLeafDistances(maxNodeLeafs, 0.0) + , mNodeDistances(leafRanges.size(), 0.0) + , mTransformPoints(transformPoints) + , mClosestPointIndex(0) +{ +} + + +template +void +ClosestPointDist::run(bool threaded) +{ + if (threaded) { + tbb::parallel_for(tbb::blocked_range(0, mInstancePoints.size()), *this); + } else { + (*this)(tbb::blocked_range(0, mInstancePoints.size())); + } +} + +template +void +ClosestPointDist::evalLeaf(size_t index, const IntLeafT& leaf) const +{ + typename IntLeafT::ValueOnCIter iter; + const Vec3s center = mInstancePoints[index]; + size_t& closestPointIndex = const_cast(mClosestPointIndex); + + for (iter = leaf.cbeginValueOn(); iter; ++iter) { + + const Vec3s& point = mSurfacePointList[iter.getValue()]; + float tmpDist = (point - center).lengthSqr(); + + if (tmpDist < mInstanceDistances[index]) { + mInstanceDistances[index] = tmpDist; + closestPointIndex = iter.getValue(); + } + } +} + + +template +void +ClosestPointDist::evalNode(size_t pointIndex, size_t nodeIndex) const +{ + const Vec3R& pos = mInstancePoints[pointIndex]; + float minDist = mInstanceDistances[pointIndex]; + size_t minDistIdx = 0; + Vec3R center; + bool updatedDist = false; + + for (size_t i = mLeafRanges[nodeIndex].first, n = 0; + i < mLeafRanges[nodeIndex].second; ++i, ++n) + { + float& distToLeaf = const_cast(mLeafDistances[n]); + + center[0] = mLeafBoundingSpheres[i][0]; + center[1] = mLeafBoundingSpheres[i][1]; + center[2] = mLeafBoundingSpheres[i][2]; + + distToLeaf = float((pos - center).lengthSqr() - mLeafBoundingSpheres[i][3]); + + if (distToLeaf < minDist) { + minDist = distToLeaf; + minDistIdx = i; + updatedDist = true; + } + } + + if (!updatedDist) return; + + evalLeaf(pointIndex, *mLeafNodes[minDistIdx]); + + for (size_t i = mLeafRanges[nodeIndex].first, n = 0; + i < mLeafRanges[nodeIndex].second; ++i, ++n) + { + if (mLeafDistances[n] < mInstanceDistances[pointIndex] && i != minDistIdx) { + evalLeaf(pointIndex, *mLeafNodes[i]); + } + } +} + + +template +void +ClosestPointDist::operator()(const tbb::blocked_range& range) const +{ + Vec3R center; + for (size_t n = range.begin(); n != range.end(); ++n) { + + const Vec3R& pos = mInstancePoints[n]; + float minDist = mInstanceDistances[n]; + size_t minDistIdx = 0; + + for (size_t i = 0, I = mNodeDistances.size(); i < I; ++i) { + float& distToNode = const_cast(mNodeDistances[i]); + + center[0] = mNodeBoundingSpheres[i][0]; + center[1] = mNodeBoundingSpheres[i][1]; + center[2] = mNodeBoundingSpheres[i][2]; + + distToNode = float((pos - center).lengthSqr() - mNodeBoundingSpheres[i][3]); + + if (distToNode < minDist) { + minDist = distToNode; + minDistIdx = i; + } + } + + evalNode(n, minDistIdx); + + for (size_t i = 0, I = mNodeDistances.size(); i < I; ++i) { + if (mNodeDistances[i] < mInstanceDistances[n] && i != minDistIdx) { + evalNode(n, i); + } + } + + mInstanceDistances[n] = std::sqrt(mInstanceDistances[n]); + + if (mTransformPoints) mInstancePoints[n] = mSurfacePointList[mClosestPointIndex]; + } +} + + +class UpdatePoints +{ +public: + UpdatePoints( + const Vec4s& sphere, + const std::vector& points, + std::vector& distances, + std::vector& mask, + bool overlapping); + + float radius() const { return mRadius; } + int index() const { return mIndex; } + + inline void run(bool threaded = true); + + + UpdatePoints(UpdatePoints&, tbb::split); + inline void operator()(const tbb::blocked_range& range); + void join(const UpdatePoints& rhs) + { + if (rhs.mRadius > mRadius) { + mRadius = rhs.mRadius; + mIndex = rhs.mIndex; + } + } + +private: + + const Vec4s& mSphere; + const std::vector& mPoints; + + std::vector& mDistances; + std::vector& mMask; + + bool mOverlapping; + float mRadius; + int mIndex; +}; + +inline +UpdatePoints::UpdatePoints( + const Vec4s& sphere, + const std::vector& points, + std::vector& distances, + std::vector& mask, + bool overlapping) + : mSphere(sphere) + , mPoints(points) + , mDistances(distances) + , mMask(mask) + , mOverlapping(overlapping) + , mRadius(0.0) + , mIndex(0) +{ +} + +inline +UpdatePoints::UpdatePoints(UpdatePoints& rhs, tbb::split) + : mSphere(rhs.mSphere) + , mPoints(rhs.mPoints) + , mDistances(rhs.mDistances) + , mMask(rhs.mMask) + , mOverlapping(rhs.mOverlapping) + , mRadius(rhs.mRadius) + , mIndex(rhs.mIndex) +{ +} + +inline void +UpdatePoints::run(bool threaded) +{ + if (threaded) { + tbb::parallel_reduce(tbb::blocked_range(0, mPoints.size()), *this); + } else { + (*this)(tbb::blocked_range(0, mPoints.size())); + } +} + +inline void +UpdatePoints::operator()(const tbb::blocked_range& range) +{ + Vec3s pos; + for (size_t n = range.begin(); n != range.end(); ++n) { + if (mMask[n]) continue; + + pos.x() = float(mPoints[n].x()) - mSphere[0]; + pos.y() = float(mPoints[n].y()) - mSphere[1]; + pos.z() = float(mPoints[n].z()) - mSphere[2]; + + float dist = pos.length(); + + if (dist < mSphere[3]) { + mMask[n] = 1; + continue; + } + + if (!mOverlapping) { + mDistances[n] = std::min(mDistances[n], (dist - mSphere[3])); + } + + if (mDistances[n] > mRadius) { + mRadius = mDistances[n]; + mIndex = int(n); + } + } +} + + +} // namespace internal + + +//////////////////////////////////////// + + +template +inline void +fillWithSpheres( + const GridT& grid, + std::vector& spheres, + int maxSphereCount, + bool overlapping, + float minRadius, + float maxRadius, + float isovalue, + int instanceCount, + InterrupterT* interrupter) +{ + spheres.clear(); + spheres.reserve(maxSphereCount); + + const bool addNBPoints = grid.activeVoxelCount() < 10000; + int instances = std::max(instanceCount, maxSphereCount); + + typedef typename GridT::TreeType TreeT; + typedef typename GridT::ValueType ValueT; + + typedef typename TreeT::template ValueConverter::Type BoolTreeT; + typedef typename TreeT::template ValueConverter::Type IntTreeT; + typedef typename TreeT::template ValueConverter::Type Int16TreeT; + + typedef boost::mt11213b RandGen; + RandGen mtRand(/*seed=*/0); + + const TreeT& tree = grid.tree(); + const math::Transform& transform = grid.transform(); + + std::vector instancePoints; + + { // Scatter candidate sphere centroids (instancePoints) + typename Grid::Ptr interiorMaskPtr; + + if (grid.getGridClass() == GRID_LEVEL_SET) { + interiorMaskPtr = sdfInteriorMask(grid, ValueT(isovalue)); + } else { + interiorMaskPtr = typename Grid::Ptr(Grid::create(false)); + interiorMaskPtr->setTransform(transform.copy()); + interiorMaskPtr->tree().topologyUnion(tree); + } + + if (interrupter && interrupter->wasInterrupted()) return; + + erodeVoxels(interiorMaskPtr->tree(), 1); + + instancePoints.reserve(instances); + internal::PointAccessor ptnAcc(instancePoints); + + UniformPointScatter scatter( + ptnAcc, Index64(addNBPoints ? (instances / 2) : instances), mtRand, interrupter); + + scatter(*interiorMaskPtr); + } + + if (interrupter && interrupter->wasInterrupted()) return; + + std::vector instanceRadius; + + ClosestSurfacePoint csp; + csp.initialize(grid, isovalue, interrupter); + + // add extra instance points in the interior narrow band. + if (instancePoints.size() < size_t(instances)) { + const Int16TreeT& signTree = csp.signTree(); + typename Int16TreeT::LeafNodeType::ValueOnCIter it; + typename Int16TreeT::LeafCIter leafIt = signTree.cbeginLeaf(); + + for (; leafIt; ++leafIt) { + for (it = leafIt->cbeginValueOn(); it; ++it) { + const int flags = it.getValue(); + if (!(0xE00 & flags) && (flags & 0x100)) { + instancePoints.push_back(transform.indexToWorld(it.getCoord())); + } + + if (instancePoints.size() == size_t(instances)) break; + } + if (instancePoints.size() == size_t(instances)) break; + } + } + + + if (interrupter && interrupter->wasInterrupted()) return; + + if (!csp.search(instancePoints, instanceRadius)) return; + + std::vector instanceMask(instancePoints.size(), 0); + float largestRadius = 0.0; + int largestRadiusIdx = 0; + + for (size_t n = 0, N = instancePoints.size(); n < N; ++n) { + if (instanceRadius[n] > largestRadius) { + largestRadius = instanceRadius[n]; + largestRadiusIdx = int(n); + } + } + + Vec3s pos; + Vec4s sphere; + minRadius = float(minRadius * transform.voxelSize()[0]); + maxRadius = float(maxRadius * transform.voxelSize()[0]); + + for (size_t s = 0, S = std::min(size_t(maxSphereCount), instancePoints.size()); s < S; ++s) { + + if (interrupter && interrupter->wasInterrupted()) return; + + largestRadius = std::min(maxRadius, largestRadius); + + if (s != 0 && largestRadius < minRadius) break; + + sphere[0] = float(instancePoints[largestRadiusIdx].x()); + sphere[1] = float(instancePoints[largestRadiusIdx].y()); + sphere[2] = float(instancePoints[largestRadiusIdx].z()); + sphere[3] = largestRadius; + + spheres.push_back(sphere); + instanceMask[largestRadiusIdx] = 1; + + internal::UpdatePoints op( + sphere, instancePoints, instanceRadius, instanceMask, overlapping); + op.run(); + + largestRadius = op.radius(); + largestRadiusIdx = op.index(); + } +} + +//////////////////////////////////////// + + +template +ClosestSurfacePoint::ClosestSurfacePoint() + : mIsInitialized(false) + , mLeafBoundingSpheres(0) + , mNodeBoundingSpheres(0) + , mLeafRanges(0) + , mLeafNodes(0) + , mSurfacePointList() + , mPointListSize(0) + , mMaxNodeLeafs(0) + , mMaxRadiusSqr(0.0) + , mIdxTreePt() +{ +} + +template +void +ClosestSurfacePoint::initialize(const GridT& grid, float isovalue) +{ + initialize(grid, isovalue, NULL); +} + + +template +template +void +ClosestSurfacePoint::initialize( + const GridT& grid, float isovalue, InterrupterT* interrupter) +{ + mIsInitialized = false; + typedef tree::LeafManager LeafManagerT; + typedef tree::LeafManager IntLeafManagerT; + typedef tree::LeafManager Int16LeafManagerT; + typedef typename GridT::ValueType ValueT; + + const TreeT& tree = grid.tree(); + const math::Transform& transform = grid.transform(); + + { // Extract surface point cloud + + { + LeafManagerT leafs(tree); + internal::SignData + signDataOp(tree, leafs, ValueT(isovalue)); + + signDataOp.run(); + + mSignTreePt = signDataOp.signTree(); + mIdxTreePt = signDataOp.idxTree(); + } + + if (interrupter && interrupter->wasInterrupted()) return; + + Int16LeafManagerT signLeafs(*mSignTreePt); + + std::vector regions(signLeafs.leafCount(), 0); + signLeafs.foreach(internal::CountPoints(regions)); + + mPointListSize = 0; + for (size_t tmp = 0, n = 0, N = regions.size(); n < N; ++n) { + tmp = regions[n]; + regions[n] = mPointListSize; + mPointListSize += tmp; + } + + if (mPointListSize == 0) return; + + mSurfacePointList.reset(new Vec3s[mPointListSize]); + + internal::GenPoints + pointOp(signLeafs, tree, *mIdxTreePt, mSurfacePointList, regions, transform, isovalue); + + pointOp.run(); + + mIdxTreePt->topologyUnion(*mSignTreePt); + } + + if (interrupter && interrupter->wasInterrupted()) return; + + // estimate max sphere radius (sqr dist) + CoordBBox bbox = grid.evalActiveVoxelBoundingBox(); + + Vec3s dim = transform.indexToWorld(bbox.min()) - + transform.indexToWorld(bbox.max()); + + dim[0] = std::abs(dim[0]); + dim[1] = std::abs(dim[1]); + dim[2] = std::abs(dim[2]); + + mMaxRadiusSqr = std::min(std::min(dim[0], dim[1]), dim[2]); + mMaxRadiusSqr *= 0.51f; + mMaxRadiusSqr *= mMaxRadiusSqr; + + + IntLeafManagerT idxLeafs(*mIdxTreePt); + + + typedef typename IntTreeT::RootNodeType IntRootNodeT; + typedef typename IntRootNodeT::NodeChainType IntNodeChainT; + BOOST_STATIC_ASSERT(boost::mpl::size::value > 1); + typedef typename boost::mpl::at >::type IntInternalNodeT; + + + typename IntTreeT::NodeCIter nIt = mIdxTreePt->cbeginNode(); + nIt.setMinDepth(IntTreeT::NodeCIter::LEAF_DEPTH - 1); + nIt.setMaxDepth(IntTreeT::NodeCIter::LEAF_DEPTH - 1); + + std::vector internalNodes; + + const IntInternalNodeT* node = NULL; + for (; nIt; ++nIt) { + nIt.getNode(node); + if (node) internalNodes.push_back(node); + } + + std::vector().swap(mLeafRanges); + mLeafRanges.resize(internalNodes.size()); + + std::vector().swap(mLeafNodes); + mLeafNodes.reserve(idxLeafs.leafCount()); + + typename IntInternalNodeT::ChildOnCIter leafIt; + mMaxNodeLeafs = 0; + for (size_t n = 0, N = internalNodes.size(); n < N; ++n) { + + mLeafRanges[n].first = mLeafNodes.size(); + + size_t leafCount = 0; + for (leafIt = internalNodes[n]->cbeginChildOn(); leafIt; ++leafIt) { + mLeafNodes.push_back(&(*leafIt)); + ++leafCount; + } + + mMaxNodeLeafs = std::max(leafCount, mMaxNodeLeafs); + + mLeafRanges[n].second = mLeafNodes.size(); + } + + std::vector().swap(mLeafBoundingSpheres); + mLeafBoundingSpheres.resize(mLeafNodes.size()); + + internal::LeafBS leafBS( + mLeafBoundingSpheres, mLeafNodes, transform, mSurfacePointList); + leafBS.run(); + + + std::vector().swap(mNodeBoundingSpheres); + mNodeBoundingSpheres.resize(internalNodes.size()); + + internal::NodeBS nodeBS(mNodeBoundingSpheres, mLeafRanges, mLeafBoundingSpheres); + nodeBS.run(); + mIsInitialized = true; +} + + +template +bool +ClosestSurfacePoint::search(std::vector& points, + std::vector& distances, bool transformPoints) +{ + if (!mIsInitialized) return false; + + distances.clear(); + distances.resize(points.size(), mMaxRadiusSqr); + + internal::ClosestPointDist cpd(points, distances, mSurfacePointList, + mLeafNodes, mLeafRanges, mLeafBoundingSpheres, mNodeBoundingSpheres, + mMaxNodeLeafs, transformPoints); + + cpd.run(); + + return true; +} + + +template +bool +ClosestSurfacePoint::search(const std::vector& points, std::vector& distances) +{ + return search(const_cast& >(points), distances, false); +} + + +template +bool +ClosestSurfacePoint::searchAndReplace(std::vector& points, + std::vector& distances) +{ + return search(points, distances, true); +} + + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TOOLS_VOLUME_TO_MESH_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tree/InternalNode.h b/nuparu/include/openvdb/include/openvdb/tree/InternalNode.h new file mode 100644 index 00000000..44fd6295 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tree/InternalNode.h @@ -0,0 +1,3201 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +/// +/// @todo Multi-thred topologyDifference + +#ifndef OPENVDB_TREE_INTERNALNODE_HAS_BEEN_INCLUDED +#define OPENVDB_TREE_INTERNALNODE_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // for io::readData(), etc. +#include // for Abs(), isExactlyEqual() +#include +#include +#include "Iterator.h" +#include "NodeUnion.h" + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tree { + +template struct SameInternalConfig; // forward declaration + + +template +class InternalNode +{ +public: + typedef _ChildNodeType ChildNodeType; + typedef typename ChildNodeType::LeafNodeType LeafNodeType; + typedef typename ChildNodeType::ValueType ValueType; + typedef typename ChildNodeType::BuildType BuildType; + typedef NodeUnion UnionType; + typedef util::NodeMask 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::Type is the type of an InternalNode having the same + /// child hierarchy and dimensions as this node but a different value type, T. + template + struct ValueConverter { + typedef InternalNode::Type, Log2Dim> Type; + }; + + /// @brief SameConfiguration::value is @c true if and only if OtherNodeType + /// is the type of an InternalNode with the same dimensions as this node and whose + /// ChildNodeType has the same configuration as this node's ChildNodeType. + template + struct SameConfiguration { + static const bool value = + SameInternalConfig::value; + }; + + + InternalNode() {} + + explicit InternalNode(const ValueType& offValue); + + InternalNode(const Coord&, const ValueType& fillValue, bool active = false); + +#ifndef OPENVDB_2_ABI_COMPATIBLE + InternalNode(PartialCreate, const Coord&, const ValueType& fillValue, bool active = false); +#endif + + /// @brief Deep copy constructor + /// + /// @note This method is multi-threaded! + InternalNode(const InternalNode&); + + /// @brief Value conversion copy constructor + /// + /// @note This method is multi-threaded! + template + explicit InternalNode(const InternalNode& other); + + /// @brief Topology copy constructor + /// + /// @note This method is multi-threaded! + template + InternalNode(const InternalNode& other, + const ValueType& background, TopologyCopy); + + /// @brief Topology copy constructor + /// + /// @note This method is multi-threaded! + template + InternalNode(const InternalNode& 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(), setItem() and/or modifyItem() methods. + + template + struct ChildIter: public SparseIteratorBase< + MaskIterT, ChildIter, NodeT, ChildT> + { + ChildIter() {} + ChildIter(const MaskIterT& iter, NodeT* parent): SparseIteratorBase< + MaskIterT, ChildIter, NodeT, ChildT>(iter, parent) {} + + ChildT& getItem(Index pos) const + { + assert(this->parent().isChildMaskOn(pos)); + return *(this->parent().getChildNode(pos)); + } + + // Note: setItem() can't be called on const iterators. + void setItem(Index pos, const ChildT& c) const { this->parent().resetChildNode(pos, &c); } + + // Note: modifyItem() isn't implemented, since it's not useful for child node pointers. + };// ChildIter + + template + struct ValueIter: public SparseIteratorBase< + MaskIterT, ValueIter, NodeT, ValueT> + { + ValueIter() {} + ValueIter(const MaskIterT& iter, NodeT* parent): SparseIteratorBase< + MaskIterT, ValueIter, 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); } + + // Note: modifyItem() can't be called on const iterators. + template + void modifyItem(Index pos, const ModifyOp& op) const + { + op(this->parent().mNodes[pos].getValue()); + } + };// ValueIter + + template + struct DenseIter: public DenseIteratorBase< + MaskDenseIterator, DenseIter, NodeT, ChildT, ValueT> + { + typedef DenseIteratorBase BaseT; + typedef typename BaseT::NonConstValueType NonConstValueT; + + DenseIter() {} + DenseIter(const MaskDenseIterator& iter, NodeT* parent): + DenseIteratorBase(iter, parent) {} + + bool getItem(Index pos, ChildT*& child, NonConstValueT& value) const + { + if (this->parent().isChildMaskOn(pos)) { + child = this->parent().getChildNode(pos); + return true; + } + child = NULL; + value = this->parent().mNodes[pos].getValue(); + return false; + } + + // Note: setItem() can't be called on const iterators. + void setItem(Index pos, ChildT* child) const + { + this->parent().resetChildNode(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 ChildOnIter; + typedef ChildIter ChildOnCIter; + typedef ValueIter ChildOffIter; + typedef ValueIter ChildOffCIter; + typedef DenseIter ChildAllIter; + typedef DenseIter ChildAllCIter; + + typedef ValueIter ValueOnIter; + typedef ValueIter ValueOnCIter; + typedef ValueIter ValueOffIter; + typedef ValueIter ValueOffCIter; + typedef ValueIter ValueAllIter; + typedef ValueIter 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); } + /// @warning This iterator will also visit child nodes so use isChildMaskOn to skip them! + ValueOffCIter cbeginValueOff() const { return ValueOffCIter(mValueMask.beginOff(), this); } + ValueAllCIter cbeginValueAll() const { return ValueAllCIter(mChildMask.beginOff(), this); } + ValueOnCIter beginValueOn() const { return cbeginValueOn(); } + /// @warning This iterator will also visit child nodes so use isChildMaskOn to skip them! + ValueOffCIter beginValueOff() const { return cbeginValueOff(); } + ValueAllCIter beginValueAll() const { return cbeginValueAll(); } + ValueOnIter beginValueOn() { return ValueOnIter(mValueMask.beginOn(), this); } + /// @warning This iterator will also visit child nodes so use isChildMaskOn to skip them! + 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& dims); + static Index getChildDim() { return ChildNodeType::DIM; } + + /// Return the linear table offset of the given global or local coordinates. + static Index coordToOffset(const Coord& xyz); + /// @brief Return the local coordinates for a linear table offset, + /// where offset 0 has coordinates (0, 0, 0). + static void offsetToLocalCoord(Index n, Coord& xyz); + /// Return the global coordinates for a linear table offset. + Coord offsetToGlobalCoord(Index n) const; + + /// Return the grid index coordinates of this node's local origin. + const Coord& origin() const { return mOrigin; } + /// Set the grid index coordinates of this node's local origin. + void setOrigin(const Coord& origin) { mOrigin = origin; } + + Index32 leafCount() const; + Index32 nonLeafCount() const; + Index64 onVoxelCount() const; + Index64 offVoxelCount() const; + Index64 onLeafVoxelCount() const; + Index64 offLeafVoxelCount() const; + Index64 onTileCount() 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. + /// If visitVoxels is false LeafNodes will be approximated as dense, i.e. with all + /// voxels active. Else the individual active voxels are visited to produce a tight bbox. + void evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels = true) 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. + /// + /// @note This method also returns @c false if this node contains any child nodes. + bool isConstant(ValueType& constValue, bool& state, + const ValueType& tolerance = zeroVal()) const; + + /// Return @c true if all of this node's tables entries have + /// the same active @a state and the values are in the range + /// (@a maxValue + @a minValue)/2 +/- @a tolerance. + /// + /// @param minValue Is updated with the minimum of all values IF method + /// returns @c true. Else the value is undefined! + /// @param maxValue Is updated with the maximum of all values IF method + /// returns @c true. Else the value is undefined! + /// @param state Is updated with the state of all values IF method + /// returns @c true. Else the value is undefined! + /// @param tolerance The tolerance used to determine if values are + /// approximatly constant. + /// + /// @note This method also returns @c false if this node contains any child nodes. + bool isConstant(ValueType& minValue, ValueType& maxValue, + bool& state, const ValueType& tolerance = zeroVal()) 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 of the voxel at the given coordinates but don't change its value. + void setActiveState(const Coord& xyz, bool on); + /// Set the value of the voxel at the given coordinates but don't change its active state. + void setValueOnly(const Coord& xyz, const ValueType& value); + /// 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); + /// Mark the voxel at the given coordinates as inactive but don't change its value. + void setValueOff(const Coord& xyz); + /// Set the value of the voxel at the given coordinates and mark the voxel as inactive. + void setValueOff(const Coord& xyz, const ValueType& value); + + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + template + void modifyValue(const Coord& xyz, const ModifyOp& op); + /// Apply a functor to the voxel at the given coordinates. + template + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op); + + /// 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 + 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 + 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 + 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 + void setValueOnlyAndCache(const Coord& xyz, const ValueType& value, AccessorT&); + + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// 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 + void modifyValueAndCache(const Coord& xyz, const ModifyOp& op, AccessorT&); + + /// Apply a functor to the voxel at the given coordinates. + /// 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 + void modifyValueAndActiveStateAndCache(const Coord& xyz, const ModifyOp& op, 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 + 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 + void setActiveStateAndCache(const Coord& xyz, bool on, AccessorT&); + + /// Return, in @a value, 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. + /// @return @c true if the voxel at the given coordinates is active + /// @note Used internally by ValueAccessor. + template + 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 + 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); + void readBuffers(std::istream&, const CoordBBox&, bool fromHalf = false); + + + // + // Aux methods + // + /// @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); + + /// Change the sign of all the values represented in this node and + /// its child nodes. + void negate(); + + /// @brief Densify active tiles, i.e., replace them with leaf-level active voxels. + /// @param threaded if true, this operation is multi-threaded (over the internal nodes). + void voxelizeActiveTiles(bool threaded = 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 + void copyToDense(const CoordBBox& bbox, DenseT& dense) const; + + /// @brief Efficiently merge another tree into this tree using one of several schemes. + /// @warning This operation cannibalizes the other tree. + template + void merge(InternalNode& other, const ValueType& background, const ValueType& otherBackground); + + /// @brief Merge, using one of several schemes, this node (and its descendants) + /// with a tile of the same dimensions and the given value and active state. + template void merge(const ValueType& tileValue, bool tileActive); + + /// @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 + void topologyUnion(const InternalNode& other); + + /// @brief Intersects 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 only if the corresponding + /// value was already active AND if it is active in the other tree. Also, a + /// resulting value maps to a voxel if the corresponding value + /// already mapped to an active voxel in either of the two grids + /// and it maps to an active tile or voxel in the other grid. + /// + /// @note This operation can delete branches in this grid if they + /// overlap with inactive tiles in the other grid. Likewise active + /// voxels can be turned into unactive voxels resulting in leaf + /// nodes with no active values. Thus, it is recommended to + /// subsequently call prune. + template + void topologyIntersection(const InternalNode& other, + const ValueType& background); + + /// @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 the original voxel is + /// active in this node and inactive in the other node. + /// + /// @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 + void topologyDifference(const InternalNode& other, + const ValueType& background); + + template + void combine(InternalNode& other, CombineOp&); + template + void combine(const ValueType& value, bool valueIsActive, CombineOp&); + + template + void combine2(const InternalNode& other0, const OtherNodeType& other1, CombineOp&); + template + void combine2(const ValueType& value, const OtherNodeType& other, bool valIsActive, CombineOp&); + template + void combine2(const InternalNode& other, const OtherValueType&, bool valIsActive, 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 void visitActiveBBox(BBoxOp&) const; + + template void visit(VisitorOp&); + template void visit(VisitorOp&) const; + + template + void visit2Node(OtherNodeType& other, VisitorOp&); + template + void visit2Node(OtherNodeType& other, VisitorOp&) const; + template + void visit2(IterT& otherIter, VisitorOp&, bool otherIsLHS = false); + template + void visit2(IterT& otherIter, VisitorOp&, bool otherIsLHS = false) const; + + /// Set all voxels that lie outside the given axis-aligned box to the background. + void clip(const CoordBBox&, const ValueType& background); + + /// @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()); + + /// @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, update the accessor with pointers + /// to the nodes along the path from the root node to the node containing the coordinate. + template + 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 + 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 Delete any existing child branch at the specified offset and add a tile. + void addTile(Index offset, 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 + void addTileAndCache(Index level, const Coord& xyz, const ValueType&, bool state, AccessorT&); + + //@{ + /// @brief Return a pointer to the node that contains voxel (x, y, z). + /// If no such node exists, return NULL. + template NodeType* probeNode(const Coord& xyz); + template const NodeType* probeConstNode(const Coord& xyz) const; + //@} + + //@{ + /// @brief Same as probeNode() 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 + NodeType* probeNodeAndCache(const Coord& xyz, AccessorT&); + template + const NodeType* probeConstNodeAndCache(const Coord& xyz, AccessorT&) const; + //@} + + //@{ + /// @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); + const LeafNodeType* probeConstLeaf(const Coord& xyz) const; + const LeafNodeType* probeLeaf(const Coord& xyz) const; + //@} + + //@{ + /// @brief Same as probeLeaf() 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 + LeafNodeType* probeLeafAndCache(const Coord& xyz, AccessorT& acc); + template + const LeafNodeType* probeConstLeafAndCache(const Coord& xyz, AccessorT& acc) const; + template + const LeafNodeType* probeLeafAndCache(const Coord& xyz, AccessorT& acc) const; + //@} + + /// @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, update the accessor with pointers + /// to the nodes along the path from the root node to the node containing the coordinate. + template + LeafNodeType* touchLeafAndCache(const Coord& xyz, AccessorT&); + + //@{ + /// @brief Adds all nodes of a certain type to a container with the following API: + /// @code + /// struct ArrayT { + /// typedef value_type;// defines the type of nodes to be added to the array + /// void push_back(value_type nodePtr);// method that add nodes to the array + /// }; + /// @endcode + /// @details An example of a wrapper around a c-style array is: + /// @code + /// struct MyArray { + /// typedef LeafType* value_type; + /// value_type* ptr; + /// MyArray(value_type* array) : ptr(array) {} + /// void push_back(value_type leaf) { *ptr++ = leaf; } + ///}; + /// @endcode + /// @details An example that constructs a list of pointer to all leaf nodes is: + /// @code + /// std::vector array;//most std contains have the required API + /// array.reserve(tree.leafCount());//this is a fast preallocation. + /// tree.getNodes(array); + /// @endcode + template + void getNodes(ArrayT& array); + template + void getNodes(ArrayT& array) const; + //@} + + /// @brief Steals all nodes of a certain type from the tree and + /// adds them to a container with the following API: + /// @code + /// struct ArrayT { + /// typedef value_type;// defines the type of nodes to be added to the array + /// void push_back(value_type nodePtr);// method that add nodes to the array + /// }; + /// @endcode + /// @details An example of a wrapper around a c-style array is: + /// @code + /// struct MyArray { + /// typedef LeafType* value_type; + /// value_type* ptr; + /// MyArray(value_type* array) : ptr(array) {} + /// void push_back(value_type leaf) { *ptr++ = leaf; } + ///}; + /// @endcode + /// @details An example that constructs a list of pointer to all leaf nodes is: + /// @code + /// std::vector array;//most std contains have the required API + /// array.reserve(tree.leafCount());//this is a fast preallocation. + /// tree.stealNodes(array); + /// @endcode + template + void stealNodes(ArrayT& array, const ValueType& value, bool state); + + /// @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 + bool hasSameTopology(const InternalNode* other) const; + +protected: + //@{ + /// Allow iterators to call mask accessor methods (setValueMask(), setChildMask(), etc.). + /// @todo Make mask accessors public? + friend class IteratorBase; + friend class IteratorBase; + friend class IteratorBase; + //@} + + /// @brief During topology-only construction, access is needed + /// to protected/private members of other template instances. + template 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(); } + const NodeMaskType& getValueMask() const { return mValueMask; } + const NodeMaskType& getChildMask() const { return mChildMask; } + NodeMaskType getValueOffMask() const + { + NodeMaskType mask = mValueMask; + mask |= mChildMask; + mask.toggle(); + return mask; + } + const UnionType* getTable() const { return mNodes; } +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);//assumes a tile + void resetChildNode(Index i, ChildNodeType* child);//checks for an existing child + ChildNodeType* unsetChildNode(Index i, const ValueType& value); + + template + static inline void doVisit(NodeT&, VisitorOp&); + + template + static inline void doVisit2Node(NodeT&, OtherNodeT&, VisitorOp&); + + template + static inline void doVisit2(NodeT&, OtherChildAllIterT&, VisitorOp&, bool otherIsLHS); + + ///@{ + /// @brief Returns a pointer to the child node at the linear offset n. + /// @warning This protected method assumes that a child node exists at + /// the specified linear offset! + ChildNodeType* getChildNode(Index n); + const ChildNodeType* getChildNode(Index n) const; + ///@} + + ///@{ + /// @brief Protected member classes for recursive multi-threading + struct VoxelizeActiveTiles; + template struct DeepCopy; + template struct TopologyCopy1; + template struct TopologyCopy2; + template struct TopologyUnion; + template struct TopologyDifference; + template struct TopologyIntersection; + ///@} + + 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 + + +//////////////////////////////////////// + + +//@{ +/// Helper metafunction used to implement InternalNode::SameConfiguration +/// (which, as an inner class, can't be independently specialized) +template +struct SameInternalConfig { + static const bool value = false; +}; + +template +struct SameInternalConfig > { + static const bool value = ChildT1::template SameConfiguration::value; +}; +//@} + + +//////////////////////////////////////// + + +template +inline +InternalNode::InternalNode(const ValueType& background) +{ + for (Index i = 0; i < NUM_VALUES; ++i) mNodes[i].setValue(background); +} + + +template +inline +InternalNode::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); +} + + +#ifndef OPENVDB_2_ABI_COMPATIBLE +// For InternalNodes, the PartialCreate constructor is identical to its +// non-PartialCreate counterpart. +template +inline +InternalNode::InternalNode(PartialCreate, + const Coord& origin, const ValueType& val, bool active) + : mOrigin(origin[0] & ~(DIM-1), origin[1] & ~(DIM-1), origin[2] & ~(DIM-1)) +{ + if (active) mValueMask.setOn(); + for (Index i = 0; i < NUM_VALUES; ++i) mNodes[i].setValue(val); +} +#endif + +template +template +struct InternalNode::DeepCopy +{ + DeepCopy(const OtherInternalNode* source, InternalNode* target) : s(source), t(target) { + tbb::parallel_for(tbb::blocked_range(0, NUM_VALUES), *this); + //(*this)(tbb::blocked_range(0, NUM_VALUES));//serial + } + void operator()(const tbb::blocked_range &r) const { + for (Index i = r.begin(), end=r.end(); i!=end; ++i) { + if (s->mChildMask.isOff(i)) { + t->mNodes[i].setValue(ValueType(s->mNodes[i].getValue())); + } else { + t->mNodes[i].setChild(new ChildNodeType(*(s->mNodes[i].getChild()))); + } + } + } + const OtherInternalNode* s; + InternalNode* t; +};// DeepCopy + +template +inline +InternalNode::InternalNode(const InternalNode& other): + mChildMask(other.mChildMask), + mValueMask(other.mValueMask), + mOrigin(other.mOrigin) +{ + DeepCopy > tmp(&other, this); +} + + +// Copy-construct from a node with the same configuration but a different ValueType. +template +template +inline +InternalNode::InternalNode(const InternalNode& other) + : mChildMask(other.mChildMask) + , mValueMask(other.mValueMask) + , mOrigin(other.mOrigin) +{ + DeepCopy > tmp(&other, this); +} + +template +template +struct InternalNode::TopologyCopy1 +{ + TopologyCopy1(const OtherInternalNode* source, InternalNode* target, + const ValueType& background) : s(source), t(target), b(background) { + tbb::parallel_for(tbb::blocked_range(0, NUM_VALUES), *this); + //(*this)(tbb::blocked_range(0, NUM_VALUES));//serial + } + void operator()(const tbb::blocked_range &r) const { + for (Index i = r.begin(), end=r.end(); i!=end; ++i) { + if (s->isChildMaskOn(i)) { + t->mNodes[i].setChild(new ChildNodeType(*(s->mNodes[i].getChild()), + b, TopologyCopy())); + } else { + t->mNodes[i].setValue(b); + } + } + } + const OtherInternalNode* s; + InternalNode* t; + const ValueType &b; +};// TopologyCopy1 + +template +template +inline +InternalNode::InternalNode(const InternalNode& other, + const ValueType& background, TopologyCopy): + mChildMask(other.mChildMask), + mValueMask(other.mValueMask), + mOrigin(other.mOrigin) +{ + TopologyCopy1 > tmp(&other, this, background); +} + +template +template +struct InternalNode::TopologyCopy2 +{ + TopologyCopy2(const OtherInternalNode* source, InternalNode* target, + const ValueType& offValue, const ValueType& onValue) + : s(source), t(target), offV(offValue), onV(onValue) { + tbb::parallel_for(tbb::blocked_range(0, NUM_VALUES), *this); + } + void operator()(const tbb::blocked_range &r) const { + for (Index i = r.begin(), end=r.end(); i!=end; ++i) { + if (s->isChildMaskOn(i)) { + t->mNodes[i].setChild(new ChildNodeType(*(s->mNodes[i].getChild()), + offV, onV, TopologyCopy())); + } else { + t->mNodes[i].setValue(s->isValueMaskOn(i) ? onV : offV); + } + } + } + const OtherInternalNode* s; + InternalNode* t; + const ValueType &offV, &onV; + };// TopologyCopy2 + +template +template +inline +InternalNode::InternalNode(const InternalNode& other, + const ValueType& offValue, + const ValueType& onValue, TopologyCopy): + mChildMask(other.mChildMask), + mValueMask(other.mValueMask), + mOrigin(other.mOrigin) +{ + TopologyCopy2 > tmp(&other, this, offValue, onValue); +} + + +template +inline +InternalNode::~InternalNode() +{ + for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) { + delete mNodes[iter.pos()].getChild(); + } +} + + +//////////////////////////////////////// + + +template +inline Index32 +InternalNode::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 +inline Index32 +InternalNode::nonLeafCount() const +{ + Index32 sum = 1; + if (ChildNodeType::getLevel() == 0) return sum; + for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) { + sum += iter->nonLeafCount(); + } + return sum; +} + + +template +inline Index64 +InternalNode::onVoxelCount() const +{ + Index64 sum = ChildT::NUM_VOXELS * mValueMask.countOn(); + for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) { + sum += iter->onVoxelCount(); + } + return sum; +} + + +template +inline Index64 +InternalNode::offVoxelCount() const +{ + Index64 sum = ChildT::NUM_VOXELS * (NUM_VALUES-mValueMask.countOn()-mChildMask.countOn()); + for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) { + sum += iter->offVoxelCount(); + } + return sum; +} + + +template +inline Index64 +InternalNode::onLeafVoxelCount() const +{ + Index64 sum = 0; + for (ChildOnCIter iter = this->beginChildOn(); iter; ++iter) { + sum += mNodes[iter.pos()].getChild()->onLeafVoxelCount(); + } + return sum; +} + + +template +inline Index64 +InternalNode::offLeafVoxelCount() const +{ + Index64 sum = 0; + for (ChildOnCIter iter = this->beginChildOn(); iter; ++iter) { + sum += mNodes[iter.pos()].getChild()->offLeafVoxelCount(); + } + return sum; +} + +template +inline Index64 +InternalNode::onTileCount() const +{ + Index64 sum = mValueMask.countOn(); + for (ChildOnCIter iter = this->cbeginChildOn(); LEVEL>1 && iter; ++iter) { + sum += iter->onTileCount(); + } + return sum; +} + +template +inline Index64 +InternalNode::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 +inline void +InternalNode::evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels) const +{ + if (bbox.isInside(this->getNodeBoundingBox())) return; + + for (ValueOnCIter i = this->cbeginValueOn(); i; ++i) { + bbox.expand(i.getCoord(), ChildT::DIM); + } + for (ChildOnCIter i = this->cbeginChildOn(); i; ++i) { + i->evalActiveBoundingBox(bbox, visitVoxels); + } +} + + +//////////////////////////////////////// + + +template +inline void +InternalNode::prune(const ValueType& tolerance) +{ + bool state = false; + ValueType value = zeroVal(); + for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) { + const Index i = iter.pos(); + ChildT* child = mNodes[i].getChild(); + child->prune(tolerance); + if (child->isConstant(value, state, tolerance)) { + delete child; + mChildMask.setOff(i); + mValueMask.set(i, state); + mNodes[i].setValue(value); + } + } +} + + +//////////////////////////////////////// + + +template +template +inline NodeT* +InternalNode::stealNode(const Coord& xyz, const ValueType& value, bool state) +{ + if ((NodeT::LEVEL == ChildT::LEVEL && !(boost::is_same::value)) || + NodeT::LEVEL > ChildT::LEVEL) return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + const Index n = this->coordToOffset(xyz); + if (mChildMask.isOff(n)) return NULL; + ChildT* child = mNodes[n].getChild(); + if (boost::is_same::value) { + mChildMask.setOff(n); + mValueMask.set(n, state); + mNodes[n].setValue(value); + } + return (boost::is_same::value) + ? reinterpret_cast(child) + : child->template stealNode(xyz, value, state); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +//////////////////////////////////////// + + +template +template +inline NodeT* +InternalNode::probeNode(const Coord& xyz) +{ + if ((NodeT::LEVEL == ChildT::LEVEL && !(boost::is_same::value)) || + NodeT::LEVEL > ChildT::LEVEL) return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + const Index n = this->coordToOffset(xyz); + if (mChildMask.isOff(n)) return NULL; + ChildT* child = mNodes[n].getChild(); + return (boost::is_same::value) + ? reinterpret_cast(child) + : child->template probeNode(xyz); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +template +template +inline NodeT* +InternalNode::probeNodeAndCache(const Coord& xyz, AccessorT& acc) +{ + if ((NodeT::LEVEL == ChildT::LEVEL && !(boost::is_same::value)) || + NodeT::LEVEL > ChildT::LEVEL) return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + const Index n = this->coordToOffset(xyz); + if (mChildMask.isOff(n)) return NULL; + ChildT* child = mNodes[n].getChild(); + acc.insert(xyz, child); + return (boost::is_same::value) + ? reinterpret_cast(child) + : child->template probeNodeAndCache(xyz, acc); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +template +template +inline const NodeT* +InternalNode::probeConstNode(const Coord& xyz) const +{ + if ((NodeT::LEVEL == ChildT::LEVEL && !(boost::is_same::value)) || + NodeT::LEVEL > ChildT::LEVEL) return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + const Index n = this->coordToOffset(xyz); + if (mChildMask.isOff(n)) return NULL; + const ChildT* child = mNodes[n].getChild(); + return (boost::is_same::value) + ? reinterpret_cast(child) + : child->template probeConstNode(xyz); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +template +template +inline const NodeT* +InternalNode::probeConstNodeAndCache(const Coord& xyz, AccessorT& acc) const +{ + if ((NodeT::LEVEL == ChildT::LEVEL && !(boost::is_same::value)) || + NodeT::LEVEL > ChildT::LEVEL) return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + const Index n = this->coordToOffset(xyz); + if (mChildMask.isOff(n)) return NULL; + const ChildT* child = mNodes[n].getChild(); + acc.insert(xyz, child); + return (boost::is_same::value) + ? reinterpret_cast(child) + : child->template probeConstNodeAndCache(xyz, acc); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +//////////////////////////////////////// + + +template +inline typename ChildT::LeafNodeType* +InternalNode::probeLeaf(const Coord& xyz) +{ + return this->template probeNode(xyz); +} + + +template +template +inline typename ChildT::LeafNodeType* +InternalNode::probeLeafAndCache(const Coord& xyz, AccessorT& acc) +{ + return this->template probeNodeAndCache(xyz, acc); +} + + +template +template +inline const typename ChildT::LeafNodeType* +InternalNode::probeLeafAndCache(const Coord& xyz, AccessorT& acc) const +{ + return this->probeConstLeafAndCache(xyz, acc); +} + + +template +inline const typename ChildT::LeafNodeType* +InternalNode::probeConstLeaf(const Coord& xyz) const +{ + return this->template probeConstNode(xyz); +} + + +template +template +inline const typename ChildT::LeafNodeType* +InternalNode::probeConstLeafAndCache(const Coord& xyz, AccessorT& acc) const +{ + return this->template probeConstNodeAndCache(xyz, acc); +} + + +//////////////////////////////////////// + + +template +inline void +InternalNode::addLeaf(LeafNodeType* leaf) +{ + assert(leaf != NULL); + const Coord& xyz = leaf->origin(); + const Index n = this->coordToOffset(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(leaf); + } + this->setChildNode(n, child); + } else { + if (ChildT::LEVEL>0) { + child = mNodes[n].getChild(); + } else { + delete mNodes[n].getChild(); + child = reinterpret_cast(leaf); + mNodes[n].setChild(child); + } + } + child->addLeaf(leaf); +} + + +template +template +inline void +InternalNode::addLeafAndCache(LeafNodeType* leaf, AccessorT& acc) +{ + assert(leaf != NULL); + const Coord& xyz = leaf->origin(); + const Index n = this->coordToOffset(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(leaf); + } + this->setChildNode(n, child); + } 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(leaf); + mNodes[n].setChild(child); + } + } + child->addLeafAndCache(leaf, acc); +} + + +//////////////////////////////////////// + + +template +inline void +InternalNode::addTile(Index n, const ValueType& value, bool state) +{ + assert(n < NUM_VALUES); + this->makeChildNodeEmpty(n, value); + mValueMask.set(n, state); +} + + +template +inline void +InternalNode::addTile(Index level, const Coord& xyz, + const ValueType& value, bool state) +{ + if (LEVEL >= level) { + const Index n = this->coordToOffset(xyz); + if (mChildMask.isOff(n)) {// tile case + if (LEVEL > level) { + ChildT* child = new ChildT(xyz, mNodes[n].getValue(), mValueMask.isOn(n)); + this->setChildNode(n, child); + 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 +template +inline void +InternalNode::addTileAndCache(Index level, const Coord& xyz, + const ValueType& value, bool state, AccessorT& acc) +{ + if (LEVEL >= level) { + const Index n = this->coordToOffset(xyz); + if (mChildMask.isOff(n)) {// tile case + if (LEVEL > level) { + ChildT* child = new ChildT(xyz, mNodes[n].getValue(), mValueMask.isOn(n)); + this->setChildNode(n, child); + 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 +inline typename ChildT::LeafNodeType* +InternalNode::touchLeaf(const Coord& xyz) +{ + const Index n = this->coordToOffset(xyz); + ChildT* child = NULL; + if (mChildMask.isOff(n)) { + child = new ChildT(xyz, mNodes[n].getValue(), mValueMask.isOn(n)); + this->setChildNode(n, child); + } else { + child = mNodes[n].getChild(); + } + return child->touchLeaf(xyz); +} + + +template +template +inline typename ChildT::LeafNodeType* +InternalNode::touchLeafAndCache(const Coord& xyz, AccessorT& acc) +{ + const Index n = this->coordToOffset(xyz); + if (mChildMask.isOff(n)) { + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), mValueMask.isOn(n))); + } + acc.insert(xyz, mNodes[n].getChild()); + return mNodes[n].getChild()->touchLeafAndCache(xyz, acc); +} + + +//////////////////////////////////////// + + +template +inline bool +InternalNode::isConstant(ValueType& value, bool& state, + const ValueType& tolerance) const +{ + if ( !(mChildMask.isOff()) ) return false; + + state = mValueMask.isOn(); + if (!(state || mValueMask.isOff())) return false;// Are values neither active nor inactive? + + value = mNodes[0].getValue(); + for (Index i = 1; i < NUM_VALUES; ++i) { + if ( !math::isApproxEqual(mNodes[i].getValue(), value, tolerance) ) return false; + } + return true; +} + +//////////////////////////////////////// + + +template +inline bool +InternalNode::isConstant(ValueType& minValue, ValueType& maxValue, + bool& state, const ValueType& tolerance) const +{ + if ( !(mChildMask.isOff()) ) return false; + + state = mValueMask.isOn(); + if (!(state || mValueMask.isOff())) return false;// Are values neither active nor inactive? + + const ValueType range = 2 * tolerance; + minValue = maxValue = mNodes[0].getValue(); + for (Index i = 1; i < NUM_VALUES; ++i) { + const ValueType& v = mNodes[i].getValue(); + if (v < minValue) { + if ((maxValue - v) > range) return false; + minValue = v; + } else if (v > maxValue) { + if ((v - minValue) > range) return false; + maxValue = v; + } + } + return true; +} + + +//////////////////////////////////////// + + +template +inline bool +InternalNode::hasActiveTiles() const +{ + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + 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; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +template +inline bool +InternalNode::isValueOn(const Coord& xyz) const +{ + const Index n = this->coordToOffset(xyz); + if (this->isChildMaskOff(n)) return this->isValueMaskOn(n); + return mNodes[n].getChild()->isValueOn(xyz); +} + +template +template +inline bool +InternalNode::isValueOnAndCache(const Coord& xyz, AccessorT& acc) const +{ + const Index n = this->coordToOffset(xyz); + if (this->isChildMaskOff(n)) return this->isValueMaskOn(n); + acc.insert(xyz, mNodes[n].getChild()); + return mNodes[n].getChild()->isValueOnAndCache(xyz, acc); +} + + +template +inline const typename ChildT::ValueType& +InternalNode::getValue(const Coord& xyz) const +{ + const Index n = this->coordToOffset(xyz); + return this->isChildMaskOff(n) ? mNodes[n].getValue() + : mNodes[n].getChild()->getValue(xyz); +} + +template +template +inline const typename ChildT::ValueType& +InternalNode::getValueAndCache(const Coord& xyz, AccessorT& acc) const +{ + const Index n = this->coordToOffset(xyz); + if (this->isChildMaskOn(n)) { + acc.insert(xyz, mNodes[n].getChild()); + return mNodes[n].getChild()->getValueAndCache(xyz, acc); + } + return mNodes[n].getValue(); +} + + +template +inline Index +InternalNode::getValueLevel(const Coord& xyz) const +{ + const Index n = this->coordToOffset(xyz); + return this->isChildMaskOff(n) ? LEVEL : mNodes[n].getChild()->getValueLevel(xyz); +} + +template +template +inline Index +InternalNode::getValueLevelAndCache(const Coord& xyz, AccessorT& acc) const +{ + const Index n = this->coordToOffset(xyz); + if (this->isChildMaskOn(n)) { + acc.insert(xyz, mNodes[n].getChild()); + return mNodes[n].getChild()->getValueLevelAndCache(xyz, acc); + } + return LEVEL; +} + + +template +inline bool +InternalNode::probeValue(const Coord& xyz, ValueType& value) const +{ + const Index n = this->coordToOffset(xyz); + if (this->isChildMaskOff(n)) { + value = mNodes[n].getValue(); + return this->isValueMaskOn(n); + } + return mNodes[n].getChild()->probeValue(xyz, value); +} + +template +template +inline bool +InternalNode::probeValueAndCache(const Coord& xyz, + ValueType& value, AccessorT& acc) const +{ + const Index n = this->coordToOffset(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 +inline void +InternalNode::setValueOff(const Coord& xyz) +{ + const Index n = this->coordToOffset(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. + hasChild = true; + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), /*active=*/true)); + } + if (hasChild) mNodes[n].getChild()->setValueOff(xyz); +} + + +template +inline void +InternalNode::setValueOn(const Coord& xyz) +{ + const Index n = this->coordToOffset(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. + hasChild = true; + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), /*active=*/false)); + } + if (hasChild) mNodes[n].getChild()->setValueOn(xyz); +} + + +template +inline void +InternalNode::setValueOff(const Coord& xyz, const ValueType& value) +{ + const Index n = InternalNode::coordToOffset(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. + hasChild = true; + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active)); + } + } + if (hasChild) mNodes[n].getChild()->setValueOff(xyz, value); +} + +template +template +inline void +InternalNode::setValueOffAndCache(const Coord& xyz, + const ValueType& value, AccessorT& acc) +{ + const Index n = InternalNode::coordToOffset(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. + hasChild = true; + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active)); + } + } + if (hasChild) { + ChildT* child = mNodes[n].getChild(); + acc.insert(xyz, child); + child->setValueOffAndCache(xyz, value, acc); + } +} + + +template +inline void +InternalNode::setValueOn(const Coord& xyz, const ValueType& value) +{ + const Index n = this->coordToOffset(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. + hasChild = true; + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active)); + } + } + if (hasChild) mNodes[n].getChild()->setValueOn(xyz, value); +} + +template +template +inline void +InternalNode::setValueAndCache(const Coord& xyz, + const ValueType& value, AccessorT& acc) +{ + const Index n = this->coordToOffset(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. + hasChild = true; + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active)); + } + } + if (hasChild) { + acc.insert(xyz, mNodes[n].getChild()); + mNodes[n].getChild()->setValueAndCache(xyz, value, acc); + } +} + + +template +inline void +InternalNode::setValueOnly(const Coord& xyz, const ValueType& value) +{ + const Index n = this->coordToOffset(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); + hasChild = true; + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active)); + } + if (hasChild) mNodes[n].getChild()->setValueOnly(xyz, value); +} + +template +template +inline void +InternalNode::setValueOnlyAndCache(const Coord& xyz, + const ValueType& value, AccessorT& acc) +{ + const Index n = this->coordToOffset(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); + hasChild = true; + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active)); + } + if (hasChild) { + acc.insert(xyz, mNodes[n].getChild()); + mNodes[n].getChild()->setValueOnlyAndCache(xyz, value, acc); + } +} + + +template +inline void +InternalNode::setActiveState(const Coord& xyz, bool on) +{ + const Index n = this->coordToOffset(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. + // 'on' is the voxel's new state, therefore '!on' is the tile's current state + hasChild = true; + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), !on)); + } + } + if (hasChild) mNodes[n].getChild()->setActiveState(xyz, on); +} + +template +template +inline void +InternalNode::setActiveStateAndCache(const Coord& xyz, bool on, AccessorT& acc) +{ + const Index n = this->coordToOffset(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. + // 'on' is the voxel's new state, therefore '!on' is the tile's current state + hasChild = true; + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), !on)); + } + } + if (hasChild) { + ChildT* child = mNodes[n].getChild(); + acc.insert(xyz, child); + child->setActiveStateAndCache(xyz, on, acc); + } +} + + +template +inline void +InternalNode::setValuesOn() +{ + mValueMask = !mChildMask; + for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) { + mNodes[iter.pos()].getChild()->setValuesOn(); + } +} + + +template +template +inline void +InternalNode::modifyValue(const Coord& xyz, const ModifyOp& op) +{ + const Index n = InternalNode::coordToOffset(xyz); + bool hasChild = this->isChildMaskOn(n); + if (!hasChild) { + // Need to create a child if the tile is inactive, + // in order to activate voxel (x, y, z). + const bool active = this->isValueMaskOn(n); + bool createChild = !active; + if (!createChild) { + // Need to create a child if applying the functor + // to the tile value produces a different value. + const ValueType& tileVal = mNodes[n].getValue(); + ValueType modifiedVal = tileVal; + op(modifiedVal); + createChild = !math::isExactlyEqual(tileVal, modifiedVal); + } + if (createChild) { + hasChild = true; + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active)); + } + } + if (hasChild) mNodes[n].getChild()->modifyValue(xyz, op); +} + +template +template +inline void +InternalNode::modifyValueAndCache(const Coord& xyz, const ModifyOp& op, + AccessorT& acc) +{ + const Index n = InternalNode::coordToOffset(xyz); + bool hasChild = this->isChildMaskOn(n); + if (!hasChild) { + // Need to create a child if the tile is inactive, + // in order to activate voxel (x, y, z). + const bool active = this->isValueMaskOn(n); + bool createChild = !active; + if (!createChild) { + // Need to create a child if applying the functor + // to the tile value produces a different value. + const ValueType& tileVal = mNodes[n].getValue(); + ValueType modifiedVal = tileVal; + op(modifiedVal); + createChild = !math::isExactlyEqual(tileVal, modifiedVal); + } + if (createChild) { + hasChild = true; + this->setChildNode(n, new ChildNodeType(xyz, mNodes[n].getValue(), active)); + } + } + if (hasChild) { + ChildNodeType* child = mNodes[n].getChild(); + acc.insert(xyz, child); + child->modifyValueAndCache(xyz, op, acc); + } +} + + +template +template +inline void +InternalNode::modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) +{ + const Index n = InternalNode::coordToOffset(xyz); + bool hasChild = this->isChildMaskOn(n); + if (!hasChild) { + const bool tileState = this->isValueMaskOn(n); + const ValueType& tileVal = mNodes[n].getValue(); + bool modifiedState = !tileState; + ValueType modifiedVal = tileVal; + op(modifiedVal, modifiedState); + // Need to create a child if applying the functor to the tile + // produces a different value or active state. + if (modifiedState != tileState || !math::isExactlyEqual(modifiedVal, tileVal)) { + hasChild = true; + this->setChildNode(n, new ChildNodeType(xyz, tileVal, tileState)); + } + } + if (hasChild) mNodes[n].getChild()->modifyValueAndActiveState(xyz, op); +} + +template +template +inline void +InternalNode::modifyValueAndActiveStateAndCache( + const Coord& xyz, const ModifyOp& op, AccessorT& acc) +{ + const Index n = InternalNode::coordToOffset(xyz); + bool hasChild = this->isChildMaskOn(n); + if (!hasChild) { + const bool tileState = this->isValueMaskOn(n); + const ValueType& tileVal = mNodes[n].getValue(); + bool modifiedState = !tileState; + ValueType modifiedVal = tileVal; + op(modifiedVal, modifiedState); + // Need to create a child if applying the functor to the tile + // produces a different value or active state. + if (modifiedState != tileState || !math::isExactlyEqual(modifiedVal, tileVal)) { + hasChild = true; + this->setChildNode(n, new ChildNodeType(xyz, tileVal, tileState)); + } + } + if (hasChild) { + ChildNodeType* child = mNodes[n].getChild(); + acc.insert(xyz, child); + child->modifyValueAndActiveStateAndCache(xyz, op, acc); + } +} + + +//////////////////////////////////////// + + +template +inline void +InternalNode::clip(const CoordBBox& clipBBox, const ValueType& background) +{ + CoordBBox nodeBBox = this->getNodeBoundingBox(); + if (!clipBBox.hasOverlap(nodeBBox)) { + // This node lies completely outside the clipping region. Fill it with background tiles. + this->fill(nodeBBox, background, /*active=*/false); + } else if (clipBBox.isInside(nodeBBox)) { + // This node lies completely inside the clipping region. Leave it intact. + return; + } + + // This node isn't completely contained inside the clipping region. + // Clip tiles and children, and replace any that lie outside the region + // with background tiles. + + for (Index pos = 0; pos < NUM_VALUES; ++pos) { + const Coord xyz = this->offsetToGlobalCoord(pos); // tile or child origin + CoordBBox tileBBox(xyz, xyz.offsetBy(ChildT::DIM - 1)); // tile or child bounds + if (!clipBBox.hasOverlap(tileBBox)) { + // This table entry lies completely outside the clipping region. + // Replace it with a background tile. + this->makeChildNodeEmpty(pos, background); + mValueMask.setOff(pos); + } else if (!clipBBox.isInside(tileBBox)) { + // This table entry does not lie completely inside the clipping region + // and must be clipped. + if (this->isChildMaskOn(pos)) { + mNodes[pos].getChild()->clip(clipBBox, background); + } else { + // Replace this tile with a background tile, then fill the clip region + // with the tile's original value. (This might create a child branch.) + tileBBox.intersect(clipBBox); + const ValueType val = mNodes[pos].getValue(); + const bool on = this->isValueMaskOn(pos); + mNodes[pos].setValue(background); + mValueMask.setOff(pos); + this->fill(tileBBox, val, on); + } + } else { + // This table entry lies completely inside the clipping region. Leave it intact. + } + } +} + + +//////////////////////////////////////// + + +template +inline void +InternalNode::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->coordToOffset(xyz); + tileMin = this->offsetToGlobalCoord(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)); + this->setChildNode(n, 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 +template +inline void +InternalNode::copyToDense(const CoordBBox& bbox, DenseT& dense) const +{ + typedef typename DenseT::ValueType DenseValueType; + + const size_t xStride = dense.xStride(), yStride = dense.yStride(), zStride = dense.zStride(); + 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->coordToOffset(xyz); + // Get max coordinates of the child node that contains voxel xyz. + max = this->offsetToGlobalCoord(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); + DenseValueType* a0 = dense.data() + zStride*sub.min()[2]; + for (Int32 x=sub.min()[0], ex=sub.max()[0]+1; x +inline void +InternalNode::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 values(new ValueType[NUM_VALUES]); + const ValueType zero = zeroVal(); + 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 +inline void +InternalNode::readTopology(std::istream& is, bool fromHalf) +{ +#ifndef OPENVDB_2_ABI_COMPATIBLE + const ValueType background = (!io::getGridBackgroundValuePtr(is) ? zeroVal() + : *static_cast(io::getGridBackgroundValuePtr(is))); +#endif + + 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 = +#ifdef OPENVDB_2_ABI_COMPATIBLE + new ChildNodeType(offsetToGlobalCoord(i), zeroVal()); +#else + new ChildNodeType(PartialCreate(), offsetToGlobalCoord(i), background); +#endif + mNodes[i].setChild(child); + child->readTopology(is); + } else { + ValueType value; + is.read(reinterpret_cast(&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 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) { +#ifdef OPENVDB_2_ABI_COMPATIBLE + ChildNodeType* child = new ChildNodeType(iter.getCoord(), zeroVal()); +#else + ChildNodeType* child = new ChildNodeType(PartialCreate(), iter.getCoord(), background); +#endif + mNodes[iter.pos()].setChild(child); + child->readTopology(is, fromHalf); + } + } +} + + +//////////////////////////////////////// + + +template +inline const typename ChildT::ValueType& +InternalNode::getFirstValue() const +{ + return (this->isChildMaskOn(0) ? mNodes[0].getChild()->getFirstValue() : mNodes[0].getValue()); +} + + +template +inline const typename ChildT::ValueType& +InternalNode::getLastValue() const +{ + const Index n = NUM_VALUES - 1; + return (this->isChildMaskOn(n) ? mNodes[n].getChild()->getLastValue() : mNodes[n].getValue()); +} + + +//////////////////////////////////////// + + +template +inline void +InternalNode::negate() +{ + for (Index i = 0; i < NUM_VALUES; ++i) { + if (this->isChildMaskOn(i)) { + mNodes[i].getChild()->negate(); + } else { + mNodes[i].setValue(math::negative(mNodes[i].getValue())); + } + } + +} + +//////////////////////////////////////// + +template +struct InternalNode::VoxelizeActiveTiles +{ + VoxelizeActiveTiles(InternalNode &node) : mNode(&node) { + //(*this)(tbb::blocked_range(0, NUM_VALUES));//single thread for debugging + tbb::parallel_for(tbb::blocked_range(0, NUM_VALUES), *this); + + node.mChildMask |= node.mValueMask; + node.mValueMask.setOff(); + } + void operator()(const tbb::blocked_range &r) const + { + for (Index i = r.begin(), end=r.end(); i!=end; ++i) { + if (mNode->mChildMask.isOn(i)) {// Loop over node's child nodes + mNode->mNodes[i].getChild()->voxelizeActiveTiles(true); + } else if (mNode->mValueMask.isOn(i)) {// Loop over node's active tiles + const Coord &ijk = mNode->offsetToGlobalCoord(i); + ChildNodeType *child = new ChildNodeType(ijk, mNode->mNodes[i].getValue(), true); + child->voxelizeActiveTiles(true); + mNode->mNodes[i].setChild(child); + } + } + } + InternalNode* mNode; +};// VoxelizeActiveTiles + +template +inline void +InternalNode::voxelizeActiveTiles(bool threaded) +{ + if (threaded) { + VoxelizeActiveTiles tmp(*this); + } else { + for (ValueOnIter iter = this->beginValueOn(); iter; ++iter) { + this->setChildNode(iter.pos(), new ChildNodeType(iter.getCoord(), iter.getValue(), true)); + } + for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) + iter->voxelizeActiveTiles(false); + } +} + + +//////////////////////////////////////// + + +template +template +inline void +InternalNode::merge(InternalNode& other, + const ValueType& background, const ValueType& otherBackground) +{ + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + + switch (Policy) { + + case MERGE_ACTIVE_STATES: + default: + { + for (ChildOnIter iter = other.beginChildOn(); iter; ++iter) { + const Index n = iter.pos(); + if (mChildMask.isOn(n)) { + // Merge this node's child with the other node's child. + mNodes[n].getChild()->template merge(*iter, + background, otherBackground); + } else if (mValueMask.isOff(n)) { + // Replace this node's inactive tile with the other node's child + // and replace the other node's child with a tile of undefined value + // (which is okay since the other tree is assumed to be cannibalized + // in the process of merging). + ChildNodeType* child = other.mNodes[n].getChild(); + other.mChildMask.setOff(n); + child->resetBackground(otherBackground, background); + this->setChildNode(n, child); + } + } + + // Copy active tile values. + for (ValueOnCIter iter = other.cbeginValueOn(); iter; ++iter) { + const Index n = iter.pos(); + if (mValueMask.isOff(n)) { + // Replace this node's child or inactive tile with the other node's active tile. + this->makeChildNodeEmpty(n, iter.getValue()); + mValueMask.setOn(n); + } + } + break; + } + + case MERGE_NODES: + { + for (ChildOnIter iter = other.beginChildOn(); iter; ++iter) { + const Index n = iter.pos(); + if (mChildMask.isOn(n)) { + // Merge this node's child with the other node's child. + mNodes[n].getChild()->template merge(*iter, background, otherBackground); + } else { + // Replace this node's tile (regardless of its active state) with + // the other node's child and replace the other node's child with + // a tile of undefined value (which is okay since the other tree + // is assumed to be cannibalized in the process of merging). + ChildNodeType* child = other.mNodes[n].getChild(); + other.mChildMask.setOff(n); + child->resetBackground(otherBackground, background); + this->setChildNode(n, child); + } + } + break; + } + + case MERGE_ACTIVE_STATES_AND_NODES: + { + // Transfer children from the other tree to this tree. + for (ChildOnIter iter = other.beginChildOn(); iter; ++iter) { + const Index n = iter.pos(); + if (mChildMask.isOn(n)) { + // Merge this node's child with the other node's child. + mNodes[n].getChild()->template merge(*iter, background, otherBackground); + } else { + // Replace this node's tile with the other node's child, leaving the other + // node with an inactive tile of undefined value (which is okay since + // the other tree is assumed to be cannibalized in the process of merging). + ChildNodeType* child = other.mNodes[n].getChild(); + other.mChildMask.setOff(n); + child->resetBackground(otherBackground, background); + if (mValueMask.isOn(n)) { + // Merge the child with this node's active tile. + child->template merge(mNodes[n].getValue(), /*on=*/true); + mValueMask.setOff(n); + } + mChildMask.setOn(n); + mNodes[n].setChild(child); + } + } + + // Merge active tiles into this tree. + for (ValueOnCIter iter = other.cbeginValueOn(); iter; ++iter) { + const Index n = iter.pos(); + if (mChildMask.isOn(n)) { + // Merge the other node's active tile into this node's child. + mNodes[n].getChild()->template merge(iter.getValue(), /*on=*/true); + } else if (mValueMask.isOff(n)) { + // Replace this node's inactive tile with the other node's active tile. + mNodes[n].setValue(iter.getValue()); + mValueMask.setOn(n); + } + } + break; + } + + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +template +template +inline void +InternalNode::merge(const ValueType& tileValue, bool tileActive) +{ + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + + if (Policy != MERGE_ACTIVE_STATES_AND_NODES) return; + + // For MERGE_ACTIVE_STATES_AND_NODES, inactive tiles in the other tree are ignored. + if (!tileActive) return; + + // Iterate over this node's children and inactive tiles. + for (ValueOffIter iter = this->beginValueOff(); iter; ++iter) { + const Index n = iter.pos(); + if (mChildMask.isOn(n)) { + // Merge the other node's active tile into this node's child. + mNodes[n].getChild()->template merge(tileValue, /*on=*/true); + } else { + // Replace this node's inactive tile with the other node's active tile. + iter.setValue(tileValue); + mValueMask.setOn(n); + } + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + +//////////////////////////////////////// + +template +template +struct InternalNode::TopologyUnion +{ + typedef typename NodeMaskType::Word W; + struct A { inline void operator()(W &tV, const W& sV, const W& tC) const + { tV = (tV | sV) & ~tC; } + }; + TopologyUnion(const OtherInternalNode* source, InternalNode* target) : s(source), t(target) { + //(*this)(tbb::blocked_range(0, NUM_VALUES));//single thread for debugging + tbb::parallel_for(tbb::blocked_range(0, NUM_VALUES), *this); + + // Bit processing is done in a single thread! + t->mChildMask |= s->mChildMask;//serial but very fast bitwise post-process + A op; + t->mValueMask.foreach(s->mValueMask, t->mChildMask, op); + assert((t->mValueMask & t->mChildMask).isOff());//no overlapping active tiles and child nodes + } + void operator()(const tbb::blocked_range &r) const { + for (Index i = r.begin(), end=r.end(); i!=end; ++i) { + if (s->mChildMask.isOn(i)) {// Loop over other node's child nodes + const typename OtherInternalNode::ChildNodeType& other = *(s->mNodes[i].getChild()); + if (t->mChildMask.isOn(i)) {//this has a child node + t->mNodes[i].getChild()->topologyUnion(other); + } else {// this is a tile so replace it with a child branch with identical topology + ChildT* child = new ChildT(other, t->mNodes[i].getValue(), TopologyCopy()); + if (t->mValueMask.isOn(i)) child->setValuesOn();//activate all values + t->mNodes[i].setChild(child); + } + } else if (s->mValueMask.isOn(i) && t->mChildMask.isOn(i)) { + t->mNodes[i].getChild()->setValuesOn(); + } + } + } + const OtherInternalNode* s; + InternalNode* t; +};// TopologyUnion + +template +template +inline void +InternalNode::topologyUnion(const InternalNode& other) +{ + TopologyUnion > tmp(&other, this); +} + +template +template +struct InternalNode::TopologyIntersection +{ + typedef typename NodeMaskType::Word W; + struct A { inline void operator()(W &tC, const W& sC, const W& sV, const W& tV) const + { tC = (tC & (sC | sV)) | (tV & sC); } + }; + TopologyIntersection(const OtherInternalNode* source, InternalNode* target, + const ValueType& background) : s(source), t(target), b(background) { + //(*this)(tbb::blocked_range(0, NUM_VALUES));//single thread for debugging + tbb::parallel_for(tbb::blocked_range(0, NUM_VALUES), *this); + + // Bit processing is done in a single thread! + A op; + t->mChildMask.foreach(s->mChildMask, s->mValueMask, t->mValueMask, op); + + t->mValueMask &= s->mValueMask; + assert((t->mValueMask & t->mChildMask).isOff());//no overlapping active tiles and child nodes + } + void operator()(const tbb::blocked_range &r) const { + for (Index i = r.begin(), end=r.end(); i!=end; ++i) { + if (t->mChildMask.isOn(i)) {// Loop over this node's child nodes + ChildT* child = t->mNodes[i].getChild(); + if (s->mChildMask.isOn(i)) {//other also has a child node + child->topologyIntersection(*(s->mNodes[i].getChild()), b); + } else if (s->mValueMask.isOff(i)) {//other is an inactive tile + delete child;//convert child to an inactive tile + t->mNodes[i].setValue(b); + } + } else if (t->mValueMask.isOn(i) && s->mChildMask.isOn(i)) {//active tile -> a branch + t->mNodes[i].setChild(new ChildT(*(s->mNodes[i].getChild()), + t->mNodes[i].getValue(), TopologyCopy())); + } + } + } + const OtherInternalNode* s; + InternalNode* t; + const ValueType& b; +};// TopologyIntersection + +template +template +inline void +InternalNode::topologyIntersection(const InternalNode& other, + const ValueType& background) +{ + TopologyIntersection > tmp(&other, this, background); +} + +template +template +struct InternalNode::TopologyDifference +{ + typedef typename NodeMaskType::Word W; + struct A {inline void operator()(W &tC, const W& sC, const W& sV, const W& tV) const + { tC = (tC & (sC | ~sV)) | (tV & sC); } + }; + struct B {inline void operator()(W &tV, const W& sC, const W& sV, const W& tC) const + { tV &= ~((tC & sV) | (sC | sV)); } + }; + TopologyDifference(const OtherInternalNode* source, InternalNode* target, + const ValueType& background) : s(source), t(target), b(background) { + //(*this)(tbb::blocked_range(0, NUM_VALUES));//single thread for debugging + tbb::parallel_for(tbb::blocked_range(0, NUM_VALUES), *this); + + // Bit processing is done in a single thread! + const NodeMaskType oldChildMask(t->mChildMask);//important to avoid cross pollution + A op1; + t->mChildMask.foreach(s->mChildMask, s->mValueMask, t->mValueMask, op1); + + B op2; + t->mValueMask.foreach(t->mChildMask, s->mValueMask, oldChildMask, op2); + assert((t->mValueMask & t->mChildMask).isOff());//no overlapping active tiles and child nodes + } + void operator()(const tbb::blocked_range &r) const { + for (Index i = r.begin(), end=r.end(); i!=end; ++i) { + if (t->mChildMask.isOn(i)) {// Loop over this node's child nodes + ChildT* child = t->mNodes[i].getChild(); + if (s->mChildMask.isOn(i)) { + child->topologyDifference(*(s->mNodes[i].getChild()), b); + } else if (s->mValueMask.isOn(i)) { + delete child;//convert child to an inactive tile + t->mNodes[i].setValue(b); + } + } else if (t->mValueMask.isOn(i)) {//this is an active tile + if (s->mChildMask.isOn(i)) { + const typename OtherInternalNode::ChildNodeType& other = *(s->mNodes[i].getChild()); + ChildT* child = new ChildT(other.origin(), t->mNodes[i].getValue(), true); + child->topologyDifference(other, b); + t->mNodes[i].setChild(child);//replace the active tile with a child branch + } + } + } + } + const OtherInternalNode* s; + InternalNode* t; + const ValueType& b; +};// TopologyDifference + +template +template +inline void +InternalNode::topologyDifference(const InternalNode& other, + const ValueType& background) +{ + TopologyDifference > tmp(&other, this, background); +} + +//////////////////////////////////////// + + +template +template +inline void +InternalNode::combine(InternalNode& other, CombineOp& op) +{ + const ValueType zero = zeroVal(); + + CombineArgs 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 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); + this->setChildNode(i, 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 +template +inline void +InternalNode::combine(const ValueType& value, bool valueIsActive, CombineOp& op) +{ + CombineArgs 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 +template +inline void +InternalNode::combine2(const InternalNode& other0, const OtherNodeType& other1, + CombineOp& op) +{ + CombineArgs 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 { + if (this->isChildMaskOff(i)) { + // Add a new child with the same coordinates, etc. as the other node's child. + const Coord& childOrigin = other0.isChildMaskOn(i) + ? other0.mNodes[i].getChild()->origin() + : other1.mNodes[i].getChild()->origin(); + this->setChildNode(i, new ChildNodeType(childOrigin, 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 +template +inline void +InternalNode::combine2(const ValueType& value, const OtherNodeType& other, + bool valueIsActive, CombineOp& op) +{ + CombineArgs 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 { + typename OtherNodeType::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. + this->setChildNode(i, 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 +template +inline void +InternalNode::combine2(const InternalNode& other, const OtherValueType& value, + bool valueIsActive, CombineOp& op) +{ + CombineArgs 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. + this->setChildNode(i, + new ChildNodeType(otherChild->origin(), 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 +template +inline void +InternalNode::visitActiveBBox(BBoxOp& op) const +{ + for (ValueOnCIter i = this->cbeginValueOn(); i; ++i) { +#ifdef _MSC_VER + op.operator()(CoordBBox::createCube(i.getCoord(), ChildNodeType::DIM)); +#else + op.template operator()(CoordBBox::createCube(i.getCoord(), ChildNodeType::DIM)); +#endif + } + if (op.template descent()) { + for (ChildOnCIter i = this->cbeginChildOn(); i; ++i) i->visitActiveBBox(op); + } else { + for (ChildOnCIter i = this->cbeginChildOn(); i; ++i) { +#ifdef _MSC_VER + op.operator()(i->getNodeBoundingBox()); +#else + op.template operator()(i->getNodeBoundingBox()); +#endif + } + } +} + + +template +template +inline void +InternalNode::visit(VisitorOp& op) +{ + doVisit(*this, op); +} + + +template +template +inline void +InternalNode::visit(VisitorOp& op) const +{ + doVisit(*this, op); +} + + +template +template +inline void +InternalNode::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 +template +inline void +InternalNode::visit2Node(OtherNodeType& other, VisitorOp& op) +{ + doVisit2Node(*this, other, op); +} + + +template +template +inline void +InternalNode::visit2Node(OtherNodeType& other, VisitorOp& op) const +{ + doVisit2Node(*this, other, op); +} + + +template +template< + typename NodeT, + typename OtherNodeT, + typename VisitorOp, + typename ChildAllIterT, + typename OtherChildAllIterT> +inline void +InternalNode::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(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 +template +inline void +InternalNode::visit2(OtherChildAllIterType& otherIter, + VisitorOp& op, bool otherIsLHS) +{ + doVisit2( + *this, otherIter, op, otherIsLHS); +} + + +template +template +inline void +InternalNode::visit2(OtherChildAllIterType& otherIter, + VisitorOp& op, bool otherIsLHS) const +{ + doVisit2( + *this, otherIter, op, otherIsLHS); +} + + +template +template +inline void +InternalNode::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( + 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 +inline void +InternalNode::writeBuffers(std::ostream& os, bool toHalf) const +{ + for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) { + iter->writeBuffers(os, toHalf); + } +} + + +template +inline void +InternalNode::readBuffers(std::istream& is, bool fromHalf) +{ + for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) { + iter->readBuffers(is, fromHalf); + } +} + + +template +inline void +InternalNode::readBuffers(std::istream& is, + const CoordBBox& clipBBox, bool fromHalf) +{ + for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) { + // Stream in the branch rooted at this child. + // (We can't skip over children that lie outside the clipping region, + // because buffers are serialized in depth-first order and need to be + // unserialized in the same order.) + iter->readBuffers(is, clipBBox, fromHalf); + } + + // Get this tree's background value. + ValueType background = zeroVal(); + if (const void* bgPtr = io::getGridBackgroundValuePtr(is)) { + background = *static_cast(bgPtr); + } + this->clip(clipBBox, background); +} + + +//////////////////////////////////////// + + +template +void +InternalNode::getNodeLog2Dims(std::vector& dims) +{ + dims.push_back(Log2Dim); + ChildNodeType::getNodeLog2Dims(dims); +} + + +template +inline void +InternalNode::offsetToLocalCoord(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< +inline Index +InternalNode::coordToOffset(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 +inline Coord +InternalNode::offsetToGlobalCoord(Index n) const +{ + Coord local; + this->offsetToLocalCoord(n, local); + local <<= ChildT::TOTAL; + return local + this->origin(); +} + +//////////////////////////////////////// + +template +template +inline void +InternalNode::getNodes(ArrayT& array) +{ + typedef typename ArrayT::value_type T; + BOOST_STATIC_ASSERT(boost::is_pointer::value); + typedef typename boost::mpl::if_::type>, + const ChildT, ChildT>::type ArrayChildT; + for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (boost::is_same::value) { + array.push_back(reinterpret_cast(mNodes[iter.pos()].getChild())); + } else { + iter->getNodes(array);//descent + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } +} + +template +template +inline void +InternalNode::getNodes(ArrayT& array) const +{ + typedef typename ArrayT::value_type T; + BOOST_STATIC_ASSERT(boost::is_pointer::value); + BOOST_STATIC_ASSERT(boost::is_const::type>::value); + for (ChildOnCIter iter = this->cbeginChildOn(); iter; ++iter) { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (boost::is_same::value) { + array.push_back(reinterpret_cast(mNodes[iter.pos()].getChild())); + } else { + iter->getNodes(array);//descent + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } +} + +//////////////////////////////////////// + +template +template +inline void +InternalNode::stealNodes(ArrayT& array, const ValueType& value, bool state) +{ + typedef typename ArrayT::value_type T; + BOOST_STATIC_ASSERT(boost::is_pointer::value); + typedef typename boost::mpl::if_::type>, + const ChildT, ChildT>::type ArrayChildT; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + for (ChildOnIter iter = this->beginChildOn(); iter; ++iter) { + const Index n = iter.pos(); + if (boost::is_same::value) { + array.push_back(reinterpret_cast(mNodes[n].getChild())); + mValueMask.set(n, state); + mNodes[n].setValue(value); + } else { + iter->stealNodes(array, value, state);//descent + } + } + if (boost::is_same::value) mChildMask.setOff(); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + +//////////////////////////////////////// + + +template +inline void +InternalNode::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(), math::negative(oldBackground))) { + mNodes[i].setValue(math::negative(newBackground)); + } + } + } +} + +template +template +inline bool +InternalNode::hasSameTopology( + const InternalNode* 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 +inline void +InternalNode::resetChildNode(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 +inline void +InternalNode::setChildNode(Index i, ChildNodeType* child) +{ + assert(child); + assert(mChildMask.isOff(i)); + mChildMask.setOn(i); + mValueMask.setOff(i); + mNodes[i].setChild(child); +} + + +template +inline ChildT* +InternalNode::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 +inline void +InternalNode::makeChildNodeEmpty(Index n, const ValueType& value) +{ + delete this->unsetChildNode(n, value); +} + +template +inline ChildT* +InternalNode::getChildNode(Index n) +{ + assert(this->isChildMaskOn(n)); + return mNodes[n].getChild(); +} + + +template +inline const ChildT* +InternalNode::getChildNode(Index n) const +{ + assert(this->isChildMaskOn(n)); + return mNodes[n].getChild(); +} + +} // namespace tree +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TREE_INTERNALNODE_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tree/Iterator.h b/nuparu/include/openvdb/include/openvdb/tree/Iterator.h new file mode 100644 index 00000000..41fa104d --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tree/Iterator.h @@ -0,0 +1,290 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include +#include +#include +#include +#include + +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 +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().offsetToGlobalCoord(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 +{ + typedef NodeT NodeType; + typedef ItemT ValueType; + typedef typename boost::remove_const::type NonConstNodeType; + typedef typename boost::remove_const::type NonConstValueType; + static const bool IsSparseIterator = true, IsDenseIterator = false; + + SparseIteratorBase() {} + SparseIteratorBase(const MaskIterT& iter, NodeT* parent): + IteratorBase(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(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 + { + BOOST_STATIC_ASSERT(!boost::is_const::value); + static_cast(this)->setItem(this->pos(), value); // static polymorphism + } + /// @brief Apply a functor to the item to which this iterator is pointing. + /// (Not valid for const iterators.) + /// @param op a functor of the form void op(ValueType&) const that modifies + /// its argument in place + /// @see Tree::modifyValue() + template + void modifyValue(const ModifyOp& op) const + { + BOOST_STATIC_ASSERT(!boost::is_const::value); + static_cast(this)->modifyItem(this->pos(), op); // 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 +{ + typedef NodeT NodeType; + typedef UnsetItemT ValueType; + typedef SetItemT ChildNodeType; + typedef typename boost::remove_const::type NonConstNodeType; + typedef typename boost::remove_const::type NonConstValueType; + typedef typename boost::remove_const::type NonConstChildNodeType; + static const bool IsSparseIterator = false, IsDenseIterator = true; + + DenseIteratorBase() {} + DenseIteratorBase(const MaskIterT& iter, NodeT* parent): + IteratorBase(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(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(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(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(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-2016 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/nuparu/include/openvdb/include/openvdb/tree/LeafManager.h b/nuparu/include/openvdb/include/openvdb/tree/LeafManager.h new file mode 100644 index 00000000..c06a0243 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tree/LeafManager.h @@ -0,0 +1,846 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +/// +/// @brief 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. +/// @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 synching +/// (i.e., copying the contents) of these buffers. + +#ifndef OPENVDB_TREE_LEAFMANAGER_HAS_BEEN_INCLUDED +#define OPENVDB_TREE_LEAFMANAGER_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 struct TreeTraits { + static const bool IsConstTree = false; + typedef typename TreeT::LeafIter LeafIterType; +}; +template struct TreeTraits { + 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 +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 +class LeafManager +{ +public: + typedef TreeT TreeType; + typedef typename TreeT::ValueType ValueType; + typedef typename TreeT::RootNodeType RootNodeType; + typedef typename TreeType::LeafNodeType NonConstLeafType; + typedef typename CopyConstness::Type LeafType; + typedef LeafType LeafNodeType; + typedef typename leafmgr::TreeTraits::LeafIterType LeafIterType; + typedef typename LeafType::Buffer NonConstBufferType; + typedef typename CopyConstness::Type BufferType; + typedef tbb::blocked_range RangeType;//leaf index range + static const Index DEPTH = 2;//root + leafs + + static const bool IsConstTree = leafmgr::TreeTraits::IsConstTree; + + class LeafRange + { + public: + class Iterator + { + public: + Iterator(const LeafRange& range, size_t pos): mRange(range), mPos(pos) + { + assert(this->isValid()); + } + Iterator& operator=(const Iterator& other) + { + mRange = other.mRange; mPos = other.mPos; return *this; + } + /// 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; } + /// Return @c true if the position of this iterator is in a valid range. + 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) || (&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); + } + + /// @brief Constructor from a tree reference and an existing array + /// of pointers to LeafNodes from said tree. This c-tor is only + /// intended for experts that try to squice out a + LeafManager(TreeType& tree, LeafType** begin, LeafType** end, + size_t auxBuffersPerLeaf=0, bool serial=false) + : mTree(&tree) + , mLeafCount(end-begin) + , mAuxBufferCount(0) + , mAuxBuffersPerLeaf(auxBuffersPerLeaf) + , mLeafs(new LeafType*[mLeafCount]) + , mAuxBuffers(NULL) + , mTask(0) + , mIsMaster(true) + { + size_t n = mLeafCount; + LeafType **target = mLeafs, **source = begin; + while (n--) *target++ = *source++; + if (auxBuffersPerLeaf) this->initAuxBuffers(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(); + } + + /// 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 a const reference to tree associated with this manager. + const TreeType& tree() const { return *mTree; } + + /// Return a reference to the tree associated with this manager. + TreeType& tree() { return *mTree; } + + /// Return a const reference to root node associated with this manager. + const RootNodeType& root() const { return mTree->root(); } + + /// Return a reference to the root node associated with this manager. + RootNodeType& root() { return mTree->root(); } + + /// 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 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 + } + + /// @brief Threaded method that applies a user-supplied functor + /// to each leaf node in the LeafManager. + /// + /// @details The user-supplied functor needs to define the methods + /// required for tbb::parallel_for. + /// + /// @param op user-supplied functor, see examples for interface details. + /// @param threaded optional toggle to disable threading, on by default. + /// @param grainSize optional parameter to specify the grainsize + /// for threading, one by default. + /// + /// @warning The functor object is deep-copied to create TBB tasks. + /// This allows the function to use non-thread-safe members + /// like a ValueAccessor. + /// + /// @par Example: + /// @code + /// // Functor to offset a tree's voxel values with values from another tree. + /// template + /// struct OffsetOp + /// { + /// typedef tree::ValueAccessor Accessor; + /// + /// OffsetOp(const TreeType& tree): mRhsTreeAcc(tree) {} + /// + /// template + /// void operator()(LeafNodeType &lhsLeaf, size_t) const + /// { + /// const LeafNodeType *rhsLeaf = mRhsTreeAcc.probeConstLeaf(lhsLeaf.origin()); + /// if (rhsLeaf) { + /// typename LeafNodeType::ValueOnIter iter = lhsLeaf.beginValueOn(); + /// for (; iter; ++iter) { + /// iter.setValue(iter.getValue() + rhsLeaf->getValue(iter.pos())); + /// } + /// } + /// } + /// Accessor mRhsTreeAcc; + /// }; + /// + /// // usage: + /// tree::LeafManager leafNodes(lhsTree); + /// leafNodes.foreach(OffsetOp(rhsTree)); + /// + /// // A functor that performs a min operation between different auxiliary buffers. + /// template + /// struct MinOp + /// { + /// typedef typename LeafManagerType::BufferType BufferType; + /// + /// MinOp(LeafManagerType& leafNodes): mLeafs(leafNodes) {} + /// + /// template + /// void operator()(LeafNodeType &leaf, size_t leafIndex) const + /// { + /// // get the first buffer + /// BufferType& buffer = mLeafs.getBuffer(leafIndex, 1); + /// + /// // min ... + /// } + /// LeafManagerType& mLeafs; + /// }; + /// @endcode + template + void foreach(const LeafOp& op, bool threaded = true, size_t grainSize=1) + { + LeafTransformer transform(op); + transform.run(this->leafRange(grainSize), threaded); + } + + /// @brief Threaded method that applies a user-supplied functor + /// to each leaf node in the LeafManager. Unlike foreach + /// (defined above) this method performs a reduction on + /// all the leaf nodes. + /// + /// @details The user-supplied functor needs to define the methods + /// required for tbb::parallel_reduce. + /// + /// @param op user-supplied functor, see examples for interface details. + /// @param threaded optional toggle to disable threading, on by default. + /// @param grainSize optional parameter to specify the grainsize + /// for threading, one by default. + /// + /// @warning The functor object is deep-copied to create TBB tasks. + /// This allows the function to use non-thread-safe members + /// like a ValueAccessor. + /// + /// @par Example: + /// @code + /// // Functor to count the number of negative (active) leaf values + /// struct CountOp + /// { + /// CountOp() : mCounter(0) {} + /// CountOp(const CountOp &other) : mCounter(other.mCounter) {} + /// CountOp(const CountOp &other, tbb::split) : mCounter(0) {} + /// template + /// void operator()(LeafNodeType &leaf, size_t) + /// { + /// typename LeafNodeType::ValueOnIter iter = leaf.beginValueOn(); + /// for (; iter; ++iter) if (*iter < 0.0f) ++mCounter; + /// } + /// void join(const CountOp &other) {mCounter += other.mCounter;} + /// size_t mCounter; + /// }; + /// + /// // usage: + /// tree::LeafManager leafNodes(tree); + /// MinValueOp min; + /// leafNodes.reduce(min); + /// std::cerr << "Number of negative active voxels = " << min.mCounter << std::endl; + /// + /// @endcode + template + void reduce(LeafOp& op, bool threaded = true, size_t grainSize=1) + { + LeafReducer transform(op); + transform.run(this->leafRange(grainSize), threaded); + } + + + /// @brief Insert pointers to nodes of the specified type into the array. + /// @details The type of node pointer is defined by the type + /// ArrayT::value_type. If the node type is a LeafNode the nodes + /// are inserted from this LeafManager, else of the corresponding tree. + template + void getNodes(ArrayT& array) + { + typedef typename ArrayT::value_type T; + BOOST_STATIC_ASSERT(boost::is_pointer::value); + typedef typename boost::mpl::if_::type>, + const LeafType, LeafType>::type LeafT; + + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (boost::is_same::value) { + array.resize(mLeafCount); + for (size_t i=0; i(mLeafs[i]); + } else { + mTree->getNodes(array); + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + + /// @brief Insert node pointers of the specified type into the array. + /// @details The type of node pointer is defined by the type + /// ArrayT::value_type. If the node type is a LeafNode the nodes + /// are inserted from this LeafManager, else of the corresponding tree. + template + void getNodes(ArrayT& array) const + { + typedef typename ArrayT::value_type T; + BOOST_STATIC_ASSERT(boost::is_pointer::value); + BOOST_STATIC_ASSERT(boost::is_const::type>::value); + + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (boost::is_same::value) { + array.resize(mLeafCount); + for (size_t i=0; i(mLeafs[i]); + } else { + mTree->getNodes(array); + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + + /// @brief Generate a linear array of pre-fix sums of offsets into the + /// active voxels in the leafs. So @a offsets[n]+m is the offset to the + /// mth active voxel in the nth leaf node (useful for + /// user-managed value buffers, e.g. in tools/LevelSetAdvect.h). + /// @return The total number of active values in the leaf nodes + /// @param offsets Array of pre-fix sums of offsets to active voxels + /// @param size On input the size of @a offsets, and on ouput + /// the new size of @a offsets. + /// @param grainSize Optional parameter to specify the grainsize + /// for threading, one by default. + /// @details If @a offsets is NULL or @a size is smaller than the + /// total number of active voxels (the return value) then @a offsets + /// is re-allocated and @a size equals the total number of active voxels. + size_t getPreFixSum(size_t*& offsets, size_t& size, size_t grainSize=1) const + { + if (offsets == NULL || size < mLeafCount) { + delete [] offsets; + offsets = new size_t[mLeafCount]; + size = mLeafCount; + } + size_t prefix = 0; + if ( grainSize > 0 ) { + PreFixSum tmp(this->leafRange( grainSize ), offsets, prefix); + } else {// serial + for (size_t i=0; ionVoxelCount(); + } + } + return prefix; + } + + //////////////////////////////////////////////////////////////////////////////////// + // 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(this), r); + else OPENVDB_THROW(ValueError, "task is undefined"); + } + + private: + + // This a simple wrapper for a c-style array so it mimics the api + // of a std container, e.g. std::vector or std::deque, and can be + // passed to Tree::getNodes(). + struct MyArray { + typedef LeafType* value_type;//required by Tree::getNodes + value_type* ptr; + MyArray(value_type* array) : ptr(array) {} + void push_back(value_type leaf) { *ptr++ = leaf; }//required by Tree::getNodes + }; + + void initLeafArray() + { + const size_t leafCount = mTree->leafCount(); + if (leafCount != mLeafCount) { + delete [] mLeafs; + mLeafs = (leafCount == 0) ? NULL : new LeafType*[leafCount]; + mLeafCount = leafCount; + } + MyArray a(mLeafs); + mTree->getNodes(a); + } + + 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::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; + } + } + + /// @brief Private member class that applies a user-defined + /// functor to perform parallel_for on all the leaf nodes. + template + struct LeafTransformer + { + LeafTransformer(const LeafOp &leafOp) : mLeafOp(leafOp) + { + } + void run(const LeafRange &range, bool threaded) const + { + threaded ? tbb::parallel_for(range, *this) : (*this)(range); + } + void operator()(const LeafRange &range) const + { + for (typename LeafRange::Iterator it = range.begin(); it; ++it) mLeafOp(*it, it.pos()); + } + const LeafOp mLeafOp; + };// LeafTransformer + + /// @brief Private member class that applies a user-defined + /// functor to perform parallel_reduce on all the leaf nodes. + template + struct LeafReducer + { + LeafReducer(LeafOp &leafOp) : mLeafOp(&leafOp), mOwnsOp(false) + { + } + LeafReducer(const LeafReducer &other, tbb::split) + : mLeafOp(new LeafOp(*(other.mLeafOp), tbb::split())), mOwnsOp(true) + { + } + ~LeafReducer() { if (mOwnsOp) delete mLeafOp; } + void run(const LeafRange& range, bool threaded) + { + threaded ? tbb::parallel_reduce(range, *this) : (*this)(range); + } + void operator()(const LeafRange& range) + { + LeafOp &op = *mLeafOp;//local registry + for (typename LeafRange::Iterator it = range.begin(); it; ++it) op(*it, it.pos()); + } + void join(const LeafReducer& other) { mLeafOp->join(*(other.mLeafOp)); } + LeafOp *mLeafOp; + const bool mOwnsOp; + };// LeafReducer + + // Helper class to compute a pre-fix sum of offsets to active voxels + struct PreFixSum + { + PreFixSum(const LeafRange& r, size_t* offsets, size_t& prefix) + : mOffsets(offsets) + { + tbb::parallel_for( r, *this); + for (size_t i=0, leafCount = r.size(); ionVoxelCount(); + } + } + size_t* mOffsets; + };// PreFixSum + + typedef typename boost::function 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 +struct LeafManagerImpl > +{ + typedef LeafManager 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-2016 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/nuparu/include/openvdb/include/openvdb/tree/LeafNode.h b/nuparu/include/openvdb/include/openvdb/tree/LeafNode.h new file mode 100644 index 00000000..33d0f80b --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tree/LeafNode.h @@ -0,0 +1,2219 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include // for std::swap +#include // for std::memcpy() +#include +#include +#include +#include +#include +#include +#include +#include +#include // for io::readData(), etc. +#include "Iterator.h" + + +class TestLeaf; +template class TestLeafIO; + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tree { + +template struct SameLeafConfig; // forward declaration + + +/// @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 +class LeafNode +{ +public: + typedef T BuildType; + typedef T ValueType; + typedef LeafNode LeafNodeType; + typedef boost::shared_ptr Ptr; + typedef util::NodeMask 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::Type is the type of a LeafNode having the same + /// dimensions as this node but a different value type, T. + template + struct ValueConverter { + typedef LeafNode Type; + }; + + /// @brief SameConfiguration::value is @c true if and only if + /// OtherNodeType is the type of a LeafNode with the same dimensions as this node. + template + struct SameConfiguration { + static const bool value = SameLeafConfig::value; + }; + +#ifndef OPENVDB_2_ABI_COMPATIBLE + struct FileInfo + { + FileInfo(): bufpos(0) , maskpos(0) {} + std::streamoff bufpos; + std::streamoff maskpos; + io::MappedFile::Ptr mapping; + boost::shared_ptr meta; + }; +#endif + + /// @brief Array of fixed size @f$2^{3 \times {\rm Log2Dim}}@f$ that stores + /// the voxel values of a LeafNode + class Buffer + { + public: +#ifdef OPENVDB_2_ABI_COMPATIBLE + /// Default constructor + Buffer(): mData(new ValueType[SIZE]) {} + /// Construct a buffer populated with the specified value. + explicit Buffer(const ValueType& val): mData(new ValueType[SIZE]) { this->fill(val); } + /// Copy constructor + Buffer(const Buffer& other): mData(new ValueType[SIZE]) { *this = other; } + /// Destructor + ~Buffer() { delete[] mData; } + + /// Return @c true if this buffer's values have not yet been read from disk. + bool isOutOfCore() const { return false; } + /// Return @c true if memory for this buffer has not yet been allocated. + bool empty() const { return (mData == NULL); } +#else + typedef ValueType WordType; + static const Index WORD_COUNT = SIZE; + /// Default constructor + Buffer(): mData(new ValueType[SIZE]), mOutOfCore(0) {} + /// Construct a buffer populated with the specified value. + explicit Buffer(const ValueType& val): mData(new ValueType[SIZE]), mOutOfCore(0) + { + this->fill(val); + } + /// Copy constructor + Buffer(const Buffer& other): mData(NULL), mOutOfCore(other.mOutOfCore) + { + if (other.isOutOfCore()) { + mFileInfo = new FileInfo(*other.mFileInfo); + } else { + this->allocate(); + ValueType* target = mData; + const ValueType* source = other.mData; + Index n = SIZE; + while (n--) *target++ = *source++; + } + } + /// Construct a buffer but don't allocate memory for the full array of values. + Buffer(PartialCreate, const ValueType&): mData(NULL), mOutOfCore(0) {} + /// Destructor + ~Buffer() + { + if (this->isOutOfCore()) { + this->detachFromFile(); + } else { + this->deallocate(); + } + } + + /// Return @c true if this buffer's values have not yet been read from disk. + bool isOutOfCore() const { return bool(mOutOfCore); } + /// Return @c true if memory for this buffer has not yet been allocated. + bool empty() const { return !mData || this->isOutOfCore(); } +#endif + /// Allocate memory for this buffer if it has not already been allocated. + bool allocate() { if (mData == NULL) mData = new ValueType[SIZE]; return !this->empty(); } + + /// Populate this buffer with a constant value. + void fill(const ValueType& val) + { + this->detachFromFile(); + if (mData != NULL) { + ValueType* target = mData; + Index n = SIZE; + while (n--) *target++ = val; + } + } + + /// Return a const reference to the i'th element of this buffer. + const ValueType& getValue(Index i) const { return this->at(i); } + /// Return a const reference to the i'th element of this buffer. + const ValueType& operator[](Index i) const { return this->at(i); } + /// Set the i'th value of this buffer to the specified value. + void setValue(Index i, const ValueType& val) + { + assert(i < SIZE); +#ifdef OPENVDB_2_ABI_COMPATIBLE + mData[i] = val; +#else + this->loadValues(); + if (mData) mData[i] = val; +#endif + } + + /// Copy the other buffer's values into this buffer. + Buffer& operator=(const Buffer& other) + { + if (&other != this) { +#ifndef OPENVDB_2_ABI_COMPATIBLE + if (this->isOutOfCore()) { + this->detachFromFile(); + } else { + if (other.isOutOfCore()) this->deallocate(); + } + if (other.isOutOfCore()) { + mOutOfCore = other.mOutOfCore; + mFileInfo = new FileInfo(*other.mFileInfo); + } else { +#endif + this->allocate(); + ValueType* target = mData; + const ValueType* source = other.mData; + Index n = SIZE; + while (n--) *target++ = *source++; +#ifndef OPENVDB_2_ABI_COMPATIBLE + } +#endif + } + return *this; + } + + /// @brief Return @c true if the contents of the other buffer + /// exactly equal the contents of this buffer. + bool operator==(const Buffer& other) const + { + this->loadValues(); + other.loadValues(); + const ValueType *target = mData, *source = other.mData; + if (!target && !source) return true; + if (!target || !source) return false; + Index n = SIZE; + while (n && math::isExactlyEqual(*target++, *source++)) --n; + return n == 0; + } + /// @brief Return @c true if the contents of the other buffer + /// are not exactly equal to the contents of this buffer. + bool operator!=(const Buffer& other) const { return !(other == *this); } + + /// Exchange this buffer's values with the other buffer's values. + void swap(Buffer& other) + { + std::swap(mData, other.mData); +#ifndef OPENVDB_2_ABI_COMPATIBLE + std::swap(mOutOfCore, other.mOutOfCore); +#endif + } + + /// Return the memory footprint of this buffer in bytes. + Index memUsage() const + { + size_t n = sizeof(*this); +#ifdef OPENVDB_2_ABI_COMPATIBLE + if (mData) n += SIZE * sizeof(ValueType); +#else + if (this->isOutOfCore()) n += sizeof(FileInfo); + else if (mData) n += SIZE * sizeof(ValueType); +#endif + return static_cast(n); + } + /// Return the number of values contained in this buffer. + static Index size() { return SIZE; } + + /// @brief Return a const pointer to the array of voxel values. + /// @details This method guarantees that the buffer is allocated and loaded. + /// @warning This method should only be used by experts seeking low-level optimizations. + const ValueType* data() const + { +#ifndef OPENVDB_2_ABI_COMPATIBLE + this->loadValues(); + if (mData == NULL) { + Buffer* self = const_cast(this); + // This lock will be contended at most once. + tbb::spin_mutex::scoped_lock lock(self->mMutex); + if (mData == NULL) self->mData = new ValueType[SIZE]; + } +#endif + return mData; + } + + /// @brief Return a pointer to the array of voxel values. + /// @details This method guarantees that the buffer is allocated and loaded. + /// @warning This method should only be used by experts seeking low-level optimizations. + ValueType* data() + { +#ifndef OPENVDB_2_ABI_COMPATIBLE + this->loadValues(); + if (mData == NULL) { + // This lock will be contended at most once. + tbb::spin_mutex::scoped_lock lock(mMutex); + if (mData == NULL) mData = new ValueType[SIZE]; + } +#endif + return mData; + } + + private: + /// If this buffer is empty, return zero, otherwise return the value at index @ i. + const ValueType& at(Index i) const + { + assert(i < SIZE); +#ifdef OPENVDB_2_ABI_COMPATIBLE + return mData[i]; +#else + this->loadValues(); + // We can't use the ternary operator here, otherwise Visual C++ returns + // a reference to a temporary. + if (mData) return mData[i]; else return sZero; +#endif + } + + /// @brief Return a non-const reference to the value at index @a i. + /// @details This method is private since it makes assumptions about the + /// buffer's memory layout. Buffers associated with custom leaf node types + /// (e.g., a bool buffer implemented as a bitmask) might not be able to + /// return non-const references to their values. + ValueType& operator[](Index i) { return const_cast(this->at(i)); } + + bool deallocate() + { + if (mData != NULL && !this->isOutOfCore()) { + delete[] mData; + mData = NULL; + return true; + } + return false; + } + +#ifdef OPENVDB_2_ABI_COMPATIBLE + void setOutOfCore(bool) {} + void loadValues() const {} + void doLoad() const {} + bool detachFromFile() { return false; } +#else + inline void setOutOfCore(bool b) { mOutOfCore = b; } + // To facilitate inlining in the common case in which the buffer is in-core, + // the loading logic is split into a separate function, doLoad(). + inline void loadValues() const { if (this->isOutOfCore()) this->doLoad(); } + inline void doLoad() const; + inline bool detachFromFile() + { + if (this->isOutOfCore()) { + delete mFileInfo; + mFileInfo = NULL; + this->setOutOfCore(false); + return true; + } + return false; + } +#endif + + friend class ::TestLeaf; + // Allow the parent LeafNode to access this buffer's data pointer. + friend class LeafNode; + +#ifdef OPENVDB_2_ABI_COMPATIBLE + ValueType* mData; +#else + union { + ValueType* mData; + FileInfo* mFileInfo; + }; + Index32 mOutOfCore; // currently interpreted as bool; extra bits reserved for future use + tbb::spin_mutex mMutex; // 1 byte + //int8_t mReserved[3]; // padding for alignment + + static const ValueType sZero; +#endif + }; // 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(), + bool active = false); + + +#ifndef OPENVDB_2_ABI_COMPATIBLE + /// @brief "Partial creation" constructor used during file input + /// @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 + /// @details This constructor does not allocate memory for voxel values. + LeafNode(PartialCreate, + const Coord& coords, + const ValueType& value = zeroVal(), + bool active = false); +#endif + + /// Deep copy constructor + LeafNode(const LeafNode&); + + /// Value conversion copy constructor + template + explicit LeafNode(const LeafNode& other); + + /// Topology copy constructor + template + LeafNode(const LeafNode& other, + const ValueType& offValue, const ValueType& onValue, TopologyCopy); + + /// Topology copy constructor + template + LeafNode(const LeafNode& 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& 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(); } + static Index64 onTileCount() { return 0; } + static Index64 offTileCount() { return 0; } + /// 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(); } + +#ifndef OPENVDB_2_ABI_COMPATIBLE + /// Return @c true if memory for this node's buffer has been allocated. + bool isAllocated() const { return !mBuffer.isOutOfCore() && !mBuffer.empty(); } + /// Allocate memory for this node's buffer if it has not already been allocated. + bool allocate() { return mBuffer.allocate(); } +#endif + + /// 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. + /// If visitVoxels is false this LeafNode will be approximated as dense, i.e. with all + /// voxels active. Else the individual active voxels are visited to produce a tight bbox. + void evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels = true) 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; } + //@{ + /// Return the grid index coordinates of this node's local origin. + const Coord& origin() 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 global or local coordinates. + static Index coordToOffset(const Coord& xyz); + /// @brief Return the local coordinates for a linear table offset, + /// where offset 0 has coordinates (0, 0, 0). + static Coord offsetToLocalCoord(Index n); + /// Return the global coordinates for a linear table offset. + Coord offsetToGlobalCoord(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 + bool hasSameTopology(const LeafNode* 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 + 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, NodeT, ValueT> + { + typedef SparseIteratorBase 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); + } + + // Note: modifyItem() can't be called on const iterators. + template + void modifyItem(Index n, const ModifyOp& op) const { this->parent().modifyValue(n, op); } + // Note: modifyValue() can't be called on const iterators. + template + void modifyValue(const ModifyOp& op) const { this->parent().modifyValue(this->pos(), op); } + }; + + /// Leaf nodes have no children, so their child iterators have no get/set accessors. + template + struct ChildIter: + public SparseIteratorBase, NodeT, ValueType> + { + ChildIter() {} + ChildIter(const MaskIterT& iter, NodeT* parent): SparseIteratorBase< + MaskIterT, ChildIter, NodeT, ValueType>(iter, parent) {} + }; + + template + struct DenseIter: public DenseIteratorBase< + MaskDenseIterator, DenseIter, NodeT, /*ChildT=*/void, ValueT> + { + typedef DenseIteratorBase 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 ValueOnIter; + typedef ValueIter ValueOnCIter; + typedef ValueIter ValueOffIter; + typedef ValueIter ValueOffCIter; + typedef ValueIter ValueAllIter; + typedef ValueIter ValueAllCIter; + typedef ChildIter ChildOnIter; + typedef ChildIter ChildOnCIter; + typedef ChildIter ChildOffIter; + typedef ChildIter ChildOffCIter; + typedef DenseIter ChildAllIter; + typedef DenseIter 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 Read buffers that intersect the given bounding box. + /// @param is the stream from which to read + /// @param bbox an index-space bounding box + /// @param fromHalf if true, floating-point input values are assumed to be 16-bit + void readBuffers(std::istream& is, const CoordBBox& bbox, 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 of the voxel at the given coordinates but don't change its value. + void setActiveState(const Coord& xyz, bool on); + /// Set the active state of the voxel at the given offset but don't change its value. + void setActiveState(Index offset, bool on) { assert(offsetsetValueOn(LeafNode::coordToOffset(xyz), val); + } + /// Set the value of the voxel at the given coordinates and mark the voxel as active. + void setValue(const Coord& xyz, const ValueType& val) { this->setValueOn(xyz, val); } + /// Set the value of the voxel at the given offset and mark the voxel as active. + void setValueOn(Index offset, const ValueType& val) { + mBuffer.setValue(offset, val); + mValueMask.setOn(offset); + } + + /// @brief Apply a functor to the value of the voxel at the given offset + /// and mark the voxel as active. + template + void modifyValue(Index offset, const ModifyOp& op) + { + ValueType val = mBuffer[offset]; + op(val); + mBuffer.setValue(offset, val); + mValueMask.setOn(offset); + } + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + template + void modifyValue(const Coord& xyz, const ModifyOp& op) + { + this->modifyValue(this->coordToOffset(xyz), op); + } + + /// Apply a functor to the voxel at the given coordinates. + template + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) + { + const Index offset = this->coordToOffset(xyz); + bool state = mValueMask.isOn(offset); + ValueType val = mBuffer[offset]; + op(val, state); + mBuffer.setValue(offset, val); + mValueMask.set(offset, state); + } + + /// 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::coordToOffset(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; } + + /// Set all voxels that lie outside the given axis-aligned box to the background. + void clip(const CoordBBox&, const ValueType& background); + + /// Set all voxels within an axis-aligned box to the specified value and active state. + void fill(const CoordBBox& bbox, const ValueType&, bool active = true); + + /// Set all voxels to the specified value but don't change their active states. + void fill(const ValueType& value); + /// Set all voxels to the specified value and active 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 + 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 + 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. + template + 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. + template + 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. + template + 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. + template + void setValueOnlyAndCache(const Coord& xyz, const ValueType& val, AccessorT&) + { + this->setValueOnly(xyz, val); + } + + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// @note Used internally by ValueAccessor. + template + void modifyValueAndCache(const Coord& xyz, const ModifyOp& op, AccessorT&) + { + this->modifyValue(xyz, op); + } + + /// Apply a functor to the voxel at the given coordinates. + /// @note Used internally by ValueAccessor. + template + void modifyValueAndActiveStateAndCache(const Coord& xyz, const ModifyOp& op, AccessorT&) + { + this->modifyValueAndActiveState(xyz, op); + } + + /// @brief Change the value of the voxel at the given coordinates and mark it as inactive. + /// @note Used internally by ValueAccessor. + template + 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. + template + 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. + template + 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. + template + const ValueType& getValue(const Coord& xyz, bool& state, int& level, AccessorT&) const + { + const Index offset = this->coordToOffset(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 + 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&. + 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); + + void negate(); + + /// @brief No-op + /// @details This function exists only to enable template instantiation. + void voxelizeActiveTiles(bool = true) {} + + template void merge(const LeafNode&); + template void merge(const ValueType& tileValue, bool tileActive); + template + void merge(const LeafNode& other, const ValueType& /*bg*/, const ValueType& /*otherBG*/); + + /// @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 + void topologyUnion(const LeafNode& 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 + void topologyIntersection(const LeafNode& 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 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, because it can deactivate all of this node's voxels, + /// consider subsequently calling prune. + template + void topologyDifference(const LeafNode& other, const ValueType&); + + template + void combine(const LeafNode& other, CombineOp& op); + template + void combine(const ValueType& value, bool valueIsActive, CombineOp& op); + + template + void combine2(const LeafNode& other, const OtherType&, bool valueIsActive, CombineOp&); + template + void combine2(const ValueType&, const OtherNodeT& other, bool valueIsActive, CombineOp&); + template + void combine2(const LeafNode& b0, const OtherNodeT& 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 void visitActiveBBox(BBoxOp&) const; + + template void visit(VisitorOp&); + template void visit(VisitorOp&) const; + + template + void visit2Node(OtherLeafNodeType& other, VisitorOp&); + template + void visit2Node(OtherLeafNodeType& other, VisitorOp&) const; + template + void visit2(IterT& otherIter, VisitorOp&, bool otherIsLHS = false); + template + void visit2(IterT& otherIter, VisitorOp&, bool otherIsLHS = false) const; + + //@{ + /// This function exists only to enable template instantiation. + void prune(const ValueType& /*tolerance*/ = zeroVal()) {} + void addLeaf(LeafNode*) {} + template + void addLeafAndCache(LeafNode*, AccessorT&) {} + template + NodeT* stealNode(const Coord&, const ValueType&, bool) { return NULL; } + template + NodeT* probeNode(const Coord&) { return NULL; } + template + const NodeT* probeConstNode(const Coord&) const { return NULL; } + template void getNodes(ArrayT&) const {} + template void stealNodes(ArrayT&, const ValueType&, bool) {} + //@} + + void addTile(Index level, const Coord&, const ValueType&, bool); + void addTile(Index offset, const ValueType&, bool); + template + void addTileAndCache(Index, const Coord&, const ValueType&, bool, AccessorT&); + + //@{ + /// @brief Return a pointer to this node. + LeafNode* touchLeaf(const Coord&) { return this; } + template + LeafNode* touchLeafAndCache(const Coord&, AccessorT&) { return this; } + template + NodeT* probeNodeAndCache(const Coord&, AccessorT&) + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (!(boost::is_same::value)) return NULL; + return reinterpret_cast(this); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + LeafNode* probeLeaf(const Coord&) { return this; } + template + LeafNode* probeLeafAndCache(const Coord&, AccessorT&) { return this; } + //@} + //@{ + /// @brief Return a @const pointer to this node. + const LeafNode* probeConstLeaf(const Coord&) const { return this; } + template + const LeafNode* probeConstLeafAndCache(const Coord&, AccessorT&) const { return this; } + template + const LeafNode* probeLeafAndCache(const Coord&, AccessorT&) const { return this; } + const LeafNode* probeLeaf(const Coord&) const { return this; } + template + const NodeT* probeConstNodeAndCache(const Coord&, AccessorT&) const + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (!(boost::is_same::value)) return NULL; + return reinterpret_cast(this); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + //@} + + /// Return @c true if all of this node's values have the same active state + /// and are in the range this->getFirstValue() +/- @a tolerance. + /// + /// + /// @param constValue Is updated with the first value of this leaf node. + /// @param state Is updated with the state of all values IF method + /// returns @c true. Else the value is undefined! + /// @param tolerance The tolerance used to determine if values are + /// approximatly equal to the for value. + bool isConstant(ValueType& constValue, bool& state, + const ValueType& tolerance = zeroVal()) const; + + /// Return @c true if all of this node's values have the same active state + /// and are in the range (@a maxValue + @a minValue)/2 +/- @a tolerance. + /// + /// @param minValue Is updated with the minimum of all values IF method + /// returns @c true. Else the value is undefined! + /// @param maxValue Is updated with the maximum of all values IF method + /// returns @c true. Else the value is undefined! + /// @param state Is updated with the state of all values IF method + /// returns @c true. Else the value is undefined! + /// @param tolerance The tolerance used to determine if values are + /// approximatly constant. + bool isConstant(ValueType& minValue, ValueType& maxValue, + bool& state, const ValueType& tolerance = zeroVal()) const; + + /// Return @c true if all of this node's values are inactive. + bool isInactive() const { return mValueMask.isOff(); } + +protected: + friend class ::TestLeaf; + template friend class ::TestLeafIO; + + // During topology-only construction, access is needed + // to protected/private members of other template instances. + template friend class LeafNode; + + friend struct ValueIter; + friend struct ValueIter; + friend struct ValueIter; + friend struct ValueIter; + friend struct ValueIter; + friend struct ValueIter; + + // Allow iterators to call mask accessor methods (see below). + /// @todo Make mask accessors public? + friend class IteratorBase; + friend class IteratorBase; + friend class IteratorBase; + + // 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; } + const NodeMaskType& valueMask() const { 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 + static inline void doVisit(NodeT&, VisitorOp&); + + template + static inline void doVisit2Node(NodeT& self, OtherNodeT& other, VisitorOp&); + + template + static inline void doVisit2(NodeT& self, OtherChildAllIterT&, VisitorOp&, bool otherIsLHS); + +private: + /// 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; +}; // end of LeafNode class + + +#ifndef OPENVDB_2_ABI_COMPATIBLE +template +const T LeafNode::Buffer::sZero = zeroVal(); +#endif + + +//////////////////////////////////////// + + +//@{ +/// Helper metafunction used to implement LeafNode::SameConfiguration +/// (which, as an inner class, can't be independently specialized) +template +struct SameLeafConfig { static const bool value = false; }; + +template +struct SameLeafConfig > { static const bool value = true; }; +//@} + + +//////////////////////////////////////// + + +template +inline +LeafNode::LeafNode(): + mValueMask(),//default is off! + mOrigin(0, 0, 0) +{ +} + + +template +inline +LeafNode::LeafNode(const Coord& xyz, const ValueType& val, bool active): + mBuffer(val), + mValueMask(active), + mOrigin(xyz & (~(DIM - 1))) +{ +} + + +#ifndef OPENVDB_2_ABI_COMPATIBLE +template +inline +LeafNode::LeafNode(PartialCreate, const Coord& xyz, const ValueType& val, bool active): + mBuffer(PartialCreate(), val), + mValueMask(active), + mOrigin(xyz & (~(DIM - 1))) +{ +} +#endif + + +template +inline +LeafNode::LeafNode(const LeafNode &other): + mBuffer(other.mBuffer), + mValueMask(other.valueMask()), + mOrigin(other.mOrigin) +{ +} + + +// Copy-construct from a leaf node with the same configuration but a different ValueType. +template +template +inline +LeafNode::LeafNode(const LeafNode& other): + mValueMask(other.valueMask()), + mOrigin(other.mOrigin) +{ + struct Local { + /// @todo Consider using a value conversion functor passed as an argument instead. + static inline ValueType convertValue(const OtherValueType& val) { return ValueType(val); } + }; + + for (Index i = 0; i < SIZE; ++i) { + mBuffer[i] = Local::convertValue(other.mBuffer[i]); + } +} + + +template +template +inline +LeafNode::LeafNode(const LeafNode& other, + const ValueType& background, TopologyCopy): + mBuffer(background), + mValueMask(other.valueMask()), + mOrigin(other.mOrigin) +{ +} + + +template +template +inline +LeafNode::LeafNode(const LeafNode& other, + const ValueType& offValue, const ValueType& onValue, TopologyCopy): + mValueMask(other.valueMask()), + mOrigin(other.mOrigin) +{ + for (Index i = 0; i < SIZE; ++i) { + mBuffer[i] = (mValueMask.isOn(i) ? onValue : offValue); + } +} + + +template +inline +LeafNode::~LeafNode() +{ +} + + +template +inline std::string +LeafNode::str() const +{ + std::ostringstream ostr; + ostr << "LeafNode @" << mOrigin << ": " << mBuffer; + return ostr.str(); +} + + +//////////////////////////////////////// + + +template +inline Index +LeafNode::coordToOffset(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 +inline Coord +LeafNode::offsetToLocalCoord(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< +inline Coord +LeafNode::offsetToGlobalCoord(Index n) const +{ + return (this->offsetToLocalCoord(n) + this->origin()); +} + + +//////////////////////////////////////// + + +template +inline const ValueT& +LeafNode::getValue(const Coord& xyz) const +{ + return this->getValue(LeafNode::coordToOffset(xyz)); +} + +template +inline const ValueT& +LeafNode::getValue(Index offset) const +{ + assert(offset < SIZE); + return mBuffer[offset]; +} + + +template +inline bool +LeafNode::probeValue(const Coord& xyz, ValueType& val) const +{ + return this->probeValue(LeafNode::coordToOffset(xyz), val); +} + +template +inline bool +LeafNode::probeValue(Index offset, ValueType& val) const +{ + assert(offset < SIZE); + val = mBuffer[offset]; + return mValueMask.isOn(offset); +} + + +template +inline void +LeafNode::setValueOff(const Coord& xyz, const ValueType& val) +{ + this->setValueOff(LeafNode::coordToOffset(xyz), val); +} + +template +inline void +LeafNode::setValueOff(Index offset, const ValueType& val) +{ + assert(offset < SIZE); + mBuffer.setValue(offset, val); + mValueMask.setOff(offset); +} + + +template +inline void +LeafNode::setActiveState(const Coord& xyz, bool on) +{ + mValueMask.set(this->coordToOffset(xyz), on); +} + + +template +inline void +LeafNode::setValueOnly(const Coord& xyz, const ValueType& val) +{ + this->setValueOnly(LeafNode::coordToOffset(xyz), val); +} + +template +inline void +LeafNode::setValueOnly(Index offset, const ValueType& val) +{ + assert(offset +inline void +LeafNode::clip(const CoordBBox& clipBBox, const T& background) +{ + CoordBBox nodeBBox = this->getNodeBoundingBox(); + if (!clipBBox.hasOverlap(nodeBBox)) { + // This node lies completely outside the clipping region. Fill it with the background. + this->fill(background, /*active=*/false); + } else if (clipBBox.isInside(nodeBBox)) { + // This node lies completely inside the clipping region. Leave it intact. + return; + } + + // This node isn't completely contained inside the clipping region. + // Set any voxels that lie outside the region to the background value. + + // Construct a boolean mask that is on inside the clipping region and off outside it. + NodeMaskType mask; + nodeBBox.intersect(clipBBox); + Coord xyz; + int &x = xyz.x(), &y = xyz.y(), &z = xyz.z(); + for (x = nodeBBox.min().x(); x <= nodeBBox.max().x(); ++x) { + for (y = nodeBBox.min().y(); y <= nodeBBox.max().y(); ++y) { + for (z = nodeBBox.min().z(); z <= nodeBBox.max().z(); ++z) { + mask.setOn(static_cast(this->coordToOffset(xyz))); + } + } + } + + // Set voxels that lie in the inactive region of the mask (i.e., outside + // the clipping region) to the background value. + for (MaskOffIterator maskIter = mask.beginOff(); maskIter; ++maskIter) { + this->setValueOff(maskIter.pos(), background); + } +} + + +//////////////////////////////////////// + + +template +inline void +LeafNode::fill(const CoordBBox& bbox, const ValueType& value, bool active) +{ +#ifndef OPENVDB_2_ABI_COMPATIBLE + if (!this->allocate()) return; +#endif + + 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 +inline void +LeafNode::fill(const ValueType& value) +{ + mBuffer.fill(value); +} + +template +inline void +LeafNode::fill(const ValueType& value, bool active) +{ + mBuffer.fill(value); + mValueMask.set(active); +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::copyToDense(const CoordBBox& bbox, DenseT& dense) const +{ +#ifndef OPENVDB_2_ABI_COMPATIBLE + if (!this->isAllocated()) return; +#endif + + typedef typename DenseT::ValueType DenseValueType; + + const size_t xStride = dense.xStride(), yStride = dense.yStride(), zStride = dense.zStride(); + const Coord& min = dense.bbox().min(); + DenseValueType* t0 = dense.data() + zStride * (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) { + DenseValueType* 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) { + DenseValueType* 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 += zStride) { + *t2 = DenseValueType(*s2++); + } + } + } +} + + +template +template +inline void +LeafNode::copyFromDense(const CoordBBox& bbox, const DenseT& dense, + const ValueType& background, const ValueType& tolerance) +{ +#ifndef OPENVDB_2_ABI_COMPATIBLE + if (!this->allocate()) return; +#endif + + typedef typename DenseT::ValueType DenseValueType; + + const size_t xStride = dense.xStride(), yStride = dense.yStride(), zStride = dense.zStride(); + const Coord& min = dense.bbox().min(); + + const DenseValueType* s0 = dense.data() + zStride * (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 DenseValueType* 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 DenseValueType* 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 += zStride) { + if (math::isApproxEqual(background, ValueType(*s2), tolerance)) { + mValueMask.setOff(n2); + mBuffer[n2] = background; + } else { + mValueMask.setOn(n2); + mBuffer[n2] = ValueType(*s2); + } + } + } + } +} + + +//////////////////////////////////////// + + +template +inline void +LeafNode::readTopology(std::istream& is, bool /*fromHalf*/) +{ + mValueMask.load(is); +} + + +template +inline void +LeafNode::writeTopology(std::ostream& os, bool /*toHalf*/) const +{ + mValueMask.save(os); +} + + +//////////////////////////////////////// + + +#ifndef OPENVDB_2_ABI_COMPATIBLE +template +inline void +LeafNode::Buffer::doLoad() const +{ + if (!this->isOutOfCore()) return; + + Buffer* self = const_cast(this); + + // This lock will be contended at most once, after which this buffer + // will no longer be out-of-core. + tbb::spin_mutex::scoped_lock lock(self->mMutex); + if (!this->isOutOfCore()) return; + + boost::scoped_ptr info(self->mFileInfo); + assert(info.get() != NULL); + assert(info->mapping.get() != NULL); + assert(info->meta.get() != NULL); + + /// @todo For now, we have to clear the mData pointer in order for allocate() to take effect. + self->mData = NULL; + self->allocate(); + + boost::shared_ptr buf = info->mapping->createBuffer(); + std::istream is(buf.get()); + + io::setStreamMetadataPtr(is, info->meta, /*transfer=*/true); + + NodeMaskType mask; + is.seekg(info->maskpos); + mask.load(is); + + is.seekg(info->bufpos); + io::readCompressedValues(is, self->mData, SIZE, mask, io::getHalfFloat(is)); + + self->setOutOfCore(false); +} +#endif + + +//////////////////////////////////////// + + +template +inline void +LeafNode::readBuffers(std::istream& is, bool fromHalf) +{ + this->readBuffers(is, CoordBBox::inf(), fromHalf); +} + + +template +inline void +LeafNode::readBuffers(std::istream& is, const CoordBBox& clipBBox, bool fromHalf) +{ +#ifndef OPENVDB_2_ABI_COMPATIBLE + std::streamoff maskpos = is.tellg(); +#endif + + // 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(&mOrigin), sizeof(Coord::ValueType) * 3); + + // Read in the number of buffers, which should now always be one. + is.read(reinterpret_cast(&numBuffers), sizeof(int8_t)); + } + + CoordBBox nodeBBox = this->getNodeBoundingBox(); + if (!clipBBox.hasOverlap(nodeBBox)) { + // This node lies completely outside the clipping region. + // Read and discard its voxel values. + Buffer temp; + io::readCompressedValues(is, temp.mData, SIZE, mValueMask, fromHalf); + mValueMask.setOff(); + mBuffer.setOutOfCore(false); + } else { +#ifndef OPENVDB_2_ABI_COMPATIBLE + // If this node lies completely inside the clipping region and it is being read + // from a memory-mapped file, delay loading of its buffer until the buffer + // is actually accessed. (If this node requires clipping, its buffer + // must be accessed and therefore must be loaded.) + io::MappedFile::Ptr mappedFile = io::getMappedFilePtr(is); + const bool delayLoad = ((mappedFile.get() != NULL) && clipBBox.isInside(nodeBBox)); + + if (delayLoad) { + mBuffer.setOutOfCore(true); + mBuffer.mFileInfo = new FileInfo; + mBuffer.mFileInfo->bufpos = is.tellg(); + mBuffer.mFileInfo->mapping = mappedFile; + // Save the offset to the value mask, because the in-memory copy + // might change before the value buffer gets read. + mBuffer.mFileInfo->maskpos = maskpos; + + mBuffer.mFileInfo->meta = io::getStreamMetadataPtr(is); + + // Read and discard voxel values. + Buffer temp; + io::readCompressedValues(is, temp.mData, SIZE, mValueMask, fromHalf); + } else { +#endif + mBuffer.allocate(); + io::readCompressedValues(is, mBuffer.mData, SIZE, mValueMask, fromHalf); + mBuffer.setOutOfCore(false); + + // Get this tree's background value. + T background = zeroVal(); + if (const void* bgPtr = io::getGridBackgroundValuePtr(is)) { + background = *static_cast(bgPtr); + } + this->clip(clipBBox, background); +#ifndef OPENVDB_2_ABI_COMPATIBLE + } +#endif + } + + 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::isReal, T>::read(is, temp.mData, SIZE, zipped); + } else { + io::readData(is, temp.mData, SIZE, zipped); + } + } + } +} + + +template +inline void +LeafNode::writeBuffers(std::ostream& os, bool toHalf) const +{ + // Write out the value mask. + mValueMask.save(os); + + mBuffer.loadValues(); + + io::writeCompressedValues(os, mBuffer.mData, SIZE, + mValueMask, /*childMask=*/NodeMaskType(), toHalf); +} + + +//////////////////////////////////////// + + +template +inline bool +LeafNode::operator==(const LeafNode& other) const +{ + return mOrigin == other.mOrigin && + mValueMask == other.valueMask() && + mBuffer == other.mBuffer; +} + + +template +inline Index64 +LeafNode::memUsage() const +{ + // Use sizeof(*this) to capture alignment-related padding + // (but note that sizeof(*this) includes sizeof(mBuffer)). + return sizeof(*this) + mBuffer.memUsage() - sizeof(mBuffer); +} + + +template +inline void +LeafNode::evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels) const +{ + CoordBBox this_bbox = this->getNodeBoundingBox(); + if (bbox.isInside(this_bbox)) return;//this LeafNode is already enclosed in the bbox + if (ValueOnCIter iter = this->cbeginValueOn()) {//any active values? + if (visitVoxels) {//use voxel granularity? + this_bbox.reset(); + for(; iter; ++iter) this_bbox.expand(this->offsetToLocalCoord(iter.pos())); + this_bbox.translate(this->origin()); + } + bbox.expand(this_bbox); + } +} + + +template +template +inline bool +LeafNode::hasSameTopology(const LeafNode* other) const +{ + assert(other); + return (Log2Dim == OtherLog2Dim && mValueMask == other->getValueMask()); +} + + +template +inline bool +LeafNode::isConstant(ValueType& value, bool& state, + const ValueType& tolerance) const +{ + state = mValueMask.isOn(); + if (!(state || mValueMask.isOff())) return false;// Are values neither active nor inactive? + + value = mBuffer[0]; + for (Index i = 1; i < SIZE; ++i) { + if ( !math::isApproxEqual(mBuffer[i], value, tolerance) ) return false; + } + return true; +} + +template +inline bool +LeafNode::isConstant(ValueType& minValue, ValueType& maxValue, + bool& state, const ValueType& tolerance) const +{ + state = mValueMask.isOn(); + if (!(state || mValueMask.isOff())) return false;// Are values neither active nor inactive? + + const T range = 2 * tolerance; + minValue = maxValue = mBuffer[0]; + for (Index i = 1; i < SIZE; ++i) {// early termination + const T& v = mBuffer[i]; + if (v < minValue) { + if ((maxValue - v) > range) return false; + minValue = v; + } else if (v > maxValue) { + if ((v - minValue) > range) return false; + maxValue = v; + } + } + return true; +} + +//////////////////////////////////////// + + +template +inline void +LeafNode::addTile(Index /*level*/, const Coord& xyz, const ValueType& val, bool active) +{ + this->addTile(this->coordToOffset(xyz), val, active); +} + +template +inline void +LeafNode::addTile(Index offset, const ValueType& val, bool active) +{ + assert(offset < SIZE); + setValueOnly(offset, val); + setActiveState(offset, active); +} + +template +template +inline void +LeafNode::addTileAndCache(Index level, const Coord& xyz, + const ValueType& val, bool active, AccessorT&) +{ + this->addTile(level, xyz, val, active); +} + + +//////////////////////////////////////// + + +template +inline void +LeafNode::resetBackground(const ValueType& oldBackground, + const ValueType& newBackground) +{ +#ifndef OPENVDB_2_ABI_COMPATIBLE + if (!this->allocate()) return; +#endif + + 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, math::negative(oldBackground))) { + inactiveValue = math::negative(newBackground); + } + } +} + + +template +template +inline void +LeafNode::merge(const LeafNode& other) +{ +#ifndef OPENVDB_2_ABI_COMPATIBLE + if (!this->allocate()) return; +#endif + + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (Policy == MERGE_NODES) return; + typename NodeMaskType::OnIterator iter = other.valueMask().beginOn(); + for (; iter; ++iter) { + const Index n = iter.pos(); + if (mValueMask.isOff(n)) { + mBuffer[n] = other.mBuffer[n]; + mValueMask.setOn(n); + } + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + +template +template +inline void +LeafNode::merge(const LeafNode& other, + const ValueType& /*bg*/, const ValueType& /*otherBG*/) +{ + this->template merge(other); +} + +template +template +inline void +LeafNode::merge(const ValueType& tileValue, bool tileActive) +{ +#ifndef OPENVDB_2_ABI_COMPATIBLE + if (!this->allocate()) return; +#endif + + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (Policy != MERGE_ACTIVE_STATES_AND_NODES) return; + if (!tileActive) return; + // Replace all inactive values with the active tile value. + for (typename NodeMaskType::OffIterator iter = mValueMask.beginOff(); iter; ++iter) { + const Index n = iter.pos(); + mBuffer[n] = tileValue; + mValueMask.setOn(n); + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +template +template +inline void +LeafNode::topologyUnion(const LeafNode& other) +{ + mValueMask |= other.valueMask(); +} + +template +template +inline void +LeafNode::topologyIntersection(const LeafNode& other, + const ValueType&) +{ + mValueMask &= other.valueMask(); +} + +template +template +inline void +LeafNode::topologyDifference(const LeafNode& other, + const ValueType&) +{ + mValueMask &= !other.valueMask(); +} + +template +inline void +LeafNode::negate() +{ +#ifndef OPENVDB_2_ABI_COMPATIBLE + if (!this->allocate()) return; +#endif + for (Index i = 0; i < SIZE; ++i) { + mBuffer[i] = -mBuffer[i]; + } +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::combine(const LeafNode& other, CombineOp& op) +{ +#ifndef OPENVDB_2_ABI_COMPATIBLE + if (!this->allocate()) return; +#endif + CombineArgs args; + for (Index i = 0; i < SIZE; ++i) { + op(args.setARef(mBuffer[i]) + .setAIsActive(mValueMask.isOn(i)) + .setBRef(other.mBuffer[i]) + .setBIsActive(other.valueMask().isOn(i)) + .setResultRef(mBuffer[i])); + mValueMask.set(i, args.resultIsActive()); + } +} + + +template +template +inline void +LeafNode::combine(const ValueType& value, bool valueIsActive, CombineOp& op) +{ +#ifndef OPENVDB_2_ABI_COMPATIBLE + if (!this->allocate()) return; +#endif + CombineArgs 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 +template +inline void +LeafNode::combine2(const LeafNode& other, const OtherType& value, + bool valueIsActive, CombineOp& op) +{ +#ifndef OPENVDB_2_ABI_COMPATIBLE + if (!this->allocate()) return; +#endif + CombineArgs args; + args.setBRef(value).setBIsActive(valueIsActive); + for (Index i = 0; i < SIZE; ++i) { + op(args.setARef(other.mBuffer[i]) + .setAIsActive(other.valueMask().isOn(i)) + .setResultRef(mBuffer[i])); + mValueMask.set(i, args.resultIsActive()); + } +} + + +template +template +inline void +LeafNode::combine2(const ValueType& value, const OtherNodeT& other, + bool valueIsActive, CombineOp& op) +{ +#ifndef OPENVDB_2_ABI_COMPATIBLE + if (!this->allocate()) return; +#endif + CombineArgs args; + args.setARef(value).setAIsActive(valueIsActive); + for (Index i = 0; i < SIZE; ++i) { + op(args.setBRef(other.mBuffer[i]) + .setBIsActive(other.valueMask().isOn(i)) + .setResultRef(mBuffer[i])); + mValueMask.set(i, args.resultIsActive()); + } +} + + +template +template +inline void +LeafNode::combine2(const LeafNode& b0, const OtherNodeT& b1, CombineOp& op) +{ +#ifndef OPENVDB_2_ABI_COMPATIBLE + if (!this->allocate()) return; +#endif + CombineArgs args; + for (Index i = 0; i < SIZE; ++i) { + mValueMask.set(i, b0.valueMask().isOn(i) || b1.valueMask().isOn(i)); + op(args.setARef(b0.mBuffer[i]) + .setAIsActive(b0.valueMask().isOn(i)) + .setBRef(b1.mBuffer[i]) + .setBIsActive(b1.valueMask().isOn(i)) + .setResultRef(mBuffer[i])); + mValueMask.set(i, args.resultIsActive()); + } +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::visitActiveBBox(BBoxOp& op) const +{ + if (op.template descent()) { + for (ValueOnCIter i=this->cbeginValueOn(); i; ++i) { +#ifdef _MSC_VER + op.operator()(CoordBBox::createCube(i.getCoord(), 1)); +#else + op.template operator()(CoordBBox::createCube(i.getCoord(), 1)); +#endif + } + } else { +#ifdef _MSC_VER + op.operator()(this->getNodeBoundingBox()); +#else + op.template operator()(this->getNodeBoundingBox()); +#endif + } +} + + +template +template +inline void +LeafNode::visit(VisitorOp& op) +{ + doVisit(*this, op); +} + + +template +template +inline void +LeafNode::visit(VisitorOp& op) const +{ + doVisit(*this, op); +} + + +template +template +inline void +LeafNode::doVisit(NodeT& self, VisitorOp& op) +{ + for (ChildAllIterT iter = self.beginChildAll(); iter; ++iter) { + op(iter); + } +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::visit2Node(OtherLeafNodeType& other, VisitorOp& op) +{ + doVisit2Node(*this, other, op); +} + + +template +template +inline void +LeafNode::visit2Node(OtherLeafNodeType& other, VisitorOp& op) const +{ + doVisit2Node(*this, other, op); +} + + +template +template< + typename NodeT, + typename OtherNodeT, + typename VisitorOp, + typename ChildAllIterT, + typename OtherChildAllIterT> +inline void +LeafNode::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 +template +inline void +LeafNode::visit2(IterT& otherIter, VisitorOp& op, bool otherIsLHS) +{ + doVisit2( + *this, otherIter, op, otherIsLHS); +} + + +template +template +inline void +LeafNode::visit2(IterT& otherIter, VisitorOp& op, bool otherIsLHS) const +{ + doVisit2( + *this, otherIter, op, otherIsLHS); +} + + +template +template< + typename NodeT, + typename VisitorOp, + typename ChildAllIterT, + typename OtherChildAllIterT> +inline void +LeafNode::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 +inline std::ostream& +operator<<(std::ostream& os, const typename LeafNode::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" + +// Specialization for LeafNodes with mask information only +#include "LeafNodeMask.h" + +#endif // OPENVDB_TREE_LEAFNODE_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tree/LeafNodeBool.h b/nuparu/include/openvdb/include/openvdb/tree/LeafNodeBool.h new file mode 100644 index 00000000..8591b6c1 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tree/LeafNodeBool.h @@ -0,0 +1,1773 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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_LEAF_NODE_BOOL_HAS_BEEN_INCLUDED +#define OPENVDB_TREE_LEAF_NODE_BOOL_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include // for io::readData(), etc. +#include // for math::isZero() +#include +#include "LeafNode.h" +#include "Iterator.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 +class LeafNode +{ +public: + typedef LeafNode LeafNodeType; + typedef boost::shared_ptr Ptr; + typedef bool BuildType; + typedef bool ValueType; + typedef util::NodeMask 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::Type is the type of a LeafNode having the same + /// dimensions as this node but a different value type, T. + template + struct ValueConverter { typedef LeafNode Type; }; + + /// @brief SameConfiguration::value is @c true if and only if + /// OtherNodeType is the type of a LeafNode with the same dimensions as this node. + template + struct SameConfiguration { + static const bool value = SameLeafConfig::value; + }; + + + class Buffer + { + public: + typedef typename NodeMaskType::Word WordType; + static const Index WORD_COUNT = NodeMaskType::WORD_COUNT; + 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); + // We can't use the ternary operator here, otherwise Visual C++ returns + // a reference to a temporary. + if (mData.isOn(i)) return LeafNode::sOn; else return 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; } + + /// Return a point to the c-style array of words encoding the bits. + /// @warning This method should only be used by experts that + /// seek low-level optimizations. + WordType* data() + { + return &(mData.template getWord(0)); + } + /// Return a const point to the c-style array of words + /// encoding the bits. + /// @warning This method should only be used by experts that + /// seek low-level optimizations. + const WordType* data() const + { + return const_cast(this)->data(); + } + + 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); + +#ifndef OPENVDB_2_ABI_COMPATIBLE + /// "Partial creation" constructor used during file input + LeafNode(PartialCreate, const Coord& xyz, bool value = false, bool active = false); +#endif + + /// Deep copy constructor + LeafNode(const LeafNode&); + + /// Value conversion copy constructor + template + explicit LeafNode(const LeafNode& other); + + /// Topology copy constructor + template + LeafNode(const LeafNode& other, TopologyCopy); + + //@{ + /// @brief Topology copy constructor + /// @note This variant exists mainly to enable template instantiation. + template + LeafNode(const LeafNode& other, bool offValue, bool onValue, TopologyCopy); + template + LeafNode(const LeafNode& 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& 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(); } + static Index64 onTileCount() { return 0; } + static Index64 offTileCount() { return 0; } + + /// 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(); } + +#ifndef OPENVDB_2_ABI_COMPATIBLE + /// @brief Return @c true if memory for this node's buffer has been allocated. + /// @details Currently, boolean leaf nodes don't support partial creation, + /// so this always returns @c true. + bool isAllocated() const { return true; } + /// @brief Allocate memory for this node's buffer if it has not already been allocated. + /// @details Currently, boolean leaf nodes don't support partial creation, + /// so this has no effect. + bool allocate() { return true; } +#endif + + /// 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. + /// If visitVoxels is false this LeafNode will be approximated as dense, i.e. with all + /// voxels active. Else the individual active voxels are visited to produce a tight bbox. + void evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels = true) 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; } + //@{ + /// Return the grid index coordinates of this node's local origin. + const Coord& origin() 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 global or local coordinates. + static Index coordToOffset(const Coord& xyz); + /// @brief Return the local coordinates for a linear table offset, + /// where offset 0 has coordinates (0, 0, 0). + static Coord offsetToLocalCoord(Index n); + /// Return the global coordinates for a linear table offset. + Coord offsetToGlobalCoord(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 + bool hasSameTopology(const LeafNode* 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); + void readBuffers(std::istream& is, const CoordBBox&, 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 of the voxel at the given coordinates but don't change its value. + void setActiveState(const Coord& xyz, bool on); + /// Set the active state of the voxel at the given offset but don't change its value. + void setActiveState(Index offset, bool on) { assert(offsetcoordToOffset(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); } + + /// Set the value of the voxel at the given coordinates and mark the voxel as inactive. + void setValueOff(const Coord& xyz, bool val); + /// Set the value of the voxel at the given offset and mark the voxel as inactive. + void setValueOff(Index offset, 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->coordToOffset(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, bool val); + /// Set the value of the voxel at the given coordinates and mark the voxel as active. + void setValue(const Coord& xyz, bool val) { this->setValueOn(xyz, val); } + /// Set the value of the voxel at the given offset and mark the voxel as active. + void setValueOn(Index offset, bool val); + + /// @brief Apply a functor to the value of the voxel at the given offset + /// and mark the voxel as active. + template + void modifyValue(Index offset, const ModifyOp& op); + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + template + void modifyValue(const Coord& xyz, const ModifyOp& op); + + /// Apply a functor to the voxel at the given coordinates. + template + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op); + + /// 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->coordToOffset(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; } + + /// Set all voxels that lie outside the given axis-aligned box to the background. + void clip(const CoordBBox&, bool background); + + /// Set all voxels within an axis-aligned box to the specified value and active state. + void fill(const CoordBBox& bbox, bool value, bool active = true); + + /// Set all voxels to the specified value but don't change their active states. + void fill(const bool& value); + /// Set all voxels to the specified value and active 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 + 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 + 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. + template + 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. + template + 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. + template + 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. + template + 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. + template + void setValueOffAndCache(const Coord& xyz, bool value, AccessorT&) + { + this->setValueOff(xyz, value); + } + + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// @note Used internally by ValueAccessor. + template + void modifyValueAndCache(const Coord& xyz, const ModifyOp& op, AccessorT&) + { + this->modifyValue(xyz, op); + } + + /// Apply a functor to the voxel at the given coordinates. + /// @note Used internally by ValueAccessor. + template + void modifyValueAndActiveStateAndCache(const Coord& xyz, const ModifyOp& op, AccessorT&) + { + this->modifyValueAndActiveState(xyz, op); + } + + /// @brief Set the active state of the voxel at the given coordinates + /// without changing its value. + /// @note Used internally by ValueAccessor. + template + 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. + template + 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. + template + 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(); } + + template + void merge(const LeafNode& other, bool bg = false, bool otherBG = false); + template void merge(bool tileValue, bool tileActive); + + /// @brief No-op + /// @details This function exists only to enable template instantiation. + void voxelizeActiveTiles(bool = true) {} + + /// @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 + void topologyUnion(const LeafNode& 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 + void topologyIntersection(const LeafNode& 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 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, because it can deactivate all of this node's voxels, + /// consider subsequently calling prune. + template + void topologyDifference(const LeafNode& other, const bool&); + + template + void combine(const LeafNode& other, CombineOp& op); + template + void combine(bool, bool valueIsActive, CombineOp& op); + + template + void combine2(const LeafNode& other, const OtherType&, bool valueIsActive, CombineOp&); + template + void combine2(bool, const OtherNodeT& other, bool valueIsActive, CombineOp&); + template + void combine2(const LeafNode& b0, const OtherNodeT& 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 void visitActiveBBox(BBoxOp&) const; + + template void visit(VisitorOp&); + template void visit(VisitorOp&) const; + + template + void visit2Node(OtherLeafNodeType& other, VisitorOp&); + template + void visit2Node(OtherLeafNodeType& other, VisitorOp&) const; + template + void visit2(IterT& otherIter, VisitorOp&, bool otherIsLHS = false); + template + void visit2(IterT& otherIter, VisitorOp&, bool otherIsLHS = false) const; + + //@{ + /// This function exists only to enable template instantiation. + void prune(const ValueType& /*tolerance*/ = zeroVal()) {} + void addLeaf(LeafNode*) {} + template + void addLeafAndCache(LeafNode*, AccessorT&) {} + template + NodeT* stealNode(const Coord&, const ValueType&, bool) { return NULL; } + template + NodeT* probeNode(const Coord&) { return NULL; } + template + const NodeT* probeConstNode(const Coord&) const { return NULL; } + template void getNodes(ArrayT&) const {} + template void stealNodes(ArrayT&, const ValueType&, bool) {} + //@} + + void addTile(Index level, const Coord&, bool val, bool active); + void addTile(Index offset, bool val, bool active); + template + void addTileAndCache(Index level, const Coord&, bool val, bool active, AccessorT&); + + //@{ + /// @brief Return a pointer to this node. + LeafNode* touchLeaf(const Coord&) { return this; } + template + LeafNode* touchLeafAndCache(const Coord&, AccessorT&) { return this; } + LeafNode* probeLeaf(const Coord&) { return this; } + template + LeafNode* probeLeafAndCache(const Coord&, AccessorT&) { return this; } + template + NodeT* probeNodeAndCache(const Coord&, AccessorT&) + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (!(boost::is_same::value)) return NULL; + return reinterpret_cast(this); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + //@} + //@{ + /// @brief Return a @const pointer to this node. + const LeafNode* probeLeaf(const Coord&) const { return this; } + template + const LeafNode* probeLeafAndCache(const Coord&, AccessorT&) const { return this; } + const LeafNode* probeConstLeaf(const Coord&) const { return this; } + template + const LeafNode* probeConstLeafAndCache(const Coord&, AccessorT&) const { return this; } + template + const NodeT* probeConstNodeAndCache(const Coord&, AccessorT&) const + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (!(boost::is_same::value)) return NULL; + return reinterpret_cast(this); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + //@} + + // + // Iterators + // +protected: + typedef typename NodeMaskType::OnIterator MaskOnIter; + typedef typename NodeMaskType::OffIterator MaskOffIter; + typedef typename NodeMaskType::DenseIterator MaskDenseIter; + + template + struct ValueIter: + // Derives from SparseIteratorBase, but can also be used as a dense iterator, + // if MaskIterT is a dense mask iterator type. + public SparseIteratorBase, NodeT, ValueT> + { + typedef SparseIteratorBase 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); } + + // Note: modifyItem() can't be called on const iterators. + template + void modifyItem(Index n, const ModifyOp& op) const { this->parent().modifyValue(n, op); } + // Note: modifyValue() can't be called on const iterators. + template + void modifyValue(const ModifyOp& op) const { this->modifyItem(this->pos(), op); } + }; + + /// Leaf nodes have no children, so their child iterators have no get/set accessors. + template + struct ChildIter: + public SparseIteratorBase, NodeT, bool> + { + ChildIter() {} + ChildIter(const MaskIterT& iter, NodeT* parent): SparseIteratorBase< + MaskIterT, ChildIter, NodeT, bool>(iter, parent) {} + }; + + template + struct DenseIter: public DenseIteratorBase< + MaskDenseIter, DenseIter, NodeT, /*ChildT=*/void, ValueT> + { + typedef DenseIteratorBase 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 ValueOnIter; + typedef ValueIter ValueOnCIter; + typedef ValueIter ValueOffIter; + typedef ValueIter ValueOffCIter; + typedef ValueIter ValueAllIter; + typedef ValueIter ValueAllCIter; + typedef ChildIter ChildOnIter; + typedef ChildIter ChildOnCIter; + typedef ChildIter ChildOffIter; + typedef ChildIter ChildOffCIter; + typedef DenseIter ChildAllIter; + typedef DenseIter 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; } + const NodeMaskType& valueMask() 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 + static inline void doVisit(NodeT&, VisitorOp&); + + template + static inline void doVisit2Node(NodeT& self, OtherNodeT& other, VisitorOp&); + + template + 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 friend class LeafNode; + + friend struct ValueIter; + friend struct ValueIter; + friend struct ValueIter; + friend struct ValueIter; + friend struct ValueIter; + friend struct ValueIter; + + //@{ + /// Allow iterators to call mask accessor methods (see below). + /// @todo Make mask accessors public? + friend class IteratorBase; + friend class IteratorBase; + friend class IteratorBase; + //@} + +}; // class LeafNode + + +/// @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 const bool LeafNode::sOn = true; +template const bool LeafNode::sOff = false; + + +//////////////////////////////////////// + + +template +inline +LeafNode::LeafNode() + : mOrigin(0, 0, 0) +{ +} + + +template +inline +LeafNode::LeafNode(const Coord& xyz, bool value, bool active) + : mValueMask(active) + , mBuffer(value) + , mOrigin(xyz & (~(DIM - 1))) +{ +} + + +#ifndef OPENVDB_2_ABI_COMPATIBLE +template +inline +LeafNode::LeafNode(PartialCreate, const Coord& xyz, bool value, bool active) + : mValueMask(active) + , mBuffer(value) + , mOrigin(xyz & (~(DIM - 1))) +{ + /// @todo For now, this is identical to the non-PartialCreate constructor. + /// Consider modifying the Buffer class to allow it to be constructed + /// without allocating a bitmask. +} +#endif + + +template +inline +LeafNode::LeafNode(const LeafNode &other) + : mValueMask(other.valueMask()) + , mBuffer(other.mBuffer) + , mOrigin(other.mOrigin) +{ +} + + +// Copy-construct from a leaf node with the same configuration but a different ValueType. +template +template +inline +LeafNode::LeafNode(const LeafNode& other) + : mValueMask(other.valueMask()) + , mOrigin(other.origin()) +{ + struct Local { + /// @todo Consider using a value conversion functor passed as an argument instead. + static inline bool convertValue(const ValueT& val) { return bool(val); } + }; + + for (Index i = 0; i < SIZE; ++i) { + mBuffer.setValue(i, Local::convertValue(other.mBuffer[i])); + } +} + + +template +template +inline +LeafNode::LeafNode(const LeafNode& other, + bool background, TopologyCopy) + : mValueMask(other.valueMask()) + , mBuffer(background) + , mOrigin(other.origin()) +{ +} + + +template +template +inline +LeafNode::LeafNode(const LeafNode& other, TopologyCopy) + : mValueMask(other.valueMask()) + , mBuffer(other.valueMask())// value = active state + , mOrigin(other.origin()) +{ +} + + +template +template +inline +LeafNode::LeafNode(const LeafNode& other, + bool offValue, bool onValue, TopologyCopy) + : mValueMask(other.valueMask()) + , mBuffer(other.valueMask()) + , mOrigin(other.origin()) +{ + if (offValue) { if (!onValue) mBuffer.mData.toggle(); else mBuffer.mData.setOn(); } +} + + +template +inline +LeafNode::~LeafNode() +{ +} + + +//////////////////////////////////////// + + +template +inline Index64 +LeafNode::memUsage() const +{ + return sizeof(mOrigin) + mValueMask.memUsage() + mBuffer.memUsage(); +} + + +template +inline void +LeafNode::evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels) const +{ + CoordBBox this_bbox = this->getNodeBoundingBox(); + if (bbox.isInside(this_bbox)) return;//this LeafNode is already enclosed in the bbox + if (ValueOnCIter iter = this->cbeginValueOn()) {//any active values? + if (visitVoxels) {//use voxel granularity? + this_bbox.reset(); + for(; iter; ++iter) this_bbox.expand(this->offsetToLocalCoord(iter.pos())); + this_bbox.translate(this->origin()); + } + bbox.expand(this_bbox); + } +} + + +template +template +inline bool +LeafNode::hasSameTopology(const LeafNode* other) const +{ + assert(other); + return (Log2Dim == OtherLog2Dim && mValueMask == other->getValueMask()); +} + + +template +inline std::string +LeafNode::str() const +{ + std::ostringstream ostr; + ostr << "LeafNode @" << mOrigin << ": "; + for (Index32 n = 0; n < SIZE; ++n) ostr << (mValueMask.isOn(n) ? '#' : '.'); + return ostr.str(); +} + + +//////////////////////////////////////// + + +template +inline Index +LeafNode::coordToOffset(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 +inline Coord +LeafNode::offsetToLocalCoord(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 +inline Coord +LeafNode::offsetToGlobalCoord(Index n) const +{ + return (this->offsetToLocalCoord(n) + this->origin()); +} + + +//////////////////////////////////////// + + +template +inline void +LeafNode::readTopology(std::istream& is, bool /*fromHalf*/) +{ + mValueMask.load(is); +} + + +template +inline void +LeafNode::writeTopology(std::ostream& os, bool /*toHalf*/) const +{ + mValueMask.save(os); +} + + +template +inline void +LeafNode::readBuffers(std::istream& is, const CoordBBox& clipBBox, bool fromHalf) +{ + // Boolean LeafNodes don't currently implement lazy loading. + // Instead, load the full buffer, then clip it. + + this->readBuffers(is, fromHalf); + + // Get this tree's background value. + bool background = false; + if (const void* bgPtr = io::getGridBackgroundValuePtr(is)) { + background = *static_cast(bgPtr); + } + this->clip(clipBBox, background); +} + + +template +inline void +LeafNode::readBuffers(std::istream& is, bool /*fromHalf*/) +{ + // Read in the value mask. + mValueMask.load(is); + // Read in the origin. + is.read(reinterpret_cast(&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(&numBuffers), sizeof(int8_t)); + + // Read in the buffer. + // (Note: prior to the bool leaf optimization, buffers were always compressed.) + boost::shared_array buf(new bool[SIZE]); + io::readData(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(is, buf.get(), SIZE, /*isCompressed=*/true); + } + } + } +} + + +template +inline void +LeafNode::writeBuffers(std::ostream& os, bool /*toHalf*/) const +{ + // Write out the value mask. + mValueMask.save(os); + // Write out the origin. + os.write(reinterpret_cast(&mOrigin), sizeof(Coord::ValueType) * 3); + // Write out the voxel values. + mBuffer.mData.save(os); +} + + +//////////////////////////////////////// + + +template +inline bool +LeafNode::operator==(const LeafNode& other) const +{ + return mOrigin == other.mOrigin && + mValueMask == other.valueMask() && + mBuffer == other.mBuffer; +} + + +template +inline bool +LeafNode::operator!=(const LeafNode& other) const +{ + return !(this->operator==(other)); +} + + +//////////////////////////////////////// + + +template +inline bool +LeafNode::isConstant(bool& constValue, bool& state, bool tolerance) const +{ + state = mValueMask.isOn(); + + if (!(state || 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; + + constValue = mBuffer.mData.isOn(); + return true; +} + + +//////////////////////////////////////// + + +template +inline void +LeafNode::addTile(Index /*level*/, const Coord& xyz, bool val, bool active) +{ + this->addTile(this->coordToOffset(xyz), val, active); +} + +template +inline void +LeafNode::addTile(Index offset, bool val, bool active) +{ + assert(offset < SIZE); + this->setValueOnly(offset, val); + this->setActiveState(offset, active); +} + +template +template +inline void +LeafNode::addTileAndCache(Index level, const Coord& xyz, + bool val, bool active, AccessorT&) +{ + this->addTile(level, xyz, val, active); +} + + +//////////////////////////////////////// + + +template +inline const bool& +LeafNode::getValue(const Coord& xyz) const +{ + // This *CANNOT* use operator ? because Visual C++ + if (mBuffer.mData.isOn(this->coordToOffset(xyz))) return sOn; else return sOff; +} + + +template +inline const bool& +LeafNode::getValue(Index offset) const +{ + assert(offset < SIZE); + // This *CANNOT* use operator ? for Windows + if (mBuffer.mData.isOn(offset)) return sOn; else return sOff; +} + + +template +inline bool +LeafNode::probeValue(const Coord& xyz, bool& val) const +{ + const Index offset = this->coordToOffset(xyz); + val = mBuffer.mData.isOn(offset); + return mValueMask.isOn(offset); +} + + +template +inline void +LeafNode::setValueOn(const Coord& xyz, bool val) +{ + this->setValueOn(this->coordToOffset(xyz), val); +} + + +template +inline void +LeafNode::setValueOn(Index offset, bool val) +{ + assert(offset < SIZE); + mValueMask.setOn(offset); + mBuffer.mData.set(offset, val); +} + + +template +inline void +LeafNode::setValueOnly(const Coord& xyz, bool val) +{ + this->setValueOnly(this->coordToOffset(xyz), val); +} + + +template +inline void +LeafNode::setActiveState(const Coord& xyz, bool on) +{ + mValueMask.set(this->coordToOffset(xyz), on); +} + + +template +inline void +LeafNode::setValueOff(const Coord& xyz, bool val) +{ + this->setValueOff(this->coordToOffset(xyz), val); +} + + +template +inline void +LeafNode::setValueOff(Index offset, bool val) +{ + assert(offset < SIZE); + mValueMask.setOff(offset); + mBuffer.mData.set(offset, val); +} + + +template +template +inline void +LeafNode::modifyValue(Index offset, const ModifyOp& op) +{ + bool val = mBuffer.mData.isOn(offset); + op(val); + mBuffer.mData.set(offset, val); + mValueMask.setOn(offset); +} + + +template +template +inline void +LeafNode::modifyValue(const Coord& xyz, const ModifyOp& op) +{ + this->modifyValue(this->coordToOffset(xyz), op); +} + + +template +template +inline void +LeafNode::modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) +{ + const Index offset = this->coordToOffset(xyz); + bool val = mBuffer.mData.isOn(offset), state = mValueMask.isOn(offset); + op(val, state); + mBuffer.mData.set(offset, val); + mValueMask.set(offset, state); +} + + +//////////////////////////////////////// + + +template +inline void +LeafNode::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 +template +inline void +LeafNode::merge(const LeafNode& other, bool /*bg*/, bool /*otherBG*/) +{ + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (Policy == MERGE_NODES) return; + for (typename NodeMaskType::OnIterator iter = other.valueMask().beginOn(); iter; ++iter) { + const Index n = iter.pos(); + if (mValueMask.isOff(n)) { + mBuffer.mData.set(n, other.mBuffer.mData.isOn(n)); + mValueMask.setOn(n); + } + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + +template +template +inline void +LeafNode::merge(bool tileValue, bool tileActive) +{ + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (Policy != MERGE_ACTIVE_STATES_AND_NODES) return; + if (!tileActive) return; + // Replace all inactive values with the active tile value. + if (tileValue) mBuffer.mData |= !mValueMask; // -0=>1, +0=>0, -1=>1, +1=>1 (-,+ = off,on) + else mBuffer.mData &= mValueMask; // -0=>0, +0=>0, -1=>0, +1=>1 + mValueMask.setOn(); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::topologyUnion(const LeafNode& other) +{ + mValueMask |= other.valueMask(); +} + + +template +template +inline void +LeafNode::topologyIntersection(const LeafNode& other, + const bool&) +{ + mValueMask &= other.valueMask(); +} + + +template +template +inline void +LeafNode::topologyDifference(const LeafNode& other, + const bool&) +{ + mValueMask &= !other.valueMask(); +} + + +//////////////////////////////////////// + + +template +inline void +LeafNode::clip(const CoordBBox& clipBBox, bool background) +{ + CoordBBox nodeBBox = this->getNodeBoundingBox(); + if (!clipBBox.hasOverlap(nodeBBox)) { + // This node lies completely outside the clipping region. Fill it with background tiles. + this->fill(nodeBBox, background, /*active=*/false); + } else if (clipBBox.isInside(nodeBBox)) { + // This node lies completely inside the clipping region. Leave it intact. + return; + } + + // This node isn't completely contained inside the clipping region. + // Set any voxels that lie outside the region to the background value. + + // Construct a boolean mask that is on inside the clipping region and off outside it. + NodeMaskType mask; + nodeBBox.intersect(clipBBox); + Coord xyz; + int &x = xyz.x(), &y = xyz.y(), &z = xyz.z(); + for (x = nodeBBox.min().x(); x <= nodeBBox.max().x(); ++x) { + for (y = nodeBBox.min().y(); y <= nodeBBox.max().y(); ++y) { + for (z = nodeBBox.min().z(); z <= nodeBBox.max().z(); ++z) { + mask.setOn(static_cast(this->coordToOffset(xyz))); + } + } + } + + // Set voxels that lie in the inactive region of the mask (i.e., outside + // the clipping region) to the background value. + for (MaskOffIter maskIter = mask.beginOff(); maskIter; ++maskIter) { + this->setValueOff(maskIter.pos(), background); + } +} + + +//////////////////////////////////////// + + +template +inline void +LeafNode::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 +inline void +LeafNode::fill(const bool& value) +{ + mBuffer.fill(value); +} + +template +inline void +LeafNode::fill(const bool& value, bool active) +{ + mBuffer.fill(value); + mValueMask.set(active); +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::copyToDense(const CoordBBox& bbox, DenseT& dense) const +{ + typedef typename DenseT::ValueType DenseValueType; + + const size_t xStride = dense.xStride(), yStride = dense.yStride(), zStride = dense.zStride(); + const Coord& min = dense.bbox().min(); + DenseValueType* t0 = dense.data() + zStride * (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) { + DenseValueType* 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) { + DenseValueType* 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 += zStride) { + *t2 = DenseValueType(mBuffer.mData.isOn(n2++)); + } + } + } +} + + +template +template +inline void +LeafNode::copyFromDense(const CoordBBox& bbox, const DenseT& dense, + bool background, bool tolerance) +{ + typedef typename DenseT::ValueType DenseValueType; + struct Local { + inline static bool toBool(const DenseValueType& v) { return !math::isZero(v); } + }; + + const size_t xStride = dense.xStride(), yStride = dense.yStride(), zStride = dense.zStride(); + const Coord& min = dense.bbox().min(); + const DenseValueType* s0 = dense.data() + zStride * (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 DenseValueType* 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 DenseValueType* 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 += zStride) { + // Note: if tolerance is true (i.e., 1), then all boolean values compare equal. + if (tolerance || (background == Local::toBool(*s2))) { + mValueMask.setOff(n2); + mBuffer.mData.set(n2, background); + } else { + mValueMask.setOn(n2); + mBuffer.mData.set(n2, Local::toBool(*s2)); + } + } + } + } +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::combine(const LeafNode& other, CombineOp& op) +{ + CombineArgs 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.valueMask().isOn(i)) + .setResultRef(result)); + mValueMask.set(i, args.resultIsActive()); + mBuffer.mData.set(i, result); + } +} + + +template +template +inline void +LeafNode::combine(bool value, bool valueIsActive, CombineOp& op) +{ + CombineArgs 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 +template +inline void +LeafNode::combine2(const LeafNode& other, const OtherType& value, + bool valueIsActive, CombineOp& op) +{ + CombineArgs 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.valueMask().isOn(i)) + .setResultRef(result)); + mValueMask.set(i, args.resultIsActive()); + mBuffer.mData.set(i, result); + } +} + + +template +template +inline void +LeafNode::combine2(bool value, const OtherNodeT& other, + bool valueIsActive, CombineOp& op) +{ + CombineArgs 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.valueMask().isOn(i)) + .setResultRef(result)); + mValueMask.set(i, args.resultIsActive()); + mBuffer.mData.set(i, result); + } +} + + +template +template +inline void +LeafNode::combine2(const LeafNode& b0, const OtherNodeT& b1, CombineOp& op) +{ + CombineArgs args; + for (Index i = 0; i < SIZE; ++i) { + // Default behavior: output voxel is active if either input voxel is active. + mValueMask.set(i, b0.valueMask().isOn(i) || b1.valueMask().isOn(i)); + + bool result = false, b0Val = b0.mBuffer.mData.isOn(i), b1Val = b1.mBuffer.mData.isOn(i); + op(args.setARef(b0Val) + .setAIsActive(b0.valueMask().isOn(i)) + .setBRef(b1Val) + .setBIsActive(b1.valueMask().isOn(i)) + .setResultRef(result)); + mValueMask.set(i, args.resultIsActive()); + mBuffer.mData.set(i, result); + } +} + + +//////////////////////////////////////// + +template +template +inline void +LeafNode::visitActiveBBox(BBoxOp& op) const +{ + if (op.template descent()) { + for (ValueOnCIter i=this->cbeginValueOn(); i; ++i) { +#ifdef _MSC_VER + op.operator()(CoordBBox::createCube(i.getCoord(), 1)); +#else + op.template operator()(CoordBBox::createCube(i.getCoord(), 1)); +#endif + } + } else { +#ifdef _MSC_VER + op.operator()(this->getNodeBoundingBox()); +#else + op.template operator()(this->getNodeBoundingBox()); +#endif + } +} + + +template +template +inline void +LeafNode::visit(VisitorOp& op) +{ + doVisit(*this, op); +} + + +template +template +inline void +LeafNode::visit(VisitorOp& op) const +{ + doVisit(*this, op); +} + + +template +template +inline void +LeafNode::doVisit(NodeT& self, VisitorOp& op) +{ + for (ChildAllIterT iter = self.beginChildAll(); iter; ++iter) { + op(iter); + } +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::visit2Node(OtherLeafNodeType& other, VisitorOp& op) +{ + doVisit2Node(*this, other, op); +} + + +template +template +inline void +LeafNode::visit2Node(OtherLeafNodeType& other, VisitorOp& op) const +{ + doVisit2Node(*this, other, op); +} + + +template +template< + typename NodeT, + typename OtherNodeT, + typename VisitorOp, + typename ChildAllIterT, + typename OtherChildAllIterT> +inline void +LeafNode::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 +template +inline void +LeafNode::visit2(IterT& otherIter, VisitorOp& op, bool otherIsLHS) +{ + doVisit2(*this, otherIter, op, otherIsLHS); +} + + +template +template +inline void +LeafNode::visit2(IterT& otherIter, VisitorOp& op, bool otherIsLHS) const +{ + doVisit2(*this, otherIter, op, otherIsLHS); +} + + +template +template< + typename NodeT, + typename VisitorOp, + typename ChildAllIterT, + typename OtherChildAllIterT> +inline void +LeafNode::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_LEAF_NODE_BOOL_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tree/LeafNodeMask.h b/nuparu/include/openvdb/include/openvdb/tree/LeafNodeMask.h new file mode 100644 index 00000000..46aa78d3 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tree/LeafNodeMask.h @@ -0,0 +1,1687 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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_LEAF_NODE_MASK_HAS_BEEN_INCLUDED +#define OPENVDB_TREE_LEAF_NODE_MASK_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include +#include // for io::readData(), etc. +#include // for math::isZero() +#include +#include "LeafNode.h" +#include "Iterator.h" + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tree { + +/// @brief LeafNode specialization for values of type ValueMask that encodes both +/// the active states and the boolean values of (2^Log2Dim)^3 voxels +/// in a single bit mask, i.e. voxel values and states are indistinguishable! +template +class LeafNode +{ +public: + typedef LeafNode LeafNodeType; + typedef boost::shared_ptr Ptr; + typedef ValueMask BuildType;// this is a rare case where + typedef bool ValueType;// value type != build type + typedef util::NodeMask 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::Type is the type of a LeafNode having the same + /// dimensions as this node but a different value type, T. + template + struct ValueConverter { + typedef LeafNode Type; + }; + + /// @brief SameConfiguration::value is @c true if and only if + /// OtherNodeType is the type of a LeafNode with the same dimensions as this node. + template + struct SameConfiguration { + static const bool value = SameLeafConfig::value; + }; + + class Buffer + { + public: + typedef typename NodeMaskType::Word WordType; + static const Index WORD_COUNT = NodeMaskType::WORD_COUNT; + Buffer() {} + explicit 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); + // We can't use the ternary operator here, otherwise Visual C++ returns + // a reference to a temporary. + if (mData.isOn(i)) return LeafNode::sOn; + return 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; } + + /// Return a point to the c-style array of words encoding the bits. + /// @warning This method should only be used by experts that + /// seek low-level optimizations. + WordType* data() + { + return &(mData.template getWord(0)); + } + /// Return a const point to the c-style array of words + /// encoding the bits. + /// @warning This method should only be used by experts that + /// seek low-level optimizations. + const WordType* data() const + { + return const_cast(this)->data(); + } + + 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 = state for all of this node's voxels + /// @param dummy dummy value + explicit LeafNode(const Coord& xyz, bool value = false, bool dummy = false); + +#ifndef OPENVDB_2_ABI_COMPATIBLE + /// "Partial creation" constructor used during file input + LeafNode(PartialCreate, const Coord& xyz, bool value = false, bool dummy = false); +#endif + + /// Deep copy constructor + LeafNode(const LeafNode&); + + /// Value conversion copy constructor + template + explicit LeafNode(const LeafNode& other); + + /// Topology copy constructor + template + LeafNode(const LeafNode& other, TopologyCopy); + + //@{ + /// @brief Topology copy constructor + /// @note This variant exists mainly to enable template instantiation. + template + LeafNode(const LeafNode& other, bool offValue, bool onValue, TopologyCopy); + template + LeafNode(const LeafNode& 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; } + /// 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& 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 active voxels. + Index64 onVoxelCount() const { return mBuffer.mData.countOn(); } + /// Return the number of inactive voxels. + Index64 offVoxelCount() const { return mBuffer.mData.countOff(); } + Index64 onLeafVoxelCount() const { return this->onVoxelCount(); } + Index64 offLeafVoxelCount() const { return this->offVoxelCount(); } + static Index64 onTileCount() { return 0; } + static Index64 offTileCount() { return 0; } + + /// Return @c true if this node has no active voxels. + bool isEmpty() const { return mBuffer.mData.isOff(); } + /// Return @c true if this node only contains active voxels. + bool isDense() const { return mBuffer.mData.isOn(); } + +#ifndef OPENVDB_2_ABI_COMPATIBLE + /// @brief Return @c true if memory for this node's buffer has been allocated. + /// @details Currently, boolean leaf nodes don't support partial creation, + /// so this always returns @c true. + bool isAllocated() const { return true; } + /// @brief Allocate memory for this node's buffer if it has not already been allocated. + /// @details Currently, boolean leaf nodes don't support partial creation, + /// so this has no effect. + bool allocate() { return true; } +#endif + + /// 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. + /// If visitVoxels is false this LeafNode will be approximated as dense, i.e. with all + /// voxels active. Else the individual active voxels are visited to produce a tight bbox. + void evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels = true) 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; } + //@{ + /// Return the grid index coordinates of this node's local origin. + const Coord& origin() 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 global or local coordinates. + static Index coordToOffset(const Coord& xyz); + /// @brief Return the local coordinates for a linear table offset, + /// where offset 0 has coordinates (0, 0, 0). + static Coord offsetToLocalCoord(Index n); + /// Return the global coordinates for a linear table offset. + Coord offsetToGlobalCoord(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 + bool hasSameTopology(const LeafNode* 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); + void readBuffers(std::istream& is, const CoordBBox&, 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 of the voxel at the given coordinates but don't change its value. + void setActiveState(const Coord& xyz, bool on); + /// Set the active state of the voxel at the given offset but don't change its value. + void setActiveState(Index offset, bool on) { assert(offsetcoordToOffset(xyz)); } + /// Mark the voxel at the given offset as inactive but don't change its value. + void setValueOff(Index offset) { assert(offset < SIZE); mBuffer.mData.setOff(offset); } + + /// Set the value of the voxel at the given coordinates and mark the voxel as inactive. + void setValueOff(const Coord& xyz, bool val); + /// Set the value of the voxel at the given offset and mark the voxel as inactive. + void setValueOff(Index offset, bool val); + + /// Mark the voxel at the given coordinates as active but don't change its value. + void setValueOn(const Coord& xyz) { mBuffer.mData.setOn(this->coordToOffset(xyz)); } + /// Mark the voxel at the given offset as active but don't change its value. + void setValueOn(Index offset) { assert(offset < SIZE); mBuffer.mData.setOn(offset); } + + /// Set the value of the voxel at the given coordinates and mark the voxel as active. + void setValueOn(const Coord& xyz, bool val); + /// Set the value of the voxel at the given coordinates and mark the voxel as active. + void setValue(const Coord& xyz, bool val) { this->setValueOn(xyz, val); } + /// Set the value of the voxel at the given offset and mark the voxel as active. + void setValueOn(Index offset, bool val); + + /// @brief Apply a functor to the value of the voxel at the given offset + /// and mark the voxel as active. + template + void modifyValue(Index offset, const ModifyOp& op); + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + template + void modifyValue(const Coord& xyz, const ModifyOp& op); + + /// Apply a functor to the voxel at the given coordinates. + template + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op); + + /// Mark all voxels as active but don't change their values. + void setValuesOn() { mBuffer.mData.setOn(); } + /// Mark all voxels as inactive but don't change their values. + void setValuesOff() { mBuffer.mData.setOff(); } + + /// Return @c true if the voxel at the given coordinates is active. + bool isValueOn(const Coord& xyz) const { return mBuffer.mData.isOn(this->coordToOffset(xyz)); } + /// Return @c true if the voxel at the given offset is active. + bool isValueOn(Index offset) const { assert(offset < SIZE); return mBuffer.mData.isOn(offset); } + + /// Return @c false since leaf nodes never contain tiles. + static bool hasActiveTiles() { return false; } + + /// Set all voxels that lie outside the given axis-aligned box to the background. + void clip(const CoordBBox&, bool background); + + /// Set all voxels within an axis-aligned box to the specified value and active state. + void fill(const CoordBBox& bbox, bool value, bool dummy = false); + + /// Set the state of all voxels to the specified active state. + void fill(const bool& value, bool dummy = false); + + /// @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 + 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 + 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. + template + 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. + template + 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. + template + 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. + template + 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. + template + void setValueOffAndCache(const Coord& xyz, bool value, AccessorT&) + { + this->setValueOff(xyz, value); + } + + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// @note Used internally by ValueAccessor. + template + void modifyValueAndCache(const Coord& xyz, const ModifyOp& op, AccessorT&) + { + this->modifyValue(xyz, op); + } + + /// Apply a functor to the voxel at the given coordinates. + /// @note Used internally by ValueAccessor. + template + void modifyValueAndActiveStateAndCache(const Coord& xyz, const ModifyOp& op, AccessorT&) + { + this->modifyValueAndActiveState(xyz, op); + } + + /// @brief Set the active state of the voxel at the given coordinates + /// without changing its value. + /// @note Used internally by ValueAccessor. + template + 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. + template + 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. + template + 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 (mBuffer.mData.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 (mBuffer.mData.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 mBuffer.mData.isOff(); } + + /// @brief no-op since for this temaplte specialization voxel + /// values and states are indistinguishable. + void resetBackground(bool, bool) {} + + /// @brief Invert the bits of the voxels, i.e. states and values + void negate() { mBuffer.mData.toggle(); } + + template + void merge(const LeafNode& other, bool bg = false, bool otherBG = false); + template void merge(bool tileValue, bool tileActive=false); + + /// @brief No-op + /// @details This function exists only to enable template instantiation. + void voxelizeActiveTiles(bool = true) {} + + /// @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 + void topologyUnion(const LeafNode& 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 + void topologyIntersection(const LeafNode& 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 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, because it can deactivate all of this node's voxels, + /// consider subsequently calling prune. + template + void topologyDifference(const LeafNode& other, const bool&); + + template + void combine(const LeafNode& other, CombineOp& op); + template + void combine(bool, bool valueIsActive, CombineOp& op); + + template + void combine2(const LeafNode& other, const OtherType&, bool valueIsActive, CombineOp&); + template + void combine2(bool, const OtherNodeT& other, bool valueIsActive, CombineOp&); + template + void combine2(const LeafNode& b0, const OtherNodeT& 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 void visitActiveBBox(BBoxOp&) const; + + template void visit(VisitorOp&); + template void visit(VisitorOp&) const; + + template + void visit2Node(OtherLeafNodeType& other, VisitorOp&); + template + void visit2Node(OtherLeafNodeType& other, VisitorOp&) const; + template + void visit2(IterT& otherIter, VisitorOp&, bool otherIsLHS = false); + template + void visit2(IterT& otherIter, VisitorOp&, bool otherIsLHS = false) const; + + //@{ + /// This function exists only to enable template instantiation. + void prune(const ValueType& /*tolerance*/ = zeroVal()) {} + void addLeaf(LeafNode*) {} + template + void addLeafAndCache(LeafNode*, AccessorT&) {} + template + NodeT* stealNode(const Coord&, const ValueType&, bool) { return NULL; } + template + NodeT* probeNode(const Coord&) { return NULL; } + template + const NodeT* probeConstNode(const Coord&) const { return NULL; } + template void getNodes(ArrayT&) const {} + template void stealNodes(ArrayT&, const ValueType&, bool) {} + //@} + + void addTile(Index level, const Coord&, bool val, bool active); + void addTile(Index offset, bool val, bool active); + template + void addTileAndCache(Index level, const Coord&, bool val, bool active, AccessorT&); + + //@{ + /// @brief Return a pointer to this node. + LeafNode* touchLeaf(const Coord&) { return this; } + template + LeafNode* touchLeafAndCache(const Coord&, AccessorT&) { return this; } + LeafNode* probeLeaf(const Coord&) { return this; } + template + LeafNode* probeLeafAndCache(const Coord&, AccessorT&) { return this; } + template + NodeT* probeNodeAndCache(const Coord&, AccessorT&) + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (!(boost::is_same::value)) return NULL; + return reinterpret_cast(this); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + //@} + //@{ + /// @brief Return a @const pointer to this node. + const LeafNode* probeLeaf(const Coord&) const { return this; } + template + const LeafNode* probeLeafAndCache(const Coord&, AccessorT&) const { return this; } + const LeafNode* probeConstLeaf(const Coord&) const { return this; } + template + const LeafNode* probeConstLeafAndCache(const Coord&, AccessorT&) const { return this; } + template + const NodeT* probeConstNodeAndCache(const Coord&, AccessorT&) const + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (!(boost::is_same::value)) return NULL; + return reinterpret_cast(this); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + //@} + + // + // Iterators + // +protected: + typedef typename NodeMaskType::OnIterator MaskOnIter; + typedef typename NodeMaskType::OffIterator MaskOffIter; + typedef typename NodeMaskType::DenseIterator MaskDenseIter; + + template + struct ValueIter: + // Derives from SparseIteratorBase, but can also be used as a dense iterator, + // if MaskIterT is a dense mask iterator type. + public SparseIteratorBase, NodeT, ValueT> + { + typedef SparseIteratorBase 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); } + + // Note: modifyItem() can't be called on const iterators. + template + void modifyItem(Index n, const ModifyOp& op) const { this->parent().modifyValue(n, op); } + // Note: modifyValue() can't be called on const iterators. + template + void modifyValue(const ModifyOp& op) const { this->modifyItem(this->pos(), op); } + }; + + /// Leaf nodes have no children, so their child iterators have no get/set accessors. + template + struct ChildIter: + public SparseIteratorBase, NodeT, bool> + { + ChildIter() {} + ChildIter(const MaskIterT& iter, NodeT* parent): SparseIteratorBase< + MaskIterT, ChildIter, NodeT, bool>(iter, parent) {} + }; + + template + struct DenseIter: public DenseIteratorBase< + MaskDenseIter, DenseIter, NodeT, /*ChildT=*/void, ValueT> + { + typedef DenseIteratorBase 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 ValueOnIter; + typedef ValueIter ValueOnCIter; + typedef ValueIter ValueOffIter; + typedef ValueIter ValueOffCIter; + typedef ValueIter ValueAllIter; + typedef ValueIter ValueAllCIter; + typedef ChildIter ChildOnIter; + typedef ChildIter ChildOnCIter; + typedef ChildIter ChildOffIter; + typedef ChildIter ChildOffCIter; + typedef DenseIter ChildAllIter; + typedef DenseIter ChildAllCIter; + + ValueOnCIter cbeginValueOn() const { return ValueOnCIter(mBuffer.mData.beginOn(), this); } + ValueOnCIter beginValueOn() const { return ValueOnCIter(mBuffer.mData.beginOn(), this); } + ValueOnIter beginValueOn() { return ValueOnIter(mBuffer.mData.beginOn(), this); } + ValueOffCIter cbeginValueOff() const { return ValueOffCIter(mBuffer.mData.beginOff(), this); } + ValueOffCIter beginValueOff() const { return ValueOffCIter(mBuffer.mData.beginOff(), this); } + ValueOffIter beginValueOff() { return ValueOffIter(mBuffer.mData.beginOff(), this); } + ValueAllCIter cbeginValueAll() const { return ValueAllCIter(mBuffer.mData.beginDense(), this); } + ValueAllCIter beginValueAll() const { return ValueAllCIter(mBuffer.mData.beginDense(), this); } + ValueAllIter beginValueAll() { return ValueAllIter(mBuffer.mData.beginDense(), this); } + + ValueOnCIter cendValueOn() const { return ValueOnCIter(mBuffer.mData.endOn(), this); } + ValueOnCIter endValueOn() const { return ValueOnCIter(mBuffer.mData.endOn(), this); } + ValueOnIter endValueOn() { return ValueOnIter(mBuffer.mData.endOn(), this); } + ValueOffCIter cendValueOff() const { return ValueOffCIter(mBuffer.mData.endOff(), this); } + ValueOffCIter endValueOff() const { return ValueOffCIter(mBuffer.mData.endOff(), this); } + ValueOffIter endValueOff() { return ValueOffIter(mBuffer.mData.endOff(), this); } + ValueAllCIter cendValueAll() const { return ValueAllCIter(mBuffer.mData.endDense(), this); } + ValueAllCIter endValueAll() const { return ValueAllCIter(mBuffer.mData.endDense(), this); } + ValueAllIter endValueAll() { return ValueAllIter(mBuffer.mData.endDense(), this); } + + // Note that [c]beginChildOn() and [c]beginChildOff() actually return end iterators, + // because leaf nodes have no children. + ChildOnCIter cbeginChildOn() const { return ChildOnCIter(mBuffer.mData.endOn(), this); } + ChildOnCIter beginChildOn() const { return ChildOnCIter(mBuffer.mData.endOn(), this); } + ChildOnIter beginChildOn() { return ChildOnIter(mBuffer.mData.endOn(), this); } + ChildOffCIter cbeginChildOff() const { return ChildOffCIter(mBuffer.mData.endOff(), this); } + ChildOffCIter beginChildOff() const { return ChildOffCIter(mBuffer.mData.endOff(), this); } + ChildOffIter beginChildOff() { return ChildOffIter(mBuffer.mData.endOff(), this); } + ChildAllCIter cbeginChildAll() const { return ChildAllCIter(mBuffer.mData.beginDense(), this); } + ChildAllCIter beginChildAll() const { return ChildAllCIter(mBuffer.mData.beginDense(), this); } + ChildAllIter beginChildAll() { return ChildAllIter(mBuffer.mData.beginDense(), this); } + + ChildOnCIter cendChildOn() const { return ChildOnCIter(mBuffer.mData.endOn(), this); } + ChildOnCIter endChildOn() const { return ChildOnCIter(mBuffer.mData.endOn(), this); } + ChildOnIter endChildOn() { return ChildOnIter(mBuffer.mData.endOn(), this); } + ChildOffCIter cendChildOff() const { return ChildOffCIter(mBuffer.mData.endOff(), this); } + ChildOffCIter endChildOff() const { return ChildOffCIter(mBuffer.mData.endOff(), this); } + ChildOffIter endChildOff() { return ChildOffIter(mBuffer.mData.endOff(), this); } + ChildAllCIter cendChildAll() const { return ChildAllCIter(mBuffer.mData.endDense(), this); } + ChildAllCIter endChildAll() const { return ChildAllCIter(mBuffer.mData.endDense(), this); } + ChildAllIter endChildAll() { return ChildAllIter(mBuffer.mData.endDense(), this); } + + // + // Mask accessors + // + bool isValueMaskOn(Index n) const { return mBuffer.mData.isOn(n); } + bool isValueMaskOn() const { return mBuffer.mData.isOn(); } + bool isValueMaskOff(Index n) const { return mBuffer.mData.isOff(n); } + bool isValueMaskOff() const { return mBuffer.mData.isOff(); } + const NodeMaskType& getValueMask() const { return mBuffer.mData; } + const NodeMaskType& valueMask() const { return mBuffer.mData; } + NodeMaskType& getValueMask() { return mBuffer.mData; } + void setValueMask(const NodeMaskType& mask) { mBuffer.mData = 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) { mBuffer.mData.set(n, on); } + void setValueMaskOn(Index n) { mBuffer.mData.setOn(n); } + void setValueMaskOff(Index n) { mBuffer.mData.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 + static inline void doVisit(NodeT&, VisitorOp&); + + template + static inline void doVisit2Node(NodeT& self, OtherNodeT& other, VisitorOp&); + + template + static inline void doVisit2(NodeT& self, OtherChildAllIterT&, VisitorOp&, bool otherIsLHS); + + /// Bitmask representing the values AND state 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 friend class LeafNode; + + friend struct ValueIter; + friend struct ValueIter; + friend struct ValueIter; + friend struct ValueIter; + friend struct ValueIter; + friend struct ValueIter; + + //@{ + /// Allow iterators to call mask accessor methods (see below). + /// @todo Make mask accessors public? + friend class IteratorBase; + friend class IteratorBase; + friend class IteratorBase; + //@} + +}; // class LeafNode + + +/// @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 const bool LeafNode::sOn = true; +template const bool LeafNode::sOff = false; + + +//////////////////////////////////////// + + +template +inline +LeafNode::LeafNode() + : mOrigin(0, 0, 0) +{ +} + +template +inline +LeafNode::LeafNode(const Coord& xyz, bool value, bool) + : mBuffer(value) + , mOrigin(xyz & (~(DIM - 1))) +{ +} + + +#ifndef OPENVDB_2_ABI_COMPATIBLE +template +inline +LeafNode::LeafNode(PartialCreate, const Coord& xyz, bool value, bool) + : mBuffer(value) + , mOrigin(xyz & (~(DIM - 1))) +{ +} +#endif + + +template +inline +LeafNode::LeafNode(const LeafNode &other) + : mBuffer(other.mBuffer) + , mOrigin(other.mOrigin) +{ +} + + +// Copy-construct from a leaf node with the same configuration but a different ValueType. +template +template +inline +LeafNode::LeafNode(const LeafNode& other) + : mBuffer(other.valueMask()) + , mOrigin(other.origin()) +{ +} + + +template +template +inline +LeafNode::LeafNode(const LeafNode& other, + bool, TopologyCopy) + : mBuffer(other.valueMask())// value = active state + , mOrigin(other.origin()) +{ +} + + +template +template +inline +LeafNode::LeafNode(const LeafNode& other, TopologyCopy) + : mBuffer(other.valueMask())// value = active state + , mOrigin(other.origin()) +{ +} + + +template +template +inline +LeafNode::LeafNode(const LeafNode& other, + bool offValue, bool onValue, TopologyCopy) + : mBuffer(other.valueMask()) + , mOrigin(other.origin()) +{ + if (offValue==true) { + if (onValue==false) { + mBuffer.mData.toggle(); + } else { + mBuffer.mData.setOn(); + } + } +} + + +template +inline +LeafNode::~LeafNode() +{ +} + + +//////////////////////////////////////// + + +template +inline Index64 +LeafNode::memUsage() const +{ + return sizeof(mOrigin) + mBuffer.memUsage(); +} + + +template +inline void +LeafNode::evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels) const +{ + CoordBBox this_bbox = this->getNodeBoundingBox(); + if (bbox.isInside(this_bbox)) return;//this LeafNode is already enclosed in the bbox + if (ValueOnCIter iter = this->cbeginValueOn()) {//any active values? + if (visitVoxels) {//use voxel granularity? + this_bbox.reset(); + for(; iter; ++iter) this_bbox.expand(this->offsetToLocalCoord(iter.pos())); + this_bbox.translate(this->origin()); + } + bbox.expand(this_bbox); + } +} + + +template +template +inline bool +LeafNode::hasSameTopology(const LeafNode* other) const +{ + assert(other); + return (Log2Dim == OtherLog2Dim && mBuffer.mData == other->getValueMask()); +} + + +template +inline std::string +LeafNode::str() const +{ + std::ostringstream ostr; + ostr << "LeafNode @" << mOrigin << ": "; + for (Index32 n = 0; n < SIZE; ++n) ostr << (mBuffer.mData.isOn(n) ? '#' : '.'); + return ostr.str(); +} + + +//////////////////////////////////////// + + +template +inline Index +LeafNode::coordToOffset(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 +inline Coord +LeafNode::offsetToLocalCoord(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 +inline Coord +LeafNode::offsetToGlobalCoord(Index n) const +{ + return (this->offsetToLocalCoord(n) + this->origin()); +} + + +//////////////////////////////////////// + + +template +inline void +LeafNode::readTopology(std::istream& is, bool /*fromHalf*/) +{ + mBuffer.mData.load(is); +} + + +template +inline void +LeafNode::writeTopology(std::ostream& os, bool /*toHalf*/) const +{ + mBuffer.mData.save(os); +} + + +template +inline void +LeafNode::readBuffers(std::istream& is, const CoordBBox& clipBBox, bool fromHalf) +{ + // Boolean LeafNodes don't currently implement lazy loading. + // Instead, load the full buffer, then clip it. + + this->readBuffers(is, fromHalf); + + // Get this tree's background value. + bool background = false; + if (const void* bgPtr = io::getGridBackgroundValuePtr(is)) { + background = *static_cast(bgPtr); + } + this->clip(clipBBox, background); +} + + +template +inline void +LeafNode::readBuffers(std::istream& is, bool /*fromHalf*/) +{ + // Read in the value mask = buffer. + mBuffer.mData.load(is); + // Read in the origin. + is.read(reinterpret_cast(&mOrigin), sizeof(Coord::ValueType) * 3); +} + + +template +inline void +LeafNode::writeBuffers(std::ostream& os, bool /*toHalf*/) const +{ + // Write out the value mask = buffer. + mBuffer.mData.save(os); + // Write out the origin. + os.write(reinterpret_cast(&mOrigin), sizeof(Coord::ValueType) * 3); +} + + +//////////////////////////////////////// + + +template +inline bool +LeafNode::operator==(const LeafNode& other) const +{ + return mOrigin == other.mOrigin && mBuffer == other.mBuffer; +} + + +template +inline bool +LeafNode::operator!=(const LeafNode& other) const +{ + return !(this->operator==(other)); +} + + +//////////////////////////////////////// + + +template +inline bool +LeafNode::isConstant(bool& constValue, bool& state, bool) const +{ + state = mBuffer.mData.isOn(); + + if (!(state || mBuffer.mData.isOff())) return false; + + constValue = state; + return true; +} + + +//////////////////////////////////////// + + +template +inline void +LeafNode::addTile(Index /*level*/, const Coord& xyz, bool val, bool active) +{ + this->addTile(this->coordToOffset(xyz), val, active); +} + +template +inline void +LeafNode::addTile(Index offset, bool val, bool active) +{ + assert(offset < SIZE); + this->setValueOnly(offset, val); + this->setActiveState(offset, active); +} + +template +template +inline void +LeafNode::addTileAndCache(Index level, const Coord& xyz, + bool val, bool active, AccessorT&) +{ + this->addTile(level, xyz, val, active); +} + + +//////////////////////////////////////// + + +template +inline const bool& +LeafNode::getValue(const Coord& xyz) const +{ + // This *CANNOT* use operator ? because Visual C++ + if (mBuffer.mData.isOn(this->coordToOffset(xyz))) return sOn; else return sOff; +} + + +template +inline const bool& +LeafNode::getValue(Index offset) const +{ + assert(offset < SIZE); + // This *CANNOT* use operator ? for Windows + if (mBuffer.mData.isOn(offset)) return sOn; else return sOff; +} + + +template +inline bool +LeafNode::probeValue(const Coord& xyz, bool& val) const +{ + const Index offset = this->coordToOffset(xyz); + val = mBuffer.mData.isOn(offset); + return val; +} + + +template +inline void +LeafNode::setValueOn(const Coord& xyz, bool val) +{ + this->setValueOn(this->coordToOffset(xyz), val); +} + + +template +inline void +LeafNode::setValueOn(Index offset, bool val) +{ + assert(offset < SIZE); + mBuffer.mData.set(offset, val); +} + + +template +inline void +LeafNode::setValueOnly(const Coord& xyz, bool val) +{ + this->setValueOnly(this->coordToOffset(xyz), val); +} + + +template +inline void +LeafNode::setActiveState(const Coord& xyz, bool on) +{ + mBuffer.mData.set(this->coordToOffset(xyz), on); +} + + +template +inline void +LeafNode::setValueOff(const Coord& xyz, bool val) +{ + this->setValueOff(this->coordToOffset(xyz), val); +} + + +template +inline void +LeafNode::setValueOff(Index offset, bool val) +{ + assert(offset < SIZE); + mBuffer.mData.set(offset, val); +} + + +template +template +inline void +LeafNode::modifyValue(Index offset, const ModifyOp& op) +{ + bool val = mBuffer.mData.isOn(offset); + op(val); + mBuffer.mData.set(offset, val); +} + + +template +template +inline void +LeafNode::modifyValue(const Coord& xyz, const ModifyOp& op) +{ + this->modifyValue(this->coordToOffset(xyz), op); +} + + +template +template +inline void +LeafNode::modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) +{ + const Index offset = this->coordToOffset(xyz); + bool val = mBuffer.mData.isOn(offset), state = val; + op(val, state); + mBuffer.mData.set(offset, val); +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::merge(const LeafNode& other, bool /*bg*/, bool /*otherBG*/) +{ + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (Policy == MERGE_NODES) return; + mBuffer.mData |= other.mBuffer.mData; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + +template +template +inline void +LeafNode::merge(bool tileValue, bool) +{ + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (Policy != MERGE_ACTIVE_STATES_AND_NODES) return; + if (tileValue) mBuffer.mData.setOn(); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::topologyUnion(const LeafNode& other) +{ + mBuffer.mData |= other.valueMask(); +} + + +template +template +inline void +LeafNode::topologyIntersection(const LeafNode& other, + const bool&) +{ + mBuffer.mData &= other.valueMask(); +} + + +template +template +inline void +LeafNode::topologyDifference(const LeafNode& other, + const bool&) +{ + mBuffer.mData &= !other.valueMask(); +} + + +//////////////////////////////////////// + + +template +inline void +LeafNode::clip(const CoordBBox& clipBBox, bool background) +{ + CoordBBox nodeBBox = this->getNodeBoundingBox(); + if (!clipBBox.hasOverlap(nodeBBox)) { + // This node lies completely outside the clipping region. Fill it with background tiles. + this->fill(nodeBBox, background, /*active=*/false); + } else if (clipBBox.isInside(nodeBBox)) { + // This node lies completely inside the clipping region. Leave it intact. + return; + } + + // This node isn't completely contained inside the clipping region. + // Set any voxels that lie outside the region to the background value. + + // Construct a boolean mask that is on inside the clipping region and off outside it. + NodeMaskType mask; + nodeBBox.intersect(clipBBox); + Coord xyz; + int &x = xyz.x(), &y = xyz.y(), &z = xyz.z(); + for (x = nodeBBox.min().x(); x <= nodeBBox.max().x(); ++x) { + for (y = nodeBBox.min().y(); y <= nodeBBox.max().y(); ++y) { + for (z = nodeBBox.min().z(); z <= nodeBBox.max().z(); ++z) { + mask.setOn(static_cast(this->coordToOffset(xyz))); + } + } + } + + // Set voxels that lie in the inactive region of the mask (i.e., outside + // the clipping region) to the background value. + for (MaskOffIter maskIter = mask.beginOff(); maskIter; ++maskIter) { + this->setValueOff(maskIter.pos(), background); + } +} + + +//////////////////////////////////////// + + +template +inline void +LeafNode::fill(const CoordBBox& bbox, bool value, bool) +{ + 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.mData.set(offset, value); + } + } + } +} + +template +inline void +LeafNode::fill(const bool& value, bool) +{ + mBuffer.fill(value); +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::copyToDense(const CoordBBox& bbox, DenseT& dense) const +{ + typedef typename DenseT::ValueType DenseValueType; + + const size_t xStride = dense.xStride(), yStride = dense.yStride(), zStride = dense.zStride(); + const Coord& min = dense.bbox().min(); + DenseValueType* t0 = dense.data() + zStride * (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) { + DenseValueType* 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) { + DenseValueType* 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 += zStride) { + *t2 = DenseValueType(mBuffer.mData.isOn(n2++)); + } + } + } +} + + +template +template +inline void +LeafNode::copyFromDense(const CoordBBox& bbox, const DenseT& dense, + bool background, bool tolerance) +{ + typedef typename DenseT::ValueType DenseValueType; + struct Local { + inline static bool toBool(const DenseValueType& v) { return !math::isZero(v); } + }; + + const size_t xStride = dense.xStride(), yStride = dense.yStride(), zStride = dense.zStride(); + const Coord& min = dense.bbox().min(); + const DenseValueType* s0 = dense.data() + zStride * (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 DenseValueType* 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 DenseValueType* 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 += zStride) { + // Note: if tolerance is true (i.e., 1), then all boolean values compare equal. + if (tolerance || (background == Local::toBool(*s2))) { + mBuffer.mData.set(n2, background); + } else { + mBuffer.mData.set(n2, Local::toBool(*s2)); + } + } + } + } +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::combine(const LeafNode& other, CombineOp& op) +{ + CombineArgs 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(aVal) + .setBRef(bVal) + .setBIsActive(bVal) + .setResultRef(result)); + mBuffer.mData.set(i, result); + } +} + + +template +template +inline void +LeafNode::combine(bool value, bool valueIsActive, CombineOp& op) +{ + CombineArgs 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(aVal) + .setResultRef(result)); + mBuffer.mData.set(i, result); + } +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::combine2(const LeafNode& other, const OtherType& value, + bool valueIsActive, CombineOp& op) +{ + CombineArgs 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(aVal) + .setResultRef(result)); + mBuffer.mData.set(i, result); + } +} + + +template +template +inline void +LeafNode::combine2(bool value, const OtherNodeT& other, + bool valueIsActive, CombineOp& op) +{ + CombineArgs 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(bVal) + .setResultRef(result)); + mBuffer.mData.set(i, result); + } +} + + +template +template +inline void +LeafNode::combine2(const LeafNode& b0, const OtherNodeT& b1, CombineOp& op) +{ + CombineArgs args; + for (Index i = 0; i < SIZE; ++i) { + bool result = false, b0Val = b0.mBuffer.mData.isOn(i), b1Val = b1.mBuffer.mData.isOn(i); + op(args.setARef(b0Val) + .setAIsActive(b0Val) + .setBRef(b1Val) + .setBIsActive(b1Val) + .setResultRef(result)); + mBuffer.mData.set(i, result); + } +} + + +//////////////////////////////////////// + +template +template +inline void +LeafNode::visitActiveBBox(BBoxOp& op) const +{ + if (op.template descent()) { + for (ValueOnCIter i=this->cbeginValueOn(); i; ++i) { +#ifdef _MSC_VER + op.operator()(CoordBBox::createCube(i.getCoord(), 1)); +#else + op.template operator()(CoordBBox::createCube(i.getCoord(), 1)); +#endif + } + } else { +#ifdef _MSC_VER + op.operator()(this->getNodeBoundingBox()); +#else + op.template operator()(this->getNodeBoundingBox()); +#endif + } +} + + +template +template +inline void +LeafNode::visit(VisitorOp& op) +{ + doVisit(*this, op); +} + + +template +template +inline void +LeafNode::visit(VisitorOp& op) const +{ + doVisit(*this, op); +} + + +template +template +inline void +LeafNode::doVisit(NodeT& self, VisitorOp& op) +{ + for (ChildAllIterT iter = self.beginChildAll(); iter; ++iter) { + op(iter); + } +} + + +//////////////////////////////////////// + + +template +template +inline void +LeafNode::visit2Node(OtherLeafNodeType& other, VisitorOp& op) +{ + doVisit2Node(*this, other, op); +} + + +template +template +inline void +LeafNode::visit2Node(OtherLeafNodeType& other, VisitorOp& op) const +{ + doVisit2Node(*this, other, op); +} + + +template +template< + typename NodeT, + typename OtherNodeT, + typename VisitorOp, + typename ChildAllIterT, + typename OtherChildAllIterT> +inline void +LeafNode::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 +template +inline void +LeafNode::visit2(IterT& otherIter, VisitorOp& op, bool otherIsLHS) +{ + doVisit2(*this, otherIter, op, otherIsLHS); +} + + +template +template +inline void +LeafNode::visit2(IterT& otherIter, VisitorOp& op, bool otherIsLHS) const +{ + doVisit2(*this, otherIter, op, otherIsLHS); +} + + +template +template< + typename NodeT, + typename VisitorOp, + typename ChildAllIterT, + typename OtherChildAllIterT> +inline void +LeafNode::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_LEAF_NODE_MASK_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tree/NodeManager.h b/nuparu/include/openvdb/include/openvdb/tree/NodeManager.h new file mode 100644 index 00000000..08cb0683 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tree/NodeManager.h @@ -0,0 +1,1128 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 NodeManager.h +/// +/// @author Ken Museth +/// +/// @brief NodeManager produces linear arrays of all tree nodes +/// allowing for efficient threading and bottom-up processing. +/// +/// @note A NodeManager can be constructed from a Tree or LeafManager. +/// The latter is slightly more efficient since the cached leaf nodes will be reused. + +#ifndef OPENVDB_TREE_NODEMANAGER_HAS_BEEN_INCLUDED +#define OPENVDB_TREE_NODEMANAGER_HAS_BEEN_INCLUDED + +#include +#include +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tree { + +// Produce linear arrays of all tree nodes, to facilitate efficient threading +// and bottom-up processing. +template +class NodeManager; + + +//////////////////////////////////////// + + +/// @brief This class caches tree nodes of a specific type in a linear array. +/// +/// @note It is for internal use and should rarely be used directly. +template +class NodeList +{ +public: + typedef NodeT* value_type; + typedef std::deque ListT; + + NodeList() {} + + void push_back(NodeT* node) { mList.push_back(node); } + + NodeT& operator()(size_t n) const { assert(nsize();} + + class Iterator + { + public: + Iterator(const NodeRange& range, size_t pos): mRange(range), mPos(pos) + { + assert(this->isValid()); + } + Iterator& operator=(const Iterator& other) + { + mRange = other.mRange; mPos = other.mPos; return *this; + } + /// Advance to the next node. + Iterator& operator++() { ++mPos; return *this; } + /// Return a reference to the node to which this iterator is pointing. + NodeT& operator*() const { return mRange.mNodeList(mPos); } + /// Return a pointer to the node to which this iterator is pointing. + NodeT* operator->() const { return &(this->operator*()); } + /// Return the index into the list of the current 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) || (&mRange != &other.mRange); + } + bool operator==(const Iterator& other) const { return !(*this != other); } + const NodeRange& nodeRange() const { return mRange; } + + private: + const NodeRange& mRange; + size_t mPos; + };// NodeList::NodeRange::Iterator + + Iterator begin() const {return Iterator(*this, mBegin);} + + Iterator end() const {return Iterator(*this, mEnd);} + + private: + size_t mEnd, mBegin, mGrainSize; + const NodeList& mNodeList; + + static size_t doSplit(NodeRange& r) + { + assert(r.is_divisible()); + size_t middle = r.mBegin + (r.mEnd - r.mBegin) / 2u; + r.mEnd = middle; + return middle; + } + };// NodeList::NodeRange + + /// Return a TBB-compatible NodeRange. + NodeRange nodeRange(size_t grainsize = 1) const + { + return NodeRange(0, this->nodeCount(), *this, grainsize); + } + + template + void foreach(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + NodeTransformer transform(op); + transform.run(this->nodeRange(grainSize), threaded); + } + + template + void reduce(NodeOp& op, bool threaded = true, size_t grainSize=1) + { + NodeReducer transform(op); + transform.run(this->nodeRange(grainSize), threaded); + } + +private: + + // Private struct of NodeList that performs parallel_for + template + struct NodeTransformer + { + NodeTransformer(const NodeOp& nodeOp) : mNodeOp(nodeOp) + { + } + void run(const NodeRange& range, bool threaded = true) + { + threaded ? tbb::parallel_for(range, *this) : (*this)(range); + } + void operator()(const NodeRange& range) const + { + for (typename NodeRange::Iterator it = range.begin(); it; ++it) mNodeOp(*it); + } + const NodeOp mNodeOp; + };// NodeList::NodeTransformer + + // Private struct of NodeList that performs parallel_reduce + template + struct NodeReducer + { + NodeReducer(NodeOp& nodeOp) : mNodeOp(&nodeOp), mOwnsOp(false) + { + } + NodeReducer(const NodeReducer& other, tbb::split) : + mNodeOp(new NodeOp(*(other.mNodeOp), tbb::split())), mOwnsOp(true) + { + } + ~NodeReducer() { if (mOwnsOp) delete mNodeOp; } + void run(const NodeRange& range, bool threaded = true) + { + threaded ? tbb::parallel_reduce(range, *this) : (*this)(range); + } + void operator()(const NodeRange& range) + { + NodeOp &op = *mNodeOp; + for (typename NodeRange::Iterator it = range.begin(); it; ++it) op(*it); + } + void join(const NodeReducer& other) + { + mNodeOp->join(*(other.mNodeOp)); + } + NodeOp *mNodeOp; + const bool mOwnsOp; + };// NodeList::NodeReducer + + +protected: + ListT mList; +};// NodeList + + +///////////////////////////////////////////// + + +/// @brief This class is a link in a chain that each caches tree nodes +/// of a specific type in a linear array. +/// +/// @note It is for internal use and should rarely be used directly. +template +class NodeManagerLink +{ +public: + NodeManagerLink() {} + + virtual ~NodeManagerLink() {} + + void clear() { mList.clear(); mNext.clear(); } + + template + void init(ParentT& parent, TreeOrLeafManagerT& tree) + { + parent.getNodes(mList); + for (size_t i=0, n=mList.nodeCount(); i + void rebuild(ParentT& parent) + { + mList.clear(); + parent.getNodes(mList); + for (size_t i=0, n=mList.nodeCount(); i + void foreachBottomUp(const NodeOp& op, bool threaded, size_t grainSize) + { + mNext.foreachBottomUp(op, threaded, grainSize); + mList.foreach(op, threaded, grainSize); + } + + template + void foreachTopDown(const NodeOp& op, bool threaded, size_t grainSize) + { + mList.foreach(op, threaded, grainSize); + mNext.foreachTopDown(op, threaded, grainSize); + } + + template + OPENVDB_DEPRECATED void processBottomUp(const NodeOp& op, bool threaded, size_t grainSize) + { + this->foreachBottomUp(op, threaded, grainSize); + } + + template + OPENVDB_DEPRECATED void processTopDown(const NodeOp& op, bool threaded, size_t grainSize) + { + this->foreachTopDown(op, threaded, grainSize); + } + + template + void reduceBottomUp(NodeOp& op, bool threaded, size_t grainSize) + { + mNext.reduceBottomUp(op, threaded, grainSize); + mList.reduce(op, threaded, grainSize); + } + + template + void reduceTopDown(NodeOp& op, bool threaded, size_t grainSize) + { + mList.reduce(op, threaded, grainSize); + mNext.reduceTopDown(op, threaded, grainSize); + } + +protected: + NodeList mList; + NodeManagerLink mNext; +};// NodeManagerLink class + + +//////////////////////////////////////// + + +/// @brief Specialization that terminates the chain of cached tree nodes +/// +/// @note It is for internal use and should rarely be used directly. +template +class NodeManagerLink +{ +public: + NodeManagerLink() {} + + virtual ~NodeManagerLink() {} + + /// @brief Clear all the cached tree nodes + void clear() { mList.clear(); } + + template + void rebuild(ParentT& parent) { mList.clear(); parent.getNodes(mList); } + + Index64 nodeCount() const { return mList.nodeCount(); } + + Index64 nodeCount(Index) const { return mList.nodeCount(); } + + template + void foreachBottomUp(const NodeOp& op, bool threaded, size_t grainSize) + { + mList.foreach(op, threaded, grainSize); + } + + template + void foreachTopDown(const NodeOp& op, bool threaded, size_t grainSize) + { + mList.foreach(op, threaded, grainSize); + } + + template + OPENVDB_DEPRECATED void processBottomUp(const NodeOp& op, bool threaded, size_t grainSize) + { + this->foreachBottomUp(op, threaded, grainSize); + } + + template + OPENVDB_DEPRECATED void processTopDown(const NodeOp& op, bool threaded, size_t grainSize) + { + this->foreachTopDown(op, threaded, grainSize); + } + + template + void reduceBottomUp(NodeOp& op, bool threaded, size_t grainSize) + { + mList.reduce(op, threaded, grainSize); + } + + template + void reduceTopDown(NodeOp& op, bool threaded, size_t grainSize) + { + mList.reduce(op, threaded, grainSize); + } + + template + void init(ParentT& parent, TreeOrLeafManagerT& tree) + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (TreeOrLeafManagerT::DEPTH == 2 && NodeT::LEVEL == 0) { + tree.getNodes(mList); + } else { + parent.getNodes(mList); + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } +protected: + NodeList mList; +};// NodeManagerLink class + + +//////////////////////////////////////// + + +/// @brief To facilitate threading over the nodes of a tree, cache +/// node pointers in linear arrays, one for each level of the tree. +/// +/// @details This implementation works with trees of any depth, but +/// optimized specializations are provided for the most typical tree depths. +template +class NodeManager +{ +public: + static const Index LEVELS = _LEVELS; + BOOST_STATIC_ASSERT(LEVELS > 0);//special implementation below + typedef typename TreeOrLeafManagerT::RootNodeType RootNodeType; + BOOST_STATIC_ASSERT(RootNodeType::LEVEL >= LEVELS); + + NodeManager(TreeOrLeafManagerT& tree) : mRoot(tree.root()) { mChain.init(mRoot, tree); } + + virtual ~NodeManager() {} + + /// @brief Clear all the cached tree nodes + void clear() { mChain.clear(); } + + /// @brief Clear and recache all the tree nodes from the + /// tree. This is required if tree nodes have been added or removed. + void rebuild() { mChain.rebuild(mRoot); } + + /// @brief Return a reference to the root node. + const RootNodeType& root() const { return mRoot; } + + /// @brief Return the total number of cached nodes (excluding the root node) + Index64 nodeCount() const { return mChain.nodeCount(); } + + /// @brief Return the number of cached nodes at level @a i, where + /// 0 corresponds to the lowest level. + Index64 nodeCount(Index i) const { return mChain.nodeCount(i); } + + //@{ + /// @brief Threaded method that applies a user-supplied functor + /// to all the nodes in the tree. + /// + /// @param op user-supplied functor, see examples for interface details. + /// @param threaded optional toggle to disable threading, on by default. + /// @param grainSize optional parameter to specify the grainsize + /// for threading, one by default. + /// + /// @warning The functor object is deep-copied to create TBB tasks. + /// + /// @par Example: + /// @code + /// // Functor to offset all the inactive values of a tree. Note + /// // this implementation also illustrates how different + /// // computation can be applied to the different node types. + /// template + /// struct OffsetOp + /// { + /// typedef typename TreeT::ValueType ValueT; + /// typedef typename TreeT::RootNodeType RootT; + /// typedef typename TreeT::LeafNodeType LeafT; + /// OffsetOp(const ValueT& v) : mOffset(v) {} + /// + /// // Processes the root node. Required by the NodeManager + /// void operator()(RootT& root) const + /// { + /// for (typename RootT::ValueOffIter i = root.beginValueOff(); i; ++i) *i += mOffset; + /// } + /// // Processes the leaf nodes. Required by the NodeManager + /// void operator()(LeafT& leaf) const + /// { + /// for (typename LeafT::ValueOffIter i = leaf.beginValueOff(); i; ++i) *i += mOffset; + /// } + /// // Processes the internal nodes. Required by the NodeManager + /// template + /// void operator()(NodeT& node) const + /// { + /// for (typename NodeT::ValueOffIter i = node.beginValueOff(); i; ++i) *i += mOffset; + /// } + /// private: + /// const ValueT mOffset; + /// }; + /// + /// // usage: + /// OffsetOp op(3.0f); + /// tree::NodeManager nodes(tree); + /// nodes.foreachBottomUp(op); + /// + /// // or if a LeafManager already exists + /// typedef tree::LeafManager T; + /// OffsetOp op(3.0f); + /// tree::NodeManager nodes(leafManager); + /// nodes.foreachBottomUp(op); + /// + /// @endcode + template + void foreachBottomUp(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + mChain.foreachBottomUp(op, threaded, grainSize); + op(mRoot); + } + template + void foreachTopDown(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + op(mRoot); + mChain.foreachTopDown(op, threaded, grainSize); + } + template + OPENVDB_DEPRECATED void processBottomUp(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + this->foreachBottomUp(op, threaded, grainSize); + } + template + OPENVDB_DEPRECATED void processTopDown(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + this->foreachTopDown(op, threaded, grainSize); + } + //@} + + //@{ + /// @brief Threaded method that processes nodes with a user supplied functor + /// + /// @param op user-supplied functor, see examples for interface details. + /// @param threaded optional toggle to disable threading, on by default. + /// @param grainSize optional parameter to specify the grainsize + /// for threading, one by default. + /// + /// @warning The functor object is deep-copied to create TBB tasks. + /// + /// @par Example: + /// @code + /// // Functor to count nodes in a tree + /// template + /// struct NodeCountOp + /// { + /// NodeCountOp() : nodeCount(TreeType::DEPTH, 0), totalCount(0) + /// { + /// } + /// NodeCountOp(const NodeCountOp& other, tbb::split) : + /// nodeCount(TreeType::DEPTH, 0), totalCount(0) + /// { + /// } + /// void join(const NodeCountOp& other) + /// { + /// for (size_t i = 0; i < nodeCount.size(); ++i) { + /// nodeCount[i] += other.nodeCount[i]; + /// } + /// totalCount += other.totalCount; + /// } + /// // do nothing for the root node + /// void operator()(const typename TreeT::RootNodeType& node) + /// { + /// } + /// // count the internal and leaf nodes + /// template + /// void operator()(const NodeT& node) + /// { + /// ++(nodeCount[NodeT::LEVEL]); + /// ++totalCount; + /// } + /// std::vector nodeCount; + /// openvdb::Index64 totalCount; + /// }; + /// + /// // usage: + /// NodeCountOp op; + /// tree::NodeManager nodes(tree); + /// nodes.reduceBottomUp(op); + /// + /// // or if a LeafManager already exists + /// NodeCountOp op; + /// typedef tree::LeafManager T; + /// T leafManager(tree); + /// tree::NodeManager nodes(leafManager); + /// nodes.reduceBottomUp(op); + /// + /// @endcode + template + void reduceBottomUp(NodeOp& op, bool threaded = true, size_t grainSize=1) + { + mChain.reduceBottomUp(op, threaded, grainSize); + op(mRoot); + } + + template + void reduceTopDown(NodeOp& op, bool threaded = true, size_t grainSize=1) + { + op(mRoot); + mChain.reduceTopDown(op, threaded, grainSize); + } + //@} + +protected: + RootNodeType& mRoot; + NodeManagerLink mChain; + +private: + NodeManager(const NodeManager&) {}//disallow copy-construction +};// NodeManager class + + +//////////////////////////////////////////// + + +/// Template specialization of the NodeManager with no caching of nodes +template +class NodeManager +{ +public: + typedef typename TreeOrLeafManagerT::RootNodeType RootNodeType; + static const Index LEVELS = 0; + + NodeManager(TreeOrLeafManagerT& tree) : mRoot(tree.root()) {} + + virtual ~NodeManager() {} + + /// @brief Clear all the cached tree nodes + void clear() {} + + /// @brief Clear and recache all the tree nodes from the + /// tree. This is required if tree nodes have been added or removed. + void rebuild() {} + + /// @brief Return a reference to the root node. + const RootNodeType& root() const { return mRoot; } + + /// @brief Return the total number of cached nodes (excluding the root node) + Index64 nodeCount() const { return 0; } + + Index64 nodeCount(Index) const { return 0; } + + template + OPENVDB_DEPRECATED void processBottomUp(const NodeOp& op, bool, size_t) { op(mRoot); } + + template + OPENVDB_DEPRECATED void processTopDown(const NodeOp& op, bool, size_t) { op(mRoot); } + + template + void foreachBottomUp(const NodeOp& op, bool, size_t) { op(mRoot); } + + template + void foreachTopDown(const NodeOp& op, bool, size_t) { op(mRoot); } + + template + void reduceBottomUp(NodeOp& op, bool, size_t) { op(mRoot); } + + template + void reduceTopDown(NodeOp& op, bool, size_t) { op(mRoot); } + +protected: + RootNodeType& mRoot; + +private: + NodeManager(const NodeManager&) {} // disallow copy-construction +}; // NodeManager<0> + + +//////////////////////////////////////////// + + +/// Template specialization of the NodeManager with one level of nodes +template +class NodeManager +{ +public: + typedef typename TreeOrLeafManagerT::RootNodeType RootNodeType; + BOOST_STATIC_ASSERT(RootNodeType::LEVEL > 0); + static const Index LEVELS = 1; + + NodeManager(TreeOrLeafManagerT& tree) : mRoot(tree.root()) + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (TreeOrLeafManagerT::DEPTH == 2 && NodeT0::LEVEL == 0) { + tree.getNodes(mList0); + } else { + mRoot.getNodes(mList0); + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + + virtual ~NodeManager() {} + + /// @brief Clear all the cached tree nodes + void clear() { mList0.clear(); } + + /// @brief Clear and recache all the tree nodes from the + /// tree. This is required if tree nodes have been added or removed. + void rebuild() { mList0.clear(); mRoot.getNodes(mList0); } + + /// @brief Return a reference to the root node. + const RootNodeType& root() const { return mRoot; } + + /// @brief Return the total number of cached nodes (excluding the root node) + Index64 nodeCount() const { return mList0.nodeCount(); } + + /// @brief Return the number of cached nodes at level @a i, where + /// 0 corresponds to the lowest level. + Index64 nodeCount(Index i) const { return i==0 ? mList0.nodeCount() : 0; } + + template + void foreachBottomUp(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + mList0.foreach(op, threaded, grainSize); + op(mRoot); + } + + template + void foreachTopDown(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + op(mRoot); + mList0.foreach(op, threaded, grainSize); + } + template + OPENVDB_DEPRECATED void processBottomUp(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + this->foreachBottomUp(op, threaded, grainSize); + } + + template + OPENVDB_DEPRECATED void processTopDown(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + this->foreachTopDown(op, threaded, grainSize); + } + + template + void reduceBottomUp(NodeOp& op, bool threaded = true, size_t grainSize=1) + { + mList0.reduce(op, threaded, grainSize); + op(mRoot); + } + + template + void reduceTopDown(NodeOp& op, bool threaded = true, size_t grainSize=1) + { + op(mRoot); + mList0.reduce(op, threaded, grainSize); + } + +protected: + typedef RootNodeType NodeT1; + typedef typename NodeT1::ChildNodeType NodeT0; + typedef NodeList ListT0; + + NodeT1& mRoot; + ListT0 mList0; + +private: + NodeManager(const NodeManager&) {} // disallow copy-construction +}; // NodeManager<1> + + +//////////////////////////////////////////// + + +/// Template specialization of the NodeManager with two levels of nodes +template +class NodeManager +{ +public: + typedef typename TreeOrLeafManagerT::RootNodeType RootNodeType; + BOOST_STATIC_ASSERT(RootNodeType::LEVEL > 1); + static const Index LEVELS = 2; + + NodeManager(TreeOrLeafManagerT& tree) : mRoot(tree.root()) + { + mRoot.getNodes(mList1); + + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (TreeOrLeafManagerT::DEPTH == 2 && NodeT0::LEVEL == 0) { + tree.getNodes(mList0); + } else { + for (size_t i=0, n=mList1.nodeCount(); iclear(); + mRoot.getNodes(mList1); + for (size_t i=0, n=mList1.nodeCount(); i + void foreachBottomUp(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + mList0.foreach(op, threaded, grainSize); + mList1.foreach(op, threaded, grainSize); + op(mRoot); + } + + template + void foreachTopDown(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + op(mRoot); + mList1.foreach(op, threaded, grainSize); + mList0.foreach(op, threaded, grainSize); + } + + template + OPENVDB_DEPRECATED void processBottomUp(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + this->foreachBottomUp(op, threaded, grainSize); + } + + template + OPENVDB_DEPRECATED void processTopDown(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + this->foreachTopDown(op, threaded, grainSize); + } + + template + void reduceBottomUp(NodeOp& op, bool threaded = true, size_t grainSize=1) + { + mList0.reduce(op, threaded, grainSize); + mList1.reduce(op, threaded, grainSize); + op(mRoot); + } + + template + void reduceTopDown(NodeOp& op, bool threaded = true, size_t grainSize=1) + { + op(mRoot); + mList1.reduce(op, threaded, grainSize); + mList0.reduce(op, threaded, grainSize); + } + +protected: + typedef RootNodeType NodeT2; + typedef typename NodeT2::ChildNodeType NodeT1;//upper level + typedef typename NodeT1::ChildNodeType NodeT0;//lower level + + typedef NodeList ListT1;//upper level + typedef NodeList ListT0;//lower level + + NodeT2& mRoot; + ListT1 mList1; + ListT0 mList0; + +private: + NodeManager(const NodeManager&) {} // disallow copy-construction +}; // NodeManager<2> + + +//////////////////////////////////////////// + + +/// Template specialization of the NodeManager with three levels of nodes +template +class NodeManager +{ +public: + typedef typename TreeOrLeafManagerT::RootNodeType RootNodeType; + BOOST_STATIC_ASSERT(RootNodeType::LEVEL > 2); + static const Index LEVELS = 3; + + NodeManager(TreeOrLeafManagerT& tree) : mRoot(tree.root()) + { + mRoot.getNodes(mList2); + for (size_t i=0, n=mList2.nodeCount(); iclear(); + mRoot.getNodes(mList2); + for (size_t i=0, n=mList2.nodeCount(); i + void foreachBottomUp(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + mList0.foreach(op, threaded, grainSize); + mList1.foreach(op, threaded, grainSize); + mList2.foreach(op, threaded, grainSize); + op(mRoot); + } + + template + void foreachTopDown(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + op(mRoot); + mList2.foreach(op, threaded, grainSize); + mList1.foreach(op, threaded, grainSize); + mList0.foreach(op, threaded, grainSize); + } + + template + OPENVDB_DEPRECATED void processBottomUp(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + this->foreachBottomUp(op, threaded, grainSize); + } + + template + OPENVDB_DEPRECATED void processTopDown(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + this->foreachTopDown(op, threaded, grainSize); + } + + template + void reduceBottomUp(NodeOp& op, bool threaded = true, size_t grainSize=1) + { + mList0.reduce(op, threaded, grainSize); + mList1.reduce(op, threaded, grainSize); + mList2.reduce(op, threaded, grainSize); + op(mRoot); + } + + template + void reduceTopDown(NodeOp& op, bool threaded = true, size_t grainSize=1) + { + op(mRoot); + mList2.reduce(op, threaded, grainSize); + mList1.reduce(op, threaded, grainSize); + mList0.reduce(op, threaded, grainSize); + } + +protected: + typedef RootNodeType NodeT3; + typedef typename NodeT3::ChildNodeType NodeT2;//upper level + typedef typename NodeT2::ChildNodeType NodeT1;//mid level + typedef typename NodeT1::ChildNodeType NodeT0;//lower level + + typedef NodeList ListT2;//upper level of internal nodes + typedef NodeList ListT1;//lower level of internal nodes + typedef NodeList ListT0;//lower level of internal nodes or leafs + + NodeT3& mRoot; + ListT2 mList2; + ListT1 mList1; + ListT0 mList0; + +private: + NodeManager(const NodeManager&) {} // disallow copy-construction +}; // NodeManager<3> + + +//////////////////////////////////////////// + + +/// Template specialization of the NodeManager with four levels of nodes +template +class NodeManager +{ +public: + typedef typename TreeOrLeafManagerT::RootNodeType RootNodeType; + BOOST_STATIC_ASSERT(RootNodeType::LEVEL > 3); + static const Index LEVELS = 4; + + NodeManager(TreeOrLeafManagerT& tree) : mRoot(tree.root()) + { + mRoot.getNodes(mList3); + for (size_t i=0, n=mList3.nodeCount(); iclear(); + mRoot.getNodes(mList3); + for (size_t i=0, n=mList3.nodeCount(); i + void foreachBottomUp(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + mList0.foreach(op, threaded, grainSize); + mList1.foreach(op, threaded, grainSize); + mList2.foreach(op, threaded, grainSize); + mList3.foreach(op, threaded, grainSize); + op(mRoot); + } + + template + void foreachTopDown(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + op(mRoot); + mList3.foreach(op, threaded, grainSize); + mList2.foreach(op, threaded, grainSize); + mList1.foreach(op, threaded, grainSize); + mList0.foreach(op, threaded, grainSize); + } + + template + OPENVDB_DEPRECATED void processBottomUp(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + this->foreachBottomUp(op, threaded, grainSize); + } + + template + OPENVDB_DEPRECATED void processTopDown(const NodeOp& op, bool threaded = true, size_t grainSize=1) + { + this->foreachTopDown(op, threaded, grainSize); + } + + template + void reduceBottomUp(NodeOp& op, bool threaded = true, size_t grainSize=1) + { + mList0.reduce(op, threaded, grainSize); + mList1.reduce(op, threaded, grainSize); + mList2.reduce(op, threaded, grainSize); + mList3.reduce(op, threaded, grainSize); + op(mRoot); + } + + template + void reduceTopDown(NodeOp& op, bool threaded = true, size_t grainSize=1) + { + op(mRoot); + mList3.reduce(op, threaded, grainSize); + mList2.reduce(op, threaded, grainSize); + mList1.reduce(op, threaded, grainSize); + mList0.reduce(op, threaded, grainSize); + } + +protected: + typedef RootNodeType NodeT4; + typedef typename NodeT4::ChildNodeType NodeT3;//upper level + typedef typename NodeT3::ChildNodeType NodeT2;//upper mid level + typedef typename NodeT2::ChildNodeType NodeT1;//lower mid level + typedef typename NodeT1::ChildNodeType NodeT0;//lower level + + typedef NodeList ListT3;//upper level of internal nodes + typedef NodeList ListT2;//upper mid level of internal nodes + typedef NodeList ListT1;//lower mid level of internal nodes + typedef NodeList ListT0;//lower level of internal nodes or leafs + + NodeT4& mRoot; + ListT3 mList3; + ListT2 mList2; + ListT1 mList1; + ListT0 mList0; + +private: + NodeManager(const NodeManager&) {} // disallow copy-construction +}; // NodeManager<4> + +} // namespace tree +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TREE_NODEMANAGER_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tree/NodeUnion.h b/nuparu/include/openvdb/include/openvdb/tree/NodeUnion.h new file mode 100644 index 00000000..c2e4097d --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tree/NodeUnion.h @@ -0,0 +1,137 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 value for a particular element of a root +/// or internal node. For space efficiency, the child pointer and the value +/// are unioned, since the two are never in use simultaneously. +/// Template specializations of NodeUnion allow for 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 +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tree { + +// Internal implementation of a union of a child node pointer and a value +template class NodeUnionImpl; + + +// Partial specialization for values of non-class types +// (int, float, pointer, etc.) that stores elements by value +template +class NodeUnionImpl +{ +private: + union { ChildT* child; ValueT value; } mUnion; + +public: + NodeUnionImpl() { setChild(NULL); } + + ChildT* getChild() const { return mUnion.child; } + const ValueT& getValue() const { return mUnion.value; } + ValueT& getValue() { return mUnion.value; } + void setChild(ChildT* child) { mUnion.child = child; } + void setValue(const ValueT& val) { mUnion.value = val; } +}; + + +// Partial specialization for values of class types (std::string, +// math::Vec, etc.) that stores elements by pointer +template +class NodeUnionImpl +{ +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 + { 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; } + ValueT& getValue() { return *mUnion.value; } + void setValue(const ValueT& val) + { + /// @todo To minimize storage across nodes, intern and reuse + /// common values, using, e.g., boost::flyweight. + if (!mHasChild) delete mUnion.value; + mUnion.value = new ValueT(val); + mHasChild = false; + } +}; + + +template +struct NodeUnion: public NodeUnionImpl< + boost::is_class::value, ValueT, ChildT> +{ + NodeUnion() {} +}; + +} // namespace tree +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TREE_NODEUNION_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tree/RootNode.h b/nuparu/include/openvdb/include/openvdb/tree/RootNode.h new file mode 100644 index 00000000..c8f28594 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tree/RootNode.h @@ -0,0 +1,3411 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include //for boost::mpl::vector +#include +#include +#include +#include +#include +#include +#include // for truncateRealToHalf() +#include // for isZero(), isExactlyEqual(), etc. +#include +#include // for backward compatibility only (see readTopology()) +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tree { + +// Forward declarations +template struct NodeChain; +template struct SameRootConfig; +template struct RootNodeCopyHelper; +template struct RootNodeCombineHelper; + + +template +class RootNode +{ +public: + typedef ChildType ChildNodeType; + typedef typename ChildType::LeafNodeType LeafNodeType; + typedef typename ChildType::ValueType ValueType; + typedef typename ChildType::BuildType BuildType; + + static const Index LEVEL = 1 + ChildType::LEVEL; // level 0 = leaf + + /// NodeChainType is a list of this tree's node types, from LeafNodeType to RootNode. + typedef typename NodeChain::Type NodeChainType; + BOOST_STATIC_ASSERT(boost::mpl::size::value == LEVEL + 1); + + /// @brief ValueConverter::Type is the type of a RootNode having the same + /// child hierarchy as this node but a different value type, T. + template + struct ValueConverter { + typedef RootNode::Type> Type; + }; + + /// @brief SameConfiguration::value is @c true if and only if + /// OtherNodeType is the type of a RootNode whose ChildNodeType has the same + /// configuration as this node's ChildNodeType. + template + struct SameConfiguration { + static const bool value = SameRootConfig::value; + }; + + + /// 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 Construct a new tree that reproduces the topology and active states + /// of a tree of a different ValueType but the same configuration (levels, + /// node dimensions and branching factors). Cast the other tree's values to + /// this tree's ValueType. + /// @throw TypeError if the other tree's configuration doesn't match this tree's + /// or if this tree's ValueType is not constructible from the other tree's ValueType. + template + explicit RootNode(const RootNode& other) { *this = other; } + + /// @brief Construct a new tree that reproduces the topology and active states of + /// another tree (which may have a different ValueType), but not the other tree's values. + /// @details All tiles and voxels that are active in the other tree are set to + /// @a foreground in the new tree, and all inactive tiles and voxels are set to @a background. + /// @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 + /// @throw TypeError if the other tree's configuration doesn't match this tree's. + template + RootNode(const RootNode& other, + const ValueType& background, const ValueType& foreground, TopologyCopy); + + /// @brief Construct a new tree that reproduces the topology and active states of + /// another tree (which may have a different ValueType), but not the other tree's values. + /// All tiles and voxels in the new tree are set to @a background regardless of + /// their active states in the other tree. + /// @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 + /// @note This copy constructor is generally faster than the one that takes both + /// a foreground and a background value. Its main application is in multithreaded + /// operations where the topology of the output tree exactly matches the input tree. + /// @throw TypeError if the other tree's configuration doesn't match this tree's. + template + RootNode(const RootNode& other, const ValueType& background, TopologyCopy); + + /// @brief Copy a root node of the same type as this node. + RootNode& operator=(const RootNode& other); + /// @brief Copy a root node of the same tree configuration as this node + /// but a different ValueType. + /// @throw TypeError if the other tree's configuration doesn't match this tree's. + /// @note This node's ValueType must be constructible from the other node's ValueType. + /// For example, a root node with values of type float can be assigned to a root node + /// with values of type Vec3s, because a Vec3s can be constructed from a float. + /// But a Vec3s root node cannot be assigned to a float root node. + template + RootNode& operator=(const RootNode& other); + + ~RootNode() { this->clear(); } + +private: + struct Tile { + Tile(): value(zeroVal()), 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 MapType; + typedef typename MapType::iterator MapIter; + typedef typename MapType::const_iterator MapCIter; + + typedef std::set 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 + 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 + class ChildIter: public BaseIter + { + public: + typedef BaseIter BaseT; + typedef RootNodeT NodeType; + typedef NodeType ValueType; + typedef ChildNodeT ChildNodeType; + typedef typename boost::remove_const::type NonConstNodeType; + typedef typename boost::remove_const::type NonConstValueType; + typedef typename boost::remove_const::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 + class ValueIter: public BaseIter + { + public: + typedef BaseIter BaseT; + typedef RootNodeT NodeType; + typedef ValueT ValueType; + typedef typename boost::remove_const::type NonConstNodeType; + typedef typename boost::remove_const::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; } + + template + void modifyValue(const ModifyOp& op) const + { + assert(isTile(mIter)); + op(getTile(mIter).value); + } + }; // ValueIter + + template + class DenseIter: public BaseIter + { + public: + typedef BaseIter BaseT; + typedef RootNodeT NodeType; + typedef ValueT ValueType; + typedef ChildNodeT ChildNodeType; + typedef typename boost::remove_const::type NonConstNodeType; + typedef typename boost::remove_const::type NonConstValueType; + typedef typename boost::remove_const::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 ChildOnIter; + typedef ChildIter ChildOnCIter; + typedef ValueIter ChildOffIter; + typedef ValueIter ChildOffCIter; + typedef DenseIter ChildAllIter; + typedef DenseIter ChildAllCIter; + + typedef ValueIter ValueOnIter; + typedef ValueIter ValueOnCIter; + typedef ValueIter ValueOffIter; + typedef ValueIter ValueOffCIter; + typedef ValueIter ValueAllIter; + typedef ValueIter 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. If visitVoxels is false LeafNodes will be approximated + /// as dense, i.e. with all voxels active. Else the individual + /// active voxels are visited to produce a tight bbox. + void evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels = true) 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. + /// + /// @param value The new background value + /// @param updateChildNodes If true the background values of the + /// child nodes is also updated. Else only the background value + /// stored in the RootNode itself is changed. + /// + /// @note Instead of setting @a updateChildNodes to true, consider + /// using tools::changeBackground or + /// tools::changeLevelSetBackground which are multi-threaded! + void setBackground(const ValueType& value, bool updateChildNodes); + + /// Return this node's background value. + const ValueType& background() 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(); + inline void clear(); + + /// 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& dims); + static Index getChildDim() { return ChildType::DIM; } + + /// Return the number of entries in this node's table. + Index getTableSize() const { return static_cast(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 + bool hasSameTopology(const RootNode& other) const; + + /// Return @c false if the other node's dimensions don't match this node's. + template + static bool hasSameConfiguration(const RootNode& other); + + /// Return @c true if values of the other node's ValueType can be converted + /// to values of this node's ValueType. + template + static bool hasCompatibleValueType(const RootNode& other); + + Index32 leafCount() const; + Index32 nonLeafCount() const; + Index64 onVoxelCount() const; + Index64 offVoxelCount() const; + Index64 onLeafVoxelCount() const; + Index64 offLeafVoxelCount() const; + Index64 onTileCount() 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); + /// Set the value of the voxel at the given coordinates but don't change its active state. + void setValueOnly(const Coord& xyz, const ValueType& value); + /// Set the value of the voxel at the given coordinates and mark the voxel as active. + void setValueOn(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); + /// Set the value of the voxel at the given coordinates and mark the voxel as inactive. + void setValueOff(const Coord& xyz, const ValueType& value); + + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + template + void modifyValue(const Coord& xyz, const ModifyOp& op); + /// Apply a functor to the voxel at the given coordinates. + template + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op); + + /// @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. Defaults to true. + /// @param sparse if false, active tiles are voxelized, i.e. only active voxels + /// are generated from the fill operation. Defaults to true. + void fill(const CoordBBox& bbox, const ValueType& value, bool active = true, bool sparse = 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 + 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); + void readBuffers(std::istream&, const CoordBBox&, bool fromHalf = false); + + + // + // Voxel access + // + /// 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 + 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 + 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 + void setValueAndCache(const Coord& xyz, const ValueType& value, AccessorT&); + + /// Set the value of the voxel at the given coordinates without changing 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 + void setValueOnlyAndCache(const Coord& xyz, const ValueType& value, AccessorT&); + + /// Apply a functor to the value of the voxel at the given coordinates + /// 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 + void modifyValueAndCache(const Coord& xyz, const ModifyOp& op, AccessorT&); + + /// Apply a functor to the voxel at the given coordinates. + /// 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 + void modifyValueAndActiveStateAndCache(const Coord& xyz, const ModifyOp& op, 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 + 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 + void setActiveStateAndCache(const Coord& xyz, bool on, AccessorT&); + + /// Return, in @a value, 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. + /// @return @c true if the voxel at the given coordinates is active + /// @note Used internally by ValueAccessor. + template + 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 + int getValueDepthAndCache(const Coord& xyz, AccessorT&) const; + + /// Set all voxels that lie outside the given axis-aligned box to the background. + void clip(const CoordBBox&); + + /// @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. + /// + /// @note Consider instead using tools::prune which is multi-threaded! + void prune(const ValueType& tolerance = zeroVal()); + + /// @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); + + /// @brief Same as addLeaf() but, if necessary, update the given accessor with pointers + /// to the nodes along the path from the root node to the node containing the coordinate. + template + 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 + NodeT* stealNode(const Coord& xyz, const ValueType& value, bool state); + + /// @brief Add a tile containing voxel (x, y, z) at the root level, + /// deleting the existing branch if necessary. + void addTile(const Coord& xyz, const ValueType& value, bool state); + + /// @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). + void addTile(Index level, const Coord& xyz, const ValueType& value, bool state); + + /// @brief Same as addTile() but, if necessary, update the given accessor with pointers + /// to the nodes along the path from the root node to the node containing the coordinate. + template + void addTileAndCache(Index level, const Coord& xyz, const ValueType&, bool state, AccessorT&); + + /// @brief Return a pointer to the leaf node that contains voxel (x, y, z). + /// If no such node exists, create one that preserves 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() but, if necessary, update the given accessor with pointers + /// to the nodes along the path from the root node to the node containing the coordinate. + template + LeafNodeType* touchLeafAndCache(const Coord& xyz, AccessorT& acc); + + //@{ + /// @brief Return a pointer to the node that contains voxel (x, y, z). + /// If no such node exists, return NULL. + template + NodeT* probeNode(const Coord& xyz); + template + const NodeT* probeConstNode(const Coord& xyz) const; + //@} + + //@{ + /// @brief Same as probeNode() but, if necessary, update the given accessor with pointers + /// to the nodes along the path from the root node to the node containing the coordinate. + template + NodeT* probeNodeAndCache(const Coord& xyz, AccessorT& acc); + template + const NodeT* probeConstNodeAndCache(const Coord& xyz, AccessorT& acc) const; + //@} + + //@{ + /// @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); + const LeafNodeType* probeConstLeaf(const Coord& xyz) const; + const LeafNodeType* probeLeaf(const Coord& xyz) const; + //@} + + //@{ + /// @brief Same as probeLeaf() but, if necessary, update the given accessor with pointers + /// to the nodes along the path from the root node to the node containing the coordinate. + template + LeafNodeType* probeLeafAndCache(const Coord& xyz, AccessorT& acc); + template + const LeafNodeType* probeConstLeafAndCache(const Coord& xyz, AccessorT& acc) const; + template + const LeafNodeType* probeLeafAndCache(const Coord& xyz, AccessorT& acc) const; + //@} + + + // + // Aux methods + // + + //@{ + /// @brief Adds all nodes of a certain type to a container with the following API: + /// @code + /// struct ArrayT { + /// typedef value_type;// defines the type of nodes to be added to the array + /// void push_back(value_type nodePtr);// method that add nodes to the array + /// }; + /// @endcode + /// @details An example of a wrapper around a c-style array is: + /// @code + /// struct MyArray { + /// typedef LeafType* value_type; + /// value_type* ptr; + /// MyArray(value_type* array) : ptr(array) {} + /// void push_back(value_type leaf) { *ptr++ = leaf; } + ///}; + /// @endcode + /// @details An example that constructs a list of pointer to all leaf nodes is: + /// @code + /// std::vector array;//most std contains have the required API + /// array.reserve(tree.leafCount());//this is a fast preallocation. + /// tree.getNodes(array); + /// @endcode + template void getNodes(ArrayT& array); + template void getNodes(ArrayT& array) const; + //@} + + //@{ + /// @brief Steals all nodes of a certain type from the tree and + /// adds them to a container with the following API: + /// @code + /// struct ArrayT { + /// typedef value_type;// defines the type of nodes to be added to the array + /// void push_back(value_type nodePtr);// method that add nodes to the array + /// }; + /// @endcode + /// @details An example of a wrapper around a c-style array is: + /// @code + /// struct MyArray { + /// typedef LeafType* value_type; + /// value_type* ptr; + /// MyArray(value_type* array) : ptr(array) {} + /// void push_back(value_type leaf) { *ptr++ = leaf; } + ///}; + /// @endcode + /// @details An example that constructs a list of pointer to all leaf nodes is: + /// @code + /// std::vector array;//most std contains have the required API + /// array.reserve(tree.leafCount());//this is a fast preallocation. + /// tree.stealNodes(array); + /// @endcode + template + void stealNodes(ArrayT& array, const ValueType& value, bool state); + template + void stealNodes(ArrayT& array) { this->stealNodes(array, mBackground, false); } + //@} + + /// @brief Densify active tiles, i.e., replace them with leaf-level active voxels. + /// + /// @param threaded if true, this operation is multi-threaded (over the internal nodes). + /// + /// @warning This method can explode the tree's memory footprint, especially if it + /// contains active tiles at the upper levels, e.g. root level! + void voxelizeActiveTiles(bool threaded = true); + + /// @brief Efficiently merge another tree into this tree using one of several schemes. + /// @details This operation is primarily intended to combine trees that are mostly + /// non-overlapping (for example, intermediate trees from computations that are + /// parallelized across disjoint regions of space). + /// @note This operation is not guaranteed to produce an optimally sparse tree. + /// Follow merge() with prune() for optimal sparseness. + /// @warning This operation always empties the other tree. + template void merge(RootNode& other); + + /// @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 + void topologyUnion(const RootNode& other); + + /// @brief Intersects 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 only if the corresponding + /// value was already active AND if it is active in the other tree. Also, a + /// resulting value maps to a voxel if the corresponding value + /// already mapped to an active voxel in either of the two grids + /// and it maps to an active tile or voxel in the other grid. + /// + /// @note This operation can delete branches in this grid if they + /// overlap with inactive tiles in the other grid. Likewise active + /// voxels can be turned into inactive voxels resulting in leaf + /// nodes with no active values. Thus, it is recommended to + /// subsequently call prune. + template + void topologyIntersection(const RootNode& other); + + /// @brief Difference this tree's set of active values with the active values + /// of the other tree, whose @c ValueType may be different. So a + /// resulting voxel will be active only if the original voxel is + /// active in this tree and inactive in the other tree. + /// + /// @note This operation can delete branches in this grid if they + /// overlap with active tiles in the other grid. Likewise active + /// voxels can be turned into inactive voxels resulting in leaf + /// nodes with no active values. Thus, it is recommended to + /// subsequently call prune. + template + void topologyDifference(const RootNode& other); + + template + void combine(RootNode& other, CombineOp&, bool prune = false); + + template + void combine2(const RootNode& other0, const OtherRootNode& other1, + CombineOp& op, bool prune = false); + + /// @brief Call 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 guaranteed to be non-overlapping. + template void visitActiveBBox(BBoxOp&) const; + + template void visit(VisitorOp&); + template void visit(VisitorOp&) const; + + template + void visit2(OtherRootNodeType& other, VisitorOp&); + template + void visit2(OtherRootNodeType& other, VisitorOp&) const; + +private: + /// During topology-only construction, access is needed + /// to protected/private members of other template instances. + template friend class RootNode; + + template friend struct RootNodeCopyHelper; + template friend struct RootNodeCombineHelper; + + /// Currently no-op, but can be used to define empty and delete keys for mTable + void initTable() {} + //@{ + /// @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); + + /// @brief Verify that the tree rooted at @a other has the same configuration + /// (levels, branching factors and node dimensions) as this tree, but allow + /// their ValueTypes to differ. + /// @throw TypeError if the other tree's configuration doesn't match this tree's. + template + static void enforceSameConfiguration(const RootNode& other); + + /// @brief Verify that @a other has values of a type that can be converted + /// to this node's ValueType. + /// @details For example, values of type float are compatible with values of type Vec3s, + /// because a Vec3s can be constructed from a float. But the reverse is not true. + /// @throw TypeError if the other node's ValueType is not convertible into this node's. + template + static void enforceCompatibleValueTypes(const RootNode& other); + + template + void doCombine2(const RootNode&, const OtherRootNode&, CombineOp&, bool prune); + + template + static inline void doVisit(RootNodeT&, VisitorOp&); + + template + static inline void doVisit2(RootNodeT&, OtherRootNodeT&, VisitorOp&); + + + MapType mTable; + ValueType mBackground; +}; // end of RootNode class + + +//////////////////////////////////////// + + +/// @brief NodeChain::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. +/// @details For example, if RootNodeType is +/// @code +/// RootNode > > +/// @endcode +/// then NodeChain::Type is +/// @code +/// boost::mpl::vector< +/// LeafNode, +/// InternalNode, +/// InternalNode >, +/// RootNode > > > +/// @endcode +/// +/// @note Use the following to get the Nth node type, where N=0 is the LeafNodeType: +/// @code +/// boost::mpl::at >::type +/// @endcode +template +struct NodeChain { + typedef typename NodeChain::Type SubtreeT; + typedef typename boost::mpl::push_back::type Type; +}; + +/// Specialization to terminate NodeChain +template +struct NodeChain { + typedef typename boost::mpl::vector::type Type; +}; + + +//////////////////////////////////////// + + +//@{ +/// Helper metafunction used to implement RootNode::SameConfiguration +/// (which, as an inner class, can't be independently specialized) +template +struct SameRootConfig { + static const bool value = false; +}; + +template +struct SameRootConfig > { + static const bool value = ChildT1::template SameConfiguration::value; +}; +//@} + + +//////////////////////////////////////// + + +template +inline +RootNode::RootNode(): mBackground(zeroVal()) +{ + this->initTable(); +} + + +template +inline +RootNode::RootNode(const ValueType& background): mBackground(background) +{ + this->initTable(); +} + + +template +template +inline +RootNode::RootNode(const RootNode& other, + const ValueType& backgd, const ValueType& foregd, TopologyCopy): + mBackground(backgd) +{ + typedef RootNode OtherRootT; + + 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 +template +inline +RootNode::RootNode(const RootNode& other, + const ValueType& backgd, TopologyCopy): + mBackground(backgd) +{ + typedef RootNode OtherRootT; + + 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()))); + } +} + + +//////////////////////////////////////// + + +// This helper class is a friend of RootNode and is needed so that assignment +// with value conversion can be specialized for compatible and incompatible +// pairs of RootNode types. +template +struct RootNodeCopyHelper +{ + static inline void copyWithValueConversion(RootT& self, const OtherRootT& other) + { + // If the two root nodes have different configurations or incompatible ValueTypes, + // throw an exception. + self.enforceSameConfiguration(other); + self.enforceCompatibleValueTypes(other); + // One of the above two tests should throw, so we should never get here: + std::ostringstream ostr; + ostr << "cannot convert a " << typeid(OtherRootT).name() + << " to a " << typeid(RootT).name(); + OPENVDB_THROW(TypeError, ostr.str()); + } +}; + +// Specialization for root nodes of compatible types +template +struct RootNodeCopyHelper +{ + static inline void copyWithValueConversion(RootT& self, const OtherRootT& other) + { + typedef typename RootT::ValueType ValueT; + typedef typename RootT::ChildNodeType ChildT; + typedef typename RootT::NodeStruct NodeStruct; + typedef typename RootT::Tile Tile; + typedef typename OtherRootT::ValueType OtherValueT; + typedef typename OtherRootT::MapCIter OtherMapCIter; + typedef typename OtherRootT::Tile OtherTile; + + struct Local { + /// @todo Consider using a value conversion functor passed as an argument instead. + static inline ValueT convertValue(const OtherValueT& val) { return ValueT(val); } + }; + + self.mBackground = Local::convertValue(other.mBackground); + + self.clear(); + self.initTable(); + + for (OtherMapCIter i = other.mTable.begin(), e = other.mTable.end(); i != e; ++i) { + if (other.isTile(i)) { + // Copy the other node's tile, but convert its value to this node's ValueType. + const OtherTile& otherTile = other.getTile(i); + self.mTable[i->first] = NodeStruct( + Tile(Local::convertValue(otherTile.value), otherTile.active)); + } else { + // Copy the other node's child, but convert its values to this node's ValueType. + self.mTable[i->first] = NodeStruct(*(new ChildT(other.getChild(i)))); + } + } + } +}; + + +// Overload for root nodes of the same type as this node +template +inline RootNode& +RootNode::operator=(const RootNode& other) +{ + if (&other != this) { + mBackground = other.mBackground; + + this->clear(); + 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; +} + +// Overload for root nodes of different types +template +template +inline RootNode& +RootNode::operator=(const RootNode& other) +{ + typedef RootNode OtherRootT; + typedef typename OtherRootT::ValueType OtherValueT; + static const bool compatible = (SameConfiguration::value + && CanConvertType::value); + RootNodeCopyHelper::copyWithValueConversion(*this, other); + return *this; +} + + +//////////////////////////////////////// + +template +inline void +RootNode::setBackground(const ValueType& background, bool updateChildNodes) +{ + if (math::isExactlyEqual(background, mBackground)) return; + + if (updateChildNodes) { + // 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, math::negative(mBackground))) { + tile.value = math::negative(background); + } + } + } + } + mBackground = background; +} + +template +inline bool +RootNode::isBackgroundTile(const Tile& tile) const +{ + return !tile.active && math::isApproxEqual(tile.value, mBackground); +} + +template +inline bool +RootNode::isBackgroundTile(const MapIter& iter) const +{ + return isTileOff(iter) && math::isApproxEqual(getTile(iter).value, mBackground); +} + +template +inline bool +RootNode::isBackgroundTile(const MapCIter& iter) const +{ + return isTileOff(iter) && math::isApproxEqual(getTile(iter).value, mBackground); +} + + +template +inline size_t +RootNode::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 +inline size_t +RootNode::eraseBackgroundTiles() +{ + std::set keysToErase; + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (this->isBackgroundTile(i)) keysToErase.insert(i->first); + } + for (std::set::iterator i = keysToErase.begin(), e = keysToErase.end(); i != e; ++i) { + mTable.erase(*i); + } + return keysToErase.size(); +} + + +//////////////////////////////////////// + + +template +inline void +RootNode::insertKeys(CoordSet& keys) const +{ + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + keys.insert(i->first); + } +} + + +template +inline typename RootNode::MapIter +RootNode::findOrAddCoord(const Coord& xyz) +{ + const Coord key = coordToKey(xyz); + std::pair result = mTable.insert( + typename MapType::value_type(key, NodeStruct(Tile(mBackground, /*active=*/false)))); + return result.first; +} + + +template +inline bool +RootNode::expand(const Coord& xyz) +{ + const Coord key = coordToKey(xyz); + std::pair 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 +inline void +RootNode::getNodeLog2Dims(std::vector& dims) +{ + dims.push_back(0); // magic number; RootNode has no Log2Dim + ChildT::getNodeLog2Dims(dims); +} + + +template +inline Coord +RootNode::getMinIndex() const +{ + return mTable.empty() ? Coord(0) : mTable.begin()->first; +} + +template +inline Coord +RootNode::getMaxIndex() const +{ + return mTable.empty() ? Coord(0) : mTable.rbegin()->first + Coord(ChildT::DIM - 1); +} + + +template +inline void +RootNode::getIndexRange(CoordBBox& bbox) const +{ + bbox.min() = this->getMinIndex(); + bbox.max() = this->getMaxIndex(); +} + + +//////////////////////////////////////// + + +template +template +inline bool +RootNode::hasSameTopology(const RootNode& other) const +{ + typedef RootNode 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 +template +inline bool +RootNode::hasSameConfiguration(const RootNode&) +{ + std::vector thisDims, otherDims; + RootNode::getNodeLog2Dims(thisDims); + RootNode::getNodeLog2Dims(otherDims); + return (thisDims == otherDims); +} + + +template +template +inline void +RootNode::enforceSameConfiguration(const RootNode&) +{ + std::vector thisDims, otherDims; + RootNode::getNodeLog2Dims(thisDims); + RootNode::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 +template +inline bool +RootNode::hasCompatibleValueType(const RootNode&) +{ + typedef typename OtherChildType::ValueType OtherValueType; + return CanConvertType::value; +} + + +template +template +inline void +RootNode::enforceCompatibleValueTypes(const RootNode&) +{ + typedef typename OtherChildType::ValueType OtherValueType; + if (!CanConvertType::value) { + std::ostringstream ostr; + ostr << "values of type " << typeNameAsString() + << " cannot be converted to type " << typeNameAsString(); + OPENVDB_THROW(TypeError, ostr.str()); + } +} + + +//////////////////////////////////////// + + +template +inline Index64 +RootNode::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 +inline void +RootNode::clear() +{ + for (MapIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + delete i->second.child; + } + mTable.clear(); +} + + +template +inline void +RootNode::evalActiveBoundingBox(CoordBBox& bbox, bool visitVoxels) const +{ + for (MapCIter iter=mTable.begin(); iter!=mTable.end(); ++iter) { + if (const ChildT *child = iter->second.child) { + child->evalActiveBoundingBox(bbox, visitVoxels); + } else if (isTileOn(iter)) { + bbox.expand(iter->first, ChildT::DIM); + } + } +} + + +template +inline Index +RootNode::getChildCount() const { + Index sum = 0; + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (isChild(i)) ++sum; + } + return sum; +} + + +template +inline Index +RootNode::getTileCount() const +{ + Index sum = 0; + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (isTile(i)) ++sum; + } + return sum; +} + + +template +inline Index +RootNode::getActiveTileCount() const +{ + Index sum = 0; + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (isTileOn(i)) ++sum; + } + return sum; +} + + +template +inline Index +RootNode::getInactiveTileCount() const +{ + Index sum = 0; + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (isTileOff(i)) ++sum; + } + return sum; +} + + +template +inline Index32 +RootNode::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 +inline Index32 +RootNode::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 +inline Index64 +RootNode::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 +inline Index64 +RootNode::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 +inline Index64 +RootNode::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 +inline Index64 +RootNode::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 +inline Index64 +RootNode::onTileCount() const +{ + Index64 sum = 0; + for (MapCIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (isChild(i)) { + sum += getChild(i).onTileCount(); + } else if (isTileOn(i)) { + sum += 1; + } + } + return sum; +} + +//////////////////////////////////////// + + +template +inline bool +RootNode::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 +inline bool +RootNode::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 +template +inline bool +RootNode::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 +inline const typename ChildT::ValueType& +RootNode::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 +template +inline const typename ChildT::ValueType& +RootNode::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 +inline int +RootNode::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 +template +inline int +RootNode::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 +inline void +RootNode::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 +inline void +RootNode::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 +template +inline void +RootNode::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 +inline void +RootNode::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 +template +inline void +RootNode::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 +inline void +RootNode::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 +template +inline void +RootNode::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 +inline void +RootNode::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 +template +inline void +RootNode::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 +template +inline void +RootNode::modifyValue(const Coord& xyz, const ModifyOp& op) +{ + 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 { + // Need to create a child if the tile is inactive, + // in order to activate voxel (x, y, z). + bool createChild = isTileOff(iter); + if (!createChild) { + // Need to create a child if applying the functor + // to the tile value produces a different value. + const ValueType& tileVal = getTile(iter).value; + ValueType modifiedVal = tileVal; + op(modifiedVal); + createChild = !math::isExactlyEqual(tileVal, modifiedVal); + } + if (createChild) { + child = new ChildT(xyz, getTile(iter).value, isTileOn(iter)); + setChild(iter, *child); + } + } + if (child) child->modifyValue(xyz, op); +} + +template +template +inline void +RootNode::modifyValueAndCache(const Coord& xyz, const ModifyOp& op, 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 { + // Need to create a child if the tile is inactive, + // in order to activate voxel (x, y, z). + bool createChild = isTileOff(iter); + if (!createChild) { + // Need to create a child if applying the functor + // to the tile value produces a different value. + const ValueType& tileVal = getTile(iter).value; + ValueType modifiedVal = tileVal; + op(modifiedVal); + createChild = !math::isExactlyEqual(tileVal, modifiedVal); + } + if (createChild) { + child = new ChildT(xyz, getTile(iter).value, isTileOn(iter)); + setChild(iter, *child); + } + } + if (child) { + acc.insert(xyz, child); + child->modifyValueAndCache(xyz, op, acc); + } +} + + +template +template +inline void +RootNode::modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) +{ + 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 { + const Tile& tile = getTile(iter); + bool modifiedState = tile.active; + ValueType modifiedVal = tile.value; + op(modifiedVal, modifiedState); + // Need to create a child if applying the functor to the tile + // produces a different value or active state. + if (modifiedState != tile.active || !math::isExactlyEqual(modifiedVal, tile.value)) { + child = new ChildT(xyz, tile.value, tile.active); + setChild(iter, *child); + } + } + if (child) child->modifyValueAndActiveState(xyz, op); +} + +template +template +inline void +RootNode::modifyValueAndActiveStateAndCache( + const Coord& xyz, const ModifyOp& op, 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 { + const Tile& tile = getTile(iter); + bool modifiedState = tile.active; + ValueType modifiedVal = tile.value; + op(modifiedVal, modifiedState); + // Need to create a child if applying the functor to the tile + // produces a different value or active state. + if (modifiedState != tile.active || !math::isExactlyEqual(modifiedVal, tile.value)) { + child = new ChildT(xyz, tile.value, tile.active); + setChild(iter, *child); + } + } + if (child) { + acc.insert(xyz, child); + child->modifyValueAndActiveStateAndCache(xyz, op, acc); + } +} + + +template +inline bool +RootNode::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 +template +inline bool +RootNode::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 +inline void +RootNode::fill(const CoordBBox& bbox, const ValueType& value, bool active, bool sparse) +{ + 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)); + } + } + } + } + if (!sparse) this->voxelizeActiveTiles(/*multi-threaded=*/true); +} + +template +template +inline void +RootNode::copyToDense(const CoordBBox& bbox, DenseT& dense) const +{ + typedef typename DenseT::ValueType DenseValueType; + + const size_t xStride = dense.xStride(), yStride = dense.yStride(), zStride = dense.zStride(); + 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); + DenseValueType* a0 = dense.data() + zStride*sub.min()[2]; + for (Int32 x=sub.min()[0], ex=sub.max()[0]+1; x +inline bool +RootNode::writeTopology(std::ostream& os, bool toHalf) const +{ + if (!toHalf) { + os.write(reinterpret_cast(&mBackground), sizeof(ValueType)); + } else { + ValueType truncatedVal = io::truncateRealToHalf(mBackground); + os.write(reinterpret_cast(&truncatedVal), sizeof(ValueType)); + } + io::setGridBackgroundValuePtr(os, &mBackground); + + const Index numTiles = this->getTileCount(), numChildren = this->getChildCount(); + os.write(reinterpret_cast(&numTiles), sizeof(Index)); + os.write(reinterpret_cast(&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(i->first.asPointer()), 3 * sizeof(Int32)); + os.write(reinterpret_cast(&getTile(i).value), sizeof(ValueType)); + os.write(reinterpret_cast(&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(i->first.asPointer()), 3 * sizeof(Int32)); + getChild(i).writeTopology(os, toHalf); + } + + return true; // not empty +} + + +template +inline bool +RootNode::readTopology(std::istream& is, bool fromHalf) +{ + // Delete the existing tree. + this->clear(); + + 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(&mBackground), sizeof(ValueType)); + ValueType inside; + is.read(reinterpret_cast(&inside), sizeof(ValueType)); + + io::setGridBackgroundValuePtr(is, &mBackground); + + // Read the index range. + Coord rangeMin, rangeMax; + is.read(reinterpret_cast(rangeMin.asPointer()), 3 * sizeof(Int32)); + is.read(reinterpret_cast(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. +#ifdef OPENVDB_2_ABI_COMPATIBLE + ChildT* child = new ChildT(origin, mBackground); +#else + ChildT* child = new ChildT(PartialCreate(), origin, mBackground); +#endif + 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(&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(&mBackground), sizeof(ValueType)); + io::setGridBackgroundValuePtr(is, &mBackground); + + Index numTiles = 0, numChildren = 0; + is.read(reinterpret_cast(&numTiles), sizeof(Index)); + is.read(reinterpret_cast(&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(vec), 3 * sizeof(Int32)); + is.read(reinterpret_cast(&value), sizeof(ValueType)); + is.read(reinterpret_cast(&active), sizeof(bool)); + mTable[Coord(vec)] = NodeStruct(Tile(value, active)); + } + + // Read child nodes. + for (Index n = 0; n < numChildren; ++n) { + is.read(reinterpret_cast(vec), 3 * sizeof(Int32)); + Coord origin(vec); +#ifdef OPENVDB_2_ABI_COMPATIBLE + ChildT* child = new ChildT(origin, mBackground); +#else + ChildT* child = new ChildT(PartialCreate(), origin, mBackground); +#endif + child->readTopology(is, fromHalf); + mTable[Coord(vec)] = NodeStruct(*child); + } + + return true; // not empty +} + + +template +inline void +RootNode::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 +inline void +RootNode::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 +inline void +RootNode::readBuffers(std::istream& is, const CoordBBox& clipBBox, bool fromHalf) +{ + const Tile bgTile(mBackground, /*active=*/false); + + for (MapIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (isChild(i)) { + // Stream in and clip the branch rooted at this child. + // (We can't skip over children that lie outside the clipping region, + // because buffers are serialized in depth-first order and need to be + // unserialized in the same order.) + ChildT& child = getChild(i); + child.readBuffers(is, clipBBox, fromHalf); + } + } + // Clip root-level tiles and prune children that were clipped. + this->clip(clipBBox); +} + + +//////////////////////////////////////// + + +template +inline void +RootNode::clip(const CoordBBox& clipBBox) +{ + const Tile bgTile(mBackground, /*active=*/false); + + // Iterate over a copy of this node's table so that we can modify the original. + // (Copying the table copies child node pointers, not the nodes themselves.) + MapType copyOfTable(mTable); + for (MapIter i = copyOfTable.begin(), e = copyOfTable.end(); i != e; ++i) { + const Coord& xyz = i->first; // tile or child origin + CoordBBox tileBBox(xyz, xyz.offsetBy(ChildT::DIM - 1)); // tile or child bounds + if (!clipBBox.hasOverlap(tileBBox)) { + // This table entry lies completely outside the clipping region. Delete it. + setTile(this->findCoord(xyz), bgTile); // delete any existing child node first + mTable.erase(xyz); + } else if (!clipBBox.isInside(tileBBox)) { + // This table entry does not lie completely inside the clipping region + // and must be clipped. + if (isChild(i)) { + getChild(i).clip(clipBBox, mBackground); + } else { + // Replace this tile with a background tile, then fill the clip region + // with the tile's original value. (This might create a child branch.) + tileBBox.intersect(clipBBox); + const Tile& origTile = getTile(i); + setTile(this->findCoord(xyz), bgTile); + this->fill(tileBBox, origTile.value, origTile.active); + } + } else { + // This table entry lies completely inside the clipping region. Leave it intact. + } + } + this->prune(); // also erases root-level background tiles +} + + +//////////////////////////////////////// + + +template +inline void +RootNode::prune(const ValueType& tolerance) +{ + bool state = false; + ValueType value = zeroVal(); + for (MapIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + if (this->isTile(i)) continue; + this->getChild(i).prune(tolerance); + if (this->getChild(i).isConstant(value, state, tolerance)) { + this->setTile(i, Tile(value, state)); + } + } + this->eraseBackgroundTiles(); +} + + +//////////////////////////////////////// + + +template +template +inline NodeT* +RootNode::stealNode(const Coord& xyz, const ValueType& value, bool state) +{ + if ((NodeT::LEVEL == ChildT::LEVEL && !(boost::is_same::value)) || + NodeT::LEVEL > ChildT::LEVEL) return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + MapIter iter = this->findCoord(xyz); + if (iter == mTable.end() || isTile(iter)) return NULL; + return (boost::is_same::value) + ? reinterpret_cast(&stealChild(iter, Tile(value, state))) + : getChild(iter).template stealNode(xyz, value, state); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +//////////////////////////////////////// + + +template +inline void +RootNode::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(leaf); + } + mTable[this->coordToKey(xyz)] = NodeStruct(*child); + } else if (isChild(iter)) { + if (ChildT::LEVEL>0) { + child = &getChild(iter); + } else { + child = reinterpret_cast(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(leaf); + } + setChild(iter, *child); + } + child->addLeaf(leaf); +} + + +template +template +inline void +RootNode::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(leaf); + } + mTable[this->coordToKey(xyz)] = NodeStruct(*child); + } else if (isChild(iter)) { + if (ChildT::LEVEL>0) { + child = &getChild(iter); + } else { + child = reinterpret_cast(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(leaf); + } + setChild(iter, *child); + } + acc.insert(xyz, child); + child->addLeafAndCache(leaf, acc); +} + +template +inline void +RootNode::addTile(const Coord& xyz, const ValueType& value, bool state) +{ + MapIter iter = this->findCoord(xyz); + if (iter == mTable.end()) {//background + mTable[this->coordToKey(xyz)] = NodeStruct(Tile(value, state)); + } else {//child or tile + setTile(iter, Tile(value, state));//this also deletes the existing child node + } +} + +template +inline void +RootNode::addTile(Index level, const Coord& xyz, + const ValueType& value, bool state) +{ + if (LEVEL >= level) { + 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 +template +inline void +RootNode::addTileAndCache(Index level, const Coord& xyz, const ValueType& value, + bool state, AccessorT& acc) +{ + if (LEVEL >= level) { + 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 +inline typename ChildT::LeafNodeType* +RootNode::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 +template +inline typename ChildT::LeafNodeType* +RootNode::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 +template +inline NodeT* +RootNode::probeNode(const Coord& xyz) +{ + if ((NodeT::LEVEL == ChildT::LEVEL && !(boost::is_same::value)) || + NodeT::LEVEL > ChildT::LEVEL) return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + MapIter iter = this->findCoord(xyz); + if (iter == mTable.end() || isTile(iter)) return NULL; + ChildT* child = &getChild(iter); + return (boost::is_same::value) + ? reinterpret_cast(child) + : child->template probeNode(xyz); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +template +template +inline const NodeT* +RootNode::probeConstNode(const Coord& xyz) const +{ + if ((NodeT::LEVEL == ChildT::LEVEL && !(boost::is_same::value)) || + NodeT::LEVEL > ChildT::LEVEL) return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + MapCIter iter = this->findCoord(xyz); + if (iter == mTable.end() || isTile(iter)) return NULL; + const ChildT* child = &getChild(iter); + return (boost::is_same::value) + ? reinterpret_cast(child) + : child->template probeConstNode(xyz); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +template +inline typename ChildT::LeafNodeType* +RootNode::probeLeaf(const Coord& xyz) +{ + return this->template probeNode(xyz); +} + + +template +inline const typename ChildT::LeafNodeType* +RootNode::probeConstLeaf(const Coord& xyz) const +{ + return this->template probeConstNode(xyz); +} + + +template +template +inline typename ChildT::LeafNodeType* +RootNode::probeLeafAndCache(const Coord& xyz, AccessorT& acc) +{ + return this->template probeNodeAndCache(xyz, acc); +} + + +template +template +inline const typename ChildT::LeafNodeType* +RootNode::probeConstLeafAndCache(const Coord& xyz, AccessorT& acc) const +{ + return this->template probeConstNodeAndCache(xyz, acc); +} + + +template +template +inline const typename ChildT::LeafNodeType* +RootNode::probeLeafAndCache(const Coord& xyz, AccessorT& acc) const +{ + return this->probeConstLeafAndCache(xyz, acc); +} + + +template +template +inline NodeT* +RootNode::probeNodeAndCache(const Coord& xyz, AccessorT& acc) +{ + if ((NodeT::LEVEL == ChildT::LEVEL && !(boost::is_same::value)) || + NodeT::LEVEL > ChildT::LEVEL) return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + MapIter iter = this->findCoord(xyz); + if (iter == mTable.end() || isTile(iter)) return NULL; + ChildT* child = &getChild(iter); + acc.insert(xyz, child); + return (boost::is_same::value) + ? reinterpret_cast(child) + : child->template probeNodeAndCache(xyz, acc); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +template +template +inline const NodeT* +RootNode::probeConstNodeAndCache(const Coord& xyz, AccessorT& acc) const +{ + if ((NodeT::LEVEL == ChildT::LEVEL && !(boost::is_same::value)) || + NodeT::LEVEL > ChildT::LEVEL) return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + MapCIter iter = this->findCoord(xyz); + if (iter == mTable.end() || isTile(iter)) return NULL; + const ChildT* child = &getChild(iter); + acc.insert(xyz, child); + return (boost::is_same::value) + ? reinterpret_cast(child) + : child->template probeConstNodeAndCache(xyz, acc); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +//////////////////////////////////////// + +template +template +inline void +RootNode::getNodes(ArrayT& array) +{ + typedef typename ArrayT::value_type NodePtr; + BOOST_STATIC_ASSERT(boost::is_pointer::value); + typedef typename boost::remove_pointer::type NodeType; + typedef typename boost::remove_const::type NonConstNodeType; + typedef typename boost::mpl::contains::type result; + BOOST_STATIC_ASSERT(result::value); + typedef typename boost::mpl::if_, + const ChildT, ChildT>::type ArrayChildT; + + for (MapIter iter=mTable.begin(); iter!=mTable.end(); ++iter) { + if (ChildT* child = iter->second.child) { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (boost::is_same::value) { + array.push_back(reinterpret_cast(iter->second.child)); + } else { + child->getNodes(array);//descent + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + } +} + +template +template +inline void +RootNode::getNodes(ArrayT& array) const +{ + typedef typename ArrayT::value_type NodePtr; + BOOST_STATIC_ASSERT(boost::is_pointer::value); + typedef typename boost::remove_pointer::type NodeType; + BOOST_STATIC_ASSERT(boost::is_const::value); + typedef typename boost::remove_const::type NonConstNodeType; + typedef typename boost::mpl::contains::type result; + BOOST_STATIC_ASSERT(result::value); + + for (MapCIter iter=mTable.begin(); iter!=mTable.end(); ++iter) { + if (const ChildNodeType *child = iter->second.child) { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (boost::is_same::value) { + array.push_back(reinterpret_cast(iter->second.child)); + } else { + child->getNodes(array);//descent + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + } +} + +//////////////////////////////////////// + +template +template +inline void +RootNode::stealNodes(ArrayT& array, const ValueType& value, bool state) +{ + typedef typename ArrayT::value_type NodePtr; + BOOST_STATIC_ASSERT(boost::is_pointer::value); + typedef typename boost::remove_pointer::type NodeType; + typedef typename boost::remove_const::type NonConstNodeType; + typedef typename boost::mpl::contains::type result; + BOOST_STATIC_ASSERT(result::value); + typedef typename boost::mpl::if_, + const ChildT, ChildT>::type ArrayChildT; + + for (MapIter iter=mTable.begin(); iter!=mTable.end(); ++iter) { + if (ChildT* child = iter->second.child) { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (boost::is_same::value) { + array.push_back(reinterpret_cast(&stealChild(iter, Tile(value, state)))); + } else { + child->stealNodes(array, value, state);//descent + } + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + } +} + + +//////////////////////////////////////// + + +template +inline void +RootNode::voxelizeActiveTiles(bool threaded) +{ + // These is little point in multi-threaded over the root table since + // each tile spans a huge index space (by default 4096^3) and hence we + // expect few if any at all. In fact, you're very likeky to run out + // of memory if this method is called on a tree with root-level + // active tiles! + 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(threaded); + } +} + + +//////////////////////////////////////// + + +template +template +inline void +RootNode::merge(RootNode& other) +{ + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + + switch (Policy) { + + default: + case MERGE_ACTIVE_STATES: + 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 node's child + ChildNodeType& child = stealChild(i, Tile(other.mBackground, /*on=*/false)); + child.resetBackground(other.mBackground, mBackground); + mTable[i->first] = NodeStruct(child); + } else if (isTile(j)) { + if (isTileOff(j)) { // replace inactive tile with other node's child + ChildNodeType& child = stealChild(i, Tile(other.mBackground, /*on=*/false)); + child.resetBackground(other.mBackground, mBackground); + setChild(j, child); + } + } else { // merge both child nodes + getChild(j).template merge(getChild(i), + other.mBackground, mBackground); + } + } else if (other.isTileOn(i)) { + if (j == mTable.end()) { // insert other node's active tile + mTable[i->first] = i->second; + } else if (!isTileOn(j)) { + // Replace anything except an active tile with the other node's active tile. + setTile(j, Tile(other.getTile(i).value, true)); + } + } + } + break; + + case MERGE_NODES: + 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 node's child + ChildNodeType& child = stealChild(i, Tile(other.mBackground, /*on=*/false)); + child.resetBackground(other.mBackground, mBackground); + mTable[i->first] = NodeStruct(child); + } else if (isTile(j)) { // replace tile with other node's child + ChildNodeType& child = stealChild(i, Tile(other.mBackground, /*on=*/false)); + child.resetBackground(other.mBackground, mBackground); + setChild(j, child); + } else { // merge both child nodes + getChild(j).template merge( + getChild(i), other.mBackground, mBackground); + } + } + } + break; + + case MERGE_ACTIVE_STATES_AND_NODES: + 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()) { + // Steal and insert the other node's child. + ChildNodeType& child = stealChild(i, Tile(other.mBackground, /*on=*/false)); + child.resetBackground(other.mBackground, mBackground); + mTable[i->first] = NodeStruct(child); + } else if (isTile(j)) { + // Replace this node's tile with the other node's child. + ChildNodeType& child = stealChild(i, Tile(other.mBackground, /*on=*/false)); + child.resetBackground(other.mBackground, mBackground); + const Tile tile = getTile(j); + setChild(j, child); + if (tile.active) { + // Merge the other node's child with this node's active tile. + child.template merge( + tile.value, tile.active); + } + } else /*if (isChild(j))*/ { + // Merge the other node's child into this node's child. + getChild(j).template merge(getChild(i), + other.mBackground, mBackground); + } + } else if (other.isTileOn(i)) { + if (j == mTable.end()) { + // Insert a copy of the other node's active tile. + mTable[i->first] = i->second; + } else if (isTileOff(j)) { + // Replace this node's inactive tile with a copy of the other's active tile. + setTile(j, Tile(other.getTile(i).value, true)); + } else if (isChild(j)) { + // Merge the other node's active tile into this node's child. + const Tile& tile = getTile(i); + getChild(j).template merge( + tile.value, tile.active); + } + } // else if (other.isTileOff(i)) {} // ignore the other node's inactive tiles + } + break; + } + + // Empty the other tree so as not to leave it in a partially cannibalized state. + other.clear(); + + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END +} + + +//////////////////////////////////////// + + +template +template +inline void +RootNode::topologyUnion(const RootNode& other) +{ + typedef RootNode 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 +template +inline void +RootNode::topologyIntersection(const RootNode& other) +{ + typedef RootNode OtherRootT; + typedef typename OtherRootT::MapCIter OtherCIterT; + + enforceSameConfiguration(other); + + std::set tmp;//keys to erase + for (MapIter i = mTable.begin(), e = mTable.end(); i != e; ++i) { + OtherCIterT j = other.mTable.find(i->first); + if (this->isChild(i)) { + if (j == other.mTable.end() || other.isTileOff(j)) { + tmp.insert(i->first);//delete child branch + } else if (other.isChild(j)) { // intersect with child branch + this->getChild(i).topologyIntersection(other.getChild(j), mBackground); + } + } else if (this->isTileOn(i)) { + if (j == other.mTable.end() || other.isTileOff(j)) { + this->setTile(i, Tile(this->getTile(i).value, false));//turn inactive + } else if (other.isChild(j)) { //replace with a child branch with identical topology + ChildT* child = + new ChildT(other.getChild(j), this->getTile(i).value, TopologyCopy()); + this->setChild(i, *child); + } + } + } + for (std::set::iterator i = tmp.begin(), e = tmp.end(); i != e; ++i) { + MapIter it = this->findCoord(*i); + setTile(it, Tile()); // delete any existing child node first + mTable.erase(it); + } +} + +template +template +inline void +RootNode::topologyDifference(const RootNode& other) +{ + typedef RootNode 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() || this->isTileOff(j)) { + //do nothing + } else if (this->isChild(j)) { // difference with child branch + this->getChild(j).topologyDifference(other.getChild(i), mBackground); + } else if (this->isTileOn(j)) { + // this is an active tile so create a child node and descent + ChildT* child = new ChildT(j->first, this->getTile(j).value, true); + child->topologyDifference(other.getChild(i), mBackground); + this->setChild(j, *child); + } + } else if (other.isTileOn(i)) { // other is an active tile + if (j == mTable.end() || this->isTileOff(j)) { + // do nothing + } else if (this->isChild(j)) { + setTile(j, Tile()); // delete any existing child node first + mTable.erase(j); + } else if (this->isTileOn(j)) { + this->setTile(j, Tile(this->getTile(j).value, false)); + } + } + } +} + +//////////////////////////////////////// + + +template +template +inline void +RootNode::combine(RootNode& other, CombineOp& op, bool prune) +{ + CombineArgs 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 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(); +} + + +//////////////////////////////////////// + + +// This helper class is a friend of RootNode and is needed so that combine2 +// can be specialized for compatible and incompatible pairs of RootNode types. +template +struct RootNodeCombineHelper +{ + static inline void combine2(RootT& self, const RootT&, const OtherRootT& other1, + CombineOp&, bool) + { + // If the two root nodes have different configurations or incompatible ValueTypes, + // throw an exception. + self.enforceSameConfiguration(other1); + self.enforceCompatibleValueTypes(other1); + // One of the above two tests should throw, so we should never get here: + std::ostringstream ostr; + ostr << "cannot combine a " << typeid(OtherRootT).name() + << " into a " << typeid(RootT).name(); + OPENVDB_THROW(TypeError, ostr.str()); + } +}; + +// Specialization for root nodes of compatible types +template +struct RootNodeCombineHelper +{ + static inline void combine2(RootT& self, const RootT& other0, const OtherRootT& other1, + CombineOp& op, bool prune) + { + self.doCombine2(other0, other1, op, prune); + } +}; + + +template +template +inline void +RootNode::combine2(const RootNode& other0, const OtherRootNode& other1, + CombineOp& op, bool prune) +{ + typedef typename OtherRootNode::ValueType OtherValueType; + static const bool compatible = (SameConfiguration::value + && CanConvertType::value); + RootNodeCombineHelper::combine2( + *this, other0, other1, op, prune); +} + + +template +template +inline void +RootNode::doCombine2(const RootNode& other0, const OtherRootNode& other1, + CombineOp& op, bool prune) +{ + enforceSameConfiguration(other1); + + typedef typename OtherRootNode::ValueType OtherValueT; + typedef typename OtherRootNode::Tile OtherTileT; + typedef typename OtherRootNode::NodeStruct OtherNodeStructT; + typedef typename OtherRootNode::MapCIter OtherMapCIterT; + + CombineArgs args; + + CoordSet keys; + other0.insertKeys(keys); + other1.insertKeys(keys); + + const NodeStruct bg0(Tile(other0.mBackground, /*active=*/false)); + const OtherNodeStructT bg1(OtherTileT(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); + OtherMapCIterT iter1 = other1.findKey(*i); + const NodeStruct& ns0 = (iter0 != other0.mTable.end()) ? iter0->second : bg0; + const OtherNodeStructT& 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 { + if (!isChild(thisIter)) { + // Add a new child with the same coordinates, etc. as the other node's child. + const Coord& childOrigin = + ns0.isChild() ? ns0.child->origin() : ns1.child->origin(); + setChild(thisIter, *(new ChildT(childOrigin, 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 +template +inline void +RootNode::visitActiveBBox(BBoxOp& op) const +{ + const bool descent = op.template descent(); + 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()(CoordBBox::createCube(i->first, ChildT::DIM)); +#else + op.template operator()(CoordBBox::createCube(i->first, ChildT::DIM)); +#endif + } + } +} + + +template +template +inline void +RootNode::visit(VisitorOp& op) +{ + doVisit(*this, op); +} + + +template +template +inline void +RootNode::visit(VisitorOp& op) const +{ + doVisit(*this, op); +} + + +template +template +inline void +RootNode::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 +template +inline void +RootNode::visit2(OtherRootNodeType& other, VisitorOp& op) +{ + doVisit2(*this, other, op); +} + + +template +template +inline void +RootNode::visit2(OtherRootNodeType& other, VisitorOp& op) const +{ + doVisit2(*this, other, op); +} + + +template +template< + typename RootNodeT, + typename OtherRootNodeT, + typename VisitorOp, + typename ChildAllIterT, + typename OtherChildAllIterT> +inline void +RootNode::doVisit2(RootNodeT& self, OtherRootNodeT& other, VisitorOp& op) +{ + 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(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-2016 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/nuparu/include/openvdb/include/openvdb/tree/Tree.h b/nuparu/include/openvdb/include/openvdb/tree/Tree.h new file mode 100644 index 00000000..8becf8f1 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tree/Tree.h @@ -0,0 +1,2337 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "RootNode.h" +#include "InternalNode.h" +#include "LeafNode.h" +#include "TreeIterator.h" +#include "ValueAccessor.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 Ptr; + typedef boost::shared_ptr 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 and active tiles. + /// @details This is faster than calling evalActiveVoxelBoundingBox, + /// which visits the individual active voxels, and hence + /// evalLeafBoundingBox produces a less tight, i.e. approximate, bbox. + /// @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. + /// @details This method produces a more accurate, i.e. tighter, + /// bounding box than evalLeafBoundingBox which is approximate but + /// faster. + /// @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; + +#ifndef OPENVDB_2_ABI_COMPATIBLE + /// @brief Replace with background tiles any nodes whose voxel buffers + /// have not yet been allocated. + /// @details Typically, unallocated nodes are leaf nodes whose voxel buffers + /// are not yet resident in memory because delayed loading is in effect. + /// @sa readNonresidentBuffers, io::File::open + virtual void clipUnallocatedNodes() = 0; +#endif + + + // + // 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; +#ifndef OPENVDB_2_ABI_COMPATIBLE + /// Return the total number of active tiles. + virtual Index64 activeTileCount() const = 0; +#endif + + /// 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; +#ifndef OPENVDB_2_ABI_COMPATIBLE + /// Read all of this tree's data buffers that intersect the given bounding box. + virtual void readBuffers(std::istream&, const CoordBBox&, bool saveFloatAsHalf = false) = 0; + /// @brief Read all of this tree's data buffers that are not yet resident in memory + /// (because delayed loading is in effect). + /// @details If this tree was read from a memory-mapped file, this operation + /// disconnects the tree from the file. + /// @sa clipUnallocatedNodes, io::File::open, io::MappedFile + virtual void readNonresidentBuffers() const = 0; +#endif + /// 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; + /// 4: include minimum and maximum voxel values + /// @warning @a verboseLevel 4 forces loading of any unallocated nodes. + 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 +class Tree: public TreeBase +{ +public: + typedef boost::shared_ptr Ptr; + typedef boost::shared_ptr ConstPtr; + + typedef _RootNodeType RootNodeType; + typedef typename RootNodeType::ValueType ValueType; + typedef typename RootNodeType::BuildType BuildType; + typedef typename RootNodeType::LeafNodeType LeafNodeType; + + static const Index DEPTH = RootNodeType::LEVEL + 1; + + /// @brief ValueConverter::Type is the type of a tree having the same + /// hierarchy as this tree but a different value type, T. + /// + /// For example, FloatTree::ValueConverter::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::Type". + template + struct ValueConverter { + typedef Tree::Type> Type; + }; + + + Tree() {} + + /// Deep copy constructor + Tree(const Tree& other): TreeBase(other), mRoot(other.mRoot) + { + } + + /// @brief Value conversion deep copy constructor + /// + /// Deep copy a tree of the same configuration as this tree type but a different + /// ValueType, casting the other tree's values to this tree's ValueType. + /// @throw TypeError if the other tree's configuration doesn't match this tree's + /// or if this tree's ValueType is not constructible from the other tree's ValueType. + template + explicit Tree(const Tree& other): TreeBase(other), mRoot(other.root()) + { + } + + /// @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 + /// @throw TypeError if the other tree's configuration doesn't match this tree's. + template + Tree(const OtherTreeType& other, + const ValueType& inactiveValue, + const ValueType& activeValue, + TopologyCopy): + TreeBase(other), + mRoot(other.root(), 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 + /// @throw TypeError if the other tree's configuration doesn't match this tree's. + template + Tree(const OtherTreeType& other, const ValueType& background, TopologyCopy): + TreeBase(other), + mRoot(other.root(), background, TopologyCopy()) + { + } + + /// Empty tree constructor + Tree(const ValueType& background): mRoot(background) {} + + virtual ~Tree() { this->clear(); 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(); } + + /// 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 this->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; } + //@} + + + // + // Tree methods + // + /// @brief Return @c true if the given tree has the same node and active value + /// topology as this tree, whether or not it has the same @c ValueType. + template + bool hasSameTopology(const Tree& 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& 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); +#ifndef OPENVDB_2_ABI_COMPATIBLE + /// Read all of this tree's data buffers that intersect the given bounding box. + virtual void readBuffers(std::istream&, const CoordBBox&, bool saveFloatAsHalf = false); + /// @brief Read all of this tree's data buffers that are not yet resident in memory + /// (because delayed loading is in effect). + /// @details If this tree was read from a memory-mapped file, this operation + /// disconnects the tree from the file. + /// @sa clipUnallocatedNodes, io::File::open, io::MappedFile + virtual void readNonresidentBuffers() const; +#endif + /// 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 total number of active tiles. + Index64 activeTileCount() const { return mRoot.onTileCount(); } + + /// 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 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. + /// @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); + /// Set the value of the voxel at the given coordinates but don't change its active state. + void setValueOnly(const Coord& xyz, const ValueType& value); + /// 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); + /// Set the value of the voxel at the given coordinates and mark the voxel as active. + void setValue(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 void setValue(const Coord& xyz, const ValueType& value, AccessT&); + /// Mark the voxel at the given coordinates as inactive but don't change its value. + void setValueOff(const Coord& xyz); + /// Set the value of the voxel at the given coordinates and mark the voxel as inactive. + void setValueOff(const Coord& xyz, const ValueType& value); + + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// @details Provided that the functor can be inlined, this is typically + /// significantly faster than calling getValue() followed by setValueOn(). + /// @param xyz the coordinates of a voxel whose value is to be modified + /// @param op a functor of the form void op(ValueType&) const that modifies + /// its argument in place + /// @par Example: + /// @code + /// Coord xyz(1, 0, -2); + /// // Multiply the value of a voxel by a constant and mark the voxel as active. + /// floatTree.modifyValue(xyz, [](float& f) { f *= 0.25; }); // C++11 + /// // Set the value of a voxel to the maximum of its current value and 0.25, + /// // and mark the voxel as active. + /// floatTree.modifyValue(xyz, [](float& f) { f = std::max(f, 0.25f); }); // C++11 + /// @endcode + /// @note The functor is not guaranteed to be called only once. + /// @see tools::foreach() + template + void modifyValue(const Coord& xyz, const ModifyOp& op); + + /// @brief Apply a functor to the voxel at the given coordinates. + /// @details Provided that the functor can be inlined, this is typically + /// significantly faster than calling getValue() followed by setValue(). + /// @param xyz the coordinates of a voxel to be modified + /// @param op a functor of the form void op(ValueType&, bool&) const that + /// modifies its arguments, a voxel's value and active state, in place + /// @par Example: + /// @code + /// Coord xyz(1, 0, -2); + /// // Multiply the value of a voxel by a constant and mark the voxel as inactive. + /// floatTree.modifyValueAndActiveState(xyz, + /// [](float& f, bool& b) { f *= 0.25; b = false; }); // C++11 + /// // Set the value of a voxel to the maximum of its current value and 0.25, + /// // but don't change the voxel's active state. + /// floatTree.modifyValueAndActiveState(xyz, + /// [](float& f, bool&) { f = std::max(f, 0.25f); }); // C++11 + /// @endcode + /// @note The functor is not guaranteed to be called only once. + /// @see tools::foreach() + template + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op); + + /// @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(); } + + /// Set all voxels that lie outside the given axis-aligned box to the background. + void clip(const CoordBBox&); + +#ifndef OPENVDB_2_ABI_COMPATIBLE + /// @brief Replace with background tiles any nodes whose voxel buffers + /// have not yet been allocated. + /// @details Typically, unallocated nodes are leaf nodes whose voxel buffers + /// are not yet resident in memory because delayed loading is in effect. + /// @sa readNonresidentBuffers, io::File::open + virtual void clipUnallocatedNodes(); +#endif + + /// @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. Defaults to true. + /// @param sparse if false, active tiles are voxelized, i.e. only active voxels + /// are generated from the fill operation. Defaults to true. + /// @note If @a sparse is true 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, bool sparse = true); + + /// @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. + /// @warning Will soon be deprecated! + void prune(const ValueType& tolerance = zeroVal()) + { + this->clearAllAccessors(); + mRoot.prune(tolerance); + } + + /// @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 @a level must be 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 + NodeT* stealNode(const Coord& xyz, const ValueType& value, bool active); + + /// @brief Return a pointer to the leaf node that contains voxel (x, y, z). + /// If no such node exists, create one that preserves 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 Return a pointer to the node of type @c NodeType that contains + /// voxel (x, y, z). If no such node exists, return NULL. + template NodeType* probeNode(const Coord& xyz); + template const NodeType* probeConstNode(const Coord& xyz) const; + template const NodeType* probeNode(const Coord& xyz) const; + //@} + + //@{ + /// @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); + const LeafNodeType* probeConstLeaf(const Coord& xyz) const; + const LeafNodeType* probeLeaf(const Coord& xyz) const { return this->probeConstLeaf(xyz); } + //@} + + //@{ + /// @brief Adds all nodes of a certain type to a container with the following API: + /// @code + /// struct ArrayT { + /// typedef value_type;// defines the type of nodes to be added to the array + /// void push_back(value_type nodePtr);// method that add nodes to the array + /// }; + /// @endcode + /// @details An example of a wrapper around a c-style array is: + /// @code + /// struct MyArray { + /// typedef LeafType* value_type; + /// value_type* ptr; + /// MyArray(value_type* array) : ptr(array) {} + /// void push_back(value_type leaf) { *ptr++ = leaf; } + ///}; + /// @endcode + /// @details An example that constructs a list of pointer to all leaf nodes is: + /// @code + /// std::vector array;//most std contains have the required API + /// array.reserve(tree.leafCount());//this is a fast preallocation. + /// tree.getNodes(array); + /// @endcode + template void getNodes(ArrayT& array) { mRoot.getNodes(array); } + template void getNodes(ArrayT& array) const { mRoot.getNodes(array); } + //@} + + /// @brief Steals all nodes of a certain type from the tree and + /// adds them to a container with the following API: + /// @code + /// struct ArrayT { + /// typedef value_type;// defines the type of nodes to be added to the array + /// void push_back(value_type nodePtr);// method that add nodes to the array + /// }; + /// @endcode + /// @details An example of a wrapper around a c-style array is: + /// @code + /// struct MyArray { + /// typedef LeafType* value_type; + /// value_type* ptr; + /// MyArray(value_type* array) : ptr(array) {} + /// void push_back(value_type leaf) { *ptr++ = leaf; } + ///}; + /// @endcode + /// @details An example that constructs a list of pointer to all leaf nodes is: + /// @code + /// std::vector array;//most std contains have the required API + /// array.reserve(tree.leafCount());//this is a fast preallocation. + /// tree.stealNodes(array); + /// @endcode + template + void stealNodes(ArrayT& array) { this->clearAllAccessors(); mRoot.stealNodes(array); } + template + void stealNodes(ArrayT& array, const ValueType& value, bool state) + { + this->clearAllAccessors(); + mRoot.stealNodes(array, value, state); + } + + // + // 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(); + + /// 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&) const; + void attachAccessor(ValueAccessorBase&) const; + //@} + + //@{ + /// Dummy implementations + void attachAccessor(ValueAccessorBase&) const {} + void attachAccessor(ValueAccessorBase&) const {} + //@} + + //@{ + /// Deregister an accessor so that it is no longer automatically cleared. + void releaseAccessor(ValueAccessorBase&) const; + void releaseAccessor(ValueAccessorBase&) const; + //@} + + //@{ + /// Dummy implementations + void releaseAccessor(ValueAccessorBase&) const {} + void releaseAccessor(ValueAccessorBase&) 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; + + /// @brief Return this tree's background value. + /// + /// @note Use tools::changeBackground to efficiently modify the + /// background values. Else use tree.root().setBackground, which + /// is serial and hence slower. + const ValueType& background() const { return mRoot.background(); } + + /// Min and max are both inclusive. + virtual void getIndexRange(CoordBBox& bbox) const { mRoot.getIndexRange(bbox); } + + /// @brief Densify active tiles, i.e., replace them with leaf-level active voxels. + /// + /// @param threaded if true, this operation is multi-threaded (over the internal nodes). + /// + /// @warning This method can explode the tree's memory footprint, especially if it + /// contains active tiles at the upper levels, e.g. root level! + void voxelizeActiveTiles(bool threaded = true); + + /// @brief Efficiently merge another tree into this tree using one of several schemes. + /// @details This operation is primarily intended to combine trees that are mostly + /// non-overlapping (for example, intermediate trees from computations that are + /// parallelized across disjoint regions of space). + /// @note This operation is not guaranteed to produce an optimally sparse tree. + /// Follow merge() with prune() for optimal sparseness. + /// @warning This operation always empties the other tree. + void merge(Tree& other, MergePolicy = MERGE_ACTIVE_STATES); + + /// @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 + void topologyUnion(const Tree& other); + + /// @brief Intersects 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 only if the corresponding + /// value was already active AND if it is active in the other tree. Also, a + /// resulting value maps to a voxel if the corresponding value + /// already mapped to an active voxel in either of the two grids + /// and it maps to an active tile or voxel in the other grid. + /// + /// @note This operation can delete branches in this grid if they + /// overlap with inactive tiles in the other grid. Likewise active + /// voxels can be turned into unactive voxels resulting in leaf + /// nodes with no active values. Thus, it is recommended to + /// subsequently call tools::pruneInactive. + template + void topologyIntersection(const Tree& other); + + /// @brief Difference this tree's set of active values with the active values + /// of the other tree, whose @c ValueType may be different. So a + /// resulting voxel will be active only if the original voxel is + /// active in this tree and inactive in the other tree. + /// + /// @note This operation can delete branches in this grid if they + /// overlap with active tiles in the other grid. Likewise active + /// voxels can be turned into inactive voxels resulting in leaf + /// nodes with no active values. Thus, it is recommended to + /// subsequently call tools::pruneInactive. + template + void topologyDifference(const Tree& other); + + /// For a given function @c f, use sparse traversal to compute f(this, other) + /// 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 void op(const T& a, const T& b, T& result), + /// where @c T is this tree's @c ValueType, that computes + /// result = f(a, b) + /// @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 f * a + (1 - f) * b 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 + void combine(Tree& other, CombineOp& op, bool prune = false); +#ifndef _MSC_VER + template + 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 void op(CombineArgs& args) that + /// computes args.setResult(f(args.a(), args.b())) and, optionally, + /// args.setResultIsActive(g(args.aIsActive(), args.bIsActive())) + /// 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& 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 + void combineExtended(Tree& other, ExtendedCombineOp& op, bool prune = false); +#ifndef _MSC_VER + template + void combineExtended(Tree& other, const ExtendedCombineOp& op, bool prune = false); +#endif + + /// For a given function @c f, use sparse traversal to compute f(a, b) 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 with the same configuration (levels and node dimensions) + /// as this tree but with the B tree possibly having a different value type + /// @param op a functor of the form void op(const T1& a, const T2& b, T1& result), + /// where @c T1 is this tree's and the A tree's @c ValueType and @c T2 is the + /// B tree's @c ValueType, that computes result = f(a, b) + /// @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) + /// + /// @throw TypeError if the B tree's configuration doesn't match this tree's + /// or if this tree's ValueType is not constructible from the B tree's ValueType. + /// + /// @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 + void combine2(const Tree& a, const OtherTreeType& b, CombineOp& op, bool prune = false); +#ifndef _MSC_VER + template + void combine2(const Tree& a, const OtherTreeType& b, const CombineOp& op, bool prune = false); +#endif + + /// Like combine2(), but with + /// @param a,b two trees with the same configuration (levels and node dimensions) + /// as this tree but with the B tree possibly having a different value type + /// @param op a functor of the form void op(CombineArgs& args), where + /// @c T1 is this tree's and the A tree's @c ValueType and @c T2 is the B tree's + /// @c ValueType, that computes args.setResult(f(args.a(), args.b())) + /// and, optionally, + /// args.setResultIsActive(g(args.aIsActive(), args.bIsActive())) + /// 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 + /// 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. + /// + /// @throw TypeError if the B tree's configuration doesn't match this tree's + /// or if this tree's ValueType is not constructible from the B tree's ValueType. + /// + /// @see openvdb/Types.h for the definition of the CombineArgs struct. + /// + /// @par Example: + /// Compute the per-voxel maximum values of two single-precision 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& 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 aTree = ...; + /// FloatTree bTree = ...; + /// FloatTree resultTree; + /// resultTree.combine2Extended(aTree, bTree, Local::max); + /// } + /// @endcode + /// + /// @par Example: + /// Compute the per-voxel maximum values of a double-precision and a single-precision + /// floating-point tree, @c aTree and @c bTree, and store the result in a third, + /// double-precision 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& 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()); + /// } + /// } + /// }; + /// DoubleTree aTree = ...; + /// FloatTree bTree = ...; + /// DoubleTree resultTree; + /// resultTree.combine2Extended(aTree, bTree, Local::max); + /// } + /// @endcode + template + void combine2Extended(const Tree& a, const OtherTreeType& b, ExtendedCombineOp& op, + bool prune = false); +#ifndef _MSC_VER + template + void combine2Extended(const Tree& a, const OtherTreeType& b, const ExtendedCombineOp&, + bool prune = false); +#endif + + /// @brief Use sparse traversal to call the given functor with bounding box + /// information for all active tiles and leaf nodes or active voxels in the tree. + /// + /// @note The bounding boxes are guaranteed to be non-overlapping. + /// @param op a functor with a templated call operator of the form + /// template void operator()(const CoordBBox& bbox), + /// where bbox is the bounding box of either an active tile + /// (if @c LEVEL > 0), a leaf node or an active voxel. + /// The functor must also provide a templated method of the form + /// template bool descent() that returns @c false + /// if bounding boxes below the specified tree level are not to be visited. + /// In such cases of early tree termination, a bounding box is instead + /// derived from each terminating child node. + /// + /// @par Example: + /// Visit and process all active tiles and leaf nodes in a tree, but don't + /// descend to the active voxels. The smallest bounding boxes that will be + /// visited are those of leaf nodes or level-1 active tiles. + /// @code + /// { + /// struct ProcessTilesAndLeafNodes { + /// // Descend to leaf nodes, but no further. + /// template inline bool descent() { return LEVEL > 0; } + /// // Use this version to descend to voxels: + /// //template inline bool descent() { return true; } + /// + /// template + /// inline void operator()(const CoordBBox &bbox) { + /// if (LEVEL > 0) { + /// // code to process an active tile + /// } else { + /// // code to process a leaf node + /// } + /// } + /// }; + /// ProcessTilesAndLeafNodes op; + /// aTree.visitActiveBBox(op); + /// } + /// @endcode + /// @see openvdb/unittest/TestTree.cc for another example. + template 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 template bool op(IterT&), + /// 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 + /// struct PrintTreeVisitor + /// { + /// typedef typename TreeT::RootNodeType RootT; + /// bool visitedRoot; + /// + /// PrintTreeVisitor(): visitedRoot(false) {} + /// + /// template + /// 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 void visit(VisitorOp& op); + template void visit(const VisitorOp& op); + + /// Like visit(), but using @c const iterators, i.e., with + /// @param op a functor of the form template bool op(IterT&), + /// where @c IterT is either a RootNode::ChildAllCIter, + /// an InternalNode::ChildAllCIter or a LeafNode::ChildAllCIter + template void visit(VisitorOp& op) const; + template 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 + /// template int op(AIterT&, BIterT&), + /// 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 + /// 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 + void visit2(OtherTreeType& other, VisitorOp& op); + template + 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 + /// template int op(AIterT&, BIterT&), + /// 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 + void visit2(OtherTreeType& other, VisitorOp& op) const; + template + 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 NodeIter; + typedef NodeIteratorBase NodeCIter; + //@} + + //@{ + /// Iterator over all leaf nodes in this tree + typedef LeafIteratorBase LeafIter; + typedef LeafIteratorBase 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 ValueAllIter; + typedef TreeValueIteratorBase ValueAllCIter; + typedef TreeValueIteratorBase ValueOnIter; + typedef TreeValueIteratorBase ValueOnCIter; + typedef TreeValueIteratorBase ValueOffIter; + typedef TreeValueIteratorBase 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() is + /// equivalent to beginValueOn()). + template IterT begin(); + /// @brief Return a const iterator of type CIterT (for example, cbegin() + /// is equivalent to cbeginValueOn()). + template CIterT cbegin() const; + + +protected: + typedef tbb::concurrent_hash_map*, bool> AccessorRegistry; + typedef tbb::concurrent_hash_map*, 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(); + + // TBB body object used to deallocates leafnodes in parallel. + struct DeallocateLeafNodes { + DeallocateLeafNodes(std::vector& nodes) + : mNodes(nodes.empty() ? NULL : &nodes.front()) { } + void operator()(const tbb::blocked_range& range) const { + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + delete mNodes[n]; mNodes[n] = NULL; + } + } + LeafNodeType ** const mNodes; + }; + + // + // Data members + // + RootNodeType mRoot; // root node of the tree + mutable AccessorRegistry mAccessorRegistry; + mutable ConstAccessorRegistry mConstAccessorRegistry; + + static tbb::atomic sTreeTypeName; +}; // end of Tree class + +template +tbb::atomic Tree<_RootNodeType>::sTreeTypeName; + + +/// @brief Tree3::Type is the type of a three-level tree +/// (Root, Internal, Leaf) with value type T and +/// internal and leaf node log dimensions N1 and N2, respectively. +/// @note This is NOT the standard tree configuration (Tree4 is). +template +struct Tree3 { + typedef Tree, N1> > > Type; +}; + + +/// @brief Tree4::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. +/// @note This is the standard tree configuration. +template +struct Tree4 { + typedef Tree, N2>, N1> > > Type; +}; + +/// @brief Tree5::Type is the type of a five-level tree +/// (Root, Internal, Internal, Internal, Leaf) with value type T and +/// internal and leaf node log dimensions N1, N2, N3 and N4, respectively. +/// @note This is NOT the standard tree configuration (Tree4 is). +template +struct Tree5 { + typedef Tree, N3>, N2>, N1> > > + Type; +}; + + +//////////////////////////////////////// + + +inline void +TreeBase::readTopology(std::istream& is, bool /*saveFloatAsHalf*/) +{ + int32_t bufferCount; + is.read(reinterpret_cast(&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(&bufferCount), sizeof(int32_t)); +} + + +inline void +TreeBase::print(std::ostream& os, int /*verboseLevel*/) const +{ + os << " Tree Type: " << type() + << " Active Voxel Count: " << activeVoxelCount() << std::endl +#ifndef OPENVDB_2_ABI_COMPATIBLE + << " Active tile Count: " << activeTileCount() << std::endl +#endif + << " 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 struct TreeIterTraits; + +template struct TreeIterTraits { + static typename TreeT::RootNodeType::ChildOnIter begin(TreeT& tree) { + return tree.beginRootChildren(); + } +}; + +template struct TreeIterTraits { + static typename TreeT::RootNodeType::ChildOnCIter begin(const TreeT& tree) { + return tree.cbeginRootChildren(); + } +}; + +template struct TreeIterTraits { + static typename TreeT::RootNodeType::ChildOffIter begin(TreeT& tree) { + return tree.beginRootTiles(); + } +}; + +template struct TreeIterTraits { + static typename TreeT::RootNodeType::ChildOffCIter begin(const TreeT& tree) { + return tree.cbeginRootTiles(); + } +}; + +template struct TreeIterTraits { + static typename TreeT::RootNodeType::ChildAllIter begin(TreeT& tree) { + return tree.beginRootDense(); + } +}; + +template struct TreeIterTraits { + static typename TreeT::RootNodeType::ChildAllCIter begin(const TreeT& tree) { + return tree.cbeginRootDense(); + } +}; + +template struct TreeIterTraits { + static typename TreeT::NodeIter begin(TreeT& tree) { return tree.beginNode(); } +}; + +template struct TreeIterTraits { + static typename TreeT::NodeCIter begin(const TreeT& tree) { return tree.cbeginNode(); } +}; + +template struct TreeIterTraits { + static typename TreeT::LeafIter begin(TreeT& tree) { return tree.beginLeaf(); } +}; + +template struct TreeIterTraits { + static typename TreeT::LeafCIter begin(const TreeT& tree) { return tree.cbeginLeaf(); } +}; + +template struct TreeIterTraits { + static typename TreeT::ValueOnIter begin(TreeT& tree) { return tree.beginValueOn(); } +}; + +template struct TreeIterTraits { + static typename TreeT::ValueOnCIter begin(const TreeT& tree) { return tree.cbeginValueOn(); } +}; + +template struct TreeIterTraits { + static typename TreeT::ValueOffIter begin(TreeT& tree) { return tree.beginValueOff(); } +}; + +template struct TreeIterTraits { + static typename TreeT::ValueOffCIter begin(const TreeT& tree) { return tree.cbeginValueOff(); } +}; + +template struct TreeIterTraits { + static typename TreeT::ValueAllIter begin(TreeT& tree) { return tree.beginValueAll(); } +}; + +template struct TreeIterTraits { + static typename TreeT::ValueAllCIter begin(const TreeT& tree) { return tree.cbeginValueAll(); } +}; + + +template +template +inline IterT +Tree::begin() +{ + return TreeIterTraits::begin(*this); +} + + +template +template +inline IterT +Tree::cbegin() const +{ + return TreeIterTraits::begin(*this); +} + + +//////////////////////////////////////// + + +template +void +Tree::readTopology(std::istream& is, bool saveFloatAsHalf) +{ + this->clearAllAccessors(); + TreeBase::readTopology(is, saveFloatAsHalf); + mRoot.readTopology(is, saveFloatAsHalf); +} + + +template +void +Tree::writeTopology(std::ostream& os, bool saveFloatAsHalf) const +{ + TreeBase::writeTopology(os, saveFloatAsHalf); + mRoot.writeTopology(os, saveFloatAsHalf); +} + + +template +inline void +Tree::readBuffers(std::istream &is, bool saveFloatAsHalf) +{ + this->clearAllAccessors(); + mRoot.readBuffers(is, saveFloatAsHalf); +} + + +#ifndef OPENVDB_2_ABI_COMPATIBLE + +template +inline void +Tree::readBuffers(std::istream &is, const CoordBBox& bbox, bool saveFloatAsHalf) +{ + this->clearAllAccessors(); + mRoot.readBuffers(is, bbox, saveFloatAsHalf); +} + + +template +inline void +Tree::readNonresidentBuffers() const +{ + for (LeafCIter it = this->cbeginLeaf(); it; ++it) { + // Retrieving the value of a leaf voxel forces loading of the leaf node's voxel buffer. + it->getValue(Index(0)); + } +} + +#endif // !OPENVDB_2_ABI_COMPATIBLE + + +template +inline void +Tree::writeBuffers(std::ostream &os, bool saveFloatAsHalf) const +{ + mRoot.writeBuffers(os, saveFloatAsHalf); +} + + +template +inline void +Tree::clear() +{ + std::vector leafnodes; + this->stealNodes(leafnodes); + mRoot.clear(); + + tbb::parallel_for(tbb::blocked_range(0, leafnodes.size()), + DeallocateLeafNodes(leafnodes)); + + this->clearAllAccessors(); +} + + +//////////////////////////////////////// + + +template +inline void +Tree::attachAccessor(ValueAccessorBase& accessor) const +{ + typename AccessorRegistry::accessor a; + mAccessorRegistry.insert(a, &accessor); +} + + +template +inline void +Tree::attachAccessor(ValueAccessorBase& accessor) const +{ + typename ConstAccessorRegistry::accessor a; + mConstAccessorRegistry.insert(a, &accessor); +} + + +template +inline void +Tree::releaseAccessor(ValueAccessorBase& accessor) const +{ + mAccessorRegistry.erase(&accessor); +} + + +template +inline void +Tree::releaseAccessor(ValueAccessorBase& accessor) const +{ + mConstAccessorRegistry.erase(&accessor); +} + + +template +inline void +Tree::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 +inline void +Tree::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 +inline const typename RootNodeType::ValueType& +Tree::getValue(const Coord& xyz) const +{ + return mRoot.getValue(xyz); +} + + +template +template +inline const typename RootNodeType::ValueType& +Tree::getValue(const Coord& xyz, AccessT& accessor) const +{ + return accessor.getValue(xyz); +} + + +template +inline int +Tree::getValueDepth(const Coord& xyz) const +{ + return mRoot.getValueDepth(xyz); +} + + +template +inline void +Tree::setValueOff(const Coord& xyz) +{ + mRoot.setValueOff(xyz); +} + + +template +inline void +Tree::setValueOff(const Coord& xyz, const ValueType& value) +{ + mRoot.setValueOff(xyz, value); +} + + +template +inline void +Tree::setActiveState(const Coord& xyz, bool on) +{ + mRoot.setActiveState(xyz, on); +} + + +template +inline void +Tree::setValue(const Coord& xyz, const ValueType& value) +{ + mRoot.setValueOn(xyz, value); +} + +template +inline void +Tree::setValueOnly(const Coord& xyz, const ValueType& value) +{ + mRoot.setValueOnly(xyz, value); +} + +template +template +inline void +Tree::setValue(const Coord& xyz, const ValueType& value, AccessT& accessor) +{ + accessor.setValue(xyz, value); +} + + +template +inline void +Tree::setValueOn(const Coord& xyz) +{ + mRoot.setActiveState(xyz, true); +} + + +template +inline void +Tree::setValueOn(const Coord& xyz, const ValueType& value) +{ + mRoot.setValueOn(xyz, value); +} + + +template +template +inline void +Tree::modifyValue(const Coord& xyz, const ModifyOp& op) +{ + mRoot.modifyValue(xyz, op); +} + + +template +template +inline void +Tree::modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) +{ + mRoot.modifyValueAndActiveState(xyz, op); +} + + +template +inline bool +Tree::probeValue(const Coord& xyz, ValueType& value) const +{ + return mRoot.probeValue(xyz, value); +} + + +//////////////////////////////////////// + + +template +inline void +Tree::addTile(Index level, const Coord& xyz, + const ValueType& value, bool active) +{ + mRoot.addTile(level, xyz, value, active); +} + + +template +template +inline NodeT* +Tree::stealNode(const Coord& xyz, const ValueType& value, bool active) +{ + this->clearAllAccessors(); + return mRoot.template stealNode(xyz, value, active); +} + + +template +inline typename RootNodeType::LeafNodeType* +Tree::touchLeaf(const Coord& xyz) +{ + return mRoot.touchLeaf(xyz); +} + + +template +inline typename RootNodeType::LeafNodeType* +Tree::probeLeaf(const Coord& xyz) +{ + return mRoot.probeLeaf(xyz); +} + + +template +inline const typename RootNodeType::LeafNodeType* +Tree::probeConstLeaf(const Coord& xyz) const +{ + return mRoot.probeConstLeaf(xyz); +} + + +template +template +inline NodeType* +Tree::probeNode(const Coord& xyz) +{ + return mRoot.template probeNode(xyz); +} + + +template +template +inline const NodeType* +Tree::probeNode(const Coord& xyz) const +{ + return this->template probeConstNode(xyz); +} + + +template +template +inline const NodeType* +Tree::probeConstNode(const Coord& xyz) const +{ + return mRoot.template probeConstNode(xyz); +} + + +//////////////////////////////////////// + + +template +inline void +Tree::clip(const CoordBBox& bbox) +{ + this->clearAllAccessors(); + return mRoot.clip(bbox); +} + + +#ifndef OPENVDB_2_ABI_COMPATIBLE +template +inline void +Tree::clipUnallocatedNodes() +{ + this->clearAllAccessors(); + for (LeafIter it = this->beginLeaf(); it; ) { + const LeafNodeType* leaf = it.getLeaf(); + ++it; // advance the iterator before deleting the leaf node + if (!leaf->isAllocated()) { + this->addTile(/*level=*/0, leaf->origin(), this->background(), /*active=*/false); + } + } +} +#endif + + +template +inline void +Tree::fill(const CoordBBox& bbox, const ValueType& value, bool active, bool sparse) +{ + this->clearAllAccessors(); + return mRoot.fill(bbox, value, active, sparse); +} + + +template +Metadata::Ptr +Tree::getBackgroundValue() const +{ + Metadata::Ptr result; + if (Metadata::isRegisteredType(valueType())) { + typedef TypedMetadata MetadataT; + result = Metadata::createMetadata(valueType()); + if (result->typeName() == MetadataT::staticTypeName()) { + MetadataT* m = static_cast(result.get()); + m->value() = mRoot.background(); + } + } + return result; +} + + +//////////////////////////////////////// + + +template +inline void +Tree::voxelizeActiveTiles(bool threaded) +{ + this->clearAllAccessors(); + mRoot.voxelizeActiveTiles(threaded); +} + + +template +inline void +Tree::merge(Tree& other, MergePolicy policy) +{ + this->clearAllAccessors(); + other.clearAllAccessors(); + switch (policy) { + case MERGE_ACTIVE_STATES: + mRoot.template merge(other.mRoot); break; + case MERGE_NODES: + mRoot.template merge(other.mRoot); break; + case MERGE_ACTIVE_STATES_AND_NODES: + mRoot.template merge(other.mRoot); break; + } +} + + +template +template +inline void +Tree::topologyUnion(const Tree& other) +{ + this->clearAllAccessors(); + mRoot.topologyUnion(other.root()); +} + +template +template +inline void +Tree::topologyIntersection(const Tree& other) +{ + this->clearAllAccessors(); + mRoot.topologyIntersection(other.root()); +} + +template +template +inline void +Tree::topologyDifference(const Tree& other) +{ + this->clearAllAccessors(); + mRoot.topologyDifference(other.root()); +} + +//////////////////////////////////////// + + +/// @brief Helper class to adapt a three-argument (a, b, result) CombineOp functor +/// into a single-argument functor that accepts a CombineArgs struct +template +struct CombineOpAdapter +{ + CombineOpAdapter(CombineOp& _op): op(_op) {} + + void operator()(CombineArgs& args) const { + op(args.a(), args.b(), args.result()); + } + + CombineOp& op; +}; + + +template +template +inline void +Tree::combine(Tree& other, CombineOp& op, bool prune) +{ + CombineOpAdapter 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: aTree.combine(bTree, MyCombineOp(...)). +#ifndef _MSC_VER +template +template +inline void +Tree::combine(Tree& other, const CombineOp& op, bool prune) +{ + CombineOpAdapter extendedOp(op); + this->combineExtended(other, extendedOp, prune); +} +#endif + + +template +template +inline void +Tree::combineExtended(Tree& other, ExtendedCombineOp& op, bool prune) +{ + this->clearAllAccessors(); + mRoot.combine(other.root(), op, prune); +} + + +/// @internal This overload is needed (for ICC and GCC, but not for VC) to disambiguate +/// code like this: aTree.combineExtended(bTree, MyCombineOp(...)). +#ifndef _MSC_VER +template +template +inline void +Tree::combineExtended(Tree& other, const ExtendedCombineOp& op, bool prune) +{ + this->clearAllAccessors(); + mRoot.template combine(other.mRoot, op, prune); +} +#endif + + +template +template +inline void +Tree::combine2(const Tree& a, const OtherTreeType& b, CombineOp& op, bool prune) +{ + CombineOpAdapter 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: tree.combine2(aTree, bTree, MyCombineOp(...)). +#ifndef _MSC_VER +template +template +inline void +Tree::combine2(const Tree& a, const OtherTreeType& b, const CombineOp& op, bool prune) +{ + CombineOpAdapter extendedOp(op); + this->combine2Extended(a, b, extendedOp, prune); +} +#endif + + +template +template +inline void +Tree::combine2Extended(const Tree& a, const OtherTreeType& b, + ExtendedCombineOp& op, bool prune) +{ + this->clearAllAccessors(); + mRoot.combine2(a.root(), b.root(), op, prune); +} + + +/// @internal This overload is needed (for ICC and GCC, but not for VC) to disambiguate +/// code like the following, where the functor argument is a temporary: +/// tree.combine2Extended(aTree, bTree, MyCombineOp(...)). +#ifndef _MSC_VER +template +template +inline void +Tree::combine2Extended(const Tree& a, const OtherTreeType& b, + const ExtendedCombineOp& op, bool prune) +{ + this->clearAllAccessors(); + mRoot.template combine2(a.root(), b.root(), op, prune); +} +#endif + + +//////////////////////////////////////// + + +template +template +inline void +Tree::visit(VisitorOp& op) +{ + this->clearAllAccessors(); + mRoot.template visit(op); +} + + +template +template +inline void +Tree::visit(VisitorOp& op) const +{ + mRoot.template visit(op); +} + + +/// @internal This overload is needed (for ICC and GCC, but not for VC) to disambiguate +/// code like this: tree.visit(MyVisitorOp(...)). +template +template +inline void +Tree::visit(const VisitorOp& op) +{ + this->clearAllAccessors(); + mRoot.template visit(op); +} + + +/// @internal This overload is needed (for ICC and GCC, but not for VC) to disambiguate +/// code like this: tree.visit(MyVisitorOp(...)). +template +template +inline void +Tree::visit(const VisitorOp& op) const +{ + mRoot.template visit(op); +} + + +//////////////////////////////////////// + + +template +template +inline void +Tree::visit2(OtherTreeType& other, VisitorOp& op) +{ + this->clearAllAccessors(); + typedef typename OtherTreeType::RootNodeType OtherRootNodeType; + mRoot.template visit2(other.root(), op); +} + + +template +template +inline void +Tree::visit2(OtherTreeType& other, VisitorOp& op) const +{ + typedef typename OtherTreeType::RootNodeType OtherRootNodeType; + mRoot.template visit2(other.root(), op); +} + + +/// @internal This overload is needed (for ICC and GCC, but not for VC) to disambiguate +/// code like this: aTree.visit2(bTree, MyVisitorOp(...)). +template +template +inline void +Tree::visit2(OtherTreeType& other, const VisitorOp& op) +{ + this->clearAllAccessors(); + typedef typename OtherTreeType::RootNodeType OtherRootNodeType; + mRoot.template visit2(other.root(), op); +} + + +/// @internal This overload is needed (for ICC and GCC, but not for VC) to disambiguate +/// code like this: aTree.visit2(bTree, MyVisitorOp(...)). +template +template +inline void +Tree::visit2(OtherTreeType& other, const VisitorOp& op) const +{ + typedef typename OtherTreeType::RootNodeType OtherRootNodeType; + mRoot.template visit2(other.root(), op); +} + + +//////////////////////////////////////// + + +template +inline const Name& +Tree::treeType() +{ + if (sTreeTypeName == NULL) { + std::vector dims; + Tree::getNodeLog2Dims(dims); + std::ostringstream ostr; + ostr << "Tree_" << typeNameAsString(); + 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()); + if (sTreeTypeName.compare_and_swap(s, NULL) != NULL) delete s; + } + return *sTreeTypeName; +} + + +template +template +inline bool +Tree::hasSameTopology(const Tree& other) const +{ + return mRoot.hasSameTopology(other.root()); +} + + +template +Index64 +Tree::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 +inline bool +Tree::evalLeafBoundingBox(CoordBBox& bbox) const +{ + bbox.reset(); // default invalid bbox + + if (this->empty()) return false; // empty + + mRoot.evalActiveBoundingBox(bbox, false); + + return true;// not empty +} + +template +inline bool +Tree::evalActiveVoxelBoundingBox(CoordBBox& bbox) const +{ + bbox.reset(); // default invalid bbox + + if (this->empty()) return false; // empty + + mRoot.evalActiveBoundingBox(bbox, true); + + return true;// not empty +} + + +template +inline bool +Tree::evalActiveVoxelDim(Coord& dim) const +{ + CoordBBox bbox; + bool notEmpty = this->evalActiveVoxelBoundingBox(bbox); + dim = bbox.extents(); + return notEmpty; +} + + +template +inline bool +Tree::evalLeafDim(Coord& dim) const +{ + CoordBBox bbox; + bool notEmpty = this->evalLeafBoundingBox(bbox); + dim = bbox.extents(); + return notEmpty; +} + + +template +inline void +Tree::evalMinMax(ValueType& minVal, ValueType& maxVal) const +{ + minVal = maxVal = zeroVal(); + 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 +inline void +Tree::getNodeLog2Dims(std::vector& dims) +{ + dims.clear(); + RootNodeType::getNodeLog2Dims(dims); +} + + +template +inline void +Tree::print(std::ostream& os, int verboseLevel) const +{ + if (verboseLevel <= 0) return; + + /// @todo Consider using boost::io::ios_precision_saver instead. + 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 dims; + Tree::getNodeLog2Dims(dims); + + 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"; + } + os << " Background value: " << mRoot.background() << "\n"; + return; + } + + // The following is tree information that is expensive to extract. + + ValueType minVal = zeroVal(), maxVal = zeroVal(); + if (verboseLevel > 3) { + // This forces loading of all non-resident nodes. + this->evalMinMax(minVal, maxVal); + } + + std::vector nodeCount(dims.size()); +#ifndef OPENVDB_2_ABI_COMPATIBLE + Index64 unallocatedLeafCount = 0; +#endif + for (NodeCIter it = cbeginNode(); it; ++it) { + ++(nodeCount[it.getDepth()]); + +#ifndef OPENVDB_2_ABI_COMPATIBLE + if (it.getLevel() == 0) { + const LeafNodeType* leaf = NULL; + it.getNode(leaf); + if (leaf && !leaf->isAllocated()) ++unallocatedLeafCount; + } +#endif + } + Index64 totalNodeCount = 0; + for (size_t i = 0; i < nodeCount.size(); ++i) totalNodeCount += nodeCount[i]; + + // Print node types, counts and sizes. + 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"; + + // Statistics of topology and values + + if (verboseLevel > 3) { + os << " Min value: " << minVal << "\n"; + os << " Max value: " << maxVal << "\n"; + } + + const uint64_t + leafCount = *nodeCount.rbegin(), + numActiveVoxels = this->activeVoxelCount(), + numActiveLeafVoxels = this->activeLeafVoxelCount(), + numActiveTiles = this->activeTileCount(); + + os << " Number of active voxels: " << util::formattedInt(numActiveVoxels) << "\n"; + os << " Number of active tiles: " << util::formattedInt(numActiveTiles) << "\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 * double(numActiveVoxels)) / double(totalVoxels); + os << " Percentage of active voxels: " << std::setprecision(3) << activeRatio << "%\n"; + + if (leafCount > 0) { + const double fillRatio = (100.0 * double(numActiveLeafVoxels)) + / (double(leafCount) * double(LeafNodeType::NUM_VOXELS)); + os << " Average leaf node fill ratio: " << fillRatio << "%\n"; + } + +#ifndef OPENVDB_2_ABI_COMPATIBLE + if (verboseLevel > 2) { + os << " Number of unallocated nodes: " + << util::formattedInt(unallocatedLeafCount) << " (" + << (100.0 * double(unallocatedLeafCount) / double(totalNodeCount)) << "%)\n"; + } +#endif + } 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: "); + util::printBytes(os, voxelsMem, " Active leaf voxels: "); + + if (numActiveVoxels) { + util::printBytes(os, denseMem, " Dense equivalent: "); + os << " Actual footprint is " << (100.0 * double(actualMem) / double(denseMem)) + << "% of an equivalent dense volume\n"; + os << " Leaf voxel footprint is " << (100.0 * double(voxelsMem) / double(actualMem)) + << "% of actual footprint\n"; + } +} + +} // namespace tree +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_TREE_TREE_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/tree/TreeIterator.h b/nuparu/include/openvdb/include/openvdb/tree/TreeIterator.h new file mode 100644 index 00000000..aadc1414 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tree/TreeIterator.h @@ -0,0 +1,1405 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// 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::Type is either const T2 or T2 with no const qualifier, +/// depending on whether T1 is const. For example, +/// - CopyConstness::Type is int +/// - CopyConstness::Type is int +/// - CopyConstness::Type is const int +/// - CopyConstness::Type is const int +template struct CopyConstness { + typedef typename boost::remove_const::type Type; +}; +template struct CopyConstness { + typedef const ToType Type; +}; + + +//////////////////////////////////////// + + +namespace iter { + +template +struct InvertedTree { + typedef typename InvertedTree::Type SubtreeT; + typedef typename boost::mpl::push_back::type Type; +}; +template +struct InvertedTree { + typedef typename boost::mpl::vector::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::NodeConverter::Type +/// is synonymous with LeafNode::ValueOnIter. +/// - a begin(node) function that returns a begin iterator for a node of arbitrary type; +/// for example, IterTraits::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 +struct IterTraits +{ + template static ChildT* getChild(const IterT&) { return NULL; } +}; + +template +struct IterTraits +{ + typedef typename NodeT::ChildOnIter IterT; + static IterT begin(NodeT& node) { return node.beginChildOn(); } + template static ChildT* getChild(const IterT& iter) { + return &iter.getValue(); + } + template struct NodeConverter { + typedef typename OtherNodeT::ChildOnIter Type; + }; +}; + +template +struct IterTraits +{ + typedef typename NodeT::ChildOnCIter IterT; + static IterT begin(const NodeT& node) { return node.cbeginChildOn(); } + template static const ChildT* getChild(const IterT& iter) { + return &iter.getValue(); + } + template struct NodeConverter { + typedef typename OtherNodeT::ChildOnCIter Type; + }; +}; + +template +struct IterTraits +{ + typedef typename NodeT::ChildOffIter IterT; + static IterT begin(NodeT& node) { return node.beginChildOff(); } + template struct NodeConverter { + typedef typename OtherNodeT::ChildOffIter Type; + }; +}; + +template +struct IterTraits +{ + typedef typename NodeT::ChildOffCIter IterT; + static IterT begin(const NodeT& node) { return node.cbeginChildOff(); } + template struct NodeConverter { + typedef typename OtherNodeT::ChildOffCIter Type; + }; +}; + +template +struct IterTraits +{ + typedef typename NodeT::ChildAllIter IterT; + static IterT begin(NodeT& node) { return node.beginChildAll(); } + template static ChildT* getChild(const IterT& iter) { + typename IterT::NonConstValueType val; + return iter.probeChild(val); + } + template struct NodeConverter { + typedef typename OtherNodeT::ChildAllIter Type; + }; +}; + +template +struct IterTraits +{ + typedef typename NodeT::ChildAllCIter IterT; + static IterT begin(const NodeT& node) { return node.cbeginChildAll(); } + template static ChildT* getChild(const IterT& iter) { + typename IterT::NonConstValueType val; + return iter.probeChild(val); + } + template struct NodeConverter { + typedef typename OtherNodeT::ChildAllCIter Type; + }; +}; + +template +struct IterTraits +{ + typedef typename NodeT::ValueOnIter IterT; + static IterT begin(NodeT& node) { return node.beginValueOn(); } + template struct NodeConverter { + typedef typename OtherNodeT::ValueOnIter Type; + }; +}; + +template +struct IterTraits +{ + typedef typename NodeT::ValueOnCIter IterT; + static IterT begin(const NodeT& node) { return node.cbeginValueOn(); } + template struct NodeConverter { + typedef typename OtherNodeT::ValueOnCIter Type; + }; +}; + +template +struct IterTraits +{ + typedef typename NodeT::ValueOffIter IterT; + static IterT begin(NodeT& node) { return node.beginValueOff(); } + template struct NodeConverter { + typedef typename OtherNodeT::ValueOffIter Type; + }; +}; + +template +struct IterTraits +{ + typedef typename NodeT::ValueOffCIter IterT; + static IterT begin(const NodeT& node) { return node.cbeginValueOff(); } + template struct NodeConverter { + typedef typename OtherNodeT::ValueOffCIter Type; + }; +}; + +template +struct IterTraits +{ + typedef typename NodeT::ValueAllIter IterT; + static IterT begin(NodeT& node) { return node.beginValueAll(); } + template struct NodeConverter { + typedef typename OtherNodeT::ValueAllIter Type; + }; +}; + +template +struct IterTraits +{ + typedef typename NodeT::ValueAllCIter IterT; + static IterT begin(const NodeT& node) { return node.cbeginValueAll(); } + template 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 +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::type _NodeT; + /// The type of iterator stored in this list item (e.g., InternalNode::ValueOnCIter) + typedef typename IterTraits::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::Type ChildT; + /// NodeT's child node type with const qualifiers removed + typedef typename CopyConstness::Type NCChildT; + typedef IterTraits 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 + 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 + 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 + 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(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(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); + } + + /// @brief Apply a functor to the item to which this iterator is pointing. + /// @note Not valid when @c IterT is a const iterator type + template + void modifyValue(Index lvl, const ModifyOp& op) const + { + if (lvl == Level) mIter.modifyValue(op); else mNext.modifyValue(lvl, op); + } + +private: + typedef typename boost::mpl::pop_front::type RestT; // NodeVecT minus its first item + typedef IterListItem NextItem; + + IterT mIter; + NextItem mNext; + PrevItemT* mPrev; +}; + + +/// The initial element of a compile-time linked list of iterators to nodes of different types +template +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::type _NodeT; + /// The type of iterator stored in this list item (e.g., InternalNode::ValueOnCIter) + typedef typename IterTraits::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 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 + void setIter(const OtherIterT& iter) { mNext.setIter(iter); } + + void getNode(Index lvl, NodeT*& node) const + { + node = (lvl == 0) ? mIter.getParentNode() : NULL; + } + template + void getNode(Index lvl, OtherNodeT*& node) const { mNext.getNode(lvl, node); } + + template + 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(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); + } + + template + void modifyValue(Index lvl, const ModifyOp& op) const + { + if (lvl == 0) mIter.modifyValue(op); else mNext.modifyValue(lvl, op); + } + +private: + typedef typename boost::mpl::pop_front::type RestT; // NodeVecT minus its first item + typedef IterListItem NextItem; + + IterT mIter; + NextItem mNext; + PrevItemT* mPrev; +}; + + +/// The final element of a compile-time linked list of iterators to nodes of different types +template +class IterListItem +{ +public: + typedef typename boost::mpl::front::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::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::Type ChildT; + /// NodeT's child node type with const qualifiers removed + typedef typename CopyConstness::Type NCChildT; + typedef IterTraits 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 + 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(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(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(); } + + template + void modifyValue(Index lvl, const ModifyOp& op) const + { + if (lvl == Level) mIter.modifyValue(op); + } + +private: + IterT mIter; + PrevItemT* mPrev; +}; + + +//////////////////////////////////////// + + +//#define DEBUG_TREE_VALUE_ITERATOR + +/// @brief Base class for tree-traversal iterators over tile and voxel values +template +class TreeValueIteratorBase +{ +public: + typedef _TreeT TreeT; + typedef _ValueIterT ValueIterT; + 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 + 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); } + + /// @brief Apply a functor to the item to which this iterator is pointing. + /// (Not valid for const iterators.) + /// @param op a functor of the form void op(ValueType&) const that modifies + /// its argument in place + /// @see Tree::modifyValue() + template + void modifyValue(const ModifyOp& op) const { mValueIterList.modifyValue(mLevel, op); } + + /// 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::Type InvTreeT; + struct PrevChildItem { typedef ChildOnIterT IterT; }; + struct PrevValueItem { typedef ValueIterT IterT; }; + + IterListItem mChildIterList; + IterListItem mValueIterList; + Index mLevel; + int mMinLevel, mMaxLevel; + TreeT* mTree; +}; // class TreeValueIteratorBase + + +template +inline +TreeValueIteratorBase::TreeValueIteratorBase(TreeT& tree): + mChildIterList(NULL), + mValueIterList(NULL), + mLevel(ROOT_LEVEL), + mMinLevel(int(LEAF_LEVEL)), + mMaxLevel(int(ROOT_LEVEL)), + mTree(&tree) +{ + mChildIterList.setIter(IterTraits::begin(tree.root())); + mValueIterList.setIter(IterTraits::begin(tree.root())); + this->advance(/*dontIncrement=*/true); +} + + +template +inline +TreeValueIteratorBase::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 +inline TreeValueIteratorBase& +TreeValueIteratorBase::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 +inline void +TreeValueIteratorBase::setMinDepth(Index minDepth) +{ + mMaxLevel = int(ROOT_LEVEL - minDepth); // level = ROOT_LEVEL - depth + if (int(mLevel) > mMaxLevel) this->next(); +} + + +template +inline void +TreeValueIteratorBase::setMaxDepth(Index maxDepth) +{ + // level = ROOT_LEVEL - depth + mMinLevel = int(ROOT_LEVEL - std::min(maxDepth, this->getLeafDepth())); + if (int(mLevel) < mMinLevel) this->next(); +} + + +template +inline bool +TreeValueIteratorBase::next() +{ + do { + if (!this->advance()) return false; + } while (int(mLevel) < mMinLevel || int(mLevel) > mMaxLevel); + return true; +} + + +template +inline bool +TreeValueIteratorBase::advance(bool dontIncrement) +{ + bool recurse = false; + do { + recurse = false; + 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 just advance its child iterator. + mChildIterList.next(mLevel); + 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 +#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); + dontIncrement = true; + recurse = true; + } + } while (recurse); + return true; +} + + +template +inline bool +TreeValueIteratorBase::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 +inline std::string +TreeValueIteratorBase::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 +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::Type InvTreeT; + static const Index LEAF_LEVEL = 0, ROOT_DEPTH = 0, LEAF_DEPTH = ROOT_LEVEL; + + typedef IterTraits 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 + void getNode(NodeT*& node) const { node = NULL; mIterList.getNode(mLevel, node); } + template + void getNode(const 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 mIterList; + Index mLevel; + int mMinLevel, mMaxLevel; + bool mDone; + TreeT* mTree; +}; // class NodeIteratorBase + + +template +inline +NodeIteratorBase::NodeIteratorBase(): + mIterList(NULL), + mLevel(ROOT_LEVEL), + mMinLevel(int(LEAF_LEVEL)), + mMaxLevel(int(ROOT_LEVEL)), + mDone(true), + mTree(NULL) +{ +} + + +template +inline +NodeIteratorBase::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.root())); +} + + +template +inline +NodeIteratorBase::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 +inline NodeIteratorBase& +NodeIteratorBase::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 +inline void +NodeIteratorBase::setMinDepth(Index minDepth) +{ + mMaxLevel = int(ROOT_LEVEL - minDepth); // level = ROOT_LEVEL - depth + if (int(mLevel) > mMaxLevel) this->next(); +} + + +template +inline void +NodeIteratorBase::setMaxDepth(Index maxDepth) +{ + // level = ROOT_LEVEL - depth + mMinLevel = int(ROOT_LEVEL - std::min(maxDepth, this->getLeafDepth())); + if (int(mLevel) < mMinLevel) this->next(); +} + + +template +inline bool +NodeIteratorBase::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 +inline Coord +NodeIteratorBase::getCoord() const +{ + if (mLevel != ROOT_LEVEL) return mIterList.getCoord(mLevel + 1); + RootNodeT* root = NULL; + this->getNode(root); + return root ? root->getMinIndex() : Coord::min(); +} + + +template +inline bool +NodeIteratorBase::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 +inline std::string +NodeIteratorBase::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 +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::Type InvTreeT; + typedef typename boost::mpl::front::type NCLeafNodeT; + typedef typename CopyConstness::Type LeafNodeT; + static const Index LEAF_LEVEL = 0, LEAF_PARENT_LEVEL = LEAF_LEVEL + 1; + + typedef IterTraits 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.root())); + // 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 mIterList; + TreeT* mTree; +}; // class LeafIteratorBase + + +template +inline bool +LeafIteratorBase::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 +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-2016 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/nuparu/include/openvdb/include/openvdb/tree/ValueAccessor.h b/nuparu/include/openvdb/include/openvdb/tree/ValueAccessor.h new file mode 100644 index 00000000..87c3f41e --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/tree/ValueAccessor.h @@ -0,0 +1,2666 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tree { + +// Forward declarations of local classes that are not intended for general use +// The IsSafe template parameter is explained in the warning below. +template +class ValueAccessor0; +template +class ValueAccessor1; +template +class ValueAccessor2; +template +class ValueAccessor3; +template 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 and ValueAccessorBase. +/// +/// @warning If IsSafe = false then the ValueAccessor will not register itself +/// with the tree from which it is constructed. While in some rare cases this can +/// lead to better performance (since it avoids the small overhead of insertion +/// on creation and deletion on destruction) it is also unsafe if the tree is +/// modified. So unless you're an expert it is highly recommended to set +/// IsSafe = true, which is the default in all derived ValueAccessors defined +/// below. However if you know that the tree is no being modifed for the lifespan +/// of the ValueAccessor AND the work performed per ValueAccessor is small relative +/// to overhead of registering it you should consider setting IsSafe = false. If +/// this turns out to improve performance you should really rewrite your code so as +/// to better amortize the construction of the ValueAccessor, i.e. reuse it as much +/// as possible! +template +class ValueAccessorBase +{ +public: + static const bool IsConstTree = boost::is_const::value; + + /// @brief Return true if this accessor is safe, i.e. registered + /// by the tree from which it is constructed. Un-registered + /// accessors can in rare cases be faster because it avoids the + /// (small) overhead of registration, but they are unsafe if the + /// tree is modified. So unless you're an expert it is highly + /// recommended to set IsSafe = true (which is the default). + static bool isSafe() { return IsSafe; } + + ValueAccessorBase(TreeType& tree): mTree(&tree) + { + if (IsSafe) tree.attachAccessor(*this); + } + + virtual ~ValueAccessorBase() { if (IsSafe && mTree) mTree->releaseAccessor(*this); } + + /// @brief Return a pointer to the tree associated with this accessor. + /// @details The pointer will be null only if the tree from which this accessor + /// was constructed was subsequently deleted (which generally leaves the + /// accessor in an unsafe state). + TreeType* getTree() const { return mTree; } + /// Return a reference to the tree associated with this accessor. + TreeType& tree() const { assert(mTree); return *mTree; } + + ValueAccessorBase(const ValueAccessorBase& other): mTree(other.mTree) + { + if (IsSafe && mTree) mTree->attachAccessor(*this); + } + + ValueAccessorBase& operator=(const ValueAccessorBase& other) + { + if (&other != this) { + if (IsSafe && mTree) mTree->releaseAccessor(*this); + mTree = other.mTree; + if (IsSafe && mTree) mTree->attachAccessor(*this); + } + return *this; + } + + virtual void clear() = 0; + +protected: + // Allow trees to deregister themselves. + template 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. +/// +/// A ValueAccessor caches pointers to tree nodes along the path to a voxel (x, y, z). +/// A subsequent access to voxel (x', y', z') starts from the cached leaf node and +/// moves up until a cached node that encloses (x', y', z') is found, then traverses +/// down the tree from that node to a leaf, updating the cache with the new path. +/// This leads to significant acceleration of spatially-coherent accesses. +/// +/// @param _TreeType the type of the tree to be accessed [required] +/// @param IsSafe if IsSafe = false then the ValueAccessor will +/// not register itself with the tree from which +/// it is consturcted (see warning). +/// @param CacheLevels the number of nodes to be cached, starting from the leaf level +/// and not including the root (i.e., CacheLevels < DEPTH), +/// and defaulting to all non-root nodes +/// @param MutexType the type of mutex to use (see note) +/// +/// @warning If IsSafe = false then the ValueAccessor will not register itself +/// with the tree from which it is constructed. While in some rare cases this can +/// lead to better performance (since it avoids the small overhead of insertion +/// on creation and deletion on destruction) it is also unsafe if the tree is +/// modified. So unless you're an expert it is highly recommended to set +/// IsSafe = true, which is the default. However if you know that the tree is no +/// being modifed for the lifespan of the ValueAccessor AND the work performed +/// per ValueAccessor is small relative to overhead of registering it you should +/// consider setting IsSafe = false. If this improves performance you should +/// really rewrite your code so as to better amortize the construction of the +/// ValueAccessor, i.e. reuse it as much as possible! +/// +/// @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. +template +class ValueAccessor: public ValueAccessorBase<_TreeType, IsSafe> +{ +public: + BOOST_STATIC_ASSERT(CacheLevels < _TreeType::DEPTH); + + typedef _TreeType TreeType; + typedef typename TreeType::RootNodeType RootNodeT; + typedef typename TreeType::LeafNodeType LeafNodeT; + typedef typename RootNodeT::ValueType ValueType; + typedef ValueAccessorBase BaseT; + typedef typename MutexType::scoped_lock LockT; + using BaseT::IsConstTree; + + ValueAccessor(TreeType& tree): BaseT(tree), mCache(*this) + { + mCache.insert(Coord(), &tree.root()); + } + + 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 accessor. + 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 don't change 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); + } + + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// @details See Tree::modifyValue() for details. + template + void modifyValue(const Coord& xyz, const ModifyOp& op) + { + LockT lock(mMutex); + mCache.modifyValue(xyz, op); + } + + /// @brief Apply a functor to the voxel at the given coordinates. + /// @details See Tree::modifyValueAndActiveState() for details. + template + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) + { + LockT lock(mMutex); + mCache.modifyValueAndActiveState(xyz, op); + } + + /// Set the active state of the voxel at the given coordinates but don't change 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 but don't change its value. + void setValueOn(const Coord& xyz) { this->setActiveState(xyz, true); } + /// Mark the voxel at the given coordinates as inactive but don't change its value. + void setValueOff(const Coord& xyz) { this->setActiveState(xyz, false); } + + /// Return the cached node of type @a NodeType. [Mainly for internal use] + template + 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 + 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 + 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 voxel (x, y, z), + /// possibly deleting existing nodes or creating new nodes in the process. + void addTile(Index level, const Coord& xyz, const ValueType& value, bool state) + { + LockT lock(mMutex); + mCache.addTile(level, xyz, value, state); + } + + /// @brief Return a pointer to 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. + LeafNodeT* touchLeaf(const Coord& xyz) + { + LockT lock(mMutex); + return mCache.touchLeaf(xyz); + } + + //@{ + /// @brief Return a pointer to the node of the specified type that contains + /// voxel (x, y, z), or NULL if no such node exists. + template + NodeT* probeNode(const Coord& xyz) + { + LockT lock(mMutex); + return mCache.template probeNode(xyz); + } + template + const NodeT* probeConstNode(const Coord& xyz) const + { + LockT lock(mMutex); + return mCache.template probeConstNode(xyz); + } + template + const NodeT* probeNode(const Coord& xyz) const + { + return this->template probeConstNode(xyz); + } + //@} + + //@{ + /// @brief Return a pointer to the leaf node that contains voxel (x, y, z), + /// or NULL if no such node exists. + LeafNodeT* probeLeaf(const Coord& xyz) + { + LockT lock(mMutex); + return mCache.probeLeaf(xyz); + } + 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->root())); + } + +private: + // Allow nodes to insert themselves into the cache. + template friend class RootNode; + template friend class InternalNode; + template friend class LeafNode; + // Allow trees to deregister themselves. + template 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 + 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 RootNodeT::NodeChainType InvTreeT; + // Remove all tree node types that are excluded from the cache + typedef typename boost::mpl::begin::type BeginT; + typedef typename boost::mpl::advance >::type FirstT; + typedef typename boost::mpl::find::type LastT; + typedef typename boost::mpl::erase::type SubtreeT; + typedef CacheItem::value==1> CacheItemT; + + // Private member data + mutable CacheItemT mCache; + mutable MutexType mMutex; + +}; // class ValueAccessor + + +/// @brief Template specialization of the ValueAccessor with no mutex and no cache levels +/// @details This specialization is provided mainly for benchmarking. +/// Accessors with caching will almost always be faster. +template +class ValueAccessor + : public ValueAccessor0 +{ +public: + ValueAccessor(TreeType& tree): ValueAccessor0(tree) {} + ValueAccessor(const ValueAccessor& other): ValueAccessor0(other) {} + virtual ~ValueAccessor() {} +}; + + +/// Template specialization of the ValueAccessor with no mutex and one cache level +template +class ValueAccessor + : public ValueAccessor1 +{ +public: + ValueAccessor(TreeType& tree): ValueAccessor1(tree) {} + ValueAccessor(const ValueAccessor& other): ValueAccessor1(other) {} + virtual ~ValueAccessor() {} +}; + + +/// Template specialization of the ValueAccessor with no mutex and two cache levels +template +class ValueAccessor + : public ValueAccessor2 +{ +public: + ValueAccessor(TreeType& tree): ValueAccessor2(tree) {} + ValueAccessor(const ValueAccessor& other): ValueAccessor2(other) {} + virtual ~ValueAccessor() {} +}; + + +/// Template specialization of the ValueAccessor with no mutex and three cache levels +template +class ValueAccessor + : public ValueAccessor3 +{ +public: + ValueAccessor(TreeType& tree): ValueAccessor3(tree) {} + ValueAccessor(const ValueAccessor& other): ValueAccessor3(other) {} + virtual ~ValueAccessor() {} +}; + + +//////////////////////////////////////// + + +/// @brief 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. +/// +/// @warning Since the mutex-locking employed by the ValueAccessorRW +/// can seriously impair performance of multithreaded applications, it +/// is recommended that, instead, each thread be assigned its own +/// (non-mutex protected) accessor. +template +class ValueAccessorRW: public ValueAccessor +{ +public: + ValueAccessorRW(TreeType& tree) + : ValueAccessor(tree) + { + } +}; + + +//////////////////////////////////////// + + +// +// The classes below are for internal use and should rarely be used directly. +// + +// An element of a compile-time linked list of node pointers, ordered from LeafNode to RootNode +template +class CacheItem +{ +public: + typedef typename boost::mpl::front::type NodeType; + typedef typename NodeType::ValueType ValueType; + typedef typename NodeType::LeafNodeType LeafNodeType; + typedef std::numeric_limits 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 + 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 + 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(mNode); + } + /// Forward the request to another level of the cache. + template + 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(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(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(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(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); + } + + template + NodeT* probeNode(const Coord& xyz) + { + BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (this->isHashed(xyz)) { + if ((boost::is_same::value)) { + assert(mNode); + return reinterpret_cast(const_cast(mNode)); + } + return const_cast(mNode)->template probeNodeAndCache(xyz, *mParent); + } + return mNext.template probeNode(xyz); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + + template + const NodeT* probeConstNode(const Coord& xyz) + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (this->isHashed(xyz)) { + if ((boost::is_same::value)) { + assert(mNode); + return reinterpret_cast(mNode); + } + return mNode->template probeConstNodeAndCache(xyz, *mParent); + } + return mNext.template probeConstNode(xyz); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + + /// 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(TreeCacheT::RootNodeT::LEVEL) - + static_cast(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(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(mNode)->setValueOnlyAndCache(xyz, value, *mParent); + } else { + mNext.setValueOnly(xyz, value); + } + } + void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); } + + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// @details See Tree::modifyValue() for details. + template + void modifyValue(const Coord& xyz, const ModifyOp& op) + { + if (this->isHashed(xyz)) { + assert(mNode); + BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree); + const_cast(mNode)->modifyValueAndCache(xyz, op, *mParent); + } else { + mNext.modifyValue(xyz, op); + } + } + + /// @brief Apply a functor to the voxel at the given coordinates. + /// @details See Tree::modifyValueAndActiveState() for details. + template + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) + { + if (this->isHashed(xyz)) { + assert(mNode); + BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree); + const_cast(mNode)->modifyValueAndActiveStateAndCache(xyz, op, *mParent); + } else { + mNext.modifyValueAndActiveState(xyz, op); + } + } + + /// 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(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(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::type RestT; // NodeVecT minus its first item + CacheItem::value == 1> mNext; +};// end of CacheItem + + +/// The tail of a compile-time list of cached node pointers, ordered from LeafNode to RootNode +template +class CacheItem +{ +public: + typedef typename boost::mpl::front::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 + 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(mRoot); + } + void getNode(const RootNodeType*& node) const { node = mRoot; } + + void addLeaf(LeafNodeType* leaf) + { + assert(mRoot); + BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree); + const_cast(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(mRoot)->addTileAndCache(level, xyz, value, state, *mParent); + } + + LeafNodeType* touchLeaf(const Coord& xyz) + { + assert(mRoot); + BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree); + return const_cast(mRoot)->touchLeafAndCache(xyz, *mParent); + } + + LeafNodeType* probeLeaf(const Coord& xyz) + { + assert(mRoot); + BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree); + return const_cast(mRoot)->probeLeafAndCache(xyz, *mParent); + } + + const LeafNodeType* probeConstLeaf(const Coord& xyz) + { + assert(mRoot); + return mRoot->probeConstLeafAndCache(xyz, *mParent); + } + + template + NodeType* probeNode(const Coord& xyz) + { + assert(mRoot); + BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree); + return const_cast(mRoot)->template probeNodeAndCache(xyz, *mParent); + } + + template + const NodeType* probeConstNode(const Coord& xyz) + { + assert(mRoot); + return mRoot->template probeConstNodeAndCache(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(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(mRoot)->setValueAndCache(xyz, value, *mParent); + } + void setValueOnly(const Coord& xyz, const ValueType& value) + { + assert(mRoot); + BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree); + const_cast(mRoot)->setValueOnlyAndCache(xyz, value, *mParent); + } + void setValueOn(const Coord& xyz, const ValueType& value) { this->setValue(xyz, value); } + + template + void modifyValue(const Coord& xyz, const ModifyOp& op) + { + assert(mRoot); + BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree); + const_cast(mRoot)->modifyValueAndCache(xyz, op, *mParent); + } + + template + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) + { + assert(mRoot); + BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree); + const_cast(mRoot)->modifyValueAndActiveStateAndCache(xyz, op, *mParent); + } + + void setValueOff(const Coord& xyz, const ValueType& value) + { + assert(mRoot); + BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree); + const_cast(mRoot)->setValueOffAndCache(xyz, value, *mParent); + } + + void setActiveState(const Coord& xyz, bool on) + { + assert(mRoot); + BOOST_STATIC_ASSERT(!TreeCacheT::IsConstTree); + const_cast(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. +/// @details This specialization is provided mainly for benchmarking. +/// Accessors with caching will almost always be faster. +template +class ValueAccessor0: public ValueAccessorBase<_TreeType, IsSafe> +{ +public: + typedef _TreeType TreeType; + typedef typename TreeType::ValueType ValueType; + typedef typename TreeType::RootNodeType RootNodeT; + typedef typename TreeType::LeafNodeType LeafNodeT; + typedef ValueAccessorBase BaseT; + + ValueAccessor0(TreeType& tree): BaseT(tree) {} + + ValueAccessor0(const ValueAccessor0& other): BaseT(other) {} + + /// Return the number of cache levels employed by this accessor. + 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 and, in @a value, the value of the voxel at the given coordinates. + 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(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 don't change 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->root().setValueOff(xyz, value); + } + + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// @details See Tree::modifyValue() for details. + template + void modifyValue(const Coord& xyz, const ModifyOp& op) + { + assert(BaseT::mTree); + BOOST_STATIC_ASSERT(!BaseT::IsConstTree); + BaseT::mTree->modifyValue(xyz, op); + } + + /// @brief Apply a functor to the voxel at the given coordinates. + /// @details See Tree::modifyValueAndActiveState() for details. + template + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) + { + assert(BaseT::mTree); + BOOST_STATIC_ASSERT(!BaseT::IsConstTree); + BaseT::mTree->modifyValueAndActiveState(xyz, op); + } + + /// Set the active state of the voxel at the given coordinates but don't change 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 but don't change its value. + void setValueOn(const Coord& xyz) { this->setActiveState(xyz, true); } + /// Mark the voxel at the given coordinates as inactive but don't change its value. + void setValueOff(const Coord& xyz) { this->setActiveState(xyz, false); } + + /// Return the cached node of type @a NodeType. [Mainly for internal use] + template 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 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 voxel (x, y, z), + /// possibly deleting existing nodes or creating new nodes in the process. + 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 void eraseNode() {} + + LeafNodeT* touchLeaf(const Coord& xyz) + { + assert(BaseT::mTree); + BOOST_STATIC_ASSERT(!BaseT::IsConstTree); + return BaseT::mTree->touchLeaf(xyz); + } + + template + NodeT* probeNode(const Coord& xyz) + { + assert(BaseT::mTree); + BOOST_STATIC_ASSERT(!BaseT::IsConstTree); + return BaseT::mTree->template probeNode(xyz); + } + + template + const NodeT* probeConstNode(const Coord& xyz) const + { + assert(BaseT::mTree); + return BaseT::mTree->template probeConstNode(xyz); + } + + LeafNodeT* probeLeaf(const Coord& xyz) + { + return this->template probeNode(xyz); + } + + const LeafNodeT* probeConstLeaf(const Coord& xyz) const + { + return this->template probeConstNode(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 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 +class ValueAccessor1 : public ValueAccessorBase<_TreeType, IsSafe> +{ +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 BaseT; + typedef typename RootNodeT::NodeChainType InvTreeT; + typedef typename boost::mpl::at >::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->root().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->root().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->root().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->root().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->root().getValueDepthAndCache(xyz, this->self()) == + static_cast(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(mNode0)->setValueAndCache(xyz, value, *this); + } else { + BaseT::mTree->root().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(mNode0)->setValueOnlyAndCache(xyz, value, *this); + } else { + BaseT::mTree->root().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(mNode0)->setValueOffAndCache(xyz, value, *this); + } else { + BaseT::mTree->root().setValueOffAndCache(xyz, value, *this); + } + } + + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// @details See Tree::modifyValue() for details. + template + void modifyValue(const Coord& xyz, const ModifyOp& op) + { + assert(BaseT::mTree); + BOOST_STATIC_ASSERT(!BaseT::IsConstTree); + if (this->isHashed(xyz)) { + assert(mNode0); + const_cast(mNode0)->modifyValueAndCache(xyz, op, *this); + } else { + BaseT::mTree->root().modifyValueAndCache(xyz, op, *this); + } + } + + /// @brief Apply a functor to the voxel at the given coordinates. + /// @details See Tree::modifyValueAndActiveState() for details. + template + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) + { + assert(BaseT::mTree); + BOOST_STATIC_ASSERT(!BaseT::IsConstTree); + if (this->isHashed(xyz)) { + assert(mNode0); + const_cast(mNode0)->modifyValueAndActiveStateAndCache(xyz, op, *this); + } else { + BaseT::mTree->root().modifyValueAndActiveStateAndCache(xyz, op, *this); + } + } + + /// Set the active state of the voxel at the given coordinates but don't change 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(mNode0)->setActiveStateAndCache(xyz, on, *this); + } else { + BaseT::mTree->root().setActiveStateAndCache(xyz, on, *this); + } + } + /// Mark the voxel at the given coordinates as active but don't change its value. + void setValueOn(const Coord& xyz) { this->setActiveState(xyz, true); } + /// Mark the voxel at the given coordinates as inactive but don't change its value. + void setValueOff(const Coord& xyz) { this->setActiveState(xyz, false); } + + /// Return the cached node of type @a NodeType. [Mainly for internal use] + template + NodeT* getNode() + { + const NodeT* node = NULL; + this->getNode(node); + return const_cast(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 + 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 + 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 voxel (x, y, z), + /// possibly deleting existing nodes or creating new nodes in the process. + 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(mNode0)->touchLeafAndCache(xyz, *this); + } + return BaseT::mTree->root().touchLeafAndCache(xyz, *this); + } + + /// @brief @return a pointer to the node of the specified type that contains + /// voxel (x, y, z) and if it doesn't exist, return NULL. + template + NodeT* probeNode(const Coord& xyz) + { + assert(BaseT::mTree); + BOOST_STATIC_ASSERT(!BaseT::IsConstTree); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if ((boost::is_same::value)) { + if (this->isHashed(xyz)) { + assert(mNode0); + return reinterpret_cast(const_cast(mNode0)); + } + return BaseT::mTree->root().template probeNodeAndCache(xyz, *this); + } + return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + LeafNodeT* probeLeaf(const Coord& xyz) + { + return this->template probeNode(xyz); + } + + /// @brief @return a const pointer to the nodeof the specified type that contains + /// voxel (x, y, z) and if it doesn't exist, return NULL. + template + const NodeT* probeConstNode(const Coord& xyz) const + { + assert(BaseT::mTree); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if ((boost::is_same::value)) { + if (this->isHashed(xyz)) { + assert(mNode0); + return reinterpret_cast(mNode0); + } + return BaseT::mTree->root().template probeConstNodeAndCache(xyz, this->self()); + } + return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + const LeafNodeT* probeConstLeaf(const Coord& xyz) const + { + return this->template probeConstNode(xyz); + } + 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 friend class RootNode; + template friend class InternalNode; + template friend class LeafNode; + // Allow trees to deregister themselves. + template friend class Tree; + + // This private method is merely for convenience. + inline ValueAccessor1& self() const { return const_cast(*this); } + + void getNode(const NodeT0*& node) { node = mNode0; } + void getNode(const RootNodeT*& node) + { + node = (BaseT::mTree ? &BaseT::mTree->root() : NULL); + } + template void getNode(const OtherNodeType*& node) { node = NULL; } + void eraseNode(const NodeT0*) { mKey0 = Coord::max(); mNode0 = NULL; } + template 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 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 +class ValueAccessor2 : public ValueAccessorBase<_TreeType, IsSafe> +{ +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 BaseT; + typedef typename RootNodeT::NodeChainType InvTreeT; + typedef typename boost::mpl::at >::type NodeT0; + typedef typename boost::mpl::at >::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->root().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->root().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->root().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->root().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->root().getValueDepthAndCache(xyz, this->self()) == + static_cast(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(mNode0)->setValueAndCache(xyz, value, *this); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + const_cast(mNode1)->setValueAndCache(xyz, value, *this); + } else { + BaseT::mTree->root().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(mNode0)->setValueOnlyAndCache(xyz, value, *this); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + const_cast(mNode1)->setValueOnlyAndCache(xyz, value, *this); + } else { + BaseT::mTree->root().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(mNode0)->setValueOffAndCache(xyz, value, *this); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + const_cast(mNode1)->setValueOffAndCache(xyz, value, *this); + } else { + BaseT::mTree->root().setValueOffAndCache(xyz, value, *this); + } + } + + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// @details See Tree::modifyValue() for details. + template + void modifyValue(const Coord& xyz, const ModifyOp& op) + { + assert(BaseT::mTree); + BOOST_STATIC_ASSERT(!BaseT::IsConstTree); + if (this->isHashed0(xyz)) { + assert(mNode0); + const_cast(mNode0)->modifyValueAndCache(xyz, op, *this); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + const_cast(mNode1)->modifyValueAndCache(xyz, op, *this); + } else { + BaseT::mTree->root().modifyValueAndCache(xyz, op, *this); + } + } + + /// @brief Apply a functor to the voxel at the given coordinates. + /// @details See Tree::modifyValueAndActiveState() for details. + template + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) + { + assert(BaseT::mTree); + BOOST_STATIC_ASSERT(!BaseT::IsConstTree); + if (this->isHashed0(xyz)) { + assert(mNode0); + const_cast(mNode0)->modifyValueAndActiveStateAndCache(xyz, op, *this); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + const_cast(mNode1)->modifyValueAndActiveStateAndCache(xyz, op, *this); + } else { + BaseT::mTree->root().modifyValueAndActiveStateAndCache(xyz, op, *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(mNode0)->setActiveStateAndCache(xyz, on, *this); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + const_cast(mNode1)->setActiveStateAndCache(xyz, on, *this); + } else { + BaseT::mTree->root().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 + NodeT* getNode() + { + const NodeT* node = NULL; + this->getNode(node); + return const_cast(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 + 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 + 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(mNode1)->addLeafAndCache(leaf, *this); + } + BaseT::mTree->root().addLeafAndCache(leaf, *this); + } + + /// @brief Add a tile at the specified tree level that contains voxel (x, y, z), + /// possibly deleting existing nodes or creating new nodes in the process. + 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(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(mNode0)->touchLeafAndCache(xyz, *this); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + return const_cast(mNode1)->touchLeafAndCache(xyz, *this); + } + return BaseT::mTree->root().touchLeafAndCache(xyz, *this); + } + /// @brief @return a pointer to the node of the specified type that contains + /// voxel (x, y, z) and if it doesn't exist, return NULL. + template + NodeT* probeNode(const Coord& xyz) + { + assert(BaseT::mTree); + BOOST_STATIC_ASSERT(!BaseT::IsConstTree); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if ((boost::is_same::value)) { + if (this->isHashed0(xyz)) { + assert(mNode0); + return reinterpret_cast(const_cast(mNode0)); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + return const_cast(mNode1)->template probeNodeAndCache(xyz, *this); + } + return BaseT::mTree->root().template probeNodeAndCache(xyz, *this); + } else if ((boost::is_same::value)) { + if (this->isHashed1(xyz)) { + assert(mNode1); + return reinterpret_cast(const_cast(mNode1)); + } + return BaseT::mTree->root().template probeNodeAndCache(xyz, *this); + } + return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + /// @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) { return this->template probeNode(xyz); } + + /// @brief @return a const pointer to the node of the specified type that contains + /// voxel (x, y, z) and if it doesn't exist, return NULL. + template + const NodeT* probeConstLeaf(const Coord& xyz) const + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if ((boost::is_same::value)) { + if (this->isHashed0(xyz)) { + assert(mNode0); + return reinterpret_cast(mNode0); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + return mNode1->template probeConstNodeAndCache(xyz, this->self()); + } + return BaseT::mTree->root().template probeConstNodeAndCache(xyz, this->self()); + } else if ((boost::is_same::value)) { + if (this->isHashed1(xyz)) { + assert(mNode1); + return reinterpret_cast(mNode1); + } + return BaseT::mTree->root().template probeConstNodeAndCache(xyz, this->self()); + } + return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + /// @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 + { + return this->template probeConstNode(xyz); + } + const LeafNodeT* probeLeaf(const Coord& xyz) const { return this->probeConstLeaf(xyz); } + + /// @brief @return a const pointer to the node of the specified type that contains + /// voxel (x, y, z) and if it doesn't exist, return NULL. + template + const NodeT* probeConstNode(const Coord& xyz) const + { + assert(BaseT::mTree); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if ((boost::is_same::value)) { + if (this->isHashed0(xyz)) { + assert(mNode0); + return reinterpret_cast(mNode0); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + return mNode1->template probeConstNodeAndCache(xyz, this->self()); + } + return BaseT::mTree->root().template probeConstNodeAndCache(xyz, this->self()); + } else if ((boost::is_same::value)) { + if (this->isHashed1(xyz)) { + assert(mNode1); + return reinterpret_cast(mNode1); + } + return BaseT::mTree->root().template probeConstNodeAndCache(xyz, this->self()); + } + return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + + /// 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 friend class RootNode; + template friend class InternalNode; + template friend class LeafNode; + // Allow trees to deregister themselves. + template friend class Tree; + + // This private method is merely for convenience. + inline ValueAccessor2& self() const { return const_cast(*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->root() : NULL); + } + template 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 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 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 +class ValueAccessor3 : public ValueAccessorBase<_TreeType, IsSafe> +{ +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 BaseT; + typedef typename RootNodeT::NodeChainType InvTreeT; + typedef typename boost::mpl::at >::type NodeT0; + typedef typename boost::mpl::at >::type NodeT1; + typedef typename boost::mpl::at >::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->root().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->root().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->root().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->root().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->root().getValueDepthAndCache(xyz, this->self()) == + static_cast(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(mNode0)->setValueAndCache(xyz, value, *this); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + const_cast(mNode1)->setValueAndCache(xyz, value, *this); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + const_cast(mNode2)->setValueAndCache(xyz, value, *this); + } else { + BaseT::mTree->root().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(mNode0)->setValueOnlyAndCache(xyz, value, *this); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + const_cast(mNode1)->setValueOnlyAndCache(xyz, value, *this); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + const_cast(mNode2)->setValueOnlyAndCache(xyz, value, *this); + } else { + BaseT::mTree->root().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(mNode0)->setValueOffAndCache(xyz, value, *this); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + const_cast(mNode1)->setValueOffAndCache(xyz, value, *this); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + const_cast(mNode2)->setValueOffAndCache(xyz, value, *this); + } else { + BaseT::mTree->root().setValueOffAndCache(xyz, value, *this); + } + } + + /// @brief Apply a functor to the value of the voxel at the given coordinates + /// and mark the voxel as active. + /// @details See Tree::modifyValue() for details. + template + void modifyValue(const Coord& xyz, const ModifyOp& op) + { + assert(BaseT::mTree); + BOOST_STATIC_ASSERT(!BaseT::IsConstTree); + if (this->isHashed0(xyz)) { + assert(mNode0); + const_cast(mNode0)->modifyValueAndCache(xyz, op, *this); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + const_cast(mNode1)->modifyValueAndCache(xyz, op, *this); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + const_cast(mNode2)->modifyValueAndCache(xyz, op, *this); + } else { + BaseT::mTree->root().modifyValueAndCache(xyz, op, *this); + } + } + + /// @brief Apply a functor to the voxel at the given coordinates. + /// @details See Tree::modifyValueAndActiveState() for details. + template + void modifyValueAndActiveState(const Coord& xyz, const ModifyOp& op) + { + assert(BaseT::mTree); + BOOST_STATIC_ASSERT(!BaseT::IsConstTree); + if (this->isHashed0(xyz)) { + assert(mNode0); + const_cast(mNode0)->modifyValueAndActiveStateAndCache(xyz, op, *this); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + const_cast(mNode1)->modifyValueAndActiveStateAndCache(xyz, op, *this); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + const_cast(mNode2)->modifyValueAndActiveStateAndCache(xyz, op, *this); + } else { + BaseT::mTree->root().modifyValueAndActiveStateAndCache(xyz, op, *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(mNode0)->setActiveStateAndCache(xyz, on, *this); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + const_cast(mNode1)->setActiveStateAndCache(xyz, on, *this); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + const_cast(mNode2)->setActiveStateAndCache(xyz, on, *this); + } else { + BaseT::mTree->root().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 + NodeT* getNode() + { + const NodeT* node = NULL; + this->getNode(node); + return const_cast(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 + 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 + 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(mNode1)->addLeafAndCache(leaf, *this); + } else if (this->isHashed2(leaf->origin())) { + assert(mNode2); + return const_cast(mNode2)->addLeafAndCache(leaf, *this); + } + BaseT::mTree->root().addLeafAndCache(leaf, *this); + } + + /// @brief Add a tile at the specified tree level that contains voxel (x, y, z), + /// possibly deleting existing nodes or creating new nodes in the process. + 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(mNode1)->addTileAndCache(level, xyz, value, state, *this); + } if (this->isHashed2(xyz)) { + assert(mNode2); + return const_cast(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(mNode0); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + return const_cast(mNode1)->touchLeafAndCache(xyz, *this); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + return const_cast(mNode2)->touchLeafAndCache(xyz, *this); + } + return BaseT::mTree->root().touchLeafAndCache(xyz, *this); + } + /// @brief @return a pointer to the node of the specified type that contains + /// voxel (x, y, z) and if it doesn't exist, return NULL. + template + NodeT* probeNode(const Coord& xyz) + { + assert(BaseT::mTree); + BOOST_STATIC_ASSERT(!BaseT::IsConstTree); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if ((boost::is_same::value)) { + if (this->isHashed0(xyz)) { + assert(mNode0); + return reinterpret_cast(const_cast(mNode0)); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + return const_cast(mNode1)->template probeNodeAndCache(xyz, *this); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + return const_cast(mNode2)->template probeNodeAndCache(xyz, *this); + } + return BaseT::mTree->root().template probeNodeAndCache(xyz, *this); + } else if ((boost::is_same::value)) { + if (this->isHashed1(xyz)) { + assert(mNode1); + return reinterpret_cast(const_cast(mNode1)); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + return const_cast(mNode2)->template probeNodeAndCache(xyz, *this); + } + return BaseT::mTree->root().template probeNodeAndCache(xyz, *this); + } else if ((boost::is_same::value)) { + if (this->isHashed2(xyz)) { + assert(mNode2); + return reinterpret_cast(const_cast(mNode2)); + } + return BaseT::mTree->root().template probeNodeAndCache(xyz, *this); + } + return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + /// @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) { return this->template probeNode(xyz); } + + /// @brief @return a const pointer to the node of the specified type that contains + /// voxel (x, y, z) and if it doesn't exist, return NULL. + template + const NodeT* probeConstNode(const Coord& xyz) const + { + assert(BaseT::mTree); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if ((boost::is_same::value)) { + if (this->isHashed0(xyz)) { + assert(mNode0); + return reinterpret_cast(mNode0); + } else if (this->isHashed1(xyz)) { + assert(mNode1); + return mNode1->template probeConstNodeAndCache(xyz, this->self()); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + return mNode2->template probeConstNodeAndCache(xyz, this->self()); + } + return BaseT::mTree->root().template probeConstNodeAndCache(xyz, this->self()); + } else if ((boost::is_same::value)) { + if (this->isHashed1(xyz)) { + assert(mNode1); + return reinterpret_cast(mNode1); + } else if (this->isHashed2(xyz)) { + assert(mNode2); + return mNode2->template probeConstNodeAndCache(xyz, this->self()); + } + return BaseT::mTree->root().template probeConstNodeAndCache(xyz, this->self()); + } else if ((boost::is_same::value)) { + if (this->isHashed2(xyz)) { + assert(mNode2); + return reinterpret_cast(mNode2); + } + return BaseT::mTree->root().template probeConstNodeAndCache(xyz, this->self()); + } + return NULL; + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + /// @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 + { + return this->template probeConstNode(xyz); + } + 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 friend class RootNode; + template friend class InternalNode; + template friend class LeafNode; + // Allow trees to deregister themselves. + template friend class Tree; + + // This private method is merely for convenience. + inline ValueAccessor3& self() const { return const_cast(*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->root() : NULL); + } + template 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 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 + 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-2016 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/nuparu/include/openvdb/include/openvdb/util/CpuTimer.h b/nuparu/include/openvdb/include/openvdb/util/CpuTimer.h new file mode 100644 index 00000000..f3c5e5d9 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/util/CpuTimer.h @@ -0,0 +1,128 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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_CPUTIMER_HAS_BEEN_INCLUDED +#define OPENVDB_UTIL_CPUTIMER_HAS_BEEN_INCLUDED + +#include +#include +#include // for ostringstream +#include //for setprecision + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace util { + +/// @brief Simple timer for basic profiling. +/// +/// @code +/// CpuTimer timer; +/// // code here will not be timed! +/// timer.start("algorithm"); +/// // code to be timed goes here +/// timer.stop(); +/// @endcode +/// +/// or to time multiple blocks of code +/// +/// @code +/// CpuTimer timer("algorithm 1"); +/// // code to be timed goes here +/// timer.restart("algorithm 2"); +/// // code to be timed goes here +/// timer.stop(); +/// @endcode +class CpuTimer +{ +public: + + /// @brief Initiate timer + CpuTimer() : mT0(tbb::tick_count::now()) {} + + /// @brief Prints message and re-start timer. + /// + /// @note Should normally be followed by a call to stop() + CpuTimer(const std::string& msg) { this->start(msg); } + + /// @brief Start timer. + /// + /// @note Should normally be followed by a call to time() + inline void start() { mT0 = tbb::tick_count::now(); } + + /// @brief Print message and re-start timer. + /// + /// @note Should normally be followed by a call to stop() + inline void start(const std::string& msg) + { + std::cerr << msg << " ... "; + this->start(); + } + + /// @brief Stop previous timer, print message and re-start timer. + /// + /// @note Should normally be followed by a call to stop() + inline void restart(const std::string& msg) + { + this->stop(); + this->start(msg); + } + + /// Return Time diference in milliseconds since construction or start was called. + inline double delta() const + { + tbb::tick_count::interval_t dt = tbb::tick_count::now() - mT0; + return 1000.0*dt.seconds(); + } + + /// @brief Print time in milliseconds since construction or start was called. + inline void stop() const + { + const double t = this->delta(); + std::ostringstream ostr; + ostr << "completed in " << std::setprecision(3) << t << " ms\n"; + std::cerr << ostr.str(); + } + +private: + + tbb::tick_count mT0; +};// CpuTimer + +} // namespace util +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + + +#endif // OPENVDB_UTIL_CPUTIMER_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/util/Formats.h b/nuparu/include/openvdb/include/openvdb/util/Formats.h new file mode 100644 index 00000000..52a8306e --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/util/Formats.h @@ -0,0 +1,140 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include +#include +#include +#include + + +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 +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 +std::ostream& operator<<(std::ostream& os, const FormattedInt& n) { return n.put(os); } + +/// @return an I/O manipulator that formats the given integer value for output to a stream. +template +FormattedInt formattedInt(IntT n) { return FormattedInt(n); } + +} // namespace util +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_UTIL_FORMATS_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/util/MapsUtil.h b/nuparu/include/openvdb/include/openvdb/util/MapsUtil.h new file mode 100644 index 00000000..6a21ecdd --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/util/MapsUtil.h @@ -0,0 +1,321 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 + + +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 +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 +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::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 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(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(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(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())); + + extreme.x() = xm; + extreme.y() = centerLS.y(); + extreme.z() = zm; + // location in world space of the tangent point + extreme2 = secondMap.applyMap(extreme); + + // 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())); + + extreme.x() = centerLS.x(); + extreme.y() = xm; + extreme.z() = zm; + extreme2 = secondMap.applyMap(extreme); + + // 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-2016 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/nuparu/include/openvdb/include/openvdb/util/Name.h b/nuparu/include/openvdb/include/openvdb/util/Name.h new file mode 100644 index 00000000..b5df425f --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/util/Name.h @@ -0,0 +1,72 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include +#include +#include +#include + +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(&size), sizeof(uint32_t)); + std::string buffer(size, ' '); + if (size>0) 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(&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-2016 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/nuparu/include/openvdb/include/openvdb/util/NodeMasks.h b/nuparu/include/openvdb/include/openvdb/util/NodeMasks.h new file mode 100644 index 00000000..8c525a51 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/util/NodeMasks.h @@ -0,0 +1,1411 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include +#include // for cout +#include +#include +//#include +//#include // 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: +#ifndef _MSC_VER // Visual C++ doesn't guarantee thread-safe initialization of local statics + static +#endif + 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(static_cast(~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 static_cast( + (((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); +#ifndef _MSC_VER // Visual C++ doesn't guarantee thread-safe initialization of local statics + static +#endif + 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); +#ifndef _MSC_VER // Visual C++ doesn't guarantee thread-safe initialization of local statics + static +#endif + 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); +#ifndef _MSC_VER // Visual C++ doesn't guarantee thread-safe initialization of local statics + static +#endif + 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) +{ +#ifndef _MSC_VER // Visual C++ doesn't guarantee thread-safe initialization of local statics + static +#endif + 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 +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;} + BaseMaskIterator& operator=(const BaseMaskIterator& iter) + { + mPos = iter.mPos; mParent = iter.mParent; return *this; + } + 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 +class OnMaskIterator: public BaseMaskIterator +{ +private: + typedef BaseMaskIterator 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 +class OffMaskIterator: public BaseMaskIterator +{ +private: + typedef BaseMaskIterator 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 +class DenseMaskIterator: public BaseMaskIterator +{ +private: + typedef BaseMaskIterator 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 +class NodeMask +{ +public: + BOOST_STATIC_ASSERT( Log2Dim>2 ); + + static const Index32 LOG2DIM = Log2Dim; + static const Index32 DIM = 1<> 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::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 + 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; + } + + typedef OnMaskIterator OnIterator; + typedef OffMaskIterator OffIterator; + typedef DenseMaskIterator 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 + // + + /// @brief Apply a functor to the words of the this and the other mask. + /// + /// @details An example that implements the "operator&=" method: + /// @code + /// struct Op { inline void operator()(W &w1, const W& w2) const { w1 &= w2; } }; + /// @endcode + template + const NodeMask& foreach(const NodeMask& other, const WordOp& op) + { + Word *w1 = mWords; + const Word *w2 = other.mWords; + for (Index32 n = WORD_COUNT; n--; ++w1, ++w2) op( *w1, *w2); + return *this; + } + template + const NodeMask& foreach(const NodeMask& other1, const NodeMask& other2, const WordOp& op) + { + Word *w1 = mWords; + const Word *w2 = other1.mWords, *w3 = other2.mWords; + for (Index32 n = WORD_COUNT; n--; ++w1, ++w2, ++w3) op( *w1, *w2, *w3); + return *this; + } + template + const NodeMask& foreach(const NodeMask& other1, const NodeMask& other2, const NodeMask& other3, + const WordOp& op) + { + Word *w1 = mWords; + const Word *w2 = other1.mWords, *w3 = other2.mWords, *w4 = other3.mWords; + for (Index32 n = WORD_COUNT; n--; ++w1, ++w2, ++w3, ++w4) op( *w1, *w2, *w3, *w4); + return *this; + } + /// @brief Bitwise intersection + const NodeMask& operator&=(const NodeMask& other) + { + Word *w1 = mWords; + const Word *w2 = other.mWords; + for (Index32 n = WORD_COUNT; n--; ++w1, ++w2) *w1 &= *w2; + return *this; + } + /// @brief Bitwise union + const NodeMask& operator|=(const NodeMask& other) + { + Word *w1 = mWords; + const Word *w2 = other.mWords; + for (Index32 n = WORD_COUNT; n--; ++w1, ++w2) *w1 |= *w2; + return *this; + } + /// @brief Bitwise difference + const NodeMask& operator-=(const NodeMask& other) + { + Word *w1 = mWords; + const Word *w2 = other.mWords; + for (Index32 n = WORD_COUNT; n--; ++w1, ++w2) *w1 &= ~*w2; + return *this; + } + /// @brief Bitwise XOR + const NodeMask& operator^=(const NodeMask& other) + { + Word *w1 = mWords; + const Word *w2 = other.mWords; + for (Index32 n = WORD_COUNT; n--; ++w1, ++w2) *w1 ^= *w2; + return *this; + } + NodeMask operator!() const { NodeMask m(*this); m.toggle(); 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; } + NodeMask operator^(const NodeMask& other) const { NodeMask m(*this); m ^= other; return m; } + + /// Return the byte size of this NodeMask + static Index32 memUsage() { return static_cast(WORD_COUNT*sizeof(Word)); } + /// 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 nth bit on + void setOn(Index32 n) { + assert( (n >> 6) < WORD_COUNT ); + mWords[n >> 6] |= Word(1) << (n & 63); + } + /// Set the nth bit off + void setOff(Index32 n) { + assert( (n >> 6) < WORD_COUNT ); + mWords[n >> 6] &= ~(Word(1) << (n & 63)); + } + /// Set the nth 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 nth bit + void toggle(Index32 n) { + assert( (n >> 6) < WORD_COUNT ); + mWords[n >> 6] ^= Word(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 nth 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 nth 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 (; nn
th word of the bit mask, for a word of arbitrary size. + template + WordT getWord(Index n) const + { + assert(n*8*sizeof(WordT) < SIZE); + return reinterpret_cast(mWords)[n]; + } + template + WordT& getWord(Index n) + { + assert(n*8*sizeof(WordT) < SIZE); + return reinterpret_cast(mWords)[n]; + } + //@} + + void save(std::ostream& os) const + { + os.write(reinterpret_cast(mWords), this->memUsage()); + } + void load(std::istream& is) { + is.read(reinterpret_cast(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> 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 +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 OnIterator; + typedef OffMaskIterator OffIterator; + typedef DenseMaskIterator 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 + // + + /// @brief Apply a functor to the words of the this and the other mask. + /// + /// @details An example that implements the "operator&=" method: + /// @code + /// struct Op { inline void operator()(Word &w1, const Word& w2) const { w1 &= w2; } }; + /// @endcode + template + const NodeMask& foreach(const NodeMask& other, const WordOp& op) + { + op(mByte, other.mByte); + return *this; + } + template + const NodeMask& foreach(const NodeMask& other1, const NodeMask& other2, const WordOp& op) + { + op(mByte, other1.mByte, other2.mByte); + return *this; + } + template + const NodeMask& foreach(const NodeMask& other1, const NodeMask& other2, const NodeMask& other3, + const WordOp& op) + { + op(mByte, other1.mByte, other2.mByte, other3.mByte); + return *this; + } + /// @brief Bitwise intersection + const NodeMask& operator&=(const NodeMask& other) + { + mByte &= other.mByte; + return *this; + } + /// @brief Bitwise union + const NodeMask& operator|=(const NodeMask& other) + { + mByte |= other.mByte; + return *this; + } + /// @brief Bitwise difference + const NodeMask& operator-=(const NodeMask& other) + { + mByte &= static_cast(~other.mByte); + return *this; + } + /// @brief Bitwise XOR + const NodeMask& operator^=(const NodeMask& other) + { + mByte ^= other.mByte; + return *this; + } + NodeMask operator!() const { NodeMask m(*this); m.toggle(); 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; } + 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 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 nth bit on + void setOn(Index32 n) { + assert( n < 8 ); + mByte = mByte | static_cast(0x01U << (n & 7)); + } + /// Set the nth bit off + void setOff(Index32 n) { + assert( n < 8 ); + mByte = mByte & static_cast(~(0x01U << (n & 7))); + } + /// Set the nth 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 nth bit + void toggle(Index32 n) { + assert( n < 8 ); + mByte = mByte ^ static_cast(0x01U << (n & 7)); + } + /// Toggle the state of all bits in the mask + void toggle() { mByte = static_cast(~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 nth bit is on + bool isOn(Index32 n) const + { + assert( n < 8 ); + return mByte & (0x01U << (n & 7)); + } + /// Return true if the nth 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 = static_cast(~mByte); + return b ? FindLowestOn(b) : 8; + } + /* + //@{ + /// Return the nth word of the bit mask, for a word of arbitrary size. + /// @note This version assumes WordT=Byte and n=0! + template + WordT getWord(Index n) const + { + BOOST_STATIC_ASSERT(sizeof(WordT) == sizeof(Byte)); + assert(n == 0); + return reinterpret_cast(mByte); + } + template + WordT& getWord(Index n) + { + BOOST_STATIC_ASSERT(sizeof(WordT) == sizeof(Byte)); + assert(n == 0); + return reinterpret_cast(mByte); + } + //@} + */ + void save(std::ostream& os) const + { + os.write(reinterpret_cast(&mByte), 1); + } + void load(std::istream& is) { is.read(reinterpret_cast(&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"<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 = static_cast(mByte & (0xFFU << start)); + return b ? FindLowestOn(b) : 8; + } + + Index32 findNextOff(Index32 start) const + { + if (start>=8) return 8; + const Byte b = static_cast(~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 OnIterator; + typedef OffMaskIterator OffIterator; + typedef DenseMaskIterator 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 + // + + /// @brief Apply a functor to the words of the this and the other mask. + /// + /// @details An example that implements the "operator&=" method: + /// @code + /// struct Op { inline void operator()(Word &w1, const Word& w2) const { w1 &= w2; } }; + /// @endcode + template + const NodeMask& foreach(const NodeMask& other, const WordOp& op) + { + op(mWord, other.mWord); + return *this; + } + template + const NodeMask& foreach(const NodeMask& other1, const NodeMask& other2, const WordOp& op) + { + op(mWord, other1.mWord, other2.mWord); + return *this; + } + template + const NodeMask& foreach(const NodeMask& other1, const NodeMask& other2, const NodeMask& other3, + const WordOp& op) + { + op(mWord, other1.mWord, other2.mWord, other3.mWord); + return *this; + } + /// @brief Bitwise intersection + const NodeMask& operator&=(const NodeMask& other) + { + mWord &= other.mWord; + return *this; + } + /// @brief Bitwise union + const NodeMask& operator|=(const NodeMask& other) + { + mWord |= other.mWord; + return *this; + } + /// @brief Bitwise difference + const NodeMask& operator-=(const NodeMask& other) + { + mWord &= ~other.mWord; + return *this; + } + /// @brief Bitwise XOR + const NodeMask& operator^=(const NodeMask& other) + { + mWord ^= other.mWord; + return *this; + } + NodeMask operator!() const { NodeMask m(*this); m.toggle(); 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; } + 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 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 nth bit on + void setOn(Index32 n) { + assert( n < 64 ); + mWord |= UINT64_C(0x01) << (n & 63); + } + /// Set the nth bit off + void setOff(Index32 n) { + assert( n < 64 ); + mWord &= ~(UINT64_C(0x01) << (n & 63)); + } + /// Set the nth 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 nth 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 nth bit is on + bool isOn(Index32 n) const + { + assert( n < 64 ); + return 0 != (mWord & (UINT64_C(0x01) << (n & 63))); + } + /// Return true if the nth 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 nth word of the bit mask, for a word of arbitrary size. + template + WordT getWord(Index n) const + { + assert(n*8*sizeof(WordT) < SIZE); + return reinterpret_cast(&mWord)[n]; + } + template + WordT& getWord(Index n) + { + assert(n*8*sizeof(WordT) < SIZE); + return reinterpret_cast(mWord)[n]; + } + //@} + void save(std::ostream& os) const + { + os.write(reinterpret_cast(&mWord), 8); + } + void load(std::istream& is) { is.read(reinterpret_cast(&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"<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>5)+1; + delete [] mBits; + mBits = new Index32[mIntSize]; + for (Index32 i=0; igetBitSize()), 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;} + BaseIterator& operator=(const BaseIterator& iter) { + mPos = iter.mPos; + mBitSize = iter.mBitSize; + mParent = iter.mParent; + return *this; + } + + 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; inext(); ++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; inext(); ++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; inext(); ++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*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>5) < mIntSize); + mBits[i>>5] ^= 1<<(i&31); + } + void toggle() { + assert(mBits); + for (Index32 i=0; isetOn(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; iisOn(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<> 5, m = start & 31;//initiate + if (n>=mIntSize) return mBitSize; // check for out of bounds + Index32 b = ~mBits[n]; + if (b & (1<(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-2016 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/nuparu/include/openvdb/include/openvdb/util/NullInterrupter.h b/nuparu/include/openvdb/include/openvdb/util/NullInterrupter.h new file mode 100644 index 00000000..8022a723 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/util/NullInterrupter.h @@ -0,0 +1,90 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 + +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 +inline bool wasInterrupted(T* i, int percent = -1) { return i && i->wasInterrupted(percent); } + +/// Specialization for NullInterrupter +template<> +inline bool wasInterrupted(util::NullInterrupter*, int) { return false; } + +} // namespace util +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_UTIL_NULL_INTERRUPTER_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/util/PagedArray.h b/nuparu/include/openvdb/include/openvdb/util/PagedArray.h new file mode 100644 index 00000000..0fbef832 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/util/PagedArray.h @@ -0,0 +1,735 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 PagedArray.h +/// +/// @author Ken Museth +/// +/// @brief Concurrent page-based linear data structure with O(1) +/// random access and std-compliant iterators. It is +/// primarily intended for applications that involve +/// multi-threading of dynamically growing linear arrays with +/// fast random access. + +#ifndef OPENVDB_UTIL_PAGED_ARRAY_HAS_BEEN_INCLUDED +#define OPENVDB_UTIL_PAGED_ARRAY_HAS_BEEN_INCLUDED + + +#include +#include +#include +#include // std::swap +#include +#include +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace util { + +//////////////////////////////////////// + + +/// @brief Concurrent page-based linear data structure with O(1) +/// random access and std-compliant iterators. It is +/// primarily intended for applications that involve +/// multi-threading of dynamically growing linear arrays with +/// fast random access. +/// +/// @note Multiple threads can grow the page-table and push_back +/// new elements concurrently. A ValueBuffer provides accelerated +/// and threadsafe push_back at the cost of potentially re-ordering +/// elements (when multiple instances are used). +/// +/// @details This data structure employes contiguous pages of elements +/// (like a std::deque) which avoids moving data when the +/// capacity is out-grown and new pages are allocated. The +/// size of the pages can be controlled with the Log2PageSize +/// template parameter (defaults to 1024 elements of type ValueT). +/// +/// There are three fundamentally different ways to insert elements to +/// this container - each with different advanteges and disadvanteges. +/// +/// The simplest way to insert elements is to use PagedArray::push_back e.g. +/// @code +/// PagedArray array; +/// for (int i=0; i<100000; ++i) array.push_back(i); +/// @endcode +/// or with tbb task-based multi-threading +/// @code +/// struct Functor1 { +/// Functor1(int n, PagedArray& _array) : array(&_array) { +/// tbb::parallel_for(tbb::blocked_range(0, n, PagedArray::pageSize()), *this); +/// } +/// void operator()(const tbb::blocked_range& r) const { +/// for (int i=r.begin(), n=r.end(); i!=n; ++i) array->push_back(i); +/// } +/// PagedArray* array; +/// }; +/// PagedArray array; +/// Functor1 tmp(10000, array); +/// @endcode +/// PagedArray::push_back has the advantage that it's thread-safe and +/// preserves the ordering of the inserted elements. In fact it returns +/// the linear offset to the added element which can then be used for +/// fast O(1) random access. The disadvantage is it's the slowest of +/// the three different ways of inserting elements. +/// +/// The fastest way (by far) to insert elements is to use one (or +/// more) instances of a PagedArray::ValueBuffer, e.g. +/// @code +/// PagedArray array; +/// PagedArray::ValueBuffer buffer(array); +/// for (int i=0; i<100000; ++i) buffer.push_back(i); +/// buffer.flush(); +/// @endcode +/// or +/// @code +/// PagedArray array; +/// {//local scope of a single thread +/// PagedArray::ValueBuffer buffer(array); +/// for (int i=0; i<100000; ++i) buffer.push_back(i); +/// } +/// @endcode +/// or with tbb task-based multi-threading +/// @code +/// struct Functor2 { +/// Functor2(int n, PagedArray& array) : buffer(array) { +/// tbb::parallel_for(tbb::blocked_range(0, n, PagedArray::pageSize()), *this); +/// } +/// void operator()(const tbb::blocked_range& r) const { +/// for (int i=r.begin(), n=r.end(); i!=n; ++i) buffer.push_back(i); +/// } +/// mutable typename PagedArray::ValueBuffer buffer; +/// }; +/// PagedArray array; +/// Functor2 tmp(10000, array); +/// @endcode +/// or with tbb Thread Local Storage for even better performance (due +/// to fewer concurrent instantiations of partially full ValueBuffers) +/// @code +/// struct Functor3 { +/// typedef tbb::enumerable_thread_specific::ValueBuffer> PoolType; +/// Functor3(size_t n, PoolType& _pool) : pool(&_pool) { +/// tbb::parallel_for(tbb::blocked_range(0, n, PagedArray::pageSize()), *this); +/// } +/// void operator()(const tbb::blocked_range& r) const { +/// PagedArray::ValueBuffer& buffer = pool->local(); +/// for (int i=r.begin(), n=r.end(); i!=n; ++i) buffer.push_back(i); +/// } +/// PoolType* pool; +/// }; +/// PagedArray array; +/// PagedArray::ValueBuffer exemplar(array);//dummy used for initialization +/// Functor3::PoolType pool(exemplar);//thread local storage pool of ValueBuffers +/// Functor3 tmp(10000, pool); +/// for (Functor3::PoolType::iterator i=pool.begin(); i!=pool.end(); ++i) i->flush(); +/// @endcode +/// This technique generally outperforms PagedArray::push_back, +/// std::vector::push_back, std::deque::push_back and even +/// tbb::concurrent_vector::push_back. Additionally it +/// is thread-safe as long as each thread has it's own instance of a +/// PagedArray::ValueBuffer. The only disadvantage is the ordering of +/// the elements is undefined if multiple instance of a +/// PagedArray::ValueBuffer are employed. This is typically the case +/// in the context of multi-threading, where the +/// ordering of inserts are undefined anyway. Note that a local scope +/// can be used to guarentee that the ValueBuffer has inerted all its +/// elements by the time the scope ends. Alternatively the ValueBuffer +/// can be explicitly flushed by calling ValueBuffer::flush. +/// +/// The third way to insert elements is to resize the container and use +/// random access, e.g. +/// @code +/// PagedArray array; +/// array.resize(100000); +/// for (int i=0; i<100000; ++i) array[i] = i; +/// @endcode +/// or in terms of the random access iterator +/// @code +/// PagedArray array; +/// array.resize(100000); +/// for (PagedArray::Iterator i=array.begin(); i!=array.end(); ++i) *i = i.pos(); +/// @endcode +/// While this approach is both fast and thread-safe it suffers from the +/// major disadvantage that the problem size, i.e. number of elements, needs to +/// be known in advance. If that's the case you might as well consider +/// using std::vector or a raw c-style array! In other words the +/// PagedArray is most useful in the context of applications that +/// involve multi-threading of dynamically growing linear arrays that +/// require fast random access. +template +class PagedArray { + + private: + class Page; + typedef std::deque PageTableT; + + public: + typedef ValueT ValueType; + + /// @brief Default constructor + PagedArray() : mPageTable(), mSize(), mCapacity(0), mGrowthMutex() { mSize = 0; } + + /// @brief Destructor removed all allocated pages + ~PagedArray() { this->clear(); } + + /// @brief Caches values into a local memory Page to improve + /// performance of push_back into a PagedArray. + /// + /// @note The ordering of inserted elements is undefined when + /// multiple ValueBuffers are used! + /// + /// @warning By design this ValueBuffer is not threadsafe so + /// make sure to create an instance per thread! + class ValueBuffer; + + /// Const std-compliant iterator + class ConstIterator; + + /// Non-const std-compliant iterator + class Iterator; + + /// @brief Thread safe insertion, adds a new element at + /// the end and increases the container size by one. + /// + /// @note Constant time complexity. May allocate a new page. + size_t push_back(const ValueType& value) + { + const size_t index = mSize.fetch_and_increment(); + if (index >= mCapacity) this->grow(index); + (*mPageTable[index >> Log2PageSize])[index] = value; + return index; + } + + /// @brief Slightly faster then the thread-safe push_back above. + /// + /// @note For best performance consider using the ValueBuffer! + /// + /// @warning Not thread-safe! + size_t push_back_unsafe(const ValueType& value) + { + const size_t index = mSize.fetch_and_increment(); + if (index >= mCapacity) { + mPageTable.push_back( new Page() ); + mCapacity += Page::Size; + } + (*mPageTable[index >> Log2PageSize])[index] = value; + return index; + } + + /// @brief Returns the last element, decrements the size by one. + /// + /// @details Consider subsequnetly calling shrink_to_fit to + /// reduce the page table to match the new size. + /// + /// @note Calling this method on an empty containter is + /// undefined (as is also the case for std containers). + /// + /// @warning If values were added to the container by means of + /// multiple ValueBuffers the last value might not be what you + /// expect since the ordering is generally not perserved. Only + /// PagedArray::push_back preserves the ordering (or a single + /// instance of a ValueBuffer). + ValueType pop_back() + { + assert(mSize>0); + --mSize; + return (*mPageTable[mSize >> Log2PageSize])[mSize]; + } + + /// @brief Reduce the page table to fix the current size. + /// + /// @warning Not thread-safe! + void shrink_to_fit(); + + /// @brief Return a reference to the value at the specified offset + /// + /// @note This random access has constant time complexity. + /// + /// @warning It is assumed that the i'th element is already allocated! + ValueType& operator[](size_t i) + { + assert(i>Log2PageSize])[i]; + } + + /// @brief Return a const-reference to the value at the specified offset + /// + /// @note This random access has constant time complexity. + /// + /// @warning It is assumed that the i'th element is already allocated! + const ValueType& operator[](size_t i) const + { + assert(i>Log2PageSize])[i]; + } + + /// @brief Set all elements to the specified value + void fill(const ValueType& v) + { + tbb::spin_mutex::scoped_lock lock(mGrowthMutex); + Fill tmp(this, v); + } + + /// @brief Resize this array to the specified size. + /// + /// @note This will grow or shrink the page table. + /// + /// @warning Not thread-safe! + void resize(size_t size) + { + mSize = size; + if (size > mCapacity) { + this->grow(size-1); + } else { + this->shrink_to_fit(); + } + } + + /// @brief Resize this array to the specified size and + /// set all elements to the specified value. + /// + /// @warning Not thread-safe! + void resize(size_t size, const ValueType& v) + { + this->resize(size); + this->fill(v); + } + + /// @brief Return the number of elements in this array. + size_t size() const { return mSize; } + + /// @brief Return the maximum number of elements that this array + /// can contain without allocating more memory pages. + size_t capacity() const { return mCapacity; } + + /// @brief Return the number of additional elements that can be + /// added to this array without allocating more memory pages. + size_t freeCount() const { return mCapacity - mSize; } + + /// @brief Return the number of allocated memory pages. + size_t pageCount() const { return mPageTable.size(); } + + /// @brief Return the number of elements per memory page. + static size_t pageSize() { return Page::Size; } + + /// @brief Return log2 of the number of elements per memory page. + static size_t log2PageSize() { return Log2PageSize; } + + /// @brief Return the memory footprint of this array in bytes. + size_t memUsage() const + { + return sizeof(*this) + mPageTable.size() * Page::memUsage(); + } + + /// @brief Return true if the container contains no elements. + bool isEmpty() const { return mSize == 0; } + + /// @brief Return true if the page table is partially full, i.e. the + /// last non-empty page contains less than pageSize() elements. + /// + /// @details When the page table is partially full calling merge() + /// or using a ValueBuffer will rearrange the ordering of + /// existing elements. + bool isPartiallyFull() const { return (mSize & Page::Mask) > 0; } + + /// @brief Removes all elements from the array and delete all pages. + /// + /// @warning Not thread-safe! + void clear() + { + tbb::spin_mutex::scoped_lock lock(mGrowthMutex); + for (size_t i=0, n=mPageTable.size(); ibegin(), this->end(), std::less() ); } + + /// @brief Parallel sort of all the elements in descending order. + void invSort() { tbb::parallel_sort(this->begin(), this->end(), std::greater()); } + + /// @brief Parallel sort of all the elements based on a custom + /// functor with the api: + /// @code bool operator()(const ValueT& a, const ValueT& b) @endcode + /// which returns true if a comes before b. + template + void sort() { tbb::parallel_sort(this->begin(), this->end(), Functor() ); } + + /// @brief Transfer all the elements (and pages) from the other array to this array. + /// + /// @note The other PagedArray is empty on return. + /// + /// @warning The ordering of elements is undefined if this page table is partially full! + void merge(PagedArray& other); + + /// @brief Print information for debugging + void print(std::ostream& os = std::cout) const + { + os << "PagedArray:\n" + << "\tSize: " << this->size() << " elements\n" + << "\tPage table: " << this->pageCount() << " pages\n" + << "\tPage size: " << this->pageSize() << " elements\n" + << "\tCapacity: " << this->capacity() << " elements\n" + << "\tFootrpint: " << this->memUsage() << " bytes\n"; + } + +private: + // Disallow copy construction and assignment + PagedArray(const PagedArray&);//not implemented + void operator=(const PagedArray&);//not implemented + + friend class ValueBuffer; + + // Private class for concurrent fill + struct Fill; + + void grow(size_t index) + { + tbb::spin_mutex::scoped_lock lock(mGrowthMutex); + while(index >= mCapacity) { + mPageTable.push_back( new Page() ); + mCapacity += Page::Size; + } + } + + void add_full(Page*& page, size_t size); + + void add_partially_full(Page*& page, size_t size); + + void add(Page*& page, size_t size) { + tbb::spin_mutex::scoped_lock lock(mGrowthMutex); + if (size == Page::Size) {//page is full + this->add_full(page, size); + } else if (size>0) {//page is only partially full + this->add_partially_full(page, size); + } + } + PageTableT mPageTable;//holds points to allocated pages + tbb::atomic mSize;// current number of elements in array + size_t mCapacity;//capacity of array given the current page count + tbb::spin_mutex mGrowthMutex;//Mutex-lock required to grow pages +}; // Public class PagedArray + +//////////////////////////////////////////////////////////////////////////////// + +template +void PagedArray::shrink_to_fit() +{ + if (mPageTable.size() > (mSize >> Log2PageSize) + 1) { + tbb::spin_mutex::scoped_lock lock(mGrowthMutex); + const size_t pageCount = (mSize >> Log2PageSize) + 1; + if (mPageTable.size() > pageCount) { + delete mPageTable.back(); + mPageTable.pop_back(); + mCapacity -= Page::Size; + } + } +} + +template +void PagedArray::merge(PagedArray& other) +{ + if (!other.isEmpty()) { + tbb::spin_mutex::scoped_lock lock(mGrowthMutex); + // extract last partially full page if it exists + Page* page = NULL; + const size_t size = mSize & Page::Mask; //number of elements in the last page + if ( size > 0 ) { + page = mPageTable.back(); + mPageTable.pop_back(); + mSize -= size; + } + // transfer all pages from the other page table + mPageTable.insert(mPageTable.end(), other.mPageTable.begin(), other.mPageTable.end()); + mSize += other.mSize; + mCapacity = Page::Size*mPageTable.size(); + other.mSize = 0; + other.mCapacity = 0; + PageTableT().swap(other.mPageTable); + // add back last partially full page + if (page) this->add_partially_full(page, size); + } +} + +template +void PagedArray::add_full(Page*& page, size_t size) +{ + assert(size == Page::Size);//page must be full + if (mSize & Page::Mask) {//page-table is partially full + Page*& tmp = mPageTable.back(); + std::swap(tmp, page);//swap last table entry with page + } + mPageTable.push_back( page ); + mCapacity += Page::Size; + mSize += size; + page = NULL; +} + +template +void PagedArray::add_partially_full(Page*& page, size_t size) +{ + assert(size > 0 && size < Page::Size);//page must be partially full + if (size_t m = mSize & Page::Mask) {//page table is also partially full + ValueT *s = page->data(), *t = mPageTable.back()->data() + m; + for (size_t i=std::min(mSize+size, mCapacity)-mSize; i; --i) *t++ = *s++; + if (mSize+size > mCapacity) {//grow page table + mPageTable.push_back( new Page() ); + t = mPageTable.back()->data(); + for (size_t i=mSize+size-mCapacity; i; --i) *t++ = *s++; + mCapacity += Page::Size; + } + } else {//page table is full so simply append page + mPageTable.push_back( page ); + mCapacity += Page::Size; + page = NULL; + } + mSize += size; +} + +//////////////////////////////////////////////////////////////////////////////// + +// Public member-class of PagedArray +template +class PagedArray:: +ValueBuffer +{ +public: + typedef PagedArray PagedArrayType; + /// @brief Constructor from a PageArray + ValueBuffer(PagedArray& parent) : mParent(&parent), mPage(new Page()), mSize(0) {} + /// @warning This copy-constructor is shallow in the sense that no + /// elements are copied, i.e. size = 0. + ValueBuffer(const ValueBuffer& other) : mParent(other.mParent), mPage(new Page()), mSize(0) {} + /// @brief Destructor that transfers an buffered values to the parent PagedArray. + ~ValueBuffer() { this->flush(); delete mPage; } + /// @brief Add a value to the buffer and increment the size. + /// + /// @details If the internal memory page is full it will + /// automaically flush the page to the parent PagedArray. + void push_back(const ValueT& v) { + (*mPage)[mSize++] = v; + if (mSize == Page::Size) this->flush(); + } + /// @brief Manually transfer the values in this buffer to the parent PagedArray. + /// + /// @note This method is also called by the destructor and + /// puach_back so it should only be called when manually want to + /// sync up the buffer with the array, e.g. during debugging. + void flush() { + mParent->add(mPage, mSize); + if (mPage == NULL) mPage = new Page(); + mSize = 0; + } + /// @brief Return a reference to the parent PagedArray + PagedArrayType& parent() const { return *mParent; } + /// @brief Return the current number of elements cached in this buffer. + size_t size() const { return mSize; } +private: + ValueBuffer& operator=(const ValueBuffer& other);//not implemented + PagedArray* mParent; + Page* mPage; + size_t mSize; +};// Public class PagedArray::ValueBuffer + +//////////////////////////////////////////////////////////////////////////////// + +// Const std-compliant iterator +// Public member-class of PagedArray +template +class PagedArray:: +ConstIterator : public std::iterator +{ +public: + typedef std::iterator BaseT; + typedef typename BaseT::difference_type difference_type; + // constructors and assignment + ConstIterator() : mPos(0), mParent(NULL) {} + ConstIterator(const PagedArray& parent, size_t pos=0) : mPos(pos), mParent(&parent) {} + ConstIterator(const ConstIterator& other) : mPos(other.mPos), mParent(other.mParent) {} + ConstIterator& operator=(const ConstIterator& other) { + mPos=other.mPos; + mParent=other.mParent; + return *this; + } + // prefix + ConstIterator& operator++() { ++mPos; return *this; } + ConstIterator& operator--() { --mPos; return *this; } + // postfix + ConstIterator operator++(int) { ConstIterator tmp(*this); ++mPos; return tmp; } + ConstIterator operator--(int) { ConstIterator tmp(*this); --mPos; return tmp; } + // value access + const ValueT& operator*() const { return (*mParent)[mPos]; } + const ValueT* operator->() const { return &(this->operator*()); } + const ValueT& operator[](const difference_type& pos) const { return (*mParent)[mPos+pos]; } + // offset + ConstIterator& operator+=(const difference_type& pos) { mPos += pos; return *this; } + ConstIterator& operator-=(const difference_type& pos) { mPos -= pos; return *this; } + ConstIterator operator+(const difference_type &pos) const { return Iterator(*mParent,mPos+pos); } + ConstIterator operator-(const difference_type &pos) const { return Iterator(*mParent,mPos-pos); } + difference_type operator-(const ConstIterator& other) const { return mPos - other.pos(); } + // comparisons + bool operator==(const ConstIterator& other) const { return mPos == other.mPos; } + bool operator!=(const ConstIterator& other) const { return mPos != other.mPos; } + bool operator>=(const ConstIterator& other) const { return mPos >= other.mPos; } + bool operator<=(const ConstIterator& other) const { return mPos <= other.mPos; } + bool operator< (const ConstIterator& other) const { return mPos < other.mPos; } + bool operator> (const ConstIterator& other) const { return mPos > other.mPos; } + // non-std methods + bool isValid() const { return mParent != NULL && mPos < mParent->size(); } + size_t pos() const { return mPos; } +private: + size_t mPos; + const PagedArray* mParent; +};// Public class PagedArray::ConstIterator + +//////////////////////////////////////////////////////////////////////////////// + +// Public member-class of PagedArray +template +class PagedArray:: +Iterator : public std::iterator +{ +public: + typedef std::iterator BaseT; + typedef typename BaseT::difference_type difference_type; + // constructors and assignment + Iterator() : mPos(0), mParent(NULL) {} + Iterator(PagedArray& parent, size_t pos=0) : mPos(pos), mParent(&parent) {} + Iterator(const Iterator& other) : mPos(other.mPos), mParent(other.mParent) {} + Iterator& operator=(const Iterator& other) { + mPos=other.mPos; + mParent=other.mParent; + return *this; + } + // prefix + Iterator& operator++() { ++mPos; return *this; } + Iterator& operator--() { --mPos; return *this; } + // postfix + Iterator operator++(int) { Iterator tmp(*this); ++mPos; return tmp; } + Iterator operator--(int) { Iterator tmp(*this); --mPos; return tmp; } + // value access + ValueT& operator*() const { return (*mParent)[mPos]; } + ValueT* operator->() const { return &(this->operator*()); } + ValueT& operator[](const difference_type& pos) const { return (*mParent)[mPos+pos]; } + // offset + Iterator& operator+=(const difference_type& pos) { mPos += pos; return *this; } + Iterator& operator-=(const difference_type& pos) { mPos -= pos; return *this; } + Iterator operator+(const difference_type &pos) const { return Iterator(*mParent, mPos+pos); } + Iterator operator-(const difference_type &pos) const { return Iterator(*mParent, mPos-pos); } + difference_type operator-(const Iterator& other) const { return mPos - other.pos(); } + // comparisons + bool operator==(const Iterator& other) const { return mPos == other.mPos; } + bool operator!=(const Iterator& other) const { return mPos != other.mPos; } + bool operator>=(const Iterator& other) const { return mPos >= other.mPos; } + bool operator<=(const Iterator& other) const { return mPos <= other.mPos; } + bool operator< (const Iterator& other) const { return mPos < other.mPos; } + bool operator> (const Iterator& other) const { return mPos > other.mPos; } + // non-std methods + bool isValid() const { return mParent != NULL && mPos < mParent->size(); } + size_t pos() const { return mPos; } + private: + size_t mPos; + PagedArray* mParent; +};// Public class PagedArray::Iterator + +//////////////////////////////////////////////////////////////////////////////// + +// Private member-class of PagedArray implementing a memory page +template +class PagedArray:: +Page +{ +public: + static const size_t Size = 1UL << Log2PageSize; + static const size_t Mask = Size - 1UL; + static size_t memUsage() { return sizeof(ValueT)*Size; } + Page() : mData(new ValueT[Size]) {} + ~Page() { delete [] mData; } + ValueT& operator[](const size_t i) { return mData[i & Mask]; } + const ValueT& operator[](const size_t i) const { return mData[i & Mask]; } + void fill(const ValueT& v) { ValueT* p = mData; for (size_t i=Size; i; --i) *p++ = v; } + ValueT* data() { return mData; } +protected: + Page(const Page& other);//copy construction is not implemented + Page& operator=(const Page& rhs);//copy assignment is not implemented + ValueT* mData; +};// Private class PagedArray::Page + +//////////////////////////////////////////////////////////////////////////////// + +// Private member-class of PagedArray implementing concurrent fill of a Page +template +struct PagedArray:: +Fill { + Fill(PagedArray* _d, const ValueT& _v) : d(_d), v(_v) { + tbb::parallel_for(tbb::blocked_range(0, d->pageCount()), *this); + } + void operator()(const tbb::blocked_range& r) const { + for (size_t i=r.begin(); i!=r.end(); ++i) d->mPageTable[i]->fill(v); + } + PagedArray* d; + const ValueT& v; +};// Private class PagedArray::Fill + +} // namespace util +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_UTIL_PAGED_ARRAY_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/util/Util.h b/nuparu/include/openvdb/include/openvdb/util/Util.h new file mode 100644 index 00000000..2751fcbc --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/util/Util.h @@ -0,0 +1,166 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 +#include +#include +#include // for tree::pruneInactive + + +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 LeafTopologyIntOp +{ +public: + LeafTopologyIntOp(const TreeType2& tree): mOtherTree(&tree) {} + + inline void operator()(const typename TreeType1::LeafIter& lIter) const + { + const Coord xyz = lIter->origin(); + const typename TreeType2::LeafNodeType* leaf = mOtherTree->probeConstLeaf(xyz); + if (leaf) {//leaf node + lIter->topologyIntersection(*leaf, zeroVal()); + } 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 LeafTopologyDiffOp +{ +public: + LeafTopologyDiffOp(const TreeType2& tree): mOtherTree(&tree) {} + + inline void operator()(const typename TreeType1::LeafIter& lIter) const + { + const Coord xyz = lIter->origin(); + const typename TreeType2::LeafNodeType* leaf = mOtherTree->probeConstLeaf(xyz); + if (leaf) {//leaf node + lIter->topologyDifference(*leaf, zeroVal()); + } 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 +inline typename TreeType1::template ValueConverter::Type::Ptr +leafTopologyIntersection(const TreeType1& lhs, const TreeType2& rhs, bool threaded = true) +{ + typedef typename TreeType1::template ValueConverter::Type BoolTreeType; + + typename BoolTreeType::Ptr topologyTree(new BoolTreeType( + lhs, /*inactiveValue=*/false, /*activeValue=*/true, TopologyCopy())); + + tools::foreach(topologyTree->beginLeaf(), + LeafTopologyIntOp(rhs), threaded); + + tools::pruneInactive(*topologyTree, threaded); + 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 +inline typename TreeType1::template ValueConverter::Type::Ptr +leafTopologyDifference(const TreeType1& lhs, const TreeType2& rhs, bool threaded = true) +{ + typedef typename TreeType1::template ValueConverter::Type BoolTreeType; + + typename BoolTreeType::Ptr topologyTree(new BoolTreeType( + lhs, /*inactiveValue=*/false, /*activeValue=*/true, TopologyCopy())); + + tools::foreach(topologyTree->beginLeaf(), + LeafTopologyDiffOp(rhs), threaded); + + tools::pruneInactive(*topologyTree, threaded); + return topologyTree; +} + +} // namespace util +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_UTIL_UTIL_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/util/logging.h b/nuparu/include/openvdb/include/openvdb/util/logging.h new file mode 100644 index 00000000..3d5cff79 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/util/logging.h @@ -0,0 +1,83 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 + +/// Log an info message of the form 'someVar << "some text" << ...'. +#define OPENVDB_LOG_INFO(message) +/// Log a warning message of the form 'someVar << "some text" << ...'. +#define OPENVDB_LOG_WARN(message) do { std::cerr << message << std::endl; } while (0); +/// Log an error message of the form 'someVar << "some text" << ...'. +#define OPENVDB_LOG_ERROR(message) do { std::cerr << message << std::endl; } while (0); +/// Log a fatal error message of the form 'someVar << "some text" << ...'. +#define OPENVDB_LOG_FATAL(message) do { std::cerr << message << std::endl; } while (0); +/// In debug builds only, log a debugging message of the form 'someVar << "text" << ...'. +#define OPENVDB_LOG_DEBUG(message) +/// @brief Log a debugging message in both debug and optimized builds. +/// @warning Don't use this in performance-critical code. +#define OPENVDB_LOG_DEBUG_RUNTIME(message) + +#else // ifdef OPENVDB_USE_LOG4CPLUS + +#include +#include +#include + +#define OPENVDB_LOG(level, message) \ + do { \ + log4cplus::Logger _log = log4cplus::Logger::getInstance(LOG4CPLUS_TEXT("main")); \ + if (_log.isEnabledFor(log4cplus::level##_LOG_LEVEL)) { \ + std::ostringstream _buf; \ + _buf << message; \ + _log.forcedLog(log4cplus::level##_LOG_LEVEL, _buf.str(), __FILE__, __LINE__); \ + } \ + } while (0); + +#define OPENVDB_LOG_INFO(message) OPENVDB_LOG(INFO, message) +#define OPENVDB_LOG_WARN(message) OPENVDB_LOG(WARN, message) +#define OPENVDB_LOG_ERROR(message) OPENVDB_LOG(ERROR, message) +#define OPENVDB_LOG_FATAL(message) OPENVDB_LOG(FATAL, message) +#ifdef DEBUG +#define OPENVDB_LOG_DEBUG(message) OPENVDB_LOG(DEBUG, message) +#else +#define OPENVDB_LOG_DEBUG(message) +#endif +#define OPENVDB_LOG_DEBUG_RUNTIME(message) OPENVDB_LOG(DEBUG, message) + +#endif // OPENVDB_USE_LOG4CPLUS + +#endif // OPENVDB_UTIL_LOGGING_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb/version.h b/nuparu/include/openvdb/include/openvdb/version.h new file mode 100644 index 00000000..adc3d8ee --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb/version.h @@ -0,0 +1,133 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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" + + +/// The version namespace name for this library version +/// +/// Fully-namespace-qualified symbols are named as follows: +/// openvdb::vX_Y_Z::Vec3i, openvdb::vX_Y_Z::io::File, openvdb::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 v3_2_0 + +// Library major, minor and patch version numbers +#define OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER 3 +#define OPENVDB_LIBRARY_MINOR_VERSION_NUMBER 2 +#define OPENVDB_LIBRARY_PATCH_VERSION_NUMBER 0 + +/// @brief Library version number string of the form ".." +/// @details This is a macro rather than a static constant because we typically +/// want the compile-time version number, not the runtime version number +/// (although the two are usually the same). +#define OPENVDB_LIBRARY_VERSION_STRING "3.2.0" + +/// Library version number as a packed integer ("%02x%02x%04x", major, minor, patch) +#define OPENVDB_LIBRARY_VERSION_NUMBER \ + ((OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER << 24) | \ + ((OPENVDB_LIBRARY_MINOR_VERSION_NUMBER & 0xFF) << 16) | \ + (OPENVDB_LIBRARY_PATCH_VERSION_NUMBER & 0xFFFF)) + +/// If OPENVDB_REQUIRE_VERSION_NAME is undefined, symbols from the version +/// namespace are promoted to the top-level namespace (e.g., openvdb::v1_0_0::io::File +/// can be referred to simply as openvdb::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; + +// Library major, minor and patch version numbers +const uint32_t + OPENVDB_LIBRARY_MAJOR_VERSION = OPENVDB_LIBRARY_MAJOR_VERSION_NUMBER, + OPENVDB_LIBRARY_MINOR_VERSION = OPENVDB_LIBRARY_MINOR_VERSION_NUMBER, + OPENVDB_LIBRARY_PATCH_VERSION = OPENVDB_LIBRARY_PATCH_VERSION_NUMBER; +/// Library version number as a packed integer ("%02x%02x%04x", major, minor, patch) +const uint32_t OPENVDB_LIBRARY_VERSION = OPENVDB_LIBRARY_VERSION_NUMBER; + +/// @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 = 223; + +/// 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, + OPENVDB_FILE_VERSION_BLOSC_COMPRESSION = 223, + OPENVDB_FILE_VERSION_POINT_INDEX_GRID = 223 +}; + + +/// Return a library version number string of the form "..". +inline const char* getLibraryVersionString() { return OPENVDB_LIBRARY_VERSION_STRING; } + + +struct VersionId { + uint32_t first, second; + VersionId(): first(0), second(0) {} + VersionId(uint32_t major, uint32_t minor): first(major), second(minor) {} +}; + +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_VERSION_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb_houdini/AttributeTransferUtil.h b/nuparu/include/openvdb/include/openvdb_houdini/AttributeTransferUtil.h new file mode 100644 index 00000000..eb46c089 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb_houdini/AttributeTransferUtil.h @@ -0,0 +1,1495 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 AttributeTransferUtil.h +/// @author FX R&D Simulation team +/// @brief Utility methods used by the From Mesh and From Particle SOPs + +#ifndef OPENVDB_HOUDINI_ATTRIBUTE_TRANSFER_UTIL_HAS_BEEN_INCLUDED +#define OPENVDB_HOUDINI_ATTRIBUTE_TRANSFER_UTIL_HAS_BEEN_INCLUDED + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace openvdb_houdini { + +//////////////////////////////////////// + +/// Get OpenVDB specific value, by calling GA_AIFTuple::get() +/// with appropriate arguments. + +template inline ValueType +evalAttr(const GA_Attribute* atr, const GA_AIFTuple* aif, + GA_Offset off, int idx) +{ + fpreal64 value; + aif->get(atr, off, value, idx); + return ValueType(value); +} + +template <> inline float +evalAttr(const GA_Attribute* atr, const GA_AIFTuple* aif, + GA_Offset off, int idx) +{ + fpreal32 value; + aif->get(atr, off, value, idx); + return float(value); +} + +template <> inline openvdb::Int32 +evalAttr(const GA_Attribute* atr, const GA_AIFTuple* aif, + GA_Offset off, int idx) +{ + int32 value; + aif->get(atr, off, value, idx); + return openvdb::Int32(value); +} + +template <> inline openvdb::Int64 +evalAttr(const GA_Attribute* atr, const GA_AIFTuple* aif, + GA_Offset off, int idx) +{ + int64 value; + aif->get(atr, off, value, idx); + return openvdb::Int64(value); +} + +template <> inline openvdb::Vec3i +evalAttr(const GA_Attribute* atr, const GA_AIFTuple* aif, + GA_Offset off, int) +{ + openvdb::Vec3i vec; + + int32 comp; + aif->get(atr, off, comp, 0); + vec[0] = openvdb::Int32(comp); + + aif->get(atr, off, comp, 1); + vec[1] = openvdb::Int32(comp); + + aif->get(atr, off, comp, 2); + vec[2] = openvdb::Int32(comp); + + return vec; +} + +template <> inline openvdb::Vec3s +evalAttr(const GA_Attribute* atr, const GA_AIFTuple* aif, + GA_Offset off, int) +{ + openvdb::Vec3s vec; + + fpreal32 comp; + aif->get(atr, off, comp, 0); + vec[0] = float(comp); + + aif->get(atr, off, comp, 1); + vec[1] = float(comp); + + aif->get(atr, off, comp, 2); + vec[2] = float(comp); + + return vec; +} + +template <> inline openvdb::Vec3d +evalAttr(const GA_Attribute* atr, const GA_AIFTuple* aif, + GA_Offset off, int) +{ + openvdb::Vec3d vec; + + fpreal64 comp; + aif->get(atr, off, comp, 0); + vec[0] = double(comp); + + aif->get(atr, off, comp, 1); + vec[1] = double(comp); + + aif->get(atr, off, comp, 2); + vec[2] = double(comp); + + return vec; +} + + +//////////////////////////////////////// + +/// Combine differnet value types. + +template inline ValueType +combine(const ValueType& v0, const ValueType& v1, const ValueType& v2, + const openvdb::Vec3d& w) +{ + return ValueType(v0 * w[0] + v1 * w[1] + v2 * w[2]); +} + +template <> inline openvdb::Int32 +combine(const openvdb::Int32& v0, const openvdb::Int32& v1, + const openvdb::Int32& v2, const openvdb::Vec3d& w) +{ + if (w[2] > w[0] && w[2] > w[1]) return v2; + if (w[1] > w[0] && w[1] > w[2]) return v1; + return v0; +} + +template <> inline openvdb::Int64 +combine(const openvdb::Int64& v0, const openvdb::Int64& v1, + const openvdb::Int64& v2, const openvdb::Vec3d& w) +{ + if (w[2] > w[0] && w[2] > w[1]) return v2; + if (w[1] > w[0] && w[1] > w[2]) return v1; + return v0; +} + +template <> inline openvdb::Vec3i +combine(const openvdb::Vec3i& v0, const openvdb::Vec3i& v1, + const openvdb::Vec3i& v2, const openvdb::Vec3d& w) +{ + if (w[2] > w[0] && w[2] > w[1]) return v2; + if (w[1] > w[0] && w[1] > w[2]) return v1; + return v0; +} + +template <> inline openvdb::Vec3s +combine(const openvdb::Vec3s& v0, const openvdb::Vec3s& v1, + const openvdb::Vec3s& v2, const openvdb::Vec3d& w) +{ + openvdb::Vec3s vec; + + vec[0] = float(v0[0] * w[0] + v1[0] * w[1] + v2[0] * w[2]); + vec[1] = float(v0[1] * w[0] + v1[1] * w[1] + v2[1] * w[2]); + vec[2] = float(v0[2] * w[0] + v1[2] * w[1] + v2[2] * w[2]); + + return vec; +} + +template <> inline openvdb::Vec3d +combine(const openvdb::Vec3d& v0, const openvdb::Vec3d& v1, + const openvdb::Vec3d& v2, const openvdb::Vec3d& w) +{ + openvdb::Vec3d vec; + + vec[0] = v0[0] * w[0] + v1[0] * w[1] + v2[0] * w[2]; + vec[1] = v0[1] * w[0] + v1[1] * w[1] + v2[1] * w[2]; + vec[2] = v0[2] * w[0] + v1[2] * w[1] + v2[2] * w[2]; + + return vec; +} + + +//////////////////////////////////////// + +/// Gets OpenVDB specific value by evaluating GA_Default::get() +/// with appropriate arguments. + +template inline ValueType +evalAttrDefault(const GA_Defaults& defaults, int idx) +{ + fpreal64 value; + defaults.get(idx, value); + return ValueType(value); +} + +template <> inline float +evalAttrDefault(const GA_Defaults& defaults, int /*idx*/) +{ + fpreal32 value; + defaults.get(0, value); + return float(value); +} + +template <> inline openvdb::Int32 +evalAttrDefault(const GA_Defaults& defaults, int idx) +{ + int32 value; + defaults.get(idx, value); + return openvdb::Int32(value); +} + +template <> inline openvdb::Int64 +evalAttrDefault(const GA_Defaults& defaults, int idx) +{ + int64 value; + defaults.get(idx, value); + return openvdb::Int64(value); +} + +template <> inline openvdb::Vec3i +evalAttrDefault(const GA_Defaults& defaults, int) +{ + openvdb::Vec3i vec; + int32 value; + + defaults.get(0, value); + vec[0] = openvdb::Int32(value); + + defaults.get(1, value); + vec[1] = openvdb::Int32(value); + + defaults.get(2, value); + vec[2] = openvdb::Int32(value); + + return vec; +} + +template <> inline openvdb::Vec3s +evalAttrDefault(const GA_Defaults& defaults, int) +{ + openvdb::Vec3s vec; + fpreal32 value; + + defaults.get(0, value); + vec[0] = float(value); + + defaults.get(1, value); + vec[1] = float(value); + + defaults.get(2, value); + vec[2] = float(value); + + return vec; +} + +template <> inline openvdb::Vec3d +evalAttrDefault(const GA_Defaults& defaults, int) +{ + openvdb::Vec3d vec; + fpreal64 value; + + defaults.get(0, value); + vec[0] = double(value); + + defaults.get(1, value); + vec[1] = double(value); + + defaults.get(2, value); + vec[2] = double(value); + + return vec; +} + + +//////////////////////////////////////// + + +class AttributeDetailBase +{ +public: + typedef boost::shared_ptr Ptr; + + virtual ~AttributeDetailBase() {} + + virtual void set(const openvdb::Coord& ijk, const GA_Offset (&offsets)[3], + const openvdb::Vec3d& weights) = 0; + + virtual void set(const openvdb::Coord& ijk, GA_Offset offset) = 0; + + virtual openvdb::GridBase::Ptr& grid() = 0; + virtual std::string& name() = 0; + + virtual AttributeDetailBase::Ptr copy() = 0; + +protected: + AttributeDetailBase() {} +}; + + +typedef std::vector AttributeDetailList; + + +//////////////////////////////////////// + + +template +class AttributeDetail: public AttributeDetailBase +{ +public: + typedef typename VDBGridType::ValueType ValueType; + + AttributeDetail( + openvdb::GridBase::Ptr grid, + const GA_Attribute* attribute, + const GA_AIFTuple* tupleAIF, + const int tupleIndex, + const bool isVector = false); + + void set(const openvdb::Coord& ijk, const GA_Offset (&offsets)[3], + const openvdb::Vec3d& weights); + + void set(const openvdb::Coord& ijk, GA_Offset offset); + + openvdb::GridBase::Ptr& grid() { return mGrid; } + std::string& name() { return mName; } + + AttributeDetailBase::Ptr copy(); + +protected: + AttributeDetail(); + +private: + openvdb::GridBase::Ptr mGrid; + typename VDBGridType::Accessor mAccessor; + + const GA_Attribute* mAttribute; + const GA_AIFTuple* mTupleAIF; + const int mTupleIndex; + std::string mName; +}; + + +template +AttributeDetail::AttributeDetail(): + mAttribute(NULL), + mTupleAIF(NULL), + mTupleIndex(0) +{ +} + + +template +AttributeDetail::AttributeDetail( + openvdb::GridBase::Ptr grid, + const GA_Attribute* attribute, + const GA_AIFTuple* tupleAIF, + const int tupleIndex, + const bool isVector): + mGrid(grid), + mAccessor(openvdb::GridBase::grid(mGrid)->getAccessor()), + mAttribute(attribute), + mTupleAIF(tupleAIF), + mTupleIndex(tupleIndex) +{ + std::ostringstream name; + name << mAttribute->getName(); + + const int tupleSize = mTupleAIF->getTupleSize(mAttribute); + + if(!isVector && tupleSize != 1) { + name << "_" << mTupleIndex; + } + + mName = name.str(); +} + + +template +void +AttributeDetail::set(const openvdb::Coord& ijk, + const GA_Offset (&offsets)[3], const openvdb::Vec3d& weights) +{ + ValueType v0 = evalAttr( + mAttribute, mTupleAIF, offsets[0], mTupleIndex); + + ValueType v1 = evalAttr( + mAttribute, mTupleAIF, offsets[1], mTupleIndex); + + ValueType v2 = evalAttr( + mAttribute, mTupleAIF, offsets[2], mTupleIndex); + + mAccessor.setValue(ijk, combine(v0, v1, v2, weights)); +} + +template +void +AttributeDetail::set(const openvdb::Coord& ijk, GA_Offset offset) +{ + mAccessor.setValue(ijk, + evalAttr(mAttribute, mTupleAIF, offset, mTupleIndex)); +} + +template +AttributeDetailBase::Ptr +AttributeDetail::copy() +{ + return AttributeDetailBase::Ptr(new AttributeDetail(*this)); +} + + +//////////////////////////////////////// + + +// TBB object to transfer mesh attributes. +// Only quads and/or triangles are supported +// NOTE: This class has all code in the header and so it cannot have +// OPENVDB_HOUDINI_API. +class MeshAttrTransfer +{ +public: + typedef openvdb::tree::IteratorRange IterRange; + + inline + MeshAttrTransfer( + AttributeDetailList &pointAttributes, + AttributeDetailList &vertexAttributes, + AttributeDetailList &primitiveAttributes, + const openvdb::Int32Grid& closestPrimGrid, + const openvdb::math::Transform& transform, + const GU_Detail& meshGdp); + + inline + MeshAttrTransfer(const MeshAttrTransfer &other); + + inline + ~MeshAttrTransfer() {} + + /// Main calls + inline void runParallel(); + inline void runSerial(); + + inline void operator()(IterRange &range) const; + +private: + AttributeDetailList mPointAttributes, mVertexAttributes, mPrimitiveAttributes; + const openvdb::Int32Grid& mClosestPrimGrid; + + const openvdb::math::Transform& mTransform; + + const GA_Detail &mMeshGdp; +}; + + +MeshAttrTransfer::MeshAttrTransfer( + AttributeDetailList& pointAttributes, + AttributeDetailList& vertexAttributes, + AttributeDetailList& primitiveAttributes, + const openvdb::Int32Grid& closestPrimGrid, + const openvdb::math::Transform& transform, + const GU_Detail& meshGdp): + mPointAttributes(pointAttributes), + mVertexAttributes(vertexAttributes), + mPrimitiveAttributes(primitiveAttributes), + mClosestPrimGrid(closestPrimGrid), + mTransform(transform), + mMeshGdp(meshGdp) +{ +} + + +MeshAttrTransfer::MeshAttrTransfer(const MeshAttrTransfer &other): + mPointAttributes(other.mPointAttributes.size()), + mVertexAttributes(other.mVertexAttributes.size()), + mPrimitiveAttributes(other.mPrimitiveAttributes.size()), + mClosestPrimGrid(other.mClosestPrimGrid), + mTransform(other.mTransform), + mMeshGdp(other.mMeshGdp) +{ + // Deep-copy the AttributeDetail arrays, to construct unique tree + // accessors per thread. + + for (size_t i = 0, N = other.mPointAttributes.size(); i < N; ++i) { + mPointAttributes[i] = other.mPointAttributes[i]->copy(); + } + + for (size_t i = 0, N = other.mVertexAttributes.size(); i < N; ++i) { + mVertexAttributes[i] = other.mVertexAttributes[i]->copy(); + } + + for (size_t i = 0, N = other.mPrimitiveAttributes.size(); i < N; ++i) { + mPrimitiveAttributes[i] = other.mPrimitiveAttributes[i]->copy(); + } +} + + +void +MeshAttrTransfer::runParallel() +{ + IterRange range(mClosestPrimGrid.tree().beginLeaf()); + tbb::parallel_for(range, *this); +} + +void +MeshAttrTransfer::runSerial() +{ + IterRange range(mClosestPrimGrid.tree().beginLeaf()); + (*this)(range); +} + + +void +MeshAttrTransfer::operator()(IterRange &range) const +{ + openvdb::Int32Tree::LeafNodeType::ValueOnCIter iter; + + openvdb::Coord ijk; + unsigned int vtx; + GA_Size vtxn; + + const bool ptnAttrTransfer = mPointAttributes.size() > 0; + const bool vtxAttrTransfer = mVertexAttributes.size() > 0; + + GA_Primitive::const_iterator it; + GA_Offset vtxOffsetList[4], ptnOffsetList[4], vtxOffsets[3], ptnOffsets[3], prmOffset; + openvdb::Vec3d ptnList[4], xyz, cpt, cpt2, uvw, uvw2; + + for ( ; range; ++range) { + iter = range.iterator()->beginValueOn(); + for ( ; iter; ++iter) { + + ijk = iter.getCoord(); + + const GA_Index prmIndex = iter.getValue(); + prmOffset = mMeshGdp.primitiveOffset(prmIndex); + + // Transfer primitive attributes + for (size_t i = 0, N = mPrimitiveAttributes.size(); i < N; ++i) { + mPrimitiveAttributes[i]->set(ijk, prmOffset); + } + + if (!ptnAttrTransfer && !vtxAttrTransfer) continue; + + // Transfer vertex and point attributes + const GA_Primitive * primRef = mMeshGdp.getPrimitiveList().get(prmOffset); + + vtxn = primRef->getVertexCount(); + + // Get vertex and point offests + primRef->beginVertex(it); + + for (vtx = 0; !it.atEnd(); ++it, ++vtx) { + + ptnOffsetList[vtx] = it.getPointOffset(); + vtxOffsetList[vtx] = it.getVertexOffset(); + + UT_Vector3 p = mMeshGdp.getPos3(ptnOffsetList[vtx]); + ptnList[vtx][0] = double(p[0]); + ptnList[vtx][1] = double(p[1]); + ptnList[vtx][2] = double(p[2]); + } + + xyz = mTransform.indexToWorld(ijk); + + // Compute barycentric coordinates + + cpt = closestPointOnTriangleToPoint( + ptnList[0], ptnList[2], ptnList[1], xyz, uvw); + + vtxOffsets[0] = vtxOffsetList[0]; // cpt offsets + ptnOffsets[0] = ptnOffsetList[0]; + vtxOffsets[1] = vtxOffsetList[2]; + ptnOffsets[1] = ptnOffsetList[2]; + vtxOffsets[2] = vtxOffsetList[1]; + ptnOffsets[2] = ptnOffsetList[1]; + + if (4 == vtxn) { + cpt2 = closestPointOnTriangleToPoint( + ptnList[0], ptnList[3], ptnList[2], xyz, uvw2); + + if ((cpt2 - xyz).lengthSqr() < (cpt - xyz).lengthSqr()) { + uvw = uvw2; + vtxOffsets[1] = vtxOffsetList[3]; + ptnOffsets[1] = ptnOffsetList[3]; + vtxOffsets[2] = vtxOffsetList[2]; + ptnOffsets[2] = ptnOffsetList[2]; + } + } + + // Transfer vertex attributes + for (size_t i = 0, N = mVertexAttributes.size(); i < N; ++i) { + mVertexAttributes[i]->set(ijk, vtxOffsets, uvw); + } + + // Transfer point attributes + for (size_t i = 0, N = mPointAttributes.size(); i < N; ++i) { + mPointAttributes[i]->set(ijk, ptnOffsets, uvw); + } + + } // end sparse voxel iteration. + } // end leaf-node iteration +} + + +//////////////////////////////////////// + + +// TBB object to transfer mesh attributes. +// Only quads and/or triangles are supported +// NOTE: This class has all code in the header and so it cannot have +// OPENVDB_HOUDINI_API. +class PointAttrTransfer +{ +public: + typedef openvdb::tree::IteratorRange IterRange; + + inline + PointAttrTransfer( + AttributeDetailList &pointAttributes, + const openvdb::Int32Grid& closestPtnIdxGrid, + const GU_Detail& ptGeop); + + inline + PointAttrTransfer(const PointAttrTransfer &other); + + inline + ~PointAttrTransfer() {} + + /// Main calls + inline void runParallel(); + inline void runSerial(); + + inline void operator()(IterRange &range) const; + +private: + AttributeDetailList mPointAttributes; + const openvdb::Int32Grid& mClosestPtnIdxGrid; + const GA_Detail &mPtGeo; +}; + + +PointAttrTransfer::PointAttrTransfer( + AttributeDetailList& pointAttributes, + const openvdb::Int32Grid& closestPtnIdxGrid, + const GU_Detail& ptGeop): + mPointAttributes(pointAttributes), + mClosestPtnIdxGrid(closestPtnIdxGrid), + mPtGeo(ptGeop) +{ +} + + +PointAttrTransfer::PointAttrTransfer(const PointAttrTransfer &other): + mPointAttributes(other.mPointAttributes.size()), + mClosestPtnIdxGrid(other.mClosestPtnIdxGrid), + mPtGeo(other.mPtGeo) +{ + // Deep-copy the AttributeDetail arrays, to construct unique tree + // accessors per thread. + + for (size_t i = 0, N = other.mPointAttributes.size(); i < N; ++i) { + mPointAttributes[i] = other.mPointAttributes[i]->copy(); + } +} + + +void +PointAttrTransfer::runParallel() +{ + IterRange range(mClosestPtnIdxGrid.tree().beginLeaf()); + tbb::parallel_for(range, *this); +} + +void +PointAttrTransfer::runSerial() +{ + IterRange range(mClosestPtnIdxGrid.tree().beginLeaf()); + (*this)(range); +} + + +void +PointAttrTransfer::operator()(IterRange &range) const +{ + openvdb::Int32Tree::LeafNodeType::ValueOnCIter iter; + openvdb::Coord ijk; + + for ( ; range; ++range) { + iter = range.iterator()->beginValueOn(); + for ( ; iter; ++iter) { + + ijk = iter.getCoord(); + + const GA_Index pointIndex = iter.getValue(); + const GA_Offset pointOffset = mPtGeo.pointOffset(pointIndex); + + // Transfer point attributes + for (size_t i = 0, N = mPointAttributes.size(); i < N; ++i) { + mPointAttributes[i]->set(ijk, pointOffset); + } + } // end sparse voxel iteration. + } // end leaf-node iteration +} + + +//////////////////////////////////////// + +// Mesh to Mesh Attribute Transfer Utils + + +struct AttributeCopyBase +{ + typedef boost::shared_ptr Ptr; + + virtual ~AttributeCopyBase() {} + virtual void copy(GA_Offset /*source*/, GA_Offset /*target*/) = 0; + virtual void copy(GA_Offset&, GA_Offset&, GA_Offset&, GA_Offset /*target*/, + const openvdb::Vec3d& /*uvw*/) = 0; +protected: + AttributeCopyBase() {} +}; + + +template +struct AttributeCopy: public AttributeCopyBase +{ +public: + AttributeCopy(const GA_Attribute& sourceAttr, GA_Attribute& targetAttr) + : mSourceAttr(sourceAttr) + , mTargetAttr(targetAttr) + , mAIFTuple(*mSourceAttr.getAIFTuple()) + , mTupleSize(mAIFTuple.getTupleSize(&mSourceAttr)) + { + } + + void copy(GA_Offset source, GA_Offset target) + { + ValueType data; + for (int i = 0; i < mTupleSize; ++i) { + mAIFTuple.get(&mSourceAttr, source, data, i); + mAIFTuple.set(&mTargetAttr, target, data, i); + } + } + + void copy(GA_Offset& v0, GA_Offset& v1, GA_Offset& v2, GA_Offset target, + const openvdb::Vec3d& uvw) + { + doCopy(v0, v1, v2, target, uvw); + } + +private: + + template + typename boost::enable_if, void>::type + doCopy(GA_Offset& v0, GA_Offset& v1, GA_Offset& v2, GA_Offset target, const openvdb::Vec3d& uvw) + { + GA_Offset source = v0; + double min = uvw[0]; + + if (uvw[1] < min) { + min = uvw[1]; + source = v1; + } + if (uvw[2] < min) source = v2; + + + ValueType data; + for (int i = 0; i < mTupleSize; ++i) { + mAIFTuple.get(&mSourceAttr, source, data, i); + mAIFTuple.set(&mTargetAttr, target, data, i); + } + } + + template + typename boost::enable_if, void>::type + doCopy(GA_Offset& v0, GA_Offset& v1, GA_Offset& v2, GA_Offset target, const openvdb::Vec3d& uvw) + { + ValueType a, b, c; + for (int i = 0; i < mTupleSize; ++i) { + mAIFTuple.get(&mSourceAttr, v0, a, i); + mAIFTuple.get(&mSourceAttr, v1, b, i); + mAIFTuple.get(&mSourceAttr, v2, c, i); + mAIFTuple.set(&mTargetAttr, target, a*uvw[0] + b*uvw[1] + c*uvw[2], i); + } + } + + + const GA_Attribute& mSourceAttr; + GA_Attribute& mTargetAttr; + const GA_AIFTuple& mAIFTuple; + int mTupleSize; +}; + + +struct StrAttributeCopy: public AttributeCopyBase +{ +public: + StrAttributeCopy(const GA_Attribute& sourceAttr, GA_Attribute& targetAttr) + : mSourceAttr(sourceAttr) + , mTargetAttr(targetAttr) + , mAIF(*mSourceAttr.getAIFSharedStringTuple()) + , mTupleSize(mAIF.getTupleSize(&mSourceAttr)) + { + } + + void copy(GA_Offset source, GA_Offset target) + { + for (int i = 0; i < mTupleSize; ++i) { + mAIF.setString(&mTargetAttr, target, mAIF.getString(&mSourceAttr, source, i), i); + } + } + + void copy(GA_Offset& v0, GA_Offset& v1, GA_Offset& v2, GA_Offset target, + const openvdb::Vec3d& uvw) + { + GA_Offset source = v0; + double min = uvw[0]; + + if (uvw[1] < min) { + min = uvw[1]; + source = v1; + } + if (uvw[2] < min) source = v2; + + for (int i = 0; i < mTupleSize; ++i) { + mAIF.setString(&mTargetAttr, target, mAIF.getString(&mSourceAttr, source, i), i); + } + } + +protected: + const GA_Attribute& mSourceAttr; + GA_Attribute& mTargetAttr; + const GA_AIFSharedStringTuple& mAIF; + int mTupleSize; +}; + + +//////////////////////////////////////// + + +inline AttributeCopyBase::Ptr +createAttributeCopier(const GA_Attribute& sourceAttr, GA_Attribute& targetAttr) +{ + const GA_AIFTuple * aifTuple = sourceAttr.getAIFTuple(); + AttributeCopyBase::Ptr attr; + + if (aifTuple) { + const GA_Storage sourceStorage = aifTuple->getStorage(&sourceAttr); + const GA_Storage targetStorage = aifTuple->getStorage(&targetAttr); + + const int sourceTupleSize = aifTuple->getTupleSize(&sourceAttr); + const int targetTupleSize = aifTuple->getTupleSize(&targetAttr); + + if (sourceStorage == targetStorage && sourceTupleSize == targetTupleSize) { + switch (sourceStorage) + { + case GA_STORE_INT16: + case GA_STORE_INT32: + attr = AttributeCopyBase::Ptr( + new AttributeCopy(sourceAttr, targetAttr)); + break; + case GA_STORE_INT64: + attr = AttributeCopyBase::Ptr( + new AttributeCopy(sourceAttr, targetAttr)); + break; + case GA_STORE_REAL16: + case GA_STORE_REAL32: + attr = AttributeCopyBase::Ptr( + new AttributeCopy(sourceAttr, targetAttr)); + break; + case GA_STORE_REAL64: + attr = AttributeCopyBase::Ptr( + new AttributeCopy(sourceAttr, targetAttr)); + break; + default: + break; + } + } + } else { + const GA_AIFSharedStringTuple * aifString = sourceAttr.getAIFSharedStringTuple(); + if (aifString) { + attr = AttributeCopyBase::Ptr(new StrAttributeCopy(sourceAttr, targetAttr)); + } + } + + return attr; +} + + +//////////////////////////////////////// + + +inline GA_Offset +findClosestPrimitiveToPoint( + const GU_Detail& geo, const std::set& primitives, const openvdb::Vec3d& p, + GA_Offset& vert0, GA_Offset& vert1, GA_Offset& vert2, openvdb::Vec3d& uvw) +{ + std::set::const_iterator it = primitives.begin(); + + GA_Offset primOffset = GA_INVALID_OFFSET; + const GA_Primitive * primRef = NULL; + double minDist = std::numeric_limits::max(); + + openvdb::Vec3d a, b, c, d, tmpUVW; + UT_Vector3 tmpPoint; + + for (; it != primitives.end(); ++it) { + + const GA_Offset offset = geo.primitiveOffset(*it); + primRef = geo.getPrimitiveList().get(offset); + + const GA_Size vertexCount = primRef->getVertexCount(); + + + if (vertexCount == 3 || vertexCount == 4) { + + tmpPoint = geo.getPos3(primRef->getPointOffset(0)); + a[0] = tmpPoint.x(); + a[1] = tmpPoint.y(); + a[2] = tmpPoint.z(); + + tmpPoint = geo.getPos3(primRef->getPointOffset(1)); + b[0] = tmpPoint.x(); + b[1] = tmpPoint.y(); + b[2] = tmpPoint.z(); + + tmpPoint = geo.getPos3(primRef->getPointOffset(2)); + c[0] = tmpPoint.x(); + c[1] = tmpPoint.y(); + c[2] = tmpPoint.z(); + + double tmpDist = + (p - openvdb::math::closestPointOnTriangleToPoint(a, c, b, p, tmpUVW)).lengthSqr(); + + if (tmpDist < minDist) { + minDist = tmpDist; + primOffset = offset; + uvw = tmpUVW; + vert0 = primRef->getVertexOffset(0); + vert1 = primRef->getVertexOffset(2); + vert2 = primRef->getVertexOffset(1); + } + + if (vertexCount == 4) { + tmpPoint = geo.getPos3(primRef->getPointOffset(3)); + d[0] = tmpPoint.x(); + d[1] = tmpPoint.y(); + d[2] = tmpPoint.z(); + + tmpDist = (p - openvdb::math::closestPointOnTriangleToPoint( + a, d, c, p, tmpUVW)).lengthSqr(); + if (tmpDist < minDist) { + minDist = tmpDist; + primOffset = offset; + uvw = tmpUVW; + vert0 = primRef->getVertexOffset(0); + vert1 = primRef->getVertexOffset(3); + vert2 = primRef->getVertexOffset(2); + } + } + + } + } + + return primOffset; +} + + +// Faster for small primitive counts +inline GA_Offset +findClosestPrimitiveToPoint( + const GU_Detail& geo, std::vector& primitives, const openvdb::Vec3d& p, + GA_Offset& vert0, GA_Offset& vert1, GA_Offset& vert2, openvdb::Vec3d& uvw) +{ + GA_Offset primOffset = GA_INVALID_OFFSET; + const GA_Primitive * primRef = NULL; + double minDist = std::numeric_limits::max(); + + openvdb::Vec3d a, b, c, d, tmpUVW; + UT_Vector3 tmpPoint; + + std::sort(primitives.begin(), primitives.end()); + + GA_Index lastPrim = -1; + for (size_t n = 0, N = primitives.size(); n < N; ++n) { + if (primitives[n] == lastPrim) continue; + lastPrim = primitives[n]; + + const GA_Offset offset = geo.primitiveOffset(lastPrim); + primRef = geo.getPrimitiveList().get(offset); + + const GA_Size vertexCount = primRef->getVertexCount(); + + + if (vertexCount == 3 || vertexCount == 4) { + + tmpPoint = geo.getPos3(primRef->getPointOffset(0)); + a[0] = tmpPoint.x(); + a[1] = tmpPoint.y(); + a[2] = tmpPoint.z(); + + tmpPoint = geo.getPos3(primRef->getPointOffset(1)); + b[0] = tmpPoint.x(); + b[1] = tmpPoint.y(); + b[2] = tmpPoint.z(); + + tmpPoint = geo.getPos3(primRef->getPointOffset(2)); + c[0] = tmpPoint.x(); + c[1] = tmpPoint.y(); + c[2] = tmpPoint.z(); + + double tmpDist = + (p - openvdb::math::closestPointOnTriangleToPoint(a, c, b, p, tmpUVW)).lengthSqr(); + + if (tmpDist < minDist) { + minDist = tmpDist; + primOffset = offset; + uvw = tmpUVW; + vert0 = primRef->getVertexOffset(0); + vert1 = primRef->getVertexOffset(2); + vert2 = primRef->getVertexOffset(1); + } + + if (vertexCount == 4) { + tmpPoint = geo.getPos3(primRef->getPointOffset(3)); + d[0] = tmpPoint.x(); + d[1] = tmpPoint.y(); + d[2] = tmpPoint.z(); + + tmpDist = (p - openvdb::math::closestPointOnTriangleToPoint( + a, d, c, p, tmpUVW)).lengthSqr(); + if (tmpDist < minDist) { + minDist = tmpDist; + primOffset = offset; + uvw = tmpUVW; + vert0 = primRef->getVertexOffset(0); + vert1 = primRef->getVertexOffset(3); + vert2 = primRef->getVertexOffset(2); + } + } + + } + } + + return primOffset; +} + +//////////////////////////////////////// + + +template +class TransferPrimitiveAttributesOp +{ +public: + TransferPrimitiveAttributesOp( + const GU_Detail& sourceGeo, GU_Detail& targetGeo, const GridType& indexGrid, + std::vector& primAttributes, + std::vector& vertAttributes); + + void operator()(const GA_SplittableRange&) const; +private: + const GU_Detail& mSourceGeo; + GU_Detail& mTargetGeo; + const GridType& mIndexGrid; + std::vector& mPrimAttributes; + std::vector& mVertAttributes; +}; + +template +TransferPrimitiveAttributesOp::TransferPrimitiveAttributesOp( + const GU_Detail& sourceGeo, GU_Detail& targetGeo, const GridType& indexGrid, + std::vector& primAttributes, + std::vector& vertAttributes) + : mSourceGeo(sourceGeo) + , mTargetGeo(targetGeo) + , mIndexGrid(indexGrid) + , mPrimAttributes(primAttributes) + , mVertAttributes(vertAttributes) +{ + +} + +template +void +TransferPrimitiveAttributesOp::operator()(const GA_SplittableRange& range) const +{ + typedef typename GridType::ValueType IndexT; + + GA_Offset start, end, source, target, v0, v1, v2; + const GA_Primitive * primRef = NULL; + GA_Primitive::const_iterator vtxIt; + + typename GridType::ConstAccessor acc = mIndexGrid.getConstAccessor(); + const openvdb::math::Transform& transform = mIndexGrid.transform(); + openvdb::Vec3d pos, indexPos, uvw; + openvdb::Coord ijk, coord; + std::vector primitives(8), similar_primitives(8); + int count; + + UT_Vector3 targetN, sourceN; + + for (GA_PageIterator pageIt = range.beginPages(); !pageIt.atEnd(); ++pageIt) { + for (GA_Iterator blockIt(pageIt.begin()); blockIt.blockAdvance(start, end); ) { + for (target = start; target < end; ++target) { + + primRef = mTargetGeo.getPrimitiveList().get(target); + targetN = mTargetGeo.getGEOPrimitive(target)->computeNormal(); + + if (mPrimAttributes.size() != 0 ) { + + // Compute avg. vertex position + pos[0] = 0.0; + pos[1] = 0.0; + pos[2] = 0.0; + count = 0; + + for (primRef->beginVertex(vtxIt); !vtxIt.atEnd(); ++vtxIt, ++count) { + const UT_Vector3 p = mTargetGeo.getPos3(vtxIt.getPointOffset()); + pos[0] += p.x(); + pos[1] += p.y(); + pos[2] += p.z(); + } + + if (count > 1) pos *= (1.0 / float(count)); + indexPos = transform.worldToIndex(pos); + + // Find closest source primitive to current avg. vertex position. + coord[0] = int(std::floor(indexPos[0])); + coord[1] = int(std::floor(indexPos[1])); + coord[2] = int(std::floor(indexPos[2])); + + primitives.clear(); + similar_primitives.clear(); + IndexT primIndex; + + for (int d = 0; d < 8; ++d) { + ijk[0] = coord[0] + (((d & 0x02) >> 1) ^ (d & 0x01)); + ijk[1] = coord[1] + ((d & 0x02) >> 1); + ijk[2] = coord[2] + ((d & 0x04) >> 2); + + if (acc.probeValue(ijk, primIndex) && + openvdb::Index32(primIndex) != openvdb::util::INVALID_IDX) { + + GA_Offset tmpOffset = mSourceGeo.primitiveOffset(primIndex); + sourceN = mSourceGeo.getGEOPrimitive(tmpOffset)->computeNormal(); + + if (sourceN.dot(targetN) > 0.5) { + similar_primitives.push_back(primIndex); + } else { + primitives.push_back(primIndex); + } + } + } + + if (!primitives.empty() || !similar_primitives.empty()) { + + if (!similar_primitives.empty()) { + source = findClosestPrimitiveToPoint( + mSourceGeo, similar_primitives, pos, v0, v1, v2, uvw); + } else { + source = findClosestPrimitiveToPoint( + mSourceGeo, primitives, pos, v0, v1, v2, uvw); + } + + // Transfer attributes + for (size_t n = 0, N = mPrimAttributes.size(); n < N; ++n) { + mPrimAttributes[n]->copy(source, target); + } + } + } + + if (mVertAttributes.size() != 0) { + for (primRef->beginVertex(vtxIt); !vtxIt.atEnd(); ++vtxIt) { + + + const UT_Vector3 p = mTargetGeo.getPos3(vtxIt.getPointOffset()); + pos[0] = p.x(); + pos[1] = p.y(); + pos[2] = p.z(); + + indexPos = transform.worldToIndex(pos); + coord[0] = int(std::floor(indexPos[0])); + coord[1] = int(std::floor(indexPos[1])); + coord[2] = int(std::floor(indexPos[2])); + + int primIndex; + primitives.clear(); + similar_primitives.clear(); + + for (int d = 0; d < 8; ++d) { + ijk[0] = coord[0] + (((d & 0x02) >> 1) ^ (d & 0x01)); + ijk[1] = coord[1] + ((d & 0x02) >> 1); + ijk[2] = coord[2] + ((d & 0x04) >> 2); + + if (acc.probeValue(ijk, primIndex) && + openvdb::Index32(primIndex) != openvdb::util::INVALID_IDX) { + + GA_Offset tmpOffset = mSourceGeo.primitiveOffset(primIndex); + sourceN = mSourceGeo.getGEOPrimitive(tmpOffset)->computeNormal(); + + if (sourceN.dot(targetN) > 0.5) { + primitives.push_back(primIndex); + } + } + } + + if (!primitives.empty() || !similar_primitives.empty()) { + + if (!similar_primitives.empty()) { + findClosestPrimitiveToPoint( + mSourceGeo, similar_primitives, pos, v0, v1, v2, uvw); + } else { + findClosestPrimitiveToPoint( + mSourceGeo, primitives, pos, v0, v1, v2, uvw); + } + + for (size_t n = 0, N = mVertAttributes.size(); n < N; ++n) { + mVertAttributes[n]->copy(v0, v1, v2, vtxIt.getVertexOffset(), uvw); + } + } + } + } + } + } + } +} + + +//////////////////////////////////////// + +template +class TransferPointAttributesOp +{ +public: + TransferPointAttributesOp( + const GU_Detail& sourceGeo, GU_Detail& targetGeo, const GridType& indexGrid, + std::vector& pointAttributes, + const GA_PrimitiveGroup* surfacePrims = NULL); + + void operator()(const GA_SplittableRange&) const; +private: + const GU_Detail& mSourceGeo; + GU_Detail& mTargetGeo; + const GridType& mIndexGrid; + std::vector& mPointAttributes; + const GA_PrimitiveGroup* mSurfacePrims; +}; + +template +TransferPointAttributesOp::TransferPointAttributesOp( + const GU_Detail& sourceGeo, GU_Detail& targetGeo, const GridType& indexGrid, + std::vector& pointAttributes, + const GA_PrimitiveGroup* surfacePrims) + : mSourceGeo(sourceGeo) + , mTargetGeo(targetGeo) + , mIndexGrid(indexGrid) + , mPointAttributes(pointAttributes) + , mSurfacePrims(surfacePrims) +{ +} + +template +void +TransferPointAttributesOp::operator()(const GA_SplittableRange& range) const +{ + typedef typename GridType::ValueType IndexT; + + GA_Offset start, end, vtxOffset, primOffset, target, v0, v1, v2; + + typename GridType::ConstAccessor acc = mIndexGrid.getConstAccessor(); + const openvdb::math::Transform& transform = mIndexGrid.transform(); + openvdb::Vec3d pos, indexPos, uvw; + std::vector primitives(8); + openvdb::Coord ijk, coord; + + for (GA_PageIterator pageIt = range.beginPages(); !pageIt.atEnd(); ++pageIt) { + for (GA_Iterator blockIt(pageIt.begin()); blockIt.blockAdvance(start, end); ) { + for (target = start; target < end; ++target) { + + + vtxOffset = mTargetGeo.pointVertex(target); + + // Check if point is referenced by a surface primitive. + if (mSurfacePrims) { + bool surfacePrim = false; + + while (GAisValid(vtxOffset)) { + + primOffset = mTargetGeo.vertexPrimitive(vtxOffset); + + if (mSurfacePrims->containsIndex(mTargetGeo.primitiveIndex(primOffset))) { + surfacePrim = true; + break; + } + + vtxOffset = mTargetGeo.vertexToNextVertex(vtxOffset); + } + + if (!surfacePrim) continue; + } + + const UT_Vector3 p = mTargetGeo.getPos3(target); + pos[0] = p.x(); + pos[1] = p.y(); + pos[2] = p.z(); + + indexPos = transform.worldToIndex(pos); + coord[0] = int(std::floor(indexPos[0])); + coord[1] = int(std::floor(indexPos[1])); + coord[2] = int(std::floor(indexPos[2])); + + primitives.clear(); + IndexT primIndex; + + for (int d = 0; d < 8; ++d) { + ijk[0] = coord[0] + (((d & 0x02) >> 1) ^ (d & 0x01)); + ijk[1] = coord[1] + ((d & 0x02) >> 1); + ijk[2] = coord[2] + ((d & 0x04) >> 2); + + if (acc.probeValue(ijk, primIndex) && + openvdb::Index32(primIndex) != openvdb::util::INVALID_IDX) { + primitives.push_back(primIndex); + } + } + + if (!primitives.empty()) { + findClosestPrimitiveToPoint(mSourceGeo, primitives, pos, v0, v1, v2, uvw); + + v0 = mSourceGeo.vertexPoint(v0); + v1 = mSourceGeo.vertexPoint(v1); + v2 = mSourceGeo.vertexPoint(v2); + + for (size_t n = 0, N = mPointAttributes.size(); n < N; ++n) { + mPointAttributes[n]->copy(v0, v1, v2, target, uvw); + } + } + } + } + } +} + + +//////////////////////////////////////// + + +template +inline void +transferPrimitiveAttributes( + const GU_Detail& sourceGeo, + GU_Detail& targetGeo, + GridType& indexGrid, + Interrupter& boss, + const GA_PrimitiveGroup *primitives = NULL) +{ + // Match public primitive attributes + GA_AttributeDict::iterator it = sourceGeo.primitiveAttribs().begin(GA_SCOPE_PUBLIC); + + if (indexGrid.activeVoxelCount() == 0) return; + + std::vector primAttributeList; + + // Primitive attributes + for (; !it.atEnd(); ++it) { + const GA_Attribute* sourceAttr = it.attrib(); +#if (UT_VERSION_INT >= 0x0e0000b0) // 14.0.176 or later + if (NULL == targetGeo.findPrimitiveAttribute(it.name())) { + targetGeo.addPrimAttrib(sourceAttr); + } + GA_Attribute* targetAttr = targetGeo.findPrimitiveAttribute(it.name()); +#else + if (!targetGeo.findPrimitiveAttribute(it.name()).isValid()) { + targetGeo.addPrimAttrib(sourceAttr); + } + GA_Attribute* targetAttr = targetGeo.findPrimitiveAttribute(it.name()).getAttribute(); +#endif + + if (sourceAttr && targetAttr) { + AttributeCopyBase::Ptr att = createAttributeCopier(*sourceAttr, *targetAttr); + if(att) primAttributeList.push_back(att); + } + } + + if (boss.wasInterrupted()) return; + + std::vector vertAttributeList; + + it = sourceGeo.vertexAttribs().begin(GA_SCOPE_PUBLIC); + + // Vertex attributes + for (; !it.atEnd(); ++it) { + const GA_Attribute* sourceAttr = it.attrib(); +#if (UT_VERSION_INT >= 0x0e0000b0) // 14.0.176 or later + if (NULL == targetGeo.findVertexAttribute(it.name())) { + targetGeo.addVertexAttrib(sourceAttr); + } + GA_Attribute* targetAttr = targetGeo.findVertexAttribute(it.name()); +#else + if (!targetGeo.findVertexAttribute(it.name()).isValid()) { + targetGeo.addVertexAttrib(sourceAttr); + } + GA_Attribute* targetAttr = targetGeo.findVertexAttribute(it.name()).getAttribute(); +#endif + + if (sourceAttr && targetAttr) { +#if (UT_VERSION_INT >= 0x0c01007D) // 12.1.125 or later + targetAttr->hardenAllPages(); +#endif + AttributeCopyBase::Ptr att = createAttributeCopier(*sourceAttr, *targetAttr); + if(att) vertAttributeList.push_back(att); + } + } + + if (!boss.wasInterrupted() && (!primAttributeList.empty() || !vertAttributeList.empty())) { + + UTparallelFor(GA_SplittableRange(targetGeo.getPrimitiveRange(primitives)), + TransferPrimitiveAttributesOp(sourceGeo, targetGeo, indexGrid, + primAttributeList, vertAttributeList)); + } + + if (!boss.wasInterrupted()) { + std::vector pointAttributeList; + it = sourceGeo.pointAttribs().begin(GA_SCOPE_PUBLIC); + + // Point attributes + for (; !it.atEnd(); ++it) { + if (std::string(it.name()) == "P") continue; // Ignore previous point positions. + + const GA_Attribute* sourceAttr = it.attrib(); +#if (UT_VERSION_INT >= 0x0e0000b0) // 14.0.176 or later + if (NULL == targetGeo.findPointAttribute(it.name())) { + targetGeo.addPointAttrib(sourceAttr); + } + GA_Attribute* targetAttr = targetGeo.findPointAttribute(it.name()); +#else + if (!targetGeo.findPointAttribute(it.name()).isValid()) { + targetGeo.addPointAttrib(sourceAttr); + } + GA_Attribute* targetAttr = targetGeo.findPointAttribute(it.name()).getAttribute(); +#endif + + if (sourceAttr && targetAttr) { + AttributeCopyBase::Ptr att = createAttributeCopier(*sourceAttr, *targetAttr); + if(att) pointAttributeList.push_back(att); + } + } + + if (!boss.wasInterrupted() && !pointAttributeList.empty()) { + UTparallelFor(GA_SplittableRange(targetGeo.getPointRange()), + TransferPointAttributesOp(sourceGeo, targetGeo, indexGrid, + pointAttributeList, primitives)); + + } + } +} + +} // namespace openvdb_houdini + +#endif // OPENVDB_HOUDINI_ATTRIBUTE_TRANSFER_UTIL_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb_houdini/GEO_PrimVDB.h b/nuparu/include/openvdb/include/openvdb_houdini/GEO_PrimVDB.h new file mode 100644 index 00000000..bc538239 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb_houdini/GEO_PrimVDB.h @@ -0,0 +1,834 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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. +// +/////////////////////////////////////////////////////////////////////////// + +/* + * PROPRIETARY INFORMATION. This software is proprietary to + * Side Effects Software Inc., and is not to be reproduced, + * transmitted, or disclosed in any way without written permission. + * + * Produced by: + * Jeff Lait + * Side Effects Software Inc + * 477 Richmond Street West + * Toronto, Ontario + * Canada M5V 3E7 + * 416-504-9876 + * + * NAME: GEO_PrimVDB.h ( GEO Library, C++) + * + * COMMENTS: Custom VDB primitive. + */ + +#include +#if !defined(SESI_OPENVDB) && (UT_VERSION_INT >= 0x0c050157) // 12.5.343 or later + +#include + +namespace openvdb_houdini { +using ::GEO_VolumeOptions; +using ::GEO_PrimVDB; +} + +#else // earlier than 12.5.343 + +#ifndef __HDK_GEO_PrimVDB__ +#define __HDK_GEO_PrimVDB__ + +//#include "GEO_API.h" + +#include +#include +#if (UT_VERSION_INT < 0x0c010072) // earlier than 12.1.114 +#include +#else +#include +#endif + +#include + +#include // for SYS_AtomicCounter + +#include +#include "UT_VDBUtils.h" +#include + +#include +#include + + +class GEO_Detail; +class GEO_PrimVolume; +class GEO_PrimVolumeXform; + + +#if (UT_VERSION_INT < 0x0c050000) // earlier than 12.5.0 +struct OPENVDB_HOUDINI_API GEO_VolumeOptions +{ + GEO_VolumeOptions() + { + } + GEO_VolumeOptions(GEO_VolumeVis mode, fpreal iso, fpreal density) + : myMode(mode) + , myIso(iso) + , myDensity(density) + { + } + bool operator==(const GEO_VolumeOptions &v) const + { + OPENVDB_NO_FP_EQUALITY_WARNING_BEGIN + return (myMode == v.myMode + && myIso == v.myIso + && myDensity == v.myDensity); + OPENVDB_NO_FP_EQUALITY_WARNING_END + } + bool operator!=(const GEO_VolumeOptions &v) const + { + return !(*this == v); + } + + GEO_VolumeVis myMode; + fpreal myIso; + fpreal myDensity; +}; +#endif + + +class OPENVDB_HOUDINI_API GEO_PrimVDB : public GEO_Primitive +{ +public: + typedef uint64 UniqueId; + +protected: + /// NOTE: The constructor should only be called from subclass + /// constructors. + GEO_PrimVDB(GEO_Detail *d, GA_Offset offset = GA_INVALID_OFFSET); + + /// NOTE: The constructor should only be called from subclass + /// constructors. + GEO_PrimVDB(const GA_MergeMap &map, GA_Detail &detail, + GA_Offset offset, const GEO_PrimVDB &src_prim); + + /// NOTE: The destructor should only be called from subclass + /// destructors. + virtual ~GEO_PrimVDB(); + +public: + static GA_PrimitiveFamilyMask buildFamilyMask() + { return GA_FAMILY_NONE; } + + /// @{ + /// Required interface methods + virtual bool isDegenerate() const; + virtual int getBBox(UT_BoundingBox *bbox) const; + virtual void reverse(); + virtual UT_Vector3 computeNormal() const; +#if (UT_VERSION_INT >= 0x0d000000) + virtual void copyPrimitive(const GEO_Primitive *src); +#else + virtual void copyPrimitive(const GEO_Primitive *src, + GEO_Point **ptredirect); +#endif + virtual void copyUnwiredForMerge(const GA_Primitive *src, + const GA_MergeMap &map); + + // Query the number of vertices in the array. This number may be smaller + // than the actual size of the array. + virtual GA_Size getVertexCount() const; + virtual GA_Offset getVertexOffset(GA_Size /*index*/) const + { return myVertex; } + + /// Convert an index in the voxel array into the corresponding worldspace + /// location + void indexToPos(int x, int y, int z, UT_Vector3 &pos) const; + void findexToPos(UT_Vector3 index, UT_Vector3 &pos) const; + + /// Convert a 3d position into the closest index value. + void posToIndex(UT_Vector3 pos, int &x, int &y, int &z) const; + void posToIndex(UT_Vector3 pos, UT_Vector3 &index) const; + + /// Evaluate the voxel value at the given world space position. + /// Note that depending on the underlying VDB type, this may not + /// be sensible, in which case a zero will silently be returned + fpreal getValueF(const UT_Vector3 &pos) const; + fpreal getValueAtIndexF(int ix, int iy, int iz) const; + UT_Vector3D getValueV3(const UT_Vector3 &pos) const; + UT_Vector3D getValueAtIndexV3(int ix, int iy, int iz) const; + void getValues(float *f, int stride, const UT_Vector3 *pos, int num) const; + void getValues(int *f, int stride, const UT_Vector3 *pos, int num) const; + void getValuesAtIndices(float *f, int stride, const int *ix, const int *iy, const int *iz, int num) const; + void getValuesAtIndices(int *f, int stride, const int *ix, const int *iy, const int *iz, int num) const; + + /// Vector grid variants. + void getValues(UT_Vector3 *f, int stride, const UT_Vector3 *pos, int num) const; + void getValuesAtIndices(UT_Vector3 *f, int stride, const int *ix, const int *iy, const int *iz, int num) const; + + // Worldspace gradient at the given position + UT_Vector3 getGradient(const UT_Vector3 &pos) const; + + /// Evaluate this grid's gradients at the given world space positions. + /// Does nothing and returns false if grid is non-scalar. + /// If normalize is true, then the gradients will be normalized to be unit + /// length. + bool evalGradients( + UT_Vector3 *gradients, + int gradients_stride, + const UT_Vector3 *positions, + int num_positions, + bool normalize = false) const; + + /// Get the storage type of the grid + UT_VDBType getStorageType() const + { return myGridAccessor.getStorageType(); } + /// Get the tuple size, usually 1 or 3 + int getTupleSize() const + { return UTvdbGetGridTupleSize(getStorageType()); } + + bool isSDF() const; + + /// True if the two volumes map the same indices to the same positions. + bool isAligned(const GEO_PrimVDB *vdb) const; + /// True if the two volumes have the same active regions + bool isActiveRegionMatched(const GEO_PrimVDB *vdb) const; + + // Transform the matrix associated with this primitive. Translate is + // ignored. + virtual void transform(const UT_Matrix4 &mat); + + /// Accessors for the 4x4 matrix representing the affine transform that + /// converts from index space voxel coordinates to world space. For frustum + /// maps, this will be transform as if the taper value is set to 1. + /// @{ + void setTransform4(const UT_DMatrix4 &xform4); + void setTransform4(const UT_Matrix4 &xform4); + UT_Matrix4D getTransform4() const; + /// @} + + // Take the whole set of points into consideration when applying the + // point removal operation to this primitive. The method returns 0 if + // successful, -1 if it failed because it would have become degenerate, + // and -2 if it failed because it would have had to remove the primitive + // altogether. + virtual int detachPoints(GA_PointGroup &grp); + /// Before a point is deleted, all primitives using the point will be + /// notified. The method should return "false" if it's impossible to + /// delete the point. Otherwise, the vertices should be removed. + virtual GA_DereferenceStatus dereferencePoint(GA_Offset point, + bool dry_run=false); + virtual GA_DereferenceStatus dereferencePoints( + const GA_RangeMemberQuery &pt_q, + bool dry_run=false); + virtual const GA_PrimitiveJSON *getJSON() const; + + /// This method assigns a preallocated vertex to the quadric, optionally + /// creating the topological link between the primitive and new vertex. + void assignVertex(GA_Offset new_vtx, bool update_topology); + + /// Defragmentation + virtual void swapVertexOffsets(const GA_Defragment &defrag); + + /// Evalaute a point given a u,v coordinate (with derivatives) + virtual bool evaluatePointRefMap(GA_Offset result_vtx, + GA_AttributeRefMap &hlist, + fpreal u, fpreal v, uint du, uint dv) const; + /// Evalaute position given a u,v coordinate (with derivatives) + virtual int evaluatePointV4(UT_Vector4 &pos, float u, float v = 0, + unsigned du=0, unsigned dv=0) const + { + return GEO_Primitive::evaluatePointV4(pos, u, v, + du, dv); + } + /// @} + + /// Convert transforms between native volumes and VDBs + /// @{ + + /// Get a GEO_PrimVolumeXform which represent's the grid's full transform. + /// The returned space's fromVoxelSpace() method will convert index space + /// voxel coordinates to world space positions (and the vice versa for + /// toVoxelSpace()). + GEO_PrimVolumeXform getIndexSpaceTransform() const; + + /// Equivalent to getSpaceTransform(getGrid().evalActiveVoxelBoundingBox()). + /// The returned space's fromVoxelSpace() method will convert 0-1 + /// coordinates over the active voxel bounding box to world space (and vice + /// versa for toVoxelSpace()). + GEO_PrimVolumeXform getSpaceTransform() const; + + /// Gives the equivalent to GEO_PrimVolume's getSpaceTransform() by using + /// the given bounding box to determine the bounds of the transform. + /// The resulting world space sample points will be offset by half a voxel + /// so that they match GEO_PrimVolume. + /// The returned space's fromVoxelSpace() method will convert 0-1 + /// coordinates over the bbox extents to world space (and vice versa for + /// toVoxelSpace()). + GEO_PrimVolumeXform getSpaceTransform(const UT_BoundingBoxD &bbox) const; + + /// Sets the transform from a GEO_PrimVolume's getSpaceTransform() by using + /// the index space [(0,0,0), resolution] bbox. If force_taper is true, + /// then the resulting transform will always be a NonlinearFrustumMap even + /// if there is no tapering. + void setSpaceTransform(const GEO_PrimVolumeXform &space, + const UT_Vector3R &resolution, + bool force_taper = false); + + /// @} + + fpreal getTaper() const; + + /// Returns the resolution of the active voxel array. + /// Does *not* mean the indices go from 0..rx, however! + void getRes(int &rx, int &ry, int &rz) const; + + /// Computes the voxel diameter by taking a step in x, y, and z + /// converting to world space and taking the length of that vector. + fpreal getVoxelDiameter() const; + + /// Returns the length of the voxel when you take an x, y, and z step + UT_Vector3 getVoxelSize() const; + + /// VDBs may either be unbounded, or created with a specific frustum + /// range. The latter is important for tapered VDBs that otherwise + /// have a singularity at the camera location. Tools can use the + /// presence of an idxbox as a clipping box in index space. + /// This does *NOT* relate to getRes - it may be much larger or + /// even in some cases smaller. + bool getFrustumBounds(UT_BoundingBox &idxbox) const; + + enum ActivateOperation + { + ACTIVATE_UNION, // Activate anything in source + ACTIVATE_INTERSECT, // Deactivate anything not in source + ACTIVATE_SUBTRACT, // Deactivate anything in source + ACTIVATE_COPY // Set our activation to match source + }; + + /// Activates voxels given an *index* space bounding box. This + /// is an inclusive box. + /// If this is Frustum VDB, the activation will be clipped by that. + /// Setting the value only takes effect if the voxels are activated, + /// deactivated voxels are set to the background. + void activateIndexBBox( + const openvdb::CoordBBox& bbox, + ActivateOperation operation, + bool setvalue, fpreal value) + { + activateIndexBBoxAdapter( + &bbox, operation, setvalue, value); + } + + /// Activates all of the voxels in this VDB that are touched + /// by active voxels in the source. + void activateByVDB(const GEO_PrimVDB *vdb, + ActivateOperation operation, + bool setvalue, fpreal value); + + /// @{ + /// Though not strictly required (i.e. not pure virtual), these methods + /// should be implemented for proper behaviour. + virtual GEO_Primitive *copy(int preserve_shared_pts = 0) const; + + // Have we been deactivated and stashed? +#if (UT_VERSION_INT >= 0x0d000000) // 13.0 or later + virtual void stashed(bool beingstashed, GA_Offset offset=GA_INVALID_OFFSET); +#else + virtual void stashed(int onoff, GA_Offset offset=GA_INVALID_OFFSET); +#endif + + // We need to invalidate the vertex offsets + virtual void clearForDeletion(); + +#if (UT_VERSION_INT < 0x0c050132) // Before 12.5.306 + virtual void copyOffsetPrimitive(const GEO_Primitive *src, int base); +#elif (UT_VERSION_INT < 0x0d000000) // Before 13.0, when the function was deleted + virtual void copyOffsetPrimitive(const GEO_Primitive *src, GA_Index base); +#endif + /// @} + + /// @{ + /// Optional interface methods. Though not required, implementing these + /// will give better behaviour for the new primitive. + virtual UT_Vector3 baryCenter() const; + virtual fpreal calcVolume(const UT_Vector3 &refpt) const; + /// Calculate the surface area of the active voxels where + /// a voxel face contributes if it borders an inactive voxel. + virtual fpreal calcArea() const; + /// @} + + /// @{ + /// Enlarge a bounding box by the bounding box of the primitive. A + /// return value of false indicates an error in the operation, most + /// likely an invalid P. For any attribute other than the position + /// these methods simply enlarge the bounding box based on the vertex. + virtual bool enlargeBoundingBox(UT_BoundingRect &b, + const GA_Attribute *P) const; + virtual bool enlargeBoundingBox(UT_BoundingBox &b, + const GA_Attribute *P) const; + virtual void enlargePointBounds(UT_BoundingBox &e) const; + /// @} + /// Enlarge a bounding sphere to encompass the primitive. A return value + /// of false indicates an error in the operation, most likely an invalid + /// P. For any attribute other than the position this method simply + /// enlarges the sphere based on the vertex. + virtual bool enlargeBoundingSphere(UT_BoundingSphere &b, + const GA_Attribute *P) const; + + /// Accessor for the local 3x3 affine transform matrix for the primitive. + /// For frustum maps, this will be transform as if the taper value is set + /// to 1. + /// @{ + virtual void getLocalTransform(UT_Matrix3D &result) const; + virtual void setLocalTransform(const UT_Matrix3D &new_mat3); + /// @} + + /// @internal Hack to condition 4x4 matrices that we avoid creating what + /// OpenVDB erroneously thinks are singular matrices. Returns true if mat4 + /// was modified. + static bool conditionMatrix(UT_Matrix4D &mat4); + + /// Visualization accessors + /// @{ + const GEO_VolumeOptions &getVisOptions() const { return myVis; } + + void setVisualization( + GEO_VolumeVis vismode, + fpreal iso, + fpreal density) + { + myVis.myMode = vismode; + myVis.myIso = iso; + myVis.myDensity = density; + } + GEO_VolumeVis getVisualization() const { return myVis.myMode; } + fpreal getVisIso() const { return myVis.myIso; } + fpreal getVisDensity() const { return myVis.myDensity; } + /// @} + + /// Load the order from a JSON value + bool loadOrder(const UT_JSONValue &p); + + /// @{ + /// Save/Load vdb to a JSON stream + bool saveVDB(UT_JSONWriter &w) const; + bool loadVDB(UT_JSONParser &p); + /// @} + + bool saveVisualization( + UT_JSONWriter &w, + const GA_SaveMap &map) const; + bool loadVisualization( + UT_JSONParser &p, + const GA_LoadMap &map); + + /// Method to perform quick lookup of vertex without the virtual call + GA_Offset fastVertexOffset(GA_Size UT_IF_ASSERT_P(index)) const + { + UT_ASSERT_P(index < 1); + return myVertex; + } + + void setVertexPoint(int i, GA_Offset pt) + { + if (i == 0) + wireVertex(myVertex, pt); + } + + /// @brief Computes the total density of the volume, scaled by + /// the volume's size. Negative values will be ignored. + fpreal calcPositiveDensity() const; + + bool hasGrid() const { return myGridAccessor.hasGrid(); } + + /// @brief If this primitive's grid's voxel data (i.e., its tree) + /// is shared, replace the tree with a deep copy of itself that is + /// not shared with anyone else. + void makeGridUnique() + { myGridAccessor.makeGridUnique(); } + + /// @brief Return a reference to this primitive's grid. + /// @note Calling setGrid() invalidates all references previously returned. + const openvdb::GridBase & getConstGrid() const + { return myGridAccessor.getConstGrid(*this); } + /// @brief Return a reference to this primitive's grid. + /// @note Calling setGrid() invalidates all references previously returned. + const openvdb::GridBase & getGrid() const + { return getConstGrid(); } + /// @brief Return a reference to this primitive's grid. + /// @note Calling setGrid() invalidates all references previously returned. + /// @warning Call makeGridUnique() before modifying the grid's voxel data. + openvdb::GridBase & getGrid() + { + incrGridUniqueIds(); + return myGridAccessor.getGrid(*this); + } + + /// @brief Return a shared pointer to this primitive's grid. + /// @note Calling setGrid() causes the grid to which the shared pointer + /// refers to be disassociated with this primitive. + openvdb::GridBase::ConstPtr getConstGridPtr() const + { return myGridAccessor.getConstGridPtr(*this); } + /// @brief Return a shared pointer to this primitive's grid. + /// @note Calling setGrid() causes the grid to which the shared pointer + /// refers to be disassociated with this primitive. + openvdb::GridBase::ConstPtr getGridPtr() const + { return getConstGridPtr(); } + /// @brief Return a shared pointer to this primitive's grid. + /// @note Calling setGrid() causes the grid to which the shared pointer + /// refers to be disassociated with this primitive. + /// @warning Call makeGridUnique() before modifying the grid's voxel data. + openvdb::GridBase::Ptr getGridPtr() + { + incrGridUniqueIds(); + return myGridAccessor.getGridPtr(*this); + } + + /// @brief Set this primitive's grid to a shallow copy of the given grid. + /// @note Invalidates all previous getGrid() and getConstGrid() references + void setGrid(const openvdb::GridBase &grid) + { + incrGridUniqueIds(); + myGridAccessor.setGrid(grid, *this); + } + + /// @brief Return a reference to this primitive's grid metadata. + /// @note Calling setGrid() invalidates all references previously returned. + const openvdb::MetaMap& getConstMetadata() const + { return getConstGrid(); } + /// @brief Return a reference to this primitive's grid metadata. + /// @note Calling setGrid() invalidates all references previously returned. + const openvdb::MetaMap& getMetadata() const + { return getConstGrid(); } + /// @brief Return a reference to this primitive's grid metadata. + /// @note Calling setGrid() invalidates all references previously returned. + openvdb::MetaMap& getMetadata() + { + incrMetadataUniqueId(); + return myGridAccessor.getGrid(*this); + } + + /// @brief Return the value of this primitive's "name" attribute + /// in the given detail. + const char * getGridName() const; + + /// @brief Return this primitive's serial number. + /// @details A primitive's serial number never changes. + /// @todo Because serial numbers are currently 32-bit, it is possible, + /// though unlikely, for two primitives to have the same serial number. + UniqueId getUniqueId() const + { return static_cast(myUniqueId); } + + /// @brief Return the serial number of this primitive's voxel data. + /// @details The serial number is incremented whenever a non-const + /// reference or pointer to this primitive's grid is requested + /// (whether or not the voxel data is ultimately modified). + UniqueId getTreeUniqueId() const + { return static_cast(myTreeUniqueId); } + /// @brief Return the serial number of this primitive's grid metadata. + /// @details The serial number is incremented whenever a non-const + /// reference to the metadata or non-const access to the grid is requested + /// (whether or not the metadata is ultimately modified). + UniqueId getMetadataUniqueId() const + { return static_cast(myMetadataUniqueId); } + /// @brief Return the serial number of this primitive's transform. + /// @details The serial number is incremented whenever the transform + /// is modified or non-const access to this primitive's grid is requested + /// (whether or not the transform is ultimately modified). + UniqueId getTransformUniqueId() const + { return static_cast(myTransformUniqueId); } + +protected: + typedef SYS_AtomicCounter AtomicUniqueId; // 32-bit on non-AMD systems + + /// Register intrinsic attributes +#if (UT_VERSION_INT >= 0x0c010048) // 12.1.72 or later + GA_DECLARE_INTRINSICS() +#else + static GA_IntrinsicManager::Registrar + registerIntrinsics(GA_PrimitiveDefinition &defn); +#endif + + /// Return true if the given metadata token is an intrinsic + static bool isIntrinsicMetadata(const char *name); + + /// @warning vertexPoint() doesn't check the bounds. Use with caution. + GA_Offset vertexPoint(GA_Size) const + { return getDetail().vertexPoint(myVertex); } + + /// Report approximate memory usage, excluding sizeof(*this), + /// because the subclass doesn't have access to myGridAccessor. + int64 getBaseMemoryUsage() const; + + /// @brief Return an ID number that is guaranteed to be unique across + /// all VDB primitives. + static UniqueId nextUniqueId(); + + void incrTreeUniqueId() + { myTreeUniqueId.maximum(nextUniqueId()); } + void incrMetadataUniqueId() + { myMetadataUniqueId.maximum(nextUniqueId()); } + void incrTransformUniqueId() + { myTransformUniqueId.maximum(nextUniqueId()); } + void incrGridUniqueIds() + { + incrTreeUniqueId(); + incrMetadataUniqueId(); + incrTransformUniqueId(); + } + + /// @brief Replace this primitive's grid with a shallow copy + /// of another primitive's grid. + void copyGridFrom(const GEO_PrimVDB&); + + GA_Offset myVertex; + + /// @brief GridAccessor manages access to a GEO_PrimVDB's grid. + /// @details In keeping with OpenVDB library conventions, the grid + /// is stored internally by shared pointer. However, grid objects + /// are never shared among primitives, though their voxel data + /// (i.e., their trees) may be shared. + ///

Among other things, GridAccessor + /// - ensures that each primitive's transform and metadata are unique + /// (i.e., not shared with anyone else) + /// - allows primitives to share voxel data but, via makeGridUnique(), + /// provides a way to break the connection + /// - ensures that the primitive's transform and the grid's transform + /// are in sync (specifically, the translation component, which is + /// stored independently as a vertex offset). + class GridAccessor + { + public: + GridAccessor() : myStorageType(UT_VDB_INVALID) + { } + + openvdb::GridBase & + getGrid(const GEO_PrimVDB &prim) + { updateGridTranslates(prim); return *myGrid; } + + const openvdb::GridBase & + getConstGrid(const GEO_PrimVDB &prim) const + { updateGridTranslates(prim); return *myGrid; } + + openvdb::GridBase::Ptr + getGridPtr(const GEO_PrimVDB &prim) + { updateGridTranslates(prim); return myGrid; } + + openvdb::GridBase::ConstPtr + getConstGridPtr(const GEO_PrimVDB &prim) const + { updateGridTranslates(prim); return myGrid; } + + // These accessors will ensure the transform's translate is set into + // the vertex position. + void setGrid(const openvdb::GridBase& grid, GEO_PrimVDB& prim) + { setGridAdapter(&grid, prim); } + void setTransform( + const openvdb::math::Transform &xform, + GEO_PrimVDB &prim) + { setTransformAdapter(&xform, prim); } + + void makeGridUnique(); + + UT_VDBType getStorageType() const { return myStorageType; } + bool hasGrid() const { return myGrid != 0; } + + private: + void updateGridTranslates(const GEO_PrimVDB &prim) const; + void setVertexPosition( + const openvdb::math::Transform &xform, + GEO_PrimVDB &prim) + { setVertexPositionAdapter(&xform, prim); } + + void setGridAdapter(const void* grid, GEO_PrimVDB&); + void setTransformAdapter(const void* xform, GEO_PrimVDB&); + void setVertexPositionAdapter(const void* xform, GEO_PrimVDB&); + + private: + openvdb::GridBase::Ptr myGrid; + UT_VDBType myStorageType; + }; + +private: + void activateIndexBBoxAdapter( + const void* bbox, + ActivateOperation, + bool setvalue, fpreal value); + + + GridAccessor myGridAccessor; + + GEO_VolumeOptions myVis; + +#if (UT_VERSION_INT < 0x0c050000) // earlier than 12.5.0 + bool myStashedState; +#endif + + AtomicUniqueId myUniqueId; + AtomicUniqueId myTreeUniqueId; + AtomicUniqueId myMetadataUniqueId; + AtomicUniqueId myTransformUniqueId; + +}; // class GEO_PrimVDB + + +#ifndef SESI_OPENVDB +namespace openvdb_houdini { +using ::GEO_VolumeOptions; +using ::GEO_PrimVDB; +} +#endif + + +//////////////////////////////////////// + + +namespace UT_VDBUtils { + +// This overload of UT_VDBUtils::callTypedGrid(), for GridBaseType = GEO_PrimVDB, +// calls makeGridUnique() on the primitive just before instantiating and +// invoking the functor on the primitive's grid. This delays the call +// to makeGridUnique() until it is known to be necessary and thus avoids +// making deep copies of grids of types that won't be processed. +template +inline void +callTypedGrid(GEO_PrimVDB& prim, OpType& op) +{ + prim.makeGridUnique(); + +#ifdef _MSC_VER + op.operator()(*(UTverify_cast(&prim.getGrid()))); +#else + op.template operator()(*(UTverify_cast(&prim.getGrid()))); +#endif +} + +// Overload of callTypedGrid() for GridBaseType = const GEO_PrimVDB +template +inline void +callTypedGrid(const GEO_PrimVDB& prim, OpType& op) +{ +#ifdef _MSC_VER + op.operator()(*(UTverify_cast(&prim.getConstGrid()))); +#else + op.template operator()(*(UTverify_cast(&prim.getConstGrid()))); +#endif +} + +} // namespace UT_VDBUtils + +// Define UTvdbProcessTypedGrid*() (see UT_VDBUtils.h) for grids +// belonging to primitives, for various subsets of grid types. +UT_VDB_DECL_PROCESS_TYPED_GRID(GEO_PrimVDB&) +UT_VDB_DECL_PROCESS_TYPED_GRID(const GEO_PrimVDB&) + + +//////////////////////////////////////// + + +/// @brief Utility function to process the grid of a const primitive using functor @a op. +/// @details It will invoke @code op.operator()(const GridT &grid) @endcode +/// @{ +template +inline bool GEOvdbProcessTypedGrid(const GEO_PrimVDB &vdb, OpT &op) +{ + return UTvdbProcessTypedGrid(vdb.getStorageType(), vdb.getGrid(), op); +} + +template +inline bool GEOvdbProcessTypedGridReal(const GEO_PrimVDB &vdb, OpT &op) +{ + return UTvdbProcessTypedGridReal(vdb.getStorageType(), vdb.getGrid(), op); +} + +template +inline bool GEOvdbProcessTypedGridScalar(const GEO_PrimVDB &vdb, OpT &op) +{ + return UTvdbProcessTypedGridScalar(vdb.getStorageType(), vdb.getGrid(), op); +} + +template +inline bool GEOvdbProcessTypedGridTopology(const GEO_PrimVDB &vdb, OpT &op) +{ + return UTvdbProcessTypedGridTopology(vdb.getStorageType(), vdb.getGrid(), op); +} + +template +inline bool GEOvdbProcessTypedGridVec3(const GEO_PrimVDB &vdb, OpT &op) +{ + return UTvdbProcessTypedGridVec3(vdb.getStorageType(), vdb.getGrid(), op); +} + +/// @brief Utility function to process the grid of a primitive using functor @a op. +/// @param vdb the primitive whose grid is to be processed +/// @param op a functor with a call operator of the form +/// @code op.operator()(GridT &grid) @endcode +/// @param makeUnique if @c true, call vdb.makeGridUnique() before +/// invoking the functor +/// @{ +template +inline bool GEOvdbProcessTypedGrid(GEO_PrimVDB &vdb, OpT &op, bool makeUnique = true) +{ + if (makeUnique) return UTvdbProcessTypedGrid(vdb.getStorageType(), vdb, op); + return UTvdbProcessTypedGrid(vdb.getStorageType(), vdb.getGrid(), op); +} + +template +inline bool GEOvdbProcessTypedGridReal(GEO_PrimVDB &vdb, OpT &op, bool makeUnique = true) +{ + if (makeUnique) return UTvdbProcessTypedGridReal(vdb.getStorageType(), vdb, op); + return UTvdbProcessTypedGridReal(vdb.getStorageType(), vdb.getGrid(), op); +} + +template +inline bool GEOvdbProcessTypedGridScalar(GEO_PrimVDB &vdb, OpT &op, bool makeUnique = true) +{ + if (makeUnique) return UTvdbProcessTypedGridScalar(vdb.getStorageType(), vdb, op); + return UTvdbProcessTypedGridScalar(vdb.getStorageType(), vdb.getGrid(), op); +} + +template +inline bool GEOvdbProcessTypedGridTopology(GEO_PrimVDB &vdb, OpT &op, bool makeUnique = true) +{ + if (makeUnique) return UTvdbProcessTypedGridTopology(vdb.getStorageType(), vdb, op); + return UTvdbProcessTypedGridTopology(vdb.getStorageType(), vdb.getGrid(), op); +} + +template +inline bool GEOvdbProcessTypedGridVec3(GEO_PrimVDB &vdb, OpT &op, bool makeUnique = true) +{ + if (makeUnique) return UTvdbProcessTypedGridVec3(vdb.getStorageType(), vdb, op); + return UTvdbProcessTypedGridVec3(vdb.getStorageType(), vdb.getGrid(), op); +} +/// @} + +#endif // __HDK_GEO_PrimVDB__ + +#endif // UT_VERSION_INT < 0x0c050157 // earlier than 12.5.343 + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb_houdini/GT_GEOPrimCollectVDB.h b/nuparu/include/openvdb/include/openvdb_houdini/GT_GEOPrimCollectVDB.h new file mode 100644 index 00000000..d8d568e6 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb_houdini/GT_GEOPrimCollectVDB.h @@ -0,0 +1,92 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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. +// +/////////////////////////////////////////////////////////////////////////// + +/* + * PROPRIETARY INFORMATION. This software is proprietary to + * Side Effects Software Inc., and is not to be reproduced, + * transmitted, or disclosed in any way without written permission. + * + * Produced by: + * Side Effects Software Inc + * 123 Front Street West, Suite 1401 + * Toronto, Ontario + * Canada M5J 2M2 + * 416-504-9876 + * + * NAME: GT_GEOPrimCollectVDB.h ( GT Library, C++) + * + * COMMENTS: + */ + +#ifndef __GT_GEOPrimCollectVDB__ +#define __GT_GEOPrimCollectVDB__ + +#include +#include + +namespace openvdb_houdini { + +class OPENVDB_HOUDINI_API GT_GEOPrimCollectVDB : public GT_GEOPrimCollect +{ +public: + GT_GEOPrimCollectVDB(const GA_PrimitiveTypeId &id); + virtual ~GT_GEOPrimCollectVDB(); + + static void registerPrimitive(const GA_PrimitiveTypeId &id); + + virtual GT_GEOPrimCollectData * + beginCollecting( + const GT_GEODetailListHandle &, + const GT_RefineParms *) const; + + virtual GT_PrimitiveHandle + collect( + const GT_GEODetailListHandle &geometry, + const GEO_Primitive *const* prim_list, + int nsegments, + GT_GEOPrimCollectData *data) const; + + virtual GT_PrimitiveHandle + endCollecting( + const GT_GEODetailListHandle &geometry, + GT_GEOPrimCollectData *data) const; +private: + + GA_PrimitiveTypeId myId; + +}; + +} // namespace openvdb_houdini + +#endif // __GT_GEOPrimCollectVDB__ + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb_houdini/GU_PrimVDB.h b/nuparu/include/openvdb/include/openvdb_houdini/GU_PrimVDB.h new file mode 100644 index 00000000..807d77bf --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb_houdini/GU_PrimVDB.h @@ -0,0 +1,355 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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. +// +/////////////////////////////////////////////////////////////////////////// + +/* + * PROPRIETARY INFORMATION. This software is proprietary to + * Side Effects Software Inc., and is not to be reproduced, + * transmitted, or disclosed in any way without written permission. + * + * Produced by: + * Jeff Lait + * Side Effects Software Inc + * 477 Richmond Street West + * Toronto, Ontario + * Canada M5V 3E7 + * 416-504-9876 + * + * NAME: GU_PrimVDB.h ( GU Library, C++) + * + * COMMENTS: Custom VDB primitive. + */ + +#include +#if !defined(SESI_OPENVDB) && (UT_VERSION_INT >= 0x0c050157) // 12.5.343 or later + +#include + +namespace openvdb_houdini { +using ::GU_PrimVDB; +} + +#else // earlier than 12.5.343 + +#ifndef __HDK_GU_PrimVDB__ +#define __HDK_GU_PrimVDB__ + +//#include "GU_API.h" +#include +#include "GEO_PrimVDB.h" +#include +#include +#include +#include +#include +#include + + +class GA_Attribute; +class GEO_PrimVolume; +class UT_MemoryCounter; + + +class OPENVDB_HOUDINI_API GU_PrimVDB : public GEO_PrimVDB, public GU_Primitive +{ +protected: + /// NOTE: Primitives should not be deleted directly. They are managed + /// by the GA_PrimitiveList and the stash. + virtual ~GU_PrimVDB() {} + +public: + /// NOTE: This constructor should only be called via GU_PrimitiveFactory. + GU_PrimVDB(GU_Detail *gdp, GA_Offset offset=GA_INVALID_OFFSET) + : GEO_PrimVDB(gdp, offset) + , GU_Primitive() + {} + + /// NOTE: This constructor should only be called via GU_PrimitiveFactory. + GU_PrimVDB(const GA_MergeMap &map, GA_Detail &detail, + GA_Offset offset, const GU_PrimVDB &src_prim) + : GEO_PrimVDB(map, detail, offset, src_prim) + , GU_Primitive() + {} + + /// Report approximate memory usage. + virtual int64 getMemoryUsage() const; + +#ifndef SESI_OPENVDB + /// Allows you to find out what this primitive type was named. + static GA_PrimitiveTypeId theTypeId() { return theDefinition->getId(); } + + /// Must be invoked during the factory callback to add us to the + /// list of primitives + static void registerMyself(GA_PrimitiveFactory *factory); +#endif + + virtual const GA_PrimitiveDefinition &getTypeDef() const + { + UT_ASSERT(theDefinition); + return *theDefinition; + } + + // Conversion Methods + + virtual GEO_Primitive *convert(GU_ConvertParms &parms, + GA_PointGroup *usedpts = 0); + virtual GEO_Primitive *convertNew(GU_ConvertParms &parms); + + /// Convert all GEO_PrimVolume primitives in geometry to + /// GEO_PrimVDB, preserving prim/vertex/point attributes (and prim/point + /// groups if requested). + static void convertVolumesToVDBs( + GU_Detail &dst_geo, + const GU_Detail &src_geo, + GU_ConvertParms &parms, + bool flood_sdf, + bool prune, + fpreal tolerance, + bool keep_original); + + /// Convert all GEO_PrimVDB primitives in geometry to parms.toType, + /// preserving prim/vertex/point attributes (and prim/point groups if + /// requested). + /// @{ + static void convertVDBs( + GU_Detail &dst_geo, + const GU_Detail &src_geo, + GU_ConvertParms &parms, + fpreal adaptivity, + bool keep_original); + static void convertVDBs( + GU_Detail &dst_geo, + const GU_Detail &src_geo, + GU_ConvertParms &parms, + fpreal adaptivity, + bool keep_original, + bool split_disjoint_volumes); + /// @} + +#if (UT_VERSION_INT < 0x0d050000) // Earlier than 13.5 + virtual void *castTo (void) const; + virtual const GEO_Primitive *castToGeo(void) const; +#endif + + // NOTE: For static member functions please call in the following + // manner. = GU_PrimVDB:: + // i.e. partptr = GU_PrimVDB::build(params...); + + // Optional Build Method + + static GU_PrimVDB * build(GU_Detail *gdp, bool append_points = true); + + /// Store a VDB grid in a new VDB primitive and add the primitive + /// to a geometry detail. + /// @param gdp the detail to which to add the new primitive + /// @param grid a grid to be associated with the new primitive + /// @param src if non-null, copy attributes and groups from this primitive + /// @param name if non-null, set the new primitive's @c name attribute to + /// this string; otherwise, if @a src is non-null, use its name + static GU_PrimVDB* buildFromGrid(GU_Detail& gdp, openvdb::GridBase::Ptr grid, + const GEO_PrimVDB* src = NULL, const char* name = NULL) + { + return GU_PrimVDB::buildFromGridAdapter(gdp, &grid, src, name); + } + + /// Create new VDB primitive from the given native volume primitive + static GU_PrimVDB * buildFromPrimVolume( + GU_Detail &geo, + const GEO_PrimVolume &vol, + const char *name, + const bool flood_sdf = false, + const bool prune = false, + const float tolerance = 0.0); + + /// A fast method for converting a primitive volume to a polysoup via VDB + /// into the given gdp. It will _not_ copy attributes because this is a + /// special case used for display purposes only. + static void convertPrimVolumeToPolySoup( + GU_Detail &dst_geo, + const GEO_PrimVolume &src_vol); + + virtual void normal(NormalComp &output) const; + +#if (UT_VERSION_INT < 0x0d050000) // Earlier than 13.5 + virtual int intersectRay(const UT_Vector3 &o, const UT_Vector3 &d, + float tmax = 1E17F, float tol = 1E-12F, + float *distance = 0, UT_Vector3 *pos = 0, + UT_Vector3 *nml = 0, int accurate = 0, + float *u = 0, float *v = 0, + int ignoretrim = 1) const; +#endif + + // callermustdelete is true if the returned cache is to be deleted by + // the caller. +#if (UT_VERSION_INT < 0x0d050000) // Earlier than 13.5 + +#if (UT_VERSION_INT >= 0x0d000000) // 13.0 or later + SYS_DEPRECATED_HDK(13.0) +#endif + virtual GU_RayIntersect *createRayCache(int &callermustdelete); +#endif + + /// @brief Transfer any metadata associated with this primitive's + /// VDB grid to primitive attributes. + void syncAttrsFromMetadata(); + + /// @brief Transfer any metadata associated with a VDB grid + /// to primitive attributes on a VDB primitive. + /// @param prim the primitive to be populated with attributes + /// @param grid the grid whose metadata should be transferred + /// @param gdp the detail to which to transfer attributes + static void createGridAttrsFromMetadata( + const GEO_PrimVDB& prim, + const openvdb::GridBase& grid, + GEO_Detail& gdp) + { + GU_PrimVDB::createGridAttrsFromMetadataAdapter(prim, &grid, gdp); + } + + /// @brief Transfer any metadata associated with the given MetaMap + /// to attributes on the given element specified by owner. + /// @param owner the type of element + /// @param element the offset of the element + /// @param meta_map the metadata that should be transferred + /// @param gdp the detail to which to transfer attributes + static void createAttrsFromMetadata( + GA_AttributeOwner owner, + GA_Offset element, + const openvdb::MetaMap& meta_map, + GEO_Detail& gdp) + { + GU_PrimVDB::createAttrsFromMetadataAdapter(owner, element, &meta_map, gdp); + } + + /// @brief Transfer a VDB primitive's attributes to a VDB grid as metadata. + /// @param grid the grid to be populated with metadata + /// @param prim the primitive whose attributes should be transferred + /// @param gdp the detail from which to retrieve primitive attributes + static void createMetadataFromGridAttrs( + openvdb::GridBase& grid, + const GEO_PrimVDB& prim, + const GEO_Detail& gdp) + { + GU_PrimVDB::createMetadataFromGridAttrsAdapter(&grid, prim, gdp); + } + + /// @brief Transfer attributes to VDB metadata. + /// @param meta_map the output metadata + /// @param owner the type of element + /// @param element the offset of the element + /// @param geo the detail from which to retrieve primitive attributes + static void createMetadataFromAttrs( + openvdb::MetaMap& meta_map, + GA_AttributeOwner owner, + GA_Offset element, + const GEO_Detail& geo) + { + GU_PrimVDB::createMetadataFromAttrsAdapter(&meta_map, owner, element, geo); + } + +private: // METHODS + + /// Add a border of the given radius by evaluating from the given volume. + /// It assumes that the VDB is a float grid and that the voxel array has + /// the same index space, so this can really only be safely called after + /// buildFromPrimVolume(). This is used to ensure that non-constant borders + /// can be converted at the expense of some extra memory. + void expandBorderFromPrimVolume( + const GEO_PrimVolume &vol, + int border_radius); + + GEO_Primitive * convertToNewPrim( + GEO_Detail &dst_geo, + GU_ConvertParms &parms, + fpreal adaptivity, + bool split_disjoint_volumes, + bool &success) const; + GEO_Primitive * convertToPrimVolume( + GEO_Detail &dst_geo, + GU_ConvertParms &parms, + bool split_disjoint_volumes) const; + GEO_Primitive * convertToPoly( + GEO_Detail &dst_geo, + GU_ConvertParms &parms, + fpreal adaptivity, + bool buildpolysoup, + bool &success) const; + + static GU_PrimVDB* buildFromGridAdapter( + GU_Detail& gdp, + void* grid, + const GEO_PrimVDB*, + const char* name); + static void createGridAttrsFromMetadataAdapter( + const GEO_PrimVDB& prim, + const void* grid, + GEO_Detail& gdp); + static void createMetadataFromGridAttrsAdapter( + void* grid, + const GEO_PrimVDB&, + const GEO_Detail&); + + static void createAttrsFromMetadataAdapter( + GA_AttributeOwner owner, + GA_Offset element, + const void* meta_map_ptr, + GEO_Detail& geo); + + static void createMetadataFromAttrsAdapter( + void* meta_map_ptr, + GA_AttributeOwner owner, + GA_Offset element, + const GEO_Detail& geo); + +private: // DATA + + static GA_PrimitiveDefinition *theDefinition; + friend class GU_PrimitiveFactory; +#if (UT_VERSION_INT >= 0x0d000000) // 13.0 or later + SYS_DEPRECATED_PUSH_DISABLE() +#endif +}; +#if (UT_VERSION_INT >= 0x0d000000) // 13.0 or later + SYS_DEPRECATED_POP_DISABLE() +#endif + + +#ifndef SESI_OPENVDB +namespace openvdb_houdini { +using ::GU_PrimVDB; +} // namespace openvdb_houdini +#endif + +#endif // __HDK_GU_PrimVDB__ + +#endif // UT_VERSION_INT < 0x0c050157 // earlier than 12.5.343 + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb_houdini/GU_VDBPointTools.h b/nuparu/include/openvdb/include/openvdb_houdini/GU_VDBPointTools.h new file mode 100644 index 00000000..8c8c0756 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb_houdini/GU_VDBPointTools.h @@ -0,0 +1,434 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 GU_VDBPointTools.h +/// @author FX R&D OpenVDB team +/// +/// @brief Collection of PointIndexGrid helpers for Houdini + +#ifndef __GU_VDBPOINTTOOLS_H_HAS_BEEN_INCLUDED__ +#define __GU_VDBPOINTTOOLS_H_HAS_BEEN_INCLUDED__ + +#if defined(SESI_OPENVDB) + #include "GU_Detail.h" +#else + #include +#endif +#include +#include + +#include + +#if (UT_MAJOR_VERSION_INT >= 15) + #include +#endif + +#if (UT_MAJOR_VERSION_INT >= 14) + #include + #include + #include + #include +#endif + +#include +#include +#include +#include + + +/// @brief Houdini point attribute wrapper +template +struct GU_VDBPointList +{ + typedef boost::shared_ptr Ptr; + typedef boost::shared_ptr ConstPtr; + + typedef VectorType PosType; + typedef typename PosType::value_type ScalarType; + + GU_VDBPointList(const GU_Detail& detail, const GA_PointGroup* group = NULL) + : mPositionHandle(detail.getP()) + , mVelocityHandle() + , mRadiusHandle() + , mIndexMap(&detail.getP()->getIndexMap()) + , mOffsets() + , mSize(mIndexMap->indexSize()) + { + if (group) { + mSize = group->entries(); + mOffsets.reserve(mSize); + + GA_Offset start, end; + GA_Range range(*group); + for (GA_Iterator it = range.begin(); it.blockAdvance(start, end); ) { + for (GA_Offset off = start; off < end; ++off) { + mOffsets.push_back(off); + } + } + + getOffset = &GU_VDBPointList::offsetFromGroupMap; + } else if (mIndexMap->isTrivialMap()) { + getOffset = &GU_VDBPointList::offsetFromIndexCast; + } else { + getOffset = &GU_VDBPointList::offsetFromGeoMap; + } + + // Bind optional attributes + + GA_ROAttributeRef velRef = detail.findFloatTuple(GA_ATTRIB_POINT, GEO_STD_ATTRIB_VELOCITY, 3); + if (velRef.isValid()) { + mVelocityHandle.bind(velRef.getAttribute()); + } + + GA_ROAttributeRef radRef = detail.findFloatTuple(GA_ATTRIB_POINT, GEO_STD_ATTRIB_PSCALE); + if (radRef.isValid()) { + mRadiusHandle.bind(radRef.getAttribute()); + } + } + + static Ptr create(const GU_Detail& detail, const GA_PointGroup* group = NULL) + { + return Ptr(new GU_VDBPointList(detail, group)); + } + + size_t size() const { return mSize; } + + bool hasVelocity() const { return mVelocityHandle.isValid(); } + bool hasRadius() const { return mRadiusHandle.isValid(); } + + // Index access methods + + void getPos(size_t n, PosType& xyz) const { + getPosFromOffset((this->*getOffset)(n), xyz); + } + + void getVelocity(size_t n, PosType& v) const { + getVelocityFromOffset((this->*getOffset)(n), v); + } + + void getRadius(size_t n, ScalarType& r) const { + getRadiusFromOffset((this->*getOffset)(n), r); + } + + // Offset access methods + + GA_Offset offsetFromIndex(size_t n) const { + return (this->*getOffset)(n); + } + + void getPosFromOffset(const GA_Offset offset, PosType& xyz) const { + const UT_Vector3 data = mPositionHandle.get(offset); + xyz[0] = ScalarType(data[0]); + xyz[1] = ScalarType(data[1]); + xyz[2] = ScalarType(data[2]); + } + + void getVelocityFromOffset(const GA_Offset offset, PosType& v) const { + const UT_Vector3 data = mVelocityHandle.get(offset); + v[0] = ScalarType(data[0]); + v[1] = ScalarType(data[1]); + v[2] = ScalarType(data[2]); + } + + void getRadiusFromOffset(const GA_Offset offset, ScalarType& r) const { + r = ScalarType(mRadiusHandle.get(offset)); + } + +private: + // Disallow copying + GU_VDBPointList(const GU_VDBPointList&); + GU_VDBPointList& operator=(const GU_VDBPointList&); + + GA_Offset (GU_VDBPointList::* getOffset)(const size_t) const; + + GA_Offset offsetFromGeoMap(const size_t n) const { + return mIndexMap->offsetFromIndex(GA_Index(n)); + } + + GA_Offset offsetFromGroupMap(const size_t n) const { + return mOffsets[n]; + } + + GA_Offset offsetFromIndexCast(const size_t n) const { + return GA_Offset(n); + } + + GA_ROHandleV3 mPositionHandle, mVelocityHandle; + GA_ROHandleF mRadiusHandle; + GA_IndexMap const * const mIndexMap; + std::vector mOffsets; + size_t mSize; +}; // GU_VDBPointList + + +//////////////////////////////////////// + + +// PointIndexGrid utility methods + + +namespace GU_VDBPointToolsInternal { + +template +struct IndexToOffsetOp { + IndexToOffsetOp(const PointArrayType& points): mPointList(&points) {} + + template + void operator()(LeafT &leaf, size_t /*leafIndex*/) const { + typename LeafT::IndexArray& indices = leaf.indices(); + for (size_t n = 0, N = indices.size(); n < N; ++n) { + indices[n] = typename LeafT::ValueType(mPointList->offsetFromIndex(GA_Index(indices[n]))); + } + } + PointArrayType const * const mPointList; +}; + +#if (UT_MAJOR_VERSION_INT >= 14) + +struct PackedMaskConstructor +{ + PackedMaskConstructor(const std::vector& prims, + const openvdb::math::Transform& xform) + : mPrims(prims.empty() ? NULL : &prims.front()) + , mXForm(xform) + , mMaskGrid(new openvdb::MaskGrid(false)) + { + mMaskGrid->setTransform(mXForm.copy()); + } + + PackedMaskConstructor(PackedMaskConstructor& rhs, tbb::split) + : mPrims(rhs.mPrims) + , mXForm(rhs.mXForm) + , mMaskGrid(new openvdb::MaskGrid(false)) + { + mMaskGrid->setTransform(mXForm.copy()); + } + + openvdb::MaskGrid::Ptr getMaskGrid() { return mMaskGrid; } + + void join(PackedMaskConstructor& rhs) { mMaskGrid->tree().topologyUnion(rhs.mMaskGrid->tree()); } + + void operator()(const tbb::blocked_range& range) + { + +#if (UT_MAJOR_VERSION_INT >= 15) + GU_PackedContext packedcontext; +#endif + + for (size_t n = range.begin(), N = range.end(); n < N; ++n) { + const GA_Primitive *prim = mPrims[n]; + if (!prim || !GU_PrimPacked::isPackedPrimitive(*prim)) continue; + + const GU_PrimPacked * pprim = static_cast(prim); + + GU_Detail tmpdetail; + const GU_Detail *detailtouse; + +#if (UT_MAJOR_VERSION_INT >= 15) + + GU_DetailHandleAutoReadLock readlock(pprim->getPackedDetail(packedcontext)); + + UT_Matrix4D mat; + pprim->getFullTransform4(mat); + if (mat.isIdentity() && readlock.isValid() && readlock.getGdp()) { + detailtouse = readlock.getGdp(); + } else { + pprim->unpackWithContext(tmpdetail, packedcontext); + detailtouse = &tmpdetail; + } +#else + pprim->unpack(tmpdetail); + detailtouse = &tmpdetail; +#endif + + GU_VDBPointList points(*detailtouse); + openvdb::MaskGrid::Ptr grid = openvdb::tools::createPointMaskGrid(points, mXForm); + mMaskGrid->tree().topologyUnion(grid->tree()); + } + } + +private: + GA_Primitive const * const * const mPrims; + openvdb::math::Transform mXForm; + openvdb::MaskGrid::Ptr mMaskGrid; +}; // struct PackedMaskConstructor + + +inline void +getPackedPrimitiveOffsets(const GU_Detail& detail, std::vector& primitives) +{ + const GA_Size numPacked = GU_PrimPacked::countPackedPrimitives(detail); + + primitives.clear(); + primitives.reserve(size_t(numPacked)); + + if (numPacked != GA_Size(0)) { + GA_Offset start, end; + GA_Range range = detail.getPrimitiveRange(); + const GA_PrimitiveList& primList = detail.getPrimitiveList(); + + for (GA_Iterator it = range.begin(); it.blockAdvance(start, end); ) { + for (GA_Offset off = start; off < end; ++off) { + + const GA_Primitive *prim = primList.get(off); + + if (prim && GU_PrimPacked::isPackedPrimitive(*prim)) { + primitives.push_back(prim); + } + } + } + } +} + +#endif + + +} // namespace GU_VDBPointToolsInternal + + +//////////////////////////////////////// + + +/// @brief Utility method to construct a GU_VDBPointList. +/// @details The GU_VDBPointList is compatible with the PointIndexGrid and ParticleAtals structures. +inline GU_VDBPointList::Ptr +GUvdbCreatePointList(const GU_Detail& detail, const GA_PointGroup* pointGroup = NULL) +{ + return GU_VDBPointList::create(detail, pointGroup); +} + + +/// @brief Utility method to change point indices into Houdini geometry offsets. +/// @note PointIndexGrid's that store Houdini geometry offsets are not +/// safe to write to disk, offsets are not guaranteed to be immutable +/// under defragmentation operations or I/O. +template +inline void +GUvdbConvertIndexToOffset(PointIndexTreeType& tree, const PointArrayType& points) +{ + openvdb::tree::LeafManager leafnodes(tree); + leafnodes.foreach(GU_VDBPointToolsInternal::IndexToOffsetOp(points)); +} + + +/// @brief Utility method to construct a PointIndexGrid. +/// @details The PointIndexGrid supports fast spatial queries for points. +inline openvdb::tools::PointIndexGrid::Ptr +GUvdbCreatePointIndexGrid( + const openvdb::math::Transform& xform, + const GU_Detail& detail, + const GA_PointGroup* pointGroup = NULL) +{ + GU_VDBPointList points(detail, pointGroup); + return openvdb::tools::createPointIndexGrid(points, xform); +} + + +/// @brief Utility method to construct a PointIndexGrid. +/// @details The PointIndexGrid supports fast spatial queries for points. +template +inline openvdb::tools::PointIndexGrid::Ptr +GUvdbCreatePointIndexGrid(const openvdb::math::Transform& xform, const PointArrayType& points) +{ + return openvdb::tools::createPointIndexGrid(points, xform); +} + + +/// @brief Utility method to construct a ParticleAtals. +/// @details The ParticleAtals supports fast spatial queries for particles. +template +inline openvdb::tools::ParticleIndexAtlas::Ptr +GUvdbCreateParticleAtlas(const double minVoxelSize, const ParticleArrayType& particles) +{ + typedef openvdb::tools::ParticleIndexAtlas ParticleIndexAtlas; + ParticleIndexAtlas::Ptr atlas(new ParticleIndexAtlas()); + + if (particles.hasRadius()) { + atlas->construct(particles, minVoxelSize); + } + + return atlas; +} + + +/// @brief Utility method to construct a boolean PointMaskGrid +/// @details This method supports packed points. +inline openvdb::MaskGrid::Ptr +GUvdbCreatePointMaskGrid( + const openvdb::math::Transform& xform, + const GU_Detail& detail, + const GA_PointGroup* pointGroup = NULL) +{ +#if (UT_MAJOR_VERSION_INT >= 14) + + std::vector packed; + GU_VDBPointToolsInternal::getPackedPrimitiveOffsets(detail, packed); + + if (!packed.empty()) { + GU_VDBPointToolsInternal::PackedMaskConstructor op(packed, xform); + tbb::parallel_reduce(tbb::blocked_range(0, packed.size()), op); + return op.getMaskGrid(); + } + +#endif + + GU_VDBPointList points( detail, pointGroup ); + return openvdb::tools::createPointMaskGrid( points, xform ); +} + + +/// @brief Utility method to construct a PointIndexGrid that stores +/// Houdini geometry offsets. +/// +/// @note PointIndexGrid's that store Houdini geometry offsets are not +/// safe to write to disk, offsets are not guaranteed to be immutable +/// under defragmentation operations or I/O. +inline openvdb::tools::PointIndexGrid::Ptr +GUvdbCreatePointOffsetGrid( + const openvdb::math::Transform& xform, + const GU_Detail& detail, + const GA_PointGroup* pointGroup = NULL) +{ + GU_VDBPointList points(detail, pointGroup); + + openvdb::tools::PointIndexGrid::Ptr grid = + openvdb::tools::createPointIndexGrid(points, xform); + + GUvdbConvertIndexToOffset(grid->tree(), points); + + return grid; +} + + +#endif // __GU_VDBPOINTTOOLS_H_HAS_BEEN_INCLUDED__ + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb_houdini/GeometryUtil.h b/nuparu/include/openvdb/include/openvdb_houdini/GeometryUtil.h new file mode 100644 index 00000000..b4e26b9f --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb_houdini/GeometryUtil.h @@ -0,0 +1,360 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 GeometryUtil.h +/// @author FX R&D Simulation team +/// @brief Utility methods and tools for geometry processing + +#ifndef OPENVDB_HOUDINI_GEOMETRY_UTIL_HAS_BEEN_INCLUDED +#define OPENVDB_HOUDINI_GEOMETRY_UTIL_HAS_BEEN_INCLUDED + +#include +#include // for openvdb::tools::MeshToVoxelEdgeData +#include +#include // for openvdb::util::COORD_OFFSETS + +#include +#include +#include + + +class GA_SplittableRange; +class OBJ_Camera; +class OP_Context; +class OP_Node; + + +#ifdef SESI_OPENVDB + #ifdef OPENVDB_HOUDINI_API + #undef OPENVDB_HOUDINI_API + #define OPENVDB_HOUDINI_API + #endif +#endif + + +namespace openvdb_houdini { + +class Interrupter; + + +/// Add geometry to the given detail to indicate the extents of a frustum transform. +OPENVDB_HOUDINI_API +void +drawFrustum(GU_Detail&, const openvdb::math::Transform&, + const UT_Vector3* boxColor, const UT_Vector3* tickColor, + bool shaded, bool drawTicks = true); + + +/// Construct a frustum transform from a Houdini camera. +OPENVDB_HOUDINI_API +openvdb::math::Transform::Ptr +frustumTransformFromCamera( + OP_Node&, OP_Context&, OBJ_Camera&, + float offset, float nearPlaneDist, float farPlaneDist, + float voxelDepthSize = 1.0, int voxelCountX = 100); + + +//////////////////////////////////////// + + +/// @brief Return @c true if the point at the given offset is referenced +/// by primitives from a certain primitive group. +OPENVDB_HOUDINI_API +bool +pointInPrimGroup(GA_Offset ptnOffset, GU_Detail&, const GA_PrimitiveGroup&); + + +//////////////////////////////////////// + + +/// @brief Convert geometry to quads and triangles. +/// +/// @return a pointer to a new GU_Detail object if the geometry was +/// converted or subdivided, otherwise a null pointer +OPENVDB_HOUDINI_API +boost::shared_ptr +validateGeometry(const GU_Detail& geometry, std::string& warning, Interrupter*); + + +//////////////////////////////////////// + + +/// TBB body object for threaded world to voxel space transformation and copy of points +class OPENVDB_HOUDINI_API TransformOp +{ +public: + TransformOp(GU_Detail const * const gdp, + const openvdb::math::Transform& transform, + std::vector& pointList); + + void operator()(const GA_SplittableRange&) const; + +private: + GU_Detail const * const mGdp; + const openvdb::math::Transform& mTransform; + std::vector* const mPointList; +}; + + +//////////////////////////////////////// + + +/// @brief TBB body object for threaded primitive copy +/// @details Produces a primitive-vertex index list. +class OPENVDB_HOUDINI_API PrimCpyOp +{ +public: + PrimCpyOp(GU_Detail const * const gdp, std::vector& primList); + void operator()(const GA_SplittableRange&) const; + +private: + GU_Detail const * const mGdp; + std::vector* const mPrimList; +}; + + +//////////////////////////////////////// + + +/// @brief TBB body object for threaded vertex normal generation +/// @details Averages face normals from all similarly oriented primitives, +/// that share the same vertex-point, to maintain sharp features. +class OPENVDB_HOUDINI_API VertexNormalOp +{ +public: + VertexNormalOp(GU_Detail&, const GA_PrimitiveGroup* interiorPrims = NULL, float angle = 0.7f); + void operator()(const GA_SplittableRange&) const; + +private: + bool isInteriorPrim(GA_Offset primOffset) const + { + return mInteriorPrims && mInteriorPrims->containsIndex( + mDetail.primitiveIndex(primOffset)); + } + + const GU_Detail& mDetail; + const GA_PrimitiveGroup* mInteriorPrims; + GA_RWHandleV3 mNormalHandle; + const float mAngle; +}; + + +//////////////////////////////////////// + + +/// TBB body object for threaded sharp feature construction +class OPENVDB_HOUDINI_API SharpenFeaturesOp +{ +public: + typedef openvdb::tools::MeshToVoxelEdgeData EdgeData; + + SharpenFeaturesOp(GU_Detail& meshGeo, const GU_Detail& refGeo, EdgeData& edgeData, + const openvdb::math::Transform& xform, const GA_PrimitiveGroup* surfacePrims = NULL, + const openvdb::BoolTree* mask = NULL); + + void operator()(const GA_SplittableRange&) const; + +private: + GU_Detail& mMeshGeo; + const GU_Detail& mRefGeo; + EdgeData& mEdgeData; + const openvdb::math::Transform& mXForm; + const GA_PrimitiveGroup* mSurfacePrims; + const openvdb::BoolTree* mMaskTree; +}; + + +//////////////////////////////////////// + + +/// TBB body object for threaded sharp feature construction +template +class GenAdaptivityMaskOp +{ +public: + typedef openvdb::tree::LeafManager BoolLeafManager; + + GenAdaptivityMaskOp(const GU_Detail& refGeo, + const IndexTreeType& indexTree, BoolLeafManager&, float edgetolerance = 0.0); + + void run(bool threaded = true); + + void operator()(const tbb::blocked_range&) const; + +private: + const GU_Detail& mRefGeo; + const IndexTreeType& mIndexTree; + BoolLeafManager& mLeafs; + float mEdgeTolerance; +}; + + +template +GenAdaptivityMaskOp::GenAdaptivityMaskOp(const GU_Detail& refGeo, + const IndexTreeType& indexTree, BoolLeafManager& leafMgr, float edgetolerance) + : mRefGeo(refGeo) + , mIndexTree(indexTree) + , mLeafs(leafMgr) + , mEdgeTolerance(edgetolerance) +{ + mEdgeTolerance = std::max(0.0f, mEdgeTolerance); + mEdgeTolerance = std::min(1.0f, mEdgeTolerance); +} + + +template +void +GenAdaptivityMaskOp::run(bool threaded) +{ + if (threaded) { + tbb::parallel_for(mLeafs.getRange(), *this); + } else { + (*this)(mLeafs.getRange()); + } +} + + +template +void +GenAdaptivityMaskOp::operator()( + const tbb::blocked_range& range) const +{ + typedef typename openvdb::tree::ValueAccessor IndexAccessorType; + IndexAccessorType idxAcc(mIndexTree); + + UT_Vector3 tmpN, normal; + GA_Offset primOffset; + int tmpIdx; + + openvdb::Coord ijk, nijk; + typename BoolTreeType::LeafNodeType::ValueOnIter iter; + + for (size_t n = range.begin(); n < range.end(); ++n) { + iter = mLeafs.leaf(n).beginValueOn(); + for (; iter; ++iter) { + ijk = iter.getCoord(); + + bool edgeVoxel = false; + + int idx = idxAcc.getValue(ijk); + + primOffset = mRefGeo.primitiveOffset(idx); + normal = mRefGeo.getGEOPrimitive(primOffset)->computeNormal(); + + for (size_t i = 0; i < 18; ++i) { + nijk = ijk + openvdb::util::COORD_OFFSETS[i]; + if (idxAcc.probeValue(nijk, tmpIdx) && tmpIdx != idx) { + primOffset = mRefGeo.primitiveOffset(tmpIdx); + tmpN = mRefGeo.getGEOPrimitive(primOffset)->computeNormal(); + + if (normal.dot(tmpN) < mEdgeTolerance) { + edgeVoxel = true; + break; + } + } + } + + if (!edgeVoxel) iter.setValueOff(); + } + } +} + + +} // namespace openvdb_houdini + + +//////////////////////////////////////// + + +#if (UT_VERSION_INT < 0x0c0500F5) // Prior to 12.5.245 + +// Symbols in namespace GU_Convert_H12_5 were added to GU_ConvertParms.h in 12.5.245 + +namespace GU_Convert_H12_5 { + +/// Simple helper class for tracking a range of new primitives and points +class GU_ConvertMarker +{ +public: + GU_ConvertMarker(const GA_Detail &geo) + : myGeo(geo) + , myPrimBegin(primOff()) + , myPtBegin(ptOff()) + { + } + + GA_Range getPrimitives() const + { + return GA_Range(myGeo.getPrimitiveMap(), myPrimBegin, primOff()); + } + GA_Range getPoints() const + { + return GA_Range(myGeo.getPointMap(), myPtBegin, ptOff()); + } + + GA_Offset primitiveBegin() const { return myPrimBegin; } + GA_Offset pointBegin() const { return myPtBegin; } + + GA_Size numPrimitives() const { return primOff() - myPrimBegin; } + GA_Size numPoints() const { return ptOff() - myPtBegin; } + +private: + GA_Offset primOff() const { return myGeo.getPrimitiveMap().lastOffset() + 1; } + GA_Offset ptOff() const { return myGeo.getPointMap().lastOffset() + 1; } + +private: + const GA_Detail& myGeo; + GA_Offset myPrimBegin; + GA_Offset myPtBegin; +}; + + +OPENVDB_HOUDINI_API +void +GUconvertCopySingleVertexPrimAttribsAndGroups( + GU_ConvertParms &parms, + const GA_Detail &src, + GA_Offset src_primoff, + GA_Detail &dst, + const GA_Range &dst_prims, + const GA_Range &dst_points); + +} // namespace GU_Convert_H12_5 + +using GU_Convert_H12_5::GU_ConvertMarker; +using GU_Convert_H12_5::GUconvertCopySingleVertexPrimAttribsAndGroups; + +#endif // Prior to 12.5.245 + +#endif // OPENVDB_HOUDINI_GEOMETRY_UTIL_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb_houdini/SOP_NodeVDB.h b/nuparu/include/openvdb/include/openvdb_houdini/SOP_NodeVDB.h new file mode 100644 index 00000000..4d6015a6 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb_houdini/SOP_NodeVDB.h @@ -0,0 +1,230 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 SOP_NodeVDB.h +/// @author FX R&D OpenVDB team +/// @brief Base class for OpenVDB plugins + +#ifndef OPENVDB_HOUDINI_SOP_NODEVDB_HAS_BEEN_INCLUDED +#define OPENVDB_HOUDINI_SOP_NODEVDB_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#ifndef SESI_OPENVDB +#include +#endif +#include + + +class GU_Detail; + +namespace openvdb_houdini { + +/// @brief Use this class to register a new OpenVDB operator (SOP, POP, etc.) +/// @details This class ensures that the operator uses the appropriate OpPolicy. +/// @sa houdini_utils::OpFactory, houdini_utils::OpPolicy +class OPENVDB_HOUDINI_API OpenVDBOpFactory: public houdini_utils::OpFactory +{ +public: + /// Construct an OpFactory that on destruction registers a new OpenVDB operator type. + OpenVDBOpFactory(const std::string& english, OP_Constructor, houdini_utils::ParmList&, + OP_OperatorTable&, houdini_utils::OpFactory::OpFlavor = SOP); +}; + + +class OPENVDB_HOUDINI_API DWAOpenVDBOpFactory: public OpenVDBOpFactory +{ +public: + /// @brief Construct an OpFactory that on destruction registers a new + /// OpenVDB operator type using the DreamWorks naming convention. + /// @deprecated Use OpenVDBOpFactory instead. + OPENVDB_DEPRECATED + DWAOpenVDBOpFactory(const std::string& english, OP_Constructor ctor, + houdini_utils::ParmList& plist, OP_OperatorTable& optable, + houdini_utils::OpFactory::OpFlavor flavor = SOP) + : OpenVDBOpFactory(english, ctor, plist, optable, flavor) + { + } +}; + + +//////////////////////////////////////// + + +/// @brief Base class from which to derive OpenVDB-related Houdini SOPs +class OPENVDB_HOUDINI_API SOP_NodeVDB: public SOP_Node +{ +public: + SOP_NodeVDB(OP_Network*, const char*, OP_Operator*); + virtual ~SOP_NodeVDB() {} + + virtual void fillInfoTreeNodeSpecific(UT_InfoTree&, fpreal time); + virtual void getNodeSpecificInfoText(OP_Context&, OP_NodeInfoParms&); + +protected: + virtual OP_ERROR cookMyGuide1(OP_Context&); + //virtual OP_ERROR cookMyGuide2(OP_Context&); + + /// @brief Retrieve a group from a geometry detail by parsing a pattern + /// (typically, the value of a Group parameter belonging to this node). + /// @throw std::runtime_error if the pattern is nonempty but doesn't match any group. + /// @todo This is a wrapper for SOP_Node::parsePrimitiveGroups(), so it needs access + /// to a SOP_Node instance. But it probably doesn't need to be a SOP_NodeVDB method. + const GA_PrimitiveGroup* matchGroup(GU_Detail&, const std::string& pattern); + + //@{ + /// @brief Evaluate a vector-valued parameter. + openvdb::Vec3f evalVec3f(const char* name, fpreal time) const; + openvdb::Vec3R evalVec3R(const char* name, fpreal time) const; + openvdb::Vec3i evalVec3i(const char* name, fpreal time) const; + openvdb::Vec2R evalVec2R(const char* name, fpreal time) const; + openvdb::Vec2i evalVec2i(const char* name, fpreal time) const; + //@} + + /// @brief Transfer the value of an obsolete parameter that was renamed + /// to the parameter with the new name. + /// @details This convenience method is intended to be called from + /// @c resolveObsoleteParms(), when that function is implemented. + void resolveRenamedParm(PRM_ParmList& obsoleteParms, + const char* oldName, const char* newName); + + + /// @brief Steal the geometry on the specified input if possible, instead of copying the data. + /// + /// @details In certain cases where a node's input geometry isn't being shared with + /// other nodes, it is safe for the node to directly modify the geometry. + /// Normally, input geometry is shared with the upstream node's output cache, + /// so for stealing to be possible, the "unload" flag must be set on the upstream node + /// to inhibit caching. In addition, reference counting of GEO_PrimVDB shared pointers + /// ensures we cannot steal data that is in use elsewhere. When stealing is not possible, + /// this method falls back to copying the shared pointer, effectively performing + /// a duplicateSource(). + /// + /// @param index the index of the input from which to perform this operation + /// @param context the current SOP context is used for cook time for network traversal + /// @param pgdp pointer to the SOP's gdp + /// @param gdh handle to manage input locking + /// @param clean (forwarded to duplicateSource()) + /// + /// @note Prior to Houdini 13.0, this method peforms a duplicateSource() and unlocks the + /// inputs to the SOP. From Houdini 13.0 on, this method will insert the existing data + /// into the detail and update the detail handle in the SOP. + /// + /// @warning No attempt to call duplicateSource() or inputGeo() should be made after + /// calling this method, as there will be no data on the input stream if isSourceStealable() + /// returns @c true. + OP_ERROR duplicateSourceStealable(const unsigned index, + OP_Context& context, GU_Detail **pgdp, GU_DetailHandle& gdh, bool clean = true); + + + /// @brief Steal the geometry on the specified input if possible, instead of copying the data. + /// + /// @details In certain cases where a node's input geometry isn't being shared with + /// other nodes, it is safe for the node to directly modify the geometry. + /// Normally, input geometry is shared with the upstream node's output cache, + /// so for stealing to be possible, the "unload" flag must be set on the upstream node + /// to inhibit caching. In addition, reference counting of GEO_PrimVDB shared pointers + /// ensures we cannot steal data that is in use elsewhere. When stealing is not possible, + /// this method falls back to copying the shared pointer, effectively performing + /// a duplicateSource(). + /// + /// @note Prior to Houdini 13.0, this method peforms a duplicateSource() and unlocks the + /// inputs to the SOP. From Houdini 13.0 on, this method will insert the existing data + /// into the detail and update the detail handle in the SOP. + /// + /// @param index the index of the input from which to perform this operation + /// @param context the current SOP context is used for cook time for network traversal + OP_ERROR duplicateSourceStealable(const unsigned index, OP_Context& context); + +private: + + /// @brief Traverse the upstream network to determine if the source input can be stolen. + /// + /// An upstream SOP cannot be stolen if it is implicitly caching the data (no "unload" flag) + /// or explictly caching the data (using a Cache SOP) + /// + /// The traversal ignores pass through nodes such as null SOPs and bypassing. + /// + /// @param index the index of the input from which to perform this operation + /// @param context the current SOP context is used for cook time for network traversal + bool isSourceStealable(const unsigned index, OP_Context& context) const; +}; + + +//////////////////////////////////////// + + +/// @brief Namespace to hold functionality for registering info text callbacks. Whenever +/// getNodeSpecificInfoText() is called, the default info text is added to MMB output unless +/// a valid callback has been registered for the grid type. +/// +/// @details Use node_info_text::registerGridSpecificInfoText<> to register a grid type to +/// a function pointer which matches the ApplyGridSpecificInfoText signature. +/// +/// void floatGridText(std::ostream&, const openvdb::GridBase&); +/// +/// node_info_text::registerGridSpecificInfoText(&floatGridText); +/// +namespace node_info_text +{ + // The function pointer signature expected when registering an grid type text + // callback. The grid is passed untyped but is guaranteed to match the registered + // type. + typedef void (*ApplyGridSpecificInfoText)(std::ostream&, const openvdb::GridBase&); + + /// @brief Register an info text callback to a specific grid type. + /// @note Does not add the callback if the grid type already has a registered callback. + /// @param gridType the grid type as a unique string (see templated + /// registerGridSpecificInfoText<>) + /// @param callback a pointer to the callback function to execute + void registerGridSpecificInfoText(const std::string& gridType, + ApplyGridSpecificInfoText callback); + + /// @brief Register an info text callback to a templated grid type. + /// @note Does not add the callback if the grid type already has a registered callback. + /// @param callback a pointer to the callback function to execute + template + inline void registerGridSpecificInfoText(ApplyGridSpecificInfoText callback) + { + registerGridSpecificInfoText(GridType::gridType(), callback); + } + +} // namespace node_info_text + + +} // namespace openvdb_houdini + +#endif // OPENVDB_HOUDINI_SOP_NODEVDB_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb_houdini/SOP_NodeVDBPoints.h b/nuparu/include/openvdb/include/openvdb_houdini/SOP_NodeVDBPoints.h new file mode 100644 index 00000000..ebbfc066 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb_houdini/SOP_NodeVDBPoints.h @@ -0,0 +1,272 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015-2016 Double Negative Visual Effects +// +// 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 Double Negative Visual Effects 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 SOP_NodeVDB.h +/// +/// @author Dan Bailey +/// +/// @brief Base class for OpenVDB Points plugins + + +#ifndef OPENVDB_HOUDINI_SOP_NODEVDB_POINTS_HAS_BEEN_INCLUDED +#define OPENVDB_HOUDINI_SOP_NODEVDB_POINTS_HAS_BEEN_INCLUDED + +#include +#include +#include +#include + +#include +#include + +#include + +#include // for starts_with + +#include +#include +#include // for dynamic cast + +#ifndef SESI_OPENVDB +#include +#endif + + +class GU_Detail; + +namespace { + +template +std::string addDigitSeparators(T value) +{ + std::stringstream ss; + ss.imbue(std::locale("")); + ss << std::fixed << value; + return ss.str(); +} + +} // namespace + +namespace openvdb_houdini { + +/// @brief Base class from which to derive OpenVDB Points-related Houdini SOPs +class OPENVDB_HOUDINI_API SOP_NodeVDBPoints: public SOP_NodeVDB +{ +public: + SOP_NodeVDBPoints(OP_Network* network, const char* name, OP_Operator* op) + : SOP_NodeVDB(network, name, op) { } + virtual ~SOP_NodeVDBPoints() { } + + void + getNodeSpecificInfoText(OP_Context &context, OP_NodeInfoParms &parms) + { + SOP_Node::getNodeSpecificInfoText(context, parms); + + #ifdef SESI_OPENVDB + // Nothing needed since we will report it as part of native prim info + #else + // Get a handle to the geometry. + GU_DetailHandle gd_handle = getCookedGeoHandle(context); + + // Check if we have a valid detail handle. + if (gd_handle.isNull()) return; + + // Lock it for reading. + GU_DetailHandleAutoReadLock gd_lock(gd_handle); + // Finally, get at the actual GU_Detail. + const GU_Detail* tmp_gdp = gd_lock.getGdp(); + + std::ostringstream infoStr; + + unsigned gridn = 0; + for (VdbPrimCIterator it(tmp_gdp); it; ++it) { + + const openvdb::GridBase& grid = it->getGrid(); + + // ignore openvdb point grids + if (dynamic_cast(&grid)) continue; + + openvdb::Coord dim = grid.evalActiveVoxelDim(); + const UT_String gridName = it.getPrimitiveName(); + + infoStr << " "; + infoStr << "(" << it.getIndex() << ")"; + if (gridName.isstring()) infoStr << " name: '" << gridName << "',"; + infoStr << " voxel size: " << grid.transform().voxelSize()[0] << ","; + infoStr << " type: "<< grid.valueType() << ","; + + if (grid.activeVoxelCount() != 0) { + infoStr << " dim: " << dim[0] << "x" << dim[1] << "x" << dim[2]; + } else { + infoStr <<" "; + } + + infoStr<<"\n"; + + ++gridn; + } + + if (gridn > 0) { + std::ostringstream headStr; + headStr << gridn << " VDB grid" << (gridn == 1 ? "" : "s") << "\n"; + + parms.append(headStr.str().c_str()); + parms.append(infoStr.str().c_str()); + } + #endif + + + // VDB points info (to be included in native VDB primitive) + + // Get a handle to the geometry. + GU_DetailHandle gd_handle_pts = getCookedGeoHandle(context); + + // Check if we have a valid detail handle. + if (gd_handle_pts.isNull()) return; + + // Lock it for reading. + GU_DetailHandleAutoReadLock gd_lock_pts(gd_handle_pts); + // Finally, get at the actual GU_Detail. + const GU_Detail* tmp_gdp_pts = gd_lock_pts.getGdp(); + + std::ostringstream infoStr_pts; + + unsigned gridn_pts = 0; + for (VdbPrimCIterator it(tmp_gdp_pts); it; ++it) { + + const openvdb::GridBase& grid = it->getGrid(); + const UT_String gridName = it.getPrimitiveName(); + + infoStr_pts << " "; + infoStr_pts << "(" << it.getIndex() << ")"; + if (gridName.isstring()) infoStr_pts << " name: '" << gridName << "',"; + + typedef openvdb::tools::PointDataGrid PointDataGrid; + typedef openvdb::tools::PointDataTree PointDataTree; + typedef openvdb::tools::AttributeSet AttributeSet; + + const PointDataGrid* pointDataGrid = dynamic_cast(&grid); + + if (!pointDataGrid) continue; + + const PointDataTree& pointDataTree = pointDataGrid->tree(); + + PointDataTree::LeafCIter iter = pointDataTree.cbeginLeaf(); + + const openvdb::Index64 count = pointCount(pointDataTree); + + infoStr_pts << " count: " << addDigitSeparators(count) << ","; + + if (!iter.getLeaf()) { + infoStr_pts << " attributes: "; + infoStr_pts << ""; + } + else { + const AttributeSet::DescriptorPtr& descriptor = iter->attributeSet().descriptorPtr(); + + infoStr_pts << " groups: "; + + const AttributeSet::Descriptor::NameToPosMap& groupMap = descriptor->groupMap(); + + bool first = true; + for (AttributeSet::Descriptor::ConstIterator it = groupMap.begin(), it_end = groupMap.end(); + it != it_end; ++it) { + if (first) { + first = false; + } + else { + infoStr_pts << ", "; + } + + infoStr_pts << it->first << "("; + + infoStr_pts << addDigitSeparators(groupPointCount(pointDataTree, it->first)); + + infoStr_pts << ")"; + } + + if (first) infoStr_pts << ""; + + infoStr_pts << ","; + + infoStr_pts << " attributes: "; + + const AttributeSet::Descriptor::NameToPosMap& nameToPosMap = descriptor->map(); + + first = true; + for (AttributeSet::Descriptor::ConstIterator it = nameToPosMap.begin(), it_end = nameToPosMap.end(); + it != it_end; ++it) { + const openvdb::tools::AttributeArray& array = iter->attributeArray(it->second); + if (openvdb::tools::GroupAttributeArray::isGroup(array)) continue; + + if (first) { + first = false; + } + else { + infoStr_pts << ", "; + } + const openvdb::NamePair& type = descriptor->type(it->second); + + // if no value compression, hide the codec from the middle-click output + + if (boost::starts_with(type.second, "null_") && + boost::ends_with(type.second, type.first)) { + infoStr_pts << it->first << "[" << type.first << "]"; + } + else { + infoStr_pts << it->first << "[" << type.first << "_" << type.second << "]"; + } + } + + if (first) infoStr_pts << ""; + } + + infoStr_pts << "\n"; + + ++gridn_pts; + } + + if (gridn_pts > 0) { + std::ostringstream headStr; + headStr << gridn_pts << " VDB points" << "\n"; + + parms.append(headStr.str().c_str()); + parms.append(infoStr_pts.str().c_str()); + } + } +}; + +} // namespace openvdb_houdini + +#endif // OPENVDB_HOUDINI_SOP_NODEVDB_POINTS_HAS_BEEN_INCLUDED + +// Copyright (c) 2015-2016 Double Negative Visual Effects +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) diff --git a/nuparu/include/openvdb/include/openvdb_houdini/UT_VDBTools.h b/nuparu/include/openvdb/include/openvdb_houdini/UT_VDBTools.h new file mode 100644 index 00000000..a4ffe67d --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb_houdini/UT_VDBTools.h @@ -0,0 +1,203 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 UT_VDBTools.h +/// @author FX R&D Simulation team +/// @brief Less commonly-used utility classes and functions for OpenVDB plugins + +#ifndef OPENVDB_HOUDINI_UT_VDBTOOLS_HAS_BEEN_INCLUDED +#define OPENVDB_HOUDINI_UT_VDBTOOLS_HAS_BEEN_INCLUDED + +#include +#include +#include "Utils.h" // for GridPtr + +namespace openvdb_houdini { + +/// @brief GridTransformOp is a functor class for use with UTvdbProcessTypedGrid() +/// that samples an input grid into an output grid of the same type through +/// a given affine transform. +/// @details The output grid's transform is unchanged by this operation. +/// @sa GridResampleOp, GridResampleToMatchOp +/// @par Example: +/// @code +/// const Grid& inGrid = ...; // generic reference to a grid of any type +/// +/// // Create a new, empty output grid of the same (so far, unknown) type +/// // as the input grid and with the same transform and metadata. +/// GridPtr outGrid = inGrid.copyGrid(openvdb::CP_NEW); +/// +/// // Initialize a GridTransformer with the parameters of an affine transform. +/// openvdb::tools::GridTransformer xform(pivot, scale, rotate, ...); +/// +/// // Resolve the input grid's type and resample it into the output grid, +/// // using a second-order sampling kernel. +/// GridTransformOp op(outGrid, xform); +/// UTvdbProcessTypedGrid(UTvdbGetGridType(inGrid), inGrid, op); +/// @endcode +template +class GridTransformOp +{ +public: + /// @param outGrid a generic pointer to an output grid of the same type + /// as the grid to be resampled + /// @param t a @c GridTransformer that defines an affine transform + /// @note GridTransformOp makes an internal copy of the @c GridTransformer + /// and supplies the copy with a default Interrupter that replaces any + /// existing interrupter. + GridTransformOp(GridPtr& outGrid, const openvdb::tools::GridTransformer& t): + mOutGrid(outGrid), mTransformer(t) {} + + template + void operator()(const GridType& inGrid) + { + typename GridType::Ptr outGrid = openvdb::gridPtrCast(mOutGrid); + + Interrupter interrupter; + mTransformer.setInterrupter(interrupter); + + mTransformer.transformGrid(inGrid, *outGrid); + } + +private: + GridPtr mOutGrid; + openvdb::tools::GridTransformer mTransformer; +}; + + +//////////////////////////////////////// + + +/// @brief GridResampleOp is a functor class for use with processTypedGrid() +/// that samples an input grid into an output grid of the same type through +/// a given transform. +/// @details The output grid's transform is unchanged by this operation. +/// @sa GridTransformOp, GridResampleToMatchOp +/// @par Example: +/// @code +/// namespace { +/// // Class that implements GridResampler's Transformer interface +/// struct MyXform +/// { +/// bool isAffine() const { ... } +/// openvdb::Vec3d transform(const openvdb::Vec3d&) const { ... } +/// openvdb::Vec3d invTransform(const openvdb::Vec3d&) const { ... } +/// }; +/// } +/// +/// const Grid& inGrid = ...; // generic reference to a grid of any type +/// +/// // Create a new, empty output grid of the same (so far, unknown) type +/// // as the input grid and with the same transform and metadata. +/// GridPtr outGrid = inGrid.copyGrid(openvdb::CP_NEW); +/// +/// // Resolve the input grid's type and resample it into the output grid, +/// // using a trilinear sampling kernel. +/// GridResampleOp op(outGrid, MyXform()); +/// processTypedGrid(inGrid, op); +/// @endcode +template +class GridResampleOp +{ +public: + /// @param outGrid a generic pointer to an output grid of the same type + /// as the grid to be resampled + /// @param t an object that implements GridResampler's + /// Transformer interface + /// @note GridResampleOp makes an internal copy of @a t. + GridResampleOp(GridPtr& outGrid, const TransformerType& t): + mOutGrid(outGrid), mTransformer(t) {} + + template + void operator()(const GridType& inGrid) + { + typename GridType::Ptr outGrid = openvdb::gridPtrCast(mOutGrid); + + openvdb::tools::GridResampler resampler; + + Interrupter interrupter; + resampler.setInterrupter(interrupter); + + resampler.transformGrid(mTransformer, inGrid, *outGrid); + } + +private: + GridPtr mOutGrid; + const TransformerType mTransformer; +}; + + +//////////////////////////////////////// + + +/// @brief GridResampleToMatchOp is a functor class for use with +/// UTvdbProcessTypedGrid() that samples an input grid into an output grid +/// of the same type such that, after resampling, the input and output grids +/// coincide, but the output grid's transform is unchanged. +/// @sa GridTransformOp, GridResampleOp +/// @par Example: +/// @code +/// const Grid& inGrid = ...; // generic reference to a grid of any type +/// +/// // Create a new, empty output grid of the same (so far, unknown) type as +/// // the input grid and with the same metadata, but with a different transform. +/// GridPtr outGrid = inGrid.copyGrid(openvdb::CP_NEW); +/// outGrid->setTransform(myTransform); +/// +/// // Resolve the input grid's type and resample it into the output grid, +/// // using a second-order sampling kernel. +/// GridResampleToMatchOp op(outGrid); +/// UTvdbProcessTypedGrid(UTvdbGetGridType(inGrid), inGrid, op); +/// @endcode +template +class GridResampleToMatchOp +{ +public: + GridResampleToMatchOp(GridPtr outGrid): mOutGrid(outGrid) {} + + template + void operator()(const GridType& inGrid) + { + typename GridType::Ptr outGrid = openvdb::gridPtrCast(mOutGrid); + Interrupter interrupter; + openvdb::tools::resampleToMatch(inGrid, *outGrid, interrupter); + } + +private: + GridPtr mOutGrid; +}; + +} // namespace openvdb_houdini + +#endif // OPENVDB_HOUDINI_UT_VDBTOOLS_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb_houdini/UT_VDBUtils.h b/nuparu/include/openvdb/include/openvdb_houdini/UT_VDBUtils.h new file mode 100644 index 00000000..0b1f9cc2 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb_houdini/UT_VDBUtils.h @@ -0,0 +1,706 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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. +// +/////////////////////////////////////////////////////////////////////////// + +/* + * PROPRIETARY INFORMATION. This software is proprietary to + * Side Effects Software Inc., and is not to be reproduced, + * transmitted, or disclosed in any way without written permission. + * + * Produced by: + * Adrian Saldanha + * Side Effects Software Inc. + * 123 Front Street West, Suite 1401 + * Toronto, Ontario + * Canada M5J 2M2 + * 416-366-4607 + */ + +#include +#if !defined(SESI_OPENVDB) && (UT_VERSION_INT >= 0x0c050157) // 12.5.343 or later + +#include + +#else // earlier than 12.5.343 + +#ifndef __HDK_UT_VDBUtils__ +#define __HDK_UT_VDBUtils__ + +enum UT_VDBType +{ + UT_VDB_INVALID, + UT_VDB_FLOAT, + UT_VDB_DOUBLE, + UT_VDB_INT32, + UT_VDB_INT64, + UT_VDB_BOOL, + UT_VDB_VEC3F, + UT_VDB_VEC3D, + UT_VDB_VEC3I, +}; + +#include + +#include +#include +#include +#include +#include +#include + +#include + + +#if (UT_VERSION_INT < 0x0c010072) // 12.1.114 or earlier +#define UTverify_cast static_cast +#endif + + +/// Calls openvdb::initialize() +inline void UTvdbInitialize() { openvdb::initialize(); } + +/// Find the UT_VDBType from a grid +inline UT_VDBType +UTvdbGetGridType(const openvdb::GridBase &grid) +{ + if (grid.isType()) + return UT_VDB_FLOAT; + if (grid.isType()) + return UT_VDB_DOUBLE; + if (grid.isType()) + return UT_VDB_INT32; + if (grid.isType()) + return UT_VDB_INT64; + if (grid.isType()) + return UT_VDB_BOOL; + if (grid.isType()) + return UT_VDB_VEC3F; + if (grid.isType()) + return UT_VDB_VEC3D; + if (grid.isType()) + return UT_VDB_VEC3I; + + return UT_VDB_INVALID; +} + +/// Return the string representation of a grid's underlying value type +inline const char * +UTvdbGetGridTypeString(const openvdb::GridBase &grid) +{ + switch(UTvdbGetGridType(grid)) + { + case UT_VDB_FLOAT: + return "float"; + case UT_VDB_DOUBLE: + return "double"; + case UT_VDB_INT32: + return "int32"; + case UT_VDB_INT64: + return "int64"; + case UT_VDB_BOOL: + return "bool"; + case UT_VDB_VEC3F: + return "Vec3f"; + case UT_VDB_VEC3D: + return "Vec3d"; + case UT_VDB_VEC3I: + return "Vec3i"; + default: + return "invalid type"; + } +} + +/// Returns the tuple size of a grid given its value type. +inline int +UTvdbGetGridTupleSize(UT_VDBType type) +{ + switch(type) + { + case UT_VDB_FLOAT: + case UT_VDB_DOUBLE: + case UT_VDB_INT32: + case UT_VDB_INT64: + case UT_VDB_BOOL: + return 1; + + case UT_VDB_VEC3F: + case UT_VDB_VEC3D: + case UT_VDB_VEC3I: + return 3; + + case UT_VDB_INVALID: + default: + break; + } + + return 0; +} + +/// Returns the tuple size of a grid +inline int +UTvdbGetGridTupleSize(const openvdb::GridBase &grid) +{ + return UTvdbGetGridTupleSize(UTvdbGetGridType(grid)); +} + +/// Special plusEqual class to avoid bool warnings +/// @{ +template +struct UT_VDBMath +{ + static void plusEqual(T &lhs, const T &rhs) + { lhs += rhs; } +}; +template <> +struct UT_VDBMath +{ + static void plusEqual(bool &lhs, const bool &rhs) + { lhs = lhs | rhs; } +}; +/// @} + +/// Helpers for downcasting to a specific grid type +/// @{ +template +inline const GridType * +UTvdbGridCast(const openvdb::GridBase *grid) + { return UTverify_cast(grid); } + +template +inline GridType * +UTvdbGridCast(openvdb::GridBase *grid) + { return UTverify_cast(grid); } + +template +inline const GridType & +UTvdbGridCast(const openvdb::GridBase &grid) + { return *UTverify_cast(&grid); } + +template +inline GridType & +UTvdbGridCast(openvdb::GridBase &grid) + { return *UTverify_cast(&grid); } + +template +inline typename GridType::ConstPtr +UTvdbGridCast(openvdb::GridBase::ConstPtr grid) + { return openvdb::gridConstPtrCast(grid); } + +template +inline typename GridType::Ptr +UTvdbGridCast(openvdb::GridBase::Ptr grid) + { return openvdb::gridPtrCast(grid); } +/// @} + +//////////////////////////////////////// + +namespace UT_VDBUtils { + +// Helper function used internally by UTvdbProcessTypedGrid() +// to instantiate a templated functor for a specific grid type +// and then to call the functor with a grid of that type +template +inline void +callTypedGrid(GridBaseType &grid, OpType& op) +{ +#ifdef _MSC_VER + op.operator()(UTvdbGridCast(grid)); +#else + op.template operator()(UTvdbGridCast(grid)); +#endif +} + +} // namespace UT_VDBUtils + +//////////////////////////////////////// + + +/// @brief Utility function that, given a generic grid pointer, +/// calls a functor on the fully-resolved grid +/// +/// @par Example: +/// @code +/// using openvdb::Coord; +/// using openvdb::CoordBBox; +/// +/// struct FillOp { +/// const CoordBBox bbox; +/// +/// FillOp(const CoordBBox& b): bbox(b) {} +/// +/// template +/// void operator()(GridT& grid) const { +/// typedef typename GridT::ValueType ValueT; +/// grid.fill(bbox, ValueT(1)); +/// } +/// }; +/// +/// GU_PrimVDB* vdb = ...; +/// vdb->makeGridUnique(); +/// CoordBBox bbox(Coord(0,0,0), Coord(10,10,10)); +/// UTvdbProcessTypedGrid(vdb->getStorageType(), vdb->getGrid(), FillOp(bbox)); +/// @endcode +/// +/// @return @c false if the grid type is unknown or unhandled. +/// @{ +#define UT_VDB_DECL_PROCESS_TYPED_GRID(GRID_BASE_T) \ +template \ +inline bool \ +UTvdbProcessTypedGrid(UT_VDBType grid_type, GRID_BASE_T grid, OpType& op) \ +{ \ + using namespace openvdb; \ + using namespace UT_VDBUtils; \ + switch (grid_type) \ + { \ + case UT_VDB_FLOAT: callTypedGrid(grid, op); break; \ + case UT_VDB_DOUBLE: callTypedGrid(grid, op); break; \ + case UT_VDB_INT32: callTypedGrid(grid, op); break; \ + case UT_VDB_INT64: callTypedGrid(grid, op); break; \ + case UT_VDB_VEC3F: callTypedGrid(grid, op); break; \ + case UT_VDB_VEC3D: callTypedGrid(grid, op); break; \ + case UT_VDB_VEC3I: callTypedGrid(grid, op); break; \ + default: UT_ASSERT(!"Unknown grid type"); return false; \ + } \ + return true; \ +} \ +template \ +inline bool \ +UTvdbProcessTypedGridTopology(UT_VDBType grid_type, GRID_BASE_T grid, OpType& op) \ +{ \ + using namespace openvdb; \ + using namespace UT_VDBUtils; \ + switch (grid_type) \ + { \ + case UT_VDB_FLOAT: callTypedGrid(grid, op); break; \ + case UT_VDB_DOUBLE: callTypedGrid(grid, op); break; \ + case UT_VDB_INT32: callTypedGrid(grid, op); break; \ + case UT_VDB_INT64: callTypedGrid(grid, op); break; \ + case UT_VDB_VEC3F: callTypedGrid(grid, op); break; \ + case UT_VDB_VEC3D: callTypedGrid(grid, op); break; \ + case UT_VDB_VEC3I: callTypedGrid(grid, op); break; \ + case UT_VDB_BOOL: callTypedGrid(grid, op); break; \ + default: UT_ASSERT(!"Unknown grid type"); return false; \ + } \ + return true; \ +} \ +template \ +inline bool \ +UTvdbProcessTypedGridVec3(UT_VDBType grid_type, GRID_BASE_T grid, OpType& op) \ +{ \ + using namespace openvdb; \ + using namespace UT_VDBUtils; \ + switch (grid_type) \ + { \ + case UT_VDB_VEC3F: callTypedGrid(grid, op); break; \ + case UT_VDB_VEC3D: callTypedGrid(grid, op); break; \ + case UT_VDB_VEC3I: callTypedGrid(grid, op); break; \ + default: return false; \ + } \ + return true; \ +} \ +template \ +inline bool \ +UTvdbProcessTypedGridScalar(UT_VDBType grid_type, GRID_BASE_T grid, OpType& op) \ +{ \ + using namespace openvdb; \ + using namespace UT_VDBUtils; \ + switch (grid_type) \ + { \ + case UT_VDB_FLOAT: callTypedGrid(grid, op); break; \ + case UT_VDB_DOUBLE: callTypedGrid(grid, op); break; \ + case UT_VDB_INT32: callTypedGrid(grid, op); break; \ + case UT_VDB_INT64: callTypedGrid(grid, op); break; \ + default: return false; \ + } \ + return true; \ +} \ +template \ +inline bool \ +UTvdbProcessTypedGridReal(UT_VDBType grid_type, GRID_BASE_T grid, OpType& op) \ +{ \ + using namespace openvdb; \ + using namespace UT_VDBUtils; \ + switch (grid_type) \ + { \ + case UT_VDB_FLOAT: callTypedGrid(grid, op); break; \ + case UT_VDB_DOUBLE: callTypedGrid(grid, op); break; \ + default: return false; \ + } \ + return true; \ +} \ +/**/ +UT_VDB_DECL_PROCESS_TYPED_GRID(const openvdb::GridBase &) +UT_VDB_DECL_PROCESS_TYPED_GRID(const openvdb::GridBase *) +UT_VDB_DECL_PROCESS_TYPED_GRID(openvdb::GridBase::ConstPtr) +UT_VDB_DECL_PROCESS_TYPED_GRID(openvdb::GridBase &) +UT_VDB_DECL_PROCESS_TYPED_GRID(openvdb::GridBase *) +UT_VDB_DECL_PROCESS_TYPED_GRID(openvdb::GridBase::Ptr) + +/// @} + + +// Helper macro for UTvdbCall* macros, do not outside of this file! +#define UT_VDB_CALL(GRIDT, RETURN, FNAME, GRIDBASE, ...) \ + { \ + RETURN FNAME (UTvdbGridCast(GRIDBASE), __VA_ARGS__ ); \ + } \ + /**/ + +//@{ +/// Macro to invoke the correct type of grid. +/// Use like: +/// @code +/// UTvdbCallScalarType(grid_type, myfunction, grid, parms) +/// @endcode +/// to invoke +/// @code +/// template +/// static void +/// myfunction(const GridType &grid, parms) +/// { } +/// @endcode + +#define UTvdbCallRealType(TYPE, FNAME, GRIDBASE, ...) \ + if (TYPE == UT_VDB_FLOAT) \ + UT_VDB_CALL(openvdb::FloatGrid,(void),FNAME,GRIDBASE,__VA_ARGS__) \ + else if (TYPE == UT_VDB_DOUBLE) \ + UT_VDB_CALL(openvdb::DoubleGrid,(void),FNAME,GRIDBASE,__VA_ARGS__) \ + /**/ +#define UTvdbCallScalarType(TYPE, FNAME, GRIDBASE, ...) \ + UTvdbCallRealType(TYPE, FNAME, GRIDBASE, __VA_ARGS__) \ + else if (TYPE == UT_VDB_INT32) \ + UT_VDB_CALL(openvdb::Int32Grid,(void),FNAME,GRIDBASE,__VA_ARGS__) \ + else if (TYPE == UT_VDB_INT64) \ + UT_VDB_CALL(openvdb::Int64Grid,(void),FNAME,GRIDBASE,__VA_ARGS__) \ + /**/ +#define UTvdbCallVec3Type(TYPE, FNAME, GRIDBASE, ...) \ + if (TYPE == UT_VDB_VEC3F) \ + UT_VDB_CALL(openvdb::Vec3fGrid,(void),FNAME,GRIDBASE,__VA_ARGS__) \ + else if (TYPE == UT_VDB_VEC3D) \ + UT_VDB_CALL(openvdb::Vec3dGrid,(void),FNAME,GRIDBASE,__VA_ARGS__) \ + else if (TYPE == UT_VDB_VEC3I) \ + UT_VDB_CALL(openvdb::Vec3IGrid,(void),FNAME,GRIDBASE,__VA_ARGS__) \ + /**/ +#define UTvdbCallAllType(TYPE, FNAME, GRIDBASE, ...) \ + UTvdbCallScalarType(TYPE, FNAME, GRIDBASE, __VA_ARGS__) \ + else UTvdbCallVec3Type(TYPE, FNAME, GRIDBASE, __VA_ARGS__) \ + else UT_ASSERT(!"Unknown grid type"); \ + /**/ +#define UTvdbCallAllTopology(TYPE, FNAME, GRIDBASE, ...) \ + UTvdbCallScalarType(TYPE, FNAME, GRIDBASE, __VA_ARGS__) \ + else UTvdbCallVec3Type(TYPE, FNAME, GRIDBASE, __VA_ARGS__) \ + else if (TYPE == UT_VDB_BOOL) \ + UT_VDB_CALL(openvdb::BoolGrid,(void),FNAME,GRIDBASE,__VA_ARGS__) \ + else UT_ASSERT(!"Unknown grid type"); \ + /**/ +//@} + +//@{ +/// Macro to invoke the correct type of grid. +/// Use like: +/// @code +/// UTvdbReturnScalarType(grid_type, myfunction, grid, parms) +/// @endcode +/// to invoke +/// @code +/// return myfunction(grid, parms); +/// @endcode +/// via: +/// @code +/// template +/// static RESULT +/// myfunction(const GridType &grid, parms) +/// { } +/// @endcode + +#define UTvdbReturnRealType(TYPE, FNAME, GRIDBASE, ...) \ + if (TYPE == UT_VDB_FLOAT) \ + UT_VDB_CALL(openvdb::FloatGrid,return,FNAME,GRIDBASE,__VA_ARGS__) \ + else if (TYPE == UT_VDB_DOUBLE) \ + UT_VDB_CALL(openvdb::DoubleGrid,return,FNAME,GRIDBASE,__VA_ARGS__) \ + /**/ +#define UTvdbReturnScalarType(TYPE, FNAME, GRIDBASE, ...) \ + UTvdbReturnRealType(TYPE, FNAME, GRIDBASE, __VA_ARGS__) \ + else if (TYPE == UT_VDB_INT32) \ + UT_VDB_CALL(openvdb::Int32Grid,return,FNAME,GRIDBASE,__VA_ARGS__) \ + else if (TYPE == UT_VDB_INT64) \ + UT_VDB_CALL(openvdb::Int64Grid,return,FNAME,GRIDBASE,__VA_ARGS__) \ + /**/ +#define UTvdbReturnVec3Type(TYPE, FNAME, GRIDBASE, ...) \ + if (TYPE == UT_VDB_VEC3F) \ + UT_VDB_CALL(openvdb::Vec3fGrid,return,FNAME,GRIDBASE,__VA_ARGS__) \ + else if (TYPE == UT_VDB_VEC3D) \ + UT_VDB_CALL(openvdb::Vec3dGrid,return,FNAME,GRIDBASE,__VA_ARGS__) \ + else if (TYPE == UT_VDB_VEC3I) \ + UT_VDB_CALL(openvdb::Vec3IGrid,return,FNAME,GRIDBASE,__VA_ARGS__) \ + /**/ +#define UTvdbReturnAllType(TYPE, FNAME, GRIDBASE, ...) \ + UTvdbReturnScalarType(TYPE, FNAME, GRIDBASE, __VA_ARGS__) \ + else UTvdbReturnVec3Type(TYPE, FNAME, GRIDBASE, __VA_ARGS__) \ + else UT_ASSERT(!"Unknown grid type"); \ + /**/ +#define UTvdbReturnAllTopology(TYPE, FNAME, GRIDBASE, ...) \ + UTvdbReturnScalarType(TYPE, FNAME, GRIDBASE, __VA_ARGS__) \ + else UTvdbReturnVec3Type(TYPE, FNAME, GRIDBASE, __VA_ARGS__) \ + else if (TYPE == UT_VDB_BOOL) \ + UT_VDB_CALL(openvdb::BoolGrid,return,FNAME,GRIDBASE,__VA_ARGS__) \ + else UT_ASSERT(!"Unknown grid type"); \ + /**/ +//@} + + +//////////////////////////////////////// + + +/// Matrix conversion from openvdb to UT +// @{ +template +UT_Matrix4T +UTvdbConvert(const openvdb::math::Mat4 &src) +{ + return UT_Matrix4T(src(0,0), src(0,1), src(0,2), src(0,3), + src(1,0), src(1,1), src(1,2), src(1,3), + src(2,0), src(2,1), src(2,2), src(2,3), + src(3,0), src(3,1), src(3,2), src(3,3)); +} + +template +UT_Matrix3T +UTvdbConvert(const openvdb::math::Mat3 &src) +{ + return UT_Matrix3T(src(0,0), src(0,1), src(0,2), + src(1,0), src(1,1), src(1,2), + src(2,0), src(2,1), src(2,2)); +} + +template +UT_Matrix2T +UTvdbConvert(const openvdb::math::Mat2 &src) +{ + return UT_Matrix2T(src(0,0), src(0,1), + src(1,0), src(1,1)); +} +// @} + +/// Matrix conversion from UT to openvdb +// @{ +template +openvdb::math::Mat4 +UTvdbConvert(const UT_Matrix4T &src) +{ + return openvdb::math::Mat4(src(0,0), src(0,1), src(0,2), src(0,3), + src(1,0), src(1,1), src(1,2), src(1,3), + src(2,0), src(2,1), src(2,2), src(2,3), + src(3,0), src(3,1), src(3,2), src(3,3)); +} +template +openvdb::math::Mat3 +UTvdbConvert(const UT_Matrix3T &src) +{ + return openvdb::math::Mat3(src(0,0), src(0,1), src(0,2), + src(1,0), src(1,1), src(1,2), + src(2,0), src(2,1), src(2,2)); +} +template +openvdb::math::Mat2 +UTvdbConvert(const UT_Matrix2T &src) +{ + return openvdb::math::Mat2(src(0,0), src(0,1), + src(1,0), src(1,1)); +} +// @} + +/// Vector conversion from openvdb to UT +// @{ +template +UT_Vector4T +UTvdbConvert(const openvdb::math::Vec4 &src) +{ + return UT_Vector4T(src.asPointer()); +} +template +UT_Vector3T +UTvdbConvert(const openvdb::math::Vec3 &src) +{ + return UT_Vector3T(src.asPointer()); +} +template +UT_Vector2T +UTvdbConvert(const openvdb::math::Vec2 &src) +{ + return UT_Vector2T(src.asPointer()); +} +// @} + +/// Vector conversion from UT to openvdb +// @{ +template +openvdb::math::Vec4 +UTvdbConvert(const UT_Vector4T &src) +{ + return openvdb::math::Vec4(src.data()); +} +template +openvdb::math::Vec3 +UTvdbConvert(const UT_Vector3T &src) +{ + return openvdb::math::Vec3(src.data()); +} +template +openvdb::math::Vec2 +UTvdbConvert(const UT_Vector2T &src) +{ + return openvdb::math::Vec2(src.data()); +} +// @} + + +/// Bounding box conversion from openvdb to UT +inline UT_BoundingBoxD +UTvdbConvert(const openvdb::CoordBBox &bbox) +{ + return UT_BoundingBoxD(UTvdbConvert(bbox.getStart().asVec3d()), + UTvdbConvert(bbox.getEnd().asVec3d())); +} + + +template +inline openvdb::math::Vec4 SYSabs(const openvdb::math::Vec4 &v1) +{ return openvdb::math::Vec4( SYSabs(v1[0]), + SYSabs(v1[1]), + SYSabs(v1[2]), + SYSabs(v1[3]) + ); +} +template +inline openvdb::math::Vec3 SYSabs(const openvdb::math::Vec3 &v1) +{ return openvdb::math::Vec3( SYSabs(v1[0]), + SYSabs(v1[1]), + SYSabs(v1[2]) + ); +} +template +inline openvdb::math::Vec2 SYSabs(const openvdb::math::Vec2 &v1) +{ return openvdb::math::Vec2( SYSabs(v1[0]), + SYSabs(v1[1]) + ); +} + +template +inline openvdb::math::Vec4 SYSmin(const openvdb::math::Vec4 &v1, const openvdb::math::Vec4 &v2) +{ return openvdb::math::Vec4( SYSmin(v1[0], v2[0]), + SYSmin(v1[1], v2[1]), + SYSmin(v1[2], v2[2]), + SYSmin(v1[3], v2[3]) + ); +} +template +inline openvdb::math::Vec4 SYSmax(const openvdb::math::Vec4 &v1, const openvdb::math::Vec4 &v2) +{ return openvdb::math::Vec4( SYSmax(v1[0], v2[0]), + SYSmax(v1[1], v2[1]), + SYSmax(v1[2], v2[2]), + SYSmax(v1[3], v2[3]) + ); +} +template +inline openvdb::math::Vec4 SYSmin(const openvdb::math::Vec4 &v1, const openvdb::math::Vec4 &v2, const openvdb::math::Vec4 &v3) +{ return openvdb::math::Vec4( SYSmin(v1[0], v2[0], v3[0]), + SYSmin(v1[1], v2[1], v3[1]), + SYSmin(v1[2], v2[2], v3[2]), + SYSmin(v1[3], v2[3], v3[3]) + ); +} +template +inline openvdb::math::Vec4 SYSmax(const openvdb::math::Vec4 &v1, const openvdb::math::Vec4 &v2, const openvdb::math::Vec4 &v3) +{ return openvdb::math::Vec4( SYSmax(v1[0], v2[0], v3[0]), + SYSmax(v1[1], v2[1], v3[1]), + SYSmax(v1[2], v2[2], v3[2]), + SYSmax(v1[3], v2[3], v3[3]) + ); +} +template +inline openvdb::math::Vec3 SYSmin(const openvdb::math::Vec3 &v1, const openvdb::math::Vec3 &v2) +{ return openvdb::math::Vec3( SYSmin(v1[0], v2[0]), + SYSmin(v1[1], v2[1]), + SYSmin(v1[2], v2[2]) + ); +} +template +inline openvdb::math::Vec3 SYSmax(const openvdb::math::Vec3 &v1, const openvdb::math::Vec3 &v2) +{ return openvdb::math::Vec3( SYSmax(v1[0], v2[0]), + SYSmax(v1[1], v2[1]), + SYSmax(v1[2], v2[2]) + ); +} +template +inline openvdb::math::Vec3 SYSmin(const openvdb::math::Vec3 &v1, const openvdb::math::Vec3 &v2, const openvdb::math::Vec3 &v3) +{ return openvdb::math::Vec3( SYSmin(v1[0], v2[0], v3[0]), + SYSmin(v1[1], v2[1], v3[1]), + SYSmin(v1[2], v2[2], v3[2]) + ); +} +template +inline openvdb::math::Vec3 SYSmax(const openvdb::math::Vec3 &v1, const openvdb::math::Vec3 &v2, const openvdb::math::Vec3 &v3) +{ return openvdb::math::Vec3( SYSmax(v1[0], v2[0], v3[0]), + SYSmax(v1[1], v2[1], v3[1]), + SYSmax(v1[2], v2[2], v3[2]) + ); +} +template +inline openvdb::math::Vec2 SYSmin(const openvdb::math::Vec2 &v1, const openvdb::math::Vec2 &v2) +{ return openvdb::math::Vec2( SYSmin(v1[0], v2[0]), + SYSmin(v1[1], v2[1]) + ); +} +template +inline openvdb::math::Vec2 SYSmax(const openvdb::math::Vec2 &v1, const openvdb::math::Vec2 &v2) +{ return openvdb::math::Vec2( SYSmax(v1[0], v2[0]), + SYSmax(v1[1], v2[1]) + ); +} +template +inline openvdb::math::Vec2 SYSmin(const openvdb::math::Vec2 &v1, const openvdb::math::Vec2 &v2, const openvdb::math::Vec2 &v3) +{ return openvdb::math::Vec2( SYSmin(v1[0], v2[0], v3[0]), + SYSmin(v1[1], v2[1], v3[1]) + ); +} +template +inline openvdb::math::Vec2 SYSmax(const openvdb::math::Vec2 &v1, const openvdb::math::Vec2 &v2, const openvdb::math::Vec2 &v3) +{ return openvdb::math::Vec2( SYSmax(v1[0], v2[0], v3[0]), + SYSmax(v1[1], v2[1], v3[1]) + ); +} + +#endif // __HDK_UT_VDBUtils__ + +#endif // UT_VERSION_INT < 0x0c050157 // earlier than 12.5.343 + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb_houdini/Utils.h b/nuparu/include/openvdb/include/openvdb_houdini/Utils.h new file mode 100644 index 00000000..f7056a04 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb_houdini/Utils.h @@ -0,0 +1,423 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2012-2016 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 Utils.h +/// @author FX R&D Simulation team +/// @brief Utility classes and functions for OpenVDB plugins + +#ifndef OPENVDB_HOUDINI_UTILS_HAS_BEEN_INCLUDED +#define OPENVDB_HOUDINI_UTILS_HAS_BEEN_INCLUDED + +#include "GU_PrimVDB.h" +#include +#include +#include +#include +#include + + +#ifdef SESI_OPENVDB +#ifdef OPENVDB_HOUDINI_API + #undef OPENVDB_HOUDINI_API + #define OPENVDB_HOUDINI_API +#endif +#endif + +class GEO_PrimVDB; +class GU_Detail; +class UT_String; + +namespace openvdb_houdini { + +typedef openvdb::GridBase Grid; +typedef openvdb::GridBase::Ptr GridPtr; +typedef openvdb::GridBase::ConstPtr GridCPtr; +typedef openvdb::GridBase& GridRef; +typedef const openvdb::GridBase& GridCRef; + + +/// @brief Iterator over const VDB primitives on a geometry detail +/// +/// @details At least until @c GEO_PrimVDB becomes a built-in primitive type +/// (that can be used as the mask for a @c GA_GBPrimitiveIterator), use this +/// iterator to iterate over all VDB grids belonging to a gdp and, optionally, +/// belonging to a particular group. +class OPENVDB_HOUDINI_API VdbPrimCIterator +{ +public: + typedef boost::function FilterFunc; + + /// @param gdp + /// the geometry detail over which to iterate + /// @param group + /// a group in the detail over which to iterate (if @c NULL, + /// iterate over all VDB primitives) + /// @param filter + /// an optional function or functor that takes a const reference + /// to a GU_PrimVDB and returns a boolean specifying whether + /// that primitive should be visited (@c true) or not (@c false) + explicit VdbPrimCIterator(const GEO_Detail* gdp, const GA_PrimitiveGroup* group = NULL, + FilterFunc filter = FilterFunc()); + + VdbPrimCIterator(const VdbPrimCIterator&); + VdbPrimCIterator& operator=(const VdbPrimCIterator&); + + //@{ + /// Advance to the next VDB primitive. + void advance(); + VdbPrimCIterator& operator++() { advance(); return *this; } + //@} + + //@{ + /// Return a pointer to the current VDB primitive (@c NULL if at end). + const GU_PrimVDB* getPrimitive() const; + const GU_PrimVDB* operator*() const { return getPrimitive(); } + const GU_PrimVDB* operator->() const { return getPrimitive(); } + //@} + + //@{ + GA_Offset getOffset() const { return getPrimitive()->getMapOffset(); } + GA_Index getIndex() const { return getPrimitive()->getMapIndex(); } + //@} + + /// Return @c false if there are no more VDB primitives. + operator bool() const { return getPrimitive() != NULL; } + + /// @brief Return the value of the current VDB primitive's @c name attribute. + /// @param defaultName + /// if the current primitive has no @c name attribute + /// or its name is empty, return this name instead + UT_String getPrimitiveName(const UT_String& defaultName = "") const; + + /// @brief Return the value of the current VDB primitive's @c name attribute + /// or, if the name is empty, the primitive's index (as a UT_String). + UT_String getPrimitiveNameOrIndex() const; + +protected: + /// Allow primitives to be deleted during iteration. + VdbPrimCIterator(const GEO_Detail*, GA_Range::safedeletions, + const GA_PrimitiveGroup* = NULL, FilterFunc = FilterFunc()); + + boost::shared_ptr mIter; + FilterFunc mFilter; +}; // class VdbPrimCIterator + + +/// @brief Iterator over non-const VDB primitives on a geometry detail +/// +/// @details At least until @c GEO_PrimVDB becomes a built-in primitive type +/// (that can be used as the mask for a @c GA_GBPrimitiveIterator), use this +/// iterator to iterate over all VDB grids belonging to a gdp and, optionally, +/// belonging to a particular group. +class OPENVDB_HOUDINI_API VdbPrimIterator: public VdbPrimCIterator +{ +public: + /// @param gdp + /// the geometry detail over which to iterate + /// @param group + /// a group in the detail over which to iterate (if @c NULL, + /// iterate over all VDB primitives) + /// @param filter + /// an optional function or functor that takes a @c const reference + /// to a GU_PrimVDB and returns a boolean specifying whether + /// that primitive should be visited (@c true) or not (@c false) + explicit VdbPrimIterator(GEO_Detail* gdp, const GA_PrimitiveGroup* group = NULL, + FilterFunc filter = FilterFunc()): + VdbPrimCIterator(gdp, group, filter) {} + /// @brief Allow primitives to be deleted during iteration. + /// @param gdp + /// the geometry detail over which to iterate + /// @param group + /// a group in the detail over which to iterate (if @c NULL, + /// iterate over all VDB primitives) + /// @param filter + /// an optional function or functor that takes a @c const reference + /// to a GU_PrimVDB and returns a boolean specifying whether + /// that primitive should be visited (@c true) or not (@c false) + VdbPrimIterator(GEO_Detail* gdp, GA_Range::safedeletions, + const GA_PrimitiveGroup* group = NULL, FilterFunc filter = FilterFunc()): + VdbPrimCIterator(gdp, GA_Range::safedeletions(), group, filter) {} + + VdbPrimIterator(const VdbPrimIterator&); + VdbPrimIterator& operator=(const VdbPrimIterator&); + + /// Advance to the next VDB primitive. + VdbPrimIterator& operator++() { advance(); return *this; } + + //@{ + /// Return a pointer to the current VDB primitive (@c NULL if at end). + GU_PrimVDB* getPrimitive() const { + return const_cast(VdbPrimCIterator::getPrimitive()); + } + GU_PrimVDB* operator*() const { return getPrimitive(); } + GU_PrimVDB* operator->() const { return getPrimitive(); } + //@} +}; // class VdbPrimIterator + + +//////////////////////////////////////// + + +/// @brief Wrapper class that adapts a Houdini @c UT_Interrupt object +/// for use with OpenVDB library routines +/// @sa openvdb/util/NullInterrupter.h +class Interrupter +{ +public: + Interrupter(const char* title = NULL): + mUTI(UTgetInterrupt()), mRunning(false) + { + if (title) mUTI->setAppTitle(title); + } + ~Interrupter() { if (mRunning) this->end(); } + + /// @brief Signal the start of an interruptible operation. + /// @param name an optional descriptive name for the operation + void start(const char* name = NULL) { if (!mRunning) { mRunning=true; mUTI->opStart(name); } } + /// Signal the end of an interruptible operation. + void end() { if (mRunning) { mUTI->opEnd(); mRunning = false; } } + + /// @brief 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 + bool wasInterrupted(int percent=-1) { return mUTI->opInterrupt(percent); } + +private: + UT_Interrupt* mUTI; + bool mRunning; +}; + + +//////////////////////////////////////// + + +// Utility methods + +/// @brief Store a VDB grid in a new VDB primitive and add the primitive +/// to a geometry detail. +/// @return the newly-created VDB primitive. +/// @param gdp the detail to which to add the primitive +/// @param grid the VDB grid to be added +/// @param name if non-null, set the new primitive's @c name attribute to this string +/// @note This operation clears the input grid's metadata. +OPENVDB_HOUDINI_API +GU_PrimVDB* createVdbPrimitive(GU_Detail& gdp, GridPtr grid, const char* name = NULL); + + +/// @brief Replace an existing VDB primitive with a new primitive that contains +/// the given grid. +/// @return the newly-created VDB primitive. +/// @param gdp the detail to which to add the primitive +/// @param grid the VDB grid to be added +/// @param src replace this primitive with the newly-created primitive +/// @param copyAttrs if @c true, copy attributes and group membership from the @a src primitive +/// @param name if non-null, set the new primitive's @c name attribute to this string; +/// otherwise, if @a copyAttrs is @c true, copy the name from @a src +/// @note This operation clears the input grid's metadata. +OPENVDB_HOUDINI_API +GU_PrimVDB* replaceVdbPrimitive(GU_Detail& gdp, GridPtr grid, GEO_PrimVDB& src, + const bool copyAttrs = true, const char* name = NULL); + + +/// @brief Return in @a corners the corners of the given grid's active voxel bounding box. +/// @return @c false if the grid has no active voxels. +OPENVDB_HOUDINI_API +bool evalGridBBox(GridCRef grid, UT_Vector3 corners[8], bool expandHalfVoxel = false); + + +/// Construct an index-space CoordBBox from a UT_BoundingBox. +OPENVDB_HOUDINI_API +openvdb::CoordBBox makeCoordBBox(const UT_BoundingBox&, const openvdb::math::Transform&); + + +//////////////////////////////////////// + + +/// Helper class used internally by processTypedGrid() +template +struct GridProcessor { + static inline void call(OpType& op, GridPtr grid) { +#ifdef _MSC_VER + op.operator()(openvdb::gridPtrCast(grid)); +#else + op.template operator()(openvdb::gridPtrCast(grid)); +#endif + } +}; + +/// Helper class used internally by processTypedGrid() +template +struct GridProcessor { + static inline void call(OpType& op, GridCPtr grid) { +#ifdef _MSC_VER + op.operator()(openvdb::gridConstPtrCast(grid)); +#else + op.template operator()(openvdb::gridConstPtrCast(grid)); +#endif + } +}; + + +/// Helper function used internally by processTypedGrid() +template +inline void +doProcessTypedGrid(GridPtrType grid, OpType& op) +{ + GridProcessor::value>::call(op, grid); +} + + +//////////////////////////////////////// + + +/// @brief Utility function that, given a generic grid pointer, +/// calls a functor on the fully-resolved grid +/// +/// @par Example: +/// @code +/// using openvdb::Coord; +/// using openvdb::CoordBBox; +/// +/// struct FillOp { +/// const CoordBBox bbox; +/// +/// FillOp(const CoordBBox& b): bbox(b) {} +/// +/// template +/// void operator()(typename GridT::Ptr grid) const { +/// typedef typename GridT::ValueType ValueT; +/// grid->fill(bbox, ValueT(1)); +/// } +/// }; +/// +/// CoordBBox bbox(Coord(0,0,0), Coord(10,10,10)); +/// processTypedGrid(myGridPtr, FillOp(bbox)); +/// @endcode +/// +/// @return @c false if the grid type is unknown or unhandled. +/// @deprecated Use UTvdbProcessTypedGrid() or GEOvdbProcessTypedGrid() instead. +template +OPENVDB_DEPRECATED +bool +processTypedGrid(GridPtrType grid, OpType& op) +{ + using namespace openvdb; + if (grid->template isType()) doProcessTypedGrid(grid, op); + else if (grid->template isType()) doProcessTypedGrid(grid, op); + else if (grid->template isType()) doProcessTypedGrid(grid, op); + else if (grid->template isType()) doProcessTypedGrid(grid, op); + else if (grid->template isType()) doProcessTypedGrid(grid, op); + else if (grid->template isType()) doProcessTypedGrid(grid, op); + else if (grid->template isType()) doProcessTypedGrid(grid, op); + else if (grid->template isType()) doProcessTypedGrid(grid, op); + else return false; ///< @todo throw exception ("unknown grid type") + return true; +} + + +/// @brief Utility function that, given a generic grid pointer, calls +/// a functor on the fully-resolved grid, provided that the grid's +/// voxel values are 3-vectors (vec3i, vec3s or vec3d) +/// +/// Usage: +/// @code +/// struct NormalizeOp { +/// template +/// void operator()(typename GridT::Ptr grid) const { normalizeVectors(*grid); } +/// }; +/// +/// processTypedVec3Grid(myGridPtr, NormalizeOp()); +/// @endcode +/// +/// @return @c false if the grid type is unknown or non-vector. +/// @sa UTvdbProcessTypedGridVec3 +/// @deprecated Use UTvdbProcessTypedGridVec3() or GEOvdbProcessTypedGridVec3() instead. +template +OPENVDB_DEPRECATED +bool +processTypedVec3Grid(GridPtrType grid, OpType& op) +{ + using namespace openvdb; + if (grid->template isType()) doProcessTypedGrid(grid, op); + else if (grid->template isType()) doProcessTypedGrid(grid, op); + else if (grid->template isType()) doProcessTypedGrid(grid, op); + else return false; ///< @todo throw exception ("grid type is not vec3") + return true; +} + + +/// @brief Utility function that, given a generic grid pointer, +/// calls a functor on the fully-resolved grid +/// +/// @par Example: +/// @code +/// using openvdb::Coord; +/// using openvdb::CoordBBox; +/// +/// struct FillOp { +/// const CoordBBox bbox; +/// +/// FillOp(const CoordBBox& b): bbox(b) {} +/// +/// template +/// void operator()(typename GridT::Ptr grid) const { +/// typedef typename GridT::ValueType ValueT; +/// grid->fill(bbox, ValueT(1)); +/// } +/// }; +/// +/// CoordBBox bbox(Coord(0,0,0), Coord(10,10,10)); +/// processTypedScalarGrid(myGridPtr, FillOp(bbox)); +/// @endcode +/// +/// @return @c false if the grid type is unknown or non-scalar. +/// @deprecated Use UTvdbProcessTypedGridScalar() or GEOvdbProcessTypedGridScalar() instead. +template +OPENVDB_DEPRECATED +bool +processTypedScalarGrid(GridPtrType grid, OpType& op) +{ + using namespace openvdb; + if (grid->template isType()) doProcessTypedGrid(grid, op); + else if (grid->template isType()) doProcessTypedGrid(grid, op); + else if (grid->template isType()) doProcessTypedGrid(grid, op); + else if (grid->template isType()) doProcessTypedGrid(grid, op); + else return false; ///< @todo throw exception ("grid type is not scalar") + return true; +} + +} // namespace openvdb_houdini + +#endif // OPENVDB_HOUDINI_UTILS_HAS_BEEN_INCLUDED + +// Copyright (c) 2012-2016 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/nuparu/include/openvdb/include/openvdb_points/Types.h b/nuparu/include/openvdb/include/openvdb_points/Types.h new file mode 100644 index 00000000..0231bee1 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb_points/Types.h @@ -0,0 +1,65 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015-2016 Double Negative Visual Effects +// +// 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 Double Negative Visual Effects 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_POINTS_TYPES_HAS_BEEN_INCLUDED +#define OPENVDB_POINTS_TYPES_HAS_BEEN_INCLUDED + +#include +#include +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + +// add some extra typeNameAsString specializations + +template<> inline const char* typeNameAsString() { return "half"; } +template<> inline const char* typeNameAsString() { return "uint8"; } +template<> inline const char* typeNameAsString() { return "int16"; } +template<> inline const char* typeNameAsString() { return "uint16"; } +template<> inline const char* typeNameAsString >() { return "vec2h"; } +template<> inline const char* typeNameAsString >() { return "vec3h"; } +template<> inline const char* typeNameAsString >() { return "vec3u8"; } +template<> inline const char* typeNameAsString >() { return "vec3u16"; } + + +//////////////////////////////////////// + + +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_POINTS_TYPES_HAS_BEEN_INCLUDED + +// Copyright (c) 2015-2016 Double Negative Visual Effects +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) diff --git a/nuparu/include/openvdb/include/openvdb_points/openvdb.h b/nuparu/include/openvdb/include/openvdb_points/openvdb.h new file mode 100644 index 00000000..9b95299c --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb_points/openvdb.h @@ -0,0 +1,56 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015-2016 Double Negative Visual Effects +// +// 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 Double Negative Visual Effects 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_POINTS_INIT_HAS_BEEN_INCLUDED +#define OPENVDB_POINTS_INIT_HAS_BEEN_INCLUDED + +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + +namespace points { + +/// Global registration of basic types +OPENVDB_API void initialize(); + +/// Global deregistration of basic types +OPENVDB_API void uninitialize(); + +} // namespace points +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_POINTS_INIT_HAS_BEEN_INCLUDED + +// Copyright (c) 2015-2016 Double Negative Visual Effects +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) diff --git a/nuparu/include/openvdb/include/openvdb_points/tools/AttributeArray.h b/nuparu/include/openvdb/include/openvdb_points/tools/AttributeArray.h new file mode 100644 index 00000000..9675e76b --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb_points/tools/AttributeArray.h @@ -0,0 +1,1573 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015-2016 Double Negative Visual Effects +// +// 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 Double Negative Visual Effects 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 AttributeArray.h +/// +/// @authors Dan Bailey, Mihai Alden, Peter Cucka +/// +/// @brief Attribute Array storage templated on type and compression codec. +/// + + +#ifndef OPENVDB_TOOLS_ATTRIBUTE_ARRAY_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_ATTRIBUTE_ARRAY_HAS_BEEN_INCLUDED + +#include +#include +#include +#include +#include // MappedFile +#include // COMPRESS_BLOSC + +#include + +#include +#include + +#include + +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + + +// Add new typedef for a Name pair +typedef std::pair NamePair; + +namespace tools { + + +//////////////////////////////////////// + +// Attribute Compression methods + + +namespace attribute_compression { + +/// @brief Returns true if compression is available +bool canCompress(); + +/// @brief Retrieves the uncompressed size of buffer when uncompressed +/// +/// @param buffer the compressed buffer +int uncompressedSize(const char* buffer); + +/// @brief Retrieves the compressed size of buffer when compressed +/// +/// @param buffer the uncompressed buffer +/// @param typeSize the size of the data type +/// @param uncompressedBytes number of uncompressed bytes +int compressedSize(const char* buffer, const size_t typeSize, const int uncompressedBytes); + +/// @brief Compress and return the compressed buffer. +/// +/// @param buffer the buffer to compress +/// @param typeSize the size of the data type +/// @param uncompressedBytes number of uncompressed bytes +/// @param compressedBytes number of compressed bytes (written to this variable) +/// @param cleanup if true, the supplied buffer will be deleted prior to allocating new memory +char* compress( char* buffer, const size_t typeSize, + const int uncompressedBytes, int& compressedBytes, + const bool cleanup = false); + +/// @brief Compress and return the compressed buffer. +/// +/// @param buffer the buffer to compress +/// @param typeSize the size of the data type +/// @param uncompressedBytes number of uncompressed bytes +/// @param compressedBytes number of compressed bytes (written to this variable) +/// +/// @note Unlike the non-const buffer version, the buffer will never be deleted. +char* compress( const char* buffer, const size_t typeSize, + const int uncompressedBytes, int& compressedBytes); + +/// @brief Decompress and return the uncompressed buffer. +/// +/// @param buffer the buffer to decompress +/// @param expectedBytes the number of bytes expected once the buffer is decompressed +/// @param cleanup if true, the supplied buffer will be deleted prior to allocating new memory +char* decompress(char* buffer, const int expectedBytes, const bool cleanup = false); + +/// @brief Decompress and return the uncompressed buffer. +/// +/// @param buffer the buffer to decompress +/// @param expectedBytes the number of bytes expected once the buffer is decompressed +/// +/// @note Unlike the non-const buffer version, the buffer will never be deleted. +char* decompress(const char* buffer, const int expectedBytes); + +} // namespace attribute_compression + + +//////////////////////////////////////// + +// Utility methods + +template +inline IntegerT +floatingPointToFixedPoint(const FloatT s) +{ + BOOST_STATIC_ASSERT(boost::is_unsigned::value); + if (FloatT(0.0) > s) return std::numeric_limits::min(); + else if (FloatT(1.0) <= s) return std::numeric_limits::max(); + return IntegerT(std::floor(s * FloatT(std::numeric_limits::max()))); +} + + +template +inline FloatT +fixedPointToFloatingPoint(const IntegerT s) +{ + BOOST_STATIC_ASSERT(boost::is_unsigned::value); + return FloatT(s) / FloatT((std::numeric_limits::max())); +} + + +template +inline IntegerVectorT +floatingPointToFixedPoint(const math::Vec3& v) +{ + return IntegerVectorT( + floatingPointToFixedPoint(v.x()), + floatingPointToFixedPoint(v.y()), + floatingPointToFixedPoint(v.z())); +} + +template +inline FloatVectorT +fixedPointToFloatingPoint(const math::Vec3& v) +{ + return FloatVectorT( + fixedPointToFloatingPoint(v.x()), + fixedPointToFloatingPoint(v.y()), + fixedPointToFloatingPoint(v.z())); +} + + +//////////////////////////////////////// + +// Attribute codec schemes + +template +struct NullAttributeCodec +{ + typedef StorageType_ StorageType; + template static void decode(const StorageType&, ValueType&); + template static void encode(const StorageType&, ValueType&); + static const char* name() { return "null"; } +}; + + +template +struct FixedPointAttributeCodec +{ + typedef IntType StorageType; + template static void decode(const StorageType&, ValueType&); + template static void encode(const ValueType&, StorageType&); + static const char* name() { return "fxpt"; } +}; + + +struct UnitVecAttributeCodec +{ + typedef uint16_t StorageType; + template static void decode(const StorageType&, math::Vec3&); + template static void encode(const math::Vec3&, StorageType&); + static const char* name() { return "uvec"; } +}; + + +//////////////////////////////////////// + + +/// Base class for storing attribute data +class AttributeArray +{ +protected: + struct AccessorBase; + template struct Accessor; + + typedef boost::shared_ptr AccessorBasePtr; + +public: + enum Flag { TRANSIENT = 0x1, HIDDEN = 0x2, GROUP=0x4, WRITEUNIFORM=0x8, + WRITEMEMCOMPRESS=0x10, WRITEDISKCOMPRESS=0x20, OUTOFCORE=0x40 }; + +#ifndef OPENVDB_2_ABI_COMPATIBLE + struct FileInfo + { + FileInfo(): bufpos(0), bytes(0) {} + std::streamoff bufpos; + Index64 bytes; + io::MappedFile::Ptr mapping; + boost::shared_ptr meta; + }; +#endif + + typedef boost::shared_ptr Ptr; + typedef boost::shared_ptr ConstPtr; + + template friend class AttributeHandle; + + typedef Ptr (*FactoryMethod)(size_t); + + AttributeArray() : mCompressedBytes(0), mFlags(0) {} + virtual ~AttributeArray() {} + + /// Return a copy of this attribute. + virtual AttributeArray::Ptr copy() const = 0; + + /// Return an uncompressed copy of this attribute (will return a copy if not compressed). + virtual AttributeArray::Ptr copyUncompressed() const = 0; + + /// Return the length of this array. + virtual size_t size() const = 0; + + /// Return the number of bytes of memory used by this attribute. + virtual size_t memUsage() const = 0; + + /// Create a new attribute array of the given (registered) type and length. + static Ptr create(const NamePair& type, size_t length); + /// Return @c true if the given attribute type name is registered. + static bool isRegistered(const NamePair& type); + /// Clear the attribute type registry. + static void clearRegistry(); + + /// Return the name of this attribute's type. + virtual const NamePair& type() const = 0; + /// Return @c true if this attribute is of the same type as the template parameter. + template + bool isType() const { return this->type() == AttributeArrayType::attributeType(); } + + /// Return @c true if this attribute has a value type the same as the template parameter + template + bool hasValueType() const { return this->type().first == typeNameAsString();} + + /// Set value at given index @a n from @a sourceIndex of another @a sourceArray + virtual void set(const Index n, const AttributeArray& sourceArray, const Index sourceIndex) = 0; + + /// Return @c true if this array is stored as a single uniform value. + virtual bool isUniform() const = 0; + /// @brief If this array is uniform, replace it with an array of length size(). + /// @param fill if true, assign the uniform value to each element of the array. + virtual void expand(bool fill = true) = 0; + /// Replace the existing array with a uniform zero value. + virtual void collapse() = 0; + /// Compact the existing array to become uniform if all values are identical + virtual bool compact() = 0; + + /// Return @c true if this array is compressed. + bool isCompressed() const { return mCompressedBytes != 0; } + /// Compress the attribute array. + virtual bool compress() = 0; + /// Uncompress the attribute array. + virtual bool decompress() = 0; + + /// @brief Specify whether this attribute should be hidden (e.g., from UI or iterators). + /// @details This is useful if the attribute is used for blind data or as scratch space + /// for a calculation. + /// @note Attributes are not hidden by default. + void setHidden(bool state); + /// Return @c true if this attribute is hidden (e.g., from UI or iterators). + bool isHidden() const { return bool(mFlags & HIDDEN); } + + /// @brief Specify whether this attribute should only exist in memory + /// and not be serialized during stream output. + /// @note Attributes are not transient by default. + void setTransient(bool state); + /// Return @c true if this attribute is not serialized during stream output. + bool isTransient() const { return bool(mFlags & TRANSIENT); } + + /// @brief Retrieve the attribute array flags + uint16_t flags() const { return mFlags; } + + IndexIter beginIndex() const; + + /// Read attribute metadata and buffers from a stream. + virtual void read(std::istream&) = 0; + /// Write attribute metadata and buffers to a stream. + virtual void write(std::ostream&) const = 0; + + /// Ensures all data is in-core + virtual void loadData() const = 0; + + /// Check the compressed bytes and flags. If they are equal, perform a deeper + /// comparison check necessary on the inherited types (TypedAttributeArray) + /// Requires non operator implementation due to inheritance + bool operator==(const AttributeArray& other) const; + bool operator!=(const AttributeArray& other) const { return !this->operator==(other); } + +private: + /// Virtual function used by the comparison operator to perform + /// comparisons on inherited types + virtual bool isEqual(const AttributeArray& other) const = 0; + +protected: + /// Obtain an Accessor that stores getter and setter functors. + virtual AccessorBasePtr getAccessor() const = 0; + + /// Register a attribute type along with a factory function. + static void registerType(const NamePair& type, FactoryMethod); + /// Remove a attribute type from the registry. + static void unregisterType(const NamePair& type); + + size_t mCompressedBytes; + uint16_t mFlags; + + /// Out-of-core data +#ifndef OPENVDB_2_ABI_COMPATIBLE + boost::shared_ptr mFileInfo; +#endif +}; // class AttributeArray + + +//////////////////////////////////////// + + +/// Accessor base class for AttributeArray storage where type is not available +struct AttributeArray::AccessorBase { }; + +/// Templated Accessor stores typed function pointers used in binding +/// AttributeHandles +template +struct AttributeArray::Accessor : public AttributeArray::AccessorBase +{ + typedef T (*GetterPtr)(const AttributeArray* array, const Index n); + typedef void (*SetterPtr)(AttributeArray* array, const Index n, const T& value); + typedef void (*ValuePtr)(AttributeArray* array, const T& value); + + Accessor(GetterPtr getter, SetterPtr setter, ValuePtr collapser, ValuePtr filler) : + mGetter(getter), mSetter(setter), mCollapser(collapser), mFiller(filler) { } + + GetterPtr mGetter; + SetterPtr mSetter; + ValuePtr mCollapser; + ValuePtr mFiller; +}; // struct AttributeArray::Accessor + + +//////////////////////////////////////// + + +/// Typed class for storing attribute data +template > +class TypedAttributeArray: public AttributeArray +{ +public: + typedef boost::shared_ptr Ptr; + typedef boost::shared_ptr ConstPtr; + + typedef ValueType_ ValueType; + typedef Codec_ Codec; + typedef typename Codec::StorageType StorageType; + + ////////// + + /// Default constructor, always constructs a uniform attribute. + explicit TypedAttributeArray(size_t n = 1, + const ValueType& uniformValue = zeroVal()); + /// Deep copy constructor (optionally decompress during copy). + TypedAttributeArray(const TypedAttributeArray&, bool uncompress = false); + /// Deep copy assignment operator. + TypedAttributeArray& operator=(const TypedAttributeArray&); + + virtual ~TypedAttributeArray() { this->deallocate(); } + + /// Return a copy of this attribute. + virtual AttributeArray::Ptr copy() const; + + /// Return an uncompressed copy of this attribute (will just return a copy if not compressed). + virtual AttributeArray::Ptr copyUncompressed() const; + + /// Return a new attribute array of the given length @a n with uniform value zero. + static Ptr create(size_t n); + + /// Cast an AttributeArray to TypedAttributeArray + static TypedAttributeArray& cast(AttributeArray& attributeArray); + + /// Cast an AttributeArray to TypedAttributeArray + static const TypedAttributeArray& cast(const AttributeArray& attributeArray); + + /// Return the name of this attribute's type (includes codec) + static const NamePair& attributeType(); + /// Return the name of this attribute's type. + virtual const NamePair& type() const { return attributeType(); } + + /// Return @c true if this attribute type is registered. + static bool isRegistered(); + /// Register this attribute type along with a factory function. + static void registerType(); + /// Remove this attribute type from the registry. + static void unregisterType(); + + /// Return the length of this array. + virtual size_t size() const { return mSize; }; + + /// Return the number of bytes of memory used by this attribute. + virtual size_t memUsage() const; + + /// Return the value at index @a n (assumes uncompressed and in-core) + ValueType getUnsafe(Index n) const; + /// Return the value at index @a n + ValueType get(Index n) const; + /// Return the @a value at index @a n (assumes uncompressed and in-core) + template void getUnsafe(Index n, T& value) const; + /// Return the @a value at index @a n + template void get(Index n, T& value) const; + + /// Non-member equivalent to getUnsafe() that static_casts array to this TypedAttributeArray + /// (assumes uncompressed and in-core) + static ValueType getUnsafe(const AttributeArray* array, const Index n); + + /// Set @a value at the given index @a n (assumes uncompressed and in-core) + void setUnsafe(Index n, const ValueType& value); + /// Set @a value at the given index @a n + void set(Index n, const ValueType& value); + /// Set @a value at the given index @a n (assumes uncompressed and in-core) + template void setUnsafe(Index n, const T& value); + /// Set @a value at the given index @a n + template void set(Index n, const T& value); + + /// Non-member equivalent to setUnsafe() that static_casts array to this TypedAttributeArray + /// (assumes uncompressed and in-core) + static void setUnsafe(AttributeArray* array, const Index n, const ValueType& value); + + /// Set value at given index @a n from @a sourceIndex of another @a sourceArray + virtual void set(const Index n, const AttributeArray& sourceArray, const Index sourceIndex); + + /// Return @c true if this array is stored as a single uniform value. + virtual bool isUniform() const { return mIsUniform; } + /// @brief Replace the single value storage with an array of length size(). + /// @note Non-uniform attributes are unchanged. + /// @param fill toggle to initialize the array elements with the pre-expanded value. + virtual void expand(bool fill = true); + /// Replace the existing array with a uniform zero value. + virtual void collapse(); + /// Compact the existing array to become uniform if all values are identical + virtual bool compact(); + + /// Replace the existing array with the given uniform value. + void collapse(const ValueType& uniformValue); + /// @brief Fill the existing array with the given value. + /// @note Identical to collapse() except a non-uniform array will not become uniform. + void fill(const ValueType& value); + + /// Non-member equivalent to collapse() that static_casts array to this TypedAttributeArray + static void collapse(AttributeArray* array, const ValueType& value); + /// Non-member equivalent to fill() that static_casts array to this TypedAttributeArray + static void fill(AttributeArray* array, const ValueType& value); + + /// Compress the attribute array. + virtual bool compress(); + /// Uncompress the attribute array. + virtual bool decompress(); + + /// Read attribute data from a stream. + virtual void read(std::istream& is); + /// Write attribute data to a stream. + virtual void write(std::ostream& os) const; + + /// Return @c true if this buffer's values have not yet been read from disk. + inline bool isOutOfCore() const; + + /// Ensures all data is in-core + virtual void loadData() const; + +protected: + virtual AccessorBasePtr getAccessor() const; + +private: + /// Load data from memory-mapped file. + inline void doLoad() const; + /// Load data from memory-mapped file (unsafe as this function is not protected by a mutex). + inline void doLoadUnsafe() const; + + /// Toggle out-of-core state + inline void setOutOfCore(const bool); + + /// Compare the this data to another attribute array. Used by the base class comparison operator + virtual bool isEqual(const AttributeArray& other) const; + + size_t arrayMemUsage() const; + void allocate(const size_t size); + void deallocate(); + + /// Helper function for use with registerType() + static AttributeArray::Ptr factory(size_t n) { return TypedAttributeArray::create(n); } + + static tbb::atomic sTypeName; + StorageType* mData; + size_t mSize; + bool mIsUniform; + tbb::spin_mutex mMutex; +}; // class TypedAttributeArray + + +//////////////////////////////////////// + + +/// AttributeHandles provide access to specific TypedAttributeArray methods without needing +/// to know the compression codec, however these methods also incur the cost of a function pointer +template +class AttributeHandle +{ +public: + typedef boost::shared_ptr > Ptr; + +protected: + typedef T (*GetterPtr)(const AttributeArray* array, const Index n); + typedef void (*SetterPtr)(AttributeArray* array, const Index n, const T& value); + typedef void (*ValuePtr)(AttributeArray* array, const T& value); + +public: + static Ptr create(const AttributeArray& array, const bool preserveCompression = true); + + AttributeHandle(const AttributeArray& array, const bool preserveCompression = true); + + bool isUniform() const; + + T get(Index n) const; + +protected: + const AttributeArray* mArray; + + GetterPtr mGetter; + SetterPtr mSetter; + ValuePtr mCollapser; + ValuePtr mFiller; + +private: + // local copy of AttributeArray (to preserve compression) + AttributeArray::Ptr mLocalArray; +}; // class AttributeHandle + + +/// Write-able version of AttributeHandle +template +class AttributeWriteHandle : public AttributeHandle +{ +public: + typedef boost::shared_ptr > Ptr; + + static Ptr create(AttributeArray& array); + + AttributeWriteHandle(AttributeArray& array); + + /// @brief If this array is uniform, replace it with an array of length size(). + /// @param fill if true, assign the uniform value to each element of the array. + void expand(bool fill = true); + + /// Replace the existing array with a uniform value (zero if none provided). + void collapse(); + void collapse(const T& uniformValue); + + /// Compact the existing array to become uniform if all values are identical + bool compact(); + + /// @brief Fill the existing array with the given value. + /// @note Identical to collapse() except a non-uniform array will not become uniform. + void fill(const T& value); + + void set(Index n, const T& value); +}; // class AttributeWriteHandle + + +typedef AttributeHandle AttributeHandleROF; +typedef AttributeWriteHandle AttributeHandleRWF; + +typedef AttributeHandle AttributeHandleROVec3f; +typedef AttributeWriteHandle AttributeHandleRWVec3f; + + +//////////////////////////////////////// + + +// Attribute codec implementation + + +template +template +inline void +NullAttributeCodec::decode(const StorageType& data, ValueType& val) +{ + val = static_cast(data); +} + + +template +template +inline void +NullAttributeCodec::encode(const StorageType& val, ValueType& data) +{ + data = static_cast(val); +} + + +template +template +inline void +FixedPointAttributeCodec::decode(const StorageType& data, ValueType& val) +{ + val = fixedPointToFloatingPoint(data); + + // shift value range to be -0.5 => 0.5 (as this is most commonly used for position) + + val -= ValueType(0.5); +} + + +template +template +inline void +FixedPointAttributeCodec::encode(const ValueType& val, StorageType& data) +{ + // shift value range to be -0.5 => 0.5 (as this is most commonly used for position) + + const ValueType newVal = val + ValueType(0.5); + + data = floatingPointToFixedPoint(newVal); +} + + +template +inline void +UnitVecAttributeCodec::decode(const StorageType& data, math::Vec3& val) +{ + val = math::QuantizedUnitVec::unpack(data); +} + + +template +inline void +UnitVecAttributeCodec::encode(const math::Vec3& val, StorageType& data) +{ + data = math::QuantizedUnitVec::pack(val); +} + + +//////////////////////////////////////// + +// TypedAttributeArray implementation + +template +tbb::atomic TypedAttributeArray::sTypeName; + + +template +TypedAttributeArray::TypedAttributeArray( + size_t n, const ValueType& uniformValue) + : AttributeArray() + , mData(new StorageType[1]) + , mSize(n) + , mIsUniform(true) + , mMutex() +{ + mSize = std::max(size_t(1), mSize); + Codec::encode(uniformValue, mData[0]); +} + + +template +TypedAttributeArray::TypedAttributeArray(const TypedAttributeArray& rhs, bool uncompress) + : AttributeArray(rhs) + , mData(NULL) + , mSize(rhs.mSize) + , mIsUniform(rhs.mIsUniform) + , mMutex() +{ + using attribute_compression::decompress; + using attribute_compression::uncompressedSize; + + // disable uncompress if data is not compressed + + if (!this->isCompressed()) uncompress = false; + + if (mIsUniform) { + this->allocate(1); + mData[0] = rhs.mData[0]; + } else if (this->isOutOfCore()) { + // do nothing + } else if (this->isCompressed()) { + char* buffer = 0; + if (uncompress) { + rhs.doLoad(); + const char* charBuffer = reinterpret_cast(rhs.mData); + buffer = decompress(charBuffer, uncompressedSize(charBuffer)); + } + if (buffer) mCompressedBytes = 0; + else { + // decompression wasn't requested or failed so deep copy instead + buffer = new char[mCompressedBytes]; + memcpy(buffer, rhs.mData, mCompressedBytes); + } + assert(buffer); + mData = reinterpret_cast(buffer); + } else { + this->allocate(mSize); + memcpy(mData, rhs.mData, mSize * sizeof(StorageType)); + } +} + + +template +typename TypedAttributeArray::TypedAttributeArray& +TypedAttributeArray::operator=(const TypedAttributeArray& rhs) +{ + if (&rhs != this) { + tbb::spin_mutex::scoped_lock lock(mMutex); + + this->deallocate(); + + mFlags = rhs.mFlags; + mCompressedBytes = rhs.mCompressedBytes; + mSize = rhs.mSize; + mIsUniform = rhs.mIsUniform; + + if (mIsUniform) { + this->allocate(1); + mData[0] = rhs.mData[0]; +#ifndef OPENVDB_2_ABI_COMPATIBLE + } else if (rhs.isOutOfCore()) { + mFileInfo = rhs.mFileInfo; +#endif + } else if (this->isCompressed()) { + char* buffer = new char[mCompressedBytes]; + memcpy(buffer, rhs.mData, mCompressedBytes); + mData = reinterpret_cast(buffer); + } else { + this->allocate(mSize); + memcpy(mData, rhs.mData, mSize * sizeof(StorageType)); + } + } +} + + +template +inline const NamePair& +TypedAttributeArray::attributeType() +{ + if (sTypeName == NULL) { + std::ostringstream ostr1, ostr2; + ostr1 << typeNameAsString(); + ostr2 << Codec::name() << "_" << typeNameAsString(); + NamePair* s = new NamePair(ostr1.str(), ostr2.str()); + if (sTypeName.compare_and_swap(s, NULL) != NULL) delete s; + } + return *sTypeName; +} + + +template +inline bool +TypedAttributeArray::isRegistered() +{ + return AttributeArray::isRegistered(TypedAttributeArray::attributeType()); +} + + +template +inline void +TypedAttributeArray::registerType() +{ + AttributeArray::registerType(TypedAttributeArray::attributeType(), TypedAttributeArray::factory); +} + + +template +inline void +TypedAttributeArray::unregisterType() +{ + AttributeArray::unregisterType(TypedAttributeArray::attributeType()); +} + + +template +inline typename TypedAttributeArray::Ptr +TypedAttributeArray::create(size_t n) +{ + return Ptr(new TypedAttributeArray(n)); +} + +template +inline TypedAttributeArray& +TypedAttributeArray::cast(AttributeArray& attributeArray) +{ + if (!attributeArray.isType()) { + OPENVDB_THROW(TypeError, "Invalid Attribute Type"); + } + return static_cast(attributeArray); +} + +template +inline const TypedAttributeArray& +TypedAttributeArray::cast(const AttributeArray& attributeArray) +{ + if (!attributeArray.isType()) { + OPENVDB_THROW(TypeError, "Invalid Attribute Type"); + } + return static_cast(attributeArray); +} + +template +AttributeArray::Ptr +TypedAttributeArray::copy() const +{ + return AttributeArray::Ptr(new TypedAttributeArray(*this)); +} + + +template +AttributeArray::Ptr +TypedAttributeArray::copyUncompressed() const +{ + return AttributeArray::Ptr(new TypedAttributeArray(*this, /*decompress = */true)); +} + + +template +size_t +TypedAttributeArray::arrayMemUsage() const +{ + if (mIsUniform) return sizeof(StorageType); + if (this->isOutOfCore()) return 0; + if (this->isCompressed()) return mCompressedBytes; + + return mSize * sizeof(StorageType); +} + + +template +void +TypedAttributeArray::allocate(const size_t size) +{ + assert(!mData); + mData = new StorageType[size]; +} + + +template +void +TypedAttributeArray::deallocate() +{ +#ifndef OPENVDB_2_ABI_COMPATIBLE + // detach from file if delay-loaded + if (this->isOutOfCore()) { + this->setOutOfCore(false); + this->mFileInfo.reset(); + } +#endif + if (mData) { + delete[] mData; + mData = NULL; + } +} + + +template +size_t +TypedAttributeArray::memUsage() const +{ + return sizeof(*this) + (mData != NULL ? this->arrayMemUsage() : 0); +} + + +template +typename TypedAttributeArray::ValueType +TypedAttributeArray::getUnsafe(Index n) const +{ + assert(!this->isCompressed()); + assert(!this->isOutOfCore()); + + ValueType val; + Codec::decode(/*in=*/mData[mIsUniform ? 0 : n], /*out=*/val); + return val; +} + + +template +typename TypedAttributeArray::ValueType +TypedAttributeArray::get(Index n) const +{ + if (this->isCompressed()) const_cast(this)->decompress(); + else if (this->isOutOfCore()) this->doLoad(); + + return this->getUnsafe(n); +} + + +template +template +void +TypedAttributeArray::getUnsafe(Index n, T& val) const +{ + assert(!this->isCompressed()); + assert(!this->isOutOfCore()); + + ValueType tmp; + Codec::decode(/*in=*/mData[mIsUniform ? 0 : n], /*out=*/tmp); + val = static_cast(tmp); +} + + +template +template +void +TypedAttributeArray::get(Index n, T& val) const +{ + if (this->isCompressed()) const_cast(this)->decompress(); + else if (this->isOutOfCore()) this->doLoad(); + + this->getUnsafe(n, val); +} + + +template +typename TypedAttributeArray::ValueType +TypedAttributeArray::getUnsafe(const AttributeArray* array, const Index n) +{ + return static_cast*>(array)->getUnsafe(n); +} + + +template +void +TypedAttributeArray::setUnsafe(Index n, const ValueType& val) +{ + assert(!this->isCompressed()); + assert(!this->isOutOfCore()); + + if (mIsUniform) this->expand(); + + Codec::encode(/*in=*/val, /*out=*/mData[n]); +} + + +template +void +TypedAttributeArray::set(Index n, const ValueType& val) +{ + if (this->isCompressed()) this->decompress(); + else if (this->isOutOfCore()) this->doLoad(); + + this->setUnsafe(n, val); +} + + +template +template +void +TypedAttributeArray::setUnsafe(Index n, const T& val) +{ + assert(!this->isCompressed()); + assert(!this->isOutOfCore()); + + if (mIsUniform) this->expand(); + + const ValueType tmp = static_cast(val); + Codec::encode(/*in=*/tmp, /*out=*/mData[n]); +} + + +template +template +void +TypedAttributeArray::set(Index n, const T& val) +{ + if (this->isCompressed()) this->decompress(); + else if (this->isOutOfCore()) this->doLoad(); + + this->setUnsafe(n, val); +} + + +template +void +TypedAttributeArray::setUnsafe(AttributeArray* array, const Index n, const ValueType& value) +{ + static_cast*>(array)->setUnsafe(n, value); +} + + +template +void +TypedAttributeArray::set(Index n, const AttributeArray& sourceArray, const Index sourceIndex) +{ + const TypedAttributeArray& sourceTypedArray = static_cast(sourceArray); + + ValueType sourceValue; + sourceTypedArray.get(sourceIndex, sourceValue); + + this->set(n, sourceValue); +} + + +template +void +TypedAttributeArray::expand(bool fill) +{ + if (!mIsUniform) return; + + const StorageType val = mData[0]; + + { + tbb::spin_mutex::scoped_lock lock(mMutex); + this->deallocate(); + this->allocate(mSize); + } + + mCompressedBytes = 0; + mIsUniform = false; + + if (fill) { + for (size_t i = 0; i < mSize; ++i) mData[i] = val; + } +} + + +template +bool +TypedAttributeArray::compact() +{ + if (mIsUniform) return true; + + // compaction is not possible if any values are different + const ValueType_ val = this->get(0); + for (size_t i = 1; i < size(); i++) { + if (this->get(i) != val) return false; + } + + this->collapse(this->get(0)); + return true; +} + + +template +void +TypedAttributeArray::collapse() +{ + this->collapse(zeroVal()); +} + + +template +void +TypedAttributeArray::collapse(const ValueType& uniformValue) +{ + if (!mIsUniform) { + tbb::spin_mutex::scoped_lock lock(mMutex); + this->deallocate(); + this->allocate(1); + mIsUniform = true; + } + Codec::encode(uniformValue, mData[0]); +} + + +template +void +TypedAttributeArray::collapse(AttributeArray* array, const ValueType& value) +{ + static_cast*>(array)->collapse(value); +} + + +template +void +TypedAttributeArray::fill(const ValueType& value) +{ + if (this->isOutOfCore()) { + tbb::spin_mutex::scoped_lock lock(mMutex); + this->deallocate(); + this->allocate(mSize); + } + + const size_t size = mIsUniform ? 1 : mSize; + for (size_t i = 0; i < size; ++i) { + Codec::encode(value, mData[i]); + } +} + + +template +void +TypedAttributeArray::fill(AttributeArray* array, const ValueType& value) +{ + static_cast*>(array)->fill(value); +} + + +template +inline bool +TypedAttributeArray::compress() +{ + using attribute_compression::canCompress; + using attribute_compression::compress; + + if (!canCompress()) return false; + + if (!mIsUniform && !this->isCompressed()) { + + tbb::spin_mutex::scoped_lock lock(mMutex); + + this->doLoadUnsafe(); + + const size_t typeSize = sizeof(typename Codec_::StorageType); + const int inBytes = int(mSize * sizeof(StorageType)); + int outBytes; + char* charBuffer = reinterpret_cast(mData); + char* buffer = compress(charBuffer, typeSize, inBytes, outBytes, /*cleanup=*/true); + + if (buffer) { + mData = reinterpret_cast(buffer); + mCompressedBytes = size_t(outBytes); + return true; + } + } + + return false; +} + + +template +inline bool +TypedAttributeArray::decompress() +{ + using attribute_compression::decompress; + using attribute_compression::uncompressedSize; + + tbb::spin_mutex::scoped_lock lock(mMutex); + + if (this->isCompressed()) { + this->doLoadUnsafe(); + char* charBuffer = reinterpret_cast(this->mData); + char* buffer = decompress(charBuffer, uncompressedSize(charBuffer)); + if (buffer) { + mData = reinterpret_cast(buffer); + mCompressedBytes = 0; + return true; + } + } + + return false; +} + + +template +bool +TypedAttributeArray::isOutOfCore() const +{ +#ifndef OPENVDB_2_ABI_COMPATIBLE + return (mFlags & OUTOFCORE); +#else + return false; +#endif +} + + +template +void +TypedAttributeArray::setOutOfCore(const bool b) +{ +#ifndef OPENVDB_2_ABI_COMPATIBLE + if (b) mFlags |= OUTOFCORE; + else mFlags &= ~OUTOFCORE; +#else + (void) b; +#endif +} + + +template +void +TypedAttributeArray::doLoad() const +{ +#ifndef OPENVDB_2_ABI_COMPATIBLE + if (!(this->isOutOfCore())) return; + + TypedAttributeArray* self = const_cast*>(this); + + // This lock will be contended at most once, after which this buffer + // will no longer be out-of-core. + tbb::spin_mutex::scoped_lock lock(self->mMutex); + this->doLoadUnsafe(); +#endif +} + + +template +void +TypedAttributeArray::loadData() const +{ + this->doLoad(); +} + + +template +void +TypedAttributeArray::read(std::istream& is) +{ + using attribute_compression::decompress; + + // read data + + Index64 bytes = Index64(0); + is.read(reinterpret_cast(&bytes), sizeof(Index64)); + bytes = bytes - /*flags*/sizeof(Int16) - /*size*/sizeof(Index64); + + Int16 flags = Int16(0); + is.read(reinterpret_cast(&flags), sizeof(Int16)); + mFlags = flags; + + Index64 size = Index64(0); + is.read(reinterpret_cast(&size), sizeof(Index64)); + mSize = size; + + char* buffer = new char[bytes]; + + // read uniform and compressed state + + mIsUniform = mFlags & WRITEUNIFORM; + mCompressedBytes = mFlags & WRITEMEMCOMPRESS ? bytes : Index64(0); + + // clear uniform and compress flags + + mFlags &= Int16(~WRITEUNIFORM & ~WRITEMEMCOMPRESS); + + tbb::spin_mutex::scoped_lock lock(mMutex); + + this->deallocate(); + +#ifndef OPENVDB_2_ABI_COMPATIBLE + // If this array is being read from a memory-mapped file, delay loading of its data + // until the data is actually accessed. + io::MappedFile::Ptr mappedFile = io::getMappedFilePtr(is); + const bool delayLoad = (mappedFile.get() != NULL); + + if (delayLoad) { + this->setOutOfCore(true); + mFileInfo.reset(new FileInfo); + mFileInfo->bufpos = is.tellg(); + mFileInfo->mapping = mappedFile; + mFileInfo->bytes = bytes; + mFileInfo->meta = io::getStreamMetadataPtr(is); + + // read and discard buffer + is.read(buffer, bytes); + delete[] buffer; + return; + } +#endif + + is.read(buffer, bytes); + + // compressed on-disk + + if (mFlags & WRITEDISKCOMPRESS) { + + // decompress buffer + + const int inBytes = int(mSize * sizeof(StorageType)); + char* newBuffer = decompress(buffer, inBytes, /*cleanup=*/true); + if (newBuffer) buffer = newBuffer; + } + + // set data to buffer + + mData = reinterpret_cast(buffer); + + // clear all write flags + + mFlags &= Int16(~WRITEDISKCOMPRESS); +} + + +template +void +TypedAttributeArray::write(std::ostream& os) const +{ + using attribute_compression::compress; + + if (this->isTransient()) return; + + Int16 flags(mFlags); + Index64 size(mSize); + + boost::scoped_array compressedBuffer; + int compressedBytes = 0; + + this->doLoad(); + + if (mIsUniform) + { + flags |= WRITEUNIFORM; + } + else if (this->isCompressed()) + { + flags |= WRITEMEMCOMPRESS; + } + else if (io::getDataCompression(os) & io::COMPRESS_BLOSC) + { + const char* charBuffer = reinterpret_cast(mData); + const size_t typeSize = sizeof(typename Codec_::StorageType); + const int inBytes = int(mSize * sizeof(StorageType)); + compressedBuffer.reset(compress(charBuffer, typeSize, inBytes, compressedBytes)); + if (compressedBuffer) flags |= WRITEDISKCOMPRESS; + } + + Index64 bytes = /*flags*/ sizeof(Int16) + /*size*/ sizeof(Index64); + + bytes += compressedBuffer ? compressedBytes : this->arrayMemUsage(); + + // write data + + os.write(reinterpret_cast(&bytes), sizeof(Index64)); + os.write(reinterpret_cast(&flags), sizeof(Int16)); + os.write(reinterpret_cast(&size), sizeof(Index64)); + + if (compressedBuffer) os.write(reinterpret_cast(compressedBuffer.get()), compressedBytes); + else os.write(reinterpret_cast(mData), this->arrayMemUsage()); +} + + +template +void +TypedAttributeArray::doLoadUnsafe() const +{ + using attribute_compression::decompress; + +#ifndef OPENVDB_2_ABI_COMPATIBLE + if (!(this->isOutOfCore())) return; + + // this function expects the mutex to already be locked + + TypedAttributeArray* self = const_cast*>(this); + + assert(self->mFileInfo); + assert(self->mFileInfo->mapping.get() != NULL); + + FileInfo& info = *(self->mFileInfo); + + boost::shared_ptr buf = info.mapping->createBuffer(); + std::istream is(buf.get()); + + const Index64 bytes = info.bytes; + + is.seekg(info.bufpos); + + char* buffer = new char[bytes]; + is.read(buffer, bytes); + + // compressed on-disk + + if (mFlags & WRITEDISKCOMPRESS) { + + // decompress buffer + + const int inBytes = int(mSize * sizeof(StorageType)); + char* newBuffer = decompress(buffer, inBytes, /*cleanup=*/true); + if (newBuffer) buffer = newBuffer; + } + + // set data to buffer + + self->mData = reinterpret_cast(buffer); + + // clear write and out-of-core flags + + self->mFlags &= Int16(~WRITEDISKCOMPRESS & ~OUTOFCORE); +#endif +} + + +template +AttributeArray::AccessorBasePtr +TypedAttributeArray::getAccessor() const +{ + // use the faster 'unsafe' get and set methods as attribute handles + // ensure data is uncompressed and in-core when constructed + + return AccessorBasePtr(new AttributeArray::Accessor( + &TypedAttributeArray::getUnsafe, + &TypedAttributeArray::setUnsafe, + &TypedAttributeArray::collapse, + &TypedAttributeArray::fill)); +} + + +template +bool +TypedAttributeArray::isEqual(const AttributeArray& other) const +{ + const TypedAttributeArray* const otherT = dynamic_cast* >(&other); + if(!otherT) return false; + if(this->mSize != otherT->mSize || + this->mIsUniform != otherT->mIsUniform || + *this->sTypeName != *otherT->sTypeName) return false; + + this->doLoad(); + + const StorageType *target = this->mData, *source = otherT->mData; + if (!target && !source) return true; + if (!target || !source) return false; + Index n = this->mIsUniform ? 1 : mSize; + while (n && math::isExactlyEqual(*target++, *source++)) --n; + return n == 0; +} + +//////////////////////////////////////// + +// AttributeHandle implementation + +template +typename AttributeHandle::Ptr +AttributeHandle::create(const AttributeArray& array, const bool preserveCompression) +{ + return typename AttributeHandle::Ptr(new AttributeHandle(array, preserveCompression)); +} + +template +AttributeHandle::AttributeHandle(const AttributeArray& array, const bool preserveCompression) + : mArray(&array) +{ + // load data if delay-loaded + + mArray->loadData(); + + // if array is compressed and preserve compression is true, copy and decompress + // into a local copy that is destroyed with handle to maintain thread-safety + + if (array.isCompressed()) + { + if (preserveCompression) { + mLocalArray = array.copyUncompressed(); + mLocalArray->decompress(); + mArray = mLocalArray.get(); + } + else { + const_cast(mArray)->decompress(); + } + } + + // bind getter and setter methods + + AttributeArray::AccessorBasePtr accessor = mArray->getAccessor(); + assert(accessor); + + AttributeArray::Accessor* typedAccessor = static_cast*>(accessor.get()); + + if (!typedAccessor) { + OPENVDB_THROW(RuntimeError, "Cannot bind AttributeHandle due to mis-matching types."); + } + + mGetter = typedAccessor->mGetter; + mSetter = typedAccessor->mSetter; + mCollapser = typedAccessor->mCollapser; + mFiller = typedAccessor->mFiller; +} + + +template +T AttributeHandle::get(Index n) const +{ + return mGetter(mArray, n); +} + +template +bool AttributeHandle::isUniform() const +{ + return mArray->isUniform(); +} + +//////////////////////////////////////// + +// AttributeWriteHandle implementation + +template +typename AttributeWriteHandle::Ptr +AttributeWriteHandle::create(AttributeArray& array) +{ + return typename AttributeWriteHandle::Ptr(new AttributeWriteHandle(array)); +} + +template +AttributeWriteHandle::AttributeWriteHandle(AttributeArray& array) + : AttributeHandle(array, /*preserveCompression = */ false) { } + +template +void AttributeWriteHandle::set(Index n, const T& value) +{ + this->mSetter(const_cast(this->mArray), n, value); +} + +template +void AttributeWriteHandle::expand(const bool fill) +{ + const_cast(this->mArray)->expand(fill); +} + +template +void AttributeWriteHandle::collapse() +{ + const_cast(this->mArray)->collapse(); +} + +template +bool AttributeWriteHandle::compact() +{ + return const_cast(this->mArray)->compact(); +} + +template +void AttributeWriteHandle::collapse(const T& uniformValue) +{ + this->mCollapser(const_cast(this->mArray), uniformValue); +} + +template +void AttributeWriteHandle::fill(const T& value) +{ + this->mFiller(const_cast(this->mArray), value); +} + +} // namespace tools + +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + + +#endif // OPENVDB_TOOLS_ATTRIBUTE_ARRAY_HAS_BEEN_INCLUDED + + +// Copyright (c) 2015-2016 Double Negative Visual Effects +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) diff --git a/nuparu/include/openvdb/include/openvdb_points/tools/AttributeGroup.h b/nuparu/include/openvdb/include/openvdb_points/tools/AttributeGroup.h new file mode 100644 index 00000000..830be73a --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb_points/tools/AttributeGroup.h @@ -0,0 +1,209 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015-2016 Double Negative Visual Effects +// +// 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 Double Negative Visual Effects 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 AttributeGroup.h +/// +/// @authors Dan Bailey +/// +/// @brief Attribute Group access and filtering for iteration. +/// + + +#ifndef OPENVDB_TOOLS_ATTRIBUTE_GROUP_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_ATTRIBUTE_GROUP_HAS_BEEN_INCLUDED + +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + + +typedef uint8_t GroupType; + + +//////////////////////////////////////// + + +class GroupAttributeArray : public TypedAttributeArray > +{ +public: + /// Default constructor, always constructs a uniform attribute. + explicit GroupAttributeArray( size_t n = 1, + const ValueType& uniformValue = zeroVal()); + /// Deep copy constructor (optionally decompress during copy). + GroupAttributeArray(const GroupAttributeArray& array, + const bool decompress = false); + + /// Cast an AttributeArray to GroupAttributeArray + static GroupAttributeArray& cast(AttributeArray& attributeArray); + + /// Cast an AttributeArray to GroupAttributeArray + static const GroupAttributeArray& cast(const AttributeArray& attributeArray); + + /// Return @c true if the AttributeArray provided is a group + static bool isGroup(const AttributeArray& attributeArray); + + /// @brief Specify whether this attribute is for tracking group membership + /// @note Attributes are not group attributes by default. + void setGroup(bool state); + /// Return @c true if this attribute is for tracking groups + bool isGroup() const { return bool(mFlags & GROUP); } + +}; // class GroupAttributeArray + + +inline GroupAttributeArray& +GroupAttributeArray::cast(AttributeArray& attributeArray) +{ + if (!attributeArray.isType()) { + OPENVDB_THROW(TypeError, "Invalid Attribute Type"); + } + return static_cast(attributeArray); +} + + +inline const GroupAttributeArray& +GroupAttributeArray::cast(const AttributeArray& attributeArray) +{ + if (!attributeArray.isType()) { + OPENVDB_THROW(TypeError, "Invalid Attribute Type"); + } + return static_cast(attributeArray); +} + + +inline bool +GroupAttributeArray::isGroup(const AttributeArray& attributeArray) +{ + if (!attributeArray.isType()) return false; + + return GroupAttributeArray::cast(attributeArray).isGroup(); +} + + +//////////////////////////////////////// + + +class GroupHandle +{ +public: + // Dummy class that distinguishes an offset from a bitmask on construction + struct BitMask { }; + + typedef std::pair GroupIndex; + + GroupHandle(const GroupAttributeArray& array, const GroupType& offset); + GroupHandle(const GroupAttributeArray& array, const GroupType& bitMask, BitMask); + + size_t size() const { return mArray.size(); } + bool isUniform() const { return mArray.isUniform(); } + + bool get(Index n) const; + +protected: + const GroupAttributeArray& mArray; + const GroupType mBitMask; +}; // class GroupHandle + + +//////////////////////////////////////// + + +class GroupWriteHandle : public GroupHandle +{ +public: + + GroupWriteHandle(GroupAttributeArray& array, const GroupType& offset); + + void set(Index n, bool on); + + /// @brief Set membership for the whole array and attempt to collapse + /// + /// @param on True or false for inclusion in group + /// + /// @note This method guarantees that all attributes will have group membership + /// changed according to the input bool, however compaction will not be performed + /// if other groups that share the same underlying array are non-uniform. + /// The return value indicates if the group array ends up being uniform. + bool collapse(bool on); + +}; // class GroupWriteHandle + + +//////////////////////////////////////// + + +/// Index filtering on group membership +class GroupFilter +{ +public: + struct Data + { + Data(const Name& _attribute) + : attribute(_attribute) { } + const Name attribute; + }; + + GroupFilter(const GroupHandle& handle) + : mHandle(handle) { } + + template + static GroupFilter create(const LeafT& leaf, const Data& data) { + return GroupFilter(leaf.groupHandle(data.attribute)); + } + + template + bool valid(const IterT& iter) const { + return mHandle.get(*iter); + } + +private: + const GroupHandle mHandle; +}; // class GroupFilter + + +//////////////////////////////////////// + + +} // namespace tools + +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + + +#endif // OPENVDB_TOOLS_ATTRIBUTE_GROUP_HAS_BEEN_INCLUDED + + +// Copyright (c) 2015-2016 Double Negative Visual Effects +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) diff --git a/nuparu/include/openvdb/include/openvdb_points/tools/AttributeSet.h b/nuparu/include/openvdb/include/openvdb_points/tools/AttributeSet.h new file mode 100644 index 00000000..3bb5aafd --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb_points/tools/AttributeSet.h @@ -0,0 +1,405 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015-2016 Double Negative Visual Effects +// +// 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 Double Negative Visual Effects 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 AttributeSet.h +/// +/// @authors Dan Bailey, Mihai Alden, Peter Cucka +/// +/// @brief Set of Attribute Arrays which tracks metadata about each array. +/// + + +#ifndef OPENVDB_TOOLS_ATTRIBUTE_SET_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_ATTRIBUTE_SET_HAS_BEEN_INCLUDED + +#include +#include + +#include // integer_traits +#include // shared_ptr + +#include + +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + + +//////////////////////////////////////// + + +/// Ordered collection of uniquely-named attribute arrays +class AttributeSet +{ +public: + enum { INVALID_POS = boost::integer_traits::const_max }; + + typedef boost::shared_ptr Ptr; + typedef boost::shared_ptr ConstPtr; + + class Descriptor; + + typedef boost::shared_ptr DescriptorPtr; + typedef boost::shared_ptr DescriptorConstPtr; + + ////////// + + struct Util + { + /// Attribute and type name pair. + struct NameAndType { + NameAndType(const std::string& n, const NamePair& t) + : name(n), type(t) {} + Name name; + NamePair type; + }; + + typedef std::vector NameAndTypeVec; + typedef std::map NameToPosMap; + typedef std::pair GroupIndex; + }; + + ////////// + + AttributeSet(); + + /// Construct from the given descriptor + explicit AttributeSet(const DescriptorPtr&, size_t arrayLength = 1); + + /// Shallow copy constructor, the descriptor and attribute arrays will be shared. + AttributeSet(const AttributeSet&); + + //@{ + /// @brief Return a reference to this attribute set's descriptor, which might + /// be shared with other sets. + Descriptor& descriptor() { return *mDescr; } + const Descriptor& descriptor() const { return *mDescr; } + //@} + + /// @brief Return a pointer to this attribute set's descriptor, which might be + /// shared with other sets + DescriptorPtr descriptorPtr() const { return mDescr; } + + /// Return the number of attributes in this set. + size_t size() const { return mAttrs.size(); } + + /// Return the number of attributes with this flag set + size_t size(const uint16_t flag) const; + + /// Return the number of bytes of memory used by this attribute set. + size_t memUsage() const; + + /// @brief Return the position of the attribute array whose name is @a name, + /// or @c INVALID_POS if no match is found. + size_t find(const std::string& name) const; + + /// @brief Replace the attribute array whose name is @a name. + /// @return The position of the updated attribute array or @c INVALID_POS + /// if the given name does not exist or if the replacement failed because + /// the new array type does not comply with the descriptor. + size_t replace(const std::string& name, const AttributeArray::Ptr&); + + /// @brief Replace the attribute array stored at position @a pos in this container. + /// @return The position of the updated attribute array or @c INVALID_POS + /// if replacement failed because the new array type does not comply with + /// the descriptor. + size_t replace(size_t pos, const AttributeArray::Ptr&); + + //@{ + /// @brief Return a pointer to the attribute array whose name is @a name or + /// a null pointer if no match is found. + const AttributeArray* getConst(const std::string& name) const; + const AttributeArray* get(const std::string& name) const; + AttributeArray* get(const std::string& name); + //@} + + //@{ + /// @brief Return a pointer to the attribute array stored at position @a pos + /// in this set. + const AttributeArray* getConst(size_t pos) const; + const AttributeArray* get(size_t pos) const; + AttributeArray* get(size_t pos); + //@} + + //@{ + /// @brief Return the group offset from the name or index of the group + /// A group attribute array is a single byte (8-bit), each bit of which + /// can denote a group. The group offset is the position of the bit that + /// denotes the requested group if all group attribute arrays in the set + /// (and only attribute arrays marked as group) were to be laid out linearly + /// according to their order in the set. + size_t groupOffset(const Name& groupName) const; + size_t groupOffset(const Util::GroupIndex& index) const; + //@} + + /// Return the group index from the name of the group + Util::GroupIndex groupIndex(const Name& groupName) const; + /// Return the group index from the offset of the group + /// @note see offset description for groupOffset() + Util::GroupIndex groupIndex(const size_t offset) const; + + /// Create an iterator for iterating through point indices + IndexIter beginIndex() const; + + /// Return true if the attribute array stored at position @a pos is shared. + bool isShared(size_t pos) const; + /// @brief If the attribute array stored at position @a pos is shared, + /// replace the array with a deep copy of itself that is not + /// shared with anyone else. + void makeUnique(size_t pos); + + /// Append attribute @a attribute (simple method) + AttributeArray::Ptr appendAttribute(const Util::NameAndType& attribute, + Metadata::Ptr defaultValue = Metadata::Ptr()); + + /// Append attribute @a attribute (descriptor-sharing) + /// Requires current descriptor to match @a expected + /// On append, current descriptor is replaced with @a replacement + AttributeArray::Ptr appendAttribute(const Util::NameAndType& attribute, + const Descriptor& expected, DescriptorPtr& replacement); + + /// Drop attributes with @a pos indices (simple method) + /// Creates a new descriptor for this attribute set + void dropAttributes(const std::vector& pos); + + /// Drop attributes with @a pos indices (descriptor-sharing method) + /// Requires current descriptor to match @a expected + /// On drop, current descriptor is replaced with @a replacement + void dropAttributes(const std::vector& pos, + const Descriptor& expected, DescriptorPtr& replacement); + + /// Re order attribute set to match a provided descriptor + /// Replaces own descriptor with @a replacement + void reorderAttributes(const DescriptorPtr& replacement); + + /// Re-name attributes in set to match a provided descriptor + /// Replaces own descriptor with @a replacement + void renameAttributes(const Descriptor& expected, DescriptorPtr& replacement); + + /// Read the entire set from a stream. + void read(std::istream&); + /// Write the entire set to a stream. + void write(std::ostream&) const; + + /// This will read the attribute descriptor from a stream, but no attribute data. + void readMetadata(std::istream&); + /// This will write the attribute descriptor to a stream, but no attribute data. + void writeMetadata(std::ostream&) const; + + /// Read attribute data from a stream. + void readAttributes(std::istream&); + /// Write attribute data to a stream. + void writeAttributes(std::ostream&) const; + + /// Compare the descriptors and attribute arrays on the attribute sets + /// Exit early if the descriptors do not match + bool operator==(const AttributeSet& other) const; + bool operator!=(const AttributeSet& other) const { return !this->operator==(other); } + +private: + /// Disallow assignment, since it wouldn't be obvious whether the copy is deep or shallow. + AttributeSet& operator=(const AttributeSet&); + + typedef std::vector AttrArrayVec; + + DescriptorPtr mDescr; + AttrArrayVec mAttrs; +}; // class AttributeSet + +//////////////////////////////////////// + + +/// @brief An immutable object that stores name, type and AttributeSet position +/// for a constant collection of attribute arrays. +/// @note The attribute name is actually mutable, but the attribute type +/// and position can not be changed after creation. +class AttributeSet::Descriptor +{ +public: + typedef boost::shared_ptr Ptr; + + typedef Util::NameAndType NameAndType; + typedef Util::NameAndTypeVec NameAndTypeVec; + typedef Util::GroupIndex GroupIndex; + typedef Util::NameToPosMap NameToPosMap; + typedef NameToPosMap::const_iterator ConstIterator; + + /// Utility method to construct a NameAndType sequence. + struct Inserter { + NameAndTypeVec vec; + Inserter& add(const NameAndType& nameAndType) { + vec.push_back(nameAndType); return *this; + } + Inserter& add(const Name& name, const NamePair& type) { + vec.push_back(NameAndType(name, type)); return *this; + } + Inserter& add(const NameAndTypeVec& other) { + for (NameAndTypeVec::const_iterator it = other.begin(), itEnd = other.end(); it != itEnd; ++it) { + vec.push_back(NameAndType(it->name, it->type)); + } + return *this; + } + }; + + ////////// + + Descriptor(); + + /// Copy constructor + Descriptor(const Descriptor&); + + /// Create a new descriptor from the given attribute and type name pairs. + static Ptr create(const NameAndTypeVec&); + + /// Create a new descriptor from the given attribute and type name pairs + /// and copy the group maps and metamap. + static Ptr create(const NameAndTypeVec&, const NameToPosMap&, const MetaMap&); + + /// Create a new descriptor from a position attribute type and assumes "P" (for convenience). + static Ptr create(const NamePair&); + + Ptr duplicateAppend(const NameAndType& attribute) const; + Ptr duplicateAppend(const NameAndTypeVec& vec) const; + Ptr duplicateDrop(const std::vector& pos) const; + + /// Return the number of attributes in this descriptor. + size_t size() const { return mTypes.size(); } + + /// Return the number of bytes of memory used by this attribute set. + size_t memUsage() const; + + /// @brief Return the position of the attribute array whose name is @a name, + /// or @c INVALID_POS if no match is found. + size_t find(const std::string& name) const; + + /// Rename an attribute array + size_t rename(const std::string& fromName, const std::string& toName); + + /// Return the name of the attribute array's type. + const Name& valueType(size_t pos) const; + /// Return the name of the attribute array's type. + const NamePair& type(size_t pos) const; + + /// Retrieve metadata map + MetaMap& getMetadata(); + const MetaMap& getMetadata() const; + + /// Return true if the attribute has a default value + bool hasDefaultValue(const Name& name) const; + /// Get a default value for an existing attribute + template + ValueType getDefaultValue(const Name& name) const; + /// Set a default value for an existing attribute + void setDefaultValue(const Name& name, const Metadata& defaultValue); + // Remove the default value if it exists + void removeDefaultValue(const Name& name); + // Prune any default values for which the key is no longer present + void pruneUnusedDefaultValues(); + + /// Return true if this descriptor is equal to the given one. + bool operator==(const Descriptor&) const; + /// Return true if this descriptor is not equal to the given one. + bool operator!=(const Descriptor& rhs) const { return !this->operator==(rhs); } + /// Return true if this descriptor contains the same attributes + /// as the given descriptor, ignoring attribute order + bool hasSameAttributes(const Descriptor& rhs) const; + + /// Return a reference to the name-to-position map. + const NameToPosMap& map() const { return mNameMap; } + /// Return a reference to the name-to-position group map. + const NameToPosMap& groupMap() const { return mGroupMap; } + + /// Append to a vector of names and types from this Descriptor in position order + void appendTo(NameAndTypeVec& attrs) const; + + /// Return @c true if group exists + bool hasGroup(const Name& group) const; + /// Define a group name to offset mapping + void setGroup(const Name& group, const size_t offset); + /// Drop any mapping keyed by group name + void dropGroup(const Name& group); + /// Clear all groups + void clearGroups(); + + /// Return a unique name for an attribute array based on given name + const Name uniqueName(const Name& name) const; + + /// Serialize this descriptor to the given stream. + void write(std::ostream&) const; + /// Unserialize this transform from the given stream. + void read(std::istream&); + +private: + size_t insert(const std::string& name, const NamePair& typeName); + NameToPosMap mNameMap; + std::vector mTypes; + NameToPosMap mGroupMap; + MetaMap mMetadata; +}; // class Descriptor + + +template +ValueType +AttributeSet::Descriptor::getDefaultValue(const Name& name) const +{ + typedef typename TypedMetadata::ConstPtr MetadataPtr; + + const size_t pos = find(name); + if (pos == INVALID_POS) { + OPENVDB_THROW(LookupError, "Cannot find attribute name to set default value.") + } + + std::stringstream ss; + ss << "default:" << name; + + MetadataPtr metadata = mMetadata.getMetadata >(ss.str()); + + if (metadata) return metadata->value(); + + return zeroVal(); +} + + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + + +#endif // OPENVDB_TOOLS_ATTRIBUTE_ARRAY_HAS_BEEN_INCLUDED + + +// Copyright (c) 2015-2016 Double Negative Visual Effects +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) + diff --git a/nuparu/include/openvdb/include/openvdb_points/tools/IndexFilter.h b/nuparu/include/openvdb/include/openvdb_points/tools/IndexFilter.h new file mode 100644 index 00000000..4e45a5c6 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb_points/tools/IndexFilter.h @@ -0,0 +1,161 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015-2016 Double Negative Visual Effects +// +// 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 Double Negative Visual Effects 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 IndexFilter.h +/// +/// @authors Dan Bailey +/// +/// @brief Index filters primarily designed to be used with a FilterIndexIter. +/// + + +#ifndef OPENVDB_TOOLS_INDEX_FILTER_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_INDEX_FILTER_HAS_BEEN_INCLUDED + +#include +#include + +#include + +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + + +//////////////////////////////////////// + + +// Random index filtering per leaf +template +class RandomLeafFilter +{ +public: + typedef std::map LeafSeedMap; + typedef boost::uniform_01 Distribution; + typedef typename Distribution::result_type ResultT; + + struct Data + { + Data(const ResultT _factor, const LeafSeedMap& _leafSeedMap) + : factor(_factor), leafSeedMap(_leafSeedMap) { } + const ResultT factor; + const LeafSeedMap& leafSeedMap; + }; + + RandomLeafFilter(const Data& data, const unsigned int seed) + : mData(data) + , mDistribution(RandGenT(seed)) { } + + inline ResultT next() const { + return const_cast&>(mDistribution)(); + } + + template + static RandomLeafFilter create(const LeafT& leaf, const Data& data) { + const LeafSeedMap::const_iterator it = data.leafSeedMap.find(leaf.origin()); + if (it == data.leafSeedMap.end()) { + OPENVDB_THROW(openvdb::KeyError, "Cannot find leaf origin in offset map for random filter"); + } + return RandomLeafFilter(data, (unsigned int) it->second); + } + + template + bool valid(const IterT&) const { + return next() < mData.factor; + } + +private: + const Data mData; + Distribution mDistribution; +}; // class RandomLeafFilter + + +// BBox index filtering +class BBoxFilter +{ +public: + struct Data + { + Data(const openvdb::math::Transform& _transform, + const openvdb::BBoxd& _bboxWS) + : transform(_transform) + , bbox(transform.worldToIndex(_bboxWS)) { } + const openvdb::math::Transform transform; + const openvdb::BBoxd bbox; + }; + + BBoxFilter( const Data& data, + const AttributeHandle::Ptr& positionHandle) + : mData(data) + , mPositionHandle(positionHandle) { } + + template + static BBoxFilter create(const LeafT& leaf, const Data& data) { + return BBoxFilter(data, AttributeHandle::create(leaf.constAttributeArray("P"))); + } + + template + bool valid(const IterT& iter) const { + const openvdb::Coord ijk = iter.getCoord(); + const openvdb::Vec3f voxelIndexSpace = ijk.asVec3d(); + + // Retrieve point position in voxel space + const openvdb::Vec3f& pointVoxelSpace = mPositionHandle->get(*iter); + + // Compute point position in index space + const openvdb::Vec3f pointIndexSpace = pointVoxelSpace + voxelIndexSpace; + + return mData.bbox.isInside(pointIndexSpace); + } + +private: + const Data mData; + const AttributeHandle::Ptr mPositionHandle; +}; // class BBoxFilter + + +//////////////////////////////////////// + + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + + +#endif // OPENVDB_TOOLS_INDEX_FILTER_HAS_BEEN_INCLUDED + + +// Copyright (c) 2015-2016 Double Negative Visual Effects +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) diff --git a/nuparu/include/openvdb/include/openvdb_points/tools/IndexIterator.h b/nuparu/include/openvdb/include/openvdb_points/tools/IndexIterator.h new file mode 100644 index 00000000..06d70fe5 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb_points/tools/IndexIterator.h @@ -0,0 +1,347 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015-2016 Double Negative Visual Effects +// +// 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 Double Negative Visual Effects 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 IndexIterator.h +/// +/// @authors Dan Bailey +/// +/// @brief Index Iterators. +/// + + +#ifndef OPENVDB_TOOLS_INDEX_ITERATOR_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_INDEX_ITERATOR_HAS_BEEN_INCLUDED + +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + + +/// @brief Count up the number of times the iterator can iterate +/// +/// @param iter the iterator. +/// +/// @note counting by iteration only performed where a dynamic filter is in use, +template +inline Index64 iterCount(const IterT& iter); + + +//////////////////////////////////////// + + +/// @brief A forward iterator over array indices +class IndexIter +{ +public: + IndexIter() + : mEnd(0), mItem(0) {} + IndexIter(Index32 item, Index32 end) + : mEnd(end), mItem(item) {} + IndexIter(const IndexIter& other) + : mEnd(other.mEnd), mItem(other.mItem) { } + + inline Index32 end() const { return mEnd; } + + /// @brief Reset the begining and end of the iterator. + inline void reset(Index32 item, Index32 end) { + mItem = item; + mEnd = end; + } + + /// @brief Returns the item to which this iterator is currently pointing. + inline Index32 operator*() { return mItem; } + inline Index32 operator*() const { return mItem; } + + /// @brief Return @c true if this iterator is not yet exhausted. + inline operator bool() const { return mItem < mEnd; } + inline bool test() const { return mItem < mEnd; } + + /// @brief Advance to the next (valid) item (prefix). + inline IndexIter& operator++() { + ++mItem; + return *this; + } + + /// @brief Advance to the next (valid) item (postfix). + inline IndexIter operator++(int /*dummy*/) { + IndexIter newIterator(*this); + this->operator++(); + return newIterator; + } + + /// @brief Advance to the next (valid) item. + inline bool next() { this->operator++(); return this->test(); } + inline bool increment() { this->next(); return this->test(); } + + /// Throw an error as Coord methods are not available on this iterator + inline Coord getCoord() const { OPENVDB_THROW(RuntimeError, "IndexIter does not provide a valid Coord, use a ValueIndexIter instead."); } + /// Throw an error as Coord methods are not available on this iterator + inline void getCoord(Coord&) const { OPENVDB_THROW(RuntimeError, "IndexIter does not provide a valid Coord, use a ValueIndexIter instead."); } + + /// @brief Equality operators + inline bool operator==(const IndexIter& other) const { return mItem == other.mItem; } + inline bool operator!=(const IndexIter& other) const { return !this->operator==(other); } + +private: + Index32 mEnd, mItem; +}; // class IndexIter + + +/// @brief A forward iterator over array indices from a value iterator (such as ValueOnCIter) +template +class ValueIndexIter +{ +public: + ValueIndexIter(ValueIterT& iter) + : mIndexIter(), mIter(iter), mParent(mIter.parent()) + { + if (mIter) { + Index32 start = mIter.offset() > 0 ? Index32(mParent.getValue(mIter.offset() - 1)) : Index32(0); + mIndexIter.reset(start, *mIter); + if (!mIndexIter.test()) this->operator++(); + } + } + ValueIndexIter(const ValueIndexIter& other) + : mIndexIter(other.mIndexIter), mIter(other.mIter), mParent(other.mParent) { } + + inline Index32 end() const { return mIndexIter.end(); } + + inline void reset(Index32 item, Index32 end) { + mIndexIter.reset(item, end); + } + + /// @brief Returns the item to which this iterator is currently pointing. + inline Index32 operator*() { return *mIndexIter; } + inline Index32 operator*() const { return *mIndexIter; } + + /// @brief Return @c true if this iterator is not yet exhausted. + inline operator bool() const { return mIter; } + inline bool test() const { return mIter; } + + /// @brief Advance to the next (valid) item (prefix). + inline ValueIndexIter& operator++() { + mIndexIter.next(); + while (!mIndexIter.test() && mIter.next()) { + mIndexIter.reset(mParent.getValue(mIter.offset() - 1), *mIter); + } + return *this; + } + + /// @brief Advance to the next (valid) item (postfix). + inline ValueIndexIter operator++(int /*dummy*/) { + IndexIter newIterator(*this); + this->operator++(); + return newIterator; + } + + /// @brief Advance to the next (valid) item. + inline bool next() { this->operator++(); return this->test(); } + inline bool increment() { this->next(); return this->test(); } + + /// Return the coordinates of the item to which the value iterator is pointing. + inline Coord getCoord() const { return mIter.getCoord(); } + /// Return in @a xyz the coordinates of the item to which the value iterator is pointing. + inline void getCoord(Coord& xyz) const { xyz = mIter.getCoord(); } + + /// Return the const index iterator + inline const IndexIter& indexIter() const { return mIndexIter; } + /// Return the const value iterator + inline const ValueIterT& valueIter() const { return mIter; } + + /// @brief Equality operators + bool operator==(const ValueIndexIter& other) const { return *mIndexIter == *other.mIndexIter; } + bool operator!=(const ValueIndexIter& other) const { return !this->operator==(other); } + +private: + IndexIter mIndexIter; + ValueIterT mIter; + const typename ValueIterT::NodeType& mParent; +}; // ValueIndexIter + + +/// IndexIterTraits provides the following for iterators of the three value +/// types, i.e., for {Value}{On,Off,All}{CIter}: +/// - a begin(leaf) function that returns an index iterator or an index value +/// iterator for the leaf provided, +/// eg IndexIterTraits::begin(leaf) returns +/// leaf.beginIndexOn() +/// - an Iterator typedef that aliases to the index iterator for this value type +template struct IndexIterTraits; + +template +struct IndexIterTraits { + typedef IndexIter Iterator; + static Iterator begin(const typename TreeT::LeafNodeType& leaf) { + return Iterator(leaf.beginIndexAll()); + } +}; + +template +struct IndexIterTraits { + typedef typename TreeT::LeafNodeType::IndexOnIter Iterator; + static Iterator begin(const typename TreeT::LeafNodeType& leaf) { + return Iterator(leaf.beginIndexOn()); + } +}; + +template +struct IndexIterTraits { + typedef typename TreeT::LeafNodeType::IndexOffIter Iterator; + static Iterator begin(const typename TreeT::LeafNodeType& leaf) { + return Iterator(leaf.beginIndexOff()); + } +}; + + +/// @brief A forward iterator over array indices with filtering +/// IteratorT can be either IndexIter or ValueIndexIter (or some custom index iterator) +/// FilterT should be a struct or class with a valid() method than can be evaluated per index +/// Here's a simple filter example that only accepts even indices: +/// +/// struct EvenIndexFilter +/// { +/// bool valid(const Index32 offset) const { +/// return (offset % 2) == 0; +/// } +/// }; +/// +template +class FilterIndexIter +{ +public: + FilterIndexIter(const IteratorT& iterator, const FilterT& filter) + : mIterator(iterator), mFilter(filter) { if (mIterator) { this->reset(*mIterator, mIterator.end()); } } + FilterIndexIter(const FilterIndexIter& other) + : mIterator(other.mIterator), mFilter(other.mFilter) { } + + Index32 end() const { return mIterator.end(); } + + /// @brief Reset the begining and end of the iterator. + void reset(Index32 begin, Index32 end) { + mIterator.reset(begin, end); + while (mIterator.test() && !mFilter.template valid(mIterator)) { + ++mIterator; + } + } + + /// @brief Returns the item to which this iterator is currently pointing. + Index32 operator*() { return *mIterator; } + Index32 operator*() const { return *mIterator; } + + /// @brief Return @c true if this iterator is not yet exhausted. + operator bool() const { return mIterator.test(); } + bool test() const { return mIterator.test(); } + + /// @brief Advance to the next (valid) item (prefix). + FilterIndexIter& operator++() { + while (true) { + ++mIterator; + if (!mIterator.test() || mFilter.template valid(mIterator)) { + break; + } + } + return *this; + } + + /// @brief Advance to the next (valid) item (postfix). + FilterIndexIter operator++(int /*dummy*/) { + FilterIndexIter newIterator(*this); + this->operator++(); + return newIterator; + } + + /// @brief Advance to the next (valid) item. + bool next() { this->operator++(); return this->test(); } + bool increment() { this->next(); return this->test(); } + + /// Return the const index iterator + inline const IteratorT& indexIter() const { return mIterator; } + /// Return the const filter + inline const FilterT& filter() const { return mFilter; } + + /// @brief Equality operators + bool operator==(const FilterIndexIter& other) const { return mIterator == other.mIterator; } + bool operator!=(const FilterIndexIter& other) const { return !this->operator==(other); } + +private: + IteratorT mIterator; + const FilterT mFilter; +}; // class FilterIndexIter + + +//////////////////////////////////////// + + +template +inline Index64 iterCount(const IterT& iter) +{ + Index64 size = 0; + for (IterT newIter(iter); newIter; ++newIter, ++size) { } + return size; +} + + +template <> +inline Index64 iterCount(const IndexIter& iter) +{ + return iter ? iter.end() - *iter : 0; +} + + +template +inline Index64 iterCount(const ValueIndexIter& iter) +{ + T newIter(iter.valueIter()); + Index64 size = 0; + for ( ; newIter; ++newIter) { + size += *newIter - (newIter.offset() == 0 ? Index32(0) : Index32(newIter.parent().getValue(newIter.offset() - 1))); + } + return size; +} + + +//////////////////////////////////////// + + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + + +#endif // OPENVDB_TOOLS_INDEX_ITERATOR_HAS_BEEN_INCLUDED + + +// Copyright (c) 2015-2016 Double Negative Visual Effects +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) diff --git a/nuparu/include/openvdb/include/openvdb_points/tools/PointAttribute.h b/nuparu/include/openvdb/include/openvdb_points/tools/PointAttribute.h new file mode 100644 index 00000000..5506b6d3 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb_points/tools/PointAttribute.h @@ -0,0 +1,545 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015-2016 Double Negative Visual Effects +// +// 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 Double Negative Visual Effects 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 Dan Bailey +/// +/// @file PointAttribute.h +/// +/// @brief Point attribute manipulation in a VDB Point Grid. +/// + + +#ifndef OPENVDB_TOOLS_POINT_ATTRIBUTE_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_POINT_ATTRIBUTE_HAS_BEEN_INCLUDED + +#include + +#include +#include +#include + + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Appends a new attribute to the VDB tree. +/// +/// @param tree the PointDataTree to be appended to. +/// @param newAttribute name and type for the new attribute. +/// @param defaultValue metadata default attribute value +/// @param hidden mark attribute as hidden +/// @param transient mark attribute as transient +/// @param group mark attribute as group +template +inline void appendAttribute(PointDataTree& tree, + const AttributeSet::Util::NameAndType& newAttribute, + Metadata::Ptr defaultValue = Metadata::Ptr(), + const bool hidden = false, + const bool transient = false, + const bool group = false); + +/// @brief Drops attributes from the VDB tree. +/// +/// @param tree the PointDataTree to be dropped from. +/// @param indices indices of the attributes to drop. +template +inline void dropAttributes( PointDataTree& tree, + const std::vector& indices); + +/// @brief Drops attributes from the VDB tree. +/// +/// @param tree the PointDataTree to be dropped from. +/// @param names names of the attributes to drop. +template +inline void dropAttributes( PointDataTree& tree, + const std::vector& names); + +/// @brief Drop one attribute from the VDB tree (convenience method). +/// +/// @param tree the PointDataTree to be dropped from. +/// @param index index of the attribute to drop. +template +inline void dropAttribute( PointDataTree& tree, + const size_t& index); + +/// @brief Drop one attribute from the VDB tree (convenience method). +/// +/// @param tree the PointDataTree to be dropped from. +/// @param name name of the attribute to drop. +template +inline void dropAttribute( PointDataTree& tree, + const Name& name); + +/// @brief Rename attributes in a VDB tree. +/// +/// @param tree the PointDataTree. +/// @param oldNames a list of old attribute names to rename from. +/// @param newNames a list of new attribute names to rename to. +/// +/// @note Number of oldNames must match the number of newNames. +/// +/// @note Duplicate names and renaming group attributes are not allowed. +template +inline void renameAttributes(PointDataTree& tree, + const std::vector& oldNames, + const std::vector& newNames); + +/// @brief Rename an attribute in a VDB tree. +/// +/// @param tree the PointDataTree. +/// @param oldName the old attribute name to rename from. +/// @param newName the new attribute name to rename to. +/// +/// @note newName must not already exist and must not be a group attribute. +template +inline void renameAttribute(PointDataTree& tree, + const Name& oldName, + const Name& newName); + +/// @brief Compact attributes in a VDB tree (if possible). +/// +/// @param tree the PointDataTree. +template +inline void compactAttributes(PointDataTree& tree); + +/// @brief Apply Blosc compression to one attribute in the VDB tree. +/// +/// @param tree the PointDataTree. +/// @param name name of the attribute to compress. +template +inline void bloscCompressAttribute( PointDataTree& tree, + const Name& name); + +//////////////////////////////////////// + + +namespace point_attribute_internal { + +template +struct AppendAttributeOp { + + typedef typename tree::LeafManager LeafManagerT; + typedef typename LeafManagerT::LeafRange LeafRangeT; + typedef AttributeSet::Descriptor::NameAndType NameAndType; + + AppendAttributeOp( PointDataTreeType& tree, + const NameAndType& newAttribute, + AttributeSet::DescriptorPtr& descriptor, + const bool hidden = false, + const bool transient = false, + const bool group = false) + : mTree(tree) + , mNewAttribute(newAttribute) + , mDescriptor(descriptor) + , mHidden(hidden) + , mTransient(transient) + , mGroup(group) { } + + void operator()(const LeafRangeT& range) const { + + for (typename LeafRangeT::Iterator leaf=range.begin(); leaf; ++leaf) { + + const AttributeSet::Descriptor& expected = leaf->attributeSet().descriptor(); + + AttributeArray::Ptr attribute = leaf->appendAttribute(mNewAttribute, expected, mDescriptor); + + if (mHidden) attribute->setHidden(true); + if (mTransient) attribute->setTransient(true); + + if (mGroup) { + GroupAttributeArray::cast(*attribute).setGroup(true); + } + } + } + + ////////// + + PointDataTreeType& mTree; + const NameAndType& mNewAttribute; + AttributeSet::DescriptorPtr& mDescriptor; + const bool mHidden; + const bool mTransient; + const bool mGroup; +}; // class AppendAttributeOp + + +//////////////////////////////////////// + + +template +struct DropAttributesOp { + + typedef typename tree::LeafManager LeafManagerT; + typedef typename LeafManagerT::LeafRange LeafRangeT; + typedef std::vector Indices; + + DropAttributesOp( PointDataTreeType& tree, + const Indices& indices, + AttributeSet::DescriptorPtr& descriptor) + : mTree(tree) + , mIndices(indices) + , mDescriptor(descriptor) { } + + void operator()(const LeafRangeT& range) const { + + for (typename LeafRangeT::Iterator leaf=range.begin(); leaf; ++leaf) { + + const AttributeSet::Descriptor& expected = leaf->attributeSet().descriptor(); + + leaf->dropAttributes(mIndices, expected, mDescriptor); + } + } + + ////////// + + PointDataTreeType& mTree; + const Indices& mIndices; + AttributeSet::DescriptorPtr& mDescriptor; +}; // class DropAttributesOp + + +//////////////////////////////////////// + + +template +struct CompactAttributesOp { + + typedef typename tree::LeafManager LeafManagerT; + typedef typename LeafManagerT::LeafRange LeafRangeT; + + CompactAttributesOp() { } + + void operator()(const LeafRangeT& range) const { + for (typename LeafRangeT::Iterator leaf=range.begin(); leaf; ++leaf) { + leaf->compactAttributes(); + } + } +}; // class CompactAttributesOp + + +//////////////////////////////////////// + + +template +struct BloscCompressAttributesOp { + + typedef typename tree::LeafManager LeafManagerT; + typedef typename LeafManagerT::LeafRange LeafRangeT; + typedef std::vector Indices; + + BloscCompressAttributesOp( PointDataTreeType& tree, + const Indices& indices) + : mTree(tree) + , mIndices(indices) { } + + void operator()(const LeafRangeT& range) const { + + for (typename LeafRangeT::Iterator leaf=range.begin(); leaf; ++leaf) { + + for (Indices::const_iterator it = mIndices.begin(), + itEnd = mIndices.end(); it != itEnd; ++it) { + + AttributeArray& array = leaf->attributeArray(*it); + array.compress(); + } + } + } + + ////////// + + PointDataTreeType& mTree; + const Indices& mIndices; +}; // class BloscCompressAttributesOp + + +} // namespace point_attribute_internal + + +//////////////////////////////////////// + + +template +inline void appendAttribute(PointDataTree& tree, + const AttributeSet::Util::NameAndType& newAttribute, + Metadata::Ptr defaultValue, + const bool hidden, const bool transient, const bool group) +{ + typedef AttributeSet::Util::NameAndTypeVec NameAndTypeVec; + typedef AttributeSet::Descriptor Descriptor; + + using point_attribute_internal::AppendAttributeOp; + + typename PointDataTree::LeafCIter iter = tree.cbeginLeaf(); + + if (!iter) return; + + // do not append a non-unique attribute + + const Descriptor& descriptor = iter->attributeSet().descriptor(); + const size_t index = descriptor.find(newAttribute.name); + + if (index != AttributeSet::INVALID_POS) { + OPENVDB_THROW(KeyError, "Cannot append an attribute with a non-unique name - " << newAttribute.name << "."); + } + + // create a new attribute descriptor + NameAndTypeVec vec; + vec.push_back(newAttribute); + + Descriptor::Ptr newDescriptor = descriptor.duplicateAppend(vec); + + // store the attribute default value in the descriptor metadata + + if (defaultValue) { + newDescriptor->setDefaultValue(newAttribute.name, *defaultValue); + } + + // insert attributes using the new descriptor + + AppendAttributeOp append(tree, newAttribute, newDescriptor, hidden, transient, group); + tbb::parallel_for(typename tree::template LeafManager(tree).leafRange(), append); +} + + +//////////////////////////////////////// + + +template +inline void dropAttributes( PointDataTree& tree, + const std::vector& indices) +{ + typedef typename tree::LeafManager LeafManagerT; + typedef AttributeSet::Descriptor Descriptor; + + using point_attribute_internal::DropAttributesOp; + + typename PointDataTree::LeafCIter iter = tree.cbeginLeaf(); + + if (!iter) return; + + const Descriptor& descriptor = iter->attributeSet().descriptor(); + + // throw if position index present in the indices as this attribute is mandatory + + const size_t positionIndex = descriptor.find("P"); + if (positionIndex!= AttributeSet::INVALID_POS && + std::find(indices.begin(), indices.end(), positionIndex) != indices.end()) { + OPENVDB_THROW(KeyError, "Cannot drop mandatory position attribute."); + } + + // insert attributes using the new descriptor + + Descriptor::Ptr newDescriptor = descriptor.duplicateDrop(indices); + tbb::parallel_for(LeafManagerT(tree).leafRange(), DropAttributesOp(tree, indices, newDescriptor)); +} + + +//////////////////////////////////////// + + +template +inline void dropAttributes( PointDataTree& tree, + const std::vector& names) +{ + typename PointDataTree::LeafCIter iter = tree.cbeginLeaf(); + + if (!iter) return; + + const AttributeSet& attributeSet = iter->attributeSet(); + const AttributeSet::Descriptor& descriptor = attributeSet.descriptor(); + + std::vector indices; + + for (std::vector::const_iterator it = names.begin(), itEnd = names.end(); it != itEnd; ++it) { + const size_t index = descriptor.find(*it); + + // do not attempt to drop an attribute that does not exist + if (index == AttributeSet::INVALID_POS) { + OPENVDB_THROW(KeyError, "Cannot drop an attribute that does not exist - " << *it << "."); + } + + indices.push_back(index); + } + + dropAttributes(tree, indices); +} + + +//////////////////////////////////////// + + +template +inline void dropAttribute( PointDataTree& tree, + const size_t& index) +{ + std::vector indices; + indices.push_back(index); + dropAttributes(tree, indices); +} + + +template +inline void dropAttribute( PointDataTree& tree, + const Name& name) +{ + std::vector names; + names.push_back(name); + dropAttributes(tree, names); +} + + +//////////////////////////////////////// + + +template +inline void renameAttributes( PointDataTree& tree, + const std::vector& oldNames, + const std::vector& newNames) +{ + if (oldNames.size() != newNames.size()) { + OPENVDB_THROW(ValueError, "Mis-matching sizes of name vectors, cannot rename attributes."); + } + + typedef AttributeSet::Descriptor Descriptor; + + typename PointDataTree::LeafIter iter = tree.beginLeaf(); + + if (!iter) return; + + const AttributeSet& attributeSet = iter->attributeSet(); + const Descriptor& descriptor = attributeSet.descriptor(); + AttributeSet::DescriptorPtr newDescriptor(new Descriptor(descriptor)); + + for (size_t i = 0; i < oldNames.size(); i++) { + const Name oldName(oldNames[i]); + if (descriptor.find(oldName) == AttributeSet::INVALID_POS) { + OPENVDB_THROW(KeyError, "Cannot find requested attribute - " << oldName << "."); + } + + const Name newName(newNames[i]); + if (descriptor.find(newName) != AttributeSet::INVALID_POS) { + OPENVDB_THROW(KeyError, "Cannot rename attribute as new name already exists - " << newName << "."); + } + + const AttributeArray* array = attributeSet.getConst(oldName); + assert(array); + + if (GroupAttributeArray::isGroup(*array)) { + OPENVDB_THROW(KeyError, "Cannot rename group attribute - " << oldName << "."); + } + + newDescriptor->rename(oldName, newName); + } + + for (; iter; ++iter) { + iter->renameAttributes(descriptor, newDescriptor); + } +} + + +template +inline void renameAttribute(PointDataTree& tree, + const Name& oldName, + const Name& newName) +{ + std::vector oldNames; + std::vector newNames; + oldNames.push_back(oldName); + newNames.push_back(newName); + renameAttributes(tree, oldNames, newNames); +} + + +//////////////////////////////////////// + + +template +inline void compactAttributes(PointDataTree& tree) +{ + typedef typename tree::LeafManager LeafManagerT; + + using point_attribute_internal::CompactAttributesOp; + + typename PointDataTree::LeafIter iter = tree.beginLeaf(); + if (!iter) return; + + tbb::parallel_for(LeafManagerT(tree).leafRange(), CompactAttributesOp()); +} + + +//////////////////////////////////////// + + +template +inline void bloscCompressAttribute( PointDataTree& tree, + const Name& name) +{ + using point_attribute_internal::BloscCompressAttributesOp; + + typedef typename tree::LeafManager LeafManagerT; + typedef AttributeSet::Descriptor Descriptor; + + typename PointDataTree::LeafCIter iter = tree.cbeginLeaf(); + + if (!iter) return; + + const Descriptor& descriptor = iter->attributeSet().descriptor(); + + // throw if index cannot be found in descriptor + + const size_t index = descriptor.find(name); + if (index == AttributeSet::INVALID_POS) { + OPENVDB_THROW(KeyError, "Cannot find requested attribute - " << name << "."); + } + + // blosc compress attributes + + std::vector indices; + indices.push_back(index); + + tbb::parallel_for(LeafManagerT(tree).leafRange(), BloscCompressAttributesOp(tree, indices)); +} + +//////////////////////////////////////// + + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + + +#endif // OPENVDB_TOOLS_POINT_ATTRIBUTE_HAS_BEEN_INCLUDED + + +// Copyright (c) 2015-2016 Double Negative Visual Effects +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) diff --git a/nuparu/include/openvdb/include/openvdb_points/tools/PointConversion.h b/nuparu/include/openvdb/include/openvdb_points/tools/PointConversion.h new file mode 100644 index 00000000..5593919d --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb_points/tools/PointConversion.h @@ -0,0 +1,414 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015-2016 Double Negative Visual Effects +// +// 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 Double Negative Visual Effects 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 Dan Bailey +/// +/// @file PointConversion.h +/// +/// @brief Convert existing points and attributes into VDB Point Data grids and attributes. +/// + + +#ifndef OPENVDB_TOOLS_POINT_CONVERSION_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_POINT_CONVERSION_HAS_BEEN_INCLUDED + +#include + +#include + +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + + +/// @brief Localises points with position into a @c PointDataGrid into two stages: +/// allocation of the leaf attribute data and population of the positions. +/// +/// @param pointIndexGrid a PointIndexGrid into the points. +/// @param positions list of world space point positions. +/// @param positionType the type of the position (includes compression info). +/// @param xform world to index space transform. +/// @param positionDefaultValue metadata default position value +/// +/// @note The position data must be supplied in a Point-Partitioner compatible +/// data structure. A convenience PointAttributeVector class is offered. +/// +/// @note The position data is populated separately to perform world space to +/// voxel space conversion and apply quantisation. +/// +/// @note A @c PointIndexGrid to the points must be supplied to perform this +/// operation. Typically this is built implicitly by the PointDataGrid constructor. + +template +inline typename PointDataGridT::Ptr +createPointDataGrid(const PointIndexGridT& pointIndexGrid, const PositionArrayT& positions, + const openvdb::NamePair& positionType, const math::Transform& xform, + Metadata::Ptr positionDefaultValue = Metadata::Ptr()); + + +/// @brief Convenience method to create a @c PointDataGrid from a std::vector of +/// point positions. +/// +/// @param positions list of world space point positions. +/// @param positionType the type of the position (includes compression info). +/// @param xform world to index space transform. +/// @param positionDefaultValue metadata default position value +/// +/// @note This method implicitly wraps the std::vector for a Point-Partitioner compatible +/// data structure and creates the required @c PointIndexGrid to the points. + +template +inline typename PointDataGridT::Ptr +createPointDataGrid(const std::vector& positions, + const openvdb::NamePair& positionType, const math::Transform& xform, + Metadata::Ptr positionDefaultValue = Metadata::Ptr()); + + +/// @brief Stores point attribute data in an existing @c PointDataGrid attribute. +/// +/// @param tree the PointDataGrid to be populated. +/// @param pointIndexTree a PointIndexTree into the points. +/// @param attributeName the name of the VDB Points attribute to be populated. +/// @param data a wrapper to the attribute data. +/// +/// @note A @c PointIndexGrid to the points must be supplied to perform this +/// operation. This is required to ensure the same point index ordering. + +template +inline void +populateAttribute( PointDataTreeT& tree, const PointIndexTreeT& pointIndexTree, + const openvdb::Name& attributeName, const PointArrayT& data); + + +//////////////////////////////////////// + + +/// @brief Point-partitioner compatible STL vector attribute wrapper for convenience +template +class PointAttributeVector { +public: + typedef ValueType PosType; + typedef ValueType value_type; + + PointAttributeVector(const std::vector& data) + : mData(data) { } + + size_t size() const { return mData.size(); } + void getPos(size_t n, ValueType& xyz) const { xyz = mData[n]; } + + template + void get(size_t n, T& value) const { value = mData[n]; } + +private: + const std::vector& mData; +}; // PointAttributeVector + + +//////////////////////////////////////// + + +namespace point_conversion_internal { + +template +struct InitialiseAttributesOp { + + typedef typename tree::LeafManager LeafManagerT; + typedef typename LeafManagerT::LeafRange LeafRangeT; + + typedef typename PointIndexTreeType::LeafNodeType PointIndexLeafNode; + typedef typename PointIndexLeafNode::IndexArray IndexArray; + + InitialiseAttributesOp( PointDataTreeType& tree, + const PointIndexTreeType& pointIndexTree, + const AttributeSet::Descriptor::Ptr& attributeDescriptor) + : mTree(tree) + , mPointIndexTree(pointIndexTree) + , mAttributeDescriptor(attributeDescriptor) { } + + void operator()(const typename LeafManagerT::LeafRange& range) const { + for (typename LeafManagerT::LeafRange::Iterator leaf=range.begin(); leaf; ++leaf) { + + // obtain the PointIndexLeafNode (using the origin of the current leaf) + + const PointIndexLeafNode* pointIndexLeaf = mPointIndexTree.probeConstLeaf(leaf->origin()); + + if (!pointIndexLeaf) continue; + + // initialise the attribute storage + + pointIndexLeaf->indices(); + + const IndexArray& indices = pointIndexLeaf->indices(); + + Index64 pointCount = indices.size(); + + leaf->initializeAttributes(mAttributeDescriptor, pointCount); + } + } + + ////////// + + const PointDataTreeType& mTree; + const PointIndexTreeType& mPointIndexTree; + const AttributeSet::Descriptor::Ptr& mAttributeDescriptor; +}; + +template< typename PointDataTreeType, + typename PointIndexTreeType, + typename PositionListType> +struct PopulatePositionAttributeOp { + + typedef typename tree::LeafManager LeafManagerT; + typedef typename LeafManagerT::LeafRange LeafRangeT; + + typedef typename PointIndexTreeType::LeafNodeType PointIndexLeafNode; + typedef typename PointIndexLeafNode::IndexArray IndexArray; + + typedef typename PositionListType::value_type ValueType; + + PopulatePositionAttributeOp(const PointIndexTreeType& pointIndexTree, + const math::Transform& transform, + const PositionListType& positions) + : mPointIndexTree(pointIndexTree) + , mTransform(transform) + , mPositions(positions) { } + + void operator()(const typename LeafManagerT::LeafRange& range) const { + + for (typename LeafManagerT::LeafRange::Iterator leaf=range.begin(); leaf; ++leaf) { + + // obtain the PointIndexLeafNode (using the origin of the current leaf) + + const PointIndexLeafNode* pointIndexLeaf = mPointIndexTree.probeConstLeaf(leaf->origin()); + + if (!pointIndexLeaf) continue; + + typename AttributeWriteHandle::Ptr attributeWriteHandle = + AttributeWriteHandle::create(leaf->template attributeArray("P")); + + Index64 index = 0; + + const IndexArray& indices = pointIndexLeaf->indices(); + + for (typename IndexArray::const_iterator it = indices.begin(), it_end = indices.end(); it != it_end; ++it) + { + ValueType positionWorldSpace; + mPositions.getPos(*it, positionWorldSpace); + + const ValueType positionIndexSpace = mTransform.worldToIndex(positionWorldSpace); + + const ValueType positionVoxelSpace = ValueType( + positionIndexSpace.x() - math::Round(positionIndexSpace.x()), + positionIndexSpace.y() - math::Round(positionIndexSpace.y()), + positionIndexSpace.z() - math::Round(positionIndexSpace.z())); + + attributeWriteHandle->set(index, Vec3f(positionVoxelSpace)); + + index++; + } + } + } + + ////////// + + const PointIndexTreeType& mPointIndexTree; + const math::Transform& mTransform; + const PositionListType& mPositions; +}; + +template< typename PointDataTreeType, + typename PointIndexTreeType, + typename AttributeListType> +struct PopulateAttributeOp { + + typedef typename tree::LeafManager LeafManagerT; + typedef typename LeafManagerT::LeafRange LeafRangeT; + + typedef typename PointIndexTreeType::LeafNodeType PointIndexLeafNode; + typedef typename PointIndexLeafNode::IndexArray IndexArray; + + typedef typename AttributeListType::value_type ValueType; + + PopulateAttributeOp(const PointIndexTreeType& pointIndexTree, + const AttributeListType& data, + const openvdb::Name& attributeName) + : mPointIndexTree(pointIndexTree) + , mData(data) + , mAttributeName(attributeName) { } + + void operator()(const typename LeafManagerT::LeafRange& range) const { + + for (typename LeafManagerT::LeafRange::Iterator leaf=range.begin(); leaf; ++leaf) { + + // obtain the PointIndexLeafNode (using the origin of the current leaf) + + const PointIndexLeafNode* pointIndexLeaf = mPointIndexTree.probeConstLeaf(leaf->origin()); + + if (!pointIndexLeaf) continue; + + typename AttributeWriteHandle::Ptr attributeWriteHandle = + AttributeWriteHandle::create(leaf->attributeArray(mAttributeName)); + + Index64 index = 0; + + const IndexArray& indices = pointIndexLeaf->indices(); + + for (typename IndexArray::const_iterator it = indices.begin(), it_end = indices.end(); it != it_end; ++it) + { + ValueType value; + mData.template get(*it, value); + + attributeWriteHandle->set(index, value); + + index++; + } + } + } + + ////////// + + const PointIndexTreeType& mPointIndexTree; + const AttributeListType& mData; + const openvdb::Name& mAttributeName; +}; + +} // namespace point_conversion_internal + + +//////////////////////////////////////// + + +template +inline typename PointDataGridT::Ptr +createPointDataGrid(const PointIndexGridT& pointIndexGrid, const PositionArrayT& positions, + const openvdb::NamePair& positionType, const math::Transform& xform, + Metadata::Ptr positionDefaultValue) +{ + typedef typename PointDataGridT::TreeType PointDataTreeT; + typedef typename PointIndexGridT::TreeType PointIndexTreeT; + typedef typename tree::template LeafManager LeafManagerT; + typedef typename LeafManagerT::LeafRange LeafRangeT; + + using point_conversion_internal::InitialiseAttributesOp; + using point_conversion_internal::PopulatePositionAttributeOp; + + // construct the Tree using a topology copy of the PointIndexGrid + + const PointIndexTreeT& pointIndexTree(pointIndexGrid.tree()); + typename PointDataTreeT::Ptr treePtr(new PointDataTreeT(pointIndexTree)); + + LeafManagerT leafManager = LeafManagerT(*treePtr); + LeafRangeT leafRange = leafManager.leafRange(); + + // create attribute descriptor from position type + + AttributeSet::Descriptor::Ptr descriptor = AttributeSet::Descriptor::create(positionType); + + // add default value for position if provided + + if (positionDefaultValue) descriptor->setDefaultValue("P", *positionDefaultValue); + + // create point attribute storage on each leaf + + InitialiseAttributesOp initialise( + *treePtr, pointIndexGrid.tree(), descriptor); + tbb::parallel_for(leafRange, initialise); + + // populate position attribute + + PopulatePositionAttributeOp populate(pointIndexTree, + xform, + positions); + + tbb::parallel_for(leafRange, populate); + + typename PointDataGridT::Ptr grid = PointDataGridT::create(treePtr); + grid->setTransform(xform.copy()); + return grid; +} + + +//////////////////////////////////////// + + +template +inline typename PointDataGridT::Ptr +createPointDataGrid(const std::vector& positions, + const openvdb::NamePair& positionType, + const math::Transform& xform, + Metadata::Ptr positionDefaultValue) +{ + const PointAttributeVector pointList(positions); + + PointIndexGrid::Ptr pointIndexGrid = createPointIndexGrid(pointList, xform); + return createPointDataGrid(*pointIndexGrid, pointList, positionType, xform, positionDefaultValue); +} + + +//////////////////////////////////////// + + +template +inline void +populateAttribute( PointDataTreeT& tree, const PointIndexTreeT& pointIndexTree, + const openvdb::Name& attributeName, const PointArrayT& data) +{ + using point_conversion_internal::PopulateAttributeOp; + + // populate attribute + + PopulateAttributeOp populate(pointIndexTree, data, attributeName); + + tbb::parallel_for(typename tree::template LeafManager(tree).leafRange(), populate); +} + + +//////////////////////////////////////// + + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + + +#endif // OPENVDB_TOOLS_POINT_CONVERSION_HAS_BEEN_INCLUDED + + +// Copyright (c) 2015-2016 Double Negative Visual Effects +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) diff --git a/nuparu/include/openvdb/include/openvdb_points/tools/PointCount.h b/nuparu/include/openvdb/include/openvdb_points/tools/PointCount.h new file mode 100644 index 00000000..a3ada00e --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb_points/tools/PointCount.h @@ -0,0 +1,277 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015-2016 Double Negative Visual Effects +// +// 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 Double Negative Visual Effects 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 Dan Bailey +/// +/// @file PointCount.h +/// +/// @brief Various point counting methods using a VDB Point Grid. +/// + + +#ifndef OPENVDB_TOOLS_POINT_COUNT_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_POINT_COUNT_HAS_BEEN_INCLUDED + +#include + +#include +#include +#include + +#include + +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + + +/// @brief Total points in the PointDataTree +/// @param tree PointDataTree. +/// @param inCoreOnly if true, points in out-of-core leaf nodes are not counted +template +Index64 pointCount(const PointDataTreeT& tree, const bool inCoreOnly = false); + + +/// @brief Total active points in the PointDataTree +/// @param tree PointDataTree. +/// @param inCoreOnly if true, points in out-of-core leaf nodes are not counted +template +Index64 activePointCount(const PointDataTreeT& tree, const bool inCoreOnly = false); + + +/// @brief Total inactive points in the PointDataTree +/// @param tree PointDataTree. +/// @param inCoreOnly if true, points in out-of-core leaf nodes are not counted +template +Index64 inactivePointCount(const PointDataTreeT& tree, const bool inCoreOnly = false); + + +/// @brief Total points in the group in the PointDataTree +/// @param tree PointDataTree. +/// @param name group name. +/// @param inCoreOnly if true, points in out-of-core leaf nodes are not counted +template +Index64 groupPointCount(const PointDataTreeT& tree, const Name& name, const bool inCoreOnly = false); + + +/// @brief Total active points in the group in the PointDataTree +/// @param tree PointDataTree. +/// @param name group name. +/// @param inCoreOnly if true, points in out-of-core leaf nodes are not counted +template +Index64 activeGroupPointCount(const PointDataTreeT& tree, const Name& name, const bool inCoreOnly = false); + + +/// @brief Total inactive points in the group in the PointDataTree +/// @param tree PointDataTree. +/// @param name group name. +/// @param inCoreOnly if true, points in out-of-core leaf nodes are not counted +template +Index64 inactiveGroupPointCount(const PointDataTreeT& tree, const Name& name, const bool inCoreOnly = false); + + +//////////////////////////////////////// + + +namespace point_count_internal { + +template < typename PointDataTreeT, + typename ValueIterT, + typename FilterT> +struct PointCountOp +{ + typedef typename tree::LeafManager LeafManagerT; + typedef IndexIterTraits IndexIteratorFromLeafT; + typedef typename IndexIteratorFromLeafT::Iterator IndexIterator; + typedef typename FilterT::Data FilterDataT; + typedef FilterIndexIter Iterator; + + PointCountOp(const FilterDataT& filterData, + const bool inCoreOnly = false) + : mFilterData(filterData) + , mInCoreOnly(inCoreOnly) { } + + Index64 operator()(const typename LeafManagerT::LeafRange& range, Index64 size) const { + + for (typename LeafManagerT::LeafRange::Iterator leaf = range.begin(); leaf; ++leaf) { +#ifndef OPENVDB_2_ABI_COMPATIBLE + if (mInCoreOnly && leaf->buffer().isOutOfCore()) continue; +#endif + IndexIterator indexIterator(IndexIteratorFromLeafT::begin(*leaf)); + FilterT filter(FilterT::create(*leaf, mFilterData)); + Iterator iter(indexIterator, filter); + size += iterCount(iter); + } + + return size; + } + + static Index64 join(Index64 size1, Index64 size2) { + return size1 + size2; + } + +private: + const FilterDataT& mFilterData; + const bool mInCoreOnly; +}; // struct PointCountOp + + +template +Index64 threadedFilterPointCount( const PointDataTreeT& tree, + const typename FilterT::Data& filter, + const bool inCoreOnly = false) +{ + typedef point_count_internal::PointCountOp< PointDataTreeT, ValueIterT, FilterT> PointCountOp; + + typename tree::LeafManager leafManager(tree); + const PointCountOp pointCountOp(filter, inCoreOnly); + return tbb::parallel_reduce(leafManager.leafRange(), Index64(0), pointCountOp, PointCountOp::join); +} + + +template +Index64 filterPointCount(const PointDataTreeT& tree, + const typename FilterT::Data& filter, + const bool inCoreOnly = false) +{ + typedef typename PointDataTreeT::LeafNodeType::ValueAllCIter ValueIterT; + return threadedFilterPointCount< PointDataTreeT, FilterT, ValueIterT>(tree, filter, inCoreOnly); +} + + +template +Index64 filterActivePointCount( const PointDataTreeT& tree, + const typename FilterT::Data& filter, + const bool inCoreOnly = false) +{ + typedef typename PointDataTreeT::LeafNodeType::ValueOnCIter ValueIterT; + return threadedFilterPointCount< PointDataTreeT, FilterT, ValueIterT>(tree, filter, inCoreOnly); +} + + +template +Index64 filterInactivePointCount( const PointDataTreeT& tree, + const typename FilterT::Data& filter, + const bool inCoreOnly = false) +{ + typedef typename PointDataTreeT::LeafNodeType::ValueOffCIter ValueIterT; + return threadedFilterPointCount< PointDataTreeT, FilterT, ValueIterT>(tree, filter, inCoreOnly); +} + + +} // namespace point_count_internal + + +template +Index64 pointCount(const PointDataTreeT& tree, const bool inCoreOnly) +{ + (void) inCoreOnly; + Index64 size = 0; + for (typename PointDataTreeT::LeafCIter iter = tree.cbeginLeaf(); iter; ++iter) { +#ifndef OPENVDB_2_ABI_COMPATIBLE + if (inCoreOnly && iter->buffer().isOutOfCore()) continue; +#endif + size += iter->pointCount(); + } + return size; +} + + +template +Index64 activePointCount(const PointDataTreeT& tree, const bool inCoreOnly) +{ + (void) inCoreOnly; + Index64 size = 0; + for (typename PointDataTreeT::LeafCIter iter = tree.cbeginLeaf(); iter; ++iter) { +#ifndef OPENVDB_2_ABI_COMPATIBLE + if (inCoreOnly && iter->buffer().isOutOfCore()) continue; +#endif + size += iter->onPointCount(); + } + return size; +} + + +template +Index64 inactivePointCount(const PointDataTreeT& tree, const bool inCoreOnly) +{ + (void) inCoreOnly; + Index64 size = 0; + for (typename PointDataTreeT::LeafCIter iter = tree.cbeginLeaf(); iter; ++iter) { +#ifndef OPENVDB_2_ABI_COMPATIBLE + if (inCoreOnly && iter->buffer().isOutOfCore()) continue; +#endif + size += iter->offPointCount(); + } + return size; +} + + +template +Index64 groupPointCount(const PointDataTreeT& tree, const Name& name, const bool inCoreOnly) +{ + GroupFilter::Data groupFilterData(name); + return point_count_internal::filterPointCount(tree, groupFilterData, inCoreOnly); +} + + +template +Index64 activeGroupPointCount(const PointDataTreeT& tree, const Name& name, const bool inCoreOnly) +{ + GroupFilter::Data groupFilterData(name); + return point_count_internal::filterActivePointCount(tree, groupFilterData, inCoreOnly); +} + + +template +Index64 inactiveGroupPointCount(const PointDataTreeT& tree, const Name& name, const bool inCoreOnly) +{ + GroupFilter::Data groupFilterData(name); + return point_count_internal::filterInactivePointCount(tree, groupFilterData, inCoreOnly); +} + + +//////////////////////////////////////// + + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + + +#endif // OPENVDB_TOOLS_POINT_COUNT_HAS_BEEN_INCLUDED + + +// Copyright (c) 2015-2016 Double Negative Visual Effects +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) diff --git a/nuparu/include/openvdb/include/openvdb_points/tools/PointDataGrid.h b/nuparu/include/openvdb/include/openvdb_points/tools/PointDataGrid.h new file mode 100644 index 00000000..2a691cfd --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb_points/tools/PointDataGrid.h @@ -0,0 +1,920 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015-2016 Double Negative Visual Effects +// +// 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 Double Negative Visual Effects 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 Dan Bailey, Nick Avramoussis, Matt Warner +/// +/// @file PointDataGrid.h +/// +/// @brief Attribute-owned data structure for points. Point attributes are +/// stored in leaf nodes and ordered by voxel for fast random and +/// sequential access. +/// + + +#ifndef OPENVDB_TOOLS_POINT_DATA_GRID_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_POINT_DATA_GRID_HAS_BEEN_INCLUDED + +#include +#include +#include + +#include + +#include +#include + +#include // std::pair, std::make_pair + + +class TestPointDataLeaf; + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + +// forward declaration +namespace tree { + template struct SameLeafConfig; +} + + +//////////////////////////////////////// + + +namespace tools { + + +// forward declaration +template class PointDataLeafNode; + +/// @brief Point index tree configured to match the default VDB configurations. +typedef tree::Tree, 4>, 5> > > PointDataTree; + + +/// @brief Point data grid. +typedef Grid PointDataGrid; + + +//////////////////////////////////////// + +// Internal utility methods +namespace point_data_grid_internal { + +template +struct UniquePtr +{ +#ifdef OPENVDB_HAS_CXX11 + typedef std::unique_ptr type; +#else + typedef std::auto_ptr type; +#endif +}; +} + + +template +class PointDataLeafNode : public tree::LeafNode { + +public: + typedef PointDataLeafNode LeafNodeType; + typedef boost::shared_ptr Ptr; + + typedef T ValueType; + typedef std::pair ValueTypePair; + typedef std::vector IndexArray; + + typedef AttributeSet::Descriptor Descriptor; + + //////////////////////////////////////// + + // The following methods had to be copied from the LeafNode class + // to make the derived PointDataLeafNode class compatible with the tree structure. + + typedef tree::LeafNode BaseLeaf; + typedef util::NodeMask NodeMaskType; + + using BaseLeaf::LOG2DIM; + using BaseLeaf::TOTAL; + using BaseLeaf::DIM; + using BaseLeaf::NUM_VALUES; + using BaseLeaf::NUM_VOXELS; + using BaseLeaf::SIZE; + using BaseLeaf::LEVEL; + + /// Default constructor + PointDataLeafNode() + : BaseLeaf() + , mAttributeSet(new AttributeSet) { } + + ~PointDataLeafNode() { } + + /// Construct using deep copy of other PointDataLeafNode + explicit PointDataLeafNode(const PointDataLeafNode& other) + : BaseLeaf(other) + , mAttributeSet(new AttributeSet(*other.mAttributeSet)) { } + + /// Construct using supplied origin, value and active status + explicit + PointDataLeafNode(const Coord& coords, const T& value = zeroVal(), bool active = false) + : BaseLeaf(coords, value, active) + , mAttributeSet(new AttributeSet) { } + + /// Construct using supplied origin, value and active status + /// use attribute map from another PointDataLeafNode + PointDataLeafNode(const PointDataLeafNode& other, const Coord& coords, const T& value = zeroVal(), bool active = false) + : BaseLeaf(coords, value, active) + , mAttributeSet(new AttributeSet(*other.mAttributeSet)) { } + + // Copy-construct from a PointIndexLeafNode with the same configuration but a different ValueType. + template + PointDataLeafNode(const tools::PointIndexLeafNode& other) + : BaseLeaf(other) + , mAttributeSet(new AttributeSet) { } + + // Copy-construct from a LeafNode with the same configuration but a different ValueType. + // Used for topology copies - explicitly sets the value (background) to zeroVal + template + PointDataLeafNode(const tree::LeafNode& other, const T& /*value*/, TopologyCopy) + : BaseLeaf(other, zeroVal(), TopologyCopy()) + , mAttributeSet(new AttributeSet) { } + + // Copy-construct from a LeafNode with the same configuration but a different ValueType. + // Used for topology copies - explicitly sets the on and off value (background) to zeroVal + template + PointDataLeafNode(const tree::LeafNode& other, const T& /*offValue*/, const T& /*onValue*/, TopologyCopy) + : BaseLeaf(other, zeroVal(), zeroVal(), TopologyCopy()) + , mAttributeSet(new AttributeSet) { } + +#ifndef OPENVDB_2_ABI_COMPATIBLE + PointDataLeafNode(PartialCreate, const Coord& coords, + const T& value = zeroVal(), bool active = false) + : BaseLeaf(PartialCreate(), coords, value, active) + , mAttributeSet(new AttributeSet) { } +#endif + +public: + + /// Retrieve the attribute set. + const AttributeSet& attributeSet() const { return *mAttributeSet; } + + /// @brief Create a new attribute set. Existing attributes will be removed. + void initializeAttributes(const Descriptor::Ptr& descriptor, const size_t arrayLength); + /// @brief Clear the attribute set. + void clearAttributes(const bool updateValueMask = true); + + /// @brief Returns @c true if an attribute with this index exists. + /// @param pos Index of the attribute + bool hasAttribute(const size_t pos) const; + /// @brief Returns @c true if an attribute with this name exists. + /// @param attributeName Name of the attribute + bool hasAttribute(const Name& attributeName) const; + + /// @brief Append an attribute to the leaf. + /// @param attribute Name and type of the attribute to append. + /// @param expected Existing descriptor is expected to match this parameter. + /// @param replacement New descriptor to replace the existing one. + AttributeArray::Ptr appendAttribute(const AttributeSet::Util::NameAndType& attribute, + const Descriptor& expected, Descriptor::Ptr& replacement); + /// @brief Drop list of attributes. + /// @param pos vector of attribute indices to drop + /// @param expected Existing descriptor is expected to match this parameter. + /// @param replacement New descriptor to replace the existing one. + void dropAttributes(const std::vector& pos, + const Descriptor& expected, Descriptor::Ptr& replacement); + /// @brief Reorder attribute set. + /// @param replacement New descriptor to replace the existing one. + void reorderAttributes(const Descriptor::Ptr& replacement); + /// @brief Rename attributes in attribute set (order must remain the same). + /// @param expected Existing descriptor is expected to match this parameter. + /// @param replacement New descriptor to replace the existing one. + void renameAttributes(const Descriptor& expected, Descriptor::Ptr& replacement); + /// @brief Compact all attributes in attribute set. + void compactAttributes(); + + /// @brief Swap the underlying attribute set with the given @a attributeSet. + /// This leaf will assume ownership of the given attribute set. The descriptors must + /// match and the voxel offsets values will need updating if the point order is different. + void swap(AttributeSet* attributeSet); + + /// @brief Sets all of the voxel offset values on this leaf, from the given vector + /// of @a offsets. If @a updateValueMask is true, then the active value mask will + /// be updated so voxels with points are active and empty voxels are inactive. + void setOffsets(const std::vector& offsets, const bool updateValueMask = true); + + /// @brief Throws an error if the voxel values on this leaf are not monotonically + /// increasing or within the bounds of the attribute arrays + void validateOffsets() const; + + /// @brief Read-write attribute array reference from index + /// { + AttributeArray& attributeArray(const size_t pos); + const AttributeArray& attributeArray(const size_t pos) const; + const AttributeArray& constAttributeArray(const size_t pos) const; + /// } + /// @brief Read-write attribute array reference from name + /// { + AttributeArray& attributeArray(const Name& attributeName); + const AttributeArray& attributeArray(const Name& attributeName) const; + const AttributeArray& constAttributeArray(const Name& attributeName) const; + /// } + + /// @brief Read-only group handle from group index + GroupHandle groupHandle(const AttributeSet::Descriptor::GroupIndex& index) const; + /// @brief Read-only group handle from group name + GroupHandle groupHandle(const Name& group) const; + /// @brief Read-write group handle from group index + GroupWriteHandle groupWriteHandle(const AttributeSet::Descriptor::GroupIndex& index); + /// @brief Read-write group handle from group name + GroupWriteHandle groupWriteHandle(const Name& name); + + /// @brief Compute the total point count for the leaf + Index64 pointCount() const; + /// @brief Compute the total active (on) point count for the leaf + Index64 onPointCount() const; + /// @brief Compute the total inactive (off) point count for the leaf + Index64 offPointCount() const; + /// @brief Compute the point count in a specific group for the leaf + Index64 groupPointCount(const Name& groupName) const; + + /// @brief Activate voxels with non-zero points, deactivate voxels with zero points. + void updateValueMask(); + + //////////////////////////////////////// + + void setOffsetOn(Index offset, const ValueType& val); + void setOffsetOnly(Index offset, const ValueType& val); + + /// @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 + bool hasSameTopology(const PointDataLeafNode* other) const { + return BaseLeaf::hasSameTopology(other); + } + + /// Check for buffer, state and origin equivalence first. + /// If this returns true, do a deeper comparison on the attribute set to check + bool operator==(const PointDataLeafNode& other) const { + if(BaseLeaf::operator==(other) != true) return false; + return (*this->mAttributeSet == *other.mAttributeSet); + } + + bool operator!=(const PointDataLeafNode& other) const { return !(other == *this); } + + void addLeaf(PointDataLeafNode*) {} + template + void addLeafAndCache(PointDataLeafNode*, AccessorT&) {} + + //@{ + /// @brief Return a pointer to this node. + PointDataLeafNode* touchLeaf(const Coord&) { return this; } + template + PointDataLeafNode* touchLeafAndCache(const Coord&, AccessorT&) { return this; } + + template + NodeT* probeNodeAndCache(const Coord&, AccessorT&) + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (!(boost::is_same::value)) return NULL; + return reinterpret_cast(this); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + PointDataLeafNode* probeLeaf(const Coord&) { return this; } + template + PointDataLeafNode* probeLeafAndCache(const Coord&, AccessorT&) { return this; } + //@} + + //@{ + /// @brief Return a @const pointer to this node. + const PointDataLeafNode* probeConstLeaf(const Coord&) const { return this; } + template + const PointDataLeafNode* probeConstLeafAndCache(const Coord&, AccessorT&) const { return this; } + template + const PointDataLeafNode* probeLeafAndCache(const Coord&, AccessorT&) const { return this; } + const PointDataLeafNode* probeLeaf(const Coord&) const { return this; } + template + const NodeT* probeConstNodeAndCache(const Coord&, AccessorT&) const + { + OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN + if (!(boost::is_same::value)) return NULL; + return reinterpret_cast(this); + OPENVDB_NO_UNREACHABLE_CODE_WARNING_END + } + //@} + + // I/O methods + + void readTopology(std::istream& is, bool fromHalf = false); + void writeTopology(std::ostream& os, bool toHalf = false) const; + + void readBuffers(std::istream& is, bool fromHalf = false); + void readBuffers(std::istream& is, const CoordBBox&, bool fromHalf = false); + void writeBuffers(std::ostream& os, bool toHalf = false) const; + + + Index64 memUsage() const; + + //////////////////////////////////////// + + // Disable all write methods to avoid unintentional changes + // to the point-array offsets. + + void assertNonmodifiable() { + assert(false && "Cannot modify voxel values in a PointDataTree."); + } + + void setActiveState(const Coord& xyz, bool on) { BaseLeaf::setActiveState(xyz, on); } + void setActiveState(Index offset, bool on) { BaseLeaf::setActiveState(offset, on); } + + void setValueOnly(const Coord&, const ValueType&) { assertNonmodifiable(); } + void setValueOnly(Index, const ValueType&) { assertNonmodifiable(); } + + void setValueOff(const Coord& xyz) { BaseLeaf::setValueOff(xyz); } + void setValueOff(Index offset) { BaseLeaf::setValueOff(offset); } + + void setValueOff(const Coord&, const ValueType&) { assertNonmodifiable(); } + void setValueOff(Index, const ValueType&) { assertNonmodifiable(); } + + void setValueOn(const Coord& xyz) { BaseLeaf::setValueOn(xyz); } + void setValueOn(Index offset) { BaseLeaf::setValueOn(offset); } + + void setValueOn(const Coord&, const ValueType&) { assertNonmodifiable(); } + void setValueOn(Index, const ValueType&) { assertNonmodifiable(); } + + void setValue(const Coord&, const ValueType&) { assertNonmodifiable(); } + + void setValuesOn() { BaseLeaf::setValuesOn(); } + void setValuesOff() { BaseLeaf::setValuesOff(); } + + template + void modifyValue(Index, const ModifyOp&) { assertNonmodifiable(); } + + template + void modifyValue(const Coord&, const ModifyOp&) { assertNonmodifiable(); } + + template + void modifyValueAndActiveState(const Coord&, const ModifyOp&) { assertNonmodifiable(); } + + void clip(const CoordBBox&, const ValueType&) { assertNonmodifiable(); } + + void fill(const CoordBBox&, const ValueType&, bool) { assertNonmodifiable(); } + void fill(const ValueType&) {} + void fill(const ValueType&, bool) { assertNonmodifiable(); } + + template + void setValueOnlyAndCache(const Coord&, const ValueType&, AccessorT&) {assertNonmodifiable();} + + template + void modifyValueAndActiveStateAndCache(const Coord&, const ModifyOp&, AccessorT&) { + assertNonmodifiable(); + } + + template + void setValueOffAndCache(const Coord&, const ValueType&, AccessorT&) { assertNonmodifiable(); } + + template + void setActiveStateAndCache(const Coord& xyz, bool on, AccessorT& parent) { BaseLeaf::setActiveStateAndCache(xyz, on, parent); } + + void resetBackground(const ValueType&, const ValueType&) { assertNonmodifiable(); } + + void signedFloodFill(const ValueType&) { assertNonmodifiable(); } + void signedFloodFill(const ValueType&, const ValueType&) { assertNonmodifiable(); } + + void negate() { assertNonmodifiable(); } + + friend class ::TestPointDataLeaf; + + typedef typename BaseLeaf::ValueOn ValueOn; + typedef typename BaseLeaf::ValueOff ValueOff; + typedef typename BaseLeaf::ValueAll ValueAll; + +private: + point_data_grid_internal::UniquePtr::type mAttributeSet; + +protected: + typedef typename BaseLeaf::ChildOn ChildOn; + typedef typename BaseLeaf::ChildOff ChildOff; + typedef typename BaseLeaf::ChildAll ChildAll; + + typedef typename NodeMaskType::OnIterator MaskOnIterator; + typedef typename NodeMaskType::OffIterator MaskOffIterator; + typedef typename NodeMaskType::DenseIterator MaskDenseIterator; + + // During topology-only construction, access is needed + // to protected/private members of other template instances. + template friend class PointDataLeafNode; + + friend class tree::IteratorBase; + friend class tree::IteratorBase; + friend class tree::IteratorBase; + +public: + + typedef typename BaseLeaf::template ValueIter< + MaskOnIterator, PointDataLeafNode, const ValueType, ValueOn> ValueOnIter; + typedef typename BaseLeaf::template ValueIter< + MaskOnIterator, const PointDataLeafNode, const ValueType, ValueOn> ValueOnCIter; + typedef typename BaseLeaf::template ValueIter< + MaskOffIterator, PointDataLeafNode, const ValueType, ValueOff> ValueOffIter; + typedef typename BaseLeaf::template ValueIter< + MaskOffIterator,const PointDataLeafNode,const ValueType,ValueOff> ValueOffCIter; + typedef typename BaseLeaf::template ValueIter< + MaskDenseIterator, PointDataLeafNode, const ValueType, ValueAll> ValueAllIter; + typedef typename BaseLeaf::template ValueIter< + MaskDenseIterator,const PointDataLeafNode,const ValueType,ValueAll> ValueAllCIter; + typedef typename BaseLeaf::template ChildIter< + MaskOnIterator, PointDataLeafNode, ChildOn> ChildOnIter; + typedef typename BaseLeaf::template ChildIter< + MaskOnIterator, const PointDataLeafNode, ChildOn> ChildOnCIter; + typedef typename BaseLeaf::template ChildIter< + MaskOffIterator, PointDataLeafNode, ChildOff> ChildOffIter; + typedef typename BaseLeaf::template ChildIter< + MaskOffIterator, const PointDataLeafNode, ChildOff> ChildOffCIter; + typedef typename BaseLeaf::template DenseIter< + PointDataLeafNode, ValueType, ChildAll> ChildAllIter; + typedef typename BaseLeaf::template DenseIter< + const PointDataLeafNode, const ValueType, ChildAll> ChildAllCIter; + + typedef openvdb::tools::IndexIter IndexIter; + typedef ValueIndexIter IndexOnIter; + typedef ValueIndexIter IndexOffIter; + + /// @brief Leaf index iterator + IndexIter beginIndexAll() const; + IndexOnIter beginIndexOn() const; + IndexOffIter beginIndexOff() const; + /// @brief Leaf index iterator from voxel + IndexIter beginIndex(const unsigned index) const; + IndexIter beginIndex(const Coord& ijk) const; + +#define VMASK_ this->getValueMask() + ValueOnCIter cbeginValueOn() const { return ValueOnCIter(VMASK_.beginOn(), this); } + ValueOnCIter beginValueOn() const { return ValueOnCIter(VMASK_.beginOn(), this); } + ValueOnIter beginValueOn() { return ValueOnIter(VMASK_.beginOn(), this); } + ValueOffCIter cbeginValueOff() const { return ValueOffCIter(VMASK_.beginOff(), this); } + ValueOffCIter beginValueOff() const { return ValueOffCIter(VMASK_.beginOff(), this); } + ValueOffIter beginValueOff() { return ValueOffIter(VMASK_.beginOff(), this); } + ValueAllCIter cbeginValueAll() const { return ValueAllCIter(VMASK_.beginDense(), this); } + ValueAllCIter beginValueAll() const { return ValueAllCIter(VMASK_.beginDense(), this); } + ValueAllIter beginValueAll() { return ValueAllIter(VMASK_.beginDense(), this); } + + ValueOnCIter cendValueOn() const { return ValueOnCIter(VMASK_.endOn(), this); } + ValueOnCIter endValueOn() const { return ValueOnCIter(VMASK_.endOn(), this); } + ValueOnIter endValueOn() { return ValueOnIter(VMASK_.endOn(), this); } + ValueOffCIter cendValueOff() const { return ValueOffCIter(VMASK_.endOff(), this); } + ValueOffCIter endValueOff() const { return ValueOffCIter(VMASK_.endOff(), this); } + ValueOffIter endValueOff() { return ValueOffIter(VMASK_.endOff(), this); } + ValueAllCIter cendValueAll() const { return ValueAllCIter(VMASK_.endDense(), this); } + ValueAllCIter endValueAll() const { return ValueAllCIter(VMASK_.endDense(), this); } + ValueAllIter endValueAll() { return ValueAllIter(VMASK_.endDense(), this); } + + ChildOnCIter cbeginChildOn() const { return ChildOnCIter(VMASK_.endOn(), this); } + ChildOnCIter beginChildOn() const { return ChildOnCIter(VMASK_.endOn(), this); } + ChildOnIter beginChildOn() { return ChildOnIter(VMASK_.endOn(), this); } + ChildOffCIter cbeginChildOff() const { return ChildOffCIter(VMASK_.endOff(), this); } + ChildOffCIter beginChildOff() const { return ChildOffCIter(VMASK_.endOff(), this); } + ChildOffIter beginChildOff() { return ChildOffIter(VMASK_.endOff(), this); } + ChildAllCIter cbeginChildAll() const { return ChildAllCIter(VMASK_.beginDense(), this); } + ChildAllCIter beginChildAll() const { return ChildAllCIter(VMASK_.beginDense(), this); } + ChildAllIter beginChildAll() { return ChildAllIter(VMASK_.beginDense(), this); } + + ChildOnCIter cendChildOn() const { return ChildOnCIter(VMASK_.endOn(), this); } + ChildOnCIter endChildOn() const { return ChildOnCIter(VMASK_.endOn(), this); } + ChildOnIter endChildOn() { return ChildOnIter(VMASK_.endOn(), this); } + ChildOffCIter cendChildOff() const { return ChildOffCIter(VMASK_.endOff(), this); } + ChildOffCIter endChildOff() const { return ChildOffCIter(VMASK_.endOff(), this); } + ChildOffIter endChildOff() { return ChildOffIter(VMASK_.endOff(), this); } + ChildAllCIter cendChildAll() const { return ChildAllCIter(VMASK_.endDense(), this); } + ChildAllCIter endChildAll() const { return ChildAllCIter(VMASK_.endDense(), this); } + ChildAllIter endChildAll() { return ChildAllIter(VMASK_.endDense(), this); } +#undef VMASK_ +}; // struct PointDataLeafNode + +//////////////////////////////////////// + +// PointDataLeafNode implementation + +template +inline void +PointDataLeafNode::initializeAttributes(const Descriptor::Ptr& descriptor, const size_t arrayLength) +{ + mAttributeSet.reset(new AttributeSet(descriptor, arrayLength)); +} + +template +inline void +PointDataLeafNode::clearAttributes(const bool updateValueMask) +{ + mAttributeSet.reset(new AttributeSet(mAttributeSet->descriptorPtr(), 0)); + + // zero voxel values + + for (Index n = 0; n < LeafNodeType::NUM_VALUES; n++) { + this->setOffsetOnly(n, 0); + } + + // if updateValueMask, also de-activate all voxels + + if (updateValueMask) this->setValuesOff(); +} + +template +inline bool +PointDataLeafNode::hasAttribute(const size_t pos) const +{ + return pos < mAttributeSet->size(); +} + +template +inline bool +PointDataLeafNode::hasAttribute(const Name& attributeName) const +{ + const size_t pos = mAttributeSet->find(attributeName); + return pos != AttributeSet::INVALID_POS; +} + +template +inline AttributeArray::Ptr +PointDataLeafNode::appendAttribute(const AttributeSet::Util::NameAndType& attribute, + const Descriptor& expected, Descriptor::Ptr& replacement) +{ + return mAttributeSet->appendAttribute(attribute, expected, replacement); +} + +template +inline void +PointDataLeafNode::dropAttributes(const std::vector& pos, + const Descriptor& expected, Descriptor::Ptr& replacement) +{ + mAttributeSet->dropAttributes(pos, expected, replacement); +} + +template +inline void +PointDataLeafNode::reorderAttributes(const Descriptor::Ptr& replacement) +{ + mAttributeSet->reorderAttributes(replacement); +} + +template +inline void +PointDataLeafNode::renameAttributes(const Descriptor& expected, Descriptor::Ptr& replacement) +{ + mAttributeSet->renameAttributes(expected, replacement); +} + +template +inline void +PointDataLeafNode::compactAttributes() +{ + for (size_t i = 0; i < mAttributeSet->size(); i++) { + AttributeArray* array = mAttributeSet->get(i); + array->compact(); + } +} + +template +inline void +PointDataLeafNode::swap(AttributeSet* attributeSet) +{ + if (!attributeSet) { + OPENVDB_THROW(ValueError, "Cannot swap with a null attribute set"); + } + + if (mAttributeSet->descriptor() != attributeSet->descriptor()) { + OPENVDB_THROW(ValueError, "Attribute set descriptors are not equal."); + } + + mAttributeSet.reset(attributeSet); +} + +template +inline void +PointDataLeafNode::setOffsets(const std::vector& offsets, const bool updateValueMask) +{ + if (offsets.size() != LeafNodeType::NUM_VALUES) { + OPENVDB_THROW(ValueError, "Offset vector size doesn't match number of voxels.") + } + + for (size_t index = 0; index < offsets.size(); ++index) { + setOffsetOnly(index, offsets[index]); + } + + if (updateValueMask) this->updateValueMask(); +} + +template +inline void +PointDataLeafNode::validateOffsets() const +{ + // Ensure all of the offset values are monotonically increasing + for (size_t index = 1; index < BaseLeaf::SIZE; ++index) { + if (this->getValue(index-1) > this->getValue(index)) { + OPENVDB_THROW(ValueError, "Voxel offset values are not monotonically increasing"); + } + } + + // Ensure all attribute arrays are of equal length + for (size_t attributeIndex = 1; attributeIndex < mAttributeSet->size(); ++attributeIndex ) { + if (mAttributeSet->getConst(attributeIndex-1)->size() != mAttributeSet->getConst(attributeIndex)->size()) { + OPENVDB_THROW(ValueError, "Attribute arrays have inconsistent length"); + } + } + + // Ensure the last voxel's offset value matches the size of each attribute array + if (mAttributeSet->size() > 0 && this->getValue(BaseLeaf::SIZE-1) != mAttributeSet->getConst(0)->size()) { + OPENVDB_THROW(ValueError, "Last voxel offset value does not match attribute array length"); + } +} + +template +inline AttributeArray& +PointDataLeafNode::attributeArray(const size_t pos) +{ + if (pos >= mAttributeSet->size()) OPENVDB_THROW(LookupError, "Attribute Out Of Range - " << pos); + return *mAttributeSet->get(pos); +} + +template +inline const AttributeArray& +PointDataLeafNode::attributeArray(const size_t pos) const +{ + if (pos >= mAttributeSet->size()) OPENVDB_THROW(LookupError, "Attribute Out Of Range - " << pos); + return *mAttributeSet->getConst(pos); +} + +template +inline const AttributeArray& +PointDataLeafNode::constAttributeArray(const size_t pos) const +{ + return this->attributeArray(pos); +} + +template +inline AttributeArray& +PointDataLeafNode::attributeArray(const Name& attributeName) +{ + const size_t pos = mAttributeSet->find(attributeName); + if (pos == AttributeSet::INVALID_POS) OPENVDB_THROW(LookupError, "Attribute Not Found - " << attributeName); + return *mAttributeSet->get(pos); +} + +template +inline const AttributeArray& +PointDataLeafNode::attributeArray(const Name& attributeName) const +{ + const size_t pos = mAttributeSet->find(attributeName); + if (pos == AttributeSet::INVALID_POS) OPENVDB_THROW(LookupError, "Attribute Not Found - " << attributeName); + return *mAttributeSet->getConst(pos); +} + +template +inline const AttributeArray& +PointDataLeafNode::constAttributeArray(const Name& attributeName) const +{ + return this->attributeArray(attributeName); +} + +template +inline GroupHandle +PointDataLeafNode::groupHandle(const AttributeSet::Descriptor::GroupIndex& index) const +{ + const AttributeArray& array = this->attributeArray(index.first); + assert(GroupAttributeArray::isGroup(array)); + + const GroupAttributeArray& groupArray = GroupAttributeArray::cast(array); + + return GroupHandle(groupArray, index.second); +} + +template +inline GroupHandle +PointDataLeafNode::groupHandle(const Name& name) const +{ + const AttributeSet::Descriptor::GroupIndex index = this->attributeSet().groupIndex(name); + return this->groupHandle(index); +} + +template +inline GroupWriteHandle +PointDataLeafNode::groupWriteHandle(const AttributeSet::Descriptor::GroupIndex& index) +{ + AttributeArray& array = this->attributeArray(index.first); + assert(GroupAttributeArray::isGroup(array)); + + GroupAttributeArray& groupArray = GroupAttributeArray::cast(array); + + return GroupWriteHandle(groupArray, index.second); +} + +template +inline GroupWriteHandle +PointDataLeafNode::groupWriteHandle(const Name& name) +{ + const AttributeSet::Descriptor::GroupIndex index = this->attributeSet().groupIndex(name); + return this->groupWriteHandle(index); +} + +template +inline IndexIter +PointDataLeafNode::beginIndexAll() const +{ + const ValueType start = 0; + const ValueType end = this->getValue(NUM_VOXELS - 1); + return IndexIter(start, end); +} + +template +inline typename PointDataLeafNode::IndexOnIter +PointDataLeafNode::beginIndexOn() const +{ + ValueOnCIter iter = this->cbeginValueOn(); + return IndexOnIter(iter); +} + +template +inline typename PointDataLeafNode::IndexOffIter +PointDataLeafNode::beginIndexOff() const +{ + ValueOffCIter iter = this->cbeginValueOff(); + return IndexOffIter(iter); +} + +template +inline IndexIter +PointDataLeafNode::beginIndex(const unsigned index) const +{ + assert(index < BaseLeaf::SIZE); + const ValueType end = this->getValue(index); + const ValueType start = (index == 0) ? ValueType(0) : this->getValue(index - 1); + return IndexIter(start, end); +} + +template +inline IndexIter +PointDataLeafNode::beginIndex(const Coord& ijk) const +{ + return this->beginIndex(LeafNodeType::coordToOffset(ijk)); +} + +template +inline Index64 +PointDataLeafNode::pointCount() const +{ + return iterCount(this->beginIndexAll()); +} + +template +inline Index64 +PointDataLeafNode::onPointCount() const +{ + if (this->isEmpty()) return 0; + else if (this->isDense()) return this->pointCount(); + return iterCount(this->beginIndexOn()); +} + +template +inline Index64 +PointDataLeafNode::offPointCount() const +{ + if (this->isEmpty()) return this->pointCount(); + else if (this->isDense()) return 0; + return iterCount(this->beginIndexOff()); +} + +template +inline Index64 +PointDataLeafNode::groupPointCount(const Name& groupName) const +{ + IndexIter indexIter = this->beginIndexAll(); + GroupFilter filter(GroupFilter::create(*this, GroupFilter::Data(groupName))); + FilterIndexIter filterIndexIter(indexIter, filter); + return iterCount(filterIndexIter); +} + +template +inline void +PointDataLeafNode::updateValueMask() +{ + ValueType start = 0, end = 0; + for (Index n = 0; n < LeafNodeType::NUM_VALUES; n++) { + end = this->getValue(n); + this->setValueMask(n, (end - start) > 0); + start = end; + } +} + +template +inline void +PointDataLeafNode::setOffsetOn(Index offset, const ValueType& val) +{ + this->buffer().setValue(offset, val); + this->setValueMaskOn(offset); +} + +template +inline void +PointDataLeafNode::setOffsetOnly(Index offset, const ValueType& val) +{ + this->buffer().setValue(offset, val); +} + +template +inline void +PointDataLeafNode::readTopology(std::istream& is, bool fromHalf) +{ + BaseLeaf::readTopology(is, fromHalf); +} + +template +inline void +PointDataLeafNode::writeTopology(std::ostream& os, bool toHalf) const +{ + BaseLeaf::writeTopology(os, toHalf); +} + +template +inline void +PointDataLeafNode::readBuffers(std::istream& is, bool fromHalf) +{ + BaseLeaf::readBuffers(is, fromHalf); + + mAttributeSet->read(is); +} + +template +inline void +PointDataLeafNode::readBuffers(std::istream& is, const CoordBBox& bbox, bool fromHalf) +{ + // Read and clip voxel values (no clipping yet). + BaseLeaf::readBuffers(is, bbox, fromHalf); + + mAttributeSet->read(is); +} + +template +inline void +PointDataLeafNode::writeBuffers(std::ostream& os, bool toHalf) const +{ + BaseLeaf::writeBuffers(os, toHalf); + + mAttributeSet->write(os); +} + +template +inline Index64 +PointDataLeafNode::memUsage() const +{ + return BaseLeaf::memUsage() + mAttributeSet->memUsage(); +} + +} // namespace tools + +//////////////////////////////////////// + +namespace tree +{ + +/// Helper metafunction used to implement LeafNode::SameConfiguration +/// (which, as an inner class, can't be independently specialized) +template +struct SameLeafConfig > { static const bool value = true; }; + +} // namespace tree +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + + +#endif // OPENVDB_TOOLS_POINT_DATA_GRID_HAS_BEEN_INCLUDED + + +// Copyright (c) 2015-2016 Double Negative Visual Effects +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) diff --git a/nuparu/include/openvdb/include/openvdb_points/tools/PointGroup.h b/nuparu/include/openvdb/include/openvdb_points/tools/PointGroup.h new file mode 100644 index 00000000..413fc6a2 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb_points/tools/PointGroup.h @@ -0,0 +1,671 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015-2016 Double Negative Visual Effects +// +// 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 Double Negative Visual Effects 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 Dan Bailey +/// +/// @file PointGroup.h +/// +/// @brief Point group manipulation in a VDB Point Grid. +/// + + +#ifndef OPENVDB_TOOLS_POINT_GROUP_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_POINT_GROUP_HAS_BEEN_INCLUDED + +#include + +#include +#include +#include + +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + +/// @brief Appends a new empty group to the VDB tree. +/// +/// @param tree the PointDataTree to be appended to. +/// @param group name of the new group. +template +inline void appendGroup(PointDataTree& tree, + const Name& group); + +/// @brief Appends new empty groups to the VDB tree. +/// +/// @param tree the PointDataTree to be appended to. +/// @param groups names of the new groups. +template +inline void appendGroups(PointDataTree& tree, + const std::vector& groups); + +/// @brief Drops an existing group from the VDB tree. +/// +/// @param tree the PointDataTree to be dropped from. +/// @param group name of the group. +/// @param compact compact attributes if possible to reduce memory - if dropping +/// more than one group, compacting once at the end will be faster +template +inline void dropGroup( PointDataTree& tree, + const Name& group, + const bool compact = true); + +/// @brief Drops existing groups from the VDB tree, the tree is compacted after dropping. +/// +/// @param tree the PointDataTree to be dropped from. +/// @param groups names of the groups. +template +inline void dropGroups( PointDataTree& tree, + const std::vector& groups); + +/// @brief Drops all existing groups from the VDB tree, the tree is compacted after dropping. +/// +/// @param tree the PointDataTree to be dropped from. +template +inline void dropGroups( PointDataTree& tree); + +/// @brief Compacts existing groups of a VDB Tree to use less memory if possible. +/// +/// @param tree the PointDataTree to be compacted. +template +inline void compactGroups(PointDataTree& tree); + +/// @brief Sets group membership from a PointIndexTree-ordered vector. +/// +/// @param tree the PointDataTree. +/// @param indexTree the PointIndexTree. +/// @param membership @c true if the point is in the group. +/// @param group the name of the group. +/// @param remove if @c true also perform removal of points from the group. +template +inline void setGroup( PointDataTree& tree, + const PointIndexTree& indexTree, + const std::vector& membership, + const Name& group, + const bool remove = false); + + +//////////////////////////////////////// + + +namespace point_group_internal { + + +/// Copy a group attribute value from one group offset to another +template +struct CopyGroupOp { + + typedef typename tree::LeafManager LeafManagerT; + typedef typename LeafManagerT::LeafRange LeafRangeT; + typedef AttributeSet::Descriptor::NameAndType NameAndType; + typedef AttributeSet::Descriptor::GroupIndex GroupIndex; + + CopyGroupOp(PointDataTreeType& tree, + const GroupIndex& targetIndex, + const GroupIndex& sourceIndex) + : mTree(tree) + , mTargetIndex(targetIndex) + , mSourceIndex(sourceIndex) { } + + void operator()(const typename LeafManagerT::LeafRange& range) const { + + for (typename LeafManagerT::LeafRange::Iterator leaf=range.begin(); leaf; ++leaf) { + + GroupHandle sourceGroup = leaf->groupHandle(mSourceIndex); + GroupWriteHandle targetGroup = leaf->groupWriteHandle(mTargetIndex); + + for (IndexIter iter = leaf->beginIndexAll(); iter; ++iter) { + const bool groupOn = sourceGroup.get(*iter); + targetGroup.set(*iter, groupOn); + } + } + } + + ////////// + + PointDataTreeType& mTree; + const GroupIndex mTargetIndex; + const GroupIndex mSourceIndex; +}; + + +/// Set membership on or off for the specified group +template +struct SetGroupOp +{ + typedef typename tree::LeafManager LeafManagerT; + typedef AttributeSet::Descriptor::GroupIndex GroupIndex; + + SetGroupOp(const AttributeSet::Descriptor::GroupIndex& index) + : mIndex(index) { } + + void operator()(const typename LeafManagerT::LeafRange& range) const + { + for (typename LeafManagerT::LeafRange::Iterator leaf=range.begin(); leaf; ++leaf) { + + // obtain the group attribute array + + GroupWriteHandle group(leaf->groupWriteHandle(mIndex)); + + // set the group value + + group.collapse(Member); + } + } + + ////////// + + const GroupIndex mIndex; +}; // struct SetGroupOp + + +template +struct SetGroupFromIndexOp +{ + typedef typename tree::LeafManager LeafManagerT; + typedef typename LeafManagerT::LeafRange LeafRangeT; + typedef typename PointIndexTree::LeafNodeType PointIndexLeafNode; + typedef typename PointIndexLeafNode::IndexArray IndexArray; + typedef AttributeSet::Descriptor::GroupIndex GroupIndex; + typedef std::vector BoolArray; + + SetGroupFromIndexOp(const PointIndexTree& indexTree, + const BoolArray& membership, + const GroupIndex& index) + : mIndexTree(indexTree) + , mMembership(membership) + , mIndex(index) { } + + void operator()(const typename LeafManagerT::LeafRange& range) const + { + for (typename LeafManagerT::LeafRange::Iterator leaf=range.begin(); leaf; ++leaf) { + + // obtain the PointIndexLeafNode (using the origin of the current leaf) + + const PointIndexLeafNode* pointIndexLeaf = mIndexTree.probeConstLeaf(leaf->origin()); + + if (!pointIndexLeaf) continue; + + // obtain the group attribute array + + GroupWriteHandle group(leaf->groupWriteHandle(mIndex)); + + // initialise the attribute storage + + Index64 index = 0; + + const IndexArray& indices = pointIndexLeaf->indices(); + + for (typename IndexArray::const_iterator it = indices.begin(), + it_end = indices.end(); it != it_end; ++it) + { + if (Remove) { + group.set(index++, mMembership.at(*it)); + } + else { + if (mMembership.at(*it)) group.set(index, true); + + index++; + } + } + } + } + + ////////// + + const PointIndexTree& mIndexTree; + const BoolArray& mMembership; + const GroupIndex mIndex; +}; // struct SetGroupFromIndexOp + + +//////////////////////////////////////// + + +/// Convenience class with methods for analyzing group data +class GroupInfo +{ +public: + typedef AttributeSet::Descriptor Descriptor; + + GroupInfo(const AttributeSet& attributeSet) + : mAttributeSet(attributeSet) { } + + /// Return the number of bits in a group (typically 8) + static size_t groupBits() { return sizeof(GroupType) * CHAR_BIT; } + + /// Return the number of empty group slots which correlates to the number of groups + /// that can be stored without increasing the number of group attribute arrays + size_t unusedGroups() const + { + // compute total slots (one slot per bit of the group attributes) + + const size_t groupAttributes = mAttributeSet.size(AttributeArray::GROUP); + + if (groupAttributes == 0) return 0; + + const size_t totalSlots = groupAttributes * this->groupBits(); + + // compute slots in use + + const AttributeSet::Descriptor::NameToPosMap& groupMap = mAttributeSet.descriptor().groupMap(); + const size_t usedSlots = groupMap.size(); + + return totalSlots - usedSlots; + } + + /// Return @c true if there are sufficient empty slots to allow compacting + bool canCompactGroups() const + { + // can compact if more unused groups than in one group attribute array + + return this->unusedGroups() >= this->groupBits(); + } + + /// Return the next empty group slot + size_t nextUnusedOffset() const + { + const Descriptor::NameToPosMap& groupMap = mAttributeSet.descriptor().groupMap(); + + // build a list of group indices + + std::vector indices; + for (Descriptor::ConstIterator it = groupMap.begin(), + endIt = groupMap.end(); it != endIt; ++it) { + indices.push_back(it->second); + } + + std::sort(indices.begin(), indices.end()); + + // return first index not present + + size_t offset = 0; + for (std::vector::const_iterator it = indices.begin(), + endIt = indices.end(); it != endIt; ++it) { + if (*it != offset) break; + offset++; + } + + return offset; + } + + /// Fill the @p indices vector with the indices correlating to the group attribute arrays + void populateGroupIndices(std::vector& indices) const + { + const Descriptor::NameToPosMap& map = mAttributeSet.descriptor().map(); + + for (Descriptor::ConstIterator it = map.begin(), + itEnd = map.end(); it != itEnd; ++it) { + + const AttributeArray* array = mAttributeSet.getConst(it->first); + if (GroupAttributeArray::isGroup(*array)) { + indices.push_back(it->second); + } + } + } + + /// Determine if a move is required to efficiently compact the data and store the + /// source name, offset and the target offset in the input parameters + bool requiresMove(Name& sourceName, size_t& sourceOffset, size_t& targetOffset) const { + + targetOffset = this->nextUnusedOffset(); + + const Descriptor::NameToPosMap& groupMap = mAttributeSet.descriptor().groupMap(); + + typedef Descriptor::NameToPosMap::const_reverse_iterator ReverseMapIterator; + + for (ReverseMapIterator it = groupMap.rbegin(), + itEnd = groupMap.rend(); it != itEnd; ++it) { + + // move only required if source comes after the target + + if (it->second >= targetOffset) { + sourceName = it->first; + sourceOffset = it->second; + return true; + } + } + + return false; + } + +private: + const AttributeSet& mAttributeSet; +}; // class GroupInfo + + +} // namespace point_group_internal + + +//////////////////////////////////////// + + +template +inline void appendGroup(PointDataTree& tree, const Name& group) +{ + typedef AttributeSet::Descriptor Descriptor; + typedef AttributeSet::Util::NameAndType NameAndType; + + using point_attribute_internal::AppendAttributeOp; + using point_group_internal::GroupInfo; + + if (group.empty()) { + OPENVDB_THROW(KeyError, "Cannot use an empty group name as a key."); + } + + typename PointDataTree::LeafCIter iter = tree.cbeginLeaf(); + + if (!iter) return; + + const AttributeSet& attributeSet = iter->attributeSet(); + Descriptor::Ptr descriptor = attributeSet.descriptorPtr(); + GroupInfo groupInfo(attributeSet); + + // don't add if group already exists + + if (descriptor->hasGroup(group)) return; + + // add a new group attribute if there are no unused groups + + if (groupInfo.unusedGroups() == 0) { + + // find a new internal group name + + const NameAndType groupAttribute(descriptor->uniqueName("__group"), GroupAttributeArray::attributeType()); + + descriptor = descriptor->duplicateAppend(groupAttribute); + + // insert new group attribute + + AppendAttributeOp append(tree, groupAttribute, descriptor, + /*hidden=*/false, /*transient=*/false, /*group=*/true); + tbb::parallel_for(typename tree::template LeafManager(tree).leafRange(), append); + } + + // ensure that there are now available groups + + assert(groupInfo.unusedGroups() > 0); + + // find next unused offset + + const size_t offset = groupInfo.nextUnusedOffset(); + + // add the group mapping to the descriptor + + descriptor->setGroup(group, offset); +} + + +//////////////////////////////////////// + + +template +inline void appendGroups(PointDataTree& tree, + const std::vector& groups) +{ + // TODO: could be more efficient by appending multiple groups at once + // instead of one-by-one, however this is likely not that common a use case + + for (std::vector::const_iterator it = groups.begin(), + itEnd = groups.end(); it != itEnd; ++it) { + appendGroup(tree, *it); + } +} + + +//////////////////////////////////////// + + +template +inline void dropGroup(PointDataTree& tree, const Name& group, const bool compact) +{ + typedef AttributeSet::Descriptor Descriptor; + + if (group.empty()) { + OPENVDB_THROW(KeyError, "Cannot use an empty group name as a key."); + } + + typename PointDataTree::LeafCIter iter = tree.cbeginLeaf(); + + if (!iter) return; + + const AttributeSet& attributeSet = iter->attributeSet(); + Descriptor::Ptr descriptor = attributeSet.descriptorPtr(); + + descriptor->dropGroup(group); + + if (compact) { + compactGroups(tree); + } +} + + +//////////////////////////////////////// + + +template +inline void dropGroups( PointDataTree& tree, + const std::vector& groups) +{ + for (std::vector::const_iterator it = groups.begin(), + itEnd = groups.end(); it != itEnd; ++it) { + dropGroup(tree, *it, /*compact=*/false); + } + + // compaction done once for efficiency + + compactGroups(tree); +} + + +//////////////////////////////////////// + + +template +inline void dropGroups( PointDataTree& tree) +{ + typedef AttributeSet::Descriptor Descriptor; + + using point_group_internal::GroupInfo; + + typename PointDataTree::LeafCIter iter = tree.cbeginLeaf(); + + if (!iter) return; + + const AttributeSet& attributeSet = iter->attributeSet(); + Descriptor::Ptr descriptor = attributeSet.descriptorPtr(); + GroupInfo groupInfo(attributeSet); + + descriptor->clearGroups(); + + // find all indices for group attribute arrays + + std::vector indices; + groupInfo.populateGroupIndices(indices); + + // drop these attributes arrays + + dropAttributes(tree, indices); +} + + +//////////////////////////////////////// + + +template +inline void compactGroups(PointDataTree& tree) +{ + typedef AttributeSet::Descriptor Descriptor; + typedef Descriptor::GroupIndex GroupIndex; + + using point_group_internal::CopyGroupOp; + using point_group_internal::GroupInfo; + + typename PointDataTree::LeafCIter iter = tree.cbeginLeaf(); + + if (!iter) return; + + const AttributeSet& attributeSet = iter->attributeSet(); + Descriptor::Ptr descriptor = attributeSet.descriptorPtr(); + GroupInfo groupInfo(attributeSet); + + // early exit if not possible to compact + + if (!groupInfo.canCompactGroups()) return; + + // generate a list of group offsets and move them (one-by-one) + // TODO: improve this algorithm to move multiple groups per array at once + // though this is likely not that common a use case + + Name sourceName; + size_t sourceOffset, targetOffset; + + while (groupInfo.requiresMove(sourceName, sourceOffset, targetOffset)) { + + const GroupIndex sourceIndex = attributeSet.groupIndex(sourceOffset); + const GroupIndex targetIndex = attributeSet.groupIndex(targetOffset); + + CopyGroupOp copy(tree, targetIndex, sourceIndex); + tbb::parallel_for(typename tree::template LeafManager(tree).leafRange(), copy); + + descriptor->setGroup(sourceName, targetOffset); + } + + // drop unused attribute arrays + + std::vector indices; + groupInfo.populateGroupIndices(indices); + + const size_t totalAttributesToDrop = groupInfo.unusedGroups() / groupInfo.groupBits(); + + assert(totalAttributesToDrop <= indices.size()); + + std::vector indicesToDrop(indices.end() - totalAttributesToDrop, indices.end()); + + dropAttributes(tree, indicesToDrop); +} + + +//////////////////////////////////////// + + +template +inline void setGroup( PointDataTree& tree, + const PointIndexTree& indexTree, + const std::vector& membership, + const Name& group, + const bool remove) +{ + typedef AttributeSet::Descriptor Descriptor; + typedef typename tree::template LeafManager LeafManagerT; + + if (membership.size() != pointCount(tree)) { + OPENVDB_THROW(LookupError, "Membership vector size must match number of points."); + } + + using point_group_internal::SetGroupFromIndexOp; + + typename PointDataTree::LeafCIter iter = tree.cbeginLeaf(); + + if (!iter) return; + + const AttributeSet& attributeSet = iter->attributeSet(); + const Descriptor& descriptor = attributeSet.descriptor(); + + if (!descriptor.hasGroup(group)) { + OPENVDB_THROW(LookupError, "Group must exist on Tree before defining membership."); + } + + const Descriptor::GroupIndex index = attributeSet.groupIndex(group); + + // set membership + + if (remove) { + SetGroupFromIndexOp set(indexTree, membership, index); + tbb::parallel_for(LeafManagerT(tree).leafRange(), set); + } + else { + SetGroupFromIndexOp set(indexTree, membership, index); + tbb::parallel_for(LeafManagerT(tree).leafRange(), set); + } +} + + +//////////////////////////////////////// + + +template +inline void setGroup( PointDataTree& tree, + const Name& group, + const bool member = true) +{ + typedef AttributeSet::Descriptor Descriptor; + typedef typename tree::template LeafManager LeafManagerT; + + using point_group_internal::SetGroupOp; + + typename PointDataTree::LeafCIter iter = tree.cbeginLeaf(); + + if (!iter) return; + + const AttributeSet& attributeSet = iter->attributeSet(); + const Descriptor& descriptor = attributeSet.descriptor(); + + if (!descriptor.hasGroup(group)) { + OPENVDB_THROW(LookupError, "Group must exist on Tree before defining membership."); + } + + const Descriptor::GroupIndex index = attributeSet.groupIndex(group); + + // set membership based on member variable + + if (member) tbb::parallel_for(LeafManagerT(tree).leafRange(), SetGroupOp(index)); + else tbb::parallel_for(LeafManagerT(tree).leafRange(), SetGroupOp(index)); +} + + +//////////////////////////////////////// + + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + + +#endif // OPENVDB_TOOLS_POINT_GROUP_HAS_BEEN_INCLUDED + + +// Copyright (c) 2015-2016 Double Negative Visual Effects +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) diff --git a/nuparu/include/openvdb/include/openvdb_points/tools/PointLoad.h b/nuparu/include/openvdb/include/openvdb_points/tools/PointLoad.h new file mode 100644 index 00000000..65ebaaca --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb_points/tools/PointLoad.h @@ -0,0 +1,161 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015-2016 Double Negative Visual Effects +// +// 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 Double Negative Visual Effects 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 Dan Bailey +/// +/// @file PointLoad.h +/// +/// @brief Various point loading methods using a VDB Point Grid. +/// + + +#ifndef OPENVDB_TOOLS_POINT_LOAD_HAS_BEEN_INCLUDED +#define OPENVDB_TOOLS_POINT_LOAD_HAS_BEEN_INCLUDED + +#include + +#include +#include + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { +namespace tools { + + +/// @brief Loads all leaf node voxel data in the given grid. +/// +/// @param grid the Grid to be loaded. +/// @note This method wraps readNonresidentBuffers(). +template +void loadPoints(PointDataGridT& grid); + + +/// @brief Loads all leaf node voxel data in the given grid that +/// overlap with mask grid leaf nodes. +/// +/// @param grid the Grid to be loaded. +/// @param mask the mask to denote region of points to load +template +void loadPoints(PointDataGridT& grid, const MaskGridT& mask); + + +/// @brief Load the leaf node voxel data in the given grid that +/// overlap with a world-space bounding box. +/// +/// @param grid the Grid to be loaded. +/// @param bbox the bbox to denote region of points to load +/// +/// @note Does not clip to the bounding box, leaf nodes with any +/// overlap will be loaded. +template +void loadPoints(PointDataGridT& grid, const BBoxd& bbox); + + +//////////////////////////////////////// + + +#ifndef OPENVDB_2_ABI_COMPATIBLE +template +void loadPoints(PointDataGridT& grid) +{ + grid.constTree().readNonresidentBuffers(); +} +#else +template +void loadPoints(PointDataGridT&) +{ + // out-of-core not supported with ABI 2 +} +#endif + + +template +void loadPoints(PointDataGridT& grid, const MaskGridT& mask) +{ + typedef typename PointDataGridT::TreeType PointDataTreeT; + + tree::ValueAccessor pointsAcc(grid.constTree()); + + typename MaskGridT::TreeType::LeafCIter leafIter = mask.constTree().cbeginLeaf(); + + for (; leafIter; ++leafIter) { + const Coord& ijk = leafIter->origin(); + const typename PointDataTreeT::LeafNodeType* leaf = pointsAcc.probeConstLeaf(ijk); + + if (!leaf) continue; + + // load out of core leaf nodes + if (leaf->buffer().isOutOfCore()) leaf->buffer().data(); + } +} + + +template +void loadPoints(PointDataGridT& grid, const BBoxd& bbox) +{ + typedef typename PointDataGridT::template ValueConverter::Type BoolGridT; + + // Transform the world-space bounding box into the source grid's index space. + Vec3d idxMin, idxMax; + math::calculateBounds(grid.constTransform(), bbox.min(), bbox.max(), idxMin, idxMax); + CoordBBox region(Coord::floor(idxMin), Coord::floor(idxMax)); + + // Construct a boolean mask grid that is true inside the index-space bounding box + // and false everywhere else. + BoolGridT clipMask(/*background=*/false); + clipMask.fill(region, /*value=*/true, /*active=*/true); + + // MaskGrid introduced in OpenVDB 3.2 + typedef BoolGrid MaskType; + + // Convert the input grid to a mask grid (with the same tree configuration). + MaskType::Ptr pointsMask = MaskType::create(/*background=*/false); + pointsMask->topologyUnion(grid); + pointsMask->topologyIntersection(clipMask); + + loadPoints(grid, *pointsMask); +} + + +//////////////////////////////////////// + + +} // namespace tools +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + + +#endif // OPENVDB_TOOLS_POINT_LOAD_HAS_BEEN_INCLUDED + + +// Copyright (c) 2015-2016 Double Negative Visual Effects +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) diff --git a/nuparu/include/openvdb/include/openvdb_points/version.h b/nuparu/include/openvdb/include/openvdb_points/version.h new file mode 100644 index 00000000..5ee0be85 --- /dev/null +++ b/nuparu/include/openvdb/include/openvdb_points/version.h @@ -0,0 +1,85 @@ +/////////////////////////////////////////////////////////////////////////// +// +// Copyright (c) 2015-2016 Double Negative Visual Effects +// +// 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 Double Negative Visual Effects 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_POINTS_VERSION_HAS_BEEN_INCLUDED +#define OPENVDB_POINTS_VERSION_HAS_BEEN_INCLUDED + +#include + +/// Always disable Houdini warnings +#ifndef IGNORE_HDK_DEPRECATIONS +#define IGNORE_HDK_DEPRECATIONS +#endif + +// Library major, minor and patch version numbers +#define OPENVDB_POINTS_LIBRARY_MAJOR_VERSION_NUMBER 0 +#define OPENVDB_POINTS_LIBRARY_MINOR_VERSION_NUMBER 2 +#define OPENVDB_POINTS_LIBRARY_PATCH_VERSION_NUMBER 0 + +/// @brief Library version number string of the form ".." +/// @details This is a macro rather than a static constant because we typically +/// want the compile-time version number, not the runtime version number +/// (although the two are usually the same). +#define OPENVDB_POINTS_LIBRARY_VERSION_STRING "0.2.0" + +/// Library version number as a packed integer ("%02x%02x%04x", major, minor, patch) +#define OPENVDB_POINTS_LIBRARY_VERSION_NUMBER \ + ((OPENVDB_POINTS_LIBRARY_MAJOR_VERSION_NUMBER << 24) | \ + ((OPENVDB_POINTS_LIBRARY_MINOR_VERSION_NUMBER & 0xFF) << 16) | \ + (OPENVDB_POINTS_LIBRARY_PATCH_VERSION_NUMBER & 0xFFFF)) + +namespace openvdb { +OPENVDB_USE_VERSION_NAMESPACE +namespace OPENVDB_VERSION_NAME { + +namespace points { + +// Library major, minor and patch version numbers +const uint32_t + OPENVDB_POINTS_LIBRARY_MAJOR_VERSION = OPENVDB_POINTS_LIBRARY_MAJOR_VERSION_NUMBER, + OPENVDB_POINTS_LIBRARY_MINOR_VERSION = OPENVDB_POINTS_LIBRARY_MINOR_VERSION_NUMBER, + OPENVDB_POINTS_LIBRARY_PATCH_VERSION = OPENVDB_POINTS_LIBRARY_PATCH_VERSION_NUMBER; +/// Library version number as a packed integer ("%02x%02x%04x", major, minor, patch) +const uint32_t OPENVDB_POINTS_LIBRARY_VERSION = OPENVDB_POINTS_LIBRARY_VERSION_NUMBER; + + +/// Return a library version number string of the form "..". +inline const char* getLibraryVersionString() { return OPENVDB_POINTS_LIBRARY_VERSION_STRING; } + +} // namespace points + +} // namespace OPENVDB_VERSION_NAME +} // namespace openvdb + +#endif // OPENVDB_POINTS_VERSION_HAS_BEEN_INCLUDED + +// Copyright (c) 2015-2016 Double Negative Visual Effects +// All rights reserved. This software is distributed under the +// Mozilla Public License 2.0 ( http://www.mozilla.org/MPL/2.0/ ) diff --git a/nuparu/include/openvdb/lib/libHalf.12.0.0.dylib b/nuparu/include/openvdb/lib/libHalf.12.0.0.dylib new file mode 100755 index 0000000000000000000000000000000000000000..289bc2423992ca39007a5c01f2d1be6958b2ebf0 GIT binary patch literal 277624 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 ODwg?o!WsWa5cwZn`+ea6 literal 0 HcmV?d00001 diff --git a/nuparu/include/openvdb/lib/libboost_iostreams-clang-darwin61-mt-1_55.dylib b/nuparu/include/openvdb/lib/libboost_iostreams-clang-darwin61-mt-1_55.dylib new file mode 100755 index 0000000000000000000000000000000000000000..e8a0d0da1d3fc7dd9b4db633eaafc288483f7120 GIT binary patch literal 103104 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 literal 0 HcmV?d00001 diff --git a/nuparu/include/openvdb/lib/libopenvdb.3.0.0.dylib b/nuparu/include/openvdb/lib/libopenvdb.3.0.0.dylib new file mode 100755 index 0000000000000000000000000000000000000000..377e1e602ed7efcc5aae75238a937ff3cceb0952 GIT binary patch literal 2643180 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

    }g8oI~%uA&{6wE+sC#Lp?Vg`Dn^ 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�z&?L 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%0D$Z8zm1dl@NEHu|C zzXT#PH<5^j;y|>pt^@LcoIiK#F_Z+ZO2K;fE}ZG~rKMjY;*2pw{0ASzC#`NSq-nkyGCWp;2t$|<2XyuzocaI=pmcO09A5m z0W!foeDOJIGxF#qONk94>0b`?LF;jm=MQT6SLOTwIk~Wfyo*v6aeJzTjv%*J!65tk z9=;*=AGZs<>^r6159oWJ{S9hOAzO1U#Fr8fM7|sQ?)X=CuX!G=nW~1oFn*T0%KZ

    @BnV3$rO`?B`8*O+n)T#sl*z_xYrx zwc$PQiBEPUZtyC|uPeU-em(g0;@6j7F+VeCdrjS3wRta`L77~pHDs7IV3h7-H4BNj zT_QTvsvgI&yV;#r8mqfDsO7g&kdf{~=*eOGGTAY^gx~^x!ou|8L)Jd1f(j|0 zO|u6`<03XqZwi!XjSTiq*&4XYG^(f50VQ^H9^MhzLxBrtU;6x9gx?N zDttY)g(5Qu2LGcKS_aj{Hd?yM8u^ADll9LQD$ae8kYMwasT#6g0lre}Eq6ZjO{ujL z@IscG=mt5breSwI4L!j0dJf<#7x-m?2k=>Ctuh|?(E(ZWNchx$Qs9gigQxRkzh7z8 zHhm>A$bWEiD+{RT3K40Qz(ZvF)JBUX3d9E$nnwn&-Ue3XoWQH(k^+2nqgc^*I> z4`HUT^@fN%RuyGMFXe=#7vzt~&83Sh!gPM~V3+|}Zo|`=3o>3}oeedi&bP74K^Jxx zdq|!f2F{m{0nPp*?0a*d?52xWv$YOXIuFNYcq_F^7Y3}?#6y`qq;qu=o0_eQ#@CR-_a1 zMjeo6$tmV{A6)1{pEZnocDr+7nFWiP0OYZTfjD?WB5PPq$I>?xGDZv^`?u8y2p^1e z9@H_I$WI)b(Lbz(L8lQs$l@Pu0rr41fIIyLqgK`_eZkzU^EW!zWLs|=wcR9a2ux+R zirH5WL>%-q{sWm< zJru9;(IBT^k=uTrOy5TKlP0~NgspCcsoQCNC#ZH4{B_wOpOw>*nGoWlE7(!)FG5U? zU`M&W2r)T=9n&|IHFoF?xdpG^4Bps#xv&R7+endVMgebla1duc{Oc?Kisc^;eAnWoOE_U`<~B8Zf#G(TeGkz0pr!&VrtES94J$1h`PGIaQl0sQV&=JvRKe!=vfKnHzi4hQV3s;%`KWG*{HC5*hG^aG z)tK@VfYLMTQ{EKQoQ?Ja_td9^uSYV4_u!7?YwwoAuN=SNkiJ<4BS(Z@o7~?}3>RAB z4olUCKO(8`WUs!vrItgT5WOW#l{a2&?R+vie(qyx3I;RmogPmhSVvhfIbDIS(C`Vo z%J|esSt)*CN?)E~neq43u)tP%De10R--juAXgG=XNEv^DZk1_Rq`N2&oDbg$xANC+0OKij{eA7aqPV7vS>Z+!oQ2{*Vgc-dZH z%j(~db6GY^e|z+;LuZV%>!+mlCQF56(Eh8^=gS~c9B#h+5#n98gJ$|W6F;|DkDq^- z8u-+D)CHvYcdf6-0@mYY)U`nP1mbzUd%fDKx-SWwrVgdbxnIEgwQ$13vl)-ndW75N ztw+Y*jZ)0luE!s;?)7@Sp10=p_^ZDiWb z7n#7V&ljxTb5Qx3^$5RX79Az}(27yXQhHczWcF0q$Z-5Ep1$qrvYp}lTP`NGVT6Fa zSi*3*5VjXf4k1b5l&pdP3EC6#pzs1HMPC#|pDSqJfl_q5yl2qvpHhnM78Kpljpd!~ z2|51l<(-1|SX2zR7|uDcJmkzJI%slAn&JNbcXYnfU+F{h6n$u6mhuXX8&juEIDDQm z^lMBzEHRW14i?RplX>|SCHAf8Kd+j6tfjQ#G;c>uDfFLvq3&FugM$87jare6*6D&! z>CQCR015P+O-f2;6a~~YeP=nphV5F8P0|TU&((9l2AknylosXW*vxIPm3R(w5Orr5 z#}iZP&LHfG927lu=V5>1Mj(&cwFFMHcJo}@k!Ce(H>2(>hgDjLy7Lweu_<+D8ibX2oFZ`iZ{&4ME!E9hkLdv+AMl53P|36ic0tX~c%)>BwNdLhzm?K)z7^(P>9f)^oWr$}^ZN2Z_O&QEzgvEVa3Ac;g8psg7u&s= zr@i5C$mA3CW<3ro%1`om9(D)eggN)Z7f;*&n8iuu<`usY9K0-bIXwAsLB!kKea~ia zx(-ou4tK9Mr1P|zvoA@s+_#0^O8gual$@)yRgw~CEc%qUR-h%pI(bg7lL@R7Wqd(# z!u^|!+BI+>f<=Fm7U&6S)Gj25g=5q{qh8)JYX72M>a{+!G_7_PA#frDWuzU>^tK|d zFO`MNvuTZNFTa`BDt}e(3lvEXt{FOv6(cZ)aB}Vh+FOccFRso5_LNaUXJUK%Y7Wum zy@M!0hWwwD_ds3LzZZAs_K`uq(l0MRHi%Q~5avDdxD=IxveN*uuB6nB>UXG2E?Z-D zMzv_F(GQAOq)l>gXID{TkLh7@{Uw9qUjQggVy7Gu!gGm{gP5-f;BISL%Rij}+vZA; z)H8R|RXin#(XufJPJR^W*Rqte!yFn?rKH8GLQq&!u?X@~2S?)?H_l~KP%PGHt1+e? zB9AK`@R;O@D|Evmm*|69@;q!0_gS5NW%e;;)(9Wmzu}zKM)+X<4d5B9k z-v5Cy{m0-B|30H+%^uv$?kC%+oLgf7=nFM_4k8t zjNhCQ856>Fd-k@F^YC_XmfTG_$Z zW`ti(9@Fq9))YO{bgs;<+V2~`iGL-wLP^ZuQB+}6Phf;3)2u|kwK6lZerr1u?@O?W zaZ}KJdDwUo62s-Rc4=Y*eryrtX`lUi;`HYJOQ*m{jisjeC6dg@UW(`rJEnH4S|zG3 z9Dx7nevUYS=x!@C5INU+M;+QtCogP`s7dm?I@iF=P)x58H7Tywh?*SLjWx<&=AO0I zJF@e453b2?qOK+yWlR-Zm@Zpm%J9qfZd8eDJ=9pu#wEOm&lROz01Yc2of+~CJbDRDc4?pcFg+duCTvr z6sbivUNc`fFn_sUH`0U$pchzG(&0#IeW%X5BgCT%M<^I_ICUAhf$M+w{kcK`U^(*? z(G7_Gn)d1&)i#s}@ajg@>aQ+TO{;%_x=`f>h5CEE3)OCqK(&d6qIaQMGg#lJn%3Z! z_o)o^2CN~=tx!_3iLw9*zw(~&4Xui>aU*}!2RH}o)^ zO#+es;u8DFu=Pdcno?w$j%6yyc{nUqp`q+Ch`B3qvT1D>?3so?OKdz|`P*W^D6%VP z9~F}M(q+mIY?NpH{R_)GPU#-NHd;}@H<>BE!l=FpAWEzg!c|L=651Faqfn7hwOkjK z$_GYqiiQ=GA2+3sJ+9FI3nf2=pX{P>JtpVFpD}IB8|;p5h(9LqkLywN3!mW#$?EL~ z;<`l=%r~tz3<^E8B>oji>4PaJ0F{4$GQ07a?K)7Y_OnlZ zn8AYv*13x~_L)wf8$atP?G(^n%%$<}rIOs~)3Ci`){7z=AtEF_3FEGFZpqovIpO+) zZ34<0D}GYjjPGCKoYP&EFR2Rln!;<3Ws(+z3 zq|uNWs!yFooB48IAsh5Beqv+{+#94D>^{f8Dg(e2(_P{2^9p}NMNgh1C)3kFa&uwQ zM~-C<958$C6H=mT%KaK#e+W5}-2DVg~_@vM+%OKN(N|&Fa@f%GC(U~JI$4@*+;pu zq1>kUm5uIQlpvF?xsj!*nn!zacJ5b9ze8L6<{y)AT#zay+5FomMJDI*DyGpril^d# zD&K2z18gH=4`BebD8(KE?QoFY+QyJe-!TUUgO-=mD%#ji-x&c7hwlD9Y4j zRAvji8L1mXpUPTJ&!oQ7Gkq$VewdMIJEz%AEX@rJMnlO)|J&}03Tdo^48Q~2+?Csg9+NkQzyK&A|keQym| zTC=^mHDT_n)4U$8D%VqJ;Un@y$|85!Vbjj>(*9*8dJ%{2e~LcrHz~sZKKKa&Wq;%2_T!aoO`R*Akn{Q!cdGb79R^&+7x(u5k3zTCqvP$s zAIuRHH*{w6;^r>Qh8{rrSmdRMMxu^>VTVaOv-9;V&t)g=ONhIeIFc4}3#fZ5TR zR@+2xvk;cP^yx*Er0o?1WdRBufpJF*0|V}00?6cE%u~9(Qe|m$ADk{LRQrof>i57g zFtzXw7kY)>mVl5Oe?r8%+o4B=@zhLUU}4d zEtMp=?3RF8g7@x`0O12k%PvXbgplHIouz$J2!reMq*VyG{h=fyJ_L}$;UZE7^=+YT zC^POTXAjNvQM#+0c5K}so+--hO$9w)fv{{|loWC zE*gr*rh4p=t-v1H3ha@s@Xq@f1`tvkdk3&B9TWOg`xB}VZmbjC+%&A0UHWNLy*+Y! z(-J$1Vh7B`%^c$DKQHLH|7?h8EE1a?I4K-saf{`Vsu}PKy_#O{|ElrGm+{a>cxoTi zLn8h0J;tN>UojrAd8?*$&1&`x6_5*)tO%A@u3Q zO9A3{h(jyM^Op51jV?iQPm)VyaiblTXmK}155Q3xCU)EF)DD7))E(}ZHL22(HLqMI zK&18?8P5edgve_1=!v9%tb-6;K29gd5;m3izle5TbjKsId4vGF~7g#{FzRFr6zNd-a$T1-HjY<3~dG| ztd)PHAFe!vc2S!Hr^JtpCqtY0x#n2>GQyYJ(=?-oG-gkZ!9tI6MM`?d}yXC1wi#dsWrMG3K5F!^ateG=w4iXh`p)d$bx$fWwt3= z--&*dYN~x`l^239(lDP#Po?^A#BgP^N+j*w(G_$B?zce3%koS7A4YzNOrt)3C&#xV zG&lx_+;MV1^ic9KivId8`1r%^`-RoVxU(Ey7qdNqS8i%*;6K9XL?0~3N*?)-_;I`(CD5%>lTvOZJ$ z2WO)U{83_$qfi#=cDX@Y_${qS>@ze zYSaAzR|6uJA@&TEqOZE&&vlPx4a>mSHK&cfpS<@@(Wk0^#2=#!R>nBpjK#kF8Su9p zuOo#L__vK$<)<6C>k7yv@t5qUAq43jxCcD#9aP*QHu{KV#^-fq#*|n-9e~Pn0S_4| zx=KRajgw`3`<(v<3$rWXvgGn^S78@Rm`~EODE8haeZ}pn93(jcagT32pYQoE9LDl2 z&QG)xF${5)xJgE!L2@{K{Ju2zsnJAu`9q{wM`zfmGKB9htYzQ)pi zQSrpJ38nYTNZ7QLl{WTshMGCOf!Uxidyj!q^)2EGO z75pjH+%LsCeZE*vn4AZtuf|Ilqaa?7okcyQ(OgsBIb|36hRWFj1H#QTR|Ci$^&;A| zpXrPA8Lxm+babvPtq*q4LUoqZeP1e6>6aNZu`^=g~Sgmw0NM=Ot^5 zE#OV&Nn>nbDy+^6YltnAuu|(|{Ix9SRd8h38Iqsi$~;AnsPldFEYoI%aX;UQD|diy;;>)+!Be(R@L_GaP~4m zr#BIuc-&iYB9teyo@>KfI?KMz6?*?FGk(5~--a)YpXco3U&%i3-5WShcw;5IMvm#V*6YEQvG4J?Yz%yYBc?O#_uB0&z6)KH|HMb%Y)r| zeeNmhS+21uZQ|~|Kt0ki3@i5PHLO_Lst|x~zWc(>%z6C1kly}`=9hQ4?0Kh#|odZ>8T-b z`b;CvJy(Mw!!XFsLww3n1!$J!Iw&QL+jJ(m=>O+U3U*ocS4_)$Aq{OUotjLIyDBbc zUMFR^sS$UN<72e#Y^u`|2f8r#8C{YLJ@njOs_rGP&LZ_-&$vnmHK)(1l2Kt;FXP=h z{xbW#rxzvoh{6q{iKrC_DAsWY#AN0zKagk8KQ(U1b_iE)S1=g$GhEv0fC1Yw9`~ zz+R}(!0?T(O`HQ)PR9YsodSt6tnDq&cSkehUsv02v|a|-UL;VtO9>YM+*9?s{#64R z)iU)G{6P{}OKsInR;b2~NhesLT017)V1;Io9fLd38h1EJfoqn)bp&wD0j{|TVFdri zn^GfnZL{2G?~`i#oby6*1ijiWR0;a@SV?&Lbm*p_K*8mpyF+JvbOlK~63_kT^#UZ( zAG_;y^nK~kA0(qcbDz=C&n`$-3>mjrqMbf(N|y9W|3vr~#=UChvNs+3RJQCgH?!mP zIO#d{em(nGc^zpS8581GvI`;WTE1eZac%BR$L3}2G;Uy@mb0;IHU_ivlMo-T;i!^2 z85M!vy;$JDJt;*oh1J?|`rOhIs-rYijwA!B+!GZfKVPeB@8Ny-*pP%oS|F=TL#`nP zx=ycl7U-_ceMvom5(ug)RZsk5c^XuYUYA64ZH?Mr3aCz>9g-lutIA2l=2<{JY#Bba z9JHBwmg^~#RjD6IpC{Wom<$4$Ym|(5#SCgnuH}k3fZenfW(Kg|A=!_Xc6dvp?uQDz+X)BVZKtvqyAbWp zo1((4=T-Oy3D1`BS%hQKUqJvVF2FgCM8otAyue11ELl#(e;X`?ERtwKT)GZ+r zD&*loQsyFwc6IrSB!@d&*L5l|sLGQ=bken4WrvNQy~l*_bk;#3pKgAKWq9nl>iznS zEV*~fjGnW{P|8jtKlkIK)Z|_&6e1ClSSMmV!dPr{f1_(!Bj~O`hK#WJqf|@Zlduz- zhtYk6aH+jfCh8Oal-jS)eO5j7M4hB+yFn8z6x2@_{$Em%jH(p=Ln+)=CoSX}OKe4R zdxy_RPdZYOToAlSrkQ(Ff{uHYprblXxlgGcS4;sBjg(AX*`I_;mP{?BBh?|+Ehkfo z+hDV@b^iC*eOUzChS%!s&KCGJmN+zF|;SrQkP zINOUm`12%CvLYq=ETYwgGVM@nnXoPrcAX;JVzO!^U7NIYYP}Km**&DYK&O+rEa^5% z7}qNheKEBP2y2iqB|LerSHG}i@Nq8PfB6ZXG;yhdSq~qswJ2Qc)6yf>Hf}Og%>#H@9cYtaH0lR^rr3MdJ!R zXjBVsBhBr^+RBlvTS*!$6=FUt*wvRf&BQX7;&b>zHAb~9$;FytRb5{w9XogQZ}YOa ziw|Ja1SLJO=8nl7dz>^sB7KAZGwC?`W{C7n7k+n2?2 z^RILocck_40&#L)6k2fak-Zt4P436=1o1!f0iyGL0iWB;Ve0X?G=|eB8m2T&6&Y`~ zhY=ndjAfN%yO|v>&e?-kfJpAkKJP2}T?rasBI~>I zYvRMnuTJWf_Y^25crY%0Z(k-!R^>f?Wj;oFCys$RZOVjuSrHum{=*{pN83Yf*o{TL ztEs`7FiD30l$%*l6DC>Ps}8oE7;hfd7WJ-)zblncHX*{p-q)n3r8-S5U{jU6bQfJ&93G zv~Q@AVZ*hPzS2qEB5}@a61DTE_|@~daXE69*h70*Z`FU+&RQonDYkQWR*$ma za^t?m#HMQsu8nrn>!Ycm>fq(Z)c+vfOAiA3r2b>AHu;X(y&f&d<)Q@%cA5W(SU>OV z%ifvz1>Cb`$#H)d9OEca1TUwiCcXxOOhv%}OixVf&bsuQeh3M%(Y zZ@^9*y;L>Yy2eM4plAt7$uBSijhH$r*h33U`%1+bP01&u@kix;%nraGg(HeSo~dMb zhjv9g&zCqt_m2ug5Pytjv|gKJv>unzcWN&0b#>piL=Z$Xzc7vens^#MUG!vxw!{j^ z6o&Ws1+vWI56I~Fm#ZiK)TZ%?SXaKa1WzDVy$t1BVf(`EqW3xeR_8n1}2#pDaj5p=RIU^zBz1P-IXc|%&H}5Vyugd+tgiJPs1YKm_% z_iQq|Z(=!NdH8dd(@D4gPB{}O%KifDwALsl;jsKfZ?c?s*ere0vSeG=lI6<$dD+c( zwkdfLeP3JosW?6nh7wH|A6ehg56XW%D3|hI;|JwZwpxVNXVAk)SVA1ZD08q-B4}C+#?JBab;eFvz@FzWy~zapF0c`|1XFh&qaDqJ17a zg6ZTw0uP9z;w)-Q_k(hlN||&7B6nz6I`143S13SXXs>q1sZx^Z-0S4?+xbE1MN(M_ zYgLw9`ytg2+?uC*&$v}0*Nmj<^7*S+$8;=tmQ+>R}k8{XSZ5@u2=qFd8SS` zqsD3vBu=VX)yaatW!px0b?@TI7(Xe~2pS@@ZI(pRw)3bvF-R$2>~WV!P3o+&{$>q@ zojb18?1NG%=XxnA>q7!XwB#hpbPM^X{h;`GQ)i;wU;kBxyL1i}Ga>M{hxr}>wYTlxlbxa(jn{p5|uJJ;bpz&>%4O^*L#I3 zC7I6siF|&$^!l{O-nZV6T<;aF zX8&O-Gj@K;1C{2?tlKN<$6COBtw>awqB*|JF?zScYxUL zG+-K!)Oz2o0B&?mU7vLBX(=EsNP)Xfki;Ra{VNOx-LMYL;07hTTJO6?>-GMrdZyO< ziR6+#dg->*dS6M3!>{*QY`g&xJZcI%138F=7VzxFb2(2rH`axnsj@xux;9`>-OekA z@pJGCXf*^D%7$(2gkXL_-i)c!C8g8pUZh;u-ORChzc#aXG;P5~TLm$RU+^L|CT{0t zp1j-`wjZwJ1=oLwA6#kL=M%781tiWBdbo-EhkK#Gn<7Vv{ERq3QO3Bnd;-MXMCk9! zAoD~%;jQQzp`O#|FRMYFD2=IAlxaH0?m9vsd)pk_d8(PcziDg8zRj2TG5-q^xA4Dj z;)bw&jVS^RfOC~;pTOrZJ`?9_dLI3AM6lA~K`mFj@Mw6rAG{HGiWmcd=k-J2!H8V+ zL^)vG|0@8pwkKN1hhy%cW@p9LY|>2_i%-VDv)R=s8g>z|F-#8)a`kXIwi?x6K|^9` zOa^3ubUu;|u%g5-A=CC!)y#UhQBNsu6eCwb!uX{`XY`L0{uCv}rGKQqonyBk8AZnX z%5Bskf zhRYLyl18`>UL_(|YAy)_?at!^yBp9X?MX50P_*%^D@ z{)8-$XM@J(F_G5^3L5H*-%A`X1qp2Q4egs4FOyVbp4&|O=&(Iy7|KazI{r{y{zD*s zH$s;eQ{+aFf*j?(OlbY2*E80WX%7ef6EGns@b8Z1kKl+GJoF+8j@f>ZAAy z+e2Gn#=)>nt#_L%fQif>L7|(#P2OB@!o{ze^lBL(CcfVte)v{SRH!NUVv%{YmZY~* z@KO9uqT%H$9+Lv?Uezl&WOeEdbon-gsth`qLeS1qFu4^8KehR<+i~Ikk{i*&R%`0G@D_|yX#xn7b(M!|obK1h3^EQ|&Y=O!4EKark;r3tbRd&L9xk^Vv^jGmb(? z2{keip`{UWr}RnF*512Q@Wk#TK2W*1f`^bpky{fJR8J?ti{3)4ELXuJu^)osZkO7C|c-EAfcMVeudbh`EtJ)8wlZ)KpP_n1kYyUqu7X5 z?jooyglVv5GkiIzH|^S}Y{mn`Kg@3qzd!Tq`ejqo$@~iWoyD)mZp`%YE9G|+zd!N2 zVNX-j&-qpGTSJ=13HvX8^ZA`k*vtG@@_UP46F=YHrlzC#oxtx=et+RNkKgnBE~M{^ z`IYb+$?th+93k1h;CB?+pThv)n+#_o&)4}q!EYdx%2oVE^V`d>BWX|O*PGvB%J`D> z*(~Tz{C>^v5q^*J`zycYq<@#+XZ*5BpTzgV-}t}ge&pf%TilOqgtzo>x*s{axvuYe zKk|P~e-4bb=+A2N-`1b+R;TLvuKfx16d9M^soZ}oVL_YrRlNc)3c?5}BGstgOK6ER z&6h9N3<@7$AJDXGmq-Xl9wW9<@`2>MJBH2rX;g1h<<&09C!xBhMX=xOy~c4=jem0W3dBSuE+4(L z_xi-?R)e|-We1d+w_iRLT^4bXYi9_yR z_Dud3_b)d~Ry%hoY5&LWUp_d8IvsuAS`6P4&gOZ)YxTbMMU^snuOjy^Gj!fLnfEVQ zs+43pHvwAymUk3&Bo!9^7%9QdJ#k`dT>CGjJoxXwCDAwEzuZCU)cwmCuY3ieo%@mu z6F@729n3Qyj*UL_yu z{^e#53OhHe)$ALoL#OQPrKF!GkX;U9f8*&s%RhbpGK)8T|8m9yE$?6c^K1pCw~H+=>H zW}~{+(P^d%w?h6J6~jqrHPd-`E&(Kyhy3w6N4-e0dGc1F0_)_JKjIhYH@e{AwGv`Y z-=H2-H_M}Os>?(E8r5kV?i+8yR+PWSseAb^VWu@-1lAxep-TtkCkCdS-!**-$)}~V z@|C^+qu__8__2y(pE~cG_8{ofqu>M!eOeEFDi(XKi6#Upgl`wq54l<&xt4M?J)K7X z29vd<%X^A{f$vb?lQgDIwMpGWD2~DC`2I_k$TH{o%=`DMPrG6TKuId2&jgXbLYO=D z!Y{P^P*b$fbcSSC?vrsG{}Ha7Di9!2UlG3~QvyV7tQ~mll#8-GADG<_nkfk@1h)2= z^`Fssm!8AwfnNT>#ewYC#0mor$d{XP!-v~8$~3A!1m+SHt4M@<*5>&uGx}UkQOXY; zzJ|8Ii}j_v1i`xWK|y0lVym0qAKpsSE0idrYR1%Ql$hyz&a@wTT{TQxUR{^z@(Nxx zaG>^rq#JW#;!v@x^w6uiAJ#|QbUzE`i%A(WTDVe+q#ZjL{46az0&jZKx@5~@mk2YZ zePd|3n!cIV9@6YBDS3#hPj62f4^Ax2<00o2JOj&kRPE!u5?Cm!hS%gsC|`fyJQ9zA zEP=C-kywSv?MXm~m!t{?qefhfQKnISAw7UkTPqeJR@i6F<6&xp6mFLRGA8&U+mad+ z@$?0+DYwM_sn1vHOwNC?m~XJ4*dDl~gwd%&vq`*;fA0PWToksWo>}t@hbW4Rt#a)v zaY8fjkStKR*10mg%CDIr!)sA?rbjb9y!4X{?+-JF_v9phM)?Z|B-K|$u zS3I|XAl!bTbWq{;DcYsql{h&ng%$43OU1ucvKSJ&GhSg&;XvLK34HB%sLb}2S&qnh zm)Rzsa-8w%6)Ai@7j*I1Ongq#xQ|GY&g6xrwP;?lavc*RCY;}z@h#rH+j%WtZUv%cC_)R&2^Hr(Z8%^5$2A9P# z-kOIF>0>*L`(C1J;XvRWC1lLco6fb*19ua|<C-MWv{1MaJ z@Oh%x>r-Dj#{4EO<&MCa%5-1R$OVy~Rqbt4USv;RXv}{*;9nm-z5erLzWvdz$vpT2 zZqBqU;>Y>Y6i*(&9E(&$`E)j_&R1FGGZ&Nvt&*AZunt*1o$)H4`82O3(;2Ulna?)W zHPHnMo!w*YssFs4xIVtz+K2Ecgi)d|D#Qk^dIyfJ!)toF42Xlkr#YCo}`wJ(f6 z7DmnU$5OwodBG(6UU&LIS~w-oV*|zh=5;%PM(hcCE^R0<+8BCT+|dPXHEDv@?)uLV zWdATMy1d5xgZ!5H_ZqRgStDwx=Nt1sFy=4!zhlJ82qXSoWByD*4_raTL_q*=KnsFJ zN=Ms>T_FM1dhshx(p|><^*Hp3{gbOg9okDmR+fdVkNM&0L}{BD)z^?M{;^7>rVYJs z#5xP*gz|)~R)-q;?S5{LRi5ZM^>HQrjMwsLcTre2=GPhX72?8%g@M2JBIQQOsQx>J zv)`&Yt{t^|Yial;;wX6os{Ay^YOLIowV73DsWkO&exIz(TuEE8n~9~(xA3Uksl(KV zSP-nZz!BXQOZ`WgcpkHl9`k=?#BQTnnF916zNU?sw2`eDIF}gn-@+$+OqA99r3;K$ zKVDcV*(FwE{9@ki5R-K|9;IS!`2rMU{%SFVDN6hdVJQt&`vg9wj2J-T25aQ9)EKNG zpiFp%s-qZ&6~_FLb$9}gRdbmc^p9M|_^NA7uTO^2f+T*q-0$^?1g7aGkD4M+LJh*`x5WH zyb}@a(5whe$Lj~ewyMSH$jt47C>OiPtj60kDh7?uu4^b|6uN1L_=+WB&pilE(&3AFn$aKxgC>j(6m%XpqCb-DfB|WViLMr$cWLcszs6}E7FG(r)+%5 zWF<~vaqoAV>6Nh4?_~GP3)!ctmga^1??pe8TCx2r6@!%~LUhlR&B3GpO02kP=d6@l z^w?;|r^NYDcA)B?+?UILBQ}JvMg^Y$1?d^T3C!R@qnxxTl|!RvEQ_Awv0cjUsSF8W z)k$ii3j|`LTGj@uD`FE^fF4_<$Lf*-;+U>`@P6r*H#znBre22bTy=lkUiPvr!PU$1Kp zsMj3m=0qQT--jjLz4%0np21qZW`B@9s++tW2!h&<4*A$>ko0fl1b0`dnPeUCwQT-T8GzVh+wEG10K0KeYd{fpU82{HoBF zpM5=T$u#We%WiB^J)>JgTZ~wc&P;UB!321HX)6NJc7*zKuPYRb(z}JAM)MF=Fi6Jt zf2>KyqR`r?=uZfFnMRSiy)T0tWwVn_&nuJB#r5F)Hv}gS=+VlibBBSTK+XvWGQ9?^Jsid&!dMPyD6JqGcdnE~th6ouhDNHlWDT3N2ufm0?Ll z;kVG!WjI^l^I1DI*9tAOLvv6K7UWoFW$P3#lj4u0s6zoWej%5@l$;~mVGrJQ4OsC2 z8$WXXH`Opp)i9&A8Xl4wbjjP|tGRB50hNGNfT?b8Oh36?Gk0$2C&u%uX?)A`yXN2S zuFr{{10Nf+0^HR@85T#{yJK(L*_6n2moVVpU5}=04AbcA__|+8v4`+=Pr3bD@O7t2 zR@U=viVFrq<$pkrrd?O+^!U2B!wZ40`|_Qw@^!Z!C$UL>Ei2DmphB1>8G1B5R4K`H z?lJQD?d0lRMpIU7jmnbS|0dNB5cKJKG$%Jf z&P&SG_h>bHn^ej%UP{{WTLOhu^d?XDJ^pFFZXIu$uX}Q&C0}>-u?ohHwSdf%k6uCX+i&Jq(X7Pp8PmeTzoDHUZ;Z8QM z)#FE)jg6pJ)H1ihvx}aP#4nmZGDh~V3!#E`d;8Z8wKMO?;Ek-)5^SBWyhUBJ9+kZ` zU?pLRoPpqwCtlfs9Xjkx>FRF1z?Wsc6JNoegxg-larT7&*J^KV2>S1m$f^TvDFBO> z(c+iLoD%hp{30>5#rZT{3@dtm{7JHBoL?~^8tvQr-K1Dmms=&n0*qJ7;#k@psXbEg6v~PM9ZP*E)q(Mdp7}Y;!drS^!q@NnhW6u{` znmbb5zsTtjaz2rajhuKGso13Wm@=Q9(l{4ohxm-AsbM2O(KYXr2Jin>@>B!C=Igk}AxdHD!5dSw(uFSN63gj`o zf6qQHcT`X%hqu=t_HxyIW0-<*oD?h^7FYy7g&i?biCF3xf_7O{4sXVQ= zwAD&mQK}+Yz-&N-@S=cJ37{~`BJc95sWzz2}~D z?m6e4b8aA7QF?byIO0W;_7W@XA^bf`r8iy)!NZPtK#l=s@gYwi&Qsy;Zku-`)mtp6 z>Gs7Uw8z}qTdXHNye)4>X0}Q(0<9{$BzblT6`U%Le}Jjumz|g1#1eaykFpi58O8;lw$-vOIQA&V zWm_07!udkGU&_c0C z6q+wLh(ge&xjW>4Nj1jKhsaEg>Ce=*qRaX@3=ir3pEJxawmeTUzuc)4`=7VEqQ^7) zpU1QJKhHd~zrF3$GYdjjYBpFii!Q^SBj)!9S9EZGe*;8zZcJpuHnb#QPMg(sk&ry|Za7qMgUwOZO?=4=NL>&&h@D4P@k{K5Q<+^M=@BG~{C1 zGHiwhT`i^&+23-*`+)M)%K~^~v@kUjX&)EfSPNKRY$USmCnzJw8x13`=eEL%GQWOr z=@_5qo}!A&c+~uOkGDrfFXNW{L1$O=^cs=;K{)=r(3qA#=*KvwJcf}h&cUK!ej8iS z0qxDQ&6ux%QPDfcZ0Icw%w(mg5lLd3YaTwG$(`O1dkMc`+9oK#IO?r!Zj5aY4?Ws- z7TQb<9f+au+TXg0r3JrBtl!p}G!&C5>R~UGSL{>RD;RFDVZe`@S6&RZ`i6iR_upnx z!*IavFX8jmkSKh-7~Vt<3jZ2nck(Xt zwTjDx-cy$u<_>5J<{gR~8md*zv$UcaePv5#6&4I2M(>S&S~$L_@~jy>Q*F1Gpt0flb82x577dkp6yZbj&d_YKfq%R>Iyr^JWwSU@Gl z*cN&UXj@uF7wz*Y@yUf=c-AYi7ab3ZlmEr{*>6B3XE8C>98O=kM7h9PTuV^K-vVoK z6e-QUSnMP$ zbx|>(g-SyDU65D&{X(%1!-FMIah@U|1h7WFB2Nb5Q6oDYr!D$Q?c){!2XWjYo?@qn zpZdb>Nc{XWU<$g%M$!l)_exru&bt8E6@9ZX0QW2MD;zKS+%$YJAq<+FmWEgjep~!1 zi(TP(hy7ly-W-SkZCFbA8k<~v0W67gfD$|rR6-`XT+D9+9DY*0MX@|l0=0k2+NS+@ zr`ggEzcoLa&Y)9{uD-j0@2323)H@OFTF zW*vBY85=^X#fKb?^H_Sv;I>hs(J%S}ct+X!DUdipHSaX9{jvY7f_XInSzzwzi{?qFHaWX_n%TwA;IJC++r6O>AwqKksBd`*_g^ z8xLu>?>Q8|kpF53E@`{{4$)!czmnf0aL-M5;?i_?<9=o*WpuJAkhX zyZwhKBrX3#T+?>@Zg`6@`cny6uJFfx6*V&QRBQS83+BJ_q3frk_YnKjhTf+JJ3#N- zV7mllhoJ7ijo!cgDdyIq_xe8gKJs6Ezg>F&x~OT>G3mWXzT1l4t3)eyJ@W_r*)e)A zM^&Osvv?)%SN`^}B*KT7?~4GeGt>LWz=|}zH{%D}7d=G&t9IM(X|?|vu4#I|yGLt! zf6{KagJZkX`gX1y+hU?o8+!i*DsgN#p;DUO*YZm;|JAuRy;rsS`Zv=x7g{yZ)fdy&pP9()(_4P1E~Dc#AN44YIXG?<-K_RP_FD z^S56F77Q)(pYylRfz)+ofBTc2Y3ulJ^S9p&eBcPbpuhdV4*T0L=04r$_qYF=?B`(k z+e4F>Fwdt|!cOL2=!LTn8dPfXp*~?C+i+TFJ>rv%%6aAqmFDqUnZxfrsV5qe*NI%jgKYHTlwhO|(2(mZ{d! z5o!C~Kj}Yz71n1Aaxi8K$IevFw?`iaQfKNpz+;e zf6ZZ}_crcpNo;EZO>->k1M(s%tnT$U(#y-*cqQL?enX$?bD8U^`Ivid9iFcKBbLa7XCjx zlO98!hW0n18`9w4w*8LV|Iu*d#Z*DUzl@qi*>WvZ4>zS{;(Q_sSubA*W@SX`(*@6= z0M1a|{L7S@^MHoH&xKHPu+tm#q?DR2EQ&sa{RpHHo?K$X()^)lXY#;w!30_0OIzB4 zEYRga+hT(t15a>ym=@CKrQ%q{R`7D3x1CaA4*@}77ev2`Q$GZKus`8GS^fC{{0d2Z z`oov*UD^;HI6L$8w($I1-;X?zPI{7U&~9IUi|$r&c&Vx(LGpyTJP_P?Sws+Nk$EB^ z0hfyY=xxaOGczdIWjvGa0u*D@pi7R~f;+4wG%uFJzBu?2^LqsudlG6yV1K)>4q+%y z%hhwec;QWN!Y@oQxE5mO0Fu0ixjrIUF^m$}g{mwH8h3$IcADq0K^?QcR9|F=|+MrN4p)h*F73rETEas8%C+a|~0sYBUR-9JkORC{@-5|o%K9b>T;z!uS zN>;R5-wNxYGMU3psz_$f|=QLq_8OzX6xH z)IC&x?!p~QZo<`k0DqEGXd~2?n&Vi<&pLlEpy;$TZJ&|T`(^8t9T;f#I?Q2y+ZyHv z{+7c+gNLM=Z=MU9Mq~G)F+e;G@9g!VdN(qv1NP~7s$P3(kP>G<>v{F01|7|0#Z&ze z`AW4|Au7TdQ^ZrzFWaXrIJ(F7_N+2L%4-u(^&2;72qYoGvgn##UWb8xk$9?6Y*NNk z?Xy2F)b zVMYNsIWd>Z#~ilGn5y*r{4b2B!p`l~hue>*y0uL_)fIw0WZ;qZDbEV2N+OW010hua zJWdCIZ4{WA;|!^?X9Dp8{~aM!yRrXtijXQIgcVZtj;JND=JX*|eCJ;gQk4Uu{a+nY zML@wfKOIsvIUQ1UMHc6YX5G8!wc!#|%t9sc?MWJpy{OtCYhDi6Q2L#hsNQ5)gK* zlpRv_IKGPgj_4xR$q9cIMkHr78B%5W=ZJW!4JQwtGM;KU_UFis+KH#)xp*R;3cF5l zw0dwmWe(gR&;(BsG4XkSc zQBmLepAkRBaY_G@Ht|z@BozaFk(m7!2#NrSh@ay4lVyNbvL|Ns^Tbd6={jfplnrW2 zN=FFM;iO3ArnQNmV%0AYKgHGZC+)^hh1z((_%ity91&EzPcN^hBdFL>IUbA#(ZAqm zjG$sM3RDSAeHMS~2IW2Tg1z~f2&xKz87kASu9ZTf?SVjn(jftxd)-+PRGcBO;B*Al zcmzf{BB%oR>5QQA;J1vRx(0XcMo*7|y>cwt%EcY-&z#=i0zTVu~HrQax2c46HzzsojT zsqCPacJy^&!h^nUeqMbYm~QLqjTzHKT3E z@VcEeMJVpJ{Uoiwqt{5PgZw65WQ3r9-y*Vo+=j1_y1U44lBK&N@xV$e`^wv88Ev*b zjU7!%pG`|+PwOXCUWdEd=<9D_NfGXwVT3e98>A;1vEe~rlBKo7jykm>JHw3B*ex4s zEB*%WO~K#j-{-fj7Gl)d`9~b1I?_KgDqBbANMb|D+h8?aXjx6$(a~Q(M^itb${fcX zhxsd9&6W6*e8kex|HeAQSf}UleOWsCKjE`N=)8nI0XElgY#)xP=6Kua=b$R5el8qF zEd9I$!)>ddcO5D8b5ykTb1UD;QKayd(U?`H|L{S1pUHR9Nf3-vKc8Fm1@-e6kAem* z{oMYz)X!(($(i+YE8j^u^PQBRCf`XB25Y+Wq;9_T47&N+tDU+zX;|py5?6CAqyt|SCn(+H*qTGUGO_wIX`48=c)FTbBrgg zoa4S7z+FHa|N63HFQU zI}sn8iVwC@&M{zfqO%{_$~hiWIcG<{&RXp#=bfc;&YDopQM0vjz8^12iq z<4VmOLs4qp#ycI-%+veJ|5NmHjN(-KIUY$x02(Fk%{xBJ&*!ubulG+WEj%+WF#-&-4X{cHa66S=u?zN5LLoc7BttU>wvM9zK$vmEQ#E z@YDJk^@j<`9oQz$=o6|wo#z>ayG%$X@Br1b>x+=eL3#J|F3bt%mbIjEarQUj1dgJj zIB!@*23(PHza9N#Hu#-3i@xsx?NC7bEU4e0T?%hW_>ExOFrZJs-hITK8k5#v;ddbV zfG*!1oh=NR4<5R zLpfR+GVUt2oH3AWbBFoUqxm`2C)T63GNTMjIri#A$b>NgTi9F7;^aZwO9q7={wgYn-?RMv z=a=QjlNW&v?C2wd-;Loe5oRQkV6EHfIuhSm*s@xytkpwLYpdfeh1#Mj3`DDJtlla< z7YHzOm<(9vS z3Rd5M2OxL^CSL$YRlmC+K$w_4(i zT!@3ZxnZ@fP@7f3n;n3Bm0$bB8~-@(W$UV#z5+Wp>jJMp(_>m|y|%az|YDp8)1 zxVRX8o#q^jh2d_?2^V7l8-#2m5I^^Wd^m!!TwhR!UYM)j=kg(lLWf29?=YG85GQ14Z1B9u9eRxA_znl6yi%aN z(uB-yXq(?A7f@aaT!=vN>+qKRS>6k&Dw!`4>8eZg*m(ImpAGVsC^gJslHsqdfG}sD z#4y=|oAqAv0Em8U!RlYd&Vw!9*cN40jF|*+KKejh&>}Z?803X|tZt4$y~jkI+E|d~ zx~M6l!_YWNyohJ#82A?Rvz1y9L@v8h2}2eND0*vsr3gJowi^*iLzGa;H0DUI-Gx zcuJgQT~H>IEOrwuC^a=`-(s+UK@lpOGqxXWi7sMWcmm(CIh^5;wnuv9!Cq=S$9h5I zg^hTSCbWuyv!ci1RT443l(Sk%#OdbK!Qa?z7@R5AclLbUk?3B!RMHdm~u<%}qVIs^; z7d!@0Ynr%m`U_d>5eOcTx(6gIpwNhx1QeK9Pe7pwHW1?P z^YQaf_!-a@bx=rV45R?$l&D+KL!j$u(SwFmKgbfQbq}{f5jdv;j0|f4l}W-XBWu(b z@g1`-?z!6hLW~skTg+ms=us54whvRhuw5wUJb)g)~>3TD|L5(0GY zTLlM8_+D3Ut%3^X({@#F<4xzN8*5W%y^w$Zvj~6T0$!pP#8sqT(8ksi4MCfcg{m>O z(R_A&e$LHR6|WWLM3mur#cN2_(Gt!xU%@CwYT?)1H#hiRD{^rq!@1>$1KJ0RnS9n9Pk=$j6})GjX#j0UXt%%N9@5}@ zqbLWbS0g)Ut~dH|m7Qml5(OM|2N1wo8z*|=3}U~z7zhe#h=Yv@qf)E zWj$)HXU$L9NG@)c;HC*T>rt|P2^&1dKWqi{4z0s=CkhU2L%l;KsKc8LxH)tJH%C~4 zH}7w<>NdBj+r+v{QFkBfaugc|;!hL)tUrnWZ^8#SR}~(CO6)k`kFo?>Y@DRrw;cDl zKHMll?!Dqo2zFd=eOd~(s5zJcENX+x&ASBwBcx3TB>vdP$oLiC-U$Y%Zy{rVt?vkb zz=g1~lYH zA8dF647X#b$il{KTO*BoFLV2}dgutG5)kKQApNrfdL^@?fbth%i&hfmVzF6E5r{RJ z^)_G(m_brczR#Ux0kGkQ?=oaukK{C1i36vDEOqfkWyW7QjEziuyU9vT^SbShyn6C? z)FT_5nd6T~`da&&;`|I{MwoB&3=Qd<&BXEB>E(E)lNLJnutDN&skfjKJ4~MOr4EC2 z@ws*zsjr;zgFj1Gw&#FJXaVpLPV$U@1ejM`?FOW;2BfEgser3QdF(=K04uRFl-l`!id(^UCt#Z_!gKFp z*7`gDU^yb0AhE*;cUgV(m@9%*7fkDbm?!MhgT*ts!$XB-_1BQftY4kt(s+UJuUWgLsbY zSV3)T(75vgcU8p&z>1y$eK7KYw1f-I8_)@B)lm)K1(@=|r42dArBA6?pCve&_LkYW zl1yw1R1^$E9#>O|?m-zA0a^R)R8Sji_kZP)&V)sx0e_LRbBEgJK%^JrLu`ET3Vc{C z>Lcs)na`{FRZ#}&$=`v#Xxaft+aB%<9U0i1q?a9y3D{76UEWfb8@iL?-A?n>?Ho}8 zdK`b_%pP3LJMbr20HY$_6E%}F@FuH0w~77+0zFgtsXRPSx%sCX{*ZJ&*^!^KP>fY7 ze86F^hzbb+xLK2WOiI0se$9A#cAi=4hKTCzTvJ*o zu1}U0;i@K>J}w8VHZKhboQ-Zh8f`ur-G(Tk<`T7_QB4d{`3%=`>pIZ7_P4J6tZRvN zEw--0w5e)CMr*f>A5v9uFNN3=^*|qz3rR>^M zUVHYI*WP{QweMBDwvtyrIp|gHe|;Gz7Bbsj2zKuX(G+s*iO*!o<|Ji43ZW25*IR{? z-=WNj`kQy5778g&-v1`9ln{le2qAFtJCGR2(UT{tN8WWx zdybwt|h*);O&n9H64%m#=DVBxP!3)KMd z)LWgxo)fN0G=jmlhOHt@p*DP{LfXrME#rlf=ioh=zsr{Pi2M#|?^3cTEtABHavl!I zF0_jgLJ9oNw0sKxOU6#F6a>);`!i>f--m!;L7O5jKd9X`7k-QLAj(lZ1nSODos+%Z z@-f2z#6&!d8J8=T3wyuBVw~k*e!PKl*Pi&Eyy_it=}N9iMz>CbQ1>xrE(hyZ=lNW{ zI@faT>Rfkl)K)VD7&=f%w zOfO(qrA+f5`A%=rpS1BNwaIUV^2k~&K-mIdCSN3^zchYy=sdeQT{5$joF=5dd6qBf zG>7#s`3B%Ctb8rz?brgLXx0P+C&j7ni{CA#4_7mSKgpYfq}L#7CZE8YtoGV!A?Xn{ zQ%b5kt%P7jJyAbJ`ynMhVHE6)5?kW4@Hiv!vEZMO_-?|ttq(p6NVE6np!d%8HCx`R z-aQ_O7|>S11Zjtj+8(54^`%_lpN7MKgMR~TvFQhHJh*|&&4XvFMr4C+Yd5=Y&WEq- zN0bW9XWybOXupDmYA)I_&$`tTBt>e~CYf5b(Mqkl!b+{W5)CYrk=r}WD_6m=K4Alj zV7A*~_Qubu6FK2a5uL2XtM}(LL)_3p^D?1a{y$blUTCh0{H+R9dDjNyf6s$uXX-GX ze`Fs#HQ9rH&y#xw`XbS@k?(=%>=z;z;Q7L==U4OjV|Sq=i{@b@GDhb$Qfh5;?q^t= zdA3q7*vt9U4rdW9AHodq&9Zt}G3xMC7ch-B<0Qj#vHWSVnJ8rUa41wr`_!wwk5mMB z0Lh;X2s~iS?P~LhKKomEM5e)J#0*&VA9)v?#9=brahnvo_V7xR;U}`7_h(i?&)jda z#$L!bmVFR1S7`$!uQ3hKKL6h^4Yq(0K)`El8eE>9D$gbf;P8*V+REWZ?sGtTVI4#y zk~vH;1~H|u4k|U{g*@>>4dzChQ;WZ_V09X0l-^mk8KwbF8P+$MKlRZ@TOj;LuGq_mN?rR_)pi`9wHe1_Y?(YzTf!3-y?Xdbw^zF!M`aCo z5_>IEaflX*cvjR@oT8T6g~hW_R>2<`JlEUp!l2g2l=}GRq-y4 zE)p-Q(G|Iq^mD|U*7;c~PSve~EFw&A7$Tt~4NV_`rl6CPFF6=9l|bZ(M6G0s>w>Vr zo7cj&Z{2=b-eSKl)q_P*(PT~-i@&%t4YmtT>_SC{yxE!`-c8|_{BQ#joARLGG!HX> zbg|%v*idc750Cb4pC2B7hy3tU@UBMl$%fYakS<>*M|YebRyI;=>$Tj$4^Nui@$+-= z!%FbO7VyLEf*&SM!4KK98_{?0L-|50e#qw+^Z8?yf*<0y^o;z|TJpoA+58YsZTG>s zeDgz00N<2Eh7#~Y{1yE0DT=@`?pcE$CImm+?bWs@H5wj}AL5CFA5QOO^Fx$d{7}?? zgeGZ+8hY7S$q%oV*PhqOYwsK7weL`IB|d}o3H@_$1MdFqx@V*cUxmJTANaD+JL736 zkrhc;&~k>TPAyhNXgH1w^lGckzXG~2`uJn;MrE!&pSEZks%dj71nA^DI5;baEe=aR z?PVTWYO|v)5xBiPSBJVjUA(A^7X?c~q3i-#5`GAlbQ~-R_vVzB1xv!MgC#8!ED6)c zV`XVwC{~(T)2n=S!kC^Fk%+ZRR?r@W`q zj0sa#x!c8>s^@5A_ZvC6Fp`~I1z8+HY&bu+G&kB@72R3|29Mmm+p4_Uqry6-JuZx6 z+Edgp{537%^Y{r^@ZO;RgP>~jT68yLOezA0rhU#^HMkUZt$H`0<432id`ke|U&DO9 zkWP4By|A=ToUqO9v&4HTLe0F|<21y3wWnx_2efI5Ps0~m^cH*p3G*E^b6WVm+GNA` zb=+m(dtk{a;9GSD_}+tN0AC(a(H?w<^DC!-@3XM;p9a3F$OnilQIFDY?}(7ZrmU48 z82kX%0=e&ojN?d^J=_IS1hL_Ki$#3G#+<@QX7c-2$QL~pC# zZD#_UC2bE`D`o;EoCzskaz1|90Et0PhN=hM?&y!*IScDhH-gk#o}7gfaU0HCI1z^S z%(T!5^>BMrf`KT%UGd?!GO#fQjzf9cuasMnq{UUk^vwzC16&BUuV3L8V)*~rX) z>-o4FGA9I=wF^6g%?kIASA#-!xnT*f)a91Tf@{}U1wA*4g1sVt8srB!M%=#wZXWk8 z-G{%W{P_;7+-vFR@ekar6V~)5x@2sGNgc-W6We8-&2-~HmCajd6^D6UczbN7Nqr{- z64YF8)ttN*wszSXjqYfF{Xf{?8vfaYM{wylv;)^&bl$*=(%QZoH)~7q=v~|#zzyHL zA8m@dsWx@jvF-<`dysYC=i7AjSVx0BT~C1KboA&9lu=^z_d?lwXc1!;BGgi}_%ITi zM^DFZRQ)9+f+XFYmfx_Nl~L_fSbqJX48Or9Nt)jf!K=t>ehZ0A;C9;HP5i!bnZ<8* zsqt+{e_2$N!!3Ccj~y&>Q%$Fb#Ql9=94=7+`x*CN^;iB-?!Wd`dA(tYy!KteYr1{Z zXcR6#T(;T3bA`*!S0jMVn zUmWhMhz%A1Ub^ALGutUY7W0zrd=qdrIN4_nMw?Bm*jSg7LRxKgZp@_){msEY@yc86 z?gu;g=d1&5DV8%63qxp==f>bCHyNYL?(Pu(l=Rw?q1SH)#JqI>4zHxwdt^cHA6W%G zr-_2Sa5pIDlwb==_-EZI=(Ptx_`luyl0~oC))&z0D%h8MwnMX0o`=5hFQeBP`_4eG zJoTzAy*^%ldimf1m}goccw6}(3&A@iA9Mu27wkO+erL%C62ChpA4vTEc%ioh$}!M6 zZ^clF-)rTy=X>(ndz-xW-Nmbu|9k@eGniQY?^UO7s78-vK2>}>qf~Q6@Wyw*X%?$O zw$vm1e=Phujl3gQg44=7SM519W>G>AIh<^H^VLPC#V15_6`47}Ijuf}RTOWgx5qLq z@n&?kcyqBWU#I08iA|@MZ@%|&TWo48-?W2GnfcF>Z$=})tgJq;*v^b1;LV+!w=KRM zxm3;MGlj>8n-7N=pqh5usrFb`0>fF1tt69&} z^04g#SB+0-n<+TQn{&NY6?sL>PRlhIs%)OyeCZWI)8e#YI1Nr6lfA$B2|C;B7V<#e&Z?OCuTBo3E{`>{hfe-*sVXD zhF!Y}Wo+10q_SYw?{u(p>c94$GPl1@13!VogMsINrz&?vJm6X-KuI(n*A|o(691+Z zD8ENgX6u&{pVQE^@Hu%yyU+x_{X6lo->xi}{!8)k;syUd#m5~T!^hob7VJJZK7vNi zh>y>|YVp)GDh5x9iob7#iuVG+D8HQo|3V$YKd1iv@34pbd}n4=%Cd(*EfHFH6_+NS zsxG2T?e*!`;C(Gw=8m@VI@oi@+|e<49k1AVth4<$9rF4|JKC;5>}2dt05~nLw^gvy z{5Ff;0q?s-mhyj-{poV6_pPlSJTu+W2!9IDV}CHq-f=Z->B9bWCVlXU8>EUM^ueF; z2lT;T%Igikk=MR|UAbBh3SsK1%5MH`adEzJd>L9$RC{Nta+adiFV-jxQFfbbAf)wA3 zG(`_PDKofREaUwi1s8gl)%1Zi&=y92>Y(!D)X`5xzK#}4~XZ(!3=O3o3HW+8-!Xi92n>6o+cJp z7qPI|cZ41Gh>xM!yRhrW&YRytmy(a7*V`QI~B~?A-%TZ?4tHvz@m6w4Z z$kMRKeC`>bNOt;&pfr1WUCB^yJ%LTC=R<+}B?*l5| z42hD+>Iap@n>+W-sle~}-cH`?oy~CP_7)_(`UH18l^@-HiLc-U9(5V6*Bqz7?tLBP zkyE=VuFH3>@b09{o3#DfLR{+fDK@x{eM6nd*}%!!;$ zCZOsA<+fmF^i*yO=CRMhp#wc{GQU2Lo)W036?+@HzA^QQt+`U3j^(#yaf$jeXLts7f*GLZdZH-2fgtv8N{&5X z1bex$0Smpg7jZSHUlBVjJ%Tu=A;d27WC^p|bL{+Ha|Q^@{zk~Sw--XAj>*{^p~)|c zr?41{**vO`U0Ji1^C=#q*jrVJ-IweM{VE!jfF6K5$AsSVJI93jk;=I{2$uc(4JnCP z(4GXjj4F8j5N*`SpW#PzXTCSSwX-)`>COrFfXF7|@ZU1O^k+`tCQKnt5Aa5}4%8>q zN4MvTqcw04#noP;&{Kd=m`Ia;t6Mb+1KOBc_ayziNm`HEN&2lgyk$&nC*0)nCf8fA zNhIs=)h<`pHS3|3HOpmoKyZjaf$y`Tkt7M>eT__GbmT zu93UW-OJwVmIEu)*^p^9yD#C(aL803I{j|wR|P=-fHA$;%>5(u&rQkYKr(M)SPAzE z1IDoa+@Y2WQ_#5T5FbLOL<0~jBIU2)WvFV?2XaFx{G-&UR`!s9K1{{di#y;MHahav zSjGrYVN|+rG_{MAMR$={%|(1(g77Eb}1^`^UINNkfJd4L&(>a zO>j<`^~?MmEIauvZ$(dZn!L-=GXOxT;o0N%goX!;$-I6ZH>vlhJq8dhuKSQo8B}>u zKp%yDUEzR}@CvxAnzzVt(w5WYhdc|ZWZhr!h4=8Eks;SQ`b&HdEVK=%3~xAJA6L?{ zr206|xtns8a6L|f>yX377=EaabBo6)QXZ{Ff$-Z&?!#SBAJtEL7YmNIh7p+iz((Sf zf0RdP`*a4sG(~#J{rHxK=*gRu8BFqE)yDvLffK195z<5)+Iyo6_s+X~11X`0+jSuI zBUk|Yn?B6kgr)dwQG1_#@Q&8wC#Ctvm|Gb2crs(0j-KeO)Xc&-fdNVkG9`SJ9_|nNJ%n4o zKFJ-Z{!|d*53n4Wum28MhKO|pPM2T_9yeUaS(=>uP(UBqUp4Buo|hd){0XFwryOl&|DDUU6x+;gxR`&392Q{kHcGJAYmDBnJIc0g+K9s{ReYb#3@O ziI1w*r0Nh>*G4``Dcn!cy>N0vJMqXNRU3xtql$4xn>*Fju8%TLV|XGDBFu^0W}icN zzUBYnY;T>|pIDN`KUMu^v|ofUxyYf^U!i|QF4l*+X@!8ramoea5V7Rr?5n3yTrqJx zdorjGE7F$06I+Xu)ZPu~1NMo9=Por-5BkQ!)2#Msh?KVC$2RFs9jGB2-++29L8^%E~bM!2*X4qv5kuL0iS21qKO3lBNwBXX)K6e?Yv8qG=+Tp)s;3<(2!rVw!+m^0-Inq zP3gX3LDs_qaGPw8sf*(uGwq8-XpiZCQmiM)PxN+VCd3^l!-qVVU6PDEMdhN3FljwM zurZws=obets-WkfQnMd*yo<>Dq1qj%eaNyxNbg*85|r*FTe8}tW*(lb4=JHnqNh3S+%4Z=zH`>MOb2c^SP+ae4<*1=Cix(&6RRz;4Yk!eBXS` zXZlpin(h3yF=3wWe-?y&6pI%j#r|wD&-(Rg{RLpw#6+8J$XZSxD|rI4DH3iVob)CJ zdIQoN7$M#I1Y~Rct>G=;&=GilJ5E5BJ09utHs=#~pk(I>$TDATdcV6Z{nA?$ z^gBqhN#P zoPRAS6%{%(vtL_zYD&ebqSOqtC{@ZMq}Nwrd&7*ycOli$F%jwuijD42K%R8N2eAq_ ze52PIuQAw7Dn+T^=yiZpDk)P^sifF8RO&T+Yw|&*Zu}W@Api4GDOjO0gK2Nup;7{k zxHBPpQ>Kq&eUUxALdW(xDLylEh-)^Wzn5sg=-+F=3&kR{WII%BghFhefY# z=2M=p1Ak@7c_CPLXg-BAm`5txKQ~Q%!^jZ!qu;^95oZA#_ zHp0^J(GT914fAaOWgFf?%jgKaE0?#WRsrw9SZ)dL4xLx7j~eaM?;jJ5=lhKbSY(Ec z_ZupbE{+==LVR(2cZpBG(eF84(Z@GvQf|dXJ}$RCJtGS%ihQ1L7gcoic}9XryFus- z#J86O3f?M%M-I$+zSWTM>luZ1n=5+4{opSAQEIk>koc?^^Y~VLdqx9Q)B3SPB>q*S z7~uf7*YyF74ETsrGZ#(NtW|1gug4kF^;lL{2Dz-#|F%jMe|hI+r1tdJ_{aM?|EBTH zr>&4VB>Wmow<(w`x99t{{jg2udp0T~H~Kx>{mRJgeoq{ixF@cJupTCWLQ6z}%|2P$fC6>aFmZyk8(a-Xy{zv*9 z`mcNA9|1f~LStyk(iq~%rUQcOUXhUCars?{0Nccz0l|V}HlSiXF=(G_9kw3oy{?t8 zy*GJX^?uiJuj?&NRj6Q{*M)(-=hG`!Vumr+<38<1NDf}lG6)OHAUX)ip&mc#wV`f( zXra%u(W?ZPYC{VlC2^3_GZUIl_IdWO#WFX1T3!!oN6GNg_!rct6$L!`D2^Nod5$QtuK;tHG&DON1w-Qy zPKsTpE#|!#ty~YY^&OFkiji(_E_Fm=@eFt2!9o2mfk{Vvo0suzIEo!Pop-B-oR-TzMvG%Ji#oPUghkXlPti^ z%#$pEAuG51Wfu>82M>v?Dz7;lxhTyKTHrofrWyfQAYk<=cSrVQzLHbo=oG%lPKhE% zr@q1JEJUYp5uI|vSM(3>ivHm$`zLSEKiquY{^?Pn`p6(ORTueK0|)@b*F(k+VVhry zttF&>mGWm+{?~wB(}l1>{;lT8e!1CsJuIg{lB$Og#m1T8&gs{n1)GN+#`-4qjhMgD z3fB7!OCwUpAO_H%<4ToJstf9a+Em(zO2|UhBzGC!#V>pSO^R*2Za{py1(|>KP4>Ac zoqu&W5U`cPh#f*E1misbI5qFHf%ggU}zgQTHrZwxG~bhCWvn6o5^Is2#~dr+LapZn6DuA z{Iy798ya~@yfne0k-y@XK<%B(8DQ3uMD7wq1_(yrfB-18U~wU-G!$9eWsit_-PwE^ z%T4NB`~g6?{f6sKGl0fHET@XU{W8DZF1_qEYkqc0dKq&I6421NmFU8$@M86Wt-$SV z7>aX(qS&npe`j%jJTTt{%`t(uj_v0k0c{_^{~}n0VHzCGD^egLohPK8+lv={?Zx9)*+iF>ZvWYhwzJ@)n`#xINC>PsQ!0SgZ=GkGa{}%Cxy#tcp9$ zZ7>!J|CgV(+TWD&4p6faM$o*1MK({#H@8L!lo=v5TVh64E+Z(o+85mjcG^Zz4mC zZI7UbNN4jBOsuL8LXDfI?nV~ukmu6}69H`9OgS6td=Q&zW+zlh(tn99qd>}QsN+&% z6NHek*Zcq@PSV{Awva|1p5sSW)k+w?L0X9!1FZpA!)i{QHK}ey!nbpv9M#GZ`t3Uh zdPRN{U+vbwx*eg|hhlsf$UbB+r@oKDOjq@arE)Nr2aS7|7CHGL#J+&$&oM`$CnC)- zN3<)@l7KMt;F3V})BGvDI5z|EoLx&kgx)&n6Q5);oN};xDhI~udG^4P+))Z7hV`e| z5;DFAkH9;m{)HkHP+p^yl(DgPkbsIHNF6Kuy#=C?1M0e15082n3`2lLk z=QSH`YEoO?Vo^|kUKC~ADX_g4P<(L9^4B94V}wyS1)TqF;2okfdO`8Pt&W`ug-OGj z34e^3XXPf5i8DATQvcveHF_d{iWp}Do~L5mFsylseZX16N{mU9Io?0uVUio*Ne@QJ z(7F)~;PO3h$Sa_FZv%+t*s(m9<#xQEbg_WmpPpTOU+o|3=SV^6(5 zbeSfM76MMB2`htn^ZiVP@cVL1R6u`bG%f%{`GfR)f(<1V7O=;ilb*3=fh5DXsCt)x z=gp}j0*3!&FlL7Hk#M3o^_4mt8%=fs3}_Uua~m$&41c3t>J>|n>-|5lsoOc!`Oei? z(r~sq)h|M6>w0YIHgKw_N5~TenS(ZbM?w2K9<}(Z4C# z{X^ewLO&qMxu!guydOx>J^?o)FQI_{=ir|rzX!7W-G{N+474<8;MOr7!9Vj&g?NM9 z;nsTaX2|o&gE$)DQy7CP>!&K5lJg~pB>yCb+z!-8{*WEEm?LJt(VBe-EO03vav$0> zUYUk@4;io2VvPe9%*I7bNFJ~tHCfDIoB2P?U2!r0A;{O`d0zqFe4 z9?-v?!GPx^2EY#$7yuCzL{7+a{%LCO@cbVRgQJW2f6X!f=v-L`=6?X@Kf8ZU4ERm* zK7j$JlnZ)5->R%{B^->>UzgH=aE`@kQt*xii?vtMW&k=PKL~t)bO7TNrGrv1??fwn zARlkhviLag;UnS$TswdZhlvXm1BkCw$2OpL@;gKfoGhazAZl`wo&KsY1=SEa+Sr32 z+~KScFc3^QX$PBNY8l+@L-T|B<;h>;4I!|l^&fnpKB$)nynzuZl3%tt>BdyUzcpC4 zU)9IXS7R%}w}K~2dI`o3j4}pX7d`?hD$oZ>W%jp6o`Eag#jEH;aD{t>(PIj3)&M}q zI)@9(HVZ%|e}_TgThHL)#8(`57rZnwzX6<2L<6?Rnht<9$$sD42rtK8|AE!yed;Ip zgF77HezbGsIyB(|AR5hEYPt7Jwj1sfh~CsW(l20y-v@#++sOfQ$30kEAY4dz@(q66 z?1s0{nUH=>fXAa=FQ!6Ar%n)nY4`;O`HRAr1+`R2zYh*FpOsJk7-CuGIqJjx6fT}v znyM`xMjqvi2^u>3d7P4 zvD@ME)kGZY88C8e<36=d*Ocvl;w)C;U6BS-skt75#i`N@iJ)PGWIp6JmplprNYt|l zygmdL!M)}#l+DL-l7-lR7xLMgw-U12rqq|)8Qy4N=A=;2 zXihrtGZU&>CfPMpN5*$jZ>EH1|% z^N++jXbeWAnQQE4Aaxjpo0xuU`Xt;#a1}$IurZ*08}i}i@b&vzhPdUK8X`L_(kl;f z1DCNg3O5bSPOkh=p*TVVT8b=$dpQV^DTjpL7L)^i?-G2Tih=P+wm-|Qw@AZoK)X1Q z7%FVwi>a8H$1xiTq$RSILiPuZEvmnjeba$;C8G+YA-y5xXTl=qzvyXKgECw~H}$$R5Z zX_LX?g^-*~4a5aVK@shb$2*^wkBR#R=q52lsEfEtRZ$xPbrs9%kP`5;enI1J1HmTW zpgeBQui{ishRi;sAE!?>UV8xLs6%6ftTR*8`P(#JhYkd-$?T#qjIBpe*|ypelQu3+=S+T zEw9Pu&-VV2^?h0tB`bVJkT96W7MesqCbpq##JtG=`M=mZ3rIcjbpk0`DGi`m{Nk3; z1n7MLq-~6#nDenjR!<36j^ZIW#(7WOXSe#QChpEYO4!sG*d>V zI;(4TDKGaguRU0`-4(_pKOoBXsGb8#jB;1aa#pP}zT zhrO{PKL>s7n-r&u!9aTv_Tyf5D=%N~S*65&M7GhAKz+4kMPw(;B_Vw}SbIyj>ppMG zFt_-F#Q{e6Zx0M<4I4qkh-~jPe0-Om| z8_Zv`JuCn|R7zehNFb3s2Uju1SUJ0wi1B<+Inp`vXzwzfE5`+mUjFO!EDmIeoJH0V zv$(^2ne8WH{jCOYDlqQ&4==y>T>53%b;HqyHLv1x7|e}g+K%0uvtJgsr1&f`$J9%0!nb z>b2NZ58@q0QBR3=OFxIWhv&)TI6xl8UbqL~PbvP`n0(1kpeRE{0GjSJ&%qC()n9SN zB;H31h}O44t6_WywE7s=t5MkwAB}{14!l_xSEsjXK@Jz zWe&$3rT|pFaRD(QRD}-%b>KT|j=x2~XM!Xz-90L00M+z`NK)wjL1y|bz#6$1l23I5;ShFWJg*A5mK!AWSU&3z+xU7|h_|^lN zZwV$YzcoBvbDmWbAKs3_n`H!d~@9#bhZLfBg99`vOM&SXDPjk?4s8tWEHDTv95kDnWC(Lc~ zr{Duo1(N+(%MXDHyt2q{bBt!7vPBMg-|0tGyF^y?T_l@G5<^4hO zOp7OUCNt2x&wL7(bWOHzG2B4<`?rbjC(yzXz9YG=gUHu}?J-?r)kIIf&)2UaDqn~~ zUe6I%a4osKqvS@sNC8JdkjGO;(+muqO!v( zVm-u8r3gNB!cTbe?IpJLVyiC>Afq}2k8Dljg3zgt8gvggGzfNSq zzg&5Fwdz`>>eHatw1ffUqcWug;{RkkSayAtdzl^#I)QjV^eV>vrhsRk@}w}jr1uYQ z866aEl}Ak9cPW*88=LYE7o~##dVf&kVHi=S6qysTn+fZeu&_W~!Pcs@$VwxKFr!)e z9P~Z?j{eHH+qD!sOF+ado7>?Km-?`I% zhnnzVq0Qfe*Btd>uL2swR5h0ynFN2feM{<}M4ajTk2O6p#4YY<@vh>3;C5kR2nGEr zRezYRLDOy-T~7C`HvNO?vMAv{ipn_&Kcj1L*~g335st29v|5Y9Flv^I%*e0uY4|)< z5A9R=%^ZBaMeWn5k3Fg_=P8}hWkn3|mH9a`FQ$`O5#W}vg{u$mH(9rxcpLEYE5_Q!w2j1a1vkr#%w zoslMr6A#}jW@P8%az^eEKdCFWnUN;54*j%eBnS7Lk(1&D&d38L_^1mXMQXe{3m7ft zU_6JsfmBIBM7Y&G52H4ZzmB0;Y~<<0k3B&nQctO5-w#A@ZWFyZVr~{c+w=zVS7p7~ zN%jV0zSn$Ed}Xis0DBW%Hr`>+k-7=UDPcC@Y3iH2YrKOJ1tbaoJpMd_@1(r40L&v{ z{*E=ldlTlb(>3e(^KI5VpLenJgL?i9^UIjaG2F1h?@4%^vClg915RR@>K8J8M+qXJ z|5~LsY&*pyr(?dPM?~7*2p0w64+A9lLjEwTu;OR?!}JtRFsZZBzA(~XEz2JUD>l@9 zIK>oMPBG2WDJHYNuyb%g68;bi9R3h+*1;Ic0gZTj8}8ILv;1MK{>g1Qr$0={c!N6+ z02u`;fUJ%xXBuS8^B_QGt%nx8ZnfYw681KS|IiuzM{NJ0XID{L%LduAsdFUAy4fHD zKZazc)adAh@E_?Iyy`7@EzE{jNBl=@{~<^JoFMzJBs0dzb?ccK}ChnvUB@gg_o;{k0=P%~8g#8!L;e;^W|1^;ZMEeq;DC<$pP5n6^tWVgHGLFh&E;^H zgTHA|{|UV1&IkJmAM$(dFR8eA`e483j?#+rrVsFY#`UYbI(z*@drA${uwegsUI^(G zApInNj^7r3k=g&i(SPt~{~$Db{`718gV3xp9nr1B`guJ|D$gSRTg-c7>SiCLS%dmH zKbWC!8MPC6g}$ZyX6mY}=YTM}kq9C9p@-F_tmh%^1D`hQZh$oq9b5``jYrYf#VY=o z6Ygf1>fI$i?SWF=U=>a6hy86$^#SStn>rZR`BeS&I^{;;-9PP0*QzuL0!zVYB|F8a*h{vL{;x3*E5;_ z;}*#K7h?J&hk~9JN-PB}Qwy?8D-YeIw4EWB%>@p(EPMA9@b+Lkxo61Yta~G}VRt zMmrevY!4SRh3L7{dkn&Rh10tub!eqq@b93u-By7dF3DC>NOV*>YY!RNFr*xmDhQtY zYg3_QU_};tPg(N?ekxXK2q1@2mw=u$GpqkW{cg~gAE;tRaKgHOHdZMM6_nT=kVTSA zZR+!!s3`UlgkI0Vin9RVu71z{iaf8UzOq(ZFc?q|=n*Wr3y7vT43ZbR;^;D=ro=5( zpG9_J`bNUYGZJ@b7D0zwG%<^4-lqgNfEqZ7x@Z(%Lm4P!LX~HYGIEVJwg%rtwBvps z4%5chMR=osW~&)|;EXevre8UK=X=Eb6*2#7w@m(5Wc;*eFM*viId$i9>du<3aO(1> zyE%36aO(K11&px+v7;9%x28{(2~^E*jssMl1)*n<{#OA>{fz z2P%--VuBmdy@r>DTnL-Yn|lIX2kWadKOI5gcoPJL9$4_IaLvWKMWEpp>`ts^aI>n# z@#Mn85{{$E2#{J^q!O~)9MY6eGnlbfn$Ua$6gKB>3@yA6_3Jux;{ah@LIrv{0nxY|+0xE;a?qNtC; zV(&POk?Do?nK*_gH)k>WmxJ7RGDm7a`R($_=MC2f1Ir?{V$>s{Wr&n*y1{;WJ&hog zxDTrhx{BM4pqc0O7(43qBv5b^vDWj^J5bCgTI3~N^vp{3E0WG=p@lMQujKv+ogNuS zO4Z?d?smX+t{QuNox2EAty5&k8Ls7SM{*zu2t%&dHT)nNTrR5TzcCdPT6>v*SRPW@~tN(?l zdxJ$aB0~ewE1)egeCJK~8-qPwJi$%SGd-xi$5hEdNTvnY?m{Vo7uvY2U`GA)<={ISM2vN_`imy?-7v!g3r!Aml2IdEg z-2%!-ERxXOuNZ&9q9^qQ$}dd1SiKW?()|@6K4c1j+c} z5>W9KFBt#D;JP90m@@N)PeDA@%g7)c+y;-5|A{BO>ee-CT_3WplW{#2zcJCAX7oTe zRBcwVxGZpsi&*RQE}BTHtlwG)aqNjB&?T_)=VI~qdX_6s>=eHl%|6tvJh6$IhFl|6 zV>*Vns6SdljsyLMo-@ERan1|Ma!{7>$2A^uX^S}3Y*w_XqZNG?i*QIWavW1I$jE=- z8@G$vx4QkFCCU@SP@DN?t9P)TCB-1J9}WloP%pU~s~ZHVwz_&X zmldDZ*qv9uw$5Aq7Vm@;=~gT$$FSlnvGstP&m(3=iBY=sd$uXDH*n*5xKNq7fHy;n zl$p#A0PGWcB>Ff+iP+tl4?P85@BVtc!L?5KV?XA9Q8hwqYQ0@1 z>HZfcX)<1WqG{+U*SiGqj>u zMqT>I^Tf6JMXUXTRs**TY6un8fYPi1gIg@{FSl2$c^9$(?5SD_a2?MA6wQj=w_MFP z_QDeV9kMZX5Y;vXC+N&7>5CyTuj%Xy8hty2ilDFdUeJ)X`KPT0VgxGFj9FZ-vVVP0 z7=#B<9IU22co>mDd+YquuEQ%(FL9*@^^$S*I}A()eV<9sLEnol5`t{PJh)ZINl|J2 zM2t%#=AF4EMIR}L_Fx0n;{dll%-aO)M36{)&wBJqOf>>P_(b$ASX9)| z`WS~j<$5QF4Gg$u5nw3MNA(I-Oe_wQY#Uys%I+rzmNPu_k1J7%sCf9LyEFO&P9@GO ztswqPdlw)1ZGX{VDrAWDC$l2FVzKRoJMyBuhC5HpBkZ@fa)gbsIJ57!*2`!sNz1pf zWJTP$8UCM=`G(rjXWpU9SieA9F!KdJQ=pV!BokcKfKR+?jro`DT#L4Em?R%bKm*o$ zJwG*To9U{_V&=20uLkPSWk4O}txyC95a&t#?GghjC}8GNm@D`r;p+lR;Q+k_yN`88 ziA>OsDPiXd&gBZfx&lV1F^O+#``;83&EZZjEM>dH#YjE`G^l?~M zp&&TwVcPNCTru56=%huQPLMz+QH5R zo*pKaO*%s}#}*RK?;WDBqi4PyCRf@XEsn{@Z67nG?zf519DQ~Tb_^mShr_=hj>PhY6}G6ZG9LR~JFfddN_E6TRQhsWCh_V>mPjZjUgDLn}Q ziyz31{tOmhYx8k?RP2lHL>jj}j0{L5PWFi0mHa&qjBdPk-a~b{QwTweeg1N4swkL% z5@BRDG?CXPFGCmr1LxfZAmm_9tYkKfFFi*n3&bZX1OEH1*}#KpW8vEH2ixB_7I$Xw zF|rV}YSt^{n#%fAS=w>6+@8*2w2vTp6fR2gKsdIEbOk-XobJ#4ku?I+V6GM|pVWb} z0W`dbG6QDJ7tlW)^DH9#)9J(n433rTM)XuP8|R}?#bz8vD+xj1R4zzGgCIJC6;+*m zU=O_x;4NAlF-m5~29|V%l;!~#6ASwzbx>RexYneI8ypy4T($)$U~N%D3_SMW>fA`z zdh%(deGSA2TJL-a~_b&RkF6y^B+HyKk3}WTD@UnJDHzPW;E8v4*Ax4^2FXwo$*hfUB5LP%taM> zBMXdu8v8P-}xmV&h8^0O&l_4SNQapH$z+;Gd z2z`sK15_wqUp^6aYx<5)w5RO0S<5kCPCL!HEPsA65?N0p91vrR2CLHY)q|}^6}!=E zJT|Kb#n|yxr5=@d;7r>MDN!}QGOC^>9S8F__1Ai>u z-IyZtKr_|wB5V&RW6Db|^^BD^5A)PW6$>?1Fuw4)V+BKg>9IU80+~3rJa^hnh0<9yFz-) z>G?Z`Q%w&=8BT`m461Xg`PyrcC0~V-Ika#6NC{A`!SfjUc04q21Ns|beJ}2&NVyo} zEzO7d_}y;stLku0!8yzgef7cJjeXw3P>eYdwCZ}nIIN-yi^r2}{nFiy-IwvI6ggoe z7k-pl=)w=ju3G;d!#W#t*g_cQNvm2|{9N1u9SKG^ttiVKQ=dhegqa zJeYT178fpVR`eIZBJl&QO7tB;{TUwCqc?hb`pj?HQyCJbFeJs8%Q*Z;L)b``8FIic zYCtT8Ia;X0ME6oS#A|8I#S`>#IV%bu{&zzI|YMO7_YkVg?-x$_+{V9 zSiF(byV<%s+I7Fe!X2%hLHrOr|5LjaM$VC2nrr%-s6(afVW-46-HGhnhX01ARf&H} zNdF+GzDd ziqGMX;T+ju+mymN59den!&dXuhyzOu_F-**>EbN?G~gvh=Og;Kcwr%o5a`0r@G7t* zb;*}9jZX0-SM8;lQXX^4|A++~(znruT`p-U+kQacMVwdLM!ReA%WKqe6{i~yzkuh} z6SX*uFkuSiyh_(WxKf6fi268;uu{TilhyqSPAJd$_Hi@sW*#@Y1{SfZzykNYDOs@A zHd#l(yR)+x?cel*I2{s_#64#kn~B)A@$)PcfH^s9g}1Mn*p z(D#pd4iU65L{99aYsv5gR@-EC?WyMQGjN|p+ztl&XnKpBpfy2#3moNyJ55{9X@d3r z^*nLY-CCR7q4IrnL>Sh~C2+8Q+m2j1B%6GMmo(#U2KGhXW~t+&}-68HCch zS|Vg^-FB@Lbdv`lD1)Et_2=!ZlIRD?bD1@hw?}0e^rSa>z*YY@j4=3#4nIR=pSM|u#l zVFG%;b(r#A7{^<%+&tqa`}Yt>6MYZ`@S@iSK@p#b@=fh1tG6L_kuk7Kp!eWhzc~+m1F)5|k66)AzCnVQBId6E8N(Y=?ZPHZ zpWrR5zguG5oCOKg*cc`8_A9gvO7F@RDQ&&dI3$d}lD;mckyCg$!XIC{IWIDl&Y3=} zLq-;+SFwme_5$NnL1+t_@OmqVCn0cRuJy~)QAZ;zpvRZs9%Hi_ZdcZl?SfmE2C;_K z9DT#iskmpCVxy)(=JWhk4=+;6O`nK6$uIntl*-F0Oi@rekyr5~;@* z11KNvf|S6I6J`gX)1S@D;~FGofSLChyEplLy4Kar&PYhRS}>}mAB?|WrklMG5`sK% zhZzAV`RC%c9xkYnSs(he-cW6|IG^5U^y+q!RWF%Vy}5-3QBPbpfUON7nfR76{o%_| zNa_mgrtAhrYoH$dAlFEcdeVbHw#Hu8I?D(xh;J^7Bn}(0 zngaT7auH>iTow4p!v}3Z<3Z7ZSGgnDq63(I>p7GVF=Vmt>FYlU3kOFpL)v3JJc2E? z#Yqj-ZY5I$d?OdTwd#Ji*16xsh8)|jwRvlxK264pGBNhmtcb>JVSfhe>9L||nvn!5 zh=#%S6?`BKstpvahHTSn$nymKgEs2${``KS)&{m7L-1N&WrUvALrc(U7$wEZS_yXX z>uRmz01LdxLo4JH2?{0po7Si2(i+wq0 zDjyc*w7YS8D^PDv^ud6}v`+ZOafcYgbKGe%zs~;kE#lvWsL+Ma#2p{14w3AmQPd zjV~H6TmId}#|jHWUB&jdemG#9ioGh29Qd_@hWkTVGK2mI7?ZN$d29L{!Jf0}m~(vf z`#HK9MBUZN96`DWJb;6}!n%VypaA!NA_Q~7p`sQ3=~uCU2~Ud8Y2sQlJA`@2YL{R^ zkX8=pRRLh%h?2!;vIAQ6=0dGyg2m#XMlc^WBc#6@sQwUcP5$MFqX0l(KeL0?&x~5F ziCc}h0I5Of4D0V`#)KJWhaxORpwe!w&C(+5*N{ovj)nhT(a|WvwuUEi$qYE=tcb*5Fh$E?{_=N_tAw`A_glZcKv8oy7ZKj9WVMX}-SNeS z3};=j$DLZd?XvAl4A(d|(>Ln<1kg?z} z5nM^99qLx#9SLt0u#KgOQS|Q1Ag6Jc^hJsUhy}B7?R!lM)J%tZb%O%j7~2_!I_+to zz=a1tz?dcj5oz;29m}8hWDRQy!$%u_fUJH1vwnahC=Ekd7{6ltG~iOz-VIzQhU@`h z{n{Zg(LQW|u1XxFtFi{^9C$;md1L4t8RI9>chFJ5kAw3;S`1n&axKQt$Ho86pye)p zbkO6IK#xF*HgYU!4molnOTfW+vkNC{cdD;5DNQEXr6Jl5YO_GP3!-oT`azx4huu^) ze%ywtazn;o@j#>3_ivq#5jNT@ODt0eDTaYUrp+4FVn$EV@HC%v3;42)i;zF zx77mQeHhK>#lW2N*^6CPX^+;fi}Z0NO(bXDlx9+L2J(oWb92ma_jB)g+36L|%fBRE zl9Qw7C<8FqWwk?vJn!9$MPi@#h9@_l+06K_K%g`JZvbfQ@n4Qtj(;x{;2D4FKOe#1 zueiY({OrycebqRf}^OT5t)`>3OAVr_)Q^0%ZOz2Y>kIH5~m76(yD8gx?F`YX;+$fSQD7*NZ(5FCcCm|l<;NbUaQ`@4=!K zr6&f9s{H2fv6ZG;sZeak)*LYSN5p2L*C;5uz2{iD1P+s81jPk4^Yc zVp|aZBsBniiWOrazBG%a4lqg&=kkmS)HT!E3l4koPnaYnlNl&_4Z0IaxC@kpOpa(dAtbnS)JoE#vxRMG%>%gunh`OjvVVH8B zQ#-qsfYt}70$}!;ic*5VJt|k=7^-4$NC9YKu9R}cwWr7&i5g2L{uV#9xUxI6=7ac_ z@ldUZZyj&E#Dx|#WTgeo@2v`%o%4;UTv%bqZJHel%;+k(5RBxv4w}J&*0??r(zMNP zSmow1idKWMkzg-lPtks8Qm{`YGmNX?SMGz-uyH*UDYljd(1S7qNArf#9~wnar|5=` za_JatbPnIL(sK|%BFbn3l@-5PgX2`j{zH|`>YdP^*oH*VtJ(xAbO$HbK0{+xafY_1 z6Nl*fvBL)#k;Yu5Mggi*5~l|eG2mo%ia=eAn+3Ep^w+F23KcTm^?gBY5h&FR==Asw zPC@GY%<;@9Y5zJW861=Z;dDdNQ4YM!R5b2q6^kl1*IkW&O`YgVT4Q4ck`gq8bQop9Ji}PwpqCt*hwOk00t%#92LVH zv}-T|0vOw(m%BvV2&pdUnYuHl$88t?hmBJ*PX`~t61axWgBjef*nSFE6W_wr#IuVXUT8lH z>?f8Q;kX^g7{iNM;f!DwY`#!L7u6(nqe?@V?&UbxF`VWg2swWQtTp)pQsPtw>0dgs z!BzwU8zrP4A(~T15UfmaQ{*%fD%uEv_>I9L3uw*izP9K+=neq;2a67(fYtlq#`6Hm z3w1phD5^#Y^#R;16fFz-OO^#1s{>u3wO3Lm;FTU9dpUu-;-4<(HFaJL=!*mKEx<%W zD{%T@D{XorRJ7ASoh%je%r`h1@vXgr`s(*=0uXYZ#NLj7+9qI?rsIV(1>D-qNE2FG{6J&uyBe5K z@k6;|HsK!_in2h}kK>4f2J`9m{t!^;}3KvUT;Ue5dG;jJG!bOZzrq9(? za`JS!tP!+Rf}=K(`YRYk)`Hhcyr$=a=clW24muEq_Buqa$(&w_)|HrAu5k)?a&Tny z74G?g`pz9Db%k;pRi4{UItSM_y$9t^IMq=hdM|aF7k{by9mpKVUQXc#q6W4gidIBN zb+4ip^i7ja!L3mDsv89{wR$ ziP>}duP9%zkWHJPgSBAY&g5p#zy1U^g&4qF{p%41!e>t8!r1I^_0bkeHyVCnT8!x% zF)zAG%9zD6h=JVkcSOcu639A*#o|WhN~6c0SmaJr56x5fv>LNP`4{*-PQ7Z`LYRi5 zKm$p2M_F^v--p^!P&e#<{%m3fjMFCTd$P`g`4qZ zeR>{o%nn<0$odUSF?xnIPCzZBS!5`zu6E(8C3?bao(0bX+|JQ}z&m0M9MK@x4OAF^ z|A^+R2IqH<^%~N0T}@uXh2{Zy9F9{s0oSIwNAyp4Qnn>}Zbeg4;PR?4k&WfhT#aCM z@Do0boM*Y{juC{ZV5FJgI$|DT&g5Qux?Vqp@*cIRllZSaWjf_H{37YgaSW8(l4ZEr zKqWGKsO0CWL1#7Fp#3VX)1XI?HZ=GTl`Z(Y@9En(N-*P?}2F z+D@77Yj3xdR7dG3<^>Z}75xqt7$&Hi6Lb|O2vY59;gAaxBL|p( zk7@1Iz%P^lU2w}6w&~S zcIY1mKigvhFgR*y+4~@o*XP6mz|qjTF45tNW?~_f?+UFXu7R*%9uZI#4jzJWXyl$) z2$h!&B){p%fQCM8dzu)rzG7Y*Q@-=PFOUqqBi&TFs8MF+NyIsDK$ToUY7?$Nv zeq@-w)`5etJ}LWm`nK)N!kWG?Z%TJYvtl!XMC0SzHT@WVT7S$ znafpW`*MvRf=?uBM8^-o;5ISHqGJ5B@^I|6u`$r_KU(Yq1(b82-2kwkONvNo*WW|S#03$K2dfH=%Uyr z&oMgo)Iwo8v;hswN*=&J)z%RL%p`cYWjxygEyoVWa(<}Fp=f0lGpTaGz=txat<0Jv zFCFSJA~~SS%SU-ic#Y+~=##!rQdkcYg&R#?WQvnvxz-%qr0}b0OMV2&gn8naLk_ORJhjrJPdAr`=;HV=z>N6q zB}%Rk={8zFw1b{>g7#aFsn>4+5Uk^BltLVOb3VPT1k?WBNMn-MzBA|y0M_!ae7wn*w?Rpy21GMfN$YpHjWb)w&<>vI~tgP4IJ-u_e}h!q9o*4yI`^qK+x!;?SE z;NQcZXg-M)e~E0;Ov*amZ{eU6``h3hf0>=<8zoIsJI~>!PP3K*M(Ub6&FWFiW@b3e0zkXK%&~)KG_;W0z#EEv zYu$;Gl%mcA!W}N}T$ftq2tX$5E<8Ef^Ce52wK7i(WM{00w7y+@rzS3|jvOnJp^lmG zB}qJrU6tpBJKz1Zv7*Kfv!26*mctq%F%)TDoJv*HOq_%o8`OIxHvgH{Bila3jtn5uXoAesFILuF2RE#)$a;*hD`XqGn+qO!ftE2Cs^qID_0C!-sd*es_Jw`3_dWL70LMRZn|<%and| z2{cQy(S-IenUmXnt+oCnI#Q&;XX&p)>xmYb-S5z5P`XV!9@?qwO_z}y1Rr^gL%JCy zI~%Kwp}QLw8?n7~JUL-A4q?^13cDV<5Ja%DyrJnQ>to^SP?4G$?Ik_rt@r<`@y_RX zE49#HoT!GJ{rv{xUHol~H#Pew6=bC?g9o^{v!Z8e#&uZF0}3^X!t6IjIzejY$}T}@ zOSgeA#0f!CY0RuFieD?{;;}GKao0Q7rz}?3D}>)iI=982i|;hZvuH&{xX6j_RR-zq zE%enHS9OLdx zmJS)i$xGM>cVpmu(lcNdp{XgoMYH%gJ9+&5uX3_3vr>@2p?!%^!6pL^Z%)J_!p4@8Jb_a`k4- zaMs7E)&SKWCE(-24-I8RTbB5CIO~SDRbKe|itA@O#>-vI_I{web>LKWv@W!nEvpM{ zQxD;NkhT!ykK^Aq|8t?u_*vRW2xx$Bw!d*k1=4C=ar7g-n6GT{xb1h&zZvVZnfYjK z{iqE#`E37-eTzM-BA-;iSAvh@Sq}lx@Ur2f5Y2B6KHkgyR@O&&avkwSX0y`uIpgEl zzbObPF9rHx>unyK&DhHML^niW%Rwl|C#>{E$G{@q@4A7;GJC#y4QTcYkihDdYz17o zL|ui_&ii&MMo^?Emjt%a^KRte=O^v?38F94>O6GLCp1{6`+*hwuYO8a;RDFWX9e@q zQilIUTtwr5f7BYt1AeBykD}K=W5MezGl15bD<_gVtIkJSs|>C5=>NDK#cBhumR;TE zcJ+*7tIPv@crag+@&Ioz_os+y>rXho)0Q}p@sfO+@BK;0|Drk{JMadJA^RY8Vcoc& zCw+yIPX;`Ewe_|&_M8h$V5xcb10^303Bs`cIxn}O>WC(ffdqROrTAgv`ly88tVEgAU0Yi|n`&>jugIpWH7yBa-5 z;4Jw&T;JIIP$g$TUoobL7x2)t@lY#lvNL6q{T^8Lyo#g#kR*`%!W`=mwn|R)oOIgQ zyUu-^OK-|~hv!9D;yRKsFF%KF;GvB6H=Uo$a!|v+YkojsjKS3 zlCuvK)*z#_Oa3VTuBuV~Ng_l6c91M$j1sT7txJv&LVK>i3NToJB~0jg{$CRI4#UXg zBRtGPn1Ch^X*g6u=E47BzU``F2p|!TK3nldcg+WYHxB- zq=kRV5-DRYjhW!8Y#tur#+4$|!}u}RN6bEFqppbA^E^D#_-uYVRXLHwU$+sqM11Ih zyvO9q9%_YX%`NE@CYSI&!u(1Y^3dJ(B=Nsfhw6@DJq;l&gNCMw)Y>zFX^g#HC zr^wTZ9n7A8W>(@$R`VSeYxevd!+3tlo5@Q|!#9t1x`7ER$Bo)TMY z?u7)*fUbz8kU~@SRN}gg?XFAA;~N&cW+v)+BUdL6#^JTfP1e9SP$rsb@~RGTeAvl0 zxXDD9zD)8SNXPGXUSVj5_3MdzKFkE0fv*D9s6sqgVx4F@aUIkpzUPK@!yChD6FnGK zqHl+oOfS3=2Zqf-`Ke32^iO~zy$=jYin^{#{D%;(pQ3h1{txK z{;ld`t5E{(bxgJzEy_Z`09Wht{yT70)J?1TNtO;!jPJ@u3gS5Wq+qT$0+}uYw+9fiT}>*gZLLY2#oeR?tflij$I*JD>@W=N7L8iyBf*rNBM6@#@Fjd zY5s}JdCv-4XqouAWru_GGF-HO%w8nAWAP~e&vV9A00sDO%$X$5Qo)yH=o9a{<>AF6 zfXGuy0C9jZLZsb&>)y?REC}Gn-GZ`8S_w!8`gp+eYNX19FW!ao7TiDO0>E&%ah3Xb z0;Su>AoHm?m|5JBKNI0S`&nb~z~Gc41x%w6Utv>|DKL8uK%Di_CTR+%VStebaST%m zP?-6*J_mQHOuSfMs4@_PUim=Y%O128$M)r5I$nnS4k;mv2v1655*UcoK=h56&TfG< zR+S{sYlIJ=YPtAThOW3VoP_F;aSJmF234)FE>jt~NoOKPA2(35r_KxxVrx{nPi{Ina!|1xId{lXuUW%aGK;k*km_zL3~1yYwEEb3M!9bUNicBjqS02|W9_ zDr1tjj0c#+?D@T>S;wfXBhU|`=%1;qL2uT-jyLxnUqxxC62(sf4ET^{bG@4Fshj$ zzK|IpOSPLt%|qiS@rT;Wu=MIg20{WnT*bsRiFyeCk*N&$UNx~8+Y56wHUy1@OcOLj zVg$x+m^RP|e-E>mAb8<36?mW*r!?)w_t+?SvSud@X@efB#5Pk1QD8$_qr;FSFX(6= zdE+~v6CfZ9GXY&UY1OI1K-YJ|BkRH=S8D#}>cb&PWGKxHN9~e3)w2`0fe&wR#GkK>qE#W>(fqf3lr!y zh76M)s!-A;bR&}BFhFAu*=M5t71rrUigwMk396y9(XKf_y=Lq&@iVr`I#bm))4EU9 zw)awJ2JPAozs=_K+@&GhyBxMMT(1j)nwh7vj%E^pJS@*8PV{!`F%=#5MnC05e`Nhy zMc?OEjNJFDN*`}p=^Y6F#6MA1E|7A^f;bV@&w*;YoOPzW-v|U=T7@*`Yz!vimIM7l zXsmSE9{=?@T^?$iwa5QcY*0MpSz2Atm+!QAGiA1b=U69Zg`KmGZbT>J3kgM7!U8*H z&kLKjY6DWSMkYh6a;8jStL|)2wRf?Cl;GLhoY`S62Ks~^f;{_ZR$^+oK{jf~x-#O+^Ur$oK z5WnAOSGHLJVau%|X(boM^pwwXfS{1&gpA_DWg-fL^RQTIHB)ov( zQt1buv7d7VT~=>znXaE%K_oI|)B17Hv-U2N`WbDtQuQ;3b%^D;=<){Cfg$*bYE2dr zsMbW8%IsO9T4P}zRBLWjt>L1FzKy3f%!<}9Wz*I`blw`JTEl2-yp+9=Ijq}MJs%VK za2TAa*}1+|)~K-mj!jtL?H`*Y;P)25uj4Fvzi}f#?d?(iuCuu1?D6O4%$*^Nx`reX zB7Tm~v4ACU4T;euJAF&8Ft(tCoVxi;86V<(Q=iDQ;C2O!bUL+ z2QiAEalta(k3H#RsUz%147)_7<5FU}3q0v$5hCmqhKY#Xs*hCLY~u(x8o=PII-A)- zXBc@~L=Gbl5joKuX2C6waPSq|!Oo`!zjkn0_#m}Md05cJ6V{K zD5@{b=^d({cmfQ?U|2u0m^k(x4jTPY0dAw1C)p0~p7UyWWH6Bx@mZN8L|fU!)*PW{ z_s9qt54!^djg(DT8TBa6s$i!KdACKqjhqpuLwWc%&CFht8s#0eStwNv7s@6@Z9T-Y zWz_oE{8M}IqEU<(QgQ68J#MN~!S73tTy(HAYM?B_z4JOxfTf;1sO8|}go%(0$4HP* zy={jK8~wL>#%e3ZYSMeoqPhPQuB6FJxZXUdVMaYxDlHmcg5S0HZN!fp0XDx3zl`-d z1Z>r^SL7h)Py9&+9m0raUmPDp4aUIn#?_?}^Zdso;-Wv}QM6{_2|Daww!G(5eN_^R zCQDzHD)pZC!y~iHBfiazo4fCujJratTyH!jm{ z(%0K4LwyqqoOvm)zzn8Z*Ty+bwB;`XUhZD-7-FBruNuEK`1vr$r{MP;{EF@Q4$r&2 z*TXAEzI$;CFh63a8rKyzE}ndd_RQ|NXg-^s%)cE}0L?g(5Kl?tPvzX+` z435~l=xad#8Suz;=g{Xthg{#D)ZO5&bhC=Lw8BL(s?Be%+A%j}Rtb-qS*)-1 zxIntDk3Ofa)Y)!b1u_Wwb%m1;3A%h3uG^*4`gOSoLhr$W^4wQr#rYGo9)ipHLfeH+ z)V+`P@XvT3Vg$fH1IG;dADYNi4laP)RBo!R8XpORxbg^bTpzpMVZ@zk;}n!Z6G<-NqV_Yx20ePzEc{(LpRl6yb(`mQ%va{HD z__Zf?;#nN*>|9?%Do2{6_s{Larpds1@rR3_uDO~z1xM574dRDeV(UTEO>`)Z zjF1pm7QIF@e@o4JF~F0?FH*mFNQ1-}TsWX^_?o~P8?)^dc0qka`=IuNVj|!qutD#@ z?^gVvhm`ucepHt9CJkm+I&A{@P(>Wzb5_Bb9OW~6``|nyS09E&HzYsR9a%2^+${gX z&h2FJ>DMF6Rr!^^^qrh551{T>ao+>9Kt2n)PTz}P(YEbRPv)hohW$mchSJJMKbXSL(!aoT^~1=cQB zf0<;->D@ub0XL;y+qNAUiQV%RJipwwt%)LC+qPwP`O-oo=WR~qF)BTCk%;Br!1C4U zqt43Rx(m8Ibz0iCZ4am5QAGcC6sky7d`>wRe%C$GkB#5xs-ymz0}+(-{}Q~u3PiRNx&;00Wb4jx?f=NM$Vnh)*ZI%-o?Tlc%Q-#f%gaBbKt#Ov)S9aDM#BW z(T~3%knsL4p4K+}Q}AAoHwEw4ezz&Szqv%(`1Xm;IwZWmk%up%{k}GMxbXg0DW%Rj z@U9E*>%a$4&E2X3k$3V5&5iU8ZpxSKlxXf=1`51Ci9oK($K~n5`wE=EWqxp^wBh~N zNb&jbJ`=P}fPyBle$yWZt&Gs-$jSQ=_k$I13zKSU$v#tUmqjmAqQUv3H;k5=J@ zrX|+Wf7lNYP%i@Dr$|^CNt!`)Uw(<3Ml!2xK1btKW>RZbHB6 zwQ^$gPmQBY&Fr|XEnBu;8`)v1<~!85C1m_EH+3ui7p8uJ|GiQ_4I9^Kyx>N2eyka% z<8ui8lKnu@@6i8+$c?u>@Jc}|$ErQJHoFP!>5VyHd)6J(o?y}Yu`_s}xc@;k2u9|b z$p7lQgB5$qw)l{4>_`{`gny}WD0;PGvyBP!z@;Cwj>l4e`C-tI0plf3$UF`{Zo0u+ zx{>snnRAhJvl{HB=Y_q5nJYuWWFJIloF`cL^C&6F@!@!z9oIk}1{tSlz6KoYs{)f@ z>i{WY!Q{_Sja;T9OwQJ^ZpjjcAvz0Ta;T4;nJhvWhY^N=bLX{0T)61EA^!4n*-CW6 zGS*ViM%EHJV)vXD*jEcpzOE<+z=%Hn0tu3*;}dJ?$ErDsO=d47Q0KHzi!x+2-HI1k zO+O18)2mP)j?JLRuZC@L1juTtN_D5eknwAoIv(E2kq#B;hcx3dcmCEx#2fQ>8I}m= zZ+UEall25U9*J$pXRoTaScKVr6>bw;R-Q9p{>0xAn_Po`_Oh3;OP(Pum*p=5mn8qj z1p^r`EIv4dBNH+M9z4JR1AApC814UJcvzWy8pXKtb!KXiJ5Twkfv|Zh%$IOc{~^(S zGB>*#I5)Da>^^d#qK}7g@1E|XT2HB;Ia4sqnS#L?QxM3Qg73=|jKLJ_i=OP9hxee5 z2vG3zrUnSwo#4$YegHzc1S{Ojx5+9TEO))Fx^I3`Z)flQ4m=VXzmScuD*Q0 z$UH3MHAF#!bCVMhhnK?Cc>M2`8pB1ZFwZYxb3SZL8q$4PO7osiWypVm7QY?EkQbxK zjX(-K%6*zTN6zQ)UhCmkYBV8Kbh@UmWBU)q7ByKv0;K;XP0Rt|i1HQ>a9|BXScaHn zg|t$Zws66mp&jci60GjE(VGQ1DO_2V>g%irE^>vR?DnSdlWOm#Chg6{S54as!@u!4 z*%-CnFYQIMVuQI`E?woW5yAUJJu(sgsVf!!>7%-a_3{rvBTm;=G{rx(m#U$Ba;mNl z$0v=(A-@;vGj1x>l(9j7`F9b3iLbJ<-{sy5E;i1i{&E;i#<_fOGYq%XX)b&l6Y8zM zV7(A!ZUQc{*LEYk3dtguWe>66L)N9A->Y$gigGT6Mmi;kJiQ|RN#28K=u8R^Nm@2h zMnTJmH#+@2%_!YX{Y8J`b_xEeFMc?8%y#_4?I%MJUy!^SZx|&hk7jJ`Nl(tfyLaCG z<&Pu8JNMXT{N+;-DC_fEo`2cg{Zk)ioo4%cI1%vRGcxZ{&3DiSs(TS9Ks8o?>h?5H z{oLL^0aWSriT(WBT%Yy+#K&+KY~!jA@tT~8q3pg4e2|OuPpY?q^LU7^0y`bjT;q*k zRl!={2VkP~T-R3kf~WX4LZNwlK!3Ch-3jZ{Z6M?qx|Q2~Uy@gD&a9Bm$($9YZ!#;< zaghK0rt@J>z(XwFY`>HXF@vxgTqw-$)IA)0;d7^crDi~J)PX5(NKeD%g3rF?@xS|! zJN~!h70vuUo9SF_W#XGA_g?O1S$-k1CeOk@XS4E`7ci);3KH7FU!KFD{Rg>WrnfHe z?+}vQjDJ{a=Xvq5c^AyO3ZN5l<$(tS4=B(3FpnghNPUch?9Mg3WBQ#4$pP?(HLNVP$oadZ#aGj@N0*(XXDo$ztt$?Q>6C+pxfj32!2oC_Y{7A$B*}3 zZ{zn7em)$k< zf5`sY;|XdhdY5-lTlf=;0nnQ9<8FcZL>TZ)EJuI?2#uHz&^-h_=m8&Su%cm1Tg(s~ zar}vm%m*ZA-zjWXPk;FiDR0{1d?ZZ1Bp+95-Pe+jk4s8$iHGE-t|0wN{p(n(vU`iO zVm%sc(a{~T+pl^oZpOnA8gX8Qm4uV3Z<^=M<;uWHW$H`^b&{g8S52uj7Dx%>7E;XKlayKJ^F8YUKP5Y5!yQ zsn0%xI!$%Ik`KNnIG2rQu#3msuRI_roqH4BuZ&Q6XJ_88GywOe?_sQ0_<4NyD>IOk z_bc7l4%~lmsE@t>V0pa%K%y_dU+II??)^$PyL@S(kz+Jl{>^sz?O1*dw{7tF=Phk| zzp`zOUGQ=C?FFP~->zZtMvlMPx|2x6zD;MVo%@w@`62f!Kd@V2z6^Oi@nr-f3@Pt%zueVd8xeqbB+^+-?$g#g%p6>n1uaJvl z)DFBN`+ntgq)5|CJb!3);5|Y~ufGxi<2_1U{{jZ#39^V$!amo`!QBwl&5r7#qU8LD zV9gFLmZv^=7-WY=X!@kl8gFXq`j5Bc@8YvgScg2QYUcDA2+-=}+D4_BO6~;z>V&~W z+DtPaor3@*g+R>`+A39RBP{t!P;yg03{ zZX4dnHsoL3)C2g>Fin4n0%#y5L5KFsPxbfmPlX-@iFXw!(Y3Ui3VYBe;??E13P(OI zUt$&+3ckzvs5nfL4Qodpo~r*gX)7g?cYWabzPK2iQd8e$x~JL6IJO$$F5-Q2XCvos3IQXi_0N<;?t3 zO=J)1mbgYuQlJcqqYtff9gF-l)3M08Y4~vbiwve6oQGDZey@#MUc1}cd{2*-E0MB} z7ur37XWXUijnk9F05VHj(=sAD*2Knw$3Bk8H^g_2PLDs5#>lUP3FGl-9sFsOVel(l-@Ki%`u#61*2Nbk*<KLsHLy8mp(EWU80 zCD*}-8JYnHUs1Hj+WIVK({Xa^%%<1Tt;{CJgwA@7nN3f7mA@WmHkkuZ`@xsa=5XL- zjuQPGb9kJgNgM$$hqGBGI4Ztmw1>+@eEcBRrgywD;cN0vBD0|KZYQSjSLzSbbUF+c z0P0|s`N&?7LNEvcSfKd4G2YUAo2>1yL8>RNLBiV5qE>TSL~n&v z4FU{t^T#`s`|za%)kBruqAHd324$1qGCUb;>lX?lB(#b@dGVNbMaaqYny7Tjl}RoKCNo3a-~+s z=P~B% z@m2%95iOXM=Zy9sG?}A~z}N+R(TtKRj8=W5XpO&|GZHCk@R#qGUamrdoa9I>_!4~; z+;aF6ui0ZOiRel?;%o3H#$(S!QeUzcLcBv>l`-T6fDC&QW1ZY&-XvIE_14G077v6L z0r)I8Q6T_(wv<7UTMP;SmPG zU{Pl!Z^04=kuzf6?F+<@;=YUj{fX1j#kvH`X1S=6r6z|U)V633uO2h-EAw|1U7onK z0|{mAMwBuTw6mBkdTRtj+kF`t;9B`#=1oY`QiIlGC8ND}d_@hAR*7tC(Uvov1RTGo{hx1bulIoAes0Vt{5WdGg<><7w2&zG`&Rdd2$)z z5YQr7rCzd=FX079OrjB!o&2i=)IdR#D{%&|I5GuoLqJ9>JAI+Td{vk6@If^zsNUS9xb=@(7-mQk-Hx~ zH#kS<)i6s=-F6uVoMOB(*MDMpnEU=n^yNH)(~#Qb5#-tBOAC#hUp8BQz%G9Wo-U8z zp}9?Y1XWmiQt)y1Z3fb_Z{1it_#a$G$MQdrh<*DN61hBrqpV3z{wLV2Fmj%5Hv9c< z%55*G_M)tFP!^9J*W+o8z(2(!2;xog2>yJK)9vLE{P+*j#_1n8>ySKxgIn=sw0~d= z9xjjITq&i_imLk9!NG1LZn7I;QqaYbbD`Xa_y_W1JCu_;ZjHNmd<4OMY>z!8Va8me z)ztyzKy7CS&VU|#3;#e@j2pqzRgdMAv-(KLMYwzizWq4kWVJ>~K(t?zBs25O6MZ!S z-DykoJGgkZ#c_@TMTN~t1=js9x60DrN-o29z~*4s!lDhzy9!h}Ek?$Vv_t{0dlf5w z1;m6a8W_DhH5i+XcfavoRY)2##uWx|;f8Z!^wA{6ax!}xx6?j^v>7q}@|!VrslMLMN88z{M@YUIujc0E|dB4 zOFUrWaxYj8p==pERazoI@U z`E}SIVd+1+GcYT5oM30+r_+9Q0qy@6FqkjEzbal(bctEoxf_GKIq_Ybc;T!wN^_!$ z`BI{nb{~%w(SOl-G!xt<2@o`1qa{&V(F@r8W7^LHi&b4{fV4|#?BaPNKyo*%Fi; zq-U1V$EusB3NrRbY!4g%BHP2sFn0LICJN;=pqYW&lkNU0%nE@JFL?6cY| z?6n#DH3YYt@UyS=spA~@*$c>`RbLtW>^-7T{6+lii{5v+s;?`4_RTgwTl9ea$=c9Gz$bk>qyJ-7Dxi?g6?puf(;smH$Ya>4B60mATN0Z`t}o` zG~-LUc#gIi^raTxnK@r&VC6JFUx=~!-`B#=W}5$fD3kyF|11;Arf#UPF9sViBTz;?4()$=7JjI=ri~&$}4Nao-6e$B_BZ}Wr?M{4jO|a;e{L=3P;7@8;QJ_QVnda54d!Ja^ zF<{18_r5#3G|wF0y7#@gelvugEF$Kz?sx%Z!|EW)t`nP|s1t2_0pD~(jtAC5wS8BC z6BS%QeQZs77-Vo=zg|p>XE*yPxE$Yw_P4zW^g}(8E>}IFe&B2wc)^&la;xQ2V3EEm zg8~k%f7*SB^9;`BL%SLSdaY{o%ji685W^%V%^tzHEnADf+Qe;qOBYl6J^1DI$teN& z-{}S1E`YkWUPCV$YwX1E8oe1!VR_d0CpmpNx4)w%)@4*-&^*v=RRc2gg*}i1HeLDu z;mh$KgISD+G@X{qkD^p6plh&Wi-&dUMOcKeSjC{Y#AHQ#_bw>CG}wD|c4>E*J7V_U zT5jq2hRFhpw7k;O4U;t#Y5Apo!(^33S_i*Lb1FE7gLmGUQh+C_(fI@RAxuZs5d}%g2}_%J zqFcljWxa)+LaQs5RfT6E!uTz#3Qt*8c-p;SEEa{BJgP!ryo?)H$wrC|S}`TBgMX`x zkU|43o4(~>en?HukbKP;2AxXq=DxlsuoB~YuplHbAJ#)~5JWSbSOW*ZgsNb7p`HN^ zy7IGS3Cv&!HV-tU7!E(gM=9FJgkim;Fl-FR1`+D=4cdbVQk8sd-)3q>g*lGO-b0M8e*#a8vf)^=c6XE9=Y^3Z7>Ep; z!?Tg=xY}N7QNy_^Xw>imwes&$Xa~{(E$9Tyl ze6W0Jp^J)sOeGA%kZ)5RB&J$cl$MBNxwd+0y zb-TRef3POuB`^M?YK4(AqS@@1xhZGbDKSp#5J=LcJ<7B?;Gg0pA3(6cV$S_Ln(~rc zC8Uk*);jBuyyTqM@MX0B&T>3lUh*E0kp6!Z}#6vkv`vjyYT+(3N-zT;JpueaV)(5a@}#j z`&xFz$hib*|6}kzD2_U9c&`L+5AgoP$IZg~1(MQ%W5Rou%G(6q?~+oSbk+_0JU)2u zhNN6F4U#3tSlQevxtWoe?{*lhFNgPcu_|16Z@>r3mlneRbF<~=*yaBnPZ!>Y{h=wm zmpcU?XWy=|>)wke=J+`-qhsJ*XC(S|G3s{VeKBhicwhT_2j1^$Hv0fKt!R!L>+Ym^2e@C7!yp#9BG5R}5UmM=%BgN;z z`+rQH@EM?B9MZm?JYfgWJx$~ZM^59}{(q-D;oF#h_UIeR6T&gFBhOj(#qxwg=6-c~ z!b?;;FNZuK2>ao4f;!wRgAEjz+{nWGU>nCR$4VOC>uGZb~oDX_@-(nfoE&L)>P4O5s0q zWI?EWU-T4pOoRNN8EX*nC8{A#BkBedx;Q&$=%Y zeZBE0|1GpdF`$nv|M>aQC^+_9s!d&JC6)(dVfB)FjEUnvZI@Mx!xRGQ#m@jGP3Iy2 zET?v%mC((`Av$85SLrJ$I^r+S!K$+`xE{DH6%Vys$;|7?`>c3;(A`MC-9iQK00OQ;gO|%)e0M(+T5( z?26z;SzuOMehwViR)v^ED_1_22I@k&W&@j@ zR!i;t1C5R6XhwNUTZX%yXd`P(4(^9n#UFC`u?AJK~`Om0iZ*L$UpA+5Q}mLN~or!l^|L%6*@qTigBiC>By zN6ee6yzmHCly%Z$>HQ0?GUm|_Uap|X^aeHsdA~p){_0HTbSE&s)%}WdpAgFEXQn*7 zrw`|Q`j8<7PdPZR6ukFNfON(YYzbUVZ{lgS#=SxEEyX3VK0-(;#56;GOPZv{^)ey8@qS8Ez3#or56j;Hh6zK0BmAK3gp=OCF%aGG(+`J}{zJMtdf! zeEe~a?nlX!zFbCoB*0LKJ5oj);M7z`n+3X^aVxWmP=za_{V~z)iyj&6%?L0O997C_ zKY*_c8Ew1Wu8g)cdI#ixWc!2T6WNx*9|*v~qWT<>?axrONMrAnPAie^YseV1MYfkC zoI@>%dLtrJWV`O0J0+6npD(h_N>zVEWcwCwMIy4z2qm)3wO|OQH%y{& z>&UN3bEz(h$aWvh5fQ(06UL3gL)%*~J%l-=$o89%x&Azn?fHnqfcUlbw820w}W0p;RK#dQf1m}81tKBFcOWZ3dzBtrcWT8W?Wp}20ijZqTz}qqO8j8g! ztSP;=+NReqim??&=mi|6>eJ*SL?V&^#d+4I@u1ZfVZze18YpmHNOa;c3Dl2Gtd({K zwbtk(wPptwgco=eo}BfA6Oriku~ksogg#vWDo>OrSV$CsSDRKt0_f8rq`knZG?OI`NdfPUJyT@Vrp%!1J1SMCN)E_ zvE7s=7?Me_rS*eEp1`5jn7XOH8>ls>7vo+{_rjsp*z&}+1QAz?AX6rQ#M2;9m+VOw zJgw{TFS*{P){60w>d!QPo|;Lm+4FXx9B<`%YDZ&(OUq$paaP;gvACc@ESGzL7icK% zY82NTy$1YGdJWaP^cvn>dhKbf(_`tiN8T2C4G95rjy_!lyWr~Fsa+94R``UkNw3-Z zbj8%CD?XN1V|7hvHCDI#LD>wBN1yJc1zuWBX6`vGCG-iiLw!2B`EonrR%V%~!lA6D zevUpJN3>aex;d)dX)kd;HSG8Hc=nhC7M2J{^;}#2Ma&IQtWlWe{hGNE2uHaEUX#XA)-!uzoDyCmuN+ z;;dgLK5JH=u2}TxiXDBrCs7m@rch?AlKOO{%<%pyDLCtArch=~M#>CNQfBgahTV$Z z=~8B>-OG>q*61_T(u6)ki0qQq@aY1QiatXqD4uIxR`l7oqff^!eja_cc3uX3hLjvh z&@)(pE`6pNwFpT`G}d#db#VYmchtsNe^n8r&ydnPeu_Sm>_VU6%|o9Nq`ta7-4)|J z#MzhW({X92&NR`dV}wnd;SIzY|Cp(gY)dE9jj3$-B7&IC0VEWEC>ktyh>0-JUrqTLWP|DtXJbDP!;`uGRDryG7BTuf z_#$`4)}&Y5%Dwc(SGy~BE9od~#v&EW+$BX$R!EdLp^hoj!>$myTDG@G>2Gx_KT z@SEC&$OM~!M4qx}*H)jEi&WNI8+J9;h32s0y3mvAF9Hl}XYB^Ogl5C3TJ?dvuDkW>HOV&Bpc<_fZR+*v>SVV1 z%2i+aZK`is_U_Y5T308}(2Sg{xk|mm9H7MW7GbWk*6r}^uilZ@xLThu!zg)DUtQgp zr!U-#bGkmEe)9WLVabz5$wSo#^SU0?SFQP|%_^9ENVaJ6vA(MMqipq+tG@Evth!~D zU7`QQ9=V!6w-7@D8xrOul>E>>}1oM5uO<%V~tw`US zcEvL*s@HABbZua&8yc%qIYD!*FI>I71*ziIfT@Ui`7BI4up1;EcTleF;;F>R`>p2@ z3;Ga2Sen*p63QD92vu9RV9kM_i?zV78}Nln8c+!U;Q$`EJ?3yg;7l4C@wL$9%?K^g z6WfqL45F{w+*q$CHsf=0f`G(JCtR%roL;E6`IcZ4$gAF(7pUHr7tr4bRDTEtcY;m5(#W2p z{GV5>39Q>uv8L?(bNnwZ!@(-3$IA5K<*-sw;g7$DFu&_Gj$x4n4zKY#n#&K}>QGmk~ySb9rUr%sF=USRulw7>^lcKeP*dJnuoZ z&>yLXIme0DKsx~Rh1<+6l%X(8yO_V>RdQ1VoI9wN>wma7{{iz(l%*PeJKj{o$wffJ zv9KCpTPDG!TeR3e%da3 z;eoou2$V0aFz3=5fqlod!+Kc6yyi064V#QG!(=i;BJ@3Nmix>5p%NQY!V+h>SmIPD zvHRO401)$;>yQI}Ej2!jr#LDOrrQs!H_z&da&1J6fVsnv!0N~_A}RnMc?UVHJ2X!h z|FcGDfi5EG;5b6=o8>BKy%%>b516bEQ^j&LXN-sBR%jSvniN(;9KxF#V(RQ+h#PBq z&tl*FQ*Oe4)k6&A+UMG0BiRf2PEK{DkCxhplCE_62nc=lSr4Xz75UgBz@RHG28UXwZ}6UU}7As`tPL>vHGa3EqQ-$62sK=$-_WYRD3o#S=5f7jta zZg?fU@8KE}unvJ3WfteTqr1Q|76IauJi#*&95H&8n9uUEP`FB{h>LX?;Jw8Bt9w@| z!P~8XpaHkof)!9gcmqq^KQ)5BhPr=HEsC zQQnG8i1`0v!A=cITvSkw(@sn2dQC*%9nwDvRDaYmFnN0!?l%XSmp?<84@}-s2GYAk z-x;pn);chG8^3}rE4i|QE$4A%1zXNmD=UQIX{nj_VGXkAf-=xb@$F@S_zwKGmHjXF z-UU9Y>RS9yAc2VlPqd(DeIyzyR&C+>VhYxb%)l9#0jy$aE7d;4KD2rv15^WqPNwE~ zG9|ZHTW>2ZZEZ_$?Z31|MFmZGWWqxT4|(|FrErEpK}5ns$?v=NKJ!YV)!JV__y7C- z^7&-WIs5Fr&)#dVwf5R;uf3Mv`kRMzzx7P1y8Nb$3%q?GUQEvS)5X*L%RKUv#ywe8 z>qG!FJQ0h)Kt*&Q0G&w=*ywe>Hz555@}4F@_Q?SlejJ|E4F{u|kf9 z)$)y);8SuOhaTWZ)4?zzL*K(CXUra5iAE;S=u>J)bpph=|8{^s$vA)pW0K#vyST7O z;&$KNw?wWa^exi~=MHca`k${74xn($$lDZf_-}vPXWad0iwuU`J=ijUJJOd=GN!Z@ za&w%UErn3yOHJ}~bdqsm2mf1io|l?+mY0r^ZBq|^Ht_S(PX5BW{I{Iv12=_LOv+?kpwz)+s`44biVWTUInvf zG`>ptY|zXp*AHG9ww&zvpf3*E52}F?Z$^=CW8Rr|Ft=N~mNV+M(Fn< zJ~ywtcIuN;$M$p`59QR6+)Seo&&5k!o7vuGGKE(xMKQWXyBsL~>K{ZhA68bl4l5?X z;JTYBF8-mdW7Z*DFSP}&o7Q?d5?COyhhf}*4X>@^624T~A8icUkG$$yDy*Jzv)R5e z*B!$4$fq;=TWPm7hVnxw?xPkHtOrRfiqd13*}g>`J#_arXyH0hK+`l&;TE<+bmL-o zKugLOzz6l|f%(#>E6WPkrp{KCx;_`Om0sy7+{}&|{jL6vm;v=$t$Qh3F7$HRc|21wr)x_vv@QN4#f(_}Rbe}?~#r{o7~ zjam-P5X_<}daMv%gEsM~-$lWX5JCE_rKEHG6el(;O$Ug8TEz^~3})zR(dS<#`uw#0 zh2|xqU#j*>CPAMpDt4pYSHLyx{%!;**1K@-^H~G* z&hY&C`8>ZdkLOpiSEYIW>-jwYk9j;F@(y|DLK4$T?@L@3%YkY+ZFeKN;OWMB7t=*P^dd_IvHl-gnq4;S`jSleUqxVEmCv z*~3fk)yX9M5Q80#KUUdg50CVm;x{eL$S|J-{>U)wM72r0pRIZFNs-zkG=8(yh&D5} z&)vq4^NssB)%RlazT(-tj&^kPwzjRuhvsy@^=5R>bYIU0D@CI4^=zmV^z!vwSSd1v zE6`_T&C-JCuA|YNN27a=n$0`Q&64%Vp(eG<`L!zdI>fyWa<2p3Yq@(J;9e0*C})}D zta~N|E&t1$Lb)fqZT5rjGp^@reOh|CA}WIKkS^kgF43{f7hT~=$83s8;E&`Vv4;V< z79w8G%K_e*%_58}P~n$dSw#GiB8$A?$|5UHl<;_W-{lYL;~nxvhjq2KG0m3!~z%DfVsa(_3e^nP`XU~ zcLJ%*w(spcbeZ^X`DM<;KV2sN`Fhv3Nv#7iJ)RB|` zc-_s4u5+Q{>R;u|^q`qh_a%x;Fbi;6%4X;LHF~~>LmT&BNv2No{mU*Tt)6mYm-+tU zocTU_ocQyHrD~b%*Qio5*}vX(vR~P0vX}P7zQbs~?_{6u*4%mTJK4LHx|2Os=1lf< zQ6_u3*k!VR*X>@nlYLg6>sU(v+{xbU82 zvv?b_Rx|4dt!V<&JsUOi6h9jUiQ!5m-ZH!tl%7%+-K!YrE|-BGGZVvI z20FY-vh(aR(BXsJXP1EvFL$3^20EO!3+;^_XWcjfs{*F^<({XjND~$6_0=-TD_c>8 z+&>~c-}C_JZF0wd)#O|ACwU2Hl^u_azg+>iD$?2hpxfF0jk#I43(UoPvcNm`NM^R@ zv6DT$OuWl!yMX-{T%C3NBz^8|{{#-|qbE z9(H4qNAvX;v-c07*;tXH%yq4Dvf7K&o{`;-L`z@`foDr(P@(8S z@g0e+R{}@6(}q8FC5gswnx}aAP-bsgRXd1}K?XCnU{h1fCZibH3u>afQSZy-J$18( z+e9<8b=_RS{Q9Fs@39)heZjP=&G_T;Rx4oKlABQ!OUV?{Q@AOw{6xy5%^AE>&eAtS zXS$`m!dAQA`V&6v=*L2m(b7sI)VPTrXq|J%QvwAAHOXx2%s;rF%sKjluN~HN_P6CK z`h$CP!nxDkg#NQs!u#qEDwoSle$(`xjM!ZKK{)eU`|P+_Ksuvt>{Woffrx*o8aARa zafE|4KAwpOA7P=xqU-?va`!Ur;V-x7UFFHb+_<}bRqd|pj3+i=4%Ieg0KRhVBJ8z` z*5WPq+$=vbV~@NiNEkxTU=S=i|D?dLXpm}`GV%K|G>kt}A=#WYg;HRq`pWXnwaJUf z3dxy&rx2tn?vzVO(jFu&Uta0L4_J-94?ie;vO9iYKhZruh@%S5=LfP6JvZ46Kj6j3 z;0ID|IUjpJejx7)P)JQ1gR7=xhJK=FY(!TpJ;ECBF}Y;Q#fq zd!)9L+`d$i9=+*~^nM2{JCUBi1=3UCQ2@`79!bu+v5NHW71HCGQwuqymzLN63G_$Z zUFi=hizxb&#wq%HQ78)fgGAn!{>WUQ>2Dy~XrVt+oOx0b`s+v92hpE8kNRFP9utj4 zRuwcDm1rj3_A1m=FDQ)$?qn`B*__uZ#L2HcQ&5`;cX7Goau4*ZSBk_#Dv0Z$tQPUA z0S}1~e$E+%t3!x1*2Gp;O>Ihhk3&_eGU^t9kt!j?46N2iYJ66m+I(u^M%wE(Q|T9T5AO`f86E}hEWgd?cM9GogYh4VB$#85+Zo=QwWN4N>~TlA z@h!AHF7YisOCL8Y*}Vs+H$-Qf1@$Jau)XR}|EcB2Gl(aG#;!@MSq0i^*CQ=V)@=aX zDn(i4DtL?RI0hw`I{zDL^+dn#DOl7y_K;jfEIOtWt}5KG6Z(7P#yIej$hYt#wjJ(XC6dsc zD-O!#n=2&s1q7lEvXk;MHyf3LqfPcawxZ=ge{o{B&hwJ&laS@5?V_kafiLTRJdM#w(*KRlf!$s?jzwa1+@ZXKQ|l7d9|6FL)=Ko zyQx#9E)ow5Dlm|T z1!ENSf$Nm@G9K%=c}vD)wWP>Uw6g1jItv|9OiV^7$!3}w0IoBe{$>2pKc^f1_)~Y0 z+A3TfTFea!(`h`q;g3R(9a(z(Z$|;X?_cFA^!SucIJenN=-;Xm4ygSP0D;O+?{9un z)8kEw9vRgClNTs?^w@cU!ja(}{kx?}q=bJSJr0R~06jhqvwwejymexBe!t;M|HApb zuAv+JmHoqie0~@FePfZ28B)zBpWCDH_j-NpKT2QE{er$;^kuo`?$0avx<(=Gzg9VZ z?QGhd;U}oYv+D8Y9VLQ4kaLcd2v5)8j}SWi{9NJZ${(pxsB>4XIXUmyV0=GLe)56t zF-O`{x5&kuTzT-1DArwh5aZu6XXhN+C9P>-2^w|eEiCbhpAVsWXn)x z*HvqK-TSOsk>#-l(Z+Li5_Lr>J!RD@>(vWjQ0!*wwWu=7U~$sjT2$lJs>$Q?*P_e- zS$Mhj8R-?N>Q|?DmpLoafZO6OUa7=DHzPuHU65wJmq)+ATh^ohDt%0SRL#2i`WPk| zR~Zup>IC*vYp6_neRANS-T5;#Ec0*P`f1)PT0&Rrr{Bl}>!&~J>$QK=*NdK!YZl(J zaM9~0(3yw-A?qjqze>MU^n-5X&yTTwnh+2#&&MQ0{Qa#AAU2KIZu{*|2P)q4+g~pV zRJ`lI@Lt>ye;>b19o5sqaUYh8P53g>^NHX4-SV2USp)svZ&cQJW|jNB_YItJwrPKb z>@P)jrq1cKelzW%)FbYc7N_v4w2XLs)ff1OQpq{S95E8~52d0qV~#kV@DIIjpb@X) zj?J!l(UU!l`(K7qtw@7ujc?@O#v{t6oSkjw;1^x9^5WyOv^80tX}RwvrQpP@Tw9m_ zPFt5hD>lHK))ze&h{@bJ9RP=4=0p` zczA^}4-YqE8*T?4VFLj6({9sxdprll2zV7;J|Qd4Bu`!^dbFIcEZ{4|1BfgLS?ips zd`K0tOESj)rpI4@PuTj#)nN^() ztKTMZDYvS)ltI}ku0SLGqDT2IzyE5`nt^EYE4O?h>sazMQZS~%l?8Sa6QQvPXckxw zuD9tTw~`@bg&z)CGv;Z-$YbC&hDTzhHOj8rAz%{IA|U!i&yfcm*~AYXzem;NXBgzAX)Z&IVwZ9IR*_W9b1I zB?BYisb~$AycMWxG7W$0D0xYuI>+gM(UZE8qidiXry;`l*$cFeDxtMxz)F!PF?Hs} zs0y8^OKrmk#qyjcF3XdrI?vZ2D>120i1+&YW5CgGpYFH6Q{=bWqC2KH?Kff+1WNV@iZRf;Wo508v-#`a3U1uXZ|zQYb5|k)yhh-KX!f47W4N%Z1I|JyC|wb_XSS~gFpQ0=S-oJm&5Gtw$x(a!nmTrg z{mtsNB!g|qCXI`2a%^Ze6e18M-Kcwn7h_5m?-qPX{)`9dL3CGdU-Rodqq7KVO)&Qp zVQY)?d`R)+Fm%Uy$`^fo2u8SgyYDUL7@rs(5A`qbmtaOc9-!lCy%1*wkB0!aG>)=wdfOu($KQ%xO-J^d?VRU0Yvm~WK?SW)$?;(O zmN6IVE)$m?>%Os=XN)c9e<1&Z_#eW575}E+T8~wyvIWI1X-~OXvX*f6(=skIAui?(+>O?A@aPEmGdVOaet>te zCQmZZ7$?hbfvje$r`W^GeRgT3x78Ta>a$8K?ctR^Vl@`I3EbJk2TF=3lj5O_ztFKq zSNaQ+_KgGmh5LMVb!Dly#R#|fh!t5XA=k>tAEwGsWtlwo^R^k`HlKA4^%fgXKAVF}s7ri9&!`#hvpKz_9>x^a zT;;RtDn*mx8#=;MbMfq@0#2Xz=F*x^&TiKCV+cA{tZzpUbnGhD&%tNC$o1 zzG0Oxqm{(~zM|Wl4F(%=&dim@TMc!q`7@}{FDB{86^Dgu0 zN6+sy5IX3yrh*P51RFxllmi(?2rh({^3X5$K%v%LcREn0 z!6#N!oix{HbvjSDM#H(2zPj5J2KHSVg09DYY^mJCv#WBAH`JgO$e?EIcl3CYCgJ7s z$8(9W(7f>^+lLy@_wLNq{NFwwd$k3OCs7kzrN5jvGvgEI+kr zK!DY0rPj^)$b5PQJ%nC|Dtc89(Cg)jUey!ydVX~2EJdtmM4P#RQu{`mxDh(NG)~UJfbq<+H_XPrP?SlA4>gikeE0n+Q})N}M!J-#^|L~suc8BD(3`fjQ7jc<3QPJv!* zO@=yoRMZI;b+Omghm-QhGh;uhIW5IL)OdbFx8yQyNt#cYYCL7fJ(n^&&u816ldNL$ z;>jF$e$J5R8jsovz>Ydc&Dq-PtfhHJtZvB%aCuff%ZD$W>f+zETlmJmrTI-Rbt?F# zaP42i8*l9fa8AMa2-ywc+?CdT8{ut!?t*cN{+%C_x~rK z3|Q~r-K!TG07f59ZQ3^_$`o;Z%}g}W1k81I;FKa#nU7zBMA7$pPy0fqjGV7Ss|I** zWwF()Xfrb>Zvjs;r@aAB--byVJcWV6H$%XjBiW&nV`)Ie`tw15;YPH>O}@f4q6qaB zHU$cg`wCafP=!l2`U>gTi+D#`!wA#Y<9_S&NCrOda)g29hz3eB;B?uTCToPp9#QJ| zZuJ?VW!8vN1c6|43V^r?We5X)Z!2$`kp{BL@nxOuVPrqv^qxdZhX`lXO$B;Edjxvm zkpqJE`Qi8-p43+qc(U}7mVe#7leE2)irR)pg zV<$5ohwLds9vEKr!03_n;mwom(Sb=;c#}Qahk`sX__eTgJ6?h!9$$T+?18{$E_hc9 zY?|b=0{vwSt-xt%2%O=zM|7eeQrnFc5^JJc;GiWZRH>(Id=;{;`hr|N-+MT0t8{g&rdstd!6)CowR;hi8?Pd z_(;l>?BtUyTn29#+%c)qXOz_`KP22VoD{D-MP|X`jBKecXJs_WA4|s?z?Vi-BvcDL#{jYj(n6RA8Gn%!(&Mj^eyWoM)Kvv?B0qJ7WGA11 znoI8ZQ$i0rH$bz8jCQo6Dza0CT2`DHdg|zQymhVOKr7P-mGo1cl+%dMOC!i9jo|V= z<4s}R9&SErdt`j6*~j?GcNkx}FuwA+F8(`u3jmonWPEQc0It7Rj3zt2j}QF7@r9Xn z9pC3sSGeOFqB%g20L0*{UL92K2 z$9!1thCOz*Aa!r~PW(TPBEp*9wrK;*c%!~wKdn?V5ezq4QobN(L1#CKT_ca?>=wCW zAl3O6qpWC~Q5M=(W}H^E)i^D*m1985imIN3dz?_*16QKWRdpSa0h*r*J{4;I(aVfa z75*X9oi(y7J5!CIwIj=U9>|qI$|DAG9mJK*)R9BDR&lN3%4YsZ6Hrb7loNOc#v{kU zc(EB(13q!_|gQ>GdC+ga# z^HTnT9c#1nu}Bif+K;KXxjVE3g$^11qf9!Z<3-=ZQg#K9>8P4~K`?&H3Qk13_OxnG zyQkCfZ=emPGSQCfrtJ@QEGP2WA>ZHce3IEsvP+qdi;`BSr|1e)1A3E6u)y>SuVezKjJ&p`3x-y+A{zlJ=z!S zNQAxZla~gg@AjU0j?hOx-b>Sx_tDxMe#0jvx%wQk67XF*CfQ#f>~<{qkc>cL%+ti5mb3n|bSN=W zIfw{bcYC;p48Re4Mys<%URo_;`S#L9_&P`^Op+#c5Bh_ti^-m@dMc%*=sb!_=NXfB zcTCo5_=B)x?K|5CeHa8gOCgwxSHBmTtt)gbZ9O%e+uvnBESBq~Hp zLGK!Yl0RlyDG%M#h-oa@>RT+>mAXP?VLTS8@iF5QJOuP3Q2jlScp@oo8ua_Rt!Zj9 zJ620l$bL*EsXcA6quGHyS2rpar}=!xD_RX=~n`&5h?jk=#m8*qze#3s8$WAQS1I}b@Oz2qa6 zc=T?n#%&txRRo$~uy&R|L`&9AZmtc+5u|I>58DMc-NAO3nEqomZnesLK&F1rq>Shyf zs|Z6+;n(P-8EdXSMKul?rV0#hHlr)0eJizVkd3>{=q`L;x0sh~rhQZM+eh23qa8@L zg;O6*-VIW8Pry4;r|bFbBk<2RH2?J9Mc>j4v@#UuHm5&|fA)3`TqX#8U3xxP5%#__ zdAV>;kfdpvAwzPe=8(cag+nf*ggP^kW{!-0t2_Fw0yO8lr&4G9Ntxrq@h5AUodl>(P>xABe_E)nQ@LP=m*x`Wz z9>Dk#=4gL`0e@B!(L~sEEvzXwkES{evck=6k7!qPBQ6TL=EE<@nIzT+;FSGtk^A8ygQ~7UH z3M4i~%)!l=i!KP+7bkx~39us%Ux~$Kmn*ypd-p_o4}K}>VW{zF8X7FdTno(Ds_L8J z$(mk5u|s3yS6*G+juaKFAYAKP-H~VT5{v~g$g)GLJfq|NrgF0$03GXDT{`$B1<2&@ z=oH`jC70axh@mwm%z; zzE?FFe_afPO{0{9w#@n}I5bdJ{V5!@hHdbGoKxOFEX(2t`NOd{jJgMznrdgXq2U$c zP;C>zV-{bjYl?hdcc+{YsE*5=H29zy3oJ0|?iKv)XyNsOH2e?dz#oaFTNni|ks|aN zV;h-yfq!e6g1ZcYxRb3br{+{;V^G2><51bv!J!X3Xd2D41qC=8K)0;J((LLp%|tWv z85lXf^`KdCN-C{i$xSwzPr(tAQFjfUCAyUy>?^1~-^5w7^XrQtfJBp2LGj@%#Sb{E zNL#>kl84xxz9Z7XXFf$)uAh=??)NHAkz}KDGR=_mcGd*>=kfIXx(O0N#2KN)zo4;X zjcrO6wc3&RV`zp6Mc=i1gXjizNpa_9>JtaxQipw( zusY+1vh^j%i6~!C7__iX3^!%*59yf}q7_wqAXJeId3Q!WhmS1$JqQAxq;^_`1tOAP z&!B}KQ7mbF6vEv9OJ%9k^7#efc2NGwm#00N>aUS(fOJbuye-q_q>8B=n^8U1)F$Xs zm@3OlFq*pzI|DWXZD1Y%+=2W|=X5>pXOf&5Uy+?ql%7lkUK6Z)u8JSW7Nldr5RV|I zY&QTf{77IsrsBO zOq>@P-!85wtENU|RP(3Gm@!{~zzw^XHM|^_HZTQfg=a{3Lf8M_`{MH{kZ2ct{`cVf_Z|E2-e~My~%9e0~`vu;B5V{WU)C;hM$QiOKoBa`D$E{X60OCy9VbXfJsYEpe&* zY;J|h>!~xlzn|tu@SCkp@C-`Y0k{k#H*v>5m)Yd==d?WNe!m?qqj}*hMZz$R?cyr< z89_IiCa5>I$N$FGxj=eH;eNaGjq?CfuDmDhy>Xm?_h+Xm@Crg&3K7N2kLN4OTrZ!$ zsQjiq6_V{G>q~X~QgXWbLgI%FGA+2xh#@9yL6iX~TZK7F2(j+rfp~MoqN2R@K zdJh>fk-O?vN>;+griu|!D+}vhl3Z0~lR%lawq_0yI=WhO z{q!t$v7RQ%z=AZ>Py6|HXAq#tE2;J0WP3OphIXWyHO}R;a1EITnJsQD760@l>9hgj z%nbPDE>pfJK|^X;%QnL6K)JaRc`6ityp{`$=J_gkLEPl4@#&HhZK`s^XemTJ;@l#X znout0rGkWV?ko+1+1Hvd`*?uDu|-PP5|cV$|5b$DP~U+pQ(zn)3XzxE>3M8{&MPel z#edr*pG~NEMyiDg6=cc1l~7Xdt>ly2go=zMvZJB;Hjln7P_9>wWRYo)$(ZP z_S)?+iMicF?-?Yku&1OdQv9e-!2$7A0~~jgG>xyecjLxA%3KQ5+&EdK8rxK4CX|oS#VjiP)eF42N(he-wUtM5eHAdOW8N zcU;xy=*)S#6SJg<9?bcAFr^^`c&e4JCt?y$Qw>QX)|kDm&)!Nt$&iT6<1RaxO<9$j zgs5g_ZgGBgL=9$>4CcF^6txbVMKp`x5}HLb-gn~HWo#chsG72c;WDkI&VQ+5#kZ<= zaSxx#WC7%xCCdZt#ndKzpq$~@s)}Rnc%-9EgS}h|0X4U=#uG8AroI`*W5h-YJ6P`| zgK|Yk5LR^^5oa=Do+4Nyb}4D9$s+1?q^Ta_9Vw!o5o7i1zDL~SeeyUy;!*adM#xVk zKWR+vIGcqsO9aq#uXBh8L8~7F5e6|V7!YmhgjN^JOVH|huE}Kz>l4nsr;0OSYRzEt z@6r{2NR*!He)|ml?e(AU?%RJPlYILIGNneTR<}E^$(Q=bNFMI1TVWUfxZC1)W^%4_ zb5d6^bpa&jI z7%jH@PVjQckGC=)XaNp5U*kar zrcnBA7cagx0~AG0n$CYhCIQMYG9`yY-%?Go6d}ltm4REBUOp~(=sVI<7^x1fe={{z zawN#%Lh#q}O$EWf>O{UZF8eLT#Pzp6oyl46=H$c6NKF35{qQ~VVXFU@d%cZo$bNhW z4X9fgDS;+RXj2Muix1!?94}(a@gYSOmP=-APC9)D^eQ+KlV&p)fmYkNlR`0v8!FKS zgwKh(=dCkYj6D&P#QBAhOL;+G35ANOdgJEX2~Ej_*S<~#}tgq}U!{c4BvJej)4>#`nCuW$4+`T}aF z!))OrllPsjII1Wt`k3+2-7KR=dzkVv%jjpa%ji*jIb;!$H_>Ss{Y-Wl{VA2_nNG{- z^kQcDI|5slTK1ShXu?=>X@Quu9o(}Zm?%4w)mQkQ75O7yJkD40OevL9{IHa?k|#&Q z!(y=x*01&NA$u%Bx6c!{eFK8=8_EVB!5pJ{sszP@GoPFycOUO^zUHDIJ3TaFiSzP(Zy>9f7NH0cKDzv-z?zk9cD$VJ^m=} z2ufOkLYXXgwf-gBpLt~M+8;gsX%N{xL-wNiWOQyj@@9weL>oW@P}=0twY=%rkq2L) z_i=gUWyRx^|KsJQQ_hZeKK%FD9VeD8D|0o>(ua9XQpY2zj#kwNcC8Pwd6Kr?lTK*( zXU1DLR6i7a+NAEPkJ3JO|7}9hdIxO=Y^Gk37F5-^mC7)L9l+s;AB#K@zv}JrB?W33 zD3aTC`&b582_>Z8LtOBaq4@2sGC7qQ;Jba*_XnS%X15dU|LT>;kY`VkDrNcMt6vW5 zYjrMAdWmNVdEVjtm~wJ$ROJKpA()AnaA3k?)RobKQ2e6524rHIIR9yu4`e<-qo&X( z=da0P(QkaQ{K(S6>eGYv7ts?>82v~rUTsrBqzB%C^O^B(?mr24yPV;F{x zaRZGf0`un=R&xX{u%Pq)0fLt3W3JBmZjGPsevX=qC&tg0&z@q+wxi9qa&P%gJn{$m z)&Oz{eNP7r$`pWBi=$UWtJ zw2+VD^Q_(waP%MyLl72ymPNyxVx2`m)@$7Kp?kFXqc*JKst9 zbOOvH;rvc=!g~|WFETkB&Sl%Q%GCWV>byokHtM5O_G&PVA zwjv`0?O)^>y(DKbUfSXzv;Tkzg0gpknm(4j%UWjie0!Jv%E%>kYR1+@+oR>#yD($x ziL}f$dfB0kUb6a2X&eSd%HHAe9D4_haXym0i`%|jYX@!b5{^HsEku4NA`>9XScr(7 zP!hLD8f5N#=)&t}7hYq~r{&lO{ayQzw0+Q{YegQ*2ic>k(>2KYq(LU~i@-}x11bBU z4+pO%7hbs5%C!&rF!mv7`=Ffm<$~;2$vb6Y&EI#);}>atdRjj$6Q^mZ(BP)E9QLFk`(F4Xr>T!u9}al$F0VOn z*06y0*2lVUr{p7?^) z<$3RcG0jMXt>vkH@|q^)y$@Tj`mKk)1z>~GVU-1unR<8RYbA1o?OWx3>z+zGF0U%a=gPBpTg?!^J*LcWKUBkGz`MR?pkK~3*%NC6-mM&bvp?_W z7~Ci9nVgS0S<{CtXI3HcPceAmIMup1?yhNSq)pF?+rkP?c!n!0-jXI&9Aq*_a+1-? z!58n1AH5y&?yep%lyaY# z)o&=}N@w*QN;!|>*CA_nddZVxV(j|CvlZpy46kMQFd(nUTXxH)v7})B6~wFVbe@YDo8!;ToYk$c87W5Q=_MOPv%%Bf5E4gT3#BN3QNZtjM{N58a1AGEPpZD#W zQvvWP0qUtggn>Bc9VjkH%0Wk} z<M;XTKhRpiX-7n=9-Joo#&H!;Uw1hhgmi(5;C>t z$nmDaX8Wh&si1IaPj%`^HVm}yG5eQt3QA(@*uT^Vp&(b~dvJ{v*yOV#rI@urgF^y# z7=pchdBD4~=F)&2fLh152E1)G=iw(2gxglrABIrm_a3Ujb-_drr#s_=L@|qSDG|q6 z!fc1_J?w1WFT(IP1cQD|aP?e^n5hJ)+`{T&f{U0X6;AQI3Z_ zN8<|2jeE5RJ{&!+oN~q}dl)DdfuGRjOIcE=Nz4w{`Mj-=NV6ZubhMM(tx(Hjc1&97 z^Cn2ZIoyl|G!0sL$BHaWGrPEIqj(&#!izL#vE$NG4$Y21#j*H$yJ)u+c+`w7-l*I< zFss`I*IPxqaZ)4k#Vd5W@dV=dc7|oX86W?sGcu)yBl#^l0ly;(eJ~!X?66if@9Q~x zSFLh?@%HT95?I?x%-$m4N*R!6|>d=!`)8mUCfAt(?GDhw7}QasL^!0`Xy9 zA8)t`d1r;J7tQ8f$eq zMghrs`)WUUc%6NnHKWdZwB~AAx+_|=ueKD~!Rhd?sW_S>X@I_E#FsO}W%O$zpAZi( zsN>;*3#0A2V={>8uy{Fc6o-$thwYSDv=v)NTRN9erCl)GD%vU9H1@cAhpp%FJ{h)> zqbnXcCU8%2oIgjN$rfnen9jn19$&Pj$PUblI<>*s?-teq`xwWYJ5nKuq#U0JX-tGP zCPEsD*>VeM%uSPq-@Cmg6!0FlXUwY!37=<2zvfaOeEvL`Ea6{`hG#|hKYO|GgBqCyE{~V1i<;`OW(w5VHOqzxb~)jtQG z@%suHf2mg0c^S3Xzlnt-nvo>C$}*W zkhv}SGW|jD9VAAzu~BLx$z?N&CosDS9)&dM^|9kvhfvIl2Zj%;kDbX&*oQPzjyVP6 zGw`$uQ8;ZUiVJF&$ZI~oM3Ia2xR6W0`mKZQsa1Ei&Kv3cITzYoIGawp5iCJP>}g-ZUoJ;a{t*+<>(Sf3?zg_L-!|2xE9l zxqShw$XYjhoy-w_tF^CO1J*|0?A3CoEIfCXn1Jg#swHq9-=_=j@xMaiyYrbw*xP?h{5S&33zgaDJ^nt zqf2W3iwS!9?eesIl~yUc!j<3p#t=Ium{>m?W7t&4{;iNl@)a0a7JhCE_^84(`v!!r z4Lk|58Fe#hDxz=>yzyV{`m>(m`m_E=FquPdvzu5 zt6A?Da~^>K`G->9>BbzfAHc;mZmoYPY>Dap@1ffnSjd5H-D25yjum&0UgVbk+EoLT zd!=l9@ig^3$kc4&cBhuZ+y`>8sV@RcXKebt#-`umS{m_6(Ylt^6`h4_#gmZag-qOu zlq$!`$C3AMF7vK4#;k)KjA;vammvu*L*dLcV3w>B3=G%9^Bcee05SeSCdEhekTOYy zt(QfQXd&fB>Jdc+8=U`ojSk0esx0)|!}|ri%h8hiZPgN^;WPjWuQBbpf^%wi*;no- z9tz8xhSyH;627$2Xn2ZSIHPc~9L^|eqyF^2)A;7|Kk=tPgH$NO8BIhUMgcFXU0BF9 zq4?dNu=hS)}&|Q5M@{z>< zM8IlGmvGUYZH__mg@83_NT7b^FuWyT^VcesvpuGk5TDG!U$z{@1VpX(WPb-k;yiwW zrjYeRFOo%H`KA&3iW;{WO(-pL{1YT^egplfyC0zJ6~(?-Ho7h}?JGoCxLcK*!kW3= z*`naBEg6L)DRW|`5Wn89$ko4LZ~I;j++22-#ZNd7o?P58iyIAri!uDkG2Z|Fd;=y&+cNj zaGrfr4bTl@!Im*h^C*3>oJwR6HxrxP+KPvhV8y}}B1r`8h84ouYiDdB^r2a@o~1~| zTR}^wVj~U>zT(M*$Ei_;bvR1#VxwbcikqOD^S#&*jb&9TKd!F{M7(F@4-x3GlpdDbcw>IDQKTl8IIpsi6=u2;OwS!CSY1-y{sIj zL@uelZ&*R(LVGw{#Qw?wZkJ*!XO)@Cze>Av9`+%EUugx>nQ48StVBPgzY{qhJqPC0 zlp3FrAF2Nw+3Bbg`x93=mCLWxO#h+lXFr&w0An!`S%F=M@4Y3mf*AJZ@GiCKV6*8E z7tB@mh$B?K?ar07LLo#3KAriy3BI?<0vdd0PMWb`@Jio;Lf>BteSa$q3|>i6K5i$( ze?%I$>sQzA#tHjN_>*{b%J`5R>**}d`4$#Nk9w-V7L3-kl)-4K3aj^%C(|Bz=~FLR zIrH*>a-p7m556bFdxwvWEu&7f4jSj1{B~#^8u<;$&%qwRd{&i!I8>yTrR{RzLCjAVfiJzIde2XT39dA@lH@SEv}x!5V3Q^_jFc zU>yt8?_kxpK*$~y;NsMM{s#ho_SZL67NU9C1X+5Q;*xz{#2>h1o!>gzE^d$#;{WIu z%Q#=2rQM~qyP(|-SS?(P+bfU%l2&V}G^-8TYa3do@}D1&C5HZ3>U18u@Q1C?v!T7c zmjQ$yoT91uQYskuVxCvwE1)<7Pn8*zZx3c3OJzh5Jg4hPDZDt=eK5sX6 zv__5>ZpbMUc`5bDzZJUQclu%+q+i}56-M4ne@j{twy&%VCWuMP-I>4Nfv3^()uWZ7q?Z&+UjK@RzM!9)3k9SviMxd z;Or6_j4x4{6LmvnVJd&zHgaLA5b;sH?K#~E#;2C6o;!cMlY=2*86Dd}EnM34rAc2F za1l8OtMCQ<&*Q(5|5`HMLzP@^(-)CQdD2TD>>cBe%k5LA@om%E`?mR8w{6b7Vol|? zGyb^L|8y0ZoLfkTVq8;NU0a#HNb9NlAid_YLtonTrAc2Fa6z{vLM)$_r@T+kRUJpa z_bx;hwuhnWcVRKX5w+j#Lk@^BCC%0VjK@{OguFzdT)u%__JkiClJ4j{&Pu8Z*-Pg0 zEsAw%a5rDnuKK!@`SN)QDB0e{S{H#<^lH5J$3CGFQpcw%$7A_rFHBF0G_ zZqu;B$;|u0>YQ9yrB0OzjRAXY=bLB**gFPxZ~PNsXVl4NSbp=2Sd_F6XyP4tP5cr~ zO#V?y&^~uW0{JyYuKIx5&dRGz89N@?p?WLB^A*6Uheu2UmSKy+=hC0~hjCD9E~I7w zt;NS=FlTe^BCk&8#H!DFA!r>Is~IF0d%9y5G9FhR=)kccOAppnnbhx*dhM%#V)AJS zpIh`i&L{2xd2+Jdf5(B2!7F9AriYEs(WB8FJ*-#_7=oGf^R!H+((tpX6sQt8Lf9fQ&2QSK46qLR?4tX9R#8JoOX}?Gd}+pRd9M})m(K_^$y-5SCW#%mtjtk<)W{h}wrq^%aVCcgP~>DYrXs0uHyVGf9e(_9AJS^)mKt>HU3C zs~>4Did_`UY@CI%^*8>ze;w1Ag z6Ysu0nThvdNzsR6B#(mh5$F3-t$RQ!5qOvva=7U?pU50;(nj?$DeWF^QeR-8PnDu} z@!jO5-ZCbvd*l;VOuo(e9GTiUK_Uwr)wYD1^W0k}n=1Jv6-j`vBSyK0n?&2!rm}P7 z0L+rj><5;z^BhAyDP;{QaNL_Bt!qQULU5*&NJh2H&sm9 zA5*oY^Ep42NAzK;nCdl$n-sk4yza3|xs$Ad@TFoKW)~+)6&&OginPUT#;>G4hJ2*# z>I`D=ut7+EQeX2fFYUHfr~CC!`fG{`L7X+=RnJwZ-m`waWQIvufjiavh+q zVy{LM6V6CV35@pc7Eu{I>~M~3d$T)xGw~t7I-PM872- ztoHI_)Rn-O|=qc5M&FHXYdb!H#CP&{W>s5jc;haW@5 zGGEvs11jr$ce%N+mn=6g@jWM>@-5D+H&qdEZM*a9Y!SO}jO^7#qvECI_G$i0_F3yi zCxh$2@oo0$-u1KkVJ5_KQ~4+L#iy~{Y@htNcfB~{3E8)|&bnDNm$0+#&eF|#@D{#a ztnJqDY$wjeSOmAP_glNz;0;zZY?1N7jrkT?YNNa}OAZF=jz|o&pjD?5M_PC8_4wGw zoBghAn20~&9m*T^&a^jbRfl*aHhv)SIrDURM(hTXv_4OKl=aKQ3gS2ao0Q1cYRF*{ zI&cCFxM2CWcglp5(HAKja{-=F_d6gOvak3bCbkc!-DZOIEwh65`W3Y6Xs$fL=N9Q}Z=8>H4Jh81(8Zt8exp^;@fl z4ll16Fzad}clf+xi)#ALy3~h-+tASiXOxP)P#>{%+nT!E9zD=EbgajSi!FLq|7DMs zx*|`8;@^c5Cbp&e1mpMy;Vx*s61MR_9D6M?`MD|zIhQJqLRd*0ff5l!R%p1Att=0+ zv>QYQwZf<2Z!7S~1z>w?XrSC~6XYZ7# zn^wG%4B~GFr&BomkM69s;#ZOsz@WCDk3;Ytw!@7%)s(cxKW4QxJ3TIvXdPB_A61kM zrsf?zOpGP1R#8h^f!gSFi_LOeOroMllM_Yy<(Dx0OEPPQE%gOhIC%117 zSGdn_@SK$|tr?3gB8Bvh?mXXTJ@4;xS3~noA_n|Gc6@#Ik7dUfjb)=hUixo7`)S$v z^~bL?P+%^I-!foU*xH3bp+8>pZ?j}4EABUDF`Xbn;}71nj%ka!x9Ir8esB0OBlav% zK~1)xuoY7gBlc$={ocr8BlcVFd|txI7!5yDx3~r{8s>1zuA9;DL&+$Pys)q%vf;d- zUH76W<$bPm&m_)0iK>{<&9@rS*Wf^-?V5dP3*XYwAz^*0P(H*UV54q@uq&~Jc#pu2 z${gZ&*gBB>|48nvU!xVUWYvn%_Jb-`s*>ncP{-Z=dPhY|g>NpM@`arP;9rYkjrN&|HO7&u!IL44OMv-)&Xgm7;>Va-bxJu*x@!vgt?O) z6Rh^zszIhU({#5**~*k2xcWKhd~JF^mU)={*pe4VTMr(W(ZTd2Sn={$>(S%V{IOOR z7+9qGwOx>Sarvqc7)DnF4I+BXm=?9=%2qtPty>Az+`O~gEWzMtMw15C=a)mjvzHFj-LJwhUvvcoqbWzqJHZCwzQPhSDdMjc4s5eS6Mbw}+igGMJ zljZRn3Nb`#xD(?MMg9Kx%tG8uG<=c-7CFKIn9-o!tEn)-#KKY;g6 zQs@C<@|Oo6^vCl_v*LG>14S=f_lWp8$r17DWN)}{og|8< zZ;4hVG)w#I<5%_o4D;hP-rlg-9vs$Ls!b4%dm|4UF*zXzBF`^;%Nu^si2XN@e(y_0 z>{lv1u*f?duKJ++;P+O+ScRX8WlX~i$!twr2#--QRMHN4VW@jMG1Ow|v+g>4Z9ja~ zjk=3%nU24b2M{H`?SX-Yu*^qpDOscCz7sDBe!iGo%`4_lwy5U8*&D5ik4QV*55QTw zX-*Dn9iW&iy6W9jVUqOOj-Az^IJY6=q7ihqbq+2j{5rsOi%KaV|2lIqG-p<%A zf;cb@fj-78#Q+n_%2xa4e$30>&BmC`86If|9>n<`n>&h2KH_poYdlO+urrTI3hv4z z^W9a#B#+yHg|e82@cedAqH|cwj314KPbva3%a{Wc(p!hHAuW&JtBg{9DZEh#tg1AA zb0NIDA*{KjP|=JduVB47L~mcVx(d+f(1J)`JzA}*Y)`dD zh+WBQ&u913f|~5hU8oWWK-X!$mKb$Kv}{g0O(F!5HGV#!Tbg(Fvcd}ycq9gkuzQ$= z=2rpodwekdISVKdFM5YxkR-Awx?>0i#eD(`fo;zEkx3Z{U^rTyT`bOLH$w$>LFcZP zjCTLn(GpHQ6JNY!l^O5HKHLR;!kf|#hS_w)q|D81L;;kpqhoF9gss=X& zpGybp(fnAg6xY6w{$M4*Mt`ujJN*GsLQ$nAKNqqudak2{ql8bKQ(CwzbzZL0Af3m3 z34g(BKVnJs6l-hPzOx1QP;XS1iDLiWg!JNob0_O*Wy&Ip;BY5Xmd&b}V#R^4Ao;u4T4=eIk=MC(i%EW8mFVikD~a?G4gJL^AD$V~TRD zxXhvIc!9tE7$*$eWe*Q}sfPKbM1yzP_~$(?3xoyMDEdp7Bu9^UouE`T>9hl zKFuvuDkDNKsClTDGjTz%y1X2QwU7bK6k2->|-mK6sm`EW^MoJ$+IDL^Cq2weW z&jPwam#q!NE6Z#h-UYkKc)tPh>a~Pa3zdm{ zHvst#8y)1%l7o1imgSFl#bx4P?vkD%?{d;W`VelkZy?UrzM7!@B@P$Xm%W;E2^i1Y$y99X`i}RZENl;>I337xH-^dJ~H{ZyS#peB-FL;eo91__+N>-n> znAFfYti>2b7P;>5vC3_oGI5k0>hS1e2BR#UIRiOfpoppnae7MT1`aP+&pRj4jtlQS z*rqsHh{F-1DjwM@AWvP?iC^T_)V?vDjrbeBXp2WQrf_%T2R~$LOWmT69LUiFsHF4J zgEW=M(E~q655Qb|V!hGOg2dwUzGyTo;RbQ4;c=C~Qt&Z#(`q#Qh8vcE!YXov8Y)Oy z@<#M>2b>G(jPUjUK+esXzdQ4rdMd$jgR;UsY0n%7MX2!^aTBW-X=8kBZ@oV9$0rbC zZzdu3?(S{hSjr&@uU`&7YAKroqBWObL+s}$Nrm6;-3Y(&I*V{eWRHq-#} zwy*mZ`wiZ*_NjN4v+F9`uu1`>PqHO?(T4(8Y)@TG&-5O*9O%u+Pd@t$t6#k~hZ5=i z0FJIrDaWuB~}!XdVBt&2V= zLX0^F=nl`*<+Ch(qr2cd?e$ETh{pju?ersKH?@2k765Vrh zlwJKoWA$wTLIW1=4OqJa+F&pPe>6Qo!X?Is7y89SxOa3%?~-jLTb14pValgu;QHp^ z#vP%Owc(PZzUZ)Va-68 zmd30gWk!#7L@qYb7e$YICU55l4WEoV`mqTDxVq5{iWGV{Rc5Wqf7!eX{jF@($a$EG zR^%xJ3kR9@+SFk!PxUU`2oub}?*k+&9gxbXlOsUUS?pBx!8YBwv_&yjp-1dJN+is; z^`ceUr~$14(X_W&+nSTT%nIUeD%qFz8uv>eqFl+#l#zwQubuJ}EX2*kQu>Dcy9@c( zh)H-ECC`ie+k4tA^vS;I`9?E(yrX)**}fqux)~-}0j}s?8pH`5&o06n4J#FMU1v0i ztFxgGspV(gunCSlm14(W z8Qu@PtE(psZjw{Lx6V3)U7W#9UVcxEf zJ;9RrRln>jU0Nbddm0xO140afe#%?kX`q0lZK=-KXhqiUaJk@?Ac!VyhjVyjA1W0q@TqToxSbQ(6PU>aYt zgD0n$Voc4hX^-;@sl~qPOZ#L1TCg@zpm4ly<=fTk8S`YVcOi5;--Lnp2M69YFHtJQ z=+1Y|!e%tG^PN}7D-=3=xgc5&$+P%PvBGUh4Gu}DfQ--2dskrXfXY^vE`C;!u8XnA!;&c;6FibK*N``P*d4Cd7K!~wOX_s_=_@vuYWMnDRo{mIRHh}{ z*o5FC3VeKIvV7AnzP#H8_0Dh5?|4j2Vmy_}yVF=B};S50B;;a@ZoY%FJ@Y43|5{*uMc`exXQJY3L;Suq+Cc z)8!dkMcfw0{NlLM^$-cfhAVgRxat=>lz-5Nx@vjnNDiOJK;q-sWYe2LDTV%`ZwyNC zO?VLaDYki~kI-Ep_pVZhy>H$fKi7HpEyNZo!++E8)zRn%u>b0IzuvLjclwdP2R5^qac$&*8pTy@{esZSf1 zN#9$3qxwEab3F!%tw{PFv_|T_2d%N4`Yx~N`#Vfp&}q5^@Rz`;^qmr&`Yx|%^8z_f z&@hko%PASx2sLPZ9@BwED$s;ls8-1;RYKGZ%IF9)JpN0NneexSSegja?Qll8Nek0S z?am+c?Z+Sp`NlV>MR;iBueg59J#Q*9rs&cS?@0py-2N(E{>SOhE;d|DQiSqVApUVG zdpa{Tv}|gI>Xg!3l&;A+4@=*K=zf~bnx^+}FJjisQ8IifsKy0-Ko_6vk!$rkeVQZn zSzYJQY@MBm{!I1V?M}sCT)Dp3c;CFKpGq6IJ1-%pW#A8c&7=Q`$q{+_dHjo(i72)O%E~Ux!UpgKU#eIuO11Ts>mnc`ufqPn-*abXXE#AXt^cRL=gH@@nVCCx?#sFN zoO|v$=bn>i@tbC@OeinaRLa+IVVKa6G*^EW+kqK_5%5&-hA&LSPfkKxU(Ku z{!KNwfra9wsU@4*X2QiF=_poJ?ia?6-IW?&XQi2MVL+?l{f|~q)~-~&nuMKDs!`of z#o|B7{PQDR(GiIa>Z?2iHVHuHKwoa%OUiYepCxVkI>;O!J;^LZYbgDLRIba9{te(7 zZ9CyY*GDPy;Wq#-@By$4>tC#E82JsLo$h0UL`usDehY>*34Z}hm_*{Xk_p_*viX-g zh#wG1;CafCCwJ@HU6R0nEeH&tCETv#B@y-euc9R=)Bhp~rseOKMULf9(~p5XQ0^$R zs^m}8dl%OCZX>s_e+j`bCKMk~grl1wcy+0Ug&xd(m)ZQ37JW$kQ`gx3sUH&m)HTVI zjjOtze|@puxM#IDG#et+-_@{w97zd!;G)ilJ$Fs`go%{y7#`d^{;3->_H&wny>}Vp zP7U;5H$O6=9LGI#ap1BwJjUdgE;F}J%frD;Ix}YL#eT--?c`LK)z83&`hc0;(ZE*a zPq~uPpLk?LYG7xhd~2%K7_=ib+lcI9;Kb1zu<5GZUVQHU^TG5gPZpYfl3syOeTucr zXfNlxyWam=_!beq>PY5eFWVqi^XF^8x92wjU!J8O+b$>V>DtH1o5KV6*JHl+E!N}} zMmrVih%-i4cJjlOR{@L}j`x!bw#tr zzZr$SnZ~q{}`Q~h_@!wK=nuW&?QGf93!z06wdR_DIXtw?hFZ?I#7ys#qY37~~ z-s-WKIA~08Ca#>l@XukW@i@ios@kIrbR7JlV~TLga?cif-emd1QHEAxAQ$10xdeWh1-*Q_Wy+Wx(CB=f;1C#)>|C`R*}!;eLO`$pzRu&SQ) zBC}cP`fT{v{$$$ul-GUzfcY^u#`gJG60rSXgrlPudBVfs1g>rwc&K=nZM*YxUOx|A zFz+2Y|rT%8^m}{5#7`NFyMVf?SE~2 zgU62Du%a-pdm5!$B>5!Z!_m#RO_7%zWP-@dwH~PRGOz#)>MN+M#$jt5X)#Yl%R&Zt zq4-U3Gqo=KV>Ikz_j+l2w>lnBhKnV?o-kj;eiK_zSv7#9Ffu(t|5d1f{$L=kk=Ub~ z74+=0EQzO~p0VVeRa*s#v9!dM$3cCbdFUA~gOg;D{T@W|RJGAwS~7?}FwguBaD)TB zD4jR*Z(E<{#D{eDgR+xxkIQZF*|0xry;Jni9-mHs`45f{IE)*hThf=q8!V_FT&4e_ zAMEt39fR557}cq0sCRqKQ14i=oq)JUCNZEyMWY$`9U2$!(rOoQupK5hvFrVDl2*vH ze54T{-bR|BCdHuGh`@%y`$~}&dSM_i5rHX>p~iOUN5Y28Gk>c}8MO(e2y&p5aVqVN zPmxBq9IUMZX^08woluTkXv$Q+EU;c)hnGI%!FFsyXQE=fOUVbW0x!jT>-0$I{6Dcr z!P?JT1f!p~@IE|8%9u`{B2a}j;1WzN5*r=9A9iCX-lIS23dOrzgG-v8y?&gkoJ3;f zBOE(12YR&dX;I0;yb+Cg7O4EX$e`6RHR_7)nM$4<1e$}yyJ^HF4BtTM3} z7L%h#gP*&K>`Uqp@3L$sJ_WfIuwTbw<6Y{Q#{C%qb|umPlVUdGWcAG{IAr4h`Aob| zg}XZe8s0?@O7i%+LES&Pd#5bot4{&QZA{_F}g4bj`tNMcIp{OJTJ z6h~U^ffn{88psdp#qyiwg$HxdtTlVUQ)*m%0-9l9RJjLXBleg**gRbfx^a>6UEQRCKZ2u>AjlPIsn<2zI~ zURVUauurgCs?SPEcl4mShTX$%ee>6gZfx^C#@&$ zm^M$2O~7`1Gwfb8B$5UOzv8C78ymPVd z5fghYmAe4!`#Df$N;feer$fHxTQcI$5c>GZgJvh=PveE{}okgwTD`22$7|XQM`TTVSAy^ruIzDukG{0m+`I? zXRWu-SL@MG?|n=0Tm7l`zTAEI7upAkwgUQ{6$`K`RBTf1(=Yj&5+uxiwK(3>NUQR@xpv#^t z7dIUNM)uwgj027lBdzY6e|;FU-G~#xjR%Bejh-Ut`@nh^dPb2e6T_(Mrk30L@OdDC z!PH{)@OzYQZG+-}nt_pp4*cKS@-eSF{1F!bry4onnvC=ci<%5Uyvxt=&isyQ3Nfy) zQ3!L3xD=qmlh!_G(=PQ973UvUN!#zo2Uk8?_?n9>z5EwDH+&O7*-?j_FfK$SoMABl z;&fCel+J2_`J5_Au-6FgMb%S@I`LU@!K!>Es>g>X@)rTZKnS`+vEx-q_ zeT)xm!q|hE<6Zv7G!jO;_On$XY7B%#ehSitoV(sbPJ10nRnkET$zATKFkU{&VmO+1 zG3$`QP(%_`cB(4kpY9@NA_u;_CHu?g<;y$K3DV~W)tAHFUp~w))hQP&n*dO31{#8z z)ye>od8J}y4p5#KTg{jB2#OayM_`SYR0-4=@AC0hG`yp$;b%+XarIGrih{gc0QrVL zNShODU7*-C7HQF0`~dckJ6O~VG=409sC^)$eA)O5{+lwBs z%VS&cp-fII!!yjF*BJsPJuu!7`v%6yh!G5o>jIfF+CSbbM^?0JlEqC8nv8Z$0o{#9 zo|d3l>&*eGwn^qc6z-gZQ@Uz0TeV#%8M>UAM7n!AoGh)V_k)#5s!xCW5wZx{VS4Tv z%|_z&W^<8xjnlyC*Q@Q<+sqf$>wm1VE5_Ha@pZh*5`N;Y^bbh?)H_CIE+}%_abnzZwB=qRf9a@;|%W>fcC;7Y}EXn-W&67y0jo?r}q}DPTb*{TD{*>WViSv z)sE4u6Qjb`o26_=bQZA)Q&?~Zh?(80<5a8S%!XFw4U(4J*QjdmWIcCUZMTFyAeO0? zs}`emt><=O>u$`OE?2Y?Ef|e`2q3W~;cC@yN7*!{rT4MN+0=NKBr~`{^&m1~$(W3K zKq$uxI?R#_(Dl^cNvZ1ybGxqre886R3$!e6!gtlW^^{yk)v#r!tCmS1YqoB^Li>rV z+=k=9k*rwO4D+{muc#rE^y5*Jjmwpj(d<;CIHl?G6MlXo`(b_dI*W*Rxee*uO`z?s zpN!Y$-zH0Xbd{vvz;qAOe~ENBB9s=ypJaY;XnYOPrSCP_XsD9Oc>m>Y7BY*k&DW$r zslj|v6`rdq?a9KW(k?3fcvWd9rc0$Am~KoLa#_2%MY?`|iu#!|yMAoJ&2v=!d~KGg z`uT-<0q-4hc@ydY2;!!6wFnthYyR?k0YT+TN#BdHQmvW5bVEE2;;@XXHT)E;_c0(_55heqSba9r+==xDA4RZUAt=88E9koMX`(#v&B zJB?}9#P|c!S^GRr&~0a_3A$UZYJ&b;YP&_;J+h!5sKTeRAdV)}Zf4=i%Q%F+AwxJ! z`0wLBE_|-NeuBM&U6Z!gF_YUXW^%H3Fkfb|hU{}GFXl&NF31z|qiowc*^-%K@R757 z*0Fo0p&f97LXyGCHYW)qaA}8&?>zxCp=oNJbfzxxhPrB&`p37pr`nY0Nj+ zl}{!i(t=DA`#FzQzlxVa^&KLGsg0RsQ3l9c_HTeZ?BC>QF$=zw#7iB-u)kBlZ{8A3 zofpSGa?`D4}Ab{Gl?{0WP&y*_1s zDMYqR3Do{f(T{omQ0a9bv4V`FDnnylm531&H zzPYHwL{3(E6uub0>+pLQzs_hBn_r1v#(W(Jv1;WTvXO&30s%UV4dcDAeMDryej|)) zMup;MKP@jV_#-Z5OUE3q!v|%{2T#!#CopMp^u@_hZhQz1$*m4~Hl#L`?yiEa5HnZl zO0Q4!!uPVXs~TA>LQ#KW8VYz5S#qjIcI6ZdmnKhwFj?bNqn`=bT=p6mhJWupsEvjI zLno%@=@lb5T@FTpK@}tP9nnKAhbPk0BCFmdiG}zsqu!axD>5p50TFc^eM^(-z13AH zfbyiJF%C?mJo$4tJ--uYmT|V+Zwx6$fF0@25DlVY^S8gugR^=61`#(8gr(}8n(je| zit2p=4o&y*u(-W{29#6tbpbV_s7>{BGb&Nvn0EHKRFB69CR@#ma+u({Ia3H-_cY$p z4Nfh7OYx)A{gd(gE`F2*alC^w>?7Wc4}OYqeKDOS(5={m7rf4hdQ9s zme)w~KEl4g&QVNobB4g-o%EHp+m6wf(hJ!R=t|(*`?&`1!*AiO`v^*7q`i`#q~=8r zUEv+~8@#vjgN}bxyICExS)+BV#TgqMr%YT9L>yOJfW1hYe9zA01CXwCHgB)agQ6JK z7ByGxupebs*_P*xQD3X!zI0vN4pZ0Z>AKnp5cKPd;X#7r@=1gvmrm=~=PRcYlH}+o zFyp*2VXBP*#hMEF5_a^_F8LYtA%+j~Gf2#EzC#n6$pP(P7nPqpR>4QoV2*sN1YVEa zXba*_vFM9i9=BZdE3-4@NiO+Z@?VhSM7{&>UOaU@7Q=h$45C%y7aX0k2Dz5jB zn%bf5S*>;4p}l5LHtv6|f&Ir@9N64^Ph&Dqnxyy1@4==CVBO_A^y%7bcxK>G+C4$^ zKuat=XnOQOi^%=K=$Ot}vP?V&jG_6&K@=DM5fSy72`&^Y4;uYJ(TOI$z9M{;sKEokwiy>TLOi%YnA(Y~#ce_3 zx^}3d7;eX~JT46n!mbe(0`tSyYVpVEWhvX=EbEtuV6X^_T+F(w4iUqKeeedmpdP|K zm`jfMp9nGNz4-kUKb$j4-liXtDZO6XvtH}C9_Yg(-awxY+M9%O`)(dYkjd9?z@!^k zlq|(37ajJKwysY+`i=PHs-mf$v`)^k2XOnFrKkm6XL!M*^ey{~=~TLESYMRG)AZpY zkaV6e>c9my`+U&_IwX=QJBX{9i-MJJSrd=4mC1YXZ4P(&WOSNNDBxRm_fMsb z2VS-3VN105vwz{qXnpcjT%4Tqq!jpnRrL|LpH8%!+tO+7AEq_e+mTMrHBr?uG`9^( zRK8{BAd$0jrd(<2i6+*YNsvYvpExzD_3YYjkR#3iIP_QSXREdn!M{4b|L&h?`WNB* z1?WXne82v+Bf$6B?23_h7V>^;`2K^tP^X3OlYhbcya^)oX@c*kNlqJ&iSIk^ly4_z z;`^;qik;8w$HybX_hMw_l&O(V@1q zAArrHzItV3iLkHf{_68&$LJjwMVVT>?FYxQWvjFyZN_Py{i%%s<0tvapW=UU@^<|1 zn!F`wT(42Vjpkgf8K>cKAOnAWU-9oa-wTr{cY6>Of?kJJd!RY{5!%xYW5D(-Z_*xr z+53^xxuJN3@h$Zo$t?R{eTRSiPQ)ETzKS6TD~|yZX!WN5r4_F{%+|I?`lMAL*PZXBuATAWb&)k@kr)q!Endk!Q5T zyI|S%1HIK}vX$tB`BIohB{G*N5qt1d-|l%hsdp}l0WzYGM_ewSm4aQqfT7G%vQ`00p!fbXnXwT4B+TQ7z}!4c)z0Q1KFj<<=W_-9Rf zfnDNsX}L^)0bGLe8wU*pUYLB|>Q5<$%)kd1GQdGzB?|shd{~rt0mV4u)jrwZ8K5;Y+Zr&%khR8Jm+TI5sk^tUhw0qL2GwVU+Hpnor3e+e2`JJp= z48suY4xeQ2hj*fnNKp8*QbUMOt?c^ERHi=fK+se=RbKfwhjWYGI{ZEr1 ziXPD1o7h}^LEj;Tn8-^>f(GO##^4=pijyPoziV#GAx;X{AhUk%HNvt*|EvX7@7sp#*VKw6Z4I%bpLM4s)!y}4 z?aicD+3kf7-sqHEK&{)WyU?u20Irsa`yKcT-6!dhf$)x7r07o%)iunQe+e0}yS6Z! z{xF`AhVm(?x+WMMdnY#eU0I*;qhd`t3-nf>g9J>@8uP-jTzetK#(vaW4JXFXo(yP4 z;Fdhq!Ea-9gISCDLXx>2w8)yFYFO)DE6c^v3RyB|8~fw{m=IC`@3@a~o0tSpmd=M9AjrAvif{{n&^Ok_G0C&ZpWl!Rg&-) z-s(Ii?b*i#Gre>{eutFA2K>WRJIhUvxQ(&_`QqYX8gQ+Q>k|Nb;R#H{QimG>-g2+HxEBQU_TJQoAJ8? zzgNK1kVT`~>I8I$Ui{v{*wy2@96vhq>jTc@YWxP{M-}mL$on1qO7UBQGCo6o4-mRF ze!s@=_xSw@zrW*0z1Ld&w&CYNew*I^cj-T-Wa&RjFhBoO`j2#7N2~w%uhA!6_LnSp zE{1IGKLpQ(e|G9RT6kim3g7PrY71{{HV|4fuI}u6odg4sfn^J@7pbB6qYT$jx?Abf z;U&X(Y&KJ{#qq}8<#!-*mQG=Vx_Ya(NO_OVE<(n{tMYKAR=SLGd?+a)NgbG7B$R(Qbfbiz0r1#H^#$c2>{P|)HZqK!{QmjGbZG4~YTz>AvLR z0_a9NbOTgTbt8N?y8v=3@dK>(;}PwT3}V}~6-_8$hUI7U*YKdfTCvn_uzcg zSJp=rVu$Xig#Q=mqh>rulsGDVRLu3sVf9hu?EX*xdNlgi3;)QGI8y!Ve-OUMRE_{) zfgna5H=#di?uwem9n1 z$7LHbe)A{U`js|z!AID)4O->k#|M2b)Rb0{T2)FE84<6=GA-<{mN}tE1+R$ z_Vag9hsxR2$_Y4C;B#)gUc}X$hJQ-GG6{D|pZeXY+4_|~mrEP}{u_H9Qor)2C-G#o zp7%>!9R13JQcAtqFjU~f4nF5J;$@YHZ?Af)IY^)HZD%D{t?NVmE2RV`hXi?J9Mk2kG)f)rl!8n2GI~XYzvmNh z>&RP0BJZ-_>kEGL1W07YxsGOJ-n@Gt>BC#|IfeKR8b;RQ+BSHr;-;wbxxH zWeb!^S=*aHkJ%$crk%^)*gc8$rLd$GJs+ZDSw0qA{&6h-V0WhBkL=iHS@7q#fd6+0 z|L8vKM|?AhCAhU9j||Bc`j~qL+IGUA0qoQ-}bV<2Lk7r|Uy5W^=zcLA6^6T+y0a$T$zes z7dl1b6kfeLYR5%RLZX=WLnZ_)OnjOPtz#oT6W}hOA7JRlJ1)hp*opzv`m$e(&-zOW zJo79$fT$qRC6oXoK{+ANv@)|bs_?!NsE_W=^Nw3Dle6LVOVBw+8;&kUf)OJ`sY4mT zr$7S?1Kb7G*vAoh7S){fvvhf}3G%_BFZ|#!e2^G|jODrs*HVTY>=5_?;}Y;^S-1o< z@snXC2wh+lJ{qeTb3(Ur`1FnLZ854C60D2{wFt*T!c7HX2a*!D5M6es>4Mt#*zif@SnoVMuhw9zZ&$)P=m$(DdyS(>)Of2Z6&T#F@VF?z1?lhgF?R2^edz% zU?skYTCmt|d>o43@*Mfs0aL(YxyJ0>v_HyCM-ZfqE`_&sa9u_thYw=ENc@fI$47vT)5 zoGJ0uewenH$GX0dt6<2;8-$uL@!I73Js+jPXhlLil{|rpUxP+nX*G&eDu}=X&Wm%^ z3)MfeJ2LV{;V)F^R(k4`WlJx7*|Gk<*JY2tCuns49H_Kp*cEPj{8f>(J^mH?C&+|Z zkP0h%eE5|I?(75Z%x6!e3!a^+8U;eteyB>l;zT5Qoc%(yr!x44kZ~hU5^W~>a*H5< zVikF2XN-wZ^=^U22!_#jbDF#ko1Fl-9Gi%PQBBkuQi*XBmf)tA9} zUkm7R!mZ59CTve7^UPm^sXBU_>dVJtJM%>{fcY%8i45S=2}d9UxP_k@d2b=_wl{;pXWz$=o|-rG|K@R<5`a;6O6bBu!)xAU1>_;_SCmNSu++sK>Q z4kPary}6y^CoB)c{S-2NxeVYg@L&^YxkKoB@{cbfbjtpR!lu~cb zxz3RR{N8EAODYlHmN(nnbDW$pD<_&;!$gq*+>b=U{!Y0%GJvOUQ{#UbHhGo|pf_?T zdYXa%@EHypwI3DRb}Y2(u!CEAWhV};a3@!gACTTlc7n7n9(Ju(Uk_4;V>w%}$BUe1 zuTP5KzLmSDOBT?MYMZcw7H^WVb6Tn_99nk-6d{&B!Bgkc?Y4x!3&pZ!ZgU7EDi|Nz z&TPC1ewo%J=HuCCNhn|h+F9eiaV8S6m|tM>W%TowC;)z~B0b*#zu=hug&#-`$l?dw zYWm3c6VKyw=5Y4!f!GI;uBB^jwR`xYtnV>#=rCerKW=$GN6@NG-bW6h%ETu8!;?>c zk2@K($UCkEN&SZS{{&q=&WpMVg_}|-r3*@NCD+M;#5dS8Td5z0KSarTEV2fS;l)0% zcGw4opGwf|CbPFuEAKiiM;ULt)r`lK?B(u!_&6)~ROEKQ@W#$UW_x_u6LwM(lS3xL z?5PocaC(H~kZ7yaLY#)G_{h~V0Ki-QQ!t12fQCzDFkAkR8%QYc{0+303ZH%$ODWr? zKzyV+r_O2dns^hXGGg?JXrE8osQmM~RhR#x&ljEp`kisU@N>ZaKco+S1N79Y>uB}C zmcPHRtZ#hghX0-VMh!&$DD{nPU7!4)zVRD7@A+noBXOkqMn@is^%0)>b4x&3vEukk zL|#t+)a~fMza5i#1Ny7pi_pA0KB}ZMlRMk*JK66=a?V$p{i-K#%JoqtH5QMWqCLW&J2H-bzqzhGBap1u)?YrhiR+DV*Qi2qefM7=Cyy@p|Smc05p zkJ`Qs`v4--O317Ci@BUzUcFP~CtoD59vyY0rYn@Z`VLE8E$4i_)#QUAwn%7ngZ1$> zA{?~@)bRVdlYU=Y_H?A0BBR#%iQYu2Nl81Unm=?4fp$i|qxiS4ET4Yk;S7l;xRtnvi%Jp-sKg;k6QlR+UkWRf0of=>ti7@ZlX=VW7E-kd+A|D zYZ7-X*`wvp#;>ZaQ^0NN@teU_1@(8}=ol0b+ml~ca%+Qa4TRE`Tay9TaSIdx7Q_Z3 zH>skn1c0ANoSeq{d9kbpL~gSw&2*Jf1v_%*G%oiM7cFCJ;t z?Sb4;h4B%sx;>QdjR(+^w{r5~YwJxmRgXVf#wrRXWm~dQxxwV!q0y`>% zaQeiY1!H=|?dral-G!}$}+8uH{L+_>2(lym(a@Z1O=TW)*%d3P`{+a zh*=|8lkWQ`=AkZ_J6K3%JRpfgafF@=+J9WEP$eIPP2R%;Ftdrr=5^-i00E>zgTgUG zwI8p1Zvy*LGx%?Zx3(AK(RgF$;{~-a`ufzsq8)y3ZABp@&vqWanF284kxSe~8 z)5-iEJ5k?LrWCHlNaM3iDO_br;VNCQTmlD`L!%lLMorrS3=g4+28o||9rD@uWY&x0 zWw{It2j44{wOmP{t|_soCc9)~Z;=-Bsi#<#as#PNE+HqI5`(;74#so;*=i zJHjgewJYI>8c4)KyQV+n9FBDQfS z-b8l%>O)5$J3f=28hKIV{nlj1zdM%`MA?t!h&@=|_NSVa9sgW%+A?7-paa#nlQU(< zHT_wlozMI`ACF9ST#2k?;fvW0BX8|6fdlpyZhsupSsn)YCS>|@+3^L)?Z}Rct@5RX zM&8I~%O7Nwe*jlUc09$GEjwO;IVJ@kVc(ude)i4J;*Go%r%@BR^%|MD>&>yqwBRjqcUvc~ofcLdz$HyW^dOw%R-+x_y9LL{_{(cd@Kg;$s!T0*e5#amx z`KgijE9BL_Eql>_oQ*myd@q88R*UDQe$_0#KPowGJSM*1s=l3^iSLb6C8qKH4L%+j zzE4C}PMOYZhmn^gsGH!#T$YFN{uwfTIllKmZU^5xTjfg&jXa~-^6#+9KaStElAi-YgIrIdPe zQg=sJRN*utsuJ;S$7Y**08$gx+#9W&XzmmIT=3nGMB;mQxjOj%GkirD9f$3Jh3}^! z$Kmk(TeA;rjnxC2vaek|7joC4OE&v`kMBE2xM&AXI6f(ec)S^ z+8w=p;IA-G(a!F$4+Jkic#2;~SY)_e@l~Z*ptpJtSXX4&h-g$bI6oX8J;i(ok}bc! zPvj@s47^9vaw_pvjSBc&>Gq-@SX3Wafep)85WSH85#%oBaoDBOXMGj)Oq8@J>&?Al zy}2)Ky@^sq($d6w)1|NbiuQibV*O>WF*^uZ0U0u6lo@dWo z4}l*N2J17J(1zke+6Ah2huiuP!AlRUfQ8NqF?fPaWb!!sj?u0!#8y*zS*9ES5i~ZN zgMP^!R7;7PTejC*O`zg*9Mr0?5B5Ztfq1B+aA#!cBi-lWlstHGrhL?OJ}y+YFY*!s zwwCSH2Q`L&TsmLQ+l@f^Yf!loFgGp#$mPu_IPy}mpgz!u*29)nFJA;8G}>J^FZF}? zqywUDH_t`_jHZqYG~#SE_Q@gRtf`*zvd_KM4C5>EZ;GZN0Kb&dqhd>XwB6i_%@b3) zI%U6WrF6HXN!!f^ra)>WqU(0HKGTk{hKMh_l4lH^h)A^=@Cq5XBCuC(3DP5B$EydHhJkf9pq^yYpFviEIkbItmYtFka+=$b_k$ zWD8ilMG<>7?S%XA^tzLZq88F4GsLX>WwB`1BU9x7B-hZXpq8Qdb3CzWr*Q^8jpVm@ zvfLa?z(Fikj6pO<$irM;eLw^mxLOgG^KQFKEpXV!WVA{==;na zB_I}WVh}7r47SE9GBCEk0W8kjDTWDnYa=3ti5%;swsTv*+T%}@O51ry&eY;QPY*F~ z|4^B?%R~4gX0oi?8{F2zwzVa~AK!>I-KOwLM{rL=NWUX((>@nH4#n@N!31;c3mA`Y zRJZhcg=iNu7{^Y7_|d&!J&bR^z@Yt_OyP4!2)C>81@=@+Td}*v{^Tp}hBkn+sW-0XaQsWG!O3^A63_SN^p zzIqZKv+S#9Aj!P*b@bya*jEQ=U#%vWDHm{FnKsre9u(1SV?Bj67Q&7Uh>+Ns`=vJ4 z%fV7A(_h+HS5jv{H&5smN}!E33lx*tjLOWv{=l)&^^RLda{G$Q#`?1a@9tr_nb0*gPk)CN{O-Z?X&CwbrafHn)j2GdU*K1i5WuO|;1{v1U$ln4n*b_qK_3D3hM0?W^};-iduR zG{`W+8n%gNVsPV)wZSmg$5;chfXq*4d5OMAg(jf}_ivKjGDns%7K>!Fx&Rw%wh;nS zei6RQv9Y%4-8W=kJ?$N~g8YzUUp<{EvO=1pL9l_2pvSs&0@lpoJ3v>OH?fY{Cf1|A z(8T%{mx(nycQ_O4owvJ9tZf)VtBvUwu}rM_JsPzhS=p$xiM91&o_c})iGB4XWEcBt zmL~Sq%xBwI6O|rl*1r16O#5muRq%Ii9d2-$SSQ6v!r@J#AWt<*`XeURZ(nXP(Y7j!D}jqeGJ9i16%%W^ZE4oT`aQe=>b6KM zS2wwd1-NmWSig>E?(-2}35`%D*1R(Zpmy$~>n@e!KWTcBy+7!r51M9g;a(Q6-{8zF z&B7kD1QR|K|2v&g*lA=STO;|coSwjlPjQERy5hyQ%EKH-tHc|d8(Z-sn;Xk5p?!(4 zV}9UQ6MhWS2D_>(er$rpj{$~}MMhv6_?m_^cLT*_M*uQV?P2Ae8{ zj?_nixv`kBQ6=EUQXX<+R&u-GO#2ia<PV*XGCADjcfBc{d-9B+(vV7D)nbEnW;6 zaNaGO7h7bV#(Vq6X+Fm0#Wb`Sds5-YP?WOS;2FbA`i1qq)Z)j&Fik}G_tyb=VQyk+ zrVRVk;bWU&$k=XL0}RY$$i8FKC$&GdxiRL>K92@&%i(-`XrccJ>(I$B@(F$9Udt68qqsiBl^f zlFZdpzZySgopUmC!7c7dwLUZhe4+X*sv5u(kUcblPFo#w^Z5Bm@>S2fUGY`Uy9u%)_KiIw=5KJr#&^J61i{MdcSB1hgH#|>ys%ft`e{1^&796xp&YLfF}a^#J~ zjgSOCgBj@XW2#XDtVU7xp^m)C=#`r(}JpP;ePEyPma|kom69XccfX7elQ-S%URCh*;wKpSsH3!I078 z${V@zwIW9XOPHMUcqBhrj`I{F5v>JnL6CzKM6j0F(2-Y?`#f@a;ir+8aPnSG;it~W zkkkh-DY?Ur@Lmjy)PhdI^A>Z!gJOUdj1O)nq{=ddx|+(9szWRArFUFaE_tCB8N&-C zY?__Vya1V8WpSqH&vrsCQy;l4MFBeg>H{yR%M^8)tS&Tr!MjPkpwKCnTz8AP8Y75= zNlrpN5{$qU_+2|xs7L0UowI`s`V<6ztKD08?hd_nX`-MOT%p;bph2&#P2{SleDzdR zPf4?xNRc+#Q&B&X2y*d&R@2kw`-U909EZ^eUQ@f=wwL~91$tZt8Us9VY)MvFp zNA%GRRqsoM{#XqJqVDEw(Deacmr_ zpH5pX_;83{dvXgo`MZ3x2k~M3766%{PGYl&4;x&9%I-C@v_&&o899xvjJzsMuf@Z}rTjP$IuDxN&PcKId@ z*Io!1;y0yglX?Dlg(rxhoD}Je1(3?4-DCckL&V4(INDoh z&X-iGFktj-p87rJ*^arur{nO1N_LRY~L z{L;$z&+xu79~&pX9vQC>ehgkBCVHbU0!DAOE6&X^2cWk>eJ(y{+b9$d#z*JBnZtL_ zb;98-eR@9befqP7xCHb+72$&1L>U9h63bt<~hSbVPd0@7yW`@X@qqLgJLE+G)l+pL>gZ^X%VCuab{Cz{q>y%3%u2*pc0G9 z!HZ=zCHEFND_vWT$sK>O0$(5|8*jc`C}bDni2aFfy5WOe3KI z@^Eke8Rh~urh1Ge8G)I~%Ll?L>ajanw!*d{Fb?s)5@)3XN;z$w3C&bpw`bW6@5Ah$ z>I`7E_UZ!A`(3b<;~a$teb8TMk1)(EI;bBLjE}L<;2Cr(pub5p2PCIr}Mpe3KU2oy|Wuh z;sfwP@FdQ75kwV&lYsO-T!8-o^uU-Obo+kir#p)9aFFWD>k2 zz>f7>P4UwKJ$s6Kd5_(66rX3nJCmP?ZoR>$;{Xce_=Beg^@Ab(9-t=-N|cwFC4blk z2h%)Fd4enTHKZE(dinF^@mbWqi5v%&k%|uovX;lEJIX&vhAooduv6}3_rhxb9rV;Y z?mSE#MjtWq&KzJ*n!uzqzXUyLTHhBms`FT5$ot#|Z?qN#yw$E6yNQcw0;ksn^v%B7 zZ7qFOn=7Fl4C!Y7`1k&VOkg%XtOS>fc$UH1Ev;)+I+JN6W9o>=%xEV%tzoA zE9jp$wg(g}QHnDFSQI9O^wA)j4lI**L$VQ==zA-tGABolLa3l9L%rJ}3K;75-j`?o zH6PUZzScW@Sed#Ba~1KJR_c&n2*V_r%JZ|(c|EgkAzft)Lt^K%xG ziyK6*uV}>03Z$%<%>wuFA4@^LUCVLZf`nZgk#AQq^6+LAZgw5O%^s%UONN`5yc?S2 zZDig$Ot)K17NnML$by%Hl#U(Hvyd|WcHGZ8l4M>A? z75jNWWJ+FMPSxm%>+pSAv0QOw>3ry^)A#^+YtVQ?21bkSPety6xufksZIg1H=B=hn z7UmKKa}X98IAHC>f64gX8{;P4RmOU|j3-@XBvv95*k5{KYt;5MYFK7F&W2#o3M!EI zj}STE?nRy@um zHz2N}(6oHdDyR}v<6=Jn1(vh$U(}~}`>{`#6nPdWPixK}68Ue>N~IGG|5X3PXk(~O zQe%Nm4!_Q&E?k9n9<(~Rqyo;YR=MT5ha$hr?%(is?AtV}Z*a@B89KuRSNu2dOTET_ zFlQK#b0Y}WP!rwffiFFy6&x@?LJmRv_+yphMgzW~@hwfDh{^ z1YCm1BGMyQK7Fk^e zW`N&HYrGdQj_*7fu;rVzjyod=*x<+yYec-V8O% z$~nt?qK4_M8Rg?O!-HcXAZ9S=TWHJlaehf8`Xw5CFG&W7G?w7p$Vn`)ttt>8G=GkI zxzRH*&e`WZqFcKv`yw8c=HU~{5%!pUo)&?8*y~EW7-_-yGbPk{*}5&Rt)F4A%i}MU zWb3^Cjda)N)9!Fs5Q;1z?u6!M1>@JO&fqt4MW^O{Zned4+=`Jl>%N0iZ12{7lvlNJ zZz|PFU$YcGAxHT2ZPCpmd@Y|SA&1~=`B(|3m#^it63#DcpW@;YqZ{`|-`^YEyq6rf zR&epY)buG58?X0UvVQ>~}pnm@iLEZlzPGQ`iRx@pEPj;_n3e2}X1Md#+*A&6qT$a1`p$7wx!8o0yBF4&E@oh-*xi6(4u9F*mN zf%w?sIOhF9aU6@)we3oE?XXT=&)zIo@;_K(KO=`Ra}EKGr|npl-x0hjOFcd4S#EN&wF(CiikB|$AWCre=i!0Z6S?IBZSG~3 z4xcOu^UZ^~SmUS48t=rBuIS1&9vl?mq1JZ+hOF;HxHi*4=Vtu#R=@qe=84MR>myuY5fopCRw4CYWt?;{6c1$yG^^N$P!5J{8`E7cA zU!>M|P-ySCOOPpReSgowq=gmNG+E!zajoys{fMW&i$!yBUnE~~asROC;x4|`T#Gx~ z7yBuQ)?pWSyITi)9d>cI3bht@6q#P!(L*lo=wXw^{d22#%`Wb)@kzno;uY57ZuJd| zd!pIJeTr*wzf2bQgXJKp1B=S_eGuBXmiSZgHUwHc5bk!@00-Ukf$NH@$zJa>_1{?2 zCWkL(CYe;nK%tM6ESp3D3TzVRVwz*s5vz09B4W+Ir$*BwSntKDgSYx23_K|5410dO zbt^{F{N<^#zEg$#?1_{IZLi&)SG9%26NdFdJ+F+n`50Mqq8mrFoIwKGay-dr%ZZA3 z(yv5LeJ>F)&+AnPmbIcNxeBn=WU-fZhsg0yMYUyT-$@CwGm? z5%`ZS_od-ZnA`A#tQ=t7n~ITzlNc3f>C|3V+ki|~-}S*!iYcu=eTh+{KWdrrIb zY7cq9QLpyrr~IH~JqP zy(&cVFz@9ZQCx_mRPGN`ldhHSc~HpN z7U&SYNR?r}N6FB4HlQkHdFDa){p0uM9>mTMmVCpzXz}TMt7IqEmzP^#CQlK|cGu^# zfi!_q$YOYM{1u>>YUnZL`X0aj;!G6et^O;LAtz-UWXLGOSz<1_nE()gF|(mEKRgPV zT@r$!zftS;y}VLF5K##oMq3GOt0e&v0+)QCWDZyEYv&XpkxTCX^MZjCkPQq%wqRgE z>3k!`K`5II82j|O;bMIiX0)&lk5i-$=3ZZtJT^UC@N6(u21@1(Bxip8BO@-RU(ktG$cg9I8+_hCO_r(rA25lfU7~eeKs9XD9HOXe z4y0)FCt{oPu>)9*i>gLzG z1L41YUOiHo#i!2})I*n=L%owZB{ETuzyazBj0ETo>LJN2gAEo?@8?84Jcl1}qn^#5 ze*^f3vYX-`uq-0@$Hoc%^(96D|9~Qg#Xn@W@NW`?%EUjUm__^&_*adzFTy`Le|az% zk5TRrbOI}sLgeYl3l~5P^^THD?2eqJLAk9&gu^~~Bd5{{*Kh%ISpuV_A}HJ`K}dH( zO1R1jJvzQCneO%{dlo`mR09|C*d;a2pyUP)Ls68sdMc)+B!uD-h^zdh4Nws8fdreb zI^{s1`D89w3eN^{EJ``saBjtq(bw=`-%2#&GfVEWcvvXYsc3uJ6I=4b;Xapq4K6IU zWQq7N$_aQy>$2i9Y4h=(yY;S@a~sNgJkv9rUH^C4!rmd6ql$q`c84 zB{3|Aq#}Zj>G8Md$&H{Pq$h9|i_xD?f_LB9ITO{0b7{MvB_A;A$Ej(|Jvo zhb*<9LP6F*c_SVc09H$7w4pLO0Dv)N?b`$+#aA3PE3slJhmmg=BNq~PaVlUScX7rD z^a0n3r5ukXxcP+Rv5+YoiUw$vAijc3DE1I0KfZ*rOa=hgF*V;BeVq3FX6WNn)~2+9 zR2{hJ4r-D$9@+Q^0S56gmBz={e<1_WdN!}zo6J`U9hO=NZC6XecF}JD5D@Vx{pIZn zAFmaB#GwAayg|vp$LtLXj?8Xo+bm9kOZevSaZ2Yez{lxW9uAL>^K;Vc`{SQO!~boq z@4GH)X8uw?_|?~U&fkw`_`uJ}deZkXHGi*G*S0@Y*ABO->)Ai$)zJrG?utBoluX)h zR~X)-3T<}i3B=%Oad_>90?r?xvx`dvO?T!G5gqir1L?W=+Y|vh?!tcb0wd( znM3K-Zpi00Li3%DhhsB`uU?Qjc?5hvbS4iTvl)?0|7P?DTsl9k?H720->zPv`0X0T zM@#eDr8Ty4eohVs0@*Sc?%Bd{J8F5D7@dOlK~mKI6wkuanq)~j1A3aidiC+2Z z&ZqkG+)(?f^QjI3E*nJLzcmFB8%q%V_spjk&virQ>(8gE1DH>sXA0{$k*;cjb@oJ> znK6+f(Prq`SDa7qw+wzv_N$Z+qz_m&-LK00+MP+SGU7R^T`RIVSeX_O*t^!BbUMtg z6}&u*_zset^j0&fhzLEoYvq3RvAwx)O6&G+fz3&Cdr>v777QDdxfgBD9s?&TWk)Lg z%Cg6B?MSDe({xA5MoMTshlOamA$90U2L1xx+>d@+@|eBfmE|i_9)m^362?RUbp)rA zJ(Sa4pOVL%x<{?Q8T+THi1A65NA^!I^8x#(H`KL9ow}aAkk<@;$J##uY{>sVWdG#< zw)oHR*_kYaOrEc?f6{^o@Tu=*i1TsO2*G9%w$y&(yg>K*!JhXZ<`MmNoEO zlnM_I#Wh$aA}lCuCn#_8m;J1GRMFT@{<0w@qw>cV`^!dl8hx5(+=T3FBkw2M%W{|= zKh5Zda$xflnVE#o;)S8Ih2b;(-B3t-?*v-;`MaT@BJYIr<$h!*Z@eS#^uRQP)ANp7 z1Wd(A6-^&B8FH#AV&lz&AGd-4ikWxbZD%Rdu?$o1e`5YYzzH*NW$pcEW$pbY?Z1pZ z?ReaQGywh2w)duXxAueLU;*Tcci^zaXm}uxG?-U&QOY#pF0WT79p=(}J8jHiptZ)J z$uNYP0`^5qvo8s46YPi)4^WC-7V4G8GGs;&v29E`t7 z8}K(%7y{98nxpYWY0X5VA1u5=Fs2h5=}O2IKeoFHEiaF#h4hNfi8v$(G9R<)Bj)!K zc#`+gYu$cEWb}?Z6HHgY7=+*8+$-a`v@6d!J7*LP1>f`4Zfyax!3{7U@D%iJKUt_O~M>&hO@n`|WWTVA^sV{NiRV zr0+}6))&@MA$<_+AO_da4gzB`2XnbmeKH6cLV7t(wkIn?M;%KQ42fVS+1S^p-ar`9 zR0v=`tJnr}E5tLhtj`zh!h559M)dtkoIZ$ds5Aymi*Brh;U|qWrf4x}8z+dhnLbV= zyZTc!mk6I0jK4*Jlm&kP$UG=T@w-8dhX37sC{7}ChQcn_zr;Qd0me3hqd*w-g1vq9 zYJm$aRdGO0>!?!D-jEKEK(-98_RwO``j8;0Dp1c?bx-iR5M8)tP z7ij2PQ2jpN!`2W_G*HHiz8zJfe6$9R7u>j7XVZu5`PC_?&zF!OpVR01xVbX7P7WAj za{YQ^bi;_+4SCA`bOh{AJ-&ju0pk`J7ObsJwv2A_`U{dc<8@3wW9C-wmBl!LlWL*%-Ep5imtTb2w{&ocniV2#~c8hvC9hSd)cv$_5E~J zct7K@tsG;4)IMX-ifX8S{SEzeD;bb5NdDf-Ptaf*Lr}xIcvOpE#eDKszls`mPTWhBRiWY%58dB6NUU-VX z;C&2hgK?tXW_+Mvi=4CbkKd;)-(ZxlsK$vWI%4ucP1CQyzp^NCuQZ6x*c2p`mC)Uw zkU)-phH4NHW2$neUXB+S(y35>!%av{sAGLlZ-tO;Vh!Y**QXHY_k#LD%Hw^6seZ{s zn5g5_F-9-)alkm)XY|hv7!T%ad)8~G?$#H;^fG|Zx-h@=>#JZ!giw{mTETwc)r?R< zeQ-}>c}JK-ZfsTiKENI1{lEYs*4twJpgI_W{eztpt!)#xFU+U$EN-I*r_nfu)g{J?(ve?WXcUwdJhzC6C)_qUQs{T~_M@84@d&AxVgzo$^j5ytns8(CdZ z^u92@Ur*#c^7wuyBKJ|p_j~`n+#FEwqloW!$46wY{&&as>vJk+ew%#H&g26SJ9RcE z1NQ%+_*&~1B&L!!Uz!CWGq zJrSbUd-3}zenauQO~?6s5Z?7%j5@A|Fad|jw!_w-!=0xHVe96>am;-E23S=MEJ~K* zlZy`fNf*~A{}Tp|VfC)t>VU#@w7lB{Es|3fu~Z zD~w*nKBKUttijvA!KW9N7`;k-h*g+tCE(8J)rl$jPD(z$@q0|8Uy0w7Fs|tYZ$98n zOA5>8dCTX4U4sKVIFBwu{&Ex=IVaN9f=A zQ*Z3_W;6#bdX@n%$RE4vgPzC64T>qBfrCsCr}(;^JgS$^z-cCV=$|{Pi_fSop`^vv zt#|&YbE;+&oW8Q_3P+t@Rjcm%Bj{G1y6ugiTV1gK4#ulXxJiSpg{yw}*MvFK3fc0( z-YG-hrOw+bomaSw*^0Qyv$Z;w{8)R__WP$f2U5FqI8o%X-C_T<1cDfB+OhK$UwziO z#9r8e(BnMLQJ5i&Io!?HCR$d#&y(YR{ZQ3riVQFzq8rbs-Pp=+RLWU#V?2RyWxTt` z7jOShzcGE5|`$oG}i`P5_nm2tmktS=t= zC;nY~;#i;YAm>A9hfjYH)1fzKL#P(tVKVgQTnNp^LmT%=L(cLhCk++&Xl0cp**ly@hn%ZvFzFIm;!q?>;@S{z)}h<2VAuH;3-Dnc_8*)haPZObN2?3%Zc@% zxb@m^_5=UvRv7REKojFNrG+I}n?wk3_ zHJ%hSWZEO9<3R{!BEu1lWc2vMVKb#hapk<$R)bdbtv)6Bo zr|b*JDYCD4`=!2k_k3a{Qh}8?cz%q9i3%$fhBm=UU);a66|gcN|E}HH>gcc%q|ZC< zF_}JD=YLm-bcWMsaHhVgql^A!o(B>rXW2hCi=91MH^ojuFSf|RPCN>BViq;A=T(Q5 zGRM=gFIAKlpB>S7zFes0v9dJH#7r4aYUJIRnZ2H^H#Hz*Mtqh1=6c8v-NvPc0Qj&+ zu5Qi*aB-Sn&x9{J)x#(;r-oUw7!;;o*HBC3~ptAoTr*l1(fgdPJfZeIiZD>eDgJ0DMm zk9~vxd^7pqp|^g0fnVQ&qj3TKQy+w)zUU@sA1G>T1>czcR|m>A1-$(?1>u&ynlPG$y)$psGZ|EOa`Cf!8i`81kM$I{i3kISVr@sN7*6$T4 z@OT2AZ6UxMN3=r)`)okP+M+_gXE}tDH9pTG2vZw?^0 zOtF9QaTFW`O=ej zH;)~nS011DgzA(^IpMJsQy?13?fHsF1IGrejx6d~y*qNM1&`DM%_!C4K@1S2Dn0Wf z+tRhrmz>imsrV_Cg&KC;0Gcg0+R;?c$om{>?ikbj zX|v<4N~MD=nv1$))joJC(az92?T$9M25XMEoSnsR*mBFR0z*Fg7yU2!T{-wL&Y zuwq}Q_sJLKlhTykp@J~~g?MfpuNiQehLBqN{rDAW##Kd>=HUvjC<75Pc^RV^UkP&M zyeP&Ckix4v;R?~DlOc!~T5(Wsm6(W%l|rn$5KCy_j@ROke~uPuWsSqzK}xFbmkuvf zLm}Q)83-c(?#ABawmCD2u*^fs4lvRODpEkEa&kICwmms~@AqAIy#HfE*Q?u|7q(LOa$6qSW4KYoB7uS8+uo z;NH-b_34Z&v}Jv|;@T6}p14A5)<**%;exKu4R{8G`wUcMSCB80z7FzTdyTS3gAYTj zaz@@CJPw+fTN()Uard{t)TtjgB9~*tso3BNd1viNp4zXzdd~>F&oVoO4M55}Uy6|O(?(5WEy zwhc+loInG!1`PnReq3j(16H>-B#$3Dv0uG^OJ~)qMg5GMw{-T2C4+zb`@!X#%VQr% z|Im>gI6lt#jLwY3Mz~IO=A2Oc?z$p3y+D8HThx5>9RhSTK!_fF6in5Zm+cugI~e`E z)r0McAJ5}Go0b@f*1G5os{H@hdl&Gis;dDzkpzN*Cn_RV)S#&at(H_#rg+K744#1* zKoL<3YUT6NR;$7cU=0vVMsqxzLR)ERE0tbUY_(D=h!->g%mh>jVgG{3W9Re zlK)+MpEH*btJd~={_pw!JkR8uv+sNDwbovH?X}mI;COG6a%Z`8Op+%^t~!>UBO{PH zuP@RGetNDci`bW!#he?;Gv}-E_T`bT0$97cj7tH$8U1h_|E2U_8Sz&<8Gl&#B3?&_ zQs*g$4>9Y8GTxP7BDNInSc$Z-XxQccaOL|DxxyrAV#lE0miY$B{h~j5L2uXhC#!Ux zG1=vf$!ZOM5O#!p-LC#$1i_9}2NCXNz0aD#HoFWq9e3zri$xzcpe5)R&8jTj?#s zkZ47X?SAk#rZ(rkfeci{zT4yhAgU*niSsIJXC|&-&s#n{BR)ft@lmGTITCng^00^# z-WN@}@h%a1kU42KUNVK9fB`MOt$|s$i3?fYHTx}#I+Yh-&DJT_>3`P1!N*pXlO0Ke zO7F{*R=p~YJGpKqbD(;qD)i|xMhyy2Zef7M@f?>M5}J5B()NthhK)rbzlb-PI6y@h zdJI2B`^;oh{6N(>WSD9&m@6t*O8Zu7S0C%%FzepHH*~Yv?Kan^7G_L9kjQ?qje_{LbG2r*<>lFrkrd-g2_2bmWcEZ63 z`$;VgJSjX-Nds@eVl^i>LYn||PJS5XfDcFqC?iBVsDyc^+Tnv~b#iZ5UBJh|hqnbE z_H_|l*eSSxwOLZ|Rm^eDBX#;pK@7QKC{?6*aiIZ^FMqV(zd?Ewb5rres1!hKYM50` zrWIQlwZD=6Egb?o3h)(sD!W4AO)Rh_(WUCubQwb(wKhkqu(lYz&`hq1Uj$Fq^b$>O zAK{#KdVCjBRHP5rk$%__c}Du-!h1Arh^{Id=Jc4v%US@mr&GLJ)vF4S>3iuEb=}P) zzdhm%`}oAdW;mbN26o7rw!@l$4DG(I1y6UqFJLx#4GA>FKi)9)+ubQ~IzVGWw+ty6Wq3QEpY&W_k&`rOIap`p*(W*SX11O>4Dr(4!X((L#VW~zz#42&Gzvd!#s zV8&Yy6a*Vh2jbia)f1iNb|C-x7oN9c{lNT+Azq@@Obt=uU&L7YTyUunSa99j!8V*F zT)!mN=k0$zE!uHxX2H9N6M7v0V=y`;)CS3fIsYZ) znL`TsMZ|iY?R2Sm@*gvYYb5I>-BJ^8^W<5XQVPdkxE^at6LcxglotdTP5TWy1NM^W z_woVY4dhep!~3Z5y^rAR_$y;wSXA zKk<&>s=7RGL&Ula`S8{F8Qa<-!%KdX8qv@7z>iRiWDLN3d zT?kR`P*zJ<76($_EQVj!6#r}%X|I+RX?^WQqj0${h6&a&k<<`YHbhW+Uu&lI4=Lu+=?JLapWQsXSwty~-3bP&sqm z#_h&KRDW2RGuG8ez`J}Gt`etwxOUTVVvDRVWjm7&NOi-Xd>=ebxkGqrekAkH{`cYh z0<6l#`}g6npzAWO?TWJY>8l#Y71(39{|=4`UKEt||42^<ds(FF|s0_dr}giiK!@qR%0> zL<{aa&`oNvS}yuJY||QCwjI4@=k3NJgDYTbeWT9b2Er+ys^*`2^fNNr6Z@0gTRlA? zvKnbbR?}(rtdKN+n?ksms*`A8J*-a!C1)<6G5e{V<#xU0^e1Ld?)%jS(k{I%*MC{$ zh#DT&A`6`>eR^18-#fwkbcVQMCrZ??L zkZfmJU#jDklG57`<9@CFrUkbdGSGxA$g&@0t1w5oVoUvHwUC8ttZ+?Q$(i{cm2zWGG0SNC!pgEtCv)w)#^V0f4LfSzES9^rZd73( zn1Ss^Qsl1sm6DWeQ!}NAsFlU_uS%-wa&opc5_yPp+XOhY_3P=|ggu}EKI-b(#^N$# z@fm^DM)D>JY)i?(skT*#cTkta?CG%fw)lbDf^FxOseeokEYvR>7_+7g6goOxbA5jj z`-Yw-U~o(m*(lBPbrjGfN=p57+?HV(c zypg6z0s1Z;X&B7D97e)=mdwc|O4pKfh=#40_@U%WPH}1T&UAZ3!=_F zjZ$qYxq*jjVJg|0du_?RN;=(wO}vThXsEu;r*8{YPA5DcJ|F|R>o?TNr?8ky7*x|K zeT0oE(@~v#ya&mZ^YmtSol2R!f+9(b?#8`e=dC5N7iwCo-6K^gemb{AKJENodp#yF zzkBFCUvAXw>1LIs^t87X91veMz{Q7qvwN^a!u=oNnf`_%fmHfXo~n<@YH407Ino0G zqo{6dbndA}?FDdu$x$(H6bDL(9z{u`MsbUKf|Q?1_V)@%B?s~*e`Fr|ZZ@-Sx|3Ii zH?FEWI`bDgLl#4_sChcMKo6!ggiDlarOH%tE)O*%spRb3YeVi;(n*3;az1al!E9tO zTUAbn<)zHZ&F+c6rcOR(FlRmu+ZV;OXcnO&G>fLw{rytu!JKWXDeD<7(^}^CQ`t(l zsyL^NYO+ZH`Ad@IYYerPv?>^m4JtdnRq>ED=D{cida~kXi3IAi{{eCKyoH*bc28C6S=jI1;6o16P6Jfe)i#mr}FX ze7axo7ewjKUfuUf@pkF=I;?w;SNCQqe5h*mE;lY82T3dH|EgQTy+a*l|8%LJ@=`A) z{g^{`faI>>>Au9jrz_}``#5hRGdZQ^-Y#H5td>rLI@q?lEx%N4d|vpRCt2F1sf~Qu z-Q9oi$flf2DQQF0c~X>IqmgoZ(~9)>31b?S0cn;2Y4)*k-;;Vk&;o3AKjcFO=BH!< zDACQ?0a4_n>HGwVq$AHsxr3o^spRH`2tjtT9NfZma*^O+aA$LIA|6@$L1vPq7()sV zg5M^mhTyq6kh(T)%GRZrxUTDQX`O;~FE1q(kCKZ2;8k2C6;u4~hxBtBj<6Ah?e zndk~lbfrxh$j#}+OUx-@({YZX3d@h$uj62fDA!6soI4g=)RdctDF2jV8LvN(DE$p2AYhX|R6p}N zSwk{m37}-Qr`P6%`Q5!RFU;?AwS*m^^LqVrznM+|9!~&t=i@jRg+;Jq@PyUwmxWB&9%jkzG zoer~!k4)Zux8gmbusFzchIO!v9#+P}yqeW{b{YLdZW;a3B7rHl*0x_pKapETKdjO` z(QX;-FK9x)68N&*l9OXrjAfS=iV54qJBxyGvO5_+HTaZFmRUKt13k~gZgP?zwt{*^ zls~=llswt}JJTLPw}a>(Ftc;cFR$8(IfmY;tlB9mhhH(1S`p_ArnedYZ`ucb!hSs0HAd`7Pwemw%)Gakw1pQjmo+hQS(rz1B zoxfIj_K^)cEGSWtr^EvRbt1`5OOwHF51 zKNDqxGhqClE=6jYD3aYnj$+43h`*8hu=Yirt6F68s(l!2w+Ey-ry`#}RG+dk8TizJ zQluvG~FxjwL%f3d+{b;7} zlF9I=o$(*49Z7g-Ut@82!GhvAN8G{-+wU(Rh>5=D^t^iCdxv`eL`lZt(F>&715MeM zwApsVj;4L|SDCK3J$mWXw7}A}_1Y4o9TbEn>L*5xJ2z z%ZU7lryh|TE2#7)D#gj!Y!4W1?gJDEkLI4=C}$OmK18WawEHo^Y9mjz<^yrrC7k>U z-I|pzNNaYv!#ZvavfI=~cLFuj8pwRB`-ZAztGh`uw7)=3)+Ufs%B?0x=2CeZ_(zc_ zBw~SlUPHD_PzS&~Qf^O43GYq02WL~xmd~>!vpJq$?jmF!_w?`IElxH5vt9Gta< z(e`Qu_Asc`^h8?j8O3bUMlrelVrd)(NXi~!QJy^n#yVfh9>!~*-j>U^hlx2)X$z8j zMVta;vt`NY+YzO4o1{VJ&4(VmF7n_t9DQ4!{m|#ykNEaOe_SoHTA>NiA2Ww)kR9ZM zOyn1Vmz?&2JC=+4pf3QgMh{*~3gPud>_>e2p}h9xgY0+d>ttdr+>yKsP zG_6G!OEyd0=8bHB-+Gbl2@kg-<(=0H`6Ve3<}S8EYxqj;K6RtPmb+q920oiTVL zSZCDNp^agEcZHPaREw&!npfGH=SrEiQOhy;%=Cab>LojAW8}w$leI2t|F9xvWz zu_JRZ_A*z(AA3|rtvBL3!h!25ar8F*JK@0C%F3FdzF6|(cFhp4Ogm_RCw(ItYWGGfi{swDibEj-_}6$S}@L~S+1zTkbVv^K9l=n+l( z06m`>LO=E>}RF{PKeK z=!(bW;CAtWcHg z%QXW-_VDtMJ*S4~aA0js-;kVPvd7hi0~0H{-nqk?*kSJqD4l+w|^($XBNLw>r+mViZo4 zXOiss%!8N}9m_&?VDsQ=psz??cGwBW+ z=qn7axQQLisB@X6JZvzMmm`a$Zvsl8z$Z1mf&#)|U`Nft0QkUg;KQ2IV4!j89oC{V zfqKMFFy$^1JmsDyc@$xaW2-l>K+~Qh%)~wPXDE4uA5koa%r;tT6e&JrMAt(NGKjV+ zl-dY`LdMkw)*8dtTBGZ!m(y(@hO9&bwX5T7;hF75@W#&_!7Tmi@jF6~U%8CmLAmi` zDE-k(v2$jrp*u{5?%?T$3|*J$WioVFIKAmFT))7mMV!$Ck!fSq-tciZo1nEj^f$mo zb}BL#qK5akpoo=MFRgnLiI7F-nj(lb|4}|lA6={Y{h)Ab;!rllWKzi66mt(^$5o88 z*=C1RJ8*2RGQC~0BlbMcGjBbbm3P>moJ&#gYvkCEmk62Kv*b8bag+T^@k~&>tdlw= zB^wM)TPn=1tL?kx)Rf${W8bY0PC>59SK)JbpbOfGZkWMAg9F2M41&FCML4jz=A^J4 zhFV9rgafTL$KY2HgnPN>a2P^KD6qW-R|VtBIL+yVM3IZIDL0I>q}d4Xo9(WeCBpH` z2ns!z@M?LMFl!0Ocx4Y(&1INbHB>bQm4zjSqv-@lZ7nqguGTz-ik2fsZLK+aCT$K4 zN(vg*RN6_IlJTh!v)0y(z%0r(Y_*c-8;3LxngZ4 zz9Cmm>^amv6D&(Ks9yJGNs#_MiEKyO84R>QC`}<|8=A`N259C1J1Gqf22upzd2Z@L zS_j2^Y$YDCRm;#VOXrmn5i7RDRt?k>07qqqH(B8)x1Yx@T$BSDmYhMOwf$5B%@1Fb!Ip`GhuLRtmb+>DY#3(gR1m1ZwP^cb;JR660W7V$#H{Dh zg~hD&us*ngVgxdMa`IO}hF$CYBpeC~)-{*d;k)YG+Q^Jgi))dAl2e7dM6GcQ!Z^rf z9ON<%a+#;eWgO%(PdX>$5(;driG~9o*;DVTi3;z>Dxl`1AiVz=7%x}C8Z-a&DfBaA zh46?xc0*KpHZk&S@Se5abBg!8k*C6k_VTPtS(ej-Hkj6&Uh1)^Og+?0XLq&~J=zy_ zLmHda##$m|UsS^6A4K?=g||b#<@EB9GGo?ezOpQKI(=K(pOQG^XPaFBq)#P3^LwG8XYLqW-H?l33W;r$$ai-$A6(VuV?=C5-T`JL3zf_TrwMfV(Y~3RyBQA263hLy!$i0+2a(OR* z+4&IU8$1xBvqM5$&Yks{o6dqUG1s-{iBf>mB3jsNt}ZXSBR0FiJmP9Q_7i*;A8yC) zP$ng!4-9kH>WsGp7VsK@0K(hckag93+YyjjYi6vGX$5De-Q^j!o(;};R^F5m>1L*s zJz+6wc?-CO0%~N8q_}us-Y}A@c?nGGX3Ts>UWSw#GoMsZ8;qHMSOqj=jIcT1g z9I+EJ7IC4;6o@QE`q&#h+RxCu0>4(Q5)luTfji`u<`^t#T<@NKw`m z7nKLr7{k}VV}`ee1Iv*(m!s%bURzgrnCsj~TF5`H9?ldVu&f&O6|UD~Y%FexS^p4q zriF0r$#IIUxrf~jD3}S0L-ybv;Q;5>UT6Ixyu_H9f;+;}OnaW-t(wQ)tq3_66vN|Y z-th^Mu)lT2%n`i8e}!k}@n2D3ee!FO=t7?TuW!#P_UO_QYaC0)sT_J(&r(sJ`ia_S zL^&PFfdHfaLCRzC)`GvP^%2=kU>X`LmU~y-#?TUg;G+!5Uv{UmZHz7V*~nvZ2Pxwx zXZ~~++ApLKZq#&u)p}}(CIZ;Ykb&WbY1I*t;l?dtA5X1YSTC_9Dkds<`qFI${3&X4 zG%(N^^JmeQZZVSI6$l}fp)~26144Q`QlY;eq3kKe{#iERjyLU7MLYSX$~Tb}`!09A zg0i->90qACbLS59fBHNy$BzD)54r+;g>7+oIs z4Q3yA_)paUjTQ^Ltl?Z)&QY{T?z>*HHsC=e(&rIsk`bQyjLa0ZQ`d9#qS(jBp#l^u#ihX%1;*5aca9ZPbHor~< zolN_Y3O}dDh}a zmI;BD#M!EXOm8wGrHl7?FJ2-~@XHw+Ze-85w6#K(jo~Hi{i2Ki#fr?K9I(uE=U+}9 z2sLb~ERG-Id1H$?(~FH;#dZ;*uORONsZRh}6s&!lvnI@?4Sg3DXFB6W^nj{Hh9}>h z82DN!@2UGpFyE5inhgL1X0iqp1irTuLNUPN)A&S2#|1F6??Jd}$&P;{a(|DWLN9E1eN^1DR{uJ52W@*A&dK`GguE{AhUvKH=UjeHZ_AN*& zjo!k(vr4zo{fMWq&)j&nhrfxl2FPPRE^;8$k2XI%7toaj;Xl034>goQ_?qbRZYfy~CwQM*465CAfr)-E}iY^6ImdEjE&frE{s-oquTQo?&a{KZg2VYcwJXHxS~rnpYK>N*0ku z$~0{ZcAh+161cYtjXFzJ;#B>h28rj7*V#OndMGuiVmnQ7BhI7>)pK{4@j;t5*WE-Z zM!#gMel+UGLLMScVFkaC-+X?v#Uh@BBCqlgIh)5w{iv3DI8lgnDzA?nLfxkI&JOb) zuWjzvSOEL&bQVeZk5`k({S-h#PoC+pF0EWYr1ey>jb8KEq#t7D&R3&;EabtOw?8SV zTD}UZo~JsFYVs~>iP>jXW?^x$i+;&1ef51Y9;MkDfR4IjT{)oIVTS4XjCKYT-co0 zSU!^nD`k9?v+_>hR_8Ne)jJEVP9xZam`}v)7HP=hD$_N zG;DomLrVxf>vb|uI0UJ7y`J%@tTSkom+a^RKS1$0rct(4OPE2ZYw76gU8&Pk=10U|R>pOEN+%C|{4`=%C`>$sY8oFGf|Wkqjouiuo%MCQOA|ea zY01VUiJ?%b^lD*p@^8NUn=SupIjzw2Ph{b`JqJu4K*n0dtfa`O&X_q?)&E)fky*7i zv}$EI$?{(9U&U{L)zt0o%;s&#%EYfhA0y1vw3+w>h-)NQQZxYWbANxot!kx$oK8I< zAY;|%4rZN|p@8cZQE8>13vRv1<=miDp|h!dWXRWrkE`V<_E3U zhB-AS7b%E|ug;~aWmdDbN^2%1AB|-BgAsvoG-mngDyA=qTdHxX006jA?X*FkR~vU6nhA zdb}M#qkbxF9Hey0@dWr0ZmGhevOLy^45;Prf*p{EedbHbpOoB-l@l&EPV8kZW6fFK zW4)|Adpy_Hm^sSJpN#*#_G+VVYl7R&`>xf|NGqmx`<%F&1_}^Ghf(TYE95t0ggiYiP z2JF`lW(AG85~2@3Ng75~lmPwM8X~ty5!iRmB|;9bjarmFyrvILBqg_ZcumznSDum< z*`>#lR-Yb8-Y8XANvWH|YciFyHMyIIYFkRp@!qSO3zc-LdZnpGZ1fJVHD;H2>b%n~ zu|fFGRn+O7ce)EQF`d#wS12oXY2f3>lG$6;^i(O6wFEOn($)9=N9CPwI*u1AM;6Aa zQv7u8dGcwivwHMI3gz&cf|p&;JyvbKNh(A7q{>n{`7a6%a>_`udCfRqB4H@Ic&7U^ zc$ns;kJ8VAQ%o?0l)Dn_$ev<)k`F0gwa_j-qQmk(6da@cUrPDQRrkTisoDtN$#&~T zzu+&}w`#BMO;WsF`WrzbeGVh9ZwasNqo_N3is>&>lbY9_s_~BkjO{S{>DiQbdMWA0 z8UjV^=t`MxIsf#bg^yHKSn-u!&?Lhhn@^{`Q%nbpS8Y7!VIL2fmrC#dGhcT1@BE2J zHsuSdl$2Yk>l5I%HTcanvdw)EF1LSgZ_lQD`xf0Xnp+`#I8lbKl|Xs6sHfN)(!`Xz z3(c(>pQK1s;9;A4FDW`+-}v<37y2)LL6iTre^ccDgp@O5#g`=i-`r16^g2V0+kWJK zBAwcw{4f0L7a;$qrA2mW1!@1+$bZoHsM90=dFYqHhr=2>B>y8+N+17){NKMq<(*$h z{^L|Bemd8b&o4&)N0U@AXsOClIz=#TKN7h>BEgg0JU^fO#lk8}{!96g@>L7%(q0{w zznw%<{#+^FC;#xx?aBWpzu+&}x1d+|W+~n-y-Uz&zrIcK>h4Y5S@M5KYEtC?^c0`` z$90(fjBLs|UP}7WN}$llft2YU!aq&^AFHZ_{6{TqPyU@QS8c4;?2%1cA^$(!M~m(5 zgYM;#CI5A*l$85*U7rBADBFngY;*szprhV4)cYOVGC52BeI&ag|Fr}v4z8YA^50$O zjn9lM`QJy14((@@ev>O*pd z^7(rM_rQi(2Y-`WGoHzu`ngKu^d3TW?v#=iST_xfovqN4Zo zly4XQF<<$%R4NQUEz-YeEAz{jB0xSoAm@s(yB|(=rGDcClW6e9hDH5WdiH^oik#T3 z5=~f>DBw~k;Qpx)3h~C_-+lm~Vu(mgQ~~Q2yZA zQwUjU{K;FgN1}=nkMhQ?lSQ~lHH)N%;A{3_zq^Vc*}u~F3G`6AB|g@~-%TGXIqJm! z&)DF&0v|xO+5+0;?g3nR`gV1ONXEmczZWP+?Nk3>rp8zYIhiT+eF&|C1rdre9M}-^ zyLdNuVJ?zhi#sCETeiQD{)+4MEfa;*ak$N?1*r-=cx-9`{M34Sr0=O?xY#GhFXF0K zylD*@Tv5|&`sv)f6by_g(e}e5$_5SVJGGnGe;y>J%o{Q%+r#<>2aPB*oF()vyIwHu zVNzD&(Wvu7C}mu0=Aej!Ybf3#)@w2Qsv<1(6BC{`$>hq3L=2YkvM(;gD@9`!;aMT- zpK1=bg(?RM;N+#h|Dr(Y>og-4J~d<3RiH47eN^d<7`5Ul#OjRsCV|t++($2=jy9Yu z*R5cmcasKp7RjVtYzwz4Z-LqU)$IQAjQ3io*K%Y}oXzlSd7y8ITKC8jEMo1AT66mH z7Dk~bw^wxwvdccJtOVM}j9Shy+K1mFY`$(8G&!e&q)}~e5X9|CNnn{VYYiB3uG8(G z(!Cj*<<@1(=}kKEH)f&txAt&%vE`hmY8hMEBZw1}GEA9_S<}eL>L>ZZLm|mqDkhL<(3cXuqC#8GnqPxesKJtoe z)}=d9Tx8cvu|D4S4)~eopVrhT<))`Db(@cc`-Bd!+zS~j)&G+|6{86 zjtnQDh05rGAfV6&M@XM4Vtw!a>1OU!4?kJAsWzH?(^gm(w3MTI+Yxl(qZ{dhmU1z# zuO(-q$h#JM3nfHtMFmU}<)9N9Aln&8=8&ygB|R~oDgQ%7|Gnxp#g~pthY$>oGPA z5W-P=;0&CYB(e+C4FQ5);bK zaAUUfmar4TO{C0UQYCD}!gnQbH~Px{1i^cj zgzQKUt>050mtY+EtTF30DPihe)^08~hs`!PC1ILVbWl=CvcmP&@}>;@cb-tJB;uaz zO%-M*y-TD|B4viN$NLBe4U|$31rABGym>40<~m8%0h_Z-E_0cD)$u<9)xx zw+v<6gco)mE=QX z<}6;Y+kx>a;ZU2a!}%iX-fG8qcfm0800Af53J=DZ*yF)kxZDps@BkoW)Ndd=;)TdN zU;n;dlaAsN1MCsgONABZ_E(wk=fjil%&UyU4v4QCMG`J-VIFVprCOBk#em_{J zSemu;Vn)tm#8|xwCwck``tQCpSux{?eU}|92b7L879wL{q zhx7p{_@$JZzkX0mb}QT7hm^GD?r2ZTMO8;OM!5NqAM`4hNXP{eIBvjU1HWp1rq7YB z4T6idQ%&I!YxqbmNxYcpK{^ucx(PEqL0D!!1rrc0+Gk6|dPFrJCJa{Eqvs3z{hpA9 z9qfiLCuEu7%M|;yN8hRLpc=j^%YoThXG<2-ZFn%8*Wk#5dKiWiq^UM$=qj~A;m zB!V<2OwvN~ofAIKzGNVi2lge6vgcGcs3>um8c)vtyKX{S3_f;J%aUYYa3#xyM4ie_ zA6L)j$d>RPaa(Dv6+;y+_G;SP*@``YI3+i9*~5F}k9UZ(T|s7(S>m!lj2uJGnMzn> zM`|D!SsoOAfI|&A{Fd!7JQoWKQLEKMk^Nc{G;u( zHRudsW!h}al(D4Ku$YB`lryH-T5rt!ri2aW5Wwj9fmK+58Z+O8K-<|><?%l0;dFGgWC5E5Ii)1~^ zEip(v@rl-kn0;L{?g-zHm&?ZLW$XmVbLPa7B4w6{3Hjt$JF~=>R5Qga5p(an<#xi( z&j0!AZNFEnx36Is_FD?EUfk*)MTG)J3v}_;+vC#1zuN))0|ff;e;dN_;QtD*0)LYN z^58H2B|ZiC%YW?yzoZiAXD^@i<}dUU0Qfm2{dH__DrZ3@Nk{gkz<0GCgR{QNAPblc z#^A(uW1!M_k?rtVUttx$%EJ3i%?g&X{t4=waVQo`EUR+Hp}A{LmlckJ1^j&bP_6Os z`xST7-)97b6{(efj(!W|!Q6SZxtv$Ce<|~}eY;HC+(F8vqxP(Fd2eDR)vTxNmpHFR zkau2dL3=hEB^tiB(|5r7f`NZp>d={7-FQ_))Y@2cd=#e?98j5hoaM-*7nLJ;97a3U z|J<1At6wt}oT2Nf-$Q|CYYqto8VzS2GEk`VPVPT@Ek~chi_KYOJ!y= z?U{QNmeB@iV?&vpNF z1_;wbD^dMvm ze^!oc&<|c4IrPS)KpxQ0HF**-j<<%yCs>_w^%$vbz5EU3?}VYsHxu94W>^@7>Q zi!406_SA)Hq|y@@0e^p2-P_E)uzT0OTldD4khP=k%_;Vyf@mR)X(8vEtHVxZxt1g& z*u^_NdO7jcT*(Rw{O(lYD*KH!QHEpgCV|r#2Yc%=`u~ZIoQce04n{A)Z zJ&o_yMC?mAsP<+}kh!YX<{(UK&FOZ4<7)kpN(6Q6u9kprWzDe!sClF-M*)gwq!SV&y1}jPx9E)U`gzFA9qRjMyw@Py7E-i0 zWwFnmHWCU^<1^$!kFuQ+UG(}ZK2Xz-lOZ9_ zA@&K`2ZaJ^(N=Rf$3;pwky6tQ#Ua#VIP9nt)lCz{?km(SPH)5o!c)AXI}>U|f~>{O zOkY`XiM83_B?RWIr4py+Y@3jV+FI!}0*p@Nx$R7#j#372C_p8F-1y1J;mPL;4A|a^ zpP4O^m92QMKPh74H$hp`y_4qy6y<}&cK2ejrz2LDRPKvE6g=4{NA|HhlV+Q#&h(+k z6ZA7D(X*2!YOsqK@-csOo5CNUmg$ zZtzTt(0YbZ>A~sjc_Pq*nZsYP)N45K3c8qAFcgBbxm7O&UN>faANCUp?4f`)ySN#5 zH#x%n_5=e>l+Y4nb0V-ZWW-hm+nR#?5ItkH`S9`333ZGyYb#yjdkO^vt!2TwH{e90 z8{m^dHy%ER1MeBLmXOft{dWOH-J3|wYwF(F7sh(5Zp*%*cKq+N<5z^aYPR^Du=QqG zc7yW914kDTuHSVA7l&lV?^3s^OV^jXZt#tIf=W(qXo{@c6z#e?)^&HVZutShy5@a} z=CF7Wi81=48#3&ZyhF)@osvG$uFZkn@k*cnihh?8eR13D+8hdOb-M(A-(v<%i}sUASb)n6ZOnXLxTftRzcF*EVzg_FnUC-?NFAp&W{NXqFy5GX zH!tA4G4mI^>~Ek2`_KxtKYp?dUIMs?qxv0q>Q zP&!ib*hl}EMEGK)XY=OElK4MkmZm+K-Fvn`YF-27(uUbmyd1yzr(lUQ;O;+(!NO8$ z+T*3*f;&}`I|B}W5T9)_X^GXq_X$JlSr9!bF+b9@?*OeTHve4vwBJdQpHe-?`eT8QCT!66xBQ7!uSQ#p^8^Y^-0u3bq~-xX zt1pv-4iV$8#|UQtP8;1b6`F5!uao$LC7B`97*nN$( zf>$$E2%_alUM!9?tbm&{RcN(jrRt@a|UVC;{H-T-Wn|yAkCRWC0?gXjR zEd?mb4$I^_{w%3NQEWlQ>hl=~PZ2-4*`>11o3 z=f&BjgF0;E;n|cEv!(dsai2hrF}y^@q}=m)DtmV2``0&GXSg#EBe@3yTbw@aCI@b47&m+;Yv+w#>wCeW_3CYhp6ufE$3%a-}#x|lT5!Z z$g~aVyC9SL&d;<>GF?@W=_B+41)0>hpQ#(o!$yCj+g~z?kq0c=mw$Zz?7;*w*ymn6 zcn21~XZ5k*(fG88SzPrst%6(ZE8IP!q=%(HQKTyO5HT5YW00B?E8uAxMEsVEV#JZM z=dBos4OZ8eaCMBB$NZajdZ?CX4K*_7fikoVgN?6tW`^GMZ&2tjy@|+m(C{GeQ*4R5 z1vCnMs#I+V4W%WYnRe4ao%Y@HvEAH%{!_O<_h&%_g#AmiB6>+ux0gHT7BPf%d%4g+ zILOo0{BwJY*_sj}gbgnX?mz-6W%o!}5$v}Ux3*PQfPod_;jXV~4eKil=dRn@f@Rq# za%|R=^CvXVbhKDvD8TmD#;U%@*NSrqfnh1OH&G)zFp^v_XQ>yZNWv zdZQv{5qPAn4MAGY3@7EBrpsw0Q=??k<+OkzQcjBmr5Ey#Lx#x(Jau^$l&6hhrM%{9 z)%RJN>oHL5fXc?Fx~N-igN|s|cZs6!Z!>8Lw7eX^U#{||?-6T6yS_^lZC)tny=u>* z{o(|`Gn$WBXJf>$L|uHO7OvH@YLy$)`e${BSss50WG4JAC6+J3blcn^UeF_c5T`%& z>sKHMsbf5)2oFt+5R1W3?!17=n4)WctIh`ixP2L2{`cvRdrLe_QiO6D5dSKLJ)RvJ zS~e+5by;()^sE6I{kfkdm(-{C?=NB2%~LvD5>ew)4z%<11L}^D#92BH@sGI7w}hma zPjY3pBl;s$y&K&=!#%ma)cDM_nbV|=8{JpOhz7@lKkT)D{>P<<6zK22>V0zK(hWm9 z*bkUUAZ=0mF@I-o#uGVK@hYe?kS6N=fN|+_3-W7!;Ld>G{@#-}-4caXc^6m}^hfz~ zll=tYW!i!MO=>Sl;QGX^IsU|0nxAnGUDUY+CadJjMN!)A$YpeSUi4?9)&4v$`q!y6 z?Y-#lcPZiD=}fA+40(`8H+h(L_iwV`ZFeal1f3;jfgLwb(n6UA9+HI!qdc9G^g%~Q z0K^Vn_R7LL2_TbO%Tu7eN@Yp8{}`q&AlM&%avvc6KTNhR*_heighNfKy|}Aa_$afg ztQV&D-qF^u+zWG`W_#X7o3~Kl4v^3t@U<`I`)FhFj|-V6HH@7bHLyw9t{=a-z_?^)Y!dRv&xAe6@w$`+UbW08Cp9`|||=*z6TS>2D&=--cUv$`LlF(daQM6fO%JE8*1Vs$@4 z-@kMzGJa90?~*gf#kfQbvIcLAA2M6CJsbP0?w$l}mtuF}g>i-11Q8U#|?^Tq$c_AnY*`j!F(;fpp z53Mj&6b);O_E{OS&+QxH6lGV;BMuIUlqQ+=jSUy6>o`Enp8onl;ip2@#77zK)s@S` z`OES}g#+2>c{>ND6^nuzES^a{nM+(7FKp$P)hVz)=ON_;XtMjm;{7civ7r}?1Ix^4&=qXw&bvBO3*3;ccVYt<4|a9&6YYY12=_if35p!JMek)+)9 z)%%@i8->xM*HN%d3FS`Wt!!6M+P})(&_^EUYrkK9;i#asUJKLin6m%{R^u?^73Ja{ z{n9YkPHBGY(HkC^c4Bqmu9C%`=U3}6=;6!Z+nb4pvc#nMdBJ$n$LAx~i+ViesEfPw zOh9TQ8#&*_?cnS2@6oXL^Xc7pc6cyQ#&nnSU2Y$m$P!fvgb-=7&|`MKV9`>*FY^5v zQi1sULY;$H!n~=W=bybvf1T|cPamr74CF2@N0}GMqNh0^)8Je+$$k@6y==va{kBS= z_6tkhElia%(0At2CSLRR+p_XzcD<-pI{D*rC1(xy7PQ~zhyM7~%gq1Z8XqK#YuS6U z)?wWRd%_3CPtM2A&s$Bn!|j^B(E$S?p*~m5=!JE<<7FByY^irhBUjk=u*bk zQi>uDO1Vad{YFX_w@OmmD8jhI7(c2q)4(pELnwLN;RiTvCe=$NSLhO1YpMzrWc`-;tk;` z&VY}n0^3rxr;uOdatYuzU$H9bv7UrgZ4(Zb~}_kzKAsh_uk=EXRm zrj4SbCn*oDY%ZBipB&&LQZ)rp-bk;!_$4xNHo0fvncG<^k?u%lo>(y_UfG+JZduyp zuRgF~I^8yKFha7+c{y1ncJc1#K+%bhY-PO?RIcP@UL|B9nB>7|t{mxrU8!B;Q&N~vQ| zQesLuOcp#yCB-gThxO-8!kUwE=#FDRTdxAb4LF9Lhd6>oM_sR5jT$G zYlRI6F5Y=T?_aZ$J{S2JF=CnAAJ~#uLkaBbIMQlbR7AN8i}CgcgA-V(&GdYJT)R}Y zg6jsk@oT)TTKt$auOI7jb^V0dRwlU@(;fZYm1w3(qGjoELtQGY*GnoavUnOx_nja7{E znybjQOm^2aZf#u|KSSQCIQzDBrJj$jHg3S1-whjf7&nyc;a_wQ9PLH+e_mY0tWaVG z_9p&eTDU)Y5jTD}pq99vj4!raZ5q>7$v!K-CJXsb@?^?%#>9!*TZr`L>*0``D&hu;X zh;uxt3aLx>!etlSxb;L>V5Due{l;x97?AgD zo%j6wykjM=t=Xbo4VaSpuHdD5e&}M_wQ5yjeIgpaWD6~Y?zGL}6lQuAI#)uS0V#{d z_sMH~TW=}c(V4WP9&M2z@c;eUs@2P<RDz9VnH9Ut(OO)G34>cd6?DRS{ z^(30)!b5whw95S@;nDzir^e~4>&d84)Jrf)5?IS^1tu%ppbbpqrlzETfdkfLpG*52 z+;5PScFpxNN3T?x1Cdvsd8kA;WVG?K;bCj&}Tr(9a zF85pfK5{>Gj*&VU73rLNkhG(2sZbFptZ2s>FtB~A-ab;b%8_KWs&s^E$ql&5klOot z-+eduEs-7wmFbr27Sp=!cfTO5yQFlMdeTZ-Fh;kk>9)L9C8$sHnNG$`(X`X-XCy)X z_>3eGBL|t9y}@9JH*|YSy#YPt~)gQCjx#S_QwP z%wp2I$28ghKq~j)xW1QEtY!{3LA-7$Tlo#tzh`un zLY7Lj`*T&Gs=>Wo7yf!u72aP8SCtw%+?}Yxdr7#ebQfz@(_W<5Wz&{Ux%wq5a-yVc zU%wp4E7tW(w0oGYUvjuzC5L;nu4kVx9|40uG<$loT^ccdv=Kz_el-r{K$Vw4 z^22|i-p9TIslAGe$a$@l$Xr*tY>h(df==Q>iX=L3?>J;@{@hDe5zsl5fBIy>TCOs4D_ z+F}ijMnKltqAKy6%VaPWSMA7?eh&c?_77dIAhkR#=bp8SsZsj748m6De&l2FDV_zH z-YdLU;%pDX-}U2?LI{%jcB0i*A`25mLWE6}u-K!^iRs{M=5LO3xy4F`BdUj_(-m{Ve@>t1v4klQ$4-(*O&4ZUu z1HWT?wK*!1$~RCsb!?LMiA&LHm!*mfx@lcl#!_gb`^_^7><9Avt@L6L)J4=-jP3U9 zlYDP4Fq2jV8~7^9Mvl2+zuU>U{VK-Rbh_UpkMSil%;J`|O*53I30ZI&^NUkmlzxh1A8x->{Upbuxn^gU)v+9*uU{nT zz#*OGUrD9c*Q%ZF0dy7TECX2~O7GjRhXe}j(Ke+k0xdPAgTgJ-4#w{cJzDh*$(MMY zg~v65*&2B1lX%ZtzedJ~l10&Cw-927>7FQv-BqO^rca+Kfb|qv)|%5oYux{lPTJ~S zy9S~=<|V8?YfPtPZDvwF6qiC08hkR@$Y|UuWh*GgJbh{wn6oAExLQ&QTDkworaZoI zJ(vgC_~kxS;fFcDSuK+e)G(D$@e$v7QmKNpIQ43Ce}964FBcul<0nEYxbIOCQIr9=&-AEAJ!NK!Q8mVtt@@v6*WC^t<(&8O6)9vD0_ z&V3KNs0~*{*b}rhTN9@UI{mH!ikmpa+E%x()8upxZQ9klCjG2bLfurN>wPkFPC=y) zWcuXVzhCu6{S5Mo?&xAPZDVhRn;vjfbnDZ^ z<{xKiP4w3KmbT`)edidr|C)F&J)Ha9rY-u|&dX+>CeG||Tr>S02=$E1IA*uFM_KXq z%uQ%x`n#Vp$m5n%ZFxu4PPn|VN^UqVlKQ$#4(96m;4|uac3ifuUJ3}-n2H&D6?>i2 zIBvP>v^A!TAP^LdRo?zK7-uAv_-&7<@U9Tc?O*5V+hqSF@4SeqY%oXk5`WK`12ue9 z7>3kWm`ZVXiLbaj!s9>r`nz>%KW14wev{MZWgbTFlQ_mcuS`yPPiJ_KTq0zZ{)voE zphZ2a8q?yD3bUPZYZ*@WtR@-Cmm>CQ+vH%~PW%(@skVq|%yiY(vy%q-93Whu5UA(-ox*U6-41 zJpE+Fe}(%HA)n%8)qMNr~7<=CgAqxl}_Hp1kT}orlou@ z)9rj~UnyDf=RjIvx1_yMsz67>rSC`4DgMXD4(otrsDI+fpR76=Mn$iaEtV^_M7q=NldNC|f7QKJ+b z+WFT0SjuDe{0)gd-?{Gfq|Q3my}~PBwGg{j8A=f$3w(Fa@yZ`0J0{`LQTdLKTVrvEE?e@)u6AH8?E=nK&M z1j)+1K%*6O{&(p8gHr1BOmat_B=4o?D4o=P^u9`^^yygWeZI~+zmVR`RVjWt_Y=nD z3(`a@Nd;wYloIUHlLW)|qsLN-WW4{(^YiI_Dn(}L{YI~R)k3@USsBXx%D+e2OZmg3 ze4pNTAKjkbEB%7MVBbFKr~7uN6mOS~=&HYMp+td5vPO6PhjP&tP=zY;JzU=NVnLM)eew!)< zeMIjpz5gxSh~+wvyx)s;1nEK9l)v&)(%cs%P|Y1hYKeJd@<>|=D034pQe);?UKYyBB2mG(d{15*JW-g1^V@%l)r#Tmx z_Sg6v$!F%kD*Xcwm1U_>{|Pc=(0)_~WQhQuopHK3 zR^&CO^kmh|Ce%@XRq7~4u9k%9vxpW5W!LXg_+Ke0E$|U|JBP2ueuj*l=eJ zI4VMlL?|sD3)JR2VQOui=%0>B7|}(9sYQLFcRE0rfKg=RC7p7!K375Xc{r4J+^O9l#F zARiZWD*a_XJ>(Gs-au<9nD+l`dU!7V0L5g-t9NF2cAUyH!|geNGw^M$(be#OKCoe8k8~I6_R|?=Pcbd@a18}M*OZl+n)c(-G3(Ki>7K+q;C4PEb<3R zefH%Qrglzf)T2oj?$_wPAbZp{T5r~)MtwQKcmnP~;g&fvOW*d?7B>dI2+Q0EFY<7^ zjl0_vk}I^wOn9$b`q{mjtXNU$8Ju7ji=(_=3I9n?BAPRnN{6IcHcdgnEU)4Mtk+Gu zW*Zh+ZAS8X6?|E7-6v%ew(yUwQVAixS9%n0KuMMN7mWbI($Wj~mp|@C{au9Qk3G|j zQGYvu3P1nDCK-IRSrLy z&;OJ7*T8SRyWNSy$n`j>Qn!8>x`|LHd&K^-!;oa|DlqduK z7EyY^p2e?l0D4N$`eiNMiCD8dB9t$>RaUh#E&FpGDW*lvDI66^-)>amld%{6s^j4e zz@1XO)BPDi!~(%;M1@jzXC?{om7Y8ELp=goqYkI|Zf+RPRQ&8e4F8+*Ih?!o-X>||Mg5?Rwn^Ut5GjQU;@bf6AWvV~D!DnYyU$b^}jy3pSdlHSBW zP)#;yvhs~{Bfel2p$p?`DT8q>F8*sV<0yiYNFS3i^tn*>px$kJgFD1MX~#(3N_=p} z)3rQA{E1(cnW!M`H!tx�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

&R8 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≥t=z)9L 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 F{{i?lR4)Jk literal 0 HcmV?d00001 diff --git a/nuparu/include/openvdb/bin/vdb_view b/nuparu/include/openvdb/bin/vdb_view new file mode 100755 index 0000000000000000000000000000000000000000..7242c1bcd2cf5ebb3acf7fb840a07ef5bb79d9d4 GIT binary patch literal 2572520 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%0DAEA)7}vUo%xU(~i}g-g*2H@DSCk-WNL${! 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

rBxr2 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