#include "readPLY.h"
#include <string>
#include <set>
#include <fstream>
#include <iostream>
#include <Eigen/Core>

#include "tinyply.h"


namespace igl 
{

template <typename T, typename Derived>
IGL_INLINE bool _tinyply_buffer_to_matrix(
  tinyply::PlyData & D, 
  Eigen::PlainObjectBase<Derived> & M,
  size_t rows,
  size_t cols )
{
  Eigen::Map< Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> > 
    _map( reinterpret_cast<T *>( D.buffer.get()), rows, cols );

  M = _map.template cast<typename Derived::Scalar>();
  return true;
}



template <typename Derived> 
IGL_INLINE bool tinyply_buffer_to_matrix(
  tinyply::PlyData & D, 
  Eigen::PlainObjectBase<Derived> & M,
  size_t rows,
  size_t cols )
{
  switch(D.t)
  {
    case tinyply::Type::INT8 :
      return   _tinyply_buffer_to_matrix<int8_t,Derived>(D, M,rows,cols);
    case tinyply::Type::UINT8 :
      return   _tinyply_buffer_to_matrix<uint8_t,Derived>(D, M,rows,cols);
    case tinyply::Type::INT16 :
      return   _tinyply_buffer_to_matrix<int16_t,Derived>(D, M,rows,cols);
    case tinyply::Type::UINT16 :
      return   _tinyply_buffer_to_matrix<uint16_t,Derived>(D, M,rows,cols);
    case tinyply::Type::INT32 :
      return   _tinyply_buffer_to_matrix<int32_t,Derived>(D, M,rows,cols);
    case tinyply::Type::UINT32 :
      return   _tinyply_buffer_to_matrix<uint32_t,Derived>(D, M,rows,cols);
    case tinyply::Type::FLOAT32 :
      return   _tinyply_buffer_to_matrix<float,Derived>(D, M,rows,cols);
    case tinyply::Type::FLOAT64 :
      return   _tinyply_buffer_to_matrix<double,Derived>(D, M,rows,cols);
    default:
      return false;
  }
}


template <typename T, typename Derived>
IGL_INLINE bool _tinyply_tristrips_to_trifaces(
  tinyply::PlyData & D, 
  Eigen::PlainObjectBase<Derived> & M,
  size_t el,
  size_t el_len )
{

  Eigen::Map< Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor> > 
     _map( reinterpret_cast<T *>( D.buffer.get()), el, el_len );

  // to make it more interesting, triangles in triangle strip can be separated by negative index elements
  // 1. count all triangles
  size_t triangles=0;

  // TODO: it's possible to optimize this , i suppose
  for(size_t i=0; i<el; i++)
    for(size_t j=0; j<(el_len-2); j++)
    {
      if(_map(i,j)>=0 && _map(i,j+1)>=0 && _map(i,j+2)>=0) 
        triangles++;
    }

  // 2. convert triangles to faces, skipping over the negative indeces, indicating separate strips
  M.resize(triangles, 3);
  size_t k=0;
  for(size_t i=0; i<el; i++)
  {
    int flip=0;
    for(size_t j=0; j<(el_len-2); j++)
    {
      if(_map(i,j)>=0 && _map(i,j+1)>=0 && _map(i,j+2)>=0)
      {
        // consequtive faces on the same strip have to be flip-flopped, to preserve orientation
        M( k,0 ) = static_cast<typename Derived::Scalar>( _map(i, j ) );
        M( k,1 ) = static_cast<typename Derived::Scalar>( _map(i, j+1+flip ) );
        M( k,2 ) = static_cast<typename Derived::Scalar>( _map(i, j+1+(flip^1) ) );
        k++;
        flip ^= 1;
      } else {
        // reset flip on new strip start
        flip = 0;
      }
    }
  }
  assert(k==triangles);
  return true;
}

template <typename Derived> 
IGL_INLINE bool tinyply_tristrips_to_faces(
  tinyply::PlyData & D, 
  Eigen::PlainObjectBase<Derived> & M,
  size_t el,
  size_t el_len )
{
  switch(D.t)
  {
    case tinyply::Type::INT8 :
      return   _tinyply_tristrips_to_trifaces<int8_t,Derived>(D, M,el,el_len);
    case tinyply::Type::UINT8 :
      return   _tinyply_tristrips_to_trifaces<uint8_t,Derived>(D, M,el,el_len);
    case tinyply::Type::INT16 :
      return   _tinyply_tristrips_to_trifaces<int16_t,Derived>(D, M,el,el_len);
    case tinyply::Type::UINT16 :
      return   _tinyply_tristrips_to_trifaces<uint16_t,Derived>(D, M,el,el_len);
    case tinyply::Type::INT32 :
      return   _tinyply_tristrips_to_trifaces<int32_t,Derived>(D, M,el,el_len);
    case tinyply::Type::UINT32 :
      return   _tinyply_tristrips_to_trifaces<uint32_t,Derived>(D, M,el,el_len);
    case tinyply::Type::FLOAT32 :
      return   _tinyply_tristrips_to_trifaces<float,Derived>(D, M,el,el_len);
    case tinyply::Type::FLOAT64 :
      return   _tinyply_tristrips_to_trifaces<double,Derived>(D, M,el,el_len);
    default:
      return false;
  }
}


inline void read_file_binary(FILE *fp, std::vector<uint8_t>& fileBufferBytes)
{
    if (!ferror(fp))
    {
        fseek(fp,0,SEEK_END);
        size_t sizeBytes = ftell(fp);
        fseek(fp,0,SEEK_SET);
        fileBufferBytes.resize(sizeBytes);

        if(fread((char*)fileBufferBytes.data(), 1, sizeBytes,fp)==sizeBytes)
          return;
    }

    throw std::runtime_error("error reading from file");
}

struct ply_memory_buffer : public std::streambuf
{
    char * p_start {nullptr};
    char * p_end {nullptr};
    size_t size;

    ply_memory_buffer(char const * first_elem, size_t size)
        : p_start(const_cast<char*>(first_elem)), p_end(p_start + size), size(size)
    {
        setg(p_start, p_start, p_end);
    }

    pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode which) override
    {
        if (dir == std::ios_base::cur) gbump(static_cast<int>(off));
        else setg(p_start, (dir == std::ios_base::beg ? p_start : p_end) + off, p_end);
        return gptr() - p_start;
    }

    pos_type seekpos(pos_type pos, std::ios_base::openmode which) override
    {
        return seekoff(pos, std::ios_base::beg, which);
    }
};

struct ply_memory_stream : virtual ply_memory_buffer, public std::istream
{
    ply_memory_stream(char const * first_elem, size_t size)
        : ply_memory_buffer(first_elem, size), std::istream(static_cast<std::streambuf*>(this)) {}
};

template <
  typename DerivedV,
  typename DerivedF,
  typename DerivedE,
  typename DerivedN,
  typename DerivedUV,
  typename DerivedVD,
  typename DerivedFD,
  typename DerivedED
  >
IGL_INLINE bool readPLY(
  FILE *fp,
  Eigen::PlainObjectBase<DerivedV> & V,
  Eigen::PlainObjectBase<DerivedF> & F,
  Eigen::PlainObjectBase<DerivedE> & E,
  Eigen::PlainObjectBase<DerivedN> & N,
  Eigen::PlainObjectBase<DerivedUV> & UV,

  Eigen::PlainObjectBase<DerivedVD> & VD,
  std::vector<std::string> & Vheader,
  Eigen::PlainObjectBase<DerivedFD> & FD,
  std::vector<std::string> & Fheader,
  Eigen::PlainObjectBase<DerivedED> & ED,
  std::vector<std::string> & Eheader,
  std::vector<std::string> & comments
  )
{
  // buffer the whole file in memory 
  // then read from memory buffer
  try
  {
    std::vector<uint8_t> fileBufferBytes;
    read_file_binary(fp,fileBufferBytes);
    ply_memory_stream stream((char*)fileBufferBytes.data(), fileBufferBytes.size());
    return readPLY(stream,V,F,E,N,UV,VD,Vheader,FD,Fheader,ED,Eheader,comments);
  }
  catch(const std::exception& e)
  {
    std::cerr << "ReadPLY error: " << e.what() << std::endl;
  }
  return false;
}



template <
  typename DerivedV,
  typename DerivedF
  >
IGL_INLINE bool readPLY(
  FILE *fp,
  Eigen::PlainObjectBase<DerivedV> & V,
  Eigen::PlainObjectBase<DerivedF> & F
  )
{
  Eigen::MatrixXd N,UV,VD,FD,ED;
  Eigen::MatrixXi E;
  std::vector<std::string> Vheader,Eheader,Fheader,comments;
  return readPLY(fp,V,F,E,N,UV,VD,Vheader,FD,Fheader,ED,Eheader,comments);
}

template <
  typename DerivedV,
  typename DerivedF,
  typename DerivedE
  >
IGL_INLINE bool readPLY(
  FILE *fp,
  Eigen::PlainObjectBase<DerivedV> & V,
  Eigen::PlainObjectBase<DerivedF> & F,
  Eigen::PlainObjectBase<DerivedE> & E
  )
{
  Eigen::MatrixXd N,UV,VD,FD,ED;
  std::vector<std::string> Vheader,Eheader,Fheader,comments;
  return readPLY(fp,V,F,E,N,UV,VD,Vheader,FD,Fheader,ED,Eheader,comments);
}


template <
  typename DerivedV,
  typename DerivedF,
  typename DerivedE,
  typename DerivedN,
  typename DerivedUV,
  typename DerivedVD,
  typename DerivedFD,
  typename DerivedED
  >
IGL_INLINE bool readPLY(
  std::istream & ply_stream,
  Eigen::PlainObjectBase<DerivedV> & V,
  Eigen::PlainObjectBase<DerivedF> & F,
  Eigen::PlainObjectBase<DerivedE> & E,
  Eigen::PlainObjectBase<DerivedN> & N,
  Eigen::PlainObjectBase<DerivedUV> & UV,

  Eigen::PlainObjectBase<DerivedVD> & VD,
  std::vector<std::string> & Vheader,
  Eigen::PlainObjectBase<DerivedFD> & FD,
  std::vector<std::string> & Fheader,
  Eigen::PlainObjectBase<DerivedED> & ED,
  std::vector<std::string> & Eheader,
  std::vector<std::string> & comments
  )
{
  tinyply::PlyFile file;
  file.parse_header(ply_stream);

  std::set<std::string> vertex_std{ "x","y","z", "nx","ny","nz",  "u","v",  "texture_u", "texture_v", "s", "t"};
  std::set<std::string> face_std  { "vertex_index", "vertex_indices"};
  std::set<std::string> edge_std  { "vertex1", "vertex2"}; //non-standard edge indexes

  // Tinyply treats parsed data as untyped byte buffers.
  std::shared_ptr<tinyply::PlyData> vertices, normals, faces, texcoords, edges; 

  // Some ply files contain tristrips instead of faces
  std::shared_ptr<tinyply::PlyData> tristrips;

  std::shared_ptr<tinyply::PlyData> _vertex_data;
  std::vector<std::string> _vertex_header;

  std::shared_ptr<tinyply::PlyData> _face_data;
  std::vector<std::string> _face_header;

  std::shared_ptr<tinyply::PlyData> _edge_data;
  std::vector<std::string> _edge_header;

  for (auto c : file.get_comments())
      comments.push_back(c);
  
  for (auto e : file.get_elements())
  {
      if(e.name == "vertex" ) // found a vertex
      {
        for (auto p : e.properties)
        {
          if(vertex_std.find(p.name) == vertex_std.end())
          {
              _vertex_header.push_back(p.name);
          }
        }
      }
      else if(e.name == "face" ) // found face
      {
        for (auto p : e.properties)
        {
          if(face_std.find(p.name) == face_std.end())
          {
              _face_header.push_back(p.name);
          }
        }
      }
      else if(e.name == "edge" ) // found edge
      {
        for (auto p : e.properties)
        {
          if(edge_std.find(p.name) == edge_std.end())
          {
              _edge_header.push_back(p.name);
          }
        }
      }
      // skip the unknown entries
  }

  // The header information can be used to programmatically extract properties on elements
  // known to exist in the header prior to reading the data. For brevity of this sample, properties
  // like vertex position are hard-coded:  
  try {
    vertices = file.request_properties_from_element("vertex", { "x", "y", "z" });
  }
  catch (const std::exception & ) { }

  try {
    normals = file.request_properties_from_element("vertex", { "nx", "ny", "nz" });
  }
  catch (const std::exception & ) { }

  //Try texture coordinates with several names
  try {
    //texture_u texture_v are the names used by meshlab to store textures
    texcoords = file.request_properties_from_element("vertex", { "texture_u", "texture_v" });
  }
  catch (const std::exception & ) { }
  if (!texcoords)
  {
      try {
        //u v are the naive names
        texcoords = file.request_properties_from_element("vertex", { "u", "v" });
      }
      catch (const std::exception & ) { }

  }
  if (!texcoords)
  {
      try {
        //s t were the names used by blender and the previous libigl PLY reader.
        texcoords = file.request_properties_from_element("vertex", { "s", "t" });
      }
      catch (const std::exception & ) { }

  }

  // Providing a list size hint (the last argument) is a 2x performance improvement. If you have
  // arbitrary ply files, it is best to leave this 0.
  try {
    faces = file.request_properties_from_element( "face", { "vertex_indices" }, 0);
  }
  catch (const std::exception & ) { }

  if (!faces)
  {
      try {
        // alternative name of the elements
        faces = file.request_properties_from_element( "face", { "vertex_index" },0);
      }
      catch (const std::exception & ) { }
  }

  if (!faces)
  {
      try {
        // try using tristrips
        tristrips = file.request_properties_from_element( "tristrips", { "vertex_indices" }, 0);
      }
      catch (const std::exception & ) { }

      if (!tristrips)
      {
          try {
            // alternative name of the elements
            tristrips = file.request_properties_from_element( "tristrips", { "vertex_index" }, 0);
          }
          catch (const std::exception & ) { }
      }
  }

  
  try {
    edges = file.request_properties_from_element("edge", { "vertex1", "vertex2" });
  }
  catch (const std::exception & ) { }
  
  if(! _vertex_header.empty())
    _vertex_data = file.request_properties_from_element( "vertex", _vertex_header);
  if(! _face_header.empty())
    _face_data = file.request_properties_from_element( "face", _face_header);
  if(! _edge_header.empty())
    _edge_data = file.request_properties_from_element( "edge", _edge_header);

  // Parse the geometry data
  file.read(ply_stream);

  if (!vertices || !tinyply_buffer_to_matrix(*vertices,V,vertices->count,3) ) {
    V.resize(0,0);
  }

  if (!normals || !tinyply_buffer_to_matrix(*normals,N,normals->count,3) ) {
    N.resize(0,0);
  }

  if (!texcoords || !tinyply_buffer_to_matrix(*texcoords,UV,texcoords->count,2) ) {
    UV.resize(0,0);
  }

  //HACK: Unfortunately, tinyply doesn't store list size as a separate variable
  if (!faces || !tinyply_buffer_to_matrix(*faces, F, faces->count, faces->buffer.size_bytes()/(tinyply::PropertyTable[faces->t].stride*faces->count) )) {

    if(tristrips) { // need to convert to faces
      // code based on blender importer for ply
      // converting triangle strips into triangles
      // tinyply supports tristrips of the same length only
      size_t el_count = tristrips->buffer.size_bytes()/(tinyply::PropertyTable[tristrips->t].stride*tristrips->count);

      // all strips should have tristrips->count elements
      if(!tinyply_tristrips_to_faces(*tristrips, F , tristrips->count, el_count))
        F.resize(0,0);

    } else {
      F.resize(0,0);
    }
  }

  if(!edges || !tinyply_buffer_to_matrix(*edges,E, edges->count,2)) {
    E.resize(0,0);
  }

  /// convert vertex data:
  Vheader=_vertex_header;
  if(_vertex_header.empty())
  {
    VD.resize(0,0);
  }
  else
  {
    VD.resize(vertices->count,_vertex_header.size());
    tinyply_buffer_to_matrix(*_vertex_data, VD, vertices->count, _vertex_header.size());
  }
  
  /// convert face data:
  Fheader=_face_header;
  if(_face_header.empty())
  {
    FD.resize(0,0);
  }
  else
  {
    FD.resize(faces->count, _face_header.size());
    tinyply_buffer_to_matrix(*_face_data, FD, faces->count, 1);
  }

  /// convert edge data:
  Eheader=_edge_header;
  if(_edge_header.empty())
  {
    ED.resize(0,0);
  }
  else
  {
    ED.resize(_edge_data->count, _edge_header.size());
    tinyply_buffer_to_matrix(*_edge_data, ED, _edge_data->count, _edge_header.size());
  }
  return true;
}


template <
  typename DerivedV,
  typename DerivedF,
  typename DerivedE,
  typename DerivedN,
  typename DerivedUV,
  typename DerivedVD,
  typename DerivedFD,
  typename DerivedED
  >
IGL_INLINE bool readPLY(
  const std::string& ply_file,
  Eigen::PlainObjectBase<DerivedV> & V,
  Eigen::PlainObjectBase<DerivedF> & F,
  Eigen::PlainObjectBase<DerivedE> & E,
  Eigen::PlainObjectBase<DerivedN> & N,
  Eigen::PlainObjectBase<DerivedUV> & UV,

  Eigen::PlainObjectBase<DerivedVD> & VD,
  std::vector<std::string> & VDheader,

  Eigen::PlainObjectBase<DerivedFD> & FD,
  std::vector<std::string> & FDheader,

  Eigen::PlainObjectBase<DerivedED> & ED,
  std::vector<std::string> & EDheader,
  std::vector<std::string> & comments
  )
{

  std::ifstream ply_stream(ply_file, std::ios::binary);
  if (ply_stream.fail())
  {
      std::cerr << "ReadPLY: Error opening file " << ply_file << std::endl;
      return false;
  }
  try
  {
    return readPLY(ply_stream, V, F, E, N, UV, VD, VDheader, FD,FDheader, ED, EDheader, comments );
  } catch (const std::exception& e) {
    std::cerr << "ReadPLY error: " << ply_file << e.what() << std::endl;
  }
  return false;
}


template <
  typename DerivedV,
  typename DerivedF,
  typename DerivedE,
  typename DerivedN,
  typename DerivedUV,
  typename DerivedD
  >
IGL_INLINE bool readPLY(
  const std::string & filename,
  Eigen::PlainObjectBase<DerivedV> & V,
  Eigen::PlainObjectBase<DerivedF> & F,
  Eigen::PlainObjectBase<DerivedE> & E,
  Eigen::PlainObjectBase<DerivedN> & N,
  Eigen::PlainObjectBase<DerivedUV> & UV,
  Eigen::PlainObjectBase<DerivedD> & VD,
  std::vector<std::string> & Vheader
  )
{
  Eigen::MatrixXd FD,ED;
  std::vector<std::string> Fheader,Eheader;
  std::vector<std::string> comments;
  return readPLY(filename,V,F,E,N,UV,VD,Vheader,FD,Fheader,ED,Eheader,comments);
}

template <
  typename DerivedV,
  typename DerivedF,
  typename DerivedE,
  typename DerivedN,
  typename DerivedUV
  >
IGL_INLINE bool readPLY(
  const std::string & filename,
  Eigen::PlainObjectBase<DerivedV> & V,
  Eigen::PlainObjectBase<DerivedF> & F,
  Eigen::PlainObjectBase<DerivedE> & E,
  Eigen::PlainObjectBase<DerivedN> & N,
  Eigen::PlainObjectBase<DerivedUV> & UV
  )
{
  Eigen::MatrixXd VD,FD,ED;
  std::vector<std::string> Vheader,Fheader,Eheader;
  std::vector<std::string> comments;
  return readPLY(filename,V,F,E, N,UV,VD,Vheader,FD,Fheader,ED,Eheader,comments);
}

template <
  typename DerivedV,
  typename DerivedF,
  typename DerivedN,
  typename DerivedUV
  >
IGL_INLINE bool readPLY(
  const std::string & filename,
  Eigen::PlainObjectBase<DerivedV> & V,
  Eigen::PlainObjectBase<DerivedF> & F,
  Eigen::PlainObjectBase<DerivedN> & N,
  Eigen::PlainObjectBase<DerivedUV> & UV
  )
{
  Eigen::MatrixXd VD,FD,ED;
  Eigen::MatrixXi E;
  std::vector<std::string> Vheader,Fheader,Eheader;
  std::vector<std::string> comments;
  return readPLY(filename,V,F,E, N,UV,VD,Vheader,FD,Fheader,ED,Eheader,comments);
}

template <
  typename DerivedV,
  typename DerivedF
  >
IGL_INLINE bool readPLY(
  const std::string & filename,
  Eigen::PlainObjectBase<DerivedV> & V,
  Eigen::PlainObjectBase<DerivedF> & F
  )
{
  Eigen::MatrixXd N,UV;
  Eigen::MatrixXd VD,FD,ED;
  Eigen::MatrixXi E;

  std::vector<std::string> Vheader,Fheader,Eheader;
  std::vector<std::string> comments;
  return readPLY(filename,V,F,E,N,UV,VD,Vheader,FD,Fheader,ED,Eheader,comments);
}

template <
  typename DerivedV,
  typename DerivedF,
  typename DerivedE
  >
IGL_INLINE bool readPLY(
  const std::string & filename,
  Eigen::PlainObjectBase<DerivedV> & V,
  Eigen::PlainObjectBase<DerivedF> & F,
  Eigen::PlainObjectBase<DerivedE> & E
  )
{
  Eigen::MatrixXd N,UV;
  Eigen::MatrixXd VD,FD,ED;

  std::vector<std::string> Vheader,Fheader,Eheader;
  std::vector<std::string> comments;
  return readPLY(filename,V,F,E,N,UV,VD,Vheader,FD,Fheader,ED,Eheader,comments);
}


} //igl namespace

#ifdef IGL_STATIC_LIBRARY
// Explicit template instantiation
template bool igl::readPLY<Eigen::Matrix<double, -1, 3, 0, -1, 3>, Eigen::Matrix<int, -1, 3, 0, -1, 3> >(FILE*, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 3, 0, -1, 3> >&);
template bool igl::readPLY<Eigen::Matrix<double, -1, 3, 1, -1, 3>, Eigen::Matrix<int, -1, 3, 1, -1, 3> >(FILE*, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 3, 1, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 3, 1, -1, 3> >&);
template bool igl::readPLY<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1> >(FILE*, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&);
template bool igl::readPLY<Eigen::Matrix<double, -1, -1, 1, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1> >(FILE*, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 1, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&);
template bool igl::readPLY<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&);
template bool igl::readPLY<Eigen::Matrix<float, -1, 3, 0, -1, 3>, Eigen::Matrix<int, -1, 3, 0, -1, 3> >(FILE*, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, 3, 0, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 3, 0, -1, 3> >&);
template bool igl::readPLY<Eigen::Matrix<float, -1, 3, 1, -1, 3>, Eigen::Matrix<unsigned int, -1, 3, 1, -1, 3> >(FILE*, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, 3, 1, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<unsigned int, -1, 3, 1, -1, 3> >&);
template bool igl::readPLY<Eigen::Matrix<float, -1, 3, 1, -1, 3>, Eigen::Matrix<int, -1, 3, 1, -1, 3> >(FILE*, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, 3, 1, -1, 3> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 3, 1, -1, 3> >&);
template bool igl::readPLY<Eigen::Matrix<float, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1> >(FILE*, Eigen::PlainObjectBase<Eigen::Matrix<float, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&);

template bool igl::readPLY<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, -1, 0, -1, -1> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >&, std::vector<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::allocator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > > >&);
#endif

