Created
April 2, 2010 17:43
-
-
Save millenomi/353435 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// MvrInertia.h | |
// Mover-iPad | |
// | |
// Created by ∞ on 30/03/10. | |
// Copyright 2010 __MyCompanyName__. All rights reserved. | |
// | |
#import <UIKit/UIKit.h> | |
@protocol MvrAnimationStep; | |
typedef struct { | |
BOOL shouldAnimate; | |
CGPoint destination; | |
NSTimeInterval duration; | |
} MvrAnimationStep; | |
@interface MvrInertia : NSObject { | |
NSDate* motionStartTime; | |
NSTimer* speedTrap; | |
CGPoint lastMotion; | |
CGPoint lastCheckpointLocation; | |
NSTimeInterval lastCheckpointTime; | |
CGFloat minimumSpeedToTriggerSlide, attrition; | |
} | |
- (void) addMotionToPoint:(CGPoint) p; | |
- (MvrAnimationStep) endMotionWithPoint:(CGPoint) p; | |
// measured in point units per 100 ms. | |
// Defaults to 7 units per 100 ms. | |
@property CGFloat minimumSpeedToTriggerSlide; | |
// this is a slowdown factor -- how much speed the slide loses for each unit of "travel" | |
// a factor of 0.5 means that the inertial slide will cause the point to travel X, then 0.5*X, | |
// then 0.5*0.5*X and so on until it converges to a value. | |
// Defaults to 0.5. MUST be a reasonable value (in range 0.0-1.0, but not too near 1.0 nor too near 0.0). | |
@property CGFloat attrition; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// MvrInertia.m | |
// Mover-iPad | |
// | |
// Created by ∞ on 30/03/10. | |
// Copyright 2010 __MyCompanyName__. All rights reserved. | |
// | |
#import "MvrInertia.h" | |
#define MvrILog(x, ...) NSLog(@"%@, %s: " x, self, __func__, ## __VA_ARGS__) | |
static inline BOOL MvrAbsoluteWithinRange(CGPoint vector, CGFloat rangeAbs) { | |
return | |
ABS(vector.x) < rangeAbs && ABS(vector.y) < rangeAbs; | |
} | |
static inline NSString* MvrNSStringFromAnimationStep(MvrAnimationStep s) { | |
if (s.shouldAnimate) | |
return [NSString stringWithFormat:@"<Animation step: animation %f s long to %@", (double) s.duration, NSStringFromCGPoint(s.destination)]; | |
else | |
return @"<Animation step: none>"; | |
} | |
@interface MvrInertia () | |
- (NSTimeInterval) timestamp; | |
@end | |
static MvrAnimationStep MvrMakeAnimationStep(BOOL shouldAnimate, CGPoint destination, NSTimeInterval duration) { | |
MvrAnimationStep s; | |
s.shouldAnimate = shouldAnimate; | |
s.destination = destination; | |
s.duration = duration; | |
return s; | |
} | |
@implementation MvrInertia | |
- (id) init | |
{ | |
self = [super init]; | |
if (self != nil) { | |
self.attrition = 0.5; | |
self.minimumSpeedToTriggerSlide = 7.0; | |
} | |
return self; | |
} | |
- (void) dealloc | |
{ | |
[speedTrap invalidate]; | |
[speedTrap release]; | |
[motionStartTime release]; | |
[super dealloc]; | |
} | |
@synthesize minimumSpeedToTriggerSlide, attrition; | |
- (void) addMotionToPoint:(CGPoint) p; | |
{ | |
if (!speedTrap) { | |
MvrILog(@"Setting up the speed trap."); | |
speedTrap = [[NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(recordSpeed:) userInfo:nil repeats:YES] retain]; | |
motionStartTime = [NSDate new]; | |
lastCheckpointLocation = p; | |
lastCheckpointTime = 0.0; | |
} | |
MvrILog(@"Recorded motion to point: %@", NSStringFromCGPoint(p)); | |
lastMotion = p; | |
} | |
- (NSTimeInterval) timestamp; | |
{ | |
NSTimeInterval t = -[motionStartTime timeIntervalSinceNow]; | |
MvrILog(@"%f", t); | |
return t; | |
} | |
- (void) recordSpeed:(NSTimer*) t; | |
{ | |
lastCheckpointLocation = lastMotion; | |
lastCheckpointTime = [self timestamp]; | |
MvrILog(@"Checkpointed speed"); | |
} | |
- (MvrAnimationStep) endMotionWithPoint:(CGPoint) here; | |
{ | |
MvrILog(@"Final point for movement is %@", NSStringFromCGPoint(here)); | |
NSTimeInterval movementTime = [self timestamp] - lastCheckpointTime; | |
MvrILog(@"Last movement lasted: %f", movementTime); | |
CGPoint movementVector; | |
movementVector.x = here.x - lastCheckpointLocation.x; | |
movementVector.y = here.y - lastCheckpointLocation.y; | |
MvrILog(@"Movement delta vector is %@", NSStringFromCGPoint(movementVector)); | |
CGPoint speedPointsPer100MS; | |
speedPointsPer100MS.x = (movementVector.x / movementTime) * 0.1; | |
speedPointsPer100MS.y = (movementVector.y / movementTime) * 0.1; | |
MvrILog(@"Speed vector is %@", NSStringFromCGPoint(speedPointsPer100MS)); | |
BOOL performInertialSlide = !MvrAbsoluteWithinRange(speedPointsPer100MS, self.minimumSpeedToTriggerSlide); | |
MvrILog(@"Enough to slide? %d", performInertialSlide); | |
MvrAnimationStep result; | |
if (!performInertialSlide) | |
result = MvrMakeAnimationStep(NO, CGPointZero, 0); | |
else { | |
CGFloat dampening = self.attrition; | |
CGPoint delta = movementVector; | |
int timeScale = 1; | |
while (!MvrAbsoluteWithinRange(movementVector, 5.0)) { | |
movementVector.x *= dampening; | |
movementVector.y *= dampening; | |
delta.x += movementVector.x; | |
delta.y += movementVector.y; | |
timeScale++; | |
} | |
here.x += delta.x; | |
here.y += delta.y; | |
result = MvrMakeAnimationStep(YES, here, movementTime * timeScale); | |
} | |
[speedTrap invalidate]; | |
[speedTrap release]; speedTrap = nil; | |
MvrILog(@"Result: %@", MvrNSStringFromAnimationStep(result)); | |
return result; | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment