话题引入
之前我曾经绘制过立体图形,不过后来发现这东西和数据结构的关系是很密切的,几个点之间是有不同关系,有的之前相互有线相连,有的之间没有线相连接,不能简单通过肉眼绘制,这在点很多的时候会出很大的问题。关于图的数据结构的博文,在网上有很多很好的讲稿
http://blog.csdn.net/xiazdong/article/details/7354411#t14
http://blog.chinaunix.net/uid-26548237-id-3483650.html
其中这两篇文章写的特别好,照例我还是从最简单的正四面体和正方体开始下刀
其实当时已经用到了一些数据结构的想法
一个就是这里
static const GLfloat vertex_list[][3] = {
-0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
-0.5f, 0.5f, -0.5f,
-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
};
static const GLint index_list[][4] = {
0, 1, 2, 3,//bottem
0, 3, 7, 4,//left
2, 3, 7, 6,//front
1, 2, 6, 5,//right
0, 1, 5, 4,//back
4, 5, 6, 7//top
};
分别储存点的坐标,和每个面由哪几个点构成
之后绘制的时候只需要调用
for (int i = 0; i < 6; ++i) // 有六个面,循环六次
{
glBegin(GL_LINE_LOOP);
for (int j = 0; j < 4; ++j) // 每个面有四个顶点,循环四次
glVertex3fv(vertex_list[index_list[i][j]]);
glEnd();
}
即可
连最经典的斯坦福兔子,其实都是用这种方法实现的,第一部分储存顶点的信息,第二部分储存面上的点
下面分别用几种图中最常见的数据结构再次绘制这个立方体
利用顶点表、边表、面表储存信息
利用三个表储存信息,分别为顶点表、边表和面表,其中顶点是一个二维数组
p[][3]
(用来表示三维坐标)或者
p[][2]
(用来表示二维数组),第二张表是边表,如果两个点相连,那么这条边存入边表
p[][2]
,其也是一个二维数组,第三张表是面表,相应的几条边组成了面的话就把这几条边存入一行,因此行数和列数都不定。
下面以正方体为例
想要绘出立体图的大致来非常容易,只需要把所有的边遍历一遍
完整的代码如下
#include<GL/GLUT.H>
#include <windows.h>
#include <math.h>
#include <gl/GL.h>
static const GLfloat vertex_list[][3] = {
-0.5f, -0.5f, -0.5f,//0
0.5f, -0.5f, -0.5f,//1
0.5f, 0.5f, -0.5f,//2
-0.5f, 0.5f, -0.5f,//3
-0.5f, -0.5f, 0.5f,//4
0.5f, -0.5f, 0.5f,//5
0.5f, 0.5f, 0.5f,//6
-0.5f, 0.5f, 0.5f,//7
};
static const GLint line_list[][2] = {
0,1,//0
0,3,//1
0,4,//2
1,2,//3
1,5,//4
2,3,//5
2,6,//6
3,7,//7
4,5,//8
4,7,//9
5,6,//10
6,7,//11
};
static const GLint face_list[][4] = {
0, 3, 5, 1,//bottem
2, 1, 7, 9,//left
5, 6, 11, 7,//front
3, 4, 10, 6,//right
0, 4, 8, 2,//back
8, 9, 11, 10,//top
};
template <class T>
int getArrayLen(T& array)
{
return (sizeof(array) / sizeof(array[0]));
}
void myDisplay(void){
glClear(GL_COLOR_BUFFER_BIT);
glRotatef(45, 1, 1, 1);
glFrontFace(GL_CCW);
int L = getArrayLen(line_list);
for (int i = 0; i < L; ++i) // 有L个面,循环L次
{
glBegin(GL_LINES);
{glVertex3fv(vertex_list[line_list[i][0]]);//其中每条边由两点组成
glVertex3fv(vertex_list[line_list[i][1]]); }
glEnd();
}
glFlush();
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(400, 400);
glutCreateWindow("opengl1");
glutDisplayFunc(&myDisplay);
glutMainLoop();
return 0;
}
其中的
template <class T>
int getArrayLen(T& array)
{
return (sizeof(array) / sizeof(array[0]));
}
部分可以计算数组长度
面点的信息虽然看起来没用,但其实很有用,我想想啊。。日后再说吧
好像仅仅通过保存边表也可以推断出面表。。
利用邻接矩阵表示
维持一个二维数组,arr[i][j]表示i到j的边,如果两顶点之间存在边,则为1,否则为0;
维持一个一维数组,存储顶点信息,比如顶点的名字;
如果我们要看vi节点邻接的点,则只需要遍历arr[i]即可;
缺点:邻接矩阵表示法对于稀疏图来说不合理,因为太浪费空间;
我这里基本全是无向图,所以矩阵都是对称阵
#include<GL/GLUT.H>
#include <windows.h>
#include <math.h>
#include <gl/GL.h>
template <class T>
int getArrayLen(T& array)
{
return (sizeof(array) / sizeof(array[0]));
}
static const GLfloat vertex_list[][3] = {
-0.5f, -0.5f, -0.5f,//0
0.5f, -0.5f, -0.5f,//1
0.5f, 0.5f, -0.5f,//2
-0.5f, 0.5f, -0.5f,//3
-0.5f, -0.5f, 0.5f,//4
0.5f, -0.5f, 0.5f,//5
0.5f, 0.5f, 0.5f,//6
-0.5f, 0.5f, 0.5f,//7
};
int N = getArrayLen(vertex_list);
static const GLint line_list[8][8] = {
0,1,0,1,1,0,0,0,
1,0,1,0,0,1,0,0,
0,1,0,1,0,0,1,0,
1,0,1,0,0,0,0,1,
1,0,0,0,0,1,0,1,
0,1,0,0,1,0,1,0,
0,0,1,0,0,1,0,1,
0,0,0,1,1,0,1,0,
};
void myDisplay(void){
glClear(GL_COLOR_BUFFER_BIT);
glRotatef(45, 1, 1, 1);
glFrontFace(GL_CCW);
int L = getArrayLen(line_list);
for (int i = 0; i < 8; ++i) // 有8个点,循环8次
{
for (int j = i; j < 8;j++)
{
if (line_list[i][j]==1)
{ glBegin(GL_LINES);
{
glVertex3fv(vertex_list[i]);//其中每条边由两点组成
glVertex3fv(vertex_list[j]); }
glEnd();
}
else
continue;
}
}
glFlush();
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(400, 400);
glutCreateWindow("opengl1");
glutDisplayFunc(&myDisplay);
glutMainLoop();
return 0;
}
这是立方体利用邻接矩阵表示
利用邻接表表示
邻接表的处理方法是这样的:
(1)图中顶点用一个一维数组存储,当然,顶点也可以用单链表来存储,不过,数组可以较容易的读取顶点的信息,更加方便。
(2)图中每个顶点vi的所有邻接点构成一个线性表,由于邻接点的个数不定,所以,用单链表存储,无向图称为顶点vi的边表,有向图则称为顶点vi作为弧尾的出边表。
例如,下图就是一个无向图的邻接表的结构。
实现代码如下。。不过看起来是问题有点多。。不过慢慢改进吧。代码这么冗余我也是醉了,我现在是把每个点都开辟了一个空间
#include<GL/GLUT.H>
#include <windows.h>
#include <math.h>
#include <gl/GL.h>
static const GLfloat vertex_list[][3] = {
-0.5f, -0.5f, -0.5f,//0
0.5f, -0.5f, -0.5f,//1
0.5f, 0.5f, -0.5f,//2
-0.5f, 0.5f, -0.5f,//3
-0.5f, -0.5f, 0.5f,//4
0.5f, -0.5f, 0.5f,//5
0.5f, 0.5f, 0.5f,//6
-0.5f, 0.5f, 0.5f,//7
};
#define MAXVEX 1000 //最大顶点数
typedef int VertexType; //顶点类型应由用户定义
typedef int EdgeType; //边上的权值类型应由用户定义
typedef struct EdgeNode //边表结点
{
int adjvex; //邻接点域,存储该顶点对应的下标
struct EdgeNode *next; //链域,指向下一个邻接点
}EdgeNode;
typedef struct VertexNode //顶点表结构
{
VertexType data; //顶点域,存储顶点信息
EdgeNode *firstedge; //边表头指针
}
VertexNode, AdjList[MAXVEX];
void myDisplay(void){
glClear(GL_COLOR_BUFFER_BIT);
AdjList p;
for (int i = 0; i < 8; i++)
{
p[i].data = i;
}
//关于0点的
EdgeNode* pn00 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn00->adjvex=1;
p[0].firstedge = pn00;
EdgeNode* pn01 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn01->adjvex=3;
p[0].firstedge->next = pn01;
EdgeNode* pn02 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn02->adjvex = 4;
p[0].firstedge->next->next = pn02;
EdgeNode* pn03 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn03 = NULL;
p[0].firstedge->next->next->next = pn03;
//关于1点的
EdgeNode* pn10 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn10->adjvex = 0;
p[1].firstedge = pn10;
EdgeNode* pn11 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn11->adjvex = 2;
p[1].firstedge->next = pn11;
EdgeNode* pn12 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn12->adjvex = 5;
p[1].firstedge->next->next = pn12;
EdgeNode* pn13 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn13 = NULL;
p[1].firstedge->next->next->next = pn13;
//关于2点的
EdgeNode* pn20 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn20->adjvex = 1;
p[2].firstedge = pn20;
EdgeNode* pn21 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn21->adjvex = 3;
p[2].firstedge->next = pn21;
EdgeNode* pn22 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn22->adjvex = 6;
p[2].firstedge->next->next = pn22;
EdgeNode* pn23 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn23 = NULL;
p[2].firstedge->next->next->next = pn23;
//关于3点的
EdgeNode* pn30 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn30->adjvex = 0;
p[3].firstedge = pn30;
EdgeNode* pn31 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn31->adjvex = 2;
p[3].firstedge->next = pn31;
EdgeNode* pn32 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn32->adjvex = 7;
p[3].firstedge->next->next = pn32;
EdgeNode* pn33 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn33 = NULL;
p[3].firstedge->next->next->next = pn33;
//关于4点的
EdgeNode* pn40 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn40->adjvex = 0;
p[4].firstedge = pn40;
EdgeNode* pn41 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn41->adjvex = 5;
p[4].firstedge->next = pn41;
EdgeNode* pn42 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn42->adjvex = 7;
p[4].firstedge->next->next = pn42;
EdgeNode* pn43 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn43 = NULL;
p[4].firstedge->next->next->next = pn43;
//关于5点的
EdgeNode* pn50 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn50->adjvex = 1;
p[5].firstedge = pn50;
EdgeNode* pn51 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn51->adjvex = 4;
p[5].firstedge->next = pn51;
EdgeNode* pn52 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn52->adjvex = 6;
p[5].firstedge->next->next = pn52;
EdgeNode* pn53 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn53 = NULL;
p[5].firstedge->next->next->next = pn53;
//关于6点的
EdgeNode* pn60 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn60->adjvex = 2;
p[6].firstedge = pn60;
EdgeNode* pn61 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn61->adjvex = 5;
p[6].firstedge->next = pn61;
EdgeNode* pn62 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn62->adjvex = 7;
p[6].firstedge->next->next = pn62;
EdgeNode* pn63 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn63 = NULL;
p[6].firstedge->next->next->next = pn63;
//关于7点的
EdgeNode* pn70 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn70->adjvex = 3;
p[7].firstedge = pn70;
EdgeNode* pn71 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn71->adjvex = 4;
p[7].firstedge->next = pn71;
EdgeNode* pn72 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn72->adjvex = 6;
p[7].firstedge->next->next = pn72;
EdgeNode* pn73 = (EdgeNode*)malloc(sizeof(EdgeNode));
pn73 = NULL;
p[7].firstedge->next->next->next = pn73;
EdgeNode* pn = (EdgeNode*)malloc(sizeof(EdgeNode));
EdgeNode* s = (EdgeNode*)malloc(sizeof(EdgeNode));
glRotatef(45, 1, 1, 1);
for (int i = 0; i < 8; i++)
{
pn = p[i].firstedge;
do
{
glBegin(GL_LINES);
{glVertex3fv(vertex_list[p[i].data]);//其中每条边由两点组成
glVertex3fv(vertex_list[pn->adjvex]);
}
glEnd();
s = pn;
pn = pn->next;
//free(s);
}
while (pn != NULL);
}
glFlush();
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(400, 400);
glutCreateWindow("opengl1");
glutDisplayFunc(&myDisplay);
glutMainLoop();
return 0;
}