1.实验目的:
了解二维图形裁剪的原理(点的裁剪、直线的裁剪、多边形的裁剪),利用VC+OpenGL实现直线的裁剪算法。
2.实验内容:
(1) 理解直线裁剪的原理(Cohen-Surtherland算法)
(2) 利用VC+OpenGL实现直线的编码裁剪算法,在屏幕上用一个封闭矩形裁剪任意一条直线。
(3) 调试、编译、修改程序。
3.实验原理:
编码裁剪算法的主要思想是:对于每条线段,分为三种情况处理。(1)若线段完全在窗口之内,则显示该线段,称为“取”;(2)若线段明显在窗口之外,则丢弃该线段,称为“弃”;(3)若线段既不满足“取”的条件,也不满足“舍”的条件,则把线段分割为两段。其中一段完全在窗口之外,可弃之;对另一段则重复上述处理。
算法中,为了快速判断一条直线段与矩形窗口的位置关系,采用了如图所示的空间划分和编码方案。延长窗口的四条边界,把未经裁剪的图形区域分为九个区,每个区有一个四位二进制的编码,从左到右各位依次表示上、下、右、左。例如,区号0101,左起第二位1表示该区在窗口的下方;右起第一位的1表示该区在窗口的左方。整个区号表示该区在窗口的左下方。
裁剪一条线段时,先求出两端点所在的区号code1和code2,若code1 = 0且code2 = 0,则说明线段的两个端点均在窗口内,那么整条线段必在窗口内,应取之;若code1和code2经按位与运算的结果不为0,则说明两个端点同在窗口的上方、下方、左方或右方。这种情况下,对线段的处理是弃之。如果上述两种条件都不成立,则按第三种情况处理。求出线段与窗口某边的交点,在交点处把线段一分为二,其中必有一段完全在窗口外,可弃之,对另一段则重复上述处理。
4.实验代码:
#include <GL/glut.h>
#include <stdio.h>
#define LEFT_EDGE 1 //代表0001
#define RIGHT_EDGE 2 //代表0010
#define BOTTOM_EDGE 4 //代表0100
#define TOP_EDGE 8 //代表1000
bool bInput, accept, bDraw;
struct Rectangle
{
float xmin,xmax,ymin,ymax;
};
Rectangle rect;
struct Point { int x, y;};
Point pt[2];
void LineGL(Point pt0, Point pt1)
{
glBegin (GL_LINES);
glColor3f (1.0f, 0.0f, 0.0f); glVertex2f (pt0.x,pt0.y);
glColor3f (0.0f, 1.0f, 0.0f); glVertex2f (pt1.x,pt1.y);
glEnd ();
}
void PointGL(Point pt)
{
glPointSize(2);
glBegin (GL_POINTS);
glColor3f (1.0f, 0.0f, 0.0f); glVertex2f (pt.x,pt.y);
glEnd ();
}
int CompCode(Point pt,Rectangle rect)
{
int code=0x00;
if(pt.y<rect.ymin)
code=code|BOTTOM_EDGE;
if(pt.y>rect.ymax)
code=code|TOP_EDGE;
if(pt.x>rect.xmax)
code=code|RIGHT_EDGE;
if(pt.x<rect.xmin)
code=code|LEFT_EDGE;
return code;
}
void cohensutherlandlineclip(Rectangle rect, Point &pt0, Point &pt1)
{
int &x0 = pt0.x;
int &y0 = pt0.y;
int &x1 = pt1.x;
int &y1 = pt1.y;
bool done = false;
float x,y;
int code0,code1, codeout;
code0 = CompCode(pt0,rect);
code1 = CompCode(pt1,rect);
do{
if(!(code0 | code1))
{
accept=true;
done=true;
}
else if(code0 & code1)
{
done = true;
}
else
{
if(code0!=0)
codeout = code0;
else
codeout = code1;
if(codeout&LEFT_EDGE){
y=y0+(y1-y0)*(rect.xmin-x0)/(x1-x0);
x=(float)rect.xmin;
}
else if(codeout&RIGHT_EDGE){
y=y0+(y1-y0)*(rect.xmax-x0)/(x1-x0);
x=(float)rect.xmax;
}
else if(codeout&BOTTOM_EDGE){
x=x0+(x1-x0)*(rect.ymin-y0)/(y1-y0);
y=(float)rect.ymin;
}
else if(codeout&TOP_EDGE){
x=x0+(x1-x0)*(rect.ymax-y0)/(y1-y0);
y=(float)rect.ymax;
}
if(codeout == code0)
{
x0=x;y0=y;
code0 = CompCode(pt0,rect);
}
else
{
x1=x;y1=y;
code1 = CompCode(pt1,rect);
}
}
}while(!done);
}
void myDisplay()
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f (1.0f, 0.0f, 0.0f);
glRectf(rect.xmin,rect.ymin,rect.xmax,rect.ymax);
if(accept)
{
if(bDraw)
{
LineGL(pt[0],pt[1]);
}
else
PointGL(pt[0]);
}
glFlush();
}
void Init()
{
glClearColor(0.0, 0.0, 0.0, 0.0);
glShadeModel(GL_FLAT);
rect.xmin=100;
rect.xmax=300;
rect.ymin=100;
rect.ymax=300;
accept = true;
bInput = false;
printf("Press Left Mouse Button to Draw two Point to create a line!"nPress key 'c' to Clip!"n");
}
void Reshape(int w, int h)
{
glViewport(0, 0, (GLsizei) w, (GLsizei) h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, (GLdouble) w, 0.0, (GLdouble) h);
}
void keyboard(unsigned char key, int x, int y)
{
switch (key)
{
case 'c':
cohensutherlandlineclip(rect, pt[0],pt[1]);
glutPostRedisplay();
break;
default:
break;
}
}
void mouse(int button, int state, int x, int y) //鼠标处理回调函数
{
switch (button)
{
case GLUT_LEFT_BUTTON://鼠标左键
if (state == GLUT_DOWN)//按下
{
if (bInput == false)
{
pt[0].x = x;
pt[0].y = 480 - y;
bInput = true;
bDraw = false;
accept = true;
glutPostRedisplay();//
}
else
{
pt[1].x = x;
pt[1].y = 480 - y;
bDraw = true;
bInput = false;
glutPostRedisplay();//
}
}
break;
default:
break;
}
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(640, 480);
glutCreateWindow("Hello World!");
Init();
glutDisplayFunc(myDisplay);
glutReshapeFunc(Reshape);
glutKeyboardFunc(keyboard);
glutMouseFunc(mouse); // 注册鼠标处理函数
glutMainLoop();
return 0;
}
(1)程序增加了鼠标功能,在窗口内不同位置点击左键可生成一条直线;
(2)实现鼠标函数mouse(int button, int state, int x, int y):处理鼠标click事件的函数。一般有4个参数:第一个参数表明哪个鼠标键被按下或松开,这个变量可以是下面的三个值中的一个:
剩下的两个参数(x,y)提供了鼠标当前的窗口坐标(以左上角为原点)。
更多详细介绍请参考:http://blog.csdn.net/nauty_li/archive/2008/03/29/2227163.aspx
(3)为确保程序运行正确请不要用鼠标拖动改变窗口的大小。
(4)附MFC代码示例:/Files/opengl/LineClip_GDI.rar
5.实验思考题
请分别给出直线的三种不同位置情况,测试实验代码是否存在问题,有的话请调试改正。