OLD | NEW |
---|---|
(Empty) | |
1 /* | |
2 * Copyright 2014 Google Inc. | |
3 * | |
4 * Use of this source code is governed by a BSD-style license that can be | |
5 * found in the LICENSE file. | |
6 */ | |
7 | |
8 #include "SkQuadTree.h" | |
9 #include "SkTSort.h" | |
10 #include <stdio.h> | |
11 #include <vector> | |
12 | |
13 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
| |
14 public: | |
15 QuadTree(const SkIRect& boundary) | |
16 : fBoundary(boundary) | |
17 , 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.
| |
18 , fNorthEast(NULL) | |
19 , fSouthWest(NULL) | |
20 , fSouthEast(NULL) | |
21 , fCanSubdivide((fBoundary.width() * fBoundary.height()) > 0) {} | |
22 | |
23 ~QuadTree() { | |
24 clear(); | |
25 } | |
26 | |
27 void clear() { | |
28 SkDELETE(fNorthWest); | |
29 fNorthWest = NULL; | |
30 SkDELETE(fNorthEast); | |
31 fNorthEast = NULL; | |
32 SkDELETE(fSouthWest); | |
33 fSouthWest = NULL; | |
34 SkDELETE(fSouthEast); | |
35 fSouthEast = NULL; | |
36 fData.reset(); | |
37 } | |
38 | |
39 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.
| |
40 | |
41 // Insert data into the QuadTree | |
42 bool insert(SkQuadTree::Data& data) { | |
43 // Ignore objects which do not belong in this quad tree | |
44 return data.fInnerBounds.intersect(fBoundary) && doInsert(data); | |
45 } | |
46 | |
47 // Find all data which appear within a range | |
48 void queryRange(const SkIRect& range, SkTDArray<void*>* dataInRange) const { | |
49 // Automatically abort if the range does not collide with this quad | |
50 if (!SkIRect::Intersects(fBoundary, range)) { | |
51 return; // nothing added to the list | |
52 } | |
53 | |
54 // Check objects at this quad level | |
55 for (int p = 0; p < fData.count(); ++p) { | |
56 if (SkIRect::Intersects(fData[p].fBounds, range)) { | |
57 dataInRange->push(fData[p].fData); | |
58 } | |
59 } | |
60 | |
61 // Terminate here, if there are no children | |
62 if (!hasChildren()) { | |
63 return; | |
64 } | |
65 | |
66 // Otherwise, add the data from the children | |
67 fNorthWest->queryRange(range, dataInRange); | |
68 fNorthEast->queryRange(range, dataInRange); | |
69 fSouthWest->queryRange(range, dataInRange); | |
70 fSouthEast->queryRange(range, dataInRange); | |
71 } | |
72 | |
73 int getDepth(int i = 1) const { | |
74 if (hasChildren()) { | |
75 int depthNW = fNorthWest->getDepth(++i); | |
76 int depthNE = fNorthWest->getDepth(i); | |
77 int depthSW = fNorthWest->getDepth(i); | |
78 int depthSE = fNorthWest->getDepth(i); | |
79 return SkTMax(SkTMax(depthNW, depthNE), SkTMax(depthSW, depthSE)); | |
80 } | |
81 return i; | |
82 } | |
83 | |
84 private: | |
85 // create four children which fully divide this quad into four quads of equa l area | |
86 void subdivide() { | |
87 if (!hasChildren() && fCanSubdivide) { | |
88 SkPoint center = SkPoint::Make(fBoundary.centerX(), fBoundary.center Y()); | |
89 fNorthWest = SkNEW_ARGS(QuadTree, (SkIRect::MakeLTRB( | |
90 fBoundary.fLeft, fBoundary.fTop, center.fX, center.fY))); | |
91 fNorthEast = SkNEW_ARGS(QuadTree, (SkIRect::MakeLTRB( | |
92 center.fX, fBoundary.fTop, fBoundary.fRight, center.fY))); | |
93 fSouthWest = SkNEW_ARGS(QuadTree, (SkIRect::MakeLTRB( | |
94 fBoundary.fLeft, center.fY, center.fX, fBoundary.fBottom))); | |
95 fSouthEast = SkNEW_ARGS(QuadTree, (SkIRect::MakeLTRB( | |
96 center.fX, center.fY, fBoundary.fRight, fBoundary.fBottom))); | |
97 | |
98 // If any of the data can fit entirely into a subregion, move it dow n now | |
99 for (int i = fData.count() - 1; i >= 0; --i) { | |
100 // If the data fits entirely into one of the 4 subregions, move that data | |
101 // down to that subregion. | |
102 if (fNorthWest->doInsert(fData[i]) || | |
103 fNorthEast->doInsert(fData[i]) || | |
104 fSouthWest->doInsert(fData[i]) || | |
105 fSouthEast->doInsert(fData[i])) { | |
106 fData.remove(i); | |
107 } | |
108 } | |
109 } | |
110 } | |
111 | |
112 bool doInsert(const SkQuadTree::Data& data) { | |
113 if (!fBoundary.contains(data.fInnerBounds)) { | |
114 return false; | |
115 } | |
116 | |
117 if (fData.count() > QT_NODE_CAPACITY) { | |
118 subdivide(); | |
119 } | |
120 | |
121 // If there is space in this quad tree, add the object here | |
122 // If this quadtree can't be subdivided, we have no choice but to add it here | |
123 if ((fData.count() <= QT_NODE_CAPACITY) || !fCanSubdivide) { | |
124 if (fData.isEmpty()) { | |
125 fData.setReserve(QT_NODE_CAPACITY); | |
126 } | |
127 fData.push(data); | |
128 } else if (!fNorthWest->doInsert(data) && !fNorthEast->doInsert(data) && | |
129 !fSouthWest->doInsert(data) && !fSouthEast->doInsert(data)) { | |
130 // Can't be pushed down to children ? keep it here | |
131 fData.push(data); | |
132 } | |
133 | |
134 return true; | |
135 } | |
136 | |
137 bool hasChildren() const { | |
138 return (NULL != fNorthWest); | |
139 } | |
140 | |
141 // Arbitrary constant to indicate how many elements can be stored in this qu ad tree node | |
142 static const int QT_NODE_CAPACITY = 4; | |
143 | |
144 // Axis-aligned bounding box stored as a center with half-dimensions | |
145 // to represent the boundaries of this quad tree | |
146 SkIRect fBoundary; | |
147 | |
148 // Data in this quad tree node | |
149 SkTDArray<SkQuadTree::Data> fData; | |
150 | |
151 // Children | |
152 QuadTree* fNorthWest; | |
153 QuadTree* fNorthEast; | |
154 QuadTree* fSouthWest; | |
155 QuadTree* fSouthEast; | |
156 bool fCanSubdivide; | |
157 }; | |
158 | |
159 //////////////////////////////////////////////////////////////////////////////// /////////////////// | |
160 | |
161 SkQuadTree* SkQuadTree::Create(const SkIRect& bounds) { | |
162 return new SkQuadTree(bounds); | |
163 } | |
164 | |
165 SkQuadTree::SkQuadTree(const SkIRect& bounds) | |
166 : fCount(0) | |
167 , fQuadTree(SkNEW_ARGS(QuadTree, (bounds))) { | |
168 SkASSERT((bounds.width() * bounds.height()) > 0); | |
169 } | |
170 | |
171 SkQuadTree::~SkQuadTree() { | |
172 } | |
173 | |
174 void SkQuadTree::insert(void* data, const SkIRect& bounds, bool) { | |
175 if (bounds.isEmpty()) { | |
176 SkASSERT(false); | |
177 return; | |
178 } | |
179 | |
180 SkQuadTree::Data quadTreeData(bounds, data); | |
181 fQuadTree->insert(quadTreeData); | |
182 ++fCount; | |
183 } | |
184 | |
185 void SkQuadTree::search(const SkIRect& query, SkTDArray<void*>* results) { | |
186 SkASSERT(NULL != results); | |
187 fQuadTree->queryRange(query, results); | |
188 } | |
189 | |
190 void SkQuadTree::clear() { | |
191 fCount = 0; | |
192 fQuadTree->clear(); | |
193 } | |
194 | |
195 int SkQuadTree::getDepth() const { | |
196 return fQuadTree->getDepth(); | |
197 } | |
OLD | NEW |