Sutherland-Hodgeman 多边形裁剪算法

说明

在阅读此博客前,请访问2018级山东大学计算机学院图形学实验汇总

原笔记通过latex编写,csdn只支持latex部分功能,所以下面主要是将pdf截屏上传。部分内容参考中国农业大学mooc。

Sutherland-Hodgeman

算法较为简单,直接看课本就可以。

在这里插入图片描述在这里插入图片描述

代码

/*
 * 项目名称:Sutherland-Hodgeman
 * 注意:polyPoint中存储的是多边形的所有顶点,每个顶点仅仅存一次
 * 问题:processInput中,有一个崩溃bug,已经避免,但是不知道为什么有bug
 */
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include "shader.h"
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <vector>
using namespace std;

const unsigned int MAXN = 800*600*2;
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
const char* vertexShaderPath="/Users/longzhengtian/Desktop/代码/C++代码/ComputerGraphics/shader.vs";
const char* fragmentShaderPath="/Users/longzhengtian/Desktop/代码/C++代码/ComputerGraphics/shader.fs";

struct Point{
    double x,y;
    Point(double _x=0,double _y=0):x(_x),y(_y){};
    Point&operator=(const Point &p){
        x=p.x; y=p.y;
        return *this;
    };
    bool operator ==(const Point &p){
        return (x==p.x&&y==p.y);
    }
}templine[2];
vector<Point> polyPoint;//多边形
vector<Point> cropPoint;//裁剪点
struct Rectangle{
    double xleft,xright,ytop,ybottom;
}rec;//裁剪窗口,注意,xleft<xright,ytop<ybottom,这是窗口坐标决定的,左上角坐标是(0, 0),左上角是(xleft,ytop),但是ytop比ybottom小

bool DrawRec=false,DrawPoly=false;
bool DrawPolyEnd=false,DrawRecEnd=false;

int clickwho=-1;
unsigned int VBO[4], VAO[4];

void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow *window);
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods);
void SutherlandHodgeman(vector<Point> part_polyPoint,vector<Point> &part_cropPoint,const Rectangle &part_rec);

float transX(double x){return (float)((float)(2*x)-SCR_WIDTH)/(SCR_WIDTH*1.0f);}
float transY(double y){return (float)(SCR_HEIGHT-2*(float)y)/(SCR_HEIGHT*1.0f);}

bool distant(Point p1,Point p2){return (pow(fabs(p1.x-p2.x),2)+pow(fabs(p1.y-p2.y),2)<=25);}
bool IsItOnTheVisibleSide(const Point &p,const int &id,const Rectangle &part_rec);

Point GetTheIntersection(const Point &p1,const Point &p2,const int &id,const Rectangle &part_rec);

