LibreCAD
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
quadtree.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include <functional>
4 #include <algorithm>
5 #include <unordered_map>
6 #include <vector>
7 #include <climits>
8 #include <array>
9 #include "cad/geometry/geoarea.h"
10 #include "cad/base/cadentity.h"
11 #include <typeinfo>
12 #include <iostream>
13 #include "cad/const.h"
14 
15 namespace lc {
16  template<typename E>
17  class QuadTree;
18  //class CADEntity;
24  template<typename E>
25  class QuadTreeSub {
26  public:
27  QuadTreeSub(int level, const geo::Area& pBounds, short maxLevels, short maxObjects) :
28  _level(level) ,
29  _verticalMidpoint(pBounds.minP().x() + (pBounds.width() / 2.)),
30  _horizontalMidpoint(pBounds.minP().y() + (pBounds.height() / 2.)),
31  _bounds(pBounds), _maxLevels(maxLevels),
32  _maxObjects(maxObjects) {
33  _objects.reserve(maxObjects / 2);
34  _nodes[0] = nullptr;
35  _nodes[1] = nullptr;
36  _nodes[2] = nullptr;
37  _nodes[3] = nullptr;
38 
39 
40  }
41 
42  QuadTreeSub(const geo::Area& bounds) : QuadTreeSub(0, bounds, 10, 25) {
43 
44  }
45 
46  QuadTreeSub(const QuadTreeSub& other) :
47  QuadTreeSub(0, other.bounds(), other.maxLevels(), other.maxObjects()) {
48  for (auto i : other.retrieve()) {
49  insert(i);
50  }
51  }
52 
53  QuadTreeSub() : QuadTreeSub(0, geo::Area(geo::Coordinate(0., 0.), geo::Coordinate(1., 1.)), 10, 25) {
54 
55  }
56 
57  virtual ~QuadTreeSub() {
58  if (_nodes[0] != nullptr) {
59  delete _nodes[0];
60  delete _nodes[1];
61  delete _nodes[2];
62  delete _nodes[3];
63  }
64  }
65 
70  void clear() {
71  if (_nodes[0] != nullptr) {
72  delete _nodes[0];
73  delete _nodes[1];
74  delete _nodes[2];
75  delete _nodes[3];
76  _nodes[0] = nullptr;
77  _nodes[1] = nullptr;
78  _nodes[2] = nullptr;
79  _nodes[3] = nullptr;
80  }
81  }
82 
90  void insert(const E entity, const lc::geo::Area& entityBoundingBox) {
91  // Find a Quad Tree area where this item fits
92  if (_nodes[0] != nullptr) {
93  short entityIndex = quadrantIndex(entityBoundingBox);
94 
95  if (entityIndex != -1) {
96  _nodes[entityIndex]->insert(entity, entityBoundingBox);
97  return;
98  }
99  }
100 
101  _objects.push_back(entity);
102 
103  // If it fits in this box, see if we can/must split this area into sub area's
104  // loop over the current container and see if the entities fit at a lower level
105  // So each entity is only tried once
106  if (_nodes[0] == nullptr && _objects.size() >= _maxObjects && _level < _maxLevels) {
107  split();
108  // Split two level's deep to reduce the number of object iterations
109  // This will help mostly when adding lots of little objects that would fit in 1/8 of the quad
110  _nodes[0]->split();
111  _nodes[1]->split();
112  _nodes[2]->split();
113  _nodes[3]->split();
114 
115  for (auto it = _objects.begin() ; it != _objects.end();) {
116  auto sentityBoundingBox = (*it)->boundingBox();
117  short index = quadrantIndex(sentityBoundingBox);
118 
119  if (index != -1) {
120  _nodes[index]->insert(*it, sentityBoundingBox);
121  it = _objects.erase(it);
122  }
123  else {
124  it++;
125  }
126  }
127  }
128  }
133  inline void insert(const E entity) {
134  insert(entity, entity->boundingBox());
135  }
136 
143  bool erase(const E entity) {
144  // Find a Quad Tree area where this item might be located
145  if (_nodes[0] != nullptr) {
146  short index = quadrantIndex(entity->boundingBox());
147 
148  if (index != -1) {
149  if (_nodes[index]->erase(entity)) {
150  return true;
151  }
152  }
153  }
154 
155  for (typename std::vector<E>::iterator it = _objects.begin(); it != _objects.end(); it++) {
156  if ((*it)->id() == entity->id()) {
157  _objects.erase(it);
158  return true;
159  }
160  }
161 
162  return false;
163  }
164 
165 
172  std::vector<E> retrieve(const geo::Area& area, const short maxLevel = SHRT_MAX) const {
173  std::vector<E> list;
174  _retrieve(list, area, maxLevel);
175  return list;
176  }
177 
184  std::vector<E> retrieve(const short maxLevel = SHRT_MAX) const {
185  std::vector<E> list;
186  _retrieve(list, maxLevel);
187  return list;
188  }
195  unsigned int size() const {
196  return _size(0);
197  }
198 
205  const E entityByID(const ID_DATATYPE id) const {
206  // // LOG4CXX_WARN(logger, "Using non optmised entityById method, this is going to be VERY slow. To solve please call the root QuadTreeSub to get a cached version!")
207 
208  for (auto i : retrieve()) {
209  if (i->id() == id) {
210  return i;
211  }
212  }
213 
214  return E();
215  }
216 
217 
223  geo::Area bounds() const {
224  return _bounds;
225  }
226 
232  short level() const {
233  return _level;
234  }
235 
242  short maxLevels() const {
243  return _maxLevels;
244  }
251  short maxObjects() const {
252  return _maxObjects;
253  }
254 
260  void walkQuad(const std::function<void(const QuadTreeSub<E>&)>& func) {
261  func(*this);
262 
263  if (_nodes[0] != nullptr) {
264  _nodes[0]->walkQuad(func);
265  _nodes[1]->walkQuad(func);
266  _nodes[2]->walkQuad(func);
267  _nodes[3]->walkQuad(func);
268  }
269  }
270 
274  template<typename U, typename T> void each(T func) {
275  if (_nodes[0] != nullptr) {
276  _nodes[0]->template each<U>(func);
277  _nodes[1]->template each<U>(func);
278  _nodes[2]->template each<U>(func);
279  _nodes[3]->template each<U>(func);
280  }
281 
282  std::for_each(_objects.begin(), _objects.end(), [&](E item) {
283  std::shared_ptr<U> b = std::dynamic_pointer_cast< U >(item);
284  func(b);
285  });
286  }
287 
294  bool optimise() {
295  if (_nodes[0] != nullptr) {
296  bool ret1 = _nodes[0]->optimise();
297  bool ret2 = _nodes[1]->optimise();
298  bool ret3 = _nodes[2]->optimise();
299  bool ret4 = _nodes[3]->optimise();
300 
301  if (ret1 && ret2 && ret3 && ret4) {
302  delete _nodes[0];
303  delete _nodes[1];
304  delete _nodes[2];
305  delete _nodes[3];
306  _nodes[0] = nullptr;
307  _nodes[1] = nullptr;
308  _nodes[2] = nullptr;
309  _nodes[3] = nullptr;
310  }
311  else {
312  return false;
313  }
314  }
315 
316  return _objects.size() == 0;
317  }
318 
319  private:
326  void _retrieve(std::vector<E>& list, const short maxLevel) const {
327  if (_nodes[0] != nullptr && maxLevel > _level) {
328  _nodes[0]->_retrieve(list, maxLevel);
329  _nodes[1]->_retrieve(list, maxLevel);
330  _nodes[2]->_retrieve(list, maxLevel);
331  _nodes[3]->_retrieve(list, maxLevel);
332  }
333 
334  list.insert(list.end(), _objects.begin(), _objects.end());
335  }
342  void _retrieve(std::vector<E>& list, const geo::Area& area, const short maxLevel) const {
343  if (_nodes[0] != nullptr && maxLevel > _level) {
344  if (_nodes[0] -> includes(area)) {
345  _nodes[0]->_retrieve(list, area, maxLevel);
346  }
347 
348  if (_nodes[1] -> includes(area)) {
349  _nodes[1]->_retrieve(list, area, maxLevel);
350  }
351 
352  if (_nodes[2] -> includes(area)) {
353  _nodes[2]->_retrieve(list, area, maxLevel);
354  }
355 
356  if (_nodes[3] -> includes(area)) {
357  _nodes[3]->_retrieve(list, area, maxLevel);
358  }
359  }
360 
361  list.insert(list.end(), _objects.begin(), _objects.end());
362  }
366  unsigned int _size(unsigned int c) const {
367  if (_nodes[0] != nullptr) {
368  c += _nodes[0]->_size(_nodes[1]->_size(_nodes[2]->_size(_nodes[3]->_size(c))));
369  }
370 
371  return c + _objects.size();
372  }
373 
380  short quadrantIndex(const geo::Area& pRect) const {
381 
382  bool topQuadrant = (pRect.minP().y() >= _horizontalMidpoint) &&
383  (pRect.maxP().y() < _bounds.maxP().y());
384  bool bottomQuadrant = (pRect.minP().y() > _bounds.minP().y()) &&
385  (pRect.maxP().y() <= _horizontalMidpoint);
386 
387  if (!(topQuadrant || bottomQuadrant)) {
388  return -1;
389  }
390 
391  bool leftQuadrant = (pRect.minP().x() > _bounds.minP().x()) &&
392  (pRect.maxP().x() <= _verticalMidpoint);
393  bool rightQuandrant = (pRect.minP().x() >= _verticalMidpoint) &&
394  (pRect.maxP().x() < _bounds.maxP().x());
395 
396  if (!(leftQuadrant || rightQuandrant)) {
397  return -1;
398  }
399  else if (topQuadrant && rightQuandrant) {
400  return 0;
401  }
402  else if (topQuadrant && leftQuadrant) {
403  return 1;
404  }
405  else if (bottomQuadrant && leftQuadrant) {
406  return 2;
407  }
408 
409  return 3;
410  }
414  bool includes(const geo::Area& area) const {
415 
416  if (area.maxP().x() <= _bounds.minP().x() ||
417  area.minP().x() >= _bounds.maxP().x() ||
418  area.maxP().y() <= _bounds.minP().y() ||
419  area.minP().y() >= _bounds.maxP().y()) {
420  return false;
421  } else {
422  return true;
423  }
424  }
425 
430  void split() {
431  double subWidth = _bounds.width() / 2.;
432  double subHeight = _bounds.height() / 2.;
433  double x = _bounds.minP().x();
434  double y = _bounds.minP().y();
435 
436  if (_nodes[0] != nullptr) {
437  // // LOG4CXX_DEBUG(logger, "Split is called on an already split node, please fix!");
438  } else {
439  _nodes[0] = new QuadTreeSub(
440  _level + 1,
441  geo::Area(geo::Coordinate(x + subWidth, y + subHeight),
443  ),
444  _maxLevels,
446  );
447 
448  _nodes[1] = new QuadTreeSub(
449  _level + 1,
450  geo::Area(geo::Coordinate(x, y + subHeight),
451  geo::Coordinate(x + subWidth, _bounds.maxP().y())
452  ),
453  _maxLevels,
455  );
456 
457  _nodes[2] = new QuadTreeSub(
458  _level + 1,
460  geo::Coordinate(x + subWidth, y + subHeight)
461  ),
462  _maxLevels,
464  );
465 
466  _nodes[3] = new QuadTreeSub(
467  _level + 1,
468  geo::Area(geo::Coordinate(x + subWidth, y),
469  geo::Coordinate(_bounds.maxP().x(), y + subHeight)
470  ),
471  _maxLevels,
473  );
474  }
475 
476  }
477 
478  private:
479  const short _level;
480  std::vector<E> _objects;
481  const double _verticalMidpoint;
482  const double _horizontalMidpoint;
485  const unsigned short _maxLevels;
486  const unsigned short _maxObjects;
487  };
488 
506  template<typename E>
507  class QuadTree : public QuadTreeSub<E> {
508  public:
509  QuadTree(int level, const geo::Area& pBounds, short maxLevels, short maxObjects) :
510  QuadTreeSub<E>(level, pBounds, maxLevels, maxObjects) {
511 
512  }
513 
514  QuadTree(const geo::Area& bounds) : QuadTreeSub<E>(bounds) {
515 
516  }
517 
518  QuadTree(const QuadTree& other) : QuadTreeSub<E>(other), _cadentities(other._cadentities) {
519 
520  }
521 
522  QuadTree() : QuadTreeSub<E>(0, geo::Area(geo::Coordinate(0., 0.), geo::Coordinate(1., 1.)), 8, 25) {
523 
524  }
525 
530  void clear() {
532  _cadentities.clear();
533  }
534 
541  void insert(const E entity) {
542  // LOG4CXX_DEBUG(logger, "level " << _level);
543  //Update cache
544  if (_cadentities.count(entity->id()) > 0) {
545  //LOG4CXX_DEBUG(logger, "This id was already added, please fix. It's not allowed to add the same ID twice");
546  }
547 
548  _cadentities.insert(std::make_pair(entity->id(), entity));
549 
550  QuadTreeSub<E>::insert(entity);
551  }
552 
557  void test() const {
558  const auto list = QuadTreeSub<E>::retrieve();
559 
560  if (list.size() != _cadentities.size()) {
561  // // LOG4CXX_DEBUG(logger, "Cache size doesn't agree with QuadTreeSub size, this must be fixed ASAP difference : " << list.size() - _cadentities.size())
562  }
563  }
564 
571  bool erase(const E entity) {
572  if (_cadentities.count(entity->id()) == 0) {
573  // // LOG4CXX_DEBUG(logger, "It's bad that we end up here, normally we should call erase on entities we know that don't exists. ")
574  return false;
575  }
576 
577  E work = _cadentities.at(entity->id());
578  _cadentities.erase(entity->id());
579 
580  return QuadTreeSub<E>::erase(work);
581  }
582 
583  const E entityByID(ID_DATATYPE id) const {
584  if (_cadentities.count(id) > 0) {
585  return _cadentities.at(id);
586  }
587 
588  return E();
589  }
590 
591  private:
592  // used as a cache on root level
593  // This will allow is to quickly lookup a CAD entity from the root
594  // SHould we consider using https://github.com/attractivechaos/klib I didn't do integer testing but this lib seems faster
595  std::unordered_map<ID_DATATYPE, const E> _cadentities;
596  };
597 
598 }
const Coordinate minP() const
Definition: geoarea.h:60
void clear()
clear Clear the quad tree by removing all levels and removing all stored entities ...
Definition: quadtree.h:70
const double _verticalMidpoint
Definition: quadtree.h:481
QuadTree(int level, const geo::Area &pBounds, short maxLevels, short maxObjects)
Definition: quadtree.h:509
The QuadTreeSub class each nide below QuadTree will be a QuadTreeSub type.
Definition: quadtree.h:25
QuadTreeSub(const geo::Area &bounds)
Definition: quadtree.h:42
bool erase(const E entity)
remove Remove entity from quad tree
Definition: quadtree.h:571
std::vector< E > retrieve(const short maxLevel=SHRT_MAX) const
retrieve all object's within this QuadTree up until some level
Definition: quadtree.h:184
QuadTreeSub(const QuadTreeSub &other)
Definition: quadtree.h:46
short maxLevels() const
maxLevels Maximum number of level's possible This value should be copied from one level to a other le...
Definition: quadtree.h:242
const geo::Area _bounds
Definition: quadtree.h:483
unsigned int size() const
size all object's that are located within a given area
Definition: quadtree.h:195
double x() const
Returns x of Coordinate.
Definition: geocoordinate.h:26
#define ID_DATATYPE
Definition: id.h:7
const E entityByID(ID_DATATYPE id) const
Definition: quadtree.h:583
std::vector< E > _objects
Definition: quadtree.h:480
void _retrieve(std::vector< E > &list, const geo::Area &area, const short maxLevel) const
retrieve all object's that are located within a given area
Definition: quadtree.h:342
const unsigned short _maxLevels
Definition: quadtree.h:485
double y() const
Returns y of Coordinate.
Definition: geocoordinate.h:34
bool optimise()
optimise Optmise this tree. Current implementation will remove empty nodes up till the root node ...
Definition: quadtree.h:294
const double _horizontalMidpoint
Definition: quadtree.h:482
void walkQuad(const std::function< void(const QuadTreeSub< E > &)> &func)
walk Allows to walk over each node within the tree specifying a function that can be called for each ...
Definition: quadtree.h:260
short level() const
level returns the current level of this QuadTree
Definition: quadtree.h:232
bool includes(const geo::Area &area) const
Definition: quadtree.h:414
The QuadTree class Quad tree implementation to spatially store CADEntities Useful for area selections...
Definition: quadtree.h:17
void insert(const E entity)
Definition: quadtree.h:133
Definition: cadentity.h:12
void test() const
test validy of the tree by comparing all nodes with the std::map
Definition: quadtree.h:557
void insert(const E entity, const lc::geo::Area &entityBoundingBox)
insert Insert entity into the qauad tree
Definition: quadtree.h:90
bool erase(const E entity)
remove Remove entity from quad tree
Definition: quadtree.h:143
geo::Area bounds() const
bounds of the root portion of the tree
Definition: quadtree.h:223
QuadTreeSub(int level, const geo::Area &pBounds, short maxLevels, short maxObjects)
Definition: quadtree.h:27
QuadTree(const geo::Area &bounds)
Definition: quadtree.h:514
QuadTree(const QuadTree &other)
Definition: quadtree.h:518
QuadTreeSub * _nodes[4]
Definition: quadtree.h:484
std::vector< E > retrieve(const geo::Area &area, const short maxLevel=SHRT_MAX) const
retrieve all object's that are located within a given area
Definition: quadtree.h:172
void each(T func)
Definition: quadtree.h:274
const short _level
Definition: quadtree.h:479
void _retrieve(std::vector< E > &list, const short maxLevel) const
retrieve all object's that are located within a given area
Definition: quadtree.h:326
void insert(const E entity)
insert Insert entity into the quad tree
Definition: quadtree.h:541
void split()
split Create 4 new quads below the current quad
Definition: quadtree.h:430
const Coordinate maxP() const
Definition: geoarea.h:67
std::unordered_map< ID_DATATYPE, const E > _cadentities
Definition: quadtree.h:595
unsigned int _size(unsigned int c) const
Definition: quadtree.h:366
const unsigned short _maxObjects
Definition: quadtree.h:486
double height() const
height Returns the height of this area
Definition: geoarea.h:85
virtual ~QuadTreeSub()
Definition: quadtree.h:57
void clear()
clear Clear the quad tree by removing all levels and removing all stored entities ...
Definition: quadtree.h:530
short maxObjects() const
maxObjects Maximum number of objects on this level This value should be copied one level to a other l...
Definition: quadtree.h:251
double width() const
width Returns the width of this area
Definition: geoarea.h:76
short quadrantIndex(const geo::Area &pRect) const
quadrantIndex located a possible quadrant index
Definition: quadtree.h:380
const E entityByID(const ID_DATATYPE id) const
entityByID returns a entity by its ID
Definition: quadtree.h:205