2013-03-06 18 views
12

Powiedzmy mam Bezier curveB(u), jeśli przyrost u parametr w stałym tempie nie uzyskania prędkości przepływu costant wzdłuż krzywej, ponieważ relacja między u parametru a punktem otrzymana ocena krzywej nie jest liniowa.Beziera Cubic Krzywe: przeniesienie z jednolitego przyspieszenie

Przeczytałem i zaimplementowałem wersję Davida Eberly'ego article. Wyjaśnia, jak poruszać się ze stałą prędkością wzdłuż krzywej parametrycznej.

Załóżmy, że pełnią funkcję F(t) którego wprowadzany wartość czasu t i funkcją prędkości sigma, która zwraca wartość prędkości w czasie t, można uzyskać prędkości przepływu costant wzdłuż krzywej różnych t parametr ze stałą szybkością : B(F(t))

trzon artykule używam jest następująca funkcja:

float umin, umax; // The curve parameter interval [umin,umax]. 
Point Y (float u); // The position Y(u), umin <= u <= umax. 
Point DY (float u); // The derivative dY(u)/du, umin <= u <= umax. 
float LengthDY (float u) { return Length(DY(u)); } 
float ArcLength (float t) { return Integral(umin,u,LengthDY()); } 
float L = ArcLength(umax); // The total length of the curve. 
float tmin, tmax; // The user-specified time interval [tmin,tmax] 
float Sigma (float t); // The user-specified speed at time t. 

float GetU (float t) // tmin <= t <= tmax 
{ 
    float h = (t - tmin)/n; // step size, `n' is application-specified 
    float u = umin; // initial condition 
    t = tmin; // initial condition 
    for (int i = 1; i <= n; i++) 
    { 
    // The divisions here might be a problem if the divisors are 
    // nearly zero. 
    float k1 = h*Sigma(t)/LengthDY(u); 
    float k2 = h*Sigma(t + h/2)/LengthDY(u + k1/2); 
    float k3 = h*Sigma(t + h/2)/LengthDY(u + k2/2); 
    float k4 = h*Sigma(t + h)/LengthDY(u + k3); 
    t += h; 
    u += (k1 + 2*(k2 + k3) + k4)/6; 
    } 
    return u; 
} 

to pozwala mi uzyskać krzywą u obliczoną z użyciem czasu dostarczonego t i funkcja sigma. Teraz funkcja działa poprawnie, gdy sigma prędkości jest kosztowna. Jeśli sigma reprezentuje jednolity accelartion, otrzymuję z niego błędne wartości.

Oto przykład prostej krzywej Beziera, gdzie P0 i P1 są punktami kontrolnymi, T0 T1 styczną. Krzywa zdefiniowana:

[x,y,z]= B(u) =(1–u)3P0 + 3(1–u)2uT0 + 3(1–u)u2T1 + u3P2 

enter image description here

Powiedzmy chcę wiedzieć pozycję wzdłuż krzywej w czasie t = 3. Jeśli I stałą prędkość:

float sigma(float t) 
{ 
    return 1f; 
} 

oraz następujące dane:

V0 = 1; 
V1 = 1; 
t0 = 0; 
L = 10; 

mogę analitycznie obliczenia pozycji:

px = v0 * t = 1 * 3 = 3 

Gdybym rozwiązania tego samego równania, używając mojego Beziera splajn a powyższy algorytm z n =5 otrzymuję:

px = 3.002595; 

Biorąc pod uwagę numeryczne przybliżenie, wartość jest dość dokładna (wykonałem na nim wiele testów. Pomijam szczegóły, ale Beziera, moja implementacja krzywych jest w porządku, a długość samej krzywej obliczana jest dość dokładnie przy użyciu Gaussian Quadrature).

Teraz Jeśli spróbuję zdefiniować sigmę jako jednolitą funkcję przyspieszenia, otrzymam złe wyniki. rozważyć następujące dane:

V0 = 1; 
V1 = 2; 
t0 = 0; 
L = 10; 

można obliczyć czas cząstka osiągnie P1 stosując liniowe równanie ruchu:

L = 0.5 * (V0 + V1) * t1 => 
t1 = 2 * L/(V1 + V0) = 2 * 10/3 = 6.6666666 

Having t można obliczyć przyspieszeniu

a = (V1 - V0)/(t1 - t0) = (2 - 1)/6.6666666 = 0.15 

Mam wszystkie dane do zdefiniowania mojej funkcji sigma:

float sigma (float t) 
{ 
    float speed = V0 + a * t; 
} 

Gdybym analitycznie rozwiązać ten Spodziewam następujące prędkości cząstki po raz t =3:

Vx = V0 + a * t = 1 + 0.15 * 3 = 1.45 

i pozycja będzie:

px = 0.5 * (V0 + Vx) * t = 0.5 * (1 + 1.45) * 3 = 3.675 

Ale gdybym go obliczyć z alorithm powyżej, pozycja wyniki:

px = 4.358587 

, który jest zupełnie inny fr om tego, czego się spodziewam.

Przepraszam za długi post, jeśli ktoś ma dość cierpliwości, by go przeczytać, byłbym zadowolony.

Masz jakieś sugestie? Czego mi brakuje? Ktoś może mi powiedzieć, co robię źle?


EDIT: Próbuję z krzywej 3D Beziera. Zdefiniowane w ten sposób:

public Vector3 Bezier(float t) 
{ 
    float a = 1f - t; 
    float a_2 = a * a; 
    float a_3 = a_2 *a; 

    float t_2 = t * t; 

    Vector3 point = (P0 * a_3) + (3f * a_2 * t * T0) + (3f * a * t_2 * T1) + t_2 * t * P1 ; 

    return point; 
} 

