SWUST 计算机图形学 实验二 绘制交互式多边形

发布于 2021-07-03  42 次阅读


一、多边形分割成三角形

  这里我们直接使用第三方库:

  1.   Efficient Polygon Triangulation,进入复制源代码即可使用。
  2.   GPC – General Polygon Clipper library,很遗憾这个项目在20年8月停止了支持,还是很专业的一个图形分割项目,可以自行查找是否还有可以源码。

  EPT方法:
  函数按顺时针取3个点判断是否为凸角(同时其他点不得在其内),如果条件满足则去掉这三点中的中间点,切除出一个三角形,剩下多边形继续切割直到完成。

  1.   rea函数:计算多边形面积。如果计算的多边形面积是负的则说明是逆时针取点。
  2.   InsideTriangle函数:判断P是否在ABC三角形内(用的向量差积法,其实也是面积法)。
  3.   Snip函数:判断三点条件。
  4.   Process切割函数,当snip满足时,去除一个点,给多边形重新编号,取点向后移2位。

二、实验要求

  (1)使用鼠标在屏幕客户区绘制任意点数的多边形。要求使用橡皮筋技术 动态绘制每条边;鼠标移动过程中按下 Shi 键时可绘制垂直边或水平边;将多 边形的终点移动到多边形的起点时自动封闭多边形;在绘制多边形的过程中,状 态栏动态显示鼠标光标的位置坐标。
  (2)当开始绘制多边形时,更改鼠标光标为十字光标,多边形绘制完毕后 恢复为箭头光标。
  (3)多边形闭合后自动调用有效边表算法填充多边形内部区域。

三、源码(使用EPT完成)

#include <iostream>
#include <GL/glut.h>
#include <glm/glm.hpp>
#include <vector>
#include "triangulate.h"

/*---常量---*/
const int HEIGHT(800);
const int WIDTH(800);

/*---全局变量---*/
glm::vec2 StartPoint{ 0, 0 };
glm::vec2 EndPoint{ 0, 0 };
//绘线变量
std::vector<glm::vec2> VertexArray;
//顶点数组

/*---函数---*/
void Display();                                 //mian中显示子程序
void Reshape(int, int);                         //投影变换函数
void MouseMove(int, int);                       //鼠标移动事件
void MouseClick(int, int, int, int);            //鼠标点击事件
void DrawLine(int, int);                        //绘制直线
void DrawEdge();                                //绘制多边形的边
void DrawPolygon();                             //绘制多边形
Vector2dVector PolygonToTriangle();             //剪裁多边形

int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    //GLUT初始化

    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
    //双缓存模式

    glutInitWindowSize(WIDTH, HEIGHT);
    glutInitWindowPosition(50, 100);
    //窗口初始化

    glClearColor(0, 0, 0, 1);
    //窗口颜色

    glutCreateWindow("Draw and Filled Ploygon");

    glutDisplayFunc(Display);
    glutMouseFunc(MouseClick);
    glutReshapeFunc(Reshape);

    glutMainLoop();
}

void Display()
{
    glClear(GL_COLOR_BUFFER_BIT);
}

void Reshape(int w, int h)
{
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0, WIDTH, 0, HEIGHT);
}

void MouseMove(int x, int y)
{
    std::cout << "(" << x << ", " << y << ")" << std::endl;

    int slope = ((y - StartPoint.y) / (x - StartPoint.x));
    //斜率
    int Shift = glutGetModifiers();
    //获取特殊按键
    glm::vec2 GoalPoint{ 0,0 };
    if (GLUT_ACTIVE_SHIFT == Shift)
    {
        if (slope == 1)                     //斜率为1, 绘制 45°斜线
        {
            GoalPoint.x = x;
            GoalPoint.y = (HEIGHT - x);
        }
        else if (slope == -1)               //斜率为-1, 绘制 -45°斜线
        {
            GoalPoint.x = x;
            GoalPoint.y = x;
        }
        else if ((slope > -1) && (slope < 1))   //斜率在-1到1之间, 绘制 x 方向平行线
        {
            GoalPoint.x = x;
            GoalPoint.y = StartPoint.y;
        }
        else if ((slope < -1) || (slope > 1))   //斜率小于-1 或 大于1, 绘制 y 方向平行线
        {
            GoalPoint.x = StartPoint.x;
            GoalPoint.y = (HEIGHT - y);
        }
    }
    else if (GLUT_ACTIVE_SHIFT != Shift)
    {
        GoalPoint.x = x;
        GoalPoint.y = (HEIGHT - y);
    }
    DrawLine(GoalPoint.x, GoalPoint.y);
}

