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