/* * Copyright 2012, The Android Open Source Project * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define LOG_TAG "RTree" #define LOG_NDEBUG 1 #include "config.h" #include "RTree.h" #include "AndroidLog.h" #include "LinearAllocator.h" namespace WebCore { void* RecordingData::operator new(size_t size, LinearAllocator* allocator) { return allocator->alloc(size); } } namespace RTree { #ifdef DEBUG static unsigned gID = 0; #endif class Element; ////////////////////////////////////////////////////////////////////// // utility functions used by ElementList and Node static void recomputeBounds(int& minx, int& miny, int& maxx, int& maxy, unsigned& nbChildren, Node**& children, int* area) { // compute the bounds if (nbChildren) { minx = children[0]->m_minX; miny = children[0]->m_minY; maxx = children[0]->m_maxX; maxy = children[0]->m_maxY; } for (unsigned int i = 1; i < nbChildren; i++) { minx = std::min(minx, children[i]->m_minX); miny = std::min(miny, children[i]->m_minY); maxx = std::max(maxx, children[i]->m_maxX); maxy = std::max(maxy, children[i]->m_maxY); } if (area) { int w = maxx - minx; int h = maxy - miny; *area = w * h; } } int computeDeltaArea(Node* node, int& minx, int& miny, int& maxx, int& maxy) { int newMinX = std::min(minx, node->m_minX); int newMinY = std::min(miny, node->m_minY); int newMaxX = std::max(maxx, node->m_maxX); int newMaxY = std::max(maxy, node->m_maxY); int w = newMaxX - newMinX; int h = newMaxY - newMinY; return w * h; } ////////////////////////////////////////////////////////////////////// // RTree ////////////////////////////////////////////////////////////////////// // // This is an implementation of the R-Tree data structure // "R-Trees - a dynamic index structure for spatial searching", Guttman(84) // // The structure works as follow -- elements have bounds, intermediate // nodes will also maintain bounds (the union of their children' bounds). // // Searching is simple -- we just traverse the tree comparing the bounds // until we find the elements we are interested in. // // Each node can have at most M children -- the performances / memory usage // is strongly impacted by a choice of a good M value (RTree::m_maxChildren). // // Inserting an element // -------------------- // // To find the leaf node N where we can insert a new element (RTree::insert(), // Node::insert()), we need to traverse the tree, picking the branch where // adding the new element will result in the least growth of its bounds, // until we reach a leaf node (Node::findNode()). // // If the number of children of that leaf node is under M, we simply // insert it. Otherwise, if we reached maximum capacity for that leaf, // we split the list of children (Node::split()), creating two lists, // where each list' elements is as far as each other as possible // (to decrease the likelyhood of future splits). // // We can then assign one of the list to the original leaf node N, and // we then create a new node NN that we try to attach to N's parent. // // If N's parent is also full, we go up in the hierachy and repeat // (Node::adjustTree()). // ////////////////////////////////////////////////////////////////////// RTree::RTree(WebCore::LinearAllocator* allocator, int M) : m_allocator(allocator) { m_maxChildren = M; m_listA = new ElementList(M); m_listB = new ElementList(M); m_root = Node::create(this); } RTree::~RTree() { delete m_listA; delete m_listB; deleteNode(m_root); } void RTree::insert(WebCore::IntRect& bounds, WebCore::RecordingData* payload) { Node* e = Node::create(this, bounds.x(), bounds.y(), bounds.maxX(), bounds.maxY(), payload); m_root->insert(e); } void RTree::search(WebCore::IntRect& clip, Vector&list) { m_root->search(clip.x(), clip.y(), clip.maxX(), clip.maxY(), list); } void RTree::remove(WebCore::IntRect& clip) { m_root->remove(clip.x(), clip.y(), clip.maxX(), clip.maxY()); } void RTree::display() { #ifdef DEBUG m_root->drawTree(); #endif } void* RTree::allocateNode() { return m_allocator->alloc(sizeof(Node)); } void RTree::deleteNode(Node* n) { if (n) n->~Node(); } ////////////////////////////////////////////////////////////////////// // ElementList ElementList::ElementList(int size) : m_nbChildren(0) , m_minX(0) , m_maxX(0) , m_minY(0) , m_maxY(0) , m_area(0) , m_didTighten(false) { m_children = new Node*[size]; } ElementList::~ElementList() { delete[] m_children; } void ElementList::add(Node* node) { m_children[m_nbChildren] = node; m_nbChildren++; m_didTighten = false; } void ElementList::tighten() { if (m_didTighten) return; recomputeBounds(m_minX, m_minY, m_maxX, m_maxY, m_nbChildren, m_children, &m_area); m_didTighten = true; } int ElementList::delta(Node* node) { if (!m_didTighten) tighten(); return computeDeltaArea(node, m_minX, m_minY, m_maxX, m_maxY); } void ElementList::removeAll() { m_nbChildren = 0; m_minX = 0; m_maxX = 0; m_minY = 0; m_maxY = 0; m_area = 0; m_didTighten = false; } void ElementList::display() { #ifdef DEBUG for (unsigned int i = 0; i < m_nbChildren; i++) m_children[i]->display(0); #endif } ////////////////////////////////////////////////////////////////////// // Node Node::Node(RTree* t, int minx, int miny, int maxx, int maxy, WebCore::RecordingData* payload) : m_tree(t) , m_parent(0) , m_children(0) , m_nbChildren(0) , m_payload(payload) , m_minX(minx) , m_minY(miny) , m_maxX(maxx) , m_maxY(maxy) #ifdef DEBUG , m_tid(gID++) #endif { #ifdef DEBUG ALOGV("-> New Node %d", m_tid); #endif } Node::~Node() { for (unsigned i = 0; i < m_nbChildren; i++) m_tree->deleteNode(m_children[i]); delete[] m_children; if (m_payload) m_payload->~RecordingData(); } void Node::setParent(Node* node) { m_parent = node; } void Node::insert(Node* node) { Node* N = findNode(node); #ifdef DEBUG ALOGV("-> Insert Node %d (%d, %d) in node %d", node->m_tid, node->m_minX, node->m_minY, N->m_tid); #endif N->add(node); } Node* Node::findNode(Node* node) { if (m_nbChildren == 0) return m_parent ? m_parent : this; // pick the child whose bounds will be extended least Node* pick = m_children[0]; int minIncrease = pick->delta(node); for (unsigned int i = 1; i < m_nbChildren; i++) { int increase = m_children[i]->delta(node); if (increase < minIncrease) { minIncrease = increase; pick = m_children[i]; } } return pick->findNode(node); } void Node::tighten() { recomputeBounds(m_minX, m_minY, m_maxX, m_maxY, m_nbChildren, m_children, 0); } int Node::delta(Node* node) { return computeDeltaArea(node, m_minX, m_minY, m_maxX, m_maxY); } void Node::simpleAdd(Node* node) { node->setParent(this); if (!m_children) m_children = new Node*[m_tree->m_maxChildren + 1]; m_children[m_nbChildren] = node; m_nbChildren++; } void Node::add(Node* node) { simpleAdd(node); Node* NN = 0; if (m_nbChildren > m_tree->m_maxChildren) NN = split(); adjustTree(this, NN); } void Node::remove(Node* node) { int nodeIndex = -1; for (unsigned int i = 0; i < m_nbChildren; i++) { if (m_children[i] == node) { nodeIndex = i; break; } } if (nodeIndex == -1) return; // compact for (unsigned int i = nodeIndex; i < m_nbChildren - 1; i++) m_children[i] = m_children[i + 1]; m_nbChildren--; } void Node::destroy(int index) { delete m_children[index]; // compact for (unsigned int i = index; i < m_nbChildren - 1; i++) m_children[i] = m_children[i + 1]; m_nbChildren--; } void Node::removeAll() { m_nbChildren = 0; m_minX = 0; m_maxX = 0; m_minY = 0; m_maxY = 0; } Node* Node::split() { // First, let's get the seeds // The idea is to get elements as distant as possible // as we can, so that the resulting splitted lists // will be more likely to not overlap. Node* minElementX = m_children[0]; Node* maxElementX = m_children[0]; Node* minElementY = m_children[0]; Node* maxElementY = m_children[0]; for (unsigned int i = 1; i < m_nbChildren; i++) { if (m_children[i]->m_minX < minElementX->m_minX) minElementX = m_children[i]; if (m_children[i]->m_minY < minElementY->m_minY) minElementY = m_children[i]; if (m_children[i]->m_maxX >= maxElementX->m_maxX) maxElementX = m_children[i]; if (m_children[i]->m_maxY >= maxElementY->m_maxY) maxElementY = m_children[i]; } int dx = maxElementX->m_maxX - minElementX->m_minX; int dy = maxElementY->m_maxY - minElementY->m_minY; // assign the two seeds... Node* elementA = minElementX; Node* elementB = maxElementX; if (dx < dy) { elementA = minElementY; elementB = maxElementY; } // If we get the same element, just get the first and // last element inserted... if (elementA == elementB) { elementA = m_children[0]; elementB = m_children[m_nbChildren - 1]; } #ifdef DEBUG ALOGV("split Node %d, dx: %d dy: %d elem A is %d, elem B is %d", m_tid, dx, dy, elementA->m_tid, elementB->m_tid); #endif // Let's use some temporary lists to do the split ElementList* listA = m_tree->m_listA; ElementList* listB = m_tree->m_listB; listA->removeAll(); listB->removeAll(); listA->add(elementA); listB->add(elementB); remove(elementA); remove(elementB); // For any remaining elements, insert it into the list // resulting in the smallest growth for (unsigned int i = 0; i < m_nbChildren; i++) { Node* node = m_children[i]; int dA = listA->delta(node); int dB = listB->delta(node); if (dA < dB && listA->m_nbChildren < m_tree->m_maxChildren) listA->add(node); else if (dB < dA && listB->m_nbChildren < m_tree->m_maxChildren) listB->add(node); else { ElementList* smallestList = listA->m_nbChildren > listB->m_nbChildren ? listB : listA; smallestList->add(node); } } // Use the list to rebuild the nodes removeAll(); for (unsigned int i = 0; i < listA->m_nbChildren; i++) simpleAdd(listA->m_children[i]); Node* NN = Node::create(m_tree); for (unsigned int i = 0; i < listB->m_nbChildren; i++) NN->simpleAdd(listB->m_children[i]); NN->tighten(); return NN; } bool Node::isRoot() { return m_tree->m_root == this; } void Node::adjustTree(Node* N, Node* NN) { bool callParent = N->updateBounds(); if (N->isRoot() && NN) { // build new root Node* root = Node::create(m_tree); #ifdef DEBUG ALOGV("-> node %d created as new root", root->m_tid); #endif root->simpleAdd(N); root->simpleAdd(NN); root->tighten(); m_tree->m_root = root; return; } if (N->isRoot()) return; if (N->m_parent) { if (NN) N->m_parent->add(NN); else if (callParent) adjustTree(N->m_parent, 0); } } bool Node::updateBounds() { int ominx = m_minX; int ominy = m_minY; int omaxx = m_maxX; int omaxy = m_maxY; tighten(); if ((ominx != m_minX) || (ominy != m_minY) || (omaxx != m_maxX) || (omaxy != m_maxY)) return true; return false; } #ifdef DEBUG static int gMaxLevel = 0; static int gNbNodes = 0; static int gNbElements = 0; void Node::drawTree(int level) { if (level == 0) { ALOGV("\n*** show tree ***\n"); gMaxLevel = 0; gNbNodes = 0; gNbElements = 0; } display(level); for (unsigned int i = 0; i < m_nbChildren; i++) { m_children[i]->drawTree(level + 1); } if (gMaxLevel < level) gMaxLevel = level; if (!m_nbChildren) gNbElements++; else gNbNodes++; if (level == 0) { ALOGV("********************\n"); ALOGV("Depth level %d, total bytes: %d, %d nodes, %d bytes (%d bytes/node), %d elements, %d bytes (%d bytes/node)", gMaxLevel, gNbNodes * sizeof(Node) + gNbElements * sizeof(Element), gNbNodes, gNbNodes * sizeof(Node), sizeof(Node), gNbElements, gNbElements * sizeof(Element), sizeof(Element)); } } #endif #ifdef DEBUG void Node::display(int level) { ALOGV("%*sNode %d - %d, %d, %d, %d (%d x %d)", 2*level, "", m_tid, m_minX, m_minY, m_maxX, m_maxY, m_maxX - m_minX, m_maxY - m_minY); } #endif bool Node::overlap(int minx, int miny, int maxx, int maxy) { return ! (minx > m_maxX || maxx < m_minX || maxy < m_minY || miny > m_maxY); } void Node::search(int minx, int miny, int maxx, int maxy, Vector& list) { if (isElement() && overlap(minx, miny, maxx, maxy)) list.append(this->m_payload); for (unsigned int i = 0; i < m_nbChildren; i++) { if (m_children[i]->overlap(minx, miny, maxx, maxy)) m_children[i]->search(minx, miny, maxx, maxy, list); } } bool Node::inside(int minx, int miny, int maxx, int maxy) { return (minx <= m_minX && maxx >= m_maxX && miny <= m_minY && maxy >= m_maxY); } void Node::remove(int minx, int miny, int maxx, int maxy) { for (unsigned int i = 0; i < m_nbChildren; i++) { if (m_children[i]->inside(minx, miny, maxx, maxy)) destroy(i); else if (m_children[i]->overlap(minx, miny, maxx, maxy)) m_children[i]->remove(minx, miny, maxx, maxy); } } } // Namespace RTree