GetFEM  5.5
bgeot_kdtree.cc
1 /*===========================================================================
2 
3  Copyright (C) 2004-2026 Julien Pommier
4 
5  This file is a part of GetFEM
6 
7  GetFEM is free software; you can redistribute it and/or modify it
8  under the terms of the GNU Lesser General Public License as published
9  by the Free Software Foundation; either version 3 of the License, or
10  (at your option) any later version along with the GCC Runtime Library
11  Exception either version 3.1 or (at your option) any later version.
12  This program is distributed in the hope that it will be useful, but
13  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15  License and GCC Runtime Library Exception for more details.
16  You should have received a copy of the GNU Lesser General Public License
17  along with this program. If not, see https://www.gnu.org/licenses/.
18 
19 ===========================================================================*/
20 
21 
22 #include "getfem/bgeot_kdtree.h"
23 
24 namespace bgeot {
25 
26  /* leafs contains a list of points */
27  struct kdtree_leaf : public kdtree_elt_base {
28  kdtree_tab_type::const_iterator it;
29  kdtree_leaf(kdtree_tab_type::const_iterator begin,
30  kdtree_tab_type::const_iterator end) :
31  kdtree_elt_base(unsigned(std::distance(begin,end))) { it = begin; }
32  };
33 
34  struct kdtree_node : public kdtree_elt_base {
35  scalar_type split_v;
36  std::unique_ptr<kdtree_elt_base> left, right; /* left: <=v, right: >v */
37  kdtree_node(scalar_type v, std::unique_ptr<kdtree_elt_base> &&left_,
38  std::unique_ptr<kdtree_elt_base> &&right_) :
39  kdtree_elt_base(0), split_v(v),
40  left(std::move(left_)), right(std::move(right_)) {}
41  };
42 
43  typedef kdtree_tab_type::iterator ITER;
44 
45  /* sorting of kdtree_tab_type with respect to a component of the points */
46  struct component_sort {
47  unsigned dir;
48  component_sort(unsigned d) : dir(d) {}
49  bool operator()(const index_node_pair& a, const index_node_pair& b)
50  { return a.n.at(dir) < b.n.at(dir); }
51  };
52  /* splitting of kdtree_tab_type with respect to a given value */
53  /*struct component_split {
54  unsigned dir; scalar_type v;
55  component_split(unsigned d, scalar_type v_) : dir(d), v(v_) {}
56  bool operator()(const index_node_pair& a)
57  { return (a.n.at(dir) <= v); }
58  };
59  */
60 
61  static ITER partition(ITER begin, ITER end, unsigned dir, scalar_type median) {
62  --end;
63  do {
64  while (begin <= end && (*begin).n.at(dir) <= median) ++begin;
65  while (begin <= end && (*end).n.at(dir) > median) --end;
66  if (begin < end) { begin->swap(*end); ++begin; --end; } else break;
67  } while (1);
68  return begin;
69  }
70  /*
71  build (recursively) a complete tree for points in the interval
72  [begin, end[ dir is the splitting direction.
73  (may be ameliorated doing a single sort on each component at the begining)
74  */
75  static std::unique_ptr<kdtree_elt_base> build_tree_(ITER begin,
76  ITER end, unsigned dir) {
77  if (begin == end) return 0;
78  size_type npts = std::distance(begin,end);
79  if (npts > kdtree_elt_base::PTS_PER_LEAF) {
80  ITER itmedian;
81  scalar_type median;
82  size_type N = begin->n.size();
83  unsigned ndir_tests = unsigned(dir/N); dir = unsigned(dir % N);
84  if (npts > 50) {
85  /* too much points for an exact median: estimation of the median .. */
86  std::vector<index_node_pair> v(30);
87  //random_sample(begin,end,v.begin(),v.end());
88  for (size_type i=0; i < v.size(); ++i)
89  v[i] = begin[rand() % npts];
90  std::sort(v.begin(), v.end(), component_sort(dir));
91  median = (v[v.size()/2-1].n.at(dir)+v[v.size()/2].n.at(dir))/2;
92  //itmedian = std::partition(begin,end,component_split(dir, median));
93  itmedian = partition(begin,end,dir,median);
94  /*ITER it = begin; cout << "}"; while (it < end) {
95  if (it == itmedian) cout << "<|>"; else cout << " ";
96  cout << (*it).n[dir];
97  ++it;
98  } cout << "}\n";*/
99 
100  } else {
101  /* exact median computation */
102  std::sort(begin, end, component_sort(dir));
103  itmedian = begin + npts/2 - 1;
104  median = (*itmedian).n[dir];
105  while (itmedian < end && (*itmedian).n[dir] == median) itmedian++;
106  }
107  if (itmedian == end) /* could not split the set (all points have same value for component 'dir' !) */
108  if (ndir_tests == N-1) /* tested all N direction ? so all points are strictly the same */
109  return std::make_unique<kdtree_leaf>(begin,end);
110  else return std::make_unique<kdtree_node>
111  (median,
112  build_tree_(begin, itmedian, unsigned((dir+1)%N + (ndir_tests+1)*N)), std::unique_ptr<kdtree_node>());
113  else { /* the general case */
114  assert((*itmedian).n[dir] >= median && median >= (*(itmedian-1)).n[dir]);
115  return std::make_unique<kdtree_node>
116  (median, build_tree_(begin, itmedian, unsigned((dir+1)%N)),
117  build_tree_(itmedian,end, unsigned((dir+1)%N)));
118  }
119  } else {
120  return std::make_unique<kdtree_leaf>(begin,end);
121  }
122  }
123 
124  /* avoid pushing too much arguments on the stack for points_in_box_ */
125  struct points_in_box_data_ {
126  base_node::const_iterator bmin;
127  base_node::const_iterator bmax;
128  kdtree_tab_type *ipts;
129  size_type N;
130  };
131 
132  /* recursive lookup for points inside a given box */
133  static void points_in_box_(const points_in_box_data_& p,
134  const kdtree_elt_base *t, unsigned dir) {
135  if (!t->isleaf()) {
136  const kdtree_node *tn = static_cast<const kdtree_node*>(t);
137  if (p.bmin[dir] <= tn->split_v && tn->left.get())
138  points_in_box_(p, tn->left.get(), unsigned((dir+1)%p.N));
139  if (p.bmax[dir] > tn->split_v && tn->right)
140  points_in_box_(p, tn->right.get(), unsigned((dir+1)%p.N));
141  } else {
142  const kdtree_leaf *tl = static_cast<const kdtree_leaf*>(t);
143  kdtree_tab_type::const_iterator itpt = tl->it;
144  for (size_type i=tl->n;i; --i, ++itpt) {
145  bool is_in = true;
146  base_node::const_iterator it=itpt->n.const_begin();
147  for (size_type k=0; k < p.N; ++k) {
148  //cout << "test: k=" << k << ", " << it[k] << ", p.bmin[k]=" << p.bmin[k] << ", p.bmax[k]=" << p.bmax[k] << "\n";
149  if (it[k] < p.bmin[k] || it[k] > p.bmax[k]) {
150  is_in = false; break;
151  }
152  }
153  if (is_in) p.ipts->push_back(*itpt);
154  }
155  }
156  }
157 
158  /* avoid pushing too much arguments on the stack for nearest_neighbor_ */
159  struct nearest_neighbor_data_ {
160  base_node::const_iterator pos;
161  index_node_pair *ipt;
162  size_type N;
163  mutable scalar_type dist2;
164  base_node::iterator vec_to_tree_elm;
165  };
166 
167  /* recursive lookup for the nearest neighbor point at a given position */
168  static void nearest_neighbor_assist(const nearest_neighbor_data_& p,
169  const kdtree_elt_base *t, unsigned dir) {
170  scalar_type dist2(0);
171  for (size_type k=0; k < p.N; ++k)
172  dist2 += p.vec_to_tree_elm[k] * p.vec_to_tree_elm[k];
173  if (dist2 > p.dist2 && p.dist2 > scalar_type(0)) return;
174 
175  if (!t->isleaf()) {
176  const kdtree_node *tn = static_cast<const kdtree_node*>(t);
177  scalar_type tmp = p.vec_to_tree_elm[dir];
178  scalar_type dist = p.pos[dir] - tn->split_v;
179  if (tn->left.get()) {
180  if (dist > tmp) p.vec_to_tree_elm[dir] = dist;
181  nearest_neighbor_assist(p, tn->left.get(), unsigned((dir+1)%p.N));
182  p.vec_to_tree_elm[dir] = tmp;
183  }
184  if (tn->right) {
185  if (-dist > tmp) p.vec_to_tree_elm[dir] = -dist;
186  nearest_neighbor_assist(p, tn->right.get(), unsigned((dir+1)%p.N));
187  p.vec_to_tree_elm[dir] = tmp;
188  }
189  } else {
190  // find the nearest neighbor inside the leaf
191  const kdtree_leaf *tl = static_cast<const kdtree_leaf*>(t);
192  kdtree_tab_type::const_iterator itpt = tl->it;
193  for (size_type i=tl->n;i; --i, ++itpt) {
194  dist2 = scalar_type(0);
195  base_node::const_iterator it=itpt->n.const_begin();
196  for (size_type k=0; k < p.N; ++k) {
197  scalar_type dist = it[k] - p.pos[k];
198  dist2 += dist * dist;
199  }
200  if (dist2 < p.dist2 || p.dist2 < scalar_type(0)){
201  *(p.ipt) = *itpt;
202  p.dist2 = dist2;
203  }
204  }
205  }
206  }
207 
208  static void nearest_neighbor_main(const nearest_neighbor_data_& p,
209  const kdtree_elt_base *t, unsigned dir) {
210  if (!t->isleaf()) {
211  const kdtree_node *tn = static_cast<const kdtree_node*>(t);
212  scalar_type dist = p.pos[dir] - tn->split_v;
213  if ((dist <= scalar_type(0) && tn->left.get()) || !tn->right.get()) {
214  nearest_neighbor_main(p, tn->left.get(), unsigned((dir+1)%p.N));
215  } else if (tn->right.get()) {
216  nearest_neighbor_main(p, tn->right.get(), unsigned((dir+1)%p.N));
217  } else {
218  assert(false);
219  }
220  // check the possibility of points at the opposite side of the current
221  // tree node which are closer to pos as the current minimum distance
222  if (dist * dist <= p.dist2) {
223  for (size_type k=0; k < p.N; ++k) p.vec_to_tree_elm[k] = 0.;
224  if ((dist <= scalar_type(0) && tn->right.get()) || !tn->left.get()) {
225  p.vec_to_tree_elm[dir] = -dist;
226  nearest_neighbor_assist(p, tn->right.get(), unsigned((dir+1)%p.N));
227  } else if (tn->left.get()) {
228  p.vec_to_tree_elm[dir] = dist;
229  nearest_neighbor_assist(p, tn->left.get(), unsigned((dir+1)%p.N));
230  }
231  }
232  } else {
233  // find the nearest neighbor inside the leaf which contains pos
234  nearest_neighbor_assist(p, t, dir);
235  }
236  }
237 
238  void kdtree::clear_tree() { tree = std::unique_ptr<kdtree_elt_base>(); }
239 
240  void kdtree::points_in_box(kdtree_tab_type &ipts,
241  const base_node &min,
242  const base_node &max) {
243  ipts.resize(0);
244  if (tree == 0) { tree = build_tree_(pts.begin(), pts.end(), 0); if (!tree) return; }
245  base_node bmin(min), bmax(max);
246  for (size_type i=0; i < bmin.size(); ++i) if (bmin[i] > bmax[i]) return;
247  points_in_box_data_ p;
248  p.bmin = bmin.const_begin(); p.bmax = bmax.const_begin();
249  p.ipts = &ipts; p.N = N;
250  points_in_box_(p, tree.get(), 0);
251  }
252 
253  scalar_type kdtree::nearest_neighbor(index_node_pair &ipt,
254  const base_node &pos) {
255 
256  ipt.i = size_type(-1);
257  if (tree == 0) {
258  tree = build_tree_(pts.begin(), pts.end(), 0);
259  if (!tree) return scalar_type(-1);
260  }
261  nearest_neighbor_data_ p;
262  p.pos = pos.const_begin();
263  p.ipt = &ipt;
264  p.N = N;
265  p.dist2 = scalar_type(-1);
266  base_node tmp(N);
267  p.vec_to_tree_elm = tmp.begin();
268  nearest_neighbor_main(p, tree.get(), 0);
269  return p.dist2;
270  }
271 }
Simple implementation of a KD-tree.
Basic Geometric Tools.
std::vector< index_node_pair > kdtree_tab_type
store a set of points with associated indexes.
Definition: bgeot_kdtree.h:67
size_t size_type
used as the common size type in the library
Definition: bgeot_poly.h:48