【发布时间】:2011-03-10 21:51:37
【问题描述】:
将二次贝塞尔曲线(3 点)转换为三次贝塞尔曲线(4 点)的算法是什么?
【问题讨论】:
标签: c++ c algorithm graphics vector
将二次贝塞尔曲线(3 点)转换为三次贝塞尔曲线(4 点)的算法是什么?
【问题讨论】:
标签: c++ c algorithm graphics vector
来自https://fontforge.org/docs/techref/bezier.html#converting-truetype-to-postscript:
任何二次样条都可以表示为三次(三次项为零)。三次方的端点将与二次方的端点相同。
CP0 = QP0
CP3 = QP2
三次方的两个控制点是:
CP1 = QP0 + 2/3 *(QP1-QP0)
CP2 = QP2 + 2/3 *(QP1-QP2)
...由于四舍五入而引入了轻微错误,但不太可能引起注意。
【讨论】:
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,你的配方是从哪里来的?
作为参考,我基于 Owen's answer above 为 NSBezierPath (macOS Swift 4) 实现了 addQuadCurve。
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)
}
}
【讨论】:
只是为接受的答案提供证明。
二次贝塞尔曲线表示为:
Q(t) = Q0 (1-t)² + 2 Q1 (1-t) t + Q2 t²
三次贝塞尔曲线表示为:
C(t) = C0 (1-t)³ + 3 C1 (1-t)² t + 3 C2 sub> (1-t) t² + C3 t³
要使这两个多项式相等,它们的所有多项式系数必须相等。多项式系数是通过开发表达式获得的(例如:(1-t)² = 1 - 2t + t²),然后将所有项分解为 1、t、t² 和 t³:
Q(t) = Q0 + (-2Q0 + 2Q1) t + (Q0 sub> - 2Q1 + Q2) t²
C(t) = C0 + (-3C0 + 3C1) t + (3C0 sub> - 6C1 + 3C2) t² + (-C0 + 3C1 -3C2 + C3) t³
因此,我们得到以下 4 个等式:
C0 = Q0
-3C0 + 3C1 = -2Q0 + 2Q1
3C0 - 6C1 + 3C2 = Q0 - 2Q1子> + Q2
-C0 + 3C1 -3C2 + C3 = 0
我们可以通过在第二行简单地将 C0 替换为 Q0 来求解 C1,得到:
C1 = Q0 + (2/3) (Q1 - Q0)
那么,我们可以继续代入求解C2然后C3,或者更优雅地注意变量变化下原方程的对称性t' = 1-t,得出结论:
C0 = Q0
C1 = Q0 + (2/3) (Q1 - Q0)
C2 = Q2 + (2/3) (Q1 - Q2)
C3 = Q2
【讨论】: