Windows游戏编程大师技巧之Cohen-Sutherland裁剪直线算法

来源:互联网 发布:淘宝怎么解id锁 编辑:程序博客网 时间:2024/05/16 17:38

一。算法介绍

一般情况下,当我们需要裁剪一条直线的时候,要分为全部可见,部分可见,全部不可见这几种情况,所以这样就需要有很多种的情况需要进行处理。目前,已经发明了很多的算法来处理这些不同的情况,其中应用最广范的就是Cohen-Sutherland算法。

这个算法,是一种简单的匹配算法,通过匹配一些情况,来减少if的条件判断,确定两个端点的位置,从而进行匹配,最后运算,得出结果。

二。算法原理

Cohen-Sutherland算法,将一个矩形裁剪区,分为了9个不同的部分,并且给每个部分一个特定的代码,以此来标识不同的区域。如下图所示的,9个部分:

从上图中,就可以看出如何进行分配的了。中间的裁剪区就是我们能够实际绘制的部分,而周围的部分就是不可见的部分,如果端点在外面我们,就需要通过计算来进行。上图还给出了Windows游戏编程大师技巧中关于标识代码的定义,不要小看这些定义哦,这些代码并不是随便定义的哦^-^。
下面我们就来分析下,为什么要这样定义代码:
首先是将东南西北四个部分分别定义了,他们的值分别是:

NORTH:0x0008 ; WEST:0x0001 ; EAST:0x0002 ; SOUTH:0x0004

细心的童鞋可能发现了,这四个值,都是2的指数关系,为什么了?呵呵,计算机中按位表示的值,都是以2来进的,也就是2进制,所以看下他们的二进制码:

NORTH:1000(8) ; WEST:0001(1) ; EAST:0010(2) ; SOUTH: 0100(4) ;

哈哈,看出来了吧,他们的定义都是只有一个位是1,为什么这样做了?因为只有一个位上是1,我们就可以通过按位或(|)操作来进行组合代码了,是不是这样的了?熟悉Windows编程的都知道,WIN32 API中,有很多的类似这样的方法,微软提供很多参数,也是能够进行按位或(|)运算的。

所以,我们很自然的得出以下的四个部分的代码:

NORTHEAST:0x000A  == NORTH|EAST == (1000)|(0010)==(1010) == 10
NORTHWEST:0x0009 == NORTH|WEST == (1000)|(0001)==(1001) ==9
SOUTHEAST:0x0006 == SOUTH|EAST == (0100)|(0010) ==(0110)==6
SOURTHWEST:0x0005 == SOUTH|WEST == (0100)|(0001) ==(0101)== 5

好了,这里的代码定义就解释道这里了,如果对上面不懂的同学,你可能需要复习下计算机的位操作部分了!!!^_^

接下来,我们就应该讨论,一共可能出现的情况:
  • 全部可见
  • 全部不可见
  • 部分可见
上面的情况,其实只是最广泛的分法,真真的情况远比这三种要复杂,但是大体上就这三种情况。

由于我们是使用代码标识的方法,来进行的,所以我不打算按照这三种情况来讲解,而是按照顶点在不同的代码区域来进行讲解,我们单独的考虑一个端点的位置情况:

端点在裁剪区域中:
很容易理解吧。判断这个端点是否在裁剪区域内,只要判断x,y坐标是否在裁剪区域内就可以了。

端点在NORTH:

端点在NORTH的方向上,大概就上面这几种直线,他们有一个共同的特征,那就是裁剪之后(不管是全部裁剪,还是部分裁剪,我们先考虑一段裁剪的情况),他们的上面端点的y坐标就是裁剪区域的最上面的坐标,但是x坐标却有不同的情况,如下图表示:

上面打红色的点,表示的就是此段被裁剪之后的端点,他们的情况很容易辨别,当y=0,之后,获取的x的值,有三种情况,小于0,在裁剪区中,大于裁剪区的宽度。而通过判断现在计算的x是小于0或者大于裁剪区的宽度,就可判断,这条直线,实际上是在裁剪区外的(对着上面的图,想想看是不是这样的???),只有在裁剪区域中的点,才是部分可见的端点。