i pochodna:

public Vector3 Derivative(float t) 
{ 
    float a = 1f - t; 
    float a_2 = a * a; 
    float t_2 = t * t; 
    float t6 = 6f*t; 

    Vector3 der = -3f * a_2 * P0 + 3f * a_2 * T0 - t6 * a * T0 - 3f* t_2 * T1 + t6 * a * T1 + 3f * t_2 * P1; 

    return der; 
} 
+0

i co daje algorytm dla t = 6.6666 ...? Czy jest to wartość 10, tj. L, czy inna? – lmsteffan

Odpowiedz

1

Domyślam się, że n=5 po prostu nie daje wystarczającej precyzji dla tego problemu pod ręką. Fakt, że jest on prawidłowy dla przypadku stałej prędkości, nie oznacza, że ​​jest on również w przypadku stałego przyspieszenia. Niestety, musisz sam zdefiniować kompromis, który zapewnia wartość dla n, która pasuje do twoich potrzeb i zasobów.

W każdym razie, jeśli naprawdę mają stałą prędkością parametryzację X (u (t)), który współpracuje z wystarczającą precyzją, a następnie można zmienić nazwę tego parametru „czas” t jest „przestrzeń” (odległość) parametr s, więc to, co naprawdę masz, to: a) wystarczy podłączyć s (t), którego potrzebujesz: X (s (t)). W twoim przypadku (stałe przyspieszenie), s (t) = s + u t + t /2, gdzie u i a można łatwo określić na podstawie danych wejściowych.

1

Myślę, że po prostu masz literówkę gdzieś w funkcjach niewykazanych w twojej implementacji, Y i DY. Próbowałem krzywej jednowymiarowej z P0 = 0, T0 = 1, T1 = 9, P1 = 10 i otrzymałem 3,6963165 z n = 5, która poprawiła się do 3,675044 dla n = 30 i 3,6750002 dla n = 100.

Jeśli twoja implementacja jest dwuwymiarowa, spróbuj z P0 = (0, 0), T0 = (1, 0), T1 = (9, 0) i P1 = (10, 0). Następnie spróbuj ponownie za pomocą P0 = (0, 0), T0 = (0, 1), T1 = (0, 9) i P1 = (0, 10).

Jeśli używasz C, pamiętaj, że operator^NIE oznacza wykładnika. Musisz użyć pow (u, 3) lub u * u * u, aby uzyskać kostkę u.

Spróbuj wydrukować wartości tak dużej ilości, jak to możliwe w każdej iteracji. Oto co mam:

i=1 
    h=0.6 
    t=0.0 
    u=0.0 
    LengthDY(u)=3.0 
    sigma(t)=1.0 
    k1=0.2 
    sigma(t+h/2)=1.045 
    LengthDY(u+k1/2)=6.78 
    k2=0.09247787 
    LengthDY(u+k2/2)=4.8522377 
    k3=0.12921873 
    sigma(t+h)=1.09 
    LengthDY(u+k3)=7.7258916 
    k4=0.08465043 
    t_new=0.6 
    u_new=0.12134061 
i=2 
    h=0.6 
    t=0.6 
    u=0.12134061 
    LengthDY(u)=7.4779167 
    sigma(t)=1.09 
    k1=0.08745752 
    sigma(t+h/2)=1.135 
    LengthDY(u+k1/2)=8.788503 
    k2=0.0774876 
    LengthDY(u+k2/2)=8.64721 
    k3=0.078753725 
    sigma(t+h)=1.1800001 
    LengthDY(u+k3)=9.722377 
    k4=0.07282171 
    t_new=1.2 
    u_new=0.20013426 
i=3 
    h=0.6 
    t=1.2 
    u=0.20013426 
    LengthDY(u)=9.723383 
    sigma(t)=1.1800001 
    k1=0.072814174 
    sigma(t+h/2)=1.225 
    LengthDY(u+k1/2)=10.584761 
    k2=0.069439456 
    LengthDY(u+k2/2)=10.547299 
    k3=0.069686085 
    sigma(t+h)=1.27 
    LengthDY(u+k3)=11.274727 
    k4=0.06758479 
    t_new=1.8000001 
    u_new=0.26990926 
i=4 
    h=0.6 
    t=1.8000001 
    u=0.26990926 
    LengthDY(u)=11.276448 
    sigma(t)=1.27 
    k1=0.06757447 
    sigma(t+h/2)=1.315 
    LengthDY(u+k1/2)=11.881528 
    k2=0.06640561 
    LengthDY(u+k2/2)=11.871877 
    k3=0.066459596 
    sigma(t+h)=1.36 
    LengthDY(u+k3)=12.375444 
    k4=0.06593703 
    t_new=2.4 
    u_new=0.3364496 
i=5 
    h=0.6 
    t=2.4 
    u=0.3364496 
    LengthDY(u)=12.376553 
    sigma(t)=1.36 
    k1=0.06593113 
    sigma(t+h/2)=1.405 
    LengthDY(u+k1/2)=12.7838 
    k2=0.06594283 
    LengthDY(u+k2/2)=12.783864 
    k3=0.0659425 
    sigma(t+h)=1.45 
    LengthDY(u+k3)=13.0998535 
    k4=0.06641296 
    t_new=3.0 
    u_new=0.4024687 

Mam debugowany wiele programów takich jak ten po prostu drukowanie mnóstwo zmiennych, a następnie obliczając każdą wartość ręcznie i upewniając się, że są takie same.

+0

dzięki za dane. Spróbuję zrobić więcej eksperymentów. Właściwie używam krzywych 3D Beziera. Będę edytować post z kodem. – Heisenbug