基于半边数据结构(翼边数据结构)的Eular操作来实现扫成 通过OpenGL进行CAD实体建模 经测试可直接运行

    话说,今天重装电脑程序,把VS2010重装以后,运行了几个之前写的小程序测试了一下.不经意间,我发现2年前自己参考着资料写的一个Sweep扫成程序,当时程序能够跑,但是和翼边数据结构的含义有所不同,当时费了好大的劲也没弄明白其中一个指针的处理怎么会是那个样子的,现在时间过去两年了,自己竟然还是没有抽出时间来将其弄明白,真是惭愧.也许我当时坚持查阅资料仔细研究就不会是这个样子了,而且有些东西一旦放下,再抽出整块的时间去理解它是不现实的。现在2年过去了,如果不是重装电脑,自己早忘了有这么一回事情。这些年自己在技术方面没有多少成长不怪其他,一切源于自己没有坚持钻研和探索的精神.所以遇到挫折了不要一味的怪罪环境,最主要的还是自己不够努力.下面把程序的结果贴出来,后面还要仔细再研究一下.

     下面是我实现程序的例子,第一幅图是一个线框模型,第二幅图是另一个实体模型。

基于半边数据结构(翼边数据结构)的Eular操作来实现扫成 通过OpenGL进行CAD实体建模 经测试可直接运行_第1张图片

基于半边数据结构(翼边数据结构)的Eular操作来实现扫成 通过OpenGL进行CAD实体建模 经测试可直接运行_第2张图片

    这个程序是在一门选修课(忘了叫什么名字了,大概叫CAD实体建模之类的)上老师布置的大程作业,当时为了做出来费了好大的力气,现在看来这只不过是个要求通过欧拉操作来实现扫成的小程序,而且算法的思想很简单,只是需要用到实体建模中的名为翼边数据结构(又叫,半边数据结构)的东西,和一个欧拉公式。我看过网络上的一些同样的介绍半边数据结构的东西,他们写的也算比较详细,但是有个很致命的问题,那就是他们只写出了核心代码,没有具体实现的环境。对于一个编程初学者而言,你光给出核心代码,他们是没有能力把这个程序给搭建起来的,因为程序不仅涉及到核心数据结构和算法,通过MFC实现鼠标点击和画图也够一般初学者头疼脑大的了。

    为了方便初学者学习,也为了分享自己的一些心得,我下面不仅给出了核心代码,而且给出了整个实现程序的源代码。想学习实体建模的同学能够通过这个程序学到很多东西,对于那些想要交图形学的代码课程作业的同学来说,你也可以直接把这个交上去了吆,因为这个代码我已经测试过了,没有任何问题,可以直接运行。当然,最好你是为了学习需要,而不是为了直接down下来应付老师,而且前提是你要部署好OpenGl的库。具体的下载链接在下面。

    如果运行过程中有什么问题,欢迎留言讨论。如果大家觉得此文对您有帮助,麻烦你“顶”一下,予人玫瑰,手有余香~~


    运行的环境和平台是:
Windows 7, visual studio 2010

    刚装了win7和VS2010。配置OpenGL的方法如下。

glut下载地址:

http://download.csdn.net/detail/daringpig/4375605

glut.h ---> C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Include\gl

glut.dll,glut32.dll ---> C:\Windows\SysWOW64 (windows7 64位操作系统)

---> C:\Windows\System32 (windows7 32位操作系统)

glut.lib,glut32.lib ---> C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\lib

    核心数据结构采用半边数据结构实现,具体的实现功能是:

通过绘制一个基本的面,然后通过该面通过指定的向量和距离就可以扫成为一个体。

具体的核心代码如下:

CSolid* CEulerDoc::mvfs(GLdouble *point)
{
 CSolid *newSolid;
 CFace *newFace;
 CLoop *newLoop;
 CVertex *newVert;
 newSolid = new CSolid;
 newFace = new CFace;
 newLoop = new CLoop;
 newVert = new CVertex;

 // Set the coordinate of the new vertex
 newVert->SetCoord(point);

 // Add the new face to the new solid
 newSolid->AddFace(newFace);

 // Add the new vertex to the new solid
 newSolid->AddVertex(newVert);

 // Add the new loop to the new face
 newFace->AddLoop(newLoop);
 
 return newSolid;
}

