KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_packed_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
27
28#include <algorithm>
29#include <cstdlib>
30#include <random>
31#include <set>
32#include <vector>
33
34using namespace KIRTREE;
35
36BOOST_AUTO_TEST_SUITE( PackedRTree )
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
57{
59
60 int min[2] = { 10, 20 };
61 int max[2] = { 30, 40 };
62 builder.Add( min, max, 42 );
63
64 auto tree = builder.Build();
65
66 BOOST_CHECK( !tree.empty() );
67 BOOST_CHECK_EQUAL( tree.size(), 1 );
68
69 // Search that overlaps
70 int sMin[2] = { 0, 0 };
71 int sMax[2] = { 15, 25 };
72 std::vector<intptr_t> results;
73
74 auto collect = [&results]( intptr_t val )
75 {
76 results.push_back( val );
77 return true;
78 };
79
80 BOOST_CHECK_EQUAL( tree.Search( sMin, sMax, collect ), 1 );
81 BOOST_CHECK_EQUAL( results.size(), 1 );
82 BOOST_CHECK_EQUAL( results[0], 42 );
83
84 // Search that doesn't overlap
85 results.clear();
86 int sMin2[2] = { 100, 100 };
87 int sMax2[2] = { 200, 200 };
88 BOOST_CHECK_EQUAL( tree.Search( sMin2, sMax2, collect ), 0 );
89 BOOST_CHECK( results.empty() );
90}
91
92
94{
95 // Create a 10x10 grid of non-overlapping 10x10 items
97 builder.Reserve( 100 );
98
99 for( int y = 0; y < 10; ++y )
100 {
101 for( int x = 0; x < 10; ++x )
102 {
103 int min[2] = { x * 10, y * 10 };
104 int max[2] = { x * 10 + 10, y * 10 + 10 };
105 builder.Add( min, max, static_cast<intptr_t>( y * 10 + x ) );
106 }
107 }
108
109 auto tree = builder.Build();
110
111 BOOST_CHECK_EQUAL( tree.size(), 100 );
112
113 // Search a region that should contain exactly 4 items (2x2 in top-left)
114 int sMin[2] = { 0, 0 };
115 int sMax[2] = { 19, 19 };
116 std::set<intptr_t> results;
117
118 auto collect = [&results]( intptr_t val )
119 {
120 results.insert( val );
121 return true;
122 };
123
124 tree.Search( sMin, sMax, collect );
125
126 BOOST_CHECK_EQUAL( results.size(), 4 );
127 BOOST_CHECK( results.count( 0 ) ); // (0,0)
128 BOOST_CHECK( results.count( 1 ) ); // (1,0)
129 BOOST_CHECK( results.count( 10 ) ); // (0,1)
130 BOOST_CHECK( results.count( 11 ) ); // (1,1)
131
132 // Search entire region should return all 100
133 results.clear();
134 int fullMin[2] = { 0, 0 };
135 int fullMax[2] = { 100, 100 };
136 tree.Search( fullMin, fullMax, collect );
137 BOOST_CHECK_EQUAL( results.size(), 100 );
138}
139
140
142{
143 // Zero-area bounding boxes (points)
145
146 for( int i = 0; i < 50; ++i )
147 {
148 int min[2] = { i * 10, i * 10 };
149 int max[2] = { i * 10, i * 10 };
150 builder.Add( min, max, static_cast<intptr_t>( i ) );
151 }
152
153 auto tree = builder.Build();
154
155 BOOST_CHECK_EQUAL( tree.size(), 50 );
156
157 // Search a point
158 int sMin[2] = { 100, 100 };
159 int sMax[2] = { 100, 100 };
160 std::vector<intptr_t> results;
161
162 auto collect = [&results]( intptr_t val )
163 {
164 results.push_back( val );
165 return true;
166 };
167
168 tree.Search( sMin, sMax, collect );
169 BOOST_CHECK_EQUAL( results.size(), 1 );
170 BOOST_CHECK_EQUAL( results[0], 10 );
171}
172
173
174BOOST_AUTO_TEST_CASE( OverlappingItems )
175{
177
178 // All items overlap at the origin region
179 for( int i = 0; i < 20; ++i )
180 {
181 int min[2] = { -i, -i };
182 int max[2] = { i + 10, i + 10 };
183 builder.Add( min, max, static_cast<intptr_t>( i ) );
184 }
185
186 auto tree = builder.Build();
187
188 // Search at origin should find all 20
189 int sMin[2] = { 0, 0 };
190 int sMax[2] = { 0, 0 };
191 int count = 0;
192
193 auto counter = [&count]( intptr_t )
194 {
195 count++;
196 return true;
197 };
198
199 tree.Search( sMin, sMax, counter );
200 BOOST_CHECK_EQUAL( count, 20 );
201}
202
203
204BOOST_AUTO_TEST_CASE( EarlyTermination )
205{
207
208 for( int i = 0; i < 100; ++i )
209 {
210 int min[2] = { 0, 0 };
211 int max[2] = { 100, 100 };
212 builder.Add( min, max, static_cast<intptr_t>( i ) );
213 }
214
215 auto tree = builder.Build();
216
217 // Visitor that stops after 3 items
218 int count = 0;
219
220 auto stopAfter3 = [&count]( intptr_t )
221 {
222 count++;
223 return count < 3;
224 };
225
226 int sMin[2] = { 0, 0 };
227 int sMax[2] = { 100, 100 };
228 tree.Search( sMin, sMax, stopAfter3 );
229
230 BOOST_CHECK_EQUAL( count, 3 );
231}
232
233
234BOOST_AUTO_TEST_CASE( FullIteration )
235{
237
238 for( int i = 0; i < 100; ++i )
239 {
240 int min[2] = { i, i };
241 int max[2] = { i + 1, i + 1 };
242 builder.Add( min, max, static_cast<intptr_t>( i ) );
243 }
244
245 auto tree = builder.Build();
246
247 std::set<intptr_t> iteratedValues;
248
249 for( auto it = tree.begin(); it != tree.end(); ++it )
250 iteratedValues.insert( *it );
251
252 BOOST_CHECK_EQUAL( iteratedValues.size(), 100 );
253
254 for( int i = 0; i < 100; ++i )
255 BOOST_CHECK( iteratedValues.count( i ) );
256}
257
258
259BOOST_AUTO_TEST_CASE( LargeDatasetBruteForce )
260{
261 // Verify correctness against brute-force for 10K items
262 const int N = 10000;
263 std::mt19937 rng( 12345 );
264 std::uniform_int_distribution<int> coordDist( -1000000, 1000000 );
265 std::uniform_int_distribution<int> sizeDist( 1, 10000 );
266
267 struct ITEM
268 {
269 int min[2];
270 int max[2];
271 intptr_t id;
272 };
273
274 std::vector<ITEM> items( N );
276 builder.Reserve( N );
277
278 for( int i = 0; i < N; ++i )
279 {
280 int x = coordDist( rng );
281 int y = coordDist( rng );
282 int w = sizeDist( rng );
283 int h = sizeDist( rng );
284
285 items[i].min[0] = x;
286 items[i].min[1] = y;
287 items[i].max[0] = x + w;
288 items[i].max[1] = y + h;
289 items[i].id = static_cast<intptr_t>( i );
290
291 builder.Add( items[i].min, items[i].max, items[i].id );
292 }
293
294 auto tree = builder.Build();
295
296 // Run 100 random queries, verify against brute-force
297 for( int q = 0; q < 100; ++q )
298 {
299 int qx = coordDist( rng );
300 int qy = coordDist( rng );
301 int qw = sizeDist( rng ) * 10;
302 int qh = sizeDist( rng ) * 10;
303
304 int qMin[2] = { qx, qy };
305 int qMax[2] = { qx + qw, qy + qh };
306
307 // Brute-force
308 std::set<intptr_t> expected;
309
310 for( const ITEM& item : items )
311 {
312 if( item.min[0] <= qMax[0] && item.max[0] >= qMin[0]
313 && item.min[1] <= qMax[1] && item.max[1] >= qMin[1] )
314 {
315 expected.insert( item.id );
316 }
317 }
318
319 // R-tree
320 std::set<intptr_t> actual;
321
322 auto collect = [&actual]( intptr_t val )
323 {
324 actual.insert( val );
325 return true;
326 };
327
328 tree.Search( qMin, qMax, collect );
329
330 BOOST_CHECK_EQUAL( actual.size(), expected.size() );
331 BOOST_CHECK( actual == expected );
332 }
333}
334
335
337{
339 builder.Reserve( 1000 );
340
341 for( int i = 0; i < 1000; ++i )
342 {
343 int min[2] = { i, i };
344 int max[2] = { i + 1, i + 1 };
345 builder.Add( min, max, static_cast<intptr_t>( i ) );
346 }
347
348 auto tree = builder.Build();
349
350 size_t memUsage = tree.MemoryUsage();
351
352 // Should be reasonable: data (8KB for pointers) + node overhead
353 // With fanout=16, 1000 items need ~63 leaf nodes + ~5 internal nodes
354 // Memory overhead should be modest relative to item storage
355 BOOST_CHECK_GT( memUsage, 1000 * sizeof( intptr_t ) );
356 BOOST_CHECK_LT( memUsage, 1000 * sizeof( intptr_t ) * 5 ); // < 5x overhead
357}
358
359
360BOOST_AUTO_TEST_CASE( MoveSemantics )
361{
363
364 for( int i = 0; i < 50; ++i )
365 {
366 int min[2] = { i, i };
367 int max[2] = { i + 10, i + 10 };
368 builder.Add( min, max, static_cast<intptr_t>( i ) );
369 }
370
371 auto tree1 = builder.Build();
372 BOOST_CHECK_EQUAL( tree1.size(), 50 );
373
374 // Move construct
375 auto tree2 = std::move( tree1 );
376 BOOST_CHECK_EQUAL( tree2.size(), 50 );
377 BOOST_CHECK_EQUAL( tree1.size(), 0 );
378 BOOST_CHECK( tree1.empty() );
379
380 // Move assign
382 tree3 = std::move( tree2 );
383 BOOST_CHECK_EQUAL( tree3.size(), 50 );
384 BOOST_CHECK_EQUAL( tree2.size(), 0 );
385
386 // Verify the moved-to tree still works
387 int sMin[2] = { 0, 0 };
388 int sMax[2] = { 100, 100 };
389 int count = 0;
390
391 auto counter = [&count]( intptr_t )
392 {
393 count++;
394 return true;
395 };
396
397 tree3.Search( sMin, sMax, counter );
398 BOOST_CHECK_EQUAL( count, 50 );
399}
400
401
402BOOST_AUTO_TEST_CASE( ThreeDimensional )
403{
405
406 for( int i = 0; i < 100; ++i )
407 {
408 int min[3] = { i, i, i };
409 int max[3] = { i + 5, i + 5, i + 5 };
410 builder.Add( min, max, static_cast<intptr_t>( i ) );
411 }
412
413 auto tree = builder.Build();
414
415 BOOST_CHECK_EQUAL( tree.size(), 100 );
416
417 // Search a region
418 int sMin[3] = { 10, 10, 10 };
419 int sMax[3] = { 20, 20, 20 };
420 int count = 0;
421
422 auto counter = [&count]( intptr_t )
423 {
424 count++;
425 return true;
426 };
427
428 tree.Search( sMin, sMax, counter );
429 BOOST_CHECK_GT( count, 0 );
430}
431
432
Builder for constructing a PACKED_RTREE from a set of items.
void Add(const ELEMTYPE aMin[NUMDIMS], const ELEMTYPE aMax[NUMDIMS], const DATATYPE &aData)
void Reserve(size_t aCount)
Static (immutable) packed R-tree built via Hilbert-curve bulk loading.
Iterator end() const
size_t size() const
int Search(const ELEMTYPE aMin[NUMDIMS], const ELEMTYPE aMax[NUMDIMS], VISITOR &aVisitor) const
Search for all items whose bounding boxes overlap the query rectangle.
Iterator begin() const
size_t MemoryUsage() const
Return approximate memory usage in bytes.
static thread_local boost::mt19937 rng
Definition kiid.cpp:50
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_AUTO_TEST_SUITE_END()
VECTOR3I expected(15, 30, 45)
BOOST_AUTO_TEST_CASE(EmptyTree)
int actual
BOOST_CHECK_EQUAL(result, "25.4")