一、多边形分割成三角形
这里我们直接使用第三方库:
- Efficient Polygon Triangulation,进入复制源代码即可使用。
- GPC – General Polygon Clipper library,很遗憾这个项目在20年8月停止了支持,还是很专业的一个图形分割项目,可以自行查找是否还有可以源码。
EPT方法:
函数按顺时针取3个点判断是否为凸角(同时其他点不得在其内),如果条件满足则去掉这三点中的中间点,切除出一个三角形,剩下多边形继续切割直到完成。
- rea函数:计算多边形面积。如果计算的多边形面积是负的则说明是逆时针取点。
- InsideTriangle函数:判断P是否在ABC三角形内(用的向量差积法,其实也是面积法)。
- Snip函数:判断三点条件。
- 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;
}