KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_dynamic_rtree.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 3
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
21
24
25#include <algorithm>
26#include <random>
27#include <set>
28#include <vector>
29
30using namespace KIRTREE;
31
32BOOST_AUTO_TEST_SUITE( DynamicRTree )
33
34
36{
38
39 BOOST_CHECK( tree.empty() );
40 BOOST_CHECK_EQUAL( tree.size(), 0 );
41
42 int searchMin[2] = { 0, 0 };
43 int searchMax[2] = { 100, 100 };
44
45 auto visitor = []( intptr_t ) { return true; };
46
47 BOOST_CHECK_EQUAL( tree.Search( searchMin, searchMax, visitor ), 0 );
48 BOOST_CHECK( tree.begin() == tree.end() );
49}
50
51
52BOOST_AUTO_TEST_CASE( SingleInsert )
53{
55
56 int min[2] = { 10, 20 };
57 int max[2] = { 30, 40 };
58 tree.Insert( min, max, 42 );
59
60 BOOST_CHECK_EQUAL( tree.size(), 1 );
61 BOOST_CHECK( !tree.empty() );
62
63 // Search overlapping
64 int sMin[2] = { 0, 0 };
65 int sMax[2] = { 15, 25 };
66 std::vector<intptr_t> results;
67
68 auto collect = [&results]( intptr_t val )
69 {
70 results.push_back( val );
71 return true;
72 };
73
74 BOOST_CHECK_EQUAL( tree.Search( sMin, sMax, collect ), 1 );
75 BOOST_CHECK_EQUAL( results[0], 42 );
76
77 // Search disjoint
78 results.clear();
79 int sMin2[2] = { 100, 100 };
80 int sMax2[2] = { 200, 200 };
81 BOOST_CHECK_EQUAL( tree.Search( sMin2, sMax2, collect ), 0 );
82}
83
84
85BOOST_AUTO_TEST_CASE( BulkInsert1K )
86{
88
89 for( int i = 0; i < 1000; ++i )
90 {
91 int min[2] = { i * 10, i * 10 };
92 int max[2] = { i * 10 + 9, i * 10 + 9 };
93 tree.Insert( min, max, static_cast<intptr_t>( i ) );
94 }
95
96 BOOST_CHECK_EQUAL( tree.size(), 1000 );
97
98 // Search for specific item
99 int sMin[2] = { 500 * 10, 500 * 10 };
100 int sMax[2] = { 500 * 10 + 9, 500 * 10 + 9 };
101 std::vector<intptr_t> results;
102
103 auto collect = [&results]( intptr_t val )
104 {
105 results.push_back( val );
106 return true;
107 };
108
109 tree.Search( sMin, sMax, collect );
110 BOOST_CHECK( std::find( results.begin(), results.end(), 500 ) != results.end() );
111}
112
113
114BOOST_AUTO_TEST_CASE( RemoveExisting )
115{
117
118 int min1[2] = { 0, 0 };
119 int max1[2] = { 10, 10 };
120 tree.Insert( min1, max1, 1 );
121
122 int min2[2] = { 20, 20 };
123 int max2[2] = { 30, 30 };
124 tree.Insert( min2, max2, 2 );
125
126 BOOST_CHECK_EQUAL( tree.size(), 2 );
127
128 // Remove first item
129 BOOST_CHECK( tree.Remove( min1, max1, 1 ) );
130 BOOST_CHECK_EQUAL( tree.size(), 1 );
131
132 // Verify it's gone
133 std::vector<intptr_t> results;
134
135 auto collect = [&results]( intptr_t val )
136 {
137 results.push_back( val );
138 return true;
139 };
140
141 int fullMin[2] = { -100, -100 };
142 int fullMax[2] = { 100, 100 };
143 tree.Search( fullMin, fullMax, collect );
144
145 BOOST_CHECK_EQUAL( results.size(), 1 );
146 BOOST_CHECK_EQUAL( results[0], 2 );
147}
148
149
150BOOST_AUTO_TEST_CASE( RemoveNonExistent )
151{
153
154 int min[2] = { 0, 0 };
155 int max[2] = { 10, 10 };
156 tree.Insert( min, max, 1 );
157
158 int fakeMin[2] = { 100, 100 };
159 int fakeMax[2] = { 200, 200 };
160 BOOST_CHECK( !tree.Remove( fakeMin, fakeMax, 999 ) );
161 BOOST_CHECK_EQUAL( tree.size(), 1 );
162}
163
164
165BOOST_AUTO_TEST_CASE( SearchOverlapDisjointAll )
166{
168
169 // Insert items in distinct quadrants
170 int min1[2] = { 0, 0 };
171 int max1[2] = { 10, 10 };
172 tree.Insert( min1, max1, 1 );
173
174 int min2[2] = { 100, 0 };
175 int max2[2] = { 110, 10 };
176 tree.Insert( min2, max2, 2 );
177
178 int min3[2] = { 0, 100 };
179 int max3[2] = { 10, 110 };
180 tree.Insert( min3, max3, 3 );
181
182 int min4[2] = { 100, 100 };
183 int max4[2] = { 110, 110 };
184 tree.Insert( min4, max4, 4 );
185
186 // Search left side only
187 std::set<intptr_t> results;
188
189 auto collect = [&results]( intptr_t val )
190 {
191 results.insert( val );
192 return true;
193 };
194
195 int sMin[2] = { -10, -10 };
196 int sMax[2] = { 50, 200 };
197 tree.Search( sMin, sMax, collect );
198 BOOST_CHECK_EQUAL( results.size(), 2 );
199 BOOST_CHECK( results.count( 1 ) );
200 BOOST_CHECK( results.count( 3 ) );
201
202 // Search disjoint area
203 results.clear();
204 int dMin[2] = { 50, 50 };
205 int dMax[2] = { 90, 90 };
206 tree.Search( dMin, dMax, collect );
207 BOOST_CHECK( results.empty() );
208
209 // Search all
210 results.clear();
211 int aMin[2] = { -1000, -1000 };
212 int aMax[2] = { 1000, 1000 };
213 tree.Search( aMin, aMax, collect );
214 BOOST_CHECK_EQUAL( results.size(), 4 );
215}
216
217
218BOOST_AUTO_TEST_CASE( BulkInsertAndBruteForce )
219{
220 const int N = 5000;
221 std::mt19937 rng( 67890 );
222 std::uniform_int_distribution<int> coordDist( -500000, 500000 );
223 std::uniform_int_distribution<int> sizeDist( 1, 5000 );
224
225 struct ITEM
226 {
227 int min[2];
228 int max[2];
229 intptr_t id;
230 };
231
232 std::vector<ITEM> items( N );
234
235 for( int i = 0; i < N; ++i )
236 {
237 int x = coordDist( rng );
238 int y = coordDist( rng );
239 int w = sizeDist( rng );
240 int h = sizeDist( rng );
241
242 items[i].min[0] = x;
243 items[i].min[1] = y;
244 items[i].max[0] = x + w;
245 items[i].max[1] = y + h;
246 items[i].id = static_cast<intptr_t>( i );
247
248 tree.Insert( items[i].min, items[i].max, items[i].id );
249 }
250
251 BOOST_CHECK_EQUAL( tree.size(), N );
252
253 // Verify with random queries
254 for( int q = 0; q < 50; ++q )
255 {
256 int qx = coordDist( rng );
257 int qy = coordDist( rng );
258 int qw = sizeDist( rng ) * 10;
259 int qh = sizeDist( rng ) * 10;
260
261 int qMin[2] = { qx, qy };
262 int qMax[2] = { qx + qw, qy + qh };
263
264 std::set<intptr_t> expected;
265
266 for( const ITEM& item : items )
267 {
268 if( item.min[0] <= qMax[0] && item.max[0] >= qMin[0]
269 && item.min[1] <= qMax[1] && item.max[1] >= qMin[1] )
270 {
271 expected.insert( item.id );
272 }
273 }
274
275 std::set<intptr_t> actual;
276
277 auto collect = [&actual]( intptr_t val )
278 {
279 actual.insert( val );
280 return true;
281 };
282
283 tree.Search( qMin, qMax, collect );
284 BOOST_CHECK_EQUAL( actual.size(), expected.size() );
285 BOOST_CHECK( actual == expected );
286 }
287}
288
289
290BOOST_AUTO_TEST_CASE( RemoveAndVerify )
291{
292 const int N = 200;
294
295 struct ITEM
296 {
297 int min[2];
298 int max[2];
299 intptr_t id;
300 };
301
302 std::vector<ITEM> items( N );
303
304 for( int i = 0; i < N; ++i )
305 {
306 items[i].min[0] = i * 5;
307 items[i].min[1] = i * 5;
308 items[i].max[0] = i * 5 + 4;
309 items[i].max[1] = i * 5 + 4;
310 items[i].id = static_cast<intptr_t>( i );
311
312 tree.Insert( items[i].min, items[i].max, items[i].id );
313 }
314
315 // Remove every other item
316 for( int i = 0; i < N; i += 2 )
317 BOOST_CHECK( tree.Remove( items[i].min, items[i].max, items[i].id ) );
318
319 BOOST_CHECK_EQUAL( tree.size(), N / 2 );
320
321 // Verify remaining items are all odd
322 std::set<intptr_t> remaining;
323
324 auto collect = [&remaining]( intptr_t val )
325 {
326 remaining.insert( val );
327 return true;
328 };
329
330 int fullMin[2] = { -10000, -10000 };
331 int fullMax[2] = { 10000, 10000 };
332 tree.Search( fullMin, fullMax, collect );
333
334 BOOST_CHECK_EQUAL( remaining.size(), N / 2 );
335
336 for( int i = 1; i < N; i += 2 )
337 BOOST_CHECK( remaining.count( i ) );
338}
339
340
341BOOST_AUTO_TEST_CASE( MoveSemantics )
342{
344
345 for( int i = 0; i < 50; ++i )
346 {
347 int min[2] = { i, i };
348 int max[2] = { i + 5, i + 5 };
349 tree.Insert( min, max, static_cast<intptr_t>( i ) );
350 }
351
352 BOOST_CHECK_EQUAL( tree.size(), 50 );
353
354 // Move construct
355 DYNAMIC_RTREE<intptr_t, int, 2> tree2( std::move( tree ) );
356 BOOST_CHECK_EQUAL( tree2.size(), 50 );
357 BOOST_CHECK_EQUAL( tree.size(), 0 );
358
359 // Move assign
361 tree3 = std::move( tree2 );
362 BOOST_CHECK_EQUAL( tree3.size(), 50 );
363 BOOST_CHECK_EQUAL( tree2.size(), 0 );
364
365 // Verify the moved-to tree works
366 int sMin[2] = { 0, 0 };
367 int sMax[2] = { 100, 100 };
368 int count = 0;
369
370 auto counter = [&count]( intptr_t )
371 {
372 count++;
373 return true;
374 };
375
376 tree3.Search( sMin, sMax, counter );
377 BOOST_CHECK_EQUAL( count, 50 );
378}
379
380
382{
384
385 for( int i = 0; i < 100; ++i )
386 {
387 int min[2] = { i, i };
388 int max[2] = { i + 1, i + 1 };
389 tree.Insert( min, max, static_cast<intptr_t>( i ) );
390 }
391
392 BOOST_CHECK_EQUAL( tree.size(), 100 );
393
394 tree.RemoveAll();
395 BOOST_CHECK_EQUAL( tree.size(), 0 );
396 BOOST_CHECK( tree.empty() );
397
398 // Tree should be usable after RemoveAll
399 int min[2] = { 0, 0 };
400 int max[2] = { 10, 10 };
401 tree.Insert( min, max, 999 );
402 BOOST_CHECK_EQUAL( tree.size(), 1 );
403}
404
405
406BOOST_AUTO_TEST_CASE( ThreeDimensional )
407{
409
410 for( int i = 0; i < 100; ++i )
411 {
412 int min[3] = { i, i, i };
413 int max[3] = { i + 5, i + 5, i + 5 };
414 tree.Insert( min, max, static_cast<intptr_t>( i ) );
415 }
416
417 BOOST_CHECK_EQUAL( tree.size(), 100 );
418
419 int sMin[3] = { 10, 10, 10 };
420 int sMax[3] = { 20, 20, 20 };
421 int count = 0;
422
423 auto counter = [&count]( intptr_t )
424 {
425 count++;
426 return true;
427 };
428
429 tree.Search( sMin, sMax, counter );
430 BOOST_CHECK_GT( count, 0 );
431}
432
433
434BOOST_AUTO_TEST_CASE( NearestNeighbors )
435{
437
438 // Insert items at known positions
439 for( int i = 0; i < 20; ++i )
440 {
441 int min[2] = { i * 100, 0 };
442 int max[2] = { i * 100 + 10, 10 };
443 tree.Insert( min, max, static_cast<intptr_t>( i ) );
444 }
445
446 // Find 3 nearest to origin
447 int point[2] = { 0, 0 };
448 std::vector<std::pair<int64_t, intptr_t>> results;
449 tree.NearestNeighbors( point, 3, results );
450
451 BOOST_CHECK_EQUAL( results.size(), 3 );
452
453 // First result should be item 0 (at origin)
454 BOOST_CHECK_EQUAL( results[0].second, 0 );
455
456 // Results should be in ascending distance order
457 for( size_t i = 1; i < results.size(); ++i )
458 BOOST_CHECK_GE( results[i].first, results[i - 1].first );
459}
460
461
462BOOST_AUTO_TEST_CASE( IteratorFullCoverage )
463{
465
466 for( int i = 0; i < 200; ++i )
467 {
468 int min[2] = { i * 3, i * 7 };
469 int max[2] = { i * 3 + 2, i * 7 + 6 };
470 tree.Insert( min, max, static_cast<intptr_t>( i ) );
471 }
472
473 std::set<intptr_t> iterated;
474
475 for( const auto& val : tree )
476 iterated.insert( val );
477
478 BOOST_CHECK_EQUAL( iterated.size(), 200 );
479
480 for( int i = 0; i < 200; ++i )
481 BOOST_CHECK( iterated.count( i ) );
482}
483
484
485BOOST_AUTO_TEST_CASE( EarlyTermination )
486{
488
489 for( int i = 0; i < 100; ++i )
490 {
491 int min[2] = { 0, 0 };
492 int max[2] = { 100, 100 };
493 tree.Insert( min, max, static_cast<intptr_t>( i ) );
494 }
495
496 int count = 0;
497
498 auto stopAfter5 = [&count]( intptr_t )
499 {
500 count++;
501 return count < 5;
502 };
503
504 int sMin[2] = { 0, 0 };
505 int sMax[2] = { 100, 100 };
506 tree.Search( sMin, sMax, stopAfter5 );
507
508 BOOST_CHECK_EQUAL( count, 5 );
509}
510
511
512BOOST_AUTO_TEST_CASE( RemoveMovedItem )
513{
514 // Simulate an item whose bbox changes after insertion (e.g., VIEW_ITEM that moves).
515 // DYNAMIC_RTREE::Remove falls back to full-tree search when the provided bbox
516 // doesn't match the stored insertion bbox.
518
519 int origMin[2] = { 100, 100 };
520 int origMax[2] = { 200, 200 };
521 tree.Insert( origMin, origMax, 42 );
522
523 int otherMin[2] = { 500, 500 };
524 int otherMax[2] = { 600, 600 };
525 tree.Insert( otherMin, otherMax, 99 );
526
527 BOOST_CHECK_EQUAL( tree.size(), 2 );
528
529 // Try to remove item 42 using a DIFFERENT bbox (simulating the item having moved).
530 // The primary search won't find it, but the fallback full-tree search should.
531 int movedMin[2] = { 300, 300 };
532 int movedMax[2] = { 400, 400 };
533 BOOST_CHECK( tree.Remove( movedMin, movedMax, 42 ) );
534 BOOST_CHECK_EQUAL( tree.size(), 1 );
535
536 // Verify item 99 is still there
537 std::vector<intptr_t> results;
538
539 auto collect = [&results]( intptr_t val )
540 {
541 results.push_back( val );
542 return true;
543 };
544
545 int fullMin[2] = { -1000, -1000 };
546 int fullMax[2] = { 1000, 1000 };
547 tree.Search( fullMin, fullMax, collect );
548
549 BOOST_CHECK_EQUAL( results.size(), 1 );
550 BOOST_CHECK_EQUAL( results[0], 99 );
551}
552
553
554BOOST_AUTO_TEST_CASE( StressInterleavedInsertRemoveQuery )
555{
556 const int N = 1000;
557 std::mt19937 rng( 11111 );
558 std::uniform_int_distribution<int> coordDist( 0, 100000 );
559 std::uniform_int_distribution<int> sizeDist( 100, 5000 );
560 std::uniform_int_distribution<int> opDist( 0, 9 );
561
563
564 struct ITEM
565 {
566 int min[2];
567 int max[2];
568 intptr_t id;
569 };
570
571 std::vector<ITEM> liveItems;
572
573 for( int op = 0; op < N; ++op )
574 {
575 int which = opDist( rng );
576
577 if( which < 5 || liveItems.empty() )
578 {
579 // Insert
580 ITEM item;
581 item.min[0] = coordDist( rng );
582 item.min[1] = coordDist( rng );
583 item.max[0] = item.min[0] + sizeDist( rng );
584 item.max[1] = item.min[1] + sizeDist( rng );
585 item.id = static_cast<intptr_t>( op );
586
587 tree.Insert( item.min, item.max, item.id );
588 liveItems.push_back( item );
589 }
590 else if( which < 7 )
591 {
592 // Remove
593 std::uniform_int_distribution<int> idxDist( 0, liveItems.size() - 1 );
594 int idx = idxDist( rng );
595 ITEM& item = liveItems[idx];
596
597 BOOST_CHECK( tree.Remove( item.min, item.max, item.id ) );
598
599 liveItems.erase( liveItems.begin() + idx );
600 }
601 else
602 {
603 // Query
604 int qMin[2] = { coordDist( rng ), coordDist( rng ) };
605 int qMax[2] = { qMin[0] + sizeDist( rng ) * 5, qMin[1] + sizeDist( rng ) * 5 };
606
607 std::set<intptr_t> expected;
608
609 for( const ITEM& item : liveItems )
610 {
611 if( item.min[0] <= qMax[0] && item.max[0] >= qMin[0]
612 && item.min[1] <= qMax[1] && item.max[1] >= qMin[1] )
613 {
614 expected.insert( item.id );
615 }
616 }
617
618 std::set<intptr_t> actual;
619
620 auto collect = [&actual]( intptr_t val )
621 {
622 actual.insert( val );
623 return true;
624 };
625
626 tree.Search( qMin, qMax, collect );
627 BOOST_CHECK_EQUAL( actual.size(), expected.size() );
628 }
629 }
630
631 BOOST_CHECK_EQUAL( tree.size(), liveItems.size() );
632}
633
634
635BOOST_AUTO_TEST_CASE( BulkLoadEmpty )
636{
638 std::vector<DYNAMIC_RTREE<int, int, 2>::BULK_ENTRY> entries;
639 tree.BulkLoad( entries );
640
641 BOOST_CHECK_EQUAL( tree.size(), 0u );
642 BOOST_CHECK( tree.empty() );
643}
644
645
646BOOST_AUTO_TEST_CASE( BulkLoadSingleItem )
647{
649 std::vector<DYNAMIC_RTREE<int, int, 2>::BULK_ENTRY> entries;
650 entries.push_back( { { 10, 20 }, { 30, 40 }, 42 } );
651 tree.BulkLoad( entries );
652
653 BOOST_CHECK_EQUAL( tree.size(), 1u );
654
655 std::vector<int> results;
656 auto collect = [&results]( int val ) { results.push_back( val ); return true; };
657 int qMin[2] = { 0, 0 };
658 int qMax[2] = { 100, 100 };
659 tree.Search( qMin, qMax, collect );
660
661 BOOST_CHECK_EQUAL( results.size(), 1u );
662 BOOST_CHECK_EQUAL( results[0], 42 );
663}
664
665
666BOOST_AUTO_TEST_CASE( BulkLoadCorrectnessVsBruteForce )
667{
668 const int N = 5000;
669 std::mt19937 rng( 99999 );
670 std::uniform_int_distribution<int> coordDist( 0, 100000 );
671 std::uniform_int_distribution<int> sizeDist( 100, 5000 );
672
673 struct ITEM
674 {
675 int min[2];
676 int max[2];
677 int id;
678 };
679
680 std::vector<ITEM> items( N );
681
682 for( int i = 0; i < N; ++i )
683 {
684 items[i].min[0] = coordDist( rng );
685 items[i].min[1] = coordDist( rng );
686 items[i].max[0] = items[i].min[0] + sizeDist( rng );
687 items[i].max[1] = items[i].min[1] + sizeDist( rng );
688 items[i].id = i;
689 }
690
691 // Build tree via BulkLoad
693 std::vector<DYNAMIC_RTREE<int, int, 2>::BULK_ENTRY> entries;
694 entries.reserve( N );
695
696 for( const auto& item : items )
697 entries.push_back( { { item.min[0], item.min[1] }, { item.max[0], item.max[1] }, item.id } );
698
699 tree.BulkLoad( entries );
700
701 BOOST_CHECK_EQUAL( tree.size(), static_cast<size_t>( N ) );
702
703 // Verify with random queries against brute force
704 const int QUERIES = 200;
705
706 for( int q = 0; q < QUERIES; ++q )
707 {
708 int qMin[2] = { coordDist( rng ), coordDist( rng ) };
709 int qMax[2] = { qMin[0] + sizeDist( rng ) * 5, qMin[1] + sizeDist( rng ) * 5 };
710
711 std::set<int> expected;
712
713 for( const auto& item : items )
714 {
715 if( item.min[0] <= qMax[0] && item.max[0] >= qMin[0]
716 && item.min[1] <= qMax[1] && item.max[1] >= qMin[1] )
717 {
718 expected.insert( item.id );
719 }
720 }
721
722 std::set<int> actual;
723 auto collect = [&actual]( int val ) { actual.insert( val ); return true; };
724 tree.Search( qMin, qMax, collect );
725
726 BOOST_CHECK_EQUAL( actual.size(), expected.size() );
727 BOOST_CHECK( actual == expected );
728 }
729
730 // Verify full iteration returns all items
731 std::set<int> allViaIter;
732
733 for( int val : tree )
734 allViaIter.insert( val );
735
736 BOOST_CHECK_EQUAL( allViaIter.size(), static_cast<size_t>( N ) );
737}
738
739
740BOOST_AUTO_TEST_CASE( BulkLoadThenInsertAndRemove )
741{
742 const int N = 500;
743 std::mt19937 rng( 77777 );
744 std::uniform_int_distribution<int> coordDist( 0, 100000 );
745 std::uniform_int_distribution<int> sizeDist( 100, 5000 );
746
747 // Build initial tree via BulkLoad
749 std::vector<DYNAMIC_RTREE<int, int, 2>::BULK_ENTRY> entries;
750
751 struct ITEM
752 {
753 int min[2];
754 int max[2];
755 int id;
756 };
757
758 std::vector<ITEM> liveItems;
759
760 for( int i = 0; i < N; ++i )
761 {
762 ITEM item;
763 item.min[0] = coordDist( rng );
764 item.min[1] = coordDist( rng );
765 item.max[0] = item.min[0] + sizeDist( rng );
766 item.max[1] = item.min[1] + sizeDist( rng );
767 item.id = i;
768 entries.push_back( { { item.min[0], item.min[1] }, { item.max[0], item.max[1] }, item.id } );
769 liveItems.push_back( item );
770 }
771
772 tree.BulkLoad( entries );
773 BOOST_CHECK_EQUAL( tree.size(), static_cast<size_t>( N ) );
774
775 // Now do incremental inserts
776 for( int i = N; i < N + 100; ++i )
777 {
778 ITEM item;
779 item.min[0] = coordDist( rng );
780 item.min[1] = coordDist( rng );
781 item.max[0] = item.min[0] + sizeDist( rng );
782 item.max[1] = item.min[1] + sizeDist( rng );
783 item.id = i;
784 tree.Insert( item.min, item.max, item.id );
785 liveItems.push_back( item );
786 }
787
788 BOOST_CHECK_EQUAL( tree.size(), static_cast<size_t>( N + 100 ) );
789
790 // Remove some items
791 for( int i = 0; i < 50; ++i )
792 {
793 std::uniform_int_distribution<int> idxDist( 0, liveItems.size() - 1 );
794 int idx = idxDist( rng );
795 ITEM& item = liveItems[idx];
796
797 BOOST_CHECK( tree.Remove( item.min, item.max, item.id ) );
798 liveItems.erase( liveItems.begin() + idx );
799 }
800
801 BOOST_CHECK_EQUAL( tree.size(), liveItems.size() );
802
803 // Verify a query
804 int qMin[2] = { 0, 0 };
805 int qMax[2] = { 200000, 200000 };
806
807 std::set<int> expected;
808
809 for( const auto& item : liveItems )
810 {
811 if( item.min[0] <= qMax[0] && item.max[0] >= qMin[0]
812 && item.min[1] <= qMax[1] && item.max[1] >= qMin[1] )
813 {
814 expected.insert( item.id );
815 }
816 }
817
818 std::set<int> actual;
819 auto collect = [&actual]( int val ) { actual.insert( val ); return true; };
820 tree.Search( qMin, qMax, collect );
821
822 BOOST_CHECK_EQUAL( actual.size(), expected.size() );
823}
824
825
827
828
829// --- CoW R-tree tests ---
830
831BOOST_AUTO_TEST_SUITE( CowRTree )
832
833
834BOOST_AUTO_TEST_CASE( CloneSharesData )
835{
837
838 for( int i = 0; i < 100; ++i )
839 {
840 int min[2] = { i * 10, i * 10 };
841 int max[2] = { i * 10 + 9, i * 10 + 9 };
842 tree.Insert( min, max, static_cast<intptr_t>( i ) );
843 }
844
845 BOOST_CHECK_EQUAL( tree.size(), 100 );
846
847 // Clone should be O(1) and share data
848 auto clone = tree.Clone();
849 BOOST_CHECK_EQUAL( clone.size(), 100 );
850
851 // Both should return the same search results
852 int sMin[2] = { 0, 0 };
853 int sMax[2] = { 50, 50 };
854
855 std::set<intptr_t> origResults;
856 std::set<intptr_t> cloneResults;
857
858 auto collectOrig = [&origResults]( intptr_t val )
859 {
860 origResults.insert( val );
861 return true;
862 };
863
864 auto collectClone = [&cloneResults]( intptr_t val )
865 {
866 cloneResults.insert( val );
867 return true;
868 };
869
870 tree.Search( sMin, sMax, collectOrig );
871 clone.Search( sMin, sMax, collectClone );
872
873 BOOST_CHECK( origResults == cloneResults );
874}
875
876
877BOOST_AUTO_TEST_CASE( CloneMutateInsert )
878{
880
881 for( int i = 0; i < 50; ++i )
882 {
883 int min[2] = { i * 10, 0 };
884 int max[2] = { i * 10 + 9, 9 };
885 tree.Insert( min, max, static_cast<intptr_t>( i ) );
886 }
887
888 auto clone = tree.Clone();
889
890 // Insert into clone only
891 int newMin[2] = { 1000, 1000 };
892 int newMax[2] = { 1010, 1010 };
893 clone.Insert( newMin, newMax, 999 );
894
895 BOOST_CHECK_EQUAL( tree.size(), 50 );
896 BOOST_CHECK_EQUAL( clone.size(), 51 );
897
898 // Verify parent doesn't see the new item
899 std::vector<intptr_t> origResults;
900
901 auto collectOrig = [&origResults]( intptr_t val )
902 {
903 origResults.push_back( val );
904 return true;
905 };
906
907 tree.Search( newMin, newMax, collectOrig );
908 BOOST_CHECK( origResults.empty() );
909
910 // Clone should see it
911 std::vector<intptr_t> cloneResults;
912
913 auto collectClone = [&cloneResults]( intptr_t val )
914 {
915 cloneResults.push_back( val );
916 return true;
917 };
918
919 clone.Search( newMin, newMax, collectClone );
920 BOOST_CHECK_EQUAL( cloneResults.size(), 1 );
921 BOOST_CHECK_EQUAL( cloneResults[0], 999 );
922}
923
924
925BOOST_AUTO_TEST_CASE( CloneMutateRemove )
926{
928
929 for( int i = 0; i < 50; ++i )
930 {
931 int min[2] = { i * 10, 0 };
932 int max[2] = { i * 10 + 9, 9 };
933 tree.Insert( min, max, static_cast<intptr_t>( i ) );
934 }
935
936 auto clone = tree.Clone();
937
938 // Remove from clone only
939 int rmMin[2] = { 0, 0 };
940 int rmMax[2] = { 9, 9 };
941 BOOST_CHECK( clone.Remove( rmMin, rmMax, 0 ) );
942
943 BOOST_CHECK_EQUAL( tree.size(), 50 );
944 BOOST_CHECK_EQUAL( clone.size(), 49 );
945
946 // Parent should still have item 0
947 std::vector<intptr_t> origResults;
948
949 auto collectOrig = [&origResults]( intptr_t val )
950 {
951 origResults.push_back( val );
952 return true;
953 };
954
955 tree.Search( rmMin, rmMax, collectOrig );
956 BOOST_CHECK( std::find( origResults.begin(), origResults.end(), 0 )
957 != origResults.end() );
958}
959
960
961BOOST_AUTO_TEST_CASE( MultiLevelClone )
962{
964
965 for( int i = 0; i < 30; ++i )
966 {
967 int min[2] = { i * 10, 0 };
968 int max[2] = { i * 10 + 9, 9 };
969 root.Insert( min, max, static_cast<intptr_t>( i ) );
970 }
971
972 auto child1 = root.Clone();
973
974 int newMin1[2] = { 500, 500 };
975 int newMax1[2] = { 510, 510 };
976 child1.Insert( newMin1, newMax1, 100 );
977
978 auto grandchild = child1.Clone();
979
980 int newMin2[2] = { 600, 600 };
981 int newMax2[2] = { 610, 610 };
982 grandchild.Insert( newMin2, newMax2, 200 );
983
984 BOOST_CHECK_EQUAL( root.size(), 30 );
985 BOOST_CHECK_EQUAL( child1.size(), 31 );
986 BOOST_CHECK_EQUAL( grandchild.size(), 32 );
987
988 // Root should not see either new item
989 std::vector<intptr_t> rootResults;
990
991 auto collectRoot = [&rootResults]( intptr_t val )
992 {
993 rootResults.push_back( val );
994 return true;
995 };
996
997 root.Search( newMin1, newMax1, collectRoot );
998 BOOST_CHECK( rootResults.empty() );
999
1000 root.Search( newMin2, newMax2, collectRoot );
1001 BOOST_CHECK( rootResults.empty() );
1002}
1003
1004
1005BOOST_AUTO_TEST_CASE( CloneIterator )
1006{
1008
1009 for( int i = 0; i < 50; ++i )
1010 {
1011 int min[2] = { i, i };
1012 int max[2] = { i + 1, i + 1 };
1013 tree.Insert( min, max, static_cast<intptr_t>( i ) );
1014 }
1015
1016 auto clone = tree.Clone();
1017
1018 std::set<intptr_t> cloneItems;
1019
1020 for( const auto& val : clone )
1021 cloneItems.insert( val );
1022
1023 BOOST_CHECK_EQUAL( cloneItems.size(), 50 );
1024}
1025
1026
1027BOOST_AUTO_TEST_CASE( MoveSemantics )
1028{
1030
1031 for( int i = 0; i < 30; ++i )
1032 {
1033 int min[2] = { i, i };
1034 int max[2] = { i + 1, i + 1 };
1035 tree.Insert( min, max, static_cast<intptr_t>( i ) );
1036 }
1037
1038 auto clone = tree.Clone();
1039
1040 COW_RTREE<intptr_t, int, 2> moved( std::move( clone ) );
1041 BOOST_CHECK_EQUAL( moved.size(), 30 );
1042 BOOST_CHECK_EQUAL( clone.size(), 0 );
1043
1044 // Original tree should be unaffected
1045 BOOST_CHECK_EQUAL( tree.size(), 30 );
1046}
1047
1048
Copy-on-Write wrapper for DYNAMIC_RTREE.
int Search(const ELEMTYPE aMin[NUMDIMS], const ELEMTYPE aMax[NUMDIMS], VISITOR &aVisitor) const
Search for items whose bounding boxes overlap the query rectangle.
void Insert(const ELEMTYPE aMin[NUMDIMS], const ELEMTYPE aMax[NUMDIMS], const DATATYPE &aData)
Insert an item.
COW_RTREE Clone() const
Create a clone that shares the tree structure with this tree.
Dynamic R*-tree with SoA node layout and stored insertion bounding boxes.
Iterator begin() const
void NearestNeighbors(const ELEMTYPE aPoint[NUMDIMS], int aK, std::vector< std::pair< int64_t, DATATYPE > > &aResults) const
Nearest-neighbor search using Hjaltason & Samet's algorithm.
int Search(const ELEMTYPE aMin[NUMDIMS], const ELEMTYPE aMax[NUMDIMS], VISITOR &aVisitor) const
Search for items whose bounding boxes overlap the query rectangle.
Iterator end() const
bool Remove(const ELEMTYPE aMin[NUMDIMS], const ELEMTYPE aMax[NUMDIMS], const DATATYPE &aData)
Remove an item using its stored insertion bounding box.
void Insert(const ELEMTYPE aMin[NUMDIMS], const ELEMTYPE aMax[NUMDIMS], const DATATYPE &aData)
Insert an item with the given bounding box.
void BulkLoad(std::vector< BULK_ENTRY > &aEntries)
Build the tree from a batch of entries using Hilbert-curve packed bulk loading.
void RemoveAll()
Remove all items from the tree.
static thread_local boost::mt19937 rng
Definition kiid.cpp:48
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_AUTO_TEST_CASE(EmptyTree)
BOOST_AUTO_TEST_SUITE_END()
bool moved
VECTOR3I expected(15, 30, 45)
int actual
BOOST_CHECK_EQUAL(result, "25.4")