// Copyright (c) 2002,2011,2014 Utrecht University (The Netherlands), Max-Planck-Institute Saarbruecken (Germany). // All rights reserved. // // This file is part of CGAL (www.cgal.org). // You can redistribute it and/or modify it under the terms of the GNU // General Public License as published by the Free Software Foundation, // either version 3 of the License, or (at your option) any later version. // // Licensees holding a valid commercial license may use this file in // accordance with the commercial license agreement provided with the software. // // This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE // WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. // // $URL$ // $Id$ // SPDX-License-Identifier: GPL-3.0+ // // Author(s) : Hans Tangelder (), // : Waqar Khan #ifndef CGAL_KD_TREE_H #define CGAL_KD_TREE_H #include #include #include #include #include #include #include #include #include #include #include #include #ifdef CGAL_HAS_THREADS #include #endif namespace CGAL { //template , class UseExtendedNode = Tag_true > template , class UseExtendedNode = Tag_true > class Kd_tree { public: typedef SearchTraits Traits; typedef Splitter_ Splitter; typedef typename SearchTraits::Point_d Point_d; typedef typename Splitter::Container Point_container; typedef typename SearchTraits::FT FT; typedef Kd_tree_node Node; typedef Kd_tree_leaf_node Leaf_node; typedef Kd_tree_internal_node Internal_node; typedef Kd_tree Tree; typedef Kd_tree Self; typedef Node* Node_handle; typedef const Node* Node_const_handle; typedef Leaf_node* Leaf_node_handle; typedef const Leaf_node* Leaf_node_const_handle; typedef Internal_node* Internal_node_handle; typedef const Internal_node* Internal_node_const_handle; typedef typename std::vector::const_iterator Point_d_iterator; typedef typename std::vector::const_iterator Point_d_const_iterator; typedef typename Splitter::Separator Separator; typedef typename std::vector::const_iterator iterator; typedef typename std::vector::const_iterator const_iterator; typedef typename std::vector::size_type size_type; typedef typename internal::Get_dimension_tag::Dimension D; 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; #else boost::container::deque internal_nodes; boost::container::deque leaf_nodes; #endif Node_handle tree_root; Kd_tree_rectangle* bbox; std::vector pts; // Instead of storing the points in arrays in the Kd_tree_node // we put all the data in a vector in the Kd_tree. // and we only store an iterator range in the Kd_tree_node. // std::vector data; #ifdef CGAL_HAS_THREADS mutable CGAL_MUTEX building_mutex;//mutex used to protect const calls inducing build() #endif bool built_; bool removed_; // protected copy constructor Kd_tree(const Tree& tree) : traits_(tree.traits_),built_(tree.built_) {}; // 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. // The leaf node Node_handle create_leaf_node(Point_container& c) { Leaf_node node(true , 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; } // The internal node Node_handle create_internal_node(Point_container& c, const Tag_true&) { return create_internal_node_use_extension(c); } 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) { 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); int cd = nh->cutting_dimension(); if(!c_low.empty()){ nh->lower_low_val = c_low.tight_bounding_box().min_coord(cd); nh->lower_high_val = c_low.tight_bounding_box().max_coord(cd); } else{ nh->lower_low_val = nh->cutting_value(); nh->lower_high_val = nh->cutting_value(); } if(!c.empty()){ nh->upper_low_val = c.tight_bounding_box().min_coord(cd); nh->upper_high_val = c.tight_bounding_box().max_coord(cd); } else{ nh->upper_low_val = nh->cutting_value(); nh->upper_high_val = nh->cutting_value(); } 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; } // 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) { 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; } public: Kd_tree(Splitter s = Splitter(),const SearchTraits traits=SearchTraits()) : traits_(traits),split(s), built_(false), removed_(false) {} template Kd_tree(InputIterator first, InputIterator beyond, Splitter s = Splitter(),const SearchTraits traits=SearchTraits()) : traits_(traits),split(s), built_(false), removed_(false) { pts.insert(pts.end(), first, beyond); } bool empty() const { return pts.empty(); } void build() { // This function is not ready to be called when a tree already exists, one // must call invalidate_build() first. CGAL_assertion(!is_built()); CGAL_assertion(!pts.empty()); CGAL_assertion(!removed_); const Point_d& p = *pts.begin(); typename SearchTraits::Construct_cartesian_const_iterator_d ccci=traits_.construct_cartesian_const_iterator_d_object(); int dim = static_cast(std::distance(ccci(p), ccci(p,0))); data.reserve(pts.size()); for(unsigned int i = 0; i < pts.size(); i++){ data.push_back(&pts[i]); } 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()); } //Reorder vector for spatial locality std::vector ptstmp; ptstmp.resize(pts.size()); for (std::size_t i = 0; i < pts.size(); ++i){ ptstmp[i] = *data[i]; } for(std::size_t i = 0; i < leaf_nodes.size(); ++i){ std::ptrdiff_t tmp = leaf_nodes[i].begin() - pts.begin(); leaf_nodes[i].data = ptstmp.begin() + tmp; } pts.swap(ptstmp); data.clear(); built_ = true; } private: //any call to this function is for the moment not threadsafe void const_build() const { #ifdef CGAL_HAS_THREADS //this ensure that build() will be called once CGAL_SCOPED_LOCK(building_mutex); if(!is_built()) #endif const_cast(this)->build(); //THIS IS NOT THREADSAFE } public: bool is_built() const { return built_; } void invalidate_build() { if(removed_){ // Walk the tree to collect the remaining points. // Writing directly to pts would likely work, but better be safe. std::vector ptstmp; //ptstmp.resize(root()->num_items()); root()->tree_items(std::back_inserter(ptstmp)); pts.swap(ptstmp); removed_=false; CGAL_assertion(is_built()); // the rest of the cleanup must happen } if(is_built()){ internal_nodes.clear(); leaf_nodes.clear(); data.clear(); delete bbox; built_ = false; } } void clear() { invalidate_build(); pts.clear(); removed_ = false; } void insert(const Point_d& p) { invalidate_build(); pts.push_back(p); } template void insert(InputIterator first, InputIterator beyond) { invalidate_build(); pts.insert(pts.end(),first, beyond); } private: struct Equal_by_coordinates { SearchTraits const* traits; Point_d const* pp; bool operator()(Point_d const&q) const { typename SearchTraits::Construct_cartesian_const_iterator_d ccci=traits->construct_cartesian_const_iterator_d_object(); return std::equal(ccci(*pp), ccci(*pp,0), ccci(q)); } }; Equal_by_coordinates equal_by_coordinates(Point_d const&p){ Equal_by_coordinates ret = { &traits(), &p }; return ret; } public: void remove(const Point_d& p) { remove(p, equal_by_coordinates(p)); } template void remove(const Point_d& p, Equal const& equal_to_p) { #if 0 // This code could have quadratic runtime. if (!is_built()) { std::vector::iterator pi = std::find_if(pts.begin(), pts.end(), equal_to_p); // Precondition: the point must be there. CGAL_assertion (pi != pts.end()); pts.erase(pi); return; } #endif bool success = remove_(p, 0, false, 0, false, root(), equal_to_p); CGAL_assertion(success); // Do not set the flag is the tree has been cleared. if(is_built()) removed_ |= success; } private: template bool remove_(const Point_d& p, Internal_node_handle grandparent, bool parent_islower, Internal_node_handle parent, bool islower, Node_handle node, Equal const& equal_to_p) { // Recurse to locate the point if (!node->is_leaf()) { Internal_node_handle newparent = static_cast(node); // FIXME: This should be if(xcutting_dimension()] <= newparent->cutting_value()) { if (remove_(p, parent, islower, newparent, true, newparent->lower(), equal_to_p)) return true; } //if (traits().construct_cartesian_const_iterator_d_object()(p)[newparent->cutting_dimension()] >= newparent->cutting_value()) return remove_(p, parent, islower, newparent, false, newparent->upper(), equal_to_p); } // Actual removal Leaf_node_handle lnode = static_cast(node); if (lnode->size() > 1) { iterator pi = std::find_if(lnode->begin(), lnode->end(), equal_to_p); // FIXME: we should ensure this never happens if (pi == lnode->end()) return false; iterator lasti = lnode->end() - 1; if (pi != lasti) { // Hack to get a non-const iterator std::iter_swap(pts.begin()+(pi-pts.begin()), pts.begin()+(lasti-pts.begin())); } lnode->drop_last_point(); } else if (!equal_to_p(*lnode->begin())) { // FIXME: we should ensure this never happens return false; } else if (grandparent) { Node_handle brother = islower ? parent->upper() : parent->lower(); if (parent_islower) grandparent->set_lower(brother); else grandparent->set_upper(brother); } else if (parent) { tree_root = islower ? parent->upper() : parent->lower(); } else { clear(); } return true; } public: //For efficiency; reserve the size of the points vectors in advance (if the number of points is already known). void reserve(size_t size) { pts.reserve(size); } //Get the capacity of the underlying points vector. size_t capacity() { return pts.capacity(); } template OutputIterator search(OutputIterator it, const FuzzyQueryItem& q) const { if(! pts.empty()){ if(! is_built()){ const_build(); } Kd_tree_rectangle b(*bbox); return tree_root->search(it,q,b); } return it; } template boost::optional search_any_point(const FuzzyQueryItem& q) const { if(! pts.empty()){ if(! is_built()){ const_build(); } Kd_tree_rectangle b(*bbox); return tree_root->search_any_point(q,b); } return boost::none; } ~Kd_tree() { if(is_built()){ delete bbox; } } const SearchTraits& traits() const { return traits_; } Node_const_handle root() const { if(! is_built()){ const_build(); } return tree_root; } Node_handle root() { if(! is_built()){ build(); } return tree_root; } void print() const { if(! pts.empty()){ if(! is_built()){ const_build(); } root()->print(); }else{ std::cout << "empty tree\n"; } } const Kd_tree_rectangle& bounding_box() const { if(! is_built()){ const_build(); } return *bbox; } const_iterator begin() const { return pts.begin(); } const_iterator end() const { return pts.end(); } size_type size() const { return pts.size(); } // Print statistics of the tree. std::ostream& statistics(std::ostream& s) const { if(! is_built()){ const_build(); } s << "Tree statistics:" << std::endl; s << "Number of items stored: " << root()->num_items() << std::endl; s << "Number of nodes: " << root()->num_nodes() << std::endl; s << " Tree depth: " << root()->depth() << std::endl; return s; } }; } // namespace CGAL #include #endif // CGAL_KD_TREE_H