如何有效地将非点对象插入到四叉树中
How to efficiently insert non-point objects into a quadtree
我正在尝试在 python 中创建一个四叉树结构来检测多边形之间的碰撞,我已经取得了很大进展(请参阅 post 的末尾)。但是,我意识到这种结构只适用于 点 ,因为我要根据对象的中心决定将对象放在哪片叶子中。
所以我需要弄清楚如何修改这个四叉树,以便我可以检测到区域(比如一个圆圈!)的碰撞。
我可以考虑几种不同的方法:
- 只将对象放在它们完全填充的节点中——这似乎效率低下,因为四叉树的全部意义在于将对象放在叶子中以便于 search/retrieval 并减少我的对象数量检索。
- 将对象放入与其重叠的每一片叶子中——这似乎是一个很好的解决方案,但我不完全确定如何处理重叠决策(我想知道这是否效率低下) .
- ??? -- 有没有更好的解决方案?
选项#2(将对象放置在多个节点中),我觉得最好在对象周围绘制一个边界矩形,然后根据对象的范围来决定将其放置在哪片叶子上in -- 但这需要有效地插入每个对象 4 次(每个角一个),这似乎是解决问题的一种低效方法。
有更好的建议吗?
class Quadtree(object):
"""
A simple Quadtree implementation. This works with any child object that has a getPosition() method that returns a list or tuple.
"""
def __init__(self, depth, min_x, min_y, max_x, max_y):
self.mDepth = depth
self.mMaxDepth = 4
self.mMinX = min_x
self.mMaxX = max_x
self.mMidX = (max_x - min_x) / 2.0
self.mMinY = min_y
self.mMaxY = max_y
self.mMidY = (max_y - min_y) / 2.0
self.mHasChildren = False
self.mMaxChildren = 8
self.mChildren = []
self.mSubtrees = []
def insert(self, newChild):
"""
Insert an object into the tree. Returns True if the insert was successful, False otherwise.
"""
if self.mSubtrees:
#if subtrees exist, add the child to the subtrees
index = getIndex(newChild)
if index != -1:
self.mSubtrees[index].insert(newChild)
return True
#if no subtrees exist, add the child to the child list.
self.mChildren.append(newChild)
#and then check if we need to split the tree
#if there are more than the max children, and we haven't maxed out the tree depth, and there are no subtrees yet
if len(self.mChildren) > self.mMaxChildren and self.mDepth < self.mMaxDepth and not self.mSubtrees:
split()
for child in self.mChildren:
index = getIndex(child)
if index != -1:
self.mSubtrees[index].insert(child)
return True
return False
def retrieveNeighbors(self, targetChild):
index = getIndex(targetChild)
if index != -1 and self.mSubtrees:
return self.mSubtrees[index].retrieve(targetChild)
return self.mChildren
def getIndex(self, child):
"""
Returns the index of the node that the object belongs to. Returns -1 if the object does not exist in the tree.
"""
index = -1
childPosition = child.getPosition()
#check if it fits in the top or bottom
isInTopQuadrant = childPosition[1] > self.mMidY and childPositoin[1] < self.mMaxY
#check if it fits left
if childPosition[0] < self.mMidX and childPosition[0] > self.mMinX:
if isInTopQuadrant:
index = 1
else:
index = 2
#check if it fits right
if childPosition[0] > self.mMidX and childPosition[0] < self.mMidX:
if isInTopQuadrant:
index = 0
else:
index = 3
return index
def split(self):
"""
Split the trees into four subtrees.
"""
#top right
self.mSubtrees.append(Quadtree(depth + 1, self.mMidX, self.mMidY, self.mMaxX, self.mMaxY))
#top left
self.mSubtrees.append(Quadtree(depth + 1, self.mMinX, self.mMidY, self.mMidX, self.mMaxY))
#bottom left
self.mSubtrees.append(Quadtree(depth + 1, self.mMinX, self.mMinY, self.mMidX, self.mMidY))
#bottom right
self.mSubtrees.append(Quadtree(depth + 1, self.mMidX, self.mMinY, self.mMaxX, self.mMidY))
def clear(self):
"""
Clears the quadtree of children, and all subtrees of children.
"""
self.mChildren[:] = []
self.mHasChildren = False
for tree in range(0,4):
if self.mSubtrees[tree].mHasChildren:
self.mSubtrees[tree].clear()
到目前为止,我发现的最佳方法是修改 _get_index()
以检查粒子的整个大小——如果它不完全适合子树,那么它将成为子树的子树父节点:
def _get_index(self, child):
"""
Returns the index of the node that the object belongs to. Returns -1 if the object does not exist in the tree.
"""
index = -1
child_position = child.get_position()
child_radius = child.get_radius()
if len(child_position) != 2:
print "Quadtree is only designed to handle two-dimensional objects! Object will not be added."
return index
#check if it fits in the top or bottom
is_in_top_quadrant = child_position[1] - child_radius > self.mid_y and child_position[1] + child_radius < self.max_y
#check if it fits left
if child_position[0] + child_radius < self.mid_x and child_position[0] - child_radius > self.min_x:
if is_in_top_quadrant:
index = 1
else:
index = 2
#check if it fits right
if child_position[0] - child_radius > self.mid_x and child_position[0] + child_radius < self.mid_x:
if is_in_top_quadrant:
index = 0
else:
index = 3
return index
然后retrieve_neighbors()
可以遍历树并添加它经过的每个节点的子节点,一直向下到叶节点。
我正在尝试在 python 中创建一个四叉树结构来检测多边形之间的碰撞,我已经取得了很大进展(请参阅 post 的末尾)。但是,我意识到这种结构只适用于 点 ,因为我要根据对象的中心决定将对象放在哪片叶子中。
所以我需要弄清楚如何修改这个四叉树,以便我可以检测到区域(比如一个圆圈!)的碰撞。
我可以考虑几种不同的方法:
- 只将对象放在它们完全填充的节点中——这似乎效率低下,因为四叉树的全部意义在于将对象放在叶子中以便于 search/retrieval 并减少我的对象数量检索。
- 将对象放入与其重叠的每一片叶子中——这似乎是一个很好的解决方案,但我不完全确定如何处理重叠决策(我想知道这是否效率低下) .
- ??? -- 有没有更好的解决方案?
选项#2(将对象放置在多个节点中),我觉得最好在对象周围绘制一个边界矩形,然后根据对象的范围来决定将其放置在哪片叶子上in -- 但这需要有效地插入每个对象 4 次(每个角一个),这似乎是解决问题的一种低效方法。
有更好的建议吗?
class Quadtree(object):
"""
A simple Quadtree implementation. This works with any child object that has a getPosition() method that returns a list or tuple.
"""
def __init__(self, depth, min_x, min_y, max_x, max_y):
self.mDepth = depth
self.mMaxDepth = 4
self.mMinX = min_x
self.mMaxX = max_x
self.mMidX = (max_x - min_x) / 2.0
self.mMinY = min_y
self.mMaxY = max_y
self.mMidY = (max_y - min_y) / 2.0
self.mHasChildren = False
self.mMaxChildren = 8
self.mChildren = []
self.mSubtrees = []
def insert(self, newChild):
"""
Insert an object into the tree. Returns True if the insert was successful, False otherwise.
"""
if self.mSubtrees:
#if subtrees exist, add the child to the subtrees
index = getIndex(newChild)
if index != -1:
self.mSubtrees[index].insert(newChild)
return True
#if no subtrees exist, add the child to the child list.
self.mChildren.append(newChild)
#and then check if we need to split the tree
#if there are more than the max children, and we haven't maxed out the tree depth, and there are no subtrees yet
if len(self.mChildren) > self.mMaxChildren and self.mDepth < self.mMaxDepth and not self.mSubtrees:
split()
for child in self.mChildren:
index = getIndex(child)
if index != -1:
self.mSubtrees[index].insert(child)
return True
return False
def retrieveNeighbors(self, targetChild):
index = getIndex(targetChild)
if index != -1 and self.mSubtrees:
return self.mSubtrees[index].retrieve(targetChild)
return self.mChildren
def getIndex(self, child):
"""
Returns the index of the node that the object belongs to. Returns -1 if the object does not exist in the tree.
"""
index = -1
childPosition = child.getPosition()
#check if it fits in the top or bottom
isInTopQuadrant = childPosition[1] > self.mMidY and childPositoin[1] < self.mMaxY
#check if it fits left
if childPosition[0] < self.mMidX and childPosition[0] > self.mMinX:
if isInTopQuadrant:
index = 1
else:
index = 2
#check if it fits right
if childPosition[0] > self.mMidX and childPosition[0] < self.mMidX:
if isInTopQuadrant:
index = 0
else:
index = 3
return index
def split(self):
"""
Split the trees into four subtrees.
"""
#top right
self.mSubtrees.append(Quadtree(depth + 1, self.mMidX, self.mMidY, self.mMaxX, self.mMaxY))
#top left
self.mSubtrees.append(Quadtree(depth + 1, self.mMinX, self.mMidY, self.mMidX, self.mMaxY))
#bottom left
self.mSubtrees.append(Quadtree(depth + 1, self.mMinX, self.mMinY, self.mMidX, self.mMidY))
#bottom right
self.mSubtrees.append(Quadtree(depth + 1, self.mMidX, self.mMinY, self.mMaxX, self.mMidY))
def clear(self):
"""
Clears the quadtree of children, and all subtrees of children.
"""
self.mChildren[:] = []
self.mHasChildren = False
for tree in range(0,4):
if self.mSubtrees[tree].mHasChildren:
self.mSubtrees[tree].clear()
到目前为止,我发现的最佳方法是修改 _get_index()
以检查粒子的整个大小——如果它不完全适合子树,那么它将成为子树的子树父节点:
def _get_index(self, child):
"""
Returns the index of the node that the object belongs to. Returns -1 if the object does not exist in the tree.
"""
index = -1
child_position = child.get_position()
child_radius = child.get_radius()
if len(child_position) != 2:
print "Quadtree is only designed to handle two-dimensional objects! Object will not be added."
return index
#check if it fits in the top or bottom
is_in_top_quadrant = child_position[1] - child_radius > self.mid_y and child_position[1] + child_radius < self.max_y
#check if it fits left
if child_position[0] + child_radius < self.mid_x and child_position[0] - child_radius > self.min_x:
if is_in_top_quadrant:
index = 1
else:
index = 2
#check if it fits right
if child_position[0] - child_radius > self.mid_x and child_position[0] + child_radius < self.mid_x:
if is_in_top_quadrant:
index = 0
else:
index = 3
return index
然后retrieve_neighbors()
可以遍历树并添加它经过的每个节点的子节点,一直向下到叶节点。