chipmunk 5.3.4.0 → 5.3.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. data/README +36 -13
  2. data/Rakefile +23 -9
  3. data/ext/chipmunk/extconf.rb +55 -12
  4. data/ext/chipmunk/rb_chipmunk.c +92 -98
  5. data/ext/chipmunk/rb_chipmunk.h +44 -34
  6. data/ext/chipmunk/rb_cpArbiter.c +135 -98
  7. data/ext/chipmunk/rb_cpBB.c +84 -101
  8. data/ext/chipmunk/rb_cpBody.c +221 -243
  9. data/ext/chipmunk/rb_cpConstraint.c +173 -185
  10. data/ext/chipmunk/rb_cpShape.c +353 -240
  11. data/ext/chipmunk/rb_cpSpace.c +376 -408
  12. data/ext/chipmunk/rb_cpVect.c +135 -173
  13. data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/chipmunk.h +163 -0
  14. data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/chipmunk_ffi.h +59 -0
  15. data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/chipmunk_private.h +49 -0
  16. data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/chipmunk_types.h +151 -0
  17. data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/chipmunk_unsafe.h +54 -0
  18. data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpConstraint.h +105 -0
  19. data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpDampedRotarySpring.h +46 -0
  20. data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpDampedSpring.h +53 -0
  21. data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpGearJoint.h +41 -0
  22. data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpGrooveJoint.h +48 -0
  23. data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpPinJoint.h +43 -0
  24. data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpPivotJoint.h +42 -0
  25. data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpRatchetJoint.h +40 -0
  26. data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpRotaryLimitJoint.h +39 -0
  27. data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpSimpleMotor.h +37 -0
  28. data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpSlideJoint.h +44 -0
  29. data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/util.h +134 -0
  30. data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpArbiter.h +188 -0
  31. data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpArray.h +49 -0
  32. data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpBB.h +74 -0
  33. data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpBody.h +219 -0
  34. data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpCollision.h +28 -0
  35. data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpHashSet.h +82 -0
  36. data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpPolyShape.h +103 -0
  37. data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpShape.h +177 -0
  38. data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpSpace.h +206 -0
  39. data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpSpaceHash.h +110 -0
  40. data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpVect.h +207 -0
  41. data/ext/chipmunk/vendor/chipmunk-5.3.4/src/chipmunk.c +151 -0
  42. data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpConstraint.c +54 -0
  43. data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpDampedRotarySpring.c +105 -0
  44. data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpDampedSpring.c +115 -0
  45. data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpGearJoint.c +113 -0
  46. data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpGrooveJoint.c +161 -0
  47. data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpPinJoint.c +116 -0
  48. data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpPivotJoint.c +114 -0
  49. data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpRatchetJoint.c +126 -0
  50. data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpRotaryLimitJoint.c +120 -0
  51. data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpSimpleMotor.c +97 -0
  52. data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpSlideJoint.c +129 -0
  53. data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpArbiter.c +280 -0
  54. data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpArray.c +143 -0
  55. data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpBB.c +47 -0
  56. data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpBody.c +192 -0
  57. data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpCollision.c +411 -0
  58. data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpHashSet.c +253 -0
  59. data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpPolyShape.c +240 -0
  60. data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpShape.c +405 -0
  61. data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpSpace.c +499 -0
  62. data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpSpaceComponent.c +279 -0
  63. data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpSpaceHash.c +534 -0
  64. data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpSpaceQuery.c +246 -0
  65. data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpSpaceStep.c +398 -0
  66. data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpVect.c +71 -0
  67. data/ext/chipmunk/vendor/chipmunk-5.3.4/src/prime.h +68 -0
  68. data/lib/chipmunk.rb +19 -8
  69. metadata +85 -38
@@ -0,0 +1,279 @@
1
+ /* Copyright (c) 2007 Scott Lembcke
2
+ *
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
+ *
10
+ * The above copyright notice and this permission notice shall be included in
11
+ * all copies or substantial portions of the Software.
12
+ *
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
19
+ * SOFTWARE.
20
+ */
21
+
22
+ #include <stdlib.h>
23
+
24
+ #include "chipmunk_private.h"
25
+
26
+ #pragma mark Sleeping Functions
27
+
28
+ // Chipmunk uses a data structure called a disjoint set forest.
29
+ // My attempts to find a way to splice circularly linked lists in
30
+ // constant time failed, and so I found this neat data structure instead.
31
+
32
+ static inline cpBody *
33
+ componentNodeRoot(cpBody *body)
34
+ {
35
+ cpBody *parent = body->node.parent;
36
+
37
+ if(parent){
38
+ // path compression, attaches this node directly to the root
39
+ return (body->node.parent = componentNodeRoot(parent));
40
+ } else {
41
+ return body;
42
+ }
43
+ }
44
+
45
+ static inline void
46
+ componentNodeMerge(cpBody *a_root, cpBody *b_root)
47
+ {
48
+ if(a_root->node.rank < b_root->node.rank){
49
+ a_root->node.parent = b_root;
50
+ } else if(a_root->node.rank > b_root->node.rank){
51
+ b_root->node.parent = a_root;
52
+ } else if(a_root != b_root){
53
+ b_root->node.parent = a_root;
54
+ a_root->node.rank++;
55
+ }
56
+ }
57
+
58
+ void
59
+ cpSpaceActivateBody(cpSpace *space, cpBody *body)
60
+ {
61
+ if(space->locked){
62
+ // cpSpaceActivateBody() is called again once the space is unlocked
63
+ cpArrayPush(space->rousedBodies, body);
64
+ } else {
65
+ cpArrayPush(space->bodies, body);
66
+ for(cpShape *shape=body->shapesList; shape; shape=shape->next){
67
+ cpSpaceHashRemove(space->staticShapes, shape, shape->hashid);
68
+ cpSpaceHashInsert(space->activeShapes, shape, shape->hashid, shape->bb);
69
+ }
70
+ }
71
+ }
72
+
73
+ static inline void
74
+ componentActivate(cpBody *root)
75
+ {
76
+ if(!cpBodyIsSleeping(root)) return;
77
+
78
+ cpSpace *space = root->space;
79
+ cpAssert(space, "Trying to activate a body that was never added to a space.");
80
+
81
+ cpBody *body = root, *next;
82
+ do {
83
+ next = body->node.next;
84
+
85
+ cpComponentNode node = {NULL, NULL, 0, 0.0f};
86
+ body->node = node;
87
+
88
+ cpSpaceActivateBody(space, body);
89
+ } while((body = next) != root);
90
+
91
+ cpArrayDeleteObj(space->sleepingComponents, root);
92
+ }
93
+
94
+ void
95
+ cpBodyActivate(cpBody *body)
96
+ {
97
+ componentActivate(componentNodeRoot(body));
98
+ }
99
+
100
+ static inline void
101
+ mergeBodies(cpSpace *space, cpArray *components, cpArray *rogueBodies, cpBody *a, cpBody *b)
102
+ {
103
+ // Ignore connections to static bodies
104
+ if(cpBodyIsStatic(a) || cpBodyIsStatic(b)) return;
105
+
106
+ cpBody *a_root = componentNodeRoot(a);
107
+ cpBody *b_root = componentNodeRoot(b);
108
+
109
+ cpBool a_sleep = cpBodyIsSleeping(a_root);
110
+ cpBool b_sleep = cpBodyIsSleeping(b_root);
111
+
112
+ if(a_sleep && b_sleep){
113
+ return;
114
+ } else if(a_sleep || b_sleep){
115
+ componentActivate(a_root);
116
+ componentActivate(b_root);
117
+ }
118
+
119
+ // Add any rogue bodies found to the list and reset the idle time of anything they touch.
120
+ if(cpBodyIsRogue(a)){ cpArrayPush(rogueBodies, a); b->node.idleTime = 0.0f; }
121
+ if(cpBodyIsRogue(b)){ cpArrayPush(rogueBodies, b); a->node.idleTime = 0.0f; }
122
+
123
+ componentNodeMerge(a_root, b_root);
124
+ }
125
+
126
+ static inline cpBool
127
+ componentActive(cpBody *root, cpFloat threshold)
128
+ {
129
+ cpBody *body = root, *next;
130
+ do {
131
+ next = body->node.next;
132
+ if(body->node.idleTime < threshold) return cpTrue;
133
+ } while((body = next) != root);
134
+
135
+ return cpFalse;
136
+ }
137
+
138
+ static inline void
139
+ addToComponent(cpBody *body, cpArray *components)
140
+ {
141
+ // Check that the body is not already added to the component list
142
+ if(body->node.next) return;
143
+ cpBody *root = componentNodeRoot(body);
144
+
145
+ cpBody *next = root->node.next;
146
+ if(!next){
147
+ // If the root isn't part of a list yet, then it hasn't been
148
+ // added to the components list. Do that now.
149
+ cpArrayPush(components, root);
150
+ // Start the list
151
+ body->node.next = root;
152
+ root->node.next = body;
153
+ } else if(root != body) {
154
+ // Splice in body after the root.
155
+ body->node.next = next;
156
+ root->node.next = body;
157
+ }
158
+ }
159
+
160
+ // TODO this function needs more commenting.
161
+ void
162
+ cpSpaceProcessComponents(cpSpace *space, cpFloat dt)
163
+ {
164
+ cpArray *bodies = space->bodies;
165
+ cpArray *newBodies = cpArrayNew(bodies->num);
166
+ cpArray *rogueBodies = cpArrayNew(16);
167
+ cpArray *arbiters = space->arbiters;
168
+ cpArray *constraints = space->constraints;
169
+ cpArray *components = cpArrayNew(space->sleepingComponents->num);
170
+
171
+ cpFloat dv = space->idleSpeedThreshold;
172
+ cpFloat dvsq = (dv ? dv*dv : cpvdot(space->gravity, space->gravity)*dt*dt);
173
+
174
+ // update idling
175
+ for(int i=0; i<bodies->num; i++){
176
+ cpBody *body = (cpBody*)bodies->arr[i];
177
+
178
+ cpFloat thresh = (dvsq ? body->m*dvsq : 0.0f);
179
+ body->node.idleTime = (cpBodyKineticEnergy(body) > thresh ? 0.0f : body->node.idleTime + dt);
180
+ }
181
+
182
+ // iterate graph edges and build forests
183
+ for(int i=0; i<arbiters->num; i++){
184
+ cpArbiter *arb = (cpArbiter*)arbiters->arr[i];
185
+ mergeBodies(space, components, rogueBodies, arb->a->body, arb->b->body);
186
+ }
187
+ for(int j=0; j<constraints->num; j++){
188
+ cpConstraint *constraint = (cpConstraint *)constraints->arr[j];
189
+ mergeBodies(space, components, rogueBodies, constraint->a, constraint->b);
190
+ }
191
+
192
+ // iterate bodies and add them to their components
193
+ for(int i=0; i<bodies->num; i++) addToComponent((cpBody*)bodies->arr[i], components);
194
+ for(int i=0; i<rogueBodies->num; i++) addToComponent((cpBody*)rogueBodies->arr[i], components);
195
+
196
+ // iterate components, copy or deactivate
197
+ for(int i=0; i<components->num; i++){
198
+ cpBody *root = (cpBody*)components->arr[i];
199
+ if(componentActive(root, space->sleepTimeThreshold)){
200
+ cpBody *body = root, *next;
201
+ do {
202
+ next = body->node.next;
203
+
204
+ if(!cpBodyIsRogue(body)) cpArrayPush(newBodies, body);
205
+ cpComponentNode node = {NULL, NULL, 0, body->node.idleTime};
206
+ body->node = node;
207
+ } while((body = next) != root);
208
+ } else {
209
+ cpBody *body = root, *next;
210
+ do {
211
+ next = body->node.next;
212
+
213
+ for(cpShape *shape = body->shapesList; shape; shape = shape->next){
214
+ cpSpaceHashRemove(space->activeShapes, shape, shape->hashid);
215
+ cpSpaceHashInsert(space->staticShapes, shape, shape->hashid, shape->bb);
216
+ }
217
+ } while((body = next) != root);
218
+
219
+ cpArrayPush(space->sleepingComponents, root);
220
+ }
221
+ }
222
+
223
+ space->bodies = newBodies;
224
+ cpArrayFree(bodies);
225
+ cpArrayFree(rogueBodies);
226
+ cpArrayFree(components);
227
+ }
228
+
229
+ void
230
+ cpBodySleep(cpBody *body)
231
+ {
232
+ cpBodySleepWithGroup(body, NULL);
233
+ }
234
+
235
+ void
236
+ cpBodySleepWithGroup(cpBody *body, cpBody *group){
237
+ cpAssert(!cpBodyIsStatic(body) && !cpBodyIsRogue(body), "Rogue and static bodies cannot be put to sleep.");
238
+
239
+ cpSpace *space = body->space;
240
+ cpAssert(space, "Cannot put a body to sleep that has not been added to a space.");
241
+ cpAssert(!space->locked, "Bodies can not be put to sleep during a query or a call to cpSpaceSte(). Put these calls into a post-step callback.");
242
+ cpAssert(!group || cpBodyIsSleeping(group), "Cannot use a non-sleeping body as a group identifier.");
243
+
244
+ if(cpBodyIsSleeping(body)) return;
245
+
246
+ for(cpShape *shape = body->shapesList; shape; shape = shape->next){
247
+ cpShapeCacheBB(shape);
248
+ cpSpaceHashRemove(space->activeShapes, shape, shape->hashid);
249
+ cpSpaceHashInsert(space->staticShapes, shape, shape->hashid, shape->bb);
250
+ }
251
+
252
+ if(group){
253
+ cpBody *root = componentNodeRoot(group);
254
+
255
+ cpComponentNode node = {root, root->node.next, 0, 0.0f};
256
+ body->node = node;
257
+ root->node.next = body;
258
+ } else {
259
+ cpComponentNode node = {NULL, body, 0, 0.0f};
260
+ body->node = node;
261
+
262
+ cpArrayPush(space->sleepingComponents, body);
263
+ }
264
+
265
+ cpArrayDeleteObj(space->bodies, body);
266
+ }
267
+
268
+ static void
269
+ activateTouchingHelper(cpShape *shape, cpContactPointSet *points, cpArray **bodies){
270
+ cpBodyActivate(shape->body);
271
+ }
272
+
273
+ void
274
+ cpSpaceActivateShapesTouchingShape(cpSpace *space, cpShape *shape){
275
+ cpArray *bodies = NULL;
276
+ cpSpaceShapeQuery(space, shape, (cpSpaceShapeQueryFunc)activateTouchingHelper, &bodies);
277
+ }
278
+
279
+
@@ -0,0 +1,534 @@
1
+ /* Copyright (c) 2007 Scott Lembcke
2
+ *
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
+ *
10
+ * The above copyright notice and this permission notice shall be included in
11
+ * all copies or substantial portions of the Software.
12
+ *
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
19
+ * SOFTWARE.
20
+ */
21
+
22
+ #include <math.h>
23
+ #include <stdlib.h>
24
+
25
+ #include "chipmunk_private.h"
26
+ #include "prime.h"
27
+
28
+ static cpHandle*
29
+ cpHandleInit(cpHandle *hand, void *obj)
30
+ {
31
+ hand->obj = obj;
32
+ hand->retain = 0;
33
+ hand->stamp = 0;
34
+
35
+ return hand;
36
+ }
37
+
38
+ static inline void cpHandleRetain(cpHandle *hand){hand->retain++;}
39
+
40
+ static inline void
41
+ cpHandleRelease(cpHandle *hand, cpArray *pooledHandles)
42
+ {
43
+ hand->retain--;
44
+ if(hand->retain == 0) cpArrayPush(pooledHandles, hand);
45
+ }
46
+
47
+ cpSpaceHash*
48
+ cpSpaceHashAlloc(void)
49
+ {
50
+ return (cpSpaceHash *)cpcalloc(1, sizeof(cpSpaceHash));
51
+ }
52
+
53
+ // Frees the old table, and allocate a new one.
54
+ static void
55
+ cpSpaceHashAllocTable(cpSpaceHash *hash, int numcells)
56
+ {
57
+ cpfree(hash->table);
58
+
59
+ hash->numcells = numcells;
60
+ hash->table = (cpSpaceHashBin **)cpcalloc(numcells, sizeof(cpSpaceHashBin *));
61
+ }
62
+
63
+ // Equality function for the handleset.
64
+ static int handleSetEql(void *obj, cpHandle *hand){return (obj == hand->obj);}
65
+
66
+ // Transformation function for the handleset.
67
+ static void *
68
+ handleSetTrans(void *obj, cpSpaceHash *hash)
69
+ {
70
+ if(hash->pooledHandles->num == 0){
71
+ // handle pool is exhausted, make more
72
+ int count = CP_BUFFER_BYTES/sizeof(cpHandle);
73
+ cpAssert(count, "Buffer size is too small.");
74
+
75
+ cpHandle *buffer = (cpHandle *)cpmalloc(CP_BUFFER_BYTES);
76
+ cpArrayPush(hash->allocatedBuffers, buffer);
77
+
78
+ for(int i=0; i<count; i++) cpArrayPush(hash->pooledHandles, buffer + i);
79
+ }
80
+
81
+ cpHandle *hand = cpHandleInit((cpHandle *) cpArrayPop(hash->pooledHandles), obj);
82
+ cpHandleRetain(hand);
83
+
84
+ return hand;
85
+ }
86
+
87
+ cpSpaceHash*
88
+ cpSpaceHashInit(cpSpaceHash *hash, cpFloat celldim, int numcells, cpSpaceHashBBFunc bbfunc)
89
+ {
90
+ cpSpaceHashAllocTable(hash, next_prime(numcells));
91
+ hash->celldim = celldim;
92
+ hash->bbfunc = bbfunc;
93
+
94
+ hash->handleSet = cpHashSetNew(0, (cpHashSetEqlFunc)handleSetEql, (cpHashSetTransFunc)handleSetTrans);
95
+ hash->pooledHandles = cpArrayNew(0);
96
+
97
+ hash->pooledBins = NULL;
98
+ hash->allocatedBuffers = cpArrayNew(0);
99
+
100
+ hash->stamp = 1;
101
+
102
+ return hash;
103
+ }
104
+
105
+ cpSpaceHash*
106
+ cpSpaceHashNew(cpFloat celldim, int cells, cpSpaceHashBBFunc bbfunc)
107
+ {
108
+ return cpSpaceHashInit(cpSpaceHashAlloc(), celldim, cells, bbfunc);
109
+ }
110
+
111
+ static inline void
112
+ recycleBin(cpSpaceHash *hash, cpSpaceHashBin *bin)
113
+ {
114
+ bin->next = hash->pooledBins;
115
+ hash->pooledBins = bin;
116
+ }
117
+
118
+ static inline void
119
+ clearHashCell(cpSpaceHash *hash, int idx)
120
+ {
121
+ cpSpaceHashBin *bin = hash->table[idx];
122
+ while(bin){
123
+ cpSpaceHashBin *next = bin->next;
124
+
125
+ cpHandleRelease(bin->handle, hash->pooledHandles);
126
+ recycleBin(hash, bin);
127
+
128
+ bin = next;
129
+ }
130
+
131
+ hash->table[idx] = NULL;
132
+ }
133
+
134
+ // Clear all cells in the hashtable.
135
+ static void
136
+ clearHash(cpSpaceHash *hash)
137
+ {
138
+ for(int i=0; i<hash->numcells; i++)
139
+ clearHashCell(hash, i);
140
+ }
141
+
142
+ static void freeWrap(void *ptr, void *unused){cpfree(ptr);}
143
+
144
+ void
145
+ cpSpaceHashDestroy(cpSpaceHash *hash)
146
+ {
147
+ clearHash(hash);
148
+
149
+ cpHashSetFree(hash->handleSet);
150
+
151
+ cpArrayEach(hash->allocatedBuffers, freeWrap, NULL);
152
+ cpArrayFree(hash->allocatedBuffers);
153
+ cpArrayFree(hash->pooledHandles);
154
+
155
+ cpfree(hash->table);
156
+ }
157
+
158
+ void
159
+ cpSpaceHashFree(cpSpaceHash *hash)
160
+ {
161
+ if(hash){
162
+ cpSpaceHashDestroy(hash);
163
+ cpfree(hash);
164
+ }
165
+ }
166
+
167
+ void
168
+ cpSpaceHashResize(cpSpaceHash *hash, cpFloat celldim, int numcells)
169
+ {
170
+ // Clear the hash to release the old handle locks.
171
+ clearHash(hash);
172
+
173
+ hash->celldim = celldim;
174
+ cpSpaceHashAllocTable(hash, next_prime(numcells));
175
+ }
176
+
177
+ // Return true if the chain contains the handle.
178
+ static inline cpBool
179
+ containsHandle(cpSpaceHashBin *bin, cpHandle *hand)
180
+ {
181
+ while(bin){
182
+ if(bin->handle == hand) return cpTrue;
183
+ bin = bin->next;
184
+ }
185
+
186
+ return cpFalse;
187
+ }
188
+
189
+ // Get a recycled or new bin.
190
+ static inline cpSpaceHashBin *
191
+ getEmptyBin(cpSpaceHash *hash)
192
+ {
193
+ cpSpaceHashBin *bin = hash->pooledBins;
194
+
195
+ if(bin){
196
+ hash->pooledBins = bin->next;
197
+ return bin;
198
+ } else {
199
+ // Pool is exhausted, make more
200
+ int count = CP_BUFFER_BYTES/sizeof(cpSpaceHashBin);
201
+ cpAssert(count, "Buffer size is too small.");
202
+
203
+ cpSpaceHashBin *buffer = (cpSpaceHashBin *)cpmalloc(CP_BUFFER_BYTES);
204
+ cpArrayPush(hash->allocatedBuffers, buffer);
205
+
206
+ // push all but the first one, return the first instead
207
+ for(int i=1; i<count; i++) recycleBin(hash, buffer + i);
208
+ return buffer;
209
+ }
210
+ }
211
+
212
+ // The hash function itself.
213
+ static inline cpHashValue
214
+ hash_func(cpHashValue x, cpHashValue y, cpHashValue n)
215
+ {
216
+ return (x*1640531513ul ^ y*2654435789ul) % n;
217
+ }
218
+
219
+ // Much faster than (int)floor(f)
220
+ // Profiling showed floor() to be a sizable performance hog
221
+ static inline int
222
+ floor_int(cpFloat f)
223
+ {
224
+ int i = (int)f;
225
+ return (f < 0.0f && f != i ? i - 1 : i);
226
+ }
227
+
228
+ static inline void
229
+ hashHandle(cpSpaceHash *hash, cpHandle *hand, cpBB bb)
230
+ {
231
+ // Find the dimensions in cell coordinates.
232
+ cpFloat dim = hash->celldim;
233
+ int l = floor_int(bb.l/dim); // Fix by ShiftZ
234
+ int r = floor_int(bb.r/dim);
235
+ int b = floor_int(bb.b/dim);
236
+ int t = floor_int(bb.t/dim);
237
+
238
+ int n = hash->numcells;
239
+ for(int i=l; i<=r; i++){
240
+ for(int j=b; j<=t; j++){
241
+ int idx = hash_func(i,j,n);
242
+ cpSpaceHashBin *bin = hash->table[idx];
243
+
244
+ // Don't add an object twice to the same cell.
245
+ if(containsHandle(bin, hand)) continue;
246
+
247
+ cpHandleRetain(hand);
248
+ // Insert a new bin for the handle in this cell.
249
+ cpSpaceHashBin *newBin = getEmptyBin(hash);
250
+ newBin->handle = hand;
251
+ newBin->next = bin;
252
+ hash->table[idx] = newBin;
253
+ }
254
+ }
255
+ }
256
+
257
+ void
258
+ cpSpaceHashInsert(cpSpaceHash *hash, void *obj, cpHashValue hashid, cpBB _deprecated_unused)
259
+ {
260
+ cpHandle *hand = (cpHandle *)cpHashSetInsert(hash->handleSet, hashid, obj, hash);
261
+ hashHandle(hash, hand, hash->bbfunc(obj));
262
+ }
263
+
264
+ void
265
+ cpSpaceHashRehashObject(cpSpaceHash *hash, void *obj, cpHashValue hashid)
266
+ {
267
+ cpHandle *hand = (cpHandle *)cpHashSetRemove(hash->handleSet, hashid, obj);
268
+
269
+ if(hand){
270
+ hand->obj = NULL;
271
+ cpHandleRelease(hand, hash->pooledHandles);
272
+
273
+ cpSpaceHashInsert(hash, obj, hashid, cpBBNew(0.0f, 0.0f, 0.0f, 0.0f));
274
+ }
275
+ }
276
+
277
+ static void handleRehashHelper(cpHandle *hand, cpSpaceHash *hash){hashHandle(hash, hand, hash->bbfunc(hand->obj));}
278
+
279
+ void
280
+ cpSpaceHashRehash(cpSpaceHash *hash)
281
+ {
282
+ clearHash(hash);
283
+ cpHashSetEach(hash->handleSet, (cpHashSetIterFunc)handleRehashHelper, hash);
284
+ }
285
+
286
+ void
287
+ cpSpaceHashRemove(cpSpaceHash *hash, void *obj, cpHashValue hashid)
288
+ {
289
+ cpHandle *hand = (cpHandle *)cpHashSetRemove(hash->handleSet, hashid, obj);
290
+
291
+ if(hand){
292
+ hand->obj = NULL;
293
+ cpHandleRelease(hand, hash->pooledHandles);
294
+ }
295
+ }
296
+
297
+ typedef struct eachPair {
298
+ cpSpaceHashIterator func;
299
+ void *data;
300
+ } eachPair;
301
+
302
+ static void eachHelper(cpHandle *hand, eachPair *pair){pair->func(hand->obj, pair->data);}
303
+
304
+ // Iterate over the objects in the spatial hash.
305
+ void
306
+ cpSpaceHashEach(cpSpaceHash *hash, cpSpaceHashIterator func, void *data)
307
+ {
308
+ eachPair pair = {func, data};
309
+ cpHashSetEach(hash->handleSet, (cpHashSetIterFunc)eachHelper, &pair);
310
+ }
311
+
312
+ static inline void
313
+ removeOrphanedHandles(cpSpaceHash *hash, cpSpaceHashBin **bin_ptr)
314
+ {
315
+ cpSpaceHashBin *bin = *bin_ptr;
316
+ while(bin){
317
+ cpHandle *hand = bin->handle;
318
+ cpSpaceHashBin *next = bin->next;
319
+
320
+ if(!hand->obj){
321
+ // orphaned handle, unlink and recycle the bin
322
+ (*bin_ptr) = bin->next;
323
+ recycleBin(hash, bin);
324
+
325
+ cpHandleRelease(hand, hash->pooledHandles);
326
+ } else {
327
+ bin_ptr = &bin->next;
328
+ }
329
+
330
+ bin = next;
331
+ }
332
+ }
333
+
334
+ // Calls the callback function for the objects in a given chain.
335
+ static inline void
336
+ query(cpSpaceHash *hash, cpSpaceHashBin **bin_ptr, void *obj, cpSpaceHashQueryFunc func, void *data)
337
+ {
338
+ restart:
339
+ for(cpSpaceHashBin *bin = *bin_ptr; bin; bin = bin->next){
340
+ cpHandle *hand = bin->handle;
341
+ void *other = hand->obj;
342
+
343
+ if(hand->stamp == hash->stamp || obj == other){
344
+ continue;
345
+ } else if(other){
346
+ func(obj, other, data);
347
+ hand->stamp = hash->stamp;
348
+ } else {
349
+ // The object for this handle has been removed
350
+ // cleanup this cell and restart the query
351
+ removeOrphanedHandles(hash, bin_ptr);
352
+ goto restart; // GCC not smart enough/able to tail call an inlined function.
353
+ }
354
+ }
355
+ }
356
+
357
+ void
358
+ cpSpaceHashPointQuery(cpSpaceHash *hash, cpVect point, cpSpaceHashQueryFunc func, void *data)
359
+ {
360
+ cpFloat dim = hash->celldim;
361
+ int idx = hash_func(floor_int(point.x/dim), floor_int(point.y/dim), hash->numcells); // Fix by ShiftZ
362
+
363
+ query(hash, &hash->table[idx], &point, func, data);
364
+ hash->stamp++;
365
+ }
366
+
367
+ void
368
+ cpSpaceHashQuery(cpSpaceHash *hash, void *obj, cpBB bb, cpSpaceHashQueryFunc func, void *data)
369
+ {
370
+ // Get the dimensions in cell coordinates.
371
+ cpFloat dim = hash->celldim;
372
+ int l = floor_int(bb.l/dim); // Fix by ShiftZ
373
+ int r = floor_int(bb.r/dim);
374
+ int b = floor_int(bb.b/dim);
375
+ int t = floor_int(bb.t/dim);
376
+
377
+ int n = hash->numcells;
378
+ cpSpaceHashBin **table = hash->table;
379
+
380
+ // Iterate over the cells and query them.
381
+ for(int i=l; i<=r; i++){
382
+ for(int j=b; j<=t; j++){
383
+ query(hash, &table[hash_func(i,j,n)], obj, func, data);
384
+ }
385
+ }
386
+
387
+ hash->stamp++;
388
+ }
389
+
390
+ // Similar to struct eachPair above.
391
+ typedef struct queryRehashPair {
392
+ cpSpaceHash *hash;
393
+ cpSpaceHashQueryFunc func;
394
+ void *data;
395
+ } queryRehashPair;
396
+
397
+ // Hashset iterator func used with cpSpaceHashQueryRehash().
398
+ static void
399
+ handleQueryRehashHelper(void *elt, void *data)
400
+ {
401
+ cpHandle *hand = (cpHandle *)elt;
402
+
403
+ // Unpack the user callback data.
404
+ queryRehashPair *pair = (queryRehashPair *)data;
405
+ cpSpaceHash *hash = pair->hash;
406
+ cpSpaceHashQueryFunc func = pair->func;
407
+
408
+ cpFloat dim = hash->celldim;
409
+ int n = hash->numcells;
410
+
411
+ void *obj = hand->obj;
412
+ cpBB bb = hash->bbfunc(obj);
413
+
414
+ int l = floor_int(bb.l/dim);
415
+ int r = floor_int(bb.r/dim);
416
+ int b = floor_int(bb.b/dim);
417
+ int t = floor_int(bb.t/dim);
418
+
419
+ cpSpaceHashBin **table = hash->table;
420
+
421
+ for(int i=l; i<=r; i++){
422
+ for(int j=b; j<=t; j++){
423
+ int idx = hash_func(i,j,n);
424
+ cpSpaceHashBin *bin = table[idx];
425
+
426
+ if(containsHandle(bin, hand)) continue;
427
+
428
+ cpHandleRetain(hand); // this MUST be done first in case the object is removed in func()
429
+ query(hash, &bin, obj, func, pair->data);
430
+
431
+ cpSpaceHashBin *newBin = getEmptyBin(hash);
432
+ newBin->handle = hand;
433
+ newBin->next = bin;
434
+ table[idx] = newBin;
435
+ }
436
+ }
437
+
438
+ // Increment the stamp for each object hashed.
439
+ hash->stamp++;
440
+ }
441
+
442
+ void
443
+ cpSpaceHashQueryRehash(cpSpaceHash *hash, cpSpaceHashQueryFunc func, void *data)
444
+ {
445
+ clearHash(hash);
446
+
447
+ queryRehashPair pair = {hash, func, data};
448
+ cpHashSetEach(hash->handleSet, &handleQueryRehashHelper, &pair);
449
+ }
450
+
451
+ static inline cpFloat
452
+ segmentQuery(cpSpaceHash *hash, cpSpaceHashBin **bin_ptr, void *obj, cpSpaceHashSegmentQueryFunc func, void *data)
453
+ {
454
+ cpFloat t = 1.0f;
455
+
456
+ restart:
457
+ for(cpSpaceHashBin *bin = *bin_ptr; bin; bin = bin->next){
458
+ cpHandle *hand = bin->handle;
459
+ void *other = hand->obj;
460
+
461
+ // Skip over certain conditions
462
+ if(hand->stamp == hash->stamp){
463
+ continue;
464
+ } else if(other){
465
+ t = cpfmin(t, func(obj, other, data));
466
+ hand->stamp = hash->stamp;
467
+ } else {
468
+ // The object for this handle has been removed
469
+ // cleanup this cell and restart the query
470
+ removeOrphanedHandles(hash, bin_ptr);
471
+ goto restart; // GCC not smart enough/able to tail call an inlined function.
472
+ }
473
+ }
474
+
475
+ return t;
476
+ }
477
+
478
+ // modified from http://playtechs.blogspot.com/2007/03/raytracing-on-grid.html
479
+ void cpSpaceHashSegmentQuery(cpSpaceHash *hash, void *obj, cpVect a, cpVect b, cpFloat t_exit, cpSpaceHashSegmentQueryFunc func, void *data)
480
+ {
481
+ a = cpvmult(a, 1.0f/hash->celldim);
482
+ b = cpvmult(b, 1.0f/hash->celldim);
483
+
484
+ int cell_x = floor_int(a.x), cell_y = floor_int(a.y);
485
+
486
+ cpFloat t = 0;
487
+
488
+ int x_inc, y_inc;
489
+ cpFloat temp_v, temp_h;
490
+
491
+ if (b.x > a.x){
492
+ x_inc = 1;
493
+ temp_h = (cpffloor(a.x + 1.0f) - a.x);
494
+ } else {
495
+ x_inc = -1;
496
+ temp_h = (a.x - cpffloor(a.x));
497
+ }
498
+
499
+ if (b.y > a.y){
500
+ y_inc = 1;
501
+ temp_v = (cpffloor(a.y + 1.0f) - a.y);
502
+ } else {
503
+ y_inc = -1;
504
+ temp_v = (a.y - cpffloor(a.y));
505
+ }
506
+
507
+ // Division by zero is *very* slow on ARM
508
+ cpFloat dx = cpfabs(b.x - a.x), dy = cpfabs(b.y - a.y);
509
+ cpFloat dt_dx = (dx ? 1.0f/dx : INFINITY), dt_dy = (dy ? 1.0f/dy : INFINITY);
510
+
511
+ // fix NANs in horizontal directions
512
+ cpFloat next_h = (temp_h ? temp_h*dt_dx : dt_dx);
513
+ cpFloat next_v = (temp_v ? temp_v*dt_dy : dt_dy);
514
+
515
+ cpSpaceHashBin **table = hash->table;
516
+
517
+ int n = hash->numcells;
518
+ while(t < t_exit){
519
+ int idx = hash_func(cell_x, cell_y, n);
520
+ t_exit = cpfmin(t_exit, segmentQuery(hash, &table[idx], obj, func, data));
521
+
522
+ if (next_v < next_h){
523
+ cell_y += y_inc;
524
+ t = next_v;
525
+ next_v += dt_dy;
526
+ } else {
527
+ cell_x += x_inc;
528
+ t = next_h;
529
+ next_h += dt_dx;
530
+ }
531
+ }
532
+
533
+ hash->stamp++;
534
+ }