GetFEM  5.5
bgeot_rtree.cc
1 /*===========================================================================
2 
3  Copyright (C) 2000-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 #include "getfem/bgeot_rtree.h"
22 
23 namespace bgeot {
24 
25  struct rtree_node : public rtree_elt_base {
26  std::unique_ptr<rtree_elt_base> left, right;
27  rtree_node(const base_node& bmin, const base_node& bmax,
28  std::unique_ptr<rtree_elt_base> &&left_,
29  std::unique_ptr<rtree_elt_base> &&right_)
30  : rtree_elt_base(false, bmin, bmax), left(std::move(left_)),
31  right( std::move(right_)) { }
32  };
33 
34  struct rtree_leaf : public rtree_elt_base {
35  rtree::pbox_cont lst;
36  rtree_leaf(const base_node& bmin, const base_node& bmax,
37  rtree::pbox_cont& lst_)
38  : rtree_elt_base(true, bmin, bmax) { lst.swap(lst_); }
39  };
40 
41  /* enlarge box to hold [a..b] */
42  static void update_box(base_node& bmin, base_node& bmax,
43  const base_node& a, const base_node& b) {
44  base_node::iterator itmin=bmin.begin(), itmax=bmax.begin();
45  for (size_type i=0; i < a.size(); ++i) {
46  itmin[i] = std::min(itmin[i], a.at(i));
47  itmax[i] = std::max(itmax[i], b.at(i));
48  }
49  }
50 
51  inline static bool r1_ge_r2(const base_node& min1, const base_node& max1,
52  const base_node& min2, const base_node& max2,
53  scalar_type EPS) {
54  for (size_type i=0; i < min1.size(); ++i)
55  if ((min1[i] > min2[i]+EPS) || (max1[i] < max2[i]-EPS)) return false;
56  return true;
57  }
58 
59  inline static bool r1_inter_r2(const base_node& min1, const base_node& max1,
60  const base_node& min2, const base_node& max2,
61  scalar_type EPS) {
62  for (size_type i=0; i < min1.size(); ++i)
63  if ((max1[i] < min2[i]-EPS) || (min1[i] > max2[i]+EPS)) return false;
64  return true;
65  }
66 
67  /* some predicates for searches */
68  struct intersection_p {
69  const base_node &min, &max;
70  const scalar_type EPS;
71  intersection_p(const base_node& min_, const base_node& max_, scalar_type EPS_)
72  : min(min_), max(max_), EPS(EPS_) {}
73  bool operator()(const base_node& min2, const base_node& max2) const
74  { return r1_inter_r2(min,max,min2,max2,EPS); }
75  bool accept(const base_node& min2, const base_node& max2) const
76  { return operator()(min2,max2); }
77  };
78 
79  /* match boxes containing [min..max] */
80  struct contains_p {
81  const base_node &min, &max;
82  const scalar_type EPS;
83  contains_p(const base_node& min_, const base_node& max_, scalar_type EPS_)
84  : min(min_), max(max_), EPS(EPS_) {}
85  bool operator()(const base_node& min2, const base_node& max2) const
86  { return r1_ge_r2(min2,max2,min,max,EPS); }
87  bool accept(const base_node& min2, const base_node& max2) const
88  { return r1_inter_r2(min,max,min2,max2,EPS); }
89  };
90 
91  /* match boxes contained in [min..max] */
92  struct contained_p {
93  const base_node &min, &max;
94  const scalar_type EPS;
95  contained_p(const base_node& min_, const base_node& max_, scalar_type EPS_)
96  : min(min_), max(max_), EPS(EPS_) {}
97  bool accept(const base_node& min2, const base_node& max2) const
98  { return r1_inter_r2(min,max,min2,max2,EPS); }
99  bool operator()(const base_node& min2, const base_node& max2) const
100  { return r1_ge_r2(min,max,min2,max2,EPS); }
101  };
102 
103  /* match boxes containing P */
104  struct has_point_p {
105  const base_node &P;
106  const scalar_type EPS;
107  has_point_p(const base_node& P_, scalar_type EPS_) : P(P_), EPS(EPS_) {}
108  bool operator()(const base_node& min2, const base_node& max2) const {
109  for (size_type i = 0; i < P.size(); ++i) {
110  if (P[i] < min2[i]-EPS) return false;
111  if (P[i] > max2[i]+EPS) return false;
112  }
113  return true;
114  }
115  bool accept(const base_node& min2, const base_node& max2) const
116  { return operator()(min2,max2); }
117  };
118 
119  /* match boxes intersecting the line passing through org and of
120  direction vector dirv.*/
121  struct intersect_line {
122  const base_node org;
123  const base_small_vector dirv;
124  intersect_line(const base_node& org_, const base_small_vector &dirv_)
125  : org(org_), dirv(dirv_) {}
126  bool operator()(const base_node& min2, const base_node& max2) const {
127  size_type N = org.size();
128  GMM_ASSERT1(N == min2.size(), "Dimensions mismatch");
129  for (size_type i = 0; i < N; ++i)
130  if (dirv[i] != scalar_type(0)) {
131  scalar_type a1=(min2[i]-org[i])/dirv[i], a2=(max2[i]-org[i])/dirv[i];
132  bool interf1 = true, interf2 = true;
133  for (size_type j = 0; j < N; ++j)
134  if (j != i) {
135  scalar_type y1 = org[j] + a1*dirv[j], y2 = org[j] + a2*dirv[j];
136  if (y1 < min2[j] || y1 > max2[j]) interf1 = false;
137  if (y2 < min2[j] || y2 > max2[j]) interf2 = false;
138  }
139  if (interf1 || interf2) return true;
140  }
141  return false;
142  }
143  bool accept(const base_node& min2, const base_node& max2) const
144  { return operator()(min2,max2); }
145  };
146 
147  /* match boxes intersecting the line passing through org and of
148  direction vector dirv.*/
149  struct intersect_line_and_box {
150  const base_node org;
151  const base_small_vector dirv;
152  const base_node min,max;
153  const scalar_type EPS;
154  intersect_line_and_box(const base_node& org_,
155  const base_small_vector &dirv_,
156  const base_node& min_, const base_node& max_,
157  scalar_type EPS_)
158  : org(org_), dirv(dirv_), min(min_), max(max_), EPS(EPS_) {}
159  bool operator()(const base_node& min2, const base_node& max2) const {
160  size_type N = org.size();
161  GMM_ASSERT1(N == min2.size(), "Dimensions mismatch");
162  if (!(r1_inter_r2(min,max,min2,max2,EPS))) return false;
163  for (size_type i = 0; i < N; ++i)
164  if (dirv[i] != scalar_type(0)) {
165  scalar_type a1=(min2[i]-org[i])/dirv[i], a2=(max2[i]-org[i])/dirv[i];
166  bool interf1 = true, interf2 = true;
167  for (size_type j = 0; j < N; ++j)
168  if (j != i) {
169  scalar_type y1 = org[j] + a1*dirv[j], y2 = org[j] + a2*dirv[j];
170  if (y1 < min2[j] || y1 > max2[j]) interf1 = false;
171  if (y2 < min2[j] || y2 > max2[j]) interf2 = false;
172  }
173  if (interf1 || interf2) return true;
174  }
175  return false;
176  }
177  bool accept(const base_node& min2, const base_node& max2) const
178  { return operator()(min2,max2); }
179  };
180 
181  size_type rtree::add_box(const base_node &min, const base_node &max,
182  size_type id) {
183  box_index bi;
184  if (tree_built) {
185  GMM_WARNING3("Add a box when the tree is already built cancel the tree. "
186  "Unefficient operation.");
187  tree_built = false; root = std::unique_ptr<rtree_elt_base>();
188  }
189  bi.min = &nodes[nodes.add_node(min, EPS)];
190  bi.max = &nodes[nodes.add_node(max, EPS)];
191  bi.id = (id + 1) ? id : boxes.size();
192  return boxes.emplace(std::move(bi)).first->id;
193  }
194 
195  rtree::rtree(scalar_type EPS_)
196  : EPS(EPS_), boxes(box_index_topology_compare(EPS_)), tree_built(false)
197  {}
198 
199  void rtree::clear() {
200  root = std::unique_ptr<rtree_elt_base>();
201  boxes.clear();
202  nodes.clear();
203  tree_built = false;
204  }
205 
206  template <typename Predicate>
207  static void find_matching_boxes_(rtree_elt_base *n, rtree::pbox_set& boxlst,
208  const Predicate &p) {
209  if (n->isleaf()) {
210  const rtree_leaf *rl = static_cast<rtree_leaf*>(n);
211  for (rtree::pbox_cont::const_iterator it = rl->lst.begin();
212  it != rl->lst.end(); ++it) {
213  if (p(*(*it)->min, *(*it)->max)) { boxlst.insert(*it); }
214  }
215  } else {
216  const rtree_node *rn = static_cast<rtree_node*>(n);
217  if (p.accept(rn->left->rmin,rn->left->rmax))
218  bgeot::find_matching_boxes_(rn->left.get(), boxlst, p);
219  if (p.accept(rn->right->rmin,rn->right->rmax))
220  bgeot::find_matching_boxes_(rn->right.get(), boxlst, p);
221  }
222  }
223 
224  void rtree::find_intersecting_boxes(const base_node& bmin,
225  const base_node& bmax,
226  pbox_set& boxlst) const {
227 
228  boxlst.clear();
229  GMM_ASSERT2(tree_built, "Boxtree not initialised.");
230  if (root)
231  find_matching_boxes_(root.get(),boxlst,intersection_p(bmin,bmax, EPS));
232  }
233 
234  void rtree::find_containing_boxes(const base_node& bmin,
235  const base_node& bmax,
236  pbox_set& boxlst) const {
237  boxlst.clear();
238  GMM_ASSERT2( tree_built, "Boxtree not initialised.");
239  if (root)
240  find_matching_boxes_(root.get(), boxlst, contains_p(bmin,bmax, EPS));
241  }
242 
243  void rtree::find_contained_boxes(const base_node& bmin,
244  const base_node& bmax,
245  pbox_set& boxlst) const {
246  boxlst.clear();
247  GMM_ASSERT2(tree_built, "Boxtree not initialised.");
248  if (root)
249  find_matching_boxes_(root.get(), boxlst, contained_p(bmin,bmax, EPS));
250  }
251 
252  void rtree::find_boxes_at_point(const base_node& P, pbox_set& boxlst) const {
253  boxlst.clear();
254  GMM_ASSERT2(tree_built, "Boxtree not initialised.");
255  if (root)
256  find_matching_boxes_(root.get(), boxlst, has_point_p(P, EPS));
257  }
258 
259  void rtree::find_line_intersecting_boxes(const base_node& org,
260  const base_small_vector& dirv,
261  pbox_set& boxlst) const {
262  boxlst.clear();
263  GMM_ASSERT2(tree_built, "Boxtree not initialised.");
264  if (root)
265  find_matching_boxes_(root.get(),boxlst,intersect_line(org, dirv));
266  }
267 
268  void rtree::find_line_intersecting_boxes(const base_node& org,
269  const base_small_vector& dirv,
270  const base_node& bmin,
271  const base_node& bmax,
272  pbox_set& boxlst) const {
273  boxlst.clear();
274  GMM_ASSERT2(tree_built, "Boxtree not initialised.");
275  if (root)
276  find_matching_boxes_(root.get(), boxlst,
277  intersect_line_and_box(org, dirv, bmin, bmax, EPS));
278  }
279 
280  /*
281  try to split at the approximate center of the box. Could be much more
282  sophisticated
283  */
284  static bool split_test(const rtree::pbox_cont& b,
285  const base_node& bmin, const base_node& bmax,
286  unsigned dir, scalar_type& split_v) {
287  scalar_type v = bmin[dir] + (bmax[dir] - bmin[dir])/2; split_v = v;
288  size_type cnt = 0;
289  for (rtree::pbox_cont::const_iterator it = b.begin(); it!=b.end(); ++it) {
290  if ((*it)->max->at(dir) < v) {
291  if (cnt == 0) split_v = (*it)->max->at(dir);
292  else split_v = std::max((*it)->max->at(dir),split_v);
293  cnt++;
294  }
295  }
296  return (cnt > 0 && cnt < b.size());
297  }
298 
299  /*
300  there are many flavors of rtree ... this one is more or less a quadtree
301  where splitting does not occurs at predefined positions (hence the
302  split_test function above).
303  Regions of the tree do not overlap (box are splitted).
304  */
305  static std::unique_ptr<rtree_elt_base> build_tree_(rtree::pbox_cont b,
306  const base_node& bmin,
307  const base_node& bmax,
308  unsigned last_dir) {
309  size_type N=bmin.size();
310  scalar_type split_v(0);
311  unsigned split_dir = unsigned((last_dir+1)%N);
312  bool split_ok = false;
313  if (b.size() > rtree_elt_base::RECTS_PER_LEAF) {
314  for (size_type itry=0; itry < N; ++itry) {
315  if (split_test(b, bmin, bmax, split_dir, split_v))
316  { split_ok = true; break; }
317  split_dir = unsigned((split_dir+1)%N);
318  }
319  }
320  if (split_ok) {
321  size_type cnt1=0,cnt2=0;
322  for (rtree::pbox_cont::const_iterator it = b.begin();
323  it != b.end(); ++it) {
324  if ((*it)->min->at(split_dir) < split_v) cnt1++;
325  if ((*it)->max->at(split_dir) > split_v) cnt2++;
326  }
327  assert(cnt1); assert(cnt2);
328  GMM_ASSERT1(cnt1+cnt2 >= b.size(), "internal error");
329  rtree::pbox_cont v1(cnt1), v2(cnt2);
330  base_node bmin1(bmax), bmax1(bmin);
331  base_node bmin2(bmax), bmax2(bmin);
332  cnt1 = cnt2 = 0;
333  for (rtree::pbox_cont::const_iterator it = b.begin();
334  it != b.end(); ++it) {
335  if ((*it)->min->at(split_dir) < split_v) {
336  v1[cnt1++] = *it;
337  update_box(bmin1,bmax1,*(*it)->min,*(*it)->max);
338  }
339  if ((*it)->max->at(split_dir) > split_v) {
340  v2[cnt2++] = *it;
341  update_box(bmin2,bmax2,*(*it)->min,*(*it)->max);
342  }
343  }
344  for (size_type k=0; k < N; ++k) {
345  bmin1[k] = std::max(bmin1[k],bmin[k]);
346  bmax1[k] = std::min(bmax1[k],bmax[k]);
347  bmin2[k] = std::max(bmin2[k],bmin[k]);
348  bmax2[k] = std::min(bmax2[k],bmax[k]);
349  }
350  bmax1[split_dir] = std::min(bmax1[split_dir], split_v);
351  bmin2[split_dir] = std::max(bmin2[split_dir], split_v);
352  assert(cnt1 == v1.size()); assert(cnt2 == v2.size());
353  return std::make_unique<rtree_node>
354  (bmin, bmax,
355  build_tree_(v1, bmin1, bmax1, split_dir),
356  build_tree_(v2, bmin2, bmax2, split_dir));
357  } else {
358  return std::make_unique<rtree_leaf>(bmin, bmax, b);
359  }
360  }
361 
362  void rtree::build_tree() {
363  if (!tree_built) {
364  if (boxes.size() == 0) { tree_built = true; return; }
365  getfem::local_guard lock = locks_.get_lock();
366  assert(root == 0);
367  pbox_cont b(boxes.size());
368  pbox_cont::iterator b_it = b.begin();
369  base_node bmin(*boxes.begin()->min), bmax(*boxes.begin()->max);
370  for (box_cont::const_iterator it=boxes.begin(); it != boxes.end(); ++it) {
371  update_box(bmin,bmax,*(*it).min,*(*it).max);
372  *b_it++ = &(*it);
373  }
374  root = build_tree_(b, bmin, bmax, 0);
375  tree_built = true;
376  }
377  }
378 
379  static void dump_tree_(rtree_elt_base *p, int level, size_type& count) {
380  if (!p) return;
381  for (int i=0; i < level; ++i) cout << " ";
382  cout << "span=" << p->rmin << ".." << p->rmax << " ";
383  if (p->isleaf()) {
384  rtree_leaf *rl = static_cast<rtree_leaf*>(p);
385  cout << "Leaf [" << rl->lst.size() << " elts] = ";
386  for (size_type i=0; i < rl->lst.size(); ++i)
387  cout << " " << rl->lst[i]->id;
388  cout << "\n";
389  count += rl->lst.size();
390  } else {
391  cout << "Node\n";
392  const rtree_node *rn = static_cast<rtree_node*>(p);
393  if (rn->left) { dump_tree_(rn->left.get(), level+1, count); }
394  if (rn->right) { dump_tree_(rn->right.get(), level+1, count); }
395  }
396  }
397 
398  void rtree::dump() {
399  cout << "tree dump follows\n";
400  if (!root) build_tree();
401  size_type count = 0;
402  dump_tree_(root.get(), 0, count);
403  cout << " --- end of tree dump, nb of rectangles: " << boxes.size()
404  << ", rectangle ref in tree: " << count << "\n";
405  }
406 }
region-tree for window/point search on a set of rectangles.
size_type add_node(const base_node &pt, const scalar_type radius=0, bool remove_duplicated_nodes=true)
Add a point to the array or use an existing point, located within a distance smaller than radius.
Basic Geometric Tools.
size_t size_type
used as the common size type in the library
Definition: bgeot_poly.h:48