Spatial hash to use number instead of table arguments

This commit is contained in:
Matthias Richter 2012-05-30 15:27:09 +02:00
parent ce4b8011da
commit e03ea9b0a2
2 changed files with 66 additions and 73 deletions

View File

@ -92,7 +92,7 @@ local function new_shape(self, shape)
self._current_shape_id = self._current_shape_id + 1
self._active_shapes[self._current_shape_id] = shape
self._shape_ids[shape] = self._current_shape_id
self._hash:insert(shape, {x=x1,y=y1}, {x=x2,y=y2})
self._hash:insert(shape, x1,y1, x2,y2)
shape._groups = {}
local hash = self._hash
@ -101,24 +101,24 @@ local function new_shape(self, shape)
local x1,y1,x2,y2 = self:bbox()
move(self, ...)
local x3,y3,x4,y4 = self:bbox()
hash:update(self, {x=x1,y=y1}, {x=x2,y=y2}, {x=x3,y=y3}, {x=x4,y=y4})
hash:update(self, x1,y1, x2,y2, x3,y3, x4,y4)
end
function shape:rotate(...)
local x1,y1,x2,y2 = self:bbox()
rotate(self, ...)
local x3,y3,x4,y4 = self:bbox()
hash:update(self, {x=x1,y=y1}, {x=x2,y=y2}, {x=x3,y=y3}, {x=x4,y=y4})
hash:update(self, x1,y1, x2,y2, x3,y3, x4,y4)
end
function shape:neighbors()
local x1,y1, x2,y2 = self:bbox()
return pairs(hash:getNeighbors(self, {x=x1,y=y1}, {x=x2,y=y2}))
return pairs(hash:getNeighbors(self, x1,y1, x2,y2))
end
function shape:_removeFromHash()
local x1,y1, x2,y2 = self:bbox()
hash:remove(shape, {x=x1,y=y1}, {x=x2,y=y2})
hash:remove(shape, x1,y1, x2,y2)
end
return shape
@ -199,7 +199,7 @@ end
-- get list of shapes at point (x,y)
function HC:shapesAt(x, y)
local shapes = {}
for s in pairs(self._hash:cell{x=x,y=y}) do
for s in pairs(self._hash:cellAt(x,y)) do
if s:contains(x,y) then
shapes[#shapes+1] = s
end

View File

@ -33,108 +33,101 @@ if not (common and common.class and common.instance) then
require(_PACKAGE .. '.class')
end
-- transparent cell accessor methods
-- cells = {[0] = {[0] = <>, [1] = <>, ... }, [1] = {...}, ...}
local cells_meta = {}
function cells_meta.__newindex(tbl, key, val)
local cell = rawget(tbl, key.x)
if not cell then
rawset(tbl, key.x, {[key.y] = val})
else
rawset(cell, key.y, val)
end
end
function cells_meta.__index(tbl, key)
local cell = rawget(tbl, key.x)
if not cell then
local ret = setmetatable({}, {__mode = "kv"})
cell = {[key.y] = ret}
rawset(tbl, key.x, cell)
return ret
end
local ret = rawget(cell, key.y)
if not ret then
ret = setmetatable({}, {__mode = "kv"})
rawset(cell, key.y, ret)
end
return ret
end
local Spatialhash = {}
function Spatialhash:init(cell_size)
self.cell_size = cell_size or 100
self.cells = setmetatable({}, cells_meta)
self.cells = {}
end
function Spatialhash:cellCoords(v)
return {x=floor(v.x / self.cell_size), y=floor(v.y / self.cell_size)}
function Spatialhash:cellCoords(x,y)
return floor(x / self.cell_size), floor(y / self.cell_size)
end
function Spatialhash:cell(v)
return self.cells[ self:cellCoords(v) ]
function Spatialhash:cell(i,k)
local row = rawget(self.cells, i)
if not row then
row = {}
rawset(self.cells, i, row)
end
local cell = rawget(row, k)
if not cell then
cell = setmetatable({}, {__mode = "kv"})
rawset(row, k, cell)
end
return cell
end
function Spatialhash:insert(obj, ul, lr)
local ul = self:cellCoords(ul)
local lr = self:cellCoords(lr)
for i = ul.x,lr.x do
for k = ul.y,lr.y do
rawset(self.cells[{x=i,y=k}], obj, obj)
function Spatialhash:cellAt(x,y)
return self:cell(self:cellCoords(x,y))
end
function Spatialhash:insert(obj, x1, y1, x2, y2)
x1, y1 = self:cellCoords(x1, y1)
x2, y2 = self:cellCoords(x2, y2)
for i = x1,x2 do
for k = y1,y2 do
rawset(self:cell(i,k), obj, obj)
end
end
end
function Spatialhash:remove(obj, ul, lr)
function Spatialhash:remove(obj, x1, y1, x2,y2)
-- no bbox given. => must check all cells
if not ul or not lr then
for _,cell in pairs(self.cells) do
rawset(cell, obj, nil)
if not (x1 and y1 and x2 and y2) then
for _,row in pairs(self.cells) do
for _,cell in pairs(row) do
rawset(cell, obj, nil)
end
end
return
end
local ul = self:cellCoords(ul)
local lr = self:cellCoords(lr)
-- else: remove only from bbox
for i = ul.x,lr.x do
for k = ul.y,lr.y do
rawset(self.cells[{x=i,y=k}], obj, nil)
x1,y1 = self:cellCoords(x1,y1)
x2,y2 = self:cellCoords(x2,y2)
for i = x1,x2 do
for k = y1,y2 do
rawset(self:cell(i,k), obj, nil)
end
end
end
-- update an objects position
function Spatialhash:update(obj, ul_old, lr_old, ul_new, lr_new)
local ul_old, lr_old = self:cellCoords(ul_old), self:cellCoords(lr_old)
local ul_new, lr_new = self:cellCoords(ul_new), self:cellCoords(lr_new)
function Spatialhash:update(obj, old_x1,old_y1, old_x2,old_y2, new_x1,new_y1, new_x2,new_y2)
old_x1, old_y1 = self:cellCoords(old_x1, old_y1)
old_x2, old_y2 = self:cellCoords(old_x2, old_y2)
if ul_old.x == ul_new.x and ul_old.y == ul_new.y and
lr_old.x == lr_new.x and lr_old.y == lr_new.y then
new_x1, new_y1 = self:cellCoords(new_x1, new_y1)
new_x2, new_y2 = self:cellCoords(new_x2, new_y2)
if old_x1 == new_x1 and old_y1 == new_y1 and
old_x2 == new_x2 and old_y2 == new_y2 then
return
end
for i = ul_old.x,lr_old.x do
for k = ul_old.y,lr_old.y do
rawset(self.cells[{x=i,y=k}], obj, nil)
for i = old_x1,old_x2 do
for k = old_y1,old_y2 do
rawset(self:cell(i,k), obj, nil)
end
end
for i = ul_new.x,lr_new.x do
for k = ul_new.y,lr_new.y do
rawset(self.cells[{x=i,y=k}], obj, obj)
for i = new_x1,new_x2 do
for k = new_y1,new_y2 do
rawset(self:cell(i,k), obj, obj)
end
end
end
function Spatialhash:getNeighbors(obj, ul, lr)
local ul = self:cellCoords(ul)
local lr = self:cellCoords(lr)
function Spatialhash:getNeighbors(obj, x1,y1, x2,y2)
x1,y1 = self:cellCoords(x1,y1)
x2,y2 = self:cellCoords(x2,y2)
local set = {}
for i = ul.x,lr.x do
for k = ul.y,lr.y do
local cell = self.cells[{x=i,y=k}] or {}
for other,_ in pairs(cell) do
for i = x1,x2 do
for k = y1,y2 do
local cell = self:cell(i,k)
for other in pairs(cell) do
rawset(set, other, other)
end
end