同样的道理,可以得到在WEST,SOUTH,EAST中的情况也是这样的。所以这里就不再赘述。

下面在来讨论一种情况:
端点在NORTHEAST:

从上面的图中,可以看出,若可以裁剪,有两种不同的情况了,不像上图中的只有一种情况,这里有可能x=裁剪区宽度,y待定;有可能y=0,x待定,所以我们可以先判断一种情况,不如先假设值y=0,然后计算出x(初中的数学,应该会计算吧),在来判断下x是否在裁剪区域内,如果不在在进行x=裁剪区宽度的计算。计算之后,可以判断是否在裁剪区域内。很容易吧!!!^_^。

好了,其他的组合情况,也是这样的,所以也不再赘述了。

三。代码实现

先看看整个算法的实现:
int Clipper_Line(int& x0,int& y0,int& x1, int& y1,int screen_width,int screen_height){#define CLIP_CODE_C 0x0000#define CLIP_CODE_N 0x0008#define CLIP_CODE_S 0x0004#define CLIP_CODE_E 0x0002#define CLIP_CODE_W 0x0001#define CLIP_CODE_NE 0x000a#define CLIP_CODE_SE 0x0006#define CLIP_CODE_NW 0x0009#define CLIP_CODE_SW 0x0005int xc0 = x0 ,yc0 = y0 , xc1=x1 , yc1=y1 ;int min_clip_x = SCREEN_WIDTH/3 ,min_clip_y = SCREEN_HEIGHT/3 ,max_clip_x = screen_width*2/3-1,max_clip_y=screen_height*2/3-1 ;int p0_code = 0 ,p1_code = 0 ;//确定各个顶点所在的位置代码if(y0<min_clip_y)p0_code|=CLIP_CODE_N;else if(y0>max_clip_y)p0_code|=CLIP_CODE_S;if(x0<min_clip_x)p0_code|=CLIP_CODE_W;else if(x0>max_clip_x)p0_code|=CLIP_CODE_E;if(y1<min_clip_y)p1_code|=CLIP_CODE_N;else if(y1>max_clip_y)p1_code|=CLIP_CODE_S;if(x1<min_clip_x)p1_code|=CLIP_CODE_W;else if(x1>max_clip_x)p1_code|=CLIP_CODE_E;//先检测一些简单的情况if(p0_code&p1_code) //有相同的位置代码,表示在裁剪区外部return 0 ;if(p0_code==0&&p1_code==0) //表示两个点都在裁剪区内,不需要裁剪return 1 ;//判断第一个点的位置代码switch(p0_code){case CLIP_CODE_C:break;case CLIP_CODE_N:{yc0 = min_clip_y ;xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0);break ;}case CLIP_CODE_S:{yc0 = max_clip_y;xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0);break ;}case CLIP_CODE_W:{xc0=min_clip_x;yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0);break;}case CLIP_CODE_E:{xc0=max_clip_x;yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0);break;}case CLIP_CODE_NE:{yc0 = min_clip_y;xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0);if(xc0<min_clip_x||xc0>max_clip_x){xc0=max_clip_x;yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0);}break;}case CLIP_CODE_SE:{yc0 = max_clip_y;xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0);if(xc0<min_clip_x||xc0>max_clip_x){xc0=max_clip_x;yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0);}break;}case CLIP_CODE_NW:{yc0=min_clip_y;xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0);if(xc0<min_clip_x||xc0>max_clip_x){xc0=min_clip_x;yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0);}break;}case CLIP_CODE_SW:{yc0=max_clip_y;xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0);if(xc0<min_clip_x||xc0>max_clip_x){xc0=min_clip_x;yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0);}break;}default:break;} // end switch(p0_code)//判断第二个点的位置代码switch(p1_code){case CLIP_CODE_C:break;case CLIP_CODE_N:{yc1 = min_clip_y ;xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0);break ;}case CLIP_CODE_S:{yc1 = max_clip_y;xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0);break ;}case CLIP_CODE_W:{xc1=min_clip_x;yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0);break;}case CLIP_CODE_E:{xc1=max_clip_x;yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0);break;}case CLIP_CODE_NE:{yc1 = min_clip_y;xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0);if(xc1<min_clip_x||xc1>max_clip_x){xc1=max_clip_x;yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0);}break;}case CLIP_CODE_SE:{yc1 = max_clip_y;xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0);if(xc1<min_clip_x||xc1>max_clip_x){xc1=max_clip_x;yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0);}break;}case CLIP_CODE_NW:{yc1=min_clip_y;xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0);if(xc1<min_clip_x||xc1>max_clip_x){xc1=min_clip_x;yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0);}break;}case CLIP_CODE_SW:{yc1=max_clip_y;xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0);if(xc1<min_clip_x||xc1>max_clip_x){xc1=min_clip_x;yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0);}break;}default:break;} // end switch(p1_code)//进行最后的检测if(xc0>max_clip_x||xc0<min_clip_x||yc0>max_clip_y||yc0<min_clip_y||xc1>max_clip_x||xc1<min_clip_x||yc1>max_clip_y||yc1<min_clip_y){//表示全部在裁剪区外部return 0 ;}//将裁减后的数据返回x0 = xc0 ;x1 = xc1 ;y0 = yc0 ;y1 = yc1 ;return 1 ;}// end Clipper_Line



完整的DirectX实现:
// DEMO8_2.CPP 此Demo演示32位窗口模式下,创建裁剪直线的算法// INCLUDES ///////////////////////////////////////////////#define WIN32_LEAN_AND_MEAN  // just say no to MFC#define INITGUID // make sure directX guids are included#include <windows.h>   // include important windows stuff#include <windowsx.h> #include <mmsystem.h>#include <iostream> // include important C/C++ stuffusing namespace std ;#include <conio.h>#include <stdlib.h>#include <malloc.h>#include <memory.h>#include <string.h>#include <stdarg.h>#include <stdio.h> #include <math.h>#include <io.h>#include <fcntl.h>#include <ddraw.h> // include directdraw#pragma comment(lib,"ddraw.lib")// DEFINES ////////////////////////////////////////////////// defines for windows #define WINDOW_CLASS_NAME L"WINCLASS1"// default screen size#define SCREEN_WIDTH    640  // size of screen#define SCREEN_HEIGHT   480#define SCREEN_BPP      32   // bits per pixel#define MAX_COLORS      256  // maximum colors// TYPES //////////////////////////////////////////////////////// basic unsigned typestypedef unsigned short USHORT;typedef unsigned short WORD;typedef unsigned char  UCHAR;typedef unsigned char  BYTE;// MACROS /////////////////////////////////////////////////#define KEYDOWN(vk_code) ((GetAsyncKeyState(vk_code) & 0x8000) ? 1 : 0)#define KEYUP(vk_code)   ((GetAsyncKeyState(vk_code) & 0x8000) ? 0 : 1)// initializes a direct draw struct#define DD_INIT_STRUCT(ddstruct) { memset(&ddstruct,0,sizeof(ddstruct)); ddstruct.dwSize=sizeof(ddstruct); }//initializes a RGB value#define _RGB16BIT565(r,g,b) ((b & 31) + ((g & 63) << 5) + ((r & 31) << 11))#define _RGB32BIT(a,r,g,b) ((b) + ((g) << 8) + ((r) << 16) + ((a) << 24))// GLOBALS ////////////////////////////////////////////////HWND      main_window_handle = NULL; // globally track main windowHINSTANCE hinstance_app      = NULL; // globally track hinstance// directdraw stuffLPDIRECTDRAW7         lpdd         = NULL;   // dd objectLPDIRECTDRAWSURFACE7  lpddsprimary = NULL;   // dd primary surfaceLPDIRECTDRAWSURFACE7  lpddsback    = NULL;   // dd back surfaceLPDIRECTDRAWPALETTE   lpddpal      = NULL;   // a pointer to the created dd paletteLPDIRECTDRAWCLIPPER   lpddclipper  = NULL;   // dd clipperPALETTEENTRY          palette[256];          // color palettePALETTEENTRY          save_palette[256];     // used to save palettesDDSURFACEDESC2        ddsd;                  // a direct draw surface description structDDBLTFX               ddbltfx;               // used to fillDDSCAPS2              ddscaps;               // a direct draw surface capabilities structHRESULT               ddrval;                // result back from dd callsDWORD                 start_clock_count = 0; // used for timingLPDIRECTDRAWSURFACE7  lpddsOffScreen = NULL ;  //离屏表面int  window_close  =  0 ;    //标识窗口是否关闭// these defined the general clipping rectangleint min_clip_x = 0,                          // clipping rectangle     max_clip_x = SCREEN_WIDTH-1,    min_clip_y = 0,    max_clip_y = SCREEN_HEIGHT-1;// these are overwritten globally by DD_Init()int screen_width  = SCREEN_WIDTH,            // width of screen    screen_height = SCREEN_HEIGHT,           // height of screen    screen_bpp    = SCREEN_BPP;              // bits per pixelchar buffer[80];                     // general printing buffer//申明画线方法int Draw_Line(int x0, int y0, int x1, int y1, DWORD color , UINT * video_buffer , int stepx , int stepy);//裁剪直线算法int Clipper_Line(int& x0,int& y0,int& x1, int& y1,int screen_width,int screen_height);//交换值void Swap(int &x , int &y) ;// FUNCTIONS //////////////////////////////////////////////LRESULT CALLBACK WindowProc(HWND hwnd,     UINT msg,                             WPARAM wparam,                             LPARAM lparam){// this is the main message handler of the systemPAINTSTRUCTps;// used in WM_PAINTHDChdc;// handle to a device contextchar buffer[80];        // used to print strings// what is the message switch(msg){case WM_CREATE:         {// do initialization stuff here        // return successreturn(0);} break;   case WM_PAINT: {// simply validate the window        hdc = BeginPaint(hwnd,&ps);                 // end painting        EndPaint(hwnd,&ps);        // return successreturn(0);   } break;case WM_DESTROY: {// kill the application, this sends a WM_QUIT message PostQuitMessage(0);        // return successreturn(0);} break;default:break;    } // end switch// process any messages that we didn't take care of return (DefWindowProc(hwnd, msg, wparam, lparam));} // end WinProc/////////////////////////////////////////////////////////////程序主循环int Game_Main(void *parms = NULL, int num_parms = 0){// this is the main loop of the game, do all your processing// here// for now test if user is hitting ESC and send WM_CLOSEif(window_close)return 1 ;if (KEYDOWN(VK_ESCAPE)){PostMessage(main_window_handle,WM_CLOSE,0,0);window_close = 1 ;}   //清空表面DDBLTFX bltfx ;DD_INIT_STRUCT(bltfx);bltfx.dwFillColor = 0 ;if(FAILED(lpddsOffScreen->Blt(NULL,NULL,NULL,DDBLT_WAIT|DDBLT_COLORFILL,&bltfx))){OutputDebugString(L"OffScreen Blt error");return 1 ;}//锁定DDSURFACEDESC2 ddsd ;DD_INIT_STRUCT(ddsd);if(FAILED(lpddsOffScreen->Lock(NULL,&ddsd,DDLOCK_WAIT|DDLOCK_SURFACEMEMORYPTR,NULL))){OutputDebugString(L"Lock error");return 1 ;}//获取窗口位置RECT rect ;GetWindowRect(main_window_handle,&rect);//画线int x0 = rand()%SCREEN_WIDTH;int x1 = rand()&SCREEN_WIDTH;int y0 = rand()%SCREEN_HEIGHT;int y1 = rand()%SCREEN_HEIGHT;int clipx0 = x0 , clipx1=x1 , clipy0=y0 ,clipy1=y1 ;if(Clipper_Line(x0,y0,x1,y1,SCREEN_WIDTH,SCREEN_HEIGHT)){//返回1表示在裁剪区内,并且裁剪成功Draw_Line(rect.left+x0,rect.top+y0,rect.left+x1,rect.top+y1,(DWORD)_RGB32BIT(0,255,255,0),(UINT*)ddsd.lpSurface,1,ddsd.lPitch>>2);}//解锁if(FAILED(lpddsOffScreen->Unlock(NULL))){OutputDebugString(L"Unlock error");return 1 ;}//Blt到主表面if(FAILED(lpddsprimary->Blt(NULL,lpddsOffScreen,NULL,DDBLT_WAIT,NULL))){OutputDebugString(L"Blt error");return 1 ;}// return success or failure or your own return code herereturn(1);} // end Game_Main////////////////////////////////////////////////////////////int Game_Init(void *parms = NULL, int num_parms = 0){// this is called once after the initial window is created and// before the main event loop is entered, do all your initialization// here// create IDirectDraw interface 7.0 object and test for errorif (FAILED(DirectDrawCreateEx(NULL, (void **)&lpdd, IID_IDirectDraw7, NULL)))   return(0);// set cooperation to normal since this will be a windowed appif(FAILED(lpdd->SetCooperativeLevel(main_window_handle, DDSCL_NORMAL))){MessageBox(NULL,L"SetCooperativeLevel error",L"error",MB_OK);return 0 ;}//创建裁剪器if(FAILED(lpdd->CreateClipper(0,&lpddclipper,NULL))){OutputDebugString(L"CreateClipper error");return 1 ;}//将裁减器关联窗口,也就是用窗口的尺寸作为裁剪器的裁剪序列if(FAILED(lpddclipper->SetHWnd(0,main_window_handle))){OutputDebugString(L"SetHWnd error");return 1 ;}//创建主表面memset(&ddsd,0,sizeof(ddsd));ddsd.dwSize = sizeof(ddsd);ddsd.dwFlags = DDSD_CAPS ;ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;if(FAILED(lpdd->CreateSurface(&ddsd,&lpddsprimary,NULL))){MessageBox(NULL,L"CreateSurface error",L"error",MB_OK);return 0 ;}//将裁减器关联到表面if(FAILED(lpddsprimary->SetClipper(lpddclipper))){OutputDebugString(L"SetClipper error");return 1 ;}//创建一个离屏表面DD_INIT_STRUCT(ddsd);ddsd.ddsCaps.dwCaps = DDSCAPS_OFFSCREENPLAIN | DDSCAPS_VIDEOMEMORY;ddsd.dwFlags = DDSD_CAPS | DDSD_WIDTH | DDSD_HEIGHT ;ddsd.dwHeight = 786 ;ddsd.dwWidth = 1366 ;if(FAILED(lpdd->CreateSurface(&ddsd,&lpddsOffScreen,NULL))){OutputDebugString(L"OffScreen CreateSurface error");return 1 ;}// return success or failure or your own return code herereturn(1);} // end Game_Init/////////////////////////////////////////////////////////////int Game_Shutdown(void *parms = NULL, int num_parms = 0){// this is called after the game is exited and the main event// loop while is exited, do all you cleanup and shutdown here// simply blow away the IDirectDraw4 interfaceif(lpddclipper){lpddclipper->Release();lpddclipper = NULL ;}if(lpddsprimary){lpddsprimary->Release();lpddsprimary = NULL ;}if (lpdd)   {   lpdd->Release();   lpdd = NULL;   } // end if// return success or failure or your own return code herereturn(1);} // end Game_Shutdown// WINMAIN ////////////////////////////////////////////////int WINAPI WinMain(HINSTANCE hinstance,HINSTANCE hprevinstance,LPSTR lpcmdline,int ncmdshow){WNDCLASSEX winclass; // this will hold the class we createHWND   hwnd; // generic window handleMSG   msg; // generic messageHDC        hdc;      // graphics device context// first fill in the window class stucturewinclass.cbSize         = sizeof(WNDCLASSEX);winclass.style= CS_DBLCLKS | CS_OWNDC |   CS_HREDRAW | CS_VREDRAW;winclass.lpfnWndProc= WindowProc;winclass.cbClsExtra= 0;winclass.cbWndExtra= 0;winclass.hInstance= hinstance;winclass.hIcon= LoadIcon(NULL, IDI_APPLICATION);winclass.hCursor= LoadCursor(NULL, IDC_ARROW); winclass.hbrBackground= (HBRUSH)GetStockObject(BLACK_BRUSH);winclass.lpszMenuName= NULL;winclass.lpszClassName= WINDOW_CLASS_NAME;winclass.hIconSm        = LoadIcon(NULL, IDI_APPLICATION);// save hinstance in globalhinstance_app = hinstance;// register the window classif (!RegisterClassEx(&winclass))return(0);// create the windowif (!(hwnd = CreateWindowEx(NULL,                  // extended styleWINDOW_CLASS_NAME,     // classL"DirectDraw Initialization Demo", // titleWS_OVERLAPPED|WS_VISIBLE, 0,0,  // initial x,ySCREEN_WIDTH,SCREEN_HEIGHT,  // initial width, heightNULL,  // handle to parent NULL,  // handle to menuhinstance,// instance of this applicationNULL)))// extra creation parmsreturn(0);// save main window handlemain_window_handle = hwnd;// initialize game hereGame_Init();//调整窗口大小RECT window_rect = {0,0,SCREEN_WIDTH,SCREEN_HEIGHT} ;AdjustWindowRectEx(&window_rect,GetWindowStyle(main_window_handle),GetMenu(main_window_handle)!=NULL,GetWindowExStyle(main_window_handle));// enter main event loopwhile(TRUE){// test if there is a message in queue, if so get itif (PeekMessage(&msg,NULL,0,0,PM_REMOVE))   {    // test if this is a quit   if (msg.message == WM_QUIT)   break;   // translate any accelerator keys   TranslateMessage(&msg);   // send the message to the window proc   DispatchMessage(&msg);   } // end if       // main game processing goes here   Game_Main();       } // end while// closedown game hereGame_Shutdown();// return to Windows like thisreturn(msg.wParam);} // end WinMain//定义交换函数void Swap(int &x , int &y){int temp = y ;y = x ;x = temp ;}//定义画线函数int Draw_Line(int x0,int y0, int x1, int y1 , DWORD color , UINT *video_buffer, int stepx,int stepy){int dx ,  //起点与终点的X方向间距dy ,  //起点与终点的Y方向间距dx2, //两倍的dxdy2,  //两倍的dyx_inc ,  //实际的x步长值,带有符号y_inc , //实际的y步长值,带有符号p ;     //误差项dx = x1 - x0 ;  //计算x间距dy = y1 - y0 ;  //计算y间距//计算起点的缓冲地址video_buffer+=x0+y0*stepy ;//确定x方向的步进值if(dx>=0){x_inc = stepx;}else{x_inc = -stepx ;dx = -dx ;}//确定y方向的步进值if(dy>=0){y_inc = stepy ;}else{y_inc = -stepy ;dy = -dy ;}//确定dx2,dy2的值dx2 = dx<<1;dy2 = dy<<1 ;//进行步进的选择if(dx <= dy) //斜率绝对值大于1{Swap(dx,dy);Swap(x_inc,y_inc);Swap(dx2,dy2);}else //斜率绝对值小于1,不需要交换{}//绘制直线p = dy2 - dx ;  //计算起点的误差值for(int i = 0 ; i < dx ; i++){*video_buffer = color ;video_buffer += x_inc ;if(p>=0){video_buffer += y_inc ;p = p + dy2 - dx2 ;}else{p = p + dy2 ;}}// end forreturn 0 ;}// end Draw_Line//定义裁剪直线算法int Clipper_Line(int& x0,int& y0,int& x1, int& y1,int screen_width,int screen_height){#define CLIP_CODE_C 0x0000#define CLIP_CODE_N 0x0008#define CLIP_CODE_S 0x0004#define CLIP_CODE_E 0x0002#define CLIP_CODE_W 0x0001#define CLIP_CODE_NE 0x000a#define CLIP_CODE_SE 0x0006#define CLIP_CODE_NW 0x0009#define CLIP_CODE_SW 0x0005int xc0 = x0 ,yc0 = y0 , xc1=x1 , yc1=y1 ;int min_clip_x = SCREEN_WIDTH/3 ,min_clip_y = SCREEN_HEIGHT/3 ,max_clip_x = screen_width*2/3-1,max_clip_y=screen_height*2/3-1 ;int p0_code = 0 ,p1_code = 0 ;//确定各个顶点所在的位置代码if(y0<min_clip_y)p0_code|=CLIP_CODE_N;else if(y0>max_clip_y)p0_code|=CLIP_CODE_S;if(x0<min_clip_x)p0_code|=CLIP_CODE_W;else if(x0>max_clip_x)p0_code|=CLIP_CODE_E;if(y1<min_clip_y)p1_code|=CLIP_CODE_N;else if(y1>max_clip_y)p1_code|=CLIP_CODE_S;if(x1<min_clip_x)p1_code|=CLIP_CODE_W;else if(x1>max_clip_x)p1_code|=CLIP_CODE_E;//先检测一些简单的情况if(p0_code&p1_code) //有相同的位置代码,表示在裁剪区外部return 0 ;if(p0_code==0&&p1_code==0) //表示两个点都在裁剪区内,不需要裁剪return 1 ;//判断第一个点的位置代码switch(p0_code){case CLIP_CODE_C:break;case CLIP_CODE_N:{yc0 = min_clip_y ;xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0);break ;}case CLIP_CODE_S:{yc0 = max_clip_y;xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0);break ;}case CLIP_CODE_W:{xc0=min_clip_x;yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0);break;}case CLIP_CODE_E:{xc0=max_clip_x;yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0);break;}case CLIP_CODE_NE:{yc0 = min_clip_y;xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0);if(xc0<min_clip_x||xc0>max_clip_x){xc0=max_clip_x;yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0);}break;}case CLIP_CODE_SE:{yc0 = max_clip_y;xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0);if(xc0<min_clip_x||xc0>max_clip_x){xc0=max_clip_x;yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0);}break;}case CLIP_CODE_NW:{yc0=min_clip_y;xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0);if(xc0<min_clip_x||xc0>max_clip_x){xc0=min_clip_x;yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0);}break;}case CLIP_CODE_SW:{yc0=max_clip_y;xc0 = x0 + 0.5 + (yc0-y0)*(x1-x0)/(y1-y0);if(xc0<min_clip_x||xc0>max_clip_x){xc0=min_clip_x;yc0=y0+0.5+(xc0-x0)*(y1-y0)/(x1-x0);}break;}default:break;} // end switch(p0_code)//判断第二个点的位置代码switch(p1_code){case CLIP_CODE_C:break;case CLIP_CODE_N:{yc1 = min_clip_y ;xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0);break ;}case CLIP_CODE_S:{yc1 = max_clip_y;xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0);break ;}case CLIP_CODE_W:{xc1=min_clip_x;yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0);break;}case CLIP_CODE_E:{xc1=max_clip_x;yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0);break;}case CLIP_CODE_NE:{yc1 = min_clip_y;xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0);if(xc1<min_clip_x||xc1>max_clip_x){xc1=max_clip_x;yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0);}break;}case CLIP_CODE_SE:{yc1 = max_clip_y;xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0);if(xc1<min_clip_x||xc1>max_clip_x){xc1=max_clip_x;yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0);}break;}case CLIP_CODE_NW:{yc1=min_clip_y;xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0);if(xc1<min_clip_x||xc1>max_clip_x){xc1=min_clip_x;yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0);}break;}case CLIP_CODE_SW:{yc1=max_clip_y;xc1 = x1 + 0.5 + (yc1-y1)*(x1-x0)/(y1-y0);if(xc1<min_clip_x||xc1>max_clip_x){xc1=min_clip_x;yc1=y1+0.5+(xc1-x1)*(y1-y0)/(x1-x0);}break;}default:break;} // end switch(p1_code)//进行最后的检测if(xc0>max_clip_x||xc0<min_clip_x||yc0>max_clip_y||yc0<min_clip_y||xc1>max_clip_x||xc1<min_clip_x||yc1>max_clip_y||yc1<min_clip_y){//表示全部在裁剪区外部return 0 ;}//将裁减后的数据返回x0 = xc0 ;x1 = xc1 ;y0 = yc0 ;y1 = yc1 ;return 1 ;}// end Clipper_Line///////////////////////////////////////////////////////////

附上运行结果图:


好了,今天就到这里了,吃饭去了哦!!!

原创粉丝点击