一。算法介绍
一般情况下,当我们需要裁剪一条直线的时候,要分为全部可见,部分可见,全部不可见这几种情况,所以这样就需要有很多种的情况需要进行处理。目前,已经发明了很多的算法来处理这些不同的情况,其中应用最广范的就是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 0x0005 int 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++ stuff
using 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 types
typedef 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 window
HINSTANCE hinstance_app = NULL; // globally track hinstance
// directdraw stuff
LPDIRECTDRAW7 lpdd = NULL; // dd object
LPDIRECTDRAWSURFACE7 lpddsprimary = NULL; // dd primary surface
LPDIRECTDRAWSURFACE7 lpddsback = NULL; // dd back surface
LPDIRECTDRAWPALETTE lpddpal = NULL; // a pointer to the created dd palette
LPDIRECTDRAWCLIPPER lpddclipper = NULL; // dd clipper
PALETTEENTRY palette[256]; // color palette
PALETTEENTRY save_palette[256]; // used to save palettes
DDSURFACEDESC2 ddsd; // a direct draw surface description struct
DDBLTFX ddbltfx; // used to fill
DDSCAPS2 ddscaps; // a direct draw surface capabilities struct
HRESULT ddrval; // result back from dd calls
DWORD start_clock_count = 0; // used for timing
LPDIRECTDRAWSURFACE7 lpddsOffScreen = NULL ; //离屏表面
int window_close = 0 ; //标识窗口是否关闭
// these defined the general clipping rectangle
int 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 pixel
char 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 system
PAINTSTRUCT ps; // used in WM_PAINT
HDC hdc; // handle to a device context
char buffer[80]; // used to print strings
// what is the message
switch(msg)
{
case WM_CREATE:
{
// do initialization stuff here
// return success
return(0);
} break;
case WM_PAINT:
{
// simply validate the window
hdc = BeginPaint(hwnd,&ps);
// end painting
EndPaint(hwnd,&ps);
// return success
return(0);
} break;
case WM_DESTROY:
{
// kill the application, this sends a WM_QUIT message
PostQuitMessage(0);
// return success
return(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_CLOSE
if(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 here
return(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 error
if (FAILED(DirectDrawCreateEx(NULL, (void **)&lpdd, IID_IDirectDraw7, NULL)))
return(0);
// set cooperation to normal since this will be a windowed app
if(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 here
return(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 interface
if(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 here
return(1);
} // end Game_Shutdown
// WINMAIN
int WINAPI WinMain( HINSTANCE hinstance,
HINSTANCE hprevinstance,
LPSTR lpcmdline,
int ncmdshow)
{
WNDCLASSEX winclass; // this will hold the class we create
HWND hwnd; // generic window handle
MSG msg; // generic message
HDC hdc; // graphics device context
// first fill in the window class stucture
winclass.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 global
hinstance_app = hinstance;
// register the window class
if (!RegisterClassEx(&winclass))
return(0);
// create the window
if (!(hwnd = CreateWindowEx(NULL, // extended style
WINDOW_CLASS_NAME, // class
L"DirectDraw Initialization Demo", // title
WS_OVERLAPPED|WS_VISIBLE,
0,0, // initial x,y
SCREEN_WIDTH,SCREEN_HEIGHT, // initial width, height
NULL, // handle to parent
NULL, // handle to menu
hinstance,// instance of this application
NULL))) // extra creation parms
return(0);
// save main window handle
main_window_handle = hwnd;
// initialize game here
Game_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 loop
while(TRUE)
{
// test if there is a message in queue, if so get it
if (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 here
Game_Shutdown();
// return to Windows like this
return(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, //两倍的dx
dy2, //两倍的dy
x_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 for
return 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 0x0005
int 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
///
附上运行结果图:
好了,今天就到这里了,吃饭去了哦!!!