1
1
import copy
2
+ from collections import deque
2
3
3
4
__all__ = ['AABB' , 'AABBTree' ]
4
5
__author__ = 'Kenneth (Kip) Hart'
@@ -66,10 +67,10 @@ def __eq__(self, aabb):
66
67
return True
67
68
if (self .limits is None ) or (aabb .limits is None ):
68
69
return False
69
- if len (self ) != len (aabb ):
70
+ if len (self . limits ) != len (aabb . limits ):
70
71
return False
71
72
72
- for i , lims1 in enumerate (self ):
73
+ for i , lims1 in enumerate (self . limits ):
73
74
lims2 = aabb [i ]
74
75
if (lims1 [0 ] != lims2 [0 ]) or (lims1 [1 ] != lims2 [1 ]):
75
76
return False
@@ -98,18 +99,12 @@ def merge(cls, aabb1, aabb2):
98
99
if aabb2 .limits is None :
99
100
return cls (aabb1 .limits )
100
101
101
- if len (aabb1 ) != len (aabb2 ):
102
+ if len (aabb1 . limits ) != len (aabb2 . limits ):
102
103
e_str = 'AABBs of different dimensions: ' + str (len (aabb1 ))
103
104
e_str += ' and ' + str (len (aabb2 ))
104
105
raise ValueError (e_str )
105
106
106
- merged_limits = []
107
- n = len (aabb1 )
108
- for i in range (n ):
109
- lower = min (aabb1 [i ][0 ], aabb2 [i ][0 ])
110
- upper = max (aabb1 [i ][1 ], aabb2 [i ][1 ])
111
- merged_limits .append ((lower , upper ))
112
- return cls (merged_limits )
107
+ return cls ([_merge (* lims ) for lims in zip (aabb1 .limits , aabb2 .limits )])
113
108
114
109
@property
115
110
def perimeter (self ):
@@ -126,11 +121,11 @@ def perimeter(self):
126
121
p_n &= 2 \sum_{i=1}^n \prod_{j=1\neq i}^n l_j
127
122
128
123
"""
129
- if len (self ) == 1 :
124
+ if len (self . limits ) == 1 :
130
125
return 0
131
126
132
127
perim = 0
133
- side_lens = [ub - lb for lb , ub in self ]
128
+ side_lens = [ub - lb for lb , ub in self . limits ]
134
129
n_dim = len (side_lens )
135
130
for i in range (n_dim ):
136
131
p_edge = 1
@@ -156,7 +151,7 @@ def volume(self):
156
151
157
152
"""
158
153
vol = 1
159
- for lb , ub in self :
154
+ for lb , ub in self . limits :
160
155
vol *= ub - lb
161
156
return vol
162
157
@@ -187,12 +182,10 @@ def overlaps(self, aabb):
187
182
if (self .limits is None ) or (aabb .limits is None ):
188
183
return False
189
184
190
- for lims1 , lims2 in zip (self , aabb ):
191
- min1 , max1 = lims1
192
- min2 , max2 = lims2
193
-
194
- overlaps = (max1 >= min2 ) and (min1 <= max2 )
195
- if not overlaps :
185
+ for (min1 , max1 ), (min2 , max2 ) in zip (self .limits , aabb .limits ):
186
+ if min1 >= max2 :
187
+ return False
188
+ if min2 >= max1 :
196
189
return False
197
190
return True
198
191
@@ -219,10 +212,7 @@ def overlap_volume(self, aabb):
219
212
""" # NOQA: E501
220
213
221
214
volume = 1
222
- for lims1 , lims2 in zip (self , aabb ):
223
- min1 , max1 = lims1
224
- min2 , max2 = lims2
225
-
215
+ for (min1 , max1 ), (min2 , max2 ) in zip (self .limits , aabb .limits ):
226
216
overlap_min = max (min1 , min2 )
227
217
overlap_max = min (max1 , max2 )
228
218
if overlap_min >= overlap_max :
@@ -435,50 +425,141 @@ def add(self, aabb, value=None, method='volume'):
435
425
self .right .add (aabb , value )
436
426
self .aabb = AABB .merge (self .left .aabb , self .right .aabb )
437
427
438
- def does_overlap (self , aabb ):
428
+ def does_overlap (self , aabb , method = 'DFS' ):
439
429
"""Check for overlap
440
430
441
431
This function checks if the limits overlap any leaf nodes in the tree.
442
432
It returns true if there is an overlap.
443
433
444
434
Args:
445
435
aabb (AABB): The AABB to check.
436
+ method (str): {'DFS'|'BFS'} Method for traversing the tree.
437
+ Setting 'DFS' performs a depth-first search and 'BFS' performs
438
+ a breadth-first search. Defaults to 'DFS'.
446
439
447
440
Returns:
448
441
bool: True if overlaps with a leaf node of tree.
449
442
"""
450
- if self .is_leaf :
451
- return self .aabb .overlaps (aabb )
443
+ if method == 'DFS' :
444
+ if self .is_leaf :
445
+ return self .aabb .overlaps (aabb )
452
446
453
- left_aabb_over = self .left .aabb .overlaps (aabb )
454
- right_aabb_over = self .right .aabb .overlaps (aabb )
447
+ left_aabb_over = self .left .aabb .overlaps (aabb )
448
+ right_aabb_over = self .right .aabb .overlaps (aabb )
455
449
456
- if left_aabb_over and self .left .does_overlap (aabb ):
457
- return True
458
- if right_aabb_over and self .right .does_overlap (aabb ):
459
- return True
460
- return False
450
+ if left_aabb_over and self .left .does_overlap (aabb ):
451
+ return True
452
+ if right_aabb_over and self .right .does_overlap (aabb ):
453
+ return True
454
+ return False
455
+
456
+ if method == 'BFS' :
457
+ q = deque ()
458
+ q .append (self )
459
+ while len (q ) > 0 :
460
+ node = q .popleft ()
461
+ overlaps = node .aabb .overlaps (aabb )
462
+ if overlaps and node .is_leaf :
463
+ return True
464
+ if overlaps :
465
+ q .append (node .left )
466
+ q .append (node .right )
467
+ return False
468
+
469
+ e_str = "method should be 'DFS' or 'BFS', not " + str (method )
470
+ raise ValueError (e_str )
471
+
472
+ def overlap_aabbs (self , aabb , method = 'DFS' ):
473
+ """Get overlapping AABBs
474
+
475
+ This function gets each overlapping AABB.
476
+
477
+ Args:
478
+ aabb (AABB): The AABB to check.
479
+ method (str): {'DFS'|'BFS'} Method for traversing the tree.
480
+ Setting 'DFS' performs a depth-first search and 'BFS' performs
481
+ a breadth-first search. Defaults to 'DFS'.
461
482
462
- def overlap_values (self , aabb ):
483
+ Returns:
484
+ list: AABB objects in AABBTree that overlap with the input.
485
+ """
486
+ aabbs = []
487
+
488
+ if method == 'DFS' :
489
+ is_leaf = self .is_leaf
490
+ if is_leaf and self .does_overlap (aabb ):
491
+ aabbs .append (self .aabb )
492
+ elif is_leaf :
493
+ pass
494
+ else :
495
+ if self .left .aabb .overlaps (aabb ):
496
+ aabbs .extend (self .left .overlap_aabbs (aabb ))
497
+
498
+ if self .right .aabb .overlaps (aabb ):
499
+ aabbs .extend (self .right .overlap_aabbs (aabb ))
500
+ elif method == 'BFS' :
501
+ q = deque ()
502
+ q .append (self )
503
+ while len (q ) > 0 :
504
+ node = q .popleft ()
505
+ if node .aabb .overlaps (aabb ):
506
+ if node .is_leaf :
507
+ aabbs .append (node .aabb )
508
+ else :
509
+ q .append (node .left )
510
+ q .append (node .right )
511
+ else :
512
+ e_str = "method should be 'DFS' or 'BFS', not " + str (method )
513
+ raise ValueError (e_str )
514
+ return aabbs
515
+
516
+ def overlap_values (self , aabb , method = 'DFS' ):
463
517
"""Get values of overlapping AABBs
464
518
465
519
This function gets the value field of each overlapping AABB.
466
520
467
521
Args:
468
522
aabb (AABB): The AABB to check.
523
+ method (str): {'DFS'|'BFS'} Method for traversing the tree.
524
+ Setting 'DFS' performs a depth-first search and 'BFS' performs
525
+ a breadth-first search. Defaults to 'DFS'.
469
526
470
527
Returns:
471
528
list: Value fields of each node that overlaps.
472
529
"""
473
530
values = []
474
- if self .is_leaf and self .does_overlap (aabb ):
475
- values .append (self .value )
476
- elif self .is_leaf :
477
- pass
478
- else :
479
- if self .left .aabb .overlaps (aabb ):
480
- values .extend (self .left .overlap_values (aabb ))
481
531
482
- if self .right .aabb .overlaps (aabb ):
483
- values .extend (self .right .overlap_values (aabb ))
532
+ if method == 'DFS' :
533
+ is_leaf = self .is_leaf
534
+ if is_leaf and self .does_overlap (aabb ):
535
+ values .append (self .value )
536
+ elif is_leaf :
537
+ pass
538
+ else :
539
+ if self .left .aabb .overlaps (aabb ):
540
+ values .extend (self .left .overlap_values (aabb ))
541
+
542
+ if self .right .aabb .overlaps (aabb ):
543
+ values .extend (self .right .overlap_values (aabb ))
544
+ elif method == 'BFS' :
545
+ q = deque ()
546
+ q .append (self )
547
+ while len (q ) > 0 :
548
+ node = q .popleft ()
549
+ if node .aabb .overlaps (aabb ):
550
+ if node .is_leaf :
551
+ values .append (node .value )
552
+ else :
553
+ q .append (node .left )
554
+ q .append (node .right )
555
+ else :
556
+ e_str = "method should be 'DFS' or 'BFS', not " + str (method )
557
+ raise ValueError (e_str )
484
558
return values
559
+
560
+
561
+ def _merge (lims1 , lims2 ):
562
+ lb = min (lims1 [0 ], lims2 [0 ])
563
+ ub = max (lims1 [1 ], lims2 [1 ])
564
+
565
+ return (lb , ub )
0 commit comments