实验三:裁剪算法
实验目的: 掌握 Liang-Barsky 裁剪算法
基本要求:
实现 Liang-Barsky 裁剪算法
绘制任意方向数量线段,可移动的裁剪窗口,通过不同颜色标识裁剪窗口内外 的部分,效果可参考下图(可交互的移动裁剪窗口并实时显示裁剪效果)
画线的命令可以使用 OpenGL 提供的画线函数
实现:
使用方法:邮件开启菜单。点击规划后开始画线段集合。画完线段集合后按回车就可以退出,然后鼠标左键拖动就可以任意剪裁了。
#define EXIT_SUCCESS 0 #include<stdio.h> #include <stdlib.h> #include<GL/glut.h> #include<math.h> #include<queue> #include<vector> #include<algorithm> #include "cross.h" #include<iostream> using namespace std; // later Edition int MODE = 0; bool mouseLeftDown; // 实时记录鼠标左键状态 bool mouseRightDown; // 实时记录鼠标左键状态 float mouseX, mouseY; // 实时记录鼠标位置,与下一次鼠标位置之间形成微分 bool status = false; // 标记当前是否为规划状态,规划状态下进行线段的绘制,非规划状态下进行任意的剪裁 int startX=0, startY=0, endX=0, endY=0; int start[2] = { 0 }; int end[2] = { 0 }; float red=1.0,green=1.0, blue=0.0; float PI = 3.415926535; int clipX1, clipX2, clipY1, clipY2; Point sp, previous, now; bool start_pass = false; // 纯粹的交点模式,简单的交点判别法 vector<pair<Point,Point> > q;// 存储边集,在编辑中存储需要的数据 vector<vector<edge> > NET(501); vector<vector<edge> > AET(501);// 活性边 // 数据结构上,对于求交,需要以下几个数据结构 // 1.存储所有边,然后存储边的所有交集。对于边的所有交集,目前可以按照一个简单的有序队列的方式进行。 vector<vector<int> > t(501);// 存储所有扫描线与各个边的交点。 ostream& operator<<(ostream& output,const Point& p) { cout <<"("<< p.x << "," << p.y << ")"; return output; } bool ClipT(float p, float q, float* u1, float* u2) { float r; if (p < 0) { r = q / p; if (r > * u2) return false; if (r > * u1) *u1 = r; } else if (p > 0) { r = q / p; if (r < *u1) return false; if (r < *u2) *u2 = r; } else return (q >= 0); return true; } void LB_LineClip(float x1, float y1, float x2, float y2, float XL, float XR, float YB, float YT) { float dx, dy, u1, u2; dx = x2 - x1;dy = y2 - y1; u1 = 0;u2 = 1; if (ClipT(-dx, x1 - XL, &u1, &u2)) if (ClipT(dx, XR - x1, &u1, &u2)) if (ClipT(-dy, y1 - YB, &u1, &u2)) if (ClipT(dy, YT - y1, &u1, &u2)) { glVertex2i(int(x1 + u1 * dx), int(y1 + u1 * dy)); glVertex2i(int(x1 + u2 * dx), int(y1 + u2 * dy)); } } int arr[500][500]; void init(void) { //glClearColor(0.0, 0.0, 0.0, 0.0);/* select clearing color */ // 设置背景颜色为黑色 //glMatrixMode(GL_MODELVIEW); glClearColor(0.0, 0.0, 0.0, 0.0); /* white background */ glColor3f(1.0, 0.0, 0.0); /* draw in red */ /* set up viewing: */ /* 500 x 500 window with origin lower left */ glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(0.0, 500.0, 0.0, 500.0); glMatrixMode(GL_MODELVIEW); } #define RED 1233 #define BLUE 1234 #define GREEN 1235 #define WHITE 12366 #define YELLOW 12367 #define SCAN_LINE 12368 #define AET 12369 void processMenuEvents(int option) { //option,就是传递过来的value的值。 switch (option) { case RED: red = 1.0; green = 0.0; blue = 0.0; break; case GREEN: red = 0.0; green = 1.0; blue = 0.0; break; case BLUE: red = 0.0; green = 0.0; blue = 1.0; break; case WHITE: red = 1.0; green = 1.0; blue = 1.0; break; case YELLOW: red = 1.0; green = 1.0; blue = 0.0;break; case SCAN_LINE: for (int i = 1;i <= 500;i++)t[i].clear(); q.clear(); // 将归去的全部线段全部清空 sp.x = -1;sp.y = -1; startX = startY = endX = endY = -1;//将临时起点和中点全部清空。 glutPostRedisplay(); status = true; MODE = 0;break; case AET: MODE = 1;break; } } void createGLUTMenus() { int menu; // 创建菜单并告诉GLUT,processMenuEvents处理菜单事件。 menu = glutCreateMenu(processMenuEvents); //给菜单增加条目 glutAddMenuEntry("绘制线段集", SCAN_LINE); glutAddMenuEntry("红色", RED); glutAddMenuEntry("蓝色", BLUE); glutAddMenuEntry("绿色", GREEN); glutAddMenuEntry("白色", WHITE); glutAddMenuEntry("黄色", YELLOW); // 把菜单和鼠标右键关联起来。 glutAttachMenu(GLUT_RIGHT_BUTTON); } void display(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glColor3f(red, green,blue); glLoadIdentity(); glLineWidth(1.0); // 规划模式下的线段集合进行作图,在任何模式下都会显示,但是内容有可能会被修改 glBegin(GL_LINES); for (int i = 0;i < q.size();i++) { glVertex2i(q[i].first.x, q[i].first.y); glVertex2i(q[i].second.x, q[i].second.y); } glEnd(); // 进行剪裁作图 glColor3f(1 - red, 1 - green, 1 - blue); glLineWidth(3.0); glBegin(GL_LINES); for (int i = 0;i < q.size();i++) { LB_LineClip(q[i].first.x, q[i].first.y, q[i].second.x, q[i].second.y, min(clipX1, clipX2), max(clipX1, clipX2), min(clipY1, clipY2), max(clipY1, clipY2)); } glEnd(); // 实时显示的线段,仅在规划模式下显示。 if (status) {// 规划模式下对绘制的直线进行实时显示 glColor3f(1 - red/2, 1 - green/2, 1 - blue/2); glLineWidth(2.0); // 规划模式下的线段集合进行作图 glBegin(GL_LINES); glVertex2i(startX,startY); glVertex2i(endX, endY); glEnd(); } if (!status) {// 规划模式下对绘制的直线进行实时显示 glColor3f(1 - red, 1 - green, 1 - blue); glLineWidth(3.0); // 规划模式下的线段集合进行作图 glBegin(GL_LINES); glVertex2i(clipX1,clipY1); glVertex2i(clipX2, clipY1); glVertex2i(clipX1, clipY1); glVertex2i(clipX1, clipY2); glVertex2i(clipX2, clipY1); glVertex2i(clipX2, clipY2); glVertex2i(clipX2, clipY2); glVertex2i(clipX1, clipY2); glEnd(); } glColor3f(red, green, blue); if (!status);// 在非规划模式下作图 // Draw here glutSwapBuffers(); } int saveStack = 0; void keyboard(unsigned char key, int x, int y) { switch (key) { case 'q':case 'Q': exit(EXIT_SUCCESS); break; case 13: if (status) { status = false;// 回车退出规划模式,并且进行全部直线的正常绘制。 cout << "should display" << endl; glutPostRedisplay(); } break; case 'r':case 'R': for (int i = 1;i <= 500;i++)t[i].clear(); q.clear(); glutPostRedisplay(); break; } } int ww, hh; void mouse_process(int button, int state, int x, int y) { mouseX = x; mouseY = y; printf("<%d,%d> ", x, y); cout << "===============" << endl; for (int i = 0;i < q.size();i++) {//显示当前的所有边 cout << q[i].first <<"-"<< q[i].second << endl; } now = Point(x, hh - y);// 时刻记录当前的点位 hh = glutGet(GLUT_WINDOW_HEIGHT); if (button == GLUT_LEFT_BUTTON) { if (state == GLUT_DOWN) { if (!mouseLeftDown&&status) { startX=endX = x;startY =endY= hh-y; sp = Point(x, hh - y);// 同步初始点位进行初始化。 } if (!mouseLeftDown && !status) {// 非规划的剪裁模式下,将会进行剪裁起点的刷新。 clipX1 = x;clipY1 = hh - y; } //glutPostRedisplay(); // 鼠标左键按下的一刻只需要起始点即可,并不需要进行刷新。 mouseLeftDown = true; } else if (state == GLUT_UP) { //在规划模式下进行线段输入 // 画好了一个线段需要进行刷新 pair<Point, Point> v(sp, now); if(status)q.push_back(v); glutPostRedisplay(); mouseLeftDown = false; } } else if (button == GLUT_RIGHT_BUTTON) { if (state == GLUT_DOWN) { mouseRightDown = true; } else if (state == GLUT_UP) mouseRightDown = false; } } void mouse_process_active(int x, int y) { if (mouseLeftDown){ if (status) {// 规划模式下对终点坐标进行实时更新 endX = x;endY = hh - y; clipX1 = clipY1 = clipX2 = clipY2 = -1; } else { clipX2 = x;clipY2 = hh - y; endX = -1;endY = -1; } glutPostRedisplay(); } if(mouseRightDown) { } glutPostRedisplay(); } void mouse_process_passtive(int x, int y) {} int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH); glutInitWindowSize(500, 500); glutCreateWindow("右键开启菜单,回车退出规划"); init(); Point a(40, 40), b(150, 60), c(100, 100), d(10, 70); pair<Point, Point> v1(a, b), v2(b, c), v3(c, d), v4(d, a); q.push_back(v1);q.push_back(v2);q.push_back(v3);q.push_back(v4); glutDisplayFunc(display); // later Edition glutMouseFunc(mouse_process); glutMotionFunc(mouse_process_active); glutPassiveMotionFunc(mouse_process_passtive); createGLUTMenus(); glutKeyboardFunc(keyboard); glutMainLoop(); return 0; }