![第5章,[Win32 章節] :貝塞爾樣條曲線(一)](http://pic.xiahunao.cn/yaotu/第5章,[Win32 章節] :貝塞爾樣條曲線(一))
專欄導航上一篇第5章[Win32 章節] 邊框繪制函數的教材案例回到目錄下一篇第5章[Win32 章節] 貝塞爾樣條曲線二本節前言對于本節所講解的知識有可能你會需要時不時地參考本專欄的其它文章。真的遇到了需要參考之前的文章的知識點請你自行查閱。我呢也會提到一部分的參考課節。但是呢你不應該依賴于我的主動提及。最好呢你自己能夠多去了解和查看本專欄目錄。關于貝塞爾曲線最初我是在學習 3ds max 的時候接觸到了它。后來呢我在學習 PhotoShop 軟件的時候又一次接觸了它。沒想到后來在學習 Windows API 編程的時候再一次學習了它。在 PhotoShop 里面用鋼筆工具繪制出來的曲線便是貝塞爾樣條曲線。沒有學過 PhotoShop 的話那么你依然可以跟著佩措爾德的教材來學習這個東西。本節內容來自于對佩措爾德教材的大段抄錄。一. 本節內容英文單詞 “spline”曲尺又稱為樣條曲線最初指的是一塊可以彎曲的物體可以是木頭、橡皮或者金屬條它可以用來在紙張上繪制曲線。例如如果有一些離散的點并且想在它們之間畫曲線無論是內插擬合還是外插擬合都應當首先將這些點描繪在圖紙上。接著固定一個曲尺在這些點上使用鉛筆沿著曲尺繞著這些點彎曲的方向繪制曲線。當然現在樣條曲線已經變成了一個個數學公式。有很多種不同的樣條曲線但是貝塞爾樣條曲線是計算機圖形程序設計中用得最廣的。它是后來才加到操作系統的圖形工具庫中并且它出自一個幾乎不可能的來源20 世紀 60 年代法國雷諾汽車公司由手工設計車體使用粘土模型轉變到使用計算機輔助設計。這就需要用到一些數學工具而皮埃爾·貝濟埃Pierre Bezier提出了一套數學公式事實證明這些數學公式起到了很大的作用。從那時起二維的貝塞爾樣條曲線被認為是在計算機圖形學中最有用的曲線之一僅次于直線和橢圓。在 PostScript 中貝塞爾樣條曲線用于所有的曲線甚至橢圓也由貝塞爾樣條曲線來近似。PostScript 字體的字符輪廓的定義也使用貝塞爾樣條曲線定義。TrueType 則使用另外一種相對簡單快速的樣條曲線公式。一條二維貝塞爾樣條曲線使用四個點來定義兩個端點和兩個控點。兩個端點表示曲線的起點和終點??攸c就好像磁鐵一樣把曲線從兩個端點間的直線處吸彎。對此下述程序所示的 Bezier 交互式程序便是一個好的例證。/*--------------------------------------- BEZIER.C -- Bezier Splines Demo (c) Charles Petzold, 1998 水餃編程改編2026 ---------------------------------------*/ #include windows.h LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { static TCHAR szAppName[] TEXT(Bezier); HWND hwnd; MSG msg; WNDCLASS wndclass; wndclass.style CS_HREDRAW | CS_VREDRAW; wndclass.lpfnWndProc WndProc; wndclass.cbClsExtra 0; wndclass.cbWndExtra 0; wndclass.hInstance hInstance; wndclass.hIcon LoadIcon(NULL, IDI_APPLICATION); wndclass.hCursor LoadCursor(NULL, IDC_ARROW); wndclass.hbrBackground (HBRUSH)GetStockObject(WHITE_BRUSH); wndclass.lpszMenuName NULL; wndclass.lpszClassName szAppName; if (!RegisterClass(wndclass)) { MessageBox(NULL, TEXT(注冊窗口類失敗), szAppName, MB_ICONERROR); return 0; } hwnd CreateWindow(szAppName, TEXT(Bezier Splines), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); ShowWindow(hwnd, iCmdShow); UpdateWindow(hwnd); while (GetMessage(msg, NULL, 0, 0)) { TranslateMessage(msg); DispatchMessage(msg); } return msg.wParam; } void DrawBezier(HDC hdc, POINT apt[], BOOL flag_eraser) { HPEN hPen, hOldPen; COLORREF color; hPen hOldPen NULL; PolyBezier(hdc, apt, 4); if (TRUE flag_eraser) { color RGB(255, 0, 0); hPen CreatePen(PS_DASH, 1, color); hOldPen (HPEN)SelectObject(hdc, hPen); } MoveToEx(hdc, apt[0].x, apt[0].y, NULL); LineTo(hdc, apt[1].x, apt[1].y); MoveToEx(hdc, apt[2].x, apt[2].y, NULL); LineTo(hdc, apt[3].x, apt[3].y); if (TRUE flag_eraser) { SelectObject(hdc, hOldPen); DeleteObject(hPen); } } LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { static POINT apt[4]; HDC hdc; int cxClient, cyClient; PAINTSTRUCT ps; switch (message) { case WM_SIZE: cxClient LOWORD(lParam); cyClient HIWORD(lParam); apt[0].x cxClient / 4; apt[0].y cyClient / 2; apt[1].x cxClient / 2; apt[1].y cyClient / 4; apt[2].x cxClient / 2; apt[2].y 3 * cyClient / 4; apt[3].x 3 * cxClient / 4; apt[3].y cyClient / 2; return 0; case WM_LBUTTONDOWN: case WM_RBUTTONDOWN: case WM_MOUSEMOVE: if (wParam MK_LBUTTON || wParam MK_RBUTTON) { hdc GetDC(hwnd); SelectObject(hdc, GetStockObject(WHITE_PEN)); DrawBezier(hdc, apt, FALSE); if (wParam MK_LBUTTON) { apt[1].x LOWORD(lParam); apt[1].y HIWORD(lParam); } if (wParam MK_RBUTTON) { apt[2].x LOWORD(lParam); apt[2].y HIWORD(lParam); } SelectObject(hdc, GetStockObject(BLACK_PEN)); DrawBezier(hdc, apt, TRUE); ReleaseDC(hwnd, hdc); } return 0; case WM_PAINT: InvalidateRect(hwnd, NULL, TRUE); hdc BeginPaint(hwnd, ps); DrawBezier(hdc, apt, TRUE); EndPaint(hwnd, ps); return 0; case WM_DESTROY: PostQuitMessage(0); return 0; } return DefWindowProc(hwnd, message, wParam, lParam); }對于上述代碼塊中的代碼你暫時不需要理解。因為它涉及了以后的章節才會講到的鼠標消息的處理方法。此時你只需要能夠將其編譯出來并且在運行過程中了解這個程序的使用方法和運行效果就可以了。程序編譯和運行這種純軟件操作的問題如果你忘了可以參考下述文章。參考課節使用 VS2010 編寫 C語言程序參考課節用 VS2019 編寫C語言程序本節程序主要的代碼邏輯都是佩措爾德原教材的代碼邏輯。但是呢我也改動了一部分代碼邏輯。程序運行的結果如下圖所示。圖1圖1 所示為默認的運行結果。注意在圖1 中黑色實線的部分才是貝塞爾曲線。紅色虛線的部分它并不是貝塞爾曲線而是輔助線。程序運行起來以后你可以通過鼠標左鍵單擊和左鍵拖動的操作來改變第一個控點的位置從而影響整體的貝塞爾曲線的形狀。你也可以通過鼠標右鍵的單擊和右鍵拖動來改變第二個控點的位置從而影響整體的貝塞爾曲線的形狀。我相信你很快地就能夠理解大致的操作方式。這個程序我當初在學習的時候我覺得它是一個很好玩的程序。在我這里我在運行了程序以后沒有改變第一個控點的位置而是選擇去改變第二個控點的位置。形成的圖形如下所示。圖2除了繪制貝塞爾樣條曲線本身程序還從第一個控點向左邊的第一個端點也就是起點畫直線并從第二個控點向右邊的端點畫直線。由控點向端點繪制的直線屬于是輔助線。我將這個輔助線用虛線來繪制。用虛線來畫線條同樣是涉及了在以后的章節內容中才會講到的知識。默認地在客戶區中繪制線條采用的是實線。我們也可以用虛線來繪制線條。想要用虛線來畫線我們需要用到的是畫筆知識。畫筆知識本章的后面我們就會講到。此刻你有個數就行。程序中有關于畫筆的使用你暫時應該是還看不懂涉及畫筆的代碼。所以再次強調本節程序你暫時不需要閱讀其中的代碼更不需要掌握其中的代碼邏輯。暫時地你只需要能夠將本節程序編譯出來運行它掌握使用方法這就夠了。至于說想要很好地理解本節程序代碼你需要學習完本章后面會講到的畫筆知識也需要學習完以后的章節中會講到的鼠標消息知識你才能看懂本節程序代碼。結束語本節就先到這里了。下一節我們接著講貝塞爾樣條曲線。專欄導航上一篇第5章[Win32 章節] 邊框繪制函數的教材案例回到目錄下一篇第5章[Win32 章節] 貝塞爾樣條曲線二