Example #1
0
/************************************************************************
 *                                                                       *
 *  Since one the polygon's vertex has been changed, we need to rebuild	*
 *  'yedge'.								*
 *									*/
ReactionType
Polygon::resize_done(short, short)
{
  int index = FindDuplicateVertex(aperture/2);  
  if (index >= 0) {
    if (closed && index < npnts || (!closed) && index < npnts - 1) {
      erase();
      DeleteVertex(index);
      draw();
    }
  }
  build_yedge();
  
  some_info(FALSE);
  return REACTION_NONE;
}
Example #2
0
    bool Graph::EdgeCollapse(long v1, long v2)
	{
		long edgeToDelete = GetEdgeID(v1, v2);
        if (edgeToDelete >= 0) 
		{
			// delete the edge (v1, v2)
			DeleteEdge(edgeToDelete);
			// add v2 to v1 ancestors
            m_vertices[v1].m_ancestors.push_back(v2);
			// add v2's ancestors to v1's ancestors
			m_vertices[v1].m_ancestors.insert(m_vertices[v1].m_ancestors.begin(),
											  m_vertices[v2].m_ancestors.begin(), 
											  m_vertices[v2].m_ancestors.end());
			// update adjacency information
			SArray<long, SARRAY_DEFAULT_MIN_SIZE> & v1Edges =  m_vertices[v1].m_edges;
			long b = -1;
            long idEdge;
			for(size_t ed = 0; ed < m_vertices[v2].m_edges.Size(); ++ed) 
			{
                idEdge = m_vertices[v2].m_edges[ed];
				if (m_edges[idEdge].m_v1 == v2)
				{
					b = m_edges[idEdge].m_v2;
				}
				else
				{
					b = m_edges[idEdge].m_v1;
				}
				if (GetEdgeID(v1, b) >= 0)
				{
					m_edges[idEdge].m_deleted = true;
					m_vertices[b].DeleteEdge(idEdge);
					m_nE--;
				}
				else
				{
					m_edges[idEdge].m_v1 = v1;
					m_edges[idEdge].m_v2 = b;
					v1Edges.Insert(idEdge);
				}
			}
			// delete the vertex v2
            DeleteVertex(v2);			
            return true;
        }
		return false;
    }
Example #3
0
    bool Graph::EdgeCollapse(long v1, long v2)
	{
		long edgeToDelete = GetEdgeID(v1, v2);
        if (edgeToDelete >= 0) 
		{
			// delete the edge (v1, v2)
			DeleteEdge(edgeToDelete);
			// add v2 to v1 ancestors
            m_vertices[v1].m_ancestors.push_back(v2);
			// add v2's ancestors to v1's ancestors
			m_vertices[v1].m_ancestors.insert(m_vertices[v1].m_ancestors.begin(),
											  m_vertices[v2].m_ancestors.begin(), 
											  m_vertices[v2].m_ancestors.end());
			// update adjacency information
			std::set<long> & v1Edges =  m_vertices[v1].m_edges;
			std::set<long>::const_iterator ed(m_vertices[v2].m_edges.begin());
			std::set<long>::const_iterator itEnd(m_vertices[v2].m_edges.end());
			long b = -1;
			for(; ed != itEnd; ++ed) 
			{
				if (m_edges[*ed].m_v1 == v2)
				{
					b = m_edges[*ed].m_v2;
				}
				else
				{
					b = m_edges[*ed].m_v1;
				}
				if (GetEdgeID(v1, b) >= 0)
				{
					m_edges[*ed].m_deleted = true;
					m_vertices[b].DeleteEdge(*ed);
					m_nE--;
				}
				else
				{
					m_edges[*ed].m_v1 = v1;
					m_edges[*ed].m_v2 = b;
					v1Edges.insert(*ed);
				}
			}
			// delete the vertex v2
            DeleteVertex(v2);			
            return true;
        }
		return false;
    }
Example #4
0
//********************************************
// DeleteVertex at the end that are flagged with
// sequence_number >= sequence_number
//********************************************
int CPath3d::RemovePathEnd(int sequence_number, int ID, double x, double y, double z)
{
	int n = m_ArrayVertex.GetSize()-1;

	// first verify that the line number is there
	while (n>=0)
	{
		int SeqNum = GetVertex(n)->Get_sequence_number();
		if (SeqNum == sequence_number) break;
		n--;
	}

	if (n<0) return 1;


	// now delete until we find the sequence_number
	n = m_ArrayVertex.GetSize()-1;
	while (n>=0)
	{
		if (GetVertex(n)->Get_sequence_number() == sequence_number) break;
		DeleteVertex(n);
		n--;
	}

	if (n<0) return 1;


	// find which segment we nearly pass through by
	// finding the minimum distance to all of them

	double mindist=1e99;
	int mini=0;
	n = m_ArrayVertex.GetSize()-2;
	CVertex3d Point(x,y,z);

	while (n>=0)
	{
		double dist = FindDistPointToSegment(GetVertex(n),&Point,GetVertex(n+1));
		
		if (dist < mindist)
		{
			mindist = dist;
			mini = n;
		}
			
		if (GetVertex(n)->Get_sequence_number() != sequence_number ||
			GetVertex(n)->GetID() == ID) break;
		
		n--;
	}



	// now delete all of the line number
	// or until we find the ID

	n = m_ArrayVertex.GetSize()-1;
	while (n>=0)
	{
		if (GetVertex(n)->Get_sequence_number() != sequence_number ||
			GetVertex(n)->GetID() == ID) break;

		if (n == mini+1)
		{
			// instead of deleting the last segment
			// that goes through the point
			// modify it to go to the new point
			GetVertex(n)->Set(x,y,z);
			break;
		}

		DeleteVertex(n);
		n--;
	}

	if (n<0) return 1;
	return 0;
}
Example #5
0
block* complex::Merge(node* n)
{
   list<block*> sub_blocks;
   Recurse(n,&sub_blocks,0);

   list<block*>::iterator sbit=sub_blocks.begin();
//   if (sbit==sub_blocks.end())
//     exit(1);

   block* b=new block(BC(*sbit),this);
//    block* b=new block(*((*sbit)->bc),this);
//   assert(b->bdry_verts.Empty());
//   (b->bc)->SetAs(BC(*sbit));
   BC(b).SetAs(BC(*sbit));
   for(int i=0;i<DIM;++i)
     BC(b)++;
   int level=(cube_bits-BC(b).Bits())/DIM;
   for(int i=0;i<level*DIM;++i)
     BC(b)--;
   BC(b).Coordinates(min);
   for(int i=0;i<level*DIM;++i)
     BC(b)++;

   vertex* v;
   int vc[DIM];
   int flag=1;
   cell_iter bvit;
   cell_iter cit;
   cobdry_iter cbit;

   while(sbit!=sub_blocks.end())
     {
	bvit=(*sbit)->bdry_verts.Begin();
	while(bvit!=(*sbit)->bdry_verts.End())
	  {
             v=(vertex*)(*bvit);
	     v->bc->Coordinates(vc);
	     flag=1;
	     for (int i=0; i<DIM; ++i)
	       flag*=(((min[i]<vc[i])&&(vc[i]<min[i]+Power2(level)))||((vc[i]==0)||(vc[i]==Power2(cube_bits/DIM))));
             if (flag)
	       {
		  if (!(v->Interior()))
		    {
		       v->MarkInterior();
		       DeleteVertex(v);
		       b->cells[0].InsertUniqueSort(v);
		    }
	       }
	     else
	       b->bdry_verts.InsertUniqueSort(v);
	     ++bvit;
	  }
	for(int i=0;i<DIM;++i)
	   {
 	      cit=(*sbit)->cells[i].Begin();
	      while(cit!=(*sbit)->cells[i].End())
	         {
	            b->cells[i].InsertUniqueSort(Ptr(cit));
	            ++cit;
	         }
	   }
	++sbit;
     }

   cit=b->cells[0].Begin();
   while(cit!=b->cells[0].End())
     {
	cbit=((vertex*)(*cit))->Cobdry()->Begin();
//      cout << "size " << (*cit)->Cobdry()->Size() << endl;
	while(cbit!=((vertex*)(*cit))->Cobdry()->End())
	   {
	      ((edge*)Ptr(cbit))->MarkInterior();
//	      b->cells[1].InsertUniqueSort(Ptr(cbit));
	      if (b->cells[1].Find(Ptr(cbit))==b->cells[1].End())
		{
		   b->cells[1].InsertUniqueSort(Ptr(cbit));
		}
	      ++cbit;
	   }
	++cit;
     }
   
   cit=b->cells[1].Begin();
   while(cit!=b->cells[1].End())
     {
	cbit=((edge*)(*cit))->Cobdry()->Begin();
//      cout << "size " << (*cit)->Cobdry()->Size() << endl;
	while(cbit!=((edge*)(*cit))->Cobdry()->End())
	   {
	      ((ncell*)Ptr(cbit))->MarkInterior();
//	      b->cells[2].InsertUniqueSort(Ptr(cbit));
	      if (b->cells[2].Find(Ptr(cbit))==b->cells[2].End())
		{
		   b->cells[2].InsertUniqueSort(Ptr(cbit));
		}
	      ++cbit;
	   }
	++cit;
     }	
	
   for(int i=3;i<DIM;++i)
     {
	cit=b->cells[i-1].Begin();
	while(cit!=b->cells[i-1].End())
	  {
	     cbit=((ncell*)(*cit))->Cobdry()->Begin();
//             cout << "size " << (*cit)->Cobdry()->Size() << endl;
	     while(cbit!=((ncell*)(*cit))->Cobdry()->End())
	       {
		  ((ncell*)Ptr(cbit))->MarkInterior();
//		  b->cells[i].InsertUniqueSort(Ptr(cbit));
		  if (b->cells[i].Find(Ptr(cbit))==b->cells[i].End())
		    {
		       b->cells[i].InsertUniqueSort(Ptr(cbit));
		    }
		  ++cbit;
	       }
	     ++cit;
	  }
     }
   
   if (n==cubes.Root)
     cubes.Root=b;
   else
     {
       node* parent=n->Parent;
       if (parent->Left==n)
         parent->Left=b;
       else
         parent->Right=b;
       b->Parent=parent;	
     }	
   
   Delete(n,0);
   
   return(b);	
}
Example #6
0
//********************************************
// DeleteVertex at the end that are flagged with
// sequence_number >= sequence_number
//********************************************
int CPath3d::RemovePathEnd(int sequence_number, int ID, double x, double y, double z)
{
	if (TheFrame) TheFrame->GViewDlg.m_view.OpenGLMutex->Lock();

	int n = m_ArrayVertex.GetSize()-1;

	setup_pointer p=TheFrame->GCodeDlg.Interpreter->p_setup;

	float xtool = p->tool_table[p->selected_tool_slot].xoffset;
	float ytool = p->tool_table[p->selected_tool_slot].yoffset;
	float ztool = p->tool_table[p->selected_tool_slot].length;

	x -= xtool;
	y -= ytool;
	z -= ztool;


	// first verify that the line number is there
	while (n>=0)
	{
		int SeqNum = GetVertex(n)->Get_sequence_number();
		if (SeqNum == sequence_number) break;
		n--;
	}

	if (n<0)
	{
		if (TheFrame) TheFrame->GViewDlg.m_view.OpenGLMutex->Unlock();
		return 1;
	}


	// now delete until we find the sequence_number
	n = m_ArrayVertex.GetSize()-1;
	while (n>=0)
	{
		if (GetVertex(n)->Get_sequence_number() == sequence_number) break;
		DeleteVertex(n);
		n--;
	}

	if (n<0)
	{
		if (TheFrame) TheFrame->GViewDlg.m_view.OpenGLMutex->Unlock();
		return 1;
	}

	// find which segment we nearly pass through by
	// finding the minimum distance to all of them

	double mindist=1e99;
	int mini=0;
	n = m_ArrayVertex.GetSize()-2;
	CVertex3dFast Point(x,y,z);

	while (n>=0)
	{
		double dist = FindDistPointToSegment(GetVertex(n),&Point,GetVertex(n+1));
		
		if (dist < mindist)
		{
			mindist = dist;
			mini = n;
		}
			
		if (GetVertex(n)->Get_sequence_number() != sequence_number ||
			GetVertex(n)->GetID() == ID) break;
		
		n--;
	}



	// now delete all of the line number
	// or until we find the ID

	n = m_ArrayVertex.GetSize()-1;
	while (n>=0)
	{
		if (GetVertex(n)->Get_sequence_number() != sequence_number ||
			GetVertex(n)->GetID() == ID) break;

		if (n == mini+1)
		{
			// instead of deleting the last segment
			// that goes through the point
			// modify it to go to the new point
			GetVertex(n)->Set(x,y,z);
			break;
		}

		DeleteVertex(n);
		n--;
	}

	if (TheFrame) TheFrame->GViewDlg.m_view.OpenGLMutex->Unlock();

	if (n<0) return 1;
	return 0;
}
Example #7
0
VOID DeleteDuplicateVertices(LEVEL_DATA *pLevel, BOOL bSelOnly)
{

#if 0

  INT i, j;
  DWORD dwSize;
  ROUNDED_VERTEX huge *pRoundedVertex;

  if (pLevel->nVertices > 0)
  {
    dwSize = (DWORD)pLevel->nVertices * (DWORD)sizeof(ROUNDED_VERTEX);

    pRoundedVertex = (ROUNDED_VERTEX *)GlobalAllocPtr(GPTR, dwSize);

    if (pRoundedVertex == NULL)
    {
      MsgBox( hwndMDIClient,
              MB_ICONEXCLAMATION,
              "Out of memory" );

      return;
    }

    for(i = 0; i < pLevel->nVertices; ++i)
    {
      pRoundedVertex[i].x = (long)Round(pLevel->pVertexData[i].x);
      pRoundedVertex[i].y = (long)Round(pLevel->pVertexData[i].y);
      pRoundedVertex[i].z = (long)Round(pLevel->pVertexData[i].z);
    }

    for(i = 0; i < pLevel->nVertices - 1; ++i)
    {
      for(j = i + 1; j < pLevel->nVertices; ++j)
      {
        if ( pRoundedVertex[i].x == pRoundedVertex[j].x &&
             pRoundedVertex[i].y == pRoundedVertex[j].y &&
             pRoundedVertex[i].z == pRoundedVertex[j].z )
        {
          if ((pLevel->pVertexData + j)->flags & VF_SELECTED)
          {
            (pLevel->pVertexData + i)->flags |= VF_SELECTED;
          }
          
          ReplaceVertex(pLevel, j, i);
          DeleteVertex(pLevel, j);
          pLevel->bRebuildZone = TRUE;

          while(j < pLevel->nVertices)
          {
            pRoundedVertex[j].x = pRoundedVertex[j+1].x;
            pRoundedVertex[j].y = pRoundedVertex[j+1].y;
            pRoundedVertex[j].z = pRoundedVertex[j+1].z;

            ++j;
          }

          break;
        }
      }
    }

    GlobalFreePtr(pRoundedVertex);
  }

#else 

  INT i, j, n, num_deleted_vertices, num_deleted_polygons;
  double dx, dy, dz;
  HCURSOR hcurSave;

  hcurSave = SetCursor(LoadCursor(NULL, IDC_WAIT));

  num_deleted_vertices = 0;
  num_deleted_polygons = 0;

  if (bSelOnly)
  {
    n = pLevel->nVertices;
  }
  else
  {
    n = pLevel->nVertices - 1;
  }

  if (pLevel->nVertices > 0)
  {
    for(i = 0; i < n; ++i)
    {
      if (bSelOnly)
      {
        if (((pLevel->pVertexData + i)->flags & VF_SELECTED) == 0)
        {
          continue;
        }

        j = 0;
      }
      else
      {
        j = i + 1;
      }

      while(j < pLevel->nVertices)
      {
        if (i != j)
        {
          dx = (double)(pLevel->pVertexData + i)->x - (double)(pLevel->pVertexData + j)->x;
          dy = (double)(pLevel->pVertexData + i)->y - (double)(pLevel->pVertexData + j)->y;
          dz = (double)(pLevel->pVertexData + i)->z - (double)(pLevel->pVertexData + j)->z;

          if (dx * dx + dy * dy + dz * dz < 4.0)
          {
            if ((pLevel->pVertexData + j)->flags & VF_SELECTED)
            {
              (pLevel->pVertexData + i)->flags |= VF_SELECTED;
            }
          
            num_deleted_vertices += 1;
            num_deleted_polygons += ReplaceVertex(pLevel, j, i);
            DeleteVertex(pLevel, j);
            pLevel->bRebuildZone = TRUE;
            break;
          }
        }

        ++j;
      }
    }
  }

  if (num_deleted_vertices == 1)
  {
    MsgBox( hwndMDIClient,
            MB_ICONINFORMATION,
            "1 duplicate vertex has been deleted" );
  }
  else if (num_deleted_vertices > 1)
  {
    MsgBox( hwndMDIClient,
            MB_ICONINFORMATION,
            "%d duplicate vertices have been deleted",
            num_deleted_vertices );
  }

  if (num_deleted_polygons == 1)
  {
    MsgBox( hwndMDIClient,
            MB_ICONINFORMATION,
            "1 polygon with duplicated vertices has been deleted" );
  }
  else if (num_deleted_polygons > 1)
  {
    MsgBox( hwndMDIClient,
            MB_ICONINFORMATION,
            "%d polygons with duplicated vertices have been deleted",
            num_deleted_polygons );
  }

#endif

  SetCursor(hcurSave);

  return;

} // DeleteDuplicateVertices
Example #8
0
VOID DeleteSelections(LEVEL_DATA *pLevel)
{
  INT i, j, n;
  POLYGON huge *p;
  HCURSOR hcurSave;

  hcurSave = SetCursor(LoadCursor(NULL, IDC_WAIT));

  //
  //  If any polygon uses a selected vertex then select it also.
  //

  p = pLevel->pPolygonData;

  for(i = 0; i < pLevel->nPolygons; ++i)
  {
    if ((p->flags & PF_SELECTED) == 0)
    {
      n = (p->flags & 7) + 3;

      for(j = 0; j < n; ++j)
      {
        if ((pLevel->pVertexData + p->vertices[j])->flags & VF_SELECTED)
        {
          p->flags |= PF_SELECTED;
          break;
        }
      }
    }

    ++p;
  }

  //
  //  Unlink any polygon that is linked to a selected polygon.
  //

  p = pLevel->pPolygonData;

  for(i = 0; i < pLevel->nPolygons; ++i)
  {
    if (p->flags & PF_LINKED)
    {
      if ((pLevel->pPolygonData + p->polygon_link)->flags & PF_SELECTED)
      {
        p->flags &= ~PF_LINKED;
      }
    }

    ++p;
  }

  //
  //  Delete selected polygons.
  //

  for(i = pLevel->nPolygons - 1; i >= 0; --i)
  {
    if ((pLevel->pPolygonData + i)->flags & PF_SELECTED)
    {
      DeletePolygon(pLevel, i);
    }
  }

  //
  //  Delete the selected vertices.
  //

  for(i = pLevel->nVertices - 1; i >= 0; --i)
  {
    if ((pLevel->pVertexData + i)->flags & VF_SELECTED)
    {
      DeleteVertex(pLevel, i);
    }
  }

  SetCursor(hcurSave);

  return;

} // DeleteSelections
Example #9
0
int TopologicalGraph::MakeConnectedVertex()
//returns the initial number of connected components
  {if(debug())DebugPrintf("   CheckPropConnected");
  if(Set().exist(PROP_CONNECTED))return 1;
  Prop1<int>is_connected(Set(),PROP_CONNECTED);
  if(!nv()) return 0;
  if(debug())DebugPrintf("Executing MakeConnectedVertex");
  bool simple = Set().exist(PROP_SIMPLE);
  bool bipartite = Set().exist(PROP_BIPARTITE);
  tvertex v,w;
  tbrin b,b0;
  int ncc = 0;
  svector<tvertex> stack(1,nv()); stack.SetName("TP:stack");
  svector<tvertex> comp(0,nv()+1); comp.clear(); comp.SetName("TP:Comp");
  int rank =0;
  int max_rank = 0;
  tvertex v0 = 1;
  tvertex previous_root = 1;
  int n = nv();
  int m = ne();
  tvertex vv = NewVertex();
  NewEdge(vv,v0); 
  comp[vv] = v0;
  while (max_rank < n)
      {while (comp[v0]!=0) v0++;
      comp[v0] = v0;
      ++ncc;
      if(ncc > 1) NewEdge(vv,v0); 
      previous_root = v0;
      ++max_rank;
      stack[rank + 1] = v0;

      while(rank < max_rank)
          {v = stack[++rank];
          b = b0 = pbrin[v];
          if (b0!=0)
              do {w = vin[-b];
              if(!comp[w])
                  {comp[w] = previous_root;
                  stack[++max_rank] = w;
                  }
              }while((b = cir[b])!= b0);
          }
      }
  if(ne() == m+1)// the graph was connected
     { DeleteVertex(vv);
     if(debug())DebugPrintf("End  MakeConnectedVertex  m=%d",ne());
     return 1;
     }
 if(simple)Prop1<int> simple(Set(),PROP_SIMPLE);
 if(bipartite)Prop1<int> bipartite(Set(),PROP_BIPARTITE);
 if(Set(tvertex()).exist(PROP_COORD)) // Geometric Graph
     {Prop<Tpoint> vcoord(Set(tvertex()),PROP_COORD);
     int deg; 
     tvertex w;
     Tpoint p(.0,.0);
     deg = 0;
     Forall_adj_brins_of_G(b,vv)
       {w = vin[-b]; ++deg;
       p += vcoord[w];
       }
Example #10
0
SSHANDLE * CSSolid::MergeSameVertices(int& nDeleted)
{
	int nMerged = 0;
	nDeleted = 0;
	static SSHANDLE hDeletedList[128];

DoVertices:
	for(int v1 = 0; v1 < m_nVertices; v1++)
	{
		for(int v2 = 0; v2 < m_nVertices; v2++)
		{
			if(v1 == v2)
				continue;	// no!
			if(!VectorCompare(m_Vertices[v1].pos, m_Vertices[v2].pos))
			{	// no match
				continue;
			}

			++nMerged;

			// same vertices - kill v1, set edge refs to use v2.
			SSHANDLE hV1 = m_Vertices[v1].id;
			SSHANDLE hV2 = m_Vertices[v2].id;
			
			hDeletedList[nDeleted++] = hV1;
			
			DeleteVertex(v1);

			int nAffected;
			CSSEdge **ppEdges = FindAffectedEdges(&hV1, 1, nAffected);

			// run through edges and change references
			for(int e = 0; e < nAffected; e++)
			{
				if(ppEdges[e]->hvStart == hV1)
					ppEdges[e]->hvStart = hV2;
				if(ppEdges[e]->hvEnd == hV1)
					ppEdges[e]->hvEnd = hV2;
				CalcEdgeCenter(ppEdges[e]);
			}
			
			goto DoVertices;
		}
	}

	if(!nMerged)
		return NULL;

	int e;

	// kill edges that have same vertices
	for(e = 0; e < m_nEdges; e++)
	{
		CSSEdge &edge = m_Edges[e];

		if(edge.hvStart != edge.hvEnd)
			continue;	// edge is OK

		hDeletedList[nDeleted++] = edge.id;

		DeleteEdge(e);
		--e;
	}

	// kill similar edges (replace in faces too)
DoEdges:
	for(e = 0; e < m_nEdges; e++)
	{
		CSSEdge &edge = m_Edges[e];

		for(int e2 = 0; e2 < m_nEdges; e2++)
		{
			if(e == e2)
				continue;

			CSSEdge &edge2 = m_Edges[e2];

			if(!((edge2.hvStart == edge.hvStart && edge2.hvEnd == edge.hvEnd) || 
				(edge2.hvEnd == edge.hvStart && edge2.hvStart == edge.hvEnd)))
				continue;

			// we're going to delete edge2.
			SSHANDLE id2 = edge2.id;
			SSHANDLE id1 = edge.id;

			for(int f = 0; f < m_nFaces; f++)
			{
				CSSFace& face = m_Faces[f];
				for(int ef = 0; ef < face.nEdges; ef++)
				{
					if(face.Edges[ef] == id2)
					{
						face.Edges[ef] = id1;
						break;
					}
				}
			}

			hDeletedList[nDeleted++] = id2;
			DeleteEdge(e2);

			goto DoEdges;
		}
	}

	// delete concurrent edge references in face
	for(int f = 0; f < m_nFaces; f++)
	{
		CSSFace& face = m_Faces[f];

DoConcurrentEdges:
		for(int ef1 = 0; ef1 < face.nEdges; ef1++)
		{
			for(int ef2 = 0; ef2 < face.nEdges; ef2++)
			{
				if(ef2 == ef1)
					continue;

				if(face.Edges[ef1] != face.Edges[ef2])
					continue;
			
				// delete this ref
				memcpy(&face.Edges[ef2], &face.Edges[ef2+1], (face.nEdges-ef2) * 
					sizeof(face.Edges[0]));
				--face.nEdges;

				goto DoConcurrentEdges;
			}
		}

		if(face.nEdges < 3)
		{
			// kill this face
			hDeletedList[nDeleted++] = face.id;
			DeleteFace(f);
			--f;
		}
	}

	return hDeletedList;
}