初学opengl(二)画散点图

来源:互联网 发布:java窗口程序实例 编辑:程序博客网 时间:2024/06/11 22:54

作为一个计算机学院不合格的学生,大学都快过完了还处于编程小白的状态,各种知识都不会。作为初学的练习,自己写了一个简单的散点图。有些写的不对的地方或者可以改进的地方,还请大家指正。


我是这么考虑的,opengl画出的窗口是一个空白的白板。散点图一般都是在一个坐标系中显示一些点。于是,首先要自己建立坐标系。然后随机生成一部分点,将这些点映射到建好的坐标系中,画出来。便是完成了。

1.  画直线。

opengl画直线的语句是

glBegin(GL_LINE);     glVertex3f(25.0f,160.0f,0.0f);     glVertex3f(225.0f,160.0f,0.0f); glEnd();
glBegin()的参数如果是GL_LINE,则只能画一条线,glBegin和glEnd之间的两句话用于指定直线的起点和终点。

glBegin()的参数如果是GL_LINES,则可以画多条直线,glBegin和glEnd之间可以有多个用于指定直线起点和终点的语句,但是必须为偶数个点。因为每两个点确定一条直线。

glVertex3f(),毫无疑问,是用来指定坐标的,这个例子是用float类型的数据来表示坐标,3是指三维坐标。还可以是glVertex2i(),这样的话是二维坐标,用int型数据表示坐标。

参考的这篇文章http://blog.csdn.net/ch_soft/article/details/7209208


2. 画用于表示坐标系的那些直线。

opengl的初始化中有这样一句话

gluOrtho2D(0.0, 200.0, 0.0, 150.0); 
gluOrtho2D建立一个二维图像投影矩阵,就是把opengl画出的这个窗口投影到一个坐标系中,gluOrtho2D的四个参数分别表示左右上下的大小,即gluOrtho2D(Xmin, Xmax, Ymin, Ymax). 这就是为什么在缩放所画窗口的时候,里边的元素还能保持相对的大小和位置。


以上在学习过程中参考http://www.cnblogs.com/fly1012/archive/2010/06/27/1766089.html

这样的话,向下就比较简单了。我先画了x轴和y轴两条长的线,然后分别两条短线做出箭头的效果。将每个轴划分成五段,事先计算好坐标,然后用for循环来画短线。再在分出的地方分别标上数值。这样,一个坐标系就建好了。

其实我一开始都是一条一条短线画的,手动计算的坐标。写完了才发现,其实这是重复的工作,可以用for循环来完成。又改程序。可是这个时候我连一维数据怎么用都忘了,其实也有很大一部分原因是因为没写过,所以不敢写。看过别人的例子对照着才敢写http://c.biancheng.net/cpp/biancheng/view/151.html


3. 在opengl中标数值。

opengl是不提供直接显示文字的功能,要调用windows中的字库,自己写函数。搜了很多博客,都写的很麻烦,就一个看着还比较简单的,

我把代码拿过来直接用的,还没有很明白。http://blog.sina.com.cn/s/blog_4ff085000100devp.html

#define MAX_CHAR        128void drawString(const char* str) {    static int isFirstCall = 1;    static GLuint lists;    if( isFirstCall ) { // 如果是第一次调用,执行初始化         // 为每一个ASCII字符产生一个显示列表         isFirstCall = 0;         // 申请MAX_CHAR个连续的显示列表编号         lists = glGenLists(MAX_CHAR);         // 把每个字符的绘制命令都装到对应的显示列表中         wglUseFontBitmaps(wglGetCurrentDC(), 0, MAX_CHAR, lists);     }     // 调用每个字符对应的显示列表,绘制每个字符    for(; *str!='\0'; ++str)         glCallList(lists + *str);}

把数值标好以后,我又发现,每个轴上的五个数值,也是可以通过for循环来写的。那么问题又来了,drawString的参数是char数组,而在for循环中必须能够根据循环而变化,即可以通过计算得到。于是先计算出应该打印出的数字,然后再把数字转成字符串,找到了这个http://blog.csdn.net/touzani/article/details/1623850 还需要加一句using namespace std就可以了。

#include <sstream>#Include <string>using namespace std;string num2str(int i){        stringstream ss;        ss<<i;        return ss.str();}

这样的话是把数字转成了字符串,而刚刚的drawString的参数是char数组,于是还需要一步转换,http://blog.csdn.net/harry_lyc/article/details/6010167 因为字符串转char*比较简单,所以把drawString的参数改成string类型。

