一、 实验要求
1.使用鼠标选择窗口内任意位置画N个等腰三角形。
2.要画的三角形大小及颜色通过右键弹出菜单选定(任选M个颜色)。
3.设定三角形绕其中心旋转的开始、停止、旋转方向的功能键,并通过相应按键控制三角形旋转。
二、 完成情况
由于没能实现在鼠标任意选定的位置画三角形并且让其旋转(分开很好实现),于是便简化了一下,在固定位置画N个三角形,并让其绕中心点旋转。可以通过右键菜单退出、改变大小和改变颜色。同时还可以通过空格键控制旋转的开始和停止,通过C字母键控制旋转的方向(顺时针或逆时针)。
三、 各功能介绍
1. 画等腰三角形并旋转:
为了方便确定三角形的中点,我画的是特殊的等腰三角形——等边三角形。设其一顶点的坐标为 ( cos(spin) , sin(spin) ),则另外两点的坐标为 ( cos(spin-120) ,sin(spin-120) ) 和 (cos(spin+120) , sin(spin+120) ),其中spin为转过的角度。旋转时只要改变 spin 的值即可。
代码:
void display()
{
glClear (GL_COLOR_BUFFER_BIT);
glPushMatrix();//将当前矩阵压栈(保存现场)
glBegin(GL_TRIANGLES);
//设顶点坐标为(cos(spin),sin(spin)),其他两点坐标可由spin +/- 120°后得到
glVertex2f(cos(DR*spin)*size+30,sin(DR*spin)*size+30);
glVertex2f(cos(DR*(spin+120))*size+30,sin(DR*(spin+120))*size+30);
glVertex2f(cos(DR*(spin-120))*size+30,sin(DR*(spin-120))*size+30);
glVertex2f(cos(DR*spin)*size+10,sin(DR*spin)*size-30);
glVertex2f(cos(DR*(spin+120))*size+10,sin(DR*(spin+120))*size-30);
glVertex2f(cos(DR*(spin-120))*size+10,sin(DR*(spin-120))*size-30);
glVertex2f(cos(DR*spin)*size-20,sin(DR*spin)*size+20);
glVertex2f(cos(DR*(spin+120))*size-20,sin(DR*(spin+120))*size+20);
glVertex2f(cos(DR*(spin-120))*size-20,sin(DR*(spin-120))*size+20);
glVertex2f(cos(DR*spin)*size-35,sin(DR*spin)*size-20);
glVertex2f(cos(DR*(spin+120))*size-35,sin(DR*(spin+120))*size-20);
glVertex2f(cos(DR*(spin-120))*size-35,sin(DR*(spin-120))*size-20);
glEnd();
glPopMatrix();
glutSwapBuffers();//双缓存技术的函数,作用为交换两个缓冲区的指针
}
效果:
2. 右键弹出菜单:
要设置一个退出选项以及改变大小子菜单和颜色子菜单,直接利用代码写出即可。改变大小时,可以让全局变量 size 增大或缩小一倍。改变颜色时可以用glColor3f 函数将前景色改变为相应的颜色。
代码:
/*右键菜单*/
/*大小子菜单*/
int sub_menu1= glutCreateMenu(size_menu);
glutAddMenuEntry("Increasesize",2);
glutAddMenuEntry("Decreasesize",3);
/*颜色子菜单*/
intsub_menu2 = glutCreateMenu(color_menu);
glutAddMenuEntry("red",4);
glutAddMenuEntry("blue",5);
glutAddMenuEntry("yellow",6);
/*主菜单*/
glutCreateMenu(main_menu);
glutAddMenuEntry("Quit",1);
glutAddSubMenu("Size",sub_menu1); //添加子菜单
glutAddSubMenu("Color",sub_menu2); //添加子菜单
glutAttachMenu(GLUT_RIGHT_BUTTON);
效果:
3. 键盘控制旋转的开始停止以及旋转方向:
分别设了两个全局变量:flag和dir,取值为 0 或 1,按键一次值就随之改变一次。分别用来控制旋转的开始和停止以及旋转的方向。旋转的开始和结束可以用glutIdleFunc函数传入不同的参数实现。旋转的方向可以通过改变 spin 角度增加还是减少来实现。
代码:
void myKey(unsigned char key,intx,int y)
{ //键盘
if(key=='')
{//如果按下空格键,则控制旋转和暂停
flag^=1;
if(flag)glutIdleFunc(spinDisplay);
elseglutIdleFunc(NULL);
}
//按下C字母改变旋转方向
elseif(key=='c'||key=='C') dir^=1;
}
四、 难点分析
最困难的部分就是用鼠标点击N个位置画三角形和三角形旋转分别都好实现,但是一旦放到一起就会出各种错误,只能简化了一下要求。
其次就是三角形绕其中心旋转的实现,一开始是从网上找的一个函数,调用之后可以绕x、y、z轴旋转,但是效果不好,不是绕中心旋转,后来自己模仿PPT上面正方形旋转的案例自己改动了一下。
其他的之前的代码中都有类似的案例,不算太难,模仿一下就可以了。
五、 总代码
#include <stdlib.h>
#include <GL/glut.h>
#include <math.h>
#define DR 3.14159/180.0
int N=4;
int num=0;
int dir=1;
int flag=1;
double size=10;
static GLfloat spin = 0.0;
GLfloat x, y;
int doubleb;
void triangle(GLfloat x,GLfloat y)
{ //画三角形,实际没用到……
glPushMatrix();//将当前矩阵压栈(保存现场)
glBegin(GL_TRIANGLES);
glVertex2f(x,y+size/2);
glVertex2f(x-size/2,y-size/2);
glVertex2f(x+size/2,y-size/2);
glEnd();
glPopMatrix();
glutSwapBuffers();//双缓存技术的函数,作用为交换两个缓冲区的指针
}
void display()
{
glClear (GL_COLOR_BUFFER_BIT);
glPushMatrix();//将当前矩阵压栈(保存现场)
glBegin(GL_TRIANGLES);
//设顶点坐标为(cos(spin),sin(spin)),其他两点坐标可由spin +/- 120°后得到
glVertex2f(cos(DR*spin)*size+30,sin(DR*spin)*size+30);
glVertex2f(cos(DR*(spin+120))*size+30,sin(DR*(spin+120))*size+30);
glVertex2f(cos(DR*(spin-120))*size+30,sin(DR*(spin-120))*size+30);
glVertex2f(cos(DR*spin)*size+10,sin(DR*spin)*size-30);
glVertex2f(cos(DR*(spin+120))*size+10,sin(DR*(spin+120))*size-30);
glVertex2f(cos(DR*(spin-120))*size+10,sin(DR*(spin-120))*size-30);
glVertex2f(cos(DR*spin)*size-20,sin(DR*spin)*size+20);
glVertex2f(cos(DR*(spin+120))*size-20,sin(DR*(spin+120))*size+20);
glVertex2f(cos(DR*(spin-120))*size-20,sin(DR*(spin-120))*size+20);
glVertex2f(cos(DR*spin)*size-35,sin(DR*spin)*size-20);
glVertex2f(cos(DR*(spin+120))*size-35,sin(DR*(spin+120))*size-20);
glVertex2f(cos(DR*(spin-120))*size-35,sin(DR*(spin-120))*size-20);
glEnd();
glPopMatrix();
glutSwapBuffers();//双缓存技术的函数,作用为交换两个缓冲区的指针
}
void spinDisplay (void)
{ //角度变化
if(dir) spin = spin + 0.05;
else spin=spin - 0.05;
if (spin > 360.0) spin = spin - 360.0;
glutSetWindow(doubleb);
glutPostRedisplay();
}
void myinit ()
{ //初始化
glViewport(0,0,500,500);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0,(GLdouble)500,0.0,(GLdouble)500,-1.0,1.0);
glMatrixMode(GL_MODELVIEW);
glClearColor(0.0,0.0,0.0,1.0);
glColor3f(1.0,1.0,1.0); //前景色为白色
}
void myMouse(int btn,int state,int x,int y)
{ //鼠标
if(btn==GLUT_LEFT_BUTTON && state==GLUT_UP)
{//点击鼠标左键画三角形,但没起到作用
if(num<N) triangle(x,y);
num++;
}
}
void myKey(unsigned char key,int x,int y)
{ //键盘
if(key==' ')
{ //如果按下空格键,则控制旋转和暂停
flag^=1;
if(flag) glutIdleFunc(spinDisplay);
else glutIdleFunc(NULL);
}
//按下C字母改变旋转方向
else if(key=='c'||key=='C') dir^=1;
}
void myReshape(int w, int h)
{
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if (w <= h)
glOrtho (-50.0, 50.0, -50.0*(GLfloat)h/(GLfloat)w,
50.0*(GLfloat)h/(GLfloat)w, -10.0, 10.0);
else
glOrtho (-50.0*(GLfloat)w/(GLfloat)h,
50.0*(GLfloat)w/(GLfloat)h, -50.0, 50.0, -10.0, 10.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity ();
}
void myTimer(int v)
{
glutSetWindow(doubleb);
glutPostRedisplay();
glutTimerFunc(100, myTimer, v);
}
void main_menu(int id)
{ //主菜单
switch(id)
{
case 1: exit(0);
break;
}
}
void size_menu(int id)
{ //大小菜单
switch(id)
{
case 2: size = 2*size;
break;
case 3: if(size>1) size = size/2;
break;
}
}
void color_menu(int id)
{ //颜色菜单
switch(id)
{
case 4: glColor3f(1.0,0.0,0.0); //红色
break;
case 5: glColor3f(0.0,0.0,1.0); //蓝色
break;
case 6: glColor3f(1.0,1.0,0.0); //黄色
break;
}
}
int main(int argc, char** argv)
{
glutInit(&argc,argv);
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(800,800);
glutInitWindowPosition(0,0);
doubleb=glutCreateWindow("Triangle");
/*右键菜单*/
/*大小子菜单*/
int sub_menu1 = glutCreateMenu(size_menu);
glutAddMenuEntry("Increase size",2);
glutAddMenuEntry("Decrease size",3);
/*颜色子菜单*/
int sub_menu2 = glutCreateMenu(color_menu);
glutAddMenuEntry("red",4);
glutAddMenuEntry("blue",5);
glutAddMenuEntry("yellow",6);
/*主菜单*/
glutCreateMenu(main_menu);
glutAddMenuEntry("Quit",1);
glutAddSubMenu("Size", sub_menu1); //添加子菜单
glutAddSubMenu("Color", sub_menu2); //添加子菜单
glutAttachMenu(GLUT_RIGHT_BUTTON);
myinit ();
glutDisplayFunc(display);
glutMouseFunc (myMouse); //鼠标事件
glutKeyboardFunc(myKey); //键盘事件
glutReshapeFunc (myReshape);
glutIdleFunc (spinDisplay);
glutTimerFunc(100,myTimer,60);
glutMainLoop();
return 0;
}