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