├── .github └── workflows │ ├── build-linux.yml │ └── build-windows.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── demo ├── CMakeLists.txt └── main.cpp ├── extern └── libmorton │ ├── LICENSE │ ├── README.md │ └── libmorton │ └── include │ ├── morton.h │ ├── morton2D.h │ ├── morton2D_LUTs.h │ ├── morton3D.h │ ├── morton3D_LUTs.h │ ├── morton_BMI.h │ ├── morton_LUT_generators.h │ └── morton_common.h ├── include ├── CompactNSearch ├── CompactNSearch.h ├── Config.h ├── DataStructures.h └── PointSet.h └── src └── CompactNSearch.cpp /.github/workflows/build-linux.yml: -------------------------------------------------------------------------------- 1 | name: build-linux 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build-ubuntu: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: configure 13 | run: mkdir build-release && cd build-release && cmake -DCMAKE_BUILD_TYPE=Release .. 14 | - name: build 15 | run: cmake --build build-release 16 | 17 | -------------------------------------------------------------------------------- /.github/workflows/build-windows.yml: -------------------------------------------------------------------------------- 1 | name: build-windows 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build-windows: 7 | 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | matrix: 11 | os: [windows-latest] 12 | 13 | steps: 14 | - uses: actions/checkout@v1 15 | - name: configure 16 | run: mkdir build-release;cd build-release;cmake .. 17 | shell: pwsh 18 | - name: build 19 | run: cmake --build build-release --config Release 20 | 21 | 22 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2) 2 | 3 | project(CompactNSearch) 4 | 5 | # Visual studio solution directories. 6 | set_property(GLOBAL PROPERTY USE_FOLDERS on) 7 | 8 | set(CMAKE_CXX_STANDARD 11) 9 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 10 | 11 | if (UNIX) 12 | find_package(OpenMP) 13 | if (OPENMP_FOUND) 14 | if (CMAKE_VERSION VERSION_GREATER "3.8") 15 | link_libraries(OpenMP::OpenMP_CXX) 16 | else() 17 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") 18 | endif() 19 | endif(OPENMP_FOUND) 20 | endif (UNIX) 21 | 22 | OPTION(BUILD_AS_SHARED_LIBS "Build all the libraries as shared" OFF) 23 | if (BUILD_AS_SHARED_LIBS) 24 | if(WIN32) 25 | add_definitions(-DCMAKE_WINDOWS_EXPORT_ALL_SYMBOLS) 26 | set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS YES CACHE BOOL "Export all symbols") 27 | endif(WIN32) 28 | endif (BUILD_AS_SHARED_LIBS) 29 | 30 | SET(CMAKE_DEBUG_POSTFIX "_d") 31 | 32 | set (HEADER_FILES 33 | include/Config.h 34 | include/CompactNSearch.h 35 | include/PointSet.h 36 | include/DataStructures.h) 37 | 38 | if (BUILD_AS_SHARED_LIBS) 39 | add_library(CompactNSearch SHARED 40 | ${HEADER_FILES} 41 | src/CompactNSearch.cpp 42 | ) 43 | else() 44 | add_library(CompactNSearch 45 | ${HEADER_FILES} 46 | src/CompactNSearch.cpp 47 | ) 48 | endif () 49 | 50 | target_include_directories(CompactNSearch PUBLIC include) 51 | 52 | if (APPLE AND CMAKE_CXX_COMPILER_ID MATCHES "Clang") 53 | find_package(oneDPL REQUIRED) 54 | target_link_libraries(CompactNSearch PUBLIC oneDPL) 55 | endif() 56 | 57 | install(FILES "include/CompactNSearch" ${HEADER_FILES} 58 | DESTINATION include/) 59 | 60 | install(TARGETS CompactNSearch 61 | RUNTIME DESTINATION bin 62 | LIBRARY DESTINATION lib 63 | ARCHIVE DESTINATION lib) 64 | 65 | OPTION(USE_DOUBLE_PRECISION "Use double precision" ON) 66 | if (USE_DOUBLE_PRECISION) 67 | add_definitions( -DUSE_DOUBLE) 68 | endif (USE_DOUBLE_PRECISION) 69 | 70 | option(BUILD_DEMO "Build example of how to use this library." 71 | ON) 72 | if(BUILD_DEMO) 73 | add_subdirectory(demo) 74 | endif(BUILD_DEMO) 75 | 76 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016-present, CompactNSearch contributors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CompactNSearch 2 | 3 |

  

4 | 5 | **CompactNSearch** is a C++ library for **parallel** computation of neighboring points in a **fixed radius** in a **three-dimensional point cloud**. The implemented algorithm is a variant of the *Compact Hashing* approach proposed by Ihmsen et al. [IABT11]. The neighborhood information can be efficiently updated when the points spatially move. Moreover, the library offers the possibility to reorder the points (and other array-stored per point information) according to a space-filling Z curve to improve the cache efficiency in later queries or accesses. 6 | 7 | The library was used to generate all results of the SPH-based fluid simulations presented by Bender and Koschier in [BK15, BK16]. 8 | 9 | **Author**: Dan Koschier, **License**: MIT 10 | 11 | ## Libraries using CompactNSearch 12 | * [PBD] - A C++ library for physically-based simulation of rigid bodies, deformables, cloth and fluids using Position-Based Dynamics 13 | * [SPlisHSPlasH] - A C++ library for the physically-based simulation of fluids using Smoothed Particle Hydrodynamics (cf. video) (Coming soon) 14 | 15 | [![Video](https://img.youtube.com/vi/POnmzzhc5E0/0.jpg)](https://www.youtube.com/watch?v=POnmzzhc5E0) 16 | 17 | ## Build Instructions 18 | 19 | This project is based on [CMake](https://cmake.org/). Simply generate project, Makefiles, etc. using [CMake](https://cmake.org/) and compile the project with the compiler of your choice. The code was tested with the following configurations: 20 | - Windows 10 64-bit, CMake 3.7, Visual Studio 2015 21 | - Debian 8 64-bit, CMake 3.7, GCC 4.9.2. 22 | 23 | ## Usage 24 | A data structure to perform a neighborhood search can be created by calling the constructor given a fixed search radius ```r```. 25 | ```c++ 26 | CompactNSearch::NeighborhoodSearch nsearch(r); 27 | ``` 28 | An arbitrary number of point clouds can then be added to the data structure using the method ```add_point_set```. The library expects the point positions to be contiguously stored in an array-like structure. The method will return a unique id associated with the initialized point set. 29 | ```c++ 30 | // ... Fill array with 3 * n real numbers representing three-dimensional point positions. 31 | std::vector> point_set_1; 32 | std::vector> point_set_2; 33 | 34 | unsigned int point_set_1_id = nsearch.add_point_set(point_set_1.front().data(), positions.size()); 35 | unsigned int point_set_2_id = nsearch.add_point_set(point_set_2.front().data(), positions.size()); 36 | ``` 37 | In order to generate the neighborhood information simply execute the following command 38 | ```c++ 39 | nsearch.find_neighbors(); 40 | ``` 41 | Finally, the neighborhood information can be accessed as follows 42 | ```c++ 43 | CompactNSearch::PointSet const& ps_1 = nsearch.point_set(point_set_1_id); 44 | for (int i = 0; i < ps_1.n_points(); ++i) 45 | { 46 | // Get point set 1 neighbors of point set 1. 47 | for (size_t j = 0; j < ps_1.n_neighbors(point_set_1_id, i); ++j) 48 | { 49 | // Return the point id of the jth neighbor of the ith particle in the point_set_1. 50 | const unsigned int pid = ps_1.neighbor(point_set_1_id, i, j); 51 | } 52 | 53 | // Get point set 2 neighbors of point set 1. 54 | for (size_t j = 0; j < ps_1.n_neighbors(point_set_2_id, i); ++j) 55 | { 56 | // Return the point id of the jth neighbor of the ith particle in the point_set_1. 57 | const unsigned int pid = ps_1.neighbor(point_set_2_id, i, j); 58 | } 59 | } 60 | ``` 61 | 62 | Besides the basic functionality the library offers to compute a rule for reordering the points according to a space-filling Z curve. The reordering will improve the performance of future neighborhood queries and accesses. The rule can be computed via 63 | ```c++ 64 | nsearch.z_sort(); 65 | ``` 66 | Please note that the actual reordering must be invoked by the user by 67 | ```c++ 68 | ps_1.sort_field(positions.data()); 69 | ``` 70 | Assuming that there is additional information stored per-point (e.g. velocity, color, mass etc.) the information **must** also be reorded using the same method to maintain consistency. Subsequently, the ```find_neighbors``` function has to be invoked again to update the neighborhood information. 71 | 72 | Another self-explaining (benchmark) [demo](demo/main.cpp) is contained in the project. 73 | 74 | ## Activation Table 75 | 76 | When maintaining multiple it is sometimes desired that only certain point sets can find points from other point sets. Therefore an activation table is implemented where the user can specify whether a point set i searches points in another point set j. When nothing else is specified all point sets will search points in all other point sets. The activation table can be modified with e.g. 77 | ```c++ 78 | // Point set 2 will not look for neighbors within its own points 79 | nsearch.set_active(point_set_2_id, point_set_2_id, false) 80 | ``` 81 | 82 | ## References 83 | 84 | * [IABT11] M. Ihmsen, N. Akinci, M. Becker and M. Teschner, 2011. "A Parallel SPH Implementation on Multi-Core CPUs", Computer Graphics Forum 30, 1, 99-112. 85 | * [BK15] J. Bender and D. Koschier 2015. "Divergence-Free Smoothed Particle Hydrodynamics", ACM SIGGRAPH / Eurographics Symposium on Computer Animation, 1-9 86 | * [BK17] J. Bender and D. Koschier, 2017. "Divergence-Free SPH for Incompressible and Viscous Fluids", IEEE Transactions on Visualization and Computer Graphics. 87 | 88 | [PBD]: 89 | [SPlisHSPlasH]: 90 | -------------------------------------------------------------------------------- /demo/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | add_definitions(-D_USE_MATH_DEFINES) 4 | 5 | include_directories("../include") 6 | 7 | add_executable(Demo 8 | main.cpp 9 | ) 10 | 11 | target_link_libraries(Demo CompactNSearch) 12 | -------------------------------------------------------------------------------- /demo/main.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | using namespace CompactNSearch; 15 | 16 | std::vector> positions; 17 | 18 | std::size_t const N = 30; 19 | //std::size_t const N = 5; 20 | Real const r_omega = static_cast(0.15); 21 | Real const r_omega2 = r_omega * r_omega; 22 | Real const radius = static_cast(2.0) * (static_cast(2.0) * r_omega / static_cast(N-1)); 23 | 24 | std::size_t const N_enright_steps = 50; 25 | 26 | Real 27 | compute_average_number_of_neighbors(NeighborhoodSearch const& nsearch) 28 | { 29 | unsigned long res = 0; 30 | auto const& d = nsearch.point_set(0); 31 | for (int i = 0; i < d.n_points(); ++i) 32 | { 33 | res += static_cast(d.n_neighbors(0, i)); 34 | } 35 | return static_cast(res) / static_cast(d.n_points()); 36 | } 37 | 38 | Real 39 | compute_average_distance(NeighborhoodSearch const& nsearch) 40 | { 41 | unsigned long long res = 0; 42 | auto const& d = nsearch.point_set(0); 43 | unsigned long long count = 0; 44 | for (int i = 0; i < d.n_points(); ++i) 45 | { 46 | std::size_t nn = d.n_neighbors(0, i); 47 | for (int j = 0; j < nn; ++j) 48 | { 49 | unsigned int k = d.neighbor(0, i, j); 50 | res += std::abs(i - static_cast(k)); 51 | count++; 52 | } 53 | } 54 | return static_cast(res) / static_cast(count); 55 | } 56 | 57 | std::vector> 58 | brute_force_search(std::size_t n_positions) 59 | { 60 | std::vector> brute_force_neighbors(n_positions); 61 | for (int i = 0; i < n_positions; ++i) 62 | { 63 | std::vector& neighbors = brute_force_neighbors[i]; 64 | for (int j = 0; j < n_positions; ++j) 65 | { 66 | if (i == j) 67 | continue; 68 | std::array const& xa = positions[i]; 69 | std::array const& xb = positions[j]; 70 | Real l2 = 71 | (xa[0] - xb[0])*(xa[0] - xb[0]) + 72 | (xa[1] - xb[1])*(xa[1] - xb[1]) + 73 | (xa[2] - xb[2])*(xa[2] - xb[2]); 74 | if (l2 <= radius * radius) 75 | { 76 | neighbors.push_back(j); 77 | } 78 | } 79 | } 80 | return std::move(brute_force_neighbors); 81 | } 82 | 83 | void 84 | compare_with_bruteforce_search(NeighborhoodSearch const& nsearch) 85 | { 86 | PointSet const& d0 = nsearch.point_set(0); 87 | auto brute_force_neighbors = brute_force_search(d0.n_points()); 88 | for (int i = 0; i < d0.n_points(); ++i) 89 | { 90 | auto const& bfn = brute_force_neighbors[i]; 91 | if (bfn.size() != d0.n_neighbors(0, i)) 92 | { 93 | std::cerr << "ERROR: Not the same number of neighbors." << std::endl; 94 | } 95 | for (int j = 0; j < d0.n_neighbors(0, i); ++j) 96 | { 97 | if (std::find(bfn.begin(), bfn.end(), d0.neighbor(0, i, j)) == bfn.end()) 98 | { 99 | std::cerr << "ERROR: Neighbor not found in brute force list." << std::endl; 100 | } 101 | } 102 | } 103 | } 104 | 105 | void 106 | compare_single_query_with_bruteforce_search(NeighborhoodSearch& nsearch) 107 | { 108 | std::vector> neighbors; 109 | PointSet const& d0 = nsearch.point_set(0); 110 | auto brute_force_neighbors = brute_force_search(d0.n_points()); 111 | for (int i = 0; i < d0.n_points(); ++i) 112 | { 113 | auto const& bfn = brute_force_neighbors[i]; 114 | neighbors.clear(); 115 | nsearch.find_neighbors(0, i, neighbors); 116 | if (bfn.size() != neighbors[0].size()) 117 | { 118 | std::cerr << "ERROR: Not the same number of neighbors." << std::endl; 119 | } 120 | for (int j = 0; j < neighbors.size(); ++j) 121 | { 122 | if (std::find(bfn.begin(), bfn.end(), neighbors[0][j]) == bfn.end()) 123 | { 124 | std::cerr << "ERROR: Neighbor not found in brute force list." << std::endl; 125 | } 126 | } 127 | } 128 | } 129 | 130 | 131 | std::array 132 | enright_velocity_field(std::array const& x) 133 | { 134 | Real sin_pi_x_2 = std::sin(static_cast(M_PI) * x[0]); 135 | Real sin_pi_y_2 = std::sin(static_cast(M_PI) * x[1]); 136 | Real sin_pi_z_2 = std::sin(static_cast(M_PI) * x[2]); 137 | sin_pi_x_2 *= sin_pi_x_2; 138 | sin_pi_y_2 *= sin_pi_y_2; 139 | sin_pi_z_2 *= sin_pi_z_2; 140 | 141 | Real sin_2_pi_x = static_cast(std::sin(static_cast(2.0 * M_PI) * x[0])); 142 | Real sin_2_pi_y = static_cast(std::sin(static_cast(2.0 * M_PI) * x[1])); 143 | Real sin_2_pi_z = static_cast(std::sin(static_cast(2.0 * M_PI) * x[2])); 144 | return {{ 145 | static_cast(2.0) * sin_pi_x_2 * sin_2_pi_y * sin_2_pi_z, 146 | -sin_2_pi_x * sin_pi_y_2 * sin_2_pi_z, 147 | -sin_2_pi_x * sin_2_pi_y * sin_pi_z_2}}; 148 | 149 | } 150 | 151 | void 152 | advect() 153 | { 154 | #ifdef _MSC_VER 155 | concurrency::parallel_for_each( 156 | #elif defined(__APPLE__) && defined(__clang__) 157 | std::for_each(oneapi::dpl::execution::par, 158 | #else 159 | __gnu_parallel::for_each( 160 | #endif 161 | positions.begin(), positions.end(), [&](std::array& x) 162 | { 163 | std::array v = enright_velocity_field(x); 164 | x[0] += static_cast(0.005) * v[0]; 165 | x[1] += static_cast(0.005) * v[1]; 166 | x[2] += static_cast(0.005) * v[1]; 167 | } 168 | ); 169 | } 170 | 171 | int main(int argc, char* argv[]) 172 | { 173 | Real min_x = std::numeric_limits::max(); 174 | Real max_x = std::numeric_limits::min(); 175 | positions.reserve(N * N * N); 176 | for (unsigned int i = 0; i < N; ++i) 177 | { 178 | for (unsigned int j = 0; j < N; ++j) 179 | { 180 | for (unsigned int k = 0; k < N; ++k) 181 | { 182 | std::array x = {{ 183 | r_omega * (static_cast(2.0) * static_cast(i) / static_cast(N-1)-static_cast(1.0)), 184 | r_omega * (static_cast(2.0) * static_cast(j) / static_cast(N-1)-static_cast(1.0)), 185 | r_omega * (static_cast(2.0) * static_cast(k) / static_cast(N-1)-static_cast(1.0))}}; 186 | 187 | Real l2 = x[0] * x[0] + x[1] * x[1] + x[2] * x[2]; 188 | if (l2 < r_omega2) 189 | { 190 | x[0] += static_cast(0.35); 191 | x[1] += static_cast(0.35); 192 | x[2] += static_cast(0.35); 193 | positions.push_back(x); 194 | if (min_x > x[0]) 195 | { 196 | min_x = x[0]; 197 | } 198 | if (max_x < x[0]) 199 | { 200 | max_x = x[0]; 201 | } 202 | } 203 | } 204 | } 205 | } 206 | std::random_shuffle(positions.begin(), positions.end()); 207 | 208 | NeighborhoodSearch nsearch(radius, true); 209 | nsearch.add_point_set(positions.front().data(), positions.size(), true, true); 210 | nsearch.add_point_set(positions.front().data(), positions.size(), true, true); 211 | nsearch.find_neighbors(); 212 | 213 | nsearch.update_point_sets(); 214 | std::vector> neighbors2; 215 | nsearch.find_neighbors(0, 1, neighbors2); 216 | std::vector> neighbors3; 217 | nsearch.find_neighbors(1, 2, neighbors3); 218 | 219 | std::cout << "#Points = " << positions.size() << std::endl; 220 | std::cout << "Search radius = " << radius << std::endl; 221 | std::cout << "Min x = " << min_x << std::endl; 222 | std::cout << "Max x = " << max_x << std::endl; 223 | std::cout << "Average number of neighbors = " << compute_average_number_of_neighbors(nsearch) << std::endl; 224 | std::cout << "Average index distance prior to z-sort = " << compute_average_distance(nsearch) << std::endl; 225 | 226 | nsearch.z_sort(); 227 | for (auto i = 0u; i < nsearch.n_point_sets(); ++i) 228 | { 229 | auto const& d = nsearch.point_set(i); 230 | d.sort_field(positions.data()); 231 | 232 | } 233 | nsearch.find_neighbors(); 234 | 235 | //compare_single_query_with_bruteforce_search(nsearch); 236 | //compare_with_bruteforce_search(nsearch); 237 | 238 | std::cout << "Average index distance after z-sort = " << compute_average_distance(nsearch) << std::endl; 239 | 240 | std::cout << "Moving points:" << std::endl; 241 | for (int i = 0; i < N_enright_steps; ++i) 242 | { 243 | std::cout << "Enright step " << i << ". "; 244 | advect(); 245 | auto t0 = std::chrono::high_resolution_clock::now(); 246 | nsearch.find_neighbors(); 247 | std::cout << "Neighborhood search took " << std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - t0).count() << "ms" << std::endl; 248 | //compare_with_bruteforce_search(nsearch); 249 | //compare_single_query_with_bruteforce_search(nsearch); 250 | } 251 | 252 | return 0; 253 | } 254 | -------------------------------------------------------------------------------- /extern/libmorton/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Jeroen Baert 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /extern/libmorton/README.md: -------------------------------------------------------------------------------- 1 | # Libmorton 2 | 3 | * Libmorton is a **C++ header-only library** with methods to efficiently encode/decode between 64, 32 and 16-bit morton codes and coordinates, in 2D and 3D. *Morton order* is also known as *Z-order* or *[the Z-order curve](https://en.wikipedia.org/wiki/Z-order_curve)*. 4 | * Libmorton is a **lightweight and portable** library - in its most basic form it only depends on standard C++ headers. Architecture-specific optimizations are implemented incrementally. 5 | * This library is under active development. SHIT MIGHT BREAK. 6 | * More info and some benchmarks in these blogposts: 7 | * http://www.forceflow.be/2016/01/18/libmorton-a-library-for-morton-order-encoding-decoding/ 8 | * http://www.forceflow.be/2013/10/07/morton-encodingdecoding-through-bit-interleaving-implementations/ 9 | 10 | ## Usage 11 | Just include *libmorton/morton.h*. This will always have functions that point to the most efficient way to encode/decode Morton codes. If you want to test out alternative (and possibly slower) methods, you can find them in *libmorton/morton2D.h* and *libmorton/morton3D.h*. 12 | 13 |
14 | // ENCODING 2D / 3D morton codes, of length 32 and 64 bits
15 | inline uint_fast32_t morton2D_32_encode(const uint_fast16_t x, const uint_fast16_t y);
16 | inline uint_fast64_t morton2D_64_encode(const uint_fast32_t x, const uint_fast32_t y);
17 | inline uint_fast32_t morton3D_32_encode(const uint_fast16_t x, const uint_fast16_t y, const uint_fast16_t z);
18 | inline uint_fast64_t morton3D_64_encode(const uint_fast32_t x, const uint_fast32_t y, const uint_fast32_t z);
19 | // DECODING 2D / 3D morton codes, of length 32 and 64 bits
20 | inline void morton2D_32_decode(const uint_fast32_t morton, uint_fast16_t& x, uint_fast16_t& y);
21 | inline void morton2D_64_decode(const uint_fast64_t morton, uint_fast32_t& x, uint_fast32_t& y);
22 | inline void morton3D_32_decode(const uint_fast32_t morton, uint_fast16_t& x, uint_fast16_t& y, uint_fast16_t& z);
23 | inline void morton3D_64_decode(const uint_fast64_t morton, uint_fast32_t& x, uint_fast32_t& y, uint_fast32_t& z);
24 | 
25 | 26 | ## Testing 27 | The *test* folder contains tools I use to test correctness and performance of the libmorton implementation. This section is under heavy re-writing, but might contain some useful code for advanced usage. 28 | 29 | ## TODO 30 | * Write better test suites 31 | * Implement BMI2 instruction set detection and optimization for newer Intel CPU's 32 | * More testing 33 | -------------------------------------------------------------------------------- /extern/libmorton/libmorton/include/morton.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // This file will always contain pointers to the fastest Morton encoding/decoding implementation 4 | // IF you just want to use the fastest method to encode/decode morton codes, include this 5 | 6 | #include "morton2D.h" 7 | #include "morton3D.h" 8 | 9 | //// ENCODE 10 | //inline uint_fast32_t morton2D_32_encode(const uint_fast16_t x, const uint_fast16_t y); 11 | //inline uint_fast64_t morton2D_64_encode(const uint_fast32_t x, const uint_fast32_t y); 12 | //inline uint_fast32_t morton3D_32_encode(const uint_fast16_t x, const uint_fast16_t y, const uint_fast16_t z); 13 | //inline uint_fast64_t morton3D_64_encode(const uint_fast32_t x, const uint_fast32_t y, const uint_fast32_t z); 14 | // 15 | //// DECODE 16 | //inline void morton2D_32_decode(const uint_fast32_t morton, uint_fast16_t& x, uint_fast16_t& y); 17 | //inline void morton2D_64_decode(const uint_fast64_t morton, uint_fast32_t& x, uint_fast32_t& y); 18 | //inline void morton3D_32_decode(const uint_fast32_t morton, uint_fast16_t& x, uint_fast16_t& y, uint_fast16_t& z); 19 | //inline void morton3D_64_decode(const uint_fast64_t morton, uint_fast32_t& x, uint_fast32_t& y, uint_fast32_t& z); 20 | 21 | // Functions under this are stubs which will always point to fastest implementation at the moment 22 | //----------------------------------------------------------------------------------------------- 23 | 24 | #define morton2D_32_encode m2D_e_sLUT 25 | #define morton2D_64_encode m2D_e_sLUT 26 | #define morton2D_32_decode m2D_d_sLUT 27 | #define morton2D_64_decode m2D_d_sLUT 28 | 29 | #define morton3D_32_encode m3D_e_sLUT 30 | #define morton3D_64_encode m3D_e_sLUT 31 | #define morton3D_32_decode m3D_d_sLUT 32 | #define morton3D_64_decode m3D_d_sLUT -------------------------------------------------------------------------------- /extern/libmorton/libmorton/include/morton2D.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Libmorton - Methods to encode/decode 64-bit morton codes from/to 32-bit (x,y) coordinates 4 | // Warning: morton.h will always point to the functions that use the fastest available method. 5 | 6 | #include 7 | #include 8 | #include "morton2D_LUTs.h" 9 | #include "morton_common.h" 10 | 11 | using namespace std; 12 | 13 | // Encode methods 14 | template inline morton m2D_e_sLUT(const coord x, const coord y); 15 | template inline morton m2D_e_sLUT_ET(const coord x, const coord y); 16 | template inline morton m2D_e_LUT(const coord x, const coord y); 17 | template inline morton m2D_e_LUT_ET(const coord x, const coord y); 18 | template inline morton m2D_e_magicbits(const coord x, const coord y); 19 | template inline morton m2D_e_for(const coord x, const coord y); 20 | template inline morton m2D_e_for_ET(const coord x, const coord y); 21 | 22 | // Decode methods 23 | template inline void m2D_d_sLUT(const morton m, coord& x, coord& y); 24 | template inline void m2D_d_sLUT_ET(const morton m, coord& x, coord& y); 25 | template inline void m2D_d_LUT(const morton m, coord& x, coord& y); 26 | template inline void m2D_d_LUT_ET(const morton m, coord& x, coord& y); 27 | template inline void m2D_d_magicbits(const morton m, coord& x, coord& y); 28 | template inline void m2D_d_for(const morton m, coord& x, coord& y); 29 | 30 | // ENCODE 2D Morton code : Pre-shifted LookUpTable (sLUT) 31 | template 32 | inline morton m2D_e_sLUT(const coord x, const coord y) { 33 | morton answer = 0; 34 | const static morton EIGHTBITMASK = 0x000000FF; 35 | for (unsigned int i = sizeof(coord); i > 0; --i) { 36 | unsigned int shift = (i - 1) * 8; 37 | answer = 38 | answer << 16 | 39 | Morton2D_encode_y_256[(y >> shift) & EIGHTBITMASK] | 40 | Morton2D_encode_x_256[(x >> shift) & EIGHTBITMASK]; 41 | } 42 | return answer; 43 | } 44 | 45 | // ENCODE 2D Morton code : LookUpTable (LUT) 46 | template 47 | inline morton m2D_e_LUT(const coord x, const coord y) { 48 | morton answer = 0; 49 | const static morton EIGHTBITMASK = 0x000000FF; 50 | for (unsigned int i = sizeof(coord); i > 0; --i) { 51 | unsigned int shift = (i - 1) * 8; 52 | answer = 53 | answer << 16 | 54 | (Morton2D_encode_x_256[(y >> shift) & EIGHTBITMASK] << 1) | 55 | (Morton2D_encode_x_256[(x >> shift) & EIGHTBITMASK]); 56 | } 57 | return answer; 58 | } 59 | 60 | // HELPER METHOD for Early Termination LUT Encode 61 | template 62 | inline morton compute2D_ET_LUT_encode(const coord c, const coord *LUT) { 63 | const static morton EIGHTBITMASK = 0x000000FF; 64 | unsigned long maxbit = 0; 65 | morton answer = 0; 66 | if (findFirstSetBit(c, &maxbit) == 0) { return 0; } 67 | unsigned int i = 0; 68 | while (maxbit >= i) { 69 | answer |= (LUT[(c >> i) & EIGHTBITMASK]) << i * 2; 70 | i += 8; 71 | } 72 | return answer; 73 | } 74 | 75 | // ENCODE 2D Morton code : Pre-shifted LUT (Early termination version) 76 | // This version tries to terminate early when there are no more bits to process 77 | // Figuring this out is probably too costly in most cases. 78 | template 79 | inline morton m2D_e_sLUT_ET(const coord x, const coord y) { 80 | morton answer_x = compute2D_ET_LUT_encode(x, Morton2D_encode_x_256); 81 | morton answer_y = compute2D_ET_LUT_encode(y, Morton2D_encode_y_256); 82 | return answer_y | answer_x; 83 | } 84 | 85 | // ENCODE 2D Morton code : LUT (Early termination version) 86 | template 87 | inline morton m2D_e_LUT_ET(const coord x, const coord y) { 88 | morton answer_x = compute2D_ET_LUT_encode(x, Morton2D_encode_x_256); 89 | morton answer_y = compute2D_ET_LUT_encode(y, Morton2D_encode_x_256); 90 | return (answer_y << 1) | answer_x; 91 | } 92 | 93 | // HELPER METHOD for Magic bits encoding - split by 2 94 | template 95 | inline morton morton2D_SplitBy2Bits(const coord a) { 96 | const morton* masks = (sizeof(morton) <= 4) ? reinterpret_cast(magicbit2D_masks32) : reinterpret_cast(magicbit2D_masks64); 97 | morton x = a; 98 | if (sizeof(morton) > 4) { x = (x | x << 32) & masks[0]; } 99 | x = (x | x << 16) & masks[1]; 100 | x = (x | x << 8) & masks[2]; 101 | x = (x | x << 4) & masks[3]; 102 | x = (x | x << 2) & masks[4]; 103 | x = (x | x << 1) & masks[5]; 104 | return x; 105 | } 106 | 107 | // ENCODE 2D Morton code : Magic bits 108 | template 109 | inline morton m2D_e_magicbits(const coord x, const coord y) { 110 | return morton2D_SplitBy2Bits(x) | (morton2D_SplitBy2Bits(y) << 1); 111 | } 112 | 113 | // ENCODE 2D Morton code : For Loop 114 | template 115 | inline morton m2D_e_for(const coord x, const coord y){ 116 | morton answer = 0; 117 | unsigned int checkbits = floor(sizeof(morton) * 4.0f); 118 | for (unsigned int i = 0; i <= checkbits; ++i) { 119 | morton mshifted = static_cast(0x1) << i; // Here we need to cast 0x1 to 64bits, otherwise there is a bug when morton code is larger than 32 bits 120 | unsigned int shift = 2 * i; 121 | answer |= 122 | ((x & mshifted) << shift) 123 | | ((y & mshifted) << (shift + 1)); 124 | } 125 | return answer; 126 | } 127 | 128 | // ENCODE 2D Morton code : For Loop (Early termination version) 129 | template 130 | inline morton m2D_e_for_ET(const coord x, const coord y) { 131 | morton answer = 0; 132 | unsigned long x_max = 0, y_max = 0; 133 | unsigned int checkbits = sizeof(morton) * 4; 134 | findFirstSetBit(x, &x_max); 135 | findFirstSetBit(y, &y_max); 136 | checkbits = min(static_cast(checkbits), max(x_max, y_max) + 1ul); 137 | for (unsigned int i = 0; i <= checkbits; ++i) { 138 | morton m_shifted = static_cast(0x1) << i; // Here we need to cast 0x1 to 64bits, otherwise there is a bug when morton code is larger than 32 bits 139 | unsigned int shift = 2 * i; 140 | answer |= ((x & m_shifted) << shift) 141 | | ((y & m_shifted) << (shift + 1)); 142 | } 143 | return answer; 144 | } 145 | 146 | // HELPER METHODE for LUT decoding 147 | template 148 | inline coord morton2D_DecodeCoord_LUT256(const morton m, const uint_fast8_t *LUT, const unsigned int startshift) { 149 | morton a = 0; 150 | morton EIGHTBITMASK = 0x000000ff; 151 | unsigned int loops = sizeof(morton); 152 | for (unsigned int i = 0; i < loops; ++i) { 153 | a |= (LUT[(m >> ((i * 8) + startshift)) & EIGHTBITMASK] << (4 * i)); 154 | } 155 | return static_cast(a); 156 | } 157 | 158 | // DECODE 2D Morton code : Shifted LUT 159 | template 160 | inline void m2D_d_sLUT(const morton m, coord& x, coord& y) { 161 | x = morton2D_DecodeCoord_LUT256(m, Morton2D_decode_x_256, 0); 162 | y = morton2D_DecodeCoord_LUT256(m, Morton2D_decode_y_256, 0); 163 | } 164 | 165 | // DECODE 2D 64-bit morton code : LUT 166 | template 167 | inline void m2D_d_LUT(const morton m, coord& x, coord& y) { 168 | x = morton2D_DecodeCoord_LUT256(m, Morton2D_decode_x_256, 0); 169 | y = morton2D_DecodeCoord_LUT256(m, Morton2D_decode_x_256, 1); 170 | } 171 | 172 | // DECODE 2D Morton code : Shifted LUT (early termination) 173 | template 174 | inline void m2D_d_sLUT_ET(const morton m, coord& x, coord& y) { 175 | x = 0; y = 0; 176 | morton EIGHTBITMASK = 0x000000ff; 177 | unsigned long firstbit_location = 0; 178 | if (!findFirstSetBit(m, &firstbit_location)) { return; } 179 | unsigned int i = 0; 180 | unsigned int shiftback = 0; 181 | while (firstbit_location >= i) { 182 | morton m_shifted = (m >> i) & EIGHTBITMASK; 183 | x |= Morton2D_decode_x_256[m_shifted] << shiftback; 184 | y |= Morton2D_decode_y_256[m_shifted] << shiftback; 185 | shiftback += 4; 186 | i += 8; 187 | } 188 | } 189 | 190 | // DECODE 2D Morton code : LUT (early termination) 191 | template 192 | inline void m2D_d_LUT_ET(const morton m, coord& x, coord& y) { 193 | x = 0; y = 0; 194 | morton EIGHTBITMASK = 0x000000ff; 195 | unsigned long firstbit_location = 0; 196 | if (!findFirstSetBit(m, &firstbit_location)) { return; } 197 | unsigned int i = 0; 198 | unsigned int shiftback = 0; 199 | while (firstbit_location >= i) { 200 | morton m_shifted = (m >> i) & EIGHTBITMASK; 201 | x |= Morton2D_decode_x_256[(m >> i) & EIGHTBITMASK] << shiftback; 202 | y |= Morton2D_decode_x_256[(m >> (i+1)) & EIGHTBITMASK] << shiftback; 203 | shiftback += 4; 204 | i += 8; 205 | } 206 | } 207 | 208 | // HELPER method for Magicbits decoding 209 | template 210 | static inline coord morton2D_GetSecondBits(const morton m) { 211 | morton* masks = (sizeof(morton) <= 4) ? reinterpret_cast(magicbit2D_masks32) : reinterpret_cast(magicbit2D_masks64); 212 | morton x = m & masks[4]; 213 | x = (x ^ (x >> 2)) & masks[3]; 214 | x = (x ^ (x >> 4)) & masks[2]; 215 | x = (x ^ (x >> 8)) & masks[1]; 216 | if (sizeof(morton) > 4) x = (x ^ (x >> 16)) & masks[0]; 217 | return static_cast(x); 218 | } 219 | 220 | // DECODE 2D Morton code : Magic bits 221 | // This method splits the morton codes bits by using certain patterns (magic bits) 222 | template 223 | inline void m2D_d_magicbits(const morton m, coord& x, coord& y) { 224 | x = morton2D_GetSecondBits(m); 225 | y = morton2D_GetSecondBits(m >> 1); 226 | } 227 | 228 | 229 | // DECODE 2D morton code : For loop 230 | template 231 | inline void m2D_d_for(const morton m, coord& x, coord& y) { 232 | x = 0; y = 0; 233 | unsigned int checkbits = sizeof(morton) * 4; 234 | for (unsigned int i = 0; i <= checkbits; ++i) { 235 | morton selector = 1; 236 | unsigned int shift_selector = 2 * i; 237 | x |= (m & (selector << shift_selector)) >> i; 238 | y |= (m & (selector << (shift_selector + 1))) >> (i + 1); 239 | } 240 | } 241 | 242 | // DECODE 3D Morton code : For loop (Early termination version) 243 | template 244 | inline void m2D_d_for_ET(const morton m, coord& x, coord& y) { 245 | x = 0; y = 0; 246 | float defaultbits = sizeof(morton) * 4; 247 | unsigned long firstbit_location = 0; 248 | if (!findFirstSetBit(m, &firstbit_location)) return; 249 | unsigned int checkbits = static_cast(min(defaultbits, firstbit_location / 2.0f)); 250 | for (unsigned int i = 0; i <= checkbits; ++i) { 251 | morton selector = 1; 252 | unsigned int shift_selector = 2 * i; 253 | x |= (m & (selector << shift_selector)) >> i; 254 | y |= (m & (selector << (shift_selector + 1))) >> (i + 1); 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /extern/libmorton/libmorton/include/morton2D_LUTs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // Magicbits masks 6 | static uint_fast32_t magicbit2D_masks32[6] = { 0xFFFFFFFF, 0x0000FFFF, 0x00FF00FF, 0x0F0F0F0F, 0x33333333, 0x55555555 }; 7 | static uint_fast64_t magicbit2D_masks64[6] = { 0x00000000FFFFFFFF, 0x0000FFFF0000FFFF, 0x00FF00FF00FF00FF, 8 | 0x0F0F0F0F0F0F0F0F, 0x3333333333333333, 0x5555555555555555 }; 9 | 10 | static const uint_fast16_t Morton2D_encode_x_256[256] = 11 | { 12 | 0, 1, 4, 5, 16, 17, 20, 21, 13 | 64, 65, 68, 69, 80, 81, 84, 85, 14 | 256, 257, 260, 261, 272, 273, 276, 277, 15 | 320, 321, 324, 325, 336, 337, 340, 341, 16 | 1024, 1025, 1028, 1029, 1040, 1041, 1044, 1045, 17 | 1088, 1089, 1092, 1093, 1104, 1105, 1108, 1109, 18 | 1280, 1281, 1284, 1285, 1296, 1297, 1300, 1301, 19 | 1344, 1345, 1348, 1349, 1360, 1361, 1364, 1365, 20 | 4096, 4097, 4100, 4101, 4112, 4113, 4116, 4117, 21 | 4160, 4161, 4164, 4165, 4176, 4177, 4180, 4181, 22 | 4352, 4353, 4356, 4357, 4368, 4369, 4372, 4373, 23 | 4416, 4417, 4420, 4421, 4432, 4433, 4436, 4437, 24 | 5120, 5121, 5124, 5125, 5136, 5137, 5140, 5141, 25 | 5184, 5185, 5188, 5189, 5200, 5201, 5204, 5205, 26 | 5376, 5377, 5380, 5381, 5392, 5393, 5396, 5397, 27 | 5440, 5441, 5444, 5445, 5456, 5457, 5460, 5461, 28 | 16384, 16385, 16388, 16389, 16400, 16401, 16404, 16405, 29 | 16448, 16449, 16452, 16453, 16464, 16465, 16468, 16469, 30 | 16640, 16641, 16644, 16645, 16656, 16657, 16660, 16661, 31 | 16704, 16705, 16708, 16709, 16720, 16721, 16724, 16725, 32 | 17408, 17409, 17412, 17413, 17424, 17425, 17428, 17429, 33 | 17472, 17473, 17476, 17477, 17488, 17489, 17492, 17493, 34 | 17664, 17665, 17668, 17669, 17680, 17681, 17684, 17685, 35 | 17728, 17729, 17732, 17733, 17744, 17745, 17748, 17749, 36 | 20480, 20481, 20484, 20485, 20496, 20497, 20500, 20501, 37 | 20544, 20545, 20548, 20549, 20560, 20561, 20564, 20565, 38 | 20736, 20737, 20740, 20741, 20752, 20753, 20756, 20757, 39 | 20800, 20801, 20804, 20805, 20816, 20817, 20820, 20821, 40 | 21504, 21505, 21508, 21509, 21520, 21521, 21524, 21525, 41 | 21568, 21569, 21572, 21573, 21584, 21585, 21588, 21589, 42 | 21760, 21761, 21764, 21765, 21776, 21777, 21780, 21781, 43 | 21824, 21825, 21828, 21829, 21840, 21841, 21844, 21845 44 | }; 45 | 46 | static const uint_fast16_t Morton2D_encode_y_256[256] = 47 | { 48 | 0, 2, 8, 10, 32, 34, 40, 42, 49 | 128, 130, 136, 138, 160, 162, 168, 170, 50 | 512, 514, 520, 522, 544, 546, 552, 554, 51 | 640, 642, 648, 650, 672, 674, 680, 682, 52 | 2048, 2050, 2056, 2058, 2080, 2082, 2088, 2090, 53 | 2176, 2178, 2184, 2186, 2208, 2210, 2216, 2218, 54 | 2560, 2562, 2568, 2570, 2592, 2594, 2600, 2602, 55 | 2688, 2690, 2696, 2698, 2720, 2722, 2728, 2730, 56 | 8192, 8194, 8200, 8202, 8224, 8226, 8232, 8234, 57 | 8320, 8322, 8328, 8330, 8352, 8354, 8360, 8362, 58 | 8704, 8706, 8712, 8714, 8736, 8738, 8744, 8746, 59 | 8832, 8834, 8840, 8842, 8864, 8866, 8872, 8874, 60 | 10240, 10242, 10248, 10250, 10272, 10274, 10280, 10282, 61 | 10368, 10370, 10376, 10378, 10400, 10402, 10408, 10410, 62 | 10752, 10754, 10760, 10762, 10784, 10786, 10792, 10794, 63 | 10880, 10882, 10888, 10890, 10912, 10914, 10920, 10922, 64 | 32768, 32770, 32776, 32778, 32800, 32802, 32808, 32810, 65 | 32896, 32898, 32904, 32906, 32928, 32930, 32936, 32938, 66 | 33280, 33282, 33288, 33290, 33312, 33314, 33320, 33322, 67 | 33408, 33410, 33416, 33418, 33440, 33442, 33448, 33450, 68 | 34816, 34818, 34824, 34826, 34848, 34850, 34856, 34858, 69 | 34944, 34946, 34952, 34954, 34976, 34978, 34984, 34986, 70 | 35328, 35330, 35336, 35338, 35360, 35362, 35368, 35370, 71 | 35456, 35458, 35464, 35466, 35488, 35490, 35496, 35498, 72 | 40960, 40962, 40968, 40970, 40992, 40994, 41000, 41002, 73 | 41088, 41090, 41096, 41098, 41120, 41122, 41128, 41130, 74 | 41472, 41474, 41480, 41482, 41504, 41506, 41512, 41514, 75 | 41600, 41602, 41608, 41610, 41632, 41634, 41640, 41642, 76 | 43008, 43010, 43016, 43018, 43040, 43042, 43048, 43050, 77 | 43136, 43138, 43144, 43146, 43168, 43170, 43176, 43178, 78 | 43520, 43522, 43528, 43530, 43552, 43554, 43560, 43562, 79 | 43648, 43650, 43656, 43658, 43680, 43682, 43688, 43690 80 | }; 81 | 82 | static const uint_fast8_t Morton2D_decode_x_256[256]{ 83 | 0,1,0,1,2,3,2,3,0,1,0,1,2,3,2,3, 84 | 4,5,4,5,6,7,6,7,4,5,4,5,6,7,6,7, 85 | 0,1,0,1,2,3,2,3,0,1,0,1,2,3,2,3, 86 | 4,5,4,5,6,7,6,7,4,5,4,5,6,7,6,7, 87 | 8,9,8,9,10,11,10,11,8,9,8,9,10,11,10,11, 88 | 12,13,12,13,14,15,14,15,12,13,12,13,14,15,14,15, 89 | 8,9,8,9,10,11,10,11,8,9,8,9,10,11,10,11, 90 | 12,13,12,13,14,15,14,15,12,13,12,13,14,15,14,15, 91 | 0,1,0,1,2,3,2,3,0,1,0,1,2,3,2,3, 92 | 4,5,4,5,6,7,6,7,4,5,4,5,6,7,6,7, 93 | 0,1,0,1,2,3,2,3,0,1,0,1,2,3,2,3, 94 | 4,5,4,5,6,7,6,7,4,5,4,5,6,7,6,7, 95 | 8,9,8,9,10,11,10,11,8,9,8,9,10,11,10,11, 96 | 12,13,12,13,14,15,14,15,12,13,12,13,14,15,14,15, 97 | 8,9,8,9,10,11,10,11,8,9,8,9,10,11,10,11, 98 | 12,13,12,13,14,15,14,15,12,13,12,13,14,15,14,15 99 | }; 100 | 101 | static const uint_fast8_t Morton2D_decode_y_256[256]{ 102 | 0,0,1,1,0,0,1,1,2,2,3,3,2,2,3,3, 103 | 0,0,1,1,0,0,1,1,2,2,3,3,2,2,3,3, 104 | 4,4,5,5,4,4,5,5,6,6,7,7,6,6,7,7, 105 | 4,4,5,5,4,4,5,5,6,6,7,7,6,6,7,7, 106 | 0,0,1,1,0,0,1,1,2,2,3,3,2,2,3,3, 107 | 0,0,1,1,0,0,1,1,2,2,3,3,2,2,3,3, 108 | 4,4,5,5,4,4,5,5,6,6,7,7,6,6,7,7, 109 | 4,4,5,5,4,4,5,5,6,6,7,7,6,6,7,7, 110 | 8,8,9,9,8,8,9,9,10,10,11,11,10,10,11,11, 111 | 8,8,9,9,8,8,9,9,10,10,11,11,10,10,11,11, 112 | 12,12,13,13,12,12,13,13,14,14,15,15,14,14,15,15, 113 | 12,12,13,13,12,12,13,13,14,14,15,15,14,14,15,15, 114 | 8,8,9,9,8,8,9,9,10,10,11,11,10,10,11,11, 115 | 8,8,9,9,8,8,9,9,10,10,11,11,10,10,11,11, 116 | 12,12,13,13,12,12,13,13,14,14,15,15,14,14,15,15, 117 | 12,12,13,13,12,12,13,13,14,14,15,15,14,14,15,15 118 | }; 119 | -------------------------------------------------------------------------------- /extern/libmorton/libmorton/include/morton3D.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Libmorton - Methods to encode/decode 64-bit morton codes from/to 32-bit (x,y,z) coordinates 4 | // Warning: morton.h will always point to the functions that use the fastest available method. 5 | 6 | #include 7 | #include 8 | #include "morton3D_LUTs.h" 9 | #include "morton_common.h" 10 | 11 | using namespace std; 12 | 13 | // AVAILABLE METHODS FOR ENCODING 14 | template inline morton m3D_e_sLUT(const coord x, const coord y, const coord z); 15 | template inline morton m3D_e_sLUT_ET(const coord x, const coord y, const coord z); 16 | template inline morton m3D_e_LUT(const coord x, const coord y, const coord z); 17 | template inline morton m3D_e_LUT_ET(const coord x, const coord y, const coord z); 18 | template inline morton m3D_e_magicbits(const coord x, const coord y, const coord z); 19 | template inline morton m3D_e_for(const coord x, const coord y, const coord z); 20 | template inline morton m3D_e_for_ET(const coord x, const coord y, const coord z); 21 | 22 | // AVAILABLE METHODS FOR DECODING 23 | template inline void m3D_d_sLUT(const morton m, coord& x, coord& y, coord& z); 24 | template inline void m3D_d_sLUT_ET(const morton m, coord& x, coord& y, coord& z); 25 | template inline void m3D_d_LUT(const morton m, coord& x, coord& y, coord& z); 26 | template inline void m3D_d_LUT_ET(const morton m, coord& x, coord& y, coord& z); 27 | template inline void m3D_d_magicbits(const morton m, coord& x, coord& y, coord& z); 28 | template inline void m3D_d_for(const morton m, coord& x, coord& y, coord& z); 29 | template inline void m3D_d_for_ET(const morton m, coord& x, coord& y, coord& z); 30 | 31 | // ENCODE 3D Morton code : Pre-Shifted LookUpTable (sLUT) 32 | template 33 | inline morton m3D_e_sLUT(const coord x, const coord y, const coord z) { 34 | morton answer = 0; 35 | const static morton EIGHTBITMASK = 0x000000FF; 36 | for (unsigned int i = sizeof(coord); i > 0; --i) { 37 | unsigned int shift = (i - 1) * 8; 38 | answer = 39 | answer << 24 | 40 | (Morton3D_encode_z_256[(z >> shift) & EIGHTBITMASK] | 41 | Morton3D_encode_y_256[(y >> shift) & EIGHTBITMASK] | 42 | Morton3D_encode_x_256[(x >> shift) & EIGHTBITMASK]); 43 | } 44 | return answer; 45 | } 46 | 47 | // ENCODE 3D Morton code : LookUpTable (LUT) 48 | template 49 | inline morton m3D_e_LUT(const coord x, const coord y, const coord z) { 50 | morton answer = 0; 51 | const static morton EIGHTBITMASK = 0x000000FF; 52 | for (unsigned int i = sizeof(coord); i > 0; --i) { 53 | unsigned int shift = (i - 1) * 8; 54 | answer = 55 | answer << 24 | 56 | (Morton3D_encode_x_256[(z >> shift) & EIGHTBITMASK] << 2) | 57 | (Morton3D_encode_x_256[(y >> shift) & EIGHTBITMASK] << 1) | 58 | Morton3D_encode_x_256[(x >> shift) & EIGHTBITMASK]; 59 | } 60 | return answer; 61 | } 62 | 63 | // HELPER METHOD for ET LUT encode 64 | template 65 | inline morton compute3D_ET_LUT_encode(const coord c, const coord *LUT) { 66 | const static morton EIGHTBITMASK = 0x000000FF; 67 | unsigned long maxbit = 0; 68 | morton answer = 0; 69 | if (findFirstSetBit(c, &maxbit) == 0) { return 0; } 70 | for (int i = ceil((maxbit + 1) / 8.0f) ; i >= 0; --i){ 71 | unsigned int shift = i* 8; 72 | answer = answer << 24 | (LUT[(c >> shift) & EIGHTBITMASK]); 73 | } 74 | return answer; 75 | } 76 | 77 | // ENCODE 3D Morton code : Pre-shifted LookUpTable (LUT) (Early Termination version) 78 | // This version tries to terminate early when there are no more bits to process 79 | // Figuring this out is probably too costly in most cases. 80 | template 81 | inline morton m3D_e_sLUT_ET(const coord x, const coord y, const coord z) { 82 | morton answer_x = compute3D_ET_LUT_encode(x, Morton3D_encode_x_256); 83 | morton answer_y = compute3D_ET_LUT_encode(y, Morton3D_encode_y_256); 84 | morton answer_z = compute3D_ET_LUT_encode(z, Morton3D_encode_z_256); 85 | return answer_z | answer_y | answer_x; 86 | } 87 | 88 | // ENCODE 3D Morton code : LookUpTable (LUT) (Early termination version) 89 | // This version tries to terminate early when there are no more bits to process 90 | // Figuring this out is probably too costly in most cases. 91 | template 92 | inline morton m3D_e_LUT_ET(const coord x, const coord y, const coord z) { 93 | morton answer_x = compute3D_ET_LUT_encode(x, Morton3D_encode_x_256); 94 | morton answer_y = compute3D_ET_LUT_encode(y, Morton3D_encode_x_256); 95 | morton answer_z = compute3D_ET_LUT_encode(z, Morton3D_encode_x_256); 96 | return (answer_z << 2) | (answer_y << 1) | answer_x; 97 | } 98 | 99 | // HELPER METHOD: Magic bits encoding (helper method) 100 | template 101 | static inline morton morton3D_SplitBy3bits(const coord a) { 102 | const morton* masks = (sizeof(morton) <= 4) ? reinterpret_cast(magicbit3D_masks32) : reinterpret_cast(magicbit3D_masks64); 103 | morton x = a; 104 | x = x & masks[0]; 105 | x = (x | x << 16) & masks[1]; 106 | x = (x | x << 8) & masks[2]; 107 | x = (x | x << 4) & masks[3]; 108 | x = (x | x << 2) & masks[4]; 109 | return x; 110 | } 111 | 112 | // ENCODE 3D Morton code : Magic bits method 113 | // This method uses certain bit patterns (magic bits) to split bits in the coordinates 114 | template 115 | inline morton m3D_e_magicbits(const coord x, const coord y, const coord z){ 116 | return morton3D_SplitBy3bits(x) | (morton3D_SplitBy3bits(y) << 1) | (morton3D_SplitBy3bits(z) << 2); 117 | } 118 | 119 | // ENCODE 3D Morton code : For loop 120 | // This is the most naive way of encoding coordinates into a morton code 121 | template 122 | inline morton m3D_e_for(const coord x, const coord y, const coord z){ 123 | morton answer = 0; 124 | unsigned int checkbits = static_cast(floor((sizeof(morton) * 8.0f / 3.0f))); 125 | for (unsigned int i = 0; i < checkbits; ++i) { 126 | morton mshifted= static_cast(1) << i; // Here we need to cast 0x1 to 64bits, otherwise there is a bug when morton code is larger than 32 bits 127 | unsigned int shift = 2 * i; // because you have to shift back i and forth 3*i 128 | answer |= ((x & mshifted) << shift) 129 | | ((y & mshifted) << (shift + 1)) 130 | | ((z & mshifted) << (shift + 2)); 131 | } 132 | return answer; 133 | } 134 | 135 | // ENCODE 3D Morton code : For loop (Early termination version) 136 | // In case of the for loop, figuring out when to stop early has huge benefits. 137 | template 138 | inline morton m3D_e_for_ET(const coord x, const coord y, const coord z) { 139 | morton answer = 0; 140 | unsigned long x_max = 0, y_max = 0, z_max = 0; 141 | unsigned int checkbits = static_cast(floor((sizeof(morton) * 8.0f / 3.0f))); 142 | findFirstSetBit(x, &x_max); 143 | findFirstSetBit(y, &y_max); 144 | findFirstSetBit(z, &z_max); 145 | checkbits = min((unsigned long)checkbits, max(z_max, max(x_max, y_max)) + (unsigned long) 1); 146 | for (unsigned int i = 0; i < checkbits; ++i) { 147 | morton m_shifted = static_cast(1) << i; // Here we need to cast 0x1 to 64bits, otherwise there is a bug when morton code is larger than 32 bits 148 | unsigned int shift = 2 * i; 149 | answer |= ((x & m_shifted) << shift) 150 | | ((y & m_shifted) << (shift + 1)) 151 | | ((z & m_shifted) << (shift + 2)); 152 | } 153 | return answer; 154 | } 155 | 156 | 157 | // HELPER METHOD for LUT decoding 158 | // todo: wouldn't this be better with 8-bit aligned decode LUT? 159 | template 160 | inline coord morton3D_DecodeCoord_LUT256(const morton m, const uint_fast8_t *LUT, const unsigned int startshift) { 161 | morton a = 0; 162 | morton NINEBITMASK = 0x000001ff; 163 | unsigned int loops = (sizeof(morton) <= 4) ? 4 : 7; // ceil for 32bit, floor for 64bit 164 | for (unsigned int i = 0; i < loops; ++i){ 165 | a |= (LUT[(m >> ((i * 9) + startshift)) & NINEBITMASK] << (3 * i)); 166 | } 167 | return static_cast(a); 168 | } 169 | 170 | // DECODE 3D Morton code : Shifted LUT 171 | template 172 | inline void m3D_d_sLUT(const morton m, coord& x, coord& y, coord& z) { 173 | x = morton3D_DecodeCoord_LUT256(m, Morton3D_decode_x_512, 0); 174 | y = morton3D_DecodeCoord_LUT256(m, Morton3D_decode_y_512, 0); 175 | z = morton3D_DecodeCoord_LUT256(m, Morton3D_decode_z_512, 0); 176 | } 177 | 178 | // DECODE 3D Morton code : LUT 179 | template 180 | inline void m3D_d_LUT(const morton m, coord& x, coord& y, coord& z) { 181 | x = morton3D_DecodeCoord_LUT256(m, Morton3D_decode_x_512, 0); 182 | y = morton3D_DecodeCoord_LUT256(m, Morton3D_decode_x_512, 1); 183 | z = morton3D_DecodeCoord_LUT256(m, Morton3D_decode_x_512, 2); 184 | } 185 | 186 | // DECODE 3D Morton code : Shifted LUT (Early termination version) 187 | template 188 | inline void m3D_d_sLUT_ET(const morton m, coord& x, coord& y, coord& z){ 189 | x = 0; y = 0; z = 0; 190 | morton NINEBITMASK = 0x000001ff; 191 | unsigned long firstbit_location = 0; 192 | if (!findFirstSetBit(m, &firstbit_location)) { return; } 193 | unsigned int i = 0; 194 | unsigned int shiftback = 0; 195 | while (firstbit_location >= i) { 196 | morton m_shifted = (m >> i) & NINEBITMASK; 197 | x |= Morton3D_decode_x_512[m_shifted] << shiftback; 198 | y |= Morton3D_decode_y_512[m_shifted] << shiftback; 199 | z |= Morton3D_decode_z_512[m_shifted] << shiftback; 200 | shiftback += 3; 201 | i += 9; 202 | } 203 | return; 204 | } 205 | 206 | // DECODE 3D Morton code : LUT (Early termination version) 207 | template 208 | inline void m3D_d_LUT_ET(const morton m, coord& x, coord& y, coord& z){ 209 | x = 0; y = 0; z = 0; 210 | morton NINEBITMASK = 0x000001ff; 211 | unsigned long firstbit_location = 0; 212 | if (!findFirstSetBit(m, &firstbit_location)) { return; } 213 | unsigned int i = 0; 214 | unsigned int shiftback = 0; 215 | while (i <= firstbit_location) { 216 | x = x | Morton3D_decode_x_512[(m >> i) & NINEBITMASK] << shiftback; 217 | y = y | Morton3D_decode_x_512[(m >> (i+1)) & NINEBITMASK] << shiftback; 218 | z = z | Morton3D_decode_x_512[(m >> (i+2)) & NINEBITMASK] << shiftback; 219 | i += 9; 220 | shiftback += 3; 221 | } 222 | return; 223 | } 224 | 225 | // HELPER METHOD for Magic bits decoding 226 | template 227 | static inline coord morton3D_GetThirdBits(const morton m) { 228 | morton* masks = (sizeof(morton) <= 4) ? reinterpret_cast(magicbit3D_masks32) : reinterpret_cast(magicbit3D_masks64); 229 | morton x = m & masks[4]; 230 | x = (x ^ (x >> 2)) & masks[3]; 231 | x = (x ^ (x >> 4)) & masks[2]; 232 | x = (x ^ (x >> 8)) & masks[1]; 233 | x = (x ^ (x >> 16)) & masks[0]; 234 | return static_cast(x); 235 | } 236 | 237 | // DECODE 3D Morton code : Magic bits 238 | // This method splits the morton codes bits by using certain patterns (magic bits) 239 | template 240 | inline void m3D_d_magicbits(const morton m, coord& x, coord& y, coord& z){ 241 | x = morton3D_GetThirdBits(m); 242 | y = morton3D_GetThirdBits(m >> 1); 243 | z = morton3D_GetThirdBits(m >> 2); 244 | } 245 | 246 | // DECODE 3D Morton code : For loop 247 | template 248 | inline void m3D_d_for(const morton m, coord& x, coord& y, coord& z){ 249 | x = 0; y = 0; z = 0; 250 | unsigned int checkbits = static_cast(floor((sizeof(morton) * 8.0f / 3.0f))); 251 | for (unsigned int i = 0; i <= checkbits; ++i) { 252 | morton selector = 1; 253 | unsigned int shift_selector = 3 * i; 254 | unsigned int shiftback = 2 * i; 255 | x |= (m & (selector << shift_selector)) >> (shiftback); 256 | y |= (m & (selector << (shift_selector + 1))) >> (shiftback + 1); 257 | z |= (m & (selector << (shift_selector + 2))) >> (shiftback + 2); 258 | } 259 | } 260 | 261 | // DECODE 3D Morton code : For loop (Early termination version) 262 | template 263 | inline void m3D_d_for_ET(const morton m, coord& x, coord& y, coord& z) { 264 | x = 0; y = 0; z = 0; 265 | float defaultbits = floor((sizeof(morton) * 8.0f / 3.0f)); 266 | unsigned long firstbit_location = 0; 267 | if(!findFirstSetBit(m, &firstbit_location)) return; 268 | unsigned int checkbits = static_cast(min(defaultbits, firstbit_location / 3.0f)); 269 | for (unsigned int i = 0; i <= checkbits; ++i) { 270 | morton selector = 1; 271 | unsigned int shift_selector = 3 * i; 272 | unsigned int shiftback = 2 * i; 273 | x |= (m & (selector << shift_selector)) >> (shiftback); 274 | y |= (m & (selector << (shift_selector + 1))) >> (shiftback + 1); 275 | z |= (m & (selector << (shift_selector + 2))) >> (shiftback + 2); 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /extern/libmorton/libmorton/include/morton3D_LUTs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // Magicbits masks 6 | static uint_fast32_t magicbit3D_masks32[5] = { 0x000003ff, 0x30000ff, 0x0300f00f, 0x30c30c3, 0x9249249 }; 7 | static uint_fast64_t magicbit3D_masks64[5] = { 0x1f000000001ffff, 0x1f0000ff0000ff, 0x100f00f00f00f00f, 0x10c30c30c30c30c3, 0x1249249249249249 }; 8 | 9 | // Version with lookup table 10 | static const uint_fast32_t Morton3D_encode_x_256[256] = 11 | { 12 | 0x00000000, 13 | 0x00000001, 0x00000008, 0x00000009, 0x00000040, 0x00000041, 0x00000048, 0x00000049, 0x00000200, 14 | 0x00000201, 0x00000208, 0x00000209, 0x00000240, 0x00000241, 0x00000248, 0x00000249, 0x00001000, 15 | 0x00001001, 0x00001008, 0x00001009, 0x00001040, 0x00001041, 0x00001048, 0x00001049, 0x00001200, 16 | 0x00001201, 0x00001208, 0x00001209, 0x00001240, 0x00001241, 0x00001248, 0x00001249, 0x00008000, 17 | 0x00008001, 0x00008008, 0x00008009, 0x00008040, 0x00008041, 0x00008048, 0x00008049, 0x00008200, 18 | 0x00008201, 0x00008208, 0x00008209, 0x00008240, 0x00008241, 0x00008248, 0x00008249, 0x00009000, 19 | 0x00009001, 0x00009008, 0x00009009, 0x00009040, 0x00009041, 0x00009048, 0x00009049, 0x00009200, 20 | 0x00009201, 0x00009208, 0x00009209, 0x00009240, 0x00009241, 0x00009248, 0x00009249, 0x00040000, 21 | 0x00040001, 0x00040008, 0x00040009, 0x00040040, 0x00040041, 0x00040048, 0x00040049, 0x00040200, 22 | 0x00040201, 0x00040208, 0x00040209, 0x00040240, 0x00040241, 0x00040248, 0x00040249, 0x00041000, 23 | 0x00041001, 0x00041008, 0x00041009, 0x00041040, 0x00041041, 0x00041048, 0x00041049, 0x00041200, 24 | 0x00041201, 0x00041208, 0x00041209, 0x00041240, 0x00041241, 0x00041248, 0x00041249, 0x00048000, 25 | 0x00048001, 0x00048008, 0x00048009, 0x00048040, 0x00048041, 0x00048048, 0x00048049, 0x00048200, 26 | 0x00048201, 0x00048208, 0x00048209, 0x00048240, 0x00048241, 0x00048248, 0x00048249, 0x00049000, 27 | 0x00049001, 0x00049008, 0x00049009, 0x00049040, 0x00049041, 0x00049048, 0x00049049, 0x00049200, 28 | 0x00049201, 0x00049208, 0x00049209, 0x00049240, 0x00049241, 0x00049248, 0x00049249, 0x00200000, 29 | 0x00200001, 0x00200008, 0x00200009, 0x00200040, 0x00200041, 0x00200048, 0x00200049, 0x00200200, 30 | 0x00200201, 0x00200208, 0x00200209, 0x00200240, 0x00200241, 0x00200248, 0x00200249, 0x00201000, 31 | 0x00201001, 0x00201008, 0x00201009, 0x00201040, 0x00201041, 0x00201048, 0x00201049, 0x00201200, 32 | 0x00201201, 0x00201208, 0x00201209, 0x00201240, 0x00201241, 0x00201248, 0x00201249, 0x00208000, 33 | 0x00208001, 0x00208008, 0x00208009, 0x00208040, 0x00208041, 0x00208048, 0x00208049, 0x00208200, 34 | 0x00208201, 0x00208208, 0x00208209, 0x00208240, 0x00208241, 0x00208248, 0x00208249, 0x00209000, 35 | 0x00209001, 0x00209008, 0x00209009, 0x00209040, 0x00209041, 0x00209048, 0x00209049, 0x00209200, 36 | 0x00209201, 0x00209208, 0x00209209, 0x00209240, 0x00209241, 0x00209248, 0x00209249, 0x00240000, 37 | 0x00240001, 0x00240008, 0x00240009, 0x00240040, 0x00240041, 0x00240048, 0x00240049, 0x00240200, 38 | 0x00240201, 0x00240208, 0x00240209, 0x00240240, 0x00240241, 0x00240248, 0x00240249, 0x00241000, 39 | 0x00241001, 0x00241008, 0x00241009, 0x00241040, 0x00241041, 0x00241048, 0x00241049, 0x00241200, 40 | 0x00241201, 0x00241208, 0x00241209, 0x00241240, 0x00241241, 0x00241248, 0x00241249, 0x00248000, 41 | 0x00248001, 0x00248008, 0x00248009, 0x00248040, 0x00248041, 0x00248048, 0x00248049, 0x00248200, 42 | 0x00248201, 0x00248208, 0x00248209, 0x00248240, 0x00248241, 0x00248248, 0x00248249, 0x00249000, 43 | 0x00249001, 0x00249008, 0x00249009, 0x00249040, 0x00249041, 0x00249048, 0x00249049, 0x00249200, 44 | 0x00249201, 0x00249208, 0x00249209, 0x00249240, 0x00249241, 0x00249248, 0x00249249 45 | }; 46 | 47 | static const uint_fast32_t Morton3D_encode_y_256[256] = { 48 | 0x00000000, 49 | 0x00000002, 0x00000010, 0x00000012, 0x00000080, 0x00000082, 0x00000090, 0x00000092, 0x00000400, 50 | 0x00000402, 0x00000410, 0x00000412, 0x00000480, 0x00000482, 0x00000490, 0x00000492, 0x00002000, 51 | 0x00002002, 0x00002010, 0x00002012, 0x00002080, 0x00002082, 0x00002090, 0x00002092, 0x00002400, 52 | 0x00002402, 0x00002410, 0x00002412, 0x00002480, 0x00002482, 0x00002490, 0x00002492, 0x00010000, 53 | 0x00010002, 0x00010010, 0x00010012, 0x00010080, 0x00010082, 0x00010090, 0x00010092, 0x00010400, 54 | 0x00010402, 0x00010410, 0x00010412, 0x00010480, 0x00010482, 0x00010490, 0x00010492, 0x00012000, 55 | 0x00012002, 0x00012010, 0x00012012, 0x00012080, 0x00012082, 0x00012090, 0x00012092, 0x00012400, 56 | 0x00012402, 0x00012410, 0x00012412, 0x00012480, 0x00012482, 0x00012490, 0x00012492, 0x00080000, 57 | 0x00080002, 0x00080010, 0x00080012, 0x00080080, 0x00080082, 0x00080090, 0x00080092, 0x00080400, 58 | 0x00080402, 0x00080410, 0x00080412, 0x00080480, 0x00080482, 0x00080490, 0x00080492, 0x00082000, 59 | 0x00082002, 0x00082010, 0x00082012, 0x00082080, 0x00082082, 0x00082090, 0x00082092, 0x00082400, 60 | 0x00082402, 0x00082410, 0x00082412, 0x00082480, 0x00082482, 0x00082490, 0x00082492, 0x00090000, 61 | 0x00090002, 0x00090010, 0x00090012, 0x00090080, 0x00090082, 0x00090090, 0x00090092, 0x00090400, 62 | 0x00090402, 0x00090410, 0x00090412, 0x00090480, 0x00090482, 0x00090490, 0x00090492, 0x00092000, 63 | 0x00092002, 0x00092010, 0x00092012, 0x00092080, 0x00092082, 0x00092090, 0x00092092, 0x00092400, 64 | 0x00092402, 0x00092410, 0x00092412, 0x00092480, 0x00092482, 0x00092490, 0x00092492, 0x00400000, 65 | 0x00400002, 0x00400010, 0x00400012, 0x00400080, 0x00400082, 0x00400090, 0x00400092, 0x00400400, 66 | 0x00400402, 0x00400410, 0x00400412, 0x00400480, 0x00400482, 0x00400490, 0x00400492, 0x00402000, 67 | 0x00402002, 0x00402010, 0x00402012, 0x00402080, 0x00402082, 0x00402090, 0x00402092, 0x00402400, 68 | 0x00402402, 0x00402410, 0x00402412, 0x00402480, 0x00402482, 0x00402490, 0x00402492, 0x00410000, 69 | 0x00410002, 0x00410010, 0x00410012, 0x00410080, 0x00410082, 0x00410090, 0x00410092, 0x00410400, 70 | 0x00410402, 0x00410410, 0x00410412, 0x00410480, 0x00410482, 0x00410490, 0x00410492, 0x00412000, 71 | 0x00412002, 0x00412010, 0x00412012, 0x00412080, 0x00412082, 0x00412090, 0x00412092, 0x00412400, 72 | 0x00412402, 0x00412410, 0x00412412, 0x00412480, 0x00412482, 0x00412490, 0x00412492, 0x00480000, 73 | 0x00480002, 0x00480010, 0x00480012, 0x00480080, 0x00480082, 0x00480090, 0x00480092, 0x00480400, 74 | 0x00480402, 0x00480410, 0x00480412, 0x00480480, 0x00480482, 0x00480490, 0x00480492, 0x00482000, 75 | 0x00482002, 0x00482010, 0x00482012, 0x00482080, 0x00482082, 0x00482090, 0x00482092, 0x00482400, 76 | 0x00482402, 0x00482410, 0x00482412, 0x00482480, 0x00482482, 0x00482490, 0x00482492, 0x00490000, 77 | 0x00490002, 0x00490010, 0x00490012, 0x00490080, 0x00490082, 0x00490090, 0x00490092, 0x00490400, 78 | 0x00490402, 0x00490410, 0x00490412, 0x00490480, 0x00490482, 0x00490490, 0x00490492, 0x00492000, 79 | 0x00492002, 0x00492010, 0x00492012, 0x00492080, 0x00492082, 0x00492090, 0x00492092, 0x00492400, 80 | 0x00492402, 0x00492410, 0x00492412, 0x00492480, 0x00492482, 0x00492490, 0x00492492 81 | }; 82 | 83 | static const uint_fast32_t Morton3D_encode_z_256[256] = { 84 | 0x00000000, 85 | 0x00000004, 0x00000020, 0x00000024, 0x00000100, 0x00000104, 0x00000120, 0x00000124, 0x00000800, 86 | 0x00000804, 0x00000820, 0x00000824, 0x00000900, 0x00000904, 0x00000920, 0x00000924, 0x00004000, 87 | 0x00004004, 0x00004020, 0x00004024, 0x00004100, 0x00004104, 0x00004120, 0x00004124, 0x00004800, 88 | 0x00004804, 0x00004820, 0x00004824, 0x00004900, 0x00004904, 0x00004920, 0x00004924, 0x00020000, 89 | 0x00020004, 0x00020020, 0x00020024, 0x00020100, 0x00020104, 0x00020120, 0x00020124, 0x00020800, 90 | 0x00020804, 0x00020820, 0x00020824, 0x00020900, 0x00020904, 0x00020920, 0x00020924, 0x00024000, 91 | 0x00024004, 0x00024020, 0x00024024, 0x00024100, 0x00024104, 0x00024120, 0x00024124, 0x00024800, 92 | 0x00024804, 0x00024820, 0x00024824, 0x00024900, 0x00024904, 0x00024920, 0x00024924, 0x00100000, 93 | 0x00100004, 0x00100020, 0x00100024, 0x00100100, 0x00100104, 0x00100120, 0x00100124, 0x00100800, 94 | 0x00100804, 0x00100820, 0x00100824, 0x00100900, 0x00100904, 0x00100920, 0x00100924, 0x00104000, 95 | 0x00104004, 0x00104020, 0x00104024, 0x00104100, 0x00104104, 0x00104120, 0x00104124, 0x00104800, 96 | 0x00104804, 0x00104820, 0x00104824, 0x00104900, 0x00104904, 0x00104920, 0x00104924, 0x00120000, 97 | 0x00120004, 0x00120020, 0x00120024, 0x00120100, 0x00120104, 0x00120120, 0x00120124, 0x00120800, 98 | 0x00120804, 0x00120820, 0x00120824, 0x00120900, 0x00120904, 0x00120920, 0x00120924, 0x00124000, 99 | 0x00124004, 0x00124020, 0x00124024, 0x00124100, 0x00124104, 0x00124120, 0x00124124, 0x00124800, 100 | 0x00124804, 0x00124820, 0x00124824, 0x00124900, 0x00124904, 0x00124920, 0x00124924, 0x00800000, 101 | 0x00800004, 0x00800020, 0x00800024, 0x00800100, 0x00800104, 0x00800120, 0x00800124, 0x00800800, 102 | 0x00800804, 0x00800820, 0x00800824, 0x00800900, 0x00800904, 0x00800920, 0x00800924, 0x00804000, 103 | 0x00804004, 0x00804020, 0x00804024, 0x00804100, 0x00804104, 0x00804120, 0x00804124, 0x00804800, 104 | 0x00804804, 0x00804820, 0x00804824, 0x00804900, 0x00804904, 0x00804920, 0x00804924, 0x00820000, 105 | 0x00820004, 0x00820020, 0x00820024, 0x00820100, 0x00820104, 0x00820120, 0x00820124, 0x00820800, 106 | 0x00820804, 0x00820820, 0x00820824, 0x00820900, 0x00820904, 0x00820920, 0x00820924, 0x00824000, 107 | 0x00824004, 0x00824020, 0x00824024, 0x00824100, 0x00824104, 0x00824120, 0x00824124, 0x00824800, 108 | 0x00824804, 0x00824820, 0x00824824, 0x00824900, 0x00824904, 0x00824920, 0x00824924, 0x00900000, 109 | 0x00900004, 0x00900020, 0x00900024, 0x00900100, 0x00900104, 0x00900120, 0x00900124, 0x00900800, 110 | 0x00900804, 0x00900820, 0x00900824, 0x00900900, 0x00900904, 0x00900920, 0x00900924, 0x00904000, 111 | 0x00904004, 0x00904020, 0x00904024, 0x00904100, 0x00904104, 0x00904120, 0x00904124, 0x00904800, 112 | 0x00904804, 0x00904820, 0x00904824, 0x00904900, 0x00904904, 0x00904920, 0x00904924, 0x00920000, 113 | 0x00920004, 0x00920020, 0x00920024, 0x00920100, 0x00920104, 0x00920120, 0x00920124, 0x00920800, 114 | 0x00920804, 0x00920820, 0x00920824, 0x00920900, 0x00920904, 0x00920920, 0x00920924, 0x00924000, 115 | 0x00924004, 0x00924020, 0x00924024, 0x00924100, 0x00924104, 0x00924120, 0x00924124, 0x00924800, 116 | 0x00924804, 0x00924820, 0x00924824, 0x00924900, 0x00924904, 0x00924920, 0x00924924 117 | }; 118 | 119 | static const uint_fast8_t Morton3D_decode_x_512[512] = { 120 | 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 121 | 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 122 | 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 123 | 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 124 | 4, 5, 4, 5, 4, 5, 4, 5, 6, 7, 6, 7, 6, 7, 6, 7, 125 | 4, 5, 4, 5, 4, 5, 4, 5, 6, 7, 6, 7, 6, 7, 6, 7, 126 | 4, 5, 4, 5, 4, 5, 4, 5, 6, 7, 6, 7, 6, 7, 6, 7, 127 | 4, 5, 4, 5, 4, 5, 4, 5, 6, 7, 6, 7, 6, 7, 6, 7, 128 | 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 129 | 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 130 | 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 131 | 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 132 | 4, 5, 4, 5, 4, 5, 4, 5, 6, 7, 6, 7, 6, 7, 6, 7, 133 | 4, 5, 4, 5, 4, 5, 4, 5, 6, 7, 6, 7, 6, 7, 6, 7, 134 | 4, 5, 4, 5, 4, 5, 4, 5, 6, 7, 6, 7, 6, 7, 6, 7, 135 | 4, 5, 4, 5, 4, 5, 4, 5, 6, 7, 6, 7, 6, 7, 6, 7, 136 | 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 137 | 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 138 | 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 139 | 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 140 | 4, 5, 4, 5, 4, 5, 4, 5, 6, 7, 6, 7, 6, 7, 6, 7, 141 | 4, 5, 4, 5, 4, 5, 4, 5, 6, 7, 6, 7, 6, 7, 6, 7, 142 | 4, 5, 4, 5, 4, 5, 4, 5, 6, 7, 6, 7, 6, 7, 6, 7, 143 | 4, 5, 4, 5, 4, 5, 4, 5, 6, 7, 6, 7, 6, 7, 6, 7, 144 | 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 145 | 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 146 | 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 147 | 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 2, 3, 2, 3, 2, 3, 148 | 4, 5, 4, 5, 4, 5, 4, 5, 6, 7, 6, 7, 6, 7, 6, 7, 149 | 4, 5, 4, 5, 4, 5, 4, 5, 6, 7, 6, 7, 6, 7, 6, 7, 150 | 4, 5, 4, 5, 4, 5, 4, 5, 6, 7, 6, 7, 6, 7, 6, 7, 151 | 4, 5, 4, 5, 4, 5, 4, 5, 6, 7, 6, 7, 6, 7, 6, 7 152 | }; 153 | 154 | static const uint_fast8_t Morton3D_decode_y_512[512] = { 155 | 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 156 | 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 157 | 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 158 | 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 159 | 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 160 | 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 161 | 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 162 | 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 163 | 4, 4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 164 | 6, 6, 7, 7, 6, 6, 7, 7, 6, 6, 7, 7, 6, 6, 7, 7, 165 | 4, 4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 166 | 6, 6, 7, 7, 6, 6, 7, 7, 6, 6, 7, 7, 6, 6, 7, 7, 167 | 4, 4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 168 | 6, 6, 7, 7, 6, 6, 7, 7, 6, 6, 7, 7, 6, 6, 7, 7, 169 | 4, 4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 170 | 6, 6, 7, 7, 6, 6, 7, 7, 6, 6, 7, 7, 6, 6, 7, 7, 171 | 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 172 | 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 173 | 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 174 | 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 175 | 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 176 | 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 177 | 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 178 | 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 2, 2, 3, 3, 179 | 4, 4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 180 | 6, 6, 7, 7, 6, 6, 7, 7, 6, 6, 7, 7, 6, 6, 7, 7, 181 | 4, 4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 182 | 6, 6, 7, 7, 6, 6, 7, 7, 6, 6, 7, 7, 6, 6, 7, 7, 183 | 4, 4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 184 | 6, 6, 7, 7, 6, 6, 7, 7, 6, 6, 7, 7, 6, 6, 7, 7, 185 | 4, 4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 4, 4, 5, 5, 186 | 6, 6, 7, 7, 6, 6, 7, 7, 6, 6, 7, 7, 6, 6, 7, 7 187 | }; 188 | 189 | static const uint_fast8_t Morton3D_decode_z_512[512] = { 190 | 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 191 | 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 192 | 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 193 | 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 194 | 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 195 | 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 196 | 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 197 | 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 198 | 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 199 | 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 200 | 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 201 | 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 202 | 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 203 | 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 204 | 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 205 | 2, 2, 2, 2, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 206 | 4, 4, 4, 4, 5, 5, 5, 5, 4, 4, 4, 4, 5, 5, 5, 5, 207 | 4, 4, 4, 4, 5, 5, 5, 5, 4, 4, 4, 4, 5, 5, 5, 5, 208 | 6, 6, 6, 6, 7, 7, 7, 7, 6, 6, 6, 6, 7, 7, 7, 7, 209 | 6, 6, 6, 6, 7, 7, 7, 7, 6, 6, 6, 6, 7, 7, 7, 7, 210 | 4, 4, 4, 4, 5, 5, 5, 5, 4, 4, 4, 4, 5, 5, 5, 5, 211 | 4, 4, 4, 4, 5, 5, 5, 5, 4, 4, 4, 4, 5, 5, 5, 5, 212 | 6, 6, 6, 6, 7, 7, 7, 7, 6, 6, 6, 6, 7, 7, 7, 7, 213 | 6, 6, 6, 6, 7, 7, 7, 7, 6, 6, 6, 6, 7, 7, 7, 7, 214 | 4, 4, 4, 4, 5, 5, 5, 5, 4, 4, 4, 4, 5, 5, 5, 5, 215 | 4, 4, 4, 4, 5, 5, 5, 5, 4, 4, 4, 4, 5, 5, 5, 5, 216 | 6, 6, 6, 6, 7, 7, 7, 7, 6, 6, 6, 6, 7, 7, 7, 7, 217 | 6, 6, 6, 6, 7, 7, 7, 7, 6, 6, 6, 6, 7, 7, 7, 7, 218 | 4, 4, 4, 4, 5, 5, 5, 5, 4, 4, 4, 4, 5, 5, 5, 5, 219 | 4, 4, 4, 4, 5, 5, 5, 5, 4, 4, 4, 4, 5, 5, 5, 5, 220 | 6, 6, 6, 6, 7, 7, 7, 7, 6, 6, 6, 6, 7, 7, 7, 7, 221 | 6, 6, 6, 6, 7, 7, 7, 7, 6, 6, 6, 6, 7, 7, 7, 7 222 | }; -------------------------------------------------------------------------------- /extern/libmorton/libmorton/include/morton_BMI.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(__BMI2__) 4 | 5 | #endif -------------------------------------------------------------------------------- /extern/libmorton/libmorton/include/morton_LUT_generators.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "morton2D.h" 4 | #include "morton3D.h" 5 | #include 6 | 7 | template 8 | void printTable(const element* table, size_t howmany, unsigned int splitat){ 9 | for (size_t i = 0; i < howmany; i++){ 10 | if (i % splitat == 0){ cout << endl; } 11 | printf("%u ,", static_cast(table[i])); 12 | } 13 | cout << endl; 14 | } 15 | 16 | void generate2D_EncodeLUT(size_t how_many_bits, uint_fast16_t*& x_table, uint_fast16_t*& y_table, bool print_tables){ 17 | size_t total = 1 << how_many_bits; 18 | x_table = (uint_fast16_t*)malloc(total * sizeof(uint_fast16_t)); 19 | y_table = (uint_fast16_t*)malloc(total * sizeof(uint_fast16_t)); 20 | 21 | for (uint_fast32_t i = 0; i < total; i++){ 22 | x_table[i] = (uint_fast16_t) m2D_e_magicbits(i, 0); 23 | y_table[i] = (uint_fast16_t)m2D_e_magicbits(0, i); 24 | } 25 | 26 | if (print_tables){ 27 | cout << "X Table " << endl; 28 | printTable(x_table, total, 8); 29 | cout << "Y Table " << endl; 30 | printTable(y_table, total, 8); 31 | } 32 | } 33 | 34 | void generate2D_DecodeLUT(size_t how_many_bits, uint_fast8_t*& x_table, uint_fast8_t*& y_table, bool print_tables){ 35 | size_t total = 1 << how_many_bits; 36 | x_table = (uint_fast8_t*)malloc(total * sizeof(uint_fast8_t)); 37 | y_table = (uint_fast8_t*)malloc(total * sizeof(uint_fast8_t)); 38 | 39 | //generate tables 40 | for (size_t i = 0; i < total; i++) { 41 | m2D_d_for(i, x_table[i], y_table[i]); 42 | } 43 | 44 | if (print_tables) { 45 | cout << "X Table " << endl; 46 | printTable(x_table, total, 16); 47 | cout << "Y Table " << endl; 48 | printTable(y_table, total, 16); 49 | } 50 | } 51 | 52 | void generate3D_EncodeLUT(size_t how_many_bits, uint_fast32_t*& x_table, uint_fast32_t*& y_table, uint_fast32_t*& z_table, bool print_tables){ 53 | // how many items 54 | size_t total = 1 << how_many_bits; 55 | x_table = (uint_fast32_t*)malloc(total * sizeof(uint_fast32_t)); 56 | y_table = (uint_fast32_t*)malloc(total * sizeof(uint_fast32_t)); 57 | z_table = (uint_fast32_t*)malloc(total * sizeof(uint_fast32_t)); 58 | 59 | for (uint_fast32_t i = 0; i < total; i++){ 60 | x_table[i] = (uint_fast32_t) m3D_e_magicbits(i, 0, 0); 61 | y_table[i] = (uint_fast32_t) m3D_e_magicbits(0, i, 0); 62 | z_table[i] = (uint_fast32_t) m3D_e_magicbits(0, 0, i); 63 | } 64 | 65 | if (print_tables){ 66 | cout << "X Table " << endl; 67 | printTable(x_table, total, 8); 68 | cout << "Y Table " << endl; 69 | printTable(y_table, total, 8); 70 | cout << "Z Table " << endl; 71 | printTable(z_table, total, 8); 72 | } 73 | } 74 | 75 | // Generate a decode tables for 3D morton code 76 | // how_many_bits should be a multiple of three 77 | void generate3D_DecodeLUT(size_t how_many_bits, uint_fast8_t*& x_table, uint_fast8_t*& y_table, uint_fast8_t*& z_table, bool print_tables){ 78 | size_t total = 1 << how_many_bits; 79 | x_table = (uint_fast8_t*) malloc(total * sizeof(uint_fast8_t)); 80 | y_table = (uint_fast8_t*) malloc(total * sizeof(uint_fast8_t)); 81 | z_table = (uint_fast8_t*) malloc(total * sizeof(uint_fast8_t)); 82 | 83 | //generate tables 84 | for (size_t i = 0; i < total; i++){ 85 | x_table[i] = morton3D_GetThirdBits(i); 86 | y_table[i] = morton3D_GetThirdBits(i >> 1); 87 | z_table[i] = morton3D_GetThirdBits(i >> 2); 88 | } 89 | 90 | if (print_tables){ 91 | cout << "X Table " << endl; 92 | printTable(x_table, total, 16); 93 | cout << "Y Table " << endl; 94 | printTable(y_table, total, 16); 95 | cout << "Z Table " << endl; 96 | printTable(z_table, total, 16); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /extern/libmorton/libmorton/include/morton_common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Libmorton - Common helper methods needed in Morton encoding/decoding 4 | 5 | #include 6 | #if _MSC_VER 7 | #include 8 | #endif 9 | 10 | template 11 | inline bool findFirstSetBit(const morton x, unsigned long* firstbit_location) { 12 | #if _MSC_VER && !_WIN64 13 | // 32 BIT on 32 BIT 14 | if (sizeof(morton) <= 4) { 15 | return _BitScanReverse(firstbit_location, x) != 0; 16 | } 17 | // 64 BIT on 32 BIT 18 | else { 19 | *firstbit_location = 0; 20 | if (_BitScanReverse(firstbit_location, (x >> 32))) { // check first part 21 | firstbit_location += 32; 22 | return true; 23 | } 24 | return _BitScanReverse(firstbit_location, (x & 0xFFFFFFFF)) != 0; 25 | } 26 | #elif _MSC_VER && _WIN64 27 | // 32 or 64 BIT on 64 BIT 28 | return _BitScanReverse64(firstbit_location, x) != 0; 29 | #elif __GNUC__ 30 | if (x == 0) { 31 | return false; 32 | } 33 | else { 34 | *firstbit_location = static_cast((sizeof(morton)*8) - __builtin_clzll(x)); 35 | return true; 36 | } 37 | #endif 38 | } -------------------------------------------------------------------------------- /include/CompactNSearch: -------------------------------------------------------------------------------- 1 | #include "CompactNSearch.h" 2 | -------------------------------------------------------------------------------- /include/CompactNSearch.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Config.h" 4 | #include "DataStructures.h" 5 | #include "PointSet.h" 6 | 7 | #include 8 | 9 | namespace CompactNSearch 10 | { 11 | 12 | struct NeighborhoodSearchNotInitialized : public std::exception 13 | { 14 | virtual char const* what() const noexcept override { return "Neighborhood search was not initialized."; } 15 | }; 16 | 17 | /** 18 | * @class NeighborhoodSearch 19 | * Stores point data multiple set of points in which neighborhood information for a fixed 20 | * radius r should be generated. 21 | */ 22 | class NeighborhoodSearch 23 | { 24 | 25 | public: 26 | 27 | /** 28 | * Constructor. 29 | * Creates a new instance of the neighborhood search class. 30 | * @param r Search radius. If two points are closer to each other than a distance r they are considered neighbors. 31 | * @param erase_empty_cells If true. Empty cells in spatial hashing grid are erased if the points move. 32 | */ 33 | NeighborhoodSearch(Real r, bool erase_empty_cells = false); 34 | 35 | /** 36 | * Destructor. 37 | */ 38 | virtual ~NeighborhoodSearch() = default; 39 | 40 | /** 41 | * Get method to access a point set. 42 | * @param i Index of the point set to retrieve. 43 | */ 44 | PointSet const& point_set(unsigned int i) const { return m_point_sets[i]; } 45 | 46 | /** 47 | * Get method to access a point set. 48 | * @param i Index of the point set to retrieve. 49 | */ 50 | PointSet & point_set(unsigned int i) { return m_point_sets[i]; } 51 | 52 | 53 | /** 54 | * Returns the number of point sets contained in the search. 55 | */ 56 | std::size_t n_point_sets() const { return m_point_sets.size(); } 57 | 58 | /** 59 | * Get method to access the list of point sets. 60 | */ 61 | std::vector const& point_sets() const { return m_point_sets; } 62 | 63 | /** 64 | * Get method to access the list of point sets. 65 | */ 66 | std::vector & point_sets() { return m_point_sets; } 67 | 68 | /** 69 | * Increases the size of a point set under the assumption that the existing points remain at 70 | * the same position. 71 | * @param i Index of point set that will be resized. 72 | * @param x Pointer to the point position data. Must point to continguous data of 3 * n 73 | * real values. 74 | * @param n Number of points. 75 | */ 76 | void resize_point_set(unsigned int i, Real const* x, std::size_t n); 77 | 78 | /** 79 | * Creates and adds a new set of points. 80 | * @param x Pointer to the point position data. Must point to continguous data of 3 * n 81 | * real values. 82 | * @param n Number of points. 83 | * @param is_dynamic Specifies whether the point positions will change for future queries. 84 | * @param search_neighbors If true, neighbors in all other point sets are searched. 85 | * @param find_neighbors If true, the new point set is activated in the neighborhood search of all other point sets. 86 | * @returns Returns unique identifier in form of an index assigned to the newly created point 87 | * set. 88 | */ 89 | unsigned int add_point_set(Real const* x, std::size_t n, bool is_dynamic = true, 90 | bool search_neighbors = true, bool find_neighbors = true, void *user_data = nullptr) 91 | { 92 | m_point_sets.push_back({x, n, is_dynamic, user_data}); 93 | m_activation_table.add_point_set(search_neighbors, find_neighbors); 94 | return static_cast(m_point_sets.size() - 1); 95 | } 96 | 97 | /** 98 | * Performs the actual query. This method will assign a list of neighboring points to each point 99 | * every added point set. 100 | */ 101 | void find_neighbors(bool points_changed = true); 102 | 103 | /** 104 | * Performs the actual query for a single point. This method returns a list of neighboring points. Note: That points_changed() must be called each time 105 | * when the positions of a point set changed. 106 | */ 107 | void find_neighbors(unsigned int point_set_id, unsigned int point_index, std::vector> &neighbors); 108 | 109 | /** 110 | * Performs the actual query for a single point x. This method returns a list of neighboring points in all existing point sets. Note: That points_changed() must be called each time 111 | * when the positions of a point set changed. 112 | */ 113 | void find_neighbors(Real const* x, std::vector> &neighbors); 114 | 115 | /** 116 | * Update neighborhood search data structures after a position change. 117 | * If general find_neighbors() function is called there is no requirement to manually update the point sets. 118 | * Otherwise, in case of using point-wise search (find_neighbors(i, j, neighbors)) the method must be called explicitly. 119 | * TODO: Implement function for individual pointset. -> update_point_set(i) 120 | */ 121 | void update_point_sets(); 122 | 123 | /** 124 | * Update neighborhood search data structures after changing the activation table. 125 | * If general find_neighbors() function is called there is no requirement to manually update the point sets. 126 | * Otherwise, in case of using point-wise search (find_neighbors(i, j, neighbors)) the method must be called explicitly. 127 | */ 128 | void update_activation_table(); 129 | 130 | 131 | /* 132 | * Generates a sort table according to a space-filling Z curve. Any array-based per point 133 | * information can then be reordered using the function sort_field of the PointSet class. 134 | * Please note that the position data will not be modified by this class, such that the user has 135 | * to invoke the sort_field function on the position array. Moreover, be aware the the grid has 136 | * be reinitialized after each sort. Therefore, the points should not be reordered too 137 | * frequently. 138 | */ 139 | void z_sort(); 140 | 141 | /* 142 | * @returns Returns the radius in which point neighbors are searched. 143 | */ 144 | Real radius() const { return std::sqrt(m_r2); } 145 | 146 | /** 147 | * Sets the radius in which point point neighbors are searched. 148 | * @param r Search radius. 149 | */ 150 | void set_radius(Real r) 151 | { 152 | m_r2 = r * r; 153 | m_inv_cell_size = static_cast(1.0 / r); 154 | m_initialized = false; 155 | } 156 | 157 | /** Activate/deactivate that neighbors in point set j are found when searching for neighbors of point set i. 158 | * @param i Index of searching point set. 159 | * @param j Index of point set of which points should/shouldn't be found by point set i. 160 | * @param active Flag in order to (de)activate that points in i find point in j. 161 | */ 162 | void set_active(unsigned int i, unsigned int j, bool active) 163 | { 164 | m_activation_table.set_active(i, j, active); 165 | m_initialized = false; 166 | } 167 | 168 | /** Activate/Deactivate all point set pairs containing the given index. If search_neighbors is true, neighbors in all other point sets are searched. 169 | * If find_neighbors is true, the new point set is activated in the neighborhood search of all other point sets. 170 | * @param i Index of searching point set. 171 | * @param search_neighbors If true/false enables/disables that point set i searches points in all other point sets. 172 | * @param find_neighbors If true/false enable/disables that point set i is found by all other point sets. 173 | */ 174 | void set_active(unsigned int i, bool search_neighbors = true, bool find_neighbors = true) 175 | { 176 | m_activation_table.set_active(i, search_neighbors, find_neighbors); 177 | m_initialized = false; 178 | } 179 | 180 | /** Activate/Deactivate all point set pairs. 181 | */ 182 | void set_active(bool active) 183 | { 184 | m_activation_table.set_active(active); 185 | m_initialized = false; 186 | } 187 | 188 | /** Returns true if point set i searchs points in point set j. 189 | * @param i Searching point set. 190 | * @param j Set of points to be found by i. 191 | */ 192 | bool is_active(unsigned int i, unsigned int j) const 193 | { 194 | return m_activation_table.is_active(i, j); 195 | } 196 | 197 | private: 198 | 199 | void init(); 200 | void update_hash_table(std::vector& to_delete); 201 | void erase_empty_entries(std::vector const& to_delete); 202 | void query(); 203 | void query(unsigned int point_set_id, unsigned int point_index, std::vector> &neighbors); 204 | void query(Real const* xa, std::vector> &neighbors); 205 | 206 | HashKey cell_index(Real const* x) const; 207 | 208 | private: 209 | 210 | 211 | std::vector m_point_sets; 212 | ActivationTable m_activation_table, m_old_activation_table; 213 | 214 | Real m_inv_cell_size; 215 | Real m_r2; 216 | std::unordered_map m_map; 217 | std::vector m_entries; 218 | 219 | bool m_erase_empty_cells; 220 | bool m_initialized; 221 | }; 222 | 223 | } 224 | -------------------------------------------------------------------------------- /include/Config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace CompactNSearch 4 | { 5 | #ifdef USE_DOUBLE 6 | using Real = double; 7 | #else 8 | using Real = float; 9 | #endif 10 | } 11 | 12 | #define INITIAL_NUMBER_OF_INDICES 50 13 | #define INITIAL_NUMBER_OF_NEIGHBORS 50 14 | 15 | #ifdef _MSC_VER 16 | #include 17 | #elif defined(__APPLE__) && defined(__clang__) 18 | #include 19 | #include 20 | #else 21 | #include 22 | #endif 23 | -------------------------------------------------------------------------------- /include/DataStructures.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Config.h" 4 | 5 | #include 6 | #include 7 | 8 | namespace CompactNSearch 9 | { 10 | struct PointID 11 | { 12 | unsigned int point_set_id; 13 | unsigned int point_id; 14 | 15 | bool operator==(PointID const& other) const 16 | { 17 | return point_id == other.point_id && point_set_id == other.point_set_id; 18 | } 19 | }; 20 | 21 | struct HashKey 22 | { 23 | HashKey() = default; 24 | HashKey(int i, int j, int k) 25 | { 26 | this->k[0] = i, this->k[1] = j, this->k[2] = k; 27 | } 28 | 29 | HashKey& operator=(HashKey const& other) 30 | { 31 | k[0] = other.k[0]; 32 | k[1] = other.k[1]; 33 | k[2] = other.k[2]; 34 | return *this; 35 | } 36 | 37 | bool operator==(HashKey const& other) const 38 | { 39 | return 40 | k[0] == other.k[0] && 41 | k[1] == other.k[1] && 42 | k[2] == other.k[2]; 43 | } 44 | 45 | bool operator!=(HashKey const& other) const 46 | { 47 | return !(*this == other); 48 | } 49 | 50 | int k[3]; 51 | }; 52 | 53 | struct HashEntry 54 | { 55 | HashEntry() : n_searching_points(0u) 56 | { 57 | indices.reserve(INITIAL_NUMBER_OF_INDICES); 58 | } 59 | 60 | HashEntry(PointID const& id) : n_searching_points(0u) 61 | { 62 | add(id); 63 | } 64 | 65 | void add(PointID const& id) 66 | { 67 | indices.push_back(id); 68 | } 69 | 70 | void erase(PointID const& id) 71 | { 72 | auto it = std::find(indices.begin(), indices.end(), id); 73 | if (it != indices.end()) 74 | indices.erase(it); 75 | } 76 | 77 | unsigned int n_indices() const 78 | { 79 | return static_cast(indices.size()); 80 | } 81 | 82 | std::vector indices; 83 | unsigned int n_searching_points; 84 | }; 85 | 86 | struct SpatialHasher 87 | { 88 | std::size_t operator()(HashKey const& k) const 89 | { 90 | return static_cast( 91 | static_cast(73856093) * static_cast(k.k[0]) ^ 92 | static_cast(19349663) * static_cast(k.k[1]) ^ 93 | static_cast(83492791) * static_cast(k.k[2])); 94 | } 95 | }; 96 | 97 | class Spinlock 98 | { 99 | public: 100 | 101 | void lock() 102 | { 103 | while (m_lock.test_and_set(std::memory_order_acquire)); 104 | } 105 | 106 | void unlock() 107 | { 108 | m_lock.clear(std::memory_order_release); 109 | } 110 | 111 | Spinlock() = default; 112 | Spinlock(Spinlock const& other) {}; 113 | Spinlock& operator=(Spinlock const& other) { return *this; } 114 | 115 | private: 116 | 117 | std::atomic_flag m_lock = ATOMIC_FLAG_INIT; 118 | }; 119 | 120 | class ActivationTable 121 | { 122 | private: 123 | std::vector> m_table; 124 | 125 | public: 126 | 127 | bool operator==(ActivationTable const& other) const 128 | { 129 | return m_table == other.m_table; 130 | } 131 | 132 | bool operator!=(ActivationTable const& other) const 133 | { 134 | return !(m_table == other.m_table); 135 | } 136 | 137 | /** Add point set. If search_neighbors is true, neighbors in all other point sets are searched. 138 | * If find_neighbors is true, the new point set is activated in the neighborhood search of all other point sets. 139 | */ 140 | void add_point_set(bool search_neighbors = true, bool find_neighbors = true) 141 | { 142 | // add column to each row 143 | auto size = m_table.size(); 144 | for (auto i = 0u; i < size; i++) 145 | { 146 | m_table[i].resize(size + 1); 147 | m_table[i][size] = static_cast(find_neighbors); 148 | } 149 | 150 | // add new row 151 | m_table.resize(size + 1); 152 | m_table[size].resize(size + 1); 153 | for (auto i = 0u; i < size + 1; i++) 154 | m_table[size][i] = static_cast(search_neighbors); 155 | } 156 | 157 | /** Activate/Deactivate that neighbors in point set index2 are found when searching for neighbors of point set index1. 158 | */ 159 | void set_active(unsigned int index1, unsigned int index2, bool active) 160 | { 161 | m_table[index1][index2] = static_cast(active); 162 | } 163 | 164 | /** Activate/Deactivate all point set pairs containing the given index. If search_neighbors is true, neighbors in all other point sets are searched. 165 | * If find_neighbors is true, the new point set is activated in the neighborhood search of all other point sets. 166 | */ 167 | void set_active(unsigned int index, bool search_neighbors = true, bool find_neighbors = true) 168 | { 169 | auto size = m_table.size(); 170 | for (auto i = 0u; i < size; i++) 171 | { 172 | m_table[i][index] = static_cast(find_neighbors); 173 | m_table[index][i] = static_cast(search_neighbors); 174 | } 175 | m_table[index][index] = static_cast(search_neighbors && find_neighbors); 176 | } 177 | 178 | /** Activate/Deactivate all point set pairs. 179 | */ 180 | void set_active(bool active) 181 | { 182 | auto size = m_table.size(); 183 | for (auto i = 0u; i < size; i++) 184 | for (auto j = 0u; j < size; j++) 185 | m_table[i][j] = static_cast(active); 186 | } 187 | 188 | bool is_active(unsigned int index1, unsigned int index2) const 189 | { 190 | return m_table[index1][index2] != 0; 191 | } 192 | 193 | bool is_searching_neighbors(unsigned int const index) const 194 | { 195 | for (auto i = 0u; i < m_table[index].size(); i++) 196 | { 197 | if (m_table[index][i]) 198 | { 199 | return true; 200 | } 201 | } 202 | return false; 203 | } 204 | }; 205 | } 206 | -------------------------------------------------------------------------------- /include/PointSet.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace CompactNSearch 7 | { 8 | class NeighborhoodSearch; 9 | 10 | /** 11 | * @class PointSet. 12 | * Represents a set of points in three-dimensional space. 13 | */ 14 | class PointSet 15 | { 16 | 17 | public: 18 | 19 | /** 20 | * Copy constructor. 21 | */ 22 | PointSet(PointSet const& other) 23 | { 24 | *this = other; 25 | } 26 | 27 | /** 28 | * Assignment operator. 29 | */ 30 | PointSet& operator=(PointSet const& other) 31 | { 32 | m_x = other.m_x; 33 | m_n = other.m_n; 34 | m_dynamic = other.m_dynamic; 35 | m_user_data = other.m_user_data; 36 | 37 | m_neighbors = other.m_neighbors; 38 | m_keys = other.m_keys; 39 | m_old_keys = other.m_old_keys; 40 | 41 | m_sort_table = other.m_sort_table; 42 | 43 | m_locks.resize(other.m_locks.size()); 44 | for (unsigned int i = 0; i < other.m_locks.size(); ++i) 45 | { 46 | m_locks[i].resize(other.m_locks[i].size()); 47 | } 48 | 49 | return *this; 50 | } 51 | 52 | /** 53 | * Returns the number of neighbors of point i in the given point set. 54 | * @param i Point index. 55 | * @returns Number of points neighboring point i in point set point_set. 56 | */ 57 | std::size_t n_neighbors(unsigned int point_set, unsigned int i) const 58 | { 59 | return static_cast(m_neighbors[point_set][i].size()); 60 | } 61 | 62 | /** 63 | * Fetches id pair of kth neighbor of point i in the given point set. 64 | * @param point_set Point set index of other point set where neighbors have been searched. 65 | * @param i Point index for which the neighbor id should be returned. 66 | * @param k Represents kth neighbor of point i. 67 | * @returns Index of neighboring point i in point set point_set. 68 | */ 69 | unsigned int neighbor(unsigned int point_set, unsigned int i, unsigned int k) const 70 | { 71 | return m_neighbors[point_set][i][k]; 72 | } 73 | 74 | /** 75 | * Fetches the neighbor list of point i in the given point set. 76 | * @param point_set Point set index of other point set where neighbors have been searched. 77 | * @param i Point index for which the neighbor list should be returned. 78 | * @returns Neighbor list of point i in point set point_set. 79 | */ 80 | const std::vector& neighbor_list(unsigned int point_set, unsigned int i) const 81 | { 82 | return m_neighbors[point_set][i]; 83 | } 84 | 85 | /** 86 | * Returns the number of points contained in the point set. 87 | */ 88 | std::size_t n_points() const { return m_n; } 89 | 90 | /* 91 | * Returns true, if the point locations may be updated by the user. 92 | **/ 93 | bool is_dynamic() const { return m_dynamic; } 94 | 95 | /** 96 | * If true is passed, the point positions may be altered by the user. 97 | */ 98 | void set_dynamic(bool v) { m_dynamic = v; } 99 | 100 | /** 101 | * Return the user data which can be attached to a point set. 102 | */ 103 | void *get_user_data() { return m_user_data; } 104 | 105 | /** 106 | * Reorders an array according to a previously generated sort table by invocation of the method 107 | * "z_sort" of class "NeighborhoodSearch". Please note that the method "z_sort" of class 108 | * "Neighborhood search" has to be called beforehand. 109 | */ 110 | template 111 | void sort_field(T* lst) const; 112 | 113 | private: 114 | 115 | friend NeighborhoodSearch; 116 | PointSet(Real const* x, std::size_t n, bool dynamic, void *user_data = nullptr) 117 | : m_x(x), m_n(n), m_dynamic(dynamic), m_user_data(user_data), m_neighbors(n) 118 | , m_keys(n, { 119 | std::numeric_limits::lowest(), 120 | std::numeric_limits::lowest(), 121 | std::numeric_limits::lowest() }) 122 | { 123 | m_old_keys = m_keys; 124 | } 125 | 126 | void resize(Real const* x, std::size_t n) 127 | { 128 | m_x = x; 129 | m_n = n; 130 | m_keys.resize(n, { 131 | std::numeric_limits::lowest(), 132 | std::numeric_limits::lowest(), 133 | std::numeric_limits::lowest() }); 134 | m_old_keys.resize(n, { 135 | std::numeric_limits::lowest(), 136 | std::numeric_limits::lowest(), 137 | std::numeric_limits::lowest() }); 138 | m_neighbors.resize(n); 139 | } 140 | 141 | Real const* point(unsigned int i) const { return &m_x[3*i]; } 142 | 143 | private: 144 | 145 | Real const* m_x; 146 | std::size_t m_n; 147 | bool m_dynamic; 148 | void *m_user_data; 149 | 150 | std::vector>> m_neighbors; 151 | 152 | std::vector m_keys, m_old_keys; 153 | std::vector> m_locks; 154 | std::vector m_sort_table; 155 | }; 156 | 157 | template void 158 | PointSet::sort_field(T* lst) const 159 | { 160 | if (m_sort_table.empty()) 161 | { 162 | std::cerr << "WARNING: No sort table was generated for the current point set. " 163 | << "First invoke the method 'z_sort' of the class 'NeighborhoodSearch.'" << std::endl; 164 | return; 165 | } 166 | 167 | std::vector tmp(lst, lst + m_sort_table.size()); 168 | std::transform(m_sort_table.begin(), m_sort_table.end(), 169 | #ifdef _MSC_VER 170 | stdext::unchecked_array_iterator(lst), 171 | #else 172 | lst, 173 | #endif 174 | [&](int i){ return tmp[i]; }); 175 | } 176 | } 177 | 178 | -------------------------------------------------------------------------------- /src/CompactNSearch.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "../extern/libmorton/libmorton/include/morton.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | 13 | namespace CompactNSearch 14 | { 15 | namespace 16 | { 17 | // Determines Morten value according to z-curve. 18 | inline uint_fast64_t 19 | z_value(HashKey const& key) 20 | { 21 | return morton3D_64_encode( 22 | static_cast(static_cast(key.k[0]) - 23 | (std::numeric_limits::lowest() + 1)), 24 | static_cast(static_cast(key.k[1]) - 25 | (std::numeric_limits::lowest() + 1)), 26 | static_cast(static_cast(key.k[2]) - 27 | (std::numeric_limits::lowest() + 1)) 28 | ); 29 | } 30 | } 31 | 32 | 33 | NeighborhoodSearch::NeighborhoodSearch(Real r, bool erase_empty_cells) 34 | : m_r2(r * r), m_inv_cell_size(static_cast(1.0 / r)) 35 | , m_erase_empty_cells(erase_empty_cells) 36 | , m_initialized(false) 37 | { 38 | if (r <= 0.0) 39 | { 40 | std::cerr << "WARNING: Neighborhood search may not be initialized with a zero or negative" 41 | << " search radius. This may lead to unexpected behavior." << std::endl; 42 | } 43 | } 44 | 45 | // Computes triple index to a world space position x. 46 | HashKey 47 | NeighborhoodSearch::cell_index(Real const* x) const 48 | { 49 | HashKey ret; 50 | for (unsigned int i = 0; i < 3; ++i) 51 | { 52 | if (x[i] >= 0.0) ret.k[i] = static_cast(m_inv_cell_size * x[i]); 53 | else ret.k[i] = static_cast(m_inv_cell_size * x[i]) - 1; 54 | } 55 | return ret; 56 | } 57 | 58 | // Determines permutation table for point array. 59 | void 60 | NeighborhoodSearch::z_sort() 61 | { 62 | for (PointSet& d : m_point_sets) 63 | { 64 | d.m_sort_table.resize(d.n_points()); 65 | std::iota(d.m_sort_table.begin(), d.m_sort_table.end(), 0); 66 | 67 | std::sort(d.m_sort_table.begin(), d.m_sort_table.end(), 68 | [&](unsigned int a, unsigned int b) 69 | { 70 | return z_value(cell_index(d.point(a))) < z_value(cell_index(d.point(b))); 71 | }); 72 | } 73 | m_initialized = false; 74 | } 75 | 76 | 77 | // Build hash table and entry array from scratch. 78 | void 79 | NeighborhoodSearch::init() 80 | { 81 | m_entries.clear(); 82 | m_map.clear(); 83 | 84 | // Determine existing entries. 85 | std::vector temp_keys; 86 | for (unsigned int j = 0; j < m_point_sets.size(); ++j) 87 | { 88 | PointSet& d = m_point_sets[j]; 89 | d.m_locks.resize(m_point_sets.size()); 90 | for (auto& l : d.m_locks) 91 | l.resize(d.n_points()); 92 | for (unsigned int i = 0; i < d.n_points(); i++) 93 | { 94 | HashKey const& key = cell_index(d.point(i)); 95 | d.m_keys[i] = d.m_old_keys[i] = key; 96 | 97 | auto it = m_map.find(key); 98 | if (it == m_map.end()) 99 | { 100 | m_entries.push_back({{ j, i }}); 101 | if (m_activation_table.is_searching_neighbors(j)) 102 | m_entries.back().n_searching_points++; 103 | temp_keys.push_back(key); 104 | m_map[key] = static_cast(m_entries.size() - 1); 105 | } 106 | else 107 | { 108 | m_entries[it->second].add({j, i}); 109 | if (m_activation_table.is_searching_neighbors(j)) 110 | m_entries[it->second].n_searching_points++; 111 | } 112 | } 113 | } 114 | 115 | m_map.clear(); 116 | for (unsigned int i = 0; i < m_entries.size(); ++i) 117 | { 118 | m_map.emplace(temp_keys[i], i); 119 | } 120 | 121 | m_initialized = true; 122 | } 123 | 124 | 125 | void 126 | NeighborhoodSearch::resize_point_set(unsigned int index, Real const* x, std::size_t size) 127 | { 128 | PointSet& point_set = m_point_sets[index]; 129 | std::size_t old_size = point_set.n_points(); 130 | 131 | if (!m_initialized) 132 | { 133 | throw NeighborhoodSearchNotInitialized{}; 134 | } 135 | 136 | // Delete old entries. (Shrink) 137 | if (old_size > size) 138 | { 139 | std::vector to_delete; 140 | if (m_erase_empty_cells) 141 | { 142 | to_delete.reserve(m_entries.size()); 143 | } 144 | 145 | for (unsigned int i = static_cast(size); i < old_size; i++) 146 | { 147 | HashKey const& key = point_set.m_keys[i]; 148 | auto it = m_map.find(key); 149 | m_entries[it->second].erase({ index, i }); 150 | if (m_activation_table.is_searching_neighbors(index)) 151 | m_entries[it->second].n_searching_points--; 152 | if (m_erase_empty_cells) 153 | { 154 | if (m_entries[it->second].n_indices() == 0) 155 | { 156 | to_delete.push_back(it->second); 157 | } 158 | } 159 | } 160 | if (m_erase_empty_cells) 161 | { 162 | erase_empty_entries(to_delete); 163 | } 164 | } 165 | 166 | point_set.resize(x, size); 167 | 168 | // Insert new entries. (Grow) 169 | for (unsigned int i = static_cast(old_size); i < point_set.n_points(); i++) 170 | { 171 | HashKey key = cell_index(point_set.point(i)); 172 | point_set.m_keys[i] = point_set.m_old_keys[i] = key; 173 | auto it = m_map.find(key); 174 | if (it == m_map.end()) 175 | { 176 | m_entries.push_back({ { index, i } }); 177 | if (m_activation_table.is_searching_neighbors(index)) 178 | m_entries.back().n_searching_points++; 179 | m_map[key] = static_cast(m_entries.size() - 1); 180 | } 181 | else 182 | { 183 | m_entries[it->second].add({ index, i }); 184 | if (m_activation_table.is_searching_neighbors(index)) 185 | m_entries.back().n_searching_points++; 186 | } 187 | } 188 | 189 | // Resize spinlock arrays. 190 | for (auto& l : point_set.m_locks) 191 | l.resize(point_set.n_points()); 192 | 193 | } 194 | 195 | void 196 | NeighborhoodSearch::update_activation_table() 197 | { 198 | if (m_activation_table != m_old_activation_table) 199 | { 200 | for (auto& entry : m_entries) 201 | { 202 | auto& n = entry.n_searching_points; 203 | n = 0u; 204 | for (auto const& idx : entry.indices) 205 | { 206 | if (m_activation_table.is_searching_neighbors(idx.point_set_id)) 207 | { 208 | ++n; 209 | } 210 | } 211 | } 212 | m_old_activation_table = m_activation_table; 213 | } 214 | } 215 | 216 | void 217 | NeighborhoodSearch::find_neighbors(bool points_changed_) 218 | { 219 | if (points_changed_) 220 | { 221 | update_point_sets(); 222 | } 223 | update_activation_table(); 224 | query(); 225 | } 226 | 227 | void 228 | NeighborhoodSearch::update_point_sets() 229 | { 230 | if (!m_initialized) 231 | { 232 | init(); 233 | m_initialized = true; 234 | } 235 | 236 | // Precompute cell indices. 237 | #ifdef _MSC_VER 238 | concurrency::parallel_for_each( 239 | #elif defined(__APPLE__) && defined(__clang__) 240 | std::for_each(oneapi::dpl::execution::par, 241 | #else 242 | __gnu_parallel::for_each( 243 | #endif 244 | m_point_sets.begin(), m_point_sets.end(), [&](PointSet& d) 245 | { 246 | if (d.is_dynamic()) 247 | { 248 | d.m_keys.swap(d.m_old_keys); 249 | for (unsigned int i = 0; i < d.n_points(); ++i) 250 | { 251 | d.m_keys[i] = cell_index(d.point(i)); 252 | } 253 | } 254 | }); 255 | 256 | std::vector to_delete; 257 | if (m_erase_empty_cells) 258 | { 259 | to_delete.reserve(m_entries.size()); 260 | } 261 | update_hash_table(to_delete); 262 | if (m_erase_empty_cells) 263 | { 264 | erase_empty_entries(to_delete); 265 | } 266 | } 267 | 268 | void 269 | NeighborhoodSearch::find_neighbors(unsigned int point_set_id, unsigned int point_index, std::vector> &neighbors) 270 | { 271 | query(point_set_id, point_index, neighbors); 272 | } 273 | 274 | void 275 | NeighborhoodSearch::find_neighbors(Real const* x, std::vector> &neighbors) 276 | { 277 | query(x, neighbors); 278 | } 279 | 280 | void 281 | NeighborhoodSearch::erase_empty_entries(std::vector const& to_delete) 282 | { 283 | if (to_delete.empty()) 284 | return; 285 | 286 | // Indicated empty cells. 287 | m_entries.erase(std::remove_if(m_entries.begin(), m_entries.end(), [](HashEntry const& entry) 288 | { 289 | return entry.indices.empty(); 290 | }), m_entries.end()); 291 | 292 | { 293 | auto it = m_map.begin(); 294 | while (it != m_map.end()) 295 | { 296 | auto& kvp = *it; 297 | 298 | if (kvp.second <= to_delete.front() && kvp.second >= to_delete.back() && 299 | std::binary_search(to_delete.rbegin(), to_delete.rend(), kvp.second)) 300 | { 301 | it = m_map.erase(it); 302 | } 303 | else 304 | { 305 | ++it; 306 | } 307 | } 308 | } 309 | 310 | std::vector*> kvps(m_map.size()); 311 | std::transform(m_map.begin(), m_map.end(), kvps.begin(), 312 | [](std::pair& kvp) 313 | { 314 | return &kvp; 315 | }); 316 | 317 | // Perform neighborhood search. 318 | #ifdef _MSC_VER 319 | concurrency::parallel_for_each( 320 | #elif defined(__APPLE__) && defined(__clang__) 321 | std::for_each(oneapi::dpl::execution::par, 322 | #else 323 | __gnu_parallel::for_each( 324 | #endif 325 | kvps.begin(), kvps.end(), [&](std::pair* kvp_) 326 | { 327 | auto& kvp = *kvp_; 328 | 329 | for (unsigned int i = 0; i < to_delete.size(); ++i) 330 | { 331 | if (kvp.second >= to_delete[i]) 332 | { 333 | kvp.second -= static_cast(to_delete.size() - i); 334 | break; 335 | } 336 | } 337 | }); 338 | } 339 | 340 | void 341 | NeighborhoodSearch::update_hash_table(std::vector& to_delete) 342 | { 343 | // Indicate points changing inheriting cell. 344 | for (unsigned int j = 0; j < m_point_sets.size(); ++j) 345 | { 346 | PointSet& d = m_point_sets[j]; 347 | for (unsigned int i = 0; i < d.n_points(); ++i) 348 | { 349 | if (d.m_keys[i] == d.m_old_keys[i]) continue; 350 | 351 | HashKey const& key = d.m_keys[i]; 352 | auto it = m_map.find(key); 353 | if (it == m_map.end()) 354 | { 355 | m_entries.push_back({{j, i}}); 356 | if (m_activation_table.is_searching_neighbors(j)) 357 | m_entries.back().n_searching_points++; 358 | m_map.insert({ key, static_cast(m_entries.size() - 1) }); 359 | } 360 | else 361 | { 362 | HashEntry& entry = m_entries[it->second]; 363 | entry.add({j, i}); 364 | if (m_activation_table.is_searching_neighbors(j)) 365 | entry.n_searching_points++; 366 | } 367 | 368 | unsigned int entry_index = m_map[d.m_old_keys[i]]; 369 | m_entries[entry_index].erase({j, i}); 370 | if (m_activation_table.is_searching_neighbors(j)) 371 | m_entries[entry_index].n_searching_points--; 372 | if (m_erase_empty_cells) 373 | { 374 | if (m_entries[entry_index].n_indices() == 0) 375 | { 376 | to_delete.push_back(entry_index); 377 | } 378 | } 379 | } 380 | } 381 | 382 | to_delete.erase(std::remove_if(to_delete.begin(), to_delete.end(), 383 | [&](unsigned int index) 384 | { 385 | return m_entries[index].n_indices() != 0; 386 | }), to_delete.end()); 387 | std::sort(to_delete.begin(), to_delete.end(), std::greater()); 388 | } 389 | 390 | void 391 | NeighborhoodSearch::query() 392 | { 393 | for (unsigned int i = 0; i < m_point_sets.size(); i++) 394 | { 395 | PointSet &d = m_point_sets[i]; 396 | d.m_neighbors.resize(m_point_sets.size()); 397 | for (unsigned int j = 0; j < d.m_neighbors.size(); j++) 398 | { 399 | auto &n = d.m_neighbors[j]; 400 | n.resize(d.n_points()); 401 | for (auto& n_ : n) 402 | { 403 | n_.clear(); 404 | if (m_activation_table.is_active(i, j)) 405 | n_.reserve(INITIAL_NUMBER_OF_NEIGHBORS); 406 | } 407 | } 408 | } 409 | 410 | std::vector const*> kvps(m_map.size()); 411 | std::transform(m_map.begin(), m_map.end(), kvps.begin(), 412 | [](std::pair const& kvp) 413 | { 414 | return &kvp; 415 | }); 416 | 417 | // Perform neighborhood search. 418 | #ifdef _MSC_VER 419 | concurrency::parallel_for_each( 420 | #elif defined(__APPLE__) && defined(__clang__) 421 | std::for_each(oneapi::dpl::execution::par, 422 | #else 423 | __gnu_parallel::for_each( 424 | #endif 425 | kvps.begin(), kvps.end(), [&](std::pair const* kvp_) 426 | { 427 | auto const& kvp = *kvp_; 428 | HashEntry const& entry = m_entries[kvp.second]; 429 | HashKey const& key = kvp.first; 430 | 431 | if (entry.n_searching_points == 0u) 432 | { 433 | return; 434 | } 435 | 436 | for (unsigned int a = 0; a < entry.n_indices(); ++a) 437 | { 438 | PointID const& va = entry.indices[a]; 439 | PointSet& da = m_point_sets[va.point_set_id]; 440 | for (unsigned int b = a + 1; b < entry.n_indices(); ++b) 441 | { 442 | PointID const& vb = entry.indices[b]; 443 | PointSet& db = m_point_sets[vb.point_set_id]; 444 | 445 | if (!m_activation_table.is_active(va.point_set_id, vb.point_set_id) && 446 | !m_activation_table.is_active(vb.point_set_id, va.point_set_id)) 447 | { 448 | continue; 449 | } 450 | 451 | Real const* xa = da.point(va.point_id); 452 | Real const* xb = db.point(vb.point_id); 453 | Real tmp = xa[0] - xb[0]; 454 | Real l2 = tmp * tmp; 455 | tmp = xa[1] - xb[1]; 456 | l2 += tmp * tmp; 457 | tmp = xa[2] - xb[2]; 458 | l2 += tmp * tmp; 459 | 460 | if (l2 < m_r2) 461 | { 462 | if (m_activation_table.is_active(va.point_set_id, vb.point_set_id)) 463 | { 464 | da.m_neighbors[vb.point_set_id][va.point_id].push_back(vb.point_id); 465 | } 466 | if (m_activation_table.is_active(vb.point_set_id, va.point_set_id)) 467 | { 468 | db.m_neighbors[va.point_set_id][vb.point_id].push_back(va.point_id); 469 | } 470 | } 471 | } 472 | } 473 | } 474 | ); 475 | 476 | 477 | std::vector> visited(m_entries.size(), {false}); 478 | std::vector entry_locks(m_entries.size()); 479 | 480 | #ifdef _MSC_VER 481 | concurrency::parallel_for_each( 482 | #elif defined(__APPLE__) && defined(__clang__) 483 | std::for_each(oneapi::dpl::execution::par, 484 | #else 485 | __gnu_parallel::for_each( 486 | #endif 487 | kvps.begin(), kvps.end(), [&](std::pair const* kvp_) 488 | { 489 | auto const& kvp = *kvp_; 490 | HashEntry const& entry = m_entries[kvp.second]; 491 | 492 | if (entry.n_searching_points == 0u) 493 | return; 494 | HashKey const& key = kvp.first; 495 | 496 | for (int dj = -1; dj <= 1; dj++) 497 | for (int dk = -1; dk <= 1; dk++) 498 | for (int dl = -1; dl <= 1; dl++) 499 | { 500 | int l_ind = 9 * (dj + 1) + 3 * (dk + 1) + (dl + 1); 501 | if (l_ind == 13) 502 | { 503 | continue; 504 | } 505 | entry_locks[kvp.second].lock(); 506 | if (visited[kvp.second][l_ind]) 507 | { 508 | entry_locks[kvp.second].unlock(); 509 | continue; 510 | } 511 | entry_locks[kvp.second].unlock(); 512 | 513 | 514 | 515 | auto it = m_map.find({ key.k[0] + dj, key.k[1] + dk, key.k[2] + dl }); 516 | if (it == m_map.end()) 517 | continue; 518 | 519 | std::array entry_ids{{kvp.second, it->second}}; 520 | if (entry_ids[0] > entry_ids[1]) 521 | std::swap(entry_ids[0], entry_ids[1]); 522 | entry_locks[entry_ids[0]].lock(); 523 | entry_locks[entry_ids[1]].lock(); 524 | 525 | if (visited[kvp.second][l_ind]) 526 | { 527 | entry_locks[entry_ids[1]].unlock(); 528 | entry_locks[entry_ids[0]].unlock(); 529 | continue; 530 | } 531 | 532 | visited[kvp.second][l_ind] = true; 533 | visited[it->second][26 - l_ind] = true; 534 | 535 | entry_locks[entry_ids[1]].unlock(); 536 | entry_locks[entry_ids[0]].unlock(); 537 | 538 | for (unsigned int i = 0; i < entry.n_indices(); ++i) 539 | { 540 | PointID const& va = entry.indices[i]; 541 | HashEntry const& entry_ = m_entries[it->second]; 542 | unsigned int n_ind = entry_.n_indices(); 543 | for (unsigned int j = 0; j < n_ind; ++j) 544 | { 545 | PointID const& vb = entry_.indices[j]; 546 | PointSet& db = m_point_sets[vb.point_set_id]; 547 | 548 | PointSet& da = m_point_sets[va.point_set_id]; 549 | 550 | if (!m_activation_table.is_active(va.point_set_id, vb.point_set_id) && 551 | !m_activation_table.is_active(vb.point_set_id, va.point_set_id)) 552 | { 553 | continue; 554 | } 555 | 556 | Real const* xa = da.point(va.point_id); 557 | Real const* xb = db.point(vb.point_id); 558 | Real tmp = xa[0] - xb[0]; 559 | Real l2 = tmp * tmp; 560 | tmp = xa[1] - xb[1]; 561 | l2 += tmp * tmp; 562 | tmp = xa[2] - xb[2]; 563 | l2 += tmp * tmp; 564 | if (l2 < m_r2) 565 | { 566 | if (m_activation_table.is_active(va.point_set_id, vb.point_set_id)) 567 | { 568 | da.m_locks[vb.point_set_id][va.point_id].lock(); 569 | da.m_neighbors[vb.point_set_id][va.point_id].push_back(vb.point_id); 570 | da.m_locks[vb.point_set_id][va.point_id].unlock(); 571 | } 572 | if (m_activation_table.is_active(vb.point_set_id, va.point_set_id)) 573 | { 574 | db.m_locks[va.point_set_id][vb.point_id].lock(); 575 | db.m_neighbors[va.point_set_id][vb.point_id].push_back(va.point_id); 576 | db.m_locks[va.point_set_id][vb.point_id].unlock(); 577 | } 578 | } 579 | } 580 | } 581 | } 582 | }); 583 | 584 | } 585 | 586 | 587 | void 588 | NeighborhoodSearch::query(unsigned int point_set_id, unsigned int point_index, std::vector> &neighbors) 589 | { 590 | neighbors.resize(m_point_sets.size()); 591 | PointSet &d = m_point_sets[point_set_id]; 592 | for (unsigned int j = 0; j < m_point_sets.size(); j++) 593 | { 594 | auto &n = neighbors[j]; 595 | n.clear(); 596 | if (m_activation_table.is_active(point_set_id, j)) 597 | n.reserve(INITIAL_NUMBER_OF_NEIGHBORS); 598 | } 599 | 600 | Real const* xa = d.point(point_index); 601 | HashKey hash_key = cell_index(xa); 602 | auto it = m_map.find(hash_key); 603 | std::pair &kvp = *it; 604 | HashEntry const& entry = m_entries[kvp.second]; 605 | 606 | // Perform neighborhood search. 607 | for (unsigned int b = 0; b < entry.n_indices(); ++b) 608 | { 609 | PointID const& vb = entry.indices[b]; 610 | if ((point_set_id != vb.point_set_id) || (point_index != vb.point_id)) 611 | { 612 | if (!m_activation_table.is_active(point_set_id, vb.point_set_id)) 613 | { 614 | continue; 615 | } 616 | 617 | PointSet& db = m_point_sets[vb.point_set_id]; 618 | Real const* xb = db.point(vb.point_id); 619 | Real tmp = xa[0] - xb[0]; 620 | Real l2 = tmp * tmp; 621 | tmp = xa[1] - xb[1]; 622 | l2 += tmp * tmp; 623 | tmp = xa[2] - xb[2]; 624 | l2 += tmp * tmp; 625 | 626 | if (l2 < m_r2) 627 | { 628 | neighbors[vb.point_set_id].push_back(vb.point_id); 629 | } 630 | } 631 | } 632 | 633 | for (int dj = -1; dj <= 1; dj++) 634 | { 635 | for (int dk = -1; dk <= 1; dk++) 636 | { 637 | for (int dl = -1; dl <= 1; dl++) 638 | { 639 | int l_ind = 9 * (dj + 1) + 3 * (dk + 1) + (dl + 1); 640 | if (l_ind == 13) 641 | { 642 | continue; 643 | } 644 | 645 | auto it = m_map.find({ hash_key.k[0] + dj, hash_key.k[1] + dk, hash_key.k[2] + dl }); 646 | if (it == m_map.end()) 647 | continue; 648 | 649 | std::array entry_ids{ { kvp.second, it->second } }; 650 | if (entry_ids[0] > entry_ids[1]) 651 | std::swap(entry_ids[0], entry_ids[1]); 652 | 653 | HashEntry const& entry_ = m_entries[it->second]; 654 | unsigned int n_ind = entry_.n_indices(); 655 | for (unsigned int j = 0; j < n_ind; ++j) 656 | { 657 | PointID const& vb = entry_.indices[j]; 658 | if (!m_activation_table.is_active(point_set_id, vb.point_set_id)) 659 | { 660 | continue; 661 | } 662 | PointSet& db = m_point_sets[vb.point_set_id]; 663 | 664 | Real const* xb = db.point(vb.point_id); 665 | Real tmp = xa[0] - xb[0]; 666 | Real l2 = tmp * tmp; 667 | tmp = xa[1] - xb[1]; 668 | l2 += tmp * tmp; 669 | tmp = xa[2] - xb[2]; 670 | l2 += tmp * tmp; 671 | 672 | if (l2 < m_r2) 673 | { 674 | neighbors[vb.point_set_id].push_back(vb.point_id); 675 | } 676 | } 677 | } 678 | } 679 | } 680 | } 681 | 682 | void 683 | NeighborhoodSearch::query(Real const* xa, std::vector> &neighbors) 684 | { 685 | neighbors.resize(m_point_sets.size()); 686 | for (unsigned int j = 0; j < m_point_sets.size(); j++) 687 | { 688 | auto &n = neighbors[j]; 689 | n.clear(); 690 | n.reserve(INITIAL_NUMBER_OF_NEIGHBORS); 691 | } 692 | 693 | HashKey hash_key = cell_index(xa); 694 | auto it = m_map.find(hash_key); 695 | if (it != m_map.end()) 696 | { 697 | std::pair &kvp = *it; 698 | HashEntry const& entry = m_entries[kvp.second]; 699 | 700 | // Perform neighborhood search. 701 | for (unsigned int b = 0; b < entry.n_indices(); ++b) 702 | { 703 | PointID const& vb = entry.indices[b]; 704 | PointSet& db = m_point_sets[vb.point_set_id]; 705 | Real const* xb = db.point(vb.point_id); 706 | Real tmp = xa[0] - xb[0]; 707 | Real l2 = tmp * tmp; 708 | tmp = xa[1] - xb[1]; 709 | l2 += tmp * tmp; 710 | tmp = xa[2] - xb[2]; 711 | l2 += tmp * tmp; 712 | 713 | if (l2 < m_r2) 714 | { 715 | neighbors[vb.point_set_id].push_back(vb.point_id); 716 | } 717 | } 718 | } 719 | 720 | for (int dj = -1; dj <= 1; dj++) 721 | { 722 | for (int dk = -1; dk <= 1; dk++) 723 | { 724 | for (int dl = -1; dl <= 1; dl++) 725 | { 726 | int l_ind = 9 * (dj + 1) + 3 * (dk + 1) + (dl + 1); 727 | if (l_ind == 13) 728 | { 729 | continue; 730 | } 731 | 732 | auto it = m_map.find({ hash_key.k[0] + dj, hash_key.k[1] + dk, hash_key.k[2] + dl }); 733 | if (it == m_map.end()) 734 | continue; 735 | 736 | HashEntry const& entry_ = m_entries[it->second]; 737 | unsigned int n_ind = entry_.n_indices(); 738 | for (unsigned int j = 0; j < n_ind; ++j) 739 | { 740 | PointID const& vb = entry_.indices[j]; 741 | PointSet& db = m_point_sets[vb.point_set_id]; 742 | 743 | Real const* xb = db.point(vb.point_id); 744 | Real tmp = xa[0] - xb[0]; 745 | Real l2 = tmp * tmp; 746 | tmp = xa[1] - xb[1]; 747 | l2 += tmp * tmp; 748 | tmp = xa[2] - xb[2]; 749 | l2 += tmp * tmp; 750 | 751 | if (l2 < m_r2) 752 | { 753 | neighbors[vb.point_set_id].push_back(vb.point_id); 754 | } 755 | } 756 | } 757 | } 758 | } 759 | } 760 | 761 | 762 | } 763 | 764 | --------------------------------------------------------------------------------