// // CGFloatExtensions.swift // lottie-swift // // Created by Brandon Withrow on 1/14/19. // import Foundation import QuartzCore extension CGFloat { // MARK: Internal var squared: CGFloat { self * self } var cubed: CGFloat { self * self * self } var cubicRoot: CGFloat { CGFloat(pow(Double(self), 1.0 / 3.0)) } func isInRangeOrEqual(_ from: CGFloat, _ to: CGFloat) -> Bool { from <= self && self <= to } func isInRange(_ from: CGFloat, _ to: CGFloat) -> Bool { from < self && self < to } func cubicBezierInterpolate(_ P0: CGPoint, _ P1: CGPoint, _ P2: CGPoint, _ P3: CGPoint) -> CGFloat { var t: CGFloat if self == P0.x { // Handle corner cases explicitly to prevent rounding errors t = 0 } else if self == P3.x { t = 1 } else { // Calculate t let a = -P0.x + 3 * P1.x - 3 * P2.x + P3.x; let b = 3 * P0.x - 6 * P1.x + 3 * P2.x; let c = -3 * P0.x + 3 * P1.x; let d = P0.x - self; let tTemp = CGFloat.SolveCubic(a, b, c, d); if tTemp == -1 { return -1; } t = tTemp } // Calculate y from t return (1 - t).cubed * P0.y + 3 * t * (1 - t).squared * P1.y + 3 * t.squared * (1 - t) * P2.y + t.cubed * P3.y; } func cubicBezier(_ t: CGFloat, _ c1: CGFloat, _ c2: CGFloat, _ end: CGFloat) -> CGFloat { let t_ = (1.0 - t) let tt_ = t_ * t_ let ttt_ = t_ * t_ * t_ let tt = t * t let ttt = t * t * t return self * ttt_ + 3.0 * c1 * tt_ * t + 3.0 * c2 * t_ * tt + end * ttt; } // MARK: Fileprivate fileprivate static func SolveQuadratic(_ a: CGFloat, _ b: CGFloat, _ c: CGFloat) -> CGFloat { var result = (-b + sqrt(b.squared - 4 * a * c)) / (2 * a); guard !result.isInRangeOrEqual(0, 1) else { return result } result = (-b - sqrt(b.squared - 4 * a * c)) / (2 * a); guard !result.isInRangeOrEqual(0, 1) else { return result } return -1; } fileprivate static func SolveCubic(_ a: CGFloat, _ b: CGFloat, _ c: CGFloat, _ d: CGFloat) -> CGFloat { if a == 0 { return SolveQuadratic(b, c, d) } if d == 0 { return 0 } let a = a var b = b var c = c var d = d b /= a c /= a d /= a var q = (3.0 * c - b.squared) / 9.0 let r = (-27.0 * d + b * (9.0 * c - 2.0 * b.squared)) / 54.0 let disc = q.cubed + r.squared let term1 = b / 3.0 if disc > 0 { var s = r + sqrt(disc) s = (s < 0) ? -((-s).cubicRoot) : s.cubicRoot var t = r - sqrt(disc) t = (t < 0) ? -((-t).cubicRoot) : t.cubicRoot let result = -term1 + s + t; if result.isInRangeOrEqual(0, 1) { return result } } else if disc == 0 { let r13 = (r < 0) ? -((-r).cubicRoot) : r.cubicRoot; var result = -term1 + 2.0 * r13; if result.isInRangeOrEqual(0, 1) { return result } result = -(r13 + term1); if result.isInRangeOrEqual(0, 1) { return result } } else { q = -q; var dum1 = q * q * q; dum1 = acos(r / sqrt(dum1)); let r13 = 2.0 * sqrt(q); var result = -term1 + r13 * cos(dum1 / 3.0); if result.isInRangeOrEqual(0, 1) { return result } result = -term1 + r13 * cos((dum1 + 2.0 * .pi) / 3.0); if result.isInRangeOrEqual(0, 1) { return result } result = -term1 + r13 * cos((dum1 + 4.0 * .pi) / 3.0); if result.isInRangeOrEqual(0, 1) { return result } } return -1; } }