CHalfEdge* CEulerDoc::mev(GLdouble *point1, GLdouble *point2, CLoop* lp)
{
 CSolid  *pSolid = lp->host_f->host_s;
 CVertex  *v1, *newVert;
 CEdge  *newEdge;
 CHalfEdge *he1, *he2;
 newVert = new CVertex;
 newEdge = new CEdge;
 he1 = new CHalfEdge;
 he2 = new CHalfEdge;

 // Find the start vertex of the edge
 v1 = pSolid->FindVertex(point1);
 if(!v1)
 {
  AfxMessageBox("Can't find the point1!");
  return NULL;
 }

 // Set the coordinate of the new vertex
 newVert->SetCoord(point2);

 // Set v1 as the start vertex of halfedge1
 he1->SetVertex(v1);

 // Set v2 as the start vertex of halfedge2
 he2->SetVertex(newVert);

 // Add two new halfedges to the loop
 lp->AddHalfEdge(he1, he2);

 // Set the new edge to new halfedges
 he1->edge = he2->edge = newEdge;
 newEdge->he1 = he1;
 newEdge->he2 = he2;

 // Add the new edge to the solid
 pSolid->AddEdge(newEdge);

 // Add the new vertex to the solid
 pSolid->AddVertex(newVert);

 return he1;
}

CLoop* CEulerDoc::mef(GLdouble *point1, GLdouble *point2, CLoop* lp)
{
 CSolid  *pSolid = lp->host_f->host_s;
 CVertex  *v1, *v2;
 // Find the two given vertex
 v1 = pSolid->FindVertex(point1);
 if(!v1)
 {
  AfxMessageBox("Can't find the point1!");
  return NULL;
 }
 v2 = pSolid->FindVertex(point2);
 if(!v2)
 {
  AfxMessageBox("Can't find the point2!");
  return NULL;
 }
 // two vertex must in the same loop
 if(!lp->IsVertexIn(v1) && !lp->IsVertexIn(v2))
 {
  AfxMessageBox("该两点不位于同一个loop中");
  return NULL;
 }

 CFace *newFace;
 CLoop *newLoop;
 CEdge *newEdge;
 CHalfEdge *he1, *he2, *temphe1, *temphe2;
 newFace = new CFace;
 newLoop = new CLoop;
 newEdge = new CEdge;
 he1 = new CHalfEdge;
 he2 = new CHalfEdge;

 // Find two halfedges start with two vertexs
 temphe1 = lp->FindHostHalfEdge(v1);
 temphe2 = lp->FindHostHalfEdge(v2);

 // Change halfedges' host loop to new loop
 temphe1->prev->next = NULL;
 newLoop->AddHalfEdge(temphe1, NULL);
 while(temphe1 != temphe2)
  temphe1 = temphe1->next;
 temphe1->prev->next = he2;
 he2->prev = temphe1->prev;
 he2->next = newLoop->GetHalfEdgeHead();
 newLoop->GetHalfEdgeHead()->prev = he2;
 temphe1->prev = NULL;

 // Add halfedge start with vertex one to the old loop % close this loop
 lp->AddHalfEdge(he1, NULL);
 he1->next = temphe2;
 temphe2->prev = he1;

 // Set two halfedges' start vertex and adjacent
 he1->SetVertex(v1);
 he2->SetVertex(v2);
 he1->adj = he2;
 he2->adj = he1;

 // Set the new edge and add to the solid
 newEdge->he1 = he1;
 newEdge->he2 = he2;
 pSolid->AddEdge(newEdge);

 // Add new face to the solid
 pSolid->AddFace(newFace);

 // Add new loop to the new face
 newFace->AddLoop(newLoop);

 return newLoop;
}


CLoop* CEulerDoc::kemr(GLdouble *point1, GLdouble *point2, CLoop* lp)
{
 CSolid  *pSolid = lp->host_f->host_s;
 CVertex  *v1, *v2;
 // Find the two given vertex
 v1 = pSolid->FindVertex(point1);
 if(!v1)
 {
  AfxMessageBox("Can't find the point1!");
  return NULL;
 }
 v2 = pSolid->FindVertex(point2);
 if(!v2)
 {
  AfxMessageBox("Can't find the point2!");
  return NULL;
 }
 // two vertex must in the same loop
 if(!lp->IsVertexIn(v1) && !lp->IsVertexIn(v2))
  return NULL;

 CLoop  *newLoop;
 CHalfEdge *he1, *he2;
 CEdge  *edge;
 newLoop = new CLoop;

 // Find two halfedges start with two vertexs
 he1 = lp->GetHalfEdgeHead();
 while(he1)
 {
  if(he1->GetVertex() == v1 && he1->next->GetVertex() == v2)
   break;
  he1 = he1->next;
 }
 he2 = he1->adj;

 // Make a newloop and delete two halfedges
 newLoop->AddHalfEdge(he1->next, NULL);
 he2->prev->next = he1->next;
 he1->next->prev = he2->prev;
 he1->prev->next = he2->next;
 he2->next->prev = he1->prev;
 he1->next = he1->prev = he2->next = he2->prev = NULL;
 delete he1;
 delete he2;

 // Find the edge and delete it
 edge = pSolid->GetEdgeHead();
 while(edge)
 {
  if(edge->he1 == he1)
   break;
  edge = edge->next;
 }
 edge->prev->next = edge->next;
 edge->next->prev = edge->prev;
 edge->next = edge->prev = NULL;

 // Add new loop to the face
 lp->host_f->AddLoop(newLoop);

 return newLoop;
}

void CEulerDoc::kfmrh(CLoop* outlp, CLoop* lp)
{
 if(!outlp || !lp)
 {
  AfxMessageBox("The loop is NULL!");
  return;
 }

 CSolid  *pSolid = lp->host_f->host_s;

 // Add the loop to the face
 outlp->host_f->AddLoop(lp);

 // Get the face need to be killed
 CFace* pFace = pSolid->GetFaceEnd();

 // Delete it
 pFace->prev->next = NULL;

 delete pFace;
}

void CEulerDoc::sweep(CFace* pFace, GLdouble dx, GLdouble dy, GLdouble dz)
{
 CFace  *pEnd;
 CHalfEdge *pHe, *pHead;
 CLoop  *pLoop, *newLoop, *pOutLoop;
 GLdouble *pPoint;
 GLdouble point1[3], point2[3], first[3], last[3];
 BOOL  bOut = TRUE; // Show that if is the outloop of the top face

 // Remember the last inside loop's face 
 pEnd = pFace->host_s->GetFaceEnd();
 // Start with the second face, because the first face is for buttom
 pFace = pFace->next;
 while(pFace)
 {
  // Get the first point of the loop
  newLoop = pLoop = pFace->GetLoopHead();
  pHe = pHead = pLoop->GetHalfEdgeHead();
  pPoint = pHe->GetVertex()->vcoord;
  point1[0] = pPoint[0];
  point1[1] = pPoint[1];
  point1[2] = pPoint[2];
  // first[] is used for close the top face
  // last[] is used for side face
  first[0] = last[0] = point2[0] = point1[0] + dx;
  first[1] = last[1] = point2[1] = point1[1] + dy;
  first[2] = last[2] = point2[2] = point1[2] + dz;
  // Make the new edge
  mev(point1, point2, pLoop);
  // Goto next halfedge
  pHe = pHe->next;
  while(pHe->GetVertex() != pHead->GetVertex())
  {
   // Get the point
   pPoint = pHe->GetVertex()->vcoord;
   point1[0] = pPoint[0];
   point1[1] = pPoint[1];
   point1[2] = pPoint[2];
   point2[0] = point1[0] + dx;
   point2[1] = point1[1] + dy;
   point2[2] = point1[2] + dz;
   // Make the new edge
   mev(point1, point2, newLoop);
   // Make a new side face
   newLoop = mef(point2, last, newLoop);
   // Remember the lastest point
   last[0] = point2[0];
   last[1] = point2[1];
   last[2] = point2[2];
   pHe = pHe->next;
  }
  // Close the top face
  newLoop = mef(first, last, newLoop);

  if(bOut)
  {
   pOutLoop = newLoop;
   bOut = FALSE;
  }
  else
   kfmrh(pOutLoop, newLoop);

  if(pFace == pEnd)
   break;

  pFace = pFace->next;
 }
}

CSolid* CEulerDoc::MakeSolid(GLdouble point[][3], GLint* pointnum, GLint loopnum, GLdouble* vector, GLdouble len)
{
 CSolid *pSolid, *pTemp;
 CLoop *pLoop, *pHead;
 int  i, j, pos;

 // Create the solid
 pSolid = mvfs(point[1]);
 pHead = pLoop = pSolid->GetFaceHead()->GetLoopHead();

 // Create the out loop
 for(i = 1;i < pointnum[1]-1;i ++)
 {
  mev(point[i], point[i+1], pLoop);
 }
 mef(point[i], point[1], pLoop);
 pos = i+1;

 // Create the inside loops
 for(i = 2;i < loopnum; i ++)
 {
  mev(point[1], point[pos+1], pHead);
  for(j = 1;j < pointnum[i]-1;j ++)
  {
   mev(point[pos+j], point[pos+j+1], pHead);
  }
  mef(point[pos+j], point[pos+1], pHead);
  kemr(point[1], point[pos+1], pHead);
  pos = pos+j+1;
 }

 sweep(pSolid->GetFaceHead(), vector[0] * len, vector[1] * len, vector[2] * len);

 // Insert the new solid
 if(!solid)
  solid = pSolid;
 else
 {
  pTemp = solid;
  while(pTemp->next)
   pTemp = pTemp->next;
  pTemp->next = pSolid;
 }

 return pSolid;
}


