/* ---- This file is part of SECONDO. Copyright (C) 2019, Faculty of Mathematics and Computer Science, Database Systems for New Applications. SECONDO is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. SECONDO is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with SECONDO; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ---- //[<] [\ensuremath{<}] //[>] [\ensuremath{>}] \setcounter{tocdepth}{3} \tableofcontents 1 Preparations This section contains __includes__, __namespaces__, and __forward declarations__. */ #pragma once #include #include #include #include #include #include #include #include #include #include "Rectangle2.h" #include "MathUtils.h" #include "Algebras/Rectangle/RectangleAlgebra.h" namespace pointcloud2 { // max = maximum number of entries within a node >= 2 template class Node; /* 2 Declaration of class RTree2 This class was created from the class RtreeT and Rtree in the includes section (MMRTree.h) and enhanced by an iterator that accepts predicates. */ template class RtreeT2 { using NodeT = Node; public: /* 2.1 Constructor This constructor creates a new empty RTRee2. */ RtreeT2(); /* 2.2 Destructor */ ~RtreeT2(){ if (root) { root->destroy(); delete root; root = 0; } } Rectangle2 getBBox() const; /* 2.2 ~insert~ Inserts a box together with an id into the R-Tree. */ void insert(const Rectangle2& box, const T& id); /* ~noNodes~ Computes the number of non-object-nodes within the tree. */ int noNodes() const; /* ~noLeaves~ Returns the number of leaves within the tree. */ int noLeaves() const; /* ~noObjects~ Computes the number of stored objects. If an objects is multiple stored, each instance is count. */ int noObjects() const; /* ~height~ Computes the height of the tree rooted by ~root~. */ int height() const; /* ~usedMem~ Returns the memory used by this structure in bytes */ size_t usedMem() const; /* 1.1 An iterator class This class can be used to traverse the tree without collecting all results intersecting a given rectangle. */ class iterator{ enum IterationMode { all, bbox, pred }; public: /* 1.1.1 Constructor This constructor should only be used by the RTreeT class. */ iterator(NodeT* root, const Rectangle2& r) : mode(IterationMode::bbox), path(), box(r), predicate(0) { if(root!=0 && accept(root)){ init(root); } } iterator(NodeT* root) : mode(IterationMode::all), path(), box(), predicate(0) { if(root!=0){ box = root->box; init(root); } } iterator(NodeT* root, std::function&)>* predicate) : mode(IterationMode::pred), path(), box(), predicate(predicate) { if(root!=0 && accept(root)){ box = root->box; this->predicate = predicate; init(root); } } /* Returns the next element from this tree. If no more elements are available, 0 is returned. The caller must not destroy the pointer returned. */ T const * next() { while(!path.empty()){ std::pair< NodeT*, int> t = path.top(); path.pop(); NodeT* node = t.first; int pos = t.second; pos++; // try to find an intersecting entry while(pos < node->count){ if(accept(node->sons[pos])){ t.second = pos; path.push(t); return &(node->sons[pos]->id); } else { pos++; } } computeNext(); } return 0; } /* Returns an id for the node of the element last retrieved. */ uintptr_t getNodeId() { return path.empty() ? 0 : reinterpret_cast(path.top().first); } private: // the iteration mode const IterationMode mode; // the current stack std::stack > path; // the search rectangle Rectangle2 box; // a predicate to determine intersection const std::function&)>* predicate; bool accept(const NodeT* node) const { switch(mode) { case all: return true; case bbox: return node->box.Intersects(this->box); case pred: return (*(this->predicate))(node->box); default: assert (false); return true; } } // initializes the stack void init(NodeT* root){ if(root->isLeaf()){ std::pair p(root,-1); path.push(p); } else { std::pair p(root,-1); path.push(p); computeNext(); } } /* ~computeNext~ This function searches the next leaf having an entry with a box intersecting the searchbox or fulfilling the predicate respectively. */ void computeNext(){ bool found = false; while(!path.empty() && !found){ // get the topmost entry std::pair t = path.top(); path.pop(); // topmost element (must be updated) NodeT* n = t.first; int pos = t.second; // use the next pos pos++; bool changed = false; while(pos < n->count && !changed){ NodeT* son = n->sons[pos]; if(accept(son)){ // try this path std::pair p(son,-1); changed = true; t.second = pos; // save new state path.push(t); path.push(p); found = son->isLeaf(); } else { pos++; } } } } }; /* ~find~ This function returns an iterator which returns all entries with a box intersecting ~r~. The caller is responsible to destroy the iterator after using it. Whenever the tree is changed (inserting or deleting objects), this iterator is invalid and will crash when used anyway. */ iterator* find(const Rectangle2& r) const{ return new iterator(root, r); } /* ~find~ This function returns an iterator which returns all entries with bounding boxes for which the predicate returns true. The caller is responsible to destroy the iterator after using it. Whenever the tree is changed (inserting or deleting objects), this iterator is invalid and will crash when used anyway. */ iterator* find(std::function&)>* predicate) const { return new iterator(root, predicate); } /* ~entries~ Returns an iterator traversing all entries of the tree. */ iterator* entries() const { return new iterator(root); } void printStats(size_t pointCount, size_t pointSize = 0) const; /* 2.6 private part */ private: /* ~Data Members~ */ NodeT* root; // root node /* ~noNodes~ Computes the number of non-object-nodes within the tree. */ int noNodes(const NodeT* root) const; /* ~noLeaves~ Returns the number of leaves within the tree rooted by ~root~. */ int noLeaves(const NodeT* root) const; /* ~noObjects~ Computes the number of stored objects. If an object is multiple stored, each instance is count. */ int noObjects(const NodeT* root) const; /* ~height~ Computes the height of the tree rooted by ~root~. */ int height(const NodeT* root) const; /* ~insert~ Inserts a set of subtrees at the specified levels. This function supports the ~erase~ function. */ void insert(const std::set >& Q); /* Inserts a node at the specified level. If the tree grows, true is returned. If a leaf should be inserted, just set level to -1. */ bool insertNodeAtLevel(int level, NodeT* node); /* ~insertRecAtLevel~ Function supporting the ~insertNodeAtLevel~ function. */ std::pair* insertRecAtLevel(NodeT*& root, NodeT* node, const int targetLevel, const int currentLevel); int getHeight(const NodeT* root) const; }; /* 1 Definition of the Class ~Node~ This class represents a single node within an R-tree. There is no distinction between nodes holding the objects ids, leafs nodes and inner nodes. All nodes are represented by this class. This class should only be used within the R-tree class. For this reason, all members are private and the R-Tree class is declared as a friend of this class. */ template class Node{ friend class RtreeT2; // friend class RtreeT2::iterator; private: /* 1.1 Constructors This constructor creates a leaf node. */ Node(const Rectangle2& abox, const T& _id): box(abox), count(-1), id(_id), sons(0){ } /* Copy constructor. */ Node(const Node& src): box(src.box), count(src.count), id(src.id) { if(src.sons){ sons = new Node[max+1]; for(int i = 0; i < count; ++i) sons[i] = src.sons[i]; } else { sons = 0; } } /* 1.2 The assignment operator. */ Node& operator=(const Node& src){ this->box = src.box; this->count = src.count; this->id = src.id; if(src.sons) { sons = new Node[max+1]; for(int i = 0; i < count; ++i) sons[i] = src.sons[i]; } else { sons = 0; } return *this; } /* 1.3 Destructor The destructor does not remove any sons of the node. Just the array managing the sons is removed. If the complete subtree should be removed, use a combination of destroy and delete. */ ~Node(){ if (sons) delete[] sons; } /* 1.4 ~destroy~ Deletes the subtrees rooted by this node. */ void destroy(){ if (sons) { for(int i = 0; i < count; i++){ sons[i]->destroy(); delete sons[i]; } delete[] sons; count = 0; sons = 0; } } /* ~usedMem~ Returns the memory allocated for this subtree; */ ssize_t usedMem() const{ if (sons == 0) return sizeof(*this); size_t sum = 0; for (int i = 0; i < count; ++i) sum += sons[i]->usedMem(); return sum + sizeof(*this) + sizeof(sons); } /* 1.4 Data members */ Rectangle2 box; // the bounding box int count; // current count of entries const T id; // content Node** sons; // array of sons, 0 for object nodes /* 1.5 Private functions ~Constructor~ This constructor constructs an empty inner node. */ Node(): box(), count(0), id(0) { sons = new Node*[max+1]; } /* ~append~ Appends a new entry to this node. If the node overflows, the result will be false. Not usable for "object nodes". */ bool append(Node* entry){ assert((unsigned)count <= max); sons[count] = entry; if(count == 0) this->box = entry->box; else this->box = this->box.Union(entry->box); ++count; return (unsigned) count <= max; } /* ~selectFittestSon~ Returns the index of the son which is the best one for searching further the leaf for including ~box~. */ unsigned int selectFittestSon(const Rectangle2& box)const{ assert(sons != 0 && count > 0); // initialize best fit index to be 0 double area = sons[0]->box.Area(); double extend = sons[0]->box.UnionArea(box) - area; int index = 0; for(int i = 1; i < count; ++i){ double area2 = sons[i]->box.Area(); double extend2 =sons[i]->box.UnionArea(box) - area2; if (extend2 < extend){ extend = extend2; area = area2; index = i; } else if((extend2 == extend) && (area2 < area)){ extend = extend2; area = area2; index = i; } else if((extend2 == extend) && (area2 == area) && (sons[i]->count < sons[index]->count)){ extend = extend2; area = area2; index = i; } } return index; } /* ~pickSeeds~ Returns the indexes for the seeds using quadratic split. */ std::pair pickSeeds() const{ std::pair res; double d = 0; for (int i = 0; i < count; ++i) { for (int j = i + 1; j < count; ++j) { double d2 = sons[i]->box.UnionArea(sons[j]->box) - (sons[i]->box.Area() + sons[j]->box.Area()); if (d2 > d) { res.first = i; res.second = j; d = d2; } } } if (d == 0) { // all boxes are the same res.first = 0; res.second = unsigned(count - 1); } return res; } /* ~pickNext~ returns the next index for the quadratic split algorithm. */ std::pair pickNext(const Node* grpone,const Node* grptwo) const{ assert(count > 0); double d1 = sons[0]->box.UnionArea(grpone->box); double d2 = sons[0]->box.UnionArea(grptwo->box); unsigned int index = 0; unsigned int bestgrp = -1; double d = std::abs(d1-d2); for(int i = 1; i < count; ++i){ d1 = sons[i]->box.UnionArea(grpone->box); d2 = sons[i]->box.UnionArea(grptwo->box); double d3 = std::abs(d1-d2); if (d3 > d) { d = d3; index = i; double a1 = grpone->box.Area(); double a2 = grptwo->box.Area(); d1 = d1 - a1; d2 = d2 - a2; if (d1 != d2) bestgrp = d1 < d2 ? 1 : 2; else if(a1!=a2) bestgrp = a1 < a2 ? 1 : 2; else if(grpone->count != grptwo->count) bestgrp = grpone->count < grptwo->count ? 1 : 2; else // all criteria failed bestgrp = 1; } } std::pair res; res.first = index; res.second = bestgrp; return res; } /* ~Remove~ Removes the entry at position entry from this node. If there is an underflow, the result will be false. */ bool remove(unsigned int index) { for (int i = index; i < count - 1; i++) sons[i] = sons[i + 1]; sons[count - 1] = 0; --count; return (int)count >= (int)(max / 2); } /* ~Split~ Splits this node into two ones. After calling this function, this node will be empty and all contained elements was distributed to the new nodes. */ std::pair split(){ std::pair*, Node* > res = quadraticSplit(); return res; } /* ~isLeaf~ This function checks whether this node is a leaf, i.e. whether the sons are objects nodes. */ bool isLeaf() const{ assert(sons != 0); return (count == 0) || (sons[0]->sons == 0); } /* ~quadraticSplit~ Implementation of the quadratic split algorithm. */ std::pair quadraticSplit(){ std::pair seeds = pickSeeds(); unsigned int index1 = seeds.first; unsigned int index2 = seeds.second; Node* node1 = new Node(); Node* node2 = new Node(); node1->append(sons[index1]); node2->append(sons[index2]); this->remove(std::max(index1, index2)); // max must be removed first! this->remove(std::min(index1, index2)); unsigned min = max / 2; while (count > 0) { if ((unsigned) (count + node1->count) == min) { // all entries to node1 for (int i = 0; i < count; ++i) node1->append(sons[i]); count = 0; } else if ( (unsigned)(count + node2->count) == min) { // all entries to node2 for (int i = 0; i < count; ++i) node2->append(sons[i]); count = 0; } else { std::pair next = pickNext(node1,node2); if (next.second == 1) node1->append(sons[next.first]); else node2->append(sons[next.first]); remove(next.first); } } return std::make_pair(node1,node2); } // end of quadraticsSplit }; // end of class Node /* 2 Implementation of the class RTree2 2.1 Constructor This constructor creates a new empty RTRee2. */ template RtreeT2::RtreeT2(){ root = 0; } /* 2.2 ~insert~ Inserts a box together with an id into the R-Tree. */ template void RtreeT2::insert(const Rectangle2& box, const T& id){ using NodeT = Node; if(!root) root = new NodeT(); NodeT* obj = new NodeT(box, id); insertNodeAtLevel(-1, obj); } /* ~noNodes~ Computes the number of non-object-nodes within the tree. */ template int RtreeT2::noNodes() const{ return noNodes(root); } template int RtreeT2::noNodes(const Node* root) const{ if (!root) return 0; if (root->isLeaf()) return 1; int sum = 1; for(int i = 0; i < root->count; ++i) sum += noNodes(root->sons[i]); return sum; } /* ~noLeaves~ Returns the number of leaves within the tree. */ template int RtreeT2::noLeaves() const { return noLeaves(root); } template int RtreeT2::noLeaves(const Node* root) const{ if (!root) return 0; if (root->isLeaf()) return 1; int sum = 0; for (int i = 0; i < root->count; ++i) sum += noLeaves(root->sons[i]); return sum; } /* ~noObjects~ Computes the number of stored objects. If an object is multiple stored, each instance is count. */ template int RtreeT2::noObjects() const{ return noObjects(root); } template int RtreeT2::noObjects(const Node* root)const{ if(!root) return 0; if (root->isLeaf()) return root->count; int sum = 0; for (int i = 0; i < root->count; i++) sum += noObjects(root->sons[i]); return sum; } /* ~height~ Computes the height of the tree. */ template int RtreeT2::height() const{ return height(root); } template int RtreeT2::height(const Node* root) const{ if (!root) return -1; int h = 0; const Node* node = root; while(!node->isLeaf()) { h++; node = node->sons[0]; } return h; } /* ~usedMem~ */ template size_t RtreeT2::usedMem() const{ if (!root) return sizeof(*this); else return sizeof(*this) + root->usedMem(); } /* ~insert~ Inserts a set of subtrees at the specified levels. This function supports the ~erase~ function. */ template void RtreeT2::insert( const std::set* > >& Q){ int levelDiff = 0; // store the grow of the tree typename std::set >::iterator it; for(it = Q.begin(); it != Q.end(); it++){ std::pair node = *it; int level = node.first < 0 ? node.first : node.first + levelDiff; if (insertNodeAtLevel(level, node.second)) levelDiff++; } } /* Inserts a node at the specified level. If the tree grows, true is returned. */ template bool RtreeT2::insertNodeAtLevel(int level, Node* node){ using NodeT = Node; if (!root) { root = new NodeT(); } std::pair* res = insertRecAtLevel(root,node, level,0); if (!res) { // tree does not grow return false; } else { delete root; root = new NodeT(); root->append(res->first); root->append(res->second); delete res; return true; } } template std::pair*, Node* >* RtreeT2::insertRecAtLevel( Node*& root, Node* node, const int targetLevel, const int currentLevel) { using NodeT = Node; if (root->isLeaf() || (targetLevel == currentLevel) ){ if (root->append(node)){ // no overflow return 0; } else { // overflow std::pair res = root->split(); delete root; root = 0; return new std::pair(res); } } else { // not the target node int index = root->selectFittestSon(node->box); std::pair* res; res = insertRecAtLevel(root->sons[index], node, targetLevel, currentLevel+1); if (!res){ // son was not split root->box = root->box.Union(node->box); return 0; } else { root->sons[index] = res->first; // replace old son by a split node root->box = root->box.Union(res->first->box); if (root->append(res->second)) { // no overflow delete res; return 0; } else { delete res; res = new std::pair( root->split() ); delete root; root = 0; return res; } } } } template Rectangle2 RtreeT2::getBBox() const{ return (root) ? root->box : Rectangle2(); } template void RtreeT2::printStats(size_t pointCount, size_t pointSize /* = 0 */) const { size_t entryCount = noObjects(); size_t leafCount = noLeaves(); size_t nodeCount = noNodes(); size_t memMmRTree = usedMem(); size_t memPoints = pointCount * pointSize; size_t memTotal = memMmRTree + memPoints; cout << "Memory used for MMRTree: " << formatInt(memMmRTree) << " bytes for " << formatInt(entryCount) << " entries in " << formatInt(leafCount) << " leaves + " << formatInt(nodeCount - leafCount) << " inner nodes" << endl; if (memPoints > 0) { cout << "Memory used for points : " << formatInt(memPoints) << " bytes = " << formatInt(pointCount) << " points * " << pointSize << " bytes" << endl; } cout << "Total memory used : " << formatInt(memTotal) << " bytes = " << formatInt(pointCount) << " points * " << formatInt((int)std::round(memTotal / (double)pointCount)) << " bytes" << endl << endl; } } // end of namespace