有理有条地绘制立体图(利用数据结构)

来源:互联网 发布:linux重启ntp服务命令 编辑:程序博客网 时间:2024/05/07 11:03

  • 话题引入
  • 利用顶点表边表面表储存信息
  • 利用邻接矩阵表示
  • 利用邻接表表示

话题引入

之前我曾经绘制过立体图形,不过后来发现这东西和数据结构的关系是很密切的,几个点之间是有不同关系,有的之前相互有线相连,有的之间没有线相连接,不能简单通过肉眼绘制,这在点很多的时候会出很大的问题。关于图的数据结构的博文,在网上有很多很好的讲稿
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;}

2 0
原创粉丝点击