void drawString(string strn) {static int isFirstCall = 1;static GLuint lists;const char *str = strn.c_str();        //其他同上......}
</pre><p>打印数值的语句</p><p><pre name="code" class="cpp">for (i = 0; i < k; i++){s = num2string(20 * (i + 1));glRasterPos2f(x[i] - 2.0f, 5.0f);drawString(s);}

4. 生成散点

因为画出的坐标系,x轴范围是[0,100],y轴范围是[0,50],所以生成的点要在这个范围内。使用C++中的rand()函数生成。

参考http://blog.csdn.net/lzyzuixin/article/details/3086076 rand()函数如果种子相同,则得到的数值也相同。这样正好避免了opengl不断刷新过程中点不断闪烁。

int x_data[n], y_data[n];for (i = 0; i < n; i++){x_data[i] = 1 + rand() % 101;  //x坐标的范围在【1,100】y_data[i] = 1 + rand() % 51;   //y坐标的范围在【1,50】}

因为刚刚生成的是自己构造的坐标系中的点,要在图中画出来,就要把他们变换到之前的gluOrtho2D建立一个二维图像坐标中。以x轴为例,x轴长200,两边都空出10个单位,然后划分成100份,则要乘以这个系数(200-2*10)/100.左边空出来10,还要加上去。y轴同理。

        float x_real[n], y_real[n];for (i = 0; i < n; i++){x_real[i] = x_data[i] * (200 - 2 * x0) / k / 20 + x0;y_real[i] = y_data[i] * (150 - 2 * y0) / k / 10 + y0;}


5. 把这些点画出来

画点的基本语句如下

        GLfloat pointSize = 5.0f;glPointSize(pointSize);glBegin(GL_POINTS);            glVertex2i(10, 180);        glEnd();


完整版代码如下:

#define GLUT_DISABLE_ATEXIT_HACK #define MAX_CHAR        128#include <gl/glut.h>#include <stdio.h>#include <string.h>#include <sstream>#pragma comment(lib,"glut32.lib")using namespace std;string num2string(int i){stringstream ss;ss << i;return ss.str();}void drawString(string strn) {static int isFirstCall = 1;static GLuint lists;const char *str = strn.c_str();if (isFirstCall) { // 如果是第一次调用,执行初始化// 为每一个ASCII字符产生一个显示列表isFirstCall = 0;// 申请MAX_CHAR个连续的显示列表编号lists = glGenLists(MAX_CHAR);// 把每个字符的绘制命令都装到对应的显示列表中wglUseFontBitmaps(wglGetCurrentDC(), 0, MAX_CHAR, lists);}// 调用每个字符对应的显示列表,绘制每个字符for (; *str != '\0'; ++str)glCallList(lists + *str);}void createCoordinate(GLfloat x0, GLfloat y0){int i = 0, k = 5;GLfloat x[5], y[5];for (i = 0; i < k; i++){x[i] = x0 + (200 - 2 * x0) / k * (i + 1);y[i] = y0 + (150 - 2 * y0) / k * (i + 1);}//设置颜色glClear(GL_COLOR_BUFFER_BIT);glColor3f(0.0f, 0.0f, 0.0f);GLfloat lineWidth = 2.0f;glLineWidth(lineWidth);glBegin(GL_LINES);glVertex2f(10.0f, 5.0f);   //纵轴glVertex2f(10.0f, 147.0f);glVertex2f(9.0f, 143.0f);glVertex2f(10.0f, 147.0f);glVertex2f(11.0f, 143.0f);glVertex2f(10.0f, 147.0f);glVertex2f(5.0f, 10.0f);   //横轴glVertex2f(197.0f, 10.0f);glVertex2f(193.0f, 9.0f);glVertex2f(197.0f, 10.0f);glVertex2f(193.0f, 11.0f);glVertex2f(197.0f, 10.0f);glEnd();lineWidth = 1.0f;glLineWidth(lineWidth);glBegin(GL_LINES);for (i = 0; i < k; i++){glVertex2f(x[i], x0 + 3.0);glVertex2f(x[i], x0);}for (i = 0; i < k; i++){glVertex2f(y0 + 3.0, y[i]);glVertex2f(y0, y[i]);}glEnd();string s;for (i = 0; i < k; i++){s = num2string(20 * (i + 1));glRasterPos2f(x[i] - 2.0f, 5.0f);drawString(s);s = num2string(10 * (i + 1));glRasterPos2f(5.0f, y[i] - 1.0f);drawString(s);}}void scatter(GLfloat x0, GLfloat y0){const int n = 100;int i, k=5;//随机生成n个点的x坐标和y坐标int x_data[n], y_data[n];for (i = 0; i < n; i++){x_data[i] = 1 + rand() % 101;  //x坐标的范围在【1,100】y_data[i] = 1 + rand() % 51;   //y坐标的范围在【1,50】}//由坐标系坐标转成这个图的坐标float x_real[n], y_real[n];for (i = 0; i < n; i++){x_real[i] = x_data[i] * (200 - 2 * x0) / k / 20 + x0;y_real[i] = y_data[i] * (150 - 2 * y0) / k / 10 + y0;}GLfloat pointSize = 5.0f;glPointSize(pointSize);glBegin(GL_POINTS);glClear(GL_COLOR_BUFFER_BIT);glColor3f(1.0f, 0.0f, 0.0f);for (i = 0; i < n/2; i++){glVertex2f(x_real[i], y_real[i]);}glClear(GL_COLOR_BUFFER_BIT);glColor3f(0.0f, 0.0f, 1.0f);for (i = n / 2; i < n; i++){glVertex2f(x_real[i], y_real[i]);}glEnd();}void Display(){GLfloat x0 = 10.0f, y0 = 10.0f;createCoordinate(x0, y0);scatter(x0, y0);glFlush();}void Initial(){glClearColor(1.0f, 1.0f, 1.0f, 1.0f);  //清屏颜色glMatrixMode(GL_PROJECTION);gluOrtho2D(0.0, 200.0, 0.0, 150.0);   //投影到裁剪窗大小:世界}int main(int argc, char *argv[]){glutInit(&argc, argv);glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);glutInitWindowSize(800, 500);glutInitWindowPosition(300, 300);glutCreateWindow("Scatter Plot");glutDisplayFunc(Display);Initial();glutMainLoop();return 0;}

最终效果图如下:



0 0