int main(){
    //实例化glfw函数
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif

    //创建glfw窗口
    GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "Sutherland-Hodgeman", nullptr, nullptr);
    if (window == nullptr){
        cout << "Failed to create GLFW window" << endl;
        glfwTerminate();
        return -1;
    }

    glfwMakeContextCurrent(window);//将window设置为接下来操作的主窗口
    glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);
    glfwSetKeyCallback(window, key_callback);
    glfwSetMouseButtonCallback(window, mouse_button_callback);

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){//使用glad加载glfw的所有函数指针
        cout << "Failed to initialize GLAD" << endl;
        return -1;
    }

    Shader ourShader(vertexShaderPath,fragmentShaderPath);//创建着色器程序

    float RecVertices[13];//裁剪窗口
    float Poly1Vertices[MAXN];//原始线
    float Poly2Vertices[MAXN];//裁剪窗口里面的线
    float TempLineVertices[7];//临时边

    glGenVertexArrays(4, VAO);
    glGenBuffers(4, VBO);

    glBindVertexArray(VAO[0]);
    glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(RecVertices), RecVertices, GL_DYNAMIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);

    glBindVertexArray(VAO[1]);
    glBindBuffer(GL_ARRAY_BUFFER, VBO[1]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(Poly1Vertices), Poly1Vertices, GL_DYNAMIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);

    glBindVertexArray(VAO[2]);
    glBindBuffer(GL_ARRAY_BUFFER, VBO[2]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(Poly2Vertices), Poly2Vertices, GL_DYNAMIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);

    glBindVertexArray(VAO[3]);
    glBindBuffer(GL_ARRAY_BUFFER, VBO[3]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(TempLineVertices), TempLineVertices, GL_DYNAMIC_DRAW);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    glEnable(GL_LINE_SMOOTH);//启用线宽
    glLineWidth(10);
    glEnable(GL_PROGRAM_POINT_SIZE);
    glPointSize(10);

    while (!glfwWindowShouldClose(window)){
        //输入
        processInput(window);

        //渲染命令
        glClearColor(0.7f, 0.7f, 0.7f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        //裁剪窗口
        //ourShader.setVec4("ourColor",greenValue,blueValue,redValue,1.0f);
        ourShader.setVec4("ourColor",1.0f,1.0f,1.0f,1.0f);
        ourShader.use();
        glBindVertexArray(VAO[0]);
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

        //主多边形
        ourShader.setVec4("ourColor",1.0f,0.0f,0.0f,1.0f);
        ourShader.use();
        glBindVertexArray(VAO[1]);
        if(DrawPoly) glDrawArrays(GL_LINE_STRIP, 0, polyPoint.size());
        else glDrawArrays(GL_LINE_LOOP, 0, polyPoint.size());

        //裁剪多边形
        ourShader.setVec4("ourColor",0.0f,0.0f,0.0f,1.0f);
        ourShader.use();
        glBindVertexArray(VAO[2]);
        glDrawArrays(GL_LINE_LOOP, 0, cropPoint.size());

        //临时边
        ourShader.setVec4("ourColor",1.0f,0.0f,0.0f,1.0f);
        ourShader.use();
        glBindVertexArray(VAO[3]);
        glDrawArrays(GL_LINES, 0, 2);

        //检查并调用事件,交换缓冲
        glfwSwapBuffers(window);
        glfwPollEvents();
    }

    glDeleteVertexArrays(4, VAO);
    glDeleteBuffers(4, VBO);
    glfwTerminate();
    return 0;
}
Point GetTheIntersection(const Point &p1,const Point &p2,const int &id,const Rectangle &part_rec){//获得交点
    if(id==0) return {((part_rec.ybottom-p1.y)/(p2.y-p1.y))*(p2.x-p1.x)+p1.x,part_rec.ybottom};
    else if(id==1) return {part_rec.xright,((part_rec.xright-p1.x)/(p2.x-p1.x))*(p2.y-p1.y)+p1.y};
    else if(id==2) return {((part_rec.ytop-p1.y)/(p2.y-p1.y))*(p2.x-p1.x)+p1.x,part_rec.ytop};
    else return {part_rec.xleft,((part_rec.xleft-p1.x)/(p2.x-p1.x))*(p2.y-p1.y)+p1.y};
}
bool IsItOnTheVisibleSide(const Point &p,const int &id,const Rectangle &part_rec){//判断是否在可见侧
    if(id==0) return p.y>=part_rec.ybottom;
    else if(id==1) return p.x<=part_rec.xright;
    else if(id==2) return p.y<=part_rec.ytop;
    else return p.x>=part_rec.xleft;
}
void SutherlandHodgeman(vector<Point> part_polyPoint,vector<Point> &part_cropPoint,const Rectangle &part_rec){
    /* 按照这个顺序
     * Point windowPoint[4]={{part_rec.xleft,part_rec.ybottom},
     *                  {part_rec.xright,part_rec.ybottom},
     *                  {part_rec.xright,part_rec.ytop},
     *                  {part_rec.xleft,part_rec.ytop}};
     * double windowLine[4]={part_rec.ybottom,part_rec.xright,part_rec.ytop,part_rec.xleft};
     */
    part_cropPoint.clear();
    vector<float> tempvToCrop;
    for (int i=0; i<4; i++){//对于四条边
        for (int j=0; j<part_polyPoint.size(); j++){//对于多边形的剩余顶点
            int nextpolyid=(j+1)%part_polyPoint.size();
            if(!IsItOnTheVisibleSide(part_polyPoint[j],i,part_rec) && IsItOnTheVisibleSide(part_polyPoint[nextpolyid],i,part_rec)){//p1不可见,p2可见
                Point tempIntersection=GetTheIntersection(part_polyPoint[j],part_polyPoint[nextpolyid],i,part_rec);//交点
                part_cropPoint.push_back(tempIntersection);
                part_cropPoint.push_back(part_polyPoint[nextpolyid]);
            }
            else if(IsItOnTheVisibleSide(part_polyPoint[j],i,part_rec) && IsItOnTheVisibleSide(part_polyPoint[nextpolyid],i,part_rec)){//p1可见,p2可见
                part_cropPoint.push_back(part_polyPoint[nextpolyid]);
            }
            else if(IsItOnTheVisibleSide(part_polyPoint[j],i,part_rec) && !IsItOnTheVisibleSide(part_polyPoint[nextpolyid],i,part_rec)){//p1可见,p2不可见
                Point tempIntersection=GetTheIntersection(part_polyPoint[j],part_polyPoint[nextpolyid],i,part_rec);//交点
                part_cropPoint.push_back(tempIntersection);
            }
        }
        if(i!=3){//剩余顶点作为新的多边形
            part_polyPoint.clear();
            for (auto it:part_cropPoint) part_polyPoint.push_back(it);
            part_cropPoint.clear();
        }
    }
    for (auto it:part_cropPoint){
        tempvToCrop.push_back(transX(it.x));
        tempvToCrop.push_back(transY(it.y));
        tempvToCrop.push_back(1.0f);
    }
    float* tempfToCrop=tempvToCrop.data();
    glBindBuffer(GL_ARRAY_BUFFER, VBO[2]);
    glBufferSubData(GL_ARRAY_BUFFER, 0, tempvToCrop.size() * sizeof(float), tempfToCrop);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void processInput(GLFWwindow *window){
    if(DrawPoly){//如果此时已经开始画多边形,这里是画临时边
        double xpos,ypos;
        glfwGetCursorPos(window,&xpos,&ypos);
        templine[0]=polyPoint[polyPoint.size()-1];
        templine[1]=Point((int)xpos,(int)ypos);
        float tempvec[]={transX(templine[0].x),transY(templine[0].y),1.0f,
                         transX(templine[1].x),transY(templine[1].y),1.0f
        };
        glBindBuffer(GL_ARRAY_BUFFER, VBO[3]);
        glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(tempvec), &tempvec);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
    }
    else if(clickwho>=0 && clickwho<=3){//此时应该改变矩形
        double xpos,ypos;
        glfwGetCursorPos(window,&xpos,&ypos);
        if(clickwho==0){rec.xleft=xpos; rec.ybottom=ypos;}//"左下"
        else if(clickwho==1){rec.xright=xpos; rec.ytop=ypos;}//"右上"
        else if(clickwho==2){rec.xleft=xpos; rec.ytop=ypos;}//"左上"
        else if(clickwho==3){rec.xright=xpos; rec.ybottom=ypos;}//"右下"
        float tempvec[]={transX(rec.xleft),transY(rec.ybottom),1.0f,
                         transX(rec.xleft),transY(rec.ytop),1.0f,
                         transX(rec.xright),transY(rec.ybottom),1.0f,
                         transX(rec.xright),transY(rec.ytop),1.0f
        };
        glBindBuffer(GL_ARRAY_BUFFER, VBO[0]);
        glBufferSubData(GL_ARRAY_BUFFER,0,sizeof(tempvec),&tempvec);
        glBindBuffer(GL_ARRAY_BUFFER, 0);

        //裁剪
        if(DrawPolyEnd && DrawRecEnd) SutherlandHodgeman(polyPoint,cropPoint,rec);
    }
    else if(clickwho>=4){//移动多边形
        double xpos,ypos;
        glfwGetCursorPos(window,&xpos,&ypos);
        float tempvec[]={transX(xpos),transY(ypos),1.0f};
        glBindBuffer(GL_ARRAY_BUFFER, VBO[1]);
        glBufferSubData(GL_ARRAY_BUFFER, (clickwho-4)*3*sizeof(float), sizeof(tempvec), &tempvec);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        polyPoint[clickwho-4]=Point(xpos,ypos);

        //裁剪
        if(DrawPolyEnd && DrawRecEnd) SutherlandHodgeman(polyPoint,cropPoint,rec);
    }
}
void framebuffer_size_callback(GLFWwindow* window, int width, int height){
    glViewport(0, 0, width, height);
}
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods){
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
        glfwSetWindowShouldClose(window, GL_TRUE);
    else if (key == GLFW_KEY_R && action == GLFW_PRESS) {//如果按下"R",画裁剪窗口
        if(!DrawPoly){//此时不是属于画多边形时刻
            if(!DrawRec){//开始画窗口
                cout<<"You press ‘R’ to start drawing the crop window"<<endl;
                double xpos,ypos;
                glfwGetCursorPos(window,&xpos,&ypos);
                rec.xleft=xpos; rec.ybottom=ypos;
                clickwho=1;//此时的状态和点击了"右下"是一样的
                DrawRec=true;
            }
            else {//结束画窗口
                cout<<"You press ‘R’, drawing the cropping window ends"<<endl;
                clickwho=-1;
                DrawRec=false; DrawRecEnd=true;
                //裁剪
                if(DrawPolyEnd && DrawRecEnd) SutherlandHodgeman(polyPoint,cropPoint,rec);
            }
        }
    }
    else if (key == GLFW_KEY_P && action == GLFW_PRESS) {//如果按下"P",画多边形
        if(!DrawRec){//此时不是属于画裁剪窗口时刻
            if(!DrawPoly){//开始画多边形
                polyPoint.clear(); cropPoint.clear();
                cout<<"You press ‘P’ to start drawing the polygon"<<endl;
                DrawPoly=true;
                double xpos,ypos;
                glfwGetCursorPos(window,&xpos,&ypos);
                float tempvec[]={transX(xpos),transY(ypos),1.0f};
                glBindBuffer(GL_ARRAY_BUFFER, VBO[1]);
                glBufferSubData(GL_ARRAY_BUFFER, polyPoint.size()*3*sizeof(float), sizeof(tempvec), &tempvec);
                glBindBuffer(GL_ARRAY_BUFFER, 0);
                polyPoint.emplace_back(xpos,ypos);
            }
            else {//结束画多边形
                cout<<"You press ‘P’ and the polygon drawing ends"<<endl;
                //处理临时边
                templine[0]=Point(0,0);
                templine[1]=Point(0,0);
                float tempvec1[]={0.0f,0.0f,1.0f,
                                 0.0f,0.0f,1.0f
                };
                glBindBuffer(GL_ARRAY_BUFFER, VBO[3]);
                glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(tempvec1), &tempvec1);
                glBindBuffer(GL_ARRAY_BUFFER, 0);
                DrawPoly=false; DrawPolyEnd=true;

                //裁剪
                if(DrawPolyEnd && DrawRecEnd) SutherlandHodgeman(polyPoint,cropPoint,rec);
            }
        }
    }
}
void mouse_button_callback(GLFWwindow* window, int button, int action, int mods){
    if(button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS){//鼠标点击了左键
        double xpos,ypos;
        glfwGetCursorPos(window,&xpos,&ypos);
        if(DrawPoly){//如果此时正在画多边形
            float tempvec[]={transX(xpos),transY(ypos),1.0f};
            glBindBuffer(GL_ARRAY_BUFFER, VBO[1]);
            glBufferSubData(GL_ARRAY_BUFFER, polyPoint.size()*3*sizeof(float), sizeof(tempvec), &tempvec);
            glBindBuffer(GL_ARRAY_BUFFER, 0);
            polyPoint.emplace_back(xpos,ypos);
        }
        else if(!DrawRec){//如果此时没有画矩形
            if(clickwho!=-1) clickwho=-1;//移动中点击了鼠标,肯定是要结束了
            else if(distant({rec.xleft,rec.ybottom},{xpos,ypos})) clickwho=0;//点击矩形"左下"
            else if(distant({rec.xright,rec.ytop},{xpos,ypos})) clickwho=1;//点击矩形"右上"
            else if(distant({rec.xleft,rec.ytop},{xpos,ypos})) clickwho=2;//点击矩形"左上"
            else if(distant({rec.xright,rec.ybottom},{xpos,ypos})) clickwho=3;//点击矩形"右下"
            else {
                for (int i=0; i<polyPoint.size(); i++){//点击的点是多边形上的点
                    if(distant({xpos,ypos},polyPoint[i])){
                        clickwho=i+4; return;
                    }
                }
                //没有点击到特殊的点,不进行任何操作
            }
        }

    }
}
  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值