From fa12690d573ee220a12fe36fa3202391dd01dee4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20B=C3=B6hnke?= Date: Sun, 6 Apr 2014 14:15:36 +0200 Subject: [PATCH 01/12] Add RBBRubberbandAnimation --- RBBAnimation.xcodeproj/project.pbxproj | 6 + RBBAnimation/RBBRubberbandAnimation.h | 27 +++ RBBAnimation/RBBRubberbandAnimation.m | 154 ++++++++++++++++++ .../RBBAnimationListViewController.m | 15 ++ 4 files changed, 202 insertions(+) create mode 100644 RBBAnimation/RBBRubberbandAnimation.h create mode 100644 RBBAnimation/RBBRubberbandAnimation.m diff --git a/RBBAnimation.xcodeproj/project.pbxproj b/RBBAnimation.xcodeproj/project.pbxproj index 885d089..4207086 100644 --- a/RBBAnimation.xcodeproj/project.pbxproj +++ b/RBBAnimation.xcodeproj/project.pbxproj @@ -14,6 +14,7 @@ 545D5ABF180B2D3F00FC94AB /* RBBCubicBezier.m in Sources */ = {isa = PBXBuildFile; fileRef = 545D5ABE180B2D3F00FC94AB /* RBBCubicBezier.m */; }; 545D5AC2180B5E8400FC94AB /* RBBEasingFunction.m in Sources */ = {isa = PBXBuildFile; fileRef = 545D5AC1180B5E8400FC94AB /* RBBEasingFunction.m */; }; 545D5AC5180C138D00FC94AB /* RBBSpringAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 545D5AC4180C138D00FC94AB /* RBBSpringAnimation.m */; }; + 545FEFD618F177B000900B78 /* RBBRubberbandAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 545FEFD518F177B000900B78 /* RBBRubberbandAnimation.m */; }; 5466C56118082BEB00652BDC /* RBBBlockBasedArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 5466C56018082BEB00652BDC /* RBBBlockBasedArray.m */; }; 5466C56318082D4200652BDC /* RBBBlockBasedArraySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5466C56218082D4200652BDC /* RBBBlockBasedArraySpec.m */; }; 5466C569180849D100652BDC /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 54F3AE2D1807394800E1E688 /* Foundation.framework */; }; @@ -82,6 +83,8 @@ 545D5AC1180B5E8400FC94AB /* RBBEasingFunction.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RBBEasingFunction.m; sourceTree = ""; }; 545D5AC3180C138D00FC94AB /* RBBSpringAnimation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RBBSpringAnimation.h; sourceTree = ""; }; 545D5AC4180C138D00FC94AB /* RBBSpringAnimation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RBBSpringAnimation.m; sourceTree = ""; }; + 545FEFD418F177B000900B78 /* RBBRubberbandAnimation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RBBRubberbandAnimation.h; sourceTree = ""; }; + 545FEFD518F177B000900B78 /* RBBRubberbandAnimation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RBBRubberbandAnimation.m; sourceTree = ""; }; 5466C55F18082BEB00652BDC /* RBBBlockBasedArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RBBBlockBasedArray.h; sourceTree = ""; }; 5466C56018082BEB00652BDC /* RBBBlockBasedArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RBBBlockBasedArray.m; sourceTree = ""; }; 5466C56218082D4200652BDC /* RBBBlockBasedArraySpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RBBBlockBasedArraySpec.m; sourceTree = ""; }; @@ -228,6 +231,8 @@ 545D5AB9180B1FAE00FC94AB /* RBBTweenAnimation.m */, 545D5AC3180C138D00FC94AB /* RBBSpringAnimation.h */, 545D5AC4180C138D00FC94AB /* RBBSpringAnimation.m */, + 545FEFD418F177B000900B78 /* RBBRubberbandAnimation.h */, + 545FEFD518F177B000900B78 /* RBBRubberbandAnimation.m */, 54D5EA87181AE8620014896C /* RBBLinearInterpolation.h */, 54D5EA88181AE8620014896C /* RBBLinearInterpolation.m */, 545D5AC0180B5E8400FC94AB /* RBBEasingFunction.h */, @@ -477,6 +482,7 @@ 54D5EA89181AE8620014896C /* RBBLinearInterpolation.m in Sources */, 545D5AC2180B5E8400FC94AB /* RBBEasingFunction.m in Sources */, 5466C56118082BEB00652BDC /* RBBBlockBasedArray.m in Sources */, + 545FEFD618F177B000900B78 /* RBBRubberbandAnimation.m in Sources */, 545D5ABA180B1FAE00FC94AB /* RBBTweenAnimation.m in Sources */, 545D5AC5180C138D00FC94AB /* RBBSpringAnimation.m in Sources */, 54F3AE351807394800E1E688 /* RBBAnimation.m in Sources */, diff --git a/RBBAnimation/RBBRubberbandAnimation.h b/RBBAnimation/RBBRubberbandAnimation.h new file mode 100644 index 0000000..efa4359 --- /dev/null +++ b/RBBAnimation/RBBRubberbandAnimation.h @@ -0,0 +1,27 @@ +// +// RBBRubberbandAnimation.h +// RBBAnimation +// +// Created by Robert Böhnke on 06/04/14. +// Copyright (c) 2014 Robert Böhnke. All rights reserved. +// + +#import + +#import "RBBAnimation.h" + +@interface RBBRubberbandAnimation : RBBAnimation + +@property (readwrite, nonatomic, assign) CGFloat damping; +@property (readwrite, nonatomic, assign) CGFloat mass; +@property (readwrite, nonatomic, assign) CGFloat stiffness; +@property (readwrite, nonatomic, assign) CGVector velocity; + +@property (readwrite, nonatomic, assign) CGPoint from; +@property (readwrite, nonatomic, assign) CGPoint to; + +@property (readwrite, nonatomic, assign) BOOL allowsOverdamping; + +- (CFTimeInterval)durationForEpsilon:(double)epsilon; + +@end diff --git a/RBBAnimation/RBBRubberbandAnimation.m b/RBBAnimation/RBBRubberbandAnimation.m new file mode 100644 index 0000000..6cdd0d4 --- /dev/null +++ b/RBBAnimation/RBBRubberbandAnimation.m @@ -0,0 +1,154 @@ +// +// RBBRubberbandAnimation.m +// RBBAnimation +// +// Created by Robert Böhnke on 06/04/14. +// Copyright (c) 2014 Robert Böhnke. All rights reserved. +// + +#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR +#import +#endif + +#import "RBBRubberbandAnimation.h" + +@implementation RBBRubberbandAnimation + +#pragma mark - Lifecycle + +- (id)init { + self = [super init]; + if (self == nil) return nil; + + self.damping = 10; + self.mass = 1; + self.stiffness = 100; + + return self; +} + +#pragma mark - KVO + ++ (NSSet *)keyPathsForValuesAffectingAnimationBlock { + return [NSSet setWithArray:@[ + @"damping", + @"mass", + @"stiffness", + @"velocity", + @"from", + @"to", + @"allowsOverdamping" + ]]; +} + +#pragma mark - RBBSpringAnimation + +- (CFTimeInterval)durationForEpsilon:(double)epsilon { + CGFloat beta = self.damping / (2 * self.mass); + + CFTimeInterval duration = 0; + while (expf(-beta * duration) >= epsilon) { + duration += 0.1; + } + + return duration; +} + +#pragma mark - RBBAnimation + +- (RBBAnimationBlock)animationBlock { + CGFloat b = self.damping; + CGFloat m = self.mass; + CGFloat k = self.stiffness; + CGFloat v_x0 = self.velocity.dx; + CGFloat v_y0 = self.velocity.dy; + + NSParameterAssert(m > 0); + NSParameterAssert(k > 0); + NSParameterAssert(b > 0); + + CGFloat beta = b / (2 * m); + CGFloat omega0 = sqrtf(k / m); + CGFloat omega1 = sqrtf((omega0 * omega0) - (beta * beta)); + CGFloat omega2 = sqrtf((beta * beta) - (omega0 * omega0)); + + CGFloat x0 = self.from.x - self.to.x; + CGFloat y0 = self.from.y - self.to.y; + + if (!self.allowsOverdamping && beta > omega0) beta = omega0; + + CGFloat (^oscillationX)(CGFloat); + if (beta < omega0) { + // Underdamped + oscillationX = ^(CGFloat t) { + CGFloat envelope = expf(-beta * t); + + return -x0 + envelope * (x0 * cosf(omega1 * t) + ((beta * x0 + v_x0) / omega1) * sinf(omega1 * t)); + }; + } else if (beta == omega0) { + // Critically damped + oscillationX = ^(CGFloat t) { + CGFloat envelope = expf(-beta * t); + + return -x0 + envelope * (x0 + (beta * x0 + v_x0) * t); + }; + } else { + // Overdamped + oscillationX = ^(CGFloat t) { + CGFloat envelope = expf(-beta * t); + + return -x0 + envelope * (x0 * coshf(omega2 * t) + ((beta * x0 + v_x0) / omega2) * sinhf(omega2 * t)); + }; + } + + CGFloat (^oscillationY)(CGFloat); + if (beta < omega0) { + // Underdamped + oscillationY = ^(CGFloat t) { + CGFloat envelope = expf(-beta * t); + + return -y0 + envelope * (y0 * cosf(omega1 * t) + ((beta * y0 + v_y0) / omega1) * sinf(omega1 * t)); + }; + } else if (beta == omega0) { + // Critically damped + oscillationY = ^(CGFloat t) { + CGFloat envelope = expf(-beta * t); + + return -y0 + envelope * (y0 + (beta * y0 + v_y0) * t); + }; + } else { + // Overdamped + oscillationY = ^(CGFloat t) { + CGFloat envelope = expf(-beta * t); + + return -y0 + envelope * (y0 * coshf(omega2 * t) + ((beta * y0 + v_y0) / omega2) * sinhf(omega2 * t)); + }; + } + + return ^(CGFloat t, CGFloat _) { + CGPoint p = { .x = self.from.x + oscillationX(t), .y = self.from.y + oscillationY(t) }; + + return [NSValue valueWithCGPoint:p]; + }; +} + +#pragma mark - NSObject + +- (id)copyWithZone:(NSZone *)zone { + RBBRubberbandAnimation *copy = [super copyWithZone:zone]; + if (copy == nil) return nil; + + copy->_damping = _damping; + copy->_mass = _mass; + copy->_stiffness = _stiffness; + copy->_velocity = _velocity; + + copy->_from = _from; + copy->_to = _to; + + copy->_allowsOverdamping = _allowsOverdamping; + + return copy; +} + +@end diff --git a/RBBAnimationTest/RBBAnimationListViewController.m b/RBBAnimationTest/RBBAnimationListViewController.m index 8587956..97601dd 100644 --- a/RBBAnimationTest/RBBAnimationListViewController.m +++ b/RBBAnimationTest/RBBAnimationListViewController.m @@ -13,6 +13,7 @@ #import "RBBCustomAnimation.h" #import "RBBTweenAnimation.h" #import "RBBSpringAnimation.h" +#import "RBBRubberbandAnimation.h" #import "RBBAnimationListViewController.h" @@ -62,6 +63,19 @@ - (void)viewDidLoad { spring.duration = [spring durationForEpsilon:0.01]; spring.rbb_name = @"spring"; + RBBRubberbandAnimation *rubberband = [RBBRubberbandAnimation animationWithKeyPath:@"position"]; + + rubberband.from = CGPointMake(0, 0); + rubberband.to = CGPointMake(0, 0); + rubberband.mass = 1; + rubberband.damping = 10; + rubberband.stiffness = 100; + rubberband.velocity = CGVectorMake(100, 0); + + rubberband.additive = YES; + rubberband.duration = [rubberband durationForEpsilon:0.01]; + rubberband.rbb_name = @"rubberband"; + RBBTweenAnimation *sinus = [RBBTweenAnimation animationWithKeyPath:@"position.y"]; sinus.fromValue = @(0); sinus.toValue = @(100); @@ -93,6 +107,7 @@ - (void)viewDidLoad { self.animations = @[ easeInOutBack, spring, + rubberband, scale, sinus, bounce, From b21b6df5b9675547b63eb8f010717cdd6aa809c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20B=C3=B6hnke?= Date: Sun, 6 Apr 2014 14:18:34 +0200 Subject: [PATCH 02/12] Add Mac support for RBBRubberbandAnimation --- RBBAnimation/RBBRubberbandAnimation.m | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RBBAnimation/RBBRubberbandAnimation.m b/RBBAnimation/RBBRubberbandAnimation.m index 6cdd0d4..683e6e6 100644 --- a/RBBAnimation/RBBRubberbandAnimation.m +++ b/RBBAnimation/RBBRubberbandAnimation.m @@ -128,7 +128,11 @@ - (RBBAnimationBlock)animationBlock { return ^(CGFloat t, CGFloat _) { CGPoint p = { .x = self.from.x + oscillationX(t), .y = self.from.y + oscillationY(t) }; + #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR return [NSValue valueWithCGPoint:p]; + #elif TARGET_OS_MAC + return [NSValue valueWithPoint:p]; + #endif }; } From 6c6ed19a8bd7bbb6291cf8e6c68548d5b40fe85f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20B=C3=B6hnke?= Date: Sun, 6 Apr 2014 14:24:08 +0200 Subject: [PATCH 03/12] Define custom vector struct to not rely on CGVector --- RBBAnimation/RBBRubberbandAnimation.h | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/RBBAnimation/RBBRubberbandAnimation.h b/RBBAnimation/RBBRubberbandAnimation.h index efa4359..1b790fd 100644 --- a/RBBAnimation/RBBRubberbandAnimation.h +++ b/RBBAnimation/RBBRubberbandAnimation.h @@ -8,6 +8,16 @@ #import +#ifdef CGVECTOR_DEFINED +typedef CGVector RBBVector; +#else +struct RBBVector { + CGFloat dx; + CGFloat dy; +}; +typedef struct RBBVector RBBVector; +#endif + #import "RBBAnimation.h" @interface RBBRubberbandAnimation : RBBAnimation @@ -15,7 +25,7 @@ @property (readwrite, nonatomic, assign) CGFloat damping; @property (readwrite, nonatomic, assign) CGFloat mass; @property (readwrite, nonatomic, assign) CGFloat stiffness; -@property (readwrite, nonatomic, assign) CGVector velocity; +@property (readwrite, nonatomic, assign) RBBVector velocity; @property (readwrite, nonatomic, assign) CGPoint from; @property (readwrite, nonatomic, assign) CGPoint to; From ae43f80a4ac22b82e1b0fe9906fd94815523d086 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20B=C3=B6hnke?= Date: Sun, 6 Apr 2014 18:36:03 +0200 Subject: [PATCH 04/12] Use CGPoint for velocity It's what -[UIPanGestureRecognizer velocityInView:] returns which seems to be a more pragmatic approach. --- RBBAnimation/RBBRubberbandAnimation.h | 12 +----------- RBBAnimation/RBBRubberbandAnimation.m | 4 ++-- RBBAnimationTest/RBBAnimationListViewController.m | 2 +- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/RBBAnimation/RBBRubberbandAnimation.h b/RBBAnimation/RBBRubberbandAnimation.h index 1b790fd..1832b27 100644 --- a/RBBAnimation/RBBRubberbandAnimation.h +++ b/RBBAnimation/RBBRubberbandAnimation.h @@ -8,16 +8,6 @@ #import -#ifdef CGVECTOR_DEFINED -typedef CGVector RBBVector; -#else -struct RBBVector { - CGFloat dx; - CGFloat dy; -}; -typedef struct RBBVector RBBVector; -#endif - #import "RBBAnimation.h" @interface RBBRubberbandAnimation : RBBAnimation @@ -25,7 +15,7 @@ typedef struct RBBVector RBBVector; @property (readwrite, nonatomic, assign) CGFloat damping; @property (readwrite, nonatomic, assign) CGFloat mass; @property (readwrite, nonatomic, assign) CGFloat stiffness; -@property (readwrite, nonatomic, assign) RBBVector velocity; +@property (readwrite, nonatomic, assign) CGPoint velocity; @property (readwrite, nonatomic, assign) CGPoint from; @property (readwrite, nonatomic, assign) CGPoint to; diff --git a/RBBAnimation/RBBRubberbandAnimation.m b/RBBAnimation/RBBRubberbandAnimation.m index 683e6e6..74f014a 100644 --- a/RBBAnimation/RBBRubberbandAnimation.m +++ b/RBBAnimation/RBBRubberbandAnimation.m @@ -60,8 +60,8 @@ - (RBBAnimationBlock)animationBlock { CGFloat b = self.damping; CGFloat m = self.mass; CGFloat k = self.stiffness; - CGFloat v_x0 = self.velocity.dx; - CGFloat v_y0 = self.velocity.dy; + CGFloat v_x0 = self.velocity.x; + CGFloat v_y0 = self.velocity.y; NSParameterAssert(m > 0); NSParameterAssert(k > 0); diff --git a/RBBAnimationTest/RBBAnimationListViewController.m b/RBBAnimationTest/RBBAnimationListViewController.m index 97601dd..e63ad7b 100644 --- a/RBBAnimationTest/RBBAnimationListViewController.m +++ b/RBBAnimationTest/RBBAnimationListViewController.m @@ -70,7 +70,7 @@ - (void)viewDidLoad { rubberband.mass = 1; rubberband.damping = 10; rubberband.stiffness = 100; - rubberband.velocity = CGVectorMake(100, 0); + rubberband.velocity = CGPointMake(100, 0); rubberband.additive = YES; rubberband.duration = [rubberband durationForEpsilon:0.01]; From 7a3200261cbe71f2f31f0f2e970129ee0258c921 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20B=C3=B6hnke?= Date: Thu, 10 Apr 2014 14:26:58 +0200 Subject: [PATCH 05/12] DRY damped harmonic oscillation --- RBBAnimation.xcodeproj/project.pbxproj | 6 +++ RBBAnimation/RBBDampedHarmonicOscillaton.h | 13 ++++++ RBBAnimation/RBBDampedHarmonicOscillaton.m | 34 +++++++++++++++ RBBAnimation/RBBRubberbandAnimation.m | 51 ++-------------------- RBBAnimation/RBBSpringAnimation.m | 25 +---------- 5 files changed, 59 insertions(+), 70 deletions(-) create mode 100644 RBBAnimation/RBBDampedHarmonicOscillaton.h create mode 100644 RBBAnimation/RBBDampedHarmonicOscillaton.m diff --git a/RBBAnimation.xcodeproj/project.pbxproj b/RBBAnimation.xcodeproj/project.pbxproj index 4207086..abacfa8 100644 --- a/RBBAnimation.xcodeproj/project.pbxproj +++ b/RBBAnimation.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ 545D5AC2180B5E8400FC94AB /* RBBEasingFunction.m in Sources */ = {isa = PBXBuildFile; fileRef = 545D5AC1180B5E8400FC94AB /* RBBEasingFunction.m */; }; 545D5AC5180C138D00FC94AB /* RBBSpringAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 545D5AC4180C138D00FC94AB /* RBBSpringAnimation.m */; }; 545FEFD618F177B000900B78 /* RBBRubberbandAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 545FEFD518F177B000900B78 /* RBBRubberbandAnimation.m */; }; + 5466A9B518F6C2260090E3E6 /* RBBDampedHarmonicOscillaton.m in Sources */ = {isa = PBXBuildFile; fileRef = 5466A9B418F6C2260090E3E6 /* RBBDampedHarmonicOscillaton.m */; }; 5466C56118082BEB00652BDC /* RBBBlockBasedArray.m in Sources */ = {isa = PBXBuildFile; fileRef = 5466C56018082BEB00652BDC /* RBBBlockBasedArray.m */; }; 5466C56318082D4200652BDC /* RBBBlockBasedArraySpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 5466C56218082D4200652BDC /* RBBBlockBasedArraySpec.m */; }; 5466C569180849D100652BDC /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 54F3AE2D1807394800E1E688 /* Foundation.framework */; }; @@ -85,6 +86,8 @@ 545D5AC4180C138D00FC94AB /* RBBSpringAnimation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RBBSpringAnimation.m; sourceTree = ""; }; 545FEFD418F177B000900B78 /* RBBRubberbandAnimation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RBBRubberbandAnimation.h; sourceTree = ""; }; 545FEFD518F177B000900B78 /* RBBRubberbandAnimation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RBBRubberbandAnimation.m; sourceTree = ""; }; + 5466A9B318F6C2260090E3E6 /* RBBDampedHarmonicOscillaton.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RBBDampedHarmonicOscillaton.h; sourceTree = ""; }; + 5466A9B418F6C2260090E3E6 /* RBBDampedHarmonicOscillaton.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RBBDampedHarmonicOscillaton.m; sourceTree = ""; }; 5466C55F18082BEB00652BDC /* RBBBlockBasedArray.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RBBBlockBasedArray.h; sourceTree = ""; }; 5466C56018082BEB00652BDC /* RBBBlockBasedArray.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RBBBlockBasedArray.m; sourceTree = ""; }; 5466C56218082D4200652BDC /* RBBBlockBasedArraySpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RBBBlockBasedArraySpec.m; sourceTree = ""; }; @@ -233,6 +236,8 @@ 545D5AC4180C138D00FC94AB /* RBBSpringAnimation.m */, 545FEFD418F177B000900B78 /* RBBRubberbandAnimation.h */, 545FEFD518F177B000900B78 /* RBBRubberbandAnimation.m */, + 5466A9B318F6C2260090E3E6 /* RBBDampedHarmonicOscillaton.h */, + 5466A9B418F6C2260090E3E6 /* RBBDampedHarmonicOscillaton.m */, 54D5EA87181AE8620014896C /* RBBLinearInterpolation.h */, 54D5EA88181AE8620014896C /* RBBLinearInterpolation.m */, 545D5AC0180B5E8400FC94AB /* RBBEasingFunction.h */, @@ -483,6 +488,7 @@ 545D5AC2180B5E8400FC94AB /* RBBEasingFunction.m in Sources */, 5466C56118082BEB00652BDC /* RBBBlockBasedArray.m in Sources */, 545FEFD618F177B000900B78 /* RBBRubberbandAnimation.m in Sources */, + 5466A9B518F6C2260090E3E6 /* RBBDampedHarmonicOscillaton.m in Sources */, 545D5ABA180B1FAE00FC94AB /* RBBTweenAnimation.m in Sources */, 545D5AC5180C138D00FC94AB /* RBBSpringAnimation.m in Sources */, 54F3AE351807394800E1E688 /* RBBAnimation.m in Sources */, diff --git a/RBBAnimation/RBBDampedHarmonicOscillaton.h b/RBBAnimation/RBBDampedHarmonicOscillaton.h new file mode 100644 index 0000000..9ff59a1 --- /dev/null +++ b/RBBAnimation/RBBDampedHarmonicOscillaton.h @@ -0,0 +1,13 @@ +// +// RBBDampedHarmonicOscillaton.h +// RBBAnimation +// +// Created by Robert Böhnke on 10/04/14. +// Copyright (c) 2014 Robert Böhnke. All rights reserved. +// + +#import + +typedef CGFloat (^RBBOsciallation)(CGFloat t); + +RBBOsciallation RBBDampedHarmonicOscillation(CGFloat x0, CGFloat v0, CGFloat omega0, CGFloat omega1, CGFloat omega2, CGFloat beta); diff --git a/RBBAnimation/RBBDampedHarmonicOscillaton.m b/RBBAnimation/RBBDampedHarmonicOscillaton.m new file mode 100644 index 0000000..d6d2808 --- /dev/null +++ b/RBBAnimation/RBBDampedHarmonicOscillaton.m @@ -0,0 +1,34 @@ +// +// RBBDampedHarmonicOscillaton.m +// RBBAnimation +// +// Created by Robert Böhnke on 10/04/14. +// Copyright (c) 2014 Robert Böhnke. All rights reserved. +// + +#import "RBBDampedHarmonicOscillaton.h" + +RBBOsciallation RBBDampedHarmonicOscillation(CGFloat x0, CGFloat v0, CGFloat omega0, CGFloat omega1, CGFloat omega2, CGFloat beta) { + if (beta < omega0) { + // Underdamped + return ^(CGFloat t) { + CGFloat envelope = expf(-beta * t); + + return -x0 + envelope * (x0 * cosf(omega1 * t) + ((beta * x0 + v0) / omega1) * sinf(omega1 * t)); + }; + } else if (beta == omega0) { + // Critically damped + return ^(CGFloat t) { + CGFloat envelope = expf(-beta * t); + + return -x0 + envelope * (x0 + (beta * x0 + v0) * t); + }; + } else { + // Overdamped + return ^(CGFloat t) { + CGFloat envelope = expf(-beta * t); + + return -x0 + envelope * (x0 * coshf(omega2 * t) + ((beta * x0 + v0) / omega2) * sinhf(omega2 * t)); + }; + } +} \ No newline at end of file diff --git a/RBBAnimation/RBBRubberbandAnimation.m b/RBBAnimation/RBBRubberbandAnimation.m index 74f014a..ce80001 100644 --- a/RBBAnimation/RBBRubberbandAnimation.m +++ b/RBBAnimation/RBBRubberbandAnimation.m @@ -10,6 +10,8 @@ #import #endif +#import "RBBDampedHarmonicOscillaton.h" + #import "RBBRubberbandAnimation.h" @implementation RBBRubberbandAnimation @@ -77,53 +79,8 @@ - (RBBAnimationBlock)animationBlock { if (!self.allowsOverdamping && beta > omega0) beta = omega0; - CGFloat (^oscillationX)(CGFloat); - if (beta < omega0) { - // Underdamped - oscillationX = ^(CGFloat t) { - CGFloat envelope = expf(-beta * t); - - return -x0 + envelope * (x0 * cosf(omega1 * t) + ((beta * x0 + v_x0) / omega1) * sinf(omega1 * t)); - }; - } else if (beta == omega0) { - // Critically damped - oscillationX = ^(CGFloat t) { - CGFloat envelope = expf(-beta * t); - - return -x0 + envelope * (x0 + (beta * x0 + v_x0) * t); - }; - } else { - // Overdamped - oscillationX = ^(CGFloat t) { - CGFloat envelope = expf(-beta * t); - - return -x0 + envelope * (x0 * coshf(omega2 * t) + ((beta * x0 + v_x0) / omega2) * sinhf(omega2 * t)); - }; - } - - CGFloat (^oscillationY)(CGFloat); - if (beta < omega0) { - // Underdamped - oscillationY = ^(CGFloat t) { - CGFloat envelope = expf(-beta * t); - - return -y0 + envelope * (y0 * cosf(omega1 * t) + ((beta * y0 + v_y0) / omega1) * sinf(omega1 * t)); - }; - } else if (beta == omega0) { - // Critically damped - oscillationY = ^(CGFloat t) { - CGFloat envelope = expf(-beta * t); - - return -y0 + envelope * (y0 + (beta * y0 + v_y0) * t); - }; - } else { - // Overdamped - oscillationY = ^(CGFloat t) { - CGFloat envelope = expf(-beta * t); - - return -y0 + envelope * (y0 * coshf(omega2 * t) + ((beta * y0 + v_y0) / omega2) * sinhf(omega2 * t)); - }; - } + CGFloat (^oscillationX)(CGFloat) = RBBDampedHarmonicOscillation(x0, v_x0, omega0, omega1, omega2, beta); + CGFloat (^oscillationY)(CGFloat) = RBBDampedHarmonicOscillation(y0, v_y0, omega0, omega1, omega2, beta); return ^(CGFloat t, CGFloat _) { CGPoint p = { .x = self.from.x + oscillationX(t), .y = self.from.y + oscillationY(t) }; diff --git a/RBBAnimation/RBBSpringAnimation.m b/RBBAnimation/RBBSpringAnimation.m index 6716ef2..0c1d886 100644 --- a/RBBAnimation/RBBSpringAnimation.m +++ b/RBBAnimation/RBBSpringAnimation.m @@ -7,6 +7,7 @@ // #import "RBBBlockBasedArray.h" +#import "RBBDampedHarmonicOscillaton.h" #import "RBBLinearInterpolation.h" #import "RBBSpringAnimation.h" @@ -74,29 +75,7 @@ - (RBBAnimationBlock)animationBlock { if (!self.allowsOverdamping && beta > omega0) beta = omega0; - CGFloat (^oscillation)(CGFloat); - if (beta < omega0) { - // Underdamped - oscillation = ^(CGFloat t) { - CGFloat envelope = expf(-beta * t); - - return -x0 + envelope * (x0 * cosf(omega1 * t) + ((beta * x0 + v0) / omega1) * sinf(omega1 * t)); - }; - } else if (beta == omega0) { - // Critically damped - oscillation = ^(CGFloat t) { - CGFloat envelope = expf(-beta * t); - - return -x0 + envelope * (x0 + (beta * x0 + v0) * t); - }; - } else { - // Overdamped - oscillation = ^(CGFloat t) { - CGFloat envelope = expf(-beta * t); - - return -x0 + envelope * (x0 * coshf(omega2 * t) + ((beta * x0 + v0) / omega2) * sinhf(omega2 * t)); - }; - } + CGFloat (^oscillation)(CGFloat) = RBBDampedHarmonicOscillation(x0, v0, omega0, omega1, omega2, beta); RBBLinearInterpolation lerp = RBBInterpolate(self.fromValue, self.toValue); return ^(CGFloat t, CGFloat _) { From 95ffe07b519daf6b03d0d1c43cdd5390364a7289 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20B=C3=B6hnke?= Date: Thu, 10 Apr 2014 14:29:04 +0200 Subject: [PATCH 06/12] WS --- RBBAnimation/RBBRubberbandAnimation.m | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/RBBAnimation/RBBRubberbandAnimation.m b/RBBAnimation/RBBRubberbandAnimation.m index ce80001..94c6ff9 100644 --- a/RBBAnimation/RBBRubberbandAnimation.m +++ b/RBBAnimation/RBBRubberbandAnimation.m @@ -98,17 +98,17 @@ - (RBBAnimationBlock)animationBlock { - (id)copyWithZone:(NSZone *)zone { RBBRubberbandAnimation *copy = [super copyWithZone:zone]; if (copy == nil) return nil; - + copy->_damping = _damping; copy->_mass = _mass; copy->_stiffness = _stiffness; copy->_velocity = _velocity; - + copy->_from = _from; copy->_to = _to; - + copy->_allowsOverdamping = _allowsOverdamping; - + return copy; } From 3d619a44c7538dad9aa5944e2be0ad2a5ec726d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20B=C3=B6hnke?= Date: Thu, 10 Apr 2014 15:29:07 +0200 Subject: [PATCH 07/12] Move everything to RBBDampedHarmonicOscillation --- RBBAnimation/RBBDampedHarmonicOscillaton.h | 2 +- RBBAnimation/RBBDampedHarmonicOscillaton.m | 13 +++++++++++- RBBAnimation/RBBRubberbandAnimation.m | 24 +++++----------------- RBBAnimation/RBBSpringAnimation.m | 20 +----------------- 4 files changed, 19 insertions(+), 40 deletions(-) diff --git a/RBBAnimation/RBBDampedHarmonicOscillaton.h b/RBBAnimation/RBBDampedHarmonicOscillaton.h index 9ff59a1..e180e90 100644 --- a/RBBAnimation/RBBDampedHarmonicOscillaton.h +++ b/RBBAnimation/RBBDampedHarmonicOscillaton.h @@ -10,4 +10,4 @@ typedef CGFloat (^RBBOsciallation)(CGFloat t); -RBBOsciallation RBBDampedHarmonicOscillation(CGFloat x0, CGFloat v0, CGFloat omega0, CGFloat omega1, CGFloat omega2, CGFloat beta); +RBBOsciallation RBBDampedHarmonicOscillation(CGFloat x0, CGFloat damping, CGFloat mass, CGFloat stiffness, CGFloat velocity, BOOL allowsOverdamping); diff --git a/RBBAnimation/RBBDampedHarmonicOscillaton.m b/RBBAnimation/RBBDampedHarmonicOscillaton.m index d6d2808..ef86609 100644 --- a/RBBAnimation/RBBDampedHarmonicOscillaton.m +++ b/RBBAnimation/RBBDampedHarmonicOscillaton.m @@ -8,7 +8,18 @@ #import "RBBDampedHarmonicOscillaton.h" -RBBOsciallation RBBDampedHarmonicOscillation(CGFloat x0, CGFloat v0, CGFloat omega0, CGFloat omega1, CGFloat omega2, CGFloat beta) { +RBBOsciallation RBBDampedHarmonicOscillation(CGFloat x0, CGFloat b, CGFloat m, CGFloat k, CGFloat v0, BOOL allowsOverdamping) { + NSCAssert(m > 0, @"mass must be greater than zero."); + NSCAssert(k > 0, @"stiffness must be greater than zero."); + NSCAssert(b > 0, @"damping must be greater than zero."); + + CGFloat beta = b / (2 * m); + CGFloat omega0 = sqrtf(k / m); + CGFloat omega1 = sqrtf((omega0 * omega0) - (beta * beta)); + CGFloat omega2 = sqrtf((beta * beta) - (omega0 * omega0)); + + if (!allowsOverdamping && beta > omega0) beta = omega0; + if (beta < omega0) { // Underdamped return ^(CGFloat t) { diff --git a/RBBAnimation/RBBRubberbandAnimation.m b/RBBAnimation/RBBRubberbandAnimation.m index 94c6ff9..5109ece 100644 --- a/RBBAnimation/RBBRubberbandAnimation.m +++ b/RBBAnimation/RBBRubberbandAnimation.m @@ -59,31 +59,17 @@ - (CFTimeInterval)durationForEpsilon:(double)epsilon { #pragma mark - RBBAnimation - (RBBAnimationBlock)animationBlock { - CGFloat b = self.damping; - CGFloat m = self.mass; - CGFloat k = self.stiffness; - CGFloat v_x0 = self.velocity.x; - CGFloat v_y0 = self.velocity.y; - - NSParameterAssert(m > 0); - NSParameterAssert(k > 0); - NSParameterAssert(b > 0); - - CGFloat beta = b / (2 * m); - CGFloat omega0 = sqrtf(k / m); - CGFloat omega1 = sqrtf((omega0 * omega0) - (beta * beta)); - CGFloat omega2 = sqrtf((beta * beta) - (omega0 * omega0)); - CGFloat x0 = self.from.x - self.to.x; CGFloat y0 = self.from.y - self.to.y; - if (!self.allowsOverdamping && beta > omega0) beta = omega0; + RBBOsciallation oscillationX = RBBDampedHarmonicOscillation(x0, self.damping, self.mass, self.stiffness, self.velocity.x, self.allowsOverdamping); + RBBOsciallation oscillationY = RBBDampedHarmonicOscillation(y0, self.damping, self.mass, self.stiffness, self.velocity.y, self.allowsOverdamping); - CGFloat (^oscillationX)(CGFloat) = RBBDampedHarmonicOscillation(x0, v_x0, omega0, omega1, omega2, beta); - CGFloat (^oscillationY)(CGFloat) = RBBDampedHarmonicOscillation(y0, v_y0, omega0, omega1, omega2, beta); + CGFloat xOffset = self.from.x; + CGFloat yOffset = self.from.y; return ^(CGFloat t, CGFloat _) { - CGPoint p = { .x = self.from.x + oscillationX(t), .y = self.from.y + oscillationY(t) }; + CGPoint p = { .x = xOffset + oscillationX(t), .y = yOffset + oscillationY(t) }; #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR return [NSValue valueWithCGPoint:p]; diff --git a/RBBAnimation/RBBSpringAnimation.m b/RBBAnimation/RBBSpringAnimation.m index 0c1d886..85ab1c0 100644 --- a/RBBAnimation/RBBSpringAnimation.m +++ b/RBBAnimation/RBBSpringAnimation.m @@ -57,25 +57,7 @@ - (CFTimeInterval)durationForEpsilon:(double)epsilon { #pragma mark - RBBAnimation - (RBBAnimationBlock)animationBlock { - CGFloat b = self.damping; - CGFloat m = self.mass; - CGFloat k = self.stiffness; - CGFloat v0 = self.velocity; - - NSParameterAssert(m > 0); - NSParameterAssert(k > 0); - NSParameterAssert(b > 0); - - CGFloat beta = b / (2 * m); - CGFloat omega0 = sqrtf(k / m); - CGFloat omega1 = sqrtf((omega0 * omega0) - (beta * beta)); - CGFloat omega2 = sqrtf((beta * beta) - (omega0 * omega0)); - - CGFloat x0 = -1; - - if (!self.allowsOverdamping && beta > omega0) beta = omega0; - - CGFloat (^oscillation)(CGFloat) = RBBDampedHarmonicOscillation(x0, v0, omega0, omega1, omega2, beta); + CGFloat (^oscillation)(CGFloat) = RBBDampedHarmonicOscillation(-1, self.damping, self.mass, self.stiffness, self.velocity, self.allowsOverdamping); RBBLinearInterpolation lerp = RBBInterpolate(self.fromValue, self.toValue); return ^(CGFloat t, CGFloat _) { From a482638acef764debcf204c9efedb62fd418c528 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20B=C3=B6hnke?= Date: Thu, 10 Apr 2014 18:47:00 +0200 Subject: [PATCH 08/12] Refactor damped harmonic oscillation --- RBBAnimation.xcodeproj/project.pbxproj | 4 +++ RBBAnimation/RBBDampedHarmonicOscillaton.m | 6 ++-- RBBAnimation/RBBRubberbandAnimation.m | 14 ++++----- RBBAnimation/RBBSpringAnimation.m | 2 +- .../RBBAnimationListViewController.m | 6 ++-- Specs/RBBDampedHarmonicOscillatonSpec.m | 31 +++++++++++++++++++ 6 files changed, 49 insertions(+), 14 deletions(-) create mode 100644 Specs/RBBDampedHarmonicOscillatonSpec.m diff --git a/RBBAnimation.xcodeproj/project.pbxproj b/RBBAnimation.xcodeproj/project.pbxproj index cbfa982..d36efbc 100644 --- a/RBBAnimation.xcodeproj/project.pbxproj +++ b/RBBAnimation.xcodeproj/project.pbxproj @@ -29,6 +29,7 @@ 5466C59818084BA200652BDC /* RBBAnimationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5466C59718084BA200652BDC /* RBBAnimationViewController.m */; }; 5479BEA4181D8008009811FD /* RBBCustomAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 5479BEA3181D8008009811FD /* RBBCustomAnimation.m */; }; 549ECA8418F6ECFC00D968C7 /* RBBSpringAnimationSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 549ECA8318F6ECFC00D968C7 /* RBBSpringAnimationSpec.m */; }; + 549ECA8618F7010400D968C7 /* RBBDampedHarmonicOscillatonSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 549ECA8518F7010400D968C7 /* RBBDampedHarmonicOscillatonSpec.m */; }; 54D5EA89181AE8620014896C /* RBBLinearInterpolation.m in Sources */ = {isa = PBXBuildFile; fileRef = 54D5EA88181AE8620014896C /* RBBLinearInterpolation.m */; }; 54F3AE2E1807394800E1E688 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 54F3AE2D1807394800E1E688 /* Foundation.framework */; }; 54F3AE331807394800E1E688 /* RBBAnimation.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 54F3AE321807394800E1E688 /* RBBAnimation.h */; }; @@ -106,6 +107,7 @@ 5479BEA2181D8008009811FD /* RBBCustomAnimation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RBBCustomAnimation.h; sourceTree = ""; }; 5479BEA3181D8008009811FD /* RBBCustomAnimation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RBBCustomAnimation.m; sourceTree = ""; }; 549ECA8318F6ECFC00D968C7 /* RBBSpringAnimationSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RBBSpringAnimationSpec.m; sourceTree = ""; }; + 549ECA8518F7010400D968C7 /* RBBDampedHarmonicOscillatonSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RBBDampedHarmonicOscillatonSpec.m; sourceTree = ""; }; 54D5EA87181AE8620014896C /* RBBLinearInterpolation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RBBLinearInterpolation.h; sourceTree = ""; }; 54D5EA88181AE8620014896C /* RBBLinearInterpolation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RBBLinearInterpolation.m; sourceTree = ""; }; 54F3AE2A1807394800E1E688 /* libRBBAnimation.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRBBAnimation.a; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -265,6 +267,7 @@ isa = PBXGroup; children = ( 5466C56218082D4200652BDC /* RBBBlockBasedArraySpec.m */, + 549ECA8518F7010400D968C7 /* RBBDampedHarmonicOscillatonSpec.m */, 549ECA8318F6ECFC00D968C7 /* RBBSpringAnimationSpec.m */, 54F3AE441807394800E1E688 /* Supporting Files */, ); @@ -504,6 +507,7 @@ files = ( 5466C56318082D4200652BDC /* RBBBlockBasedArraySpec.m in Sources */, 549ECA8418F6ECFC00D968C7 /* RBBSpringAnimationSpec.m in Sources */, + 549ECA8618F7010400D968C7 /* RBBDampedHarmonicOscillatonSpec.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/RBBAnimation/RBBDampedHarmonicOscillaton.m b/RBBAnimation/RBBDampedHarmonicOscillaton.m index ef86609..7c3a38d 100644 --- a/RBBAnimation/RBBDampedHarmonicOscillaton.m +++ b/RBBAnimation/RBBDampedHarmonicOscillaton.m @@ -25,21 +25,21 @@ RBBOsciallation RBBDampedHarmonicOscillation(CGFloat x0, CGFloat b, CGFloat m, C return ^(CGFloat t) { CGFloat envelope = expf(-beta * t); - return -x0 + envelope * (x0 * cosf(omega1 * t) + ((beta * x0 + v0) / omega1) * sinf(omega1 * t)); + return envelope * (x0 * cosf(omega1 * t) + ((beta * x0 + v0) / omega1) * sinf(omega1 * t)); }; } else if (beta == omega0) { // Critically damped return ^(CGFloat t) { CGFloat envelope = expf(-beta * t); - return -x0 + envelope * (x0 + (beta * x0 + v0) * t); + return envelope * (x0 + (beta * x0 + v0) * t); }; } else { // Overdamped return ^(CGFloat t) { CGFloat envelope = expf(-beta * t); - return -x0 + envelope * (x0 * coshf(omega2 * t) + ((beta * x0 + v0) / omega2) * sinhf(omega2 * t)); + return envelope * (x0 * coshf(omega2 * t) + ((beta * x0 + v0) / omega2) * sinhf(omega2 * t)); }; } } \ No newline at end of file diff --git a/RBBAnimation/RBBRubberbandAnimation.m b/RBBAnimation/RBBRubberbandAnimation.m index 5109ece..4397445 100644 --- a/RBBAnimation/RBBRubberbandAnimation.m +++ b/RBBAnimation/RBBRubberbandAnimation.m @@ -59,17 +59,17 @@ - (CFTimeInterval)durationForEpsilon:(double)epsilon { #pragma mark - RBBAnimation - (RBBAnimationBlock)animationBlock { - CGFloat x0 = self.from.x - self.to.x; - CGFloat y0 = self.from.y - self.to.y; + CGFloat deltaX = self.from.x - self.to.x; + CGFloat deltaY = self.from.y - self.to.y; - RBBOsciallation oscillationX = RBBDampedHarmonicOscillation(x0, self.damping, self.mass, self.stiffness, self.velocity.x, self.allowsOverdamping); - RBBOsciallation oscillationY = RBBDampedHarmonicOscillation(y0, self.damping, self.mass, self.stiffness, self.velocity.y, self.allowsOverdamping); + RBBOsciallation oscillationX = RBBDampedHarmonicOscillation(deltaX, self.damping, self.mass, self.stiffness, self.velocity.x, self.allowsOverdamping); + RBBOsciallation oscillationY = RBBDampedHarmonicOscillation(deltaY, self.damping, self.mass, self.stiffness, self.velocity.y, self.allowsOverdamping); - CGFloat xOffset = self.from.x; - CGFloat yOffset = self.from.y; + CGFloat x0 = self.to.x; + CGFloat y0 = self.to.y; return ^(CGFloat t, CGFloat _) { - CGPoint p = { .x = xOffset + oscillationX(t), .y = yOffset + oscillationY(t) }; + CGPoint p = { .x = x0 + oscillationX(t), .y = y0 + oscillationY(t) }; #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR return [NSValue valueWithCGPoint:p]; diff --git a/RBBAnimation/RBBSpringAnimation.m b/RBBAnimation/RBBSpringAnimation.m index 3332a9d..121725c 100644 --- a/RBBAnimation/RBBSpringAnimation.m +++ b/RBBAnimation/RBBSpringAnimation.m @@ -63,7 +63,7 @@ - (RBBAnimationBlock)animationBlock { RBBLinearInterpolation lerp = RBBInterpolate(self.fromValue, self.toValue); return ^(CGFloat t, CGFloat _) { - return lerp(oscillation(t)); + return lerp(1 + oscillation(t)); }; } diff --git a/RBBAnimationTest/RBBAnimationListViewController.m b/RBBAnimationTest/RBBAnimationListViewController.m index e63ad7b..5a82269 100644 --- a/RBBAnimationTest/RBBAnimationListViewController.m +++ b/RBBAnimationTest/RBBAnimationListViewController.m @@ -65,12 +65,12 @@ - (void)viewDidLoad { RBBRubberbandAnimation *rubberband = [RBBRubberbandAnimation animationWithKeyPath:@"position"]; - rubberband.from = CGPointMake(0, 0); - rubberband.to = CGPointMake(0, 0); + rubberband.from = CGPointMake(0, -100); + rubberband.to = CGPointMake(0, 100); rubberband.mass = 1; rubberband.damping = 10; rubberband.stiffness = 100; - rubberband.velocity = CGPointMake(100, 0); + rubberband.velocity = CGPointMake(0, 0); rubberband.additive = YES; rubberband.duration = [rubberband durationForEpsilon:0.01]; diff --git a/Specs/RBBDampedHarmonicOscillatonSpec.m b/Specs/RBBDampedHarmonicOscillatonSpec.m new file mode 100644 index 0000000..59d0f5a --- /dev/null +++ b/Specs/RBBDampedHarmonicOscillatonSpec.m @@ -0,0 +1,31 @@ +// +// RBBDampedHarmonicOscillatonSpec.m +// RBBAnimation +// +// Created by Robert Böhnke on 10/04/14. +// Copyright (c) 2014 Robert Böhnke. All rights reserved. +// + +#import "RBBDampedHarmonicOscillaton.h" + +SpecBegin(RBBDampedHarmonicOscillaton) + +it(@"should return correct values for fix points", ^{ + RBBOsciallation oscillation = RBBDampedHarmonicOscillation(1, 10, 1, 100, 0, YES); + + expect(oscillation(0.0)).to.beCloseToWithin( 1.00, 0.01); + expect(oscillation(0.1)).to.beCloseToWithin( 0.65, 0.01); + expect(oscillation(0.2)).to.beCloseToWithin( 0.15, 0.01); + expect(oscillation(0.3)).to.beCloseToWithin(-0.12, 0.01); + expect(oscillation(0.4)).to.beCloseToWithin(-0.15, 0.01); + expect(oscillation(0.5)).to.beCloseToWithin(-0.07, 0.01); + expect(oscillation(0.6)).to.beCloseToWithin( 0.00, 0.01); + expect(oscillation(0.7)).to.beCloseToWithin( 0.02, 0.01); + expect(oscillation(0.8)).to.beCloseToWithin( 0.02, 0.01); + expect(oscillation(0.9)).to.beCloseToWithin( 0.00, 0.01); + expect(oscillation(1.0)).to.beCloseToWithin( 0.00, 0.01); + + expect(oscillation(100)).to.beCloseToWithin(0, 0.01); +}); + +SpecEnd From 71211d31cd86d149d8edd2ebecd00828539f247d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20B=C3=B6hnke?= Date: Thu, 10 Apr 2014 19:03:03 +0200 Subject: [PATCH 09/12] Add specs for RBBRubberbandAnimation --- RBBAnimation.xcodeproj/project.pbxproj | 4 ++ Specs/RBBRubberbandAnimationSpec.m | 53 ++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) create mode 100644 Specs/RBBRubberbandAnimationSpec.m diff --git a/RBBAnimation.xcodeproj/project.pbxproj b/RBBAnimation.xcodeproj/project.pbxproj index d36efbc..aa4b5b6 100644 --- a/RBBAnimation.xcodeproj/project.pbxproj +++ b/RBBAnimation.xcodeproj/project.pbxproj @@ -30,6 +30,7 @@ 5479BEA4181D8008009811FD /* RBBCustomAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 5479BEA3181D8008009811FD /* RBBCustomAnimation.m */; }; 549ECA8418F6ECFC00D968C7 /* RBBSpringAnimationSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 549ECA8318F6ECFC00D968C7 /* RBBSpringAnimationSpec.m */; }; 549ECA8618F7010400D968C7 /* RBBDampedHarmonicOscillatonSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 549ECA8518F7010400D968C7 /* RBBDampedHarmonicOscillatonSpec.m */; }; + 549ECA8818F7028E00D968C7 /* RBBRubberbandAnimationSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 549ECA8718F7028E00D968C7 /* RBBRubberbandAnimationSpec.m */; }; 54D5EA89181AE8620014896C /* RBBLinearInterpolation.m in Sources */ = {isa = PBXBuildFile; fileRef = 54D5EA88181AE8620014896C /* RBBLinearInterpolation.m */; }; 54F3AE2E1807394800E1E688 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 54F3AE2D1807394800E1E688 /* Foundation.framework */; }; 54F3AE331807394800E1E688 /* RBBAnimation.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 54F3AE321807394800E1E688 /* RBBAnimation.h */; }; @@ -108,6 +109,7 @@ 5479BEA3181D8008009811FD /* RBBCustomAnimation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RBBCustomAnimation.m; sourceTree = ""; }; 549ECA8318F6ECFC00D968C7 /* RBBSpringAnimationSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RBBSpringAnimationSpec.m; sourceTree = ""; }; 549ECA8518F7010400D968C7 /* RBBDampedHarmonicOscillatonSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RBBDampedHarmonicOscillatonSpec.m; sourceTree = ""; }; + 549ECA8718F7028E00D968C7 /* RBBRubberbandAnimationSpec.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RBBRubberbandAnimationSpec.m; sourceTree = ""; }; 54D5EA87181AE8620014896C /* RBBLinearInterpolation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RBBLinearInterpolation.h; sourceTree = ""; }; 54D5EA88181AE8620014896C /* RBBLinearInterpolation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RBBLinearInterpolation.m; sourceTree = ""; }; 54F3AE2A1807394800E1E688 /* libRBBAnimation.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRBBAnimation.a; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -269,6 +271,7 @@ 5466C56218082D4200652BDC /* RBBBlockBasedArraySpec.m */, 549ECA8518F7010400D968C7 /* RBBDampedHarmonicOscillatonSpec.m */, 549ECA8318F6ECFC00D968C7 /* RBBSpringAnimationSpec.m */, + 549ECA8718F7028E00D968C7 /* RBBRubberbandAnimationSpec.m */, 54F3AE441807394800E1E688 /* Supporting Files */, ); path = Specs; @@ -508,6 +511,7 @@ 5466C56318082D4200652BDC /* RBBBlockBasedArraySpec.m in Sources */, 549ECA8418F6ECFC00D968C7 /* RBBSpringAnimationSpec.m in Sources */, 549ECA8618F7010400D968C7 /* RBBDampedHarmonicOscillatonSpec.m in Sources */, + 549ECA8818F7028E00D968C7 /* RBBRubberbandAnimationSpec.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Specs/RBBRubberbandAnimationSpec.m b/Specs/RBBRubberbandAnimationSpec.m new file mode 100644 index 0000000..e4aa5f9 --- /dev/null +++ b/Specs/RBBRubberbandAnimationSpec.m @@ -0,0 +1,53 @@ +// +// RBBRubberbandAnimationSpec.m +// RBBAnimation +// +// Created by Robert Böhnke on 10/04/14. +// Copyright (c) 2014 Robert Böhnke. All rights reserved. +// + +#import + +#import "RBBSpringAnimation.h" + +#import "RBBRubberbandAnimation.h" + +SpecBegin(RBBRubberbandAnimation) + +it(@"should initialize", ^{ + expect([RBBRubberbandAnimation animation]).notTo.beNil(); + + expect([RBBRubberbandAnimation animationWithKeyPath:@"position"]).notTo.beNil(); +}); + +it(@"should behave like RBBSpringAnimations if v is 0", ^{ + RBBSpringAnimation *spring = [RBBSpringAnimation animationWithKeyPath:@"position"]; + spring.fromValue = [NSValue valueWithCGPoint:CGPointMake(100, 200)]; + spring.toValue = [NSValue valueWithCGPoint:CGPointMake(300, 400)]; + spring.velocity = 0; + + RBBRubberbandAnimation *rubberband = [RBBRubberbandAnimation animationWithKeyPath:@"position"]; + rubberband.from = CGPointMake(100, 200); + rubberband.to = CGPointMake(300, 400); + rubberband.velocity = CGPointZero; + + expect(rubberband.values).to.equal(spring.values); +}); + +it(@"should take the velocity into account even if ∆x and ∆y are 0", ^{ + RBBRubberbandAnimation *animation = [RBBRubberbandAnimation animationWithKeyPath:@"position"]; + animation.from = CGPointZero; + animation.to = CGPointZero; + animation.velocity = CGPointMake(500, 0); + animation.duration = 1; + + CGFloat maxX = -HUGE_VALF; + + for (NSValue *value in animation.values) { + if (value.CGPointValue.x > maxX) maxX = value.CGPointValue.x; + } + + expect(maxX).to.beCloseToWithin(27, 0.5); +}); + +SpecEnd From 20403864c62280a30d1f5fb3413f7756f2151c57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20B=C3=B6hnke?= Date: Mon, 21 Apr 2014 11:56:39 +0200 Subject: [PATCH 10/12] Fix target membership --- RBBAnimation.xcodeproj/project.pbxproj | 12 ++++++++++++ Specs/RBBLinearInterpolationSpec.m | 4 ---- Specs/RBBRubberbandAnimationSpec.m | 8 ++++---- Specs/Specs-Prefix.pch | 6 ++++++ 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/RBBAnimation.xcodeproj/project.pbxproj b/RBBAnimation.xcodeproj/project.pbxproj index b6d4e38..bed3132 100644 --- a/RBBAnimation.xcodeproj/project.pbxproj +++ b/RBBAnimation.xcodeproj/project.pbxproj @@ -7,6 +7,12 @@ objects = { /* Begin PBXBuildFile section */ + 5409356419052226007565F7 /* RBBDampedHarmonicOscillaton.m in Sources */ = {isa = PBXBuildFile; fileRef = 5466A9B418F6C2260090E3E6 /* RBBDampedHarmonicOscillaton.m */; }; + 540935651905223A007565F7 /* RBBDampedHarmonicOscillaton.h in Headers */ = {isa = PBXBuildFile; fileRef = 5466A9B318F6C2260090E3E6 /* RBBDampedHarmonicOscillaton.h */; }; + 540935661905223D007565F7 /* RBBRubberbandAnimation.h in Headers */ = {isa = PBXBuildFile; fileRef = 545FEFD418F177B000900B78 /* RBBRubberbandAnimation.h */; }; + 5409356719052241007565F7 /* RBBRubberbandAnimation.m in Sources */ = {isa = PBXBuildFile; fileRef = 545FEFD518F177B000900B78 /* RBBRubberbandAnimation.m */; }; + 540935681905224C007565F7 /* RBBDampedHarmonicOscillatonSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 549ECA8518F7010400D968C7 /* RBBDampedHarmonicOscillatonSpec.m */; }; + 5409356919052251007565F7 /* RBBRubberbandAnimationSpec.m in Sources */ = {isa = PBXBuildFile; fileRef = 549ECA8718F7028E00D968C7 /* RBBRubberbandAnimationSpec.m */; }; 540F65A318F81DC9005A6BB8 /* Pods-RBBAnimationTest.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 540F65A218F81DC9005A6BB8 /* Pods-RBBAnimationTest.xcconfig */; }; 540F65A718F82347005A6BB8 /* NSValue+PlatformIndependence.h in Headers */ = {isa = PBXBuildFile; fileRef = 540F65A518F82347005A6BB8 /* NSValue+PlatformIndependence.h */; settings = {ATTRIBUTES = (Private, ); }; }; 540F65A818F82347005A6BB8 /* NSValue+PlatformIndependence.m in Sources */ = {isa = PBXBuildFile; fileRef = 540F65A618F82347005A6BB8 /* NSValue+PlatformIndependence.m */; }; @@ -402,6 +408,8 @@ 549ECAC418F8157200D968C7 /* RBBSpringAnimation.h in Headers */, 549ECACA18F8157200D968C7 /* RBBCubicBezier.h in Headers */, 549ECAC018F8157200D968C7 /* RBBCustomAnimation.h in Headers */, + 540935651905223A007565F7 /* RBBDampedHarmonicOscillaton.h in Headers */, + 540935661905223D007565F7 /* RBBRubberbandAnimation.h in Headers */, 549ECAC618F8157200D968C7 /* RBBLinearInterpolation.h in Headers */, 540F65A718F82347005A6BB8 /* NSValue+PlatformIndependence.h in Headers */, 540F65AF18F83EB3005A6BB8 /* NSColor+PlatformIndependence.h in Headers */, @@ -711,6 +719,8 @@ 549ECAC718F8157200D968C7 /* RBBLinearInterpolation.m in Sources */, 549ECABF18F8157200D968C7 /* RBBAnimation.m in Sources */, 549ECACD18F8157200D968C7 /* RBBBlockBasedArray.m in Sources */, + 5409356719052241007565F7 /* RBBRubberbandAnimation.m in Sources */, + 5409356419052226007565F7 /* RBBDampedHarmonicOscillaton.m in Sources */, 549ECACB18F8157200D968C7 /* RBBCubicBezier.m in Sources */, 540F65A918F82347005A6BB8 /* NSValue+PlatformIndependence.m in Sources */, ); @@ -723,6 +733,8 @@ 549ECABD18F8155800D968C7 /* RBBBlockBasedArraySpec.m in Sources */, 549ECABE18F8155B00D968C7 /* RBBSpringAnimationSpec.m in Sources */, 540F65AC18F82C67005A6BB8 /* RBBLinearInterpolationSpec.m in Sources */, + 5409356919052251007565F7 /* RBBRubberbandAnimationSpec.m in Sources */, + 540935681905224C007565F7 /* RBBDampedHarmonicOscillatonSpec.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Specs/RBBLinearInterpolationSpec.m b/Specs/RBBLinearInterpolationSpec.m index 1d1c8ad..a7a1bc8 100644 --- a/Specs/RBBLinearInterpolationSpec.m +++ b/Specs/RBBLinearInterpolationSpec.m @@ -7,14 +7,10 @@ // #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR -#import - #import "UIColor+PlatformIndependence.h" #define SpecsColor UIColor #else -#import - #import "NSColor+PlatformIndependence.h" #define SpecsColor NSColor diff --git a/Specs/RBBRubberbandAnimationSpec.m b/Specs/RBBRubberbandAnimationSpec.m index e4aa5f9..079194a 100644 --- a/Specs/RBBRubberbandAnimationSpec.m +++ b/Specs/RBBRubberbandAnimationSpec.m @@ -6,7 +6,7 @@ // Copyright (c) 2014 Robert Böhnke. All rights reserved. // -#import +#import "NSValue+PlatformIndependence.h" #import "RBBSpringAnimation.h" @@ -22,8 +22,8 @@ it(@"should behave like RBBSpringAnimations if v is 0", ^{ RBBSpringAnimation *spring = [RBBSpringAnimation animationWithKeyPath:@"position"]; - spring.fromValue = [NSValue valueWithCGPoint:CGPointMake(100, 200)]; - spring.toValue = [NSValue valueWithCGPoint:CGPointMake(300, 400)]; + spring.fromValue = [NSValue rbb_valueWithCGPoint:CGPointMake(100, 200)]; + spring.toValue = [NSValue rbb_valueWithCGPoint:CGPointMake(300, 400)]; spring.velocity = 0; RBBRubberbandAnimation *rubberband = [RBBRubberbandAnimation animationWithKeyPath:@"position"]; @@ -44,7 +44,7 @@ CGFloat maxX = -HUGE_VALF; for (NSValue *value in animation.values) { - if (value.CGPointValue.x > maxX) maxX = value.CGPointValue.x; + if (value.rbb_CGPointValue.x > maxX) maxX = value.rbb_CGPointValue.x; } expect(maxX).to.beCloseToWithin(27, 0.5); diff --git a/Specs/Specs-Prefix.pch b/Specs/Specs-Prefix.pch index c6549aa..68e913f 100644 --- a/Specs/Specs-Prefix.pch +++ b/Specs/Specs-Prefix.pch @@ -1,6 +1,12 @@ #ifdef __OBJC__ #import + #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR + #import + #else + #import + #endif + #import #define EXP_SHORTHAND From 225c3c7b6b671fc959b89f202b73ea9767af7211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20B=C3=B6hnke?= Date: Mon, 21 Apr 2014 12:02:00 +0200 Subject: [PATCH 11/12] Use NSValue+PlatformIndependence in rubberband animation --- RBBAnimation/NSValue+PlatformIndependence.h | 4 ++++ RBBAnimation/NSValue+PlatformIndependence.m | 4 ---- RBBAnimation/RBBRubberbandAnimation.m | 10 ++-------- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/RBBAnimation/NSValue+PlatformIndependence.h b/RBBAnimation/NSValue+PlatformIndependence.h index dd29d0e..e58428b 100644 --- a/RBBAnimation/NSValue+PlatformIndependence.h +++ b/RBBAnimation/NSValue+PlatformIndependence.h @@ -6,6 +6,10 @@ // Copyright (c) 2014 Robert Böhnke. All rights reserved. // +#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR +#import +#endif + #import @interface NSValue (PlatformIndependence) diff --git a/RBBAnimation/NSValue+PlatformIndependence.m b/RBBAnimation/NSValue+PlatformIndependence.m index da04b5a..c56c172 100644 --- a/RBBAnimation/NSValue+PlatformIndependence.m +++ b/RBBAnimation/NSValue+PlatformIndependence.m @@ -6,10 +6,6 @@ // Copyright (c) 2014 Robert Böhnke. All rights reserved. // -#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR -#import -#endif - #import "NSValue+PlatformIndependence.h" @implementation NSValue (PlatformIndependence) diff --git a/RBBAnimation/RBBRubberbandAnimation.m b/RBBAnimation/RBBRubberbandAnimation.m index 4397445..1d565d3 100644 --- a/RBBAnimation/RBBRubberbandAnimation.m +++ b/RBBAnimation/RBBRubberbandAnimation.m @@ -6,9 +6,7 @@ // Copyright (c) 2014 Robert Böhnke. All rights reserved. // -#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR -#import -#endif +#import "NSValue+PlatformIndependence.h" #import "RBBDampedHarmonicOscillaton.h" @@ -71,11 +69,7 @@ - (RBBAnimationBlock)animationBlock { return ^(CGFloat t, CGFloat _) { CGPoint p = { .x = x0 + oscillationX(t), .y = y0 + oscillationY(t) }; - #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR - return [NSValue valueWithCGPoint:p]; - #elif TARGET_OS_MAC - return [NSValue valueWithPoint:p]; - #endif + return [NSValue rbb_valueWithCGPoint:p]; }; } From 86ed282def792987650771765ea73f22634d93c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20B=C3=B6hnke?= Date: Mon, 21 Apr 2014 12:03:35 +0200 Subject: [PATCH 12/12] WS --- RBBAnimation/RBBDampedHarmonicOscillaton.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RBBAnimation/RBBDampedHarmonicOscillaton.m b/RBBAnimation/RBBDampedHarmonicOscillaton.m index 7c3a38d..9094813 100644 --- a/RBBAnimation/RBBDampedHarmonicOscillaton.m +++ b/RBBAnimation/RBBDampedHarmonicOscillaton.m @@ -42,4 +42,4 @@ RBBOsciallation RBBDampedHarmonicOscillation(CGFloat x0, CGFloat b, CGFloat m, C return envelope * (x0 * coshf(omega2 * t) + ((beta * x0 + v0) / omega2) * sinhf(omega2 * t)); }; } -} \ No newline at end of file +}