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 "ChipmunkMultiGrab.h"
25 // A constraint subclass that tracks a grab point
26 @interface ChipmunkGrab() <ChipmunkObject>
28 @property(nonatomic, readwrite) cpVect pos;
29 @property(nonatomic, readonly) NSArray *chipmunkObjects;
34 @implementation ChipmunkGrab
36 @synthesize pos = _pos;
37 @synthesize chipmunkObjects = _chipmunkObjects;
38 @synthesize grabbedShape = _grabbedShape;
39 @synthesize data = _data;
42 GrabPreSolve(cpConstraint *constraint, cpSpace *space)
44 cpBody *grabBody = cpConstraintGetBodyA(constraint);
45 ChipmunkGrab *grab = [ChipmunkConstraint constraintFromCPConstraint:constraint].userData;
46 cpFloat dt = cpSpaceGetCurrentTimeStep(space);
47 cpFloat coef = cpfpow(grab->_smoothing, dt);
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);
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
60 ChipmunkBody *grabBody = [ChipmunkBody kinematicBody];
61 grabBody.position = pos;
62 // TODO the repeated appending is a little silly here.
63 chipmunkObjects = [chipmunkObjects arrayByAddingObject:grabBody];
65 if((self = [super init])){
67 _smoothing = multiGrab.smoothing;
68 _grabbedShape = grabbedShape;
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];
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];
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];
95 _chipmunkObjects = [chipmunkObjects retain];
104 [_chipmunkObjects release]; _chipmunkObjects = nil;
111 @implementation ChipmunkMultiGrab
113 @synthesize grabForce = _grabForce;
114 @synthesize smoothing = _smoothing;
116 @synthesize filter = _filter;
117 @synthesize grabFilter = _grabFilter;
118 @synthesize grabSort = _grabSort;
120 @synthesize grabFriction = _grabFriction, grabRotaryFriction = _grabRotaryFriction;
121 @synthesize grabRadius = _grabRadius;
123 @synthesize pullMode = _pullMode, pushMode = _pushMode;
125 @synthesize pushMass = _pushMass;
126 @synthesize pushFriction = _pushFriction, pushElasticity = _pushElasticity;
127 @synthesize pushCollisionType = _pushCollisionType;
129 -(id)initForSpace:(ChipmunkSpace *)space withSmoothing:(cpFloat)smoothing withGrabForce:(cpFloat)grabForce
131 if((self = [super init])){
132 _space = [space retain];
133 _grabs = [[NSMutableArray alloc] init];
135 _smoothing = smoothing;
136 _grabForce = grabForce;
138 _filter = CP_SHAPE_FILTER_ALL;
140 _grabFilter = ^(ChipmunkShape *shape){return (bool)TRUE;};
141 _grabSort = ^(ChipmunkShape *shape, cpFloat depth){return depth;};
158 // Don't integrate push bodies.
159 static void PushBodyVelocityUpdate(cpBody *body, cpVect gravity, cpFloat damping, cpFloat dt){}
161 -(ChipmunkGrab *)beginLocation:(cpVect)pos
163 __block cpFloat min = INFINITY;
164 __block cpVect nearest = pos;
165 __block ChipmunkShape *grabbedShape = nil;
168 cpSpacePointQuery_b(_space.space, pos, _grabRadius, _filter, ^(cpShape *c_shape, cpVect point, cpFloat dist, cpVect gradient){
169 ChipmunkShape *shape = [ChipmunkShape shapeFromCPShape:c_shape];
172 // Call the sorting callback if dist is negative.
173 // Otherwise just take the nearest shape.
175 sort = -_grabSort(shape, -dist);
176 cpAssertWarn(sort <= 0.0f, "You must return a positive value from the sorting callback.");
179 if(sort < min && cpBodyGetMass(cpShapeGetBody(c_shape)) != INFINITY){
180 if(_grabFilter(shape)){
182 nearest = (dist > 0.0 ? point : pos);
183 grabbedShape = shape;
189 ChipmunkBody *pushBody = nil;
190 NSArray *chipmunkObjects = [NSArray array];
192 if(!grabbedShape && _pushMode){
193 pushBody = [ChipmunkBody bodyWithMass:_pushMass andMoment:INFINITY];
194 pushBody.position = pos;
195 cpBodySetVelocityUpdateFunc(pushBody.body, PushBodyVelocityUpdate);
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;
203 chipmunkObjects = [NSArray arrayWithObjects:pushBody, pushShape, nil];
206 ChipmunkBody *grabBody = (grabbedShape ? grabbedShape.body : pushBody);
207 ChipmunkGrab *grab = [[ChipmunkGrab alloc] initWithMultiGrab:self pos:pos nearest:nearest body:grabBody grabbedShape:grabbedShape chipmunkObjects:chipmunkObjects];
209 [_grabs addObject:grab];
213 return (grab.grabbedShape ? grab : nil);
216 static ChipmunkGrab *
217 BestGrab(NSArray *grabs, cpVect pos)
219 ChipmunkGrab *match = nil;
220 cpFloat best = INFINITY;
222 for(ChipmunkGrab *grab in grabs){
223 cpFloat dist = cpvdistsq(pos, grab.pos);
233 -(ChipmunkGrab *)updateLocation:(cpVect)pos
235 ChipmunkGrab *grab = BestGrab(_grabs, pos);
238 return (grab.grabbedShape ? grab : nil);
241 -(ChipmunkGrab *)endLocation:(cpVect)pos
243 cpAssertHard([_grabs count] != 0, "Grab set is already empty!");
244 ChipmunkGrab *grab = BestGrab(_grabs, pos);
247 [_space remove:grab];
248 [_grabs removeObject:grab];
251 return (grab.grabbedShape ? grab : nil);
256 NSMutableArray *grabs = [NSMutableArray array];
257 for(ChipmunkGrab *grab in _grabs){
258 if(grab.grabbedShape) [grabs addObject:grab];