spacehash.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514
  1. package tamias
  2. // import "container/vector"
  3. // The spatial hash is Chipmunk's default (and currently only) spatial
  4. // index type.
  5. // Based on a chained hash table.
  6. type SpaceHashElement interface {
  7. HashElement
  8. GetBB()(BB)
  9. }
  10. // Used internally to track objects added to the hash
  11. type Handle struct {
  12. // Pointer to the object
  13. obj SpaceHashElement
  14. // Retain count
  15. retain int
  16. // Query stamp. Used to make sure two objects
  17. // aren't identified twice in the same query.
  18. stamp int
  19. }
  20. // Linked list element for in the chains.
  21. type SpaceHashBin struct {
  22. handle * Handle
  23. next * SpaceHashBin
  24. }
  25. type SpaceHash struct {
  26. // Number of cells in the table.
  27. numcells int
  28. // Dimensions of the cells.
  29. celldim Float
  30. // Hashset of the handles and the recycled ones.
  31. handleSet *HashSet
  32. pooledHandles *Array
  33. // The table and the recycled bins.
  34. table []*SpaceHashBin
  35. pooledBins *SpaceHashBin
  36. allocatedBuffers *Array
  37. // Incremented on each query. See cpHandle.stamp.
  38. stamp int
  39. }
  40. func (hand *Handle) Init(obj SpaceHashElement) (*Handle) {
  41. hand.obj = obj
  42. hand.retain = 0
  43. hand.stamp = 0
  44. return hand
  45. }
  46. // Equality function for the handleset.
  47. func (hand *Handle) Equals(el interface {}) (bool) {
  48. other, ok := el.(*Handle)
  49. if !ok { return false; }
  50. return hand == other
  51. }
  52. // Equality function for the SpaceHash
  53. func (hash *SpaceHash) Equals(el interface {}) (bool) {
  54. other, ok := el.(*SpaceHash)
  55. if !ok { return false; }
  56. return hash == other
  57. }
  58. func (hand *Handle) Retain() {
  59. hand.retain++
  60. }
  61. func (hand *Handle) Release(pooledHandles * Array) {
  62. hand.retain--
  63. if hand.retain < 1 {
  64. pooledHandles.Push(hand)
  65. }
  66. }
  67. func SpaceHashAlloc() (*SpaceHash) {
  68. return &SpaceHash{}
  69. }
  70. func (hash * SpaceHash) AllocTable(numcells int) {
  71. hash.numcells = numcells
  72. hash.table = make([]*SpaceHashBin, numcells)
  73. }
  74. // Transformation function for the handleset.
  75. /*
  76. func (handle *Handle) handleSetTrans (hash * SpaceHash) (*Handle) {
  77. if(hash.pooledHandles.num == 0){
  78. // handle pool is exhausted, make more
  79. count := 100
  80. buffer := make([]Handle, count)
  81. for i:=0; i<count; i++ {
  82. hash.pooledHandles.Push(buffer[i])
  83. }
  84. }
  85. hand := handle.Init(hash.pooledHandles.Pop(), obj)
  86. hand.Retain()
  87. return hand
  88. }
  89. */
  90. func (hash *SpaceHash) Init(celldim Float, numcells int) (* SpaceHash) {
  91. hash.AllocTable(next_prime(numcells))
  92. hash.celldim = celldim
  93. hash.handleSet = HashSetNew(0)
  94. hash.pooledHandles = ArrayNew(0)
  95. hash.pooledBins = nil
  96. hash.allocatedBuffers = ArrayNew(0)
  97. hash.stamp = 1
  98. return hash
  99. }
  100. func SpaceHashNew(celldim Float, numcells int) (* SpaceHash) {
  101. return SpaceHashAlloc().Init(celldim, numcells)
  102. }
  103. func (hash * SpaceHash) recycleBin(bin *SpaceHashBin) {
  104. bin.next = hash.pooledBins
  105. hash.pooledBins = bin
  106. }
  107. func (hash * SpaceHash) clearHashCell(idx int) {
  108. bin := hash.table[idx]
  109. for bin != nil {
  110. next := bin.next
  111. bin.handle.Release(hash.pooledHandles)
  112. hash.recycleBin(bin)
  113. bin = next
  114. }
  115. hash.table[idx] = nil
  116. }
  117. // Clear all cells in the hashtable.
  118. func (hash * SpaceHash) clearHash() {
  119. for i:=0; i<hash.numcells; i++ {
  120. hash.clearHashCell(i)
  121. }
  122. }
  123. func (hash * SpaceHash) Destroy() {
  124. hash.clearHash()
  125. hash.handleSet.Free()
  126. hash.allocatedBuffers = nil
  127. hash.pooledHandles = nil
  128. hash.table = nil
  129. }
  130. func (hash * SpaceHash) Free() {
  131. hash.Destroy()
  132. }
  133. func (hash * SpaceHash) Resize(celldim Float, numcells int) {
  134. // Clear the hash to release the old handle locks.
  135. hash.clearHash()
  136. hash.celldim = celldim
  137. hash.AllocTable(next_prime(numcells))
  138. }
  139. // Return true if the chain contains the handle.
  140. func (bin *SpaceHashBin) containsHandle(hand *Handle) (bool) {
  141. if bin != nil {
  142. if (bin.handle == hand) { return true }
  143. bin = bin.next
  144. }
  145. return false
  146. }
  147. // Get a recycled or new bin.
  148. func (hash * SpaceHash) getEmptyBin() (* SpaceHashBin) {
  149. bin := hash.pooledBins
  150. if bin != nil {
  151. hash.pooledBins = bin.next
  152. return bin
  153. }
  154. // Pool is exhausted, make more
  155. count := 100
  156. buffer := make([]SpaceHashBin, count)
  157. hash.allocatedBuffers.Push(buffer)
  158. // push all but the first one, return the first instead
  159. for i:=1; i<count; i++ {
  160. hash.recycleBin(&buffer[i])
  161. }
  162. return &buffer[0]
  163. }
  164. // The hash function itself.
  165. func hash_func(x, y, n HashValue) (HashValue) {
  166. return (x*1640531513 ^ y*2654435789) % n
  167. }
  168. // Much faster than (int)floor(f)
  169. // Profiling showed floor() to be a sizable performance hog (in C)
  170. // XXX: check this for golang.
  171. func (f Float) floor_int() (int) {
  172. i := int(f)
  173. if f < Float(0.0) && f != Float(i) {
  174. return i - 1
  175. }
  176. return i
  177. }
  178. func (hash * SpaceHash) cellDimensions(bb BB) (int, int, int, int) {
  179. // Find the dimensions in cell coordinates.
  180. dim := hash.celldim
  181. // Fix by ShiftZ
  182. l := (bb.L / dim).floor_int()
  183. r := (bb.R / dim).floor_int()
  184. b := (bb.B / dim).floor_int()
  185. t := (bb.T / dim).floor_int()
  186. return l, r, b, t
  187. }
  188. func (hash * SpaceHash) hashHandle(hand * Handle, bb BB) {
  189. // Find the dimensions in cell coordinates.
  190. l, r, b, t := hash.cellDimensions(bb)
  191. n := hash.numcells
  192. for i:=l ; i<=r; i++ {
  193. for j:=b ; j<=t ; j++ {
  194. idx := hash_func(HashValue(i), HashValue(j), HashValue(n))
  195. bin := hash.table[idx]
  196. // Don't add an object twice to the same cell.
  197. if bin.containsHandle(hand) { continue; }
  198. hand.Retain()
  199. // Insert a new bin for the handle in this cell.
  200. newBin := hash.getEmptyBin()
  201. newBin.handle = hand
  202. newBin.next = bin
  203. hash.table[idx] = newBin
  204. }
  205. }
  206. }
  207. func (hash * SpaceHash) InsertHandle(hand * Handle, obj HashElement, hashid HashValue, bb BB) {
  208. hand = hash.handleSet.Insert(hashid, obj).(*Handle)
  209. hash.hashHandle(hand, bb)
  210. }
  211. func (hash * SpaceHash) Insert(obj SpaceHashElement, hashid HashValue) {
  212. hand := hash.handleSet.Find(hashid, obj).(*Handle)
  213. hash.hashHandle(hand, obj.GetBB())
  214. }
  215. func (hash * SpaceHash) RehashObject(obj SpaceHashElement, hashid HashValue) {
  216. hand := hash.handleSet.Find(hashid, obj).(*Handle)
  217. hash.hashHandle(hand, obj.GetBB())
  218. }
  219. // Hashset iterator function for rehashing the spatial hash. (hash hash hash hash?)
  220. func handleRehashHelper(bin, data HashElement) {
  221. var hand * Handle = bin.(*Handle)
  222. var hash * SpaceHash = data.(*SpaceHash)
  223. hash.hashHandle(hand, hand.obj.GetBB())
  224. }
  225. func (hash * SpaceHash) Rehash() {
  226. hash.clearHash()
  227. // Rehash all of the handles.
  228. hash.handleSet.Each(handleRehashHelper, hash)
  229. }
  230. func (hash * SpaceHash) Remove(obj HashElement, hashid HashValue) {
  231. hand := hash.handleSet.Remove(hashid, obj).(*Handle)
  232. if hand != nil {
  233. hand.obj = nil
  234. hand.Release(hash.pooledHandles)
  235. }
  236. }
  237. type SpaceHashIterator func(a, b HashElement)
  238. type SpaceHashQueryFunc func(a, b, c HashElement) (bool)
  239. // Used by the cpSpaceHashEach() iterator.
  240. type eachPair struct {
  241. fun SpaceHashIterator
  242. data HashElement
  243. }
  244. // Equals function for eachPair
  245. func (pair *eachPair) Equals(el interface {}) (bool) {
  246. other, ok := el.(*eachPair)
  247. if !ok { return false; }
  248. return pair == other
  249. }
  250. // Calls the user iterator function. (Gross I know.)
  251. func eachHelper(bin , data HashElement) {
  252. var hand * Handle = bin.(*Handle)
  253. var pair * eachPair = data.(*eachPair)
  254. pair.fun(hand.obj, pair.data)
  255. }
  256. // Iterate over the objects in the spatial hash.
  257. func (hash *SpaceHash) Each(fun SpaceHashIterator, data HashElement) {
  258. // Bundle the callback up to send to the hashset iterator.
  259. pair := &eachPair{fun, data}
  260. hash.handleSet.Each(eachHelper, pair)
  261. }
  262. // Calls the callback function for the objects in a given chain.
  263. func (hash *SpaceHash) query(bin * SpaceHashBin, obj HashElement,
  264. fun SpaceHashQueryFunc, data HashElement) {
  265. for ; bin != nil ; bin = bin.next {
  266. hand := bin.handle
  267. other := hand.obj
  268. // Skip over certain conditions
  269. if hand.stamp == hash.stamp || obj.Equals(other) || other == nil {
  270. continue
  271. }
  272. // Have we already tried this pair in this query?
  273. // Is obj the same as other?
  274. // Has other been removed since the last rehash?
  275. fun(obj, other, data)
  276. // Stamp that the handle was checked already against this object.
  277. hand.stamp = hash.stamp
  278. }
  279. }
  280. func (hash * SpaceHash) PointQuery(point Vect, fun SpaceHashQueryFunc,
  281. data HashElement) {
  282. dim := hash.celldim
  283. xf := (point.X/dim).floor_int()
  284. yf := (point.Y/dim).floor_int()
  285. hc := hash.numcells
  286. idx := hash_func(HashValue(xf), HashValue(yf), HashValue(hc))
  287. // Fix by ShiftZ
  288. hash.query(hash.table[idx], &point, fun, data)
  289. // Increment the stamp.
  290. // Only one cell is checked, but query() requires it anyway.
  291. hash.stamp++
  292. }
  293. func (hash * SpaceHash) SpaceQuery(obj HashElement, bb BB,
  294. fun SpaceHashQueryFunc, data HashElement) {
  295. // Get the dimensions in cell coordinates.
  296. l, r, b, t := hash.cellDimensions(bb)
  297. n := hash.numcells
  298. // Iterate over the cells and query them.
  299. for i:=l ; i<=r; i++ {
  300. for j := b; j<=t; j++ {
  301. idx := hash_func(HashValue(i), HashValue(j), HashValue(n))
  302. hash.query(hash.table[idx], obj, fun, data)
  303. }
  304. }
  305. // Increment the stamp.
  306. hash.stamp++
  307. }
  308. // Similar to struct eachPair above.
  309. type queryRehashPair struct {
  310. hash * SpaceHash
  311. fun SpaceHashQueryFunc
  312. data HashElement
  313. }
  314. func (pair *queryRehashPair) Equals(el interface {}) (bool) {
  315. other, ok := el.(*queryRehashPair)
  316. if !ok { return false; }
  317. return pair == other
  318. }
  319. // Hashset iterator func used with cpSpaceHashQueryRehash().
  320. func handleQueryRehashHelper(p1, p2 HashElement) {
  321. var hand * Handle = p1.(*Handle)
  322. var pair * queryRehashPair = p2.(*queryRehashPair)
  323. // Unpack the user callback data.
  324. hash := pair.hash
  325. fun := pair.fun
  326. // dim := hash.celldim
  327. n := hash.numcells
  328. obj := hand.obj
  329. bb := obj.GetBB()
  330. var l, r, b, t int
  331. l, r, b , t = hash.cellDimensions(bb)
  332. for i := l; i<=r; i++ {
  333. for j := b; j<=t; j++ {
  334. // // exit the loops if the object has been deleted in func().
  335. // if(!hand.obj) goto break_out
  336. idx := hash_func(HashValue(i), HashValue(j), HashValue(n))
  337. bin := hash.table[idx]
  338. if bin.containsHandle(hand) { continue }
  339. hand.Retain()
  340. // this MUST be done first in case the object is removed in func()
  341. hash.query(bin, obj, fun, pair.data)
  342. newBin := hash.getEmptyBin()
  343. newBin.handle = hand
  344. newBin.next = bin
  345. hash.table[idx] = newBin
  346. }
  347. }
  348. // break_out:
  349. // Increment the stamp for each object we hash.
  350. hash.stamp++
  351. }
  352. func (hash * SpaceHash) hashRehash(fun SpaceHashQueryFunc, data HashElement) {
  353. hash.clearHash()
  354. pair := &queryRehashPair{hash, fun, data}
  355. hash.handleSet.Each(handleQueryRehashHelper, pair)
  356. }
  357. type SpaceHashSegmentQueryFunc func (obj, other, data HashElement) (Float)
  358. func (hash * SpaceHash) segmentQuery(bin *SpaceHashBin , obj HashElement,
  359. fun SpaceHashSegmentQueryFunc, data HashElement) (Float) {
  360. t := Float(1.0)
  361. for ; bin != nil ; bin = bin.next {
  362. hand := bin.handle
  363. other := hand.obj
  364. // Skip over certain conditions
  365. if hand.stamp == hash.stamp || other == nil { continue; }
  366. // Have we already tried this pair in this query?
  367. // Has other been removed since the last rehash?
  368. // Stamp that the handle was checked already against this object.
  369. hand.stamp = hash.stamp
  370. t = t.Min(fun(obj, other, data))
  371. }
  372. return t
  373. }
  374. // modified from http://playtechs.blogspot.com/2007/03/raytracing-on-grid.html
  375. func (hash * SpaceHash) SegmentQuery(obj HashElement, a, b Vect, t_exit Float, fun SpaceHashSegmentQueryFunc, data HashElement) {
  376. a = a.Mult(Float(1.0)/hash.celldim)
  377. b = b.Mult(Float(1.0)/hash.celldim)
  378. dt_dx := Float(1.0)/((b.X - a.X).Abs())
  379. dt_dy := Float(1.0)/((b.Y - a.Y).Abs())
  380. cell_x := (a.X).floor_int()
  381. cell_y := (a.Y).floor_int()
  382. t := Float(0.0)
  383. var x_inc , y_inc int
  384. var temp_v, temp_h Float
  385. if b.X > a.X {
  386. x_inc = 1
  387. temp_h = (a.X + Float(1.0)).Floor() - a.X
  388. } else {
  389. x_inc = -1
  390. temp_h = a.X - a.X.Floor()
  391. }
  392. if b.Y > a.Y {
  393. y_inc = 1
  394. temp_v = (a.Y + Float(1.0)).Floor() - a.Y
  395. } else {
  396. y_inc = -1
  397. temp_v = a.Y - a.Y.Floor()
  398. }
  399. // fix NANs in horizontal directions
  400. next_h := dt_dx
  401. if temp_h != 0.0 {
  402. next_h = temp_h*dt_dx
  403. }
  404. next_v := dt_dy
  405. if temp_v != 0.0 {
  406. next_v = temp_v*dt_dy
  407. }
  408. n := hash.numcells
  409. for t < t_exit {
  410. idx := hash_func(HashValue(cell_x), HashValue(cell_y), HashValue(n))
  411. t_exit = t_exit.Min(hash.segmentQuery(hash.table[idx], obj, fun, data))
  412. if (next_v < next_h){
  413. cell_y += y_inc
  414. t = next_v
  415. next_v += dt_dy
  416. } else {
  417. cell_x += x_inc
  418. t = next_h
  419. next_h += dt_dx
  420. }
  421. }
  422. hash.stamp++
  423. }