[dali_2.3.21] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-physics / third-party / chipmunk2d / objectivec / src / ChipmunkSpace.m
1 /* Copyright (c) 2013 Scott Lembcke and Howling Moon Software
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 #import "ObjectiveChipmunk/ObjectiveChipmunk.h"
23 #import "chipmunk/chipmunk_private.h"
24 #import "chipmunk/cpHastySpace.h"
25
26 #import <objc/message.h>
27 #import <TargetConditionals.h>
28
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>
32
33 @property(nonatomic, retain) NSArray *chipmunkObjects;
34
35 @end
36
37 @implementation NSArrayChipmunkObject
38
39 @synthesize chipmunkObjects = _chipmunkObjects;
40
41 -(id)initWithArray:(NSArray *)objects {
42         if((self = [super init])){
43                 self.chipmunkObjects = objects;
44         }
45         
46         return self;
47 }
48
49 -(NSUInteger)count
50 {
51         return [_chipmunkObjects count];
52 }
53
54 -(id)objectAtIndex:(NSUInteger)index
55 {
56         return [_chipmunkObjects objectAtIndex:index];
57 }
58
59 @end
60
61 @implementation NSArray(ChipmunkObject)
62
63 -(id<NSFastEnumeration>)chipmunkObjects
64 {
65         return self;
66 }
67
68 @end
69
70
71 // Private class used to wrap the statically allocated staticBody attached to each space.
72 @interface _ChipmunkStaticBodySingleton : ChipmunkBody {
73         cpBody *_bodyPtr;
74         ChipmunkSpace *space; // weak ref
75 }
76
77 @end
78
79 typedef struct HandlerContext {
80         ChipmunkSpace *space;
81         id delegate;
82         cpCollisionType typeA, typeB;
83         SEL beginSelector;
84         SEL preSolveSelector;
85         SEL postSolveSelector;
86         SEL separateSelector;
87 } HandlerContext;
88
89 @implementation ChipmunkSpace
90
91 +(ChipmunkSpace *)spaceFromCPSpace:(cpSpace *)space
92 {       
93         ChipmunkSpace *obj = space->userData;
94         cpAssertHard([obj isKindOfClass:[ChipmunkSpace class]], "'space->data' is not a pointer to a ChipmunkSpace object.");
95         
96         return obj;
97 }
98
99 +(instancetype)allocWithZone:(struct _NSZone *)zone
100 {
101     Class class = self;
102 #if CHIPMUNK_SPACE_USE_HASTY_SPACE
103     if (self == [ChipmunkSpace class]) {
104         class = [ChipmunkHastySpace class];
105     }
106 #endif
107
108         return NSAllocateObject(class, 0, zone);
109 }
110
111 - (id)initWithSpace:(cpSpace *)space
112 {
113         if((self = [super init])){
114                 _children = [[NSMutableSet alloc] init];
115                 _handlers = [[NSMutableArray alloc] init];
116                 
117                 _space = space;
118                 cpSpaceSetUserData(_space, self);
119                 
120                 _staticBody = [[ChipmunkBody alloc] initWithMass:0.0f andMoment:0.0f];
121                 _staticBody.type = CP_BODY_TYPE_STATIC;
122                 cpSpaceSetStaticBody(_space, _staticBody.body);
123         }
124         
125         return self;
126 }
127
128 - (id)init {
129         return [self initWithSpace:cpSpaceNew()];
130 }
131
132 -(void)freeSpace
133 {
134         cpSpaceFree(_space);
135 }
136
137 - (void) dealloc {
138         [self freeSpace];
139         [_staticBody release];
140         
141         [_children release];
142         [_handlers release];
143         
144         [super dealloc];
145 }
146
147 - (cpSpace *)space {return _space;}
148
149 @synthesize userData = _userData;
150
151 // accessor macros
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)
159
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);
169
170 - (BOOL)isLocked {return cpSpaceIsLocked(_space);}
171 - (BOOL)locked {return self.isLocked;}
172
173 - (ChipmunkBody *)staticBody {return _staticBody;}
174
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);}
177
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);}
180
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);}
183
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);}
186
187 // TODO handlers are never filtered.
188
189 - (void)setDefaultCollisionHandler:(id)delegate
190         begin:(SEL)begin
191         preSolve:(SEL)preSolve
192         postSolve:(SEL)postSolve
193         separate:(SEL)separate
194 {
195         cpCollisionType sentinel = (cpCollisionType)@"DEFAULT";
196         
197         HandlerContext context = {self, delegate, sentinel, sentinel, begin, preSolve, postSolve, separate};
198         NSData *data = [NSData dataWithBytes:&context length:sizeof(context)];
199         [_handlers addObject:data];
200         
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];
207 }
208         
209 - (void)addCollisionHandler:(id)delegate
210         typeA:(cpCollisionType)a typeB:(cpCollisionType)b
211         begin:(SEL)begin
212         preSolve:(SEL)preSolve
213         postSolve:(SEL)postSolve
214         separate:(SEL)separate
215 {
216         HandlerContext context = {self, delegate, a, b, begin, preSolve, postSolve, separate};
217         NSData *data = [NSData dataWithBytes:&context length:sizeof(context)];
218         [_handlers addObject:data];
219         
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];
226 }
227
228 - (id)add:(NSObject<ChipmunkObject> *)obj
229 {
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];
234         } else {
235                 [NSException raise:@"NSArgumentError" format:@"Attempted to add an object of type %@ to a ChipmunkSpace.", [obj class]];
236         }
237         
238         [_children addObject:obj];
239         return obj;
240 }
241
242 - (id)remove:(NSObject<ChipmunkObject> *)obj
243 {
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];
248         } else {
249                 [NSException raise:@"NSArgumentError" format:@"Attempted to remove an object of type %@ from a ChipmunkSpace.", [obj class]];
250         }
251         
252         [_children removeObject:obj];
253         return obj;
254 }
255
256 -(BOOL)contains:(NSObject<ChipmunkObject> *)obj
257 {
258         return [_children containsObject:obj];
259 }
260
261 - (NSObject<ChipmunkObject> *)smartAdd:(NSObject<ChipmunkObject> *)obj
262 {
263         if(cpSpaceIsLocked(_space)){
264                 [self addPostStepAddition:obj];
265         } else {
266                 [self add:obj];
267         }
268         
269         return obj;
270 }
271
272 - (NSObject<ChipmunkObject> *)smartRemove:(NSObject<ChipmunkObject> *)obj
273 {
274         if(cpSpaceIsLocked(_space)){
275                 [self addPostStepRemoval:obj];
276         } else {
277                 [self remove:obj];
278         }
279         
280         return obj;
281 }
282
283 struct PostStepTargetContext {
284         id target;
285         SEL selector;
286 };
287
288 static void
289 postStepPerform(cpSpace *unused, id key, struct PostStepTargetContext *context)
290 {
291         [context->target performSelector:context->selector withObject:key];
292         
293         [context->target release];
294         cpfree(context);
295         [key release];
296 }
297
298 - (BOOL)addPostStepCallback:(id)target selector:(SEL)selector key:(id)key
299 {
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);
304                 
305                 [target retain];
306                 [key retain];
307                 
308                 return TRUE;
309         } else {
310                 return FALSE;
311         }
312 }
313
314 static void
315 postStepPerformBlock(cpSpace *unused, id key, ChipmunkPostStepBlock block)
316 {
317         block();
318         
319         [block release];
320         [key release];
321 }
322
323 - (BOOL)addPostStepBlock:(ChipmunkPostStepBlock)block key:(id)key
324 {
325         if(!cpSpaceGetPostStepCallback(_space, key)){
326                 cpSpaceAddPostStepCallback(_space, (cpPostStepFunc)postStepPerformBlock, key, [block copy]);
327                 
328                 [key retain];
329                 
330                 return TRUE;
331         } else {
332                 return FALSE;
333         }
334 }
335
336 - (void)addPostStepAddition:(NSObject<ChipmunkObject> *)obj
337 {
338         [self addPostStepCallback:self selector:@selector(add:) key:obj];
339 }
340
341 - (void)addPostStepRemoval:(NSObject<ChipmunkObject> *)obj
342 {
343         [self addPostStepCallback:self selector:@selector(remove:) key:obj];
344 }
345
346 - (NSArray *)pointQueryAll:(cpVect)point maxDistance:(cpFloat)maxDistance filter:(cpShapeFilter)filter
347 {
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];
352                 [info release];
353         });
354         
355         return array;
356 }
357
358 - (ChipmunkPointQueryInfo *)pointQueryNearest:(cpVect)point maxDistance:(cpFloat)maxDistance filter:(cpShapeFilter)filter
359 {
360         cpPointQueryInfo info;
361         cpSpacePointQueryNearest(_space, point, maxDistance, filter, &info);
362         return (info.shape ? [[[ChipmunkPointQueryInfo alloc] initWithInfo:&info] autorelease] : nil);
363 }
364
365 typedef struct segmentQueryContext {
366         cpVect start, end;
367         NSMutableArray *array;
368 } segmentQueryContext;
369
370 - (NSArray *)segmentQueryAllFrom:(cpVect)start to:(cpVect)end radius:(cpFloat)radius filter:(cpShapeFilter)filter
371 {
372         NSMutableArray *array = [NSMutableArray array];
373         cpSpaceSegmentQuery_b(_space, start, end, radius, filter, ^(cpShape *shape, cpVect p, cpVect n, cpFloat t){
374                 // TODO point
375                 ChipmunkSegmentQueryInfo *info = [[ChipmunkSegmentQueryInfo alloc] initWithInfo:&(cpSegmentQueryInfo){shape, p, n, t} start:start end:end];
376                 [array addObject:info];
377                 [info release];
378         });
379         
380         return array;
381 }
382
383 - (ChipmunkSegmentQueryInfo *)segmentQueryFirstFrom:(cpVect)start to:(cpVect)end radius:(cpFloat)radius filter:(cpShapeFilter)filter
384 {
385         cpSegmentQueryInfo info;
386         cpSpaceSegmentQueryFirst(_space, start, end, radius, filter, &info);
387         
388         return (info.shape ? [[[ChipmunkSegmentQueryInfo alloc] initWithInfo:&info start:start end:end] autorelease] : nil);
389 }
390
391 - (NSArray *)bbQueryAll:(cpBB)bb filter:(cpShapeFilter)filter
392 {
393         NSMutableArray *array = [NSMutableArray array];
394         cpSpaceBBQuery_b(_space, bb, filter, ^(cpShape *shape){
395                 [array addObject:shape->userData];
396         });
397         
398         return array;
399 }
400
401 //static void
402 //shapeQueryAll(cpShape *shape, cpContactPointSet *points, NSMutableArray *array)
403 //{
404 //      ChipmunkShapeQueryInfo *info = [[ChipmunkShapeQueryInfo alloc] initWithShape:shape->userData andPoints:points];
405 //      [array addObject:info];
406 //      [info release];
407 //}
408
409 - (NSArray *)shapeQueryAll:(ChipmunkShape *)shape
410 {
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];
415                 [info release];
416         });
417         
418         return array;
419 }
420
421 - (BOOL)shapeTest:(ChipmunkShape *)shape
422 {
423         return cpSpaceShapeQuery(_space, shape.shape, NULL, NULL);
424 }
425
426 static void PushBody(cpBody *body, NSMutableArray *arr){[arr addObject:body->userData];}
427 - (NSArray *)bodies
428 {
429         NSMutableArray *arr = [NSMutableArray array];
430         cpSpaceEachBody(_space, (cpSpaceBodyIteratorFunc)PushBody, arr);
431         
432         return arr;
433 }
434
435 static void PushShape(cpShape *shape, NSMutableArray *arr){[arr addObject:shape->userData];}
436 - (NSArray *)shapes
437 {
438         NSMutableArray *arr = [NSMutableArray array];
439         cpSpaceEachShape(_space, (cpSpaceShapeIteratorFunc)PushShape, arr);
440         
441         return arr;
442 }
443
444 static void PushConstraint(cpConstraint *constraint, NSMutableArray *arr){[arr addObject:constraint->userData];}
445 - (NSArray *)constraints
446 {
447         NSMutableArray *arr = [NSMutableArray array];
448         cpSpaceEachConstraint(_space, (cpSpaceConstraintIteratorFunc)PushConstraint, arr);
449         
450         return arr;
451 }
452
453
454 - (void)reindexStatic
455 {
456         cpSpaceReindexStatic(_space);
457 }
458
459 - (void)reindexShape:(ChipmunkShape *)shape
460 {
461         cpSpaceReindexShape(_space, shape.shape);
462 }
463
464 - (void)reindexShapesForBody:(ChipmunkBody *)body
465 {
466         cpSpaceReindexShapesForBody(_space, body.body);
467 }
468
469 - (void)step:(cpFloat)dt
470 {
471         cpSpaceStep(_space, dt);
472 }
473
474 //MARK: Extras
475
476 - (ChipmunkBody *)addBody:(ChipmunkBody *)obj {
477         cpSpaceAddBody(_space, obj.body);
478         [_children addObject:obj];
479         return obj;
480 }
481
482 - (ChipmunkBody *)removeBody:(ChipmunkBody *)obj {
483         cpSpaceRemoveBody(_space, obj.body);
484         [_children removeObject:obj];
485         return obj;
486 }
487
488
489 - (ChipmunkShape *)addShape:(ChipmunkShape *)obj {
490         cpSpaceAddShape(_space, obj.shape);
491         [_children addObject:obj];
492         return obj;
493 }
494
495 - (ChipmunkShape *)removeShape:(ChipmunkShape *)obj {
496         cpSpaceRemoveShape(_space, obj.shape);
497         [_children removeObject:obj];
498         return obj;
499 }
500
501 - (ChipmunkConstraint *)addConstraint:(ChipmunkConstraint *)obj {
502         cpSpaceAddConstraint(_space, obj.constraint);
503         [_children addObject:obj];
504         return obj;
505 }
506
507 - (ChipmunkConstraint *)removeConstraint:(ChipmunkConstraint *)obj {
508         cpSpaceRemoveConstraint(_space, obj.constraint);
509         [_children removeObject:obj];
510         return obj;
511 }
512
513 static ChipmunkSegmentShape *
514 boundSeg(ChipmunkBody *body, cpVect a, cpVect b, cpFloat radius, cpFloat elasticity,cpFloat friction, cpShapeFilter filter, cpCollisionType collisionType)
515 {
516         ChipmunkSegmentShape *seg = [ChipmunkSegmentShape segmentWithBody:body from:a to:b radius:radius];
517         seg.elasticity = elasticity;
518         seg.friction = friction;
519         seg.filter = filter;
520         seg.collisionType = collisionType;
521         
522         return seg;
523 }
524
525 - (NSArray *)addBounds:(cpBB)bounds thickness:(cpFloat)radius
526         elasticity:(cpFloat)elasticity friction:(cpFloat)friction
527         filter:(cpShapeFilter)filter collisionType:(cpCollisionType)collisionType
528 {
529         cpFloat l = bounds.l - radius;
530         cpFloat b = bounds.b - radius;
531         cpFloat r = bounds.r + radius;
532         cpFloat t = bounds.t + radius;
533         
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),
539                 nil
540         ]];
541         
542         [self add:segs];
543         return [segs autorelease];
544 }
545
546 @end
547
548
549 @implementation ChipmunkHastySpace
550
551 - (id)init {
552         return [self initWithSpace:cpHastySpaceNew()];
553 }
554
555 -(void)freeSpace
556 {
557         cpHastySpaceFree(_space);
558 }
559
560 - (void)step:(cpFloat)dt
561 {
562         cpHastySpaceStep(_space, dt);
563 }
564
565 -(NSUInteger)threads
566 {
567         return cpHastySpaceGetThreads(_space);
568 }
569
570 -(void)setThreads:(NSUInteger)threads
571 {
572         cpHastySpaceSetThreads(_space, threads);
573 }
574
575 @end