Source code for quadtree
from rect import Rect
[docs]class QuadTree(object):
""" QuadTree data structure of simulated objects
"""
#def __init__(self, xywh):
# self.rect = Rect(xywh)
def __init__(self, items=None, depth=8, bounding_rect=None):
"""Creates a quad-tree.
@param items:
A sequence of items to store in the quad-tree.
Note that these items must be of SimObject.
@param depth:
The maximum recursion depth.
@param bounding_rect:
The bounding rectangle of all of the items in the quad-tree.
Type of Rect or (x,y,w,h) of the rectangle
For internal use only.
"""
# The sub-quadrants are empty to start with.
self.nw = self.ne = self.se = self.sw = None
self.depth = depth
# Find this quadrant's centre.
if bounding_rect:
bounding_rect = Rect(bounding_rect)
else:
# If there isn't a bounding rect, then calculate it from the items.
if items:
bounding_rect = Rect(items[0].get_bounding_rect())
for item in items[1:]:
bounding_rect.add(Rect(item.get_bounding_rect()))
else:
# in case there are no items, assume a big rect (100x100 meters)
bounding_rect = Rect((0.0,0.0,100.0,100.0))
self.rect = bounding_rect
self.items = []
# Insert items
self.insert_items(items)
#print("QuadTree:", self, self.items)
[docs] def insert_items(self, items):
""" Insert a list of SimObject items
"""
# nothing to do if the list is empty or None
if not items:
return
rect_items = [(item, Rect(item.get_bounding_rect()))
for item in items]
# If we've reached the maximum depth then insert all items into
# this quadrant.
if self.depth <= 0 or not items:
self.items += rect_items
return
cx, cy = self.rect.center
nw_items = []
ne_items = []
se_items = []
sw_items = []
for item, item_rect in rect_items:
# Which of the sub-quadrants does the item overlap?
in_nw = item_rect.left <= cx and item_rect.top >= cy
in_sw = item_rect.left <= cx and item_rect.bottom <= cy
in_ne = item_rect.right >= cx and item_rect.top >= cy
in_se = item_rect.right >= cx and item_rect.bottom <= cy
# If it overlaps all 4 quadrants then insert it at the current
# depth, otherwise append it to a list to be inserted under every
# quadrant that it overlaps.
if in_nw and in_ne and in_se and in_sw:
self.items.append((item, item_rect))
else:
if in_nw: nw_items.append(item)
if in_ne: ne_items.append(item)
if in_se: se_items.append(item)
if in_sw: sw_items.append(item)
# Create the sub-quadrants, recursively.
if nw_items:
self.nw = QuadTree(nw_items, self.depth-1,
(self.rect.left, cy,
self.rect.width/2, self.rect.height/2))
if ne_items:
self.ne = QuadTree(ne_items, self.depth-1,
(cx, cy,
self.rect.width/2, self.rect.height/2))
if se_items:
self.se = QuadTree(se_items, self.depth-1,
(cx, self.rect.bottom,
self.rect.width/2, self.rect.height/2))
if sw_items:
self.sw = QuadTree(sw_items, self.depth-1,
(self.rect.left, self.rect.bottom,
self.rect.width/2, self.rect.height/2))
[docs] def find_items(self, xywh):
"""Returns the items that overlap a bounding rectangle.
Returns the set of all items in the quad-tree that overlap with a
bounding rectangle.
@param xywh:
The bounding rectangle being tested against the quad-tree.
"""
rect = Rect(xywh)
def overlaps(other):
return rect.right >= other.left and rect.left <= other.right and \
rect.bottom <= other.top and rect.top >= other.bottom
# Find the hits at the current level.
hits = [item for item, item_rect in self.items
if overlaps(item_rect)]
# Recursively check the lower quadrants.
cx, cy = self.rect.center
if self.nw and rect.left <= cx and rect.top >= cy:
hits += self.nw.find_items(rect)
if self.sw and rect.left <= cx and rect.bottom <= cy:
hits += self.sw.find_items(rect)
if self.ne and rect.right >= cx and rect.top >= cy:
hits += self.ne.find_items(rect)
if self.se and rect.right >= cx and rect.bottom <= cy:
hits += self.se.find_items(rect)
return set(hits)
def __repr__(self):
return "<%s %s>" % (self.__class__.__name__, self.rect)