// Copyright (c) 2010-2023, Lawrence Livermore National Security, LLC. Produced // at the Lawrence Livermore National Laboratory. All Rights reserved. See files // LICENSE and NOTICE for details. LLNL-CODE-806117. // // This file is part of the MFEM library. For more information and source code // availability visit https://mfem.org. // // MFEM is free software; you can redistribute it and/or modify it under the // terms of the BSD-3 license. We welcome feedback and contributions, see file // CONTRIBUTING.md for details #ifndef MFEM_KDTREE_HPP #define MFEM_KDTREE_HPP #include #include #include #include #include #include namespace mfem { namespace KDTreeNorms { /// Evaluates l1 norm of a vector. template struct Norm_l1 { Tfloat operator() (const Tfloat* xx) { Tfloat tm=abs(xx[0]); for (int i=1; i struct Norm_l2 { Tfloat operator() (const Tfloat* xx) { Tfloat tm; tm=xx[0]*xx[0]; for (int i=1; i struct Norm_li { Tfloat operator() (const Tfloat* xx) { Tfloat tm; if (xx[0] > class KDTree { public: /// Structure defining a geometric point in the ndim-dimensional /// space. The coordinate type (Tfloat) can be any floating or /// integer type. It can be even a character if necessary. For /// such types users should redefine the norms. struct PointND { /// Geometric point constructor PointND() { std::fill(xx,xx+ndim,Tfloat(0.0));} /// Coordinates of the point Tfloat xx[ndim]; }; /// Structure defining a node in the KDTree. struct NodeND { /// Defines a point in the ndim-dimensional space PointND pt; /// Defines the attached index Tindex ind; }; /// Default constructor KDTree() = default; /// Returns the spatial dimension of the points int SpaceDimension() { return ndim; } /// Data iterator typedef typename std::vector::iterator iterator; /// Returns iterator to beginning of the point cloud iterator begin() { return data.begin(); } /// Returns iterator to the end of the point cloud iterator end() { return data.end(); } /// Returns the size of the point cloud size_t size() { return data.size(); } /// Clears the point cloud void clear() { data.clear(); } /// Builds the KDTree. If the point cloud is modified the tree /// needs to be rebuild by a new call to Sort(). void Sort() { SortInPlace(data.begin(),data.end(),0); } /// Adds a new node to the point cloud void AddPoint(PointND& pt, Tindex ii) { NodeND nd; nd.pt=pt; nd.ind=ii; data.push_back(nd); } /// Adds a new node by coordinates and an associated index void AddPoint(Tfloat* xx,Tindex ii) { NodeND nd; for (size_t i=0; ipt, best_candidate.sp); if (dd & res, std::vector & dist) { FindNeighborPoints(pt,R,data.begin(),data.end(),0,res,dist); } /// Finds all points within a distance R from point pt. The indices are /// returned in the vector res and the correponding distances in vector dist. void FindNeighborPoints(PointND& pt,Tfloat R, std::vector & res) { FindNeighborPoints(pt,R,data.begin(),data.end(),0,res); } /// Brute force search - please, use it only for debuging purposes void FindNeighborPointsSlow(PointND& pt,Tfloat R, std::vector & res, std::vector & dist) { Tfloat dd; for (auto iti=data.begin(); iti!=data.end(); iti++) { dd=Dist(iti->pt, pt); if (ddind); dist.push_back(dd); } } } /// Brute force search - please, use it only for debuging purposes void FindNeighborPointsSlow(PointND& pt,Tfloat R, std::vector & res) { Tfloat dd; for (auto iti=data.begin(); iti!=data.end(); iti++) { dd=Dist(iti->pt, pt); if (ddind); } } } private: /// Functor utilized in the coordinate comparison /// for building the KDTree struct CompN { /// Current coordinate index std::uint8_t dim; /// Constructor for the comparison CompN(std::uint8_t dd):dim(dd) {} /// Compares two points p1 and p2 bool operator() (const PointND& p1, const PointND& p2) { return p1.xx[dim] data; /// Finds the median for a sequence of nodes starting with itb /// and ending with ite. The current coordinate index is set by cdim. Tfloat FindMedian(typename std::vector::iterator itb, typename std::vector::iterator ite, std::uint8_t cdim) { size_t siz=ite-itb; std::nth_element(itb, itb+siz/2, ite, CompN(cdim)); return itb->pt.xx[cdim]; } /// Sorts the point cloud void SortInPlace(typename std::vector::iterator itb, typename std::vector::iterator ite, size_t level) { std::uint8_t cdim=(std::uint8_t)(level%ndim); size_t siz=ite-itb; if (siz>2) { std::nth_element(itb, itb+siz/2, ite, CompN(cdim)); level=level+1; SortInPlace(itb, itb+siz/2, level); SortInPlace(itb+siz/2+1,ite, level); } } /// Structure utilized for nearest neighbor search (NNS) struct PointS { Tfloat dist; size_t pos; size_t level; PointND sp; }; /// Finds the closest point to bc.sp in the point cloud /// bounded between [itb,ite). void PSearch(typename std::vector::iterator itb, typename std::vector::iterator ite, size_t level, PointS& bc) { std::uint8_t dim=(std::uint8_t) (level%ndim); size_t siz=ite-itb; typename std::vector::iterator mtb=itb+siz/2; if (siz>2) { // median is at itb+siz/2 level=level+1; if ((bc.sp.xx[dim]-bc.dist)>mtb->pt.xx[dim]) // look on the right only { PSearch(itb+siz/2+1, ite, level, bc); } else if ((bc.sp.xx[dim]+bc.dist)pt.xx[dim]) // look on the left only { PSearch(itb,itb+siz/2, level, bc); } else // check all { if (bc.sp.xx[dim]pt.xx[dim]) { // start with the left portion PSearch(itb,itb+siz/2, level, bc); // and continue to the right if (!((bc.sp.xx[dim]+bc.dist)pt.xx[dim])) { PSearch(itb+siz/2+1, ite, level, bc); { // check central one Tfloat dd=Dist(mtb->pt, bc.sp); if (ddmtb->pt.xx[dim])) { PSearch(itb, itb+siz/2, level, bc); { // check central one Tfloat dd=Dist(mtb->pt, bc.sp); if (ddpt, bc.sp); if (dd::iterator itb, typename std::vector::iterator ite, size_t level, std::vector< std::tuple > & res) { std::uint8_t dim=(std::uint8_t) (level%ndim); size_t siz=ite-itb; typename std::vector::iterator mtb=itb+siz/2; if (siz>2) { // median is at itb+siz/2 level=level+1; Tfloat R=std::get<0>(res[npoints-1]); // check central one Tfloat dd=Dist(mtb->pt, pt); if (ddind); std::nth_element(res.begin(), res.end()-1, res.end()); R=std::get<0>(res[npoints-1]); } if ((pt.xx[dim]-R)>mtb->pt.xx[dim]) // look to the right only { NNS(pt, npoints, itb+siz/2+1, ite, level, res); } else if ((pt.xx[dim]+R)pt.xx[dim]) // look to the left only { NNS(pt, npoints, itb, itb+siz/2, level, res); } else // check all { NNS(pt,npoints, itb+siz/2+1, ite, level, res); // right NNS(pt,npoints, itb, itb+siz/2, level, res); // left } } else { Tfloat dd; for (auto it=itb; it!=ite; it++) { dd=Dist(it->pt, pt); if (dd< std::get<0>(res[npoints-1])) // update the list { res[npoints-1]=std::make_tuple(dd,it->ind); std::nth_element(res.begin(), res.end()-1, res.end()); } } } } /// Finds the set of indices of points within a distance R of a point pt. void FindNeighborPoints(PointND& pt, Tfloat R, typename std::vector::iterator itb, typename std::vector::iterator ite, size_t level, std::vector & res) { std::uint8_t dim=(std::uint8_t) (level%ndim); size_t siz=ite-itb; typename std::vector::iterator mtb=itb+siz/2; if (siz>2) { // median is at itb+siz/2 level=level+1; if ((pt.xx[dim]-R)>mtb->pt.xx[dim]) // look to the right only { FindNeighborPoints(pt, R, itb+siz/2+1, ite, level, res); } else if ((pt.xx[dim]+R)pt.xx[dim]) // look to the left only { FindNeighborPoints(pt,R, itb, itb+siz/2, level, res); } else //check all { FindNeighborPoints(pt,R, itb+siz/2+1, ite, level, res); // right FindNeighborPoints(pt,R, itb, itb+siz/2, level, res); // left // check central one Tfloat dd=Dist(mtb->pt, pt); if (ddind); } } } else { Tfloat dd; for (auto it=itb; it!=ite; it++) { dd=Dist(it->pt, pt); if (ddind); } } } } /// Finds the set of indices of points within a distance R of a point pt. void FindNeighborPoints(PointND& pt, Tfloat R, typename std::vector::iterator itb, typename std::vector::iterator ite, size_t level, std::vector & res, std::vector & dist) { std::uint8_t dim=(std::uint8_t) (level%ndim); size_t siz=ite-itb; typename std::vector::iterator mtb=itb+siz/2; if (siz>2) { // median is at itb+siz/2 level=level+1; if ((pt.xx[dim]-R)>mtb->pt.xx[dim]) // look to the right only { FindNeighborPoints(pt, R, itb+siz/2+1, ite, level, res, dist); } else if ((pt.xx[dim]+R)pt.xx[dim]) // look to the left only { FindNeighborPoints(pt,R, itb, itb+siz/2, level, res, dist); } else // check all { FindNeighborPoints(pt,R, itb+siz/2+1, ite, level, res, dist); // right FindNeighborPoints(pt,R, itb, itb+siz/2, level, res, dist); // left // check central one Tfloat dd=Dist(mtb->pt, pt); if (ddind); dist.push_back(dd); } } } else { Tfloat dd; for (auto it=itb; it!=ite; it++) { dd=Dist(it->pt, pt); if (ddind); dist.push_back(dd); } } } } }; /// Defines KDTree in 3D typedef KDTree KDTree3D; /// Defines KDTree in 2D typedef KDTree KDTree2D; /// Defines KDTree in 1D typedef KDTree KDTree1D; } // namespace mfem #endif // MFEM_KDTREE_HPP