Skip to content

Instantly share code, notes, and snippets.

@jstn
Last active December 13, 2015 17:19
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jstn/4947040 to your computer and use it in GitHub Desktop.
Save jstn/4947040 to your computer and use it in GitHub Desktop.
iOS tweening, adapted from Webkit
#include "JXBezier.h"
#include <math.h>
JXUnitBezier JXUnitBezierMake(double p1x, double p1y, double p2x, double p2y) {
JXUnitBezier bezier;
bezier.cx = 3.0 * p1x;
bezier.bx = 3.0 * (p2x - p1x) - bezier.cx;
bezier.ax = 1.0 - bezier.cx - bezier.bx;
bezier.cy = 3.0 * p1y;
bezier.by = 3.0 * (p2y - p1y) - bezier.cy;
bezier.ay = 1.0 - bezier.cy - bezier.by;
return bezier;
}
double JXBezierEpsilon(double duration) {
return 1.0 / (200.0 * duration);
}
double JXUnitBezierSampleCurveDerivativeX(JXUnitBezier b, double t) {
return (3.0 * b.ax * t + 2.0 * b.bx) * t + b.cx;
}
double JXUnitBezierSampleCurveX(JXUnitBezier b, double t) {
return ((b.ax * t + b.bx) * t + b.cx) * t;
}
double JXUnitBezierSampleCurveY(JXUnitBezier b, double t) {
return ((b.ay * t + b.by) * t + b.cy) * t;
}
double JXUnitBezierSolveCurveX(JXUnitBezier b, double x, double epsilon) {
double t0;
double t1;
double t2;
double x2;
double d2;
int i;
for (t2 = x, i = 0; i < 8; i++) {
x2 = JXUnitBezierSampleCurveX(b, t2) - x;
if (fabs(x2) < epsilon)
return t2;
d2 = JXUnitBezierSampleCurveDerivativeX(b, t2);
if (fabs(d2) < 1e-6)
break;
t2 = t2 - x2 / d2;
}
t0 = 0.0;
t1 = 1.0;
t2 = x;
if (t2 < t0)
return t0;
if (t2 > t1)
return t1;
while (t0 < t1) {
x2 = JXUnitBezierSampleCurveX(b, t2);
if (fabs(x2 - x) < epsilon)
return t2;
if (x > x2)
t0 = t2;
else
t1 = t2;
t2 = (t1 - t0) * .5 + t0;
}
return t2;
}
double JXUnitBezierSolve(JXUnitBezier b, double x, double epsilon) {
return JXUnitBezierSampleCurveY(b, JXUnitBezierSolveCurveX(b, x, epsilon));
}
double JXCubicBezierSolve(double p1x, double p1y, double p2x, double p2y, double t, double duration) {
JXUnitBezier bezier = JXUnitBezierMake(p1x, p1y, p2x, p2y);
return JXUnitBezierSolve(bezier, t, JXBezierEpsilon(duration));
}
#ifndef _JXBEZIER_H_
#define _JXBEZIER_H_
typedef struct {
double ax;
double bx;
double cx;
double ay;
double by;
double cy;
} JXUnitBezier;
JXUnitBezier JXUnitBezierMake(double p1x, double p1y, double p2x, double p2y);
double JXBezierEpsilon(double duration);
double JXUnitBezierSampleCurveDerivativeX(JXUnitBezier b, double t);
double JXUnitBezierSampleCurveX(JXUnitBezier b, double t);
double JXUnitBezierSampleCurveY(JXUnitBezier b, double t);
double JXUnitBezierSolveCurveX(JXUnitBezier b, double x, double epsilon);
double JXUnitBezierSolve(JXUnitBezier b, double x, double epsilon);
double JXCubicBezierSolve(double p1x, double p1y, double p2x, double p2y, double t, double duration);
#endif
#import <QuartzCore/QuartzCore.h>
@interface JXTween : NSObject
@property (strong, nonatomic) CAMediaTimingFunction *timingFunction;
@property (assign, nonatomic) double startTime;
@property (assign, nonatomic) double duration;
@property (assign, nonatomic) double startValue;
@property (assign, nonatomic) double endValue;
+ (instancetype)tweenWithFunction:(CAMediaTimingFunction *)tf
startTime:(double)st
duration:(double)du
startValue:(double)sv
endValue:(double)ev;
- (double)valueAtTime:(double)now;
@end
#import "JXTween.h"
#import "JXBezier.h"
@interface JXTween ()
@property (assign, nonatomic) double p1x;
@property (assign, nonatomic) double p1y;
@property (assign, nonatomic) double p2x;
@property (assign, nonatomic) double p2y;
@end
@implementation JXTween
+ (instancetype)tweenWithFunction:(CAMediaTimingFunction *)tf
startTime:(double)st
duration:(double)du
startValue:(double)sv
endValue:(double)ev
{
JXTween *t = [[JXTween alloc] init];
t.timingFunction = tf;
t.startTime = st;
t.duration = du;
t.startValue = sv;
t.endValue = ev;
return t;
}
- (void)setTimingFunction:(CAMediaTimingFunction *)function
{
_timingFunction = function;
float p1[2];
[_timingFunction getControlPointAtIndex:1 values:p1];
float p2[2];
[_timingFunction getControlPointAtIndex:2 values:p2];
_p1x = (double)p1[0];
_p1y = (double)p1[1];
_p2x = (double)p2[0];
_p2y = (double)p2[1];
}
- (double)valueAtTime:(double)now
{
double elapsed = now - _startTime;
if (elapsed >= _duration)
return _endValue;
double fractionalTime = elapsed / _duration;
double solution = JXCubicBezierSolve(_p1x, _p1y, _p2x, _p2y, fractionalTime, _duration);
double delta = _endValue - _startValue;
double value = _startValue + (solution * delta);
return value;
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment