spatialhash.lua 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. --[[
  2. Copyright (c) 2011 Matthias Richter
  3. Permission is hereby granted, free of charge, to any person obtaining a copy
  4. of this software and associated documentation files (the "Software"), to deal
  5. in the Software without restriction, including without limitation the rights
  6. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  7. copies of the Software, and to permit persons to whom the Software is
  8. furnished to do so, subject to the following conditions:
  9. The above copyright notice and this permission notice shall be included in
  10. all copies or substantial portions of the Software.
  11. Except as contained in this notice, the name(s) of the above copyright holders
  12. shall not be used in advertising or otherwise to promote the sale, use or
  13. other dealings in this Software without prior written authorization.
  14. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  20. THE SOFTWARE.
  21. ]]--
  22. local floor = math.floor
  23. local min, max = math.min, math.max
  24. local _PACKAGE, common_local = (...):match("^(.+)%.[^%.]+"), common
  25. if not (type(common) == 'table' and common.class and common.instance) then
  26. assert(common_class ~= false, 'No class commons specification available.')
  27. require(_PACKAGE .. '.class')
  28. common_local, common = common, common_local
  29. end
  30. local Spatialhash = {}
  31. function Spatialhash:init(cell_size)
  32. self.cell_size = cell_size or 100
  33. self.cells = {}
  34. end
  35. function Spatialhash:cellCoords(x,y)
  36. return floor(x / self.cell_size), floor(y / self.cell_size)
  37. end
  38. function Spatialhash:cell(i,k)
  39. local row = rawget(self.cells, i)
  40. if not row then
  41. row = {}
  42. rawset(self.cells, i, row)
  43. end
  44. local cell = rawget(row, k)
  45. if not cell then
  46. cell = setmetatable({}, {__mode = "kv"})
  47. rawset(row, k, cell)
  48. end
  49. return cell
  50. end
  51. function Spatialhash:cellAt(x,y)
  52. return self:cell(self:cellCoords(x,y))
  53. end
  54. -- get all shapes
  55. function Spatialhash:shapes()
  56. local set = {}
  57. for i,row in pairs(self.cells) do
  58. for k,cell in pairs(row) do
  59. for obj in pairs(cell) do
  60. rawset(set, obj, obj)
  61. end
  62. end
  63. end
  64. return set
  65. end
  66. -- get all shapes that are in the same cells as the bbox x1,y1 '--. x2,y2
  67. function Spatialhash:inSameCells(x1,y1, x2,y2)
  68. local set = {}
  69. x1, y1 = self:cellCoords(x1, y1)
  70. x2, y2 = self:cellCoords(x2, y2)
  71. for i = x1,x2 do
  72. for k = y1,y2 do
  73. for obj in pairs(self:cell(i,k)) do
  74. rawset(set, obj, obj)
  75. end
  76. end
  77. end
  78. return set
  79. end
  80. function Spatialhash:register(obj, x1, y1, x2, y2)
  81. x1, y1 = self:cellCoords(x1, y1)
  82. x2, y2 = self:cellCoords(x2, y2)
  83. for i = x1,x2 do
  84. for k = y1,y2 do
  85. rawset(self:cell(i,k), obj, obj)
  86. end
  87. end
  88. end
  89. function Spatialhash:remove(obj, x1, y1, x2,y2)
  90. -- no bbox given. => must check all cells
  91. if not (x1 and y1 and x2 and y2) then
  92. for _,row in pairs(self.cells) do
  93. for _,cell in pairs(row) do
  94. rawset(cell, obj, nil)
  95. end
  96. end
  97. return
  98. end
  99. -- else: remove only from bbox
  100. x1,y1 = self:cellCoords(x1,y1)
  101. x2,y2 = self:cellCoords(x2,y2)
  102. for i = x1,x2 do
  103. for k = y1,y2 do
  104. rawset(self:cell(i,k), obj, nil)
  105. end
  106. end
  107. end
  108. -- update an objects position
  109. function Spatialhash:update(obj, old_x1,old_y1, old_x2,old_y2, new_x1,new_y1, new_x2,new_y2)
  110. old_x1, old_y1 = self:cellCoords(old_x1, old_y1)
  111. old_x2, old_y2 = self:cellCoords(old_x2, old_y2)
  112. new_x1, new_y1 = self:cellCoords(new_x1, new_y1)
  113. new_x2, new_y2 = self:cellCoords(new_x2, new_y2)
  114. if old_x1 == new_x1 and old_y1 == new_y1 and
  115. old_x2 == new_x2 and old_y2 == new_y2 then
  116. return
  117. end
  118. for i = old_x1,old_x2 do
  119. for k = old_y1,old_y2 do
  120. rawset(self:cell(i,k), obj, nil)
  121. end
  122. end
  123. for i = new_x1,new_x2 do
  124. for k = new_y1,new_y2 do
  125. rawset(self:cell(i,k), obj, obj)
  126. end
  127. end
  128. end
  129. function Spatialhash:draw(how, show_empty, print_key)
  130. if show_empty == nil then show_empty = true end
  131. for k1,v in pairs(self.cells) do
  132. for k2,cell in pairs(v) do
  133. local is_empty = (next(cell) == nil)
  134. if show_empty or not is_empty then
  135. local x = k1 * self.cell_size
  136. local y = k2 * self.cell_size
  137. love.graphics.rectangle(how or 'line', x,y, self.cell_size, self.cell_size)
  138. if print_key then
  139. love.graphics.print(("%d:%d"):format(k1,k2), x+3,y+3)
  140. end
  141. end
  142. end
  143. end
  144. end
  145. return common_local.class('Spatialhash', Spatialhash)