void CEulerView::RenderScene(void)
{
 CEulerDoc *pDoc = GetDocument();
 CSolid  *pSolid;
 CFace  *pFace;
 CLoop  *pLoop;
 CHalfEdge *pHalfEdge, *pHeHead; 
 GLint  i, j, pos = 0;
 GLfloat  draw_ambient[] = { 1.0, 1.0, 1.0},
    mat_ambient[] = { 0.5, 0.5, 0.5 }, 
    mat_specular[] = { 1.0, 1.0, 1.0, 1.0 },
    shininess[] = { 50.0 };
 GLfloat  position1[] = { 10.0, 10.0, 10.0, 0.0 };

 glLoadIdentity(); 
 glTranslatef(m_xPos, m_yPos, m_zPos);
 glRotatef(m_xAngle, 1.0f,0.0f,0.0f);
 glRotatef(m_yAngle, 0.0f,1.0f,0.0f);

 if(m_bCoord)
  RenderCoordinate(1);

 glLightfv(GL_LIGHT0, GL_POSITION, position1);

 //////////////////////////////////////////////////////////////////////////
 // Draw
 glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, draw_ambient);
 for(i = 1; i <= m_iLoopNum ;i ++)
 {
  pos += m_iLoopPointNum[i-1];
  glBegin(GL_LINE_STRIP);
  for(j = 1;j <= m_iLoopPointNum[i]; j++)
   glVertex3d(m_dPoint[pos + j][0], m_dPoint[pos + j][1], m_dPoint[pos + j][2]);
  glEnd();
 }
 //////////////////////////////////////////////////////////////////////////
 pSolid = pDoc->solid;

 if(m_bWire)
 {
  glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, draw_ambient);
  glLineWidth(2.0);
  glBegin(GL_LINES);
  while(pSolid)
  {
   pFace = pSolid->GetFaceHead();
   while(pFace)
   {
    pLoop = pFace->GetLoopHead();
    while(pLoop)
    {
     pHalfEdge = pHeHead = pLoop->GetHalfEdgeHead();
     while(pHalfEdge)
     {
      glVertex3dv(pHalfEdge->GetVertex()->vcoord);
      glVertex3dv(pHalfEdge->next->GetVertex()->vcoord);
      pHalfEdge = pHalfEdge->next;
      if(pHalfEdge == pHeHead)
       break;
     }
     pLoop = pLoop->next;
    }
    pFace = pFace->next;   
   }
   pSolid = pSolid->next;
  }
  glEnd();
 }
 else
 {
  glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, mat_ambient);
  glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
  glMaterialfv(GL_FRONT, GL_SHININESS, shininess);
  glEnable(GL_NORMALIZE);
  while(pSolid)
  {
   pFace = pSolid->GetFaceHead();
   while(pFace)
   {
    gluTessBeginPolygon(tessobj, NULL);
    glNormal3dv(pFace->feq);
    pLoop = pFace->GetLoopHead();
    while(pLoop)
    {
     gluTessBeginContour(tessobj);
     pHalfEdge = pHeHead = pLoop->GetHalfEdgeHead();
     while(pHalfEdge)
     {
      gluTessVertex(tessobj, pHalfEdge->GetVertex()->vcoord, pHalfEdge->GetVertex()->vcoord);
      pHalfEdge = pHalfEdge->next;
      if(pHalfEdge == pHeHead)
       break;
     }
     gluTessEndContour(tessobj);
     pLoop = pLoop->next;
    }
    gluTessEndPolygon(tessobj);
    pFace = pFace->next;   
   }
   pSolid = pSolid->next;
  }
 }
}


OpenGL库和Eular的源代码下载地址为:

http://download.csdn.net/detail/daringpig/4375605
如果运行过程中有什么问题,欢迎留言讨论。

http://download.csdn.net/detail/daringpig/4375599


如果大家觉得此文对您有帮助,麻烦你“顶”一下,谢谢



你可能感兴趣的:(算法)