您的位置:首页 > 运维架构

初学opengl(二)画散点图

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

我是这么考虑的,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;
}


最终效果图如下:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  opengl 散点图 C++