Fast protocol similar to Equatable

I am trying to create a protocol for types that are Lerp capable (linear interpolation). I'm declaring it in a similar way to how it is defined Equatable

:

protocol Lerpable {
    func lerp(from: Self, to: Self, alpha: Double) -> Self
}

      

Unfortunately, when I try to implement Lerpable

for Double

:

func lerp(from: Double, to: Double, alpha: Double) -> Double {
    return from + alpha * (to - from)
}
extension Double: Lerpable {}

      

I get an error: Type 'Double' does not conform to protocol 'Lerpable'

.

I assumed it would be pretty simple, but maybe I just don't understand how Equatable works. Or is this a special case in Swift? Any thoughts?


UPDATE: The correct answer is below , as well as the final version of the code, for other links:

protocol Lerpable {
    func lerp(to: Self, alpha: Double) -> Self
}

extension Double: Lerpable {
    func lerp(to: Double, alpha: Double) -> Double {
        return self + alpha * (to - self)
    }
}

func lerp<T: Lerpable>(from: T, to: T, alpha: Double) -> T {
    return from.lerp(to, alpha: alpha)
}

      

I added a global function lerp

so that I can refer to it as lerp(foo, bar, alpha: 0.5)


and not foo.lerp(bar, alpha: 0.5)

+3


source to share


2 answers


To fix your problem, you need to put a function lerp

inside yours extension

, for example:

extension Double: Lerpable {
    func lerp(from: Double, to: Double, alpha: Double) -> Double {
        return from + alpha * (to - from)
    }
}

      

If you look at Equatable

protocol

:

protocol Equatable {
    func == (lhs: Self, rhs: Self) -> Bool
}

      



The reason you declare your method (on ==

purpose) outside your type extension

is because it Equatable

wants you to overload the operator and the operators must be declared in the global scope. Here's an example to clarify:

protocol MyProtocol {
    func *** (lhs: Self, rhs: Self) -> Self
}

      

Now, to make an Int

accept protocol:

extension Int : MyProtocol {}

infix operator *** {}
func *** (lhs: Int, rhs: Int) -> Int {
    return lhs * rhs
}

      

+5


source


You can further abstract the implementation by adding typealias for the progress value 't'

public protocol Lerpable {
    typealias LerpProgressType
    func lerpTo(value: Self, t: LerpProgressType) -> Self
}
public func lerp<T:Lerpable>(from: T, _ to: T, _ t: T.LerpProgressType) -> T {
    return from.lerpTo(to, t: t)
}

// implementations
extension Double : Lerpable {
    public typealias LerpProgressType = Double
    public func lerpTo(value: Double, t: Double) -> Double {
        return (1.0 - t) * self + t * value
    }
}
extension Float : Lerpable {
    public typealias LerpProgressType = Float
    public func lerpTo(value: Float, t: Float) -> Float {
        return (1.0 - t) * self + t * value
    }
}
extension CGFloat : Lerpable {
    public typealias LerpProgressType = CGFloat
    public func lerpTo(value: CGFloat, t: CGFloat) -> CGFloat {
        return (1.0 - t) * self + t * value
    }
}

      

Now you can also extend various structures (like CGPoint, CGSize, CGRect, CATransform3D, etc.):



extension CGPoint : Lerpable {
    public typealias LerpProgressType = CGFloat
    public func lerpTo(value: CGPoint, t: CGFloat) -> CGPoint {
        return
            CGPoint(
                x: x.lerpTo(value.x, t),
                y: y.lerpTo(value.y, t)
            )
    }
}
extension CLLocationCoordinate2D : LinearInterpolation {
    public typealias LerpProgressType = CLLocationDegrees
    public func lerpTo(value: CLLocationCoordinate2D, t: CLLocationDegrees) -> CLLocationCoordinate2D {
        return
            CLLocationCoordinate2D(
                latitude:  latitude.lerpTo(value.latitude, t),
                longitude: longitude.lerpTo(value.longitude, t)
            )
    }
}

      

To implement linear interpolation in common structures:

public struct ValueRange<T> {
    public var start:T
    public var end:T
}

extension ValueRange where T:Lerpable {
    public func lerp(t: T.LerpProgressType) -> T {
        return start.lerpTo(end, t: t)
    }
}

      

0


source







All Articles