AOI十字链表python简单实现,仅用作学习,理解基本原理
AOI一般是C++去实现,本文章python写仅用来入门了解
场景内维护两个实体链表
list_x和list_y
所有实体根据x坐标大小排序,在list_x上
所有实体根据y坐标大小排序,在list_y上
实体位置改变时候,需要改变实体在list_x,list_x的位置
实体的AOI范围, 根据AOI半径 在list_x,list_x上遍历即可
十字链表发在计算AOI范围内对象时,计算量非常小,且跟空间大小无关,因此省去大量无效的遍历过程。十字链表法也有一些需要注意的地方,因为在两条链表中都必须按照大小顺序进行排列,因此对象在不停的移动,会带来比较大量的计算。
python版本:2.7
# coding: utf-8
class Entity(object):
def __init__(self, eid, x, y):
self.id = eid
self.x = x
self.y = y
self.radius = 5 # AOI半径
def __str__(self):
return "<{0},{1}-{2}>".format(self.id, self.x, self.y)
def enter(self, pobj):
print("{0} enter {1} view".format(pobj, self))
def leave(self, pobj):
print("{0} leave {1} view".format(pobj, self))
def move(self, pobj):
print("{0} move in {1} view".format(pobj, self))
class Scene(object):
def __init__(self):
self.map_entity = {}
self.list_x = [] # [实例ID] 实际应用链表 + 跳跃表
self.list_y = []
def __str__(self):
return "<list_x: {0}, list_y: {1}, entities: {2}>".format(self.list_x, self.list_y, self.map_entity.keys())
def add_by_entity(self, entity):
# 添加一个新的对象
self.map_entity[entity.id] = entity
pos = len(self.list_x)
for i, eid in enumerate(self.list_x):
x = self.map_entity[eid].x
if entity.x < x:
pos = i
break
self.list_x.insert(pos, entity.id)
pos = len(self.list_y)
for j, eid in enumerate(self.list_y):
y = self.map_entity[eid].y
if entity.y < y:
pos = j
break
self.list_y.insert(pos, entity.id)
return entity
def remove(self, eid):
entity = self.map_entity.pop(eid)
for i, eid in enumerate(self.list_x):
if eid == entity.id:
del self.list_x[i]
break
for j, eid in enumerate(self.list_y):
if eid == entity.id:
del self.list_y[j]
break
def get_range_entities(self, entity):
# 获取指定对象的AOI对象列表
ret_set = set([])
for ob_id in self.list_x:
ob = self.map_entity[ob_id]
if abs(ob.x - entity.x) <= entity.radius:
if ob != entity:
ret_set.add(ob)
for ob_id in self.list_y:
ob = self.map_entity[ob_id]
if abs(ob.y - entity.y) <= entity.radius:
if ob not in ret_set and entity != ob:
ret_set.add(ob)
else:
break
return list(ret_set)
def update_pos(self, eid, x, y):
# 更新对象eid对象的坐标为(x, y)
if eid not in self.map_entity:
# 首次进入
entity = Entity(eid, x, y)
entity = self.add_by_entity(entity)
entities = self.get_range_entities(entity)
for ob in entities:
ob.enter(entity)
else:
entity = self.map_entity[eid]
if x < 0 or y < 0:
# 坐标小于0时为离开场景
obs = self.get_range_entities(entity)
self.remove(eid)
for ob in obs:
ob.leave(entity)
else:
old_entities = self.get_range_entities(entity)
self.remove(eid)
entity.x = x
entity.y = y
self.add_by_entity(entity)
new_entities = self.get_range_entities(entity)
# enter处理
enter_obs = [ob for ob in new_entities if ob not in old_entities]
for ob in enter_obs:
ob.enter(entity)
# leave处理
leave_obs = [ob for ob in old_entities if ob not in new_entities]
for ob in leave_obs:
ob.leave(entity)
# move处理
move_obs = [ob for ob in old_entities if ob in new_entities]
for ob in move_obs:
ob.move(entity)
def test():
scene = Scene()
scene.update_pos(1, 1, 2)
scene.update_pos(2, 5, 9)
scene.update_pos(3, 10, 10)
pobj1 = scene.map_entity[1]
pobj2 = scene.map_entity[2]
pobj3 = scene.map_entity[3]
print scene
print("\nscene.update_pos(1, 7, 7), pobj1.range: ")
scene.update_pos(1, 7, 7)
for ob in scene.get_range_entities(pobj1):
print(ob)
print("\nscene.update_pos(2, 1, 2)), pobj2.range:")
scene.update_pos(2, 1, 2)
for ob in scene.get_range_entities(pobj2):
print(ob)
print("\nscene.update_pos(3, 5, 5), pobj3.range: ")
scene.update_pos(3, 5, 5)
for ob in scene.get_range_entities(pobj3):
print(ob)
test()
运行结果:
<2,5-9> enter <1,1-2> view
<3,10-10> enter <2,5-9> view
<list_x: [1, 2, 3], list_y: [1, 2, 3], entities: [1,2,3]>
scene.update_pos(1, 7, 7), pobj1.range:
<1,7-7> enter <3,10-10> view
<1,7-7> move in <2,5-9> view
<3,10-10>
<2,5-9>
scene.update_pos(2, 1, 2)), pobj2.range:
<2,1-2> leave <3,10-10> view
<2,1-2> move in <1,7-7> view
<1,7-7>
scene.update_pos(3, 5, 5), pobj3.range:
<3,5-5> enter <2,1-2> view
<3,5-5> move in <1,7-7> view
<1,7-7>
<2,1-2>
规范版本应该双向列表维护,实体数量较多时候,应该用跳跃表去维护,类的设计如下
参考:https://www.cnblogs.com/rond/p/6114919.html
class Entity(object):
def __init__(self):
self.id = 0
self.x = 0
self.y = 0
self.x_prev = None # 双向列表
self.x_next = None
self.y_prev = None
self.y_next = None
class Scene(object):
def __init__(self):
self.list_x_head = None
self.list_x_tail = None
self.list_y_head = None
self.list_y_tail = None