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

话题引入

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拉风小宇

请我喝个咖啡呗

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值