| Index: src/core/SkQuadTree.cpp
|
| diff --git a/src/core/SkQuadTree.cpp b/src/core/SkQuadTree.cpp
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..c6ed4bf2e4bfc068557396b24e21b119a1646072
|
| --- /dev/null
|
| +++ b/src/core/SkQuadTree.cpp
|
| @@ -0,0 +1,224 @@
|
| +/*
|
| + * Copyright 2014 Google Inc.
|
| + *
|
| + * Use of this source code is governed by a BSD-style license that can be
|
| + * found in the LICENSE file.
|
| + */
|
| +
|
| +#include "SkQuadTree.h"
|
| +#include "SkTSort.h"
|
| +#include <stdio.h>
|
| +#include <vector>
|
| +
|
| +class SkQuadTree::QuadTreeNode {
|
| +public:
|
| + struct Data {
|
| + Data(const SkIRect& bounds, void* data) : fBounds(bounds), fInnerBounds(bounds), fData(data) {}
|
| + SkIRect fBounds;
|
| + SkIRect fInnerBounds;
|
| + void* fData;
|
| + };
|
| +
|
| + QuadTreeNode(const SkIRect& bounds)
|
| + : fBounds(bounds)
|
| + , fTopLeft(NULL)
|
| + , fTopRight(NULL)
|
| + , fBottomLeft(NULL)
|
| + , fBottomRight(NULL)
|
| + , fCanSubdivide((fBounds.width() * fBounds.height()) > 0) {}
|
| +
|
| + ~QuadTreeNode() {
|
| + clear();
|
| + }
|
| +
|
| + void clear() {
|
| + SkDELETE(fTopLeft);
|
| + fTopLeft = NULL;
|
| + SkDELETE(fTopRight);
|
| + fTopRight = NULL;
|
| + SkDELETE(fBottomLeft);
|
| + fBottomLeft = NULL;
|
| + SkDELETE(fBottomRight);
|
| + fBottomRight = NULL;
|
| + fData.reset();
|
| + }
|
| +
|
| + const SkIRect& getBounds() const { return fBounds; }
|
| +
|
| + // Insert data into the QuadTreeNode
|
| + bool insert(Data& data) {
|
| + // Ignore objects which do not belong in this quad tree
|
| + return data.fInnerBounds.intersect(fBounds) && doInsert(data);
|
| + }
|
| +
|
| + // Find all data which appear within a range
|
| + void queryRange(const SkIRect& range, SkTDArray<void*>* dataInRange) const {
|
| + // Automatically abort if the range does not collide with this quad
|
| + if (!SkIRect::Intersects(fBounds, range)) {
|
| + return; // nothing added to the list
|
| + }
|
| +
|
| + // Check objects at this quad level
|
| + for (int i = 0; i < fData.count(); ++i) {
|
| + if (SkIRect::Intersects(fData[i].fBounds, range)) {
|
| + dataInRange->push(fData[i].fData);
|
| + }
|
| + }
|
| +
|
| + // Terminate here, if there are no children
|
| + if (!hasChildren()) {
|
| + return;
|
| + }
|
| +
|
| + // Otherwise, add the data from the children
|
| + fTopLeft->queryRange(range, dataInRange);
|
| + fTopRight->queryRange(range, dataInRange);
|
| + fBottomLeft->queryRange(range, dataInRange);
|
| + fBottomRight->queryRange(range, dataInRange);
|
| + }
|
| +
|
| + int getDepth(int i = 1) const {
|
| + if (hasChildren()) {
|
| + int depthTL = fTopLeft->getDepth(++i);
|
| + int depthTR = fTopRight->getDepth(i);
|
| + int depthBL = fBottomLeft->getDepth(i);
|
| + int depthBR = fBottomRight->getDepth(i);
|
| + return SkTMax(SkTMax(depthTL, depthTR), SkTMax(depthBL, depthBR));
|
| + }
|
| + return i;
|
| + }
|
| +
|
| + void rewindInserts(SkBBoxHierarchyClient* client) {
|
| + for (int i = fData.count() - 1; i >= 0; --i) {
|
| + if (client->shouldRewind(fData[i].fData)) {
|
| + fData.remove(i);
|
| + }
|
| + }
|
| + if (hasChildren()) {
|
| + fTopLeft->rewindInserts(client);
|
| + fTopRight->rewindInserts(client);
|
| + fBottomLeft->rewindInserts(client);
|
| + fBottomRight->rewindInserts(client);
|
| + }
|
| + }
|
| +
|
| +private:
|
| + // create four children which fully divide this quad into four quads of equal area
|
| + void subdivide() {
|
| + if (!hasChildren() && fCanSubdivide) {
|
| + SkIPoint center = SkIPoint::Make(fBounds.centerX(), fBounds.centerY());
|
| + fTopLeft = SkNEW_ARGS(QuadTreeNode, (SkIRect::MakeLTRB(
|
| + fBounds.fLeft, fBounds.fTop, center.fX, center.fY)));
|
| + fTopRight = SkNEW_ARGS(QuadTreeNode, (SkIRect::MakeLTRB(
|
| + center.fX, fBounds.fTop, fBounds.fRight, center.fY)));
|
| + fBottomLeft = SkNEW_ARGS(QuadTreeNode, (SkIRect::MakeLTRB(
|
| + fBounds.fLeft, center.fY, center.fX, fBounds.fBottom)));
|
| + fBottomRight = SkNEW_ARGS(QuadTreeNode, (SkIRect::MakeLTRB(
|
| + center.fX, center.fY, fBounds.fRight, fBounds.fBottom)));
|
| +
|
| + // If any of the data can fit entirely into a subregion, move it down now
|
| + for (int i = fData.count() - 1; i >= 0; --i) {
|
| + // If the data fits entirely into one of the 4 subregions, move that data
|
| + // down to that subregion.
|
| + if (fTopLeft->doInsert(fData[i]) ||
|
| + fTopRight->doInsert(fData[i]) ||
|
| + fBottomLeft->doInsert(fData[i]) ||
|
| + fBottomRight->doInsert(fData[i])) {
|
| + fData.remove(i);
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + bool doInsert(const Data& data) {
|
| + if (!fBounds.contains(data.fInnerBounds)) {
|
| + return false;
|
| + }
|
| +
|
| + if (fData.count() > kQuadTreeNodeCapacity) {
|
| + subdivide();
|
| + }
|
| +
|
| + // If there is space in this quad tree, add the object here
|
| + // If this quadtree can't be subdivided, we have no choice but to add it here
|
| + if ((fData.count() <= kQuadTreeNodeCapacity) || !fCanSubdivide) {
|
| + if (fData.isEmpty()) {
|
| + fData.setReserve(kQuadTreeNodeCapacity);
|
| + }
|
| + fData.push(data);
|
| + } else if (!fTopLeft->doInsert(data) && !fTopRight->doInsert(data) &&
|
| + !fBottomLeft->doInsert(data) && !fBottomRight->doInsert(data)) {
|
| + // Can't be pushed down to children ? keep it here
|
| + fData.push(data);
|
| + }
|
| +
|
| + return true;
|
| + }
|
| +
|
| + bool hasChildren() const {
|
| + return (NULL != fTopLeft);
|
| + }
|
| +
|
| + // Arbitrary constant to indicate how many elements can be stored in this quad tree node
|
| + enum { kQuadTreeNodeCapacity = 4 };
|
| +
|
| + // Bounds of this quad tree
|
| + SkIRect fBounds;
|
| +
|
| + // Data in this quad tree node
|
| + SkTDArray<Data> fData;
|
| +
|
| + // Children
|
| + QuadTreeNode* fTopLeft;
|
| + QuadTreeNode* fTopRight;
|
| + QuadTreeNode* fBottomLeft;
|
| + QuadTreeNode* fBottomRight;
|
| +
|
| + // Whether or not this node can have children
|
| + bool fCanSubdivide;
|
| +};
|
| +
|
| +///////////////////////////////////////////////////////////////////////////////////////////////////
|
| +
|
| +SkQuadTree* SkQuadTree::Create(const SkIRect& bounds) {
|
| + return new SkQuadTree(bounds);
|
| +}
|
| +
|
| +SkQuadTree::SkQuadTree(const SkIRect& bounds)
|
| + : fCount(0)
|
| + , fRoot(SkNEW_ARGS(QuadTreeNode, (bounds))) {
|
| + SkASSERT((bounds.width() * bounds.height()) > 0);
|
| +}
|
| +
|
| +SkQuadTree::~SkQuadTree() {
|
| +}
|
| +
|
| +void SkQuadTree::insert(void* data, const SkIRect& bounds, bool) {
|
| + if (bounds.isEmpty()) {
|
| + SkASSERT(false);
|
| + return;
|
| + }
|
| +
|
| + QuadTreeNode::Data quadTreeData(bounds, data);
|
| + fRoot->insert(quadTreeData);
|
| + ++fCount;
|
| +}
|
| +
|
| +void SkQuadTree::search(const SkIRect& query, SkTDArray<void*>* results) {
|
| + SkASSERT(NULL != results);
|
| + fRoot->queryRange(query, results);
|
| +}
|
| +
|
| +void SkQuadTree::clear() {
|
| + fCount = 0;
|
| + fRoot->clear();
|
| +}
|
| +
|
| +int SkQuadTree::getDepth() const {
|
| + return fRoot->getDepth();
|
| +}
|
| +
|
| +void SkQuadTree::rewindInserts() {
|
| + SkASSERT(fClient);
|
| + fRoot->rewindInserts(fClient);
|
| +}
|
|
|