结果如下:
代码:
*/
// =========================================================================================
// main.cpp
/*简述:这个程序实现了用DDA和Bresenham两个算法进行直线的生成,同时用两种方法进行圆的生成,
*不过这两种方法是类似的
*/
// =========================================================================================
// includes
// =========================================================================================
#define WIN32_LEAN_AND_MEAN
#include <windows.h> // this provides access to the windows GDI functions
#include <stdlib.h>
#include <math.h>
// =========================================================================================
// Constants
// =========================================================================================
const int kWindowLeft = 50, // The location of the drawing window
kWindowTop = 50,
kWindowRight = 960,
kWindowBottom = 535;
const int kWindowHeight = kWindowBottom - kWindowTop,
kWindowWidth = kWindowRight - kWindowLeft;
// =========================================================================================
// Global Variables
// =========================================================================================
bool gDone; // set to true when the program is finished
HINSTANCE thisAppInstance; // a handle for this application instance
HWND gMainWindowHandle = NULL; // handle to main window
HDC gWindowDC; // handle to main window drawing context (all drawing operations use this)
int gTestCase;
POINT gStartPt, gEndPt;
// =========================================================================================
// Function Prototypes
// =========================================================================================
int EventLoop(void);
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
void HandleUpdate ();
void HandleKeyPress(WPARAM theChar);
void PlotPixel(int x, int y);
void myDDALine(int x1, int y1, int x2, int y2);
void myBresLine(int x1, int y1, int x2, int y2);
int Slope(int x1, int y1, int x2, int y2);//用于计算斜率(一共分为了6种情况)
void testLine(int index, POINT start, POINT end);
void DrawContent();
//画圆方法一的两个函数
/*void CirclePoints(int cx,int cy,int radius)
void DrawCircle(int cx, int cy, int radius)*/
//画圆方法二的两个函数
void CirclePoints(int cx,int cy,int x,int y);//cx,cy为坐标在原点时画圆的圆心,x,y是偏移,那么现在圆心就是(cx+x,cy+y)
void DrawCircle(int cx, int cy, int radius);
// =========================================================================================
// main (for Windows)
// -----------------------------------------------------------------------------------------
// This is the windows equivalent of the "main" function in a standard C or C++ program.
// The main task for this function is to create a window for us to draw in, and then
// call the event loop. The event loop will get a "paint" message from the system
// which we use as a signal to call our DrawContent() function to draw something on the
// display.
// =========================================================================================
// =========================================================================================
// main (for Windows)
// -----------------------------------------------------------------------------------------
// This is the windows equivalent of the "main" function in a standard C or C++ program.
// The main task for this function is to create a window for us to draw in, and then
// call the event loop. The event loop will get a "paint" message from the system
// which we use as a signal to call our DrawContent() function to draw something on the
// display.
// =========================================================================================
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
thisAppInstance = hInstance; // snag this - we need it
WNDCLASSEX win_class; // Window class
// Fill in the Window class fields
win_class.cbSize = sizeof(WNDCLASSEX);
win_class.style = CS_DBLCLKS | CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
win_class.lpfnWndProc = (WNDPROC)WndProc;
win_class.cbClsExtra = 0;
win_class.cbWndExtra = 0;
win_class.hInstance = hInstance;
win_class.hIcon = LoadIcon(NULL, IDI_APPLICATION);
win_class.hCursor = LoadCursor(NULL, IDC_ARROW);
win_class.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
win_class.lpszMenuName = NULL;
win_class.lpszClassName = TEXT("MAIN");
win_class.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
// Register the Window class
if (!RegisterClassEx(&win_class))
{
MessageBox(NULL, TEXT("Window registration failed."), TEXT("ERROR"),
MB_ICONEXCLAMATION | MB_OK);
return(0);
}
// Create the main window
gMainWindowHandle = CreateWindowEx(0, // extended style
TEXT("MAIN"), // class name
TEXT("图形学实验二"),
WS_OVERLAPPED | WS_SYSMENU, // style
kWindowLeft,
kWindowTop,
kWindowWidth,
kWindowHeight,
NULL, // handle to parent
NULL, // handle to menu
hInstance, // app instance
NULL); // extra creation params
// Check for error
if (NULL == gMainWindowHandle)
{
MessageBox(NULL, TEXT("Window creation failed."), TEXT("ERROR"),
MB_ICONEXCLAMATION | MB_OK);
return(0);
}
// Display the main window
ShowWindow(gMainWindowHandle, nCmdShow);
UpdateWindow(gMainWindowHandle);
// call the application event loop - when this terminates, the program terminates
return EventLoop();
}
// =========================================================================================
// EventLoop
// -----------------------------------------------------------------------------------------
// This is the main event loop. The program just sits here, waiting for an "event" to
// happen. An event could be a mouse click, key press, or window repaint event, or other
// event type
// =========================================================================================
int EventLoop (void)
{
MSG msg; // Windows message
// event loop - stay in this loop (processing events) until the user quits the program
// by closing the window
while (!gDone)
{
// check message queue
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
// if it is a Windows quit message, stop
if (WM_QUIT == msg.message)
break;
// translate and dispatch message
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// Do application-specific stuff here
}
return(msg.wParam);
}
// =========================================================================================
// WndProc
// -----------------------------------------------------------------------------------------
// This is the Windows event callback. We previously registered this as the callback
// when we created the window class. When an event has been generated, it gets translated
// and then dispatched to the appropriate handler (ours).
// =========================================================================================
LRESULT CALLBACK WndProc(HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
// Process the message
switch (msg)
{
// Check for window closing
case WM_CLOSE:
case WM_DESTROY:
{
// Post a quit message on the queue
PostQuitMessage(0);
break;
}
// Check for a keypress - any key press means quit
case WM_CHAR:
{
gDone = true;
break;
}
// check for a left-button mousedown - this would signal the start
// Check for window update
case WM_PAINT:
{
HandleUpdate();
break;
}
default:
{
// Handle any unprocessed message
return DefWindowProc(hwnd, msg, wParam, lParam);
}
}
return(0);
}
// =========================================================================================
// HandleUpdate
// -----------------------------------------------------------------------------------------
// This function gets called whenever some part of our window needs to be updated.
// =========================================================================================
void HandleUpdate ()
{
PAINTSTRUCT ps;
gWindowDC = BeginPaint(gMainWindowHandle, &ps);
DrawContent(); // Draw the picture
EndPaint(gMainWindowHandle, &ps);
}
// =========================================================================================
// PlotPixel
// -----------------------------------------------------------------------------------------
// This function plots a single pixel on the display at the specified location.
// =========================================================================================
void PlotPixel(int x, int y)
{
SetPixelV(gWindowDC, x, y, RGB(0, 0, 0));
}
// =========================================================================================
// SetPt
// -----------------------------------------------------------------------------------------
// This function initialises a variable of type POINT
// =========================================================================================
void SetPt(POINT *thePoint, int px, int py)
{
thePoint->x = px;
thePoint->y = py;
}
// -----------------------------------------------------------------------------------------
// TestLine
// -----------------------------------------------------------------------------------------
// This function calls the line drawing function to draw a line between the two points
// which it highlights with red (start) and blue (end) dots.
// -----------------------------------------------------------------------------------------
void testLine(int index, POINT start, POINT end)
{
RECT r;
// calculate the offsets so that each line case is drawn in a different part of the window
int xoffset = ((index % 6) * 150) + 4;
int yoffset = ((index / 6) * 150) + 4;
// draw a small red square at the "start" of the line
SetRect (&r, start.x+xoffset-2, start.y+yoffset-2, start.x+xoffset+3, start.y+yoffset+3);
HBRUSH theBrush = CreateSolidBrush(RGB(255, 0, 0));
HBRUSH oldBrush = (HBRUSH) SelectObject(gWindowDC, theBrush);
FillRect(gWindowDC, &r, theBrush);
// draw a small cyan square at the "end" of the line
SetRect (&r, end.x+xoffset-2, end.y+yoffset-2, end.x+xoffset+3, end.y+yoffset+3);
theBrush = CreateSolidBrush(RGB(0, 255, 0));
FillRect(gWindowDC, &r, theBrush);
// draw a black frame around the test area
SetRect (&r, xoffset, yoffset, xoffset+145, yoffset+145);
theBrush = CreateSolidBrush(RGB(0, 0, 0));
FrameRect(gWindowDC, &r, theBrush);
// 调用画线函数----这两种方法都已经验证都过
//myDDALine(start.x+xoffset, start.y+yoffset, end.x+xoffset, end.y+yoffset);
myBresLine (start.x+xoffset, start.y+yoffset, end.x+xoffset, end.y+yoffset);
// delete the windows brush we created
SelectObject(gWindowDC, oldBrush);
DeleteObject(theBrush);
}
// =========================================================================================
// DrawContent
// -----------------------------------------------------------------------------------------
// For this tutorial, this function implements a comprehensive test strategy,
// generating lines of all slopes to test the line drawing function.
// -----------------------------------------------------------------------------------------
void DrawContent()
{
int i = 0;
// case 0 - left-to-right horizontal line
// --------------------------------------------------------------------
SetPt(&gStartPt, 10, 75);
SetPt(&gEndPt, 135, 75);
testLine(i++, gStartPt, gEndPt);
// case 1 - right-to-left horizontal line
// --------------------------------------------------------------------
testLine(i++, gEndPt, gStartPt);
// case 2 - top to bottom vertical line
// --------------------------------------------------------------------
SetPt(&gStartPt, 75, 10);
SetPt(&gEndPt, 75, 135);
testLine(i++, gStartPt, gEndPt);
// case 3 - bottom to top vertical line
// --------------------------------------------------------------------
testLine(i++, gEndPt, gStartPt);
// case 4 - slope between 0 and 1, start to lower left of end
// --------------------------------------------------------------------
SetPt(&gStartPt, 10, 100);
SetPt(&gEndPt, 135, 50);
testLine(i++, gStartPt, gEndPt);
// case 5- slope between 0 and 1, start to upper right of end
// --------------------------------------------------------------------
testLine(i++, gEndPt, gStartPt);
// case 6 - slope between 0 and -1, start to upper left of end
// --------------------------------------------------------------------
SetPt(&gStartPt, 10, 50);
SetPt(&gEndPt, 135, 100);
testLine(i++, gStartPt, gEndPt);
// case 7 - slope between 0 and -1, start to lower right of end
// --------------------------------------------------------------------
testLine(i++, gEndPt, gStartPt);
// case 8 - slope between 1 and infinity, start to lower left of end
// --------------------------------------------------------------------
SetPt(&gStartPt, 50, 135);
SetPt(&gEndPt, 100, 10);
testLine(i++, gStartPt, gEndPt);
// case 9 - slope between 1 and infinity, start to upper right of end
// --------------------------------------------------------------------
testLine(i++, gEndPt, gStartPt);
// case 10 - slope between -1 and infinity, start to upper left of end
// --------------------------------------------------------------------
SetPt(&gStartPt, 50, 10);
SetPt(&gEndPt, 100, 135);
testLine(i++, gStartPt, gEndPt);
// case 11 - slope between -1 and infinity, start to lower right of end
// --------------------------------------------------------------------
testLine(i++, gEndPt, gStartPt);
// case 12 - slope of 1, start to lower left of end
// --------------------------------------------------------------------
SetPt(&gStartPt, 10, 135);
SetPt(&gEndPt, 135, 10);
testLine(i++, gStartPt, gEndPt);
// case 13 - slope of 1, start to upper right of end
// --------------------------------------------------------------------
testLine(i++, gEndPt, gStartPt);
// case 14 - slope of -1, start to upper left of end
// --------------------------------------------------------------------
SetPt(&gStartPt, 10, 10);
SetPt(&gEndPt, 135, 135);
testLine(i++, gStartPt, gEndPt);
// case 15 - slope of -1, start to lower right of end
// --------------------------------------------------------------------
testLine(i++, gEndPt, gStartPt);
// case 16 - a single point
// --------------------------------------------------------------------
SetPt(&gStartPt, 75, 75);
SetPt(&gEndPt, 75, 75);
testLine(i++, gStartPt, gEndPt);
//case 17 - draw circle
DrawCircle(kWindowWidth-75,kWindowHeight-100, 50);
}
// -----------------------------------------------------------------------------------------
// myDDALine
// -----------------------------------------------------------------------------------------
// Draw a line from a to b using the DDA Algorithm
//
// This function is a rudimentary version of DDA that only works for some line cases.
// You have to modify it to work with all line cases.
// -----------------------------------------------------------------------------------------
void myDDALine (int x1, int y1, int x2, int y2)
{
int i;
float dx, dy, length,x,y;
if (abs(x2-x1)>=abs(y2-y1))
{
length=abs(x2-x1);
}
else
{
length=abs(y2-y1);
}
dx = (x2-x1)/length;
dy=(y2-y1)/length;
i=1;x= x1;y= y1;
while(i<=length)
{
SetPixel (gWindowDC,(x+0.5), int(y+0.5),RGB(0,0,0));
x=x+dx;
y=y+dy;
i++;
}
}
// -----------------------------------------------------------------------------------------
// myBresLine
// -----------------------------------------------------------------------------------------
// Draw a line from a to b using Bresenham's algorithm
// -----------------------------------------------------------------------------------------
void myBresLine (int x1, int y1, int x2, int y2)//起点(x1,y1),终点(x2,y2)
{
int xa = x1; // make copies of the end-points that you can modify
int ya = y1;
int xb = x2;
int yb = y2;
//计算斜率
int slope=Slope(xa,ya,xb,yb);
int dx = xb - xa, dy = ya - yb;//这里的dy是对于斜率大于0的时候的
int e = 2 * dy - dx;//就是书上的e
int ey = 2 * dx - dy;
int twoDy = 2 * dy, twoDyDx = 2 * (dy - dx);//这里的twoDy,twoDyDx表示斜率绝对值<=1
int twoDx =2 * dx, twoDxDy = 2 * (dx - dy);
int x, y, xEnd;
x = xa;
y = ya;
// plot the first pixel
PlotPixel(x, y);
switch(slope)
{
//=============================重合=======
case 0: //因为是重合所以只要画一个点就好了
break;
//=============================垂直=======
case 1:
if(y<yb)
{
while(y<=yb)
{
PlotPixel(xa,y);
y++;
}
}
else
{
while(y>=yb)
{
PlotPixel(xa,y);
y--;
}
}
break;
//=============================[0,1]=======
case 2:
if(x1<x2)
{
while (x < x2)
{
x++;
if (e < 0)
e += twoDy;
else //e>=0
{
y--;//其实书上是y++,这里是y--因为和书上用到的坐标系是不一样的
e += twoDyDx;
}
PlotPixel(x, y);
}
}
else
{
while (x>x2)
{
x--;
if (e >= 0)// 注意:因为书上是在dx>0的情况下得到e的递推关系式,但是这里因为xa>xb,所以得到的递推关系式e的符号要相反,所以是当e>=0时: e += twoDy;
e += twoDy;
else //e>=0
{
y++;
e += twoDyDx;
}
PlotPixel(x, y);
}
}
break;
//=============================(1,..)=======
case 3:
if(y1>y2)
{
while (y > y2)
{
y--;
if (ey < 0)
ey += twoDx;
else //ey>=0
{
x++;
ey += twoDxDy;
}
PlotPixel(x, y);
}
}
else
{
while (y < y2)
{
y++;
if (ey >= 0)// 注意:因为书上是在dx>0的情况下得到e的递推关系式,但是这里因为xa>xb,所以得到的递推关系式e的符号要相反,所以是当e>=0时: e += twoDy;
ey += twoDx;
else //e>=0
{
x--;
ey += twoDxDy;
}
PlotPixel(x, y);
}
}
break;
//=============================[-1,0]=======
case 4:
dx = xb - xa;
dy = yb- ya;//对于斜率小于0的线段,dy=yb-xa
e = 2 * dy - dx;//就是书上的e
twoDy = 2 * dy, twoDyDx = 2 * (dy - dx);
if(x1<x2)
{
while (x < x2)
{
x++;
if (e < 0)
e += twoDy;
else //e>=0
{
y++;//其实书上是y++,这里是y--因为和书上用到的坐标系是不一样的
e += twoDyDx;
}
PlotPixel(x, y);
}
}
else
{
while (x>x2)
{
x--;
if (e >= 0)// 注意:因为书上是在dx>0的情况下得到e的递推关系式,但是这里因为xa>xb,所以得到的递推关系式e的符号要相反,所以是当e>=0时: e += twoDy;
e += twoDy;
else //e>=0
{
y--;
e += twoDyDx;
}
PlotPixel(x, y);
}
}
break;
//=============================(..,-1)=======
case 5:
dx = xb - xa, dy = yb - ya;
ey = 2 * dx - dy;
twoDx =2 * dx, twoDxDy = 2 * (dx - dy);
if(y1<y2)
{
while (y < y2)
{
y++;
if (ey < 0)
ey += twoDx;
else //ey>=0
{
x++;
ey += twoDxDy;
}
PlotPixel(x, y);
}
}
else
{
while (y > y2)
{
y--;
if (ey >= 0)// 注意:因为书上是在dx>0的情况下得到e的递推关系式,但是这里因为xa>xb,所以得到的递推关系式e的符号要相反,所以是当e>=0时: e += twoDy;
ey += twoDx;
else //e>=0
{
x--;
ey += twoDxDy;
}
PlotPixel(x, y);
}
}
break;
default:
break;
}
}
//斜率(注意:默认为x1<x2,所以传参数是要注意)
int Slope(int x1, int y1, int x2, int y2)
{
int x=x2-x1;
int y=y1-y2;//注意坐标,假设start(10,10),end(12,12),根据原来计算方法k=1,但是这里画点的坐标系是左上角为(0,0),所以实际应该是k=-1
//重合
if(x1==x2&&y1==y2)
{
return 0;
}
else if(x1==x2)
{
return 1;
}
else
{
double k=(double)y/(double)x;
//[0--1]
if(k>=0&&k<=1)
{
return 2;
}
//(1-无穷)
else if(k>1)
{
return 3;
}
//[-1,0]
else if(k>=-1&&k<=0)
{
return 4;
}
//(无穷-1)
else if(k<-1)
{
return 5;
}
}
}
//画圆方法一
/*void CirclePoints(int cx,int cy,int radius)
{
int x=0,y=radius,e=1-radius;
PlotPixel(x, y);
PlotPixel(x, -y);
PlotPixel(-x, y);
PlotPixel(-x, -y);
PlotPixel(y ,x );
PlotPixel(y ,-x );
PlotPixel(-y ,x );
PlotPixel(-y ,-x );
// PlotPixel(x, -y);
while(x<=y)
{
if(e<0)
e+=2*x+3;
else
{
e+=2*(x-y)+5;
y--;
}
x++;
PlotPixel(x+cx, y+cy);PlotPixel(y+cy,x+cx);PlotPixel(x+cx, -(y+cy));PlotPixel(-(y+cy),x+cx);
PlotPixel(-(x+cx),(y+cy));PlotPixel((y+cy),-(x+cx));PlotPixel(-(x+cx), -(y+cy));PlotPixel(-(y+cy),-(x+cx));
}
}*/
/*void DrawCircle(int cx, int cy, int radius)
{
// fill in the code here to complete the tutorial work
int x1,y1,r;
x1=cx,y1=cy;r=radius;
CirclePoints(x1,y1,r);
CirclePoints(x1,-y1,r);
CirclePoints(y1,x1,r);
CirclePoints(-y1,x1,r);
CirclePoints(-x1,-y1,r);
CirclePoints(-y1,-x1,r);
CirclePoints(-x1,y1,r);
CirclePoints(y1,-x1,r);
}*/
//画圆方法二
void CirclePoints(int x,int y,int cx,int cy)//x^2+y^2=R2^2cx,cy为坐标在原点时画圆的圆心
{
SetPixel(gWindowDC,x+cx,y+cy,RGB(0,0,0));
SetPixel(gWindowDC,x+cx,-y+cy,RGB(0,0,0));
SetPixel(gWindowDC,-x+cx,y+cy,RGB(0,0,0));
SetPixel(gWindowDC,-x+cx,-y+cy,RGB(0,0,0));
SetPixel(gWindowDC,y+cx,x+cy,RGB(0,0,0));
SetPixel(gWindowDC,y+cx,-x+cy,RGB(0,0,0));
SetPixel(gWindowDC,-y+cx,x+cy,RGB(0,0,0));
SetPixel(gWindowDC,-y+cx,-x+cy,RGB(0,0,0));
}
void DrawCircle(int cx, int cy, int radius)//以cx,cy为圆心画圆,这里的cx,cy实际上就是偏移量
{
int x=0,y=radius,e=1-radius;
CirclePoints(x,y,cx,cy);
while(x<=y)//画了1/8个圆
{
if(e<0)
e+=2*x+3;
else
{
e+=2*(x-y)+5;
y--;
}
x++;
CirclePoints(x,y,cx,cy);
}
}