Index: src/core/SkQuadTree.cpp |
diff --git a/src/core/SkQuadTree.cpp b/src/core/SkQuadTree.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..fd9e757a036017016bbe2676d077bbde0fa34448 |
--- /dev/null |
+++ b/src/core/SkQuadTree.cpp |
@@ -0,0 +1,197 @@ |
+/* |
+ * 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::QuadTree { |
Justin Novosad
2014/01/28 21:26:13
I find SkQuadTree vs. QuadTree confusing. Why is
sugoi1
2014/01/29 15:29:08
Renamed to QuadTreeNode
|
+public: |
+ QuadTree(const SkIRect& boundary) |
+ : fBoundary(boundary) |
+ , fNorthWest(NULL) |
Justin Novosad
2014/01/28 21:26:13
We use top/left/right/bottom in skia.
sugoi1
2014/01/29 15:29:08
Done.
|
+ , fNorthEast(NULL) |
+ , fSouthWest(NULL) |
+ , fSouthEast(NULL) |
+ , fCanSubdivide((fBoundary.width() * fBoundary.height()) > 0) {} |
+ |
+ ~QuadTree() { |
+ clear(); |
+ } |
+ |
+ void clear() { |
+ SkDELETE(fNorthWest); |
+ fNorthWest = NULL; |
+ SkDELETE(fNorthEast); |
+ fNorthEast = NULL; |
+ SkDELETE(fSouthWest); |
+ fSouthWest = NULL; |
+ SkDELETE(fSouthEast); |
+ fSouthEast = NULL; |
+ fData.reset(); |
+ } |
+ |
+ const SkIRect& getBoundary() const { return fBoundary; } |
Justin Novosad
2014/01/28 21:26:13
Bondary -> Bounds, to be consistent with rest of s
sugoi1
2014/01/29 15:29:08
Done.
|
+ |
+ // Insert data into the QuadTree |
+ bool insert(SkQuadTree::Data& data) { |
+ // Ignore objects which do not belong in this quad tree |
+ return data.fInnerBounds.intersect(fBoundary) && 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(fBoundary, range)) { |
+ return; // nothing added to the list |
+ } |
+ |
+ // Check objects at this quad level |
+ for (int p = 0; p < fData.count(); ++p) { |
+ if (SkIRect::Intersects(fData[p].fBounds, range)) { |
+ dataInRange->push(fData[p].fData); |
+ } |
+ } |
+ |
+ // Terminate here, if there are no children |
+ if (!hasChildren()) { |
+ return; |
+ } |
+ |
+ // Otherwise, add the data from the children |
+ fNorthWest->queryRange(range, dataInRange); |
+ fNorthEast->queryRange(range, dataInRange); |
+ fSouthWest->queryRange(range, dataInRange); |
+ fSouthEast->queryRange(range, dataInRange); |
+ } |
+ |
+ int getDepth(int i = 1) const { |
+ if (hasChildren()) { |
+ int depthNW = fNorthWest->getDepth(++i); |
+ int depthNE = fNorthWest->getDepth(i); |
+ int depthSW = fNorthWest->getDepth(i); |
+ int depthSE = fNorthWest->getDepth(i); |
+ return SkTMax(SkTMax(depthNW, depthNE), SkTMax(depthSW, depthSE)); |
+ } |
+ return i; |
+ } |
+ |
+private: |
+ // create four children which fully divide this quad into four quads of equal area |
+ void subdivide() { |
+ if (!hasChildren() && fCanSubdivide) { |
+ SkPoint center = SkPoint::Make(fBoundary.centerX(), fBoundary.centerY()); |
+ fNorthWest = SkNEW_ARGS(QuadTree, (SkIRect::MakeLTRB( |
+ fBoundary.fLeft, fBoundary.fTop, center.fX, center.fY))); |
+ fNorthEast = SkNEW_ARGS(QuadTree, (SkIRect::MakeLTRB( |
+ center.fX, fBoundary.fTop, fBoundary.fRight, center.fY))); |
+ fSouthWest = SkNEW_ARGS(QuadTree, (SkIRect::MakeLTRB( |
+ fBoundary.fLeft, center.fY, center.fX, fBoundary.fBottom))); |
+ fSouthEast = SkNEW_ARGS(QuadTree, (SkIRect::MakeLTRB( |
+ center.fX, center.fY, fBoundary.fRight, fBoundary.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 (fNorthWest->doInsert(fData[i]) || |
+ fNorthEast->doInsert(fData[i]) || |
+ fSouthWest->doInsert(fData[i]) || |
+ fSouthEast->doInsert(fData[i])) { |
+ fData.remove(i); |
+ } |
+ } |
+ } |
+ } |
+ |
+ bool doInsert(const SkQuadTree::Data& data) { |
+ if (!fBoundary.contains(data.fInnerBounds)) { |
+ return false; |
+ } |
+ |
+ if (fData.count() > QT_NODE_CAPACITY) { |
+ 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() <= QT_NODE_CAPACITY) || !fCanSubdivide) { |
+ if (fData.isEmpty()) { |
+ fData.setReserve(QT_NODE_CAPACITY); |
+ } |
+ fData.push(data); |
+ } else if (!fNorthWest->doInsert(data) && !fNorthEast->doInsert(data) && |
+ !fSouthWest->doInsert(data) && !fSouthEast->doInsert(data)) { |
+ // Can't be pushed down to children ? keep it here |
+ fData.push(data); |
+ } |
+ |
+ return true; |
+ } |
+ |
+ bool hasChildren() const { |
+ return (NULL != fNorthWest); |
+ } |
+ |
+ // Arbitrary constant to indicate how many elements can be stored in this quad tree node |
+ static const int QT_NODE_CAPACITY = 4; |
+ |
+ // Axis-aligned bounding box stored as a center with half-dimensions |
+ // to represent the boundaries of this quad tree |
+ SkIRect fBoundary; |
+ |
+ // Data in this quad tree node |
+ SkTDArray<SkQuadTree::Data> fData; |
+ |
+ // Children |
+ QuadTree* fNorthWest; |
+ QuadTree* fNorthEast; |
+ QuadTree* fSouthWest; |
+ QuadTree* fSouthEast; |
+ bool fCanSubdivide; |
+}; |
+ |
+/////////////////////////////////////////////////////////////////////////////////////////////////// |
+ |
+SkQuadTree* SkQuadTree::Create(const SkIRect& bounds) { |
+ return new SkQuadTree(bounds); |
+} |
+ |
+SkQuadTree::SkQuadTree(const SkIRect& bounds) |
+ : fCount(0) |
+ , fQuadTree(SkNEW_ARGS(QuadTree, (bounds))) { |
+ SkASSERT((bounds.width() * bounds.height()) > 0); |
+} |
+ |
+SkQuadTree::~SkQuadTree() { |
+} |
+ |
+void SkQuadTree::insert(void* data, const SkIRect& bounds, bool) { |
+ if (bounds.isEmpty()) { |
+ SkASSERT(false); |
+ return; |
+ } |
+ |
+ SkQuadTree::Data quadTreeData(bounds, data); |
+ fQuadTree->insert(quadTreeData); |
+ ++fCount; |
+} |
+ |
+void SkQuadTree::search(const SkIRect& query, SkTDArray<void*>* results) { |
+ SkASSERT(NULL != results); |
+ fQuadTree->queryRange(query, results); |
+} |
+ |
+void SkQuadTree::clear() { |
+ fCount = 0; |
+ fQuadTree->clear(); |
+} |
+ |
+int SkQuadTree::getDepth() const { |
+ return fQuadTree->getDepth(); |
+} |