总结出以下经验:
- 想清空屏幕时,要用双缓冲模式,因为单缓冲模式清空时没反应,只会在下一次绘制时清空原来的图形。双缓冲模式可以立即清空屏幕。
- glutMotionFunc() 函数和 glutPassiveMotionFunc() 函数不同。后者在鼠标按键按下后不再跟踪鼠标位置,前者是在鼠标按下后松开前跟踪鼠标位置。
代码:
#include <windows.h>
#ifdef __APPLE__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif
#include <bits/stdc++.h>
using namespace std;
#include <stdlib.h>
const int maxn = 1e5+5;
const double PI = acos(-1.0);
static GLsizei iMode = 1;
static GLboolean IsFill = false;
int iPointNum = 0;
double x1=0, x2=0, y1=0, y2=0;
int winWidth = 400, winHeight = 300;
void Initial(){
glClearColor(1, 1, 1, 1); // 背景颜色
}
void ChangeSize(int w, int h){
winWidth = w; winHeight = h;
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0, winWidth, 0, winHeight);
}
namespace Draw_Graph{
void DrawLine(double x1, double y1, double x2, double y2){
glBegin(GL_LINES);
glVertex2f(x1, y1);
glVertex2f(x2, y2);
glEnd();
}
void DrawCircle(double x1, double y1, double x2, double y2){
double o_x = (x1+x2)/2, o_y = (y1+y2)/2;
double r = sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
glBegin(GL_POLYGON);
int n = 360;
for(int i=0; i<n; i++)
glVertex2f(r*cos(2*i*PI/n)+o_x, r*sin(2*i*PI/n)+o_y);
glEnd();
}
void DrawRectangle(double x1, double y1, double x2, double y2){
glRectf(x1, y1, x2, y2);
}
void DrawTriangle(double x1, double y1, double x2, double y2){
glBegin(GL_POLYGON);
glVertex2f(x1, y1);
glVertex2f(x2, y2);
glVertex2f(2*x1-x2, y2);
glEnd();
}
}
using namespace Draw_Graph;
struct Node{
double x1, y1, x2, y2; int type;
}graph[maxn];
int tot = 0;
void Display(){
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1, 0, 0);
glPolygonMode(GL_FRONT, GL_FILL);
for(int i=1; i<=tot; i++){
switch(graph[i].type){
case 1: DrawLine(graph[i].x1, graph[i].y1, graph[i].x2, graph[i].y2); break;
case 2: DrawCircle(graph[i].x1, graph[i].y1, graph[i].x2, graph[i].y2); break;
case 3: DrawRectangle(graph[i].x1, graph[i].y1, graph[i].x2, graph[i].y2); break;
case 4: DrawTriangle(graph[i].x1, graph[i].y1, graph[i].x2, graph[i].y2); break;
}
}
if(iPointNum == 1){
switch(iMode){
case 1: DrawLine(x1, y1, x2, y2); break;
case 2: DrawCircle(x1, y1, x2, y2); break;
case 3: DrawRectangle(x1, y1, x2, y2); break;
case 4: DrawTriangle(x1, y1, x2, y2); break;
}
}
glutSwapBuffers();
}
void MousePlot(GLint button, GLint action, GLint xMouse, GLint yMouse){
if(button == GLUT_LEFT_BUTTON && action == GLUT_DOWN){
if(iPointNum == 0 || iPointNum == 2){
iPointNum = 1;
x1 = xMouse; y1 = winHeight - yMouse;
}
}
if(button == GLUT_LEFT_BUTTON && action == GLUT_UP){
iPointNum = 2;
x2 = xMouse; y2 = winHeight - yMouse;
if(!(x1==x2 && y1==y2))
graph[++tot] = Node{x1, y1, x2, y2, iMode};
glutPostRedisplay();
}
}
void MotionMouse(GLint xMouse, GLint yMouse){ //跟踪鼠标按键按下后松开前的位置
if(iPointNum == 1){
x2 = xMouse; y2 = winHeight - yMouse;
glutPostRedisplay();
}
}
void ProcessMenu(int value){
if(value<5) iMode = value;
if(value == 5) tot = 0;
glutPostRedisplay();
}
int main(int argc, char **argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); //清空屏幕时可以立刻显示出效果
glutInitWindowSize(400, 300);
glutInitWindowPosition(100, 100);
glutCreateWindow("橡皮筋技术");
int nGraphMenu = glutCreateMenu(ProcessMenu);
glutAddMenuEntry("直线", 1);
glutAddMenuEntry("圆", 2);
glutAddMenuEntry("矩形", 3);
glutAddMenuEntry("三角形", 4);
glutAddMenuEntry("清屏", 5);
glutAttachMenu(GLUT_RIGHT_BUTTON);
glutDisplayFunc(Display);
glutReshapeFunc(ChangeSize);
glutMouseFunc(MousePlot);
glutMotionFunc(MotionMouse);
Initial();
glutMainLoop();
}