From e03ea9b0a2a27373073fbc24bf8bb282836dea29 Mon Sep 17 00:00:00 2001 From: Matthias Richter Date: Wed, 30 May 2012 15:27:09 +0200 Subject: [PATCH] Spatial hash to use number instead of table arguments --- init.lua | 12 ++--- spatialhash.lua | 127 +++++++++++++++++++++++------------------------- 2 files changed, 66 insertions(+), 73 deletions(-) diff --git a/init.lua b/init.lua index ec5d107..9be77c3 100644 --- a/init.lua +++ b/init.lua @@ -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 diff --git a/spatialhash.lua b/spatialhash.lua index 9bd8d0d..2756e05 100644 --- a/spatialhash.lua +++ b/spatialhash.lua @@ -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