我面臨同樣的問題:每個動畫軟件包似乎都使用Bézier曲線來控制隨時間變化的值,但是沒有關於如何實現Bézier曲線作爲y(x)函數的信息。所以這就是我想出的。
在二維空間中的標準三次貝塞爾曲線可以通過四個點P =被定義(X ,Y ).. P =(X ,Y )。
P 和P 是曲線的結束點,而P 1 和P 是影響其形狀的手柄。使用參數tε[0,1],然後可以使用等式
A)確定沿曲線的任何給定的點的x和y座標X =(1-T) X + 3噸(1-T) X + 3T (1-叔)× +噸 X和
B)Y =(1-t)的 y + 3T(1-T)ý + 3T (1-叔)Y +噸ý。
我們想要的是一個函數y(x),給定一個x座標,將返回曲線的相應y座標。爲此,曲線必須從左向右單調移動,以便它不會在不同的y位置上多次使用同一個x座標。確保這一點的最簡單的辦法是限制該輸入點,使得X < X和X ,X ε[X ,X ]。換句話說,P 必須在它們之間的兩個手柄的左邊。
爲了計算給定x的y,我們必須首先從x確定t。從t獲得y是將t應用於等式B的簡單問題。
我看到了確定給定y的t的兩種方法。
首先,你可以嘗試一個二進制搜索t。從0的下限開始,1的上限開始,並通過方程A計算這些t的值。保持等分間隔,直到獲得相當近似的近似值。雖然這應該可以正常工作,但它既不會特別快也不會非常精確(至少不會同時出現)。
第二種方法是實際解出t的方程式A.由於方程是立方體,因此實施起來有點困難。另一方面,計算變得非常快並且產生精確的結果。
公式A可以改寫爲
(-x 0 + 3× -3x + X )噸 +(3× -6x + 3×2 )噸 +(-3x + 3×)T +(X 0 -x)= 0。
插入您的實際值對於x ..x ,我們得到以下形式的三次方程一個噸 + B噸 + C * T +爲d = 0我們知道[0,1]內只有一個解決方案。現在我們可以使用類似於this Stack Overflow answer中發佈的算法來求解這個公式。
以下是展示這種方法的一些C#類。它應該很簡單,可以將其轉換爲您選擇的語言。
using System;
public class Point {
public Point(double x, double y) {
X = x;
Y = y;
}
public double X { get; private set; }
public double Y { get; private set; }
}
public class BezierCurve {
public BezierCurve(Point p0, Point p1, Point p2, Point p3) {
P0 = p0;
P1 = p1;
P2 = p2;
P3 = p3;
}
public Point P0 { get; private set; }
public Point P1 { get; private set; }
public Point P2 { get; private set; }
public Point P3 { get; private set; }
public double? GetY(double x) {
// Determine t
double t;
if (x == P0.X) {
// Handle corner cases explicitly to prevent rounding errors
t = 0;
} else if (x == P3.X) {
t = 1;
} else {
// Calculate t
double a = -P0.X + 3 * P1.X - 3 * P2.X + P3.X;
double b = 3 * P0.X - 6 * P1.X + 3 * P2.X;
double c = -3 * P0.X + 3 * P1.X;
double d = P0.X - x;
double? tTemp = SolveCubic(a, b, c, d);
if (tTemp == null) return null;
t = tTemp.Value;
}
// Calculate y from t
return Cubed(1 - t) * P0.Y
+ 3 * t * Squared(1 - t) * P1.Y
+ 3 * Squared(t) * (1 - t) * P2.Y
+ Cubed(t) * P3.Y;
}
// Solves the equation ax³+bx²+cx+d = 0 for x ϵ ℝ
// and returns the first result in [0, 1] or null.
private static double? SolveCubic(double a, double b, double c, double d) {
if (a == 0) return SolveQuadratic(b, c, d);
if (d == 0) return 0;
b /= a;
c /= a;
d /= a;
double q = (3.0 * c - Squared(b))/9.0;
double r = (-27.0 * d + b * (9.0 * c - 2.0 * Squared(b)))/54.0;
double disc = Cubed(q) + Squared(r);
double term1 = b/3.0;
if (disc > 0) {
double s = r + Math.Sqrt(disc);
s = (s < 0) ? -CubicRoot(-s) : CubicRoot(s);
double t = r - Math.Sqrt(disc);
t = (t < 0) ? -CubicRoot(-t) : CubicRoot(t);
double result = -term1 + s + t;
if (result >= 0 && result <= 1) return result;
} else if (disc == 0) {
double r13 = (r < 0) ? -CubicRoot(-r) : CubicRoot(r);
double result = -term1 + 2.0 * r13;
if (result >= 0 && result <= 1) return result;
result = -(r13 + term1);
if (result >= 0 && result <= 1) return result;
} else {
q = -q;
double dum1 = q * q * q;
dum1 = Math.Acos(r/Math.Sqrt(dum1));
double r13 = 2.0 * Math.Sqrt(q);
double result = -term1 + r13 * Math.Cos(dum1/3.0);
if (result >= 0 && result <= 1) return result;
result = -term1 + r13 * Math.Cos((dum1 + 2.0 * Math.PI)/3.0);
if (result >= 0 && result <= 1) return result;
result = -term1 + r13 * Math.Cos((dum1 + 4.0 * Math.PI)/3.0);
if (result >= 0 && result <= 1) return result;
}
return null;
}
// Solves the equation ax² + bx + c = 0 for x ϵ ℝ
// and returns the first result in [0, 1] or null.
private static double? SolveQuadratic(double a, double b, double c) {
double result = (-b + Math.Sqrt(Squared(b) - 4 * a * c))/(2 * a);
if (result >= 0 && result <= 1) return result;
result = (-b - Math.Sqrt(Squared(b) - 4 * a * c))/(2 * a);
if (result >= 0 && result <= 1) return result;
return null;
}
private static double Squared(double f) { return f * f; }
private static double Cubed(double f) { return f * f * f; }
private static double CubicRoot(double f) { return Math.Pow(f, 1.0/3.0); }
}
這是否有幫助:http://www.gamedev.net/topic/313018-calculating-the-length-of-a-bezier-curve/? – 2011-05-04 15:49:50
我看過這篇文章和很多其他文章,每個人似乎都有自己的解決方案。我認爲這個問題應該有一個標準的解決方案,因爲這是一個常見的問題。 – Raks 2011-05-05 05:37:01
我在CG課程中使用過的書中查了幾個章節,有很多關於樣條的信息,但沒有提到一種標準的方法來確定曲線的長度並不幸福。 – 2011-05-05 10:21:35