├── .gitattributes ├── .gitignore ├── .gitmodules ├── .travis.yml ├── CMakeLists.txt ├── LICENSE ├── README.md ├── benchmark ├── CMakeLists.txt ├── README.md ├── benchmark_boost_geometry.cpp ├── benchmark_thst.cpp ├── high_resolution_timer.hpp ├── results │ ├── benchmark_dynamic_bgi_vs_thst.png │ ├── benchmark_dynamic_small_bgi_vs_thst.png │ ├── benchmark_dynamic_vsmall_bgi_vs_thst.png │ ├── benchmark_load_bgi_vs_thst.png │ ├── benchmark_query_bgi_vs_thst.png │ ├── benchmark_query_bgi_vs_thst_best.png │ ├── bgi_benchmark_rtree_load_blk_vs_balancing.png │ ├── bgi_benchmark_rtree_load_itr_vs_blk.png │ ├── bgi_benchmark_rtree_query_blk_vs_balancing.png │ ├── bgi_benchmark_rtree_query_itr_vs_blk.png │ ├── bgi_linear_blk_ct.dat │ ├── bgi_linear_ct.dat │ ├── bgi_quadratic_blk_ct.dat │ ├── bgi_quadratic_ct.dat │ ├── bgi_rstar_blk_ct.dat │ ├── bgi_rstar_ct.dat │ ├── plot_results.plt │ ├── thst_benchmark_load_itr.png │ ├── thst_benchmark_query_cst.png │ ├── thst_benchmark_query_itr.png │ ├── thst_quadratic.dat │ ├── thst_quadratic_custom.dat │ ├── thst_quadratic_sphere.dat │ ├── thst_quadratic_sphere_custom.dat │ ├── thst_quadtree.dat │ └── thst_quadtree_custom.dat ├── run_benchmark.bat ├── run_benchmark.sh └── spatial_index_benchmark.hpp ├── bin └── ci │ ├── before_install.sh │ ├── common.sh │ └── script.sh ├── cmake ├── THST.pc.in └── THSTConfig.cmake.in ├── doc ├── README.md ├── docset.cfg ├── genDocs.sh ├── hierarchy.png ├── html.cfg ├── spatial_calls.png ├── spatial_depth.png ├── spatial_order.png └── spatial_query.png ├── include └── THST │ ├── QuadTree.h │ ├── RTree.h │ ├── allocator.h │ ├── bbox.h │ ├── config.h │ ├── indexable.h │ ├── predicates.h │ ├── quad_tree_detail.h │ └── rtree_detail.h └── test ├── CMakeLists.txt ├── custom_allocator.h ├── test_quadtree.cpp └── test_rtree.cpp /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | test/build/ 2 | benchmark/build/ 3 | doc/html 4 | doc/docset 5 | build/ 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "test/doctest"] 2 | path = test/doctest 3 | url = https://github.com/doctest/doctest.git 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Use new trusty images, should yield newer compilers and packages 2 | # ref: https://github.com/genbattle/dkm.git 3 | sudo: required 4 | dist: Rtrusty 5 | language: cpp 6 | 7 | matrix: 8 | include: 9 | - compiler: gcc 10 | addons: 11 | apt: 12 | sources: 13 | - ubuntu-toolchain-r-test 14 | packages: 15 | - libboost-all-dev 16 | - g++-4.4 17 | env: COMPILER=g++-4.4 18 | - compiler: gcc 19 | addons: 20 | apt: 21 | sources: 22 | - ubuntu-toolchain-r-test 23 | packages: 24 | - libboost-all-dev 25 | - g++-4.6 26 | env: COMPILER=g++-4.6 27 | - compiler: gcc 28 | addons: 29 | apt: 30 | sources: 31 | - ubuntu-toolchain-r-test 32 | packages: 33 | - libboost-all-dev 34 | - g++-4.8 35 | env: COMPILER=g++-4.8 36 | - compiler: gcc 37 | addons: 38 | apt: 39 | sources: 40 | - ubuntu-toolchain-r-test 41 | packages: 42 | - libboost-all-dev 43 | - g++-5 44 | env: COMPILER=g++-5 45 | - compiler: clang 46 | addons: 47 | apt: 48 | sources: 49 | - ubuntu-toolchain-r-test 50 | - llvm-toolchain-precise-3.5 51 | packages: 52 | - libboost-all-dev 53 | - clang-3.5 54 | - compiler: clang 55 | addons: 56 | apt: 57 | sources: 58 | - ubuntu-toolchain-r-test 59 | - llvm-toolchain-precise-3.6 60 | packages: 61 | - libboost-all-dev 62 | - clang-3.6 63 | env: COMPILER=clang++-3.6 64 | - compiler: clang 65 | addons: 66 | apt: 67 | sources: 68 | - ubuntu-toolchain-r-test 69 | - llvm-toolchain-precise-3.7 70 | packages: 71 | - libboost-all-dev 72 | - clang-3.7 73 | env: COMPILER=clang++-3.7 74 | - compiler: clang 75 | addons: 76 | apt: 77 | sources: 78 | - ubuntu-toolchain-r-test 79 | - llvm-toolchain-precise-3.8 80 | packages: 81 | - libboost-all-dev 82 | - clang-3.8 83 | env: COMPILER=clang++-3.8 84 | 85 | before_script: 86 | - chmod +x ./bin/ci/before_install.sh 87 | - ./bin/ci/before_install.sh 88 | script: 89 | - chmod +x ./bin/ci/script.sh 90 | - ./bin/ci/script.sh 91 | 92 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5..3.18) 2 | 3 | # version range does not set policies until version 3.12 4 | if(${CMAKE_VERSION} VERSION_LESS 3.12) 5 | cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) 6 | endif() 7 | 8 | project(THST 9 | DESCRIPTION "Templated hierarchical spatial trees designed for high performance and hierarchical spatial partitioning use cases." 10 | LANGUAGES CXX 11 | VERSION 0.1.0 12 | HOMEPAGE_URL "https://github.com/tuxalin/THST" 13 | ) 14 | 15 | add_library(THST INTERFACE) 16 | add_library(THST::THST ALIAS THST) 17 | 18 | target_include_directories(THST 19 | INTERFACE 20 | $ 21 | $ 22 | ) 23 | 24 | if(CMAKE_VERSION VERSION_GREATER 3.7) 25 | target_compile_features(THST INTERFACE cxx_std_11) 26 | endif() 27 | 28 | if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) 29 | add_subdirectory(test) 30 | endif() 31 | 32 | include(GNUInstallDirs) 33 | install( 34 | TARGETS THST 35 | EXPORT THSTTargets 36 | ) 37 | 38 | include(CMakePackageConfigHelpers) 39 | write_basic_package_version_file( 40 | "THSTConfigVersion.cmake" 41 | VERSION "${PROJECT_VERSION}" 42 | COMPATIBILITY SameMajorVersion 43 | ) 44 | configure_package_config_file( 45 | "${PROJECT_SOURCE_DIR}/cmake/${PROJECT_NAME}Config.cmake.in" 46 | "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" 47 | INSTALL_DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake" 48 | ) 49 | 50 | configure_file( 51 | "${PROJECT_SOURCE_DIR}/cmake/${PROJECT_NAME}.pc.in" 52 | "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.pc" 53 | @ONLY 54 | ) 55 | 56 | export( 57 | EXPORT ${PROJECT_NAME}Targets 58 | FILE ${PROJECT_NAME}Targets.cmake 59 | NAMESPACE THST:: 60 | ) 61 | 62 | export(PACKAGE ${PROJECT_NAME}) 63 | 64 | install( 65 | EXPORT ${PROJECT_NAME}Targets 66 | FILE ${PROJECT_NAME}Targets.cmake 67 | NAMESPACE THST:: 68 | DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake 69 | ) 70 | 71 | install( 72 | FILES 73 | "${PROJECT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" 74 | "${PROJECT_BINARY_DIR}/THSTConfigVersion.cmake" 75 | DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/${PROJECT_NAME}/cmake 76 | ) 77 | 78 | install(FILES "${PROJECT_BINARY_DIR}/${PROJECT_NAME}.pc" 79 | DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) 80 | 81 | install( 82 | DIRECTORY ${PROJECT_SOURCE_DIR}/include/THST 83 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 84 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 tuxalin 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Hierarchical spatial trees 2 | [![Build Status](https://travis-ci.org/tuxalin/THST.svg?branch=master)](https://travis-ci.org/tuxalin/THST) 3 | [![Language](https://img.shields.io/badge/language-C++-blue.svg)](https://isocpp.org/) 4 | [![License](http://img.shields.io/badge/license-MIT-blue.svg)](http://opensource.org/licenses/MIT) 5 | 6 | Templated hierarchical spatial trees designed for high-performance and hierarchical spatial partitioning use cases. 7 | 8 | ## Features 9 | 10 | There are two tree implementations, a multi-dimensional RTree and a two-dimensional QuadTree. 11 | 12 | Some of the currently implemented features are: 13 | - hierarchical, you can add values to the internal branch nodes or traverse them 14 | - leaf and depth-first tree traversals for spatial partitioning, via custom iterators 15 | - custom indexable getter similar to boost's 16 | - hierarchical query 17 | - ray box intersection query 18 | - nearest neighbour search 19 | - conditional insert with custom predicates 20 | - support for custom allocators for internal nodes 21 | - estimation for node count given a number of items 22 | - tagging of internal nodes 23 | - the spatial trees have almost identical interfaces 24 | - lightweight, resulting in faster compile times compared to boost(eg. benchmark compilation: 35,3 sec vs 1,4 sec) 25 | - C++03 support 26 | 27 | ## Installation 28 | 29 | The implementation is header only, it's only requirement is at least (C++03) support. 30 | 31 | ## Usage 32 | 33 | How to create and insert items to the trees: 34 | ```cpp 35 | spatial::QuadTree, 2> qtree(bbox.min, bbox.max); 36 | spatial::RTree, 2> rtree; 37 | 38 | const Box2 kBoxes[] = {...}; 39 | qtree.insert(kBoxes, kBoxes + sizeof(kBoxes) / sizeof(kBoxes[0])); 40 | rtree.insert(kBoxes, kBoxes + sizeof(kBoxes) / sizeof(kBoxes[0])); 41 | 42 | Box2 box = {{7, 3}, {14, 6}}; 43 | qtree.insert(box); 44 | rtree.insert(box); 45 | ``` 46 | 47 | Conditional insert: 48 | ```cpp 49 | const decltype(rtree)::bbox_type boxToAdd = {{7, 4}, {14, 6}}; 50 | bool wasAdded = 51 | rtree.insert(boxToAdd, [&boxToAdd](const decltype(rtree)::bbox_type &bbox) { 52 | return !bbox.overlaps(boxToAdd); 53 | }); 54 | ``` 55 | 56 | How to use the indexable getter: 57 | ```cpp 58 | struct Object { 59 | spatial::BoundingBox bbox; 60 | std::string name; 61 | }; 62 | 63 | // helps to get the bounding of the items inserted 64 | struct Indexable { 65 | const int *min(const Object &value) const { return value.bbox.min; } 66 | const int *max(const Object &value) const { return value.bbox.max; } 67 | }; 68 | 69 | spatial::QuadTree qtree(bbox.min, bbox.max); 70 | qtree.insert(objects.begin(), objects.end()); 71 | 72 | spatial::RTree rtree; 73 | rtree.insert(objects.begin(), objects.end()); 74 | ``` 75 | 76 | Leaf and depth traversal: 77 | ```cpp 78 | spatial::RTree rtree; 79 | 80 | // gives the spatial partioning order within the tree 81 | for (auto it = rtree.lbegin(); it.valid(); it.next()) { 82 | std::cout << (*it).name << "\n"; 83 | } 84 | 85 | assert(rtree.levels() > 0); 86 | for (auto it = rtree.dbegin(); it.valid(); it.next()) { 87 | 88 | // traverse current children of the parent node(i.e. upper level) 89 | for (auto nodeIt = it.child(); nodeIt.valid(); nodeIt.next()) { 90 | std::cout << "level: " << nodeIt.level() << " " << (*nodeIt).name 91 | << "\n"; 92 | } 93 | // level of the current internal/hierachical node 94 | std::cout << "level: " << it.level() << "\n"; 95 | } 96 | ``` 97 | 98 | How to use the search algorithms: 99 | ```cpp 100 | Box2 searchBox = {{0, 0}, {8, 31}}; 101 | 102 | std::vector> results; 103 | rtree.query(spatial::intersects<2>(searchBox.min, searchBox.max), std::back_inserter(results)); 104 | rtree.query(spatial::contains<2>(searchBox.min, searchBox.max), std::back_inserter(results)); 105 | 106 | // to be used only if inserted points into the tree 107 | rtree.query(spatial::within<2>(searchBox.min, searchBox.max), std::back_inserter(results)); 108 | 109 | // hierachical query that will break the search if a node is fully contained 110 | rtree.hierachical_query(spatial::intersects<2>(searchBox.min, searchBox.max), std::back_inserter(results)); 111 | 112 | // neatest neighbor search 113 | rtree.nearest(point, radius, std::back_inserter(results)); 114 | ``` 115 | 116 | How to use the ray query: 117 | ```cpp 118 | rayOrigin = Point({ 62, 70 }); 119 | rayDir = Point({ 0, -2 }); 120 | rtree.rayQuery(rayOrigin.data, rayDir.data, std::back_inserter(results), fnTestPredicate); 121 | 122 | // can also provide a filter predicate 123 | auto fnFilterPredicate = [](const Box2& box) {return box.min[0] == 0; }; 124 | rtree.rayQuery(rayOrigin.data, rayDir.data, std::back_inserter(results), fnFilterPredicate); 125 | ``` 126 | 127 | **Be sure to check the [test](test) folder for more detailed usage and examples.** 128 | 129 | ## Benchmarks 130 | 131 | Benchmark setup is based on [spatial_index_benchmark](https://github.com/mloskot/spatial_index_benchmark) by Mateusz Loskot and Adam Wulkiewicz. 132 | 133 | Complete set of result logs in [results](benchmark/results) directory. 134 | 135 | ### Results 136 | 137 | HW: Intel(R) Core(TM) i7-4870HQ CPU @ 2.50GHz, 16 GB RAM; OS: macOS Sierra 10.12.16 138 | 139 | * Loading times for each of the R-tree construction methods 140 | 141 | ![load thst_vs_bgi](benchmark/results/benchmark_load_bgi_vs_thst.png) 142 | 143 | ![load boost::geometry](benchmark/results/bgi_benchmark_rtree_load_itr_vs_blk.png) 144 | 145 | ![load thst](benchmark/results/thst_benchmark_load_itr.png) 146 | 147 | * Query times for each of the R-tree construction methods 148 | 149 | ![query thst_vs_bgi](benchmark/results/benchmark_query_bgi_vs_thst.png) 150 | 151 | ![query boost::geometry](benchmark/results/bgi_benchmark_rtree_query_itr_vs_blk.png) 152 | 153 | ![query thst](benchmark/results/thst_benchmark_query_itr.png) 154 | 155 | ![query thst](benchmark/results/thst_benchmark_query_cst.png) 156 | 157 | * Dynamic use case, average time for each of the R-tree construction methods 158 | 159 | ![dynamic thst_vs_bgi](benchmark/results/benchmark_dynamic_bgi_vs_thst.png) 160 | 161 | For more detailed benchmark results check the [benchmark](benchmark) directory. 162 | 163 | ### Legend 164 | ------ 165 | 166 | * ```bgi``` - boost::geometry::index, compile time 167 | * ```thst``` - thst 168 | * ```ct``` - compile-time specification of rtree parameters 169 | * ```rt``` (or non suffix) - Boost.Geometry-only, run-time specification of rtree parameters 170 | * ```L``` - linear 171 | * ```Q``` - quadratic 172 | * ```QT``` - quadtree 173 | * ```R``` - rstar 174 | * ```itr (or no suffix)``` - iterative insertion method of building rtree 175 | * ```blk``` - bulk loading method of building R-tree (custom algorithm for ```bgi```) 176 | * ```custom``` - custom allocator variant for thst(cache friendly, linear memory) 177 | * ```sphere``` - sphere volume for computing the boxes's volume, better splitting but costlier 178 | * insert 1000000 - number of objects small random boxes 179 | * query 100000 - number of instersection-based queries with random boxes 10x larger than those inserted 180 | * dynamic 200 - number of runs composed of clear, instersection-based queries and insert with small random boxes 181 | 182 | ## Future improvements 183 | 184 | Possible improvements are: 185 | - RTree bulk loading 186 | - OCtree implementation 187 | - reduced memory footprint for 1D and leaves 188 | - support for multiple splitting heuristics 189 | - SSE optimizations 190 | 191 | ## Contributing 192 | 193 | Based on: 194 | - 1983 Original algorithm and test code by Antonin Guttman and Michael Stonebraker, UC Berkely 195 | - ANCI C ported from original test code by Melinda Green 196 | - Sphere volume fix for degeneracy problem submitted by Paul Brook 197 | - Templated C++ port by Greg Douglas 198 | - N-dimensional RTree implementation in C++ by nushoin (https://github.com/nushoin/RTree). 199 | - Nearest neighbour search by Thinh Nguyen (http://andrewd.ces.clemson.edu/courses/cpsc805/references/nearest_search.pdf). 200 | 201 | Bug reports and pull requests are welcome on GitHub at https://github.com/tuxalin/thst. 202 | 203 | ## License 204 | 205 | The code is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). 206 | -------------------------------------------------------------------------------- /benchmark/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | ################################################################################ 2 | # CMake build configuration for spatial_index_benchmark 3 | ################################################################################ 4 | # Copyright (C) 2013 Mateusz Loskot 5 | # 6 | # Distributed under the Boost Software License, Version 1.0. 7 | # (See accompanying file LICENSE_1_0.txt or copy at 8 | # http://www.boost.org/LICENSE_1_0.txt) 9 | ################################################################################ 10 | cmake_minimum_required (VERSION 2.6) 11 | project(spatial_index_benchmark) 12 | 13 | enable_testing() 14 | 15 | # Set a default build type if none was specified 16 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 17 | message(STATUS "Setting build type to 'Release' as none was specified.") 18 | set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE) 19 | # Set the possible values of build type for cmake-gui 20 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" 21 | "MinSizeRel" "RelWithDebInfo") 22 | endif() 23 | 24 | option(BGI_ENABLE_CT 25 | "Enable compile-time boost::geometry::index::rtree parameters" OFF) 26 | 27 | # Compiler flags 28 | if (MSVC) 29 | add_definitions(-D_CRT_SECURE_NO_DEPRECATE) 30 | add_definitions(-D_CRT_SECURE_NO_WARNINGS) 31 | add_definitions(-D_CRT_NONSTDC_NO_WARNING) 32 | add_definitions(-D_SCL_SECURE_NO_WARNINGS) 33 | 34 | if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]") 35 | string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 36 | else() 37 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") 38 | endif() 39 | else() 40 | set(CXX_COMMON_FLAGS "-pedantic -Wpointer-arith -Wcast-align -Wcast-qual") 41 | 42 | if(CMAKE_COMPILER_IS_GNUCXX) 43 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fPIC ${CXX_COMMON_FLAGS} -std=c++0x") 44 | elseif("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" OR "${CMAKE_CXX_COMPILER}" MATCHES "clang") 45 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CXX_COMMON_FLAGS} -std=c++11") 46 | else() 47 | message(FATAL_ERROR "CMake is unable to recognize compilation toolset") 48 | endif() 49 | endif() 50 | 51 | # Dependencies 52 | find_package(Boost 1.54) 53 | if (NOT Boost_FOUND) 54 | message(FATAL_ERROR "Cannot find Boost") 55 | endif() 56 | include_directories(${Boost_INCLUDE_DIRS}) 57 | 58 | set(SRC_COMMON 59 | spatial_index_benchmark.hpp 60 | high_resolution_timer.hpp) 61 | 62 | macro(msg T) 63 | message(STATUS "Configuring ${T}") 64 | endmacro() 65 | 66 | if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") # do nothing 67 | elseif (CMAKE_HOST_UNIX) 68 | set(EXTRA_LIBS -lrt) 69 | endif() 70 | 71 | ################################################################################ 72 | # benchmark: thst 73 | set(BSI thst) 74 | 75 | set(SPATIALINDEX_INCLUDE_DIR "${CMAKE_SOURCE_DIR}/../") 76 | 77 | # choose spatial tree variant: quad tree, rtree 78 | foreach(split_variant quadtree quadratic quadratic_sphere) 79 | string(TOUPPER "${split_variant}" SPLITVARU) 80 | 81 | # choose iterative insertion, to add blk in future 82 | foreach(load_variant itr custom) 83 | string(TOUPPER "${load_variant}" LOADVARU) 84 | 85 | if(${load_variant} STREQUAL "itr") 86 | set(TARGET_BSI ${BSI}_${split_variant}) 87 | else() 88 | set(TARGET_BSI ${BSI}_${split_variant}_${load_variant}) 89 | endif() 90 | 91 | msg(${TARGET_BSI}) 92 | add_executable(${TARGET_BSI} ${SRC_COMMON} benchmark_thst.cpp) 93 | target_link_libraries(${TARGET_BSI} ${EXTRA_LIBS}) 94 | set_property(TARGET ${TARGET_BSI} APPEND PROPERTY 95 | COMPILE_DEFINITIONS SIBENCH_RTREE_SPLIT_${SPLITVARU}=1) 96 | set_property(TARGET ${TARGET_BSI} APPEND PROPERTY 97 | COMPILE_DEFINITIONS SIBENCH_RTREE_LOAD_${LOADVARU}=1 ) 98 | set_property(TARGET ${TARGET_BSI} APPEND PROPERTY 99 | COMPILE_DEFINITIONS SIBENCH_THST_RTREE_PARAMS_CT) 100 | add_test(NAME ${TARGET_BSI} CONFIGURATIONS Release COMMAND ${TARGET_BSI}) 101 | set_property(TARGET ${TARGET_BSI} APPEND PROPERTY 102 | INCLUDE_DIRECTORIES ${SPATIALINDEX_INCLUDE_DIR}) 103 | endforeach() 104 | endforeach() 105 | 106 | ################################################################################ 107 | # benchmark: Boost.Geometry 108 | set(BGI bgi) 109 | 110 | # choose tree balancing algorithm 111 | foreach(split_variant linear quadratic rstar) 112 | string(TOUPPER "${split_variant}" SPLITVARU) 113 | 114 | # choose iterative insertion or bulk loading (Split-Tile-Recurse) 115 | foreach(load_variant itr blk) 116 | string(TOUPPER "${load_variant}" LOADVARU) 117 | 118 | if (BGI_ENABLE_CT) 119 | set(params_variant ct) 120 | else() 121 | set(params_variant rt) 122 | endif() 123 | 124 | # choose between compile-time and run-time parameters 125 | foreach(params_variant ${params_variant}) 126 | string(TOUPPER "${params_variant}" PARAMSVARU) 127 | 128 | if(${load_variant} STREQUAL "itr") 129 | set(TARGET_BGI_BASE ${BGI}_${split_variant}) 130 | else() 131 | set(TARGET_BGI_BASE ${BGI}_${split_variant}_${load_variant}) 132 | endif() 133 | 134 | if (BGI_ENABLE_CT AND ${params_variant} STREQUAL "ct") 135 | set(TARGET_BGI ${TARGET_BGI_BASE}_${params_variant}) 136 | else() 137 | set(TARGET_BGI ${TARGET_BGI_BASE}) 138 | endif() 139 | 140 | msg(${TARGET_BGI}) 141 | add_executable(${TARGET_BGI} ${SRC_COMMON} benchmark_boost_geometry.cpp) 142 | target_link_libraries(${TARGET_BGI} ${EXTRA_LIBS}) 143 | set_property(TARGET ${TARGET_BGI} APPEND PROPERTY 144 | COMPILE_DEFINITIONS SIBENCH_RTREE_SPLIT_${SPLITVARU}=1) 145 | set_property(TARGET ${TARGET_BGI} APPEND PROPERTY 146 | COMPILE_DEFINITIONS SIBENCH_RTREE_LOAD_${LOADVARU}=1) 147 | set_property(TARGET ${TARGET_BGI} APPEND PROPERTY 148 | COMPILE_DEFINITIONS SIBENCH_BGI_RTREE_PARAMS_${PARAMSVARU}=1) 149 | add_test(NAME ${TARGET_BGI} CONFIGURATIONS Release COMMAND ${TARGET_BGI}) 150 | endforeach() 151 | endforeach() 152 | endforeach() 153 | -------------------------------------------------------------------------------- /benchmark/README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Installation 3 | 4 | ### Requirements 5 | * C++11 compiler 6 | * CMake 7 | * Boost headers 8 | * gnuplot 9 | 10 | ### Instructions 11 | 12 | ```bash 13 | cmake .. -G Xcode -DBGI_ENABLE_CT=ON -DCMAKE_BUILD_TYPE=Release 14 | make -j 8 15 | ./run_benchmark.sh ./build/Release/ 16 | 17 | # the benchmark script will also run the gnuplot command: 18 | gnuplot ./plot_results.plt 19 | ``` 20 | 21 | ## Benchmarks 22 | 23 | Benchmark setup is based on [spatial_index_benchmark](https://github.com/mloskot/spatial_index_benchmark) by Mateusz Loskot and Adam Wulkiewicz. 24 | 25 | ### Results 26 | 27 | HW: Intel(R) Core(TM) i7-4870HQ CPU @ 2.50GHz, 16 GB RAM; OS: macOS Sierra 10.12.16 28 | 29 | * Loading times for each of the R-tree construction methods 30 | 31 | ![load thst_vs_bgi](results/benchmark_load_bgi_vs_thst.png) 32 | 33 | Best loading performance is given by the bulk loading algorithm, followed by linear: 34 | ![load boost::geometry](results/bgi_benchmark_rtree_load_itr_vs_blk.png) 35 | 36 | As expected, the heurestic type doesn't affect bulk loading performance: 37 | ![load balance boost::geometry](results/bgi_benchmark_rtree_load_blk_vs_balancing.png) 38 | 39 | Creating the quadtree is very slow for capacities between 256 and 1024, while the custom quadratic variant produces the best performance: 40 | ![load thst](results/thst_benchmark_load_itr.png) 41 | 42 | * Query times for each of the R-tree construction methods 43 | 44 | ![query thst_vs_bgi](results/benchmark_query_bgi_vs_thst.png) 45 | 46 | Best query times, rstar and bulk loading produce best times and followed by the thst's quadratic custom: 47 | ![query thst_vs_bgi](results/benchmark_query_bgi_vs_thst_best.png) 48 | 49 | Bulk loading produces the best query times from all heurestics: 50 | ![query boost::geometry](results/bgi_benchmark_rtree_query_itr_vs_blk.png) 51 | 52 | As expected, the heurestic type doesn't affect query performance of the bulk loading algorithm: 53 | ![query balance boost::geometry](results/bgi_benchmark_rtree_query_blk_vs_balancing.png) 54 | 55 | Query times for the quadtree are really slow: 56 | ![query thst](results/thst_benchmark_query_itr.png) 57 | 58 | The custom allocator is slightly faster than the normal one: 59 | ![query thst](results/thst_benchmark_query_cst.png) 60 | 61 | * Dynamic use case, average time for each of the R-tree construction methods 62 | 63 | ![dynamic thst_vs_bgi](results/benchmark_dynamic_bgi_vs_thst.png) 64 | 65 | At around 400 objects the thst quadratic method is faster than the bgi linear version: 66 | ![dynamic2 thst_vs_bgi](results/benchmark_dynamic_small_bgi_vs_thst.png) 67 | 68 | Under 400 objects bgi linear is slightly faster, however this heavily depends on how many objects pass the query test, in the given test case almost all objects pass the intersection test. 69 | ![dynamic2 thst_vs_bgi](results/benchmark_dynamic_vsmall_bgi_vs_thst.png) 70 | 71 | ### Legend 72 | ------ 73 | 74 | * ```bgi``` - boost::geometry::index, compile time 75 | * ```thst``` - thst 76 | * ```ct``` - compile-time specification of rtree parameters 77 | * ```rt``` (or non suffix) - Boost.Geometry-only, run-time specification of rtree parameters 78 | * ```L``` - linear 79 | * ```Q``` - quadratic 80 | * ```QT``` - quadtree 81 | * ```R``` - rstar 82 | * ```itr (or no suffix)``` - iterative insertion method of building rtree 83 | * ```blk``` - bulk loading method of building R-tree (custom algorithm for ```bgi```) 84 | * ```custom``` - custom allocator variant for thst(cache friendly, linear memory) 85 | * ```sphere``` - sphere volume for computing the boxes's volume, better splitting but costlier 86 | * insert 1000000 - number of objects small random boxes 87 | * query 100000 - number of instersection-based queries with random boxes 10x larger than those inserted 88 | * dynamic 200 - number of runs composed of clear, instersection-based queries and insert with small random boxes 89 | -------------------------------------------------------------------------------- /benchmark/benchmark_boost_geometry.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Mateusz Loskot 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. 5 | // (See accompanying file LICENSE_1_0.txt or copy 6 | // at http://www.boost.org/LICENSE_1_0.txt) 7 | // 8 | #include "spatial_index_benchmark.hpp" 9 | #ifdef SIBENCH_RTREE_LOAD_BLK 10 | #define BOOST_GEOMETRY_INDEX_DETAIL_EXPERIMENTAL 1 11 | #endif 12 | #include 13 | #include 14 | #include 15 | #include 16 | // enable internal debugging utilities 17 | #include "../config.h" 18 | 19 | #include 20 | #include 21 | 22 | namespace bg = boost::geometry; 23 | namespace bgi = boost::geometry::index; 24 | 25 | typedef bg::model::point point_t; 26 | typedef bg::model::box box_t; 27 | 28 | namespace { 29 | 30 | #ifdef SIBENCH_BGI_RTREE_PARAMS_CT 31 | std::string const lib("bgi_ct"); 32 | #elif SIBENCH_BGI_RTREE_PARAMS_RT 33 | std::string const lib("bgi"); 34 | #else 35 | #error Boost.Geometry rtree parameters variant unknown 36 | #endif 37 | 38 | struct ArrayIndexable { 39 | typedef sibench::id_type V; 40 | typedef box_t result_type; 41 | 42 | ArrayIndexable(const sibench::boxes2d_t &array) : array(&array) {} 43 | 44 | result_type operator()(sibench::id_type id) const { 45 | const auto &bbox = (*array)[id]; 46 | return result_type(point_t(bbox.min[0], bbox.min[1]), 47 | point_t(bbox.max[0], bbox.max[1])); 48 | } 49 | 50 | private: 51 | const sibench::boxes2d_t *array; 52 | }; 53 | 54 | #ifdef SIBENCH_RTREE_SPLIT_LINEAR 55 | template 56 | using parameters_t = bgi::linear; 57 | #elif SIBENCH_RTREE_SPLIT_QUADRATIC 58 | template 59 | using parameters_t = bgi::quadratic; 60 | #else 61 | template 62 | using parameters_t = bgi::rstar; 63 | #endif 64 | template 65 | using rtree_t = 66 | bgi::rtree, 67 | ArrayIndexable>; 68 | 69 | template 70 | void print_statistics(std::ostream &os, std::string const &lib, T const &i) { 71 | using bgi::detail::rtree::utilities::statistics; 72 | auto const s = statistics(i); 73 | os << sibench::get_banner(lib) << " stats: levels=" << boost::get<0>(s) 74 | << ", nodes=" << boost::get<1>(s) << ", leaves=" << boost::get<2>(s) 75 | << ", values=" << boost::get<3>(s) << ", values_min=" << boost::get<4>(s) 76 | << ", values_max=" << boost::get<5>(s) << std::endl; 77 | } 78 | } 79 | 80 | template 81 | void setCapacity(const Parameters ¶meters, sibench::result_info &info) { 82 | info.min_capacity = Parameters::min_elements; 83 | info.max_capacity = Parameters::max_elements; 84 | } 85 | 86 | void setCapacity(const bgi::dynamic_linear ¶meters, 87 | sibench::result_info &info) { 88 | info.min_capacity = parameters.get_min_elements(); 89 | info.max_capacity = parameters.get_max_elements(); 90 | } 91 | 92 | void setCapacity(const bgi::dynamic_quadratic ¶meters, 93 | sibench::result_info &info) { 94 | info.min_capacity = parameters.get_min_elements(); 95 | info.max_capacity = parameters.get_max_elements(); 96 | } 97 | 98 | void setCapacity(const bgi::dynamic_rstar ¶meters, 99 | sibench::result_info &info) { 100 | info.min_capacity = parameters.get_min_elements(); 101 | info.max_capacity = parameters.get_max_elements(); 102 | } 103 | 104 | template 105 | sibench::result_info benchmark_load(const sibench::boxes2d_t &boxes, 106 | TreeClass &rtree) { 107 | 108 | sibench::result_info load_r; 109 | setCapacity(rtree.parameters(), load_r); 110 | 111 | typedef std::vector box_values_t; 112 | auto const iterations = boxes.size(); 113 | box_values_t vboxes(iterations); 114 | std::iota(vboxes.begin(), vboxes.end(), 0); 115 | 116 | #ifdef SIBENCH_RTREE_LOAD_BLK 117 | auto const marks = sibench::benchmark( 118 | "load", iterations, vboxes, 119 | [&rtree](box_values_t const &boxes, std::size_t iterations) { 120 | assert(iterations <= boxes.size()); 121 | rtree = TreeClass(boxes.cbegin(), boxes.cbegin() + iterations, // 122 | rtree.parameters(), rtree.indexable_get()); 123 | }); 124 | #else 125 | auto const marks = sibench::benchmark( 126 | "load", iterations, vboxes, 127 | [&rtree](box_values_t const &boxes, std::size_t iterations) { 128 | assert(iterations <= boxes.size()); 129 | rtree.insert(boxes.cbegin(), boxes.cbegin() + iterations); 130 | }); 131 | #endif 132 | 133 | load_r.accumulate(marks); 134 | 135 | #if SIBENCH_DEBUG_PRINT_INFO == 1 136 | sibench::print_result(std::cout, lib, marks); 137 | print_statistics(std::cout, lib, rtree); 138 | #endif 139 | 140 | return load_r; 141 | } 142 | 143 | template 144 | sibench::result_info benchmark_query(const sibench::boxes2d_t &boxes, 145 | const TreeClass &rtree) { 146 | std::size_t query_found = 0; 147 | 148 | sibench::result_info query_r; 149 | setCapacity(rtree.parameters(), query_r); 150 | 151 | auto const marks = sibench::benchmark( 152 | "query", sibench::max_queries, boxes, 153 | [&rtree, &query_found](sibench::boxes2d_t const &boxes, 154 | std::size_t iterations) { 155 | std::vector results; 156 | results.reserve(iterations); 157 | 158 | for (std::size_t i = 0; i < iterations; ++i) { 159 | results.clear(); 160 | 161 | auto const &box = boxes[i]; 162 | point_t p1(box.min[0] - sibench::query_size, 163 | box.min[1] - sibench::query_size); 164 | point_t p2(box.max[0] + sibench::query_size, 165 | box.max[1] + sibench::query_size); 166 | box_t region(p1, p2); 167 | 168 | rtree.query(bgi::intersects(region), std::back_inserter(results)); 169 | query_found += results.size(); 170 | } 171 | }); 172 | 173 | query_r.accumulate(marks); 174 | 175 | #if SIBENCH_DEBUG_PRINT_INFO == 1 176 | sibench::print_result(std::cout, lib, marks); 177 | sibench::print_query_count(std::cout, lib, query_found); 178 | #endif 179 | return query_r; 180 | } 181 | 182 | sibench::result_info benchmark_random(const sibench::boxes2d_t &boxes, 183 | size_t iterations) { 184 | 185 | ArrayIndexable indexable(boxes); 186 | rtree_t<16, 8> tree(parameters_t<16, 8>(), indexable); 187 | 188 | size_t insert_count = 0, total_count = 0; 189 | 190 | sibench::result_info res; 191 | setCapacity(tree.parameters(), res); 192 | res.iterations = iterations; 193 | 194 | size_t start = 0; 195 | for (int i = 0; i < sibench::random_runs; ++i) { 196 | sibench::result_info const marks = sibench::benchmark( 197 | "random", iterations, boxes, 198 | [&tree, &start, &insert_count, &total_count]( 199 | sibench::boxes2d_t const &boxes, std::size_t iterations) { 200 | 201 | tree.clear(); 202 | 203 | start = (start + iterations) % boxes.size(); 204 | 205 | auto end = start + iterations; 206 | 207 | if (end > boxes.size()) { 208 | start = boxes.size() - iterations; 209 | end = boxes.size(); 210 | } 211 | 212 | assert(end <= boxes.size()); 213 | for (size_t i = start; i < end; ++i) { 214 | assert(iterations <= boxes.size()); 215 | auto const &box = boxes[i]; 216 | point_t p1(box.min[0], box.min[1]); 217 | point_t p2(box.max[0], box.max[1]); 218 | box_t region(p1, p2); 219 | 220 | if (tree.query(bgi::intersects(region), 221 | spatial::detail::dummy_iterator()) == 0) { 222 | tree.insert(i); 223 | ++insert_count; 224 | } 225 | ++total_count; 226 | } 227 | }); 228 | res.accumulate(marks); 229 | } 230 | 231 | #if SIBENCH_DEBUG_PRINT_INFO == 1 232 | sibench::print_insert_count(std::cout, lib, insert_count, total_count); 233 | #endif 234 | 235 | return res; 236 | } 237 | 238 | void benchmark_run_rt(const sibench::boxes2d_t &boxes) { 239 | for (std::size_t next_capacity = 4; next_capacity <= sibench::max_capacities; 240 | next_capacity += sibench::capacity_step) { 241 | double const fill_factor = 0.5; 242 | std::size_t const min_capacity = next_capacity; 243 | std::size_t const max_capacity = 244 | std::size_t(std::floor(min_capacity / fill_factor)); 245 | { 246 | ArrayIndexable indexable(boxes); 247 | 248 | #ifdef SIBENCH_RTREE_SPLIT_LINEAR 249 | typedef bgi::dynamic_linear rtree_parameters_t; 250 | #elif SIBENCH_RTREE_SPLIT_QUADRATIC 251 | typedef bgi::dynamic_quadratic rtree_parameters_t; 252 | #else 253 | typedef bgi::dynamic_rstar rtree_parameters_t; 254 | #endif 255 | typedef bgi::rtree 256 | rtree_t; 257 | 258 | rtree_parameters_t rtree_parameters(max_capacity, min_capacity); 259 | rtree_t rtree(rtree_parameters, indexable); 260 | 261 | sibench::result_info load_r = benchmark_load(boxes, rtree); 262 | sibench::result_info query_r = benchmark_query(boxes, rtree); 263 | 264 | // single line per run 265 | sibench::print_result(std::cout, lib, load_r, query_r); 266 | 267 | } // for capacity 268 | } 269 | } 270 | 271 | template 272 | void benchmark_run_ct(const sibench::boxes2d_t &boxes) { 273 | 274 | ArrayIndexable indexable(boxes); 275 | rtree_t rtree( 276 | parameters_t(), indexable); 277 | 278 | sibench::result_info load_r = benchmark_load(boxes, rtree); 279 | sibench::result_info query_r = benchmark_query(boxes, rtree); 280 | 281 | // single line per run 282 | sibench::print_result(std::cout, lib, load_r, query_r); 283 | } 284 | 285 | int main() { 286 | try { 287 | 288 | sibench::print_result_header(std::cout, lib); 289 | 290 | // Generate random objects for indexing 291 | auto const boxes = sibench::generate_boxes(sibench::max_insertions); 292 | 293 | #ifdef SIBENCH_BGI_RTREE_PARAMS_RT 294 | benchmark_run_rt(boxes); 295 | #elif SIBENCH_BGI_RTREE_PARAMS_CT 296 | std::size_t const max_capacity = sibench::constant_max_capacity; 297 | std::size_t const min_capacity = sibench::constant_min_capacity; 298 | benchmark_run_ct(boxes); 299 | benchmark_run_ct(boxes); 300 | benchmark_run_ct(boxes); 301 | benchmark_run_ct(boxes); 302 | benchmark_run_ct(boxes); 303 | benchmark_run_ct(boxes); 304 | benchmark_run_ct(boxes); 305 | benchmark_run_ct(boxes); 306 | benchmark_run_ct(boxes); 307 | benchmark_run_ct(boxes); 308 | benchmark_run_ct(boxes); 309 | benchmark_run_ct(boxes); 310 | benchmark_run_ct(boxes); 311 | benchmark_run_ct(boxes); 312 | benchmark_run_ct(boxes); 313 | benchmark_run_ct(boxes); 314 | benchmark_run_ct(boxes); 315 | #else 316 | #error "Unknown params!" 317 | #endif 318 | 319 | #ifndef SIBENCH_RTREE_LOAD_BLK 320 | // bulk load cannot be used since only dynamic insertions 321 | sibench::print_insert_result_header(std::cout, lib); 322 | for (int insertions = 10; insertions < sibench::max_random_insertions; 323 | insertions += insertions * sibench::insert_step) { 324 | 325 | sibench::result_info random_r = benchmark_random(boxes, insertions); 326 | sibench::print_insert_result(std::cout, lib, random_r); 327 | } 328 | #endif 329 | 330 | return EXIT_SUCCESS; 331 | } catch (std::exception const &e) { 332 | std::cerr << e.what() << std::endl; 333 | } catch (...) { 334 | std::cerr << "unknown error" << std::endl; 335 | } 336 | return EXIT_FAILURE; 337 | } 338 | -------------------------------------------------------------------------------- /benchmark/benchmark_thst.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "../test/custom_allocator.h" 3 | #include "spatial_index_benchmark.hpp" 4 | 5 | #include 6 | #include 7 | 8 | namespace { 9 | 10 | std::string const lib("thst"); 11 | 12 | struct ArrayIndexable { 13 | 14 | ArrayIndexable(const sibench::boxes2d_t &array) : array(array) {} 15 | 16 | const sibench::coord_t *min(const uint32_t index) const { 17 | return array[index].min; 18 | } 19 | const sibench::coord_t *max(const uint32_t index) const { 20 | return array[index].max; 21 | } 22 | 23 | private: 24 | const sibench::boxes2d_t &array; 25 | }; 26 | 27 | #ifdef SIBENCH_RTREE_SPLIT_QUADTREE 28 | const int quadtree_factor = 64; 29 | 30 | template 31 | using qtree_t = spatial::QuadTree; 33 | 34 | #else 35 | 36 | #ifdef SIBENCH_RTREE_SPLIT_QUADRATIC_SPHERE 37 | const int kVolumeMode = spatial::box::eSphericalVolume; 38 | #else 39 | const int kVolumeMode = spatial::box::eNormalVolume; 40 | #endif 41 | 42 | using tree_bbox_type = spatial::BoundingBox; 43 | template 44 | using tree_node_type = 45 | spatial::detail::Node; 46 | 47 | #ifdef SIBENCH_RTREE_LOAD_CUSTOM 48 | template 49 | using tree_allocator_type = 50 | test::tree_allocator, false>; 51 | #else 52 | template 53 | using tree_allocator_type = test::heap_allocator>; 54 | #endif //#ifdef SIBENCH_RTREE_LOAD_CUSTOM 55 | 56 | template 57 | using rtree_t = 58 | spatial::RTree>; 61 | #endif // #ifdef SIBENCH_RTREE_SPLIT_QUADTREE 62 | 63 | template 64 | void print_statistics(std::ostream &os, std::string const &lib, T const &i) { 65 | os << sibench::get_banner(lib) << " stats: levels=" << i.levels() 66 | << " values=" << i.count() 67 | #ifndef SIBENCH_RTREE_SPLIT_QUADTREE 68 | << " nodes=" << i.allocator().count 69 | #ifdef SIBENCH_RTREE_LOAD_CUSTOM 70 | << " estimated nodes=" << i.allocator().buffer.size() 71 | #endif 72 | #endif 73 | << std::endl; 74 | } 75 | } // unnamed namespace 76 | 77 | template 78 | void setCapacity(const TreeClass &tree, sibench::result_info &res) { 79 | #ifdef SIBENCH_RTREE_SPLIT_QUADTREE 80 | res.max_capacity = res.min_capacity = TreeClass::max_items / quadtree_factor; 81 | #else 82 | res.min_capacity = TreeClass::min_items; 83 | res.max_capacity = TreeClass::max_items; 84 | #endif 85 | } 86 | 87 | template 88 | sibench::result_info benchmark_load(const sibench::boxes2d_t &boxes, 89 | TreeClass &tree) { 90 | 91 | sibench::result_info res; 92 | setCapacity(tree, res); 93 | 94 | typedef std::vector box_values_t; 95 | auto const iterations = boxes.size(); 96 | box_values_t vboxes(iterations); 97 | std::iota(vboxes.begin(), vboxes.end(), 0); 98 | 99 | #if defined(SIBENCH_RTREE_LOAD_ITR) || defined(SIBENCH_RTREE_LOAD_CUSTOM) 100 | auto const marks = sibench::benchmark( 101 | "insert", iterations, vboxes, 102 | [&tree](box_values_t const &boxes, std::size_t iterations) { 103 | assert(iterations <= boxes.size()); 104 | tree.insert(boxes.cbegin(), boxes.cbegin() + iterations); 105 | }); 106 | 107 | res.accumulate(marks); 108 | #else 109 | #error Unknown tree loading method 110 | #endif 111 | 112 | #if SIBENCH_DEBUG_PRINT_INFO == 1 113 | sibench::print_result(std::cout, lib, marks); 114 | print_statistics(std::cout, lib, tree); 115 | #endif 116 | 117 | return res; 118 | } 119 | 120 | template 121 | sibench::result_info benchmark_query(const sibench::boxes2d_t &boxes, 122 | const TreeClass &tree) { 123 | size_t query_found = 0; 124 | 125 | sibench::result_info res; 126 | setCapacity(tree, res); 127 | 128 | sibench::result_info const marks = sibench::benchmark( 129 | "query", sibench::max_queries, boxes, 130 | [&tree, &query_found](sibench::boxes2d_t const &boxes, 131 | std::size_t iterations) { 132 | std::vector results; 133 | results.reserve(iterations); 134 | 135 | for (size_t i = 0; i < iterations; ++i) { 136 | results.clear(); 137 | auto const &box = boxes[i]; 138 | sibench::coord_t min[2] = {box.min[0] - sibench::query_size, 139 | box.min[1] - sibench::query_size}; 140 | sibench::coord_t max[2] = {box.max[0] + sibench::query_size, 141 | box.max[1] + sibench::query_size}; 142 | 143 | tree.query(spatial::intersects<2>(min, max), 144 | std::back_inserter(results)); 145 | 146 | query_found += results.size(); 147 | } 148 | }); 149 | 150 | res.accumulate(marks); 151 | 152 | #if SIBENCH_DEBUG_PRINT_INFO == 1 153 | sibench::print_result(std::cout, lib, marks); 154 | sibench::print_query_count(std::cout, lib, query_found); 155 | #endif 156 | 157 | return res; 158 | } 159 | 160 | #ifndef SIBENCH_RTREE_SPLIT_QUADTREE 161 | sibench::result_info benchmark_random(const sibench::boxes2d_t &boxes, 162 | size_t iterations) { 163 | 164 | typedef rtree_t<8, 16> tree_t; 165 | ArrayIndexable indexable(boxes); 166 | 167 | #if SIBENCH_RTREE_LOAD_CUSTOM 168 | tree_t tree(indexable, tree_t::allocator_type(), false); 169 | size_t nodeCount = tree_t::nodeCount(iterations); 170 | tree.allocator().resize(std::max(nodeCount * 3, (size_t)10)); 171 | #else 172 | tree_t tree(indexable); 173 | #endif 174 | 175 | size_t insert_count = 0, total_count = 0; 176 | 177 | sibench::result_info res; 178 | setCapacity(tree, res); 179 | res.iterations = iterations; 180 | 181 | size_t start = 0; 182 | for (int i = 0; i < sibench::random_runs; ++i) { 183 | sibench::result_info const marks = sibench::benchmark( 184 | "random", iterations, boxes, 185 | [&tree, &start, &insert_count, &total_count]( 186 | sibench::boxes2d_t const &boxes, std::size_t iterations) { 187 | 188 | #ifdef SIBENCH_RTREE_LOAD_CUSTOM 189 | tree.allocator().index = tree.allocator().count = 0; 190 | // cleanup was handled by the allocator 191 | tree.clear(false); 192 | #else 193 | tree.clear(); 194 | #endif 195 | 196 | start = (start + iterations) % boxes.size(); 197 | 198 | auto end = start + iterations; 199 | 200 | if (end > boxes.size()) { 201 | start = boxes.size() - iterations; 202 | end = boxes.size(); 203 | } 204 | 205 | assert(end <= boxes.size()); 206 | for (size_t i = start; i < end; ++i) { 207 | assert(iterations <= boxes.size()); 208 | auto const ¤tBox = boxes[i]; 209 | 210 | bool added = tree.insert( 211 | i, [¤tBox](const decltype(tree)::bbox_type &bbox) { 212 | const decltype(tree)::bbox_type cbbox(currentBox.min, 213 | currentBox.max); 214 | return !bbox.overlaps(cbbox); 215 | }); 216 | 217 | insert_count += added; 218 | ++total_count; 219 | } 220 | }); 221 | res.accumulate(marks); 222 | } 223 | 224 | #if SIBENCH_DEBUG_PRINT_INFO == 1 225 | sibench::print_insert_count(std::cout, lib, insert_count, total_count); 226 | #endif 227 | 228 | return res; 229 | } 230 | #endif 231 | 232 | template 233 | void benchmark_run(const sibench::boxes2d_t &boxes, 234 | const spatial::BoundingBox &world_box) { 235 | 236 | ArrayIndexable indexable(boxes); 237 | #if SIBENCH_RTREE_SPLIT_QUADTREE 238 | qtree_t tree(world_box.min, world_box.max, 239 | indexable); 240 | 241 | #elif SIBENCH_RTREE_SPLIT_QUADRATIC || SIBENCH_RTREE_SPLIT_QUADRATIC_SPHERE 242 | 243 | #if SIBENCH_RTREE_LOAD_CUSTOM 244 | typedef rtree_t tree_t; 245 | tree_t tree(indexable, typename tree_t::allocator_type(), false); 246 | size_t nodeCount = 247 | rtree_t::nodeCount(sibench::max_insertions); 248 | tree.allocator().resize(nodeCount * 1.5); 249 | tree.clear(); 250 | #else 251 | rtree_t tree(indexable); 252 | #endif 253 | 254 | #else 255 | #error Unknown tree split method 256 | #endif //#ifdef SIBENCH_RTREE_SPLIT_QUADTREE 257 | sibench::result_info load_r = benchmark_load(boxes, tree); 258 | sibench::result_info query_r = benchmark_query(boxes, tree); 259 | 260 | // single line per run 261 | sibench::print_result(std::cout, lib, load_r, query_r); 262 | } 263 | 264 | int main() { 265 | try { 266 | 267 | sibench::print_result_header(std::cout, lib); 268 | 269 | #ifdef SIBENCH_THST_RTREE_PARAMS_CT 270 | // Generate random objects for indexing 271 | auto const boxes = sibench::generate_boxes(sibench::max_insertions); 272 | spatial::BoundingBox world_box; 273 | world_box.init(); 274 | for (const auto &box : boxes) { 275 | world_box.extend(box.min); 276 | world_box.extend(box.max); 277 | } 278 | 279 | std::size_t const max_capacity = sibench::constant_max_capacity; 280 | std::size_t const min_capacity = sibench::constant_min_capacity; 281 | benchmark_run(boxes, world_box); 282 | benchmark_run(boxes, world_box); 283 | benchmark_run(boxes, world_box); 284 | benchmark_run(boxes, world_box); 285 | benchmark_run(boxes, world_box); 286 | benchmark_run(boxes, world_box); 287 | benchmark_run(boxes, world_box); 288 | benchmark_run(boxes, world_box); 289 | benchmark_run(boxes, world_box); 290 | benchmark_run(boxes, world_box); 291 | benchmark_run(boxes, world_box); 292 | benchmark_run(boxes, world_box); 293 | benchmark_run(boxes, world_box); 294 | benchmark_run(boxes, world_box); 295 | benchmark_run(boxes, world_box); 296 | benchmark_run(boxes, world_box); 297 | benchmark_run(boxes, world_box); 298 | #else 299 | #error Only compile time support for rtree/quadtree 300 | #endif //#ifdef SIBENCH_THST_RTREE_PARAMS_CT 301 | 302 | #ifndef SIBENCH_RTREE_SPLIT_QUADTREE 303 | sibench::print_insert_result_header(std::cout, lib); 304 | for (int insertions = 10; insertions < sibench::max_random_insertions; 305 | insertions += insertions * sibench::insert_step) { 306 | 307 | sibench::result_info random_r = benchmark_random(boxes, insertions); 308 | sibench::print_insert_result(std::cout, lib, random_r); 309 | } 310 | #endif 311 | 312 | return EXIT_SUCCESS; 313 | } catch (std::exception const &e) { 314 | std::cerr << e.what() << std::endl; 315 | } catch (...) { 316 | std::cerr << "unknown error" << std::endl; 317 | } 318 | return EXIT_FAILURE; 319 | } 320 | -------------------------------------------------------------------------------- /benchmark/high_resolution_timer.hpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2005-2010 Hartmut Kaiser 2 | // Copyright (c) 2009 Edward Grace 3 | // 4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying 5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 6 | 7 | #if !defined(HIGH_RESOLUTION_TIMER_MAR_24_2008_1222PM) 8 | #define HIGH_RESOLUTION_TIMER_MAR_24_2008_1222PM 9 | 10 | #include 11 | #include 12 | 13 | #if defined(BOOST_HAS_UNISTD_H) 14 | #include 15 | #endif 16 | #include 17 | #include 18 | #include 19 | 20 | #if defined(BOOST_WINDOWS) 21 | 22 | #include 23 | 24 | namespace util 25 | { 26 | /////////////////////////////////////////////////////////////////////////////// 27 | // 28 | // high_resolution_timer 29 | // A timer object measures elapsed time. 30 | // CAUTION: Windows only! 31 | // 32 | /////////////////////////////////////////////////////////////////////////////// 33 | class high_resolution_timer 34 | { 35 | public: 36 | high_resolution_timer() 37 | { 38 | restart(); 39 | } 40 | 41 | high_resolution_timer(double t) 42 | { 43 | LARGE_INTEGER frequency; 44 | if (!QueryPerformanceFrequency(&frequency)) 45 | boost::throw_exception(std::runtime_error("Couldn't acquire frequency")); 46 | 47 | start_time.QuadPart = (LONGLONG)(t * frequency.QuadPart); 48 | } 49 | 50 | high_resolution_timer(high_resolution_timer const& rhs) 51 | : start_time(rhs.start_time) 52 | { 53 | } 54 | 55 | static double now() 56 | { 57 | SYSTEMTIME st; 58 | GetSystemTime(&st); 59 | 60 | FILETIME ft; 61 | SystemTimeToFileTime(&st, &ft); 62 | 63 | LARGE_INTEGER now; 64 | now.LowPart = ft.dwLowDateTime; 65 | now.HighPart = ft.dwHighDateTime; 66 | 67 | // FileTime is in 100ns increments, result needs to be in [s] 68 | return now.QuadPart * 1e-7; 69 | } 70 | 71 | void restart() 72 | { 73 | if (!QueryPerformanceCounter(&start_time)) 74 | boost::throw_exception(std::runtime_error("Couldn't initialize start_time")); 75 | } 76 | double elapsed() const // return elapsed time in seconds 77 | { 78 | LARGE_INTEGER now; 79 | if (!QueryPerformanceCounter(&now)) 80 | boost::throw_exception(std::runtime_error("Couldn't get current time")); 81 | 82 | LARGE_INTEGER frequency; 83 | if (!QueryPerformanceFrequency(&frequency)) 84 | boost::throw_exception(std::runtime_error("Couldn't acquire frequency")); 85 | 86 | return double(now.QuadPart - start_time.QuadPart) / frequency.QuadPart; 87 | } 88 | 89 | double elapsed_max() const // return estimated maximum value for elapsed() 90 | { 91 | LARGE_INTEGER frequency; 92 | if (!QueryPerformanceFrequency(&frequency)) 93 | boost::throw_exception(std::runtime_error("Couldn't acquire frequency")); 94 | 95 | return double((std::numeric_limits::max)() - start_time.QuadPart) / 96 | double(frequency.QuadPart); 97 | } 98 | 99 | double elapsed_min() const // return minimum value for elapsed() 100 | { 101 | LARGE_INTEGER frequency; 102 | if (!QueryPerformanceFrequency(&frequency)) 103 | boost::throw_exception(std::runtime_error("Couldn't acquire frequency")); 104 | 105 | return 1.0 / frequency.QuadPart; 106 | } 107 | 108 | private: 109 | LARGE_INTEGER start_time; 110 | }; 111 | 112 | } // namespace util 113 | 114 | #elif defined(_POSIX_TIMERS) && _POSIX_TIMERS > 0 && defined(_POSIX_THREAD_CPUTIME) 115 | 116 | #if _POSIX_THREAD_CPUTIME > 0 // timer always supported 117 | 118 | namespace util 119 | { 120 | 121 | /////////////////////////////////////////////////////////////////////////////// 122 | // 123 | // high_resolution_timer 124 | // A timer object measures elapsed time. 125 | // 126 | /////////////////////////////////////////////////////////////////////////////// 127 | class high_resolution_timer 128 | { 129 | public: 130 | high_resolution_timer() 131 | { 132 | start_time.tv_sec = 0; 133 | start_time.tv_nsec = 0; 134 | 135 | restart(); 136 | } 137 | 138 | high_resolution_timer(double t) 139 | { 140 | start_time.tv_sec = time_t(t); 141 | start_time.tv_nsec = (t - start_time.tv_sec) * 1e9; 142 | } 143 | 144 | high_resolution_timer(high_resolution_timer const& rhs) 145 | : start_time(rhs.start_time) 146 | { 147 | } 148 | 149 | static double now() 150 | { 151 | timespec now; 152 | if (-1 == clock_gettime(CLOCK_REALTIME, &now)) 153 | boost::throw_exception(std::runtime_error("Couldn't get current time")); 154 | return double(now.tv_sec) + double(now.tv_nsec) * 1e-9; 155 | } 156 | 157 | void restart() 158 | { 159 | if (-1 == clock_gettime(CLOCK_REALTIME, &start_time)) 160 | boost::throw_exception(std::runtime_error("Couldn't initialize start_time")); 161 | } 162 | double elapsed() const // return elapsed time in seconds 163 | { 164 | timespec now; 165 | if (-1 == clock_gettime(CLOCK_REALTIME, &now)) 166 | boost::throw_exception(std::runtime_error("Couldn't get current time")); 167 | 168 | if (now.tv_sec == start_time.tv_sec) 169 | return double(now.tv_nsec - start_time.tv_nsec) * 1e-9; 170 | 171 | return double(now.tv_sec - start_time.tv_sec) + 172 | (double(now.tv_nsec - start_time.tv_nsec) * 1e-9); 173 | } 174 | 175 | double elapsed_max() const // return estimated maximum value for elapsed() 176 | { 177 | return double((std::numeric_limits::max)() - start_time.tv_sec); 178 | } 179 | 180 | double elapsed_min() const // return minimum value for elapsed() 181 | { 182 | timespec resolution; 183 | if (-1 == clock_getres(CLOCK_REALTIME, &resolution)) 184 | boost::throw_exception(std::runtime_error("Couldn't get resolution")); 185 | return double(resolution.tv_sec + resolution.tv_nsec * 1e-9); 186 | } 187 | 188 | private: 189 | timespec start_time; 190 | }; 191 | 192 | } // namespace util 193 | 194 | #else // _POSIX_THREAD_CPUTIME > 0 195 | 196 | #include 197 | 198 | // availability of high performance timers must be checked at runtime 199 | namespace util 200 | { 201 | /////////////////////////////////////////////////////////////////////////////// 202 | // 203 | // high_resolution_timer 204 | // A timer object measures elapsed time. 205 | // 206 | /////////////////////////////////////////////////////////////////////////////// 207 | class high_resolution_timer 208 | { 209 | public: 210 | high_resolution_timer() 211 | : use_backup(sysconf(_SC_THREAD_CPUTIME) <= 0) 212 | { 213 | if (!use_backup) { 214 | start_time.tv_sec = 0; 215 | start_time.tv_nsec = 0; 216 | } 217 | restart(); 218 | } 219 | 220 | high_resolution_timer(double t) 221 | : use_backup(sysconf(_SC_THREAD_CPUTIME) <= 0) 222 | { 223 | if (!use_backup) { 224 | start_time.tv_sec = time_t(t); 225 | start_time.tv_nsec = (t - start_time.tv_sec) * 1e9; 226 | } 227 | } 228 | 229 | high_resolution_timer(high_resolution_timer const& rhs) 230 | : use_backup(sysconf(_SC_THREAD_CPUTIME) <= 0), 231 | start_time(rhs.start_time) 232 | { 233 | } 234 | 235 | static double now() 236 | { 237 | if (sysconf(_SC_THREAD_CPUTIME) <= 0) 238 | return double(std::clock()); 239 | 240 | timespec now; 241 | if (-1 == clock_gettime(CLOCK_REALTIME, &now)) 242 | boost::throw_exception(std::runtime_error("Couldn't get current time")); 243 | return double(now.tv_sec) + double(now.tv_nsec) * 1e-9; 244 | } 245 | 246 | void restart() 247 | { 248 | if (use_backup) 249 | start_time_backup.restart(); 250 | else if (-1 == clock_gettime(CLOCK_REALTIME, &start_time)) 251 | boost::throw_exception(std::runtime_error("Couldn't initialize start_time")); 252 | } 253 | double elapsed() const // return elapsed time in seconds 254 | { 255 | if (use_backup) 256 | return start_time_backup.elapsed(); 257 | 258 | timespec now; 259 | if (-1 == clock_gettime(CLOCK_REALTIME, &now)) 260 | boost::throw_exception(std::runtime_error("Couldn't get current time")); 261 | 262 | if (now.tv_sec == start_time.tv_sec) 263 | return double(now.tv_nsec - start_time.tv_nsec) * 1e-9; 264 | 265 | return double(now.tv_sec - start_time.tv_sec) + 266 | (double(now.tv_nsec - start_time.tv_nsec) * 1e-9); 267 | } 268 | 269 | double elapsed_max() const // return estimated maximum value for elapsed() 270 | { 271 | if (use_backup) 272 | start_time_backup.elapsed_max(); 273 | 274 | return double((std::numeric_limits::max)() - start_time.tv_sec); 275 | } 276 | 277 | double elapsed_min() const // return minimum value for elapsed() 278 | { 279 | if (use_backup) 280 | start_time_backup.elapsed_min(); 281 | 282 | timespec resolution; 283 | if (-1 == clock_getres(CLOCK_REALTIME, &resolution)) 284 | boost::throw_exception(std::runtime_error("Couldn't get resolution")); 285 | return double(resolution.tv_sec + resolution.tv_nsec * 1e-9); 286 | } 287 | 288 | private: 289 | bool use_backup; 290 | timespec start_time; 291 | boost::timer start_time_backup; 292 | }; 293 | 294 | } // namespace util 295 | 296 | #endif // _POSIX_THREAD_CPUTIME > 0 297 | 298 | #else // !defined(BOOST_WINDOWS) && (!defined(_POSIX_TIMERS) 299 | // || _POSIX_TIMERS <= 0 300 | // || !defined(_POSIX_THREAD_CPUTIME) 301 | // || _POSIX_THREAD_CPUTIME <= 0) 302 | 303 | #if defined(BOOST_HAS_GETTIMEOFDAY) 304 | 305 | // For platforms that do not support _POSIX_TIMERS but do have 306 | // GETTIMEOFDAY, which is still preferable to std::clock() 307 | #include 308 | 309 | namespace util 310 | { 311 | 312 | /////////////////////////////////////////////////////////////////////////// 313 | // 314 | // high_resolution_timer 315 | // A timer object measures elapsed time. 316 | // 317 | // Implemented with gettimeofday() for platforms that support it, 318 | // such as Darwin (OS X) but do not support the previous options. 319 | // 320 | // Copyright (c) 2009 Edward Grace 321 | // 322 | /////////////////////////////////////////////////////////////////////////// 323 | class high_resolution_timer 324 | { 325 | private: 326 | template 327 | static inline double unsigned_diff(const U &a, const U &b) 328 | { 329 | if (a > b) 330 | return static_cast(a-b); 331 | return -static_cast(b-a); 332 | } 333 | 334 | // @brief Return the difference between two timeval types. 335 | // 336 | // @param t1 The most recent timeval. 337 | // @param t0 The historical timeval. 338 | // 339 | // @return The difference between the two in seconds. 340 | double elapsed(const timeval &t1, const timeval &t0) const 341 | { 342 | if (t1.tv_sec == t0.tv_sec) 343 | return unsigned_diff(t1.tv_usec,t0.tv_usec) * 1e-6; 344 | 345 | // We do it this way as the result of the difference of the 346 | // microseconds can be negative if the clock is implemented so 347 | // that the seconds timer increases in large steps. 348 | // 349 | // Naive subtraction of the unsigned types and conversion to 350 | // double can wreak havoc! 351 | return unsigned_diff(t1.tv_sec,t0.tv_sec) + 352 | unsigned_diff(t1.tv_usec,t0.tv_usec) * 1e-6; 353 | } 354 | 355 | public: 356 | high_resolution_timer() 357 | { 358 | start_time.tv_sec = 0; 359 | start_time.tv_usec = 0; 360 | 361 | restart(); 362 | } 363 | 364 | high_resolution_timer(double t) 365 | { 366 | start_time.tv_sec = time_t(t); 367 | start_time.tv_usec = (t - start_time.tv_sec) * 1e6; 368 | } 369 | 370 | high_resolution_timer(high_resolution_timer const& rhs) 371 | : start_time(rhs.start_time) 372 | { 373 | } 374 | 375 | static double now() 376 | { 377 | // Under some implementations gettimeofday() will always 378 | // return zero. If it returns anything else however then 379 | // we accept this as evidence of an error. Note we are 380 | // not assuming that -1 explicitly indicates the error 381 | // condition, just that non zero is indicative of the 382 | // error. 383 | timeval now; 384 | if (gettimeofday(&now, NULL)) 385 | boost::throw_exception(std::runtime_error("Couldn't get current time")); 386 | return double(now.tv_sec) + double(now.tv_usec) * 1e-6; 387 | } 388 | 389 | void restart() 390 | { 391 | if (gettimeofday(&start_time, NULL)) 392 | boost::throw_exception(std::runtime_error("Couldn't initialize start_time")); 393 | } 394 | 395 | double elapsed() const // return elapsed time in seconds 396 | { 397 | timeval now; 398 | if (gettimeofday(&now, NULL)) 399 | boost::throw_exception(std::runtime_error("Couldn't get current time")); 400 | return elapsed(now,start_time); 401 | } 402 | 403 | double elapsed_max() const // return estimated maximum value for elapsed() 404 | { 405 | return double((std::numeric_limits::max)() - start_time.tv_sec); 406 | } 407 | 408 | double elapsed_min() const // return minimum value for elapsed() 409 | { 410 | // On systems without an explicit clock_getres or similar 411 | // we can only estimate an upper bound on the resolution 412 | // by repeatedly calling the gettimeofday function. This 413 | // is often likely to be indicative of the true 414 | // resolution. 415 | timeval t0, t1; 416 | double delta(0); 417 | 418 | if (gettimeofday(&t0, NULL)) 419 | boost::throw_exception(std::runtime_error("Couldn't get resolution.")); 420 | 421 | // Spin around in a tight loop until we observe a change 422 | // in the reported timer value. 423 | do { 424 | if (gettimeofday(&t1, NULL)) 425 | boost::throw_exception(std::runtime_error("Couldn't get resolution.")); 426 | delta = elapsed(t1, t0); 427 | } while (delta <= 0.0); 428 | 429 | return delta; 430 | } 431 | 432 | private: 433 | timeval start_time; 434 | }; 435 | 436 | } 437 | 438 | #else // BOOST_HAS_GETTIMEOFDAY 439 | 440 | // For platforms other than Windows or Linux, or not implementing gettimeofday 441 | // simply fall back to boost::timer 442 | #include 443 | 444 | namespace util 445 | { 446 | struct high_resolution_timer 447 | : boost::timer 448 | { 449 | static double now() 450 | { 451 | return double(std::clock()); 452 | } 453 | }; 454 | } 455 | 456 | #endif 457 | 458 | #endif 459 | 460 | #endif // HIGH_RESOLUTION_TIMER_AUG_14_2009_0425PM 461 | 462 | // 463 | // $Log: high_resolution_timer.hpp,v $ 464 | // Revision 1.4 2009/08/14 15:28:10 graceej 465 | // * It is entirely possible for the updating clock to increment the 466 | // * seconds and *decrement* the microseconds field. Consequently 467 | // * when subtracting these unsigned microseconds fields a wrap-around 468 | // * error can occur. For this reason elapsed(t1, t0) is used in a 469 | // * similar maner to cycle.h this preserves the sign of the 470 | // * difference. 471 | // 472 | 473 | 474 | -------------------------------------------------------------------------------- /benchmark/results/benchmark_dynamic_bgi_vs_thst.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuxalin/THST/4ff6aa575f87dce4321754981314a923b0b4bbb6/benchmark/results/benchmark_dynamic_bgi_vs_thst.png -------------------------------------------------------------------------------- /benchmark/results/benchmark_dynamic_small_bgi_vs_thst.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuxalin/THST/4ff6aa575f87dce4321754981314a923b0b4bbb6/benchmark/results/benchmark_dynamic_small_bgi_vs_thst.png -------------------------------------------------------------------------------- /benchmark/results/benchmark_dynamic_vsmall_bgi_vs_thst.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuxalin/THST/4ff6aa575f87dce4321754981314a923b0b4bbb6/benchmark/results/benchmark_dynamic_vsmall_bgi_vs_thst.png -------------------------------------------------------------------------------- /benchmark/results/benchmark_load_bgi_vs_thst.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuxalin/THST/4ff6aa575f87dce4321754981314a923b0b4bbb6/benchmark/results/benchmark_load_bgi_vs_thst.png -------------------------------------------------------------------------------- /benchmark/results/benchmark_query_bgi_vs_thst.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuxalin/THST/4ff6aa575f87dce4321754981314a923b0b4bbb6/benchmark/results/benchmark_query_bgi_vs_thst.png -------------------------------------------------------------------------------- /benchmark/results/benchmark_query_bgi_vs_thst_best.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuxalin/THST/4ff6aa575f87dce4321754981314a923b0b4bbb6/benchmark/results/benchmark_query_bgi_vs_thst_best.png -------------------------------------------------------------------------------- /benchmark/results/bgi_benchmark_rtree_load_blk_vs_balancing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuxalin/THST/4ff6aa575f87dce4321754981314a923b0b4bbb6/benchmark/results/bgi_benchmark_rtree_load_blk_vs_balancing.png -------------------------------------------------------------------------------- /benchmark/results/bgi_benchmark_rtree_load_itr_vs_blk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuxalin/THST/4ff6aa575f87dce4321754981314a923b0b4bbb6/benchmark/results/bgi_benchmark_rtree_load_itr_vs_blk.png -------------------------------------------------------------------------------- /benchmark/results/bgi_benchmark_rtree_query_blk_vs_balancing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuxalin/THST/4ff6aa575f87dce4321754981314a923b0b4bbb6/benchmark/results/bgi_benchmark_rtree_query_blk_vs_balancing.png -------------------------------------------------------------------------------- /benchmark/results/bgi_benchmark_rtree_query_itr_vs_blk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuxalin/THST/4ff6aa575f87dce4321754981314a923b0b4bbb6/benchmark/results/bgi_benchmark_rtree_query_itr_vs_blk.png -------------------------------------------------------------------------------- /benchmark/results/bgi_linear_blk_ct.dat: -------------------------------------------------------------------------------- 1 | bgi_ct(L,BLK) ----------------------------- 2 | capacity load query 3 | 4 2 0.324497 0.064723 4 | 8 4 0.305226 0.068516 5 | 12 6 0.276268 0.073830 6 | 16 8 0.251592 0.068627 7 | 20 10 0.277161 0.079231 8 | 24 12 0.253156 0.071495 9 | 32 16 0.236099 0.086375 10 | 36 18 0.244142 0.097201 11 | 44 22 0.231323 0.108338 12 | 48 24 0.228226 0.113453 13 | 64 32 0.218781 0.134914 14 | 80 40 0.209921 0.144560 15 | 96 48 0.200973 0.180334 16 | 112 56 0.218221 0.204017 17 | 128 64 0.218399 0.216421 18 | 144 72 0.200857 0.245209 19 | 160 80 0.213541 0.263210 20 | -------------------------------------------------------------------------------- /benchmark/results/bgi_linear_ct.dat: -------------------------------------------------------------------------------- 1 | bgi_ct(L,ITR) ----------------------------- 2 | capacity load query 3 | 4 2 0.973636 1.223432 4 | 8 4 0.696464 2.254335 5 | 12 6 0.759033 1.946901 6 | 16 8 0.765923 2.042494 7 | 20 10 0.817272 1.877320 8 | 24 12 0.797958 1.906852 9 | 32 16 0.852861 1.822924 10 | 36 18 0.894820 2.098527 11 | 44 22 0.985376 2.113979 12 | 48 24 0.940643 2.447059 13 | 64 32 1.009397 2.520136 14 | 80 40 1.175414 2.965804 15 | 96 48 1.200753 3.959622 16 | 112 56 1.390010 4.362403 17 | 128 64 1.432562 4.732639 18 | 144 72 1.428042 5.373647 19 | 160 80 1.333911 4.710946 20 | bgi_ct(L,ITR) ----------------------------- 21 | capacity insertions avg total 22 | 16 8 10 0.001 0.00011 23 | 16 8 11 0.001 0.00011 24 | 16 8 12 0.001 0.00013 25 | 16 8 13 0.001 0.00015 26 | 16 8 14 0.001 0.00015 27 | 16 8 15 0.001 0.00017 28 | 16 8 16 0.001 0.00017 29 | 16 8 17 0.002 0.00034 30 | 16 8 18 0.002 0.00036 31 | 16 8 19 0.002 0.00039 32 | 16 8 20 0.002 0.00041 33 | 16 8 22 0.002 0.00045 34 | 16 8 24 0.003 0.00052 35 | 16 8 26 0.003 0.00058 36 | 16 8 28 0.003 0.00061 37 | 16 8 30 0.004 0.00071 38 | 16 8 33 0.004 0.00084 39 | 16 8 36 0.006 0.00114 40 | 16 8 39 0.005 0.00109 41 | 16 8 42 0.006 0.00122 42 | 16 8 46 0.009 0.00177 43 | 16 8 50 0.011 0.00221 44 | 16 8 55 0.012 0.00242 45 | 16 8 60 0.012 0.00243 46 | 16 8 66 0.013 0.00263 47 | 16 8 72 0.014 0.00283 48 | 16 8 79 0.016 0.00314 49 | 16 8 86 0.017 0.00347 50 | 16 8 94 0.021 0.00413 51 | 16 8 103 0.023 0.00458 52 | 16 8 113 0.027 0.00540 53 | 16 8 124 0.037 0.00735 54 | 16 8 136 0.037 0.00736 55 | 16 8 149 0.039 0.00775 56 | 16 8 163 0.047 0.00935 57 | 16 8 179 0.051 0.01020 58 | 16 8 196 0.063 0.01265 59 | 16 8 215 0.067 0.01341 60 | 16 8 236 0.089 0.01776 61 | 16 8 259 0.087 0.01731 62 | 16 8 284 0.111 0.02215 63 | 16 8 312 0.114 0.02272 64 | 16 8 343 0.125 0.02498 65 | 16 8 377 0.143 0.02862 66 | 16 8 414 0.168 0.03354 67 | 16 8 455 0.189 0.03772 68 | 16 8 500 0.211 0.04223 69 | 16 8 550 0.242 0.04843 70 | 16 8 605 0.283 0.05666 71 | 16 8 665 0.314 0.06280 72 | 16 8 731 0.339 0.06786 73 | 16 8 804 0.397 0.07950 74 | 16 8 884 0.431 0.08614 75 | 16 8 972 0.510 0.10203 76 | 16 8 1069 0.573 0.11466 77 | 16 8 1175 0.728 0.14567 78 | 16 8 1292 0.747 0.14936 79 | 16 8 1421 0.815 0.16302 80 | 16 8 1563 0.921 0.18424 81 | 16 8 1719 1.073 0.21460 82 | 16 8 1890 1.269 0.25374 83 | 16 8 2079 1.431 0.28617 84 | 16 8 2286 1.622 0.32437 85 | 16 8 2514 1.836 0.36726 86 | 16 8 2765 2.133 0.42651 87 | 16 8 3041 2.539 0.50774 88 | 16 8 3345 2.974 0.59472 89 | 16 8 3679 3.280 0.65594 90 | 16 8 4046 3.806 0.76121 91 | 16 8 4450 4.382 0.87633 92 | 16 8 4895 5.140 1.02802 93 | 16 8 5384 5.809 1.16181 94 | 16 8 5922 6.642 1.32835 95 | 16 8 6514 7.535 1.50707 96 | 16 8 7165 8.726 1.74517 97 | 16 8 7881 9.834 1.96683 98 | 16 8 8669 11.344 2.26872 99 | 16 8 9535 13.301 2.66023 100 | 16 8 10488 14.594 2.91881 101 | 16 8 11536 16.407 3.28143 102 | 16 8 12689 19.381 3.87624 103 | 16 8 13957 23.182 4.63641 104 | 16 8 15352 28.328 5.66565 105 | 16 8 16887 32.084 6.41676 106 | 16 8 18575 37.582 7.51633 107 | -------------------------------------------------------------------------------- /benchmark/results/bgi_quadratic_blk_ct.dat: -------------------------------------------------------------------------------- 1 | bgi_ct(Q,BLK) ----------------------------- 2 | capacity load query 3 | 4 2 0.326941 0.070172 4 | 8 4 0.277352 0.065390 5 | 12 6 0.261072 0.077335 6 | 16 8 0.255953 0.063240 7 | 20 10 0.244725 0.068916 8 | 24 12 0.238286 0.083821 9 | 32 16 0.232412 0.084690 10 | 36 18 0.225113 0.091341 11 | 44 22 0.236996 0.104115 12 | 48 24 0.216373 0.107511 13 | 64 32 0.211364 0.137263 14 | 80 40 0.212507 0.156390 15 | 96 48 0.208818 0.168593 16 | 112 56 0.195095 0.185177 17 | 128 64 0.204550 0.197604 18 | 144 72 0.204583 0.218740 19 | 160 80 0.194872 0.255641 20 | -------------------------------------------------------------------------------- /benchmark/results/bgi_quadratic_ct.dat: -------------------------------------------------------------------------------- 1 | bgi_ct(Q,ITR) ----------------------------- 2 | capacity load query 3 | 4 2 1.157041 0.279948 4 | 8 4 1.009719 0.602721 5 | 12 6 1.064459 0.638796 6 | 16 8 0.980587 0.506765 7 | 20 10 1.131947 0.410805 8 | 24 12 1.245931 0.432642 9 | 32 16 1.448077 0.448606 10 | 36 18 1.549736 0.357388 11 | 44 22 1.675735 0.421443 12 | 48 24 1.729788 0.367865 13 | 64 32 2.040832 0.448258 14 | 80 40 2.370330 0.429717 15 | 96 48 2.725554 0.423232 16 | 112 56 3.075861 0.460734 17 | 128 64 3.433537 0.489704 18 | 144 72 3.716193 0.617191 19 | 160 80 3.886743 0.536655 20 | bgi_ct(Q,ITR) ----------------------------- 21 | capacity insertions avg total 22 | 16 8 10 0.001 0.00010 23 | 16 8 11 0.001 0.00012 24 | 16 8 12 0.001 0.00012 25 | 16 8 13 0.001 0.00014 26 | 16 8 14 0.001 0.00015 27 | 16 8 15 0.001 0.00017 28 | 16 8 16 0.001 0.00019 29 | 16 8 17 0.004 0.00087 30 | 16 8 18 0.005 0.00091 31 | 16 8 19 0.004 0.00090 32 | 16 8 20 0.005 0.00092 33 | 16 8 22 0.005 0.00096 34 | 16 8 24 0.005 0.00103 35 | 16 8 26 0.005 0.00107 36 | 16 8 28 0.006 0.00121 37 | 16 8 30 0.007 0.00147 38 | 16 8 33 0.009 0.00182 39 | 16 8 36 0.011 0.00220 40 | 16 8 39 0.012 0.00235 41 | 16 8 42 0.013 0.00252 42 | 16 8 46 0.016 0.00320 43 | 16 8 50 0.017 0.00344 44 | 16 8 55 0.018 0.00353 45 | 16 8 60 0.022 0.00432 46 | 16 8 66 0.025 0.00501 47 | 16 8 72 0.026 0.00530 48 | 16 8 79 0.030 0.00591 49 | 16 8 86 0.033 0.00650 50 | 16 8 94 0.043 0.00863 51 | 16 8 103 0.041 0.00820 52 | 16 8 113 0.048 0.00966 53 | 16 8 124 0.052 0.01037 54 | 16 8 136 0.058 0.01166 55 | 16 8 149 0.063 0.01269 56 | 16 8 163 0.069 0.01383 57 | 16 8 179 0.094 0.01879 58 | 16 8 196 0.098 0.01954 59 | 16 8 215 0.123 0.02458 60 | 16 8 236 0.113 0.02252 61 | 16 8 259 0.149 0.02976 62 | 16 8 284 0.145 0.02894 63 | 16 8 312 0.178 0.03566 64 | 16 8 343 0.208 0.04157 65 | 16 8 377 0.229 0.04578 66 | 16 8 414 0.261 0.05225 67 | 16 8 455 0.264 0.05285 68 | 16 8 500 0.317 0.06344 69 | 16 8 550 0.342 0.06840 70 | 16 8 605 0.367 0.07345 71 | 16 8 665 0.412 0.08232 72 | 16 8 731 0.456 0.09117 73 | 16 8 804 0.516 0.10325 74 | 16 8 884 0.585 0.11692 75 | 16 8 972 0.638 0.12764 76 | 16 8 1069 0.714 0.14288 77 | 16 8 1175 0.846 0.16917 78 | 16 8 1292 0.943 0.18864 79 | 16 8 1421 0.979 0.19574 80 | 16 8 1563 1.146 0.22920 81 | 16 8 1719 1.330 0.26596 82 | 16 8 1890 1.543 0.30851 83 | 16 8 2079 1.627 0.32535 84 | 16 8 2286 1.941 0.38826 85 | 16 8 2514 2.062 0.41235 86 | 16 8 2765 2.287 0.45739 87 | 16 8 3041 2.668 0.53351 88 | 16 8 3345 2.995 0.59902 89 | 16 8 3679 3.285 0.65704 90 | 16 8 4046 3.488 0.69754 91 | 16 8 4450 4.049 0.80983 92 | 16 8 4895 4.553 0.91062 93 | 16 8 5384 5.096 1.01927 94 | 16 8 5922 5.581 1.11620 95 | 16 8 6514 6.215 1.24300 96 | 16 8 7165 6.848 1.36967 97 | 16 8 7881 7.696 1.53917 98 | 16 8 8669 8.446 1.68922 99 | 16 8 9535 8.972 1.79438 100 | 16 8 10488 10.238 2.04754 101 | 16 8 11536 12.587 2.51732 102 | 16 8 12689 14.092 2.81830 103 | 16 8 13957 15.553 3.11066 104 | 16 8 15352 17.217 3.44342 105 | 16 8 16887 19.614 3.92282 106 | 16 8 18575 21.144 4.22881 107 | -------------------------------------------------------------------------------- /benchmark/results/bgi_rstar_blk_ct.dat: -------------------------------------------------------------------------------- 1 | bgi_ct(R,BLK) ----------------------------- 2 | capacity load query 3 | 4 2 0.321297 0.070905 4 | 8 4 0.270455 0.059562 5 | 12 6 0.258771 0.060569 6 | 16 8 0.231068 0.065003 7 | 20 10 0.243472 0.082795 8 | 24 12 0.231078 0.072328 9 | 32 16 0.223868 0.096825 10 | 36 18 0.220398 0.090646 11 | 44 22 0.236174 0.099294 12 | 48 24 0.217177 0.112642 13 | 64 32 0.208549 0.128059 14 | 80 40 0.206633 0.147441 15 | 96 48 0.201561 0.166745 16 | 112 56 0.203956 0.190325 17 | 128 64 0.195690 0.201152 18 | 144 72 0.192857 0.247921 19 | 160 80 0.203807 0.249643 20 | -------------------------------------------------------------------------------- /benchmark/results/bgi_rstar_ct.dat: -------------------------------------------------------------------------------- 1 | bgi_ct(R,ITR) ----------------------------- 2 | capacity load query 3 | 4 2 2.445516 0.193045 4 | 8 4 2.336408 0.157228 5 | 12 6 2.722562 0.203490 6 | 16 8 3.178095 0.127149 7 | 20 10 3.775004 0.169141 8 | 24 12 4.196342 0.172045 9 | 32 16 5.077666 0.147902 10 | 36 18 5.670488 0.186678 11 | 44 22 6.299128 0.195234 12 | 48 24 6.569764 0.186263 13 | 64 32 6.691917 0.248554 14 | 80 40 6.244863 0.297300 15 | 96 48 5.940594 0.389491 16 | 112 56 6.072672 0.509261 17 | 128 64 5.650804 0.571649 18 | 144 72 5.833564 0.668141 19 | 160 80 5.798504 0.672752 20 | bgi_ct(R,ITR) ----------------------------- 21 | capacity insertions avg total 22 | 16 8 10 0.001 0.00017 23 | 16 8 11 0.001 0.00012 24 | 16 8 12 0.001 0.00016 25 | 16 8 13 0.001 0.00016 26 | 16 8 14 0.001 0.00018 27 | 16 8 15 0.001 0.00020 28 | 16 8 16 0.001 0.00022 29 | 16 8 17 0.005 0.00110 30 | 16 8 18 0.006 0.00129 31 | 16 8 19 0.007 0.00137 32 | 16 8 20 0.005 0.00104 33 | 16 8 22 0.005 0.00110 34 | 16 8 24 0.008 0.00161 35 | 16 8 26 0.009 0.00179 36 | 16 8 28 0.008 0.00159 37 | 16 8 30 0.010 0.00193 38 | 16 8 33 0.012 0.00237 39 | 16 8 36 0.015 0.00307 40 | 16 8 39 0.017 0.00345 41 | 16 8 42 0.018 0.00366 42 | 16 8 46 0.019 0.00386 43 | 16 8 50 0.024 0.00480 44 | 16 8 55 0.027 0.00542 45 | 16 8 60 0.030 0.00600 46 | 16 8 66 0.036 0.00725 47 | 16 8 72 0.053 0.01057 48 | 16 8 79 0.057 0.01133 49 | 16 8 86 0.058 0.01166 50 | 16 8 94 0.066 0.01325 51 | 16 8 103 0.077 0.01531 52 | 16 8 113 0.092 0.01833 53 | 16 8 124 0.107 0.02149 54 | 16 8 136 0.125 0.02509 55 | 16 8 149 0.150 0.03005 56 | 16 8 163 0.176 0.03517 57 | 16 8 179 0.216 0.04312 58 | 16 8 196 0.238 0.04751 59 | 16 8 215 0.287 0.05748 60 | 16 8 236 0.310 0.06200 61 | 16 8 259 0.352 0.07032 62 | 16 8 284 0.381 0.07611 63 | 16 8 312 0.442 0.08842 64 | 16 8 343 0.511 0.10225 65 | 16 8 377 0.568 0.11360 66 | 16 8 414 0.634 0.12683 67 | 16 8 455 0.699 0.13986 68 | 16 8 500 0.784 0.15678 69 | 16 8 550 0.880 0.17598 70 | 16 8 605 1.001 0.20018 71 | 16 8 665 1.167 0.23335 72 | 16 8 731 1.315 0.26306 73 | 16 8 804 1.383 0.27668 74 | 16 8 884 1.641 0.32815 75 | 16 8 972 1.787 0.35740 76 | 16 8 1069 1.927 0.38535 77 | 16 8 1175 2.208 0.44156 78 | 16 8 1292 2.466 0.49316 79 | 16 8 1421 2.741 0.54816 80 | 16 8 1563 3.058 0.61160 81 | 16 8 1719 3.414 0.68279 82 | 16 8 1890 3.727 0.74536 83 | 16 8 2079 4.197 0.83942 84 | 16 8 2286 4.570 0.91407 85 | 16 8 2514 5.229 1.04579 86 | 16 8 2765 5.763 1.15251 87 | 16 8 3041 6.379 1.27584 88 | 16 8 3345 7.173 1.43460 89 | 16 8 3679 7.947 1.58942 90 | 16 8 4046 8.959 1.79187 91 | 16 8 4450 10.046 2.00924 92 | 16 8 4895 11.067 2.21338 93 | 16 8 5384 12.292 2.45842 94 | 16 8 5922 13.539 2.70779 95 | 16 8 6514 14.829 2.96589 96 | 16 8 7165 16.403 3.28056 97 | 16 8 7881 18.156 3.63125 98 | 16 8 8669 20.128 4.02569 99 | 16 8 9535 22.396 4.47927 100 | 16 8 10488 25.111 5.02217 101 | 16 8 11536 28.012 5.60232 102 | 16 8 12689 31.020 6.20393 103 | 16 8 13957 34.853 6.97065 104 | 16 8 15352 37.960 7.59208 105 | 16 8 16887 42.203 8.44053 106 | 16 8 18575 46.333 9.26656 107 | -------------------------------------------------------------------------------- /benchmark/results/plot_results.plt: -------------------------------------------------------------------------------- 1 | #!/gnuplot 2 | ################################################################################ 3 | # Results plotting configuration for spatial_index_benchmark 4 | # https://github.com/mloskot/spatial_index_benchmark 5 | ################################################################################ 6 | # Copyright (C) 2013 Mateusz Loskot 7 | # 8 | # Distributed under the Boost Software License, Version 1.0. 9 | # (See accompanying file LICENSE_1_0.txt or copy at 10 | # http://www.boost.org/LICENSE_1_0.txt) 11 | ################################################################################ 12 | # 13 | #set terminal wxt 14 | set key on left horizontal 15 | set terminal png size 1200,1000 font ",13" 16 | outfmt = ".png" 17 | #set terminal svg size 1200,1000 dynamic font ",13" 18 | #outfmt = ".svg" 19 | 20 | algos = "linear quadratic rstar" 21 | variants = "quadratic quadratic_custom quadratic_sphere quadratic_sphere_custom" 22 | 23 | array libs[2] = ["bgi", "thst"] 24 | array arr[2] = [algos, variants] 25 | array ext[2] = ["_ct", ""] 26 | 27 | set xlabel "max capacity (min capacity = max * 0.5, quadtree capacity = max * 64 )" 28 | # 29 | # Plot loading times 30 | # 31 | set ylabel "load 1M objects in seconds" 32 | 33 | set title "Iterative loading using RTree balancing algorithms" 34 | set output "benchmark_load_bgi_vs_thst".outfmt 35 | plot for[i=1:2] for [ m in arr[i] ] \ 36 | "<(head -20 ".libs[i]."_".m.ext[i].".dat)" using 1:3 with linespoints title libs[i]."-".m noenhanced 37 | 38 | set title "BGI: Iterative loading using R-tree balancing algorithms vs bulk loading (blk)" 39 | set output "bgi_benchmark_rtree_load_itr_vs_blk".outfmt 40 | plot for [l in "bgi"] for [ m in algos." rstar\_blk" ] \ 41 | "<(head -20 ".l."_".m."_ct.dat)" using 1:3 with linespoints title l."-".m noenhanced 42 | 43 | set title "THST: Iterative loading between the tree variations" 44 | set output "thst_benchmark_load_itr".outfmt 45 | plot for [l in "thst"] for [ m in variants." quadtree" ] \ 46 | "<(head -20 ".l."_".m.".dat)" using 1:3 with linespoints title l."-".m noenhanced 47 | 48 | set title "Bulk loading (blk) times not affected by R-tree balancing algorithms" 49 | set output "bgi_benchmark_rtree_load_blk_vs_balancing".outfmt 50 | plot for [l in "bgi"] for [m in algos] \ 51 | "<(head -20 ".l."_".m."_blk_ct.dat)" using 1:3 with linespoints title l." ".m."_blk" noenhanced 52 | 53 | # 54 | # Plot querying times 55 | # 56 | set ylabel "query 100K of 1M objects in seconds" 57 | 58 | set title "Query times for each tree balancing algorithms" 59 | set output "benchmark_query_bgi_vs_thst".outfmt 60 | plot for[i=1:2] for [ m in arr[i] ] \ 61 | "<(head -20 ".libs[i]."_".m.ext[i].".dat)" using 1:4 with linespoints title libs[i]."-".m noenhanced 62 | 63 | array qarr[2] = ["rstar rstar_blk", "quadratic_custom quadratic_sphere_custom"] 64 | set title "Best query times for the tree balancing algorithms" 65 | set output "benchmark_query_bgi_vs_thst_best".outfmt 66 | plot for[i=1:2] for [ m in qarr[i] ] \ 67 | "<(head -20 ".libs[i]."_".m.ext[i].".dat)" using 1:4 with linespoints title libs[i]."-".m noenhanced 68 | 69 | set title "BGI: Query times using R-tree balancing algorithms vs bulk loading (blk)" 70 | set output "bgi_benchmark_rtree_query_itr_vs_blk".outfmt 71 | plot for [l in "bgi"] for [ m in algos." rstar_blk" ] \ 72 | "<(head -20 ".l."_".m."_ct.dat)" using 1:4 with linespoints title l."-".m noenhanced 73 | 74 | set title "THST: Query times between the tree variations" 75 | set output "thst_benchmark_query_itr".outfmt 76 | plot for [l in "thst"] for [ m in variants." quadtree" ] \ 77 | "<(head -20 ".l."_".m.".dat)" using 1:4 with linespoints title l."-".m noenhanced 78 | 79 | set title "THST: Query times between the custom allocator variant" 80 | set output "thst_benchmark_query_cst".outfmt 81 | plot for [l in "thst"] for [ m in variants ] \ 82 | "<(head -20 ".l."_".m.".dat)" using 1:4 with linespoints title l."-".m noenhanced 83 | 84 | set title "BGI: Query times not affected by R-tree bulk loading (blk) vs balancing algorithms" 85 | set output "bgi_benchmark_rtree_query_blk_vs_balancing".outfmt 86 | plot for [l in "bgi"] for [m in algos] \ 87 | "<(head -20 ".l."_".m."_blk_ct.dat)" using 1:4 with linespoints title l." ".m."_blk" noenhanced 88 | 89 | # 90 | # Plot dynamic use case(querying + insert + clear) times 91 | # 92 | 93 | set xlabel "number of iterations(insertions/queries)" 94 | set ylabel "average time for an 16, 8 RTree of 200 runs in msec" 95 | 96 | set title "Dynamic use case(clear followed by query and insert) using RTree balancing algorithms" 97 | set output "benchmark_dynamic_bgi_vs_thst".outfmt 98 | plot for[i=1:2] for [ m in arr[i] ] \ 99 | "<(sed -n '21, 107p' ".libs[i]."_".m.ext[i].".dat)" using 3:4 with linespoints title libs[i]."-".m noenhanced 100 | 101 | array darr[2] = ["linear quadratic", "quadratic quadratic_custom quadratic_sphere"] 102 | set title "Dynamic use case(clear followed by query and insert) using RTree balancing algorithms" 103 | set output "benchmark_dynamic_vsmall_bgi_vs_thst".outfmt 104 | plot for[i=1:2] for [ m in darr[i] ] \ 105 | "<(sed -n '21, 60p' ".libs[i]."_".m.ext[i].".dat)" using 3:4 with linespoints title libs[i]."-".m noenhanced 106 | 107 | set title "Dynamic use case(clear followed by query and insert) using RTree balancing algorithms" 108 | set output "benchmark_dynamic_small_bgi_vs_thst".outfmt 109 | plot for[i=1:2] for [ m in darr[i] ] \ 110 | "<(sed -n '21, 80p' ".libs[i]."_".m.ext[i].".dat)" using 3:4 with linespoints title libs[i]."-".m noenhanced 111 | 112 | #pause -1 113 | # EOF 114 | -------------------------------------------------------------------------------- /benchmark/results/thst_benchmark_load_itr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuxalin/THST/4ff6aa575f87dce4321754981314a923b0b4bbb6/benchmark/results/thst_benchmark_load_itr.png -------------------------------------------------------------------------------- /benchmark/results/thst_benchmark_query_cst.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuxalin/THST/4ff6aa575f87dce4321754981314a923b0b4bbb6/benchmark/results/thst_benchmark_query_cst.png -------------------------------------------------------------------------------- /benchmark/results/thst_benchmark_query_itr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuxalin/THST/4ff6aa575f87dce4321754981314a923b0b4bbb6/benchmark/results/thst_benchmark_query_itr.png -------------------------------------------------------------------------------- /benchmark/results/thst_quadratic.dat: -------------------------------------------------------------------------------- 1 | thst(Q,ITR) ----------------------------- 2 | capacity load query 3 | 4 2 0.927175 0.252110 4 | 8 4 0.770737 0.442419 5 | 12 6 0.793147 0.550713 6 | 16 8 0.816003 0.404724 7 | 20 10 0.868807 0.355388 8 | 24 12 0.937493 0.344059 9 | 32 16 1.037767 0.313201 10 | 36 18 1.119070 0.291604 11 | 44 22 1.179968 0.292458 12 | 48 24 1.149176 0.281764 13 | 64 32 1.210558 0.325131 14 | 80 40 1.412647 0.289669 15 | 96 48 1.613721 0.289752 16 | 112 56 1.791406 0.287840 17 | 128 64 1.947521 0.284779 18 | 144 72 2.004791 0.302553 19 | 160 80 2.203736 0.296073 20 | thst(Q,ITR) ----------------------------- 21 | capacity insertions avg total 22 | 16 8 10 0.000 0.00010 23 | 16 8 11 0.000 0.00010 24 | 16 8 12 0.000 0.00010 25 | 16 8 13 0.001 0.00011 26 | 16 8 14 0.001 0.00013 27 | 16 8 15 0.001 0.00013 28 | 16 8 16 0.001 0.00014 29 | 16 8 17 0.004 0.00077 30 | 16 8 18 0.004 0.00078 31 | 16 8 19 0.004 0.00079 32 | 16 8 20 0.004 0.00080 33 | 16 8 22 0.004 0.00085 34 | 16 8 24 0.004 0.00087 35 | 16 8 26 0.005 0.00092 36 | 16 8 28 0.005 0.00106 37 | 16 8 30 0.006 0.00128 38 | 16 8 33 0.008 0.00160 39 | 16 8 36 0.010 0.00192 40 | 16 8 39 0.010 0.00207 41 | 16 8 42 0.011 0.00219 42 | 16 8 46 0.012 0.00243 43 | 16 8 50 0.013 0.00261 44 | 16 8 55 0.015 0.00301 45 | 16 8 60 0.017 0.00341 46 | 16 8 66 0.019 0.00387 47 | 16 8 72 0.021 0.00426 48 | 16 8 79 0.024 0.00475 49 | 16 8 86 0.026 0.00522 50 | 16 8 94 0.030 0.00591 51 | 16 8 103 0.034 0.00673 52 | 16 8 113 0.035 0.00702 53 | 16 8 124 0.039 0.00783 54 | 16 8 136 0.043 0.00864 55 | 16 8 149 0.048 0.00969 56 | 16 8 163 0.053 0.01064 57 | 16 8 179 0.060 0.01205 58 | 16 8 196 0.068 0.01361 59 | 16 8 215 0.076 0.01519 60 | 16 8 236 0.086 0.01720 61 | 16 8 259 0.093 0.01861 62 | 16 8 284 0.103 0.02057 63 | 16 8 312 0.114 0.02279 64 | 16 8 343 0.140 0.02794 65 | 16 8 377 0.177 0.03537 66 | 16 8 414 0.159 0.03182 67 | 16 8 455 0.176 0.03511 68 | 16 8 500 0.198 0.03968 69 | 16 8 550 0.215 0.04302 70 | 16 8 605 0.239 0.04790 71 | 16 8 665 0.267 0.05333 72 | 16 8 731 0.293 0.05852 73 | 16 8 804 0.340 0.06806 74 | 16 8 884 0.364 0.07286 75 | 16 8 972 0.422 0.08435 76 | 16 8 1069 0.443 0.08852 77 | 16 8 1175 0.503 0.10062 78 | 16 8 1292 0.545 0.10909 79 | 16 8 1421 0.606 0.12119 80 | 16 8 1563 0.674 0.13480 81 | 16 8 1719 0.789 0.15789 82 | 16 8 1890 0.853 0.17064 83 | 16 8 2079 0.942 0.18847 84 | 16 8 2286 1.034 0.20686 85 | 16 8 2514 1.147 0.22936 86 | 16 8 2765 1.307 0.26139 87 | 16 8 3041 1.429 0.28574 88 | 16 8 3345 1.530 0.30601 89 | 16 8 3679 1.731 0.34611 90 | 16 8 4046 1.889 0.37778 91 | 16 8 4450 2.107 0.42130 92 | 16 8 4895 2.360 0.47200 93 | 16 8 5384 2.556 0.51112 94 | 16 8 5922 2.833 0.56650 95 | 16 8 6514 3.236 0.64711 96 | 16 8 7165 3.563 0.71255 97 | 16 8 7881 3.927 0.78531 98 | 16 8 8669 4.379 0.87574 99 | 16 8 9535 4.926 0.98511 100 | 16 8 10488 5.315 1.06310 101 | 16 8 11536 5.998 1.19968 102 | 16 8 12689 6.749 1.34974 103 | 16 8 13957 7.124 1.42474 104 | 16 8 15352 7.947 1.58943 105 | 16 8 16887 8.844 1.76874 106 | 16 8 18575 9.885 1.97708 107 | -------------------------------------------------------------------------------- /benchmark/results/thst_quadratic_custom.dat: -------------------------------------------------------------------------------- 1 | thst(Q,CST) ----------------------------- 2 | capacity load query 3 | 4 2 0.767547 0.226498 4 | 8 4 0.675204 0.457969 5 | 12 6 0.675268 0.471975 6 | 16 8 0.728377 0.358054 7 | 20 10 0.776270 0.328221 8 | 24 12 0.794084 0.318245 9 | 32 16 0.925584 0.271476 10 | 36 18 0.985688 0.276220 11 | 44 22 1.026657 0.281780 12 | 48 24 1.042686 0.245466 13 | 64 32 1.145558 0.311816 14 | 80 40 1.303836 0.292341 15 | 96 48 1.635151 0.261068 16 | 112 56 1.717082 0.294759 17 | 128 64 1.905360 0.287394 18 | 144 72 1.964808 0.315453 19 | 160 80 2.194842 0.290316 20 | thst(Q,CST) ----------------------------- 21 | capacity insertions avg total 22 | 16 8 10 0.000 0.00008 23 | 16 8 11 0.000 0.00008 24 | 16 8 12 0.000 0.00009 25 | 16 8 13 0.000 0.00010 26 | 16 8 14 0.001 0.00011 27 | 16 8 15 0.001 0.00012 28 | 16 8 16 0.001 0.00013 29 | 16 8 17 0.004 0.00076 30 | 16 8 18 0.004 0.00076 31 | 16 8 19 0.004 0.00077 32 | 16 8 20 0.004 0.00079 33 | 16 8 22 0.004 0.00082 34 | 16 8 24 0.004 0.00085 35 | 16 8 26 0.005 0.00090 36 | 16 8 28 0.005 0.00107 37 | 16 8 30 0.006 0.00127 38 | 16 8 33 0.008 0.00157 39 | 16 8 36 0.009 0.00184 40 | 16 8 39 0.010 0.00200 41 | 16 8 42 0.011 0.00215 42 | 16 8 46 0.012 0.00236 43 | 16 8 50 0.013 0.00254 44 | 16 8 55 0.015 0.00291 45 | 16 8 60 0.017 0.00336 46 | 16 8 66 0.018 0.00362 47 | 16 8 72 0.020 0.00407 48 | 16 8 79 0.023 0.00450 49 | 16 8 86 0.025 0.00494 50 | 16 8 94 0.027 0.00545 51 | 16 8 103 0.041 0.00826 52 | 16 8 113 0.045 0.00902 53 | 16 8 124 0.040 0.00804 54 | 16 8 136 0.042 0.00847 55 | 16 8 149 0.048 0.00954 56 | 16 8 163 0.052 0.01032 57 | 16 8 179 0.058 0.01165 58 | 16 8 196 0.066 0.01317 59 | 16 8 215 0.074 0.01473 60 | 16 8 236 0.082 0.01633 61 | 16 8 259 0.094 0.01877 62 | 16 8 284 0.099 0.01977 63 | 16 8 312 0.110 0.02207 64 | 16 8 343 0.123 0.02468 65 | 16 8 377 0.141 0.02814 66 | 16 8 414 0.168 0.03355 67 | 16 8 455 0.192 0.03841 68 | 16 8 500 0.189 0.03775 69 | 16 8 550 0.207 0.04132 70 | 16 8 605 0.228 0.04569 71 | 16 8 665 0.272 0.05433 72 | 16 8 731 0.283 0.05665 73 | 16 8 804 0.319 0.06388 74 | 16 8 884 0.418 0.08356 75 | 16 8 972 0.433 0.08657 76 | 16 8 1069 0.431 0.08612 77 | 16 8 1175 0.475 0.09508 78 | 16 8 1292 0.548 0.10967 79 | 16 8 1421 0.588 0.11754 80 | 16 8 1563 0.708 0.14153 81 | 16 8 1719 0.768 0.15357 82 | 16 8 1890 0.857 0.17146 83 | 16 8 2079 0.914 0.18285 84 | 16 8 2286 0.984 0.19673 85 | 16 8 2514 1.124 0.22478 86 | 16 8 2765 1.236 0.24714 87 | 16 8 3041 1.366 0.27316 88 | 16 8 3345 1.501 0.30013 89 | 16 8 3679 1.653 0.33050 90 | 16 8 4046 1.883 0.37659 91 | 16 8 4450 2.062 0.41247 92 | 16 8 4895 2.263 0.45262 93 | 16 8 5384 2.576 0.51526 94 | 16 8 5922 2.950 0.58999 95 | 16 8 6514 3.128 0.62565 96 | 16 8 7165 3.556 0.71127 97 | 16 8 7881 3.908 0.78153 98 | 16 8 8669 4.219 0.84384 99 | 16 8 9535 4.708 0.94160 100 | 16 8 10488 5.193 1.03850 101 | 16 8 11536 5.953 1.19059 102 | 16 8 12689 6.285 1.25692 103 | 16 8 13957 7.002 1.40039 104 | 16 8 15352 7.817 1.56332 105 | 16 8 16887 8.696 1.73927 106 | 16 8 18575 9.658 1.93166 107 | -------------------------------------------------------------------------------- /benchmark/results/thst_quadratic_sphere.dat: -------------------------------------------------------------------------------- 1 | thst(QS,ITR) ----------------------------- 2 | capacity load query 3 | 4 2 1.137219 0.247682 4 | 8 4 1.108996 0.323087 5 | 12 6 1.146634 0.282006 6 | 16 8 1.204969 0.361967 7 | 20 10 1.301295 0.326505 8 | 24 12 1.415720 0.271479 9 | 32 16 1.609227 0.324816 10 | 36 18 1.645151 0.274130 11 | 44 22 1.834360 0.255746 12 | 48 24 1.867339 0.307826 13 | 64 32 2.215536 0.310829 14 | 80 40 2.488650 0.308417 15 | 96 48 2.879520 0.324244 16 | 112 56 3.142033 0.383934 17 | 128 64 3.496276 0.326044 18 | 144 72 3.642383 0.355062 19 | 160 80 3.788182 0.359247 20 | thst(QS,ITR) ----------------------------- 21 | capacity insertions avg total 22 | 16 8 10 0.000 0.00009 23 | 16 8 11 0.000 0.00010 24 | 16 8 12 0.000 0.00010 25 | 16 8 13 0.001 0.00011 26 | 16 8 14 0.001 0.00013 27 | 16 8 15 0.001 0.00013 28 | 16 8 16 0.001 0.00014 29 | 16 8 17 0.005 0.00099 30 | 16 8 18 0.005 0.00103 31 | 16 8 19 0.005 0.00104 32 | 16 8 20 0.005 0.00103 33 | 16 8 22 0.006 0.00114 34 | 16 8 24 0.005 0.00109 35 | 16 8 26 0.006 0.00118 36 | 16 8 28 0.007 0.00136 37 | 16 8 30 0.008 0.00163 38 | 16 8 33 0.010 0.00205 39 | 16 8 36 0.012 0.00245 40 | 16 8 39 0.014 0.00273 41 | 16 8 42 0.015 0.00291 42 | 16 8 46 0.016 0.00327 43 | 16 8 50 0.018 0.00351 44 | 16 8 55 0.020 0.00402 45 | 16 8 60 0.023 0.00452 46 | 16 8 66 0.027 0.00534 47 | 16 8 72 0.029 0.00588 48 | 16 8 79 0.045 0.00902 49 | 16 8 86 0.039 0.00784 50 | 16 8 94 0.040 0.00809 51 | 16 8 103 0.048 0.00959 52 | 16 8 113 0.065 0.01291 53 | 16 8 124 0.063 0.01268 54 | 16 8 136 0.063 0.01264 55 | 16 8 149 0.070 0.01405 56 | 16 8 163 0.079 0.01574 57 | 16 8 179 0.088 0.01768 58 | 16 8 196 0.101 0.02010 59 | 16 8 215 0.112 0.02243 60 | 16 8 236 0.149 0.02974 61 | 16 8 259 0.142 0.02848 62 | 16 8 284 0.157 0.03141 63 | 16 8 312 0.176 0.03530 64 | 16 8 343 0.197 0.03942 65 | 16 8 377 0.215 0.04297 66 | 16 8 414 0.238 0.04764 67 | 16 8 455 0.267 0.05331 68 | 16 8 500 0.292 0.05842 69 | 16 8 550 0.330 0.06609 70 | 16 8 605 0.388 0.07767 71 | 16 8 665 0.415 0.08309 72 | 16 8 731 0.475 0.09509 73 | 16 8 804 0.598 0.11951 74 | 16 8 884 0.558 0.11160 75 | 16 8 972 0.611 0.12213 76 | 16 8 1069 0.674 0.13484 77 | 16 8 1175 0.749 0.14975 78 | 16 8 1292 0.837 0.16731 79 | 16 8 1421 0.932 0.18632 80 | 16 8 1563 1.077 0.21534 81 | 16 8 1719 1.161 0.23229 82 | 16 8 1890 1.286 0.25712 83 | 16 8 2079 1.523 0.30466 84 | 16 8 2286 1.618 0.32360 85 | 16 8 2514 1.772 0.35449 86 | 16 8 2765 2.016 0.40318 87 | 16 8 3041 2.244 0.44884 88 | 16 8 3345 2.564 0.51277 89 | 16 8 3679 2.768 0.55364 90 | 16 8 4046 3.033 0.60667 91 | 16 8 4450 3.374 0.67489 92 | 16 8 4895 3.748 0.74950 93 | 16 8 5384 4.137 0.82747 94 | 16 8 5922 4.612 0.92230 95 | 16 8 6514 5.087 1.01737 96 | 16 8 7165 5.706 1.14117 97 | 16 8 7881 6.200 1.23993 98 | 16 8 8669 7.035 1.40695 99 | 16 8 9535 7.587 1.51734 100 | 16 8 10488 8.635 1.72708 101 | 16 8 11536 9.387 1.87733 102 | 16 8 12689 10.439 2.08781 103 | 16 8 13957 11.511 2.30224 104 | 16 8 15352 13.043 2.60862 105 | 16 8 16887 14.437 2.88736 106 | 16 8 18575 15.984 3.19670 107 | -------------------------------------------------------------------------------- /benchmark/results/thst_quadratic_sphere_custom.dat: -------------------------------------------------------------------------------- 1 | thst(QS,CST) ----------------------------- 2 | capacity load query 3 | 4 2 1.073648 0.249352 4 | 8 4 1.036229 0.357340 5 | 12 6 1.129157 0.273136 6 | 16 8 1.188680 0.362407 7 | 20 10 1.293878 0.328609 8 | 24 12 1.384162 0.283503 9 | 32 16 1.586171 0.321729 10 | 36 18 1.623198 0.269221 11 | 44 22 1.797812 0.244187 12 | 48 24 1.854030 0.311311 13 | 64 32 2.132996 0.311006 14 | 80 40 2.427761 0.294628 15 | 96 48 2.867575 0.335935 16 | 112 56 3.106449 0.362752 17 | 128 64 3.402298 0.353829 18 | 144 72 3.603200 0.356362 19 | 160 80 3.851543 0.379926 20 | thst(QS,CST) ----------------------------- 21 | capacity insertions avg total 22 | 16 8 10 0.000 0.00009 23 | 16 8 11 0.000 0.00007 24 | 16 8 12 0.000 0.00009 25 | 16 8 13 0.000 0.00010 26 | 16 8 14 0.001 0.00011 27 | 16 8 15 0.001 0.00012 28 | 16 8 16 0.001 0.00013 29 | 16 8 17 0.005 0.00101 30 | 16 8 18 0.005 0.00104 31 | 16 8 19 0.005 0.00106 32 | 16 8 20 0.005 0.00108 33 | 16 8 22 0.006 0.00112 34 | 16 8 24 0.006 0.00115 35 | 16 8 26 0.006 0.00123 36 | 16 8 28 0.007 0.00141 37 | 16 8 30 0.008 0.00164 38 | 16 8 33 0.010 0.00207 39 | 16 8 36 0.012 0.00247 40 | 16 8 39 0.013 0.00267 41 | 16 8 42 0.014 0.00289 42 | 16 8 46 0.016 0.00319 43 | 16 8 50 0.028 0.00556 44 | 16 8 55 0.028 0.00556 45 | 16 8 60 0.028 0.00555 46 | 16 8 66 0.027 0.00532 47 | 16 8 72 0.029 0.00572 48 | 16 8 79 0.032 0.00647 49 | 16 8 86 0.035 0.00710 50 | 16 8 94 0.039 0.00784 51 | 16 8 103 0.045 0.00898 52 | 16 8 113 0.062 0.01248 53 | 16 8 124 0.063 0.01264 54 | 16 8 136 0.062 0.01236 55 | 16 8 149 0.069 0.01377 56 | 16 8 163 0.079 0.01573 57 | 16 8 179 0.088 0.01753 58 | 16 8 196 0.098 0.01961 59 | 16 8 215 0.109 0.02186 60 | 16 8 236 0.126 0.02530 61 | 16 8 259 0.136 0.02727 62 | 16 8 284 0.150 0.03004 63 | 16 8 312 0.168 0.03357 64 | 16 8 343 0.187 0.03747 65 | 16 8 377 0.210 0.04207 66 | 16 8 414 0.233 0.04657 67 | 16 8 455 0.260 0.05203 68 | 16 8 500 0.285 0.05707 69 | 16 8 550 0.317 0.06342 70 | 16 8 605 0.407 0.08145 71 | 16 8 665 0.408 0.08154 72 | 16 8 731 0.440 0.08805 73 | 16 8 804 0.486 0.09720 74 | 16 8 884 0.540 0.10792 75 | 16 8 972 0.640 0.12796 76 | 16 8 1069 0.671 0.13427 77 | 16 8 1175 0.744 0.14879 78 | 16 8 1292 0.844 0.16872 79 | 16 8 1421 0.930 0.18598 80 | 16 8 1563 1.105 0.22101 81 | 16 8 1719 1.288 0.25755 82 | 16 8 1890 1.276 0.25514 83 | 16 8 2079 1.442 0.28845 84 | 16 8 2286 1.582 0.31650 85 | 16 8 2514 1.756 0.35110 86 | 16 8 2765 2.006 0.40124 87 | 16 8 3041 2.135 0.42705 88 | 16 8 3345 2.456 0.49122 89 | 16 8 3679 2.703 0.54066 90 | 16 8 4046 3.009 0.60179 91 | 16 8 4450 3.302 0.66048 92 | 16 8 4895 3.671 0.73413 93 | 16 8 5384 4.083 0.81656 94 | 16 8 5922 4.653 0.93051 95 | 16 8 6514 5.078 1.01556 96 | 16 8 7165 5.498 1.09954 97 | 16 8 7881 6.124 1.22487 98 | 16 8 8669 7.300 1.45998 99 | 16 8 9535 7.514 1.50287 100 | 16 8 10488 8.472 1.69431 101 | 16 8 11536 9.285 1.85692 102 | 16 8 12689 10.347 2.06931 103 | 16 8 13957 11.509 2.30171 104 | 16 8 15352 12.627 2.52536 105 | 16 8 16887 13.995 2.79904 106 | 16 8 18575 15.744 3.14886 107 | -------------------------------------------------------------------------------- /benchmark/results/thst_quadtree.dat: -------------------------------------------------------------------------------- 1 | thst(QT,ITR) ----------------------------- 2 | capacity load query 3 | 4 4 39.787768 0.568436 4 | 8 8 19.210525 0.614297 5 | 12 12 7.774024 0.855194 6 | 16 16 4.791860 1.170430 7 | 20 20 4.439733 1.345439 8 | 24 24 4.066016 1.575721 9 | 32 32 3.246050 1.877012 10 | 36 36 2.885539 2.216081 11 | 44 44 2.363216 2.600779 12 | 48 48 1.987565 2.732673 13 | 64 64 1.215387 3.763195 14 | 80 80 1.393919 4.426569 15 | 96 96 1.378581 5.354411 16 | 112 112 2.097645 9.274902 17 | 128 128 3.640999 14.455510 18 | 144 144 5.352901 18.471591 19 | 160 160 4.128191 12.898307 20 | -------------------------------------------------------------------------------- /benchmark/results/thst_quadtree_custom.dat: -------------------------------------------------------------------------------- 1 | thst(QT,CST) ----------------------------- 2 | capacity load query 3 | 4 4 42.572323 0.466008 4 | 8 8 18.767200 0.674832 5 | 12 12 8.139392 0.874513 6 | 16 16 5.384599 1.451549 7 | 20 20 4.783672 1.428283 8 | 24 24 4.185926 1.908267 9 | 32 32 4.306634 2.751207 10 | 36 36 3.755774 2.518375 11 | 44 44 2.798594 3.112093 12 | 48 48 2.465163 3.325617 13 | 64 64 1.436088 4.017000 14 | 80 80 1.558877 5.464797 15 | 96 96 1.645984 5.479203 16 | 112 112 1.547888 5.990375 17 | 128 128 1.853585 6.736912 18 | 144 144 1.867335 7.089566 19 | 160 160 2.176649 8.029535 20 | -------------------------------------------------------------------------------- /benchmark/run_benchmark.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | REM ############################################################################ 3 | REM Script running spatial_index_benchmark programs 4 | REM https://github.com/mloskot/spatial_index_benchmark 5 | REM ############################################################################ 6 | REM Copyright (C) 2013 Mateusz Loskot 7 | REM 8 | REM Distributed under the Boost Software License, Version 1.0. 9 | REM (See accompanying file LICENSE_1_0.txt or copy at 10 | REM http://www.boost.org/LICENSE_1_0.txt) 11 | REM ############################################################################ 12 | 13 | IF NOT EXIST "%1" GOTO :NOBUILDDIR 14 | 15 | for /r %%f in (%1\*.exe) do ( 16 | @echo Running %%~nf 17 | %%~ff > %%~nf.dat 18 | ) 19 | GOTO :EOF 20 | 21 | :NOBUILDDIR 22 | @echo Cannot find '%1' build directory -------------------------------------------------------------------------------- /benchmark/run_benchmark.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ################################################################################ 3 | # Script running spatial_index_benchmark programs 4 | # https://github.com/mloskot/spatial_index_benchmark 5 | ################################################################################ 6 | # Copyright (C) 2013 Mateusz Loskot 7 | # 8 | # Distributed under the Boost Software License, Version 1.0. 9 | # (See accompanying file LICENSE_1_0.txt or copy at 10 | # http://www.boost.org/LICENSE_1_0.txt) 11 | ################################################################################ 12 | 13 | if [[ ! -d $1 ]]; then 14 | echo "Cannot find '$1' build directory" 15 | exit 1 16 | fi 17 | 18 | RDIR="$PWD" 19 | if [[ -d $2 ]]; then 20 | RDIR="$2" 21 | fi 22 | 23 | BDIR="$1" 24 | LOGEXT="dat" 25 | # 26 | # Benchmark iterative loading (takes long time, skipped on Travis CI) 27 | # 28 | if [[ "$TRAVIS" != "true" ]] ; then 29 | for variant in linear quadratic rstar quadtree 30 | do 31 | for benchmark in `find $BDIR -type f -name "*${variant}*" | sort` 32 | do 33 | name=`basename ${benchmark}` 34 | echo "$name" 35 | ${benchmark} > ${RDIR}/${name}.${LOGEXT} 36 | done; 37 | done; 38 | fi 39 | 40 | # 41 | # Benchmark bulk loading 42 | # 43 | for variant in linear quadratic rstar 44 | do 45 | for benchmark in `find $BDIR -type f -name "*${variant}_blk" | sort` 46 | do 47 | name=`basename ${benchmark}` 48 | echo "$name" 49 | ${benchmark} > ${RDIR}/${name}.${LOGEXT} 50 | done; 51 | done; 52 | 53 | cd ./results 54 | gnuplot ./plot_results.plt 55 | -------------------------------------------------------------------------------- /benchmark/spatial_index_benchmark.hpp: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (C) 2013 Mateusz Loskot 3 | // Copyright (c) 2011-2013 Adam Wulkiewicz, Lodz, Poland. 4 | // 5 | // Distributed under the Boost Software License, Version 1.0. 6 | // (See accompanying file LICENSE_1_0.txt or copy 7 | // at http://www.boost.org/LICENSE_1_0.txt) 8 | // 9 | #ifndef MLOSKOT_SPATIAL_INDEX_BENCHMARK_HPP_INCLUDED 10 | #define MLOSKOT_SPATIAL_INDEX_BENCHMARK_HPP_INCLUDED 11 | #ifdef _MSC_VER 12 | #if (_MSC_VER == 1700) 13 | #define _VARIADIC_MAX 6 14 | #endif 15 | #define NOMINMAX 16 | #endif // _MSC_VER 17 | #include "high_resolution_timer.hpp" 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | namespace sibench { 37 | 38 | // 39 | // Default benchmark settings 40 | // 41 | std::size_t const max_iterations = 1000000; 42 | std::size_t const max_capacities = 100; 43 | std::size_t const capacity_step = 2; 44 | std::size_t const max_insertions = max_iterations; 45 | std::size_t const max_random_insertions = 20000; 46 | double const insert_step = 0.10; 47 | std::size_t const random_runs = 200; 48 | std::size_t const max_queries = std::size_t(max_insertions * 0.1); 49 | std::size_t const constant_max_capacity = 4; 50 | std::size_t const constant_min_capacity = 2; 51 | std::size_t const query_size = 100; 52 | 53 | typedef uint32_t id_type; 54 | 55 | #ifndef NDEBUG 56 | #define SIBENCH_DEBUG_PRINT_INFO 1 57 | #endif 58 | 59 | enum class rtree_variant { rstar, linear, quadratic, rtree, quadtree }; 60 | 61 | inline std::pair get_rtree_split_variant() { 62 | #ifdef SIBENCH_RTREE_SPLIT_LINEAR 63 | return std::make_pair(rtree_variant::linear, "L"); 64 | #elif SIBENCH_RTREE_SPLIT_QUADRATIC 65 | return std::make_pair(rtree_variant::quadratic, "Q"); 66 | #elif SIBENCH_RTREE_SPLIT_QUADRATIC_SPHERE 67 | return std::make_pair(rtree_variant::quadratic, "QS"); 68 | #elif SIBENCH_RTREE_SPLIT_RSTAR 69 | return std::make_pair(rtree_variant::rstar, "R"); 70 | #elif SIBENCH_RTREE_SPLIT_QUADTREE 71 | return std::make_pair(rtree_variant::quadtree, "QT"); 72 | #else 73 | #error Unknown rtree split algorithm 74 | #endif 75 | } 76 | 77 | inline std::string get_rtree_load_variant() { 78 | #ifdef SIBENCH_RTREE_LOAD_ITR 79 | return "ITR"; 80 | #elif SIBENCH_RTREE_LOAD_BLK 81 | return "BLK"; 82 | #elif SIBENCH_RTREE_LOAD_CUSTOM 83 | return "CST"; 84 | #else 85 | #error Unknown rtree loading method 86 | #endif 87 | } 88 | 89 | inline std::string get_banner(std::string const &lib) { 90 | return lib + "(" + get_rtree_split_variant().second + ',' + 91 | get_rtree_load_variant() + ")"; 92 | } 93 | 94 | // 95 | // Generators of random objects 96 | // 97 | typedef float coord_t; 98 | typedef std::vector coords_t; 99 | typedef std::tuple point2d_t; 100 | typedef std::vector points2d_t; 101 | typedef struct box2d { 102 | coord_t min[2]; 103 | coord_t max[2]; 104 | } box2d_t; 105 | typedef std::vector boxes2d_t; 106 | 107 | template struct random_generator { 108 | typedef typename std::uniform_real_distribution::result_type result_type; 109 | 110 | T const max; 111 | std::mt19937 gen; 112 | std::uniform_real_distribution dis; 113 | 114 | random_generator(std::size_t n) 115 | : max(static_cast(n / 2)), 116 | gen(1) // generate the same succession of results for everyone 117 | // (unsigned 118 | // int)std::chrono::system_clock::now().time_since_epoch().count()) 119 | , 120 | dis(-max, max) {} 121 | 122 | result_type operator()() { return dis(gen); } 123 | 124 | private: 125 | random_generator(random_generator const &) /*= delete*/; 126 | random_generator &operator=(random_generator const &) /*= delete*/; 127 | }; 128 | 129 | inline coords_t generate_coordinates(std::size_t n) { 130 | random_generator rg(n); 131 | coords_t coords; 132 | coords.reserve(n); 133 | for (decltype(n) i = 0; i < n; ++i) { 134 | coords.emplace_back(rg()); 135 | } 136 | return coords; 137 | } 138 | 139 | inline points2d_t generate_points(std::size_t n) { 140 | auto coords = generate_coordinates(n * 2); 141 | points2d_t points; 142 | points.reserve(n); 143 | auto s = coords.size(); 144 | for (decltype(s) i = 0; i < s; i += 2) { 145 | points.emplace_back(coords[i], coords[i + 1]); 146 | } 147 | return points; 148 | } 149 | 150 | inline boxes2d_t generate_boxes(std::size_t n) { 151 | random_generator rg(n); 152 | 153 | boxes2d_t boxes; 154 | boxes.reserve(n); 155 | for (decltype(n) i = 0; i < n; ++i) { 156 | auto const x = rg(); 157 | auto const y = rg(); 158 | boxes.emplace_back(); 159 | auto &box = boxes.back(); 160 | float const size = 0.5f; 161 | box.min[0] = x - size; 162 | box.min[1] = y - size; 163 | box.max[0] = x + size; 164 | box.max[1] = y + size; 165 | } 166 | return boxes; 167 | } 168 | 169 | // 170 | // Benchmark running routines 171 | // 172 | struct result_info { 173 | std::string step; 174 | double min; 175 | double max; 176 | double sum; 177 | double count; 178 | std::size_t min_capacity; 179 | std::size_t max_capacity; 180 | std::size_t iterations; 181 | 182 | result_info(char const *step = "") 183 | : step(step), min(-1), max(-1), sum(0), count(0), min_capacity(0), 184 | max_capacity(0), iterations(0) {} 185 | 186 | void accumulate(result_info const &r) { 187 | min = min < 0 ? r.min : (std::min)(min, r.min); 188 | max = max < 0 ? r.max : (std::max)(max, r.max); 189 | sum += r.sum; 190 | count += r.count; 191 | 192 | assert(min <= max); 193 | } 194 | 195 | template void set_mark(Timer const &t) { 196 | auto const m = t.elapsed(); 197 | sum += m; 198 | count++; 199 | min = min < 0 ? m : (std::min)(m, min); 200 | max = max < 0 ? m : (std::max)(m, max); 201 | 202 | assert(min <= max); 203 | } 204 | 205 | double avg() const { return sum / count; } 206 | }; 207 | 208 | inline std::ostream &operator<<(std::ostream &os, result_info const &r) { 209 | os << r.step << " " << r.iterations << " in " << r.min << " to " << r.max 210 | << " sec" << std::endl; 211 | return os; 212 | } 213 | 214 | inline std::ostream &print_result(std::ostream &os, std::string const &lib, 215 | result_info const &r) { 216 | os << std::setw(15) << std::setfill(' ') << std::left 217 | << sibench::get_banner(lib) << ": " << r; 218 | return os; 219 | } 220 | 221 | inline std::ostream &print_result(std::ostream &os, std::string const & /*lib*/, 222 | result_info const &load, 223 | result_info const &query) { 224 | assert(load.min_capacity == query.min_capacity); 225 | assert(load.max_capacity == query.max_capacity); 226 | 227 | std::streamsize wn(5), wf(10); 228 | os << std::left << std::setfill(' ') << std::fixed << std::setprecision(6) 229 | << std::setw(wn) << load.max_capacity << std::setw(wn) << load.min_capacity 230 | << std::setw(wf) << load.min << std::setw(wf) << query.min << std::endl; 231 | return os; 232 | } 233 | 234 | inline std::ostream &print_insert_result(std::ostream &os, 235 | std::string const & /*lib*/, 236 | result_info const &res) { 237 | std::streamsize wn(5), wf(10); 238 | os << std::left << std::setfill(' ') << std::fixed << std::setprecision(6) 239 | << std::setw(wn) << res.max_capacity << std::setw(wn) << res.min_capacity 240 | << std::setw(wn * 2 + 1) << res.iterations << std::setw(wf) 241 | << std::setprecision(3) << res.avg() * 1000.0 << std::setw(wf) 242 | << std::setprecision(5) << res.sum << std::endl; 243 | return os; 244 | } 245 | 246 | inline std::ostream &print_result_header(std::ostream &os, 247 | std::string const &lib) { 248 | std::streamsize const wn(5), wf(10), vn(2); 249 | os << sibench::get_banner(lib) << ' ' << std::setw(wn * vn + wf * vn) 250 | << std::setfill('-') << ' ' << std::endl; 251 | os << std::left << std::setfill(' ') << std::setw(wn * vn) << "capacity" 252 | << std::setw(wf) << "load" << std::setw(wf) << "query" << std::endl; 253 | return os; 254 | } 255 | 256 | inline std::ostream &print_insert_result_header(std::ostream &os, 257 | std::string const &lib) { 258 | std::streamsize const wn(5), wf(10), vn(2); 259 | os << sibench::get_banner(lib) << ' ' << std::setw(wn * vn + wf * vn) 260 | << std::setfill('-') << ' ' << std::endl; 261 | os << std::left << std::setfill(' ') << std::setw(wn * vn) << "capacity" 262 | << std::setw(wn * vn + 1) << "insertions" << std::setw(wf) << "avg" 263 | << std::setw(wf) << "total" << std::endl; 264 | return os; 265 | } 266 | 267 | inline std::ostream &print_query_count(std::ostream &os, std::string const &lib, 268 | size_t i) { 269 | os << sibench::get_banner(lib) << " stats: found=" << i << std::endl; 270 | return os; 271 | } 272 | 273 | inline std::ostream &print_insert_count(std::ostream &os, 274 | std::string const &lib, size_t i, 275 | size_t t) { 276 | os << sibench::get_banner(lib) << " stats: insertions=" << i << " total=" << t 277 | << std::endl; 278 | return os; 279 | } 280 | 281 | template 282 | inline result_info benchmark(char const *step, std::size_t iterations, 283 | Container const &objects, Operation op) { 284 | result_info r(step); 285 | r.iterations = iterations; 286 | { 287 | util::high_resolution_timer t; 288 | op(objects, iterations); 289 | r.set_mark(t); 290 | } 291 | return r; 292 | } 293 | 294 | } // namespace sibench 295 | 296 | #endif 297 | -------------------------------------------------------------------------------- /bin/ci/before_install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Installs requirements for spatial_index_benchmark 3 | source ./bin/ci/common.sh 4 | gcc --version 5 | clang --version 6 | cmake --version 7 | 8 | -------------------------------------------------------------------------------- /bin/ci/common.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [[ "$TRAVIS" != "true" ]] ; then 3 | echo "Running this script makes no sense outside of travis-ci.org" 4 | exit 1 5 | fi 6 | # Functions 7 | tmstamp() { echo -n "[$(date '+%H:%M:%S')]" ; } 8 | # Environment 9 | NUMTHREADS=4 10 | if [[ -f /sys/devices/system/cpu/online ]]; then 11 | # Calculates 1.5 times physical threads 12 | NUMTHREADS=$(( ( $(cut -f 2 -d '-' /sys/devices/system/cpu/online) + 1 ) * 15 / 10 )) 13 | fi 14 | NUMTHREADS=1 # disable MP 15 | export NUMTHREADS 16 | export BOOST_SVN="http://svn.boost.org/svn/boost/trunk/boost" 17 | export BOOST_PREFIX="${TRAVIS_BUILD_DIR}/trunk" 18 | export BOOST_HEADERS="${TRAVIS_BUILD_DIR}/trunk/boost" 19 | -------------------------------------------------------------------------------- /bin/ci/script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "Building test examples" 4 | cd test 5 | mkdir build && cd build && cmake .. 6 | make 7 | echo "Current folder" 8 | ls 9 | ./thst_test 10 | cd ../../ 11 | 12 | # Builds and runs spatial_index_benchmark 13 | source ./bin/ci/common.sh 14 | cd benchmark 15 | mkdir build && cd build 16 | #echo "Calling cmake -DBOOST_PREFIX=${BOOST_PREFIX}" 17 | #cmake \ 18 | # -DCMAKE_BUILD_TYPE=Release \ 19 | # -DBGI_ENABLE_CT=ON \ 20 | # -DBOOST_ROOT=${BOOST_PREFIX} \ 21 | # .. 22 | #make -j ${NUMTHREADS} 23 | 24 | # Benchmarks run takes long time, skip 25 | #ctest -C Release -V --output-on-failure . 26 | -------------------------------------------------------------------------------- /cmake/THST.pc.in: -------------------------------------------------------------------------------- 1 | includedir=@INCLUDE_INSTALL_DIR@ 2 | 3 | Name: @PROJECT_NAME@ 4 | Description: Templated hierarchical spatial trees designed for high-performance and hierarchical spatial partitioning use cases. 5 | Version: @LIB_VERSION_STRING@ 6 | URL: https://github.com/tuaxlin/THST 7 | Cflags: -I${includedir} 8 | -------------------------------------------------------------------------------- /cmake/THSTConfig.cmake.in: -------------------------------------------------------------------------------- 1 | @PACKAGE_INIT@ 2 | 3 | include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake") 4 | check_required_components("@PROJECT_NAME@") 5 | -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | # Documentation 2 | 3 | ## Doxygen 4 | 5 | Install doxygen and run the generation script, i.e. genDocs.sh, which will generate a docset and html format. 6 | 7 | ## Usage examples 8 | 9 | ### Batching draw calls using hierarchical trees 10 | 11 | Consider the following example, for simplicity it's in 2D space. 12 | ![rtree hierarchy](hierarchy.png) 13 | 14 | The leaves represent draw calls(eg. game objects) in world coordinates, they can be represented in the tree values by some IDs or indices in some arrays. 15 | 16 | #### Spatial order 17 | Insert the objects in any order, after a leaf traversal(via the leaf iterator) must be done which will give the spatial order of the objects: 18 | 19 | ![spatial order](spatial_order.png) 20 | 21 | NOTE: The spatial tree has a translate method which will be applied to the boxes of all the nodes and leaves. 22 | 23 | #### Buffer spatial ordered layout 24 | Afterwards using the spatial order we insert the object's data in a vertex buffer(if applicable, also to an index buffer or more vertex buffers) and save the draw call start and count for the objects: 25 | 26 | ![spatial calls](spatial_calls.png) 27 | 28 | NOTE: It's required that all calls must use the same primitive type. 29 | 30 | This way the interleaved data(or indices if using an index buffer) will be spatially ordered. 31 | 32 | #### Depth nodes 33 | The final step is to do a depth traversal(via the depth and node iterators) starting from the penultimate level towards the root level(eg. zero). 34 | For each depth node access it's children draw calls and append new ones, where the start is the minimum of it's children and count is the sum: 35 | 36 | ![spatial depth](spatial_depth.png) 37 | 38 | NOTE: The traversal is done in reverse, from the level one downards, in left to right order. 39 | 40 | We also set the new values(eg. an ID or address) of depth nodes which will link to the new draw calls. 41 | 42 | #### Hierachical query 43 | When using hierachical query the values/objects that are fully contained by the query box will be returned and the other branches pruned. 44 | 45 | ![spatial query](spatial_query.png) 46 | 47 | NOTE: The quad tree variant has a containment factor, if the number of nodes contained exceed the given percentage then the depth node is selected. 48 | 49 | In the above example the query would return the draw calls for: Q(composed of F and G), E, K and M. 50 | Note that further optimizations can be done for adjiacent nodes(as they can be merged). -------------------------------------------------------------------------------- /doc/genDocs.sh: -------------------------------------------------------------------------------- 1 | 2 | doxygen html.cfg 3 | doxygen docset.cfg 4 | #generate docset 5 | cd docset 6 | make 7 | for f in $(ls | grep -v ".docset"); do 8 | rm -rf $f 9 | done 10 | 11 | cd .. 12 | 13 | -------------------------------------------------------------------------------- /doc/hierarchy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuxalin/THST/4ff6aa575f87dce4321754981314a923b0b4bbb6/doc/hierarchy.png -------------------------------------------------------------------------------- /doc/spatial_calls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuxalin/THST/4ff6aa575f87dce4321754981314a923b0b4bbb6/doc/spatial_calls.png -------------------------------------------------------------------------------- /doc/spatial_depth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuxalin/THST/4ff6aa575f87dce4321754981314a923b0b4bbb6/doc/spatial_depth.png -------------------------------------------------------------------------------- /doc/spatial_order.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuxalin/THST/4ff6aa575f87dce4321754981314a923b0b4bbb6/doc/spatial_order.png -------------------------------------------------------------------------------- /doc/spatial_query.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuxalin/THST/4ff6aa575f87dce4321754981314a923b0b4bbb6/doc/spatial_query.png -------------------------------------------------------------------------------- /include/THST/QuadTree.h: -------------------------------------------------------------------------------- 1 | // 2 | // QuadTree.h 3 | // 4 | // 5 | 6 | #pragma once 7 | 8 | #include "allocator.h" 9 | #include "bbox.h" 10 | #include "config.h" 11 | #include "indexable.h" 12 | #include "predicates.h" 13 | #include "quad_tree_detail.h" 14 | #include 15 | 16 | namespace spatial { 17 | /** 18 | \class QuadTree 19 | \brief Implementation of a Quadtree for 2D space. 20 | 21 | It has the following properties: 22 | - hierarchical, you can add values to the internal nodes 23 | - custom indexable getter similar to boost's 24 | - custom allocation for internal nodes 25 | 26 | @tparam T type of the space(eg. int, float, etc.) 27 | @tparam ValueType type of value stored in the tree's nodes, requires a equality operator. 28 | @tparam max_child_items maximum number of items per node 29 | @tparam indexable_getter the indexable getter, i.e. the getter for the 30 | bounding box of a value 31 | @tparam custom_allocator the allocator class items exceeds the given factor 32 | then it'll use only the parent value 33 | 34 | @note It's recommended that ValueType should be a fast to copy object, eg: int, 35 | id, obj*, etc. 36 | */ 37 | template , 39 | typename custom_allocator = spatial::allocator< 40 | detail::QuadTreeNode>> 41 | class QuadTree { 42 | public: 43 | typedef BoundingBox bbox_type; 44 | typedef custom_allocator allocator_type; 45 | 46 | static const size_t max_items = max_child_items; 47 | 48 | private: 49 | typedef detail::QuadTreeNode node_type; 50 | typedef detail::QuadTreeObject object_type; 51 | typedef node_type *node_ptr_type; 52 | 53 | public: 54 | class base_iterator : public detail::QuadTreeStack { 55 | public: 56 | /// Returns the depth level of the current branch. 57 | int level() const; 58 | 59 | /// Is iterator still valid. 60 | bool valid() const; 61 | /// Access the current data element. Caller must be sure iterator is not 62 | /// NULL first. 63 | ValueType &operator*(); 64 | /// Access the current data element. Caller must be sure iterator is not 65 | /// NULL first. 66 | const ValueType &operator*() const; 67 | const bbox_type &bbox() const; 68 | 69 | protected: 70 | base_iterator(const int maxLevel); 71 | 72 | const int m_maxLevel; 73 | 74 | typedef detail::QuadTreeStack base_type; 75 | using base_type::m_stack; 76 | using base_type::m_tos; 77 | }; 78 | 79 | /** 80 | @brief Iterator to traverese the items of a node of the tree. 81 | */ 82 | struct node_iterator { 83 | node_iterator(); 84 | 85 | /// Returns the depth level of the current branch. 86 | int level() const; 87 | /// Is iterator still valid. 88 | bool valid() const; 89 | /// Access the current data element. Caller must be sure iterator is not 90 | /// NULL first. 91 | ValueType &operator*(); 92 | /// Access the current data element. Caller must be sure iterator is not 93 | /// NULL first. 94 | const ValueType &operator*() const; 95 | const bbox_type &bbox() const; 96 | /// Advances to the next item. 97 | void next(); 98 | 99 | private: 100 | node_iterator(node_ptr_type node, int maxLevel); 101 | node_iterator(node_ptr_type node, size_t childIndex, int maxLevel); 102 | 103 | const int m_maxLevel; 104 | size_t m_childIndex; 105 | int m_objectIndex; 106 | node_ptr_type m_node; 107 | 108 | friend class QuadTree; 109 | }; 110 | 111 | /** 112 | @brief Iterator to traverse the whole tree similar to depth first search. It 113 | reaches the lowest level 114 | (i.e. leaves) and afterwards visits the higher levels until it reaches the 115 | root(highest) level. 116 | */ 117 | class depth_iterator : public base_iterator { 118 | typedef base_iterator base_type; 119 | using base_type::m_stack; 120 | using base_type::m_tos; 121 | using base_type::push; 122 | using base_type::pop; 123 | 124 | public: 125 | // Returns a lower level, ie. child, node of the current branch. 126 | node_iterator child(); 127 | node_iterator current(); 128 | 129 | /// Access the current data element. Caller must be sure iterator is not 130 | /// NULL first. 131 | ValueType &operator*(); 132 | /// Access the current data element. Caller must be sure iterator is not 133 | /// NULL first. 134 | const ValueType &operator*() const; 135 | /// Advances to the next item. 136 | void next(); 137 | 138 | private: 139 | depth_iterator(const int maxLevel); 140 | friend class QuadTree; 141 | }; 142 | 143 | /** 144 | @brief Iterator to traverse only the leaves of the tree similar, left to 145 | right order. 146 | */ 147 | class leaf_iterator : public base_iterator { 148 | typedef base_iterator base_type; 149 | using base_type::m_stack; 150 | using base_type::m_tos; 151 | using base_type::push; 152 | using base_type::pop; 153 | 154 | public: 155 | /// Advances to the next item. 156 | void next(); 157 | 158 | private: 159 | leaf_iterator(const int maxLevel); 160 | friend class QuadTree; 161 | }; 162 | 163 | QuadTree(const T min[2], const T max[2], // 164 | indexable_getter indexable = indexable_getter(), // 165 | const allocator_type &allocator = allocator_type()); 166 | template 167 | QuadTree(const T min[2], const T max[2], // 168 | Iter first, Iter last, // 169 | indexable_getter indexable = indexable_getter(), // 170 | const allocator_type &allocator = allocator_type()); 171 | QuadTree(const QuadTree &src); 172 | #ifdef SPATIAL_TREE_USE_CPP11 173 | QuadTree(QuadTree &&src); 174 | #endif 175 | ~QuadTree(); 176 | 177 | QuadTree &operator=(const QuadTree &rhs); 178 | #ifdef SPATIAL_TREE_USE_CPP11 179 | QuadTree &operator=(QuadTree &&rhs) noexcept; 180 | #endif 181 | 182 | void swap(QuadTree &other); 183 | 184 | ///@note The tree must be empty before doing this. 185 | void setBox(const T min[2], const T max[2]); 186 | template void insert(Iter first, Iter last); 187 | void insert(const ValueType &value); 188 | bool remove(const ValueType& value); 189 | 190 | /// Translates the internal boxes with the given offset point. 191 | void translate(const T point[2]); 192 | 193 | /// Special query to find all within search rectangle using the hierarchical 194 | /// order. 195 | /// @see spatial::SpatialPredicate for available predicates. 196 | /// \return Returns the number of entries found. 197 | template 198 | size_t hierachical_query(const Predicate &predicate, OutIter out_it) const; 199 | ///@param containment_factor the containment factor in percentage used for 200 | /// query, if the total number of visible. 201 | ///@note Only used for hierarchical query. 202 | void setContainmentFactor(int factor); 203 | 204 | /// @see spatial::SpatialPredicate for available predicates. 205 | template bool query(const Predicate &predicate) const; 206 | template 207 | size_t query(const Predicate &predicate, OutIter out_it) const; 208 | 209 | /// Remove all entries from tree 210 | void clear(bool recursiveCleanup = true); 211 | /// Count the data elements in this container. 212 | size_t count() const; 213 | /// Returns the number of levels(height) of the tree. 214 | int levels() const; 215 | /// Returns the bbox of the root node. 216 | bbox_type bbox() const; 217 | /// Returns the custom allocator 218 | allocator_type &allocator(); 219 | const allocator_type &allocator() const; 220 | 221 | node_iterator root(); 222 | depth_iterator dbegin(); 223 | leaf_iterator lbegin(); 224 | 225 | private: 226 | indexable_getter m_indexable; 227 | mutable allocator_type m_allocator; 228 | 229 | size_t m_count; 230 | int m_levels; 231 | float m_factor; 232 | node_ptr_type m_root; 233 | }; 234 | 235 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////// 236 | 237 | #define TREE_TEMPLATE \ 238 | template 240 | 241 | #define TREE_QUAL \ 242 | QuadTree 243 | 244 | TREE_TEMPLATE 245 | TREE_QUAL::QuadTree(const T min[2], const T max[2], // 246 | indexable_getter indexable /*= indexable_getter()*/, 247 | const allocator_type &allocator /*= allocator_type()*/) 248 | : m_indexable(indexable), m_allocator(allocator), m_count(0), m_levels(0), 249 | m_factor(60), m_root(NULL) { 250 | SPATIAL_TREE_STATIC_ASSERT((max_child_items > 1), "Invalid child size!"); 251 | 252 | m_root = detail::allocate(m_allocator, 0); 253 | m_root->box.set(min, max); 254 | } 255 | 256 | TREE_TEMPLATE 257 | template 258 | TREE_QUAL::QuadTree(const T min[2], const T max[2], // 259 | Iter first, Iter last, // 260 | indexable_getter indexable /*= indexable_getter()*/, 261 | const allocator_type &allocator /*= allocator_type()*/) 262 | : m_indexable(indexable), m_allocator(allocator), m_count(0), m_levels(0), 263 | m_factor(60) { 264 | SPATIAL_TREE_STATIC_ASSERT((max_child_items > 1), "Invalid child size!"); 265 | 266 | m_root = detail::allocate(m_allocator, 0); 267 | m_root->box.set(min, max); 268 | 269 | insert(first, last); 270 | } 271 | 272 | TREE_TEMPLATE 273 | TREE_QUAL::QuadTree(const QuadTree &src) 274 | : m_indexable(src.m_indexable), m_allocator(src.m_allocator), 275 | m_count(src.m_count), m_levels(src.m_levels), m_factor(src.m_factor), 276 | m_root(detail::allocate(m_allocator, 0)) { 277 | m_root->copy(*src.m_root, m_allocator); 278 | } 279 | 280 | #ifdef SPATIAL_TREE_USE_CPP11 281 | TREE_TEMPLATE 282 | TREE_QUAL::QuadTree(QuadTree &&src) : m_root(NULL) { swap(src); } 283 | #endif 284 | 285 | TREE_TEMPLATE 286 | TREE_QUAL::~QuadTree() { 287 | if (m_root) 288 | m_root->clear(m_allocator); 289 | detail::deallocate(m_allocator, m_root); 290 | } 291 | 292 | TREE_TEMPLATE 293 | TREE_QUAL &TREE_QUAL::operator=(const QuadTree &rhs) { 294 | if (&rhs != this) { 295 | if (m_count > 0) 296 | clear(true); 297 | 298 | m_count = rhs.m_count; 299 | m_levels = rhs.m_levels; 300 | m_factor = rhs.m_factor; 301 | m_allocator = rhs.m_allocator; 302 | m_indexable = rhs.m_indexable; 303 | m_root->copy(*rhs.m_root, m_allocator); 304 | } 305 | return *this; 306 | } 307 | 308 | #ifdef SPATIAL_TREE_USE_CPP11 309 | TREE_TEMPLATE 310 | TREE_QUAL &TREE_QUAL::operator=(QuadTree &&rhs) noexcept { 311 | assert(this != &rhs); 312 | 313 | if (m_count > 0) 314 | clear(true); 315 | swap(rhs); 316 | 317 | return *this; 318 | } 319 | #endif 320 | 321 | TREE_TEMPLATE 322 | void TREE_QUAL::swap(QuadTree &other) { 323 | std::swap(m_root, other.m_root); 324 | std::swap(m_count, other.m_count); 325 | std::swap(m_factor, other.m_factor); 326 | std::swap(m_levels, other.m_levels); 327 | std::swap(m_allocator, other.m_allocator); 328 | std::swap(m_indexable, other.m_indexable); 329 | } 330 | 331 | TREE_TEMPLATE 332 | void TREE_QUAL::setBox(const T min[2], const T max[2]) { 333 | assert(m_root); 334 | assert(m_count == 0); 335 | m_root->box.set(min, max); 336 | } 337 | 338 | TREE_TEMPLATE 339 | template void TREE_QUAL::insert(Iter first, Iter last) { 340 | assert(m_root); 341 | 342 | bool success = true; 343 | for (Iter it = first; it != last; ++it) { 344 | const object_type obj(*it, m_indexable); 345 | assert(m_root->box.contains(obj.box)); 346 | success &= m_root->insert(obj, m_levels, m_allocator); 347 | ++m_count; 348 | } 349 | (void)(success); 350 | assert(success); 351 | } 352 | 353 | TREE_TEMPLATE 354 | void TREE_QUAL::insert(const ValueType &value) { 355 | assert(m_root); 356 | 357 | const object_type obj(value, m_indexable); 358 | assert(m_root->box.contains(obj.box)); 359 | 360 | bool success = m_root->insert(obj, m_levels, m_allocator); 361 | if (success) 362 | ++m_count; 363 | assert(m_count == m_root->count()); 364 | } 365 | 366 | TREE_TEMPLATE 367 | bool TREE_QUAL::remove(const ValueType& value) { 368 | assert(m_root); 369 | 370 | const object_type obj(value, m_indexable); 371 | assert(m_root->box.contains(obj.box)); 372 | 373 | const bool success = m_root->remove(obj, m_allocator); 374 | if (success) 375 | --m_count; 376 | 377 | return success; 378 | } 379 | 380 | TREE_TEMPLATE 381 | void TREE_QUAL::translate(const T point[2]) { m_root->translate(point); } 382 | 383 | TREE_TEMPLATE 384 | template 385 | size_t TREE_QUAL::hierachical_query(const Predicate &predicate, 386 | OutIter out_it) const { 387 | assert(m_root); 388 | return m_root->queryHierachical(predicate, m_factor, out_it); 389 | } 390 | 391 | TREE_TEMPLATE 392 | void TREE_QUAL::setContainmentFactor(int factor) { m_factor = factor / 100.f; } 393 | 394 | TREE_TEMPLATE 395 | template 396 | bool TREE_QUAL::query(const Predicate &predicate) const { 397 | return query(predicate, spatial::detail::dummy_iterator()) > 0; 398 | } 399 | 400 | TREE_TEMPLATE 401 | template 402 | size_t TREE_QUAL::query(const Predicate &predicate, OutIter out_it) const { 403 | assert(m_root); 404 | return m_root->query(predicate, m_factor, out_it); 405 | } 406 | 407 | TREE_TEMPLATE 408 | void TREE_QUAL::clear(bool recursiveCleanup /*= true*/) { 409 | 410 | #if SPATIAL_TREE_ALLOCATOR == SPATIAL_TREE_DEFAULT_ALLOCATOR 411 | if (allocator_type::is_overflowable) 412 | recursiveCleanup &= !m_allocator.overflowed(); 413 | #endif 414 | 415 | // Delete all existing nodes 416 | if (recursiveCleanup) { 417 | if (m_root) 418 | m_root->clear(m_allocator); 419 | } 420 | assert(m_root != nullptr); 421 | const bbox_type box = m_root->box; 422 | detail::deallocate(m_allocator, m_root); 423 | 424 | m_root = detail::allocate(m_allocator, 0); 425 | m_root->box = box; 426 | m_levels = m_count = 0u; 427 | } 428 | 429 | TREE_TEMPLATE 430 | size_t TREE_QUAL::count() const { 431 | assert(m_root); 432 | assert(m_root->count() == m_count); 433 | return m_count; 434 | } 435 | 436 | TREE_TEMPLATE 437 | int TREE_QUAL::levels() const { 438 | assert(m_root); 439 | return m_levels; 440 | } 441 | 442 | TREE_TEMPLATE 443 | typename TREE_QUAL::bbox_type TREE_QUAL::bbox() const { 444 | assert(m_root); 445 | return m_root->box; 446 | } 447 | 448 | TREE_TEMPLATE 449 | typename TREE_QUAL::allocator_type &TREE_QUAL::allocator() { 450 | return m_allocator; 451 | } 452 | 453 | TREE_TEMPLATE 454 | const typename TREE_QUAL::allocator_type &TREE_QUAL::allocator() const { 455 | return m_allocator; 456 | } 457 | 458 | TREE_TEMPLATE 459 | typename TREE_QUAL::node_iterator TREE_QUAL::root() { 460 | assert(m_root); 461 | return node_iterator(m_root, m_levels); 462 | } 463 | 464 | TREE_TEMPLATE 465 | typename TREE_QUAL::depth_iterator TREE_QUAL::dbegin() { 466 | assert(m_root); 467 | depth_iterator it(m_levels); 468 | 469 | it.push(m_root, 0, -1); 470 | for (;;) { 471 | if (it.m_tos <= 0) 472 | break; 473 | 474 | // Copy stack top cause it may change as we use it 475 | const typename depth_iterator::StackElement curTos = it.pop(); 476 | 477 | if (curTos.node->isLeaf()) { 478 | if (curTos.node->objectCount()) { 479 | // Visit leaf once 480 | it.push(curTos.node, 0); 481 | break; 482 | } 483 | // fallback to previous 484 | } 485 | else { 486 | // Keep walking through children while we can 487 | if (curTos.childIndex + 1 < 4) { 488 | // Push sibling on for future tree walk 489 | // This is the 'fall back' node when we finish with the current level 490 | it.push(curTos.node, curTos.childIndex + 1, 0); 491 | } 492 | else { 493 | // Push current node on for future objects walk 494 | it.push(curTos.node, 4, 0); 495 | } 496 | 497 | // Since cur node is not a leaf, push first of next level to get deeper 498 | // into the tree 499 | node_ptr_type nextLevelnode = curTos.node->children[curTos.childIndex]; 500 | if (nextLevelnode) { 501 | it.push(nextLevelnode, 0, 0); 502 | } 503 | } 504 | } 505 | return it; 506 | } 507 | 508 | TREE_TEMPLATE 509 | typename TREE_QUAL::leaf_iterator TREE_QUAL::lbegin() { 510 | assert(m_root); 511 | leaf_iterator it(m_levels); 512 | 513 | it.push(m_root, 0, -1); 514 | for (;;) { 515 | if (it.m_tos <= 0) 516 | break; 517 | 518 | // Copy stack top cause it may change as we use it 519 | const typename leaf_iterator::StackElement curTos = it.pop(); 520 | 521 | if (curTos.node->isLeaf()) { 522 | if (curTos.node->objectCount()) { 523 | it.push(curTos.node, 0); 524 | break; 525 | } 526 | // Empty node, fallback to previous 527 | } 528 | else { 529 | // Keep walking through children while we can 530 | if (curTos.childIndex + 1 < 4) { 531 | // Push sibling on for future tree walk 532 | // This is the 'fall back' node when we finish with the current level 533 | it.push(curTos.node, curTos.childIndex + 1, -1); 534 | } 535 | else { 536 | // Push current node on for future objects walk 537 | it.push(curTos.node, 4, -1); 538 | } 539 | 540 | // Since cur node is not a leaf, push first of next level to get deeper 541 | // into the tree 542 | node_ptr_type nextLevelnode = curTos.node->children[curTos.childIndex]; 543 | if (nextLevelnode) { 544 | it.push(nextLevelnode, 0, -1); 545 | } 546 | } 547 | } 548 | return it; 549 | } 550 | 551 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////// 552 | 553 | TREE_TEMPLATE 554 | TREE_QUAL::base_iterator::base_iterator(const int maxLevel) 555 | : m_maxLevel(maxLevel) {} 556 | 557 | TREE_TEMPLATE 558 | int TREE_QUAL::base_iterator::level() const { 559 | assert(m_tos > 0); 560 | const typename base_type::StackElement &curTos = m_stack[m_tos - 1]; 561 | return m_maxLevel - curTos.node->level; 562 | } 563 | 564 | TREE_TEMPLATE 565 | bool TREE_QUAL::base_iterator::valid() const { return (m_tos > 0); } 566 | 567 | TREE_TEMPLATE 568 | ValueType &TREE_QUAL::base_iterator::operator*() { 569 | assert(valid()); 570 | typename base_type::StackElement &curTos = m_stack[m_tos - 1]; 571 | return curTos.node->objectValue(curTos.objectIndex); 572 | } 573 | 574 | TREE_TEMPLATE 575 | const ValueType &TREE_QUAL::base_iterator::operator*() const { 576 | assert(valid()); 577 | typename base_type::StackElement &curTos = m_stack[m_tos - 1]; 578 | return curTos.node->objectValue(curTos.objectIndex); 579 | } 580 | 581 | TREE_TEMPLATE 582 | const typename TREE_QUAL::bbox_type &TREE_QUAL::base_iterator::bbox() const { 583 | assert(valid()); 584 | typename base_type::StackElement &curTos = m_stack[m_tos - 1]; 585 | return curTos.node->children[curTos.childIndex]->bbox; 586 | } 587 | 588 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////// 589 | 590 | TREE_TEMPLATE 591 | TREE_QUAL::node_iterator::node_iterator() 592 | : m_maxLevel(0), m_childIndex(0), m_objectIndex(0), m_node(NULL) {} 593 | 594 | TREE_TEMPLATE 595 | TREE_QUAL::node_iterator::node_iterator(node_ptr_type node, size_t childIndex, 596 | int maxLevel) 597 | : m_maxLevel(maxLevel), m_childIndex(childIndex), 598 | m_objectIndex(node->isLeaf() ? 0 : -1), m_node(node) { 599 | assert(node); 600 | 601 | while (m_childIndex < 4 && m_node->children[m_childIndex]->isEmpty()) 602 | ++m_childIndex; 603 | if (m_childIndex >= 4) 604 | m_objectIndex = 0; 605 | } 606 | 607 | TREE_TEMPLATE 608 | TREE_QUAL::node_iterator::node_iterator(node_ptr_type node, int maxLevel) 609 | : m_maxLevel(maxLevel), m_childIndex(node->isLeaf() ? 4 : 0), 610 | m_objectIndex(node->isLeaf() ? 0 : -1), m_node(node) { 611 | assert(node); 612 | 613 | while (m_childIndex < 4 && m_node->children[m_childIndex]->isEmpty()) 614 | ++m_childIndex; 615 | if (m_childIndex >= 4) 616 | m_objectIndex = 0; 617 | } 618 | 619 | TREE_TEMPLATE 620 | int TREE_QUAL::node_iterator::level() const { 621 | assert(m_node); 622 | return m_maxLevel - m_node->level; 623 | } 624 | 625 | TREE_TEMPLATE 626 | bool TREE_QUAL::node_iterator::valid() const { 627 | assert(m_node); 628 | return (m_objectIndex < (int)m_node->objectCount()); 629 | } 630 | 631 | TREE_TEMPLATE 632 | ValueType &TREE_QUAL::node_iterator::operator*() { 633 | assert(valid()); 634 | if (m_childIndex < 4) { 635 | assert(m_node->isBranch()); 636 | return m_node->children[m_childIndex]->value; 637 | } 638 | else 639 | return m_node->objectValue(m_objectIndex); 640 | } 641 | 642 | TREE_TEMPLATE 643 | const ValueType &TREE_QUAL::node_iterator::operator*() const { 644 | assert(valid()); 645 | if (m_childIndex < 4) { 646 | assert(m_node->isBranch()); 647 | return m_node->children[m_childIndex]->value; 648 | } 649 | else 650 | return m_node->objectValue(m_objectIndex); 651 | } 652 | 653 | TREE_TEMPLATE 654 | const typename TREE_QUAL::bbox_type &TREE_QUAL::node_iterator::bbox() const { 655 | return m_node->box; 656 | } 657 | 658 | TREE_TEMPLATE 659 | void TREE_QUAL::node_iterator::next() { 660 | assert(valid()); 661 | if (m_childIndex < 4 && m_node->isBranch()) { 662 | ++m_childIndex; 663 | while (m_childIndex < 4 && m_node->children[m_childIndex]->isEmpty()) 664 | ++m_childIndex; 665 | 666 | if (m_childIndex < 4) 667 | // found a children which is not empty 668 | return; 669 | } 670 | ++m_objectIndex; 671 | } 672 | 673 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////// 674 | 675 | TREE_TEMPLATE 676 | TREE_QUAL::depth_iterator::depth_iterator(const int maxLevel) 677 | : base_type(maxLevel) {} 678 | 679 | TREE_TEMPLATE 680 | ValueType &TREE_QUAL::depth_iterator::operator*() { 681 | assert(!base_type::valid()); 682 | typename base_type::StackElement &curTos = m_stack[m_tos - 1]; 683 | return curTos.node->value; 684 | } 685 | 686 | TREE_TEMPLATE 687 | const ValueType &TREE_QUAL::depth_iterator::operator*() const { 688 | assert(!base_type::valid()); 689 | typename base_type::StackElement &curTos = m_stack[m_tos - 1]; 690 | return curTos.node->value; 691 | } 692 | 693 | TREE_TEMPLATE 694 | void TREE_QUAL::depth_iterator::next() { 695 | for (;;) { 696 | if (m_tos <= 0) 697 | break; 698 | 699 | // Copy stack top cause it may change as we use it 700 | const typename base_type::StackElement curTos = pop(); 701 | 702 | if (curTos.node->isLeaf()) { 703 | // Reached leaves, fall back to previous level 704 | } 705 | else { 706 | // Keep walking through children while we can 707 | if (curTos.childIndex + 1 < 4) { 708 | // Push sibling on for future tree walk 709 | // This is the 'fall back' node when we finish with the current level 710 | push(curTos.node, curTos.childIndex + 1, 0); 711 | } 712 | else { 713 | if (curTos.childIndex == 3) { 714 | // Visit node after visiting the last child 715 | push(curTos.node, 4, 0); 716 | } 717 | else if (curTos.childIndex == 4) { 718 | assert(!curTos.node->isEmpty()); 719 | push(curTos.node, 5, 0); 720 | break; 721 | } 722 | else if (curTos.childIndex > 4) 723 | // Node visited, fallback to previous 724 | continue; 725 | } 726 | 727 | // Since cur node is not a leaf, push first of next level to get deeper 728 | // into the tree 729 | node_ptr_type nextLevelnode = curTos.node->children[curTos.childIndex]; 730 | if (nextLevelnode->isLeaf()) { 731 | push(nextLevelnode, 0, 0); 732 | if (nextLevelnode->objectCount()) { 733 | // If we pushed on a new leaf, exit as the data is ready at TOS 734 | break; 735 | } 736 | } 737 | else { 738 | push(nextLevelnode, 0, 0); 739 | } 740 | } 741 | } 742 | } 743 | 744 | TREE_TEMPLATE 745 | typename TREE_QUAL::node_iterator TREE_QUAL::depth_iterator::child() { 746 | assert(m_tos > 0); 747 | typename base_type::StackElement &curTos = m_stack[m_tos - 1]; 748 | return node_iterator(curTos.node, base_type::m_maxLevel); 749 | } 750 | 751 | TREE_TEMPLATE 752 | typename TREE_QUAL::node_iterator TREE_QUAL::depth_iterator::current() { 753 | assert(m_tos > 0); 754 | typename base_type::StackElement &curTos = m_stack[m_tos - 1]; 755 | return node_iterator(curTos.node, 4, base_type::m_maxLevel); 756 | } 757 | 758 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////// 759 | 760 | TREE_TEMPLATE 761 | TREE_QUAL::leaf_iterator::leaf_iterator(const int maxLevel) 762 | : base_type(maxLevel) {} 763 | 764 | TREE_TEMPLATE 765 | void TREE_QUAL::leaf_iterator::next() { 766 | for (;;) { 767 | if (m_tos <= 0) 768 | break; 769 | 770 | // Copy stack top cause it may change as we use it 771 | const typename base_type::StackElement curTos = pop(); 772 | 773 | assert(curTos.objectIndex >= -1); 774 | if (size_t(curTos.objectIndex + 1) < curTos.node->objectCount()) { 775 | // Keep walking through data while we can 776 | push(curTos.node, curTos.objectIndex + 1); 777 | break; 778 | } 779 | 780 | if (curTos.node->isLeaf()) { 781 | // reached leaves, fall back to previous level 782 | } 783 | else { 784 | // Keep walking through children while we can 785 | if (curTos.childIndex + 1 < 4) { 786 | // Push sibling on for future tree walk 787 | // This is the 'fall back' node when we finish with the current level 788 | push(curTos.node, curTos.childIndex + 1, curTos.objectIndex); 789 | } 790 | else if (curTos.childIndex >= 4) 791 | // Node visited, fallback to previous 792 | continue; 793 | 794 | // Since cur node is not a leaf, push first of next level to get deeper 795 | // into the tree 796 | node_ptr_type nextLevelnode = curTos.node->children[curTos.childIndex]; 797 | if (nextLevelnode->isLeaf()) { 798 | push(nextLevelnode, 0, 0); 799 | if (nextLevelnode->objectCount()) { 800 | // If we pushed on a new leaf, exit as the data is ready at TOS 801 | break; 802 | } 803 | } 804 | else { 805 | push(nextLevelnode, 0, -1); 806 | } 807 | } 808 | } 809 | } 810 | 811 | #undef TREE_TEMPLATE 812 | #undef TREE_QUAL 813 | 814 | } // namespace spatial 815 | -------------------------------------------------------------------------------- /include/THST/allocator.h: -------------------------------------------------------------------------------- 1 | // 2 | // allocator.h 3 | // 4 | // 5 | 6 | #pragma once 7 | 8 | #include "config.h" 9 | 10 | #if SPATIAL_TREE_ALLOCATOR == SPATIAL_TREE_DEFAULT_ALLOCATOR 11 | #include 12 | #include 13 | #elif SPATIAL_TREE_ALLOCATOR == SPATIAL_TREE_STD_ALLOCATOR 14 | #include 15 | #endif 16 | 17 | namespace spatial { 18 | 19 | #if SPATIAL_TREE_ALLOCATOR == SPATIAL_TREE_DEFAULT_ALLOCATOR 20 | 21 | /// A simplified heap allocator. 22 | template struct allocator { 23 | typedef NodeClass value_type; 24 | 25 | /// @note If true then overflow checks will be performed. 26 | enum { is_overflowable = 0 }; 27 | 28 | value_type *allocate(int level) { return new NodeClass(level); } 29 | 30 | void deallocate(const value_type *node) { 31 | assert(node); 32 | delete node; 33 | } 34 | 35 | /// If true then an overflowed has occurred. 36 | /// @note Only used if is_overflowable is true. 37 | bool overflowed() const { return false; } 38 | }; 39 | 40 | #elif SPATIAL_TREE_ALLOCATOR == SPATIAL_TREE_STD_ALLOCATOR 41 | 42 | using std::allocator; 43 | #else 44 | #error "Unknown allocator type!" 45 | #endif 46 | } // namespace spatial 47 | -------------------------------------------------------------------------------- /include/THST/bbox.h: -------------------------------------------------------------------------------- 1 | // 2 | // bbox.h 3 | // 4 | // 5 | 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace spatial { 14 | namespace box { 15 | enum VolumeMode { 16 | eNormalVolume = 0, // Faster but can cause poor merges 17 | eSphericalVolume // Slower but helps certain merge cases 18 | }; 19 | 20 | enum RegionType { 21 | eNW = 0, ///< North-West (Top left) 22 | eNE, ///< North-East (Top right) 23 | eSW, ///< South-West (Bottom left) 24 | eSE ///< South-East (Bottom right) 25 | }; 26 | 27 | /// Initialize a bounding box with an empty box. 28 | struct empty_init {}; 29 | } // namespace spatial 30 | 31 | /// Minimal bounding bbox (n-dimensional) 32 | template struct BoundingBox { 33 | 34 | T min[Dimension]; 35 | T max[Dimension]; 36 | 37 | BoundingBox(); 38 | BoundingBox(box::empty_init); 39 | BoundingBox(const T min[Dimension], const T max[Dimension]); 40 | 41 | void extend(const T point[Dimension]); 42 | void extend(const BoundingBox &bbox); 43 | BoundingBox extended(const BoundingBox &bbox) const; 44 | void set(const T min[Dimension], const T max[Dimension]); 45 | void translate(const T point[Dimension]); 46 | 47 | bool overlaps(const BoundingBox &bbox) const; 48 | bool overlaps(const T point[Dimension], T radius) const; 49 | bool contains(const BoundingBox &bbox) const; 50 | bool contains(const T point[Dimension]) const; 51 | 52 | template 53 | bool intersectsRay(const RealType rayOrigin[Dimension], const RealType rayDirection[Dimension]) const; 54 | 55 | float distance(const T point[Dimension]) const; 56 | T distanceSquare(const T point[Dimension]) const; 57 | 58 | void center(T center[Dimension]) const; 59 | 60 | template RealType volume() const; 61 | BoundingBox quad2d(box::RegionType type) const; 62 | 63 | private: 64 | void checkValid() const; 65 | 66 | template RealType normalVolume() const; 67 | // The exact volume of the bounding sphere for the given bbox. 68 | template RealType sphericalVolume() const; 69 | /// Unit sphere constant for required number of dimensions 70 | template static RealType unitSphereVolume(); 71 | }; 72 | 73 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////// 74 | 75 | namespace detail { 76 | /// C++03 backward compatibility 77 | /// http://en.cppreference.com/w/cpp/types/numeric_limits/lowest 78 | template struct NumericLimitsImpl; 79 | 80 | template struct NumericLimitsImpl { 81 | static const T highest() { return std::numeric_limits::max(); } 82 | 83 | static const T lowest() { return -std::numeric_limits::max(); } 84 | }; 85 | 86 | template struct NumericLimitsImpl { 87 | static const T highest() { return std::numeric_limits::max(); } 88 | 89 | static const T lowest() { return std::numeric_limits::min(); } 90 | }; 91 | 92 | template 93 | struct NumericLimits 94 | : public NumericLimitsImpl::is_integer> {}; 95 | } // namespace detail 96 | 97 | #define BBOX_TEMPLATE template 98 | #define BBOX_QUAL BoundingBox 99 | 100 | BBOX_TEMPLATE 101 | BBOX_QUAL::BoundingBox() {} 102 | 103 | BBOX_TEMPLATE 104 | BBOX_QUAL::BoundingBox(const T min[Dimension], const T max[Dimension]) { 105 | set(min, max); 106 | } 107 | 108 | BBOX_TEMPLATE 109 | BBOX_QUAL::BoundingBox(box::empty_init) { 110 | for (size_t index = 0; index < (size_t)Dimension; ++index) { 111 | min[index] = spatial::detail::NumericLimits::highest(); 112 | max[index] = spatial::detail::NumericLimits::lowest(); 113 | } 114 | } 115 | 116 | BBOX_TEMPLATE 117 | void BBOX_QUAL::set(const T min[Dimension], const T max[Dimension]) { 118 | // loop will get unrolled 119 | for (int axis = 0; axis < Dimension; ++axis) { 120 | this->min[axis] = min[axis]; 121 | this->max[axis] = max[axis]; 122 | } 123 | checkValid(); 124 | } 125 | 126 | BBOX_TEMPLATE 127 | void BBOX_QUAL::translate(const T point[Dimension]) { 128 | // loop will get unrolled 129 | for (int axis = 0; axis < Dimension; ++axis) { 130 | this->min[axis] += point[axis]; 131 | this->max[axis] += point[axis]; 132 | } 133 | checkValid(); 134 | } 135 | 136 | BBOX_TEMPLATE 137 | void BBOX_QUAL::extend(const T point[Dimension]) { 138 | // loop will get unrolled 139 | for (int index = 0; index < Dimension; ++index) { 140 | min[index] = std::min(min[index], point[index]); 141 | max[index] = std::max(max[index], point[index]); 142 | } 143 | checkValid(); 144 | } 145 | 146 | BBOX_TEMPLATE 147 | void BBOX_QUAL::extend(const BoundingBox &bbox) { 148 | bbox.checkValid(); 149 | // loop will get unrolled 150 | for (int index = 0; index < Dimension; ++index) { 151 | min[index] = std::min(min[index], bbox.min[index]); 152 | max[index] = std::max(max[index], bbox.max[index]); 153 | } 154 | checkValid(); 155 | } 156 | 157 | BBOX_TEMPLATE 158 | BBOX_QUAL 159 | BBOX_QUAL::extended(const BoundingBox &obbox) const { 160 | checkValid(); 161 | 162 | BoundingBox res; 163 | // loop will get unrolled 164 | for (int index = 0; index < Dimension; ++index) { 165 | res.min[index] = std::min(min[index], obbox.min[index]); 166 | res.max[index] = std::max(max[index], obbox.max[index]); 167 | } 168 | return res; 169 | } 170 | 171 | BBOX_TEMPLATE 172 | bool BBOX_QUAL::overlaps(const BoundingBox &bbox) const { 173 | // loop will get unrolled 174 | for (int index = 0; index < Dimension; ++index) { 175 | if (min[index] > bbox.max[index] || bbox.min[index] > max[index]) { 176 | return false; 177 | } 178 | } 179 | return true; 180 | } 181 | 182 | 183 | BBOX_TEMPLATE 184 | template 185 | bool BBOX_QUAL::intersectsRay(const RealType rayOrigin[Dimension], const RealType rayDirection[Dimension]) const { 186 | // loop should get unrolled 187 | RealType tMin = -std::numeric_limits::max(); 188 | RealType tMax = std::numeric_limits::max(); 189 | for (int index = 0; index < Dimension; ++index) { 190 | 191 | // check if parallel 192 | if (rayDirection[index] == (RealType)0) 193 | { 194 | if(rayOrigin[index] < min[index] || rayOrigin[index] > max[index]) 195 | return false; 196 | 197 | continue; 198 | } 199 | 200 | const RealType invDir =(RealType)1.0f / rayDirection[index]; 201 | RealType deltaMin = (min[index] - rayOrigin[index]) * invDir; 202 | RealType deltaMax = (max[index] - rayOrigin[index]) * invDir; 203 | 204 | if (deltaMin > deltaMax) 205 | std::swap(deltaMin, deltaMax); 206 | 207 | if ((tMin > deltaMax) || (deltaMin > tMax)) 208 | return false; 209 | 210 | if (deltaMin > tMin) 211 | tMin = deltaMin; 212 | if (deltaMax < tMax) 213 | tMax = deltaMax; 214 | } 215 | return true; 216 | } 217 | 218 | BBOX_TEMPLATE 219 | bool BBOX_QUAL::overlaps(const T center[Dimension], T radius) const { 220 | T point[Dimension]; 221 | for (int index = 0; index < Dimension; ++index) 222 | point[index] = center[index]; 223 | 224 | // find the closest point near the circle 225 | for (int index = 0; index < Dimension; ++index) { 226 | if (point[index] > max[index]) 227 | point[index] = max[index]; 228 | if (point[index] < min[index]) 229 | point[index] = min[index]; 230 | } 231 | 232 | // compute euclidean distance between points 233 | double distance = 0; 234 | for (int index = 0; index < Dimension; ++index) { 235 | const T d = point[index] - center[index]; 236 | distance += (double)d * d; 237 | } 238 | return distance < (radius * radius); // avoid square root 239 | } 240 | 241 | BBOX_TEMPLATE 242 | bool BBOX_QUAL::contains(const T point[Dimension]) const { 243 | // loop will get unrolled 244 | for (int index = 0; index < Dimension; ++index) { 245 | if (min[index] > point[index] || point[index] > max[index]) { 246 | return false; 247 | } 248 | } 249 | return true; 250 | } 251 | 252 | BBOX_TEMPLATE 253 | bool BBOX_QUAL::contains(const BoundingBox &bbox) const { 254 | // loop will get unrolled 255 | for (int index = 0; index < Dimension; ++index) { 256 | if (min[index] > bbox.min[index] || bbox.max[index] > max[index]) { 257 | return false; 258 | } 259 | } 260 | return true; 261 | } 262 | 263 | BBOX_TEMPLATE 264 | void BBOX_QUAL::center(T center[Dimension]) const { 265 | for (int i = 0; i < Dimension; ++i) { 266 | center[i] = min[i] + (max[i] - min[i]) / 2; 267 | } 268 | } 269 | 270 | BBOX_TEMPLATE 271 | T BBOX_QUAL::distanceSquare(const T point[Dimension]) const { 272 | 273 | T d = std::max(std::max(min[0] - point[0], (T)0), point[0] - max[0]); 274 | d *= d; 275 | for (int i = 1; i < Dimension; i++) 276 | { 277 | T temp = std::max(std::max(min[i] - point[i], (T)0), point[i] - max[i]); 278 | d += temp * temp; 279 | } 280 | 281 | return d; 282 | } 283 | 284 | BBOX_TEMPLATE 285 | float BBOX_QUAL::distance(const T point[Dimension]) const { 286 | 287 | return std::sqrt(float(distanceSquare(point))); 288 | } 289 | 290 | BBOX_TEMPLATE 291 | template 292 | RealType BBOX_QUAL::volume() const { 293 | if (VolumeMode == spatial::box::eSphericalVolume) 294 | return sphericalVolume(); 295 | 296 | return normalVolume(); 297 | } 298 | 299 | BBOX_TEMPLATE 300 | BBOX_QUAL BBOX_QUAL::quad2d(box::RegionType type) const { 301 | const T halfW = (max[0] - min[0]) / 2; 302 | const T halfH = (max[1] - min[1]) / 2; 303 | switch (type) { 304 | case box::eNW: { 305 | const T hmin[Dimension] = { min[0], min[1] + halfH }; 306 | const T hmax[Dimension] = { min[0] + halfW, max[1] }; 307 | return BoundingBox(hmin, hmax); 308 | } 309 | case box::eNE: { 310 | const T hmin[Dimension] = { min[0] + halfW, min[1] + halfH }; 311 | return BoundingBox(hmin, max); 312 | } 313 | case box::eSW: { 314 | const T hmax[Dimension] = { min[0] + halfW, min[1] + halfH }; 315 | return BoundingBox(min, hmax); 316 | } 317 | case box::eSE: { 318 | const T hmin[Dimension] = { min[0] + halfW, min[1] }; 319 | const T hmax[Dimension] = { max[0], min[1] + halfH }; 320 | return BoundingBox(hmin, hmax); 321 | } 322 | default: 323 | assert(false); 324 | return *this; 325 | } 326 | } 327 | 328 | BBOX_TEMPLATE 329 | void BBOX_QUAL::checkValid() const { 330 | #ifndef NDEBUG 331 | for (int index = 0; index < Dimension; ++index) { 332 | assert(min[index] <= max[index]); 333 | } 334 | #endif 335 | } 336 | 337 | BBOX_TEMPLATE 338 | template RealType BBOX_QUAL::normalVolume() const { 339 | RealType volume = (RealType)1; 340 | 341 | for (int index = 0; index < Dimension; ++index) { 342 | volume *= max[index] - min[index]; 343 | } 344 | 345 | assert(volume >= (RealType)0); 346 | return volume; 347 | } 348 | 349 | // The exact volume of the bounding sphere for the given bbox 350 | BBOX_TEMPLATE 351 | template RealType BBOX_QUAL::sphericalVolume() const { 352 | RealType sumOfSquares = (RealType)0; 353 | 354 | for (int index = 0; index < Dimension; ++index) { 355 | RealType halfExtent = (max[index] - min[index]) * (RealType)0.5; 356 | sumOfSquares += halfExtent * halfExtent; 357 | } 358 | 359 | RealType radius = (RealType)std::sqrt(sumOfSquares); 360 | 361 | // Pow maybe slow, so test for common dims like 2,3 and just use x*x, x*x*x. 362 | if (Dimension == 3) { 363 | return (radius * radius * radius * unitSphereVolume()); 364 | } 365 | else if (Dimension == 2) { 366 | return (radius * radius * unitSphereVolume()); 367 | } 368 | else { 369 | return (RealType)(pow(radius, Dimension) * unitSphereVolume()); 370 | } 371 | } 372 | 373 | BBOX_TEMPLATE 374 | template RealType BBOX_QUAL::unitSphereVolume() { 375 | // Precomputed volumes of the unit spheres for the first few dimensions 376 | static const float kVolumes[] = { 377 | 0.000000f, 2.000000f, 3.141593f, // Dimension 0,1,2 378 | 4.188790f, 4.934802f, 5.263789f, // Dimension 3,4,5 379 | 5.167713f, 4.724766f, 4.058712f, // Dimension 6,7,8 380 | 3.298509f, 2.550164f, 1.884104f, // Dimension 9,10,11 381 | 1.335263f, 0.910629f, 0.599265f, // Dimension 12,13,14 382 | 0.381443f, 0.235331f, 0.140981f, // Dimension 15,16,17 383 | 0.082146f, 0.046622f, 0.025807f, // Dimension 18,19,20 384 | }; 385 | static const RealType val = (RealType)kVolumes[Dimension]; 386 | return val; 387 | } // namespace box 388 | 389 | template 390 | std::ostream &operator<<(std::ostream &stream, const BoundingBox &bbox) { 391 | stream << "min: " << bbox.min[0] << " " << bbox.min[1] << " "; 392 | stream << "max: " << bbox.max[0] << " " << bbox.max[1]; 393 | return stream; 394 | } 395 | 396 | template 397 | std::ostream &operator<<(std::ostream &stream, const BoundingBox &bbox) { 398 | stream << "min: " << bbox.min[0] << " " << bbox.min[1] << " " << bbox.min[2] 399 | << " "; 400 | stream << "max: " << bbox.max[0] << " " << bbox.max[1] << " " << bbox.max[2] 401 | << " "; 402 | return stream; 403 | } 404 | 405 | #undef BBOX_TEMPLATE 406 | #undef BBOX_QUAL 407 | } // namespace spatial 408 | -------------------------------------------------------------------------------- /include/THST/config.h: -------------------------------------------------------------------------------- 1 | // 2 | // config.h 3 | // 4 | // 5 | 6 | #pragma once 7 | 8 | #if defined(_MSC_VER) 9 | #if _MSC_VER >= 1800 10 | #define SPATIAL_TREE_USE_CPP11 11 | #endif 12 | #elif __cplusplus >= 201103L 13 | #define SPATIAL_TREE_USE_CPP11 14 | #endif 15 | 16 | #define SPATIAL_TREE_STD_ALLOCATOR 1 17 | #define SPATIAL_TREE_DEFAULT_ALLOCATOR 2 18 | 19 | #ifndef SPATIAL_TREE_ALLOCATOR 20 | #define SPATIAL_TREE_ALLOCATOR 2 21 | #endif 22 | 23 | /// Preprocessor helper macros 24 | #define SPATIAL_TREE_STRINGIFY(x) #x 25 | #define SPATIAL_TREE_TOSTRING(x) SPATIAL_TREE_STRINGIFY(x) 26 | #define SPATIAL_TREE_TOKENPASTE(x, y) x##y 27 | #define SPATIAL_TREE_TOKENPASTE2(x, y) SPATIAL_TREE_TOKENPASTE(x, y) 28 | 29 | #ifdef SPATIAL_TREE_CPP11 30 | // we have C++11 so use built in static assert 31 | #define SPATIAL_TREE_STATIC_ASSERT(cond, msg) static_assert(cond, msg) 32 | #else 33 | 34 | #define SPATIAL_TREE_STATIC_ASSERT(cond, msg) \ 35 | spatial::detail::static_assertion<(cond)> SPATIAL_TREE_TOKENPASTE2( \ 36 | static_assert_t, __LINE__); \ 37 | (void)(SPATIAL_TREE_TOKENPASTE2(static_assert_t, __LINE__)); 38 | 39 | #if defined(__clang__) 40 | #if __has_attribute(enable_if) && !defined(NDEBUG) 41 | // use enable_if extension on clang to print the assert message 42 | #undef SPATIAL_TREE_STATIC_ASSERT 43 | #define SPATIAL_TREE_STATIC_ASSERT(cond, msg) \ 44 | _Pragma("clang diagnostic push"); \ 45 | _Pragma("clang diagnostic ignored \"-Wgcc-compat\""); \ 46 | struct SPATIAL_TREE_TOKENPASTE2(s, __LINE__) { \ 47 | static void static_check(bool) {} \ 48 | static void static_check(bool c) \ 49 | __attribute__((enable_if(c == false, "invalid"))) \ 50 | __attribute__((unavailable(msg))) { \ 51 | (void)(c); \ 52 | } \ 53 | }; \ 54 | SPATIAL_TREE_TOKENPASTE2(s, __LINE__)::static_check(bool(cond)); \ 55 | _Pragma("clang diagnostic pop"); \ 56 | do { \ 57 | } while (false) 58 | #endif //#if __has_attribute(enable_if) && !defined(NDEBUG) 59 | #endif //#if defined(__clang__) 60 | 61 | #endif // #ifdef SPATIAL_TREE_CPP11 62 | 63 | namespace spatial { 64 | namespace detail { 65 | struct dummy_iterator { 66 | dummy_iterator &operator++() { return *this; } 67 | dummy_iterator &operator*() { return *this; } 68 | template dummy_iterator &operator=(const T &) { return *this; } 69 | }; 70 | 71 | #if SPATIAL_TREE_ALLOCATOR == SPATIAL_TREE_STD_ALLOCATOR 72 | template 73 | inline typename AllocatorClass::value_type *allocate(AllocatorClass &allocator, 74 | int level) { 75 | typedef typename AllocatorClass::value_type Node; 76 | Node *p = allocator.allocate(1); 77 | // not using construct as deprecated from C++17 78 | new (p) Node(level); 79 | return p; 80 | } 81 | 82 | template 83 | inline void deallocate(AllocatorClass &allocator, 84 | typename AllocatorClass::value_type *node) { 85 | allocator.deallocate(node, 1); 86 | } 87 | #elif SPATIAL_TREE_ALLOCATOR == SPATIAL_TREE_DEFAULT_ALLOCATOR 88 | template 89 | inline typename AllocatorClass::value_type *allocate(AllocatorClass &allocator, 90 | int level) { 91 | return allocator.allocate(level); 92 | } 93 | 94 | template 95 | inline void deallocate(AllocatorClass &allocator, 96 | typename AllocatorClass::value_type *node) { 97 | allocator.deallocate(node); 98 | } 99 | #endif 100 | 101 | template struct static_assertion; 102 | template <> struct static_assertion {}; 103 | } // namespace detail 104 | } // namespace spatial 105 | -------------------------------------------------------------------------------- /include/THST/indexable.h: -------------------------------------------------------------------------------- 1 | // 2 | // indexable.h 3 | // 4 | // 5 | 6 | #pragma once 7 | 8 | namespace spatial { 9 | // Indexable getter for bbox limits for a given type 10 | template struct Indexable { 11 | const T *min(const ValueType &value) const { return value.min; } 12 | const T *max(const ValueType &value) const { return value.max; } 13 | }; 14 | } // namespace spatial 15 | -------------------------------------------------------------------------------- /include/THST/predicates.h: -------------------------------------------------------------------------------- 1 | // 2 | // predicates.h 3 | // 4 | // 5 | 6 | #pragma once 7 | 8 | #include "bbox.h" 9 | 10 | namespace spatial { 11 | 12 | namespace detail { 13 | struct intersects_tag; 14 | struct contains_tag; 15 | struct within_tag; 16 | 17 | template 18 | struct CheckPredicateHelper; 19 | 20 | template 21 | struct CheckPredicateHelper { 22 | typedef spatial::BoundingBox box_t; 23 | 24 | inline bool operator()(const box_t &predicateBBox, 25 | const box_t &valueBBox) const { 26 | return predicateBBox.overlaps(valueBBox); 27 | } 28 | }; 29 | 30 | template 31 | struct CheckPredicateHelper { 32 | typedef spatial::BoundingBox box_t; 33 | 34 | inline bool operator()(const box_t &predicateBBox, 35 | const box_t &valueBBox) const { 36 | return predicateBBox.contains(valueBBox); 37 | } 38 | }; 39 | 40 | template 41 | struct CheckPredicateHelper { 42 | typedef spatial::BoundingBox box_t; 43 | 44 | inline bool operator()(const box_t &predicateBBox, 45 | const box_t &valueBBox) const { 46 | return predicateBBox.contains(valueBBox.min); 47 | } 48 | }; 49 | 50 | template 51 | bool checkPredicate(const spatial::BoundingBox &predicateBBox, 52 | const spatial::BoundingBox &valueBBox) { 53 | return CheckPredicateHelper()(predicateBBox, 54 | valueBBox); 55 | } 56 | }; 57 | 58 | template 59 | struct SpatialPredicate { 60 | typedef spatial::BoundingBox box_t; 61 | typedef OperationTag op_t; 62 | 63 | SpatialPredicate(const box_t &bbox) : bbox(bbox) {} 64 | inline bool operator()(const box_t &valueBBox) const { 65 | return detail::checkPredicate(bbox, valueBBox); 66 | } 67 | 68 | box_t bbox; 69 | }; 70 | 71 | template 72 | SpatialPredicate 73 | intersects(const spatial::BoundingBox &bbox) { 74 | return SpatialPredicate(bbox); 75 | } 76 | template 77 | SpatialPredicate 78 | intersects(const T min[Dimension], const T max[Dimension]) { 79 | typedef SpatialPredicate predicate_t; 80 | return predicate_t(typename predicate_t::box_t(min, max)); 81 | } 82 | 83 | template 84 | SpatialPredicate 85 | contains(const spatial::BoundingBox &bbox) { 86 | return SpatialPredicate(bbox); 87 | } 88 | template 89 | SpatialPredicate 90 | contains(const T min[Dimension], const T max[Dimension]) { 91 | typedef SpatialPredicate predicate_t; 92 | return predicate_t(typename predicate_t::box_t(min, max)); 93 | } 94 | 95 | ///@note To be used with points, as it only check the min point of the 96 | /// bounding boxes of the leaves. 97 | template 98 | SpatialPredicate 99 | within(const spatial::BoundingBox &bbox) { 100 | return SpatialPredicate(bbox); 101 | } 102 | template 103 | SpatialPredicate 104 | within(const T min[Dimension], const T max[Dimension]) { 105 | typedef SpatialPredicate predicate_t; 106 | return predicate_t(typename predicate_t::box_t(min, max)); 107 | } 108 | 109 | } // namespace spatial 110 | -------------------------------------------------------------------------------- /include/THST/quad_tree_detail.h: -------------------------------------------------------------------------------- 1 | // 2 | // quad_tree_detail.h 3 | // 4 | // 5 | 6 | #pragma once 7 | 8 | #include "bbox.h" 9 | 10 | #include 11 | #include 12 | 13 | namespace spatial { 14 | namespace detail { 15 | template class QuadTreeStack { 16 | protected: 17 | struct StackElement { 18 | NodeClass *node; 19 | size_t childIndex; 20 | int objectIndex; 21 | }; 22 | 23 | protected: 24 | QuadTreeStack() : m_tos(0) {} 25 | 26 | void push(NodeClass *node, size_t childIndex, int objectIndex) { 27 | assert(node); 28 | 29 | StackElement &el = m_stack[m_tos++]; 30 | el.node = node; 31 | el.childIndex = childIndex; 32 | el.objectIndex = objectIndex; 33 | 34 | assert(m_tos <= kMaxStackchildIndexze); 35 | } 36 | 37 | void push(NodeClass *node, int objectIndex) { 38 | assert(node); 39 | 40 | StackElement &el = m_stack[m_tos++]; 41 | el.node = node; 42 | el.objectIndex = objectIndex; 43 | 44 | assert(m_tos <= kMaxStackchildIndexze); 45 | } 46 | 47 | StackElement &pop() { 48 | assert(m_tos > 0); 49 | 50 | StackElement &el = m_stack[--m_tos]; 51 | return el; 52 | } 53 | 54 | protected: 55 | // Max stack size. Allows almost n^16 where n is number of branches in node 56 | static const int kMaxStackchildIndexze = 16; 57 | StackElement m_stack[kMaxStackchildIndexze]; ///< Stack as we are doing 58 | /// iteration instead of recursion 59 | int m_tos; ///< Top Of Stack index 60 | }; 61 | 62 | template 63 | struct QuadTreeObject { 64 | ValueType value; 65 | BBoxClass box; 66 | 67 | QuadTreeObject() {} 68 | 69 | template 70 | QuadTreeObject(ValueType value, const indexable_getter &indexable) 71 | : value(value), box(indexable.min(value), indexable.max(value)) {} 72 | 73 | inline bool operator==(const QuadTreeObject& other) const 74 | { 75 | return value == other.value; 76 | } 77 | }; 78 | 79 | template struct QuadTreeNode { 80 | typedef BoundingBox bbox_type; 81 | typedef QuadTreeObject object_type; 82 | typedef std::vector ObjectList; 83 | 84 | const int level; 85 | ValueType value; 86 | ObjectList objects; 87 | bbox_type box; 88 | QuadTreeNode *children[4]; 89 | 90 | QuadTreeNode(int level); 91 | 92 | template 93 | void copy(const QuadTreeNode &src, custom_allocator &allocator); 94 | template 95 | bool insert(const object_type &obj, int &levels, custom_allocator &allocator); 96 | template 97 | bool remove(const object_type& obj, custom_allocator& allocator); 98 | template 99 | size_t query(const Predicate &predicate, float factor, OutIter out_it) const; 100 | template 101 | size_t queryHierachical(const Predicate &predicate, float factor, 102 | OutIter out_it) const; 103 | template void clear(custom_allocator &allocator); 104 | void translate(const T point[2]); 105 | size_t count() const; 106 | bool isEmpty() const; 107 | 108 | bool isBranch() const { return !isLeaf(); } 109 | 110 | bool isLeaf() const { return children[0] == NULL; } 111 | 112 | size_t objectCount() const { return objects.size(); } 113 | 114 | ValueType &objectValue(size_t objectIndex) { 115 | return objects[objectIndex].value; 116 | } 117 | 118 | const ValueType &objectValue(size_t objectIndex) const { 119 | return objects[objectIndex].value; 120 | } 121 | 122 | ValueType &objectValue(int objectIndex) { 123 | assert(objectIndex >= 0); 124 | return objects[(size_t)objectIndex].value; 125 | } 126 | 127 | const ValueType &objectValue(int objectIndex) const { 128 | assert(objectIndex >= 0); 129 | return objects[(size_t)objectIndex].value; 130 | } 131 | 132 | void updateCount() { 133 | m_count = count(); 134 | m_invCount = 1.f / m_count; 135 | } 136 | 137 | private: 138 | size_t m_count; 139 | float m_invCount; 140 | 141 | template 142 | void subdivide(custom_allocator &allocator); 143 | void addObject(const object_type &obj); 144 | bool removeObject(const object_type& obj); 145 | template 146 | bool addObjectsToChildren(custom_allocator &allocator); 147 | 148 | template 149 | void insertAll(size_t &foundCount, OutIter out_it) const; 150 | }; 151 | 152 | #define TREE_TEMPLATE template 153 | #define TREE_QUAL QuadTreeNode 154 | 155 | TREE_TEMPLATE 156 | TREE_QUAL::QuadTreeNode(int level) 157 | : level(level), children() 158 | #ifndef NDEBUG 159 | , 160 | m_count(0) 161 | #endif 162 | { 163 | } 164 | 165 | TREE_TEMPLATE 166 | template 167 | void TREE_QUAL::copy(const QuadTreeNode &src, custom_allocator &allocator) { 168 | assert(m_count == 0); 169 | value = src.value; 170 | objects = src.objects; 171 | box = src.box; 172 | m_count = src.m_count; 173 | m_invCount = src.m_invCount; 174 | 175 | if (src.isBranch()) { 176 | for (int i = 0; i < 4; ++i) { 177 | const QuadTreeNode *srcCurrent = src.children[i]; 178 | QuadTreeNode *dstCurrent = children[i] = 179 | detail::allocate(allocator, srcCurrent->level); 180 | dstCurrent->copy(*srcCurrent, allocator); 181 | } 182 | } 183 | } 184 | 185 | TREE_TEMPLATE 186 | template 187 | bool TREE_QUAL::insert(const object_type &obj, int &levels, 188 | custom_allocator &allocator) { 189 | if (!this->box.contains(obj.box)) 190 | // this object doesn't fit in this quadtree 191 | return false; 192 | 193 | if (isLeaf()) // No subdivision yet 194 | { 195 | if (objects.size() < max_child_items + 1) { 196 | size_t count = objects.size(); 197 | addObject(obj); 198 | return objects.size() > count; 199 | } 200 | 201 | // subdivide node 202 | subdivide(allocator); 203 | if (addObjectsToChildren(allocator)) { 204 | levels = std::max(levels, level + 1); 205 | } 206 | else { 207 | // could not insert anything in any of the sub-trees 208 | for (int i = 0; i < 4; ++i) { 209 | detail::deallocate(allocator, children[i]); 210 | children[i] = NULL; 211 | } 212 | 213 | addObject(obj); 214 | return true; 215 | } 216 | } 217 | 218 | // try to add to children 219 | for (int i = 0; i < 4; ++i) { 220 | assert(children[i]); 221 | if (children[i]->insert(obj, levels, allocator)) { 222 | updateCount(); 223 | return true; 224 | } 225 | } 226 | addObject(obj); 227 | return true; 228 | } 229 | 230 | TREE_TEMPLATE 231 | template 232 | bool TREE_QUAL::remove(const object_type& obj, 233 | custom_allocator& allocator) { 234 | if (!this->box.contains(obj.box)) 235 | // this object doesn't fit in this quadtree 236 | return false; 237 | 238 | if (isLeaf()) 239 | return removeObject(obj); 240 | 241 | // try to remove from one of the children 242 | for (int i = 0; i < 4; ++i) { 243 | assert(children[i]); 244 | if (children[i]->remove(obj, allocator)) { 245 | updateCount(); 246 | return true; 247 | } 248 | } 249 | return removeObject(obj); 250 | } 251 | 252 | TREE_TEMPLATE 253 | bool TREE_QUAL::removeObject(const object_type& obj) { 254 | const auto found = std::find(objects.begin(), objects.end(), obj); 255 | if (found != objects.end()) 256 | { 257 | objects.erase(found); 258 | updateCount(); 259 | return true; 260 | } 261 | return false; 262 | } 263 | 264 | TREE_TEMPLATE 265 | template 266 | size_t TREE_QUAL::query(const Predicate &predicate, float containmentFactor, 267 | OutIter out_it) const { 268 | assert(m_count == count()); 269 | 270 | size_t foundCount = 0; 271 | 272 | // go further into the tree 273 | for (typename ObjectList::const_iterator it = objects.begin(); 274 | it != objects.end(); ++it) { 275 | if (predicate(it->box)) { 276 | *out_it = it->value; 277 | ++out_it; 278 | ++foundCount; 279 | } 280 | } 281 | 282 | if (isLeaf()) { 283 | // reached leaves 284 | return foundCount; 285 | } 286 | 287 | for (int i = 0; i < 4; i++) { 288 | assert(children[i]); 289 | 290 | QuadTreeNode &node = *children[i]; 291 | // Break if we know that the zone is fully contained by a region 292 | if (predicate.bbox.overlaps(node.box)) { 293 | foundCount += node.query(predicate, containmentFactor, out_it); 294 | if (node.box.contains(predicate.bbox)) { 295 | break; 296 | } 297 | } 298 | } 299 | 300 | return foundCount; 301 | } 302 | 303 | TREE_TEMPLATE 304 | template 305 | size_t TREE_QUAL::queryHierachical(const Predicate &predicate, 306 | float containmentFactor, 307 | OutIter out_it) const { 308 | assert(m_count == count()); 309 | 310 | size_t foundCount = 0; 311 | if (predicate.bbox.contains(this->box) && !isEmpty()) { 312 | // node is fully contained by the query 313 | *out_it = value; 314 | ++out_it; 315 | foundCount += m_count; 316 | 317 | return foundCount; 318 | } 319 | 320 | const OutIter start = out_it; 321 | // go further into the tree 322 | for (typename ObjectList::const_iterator it = objects.begin(); 323 | it != objects.end(); ++it) { 324 | if (predicate(it->box)) { 325 | *out_it = it->value; 326 | ++out_it; 327 | ++foundCount; 328 | } 329 | } 330 | 331 | if (isLeaf()) { 332 | if (foundCount) { 333 | const float factor = foundCount * m_invCount; 334 | if (factor > containmentFactor) { 335 | out_it = start; 336 | // node is fully contained by the query 337 | *out_it = value; 338 | ++out_it; 339 | foundCount = m_count; 340 | } 341 | } 342 | 343 | // reached leaves 344 | return foundCount; 345 | } 346 | 347 | for (int i = 0; i < 4; i++) { 348 | assert(children[i]); 349 | 350 | QuadTreeNode &node = *children[i]; 351 | // Break if we know that the zone is fully contained by a region 352 | if (predicate.bbox.overlaps(node.box)) { 353 | foundCount += node.query(predicate, containmentFactor, out_it); 354 | if (node.box.contains(predicate.bbox)) { 355 | break; 356 | } 357 | } 358 | } 359 | 360 | if (foundCount) { 361 | const float factor = foundCount * m_invCount; 362 | if (factor > containmentFactor) { 363 | out_it = start; 364 | // node is fully contained by the query 365 | *out_it = value; 366 | ++out_it; 367 | foundCount = m_count; 368 | } 369 | } 370 | 371 | return foundCount; 372 | } 373 | 374 | TREE_TEMPLATE 375 | void TREE_QUAL::translate(const T point[2]) { 376 | for (typename ObjectList::iterator it = objects.begin(); it != objects.end(); 377 | ++it) { 378 | it->box.translate(point); 379 | } 380 | box.translate(point); 381 | 382 | if (isBranch()) { 383 | for (int i = 0; i < 4; ++i) { 384 | if (children[i]) { 385 | children[i]->translate(point); 386 | } 387 | } 388 | } 389 | } 390 | 391 | TREE_TEMPLATE 392 | size_t TREE_QUAL::count() const { 393 | size_t count = objectCount(); 394 | if (isBranch()) { 395 | for (int i = 0; i < 4; ++i) { 396 | count += children[i]->count(); 397 | } 398 | } 399 | return count; 400 | } 401 | 402 | TREE_TEMPLATE 403 | bool TREE_QUAL::isEmpty() const { 404 | if (!objects.empty()) 405 | return false; 406 | 407 | if (isBranch()) { 408 | for (int i = 0; i < 4; ++i) { 409 | if (!children[i]->isEmpty()) 410 | return false; 411 | } 412 | } 413 | return true; 414 | } 415 | 416 | TREE_TEMPLATE 417 | template 418 | void TREE_QUAL::clear(custom_allocator &allocator) { 419 | if (isLeaf()) 420 | return; 421 | 422 | for (int i = 0; i < 4; ++i) { 423 | children[i]->clear(allocator); 424 | detail::deallocate(allocator, children[i]); 425 | } 426 | } 427 | 428 | TREE_TEMPLATE 429 | template 430 | void TREE_QUAL::subdivide(custom_allocator &allocator) { 431 | for (int i = 0; i < 4; ++i) { 432 | assert(children[i] == NULL); 433 | children[i] = detail::allocate(allocator, level + 1); 434 | QuadTreeNode &node = *children[i]; 435 | node.box = box.quad2d(static_cast(i)); 436 | } 437 | } 438 | 439 | TREE_TEMPLATE 440 | void TREE_QUAL::addObject(const object_type &obj) { 441 | auto found = std::find(objects.begin(), objects.end(), obj); 442 | if (found == objects.end()) 443 | { 444 | objects.push_back(obj); 445 | updateCount(); 446 | } 447 | } 448 | 449 | TREE_TEMPLATE 450 | template 451 | bool TREE_QUAL::addObjectsToChildren(custom_allocator &allocator) { 452 | int dummy = 0; 453 | const size_t prevSize = objects.size(); 454 | for (typename ObjectList::iterator it = objects.begin(); it != objects.end(); 455 | ++it) { 456 | for (int i = 0; i < 4; ++i) { 457 | if (children[i]->insert(*it, dummy, allocator)) { 458 | it = objects.erase(it); 459 | if (it == objects.end()) { 460 | objects.shrink_to_fit(); 461 | for (int i = 0; i < 4; ++i) { 462 | children[i]->updateCount(); 463 | assert(children[i]->m_count == children[i]->count()); 464 | } 465 | 466 | return true; 467 | } 468 | break; 469 | } 470 | } 471 | } 472 | 473 | objects.shrink_to_fit(); 474 | for (int i = 0; i < 4; ++i) { 475 | children[i]->updateCount(); 476 | assert(children[i]->m_count == children[i]->count()); 477 | } 478 | 479 | // return if we could insert anything in any of the sub-trees 480 | return prevSize != objects.size(); 481 | } 482 | 483 | TREE_TEMPLATE 484 | template 485 | void TREE_QUAL::insertAll(size_t &foundCount, OutIter out_it) const { 486 | for (typename ObjectList::const_iterator it = objects.begin(); 487 | it != objects.end(); ++it) { 488 | // add crossing/overlapping results 489 | *out_it = it->value; 490 | ++out_it; 491 | } 492 | foundCount += objects.size(); 493 | 494 | if (isBranch()) { 495 | for (int i = 0; i < 4; ++i) { 496 | children[i]->insertAll(foundCount, out_it); 497 | } 498 | } 499 | } 500 | } // namespace detail 501 | 502 | #undef TREE_TEMPLATE 503 | #undef TREE_QUAL 504 | } // namespace spatial 505 | -------------------------------------------------------------------------------- /include/THST/rtree_detail.h: -------------------------------------------------------------------------------- 1 | // 2 | // rtree_detail.h 3 | // 4 | // 5 | 6 | #pragma once 7 | 8 | //#define TREE_DEBUG_TAG 9 | 10 | namespace spatial { 11 | namespace detail { 12 | 13 | template class Stack { 14 | public: 15 | enum StatusType { eNormal = 0, eBranchTraversed, eNextBranch }; 16 | 17 | protected: 18 | struct StackElement { 19 | NodeClass *node; 20 | CountType branchIndex; 21 | StatusType status; 22 | 23 | StackElement() : status(eNormal) {} 24 | }; 25 | 26 | protected: 27 | Stack() : m_tos(0) {} 28 | 29 | void push(NodeClass *node, CountType branchIndex, 30 | StatusType status = eNormal) { 31 | assert(node); 32 | 33 | StackElement &el = m_stack[m_tos++]; 34 | el.node = node; 35 | el.branchIndex = branchIndex; 36 | el.status = status; 37 | 38 | assert(m_tos <= kMaxStackSize); 39 | } 40 | StackElement &pop() { 41 | assert(m_tos > 0); 42 | 43 | StackElement &el = m_stack[--m_tos]; 44 | return el; 45 | } 46 | 47 | protected: 48 | // Max stack size. Allows almost n^16 where n is number of branches in node 49 | static const int kMaxStackSize = 16; 50 | StackElement m_stack[kMaxStackSize]; ///< Stack as we are doing iteration 51 | /// instead of recursion 52 | int m_tos; ///< Top Of Stack index 53 | }; // class stack_iterator 54 | 55 | /// May be data or may be another subtree 56 | /// The parents level determines this. 57 | /// If the parents level is 0, then this is data 58 | template struct Branch { 59 | ValueType value; 60 | BBoxClass bbox; 61 | NodeClass *child; 62 | 63 | #ifndef NDEBUG 64 | Branch() : child(NULL) {} 65 | #endif 66 | }; // Branch 67 | 68 | template 69 | struct Node { 70 | typedef Branch branch_type; 71 | typedef uint32_t count_type; 72 | typedef BBoxClass box_type; 73 | 74 | count_type count; ///< Number of branches in the node 75 | int32_t level; ///< Leaf is zero, others positive 76 | ValueType values[max_child_items]; 77 | BBoxClass bboxes[max_child_items]; 78 | Node *children[max_child_items]; 79 | 80 | #ifdef TREE_DEBUG_TAG 81 | tn::string debugTags[max_child_items]; 82 | #endif 83 | 84 | Node() : level(0) {} 85 | 86 | Node(int level) : count(0), level(level) {} 87 | 88 | // Not a leaf, but a internal/branch node 89 | bool isBranch() const { return (level > 0); } 90 | 91 | bool isLeaf() const { return (level == 0); } 92 | 93 | // Find the smallest rectangle that includes all rectangles in branches of a 94 | // node. 95 | BBoxClass cover() const { 96 | BBoxClass bbox = bboxes[0]; 97 | for (count_type index = 1; index < count; ++index) { 98 | bbox.extend(bboxes[index]); 99 | } 100 | 101 | return bbox; 102 | } 103 | bool addBranch(const branch_type &branch) { 104 | if (count >= max_child_items) // Split is necessary 105 | return false; 106 | 107 | values[count] = branch.value; 108 | children[count] = branch.child; 109 | bboxes[count++] = branch.bbox; 110 | return true; 111 | } 112 | 113 | // Disconnect a dependent node. 114 | // Caller must return (or stop using iteration index) after this as count has 115 | // changed 116 | void disconnectBranch(count_type index) { 117 | assert(index >= 0 && index < max_child_items); 118 | assert(count > 0); 119 | 120 | // Remove element by swapping with the last element to prevent gaps in array 121 | values[index] = values[--count]; 122 | children[index] = children[count]; 123 | bboxes[index] = bboxes[count]; 124 | } 125 | }; // Node 126 | 127 | 128 | struct AlwayTruePredicate { 129 | 130 | template 131 | inline bool operator()(const T&) const { 132 | return true; 133 | } 134 | }; 135 | 136 | struct DummyInsertPredicate {}; 137 | 138 | template 139 | struct CheckInsertPredicateHelper { 140 | 141 | inline bool operator()(const Predicate &predicate, 142 | const NodeClass &node) const { 143 | for (typename NodeClass::count_type index = 0; index < node.count; 144 | ++index) { 145 | if (!predicate(node.bboxes[index])) 146 | return false; 147 | } 148 | return true; 149 | } 150 | }; 151 | 152 | template 153 | struct CheckInsertPredicateHelper { 154 | inline bool operator()(const DummyInsertPredicate & /*predicate*/, 155 | const NodeClass & /*node*/) const { 156 | return true; 157 | }; 158 | }; 159 | 160 | template 161 | inline bool checkInsertPredicate(const Predicate &predicate, 162 | const NodeClass &node) { 163 | return CheckInsertPredicateHelper()(predicate, node); 164 | } 165 | 166 | template 167 | typename RTreeClass::node_ptr_type &getRootNode(RTreeClass &tree) { 168 | return tree.m_root; 169 | } 170 | } // namespace detail 171 | } // namespace spatial 172 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_subdirectory(doctest) 2 | 3 | add_executable(test_quadtree 4 | test_quadtree.cpp 5 | ) 6 | add_executable(THST::test_quadtree ALIAS test_quadtree) 7 | 8 | target_link_libraries(test_quadtree 9 | PRIVATE 10 | doctest::doctest 11 | THST::THST 12 | ) 13 | 14 | set_target_properties(test_quadtree 15 | PROPERTIES 16 | CXX_STANDARD 11 17 | CXX_STANDARD_REQUIRED ON 18 | ) 19 | 20 | target_compile_definitions(test_quadtree 21 | PRIVATE 22 | DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 23 | ) 24 | 25 | ## 26 | 27 | add_executable(test_rtree 28 | test_rtree.cpp 29 | ) 30 | add_executable(THST::test_rtree ALIAS test_rtree) 31 | 32 | target_link_libraries(test_rtree 33 | PRIVATE 34 | doctest::doctest 35 | THST::THST 36 | ) 37 | 38 | set_target_properties(test_rtree 39 | PROPERTIES 40 | CXX_STANDARD 11 41 | CXX_STANDARD_REQUIRED ON 42 | ) 43 | 44 | target_compile_definitions(test_rtree 45 | PRIVATE 46 | DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 47 | ) -------------------------------------------------------------------------------- /test/custom_allocator.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace test { 9 | 10 | // example of a cache friendly allocator with contiguous memory 11 | template 13 | struct tree_allocator { 14 | 15 | typedef NodeClass value_type; 16 | typedef NodeClass *ptr_type; 17 | 18 | enum { is_overflowable = 0 }; 19 | 20 | tree_allocator() : buffer(DefaultNodeCount), count(0), index(0) {} 21 | 22 | ptr_type allocate(int level) { 23 | if (IsDebugMode) 24 | std::cout << "Allocate node: " << level << " current count: " << count 25 | << "\n"; 26 | ++count; 27 | assert(index + 1 <= buffer.size()); 28 | ptr_type node = &buffer[index++]; 29 | *node = NodeClass(level); 30 | return node; 31 | } 32 | 33 | void deallocate(const ptr_type node) { 34 | if (IsDebugMode) 35 | std::cout << "Deallocate node: " << node->level << " - " << node->count 36 | << " current count: " << count << "\n"; 37 | 38 | assert(count > 0); 39 | if (count > 1) 40 | --count; 41 | else { 42 | count = 0; 43 | index = 0; 44 | } 45 | } 46 | 47 | void clear() { 48 | buffer.clear(); 49 | count = index = 0; 50 | } 51 | void resize(size_t count) { buffer.resize(count); } 52 | 53 | bool overflowed() const { return false; } 54 | 55 | std::vector buffer; 56 | size_t count; 57 | size_t index; 58 | }; 59 | 60 | template struct heap_allocator { 61 | typedef NodeClass value_type; 62 | typedef NodeClass *ptr_type; 63 | 64 | enum { is_overflowable = 0 }; 65 | 66 | heap_allocator() : count(0) {} 67 | 68 | ptr_type allocate(int level) { 69 | ++count; 70 | return new NodeClass(level); 71 | } 72 | 73 | void deallocate(const ptr_type node) { 74 | assert(node); 75 | delete node; 76 | } 77 | 78 | bool overflowed() const { return false; } 79 | 80 | size_t count; 81 | }; 82 | } 83 | -------------------------------------------------------------------------------- /test/test_quadtree.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | template 10 | struct Box2 { 11 | T min[2]; 12 | T max[2]; 13 | 14 | explicit operator spatial::BoundingBox() const { 15 | return spatial::BoundingBox(min, max); 16 | } 17 | 18 | bool operator==(const Box2& other) const 19 | { 20 | return (min[0] == other.min[0] 21 | && min[1] == other.min[1] 22 | && max[0] == other.max[0] 23 | && max[1] == other.max[1]); 24 | } 25 | }; 26 | 27 | template struct Point { 28 | union { 29 | T data[2]; 30 | struct { 31 | T x, y; 32 | }; 33 | }; 34 | 35 | void set(T x, T y) { 36 | this->x = x; 37 | this->y = y; 38 | } 39 | 40 | inline float distance(const Point point) const { 41 | 42 | float d = data[0] - point.data[0]; 43 | d *= d; 44 | for (int i = 1; i < 2; i++) 45 | { 46 | float temp = data[i] - point.data[i]; 47 | d += temp * temp; 48 | } 49 | return std::sqrt(d); 50 | } 51 | }; 52 | 53 | const Box2 kBoxes[] = { 54 | {{5, 2}, {16, 7}}, {{1, 1}, {2, 2}}, {{26, 24}, {44, 28}}, {{22, 21}, {23, 24}}, 55 | {{16, 0}, {32, 16}}, {{0, 0}, {8, 8}}, {{4, 4}, {6, 8}}, {{2, 1}, {2, 3}}, 56 | {{4, 2}, {8, 4}}, {{3, 3}, {12, 16}}, {{0, 0}, {64, 32}}, {{3, 2}, {32, 35}}, 57 | {{32, 32}, {64, 128}}, {{128, 0}, {256, 64}}, {{120, 64}, {250, 128}}, {{123, 84}, {230, 122}} }; 58 | 59 | template 60 | std::ostream& operator<<(std::ostream& os, const Box2 box) { 61 | os << "{{" << box.min[0] << ", " << box.min[1] << "}, {" 62 | << box.max[0] << ", " << box.max[1] << "}}"; 63 | return os; 64 | } 65 | 66 | TEST_CASE("test count") { 67 | int min[]{0, 0}; 68 | int max[]{1, 1}; 69 | spatial::BoundingBox bbox{min, max}; 70 | spatial::QuadTree> qtree{bbox.min, bbox.max}; 71 | Box2 box{{0, 0}, {1, 1}}; 72 | 73 | SUBCASE("after value insert") { 74 | for (int inserts{0}; inserts < 3; ++inserts) { 75 | CHECK(qtree.count() <= 1); 76 | qtree.insert(box); 77 | } 78 | } 79 | 80 | SUBCASE("after iterator insert") { 81 | std::array, 2> boxes{box, box}; 82 | 83 | int expected_count{0}; 84 | for (int inserts{0}; inserts < 2; ++inserts) { 85 | CAPTURE(inserts); 86 | qtree.insert( 87 | std::begin(boxes), 88 | std::begin(boxes) + inserts); 89 | expected_count += inserts; 90 | CHECK(qtree.count() == expected_count); 91 | } 92 | } 93 | } 94 | 95 | TEST_CASE("test query") { 96 | int min[]{ -2, -1 }; 97 | int max[]{ 9, 10 }; 98 | spatial::BoundingBox bbox{ min, max }; 99 | spatial::QuadTree> qtree{ bbox.min, bbox.max }; 100 | 101 | SUBCASE("with empty tree") { 102 | CHECK(qtree.query(spatial::intersects(bbox)) == false); 103 | } 104 | 105 | Box2 box{ {0, 1}, {5, 6} }; 106 | qtree.insert(box); 107 | 108 | SUBCASE("with lower left corner touching") { 109 | int corner_min[]{ -1, 0 }; 110 | int corner_max[]{ 0, 1 }; 111 | spatial::BoundingBox qbox{ corner_min, corner_max }; 112 | CHECK(qtree.query(spatial::intersects(qbox)) == true); 113 | } 114 | 115 | SUBCASE("with upper right corner touching") { 116 | int corner_min[]{ 5, 6 }; 117 | int corner_max[]{ 6, 7 }; 118 | spatial::BoundingBox qbox{ corner_min, corner_max }; 119 | CHECK(qtree.query(spatial::intersects(qbox)) == true); 120 | } 121 | 122 | SUBCASE("with non-intersecting box") { 123 | std::array, 4> queries{ 124 | Box2{ { 0, -1}, { 1, 0}}, 125 | Box2{ {-2, 1}, {-1, 2}}, 126 | Box2{ { 5, 7}, { 6, 8}}, 127 | Box2{ { 6, 5}, { 7, 8}} }; 128 | 129 | for (const auto query : queries) { 130 | CAPTURE(query); 131 | spatial::BoundingBox qbox{ query }; 132 | CHECK(qtree.query(spatial::intersects(qbox)) == false); 133 | } 134 | } 135 | } 136 | 137 | TEST_CASE("test insert") { 138 | // create a quad tree with the given box 139 | spatial::BoundingBox bbox((spatial::box::empty_init())); 140 | Point point; 141 | point.set(0, 0); 142 | bbox.extend(point.data); 143 | point.set(256, 128); 144 | bbox.extend(point.data); 145 | 146 | typedef spatial::QuadTree, 2> qtree_box_t; 147 | qtree_box_t qtree(bbox.min, bbox.max); 148 | 149 | CHECK(qtree.count() == 0u); 150 | qtree = qtree_box_t(bbox.min, bbox.max, std::begin(kBoxes), std::end(kBoxes)); 151 | CHECK(qtree.count() == 16u); 152 | qtree.clear(); 153 | CHECK(qtree.count() == 0u); 154 | 155 | // or construction via insert 156 | qtree.insert(std::begin(kBoxes), std::end(kBoxes)); 157 | CHECK(qtree.count() == 16u); 158 | Box2 box = { {7, 3}, {14, 6} }; 159 | qtree.insert(box); 160 | CHECK(qtree.count() == 17u); 161 | 162 | SUBCASE("count and remove") 163 | { 164 | // remove an element 165 | bool removed = qtree.remove(box); 166 | CHECK(removed); 167 | 168 | removed = qtree.remove(box); 169 | CHECK(!removed); 170 | std::vector> results; 171 | qtree.query(spatial::contains<2>(box.min, box.max), std::back_inserter(results)); 172 | CHECK(results.empty()); 173 | 174 | box = { {0, 0}, {20, 50} }; 175 | qtree.query(spatial::contains<2>(box.min, box.max), std::back_inserter(results)); 176 | CHECK(!results.empty()); 177 | CHECK(results.size() == 7); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /test/test_rtree.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "custom_allocator.h" 4 | 5 | #define SPATIAL_TREE_ALLOCATOR 2 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | template struct Point { 13 | union { 14 | T data[2]; 15 | struct { 16 | T x, y; 17 | }; 18 | }; 19 | 20 | void set(T x, T y) { 21 | this->x = x; 22 | this->y = y; 23 | } 24 | 25 | inline float distance(const Point point) const { 26 | 27 | float d = float(data[0] - point.data[0]); 28 | d *= d; 29 | for (int i = 1; i < 2; i++) 30 | { 31 | float temp = float(data[i] - point.data[i]); 32 | d += temp * temp; 33 | } 34 | return std::sqrt(d); 35 | } 36 | }; 37 | 38 | template struct Box2 { 39 | T min[2]; 40 | T max[2]; 41 | 42 | explicit operator spatial::BoundingBox() const { 43 | return spatial::BoundingBox(min, max); 44 | } 45 | 46 | bool operator==(const Box2& other) const 47 | { 48 | return min[0] == other.min[0] && min[1] == other.min[1] && max[0] == other.max[0] && max[1] == other.max[1]; 49 | } 50 | }; 51 | 52 | template 53 | std::ostream &operator<<(std::ostream &stream, const Box2 &bbox) { 54 | stream << "min: " << bbox.min[0] << " " << bbox.min[1] << " "; 55 | stream << "max: " << bbox.max[0] << " " << bbox.max[1]; 56 | return stream; 57 | } 58 | 59 | struct Object { 60 | spatial::BoundingBox bbox; 61 | std::string name; 62 | 63 | // needed to avoid adding duplicates 64 | bool operator==(const Object& other) const 65 | { 66 | return name == other.name; 67 | } 68 | }; 69 | 70 | typedef spatial::RTree, 2, 4, 2> rtree_box_t; 71 | 72 | const Box2 kBoxes[] = { 73 | {{5, 2}, {16, 7}}, {{1, 1}, {2, 2}}, {{26, 24}, {44, 28}}, {{22, 21}, {23, 24}}, 74 | {{16, 0}, {32, 16}}, {{0, 0}, {8, 8}}, {{4, 4}, {6, 8}}, {{2, 1}, {2, 3}}, 75 | {{4, 2}, {8, 4}}, {{3, 3}, {12, 16}}, {{0, 0}, {64, 32}}, {{3, 2}, {32, 35}}, 76 | {{32, 32}, {64, 128}}, {{128, 0}, {256, 64}}, {{120, 64}, {250, 128}}, {{123, 84}, {230, 122}} }; 77 | 78 | // use a custom indexable for custom objects 79 | struct Indexable { 80 | const int* min(const Object& value) const { return value.bbox.min; } 81 | const int* max(const Object& value) const { return value.bbox.max; } 82 | }; 83 | 84 | std::vector objects; 85 | std::vector> boxes; 86 | rtree_box_t rtree; 87 | 88 | TEST_CASE("test rtree create") { 89 | 90 | size_t i = 0; 91 | MESSAGE("Creating objects:\n"); 92 | for (const auto& bbox : kBoxes) { 93 | boxes.push_back(bbox); 94 | objects.emplace_back(); 95 | Object& obj = objects.back(); 96 | 97 | std::stringstream ss; 98 | ss << "object" << i++; 99 | obj.name = ss.str(); 100 | obj.bbox = (spatial::BoundingBox)bbox; 101 | 102 | MESSAGE(obj.name << " " << obj.bbox << "\n"); 103 | } 104 | 105 | // create a quad tree with the given box 106 | spatial::BoundingBox bbox((spatial::box::empty_init())); 107 | Point point; 108 | point.set(0, 0); 109 | bbox.extend(point.data); 110 | point.set(256, 128); 111 | bbox.extend(point.data); 112 | 113 | CHECK(rtree.count() == 0u); 114 | rtree = rtree_box_t(std::begin(kBoxes), std::end(kBoxes)); 115 | CHECK(rtree.count() == 16u); 116 | rtree.clear(); 117 | CHECK(rtree.count() == 0u); 118 | 119 | // or construction via insert 120 | rtree.insert(std::begin(kBoxes), std::end(kBoxes)); 121 | CHECK(rtree.count() == 16u); 122 | Box2 box = { {7, 3}, {14, 6} }; 123 | rtree.insert(box); 124 | CHECK(rtree.count() == 17u); 125 | 126 | SUBCASE("conditional insert") 127 | { 128 | // insert only if predicate is always valid 129 | Box2 box2 = { {7, 4}, {14, 6} }; 130 | bool wasAdded = 131 | rtree.insert(box2, [&box2](const decltype(rtree)::bbox_type& bbox) { 132 | const decltype(rtree)::bbox_type cbbox(box2.min, box2.max); 133 | return !bbox.overlaps(cbbox); 134 | }); 135 | CHECK(!wasAdded); 136 | CHECK(rtree.count() == 17u); 137 | 138 | wasAdded = 139 | rtree.insert(box, [&box](const decltype(rtree)::bbox_type& bbox) { 140 | const decltype(rtree)::bbox_type cbbox(box.min, box.max); 141 | return !bbox.overlaps(cbbox); 142 | }); 143 | CHECK(!wasAdded); 144 | CHECK(rtree.count() == 17u); 145 | } 146 | } 147 | 148 | TEST_CASE("test rtree create") { 149 | Box2 box = { {7, 3}, {14, 6} }; 150 | 151 | MESSAGE("Created trees, element count: " << rtree.count() << "\n"); 152 | CHECK(rtree.count() == 17u); 153 | rtree_box_t::bbox_type treeBBox = rtree.bbox(); 154 | CHECK(treeBBox.min[0] == 0); 155 | CHECK(treeBBox.min[1] == 0); 156 | CHECK(treeBBox.max[0] == 256); 157 | CHECK(treeBBox.max[1] == 128); 158 | 159 | // remove an element 160 | bool removed = rtree.remove(box); 161 | CHECK(removed); 162 | CHECK(rtree.count() == 16u); 163 | 164 | std::vector> results; 165 | removed = rtree.remove(box); 166 | CHECK(!removed); 167 | results.clear(); 168 | rtree.query(spatial::contains<2>(box.min, box.max), std::back_inserter(results)); 169 | CHECK(results.empty()); 170 | 171 | box = { {0, 0}, {20, 50} }; 172 | rtree.query(spatial::contains<2>(box.min, box.max), std::back_inserter(results)); 173 | CHECK(!results.empty()); 174 | CHECK(results.size() == 7); 175 | } 176 | 177 | TEST_CASE("query for results within the search box") 178 | { 179 | Box2 searchBox = { {0, 0}, {8, 31} }; 180 | std::vector> results; 181 | rtree.query(spatial::intersects<2>(searchBox.min, searchBox.max), 182 | std::back_inserter(results)); 183 | 184 | std::stringstream resultsStream; 185 | for (const auto& res : results) 186 | resultsStream << res << ", "; 187 | CHECK(resultsStream.str() == "min: 3 3 max: 12 16, min: 0 0 max: 64 32, min: 3 2 max: 32 35, min: 1 1 max: 2 2, min: 2 1 max: 2 3, min: 5 2 max: 16 7, min: 0 0 max: 8 8, min: 4 4 max: 6 8, min: 4 2 max: 8 4, "); 188 | 189 | results.clear(); 190 | rtree.query(spatial::contains<2>(searchBox.min, searchBox.max), 191 | std::back_inserter(results)); 192 | resultsStream.clear(); 193 | 194 | for (const auto& res : results) 195 | resultsStream << res << ", "; 196 | CHECK(resultsStream.str() == "min: 3 3 max: 12 16, min: 0 0 max: 64 32, min: 3 2 max: 32 35, min: 1 1 max: 2 2, min: 2 1 max: 2 3, min: 5 2 max: 16 7, min: 0 0 max: 8 8, min: 4 4 max: 6 8, min: 4 2 max: 8 4, min: 1 1 max: 2 2, min: 2 1 max: 2 3, min: 0 0 max: 8 8, min: 4 4 max: 6 8, min: 4 2 max: 8 4, "); 197 | } 198 | 199 | TEST_CASE("ray query") 200 | { 201 | Point rayOrigin({ 0.5, 0 }); 202 | Point rayDir({ 0, 1 }); 203 | std::vector> results; 204 | rtree.rayQuery(rayOrigin.data, rayDir.data, std::back_inserter(results)); 205 | 206 | std::stringstream resultsStream; 207 | for (const auto& res : results) 208 | resultsStream << res << ", "; 209 | CHECK(resultsStream.str() == "min: 0 0 max: 64 32, min: 0 0 max: 8 8, "); 210 | 211 | rayOrigin = Point({ 100.5, 0.5 }); 212 | rayDir = Point({ 0, 1 }); 213 | results.clear(); 214 | rtree.rayQuery(rayOrigin.data, rayDir.data, std::back_inserter(results)); 215 | CHECK(results.empty()); 216 | 217 | rayOrigin = Point({ 68,130 }); 218 | rayDir = Point({ 120, 52 }); 219 | results.clear(); 220 | rtree.rayQuery(rayOrigin.data, rayDir.data, std::back_inserter(results)); 221 | resultsStream.str(""); 222 | for (const auto& res : results) 223 | resultsStream << res << ", "; 224 | CHECK(resultsStream.str() == "min: 32 32 max: 64 128, "); 225 | 226 | rayOrigin = Point({ 11,110 }); 227 | rayDir = Point({ 1, 0 }); 228 | results.clear(); 229 | rtree.rayQuery(rayOrigin.data, rayDir.data, std::back_inserter(results)); 230 | resultsStream.str(""); 231 | for (const auto& res : results) 232 | resultsStream << res << ", "; 233 | CHECK(resultsStream.str() == "min: 32 32 max: 64 128, min: 123 84 max: 230 122, min: 120 64 max: 250 128, "); 234 | 235 | rayOrigin = Point({ 63, 25 }); 236 | rayDir = Point({ 0, 2}); 237 | results.clear(); 238 | rtree.rayQuery(rayOrigin.data, rayDir.data, std::back_inserter(results)); 239 | resultsStream.str(""); 240 | for (const auto& res : results) 241 | resultsStream << res << ", "; 242 | CHECK(resultsStream.str() == "min: 32 32 max: 64 128, min: 0 0 max: 64 32, "); 243 | 244 | rayOrigin = Point({ 62, 70 }); 245 | rayDir = Point({ 0, -2 }); 246 | results.clear(); 247 | rtree.rayQuery(rayOrigin.data, rayDir.data, std::back_inserter(results)); 248 | resultsStream.str(""); 249 | for (const auto& res : results) 250 | resultsStream << res << ", "; 251 | CHECK(resultsStream.str() == "min: 32 32 max: 64 128, min: 0 0 max: 64 32, "); 252 | 253 | auto fnTestPredicate = [](const Box2& box) {return box.min[0] == 0; }; 254 | rayOrigin = Point({ 62, 70 }); 255 | rayDir = Point({ 0, -2 }); 256 | results.clear(); 257 | rtree.rayQuery(rayOrigin.data, rayDir.data, std::back_inserter(results), fnTestPredicate); 258 | resultsStream.str(""); 259 | for (const auto& res : results) 260 | resultsStream << res << ", "; 261 | CHECK(resultsStream.str() == "min: 0 0 max: 64 32, "); 262 | 263 | rayOrigin = Point({ 65, 70 }); 264 | rayDir = Point({ 0, -2 }); 265 | results.clear(); 266 | rtree.rayQuery(rayOrigin.data, rayDir.data, std::back_inserter(results)); 267 | CHECK(results.empty()); 268 | } 269 | 270 | TEST_CASE("tree traversal") 271 | { 272 | spatial::RTree rtree; 273 | rtree.insert(objects.begin(), objects.end()); 274 | 275 | SUBCASE("leaf traversal of the tree") 276 | { 277 | spatial::RTree rtree; 278 | rtree.insert(objects.begin(), objects.end()); 279 | 280 | std::stringstream resultsStream; 281 | for (const auto& obj : objects) { 282 | resultsStream << obj.name << " "; 283 | } 284 | CHECK(resultsStream.str() == "object0 object1 object2 object3 object4 object5 object6 object7 object8 object9 object10 object11 object12 object13 object14 object15 "); 285 | 286 | // gives the spatial partitioning order within the tree 287 | resultsStream.clear(); 288 | for (auto it = rtree.lbegin(); it.valid(); it.next()) { 289 | resultsStream << (*it).name << " "; 290 | } 291 | CHECK(resultsStream.str() == "object0 object1 object2 object3 object4 object5 object6 object7 object8 object9 object10 object11 object12 object13 object14 object15 object2 object3 object12 object15 object9 object10 object11 object13 object14 object1 object7 object0 object4 object5 object6 object8 "); 292 | } 293 | 294 | SUBCASE("depth traversal of the tree") 295 | { 296 | CHECK(rtree.levels() > 0); 297 | std::stringstream resultsStream; 298 | for (auto it = rtree.dbegin(); it.valid(); it.next()) { 299 | std::string parentName; 300 | spatial::BoundingBox parentBBox; 301 | 302 | // traverse current children of the parent node(i.e. upper level) 303 | for (auto nodeIt = it.child(); nodeIt.valid(); nodeIt.next()) { 304 | resultsStream << "level: " << nodeIt.level() << " " << (*nodeIt).name << " | "; 305 | parentName += (*nodeIt).name + " + "; 306 | parentBBox.extend(nodeIt.bbox()); 307 | } 308 | (*it).name = parentName; 309 | (*it).bbox = parentBBox; 310 | resultsStream << "level: " << it.level() << " " << parentName << " \n "; 311 | } 312 | CHECK(resultsStream.str() == "level: 0 object2 | level: 0 object3 | level: 0 object12 | level: 0 object15 | level: 1 object2 + object3 + object12 + object15 + \n level: 0 object9 | level: 0 object10 | level: 0 object11 | level: 1 object9 + object10 + object11 + \n level: 0 object13 | level: 0 object14 | level: 1 object13 + object14 + \n level: 1 object2 + object3 + object12 + object15 + | level: 1 object9 + object10 + object11 + | level: 1 object13 + object14 + | level: 2 object2 + object3 + object12 + object15 + + object9 + object10 + object11 + + object13 + object14 + + \n level: 0 object1 | level: 0 object7 | level: 1 object1 + object7 + \n level: 0 object0 | level: 0 object4 | level: 1 object0 + object4 + \n level: 0 object5 | level: 0 object6 | level: 0 object8 | level: 1 object5 + object6 + object8 + \n level: 1 object1 + object7 + | level: 1 object0 + object4 + | level: 1 object5 + object6 + object8 + | level: 2 object1 + object7 + + object0 + object4 + + object5 + object6 + object8 + + \n "); 313 | } 314 | 315 | SUBCASE("special hierarchical query") 316 | { 317 | std::vector results; 318 | Box2 searchBox = { {4, 14}, {8, 31} }; 319 | rtree.hierachical_query( 320 | spatial::intersects<2>(searchBox.min, searchBox.max), 321 | std::back_inserter(results)); 322 | std::stringstream resultsStream; 323 | for (const auto& res : results) 324 | resultsStream << res.name << " | "; 325 | 326 | CHECK(resultsStream.str() == "object9 | object10 | object11 | "); 327 | } 328 | } 329 | 330 | TEST_CASE("nearest neighbor query") 331 | { 332 | spatial::RTree rtree; 333 | rtree.insert(objects.begin(), objects.end()); 334 | Point p = { {0, 0} }; 335 | SUBCASE("nearest neighbor radius query") 336 | { 337 | std::vector results; 338 | rtree.nearest(p.data, 100, std::back_inserter(results)); 339 | std::stringstream resultsStream; 340 | for (const auto& res : results) { 341 | Point center; 342 | res.bbox.center(center.data); 343 | resultsStream << res.name << " : " << p.distance(center) << " | "; 344 | } 345 | CHECK(resultsStream.str() == "object1 : 1.41421 | object7 : 2.82843 | object5 : 5.65685 | object8 : 6.7082 | object6 : 7.81025 | object0 : 10.7703 | object4 : 25.2982 | object9 : 11.4018 | object11 : 24.7588 | object10 : 35.7771 | object3 : 31.1127 | object2 : 43.6005 | object12 : 93.2952 | "); 346 | } 347 | 348 | SUBCASE("Nearest knn query:") 349 | { 350 | std::stringstream resultsStream; 351 | std::vector results; 352 | rtree.k_nearest(p.data, 3, std::back_inserter(results)); 353 | for (const auto& res : results) 354 | resultsStream << res.name << " : " << res.bbox.distance(p.data) << " | "; 355 | CHECK(resultsStream.str() == "object10 : 0 | object5 : 0 | object1 : 1.41421 | "); 356 | } 357 | } 358 | 359 | TEST_CASE("custom indexable for array and indices as values") 360 | { 361 | struct ArrayIndexable { 362 | 363 | ArrayIndexable(const std::vector>& array) : array(array) {} 364 | 365 | const int* min(const uint32_t index) const { return array[index].min; } 366 | const int* max(const uint32_t index) const { return array[index].max; } 367 | 368 | const std::vector>& array; 369 | }; 370 | 371 | ArrayIndexable indexable(boxes); 372 | 373 | typedef uint32_t IndexType; 374 | spatial::RTree rtree(indexable); 375 | 376 | std::vector indices(boxes.size()); 377 | std::iota(indices.begin(), indices.end(), 0); 378 | rtree.insert(indices.begin(), indices.end()); 379 | 380 | indices.clear(); 381 | Box2 searchBox = { {0, 0}, {8, 31} }; 382 | rtree.query(spatial::intersects<2>(searchBox.min, searchBox.max), 383 | std::back_inserter(indices)); 384 | std::stringstream resultsStream; 385 | for (auto index : indices) 386 | resultsStream << "index: " << index << " " << objects[index].name << " " << objects[index].bbox << " | "; 387 | CHECK(resultsStream.str() == "index: 9 object9 min: 3 3 max: 12 16 | index: 10 object10 min: 0 0 max: 64 32 | index: 11 object11 min: 3 2 max: 32 35 | index: 1 object1 min: 1 1 max: 2 2 | index: 7 object7 min: 2 1 max: 2 3 | index: 0 object0 min: 5 2 max: 16 7 | index: 5 object5 min: 0 0 max: 8 8 | index: 6 object6 min: 4 4 max: 6 8 | index: 8 object8 min: 4 2 max: 8 4 | "); 388 | } 389 | 390 | #if SPATIAL_TREE_ALLOCATOR == SPATIAL_TREE_DEFAULT_ALLOCATOR 391 | TEST_CASE("Custom allocator test") 392 | { 393 | struct ArrayIndexable { 394 | 395 | ArrayIndexable(const std::vector>& array) : array(array) {} 396 | 397 | const int* min(const uint32_t index) const { return array[index].min; } 398 | const int* max(const uint32_t index) const { return array[index].max; } 399 | 400 | const std::vector>& array; 401 | }; 402 | 403 | const int kMaxKeysPerNode = 4; 404 | const int kVolumeMode = spatial::box::eSphericalVolume; 405 | 406 | typedef spatial::BoundingBox tree_bbox_type; 407 | typedef spatial::detail::Node 408 | tree_node_type; 409 | typedef test::tree_allocator tree_allocator_type; 410 | 411 | typedef spatial::RTree 414 | CustomTree; 415 | 416 | ArrayIndexable indexable(boxes); 417 | std::vector indices(boxes.size() / 2); 418 | CustomTree rtree(indexable, tree_allocator_type(), true); 419 | 420 | indices.resize(boxes.size() / 2); 421 | std::iota(indices.begin(), indices.end(), 0); 422 | rtree.insert(indices.begin(), indices.end()); 423 | std::stringstream resultsStream; 424 | for (auto index : indices) 425 | resultsStream << "index: " << index << " " << objects[index].name << " " << objects[index].bbox << " | "; 426 | CHECK(resultsStream.str() == "index: 0 object0 min: 5 2 max: 16 7 | index: 1 object1 min: 1 1 max: 2 2 | index: 2 object2 min: 26 24 max: 44 28 | index: 3 object3 min: 22 21 max: 23 24 | index: 4 object4 min: 16 0 max: 32 16 | index: 5 object5 min: 0 0 max: 8 8 | index: 6 object6 min: 4 4 max: 6 8 | index: 7 object7 min: 2 1 max: 2 3 | "); 427 | } 428 | #endif 429 | --------------------------------------------------------------------------------