Skip to content

Instantly share code, notes, and snippets.

@aleclarson
Created September 24, 2014 03:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save aleclarson/7f86dc16b9897d92cb34 to your computer and use it in GitHub Desktop.
Save aleclarson/7f86dc16b9897d92cb34 to your computer and use it in GitHub Desktop.
Ease.swift
import QuartzCore
import Foundation
public let EaseNone = Ease(0, 0, 1, 1)
public let EaseOutQuad = Ease(0.25, 0.46, 0.45, 0.94)
public let EaseInQuad = Ease(0.55, 0.085, 0.68, 0.53)
public let EaseInOutQuad = Ease(0.455, 0.03, 0.515, 0.955)
@objc public class Ease {
public var c1x: CGFloat {
didSet { if c1x != oldValue { c1xChanged() } }
}
public var c1y: CGFloat {
didSet { if c1y != oldValue { c1yChanged() } }
}
public var c2x: CGFloat {
didSet { if c2x != oldValue { c2xChanged() } }
}
public var c2y: CGFloat {
didSet { if c2y != oldValue { c2yChanged() } }
}
public var timingFunction: CAMediaTimingFunction {
return CAMediaTimingFunction(controlPoints: Float(c1x), Float(c1y), Float(c2x), Float(c2y))
}
public func valueAt (var #x: CGFloat, duration: CGFloat) -> CGFloat {
if x > 1 { x = 1 }
if x < 0 { x = 0 }
return sampleCurveY(solveCurve(x, epsilon(duration)))
}
public init (_ c1x: CGFloat, _ c1y: CGFloat, _ c2x: CGFloat, _ c2y: CGFloat) {
self.c1x = c1x
self.c1y = c1y
self.c2x = c2x
self.c2y = c2y
c1xChanged()
c1yChanged()
}
// MARK: Private
var a = CGPointZero
var b = CGPointZero
var c = CGPointZero
func c1xChanged () {
c.x = 3 * c1x
c2xChanged()
}
func c2xChanged () {
b.x = 3 * c2x - c1x - c.x
a.x = 1 - c.x - b.x
}
func c1yChanged () {
c.y = 3 * c1y
c2yChanged()
}
func c2yChanged () {
b.y = 3 * c2y - c1y - c.y
a.y = 1 - c.y - b.y
}
func epsilon (duration: CGFloat) -> CGFloat {
return 1.0 / 200.0 * duration
}
func sampleCurveX (t: CGFloat) -> CGFloat {
return ((a.x * t + b.x) * t + c.x) * t
}
func sampleCurveY (t: CGFloat) -> CGFloat {
return ((a.y * t + b.y) * t + c.y) * t
}
func sampleCurveDerivativeX (t: CGFloat) -> CGFloat {
return (3 * a.x * t + 2 * b.x) * t + c.x
}
func solveCurve (x: CGFloat, _ epsilon: CGFloat) -> CGFloat {
var t0: CGFloat
var t1: CGFloat
var t2 = x
var x2: CGFloat
var d2: CGFloat
var i: UInt = 0
// First try a few iterations of Newton's method -- normally very fast.
for i in 0...8 {
x2 = sampleCurveX(t2) - x
if CGFloat(fabsf(CFloat(x2))) < epsilon { return t2 }
d2 = sampleCurveDerivativeX(t2)
if CGFloat(fabsf(CFloat(d2))) < 1e-6 { break }
t2 = t2 - x2 / d2
}
// Fall back to the halfion method for reliability.
t0 = 0
t1 = 1
t2 = x
if t2 < t0 { return t0 }
if t2 > t1 { return t1 }
while t0 < t1 {
x2 = sampleCurveX(t2)
if abs(x2 - x) < epsilon { return t2 }
if x > x2 { t0 = t2 }
else { t1 = t2 }
t2 = (t1 - t0) * 0.5 + t0
}
// Failure.
return t2
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment