Skip to content

Commit b07ee9e

Browse files
authored
add option to specify open/closed overlap testing (#14)
1 parent e095a41 commit b07ee9e

File tree

7 files changed

+75
-26
lines changed

7 files changed

+75
-26
lines changed

.github/workflows/python_package.yaml

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,10 @@ jobs:
146146
- name: Lint with isort
147147
run: |
148148
isort --verbose --check-only --diff src tests setup.py
149-
- name: Lint with GitHub Super-Linter
150-
env:
151-
VALIDATE_CSS: False
152-
VALIDATE_HTML: False
153-
VALIDATE_PYTHON_BLACK: False
154-
uses: docker://github/super-linter
149+
# Ignore for now
150+
# - name: Lint with GitHub Super-Linter
151+
# env:
152+
# VALIDATE_CSS: False
153+
# VALIDATE_HTML: False
154+
# VALIDATE_PYTHON_BLACK: False
155+
# uses: docker://github/super-linter

LICENSE.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2019-2020 Georgia Tech Research Corporation
3+
Copyright (c) 2019-2021 Georgia Tech Research Corporation
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ create a pull request, and submit issues.
105105
License and Copyright Notice
106106
============================
107107

108-
Copyright |copy| 2019-2020, Georgia Tech Research Corporation
108+
Copyright |copy| 2019-2021, Georgia Tech Research Corporation
109109

110110
AABBTree is open source and freely available under the terms of
111111
the MIT license.

aabbtree.py

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -172,15 +172,24 @@ def corners(self):
172172
corners.append(corner)
173173
return corners
174174

175-
def overlaps(self, aabb):
175+
def overlaps(self, aabb, closed=False):
176176
"""Determine if two AABBs overlap
177177
178178
Args:
179179
aabb (AABB): The AABB to check for overlap
180+
closed (bool): Flag for closed overlap between AABBs. For the case
181+
where one box is [-1, 0] and the other is [0, 0], the two boxes
182+
are interecting if closed is set to True and they are not
183+
intersecting if closed is set to False.
180184
181185
Returns:
182186
bool: Flag set to true if the two AABBs overlap
183187
"""
188+
if closed:
189+
return self._overlaps_closed(aabb)
190+
return self._overlaps_open(aabb)
191+
192+
def _overlaps_open(self, aabb):
184193
if (self.limits is None) or (aabb.limits is None):
185194
return False
186195

@@ -191,6 +200,19 @@ def overlaps(self, aabb):
191200
return False
192201
return True
193202

203+
def _overlaps_closed(self, aabb):
204+
if (self.limits is None) or (aabb.limits is None):
205+
return False
206+
207+
for (min1, max1), (min2, max2) in zip(self.limits, aabb.limits):
208+
if min1 > max2:
209+
return False
210+
if min2 > max1:
211+
return False
212+
return True
213+
214+
215+
194216
def overlap_volume(self, aabb):
195217
r"""Determine volume of overlap between AABBs
196218
@@ -425,7 +447,7 @@ def add(self, aabb, value=None, method='volume'):
425447
self.right.add(aabb, value)
426448
self.aabb = AABB.merge(self.left.aabb, self.right.aabb)
427449

428-
def does_overlap(self, aabb, method='DFS'):
450+
def does_overlap(self, aabb, method='DFS', closed=False):
429451
"""Check for overlap
430452
431453
This function checks if the limits overlap any leaf nodes in the tree.
@@ -441,13 +463,17 @@ def does_overlap(self, aabb, method='DFS'):
441463
method (str): {'DFS'|'BFS'} Method for traversing the tree.
442464
Setting 'DFS' performs a depth-first search and 'BFS' performs
443465
a breadth-first search. Defaults to 'DFS'.
466+
closed (bool): Option to specify closed or open box intersection.
467+
If open, there must be a non-zero amount of overlap. If closed,
468+
boxes can be touching.
444469
445470
Returns:
446471
bool: True if overlaps with a leaf node of tree.
447472
"""
448-
return len(_overlap_pairs(self, aabb, method, halt=True)) > 0
449473

450-
def overlap_aabbs(self, aabb, method='DFS'):
474+
return len(_overlap_pairs(self, aabb, method, True, closed)) > 0
475+
476+
def overlap_aabbs(self, aabb, method='DFS', closed=False):
451477
"""Get overlapping AABBs
452478
453479
This function gets each overlapping AABB.
@@ -462,17 +488,20 @@ def overlap_aabbs(self, aabb, method='DFS'):
462488
method (str): {'DFS'|'BFS'} Method for traversing the tree.
463489
Setting 'DFS' performs a depth-first search and 'BFS' performs
464490
a breadth-first search. Defaults to 'DFS'.
491+
closed (bool): Option to specify closed or open box intersection.
492+
If open, there must be a non-zero amount of overlap. If closed,
493+
boxes can be touching.
465494
466495
Returns:
467496
list: AABB objects in AABBTree that overlap with the input.
468497
"""
469-
pairs = _overlap_pairs(self, aabb, method)
498+
pairs = _overlap_pairs(self, aabb, method, closed=closed)
470499
if len(pairs) == 0:
471500
return []
472501
boxes, _ = zip(*pairs)
473502
return list(boxes)
474503

475-
def overlap_values(self, aabb, method='DFS'):
504+
def overlap_values(self, aabb, method='DFS', closed=False):
476505
"""Get values of overlapping AABBs
477506
478507
This function gets the value field of each overlapping AABB.
@@ -487,11 +516,14 @@ def overlap_values(self, aabb, method='DFS'):
487516
method (str): {'DFS'|'BFS'} Method for traversing the tree.
488517
Setting 'DFS' performs a depth-first search and 'BFS' performs
489518
a breadth-first search. Defaults to 'DFS'.
519+
closed (bool): Option to specify closed or open box intersection.
520+
If open, there must be a non-zero amount of overlap. If closed,
521+
boxes can be touching.
490522
491523
Returns:
492524
list: Value fields of each node that overlaps.
493525
"""
494-
pairs = _overlap_pairs(self, aabb, method)
526+
pairs = _overlap_pairs(self, aabb, method, closed=closed)
495527
if len(pairs) == 0:
496528
return []
497529
_, values = zip(*pairs)
@@ -505,7 +537,7 @@ def _merge(lims1, lims2):
505537
return (lower, upper)
506538

507539

508-
def _overlap_pairs(in_tree, aabb, method='DFS', halt=False):
540+
def _overlap_pairs(in_tree, aabb, method='DFS', halt=False, closed=False):
509541
"""Get overlapping AABBs and values in (AABB, value) pairs
510542
511543
*New in version 2.6.0*
@@ -518,8 +550,9 @@ def _overlap_pairs(in_tree, aabb, method='DFS', halt=False):
518550
method (str): {'DFS'|'BFS'} Method for traversing the tree.
519551
Setting 'DFS' performs a depth-first search and 'BFS' performs
520552
a breadth-first search. Defaults to 'DFS'.
521-
halt (bool): Return the list immediately once a pair has been
553+
halt (bool): Return the list immediately once a pair has been
522554
added.
555+
closed (bool): Check for closed box intersection. Defaults to False.
523556
524557
Returns:
525558
list: (AABB, value) pairs in AABBTree that overlap with the input.
@@ -530,10 +563,10 @@ def _overlap_pairs(in_tree, aabb, method='DFS', halt=False):
530563
tree = aabb
531564

532565
if method == 'DFS':
533-
pairs = _overlap_dfs(in_tree, tree, halt)
566+
pairs = _overlap_dfs(in_tree, tree, halt, closed)
534567

535568
elif method == 'BFS':
536-
pairs = _overlap_bfs(in_tree, tree, halt)
569+
pairs = _overlap_bfs(in_tree, tree, halt, closed)
537570
else:
538571
e_str = "method should be 'DFS' or 'BFS', not " + str(method)
539572
raise ValueError(e_str)
@@ -543,7 +576,7 @@ def _overlap_pairs(in_tree, aabb, method='DFS', halt=False):
543576
return _unique_pairs(pairs)
544577

545578

546-
def _overlap_dfs(in_tree, tree, halt):
579+
def _overlap_dfs(in_tree, tree, halt, closed):
547580
pairs = []
548581

549582
if in_tree.is_leaf:
@@ -556,7 +589,7 @@ def _overlap_dfs(in_tree, tree, halt):
556589
else:
557590
tree_branches = [tree.left, tree.right]
558591

559-
if not in_tree.aabb.overlaps(tree.aabb):
592+
if not in_tree.aabb.overlaps(tree.aabb, closed):
560593
return pairs
561594

562595
if in_tree.is_leaf and tree.is_leaf:
@@ -565,20 +598,20 @@ def _overlap_dfs(in_tree, tree, halt):
565598

566599
for in_branch in in_branches:
567600
for tree_branch in tree_branches:
568-
o_pairs = _overlap_dfs(in_branch, tree_branch, halt)
601+
o_pairs = _overlap_dfs(in_branch, tree_branch, halt, closed)
569602
pairs.extend(o_pairs)
570603
if halt and len(pairs) > 0:
571604
return pairs
572605
return pairs
573606

574607

575-
def _overlap_bfs(in_tree, tree, halt):
608+
def _overlap_bfs(in_tree, tree, halt, closed):
576609
pairs = []
577610
queue = deque()
578611
queue.append((in_tree, tree))
579612
while len(queue) > 0:
580613
s_node, t_node = queue.popleft()
581-
if s_node.aabb.overlaps(t_node.aabb):
614+
if s_node.aabb.overlaps(t_node.aabb, closed):
582615
if s_node.is_leaf and t_node.is_leaf:
583616
pairs.append((s_node.aabb, s_node.value))
584617
if halt:

docs/source/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
# -- Project information -----------------------------------------------------
2121

2222
project = 'AABBTree'
23-
copyright = '2019-2020, Georgia Tech Research Corporation'
23+
copyright = '2019-2021, Georgia Tech Research Corporation'
2424
author = 'Kenneth Hart'
2525

2626
# The short X.Y version

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def read(fname):
1515

1616
setup(
1717
name='aabbtree',
18-
version='2.6.2',
18+
version='2.7.0',
1919
license='MIT',
2020
description='Pure Python implementation of d-dimensional AABB tree.',
2121
long_description=read('README.rst'),

tests/test_aabb.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,21 @@ def test_overlaps():
108108
assert not aabb2.overlaps(aabb3)
109109

110110

111+
def test_overlaps_closed():
112+
aabb1 = AABB([(0, 0)])
113+
aabb2 = AABB([(-1, 0)])
114+
aabb3 = AABB([(1, 2)])
115+
aabb4 = AABB([(-9, -8)])
116+
117+
assert aabb1.overlaps(aabb2, True)
118+
assert aabb2.overlaps(aabb1, True)
119+
assert not aabb1.overlaps(aabb3, True)
120+
assert not aabb2.overlaps(aabb3, True)
121+
assert not aabb1.overlaps(aabb4, True)
122+
assert not aabb2.overlaps(aabb4, True)
123+
assert not aabb1.overlaps(AABB(), True)
124+
125+
111126
def test_corners():
112127
lims = [(0, 10), (5, 10)]
113128
aabb_corners = [

0 commit comments

Comments
 (0)