aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/SkQuadTree.cpp
blob: 97f86cfb5add9257af5684e857b1720bd84ded70 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
/*
 * 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() {
    SkDELETE(fRoot);
}

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);
}