1 /* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
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:
10 * The above copyright notice and this permission notice shall be included in
11 * all copies or substantial portions of the Software.
13 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 #include "chipmunk/chipmunk_private.h"
25 typedef struct cpSpaceHashBin cpSpaceHashBin;
26 typedef struct cpHandle cpHandle;
29 cpSpatialIndex spatialIndex;
34 cpSpaceHashBin **table;
37 cpSpaceHashBin *pooledBins;
38 cpArray *pooledHandles;
39 cpArray *allocatedBuffers;
45 //MARK: Handle Functions
54 cpHandleInit(cpHandle *hand, void *obj)
63 static inline void cpHandleRetain(cpHandle *hand){hand->retain++;}
66 cpHandleRelease(cpHandle *hand, cpArray *pooledHandles)
69 if(hand->retain == 0) cpArrayPush(pooledHandles, hand);
72 static int handleSetEql(void *obj, cpHandle *hand){return (obj == hand->obj);}
75 handleSetTrans(void *obj, cpSpaceHash *hash)
77 if(hash->pooledHandles->num == 0){
78 // handle pool is exhausted, make more
79 int count = CP_BUFFER_BYTES/sizeof(cpHandle);
80 cpAssertHard(count, "Internal Error: Buffer size is too small.");
82 cpHandle *buffer = (cpHandle *)cpcalloc(1, CP_BUFFER_BYTES);
83 cpArrayPush(hash->allocatedBuffers, buffer);
85 for(int i=0; i<count; i++) cpArrayPush(hash->pooledHandles, buffer + i);
88 cpHandle *hand = cpHandleInit((cpHandle *)cpArrayPop(hash->pooledHandles), obj);
96 struct cpSpaceHashBin {
102 recycleBin(cpSpaceHash *hash, cpSpaceHashBin *bin)
104 bin->next = hash->pooledBins;
105 hash->pooledBins = bin;
109 clearTableCell(cpSpaceHash *hash, int idx)
111 cpSpaceHashBin *bin = hash->table[idx];
113 cpSpaceHashBin *next = bin->next;
115 cpHandleRelease(bin->handle, hash->pooledHandles);
116 recycleBin(hash, bin);
121 hash->table[idx] = NULL;
125 clearTable(cpSpaceHash *hash)
127 for(int i=0; i<hash->numcells; i++) clearTableCell(hash, i);
130 // Get a recycled or new bin.
131 static inline cpSpaceHashBin *
132 getEmptyBin(cpSpaceHash *hash)
134 cpSpaceHashBin *bin = hash->pooledBins;
137 hash->pooledBins = bin->next;
140 // Pool is exhausted, make more
141 int count = CP_BUFFER_BYTES/sizeof(cpSpaceHashBin);
142 cpAssertHard(count, "Internal Error: Buffer size is too small.");
144 cpSpaceHashBin *buffer = (cpSpaceHashBin *)cpcalloc(1, CP_BUFFER_BYTES);
145 cpArrayPush(hash->allocatedBuffers, buffer);
147 // push all but the first one, return the first instead
148 for(int i=1; i<count; i++) recycleBin(hash, buffer + i);
153 //MARK: Memory Management Functions
156 cpSpaceHashAlloc(void)
158 return (cpSpaceHash *)cpcalloc(1, sizeof(cpSpaceHash));
161 // Frees the old table, and allocate a new one.
163 cpSpaceHashAllocTable(cpSpaceHash *hash, int numcells)
167 hash->numcells = numcells;
168 hash->table = (cpSpaceHashBin **)cpcalloc(numcells, sizeof(cpSpaceHashBin *));
171 static inline cpSpatialIndexClass *Klass();
174 cpSpaceHashInit(cpSpaceHash *hash, cpFloat celldim, int numcells, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex)
176 cpSpatialIndexInit((cpSpatialIndex *)hash, Klass(), bbfunc, staticIndex);
178 cpSpaceHashAllocTable(hash, next_prime(numcells));
179 hash->celldim = celldim;
181 hash->handleSet = cpHashSetNew(0, (cpHashSetEqlFunc)handleSetEql);
183 hash->pooledHandles = cpArrayNew(0);
185 hash->pooledBins = NULL;
186 hash->allocatedBuffers = cpArrayNew(0);
190 return (cpSpatialIndex *)hash;
194 cpSpaceHashNew(cpFloat celldim, int cells, cpSpatialIndexBBFunc bbfunc, cpSpatialIndex *staticIndex)
196 return cpSpaceHashInit(cpSpaceHashAlloc(), celldim, cells, bbfunc, staticIndex);
200 cpSpaceHashDestroy(cpSpaceHash *hash)
202 if(hash->table) clearTable(hash);
205 cpHashSetFree(hash->handleSet);
207 cpArrayFreeEach(hash->allocatedBuffers, cpfree);
208 cpArrayFree(hash->allocatedBuffers);
209 cpArrayFree(hash->pooledHandles);
212 //MARK: Helper Functions
215 containsHandle(cpSpaceHashBin *bin, cpHandle *hand)
218 if(bin->handle == hand) return cpTrue;
225 // The hash function itself.
226 static inline cpHashValue
227 hash_func(cpHashValue x, cpHashValue y, cpHashValue n)
229 return (x*1640531513ul ^ y*2654435789ul) % n;
232 // Much faster than (int)floor(f)
233 // Profiling showed floor() to be a sizable performance hog
238 return (f < 0.0f && f != i ? i - 1 : i);
242 hashHandle(cpSpaceHash *hash, cpHandle *hand, cpBB bb)
244 // Find the dimensions in cell coordinates.
245 cpFloat dim = hash->celldim;
246 int l = floor_int(bb.l/dim); // Fix by ShiftZ
247 int r = floor_int(bb.r/dim);
248 int b = floor_int(bb.b/dim);
249 int t = floor_int(bb.t/dim);
251 int n = hash->numcells;
252 for(int i=l; i<=r; i++){
253 for(int j=b; j<=t; j++){
254 cpHashValue idx = hash_func(i,j,n);
255 cpSpaceHashBin *bin = hash->table[idx];
257 // Don't add an object twice to the same cell.
258 if(containsHandle(bin, hand)) continue;
260 cpHandleRetain(hand);
261 // Insert a new bin for the handle in this cell.
262 cpSpaceHashBin *newBin = getEmptyBin(hash);
263 newBin->handle = hand;
265 hash->table[idx] = newBin;
270 //MARK: Basic Operations
273 cpSpaceHashInsert(cpSpaceHash *hash, void *obj, cpHashValue hashid)
275 cpHandle *hand = (cpHandle *)cpHashSetInsert(hash->handleSet, hashid, obj, (cpHashSetTransFunc)handleSetTrans, hash);
276 hashHandle(hash, hand, hash->spatialIndex.bbfunc(obj));
280 cpSpaceHashRehashObject(cpSpaceHash *hash, void *obj, cpHashValue hashid)
282 cpHandle *hand = (cpHandle *)cpHashSetRemove(hash->handleSet, hashid, obj);
286 cpHandleRelease(hand, hash->pooledHandles);
288 cpSpaceHashInsert(hash, obj, hashid);
293 rehash_helper(cpHandle *hand, cpSpaceHash *hash)
295 hashHandle(hash, hand, hash->spatialIndex.bbfunc(hand->obj));
299 cpSpaceHashRehash(cpSpaceHash *hash)
302 cpHashSetEach(hash->handleSet, (cpHashSetIteratorFunc)rehash_helper, hash);
306 cpSpaceHashRemove(cpSpaceHash *hash, void *obj, cpHashValue hashid)
308 cpHandle *hand = (cpHandle *)cpHashSetRemove(hash->handleSet, hashid, obj);
312 cpHandleRelease(hand, hash->pooledHandles);
316 typedef struct eachContext {
317 cpSpatialIndexIteratorFunc func;
321 static void eachHelper(cpHandle *hand, eachContext *context){context->func(hand->obj, context->data);}
324 cpSpaceHashEach(cpSpaceHash *hash, cpSpatialIndexIteratorFunc func, void *data)
326 eachContext context = {func, data};
327 cpHashSetEach(hash->handleSet, (cpHashSetIteratorFunc)eachHelper, &context);
331 remove_orphaned_handles(cpSpaceHash *hash, cpSpaceHashBin **bin_ptr)
333 cpSpaceHashBin *bin = *bin_ptr;
335 cpHandle *hand = bin->handle;
336 cpSpaceHashBin *next = bin->next;
339 // orphaned handle, unlink and recycle the bin
340 (*bin_ptr) = bin->next;
341 recycleBin(hash, bin);
343 cpHandleRelease(hand, hash->pooledHandles);
345 bin_ptr = &bin->next;
352 //MARK: Query Functions
355 query_helper(cpSpaceHash *hash, cpSpaceHashBin **bin_ptr, void *obj, cpSpatialIndexQueryFunc func, void *data)
358 for(cpSpaceHashBin *bin = *bin_ptr; bin; bin = bin->next){
359 cpHandle *hand = bin->handle;
360 void *other = hand->obj;
362 if(hand->stamp == hash->stamp || obj == other){
365 func(obj, other, 0, data);
366 hand->stamp = hash->stamp;
368 // The object for this handle has been removed
369 // cleanup this cell and restart the query
370 remove_orphaned_handles(hash, bin_ptr);
371 goto restart; // GCC not smart enough/able to tail call an inlined function.
377 cpSpaceHashQuery(cpSpaceHash *hash, void *obj, cpBB bb, cpSpatialIndexQueryFunc func, void *data)
379 // Get the dimensions in cell coordinates.
380 cpFloat dim = hash->celldim;
381 int l = floor_int(bb.l/dim); // Fix by ShiftZ
382 int r = floor_int(bb.r/dim);
383 int b = floor_int(bb.b/dim);
384 int t = floor_int(bb.t/dim);
386 int n = hash->numcells;
387 cpSpaceHashBin **table = hash->table;
389 // Iterate over the cells and query them.
390 for(int i=l; i<=r; i++){
391 for(int j=b; j<=t; j++){
392 query_helper(hash, &table[hash_func(i,j,n)], obj, func, data);
399 // Similar to struct eachPair above.
400 typedef struct queryRehashContext {
402 cpSpatialIndexQueryFunc func;
404 } queryRehashContext;
406 // Hashset iterator func used with cpSpaceHashQueryRehash().
408 queryRehash_helper(cpHandle *hand, queryRehashContext *context)
410 cpSpaceHash *hash = context->hash;
411 cpSpatialIndexQueryFunc func = context->func;
412 void *data = context->data;
414 cpFloat dim = hash->celldim;
415 int n = hash->numcells;
417 void *obj = hand->obj;
418 cpBB bb = hash->spatialIndex.bbfunc(obj);
420 int l = floor_int(bb.l/dim);
421 int r = floor_int(bb.r/dim);
422 int b = floor_int(bb.b/dim);
423 int t = floor_int(bb.t/dim);
425 cpSpaceHashBin **table = hash->table;
427 for(int i=l; i<=r; i++){
428 for(int j=b; j<=t; j++){
429 cpHashValue idx = hash_func(i,j,n);
430 cpSpaceHashBin *bin = table[idx];
432 if(containsHandle(bin, hand)) continue;
434 cpHandleRetain(hand); // this MUST be done first in case the object is removed in func()
435 query_helper(hash, &bin, obj, func, data);
437 cpSpaceHashBin *newBin = getEmptyBin(hash);
438 newBin->handle = hand;
444 // Increment the stamp for each object hashed.
449 cpSpaceHashReindexQuery(cpSpaceHash *hash, cpSpatialIndexQueryFunc func, void *data)
453 queryRehashContext context = {hash, func, data};
454 cpHashSetEach(hash->handleSet, (cpHashSetIteratorFunc)queryRehash_helper, &context);
456 cpSpatialIndexCollideStatic((cpSpatialIndex *)hash, hash->spatialIndex.staticIndex, func, data);
459 static inline cpFloat
460 segmentQuery_helper(cpSpaceHash *hash, cpSpaceHashBin **bin_ptr, void *obj, cpSpatialIndexSegmentQueryFunc func, void *data)
465 for(cpSpaceHashBin *bin = *bin_ptr; bin; bin = bin->next){
466 cpHandle *hand = bin->handle;
467 void *other = hand->obj;
469 // Skip over certain conditions
470 if(hand->stamp == hash->stamp){
473 t = cpfmin(t, func(obj, other, data));
474 hand->stamp = hash->stamp;
476 // The object for this handle has been removed
477 // cleanup this cell and restart the query
478 remove_orphaned_handles(hash, bin_ptr);
479 goto restart; // GCC not smart enough/able to tail call an inlined function.
486 // modified from http://playtechs.blogspot.com/2007/03/raytracing-on-grid.html
488 cpSpaceHashSegmentQuery(cpSpaceHash *hash, void *obj, cpVect a, cpVect b, cpFloat t_exit, cpSpatialIndexSegmentQueryFunc func, void *data)
490 a = cpvmult(a, 1.0f/hash->celldim);
491 b = cpvmult(b, 1.0f/hash->celldim);
493 int cell_x = floor_int(a.x), cell_y = floor_int(a.y);
498 cpFloat temp_v, temp_h;
502 temp_h = (cpffloor(a.x + 1.0f) - a.x);
505 temp_h = (a.x - cpffloor(a.x));
510 temp_v = (cpffloor(a.y + 1.0f) - a.y);
513 temp_v = (a.y - cpffloor(a.y));
516 // Division by zero is *very* slow on ARM
517 cpFloat dx = cpfabs(b.x - a.x), dy = cpfabs(b.y - a.y);
518 cpFloat dt_dx = (dx ? 1.0f/dx : INFINITY), dt_dy = (dy ? 1.0f/dy : INFINITY);
520 // fix NANs in horizontal directions
521 cpFloat next_h = (temp_h ? temp_h*dt_dx : dt_dx);
522 cpFloat next_v = (temp_v ? temp_v*dt_dy : dt_dy);
524 int n = hash->numcells;
525 cpSpaceHashBin **table = hash->table;
528 cpHashValue idx = hash_func(cell_x, cell_y, n);
529 t_exit = cpfmin(t_exit, segmentQuery_helper(hash, &table[idx], obj, func, data));
531 if (next_v < next_h){
548 cpSpaceHashResize(cpSpaceHash *hash, cpFloat celldim, int numcells)
550 if(hash->spatialIndex.klass != Klass()){
551 cpAssertWarn(cpFalse, "Ignoring cpSpaceHashResize() call to non-cpSpaceHash spatial index.");
557 hash->celldim = celldim;
558 cpSpaceHashAllocTable(hash, next_prime(numcells));
562 cpSpaceHashCount(cpSpaceHash *hash)
564 return cpHashSetCount(hash->handleSet);
568 cpSpaceHashContains(cpSpaceHash *hash, void *obj, cpHashValue hashid)
570 return cpHashSetFind(hash->handleSet, hashid, obj) != NULL;
573 static cpSpatialIndexClass klass = {
574 (cpSpatialIndexDestroyImpl)cpSpaceHashDestroy,
576 (cpSpatialIndexCountImpl)cpSpaceHashCount,
577 (cpSpatialIndexEachImpl)cpSpaceHashEach,
578 (cpSpatialIndexContainsImpl)cpSpaceHashContains,
580 (cpSpatialIndexInsertImpl)cpSpaceHashInsert,
581 (cpSpatialIndexRemoveImpl)cpSpaceHashRemove,
583 (cpSpatialIndexReindexImpl)cpSpaceHashRehash,
584 (cpSpatialIndexReindexObjectImpl)cpSpaceHashRehashObject,
585 (cpSpatialIndexReindexQueryImpl)cpSpaceHashReindexQuery,
587 (cpSpatialIndexQueryImpl)cpSpaceHashQuery,
588 (cpSpatialIndexSegmentQueryImpl)cpSpaceHashSegmentQuery,
591 static inline cpSpatialIndexClass *Klass(){return &klass;}
593 //MARK: Debug Drawing
595 //#define CP_BBTREE_DEBUG_DRAW
596 #ifdef CP_BBTREE_DEBUG_DRAW
597 #include "OpenGL/gl.h"
598 #include "OpenGL/glu.h"
599 #include <GLUT/glut.h>
602 cpSpaceHashRenderDebug(cpSpatialIndex *index)
604 if(index->klass != &klass){
605 cpAssertWarn(cpFalse, "Ignoring cpSpaceHashRenderDebug() call to non-spatial hash spatial index.");
609 cpSpaceHash *hash = (cpSpaceHash *)index;
610 cpBB bb = cpBBNew(-320, -240, 320, 240);
612 cpFloat dim = hash->celldim;
613 int n = hash->numcells;
615 int l = (int)floor(bb.l/dim);
616 int r = (int)floor(bb.r/dim);
617 int b = (int)floor(bb.b/dim);
618 int t = (int)floor(bb.t/dim);
620 for(int i=l; i<=r; i++){
621 for(int j=b; j<=t; j++){
624 int index = hash_func(i,j,n);
625 for(cpSpaceHashBin *bin = hash->table[index]; bin; bin = bin->next)
628 GLfloat v = 1.0f - (GLfloat)cell_count/10.0f;
630 glRectf(i*dim, j*dim, (i + 1)*dim, (j + 1)*dim);