KiCad PCB EDA Suite
Loading...
Searching...
No Matches
rtree_node.h
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
20#ifndef RTREE_NODE_H
21#define RTREE_NODE_H
22
23#include <algorithm>
24#include <atomic>
25#include <bitset>
26#include <cassert>
27#include <cstdint>
28#include <cstring>
29#include <limits>
30#include <memory>
31#include <vector>
32
33#if defined( __SSE2__ )
34#include <emmintrin.h>
35#elif defined( __ARM_NEON ) || defined( __ARM_NEON__ )
36#include <arm_neon.h>
37#endif
38
39namespace KIRTREE
40{
41
54inline uint64_t HilbertXY2D( int aOrder, uint32_t aX, uint32_t aY )
55{
56 uint64_t d = 0;
57 uint32_t x = aX;
58 uint32_t y = aY;
59
60 for( uint32_t s = ( 1U << ( aOrder - 1 ) ); s > 0; s >>= 1 )
61 {
62 uint32_t rx = ( x & s ) ? 1 : 0;
63 uint32_t ry = ( y & s ) ? 1 : 0;
64
65 d += static_cast<uint64_t>( s ) * s * ( ( 3 * rx ) ^ ry );
66
67 // Rotate quadrant
68 if( ry == 0 )
69 {
70 if( rx == 1 )
71 {
72 x = s * 2 - 1 - x;
73 y = s * 2 - 1 - y;
74 }
75
76 uint32_t tmp = x;
77 x = y;
78 y = tmp;
79 }
80 }
81
82 return d;
83}
84
85
97template <int NUMDIMS>
98inline uint64_t HilbertND2D( int aOrder, const uint32_t aCoords[NUMDIMS] )
99{
100 if constexpr( NUMDIMS == 2 )
101 {
102 return HilbertXY2D( aOrder, aCoords[0], aCoords[1] );
103 }
104 else
105 {
106 // For 3+ dimensions, fall back to Z-order (Morton) curve interleaving.
107 // This gives good-enough clustering without the complexity of generalized
108 // Hilbert in N dimensions.
109 //
110 // A 64-bit index holds only 64/NUMDIMS bits per axis. Interleaving every bit
111 // shifts the significant ones back out of the index, so take the high bits.
112 const int bits = std::min( aOrder, 64 / NUMDIMS );
113 const int drop = aOrder - bits;
114
115 uint64_t d = 0;
116
117 for( int bit = bits - 1; bit >= 0; --bit )
118 {
119 for( int dim = NUMDIMS - 1; dim >= 0; --dim )
120 {
121 d <<= 1;
122 d |= ( aCoords[dim] >> ( bit + drop ) ) & 1;
123 }
124 }
125
126 return d;
127 }
128}
129
130
150template <class ELEMTYPE, int FANOUT>
151inline uint32_t OverlapMask2D( const ELEMTYPE* aBoundsMin0, const ELEMTYPE* aBoundsMax0,
152 const ELEMTYPE* aBoundsMin1, const ELEMTYPE* aBoundsMax1,
153 int aCount,
154 const ELEMTYPE aMin[2], const ELEMTYPE aMax[2] )
155{
156 static_assert( FANOUT <= 31, "OverlapMask2D requires FANOUT <= 31" );
157 static_assert( FANOUT % 4 == 0, "OverlapMask2D requires FANOUT divisible by 4 for SIMD" );
158
159#if defined( __SSE2__ )
160 if constexpr( sizeof( ELEMTYPE ) == 4 )
161 {
162 // Broadcast query bounds to all 4 SIMD lanes
163 __m128i qmin0 = _mm_set1_epi32( static_cast<int>( aMin[0] ) );
164 __m128i qmax0 = _mm_set1_epi32( static_cast<int>( aMax[0] ) );
165 __m128i qmin1 = _mm_set1_epi32( static_cast<int>( aMin[1] ) );
166 __m128i qmax1 = _mm_set1_epi32( static_cast<int>( aMax[1] ) );
167
168 uint32_t mask = 0;
169
170 // Test 4 children per iteration using 128-bit packed integer comparisons.
171 // Each iteration loads one 16-byte chunk from each of the 4 bounds arrays,
172 // tests all 4 separation axes, and extracts the result to 4 mask bits via
173 // movemask (single instruction to extract the top bit of each 32-bit lane).
174 for( int j = 0; j < FANOUT; j += 4 )
175 {
176 __m128i bmin0 = _mm_loadu_si128(
177 reinterpret_cast<const __m128i*>( &aBoundsMin0[j] ) );
178 __m128i bmax0 = _mm_loadu_si128(
179 reinterpret_cast<const __m128i*>( &aBoundsMax0[j] ) );
180 __m128i bmin1 = _mm_loadu_si128(
181 reinterpret_cast<const __m128i*>( &aBoundsMin1[j] ) );
182 __m128i bmax1 = _mm_loadu_si128(
183 reinterpret_cast<const __m128i*>( &aBoundsMax1[j] ) );
184
185 // Any separation axis proves non-overlap
186 __m128i fail = _mm_or_si128(
187 _mm_or_si128( _mm_cmpgt_epi32( bmin0, qmax0 ),
188 _mm_cmpgt_epi32( qmin0, bmax0 ) ),
189 _mm_or_si128( _mm_cmpgt_epi32( bmin1, qmax1 ),
190 _mm_cmpgt_epi32( qmin1, bmax1 ) ) );
191
192 // Extract top bit of each 32-bit lane into a 4-bit integer
193 int failBits = _mm_movemask_ps( _mm_castsi128_ps( fail ) );
194 mask |= static_cast<uint32_t>( ~failBits & 0xF ) << j;
195 }
196
197 return mask & ( ( 1U << aCount ) - 1 );
198 }
199 else
200#elif defined( __ARM_NEON ) || defined( __ARM_NEON__ )
201 if constexpr( sizeof( ELEMTYPE ) == 4 )
202 {
203 // Broadcast query bounds to all 4 NEON lanes
204 int32x4_t qmin0 = vdupq_n_s32( static_cast<int32_t>( aMin[0] ) );
205 int32x4_t qmax0 = vdupq_n_s32( static_cast<int32_t>( aMax[0] ) );
206 int32x4_t qmin1 = vdupq_n_s32( static_cast<int32_t>( aMin[1] ) );
207 int32x4_t qmax1 = vdupq_n_s32( static_cast<int32_t>( aMax[1] ) );
208
209 // Bit-position shifts for converting per-lane fail bits into a bitmask.
210 // Lane 0 stays at bit 0, lane 1 shifts left by 1, etc.
211 static const int32_t kBitShifts[4] = { 0, 1, 2, 3 };
212 int32x4_t shifts = vld1q_s32( kBitShifts );
213
214 uint32_t mask = 0;
215
216 // Test 4 children per iteration using 128-bit NEON comparisons.
217 // NEON lacks movemask, so we extract each lane's 1-bit result via
218 // right-shift-31, position it with a variable left shift, and
219 // OR-reduce the 4 lanes to a 4-bit chunk.
220 for( int j = 0; j < FANOUT; j += 4 )
221 {
222 int32x4_t bmin0 = vld1q_s32(
223 reinterpret_cast<const int32_t*>( &aBoundsMin0[j] ) );
224 int32x4_t bmax0 = vld1q_s32(
225 reinterpret_cast<const int32_t*>( &aBoundsMax0[j] ) );
226 int32x4_t bmin1 = vld1q_s32(
227 reinterpret_cast<const int32_t*>( &aBoundsMin1[j] ) );
228 int32x4_t bmax1 = vld1q_s32(
229 reinterpret_cast<const int32_t*>( &aBoundsMax1[j] ) );
230
231 // Any separation axis proves non-overlap (vcgtq_s32 yields all-1s on true)
232 uint32x4_t fail = vorrq_u32(
233 vorrq_u32( vcgtq_s32( bmin0, qmax0 ),
234 vcgtq_s32( qmin0, bmax0 ) ),
235 vorrq_u32( vcgtq_s32( bmin1, qmax1 ),
236 vcgtq_s32( qmin1, bmax1 ) ) );
237
238 // Extract sign bit (0 or 1) from each lane, shift to bit position
239 uint32x4_t bits = vshrq_n_u32( fail, 31 );
240 uint32x4_t positioned = vshlq_u32( bits, shifts );
241
242 // OR-reduce 4 lanes into a 4-bit fail mask
243 uint32x2_t half = vorr_u32( vget_low_u32( positioned ),
244 vget_high_u32( positioned ) );
245 uint32_t failMask = vget_lane_u32( half, 0 ) | vget_lane_u32( half, 1 );
246
247 mask |= static_cast<uint32_t>( ~failMask & 0xF ) << j;
248 }
249
250 return mask & ( ( 1U << aCount ) - 1 );
251 }
252 else
253#endif
254 {
255 // Scalar fallback with a two-pass structure for auto-vectorization.
256 //
257 // The first loop performs branchless comparisons across all FANOUT slots,
258 // writing boolean results to a flat array. This fixed-trip-count pattern
259 // without data-dependent exits allows GCC and Clang to auto-vectorize the
260 // comparisons even at baseline SSE2/NEON instruction levels.
261 //
262 // The second loop packs the booleans into a bitmask. The variable-shift
263 // bit-packing is inherently scalar but operates on only FANOUT iterations.
264 int result[FANOUT];
265
266 for( int i = 0; i < FANOUT; ++i )
267 {
268 result[i] = ( aBoundsMin0[i] <= aMax[0] )
269 & ( aBoundsMax0[i] >= aMin[0] )
270 & ( aBoundsMin1[i] <= aMax[1] )
271 & ( aBoundsMax1[i] >= aMin[1] );
272 }
273
274 uint32_t mask = 0;
275
276 for( int i = 0; i < FANOUT; ++i )
277 mask |= ( static_cast<uint32_t>( result[i] ) << i );
278
279 return mask & ( ( 1U << aCount ) - 1 );
280 }
281}
282
283
300template <class DATATYPE, class ELEMTYPE, int NUMDIMS, int MAXNODES>
302{
303 static constexpr int MINNODES = MAXNODES * 2 / 5; // ~40%, R*-tree convention
304
305 int count = 0; // Number of valid children
306 int level = 0; // 0 = leaf, higher = internal
307
308 // SoA bounding boxes: bounds[axis*2+side][slot]
309 // side 0 = min, side 1 = max
310 ELEMTYPE bounds[NUMDIMS * 2][MAXNODES];
311
312 union
313 {
314 RTREE_NODE* children[MAXNODES]; // Internal node children
315 DATATYPE data[MAXNODES]; // Leaf node data
316 };
317
318 // Stored insertion bboxes for leaf entries, enabling O(log N) removal
319 // even when the item has moved since insertion
320 ELEMTYPE insertBounds[NUMDIMS * 2][MAXNODES];
321
322 std::atomic<int> refcount; // For CoW support
323
325 {
326 std::memset( bounds, 0, sizeof( bounds ) );
327 std::memset( insertBounds, 0, sizeof( insertBounds ) );
328 std::memset( &children, 0, sizeof( children ) );
329 }
330
331 bool IsLeaf() const { return level == 0; }
332 bool IsInternal() const { return level > 0; }
333 bool IsFull() const { return count >= MAXNODES; }
334 bool IsUnderflow() const { return count < MINNODES; }
335
339 void ComputeEnclosingBounds( ELEMTYPE aMin[NUMDIMS], ELEMTYPE aMax[NUMDIMS] ) const
340 {
341 for( int d = 0; d < NUMDIMS; ++d )
342 {
343 aMin[d] = std::numeric_limits<ELEMTYPE>::max();
344 aMax[d] = std::numeric_limits<ELEMTYPE>::lowest();
345 }
346
347 for( int i = 0; i < count; ++i )
348 {
349 for( int d = 0; d < NUMDIMS; ++d )
350 {
351 if( bounds[d * 2][i] < aMin[d] )
352 aMin[d] = bounds[d * 2][i];
353
354 if( bounds[d * 2 + 1][i] > aMax[d] )
355 aMax[d] = bounds[d * 2 + 1][i];
356 }
357 }
358 }
359
363 int64_t ChildArea( int i ) const
364 {
365 int64_t area = 1;
366
367 for( int d = 0; d < NUMDIMS; ++d )
368 area *= static_cast<int64_t>( bounds[d * 2 + 1][i] ) - bounds[d * 2][i];
369
370 return area;
371 }
372
376 bool ChildOverlaps( int i, const ELEMTYPE aMin[NUMDIMS], const ELEMTYPE aMax[NUMDIMS] ) const
377 {
378 for( int d = 0; d < NUMDIMS; ++d )
379 {
380 if( bounds[d * 2][i] > aMax[d] || bounds[d * 2 + 1][i] < aMin[d] )
381 return false;
382 }
383
384 return true;
385 }
386
391 uint32_t ChildOverlapMask( const ELEMTYPE aMin[NUMDIMS],
392 const ELEMTYPE aMax[NUMDIMS] ) const
393 {
394 if constexpr( NUMDIMS == 2 )
395 {
397 bounds[0], bounds[1], bounds[2], bounds[3], count, aMin, aMax );
398 }
399 else
400 {
401 uint32_t mask = 0;
402
403 for( int i = 0; i < count; ++i )
404 {
405 if( ChildOverlaps( i, aMin, aMax ) )
406 mask |= ( 1U << i );
407 }
408
409 return mask;
410 }
411 }
412
416 int64_t ChildOverlapArea( int i, const ELEMTYPE aMin[NUMDIMS],
417 const ELEMTYPE aMax[NUMDIMS] ) const
418 {
419 int64_t overlap = 1;
420
421 for( int d = 0; d < NUMDIMS; ++d )
422 {
423 ELEMTYPE lo = std::max( bounds[d * 2][i], aMin[d] );
424 ELEMTYPE hi = std::min( bounds[d * 2 + 1][i], aMax[d] );
425
426 if( lo > hi )
427 return 0;
428
429 overlap *= static_cast<int64_t>( hi ) - lo;
430 }
431
432 return overlap;
433 }
434
439 int64_t ChildEnlargement( int i, const ELEMTYPE aMin[NUMDIMS],
440 const ELEMTYPE aMax[NUMDIMS] ) const
441 {
442 int64_t originalArea = ChildArea( i );
443 int64_t enlargedArea = 1;
444
445 for( int d = 0; d < NUMDIMS; ++d )
446 {
447 ELEMTYPE lo = std::min( bounds[d * 2][i], aMin[d] );
448 ELEMTYPE hi = std::max( bounds[d * 2 + 1][i], aMax[d] );
449 enlargedArea *= static_cast<int64_t>( hi ) - lo;
450 }
451
452 return enlargedArea - originalArea;
453 }
454
459 int64_t ChildPerimeter( int i ) const
460 {
461 int64_t perimeter = 0;
462
463 for( int d = 0; d < NUMDIMS; ++d )
464 perimeter += static_cast<int64_t>( bounds[d * 2 + 1][i] ) - bounds[d * 2][i];
465
466 return 2 * perimeter;
467 }
468
472 void SetChildBounds( int i, const ELEMTYPE aMin[NUMDIMS], const ELEMTYPE aMax[NUMDIMS] )
473 {
474 for( int d = 0; d < NUMDIMS; ++d )
475 {
476 bounds[d * 2][i] = aMin[d];
477 bounds[d * 2 + 1][i] = aMax[d];
478 }
479 }
480
484 void GetChildBounds( int i, ELEMTYPE aMin[NUMDIMS], ELEMTYPE aMax[NUMDIMS] ) const
485 {
486 for( int d = 0; d < NUMDIMS; ++d )
487 {
488 aMin[d] = bounds[d * 2][i];
489 aMax[d] = bounds[d * 2 + 1][i];
490 }
491 }
492
496 void SetInsertBounds( int i, const ELEMTYPE aMin[NUMDIMS], const ELEMTYPE aMax[NUMDIMS] )
497 {
498 for( int d = 0; d < NUMDIMS; ++d )
499 {
500 insertBounds[d * 2][i] = aMin[d];
501 insertBounds[d * 2 + 1][i] = aMax[d];
502 }
503 }
504
508 void GetInsertBounds( int i, ELEMTYPE aMin[NUMDIMS], ELEMTYPE aMax[NUMDIMS] ) const
509 {
510 for( int d = 0; d < NUMDIMS; ++d )
511 {
512 aMin[d] = insertBounds[d * 2][i];
513 aMax[d] = insertBounds[d * 2 + 1][i];
514 }
515 }
516
520 void RemoveChild( int i )
521 {
522 assert( i < count );
523 int last = count - 1;
524
525 if( i != last )
526 {
527 for( int d = 0; d < NUMDIMS * 2; ++d )
528 {
529 bounds[d][i] = bounds[d][last];
530 insertBounds[d][i] = insertBounds[d][last];
531 }
532
533 if( IsLeaf() )
534 data[i] = data[last];
535 else
536 children[i] = children[last];
537 }
538
539 count--;
540 }
541};
542
543
553template <class NODE>
555{
556public:
557 static constexpr size_t NODES_PER_PAGE = 256;
558
559 struct PAGE
560 {
561 alignas( 64 ) NODE nodes[NODES_PER_PAGE];
562 std::bitset<NODES_PER_PAGE> used;
563
564 PAGE() : used() {}
565 };
566
567 SLAB_ALLOCATOR() = default;
568
569 ~SLAB_ALLOCATOR() = default;
570
571 // Non-copyable
572 SLAB_ALLOCATOR( const SLAB_ALLOCATOR& ) = delete;
574
575 // Movable
576 SLAB_ALLOCATOR( SLAB_ALLOCATOR&& aOther ) noexcept :
577 m_pages( std::move( aOther.m_pages ) ),
578 m_freeList( std::move( aOther.m_freeList ) )
579 {
580 }
581
583 {
584 if( this != &aOther )
585 {
586 m_pages = std::move( aOther.m_pages );
587 m_freeList = std::move( aOther.m_freeList );
588 }
589
590 return *this;
591 }
592
596 NODE* Allocate()
597 {
598 if( !m_freeList.empty() )
599 {
600 NODE* node = m_freeList.back();
601 m_freeList.pop_back();
602
603 // Re-mark as used in the owning page's bitset
604 markUsed( node );
605
606 new( node ) NODE();
607 return node;
608 }
609
610 // Find a page with free slots, or create one
611 for( auto& page : m_pages )
612 {
613 if( page->used.count() < NODES_PER_PAGE )
614 {
615 for( size_t i = 0; i < NODES_PER_PAGE; ++i )
616 {
617 if( !page->used[i] )
618 {
619 page->used.set( i );
620 NODE* node = &page->nodes[i];
621 new( node ) NODE();
622 return node;
623 }
624 }
625 }
626 }
627
628 // All pages full, allocate a new one
629 m_pages.push_back( std::make_unique<PAGE>() );
630 PAGE* page = m_pages.back().get();
631 page->used.set( 0 );
632 NODE* node = &page->nodes[0];
633 new( node ) NODE();
634 return node;
635 }
636
640 void Free( NODE* aNode )
641 {
642 if( !aNode )
643 return;
644
645 // Clear the used bit so the page-scan fallback path stays consistent
646 markUnused( aNode );
647
648 aNode->~NODE();
649 m_freeList.push_back( aNode );
650 }
651
655 size_t MemoryUsage() const
656 {
657 return m_pages.size() * sizeof( PAGE )
658 + m_freeList.capacity() * sizeof( NODE* );
659 }
660
664 bool Owns( const NODE* aNode ) const
665 {
666 size_t ignored = 0;
667 return findOwningPage( aNode, ignored ) != nullptr;
668 }
669
670private:
675 PAGE* findOwningPage( const NODE* aNode, size_t& aOffset ) const
676 {
677 const auto addr = reinterpret_cast<uintptr_t>( aNode );
678
679 for( auto& page : m_pages )
680 {
681 const auto begin = reinterpret_cast<uintptr_t>( &page->nodes[0] );
682 const auto end = reinterpret_cast<uintptr_t>( &page->nodes[NODES_PER_PAGE] );
683
684 if( addr >= begin && addr < end )
685 {
686 // Safe to subtract now since both pointers are in the same array
687 aOffset = static_cast<size_t>( aNode - &page->nodes[0] );
688 return page.get();
689 }
690 }
691
692 return nullptr;
693 }
694
695 void markUsed( NODE* aNode )
696 {
697 size_t offset = 0;
698 PAGE* page = findOwningPage( aNode, offset );
699
700 if( page )
701 page->used.set( offset );
702 }
703
704 void markUnused( NODE* aNode )
705 {
706 size_t offset = 0;
707 PAGE* page = findOwningPage( aNode, offset );
708
709 if( page )
710 page->used.reset( offset );
711 }
712
713 std::vector<std::unique_ptr<PAGE>> m_pages;
714 std::vector<NODE*> m_freeList;
715};
716
717} // namespace KIRTREE
718
719#endif // RTREE_NODE_H
NODE * Allocate()
Allocate a new node, either from the free list or from a new page.
Definition rtree_node.h:596
static constexpr size_t NODES_PER_PAGE
Definition rtree_node.h:557
size_t MemoryUsage() const
Return approximate memory usage in bytes.
Definition rtree_node.h:655
SLAB_ALLOCATOR & operator=(SLAB_ALLOCATOR &&aOther) noexcept
Definition rtree_node.h:582
std::vector< std::unique_ptr< PAGE > > m_pages
Definition rtree_node.h:713
SLAB_ALLOCATOR & operator=(const SLAB_ALLOCATOR &)=delete
void markUnused(NODE *aNode)
Definition rtree_node.h:704
bool Owns(const NODE *aNode) const
Check if a node was allocated from one of this allocator's pages.
Definition rtree_node.h:664
SLAB_ALLOCATOR(SLAB_ALLOCATOR &&aOther) noexcept
Definition rtree_node.h:576
void Free(NODE *aNode)
Return a node to the free list for reuse.
Definition rtree_node.h:640
SLAB_ALLOCATOR(const SLAB_ALLOCATOR &)=delete
PAGE * findOwningPage(const NODE *aNode, size_t &aOffset) const
Find the page that contains aNode using address-range comparison (no UB).
Definition rtree_node.h:675
std::vector< NODE * > m_freeList
Definition rtree_node.h:714
void markUsed(NODE *aNode)
Definition rtree_node.h:695
uint64_t HilbertND2D(int aOrder, const uint32_t aCoords[NUMDIMS])
Compute Hilbert index for N-dimensional coordinates.
Definition rtree_node.h:98
uint64_t HilbertXY2D(int aOrder, uint32_t aX, uint32_t aY)
Compute a 64-bit Hilbert curve index from 2D unsigned coordinates.
Definition rtree_node.h:54
uint32_t OverlapMask2D(const ELEMTYPE *aBoundsMin0, const ELEMTYPE *aBoundsMax0, const ELEMTYPE *aBoundsMin1, const ELEMTYPE *aBoundsMax1, int aCount, const ELEMTYPE aMin[2], const ELEMTYPE aMax[2])
Compute a bitmask of which child slots overlap a 2D query rectangle.
Definition rtree_node.h:151
int64_t ChildOverlapArea(int i, const ELEMTYPE aMin[NUMDIMS], const ELEMTYPE aMax[NUMDIMS]) const
Compute the overlap area between child slot i and the given box.
Definition rtree_node.h:416
bool IsUnderflow() const
Definition rtree_node.h:334
bool IsInternal() const
Definition rtree_node.h:332
int64_t ChildEnlargement(int i, const ELEMTYPE aMin[NUMDIMS], const ELEMTYPE aMax[NUMDIMS]) const
Compute how much child slot i's area would increase if it were enlarged to include the given bounding...
Definition rtree_node.h:439
int64_t ChildPerimeter(int i) const
Compute the perimeter (or margin for 3D) of child slot i's bounding box.
Definition rtree_node.h:459
int64_t ChildArea(int i) const
Compute the area (or volume for 3D) of child slot i's bounding box.
Definition rtree_node.h:363
void GetChildBounds(int i, ELEMTYPE aMin[NUMDIMS], ELEMTYPE aMax[NUMDIMS]) const
Get the bounding box for child slot i.
Definition rtree_node.h:484
bool IsFull() const
Definition rtree_node.h:333
uint32_t ChildOverlapMask(const ELEMTYPE aMin[NUMDIMS], const ELEMTYPE aMax[NUMDIMS]) const
Bitmask of children whose bounding boxes overlap the query rectangle.
Definition rtree_node.h:391
bool ChildOverlaps(int i, const ELEMTYPE aMin[NUMDIMS], const ELEMTYPE aMax[NUMDIMS]) const
Test whether child slot i's bounding box overlaps with the given query box.
Definition rtree_node.h:376
bool IsLeaf() const
Definition rtree_node.h:331
void SetChildBounds(int i, const ELEMTYPE aMin[NUMDIMS], const ELEMTYPE aMax[NUMDIMS])
Set the bounding box for child slot i.
Definition rtree_node.h:472
void SetInsertBounds(int i, const ELEMTYPE aMin[NUMDIMS], const ELEMTYPE aMax[NUMDIMS])
Store the insertion bounding box for leaf entry i.
Definition rtree_node.h:496
void GetInsertBounds(int i, ELEMTYPE aMin[NUMDIMS], ELEMTYPE aMax[NUMDIMS]) const
Get the stored insertion bounding box for leaf entry i.
Definition rtree_node.h:508
void ComputeEnclosingBounds(ELEMTYPE aMin[NUMDIMS], ELEMTYPE aMax[NUMDIMS]) const
Compute the bounding box that encloses all children in this node.
Definition rtree_node.h:339
void RemoveChild(int i)
Remove child at slot i by swapping with last entry.
Definition rtree_node.h:520
NODE nodes[NODES_PER_PAGE]
Definition rtree_node.h:561
std::bitset< NODES_PER_PAGE > used
Definition rtree_node.h:562
VECTOR2I end
wxString result
Test unit parsing edge cases and error handling.