OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2014 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "testing/gtest/include/gtest/gtest.h" | |
6 #include "ui/gfx/geometry/r_tree.h" | |
7 #include "ui/gfx/geometry/rect.h" | |
8 | |
9 namespace gfx { | |
10 | |
11 class RTreeTest : public ::testing::Test { | |
12 protected: | |
13 // Given a pointer to an RTree, traverse it and verify its internal structure | |
14 // is consistent with the RTree semantics. | |
15 void ValidateRTree(RTree* rt) { | |
16 // If RTree is empty it should have an empty rectangle. | |
17 if (!rt->root_->count()) { | |
18 EXPECT_TRUE(rt->root_->rect().IsEmpty()); | |
19 EXPECT_EQ(rt->root_->level(), 0); | |
20 return; | |
21 } | |
22 // Root is allowed to have fewer than min_children_ but never more than | |
23 // max_children_. | |
24 EXPECT_LE(rt->root_->count(), rt->max_children_); | |
25 // The root should never be a record node. | |
26 EXPECT_GT(rt->root_->level(), -1); | |
27 EXPECT_FALSE(rt->root_->key()); | |
28 // The root should never have a parent pointer. | |
29 EXPECT_FALSE(rt->root_->parent()); | |
30 // Bounds must be consistent on the root. | |
31 CheckBoundsConsistent(rt->root_.get()); | |
32 // We traverse root's children ourselves, so we can avoid asserts about | |
33 // root's potential inconsistencies. | |
34 for (size_t i = 0; i < rt->root_->children_.size(); ++i) { | |
35 ValidateNode( | |
36 rt->root_->children_[i], rt->min_children_, rt->max_children_); | |
37 } | |
38 } | |
39 | |
40 // Recursive descent method used by ValidateRTree to check each node within | |
41 // the RTree for consistency with RTree semantics. | |
42 void ValidateNode(RTree::Node* node, | |
43 size_t min_children, | |
44 size_t max_children) { | |
45 // Record nodes have different requirements, handle up front. | |
46 if (node->level() == -1) { | |
47 // Record nodes may have no children. | |
48 EXPECT_EQ(node->count(), 0U); | |
49 // They must have an associated non-NULL key. | |
50 EXPECT_TRUE(node->key()); | |
51 // They must always have a parent. | |
52 EXPECT_TRUE(node->parent()); | |
53 return; | |
54 } | |
55 // Non-record node, normal expectations apply. | |
56 EXPECT_GE(node->count(), min_children); | |
57 EXPECT_LE(node->count(), max_children); | |
58 EXPECT_TRUE(node->key() == NULL); | |
59 CheckBoundsConsistent(node); | |
60 for (size_t i = 0; i < node->children_.size(); ++i) { | |
61 ValidateNode(node->children_[i], min_children, max_children); | |
62 } | |
63 } | |
64 | |
65 // Check bounds are consistent with children bounds, and other checks | |
66 // convenient to do while enumerating the children of node. | |
67 void CheckBoundsConsistent(RTree::Node* node) { | |
68 EXPECT_FALSE(node->rect_.IsEmpty()); | |
69 Rect check_bounds(0, 0, 0, 0); | |
piman
2014/04/29 22:43:06
nit: Rect check_bounds; works
luken
2014/04/29 23:22:47
Done.
| |
70 for (size_t i = 0; i < node->children_.size(); ++i) { | |
71 RTree::Node* child_node = node->children_[i]; | |
72 check_bounds.Union(child_node->rect()); | |
73 EXPECT_EQ(node->level() - 1, child_node->level()); | |
74 EXPECT_EQ(node, child_node->parent()); | |
75 } | |
76 EXPECT_EQ(node->rect_, check_bounds); | |
77 } | |
78 | |
79 // Adds count squares stacked around the point (0,0) with key equal to width. | |
80 void AddStackedSquares(RTree* rt, int count) { | |
81 for (int i = 1; i <= count; ++i) { | |
82 rt->Insert(Rect(0, 0, i, i), static_cast<intptr_t>(i)); | |
83 ValidateRTree(rt); | |
84 } | |
85 } | |
86 | |
87 // Given an unordered list of matching keys, verify that it contains all | |
88 // values [1..length] for the length of that list. | |
89 void VerifyAllKeys(const base::hash_set<intptr_t>& keys) { | |
90 // Verify that the keys are in values [1,count]. | |
91 for (size_t i = 1; i <= keys.size(); ++i) { | |
92 EXPECT_TRUE(keys.count(static_cast<intptr_t>(i)) == 1U); | |
93 } | |
94 } | |
95 | |
96 // Given a node and a rectangle, builds an expanded rectangle list where the | |
97 // ith element of the rectangle is union of the recangle of the ith child of | |
98 // the node and the argument rectangle. | |
99 void BuildExpandedRects(RTree::Node* node, | |
100 const Rect& rect, | |
101 std::vector<Rect>* expanded_rects) { | |
102 expanded_rects->clear(); | |
103 expanded_rects->reserve(node->children_.size()); | |
104 for (size_t i = 0; i < node->children_.size(); ++i) { | |
105 Rect expanded_rect(rect); | |
106 expanded_rect.Union(node->children_[i]->rect_); | |
107 expanded_rects->push_back(expanded_rect); | |
108 } | |
109 } | |
110 }; | |
111 | |
112 // An empty RTree should never return Query results, and RTrees should be empty | |
113 // upon construction. | |
114 TEST_F(RTreeTest, QueryEmptyTree) { | |
115 RTree rt(2, 10); | |
116 ValidateRTree(&rt); | |
117 base::hash_set<intptr_t> results; | |
118 Rect test_rect(25, 25); | |
119 rt.Query(test_rect, &results); | |
120 EXPECT_EQ(results.size(), 0U); | |
121 ValidateRTree(&rt); | |
122 } | |
123 | |
124 // Clear should empty the tree, meaning that all queries should not return | |
125 // results after. | |
126 TEST_F(RTreeTest, ClearEmptiesTreeOfSingleNode) { | |
127 RTree rt(2, 5); | |
128 rt.Insert(Rect(0, 0, 100, 100), static_cast<intptr_t>(1)); | |
piman
2014/04/29 22:43:06
nit: static_cast superfluous (here and many places
luken
2014/04/29 23:22:47
Done.
| |
129 rt.Clear(); | |
130 base::hash_set<intptr_t> results; | |
131 Rect test_rect(1, 1); | |
132 rt.Query(test_rect, &results); | |
133 EXPECT_EQ(results.size(), 0U); | |
134 ValidateRTree(&rt); | |
135 } | |
136 | |
137 // Even with a complex internal structure, clear should empty the tree, meaning | |
138 // that all queries should not return results after. | |
139 TEST_F(RTreeTest, ClearEmptiesTreeOfManyNodes) { | |
140 RTree rt(2, 5); | |
141 AddStackedSquares(&rt, 100); | |
142 rt.Clear(); | |
143 base::hash_set<intptr_t> results; | |
144 Rect test_rect(1, 1); | |
145 rt.Query(test_rect, &results); | |
146 EXPECT_EQ(results.size(), 0U); | |
147 ValidateRTree(&rt); | |
148 } | |
149 | |
150 // Duplicate inserts should overwrite previous inserts. | |
151 TEST_F(RTreeTest, DuplicateInsertsOverwrite) { | |
152 RTree rt(2, 5); | |
153 // Add 100 stacked squares, but always with duplicate key of 1. | |
154 for (int i = 1; i <= 100; ++i) { | |
155 rt.Insert(Rect(0, 0, i, i), static_cast<intptr_t>(1)); | |
156 ValidateRTree(&rt); | |
157 } | |
158 base::hash_set<intptr_t> results; | |
159 Rect test_rect(1, 1); | |
160 rt.Query(test_rect, &results); | |
161 EXPECT_EQ(results.size(), 1U); | |
162 EXPECT_TRUE(*results.begin() == static_cast<intptr_t>(1)); | |
piman
2014/04/29 22:43:06
nit: or EXPECT_EQ(results.count(1), 1U);
luken
2014/04/29 23:22:47
Done.
| |
163 } | |
164 | |
165 // Call Remove() once on something that's been inserted repeatedly. | |
166 TEST_F(RTreeTest, DuplicateInsertRemove) { | |
167 RTree rt(3, 9); | |
168 AddStackedSquares(&rt, 25); | |
169 for (int i = 1; i <= 100; ++i) { | |
170 rt.Insert(Rect(0, 0, i, i), static_cast<intptr_t>(26)); | |
171 ValidateRTree(&rt); | |
172 } | |
173 rt.Remove(static_cast<intptr_t>(26)); | |
174 base::hash_set<intptr_t> results; | |
175 Rect test_rect(1, 1); | |
176 rt.Query(test_rect, &results); | |
177 EXPECT_EQ(results.size(), 25U); | |
178 VerifyAllKeys(results); | |
179 } | |
180 | |
181 // Call Remove() repeatedly on something that's been inserted once. | |
182 TEST_F(RTreeTest, InsertDuplicateRemove) { | |
183 RTree rt(7, 15); | |
184 AddStackedSquares(&rt, 101); | |
185 for (int i = 0; i < 100; ++i) { | |
186 rt.Remove(static_cast<intptr_t>(101)); | |
187 ValidateRTree(&rt); | |
188 } | |
189 base::hash_set<intptr_t> results; | |
190 Rect test_rect(1, 1); | |
191 rt.Query(test_rect, &results); | |
192 EXPECT_EQ(results.size(), 100U); | |
193 VerifyAllKeys(results); | |
194 } | |
195 | |
196 // Stacked rects should meet all matching queries regardless of nesting. | |
197 TEST_F(RTreeTest, QueryStackedSquaresNestedHit) { | |
198 RTree rt(2, 5); | |
199 AddStackedSquares(&rt, 100); | |
200 base::hash_set<intptr_t> results; | |
201 Rect test_rect(1, 1); | |
202 rt.Query(test_rect, &results); | |
203 EXPECT_EQ(results.size(), 100U); | |
204 VerifyAllKeys(results); | |
205 } | |
206 | |
207 // Stacked rects should meet all matching queries when contained completely by | |
208 // the query rectangle. | |
209 TEST_F(RTreeTest, QueryStackedSquaresContainedHit) { | |
210 RTree rt(2, 10); | |
211 AddStackedSquares(&rt, 100); | |
212 base::hash_set<intptr_t> results; | |
213 Rect test_rect(0, 0, 100, 100); | |
214 rt.Query(test_rect, &results); | |
215 EXPECT_EQ(results.size(), 100U); | |
216 VerifyAllKeys(results); | |
217 } | |
218 | |
219 // Stacked rects should miss a missing query when the query has no intersection | |
220 // with the rects. | |
221 TEST_F(RTreeTest, QueryStackedSquaresCompleteMiss) { | |
222 RTree rt(2, 7); | |
223 AddStackedSquares(&rt, 100); | |
224 base::hash_set<intptr_t> results; | |
225 Rect test_rect(150, 150, 100, 100); | |
226 rt.Query(test_rect, &results); | |
227 EXPECT_EQ(results.size(), 0U); | |
228 } | |
229 | |
230 // Removing half the nodes after insertion should still result in a valid tree. | |
231 TEST_F(RTreeTest, RemoveHalfStackedRects) { | |
232 RTree rt(2, 11); | |
233 AddStackedSquares(&rt, 200); | |
234 for (int i = 101; i <= 200; ++i) { | |
235 rt.Remove(static_cast<intptr_t>(i)); | |
236 ValidateRTree(&rt); | |
237 } | |
238 base::hash_set<intptr_t> results; | |
239 Rect test_rect(1, 1); | |
240 rt.Query(test_rect, &results); | |
241 EXPECT_EQ(results.size(), 100U); | |
242 VerifyAllKeys(results); | |
243 // Add the nodes back in. | |
244 for (int i = 101; i <= 200; ++i) { | |
245 rt.Insert(Rect(0, 0, i, i), static_cast<intptr_t>(i)); | |
246 ValidateRTree(&rt); | |
247 } | |
248 results.clear(); | |
249 rt.Query(test_rect, &results); | |
250 EXPECT_EQ(results.size(), 200U); | |
251 VerifyAllKeys(results); | |
252 } | |
253 | |
254 TEST_F(RTreeTest, InsertNegativeCoordsRect) { | |
255 RTree rt(5, 11); | |
256 for (int i = 1; i <= 100; ++i) { | |
257 rt.Insert(Rect(-i, -i, i, i), static_cast<intptr_t>((i * 2) - 1)); | |
258 ValidateRTree(&rt); | |
259 rt.Insert(Rect(0, 0, i, i), static_cast<intptr_t>(i * 2)); | |
260 ValidateRTree(&rt); | |
261 } | |
262 base::hash_set<intptr_t> results; | |
263 Rect test_rect(-1, -1, 2, 2); | |
264 rt.Query(test_rect, &results); | |
265 EXPECT_EQ(results.size(), 200U); | |
266 VerifyAllKeys(results); | |
267 } | |
268 | |
269 TEST_F(RTreeTest, RemoveNegativeCoordsRect) { | |
270 RTree rt(7, 21); | |
271 // Add 100 positive stacked squares. | |
272 AddStackedSquares(&rt, 100); | |
273 // Now add 100 negative stacked squares. | |
274 for (int i = 101; i <= 200; ++i) { | |
275 rt.Insert(Rect(100 - i, 100 - i, i - 100, i - 100), | |
276 static_cast<intptr_t>(301 - i)); | |
277 ValidateRTree(&rt); | |
278 } | |
279 // Now remove half of the negative squares. | |
280 for (int i = 101; i <= 150; ++i) { | |
281 rt.Remove(static_cast<intptr_t>(301 - i)); | |
282 ValidateRTree(&rt); | |
283 } | |
284 // Queries should return 100 positive and 50 negative stacked squares. | |
285 base::hash_set<intptr_t> results; | |
286 Rect test_rect(-1, -1, 2, 2); | |
287 rt.Query(test_rect, &results); | |
288 EXPECT_EQ(results.size(), 150U); | |
289 VerifyAllKeys(results); | |
290 } | |
291 | |
292 TEST_F(RTreeTest, InsertEmptyRectReplacementRemovesKey) { | |
293 RTree rt(10, 31); | |
294 AddStackedSquares(&rt, 50); | |
295 ValidateRTree(&rt); | |
296 | |
297 // Replace last square with empty rect. | |
298 rt.Insert(Rect(0, 0, 0, 0), static_cast<intptr_t>(50)); | |
piman
2014/04/29 22:43:06
nit: Rect()
luken
2014/04/29 23:22:47
Done.
| |
299 ValidateRTree(&rt); | |
300 | |
301 // Now query large area to get all rects in tree. | |
302 base::hash_set<intptr_t> results; | |
303 Rect test_rect(0, 0, 100, 100); | |
304 rt.Query(test_rect, &results); | |
305 | |
306 // Should only be 49 rects in tree. | |
307 EXPECT_EQ(results.size(), 49U); | |
308 VerifyAllKeys(results); | |
309 } | |
310 | |
311 TEST_F(RTreeTest, NodeRemoveNodesForReinsert) { | |
312 // Make a leaf node for testing removal from. | |
313 scoped_ptr<RTree::Node> test_node(new RTree::Node(0)); | |
314 // Build 20 record nodes with rectangle centers going from (1,1) to (20,20) | |
315 for (int i = 1; i <= 20; ++i) { | |
316 test_node->AddChild( | |
317 new RTree::Node(Rect(i - 1, i - 1, 2, 2), static_cast<intptr_t>(i))); | |
318 } | |
319 // Quick verification of the node before removing children. | |
320 ValidateNode(test_node.get(), 1U, 20U); | |
321 // Use a scoped vector to delete all children that get removed from the Node. | |
322 ScopedVector<RTree::Node> removals; | |
323 test_node->RemoveNodesForReinsert(1, &removals); | |
324 // Should have gotten back 1 node pointers. | |
325 EXPECT_EQ(removals.size(), 1U); | |
326 // There should be 19 left in the test_node. | |
327 EXPECT_EQ(test_node->count(), 19U); | |
328 // If we fix up the bounds on the test_node, it should verify. | |
329 test_node->RecomputeBoundsNoParents(); | |
330 ValidateNode(test_node.get(), 2U, 20U); | |
331 // The node we removed should be node 10, as it was exactly in the center. | |
332 EXPECT_TRUE(removals[0]->key() == static_cast<intptr_t>(10)); | |
piman
2014/04/29 22:43:06
nit: EXPECT_EQ, and you can use 10U instead of the
luken
2014/04/29 23:22:47
Done.
| |
333 | |
334 // Now remove the next 2. | |
335 removals.clear(); | |
336 test_node->RemoveNodesForReinsert(2, &removals); | |
337 EXPECT_EQ(removals.size(), 2U); | |
338 EXPECT_EQ(test_node->count(), 17U); | |
339 test_node->RecomputeBoundsNoParents(); | |
340 ValidateNode(test_node.get(), 2U, 20U); | |
341 // Lastly the 2 nodes we should have gotten back are keys 9 and 11, as their | |
342 // centers were the closest to the center of the node bounding box. | |
343 EXPECT_TRUE(removals[0]->key() == static_cast<intptr_t>(9) || | |
344 removals[0]->key() == static_cast<intptr_t>(11)); | |
345 EXPECT_TRUE(removals[1]->key() == static_cast<intptr_t>(9) || | |
346 removals[1]->key() == static_cast<intptr_t>(11)); | |
347 EXPECT_TRUE(removals[0]->key() != removals[1]->key()); | |
piman
2014/04/29 22:43:06
nit: you could sort, and EXPECT_EQ that first one
luken
2014/04/29 23:22:47
I made a base::hash_set of the result keys, and EX
piman
2014/04/29 23:38:15
Sure, that's fine too.
| |
348 } | |
349 | |
350 TEST_F(RTreeTest, NodeCompareVertical) { | |
351 // One rect with lower y than another should always sort lower than higher y. | |
352 RTree::Node low(Rect(0, 1, 10, 10), static_cast<intptr_t>(1)); | |
353 RTree::Node middle(Rect(0, 5, 10, 10), static_cast<intptr_t>(5)); | |
354 EXPECT_TRUE(RTree::Node::CompareVertical(&low, &middle)); | |
355 EXPECT_FALSE(RTree::Node::CompareVertical(&middle, &low)); | |
356 | |
357 // Try a non-overlapping higher-y rectangle. | |
358 RTree::Node high(Rect(-10, 20, 10, 1), static_cast<intptr_t>(10)); | |
359 EXPECT_TRUE(RTree::Node::CompareVertical(&low, &high)); | |
360 EXPECT_FALSE(RTree::Node::CompareVertical(&high, &low)); | |
361 | |
362 // Ties are broken by lowest bottom y value. | |
363 RTree::Node shorter_tie(Rect(10, 1, 100, 2), static_cast<intptr_t>(2)); | |
364 EXPECT_TRUE(RTree::Node::CompareVertical(&shorter_tie, &low)); | |
365 EXPECT_FALSE(RTree::Node::CompareVertical(&low, &shorter_tie)); | |
366 } | |
367 | |
368 TEST_F(RTreeTest, NodeCompareHorizontal) { | |
369 // One rect with lower x than another should always sort lower than higher x. | |
370 RTree::Node low(Rect(1, 0, 10, 10), static_cast<intptr_t>(1)); | |
371 RTree::Node middle(Rect(5, 0, 10, 10), static_cast<intptr_t>(5)); | |
372 EXPECT_TRUE(RTree::Node::CompareHorizontal(&low, &middle)); | |
373 EXPECT_FALSE(RTree::Node::CompareHorizontal(&middle, &low)); | |
374 | |
375 // Try a non-overlapping higher-x rectangle. | |
376 RTree::Node high(Rect(20, -10, 1, 10), static_cast<intptr_t>(10)); | |
377 EXPECT_TRUE(RTree::Node::CompareHorizontal(&low, &high)); | |
378 EXPECT_FALSE(RTree::Node::CompareHorizontal(&high, &low)); | |
379 | |
380 // Ties are broken by lowest bottom x value. | |
381 RTree::Node shorter_tie(Rect(1, 10, 2, 100), static_cast<intptr_t>(2)); | |
382 EXPECT_TRUE(RTree::Node::CompareHorizontal(&shorter_tie, &low)); | |
383 EXPECT_FALSE(RTree::Node::CompareHorizontal(&low, &shorter_tie)); | |
384 } | |
385 | |
386 TEST_F(RTreeTest, NodeCompareCenterDistanceFromParent) { | |
387 // Create a test node we can add children to, for distance comparisons. | |
388 scoped_ptr<RTree::Node> parent(new RTree::Node(0)); | |
389 | |
390 // Add three children, one each with centers at (0, 0), (10, 10), (-9, -9), | |
391 // around which a bounding box will be centered at (0, 0) | |
392 RTree::Node* center_zero = | |
393 new RTree::Node(Rect(-1, -1, 2, 2), static_cast<intptr_t>(1)); | |
394 parent->AddChild(center_zero); | |
395 | |
396 RTree::Node* center_positive = | |
397 new RTree::Node(Rect(9, 9, 2, 2), static_cast<intptr_t>(2)); | |
398 parent->AddChild(center_positive); | |
399 | |
400 RTree::Node* center_negative = | |
401 new RTree::Node(Rect(-10, -10, 2, 2), static_cast<intptr_t>(3)); | |
402 parent->AddChild(center_negative); | |
403 | |
404 ValidateNode(parent.get(), 1U, 5U); | |
405 EXPECT_TRUE(parent->rect_ == Rect(-10, -10, 21, 21)); | |
406 | |
407 EXPECT_TRUE(RTree::Node::CompareCenterDistanceFromParent(center_zero, | |
408 center_positive)); | |
409 EXPECT_FALSE(RTree::Node::CompareCenterDistanceFromParent(center_positive, | |
410 center_zero)); | |
411 | |
412 EXPECT_TRUE(RTree::Node::CompareCenterDistanceFromParent(center_zero, | |
413 center_negative)); | |
414 EXPECT_FALSE(RTree::Node::CompareCenterDistanceFromParent(center_negative, | |
415 center_zero)); | |
416 | |
417 EXPECT_TRUE(RTree::Node::CompareCenterDistanceFromParent(center_negative, | |
418 center_positive)); | |
419 EXPECT_FALSE(RTree::Node::CompareCenterDistanceFromParent(center_positive, | |
420 center_negative)); | |
421 } | |
422 | |
423 TEST_F(RTreeTest, NodeOverlapIncreaseToAdd) { | |
424 // Create a test node with three children, for overlap comparisons. | |
425 scoped_ptr<RTree::Node> parent(new RTree::Node(0)); | |
426 | |
427 // Add three children, each 4 wide and tall, at (0, 0), (3, 3), (6, 6) with | |
428 // overlapping corners. | |
429 Rect top(0, 0, 4, 4); | |
430 parent->AddChild(new RTree::Node(top, static_cast<intptr_t>(1))); | |
431 Rect middle(3, 3, 4, 4); | |
432 parent->AddChild(new RTree::Node(middle, static_cast<intptr_t>(2))); | |
433 Rect bottom(6, 6, 4, 4); | |
434 parent->AddChild(new RTree::Node(bottom, static_cast<intptr_t>(3))); | |
435 ValidateNode(parent.get(), 1U, 5U); | |
436 | |
437 // Test a rect in corner. | |
438 Rect corner(0, 0, 1, 1); | |
439 Rect expanded = top; | |
440 expanded.Union(corner); | |
441 // It should not add any overlap to add this to the first child at (0, 0); | |
442 EXPECT_EQ(parent->OverlapIncreaseToAdd(corner, 0, expanded), 0); | |
443 | |
444 expanded = middle; | |
445 expanded.Union(corner); | |
446 // Overlap for middle rectangle should increase from 2 pixels at (3, 3) and | |
447 // (6, 6) to 17 pixels, as it will now cover 4x4 rectangle top, | |
448 // so a change of +15 | |
449 EXPECT_EQ(parent->OverlapIncreaseToAdd(corner, 1, expanded), 15); | |
450 | |
451 expanded = bottom; | |
452 expanded.Union(corner); | |
453 // Overlap for bottom rectangle should increase from 1 pixel at (6, 6) to | |
454 // 32 pixels, as it will now cover both 4x4 rectangles top and middle, | |
455 // so a change of 31 | |
456 EXPECT_EQ(parent->OverlapIncreaseToAdd(corner, 2, expanded), 31); | |
457 | |
458 // Test a rect that doesn't overlap with anything, in the far right corner. | |
459 Rect far_corner(9, 0, 1, 1); | |
460 expanded = top; | |
461 expanded.Union(far_corner); | |
462 // Overlap of top should go from 1 to 4, as it will now cover the entire first | |
463 // row of pixels in middle. | |
464 EXPECT_EQ(parent->OverlapIncreaseToAdd(far_corner, 0, expanded), 3); | |
465 | |
466 expanded = middle; | |
467 expanded.Union(far_corner); | |
468 // Overlap of middle should go from 2 to 8, as it will cover the rightmost 4 | |
469 // pixels of top and the top 4 pixles of bottom as it expands. | |
470 EXPECT_EQ(parent->OverlapIncreaseToAdd(far_corner, 1, expanded), 6); | |
471 | |
472 expanded = bottom; | |
473 expanded.Union(far_corner); | |
474 // Overlap of bottom should go from 1 to 4, as it will now cover the rightmost | |
475 // 4 pixels of middle. | |
476 EXPECT_EQ(parent->OverlapIncreaseToAdd(far_corner, 2, expanded), 3); | |
477 } | |
478 | |
479 TEST_F(RTreeTest, NodeBuildLowBounds) { | |
480 ScopedVector<RTree::Node> nodes; | |
481 nodes.reserve(10); | |
482 for (int i = 1; i <= 10; ++i) { | |
483 nodes.push_back( | |
484 new RTree::Node(Rect(0, 0, i, i), static_cast<intptr_t>(i))); | |
485 } | |
486 std::vector<Rect> vertical_bounds; | |
487 std::vector<Rect> horizontal_bounds; | |
488 RTree::Node::BuildLowBounds( | |
489 nodes.get(), nodes.get(), &vertical_bounds, &horizontal_bounds); | |
490 for (int i = 0; i < 10; ++i) { | |
491 EXPECT_EQ(vertical_bounds[i], Rect(0, 0, i + 1, i + 1)); | |
492 EXPECT_EQ(horizontal_bounds[i], Rect(0, 0, i + 1, i + 1)); | |
493 } | |
494 } | |
495 | |
496 TEST_F(RTreeTest, NodeBuildHighBounds) { | |
497 ScopedVector<RTree::Node> nodes; | |
498 nodes.reserve(25); | |
499 for (int i = 0; i < 25; ++i) { | |
500 nodes.push_back( | |
501 new RTree::Node(Rect(i, i, 25 - i, 25 - i), static_cast<intptr_t>(i))); | |
502 } | |
503 std::vector<Rect> vertical_bounds; | |
504 std::vector<Rect> horizontal_bounds; | |
505 RTree::Node::BuildHighBounds( | |
506 nodes.get(), nodes.get(), &vertical_bounds, &horizontal_bounds); | |
507 for (int i = 0; i < 25; ++i) { | |
508 EXPECT_EQ(vertical_bounds[i], Rect(i, i, 25 - i, 25 - i)); | |
509 EXPECT_EQ(horizontal_bounds[i], Rect(i, i, 25 - i, 25 - i)); | |
510 } | |
511 } | |
512 | |
513 TEST_F(RTreeTest, NodeChooseSplitAxisAndIndex) { | |
514 std::vector<Rect> low_vertical_bounds; | |
515 std::vector<Rect> high_vertical_bounds; | |
516 std::vector<Rect> low_horizontal_bounds; | |
517 std::vector<Rect> high_horizontal_bounds; | |
518 // In this test scenario we describe a mirrored, stacked configuration of | |
519 // horizontal, 1 pixel high rectangles labeled a-f like this: | |
520 // | |
521 // shape: | v sort: | h sort: | | |
522 // -------+---------+---------+ | |
523 // aaaaa | 0 | 0 | | |
524 // bbb | 1 | 2 | | |
525 // c | 2 | 4 | | |
526 // d | 3 | 5 | | |
527 // eee | 4 | 3 | | |
528 // fffff | 5 | 1 | | |
529 // | |
530 // These are already sorted vertically from top to bottom. Bounding rectangles | |
531 // of these vertically sorted will be 5 wide, i tall bounding boxes. | |
532 for (int i = 0; i < 6; ++i) { | |
533 low_vertical_bounds.push_back(Rect(0, 0, 5, i + 1)); | |
534 high_vertical_bounds.push_back(Rect(0, i, 5, 6 - i)); | |
535 } | |
536 | |
537 // Low bounds of horizontal sort start with bounds of box a and then jump to | |
538 // cover everything, as box f is second in horizontal sort. | |
539 low_horizontal_bounds.push_back(Rect(0, 0, 5, 1)); | |
540 for (int i = 0; i < 5; ++i) { | |
541 low_horizontal_bounds.push_back(Rect(0, 0, 5, 6)); | |
542 } | |
543 | |
544 // High horizontal bounds are hand-calculated. | |
545 high_horizontal_bounds.push_back(Rect(0, 0, 5, 6)); | |
546 high_horizontal_bounds.push_back(Rect(0, 1, 5, 5)); | |
547 high_horizontal_bounds.push_back(Rect(1, 1, 3, 4)); | |
548 high_horizontal_bounds.push_back(Rect(1, 2, 3, 3)); | |
549 high_horizontal_bounds.push_back(Rect(2, 2, 1, 2)); | |
550 high_horizontal_bounds.push_back(Rect(2, 3, 1, 1)); | |
551 | |
552 // This should split vertically, right down the middle. | |
553 EXPECT_TRUE(RTree::Node::ChooseSplitAxis(low_vertical_bounds, | |
554 high_vertical_bounds, | |
555 low_horizontal_bounds, | |
556 high_horizontal_bounds, | |
557 2, | |
558 5)); | |
559 EXPECT_EQ(3U, | |
560 RTree::Node::ChooseSplitIndex( | |
561 2, 5, low_vertical_bounds, high_vertical_bounds)); | |
562 | |
563 // We rotate the shape to test horizontal split axis detection: | |
564 // | |
565 // +--------+ | |
566 // | a f | | |
567 // | ab ef | | |
568 // sort: | abcdef | | |
569 // | ab ef | | |
570 // | a f | | |
571 // |--------+ | |
572 // v sort: | 024531 | | |
573 // h sort: | 012345 | | |
574 // +--------+ | |
575 // | |
576 // Clear out old bounds first. | |
577 low_vertical_bounds.clear(); | |
578 high_vertical_bounds.clear(); | |
579 low_horizontal_bounds.clear(); | |
580 high_horizontal_bounds.clear(); | |
581 | |
582 // Low bounds of vertical sort start with bounds of box a and then jump to | |
583 // cover everything, as box f is second in vertical sort. | |
584 low_vertical_bounds.push_back(Rect(0, 0, 1, 5)); | |
585 for (int i = 0; i < 5; ++i) { | |
586 low_vertical_bounds.push_back(Rect(0, 0, 6, 5)); | |
587 } | |
588 | |
589 // High vertical bounds are hand-calculated. | |
590 high_vertical_bounds.push_back(Rect(0, 0, 6, 5)); | |
591 high_vertical_bounds.push_back(Rect(1, 0, 5, 5)); | |
592 high_vertical_bounds.push_back(Rect(1, 1, 4, 3)); | |
593 high_vertical_bounds.push_back(Rect(2, 1, 3, 3)); | |
594 high_vertical_bounds.push_back(Rect(2, 2, 2, 1)); | |
595 high_vertical_bounds.push_back(Rect(3, 2, 1, 1)); | |
596 | |
597 // These are already sorted horizontally from left to right. Bounding | |
598 // rectangles of these horizontally sorted will be i wide, 5 tall bounding | |
599 // boxes. | |
600 for (int i = 0; i < 6; ++i) { | |
601 low_horizontal_bounds.push_back(Rect(0, 0, i + 1, 5)); | |
602 high_horizontal_bounds.push_back(Rect(i, 0, 6 - i, 5)); | |
603 } | |
604 | |
605 // This should split horizontally, right down the middle. | |
606 EXPECT_FALSE(RTree::Node::ChooseSplitAxis(low_vertical_bounds, | |
607 high_vertical_bounds, | |
608 low_horizontal_bounds, | |
609 high_horizontal_bounds, | |
610 2, | |
611 5)); | |
612 EXPECT_EQ(3U, | |
613 RTree::Node::ChooseSplitIndex( | |
614 2, 5, low_horizontal_bounds, high_horizontal_bounds)); | |
615 } | |
616 | |
617 TEST_F(RTreeTest, NodeDivideChildren) { | |
618 // Create a test node to split. | |
619 scoped_ptr<RTree::Node> test_node(new RTree::Node(0)); | |
620 std::vector<RTree::Node*> sorted_children; | |
621 std::vector<Rect> low_bounds; | |
622 std::vector<Rect> high_bounds; | |
623 // Insert 10 record nodes, also inserting them into our children array. | |
624 for (int i = 1; i <= 10; ++i) { | |
625 RTree::Node* node = | |
626 new RTree::Node(Rect(0, 0, i, i), static_cast<intptr_t>(i)); | |
627 test_node->AddChild(node); | |
628 sorted_children.push_back(node); | |
629 low_bounds.push_back(Rect(0, 0, i, i)); | |
630 high_bounds.push_back(Rect(0, 0, 10, 10)); | |
631 } | |
632 // Split the children in half. | |
633 scoped_ptr<RTree::Node> split_node( | |
634 test_node->DivideChildren(low_bounds, high_bounds, sorted_children, 5)); | |
635 // Both nodes should be valid. | |
636 ValidateNode(test_node.get(), 1U, 10U); | |
637 ValidateNode(split_node.get(), 1U, 10U); | |
638 // Both nodes should have five children. | |
639 EXPECT_EQ(test_node->children_.size(), 5U); | |
640 EXPECT_EQ(split_node->children_.size(), 5U); | |
641 // Test node should have children 1-5, split node should have children 6-10. | |
642 for (int i = 0; i < 5; ++i) { | |
643 EXPECT_EQ(test_node->children_[i]->key(), static_cast<intptr_t>(i + 1)); | |
644 EXPECT_EQ(split_node->children_[i]->key(), static_cast<intptr_t>(i + 6)); | |
645 } | |
646 } | |
647 | |
648 TEST_F(RTreeTest, NodeRemoveChildNoOrphans) { | |
649 scoped_ptr<RTree::Node> test_parent(new RTree::Node(0)); | |
650 scoped_ptr<RTree::Node> child_one( | |
651 new RTree::Node(Rect(0, 0, 1, 1), static_cast<intptr_t>(1))); | |
652 scoped_ptr<RTree::Node> child_two( | |
653 new RTree::Node(Rect(0, 0, 2, 2), static_cast<intptr_t>(2))); | |
654 scoped_ptr<RTree::Node> child_three( | |
655 new RTree::Node(Rect(0, 0, 3, 3), static_cast<intptr_t>(3))); | |
656 test_parent->AddChild(child_one.get()); | |
657 test_parent->AddChild(child_two.get()); | |
658 test_parent->AddChild(child_three.get()); | |
659 ValidateNode(test_parent.get(), 1U, 5U); | |
660 // Remove the middle node. | |
661 ScopedVector<RTree::Node> orphans; | |
662 EXPECT_EQ(test_parent->RemoveChild(child_two.get(), &orphans), 2U); | |
663 EXPECT_EQ(orphans.size(), 0U); | |
664 EXPECT_EQ(test_parent->count(), 2U); | |
665 test_parent->RecomputeBoundsNoParents(); | |
666 ValidateNode(test_parent.get(), 1U, 5U); | |
667 // Remove the end node. | |
668 EXPECT_EQ(test_parent->RemoveChild(child_three.get(), &orphans), 1U); | |
669 EXPECT_EQ(orphans.size(), 0U); | |
670 EXPECT_EQ(test_parent->count(), 1U); | |
671 test_parent->RecomputeBoundsNoParents(); | |
672 ValidateNode(test_parent.get(), 1U, 5U); | |
673 // Remove the first node. | |
674 EXPECT_EQ(test_parent->RemoveChild(child_one.get(), &orphans), 0U); | |
675 EXPECT_EQ(orphans.size(), 0U); | |
676 EXPECT_EQ(test_parent->count(), 0U); | |
677 } | |
678 | |
679 TEST_F(RTreeTest, NodeRemoveChildOrphans) { | |
680 // Build flattened binary tree of Nodes 4 deep, from the record nodes up. | |
681 ScopedVector<RTree::Node> nodes; | |
682 nodes.resize(15); | |
683 // Indicies 7 through 15 are record nodes. | |
684 for (int i = 7; i < 15; ++i) { | |
685 nodes[i] = new RTree::Node(Rect(0, 0, i, i), static_cast<intptr_t>(i)); | |
686 } | |
687 // Nodes 3 through 6 are level 0 (leaves) and get 2 record nodes each. | |
688 for (int i = 3; i < 7; ++i) { | |
689 nodes[i] = new RTree::Node(0); | |
690 nodes[i]->AddChild(nodes[(i * 2) + 1]); | |
691 nodes[i]->AddChild(nodes[(i * 2) + 2]); | |
692 } | |
693 // Nodes 1 and 2 are level 1 and get 2 leaves each. | |
694 for (int i = 1; i < 3; ++i) { | |
695 nodes[i] = new RTree::Node(1); | |
696 nodes[i]->AddChild(nodes[(i * 2) + 1]); | |
697 nodes[i]->AddChild(nodes[(i * 2) + 2]); | |
698 } | |
699 // Node 0 is level 2 and gets 2 childen. | |
700 nodes[0] = new RTree::Node(2); | |
701 nodes[0]->AddChild(nodes[1]); | |
702 nodes[0]->AddChild(nodes[2]); | |
703 // This should now be a valid node structure. | |
704 ValidateNode(nodes[0], 2U, 2U); | |
705 | |
706 // Now remove the level 0 nodes, so we get the record nodes as orphans. | |
707 ScopedVector<RTree::Node> orphans; | |
708 EXPECT_EQ(nodes[1]->RemoveChild(nodes[3], &orphans), 1U); | |
709 EXPECT_EQ(nodes[1]->RemoveChild(nodes[4], &orphans), 0U); | |
710 EXPECT_EQ(nodes[2]->RemoveChild(nodes[5], &orphans), 1U); | |
711 EXPECT_EQ(nodes[2]->RemoveChild(nodes[6], &orphans), 0U); | |
712 | |
713 // Orphans should be nodes 7 through 15 in order. | |
714 EXPECT_EQ(orphans.size(), 8U); | |
715 for (int i = 0; i < 8; ++i) { | |
716 EXPECT_EQ(orphans[i], nodes[i + 7]); | |
717 } | |
718 | |
719 // Now we remove nodes 1 and 2 from the root, expecting no further orphans. | |
720 // This prevents a crash due to double-delete on test exit, as no node should | |
721 // own any other node right now. | |
722 EXPECT_EQ(nodes[0]->RemoveChild(nodes[1], &orphans), 1U); | |
723 EXPECT_EQ(orphans.size(), 8U); | |
724 EXPECT_EQ(nodes[0]->RemoveChild(nodes[2], &orphans), 0U); | |
725 EXPECT_EQ(orphans.size(), 8U); | |
726 | |
727 // Prevent double-delete of nodes by both nodes and orphans. | |
728 orphans.weak_clear(); | |
729 } | |
730 | |
731 TEST_F(RTreeTest, NodeRemoveAndReturnLastChild) { | |
732 scoped_ptr<RTree::Node> test_parent(new RTree::Node(0)); | |
733 scoped_ptr<RTree::Node> child_one( | |
734 new RTree::Node(Rect(0, 0, 1, 1), static_cast<intptr_t>(1))); | |
735 scoped_ptr<RTree::Node> child_two( | |
736 new RTree::Node(Rect(0, 0, 2, 2), static_cast<intptr_t>(2))); | |
737 scoped_ptr<RTree::Node> child_three( | |
738 new RTree::Node(Rect(0, 0, 3, 3), static_cast<intptr_t>(3))); | |
739 test_parent->AddChild(child_one.get()); | |
740 test_parent->AddChild(child_two.get()); | |
741 test_parent->AddChild(child_three.get()); | |
742 ValidateNode(test_parent.get(), 1U, 5U); | |
743 | |
744 EXPECT_EQ(test_parent->RemoveAndReturnLastChild().release(), | |
745 child_three.get()); | |
746 EXPECT_EQ(test_parent->count(), 2U); | |
747 test_parent->RecomputeBoundsNoParents(); | |
748 ValidateNode(test_parent.get(), 1U, 5U); | |
749 | |
750 EXPECT_EQ(test_parent->RemoveAndReturnLastChild().release(), child_two.get()); | |
751 EXPECT_EQ(test_parent->count(), 1U); | |
752 test_parent->RecomputeBoundsNoParents(); | |
753 ValidateNode(test_parent.get(), 1U, 5U); | |
754 | |
755 EXPECT_EQ(test_parent->RemoveAndReturnLastChild().release(), child_one.get()); | |
756 EXPECT_EQ(test_parent->count(), 0U); | |
757 } | |
758 | |
759 TEST_F(RTreeTest, NodeLeastOverlapIncrease) { | |
760 scoped_ptr<RTree::Node> test_parent(new RTree::Node(0)); | |
761 // Construct 4 nodes with 1x2 retangles spaced horizontally 1 pixel apart, or: | |
762 // | |
763 // a b c d | |
764 // a b c d | |
765 // | |
766 for (int i = 0; i < 4; ++i) { | |
767 test_parent->AddChild( | |
768 new RTree::Node(Rect(i * 2, 0, 1, 2), static_cast<intptr_t>(i + 1))); | |
769 } | |
770 | |
771 ValidateNode(test_parent.get(), 1U, 5U); | |
772 | |
773 // Test rect at (7, 0) should require minimum overlap on the part of the | |
774 // fourth rectangle to add: | |
775 // | |
776 // a b c dT | |
777 // a b c d | |
778 // | |
779 Rect test_rect_far(7, 0, 1, 1); | |
780 std::vector<Rect> expanded_rects; | |
781 BuildExpandedRects(test_parent.get(), test_rect_far, &expanded_rects); | |
782 RTree::Node* result = | |
783 test_parent->LeastOverlapIncrease(test_rect_far, expanded_rects); | |
784 EXPECT_EQ(result->key(), static_cast<intptr_t>(4)); | |
785 | |
786 // Test rect covering the bottom half of all children should be a 4-way tie, | |
787 // so LeastOverlapIncrease should return NULL: | |
788 // | |
789 // a b c d | |
790 // TTTTTTT | |
791 // | |
792 Rect test_rect_tie(0, 1, 7, 1); | |
793 BuildExpandedRects(test_parent.get(), test_rect_tie, &expanded_rects); | |
794 result = test_parent->LeastOverlapIncrease(test_rect_tie, expanded_rects); | |
795 EXPECT_TRUE(result == NULL); | |
796 | |
797 // Test rect completely inside c should return the third rectangle: | |
798 // | |
799 // a b T d | |
800 // a b c d | |
801 // | |
802 Rect test_rect_inside(4, 0, 1, 1); | |
803 BuildExpandedRects(test_parent.get(), test_rect_inside, &expanded_rects); | |
804 result = test_parent->LeastOverlapIncrease(test_rect_inside, expanded_rects); | |
805 EXPECT_EQ(result->key(), static_cast<intptr_t>(3)); | |
806 | |
807 // Add a rectangle that overlaps completely with rectangle c, to test | |
808 // when there is a tie between two completely contained rectangles: | |
809 // | |
810 // a b Ted | |
811 // a b eed | |
812 // | |
813 test_parent->AddChild( | |
814 new RTree::Node(Rect(4, 0, 2, 2), static_cast<intptr_t>(9))); | |
815 BuildExpandedRects(test_parent.get(), test_rect_inside, &expanded_rects); | |
816 result = test_parent->LeastOverlapIncrease(test_rect_inside, expanded_rects); | |
817 EXPECT_TRUE(result == NULL); | |
818 } | |
819 | |
820 TEST_F(RTreeTest, NodeLeastAreaEnlargement) { | |
821 scoped_ptr<RTree::Node> test_parent(new RTree::Node(0)); | |
822 // Construct 4 nodes in a cross-hairs style configuration: | |
823 // | |
824 // a | |
825 // b c | |
826 // d | |
827 // | |
828 test_parent->AddChild( | |
829 new RTree::Node(Rect(1, 0, 1, 1), static_cast<intptr_t>(1))); | |
830 test_parent->AddChild( | |
831 new RTree::Node(Rect(0, 1, 1, 1), static_cast<intptr_t>(2))); | |
832 test_parent->AddChild( | |
833 new RTree::Node(Rect(2, 1, 1, 1), static_cast<intptr_t>(3))); | |
834 test_parent->AddChild( | |
835 new RTree::Node(Rect(1, 2, 1, 1), static_cast<intptr_t>(4))); | |
836 | |
837 ValidateNode(test_parent.get(), 1U, 5U); | |
838 | |
839 // Test rect at (1, 3) should require minimum area to add to Node d: | |
840 // | |
841 // a | |
842 // b c | |
843 // d | |
844 // T | |
845 // | |
846 Rect test_rect_below(1, 3, 1, 1); | |
847 std::vector<Rect> expanded_rects; | |
848 BuildExpandedRects(test_parent.get(), test_rect_below, &expanded_rects); | |
849 RTree::Node* result = | |
850 test_parent->LeastAreaEnlargement(test_rect_below, expanded_rects); | |
851 EXPECT_EQ(result->key(), static_cast<intptr_t>(4)); | |
852 | |
853 // Test rect completely inside b should require minimum area to add to Node b: | |
854 // | |
855 // a | |
856 // T c | |
857 // d | |
858 // | |
859 Rect test_rect_inside(0, 1, 1, 1); | |
860 BuildExpandedRects(test_parent.get(), test_rect_inside, &expanded_rects); | |
861 result = test_parent->LeastAreaEnlargement(test_rect_inside, expanded_rects); | |
862 EXPECT_EQ(result->key(), static_cast<intptr_t>(2)); | |
863 | |
864 // Add e at (0, 1) to overlap b and c, to test tie-breaking: | |
865 // | |
866 // a | |
867 // eee | |
868 // d | |
869 // | |
870 test_parent->AddChild( | |
871 new RTree::Node(Rect(0, 1, 3, 1), static_cast<intptr_t>(7))); | |
872 | |
873 ValidateNode(test_parent.get(), 1U, 5U); | |
874 | |
875 // Test rect at (3, 1) should tie between c and e, but c has smaller area so | |
876 // the algorithm should select c: | |
877 // | |
878 // | |
879 // a | |
880 // eeeT | |
881 // d | |
882 // | |
883 Rect test_rect_tie_breaker(3, 1, 1, 1); | |
884 BuildExpandedRects(test_parent.get(), test_rect_tie_breaker, &expanded_rects); | |
885 result = | |
886 test_parent->LeastAreaEnlargement(test_rect_tie_breaker, expanded_rects); | |
887 EXPECT_EQ(result->key(), static_cast<intptr_t>(3)); | |
888 } | |
889 | |
890 } // namespace gfx | |
OLD | NEW |