/*--------------------------------------------------------------
使用Cohen-Sutherland算法裁剪线段
(c) 2004-06-18
--------------------------------------------------------------*/
#include <windows.h>
#define ROUND(a) ((int)(a+0.5))
#define MAXLINE 1000
#define LEFT_EDGE 0x1
#define RIGHT_EDGE 0x2
#define BOTTOM_EDGE 0x4
#define TOP_EDGE 0x8
#define INSIDE(a) (!a)
#define REJECT(a,b) (a&b)
#define ACCEPT(a,b) (!(a|b))
void lineDDA(HDC hdc, POINT p1, POINT p2, COLORREF rgb);
void drawWindow(HDC hdc, POINT winMin, POINT winMax, COLORREF rgb);
void invalidateWindow(HWND hwnd, POINT winMin, POINT winMax);
typedef struct Line {
POINT p1, p2;
} Line;
unsigned char encode(POINT pt, POINT winMin, POINT winMax)
{
//决定pt所在的位置
unsigned char code = 0x00;
if (pt.x < winMin.x)
code = code | LEFT_EDGE;
if (pt.x > winMax.x)
code = code | RIGHT_EDGE;
if (pt.y < winMin.y)
code = code | BOTTOM_EDGE;
if (pt.y > winMax.y)
code = code | TOP_EDGE;
return code;
}
void swapPts(POINT *p1, POINT *p2)
{
POINT tmp;
tmp = *p1;
*p1 = *p2;
*p2 = tmp;
}
void swapCodes(unsigned char *c1, unsigned char *c2)
{
unsigned char tmp;
tmp = *c1;
*c1 = *c2;
*c2 = tmp;
}
void clipLine(HDC hdc, POINT winMin, POINT winMax, POINT p1, POINT p2)
{
unsigned char code1, code2;
int done = FALSE, draw = FALSE;
float m;
while (!done)
{
//得到两点的位置
code1 = encode(p1, winMin, winMax);
code2 = encode(p2, winMin, winMax);
//两点都在窗口内么
if (ACCEPT(code1, code2))
{
done = TRUE;
draw = TRUE;
}
else
{
//两点都在窗口外么
if (REJECT(code1, code2))
{
done = TRUE;
}
else
{
if (INSIDE(code1))
{
swapPts(&p1, &p2); /*确保p1在窗口之外*/
swapCodes(&code1, &code2);
}
if (p2.x != p1.x)
{
/*使用斜率(m)来找到直线和边界的交点*/
m = (float) (p2.y - p1.y) / (p2.x - p1.x);
}
//如果pt1在窗口左边
if (code1 & LEFT_EDGE)
{
//算出直线与左边界的交点
p1.y += (long)((winMin.x - p1.x) * m);
p1.x = winMin.x;
}
else
{
if (code1 & RIGHT_EDGE)
{
p1.y += (long) ((winMax.x - p1.x) * m);
p1.x = winMax.x;
}
else
{
if (code1 & BOTTOM_EDGE)
{
/*只有直线不是竖直的才要更新*/
if (p2.x != p1.x)
{
p1.x += (long)((winMin.y - p1.y) / m);
}
p1.y = winMin.y;
}
else
{
if (code1 & TOP_EDGE)
{
if (p2.x != p1.x)
{
p1.x += (long) ((winMax.y - p1.y) / m);
}
p1.y = winMax.y;
}
}
}
}
}
}
}
if (draw)
{
//如果需要,画出直线
lineDDA(hdc, p1, p2, RGB(0, 255, 0));
}
}
//窗口函数说明
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
//初始化窗口类
//WinMain函数说明
int WINAPI WinMain( HINSTANCE hInstance,HINSTANCE hPrevInst, LPSTR lpszCmdLine, int nCmdShow)
{
HWND hwnd ;
MSG Msg ;
WNDCLASS wndclass ;
char lpszClassName[] = "窗口"; //窗口类名
char lpszTitle[]= "My_Windows"; //窗口标题名
//窗口类的定义
wndclass.style = 0; //窗口类型为缺省类型
wndclass.lpfnWndProc = WndProc ; //窗口处理函数为WndProc
wndclass.cbClsExtra = 0 ; //窗口类无扩展
wndclass.cbWndExtra = 0 ; //窗口实例无扩展
wndclass.hInstance = hInstance ; //当前实例句柄
wndclass.hIcon = LoadIcon( NULL, IDI_APPLICATION) ;
//窗口的最小化图标为缺省图标
wndclass.hCursor = LoadCursor( NULL, IDC_ARROW) ;
//窗口采用箭头光标
wndclass.hbrBackground = GetStockObject( WHITE_BRUSH) ;
//窗口背景为白色
wndclass.lpszMenuName = NULL ; //窗口中无菜单
wndclass.lpszClassName = lpszClassName ;
//窗口类名为"窗口示例"
//窗口类注册
if( !RegisterClass( &wndclass)) //如果注册失败则发出警告声音
{
MessageBeep(0) ;
return FALSE ;
}
//创建窗口
hwnd=CreateWindow(lpszClassName, //窗口类名
lpszTitle, //窗口实例的标题名
WS_OVERLAPPEDWINDOW,//窗口的风格
CW_USEDEFAULT,
CW_USEDEFAULT, //窗口左上角坐标为缺省值
CW_USEDEFAULT,
CW_USEDEFAULT,, //窗口的高和宽为缺省值
NULL, //此窗口无父窗口
NULL, //此窗口无主菜单
hInstance, //创建此窗口的应用程序的当前句柄
NULL) ; //不使用该值
//显示窗口
ShowWindow( hwnd, nCmdShow) ;
//绘制用户区
UpdateWindow(hwnd);
//消息循环
while( GetMessage(&Msg, NULL, 0, 0))
{
TranslateMessage( &Msg) ;
DispatchMessage( &Msg) ;
}
return Msg.wParam; //消息循环结束即程序终止时将信息返回系统
}
//窗口函数
LRESULT CALLBACK WndProc(HWND hwnd,UINT message,WPARAM wParam, LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
static POINT winMin, winMax;
static Line ln[MAXLINE];
static iCount;
static POINT p1, p2;
int i;
switch(message)
{
case WM_SIZE:
//确定窗口位置
winMin.x = LOWORD(lParam) / 4;
winMax.x = LOWORD(lParam) * 3 / 4;
winMin.y = HIWORD(lParam) / 4;
winMax.y = HIWORD(lParam) * 3 / 4;
iCount = 0;
UpdateWindow(hwnd);
return 0;
case WM_LBUTTONDOWN:
//得到每一个点
p1.x = LOWORD(lParam);
p1.y = HIWORD(lParam);
SetCapture(hwnd);
return 0;
case WM_MOUSEMOVE:
if (wParam & MK_LBUTTON)
{
hdc = GetDC(hwnd);
p2.x = LOWORD(lParam);
p2.y = HIWORD(lParam);
InvalidateRect(hwnd, NULL, TRUE);
UpdateWindow(hwnd);
lineDDA(hdc, p1, p2, RGB(255, 0, 0));
ReleaseDC(hwnd, hdc);
}
return 0;
case WM_LBUTTONUP:
p2.x = LOWORD(lParam);
p2.y = HIWORD(lParam);
ReleaseCapture();
//把p1, p2放进直线数组
ln[iCount].p1 = p1;
ln[iCount].p2 = p2;
iCount++;
InvalidateRect(hwnd, NULL, TRUE);
UpdateWindow(hwnd);
return 0;
case WM_PAINT:
hdc = BeginPaint(hwnd, &ps);
//画出窗口
drawWindow(hdc, winMin, winMax, RGB(0, 0, 255));
//画出所有剪裁过的线
for (i = 0; i < iCount; i++)
{
clipLine(hdc, winMin, winMax, ln[i].p1, ln[i].p2);
}
EndPaint(hwnd, &ps);
return 0;
case WM_DESTROY:
PostQuitMessage(0); //调用PostQuitMessage发出WM_QUIT消息
return 0;
}
return DefWindowProc(hwnd,message,wParam,lParam);
}
void lineDDA(HDC hdc, POINT p1, POINT p2, COLORREF rgb)
{
//DDA算法画出p1,p2之间的直线
int dx = p2.x - p1.x, dy = p2.y - p1.y, steps, k;
float xIncrement, yIncrement, x = (float)p1.x, y = (float)p1.y;
if (abs(dx) > abs(dy))
steps = abs(dx);
else
steps = abs(dy);
xIncrement = dx/(float)steps;
yIncrement = dy/(float)steps;
SelectObject(hdc, CreatePen(PS_SOLID, 0, rgb));
//用rgb颜色画出出直线
SetPixel(hdc, ROUND(x), ROUND(y), rgb);
for (k = 0; k < steps; k++)
{
x += xIncrement;
y += yIncrement;
SetPixel(hdc, ROUND(x), ROUND(y), rgb);
}
DeleteObject(SelectObject(hdc, GetStockObject(BLACK_PEN)));
}
void drawWindow(HDC hdc, POINT winMin, POINT winMax, COLORREF rgb)
{
POINT p1, p2;
p1 = winMin;
p2 = winMin;
p2.x = winMax.x;
lineDDA(hdc, p1, p2, rgb); //TOP EDGE
p1 = winMax;
lineDDA(hdc, p2, p1, rgb); //RIGHT EDGE
p2 = winMax;
p2.x = winMin.x;
lineDDA(hdc, p1, p2, rgb); //BOTTOM EDGE
p1 = winMin;
lineDDA(hdc, p1, p2, rgb); //LEFT EDGE
}
评论