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 #import "ObjectiveChipmunk/ObjectiveChipmunk.h"
23 #import "chipmunk/chipmunk_private.h"
24 #import "chipmunk/cpHastySpace.h"
26 #import <objc/message.h>
27 #import <TargetConditionals.h>
29 // Just in case the user doesn't have -ObjC in their linker flags.
30 // Annoyingly, this is the case more often than not.
31 @interface NSArrayChipmunkObject : NSArray<ChipmunkObject>
33 @property(nonatomic, retain) NSArray *chipmunkObjects;
37 @implementation NSArrayChipmunkObject
39 @synthesize chipmunkObjects = _chipmunkObjects;
41 -(id)initWithArray:(NSArray *)objects {
42 if((self = [super init])){
43 self.chipmunkObjects = objects;
51 return [_chipmunkObjects count];
54 -(id)objectAtIndex:(NSUInteger)index
56 return [_chipmunkObjects objectAtIndex:index];
61 @implementation NSArray(ChipmunkObject)
63 -(id<NSFastEnumeration>)chipmunkObjects
71 // Private class used to wrap the statically allocated staticBody attached to each space.
72 @interface _ChipmunkStaticBodySingleton : ChipmunkBody {
74 ChipmunkSpace *space; // weak ref
79 typedef struct HandlerContext {
82 cpCollisionType typeA, typeB;
85 SEL postSolveSelector;
89 @implementation ChipmunkSpace
91 +(ChipmunkSpace *)spaceFromCPSpace:(cpSpace *)space
93 ChipmunkSpace *obj = space->userData;
94 cpAssertHard([obj isKindOfClass:[ChipmunkSpace class]], "'space->data' is not a pointer to a ChipmunkSpace object.");
99 +(instancetype)allocWithZone:(struct _NSZone *)zone
102 #if CHIPMUNK_SPACE_USE_HASTY_SPACE
103 if (self == [ChipmunkSpace class]) {
104 class = [ChipmunkHastySpace class];
108 return NSAllocateObject(class, 0, zone);
111 - (id)initWithSpace:(cpSpace *)space
113 if((self = [super init])){
114 _children = [[NSMutableSet alloc] init];
115 _handlers = [[NSMutableArray alloc] init];
118 cpSpaceSetUserData(_space, self);
120 _staticBody = [[ChipmunkBody alloc] initWithMass:0.0f andMoment:0.0f];
121 _staticBody.type = CP_BODY_TYPE_STATIC;
122 cpSpaceSetStaticBody(_space, _staticBody.body);
129 return [self initWithSpace:cpSpaceNew()];
139 [_staticBody release];
147 - (cpSpace *)space {return _space;}
149 @synthesize userData = _userData;
152 #define getter(type, lower, upper) \
153 - (type)lower {return cpSpaceGet##upper(_space);}
154 #define setter(type, lower, upper) \
155 - (void)set##upper:(type)value {cpSpaceSet##upper(_space, value);};
156 #define both(type, lower, upper) \
157 getter(type, lower, upper) \
158 setter(type, lower, upper)
160 both(int, iterations, Iterations);
161 both(cpVect, gravity, Gravity);
162 both(cpFloat, damping, Damping);
163 both(cpFloat, idleSpeedThreshold, IdleSpeedThreshold);
164 both(cpFloat, sleepTimeThreshold, SleepTimeThreshold);
165 both(cpFloat, collisionSlop, CollisionSlop);
166 both(cpFloat, collisionBias, CollisionBias);
167 both(cpTimestamp, collisionPersistence, CollisionPersistence);
168 getter(cpFloat, currentTimeStep, CurrentTimeStep);
170 - (BOOL)isLocked {return cpSpaceIsLocked(_space);}
171 - (BOOL)locked {return self.isLocked;}
173 - (ChipmunkBody *)staticBody {return _staticBody;}
175 typedef BOOL (*BeginProto)(id, SEL, cpArbiter *, ChipmunkSpace *);
176 static bool Begin(cpArbiter *arb, struct cpSpace *space, HandlerContext *ctx){return ((BeginProto)objc_msgSend)(ctx->delegate, ctx->beginSelector, arb, ctx->space);}
178 typedef BOOL (*PreSolveProto)(id, SEL, cpArbiter *, ChipmunkSpace *);
179 static bool PreSolve(cpArbiter *arb, struct cpSpace *space, HandlerContext *ctx){return ((PreSolveProto)objc_msgSend)(ctx->delegate, ctx->preSolveSelector, arb, ctx->space);}
181 typedef void (*PostSolveProto)(id, SEL, cpArbiter *, ChipmunkSpace *);
182 static void PostSolve(cpArbiter *arb, struct cpSpace *space, HandlerContext *ctx){((PostSolveProto)objc_msgSend)(ctx->delegate, ctx->postSolveSelector, arb, ctx->space);}
184 typedef void (*SeparateProto)(id, SEL, cpArbiter *, ChipmunkSpace *);
185 static void Separate(cpArbiter *arb, struct cpSpace *space, HandlerContext *ctx){((SeparateProto)objc_msgSend)(ctx->delegate, ctx->separateSelector, arb, ctx->space);}
187 // TODO handlers are never filtered.
189 - (void)setDefaultCollisionHandler:(id)delegate
191 preSolve:(SEL)preSolve
192 postSolve:(SEL)postSolve
193 separate:(SEL)separate
195 cpCollisionType sentinel = (cpCollisionType)@"DEFAULT";
197 HandlerContext context = {self, delegate, sentinel, sentinel, begin, preSolve, postSolve, separate};
198 NSData *data = [NSData dataWithBytes:&context length:sizeof(context)];
199 [_handlers addObject:data];
201 cpCollisionHandler *handler = cpSpaceAddDefaultCollisionHandler(_space);
202 if(begin) handler->beginFunc = (cpCollisionBeginFunc)Begin;
203 if(preSolve) handler->preSolveFunc = (cpCollisionPreSolveFunc)PreSolve;
204 if(postSolve) handler->postSolveFunc = (cpCollisionPostSolveFunc)PostSolve;
205 if(separate) handler->separateFunc = (cpCollisionSeparateFunc)Separate;
206 handler->userData = (void *)[data bytes];
209 - (void)addCollisionHandler:(id)delegate
210 typeA:(cpCollisionType)a typeB:(cpCollisionType)b
212 preSolve:(SEL)preSolve
213 postSolve:(SEL)postSolve
214 separate:(SEL)separate
216 HandlerContext context = {self, delegate, a, b, begin, preSolve, postSolve, separate};
217 NSData *data = [NSData dataWithBytes:&context length:sizeof(context)];
218 [_handlers addObject:data];
220 cpCollisionHandler *handler = cpSpaceAddCollisionHandler(_space, a, b);
221 if(begin) handler->beginFunc = (cpCollisionBeginFunc)Begin;
222 if(preSolve) handler->preSolveFunc = (cpCollisionPreSolveFunc)PreSolve;
223 if(postSolve) handler->postSolveFunc = (cpCollisionPostSolveFunc)PostSolve;
224 if(separate) handler->separateFunc = (cpCollisionSeparateFunc)Separate;
225 handler->userData = (void *)[data bytes];
228 - (id)add:(NSObject<ChipmunkObject> *)obj
230 if([obj conformsToProtocol:@protocol(ChipmunkBaseObject)]){
231 [(NSObject<ChipmunkBaseObject> *)obj addToSpace:self];
232 } else if([obj conformsToProtocol:@protocol(ChipmunkObject)]){
233 for(NSObject<ChipmunkBaseObject> *child in [obj chipmunkObjects]) [self add:child];
235 [NSException raise:@"NSArgumentError" format:@"Attempted to add an object of type %@ to a ChipmunkSpace.", [obj class]];
238 [_children addObject:obj];
242 - (id)remove:(NSObject<ChipmunkObject> *)obj
244 if([obj conformsToProtocol:@protocol(ChipmunkBaseObject)]){
245 [(NSObject<ChipmunkBaseObject> *)obj removeFromSpace:self];
246 } else if([obj conformsToProtocol:@protocol(ChipmunkObject)]){
247 for(NSObject<ChipmunkBaseObject> *child in [obj chipmunkObjects]) [self remove:child];
249 [NSException raise:@"NSArgumentError" format:@"Attempted to remove an object of type %@ from a ChipmunkSpace.", [obj class]];
252 [_children removeObject:obj];
256 -(BOOL)contains:(NSObject<ChipmunkObject> *)obj
258 return [_children containsObject:obj];
261 - (NSObject<ChipmunkObject> *)smartAdd:(NSObject<ChipmunkObject> *)obj
263 if(cpSpaceIsLocked(_space)){
264 [self addPostStepAddition:obj];
272 - (NSObject<ChipmunkObject> *)smartRemove:(NSObject<ChipmunkObject> *)obj
274 if(cpSpaceIsLocked(_space)){
275 [self addPostStepRemoval:obj];
283 struct PostStepTargetContext {
289 postStepPerform(cpSpace *unused, id key, struct PostStepTargetContext *context)
291 [context->target performSelector:context->selector withObject:key];
293 [context->target release];
298 - (BOOL)addPostStepCallback:(id)target selector:(SEL)selector key:(id)key
300 if(!cpSpaceGetPostStepCallback(_space, key)){
301 struct PostStepTargetContext *context = cpcalloc(1, sizeof(struct PostStepTargetContext));
302 (*context) = (struct PostStepTargetContext){target, selector};
303 cpSpaceAddPostStepCallback(_space, (cpPostStepFunc)postStepPerform, key, context);
315 postStepPerformBlock(cpSpace *unused, id key, ChipmunkPostStepBlock block)
323 - (BOOL)addPostStepBlock:(ChipmunkPostStepBlock)block key:(id)key
325 if(!cpSpaceGetPostStepCallback(_space, key)){
326 cpSpaceAddPostStepCallback(_space, (cpPostStepFunc)postStepPerformBlock, key, [block copy]);
336 - (void)addPostStepAddition:(NSObject<ChipmunkObject> *)obj
338 [self addPostStepCallback:self selector:@selector(add:) key:obj];
341 - (void)addPostStepRemoval:(NSObject<ChipmunkObject> *)obj
343 [self addPostStepCallback:self selector:@selector(remove:) key:obj];
346 - (NSArray *)pointQueryAll:(cpVect)point maxDistance:(cpFloat)maxDistance filter:(cpShapeFilter)filter
348 NSMutableArray *array = [NSMutableArray array];
349 cpSpacePointQuery_b(_space, point, maxDistance, filter, ^(cpShape *shape, cpVect p, cpFloat d, cpVect g){
350 ChipmunkPointQueryInfo *info = [[ChipmunkPointQueryInfo alloc] initWithInfo:&(cpPointQueryInfo){shape, p, d, g}];
351 [array addObject:info];
358 - (ChipmunkPointQueryInfo *)pointQueryNearest:(cpVect)point maxDistance:(cpFloat)maxDistance filter:(cpShapeFilter)filter
360 cpPointQueryInfo info;
361 cpSpacePointQueryNearest(_space, point, maxDistance, filter, &info);
362 return (info.shape ? [[[ChipmunkPointQueryInfo alloc] initWithInfo:&info] autorelease] : nil);
365 typedef struct segmentQueryContext {
367 NSMutableArray *array;
368 } segmentQueryContext;
370 - (NSArray *)segmentQueryAllFrom:(cpVect)start to:(cpVect)end radius:(cpFloat)radius filter:(cpShapeFilter)filter
372 NSMutableArray *array = [NSMutableArray array];
373 cpSpaceSegmentQuery_b(_space, start, end, radius, filter, ^(cpShape *shape, cpVect p, cpVect n, cpFloat t){
375 ChipmunkSegmentQueryInfo *info = [[ChipmunkSegmentQueryInfo alloc] initWithInfo:&(cpSegmentQueryInfo){shape, p, n, t} start:start end:end];
376 [array addObject:info];
383 - (ChipmunkSegmentQueryInfo *)segmentQueryFirstFrom:(cpVect)start to:(cpVect)end radius:(cpFloat)radius filter:(cpShapeFilter)filter
385 cpSegmentQueryInfo info;
386 cpSpaceSegmentQueryFirst(_space, start, end, radius, filter, &info);
388 return (info.shape ? [[[ChipmunkSegmentQueryInfo alloc] initWithInfo:&info start:start end:end] autorelease] : nil);
391 - (NSArray *)bbQueryAll:(cpBB)bb filter:(cpShapeFilter)filter
393 NSMutableArray *array = [NSMutableArray array];
394 cpSpaceBBQuery_b(_space, bb, filter, ^(cpShape *shape){
395 [array addObject:shape->userData];
402 //shapeQueryAll(cpShape *shape, cpContactPointSet *points, NSMutableArray *array)
404 // ChipmunkShapeQueryInfo *info = [[ChipmunkShapeQueryInfo alloc] initWithShape:shape->userData andPoints:points];
405 // [array addObject:info];
409 - (NSArray *)shapeQueryAll:(ChipmunkShape *)shape
411 NSMutableArray *array = [NSMutableArray array];
412 cpSpaceShapeQuery_b(_space, shape.shape, ^(cpShape *shape, cpContactPointSet *points){
413 ChipmunkShapeQueryInfo *info = [[ChipmunkShapeQueryInfo alloc] initWithShape:shape->userData andPoints:points];
414 [array addObject:info];
421 - (BOOL)shapeTest:(ChipmunkShape *)shape
423 return cpSpaceShapeQuery(_space, shape.shape, NULL, NULL);
426 static void PushBody(cpBody *body, NSMutableArray *arr){[arr addObject:body->userData];}
429 NSMutableArray *arr = [NSMutableArray array];
430 cpSpaceEachBody(_space, (cpSpaceBodyIteratorFunc)PushBody, arr);
435 static void PushShape(cpShape *shape, NSMutableArray *arr){[arr addObject:shape->userData];}
438 NSMutableArray *arr = [NSMutableArray array];
439 cpSpaceEachShape(_space, (cpSpaceShapeIteratorFunc)PushShape, arr);
444 static void PushConstraint(cpConstraint *constraint, NSMutableArray *arr){[arr addObject:constraint->userData];}
445 - (NSArray *)constraints
447 NSMutableArray *arr = [NSMutableArray array];
448 cpSpaceEachConstraint(_space, (cpSpaceConstraintIteratorFunc)PushConstraint, arr);
454 - (void)reindexStatic
456 cpSpaceReindexStatic(_space);
459 - (void)reindexShape:(ChipmunkShape *)shape
461 cpSpaceReindexShape(_space, shape.shape);
464 - (void)reindexShapesForBody:(ChipmunkBody *)body
466 cpSpaceReindexShapesForBody(_space, body.body);
469 - (void)step:(cpFloat)dt
471 cpSpaceStep(_space, dt);
476 - (ChipmunkBody *)addBody:(ChipmunkBody *)obj {
477 cpSpaceAddBody(_space, obj.body);
478 [_children addObject:obj];
482 - (ChipmunkBody *)removeBody:(ChipmunkBody *)obj {
483 cpSpaceRemoveBody(_space, obj.body);
484 [_children removeObject:obj];
489 - (ChipmunkShape *)addShape:(ChipmunkShape *)obj {
490 cpSpaceAddShape(_space, obj.shape);
491 [_children addObject:obj];
495 - (ChipmunkShape *)removeShape:(ChipmunkShape *)obj {
496 cpSpaceRemoveShape(_space, obj.shape);
497 [_children removeObject:obj];
501 - (ChipmunkConstraint *)addConstraint:(ChipmunkConstraint *)obj {
502 cpSpaceAddConstraint(_space, obj.constraint);
503 [_children addObject:obj];
507 - (ChipmunkConstraint *)removeConstraint:(ChipmunkConstraint *)obj {
508 cpSpaceRemoveConstraint(_space, obj.constraint);
509 [_children removeObject:obj];
513 static ChipmunkSegmentShape *
514 boundSeg(ChipmunkBody *body, cpVect a, cpVect b, cpFloat radius, cpFloat elasticity,cpFloat friction, cpShapeFilter filter, cpCollisionType collisionType)
516 ChipmunkSegmentShape *seg = [ChipmunkSegmentShape segmentWithBody:body from:a to:b radius:radius];
517 seg.elasticity = elasticity;
518 seg.friction = friction;
520 seg.collisionType = collisionType;
525 - (NSArray *)addBounds:(cpBB)bounds thickness:(cpFloat)radius
526 elasticity:(cpFloat)elasticity friction:(cpFloat)friction
527 filter:(cpShapeFilter)filter collisionType:(cpCollisionType)collisionType
529 cpFloat l = bounds.l - radius;
530 cpFloat b = bounds.b - radius;
531 cpFloat r = bounds.r + radius;
532 cpFloat t = bounds.t + radius;
534 NSArray *segs = [[NSArrayChipmunkObject alloc] initWithArray:[NSArray arrayWithObjects:
535 boundSeg(_staticBody, cpv(l,b), cpv(l,t), radius, elasticity, friction, filter, collisionType),
536 boundSeg(_staticBody, cpv(l,t), cpv(r,t), radius, elasticity, friction, filter, collisionType),
537 boundSeg(_staticBody, cpv(r,t), cpv(r,b), radius, elasticity, friction, filter, collisionType),
538 boundSeg(_staticBody, cpv(r,b), cpv(l,b), radius, elasticity, friction, filter, collisionType),
543 return [segs autorelease];
549 @implementation ChipmunkHastySpace
552 return [self initWithSpace:cpHastySpaceNew()];
557 cpHastySpaceFree(_space);
560 - (void)step:(cpFloat)dt
562 cpHastySpaceStep(_space, dt);
567 return cpHastySpaceGetThreads(_space);
570 -(void)setThreads:(NSUInteger)threads
572 cpHastySpaceSetThreads(_space, threads);