href="http://www.actiprosoftware.com/App_Themes/Standard/Standard.css" text="text/css" rel="stylesheet"/> 最近在看游戏编程的书,看到其中一句:在80年代,如果你能画圆,那你真是聪明极了。
这句话让我跃跃欲试,但是应该怎么画呢?
第一个想法就是按照屏幕水平扫描的方向画直线,然后可以画出一个实心的圆,下面是关键代码部分:
void UseLineDrawRound()
{
int ir, ixo, iyo, ix1, ix2, iy1, iy2, ix;
HDC gdc = GetDC(g_hwnd);
HPEN hpen, old_hpen;
ixo = 50;
iyo = 50;
ir = 50;
iy1 = iyo - ir;
iy2 = iyo + ir;
hpen = CreatePen(PS_DASH, 1, RGB(0,0,255));
old_hpen = (HPEN)SelectObject(gdc, hpen);
for(int y=iy1; y<=iy2; y++)
{
ix = (int)sqrt( pow(ir, 2) - pow(y-iyo, 2) );
ix1 = ixo - ix;
ix2 = ixo + ix;
MoveToEx(gdc, ix1, y, NULL);
LineTo(gdc, ix2, y);
}
DeleteObject(hpen);
SelectObject(gdc, old_hpen);
ReleaseDC(g_hwnd, gdc);
}
好了,现在已经可以画实心的圆了,那如果我要画一个空心的呢?
马上想到用点来画,代码如下:
void UsePixelDrawRound()
{
int ir, ixo, iyo, ix1, ix2, iy1, iy2, ix;
HDC gdc = GetDC(g_hwnd);
ixo = 150;
iyo = 150;
ir = 50;
iy1 = iyo - ir;
iy2 = iyo + ir;
for(int y=iy1; y<=iy2; y++)
{
ix = (int)sqrt( pow(ir, 2) - pow(y-iyo, 2) );
ix1 = ixo - ix;
ix2 = ixo + ix;
SetPixel(gdc, ix1, y, RGB(255,0,0));
SetPixel(gdc, ix2, y, RGB(255,0,0));
}
ReleaseDC(g_hwnd, gdc);
}
如果你运行了上面的代码,你会发现有些点没有画出来,原因是计算出来的x,y都是int类型,小数部分被舍去,也就是精度上的问题。还有什么办法可以改进一下这个算法吗?应该可以。我们可以模仿画多边形,把圆看成是一个正n边形,而当这个n趋于无穷大的时候,这个圆看上去就会很完美了,至少这样可以解决点与点之间不连续的问题。
void UseLineDrawRound2()
{
int ir, ixo, iyo, ix1, ix2, iy1, iy2, ix;
HDC gdc = GetDC(g_hwnd);
HPEN hpen, old_hpen;
static int sx = 0;
static int sy = 0;
ixo = 250;
iyo = 250;
ir = 50;
iy1 = iyo - ir;
iy2 = iyo + ir;
hpen = CreatePen(PS_SOLID, 1, RGB(0,0,255));
old_hpen = (HPEN)SelectObject(gdc, hpen);
for(int y=iy1; y<=iy2; y++)
{
ix = (int)sqrt( pow(ir, 2) - pow(y-iyo, 2) );
ix1 = ixo - ix;
ix2 = ixo + ix;
if(y==iy1) // (sx==0 && sy==0)
{
MoveToEx(gdc, ix1, y, NULL);
}
else
{
LineTo(gdc, sx, sy);
}
sx = ix1;
sy = y;
}
for(y=iy2; y>=iy1; y--)
{
ix = (int)sqrt( pow(ir, 2) - pow(y-iyo, 2) );
ix1 = ixo - ix;
ix2 = ixo + ix;
LineTo(gdc, ix2, y);
}
DeleteObject(hpen);
SelectObject(gdc, old_hpen);
ReleaseDC(g_hwnd, gdc);
}
全部代码如下:
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
HINSTANCE hInstance_app;
HWND g_hwnd;
void UseLineDrawRound()
{
int ir, ixo, iyo, ix1, ix2, iy1, iy2, ix;
HDC gdc = GetDC(g_hwnd);
HPEN hpen, old_hpen;
ixo = 50;
iyo = 50;
ir = 50;
iy1 = iyo - ir;
iy2 = iyo + ir;
hpen = CreatePen(PS_DASH, 1, RGB(0,0,255));
old_hpen = (HPEN)SelectObject(gdc, hpen);
for(int y=iy1; y<=iy2; y++)
{
ix = (int)sqrt( pow(ir, 2) - pow(y-iyo, 2) );
ix1 = ixo - ix;
ix2 = ixo + ix;
MoveToEx(gdc, ix1, y, NULL);
LineTo(gdc, ix2, y);
}
DeleteObject(hpen);
SelectObject(gdc, old_hpen);
ReleaseDC(g_hwnd, gdc);
}
void UsePixelDrawRound()
{
int ir, ixo, iyo, ix1, ix2, iy1, iy2, ix;
HDC gdc = GetDC(g_hwnd);
ixo = 150;
iyo = 150;
ir = 50;
iy1 = iyo - ir;
iy2 = iyo + ir;
for(int y=iy1; y<=iy2; y++)
{
ix = (int)sqrt( pow(ir, 2) - pow(y-iyo, 2) );
ix1 = ixo - ix;
ix2 = ixo + ix;
SetPixel(gdc, ix1, y, RGB(255,0,0));
SetPixel(gdc, ix2, y, RGB(255,0,0));
}
ReleaseDC(g_hwnd, gdc);
}
void UseLineDrawRound2()
{
int ir, ixo, iyo, ix1, ix2, iy1, iy2, ix;
HDC gdc = GetDC(g_hwnd);
HPEN hpen, old_hpen;
static int sx = 0;
static int sy = 0;
ixo = 250;
iyo = 250;
ir = 50;
iy1 = iyo - ir;
iy2 = iyo + ir;
hpen = CreatePen(PS_SOLID, 1, RGB(0,0,255));
old_hpen = (HPEN)SelectObject(gdc, hpen);
for(int y=iy1; y<=iy2; y++)
{
ix = (int)sqrt( pow(ir, 2) - pow(y-iyo, 2) );
ix1 = ixo - ix;
ix2 = ixo + ix;
if(y==iy1)//(sx==0 && sy==0)
{
MoveToEx(gdc, ix1, y, NULL);
}
else
{
LineTo(gdc, sx, sy);
}
sx = ix1;
sy = y;
}
for(y=iy2; y>=iy1; y--)
{
ix = (int)sqrt( pow(ir, 2) - pow(y-iyo, 2) );
ix1 = ixo - ix;
ix2 = ixo + ix;
LineTo(gdc, ix2, y);
}
DeleteObject(hpen);
SelectObject(gdc, old_hpen);
ReleaseDC(g_hwnd, gdc);
}
LRESULT CALLBACK WinProc(HWND hWnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
PAINTSTRUCT ps;
HDC hdc;
switch (msg)
{
case WM_CREATE:
return 0;
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
UseLineDrawRound();
UsePixelDrawRound();
UseLineDrawRound2();
EndPaint(hWnd, &ps);
return 0;
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
break;
default:
break;
}
return DefWindowProc(hWnd, msg, wparam, lparam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd )
{
HWND hwnd;
WNDCLASSEX winclass;
MSG msg;
hInstance_app = hInstance;
winclass.cbClsExtra = 0;
winclass.cbSize = sizeof(WNDCLASSEX);
winclass.cbWndExtra = 0;
winclass.hbrBackground = (HBRUSH)GetStockBrush(BLACK_BRUSH);
winclass.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(CURSOR_CROSSHAIR));
winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
winclass.hIconSm = LoadIcon(hInstance, MAKEINTRESOURCE(ICON_T3DX));
winclass.hInstance = hInstance;
winclass.lpfnWndProc = WinProc;
winclass.lpszClassName = "WINCLASS1";
winclass.lpszMenuName = NULL;
winclass.style = CS_VREDRAW|CS_HREDRAW|CS_OWNDC|CS_DBLCLKS;
if( !RegisterClassEx(&winclass) )
{
return 0;
}
hwnd = CreateWindowEx(WS_EX_TOPMOST,
"WINCLASS1",
"My First Window",
WS_POPUP/*WS_OVERLAPPEDWINDOW*/|WS_VISIBLE,
0,
0,
GetSystemMetrics(SM_CXSCREEN),
GetSystemMetrics(SM_CYSCREEN),
NULL,
NULL,
hInstance,
NULL);
if( hwnd == NULL )
{
return 0;
}
g_hwnd = hwnd;
/*
while( GetMessage(&msg, NULL, 0, 0) )
{
TranslateMessage(&msg);
DispatchMessage(&msg);
if( KEY_DOWN(VK_ESCAPE) )
{
PostMessage(hwnd, WM_DESTROY, 0, 0);
}
}
*/
while(TRUE)
{
if( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) )
{
if(msg.message == WM_QUIT)
{
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
if( KEY_DOWN(VK_ESCAPE) )
{
PostMessage(hwnd, WM_DESTROY, 0, 0);
}
}
}
return msg.wParam;
}
这句话让我跃跃欲试,但是应该怎么画呢?
第一个想法就是按照屏幕水平扫描的方向画直线,然后可以画出一个实心的圆,下面是关键代码部分:
void UseLineDrawRound()
{
int ir, ixo, iyo, ix1, ix2, iy1, iy2, ix;
HDC gdc = GetDC(g_hwnd);
HPEN hpen, old_hpen;
ixo = 50;
iyo = 50;
ir = 50;
iy1 = iyo - ir;
iy2 = iyo + ir;
hpen = CreatePen(PS_DASH, 1, RGB(0,0,255));
old_hpen = (HPEN)SelectObject(gdc, hpen);
for(int y=iy1; y<=iy2; y++)
{
ix = (int)sqrt( pow(ir, 2) - pow(y-iyo, 2) );
ix1 = ixo - ix;
ix2 = ixo + ix;
MoveToEx(gdc, ix1, y, NULL);
LineTo(gdc, ix2, y);
}
DeleteObject(hpen);
SelectObject(gdc, old_hpen);
ReleaseDC(g_hwnd, gdc);
}
好了,现在已经可以画实心的圆了,那如果我要画一个空心的呢?
马上想到用点来画,代码如下:
void UsePixelDrawRound()
{
int ir, ixo, iyo, ix1, ix2, iy1, iy2, ix;
HDC gdc = GetDC(g_hwnd);
ixo = 150;
iyo = 150;
ir = 50;
iy1 = iyo - ir;
iy2 = iyo + ir;
for(int y=iy1; y<=iy2; y++)
{
ix = (int)sqrt( pow(ir, 2) - pow(y-iyo, 2) );
ix1 = ixo - ix;
ix2 = ixo + ix;
SetPixel(gdc, ix1, y, RGB(255,0,0));
SetPixel(gdc, ix2, y, RGB(255,0,0));
}
ReleaseDC(g_hwnd, gdc);
}
如果你运行了上面的代码,你会发现有些点没有画出来,原因是计算出来的x,y都是int类型,小数部分被舍去,也就是精度上的问题。还有什么办法可以改进一下这个算法吗?应该可以。我们可以模仿画多边形,把圆看成是一个正n边形,而当这个n趋于无穷大的时候,这个圆看上去就会很完美了,至少这样可以解决点与点之间不连续的问题。
void UseLineDrawRound2()
{
int ir, ixo, iyo, ix1, ix2, iy1, iy2, ix;
HDC gdc = GetDC(g_hwnd);
HPEN hpen, old_hpen;
static int sx = 0;
static int sy = 0;
ixo = 250;
iyo = 250;
ir = 50;
iy1 = iyo - ir;
iy2 = iyo + ir;
hpen = CreatePen(PS_SOLID, 1, RGB(0,0,255));
old_hpen = (HPEN)SelectObject(gdc, hpen);
for(int y=iy1; y<=iy2; y++)
{
ix = (int)sqrt( pow(ir, 2) - pow(y-iyo, 2) );
ix1 = ixo - ix;
ix2 = ixo + ix;
if(y==iy1) // (sx==0 && sy==0)
{
MoveToEx(gdc, ix1, y, NULL);
}
else
{
LineTo(gdc, sx, sy);
}
sx = ix1;
sy = y;
}
for(y=iy2; y>=iy1; y--)
{
ix = (int)sqrt( pow(ir, 2) - pow(y-iyo, 2) );
ix1 = ixo - ix;
ix2 = ixo + ix;
LineTo(gdc, ix2, y);
}
DeleteObject(hpen);
SelectObject(gdc, old_hpen);
ReleaseDC(g_hwnd, gdc);
}
全部代码如下:
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define KEY_DOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)
HINSTANCE hInstance_app;
HWND g_hwnd;
void UseLineDrawRound()
{
int ir, ixo, iyo, ix1, ix2, iy1, iy2, ix;
HDC gdc = GetDC(g_hwnd);
HPEN hpen, old_hpen;
ixo = 50;
iyo = 50;
ir = 50;
iy1 = iyo - ir;
iy2 = iyo + ir;
hpen = CreatePen(PS_DASH, 1, RGB(0,0,255));
old_hpen = (HPEN)SelectObject(gdc, hpen);
for(int y=iy1; y<=iy2; y++)
{
ix = (int)sqrt( pow(ir, 2) - pow(y-iyo, 2) );
ix1 = ixo - ix;
ix2 = ixo + ix;
MoveToEx(gdc, ix1, y, NULL);
LineTo(gdc, ix2, y);
}
DeleteObject(hpen);
SelectObject(gdc, old_hpen);
ReleaseDC(g_hwnd, gdc);
}
void UsePixelDrawRound()
{
int ir, ixo, iyo, ix1, ix2, iy1, iy2, ix;
HDC gdc = GetDC(g_hwnd);
ixo = 150;
iyo = 150;
ir = 50;
iy1 = iyo - ir;
iy2 = iyo + ir;
for(int y=iy1; y<=iy2; y++)
{
ix = (int)sqrt( pow(ir, 2) - pow(y-iyo, 2) );
ix1 = ixo - ix;
ix2 = ixo + ix;
SetPixel(gdc, ix1, y, RGB(255,0,0));
SetPixel(gdc, ix2, y, RGB(255,0,0));
}
ReleaseDC(g_hwnd, gdc);
}
void UseLineDrawRound2()
{
int ir, ixo, iyo, ix1, ix2, iy1, iy2, ix;
HDC gdc = GetDC(g_hwnd);
HPEN hpen, old_hpen;
static int sx = 0;
static int sy = 0;
ixo = 250;
iyo = 250;
ir = 50;
iy1 = iyo - ir;
iy2 = iyo + ir;
hpen = CreatePen(PS_SOLID, 1, RGB(0,0,255));
old_hpen = (HPEN)SelectObject(gdc, hpen);
for(int y=iy1; y<=iy2; y++)
{
ix = (int)sqrt( pow(ir, 2) - pow(y-iyo, 2) );
ix1 = ixo - ix;
ix2 = ixo + ix;
if(y==iy1)//(sx==0 && sy==0)
{
MoveToEx(gdc, ix1, y, NULL);
}
else
{
LineTo(gdc, sx, sy);
}
sx = ix1;
sy = y;
}
for(y=iy2; y>=iy1; y--)
{
ix = (int)sqrt( pow(ir, 2) - pow(y-iyo, 2) );
ix1 = ixo - ix;
ix2 = ixo + ix;
LineTo(gdc, ix2, y);
}
DeleteObject(hpen);
SelectObject(gdc, old_hpen);
ReleaseDC(g_hwnd, gdc);
}
LRESULT CALLBACK WinProc(HWND hWnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
PAINTSTRUCT ps;
HDC hdc;
switch (msg)
{
case WM_CREATE:
return 0;
break;
case WM_PAINT:
hdc = BeginPaint(hWnd, &ps);
UseLineDrawRound();
UsePixelDrawRound();
UseLineDrawRound2();
EndPaint(hWnd, &ps);
return 0;
break;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
break;
default:
break;
}
return DefWindowProc(hWnd, msg, wparam, lparam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd )
{
HWND hwnd;
WNDCLASSEX winclass;
MSG msg;
hInstance_app = hInstance;
winclass.cbClsExtra = 0;
winclass.cbSize = sizeof(WNDCLASSEX);
winclass.cbWndExtra = 0;
winclass.hbrBackground = (HBRUSH)GetStockBrush(BLACK_BRUSH);
winclass.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(CURSOR_CROSSHAIR));
winclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
winclass.hIconSm = LoadIcon(hInstance, MAKEINTRESOURCE(ICON_T3DX));
winclass.hInstance = hInstance;
winclass.lpfnWndProc = WinProc;
winclass.lpszClassName = "WINCLASS1";
winclass.lpszMenuName = NULL;
winclass.style = CS_VREDRAW|CS_HREDRAW|CS_OWNDC|CS_DBLCLKS;
if( !RegisterClassEx(&winclass) )
{
return 0;
}
hwnd = CreateWindowEx(WS_EX_TOPMOST,
"WINCLASS1",
"My First Window",
WS_POPUP/*WS_OVERLAPPEDWINDOW*/|WS_VISIBLE,
0,
0,
GetSystemMetrics(SM_CXSCREEN),
GetSystemMetrics(SM_CYSCREEN),
NULL,
NULL,
hInstance,
NULL);
if( hwnd == NULL )
{
return 0;
}
g_hwnd = hwnd;
/*
while( GetMessage(&msg, NULL, 0, 0) )
{
TranslateMessage(&msg);
DispatchMessage(&msg);
if( KEY_DOWN(VK_ESCAPE) )
{
PostMessage(hwnd, WM_DESTROY, 0, 0);
}
}
*/
while(TRUE)
{
if( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) )
{
if(msg.message == WM_QUIT)
{
break;
}
TranslateMessage(&msg);
DispatchMessage(&msg);
if( KEY_DOWN(VK_ESCAPE) )
{
PostMessage(hwnd, WM_DESTROY, 0, 0);
}
}
}
return msg.wParam;
}