计算机图形学 实验三 梁氏裁剪算法

实验三:裁剪算法

实验目的: 掌握 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;
}
原文地址:https://www.cnblogs.com/PRCdefender/p/14606363.html