11// Optimized quadtrees on grid rectangles in C++.
22// https://github.com/hit9/quadtree-hpp
33//
4- // BSD license. Chao Wang, Version: 0.2.2
4+ // BSD license. Chao Wang, Version: 0.3.0
55//
66// Coordinate conventions:
77//
88// w
9- // +---------------> y
9+ // +---------------> x
1010// |
1111// h |
1212// |
1313// v
14- // x
14+ // y
1515//
1616
1717// changes
1818// ~~~~~~~
19+ // 0.3.0: **Breaking change**: inverts the coordinates conventions.
1920// 0.2.2: Add `RemoveObjects` and `BatchAddToLeafNode`.
2021
2122#ifndef HIT9_QUADTREE_HPP
@@ -42,14 +43,14 @@ using std::uint8_t;
4243// NodeId is the unique identifier of a tree node, composed of:
4344//
4445// +----- 6bit -----+------- 29bit ----+----- 29bit -----+
45- // | depth d (6bit) | floor(x*(2^d)/h ) | floor(y*(2^d)/w |
46+ // | depth d (6bit) | floor(x*(2^d)/w ) | floor(y*(2^d)/h |
4647// +----------------+------------------+-----------------+
4748//
4849// Properties:
4950// 1. Substituting this formula into any position (x,y) inside the node always give the same ID.
5051// 2. The id of tree root is always 0.
5152// 3. The deeper the node, the larger the id.
52- // 4. For nodes at the same depth, the id changes with the size of x*w +y.
53+ // 4. For nodes at the same depth, the id changes with the size of x*h +y.
5354using NodeId = uint64_t ;
5455
5556// pack caculates the id of a node.
@@ -63,8 +64,8 @@ inline NodeId pack(uint64_t d, uint64_t x, uint64_t y, uint64_t w, uint64_t h) {
6364 // other bits are all 0.
6465 // 0x1fffffff : the lowest 29 bits are all 1, the other bits are all 0.
6566 return ((d << 58 ) & 0xfc00000000000000ULL ) |
66- ((((1 << d) * x / h ) << 29 ) & 0x3ffffffe0000000ULL ) |
67- (((1 << d) * y / w ) & 0x1fffffffULL );
67+ ((((1 << d) * x / w ) << 29 ) & 0x3ffffffe0000000ULL ) |
68+ (((1 << d) * y / h ) & 0x1fffffffULL );
6869}
6970
7071template <typename Object>
@@ -280,7 +281,7 @@ class Quadtree {
280281 // For diagonal directions(4,5,6,7), it simply returns the single diagonal leaf neighbour.
281282 // For non-diagonal directions (0,1,2,3), there're two steps.
282283 // Explaination for direction=0 (North), supposing the depth of given node is d:
283- // 1. Take 2 neighbour positions p1(x1-1 ,y1), p2(x1-1 ,y2) find the smallest node containing p1
284+ // 1. Take 2 neighbour positions p1(x1,y1-1 ), p2(x1,y2-1 ) find the smallest node containing p1
284285 // and p2. This node's size should be equal or greater than current node.
285286 // This step could be done by a binary-search in time complexity O(log Depth).
286287 // 2. Find the sourth children(No. 2,3) downward recursively from the node found in step1, until
@@ -450,7 +451,7 @@ bool Quadtree<Object, ObjectHasher>::splitable(int x1, int y1, int x2, int y2, i
450451 return true ;
451452 }
452453 // ssf v1
453- if (ssf != nullptr && ssf (y2 - y1 + 1 , x2 - x1 + 1 , n)) return false ;
454+ if (ssf != nullptr && ssf (x2 - x1 + 1 , y2 - y1 + 1 , n)) return false ;
454455 return true ;
455456}
456457
@@ -501,8 +502,8 @@ Node<Object, ObjectHasher>* Quadtree<Object, ObjectHasher>::splitHelper1(
501502 uint8_t d, int x1, int y1, int x2, int y2, ObjectsT& upstreamObjects,
502503 NodeSet& createdLeafNodes) {
503504 // boundary checks.
504- if (!(x1 >= 0 && x1 < h && y1 >= 0 && y1 < w )) return nullptr ;
505- if (!(x2 >= 0 && x2 < h && y2 >= 0 && y2 < w )) return nullptr ;
505+ if (!(x1 >= 0 && x1 < w && y1 >= 0 && y1 < h )) return nullptr ;
506+ if (!(x2 >= 0 && x2 < w && y2 >= 0 && y2 < h )) return nullptr ;
506507 if (!(x1 <= x2 && y1 <= y2)) return nullptr ;
507508 // steal objects inside this rectangle from upstream.
508509 ObjectsT objs;
@@ -545,30 +546,30 @@ void Quadtree<Object, ObjectHasher>::splitHelper2(NodeT* node, NodeSet& createdL
545546 auto d = node->d ;
546547 // the following (x3,y3) is the middle point*:
547548 //
548- // y1 y3 y2
549- // x1 -+------+------+-
549+ // x1 x3 x2
550+ // y1 -+------+------+-
550551 // | 0 | 1 |
551- // x3 | * | |
552+ // y3 | * | |
552553 // -+------+------+-
553554 // | 2 | 3 |
554555 // | | |
555- // x2 -+------+------+-
556+ // y2 -+------+------+-
556557 int x3 = x1 + (x2 - x1) / 2 , y3 = y1 + (y2 - y1) / 2 ;
557558
558- // determines which side each axis x3 and y3 belongs, take y axis for instance:
559- // by default, we assume y3 belongs to the left side.
560- // but if the ids of y1 and y3 are going to dismatch, which means the y3 should belong to the
561- // right side, that is we should minus y3 by 1.
562- // And minus by 1 should be enough, because y3 -2 always equals to y3 -4, y3 -8,.. until y1 .
559+ // determines which side each axis x3 and y3 belongs, take x axis for instance:
560+ // by default, we assume x3 belongs to the left side.
561+ // but if the ids of x1 and x3 are going to dismatch, which means the x3 should belong to the
562+ // right side, that is we should minus x3 by 1.
563+ // And minus by 1 should be enough, because x3 -2 always equals to x3 -4, x3 -8,.. until x1 .
563564 // Potential optimization: how to avoid the division here?
564565 uint64_t k = 1 << (d + 1 );
565- if ((k * x3 / h ) != (k * x1 / h )) --x3;
566- if ((k * y3 / w ) != (k * y1 / w )) --y3;
566+ if ((k * x3 / w ) != (k * x1 / w )) --x3;
567+ if ((k * y3 / h ) != (k * y1 / h )) --y3;
567568
568569 // clang-format off
569570 node->children [0 ] = splitHelper1 (d + 1 , x1, y1, x3, y3, node->objects , createdLeafNodes);
570- node->children [1 ] = splitHelper1 (d + 1 , x1, y3 + 1 , x3, y2 , node->objects , createdLeafNodes);
571- node->children [2 ] = splitHelper1 (d + 1 , x3 + 1 , y1, x2, y3 , node->objects , createdLeafNodes);
571+ node->children [1 ] = splitHelper1 (d + 1 , x3 + 1 , y1, x2, y3 , node->objects , createdLeafNodes);
572+ node->children [2 ] = splitHelper1 (d + 1 , x1, y3 + 1 , x3, y2 , node->objects , createdLeafNodes);
572573 node->children [3 ] = splitHelper1 (d + 1 , x3 + 1 , y3 + 1 , x2, y2, node->objects , createdLeafNodes);
573574 // clang-format on
574575
@@ -702,16 +703,16 @@ inline bool isOverlap(int ax1, int ay1, int ax2, int ay2, int bx1, int by1, int
702703 //
703704 // Ref: https://silentmatt.com/rectangle-intersection/
704705 //
705- // ax1 < bx2 => A's upper boundary is above B's bottom boundary.
706- // ax2 > bx1 => A's bottom boundary is below B's upper boundary.
706+ // ay1 < by2 => A's left boundary is above B's bottom boundary.
707+ // ay2 > by1 => A's bottom boundary is below B's upper boundary.
707708 //
708709 // *********** B's upper A's upper -----------
709710 // A's upper ----------- OR *********** B's upper
710711 // *********** B's bottom A's bottom -----------
711712 // A's bottom ----------- *********** B's bottom
712713 //
713- // ay1 < by2 => A's left boundary is on the left of B's right boundary.
714- // ay2 > by1 => A's right boundary is on the right of B's left boundary.
714+ // ax1 < bx2 => A's left boundary is on the left of B's right boundary.
715+ // ax2 > bx1 => A's right boundary is on the right of B's left boundary.
715716 //
716717 // A's left A's right A's left A's right
717718 //
@@ -759,7 +760,7 @@ void Quadtree<Object, ObjectHasher>::queryRange(NodeT* node, CollectorT& objects
759760}
760761
761762// Using binary search to guess the depth of the target node.
762- // Reason: the id = (d, x*2^d/h , y*2^d/w ), it's the same for all (x,y) inside the same
763+ // Reason: the id = (d, x*2^d/w , y*2^d/h ), it's the same for all (x,y) inside the same
763764// node. If id(d,x,y) is not found in the map m, the guessed depth is too large, we should
764765// shrink the upper bound. Otherwise, if we found a node, but it's not a leaf, the answer
765766// is too small, we should make the lower bound larger, And finally, if we found a leaf node,
@@ -787,7 +788,7 @@ Node<Object, ObjectHasher>* Quadtree<Object, ObjectHasher>::Find(int x, int y) c
787788template <typename Object, typename ObjectHasher>
788789void Quadtree<Object, ObjectHasher>::Add(int x, int y, Object o) {
789790 // boundary checks.
790- if (!(x >= 0 && x < h && y >= 0 && y < w )) return ;
791+ if (!(x >= 0 && x < w && y >= 0 && y < h )) return ;
791792 // find the leaf node.
792793 auto node = Find (x, y);
793794 if (node == nullptr ) return ;
@@ -803,7 +804,7 @@ void Quadtree<Object, ObjectHasher>::Add(int x, int y, Object o) {
803804template <typename Object, typename ObjectHasher>
804805void Quadtree<Object, ObjectHasher>::Remove(int x, int y, Object o) {
805806 // boundary checks.
806- if (!(x >= 0 && x < h && y >= 0 && y < w )) return ;
807+ if (!(x >= 0 && x < w && y >= 0 && y < h )) return ;
807808 // find the leaf node.
808809 auto node = Find (x, y);
809810 if (node == nullptr ) return ;
@@ -818,7 +819,7 @@ void Quadtree<Object, ObjectHasher>::Remove(int x, int y, Object o) {
818819template <typename Object, typename ObjectHasher>
819820void Quadtree<Object, ObjectHasher>::RemoveObjects(int x, int y) {
820821 // boundary checks.
821- if (!(x >= 0 && x < h && y >= 0 && y < w )) return ;
822+ if (!(x >= 0 && x < w && y >= 0 && y < h )) return ;
822823 // find the leaf node.
823824 auto node = Find (x, y);
824825 if (node == nullptr ) return ;
@@ -833,7 +834,7 @@ void Quadtree<Object, ObjectHasher>::RemoveObjects(int x, int y) {
833834
834835template <typename Object, typename ObjectHasher>
835836void Quadtree<Object, ObjectHasher>::Build() {
836- root = createNode (true , 0 , 0 , 0 , h - 1 , w - 1 );
837+ root = createNode (true , 0 , 0 , 0 , w - 1 , h - 1 );
837838 if (!trySplitDown (root)) {
838839 // If the root is not splited, it's finally a new-created leaf node.
839840 if (afterLeafCreated != nullptr ) afterLeafCreated (root);
@@ -853,8 +854,8 @@ template <typename Object, typename ObjectKeyHasher>
853854Node<Object, ObjectKeyHasher>* Quadtree<Object, ObjectKeyHasher>::findSmallestNodeCoveringRange(
854855 int x1, int y1, int x2, int y2, int dma) const {
855856 // boundary checks
856- if (!(x1 >= 0 && x1 < h && y1 >= 0 && y1 < w )) return nullptr ;
857- if (!(x2 >= 0 && x2 < h && y2 >= 0 && y2 < w )) return nullptr ;
857+ if (!(x1 >= 0 && x1 < w && y1 >= 0 && y1 < h )) return nullptr ;
858+ if (!(x2 >= 0 && x2 < w && y2 >= 0 && y2 < h )) return nullptr ;
858859 // Find the target
859860 int l = 0 , r = dma;
860861 NodeT* node = root;
@@ -921,11 +922,11 @@ void Quadtree<Object, ObjectHasher>::QueryLeafNodesInRange(int x1, int y1, int x
921922// Get the neighbour position (px,py) on given diagonal direction of given node.
922923// The a,b,c,d is the target neighbour position for each direction:
923924//
924- // y1 y2
925+ // x1 x2
925926// 4 a| |b 5
926- // --+-----+-- x1
927+ // --+-----+-- y1
927928// | |
928- // --+-----+-- x2
929+ // --+-----+-- y2
929930// 7 d| |c 6
930931template <typename Object, typename ObjectKeyHasher>
931932void Quadtree<Object, ObjectKeyHasher>::getNeighbourPositionDiagonal(NodeT* node, int direction,
@@ -936,13 +937,13 @@ void Quadtree<Object, ObjectKeyHasher>::getNeighbourPositionDiagonal(NodeT* node
936937 px = x1 - 1 , py = y1 - 1 ;
937938 return ;
938939 case 5 : // b
939- px = x1 - 1 , py = y2 + 1 ;
940+ px = x2 + 1 , py = y1 - 1 ;
940941 return ;
941942 case 6 : // c
942943 px = x2 + 1 , py = y2 + 1 ;
943944 return ;
944945 case 7 : // d
945- px = x2 + 1 , py = y1 - 1 ;
946+ px = x1 - 1 , py = y2 + 1 ;
946947 return ;
947948 }
948949}
@@ -964,13 +965,13 @@ void Quadtree<Object, ObjectKeyHasher>::findNeighbourLeafNodesDiagonal(NodeT* no
964965// The ab,cd,ef,gh are the target neighbour positions at each direction:
965966//
966967// N:0
967- // y1 y2
968+ // x1 x2
968969// | |
969970// a b
970- // -g+-----+c- x1
971+ // -g+-----+c- y1
971972// W:3 | | E:1
972973// | |
973- // -h+-----+d- x2
974+ // -h+-----+d- y2
974975// e f
975976// | |
976977// S:2
@@ -981,20 +982,20 @@ void Quadtree<Object, ObjectKeyHasher>::getNeighbourPositionsHV(NodeT* node, int
981982 int x1 = node->x1 , y1 = node->y1 , x2 = node->x2 , y2 = node->y2 ;
982983 switch (direction) {
983984 case 0 : // N
984- px1 = x1 - 1 , py1 = y1; // a
985- px2 = x1 - 1 , py2 = y2 ; // b
985+ px1 = x1, py1 = y1 - 1 ; // a
986+ px2 = x2 , py2 = y1 - 1 ; // b
986987 return ;
987988 case 1 : // E
988- px1 = x1 , py1 = y2 + 1 ; // c
989- px2 = x2, py2 = y2 + 1 ; // d
989+ px1 = x2 + 1 , py1 = y1 ; // c
990+ px2 = x2 + 1 , py2 = y2; // d
990991 return ;
991992 case 2 : // S
992- px1 = x2 + 1 , py1 = y1 ; // e
993- px2 = x2 + 1 , py2 = y2; // f
993+ px1 = x1 , py1 = y2 + 1 ; // e
994+ px2 = x2, py2 = y2 + 1 ; // f
994995 return ;
995996 case 3 : // W
996- px1 = x1, py1 = y1 - 1 ; // g
997- px2 = x2 , py2 = y1 - 1 ; // h
997+ px1 = x1 - 1 , py1 = y1; // g
998+ px2 = x1 - 1 , py2 = y2 ; // h
998999 return ;
9991000 }
10001001}
0 commit comments