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