一個圖形學萌新的學習記錄。ios
學習了直線生成算法以後,繼續來學習繪製圓弧的方法,若是要生成一個整圓,能夠利用座標系的八對稱性,在其中一個象限繪製以後再在其餘象限的對稱點繪製便可。算法
咱們首先考慮圓心在原點,半徑爲r的圓,計算出像素以後只需加上一個偏移量便可繪製圓心在任意一點的圓。ide
要畫圓最暴力的方法固然是利用圓的參數方程來計算:函數
但這樣計算使用了三角函數和浮點運算,效率低下,因此一般咱們使用中點畫圓法來進行圓的繪製。學習
中點畫圓法利用的也是相似於Bresenham直線算法的思想,利用判別式選擇像素,只需作簡單的整數運算。ui
咱們八象限中第二象限的1/8圓爲例,若肯定了一個像素點爲($x_{p},y_{p}$),那麼下一個點要麼是右方的P1,要麼是右下方的P2。spa
構造函數一個函數F(x,y)=$x^{2}$+$y^{2}$-$R^{2}$,當F大於0時,點在圓外,反之則在圓內。3d
圖中的M是P1和P2的中點,因此M=($x_{p}$+1,$y_{p}-0.5$),當F(M)<0時,M在圓內,說明P1離圓弧更近,反之M在圓外,P2更近。code
根據以上原理,可構造判別式:blog
當$d_{p}$<0時,取P1爲下一像素,下一像素判別式爲:
當$d_{p}$>0時,取P2爲下一像素,下一像素判別式爲:
咱們按順時針方式生成八分圓,因此第一個像素爲(0,R),初始判別式爲:
1.25-R能夠簡化成1-R,去除浮點數運算,由於運算過程當中增量都爲整數,因此減去0.25是不會影響符號的。
代碼實現以下:
void CirclePoints(HDC hdc, int x, int y,int offx,int offy)//利用對稱性畫整圓 { SetPixel(hdc, x + offx, y + offy, RGB(0, 0, 0)); SetPixel(hdc, y + offx, x + offy, RGB(0, 0, 0)); SetPixel(hdc, x + offx, -y + offy, RGB(0, 0, 0)); SetPixel(hdc, -y + offx, x + offy, RGB(0, 0, 0)); SetPixel(hdc, -x + offx, y + offy, RGB(0, 0, 0)); SetPixel(hdc, y + offx, -x + offy, RGB(0, 0, 0)); SetPixel(hdc, -x + offx, -y + offy, RGB(0, 0, 0)); SetPixel(hdc, -y + offx, -x + offy, RGB(0, 0, 0)); } void MidPointCircle(HDC hdc, int x1, int y1, int r)//中點畫圓 { int x, y, e; x = 0; y = r; e = 1 - r; CirclePoints(hdc, x, y, x1, y1); while (x <= y) { if (e < 0) e += 2 * x + 3; else { e += 2 * (x - y) + 5; y--; } x++; CirclePoints(hdc, x, y, x1, y1); } }
完整可運行Windows代碼:
#include<Windows.h> #include<iostream> #include<cmath> using namespace std; const int ScreenWidth = 500; const int ScreenHeight = 500; LRESULT CALLBACK WinProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_CLOSE: DestroyWindow(hWnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, message, wParam, lParam); break; } return 0; } void CirclePoints(HDC hdc, int x, int y,int offx,int offy)//利用對稱性畫整圓 { SetPixel(hdc, x + offx, y + offy, RGB(0, 0, 0)); SetPixel(hdc, y + offx, x + offy, RGB(0, 0, 0)); SetPixel(hdc, x + offx, -y + offy, RGB(0, 0, 0)); SetPixel(hdc, -y + offx, x + offy, RGB(0, 0, 0)); SetPixel(hdc, -x + offx, y + offy, RGB(0, 0, 0)); SetPixel(hdc, y + offx, -x + offy, RGB(0, 0, 0)); SetPixel(hdc, -x + offx, -y + offy, RGB(0, 0, 0)); SetPixel(hdc, -y + offx, -x + offy, RGB(0, 0, 0)); } void MidPointCircle(HDC hdc, int x1, int y1, int r) { int x, y, e; x = 0; y = r; e = 1 - r; CirclePoints(hdc, x, y, x1, y1); while (x <= y) { if (e < 0) e += 2 * x + 3; else { e += 2 * (x - y) + 5; y--; } x++; CirclePoints(hdc, x, y, x1, y1); } } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int nShowCmd) { WNDCLASS wcs; wcs.cbClsExtra = 0; wcs.cbWndExtra = 0; wcs.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wcs.hCursor = LoadCursor(hInstance, IDC_CROSS); wcs.hIcon = LoadIcon(NULL, IDI_WINLOGO); wcs.hInstance = hInstance; wcs.lpfnWndProc = (WNDPROC)WinProc; wcs.lpszClassName = "CG"; wcs.lpszMenuName = NULL; wcs.style = CS_VREDRAW | CS_HREDRAW; RegisterClass(&wcs); HWND hWnd; hWnd = CreateWindow("CG", "DrawCircle", WS_OVERLAPPEDWINDOW, 200, 200, ScreenWidth, ScreenHeight, NULL, NULL, hInstance, NULL); ShowWindow(hWnd, nShowCmd); UpdateWindow(hWnd); MSG msg; // hdc init HDC hdc = GetDC(hWnd); MidPointCircle(hdc, 200, 200, 150); // 消息循環 while (GetMessage(&msg, 0, NULL, NULL)) { TranslateMessage(&msg); DispatchMessage(&msg); } // release ReleaseDC(hWnd, hdc); return 0; }
運行結果:
接下來是區域填充算法,加油~