Seth Willits was working on an interpolation protocol that would allow conforming constructs to interpolate from one value to another, regardless of their underlying types. It needed to work for `CGFloat` as well as `Double`, and be decomposable and useful for `CGPoint` and `CGRect` as well as any custom Swift `Polygon` struct.

This created interesting challenges, as some of the most fundamental animation curves use exponentiation, which is not built into the Swift standard library. So I turned to `pow`, which is only defined for float and double:

```public func powf(_: Float, _: Float) -> Float
public func pow(_: Double, _: Double) -> Double```

In the past, I’ve created horrible bridging solution that permits migration from most floating point values to double. It’s not beautiful but I decided to pull it out from my toolkit and introduce it to this problem.

What I did was to build an extension on `BinaryFloatingPoint` that returned an interpolated value along a styled curve. My hack let me create a single `interpolate` method that applied across floating-point. I bridged to `Double` to use the Darwin `pow` function: There’s an interesting `CGFloat` bug in play here, where the value history does not display as a graph but the numbers are right and work properly during animation.

Here’s the code. Shout out your improvements and alternatives.

• Hi,

I’d have written the first extension as:

``` extension BinaryFloatingPoint { public var doubleValue: Double { switch self { case let value as Double: return value case let value as Float: return Double(value) case let value as Float80: return Double(value) case let value as CGFloat: return Double(value) default: fatalError("Unsupported floating point type") } } } ```

• Hi Erica,

The following rewrite of your code moves the curve functions to the InterpolationCurve enum, where I think they conceptually belong, rather than in an extension on a numeric protocol. An additional benefit is that it doesn’t need your extension on BinaryFloatingPoint that provides a doubleValue property.

``` import Darwin // for pow(_:_:) public enum InterpolationCurve { case linear, easeIn, easeOut, easeInOut public func f(_ x: Double) -> Double { switch self { case .linear: return x case .easeIn: return pow(x, 3) case .easeOut: return 1 - pow(1 - x, 3) case .easeInOut where x Self { return self + (other - self) * Self(curve.f(fraction)) } } for fraction in stride(from: 0.0, through: 1.0, by: 0.05) { 0.0.interpolate(to: 1.0, by: fraction, of: .linear) 0.0.interpolate(to: 1.0, by: fraction, of: .easeIn) 0.0.interpolate(to: 1.0, by: fraction, of: .easeOut) 0.0.interpolate(to: 1.0, by: fraction, of: .easeInOut) } import CoreGraphics // for CGPoint public protocol Interpolating { func interpolate(to other: Self, by fraction: Double, of curve: InterpolationCurve) -> Self } extension CGPoint: Interpolating { public func interpolate(to other: CGPoint, by fraction: Double, of curve: InterpolationCurve) -> CGPoint { return CGPoint( x: self.x.interpolate(to: other.x, by: fraction, of: curve), y: self.y.interpolate(to: other.y, by: fraction, of: curve) ) } } let p1 = CGPoint.zero let p2 = CGPoint(x: 1, y: 1) for fraction in stride(from: 0.0, through: 1.0, by: 0.05) { p1.interpolate(to: p2, by: fraction, of: .easeInOut) } ```

• The blog somehow mangled my code (besides the annoying double-spacing it creates). I’ll try reposting the InterpolatingCurve enum.

``` public enum InterpolationCurve { case linear, easeIn, easeOut, easeInOut public func f(_ x: Double) -> Double { switch self { case .linear: return x case .easeIn: return pow(x, 3) case .easeOut: return 1 - pow(1 - x, 3) case .easeInOut where x < 0.5: return 0.5 * pow(2 * x , 3) case .easeInOut: return 1 - 0.5 * pow(2 * (1 - x), 3) } } } ```

• It’s OK now. 😀

• Arrrrrggghhh! Now I see there are other lines missing in my first attempt. I’ll try posting the whole thing again.

``` import Darwin // for pow(_:_:) public enum InterpolationCurve { case linear, easeIn, easeOut, easeInOut public func f(_ x: Double) -> Double { switch self { case .linear: return x case .easeIn: return pow(x, 3) case .easeOut: return 1 - pow(1 - x, 3) case .easeInOut where x Self { return self + (other - self) * Self(curve.f(fraction)) } } for fraction in stride(from: 0.0, through: 1.0, by: 0.05) { 0.0.interpolate(to: 1.0, by: fraction, of: .linear) 0.0.interpolate(to: 1.0, by: fraction, of: .easeIn) 0.0.interpolate(to: 1.0, by: fraction, of: .easeOut) 0.0.interpolate(to: 1.0, by: fraction, of: .easeInOut) } import CoreGraphics // for CGPoint public protocol Interpolating { func interpolate(to other: Self, by fraction: Double, of curve: InterpolationCurve) -> Self } extension CGPoint: Interpolating { public func interpolate(to other: CGPoint, by fraction: Double, of curve: InterpolationCurve) -> CGPoint { return CGPoint( x: self.x.interpolate(to: other.x, by: fraction, of: curve), y: self.y.interpolate(to: other.y, by: fraction, of: curve) ) } } let p1 = CGPoint.zero let p2 = CGPoint(x: 1, y: 1) for fraction in stride(from: 0.0, through: 1.0, by: 0.05) { p1.interpolate(to: p2, by: fraction, of: .easeInOut) } ```

• Nope, the same lines as vanished the first time have vanished again. I’ll try a series of posts.

Part 1:

``` import Darwin // for pow(_:_:) public enum InterpolationCurve { case linear, easeIn, easeOut, easeInOut public func f(_ x: Double) -> Double { switch self { case .linear: return x case .easeIn: return pow(x, 3) case .easeOut: return 1 - pow(1 - x, 3) case .easeInOut where x < 0.5: return 0.5 * pow(2 * x , 3) case .easeInOut: return 1 - 0.5 * pow(2 * (1 - x), 3) } } } ```

• Part 2:

``` extension BinaryFloatingPoint { public func interpolate(to other: Self, by fraction: Double, of curve: InterpolationCurve = .linear) -> Self { return self + (other - self) * Self(curve.f(fraction)) } } for fraction in stride(from: 0.0, through: 1.0, by: 0.05) { 0.0.interpolate(to: 1.0, by: fraction, of: .linear) 0.0.interpolate(to: 1.0, by: fraction, of: .easeIn) 0.0.interpolate(to: 1.0, by: fraction, of: .easeOut) 0.0.interpolate(to: 1.0, by: fraction, of: .easeInOut) } ```

• Part 3:

``` import CoreGraphics // for CGPoint public protocol Interpolating { func interpolate(to other: Self, by fraction: Double, of curve: InterpolationCurve) -> Self } extension CGPoint: Interpolating { public func interpolate(to other: CGPoint, by fraction: Double, of curve: InterpolationCurve) -> CGPoint { return CGPoint( x: self.x.interpolate(to: other.x, by: fraction, of: curve), y: self.y.interpolate(to: other.y, by: fraction, of: curve) ) } } let p1 = CGPoint.zero let p2 = CGPoint(x: 1, y: 1) for fraction in stride(from: 0.0, through: 1.0, by: 0.05) { p1.interpolate(to: p2, by: fraction, of: .easeInOut) } ```

• Success at last (fingers crossed). Let me know if it doesn’t compile.