Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

More issues with igl::readMESH #1680

Closed
3 tasks done
evouga opened this issue Dec 15, 2020 · 0 comments · Fixed by #2247
Closed
3 tasks done

More issues with igl::readMESH #1680

evouga opened this issue Dec 15, 2020 · 0 comments · Fixed by #2247

Comments

@evouga
Copy link
Contributor

evouga commented Dec 15, 2020

Describe the bug

I've hit a few more bugs in the .mesh parser:

  • Blank lines might include some whitepace before the newline; this is not detected as a comment by the parser.
  • The parser chokes on "Edges" sections in the file.

Here's a failure case "from the wild": https://www.dropbox.com/s/6ovsblr1rnrslm6/cup_85k.mesh?dl=0

I wrote my own parser, from the .mesh spec, as a replacement for my personal project; I didn't create a pull request since

  • this code is barely tested
  • it's still not fully spec-compliant (for example, the code assumes that the three vertex coordinates appear on the same line, whereas I believe the spec allows them to be split by newlines)
  • meshes "in the wild" might violate the spec, and I'm not convinced my parser is strictly more permissive than libigl's.

But I'm leaving the code here in case it's useful:

template <typename DerivedV, typename DerivedF, typename DerivedT>
bool readMESH(
   const std::string mesh_file_name,
   Eigen::PlainObjectBase<DerivedV>& V,
   Eigen::PlainObjectBase<DerivedT>& T,
   Eigen::PlainObjectBase<DerivedF>& F)
{
   V.resize(0, 0);
   T.resize(0, 0);
   F.resize(0, 0);
   auto nextNonComment = [](std::istream& fs, std::string& s) -> bool
   {
       while (true)
       {
           std::getline(fs, s);
           if (!fs)
               return false;
           for (int i = 0; i < s.length(); i++)
           {
               if (s[i] == '#')
                   break;
               else if (!std::isspace(s[i]))
                   return true;
           }
       }
       // unreachable
       return false;
   };

   std::ifstream ifs(mesh_file_name);
   if (!ifs)
   {
       std::cerr << "Error: couldn't read .mesh file: " << mesh_file_name << std::endl;
       return false;
   }

   std::string line;

   // check that first word is MeshVersionFormatted
   if (!nextNonComment(ifs, line))
   {
       std::cerr << "Error: missing MeshVersionFormatted line" << std::endl;
       return false;
   }
   std::stringstream ss(line);
   std::string format;
   int versionid;
   ss >> format >> versionid;
   if (!ss || format != "MeshVersionFormatted")
   {
       std::cerr << "Error: expected \"MeshVersionFormatted (1 or 2)\", read " << line << std::endl;
       return false;
   }
   if (versionid != 1 && versionid != 2)
   {
       std::cerr << "Error: MeshVersionFormatted must be 1 or 2" << std::endl;
       return false;
   }

   // check that third word is Dimension
   if (!nextNonComment(ifs, line))
   {
       std::cerr << "Error: missing Dimension line" << std::endl;
       return false;
   }
   ss = std::stringstream(line);
   std::string dimension;    
   ss >> dimension;
   if (!ss || dimension != "Dimension")
   {
       std::cerr << "Error: expected \"Dimension\", read " << line << std::endl;
       return false;
   }
   int dim;
   ss >> dim;
   if (!ss)
   {
       // not spec compliant, but occurs in the wild
       if (!nextNonComment(ifs, line))
       {
           std::cerr << "Error: missing dimension number" << std::endl;
           return false;
       }
       ss = std::stringstream(line);
       ss >> dim;
       if (!ss)
       {
           std::cerr << "Error: missing dimension number" << std::endl;
           return false;
       }
   }
   if (dim != 3)
   {
       std::cerr << "Error: only Dimension 3 supported not " << dim << std::endl;
       return false;
   }

   while (true)
   {
       if (!nextNonComment(ifs, line))
       {
           std::cerr << "Error: file ended before \"End\" line" << std::endl;
           return false;
       }

       ss = std::stringstream(line);
       std::string sectionid;
       ss >> sectionid;
       if (sectionid == "End")
           return true;
       else if (sectionid == "Vertices")
       {
           // Not spec compliant, but since this was in the IGL code, needed in some cases?
           int nverts;
           ss >> nverts;
           if (!ss)
           {
               // On next line
               if (!nextNonComment(ifs, line))
               {
                   std::cerr << "Error: missing number of vertices following \"Vertices\"" << std::endl;
                   return false;
               }
               ss = std::stringstream(line);
               ss >> nverts;
               if (!ss)
               {
                   std::cerr << "Error: missing number of vertices following \"Vertices\"" << std::endl;
                   return false;
               }
           }
           V.resize(nverts, 3);
           for (int i = 0; i < nverts; i++)
           {
               if (!nextNonComment(ifs, line))
               {
                   std::cerr << "Error: read " << i << " vertices, was expecting " << nverts << std::endl;
                   return false;
               }
               int ref;
               ss = std::stringstream(line);
               ss >> V(i, 0) >> V(i, 1) >> V(i, 2) >> ref;
               if (!ss)
               {
                   std::cerr << "Error: expected \"x y z ref\", read " << line << std::endl;
                   return false;
               }
           }
       }
       else if (sectionid == "Triangles")
       {
           // Not spec compliant, but since this was in the IGL code, needed in some cases?
           int ntris;
           ss >> ntris;
           if (!ss)
           {
               // On next line
               if (!nextNonComment(ifs, line))
               {
                   std::cerr << "Error: missing number of triangles following \"Triangles\"" << std::endl;
                   return false;
               }
               ss = std::stringstream(line);
               ss >> ntris;
               if (!ss)
               {
                   std::cerr << "Error: missing number of triangles following \"Triangles\"" << std::endl;
                   return false;
               }
           }
           F.resize(ntris, 3);
           for (int i = 0; i < ntris; i++)
           {
               if (!nextNonComment(ifs, line))
               {
                   std::cerr << "Error: read " << i << " triangles, was expecting " << ntris << std::endl;
                   return false;
               }
               int ref;
               DerivedF::Scalar v1, v2, v3;
               ss = std::stringstream(line);
               ss >> v1 >> v2 >> v3 >> ref;
               if (!ss)
               {
                   std::cerr << "Error: expected \"v1 v2 v3 ref\", read " << line << std::endl;
                   return false;
               }
               F(i, 0) = v1 - 1;
               F(i, 1) = v2 - 1;
               F(i, 2) = v3 - 1;
           }
       }
       
       else if (sectionid == "Quadrilaterals")
       {
           // Not spec compliant, but since this was in the IGL code, needed in some cases?
           int nquads;
           ss >> nquads;
           if (!ss)
           {
               // On next line
               if (!nextNonComment(ifs, line))
               {
                   std::cerr << "Error: missing number of quadrilaterals following \"Quadrilaterals\"" << std::endl;
                   return false;
               }
               ss = std::stringstream(line);
               ss >> nquads;
               if (!ss)
               {
                   std::cerr << "Error: missing number of quadrilaterals following \"Quadrilaterals\"" << std::endl;
                   return false;
               }
           }
           if (nquads != 0)
           {
               std::cerr << "Error: Only tetrahedral meshes are supported (file contains Quadrilaterals)" << std::endl;
               return false;
           }
       }        
       else if (sectionid == "Tetrahedra")
       {
           // Not spec compliant, but since this was in the IGL code, needed in some cases?
           int ntets;
           ss >> ntets;
           if (!ss)
           {
               // On next line
               if (!nextNonComment(ifs, line))
               {
                   std::cerr << "Error: missing number of tetrahedra following \"Tetrahedra\"" << std::endl;
                   return false;
               }
               ss = std::stringstream(line);
               ss >> ntets;
               if (!ss)
               {
                   std::cerr << "Error: missing number of tetrahedra following \"Tetrahedra\"" << std::endl;
                   return false;
               }
           }
           T.resize(ntets, 4);
           for (int i = 0; i < ntets; i++)
           {
               if (!nextNonComment(ifs, line))
               {
                   std::cerr << "Error: read " << i << " tetrahedra, was expecting " << ntets << std::endl;
                   return false;
               }
               int ref;
               DerivedT::Scalar v1, v2, v3, v4;
               ss = std::stringstream(line);
               ss >> v1 >> v2 >> v3 >> v4 >> ref;
               if (!ss)
               {
                   std::cerr << "Error: expected \"v1 v2 v3 v4 ref\", read " << line << std::endl;
                   return false;
               }
               T(i, 0) = v1 - 1;
               T(i, 1) = v2 - 1;
               T(i, 2) = v3 - 1;
               T(i, 3) = v4 - 1;
           }
       }
       else if (sectionid == "Hexahedra")
       {
           // Not spec compliant, but since this was in the IGL code, needed in some cases?
           int nhexes;
           ss >> nhexes;
           if (!ss)
           {
               // On next line
               if (!nextNonComment(ifs, line))
               {
                   std::cerr << "Error: missing number of hexahedra following \"Hexahedra\"" << std::endl;
                   return false;
               }
               ss = std::stringstream(line);
               ss >> nhexes;
               if (!ss)
               {
                   std::cerr << "Error: missing number of hexahedra following \"Hexahedra\"" << std::endl;
                   return false;
               }
           }
           if (nhexes != 0)
           {
               std::cerr << "Error: Only tetrahedral meshes are supported (file contains Hexahedra)" << std::endl;
               return false;
           }
       }
       else if (sectionid == "Edges")
       {
           // Not spec compliant, but since this was in the IGL code, needed in some cases?
           int nedges;
           ss >> nedges;
           if (!ss)
           {
               // On next line
               if (!nextNonComment(ifs, line))
               {
                   std::cerr << "Error: missing number of edges following \"Edges\"" << std::endl;
                   return false;
               }
               ss = std::stringstream(line);
               ss >> nedges;
               if (!ss)
               {
                   std::cerr << "Error: missing number of edges following \"Edges\"" << std::endl;
                   return false;
               }
           }
           for (int i = 0; i < nedges; i++)
           {
               if (!nextNonComment(ifs, line))
               {
                   std::cerr << "Error: read " << i << " edges, was expecting " << nedges << std::endl;
                   return false;
               }
               int e1, e2, ref;
               ss = std::stringstream(line);
               ss >> e1 >> e2 >> ref;
               if (!ss)
               {
                   std::cerr << "Error: expected \"e1 e2 ref\", read " << line << std::endl;
                   return false;
               }
           }
       }
       else
       {
           std::cerr << "Error: unknown section ID " << sectionid << std::endl;
           return false;
       }
   }

   // unreachable
   return false;
}

Check all that apply (change to [x])

  • Windows
  • macOS
  • Linux
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant