简体   繁体   中英

Convert a quadratic bezier to a cubic one

将二次贝塞尔曲线(3 个点)转换为三次贝塞尔曲线(4 个点)的算法是什么?

From https://fontforge.org/docs/techref/bezier.html#converting-truetype-to-postscript :

Any quadratic spline can be expressed as a cubic (where the cubic term is zero). The end points of the cubic will be the same as the quadratic's.

CP 0 = QP 0
CP 3 = QP 2

The two control points for the cubic are:

CP 1 = QP 0 + 2/3 *(QP 1 -QP 0 )
CP 2 = QP 2 + 2/3 *(QP 1 -QP 2 )

...There is a slight error introduced due to rounding, but it is unlikely to be noticeable.

Just giving a proof for the accepted answer.

A quadratic Bezier is expressed as:

Q(t) = Q 0 (1-t)² + 2 Q 1 (1-t) t + Q 2

A cubic Bezier is expressed as:

C(t) = C 0 (1-t)³ + 3 C 1 (1-t)² t + 3 C 2 (1-t) t² + C 3

For those two polynomials to be equals, all their polynomial coefficients must be equal. The polynomial coefficents are obtained by developing the expressions (example: (1-t)² = 1 - 2t + t²), then factorizing all terms in 1, t, t², and t³:

Q(t) = Q 0 + (-2Q 0 + 2Q 1 ) t + (Q 0 - 2Q 1 + Q 2 ) t²

C(t) = C 0 + (-3C 0 + 3C 1 ) t + (3C 0 - 6C 1 + 3C 2 ) t² + (-C 0 + 3C 1 -3C 2 + C 3 ) t³

Therefore, we get the following 4 equations:

C 0 = Q 0

-3C 0 + 3C 1 = -2Q 0 + 2Q 1

3C 0 - 6C 1 + 3C 2 = Q 0 - 2Q 1 + Q 2

-C 0 + 3C 1 -3C 2 + C 3 = 0

We can solve for C 1 by simply substituting C 0 by Q 0 in the 2nd row, which gives:

C 1 = Q 0 + (2/3) (Q 1 - Q 0 )

Then, we can either continue to substitute to solve for C 2 then C 3 , or more elegantly notice the symmetry in the original equations under the change of variable t' = 1-t , and conclude:

C 0 = Q 0

C 1 = Q 0 + (2/3) (Q 1 - Q 0 )

C 2 = Q 2 + (2/3) (Q 1 - Q 2 )

C 3 = Q 2

For reference, I implemented addQuadCurve for NSBezierPath (macOS Swift 4) based on Owen's answer above .

extension NSBezierPath {
    public func addQuadCurve(to qp2: CGPoint, controlPoint qp1: CGPoint) {
        let qp0 = self.currentPoint
        self.curve(to: qp2,
            controlPoint1: qp0 + (2.0/3.0)*(qp1 - qp0),
            controlPoint2: qp2 + (2.0/3.0)*(qp1 - qp2))
    }
}

extension CGPoint {
    // Vector math
    public static func +(left: CGPoint, right: CGPoint) -> CGPoint {
        return CGPoint(x: left.x + right.x, y: left.y + right.y)
    }
    public static func -(left: CGPoint, right: CGPoint) -> CGPoint {
        return CGPoint(x: left.x - right.x, y: left.y - right.y)
    }
    public static func *(left: CGFloat, right: CGPoint) -> CGPoint {
        return CGPoint(x: left * right.x, y: left * right.y)
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM