나는 같은 문제에 직면했다. 모든 애니메이션 패키지는 베 지어 곡선을 사용하여 시간에 따라 값을 제어하는 것으로 보이지만, 베 지어 곡선을 y (x) 함수로 구현하는 방법에 대한 정보는 없다. 그래서 여기에 내가 생각해 낸 것이 있습니다.
차원 공간에서의 표준 큐빅 베 지어 곡선은 네 포인트 P 0 = 의해 정의 될 수있다 (X 0, Y 0) ... P 3 = (X 3, Y). 2 P 1과 P는 그 형상에 영향을주는 핸들 동안
P 0 및 P 3은 곡선의 종점이다. 파라미터 t의 ε를 사용하여, 0, 1]의 곡선을 따라 특정 지점에 대한 X 및 Y 좌표는 다음 방정식
A)를 사용하여 결정될 수있다 X = (1-t) 3 X 0 + 3t (1-t) 2 X 1 + 3t 2 2 + t 3 X 3 및
B를 × (1-t)) Y = (1-t) y + 3t (1-t) 2 Y 1 + 3t 2 (1-t), Y 2 + t3 Y 3.
원하는 것은 x 좌표에서 주어진 곡선의 y 좌표를 반환하는 함수 y (x)입니다. 이렇게하려면 커브가 왼쪽에서 오른쪽으로 단조롭게 이동해야하므로 다른 y 위치에서 동일한 x 좌표를 두 번 이상 차지하지 않습니다. 이것을 보장하는 가장 쉬운 방법은 X 0 < X 3 및 X 1가 2 ε을 X되도록 [X 0, 3을 X] 상기 입력 포인트를 제한하는 것이다. 즉, P 은 두 개의 핸들을 사이에두고 P 의 왼쪽에 있어야합니다.
주어진 x에 대해 y를 계산하려면 먼저 x에서 t를 결정해야합니다. 따라서 t를 y로부터 구하는 것은 방정식 B에 t를 적용하는 간단한 문제입니다.
주어진 y에 대해 t를 결정하는 두 가지 방법이 있습니다.
먼저, t에 대한 2 진 검색을 시도 할 수 있습니다. 0의 하한과 1의 상한으로 시작하여 방정식 A를 통해 t에 대해이 값에 대해 x를 계산합니다. 합리적으로 가까운 근사값을 얻을 때까지 간격을 양분하십시오. 이 작업은 정상적으로 수행되어야하지만 특히 빠르지도 매우 정확하지도 않습니다.
두 번째 접근법은 실제로 t에 대한 방정식 A를 풀기위한 것입니다. 방정식이 3 차이므로 구현하기가 약간 까다 롭습니다. 반면에 계산은 정말 빨라지고 정확한 결과를 산출합니다.
식 A는
(-x 0 3X + 1 2 -3x X + 3) t 3 + (3X -6x 0 1로 쓸 수있다 3X + 2) t 2 + (0 -3x 3X + 1)의 t + (X 0 -x) = 0.
X 0 ..x 3 대한 실제 값을 삽입 우리 t 3 + B톤 2 + 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/? –
나는 이것과 많은 다른 기사를 보았고 모두가 그들 만의 해결책을 가지고있는 것처럼 보인다. 나는이 문제가 일반적인 문제이기 때문에이 문제가 표준 해결책을 가져야한다고 생각했다. – Raks
나는 CG 과정, 스플라인에 대한 많은 정보에 사용 된 책에서 몇 장을 확인했지만, 곡선의 길이를 잘 모르는 표준 방법에 대해서는 언급하지 않았다. –