├── .github └── workflows │ └── build.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── NOTICE ├── README.md ├── SRC ├── BinaryNode.h ├── CmdLineParser.cpp ├── CmdLineParser.h ├── Geometry.h ├── Geometry.inl ├── IsoOctree.h ├── IsoOctree.inl ├── MAT.h ├── MAT.inl ├── MarchingCubes.h ├── MarchingCubes.inl ├── Octree.h ├── Octree.inl ├── VertexData.h ├── VertexData.inl ├── ZOrderOctree.hpp ├── api.cpp ├── hash_map └── logging.hpp ├── VERSION.txt ├── build_and_test_python_library.sh ├── build_library_and_example.sh ├── cmake └── IsoOctreeConfig.cmake ├── example ├── CMakeLists.txt └── main.cpp ├── include └── IsoOctree │ └── IsoOctree.hpp └── python ├── CMakeLists.txt ├── README.rst ├── bindings.cpp ├── examples ├── cayley.py ├── point_cloud.py └── simple.py └── setup.py /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build & test 2 | on: push 3 | 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v4 9 | - name: Build C++ 10 | run: ./build_library_and_example.sh 11 | build_python: 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | with: 16 | submodules: 'recursive' 17 | - name: Install build dependencies 18 | run: | 19 | sudo apt-get install python3 python3-pip 20 | pip install pybind11 wheel 21 | - name: Build Python 22 | run: ./build_and_test_python_library.sh 23 | build_binary_packages: 24 | runs-on: ubuntu-latest 25 | strategy: 26 | matrix: 27 | python-version: ['3.7', '3.8', '3.9', '3.10', '3.11'] 28 | steps: 29 | - uses: actions/checkout@v4 30 | with: 31 | submodules: 'recursive' 32 | - name: Set up Python 33 | uses: actions/setup-python@v4 34 | with: 35 | python-version: ${{ matrix.python-version }} 36 | - name: Display Python version 37 | run: python -c "import sys; print(sys.version)" 38 | - name: Install build dependencies 39 | run: | 40 | sudo apt-get install python3 python3-pip 41 | pip install pybind11 wheel 42 | - name: Build Python 43 | run: ./build_and_test_python_library.sh 44 | - uses: actions/upload-artifact@v4 45 | with: 46 | name: wheel-${{ matrix.python-version }} 47 | retention-days: 5 48 | path: | 49 | dist/IsoOctree*.whl -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | build/ 3 | .vscode/ 4 | venv*/ 5 | dist/ 6 | *.egg-info/ 7 | *__pycache__* 8 | *.obj -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "python/pybind11"] 2 | path = python/pybind11 3 | url = https://github.com/pybind/pybind11.git 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Spectacular AI Ltd 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | cmake_minimum_required(VERSION 3.3) 16 | 17 | option(IsoOctree_PYTHON "Build python bindings" OFF) 18 | option(IsoOctree_STATIC "Build static lib" OFF) 19 | 20 | if (IsoOctree_PYTHON) 21 | set(LIBNAME "IsoOctreeNative") 22 | else() 23 | set(LIBNAME "IsoOctree") 24 | endif() 25 | 26 | file(READ "${CMAKE_CURRENT_SOURCE_DIR}/VERSION.txt" VERSION_CONTENTS) 27 | project(${LIBNAME} VERSION ${VERSION_CONTENTS}) 28 | 29 | if (IsoOctree_STATIC OR IsoOctree_PYTHON) 30 | set(IsoOctree_LIBTYPE "STATIC") 31 | else() 32 | set(IsoOctree_LIBTYPE "SHARED") 33 | endif() 34 | 35 | set(CMAKE_CXX_STANDARD 11) 36 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 37 | 38 | set(ISO_OCTREE_SRC SRC/api.cpp) 39 | add_library(${LIBNAME} ${IsoOctree_LIBTYPE} ${ISO_OCTREE_SRC}) 40 | target_include_directories(${LIBNAME} PRIVATE SRC) 41 | set_target_properties(${LIBNAME} PROPERTIES CXX_VISIBILITY_PRESET hidden) 42 | 43 | if(NOT MSVC) 44 | target_compile_options(${LIBNAME} PRIVATE "-Wno-dangling-else") 45 | endif() 46 | 47 | if (NOT IsoOctree_PYTHON) 48 | include(GNUInstallDirs) 49 | 50 | install(FILES 51 | include/IsoOctree/IsoOctree.hpp 52 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${LIBNAME} 53 | COMPONENT Devel) 54 | 55 | # CMake installation boilerplate 56 | install(TARGETS ${LIBNAME} 57 | EXPORT ${LIBNAME}Targets 58 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 59 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 60 | INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} 61 | PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 62 | 63 | set_property(TARGET ${LIBNAME} PROPERTY VERSION ${PROJECT_VERSION}) 64 | set_property(TARGET ${LIBNAME} PROPERTY SOVERSION ${MAJOR_VERSION}) 65 | set_property(TARGET ${LIBNAME} PROPERTY INTERFACE_${LIBNAME}_MAJOR_VERSION ${MAJOR_VERSION}) 66 | set_property(TARGET ${LIBNAME} APPEND PROPERTY COMPATIBLE_INTERFACE_STRING ${LIBNAME}_MAJOR_VERSION) 67 | 68 | include(CMakePackageConfigHelpers) 69 | write_basic_package_version_file( 70 | "${CMAKE_CURRENT_BINARY_DIR}/${LIBNAME}/${LIBNAME}ConfigVersion.cmake" 71 | VERSION ${PROJECT_VERSION} 72 | COMPATIBILITY AnyNewerVersion 73 | ) 74 | 75 | export(EXPORT ${LIBNAME}Targets 76 | FILE "${CMAKE_CURRENT_BINARY_DIR}/${LIBNAME}/${LIBNAME}Targets.cmake" 77 | NAMESPACE ${LIBNAME}:: 78 | ) 79 | configure_file(cmake/${LIBNAME}Config.cmake 80 | "${CMAKE_CURRENT_BINARY_DIR}/${LIBNAME}/${LIBNAME}Config.cmake" 81 | COPYONLY 82 | ) 83 | 84 | set(ConfigPackageLocation lib/cmake/${LIBNAME}) 85 | install(EXPORT ${LIBNAME}Targets 86 | FILE 87 | ${LIBNAME}Targets.cmake 88 | NAMESPACE 89 | ${LIBNAME}:: 90 | DESTINATION 91 | ${ConfigPackageLocation} 92 | ) 93 | install( 94 | FILES 95 | cmake/${LIBNAME}Config.cmake 96 | "${CMAKE_CURRENT_BINARY_DIR}/${LIBNAME}/${LIBNAME}ConfigVersion.cmake" 97 | DESTINATION 98 | ${ConfigPackageLocation} 99 | COMPONENT 100 | Devel 101 | ) 102 | 103 | endif() -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | IsoOctree Python wrapper, Copyright (c) 2024, Spectacular AI Ltd 2 | 3 | 4 | ----- Original IsoOctree code, https://www.cs.jhu.edu/~misha/Code/IsoOctree/ 5 | 6 | Copyright (c) 2007, Michael Kazhdan 7 | All rights reserved. 8 | 9 | Redistribution and use in source and binary forms, with or without modification, 10 | are permitted provided that the following conditions are met: 11 | 12 | Redistributions of source code must retain the above copyright notice, this list of 13 | conditions and the following disclaimer. Redistributions in binary form must reproduce 14 | the above copyright notice, this list of conditions and the following disclaimer 15 | in the documentation and/or other materials provided with the distribution. 16 | 17 | Neither the name of the Johns Hopkins University nor the names of its contributors 18 | may be used to endorse or promote products derived from this software without specific 19 | prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 22 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES 23 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 24 | SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 26 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 27 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 29 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 30 | DAMAGE. 31 | 32 | 33 | ----- Z-order Octree, https://github.com/oseiskar/z-order-octree 34 | 35 | MIT License 36 | 37 | Copyright (c) 2021 Otto Seiskari 38 | 39 | Permission is hereby granted, free of charge, to any person obtaining a copy 40 | of this software and associated documentation files (the "Software"), to deal 41 | in the Software without restriction, including without limitation the rights 42 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 43 | copies of the Software, and to permit persons to whom the Software is 44 | furnished to do so, subject to the following conditions: 45 | 46 | The above copyright notice and this permission notice shall be included in all 47 | copies or substantial portions of the Software. 48 | 49 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 50 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 51 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 52 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 53 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 54 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 55 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IsoOctree library 2 | 3 | An implementation of the _Unconstrained Isosurface Extraction on Arbitrary Octrees_ algorithm 4 | forked from the original [IsoOctree code](https://www.cs.jhu.edu/~misha/Code/IsoOctree/) by Michael Kazhdan. 5 | 6 | Main modifications: 7 | * Add an API (the original code was a command line application), see `include/IsoOctree/IsoOctree.hpp` and `example/`. 8 | * Port to C++11 9 | * Add CMake installation scripts 10 | * Python bindings 11 | -------------------------------------------------------------------------------- /SRC/BinaryNode.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | Redistributions of source code must retain the above copyright notice, this list of 9 | conditions and the following disclaimer. Redistributions in binary form must reproduce 10 | the above copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the distribution. 12 | 13 | Neither the name of the Johns Hopkins University nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without specific 15 | prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 18 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES 19 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 20 | SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 21 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 22 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 23 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 26 | DAMAGE. 27 | */ 28 | #ifndef BINARY_NODE_INCLUDED 29 | #define BINARY_NODE_INCLUDED 30 | 31 | template 32 | class BinaryNode 33 | { 34 | public: 35 | static inline int CenterCount(int depth){return 1<>=1; 61 | depth++; 62 | } 63 | offset=(idx+1)-(1< 29 | #include 30 | #include 31 | #include 32 | #include "CmdLineParser.h" 33 | 34 | 35 | #ifdef WIN32 36 | int strcasecmp(char* c1,char* c2){return _stricmp(c1,c2);} 37 | #endif 38 | 39 | cmdLineReadable::cmdLineReadable(void){set=0;} 40 | cmdLineReadable::~cmdLineReadable(void){;} 41 | int cmdLineReadable::read(char**,int){ 42 | set=1; 43 | return 0; 44 | } 45 | 46 | cmdLineInt::cmdLineInt(void){value=0;} 47 | cmdLineInt::cmdLineInt(const int& v){value=v;} 48 | int cmdLineInt::read(char** argv,int argc){ 49 | if(argc>0){ 50 | value=atoi(argv[0]); 51 | set=1; 52 | return 1; 53 | } 54 | else{return 0;} 55 | } 56 | cmdLineFloat::cmdLineFloat(void){value=0;} 57 | cmdLineFloat::cmdLineFloat(const float& v){value=v;} 58 | int cmdLineFloat::read(char** argv,int argc){ 59 | if(argc>0){ 60 | value=(float)atof(argv[0]); 61 | set=1; 62 | return 1; 63 | } 64 | else{return 0;} 65 | } 66 | cmdLineString::cmdLineString(void){value=NULL;} 67 | cmdLineString::~cmdLineString(void){ 68 | if(value){ 69 | delete[] value; 70 | value=NULL; 71 | } 72 | } 73 | int cmdLineString::read(char** argv,int argc){ 74 | if(argc>0){ 75 | value=new char[strlen(argv[0])+1]; 76 | strcpy(value,argv[0]); 77 | set=1; 78 | return 1; 79 | } 80 | else{return 0;} 81 | } 82 | cmdLinePoint3D::cmdLinePoint3D(void){value[0]=value[1]=value[2]=0;} 83 | cmdLinePoint3D::cmdLinePoint3D(const Point3D& v){value[0]=v[0];value[1]=v[1];value[2]=v[2];} 84 | cmdLinePoint3D::cmdLinePoint3D(const float& v0,const float& v1,const float& v2){value[0]=v0;value[1]=v1;value[2]=v2;} 85 | int cmdLinePoint3D::read(char** argv,int argc){ 86 | if(argc>2){ 87 | value[0]=(float)atof(argv[0]); 88 | value[1]=(float)atof(argv[1]); 89 | value[2]=(float)atof(argv[2]); 90 | set=1; 91 | return 3; 92 | } 93 | else{return 0;} 94 | } 95 | 96 | char* GetFileExtension(char* fileName){ 97 | char* fileNameCopy; 98 | char* ext=NULL; 99 | char* temp; 100 | 101 | fileNameCopy=new char[strlen(fileName)+1]; 102 | assert(fileNameCopy); 103 | strcpy(fileNameCopy,fileName); 104 | temp=strtok(fileNameCopy,"."); 105 | while(temp!=NULL){ 106 | if(ext!=NULL){delete[] ext;} 107 | ext=new char[strlen(temp)+1]; 108 | assert(ext); 109 | strcpy(ext,temp); 110 | temp=strtok(NULL,"."); 111 | } 112 | delete[] fileNameCopy; 113 | return ext; 114 | } 115 | 116 | void cmdLineParse(int argc, char **argv,const char** names,int num,cmdLineReadable** readable, 117 | int dumpError){ 118 | int i,j; 119 | 120 | while (argc > 0) { 121 | if (argv[0][0] == '-' && argv[0][1]=='-') { 122 | for(i=0;iread(argv,argc); 126 | argv+=j,argc-=j; 127 | break; 128 | } 129 | } 130 | if(i==num){ 131 | if(dumpError){ 132 | fprintf(stderr, "invalid option: %s\n",*argv); 133 | fprintf(stderr, "possible options are:\n"); 134 | for(i=0;i 31 | #include 32 | 33 | #include "Geometry.h" 34 | 35 | #ifdef WIN32 36 | int strcasecmp(char* c1,char* c2); 37 | #endif 38 | 39 | class cmdLineReadable{ 40 | public: 41 | int set; 42 | cmdLineReadable(void); 43 | virtual ~cmdLineReadable(void); 44 | virtual int read(char** argv,int argc); 45 | }; 46 | 47 | class cmdLineInt : public cmdLineReadable { 48 | public: 49 | int value; 50 | cmdLineInt(); 51 | cmdLineInt(const int& v); 52 | int read(char** argv,int argc); 53 | }; 54 | class cmdLineFloat : public cmdLineReadable { 55 | public: 56 | float value; 57 | cmdLineFloat(); 58 | cmdLineFloat(const float& f); 59 | int read(char** argv,int argc); 60 | }; 61 | class cmdLineString : public cmdLineReadable { 62 | public: 63 | char* value; 64 | cmdLineString(); 65 | ~cmdLineString(); 66 | int read(char** argv,int argc); 67 | }; 68 | class cmdLinePoint3D : public cmdLineReadable { 69 | public: 70 | Point3D value; 71 | cmdLinePoint3D(); 72 | cmdLinePoint3D(const Point3D& v); 73 | cmdLinePoint3D(const float& v0,const float& v1,const float& v2); 74 | int read(char** argv,int argc); 75 | }; 76 | 77 | // This reads the arguments in argc, matches them against "names" and sets 78 | // the values of "r" appropriately. Parameters start with "--" 79 | void cmdLineParse(int argc, char **argv,const char** names,int num,cmdLineReadable** r, 80 | int dumpError=1); 81 | 82 | char* GetFileExtension(char* fileName); 83 | 84 | #endif // CMD_LINE_PARSER_INCLUDED 85 | -------------------------------------------------------------------------------- /SRC/Geometry.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | Redistributions of source code must retain the above copyright notice, this list of 9 | conditions and the following disclaimer. Redistributions in binary form must reproduce 10 | the above copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the distribution. 12 | 13 | Neither the name of the Johns Hopkins University nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without specific 15 | prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 18 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES 19 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 20 | SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 21 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 22 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 23 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 26 | DAMAGE. 27 | */ 28 | #ifndef GEOMETRY_INCLUDED 29 | #define GEOMETRY_INCLUDED 30 | #include 31 | #include 32 | 33 | template 34 | Real Random(void); 35 | 36 | template 37 | struct Point3D{ 38 | Real coords[3]; 39 | 40 | Real& operator[] (const int& idx); 41 | const Real& operator[] (const int& idx) const; 42 | Point3D operator + (const Point3D& p) const; 43 | Point3D operator - (const Point3D& p) const; 44 | Point3D operator * (const Real& s) const; 45 | Point3D operator / (const Real& s) const; 46 | Point3D& operator += (const Point3D& p); 47 | Point3D& operator -= (const Point3D& p); 48 | Point3D& operator *= (const Real& s); 49 | Point3D& operator /= (const Real& s); 50 | }; 51 | 52 | template 53 | Point3D RandomBallPoint(void); 54 | 55 | template 56 | Point3D RandomSpherePoint(void); 57 | 58 | template 59 | Real Length(const Point3D& p); 60 | 61 | template 62 | Real SquareLength(const Point3D& p); 63 | 64 | template 65 | Real DotProduct(const Point3D& p,const Point3D& q); 66 | 67 | template 68 | Real Distance(const Point3D& p1,const Point3D& p2); 69 | 70 | template 71 | Real SquareDistance(const Point3D& p1,const Point3D& p2); 72 | 73 | template 74 | void CrossProduct(const Point3D& p1,const Point3D& p2,Point3D& p); 75 | 76 | template 77 | Point3D Normal(const Point3D& p1,const Point3D& p2,const Point3D& p3); 78 | 79 | template 80 | Real DistanceToEdge(const Point3D& p,const Point3D e[2]); 81 | template 82 | Real DistanceToTriangle(const Point3D& p,const Point3D t[3]); 83 | 84 | template 85 | Point3D NearestPointOnEdge(const Point3D& p,const Point3D e[2],int& vFlag); 86 | template 87 | Point3D NearestPointOnTriangle(const Point3D& p,const Point3D t[3],int& vFlag); 88 | 89 | 90 | template 91 | int OutCode(const Point3D& ctr,const Real& w,const Point3D& p); 92 | 93 | template 94 | int PointInCube(const Point3D& ctr,const Real& w,const Point3D& p); 95 | template 96 | int EdgeInCube(const Point3D& ctr,const Real& w,const Point3D e[2]); 97 | template 98 | int TriangleInCube(const Point3D& ctr,const Real& w,const Point3D t[3]); 99 | 100 | class TriangleIndex{ 101 | public: 102 | size_t idx[3]; 103 | }; 104 | 105 | #include "Geometry.inl" 106 | 107 | #endif // GEOMETRY_INCLUDED 108 | -------------------------------------------------------------------------------- /SRC/Geometry.inl: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | Redistributions of source code must retain the above copyright notice, this list of 9 | conditions and the following disclaimer. Redistributions in binary form must reproduce 10 | the above copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the distribution. 12 | 13 | Neither the name of the Johns Hopkins University nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without specific 15 | prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 18 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES 19 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 20 | SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 21 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 22 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 23 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 26 | DAMAGE. 27 | */ 28 | ///////////// 29 | // Point3D // 30 | ///////////// 31 | #include 32 | 33 | template 34 | Real& Point3D::operator[] (const int& idx) 35 | { 36 | return coords[idx]; 37 | } 38 | template 39 | const Real& Point3D::operator[] (const int& idx) const 40 | { 41 | return coords[idx]; 42 | } 43 | template 44 | Point3D Point3D::operator + (const Point3D& p) const 45 | { 46 | Point3D q; 47 | q.coords[0]=coords[0]+p.coords[0]; 48 | q.coords[1]=coords[1]+p.coords[1]; 49 | q.coords[2]=coords[2]+p.coords[2]; 50 | return q; 51 | } 52 | template 53 | Point3D Point3D::operator - (const Point3D& p) const 54 | { 55 | Point3D q; 56 | q.coords[0]=coords[0]-p.coords[0]; 57 | q.coords[1]=coords[1]-p.coords[1]; 58 | q.coords[2]=coords[2]-p.coords[2]; 59 | return q; 60 | } 61 | template 62 | Point3D Point3D::operator * (const Real& s) const 63 | { 64 | Point3D q; 65 | q.coords[0]=coords[0]*s; 66 | q.coords[1]=coords[1]*s; 67 | q.coords[2]=coords[2]*s; 68 | return q; 69 | } 70 | template 71 | Point3D Point3D::operator / (const Real& s) const 72 | { 73 | Point3D q; 74 | q.coords[0]=coords[0]/s; 75 | q.coords[1]=coords[1]/s; 76 | q.coords[2]=coords[2]/s; 77 | return q; 78 | } 79 | template 80 | Point3D& Point3D::operator += (const Point3D& p) 81 | { 82 | coords[0]+=p.coords[0]; 83 | coords[1]+=p.coords[1]; 84 | coords[2]+=p.coords[2]; 85 | return *this; 86 | } 87 | template 88 | Point3D& Point3D::operator -= (const Point3D& p) 89 | { 90 | coords[0]-=p.coords[0]; 91 | coords[1]-=p.coords[1]; 92 | coords[2]-=p.coords[2]; 93 | return *this; 94 | } 95 | template 96 | Point3D& Point3D::operator *= (const Real& s) 97 | { 98 | coords[0]*=s; 99 | coords[1]*=s; 100 | coords[2]*=s; 101 | return *this; 102 | } 103 | template 104 | Point3D& Point3D::operator /= (const Real& s) 105 | { 106 | coords[0]/=s; 107 | coords[1]/=s; 108 | coords[2]/=s; 109 | return *this; 110 | } 111 | template 112 | Real Random(void){return Real(rand())/RAND_MAX;} 113 | 114 | template 115 | Point3D RandomBallPoint(void){ 116 | Point3D p; 117 | while(1){ 118 | p.coords[0]=Real(1.0-2.0*Random()); 119 | p.coords[1]=Real(1.0-2.0*Random()); 120 | p.coords[2]=Real(1.0-2.0*Random()); 121 | Real l=SquareLength(p); 122 | if(l<=1){return p;} 123 | } 124 | } 125 | template 126 | Point3D RandomSpherePoint(void){ 127 | Point3D p=RandomBallPoint(); 128 | return p/Length(p); 129 | } 130 | 131 | template 132 | Real SquareLength(const Point3D& p) 133 | { 134 | return DotProduct(p,p); 135 | } 136 | 137 | template 138 | Real DotProduct(const Point3D& p,const Point3D& q) 139 | { 140 | return p.coords[0]*q.coords[0]+p.coords[1]*q.coords[1]+p.coords[2]*q.coords[2]; 141 | } 142 | 143 | template 144 | Real Length(const Point3D& p) 145 | { 146 | return Real(sqrt(SquareLength(p))); 147 | } 148 | 149 | template 150 | Real SquareDistance(const Point3D& p1,const Point3D& p2) 151 | { 152 | return SquareLength(p1-p2); 153 | } 154 | 155 | template 156 | Real Distance(const Point3D& p1,const Point3D& p2){return Real(sqrt(SquareDistance(p1,p2)));} 157 | 158 | template 159 | void CrossProduct(const Point3D& p1,const Point3D& p2,Point3D& p){ 160 | p.coords[0]= p1.coords[1]*p2.coords[2]-p1.coords[2]*p2.coords[1]; 161 | p.coords[1]=-p1.coords[0]*p2.coords[2]+p1.coords[2]*p2.coords[0]; 162 | p.coords[2]= p1.coords[0]*p2.coords[1]-p1.coords[1]*p2.coords[0]; 163 | } 164 | template 165 | Point3D Normal(const Point3D& p1,const Point3D& p2,const Point3D& p3) 166 | { 167 | Point3D q1,q2,n; 168 | q1=p2-p1; 169 | q2=p3-p1; 170 | CrossProduct(q1,q2,n); 171 | return n; 172 | } 173 | 174 | template 175 | Real DistanceToEdge(const Point3D& p,const Point3D e[2]) 176 | { 177 | Point3D q,v; 178 | Real dot; 179 | q=p-e[0]; 180 | v=e[1]-e[0]; 181 | dot=DotProduct(q,v); 182 | if(dot<=0) 183 | return Length(q); 184 | else if (dot>SquareLength(v)) 185 | return Distance(p,e[1]); 186 | else 187 | { 188 | Real t=dot/SquareLength(v); 189 | v=e[0]*(Real(1.0)-t)+e[1]*t; 190 | return Distance(p,v); 191 | } 192 | } 193 | template 194 | Real DistanceToTriangle(const Point3D& p,const Point3D t[3]) 195 | { 196 | Point3D e[2]; 197 | Point3D q,v,n,nn; 198 | 199 | n=Normal(t[0],t[1],t[2]); 200 | for(int i=0;i<3;i++) 201 | { 202 | v=t[(i+1)%3]-t[i]; 203 | q=p-t[i]; 204 | CrossProduct(n,v,nn); 205 | if(DotProduct(q,nn)<=0) 206 | { 207 | e[0]=t[i]; 208 | e[1]=t[(i+1)%3]; 209 | return DistanceToEdge(p,e); 210 | } 211 | } 212 | return fabs(DotProduct(q,n))/Length(n); 213 | } 214 | template 215 | Point3D NearestPointOnEdge(const Point3D& p,const Point3D e[2],int& vFlag) 216 | { 217 | Point3D q,v; 218 | Real dot; 219 | 220 | q=p-e[0]; 221 | v=e[1]-e[0]; 222 | 223 | dot=DotProduct(q,v); 224 | if(dot<=0) 225 | { 226 | vFlag=1; 227 | return e[0]; 228 | } 229 | else if (dot>=SquareLength(v)) 230 | { 231 | vFlag=2; 232 | return e[1]; 233 | } 234 | else 235 | { 236 | Real t=dot/Real(SquareLength(v)); 237 | v=e[0]*(Real(1.0)-t)+e[1]*t; 238 | vFlag=3; 239 | return v; 240 | } 241 | } 242 | template 243 | Point3D NearestPointOnTriangle(const Point3D& p,const Point3D t[3],int& vFlag) 244 | { 245 | Point3D e[2]; 246 | Point3D q,v,n,nn,nearest; 247 | vFlag=0; 248 | 249 | n=Normal(t[0],t[1],t[2]); 250 | for(int i=0;i<3;i++) 251 | { 252 | v=t[(i+1)%3]-t[i]; 253 | q=p-t[i]; 254 | 255 | CrossProduct(n,v,nn); 256 | if(DotProduct(q,nn)<=0) 257 | { 258 | int tempFlag; 259 | e[0]=t[i]; 260 | e[1]=t[(i+1)%3]; 261 | nearest=NearestPointOnEdge(p,e,tempFlag); 262 | if(tempFlag&1) vFlag|=1< 275 | int OutCode(const Point3D& ctr,const Real& w,const Point3D& p) 276 | { 277 | int oc=0; 278 | if(p.coords[0]ctr.coords[0]+w/2) 281 | oc|=2; 282 | if(p.coords[1]ctr.coords[1]+w/2) 285 | oc|=8; 286 | if(p.coords[2]ctr.coords[2]+w/2) 289 | oc|=32; 290 | return oc; 291 | } 292 | template 293 | int PointInCube(const Point3D& ctr,const Real& w,const Point3D& p) 294 | { 295 | return !OutCode(ctr,w,p); 296 | } 297 | template 298 | int EdgeInCube(const Point3D& ctr,const Real& w,const Point3D e[2]) 299 | { 300 | int oc[2],dir,off; 301 | Real t,x; 302 | oc[0]=OutCode(ctr,w,e[0]); 303 | oc[1]=OutCode(ctr,w,e[1]); 304 | if(!oc[0] || !oc[1]) return 1; 305 | if(oc[0] & oc[1]) return 0; 306 | #if 1 307 | for(dir=0;dir<3;dir++) 308 | if((oc[0]>>(dir<<1))&3) 309 | { 310 | off=( (oc[0]>>(dir<<1))&2) >> 1; 311 | t=( e[0][dir]-(ctr[dir] - w/2 + w*off) ) / (e[0][dir]-e[1][dir]); 312 | int inside=0; 313 | for(int i=1;i<3;i++) 314 | { 315 | int j=(dir+i)%3; 316 | x=e[0][j]*(Real(1.0)-t)+e[1][j]*t; 317 | if(x>=(ctr[j]-w/2) && x<=(ctr[j]+w/2)) 318 | inside++; 319 | } 320 | if(inside==2) 321 | return 1; 322 | } 323 | return 0; 324 | #else 325 | for(dir=0;dir<3;dir++) 326 | if((oc[0]>>(dir<<1))&3) 327 | break; 328 | off=( (oc[0]>>(dir<<1))&2) >> 1; 329 | t=( e[0][dir]-(ctr[dir] -w/2 + w*off) ) / (e[0][dir]-e[1][dir]); 330 | for(int i=1;i<3;i++) 331 | { 332 | int j=(dir+i)%3; 333 | x=e[0][j]*(Real(1.0)-t)+e[1][j]*t; 334 | if(x<(ctr[j]-w/2) || x>(ctr[j]+w/2)) 335 | return 0; 336 | } 337 | return 1; 338 | #endif 339 | } 340 | template 341 | int TriangleInCube(const Point3D& ctr,const Real& w,const Point3D t[3]) 342 | { 343 | Point3D e[2],n,nn[3]; 344 | int oc[3]; 345 | oc[0]=OutCode(ctr,w,t[0]); 346 | oc[1]=OutCode(ctr,w,t[1]); 347 | oc[2]=OutCode(ctr,w,t[2]); 348 | if(!oc[0] || !oc[1] || !oc[2]) 349 | return 1; 350 | if(oc[0] & oc[1] & oc[2]) 351 | return 0; 352 | 353 | for(int i=0;i<3;i++) 354 | { 355 | e[0]=t[i]; 356 | e[1]=t[(i+1)%3]; 357 | if(EdgeInCube(ctr,w,e)) 358 | return 1; 359 | } 360 | 361 | n=Normal(t[0],t[1],t[2]); 362 | for(int i=0;i<3;i++) 363 | CrossProduct(n,t[(i+1)%3]-t[i],nn[i]); 364 | 365 | for(int i=0;i p; 370 | Cube::EdgeCorners(i,c1,c2); 371 | Cube::FactorCornerIndex(c1,x[0],x[1],x[2]); 372 | for(int j=0;j<3;j++) e[0][j]=ctr[j]-w/2+w*x[j]; 373 | 374 | Cube::FactorCornerIndex(c2,x[0],x[1],x[2]); 375 | for(int j=0;j<3;j++) e[1][j]=ctr[j]-w/2+w*x[j]; 376 | 377 | dot[0]=DotProduct(n,e[0]-t[0]); 378 | dot[1]=DotProduct(n,e[1]-t[0]); 379 | if(dot[0]*dot[1] >=0 ) continue; 380 | tt=dot[0]/(dot[0]-dot[1]); 381 | p=e[0]*(Real(1.0)-tt)+e[1]*tt; 382 | if(DotProduct(p-t[0],nn[0])>0 && DotProduct(p-t[1],nn[1])>0 && DotProduct(p-t[2],nn[2])>0 ) 383 | return 1; 384 | } 385 | return 0; 386 | } 387 | 388 | 389 | -------------------------------------------------------------------------------- /SRC/IsoOctree.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2007, Michael Kazhdan 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | Redistributions of source code must retain the above copyright notice, this list of 9 | conditions and the following disclaimer. Redistributions in binary form must reproduce 10 | the above copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the distribution. 12 | 13 | Neither the name of the Johns Hopkins University nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without specific 15 | prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 18 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES 19 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 20 | SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 21 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 22 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 23 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 26 | DAMAGE. 27 | */ 28 | #ifndef ISO_OCTREE_INCLUDED 29 | #define ISO_OCTREE_INCLUDED 30 | 31 | #include 32 | #include "MarchingCubes.h" 33 | #include "Octree.h" 34 | #include "../include/IsoOctree/IsoOctree.hpp" 35 | 36 | class EdgeKey 37 | { 38 | public: 39 | size_t key1,key2; 40 | EdgeKey(void){;} 41 | EdgeKey(const size_t& n1,const size_t &n2); 42 | 43 | EdgeKey& operator = (const EdgeKey& key); 44 | operator size_t () const; 45 | operator size_t (); 46 | bool operator < (const EdgeKey& key) const; 47 | 48 | bool operator != (const EdgeKey& key) const; 49 | }; 50 | 51 | namespace std { 52 | template <> struct hash { 53 | std::size_t operator()(const EdgeKey& k) const { 54 | // TODO: custom bitshifts -> remove 55 | return std::hash()(k.key1) ^ (std::hash()(k.key2) << 17); 56 | } 57 | }; 58 | } 59 | 60 | template 61 | class NeighborKey : public OctNode::NeighborKey 62 | { 63 | void __GetCornerNeighbors(OctNode* node,const int& depth,const int& c,OctNode* neighbors[Cube::CORNERS]); 64 | OctNode* __FaceNeighbor(OctNode* node,const int& depth,int dir,int off); 65 | OctNode* __EdgeNeighbor(OctNode* node,const int& depth,int o,int i1,int i2); 66 | OctNode* __CornerNeighbor(OctNode* node,const int& depth,int x,int y,int z); 67 | public: 68 | void GetCornerNeighbors(OctNode* node,const int& c,OctNode* neighbors[Cube::CORNERS]); 69 | OctNode* FaceNeighbor(OctNode* node,int dir,int off); 70 | OctNode* EdgeNeighbor(OctNode* node,int o,int i1,int i2); 71 | OctNode* CornerNeighbor(OctNode* node,int x,int y,int z); 72 | 73 | static void CornerIndex(const int& c,int idx[3]); 74 | static void EdgeIndex(const int& c,int idx[3]); 75 | static void FaceIndex(const int& c,int idx[3]); 76 | }; 77 | 78 | template 79 | class IsoOctree 80 | { 81 | class TriangleIndex 82 | { 83 | public: 84 | int idx[3]; 85 | }; 86 | class RootInfo 87 | { 88 | public: 89 | const OctNode* node; 90 | int edgeIndex; 91 | long long key; 92 | typename OctNode::NodeIndex nIdx; 93 | }; 94 | template 95 | class MeshInfo 96 | { 97 | public: 98 | std::vector > vertexNormals; 99 | stdext::hash_map > edgeNormals; 100 | std::vector > triangleNormals; 101 | std::vector triangles; 102 | std::vector > vertices; 103 | 104 | template 105 | void set(const std::vector& vertices,const std::vector >& polygons,const Real& width, 106 | Point3D& translate,Real& scale,const int& noTransform); 107 | }; 108 | 109 | template 110 | void getRoots(OctNode* node,const typename OctNode::NodeIndex& nIdx,const Real& isoValue,stdext::hash_map& roots,std::vector& vertices); 111 | int getRootIndex(OctNode* node,const typename OctNode::NodeIndex& nIdx,const int& edgeIndex,RootInfo& ri); 112 | int getRootPosition(const OctNode* node,const typename OctNode::NodeIndex& nIdx,const int& eIndex,const Real& isoValue,Point3D& position); 113 | long long getRootKey(const typename OctNode::NodeIndex& nIdx,const int& edgeIndex); 114 | 115 | int getRootPair(const RootInfo& root,const int& maxDepth,RootInfo& pair); 116 | void getIsoFaceEdges(OctNode* node,const typename OctNode::NodeIndex& nIdx,const int& faceIndex,std::vector>& edges,const int& flip,const int& useFull); 117 | void getIsoPolygons(OctNode* node,const typename OctNode::NodeIndex& nIdx,stdext::hash_map& roots,std::vector>& polygons,const int& useFull); 118 | 119 | template 120 | void getEdgeLoops(std::vector >& edges,stdext::hash_map& roots,std::vector>& polygons); 121 | 122 | template 123 | void getEdgeLoops(std::vector >& edges,std::vector>& loops); 124 | 125 | template 126 | void setDistanceAndNormal(const std::vector& triangles,MeshInfo& mInfo,const Point3D& p, Real& v,Point3D& n); 127 | template 128 | void setChildren(OctNode* node,const typename OctNode::NodeIndex& nIdx, 129 | const std::vector& triangles,MeshInfo& mInfo,const int& maxDepth,const int& setCenter,const Real& flatness,stdext::hash_map*>* triangleMap=NULL); 130 | 131 | void setChildren( 132 | OctNode* node, 133 | const typename OctNode::NodeIndex& nIdx, 134 | typename isoOctree::Octree::Traverser &traverser); 135 | 136 | // Assumes NodeData::mcIndex 137 | void setMCIndex(const Real& isoValue,const int& useFull); 138 | 139 | NeighborKey nKey; 140 | public: 141 | // The maximum depth of the tree. This value must be at least as large as the true depth of the tree 142 | // as its used for assigning unique ids. (It can, however, be larger than the depth for uniqueness to still hold.) 143 | int maxDepth; 144 | // The octree itself 145 | OctNode tree; 146 | // A hash-table of data associated to the corners of the octree nodes 147 | stdext::hash_map cornerValues; 148 | 149 | // Sets an octree from a polygon mesh, generating an octree that is fully refined around the surface 150 | template 151 | int set(const std::vector& vertices,const std::vector >& polygons,const int& maxDepth,const int& setCenter,const Real& flatness,const int& noTransform); 152 | template 153 | int set(const std::vector& vertices,const std::vector >& polygons,const int& maxDepth,const int& setCenter,const Real& flatness,Point3D& translate,Real& scale,const int& noTransform); 154 | // Sets an octree from a polygon mesh, generating an octree that is fully refined around the surface 155 | template 156 | int setConforming(const std::vector& vertices,const std::vector >& polygons,const int& maxDepth,const int& setCenter,const Real& flatness,const int& noTransform); 157 | template 158 | int setConforming(const std::vector& vertices,const std::vector >& polygons,const int& maxDepth,const int& setCenter,const Real& flatness,Point3D& translate,Real& scale,const int& noTransform); 159 | 160 | bool set(typename isoOctree::Octree::Traverser &traverser); 161 | bool set(typename isoOctree::Octree::VectorizedTraverser &traverser); 162 | 163 | // A clean-up method to remove un-needed entries in the cornerValues hash-table 164 | void resetValues(void); 165 | // Reads the octree from a file pointer 166 | int read(FILE* fp,int readData); 167 | // Writes the octree to a file pointer 168 | int write(FILE* fp,int writeData) const; 169 | 170 | void interpolateSharedValues(void); 171 | 172 | // Extracts an iso-surface from the octree 173 | template 174 | void getIsoSurface(const Real& isoValue,std::vector& vertices,std::vector>& polygons,const int& useFull); 175 | template 176 | void getIsoSoup(const Real& isoValue,std::vector& vertices,std::vector>& polygons,const int& useFull); 177 | template 178 | void getDualIsoSurface(const Real& isoValue,std::vector& vertices,std::vector>& polygons,const int& useFull); 179 | // Generates a hash-table indexed by octree node, with each entry containing two pieces of data. The first is 180 | // mean-curvature vector associated to the intersection of the iso-surface with the node. The second is the area 181 | // of the intersection of the iso-surface with the node. 182 | void setNormalFlatness(const Real& isoValue,stdext::hash_map,Real>>& curvature); 183 | 184 | // A method for determing if a node has grand-children along an edge 185 | static int HasEdgeGrandChildren(const OctNode* node,int eIndex); 186 | // A method for determing if a node has grand-children along a face 187 | static int HasFaceGrandChildren(const OctNode* node,int fIndex); 188 | }; 189 | 190 | #include "IsoOctree.inl" 191 | 192 | #endif // ISO_OCTREE_INCLUDED 193 | -------------------------------------------------------------------------------- /SRC/MAT.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2007, Michael Kazhdan 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | Redistributions of source code must retain the above copyright notice, this list of 9 | conditions and the following disclaimer. Redistributions in binary form must reproduce 10 | the above copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the distribution. 12 | 13 | Neither the name of the Johns Hopkins University nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without specific 15 | prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 18 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES 19 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 20 | SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 21 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 22 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 23 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 26 | DAMAGE. 27 | */ 28 | #ifndef MAT_INCLUDED 29 | #define MAT_INCLUDED 30 | #include "Geometry.h" 31 | 32 | template 33 | class MinimalAreaTriangulation 34 | { 35 | Real* bestTriangulation; 36 | int* midPoint; 37 | Real GetArea(const size_t& i,const size_t& j,const std::vector >& vertices); 38 | void GetTriangulation(const size_t& i,const size_t& j,const std::vector >& vertices,std::vector& triangles); 39 | public: 40 | MinimalAreaTriangulation(void); 41 | ~MinimalAreaTriangulation(void); 42 | Real GetArea(const std::vector >& vertices); 43 | void GetTriangulation(const std::vector >& vertices,std::vector& triangles); 44 | }; 45 | 46 | #include "MAT.inl" 47 | 48 | #endif // MAT_INCLUDED 49 | -------------------------------------------------------------------------------- /SRC/MAT.inl: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2007, Michael Kazhdan 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | Redistributions of source code must retain the above copyright notice, this list of 9 | conditions and the following disclaimer. Redistributions in binary form must reproduce 10 | the above copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the distribution. 12 | 13 | Neither the name of the Johns Hopkins University nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without specific 15 | prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 18 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES 19 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 20 | SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 21 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 22 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 23 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 26 | DAMAGE. 27 | */ 28 | ////////////////////////////// 29 | // MinimalAreaTriangulation // 30 | ////////////////////////////// 31 | #include 32 | 33 | template 34 | MinimalAreaTriangulation::MinimalAreaTriangulation(void) 35 | { 36 | bestTriangulation=NULL; 37 | midPoint=NULL; 38 | } 39 | template 40 | MinimalAreaTriangulation::~MinimalAreaTriangulation(void) 41 | { 42 | if(bestTriangulation) 43 | delete[] bestTriangulation; 44 | bestTriangulation=NULL; 45 | if(midPoint) 46 | delete[] midPoint; 47 | midPoint=NULL; 48 | } 49 | template 50 | void MinimalAreaTriangulation::GetTriangulation(const std::vector >& vertices,std::vector& triangles) 51 | { 52 | if(vertices.size()==3) 53 | { 54 | triangles.resize(1); 55 | triangles[0].idx[0]=0; 56 | triangles[0].idx[1]=1; 57 | triangles[0].idx[2]=2; 58 | return; 59 | } 60 | else if(vertices.size()==4) 61 | { 62 | TriangleIndex tIndex[2][2]; 63 | Real area[2]; 64 | 65 | area[0]=area[1]=0; 66 | triangles.resize(2); 67 | 68 | tIndex[0][0].idx[0]=0; 69 | tIndex[0][0].idx[1]=1; 70 | tIndex[0][0].idx[2]=2; 71 | tIndex[0][1].idx[0]=2; 72 | tIndex[0][1].idx[1]=3; 73 | tIndex[0][1].idx[2]=0; 74 | 75 | tIndex[1][0].idx[0]=0; 76 | tIndex[1][0].idx[1]=1; 77 | tIndex[1][0].idx[2]=3; 78 | tIndex[1][1].idx[0]=3; 79 | tIndex[1][1].idx[1]=1; 80 | tIndex[1][1].idx[2]=2; 81 | 82 | Point3D n,p1,p2; 83 | for(int i=0;i<2;i++) 84 | for(int j=0;j<2;j++) 85 | { 86 | p1=vertices[tIndex[i][j].idx[1]]-vertices[tIndex[i][j].idx[0]]; 87 | p2=vertices[tIndex[i][j].idx[2]]-vertices[tIndex[i][j].idx[0]]; 88 | CrossProduct(p1,p2,n); 89 | area[i]+=Length(n); 90 | } 91 | if(area[0]>area[1]) 92 | { 93 | triangles[0]=tIndex[1][0]; 94 | triangles[1]=tIndex[1][1]; 95 | } 96 | else 97 | { 98 | triangles[0]=tIndex[0][0]; 99 | triangles[1]=tIndex[0][1]; 100 | } 101 | return; 102 | } 103 | if(bestTriangulation) 104 | delete[] bestTriangulation; 105 | if(midPoint) 106 | delete[] midPoint; 107 | bestTriangulation=NULL; 108 | midPoint=NULL; 109 | size_t eCount=vertices.size(); 110 | bestTriangulation=new Real[eCount*eCount]; 111 | midPoint=new int[eCount*eCount]; 112 | for(size_t i=0;i 120 | Real MinimalAreaTriangulation::GetArea(const std::vector >& vertices) 121 | { 122 | if(bestTriangulation) 123 | delete[] bestTriangulation; 124 | if(midPoint) 125 | delete[] midPoint; 126 | bestTriangulation=NULL; 127 | midPoint=NULL; 128 | int eCount=vertices.size(); 129 | bestTriangulation=new double[eCount*eCount]; 130 | midPoint=new int[eCount*eCount]; 131 | for(int i=0;i 137 | void MinimalAreaTriangulation::GetTriangulation(const size_t& i,const size_t& j,const std::vector >& vertices,std::vector& triangles) 138 | { 139 | TriangleIndex tIndex; 140 | size_t eCount=vertices.size(); 141 | size_t ii=i; 142 | if(i=ii) 145 | return; 146 | ii=midPoint[i*eCount+j]; 147 | if(ii>=0) 148 | { 149 | tIndex.idx[0]=i; 150 | tIndex.idx[1]=j; 151 | tIndex.idx[2]=ii; 152 | triangles.push_back(tIndex); 153 | GetTriangulation(i,ii,vertices,triangles); 154 | GetTriangulation(ii,j,vertices,triangles); 155 | } 156 | } 157 | 158 | template 159 | Real MinimalAreaTriangulation::GetArea(const size_t& i,const size_t& j,const std::vector >& vertices) 160 | { 161 | Real a=FLT_MAX,temp; 162 | size_t eCount=vertices.size(); 163 | size_t idx=i*eCount+j; 164 | size_t ii=i; 165 | if(i=ii) 168 | { 169 | bestTriangulation[idx]=0; 170 | return 0; 171 | } 172 | if(midPoint[idx]!=-1) 173 | return bestTriangulation[idx]; 174 | int mid=-1; 175 | for(size_t r=j+1;r p,p1,p2; 180 | p1=vertices[i]-vertices[rr]; 181 | p2=vertices[j]-vertices[rr]; 182 | CrossProduct(p1,p2,p); 183 | temp=Length(p); 184 | if(bestTriangulation[idx1]>=0) 185 | { 186 | temp+=bestTriangulation[idx1]; 187 | if(temp>a) 188 | continue; 189 | if(bestTriangulation[idx2]>0) 190 | temp+=bestTriangulation[idx2]; 191 | else 192 | temp+=GetArea(rr,j,vertices); 193 | } 194 | else 195 | { 196 | if(bestTriangulation[idx2]>=0) 197 | temp+=bestTriangulation[idx2]; 198 | else 199 | temp+=GetArea(rr,j,vertices); 200 | if(temp>a) 201 | continue; 202 | temp+=GetArea(i,rr,vertices); 203 | } 204 | 205 | if(temp 31 | 32 | class Square 33 | { 34 | public: 35 | const static int CORNERS=4,EDGES=4; 36 | static int CornerIndex (const int& x,const int& y); 37 | static void FactorCornerIndex (const int& idx,int& x,int& y); 38 | static int EdgeIndex (const int& orientation,const int& i); 39 | static void FactorEdgeIndex (const int& idx,int& orientation,int& i); 40 | 41 | static int ReflectCornerIndex (const int& idx,const int& edgeIndex); 42 | static int ReflectEdgeIndex (const int& idx,const int& edgeIndex); 43 | 44 | static void EdgeCorners(const int& idx,int& c1,int &c2); 45 | static void OrientedEdgeCorners(const int& idx,int& c1,int &c2); 46 | }; 47 | 48 | class Cube 49 | { 50 | public: 51 | const static int CORNERS=8,EDGES=12,FACES=6; 52 | 53 | static int CornerIndex (const int& x,const int& y,const int& z); 54 | static void FactorCornerIndex (const int& idx,int& x,int& y,int& z); 55 | static int EdgeIndex (const int& orientation,const int& i,const int& j); 56 | static void FactorEdgeIndex (const int& idx,int& orientation,int& i,int &j); 57 | static int FaceIndex (const int& dir,const int& offSet); 58 | static int FaceIndex (const int& x,const int& y,const int& z); 59 | static void FactorFaceIndex (const int& idx,int& x,int &y,int& z); 60 | static void FactorFaceIndex (const int& idx,int& dir,int& offSet); 61 | 62 | static int AntipodalCornerIndex (const int& idx); 63 | static int FaceReflectCornerIndex (const int& idx,const int& faceIndex); 64 | static int FaceReflectEdgeIndex (const int& idx,const int& faceIndex); 65 | static int FaceReflectFaceIndex (const int& idx,const int& faceIndex); 66 | static int EdgeReflectCornerIndex (const int& idx,const int& edgeIndex); 67 | static int EdgeReflectEdgeIndex (const int& edgeIndex); 68 | 69 | static int FaceAdjacentToEdges (const int& eIndex1,const int& eIndex2); 70 | static void FacesAdjacentToEdge (const int& eIndex,int& f1Index,int& f2Index); 71 | 72 | static void EdgeCorners(const int& idx,int& c1,int &c2); 73 | static void FaceCorners(const int& idx,int& c1,int &c2,int& c3,int& c4); 74 | 75 | static int SquareToCubeCorner(const int& fIndex,const int& cIndex); 76 | static int SquareToCubeEdge(const int& fIndex,const int& eIndex); 77 | }; 78 | 79 | class MarchingSquares 80 | { 81 | public: 82 | class FaceEdges 83 | { 84 | public: 85 | int count; 86 | std::pair edge[2]; 87 | }; 88 | private: 89 | static FaceEdges __caseTable [1<<(Square::CORNERS )]; 90 | static FaceEdges __fullCaseTable[1<<(Square::CORNERS+1)]; 91 | public: 92 | static void SetCaseTable(void); 93 | static void SetFullCaseTable(void); 94 | 95 | static const FaceEdges& caseTable(const int& idx); 96 | static const FaceEdges& fullCaseTable(const int& idx); 97 | template 98 | static int GetFullIndex(const Real values[Square::CORNERS],const Real& iso); 99 | template 100 | static int GetIndex(const Real values[Square::CORNERS],const Real& iso); 101 | }; 102 | 103 | class MarchingCubes 104 | { 105 | static void GetEdgeLoops(std::vector >& edges,std::vector>& loops); 106 | static std::vector< std::vector > __caseTable[1< > > __fullCaseTable; 109 | public: 110 | static void SetCaseTable(void); 111 | static void SetFullCaseTable(void); 112 | 113 | template 114 | static int GetFullIndex(const Real values[Cube::CORNERS],const Real& iso); 115 | template 116 | static int GetIndex(const Real values[Cube::CORNERS],const Real& iso); 117 | static const std::vector< std::vector >& caseTable(const int& idx); 118 | static const std::vector< std::vector >& fullCaseTable(const int& idx); 119 | static const std::vector< std::vector >& caseTable(const int& idx,const int& useFull); 120 | 121 | static int IsAmbiguous(const int& idx); 122 | static int IsAmbiguous(const int& idx,const int& f); 123 | static int HasRoots(const int& mcIndex); 124 | static int HasEdgeRoots(const int& mcIndex,const int& edgeIndex); 125 | }; 126 | 127 | #ifdef MARCHING_CUBES_IMPL 128 | #include "MarchingCubes.inl" 129 | #endif 130 | #endif //MARCHING_CUBES_INCLUDED 131 | -------------------------------------------------------------------------------- /SRC/MarchingCubes.inl: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2007, Michael Kazhdan 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | Redistributions of source code must retain the above copyright notice, this list of 9 | conditions and the following disclaimer. Redistributions in binary form must reproduce 10 | the above copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the distribution. 12 | 13 | Neither the name of the Johns Hopkins University nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without specific 15 | prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 18 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES 19 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 20 | SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 21 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 22 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 23 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 26 | DAMAGE. 27 | */ 28 | #include 29 | using std::memset; 30 | 31 | //////////// 32 | // Square // 33 | //////////// 34 | int Square::CornerIndex(const int& x,const int& y){return (y<<1)|x;} 35 | void Square::FactorCornerIndex(const int& idx,int& x,int& y){ 36 | x=(idx>>0)%2; 37 | y=(idx>>1)%2; 38 | } 39 | int Square::EdgeIndex(const int& orientation,const int& i){ 40 | switch(orientation){ 41 | case 0: // x 42 | if(!i) {return 0;} // (0,0) -> (1,0) 43 | else {return 2;} // (0,1) -> (1,1) 44 | case 1: // y 45 | if(!i) {return 3;} // (0,0) -> (0,1) 46 | else {return 1;} // (1,0) -> (1,1) 47 | }; 48 | return -1; 49 | } 50 | void Square::FactorEdgeIndex(const int& idx,int& orientation,int& i){ 51 | switch(idx){ 52 | case 0: case 2: 53 | orientation=0; 54 | i=idx/2; 55 | return; 56 | case 1: case 3: 57 | orientation=1; 58 | i=((idx/2)+1)%2; 59 | return; 60 | }; 61 | } 62 | void Square::EdgeCorners(const int& idx,int& c1,int& c2){ 63 | int orientation,i; 64 | FactorEdgeIndex(idx,orientation,i); 65 | switch(orientation){ 66 | case 0: 67 | c1=CornerIndex(0,i); 68 | c2=CornerIndex(1,i); 69 | break; 70 | case 1: 71 | c1=CornerIndex(i,0); 72 | c2=CornerIndex(i,1); 73 | break; 74 | }; 75 | } 76 | void Square::OrientedEdgeCorners(const int& idx,int& c1,int& c2){ 77 | int orientation,i; 78 | FactorEdgeIndex(idx,orientation,i); 79 | switch(orientation){ 80 | case 0: 81 | c1=CornerIndex((i )&1,i); 82 | c2=CornerIndex((i+1)&1,i); 83 | break; 84 | case 1: 85 | c1=CornerIndex(i,(i+1)&1); 86 | c2=CornerIndex(i,(i )&1); 87 | break; 88 | }; 89 | } 90 | int Square::ReflectEdgeIndex(const int& idx,const int& edgeIndex){ 91 | int orientation=edgeIndex%2; 92 | int o,i; 93 | FactorEdgeIndex(idx,o,i); 94 | if(o!=orientation){return idx;} 95 | else{return EdgeIndex(o,(i+1)%2);} 96 | } 97 | int Square::ReflectCornerIndex(const int& idx,const int& edgeIndex){ 98 | int orientation=edgeIndex%2; 99 | int x,y; 100 | FactorCornerIndex(idx,x,y); 101 | switch(orientation){ 102 | case 0: return CornerIndex((x+1)%2,y); 103 | case 1: return CornerIndex(x,(y+1)%2); 104 | }; 105 | return -1; 106 | } 107 | 108 | 109 | 110 | ////////// 111 | // Cube // 112 | ////////// 113 | int Cube::CornerIndex(const int& x,const int& y,const int& z){return (z<<2)|(y<<1)|x;} 114 | void Cube::FactorCornerIndex(const int& idx,int& x,int& y,int& z){ 115 | x=(idx>>0)%2; 116 | y=(idx>>1)%2; 117 | z=(idx>>2)%2; 118 | } 119 | int Cube::EdgeIndex(const int& orientation,const int& i,const int& j){return (i | (j<<1))|(orientation<<2);} 120 | void Cube::FactorEdgeIndex(const int& idx,int& orientation,int& i,int &j){ 121 | orientation=idx>>2; 122 | i=idx&1; 123 | j=(idx&2)>>1; 124 | } 125 | int Cube::FaceIndex(const int& x,const int& y,const int& z){ 126 | if (x<0) {return 0;} 127 | else if (x>0) {return 1;} 128 | else if (y<0) {return 2;} 129 | else if (y>0) {return 3;} 130 | else if (z<0) {return 4;} 131 | else if (z>0) {return 5;} 132 | else {return -1;} 133 | } 134 | int Cube::FaceIndex(const int& dir,const int& offSet){return (dir<<1)|offSet;} 135 | 136 | void Cube::FactorFaceIndex(const int& idx,int& x,int& y,int& z){ 137 | x=y=z=0; 138 | switch(idx){ 139 | case 0: x=-1; break; 140 | case 1: x= 1; break; 141 | case 2: y=-1; break; 142 | case 3: y= 1; break; 143 | case 4: z=-1; break; 144 | case 5: z= 1; break; 145 | }; 146 | } 147 | void Cube::FactorFaceIndex(const int& idx,int& dir,int& offSet){ 148 | dir = idx>>1; 149 | offSet=idx &1; 150 | } 151 | 152 | int Cube::FaceAdjacentToEdges(const int& eIndex1,const int& eIndex2){ 153 | int f1,f2,g1,g2; 154 | FacesAdjacentToEdge(eIndex1,f1,f2); 155 | FacesAdjacentToEdge(eIndex2,g1,g2); 156 | if(f1==g1 || f1==g2){return f1;} 157 | if(f2==g1 || f2==g2){return f2;} 158 | return -1; 159 | } 160 | 161 | void Cube::FacesAdjacentToEdge(const int& eIndex,int& f1Index,int& f2Index){ 162 | int orientation,i1,i2; 163 | FactorEdgeIndex(eIndex,orientation,i1,i2); 164 | i1<<=1; 165 | i2<<=1; 166 | i1--; 167 | i2--; 168 | switch(orientation){ 169 | case 0: 170 | f1Index=FaceIndex( 0,i1, 0); 171 | f2Index=FaceIndex( 0, 0,i2); 172 | break; 173 | case 1: 174 | f1Index=FaceIndex(i1, 0, 0); 175 | f2Index=FaceIndex( 0, 0,i2); 176 | break; 177 | case 2: 178 | f1Index=FaceIndex(i1, 0, 0); 179 | f2Index=FaceIndex( 0,i2, 0); 180 | break; 181 | }; 182 | } 183 | void Cube::EdgeCorners(const int& idx,int& c1,int& c2){ 184 | int orientation,i1,i2; 185 | FactorEdgeIndex(idx,orientation,i1,i2); 186 | switch(orientation){ 187 | case 0: 188 | c1=CornerIndex(0,i1,i2); 189 | c2=CornerIndex(1,i1,i2); 190 | break; 191 | case 1: 192 | c1=CornerIndex(i1,0,i2); 193 | c2=CornerIndex(i1,1,i2); 194 | break; 195 | case 2: 196 | c1=CornerIndex(i1,i2,0); 197 | c2=CornerIndex(i1,i2,1); 198 | break; 199 | }; 200 | } 201 | void Cube::FaceCorners(const int& idx,int& c1,int& c2,int& c3,int& c4){ 202 | int i=idx%2; 203 | switch(idx/2){ 204 | case 0: 205 | c1=CornerIndex(i,0,0); 206 | c2=CornerIndex(i,1,0); 207 | c3=CornerIndex(i,0,1); 208 | c4=CornerIndex(i,1,1); 209 | return; 210 | case 1: 211 | c1=CornerIndex(0,i,0); 212 | c2=CornerIndex(1,i,0); 213 | c3=CornerIndex(0,i,1); 214 | c4=CornerIndex(1,i,1); 215 | return; 216 | case 2: 217 | c1=CornerIndex(0,0,i); 218 | c2=CornerIndex(1,0,i); 219 | c3=CornerIndex(0,1,i); 220 | c4=CornerIndex(1,1,i); 221 | return; 222 | } 223 | } 224 | int Cube::AntipodalCornerIndex(const int& idx){ 225 | int x,y,z; 226 | FactorCornerIndex(idx,x,y,z); 227 | return CornerIndex((x+1)%2,(y+1)%2,(z+1)%2); 228 | } 229 | int Cube::FaceReflectFaceIndex(const int& idx,const int& faceIndex){ 230 | if(idx/2!=faceIndex/2){return idx;} 231 | else{ 232 | if(idx%2) {return idx-1;} 233 | else {return idx+1;} 234 | } 235 | } 236 | int Cube::FaceReflectEdgeIndex(const int& idx,const int& faceIndex){ 237 | int orientation=faceIndex/2; 238 | int o,i,j; 239 | FactorEdgeIndex(idx,o,i,j); 240 | if(o==orientation){return idx;} 241 | switch(orientation){ 242 | case 0: return EdgeIndex(o,(i+1)%2,j); 243 | case 1: 244 | switch(o){ 245 | case 0: return EdgeIndex(o,(i+1)%2,j); 246 | case 2: return EdgeIndex(o,i,(j+1)%2); 247 | }; 248 | case 2: return EdgeIndex(o,i,(j+1)%2); 249 | }; 250 | return -1; 251 | } 252 | int Cube::FaceReflectCornerIndex(const int& idx,const int& faceIndex){ 253 | int orientation=faceIndex/2; 254 | int x,y,z; 255 | FactorCornerIndex(idx,x,y,z); 256 | switch(orientation){ 257 | case 0: return CornerIndex((x+1)%2,y,z); 258 | case 1: return CornerIndex(x,(y+1)%2,z); 259 | case 2: return CornerIndex(x,y,(z+1)%2); 260 | }; 261 | return -1; 262 | } 263 | int Cube::EdgeReflectCornerIndex(const int& idx,const int& edgeIndex){ 264 | int orientation,x,y,z; 265 | FactorEdgeIndex(edgeIndex,orientation,x,y); 266 | FactorCornerIndex(idx,x,y,z); 267 | switch(orientation){ 268 | case 0: return CornerIndex( x ,(y+1)%2,(z+1)%2); 269 | case 1: return CornerIndex((x+1)%2, y ,(z+1)%2); 270 | case 2: return CornerIndex((x+1)%2,(y+1)%2, z ); 271 | }; 272 | return -1; 273 | } 274 | int Cube::EdgeReflectEdgeIndex(const int& edgeIndex){ 275 | int o,i1,i2; 276 | FactorEdgeIndex(edgeIndex,o,i1,i2); 277 | return Cube::EdgeIndex(o,(i1+1)%2,(i2+1)%2); 278 | } 279 | 280 | int Cube::SquareToCubeCorner(const int& fIndex,const int& cIndex) 281 | { 282 | // Assuming that the offset is 0, this returns corners in a consistent orientation 283 | int dir,off,i1,i2; 284 | FactorFaceIndex(fIndex,dir,off); 285 | Square::FactorCornerIndex(cIndex,i1,i2); 286 | switch(dir) 287 | { 288 | case 0: 289 | return CornerIndex(off,i1,i2); 290 | case 1: 291 | return CornerIndex(i1,off,(i2+1)&1); 292 | case 2: 293 | return CornerIndex(i1,i2,off); 294 | } 295 | return -1; 296 | } 297 | int Cube::SquareToCubeEdge(const int& fIndex,const int& eIndex) 298 | { 299 | // Assuming that the offset is 0, this returns corners in a consistent orientation 300 | int dir,off,o,i; 301 | FactorFaceIndex(fIndex,dir,off); 302 | Square::FactorEdgeIndex(eIndex,o,i); 303 | switch(dir) 304 | { 305 | case 0: 306 | if(o==0) 307 | return EdgeIndex(1,off,i); 308 | else if(o==1) 309 | return EdgeIndex(2,off,i); 310 | else 311 | return -1; 312 | case 1: 313 | if(o==0) 314 | return EdgeIndex(0,off,(i+1)&1); 315 | else if(o==1) 316 | return EdgeIndex(2,i,off); 317 | else 318 | return -1; 319 | case 2: 320 | if(o==0) 321 | return EdgeIndex(0,i,off); 322 | else if(o==1) 323 | return EdgeIndex(1,i,off); 324 | else 325 | return -1; 326 | } 327 | return -1; 328 | } 329 | 330 | ///////////////////// 331 | // MarchingSquares // 332 | ///////////////////// 333 | template 334 | int MarchingSquares::GetIndex(const Real v[Square::CORNERS],const Real& iso){ 335 | int idx=0; 336 | for(int i=0;i 340 | int MarchingSquares::GetFullIndex(const Real v[Square::CORNERS],const Real& iso) 341 | { 342 | int idx=0; 343 | Real sum=0; 344 | // Corner Index 345 | for(int i=0;i 421 | int MarchingCubes::GetIndex(const Real v[Cube::CORNERS],const Real& iso){ 422 | int idx=0; 423 | for(int i=0;i 429 | int MarchingCubes::GetFullIndex(const Real values[Cube::CORNERS],const Real& iso) 430 | { 431 | int idx=0; 432 | int c1,c2,c3,c4; 433 | for(int i=0;i >& edges,std::vector>& loops) 464 | { 465 | int loopSize=0; 466 | int idx; 467 | std::pair e,temp; 468 | loops.clear(); 469 | while(edges.size()) 470 | { 471 | e=edges[0]; 472 | loops.resize(loopSize+1); 473 | edges[0]=edges[edges.size()-1]; 474 | edges.pop_back(); 475 | loops[loopSize].push_back(e.first); 476 | idx=e.second; 477 | for(int j=int(edges.size())-1;j>=0;j--){ 478 | if(edges[j].first==idx || edges[j].second==idx){ 479 | if(edges[j].first==idx) {temp=edges[j];} 480 | else {temp.first=edges[j].second;temp.second=edges[j].first;} 481 | idx=temp.second; 482 | loops[loopSize].push_back(temp.first); 483 | edges[j]=edges[edges.size()-1]; 484 | edges.pop_back(); 485 | j=int(edges.size()); 486 | } 487 | } 488 | loopSize++; 489 | } 490 | } 491 | std::vector< std::vector > MarchingCubes::__caseTable[1< >& MarchingCubes::caseTable(const int& idx) 494 | { 495 | return __caseTable[idx]; 496 | } 497 | void MarchingCubes::SetCaseTable(void) 498 | { 499 | MarchingSquares::SetCaseTable(); 500 | int dir,off; 501 | for(int idx=0;idx<(1< > edges; 504 | for(int f=0;f(Cube::SquareToCubeEdge(f,MarchingSquares::caseTable(fIdx).edge[i].first),Cube::SquareToCubeEdge(f,MarchingSquares::caseTable(fIdx).edge[i].second))); 515 | else 516 | edges.push_back(std::pair(Cube::SquareToCubeEdge(f,MarchingSquares::caseTable(fIdx).edge[i].second),Cube::SquareToCubeEdge(f,MarchingSquares::caseTable(fIdx).edge[i].first))); 517 | } 518 | __caseTable[idx].clear(); 519 | GetEdgeLoops(edges,__caseTable[idx]); 520 | } 521 | } 522 | int MarchingCubes::__fullCaseMap[1<<(Cube::CORNERS+Cube::FACES)]; 523 | std::vector< std::vector< std::vector > > MarchingCubes::__fullCaseTable; 524 | 525 | const std::vector< std::vector >& MarchingCubes::caseTable(const int& idx,const int& useFull) 526 | { 527 | if(useFull) 528 | return __fullCaseTable[__fullCaseMap[idx] ]; 529 | else 530 | return __caseTable[idx]; 531 | } 532 | const std::vector< std::vector >& MarchingCubes::fullCaseTable(const int& idx) 533 | { 534 | return __fullCaseTable[__fullCaseMap[idx] ]; 535 | } 536 | void MarchingCubes::SetFullCaseTable(void) 537 | { 538 | MarchingSquares::SetFullCaseTable(); 539 | int dir,off,tSize=0; 540 | 541 | memset(__fullCaseMap,-1,sizeof(int)*(1<<(Cube::CORNERS+Cube::FACES))); 542 | for(int idx=0;idx<(1< > edges; 589 | int aFlag=0; 590 | for(int i=0;i(Cube::SquareToCubeEdge(f,MarchingSquares::fullCaseTable(fIdx).edge[i].first),Cube::SquareToCubeEdge(f,MarchingSquares::fullCaseTable(fIdx).edge[i].second))); 607 | else 608 | edges.push_back(std::pair(Cube::SquareToCubeEdge(f,MarchingSquares::fullCaseTable(fIdx).edge[i].second),Cube::SquareToCubeEdge(f,MarchingSquares::fullCaseTable(fIdx).edge[i].first))); 609 | } 610 | for(int uaIndex=0;uaIndex<(1< 37 | class OctNode 38 | { 39 | private: 40 | const OctNode* __faceNeighbor(const int& dir,const int& off) const; 41 | const OctNode* __edgeNeighbor(const int& o,const int i[2],const int idx[2]) const; 42 | OctNode* __faceNeighbor(const int& dir,const int& off,const int& forceChildren); 43 | OctNode* __edgeNeighbor(const int& o,const int i[2],const int idx[2],const int& forceChildren); 44 | public: 45 | class NodeIndex 46 | { 47 | public: 48 | NodeIndex(void); 49 | int depth,offset[3]; 50 | NodeIndex child(const int& cIndex) const; 51 | NodeIndex parent(void) const; 52 | NodeIndex& operator += (const int& cIndex); 53 | NodeIndex& operator -- (void); 54 | }; 55 | 56 | OctNode* parent; 57 | OctNode* children; 58 | NodeData nodeData; 59 | 60 | 61 | OctNode(void); 62 | ~OctNode(void); 63 | int initChildren(void); 64 | void deleteChildren(void); 65 | 66 | static inline void CenterAndWidth(const NodeIndex& nIndex,Point3D& center,Real& width); 67 | 68 | int leaves(void) const; 69 | int maxDepthLeaves(const int& maxDepth) const; 70 | int nodes(void) const; 71 | int maxDepth(void) const; 72 | 73 | const OctNode* root(void) const; 74 | 75 | const OctNode* nextLeaf(const OctNode* currentLeaf) const; 76 | OctNode* nextLeaf(OctNode* currentLeaf); 77 | const OctNode* nextNode(const OctNode* currentNode) const; 78 | OctNode* nextNode(OctNode* currentNode); 79 | const OctNode* nextBranch(const OctNode* current) const; 80 | OctNode* nextBranch(OctNode* current); 81 | 82 | const OctNode* nextLeaf(const OctNode* currentLeaf,NodeIndex &nIndex) const; 83 | OctNode* nextLeaf(OctNode* currentLeaf,NodeIndex &nIndex); 84 | const OctNode* nextNode(const OctNode* currentNode,NodeIndex &nIndex) const; 85 | OctNode* nextNode(OctNode* currentNode,NodeIndex &nIndex); 86 | const OctNode* nextBranch(const OctNode* current,NodeIndex &nIndex) const; 87 | OctNode* nextBranch(OctNode* current,NodeIndex &nIndex); 88 | 89 | void setFullDepth(const int& maxDepth); 90 | void printLeaves(void) const; 91 | void printRange(void) const; 92 | 93 | OctNode* faceNeighbor(const int& faceIndex,const int& forceChildren=0); 94 | const OctNode* faceNeighbor(const int& faceIndex) const; 95 | OctNode* edgeNeighbor(const int& edgeIndex,const int& forceChildren=0); 96 | const OctNode* edgeNeighbor(const int& edgeIndex) const; 97 | OctNode* cornerNeighbor(const int& cornerIndex,const int& forceChildren=0); 98 | const OctNode* cornerNeighbor(const int& cornerIndex) const; 99 | 100 | 101 | int write(const char* fileName,int writeData) const; 102 | int write(FILE* fp,int writeData) const; 103 | int read(const char* fileName,int readData); 104 | int read(FILE* fp,int readData); 105 | 106 | static long long CenterIndex(const NodeIndex &nIndex,const int& maxDepth); 107 | static long long CenterIndex(const NodeIndex &nIndex,const int& maxDepth,int index[3]); 108 | static long long CornerIndex(const NodeIndex &nIndex,const int& cIndex,const int& maxDepth); 109 | static long long CornerIndex(const NodeIndex &nIndex,const int& cIndex,const int& maxDepth,int index[3]); 110 | static long long EdgeIndex(const NodeIndex &nIndex,const int& eIndex,const int& maxDepth); 111 | static long long EdgeIndex(const NodeIndex &nIndex,const int& eIndex,const int& maxDepth,int idx[3]); 112 | static long long FaceIndex(const NodeIndex &nIndex,const int& fIndex,const int& maxDepth); 113 | static long long FaceIndex(const NodeIndex &nIndex,const int& fIndex,const int& maxDepth,int idx[3]); 114 | 115 | class Neighbors{ 116 | public: 117 | OctNode* neighbors[3][3][3]; 118 | NodeIndex nIndex; 119 | Neighbors(void); 120 | void clear(void); 121 | }; 122 | class NeighborKey 123 | { 124 | int _depth; 125 | Neighbors& _setNeighbors(OctNode* node,const int& d); 126 | Neighbors& _getNeighbors(OctNode* node,const int& d); 127 | public: 128 | Neighbors* neighbors; 129 | int depth; 130 | 131 | NeighborKey(void); 132 | ~NeighborKey(void); 133 | 134 | void set(const int& depth); 135 | Neighbors& setNeighbors(OctNode* node); 136 | Neighbors& getNeighbors(OctNode* node); 137 | }; 138 | }; 139 | 140 | 141 | #include "Octree.inl" 142 | 143 | #endif // OCT_NODE 144 | -------------------------------------------------------------------------------- /SRC/Octree.inl: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2006, Michael Kazhdan and Matthew Bolitho 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | Redistributions of source code must retain the above copyright notice, this list of 9 | conditions and the following disclaimer. Redistributions in binary form must reproduce 10 | the above copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the distribution. 12 | 13 | Neither the name of the Johns Hopkins University nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without specific 15 | prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 18 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES 19 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 20 | SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 21 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 22 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 23 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 26 | DAMAGE. 27 | */ 28 | #include 29 | #include 30 | #include 31 | 32 | //////////////////////// 33 | // OctNode::NodeIndex // 34 | //////////////////////// 35 | template 36 | OctNode::NodeIndex::NodeIndex(void) 37 | { 38 | depth=offset[0]=offset[1]=offset[2]=0; 39 | } 40 | template 41 | typename OctNode::NodeIndex OctNode::NodeIndex::child(const int& cIndex) const 42 | { 43 | int x,y,z; 44 | NodeIndex idx; 45 | Cube::FactorCornerIndex(cIndex,x,y,z); 46 | idx.depth=depth+1; 47 | idx.offset[0]=(offset[0]<<1)|x; 48 | idx.offset[1]=(offset[1]<<1)|y; 49 | idx.offset[2]=(offset[2]<<1)|z; 50 | return idx; 51 | } 52 | template 53 | typename OctNode::NodeIndex OctNode::NodeIndex::parent(void) const 54 | { 55 | NodeIndex idx; 56 | idx.depth=depth-1; 57 | idx.offset[0]=offset[0]>>1; 58 | idx.offset[1]=offset[1]>>1; 59 | idx.offset[2]=offset[2]>>1; 60 | return idx; 61 | } 62 | template 63 | typename OctNode::NodeIndex& OctNode::NodeIndex::operator += (const int& cIndex) 64 | { 65 | int x,y,z; 66 | Cube::FactorCornerIndex(cIndex,x,y,z); 67 | depth++; 68 | offset[0]=(offset[0]<<1)|x; 69 | offset[1]=(offset[1]<<1)|y; 70 | offset[2]=(offset[2]<<1)|z; 71 | return *this; 72 | } 73 | template 74 | typename OctNode::NodeIndex& OctNode::NodeIndex::operator -- (void) 75 | { 76 | depth--; 77 | offset[0]>>=1; 78 | offset[1]>>=1; 79 | offset[2]>>=1; 80 | return *this; 81 | } 82 | 83 | ///////////// 84 | // OctNode // 85 | ///////////// 86 | 87 | template 88 | OctNode::OctNode(void) 89 | { 90 | parent=children=NULL; 91 | } 92 | 93 | template 94 | OctNode::~OctNode(void){ 95 | if(children){delete[] children;} 96 | parent=children=NULL; 97 | } 98 | template 99 | void OctNode::setFullDepth(const int& maxDepth){ 100 | if(maxDepth){ 101 | if(!children){initChildren();} 102 | for(int i=0;i<8;i++){children[i].setFullDepth(maxDepth-1);} 103 | } 104 | } 105 | template 106 | void OctNode::deleteChildren(void) 107 | { 108 | if(children) 109 | delete[] children; 110 | children=NULL; 111 | } 112 | template 113 | int OctNode::initChildren(void){ 114 | int i,j,k; 115 | 116 | if(children){delete[] children;} 117 | children=NULL; 118 | children=new OctNode[Cube::CORNERS]; 119 | if(!children){ 120 | fprintf(stderr,"Failed to initialize children in OctNode::initChildren\n"); 121 | exit(0); 122 | return 0; 123 | } 124 | for(i=0;i<2;i++){ 125 | for(j=0;j<2;j++){ 126 | for(k=0;k<2;k++){ 127 | int idx=Cube::CornerIndex(i,j,k); 128 | children[idx].parent=this; 129 | children[idx].children=NULL; 130 | } 131 | } 132 | } 133 | return 1; 134 | } 135 | template 136 | inline void OctNode::CenterAndWidth(const NodeIndex &nIndex,Point3D& center,Real& width) 137 | { 138 | width=Real(1.0/(1< 144 | int OctNode::maxDepth(void) const{ 145 | if(!children){return 0;} 146 | else{ 147 | int c,d; 148 | for(int i=0;ic){c=d;} 151 | } 152 | return c+1; 153 | } 154 | } 155 | template 156 | int OctNode::nodes(void) const{ 157 | if(!children){return 1;} 158 | else{ 159 | int c=0; 160 | for(int i=0;i 165 | int OctNode::leaves(void) const{ 166 | if(!children){return 1;} 167 | else{ 168 | int c=0; 169 | for(int i=0;i 174 | int OctNode::maxDepthLeaves(const int& maxDepth) const{ 175 | if(NodeData::depth()>maxDepth){return 0;} 176 | if(!children){return 1;} 177 | else{ 178 | int c=0; 179 | for(int i=0;i 184 | const OctNode* OctNode::root(void) const{ 185 | const OctNode* temp=this; 186 | while(temp->parent){temp=temp->parent;} 187 | return temp; 188 | } 189 | 190 | 191 | template 192 | const OctNode* OctNode::nextBranch(const OctNode* current) const{ 193 | if(!current->parent || current==this){return NULL;} 194 | if(current-current->parent->children==Cube::CORNERS-1){return nextBranch(current->parent);} 195 | else{return current+1;} 196 | } 197 | template 198 | OctNode* OctNode::nextBranch(OctNode* current){ 199 | if(!current->parent || current==this){return NULL;} 200 | if(current-current->parent->children==Cube::CORNERS-1){return nextBranch(current->parent);} 201 | else{return current+1;} 202 | } 203 | template 204 | const OctNode* OctNode::nextLeaf(const OctNode* current) const{ 205 | if(!current){ 206 | const OctNode* temp=this; 207 | while(temp->children){temp=&temp->children[0];} 208 | return temp; 209 | } 210 | if(current->children){return current->nextLeaf(NULL);} 211 | const OctNode* temp=nextBranch(current); 212 | if(!temp){return NULL;} 213 | else{return temp->nextLeaf(NULL);} 214 | } 215 | template 216 | OctNode* OctNode::nextLeaf(OctNode* current){ 217 | if(!current){ 218 | OctNode* temp=this; 219 | while(temp->children){temp=&temp->children[0];} 220 | return temp; 221 | } 222 | if(current->children){return current->nextLeaf(NULL);} 223 | OctNode* temp=nextBranch(current); 224 | if(!temp){return NULL;} 225 | else{return temp->nextLeaf(NULL);} 226 | } 227 | 228 | template 229 | const OctNode* OctNode::nextNode(const OctNode* current) const{ 230 | if(!current){return this;} 231 | else if(current->children){return ¤t->children[0];} 232 | else{return nextBranch(current);} 233 | } 234 | template 235 | OctNode* OctNode::nextNode(OctNode* current){ 236 | if(!current){return this;} 237 | else if(current->children){return ¤t->children[0];} 238 | else{return nextBranch(current);} 239 | } 240 | template 241 | const OctNode* OctNode::nextBranch(const OctNode* current,NodeIndex& nIndex) const 242 | { 243 | if(!current->parent || current==this) 244 | return NULL; 245 | int c=current-current->parent->children; 246 | nIndex--; 247 | if(c==Cube::CORNERS-1) 248 | return nextBranch(current->parent,nIndex); 249 | else 250 | { 251 | nIndex+=c; 252 | return current+1; 253 | } 254 | } 255 | template 256 | OctNode* OctNode::nextBranch(OctNode* current,NodeIndex& nIndex) 257 | { 258 | if(!current->parent || current==this) 259 | return NULL; 260 | int c=int(current-current->parent->children); 261 | --nIndex; 262 | if(c==Cube::CORNERS-1) 263 | return nextBranch(current->parent,nIndex); 264 | else 265 | { 266 | nIndex+=c+1; 267 | return current+1; 268 | } 269 | } 270 | template 271 | const OctNode* OctNode::nextLeaf(const OctNode* current,NodeIndex& nIndex) const 272 | { 273 | if(!current) 274 | { 275 | const OctNode* temp=this; 276 | while(temp->children) 277 | { 278 | nIndex+=0; 279 | temp=&temp->children[0]; 280 | } 281 | return temp; 282 | } 283 | if(current->children) 284 | return current->nextLeaf(NULL,nIndex); 285 | const OctNode* temp=nextBranch(current,nIndex); 286 | if(!temp) 287 | return NULL; 288 | else 289 | return temp->nextLeaf(NULL,nIndex); 290 | } 291 | template 292 | OctNode* OctNode::nextLeaf(OctNode* current,NodeIndex& nIndex) 293 | { 294 | if(!current){ 295 | OctNode* temp=this; 296 | while(temp->children) 297 | { 298 | nIndex+=0; 299 | temp=&temp->children[0]; 300 | } 301 | return temp; 302 | } 303 | if(current->children) 304 | return current->nextLeaf(NULL,nIndex); 305 | OctNode* temp=nextBranch(current,nIndex); 306 | if(!temp) 307 | return NULL; 308 | else 309 | return temp->nextLeaf(NULL,nIndex); 310 | } 311 | template 312 | const OctNode* OctNode::nextNode(const OctNode* current,NodeIndex& nIndex) const 313 | { 314 | if(!current) 315 | return this; 316 | else if(current->children) 317 | { 318 | nIndex+=0; 319 | return ¤t->children[0]; 320 | } 321 | else 322 | return nextBranch(current,nIndex); 323 | } 324 | template 325 | OctNode* OctNode::nextNode(OctNode* current,NodeIndex& nIndex) 326 | { 327 | if(!current) 328 | return this; 329 | else if(current->children) 330 | { 331 | nIndex+=0; 332 | return ¤t->children[0]; 333 | } 334 | else 335 | return nextBranch(current,nIndex); 336 | } 337 | 338 | template 339 | void OctNode::printRange(void) const{ 340 | Point3D center; 341 | Real width; 342 | centerAndWidth(center,width); 343 | for(int dim=0;dim<3;dim++){ 344 | printf("%[%f,%f]",center[dim]-width/2,center[dim]+width/2); 345 | if(dim<3-1){printf("x");} 346 | else printf("\n"); 347 | } 348 | } 349 | 350 | template 351 | OctNode* OctNode::faceNeighbor(const int& faceIndex,const int& forceChildren){return __faceNeighbor(faceIndex>>1,faceIndex&1,forceChildren);} 352 | template 353 | const OctNode* OctNode::faceNeighbor(const int& faceIndex) const {return __faceNeighbor(faceIndex>>1,faceIndex&1);} 354 | template 355 | OctNode* OctNode::__faceNeighbor(const int& dir,const int& off,const int& forceChildren){ 356 | if(!parent){return NULL;} 357 | int pIndex=int(this-parent->children); 358 | pIndex^=(1<children[pIndex];} 360 | // if(!(((pIndex>>dir)^off)&1)){return &parent->children[pIndex];} 361 | else{ 362 | OctNode* temp=parent->__faceNeighbor(dir,off,forceChildren); 363 | if(!temp){return NULL;} 364 | if(!temp->children){ 365 | if(forceChildren){temp->initChildren();} 366 | else{return temp;} 367 | } 368 | return &temp->children[pIndex]; 369 | } 370 | } 371 | template 372 | const OctNode* OctNode::__faceNeighbor(const int& dir,const int& off) const { 373 | if(!parent){return NULL;} 374 | int pIndex=int(this-parent->children); 375 | pIndex^=(1<children[pIndex];} 377 | // if(!(((pIndex>>dir)^off)&1)){return &parent->children[pIndex];} 378 | else{ 379 | const OctNode* temp=parent->__faceNeighbor(dir,off); 380 | if(!temp || !temp->children){return temp;} 381 | else{return &temp->children[pIndex];} 382 | } 383 | } 384 | 385 | template 386 | OctNode* OctNode::edgeNeighbor(const int& edgeIndex,const int& forceChildren){ 387 | int idx[2],o,i[2]; 388 | Cube::FactorEdgeIndex(edgeIndex,o,i[0],i[1]); 389 | switch(o){ 390 | case 0: idx[0]=1; idx[1]=2; break; 391 | case 1: idx[0]=0; idx[1]=2; break; 392 | case 2: idx[0]=0; idx[1]=1; break; 393 | }; 394 | return __edgeNeighbor(o,i,idx,forceChildren); 395 | } 396 | template 397 | const OctNode* OctNode::edgeNeighbor(const int& edgeIndex) const { 398 | int idx[2],o,i[2]; 399 | Cube::FactorEdgeIndex(edgeIndex,o,i[0],i[1]); 400 | switch(o){ 401 | case 0: idx[0]=1; idx[1]=2; break; 402 | case 1: idx[0]=0; idx[1]=2; break; 403 | case 2: idx[0]=0; idx[1]=1; break; 404 | }; 405 | return __edgeNeighbor(o,i,idx); 406 | } 407 | template 408 | const OctNode* OctNode::__edgeNeighbor(const int& o,const int i[2],const int idx[2]) const{ 409 | if(!parent){return NULL;} 410 | int pIndex=int(this-parent->children); 411 | int aIndex,x[3]; 412 | 413 | Cube::FactorCornerIndex(pIndex,x[0],x[1],x[2]); 414 | aIndex=(~((i[0] ^ x[idx[0]]) | ((i[1] ^ x[idx[1]])<<1))) & 3; 415 | pIndex^=(7 ^ (1<__faceNeighbor(idx[0],i[0]); 418 | if(!temp || !temp->children){return NULL;} 419 | else{return &temp->children[pIndex];} 420 | } 421 | else if(aIndex==2) { // I can get the neighbor from the parent's face adjacent neighbor 422 | const OctNode* temp=parent->__faceNeighbor(idx[1],i[1]); 423 | if(!temp || !temp->children){return NULL;} 424 | else{return &temp->children[pIndex];} 425 | } 426 | else if(aIndex==0) { // I can get the neighbor from the parent 427 | return &parent->children[pIndex]; 428 | } 429 | else if(aIndex==3) { // I can get the neighbor from the parent's edge adjacent neighbor 430 | const OctNode* temp=parent->__edgeNeighbor(o,i,idx); 431 | if(!temp || !temp->children){return temp;} 432 | else{return &temp->children[pIndex];} 433 | } 434 | else{return NULL;} 435 | } 436 | template 437 | OctNode* OctNode::__edgeNeighbor(const int& o,const int i[2],const int idx[2],const int& forceChildren){ 438 | if(!parent){return NULL;} 439 | int pIndex=int(this-parent->children); 440 | int aIndex,x[3]; 441 | 442 | Cube::FactorCornerIndex(pIndex,x[0],x[1],x[2]); 443 | aIndex=(~((i[0] ^ x[idx[0]]) | ((i[1] ^ x[idx[1]])<<1))) & 3; 444 | pIndex^=(7 ^ (1<__faceNeighbor(idx[0],i[0],0); 447 | if(!temp || !temp->children){return NULL;} 448 | else{return &temp->children[pIndex];} 449 | } 450 | else if(aIndex==2) { // I can get the neighbor from the parent's face adjacent neighbor 451 | OctNode* temp=parent->__faceNeighbor(idx[1],i[1],0); 452 | if(!temp || !temp->children){return NULL;} 453 | else{return &temp->children[pIndex];} 454 | } 455 | else if(aIndex==0) { // I can get the neighbor from the parent 456 | return &parent->children[pIndex]; 457 | } 458 | else if(aIndex==3) { // I can get the neighbor from the parent's edge adjacent neighbor 459 | OctNode* temp=parent->__edgeNeighbor(o,i,idx,forceChildren); 460 | if(!temp){return NULL;} 461 | if(!temp->children){ 462 | if(forceChildren){temp->initChildren();} 463 | else{return temp;} 464 | } 465 | return &temp->children[pIndex]; 466 | } 467 | else{return NULL;} 468 | } 469 | 470 | template 471 | const OctNode* OctNode::cornerNeighbor(const int& cornerIndex) const { 472 | int pIndex,aIndex=0; 473 | if(!parent){return NULL;} 474 | 475 | pIndex=int(this-parent->children); 476 | aIndex=(cornerIndex ^ pIndex); // The disagreement bits 477 | pIndex=(~pIndex)&7; // The antipodal point 478 | if(aIndex==7){ // Agree on no bits 479 | return &parent->children[pIndex]; 480 | } 481 | else if(aIndex==0){ // Agree on all bits 482 | const OctNode* temp=((const OctNode*)parent)->cornerNeighbor(cornerIndex); 483 | if(!temp || !temp->children){return temp;} 484 | else{return &temp->children[pIndex];} 485 | } 486 | else if(aIndex==6){ // Agree on face 0 487 | const OctNode* temp=((const OctNode*)parent)->__faceNeighbor(0,cornerIndex & 1); 488 | if(!temp || !temp->children){return NULL;} 489 | else{return & temp->children[pIndex];} 490 | } 491 | else if(aIndex==5){ // Agree on face 1 492 | const OctNode* temp=((const OctNode*)parent)->__faceNeighbor(1,(cornerIndex & 2)>>1); 493 | if(!temp || !temp->children){return NULL;} 494 | else{return & temp->children[pIndex];} 495 | } 496 | else if(aIndex==3){ // Agree on face 2 497 | const OctNode* temp=((const OctNode*)parent)->__faceNeighbor(2,(cornerIndex & 4)>>2); 498 | if(!temp || !temp->children){return NULL;} 499 | else{return & temp->children[pIndex];} 500 | } 501 | else if(aIndex==4){ // Agree on edge 2 502 | const OctNode* temp=((const OctNode*)parent)->edgeNeighbor(8 | (cornerIndex & 1) | (cornerIndex & 2) ); 503 | if(!temp || !temp->children){return NULL;} 504 | else{return & temp->children[pIndex];} 505 | } 506 | else if(aIndex==2){ // Agree on edge 1 507 | const OctNode* temp=((const OctNode*)parent)->edgeNeighbor(4 | (cornerIndex & 1) | ((cornerIndex & 4)>>1) ); 508 | if(!temp || !temp->children){return NULL;} 509 | else{return & temp->children[pIndex];} 510 | } 511 | else if(aIndex==1){ // Agree on edge 0 512 | const OctNode* temp=((const OctNode*)parent)->edgeNeighbor(((cornerIndex & 2) | (cornerIndex & 4))>>1 ); 513 | if(!temp || !temp->children){return NULL;} 514 | else{return & temp->children[pIndex];} 515 | } 516 | else{return NULL;} 517 | } 518 | template 519 | OctNode* OctNode::cornerNeighbor(const int& cornerIndex,const int& forceChildren){ 520 | int pIndex,aIndex=0; 521 | if(!parent){return NULL;} 522 | 523 | pIndex=int(this-parent->children); 524 | aIndex=(cornerIndex ^ pIndex); // The disagreement bits 525 | pIndex=(~pIndex)&7; // The antipodal point 526 | if(aIndex==7){ // Agree on no bits 527 | return &parent->children[pIndex]; 528 | } 529 | else if(aIndex==0){ // Agree on all bits 530 | OctNode* temp=((OctNode*)parent)->cornerNeighbor(cornerIndex,forceChildren); 531 | if(!temp){return NULL;} 532 | if(!temp->children){ 533 | if(forceChildren){temp->initChildren();} 534 | else{return temp;} 535 | } 536 | return &temp->children[pIndex]; 537 | } 538 | else if(aIndex==6){ // Agree on face 0 539 | OctNode* temp=((OctNode*)parent)->__faceNeighbor(0,cornerIndex & 1,0); 540 | if(!temp || !temp->children){return NULL;} 541 | else{return & temp->children[pIndex];} 542 | } 543 | else if(aIndex==5){ // Agree on face 1 544 | OctNode* temp=((OctNode*)parent)->__faceNeighbor(1,(cornerIndex & 2)>>1,0); 545 | if(!temp || !temp->children){return NULL;} 546 | else{return & temp->children[pIndex];} 547 | } 548 | else if(aIndex==3){ // Agree on face 2 549 | OctNode* temp=((OctNode*)parent)->__faceNeighbor(2,(cornerIndex & 4)>>2,0); 550 | if(!temp || !temp->children){return NULL;} 551 | else{return & temp->children[pIndex];} 552 | } 553 | else if(aIndex==4){ // Agree on edge 2 554 | OctNode* temp=((OctNode*)parent)->edgeNeighbor(8 | (cornerIndex & 1) | (cornerIndex & 2) ); 555 | if(!temp || !temp->children){return NULL;} 556 | else{return & temp->children[pIndex];} 557 | } 558 | else if(aIndex==2){ // Agree on edge 1 559 | OctNode* temp=((OctNode*)parent)->edgeNeighbor(4 | (cornerIndex & 1) | ((cornerIndex & 4)>>1) ); 560 | if(!temp || !temp->children){return NULL;} 561 | else{return & temp->children[pIndex];} 562 | } 563 | else if(aIndex==1){ // Agree on edge 0 564 | OctNode* temp=((OctNode*)parent)->edgeNeighbor(((cornerIndex & 2) | (cornerIndex & 4))>>1 ); 565 | if(!temp || !temp->children){return NULL;} 566 | else{return & temp->children[pIndex];} 567 | } 568 | else{return NULL;} 569 | } 570 | //////////////////////// 571 | // OctNodeNeighborKey // 572 | //////////////////////// 573 | template 574 | OctNode::Neighbors::Neighbors(void){clear();} 575 | template 576 | void OctNode::Neighbors::clear(void){ 577 | for(int i=0;i<3;i++){for(int j=0;j<3;j++){for(int k=0;k<3;k++){neighbors[i][j][k]=NULL;}}} 578 | } 579 | template 580 | OctNode::NeighborKey::NeighborKey(void){neighbors=NULL;} 581 | template 582 | OctNode::NeighborKey::~NeighborKey(void){ 583 | if(neighbors){delete[] neighbors;} 584 | neighbors=NULL; 585 | } 586 | 587 | template 588 | void OctNode::NeighborKey::set(const int& d){ 589 | if(neighbors){delete[] neighbors;} 590 | neighbors=NULL; 591 | if(d<0){return;} 592 | _depth=d; 593 | neighbors=new Neighbors[d+1]; 594 | } 595 | template 596 | typename OctNode::Neighbors& OctNode::NeighborKey::setNeighbors(OctNode* node){ 597 | OctNode* temp=node; 598 | depth=0; 599 | while(temp->parent) 600 | { 601 | depth++; 602 | temp=temp->parent; 603 | } 604 | if(node!=neighbors[depth].neighbors[1][1][1]) 605 | for(int i=depth;i<=_depth;i++) 606 | neighbors[i].clear(); 607 | 608 | return _setNeighbors(node,depth); 609 | } 610 | template 611 | typename OctNode::Neighbors& OctNode::NeighborKey::_setNeighbors(OctNode* node,const int& d) 612 | { 613 | if(node!=neighbors[d].neighbors[1][1][1]){ 614 | neighbors[d].clear(); 615 | 616 | if(!node->parent) 617 | { 618 | neighbors[d].nIndex=OctNode::NodeIndex(); 619 | neighbors[d].neighbors[1][1][1]=node; 620 | } 621 | else 622 | { 623 | Neighbors& temp=_setNeighbors(node->parent,d-1); 624 | int i,j,k,x1,y1,z1,x2,y2,z2; 625 | int idx=int(node-node->parent->children); 626 | neighbors[d].nIndex=neighbors[d-1].nIndex.child(idx); 627 | Cube::FactorCornerIndex( idx ,x1,y1,z1); 628 | Cube::FactorCornerIndex((~idx)&7,x2,y2,z2); 629 | for(i=0;i<2;i++){ 630 | for(j=0;j<2;j++){ 631 | for(k=0;k<2;k++){ 632 | neighbors[d].neighbors[x2+i][y2+j][z2+k]=&node->parent->children[Cube::CornerIndex(i,j,k)]; 633 | } 634 | } 635 | } 636 | 637 | // Set the neighbors from across the faces 638 | i=x1<<1; 639 | if(temp.neighbors[i][1][1]){ 640 | if(!temp.neighbors[i][1][1]->children){temp.neighbors[i][1][1]->initChildren();} 641 | for(j=0;j<2;j++){for(k=0;k<2;k++){neighbors[d].neighbors[i][y2+j][z2+k]=&temp.neighbors[i][1][1]->children[Cube::CornerIndex(x2,j,k)];}} 642 | } 643 | j=y1<<1; 644 | if(temp.neighbors[1][j][1]){ 645 | if(!temp.neighbors[1][j][1]->children){temp.neighbors[1][j][1]->initChildren();} 646 | for(i=0;i<2;i++){for(k=0;k<2;k++){neighbors[d].neighbors[x2+i][j][z2+k]=&temp.neighbors[1][j][1]->children[Cube::CornerIndex(i,y2,k)];}} 647 | } 648 | k=z1<<1; 649 | if(temp.neighbors[1][1][k]){ 650 | if(!temp.neighbors[1][1][k]->children){temp.neighbors[1][1][k]->initChildren();} 651 | for(i=0;i<2;i++){for(j=0;j<2;j++){neighbors[d].neighbors[x2+i][y2+j][k]=&temp.neighbors[1][1][k]->children[Cube::CornerIndex(i,j,z2)];}} 652 | } 653 | 654 | // Set the neighbors from across the edges 655 | i=x1<<1; j=y1<<1; 656 | if(temp.neighbors[i][j][1]){ 657 | if(!temp.neighbors[i][j][1]->children){temp.neighbors[i][j][1]->initChildren();} 658 | for(k=0;k<2;k++){neighbors[d].neighbors[i][j][z2+k]=&temp.neighbors[i][j][1]->children[Cube::CornerIndex(x2,y2,k)];} 659 | } 660 | i=x1<<1; k=z1<<1; 661 | if(temp.neighbors[i][1][k]){ 662 | if(!temp.neighbors[i][1][k]->children){temp.neighbors[i][1][k]->initChildren();} 663 | for(j=0;j<2;j++){neighbors[d].neighbors[i][y2+j][k]=&temp.neighbors[i][1][k]->children[Cube::CornerIndex(x2,j,z2)];} 664 | } 665 | j=y1<<1; k=z1<<1; 666 | if(temp.neighbors[1][j][k]){ 667 | if(!temp.neighbors[1][j][k]->children){temp.neighbors[1][j][k]->initChildren();} 668 | for(i=0;i<2;i++){neighbors[d].neighbors[x2+i][j][k]=&temp.neighbors[1][j][k]->children[Cube::CornerIndex(i,y2,z2)];} 669 | } 670 | 671 | // Set the neighbor from across the corner 672 | i=x1<<1; j=y1<<1; k=z1<<1; 673 | if(temp.neighbors[i][j][k]){ 674 | if(!temp.neighbors[i][j][k]->children){temp.neighbors[i][j][k]->initChildren();} 675 | neighbors[d].neighbors[i][j][k]=&temp.neighbors[i][j][k]->children[Cube::CornerIndex(x2,y2,z2)]; 676 | } 677 | } 678 | } 679 | return neighbors[d]; 680 | } 681 | template 682 | typename OctNode::Neighbors& OctNode::NeighborKey::getNeighbors(OctNode* node){ 683 | depth=0; 684 | OctNode* temp=node; 685 | while(temp->parent) 686 | { 687 | depth++; 688 | temp=temp->parent; 689 | } 690 | if(node!=neighbors[depth].neighbors[1][1][1]) 691 | for(int i=depth;i<=_depth;i++) 692 | neighbors[i].clear(); 693 | 694 | return _getNeighbors(node,depth); 695 | } 696 | template 697 | typename OctNode::Neighbors& OctNode::NeighborKey::_getNeighbors(OctNode* node,const int& d) 698 | { 699 | if(node!=neighbors[d].neighbors[1][1][1]){ 700 | neighbors[d].clear(); 701 | 702 | if(!node->parent) 703 | { 704 | neighbors[d].neighbors[1][1][1]=node; 705 | neighbors[d].nIndex=OctNode::NodeIndex(); 706 | } 707 | else 708 | { 709 | Neighbors& temp=_getNeighbors(node->parent,d-1); 710 | int i,j,k,x1,y1,z1,x2,y2,z2; 711 | int idx=int(node-node->parent->children); 712 | neighbors[d].nIndex=neighbors[d-1].nIndex.child(idx); 713 | Cube::FactorCornerIndex( idx ,x1,y1,z1); 714 | Cube::FactorCornerIndex((~idx)&7,x2,y2,z2); 715 | for(i=0;i<2;i++){ 716 | for(j=0;j<2;j++){ 717 | for(k=0;k<2;k++){ 718 | neighbors[d].neighbors[x2+i][y2+j][z2+k]=&node->parent->children[Cube::CornerIndex(i,j,k)]; 719 | } 720 | } 721 | } 722 | 723 | // Set the neighbors from across the faces 724 | i=x1<<1; 725 | if(temp.neighbors[i][1][1] && temp.neighbors[i][1][1]->children){ 726 | for(j=0;j<2;j++){for(k=0;k<2;k++){neighbors[d].neighbors[i][y2+j][z2+k]=&temp.neighbors[i][1][1]->children[Cube::CornerIndex(x2,j,k)];}} 727 | } 728 | j=y1<<1; 729 | if(temp.neighbors[1][j][1] && temp.neighbors[1][j][1]->children){ 730 | for(i=0;i<2;i++){for(k=0;k<2;k++){neighbors[d].neighbors[x2+i][j][z2+k]=&temp.neighbors[1][j][1]->children[Cube::CornerIndex(i,y2,k)];}} 731 | } 732 | k=z1<<1; 733 | if(temp.neighbors[1][1][k] && temp.neighbors[1][1][k]->children){ 734 | for(i=0;i<2;i++){for(j=0;j<2;j++){neighbors[d].neighbors[x2+i][y2+j][k]=&temp.neighbors[1][1][k]->children[Cube::CornerIndex(i,j,z2)];}} 735 | } 736 | 737 | // Set the neighbors from across the edges 738 | i=x1<<1; j=y1<<1; 739 | if(temp.neighbors[i][j][1] && temp.neighbors[i][j][1]->children){ 740 | for(k=0;k<2;k++){neighbors[d].neighbors[i][j][z2+k]=&temp.neighbors[i][j][1]->children[Cube::CornerIndex(x2,y2,k)];} 741 | } 742 | i=x1<<1; k=z1<<1; 743 | if(temp.neighbors[i][1][k] && temp.neighbors[i][1][k]->children){ 744 | for(j=0;j<2;j++){neighbors[d].neighbors[i][y2+j][k]=&temp.neighbors[i][1][k]->children[Cube::CornerIndex(x2,j,z2)];} 745 | } 746 | j=y1<<1; k=z1<<1; 747 | if(temp.neighbors[1][j][k] && temp.neighbors[1][j][k]->children){ 748 | for(i=0;i<2;i++){neighbors[d].neighbors[x2+i][j][k]=&temp.neighbors[1][j][k]->children[Cube::CornerIndex(i,y2,z2)];} 749 | } 750 | 751 | // Set the neighbor from across the corner 752 | i=x1<<1; j=y1<<1; k=z1<<1; 753 | if(temp.neighbors[i][j][k] && temp.neighbors[i][j][k]->children){ 754 | neighbors[d].neighbors[i][j][k]=&temp.neighbors[i][j][k]->children[Cube::CornerIndex(x2,y2,z2)]; 755 | } 756 | } 757 | } 758 | return neighbors[d]; 759 | } 760 | 761 | template 762 | int OctNode::write(const char* fileName,int writeData) const{ 763 | FILE* fp=fopen(fileName,"wb"); 764 | if(!fp){return 0;} 765 | int ret=write(fp,writeData); 766 | fclose(fp); 767 | return ret; 768 | } 769 | template 770 | int OctNode::write(FILE* fp,int writeData) const 771 | { 772 | if(writeData) 773 | fwrite(this,sizeof(OctNode),1,fp); 774 | else 775 | { 776 | fwrite(&parent,sizeof(OctNode*),1,fp); 777 | fwrite(&children,sizeof(OctNode*),1,fp); 778 | } 779 | if(children){for(int i=0;i 783 | int OctNode::read(const char* fileName,int readData){ 784 | FILE* fp=fopen(fileName,"rb"); 785 | if(!fp){return 0;} 786 | int ret=read(fp,readData); 787 | fclose(fp); 788 | return ret; 789 | } 790 | template 791 | int OctNode::read(FILE* fp,int readData){ 792 | if(readData) 793 | fread(this,sizeof(OctNode),1,fp); 794 | else 795 | { 796 | fread(&parent,sizeof(OctNode*),1,fp); 797 | fread(&children,sizeof(OctNode*),1,fp); 798 | } 799 | parent=NULL; 800 | if(children) 801 | { 802 | children=NULL; 803 | initChildren(); 804 | for(int i=0;i 815 | long long OctNode::CornerIndex(const NodeIndex& nIndex,const int& cIndex,const int& maxDepth) 816 | { 817 | int idx[3]; 818 | return CornerIndex(nIndex,cIndex,maxDepth,idx); 819 | } 820 | template 821 | long long OctNode::CornerIndex(const NodeIndex& nIndex,const int& cIndex,const int& maxDepth,int idx[3]){ 822 | int x[3]; 823 | Cube::FactorCornerIndex(cIndex,x[0],x[1],x[2]); 824 | for(int i=0;i<3;i++) 825 | idx[i]=BinaryNode::CornerIndex(maxDepth+1,nIndex.depth,nIndex.offset[i],x[i]); 826 | return (long long)(idx[0]) | (long long)(idx[1])<<15 | (long long)(idx[2])<<30; 827 | } 828 | template 829 | long long OctNode::CenterIndex(const NodeIndex& nIndex,const int& maxDepth) 830 | { 831 | int idx[3]; 832 | return CenterIndex(nIndex,maxDepth,idx); 833 | } 834 | 835 | template 836 | long long OctNode::CenterIndex(const NodeIndex& nIndex,const int& maxDepth,int idx[3]) 837 | { 838 | for(int i=0;i<3;i++) 839 | idx[i]=BinaryNode::CornerIndex(maxDepth+1,nIndex.depth+1,nIndex.offset[i]<<1,1); 840 | return (long long)(idx[0]) | (long long)(idx[1])<<15 | (long long)(idx[2])<<30; 841 | } 842 | template 843 | long long OctNode::EdgeIndex(const NodeIndex& nIndex,const int& eIndex,const int& maxDepth) 844 | { 845 | int idx[3]; 846 | return EdgeIndex(nIndex,eIndex,maxDepth,idx); 847 | } 848 | template 849 | long long OctNode::EdgeIndex(const NodeIndex& nIndex,const int& eIndex,const int& maxDepth,int idx[3]) 850 | { 851 | int o,i1,i2; 852 | for(int i=0;i<3;i++){idx[i]=BinaryNode::CornerIndex(maxDepth+1,nIndex.depth+1,nIndex.offset[i]<<1,1);} 853 | Cube::FactorEdgeIndex(eIndex,o,i1,i2); 854 | switch(o){ 855 | case 0: 856 | idx[1]=BinaryNode::CornerIndex(maxDepth+1,nIndex.depth,nIndex.offset[1],i1); 857 | idx[2]=BinaryNode::CornerIndex(maxDepth+1,nIndex.depth,nIndex.offset[2],i2); 858 | break; 859 | case 1: 860 | idx[0]=BinaryNode::CornerIndex(maxDepth+1,nIndex.depth,nIndex.offset[0],i1); 861 | idx[2]=BinaryNode::CornerIndex(maxDepth+1,nIndex.depth,nIndex.offset[2],i2); 862 | break; 863 | case 2: 864 | idx[0]=BinaryNode::CornerIndex(maxDepth+1,nIndex.depth,nIndex.offset[0],i1); 865 | idx[1]=BinaryNode::CornerIndex(maxDepth+1,nIndex.depth,nIndex.offset[1],i2); 866 | break; 867 | }; 868 | return (long long)(idx[0]) | (long long)(idx[1])<<15 | (long long)(idx[2])<<30; 869 | } 870 | template 871 | long long OctNode::FaceIndex(const NodeIndex& nIndex,const int& fIndex,const int& maxDepth) 872 | { 873 | int idx[3]; 874 | return FaceIndex(nIndex,fIndex,maxDepth,idx); 875 | } 876 | template 877 | long long OctNode::FaceIndex(const NodeIndex& nIndex,const int& fIndex,const int& maxDepth,int idx[3]) 878 | { 879 | int dir,offset; 880 | Cube::FactorFaceIndex(fIndex,dir,offset); 881 | for(int i=0;i<3;i++) 882 | idx[i]=BinaryNode::CornerIndex(maxDepth+1,nIndex.depth+1,nIndex.offset[i]<<1,1); 883 | idx[dir]=BinaryNode::CornerIndex(maxDepth+1,nIndex.depth,nIndex.offset[dir],offset); 884 | return (long long)(idx[0]) | (long long)(idx[1])<<15 | (long long)(idx[2])<<30; 885 | } 886 | -------------------------------------------------------------------------------- /SRC/VertexData.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2007, Michael Kazhdan 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | Redistributions of source code must retain the above copyright notice, this list of 9 | conditions and the following disclaimer. Redistributions in binary form must reproduce 10 | the above copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the distribution. 12 | 13 | Neither the name of the Johns Hopkins University nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without specific 15 | prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 18 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES 19 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 20 | SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 21 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 22 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 23 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 26 | DAMAGE. 27 | */ 28 | 29 | #ifndef VERTEX_DATA_INCLUDED 30 | #define VERTEX_DATA_INCLUDED 31 | 32 | template 33 | class VertexValue 34 | { 35 | public: 36 | Real v; 37 | VertexValue(void); 38 | VertexValue(const Real& v); 39 | VertexValue(const Real& v,const Point3D& n); 40 | Real value(void) const; 41 | VertexValue operator + (const VertexValue& v) const; 42 | VertexValue operator - (const VertexValue& v) const; 43 | VertexValue operator * (const Real& v) const; 44 | VertexValue operator / (const Real& v) const; 45 | static Point3D RootPosition(const Real& isoValue,const Point3D& p1,const Point3D& p2,VertexValue v1,VertexValue v2); 46 | }; 47 | #include "VertexData.inl" 48 | 49 | #endif // VERTEX_DATA_INCLUDED 50 | -------------------------------------------------------------------------------- /SRC/VertexData.inl: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2007, Michael Kazhdan 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | Redistributions of source code must retain the above copyright notice, this list of 9 | conditions and the following disclaimer. Redistributions in binary form must reproduce 10 | the above copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the distribution. 12 | 13 | Neither the name of the Johns Hopkins University nor the names of its contributors 14 | may be used to endorse or promote products derived from this software without specific 15 | prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 18 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO THE IMPLIED WARRANTIES 19 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 20 | SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 21 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 22 | TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 23 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 26 | DAMAGE. 27 | */ 28 | ///////////////// 29 | // VertexValue // 30 | ///////////////// 31 | template 32 | VertexValue::VertexValue(void) 33 | { 34 | v=0; 35 | } 36 | template 37 | VertexValue::VertexValue(const Real& v) 38 | { 39 | this->v=v; 40 | } 41 | template 42 | VertexValue::VertexValue(const Real& v,const Point3D& n) 43 | { 44 | this->v=v; 45 | } 46 | template 47 | Real VertexValue::value(void) const 48 | { 49 | return v; 50 | } 51 | template 52 | VertexValue VertexValue::operator + (const VertexValue& v) const 53 | { 54 | VertexValue vv; 55 | vv.v=this->v+v.v; 56 | return vv; 57 | } 58 | template 59 | VertexValue VertexValue::operator - (const VertexValue& v) const 60 | { 61 | VertexValue vv; 62 | vv.v=this->v-v.v; 63 | return vv; 64 | } 65 | template 66 | VertexValue VertexValue::operator * (const Real& v) const 67 | { 68 | VertexValue vv; 69 | vv.v=this->v*v; 70 | return vv; 71 | } 72 | template 73 | VertexValue VertexValue::operator / (const Real& v) const 74 | { 75 | VertexValue vv; 76 | vv.v=this->v/v; 77 | return vv; 78 | } 79 | template 80 | Point3D VertexValue::RootPosition(const Real& isoValue,const Point3D& p1,const Point3D& p2,VertexValue v1,VertexValue v2) 81 | { 82 | Real t=(v1.v-isoValue)/(v1.v-v2.v); 83 | return p1*(Real(1.0)-t)+p2*t; 84 | } 85 | 86 | -------------------------------------------------------------------------------- /SRC/ZOrderOctree.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 Otto Seiskari 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | #pragma once 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | template 35 | class ZOrderOctree { 36 | public: 37 | // Note: "should" also work with 1 more bit but something is broken 38 | static constexpr size_t MAX_ROOT_LEVEL = std::numeric_limits::digits / 3 - 1; 39 | using Vector3 = std::array; 40 | struct Parameters { 41 | Vector3 origin = { 0, 0, 0 }; 42 | Float leafSize = 1.0; 43 | bool stableSort = false; 44 | bool sortKnnResponse = false; 45 | size_t rootLevel = MAX_ROOT_LEVEL; 46 | }; 47 | 48 | ZOrderOctree(const Parameters ¶ms) : 49 | params(params) 50 | { 51 | assert(params.rootLevel <= MAX_ROOT_LEVEL); 52 | } 53 | 54 | void clear() { 55 | zindices.clear(); 56 | elements.clear(); 57 | } 58 | 59 | void addData(const Element* elementsBegin, size_t nElements) { 60 | // log_debug("minCorner %g,%g,%g", minCorner[0], minCorner[1], minCorner[2]); 61 | tmp.clear(); 62 | tmp.reserve(nElements + zindices.size()); 63 | 64 | assert(zindices.size() == elements.size()); 65 | 66 | // add existing data (note: it could be faster to do a custom merge-sort) 67 | for (size_t i = 0; i < zindices.size(); ++i) { 68 | tmp.zindices.push_back(zindices.at(i)); 69 | tmp.elements.push_back(elements.at(i)); 70 | tmp.order.push_back(tmp.order.size()); 71 | } 72 | 73 | zindices.clear(); 74 | elements.clear(); 75 | 76 | for (const auto *itr = elementsBegin; itr != (elementsBegin + nElements); ++itr) { 77 | tmp.elements.push_back(&*itr); 78 | tmp.zindices.push_back(getZIndex(*itr)); 79 | tmp.order.push_back(tmp.order.size()); 80 | } 81 | 82 | assert(tmp.order.size() == tmp.zindices.size()); 83 | const auto cmp = [this](size_t a, size_t b) -> int { 84 | return tmp.zindices.at(a) < tmp.zindices.at(b); 85 | }; 86 | if (params.stableSort) { 87 | std::stable_sort(tmp.order.begin(), tmp.order.end(), cmp); 88 | } else { 89 | std::sort(tmp.order.begin(), tmp.order.end(), cmp); 90 | } 91 | 92 | elements.reserve(tmp.elements.size()); 93 | zindices.reserve(tmp.zindices.size()); 94 | for (size_t idx : tmp.order) { 95 | const auto zidx = tmp.zindices.at(idx); 96 | if (zidx == INVALID_COORD) break; 97 | else assert(zidx < INVALID_COORD); 98 | // log_debug("order: %zu (zindex %lx)", idx, zidx); 99 | zindices.push_back(tmp.zindices.at(idx)); 100 | elements.push_back(tmp.elements.at(idx)); 101 | } 102 | } 103 | 104 | template void removeData(const Predicate &func) { 105 | auto zIdxIn = zindices.begin(); 106 | auto elemIn = elements.begin(); 107 | auto zIdxOut = zIdxIn; 108 | auto elemOut = elemIn; 109 | while (elemIn != elements.end()) { 110 | if (!func(**elemIn)) { 111 | *elemOut++ = *elemIn; 112 | *zIdxOut++ = *zIdxIn; 113 | } 114 | elemIn++; 115 | zIdxIn++; 116 | } 117 | const size_t nLeft = elemOut - elements.begin(); 118 | elements.resize(nLeft); 119 | zindices.resize(nLeft); 120 | } 121 | 122 | void clearWorkspace() { 123 | tmp = {}; // should deallocate 124 | } 125 | 126 | class ElementRange { 127 | public: 128 | using Iterator = const Element * const *; 129 | Iterator begin() const { return b; } 130 | Iterator end() const { return e; } 131 | 132 | size_t size() const { 133 | return e - b; 134 | } 135 | 136 | ElementRange(Iterator b, Iterator e) : b(b), e(e) {} 137 | 138 | private: 139 | Iterator b = nullptr, e = nullptr; 140 | }; 141 | 142 | class NodeRange; 143 | 144 | struct Node { 145 | Node(const ZOrderOctree &t, ZIndex zidx, int level) : 146 | tree(&t), 147 | level(level), 148 | zindex(zidx) 149 | { 150 | ZIndex mask = levelMask(level); 151 | zindex = zindex & mask; 152 | elementsBegin = tree->findRange(zindex, mask, true); 153 | elementsEnd = tree->findRange(zindex, mask, false); 154 | // log_debug("node %lx at level %d, elements %zu to %zu", zindex, level, elementsBegin, elementsEnd); 155 | } 156 | 157 | // "end node" marker 158 | Node() : 159 | tree(nullptr), 160 | level(-1), 161 | zindex(0), 162 | elementsBegin(0), 163 | elementsEnd(0) 164 | {} 165 | 166 | int getLevel() const { 167 | return level; 168 | } 169 | 170 | bool isLeaf() const { 171 | return level == 0 || isEndNode() || empty(); 172 | } 173 | 174 | Node firstChild() const { 175 | assert(!isLeaf()); 176 | return Node(*tree, tree->zindices.at(elementsBegin), level - 1); 177 | } 178 | 179 | bool isLastAtThisLevel() const { 180 | if (isEndNode()) return true; 181 | return elementsEnd == tree->zindices.size(); 182 | } 183 | 184 | bool isLastSibling() const { 185 | if (isLastAtThisLevel()) return true; 186 | ZIndex parentMask = levelMask(level + 1); 187 | return (zindex & parentMask) != (tree->zindices.at(elementsEnd) & parentMask); 188 | } 189 | 190 | Node nextAtThisLevel() const { 191 | assert(!isLastAtThisLevel()); 192 | return Node(*tree, tree->zindices.at(elementsEnd), level); 193 | } 194 | 195 | Node nextSibling() const { 196 | assert(!isLastSibling()); 197 | return nextAtThisLevel(); 198 | } 199 | 200 | bool isEndNode() const { 201 | return tree == nullptr; 202 | } 203 | 204 | bool operator==(const Node &other) const { 205 | if (isEndNode()) return other.isEndNode(); 206 | return tree == other.tree && 207 | level == other.level && 208 | zindex == other.zindex; 209 | } 210 | 211 | NodeRange children() const { 212 | assert(!isLeaf()); 213 | Node child = firstChild(); 214 | assert(!child.empty()); 215 | return NodeRange(child, true); 216 | } 217 | 218 | ElementRange elements() const { 219 | assert(!isEndNode()); 220 | return tree->buildRange(elementsBegin, elementsEnd); 221 | } 222 | 223 | bool empty() const { 224 | return elementsEnd == elementsBegin; 225 | } 226 | 227 | Vector3 minCorner() const { 228 | return tree->zIndexToPoint(zindex, level, 0); 229 | } 230 | 231 | Vector3 maxCorner() const { 232 | return tree->zIndexToPoint(zindex, level, 1); 233 | } 234 | 235 | Vector3 center() const { 236 | return tree->zIndexToPoint(zindex, level, 0.5); 237 | } 238 | 239 | Float sideLength() const { 240 | return tree->params.leafSize * (1 << level); 241 | } 242 | 243 | bool isRoot() const { 244 | return level == tree->params.rootLevel; 245 | } 246 | 247 | // internal, do not use directly 248 | const ZOrderOctree *tree; 249 | int level; 250 | ZIndex zindex; 251 | size_t elementsBegin, elementsEnd; 252 | }; 253 | 254 | class LevelIterator { 255 | public: 256 | LevelIterator(const Node &node, bool siblingsOnly) : node(node), siblings(siblingsOnly) {} 257 | 258 | const Node &operator*() const { 259 | assert(!node.isEndNode()); 260 | return node; 261 | } 262 | 263 | LevelIterator &operator++() { // prefix OP, ++itr 264 | if ((siblings && node.isLastSibling()) || 265 | (!siblings && node.isLastAtThisLevel())) { 266 | node = Node(); 267 | } else { 268 | node = node.nextAtThisLevel(); 269 | } 270 | return *this; 271 | } 272 | 273 | bool operator==(const LevelIterator &other) const { 274 | return node == other.node && siblings == other.siblings; 275 | } 276 | 277 | bool operator!=(const LevelIterator &other) const { 278 | return !(*this == other); 279 | } 280 | 281 | private: 282 | Node node; 283 | const bool siblings; 284 | }; 285 | 286 | class NodeRange { 287 | private: 288 | LevelIterator b, e; 289 | 290 | public: 291 | NodeRange(Node beginNode, bool siblingsOnly) : 292 | b(beginNode, siblingsOnly), 293 | e(Node(), siblingsOnly) 294 | {} 295 | LevelIterator begin() const { return b; } 296 | LevelIterator end() const { return e; } 297 | bool empty() const { return b == e; } 298 | }; 299 | 300 | template Node lookup(const Point &point, int level) const { 301 | assert(level >= 0 && level < int(params.rootLevel)); 302 | ZIndex zindex = getZIndex(point); 303 | if (zindex == INVALID_COORD) return Node(); 304 | 305 | return Node(*this, zindex, level); 306 | } 307 | 308 | Node root() const { 309 | return Node(*this, zindices.empty() ? 0 : *zindices.begin(), params.rootLevel); 310 | } 311 | 312 | NodeRange nodesAtLevel(int level) const { 313 | assert(level >= 0 && level < int(params.rootLevel)); 314 | return NodeRange(Node(*this, zindices.empty() ? 0 : *zindices.begin(), level), false); 315 | } 316 | 317 | class RadiusSearchIterator { 318 | public: 319 | const Element *operator*() const { 320 | assert(currentNodeIdx < nodeCount); 321 | return nodes[currentNodeIdx].tree->elements.at(currentElementIdx); 322 | } 323 | 324 | RadiusSearchIterator &operator++() { // prefix OP, ++itr 325 | assert(currentNodeIdx < nodeCount && currentNodeIdx < 8); 326 | findNext(false); 327 | return *this; 328 | } 329 | 330 | // note: do not compare these from two unrelated ranges 331 | bool operator==(const RadiusSearchIterator &other) const { 332 | return currentNodeIdx == other.currentNodeIdx && 333 | currentElementIdx == other.currentElementIdx; 334 | } 335 | 336 | bool operator!=(const RadiusSearchIterator &other) const { 337 | return !(*this == other); 338 | } 339 | 340 | RadiusSearchIterator( 341 | size_t nodeCount, 342 | bool end = true, 343 | Float searchRadius = 0, 344 | const Vector3 *searchCenter = nullptr, 345 | Node *nodesPtr = nullptr) 346 | : 347 | currentNodeIdx(nodeCount), 348 | currentElementIdx(0), 349 | nodeCount(nodeCount) 350 | { 351 | assert(nodeCount <= 8); 352 | if (!end) { 353 | searchRadiusSquared = searchRadius*searchRadius; 354 | this->searchCenter = *searchCenter; 355 | for (size_t i = 0; i < nodeCount; ++i) nodes[i] = nodesPtr[i]; 356 | currentNodeIdx = 0; 357 | if (nodeCount > 0) { 358 | currentElementIdx = nodes[currentNodeIdx].elementsBegin; 359 | findNext(true); 360 | } 361 | } 362 | // log_debug("iterator %zu/%zu, %zu", currentNodeIdx, nodeCount, currentElementIdx); 363 | } 364 | 365 | size_t getNodeCount() const { 366 | return nodeCount; 367 | } 368 | 369 | bool containsAllElements() const { 370 | if (nodeCount == 0) return false; 371 | assert(nodes[0].tree); 372 | const auto &tree = *nodes[0].tree; 373 | if (nodeCount == 1) return nodes[0].level == int(tree.params.rootLevel); 374 | if (nodeCount == 8) return nodes[0].level == int(tree.params.rootLevel) - 1; 375 | return false; 376 | } 377 | 378 | private: 379 | void findNext(bool checkFirst) { 380 | if (currentNodeIdx >= nodeCount) return; // alerady at end 381 | 382 | bool checkCurrent = checkFirst; 383 | while (true) { 384 | //log_debug("search %zu/%zu, %zu in %zu - %zu %s", currentNodeIdx, nodeCount, currentElementIdx, 385 | // nodes[currentNodeIdx].elementsBegin, nodes[currentNodeIdx].elementsEnd, checkCurrent ? " cur" : ""); 386 | if (checkCurrent && currentElementIdx < nodes[currentNodeIdx].elementsEnd) { 387 | const Element &el = *nodes[currentNodeIdx].tree->elements.at(currentElementIdx); 388 | Float r2 = 0; 389 | for (int c = 0; c < 3; ++c) { 390 | Float d = el[c] - searchCenter[c]; 391 | r2 += d*d; 392 | } 393 | // log_debug("%g/%g (%g, %g, %g)", std::sqrt(r2), std::sqrt(searchRadiusSquared), el[0], el[1], el[2]); 394 | if (r2 < searchRadiusSquared) break; 395 | } 396 | checkCurrent = true; 397 | 398 | if (++currentElementIdx >= nodes[currentNodeIdx].elementsEnd) { 399 | if (++currentNodeIdx < nodeCount) { 400 | currentElementIdx = nodes[currentNodeIdx].elementsBegin; 401 | } else { 402 | // end node 403 | currentElementIdx = 0; 404 | break; 405 | } 406 | } 407 | } 408 | } 409 | 410 | size_t currentNodeIdx, currentElementIdx; 411 | size_t nodeCount; 412 | Node nodes[8]; 413 | Vector3 searchCenter; 414 | Float searchRadiusSquared; 415 | }; 416 | 417 | class RadiusSearchRange { 418 | private: 419 | RadiusSearchIterator b, e; 420 | 421 | public: 422 | RadiusSearchRange(RadiusSearchIterator begin) : 423 | b(begin), e(begin.getNodeCount()) 424 | {} 425 | 426 | RadiusSearchIterator begin() const { return b; } 427 | RadiusSearchIterator end() const { return e; } 428 | bool empty() const { return b == e; } 429 | bool containsAllElements() const { return b.containsAllElements(); } 430 | }; 431 | 432 | template RadiusSearchRange searchWithRadius(const Point &point, Float radius) const { 433 | size_t searchLevel = 0; 434 | const Vector3 searchCenter = { point[0], point[1], point[2] }; 435 | while (nodeCountWithinBox(searchCenter, radius, searchLevel) > 8) { 436 | assert(searchLevel < params.rootLevel); 437 | searchLevel++; 438 | } 439 | 440 | Node nodes[8]; 441 | size_t nodeCount = 0; 442 | for (int dx = -1; dx <= 1; dx += 2) { 443 | for (int dy = -1; dy <= 1; dy += 2) { 444 | for (int dz = -1; dz <= 1; dz += 2) { 445 | Vector3 corner = searchCenter; 446 | corner[0] += dx * radius; 447 | corner[1] += dy * radius; 448 | corner[2] += dz * radius; 449 | ZIndex zindex = getZIndex(corner, true) & levelMask(searchLevel); 450 | // log_debug("corner (%g, %g, %g) zindex %lx", corner[0], corner[1], corner[2], zindex); 451 | for (size_t j = 0; j < nodeCount; ++j) { 452 | if (zindex == nodes[j].zindex) { 453 | // log_debug("equal to corner %zu", j); 454 | goto NEXT_CORNER; 455 | } 456 | } 457 | 458 | assert(nodeCount < 8); 459 | nodes[nodeCount++] = Node(*this, zindex, searchLevel); 460 | 461 | NEXT_CORNER: (void)0; 462 | } 463 | } 464 | } 465 | // assert(nodeCount == nodeCountWithinBox(searchCenter, radius, searchLevel)); 466 | return RadiusSearchRange(RadiusSearchIterator(nodeCount, false, radius, &searchCenter, nodes)); 467 | } 468 | 469 | // note: not thread-safe & modifies workspace 470 | template void kNearestNeighbors(const Point ¢er, size_t k, std::vector &result, Float maxRadius = -1) { 471 | result.clear(); 472 | if (k == 0) return; 473 | 474 | auto &heap = tmp.knnHeap; 475 | heap.clear(); 476 | 477 | Float searchRadius = params.leafSize * 0.5; 478 | while (true) { 479 | if (maxRadius > 0 && searchRadius > maxRadius) searchRadius = maxRadius; 480 | HeapElement heapEl; 481 | auto search = searchWithRadius(center, searchRadius); 482 | for (const Element *el : search) { 483 | heapEl.r2 = 0; 484 | for (int c = 0; c < 3; ++c) { 485 | Float d = (*el)[c] - center[c]; 486 | heapEl.r2 += d*d; 487 | } 488 | heapEl.element = el; 489 | if (heap.size() < k) { 490 | heap.push_back(heapEl); 491 | if (heap.size() == k) std::make_heap(heap.begin(), heap.end()); 492 | } else if (!(heap.front() < heapEl)) { 493 | std::pop_heap(heap.begin(), heap.end()); 494 | heap.back() = heapEl; 495 | std::push_heap(heap.begin(), heap.end()); 496 | } 497 | } 498 | // log_debug("radius %g, heap size %zu", searchRadius, heap.size()); 499 | if (search.containsAllElements() || heap.size() == k || searchRadius == maxRadius) break; 500 | searchRadius *= 2; 501 | } 502 | 503 | if (params.sortKnnResponse) { 504 | if (params.stableSort) std::stable_sort(heap.begin(), heap.end()); 505 | else std::sort(heap.begin(), heap.end()); 506 | } 507 | result.reserve(heap.size()); 508 | for (const auto &e : heap) result.push_back(e.element); 509 | } 510 | 511 | private: 512 | struct HeapElement { 513 | const Element *element; 514 | Float r2; 515 | 516 | bool operator<(const HeapElement &other) const { 517 | return r2 < other.r2; 518 | } 519 | }; 520 | 521 | struct Workspace { 522 | std::vector order; 523 | std::vector zindices; 524 | std::vector elements; 525 | std::vector knnHeap; 526 | 527 | void clear() { 528 | order.clear(); 529 | zindices.clear(); 530 | elements.clear(); 531 | } 532 | 533 | void reserve(size_t n) { 534 | order.reserve(n); 535 | zindices.reserve(n); 536 | elements.reserve(n); 537 | } 538 | } tmp; 539 | 540 | ElementRange buildRange(size_t elementsBegin, size_t elementsEnd) const { 541 | return ElementRange( 542 | elements.data() + elementsBegin, 543 | elements.data() + elementsEnd); 544 | } 545 | 546 | static ZIndex levelMask(int level) { 547 | ZIndex mask = 0; 548 | for (int l = 0; l < level; ++l) { 549 | mask = (mask << 3l) | 0x7l; 550 | } 551 | return std::numeric_limits::max() ^ mask; 552 | } 553 | 554 | static Vector3 saxpy(Float alpha, const Vector3 &x, const Vector3 &y) { 555 | Vector3 r; 556 | for (size_t i = 0; i < 3; ++i) r[i] = alpha * x[i] + y[i]; 557 | return r; 558 | } 559 | 560 | static constexpr ZIndex INVALID_COORD = std::numeric_limits::max(); 561 | 562 | size_t findRange(ZIndex target, ZIndex mask, bool findBegin) const { 563 | // binary search 564 | size_t begin = 0; 565 | size_t end = zindices.size(); 566 | while (end > begin) { 567 | size_t mid = begin + (end - begin) / 2; 568 | ZIndex cur = zindices[mid] & mask; 569 | // log_debug("findRange [%zu, %zu, %zu] -> [?, %lx, ?]", begin, mid, end, cur); 570 | if ((findBegin && (cur < target)) || (!findBegin && (cur <= target))) { 571 | if (begin == mid) mid++; 572 | begin = mid; 573 | } else { 574 | end = mid; 575 | } 576 | } 577 | return end; 578 | } 579 | 580 | inline int maxCoord() const { 581 | return 1 << params.rootLevel; 582 | } 583 | 584 | inline int halfMaxCoord() const { 585 | return (1 << params.rootLevel) / 2; 586 | } 587 | 588 | inline int floatToCoord(Float c, int dim, bool capped) const { 589 | const int max = maxCoord(); 590 | Float rel = (c - params.origin[dim]) / params.leafSize; 591 | 592 | const Float maxF = max; // avoid integer overflow issues 593 | rel = std::min(rel, maxF); 594 | rel = std::max(rel, -maxF); 595 | int coord = std::floor(rel) + halfMaxCoord(); 596 | 597 | if (coord < 0) { 598 | if (capped) return 0; 599 | return -1; 600 | } else if (coord >= max) { 601 | if (capped) return max - 1; 602 | return -1; 603 | } 604 | return coord; 605 | } 606 | 607 | Vector3 zIndexToPoint(ZIndex zindex, int level, Float cellOffset) const { 608 | int coords[3] = { 0, 0, 0 }; 609 | for (int l = params.rootLevel; l >= level; --l) { 610 | for (int d = 0; d < 3; ++d) { 611 | int bit = (zindex >> (3*l + d)) & 0x1; 612 | if (bit) coords[d] += 1 << l; 613 | } 614 | } 615 | Vector3 v; 616 | const Float offs = params.leafSize * (1 << level) * cellOffset; 617 | for (int d = 0; d < 3; ++d) { 618 | v[d] = (coords[d] - halfMaxCoord()) * params.leafSize + params.origin[d] + offs; 619 | } 620 | return v; 621 | } 622 | 623 | template ZIndex getZIndex(const Point &xyz, bool capped = false) const { 624 | ZIndex zindex = 0; 625 | for (int d = 0; d < 3; ++d) { 626 | int coord = floatToCoord(xyz[d], d, capped); 627 | if (coord < 0) return INVALID_COORD; 628 | zindex |= interleaveBits(coord) << d; 629 | } 630 | // log_debug("getZIndex -> %lx", zindex); 631 | return zindex; 632 | } 633 | 634 | size_t nodeCountWithinBox(const Vector3 ¢er, Float radius, int level) const { 635 | size_t count = 1; 636 | for (int d = 0; d < 3; ++d) { 637 | int range = 1; 638 | for (int sign = -1; sign <= 1; sign += 2) { 639 | int coord = floatToCoord(center[d] + sign*radius, d, true); 640 | assert(coord >= 0); 641 | coord = coord >> level; 642 | range += coord * sign; 643 | } 644 | assert(range >= 0); 645 | count *= range; 646 | } 647 | // log_debug("nodeCountWithinBox %g, %d -> %zu", radius, level, count); 648 | return count; 649 | } 650 | 651 | static ZIndex interleaveBits(ZIndex coord) { 652 | auto x = static_cast(coord); 653 | // https://stackoverflow.com/a/18528775/1426569 654 | x &= 0x1fffff; 655 | x = (x | x << 32) & 0x1f00000000ffffll; 656 | x = (x | x << 16) & 0x1f0000ff0000ffll; 657 | x = (x | x << 8) & 0x100f00f00f00f00fll; 658 | x = (x | x << 4) & 0x10c30c30c30c30c3ll; 659 | x = (x | x << 2) & 0x1249249249249249ll; 660 | return static_cast(x); 661 | } 662 | 663 | const Parameters params; 664 | std::vector zindices; 665 | std::vector elements; 666 | }; 667 | -------------------------------------------------------------------------------- /SRC/api.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Spectacular AI Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #define ISO_OCTREE_API_IMPL 18 | #include "../include/IsoOctree/IsoOctree.hpp" 19 | 20 | #define MARCHING_CUBES_IMPL // TODO 21 | 22 | #include 23 | #include "IsoOctree.h" 24 | #include "VertexData.h" 25 | #include "Geometry.h" 26 | #include "MAT.h" 27 | #include "logging.hpp" 28 | #include "ZOrderOctree.hpp" 29 | 30 | namespace { 31 | template 32 | struct NodeDataImpl { 33 | int mcIndex; 34 | Point3D center; 35 | VertexData v; 36 | }; 37 | 38 | template 39 | struct VertexImpl { 40 | Point3D point; 41 | }; 42 | 43 | template isoOctree::Point3D getMappedCornerPosition( 44 | const typename isoOctree::Octree::RootInfo &root, 45 | Real x, Real y, Real z) 46 | { 47 | return { 48 | Real((x - 0.5) * root.width + root.center[0]), 49 | Real((y - 0.5) * root.width + root.center[1]), 50 | Real((z - 0.5) * root.width + root.center[2]) 51 | }; 52 | } 53 | 54 | template isoOctree::Point3D getMappedCornerPosition( 55 | const typename isoOctree::Octree &root, 56 | const Point3D ¢er) 57 | { 58 | return getMappedCornerPosition(root, center.coords[0], center.coords[1], center.coords[2]); 59 | } 60 | 61 | template typename isoOctree::Octree::Voxel convertIdx( 62 | const NodeIndex &nIdx, 63 | const typename isoOctree::Octree::RootInfo &root) 64 | { 65 | typename isoOctree::Octree::Voxel idxApi; 66 | using isoOctree::MeshIndex; 67 | idxApi.depth = nIdx.depth; 68 | idxApi.index = { MeshIndex(nIdx.offset[0]), MeshIndex(nIdx.offset[1]), MeshIndex(nIdx.offset[2]) }; 69 | const MeshIndex idxUpperBound = MeshIndex(1) << nIdx.depth; 70 | idxApi.width = root.width / idxUpperBound; 71 | idxApi.minCorner = getMappedCornerPosition(root, 72 | Real(nIdx.offset[0]) / idxUpperBound, 73 | Real(nIdx.offset[1]) / idxUpperBound, 74 | Real(nIdx.offset[2]) / idxUpperBound); 75 | return idxApi; 76 | } 77 | 78 | template 79 | void PolygonToTriangleMesh(const std::vector& vertices,const std::vector< std::vector >& polygons, 80 | isoOctree::MeshInfo &output) 81 | { 82 | MinimalAreaTriangulation mat; 83 | output.triangles.clear(); 84 | 85 | std::vector< Point3D > loop; 86 | std::vector vertexMap; 87 | std::vector tgl; 88 | 89 | for(size_t i=0;i 114 | void PolygonToManifoldTriangleMesh( std::vector& vertices , const std::vector< std::vector >& polygons , 115 | isoOctree::MeshInfo &output) 116 | { 117 | std::array t; 118 | output.triangles.clear(); 119 | for( int i=0 ; i3 ) 126 | { 127 | Point3D< Real > center; 128 | center *= 0; 129 | for( int j=0 ; j 149 | void buildMeshAny(Traverser &traverser, isoOctree::MeshInfo &output) 150 | { 151 | static bool isoOctreeCaseInit = false; 152 | if (!isoOctreeCaseInit) { 153 | isoOctreeCaseInit = true; 154 | MarchingCubes::SetCaseTable(); 155 | MarchingCubes::SetFullCaseTable(); 156 | } 157 | 158 | using VertexVal = VertexValue; 159 | IsoOctree, Real, VertexVal> isoTree; 160 | isoTree.set(traverser); 161 | 162 | log_debug("Nodes In: %d / %d", isoTree.tree.nodes(), isoTree.tree.leaves()); 163 | log_debug("Values In: %zu", isoTree.cornerValues.size()); 164 | 165 | bool fullMarchingCubes = true; 166 | bool manifoldVersion = false; 167 | 168 | std::vector> vertices; 169 | std::vector > polygons; 170 | isoTree.getIsoSurface(0, vertices, polygons, fullMarchingCubes); 171 | log_debug("Vertices %zu, Polygons: %zu", vertices.size(), polygons.size()); 172 | 173 | if(manifoldVersion) { 174 | PolygonToManifoldTriangleMesh, Real>(vertices, polygons, output); 175 | } else { 176 | PolygonToTriangleMesh, Real>(vertices, polygons, output); 177 | } 178 | 179 | output.vertices.clear(); 180 | const auto &root = traverser.root(); 181 | for (auto &vertex : vertices) { 182 | output.vertices.push_back(getMappedCornerPosition(root, vertex.point[0], vertex.point[1], vertex.point[2])); 183 | } 184 | 185 | log_debug("Vertices: %zu", output.vertices.size()); 186 | log_debug("Triangles: %zu", output.triangles.size()); 187 | } 188 | 189 | template 190 | void buildMeshWithPointCloudHintAny( 191 | const BuildMeshFunc &buildMeshFunc, 192 | const typename isoOctree::Octree::PointCloudHint &hint, 193 | isoOctree::MeshInfo &output) 194 | { 195 | log_debug("building mesh from point cloud hint with %zu point(s)", hint.nPoints); 196 | output.triangles.clear(); 197 | output.vertices.clear(); 198 | 199 | if (hint.nPoints == 0) { 200 | log_warn("empty point cloud hint: returning empty mesh"); 201 | return; 202 | } 203 | 204 | using Octree = isoOctree::Octree; 205 | using Point3D = isoOctree::Point3D; 206 | 207 | typename Octree::RootInfo root; 208 | root.maxDepth = hint.maxDepth; 209 | 210 | { 211 | Point3D minCorner = hint.points[0]; 212 | Point3D maxCorner = hint.points[0]; 213 | 214 | for (std::size_t i = 0; i < hint.nPoints; i++) { 215 | for (int j = 0; j < 3; j++) { 216 | minCorner[j] = std::min(minCorner[j], hint.points[i][j]); 217 | maxCorner[j] = std::max(maxCorner[j], hint.points[i][j]); 218 | } 219 | } 220 | 221 | root.width = std::max( 222 | maxCorner[0] - minCorner[0], 223 | std::max( 224 | maxCorner[1] - minCorner[1], 225 | maxCorner[2] - minCorner[2])); 226 | 227 | if (root.width <= 0) { 228 | log_warn("point cloud hint has zero width: returning empty mesh"); 229 | return; 230 | } 231 | 232 | root.center = { 233 | (minCorner[0] + maxCorner[0]) / 2, 234 | (minCorner[1] + maxCorner[1]) / 2, 235 | (minCorner[2] + maxCorner[2]) / 2 236 | }; 237 | } 238 | 239 | using SearchTree = ZOrderOctree; 240 | 241 | typename SearchTree::Parameters searchTreeParameters; 242 | // OK to cap. Just gets slower to search 243 | searchTreeParameters.rootLevel = std::min(hint.maxDepth + 1, (int)SearchTree::MAX_ROOT_LEVEL); 244 | searchTreeParameters.leafSize = root.width / (1 << searchTreeParameters.rootLevel); 245 | searchTreeParameters.origin = root.center; 246 | 247 | SearchTree searchTree(searchTreeParameters); 248 | searchTree.addData(hint.points, hint.nPoints); 249 | 250 | buildMeshFunc( 251 | root, 252 | [&searchTree, &hint]( 253 | const typename Octree::Voxel &voxel, 254 | const typename Octree::CornerValues &corners) -> bool 255 | { 256 | (void)corners; 257 | Point3D voxelCenter = { 258 | Real(0.5) * voxel.width + voxel.minCorner[0], 259 | Real(0.5) * voxel.width + voxel.minCorner[1], 260 | Real(0.5) * voxel.width + voxel.minCorner[2] 261 | }; 262 | 263 | // note: searches beyond voxel bounds on purpose 264 | Real searchRadius = voxel.width; 265 | std::size_t nMatches = 0; 266 | for (const auto &match : searchTree.searchWithRadius(voxelCenter, searchRadius)) { 267 | nMatches++; 268 | } 269 | 270 | return nMatches >= hint.subdivisionThreshold; 271 | }); 272 | } 273 | } 274 | 275 | namespace isoOctree { 276 | template 277 | void buildMesh(typename isoOctree::Octree::Traverser &traverser, isoOctree::MeshInfo &output) { 278 | buildMeshAny(traverser, output); 279 | } 280 | 281 | template 282 | void buildMesh(typename isoOctree::Octree::VectorizedTraverser &traverser, isoOctree::MeshInfo &output) { 283 | buildMeshAny(traverser, output); 284 | } 285 | 286 | template 287 | void buildMeshWithPointCloudHint( 288 | const std::function &)> &isoFunction, 289 | const typename Octree::PointCloudHint &hint, 290 | MeshInfo &output) 291 | { 292 | using Octree = Octree; 293 | using RootInfo = typename Octree::RootInfo; 294 | using IsoFunction = std::function &)>; 295 | using ShouldExpandCallback = std::function; 298 | 299 | class TraverserImpl : public Octree::Traverser { 300 | private: 301 | const IsoFunction &isoFunction; 302 | const ShouldExpandCallback &shouldExpandCallback; 303 | const RootInfo &rootInfo; 304 | 305 | public: 306 | const RootInfo &root() final { return rootInfo; } 307 | 308 | bool shouldExpand( 309 | const typename Octree::Voxel &voxel, 310 | const typename Octree::CornerValues &corners) final 311 | { 312 | return shouldExpandCallback(voxel, corners); 313 | } 314 | 315 | float isoValue(const Point3D &point) final { 316 | return isoFunction(point); 317 | } 318 | 319 | TraverserImpl( 320 | const IsoFunction &isoFunction, 321 | const ShouldExpandCallback &cb, 322 | const RootInfo &r 323 | ) : 324 | isoFunction(isoFunction), 325 | shouldExpandCallback(cb), 326 | rootInfo(r) 327 | {} 328 | }; 329 | 330 | buildMeshWithPointCloudHintAny([&isoFunction, &output]( 331 | const RootInfo &rootInfo, 332 | const ShouldExpandCallback &shouldExpandCallback 333 | ) { 334 | TraverserImpl traverser(isoFunction, shouldExpandCallback, rootInfo); 335 | buildMesh(traverser, output); 336 | 337 | }, hint, output); 338 | } 339 | 340 | template 341 | void buildMeshWithPointCloudHint( 342 | const std::function> &, std::vector &)> &isoFunction, 343 | const typename Octree::PointCloudHint &hint, 344 | MeshInfo &output) 345 | { 346 | using Octree = Octree; 347 | using RootInfo = typename Octree::RootInfo; 348 | using IsoFunction = std::function> &, std::vector &)>; 349 | using ShouldExpandCallback = std::function; 352 | 353 | class TraverserImpl : public Octree::VectorizedTraverser { 354 | private: 355 | const IsoFunction &isoFunction; 356 | const ShouldExpandCallback &shouldExpandCallback; 357 | const RootInfo &rootInfo; 358 | 359 | public: 360 | const RootInfo &root() final { return rootInfo; } 361 | 362 | bool shouldExpand( 363 | const typename Octree::Voxel &voxel, 364 | const typename Octree::CornerValues &corners) final 365 | { 366 | return shouldExpandCallback(voxel, corners); 367 | } 368 | 369 | void isoValues(const std::vector> &points, std::vector &values) final { 370 | isoFunction(points, values); 371 | } 372 | 373 | TraverserImpl( 374 | const IsoFunction &f, 375 | const ShouldExpandCallback &cb, 376 | const RootInfo &r 377 | ) : 378 | isoFunction(f), 379 | shouldExpandCallback(cb), 380 | rootInfo(r) 381 | {} 382 | }; 383 | 384 | buildMeshWithPointCloudHintAny([&isoFunction, &output]( 385 | const RootInfo &rootInfo, 386 | const ShouldExpandCallback &shouldExpandCallback 387 | ) { 388 | TraverserImpl traverser(isoFunction, shouldExpandCallback, rootInfo); 389 | buildMesh(traverser, output); 390 | }, hint, output); 391 | } 392 | 393 | #define SPECIALIZE(real) \ 394 | template void buildMesh( \ 395 | Octree::Traverser &, \ 396 | MeshInfo &); \ 397 | template void buildMesh( \ 398 | Octree::VectorizedTraverser &, \ 399 | MeshInfo &); \ 400 | template void buildMeshWithPointCloudHint( \ 401 | const std::function &)> &, \ 402 | const typename Octree::PointCloudHint &, \ 403 | MeshInfo &); \ 404 | template void buildMeshWithPointCloudHint( \ 405 | const std::function> &, std::vector &)> &, \ 406 | const typename Octree::PointCloudHint &, \ 407 | MeshInfo &) 408 | 409 | SPECIALIZE(float); 410 | SPECIALIZE(double); 411 | #undef SPECIALIZE 412 | 413 | } 414 | 415 | template 416 | bool IsoOctree::set(typename isoOctree::Octree::Traverser &traverser) 417 | { 418 | maxDepth = traverser.root().maxDepth; 419 | assert(maxDepth > 0); 420 | typename OctNode::NodeIndex nIdx; // Initialized to zero 421 | cornerValues.clear(); 422 | 423 | setChildren(&tree, nIdx, traverser); 424 | 425 | return true; 426 | } 427 | 428 | template 429 | bool IsoOctree::set(typename isoOctree::Octree::VectorizedTraverser &traverser) { 430 | maxDepth = traverser.root().maxDepth; 431 | assert(maxDepth > 0); 432 | cornerValues.clear(); 433 | 434 | std::vector keyList; 435 | std::vector> evalList; 436 | std::vector valueList; 437 | 438 | const auto &root = traverser.root(); 439 | 440 | struct Node { 441 | typename OctNode::NodeIndex index; 442 | OctNode* data; 443 | }; 444 | 445 | std::vector nodes, nextNodes; 446 | nodes.push_back({ 447 | {}, // initialized to zero 448 | &tree 449 | }); 450 | 451 | for (int depth = 0; depth <= maxDepth; ++depth) { 452 | keyList.clear(); 453 | evalList.clear(); 454 | 455 | for (auto &node : nodes) { 456 | const auto &nIdx = node.index; 457 | 458 | Point3D ctr; 459 | Real w; 460 | OctNode::CenterAndWidth(nIdx,ctr,w); 461 | 462 | for (int i=0; i::CornerIndex(nIdx, i, root.maxDepth); 464 | if(cornerValues.find(key) == cornerValues.end()) { 465 | int x,y,z; 466 | Cube::FactorCornerIndex(i, x, y, z); 467 | isoOctree::Point3D coords = getMappedCornerPosition( 468 | root, 469 | ctr.coords[0] + Real(x - 0.5) * w, 470 | ctr.coords[1] + Real(y - 0.5) * w, 471 | ctr.coords[2] + Real(z - 0.5) * w 472 | ); 473 | keyList.push_back(key); 474 | evalList.push_back(coords); 475 | cornerValues[key] = VertexData(); // will be evaluated on the next pass 476 | } 477 | } 478 | } 479 | 480 | log_debug("level %d: evaluating %zu values", depth, keyList.size()); 481 | valueList.resize(evalList.size()); 482 | traverser.isoValues(evalList, valueList); 483 | for (std::size_t i = 0; i < keyList.size(); ++i) { 484 | cornerValues[keyList[i]] = VertexData(valueList[i]); 485 | } 486 | 487 | if (depth == maxDepth) break; 488 | 489 | nextNodes.clear(); 490 | for (auto &node : nodes) { 491 | const auto &nIdx = node.index; 492 | 493 | typename isoOctree::Octree::CornerValues apiCornerVals; 494 | for (int i=0; i::CornerIndex(nIdx, i, root.maxDepth); 496 | apiCornerVals[i] = cornerValues.at(key).v; 497 | } 498 | 499 | if (traverser.shouldExpand(convertIdx(nIdx, root), apiCornerVals)) { 500 | if (!node.data->children) node.data->initChildren(); 501 | for (int i=0; i::CornerIndex(nIdx, i, root.maxDepth); 503 | 504 | nextNodes.push_back({ 505 | nIdx.child(i), 506 | &node.data->children[i] 507 | }); 508 | } 509 | } 510 | } 511 | 512 | std::swap(nodes, nextNodes); 513 | } 514 | 515 | return true; 516 | } 517 | 518 | template 519 | void IsoOctree::setChildren( 520 | OctNode* node, 521 | const typename OctNode::NodeIndex& nIdx, 522 | typename isoOctree::Octree::Traverser &traverser) 523 | { 524 | long long key; 525 | Real w; 526 | Point3D ctr, p; 527 | const auto &root = traverser.root(); 528 | 529 | OctNode::CenterAndWidth(nIdx,ctr,w); 530 | 531 | typename isoOctree::Octree::CornerValues apiCornerVals; 532 | // log_debug("diii %d,%d,%d,%d", nIdx.depth, nIdx.offset[0], nIdx.offset[1], nIdx.offset[2]); 533 | 534 | for (int i=0; i::CornerIndex(nIdx, i, root.maxDepth); 536 | if(cornerValues.find(key) == cornerValues.end()) { 537 | int x,y,z; 538 | Cube::FactorCornerIndex(i, x, y, z); 539 | auto coords = getMappedCornerPosition( 540 | root, 541 | ctr.coords[0] + Real(x - 0.5) * w, 542 | ctr.coords[1] + Real(y - 0.5) * w, 543 | ctr.coords[2] + Real(z - 0.5) * w 544 | ); 545 | cornerValues[key] = VertexData(traverser.isoValue(coords)); 546 | // log_debug("xyz %g,%g,%g (%d,%d,%d,%d), %g/%g", coords[0], coords[1], coords[2], i,x,y,z, w, root.width); 547 | } 548 | 549 | apiCornerVals[i] = cornerValues.at(key).v; 550 | } 551 | 552 | if (nIdx.depth == root.maxDepth) return; 553 | 554 | if (!traverser.shouldExpand(convertIdx(nIdx, root), apiCornerVals)) { 555 | return; 556 | } 557 | 558 | if (!node->children) node->initChildren(); 559 | for (int i=0; ichildren[i], nIdx.child(i), traverser); 561 | } 562 | } 563 | -------------------------------------------------------------------------------- /SRC/hash_map: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Spectacular AI Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef HASH_MAP_STDEX_ADAPTER 18 | #define HASH_MAP_STDEX_ADAPTER 19 | #include 20 | #include 21 | 22 | namespace stdext { 23 | template using hash_map = std::unordered_map ; 24 | }; 25 | 26 | #endif -------------------------------------------------------------------------------- /SRC/logging.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2022 Spectacular AI Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef ISO_OCTREE_LOGGING_INCLUDED 18 | #define ISO_OCTREE_LOGGING_INCLUDED 19 | 20 | #include 21 | 22 | #ifndef NDEBUG 23 | #define ISO_OCTREE_DEBUG 24 | #endif 25 | 26 | #ifdef ISO_OCTREE_DEBUG 27 | #define log_debug(fmt, ...) ((void)std::fprintf(stdout, "IsoOctree DEBUG: " fmt "\n", ## __VA_ARGS__)) 28 | #else 29 | #define log_debug(fmt, ...) 30 | #endif 31 | #define log_error(fmt, ...) ((void)std::fprintf(stderr, "IsoOctree ERROR: " fmt "\n", ## __VA_ARGS__)) 32 | #define log_warn(fmt, ...) ((void)std::fprintf(stderr, "IsoOctree WARNING: " fmt "\n", ## __VA_ARGS__)) 33 | 34 | #endif -------------------------------------------------------------------------------- /VERSION.txt: -------------------------------------------------------------------------------- 1 | 2.0.1 -------------------------------------------------------------------------------- /build_and_test_python_library.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2024 Spectacular AI Ltd 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -eux 18 | 19 | rm -rf dist 20 | rm -rf python/target 21 | 22 | mkdir -p python/target 23 | cd python/target 24 | cmake -DCMAKE_BUILD_TYPE=Release .. 25 | make -j8 26 | cd ../.. 27 | 28 | python python/setup.py bdist_wheel 29 | 30 | cd python/target 31 | python -m venv venv_test 32 | source venv_test/bin/activate 33 | pip install ../../dist/*.whl 34 | cd .. 35 | python examples/simple.py -------------------------------------------------------------------------------- /build_library_and_example.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -eux 3 | 4 | # Copyright 2024 Spectacular AI Ltd 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | rm -rf target 19 | mkdir -p target 20 | cd target 21 | cmake -DCMAKE_INSTALL_PREFIX=/tmp/IsoOctreeInstall -DCMAKE_BUILD_TYPE=RelWithDebInfo .. 22 | make -j8 install 23 | cd .. 24 | 25 | cd example 26 | rm -rf target 27 | mkdir -p target 28 | cd target 29 | cmake -DCMAKE_PREFIX_PATH=/tmp/IsoOctreeInstall .. 30 | make -j8 31 | 32 | ./example test.obj 33 | wc test.obj 34 | -------------------------------------------------------------------------------- /cmake/IsoOctreeConfig.cmake: -------------------------------------------------------------------------------- 1 | include("${CMAKE_CURRENT_LIST_DIR}/IsoOctreeTargets.cmake") 2 | -------------------------------------------------------------------------------- /example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Spectacular AI Ltd 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | cmake_minimum_required(VERSION 3.3) 16 | project(IsoOctreeExample) 17 | 18 | add_executable(example main.cpp) 19 | find_package(IsoOctree REQUIRED) 20 | target_link_libraries(example PUBLIC IsoOctree::IsoOctree) 21 | -------------------------------------------------------------------------------- /example/main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Spectacular AI Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | class OctreeBuilder : public isoOctree::Octree::Traverser { 22 | static constexpr int BASE_DEPTH = 3; 23 | isoOctree::Octree::RootInfo rootInfo; 24 | 25 | public: 26 | OctreeBuilder() { 27 | rootInfo.maxDepth = 6; 28 | rootInfo.center = { 0.1, 0.2, 0.3 }; 29 | rootInfo.width = 6.0; 30 | } 31 | 32 | const isoOctree::Octree::RootInfo &root() final { 33 | return rootInfo; 34 | } 35 | 36 | float isoValue(const isoOctree::Point3D &point) final { 37 | float x = point[0], y = point[1], z = point[2]; 38 | return x*x*x*x - 5*x*x + y*y*y*y - 5*y*y + z*z*z*z - 5*z*z + 11.8; // Tanglecube 39 | } 40 | 41 | bool shouldExpand( 42 | const isoOctree::Octree::NodeIndex &node, 43 | const isoOctree::Octree::CornerValues &corners) final 44 | { 45 | if (node.depth < BASE_DEPTH) return true; 46 | 47 | int nNonNegative = 0, nNegative = 0; 48 | for (float v : corners) { 49 | if (v < 0.0) nNegative++; 50 | else nNonNegative++; 51 | } 52 | 53 | if (nNonNegative == 0 || nNegative == 0) return false; 54 | // Note: this logic is rather flawed for adaptive refinement, 55 | // but results to leaf nodes at different level, which is a good 56 | // test to the IsoOctree algorithm 57 | return nNonNegative != nNegative; 58 | } 59 | }; 60 | 61 | template 62 | void writeMeshAsObj(const isoOctree::MeshInfo &mesh, const char *outFileName) { 63 | std::ofstream out(outFileName); 64 | for (const auto &vertex : mesh.vertices) { 65 | out << "v"; 66 | for (auto c : vertex) out << " " << c; 67 | out << "\n"; 68 | } 69 | for (const auto &tri : mesh.triangles) { 70 | out << "f"; 71 | for (auto vi : tri) out << " " << (vi+1); 72 | out << "\n"; 73 | } 74 | } 75 | 76 | int main(int argc, char *argv[]) { 77 | isoOctree::MeshInfo mesh; 78 | OctreeBuilder builder; 79 | isoOctree::buildMesh(builder, mesh); 80 | if (argc > 1) { 81 | assert(argc == 2); 82 | writeMeshAsObj(mesh, argv[1]); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /include/IsoOctree/IsoOctree.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Spectacular AI Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #ifndef ISO_OCTREE_API_INCLUDED 18 | #define ISO_OCTREE_API_INCLUDED 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #ifdef _MSC_VER 28 | #define ISO_OCTREE_API __declspec(dllexport) 29 | #else 30 | #define ISO_OCTREE_API __attribute__((visibility("default"))) 31 | #endif 32 | 33 | namespace isoOctree { 34 | using MeshIndex = std::size_t; 35 | using TriangleIndex = std::array; 36 | template using Point3D = std::array; 37 | 38 | template 39 | struct MeshInfo { 40 | std::vector triangles; 41 | std::vector > vertices; 42 | // Dropped edgeNormals 43 | }; 44 | 45 | template struct Octree { 46 | 47 | struct Voxel { 48 | using Index = std::array; 49 | Index index; 50 | int depth; 51 | Point3D minCorner; 52 | Real width; 53 | }; 54 | 55 | struct RootInfo { 56 | Point3D center; 57 | Real width; 58 | int maxDepth; // must be known beforehand for practical reasons 59 | }; 60 | 61 | struct PointCloudHint { 62 | const Point3D *points; 63 | MeshIndex nPoints; 64 | int maxDepth; 65 | MeshIndex subdivisionThreshold; 66 | }; 67 | 68 | using CornerValues = std::array; 69 | 70 | using NodeIndex = Voxel; // backwards compatibility 71 | 72 | struct Traverser { 73 | virtual const RootInfo &root() = 0; 74 | virtual bool shouldExpand(const NodeIndex &node, const CornerValues &corners) = 0; 75 | virtual float isoValue(const Point3D &point) = 0; 76 | // no virtual dtor, do not delete this class 77 | }; 78 | 79 | // evaluates the function one level at a time 80 | struct VectorizedTraverser { 81 | virtual const RootInfo &root() = 0; 82 | virtual bool shouldExpand(const NodeIndex &node, const CornerValues &corners) = 0; 83 | virtual void isoValues(const std::vector> &points, std::vector &result) = 0; 84 | // no virtual dtor, do not delete this class 85 | }; 86 | }; 87 | 88 | template 89 | ISO_OCTREE_API void buildMesh(typename Octree::Traverser &traverser, MeshInfo &output); 90 | 91 | template 92 | ISO_OCTREE_API void buildMesh(typename Octree::VectorizedTraverser &traverser, MeshInfo &output); 93 | 94 | template 95 | ISO_OCTREE_API void buildMeshWithPointCloudHint( 96 | const std::function &)> &isoFunction, 97 | const typename Octree::PointCloudHint &hint, 98 | MeshInfo &output); 99 | 100 | template 101 | ISO_OCTREE_API void buildMeshWithPointCloudHint( 102 | const std::function> &, std::vector &)> &isoFunction, 103 | const typename Octree::PointCloudHint &hint, 104 | MeshInfo &output); 105 | } 106 | 107 | #endif 108 | -------------------------------------------------------------------------------- /python/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2024 Spectacular AI Ltd 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | cmake_minimum_required(VERSION 3.12) 16 | file(READ "${CMAKE_CURRENT_SOURCE_DIR}/../VERSION.txt" VERSION_CONTENTS) 17 | project(IsoOctree VERSION ${VERSION_CONTENTS}) 18 | 19 | option(IsoOctree_PYTHON "Build python bindings" OFF) 20 | set(IsoOctree_PYTHON ON) 21 | 22 | add_subdirectory(pybind11) 23 | add_subdirectory(.. ${CMAKE_BINARY_DIR}/IsoOctreeNative_build) 24 | 25 | pybind11_add_module(IsoOctree bindings.cpp) 26 | target_include_directories(IsoOctree PRIVATE 27 | ${CMAKE_CURRENT_SOURCE_DIR}/../include) 28 | target_link_libraries(IsoOctree PRIVATE IsoOctreeNative) -------------------------------------------------------------------------------- /python/README.rst: -------------------------------------------------------------------------------- 1 | 2 | IsoOctree Python meshing library 3 | -------------------------------- 4 | 5 | This library is a Python wrapper for Spectacular AI IsoOctree C++ library, 6 | which implements the *Unconstrained Isosurface Extraction on Arbitrary 7 | Octrees* algorithm, based on the original code by Michael Kazhdan. -------------------------------------------------------------------------------- /python/bindings.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024 Spectacular AI Ltd 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include "IsoOctree/IsoOctree.hpp" 23 | #include 24 | 25 | #define DEF_READONLY(klass, name, doc) def_readonly(#name, &klass::name, doc) 26 | #define DEF_COPY_READONLY(klass, name, doc) def_property_readonly(\ 27 | #name,\ 28 | [](const klass &self) { return self.name; },\ 29 | py::return_value_policy::copy, doc) 30 | 31 | 32 | namespace py = pybind11; 33 | 34 | namespace { 35 | constexpr int DEFAULT_MAX_DEPTH = 7; 36 | constexpr int DEFAULT_WIDTH = 1.0; 37 | constexpr int DEFAULT_SUBDIVISION_THRESHOLD = 50; 38 | 39 | using Real = double; 40 | using Octree = isoOctree::Octree; 41 | using Voxel = Octree::Voxel; 42 | using Point3D = isoOctree::Point3D; 43 | using CornerValues = Octree::CornerValues; 44 | using RootInfo = Octree::RootInfo; 45 | using MeshInfo = isoOctree::MeshInfo; 46 | 47 | using ShouldExpandCallback = std::function; 48 | using GetValueCallback = std::function; 49 | 50 | void convertCallback(GetValueCallback &pythonCallback, const std::vector &points, std::vector &values) { 51 | const long n = static_cast(points.size()); 52 | 53 | py::array ret = pythonCallback(py::array_t( 54 | std::vector { n, 3 }, 55 | reinterpret_cast(points.data()) 56 | )); 57 | 58 | if (ret.ndim() != 1) throw std::runtime_error("must return an 1-d array"); 59 | if (ret.shape(0) != n) throw std::runtime_error("must have the same number of values as there were input points"); 60 | 61 | ret = ret.attr("astype")(py::dtype("float32")); 62 | 63 | // Check if the array is already in row-major (C-contiguous) layout 64 | if (!ret.attr("flags").attr("c_contiguous").cast()) { 65 | // If not, make it row-major by copying the array 66 | ret = ret.attr("copy")("C"); 67 | } 68 | 69 | values.resize(n); 70 | const float *data = reinterpret_cast(ret.data()); 71 | std::copy(data, data + n, values.data()); 72 | } 73 | 74 | class CallbackTraverser : public Octree::VectorizedTraverser { 75 | private: 76 | RootInfo rootInfo; 77 | ShouldExpandCallback shouldExpandCallback; 78 | GetValueCallback getValueCallback; 79 | 80 | public: 81 | CallbackTraverser( 82 | const RootInfo &rootInfo, 83 | const ShouldExpandCallback &shouldExpandCallback, 84 | const GetValueCallback &getValueCallback) 85 | : 86 | rootInfo(rootInfo), 87 | shouldExpandCallback(shouldExpandCallback), 88 | getValueCallback(getValueCallback) 89 | {} 90 | 91 | const RootInfo &root() final { return rootInfo; } 92 | bool shouldExpand(const Voxel &voxel, const CornerValues &corners) final { 93 | return shouldExpandCallback(voxel, corners); 94 | } 95 | 96 | void isoValues(const std::vector &points, std::vector &values) final { 97 | convertCallback(getValueCallback, points, values); 98 | } 99 | }; 100 | } 101 | 102 | PYBIND11_MODULE(IsoOctree, m) { 103 | m.doc() = "IsoOctree Python wrapper"; 104 | 105 | py::class_(m, "Voxel") 106 | 107 | .DEF_READONLY(Voxel, index, 108 | "Level-specific octree node index triplet (i,j,k), each in the range [0, 2^depth-1]") 109 | 110 | .DEF_READONLY(Voxel, depth, 111 | "Octree level: 0 is root, 1 is the first level, etc.") 112 | 113 | .DEF_COPY_READONLY(Voxel, minCorner, 114 | "Voxel minimum coordinate corner in world coordinates") 115 | 116 | .DEF_READONLY(Voxel, width, 117 | "Voxel cube width"); 118 | 119 | py::class_>(m, "MeshInfo") 120 | 121 | .def_property_readonly("vertices", 122 | [](MeshInfo &self) { 123 | return py::array_t( 124 | std::vector { static_cast(self.vertices.size()), 3 }, 125 | reinterpret_cast(self.vertices.data()) 126 | ); 127 | }, 128 | "Vertices numpy array of shape (N, 3)") 129 | 130 | .def_property_readonly("triangles", 131 | [](MeshInfo &self) { 132 | return py::array_t( 133 | std::vector { static_cast(self.triangles.size()), 3 }, 134 | reinterpret_cast(self.triangles.data()) 135 | ); 136 | }, 137 | "Faces (all triangles) as an index array of shape (M, 3), each element\n" 138 | "is an index to the rows in vertices, and thus in the range (0, N-1)"); 139 | 140 | m.def("buildMesh", 141 | []( 142 | GetValueCallback isoFunction, 143 | ShouldExpandCallback expandFunction, 144 | Point3D center, 145 | Real width, 146 | int maxDepth) 147 | { 148 | Octree::RootInfo rootInfo; 149 | rootInfo.center = center; 150 | rootInfo.width = width; 151 | rootInfo.maxDepth = maxDepth; 152 | CallbackTraverser traverser(rootInfo, expandFunction, isoFunction); 153 | std::unique_ptr> output = std::make_unique>(); 154 | isoOctree::buildMesh(traverser, *output); 155 | return std::move(output); 156 | }, 157 | py::arg("isoFunction"), 158 | py::arg("expandFunction"), 159 | py::kw_only(), 160 | py::arg("center") = Point3D { 0, 0, 0 }, 161 | py::arg("width") = DEFAULT_WIDTH, 162 | py::arg("maxDepth") = DEFAULT_MAX_DEPTH, 163 | "Mesh an implicit function using the Iso-Octree algorithm \n" 164 | "\n" 165 | "Applied to a cubical region [cx-w/2, cx+w/2] x [cy-w/2, cy+w/2] x [cz-w/2, cz+w/2]\n" 166 | "called the (octree) root voxel.\n" 167 | "\n" 168 | "Arguments:\n" 169 | "\t isoFunction: Function f that defines the surface (f < 0 is inside)\n" 170 | "\t expandFunction: Function that decides whether to expand a Voxel\n" 171 | "\t center: Center (cx, cy, cz) of the root voxel\n" 172 | "\t width: Width of the root voxel w\n" 173 | "\t maxDepth: Maximum octree depth d. The smallest possible voxel dimension is w/2^d\n" 174 | "\n" 175 | "Returns: a MeshInfo object"); 176 | 177 | 178 | m.def("buildMeshWithPointCloudHint", 179 | []( 180 | GetValueCallback isoFunction, 181 | py::array pointCloud, 182 | int maxDepth, 183 | int subdivisionThreshold) 184 | { 185 | Octree::PointCloudHint hint; 186 | hint.nPoints = pointCloud.shape(0); 187 | hint.points = reinterpret_cast(pointCloud.data()); 188 | hint.maxDepth = maxDepth; 189 | hint.subdivisionThreshold = subdivisionThreshold; 190 | 191 | std::unique_ptr> output = std::make_unique>(); 192 | 193 | std::function &, std::vector &)> cppCallback = [&isoFunction]( 194 | const std::vector &points, 195 | std::vector &values) 196 | { 197 | convertCallback(isoFunction, points, values); 198 | }; 199 | 200 | isoOctree::buildMeshWithPointCloudHint(cppCallback, hint, *output); 201 | return std::move(output); 202 | }, 203 | py::arg("isoFunction"), 204 | py::arg("pointCloud"), 205 | py::kw_only(), 206 | py::arg("maxDepth") = DEFAULT_MAX_DEPTH, 207 | py::arg("subdivisionThreshold") = DEFAULT_SUBDIVISION_THRESHOLD, 208 | "Mesh an implicit function using the Iso-Octree algorithm \n" 209 | "using a point cloud hint. Applied to the minimal cubical region that contains\n" 210 | "the given point cloud.\n" 211 | "\n" 212 | "Arguments:\n" 213 | "\t isoFunction: Function f that defines the surface (f < 0 is inside)\n" 214 | "\t pointCloud: Point cloud to determine the extend and use to determine subdivision\n" 215 | "\t maxDepth: Maximum octree depth d. The smallest possible voxel dimension is w/2^d\n" 216 | "\t subdivisionThreshold: Subdivide a voxel if there are at least this many points in its neighborhood\n" 217 | "\n" 218 | "Returns: a MeshInfo object"); 219 | } 220 | 221 | -------------------------------------------------------------------------------- /python/examples/cayley.py: -------------------------------------------------------------------------------- 1 | import IsoOctree 2 | import numpy as np 3 | 4 | def getValue(p): 5 | x, y, z = [p[:, i] for i in range(3)] 6 | 7 | # Cayley cubic 8 | return x**2 + y**2 - x**2*z + y**2*z + z**2 - 1 9 | 10 | def getGradient(p): 11 | x, y, z = [p[:, i] for i in range(3)] 12 | 13 | dx = 2*x - 2*x*z 14 | dy = 2*y + 2*y*z 15 | dz = -x**2 + y**2 + 2*z 16 | 17 | return np.hstack([c[:, np.newaxis] for c in [dx, dy, dz]]) 18 | 19 | def getMinDot(node): 20 | h = node.width / 2 21 | p = (node.minCorner[0] + h, node.minCorner[1] + h, node.minCorner[2] + h) 22 | normals = [] 23 | grad_points = [] 24 | for dx in [-1, 1]: 25 | for dy in [-1, 1]: 26 | for dz in [-1, 1]: 27 | grad_points.append([p[0] + dx * h, p[1] + dy * h, p[2] + dz * h]) 28 | grad = getGradient(np.array(grad_points)) 29 | normals = grad / np.maximum(np.linalg.norm(grad, axis=1), 1e-6)[:, np.newaxis] 30 | 31 | mean_normal = np.mean(normals, axis=0) 32 | mean_normal = mean_normal / max(np.linalg.norm(mean_normal), 1e-6) 33 | min_dot = np.max([np.dot(mean_normal, n) for n in normals]) 34 | return min_dot 35 | 36 | MIN_DOT_THR = np.cos(3 / 180.0 * np.pi) 37 | 38 | def shouldExpand(node, cornerValues): 39 | # expand all very big nodes 40 | if node.depth < 3: return True 41 | 42 | # early culling for voxels that seem to be far from the surface 43 | min_v = min(cornerValues) 44 | max_v = max(cornerValues) 45 | if min_v * max_v > 0 and min(abs(max_v), abs(min_v)) > max(abs(max_v), abs(min_v)) * 0.5: 46 | return False 47 | 48 | if node.depth < 5: return True 49 | 50 | # refine high-curvature nodes near the surface 51 | return getMinDot(node) < MIN_DOT_THR 52 | 53 | def refineVertices(vertices, iterations=5): 54 | for _ in range(iterations): 55 | grad = getGradient(vertices) 56 | value = getValue(vertices) 57 | vertices = vertices - grad * ((value / np.sum(grad**2, axis=1)))[:, np.newaxis] 58 | 59 | return vertices 60 | 61 | def writeMeshAsObj(vertices, triangles, filename): 62 | print('writing', filename) 63 | with open(filename, 'wt') as f: 64 | for v in vertices: 65 | f.write('v %f %f %f\n' % (v[0], v[1], v[2])) 66 | for t in triangles: 67 | f.write('f %d %d %d\n' % (t[0]+1, t[1]+1, t[2]+1)) 68 | 69 | if __name__ == '__main__': 70 | mesh = IsoOctree.buildMesh( 71 | getValue, 72 | shouldExpand, 73 | center = [0.1, 0.2, 0.3], 74 | width = 6.0, 75 | maxDepth = 10) 76 | 77 | writeMeshAsObj(refineVertices(mesh.vertices), mesh.triangles, 'cayley.obj') -------------------------------------------------------------------------------- /python/examples/point_cloud.py: -------------------------------------------------------------------------------- 1 | """ 2 | Example meshing with point cloud hints. 3 | Requires: pip install scipy 4 | """ 5 | import IsoOctree 6 | import numpy as np 7 | from scipy.spatial import KDTree 8 | import csv 9 | 10 | def buildMesh(points, normals, maxDepth, maxDist, kNearest, subdivisionThreshold): 11 | tree = KDTree(points) 12 | md2 = maxDist**2 13 | 14 | def isoValue(p0): 15 | _, ii = tree.query(p0, k=kNearest, distance_upper_bound=maxDist) 16 | ii = [i for i in ii if i < len(points)] 17 | 18 | if len(ii) == 0: 19 | return 1.0 20 | 21 | sqDists = [np.sum((points[i, :] - p0)**2) for i in ii] 22 | 23 | weights = 1.0 - np.array([min(d2, md2) / md2 for d2 in sqDists]) 24 | return np.sum([weights[i] * np.dot(normals[ii[i], :], p0 - points[ii[i], :]) for i in range(len(ii))]) 25 | 26 | def isoValues(points): 27 | return [isoValue(points[i, :]) for i in range(points.shape[0])] 28 | 29 | return IsoOctree.buildMeshWithPointCloudHint( 30 | isoValues, 31 | points, 32 | maxDepth=maxDepth, 33 | subdivisionThreshold=subdivisionThreshold) 34 | 35 | def readPointCloud(filename): 36 | with open(filename, 'r') as f: 37 | reader = csv.DictReader(f) 38 | rows = [{k: float(v) for k, v in row.items()} for row in reader] 39 | 40 | normals = np.array([[row['n%c' % c] for c in 'xyz'] for row in rows]) 41 | points = np.array([[row[c] for c in 'xyz'] for row in rows]) 42 | return points, normals 43 | 44 | def filterPointCloud(points, normals, quantile): 45 | center = np.mean(points, axis=0) 46 | dists2 = np.sum((points - center)**2, axis=1) 47 | index = dists2 < np.quantile(dists2, quantile) 48 | return points[index, :], normals[index, :] 49 | 50 | def writeMeshAsObj(mesh, filename): 51 | print('writing', filename) 52 | with open(filename, 'wt') as f: 53 | for v in mesh.vertices: 54 | f.write('v %f %f %f\n' % (v[0], v[1], v[2])) 55 | for t in mesh.triangles: 56 | f.write('f %d %d %d\n' % (t[0]+1, t[1]+1, t[2]+1)) 57 | 58 | if __name__ == '__main__': 59 | import argparse 60 | parser = argparse.ArgumentParser(__doc__) 61 | parser.add_argument('input_csv_filename') 62 | parser.add_argument('output_mesh_filename') 63 | parser.add_argument('--filter_quantile', type=float, default=0.99) 64 | parser.add_argument('--max_depth', type=int, default=9) 65 | parser.add_argument('--max_search_distance', type=float, default=0.3) 66 | parser.add_argument('--k_nearest', type=int, default=30) 67 | parser.add_argument('--subdivision_threshold', type=int, default=50) 68 | args = parser.parse_args() 69 | 70 | points, normals = readPointCloud(args.input_csv_filename) 71 | points, normals = filterPointCloud(points, normals, args.filter_quantile) 72 | 73 | mesh = buildMesh(points, normals, 74 | maxDepth=args.max_depth, 75 | maxDist=args.max_search_distance, 76 | kNearest=args.k_nearest, 77 | subdivisionThreshold=args.subdivision_threshold) 78 | 79 | writeMeshAsObj(mesh, args.output_mesh_filename) 80 | -------------------------------------------------------------------------------- /python/examples/simple.py: -------------------------------------------------------------------------------- 1 | import IsoOctree 2 | 3 | def getValue(p): 4 | x, y, z = [p[:, i] for i in range(3)] 5 | # Tanglecube 6 | return x*x*x*x - 5*x*x + y*y*y*y - 5*y*y + z*z*z*z - 5*z*z + 11.8 7 | 8 | def shouldExpand(node, corners): 9 | BASE_DEPTH = 3 10 | if node.depth < BASE_DEPTH: return True 11 | return not (all([v < 0.0 for v in corners]) or all([v > 0.0 for v in corners])) 12 | 13 | def writeMeshAsObj(mesh, filename): 14 | print('writing', filename) 15 | with open(filename, 'wt') as f: 16 | for v in mesh.vertices: 17 | f.write('v %f %f %f\n' % (v[0], v[1], v[2])) 18 | for t in mesh.triangles: 19 | f.write('f %d %d %d\n' % (t[0]+1, t[1]+1, t[2]+1)) 20 | 21 | if __name__ == '__main__': 22 | mesh = IsoOctree.buildMesh( 23 | getValue, 24 | shouldExpand, 25 | width = 6.0) 26 | 27 | writeMeshAsObj(mesh, 'example.obj') 28 | -------------------------------------------------------------------------------- /python/setup.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2024 Spectacular AI Ltd 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | import os 18 | import subprocess 19 | from os import path 20 | 21 | from setuptools import setup, Extension, find_packages 22 | from setuptools.command.build_ext import build_ext 23 | 24 | name = 'IsoOctree' 25 | root_dir = path.abspath(os.path.join(path.dirname(__file__), '..')) 26 | this_is_my_real_build_dir = os.path.join(root_dir, 'python', 'target') 27 | 28 | with open(path.join(root_dir, 'VERSION.txt')) as f: 29 | version = f.read().strip() 30 | 31 | # Get the long description from the README file 32 | long_description = '' 33 | with open(path.join(root_dir, 'python', 'README.rst'), encoding='utf-8') as f: 34 | long_description = f.read() 35 | 36 | class CMakeExtension(Extension): 37 | def __init__(self, name, sourcedir=""): 38 | Extension.__init__(self, name, sources=[]) 39 | self.sourcedir = os.path.abspath(sourcedir) 40 | 41 | class JustUseMyExistingBuild(build_ext): 42 | def build_extension(self, ext): 43 | extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name))) 44 | if not extdir.endswith(os.path.sep): 45 | extdir += os.path.sep 46 | os.makedirs(extdir, exist_ok=True) # Required for the spectacularAI_native hack to work 47 | 48 | here_distutils_would_like_it_to_be = extdir 49 | subprocess.check_call('cp -r ' + name + '*.* ' + here_distutils_would_like_it_to_be, shell=True, cwd=this_is_my_real_build_dir) 50 | 51 | setup( 52 | name=name, 53 | version=version, 54 | author="Spectacular AI Ltd", 55 | url='https://github.com/SpectacularAI/IsoOctree', 56 | author_email="apps@spectacularai.com", 57 | long_description=long_description, 58 | license='Apache-2.0', 59 | keywords='mesh', 60 | python_requires='>=3', 61 | install_requires=['numpy'], 62 | license_files = ('LICENSE', 'NOTICE'), 63 | packages=find_packages(), # Find Python packages, including subpackages! 64 | ext_modules=[CMakeExtension(name)], 65 | cmdclass={"build_ext": JustUseMyExistingBuild}, 66 | zip_safe=False 67 | ) --------------------------------------------------------------------------------