[dali_2.3.21] Merge branch 'devel/master'
[platform/core/uifw/dali-toolkit.git] / dali-physics / third-party / chipmunk2d / objectivec / src / ChipmunkMultiGrab.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 "ChipmunkMultiGrab.h"
23
24
25 // A constraint subclass that tracks a grab point
26 @interface ChipmunkGrab() <ChipmunkObject>
27
28 @property(nonatomic, readwrite) cpVect pos;
29 @property(nonatomic, readonly) NSArray *chipmunkObjects;
30
31 @end
32
33
34 @implementation ChipmunkGrab
35
36 @synthesize pos = _pos;
37 @synthesize chipmunkObjects = _chipmunkObjects;
38 @synthesize grabbedShape = _grabbedShape;
39 @synthesize data = _data;
40
41 static void 
42 GrabPreSolve(cpConstraint *constraint, cpSpace *space)
43 {
44         cpBody *grabBody = cpConstraintGetBodyA(constraint);
45         ChipmunkGrab *grab = [ChipmunkConstraint constraintFromCPConstraint:constraint].userData;
46         cpFloat dt = cpSpaceGetCurrentTimeStep(space);
47         cpFloat coef = cpfpow(grab->_smoothing, dt);
48         
49         // Smooth out the mouse position.
50         cpVect pos = cpvlerp(grab->_pos, cpBodyGetPosition(grabBody), coef);
51         cpBodySetVelocity(grabBody, cpvmult(cpvsub(pos, cpBodyGetPosition(grabBody)), 1.0/dt));
52 //      cpBodySetPosition(grabBody, pos);
53 }
54
55 // Body will be nil if no object was grabbed.
56 -(id)initWithMultiGrab:(ChipmunkMultiGrab *)multiGrab pos:(cpVect)pos nearest:(cpVect)nearest
57         body:(ChipmunkBody *)body grabbedShape:(ChipmunkShape *)grabbedShape
58         chipmunkObjects:(NSArray *)chipmunkObjects
59 {
60         ChipmunkBody *grabBody = [ChipmunkBody kinematicBody];
61         grabBody.position = pos;
62         // TODO the repeated appending is a little silly here.
63         chipmunkObjects = [chipmunkObjects arrayByAddingObject:grabBody];
64         
65         if((self = [super init])){
66                 _pos = pos;
67                 _smoothing = multiGrab.smoothing;
68                 _grabbedShape = grabbedShape;
69                 
70                 if(body){
71                         ChipmunkPivotJoint *pivot = [ChipmunkPivotJoint pivotJointWithBodyA:grabBody bodyB:body anchorA:cpvzero anchorB:[body worldToLocal:nearest]];
72                         pivot.maxForce = multiGrab.grabForce;
73                         pivot.userData = self;
74                         cpConstraintSetPreSolveFunc(pivot.constraint, GrabPreSolve);
75                         chipmunkObjects = [chipmunkObjects arrayByAddingObject:pivot];
76                         
77                         if(grabbedShape){
78                                 cpFloat frictionForce = multiGrab.grabFriction;
79                                 if(frictionForce > 0.0 && (1.0/body.mass + 1.0/grabBody.mass != 0.0)){
80                                         ChipmunkPivotJoint *friction = [ChipmunkPivotJoint pivotJointWithBodyA:grabBody bodyB:body anchorA:cpvzero anchorB:[body worldToLocal:nearest]];
81                                         friction.maxForce = frictionForce;
82                                         friction.maxBias = 0.0;
83                                         chipmunkObjects = [chipmunkObjects arrayByAddingObject:friction];
84                                 }
85                                 
86                                 cpFloat rotaryFriction = multiGrab.grabRotaryFriction;
87                                 if(rotaryFriction > 0.0 && (1.0/body.moment + 1.0/grabBody.moment != 0.0)){
88                                         ChipmunkGearJoint *friction = [ChipmunkGearJoint gearJointWithBodyA:grabBody bodyB:body phase:0.0 ratio:1.0];
89                                         friction.maxForce = rotaryFriction;
90                                         friction.maxBias = 0.0;
91                                         chipmunkObjects = [chipmunkObjects arrayByAddingObject:friction];
92                                 }
93                         }
94                         
95                         _chipmunkObjects = [chipmunkObjects retain];
96                 }
97         }
98         
99         return self;
100 }
101
102 -(void)dealloc
103 {
104         [_chipmunkObjects release]; _chipmunkObjects = nil;
105         [super dealloc];
106 }
107
108 @end
109
110
111 @implementation ChipmunkMultiGrab
112
113 @synthesize grabForce = _grabForce;
114 @synthesize smoothing = _smoothing;
115
116 @synthesize filter = _filter;
117 @synthesize grabFilter = _grabFilter;
118 @synthesize grabSort = _grabSort;
119
120 @synthesize grabFriction = _grabFriction, grabRotaryFriction = _grabRotaryFriction;
121 @synthesize grabRadius = _grabRadius;
122
123 @synthesize pullMode = _pullMode, pushMode = _pushMode;
124
125 @synthesize pushMass = _pushMass;
126 @synthesize pushFriction = _pushFriction, pushElasticity = _pushElasticity;
127 @synthesize pushCollisionType = _pushCollisionType;
128
129 -(id)initForSpace:(ChipmunkSpace *)space withSmoothing:(cpFloat)smoothing withGrabForce:(cpFloat)grabForce
130 {
131         if((self = [super init])){
132                 _space = [space retain];
133                 _grabs = [[NSMutableArray alloc] init];
134                 
135                 _smoothing = smoothing;
136                 _grabForce = grabForce;
137                 
138                 _filter = CP_SHAPE_FILTER_ALL;
139                 
140                 _grabFilter = ^(ChipmunkShape *shape){return (bool)TRUE;};
141                 _grabSort = ^(ChipmunkShape *shape, cpFloat depth){return depth;};
142                 
143                 _pullMode = TRUE;
144                 _pushMode = FALSE;
145         }
146         
147         return self;
148 }
149
150 -(void)dealloc
151 {
152         [_space release];
153         [_grabs release];
154         
155         [super dealloc];
156 }
157
158 // Don't integrate push bodies.
159 static void PushBodyVelocityUpdate(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt){}
160
161 -(ChipmunkGrab *)beginLocation:(cpVect)pos
162 {
163         __block cpFloat min = INFINITY;
164         __block cpVect nearest = pos;
165         __block ChipmunkShape *grabbedShape = nil;
166         
167         if(_pullMode){
168                 cpSpacePointQuery_b(_space.space, pos, _grabRadius, _filter, ^(cpShape *c_shape, cpVect point, cpFloat dist, cpVect gradient){
169                         ChipmunkShape *shape = [ChipmunkShape shapeFromCPShape:c_shape];
170                         cpFloat sort = dist;
171                         
172                         // Call the sorting callback if dist is negative.
173                         // Otherwise just take the nearest shape.
174                         if(dist <= 0.0f){
175                                 sort = -_grabSort(shape, -dist);
176                                 cpAssertWarn(sort <= 0.0f, "You must return a positive value from the sorting callback.");
177                         }
178                         
179                         if(sort < min && cpBodyGetMass(cpShapeGetBody(c_shape)) != INFINITY){
180                                 if(_grabFilter(shape)){
181                                         min = sort;
182                                         nearest = (dist > 0.0 ? point : pos);
183                                         grabbedShape = shape;
184                                 }
185                         }
186                 });
187         }
188         
189         ChipmunkBody *pushBody = nil;
190         NSArray *chipmunkObjects = [NSArray array];
191         
192         if(!grabbedShape && _pushMode){
193                 pushBody = [ChipmunkBody bodyWithMass:_pushMass andMoment:INFINITY];
194                 pushBody.position = pos;
195                 cpBodySetVelocityUpdateFunc(pushBody.body, PushBodyVelocityUpdate);
196                 
197                 ChipmunkShape *pushShape = [ChipmunkCircleShape circleWithBody:pushBody radius:_grabRadius offset:cpvzero];
198                 pushShape.friction = _pushFriction;
199                 pushShape.elasticity = _pushElasticity;
200                 pushShape.filter = _filter;
201                 pushShape.collisionType = _pushCollisionType;
202                 
203                 chipmunkObjects = [NSArray arrayWithObjects:pushBody, pushShape, nil];
204         }
205         
206         ChipmunkBody *grabBody = (grabbedShape ? grabbedShape.body : pushBody);
207         ChipmunkGrab *grab = [[ChipmunkGrab alloc] initWithMultiGrab:self pos:pos nearest:nearest body:grabBody grabbedShape:grabbedShape chipmunkObjects:chipmunkObjects];
208         
209         [_grabs addObject:grab];
210         [_space add:grab];
211         [grab release];
212         
213         return (grab.grabbedShape ? grab : nil);
214 }
215
216 static ChipmunkGrab *
217 BestGrab(NSArray *grabs, cpVect pos)
218 {
219         ChipmunkGrab *match = nil;
220         cpFloat best = INFINITY;
221         
222         for(ChipmunkGrab *grab in grabs){
223                 cpFloat dist = cpvdistsq(pos, grab.pos);
224                 if(dist < best){
225                         match = grab;
226                         best = dist;
227                 }
228         }
229         
230         return match;
231 }
232
233 -(ChipmunkGrab *)updateLocation:(cpVect)pos
234 {
235         ChipmunkGrab *grab = BestGrab(_grabs, pos);
236         grab.pos = pos;
237         
238         return (grab.grabbedShape ? grab : nil);
239 }
240
241 -(ChipmunkGrab *)endLocation:(cpVect)pos
242 {
243         cpAssertHard([_grabs count] != 0, "Grab set is already empty!");
244         ChipmunkGrab *grab = BestGrab(_grabs, pos);
245         [grab retain];
246         
247         [_space remove:grab];
248         [_grabs removeObject:grab];
249         
250         [grab autorelease];
251         return (grab.grabbedShape ? grab : nil);
252 }
253
254 -(NSArray *)grabs
255 {
256         NSMutableArray *grabs = [NSMutableArray array];
257         for(ChipmunkGrab *grab in _grabs){
258                 if(grab.grabbedShape) [grabs addObject:grab];
259         }
260         
261         return grabs;
262 }
263
264 @end