实验三:裁剪算法
实验目的: 掌握 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>\n", 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;
}