DDA Bresenham算法(适应于所有斜率)

结果如下:

DDA Bresenham算法(适应于所有斜率)_第1张图片

代码:


*/
// =========================================================================================
// 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);
 }
}

 

 

你可能感兴趣的:(算法,DDA,Bresenham)