Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(259)

Side by Side Diff: ui/gfx/geometry/r_tree_unittest.cc

Issue 245763002: Adds an RTree data structure to gfx/geometry (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: responding to piman early feedback, thanks Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « ui/gfx/geometry/r_tree.cc ('k') | ui/gfx/gfx.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(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
OLDNEW
« no previous file with comments | « ui/gfx/geometry/r_tree.cc ('k') | ui/gfx/gfx.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698