通过前面几节的学习,我们已经有能力实现一些高级的图形绘制功能。这一节我们给大家示例折线图、直方图和饼状图的绘制程序。
我们先看看要实现的效果。
下面我们默认大家已经掌握了前面的知识,并会把讲解的重点放在高级功能上。
#include <gl/glut.h>
GLsizei winWidth = 600, winHeight = 500;
GLubyte label[36] = { 'J', 'a', 'n', 'F', 'e', 'b', 'M', 'a', 'r',
'A', 'p', 'r', 'M', 'a', 'y', 'J', 'u', 'n',
'J', 'u', 'l', 'A', 'u', 'g', 'S', 'e', 'p', 'O', 'c', 't', 'N', 'o', 'v', 'D', 'e', 'c' };//月份标签数组
GLint dataValue[12] = { 420, 342, 324, 310, 262, 185, 190, 196, 217, 240, 312, 438 };//折线图数值项,对应12个月
void init(void)
{
glClearColor(1.0, 1.0, 1.0, 1.0);
glMatrixMode(GL_PROJECTION);
gluOrtho2D(0.0, 600.0, 0.0, 500.0);
}
void linegraph(void)
{
GLint xRaster = 25, yRaster = 150;//初始光标位置
GLint month, k;
GLint x = 30;
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0.0, 0.0, 1.0);
glBegin(GL_LINE_STRIP);
for (k = 0; k < 12; k++)
glVertex2i(x + k * 50, dataValue[k]);//绘制折线
glEnd();
glColor3f(1.0, 0.0, 0.0);
for (k = 0; k < 12;k++)
{
glRasterPos2i(xRaster + k * 50, dataValue[k] - 4);光标跟踪折线节点
glutBitmapCharacter(GLUT_BITMAP_9_BY_15, '*');//折线节点上绘制的‘*’
}
glColor3f(0.0, 0.0, 0.0);
xRaster = 20;//重置光标位置
for (month = 0; month < 12;month++)
{
glRasterPos2i(xRaster, yRaster);
for (k = 3 * month; k < 3 * month + 3;k++)
{
glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, label[k]);//标注月份
}
xRaster += 50;
}
glFlush();
}
void winReshapeFcn(GLint newWidth, GLint newHeight)
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, GLdouble(newWidth), 0.0, GLdouble(newHeight));
glClear(GL_COLOR_BUFFER_BIT);
}
void main(int argc, char**argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowPosition(100, 100);
glutInitWindowSize(winWidth, winHeight);
glutCreateWindow("Line Chart Data Plot");
init();
glutDisplayFunc(linegraph);
glutReshapeFunc(winReshapeFcn);
glutMainLoop();
}
大家可以看到,看似复杂的折线图,我们通过将其元素分解为折线,‘*’节点,月份标注分别进行绘制,整个过程非常简单。在这里给大家提出一个小问题,如果将光标位置变量设置为全局变量,程序结果会出现什么变化呢?
直方图的代码也是同样非常简单,下面给大家提供一个示例。
void barChart(void)
{
GLint xRaster = 20, yRaster = 150;
GLint month, k;
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0, 0.0, 0.0);
for (k = 0; k < 12; k++)
{
glRecti(20 + k * 50, 165, 40 + k * 50, dataValue[k]);//直方图条状图
}
glColor3f(0.0, 0.0, 0.0);
for (month = 0; month < 12;month++)
{
glRasterPos2i(xRaster, yRaster);
for (k = 3 * month; k < 3 * month + 3;k++)
{
glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, label[k]);//标注
}
xRaster += 50;
}
glFlush();
}
最后再给大家画一个简单的饼状图,大家可以试试自己去添加注释等信息。
#include <gl/glut.h>
#include <stdlib.h>
#include <math.h>
const GLdouble twoPi = 6.283185;
class scrPt{
public:
GLint x, y;
};
GLsizei winWidth = 400, winHeight = 300;
void init(void)
{
glClearColor(1.0, 1.0, 1.0, 1.0);
glMatrixMode(GL_PROJECTION);
gluOrtho2D(0.0, 200.0, 0.0, 150.0);
}
void circleMidpoint(scrPt mid, GLint r)//画圆函数
{
int i = 0;
glBegin(GL_LINE_LOOP);//通过细分曲线趋近圆形
int n = 120;
for (i = 0; i < n; i++)
{
glVertex2f(r*cos(twoPi / n*i) + mid.x, r*sin(twoPi / n*i) + mid.y);//圆上的连续点
}
glEnd();
}
void pieChart(void)
{
scrPt circCtr, piePt;
GLint radius = winWidth / 4;
GLdouble sliceAngle, previousSliceAngle = 0.0;
GLint k, nSlice = 12;
GLfloat dataValue[12] = { 10.0, 7.0, 13.0, 5.0, 13.0, 14.0, 3.0, 16.0, 5.0, 3.0, 17.0, 8.0 };//单月份数
GLfloat dataSum = 0.0;
circCtr.x = winWidth / 2;//圆心
circCtr.y = winHeight / 2;
circleMidpoint(circCtr, radius);//根据圆心和半径画圆
for (k = 0; k < nSlice;k++)
{
dataSum += dataValue[k];//计算总份数
}
for (k = 0; k < nSlice;k++)
{
sliceAngle = twoPi*dataValue[k] / dataSum + previousSliceAngle;//扇形边界线的角度
piePt.x = circCtr.x + radius*cos(sliceAngle);//扇形边界线端点的坐标
piePt.y = circCtr.y + radius*sin(sliceAngle);
glBegin(GL_LINES);
glVertex2i(circCtr.x, circCtr.y);
glVertex2i(piePt.x, piePt.y);//绘制扇形边界线
glEnd();
previousSliceAngle = sliceAngle;//更新扇形边界线角度
}
}
void displayFcn(void)
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0.0, 0.0, 1.0);
pieChart();
glFlush();
}
void winReshapeFcn(GLint newWidth, GLint newHeight)
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0, GLdouble(newWidth), 0.0, GLdouble(newHeight));
glClear(GL_COLOR_BUFFER_BIT);
winWidth = newWidth;
winHeight = newHeight;
}
void main(int argc, char**argv)
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutInitWindowPosition(100, 100);
glutInitWindowSize(winWidth, winHeight);
glutCreateWindow("Line Chart Data Plot");
init();
glutDisplayFunc(displayFcn);
glutReshapeFunc(winReshapeFcn);
glutMainLoop();
}