初学opengl(二)画散点图
2016-03-03 11:03
627 查看
作为一个计算机学院不合格的学生,大学都快过完了还处于编程小白的状态,各种知识都不会。作为初学的练习,自己写了一个简单的散点图。有些写的不对的地方或者可以改进的地方,还请大家指正。
我是这么考虑的,opengl画出的窗口是一个空白的白板。散点图一般都是在一个坐标系中显示一些点。于是,首先要自己建立坐标系。然后随机生成一部分点,将这些点映射到建好的坐标系中,画出来。便是完成了。
1. 画直线。
opengl画直线的语句是
glBegin()的参数如果是GL_LINES,则可以画多条直线,glBegin和glEnd之间可以有多个用于指定直线起点和终点的语句,但是必须为偶数个点。因为每两个点确定一条直线。
glVertex3f(),毫无疑问,是用来指定坐标的,这个例子是用float类型的数据来表示坐标,3是指三维坐标。还可以是glVertex2i(),这样的话是二维坐标,用int型数据表示坐标。
参考的这篇文章http://blog.csdn.net/ch_soft/article/details/7209208
2. 画用于表示坐标系的那些直线。
opengl的初始化中有这样一句话
以上在学习过程中参考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
把数值标好以后,我又发现,每个轴上的五个数值,也是可以通过for循环来写的。那么问题又来了,drawString的参数是char数组,而在for循环中必须能够根据循环而变化,即可以通过计算得到。于是先计算出应该打印出的数字,然后再把数字转成字符串,找到了这个http://blog.csdn.net/touzani/article/details/1623850 还需要加一句using
namespace std就可以了。
这样的话是把数字转成了字符串,而刚刚的drawString的参数是char数组,于是还需要一步转换,http://blog.csdn.net/harry_lyc/article/details/6010167 因为字符串转char*比较简单,所以把drawString的参数改成string类型。
4. 生成散点
因为画出的坐标系,x轴范围是[0,100],y轴范围是[0,50],所以生成的点要在这个范围内。使用C++中的rand()函数生成。
参考http://blog.csdn.net/lzyzuixin/article/details/3086076 rand()函数如果种子相同,则得到的数值也相同。这样正好避免了opengl不断刷新过程中点不断闪烁。
因为刚刚生成的是自己构造的坐标系中的点,要在图中画出来,就要把他们变换到之前的gluOrtho2D建立一个二维图像坐标中。以x轴为例,x轴长200,两边都空出10个单位,然后划分成100份,则要乘以这个系数(200-2*10)/100.左边空出来10,还要加上去。y轴同理。
5. 把这些点画出来
画点的基本语句如下
完整版代码如下:
最终效果图如下:
我是这么考虑的,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 128 void 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 , y_data ; 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 , y_real ; 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;
//
b5b7
申请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
, y_data
;
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
, y_real
;
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;
}
最终效果图如下:
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- 关于指针的一些事情
- c++ primer 第五版 笔记前言
- share_ptr的几个注意点
- 解决Vista系统OpenGL驱动问题的方法整理
- Delphi下OpenGL2d绘图之画四边形的方法
- Delphi下OpenGL2d绘图之画点的方法
- Delphi下OpenGL2d绘图之初始化流程详解
- Lua中调用C++函数示例
- Lua教程(一):在C++中嵌入Lua脚本
- Lua教程(二):C++和Lua相互传递数据示例
- C++联合体转换成C#结构的实现方法
- C++高级程序员成长之路
- C++编写简单的打靶游戏
- C++ 自定义控件的移植问题
- C++变位词问题分析
- C/C++数据对齐详细解析
- C++基于栈实现铁轨问题
- C++中引用的使用总结
- 使用Lua来扩展C++程序的方法