void MouseClick(int button, int state, int x, int y)
{
    if (GLUT_DOWN == state && GLUT_LEFT_BUTTON == button)
    {
        //左键按下:动态画线
        if (VertexArray.empty())
        {
            StartPoint.x = x;
            StartPoint.y = (HEIGHT - y);
            VertexArray.push_back(StartPoint);
        }
        //数组空:新建顶点存入数组,否则直接以多边形最后一个点作为顶点

        StartPoint.x = VertexArray.back().x;
        StartPoint.y = VertexArray.back().y;
        glutMotionFunc(MouseMove);
    }
    else if (GLUT_UP == state && GLUT_LEFT_BUTTON == button)
    {
        int Shift = glutGetModifiers();
        int slope = ((y - StartPoint.y) / (x - StartPoint.x));
        if (GLUT_ACTIVE_SHIFT == Shift)
        {
            if (slope == 1)                     //斜率为1, 绘制 45°斜线
            {
                EndPoint.x = x;
                EndPoint.y = (HEIGHT - x);
            }
            else if (slope == -1)               //斜率为-1, 绘制 -45°斜线
            {
                EndPoint.x = x;
                EndPoint.y = x;
            }
            else if ((slope > -1) && (slope < 1))   //斜率在-1到1之间, 绘制 x 方向平行线
            {
                EndPoint.x = x;
                EndPoint.y = StartPoint.y;
            }
            else if ((slope < -1) || (slope > 1))   //斜率小于-1 或 大于1, 绘制 y 方向平行线
            {
                EndPoint.x = StartPoint.x;
                EndPoint.y = (HEIGHT - y);
            }
        }
        else if (GLUT_ACTIVE_SHIFT != Shift)
        {
            EndPoint.x = x;
            EndPoint.y = (HEIGHT - y);
        }
        VertexArray.push_back(EndPoint);
        //终点存入

        StartPoint = glm::vec2{ 0,0 };
        EndPoint = glm::vec2{ 0,0 };
        //Clear

        DrawEdge();
    }
    else if (GLUT_DOWN == state && GLUT_RIGHT_BUTTON == button)
    {
        //右键把线段转换为多边形
        DrawPolygon();
    }
}

void DrawLine(int x, int y)
{
    DrawEdge();

    glColor3f(0, 1.0, 1.0);
    //颜色选择cyan

    glClear(GL_COLOR_BUFFER_BIT);
    //刷新缓冲区

    glLineWidth(1);
    //线条宽度:1个像素

    glBegin(GL_LINES);
    glVertex2i(StartPoint.x, StartPoint.y);
    glVertex2i(x, y);
    glEnd();
    //画线

    glutSwapBuffers();
}

void DrawEdge()
{
    glColor3f(0, 1.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glLineWidth(1);
    //基本设置同上

    glBegin(GL_LINE_STRIP);
    for (auto i : VertexArray)
    {
        glVertex2i(i.x, i.y);
    }
    glEnd();
    //画图

    glutSwapBuffers();
}

void DrawPolygon()
{
    glColor3f(0, 0.5, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glLineWidth(1);
    //基本设置同上

    Vector2dVector TriangleVertexArray = PolygonToTriangle();

    glBegin(GL_TRIANGLES);
    for (auto i : TriangleVertexArray)
    {
        glVertex2i(i.GetX(), i.GetY());
    }
    glEnd();
    //画图

    glutSwapBuffers();
}

Vector2dVector PolygonToTriangle()
{
    Vector2dVector PolygonVertexArray;
    Vector2dVector TriangleVertexArray;
    for (auto i : VertexArray)
    {
        Vector2d TheVertex(i.x, i.y);
        PolygonVertexArray.push_back(TheVertex);
    }

    Triangulate::Process(PolygonVertexArray, TriangleVertexArray);

    return TriangleVertexArray;
}