講解:Bezier曲線原理及實(shí)現(xiàn)代碼(c++)
一、原理:
???????貝塞爾曲線于1962年,由法國工程師皮埃爾·貝塞爾(Pierre Bézier)所廣泛發(fā)表,他運(yùn)用貝塞爾曲線來為汽車的主體進(jìn)行設(shè)計。貝塞爾曲線最初由?Paul de Casteljau?于1959年運(yùn)用?de Casteljau 算法開發(fā),以穩(wěn)定數(shù)值的方法求出貝塞爾曲線。
線性貝塞爾曲線
給定點(diǎn)?P0、P1,線性貝塞爾曲線只是一條兩點(diǎn)之間的直線。這條線由下式給出:
且其等同于線性插值。
二次方貝塞爾曲線的路徑由給定點(diǎn)?P0、P1、P2?的函數(shù)?B(t) 追蹤:
。
TrueType?字型就運(yùn)用了以貝塞爾樣條組成的二次貝塞爾曲線。
P0、P1、P2、P3?四個點(diǎn)在平面或在三維空間中定義了三次方貝塞爾曲線。曲線起始于?P0?走向?P1,并從?P2?的方向來到?P3。一般不會經(jīng)過?P1?或?P2;這兩個點(diǎn)只是在那里提供方向資訊。?P0?和?P1?之間的間距,決定了曲線在轉(zhuǎn)而趨進(jìn)P3?之前,走向?P2?方向的“長度有多長”。
曲線的參數(shù)形式為:
。
現(xiàn)代的成象系統(tǒng),如?PostScript、Asymptote?和?Metafont,運(yùn)用了以貝塞爾樣條組成的三次貝塞爾曲線,用來描繪曲線輪廓。
一般化
P0、P1、…、Pn,其貝塞爾曲線即
。
例如?:
。
如上公式可如下遞歸表達(dá): 用??表示由點(diǎn)?P0、P1、…、Pn?所決定的貝塞爾曲線。則
用平常話來說,?階貝塞爾曲線之間的插值。
一些關(guān)于參數(shù)曲線的術(shù)語,有
即多項(xiàng)式
又稱作?n?階的伯恩斯坦基底多項(xiàng)式,定義 00?= 1。
點(diǎn)?Pi?稱作貝塞爾曲線的控制點(diǎn)。多邊形以帶有線的貝塞爾點(diǎn)連接而成,起始于?P0?并以?Pn?終止,稱作貝塞爾多邊形(或控制多邊形)。貝塞爾多邊形的凸包(convex hull)包含有貝塞爾曲線。
線性貝塞爾曲線函數(shù)中的?t?會經(jīng)過由?P0?至P1?的?B(t) 所描述的曲線。例如當(dāng)?t=0.25?時,B(t)
即一條由點(diǎn)?P0?至?P1?路徑的四分之一處。就像由 0 至 1 的連續(xù)?t,B(t) 描述一條由?P0?至?P1?的直線。
為建構(gòu)二次貝塞爾曲線,可以中介點(diǎn)?Q0?和?Q1?作為由 0 至 1 的?t:
由?P0?至?P1?的連續(xù)點(diǎn)?Q0,描述一條線性貝塞爾曲線。由?P1?至?P2?的連續(xù)點(diǎn)?Q1,描述一條線性貝塞爾曲線。由?Q0?至?Q1?的連續(xù)點(diǎn)?B(t),描述一條二次貝塞爾曲線。?
為建構(gòu)高階曲線,便需要相應(yīng)更多的中介點(diǎn)。對于三次曲線,可由線性貝塞爾曲線描述的中介點(diǎn)?Q0、Q1、Q2,和由二次曲線描述的點(diǎn)?R0、R1?所建構(gòu):
對于四次曲線,可由線性貝塞爾曲線描述的中介點(diǎn)?Q0、Q1、Q2、Q3,由二次貝塞爾曲線描述的點(diǎn)?R0、R1、R2,和由三次貝塞爾曲線描述的點(diǎn)?S0、S1?所建構(gòu):
P(t)=(1-t)P0+tP1?,?。
矩陣表示為:
,?。
P(t)=(1-t)2P0+2t(1-t)P1+t2P2,?。
矩陣表示為:
,?。
P(t)=(1-t)3P0+3t(1-t)2P1+3t2(1-t)P2+t3P3?
矩陣表示為:
,?。
(6-3-2)?
,?。?
在(6-3-2)式中,Mn+1是一個n+1階矩陣,稱為n次Bezier矩陣。
?(6-3-3)
。
其中,
利用(6-3-3)式,我們可以得到任意次Bezier矩陣的顯式表示,例如4次和5次Bezier矩陣為:
,
?
可以證明,n次Bezier矩陣還可以表示為遞推的形式:
?(6-3-4)
?
二、算法(c++)
工程目錄是:Win32App?
vc6.0
#include
??HDC hdc;
??static POINT pt[NUM];
??TEXTMETRIC tm;
??static int cxClient,cyClient;
??HPEN hpen;
??int i,j,k,n,t;
??switch(message)
??{
??case WM_CREATE:
??????static int cxchar;
??????hdc = GetDC(hwnd);
??????GetTextMetrics(hdc,&tm);
??????cxchar = tm.tmAveCharWidth;
??????ReleaseDC(hwnd,hdc);
??case WM_SIZE:
???????cxClient = LOWORD(lparam);
??????cyClient = HIWORD(lparam);
??????return 0;
??case WM_PAINT:
???????hdc = GetDC(hwnd);
???????srand(time(0));
???????Rectangle(hdc,0,0,cxClient,cyClient);
??????for(i=0; i<500; i++)
??????????{
????????????SelectObject(hdc,GetStockObject(WHITE_PEN));
????????????PolyBezier(hdc,pt,NUM);
????????????for(j=0; j<NUM; j++)
????????????{
????????????????pt[j].x = rand()%cxClient;
????????????????pt[j].y = rand()%cyClient;
????????????}
????????????hpen = CreatePen(PS_INSIDEFRAME,3,RGB(rand()%256,rand()%256,rand()%256));
?????????????DeleteObject(SelectObject(hdc,hpen));
????????????PolyBezier(hdc,pt,NUM);
????????????for(k=0; k<50000000;k++);
??????????}
??????for(i=0; i<100;i++)
??????{
????????Ellipse(hdc,rand()%cxClient,rand()%cyClient,rand()%cxClient,rand()%cyClient);
????????Pie(hdc,j=rand()%cxClient,k=rand()%cyClient,n=rand()%cxClient,t=rand()%cyClient,rand()%cxClient,rand()%cyClient,rand()%cxClient,rand()%cyClient) ;?
??????}
???????if((n=(n+j)/2)>cxchar*20) n=cxchar*20;??
????????SetTextColor(hdc,RGB(rand()%256,rand()%256,rand()%256));
????????TextOut(hdc,n/2,(t+k)/2,TEXT("瑾以此向Pierm Bezier致敬!"),lstrlen(TEXT("瑾以此向Pierm Bezier致敬!")));
????????ReleaseDC(hwnd,hdc);
??????????DeleteObject(hpen);
??????????ValidateRect(hwnd,NULL);
???return 0;
??case WM_DESTROY:
??????PostQuitMessage(0);
??????return 0;
??}
??return DefWindowProc(hwnd,message,wparam,lparam);
}