openNURBS ERROR # 1 opennurbs_mesh.cpp.3496 ON_Mesh_SetClosedHelper(): Mesh is corrupt or collapsed

Hello,

Occasionally Rhino will trigger a break point somewhere in Redraw. This is error is then output to the Output window. I’ve searched the API documentation and all header files but I can’t find any reference or documentation on ON_Mesh_SetClosedHelper().

Additionally, I’m checking all my ON_Mesh objects before adding them to the document. I’m calling both IsValid() and InvalidFaceCount() before adding the mesh to the document. However, I’m still getting the error.

So, what exactly does this error mean? What triggers it? What do I need to look for in my mesh to prevent this error? I’m fine with not adding the mesh to the document if it’s bad. I just don’t know what to look for.

-Luke

Does it give problems in the Release version?
If not, then it may be nothing more than internal error handling and recovery. I have experienced similar behaviour when intersecting an ON_Brep where the Debug version of Rhino would halt with a “DebugBreak”, but the Release version of Rhino did not give any error or problems. The explanation was that the DebugBreak is basically an error being handled internally in the Rhino code.

Also, looking into the C++ code of OpenNURBS I find the code below. The error is listed about halfway and has to do, as far as I can tell, with problems building an edge structure.

static void ON_Mesh_SetClosedHelper( 
          bool bClosedOnly,
          const ON_Mesh& mesh,
          const char& m_mesh_is_manifold,
          const char& m_mesh_is_oriented
          )
{
  // thread safe lazy evaluation for mesh's m_mesh_is_... flags
  // Sets: m_mesh_is_closed.
  //       If bClosedOnly is false, also sets m_mesh_is_manifold and m_mesh_is_oriented
  int is_closed = 0;
  char is_manifold = 2;
  char is_oriented = 2;
  for (;;)
  {
    const int Vcount = mesh.m_V.Count();
    const int Fcount = mesh.m_F.Count();
    if ( Vcount < 3 || Fcount < 1 )
    {
      ON_ERROR("Mesh is not valid.");
      break;
    }
    if ( bClosedOnly && (Vcount < 4 || Fcount < 4) )
    {
      // not closed - don't waste any more time.
      break;
    }

    int i, j;
    int Vidbuffer[256];
    int* Vid = mesh.GetVertexLocationIds( 
                     1,
                     (Vcount*sizeof(*Vid) <= sizeof(Vidbuffer) ? &Vidbuffer[0] : 0),
                     0
                    );
    if ( 0 == Vid )
    {
      ON_ERROR("Mesh has corrupt vertex information.");
      bClosedOnly = false;
      break;
    }

    // build an edge list where the "vertex" indices identify unique 3d locations
    ON_3dex* E_list = (ON_3dex*)onmalloc(4*Fcount*sizeof(E_list[0]));
    ON_3dex E;
    int Vid0;
    const int* fvi;
    int E_count = 0;
    const ON_MeshFace* F = mesh.m_F.Array();
    for ( j = 0; j < Fcount; j++ )
    {
      fvi = F[j].vi;
      E.i = Vid[fvi[0]];
      Vid0 = E.j = Vid[fvi[1]];
      if ( E.i == E.j )
        break;
      if ( E.i > E.j )
      {
        i = E.i; E.i = E.j; E.j = i;
        E.k = 1;
      }
      else
      {
        E.k = 0;
      }
      E_list[E_count++] = E;

      E.i = Vid0;
      Vid0 = E.j = Vid[fvi[2]];
      if ( E.i == E.j )
        break;
      if ( E.i > E.j )
      {
        i = E.i; E.i = E.j; E.j = i;
        E.k = 1;
      }
      else
      {
        E.k = 0;
      }
      E_list[E_count++] = E;

      if ( fvi[2] != fvi[3] )
      {
        // quad
        E.i = Vid0;
        Vid0 = E.j = Vid[fvi[3]];
        if ( E.i == E.j )
          break;
        if ( E.i > E.j )
        {
          i = E.i; E.i = E.j; E.j = i;
          E.k = 1;
        }
        else
        {
          E.k = 0;
        }
        E_list[E_count++] = E;
      }

      E.i = Vid0;
      E.j = Vid[fvi[0]];
      if ( E.i == E.j )
        break;
      if ( E.i > E.j )
      {
        i = E.i; E.i = E.j; E.j = i;
        E.k = 1;
      }
      else
      {
        E.k = 0;
      }
      E_list[E_count++] = E;
    }
    if ( Vid != &Vidbuffer[0] )
      onfree(Vid);

    if ( E_count < 3 || j != Fcount )
    {
      ON_ERROR("Mesh is corrupt or collapsed");
      bClosedOnly = false;
      break;
    }

    // sort the the edges
    ON_hsort_3dex(E_list,E_count);

    // Look for duplicate edges.  If we find an edge with no duplicate,
    // then the mesh is open.  It is possible that degenerate meshes,
    // like a flattened box, will be flagged as closed.
    is_closed = (Fcount >= 4 && E_count >= 6) ? 1 : 0;
    is_oriented = 1;
    is_manifold = 1;
    i = -1;
    if ( !bClosedOnly || 1 == is_closed ) for ( i = 0; i < E_count; /*empty iterator*/ )
    {
      E = E_list[i];
      if ( ++i >= E_count )
      {
        // boundary edge (and the last edge in our list)
        is_closed = 0;
        break;
      }

      if ( E.i != E_list[i].i || E.j != E_list[i].j )
      {
        // boundary edge
        is_closed = 0;
        if ( 2 == is_oriented && 2 == is_manifold )
        {
          bClosedOnly = false;
          break;
        }
        if ( bClosedOnly )
          break; // don't spend time with further testing
        continue;
      }

      if ( E.k == E_list[i].k )
      {
        // opposite face normals along this edge - mesh is not oriented
        is_oriented = 2; 
      }

      if ( ++i >= E_count || E.i != E_list[i].i || E.j != E_list[i].j )
      {
        // two faces share this edge
        continue;
      }

      // three or more faces share this edge - mesh is not oriented manifold
      is_oriented = 2; 
      is_manifold = 2; 
      if ( 0 == is_closed )
      {
        bClosedOnly = false;
        break;
      }

      // Check for more faces sharing this edge.
      for ( i++; i < E_count; i++ )
      {
        if ( E.i != E_list[i].i || E.j != E_list[i].j )
        {
          // the edges E and Eid_list[i] are in different locations
          break;
        }
      }
    }
    if ( i >= E_count )
    {
      // is_manifold and is_oriented are set correctly
      bClosedOnly = false;
    }

    onfree(E_list);

    break;
  }

  const_cast<ON_Mesh&>(mesh).SetClosed(is_closed);
  if ( !bClosedOnly )
  {
    // is_manifold and is_oriented are set correctly
    if ( 2 == is_manifold )
      is_oriented = 2;
    const_cast<char&>(m_mesh_is_manifold) = is_manifold;
    const_cast<char&>(m_mesh_is_oriented) = is_oriented;
  }
}

Thanks menno!

-Luke