chipmunk 5.3.4.0-x86-mingw32 → 5.3.4.2-x86-mingw32
Sign up to get free protection for your applications and to get access to all the features.
- data/README +33 -13
- data/Rakefile +20 -7
- data/ext/chipmunk/extconf.rb +55 -12
- data/ext/chipmunk/rb_chipmunk.c +92 -98
- data/ext/chipmunk/rb_chipmunk.h +44 -34
- data/ext/chipmunk/rb_cpArbiter.c +135 -98
- data/ext/chipmunk/rb_cpBB.c +84 -101
- data/ext/chipmunk/rb_cpBody.c +219 -241
- data/ext/chipmunk/rb_cpConstraint.c +173 -185
- data/ext/chipmunk/rb_cpShape.c +347 -251
- data/ext/chipmunk/rb_cpSpace.c +376 -408
- data/ext/chipmunk/rb_cpVect.c +132 -170
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/chipmunk.h +163 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/chipmunk_ffi.h +59 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/chipmunk_private.h +49 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/chipmunk_types.h +151 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/chipmunk_unsafe.h +54 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpConstraint.h +105 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpDampedRotarySpring.h +46 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpDampedSpring.h +53 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpGearJoint.h +41 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpGrooveJoint.h +48 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpPinJoint.h +43 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpPivotJoint.h +42 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpRatchetJoint.h +40 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpRotaryLimitJoint.h +39 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpSimpleMotor.h +37 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/cpSlideJoint.h +44 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/constraints/util.h +134 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpArbiter.h +188 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpArray.h +49 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpBB.h +74 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpBody.h +219 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpCollision.h +28 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpHashSet.h +82 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpPolyShape.h +103 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpShape.h +177 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpSpace.h +206 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpSpaceHash.h +110 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/include/chipmunk/cpVect.h +207 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/chipmunk.c +151 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpConstraint.c +54 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpDampedRotarySpring.c +105 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpDampedSpring.c +115 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpGearJoint.c +113 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpGrooveJoint.c +161 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpPinJoint.c +116 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpPivotJoint.c +114 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpRatchetJoint.c +126 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpRotaryLimitJoint.c +120 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpSimpleMotor.c +97 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/constraints/cpSlideJoint.c +129 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpArbiter.c +280 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpArray.c +143 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpBB.c +47 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpBody.c +192 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpCollision.c +411 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpHashSet.c +253 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpPolyShape.c +240 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpShape.c +405 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpSpace.c +499 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpSpaceComponent.c +279 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpSpaceHash.c +534 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpSpaceQuery.c +246 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpSpaceStep.c +398 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/cpVect.c +71 -0
- data/ext/chipmunk/vendor/chipmunk-5.3.4/src/prime.h +68 -0
- data/lib/1.9/chipmunk.so +0 -0
- data/lib/chipmunk.rb +19 -8
- metadata +84 -42
- data/lib/1.8/chipmunk.so +0 -0
@@ -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
|
+
}
|