将二次贝塞尔曲线转换为立方贝塞尔曲线

jma*_*erx 31 c c++ algorithm graphics vector

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

Owe*_* S. 47

来自http://fontforge.sourceforge.net/bezier.html:

任何二次样条可以表示为立方(其中立方项为零).立方体的终点与二次方的终点相同.

CP 0 = QP 0
CP 3 = QP 2

立方体的两个控制点是:

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

...由于四舍五入引入了一个小错误,但它不太可能引人注意.

  • Flavius提出了"CP2 = CP1 + 1/3*(QP1-QP2)".但从我的数学来看,这似乎给出了不同的结果.(取点`QP0 =(0,0)`,`QP1 =(1,2)`和`QP2 =(3,0)`;我得到`CP2 =(5/3,4/3)`我的公式和弗拉维乌斯的'CP2 =(0,2)`.)我通过将立方系数设置为0并求解其余的来验证我的公式.Flavius,你的配方来自哪里? (2认同)

Bor*_*ein 7

只是为已接受的答案提供证明。

二次贝塞尔曲线表示为:

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

三次贝塞尔曲线表示为:

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

要使这两个多项式相等,它们的所有多项式系数必须相等。多项式系数是通过开发表达式获得的(例如:(1-t)² = 1 - 2t + t²),然后将所有项分解为 1、t、t² 和 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³

因此,我们得到以下 4 个方程:

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

我们可以通过简单地用第二行中的Q 0替换 C 0来求解 C 1,它给出:

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

那么,我们可以继续代入求解 C 2然后 C 3,或者更优雅地注意到变量t' = 1-t变化下原始方程的对称性,并得出结论:

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


Rob*_*art 5

作为参考,我addQuadCurve根据上面 Owen 的答案实现了 NSBezierPath (macOS Swift 4) 。

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)
    }
}
Run Code Online (Sandbox Code Playgroud)