diff --git a/Classification/include/CGAL/Classification/Point_set_feature_generator.h b/Classification/include/CGAL/Classification/Point_set_feature_generator.h index 1167bc9e8ca..e932b0a5975 100644 --- a/Classification/include/CGAL/Classification/Point_set_feature_generator.h +++ b/Classification/include/CGAL/Classification/Point_set_feature_generator.h @@ -161,9 +161,9 @@ private: CGAL::Real_timer t; t.start(); if (lower_grid == nullptr) - neighborhood = new Neighborhood (input, point_map); + neighborhood = new Neighborhood (input, point_map, ConcurrencyTag()); else - neighborhood = new Neighborhood (input, point_map, voxel_size); + neighborhood = new Neighborhood (input, point_map, voxel_size, ConcurrencyTag()); t.stop(); if (lower_grid == nullptr) diff --git a/Classification/include/CGAL/Classification/Point_set_neighborhood.h b/Classification/include/CGAL/Classification/Point_set_neighborhood.h index 8b5b52c7872..fbbd376b7fb 100644 --- a/Classification/include/CGAL/Classification/Point_set_neighborhood.h +++ b/Classification/include/CGAL/Classification/Point_set_neighborhood.h @@ -173,12 +173,32 @@ public: /*! \brief Constructs a neighborhood object based on the input range. + \tparam ConcurrencyTag enables sequential versus parallel + algorithm. Possible values are `Sequential_tag`, `Parallel_tag`, + and `Parallel_if_available_tag`. If no tag is provided, + `Parallel_if_available_tag` is used. + \param input point range. \param point_map property map to access the input points. */ + template Point_set_neighborhood (const PointRange& input, - PointMap point_map) + PointMap point_map, + const ConcurrencyTag&) : m_tree (nullptr) + { + init (input, point_map); + } + + /// \cond SKIP_IN_MANUAL + Point_set_neighborhood (const PointRange& input, PointMap point_map) + : m_tree (nullptr) + { + init (input, point_map); + } + + template + void init (const PointRange& input, PointMap point_map) { My_point_property_map pmap (&input, point_map); m_tree = new Tree (boost::counting_iterator (0), @@ -186,8 +206,9 @@ public: Splitter(), Search_traits (pmap)); m_distance = Distance (pmap); - m_tree->build(); + m_tree->template build(); } + /// \endcond /*! \brief Constructs a simplified neighborhood object based on the input range. @@ -197,14 +218,36 @@ public: present in one cell, only the point closest to the centroid of this subset is used. + \tparam ConcurrencyTag enables sequential versus parallel + algorithm. Possible values are `Sequential_tag`, `Parallel_tag`, + and `Parallel_if_available_tag`. If no tag is provided, + `Parallel_if_available_tag` is used. + \param input input range. \param point_map property map to access the input points. \param voxel_size size of the cells of the 3D grid used for simplification. */ + template + Point_set_neighborhood (const PointRange& input, + PointMap point_map, + float voxel_size, + const ConcurrencyTag&) + : m_tree (nullptr) + { + init (input, point_map, voxel_size); + } + + /// \cond SKIP_IN_MANUAL Point_set_neighborhood (const PointRange& input, PointMap point_map, float voxel_size) : m_tree (nullptr) + { + init (input, point_map, voxel_size); + } + + template + void init (const PointRange& input, PointMap point_map, float voxel_size) { // First, simplify std::vector indices; @@ -215,8 +258,9 @@ public: Splitter(), Search_traits (pmap)); m_distance = Distance (pmap); - m_tree->build(); + m_tree->template build(); } + /// \endcond /// @} diff --git a/Spatial_searching/doc/Spatial_searching/CGAL/Kd_tree.h b/Spatial_searching/doc/Spatial_searching/CGAL/Kd_tree.h index 92149e9fd1f..cb2da2dcea6 100644 --- a/Spatial_searching/doc/Spatial_searching/CGAL/Kd_tree.h +++ b/Spatial_searching/doc/Spatial_searching/CGAL/Kd_tree.h @@ -23,7 +23,7 @@ in a dynamically allocated array (e.g., `Epick_d` with dynamic dimension) — we says "to a lesser extent" because the points are re-created by the kd-tree in a cache-friendly order after its construction, so the coordinates are more likely to be stored in a near-optimal order on the -heap. When EnablePointsCache` is set to `Tag_true`, the points +heap. When `EnablePointsCache` is set to `Tag_true`, the points coordinates will be cached in an optimal way. This will increase memory consumption but provide better search performance. See also the `GeneralDistance` and `FuzzyQueryItem` concepts for @@ -115,7 +115,17 @@ at the first call to a query or removal member function. You can call `build()` explicitly to ensure that the next call to query functions will not trigger the reconstruction of the data structure. + +\tparam ConcurrencyTag enables sequential versus parallel +algorithm. Possible values are `Sequential_tag`, `Parallel_tag`, and +`Parallel_if_available_tag`. This template parameter is optional: +calling `build()` without specifying the concurrency tag will result +in `Sequential_tag` being used. If `build()` is not called by the user +but called implicitly at the first call to a query or removal member +function, `Sequential_tag` is also used. + */ +template void build(); /*! @@ -147,14 +157,14 @@ template void insert(InputIterator first, InputIterator be /*! Removes the point `p` from the `k-d` tree. It uses `equal_to_p` to identify the point after locating it, which can matter in particular when 2 points are -in the same place. `Identify_point` is a unary functor that takes a `Point_d` +in the same place. `IdentifyPoint` is a unary functor that takes a `Point_d` and returns a `bool`. This is a limited and naive implementation that does not rebalance the tree. On the other hand, the tree remains valid and ready for queries. If the internal data structure is not already built, for instance because the last operation was an insertion, it first calls `build()`. */ -template -void remove(Point_d p, Identify_point equal_to_p); +template +void remove(Point_d p, IdentifyPoint identify_point); /*! Removes point `p`, calling the 2-argument function `remove()` with a functor diff --git a/Spatial_searching/doc/Spatial_searching/Spatial_searching.txt b/Spatial_searching/doc/Spatial_searching/Spatial_searching.txt index fb7fa79d487..dc2eeb1b564 100644 --- a/Spatial_searching/doc/Spatial_searching/Spatial_searching.txt +++ b/Spatial_searching/doc/Spatial_searching/Spatial_searching.txt @@ -407,6 +407,23 @@ higher dimensions. \cgalExample{Spatial_searching/splitter_worst_cases.cpp} +\subsection Spatial_searchingExampleParallel Example for Parallel Neighbor Search + +In order to speed-up the construction of the `kd` tree, the child +branches of each internal node can be computed in parallel, by calling +`Kd_tree::build()`. On a quad-core processor, the +parallel construction is experimentally 2 to 3 times faster than the +sequential version, depending on the point cloud. The parallel version +requires the executable to be linked against the Intel TBB library. + +One query on the `kd` tree is purely sequential, but several queries +can be done in parallel. + +The following example shows how to build the `kd` tree in parallel and +how to perform parallel queries: + +\cgalExample{Spatial_searching/parallel_kdtree.cpp} + \section Performance Performance \subsection OrthogonalPerfomance Performance of the Orthogonal Search @@ -415,21 +432,33 @@ We took the gargoyle data set (Surface) from aim\@shape, and generated the same We then consider three scenarios as data/queries. The data set contains 800K points. For each query point we compute the K=10,20,30 closest points, with the default splitter and for the bucket size 10 (default) and 20. -The results were produced with the release 4.6 of \cgal, on an Intel i7 2.7 Ghz -laptop with 16 GB RAM, compiled with Visual C++ 2012 with the /O2 option. - -The values are the average of ten tests each. +The results were produced with the release 5.0 of \cgal, on an Intel i7 2.3 Ghz +laptop with 16 GB RAM, compiled with CLang++ 6 with the O3 option. +The values are the average of ten tests each. We show timings in seconds for both the building of the tree and the queries.
-k | bucket size | Surface/Surface | Surface/Random | Random/Random ---|------------:|-----------------:|---------------:|----------------: -10| 10 | 0.89 | 11.48 | 2.63 -10| 20 | 0.89 | 9.80 | 2.25 -20| 10 | 1.60 | 13.41 | 4.06 -20| 20 | 1.59 | 11.62 | 3.46 -30| 10 | 2.35 | 15.52 | 5.42 -30| 20 | 2.33 | 13.50 | 4.61 +k | bucket size | Surface Build | Random Build | Surface/Surface | Surface/Random | Random/Random +--|------------:|--------------:|-------------:|-----------------:|---------------:|----------------: +10| 10 | 0.17 | 0.31 | 1.13 | 15.35 | 3.40 +10| 20 | 0.14 | 0.28 | 1.09 | 12.28 | 3.00 +20| 10 | (see above) | (see above) | 1.88 | 18.25 | 5.39 +20| 20 | (see above) | (see above) | 1.81 | 14.99 | 4.51 +30| 10 | (see above) | (see above) | 2.87 | 22.62 | 7.07 +30| 20 | (see above) | (see above) | 2.66 | 18.39 | 5.68 +
+ +The same experiment is done using the parallel version of the tree building algorithm, and performing the queries in parallel too: + +
+k | bucket size | Surface Build | Random Build | Surface/Surface | Surface/Random | Random/Random +--|------------:|--------------:|-------------:|-----------------:|---------------:|----------------: +10| 10 | 0.07 | 0.12 | 0.24 | 3.52 | 0.66 +10| 20 | 0.06 | 0.12 | 0.22 | 2.87 | 0.57 +20| 10 | (see above) | (see above) | 0.41 | 4.28 | 1.02 +20| 20 | (see above) | (see above) | 0.38 | 3.43 | 0.88 +30| 10 | (see above) | (see above) | 0.58 | 4.90 | 1.44 +30| 20 | (see above) | (see above) | 0.60 | 4.28 | 1.28
\cgalFigureBegin{Spatial_searchingfigbenchmark,gargoyle.png} @@ -520,9 +549,12 @@ additional requirements when using such a cache. \section Spatial_searchingImplementationHistory Implementation History -The initial implementation of this package was done by Hans Tangelder and -Andreas Fabri. It was optimized in speed and memory consumption by Markus -Overtheil during an internship at GeometryFactory in 2014. +The initial implementation of this package was done by Hans Tangelder +and Andreas Fabri. It was optimized in speed and memory consumption by +Markus Overtheil during an internship at GeometryFactory in 2014. The +`EnablePointsCache` feature was introduced by Clément Jamin in 2019. +The parallel `kd` tree build function was introduced by Simon Giraudot +in 2020. */ } /* namespace CGAL */ diff --git a/Spatial_searching/doc/Spatial_searching/examples.txt b/Spatial_searching/doc/Spatial_searching/examples.txt index bf31eef0ad3..597aa1893f9 100644 --- a/Spatial_searching/doc/Spatial_searching/examples.txt +++ b/Spatial_searching/doc/Spatial_searching/examples.txt @@ -18,4 +18,5 @@ \example Spatial_searching/weighted_Minkowski_distance.cpp \example Spatial_searching/splitter_worst_cases.cpp \example Spatial_searching/searching_sphere_orthogonally.cpp +\example Spatial_searching/parallel_kdtree.cpp */ diff --git a/Spatial_searching/examples/Spatial_searching/CMakeLists.txt b/Spatial_searching/examples/Spatial_searching/CMakeLists.txt index a7e3bdd7983..8cf10aa93f4 100644 --- a/Spatial_searching/examples/Spatial_searching/CMakeLists.txt +++ b/Spatial_searching/examples/Spatial_searching/CMakeLists.txt @@ -76,3 +76,12 @@ else() message(STATUS "will not be compiled as they use CGAL::Epick_d which requires the Eigen library.") endif() + +find_package( TBB QUIET ) +if(TBB_FOUND) + create_single_source_cgal_program( "parallel_kdtree.cpp" ) + cgal_target_use_TBB(parallel_kdtree) +else() + message(STATUS "parallel_kdtree.cpp requires TBB and will not be compiled") +endif() + diff --git a/Spatial_searching/examples/Spatial_searching/parallel_kdtree.cpp b/Spatial_searching/examples/Spatial_searching/parallel_kdtree.cpp new file mode 100644 index 00000000000..a14612a5f22 --- /dev/null +++ b/Spatial_searching/examples/Spatial_searching/parallel_kdtree.cpp @@ -0,0 +1,56 @@ +#include +#include +#include +#include + +#include +#include + +using Kernel = CGAL::Simple_cartesian; +using Point_3 = Kernel::Point_3; + +using Traits = CGAL::Search_traits_3; +using Neighbor_search = CGAL::Orthogonal_k_neighbor_search; +using Tree = Neighbor_search::Tree; +using Point_with_distance = Neighbor_search::Point_with_transformed_distance; + +using Generator = CGAL::Random_points_in_sphere_3; + +int main() +{ + const unsigned int N = 1000; + const unsigned int k = 6; + + // Generate N points in a sphere + std::vector points; + points.reserve (N); + Generator generator; + for (unsigned int i = 0; i < N; ++ i) + points.push_back (*(generator++)); + + // Build tree in parallel + Tree tree(points.begin(), points.end()); + tree.build(); + + // Query tree in parallel + std::vector > neighbors (points.size()); + tbb::parallel_for (tbb::blocked_range (0, points.size()), + [&](const tbb::blocked_range& r) + { + for (std::size_t s = r.begin(); s != r.end(); ++ s) + { + // Neighbor search can be instantiated from + // several threads at the same time + Neighbor_search search (tree, points[s], k); + neighbors[s].reserve(k); + + // neighbor search returns a set of pair of + // point and distance , here we + // keep the points only + for (const Point_with_distance& pwd : search) + neighbors[s].push_back (pwd.first); + } + }); + + return 0; +} diff --git a/Spatial_searching/include/CGAL/Kd_tree.h b/Spatial_searching/include/CGAL/Kd_tree.h index 4694c885a95..f4cf65bd057 100644 --- a/Spatial_searching/include/CGAL/Kd_tree.h +++ b/Spatial_searching/include/CGAL/Kd_tree.h @@ -27,7 +27,6 @@ #include #include -#include #include #include @@ -35,6 +34,30 @@ #include #endif +/* + For building the KD Tree in parallel, TBB is needed. If TBB is + linked, the internal structures `deque` will be replaced by + `tbb::concurrent_vector`, even if the KD Tree is built in sequential + mode (this is to avoid changing the type of the KD Tree when + changing the concurrency mode of `build()`). + + Experimentally, using the `tbb::concurrent_vector` in sequential + mode does not trigger any loss of performance, so from a user's + point of view, it should be transparent. + + However, in case one wants to compile the KD Tree *without using TBB + structure even though CGAL is linked with TBB*, the macro + `CGAL_DISABLE_TBB_STRUCTURE_IN_KD_TREE` can be defined. In that + case, even if TBB is linked, the standard `deque` will be used + internally. Note that of course, in that case, parallel build will + be disabled. + */ +#if defined(CGAL_LINKED_WITH_TBB) && !defined(CGAL_DISABLE_TBB_STRUCTURE_IN_KD_TREE) +# include +# include +# define CGAL_TBB_STRUCTURE_IN_KD_TREE +#endif + namespace CGAL { //template , class UseExtendedNode = Tag_true > @@ -77,14 +100,13 @@ public: typedef EnablePointsCache Enable_points_cache; private: + SearchTraits traits_; Splitter split; - - // wokaround for https://svn.boost.org/trac/boost/ticket/9332 -#if (_MSC_VER == 1800) && (BOOST_VERSION == 105500) - std::deque internal_nodes; - std::deque leaf_nodes; +#if defined(CGAL_TBB_STRUCTURE_IN_KD_TREE) + tbb::concurrent_vector internal_nodes; + tbb::concurrent_vector leaf_nodes; #else boost::container::deque internal_nodes; boost::container::deque leaf_nodes; @@ -119,7 +141,6 @@ private: : traits_(tree.traits_),built_(tree.built_),dim_(-1) {}; - // Instead of the recursive construction of the tree in the class Kd_tree_node // we do this in the tree class. The advantage is that we then can optimize // the allocation of the nodes. @@ -128,50 +149,69 @@ private: Node_handle create_leaf_node(Point_container& c) { - Leaf_node node(true , static_cast(c.size())); + Leaf_node node(static_cast(c.size())); std::ptrdiff_t tmp = c.begin() - data.begin(); node.data = pts.begin() + tmp; - leaf_nodes.push_back(node); - Leaf_node_handle nh = &leaf_nodes.back(); - - - return nh; +#ifdef CGAL_TBB_STRUCTURE_IN_KD_TREE + return &*(leaf_nodes.push_back(node)); +#else + leaf_nodes.emplace_back (node); + return &(leaf_nodes.back()); +#endif } - // The internal node - - Node_handle - create_internal_node(Point_container& c, const Tag_true&) + Node_handle new_internal_node() { - return create_internal_node_use_extension(c); +#ifdef CGAL_TBB_STRUCTURE_IN_KD_TREE + return &*(internal_nodes.push_back(Internal_node())); +#else + internal_nodes.emplace_back (); + return &(internal_nodes.back()); +#endif } - Node_handle - create_internal_node(Point_container& c, const Tag_false&) - { - return create_internal_node(c); - } - - - // TODO: Similiar to the leaf_init function above, a part of the code should be // moved to a the class Kd_tree_node. // It is not proper yet, but the goal was to see if there is // a potential performance gain through the Compact_container - Node_handle - create_internal_node_use_extension(Point_container& c) + template + void + create_internal_node(Node_handle n, Point_container& c, const ConcurrencyTag& tag) { - Internal_node node(false); - internal_nodes.push_back(node); - Internal_node_handle nh = &internal_nodes.back(); + Internal_node_handle nh = static_cast(n); + CGAL_assertion (nh != nullptr); Separator sep; Point_container c_low(c.dimension(),traits_); split(sep, c, c_low); nh->set_separator(sep); + handle_extended_node (nh, c, c_low, UseExtendedNode()); + + if (try_parallel_internal_node_creation (nh, c, c_low, tag)) + return; + + if (c_low.size() > split.bucket_size()) + { + nh->lower_ch = new_internal_node(); + create_internal_node (nh->lower_ch, c_low, tag); + } + else + nh->lower_ch = create_leaf_node(c_low); + + if (c.size() > split.bucket_size()) + { + nh->upper_ch = new_internal_node(); + create_internal_node (nh->upper_ch, c, tag); + } + else + nh->upper_ch = create_leaf_node(c); + } + + void handle_extended_node (Internal_node_handle nh, Point_container& c, Point_container& c_low, const Tag_true&) + { int cd = nh->cutting_dimension(); if(!c_low.empty()){ nh->lower_low_val = c_low.tight_bounding_box().min_coord(cd); @@ -192,56 +232,45 @@ private: CGAL_assertion(nh->cutting_value() >= nh->lower_low_val); CGAL_assertion(nh->cutting_value() <= nh->upper_high_val); - - if (c_low.size() > split.bucket_size()){ - nh->lower_ch = create_internal_node_use_extension(c_low); - }else{ - nh->lower_ch = create_leaf_node(c_low); - } - if (c.size() > split.bucket_size()){ - nh->upper_ch = create_internal_node_use_extension(c); - }else{ - nh->upper_ch = create_leaf_node(c); - } - - - - - return nh; } + inline void handle_extended_node (Internal_node_handle, Point_container&, Point_container&, const Tag_false&) { } - // Note also that I duplicated the code to get rid if the if's for - // the boolean use_extension which was constant over the construction - Node_handle - create_internal_node(Point_container& c) + inline bool try_parallel_internal_node_creation (Internal_node_handle, Point_container&, + Point_container&, const Sequential_tag&) { - Internal_node node(false); - internal_nodes.push_back(node); - Internal_node_handle nh = &internal_nodes.back(); - Separator sep; - - Point_container c_low(c.dimension(),traits_); - split(sep, c, c_low); - nh->set_separator(sep); - - if (c_low.size() > split.bucket_size()){ - nh->lower_ch = create_internal_node(c_low); - }else{ - nh->lower_ch = create_leaf_node(c_low); - } - if (c.size() > split.bucket_size()){ - nh->upper_ch = create_internal_node(c); - }else{ - nh->upper_ch = create_leaf_node(c); - } - - - - return nh; + return false; } +#ifdef CGAL_TBB_STRUCTURE_IN_KD_TREE + inline bool try_parallel_internal_node_creation (Internal_node_handle nh, Point_container& c, + Point_container& c_low, const Parallel_tag& tag) + { + /* + The two child branches are computed in parallel if and only if: + + * both branches lead to internal nodes (if at least one branch + is a leaf, it's useless) + + * the current number of points is sufficiently high to be worth + the cost of launching new threads. Experimentally, using 10 + times the bucket size as a limit gives the best timings. + */ + if (c_low.size() > split.bucket_size() && c.size() > split.bucket_size() + && (c_low.size() + c.size() > 10 * split.bucket_size())) + { + nh->lower_ch = new_internal_node(); + nh->upper_ch = new_internal_node(); + tbb::parallel_invoke (std::bind (&Self::create_internal_node, this, nh->lower_ch, std::ref(c_low), std::cref(tag)), + std::bind (&Self::create_internal_node, this, nh->upper_ch, std::ref(c), std::cref(tag))); + return true; + } + + return false; + } + +#endif public: @@ -261,6 +290,32 @@ public: return pts.empty(); } + void build() + { + build(); + } + + /* + Note about parallel `build()`. Several different strategies have + been tried, among which: + + * keeping the `deque` and using mutex structures to secure the + insertions in them + * using free stand-alone pointers generated with `new` instead of + pushing elements in a container + * using a global `tbb::task_group` to handle the internal node + computations + * using one `tbb::task_group` per internal node to handle the + internal node computations + + Experimentally, the options giving the best timings is the one + kept, namely: + + * nodes are stored in `tbb::concurrent_vector` structures + * the parallel computations are launched using + `tbb::parallel_invoke` + */ + template void build() { @@ -277,12 +332,19 @@ public: for(unsigned int i = 0; i < pts.size(); i++){ data.push_back(&pts[i]); } + +#ifndef CGAL_TBB_STRUCTURE_IN_KD_TREE + CGAL_static_assertion_msg (!(boost::is_convertible::value), + "Parallel_tag is enabled but TBB is unavailable."); +#endif + Point_container c(dim_, data.begin(), data.end(),traits_); bbox = new Kd_tree_rectangle(c.bounding_box()); if (c.size() <= split.bucket_size()){ tree_root = create_leaf_node(c); }else { - tree_root = create_internal_node(c, UseExtendedNode()); + tree_root = new_internal_node(); + create_internal_node (tree_root, c, ConcurrencyTag()); } //Reorder vector for spatial locality diff --git a/Spatial_searching/include/CGAL/Kd_tree_node.h b/Spatial_searching/include/CGAL/Kd_tree_node.h index 270a95b9e91..41b7e11777d 100644 --- a/Spatial_searching/include/CGAL/Kd_tree_node.h +++ b/Spatial_searching/include/CGAL/Kd_tree_node.h @@ -51,15 +51,12 @@ namespace CGAL { typedef typename Kdt::iterator iterator; typedef typename Kdt::D D; - bool leaf; - public : - Kd_tree_node(bool leaf_) - :leaf(leaf_){} + Kd_tree_node() { } - bool is_leaf() const{ - return leaf; - } + virtual ~Kd_tree_node() { } + + virtual bool is_leaf() const = 0; std::size_t num_items() const @@ -97,8 +94,8 @@ namespace CGAL { Internal_node_const_handle node = static_cast(this); return - (std::max)( node->lower()->depth(current_max_depth + 1), - node->upper()->depth(current_max_depth + 1)); + (std::max)( node->lower()->depth(current_max_depth + 1), + node->upper()->depth(current_max_depth + 1)); } } @@ -112,17 +109,17 @@ namespace CGAL { OutputIterator tree_items(OutputIterator it) const { if (is_leaf()) { - Leaf_node_const_handle node = + Leaf_node_const_handle node = static_cast(this); - if (node->size()>0) - for (iterator i=node->begin(); i != node->end(); i++) - {*it=*i; ++it;} - } + if (node->size()>0) + for (iterator i=node->begin(); i != node->end(); i++) + {*it=*i; ++it;} + } else { - Internal_node_const_handle node = + Internal_node_const_handle node = static_cast(this); - it=node->lower()->tree_items(it); - it=node->upper()->tree_items(it); + it=node->lower()->tree_items(it); + it=node->upper()->tree_items(it); } return it; } @@ -206,20 +203,20 @@ namespace CGAL { else { Internal_node_const_handle node = static_cast(this); - // after splitting b denotes the lower part of b - Kd_tree_rectangle b_upper(b); - node->split_bbox(b, b_upper); + // after splitting b denotes the lower part of b + Kd_tree_rectangle b_upper(b); + node->split_bbox(b, b_upper); - if (q.outer_range_contains(b)) - it=node->lower()->tree_items(it); - else - if (q.inner_range_intersects(b)) - it=node->lower()->search(it,q,b,tree_points_begin,cache_begin,dim); - if (q.outer_range_contains(b_upper)) - it=node->upper()->tree_items(it); - else - if (q.inner_range_intersects(b_upper)) - it=node->upper()->search(it,q,b_upper,tree_points_begin,cache_begin,dim); + if (q.outer_range_contains(b)) + it=node->lower()->tree_items(it); + else + if (q.inner_range_intersects(b)) + it=node->lower()->search(it,q,b,tree_points_begin,cache_begin,dim); + if (q.outer_range_contains(b_upper)) + it=node->upper()->tree_items(it); + else + if (q.inner_range_intersects(b_upper)) + it=node->upper()->search(it,q,b_upper,tree_points_begin,cache_begin,dim); }; return it; } @@ -398,13 +395,13 @@ namespace CGAL { Kd_tree_leaf_node() {} - Kd_tree_leaf_node(bool leaf_ ) - : Base(leaf_) + Kd_tree_leaf_node(unsigned int n_ ) + : n(n_) {} - Kd_tree_leaf_node(bool leaf_,unsigned int n_ ) - : Base(leaf_), n(n_) - {} + virtual ~Kd_tree_leaf_node() { } + + virtual bool is_leaf() const { return true; } // members for all nodes @@ -475,12 +472,15 @@ namespace CGAL { // default constructor Kd_tree_internal_node() + : cut_dim(-1), cut_val(0) + , lower_ch (nullptr), upper_ch (nullptr) + , upper_low_val(0), upper_high_val(0) + , lower_low_val(0), lower_high_val(0) {} - Kd_tree_internal_node(bool leaf_) - : Base(leaf_) - {} + virtual ~Kd_tree_internal_node() { } + virtual bool is_leaf() const { return false; } // members for internal node and extended internal node @@ -621,9 +621,7 @@ namespace CGAL { Kd_tree_internal_node() {} - Kd_tree_internal_node(bool leaf_) - : Base(leaf_) - {} + virtual bool is_leaf() const { return false; } // members for internal node and extended internal node