Gamedev Framework (gf)  0.8.0
A C++14 framework for 2D games
Spatial.h
1 /*
2  * Gamedev Framework (gf)
3  * Copyright (C) 2016-2018 Julien Bernard
4  *
5  * This software is provided 'as-is', without any express or implied
6  * warranty. In no event will the authors be held liable for any damages
7  * arising from the use of this software.
8  *
9  * Permission is granted to anyone to use this software for any purpose,
10  * including commercial applications, and to alter it and redistribute it
11  * freely, subject to the following restrictions:
12  *
13  * 1. The origin of this software must not be misrepresented; you must not
14  * claim that you wrote the original software. If you use this software
15  * in a product, an acknowledgment in the product documentation would be
16  * appreciated but is not required.
17  * 2. Altered source versions must be plainly marked as such, and must not be
18  * misrepresented as being the original software.
19  * 3. This notice may not be removed or altered from any source distribution.
20  */
21 #ifndef GF_SPATIAL_H
22 #define GF_SPATIAL_H
23 
24 #include <cassert>
25 #include <cmath>
26 #include <algorithm>
27 #include <functional>
28 #include <iterator>
29 #include <map>
30 #include <memory>
31 #include <numeric>
32 #include <vector>
33 
34 #include <boost/container/static_vector.hpp>
35 
36 #include "Box.h"
37 #include "Math.h"
38 #include "Unused.h"
39 
40 #include "Log.h"
41 
42 namespace gf {
43 #ifndef DOXYGEN_SHOULD_SKIP_THIS
44 inline namespace v1 {
45 #endif
46 
51  enum class SpatialQuery {
52  Contain,
53  Intersect,
54  };
55 
60  template<typename T>
61  using SpatialQueryCallback = std::function<void(const T&)>;
62 
70  template<typename T, typename U = float, std::size_t Size = 16>
71  class QuadTree {
72  static_assert(Size > 0, "Size can not be 0");
73  public:
79  QuadTree(const Box<U, 2>& bounds)
80  : m_root(bounds)
81  {
82 
83  }
84 
92  bool insert(const T& value, const Box<U, 2>& bounds) {
93  return m_root.insert(value, bounds);
94  }
95 
104  std::size_t query(const Box<U, 2>& bounds, SpatialQueryCallback<T> callback, SpatialQuery kind = SpatialQuery::Intersect) const {
105  return m_root.query(bounds, callback, kind);
106  }
107 
111  void clear() {
112  m_root.clear();
113  }
114 
115  private:
116  struct Entry {
117  T value;
118  Box<U, 2> bounds;
119  };
120 
121  class Node {
122  public:
123  Node()
124  : m_children(nullptr)
125  {
126  m_entries.reserve(Size);
127  }
128 
129  Node(const Box<U, 2>& bounds)
130  : m_bounds(bounds)
131  , m_children(nullptr)
132  {
133  m_entries.reserve(Size);
134  }
135 
136  bool isLeaf() const {
137  return m_children == nullptr;
138  }
139 
140  bool insert(const T& value, const Box<U, 2>& bounds) {
141  if (!m_bounds.contains(bounds)) {
142  return false;
143  }
144 
145  if (isLeaf()) {
146  if (m_entries.size() < Size) {
147  m_entries.push_back({ value, bounds });
148  return true;
149  }
150 
151  subdivide();
152 
153  // try again
154  if (m_entries.size() < Size) {
155  m_entries.push_back({ value, bounds });
156  return true;
157  }
158  }
159 
160  for (std::size_t i = 0; i < 4; ++i) {
161  if (m_children[i].insert(value, bounds)) {
162  return true;
163  }
164  }
165 
166  clearChildrenIfEmpty();
167 
168  m_entries.push_back({ value, bounds });
169  return true;
170  }
171 
172  std::size_t query(const Box<U, 2>& bounds, SpatialQueryCallback<T>& callback, SpatialQuery kind) const {
173  if (!m_bounds.intersects(bounds)) {
174  return 0;
175  }
176 
177  std::size_t found = 0;
178 
179  for (auto& entry : m_entries) {
180  switch (kind) {
182  if (bounds.contains(entry.bounds)) {
183  callback(entry.value);
184  ++found;
185  }
186  break;
187 
189  if (bounds.intersects(entry.bounds)) {
190  callback(entry.value);
191  ++found;
192  }
193  break;
194  }
195  }
196 
197  if (!isLeaf()) {
198  for (std::size_t i = 0; i < 4; ++i) {
199  found += m_children[i].query(bounds, callback, kind);
200  }
201  }
202 
203  return found;
204  }
205 
206  void clear() {
207  m_entries.clear();
208 
209  // no need to explicitly clear children
210  m_children = nullptr;
211  }
212 
213  private:
214  void subdivide() {
215  m_children = std::make_unique<Node[]>(4);
216 
217  m_children[0].m_bounds = computeBoxQuadrant(m_bounds, Quadrant::UpperLeft);
218  m_children[1].m_bounds = computeBoxQuadrant(m_bounds, Quadrant::UpperRight);
219  m_children[2].m_bounds = computeBoxQuadrant(m_bounds, Quadrant::LowerRight);
220  m_children[3].m_bounds = computeBoxQuadrant(m_bounds, Quadrant::LowerLeft);
221 
222  std::vector<Entry> entries;
223 
224  for (auto& entry : m_entries) {
225  bool inserted = false;
226 
227  for (std::size_t i = 0; i < 4; ++i) {
228  if (m_children[i].insert(entry.value, entry.bounds)) {
229  inserted = true;
230  break;
231  }
232  }
233 
234  if (!inserted) {
235  entries.push_back(entry);
236  }
237  }
238 
239  std::swap(m_entries, entries);
240  }
241 
242  void clearChildrenIfEmpty() {
243  assert(!isLeaf());
244 
245  for (std::size_t i = 0; i < 4; ++i) {
246  if (!m_children[i].m_entries.empty()) {
247  return;
248  }
249  }
250 
251  m_children = nullptr;
252  }
253 
254  private:
255  Box<U, 2> m_bounds;
256  std::vector<Entry> m_entries;
257  std::unique_ptr<Node[]> m_children;
258  };
259 
260  private:
261  Node m_root;
262  };
263 
273  template<typename T, typename U = float, std::size_t N = 2, std::size_t MaxSize = 16, std::size_t MinSize = 4>
274  class RStarTree {
275  static_assert(N > 0, "N can not be 0");
276  static_assert(2 <= MinSize, "MinSize must be at least 2");
277  static_assert(MinSize <= MaxSize / 2, "MinSize must be less than MaxSize/2");
278  public:
283  : m_root(new Leaf)
284  {
285  auto branch = std::make_unique<Branch>();
286  }
287 
291  RStarTree(const RStarTree&) = delete;
292 
296  RStarTree& operator=(const RStarTree&) = delete;
297 
301  RStarTree(RStarTree&& other) noexcept
302  {
303  m_root = std::exchange(other.m_root, nullptr);
304  }
305 
309  RStarTree& operator=(RStarTree&& other) noexcept {
310  std::swap(m_root, other.m_root);
311  return *this;
312  }
313 
318  delete m_root;
319  }
320 
328  bool insert(const T& value, const Box<U, N>& bounds) {
329  Leaf *leaf = m_root->chooseSubtree(bounds);
330 
331  Node *splitted = leaf->tryInsert(value, bounds);
332  Node *current = leaf;
333 
334  while (splitted != nullptr) {
335  auto splittedBounds = splitted->computeBounds();
336  splitted->updateOriginalBounds(splittedBounds);
337 
338  auto currentBounds = current->computeBounds();
339  current->updateOriginalBounds(currentBounds);
340 
341  if (current == m_root) {
342  auto branch = new Branch;
343 
344  current->setParent(branch);
345  branch->tryInsert(current, currentBounds);
346 
347  splitted->setParent(branch);
348  branch->tryInsert(splitted, splittedBounds);
349 
350  branch->updateOriginalBounds(branch->computeBounds());
351 
352  m_root = branch;
353  current = m_root;
354  splitted = nullptr;
355  } else {
356  Branch *parent = current->getParent();
357  assert(parent != nullptr);
358 
359  parent->updateBoundsForChild(currentBounds, current);
360 
361  splitted->setParent(parent);
362  splitted = parent->tryInsert(splitted, splittedBounds);
363 
364  current = parent;
365  }
366  }
367 
368  while (current != m_root) {
369  auto currentBounds = current->computeBounds();
370 
371  Branch *parent = current->getParent();
372  assert(parent != nullptr);
373 
374  parent->updateBoundsForChild(currentBounds, current);
375 
376  current = parent;
377  }
378 
379  return true;
380  }
381 
390  std::size_t query(const Box<U, N>& bounds, SpatialQueryCallback<T> callback, SpatialQuery kind = SpatialQuery::Intersect) const {
391  return m_root->query(bounds, callback, kind);
392  }
393 
397  void clear() {
398  delete m_root;
399  m_root = new Leaf;
400  }
401 
402  private:
403  static constexpr std::size_t Size = MaxSize + 1;
404 
405  class Leaf;
406  class Branch;
407 
408  /*
409  * Node
410  */
411 
412  struct Entry {
413  Box<U, N> bounds;
414  };
415 
416  class Node {
417  public:
418  Node()
419  : m_parent(nullptr)
420  {
421 
422  }
423 
424  Branch *getParent() noexcept {
425  return m_parent;
426  }
427 
428  void setParent(Branch *parent) {
429  m_parent = parent;
430  }
431 
432  bool hasOriginalBounds() const {
433  return !m_orig.isEmpty();
434  }
435 
436  U getOriginalCenterAxisValue(std::size_t axis) const {
437  return (m_orig.min[axis] + m_orig.max[axis]) / 2;
438  }
439 
440  void updateOriginalBounds(Box<U, N> orig) {
441  m_orig = orig;
442  }
443 
444  virtual ~Node() = default;
445  virtual std::size_t query(const Box<U, N>& bounds, SpatialQueryCallback<T>& callback, SpatialQuery kind) const = 0;
446  virtual Leaf *chooseSubtree(const Box<U, N>& bounds) = 0;
447 
448  virtual Box<U, N> computeBounds() const = 0;
449 
450  protected:
451  enum SplitOrder {
452  Min,
453  Max,
454  };
455 
456  struct SplitResult {
457  std::size_t index;
458  std::size_t axis;
459  SplitOrder order;
460  };
461 
462  private:
463  Branch *m_parent;
464  Box<U, N> m_orig;
465  };
466 
467  /*
468  * Leaf
469  */
470 
471  struct LeafEntry : Entry {
472  T value;
473  };
474 
475  class Leaf : public Node {
476  public:
477  using typename Node::SplitOrder;
478  using typename Node::SplitResult;
479 
480  virtual std::size_t query(const Box<U, N>& bounds, SpatialQueryCallback<T>& callback, SpatialQuery kind) const override {
481  std::size_t found = 0;
482 
483  for (auto& entry : m_entries) {
484  switch (kind) {
486  if (bounds.contains(entry.bounds)) {
487  callback(entry.value);
488  ++found;
489  }
490  break;
491 
493  if (bounds.intersects(entry.bounds)) {
494  callback(entry.value);
495  ++found;
496  }
497  break;
498  }
499  }
500 
501  return found;
502  }
503 
504 
505  virtual Leaf *chooseSubtree(const Box<U, N>& bounds) override {
506  gf::unused(bounds);
507  return this;
508  }
509 
510  virtual Box<U,N> computeBounds() const override {
511  assert(!m_entries.empty());
512  Box<U,N> res(m_entries[0].bounds);
513 
514  for (auto& entry : m_entries) {
515  res.extend(entry.bounds);
516  }
517 
518  return res;
519  }
520 
521  Leaf *tryInsert(const T& value, const Box<U, N>& bounds) {
522  LeafEntry entry;
523  entry.bounds = bounds;
524  entry.value = value;
525  m_entries.push_back(std::move(entry));
526 
527  if (m_entries.size() < Size) {
528  return nullptr;
529  }
530 
531  std::vector<Box<U, N>> boxes;
532 
533  std::transform(m_entries.begin(), m_entries.end(), std::back_inserter(boxes), [](const Entry& entry) {
534  return entry.bounds;
535  });
536 
537  SplitResult split = computeSplit(boxes);
538 
539  switch (split.order) {
540  case SplitOrder::Min:
541  std::sort(m_entries.begin(), m_entries.end(), EntryMinAxisComparator<LeafEntry>(split.axis));
542  break;
543  case SplitOrder::Max:
544  std::sort(m_entries.begin(), m_entries.end(), EntryMaxAxisComparator<LeafEntry>(split.axis));
545  break;
546  }
547 
548  auto leaf = new Leaf;
549 
550  auto splitIteratorBegin = std::next(m_entries.begin(), split.index + 1);
551  auto splitIteratorEnd = m_entries.end();
552 
553  std::move(splitIteratorBegin, splitIteratorEnd, std::back_inserter(leaf->m_entries));
554  m_entries.erase(splitIteratorBegin, splitIteratorEnd);
555 
556  return leaf;
557  }
558 
559  private:
560  SplitResult computeSplit(std::vector<Box<U, N>>& boxes) {
561  SplitResult split;
562  std::tie(split.axis, split.order) = computeSplitAxis(boxes);
563 
564  switch (split.order) {
565  case SplitOrder::Min:
566  std::sort(boxes.begin(), boxes.end(), MinAxisComparator(split.axis));
567  break;
568  case SplitOrder::Max:
569  std::sort(boxes.begin(), boxes.end(), MaxAxisComparator(split.axis));
570  break;
571  }
572 
573  split.index = computeSplitIndex(boxes, split.axis);
574  return split;
575  }
576 
577  std::tuple<std::size_t, SplitOrder> computeSplitAxis(std::vector<Box<U, N>>& boxes) {
578  std::size_t currentAxis = 0;
579  U currentValue = std::numeric_limits<U>::max();
580  SplitOrder currentOrder = SplitOrder::Min;
581 
582  for (std::size_t axis = 0; axis < N; ++axis) {
583  std::sort(boxes.begin(), boxes.end(), MinAxisComparator(axis));
584 
585  U value = computeAxisValue(boxes);
586 
587  if (value < currentValue) {
588  currentAxis = axis;
589  currentValue = value;
590  currentOrder = SplitOrder::Min;
591  }
592 
593  std::sort(boxes.begin(), boxes.end(), MaxAxisComparator(axis));
594 
595  value = computeAxisValue(boxes);
596 
597  if (value < currentValue) {
598  currentAxis = axis;
599  currentValue = value;
600  currentOrder = SplitOrder::Max;
601  }
602  }
603 
604  return std::make_tuple(currentAxis, currentOrder);
605  }
606 
607  U computeAxisValue(const std::vector<Box<U, N>>& boxes) {
608  std::vector<Box<U, N>> firstBounds;
609 
610  std::partial_sum(boxes.begin(), boxes.end(), std::back_inserter(firstBounds), [](const Box<U, N>& lhs, const Box<U, N>& rhs) {
611  return lhs.getExtended(rhs);
612  });
613 
614  std::vector<Box<U, N>> secondBounds;
615 
616  std::partial_sum(boxes.rbegin(), boxes.rend(), std::back_inserter(secondBounds), [](const Box<U, N>& lhs, const Box<U, N>& rhs) {
617  return lhs.getExtended(rhs);
618  });
619 
620  U value = { };
621 
622  for (std::size_t j = MinSize; j <= MaxSize - MinSize + 1; ++j) {
623  value += firstBounds[j].getExtentDistance() + secondBounds[Size - j].getExtentDistance();
624  }
625 
626  return value;
627  }
628 
629  std::size_t computeSplitIndex(const std::vector<Box<U, N>>& boxes, std::size_t axis) {
630  std::vector<Box<U, N>> firstBounds;
631 
632  std::partial_sum(boxes.begin(), boxes.end(), std::back_inserter(firstBounds), [](const Box<U, N>& lhs, const Box<U, N>& rhs) {
633  return lhs.getExtended(rhs);
634  });
635 
636  std::vector<Box<U, N>> secondBounds;
637 
638  std::partial_sum(boxes.rbegin(), boxes.rend(), std::back_inserter(secondBounds), [](const Box<U, N>& lhs, const Box<U, N>& rhs) {
639  return lhs.getExtended(rhs);
640  });
641 
642 
643  // define wf
644 
645  const auto& bounds = firstBounds.back();
646  float asym = 0.0f;
647 
648  if (Node::hasOriginalBounds()) {
649  asym = 2.0f * ((bounds.max[axis] + bounds.min[axis]) / 2 - Node::getOriginalCenterAxisValue(axis)) / (bounds.max[axis] - bounds.min[axis]);
650  }
651 
652  assert(-1.0f <= asym && asym <= 1.0f);
653 
654  static constexpr float S = 0.5f;
655  const float mu = (1 - 2.0f * MinSize / (MaxSize + 1)) * asym;
656  const float rho = S * (1 + std::abs(mu));
657  const float y1 = std::exp(- 1 / (S * S));
658  const float ys = 1 / (1 - y1);
659 
660  auto wf = [mu,rho,y1,ys](std::size_t i) {
661  const float xi = 2.0f * i / (MaxSize + 1.0f) - 1.0f;
662  return ys * (std::exp(- gf::square((xi - mu) / rho) - y1));
663  };
664 
665  // compute weight
666 
667  std::size_t currentIndex = 0;
668  U currentValue = std::numeric_limits<U>::max();
669 
670  U (Box<U,N>::* overlapFn)(const Box<U, N>&) const noexcept = nullptr;
671 
672  if (firstBounds[MinSize].getVolume() == 0 || secondBounds[MinSize].getVolume() == 0) {
673  // perimeter based strategy
675  } else {
676  // volume based strategy
677  overlapFn = &Box<U,N>::getIntersectionVolume;
678  }
679 
680  bool overlapFree = false;
681 
682  U perimeterMax = 2 * bounds.getExtentDistance() - bounds.getMinimumEdge();
683 
684  for (std::size_t index = MinSize; index <= MaxSize - MinSize + 1; ++index) {
685  auto weight = (firstBounds[index].*overlapFn)(secondBounds[Size - index - 1]);
686 
687  if (!overlapFree) {
688  if (weight == 0) {
689  overlapFree = true;
690  } else {
691  auto value = weight * wf(index);
692 
693  if (value < currentValue) {
694  currentValue = value;
695  currentIndex = index;
696  }
697  }
698  }
699 
700  if (overlapFree && weight == 0) {
701  weight = firstBounds[index].getExtentDistance() + secondBounds[Size - index - 1].getExtentDistance() - perimeterMax;
702 
703  if (weight > 0) {
704  gf::Log::debug("weight: %f\n", static_cast<double>(weight));
705  }
706 
707  assert(weight <= 0);
708 
709  auto value = weight / wf(index);
710 
711  if (value < currentValue) {
712  currentValue = value;
713  currentIndex = index;
714  }
715  }
716  }
717 
718  return currentIndex;
719  }
720 
721  private:
722  boost::container::static_vector<LeafEntry, Size> m_entries;
723  };
724 
725  /*
726  * Branch
727  */
728 
729  struct BranchEntry : Entry {
730  Node *child;
731  };
732 
733  class Branch : public Node {
734  public:
735  using typename Node::SplitOrder;
736  using typename Node::SplitResult;
737 
738  virtual ~Branch() {
739  for (auto& entry : m_entries) {
740  delete entry.child;
741  }
742  }
743 
744  virtual std::size_t query(const Box<U, N>& bounds, SpatialQueryCallback<T>& callback, SpatialQuery kind) const override {
745  std::size_t found = 0;
746 
747  for (auto& entry : m_entries) {
748  if (bounds.intersects(entry.bounds)) {
749  found += entry.child->query(bounds, callback, kind);
750  }
751  }
752 
753  return found;
754  }
755 
756  virtual Leaf *chooseSubtree(const Box<U, N>& bounds) override {
757  return chooseNode(bounds)->chooseSubtree(bounds);
758  }
759 
760  Node *searchForCoveringNode(const Box<U, N>& bounds) {
761  Node *bestNodeForVolume = nullptr;
762  U bestVolume = std::numeric_limits<U>::max();
763 
764  Node *bestNodeForExtentDistance = nullptr;
765  U bestExtentDistance = std::numeric_limits<U>::max();
766 
767  for (auto& entry : m_entries) {
768  if (entry.bounds.getIntersection(bounds) == bounds) {
769  U volume = entry.bounds.getVolume();
770 
771  if (bestNodeForVolume == nullptr || volume < bestVolume) {
772  bestVolume = volume;
773  bestNodeForVolume = entry.child;
774  }
775 
776  U extentDistance = entry.bounds.getExtentDistance();
777 
778  if (bestNodeForExtentDistance == nullptr || extentDistance < bestExtentDistance) {
779  bestExtentDistance = extentDistance;
780  bestNodeForExtentDistance = entry.child;
781  }
782  }
783  }
784 
785  if (bestNodeForVolume != nullptr) {
786  if (bestVolume > 0) {
787  return bestNodeForVolume;
788  }
789 
790  assert(bestNodeForExtentDistance);
791  return bestNodeForExtentDistance;
792  }
793 
794  return nullptr;
795  }
796 
797  Node *chooseNode(const Box<U, N>& bounds) {
798  Node *covering = searchForCoveringNode(bounds);
799 
800  if (covering != nullptr) {
801  return covering;
802  }
803 
804  std::sort(m_entries.begin(), m_entries.end(), ExtentDistanceEnlargement(bounds));
805 
806  OverlapExtentDistanceEnlargement extentDistanceEnlargement(bounds, m_entries.front());
807 
808  std::size_t p;
809 
810  for (p = m_entries.size() - 1; p > 0; --p) {
811  if (extentDistanceEnlargement(m_entries[p]) != 0) {
812  break;
813  }
814  }
815 
816  if (p == 0) {
817  return m_entries[0].child;
818  }
819 
820  std::vector<Candidate> candidates(p + 1, { 0, false, U() });
821  Node *node = nullptr;
822 
823  if (existsEmptyVolumeExtension(bounds)) {
824  node = findCandidates<OverlapExtentDistanceEnlargement>(0, p, bounds, candidates);
825  } else {
826  node = findCandidates<OverlapVolumeEnlargement>(0, p, bounds, candidates);
827  }
828 
829  if (node != nullptr) {
830  return node;
831  }
832 
833  candidates.erase(std::remove_if(candidates.begin(), candidates.end(), [](const Candidate& candidate) {
834  return !candidate.isCandidate;
835  }), candidates.end());
836 
837  assert(!candidates.empty());
838 
839  auto it = std::min_element(candidates.begin(), candidates.end(), [](const Candidate& lhs, const Candidate& rhs) {
840  return lhs.overlap < rhs.overlap;
841  });
842 
843  return m_entries[it->index].child;
844  }
845 
846  virtual Box<U,N> computeBounds() const override {
847  assert(!m_entries.empty());
848  Box<U,N> res(m_entries[0].bounds);
849 
850  for (auto& entry : m_entries) {
851  res.extend(entry.bounds);
852  }
853 
854  return res;
855  }
856 
857  void updateBoundsForChild(const Box<U, N>& bounds, Node *child) {
858  for (auto& entry : m_entries) {
859  if (entry.child == child) {
860  entry.bounds = bounds;
861  return;
862  }
863  }
864 
865  assert(false);
866  }
867 
868  Branch *tryInsert(Node * node, const Box<U, N>& bounds) {
869  BranchEntry entry;
870  entry.bounds = bounds;
871  entry.child = node;
872  m_entries.push_back(std::move(entry));
873 
874  if (m_entries.size() < Size) {
875  return nullptr;
876  }
877 
878  std::vector<Box<U, N>> boxes;
879 
880  std::transform(m_entries.begin(), m_entries.end(), std::back_inserter(boxes), [](const Entry& entry) {
881  return entry.bounds;
882  });
883 
884  SplitResult split = computeSplit(boxes);
885 
886  switch (split.order) {
887  case SplitOrder::Min:
888  std::sort(m_entries.begin(), m_entries.end(), EntryMinAxisComparator<BranchEntry>(split.axis));
889  break;
890  case SplitOrder::Max:
891  std::sort(m_entries.begin(), m_entries.end(), EntryMaxAxisComparator<BranchEntry>(split.axis));
892  break;
893  }
894 
895  auto branch = new Branch;
896 
897  auto splitIteratorBegin = std::next(m_entries.begin(), split.index + 1);
898  auto splitIteratorEnd = m_entries.end();
899 
900  std::move(splitIteratorBegin, splitIteratorEnd, std::back_inserter(branch->m_entries));
901  m_entries.erase(splitIteratorBegin, splitIteratorEnd);
902 
903  for (auto& entry : branch->m_entries) {
904  entry.child->setParent(branch);
905  }
906 
907  return branch;
908  }
909 
910  private:
911  struct Candidate {
912  std::size_t index;
913  bool isCandidate;
914  U overlap;
915  };
916 
917  bool existsEmptyVolumeExtension(const Box<U, N>& bounds) {
918  for (auto& entry : m_entries) {
919  if (entry.bounds.getExtended(bounds).getVolume() == 0) {
920  return true;
921  }
922  }
923 
924  return false;
925  }
926 
927  template<typename OverlapEnlargement>
928  Node *findCandidates(std::size_t t, std::size_t p, const Box<U, N>& bounds, std::vector<Candidate>& candidates) {
929  candidates[t].index = t;
930  candidates[t].isCandidate = true;
931 
932  OverlapEnlargement enlargement(bounds, m_entries[t]);
933 
934  U overlap = { };
935 
936  for (std::size_t i = 0; i <= p; ++i) {
937  if (i == t) {
938  continue;
939  }
940 
941  U localOverlap = enlargement(m_entries[i]);
942  overlap += localOverlap;
943 
944  if (localOverlap == 0 && !candidates[i].isCandidate) {
945  Node *node = findCandidates<OverlapEnlargement>(i, p, bounds, candidates);
946 
947  if (node != nullptr) {
948  return node;
949  }
950  }
951  }
952 
953  if (overlap == 0) {
954  return m_entries[t].child;
955  }
956 
957  candidates[t].overlap = overlap;
958  return nullptr;
959  }
960 
961  struct SplitStatus {
962  bool overlapFree = false;
963  U currentValue = std::numeric_limits<U>::max();
964  };
965 
966  SplitResult computeSplit(std::vector<Box<U, N>>& boxes) {
967  SplitResult result;
968  SplitStatus status;
969 
970  for (std::size_t axis = 0; axis < N; ++axis) {
971  std::sort(boxes.begin(), boxes.end(), MinAxisComparator(axis));
972  computeBestSplitValue(boxes, result, status, axis, SplitOrder::Min);
973 
974  std::sort(boxes.begin(), boxes.end(), MaxAxisComparator(axis));
975  computeBestSplitValue(boxes, result, status, axis, SplitOrder::Max);
976  }
977 
978  return result;
979  }
980 
981  void computeBestSplitValue(std::vector<Box<U, N>>& boxes, SplitResult& result, SplitStatus& status, std::size_t axis, SplitOrder order) {
982  std::vector<Box<U, N>> firstBounds;
983 
984  std::partial_sum(boxes.begin(), boxes.end(), std::back_inserter(firstBounds), [](const Box<U, N>& lhs, const Box<U, N>& rhs) {
985  return lhs.getExtended(rhs);
986  });
987 
988  std::vector<Box<U, N>> secondBounds;
989 
990  std::partial_sum(boxes.rbegin(), boxes.rend(), std::back_inserter(secondBounds), [](const Box<U, N>& lhs, const Box<U, N>& rhs) {
991  return lhs.getExtended(rhs);
992  });
993 
994  // define wf
995 
996  const auto& bounds = firstBounds.back();
997  float asym = 0.0f;
998 
999  if (Node::hasOriginalBounds()) {
1000  asym = 2.0f * ((bounds.max[axis] + bounds.min[axis]) / 2 - Node::getOriginalCenterAxisValue(axis)) / (bounds.max[axis] - bounds.min[axis]);
1001  }
1002 
1003  assert(-1.0f <= asym && asym <= 1.0f);
1004 
1005  static constexpr float S = 0.5f;
1006  const float mu = (1 - 2.0f * MinSize / (MaxSize + 1)) * asym;
1007  const float rho = S * (1 + std::abs(mu));
1008  const float y1 = std::exp(- 1 / (S * S));
1009  const float ys = 1 / (1 - y1);
1010 
1011  auto wf = [mu,rho,y1,ys](std::size_t i) {
1012  const float xi = 2.0f * i / (MaxSize + 1.0f) - 1.0f;
1013  return ys * (std::exp(- gf::square((xi - mu) / rho) - y1));
1014  };
1015 
1016  // compute weight
1017 
1018  U (Box<U,N>::* overlapFn)(const Box<U, N>&) const noexcept = nullptr;
1019 
1020  if (firstBounds[MinSize].getVolume() == 0 || secondBounds[MinSize].getVolume() == 0) {
1021  // perimeter based strategy
1023  } else {
1024  // volume based strategy
1025  overlapFn = &Box<U,N>::getIntersectionVolume;
1026  }
1027 
1028  U perimeterMax = 2 * bounds.getExtentDistance() - bounds.getMinimumEdge();
1029 
1030  for (std::size_t index = MinSize; index <= MaxSize - MinSize + 1; ++index) {
1031  auto weight = (firstBounds[index].*overlapFn)(secondBounds[Size - index - 1]);
1032 
1033  if (!status.overlapFree) {
1034  if (weight == 0) {
1035  status.overlapFree = true;
1036  } else {
1037  auto value = weight * wf(index);
1038 
1039  if (value < status.currentValue) {
1040  status.currentValue = value;
1041  result.index = index;
1042  result.axis = axis;
1043  result.order = order;
1044  }
1045  }
1046  }
1047 
1048  if (status.overlapFree && weight == 0) {
1049  weight = firstBounds[index].getExtentDistance() + secondBounds[Size - index - 1].getExtentDistance() - perimeterMax;
1050 
1051  if (weight > 0) {
1052  gf::Log::debug("weight: %f\n", static_cast<double>(weight));
1053  }
1054 
1055  assert(weight <= 0);
1056 
1057  auto value = weight / wf(index);
1058 
1059  if (value < status.currentValue) {
1060  status.currentValue = value;
1061  result.index = index;
1062  result.axis = axis;
1063  result.order = order;
1064  }
1065  }
1066  }
1067  }
1068 
1069  private:
1070  boost::container::static_vector<BranchEntry, Size> m_entries;
1071  };
1072 
1073  /*
1074  * Comparators and Functors
1075  */
1076 
1077  class ExtentDistanceEnlargement {
1078  public:
1079  ExtentDistanceEnlargement(const Box<U, N>& bounds)
1080  : m_bounds(bounds)
1081  {
1082 
1083  }
1084 
1085  bool operator()(const Entry& lhs, const Entry& rhs) const {
1086  return getExtentDistanceEnlargement(lhs) < getExtentDistanceEnlargement(rhs);
1087  }
1088 
1089  U getExtentDistanceEnlargement(const Entry& entry) const {
1090  return entry.bounds.getExtended(m_bounds).getExtentDistance() - entry.bounds.getExtentDistance();
1091  }
1092 
1093  private:
1094  const Box<U, N>& m_bounds;
1095  };
1096 
1097  class OverlapExtentDistanceEnlargement {
1098  public:
1099  OverlapExtentDistanceEnlargement(const Box<U, N>& bounds, const Entry& reference)
1100  : m_bounds(bounds)
1101  , m_reference(reference)
1102  , m_extended(reference.bounds.getExtended(bounds))
1103  {
1104 
1105  }
1106 
1107  U operator()(const Entry& entry) const {
1108  return getOverlapExtentDistanceEnlargement(entry);
1109  }
1110 
1111  U getOverlapExtentDistanceEnlargement(const Entry& entry) const {
1112  return m_extended.getIntersectionExtentDistance(entry.bounds) - m_reference.bounds.getIntersectionExtentDistance(entry.bounds);
1113  }
1114 
1115  private:
1116  const Box<U, N>& m_bounds;
1117  const Entry& m_reference;
1118  Box<U, N> m_extended;
1119  };
1120 
1121 
1122  class OverlapVolumeEnlargement {
1123  public:
1124  OverlapVolumeEnlargement(const Box<U, N>& bounds, const Entry& reference)
1125  : m_bounds(bounds)
1126  , m_reference(reference)
1127  , m_extended(reference.bounds.getExtended(bounds))
1128  {
1129 
1130  }
1131 
1132  U operator()(const Entry& entry) const {
1133  return getOverlapVolumeEnlargement(entry);
1134  }
1135 
1136  U getOverlapVolumeEnlargement(const Entry& entry) const {
1137  return m_extended.getIntersectionVolume(entry.bounds) - m_reference.bounds.getIntersectionVolume(entry.bounds);
1138  }
1139 
1140  private:
1141  const Box<U, N>& m_bounds;
1142  const Entry& m_reference;
1143  Box<U, N> m_extended;
1144  };
1145 
1146  class MinAxisComparator {
1147  public:
1148  MinAxisComparator(std::size_t axis)
1149  : m_axis(axis)
1150  {
1151 
1152  }
1153 
1154  bool operator()(const Box<U, N>& lhs, const Box<U, N>& rhs) {
1155  return std::make_tuple(lhs.min[m_axis], lhs.max[m_axis]) < std::make_tuple(rhs.min[m_axis], rhs.max[m_axis]);
1156  }
1157 
1158  private:
1159  std::size_t m_axis;
1160  };
1161 
1162  template<typename Entry>
1163  class EntryMinAxisComparator {
1164  public:
1165  EntryMinAxisComparator(std::size_t axis)
1166  : m_axis(axis)
1167  {
1168 
1169  }
1170 
1171  bool operator()(const Entry& lhs, const Entry& rhs) {
1172  return std::make_tuple(lhs.bounds.min[m_axis], lhs.bounds.max[m_axis]) < std::make_tuple(rhs.bounds.min[m_axis], rhs.bounds.max[m_axis]);
1173  }
1174 
1175  private:
1176  std::size_t m_axis;
1177  };
1178 
1179  class MaxAxisComparator {
1180  public:
1181  MaxAxisComparator(std::size_t axis)
1182  : m_axis(axis)
1183  {
1184 
1185  }
1186 
1187  bool operator()(const Box<U, N>& lhs, const Box<U, N>& rhs) {
1188  return std::make_tuple(lhs.max[m_axis], lhs.min[m_axis]) < std::make_tuple(rhs.max[m_axis], rhs.min[m_axis]);
1189  }
1190 
1191  private:
1192  std::size_t m_axis;
1193  };
1194 
1195  template<typename Entry>
1196  class EntryMaxAxisComparator {
1197  public:
1198  EntryMaxAxisComparator(std::size_t axis)
1199  : m_axis(axis)
1200  {
1201 
1202  }
1203 
1204  bool operator()(const Entry& lhs, const Entry& rhs) {
1205  return std::make_tuple(lhs.bounds.max[m_axis], lhs.bounds.min[m_axis]) < std::make_tuple(rhs.bounds.max[m_axis], rhs.bounds.min[m_axis]);
1206  }
1207 
1208  private:
1209  std::size_t m_axis;
1210  };
1211 
1212  private:
1213  void updateBounds(Node *node) {
1214  while (node != nullptr) {
1215  Box<U, N> bounds = node->computeBounds();
1216 
1217  Branch *parent = node->getParent();
1218 
1219  if (parent != nullptr) {
1220  parent->updateBoundsForChild(bounds, node);
1221  }
1222 
1223  node = parent;
1224  }
1225  }
1226 
1227  private:
1228  Node *m_root;
1229  };
1230 
1231 
1232 #ifndef DOXYGEN_SHOULD_SKIP_THIS
1233 }
1234 #endif
1235 }
1236 
1237 #endif // GF_SPATIAL_H
constexpr Box< T, N > getExtended(const Box< T, N > &other) const noexcept
Definition: Box.h:196
The tree is a node, generally an internal node of the tree.
constexpr bool intersects(const Box< T, N > &other) const noexcept
Definition: Box.h:118
void unused(Args &&...)
A simple way to avoid warnings about unused variables.
Definition: Unused.h:35
void clear()
Remove all the objects from the tree.
Definition: Spatial.h:111
std::size_t query(const Box< U, N > &bounds, SpatialQueryCallback< T > callback, SpatialQuery kind=SpatialQuery::Intersect) const
Query objects in the tree.
Definition: Spatial.h:390
std::function< void(const T &)> SpatialQueryCallback
A callback for spatial query.
Definition: Spatial.h:61
constexpr Vector2f transform(const Matrix3f &mat, Vector2f point)
Apply an affine transformation to a 2D point.
Definition: Transform.h:331
Vector< T, N > min
Definition: Box.h:49
Search for all objects that contain the given bounds.
~RStarTree()
Destructor.
Definition: Spatial.h:317
constexpr T square(T val)
Square function.
Definition: Math.h:275
std::size_t index
Definition: Spatial.h:457
std::size_t query(const Box< U, 2 > &bounds, SpatialQueryCallback< T > callback, SpatialQuery kind=SpatialQuery::Intersect) const
Query objects in the tree.
Definition: Spatial.h:104
Search for all objects that intersect the given bounds.
RStarTree & operator=(RStarTree &&other) noexcept
Move assignement.
Definition: Spatial.h:309
void clear()
Remove all the objects from the tree.
Definition: Spatial.h:397
The namespace for gf classes.
Definition: Action.h:34
QuadTree(const Box< U, 2 > &bounds)
Constructor.
Definition: Spatial.h:79
RStarTree()
Constructor.
Definition: Spatial.h:282
An implementation of quadtree.
Definition: Spatial.h:71
Vector< T, N > max
Definition: Box.h:50
Definition: Spatial.h:456
Box< T, 2 > computeBoxQuadrant(const Box< T, 2 > &box, Quadrant quadrant)
Definition: Box.h:263
RStarTree(RStarTree &&other) noexcept
Move constructor.
Definition: Spatial.h:301
static void debug(const char *fmt,...)
Print a debug message.
Definition: Log.h:89
An implemntation of a R* tree.
Definition: Spatial.h:274
SpatialQuery
A kind of spatial query.
Definition: Spatial.h:51
bool insert(const T &value, const Box< U, N > &bounds)
Insert an object in the tree.
Definition: Spatial.h:328
SplitOrder order
Definition: Spatial.h:459
constexpr bool contains(Vector< T, N > point) const noexcept
Definition: Box.h:98
bool insert(const T &value, const Box< U, 2 > &bounds)
Insert an object in the tree.
Definition: Spatial.h:92
std::size_t axis
Definition: Spatial.h:458