Find the tangent of a point on a cubic bezier curve

前端 未结 5 825
庸人自扰
庸人自扰 2020-11-29 23:43

For a cubic Bézier curve, with the usual four points a, b, c and d,

for a given value t,

how to most elegantly find the tangent at

5条回答
  •  感动是毒
    2020-11-30 00:14

    Here goes my Swift implementation.

    Which I tried my best to optimize for speed, by eliminating all redundant math operations. i.e. make the minimal numbers of calls to math operations. And use the least possible number of multiplications (which are much more expensive than sums).

    There are 0 multiplications to create the bezier. Then 3 multiplications to get a point of bezier. And 2 multiplications to get a tangent to the bezier.

    struct CubicBezier {
    
        private typealias Me = CubicBezier
        typealias Vector = CGVector
        typealias Point = CGPoint
        typealias Num = CGFloat
        typealias Coeficients = (C: Num, S: Num, M: Num, L: Num)
    
        let xCoeficients: Coeficients
        let yCoeficients: Coeficients
    
        static func coeficientsOfCurve(from c0: Num, through c1: Num, andThrough c2: Num, to c3: Num) -> Coeficients
        {
            let _3c0 = c0 + c0 + c0
            let _3c1 = c1 + c1 + c1
            let _3c2 = c2 + c2 + c2
            let _6c1 = _3c1 + _3c1
    
            let C = c3 - _3c2 + _3c1 - c0
            let S = _3c2 - _6c1 + _3c0
            let M = _3c1 - _3c0
            let L = c0
    
            return (C, S, M, L)
        }
    
        static func xOrYofCurveWith(coeficients coefs: Coeficients, at t: Num) -> Num
        {
            let (C, S, M, L) = coefs
            return ((C * t + S) * t + M) * t + L
        }
    
        static func xOrYofTangentToCurveWith(coeficients coefs: Coeficients, at t: Num) -> Num
        {
            let (C, S, M, _) = coefs
            return ((C + C + C) * t + S + S) * t + M
        }
    
        init(from start: Point, through c1: Point, andThrough c2: Point, to end: Point)
        {
            xCoeficients = Me.coeficientsOfCurve(from: start.x, through: c1.x, andThrough: c2.x, to: end.x)
            yCoeficients = Me.coeficientsOfCurve(from: start.y, through: c1.y, andThrough: c2.y, to: end.y)
        }
    
        func x(at t: Num) -> Num {
            return Me.xOrYofCurveWith(coeficients: xCoeficients, at: t)
        }
    
        func y(at t: Num) -> Num {
            return Me.xOrYofCurveWith(coeficients: yCoeficients, at: t)
        }
    
        func dx(at t: Num) -> Num {
            return Me.xOrYofTangentToCurveWith(coeficients: xCoeficients, at: t)
        }
    
        func dy(at t: Num) -> Num {
            return Me.xOrYofTangentToCurveWith(coeficients: yCoeficients, at: t)
        }
    
        func point(at t: Num) -> Point {
            return .init(x: x(at: t), y: y(at: t))
        }
    
        func tangent(at t: Num) -> Vector {
            return .init(dx: dx(at: t), dy: dy(at: t))
        }
    }
    

    Use like:

    let bezier = CubicBezier.init(from: .zero, through: .zero, andThrough: .zero, to: .zero)
    
    let point02 = bezier.point(at: 0.2)
    let point07 = bezier.point(at: 0.7)
    
    let tangent01 = bezier.tangent(at: 0.1)
    let tangent05 = bezier.tangent(at: 0.5)
    

提交回复
热议问题