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