Browse Source

Merge pull request #1422 from vfonov/tinyply_reader_writer

Replace .ply reader/writer with tinyply library
Alec Jacobson 5 years ago
parent
commit
10dc1fd9f8

+ 1 - 1
cmake/LibiglDownloadExternal.cmake

@@ -175,7 +175,7 @@ function(igl_download_test_data)
 	igl_download_project_aux(test_data
 	igl_download_project_aux(test_data
 		"${LIBIGL_EXTERNAL}/../tests/data"
 		"${LIBIGL_EXTERNAL}/../tests/data"
 		GIT_REPOSITORY https://github.com/libigl/libigl-tests-data
 		GIT_REPOSITORY https://github.com/libigl/libigl-tests-data
-		GIT_TAG        5994ecdab65aebc6c218c4c6f35e7822acf6fe99
+		GIT_TAG        b5dddf45e329af685cd107e38770a28cfc18eb15
 	)
 	)
 endfunction()
 endfunction()
 
 

+ 6 - 12
include/igl/guess_extension.cpp

@@ -3,7 +3,6 @@
 #include <string.h>
 #include <string.h>
 
 
 #include "is_stl.h"
 #include "is_stl.h"
-#include "ply.h"
 
 
 IGL_INLINE void igl::guess_extension(FILE * fp, std::string & guess)
 IGL_INLINE void igl::guess_extension(FILE * fp, std::string & guess)
 {
 {
@@ -20,18 +19,13 @@ IGL_INLINE void igl::guess_extension(FILE * fp, std::string & guess)
     rewind(fp);
     rewind(fp);
     return f;
     return f;
   };
   };
-  const auto is_ply = [](FILE * ply_file) -> bool
+  const auto is_ply = [](FILE * fp) -> bool
   {
   {
-    int nelems;
-    char ** elem_names;
-    igl::ply::PlyFile * in_ply = igl::ply::ply_read(ply_file,&nelems,&elem_names);
-    if(in_ply==NULL)
-    {
-      return false;
-    }
-    free(in_ply);
-    rewind(ply_file);
-    return true;
+    char header[1000];
+    const std::string PLY("ply");
+    bool f = (fscanf(fp,"%s\n",header)==1 && (std::string(header).compare(0, PLY.length(), PLY)==0 ));
+    rewind(fp);
+    return f;
   };
   };
   const auto is_wrl = [](FILE * wrl_file)->bool
   const auto is_wrl = [](FILE * wrl_file)->bool
   {
   {

+ 0 - 3180
include/igl/ply.h

@@ -1,3180 +0,0 @@
-#ifndef IGL_PLY_H
-#define IGL_PLY_H
-/*
-
-Header for PLY polygon files.
-
-- Greg Turk, March 1994
-
-A PLY file contains a single polygonal _object_.
-
-An object is composed of lists of _elements_.  Typical elements are
-vertices, faces, edges and materials.
-
-Each type of element for a given object has one or more _properties_
-associated with the element type.  For instance, a vertex element may
-have as properties three floating-point values x,y,z and three unsigned
-chars for red, green and blue.
-
----------------------------------------------------------------
-
-Copyright (c) 1994 The Board of Trustees of The Leland Stanford
-Junior University.  All rights reserved.   
-  
-Permission to use, copy, modify and distribute this software and its   
-documentation for any purpose is hereby granted without fee, provided   
-that the above copyright notice and this permission notice appear in   
-all copies of this software and that you do not sell the software.   
-  
-THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND,   
-EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY   
-WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.   
-
-*/
-
-/*
---------------------------------------------------------------------------------
-Joao Fradinho Oliveira, July 2005
-Copyright (c) 2005 University College London 
-copyright conditions as above
-
-update for ply reading of multi OS ply files, in any OS (Unix, Macintosh, PC)
---------------------------------------------------------------------------------
-
-ply_open_for_reading
-
-* was changed to always open files in binary mode, files written in ascii can also be
-read with this binary mode.
-
-* allows opening of filenames that are alias files in macintosh
-
-* code tested on pc and mac
-
-
-get_words
-
-* was changed to handle line breaks in UNIX, MACINTOSH, PC, it resets the file pointer
-accordingly for the next read.
-
-
-NOTES:
-The ply file, has always an ascii part for the header, and a binary or ascii
-part for the data.
-The header part in ascii, dictates that linebreaks are used, this make models 
-operating system dependent, as a line break in unix is indicated with the escape character \n,
-on a macintosh, with \r, and on a pc with \r\n  <--2 unsigned chars, 2 bytes, instead of 1 byte.
-
-get_words allows reading of any OS, text editors such as BBEdit do not save the linebreaks
-properly to target OSs with binary files.  
-
-*/
-
-#ifndef __PLY_H__
-#define __PLY_H__
-
-
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <stddef.h>
-#include <string.h>
-
-namespace igl {
-    namespace ply {
-    
-#define PLY_ASCII         1      /* ascii PLY file */
-#define PLY_BINARY_BE     2      /* binary PLY file, big endian */
-#define PLY_BINARY_LE     3      /* binary PLY file, little endian */
-#define PLY_BINARY_NATIVE 4      /* binary PLY file, same endianness as
-				    current architecture */
-    
-#define PLY_OKAY    0           /* ply routine worked okay */
-#define PLY_ERROR  -1           /* error in ply routine */
-
-/* scalar data types supported by PLY format */
-
-#define PLY_START_TYPE 0
-#define PLY_CHAR       1
-#define PLY_SHORT      2
-#define PLY_INT        3
-#define PLY_UCHAR      4
-#define PLY_USHORT     5
-#define PLY_UINT       6
-#define PLY_FLOAT      7
-#define PLY_DOUBLE     8
-#define PLY_END_TYPE   9
-
-#define  PLY_SCALAR  0
-#define  PLY_LIST    1
-
-
-
-
-typedef struct PlyProperty {    /* description of a property */
-
-  const char *name;                           /* property name */
-  int external_type;                    /* file's data type */
-  int internal_type;                    /* program's data type */
-  int offset;                           /* offset bytes of prop in a struct */
-
-  int is_list;                          /* 1 = list, 0 = scalar */
-  int count_external;                   /* file's count type */
-  int count_internal;                   /* program's count type */
-  int count_offset;                     /* offset byte for list count */
-
-} PlyProperty;
-
-typedef struct PlyElement {     /* description of an element */
-  const char *name;                   /* element name */
-  int num;                      /* number of elements in this object */
-  int size;                     /* size of element (bytes) or -1 if variable */
-  int nprops;                   /* number of properties for this element */
-  PlyProperty **props;          /* list of properties in the file */
-  char *store_prop;             /* flags: property wanted by user? */
-  int other_offset;             /* offset to un-asked-for props, or -1 if none*/
-  int other_size;               /* size of other_props structure */
-} PlyElement;
-
-typedef struct PlyOtherProp {   /* describes other properties in an element */
-  const char *name;                   /* element name */
-  int size;                     /* size of other_props */
-  int nprops;                   /* number of properties in other_props */
-  PlyProperty **props;          /* list of properties in other_props */
-} PlyOtherProp;
-
-typedef struct OtherData { /* for storing other_props for an other element */
-  void *other_props;
-} OtherData;
-
-typedef struct OtherElem {     /* data for one "other" element */
-  char *elem_name;             /* names of other elements */
-  int elem_count;              /* count of instances of each element */
-  OtherData **other_data;      /* actual property data for the elements */
-  PlyOtherProp *other_props;   /* description of the property data */
-} OtherElem;
-
-typedef struct PlyOtherElems {  /* "other" elements, not interpreted by user */
-  int num_elems;                /* number of other elements */
-  OtherElem *other_list;        /* list of data for other elements */
-} PlyOtherElems;
-
-typedef struct PlyFile {        /* description of PLY file */
-  FILE *fp;                     /* file pointer */
-  int file_type;                /* ascii or binary */
-  float version;                /* version number of file */
-  int nelems;                   /* number of elements of object */
-  PlyElement **elems;           /* list of elements */
-  int num_comments;             /* number of comments */
-  char **comments;              /* list of comments */
-  int num_obj_info;             /* number of items of object information */
-  char **obj_info;              /* list of object info items */
-  PlyElement *which_elem;       /* which element we're currently writing */
-  PlyOtherElems *other_elems;   /* "other" elements from a PLY file */
-} PlyFile;
-
-/* memory allocation */
-extern char *my_alloc();
-#define myalloc(mem_size) my_alloc((mem_size), __LINE__, __FILE__)
-
-#ifndef ALLOCN
-#define REALLOCN(PTR,TYPE,OLD_N,NEW_N)							\
-        {										\
-	    if ((OLD_N) == 0)                                           		\
-	    {   ALLOCN((PTR),TYPE,(NEW_N));}                            		\
-	    else									\
-	    {								    		\
-	       (PTR) = (TYPE *)realloc((PTR),(NEW_N)*sizeof(TYPE));			\
-	       if (((PTR) == NULL) && ((NEW_N) != 0))					\
-	       {									\
-		   fprintf(stderr, "Memory reallocation failed on line %d in %s\n", 	\
-		           __LINE__, __FILE__);                             		\
-		   fprintf(stderr, "  tried to reallocate %d->%d\n",       		\
-			   (OLD_N), (NEW_N));                              		\
-		   exit(-1);								\
-	       }									\
-	       if ((NEW_N)>(OLD_N))							\
-		   memset((char *)(PTR)+(OLD_N)*sizeof(TYPE), 0,			\
-		          ((NEW_N)-(OLD_N))*sizeof(TYPE));				\
-	    }										\
-	}
-
-#define  ALLOCN(PTR,TYPE,N) 					\
-	{ (PTR) = (TYPE *) calloc(((unsigned)(N)),sizeof(TYPE));\
-	  if ((PTR) == NULL) {    				\
-	  fprintf(stderr, "Memory allocation failed on line %d in %s\n", \
-		 __LINE__, __FILE__);                           \
-	  exit(-1);                                             \
-	  }							\
-	}
-
-
-#define FREE(PTR)  { free((PTR)); (PTR) = NULL; }
-#endif
-    
-
-/*** delcaration of routines ***/
-
-inline int get_native_binary_type2();
-
-inline PlyFile *ply_write(FILE *, int,const char **, int);
-inline PlyFile *ply_open_for_writing(char *, int,const char **, int, float *);
-inline void ply_describe_element(PlyFile *, const char *, int, int, PlyProperty *);
-inline void ply_describe_property(PlyFile *, const char *, PlyProperty *);
-inline void ply_element_count(PlyFile *, const char *, int);
-inline void ply_header_complete(PlyFile *);
-inline void ply_put_element_setup(PlyFile *, const char *);
-inline void ply_put_element(PlyFile *, void *, int*);
-inline void ply_put_comment(PlyFile *, char *);
-inline void ply_put_obj_info(PlyFile *, char *);
-inline PlyFile *ply_read(FILE *, int *, char ***);
-inline PlyFile *ply_open_for_reading( const char *, int *, char ***, int *, float *);
-inline PlyProperty **ply_get_element_description(PlyFile *, const char *, int*, int*);
-inline void ply_get_element_setup( PlyFile *, const char *, int, PlyProperty *);
-inline void ply_get_property(PlyFile *, const char *, PlyProperty *);
-inline PlyOtherProp *ply_get_other_properties(PlyFile *, const char *, int);
-inline void ply_get_element(PlyFile *, void *, int *);
-inline char **ply_get_comments(PlyFile *, int *);
-inline char **ply_get_obj_info(PlyFile *, int *);
-inline void ply_close(PlyFile *);
-inline void ply_get_info(PlyFile *, float *, int *);
-inline PlyOtherElems *ply_get_other_element (PlyFile *, const char *, int);
-inline void ply_describe_other_elements ( PlyFile *, PlyOtherElems *);
-inline void ply_put_other_elements (PlyFile *);
-inline void ply_free_other_elements (PlyOtherElems *);
-inline void ply_describe_other_properties(PlyFile *, PlyOtherProp *, int);
-
-inline int equal_strings(const char *, const char *);
-
-
-}
-}
-#endif /* !__PLY_H__ */
-/*
-
-The interface routines for reading and writing PLY polygon files.
-
-Greg Turk, February 1994
-
----------------------------------------------------------------
-
-A PLY file contains a single polygonal _object_.
-
-An object is composed of lists of _elements_.  Typical elements are
-vertices, faces, edges and materials.
-
-Each type of element for a given object has one or more _properties_
-associated with the element type.  For instance, a vertex element may
-have as properties the floating-point values x,y,z and the three unsigned
-chars representing red, green and blue.
-
----------------------------------------------------------------
-
-Copyright (c) 1994 The Board of Trustees of The Leland Stanford
-Junior University.  All rights reserved.   
-  
-Permission to use, copy, modify and distribute this software and its   
-documentation for any purpose is hereby granted without fee, provided   
-that the above copyright notice and this permission notice appear in   
-all copies of this software and that you do not sell the software.   
-  
-THE SOFTWARE IS PROVIDED "AS IS" AND WITHOUT WARRANTY OF ANY KIND,   
-EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY   
-WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.   
-
-*/
-
-/*
---------------------------------------------------------------------------------
-Joao Fradinho Oliveira, July 2005
-University College London 
-
-update for ply reading of multi OS ply files, in any OS (Unix, Macintosh, PC)
---------------------------------------------------------------------------------
-
-ply_open_for_reading
-
-* was changed to always open files in binary mode, files written in ascii can also be
-read with this binary mode.
-
-* allows opening of filenames that are alias files in macintosh
-
-* code tested on pc and mac
-
-
-get_words
-
-* was changed to handle line breaks in UNIX, MACINTOSH, PC, it resets the file pointer
-accordingly for the next read.
-
-
-NOTES:
-The ply file, has always an ascii part for the header, and a binary or ascii
-part for the data.
-The header part in ascii, dictates that linebreaks are used, this make models 
-operating system dependent, as a line break in unix is indicated with the escape character \n,
-on a macintosh, with \r, and on a pc with \r\n  <--2 unsigned chars, 2 bytes, instead of 1 byte.
-
-get_words allows reading of any OS, text editors such as BBEdit do not save the linebreaks
-properly to target OSs with binary files.  
-
-*/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <math.h>
-#include <string.h>
-//#include "ply.h"
-
-
-namespace igl {
-    namespace ply {
-
-
-// Use unnamed namespace to avoid duplicate symbols
-/*
-namespace
-{
-const char *type_names[] = {
-"invalid",
-"char", "short", "int",
-"uchar", "ushort", "uint",
-"float", "double",
-};
-
-//  names of scalar types 
-const char *alt_type_names[] = { 
-"invalid",
-"int8", "int16", "int32", "uint8", "uint16", "uint32", "float32", "float64",
-};
-
-int ply_type_size[] = {
-  0, 1, 2, 4, 1, 2, 4, 4, 8
-};
-}
-
-typedef union
-{
-      int  int_value;
-      char byte_values[sizeof(int)];
-} endian_test_type;
-
-
-namespace
-{
-static int native_binary_type = -1;
-static int types_checked = 0;
-}
-*/
-
-#define NO_OTHER_PROPS  -1
-
-#define DONT_STORE_PROP  0
-#define STORE_PROP       1
-
-#define OTHER_PROP       0
-#define NAMED_PROP       1
-
-/* returns 1 if strings are equal, 0 if not */
-inline int equal_strings(const char *, const char *);
-
-/* find an element in a plyfile's list */
-inline PlyElement *find_element(PlyFile *, const char *);
-
-/* find a property in an element's list */
-inline PlyProperty *find_property(PlyElement *, const char *, int *);
-
-/* write to a file the word describing a PLY file data type */
-inline void write_scalar_type (FILE *, int);
-
-/* read a line from a file and break it up into separate words */
-inline char **get_words(FILE *, int *, char **);
-inline char **old_get_words(FILE *, int *);
-
-/* write an item to a file */
-inline void write_binary_item(FILE *, int, int, unsigned int, double, int, int*);
-inline void write_ascii_item(FILE *, int, unsigned int, double, int);
-inline double old_write_ascii_item(FILE *, char *, int);
-
-/* add information to a PLY file descriptor */
-inline void add_element(PlyFile *, char **);
-inline void add_property(PlyFile *, char **);
-inline void add_comment(PlyFile *, char *);
-inline void add_obj_info(PlyFile *, char *);
-
-/* copy a property */
-inline void copy_property(PlyProperty *, PlyProperty *);
-
-/* store a value into where a pointer and a type specify */
-inline void store_item(char *, int, int, unsigned int, double);
-
-/* return the value of a stored item */
-inline void get_stored_item( void *, int, int *, unsigned int *, double *);
-
-/* return the value stored in an item, given ptr to it and its type */
-inline double get_item_value(char *, int);
-
-/* get binary or ascii item and store it according to ptr and type */
-inline void get_ascii_item(char *, int, int *, unsigned int *, double *);
-inline void get_binary_item(FILE *, int, int, int *, unsigned int *, double *, int*);
-
-/* get a bunch of elements from a file */
-inline void ascii_get_element(PlyFile *, char *);
-inline void binary_get_element(PlyFile *, char *, int*);
-
-/* memory allocation */
-inline char *my_alloc(int, int, const char *);
-
-/* byte ordering */
-inline void get_native_binary_type(int*);
-inline void swap_bytes(char *, int);
-
-inline int check_types();
-
-
-/*************/
-/*  Writing  */
-/*************/
-
-
-/******************************************************************************
-Given a file pointer, get ready to write PLY data to the file.
-
-Entry:
-  fp         - the given file pointer
-  nelems     - number of elements in object
-  elem_names - list of element names
-  file_type  - file type, either ascii or binary
-
-Exit:
-  returns a pointer to a PlyFile, used to refer to this file, or NULL if error
-******************************************************************************/
-
-inline PlyFile *ply_write(
-  FILE *fp,
-  int nelems,
-  const char **elem_names,
-  int file_type
-)
-{
-  int i;
-  PlyFile *plyfile;
-  PlyElement *elem;
-
-  /* check for NULL file pointer */
-  if (fp == NULL)
-    return (NULL);
-	
-	int native_binary_type = -1;
-	int types_checked = 0;
-  if (native_binary_type == -1)
-     native_binary_type = get_native_binary_type2();
-  if (!types_checked)
-     types_checked = check_types();
-  
-  /* create a record for this object */
-
-  plyfile = (PlyFile *) myalloc (sizeof (PlyFile));
-  if (file_type == PLY_BINARY_NATIVE)
-     plyfile->file_type = native_binary_type;
-  else
-     plyfile->file_type = file_type;
-  plyfile->num_comments = 0;
-  plyfile->num_obj_info = 0;
-  plyfile->nelems = nelems;
-  plyfile->version = 1.0;
-  plyfile->fp = fp;
-  plyfile->other_elems = NULL;
-
-  /* tuck aside the names of the elements */
-
-  plyfile->elems = (PlyElement **) myalloc (sizeof (PlyElement *) * nelems);
-  for (i = 0; i < nelems; i++) {
-    elem = (PlyElement *) myalloc (sizeof (PlyElement));
-    plyfile->elems[i] = elem;
-    elem->name = strdup (elem_names[i]);
-    elem->num = 0;
-    elem->nprops = 0;
-  }
-
-  /* return pointer to the file descriptor */
-  return (plyfile);
-}
-
-
-/******************************************************************************
-Open a polygon file for writing.
-
-Entry:
-  filename   - name of file to read from
-  nelems     - number of elements in object
-  elem_names - list of element names
-  file_type  - file type, either ascii or binary
-
-Exit:
-  version - version number of PLY file
-  returns a file identifier, used to refer to this file, or NULL if error
-******************************************************************************/
-
-inline PlyFile *ply_open_for_writing(
-  const char *filename,
-  int nelems,
-  const char **elem_names,
-  int file_type,
-  float *version
-)
-{
-  PlyFile *plyfile;
-  char *name;
-  FILE *fp;
-
-  /* tack on the extension .ply, if necessary */
-
-  name = (char *) myalloc (sizeof (char) * (strlen (filename) + 5));
-  strcpy (name, filename);
-  if (strlen (name) < 4 ||
-      strcmp (name + strlen (name) - 4, ".ply") != 0)
-      strcat (name, ".ply");
-
-  /* open the file for writing */
-
-  fp = fopen (name, "w");
-  if (fp == NULL) {
-    free(name);
-    return (NULL);
-  }
-
-  free(name);
-
-  /* create the actual PlyFile structure */
-
-  plyfile = ply_write (fp, nelems, elem_names, file_type);
-  if (plyfile == NULL)
-    return (NULL);
-
-  /* say what PLY file version number we're writing */
-  *version = plyfile->version;
-
-  /* return pointer to the file descriptor */
-  return (plyfile);
-}
-
-
-/******************************************************************************
-Describe an element, including its properties and how many will be written
-to the file.
-
-Entry:
-  plyfile   - file identifier
-  elem_name - name of element that information is being specified about
-  nelems    - number of elements of this type to be written
-  nprops    - number of properties contained in the element
-  prop_list - list of properties
-******************************************************************************/
-
-inline void ply_describe_element(
-  PlyFile *plyfile,
-  const char *elem_name,
-  int nelems,
-  int nprops,
-  PlyProperty *prop_list
-)
-{
-  int i;
-  PlyElement *elem;
-  PlyProperty *prop;
-
-  /* look for appropriate element */
-  elem = find_element (plyfile, elem_name);
-  if (elem == NULL) {
-    fprintf(stderr,"ply_describe_element: can't find element '%s'\n",elem_name);
-    exit (-1);
-  }
-
-  elem->num = nelems;
-
-  /* copy the list of properties */
-
-  elem->nprops = nprops;
-  elem->props = (PlyProperty **) myalloc (sizeof (PlyProperty *) * nprops);
-  elem->store_prop = (char *) myalloc (sizeof (char) * nprops);
-
-  for (i = 0; i < nprops; i++) {
-    prop = (PlyProperty *) myalloc (sizeof (PlyProperty));
-    elem->props[i] = prop;
-    elem->store_prop[i] = NAMED_PROP;
-    copy_property (prop, &prop_list[i]);
-  }
-}
-
-
-/******************************************************************************
-Describe a property of an element.
-
-Entry:
-  plyfile   - file identifier
-  elem_name - name of element that information is being specified about
-  prop      - the new property
-******************************************************************************/
-
-inline void ply_describe_property(
-  PlyFile *plyfile,
-  const char *elem_name,
-  PlyProperty *prop
-)
-{
-  PlyElement *elem;
-  PlyProperty *elem_prop;
-
-  /* look for appropriate element */
-  elem = find_element (plyfile, elem_name);
-  if (elem == NULL) {
-    fprintf(stderr, "ply_describe_property: can't find element '%s'\n",
-            elem_name);
-    return;
-  }
-
-  /* create room for new property */
-
-  if (elem->nprops == 0) {
-    elem->props = (PlyProperty **) myalloc (sizeof (PlyProperty *));
-    elem->store_prop = (char *) myalloc (sizeof (char));
-    elem->nprops = 1;
-  }
-  else {
-    elem->nprops++;
-    elem->props = (PlyProperty **)
-                  realloc (elem->props, sizeof (PlyProperty *) * elem->nprops);
-    elem->store_prop = (char *)
-                  realloc (elem->store_prop, sizeof (char) * elem->nprops);
-  }
-
-  /* copy the new property */
-
-  elem_prop = (PlyProperty *) myalloc (sizeof (PlyProperty));
-  elem->props[elem->nprops - 1] = elem_prop;
-  elem->store_prop[elem->nprops - 1] = NAMED_PROP;
-  copy_property (elem_prop, prop);
-}
-
-
-/******************************************************************************
-Describe what the "other" properties are that are to be stored, and where
-they are in an element.
-******************************************************************************/
-
-inline void ply_describe_other_properties(
-  PlyFile *plyfile,
-  PlyOtherProp *other,
-  int offset
-)
-{
-  int i;
-  PlyElement *elem;
-  PlyProperty *prop;
-
-  /* look for appropriate element */
-  elem = find_element (plyfile, other->name);
-  if (elem == NULL) {
-    fprintf(stderr, "ply_describe_other_properties: can't find element '%s'\n",
-            other->name);
-    return;
-  }
-
-  /* create room for other properties */
-
-  if (elem->nprops == 0) {
-    elem->props = (PlyProperty **)
-                  myalloc (sizeof (PlyProperty *) * other->nprops);
-    elem->store_prop = (char *) myalloc (sizeof (char) * other->nprops);
-    elem->nprops = 0;
-  }
-  else {
-    int newsize;
-    newsize = elem->nprops + other->nprops;
-    elem->props = (PlyProperty **)
-                  realloc (elem->props, sizeof (PlyProperty *) * newsize);
-    elem->store_prop = (char *)
-                  realloc (elem->store_prop, sizeof (char) * newsize);
-  }
-
-  /* copy the other properties */
-
-  for (i = 0; i < other->nprops; i++) {
-    prop = (PlyProperty *) myalloc (sizeof (PlyProperty));
-    copy_property (prop, other->props[i]);
-    elem->props[elem->nprops] = prop;
-    elem->store_prop[elem->nprops] = OTHER_PROP;
-    elem->nprops++;
-  }
-
-  /* save other info about other properties */
-  elem->other_size = other->size;
-  elem->other_offset = offset;
-}
-
-
-/******************************************************************************
-State how many of a given element will be written.
-
-Entry:
-  plyfile   - file identifier
-  elem_name - name of element that information is being specified about
-  nelems    - number of elements of this type to be written
-******************************************************************************/
-
-inline void ply_element_count(
-  PlyFile *plyfile,
-  const char *elem_name,
-  int nelems
-)
-{
-  PlyElement *elem;
-
-  /* look for appropriate element */
-  elem = find_element (plyfile, elem_name);
-  if (elem == NULL) {
-    fprintf(stderr,"ply_element_count: can't find element '%s'\n",elem_name);
-    exit (-1);
-  }
-
-  elem->num = nelems;
-}
-
-
-/******************************************************************************
-Signal that we've described everything a PLY file's header and that the
-header should be written to the file.
-
-Entry:
-  plyfile - file identifier
-******************************************************************************/
-
-inline void ply_header_complete(PlyFile *plyfile)
-{
-  int i,j;
-  FILE *fp = plyfile->fp;
-  PlyElement *elem;
-  PlyProperty *prop;
-
-  fprintf (fp, "ply\n");
-
-  switch (plyfile->file_type) {
-    case PLY_ASCII:
-      fprintf (fp, "format ascii 1.0\n");
-      break;
-    case PLY_BINARY_BE:
-      fprintf (fp, "format binary_big_endian 1.0\n");
-      break;
-    case PLY_BINARY_LE:
-      fprintf (fp, "format binary_little_endian 1.0\n");
-      break;
-    default:
-      fprintf (stderr, "ply_header_complete: bad file type = %d\n",
-               plyfile->file_type);
-      exit (-1);
-  }
-
-  /* write out the comments */
-
-  for (i = 0; i < plyfile->num_comments; i++)
-    fprintf (fp, "comment %s\n", plyfile->comments[i]);
-
-  /* write out object information */
-
-  for (i = 0; i < plyfile->num_obj_info; i++)
-    fprintf (fp, "obj_info %s\n", plyfile->obj_info[i]);
-
-  /* write out information about each element */
-
-  for (i = 0; i < plyfile->nelems; i++) {
-
-    elem = plyfile->elems[i];
-    fprintf (fp, "element %s %d\n", elem->name, elem->num);
-
-    /* write out each property */
-    for (j = 0; j < elem->nprops; j++) {
-      prop = elem->props[j];
-      if (prop->is_list) {
-        fprintf (fp, "property list ");
-        write_scalar_type (fp, prop->count_external);
-        fprintf (fp, " ");
-        write_scalar_type (fp, prop->external_type);
-        fprintf (fp, " %s\n", prop->name);
-      }
-      else {
-        fprintf (fp, "property ");
-        write_scalar_type (fp, prop->external_type);
-        fprintf (fp, " %s\n", prop->name);
-      }
-    }
-  }
-
-  fprintf (fp, "end_header\n");
-}
-
-
-/******************************************************************************
-Specify which elements are going to be written.  This should be called
-before a call to the routine ply_put_element().
-
-Entry:
-  plyfile   - file identifier
-  elem_name - name of element we're talking about
-******************************************************************************/
-
-inline void ply_put_element_setup(PlyFile *plyfile, const char *elem_name)
-{
-  PlyElement *elem;
-
-  elem = find_element (plyfile, elem_name);
-  if (elem == NULL) {
-    fprintf(stderr, "ply_elements_setup: can't find element '%s'\n", elem_name);
-    exit (-1);
-  }
-	
-  plyfile->which_elem = elem;
-}
-
-
-/******************************************************************************
-Write an element to the file.  This routine assumes that we're
-writing the type of element specified in the last call to the routine
-ply_put_element_setup().
-
-Entry:
-  plyfile  - file identifier
-  elem_ptr - pointer to the element
-******************************************************************************/
-
-inline void ply_put_element(PlyFile *plyfile, void *elem_ptr, int *native_binary_type)
-{
-  int j,k;
-  FILE *fp = plyfile->fp;
-  PlyElement *elem;
-  PlyProperty *prop;
-  char *elem_data,*item;
-  char **item_ptr;
-  int list_count;
-  int item_size;
-  int int_val;
-  unsigned int uint_val;
-  double double_val;
-  char **other_ptr;
-
-  elem = plyfile->which_elem;
-  elem_data = (char *)elem_ptr;
-  other_ptr = (char **) (((char *) elem_ptr) + elem->other_offset);
-
-  /* write out either to an ascii or binary file */
-	int ply_type_size[] = {
-	  0, 1, 2, 4, 1, 2, 4, 4, 8
-	};
-
-  if (plyfile->file_type == PLY_ASCII) {
-
-    /* write an ascii file */
-
-    /* write out each property of the element */
-    for (j = 0; j < elem->nprops; j++) {
-      prop = elem->props[j];
-      if (elem->store_prop[j] == OTHER_PROP)
-        elem_data = *other_ptr;
-      else
-        elem_data = (char *)elem_ptr;
-      if (prop->is_list) {
-        item = elem_data + prop->count_offset;
-        get_stored_item ((void *) item, prop->count_internal,
-                         &int_val, &uint_val, &double_val);
-        write_ascii_item (fp, int_val, uint_val, double_val,
-                          prop->count_external);
-        list_count = uint_val;
-        item_ptr = (char **) (elem_data + prop->offset);
-        item = item_ptr[0];
-        item_size = ply_type_size[prop->internal_type];
-        for (k = 0; k < list_count; k++) {
-          get_stored_item ((void *) item, prop->internal_type,
-                           &int_val, &uint_val, &double_val);
-          write_ascii_item (fp, int_val, uint_val, double_val,
-                            prop->external_type);
-          item += item_size;
-        }
-      }
-      else {
-        item = elem_data + prop->offset;
-        get_stored_item ((void *) item, prop->internal_type,
-                         &int_val, &uint_val, &double_val);
-        write_ascii_item (fp, int_val, uint_val, double_val,
-                          prop->external_type);
-      }
-    }
-
-    fprintf (fp, "\n");
-  }
-  else {
-
-    /* write a binary file */
-
-    /* write out each property of the element */
-    for (j = 0; j < elem->nprops; j++) {
-      prop = elem->props[j];
-      if (elem->store_prop[j] == OTHER_PROP)
-        elem_data = *other_ptr;
-      else
-        elem_data = (char *)elem_ptr;
-      if (prop->is_list) {
-        item = elem_data + prop->count_offset;
-        item_size = ply_type_size[prop->count_internal];
-        get_stored_item ((void *) item, prop->count_internal,
-                         &int_val, &uint_val, &double_val);
-        write_binary_item (fp, plyfile->file_type, int_val, uint_val,
-			   double_val, prop->count_external, native_binary_type);
-        list_count = uint_val;
-        item_ptr = (char **) (elem_data + prop->offset);
-        item = item_ptr[0];
-        item_size = ply_type_size[prop->internal_type];
-        for (k = 0; k < list_count; k++) {
-          get_stored_item ((void *) item, prop->internal_type,
-                           &int_val, &uint_val, &double_val);
-          write_binary_item (fp, plyfile->file_type, int_val, uint_val,
-			     double_val, prop->external_type, native_binary_type);
-          item += item_size;
-        }
-      }
-      else {
-        item = elem_data + prop->offset;
-        item_size = ply_type_size[prop->internal_type];
-        get_stored_item ((void *) item, prop->internal_type,
-                         &int_val, &uint_val, &double_val);
-        write_binary_item (fp, plyfile->file_type, int_val, uint_val,
-			   double_val, prop->external_type, native_binary_type);
-      }
-    }
-
-  }
-}
-
-
-/******************************************************************************
-Specify a comment that will be written in the header.
-
-Entry:
-  plyfile - file identifier
-  comment - the comment to be written
-******************************************************************************/
-
-inline void ply_put_comment(PlyFile *plyfile, char *comment)
-{
-  /* (re)allocate space for new comment */
-  if (plyfile->num_comments == 0)
-    plyfile->comments = (char **) myalloc (sizeof (char *));
-  else
-    plyfile->comments = (char **) realloc (plyfile->comments,
-                         sizeof (char *) * (plyfile->num_comments + 1));
-
-  /* add comment to list */
-  plyfile->comments[plyfile->num_comments] = strdup (comment);
-  plyfile->num_comments++;
-}
-
-
-/******************************************************************************
-Specify a piece of object information (arbitrary text) that will be written
-in the header.
-
-Entry:
-  plyfile  - file identifier
-  obj_info - the text information to be written
-******************************************************************************/
-
-inline void ply_put_obj_info(PlyFile *plyfile, char *obj_info)
-{
-  /* (re)allocate space for new info */
-  if (plyfile->num_obj_info == 0)
-    plyfile->obj_info = (char **) myalloc (sizeof (char *));
-  else
-    plyfile->obj_info = (char **) realloc (plyfile->obj_info,
-                         sizeof (char *) * (plyfile->num_obj_info + 1));
-
-  /* add info to list */
-  plyfile->obj_info[plyfile->num_obj_info] = strdup (obj_info);
-  plyfile->num_obj_info++;
-}
-
-
-
-
-
-
-
-/*************/
-/*  Reading  */
-/*************/
-
-
-
-/******************************************************************************
-Given a file pointer, get ready to read PLY data from the file.
-
-Entry:
-  fp - the given file pointer
-
-Exit:
-  nelems     - number of elements in object
-  elem_names - list of element names
-  returns a pointer to a PlyFile, used to refer to this file, or NULL if error
-******************************************************************************/
-
-inline PlyFile *ply_read(FILE *fp, int *nelems, char ***elem_names)
-{
-  int i,j;
-  PlyFile *plyfile;
-  int nwords;
-  char **words;
-  char **elist;
-  PlyElement *elem;
-  char *orig_line;
-
-  /* check for NULL file pointer */
-  if (fp == NULL)
-    return (NULL);
-	
-	int native_binary_type = -1;
-	int types_checked = 0;
-	
-  if (native_binary_type == -1)
-     native_binary_type = get_native_binary_type2();
-  if (!types_checked)
-     types_checked = check_types();
-  
-  /* create record for this object */
-
-  plyfile = (PlyFile *) myalloc (sizeof (PlyFile));
-  plyfile->nelems = 0;
-  plyfile->comments = NULL;
-  plyfile->num_comments = 0;
-  plyfile->obj_info = NULL;
-  plyfile->num_obj_info = 0;
-  plyfile->fp = fp;
-  plyfile->other_elems = NULL;
-
-  /* read and parse the file's header */
-
-  words = get_words (plyfile->fp, &nwords, &orig_line);
-  if (nwords == 0 || !words || !equal_strings (words[0], "ply"))
-  {
-       if (words)
-	 free(words);
-     
-     
-      return (NULL);
-  }
-  
-  while (words) {
-
-    /* parse words */
-
-    if (equal_strings (words[0], "format")) {
-      if (nwords != 3) {
-	free(words);
-	return (NULL);
-      }
-      if (equal_strings (words[1], "ascii"))
-        plyfile->file_type = PLY_ASCII;
-      else if (equal_strings (words[1], "binary_big_endian"))
-        plyfile->file_type = PLY_BINARY_BE;
-      else if (equal_strings (words[1], "binary_little_endian"))
-        plyfile->file_type = PLY_BINARY_LE;
-      else {
-	free(words);
-        return (NULL);
-      }
-      plyfile->version = atof (words[2]);
-    }
-    else if (equal_strings (words[0], "element"))
-      add_element (plyfile, words);
-    else if (equal_strings (words[0], "property"))
-      add_property (plyfile, words);
-    else if (equal_strings (words[0], "comment"))
-      add_comment (plyfile, orig_line);
-    else if (equal_strings (words[0], "obj_info"))
-      add_obj_info (plyfile, orig_line);
-    else if (equal_strings (words[0], "end_header")) {
-      free(words);
-      break;
-    }
-    
-    /* free up words space */
-    free (words);
-
-    words = get_words (plyfile->fp, &nwords, &orig_line);
-  }
-
-  /* create tags for each property of each element, to be used */
-  /* later to say whether or not to store each property for the user */
-
-  for (i = 0; i < plyfile->nelems; i++) {
-    elem = plyfile->elems[i];
-    elem->store_prop = (char *) myalloc (sizeof (char) * elem->nprops);
-    for (j = 0; j < elem->nprops; j++)
-      elem->store_prop[j] = DONT_STORE_PROP;
-    elem->other_offset = NO_OTHER_PROPS; /* no "other" props by default */
-  }
-
-  /* set return values about the elements */
-
-  elist = (char **) myalloc (sizeof (char *) * plyfile->nelems);
-  for (i = 0; i < plyfile->nelems; i++)
-    elist[i] = strdup (plyfile->elems[i]->name);
-
-  *elem_names = elist;
-  *nelems = plyfile->nelems;
-
-  /* return a pointer to the file's information */
-
-  return (plyfile);
-}
-
-
-/******************************************************************************
-Open a polygon file for reading.
-
-Entry:
-  filename - name of file to read from
-
-Exit:
-  nelems     - number of elements in object
-  elem_names - list of element names
-  file_type  - file type, either ascii or binary
-  version    - version number of PLY file
-  returns a file identifier, used to refer to this file, or NULL if error
-******************************************************************************/
-
-inline PlyFile *ply_open_for_reading(
-  char *filename,
-  int *nelems,
-  char ***elem_names,
-  int *file_type,
-  float *version
-)
-{
-  FILE *fp;
-  PlyFile *plyfile;
-  //char *name;
-  
-  
-
-   /* tack on the extension .ply, if necessary */
-
-  // removing below, to handle also macintosh alias filenames
-  //name = (char *) myalloc (sizeof (char) * (strlen (filename) + 5));
-  //strcpy (name, filename);
-  //if (strlen (name) < 4 ||
-  //    strcmp (name + strlen (name) - 4, ".ply") != 0)
-  //    strcat (name, ".ply");
-
-  /* open the file for reading */
-
-  //fp = fopen (name, "r");
-  
-  //opening file in binary, ascii data can be read in binary with get_words
-  fp = fopen (filename, "rb");
-
-  if (fp == NULL)
-    return (NULL);
-  
-  /* create the PlyFile data structure */
-
-  plyfile = ply_read (fp, nelems, elem_names);
-
-  /* determine the file type and version */
-
-  *file_type = plyfile->file_type;
-  *version = plyfile->version;
-
-  /* return a pointer to the file's information */
-
-  return (plyfile);
-}
-
-
-/******************************************************************************
-Get information about a particular element.
-
-Entry:
-  plyfile   - file identifier
-  elem_name - name of element to get information about
-
-Exit:
-  nelems   - number of elements of this type in the file
-  nprops   - number of properties
-  returns a list of properties, or NULL if the file doesn't contain that elem
-******************************************************************************/
-
-inline PlyProperty **ply_get_element_description(
-  PlyFile *plyfile,
-  const char *elem_name,
-  int *nelems,
-  int *nprops
-)
-{
-  int i;
-  PlyElement *elem;
-  PlyProperty *prop;
-  PlyProperty **prop_list;
-
-  /* find information about the element */
-  elem = find_element (plyfile, elem_name);
-  if (elem == NULL)
-    return (NULL);
-
-  *nelems = elem->num;
-  *nprops = elem->nprops;
-
-  /* make a copy of the element's property list */
-  prop_list = (PlyProperty **) myalloc (sizeof (PlyProperty *) * elem->nprops);
-  for (i = 0; i < elem->nprops; i++) {
-    prop = (PlyProperty *) myalloc (sizeof (PlyProperty));
-    copy_property (prop, elem->props[i]);
-    prop_list[i] = prop;
-  }
-
-  /* return this duplicate property list */
-  return (prop_list);
-}
-
-
-/******************************************************************************
-Specify which properties of an element are to be returned.  This should be
-called before a call to the routine ply_get_element().
-
-Entry:
-  plyfile   - file identifier
-  elem_name - which element we're talking about
-  nprops    - number of properties
-  prop_list - list of properties
-******************************************************************************/
-
-inline void ply_get_element_setup(
-  PlyFile *plyfile,
-  const char *elem_name,
-  int nprops,
-  PlyProperty *prop_list
-)
-{
-  int i;
-  PlyElement *elem;
-  PlyProperty *prop;
-  int index;
-
-  /* find information about the element */
-  elem = find_element (plyfile, elem_name);
-  plyfile->which_elem = elem;
-
-  /* deposit the property information into the element's description */
-  for (i = 0; i < nprops; i++) {
-
-    /* look for actual property */
-    prop = find_property (elem, prop_list[i].name, &index);
-    if (prop == NULL) {
-      fprintf (stderr, "Warning:  Can't find property '%s' in element '%s'\n",
-               prop_list[i].name, elem_name);
-      continue;
-    }
-
-    /* store its description */
-    prop->internal_type = prop_list[i].internal_type;
-    prop->offset = prop_list[i].offset;
-    prop->count_internal = prop_list[i].count_internal;
-    prop->count_offset = prop_list[i].count_offset;
-
-    /* specify that the user wants this property */
-    elem->store_prop[index] = STORE_PROP;
-  }
-}
-
-
-/******************************************************************************
-Specify a property of an element that is to be returned.  This should be
-called (usually multiple times) before a call to the routine ply_get_element().
-This routine should be used in preference to the less flexible old routine
-called ply_get_element_setup().
-
-Entry:
-  plyfile   - file identifier
-  elem_name - which element we're talking about
-  prop      - property to add to those that will be returned
-******************************************************************************/
-
-inline void ply_get_property(
-  PlyFile *plyfile,
-  const char *elem_name,
-  PlyProperty *prop
-)
-{
-  PlyElement *elem;
-  PlyProperty *prop_ptr;
-  int index;
-
-  /* find information about the element */
-  elem = find_element (plyfile, elem_name);
-  plyfile->which_elem = elem;
-
-  /* deposit the property information into the element's description */
-
-  prop_ptr = find_property (elem, prop->name, &index);
-  if (prop_ptr == NULL) {
-    fprintf (stderr, "Warning:  Can't find property '%s' in element '%s'\n",
-             prop->name, elem_name);
-    return;
-  }
-  prop_ptr->internal_type  = prop->internal_type;
-  prop_ptr->offset         = prop->offset;
-  prop_ptr->count_internal = prop->count_internal;
-  prop_ptr->count_offset   = prop->count_offset;
-
-  /* specify that the user wants this property */
-  elem->store_prop[index] = STORE_PROP;
-}
-
-
-/******************************************************************************
-Read one element from the file.  This routine assumes that we're reading
-the type of element specified in the last call to the routine
-ply_get_element_setup().
-
-Entry:
-  plyfile  - file identifier
-  elem_ptr - pointer to location where the element information should be put
-******************************************************************************/
-
-inline void ply_get_element(PlyFile *plyfile, void *elem_ptr, int *native_binary_type)
-{
-  if (plyfile->file_type == PLY_ASCII)
-    ascii_get_element (plyfile, (char *) elem_ptr);
-  else
-    binary_get_element (plyfile, (char *) elem_ptr, native_binary_type);
-}
-
-
-/******************************************************************************
-Extract the comments from the header information of a PLY file.
-
-Entry:
-  plyfile - file identifier
-
-Exit:
-  num_comments - number of comments returned
-  returns a pointer to a list of comments
-******************************************************************************/
-
-inline char **ply_get_comments(PlyFile *plyfile, int *num_comments)
-{
-  *num_comments = plyfile->num_comments;
-  return (plyfile->comments);
-}
-
-
-/******************************************************************************
-Extract the object information (arbitrary text) from the header information
-of a PLY file.
-
-Entry:
-  plyfile - file identifier
-
-Exit:
-  num_obj_info - number of lines of text information returned
-  returns a pointer to a list of object info lines
-******************************************************************************/
-
-inline char **ply_get_obj_info(PlyFile *plyfile, int *num_obj_info)
-{
-  *num_obj_info = plyfile->num_obj_info;
-  return (plyfile->obj_info);
-}
-
-
-/******************************************************************************
-Make ready for "other" properties of an element-- those properties that
-the user has not explicitly asked for, but that are to be stashed away
-in a special structure to be carried along with the element's other
-information.
-
-Entry:
-  plyfile - file identifier
-  elem    - element for which we want to save away other properties
-******************************************************************************/
-
-inline void setup_other_props(PlyElement *elem)
-{
-  int i;
-  PlyProperty *prop;
-  int size = 0;
-  int type_size;
-
-  /* Examine each property in decreasing order of size. */
-  /* We do this so that all data types will be aligned by */
-  /* word, half-word, or whatever within the structure. */
-	int ply_type_size[] = {
-	  0, 1, 2, 4, 1, 2, 4, 4, 8
-	};
-
-  for (type_size = 8; type_size > 0; type_size /= 2) {
-
-    /* add up the space taken by each property, and save this information */
-    /* away in the property descriptor */
-
-    for (i = 0; i < elem->nprops; i++) {
-
-      /* don't bother with properties we've been asked to store explicitly */
-      if (elem->store_prop[i])
-        continue;
-
-      prop = elem->props[i];
-
-      /* internal types will be same as external */
-      prop->internal_type = prop->external_type;
-      prop->count_internal = prop->count_external;
-
-      /* check list case */
-      if (prop->is_list) {
-
-        /* pointer to list */
-        if (type_size == sizeof (void *)) {
-          prop->offset = size;
-          size += sizeof (void *);    /* always use size of a pointer here */
-        }
-
-        /* count of number of list elements */
-        if (type_size == ply_type_size[prop->count_external]) {
-          prop->count_offset = size;
-          size += ply_type_size[prop->count_external];
-        }
-      }
-      /* not list */
-      else if (type_size == ply_type_size[prop->external_type]) {
-        prop->offset = size;
-        size += ply_type_size[prop->external_type];
-      }
-    }
-
-  }
-
-  /* save the size for the other_props structure */
-  elem->other_size = size;
-}
-
-
-/******************************************************************************
-Specify that we want the "other" properties of an element to be tucked
-away within the user's structure.  The user needn't be concerned for how
-these properties are stored.
-
-Entry:
-  plyfile   - file identifier
-  elem_name - name of element that we want to store other_props in
-  offset    - offset to where other_props will be stored inside user's structure
-
-Exit:
-  returns pointer to structure containing description of other_props
-******************************************************************************/
-
-inline PlyOtherProp *ply_get_other_properties(
-  PlyFile *plyfile,
-  const char *elem_name,
-  int offset
-)
-{
-  int i;
-  PlyElement *elem;
-  PlyOtherProp *other;
-  PlyProperty *prop;
-  int nprops;
-
-  /* find information about the element */
-  elem = find_element (plyfile, elem_name);
-  if (elem == NULL) {
-    fprintf (stderr, "ply_get_other_properties: Can't find element '%s'\n",
-             elem_name);
-    return (NULL);
-  }
-
-  /* remember that this is the "current" element */
-  plyfile->which_elem = elem;
-
-  /* save the offset to where to store the other_props */
-  elem->other_offset = offset;
-
-  /* place the appropriate pointers, etc. in the element's property list */
-  setup_other_props (elem);
-
-  /* create structure for describing other_props */
-  other = (PlyOtherProp *) myalloc (sizeof (PlyOtherProp));
-  other->name = strdup (elem_name);
-#if 0
-  if (elem->other_offset == NO_OTHER_PROPS) {
-    other->size = 0;
-    other->props = NULL;
-    other->nprops = 0;
-    return (other);
-  }
-#endif
-  other->size = elem->other_size;
-  other->props = (PlyProperty **) myalloc (sizeof(PlyProperty) * elem->nprops);
-  
-  /* save descriptions of each "other" property */
-  nprops = 0;
-  for (i = 0; i < elem->nprops; i++) {
-    if (elem->store_prop[i])
-      continue;
-    prop = (PlyProperty *) myalloc (sizeof (PlyProperty));
-    copy_property (prop, elem->props[i]);
-    other->props[nprops] = prop;
-    nprops++;
-  }
-  other->nprops = nprops;
-
-#if 1
-  /* set other_offset pointer appropriately if there are NO other properties */
-  if (other->nprops == 0) {
-    elem->other_offset = NO_OTHER_PROPS;
-  }
-#endif
-  
-  /* return structure */
-  return (other);
-}
-
-
-
-
-/*************************/
-/*  Other Element Stuff  */
-/*************************/
-
-
-
-
-/******************************************************************************
-Grab all the data for an element that a user does not want to explicitly
-read in.
-
-Entry:
-  plyfile    - pointer to file
-  elem_name  - name of element whose data is to be read in
-  elem_count - number of instances of this element stored in the file
-
-Exit:
-  returns pointer to ALL the "other" element data for this PLY file
-******************************************************************************/
-
-inline PlyOtherElems *ply_get_other_element (
-  PlyFile *plyfile,
-  char *elem_name,
-  int elem_count
-)
-{
-  int i;
-  PlyElement *elem;
-  PlyOtherElems *other_elems;
-  OtherElem *other;
-
-  /* look for appropriate element */
-  elem = find_element (plyfile, elem_name);
-  if (elem == NULL) {
-    fprintf (stderr,
-             "ply_get_other_element: can't find element '%s'\n", elem_name);
-    exit (-1);
-  }
-
-  /* create room for the new "other" element, initializing the */
-  /* other data structure if necessary */
-
-  if (plyfile->other_elems == NULL) {
-    plyfile->other_elems = (PlyOtherElems *) myalloc (sizeof (PlyOtherElems));
-    other_elems = plyfile->other_elems;
-    other_elems->other_list = (OtherElem *) myalloc (sizeof (OtherElem));
-    other = &(other_elems->other_list[0]);
-    other_elems->num_elems = 1;
-  }
-  else {
-    other_elems = plyfile->other_elems;
-    other_elems->other_list = (OtherElem *) realloc (other_elems->other_list,
-                              sizeof (OtherElem) * (other_elems->num_elems + 1));
-    other = &(other_elems->other_list[other_elems->num_elems]);
-    other_elems->num_elems++;
-  }
-
-  /* count of element instances in file */
-  other->elem_count = elem_count;
-
-  /* save name of element */
-  other->elem_name = strdup (elem_name);
-
-  /* create a list to hold all the current elements */
-  other->other_data = (OtherData **)
-                  malloc (sizeof (OtherData *) * other->elem_count);
-
-  /* set up for getting elements */
-  other->other_props = ply_get_other_properties (plyfile, elem_name,
-                         offsetof(OtherData,other_props));
-
-  /* grab all these elements */
-  int native_binary_type = get_native_binary_type2();
-  for (i = 0; i < other->elem_count; i++) {
-    /* grab and element from the file */
-    other->other_data[i] = (OtherData *) malloc (sizeof (OtherData));
-    ply_get_element (plyfile, (void *) other->other_data[i], &native_binary_type);
-  }
-
-  /* return pointer to the other elements data */
-  return (other_elems);
-}
-
-
-/******************************************************************************
-Pass along a pointer to "other" elements that we want to save in a given
-PLY file.  These other elements were presumably read from another PLY file.
-
-Entry:
-  plyfile     - file pointer in which to store this other element info
-  other_elems - info about other elements that we want to store
-******************************************************************************/
-
-inline void ply_describe_other_elements (
-  PlyFile *plyfile,
-  PlyOtherElems *other_elems
-)
-{
-  int i;
-  OtherElem *other;
-  PlyElement *elem;
-  
-  /* ignore this call if there is no other element */
-  if (other_elems == NULL)
-    return;
-
-  /* save pointer to this information */
-  plyfile->other_elems = other_elems;
-
-  /* describe the other properties of this element */
-  /* store them in the main element list as elements with
-     only other properties */
-  
-  REALLOCN(plyfile->elems, PlyElement *,
-	   plyfile->nelems, plyfile->nelems + other_elems->num_elems);
-  for (i = 0; i < other_elems->num_elems; i++) {
-      other = &(other_elems->other_list[i]);
-      elem = (PlyElement *) myalloc (sizeof (PlyElement));
-      plyfile->elems[plyfile->nelems++] = elem;
-      elem->name = strdup (other->elem_name);
-      elem->num = other->elem_count;
-      elem->nprops = 0;
-      ply_describe_other_properties (plyfile, other->other_props,
-				     offsetof(OtherData,other_props));
-  }
-}
-
-
-/******************************************************************************
-Write out the "other" elements specified for this PLY file.
-
-Entry:
-  plyfile - pointer to PLY file to write out other elements for
-******************************************************************************/
-
-inline void ply_put_other_elements (PlyFile *plyfile, int *native_binary_type)
-{
-  int i,j;
-  OtherElem *other;
-
-  /* make sure we have other elements to write */
-  if (plyfile->other_elems == NULL)
-    return;
-
-  /* write out the data for each "other" element */
-
-  for (i = 0; i < plyfile->other_elems->num_elems; i++) {
-
-    other = &(plyfile->other_elems->other_list[i]);
-    ply_put_element_setup (plyfile, other->elem_name);
-
-    /* write out each instance of the current element */
-    for (j = 0; j < other->elem_count; j++)
-      ply_put_element (plyfile, (void *) other->other_data[j], native_binary_type);
-  }
-}
-
-
-/******************************************************************************
-Free up storage used by an "other" elements data structure.
-
-Entry:
-  other_elems - data structure to free up
-******************************************************************************/
-
-inline void ply_free_other_elements (PlyOtherElems *other_elems)
-{
-  // Alec: 
-  //other_elems = other_elems;
-  delete(other_elems);
-}
-
-
-
-/*******************/
-/*  Miscellaneous  */
-/*******************/
-
-
-
-/******************************************************************************
-Close a PLY file.
-
-Entry:
-  plyfile - identifier of file to close
-******************************************************************************/
-
-inline void ply_close(PlyFile *plyfile)
-{
-  fclose (plyfile->fp);
-  // Alec:
-  plyfile->fp = NULL;
-
-  /* free up memory associated with the PLY file */
-  free (plyfile);
-}
-
-
-/******************************************************************************
-Get version number and file type of a PlyFile.
-
-Entry:
-  ply - pointer to PLY file
-
-Exit:
-  version - version of the file
-  file_type - PLY_ASCII, PLY_BINARY_BE, or PLY_BINARY_LE
-******************************************************************************/
-
-inline void ply_get_info(PlyFile *ply, float *version, int *file_type)
-{
-  if (ply == NULL)
-    return;
-
-  *version = ply->version;
-  *file_type = ply->file_type;
-}
-
-
-/******************************************************************************
-Compare two strings.  Returns 1 if they are the same, 0 if not.
-******************************************************************************/
-
-inline int equal_strings(const char *s1, const char *s2)
-{
-
-  while (*s1 && *s2)
-    if (*s1++ != *s2++)
-      return (0);
-
-  if (*s1 != *s2)
-    return (0);
-  else
-    return (1);
-}
-
-
-/******************************************************************************
-Find an element from the element list of a given PLY object.
-
-Entry:
-  plyfile - file id for PLY file
-  element - name of element we're looking for
-
-Exit:
-  returns the element, or NULL if not found
-******************************************************************************/
-
-inline PlyElement *find_element(PlyFile *plyfile, const char *element)
-{
-  int i;
-
-  for (i = 0; i < plyfile->nelems; i++)
-    if (equal_strings (element, plyfile->elems[i]->name))
-      return (plyfile->elems[i]);
-
-  return (NULL);
-}
-
-
-/******************************************************************************
-Find a property in the list of properties of a given element.
-
-Entry:
-  elem      - pointer to element in which we want to find the property
-  prop_name - name of property to find
-
-Exit:
-  index - index to position in list
-  returns a pointer to the property, or NULL if not found
-******************************************************************************/
-
-inline PlyProperty *find_property(PlyElement *elem, const char *prop_name, int *index)
-{
-  int i;
-
-  for (i = 0; i < elem->nprops; i++)
-    if (equal_strings (prop_name, elem->props[i]->name)) {
-      *index = i;
-      return (elem->props[i]);
-    }
-
-  *index = -1;
-  return (NULL);
-}
-
-
-/******************************************************************************
-Read an element from an ascii file.
-
-Entry:
-  plyfile  - file identifier
-  elem_ptr - pointer to element
-******************************************************************************/
-
-inline void ascii_get_element(PlyFile *plyfile, char *elem_ptr)
-{
-	int ply_type_size[] = {
-	  0, 1, 2, 4, 1, 2, 4, 4, 8
-	};
-
-  int j,k;
-  PlyElement *elem;
-  PlyProperty *prop;
-  char **words;
-  int nwords;
-  int which_word;
-  char *elem_data,*item=NULL;
-  char *item_ptr;
-  int item_size;
-  int int_val;
-  unsigned int uint_val;
-  double double_val;
-  int list_count;
-  int store_it;
-  char **store_array;
-  char *orig_line;
-  char *other_data=NULL;
-  int other_flag;
-
-  /* the kind of element we're reading currently */
-  elem = plyfile->which_elem;
-
-  /* do we need to setup for other_props? */
-
-  if (elem->other_offset != NO_OTHER_PROPS) {
-    char **ptr;
-    other_flag = 1;
-    /* make room for other_props */
-    other_data = (char *) myalloc (elem->other_size);
-    /* store pointer in user's structure to the other_props */
-    ptr = (char **) (elem_ptr + elem->other_offset);
-    *ptr = other_data;
-  }
-  else
-    other_flag = 0;
-
-  /* read in the element */
-
-  words = get_words (plyfile->fp, &nwords, &orig_line);
-  if (words == NULL) {
-    fprintf (stderr, "ply_get_element: unexpected end of file\n");
-    exit (-1);
-  }
-
-  which_word = 0;
-
-  for (j = 0; j < elem->nprops; j++) {
-
-    prop = elem->props[j];
-    store_it = (elem->store_prop[j] | other_flag);
-
-    /* store either in the user's structure or in other_props */
-  //  if (elem->store_prop[j])
-      elem_data = elem_ptr;
-    //else
-      //elem_data = other_data;
-
-    if (prop->is_list) {       /* a list */
-
-      /* get and store the number of items in the list */
-      get_ascii_item (words[which_word++], prop->count_external,
-                      &int_val, &uint_val, &double_val);
-      if (store_it) {
-        item = elem_data + prop->count_offset;
-        store_item(item, prop->count_internal, int_val, uint_val, double_val);
-      }
-
-      /* allocate space for an array of items and store a ptr to the array */
-      list_count = int_val;
-      item_size = ply_type_size[prop->internal_type];
-      store_array = (char **) (elem_data + prop->offset);
-
-      if (list_count == 0) {
-        if (store_it)
-          *store_array = NULL;
-      }
-      else {
-        if (store_it) {
-          item_ptr = (char *) myalloc (sizeof (char) * item_size * list_count);
-           
-          item = item_ptr;
-          *store_array = item_ptr;
-        }
-
-        /* read items and store them into the array */
-        for (k = 0; k < list_count; k++) {
-          get_ascii_item (words[which_word++], prop->external_type,
-                          &int_val, &uint_val, &double_val);
-          if (store_it) {
-            store_item (item, prop->internal_type,
-                        int_val, uint_val, double_val);
-            item += item_size;
-          }
-        }
-      }
-
-    }
-    else {                     /* not a list */
-      get_ascii_item (words[which_word++], prop->external_type,
-                      &int_val, &uint_val, &double_val);
-      if (store_it) {
-        item = elem_data + prop->offset;
-        store_item (item, prop->internal_type, int_val, uint_val, double_val);
-      }
-    }
-
-  }
-
-  free (words);
-}
-
-
-/******************************************************************************
-Read an element from a binary file.
-
-Entry:
-  plyfile  - file identifier
-  elem_ptr - pointer to an element
-******************************************************************************/
-
-inline void binary_get_element(PlyFile *plyfile, char *elem_ptr, int *native_binary_type)
-{
-  int j,k;
-  PlyElement *elem;
-  PlyProperty *prop;
-  FILE *fp = plyfile->fp;
-  char *elem_data,*item=NULL;
-  char *item_ptr;
-  int item_size;
-  int int_val;
-  unsigned int uint_val;
-  double double_val;
-  int list_count;
-  int store_it;
-  char **store_array;
-  char *other_data=NULL;
-  int other_flag;
-	
-	int ply_type_size[] = {
-	  0, 1, 2, 4, 1, 2, 4, 4, 8
-	};
-
-
-  /* the kind of element we're reading currently */
-  elem = plyfile->which_elem;
-
-  /* do we need to setup for other_props? */
-
-  if (elem->other_offset != NO_OTHER_PROPS) {
-    char **ptr;
-    other_flag = 1;
-    /* make room for other_props */
-    other_data = (char *) myalloc (elem->other_size);
-    /* store pointer in user's structure to the other_props */
-    ptr = (char **) (elem_ptr + elem->other_offset);
-    *ptr = other_data;
-  }
-  else
-    other_flag = 0;
-
-  /* read in a number of elements */
-
-  for (j = 0; j < elem->nprops; j++) {
-
-    prop = elem->props[j];
-    store_it = (elem->store_prop[j] | other_flag);
-
-    /* store either in the user's structure or in other_props */
-//    if (elem->store_prop[j])
-      elem_data = elem_ptr;
-//    else
-//      elem_data = other_data;
-
-    if (prop->is_list) {       /* a list */
-
-      /* get and store the number of items in the list */
-      get_binary_item (fp, plyfile->file_type, prop->count_external,
-                      &int_val, &uint_val, &double_val, native_binary_type);
-      if (store_it) {
-        item = elem_data + prop->count_offset;
-        store_item(item, prop->count_internal, int_val, uint_val, double_val);
-      }
-
-      /* allocate space for an array of items and store a ptr to the array */
-      list_count = int_val;
-       
-      item_size = ply_type_size[prop->internal_type];
-      store_array = (char **) (elem_data + prop->offset);
-      if (list_count == 0) {
-        if (store_it)
-          *store_array = NULL;
-      }
-      else {
-        if (store_it) {
-          item_ptr = (char *) myalloc (sizeof (char) * item_size * list_count);
-           
-          item = item_ptr;
-          *store_array = item_ptr;
-        }
-
-        // read items and store them into the array  
-        for (k = 0; k < list_count; k++) {
-          get_binary_item (fp, plyfile->file_type, prop->external_type,
-                          &int_val, &uint_val, &double_val, native_binary_type);
-          if (store_it) {
-             store_item (item, prop->internal_type,
-                       int_val, uint_val, double_val);
-            item += item_size;
-          }
-        }
-        
-         
-        
-      }
-
-    }
-    else {                     /* not a list */
-      get_binary_item (fp, plyfile->file_type, prop->external_type,
-                      &int_val, &uint_val, &double_val, native_binary_type);
-      if (store_it) {
-        item = elem_data + prop->offset;
-        store_item (item, prop->internal_type, int_val, uint_val, double_val);
-      }
-    }
-
-  }
-}
-
-
-/******************************************************************************
-Write to a file the word that represents a PLY data type.
-
-Entry:
-  fp   - file pointer
-  code - code for type
-******************************************************************************/
-
-inline void write_scalar_type (FILE *fp, int code)
-{
-  /* make sure this is a valid code */
-
-  if (code <= PLY_START_TYPE || code >= PLY_END_TYPE) {
-    fprintf (stderr, "write_scalar_type: bad data code = %d\n", code);
-    exit (-1);
-  }
-
-  /* write the code to a file */
-	const char *type_names[] = {
-	"invalid",
-	"char", "short", "int",
-	"uchar", "ushort", "uint",
-	"float", "double",
-	};
-
-
-  fprintf (fp, "%s", type_names[code]);
-}
-
-/******************************************************************************
-  Reverse the order in an array of bytes.  This is the conversion from big
-  endian to little endian and vice versa
-
-Entry:
-  bytes     - array of bytes to reverse (in place)
-  num_bytes - number of bytes in array
-******************************************************************************/
-
-inline void swap_bytes(char *bytes, int num_bytes)
-{
-    int i;
-    char temp;
-    
-    for (i=0; i < num_bytes/2; i++)
-    {
-	temp = bytes[i];
-	bytes[i] = bytes[(num_bytes-1)-i];
-	bytes[(num_bytes-1)-i] = temp;
-    }
-}
-
-/******************************************************************************
-  Find out if this machine is big endian or little endian
-
-  Exit:
-    set global variable, native_binary_type =
-                              either PLY_BINARY_BE or PLY_BINARY_LE
-
-******************************************************************************/
-
-inline void get_native_binary_type(int *native_binary_type)
-{
-    typedef union
-	{
-	      int  int_value;
-	      char byte_values[sizeof(int)];
-	} endian_test_type;
-
-
-	endian_test_type test;
-     
-	test.int_value = 0;
-    test.int_value = 1;
-    if (test.byte_values[0] == 1)
-       *native_binary_type = PLY_BINARY_LE;
-    else if (test.byte_values[sizeof(int)-1] == 1)
-       *native_binary_type = PLY_BINARY_BE;
-    else
-    {
-	fprintf(stderr, "ply: Couldn't determine machine endianness.\n");
-	fprintf(stderr, "ply: Exiting...\n");
-	exit(1);
-    }
-}
-
-inline int get_native_binary_type2()
-{
-	typedef union
-	{
-	      int  int_value;
-	      char byte_values[sizeof(int)];
-	} endian_test_type;
-
-
-    endian_test_type test;
-
-    test.int_value = 0;
-    test.int_value = 1;
-    if (test.byte_values[0] == 1)
-       return PLY_BINARY_LE;
-    else if (test.byte_values[sizeof(int)-1] == 1)
-       return PLY_BINARY_BE;
-    else
-    {
-	fprintf(stderr, "ply: Couldn't determine machine endianness.\n");
-	fprintf(stderr, "ply: Exiting...\n");
-	exit(1);
-    }
-}
-
-/******************************************************************************
-  Verify that all the native types are the sizes we need
-
-
-******************************************************************************/
-
-inline int check_types()
-{
-	int ply_type_size[] = {
-	  0, 1, 2, 4, 1, 2, 4, 4, 8
-	};
-
-    if ((ply_type_size[PLY_CHAR] != sizeof(char)) ||
-	(ply_type_size[PLY_SHORT] != sizeof(short)) ||	
-	(ply_type_size[PLY_INT] != sizeof(int)) ||	
-	(ply_type_size[PLY_UCHAR] != sizeof(unsigned char)) ||	
-	(ply_type_size[PLY_USHORT] != sizeof(unsigned short)) ||	
-	(ply_type_size[PLY_UINT] != sizeof(unsigned int)) ||	
-	(ply_type_size[PLY_FLOAT] != sizeof(float)) ||	
-	(ply_type_size[PLY_DOUBLE] != sizeof(double)))
-    {
-	fprintf(stderr, "ply: Type sizes do not match built-in types\n");
-	fprintf(stderr, "ply: Exiting...\n");
-	exit(1);
-    }
-
-	return 1;    
-}
-
-/******************************************************************************
-Get a text line from a file and break it up into words.
-
-IMPORTANT: The calling routine call "free" on the returned pointer once
-finished with it.
-
-Entry:
-  fp - file to read from
-
-Exit:
-  nwords    - number of words returned
-  orig_line - the original line of characters
-  returns a list of words from the line, or NULL if end-of-file
-******************************************************************************/
-
-inline char **get_words(FILE *fp, int *nwords, char **orig_line)
-{
-  #define BIG_STRING 4096
-  char str[BIG_STRING];
-  char str_copy[BIG_STRING];
-  char **words;
-  int max_words = 10;
-  int num_words = 0;
-  char *ptr,*ptr2;
-  char *result;
-  
-  fpos_t pos; //keep track of file pointer
-  int nbytes;
-  int nonUNIX;
-  nonUNIX=0;
-  nbytes=0;
-  fgetpos(fp, &pos);
-
-  words = (char **) myalloc (sizeof (char *) * max_words);
-
-  /* read in a line */
-  result = fgets (str, BIG_STRING, fp);
-  if (result == NULL) {
-    *nwords = 0;
-    *orig_line = NULL;
-    free(words);
-    return (NULL);
-  }
-
-  /* convert line-feed and tabs into spaces */
-  /* (this guarantees that there will be a space before the */
-  /*  null character at the end of the string) */
-
-  str[BIG_STRING-2] = ' ';
-  str[BIG_STRING-1] = '\0';
-
-  for (ptr = str, ptr2 = str_copy; *ptr != '\0'; ptr++, ptr2++) {
-    *ptr2 = *ptr;
-    nbytes++;
-    if (*ptr == '\t') {
-      *ptr = ' ';
-      *ptr2 = ' ';
-    }
-    else if (*ptr == '\n') {
-      *ptr = ' '; //has to have a space, to be caught later when grouping words
-      *ptr2 = '\0';
-      break;
-    }
-    else if (*ptr == '\r') 
-    {	  //MAC line break
-      nonUNIX=1;
-      if(*(ptr+1)=='\n')		  //actuall PC line break
-      {	
-      	nbytes++;
-      }
-       
-     *ptr = ' '; 
-     
-     *(ptr+1) = '\0';	//when reading mac, best end string here
-     *ptr2 = '\0'; 		//note a pc \r is followed by \n
-      
-      break;
-    }
-  }
-
-
-  /*check to see if a PC or MAC header was detected instead of UNIX*/
-  if(nonUNIX==1)
-  {
-  	fsetpos(fp, &pos);
-  	fseek(fp, nbytes, SEEK_CUR);	
-  }
-
-  /* find the words in the line */
-
-  ptr = str;
-  while (*ptr != '\0') {
-
-    /* jump over leading spaces */
-    while (*ptr == ' ')
-      ptr++;
-
-    /* break if we reach the end */
-    if (*ptr == '\0')
-      break;
-
-    /* save pointer to beginning of word */
-    if (num_words >= max_words) {
-      max_words += 10;
-      char **temp = (char **) realloc (words, sizeof (char *) * max_words);
-
-      if(temp){
-          words = temp;
-      }
-      else{
-          free(words);
-          return NULL;
-      }
-    }
-    words[num_words++] = ptr;
-
-    /* jump over non-spaces */
-    while (*ptr != ' ')
-      ptr++;
-
-    /* place a null character here to mark the end of the word */
-    *ptr++ = '\0';
-  }
-
-  /* return the list of words */
-  *nwords = num_words;
-  *orig_line = str_copy; // ToDo: This looks like UB, returns pointer to local variable on stack.
-  return (words);
-}
-
-/*
-char **get_words(FILE *fp, int *nwords, char **orig_line)
-{
-#define BIG_STRING 4096
-  static char str[BIG_STRING];
-  static char str_copy[BIG_STRING];
-  char **words;
-  int max_words = 10;
-  int num_words = 0;
-  char *ptr,*ptr2;
-  char *result;
-
-  words = (char **) myalloc (sizeof (char *) * max_words);
-
-  // read in a line  
-  result = fgets (str, BIG_STRING, fp);
-  if (result == NULL) {
-    *nwords = 0;
-    *orig_line = NULL;
-    return (NULL);
-  }
-
-  // convert line-feed and tabs into spaces  
-  // (this guarantees that there will be a space before the  
-  //  null character at the end of the string)  
-
-  str[BIG_STRING-2] = ' ';
-  str[BIG_STRING-1] = '\0';
-
-  for (ptr = str, ptr2 = str_copy; *ptr != '\0'; ptr++, ptr2++) {
-    *ptr2 = *ptr;
-    if (*ptr == '\t') {
-      *ptr = ' ';
-      *ptr2 = ' ';
-    }
-    else if (*ptr == '\n') {
-      *ptr = ' ';
-      *ptr2 = '\0';
-      break;
-    }
-    else if (*ptr == '\r') {
-      *ptr = '\0';
-      *ptr2 = '\0'; //note don't break yet, on a pc \r is followed by \n
-    }
-  }
-
-  // find the words in the line  
-
-  ptr = str;
-  while (*ptr != '\0') {
-
-    // jump over leading spaces  
-    while (*ptr == ' ')
-      ptr++;
-
-    // break if we reach the end  
-    if (*ptr == '\0')
-      break;
-
-    // save pointer to beginning of word  
-    if (num_words >= max_words) {
-      max_words += 10;
-      words = (char **) realloc (words, sizeof (char *) * max_words);
-    }
-    words[num_words++] = ptr;
-
-    // jump over non-spaces  
-    while (*ptr != ' ')
-      ptr++;
-
-    // place a null character here to mark the end of the word  
-    *ptr++ = '\0';
-  }
-
-  // return the list of words  
-  *nwords = num_words;
-  *orig_line = str_copy;
-  return (words);
-}*/
-
-/******************************************************************************
-Return the value of an item, given a pointer to it and its type.
-
-Entry:
-  item - pointer to item
-  type - data type that "item" points to
-
-Exit:
-  returns a double-precision float that contains the value of the item
-******************************************************************************/
-
-inline double get_item_value(char *item, int type)
-{
-  unsigned char *puchar;
-  char *pchar;
-  short int *pshort;
-  unsigned short int *pushort;
-  int *pint;
-  unsigned int *puint;
-  float *pfloat;
-  double *pdouble;
-  int int_value;
-  unsigned int uint_value;
-  double double_value;
-
-  switch (type) {
-    case PLY_CHAR:
-      pchar = (char *) item;
-      int_value = *pchar;
-      return ((double) int_value);
-    case PLY_UCHAR:
-      puchar = (unsigned char *) item;
-      int_value = *puchar;
-      return ((double) int_value);
-    case PLY_SHORT:
-      pshort = (short int *) item;
-      int_value = *pshort;
-      return ((double) int_value);
-    case PLY_USHORT:
-      pushort = (unsigned short int *) item;
-      int_value = *pushort;
-      return ((double) int_value);
-    case PLY_INT:
-      pint = (int *) item;
-      int_value = *pint;
-      return ((double) int_value);
-    case PLY_UINT:
-      puint = (unsigned int *) item;
-      uint_value = *puint;
-      return ((double) uint_value);
-    case PLY_FLOAT:
-      pfloat = (float *) item;
-      double_value = *pfloat;
-      return (double_value);
-    case PLY_DOUBLE:
-      pdouble = (double *) item;
-      double_value = *pdouble;
-      return (double_value);
-    default:
-      fprintf (stderr, "get_item_value: bad type = %d\n", type);
-      exit (-1);
-  }
-}
-
-
-/******************************************************************************
-Write out an item to a file as raw binary bytes.
-
-Entry:
-  fp         - file to write to
-  int_val    - integer version of item
-  uint_val   - unsigned integer version of item
-  double_val - double-precision float version of item
-  type       - data type to write out
-******************************************************************************/
-
-inline void write_binary_item(
-  FILE *fp,
-  int file_type,
-  int int_val,
-  unsigned int uint_val,
-  double double_val,
-  int type,
-  int *native_binary_type
-)
-{
-  unsigned char uchar_val;
-  char char_val;
-  unsigned short ushort_val;
-  short short_val;
-  float float_val;
-  void  *value;
-  
-  switch (type) {
-    case PLY_CHAR:
-      char_val = int_val;
-      value = &char_val;
-      break;
-    case PLY_SHORT:
-      short_val = int_val;
-      value = &short_val;
-      break;
-    case PLY_INT:
-      value = &int_val;
-      break;
-    case PLY_UCHAR:
-      uchar_val = uint_val;
-      value = &uchar_val;
-      break;
-    case PLY_USHORT:
-      ushort_val = uint_val;
-      value = &ushort_val;
-      break;
-    case PLY_UINT:
-      value = &uint_val;
-      break;
-    case PLY_FLOAT:
-      float_val = double_val;
-      value = &float_val;
-      break;
-    case PLY_DOUBLE:
-      value = &double_val;
-      break;
-    default:
-      fprintf (stderr, "write_binary_item: bad type = %d\n", type);
-      exit (-1);
-  }
-	int ply_type_size[] = {
-	  0, 1, 2, 4, 1, 2, 4, 4, 8
-	};
-
-  if ((file_type != *native_binary_type) && (ply_type_size[type] > 1))
-     swap_bytes((char *)value, ply_type_size[type]);
-  
-  if (fwrite (value, ply_type_size[type], 1, fp) != 1)
-  {
-      fprintf(stderr, "PLY ERROR: fwrite() failed -- aborting.\n");
-      exit(1);
-  }
-}
-
-
-/******************************************************************************
-Write out an item to a file as ascii characters.
-
-Entry:
-  fp         - file to write to
-  int_val    - integer version of item
-  uint_val   - unsigned integer version of item
-  double_val - double-precision float version of item
-  type       - data type to write out
-******************************************************************************/
-
-inline void write_ascii_item(
-  FILE *fp,
-  int int_val,
-  unsigned int uint_val,
-  double double_val,
-  int type
-)
-{
-  switch (type) {
-    case PLY_CHAR:
-    case PLY_SHORT:
-    case PLY_INT:
-      if (fprintf (fp, "%d ", int_val) <= 0)
-      {
-	  fprintf(stderr, "PLY ERROR: fprintf() failed -- aborting.\n");
-	  exit(1);
-      }
-      break;
-    case PLY_UCHAR:
-    case PLY_USHORT:
-    case PLY_UINT:
-      if (fprintf (fp, "%u ", uint_val) <= 0)
-      {
-	  fprintf(stderr, "PLY ERROR: fprintf() failed -- aborting.\n");
-	  exit(1);
-      }
-      break;
-    case PLY_FLOAT:
-    case PLY_DOUBLE:
-      if (fprintf (fp, "%g ", double_val) <= 0)
-      {
-	  fprintf(stderr, "PLY ERROR: fprintf() failed -- aborting.\n");
-	  exit(1);
-      }
-      break;
-    default:
-      fprintf (stderr, "write_ascii_item: bad type = %d\n", type);
-      exit (-1);
-  }
-}
-
-
-/******************************************************************************
-Write out an item to a file as ascii characters.
-
-Entry:
-  fp   - file to write to
-  item - pointer to item to write
-  type - data type that "item" points to
-
-Exit:
-  returns a double-precision float that contains the value of the written item
-******************************************************************************/
-
-inline double old_write_ascii_item(FILE *fp, char *item, int type)
-{
-  unsigned char *puchar;
-  char *pchar;
-  short int *pshort;
-  unsigned short int *pushort;
-  int *pint;
-  unsigned int *puint;
-  float *pfloat;
-  double *pdouble;
-  int int_value;
-  unsigned int uint_value;
-  double double_value;
-
-  switch (type) {
-    case PLY_CHAR:
-      pchar = (char *) item;
-      int_value = *pchar;
-      fprintf (fp, "%d ", int_value);
-      return ((double) int_value);
-    case PLY_UCHAR:
-      puchar = (unsigned char *) item;
-      int_value = *puchar;
-      fprintf (fp, "%d ", int_value);
-      return ((double) int_value);
-    case PLY_SHORT:
-      pshort = (short int *) item;
-      int_value = *pshort;
-      fprintf (fp, "%d ", int_value);
-      return ((double) int_value);
-    case PLY_USHORT:
-      pushort = (unsigned short int *) item;
-      int_value = *pushort;
-      fprintf (fp, "%d ", int_value);
-      return ((double) int_value);
-    case PLY_INT:
-      pint = (int *) item;
-      int_value = *pint;
-      fprintf (fp, "%d ", int_value);
-      return ((double) int_value);
-    case PLY_UINT:
-      puint = (unsigned int *) item;
-      uint_value = *puint;
-      fprintf (fp, "%u ", uint_value);
-      return ((double) uint_value);
-    case PLY_FLOAT:
-      pfloat = (float *) item;
-      double_value = *pfloat;
-      fprintf (fp, "%g ", double_value);
-      return (double_value);
-    case PLY_DOUBLE:
-      pdouble = (double *) item;
-      double_value = *pdouble;
-      fprintf (fp, "%g ", double_value);
-      return (double_value);
-    default:
-      fprintf (stderr, "old_write_ascii_item: bad type = %d\n", type);
-      exit (-1);
-  }
-}
-
-
-/******************************************************************************
-Get the value of an item that is in memory, and place the result
-into an integer, an unsigned integer and a double.
-
-Entry:
-  ptr  - pointer to the item
-  type - data type supposedly in the item
-
-Exit:
-  int_val    - integer value
-  uint_val   - unsigned integer value
-  double_val - double-precision floating point value
-******************************************************************************/
-
-inline void get_stored_item(
-  void *ptr,
-  int type,
-  int *int_val,
-  unsigned int *uint_val,
-  double *double_val
-)
-{
-  switch (type) {
-    case PLY_CHAR:
-      *int_val = *((char *) ptr);
-      *uint_val = *int_val;
-      *double_val = *int_val;
-      break;
-    case PLY_UCHAR:
-      *uint_val = *((unsigned char *) ptr);
-      *int_val = *uint_val;
-      *double_val = *uint_val;
-      break;
-    case PLY_SHORT:
-      *int_val = *((short int *) ptr);
-      *uint_val = *int_val;
-      *double_val = *int_val;
-      break;
-    case PLY_USHORT:
-      *uint_val = *((unsigned short int *) ptr);
-      *int_val = *uint_val;
-      *double_val = *uint_val;
-      break;
-    case PLY_INT:
-      *int_val = *((int *) ptr);
-      *uint_val = *int_val;
-      *double_val = *int_val;
-      break;
-    case PLY_UINT:
-      *uint_val = *((unsigned int *) ptr);
-      *int_val = *uint_val;
-      *double_val = *uint_val;
-      break;
-    case PLY_FLOAT:
-      *double_val = *((float *) ptr);
-      *int_val = (int) *double_val;
-      *uint_val = (unsigned int) *double_val;
-      break;
-    case PLY_DOUBLE:
-      *double_val = *((double *) ptr);
-      *int_val = (int) *double_val;
-      *uint_val = (unsigned int) *double_val;
-      break;
-    default:
-      fprintf (stderr, "get_stored_item: bad type = %d\n", type);
-      exit (-1);
-  }
-}
-
-
-/******************************************************************************
-Get the value of an item from a binary file, and place the result
-into an integer, an unsigned integer and a double.
-
-Entry:
-  fp   - file to get item from
-  type - data type supposedly in the word
-
-Exit:
-  int_val    - integer value
-  uint_val   - unsigned integer value
-  double_val - double-precision floating point value
-******************************************************************************/
-
-inline void get_binary_item(
-  FILE *fp,
-  int file_type,
-  int type,
-  int *int_val,
-  unsigned int *uint_val,
-  double *double_val,
-  int *native_binary_type
-)
-{
-  char c[8];
-  void *ptr;
-
-  ptr = (void *) c;
-	int ply_type_size[] = {
-	  0, 1, 2, 4, 1, 2, 4, 4, 8
-	};
-
-  if (fread (ptr, ply_type_size[type], 1, fp) != 1)
-  {
-      fprintf(stderr, "PLY ERROR: fread() failed -- aborting.\n");
-      exit(1);
-  }
-  
-
-  if ((file_type != *native_binary_type) && (ply_type_size[type] > 1))
-     swap_bytes((char *)ptr, ply_type_size[type]);
-
-  switch (type) {
-    case PLY_CHAR:
-      *int_val = *((char *) ptr);
-      *uint_val = *int_val;
-      *double_val = *int_val;
-      break;
-    case PLY_UCHAR:
-      *uint_val = *((unsigned char *) ptr);
-      *int_val = *uint_val;
-      *double_val = *uint_val;
-      break;
-    case PLY_SHORT:
-      *int_val = *((short int *) ptr);
-      *uint_val = *int_val;
-      *double_val = *int_val;
-      break;
-    case PLY_USHORT:
-      *uint_val = *((unsigned short int *) ptr);
-      *int_val = *uint_val;
-      *double_val = *uint_val;
-      break;
-    case PLY_INT:
-      *int_val = *((int *) ptr);
-      *uint_val = *int_val;
-      *double_val = *int_val;
-      break;
-    case PLY_UINT:
-      *uint_val = *((unsigned int *) ptr);
-      *int_val = *uint_val;
-      *double_val = *uint_val;
-      break;
-    case PLY_FLOAT:
-      *double_val = *((float *) ptr);
-      *int_val = (int) *double_val;
-      *uint_val = (unsigned int) *double_val;
-      break;
-    case PLY_DOUBLE:
-      *double_val = *((double *) ptr);
-      *int_val = (int) *double_val;
-      *uint_val = (unsigned int) *double_val;
-      break;
-    default:
-      fprintf (stderr, "get_binary_item: bad type = %d\n", type);
-      exit (-1);
-  }
-}
-
-
-/******************************************************************************
-Extract the value of an item from an ascii word, and place the result
-into an integer, an unsigned integer and a double.
-
-Entry:
-  word - word to extract value from
-  type - data type supposedly in the word
-
-Exit:
-  int_val    - integer value
-  uint_val   - unsigned integer value
-  double_val - double-precision floating point value
-******************************************************************************/
-
-inline void get_ascii_item(
-  char *word,
-  int type,
-  int *int_val,
-  unsigned int *uint_val,
-  double *double_val
-)
-{
-  switch (type) {
-    case PLY_CHAR:
-    case PLY_UCHAR:
-    case PLY_SHORT:
-    case PLY_USHORT:
-    case PLY_INT:
-      *int_val = atoi (word);
-      *uint_val = (unsigned int) *int_val;
-      *double_val = (double) *int_val;
-      break;
-
-    case PLY_UINT:
-      *uint_val = strtol (word, (char **) NULL, 10);
-      *int_val = (int) *uint_val;
-      *double_val = (double) *uint_val;
-      break;
-
-    case PLY_FLOAT:
-    case PLY_DOUBLE:
-      *double_val = atof (word);
-      *int_val = (int) *double_val;
-      *uint_val = (unsigned int) *double_val;
-      break;
-
-    default:
-      fprintf (stderr, "get_ascii_item: bad type = %d\n", type);
-      exit (-1);
-  }
-}
-
-
-/******************************************************************************
-Store a value into a place being pointed to, guided by a data type.
-
-Entry:
-  item       - place to store value
-  type       - data type
-  int_val    - integer version of value
-  uint_val   - unsigned integer version of value
-  double_val - double version of value
-
-Exit:
-  item - pointer to stored value
-******************************************************************************/
-
-inline void store_item (
-  char *item,
-  int type,
-  int int_val,
-  unsigned int uint_val,
-  double double_val
-)
-{
-  unsigned char *puchar;
-  short int *pshort;
-  unsigned short int *pushort;
-  int *pint;
-  unsigned int *puint;
-  float *pfloat;
-  double *pdouble;
-
-  switch (type) {
-    case PLY_CHAR:
-      *item = int_val;
-      break;
-    case PLY_UCHAR:
-      puchar = (unsigned char *) item;
-      *puchar = uint_val;
-      break;
-    case PLY_SHORT:
-      pshort = (short *) item;
-      *pshort = int_val;
-      break;
-    case PLY_USHORT:
-      pushort = (unsigned short *) item;
-      *pushort = uint_val;
-      break;
-    case PLY_INT:
-      pint = (int *) item;
-      *pint = int_val;
-      break;
-    case PLY_UINT:
-      puint = (unsigned int *) item;
-      *puint = uint_val;
-      break;
-    case PLY_FLOAT:
-      pfloat = (float *) item;
-      *pfloat = double_val;
-      break;
-    case PLY_DOUBLE:
-      pdouble = (double *) item;
-      *pdouble = double_val;
-      break;
-    default:
-      fprintf (stderr, "store_item: bad type = %d\n", type);
-      exit (-1);
-  }
-}
-
-
-/******************************************************************************
-Add an element to a PLY file descriptor.
-
-Entry:
-  plyfile - PLY file descriptor
-  words   - list of words describing the element
-  nwords  - number of words in the list
-******************************************************************************/
-
-inline void add_element (PlyFile *plyfile, char **words)
-{
-  PlyElement *elem;
-
-  /* create the new element */
-  elem = (PlyElement *) myalloc (sizeof (PlyElement));
-  elem->name = strdup (words[1]);
-  elem->num = atoi (words[2]);
-  elem->nprops = 0;
-
-  /* make room for new element in the object's list of elements */
-  if (plyfile->nelems == 0)
-    plyfile->elems = (PlyElement **) myalloc (sizeof (PlyElement *));
-  else
-    plyfile->elems = (PlyElement **) realloc (plyfile->elems,
-                     sizeof (PlyElement *) * (plyfile->nelems + 1));
-
-  /* add the new element to the object's list */
-  plyfile->elems[plyfile->nelems] = elem;
-  plyfile->nelems++;
-}
-
-
-/******************************************************************************
-Return the type of a property, given the name of the property.
-
-Entry:
-  name - name of property type
-
-Exit:
-  returns integer code for property, or 0 if not found
-******************************************************************************/
-
-inline int get_prop_type(char *type_name)
-{
-  int i;
-	const char *type_names[] = {
-	"invalid",
-	"char", "short", "int",
-	"uchar", "ushort", "uint",
-	"float", "double",
-	};
-	
-	const char *alt_type_names[] = { 
-	"invalid",
-	"int8", "int16", "int32", "uint8", "uint16", "uint32", "float32", "float64",
-	};
-
-
-  for (i = PLY_START_TYPE + 1; i < PLY_END_TYPE; i++)
-    if (equal_strings (type_name, type_names[i]))
-      return (i);
-
-  for (i = PLY_START_TYPE + 1; i < PLY_END_TYPE; i++)
-    if (equal_strings (type_name, alt_type_names[i]))
-      return (i);
-
-  /* if we get here, we didn't find the type */
-  return (0);
-}
-
-
-/******************************************************************************
-Add a property to a PLY file descriptor.
-
-Entry:
-  plyfile - PLY file descriptor
-  words   - list of words describing the property
-  nwords  - number of words in the list
-******************************************************************************/
-
-inline void add_property (PlyFile *plyfile, char **words)
-{
-  PlyProperty *prop;
-  PlyElement *elem;
-
-  /* create the new property */
-
-  prop = (PlyProperty *) myalloc (sizeof (PlyProperty));
-
-  if (equal_strings (words[1], "list")) {       /* is a list */
-    prop->count_external = get_prop_type (words[2]);
-    prop->external_type = get_prop_type (words[3]);
-    prop->name = strdup (words[4]);
-    prop->is_list = 1;
-  }
-  else {                                        /* not a list */
-    prop->external_type = get_prop_type (words[1]);
-    prop->name = strdup (words[2]);
-    prop->is_list = 0;
-  }
-
-  /* add this property to the list of properties of the current element */
-
-  elem = plyfile->elems[plyfile->nelems - 1];
-
-  if (elem->nprops == 0)
-    elem->props = (PlyProperty **) myalloc (sizeof (PlyProperty *));
-  else
-    elem->props = (PlyProperty **) realloc (elem->props,
-                  sizeof (PlyProperty *) * (elem->nprops + 1));
-
-  elem->props[elem->nprops] = prop;
-  elem->nprops++;
-}
-
-
-/******************************************************************************
-Add a comment to a PLY file descriptor.
-
-Entry:
-  plyfile - PLY file descriptor
-  line    - line containing comment
-******************************************************************************/
-
-inline void add_comment (PlyFile *plyfile, char *line)
-{
-  int i;
-
-  /* skip over "comment" and leading spaces and tabs */
-  i = 7;
-  while (line[i] == ' ' || line[i] == '\t')
-    i++;
-
-  ply_put_comment (plyfile, &line[i]);
-}
-
-
-/******************************************************************************
-Add a some object information to a PLY file descriptor.
-
-Entry:
-  plyfile - PLY file descriptor
-  line    - line containing text info
-******************************************************************************/
-
-inline void add_obj_info (PlyFile *plyfile, char *line)
-{
-  int i;
-
-  /* skip over "obj_info" and leading spaces and tabs */
-  i = 8;
-  while (line[i] == ' ' || line[i] == '\t')
-    i++;
-
-  ply_put_obj_info (plyfile, &line[i]);
-}
-
-
-/******************************************************************************
-Copy a property.
-******************************************************************************/
-
-inline void copy_property(PlyProperty *dest, PlyProperty *src)
-{
-  dest->name = strdup (src->name);
-  dest->external_type = src->external_type;
-  dest->internal_type = src->internal_type;
-  dest->offset = src->offset;
-
-  dest->is_list = src->is_list;
-  dest->count_external = src->count_external;
-  dest->count_internal = src->count_internal;
-  dest->count_offset = src->count_offset;
-}
-
-
-/******************************************************************************
-Allocate some memory.
-
-Entry:
-  size  - amount of memory requested (in bytes)
-  lnum  - line number from which memory was requested
-  fname - file name from which memory was requested
-******************************************************************************/
-
-inline char *my_alloc(int size, int lnum, const char *fe)
-{
-  char *ptr;
-
-  ptr = (char *) malloc (size);
-
-  if (ptr == 0) {
-    fprintf(stderr, "Memory allocation bombed on line %d in %s\n", lnum, fe);
-  }
-
-  return (ptr);
-}
-
-}
-}
-#endif

+ 634 - 173
include/igl/readPLY.cpp

@@ -1,224 +1,685 @@
-// This file is part of libigl, a simple c++ geometry processing library.
-//
-// Copyright (C) 2014 Alec Jacobson <[email protected]>
-//
-// This Source Code Form is subject to the terms of the Mozilla Public License
-// v. 2.0. If a copy of the MPL was not distributed with this file, You can
-// obtain one at http://mozilla.org/MPL/2.0/.
 #include "readPLY.h"
 #include "readPLY.h"
-#include "list_to_matrix.h"
-#include "ply.h"
+#include <string>
+#include <set>
+#include <fstream>
 #include <iostream>
 #include <iostream>
+#include <Eigen/Core>
 
 
-template <
-  typename Vtype,
-  typename Ftype,
-  typename Ntype,
-  typename UVtype>
-IGL_INLINE bool igl::readPLY(
-  const std::string filename,
-  std::vector<std::vector<Vtype> > & V,
-  std::vector<std::vector<Ftype> > & F,
-  std::vector<std::vector<Ntype> > & N,
-  std::vector<std::vector<UVtype> >  & UV)
-{
-  using namespace std;
-  // Largely follows ply2iv.c
-  FILE * ply_file = fopen(filename.c_str(),"r");
-  if(ply_file == NULL)
-  {
-    return false;
-  }
-  return readPLY(ply_file,V,F,N,UV);
+#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 Vtype,
-  typename Ftype,
-  typename Ntype,
-  typename UVtype>
-IGL_INLINE bool igl::readPLY(
-  FILE * ply_file,
-  std::vector<std::vector<Vtype> > & V,
-  std::vector<std::vector<Ftype> > & F,
-  std::vector<std::vector<Ntype> > & N,
-  std::vector<std::vector<UVtype> >  & UV)
-{
-  using namespace std;
-   typedef struct Vertex {
-     double x,y,z;          /* position */
-     double nx,ny,nz;         /* surface normal */
-     double s,t;              /* texture coordinates */
-     void *other_props;       /* other properties */
-   } Vertex;
-
-   typedef struct Face {
-     unsigned char nverts;    /* number of vertex indices in list */
-     int *verts;              /* vertex index list */
-     void *other_props;       /* other properties */
-   } Face;
-
-  igl::ply::PlyProperty vert_props[] = { /* list of property information for a vertex */
-    {"x", PLY_DOUBLE, PLY_DOUBLE, offsetof(Vertex,x), 0, 0, 0, 0},
-    {"y", PLY_DOUBLE, PLY_DOUBLE, offsetof(Vertex,y), 0, 0, 0, 0},
-    {"z", PLY_DOUBLE, PLY_DOUBLE, offsetof(Vertex,z), 0, 0, 0, 0},
-    {"nx", PLY_DOUBLE, PLY_DOUBLE, offsetof(Vertex,nx), 0, 0, 0, 0},
-    {"ny", PLY_DOUBLE, PLY_DOUBLE, offsetof(Vertex,ny), 0, 0, 0, 0},
-    {"nz", PLY_DOUBLE, PLY_DOUBLE, offsetof(Vertex,nz), 0, 0, 0, 0},
-    {"s", PLY_DOUBLE, PLY_DOUBLE, offsetof(Vertex,s), 0, 0, 0, 0},
-    {"t", PLY_DOUBLE, PLY_DOUBLE, offsetof(Vertex,t), 0, 0, 0, 0},
-  };
-
-  igl::ply::PlyProperty face_props[] = { /* list of property information for a face */
-    {"vertex_indices", PLY_INT, PLY_INT, offsetof(Face,verts),
-      1, PLY_UCHAR, PLY_UCHAR, offsetof(Face,nverts)},
-  };
-
-  int nelems;
-  char ** elem_names;
-  igl::ply::PlyFile * in_ply = igl::ply::ply_read(ply_file,&nelems,&elem_names);
-  if(in_ply==NULL)
-  {
-    return false;
-  }
-
-  bool has_normals = false;
-  bool has_texture_coords = false;
-  igl::ply::PlyProperty **plist;
-  int nprops;
-  int elem_count;
-  plist = ply_get_element_description (in_ply,"vertex", &elem_count, &nprops);
-  int native_binary_type = igl::ply::get_native_binary_type2();
-  if (plist != NULL)
-  {
-    /* set up for getting vertex elements */
-    ply_get_property (in_ply,"vertex",&vert_props[0]);
-    ply_get_property (in_ply,"vertex",&vert_props[1]);
-    ply_get_property (in_ply,"vertex",&vert_props[2]);
-    for (int j = 0; j < nprops; j++)
+
+
+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++)
     {
     {
-      igl::ply::PlyProperty * prop = plist[j];
-      if (igl::ply::equal_strings ("nx", prop->name) 
-        || igl::ply::equal_strings ("ny", prop->name)
-        || igl::ply::equal_strings ("nz", prop->name))
-      {
-        ply_get_property (in_ply,"vertex",&vert_props[3]);
-        ply_get_property (in_ply,"vertex",&vert_props[4]);
-        ply_get_property (in_ply,"vertex",&vert_props[5]);
-        has_normals = true;
-      }
-      if (igl::ply::equal_strings ("s", prop->name) ||
-        igl::ply::equal_strings ("t", prop->name))
+      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)
       {
       {
-        ply_get_property(in_ply,"vertex",&vert_props[6]);
-        ply_get_property(in_ply,"vertex",&vert_props[7]);
-        has_texture_coords = true;
+        // 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;
       }
       }
     }
     }
-    // Is this call necessary?
-    ply_get_other_properties(in_ply,"vertex",
-				     offsetof(Vertex,other_props));
-    V.resize(elem_count,std::vector<Vtype>(3));
-    if(has_normals)
-    {
-      N.resize(elem_count,std::vector<Ntype>(3));
-    }else
+  }
+  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))
     {
     {
-      N.resize(0);
+        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;
     }
     }
-    if(has_texture_coords)
+
+    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)
     {
     {
-      UV.resize(elem_count,std::vector<UVtype>(2));
-    }else
+        setg(p_start, p_start, p_end);
+    }
+
+    pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode which) override
     {
     {
-      UV.resize(0);
+        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;
     }
     }
-   	
-	for(int j = 0;j<elem_count;j++)
+
+    pos_type seekpos(pos_type pos, std::ios_base::openmode which) override
     {
     {
-      Vertex v;
-      ply_get_element_setup(in_ply,"vertex",3,vert_props);
-      ply_get_element(in_ply,(void*)&v, &native_binary_type);
-      V[j][0] = v.x;
-      V[j][1] = v.y;
-      V[j][2] = v.z;
-      if(has_normals)
+        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
       {
       {
-        N[j][0] = v.nx;
-        N[j][1] = v.ny;
-        N[j][2] = v.nz;
+        for (auto p : e.properties)
+        {
+          if(vertex_std.find(p.name) == vertex_std.end())
+          {
+              _vertex_header.push_back(p.name);
+          }
+        }
       }
       }
-      if(has_texture_coords)
+      else if(e.name == "face" ) // found face
       {
       {
-        UV[j][0] = v.s;
-        UV[j][1] = v.t;
+        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" });
   }
   }
-  plist = ply_get_element_description (in_ply,"face", &elem_count, &nprops);
-  if (plist != NULL)
+  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)
   {
   {
-    F.resize(elem_count);
-    ply_get_property(in_ply,"face",&face_props[0]);
-    for (int j = 0; j < elem_count; j++) 
-    {
-      Face f;
-      ply_get_element(in_ply, (void *) &f, &native_binary_type);
-      for(size_t c = 0;c<f.nverts;c++)
+      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)
       {
       {
-        F[j].push_back(f.verts[c]);
+          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);
     }
     }
   }
   }
-  ply_close(in_ply);
+
+  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;
   return true;
 }
 }
 
 
+
 template <
 template <
   typename DerivedV,
   typename DerivedV,
   typename DerivedF,
   typename DerivedF,
+  typename DerivedE,
   typename DerivedN,
   typename DerivedN,
-  typename DerivedUV>
-IGL_INLINE bool igl::readPLY(
-  const std::string filename,
+  typename DerivedUV,
+  typename DerivedVD,
+  typename DerivedFD,
+  typename DerivedED
+  >
+IGL_INLINE bool readPLY(
+  const std::string& ply_file,
   Eigen::PlainObjectBase<DerivedV> & V,
   Eigen::PlainObjectBase<DerivedV> & V,
   Eigen::PlainObjectBase<DerivedF> & F,
   Eigen::PlainObjectBase<DerivedF> & F,
+  Eigen::PlainObjectBase<DerivedE> & E,
   Eigen::PlainObjectBase<DerivedN> & N,
   Eigen::PlainObjectBase<DerivedN> & N,
-  Eigen::PlainObjectBase<DerivedUV> & UV)
+  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::vector<std::vector<typename DerivedV::Scalar> > vV;
-  std::vector<std::vector<typename DerivedF::Scalar> > vF;
-  std::vector<std::vector<typename DerivedN::Scalar> > vN;
-  std::vector<std::vector<typename DerivedUV::Scalar> > vUV;
-  if(!readPLY(filename,vV,vF,vN,vUV))
+
+  std::ifstream ply_stream(ply_file, std::ios::binary);
+  if (ply_stream.fail())
   {
   {
-    return false;
+      std::cerr << "ReadPLY: Error opening file " << ply_file << std::endl;
+      return false;
   }
   }
-  return 
-    list_to_matrix(vV,V) &&
-    list_to_matrix(vF,F) &&
-    list_to_matrix(vN,N) &&
-    list_to_matrix(vUV,UV);
+  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 <
 template <
   typename DerivedV,
   typename DerivedV,
-  typename DerivedF>
-IGL_INLINE bool igl::readPLY(
-  const std::string filename,
+  typename DerivedF,
+  typename DerivedE
+  >
+IGL_INLINE bool readPLY(
+  const std::string & filename,
   Eigen::PlainObjectBase<DerivedV> & V,
   Eigen::PlainObjectBase<DerivedV> & V,
-  Eigen::PlainObjectBase<DerivedF> & F)
+  Eigen::PlainObjectBase<DerivedF> & F,
+  Eigen::PlainObjectBase<DerivedE> & E
+  )
 {
 {
   Eigen::MatrixXd N,UV;
   Eigen::MatrixXd N,UV;
-  return readPLY(filename,V,F,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
 #ifdef IGL_STATIC_LIBRARY
 // Explicit template instantiation
 // Explicit template instantiation
-template bool igl::readPLY<double, int, double, double>(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const, std::vector<std::vector<double, std::allocator<double> >, std::allocator<std::vector<double, std::allocator<double> > > >&, std::vector<std::vector<int, std::allocator<int> >, std::allocator<std::vector<int, std::allocator<int> > > >&, std::vector<std::vector<double, std::allocator<double> >, std::allocator<std::vector<double, std::allocator<double> > > >&, std::vector<std::vector<double, std::allocator<double> >, std::allocator<std::vector<double, std::allocator<double> > > >&);
-
-template bool igl::readPLY<Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>, Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic>, Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>, Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const, Eigen::PlainObjectBase<Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> > &, Eigen::PlainObjectBase<Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic> > &, Eigen::PlainObjectBase<Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> > &, Eigen::PlainObjectBase<Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> > &);
+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, Eigen::Dynamic, Eigen::Dynamic>, Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const, Eigen::PlainObjectBase<Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic> > &, Eigen::PlainObjectBase<Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic> > &);
-template bool igl::readPLY<Eigen::Matrix<float, -1, 3, 1, -1, 3>, Eigen::Matrix<int, -1, 3, 1, -1, 3> >(std::string, 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<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
 #endif
+

+ 192 - 48
include/igl/readPLY.h

@@ -1,77 +1,221 @@
-// This file is part of libigl, a simple c++ geometry processing library.
-// 
-// Copyright (C) 2014 Alec Jacobson <[email protected]>
-// 
-// This Source Code Form is subject to the terms of the Mozilla Public License 
-// v. 2.0. If a copy of the MPL was not distributed with this file, You can 
-// obtain one at http://mozilla.org/MPL/2.0/.
 #ifndef IGL_READPLY_H
 #ifndef IGL_READPLY_H
 #define IGL_READPLY_H
 #define IGL_READPLY_H
-#include "igl_inline.h"
+#include <igl/igl_inline.h>
 #include <Eigen/Core>
 #include <Eigen/Core>
 #include <string>
 #include <string>
 #include <vector>
 #include <vector>
-#include <cstdio>
+#include "tinyply.h"
 
 
 namespace igl
 namespace igl
 {
 {
-  // Read a mesh from a .ply file. 
+template <
+  typename DerivedV,
+  typename DerivedF
+  >
+IGL_INLINE bool readPLY(
+  FILE *fp,
+  Eigen::PlainObjectBase<DerivedV> & V,
+  Eigen::PlainObjectBase<DerivedF> & F
+  );
+
+
+// Read triangular mesh from ply file, filling in vertex positions, normals
+  // and texture coordinates, if available
+  // also read additional properties associated with vertex,faces and edges 
+  // and file comments
+  //
+  // Templates:
+  //   Derived from Eigen matrix parameters
+  // Inputs:
+  //  ply_stream  ply file input stream
+  // Outputs:
+  //   V  (#V,3) matrix of vertex positions 
+  //   F  (#F,3) list of face indices into vertex positions
+  //   E  (#E,2) list of edge indices into vertex positions
+  //   N  (#V,3) list of normals
+  //   UV (#V,2) list of texture coordinates
+  //   VD (#V,*) additional vertex data
+  //   Vheader (#V) list of vertex data headers
+  //   FD (#F,*) additional face data
+  //   Fheader (#F) list of face data headers
+  //   ED (#E,*) additional edge data
+  //   Eheader (#E) list of edge data headers
+  //   comments (*) file comments
+  // Returns true on success, false on errors
+  template <
+    typename DerivedV,
+    typename DerivedF,
+    typename DerivedE,
+    typename DerivedN,
+    typename DerivedUV,
+    typename DerivedVD,
+    typename DerivedFD,
+    typename DerivedED
+    >
+  bool readPLY(
+    std::istream & ply_stream,
+    Eigen::PlainObjectBase<DerivedV> & V,
+    Eigen::PlainObjectBase<DerivedF> & F,
+    Eigen::PlainObjectBase<DerivedF> & 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
+    );
+
+  // Read triangular mesh from ply file, filling in vertex positions, normals
+  // and texture coordinates, if available
+  // also read additional properties associated with vertex,faces and edges 
+  // and file comments
   //
   //
+  // Templates:
+  //   Derived from Eigen matrix parameters
   // Inputs:
   // Inputs:
-  //   filename  path to .ply file
+  //  ply_file  ply file name
   // Outputs:
   // Outputs:
-  //   V  #V by 3 list of vertex positions
-  //   F  #F list of lists of triangle indices
-  //   N  #V by 3 list of vertex normals
-  //   UV  #V by 2 list of vertex texture coordinates
-  // Returns true iff success
+  //   V  (#V,3) matrix of vertex positions 
+  //   F  (#F,3) list of face indices into vertex positions
+  //   E  (#E,2) list of edge indices into vertex positions
+  //   N  (#V,3) list of normals
+  //   UV (#V,2) list of texture coordinates
+  //   VD (#V,*) additional vertex data
+  //   Vheader (#V) list of vertex data headers
+  //   FD (#F,*) additional face data
+  //   Fheader (#F) list of face data headers
+  //   ED (#E,*) additional edge data
+  //   Eheader (#E) list of edge data headers
+  //   comments (*) file comments
+  // Returns true on success, false on errors
   template <
   template <
-    typename Vtype,
-    typename Ftype,
-    typename Ntype,
-    typename UVtype>
-  IGL_INLINE bool readPLY(
-    const std::string filename,
-    std::vector<std::vector<Vtype> > & V,
-    std::vector<std::vector<Ftype> > & F,
-    std::vector<std::vector<Ntype> > & N,
-    std::vector<std::vector<UVtype> >  & UV);
+    typename DerivedV,
+    typename DerivedF,
+    typename DerivedE,
+    typename DerivedN,
+    typename DerivedUV,
+    typename DerivedVD,
+    typename DerivedFD,
+    typename DerivedED
+    >
+  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
+    );
+
+
+  // Read triangular mesh from ply file, filling in vertex positions, normals
+  // and texture coordinates, if available
+  // also read additional properties associated with vertex,faces and edges 
+  // and file comments
+  //
+  // Templates:
+  //   Derived from Eigen matrix parameters
+  // Inputs:
+  //  ply_file  ply file name
+  // Outputs:
+  //   V  (#V,3) matrix of vertex positions 
+  //   F  (#F,3) list of face indices into vertex positions
+  //   N  (#V,3) list of normals
+  //   UV (#V,2) list of texture coordinates
+  //   VD (#V,*) additional vertex data
+  //   Vheader (#V) list of vertex data headers
+  // Returns true on success, false on errors
   template <
   template <
-    typename Vtype,
-    typename Ftype,
-    typename Ntype,
-    typename UVtype>
+    typename DerivedV,
+    typename DerivedF,
+    typename DerivedN,
+    typename DerivedUV,
+    typename DerivedVD
+    >
+  bool readPLY(
+    const std::string & filename,
+    Eigen::PlainObjectBase<DerivedV> & V,
+    Eigen::PlainObjectBase<DerivedF> & F,
+    Eigen::PlainObjectBase<DerivedN> & N,
+    Eigen::PlainObjectBase<DerivedUV> & UV,
+    Eigen::PlainObjectBase<DerivedVD> & VD,
+    std::vector<std::string> & Vheader
+    );
+
+  // Read triangular mesh from ply file, filling in vertex positions, normals
+  // and texture coordinates, if available
+  // also read additional properties associated with vertex,faces and edges 
+  // and file comments
+  //
+  // Templates:
+  //   Derived from Eigen matrix parameters
   // Inputs:
   // Inputs:
-  //   ply_file  pointer to already opened .ply file 
+  //  ply_file  ply file name
   // Outputs:
   // Outputs:
-  //   ply_file  closed file
-  IGL_INLINE bool readPLY(
-    FILE * ply_file,
-    std::vector<std::vector<Vtype> > & V,
-    std::vector<std::vector<Ftype> > & F,
-    std::vector<std::vector<Ntype> > & N,
-    std::vector<std::vector<UVtype> >  & UV);
-    template <
+  //   V  (#V,3) matrix of vertex positions 
+  //   F  (#F,3) list of face indices into vertex positions
+  //   E  (#E,2) list of edge indices into vertex positions
+  //   N  (#V,3) list of normals
+  //   UV (#V,2) list of texture coordinates
+  //   VD (#V,*) additional vertex data
+  //   Vheader (#V) list of vertex data headers
+  // Returns true on success, false on errors
+  template <
     typename DerivedV,
     typename DerivedV,
     typename DerivedF,
     typename DerivedF,
+    typename DerivedE,
     typename DerivedN,
     typename DerivedN,
-    typename DerivedUV>
-  IGL_INLINE bool readPLY(
-    const std::string filename,
+    typename DerivedUV
+    >
+  bool readPLY(
+    const std::string & filename,
     Eigen::PlainObjectBase<DerivedV> & V,
     Eigen::PlainObjectBase<DerivedV> & V,
     Eigen::PlainObjectBase<DerivedF> & F,
     Eigen::PlainObjectBase<DerivedF> & F,
+    Eigen::PlainObjectBase<DerivedE> & E,
     Eigen::PlainObjectBase<DerivedN> & N,
     Eigen::PlainObjectBase<DerivedN> & N,
-    Eigen::PlainObjectBase<DerivedUV> & UV);
+    Eigen::PlainObjectBase<DerivedUV> & UV
+    );
+
+
+  template <
+    typename DerivedV,
+    typename DerivedF
+    >
+  bool readPLY(
+    const std::string & filename,
+    Eigen::PlainObjectBase<DerivedV> & V,
+    Eigen::PlainObjectBase<DerivedF> & F
+    );
+
   template <
   template <
     typename DerivedV,
     typename DerivedV,
-    typename DerivedF>
-  IGL_INLINE bool readPLY(
-    const std::string filename,
+    typename DerivedF,
+    typename DerivedE
+    >
+  bool readPLY(
+    const std::string & filename,
     Eigen::PlainObjectBase<DerivedV> & V,
     Eigen::PlainObjectBase<DerivedV> & V,
-    Eigen::PlainObjectBase<DerivedF> & F);
+    Eigen::PlainObjectBase<DerivedF> & F,
+    Eigen::PlainObjectBase<DerivedE> & E
+    );
+
 }
 }
+
 #ifndef IGL_STATIC_LIBRARY
 #ifndef IGL_STATIC_LIBRARY
 #  include "readPLY.cpp"
 #  include "readPLY.cpp"
 #endif
 #endif
 #endif
 #endif
-

+ 2 - 4
include/igl/read_triangle_mesh.cpp

@@ -139,10 +139,8 @@ IGL_INLINE bool igl::read_triangle_mesh(
     }
     }
   }else if(ext == "ply")
   }else if(ext == "ply")
   {
   {
-    if(!readPLY(fp,vV,vF,vN,vTC))
-    {
-      return false;
-    }
+    return readPLY(fp, V, F);
+    
   }else if(ext == "stl")
   }else if(ext == "stl")
   {
   {
     if(!readSTL(fp,vV,vF,vN))
     if(!readSTL(fp,vV,vF,vN))

+ 794 - 0
include/igl/tinyply.cpp

@@ -0,0 +1,794 @@
+#include "tinyply.h"
+// Moved from origin tinyply.h
+////////////////////////////////
+//   tinyply implementation   //
+////////////////////////////////
+
+#include <algorithm>
+#include <functional>
+#include <type_traits>
+#include <iostream>
+#include <cstring>
+#include <cassert>
+
+namespace tinyply
+{
+template<typename T, typename T2> T2 endian_swap(const T & v) noexcept {assert(false);} //{ return v; }
+
+template<>  uint16_t IGL_INLINE endian_swap<uint16_t, uint16_t>(const uint16_t & v) noexcept { return (v << 8) | (v >> 8); }
+template<>  uint32_t IGL_INLINE endian_swap<uint32_t, uint32_t>(const uint32_t & v) noexcept { return (v << 24) | ((v << 8) & 0x00ff0000) | ((v >> 8) & 0x0000ff00) | (v >> 24); }
+template<>  uint64_t IGL_INLINE endian_swap<uint64_t, uint64_t>(const uint64_t & v) noexcept
+{
+    return (((v & 0x00000000000000ffLL) << 56) |
+            ((v & 0x000000000000ff00LL) << 40) |
+            ((v & 0x0000000000ff0000LL) << 24) |
+            ((v & 0x00000000ff000000LL) << 8)  |
+            ((v & 0x000000ff00000000LL) >> 8)  |
+            ((v & 0x0000ff0000000000LL) >> 24) |
+            ((v & 0x00ff000000000000LL) >> 40) |
+            ((v & 0xff00000000000000LL) >> 56));
+}
+template<>  int16_t IGL_INLINE endian_swap<int16_t, int16_t>(const int16_t & v) noexcept { uint16_t r = endian_swap<uint16_t, uint16_t>(*(uint16_t*)&v); return *(int16_t*)&r; }
+template<>  int32_t IGL_INLINE endian_swap<int32_t, int32_t>(const int32_t & v) noexcept { uint32_t r = endian_swap<uint32_t, uint32_t>(*(uint32_t*)&v); return *(int32_t*)&r; }
+template<>  int64_t IGL_INLINE endian_swap<int64_t, int64_t>(const int64_t & v) noexcept { uint64_t r = endian_swap<uint64_t, uint64_t>(*(uint64_t*)&v); return *(int64_t*)&r; }
+template<>  float IGL_INLINE endian_swap<uint32_t, float>(const uint32_t & v) noexcept { union { float f; uint32_t i; }; i = endian_swap<uint32_t, uint32_t>(v); return f; }
+template<>  double IGL_INLINE endian_swap<uint64_t, double>(const uint64_t & v) noexcept { union { double d; uint64_t i; }; i = endian_swap<uint64_t, uint64_t>(v); return d; }
+
+
+IGL_INLINE uint32_t hash_fnv1a(const std::string & str) noexcept
+{
+    static const uint32_t fnv1aBase32 = 0x811C9DC5u;
+    static const uint32_t fnv1aPrime32 = 0x01000193u;
+    uint32_t result = fnv1aBase32;
+    for (auto & c : str) { result ^= static_cast<uint32_t>(c); result *= fnv1aPrime32; }
+    return result;
+}
+
+IGL_INLINE Type property_type_from_string(const std::string & t) noexcept
+{
+    if (t == "int8" || t == "char")           return Type::INT8;
+    else if (t == "uint8" || t == "uchar")    return Type::UINT8;
+    else if (t == "int16" || t == "short")    return Type::INT16;
+    else if (t == "uint16" || t == "ushort")  return Type::UINT16;
+    else if (t == "int32" || t == "int")      return Type::INT32;
+    else if (t == "uint32" || t == "uint")    return Type::UINT32;
+    else if (t == "float32" || t == "float")  return Type::FLOAT32;
+    else if (t == "float64" || t == "double") return Type::FLOAT64;
+    return Type::INVALID;
+}
+
+struct PlyFile::PlyFileImpl
+{
+    struct PlyDataCursor
+    {
+        size_t byteOffset{ 0 };
+        size_t totalSizeBytes{ 0 };
+    };
+
+    struct ParsingHelper
+    {
+        std::shared_ptr<PlyData> data;
+        std::shared_ptr<PlyDataCursor> cursor;
+        uint32_t list_size_hint;
+    };
+
+    struct PropertyLookup
+    {
+        ParsingHelper * helper{ nullptr };
+        bool skip{ false };
+        size_t prop_stride{ 0 }; // precomputed
+        size_t list_stride{ 0 }; // precomputed
+    };
+
+    std::unordered_map<uint32_t, ParsingHelper> userData;
+
+    bool isBinary = false;
+    bool isBigEndian = false;
+    std::vector<PlyElement> elements;
+    std::vector<std::string> comments;
+    std::vector<std::string> objInfo;
+    uint8_t scratch[64]; // large enough for max list size
+
+    void read(std::istream & is);
+    void write(std::ostream & os, bool isBinary);
+
+    std::shared_ptr<PlyData> request_properties_from_element(const std::string & elementKey,
+        const std::vector<std::string> propertyKeys,
+        const uint32_t list_size_hint);
+
+    void add_properties_to_element(const std::string & elementKey,
+        const std::vector<std::string> propertyKeys,
+        const Type type, const size_t count, uint8_t * data, const Type listType, const size_t listCount);
+
+    size_t read_property_binary(const size_t & stride, void * dest, size_t & destOffset, std::istream & is) noexcept;
+    size_t read_property_ascii(const Type & t, const size_t & stride, void * dest, size_t & destOffset, std::istream & is);
+
+    std::vector<std::vector<PropertyLookup>> make_property_lookup_table();
+
+    bool parse_header(std::istream & is);
+    void parse_data(std::istream & is, bool firstPass);
+    void read_header_format(std::istream & is);
+    void read_header_element(std::istream & is);
+    void read_header_property(std::istream & is);
+    void read_header_text(std::string line, std::vector<std::string> & place, int erase = 0);
+
+    void write_header(std::ostream & os) noexcept;
+    void write_ascii_internal(std::ostream & os) noexcept;
+    void write_binary_internal(std::ostream & os) noexcept;
+    void write_property_ascii(Type t, std::ostream & os, uint8_t * src, size_t & srcOffset);
+    void write_property_binary(std::ostream & os, uint8_t * src, size_t & srcOffset, const size_t & stride) noexcept;
+};
+
+IGL_INLINE PlyProperty::PlyProperty(std::istream & is) : isList(false)
+{
+    std::string type;
+    is >> type;
+    if (type == "list")
+    {
+        std::string countType;
+        is >> countType >> type;
+        listType = property_type_from_string(countType);
+        isList = true;
+    }
+    propertyType = property_type_from_string(type);
+    is >> name;
+}
+
+IGL_INLINE PlyElement::PlyElement(std::istream & is)
+{
+    is >> name >> size;
+}
+
+template<typename T> IGL_INLINE T ply_read_ascii(std::istream & is)
+{
+    T data;
+    is >> data;
+    return data;
+}
+
+template<typename T, typename T2>
+IGL_INLINE void endian_swap_buffer(uint8_t * data_ptr, const size_t num_bytes, const size_t stride)
+{
+    for (size_t count = 0; count < num_bytes; count += stride)
+    {
+        *(reinterpret_cast<T2 *>(data_ptr)) = endian_swap<T, T2>(*(reinterpret_cast<const T *>(data_ptr)));
+        data_ptr += stride;
+    }
+}
+
+template<typename T> void ply_cast_ascii(void * dest, std::istream & is)
+{
+    *(static_cast<T *>(dest)) = ply_read_ascii<T>(is);
+}
+
+IGL_INLINE int64_t find_element(const std::string & key, const std::vector<PlyElement> & list)
+{
+    for (size_t i = 0; i < list.size(); i++) if (list[i].name == key) return i;
+    return -1;
+}
+
+IGL_INLINE int64_t find_property(const std::string & key, const std::vector<PlyProperty> & list)
+{
+    for (size_t i = 0; i < list.size(); ++i) if (list[i].name == key) return i;
+    return -1;
+}
+
+// The `userData` table is an easy data structure for capturing what data the
+// user would like out of the ply file, but an inner-loop hash lookup is non-ideal. 
+// The property lookup table flattens the table down into a 2D array optimized
+// for parsing. The first index is the element, and the second index is the property. 
+IGL_INLINE std::vector<std::vector<PlyFile::PlyFileImpl::PropertyLookup>> PlyFile::PlyFileImpl::make_property_lookup_table()
+{
+    std::vector<std::vector<PropertyLookup>> element_property_lookup;
+
+    for (auto & element : elements)
+    {
+        std::vector<PropertyLookup> lookups;
+
+        for (auto & property : element.properties)
+        {
+            PropertyLookup f;
+
+            auto cursorIt = userData.find(hash_fnv1a(element.name + property.name));
+            if (cursorIt != userData.end()) f.helper = &cursorIt->second;
+            else f.skip = true;
+
+            f.prop_stride = PropertyTable[property.propertyType].stride;
+            if (property.isList) f.list_stride = PropertyTable[property.listType].stride;
+
+            lookups.push_back(f);
+        }
+
+        element_property_lookup.push_back(lookups);
+    }
+
+    return element_property_lookup;
+}
+
+IGL_INLINE bool PlyFile::PlyFileImpl::parse_header(std::istream & is)
+{
+    std::string line;
+    bool success = true;
+    while (std::getline(is, line))
+    {
+        std::istringstream ls(line);
+        std::string token;
+        ls >> token;
+        if (token == "ply" || token == "PLY" || token == "") continue;
+        else if (token == "comment")    read_header_text(line, comments, 8);
+        else if (token == "format")     read_header_format(ls);
+        else if (token == "element")    read_header_element(ls);
+        else if (token == "property")   read_header_property(ls);
+        else if (token == "obj_info")   read_header_text(line, objInfo, 9);
+        else if (token == "end_header") break;
+        else success = false; // unexpected header field
+    }
+    return success;
+}
+
+IGL_INLINE void PlyFile::PlyFileImpl::read_header_text(std::string line, std::vector<std::string>& place, int erase)
+{
+    place.push_back((erase > 0) ? line.erase(0, erase) : line);
+}
+
+IGL_INLINE void PlyFile::PlyFileImpl::read_header_format(std::istream & is)
+{
+    std::string s;
+    (is >> s);
+    if (s == "binary_little_endian") isBinary = true;
+    else if (s == "binary_big_endian") isBinary = isBigEndian = true;
+}
+
+IGL_INLINE void PlyFile::PlyFileImpl::read_header_element(std::istream & is)
+{
+    elements.emplace_back(is);
+}
+
+IGL_INLINE void PlyFile::PlyFileImpl::read_header_property(std::istream & is)
+{
+    if (!elements.size()) throw std::runtime_error("no elements defined; file is malformed");
+    elements.back().properties.emplace_back(is);
+}
+
+IGL_INLINE size_t PlyFile::PlyFileImpl::read_property_binary(const size_t & stride, void * dest, size_t & destOffset, std::istream & is) noexcept
+{
+    destOffset += stride;
+    is.read((char*)dest, stride);
+    return stride;
+}
+
+IGL_INLINE size_t PlyFile::PlyFileImpl::read_property_ascii(const Type & t, const size_t & stride, void * dest, size_t & destOffset, std::istream & is)
+{
+    destOffset += stride;
+    switch (t)
+    {
+    case Type::INT8:       *((int8_t *)dest)  = static_cast<int8_t>(ply_read_ascii<int32_t>(is));   break;
+    case Type::UINT8:      *((uint8_t *)dest) = static_cast<uint8_t>(ply_read_ascii<uint32_t>(is)); break;
+    case Type::INT16:      ply_cast_ascii<int16_t>(dest, is);                 break;
+    case Type::UINT16:     ply_cast_ascii<uint16_t>(dest, is);                break;
+    case Type::INT32:      ply_cast_ascii<int32_t>(dest, is);                 break;
+    case Type::UINT32:     ply_cast_ascii<uint32_t>(dest, is);                break;
+    case Type::FLOAT32:    ply_cast_ascii<float>(dest, is);                   break;
+    case Type::FLOAT64:    ply_cast_ascii<double>(dest, is);                  break;
+    case Type::INVALID:    throw std::invalid_argument("invalid ply property"); 
+    }
+    return stride;
+}
+
+IGL_INLINE void PlyFile::PlyFileImpl::write_property_ascii(Type t, std::ostream & os, uint8_t * src, size_t & srcOffset)
+{
+    switch (t)
+    {
+    case Type::INT8:       os << static_cast<int32_t>(*reinterpret_cast<int8_t*>(src));   break;
+    case Type::UINT8:      os << static_cast<uint32_t>(*reinterpret_cast<uint8_t*>(src)); break;
+    case Type::INT16:      os << *reinterpret_cast<int16_t*>(src);  break;
+    case Type::UINT16:     os << *reinterpret_cast<uint16_t*>(src); break;
+    case Type::INT32:      os << *reinterpret_cast<int32_t*>(src);  break;
+    case Type::UINT32:     os << *reinterpret_cast<uint32_t*>(src); break;
+    case Type::FLOAT32:    os << *reinterpret_cast<float*>(src);    break;
+    case Type::FLOAT64:    os << *reinterpret_cast<double*>(src);   break;
+    case Type::INVALID:    throw std::invalid_argument("invalid ply property");
+    }
+    os << " ";
+    srcOffset += PropertyTable[t].stride;
+}
+
+IGL_INLINE void PlyFile::PlyFileImpl::write_property_binary(std::ostream & os, uint8_t * src, size_t & srcOffset, const size_t & stride) noexcept
+{
+    os.write((char *)src, stride);
+    srcOffset += stride;
+}
+
+IGL_INLINE void PlyFile::PlyFileImpl::read(std::istream & is)
+{
+    std::vector<std::shared_ptr<PlyData>> buffers;
+    for (auto & entry : userData) buffers.push_back(entry.second.data);
+
+    // Discover if we can allocate up front without parsing the file twice 
+    uint32_t list_hints = 0;
+    for (auto & b : buffers) for (auto & entry : userData) {list_hints += entry.second.list_size_hint;(void)b;}
+
+    // No list hints? Then we need to calculate how much memory to allocate
+    if (list_hints == 0) 
+    {
+        parse_data(is, true);
+    }
+
+    // Count the number of properties (required for allocation)
+    // e.g. if we have properties x y and z requested, we ensure
+    // that their buffer points to the same PlyData 
+    std::unordered_map<PlyData*, int32_t> unique_data_count;
+    for (auto & ptr : buffers) unique_data_count[ptr.get()] += 1;
+
+    // Since group-requested properties share the same cursor, 
+    // we need to find unique cursors so we only allocate once
+    std::sort(buffers.begin(), buffers.end());
+    buffers.erase(std::unique(buffers.begin(), buffers.end()), buffers.end());
+
+    // We sorted by ptrs on PlyData, need to remap back onto its cursor in the userData table
+    for (auto & b : buffers)
+    {
+        for (auto & entry : userData)
+        {
+            if (entry.second.data == b && b->buffer.get() == nullptr)
+            {
+                // If we didn't receive any list hints, it means we did two passes over the
+                // file to compute the total length of all (potentially) variable-length lists
+                if (list_hints == 0)
+                {
+                    b->buffer = Buffer(entry.second.cursor->totalSizeBytes);
+                }
+                else
+                {
+                    // otherwise, we can allocate up front, skipping the first pass.
+                    const size_t list_size_multiplier = (entry.second.data->isList ? entry.second.list_size_hint : 1);
+                    auto bytes_per_property = entry.second.data->count * PropertyTable[entry.second.data->t].stride * list_size_multiplier;
+                    bytes_per_property *= unique_data_count[b.get()];
+                    b->buffer = Buffer(bytes_per_property);
+                }
+
+            }
+        }
+    }
+
+    // Populate the data
+    parse_data(is, false);
+
+    // In-place big-endian to little-endian swapping if required
+    if (isBigEndian)
+    {
+        for (auto & b : buffers)
+        {
+            uint8_t * data_ptr = b->buffer.get();
+            const size_t stride = PropertyTable[b->t].stride;
+            const size_t buffer_size_bytes = b->buffer.size_bytes();
+    
+            switch (b->t)
+            {
+            case Type::INT16:   endian_swap_buffer<int16_t, int16_t>(data_ptr, buffer_size_bytes, stride);   break;
+            case Type::UINT16:  endian_swap_buffer<uint16_t, uint16_t>(data_ptr, buffer_size_bytes, stride); break;
+            case Type::INT32:   endian_swap_buffer<int32_t, int32_t>(data_ptr, buffer_size_bytes, stride);   break;
+            case Type::UINT32:  endian_swap_buffer<uint32_t, uint32_t>(data_ptr, buffer_size_bytes, stride); break;
+            case Type::FLOAT32: endian_swap_buffer<uint32_t, float>(data_ptr, buffer_size_bytes, stride);    break;
+            case Type::FLOAT64: endian_swap_buffer<uint64_t, double>(data_ptr, buffer_size_bytes, stride);   break;
+            default: break;
+            }
+        }
+    }
+}
+
+IGL_INLINE void PlyFile::PlyFileImpl::write(std::ostream & os, bool _isBinary)
+{
+    for (auto & d : userData) { d.second.cursor->byteOffset = 0; }
+    if (_isBinary) 
+    {
+        isBinary = true;
+        isBigEndian = false;
+        write_binary_internal(os);
+    }
+    else 
+    {
+        isBinary = false;
+        isBigEndian = false;
+        write_ascii_internal(os);
+    }
+}
+
+IGL_INLINE void PlyFile::PlyFileImpl::write_binary_internal(std::ostream & os) noexcept
+{
+    isBinary = true;
+
+    write_header(os);
+
+    uint8_t listSize[4] = { 0, 0, 0, 0 };
+    size_t dummyCount = 0;
+
+    auto element_property_lookup = make_property_lookup_table();
+
+    size_t element_idx = 0;
+    for (auto & e : elements)
+    {
+        for (size_t i = 0; i < e.size; ++i)
+        {
+            size_t property_index = 0;
+            for (auto & p : e.properties)
+            {   
+                auto & f = element_property_lookup[element_idx][property_index];
+                auto * helper = f.helper;
+                if (f.skip || helper == nullptr) continue;
+
+                if (p.isList)
+                {
+                    std::memcpy(listSize, &p.listCount, sizeof(uint32_t));
+                    write_property_binary(os, listSize, dummyCount, f.list_stride);
+                    write_property_binary(os, (helper->data->buffer.get() + helper->cursor->byteOffset), helper->cursor->byteOffset, f.prop_stride * p.listCount);
+                }
+                else
+                {
+                    write_property_binary(os, (helper->data->buffer.get() + helper->cursor->byteOffset), helper->cursor->byteOffset, f.prop_stride);
+                }
+                property_index++;
+            }
+        }
+        element_idx++;
+    }
+}
+
+IGL_INLINE void PlyFile::PlyFileImpl::write_ascii_internal(std::ostream & os) noexcept
+{
+    write_header(os);
+
+    auto element_property_lookup = make_property_lookup_table();
+
+    size_t element_idx = 0;
+    for (auto & e : elements)
+    {
+        for (size_t i = 0; i < e.size; ++i)
+        {
+            size_t property_index = 0;
+            for (auto & p : e.properties)
+            {
+                auto & f = element_property_lookup[element_idx][property_index];
+                auto * helper = f.helper;
+                if (f.skip || helper == nullptr) continue;
+
+                if (p.isList)
+                {
+                    os << p.listCount << " ";
+                    for (size_t j = 0; j < p.listCount; ++j)
+                    {
+                        write_property_ascii(p.propertyType, os, (helper->data->buffer.get() + helper->cursor->byteOffset), helper->cursor->byteOffset);
+                    }
+                }
+                else
+                {
+                    write_property_ascii(p.propertyType, os, (helper->data->buffer.get() + helper->cursor->byteOffset), helper->cursor->byteOffset);
+                }
+                property_index++;
+            }
+            os << "\n";
+        }
+        element_idx++;
+    }
+}
+
+IGL_INLINE void PlyFile::PlyFileImpl::write_header(std::ostream & os) noexcept
+{
+    const std::locale & fixLoc = std::locale("C");
+    os.imbue(fixLoc);
+
+    os << "ply\n";
+    if (isBinary) os << ((isBigEndian) ? "format binary_big_endian 1.0" : "format binary_little_endian 1.0") << "\n";
+    else os << "format ascii 1.0\n";
+
+    for (const auto & comment : comments) os << "comment " << comment << "\n";
+
+    auto property_lookup = make_property_lookup_table();
+
+    size_t element_idx = 0;
+    for (auto & e : elements)
+    {
+        os << "element " << e.name << " " << e.size << "\n";
+        size_t property_idx = 0;
+        for (const auto & p : e.properties)
+        {
+            PropertyLookup & lookup = property_lookup[element_idx][property_idx];
+
+            if (!lookup.skip)
+            {
+                if (p.isList)
+                {
+                    os << "property list " << PropertyTable[p.listType].str << " "
+                       << PropertyTable[p.propertyType].str << " " << p.name << "\n";
+                }
+                else
+                {
+                    os << "property " << PropertyTable[p.propertyType].str << " " << p.name << "\n";
+                }
+            }
+            property_idx++;
+        }
+        element_idx++;
+    }
+    os << "end_header\n";
+}
+
+IGL_INLINE std::shared_ptr<PlyData> PlyFile::PlyFileImpl::request_properties_from_element(const std::string & elementKey,
+    const std::vector<std::string> propertyKeys,
+    const uint32_t list_size_hint)
+{
+    if (elements.empty()) throw std::runtime_error("header had no elements defined. malformed file?");
+    if (elementKey.empty()) throw std::invalid_argument("`elementKey` argument is empty");
+    if (propertyKeys.empty()) throw std::invalid_argument("`propertyKeys` argument is empty");
+
+    std::shared_ptr<PlyData> out_data = std::make_shared<PlyData>();
+
+    const int64_t elementIndex = find_element(elementKey, elements);
+
+    std::vector<std::string> keys_not_found;
+
+    // Sanity check if the user requested element is in the pre-parsed header
+    if (elementIndex >= 0)
+    {
+        // We found the element
+        const PlyElement & element = elements[elementIndex];
+
+        // Each key in `propertyKey` gets an entry into the userData map (keyed by a hash of
+        // element name and property name), but groups of properties (requested from the
+        // public api through this function) all share the same `ParsingHelper`. When it comes 
+        // time to .read(), we check the number of unique PlyData shared pointers
+        // and allocate a single buffer that will be used by each property key group. 
+        // That way, properties like, {"x", "y", "z"} will all be put into the same buffer.
+
+        ParsingHelper helper;
+        helper.data = out_data;
+        helper.data->count = element.size; // how many items are in the element? 
+        helper.data->isList = false;
+        helper.data->t = Type::INVALID;
+        helper.cursor = std::make_shared<PlyDataCursor>();
+        helper.list_size_hint = list_size_hint;
+
+        // Find each of the keys
+        for (const auto & key : propertyKeys)
+        {
+            const int64_t propertyIndex = find_property(key, element.properties);
+            if (propertyIndex < 0) keys_not_found.push_back(key);
+        }
+
+        if (keys_not_found.size())
+        {
+            std::stringstream ss;
+            for (auto & str : keys_not_found) ss << str << ", ";
+            throw std::invalid_argument("the following property keys were not found in the header: " + ss.str());
+        }
+
+        for (const auto & key : propertyKeys)
+        {
+            const int64_t propertyIndex = find_property(key, element.properties);
+            const PlyProperty & property = element.properties[propertyIndex];
+            helper.data->t = property.propertyType;
+            helper.data->isList = property.isList;
+            auto result = userData.insert(std::pair<uint32_t, ParsingHelper>(hash_fnv1a(element.name + property.name), helper));
+            if (result.second == false)
+            {
+                throw std::invalid_argument("element-property key has already been requested: " + element.name + " " + property.name);
+            }
+        }
+
+        // Sanity check that all properties share the same type
+        std::vector<Type> propertyTypes;
+        for (const auto & key : propertyKeys)
+        {
+            const int64_t propertyIndex = find_property(key, element.properties);
+            const PlyProperty & property = element.properties[propertyIndex];
+            propertyTypes.push_back(property.propertyType);
+        }
+
+        if (std::adjacent_find(propertyTypes.begin(), propertyTypes.end(), std::not_equal_to<Type>()) != propertyTypes.end())
+        {
+            throw std::invalid_argument("all requested properties must share the same type.");
+        }
+    }
+    else throw std::invalid_argument("the element key was not found in the header: " + elementKey);
+
+    return out_data;
+}
+
+IGL_INLINE void PlyFile::PlyFileImpl::add_properties_to_element(const std::string & elementKey, 
+    const std::vector<std::string> propertyKeys, 
+    const Type type, const size_t count, uint8_t * data, const Type listType, const size_t listCount)
+{
+    ParsingHelper helper;
+    helper.data = std::make_shared<PlyData>();
+    helper.data->count = count;
+    helper.data->t = type;
+    helper.data->buffer = Buffer(data); // we should also set size for safety reasons
+    helper.cursor = std::make_shared<PlyDataCursor>();
+
+    auto create_property_on_element = [&](PlyElement & e)
+    {
+        for (auto key : propertyKeys)
+        {
+            PlyProperty newProp = (listType == Type::INVALID) ? PlyProperty(type, key) : PlyProperty(listType, type, key, listCount);
+            userData.insert(std::pair<uint32_t, ParsingHelper>(hash_fnv1a(elementKey + key), helper));
+            e.properties.push_back(newProp);
+        }
+    };
+
+    const int64_t idx = find_element(elementKey, elements);
+    if (idx >= 0)
+    {
+        PlyElement & e = elements[idx];
+        create_property_on_element(e);
+    }
+    else
+    {
+        PlyElement newElement = (listType == Type::INVALID) ? PlyElement(elementKey, count) : PlyElement(elementKey, count);
+        create_property_on_element(newElement);
+        elements.push_back(newElement);
+    }
+}
+
+IGL_INLINE void PlyFile::PlyFileImpl::parse_data(std::istream & is, bool firstPass)
+{
+    std::function<void(PropertyLookup & f, const PlyProperty & p, uint8_t * dest, size_t & destOffset, std::istream & is)> read;
+    std::function<size_t(PropertyLookup & f, const PlyProperty & p, std::istream & is)> skip;
+
+    const auto start = is.tellg();
+
+    uint32_t listSize = 0;
+    size_t dummyCount = 0;
+    std::string skip_ascii_buffer;
+
+    // Special case mirroring read_property_binary but for list types; this
+    // has an additional big endian check to flip the data in place immediately
+    // after reading. We do this as a performance optimization; endian flipping is
+    // done on regular properties as a post-process after reading (also for optimization)
+    // but we need the correct little-endian list count as we read the file. 
+    auto read_list_binary = [this](const Type & t, void * dst, size_t & destOffset, const size_t & stride, std::istream & _is) noexcept
+    {
+        destOffset += stride;
+        _is.read((char*)dst, stride);
+
+        if (isBigEndian)
+        {
+            switch (t)
+            {
+            case Type::INT16:  *(int16_t*)dst  = endian_swap<int16_t, int16_t>(*(int16_t*)dst);    break;
+            case Type::UINT16: *(uint16_t*)dst = endian_swap<uint16_t, uint16_t>(*(uint16_t*)dst); break;
+            case Type::INT32:  *(int32_t*)dst  = endian_swap<int32_t, int32_t>(*(int32_t*)dst);    break;
+            case Type::UINT32: *(uint32_t*)dst = endian_swap<uint32_t, uint32_t>(*(uint32_t*)dst); break;
+            default: break;
+            }
+        }
+
+        return stride;
+    };
+
+    if (isBinary)
+    {
+        read = [this, &listSize, &dummyCount, &read_list_binary](PropertyLookup & f, const PlyProperty & p, uint8_t * dest, size_t & destOffset, std::istream & _is) noexcept
+        {
+            if (!p.isList)
+            {
+                return read_property_binary(f.prop_stride, dest + destOffset, destOffset, _is);
+            }
+            read_list_binary(p.listType, &listSize, dummyCount, f.list_stride, _is); // the list size
+            return read_property_binary(f.prop_stride * listSize, dest + destOffset, destOffset, _is); // properties in list
+        };
+        skip = [this, &listSize, &dummyCount, &read_list_binary](PropertyLookup & f, const PlyProperty & p, std::istream & _is) noexcept
+        {
+            if (!p.isList)
+            {
+                _is.read((char*)scratch, f.prop_stride);
+                return f.prop_stride;
+            }
+            read_list_binary(p.listType, &listSize, dummyCount, f.list_stride, _is); // the list size (does not count for memory alloc)
+            auto bytes_to_skip = f.prop_stride * listSize;
+            _is.ignore(bytes_to_skip);
+            return bytes_to_skip;
+        };
+    }
+    else
+    {
+        read = [this, &listSize, &dummyCount](PropertyLookup & f, const PlyProperty & p, uint8_t * dest, size_t & destOffset, std::istream & _is) noexcept
+        { 
+            if (!p.isList)
+            {
+                read_property_ascii(p.propertyType, f.prop_stride, dest + destOffset, destOffset, _is); 
+            }
+            else
+            {
+                read_property_ascii(p.listType, f.list_stride, &listSize, dummyCount, _is); // the list size
+                for (size_t i = 0; i < listSize; ++i) 
+                {
+                    read_property_ascii(p.propertyType, f.prop_stride, dest + destOffset, destOffset, _is);
+                }
+            }
+        };
+        skip = [this, &listSize, &dummyCount, &skip_ascii_buffer](PropertyLookup & f, const PlyProperty & p, std::istream & _is) noexcept
+        { 
+            skip_ascii_buffer.clear();
+            if (p.isList)
+            {
+                read_property_ascii(p.listType, f.list_stride, &listSize, dummyCount, _is); // the list size (does not count for memory alloc)
+                for (size_t i = 0; i < listSize; ++i) _is >> skip_ascii_buffer; // properties in list
+                return listSize * f.prop_stride;
+            }
+            _is >> skip_ascii_buffer;
+            return f.prop_stride;
+        };
+    }
+
+    std::vector<std::vector<PropertyLookup>> element_property_lookup = make_property_lookup_table();
+    size_t element_idx = 0;
+    size_t property_idx = 0;
+    ParsingHelper * helper {nullptr};
+
+    // This is the inner import loop
+    for (auto & element : elements)
+    {
+        for (size_t count = 0; count < element.size; ++count)
+        {
+            property_idx = 0;
+            for (auto & property : element.properties)
+            {
+                PropertyLookup & lookup = element_property_lookup[element_idx][property_idx];
+
+                if (!lookup.skip)
+                {
+                    helper = lookup.helper;
+                    if (firstPass) 
+                    {
+                        helper->cursor->totalSizeBytes += skip(lookup, property, is);
+
+                        // These lines will be changed when tinyply supports 
+                        // variable length lists. We add it here so our header data structure
+                        // contains enough info to write it back out again (e.g. transcoding). 
+                        if (property.listCount == 0) property.listCount = listSize; 
+                        if (property.listCount != listSize) throw std::runtime_error("variable length lists are not supported yet.");
+                    }
+                    else 
+                    {
+                        read(lookup, property, helper->data->buffer.get(), helper->cursor->byteOffset, is);
+                    }
+                }
+                else 
+                {
+                    skip(lookup, property, is);
+                }
+                property_idx++;
+            }
+        }
+        element_idx++;
+    }
+
+    // Reset istream position to the start of the data
+    if (firstPass) is.seekg(start, is.beg);
+}
+
+// Wrap the public interface:
+
+IGL_INLINE PlyFile::PlyFile() { impl.reset(new PlyFileImpl()); }
+IGL_INLINE PlyFile::~PlyFile() { }
+IGL_INLINE bool PlyFile::parse_header(std::istream & is) { return impl->parse_header(is); }
+IGL_INLINE void PlyFile::read(std::istream & is) { return impl->read(is); }
+IGL_INLINE void PlyFile::write(std::ostream & os, bool isBinary) { return impl->write(os, isBinary); }
+IGL_INLINE std::vector<PlyElement> PlyFile::get_elements() const { return impl->elements; }
+IGL_INLINE std::vector<std::string> & PlyFile::get_comments() { return impl->comments; }
+IGL_INLINE std::vector<std::string> PlyFile::get_info() const { return impl->objInfo; }
+IGL_INLINE bool PlyFile::is_binary_file() const { return impl->isBinary; }
+IGL_INLINE std::shared_ptr<PlyData> PlyFile::request_properties_from_element(const std::string & elementKey,
+    const std::vector<std::string> propertyKeys,
+    const uint32_t list_size_hint)
+{
+    return impl->request_properties_from_element(elementKey, propertyKeys, list_size_hint);
+}
+IGL_INLINE void PlyFile::add_properties_to_element(const std::string & elementKey,
+    const std::vector<std::string> propertyKeys,
+    const Type type, const size_t count, uint8_t * data, const Type listType, const size_t listCount)
+{
+    return impl->add_properties_to_element(elementKey, propertyKeys, type, count, data, listType, listCount);
+}
+
+} // tinyply

+ 184 - 0
include/igl/tinyply.h

@@ -0,0 +1,184 @@
+/*
+ * tinyply 2.3.2 (https://github.com/ddiakopoulos/tinyply)
+ *
+ * A single-header, zero-dependency (except the C++ STL) public domain implementation 
+ * of the PLY mesh file format. Requires C++11; errors are handled through exceptions.
+ *
+ * This software is in the public domain. Where that dedication is not
+ * recognized, you are granted a perpetual, irrevocable license to copy,
+ * distribute, and modify this file as you see fit.
+ *
+ * Authored by Dimitri Diakopoulos (http://www.dimitridiakopoulos.com)
+ *
+ * tinyply.h may be included in many files, however in a single compiled file,
+ * the implementation must be created with the following defined prior to header inclusion
+ * #define TINYPLY_IMPLEMENTATION
+ * 
+ */
+
+////////////////////////
+//   tinyply header   //
+////////////////////////
+
+#ifndef tinyply_h
+#define tinyply_h
+#include <igl/igl_inline.h>
+
+#include <vector>
+#include <string>
+#include <stdint.h>
+#include <cstddef>
+#include <sstream>
+#include <memory>
+#include <unordered_map>
+#include <map>
+#include <algorithm>
+#include <functional>
+
+namespace tinyply
+{
+
+    enum class Type : uint8_t
+    {
+        INVALID,
+        INT8,
+        UINT8,
+        INT16,
+        UINT16,
+        INT32,
+        UINT32,
+        FLOAT32,
+        FLOAT64
+    };
+
+    struct PropertyInfo
+    {
+        PropertyInfo() {};
+        PropertyInfo(int stride, std::string str)
+            : stride(stride), str(str) {}
+        int stride {0};
+        std::string str;
+    };
+
+    static std::map<Type, PropertyInfo> PropertyTable
+    {
+        { Type::INT8,    PropertyInfo(1, std::string("char")) },
+        { Type::UINT8,   PropertyInfo(1, std::string("uchar")) },
+        { Type::INT16,   PropertyInfo(2, std::string("short")) },
+        { Type::UINT16,  PropertyInfo(2, std::string("ushort")) },
+        { Type::INT32,   PropertyInfo(4, std::string("int")) },
+        { Type::UINT32,  PropertyInfo(4, std::string("uint")) },
+        { Type::FLOAT32, PropertyInfo(4, std::string("float")) },
+        { Type::FLOAT64, PropertyInfo(8, std::string("double")) },
+        { Type::INVALID, PropertyInfo(0, std::string("INVALID"))}
+    };
+
+    class Buffer
+    {
+        uint8_t * alias{ nullptr };
+        struct delete_array { void operator()(uint8_t * p) { delete[] p; } };
+        std::unique_ptr<uint8_t, decltype(Buffer::delete_array())> data;
+        size_t size {0};
+    public:
+        Buffer() {};
+        Buffer(const size_t size) : data(new uint8_t[size], delete_array()), size(size) { alias = data.get(); } // allocating
+        Buffer(uint8_t * ptr): alias(ptr) { } // non-allocating, todo: set size?
+        uint8_t * get() { return alias; }
+        size_t size_bytes() const { return size; }
+    };
+
+    struct PlyData
+    {
+        Type t;
+        Buffer buffer;
+        size_t count {0};
+        bool isList {false};
+    };
+
+    struct PlyProperty
+    {
+        PlyProperty(std::istream & is);
+        PlyProperty(Type type, std::string & _name) : name(_name), propertyType(type) {}
+        PlyProperty(Type list_type, Type prop_type, std::string & _name, size_t list_count) 
+            : name(_name), propertyType(prop_type), isList(true), listType(list_type), listCount(list_count) {}
+        std::string name;
+        Type propertyType{ Type::INVALID };
+        bool isList{ false };
+        Type listType{ Type::INVALID };
+        size_t listCount {0};
+    };
+
+    struct PlyElement
+    {
+        PlyElement(std::istream & istream);
+        PlyElement(const std::string & _name, size_t count) : name(_name), size(count) {}
+        std::string name;
+        size_t size {0};
+        std::vector<PlyProperty> properties;
+    };
+
+    struct PlyFile
+    {
+        struct PlyFileImpl;
+        std::unique_ptr<PlyFileImpl> impl;
+
+        PlyFile();
+        ~PlyFile();
+
+        /*
+         * The ply format requires an ascii header. This can be used to determine at 
+         * runtime which properties or elements exist in the file. Limited validation of the 
+         * header is performed; it is assumed the header correctly reflects the contents of the 
+         * payload. This function may throw. Returns true on success, false on failure. 
+         */ 
+        bool parse_header(std::istream & is);
+
+        /* 
+         * Execute a read operation. Data must be requested via `request_properties_from_element(...)` 
+         * prior to calling this function.
+         */
+        void read(std::istream & is);
+
+        /* 
+         * `write` performs no validation and assumes that the data passed into 
+         * `add_properties_to_element` is well-formed. 
+         */
+        void write(std::ostream & os, bool isBinary);
+
+        /* 
+         * These functions are valid after a call to `parse_header(...)`. In the case of
+         * writing, get_comments() reference may also be used to add new comments to the ply header.
+         */
+        std::vector<PlyElement> get_elements() const;
+        std::vector<std::string> get_info() const;
+        std::vector<std::string> & get_comments();
+        bool is_binary_file() const;
+
+        /*
+         * In the general case where |list_size_hint| is zero, `read` performs a two-pass
+         * parse to support variable length lists. The most general use of the
+         * ply format is storing triangle meshes. When this fact is known a-priori, we can pass
+         * an expected list length that will apply to this element. Doing so results in an up-front
+         * memory allocation and a single-pass import, a 2x performance optimization.
+         */
+        std::shared_ptr<PlyData> request_properties_from_element(const std::string & elementKey, 
+            const std::vector<std::string> propertyKeys, const uint32_t list_size_hint = 0);
+
+        void add_properties_to_element(const std::string & elementKey, 
+            const std::vector<std::string> propertyKeys, 
+            const Type type, 
+            const size_t count, 
+            uint8_t * data, 
+            const Type listType, 
+            const size_t listCount);
+    };
+
+} // end namespace tinyply
+
+#ifndef IGL_STATIC_LIBRARY
+// implementation moved to tinyply.cpp
+#  include "tinyply.cpp"
+#endif
+
+
+#endif // end tinyply_h

+ 366 - 140
include/igl/writePLY.cpp

@@ -1,182 +1,408 @@
-// This file is part of libigl, a simple c++ geometry processing library.
-//
-// Copyright (C) 2014 Alec Jacobson <[email protected]>
-//
-// This Source Code Form is subject to the terms of the Mozilla Public License
-// v. 2.0. If a copy of the MPL was not distributed with this file, You can
-// obtain one at http://mozilla.org/MPL/2.0/.
 #include "writePLY.h"
 #include "writePLY.h"
-#include <vector>
+#include <fstream>
 
 
-#include <igl/ply.h>
-#include <vector>
+#include "tinyply.h"
 
 
-namespace
+
+namespace igl
 {
 {
-  template <typename Scalar> int ply_type();
-  template <> int ply_type<char>(){ return PLY_CHAR; }
-  template <> int ply_type<short>(){ return PLY_SHORT; }
-  template <> int ply_type<int>(){ return PLY_INT; }
-  template <> int ply_type<unsigned char>(){ return PLY_UCHAR; }
-  template <> int ply_type<unsigned short>(){ return PLY_SHORT; }
-  template <> int ply_type<unsigned int>(){ return PLY_UINT; }
-  template <> int ply_type<float>(){ return PLY_FLOAT; }
-  template <> int ply_type<double>(){ return PLY_DOUBLE; }
-}
+  template <typename Scalar> tinyply::Type tynyply_type();
+  template <> tinyply::Type IGL_INLINE tynyply_type<char>(){ return tinyply::Type::INT8; }
+  template <> tinyply::Type IGL_INLINE tynyply_type<short>(){ return tinyply::Type::INT16; }
+  template <> tinyply::Type IGL_INLINE tynyply_type<int>(){ return tinyply::Type::INT32; }
+  template <> tinyply::Type IGL_INLINE tynyply_type<unsigned char>(){ return tinyply::Type::UINT8; }
+  template <> tinyply::Type IGL_INLINE tynyply_type<unsigned short>(){ return tinyply::Type::UINT16; }
+  template <> tinyply::Type IGL_INLINE tynyply_type<unsigned int>(){ return tinyply::Type::UINT32; }
+  template <> tinyply::Type IGL_INLINE tynyply_type<float>(){ return tinyply::Type::FLOAT32; }
+  template <> tinyply::Type IGL_INLINE tynyply_type<double>(){ return tinyply::Type::FLOAT64; }
+
 
 
 template <
 template <
   typename DerivedV,
   typename DerivedV,
   typename DerivedF,
   typename DerivedF,
+  typename DerivedE,
   typename DerivedN,
   typename DerivedN,
-  typename DerivedUV>
-IGL_INLINE bool igl::writePLY(
-  const std::string & filename,
+  typename DerivedUV,
+  typename DerivedVD,
+  typename DerivedFD,
+  typename DerivedED
+>
+bool writePLY(
+  std::ostream & ply_stream,
   const Eigen::MatrixBase<DerivedV> & V,
   const Eigen::MatrixBase<DerivedV> & V,
   const Eigen::MatrixBase<DerivedF> & F,
   const Eigen::MatrixBase<DerivedF> & F,
+  const Eigen::MatrixBase<DerivedE> & E,
   const Eigen::MatrixBase<DerivedN> & N,
   const Eigen::MatrixBase<DerivedN> & N,
   const Eigen::MatrixBase<DerivedUV> & UV,
   const Eigen::MatrixBase<DerivedUV> & UV,
-  const bool ascii)
+
+  const Eigen::MatrixBase<DerivedVD> & VD,
+  const std::vector<std::string> & VDheader,
+
+  const Eigen::MatrixBase<DerivedFD> & FD,
+  const std::vector<std::string> & FDheader,
+
+  const Eigen::MatrixBase<DerivedED> & ED,
+  const std::vector<std::string> & EDheader,
+
+  const std::vector<std::string> & comments,
+  bool isBinary
+   )
 {
 {
-  // Largely based on obj2ply.c
-  typedef typename DerivedV::Scalar VScalar;
-  typedef typename DerivedN::Scalar NScalar;
-  typedef typename DerivedUV::Scalar UVScalar;
-  typedef typename DerivedF::Scalar FScalar;
+    typedef typename DerivedV::Scalar VScalar;
+    typedef typename DerivedN::Scalar NScalar;
+    typedef typename DerivedUV::Scalar UVScalar;
+    typedef typename DerivedF::Scalar FScalar;
+    typedef typename DerivedE::Scalar EScalar;
 
 
-  typedef struct Vertex
-  {
-    VScalar x,y,z,w;          /* position */
-    NScalar nx,ny,nz;         /* surface normal */
-    UVScalar s,t;              /* texture coordinates */
-  } Vertex;
+    typedef typename DerivedVD::Scalar VDScalar;
+    typedef typename DerivedFD::Scalar FDScalar;
+    typedef typename DerivedED::Scalar EDScalar;
+    
+    // temporary storage for data to be passed to tinyply internals
+    std::vector<NScalar> _v;
+    std::vector<NScalar> _n;
+    std::vector<UVScalar> _uv;
+    std::vector<VDScalar> _vd;
+    std::vector<FDScalar> _fd;
+    std::vector<EScalar> _ev;
+    std::vector<EDScalar> _ed;
 
 
-  typedef struct Face
-  {
-    unsigned char nverts;    /* number of vertex indices in list */
-    FScalar *verts;              /* vertex index list */
-  } Face;
-
-  igl::ply::PlyProperty vert_props[] =
-  { /* list of property information for a vertex */
-    {"x", ply_type<VScalar>(), ply_type<VScalar>(),offsetof(Vertex,x),0,0,0,0},
-    {"y", ply_type<VScalar>(), ply_type<VScalar>(),offsetof(Vertex,y),0,0,0,0},
-    {"z", ply_type<VScalar>(), ply_type<VScalar>(),offsetof(Vertex,z),0,0,0,0},
-    {"nx",ply_type<NScalar>(), ply_type<NScalar>(),offsetof(Vertex,nx),0,0,0,0},
-    {"ny",ply_type<NScalar>(), ply_type<NScalar>(),offsetof(Vertex,ny),0,0,0,0},
-    {"nz",ply_type<NScalar>(), ply_type<NScalar>(),offsetof(Vertex,nz),0,0,0,0},
-    {"s", ply_type<UVScalar>(),ply_type<UVScalar>(),offsetof(Vertex,s),0,0,0,0},
-    {"t", ply_type<UVScalar>(),ply_type<UVScalar>(),offsetof(Vertex,t),0,0,0,0},
-  };
-
-  igl::ply::PlyProperty face_props[] =
-  { /* list of property information for a face */
-    {"vertex_indices", ply_type<FScalar>(), ply_type<FScalar>(),
-      offsetof(Face,verts), 1, PLY_UCHAR, PLY_UCHAR, offsetof(Face,nverts)},
-  };
-  const bool has_normals = N.rows() > 0;
-  const bool has_texture_coords = UV.rows() > 0;
-  std::vector<Vertex> vlist(V.rows());
-  std::vector<Face> flist(F.rows());
-  for(size_t i = 0;i<(size_t)V.rows();i++)
-  {
-    vlist[i].x = V(i,0);
-    vlist[i].y = V(i,1);
-    vlist[i].z = V(i,2);
-    if(has_normals)
+    // check dimensions
+    if( V.cols()!=3)
     {
     {
-      vlist[i].nx = N(i,0);
-      vlist[i].ny = N(i,1);
-      vlist[i].nz = N(i,2);
+      std::cerr << "writePLY: unexpected dimensions " << std::endl;
+      return false;
     }
     }
-    if(has_texture_coords)
+    tinyply::PlyFile file;
+
+    _v.resize(V.size());
+    Eigen::Map< Eigen::Matrix<VScalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > >( &_v[0], V.rows(), V.cols() ) = V;
+
+    file.add_properties_to_element("vertex", { "x", "y", "z" }, 
+        tynyply_type<VScalar>(), V.rows(), reinterpret_cast<uint8_t*>( &_v[0] ), tinyply::Type::INVALID, 0);
+
+    if(N.rows()>0) 
     {
     {
-      vlist[i].s = UV(i,0);
-      vlist[i].t = UV(i,1);
+        _n.resize(N.size());
+        Eigen::Map<Eigen::Matrix<NScalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > >( &_n[0], N.rows(), N.cols() ) = N;
+        file.add_properties_to_element("vertex", { "nx", "ny", "nz" },
+            tynyply_type<NScalar>(), N.rows(), reinterpret_cast<uint8_t*>( &_n[0] ),tinyply::Type::INVALID, 0);
     }
     }
-  }
-  for(size_t i = 0;i<(size_t)F.rows();i++)
-  {
-    flist[i].nverts = F.cols();
-    flist[i].verts = new FScalar[F.cols()];
-    for(size_t c = 0;c<(size_t)F.cols();c++)
+
+    if(UV.rows()>0) 
     {
     {
-      flist[i].verts[c] = F(i,c);
+        _uv.resize(UV.size());
+        Eigen::Map<Eigen::Matrix<UVScalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > >( &_uv[0], UV.rows(), UV.cols() ) = UV;
+
+        file.add_properties_to_element("vertex", { "u", "v" },
+            tynyply_type<UVScalar>(), UV.rows() , reinterpret_cast<uint8_t*>( &_uv[0] ), tinyply::Type::INVALID, 0);
     }
     }
-  }
 
 
-  const char * elem_names[] = {"vertex","face"};
-  FILE * fp = fopen(filename.c_str(),"w");
-  if(fp==NULL)
-  {
-    return false;
-  }
-  igl::ply::PlyFile * ply = igl::ply::ply_write(fp, 2,elem_names,
-      (ascii ? PLY_ASCII : PLY_BINARY_LE));
-  if(ply==NULL)
-  {
-    return false;
-  }
+    if(VD.cols()>0)
+    {
+        assert(VD.cols() == VDheader.size());
+        assert(VD.rows() == V.rows());
 
 
-  std::vector<igl::ply::PlyProperty> plist;
-  plist.push_back(vert_props[0]);
-  plist.push_back(vert_props[1]);
-  plist.push_back(vert_props[2]);
-  if (has_normals)
-  {
-    plist.push_back(vert_props[3]);
-    plist.push_back(vert_props[4]);
-    plist.push_back(vert_props[5]);
-  }
-  if (has_texture_coords)
-  {
-    plist.push_back(vert_props[6]);
-    plist.push_back(vert_props[7]);
-  }
-  ply_describe_element(ply, "vertex", V.rows(),plist.size(),
-    &plist[0]);
-
-  ply_describe_element(ply, "face", F.rows(),1,&face_props[0]);
-  ply_header_complete(ply);
-  int native_binary_type = igl::ply::get_native_binary_type2();
-  ply_put_element_setup(ply, "vertex");
-  for(const auto v : vlist)
+        _vd.resize(VD.size());
+        Eigen::Map< Eigen::Matrix<VDScalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > >( &_vd[0], VD.rows(), VD.cols() ) = VD;
+
+        file.add_properties_to_element("vertex", VDheader,
+            tynyply_type<VDScalar>(), VD.rows(), reinterpret_cast<uint8_t*>( &_vd[0] ), tinyply::Type::INVALID, 0);
+    }
+
+
+
+    std::vector<FScalar> _f(F.size());
+    Eigen::Map<Eigen::Matrix<FScalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > >( &_f[0], F.rows(), F.cols() ) = F;
+    file.add_properties_to_element("face", { "vertex_indices" },
+        tynyply_type<FScalar>(), F.rows(), reinterpret_cast<uint8_t*>(&_f[0]), tinyply::Type::UINT8, F.cols() );
+
+    if(FD.cols()>0)
+    {
+        assert(FD.rows()==F.rows());
+        assert(FD.cols() == FDheader.size());
+
+        _fd.resize(FD.size());
+        Eigen::Map<Eigen::Matrix<FDScalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > >( &_fd[0], FD.rows(), FD.cols() ) = FD;
+
+        file.add_properties_to_element("face", FDheader,
+            tynyply_type<FDScalar>(), FD.rows(), reinterpret_cast<uint8_t*>( &_fd[0] ), tinyply::Type::INVALID, 0);
+    }
+
+    if(E.rows()>0) 
+    {
+        assert(E.cols()==2);
+        _ev.resize(E.size());
+        Eigen::Map<Eigen::Matrix<EScalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > >( &_ev[0], E.rows(), E.cols() ) = E;
+
+        file.add_properties_to_element("edge", { "vertex1", "vertex2" },
+            tynyply_type<EScalar>(), E.rows() , reinterpret_cast<uint8_t*>( &_ev[0] ), tinyply::Type::INVALID, 0);
+    }
+
+    if(ED.cols()>0)
+    {
+        assert(ED.rows()==F.rows());
+        assert(ED.cols() == EDheader.size());
+
+        _ed.resize(ED.size());
+        Eigen::Map<Eigen::Matrix<EDScalar, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > >( &_ed[0], ED.rows(), ED.cols() ) = ED;
+
+        file.add_properties_to_element("face", FDheader,
+            tynyply_type<EDScalar>(), ED.rows(), reinterpret_cast<uint8_t*>( &_ed[0] ), tinyply::Type::INVALID, 0);
+    }
+
+    for(auto a:comments)
+      file.get_comments().push_back(a);
+
+    // Write a binary file
+    file.write(ply_stream, isBinary);
+
+    return true;
+}
+
+template <
+  typename DerivedV,
+  typename DerivedF,
+  typename DerivedE,
+  typename DerivedN,
+  typename DerivedUV,
+  typename DerivedVD,
+  typename DerivedFD,
+  typename DerivedED
+>
+bool writePLY(
+  const std::string & filename,
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedF> & F,
+  const Eigen::MatrixBase<DerivedE> & E,
+  const Eigen::MatrixBase<DerivedN> & N,
+  const Eigen::MatrixBase<DerivedUV> & UV,
+
+  const Eigen::MatrixBase<DerivedVD> & VD,
+  const std::vector<std::string> & VDheader,
+
+  const Eigen::MatrixBase<DerivedFD> & FD,
+  const std::vector<std::string> & FDheader,
+
+  const Eigen::MatrixBase<DerivedED> & ED,
+  const std::vector<std::string> & EDheader,
+
+  const std::vector<std::string> & comments,
+  bool isBinary
+   )
+{
+  try
   {
   {
-    ply_put_element(ply, (void *) &v, &native_binary_type);
+    if(isBinary)
+    {
+      std::filebuf fb_binary;
+      fb_binary.open(filename , std::ios::out | std::ios::binary);
+      std::ostream outstream_binary(&fb_binary);
+      if (outstream_binary.fail()) {
+        std::cerr << "writePLY: Error opening file " << filename << std::endl;
+        return false; //throw std::runtime_error("failed to open " + filename);
+      }
+      return writePLY(outstream_binary,V,F,E,N,UV,VD,VDheader,FD,FDheader,ED,EDheader,comments,isBinary);
+    } else {
+      std::filebuf fb_ascii;
+      fb_ascii.open(filename, std::ios::out);
+      std::ostream outstream_ascii(&fb_ascii);
+      if (outstream_ascii.fail()) {
+        std::cerr << "writePLY: Error opening file " << filename << std::endl;
+        return false; //throw std::runtime_error("failed to open " + filename);
+      }
+      return writePLY(outstream_ascii,V,F,E,N,UV,VD,VDheader,FD,FDheader,ED,EDheader,comments,isBinary);
+    }
   }
   }
-  ply_put_element_setup(ply, "face");
-  for(const auto f : flist)
+  catch(const std::exception& e)
   {
   {
-    ply_put_element(ply, (void *) &f, &native_binary_type);
+    std::cerr << "writePLY error: " << filename << e.what() << std::endl;
   }
   }
+  return false;
+}
 
 
-  ply_close(ply);
-  for(size_t i = 0;i<(size_t)F.rows();i++)
-  {
-    delete[] flist[i].verts;
-  }
-  return true;
+template <
+  typename DerivedV,
+  typename DerivedF
+>
+bool writePLY(
+  const std::string & filename,
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedF> & F
+   )
+{
+  Eigen::MatrixXd _dummy;
+  std::vector<std::string> _dummy_header;
+  
+  return writePLY(filename,V,F,_dummy, _dummy, _dummy, _dummy, _dummy_header, _dummy, _dummy_header, _dummy, _dummy_header, _dummy_header, true);
+}
+
+template <
+  typename DerivedV,
+  typename DerivedF,
+  typename DerivedE
+>
+bool writePLY(
+  const std::string & filename,
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedF> & F,
+  const Eigen::MatrixBase<DerivedE> & E
+   )
+{
+  Eigen::MatrixXd _dummy;
+  std::vector<std::string> _dummy_header;
+  
+  return writePLY(filename,V,F,E, _dummy, _dummy, _dummy, _dummy_header, _dummy, _dummy_header, _dummy, _dummy_header, _dummy_header, true);
 }
 }
 
 
+
 template <
 template <
   typename DerivedV,
   typename DerivedV,
-  typename DerivedF>
-IGL_INLINE bool igl::writePLY(
+  typename DerivedF,
+  typename DerivedN,
+  typename DerivedUV
+>
+bool writePLY(
   const std::string & filename,
   const std::string & filename,
   const Eigen::MatrixBase<DerivedV> & V,
   const Eigen::MatrixBase<DerivedV> & V,
   const Eigen::MatrixBase<DerivedF> & F,
   const Eigen::MatrixBase<DerivedF> & F,
-  const bool ascii)
+  const Eigen::MatrixBase<DerivedN> & N,
+  const Eigen::MatrixBase<DerivedUV> & UV
+   )
 {
 {
-  Eigen::Matrix<typename DerivedV::Scalar,Eigen::Dynamic,Eigen::Dynamic> N,UV;
-  return writePLY(filename,V,F,N,UV,ascii);
+  Eigen::MatrixXd _dummy;
+  std::vector<std::string> _dummy_header;
+  
+  return writePLY(filename,V,F,_dummy, N,UV, _dummy, _dummy_header, _dummy, _dummy_header, _dummy, _dummy_header, _dummy_header, true);
 }
 }
 
 
+template <
+  typename DerivedV,
+  typename DerivedF,
+  typename DerivedE,
+  typename DerivedN,
+  typename DerivedUV
+>
+bool writePLY(
+  const std::string & filename,
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedF> & F,
+  const Eigen::MatrixBase<DerivedE> & E,
+  const Eigen::MatrixBase<DerivedN> & N,
+  const Eigen::MatrixBase<DerivedUV> & UV
+   )
+{
+  Eigen::MatrixXd _dummy;
+  std::vector<std::string> _dummy_header;
+  
+  return writePLY(filename,V,F,E, N,UV, _dummy, _dummy_header, _dummy, _dummy_header, _dummy, _dummy_header, _dummy_header, true);
+}
+
+template <
+  typename DerivedV,
+  typename DerivedF
+>
+bool writePLY(
+  const std::string & filename,
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedF> & F,
+  bool force_ascii
+   )
+{
+  Eigen::MatrixXd _dummy(0,0);
+  std::vector<std::string> _dummy_header;
+  
+  return writePLY(filename,V,F,_dummy, _dummy,_dummy, _dummy, _dummy_header, 
+                         _dummy, _dummy_header, _dummy, _dummy_header, _dummy_header, force_ascii);
+}
+
+template <
+  typename DerivedV,
+  typename DerivedF,
+  typename DerivedE
+>
+bool writePLY(
+  const std::string & filename,
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedF> & F,
+  const Eigen::MatrixBase<DerivedE> & E,
+  bool force_ascii
+   )
+{
+  Eigen::MatrixXd _dummy(0,0);
+  std::vector<std::string> _dummy_header;
+  
+  return writePLY(filename,V,F,E, _dummy,_dummy, _dummy, _dummy_header,
+                         _dummy, _dummy_header, _dummy, _dummy_header, _dummy_header, force_ascii);
+}
+
+
+
+
+template <
+  typename DerivedV,
+  typename DerivedF,
+  typename DerivedN,
+  typename DerivedUV,
+  typename DerivedVD
+>
+bool writePLY(
+  const std::string & filename,
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedF> & F,
+  const Eigen::MatrixBase<DerivedN> & N,
+  const Eigen::MatrixBase<DerivedUV> & UV,
+  const Eigen::MatrixBase<DerivedVD> & VD,
+  const std::vector<std::string> & VDheader,
+  const std::vector<std::string> & comments
+   )
+{
+  Eigen::MatrixXd _dummy(0,0);
+  std::vector<std::string> _dummy_header;
+  
+  return writePLY(filename,V,F,_dummy, N, UV, VD, VDheader, 
+                         _dummy, _dummy_header, _dummy, _dummy_header, comments, true);
+}
+
+
+
+
+template <
+  typename DerivedV,
+  typename DerivedF,
+  typename DerivedE,
+  typename DerivedN,
+  typename DerivedUV,
+  typename DerivedVD
+>
+bool writePLY(
+  const std::string & filename,
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedF> & F,
+  const Eigen::MatrixBase<DerivedE> & E,
+  const Eigen::MatrixBase<DerivedN> & N,
+  const Eigen::MatrixBase<DerivedUV> & UV,
+  const Eigen::MatrixBase<DerivedVD> & VD,
+  const std::vector<std::string> & VDheader,
+  const std::vector<std::string> & comments
+  )
+{
+  Eigen::MatrixXd _dummy(0,0);
+  std::vector<std::string> _dummy_header;
+  
+  return writePLY(filename,V,F,E, N, UV, VD, VDheader, 
+                         _dummy, _dummy_header, _dummy, _dummy_header, comments, true);
+
+}
+
+
+
+}
+
+
+
 #ifdef IGL_STATIC_LIBRARY
 #ifdef IGL_STATIC_LIBRARY
 // Explicit template instantiation
 // Explicit template instantiation
-// generated by autoexplicit.sh
-template bool igl::writePLY<Eigen::Matrix<float, -1, 3, 1, -1, 3>, Eigen::Matrix<int, -1, 3, 1, -1, 3> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, Eigen::MatrixBase<Eigen::Matrix<float, -1, 3, 1, -1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 3, 1, -1, 3> > const&, bool);
-// generated by autoexplicit.sh
-template bool igl::writePLY<Eigen::Matrix<double, 8, 3, 0, 8, 3>, Eigen::Matrix<int, 12, 3, 0, 12, 3> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, Eigen::MatrixBase<Eigen::Matrix<double, 8, 3, 0, 8, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<int, 12, 3, 0, 12, 3> > const&, bool);
-template bool igl::writePLY<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::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, bool);
-template bool igl::writePLY<Eigen::Matrix<double, -1, 3, 0, -1, 3>, Eigen::Matrix<int, -1, 3, 0, -1, 3> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, 3, 0, -1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 3, 0, -1, 3> > const&, bool);
-template bool igl::writePLY<Eigen::Matrix<double, -1, 3, 1, -1, 3>, Eigen::Matrix<int, -1, 3, 1, -1, 3> >(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, 3, 1, -1, 3> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, 3, 1, -1, 3> > const&, bool);
+template bool igl::writePLY<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::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&);
+template bool igl::writePLY<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::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, 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> > > > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, 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> > > > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, 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> > > > const&, 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> > > > const&, bool);
 #endif
 #endif

+ 225 - 35
include/igl/writePLY.h

@@ -1,48 +1,238 @@
-// This file is part of libigl, a simple c++ geometry processing library.
-// 
-// Copyright (C) 2014 Alec Jacobson <[email protected]>
-// 
-// This Source Code Form is subject to the terms of the Mozilla Public License 
-// v. 2.0. If a copy of the MPL was not distributed with this file, You can 
-// obtain one at http://mozilla.org/MPL/2.0/.
 #ifndef IGL_WRITEPLY_H
 #ifndef IGL_WRITEPLY_H
 #define IGL_WRITEPLY_H
 #define IGL_WRITEPLY_H
-#include "igl_inline.h"
-#include <Eigen/Core>
+#include <igl/igl_inline.h>
+
 #include <string>
 #include <string>
+#include <iostream>
+#include <vector>
+#include <Eigen/Core>
+
 
 
 namespace igl
 namespace igl
 {
 {
-  // Write a mesh to a .ply file. 
+  // write triangular mesh to ply file
   //
   //
+  // Templates:
+  //   Derived from Eigen matrix parameters
   // Inputs:
   // Inputs:
-  //   filename  path to .ply file
-  //   V  #V by 3 list of vertex positions
-  //   F  #F by 3 list of triangle indices
-  //   N  #V by 3 list of vertex normals
-  //   UV  #V by 2 list of vertex texture coordinates
-  // Returns true iff success
+  //  ply_stream  ply file output stream
+  //   V  (#V,3) matrix of vertex positions 
+  //   F  (#F,3) list of face indices into vertex positions
+  //   E  (#E,2) list of edge indices into vertex positions
+  //   N  (#V,3) list of normals
+  //   UV (#V,2) list of texture coordinates
+  //   VD (#V,*) additional vertex data
+  //   Vheader (#V) list of vertex data headers
+  //   FD (#F,*) additional face data
+  //   Fheader (#F) list of face data headers
+  //   ED (#E,*) additional edge data
+  //   Eheader (#E) list of edge data headers
+  //   comments (*) file comments
+  //   isBinary - bool, to set binary or ascii file format
+  // Returns true on success, false on errors
   template <
   template <
-    typename DerivedV,
-    typename DerivedF,
-    typename DerivedN,
-    typename DerivedUV>
-  IGL_INLINE bool writePLY(
-    const std::string & filename,
-    const Eigen::MatrixBase<DerivedV> & V,
-    const Eigen::MatrixBase<DerivedF> & F,
-    const Eigen::MatrixBase<DerivedN> & N,
-    const Eigen::MatrixBase<DerivedUV> & UV,
-    const bool ascii = true);
-  template <
-    typename DerivedV,
-    typename DerivedF>
-  IGL_INLINE bool writePLY(
-    const std::string & filename,
-    const Eigen::MatrixBase<DerivedV> & V,
-    const Eigen::MatrixBase<DerivedF> & F,
-    const bool ascii = true);
+  typename DerivedV,
+  typename DerivedF,
+  typename DerivedE,
+  typename DerivedN,
+  typename DerivedUV,
+  typename DerivedVD,
+  typename DerivedFD,
+  typename DerivedED
+>
+bool writePLY(
+  std::ostream & ply_stream,
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedF> & F,
+  const Eigen::MatrixBase<DerivedE> & E,
+  const Eigen::MatrixBase<DerivedN> & N,
+  const Eigen::MatrixBase<DerivedUV> & UV,
+
+  const Eigen::MatrixBase<DerivedVD> & VD,
+  const std::vector<std::string> & VDheader,
+
+  const Eigen::MatrixBase<DerivedFD> & FD,
+  const std::vector<std::string> & FDheader,
+
+  const Eigen::MatrixBase<DerivedED> & ED,
+  const std::vector<std::string> & EDheader,
+
+  const std::vector<std::string> & comments,
+  bool isBinary
+   );
+
+  // write triangular mesh to ply file
+  //
+  // Templates:
+  //   Derived from Eigen matrix parameters
+  // Inputs:
+  //  filename  ply file name
+  //   V  (#V,3) matrix of vertex positions 
+  //   F  (#F,3) list of face indices into vertex positions
+  //   E  (#E,2) list of edge indices into vertex positions
+  //   N  (#V,3) list of normals
+  //   UV (#V,2) list of texture coordinates
+  //   VD (#V,*) additional vertex data
+  //   Vheader (#V) list of vertex data headers
+  //   FD (#F,*) additional face data
+  //   Fheader (#F) list of face data headers
+  //   ED (#E,*) additional edge data
+  //   Eheader (#E) list of edge data headers
+  //   comments (*) file comments
+  //   isBinary - bool, to set binary or ascii file format
+  // Returns true on success, false on errors
+template <
+  typename DerivedV,
+  typename DerivedF,
+  typename DerivedE,
+  typename DerivedN,
+  typename DerivedUV,
+  typename DerivedVD,
+  typename DerivedFD,
+  typename DerivedED
+>
+bool writePLY(
+  const std::string & filename,
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedF> & F,
+  const Eigen::MatrixBase<DerivedE> & E,
+  const Eigen::MatrixBase<DerivedN> & N,
+  const Eigen::MatrixBase<DerivedUV> & UV,
+
+  const Eigen::MatrixBase<DerivedVD> & VD,
+  const std::vector<std::string> & VDheader,
+
+  const Eigen::MatrixBase<DerivedFD> & FD,
+  const std::vector<std::string> & FDheader,
+
+  const Eigen::MatrixBase<DerivedED> & ED,
+  const std::vector<std::string> & EDheader,
+
+  const std::vector<std::string> & comments,
+  bool isBinary
+   );
+
+template <
+  typename DerivedV,
+  typename DerivedF
+>
+bool writePLY(
+  const std::string & filename,
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedF> & F
+   );
+
+template <
+  typename DerivedV,
+  typename DerivedF,
+  typename DerivedE
+>
+bool writePLY(
+  const std::string & filename,
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedF> & F,
+  const Eigen::MatrixBase<DerivedF> & E
+   );
+
+
+template <
+  typename DerivedV,
+  typename DerivedF,
+  typename DerivedN,
+  typename DerivedUV
+>
+bool writePLY(
+  const std::string & filename,
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedF> & F,
+  const Eigen::MatrixBase<DerivedN> & N,
+  const Eigen::MatrixBase<DerivedUV> & UV
+   );
+
+
+template <
+  typename DerivedV,
+  typename DerivedF,
+  typename DerivedE,
+  typename DerivedN,
+  typename DerivedUV
+>
+bool writePLY(
+  const std::string & filename,
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedF> & F,
+  const Eigen::MatrixBase<DerivedE> & E,
+  const Eigen::MatrixBase<DerivedN> & N,
+  const Eigen::MatrixBase<DerivedUV> & UV
+   );
+
+
+template <
+  typename DerivedV,
+  typename DerivedF
+>
+bool writePLY(
+  const std::string & filename,
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedF> & F,
+  bool force_ascii
+   );   
+
+template <
+  typename DerivedV,
+  typename DerivedF,
+  typename DerivedE
+>
+bool writePLY(
+  const std::string & filename,
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedF> & F,
+  const Eigen::MatrixBase<DerivedE> & E,
+  bool force_ascii
+   );
+
+template <
+  typename DerivedV,
+  typename DerivedF,
+  typename DerivedN,
+  typename DerivedUV,
+  typename DerivedVD
+>
+bool writePLY(
+  const std::string & filename,
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedF> & F,
+  const Eigen::MatrixBase<DerivedN> & N,
+  const Eigen::MatrixBase<DerivedUV> & UV,
+  const Eigen::MatrixBase<DerivedVD> & VD=Eigen::MatrixXd(0,0),
+  const std::vector<std::string> & VDheader={},
+  const std::vector<std::string> & comments={}
+   );
+
+template <
+  typename DerivedV,
+  typename DerivedF,
+  typename DerivedE,
+  typename DerivedN,
+  typename DerivedUV,
+  typename DerivedVD
+>
+bool writePLY(
+  const std::string & filename,
+  const Eigen::MatrixBase<DerivedV> & V,
+  const Eigen::MatrixBase<DerivedF> & F,
+  const Eigen::MatrixBase<DerivedE> & E,
+  const Eigen::MatrixBase<DerivedN> & N,
+  const Eigen::MatrixBase<DerivedUV> & UV,
+  const Eigen::MatrixBase<DerivedVD> & VD=Eigen::MatrixXd(0,0),
+  const std::vector<std::string> & VDheader={},
+  const std::vector<std::string> & comments={}
+   );
+
 }
 }
+
+
+
 #ifndef IGL_STATIC_LIBRARY
 #ifndef IGL_STATIC_LIBRARY
 #  include "writePLY.cpp"
 #  include "writePLY.cpp"
 #endif
 #endif

+ 140 - 0
tests/include/igl/readPLY.cpp

@@ -0,0 +1,140 @@
+#include <test_common.h>
+#include <igl/readPLY.h>
+#include <fstream>
+#include <string>
+#include <vector>
+
+TEST_CASE("readPLY: cube_with_fold.ply", "[igl]")
+{
+    Eigen::MatrixXd V;
+    Eigen::MatrixXi F;
+    REQUIRE (igl::readPLY(test_common::data_path("cube_with_fold.ply"), V, F));
+    REQUIRE (V.rows() == 26);
+    REQUIRE (V.cols() == 3);
+    REQUIRE (F.rows() == 48);
+    REQUIRE (F.cols() == 3);
+}
+
+TEST_CASE("readPLY: bunny.ply", "[igl]")
+{
+    std::ifstream f(test_common::data_path("bunny.ply"));
+    REQUIRE (f.good());
+    f.close();
+    
+    Eigen::MatrixXd V,N,UV,VD,FD,ED;
+    std::vector<std::string> Vheader,Fheader,Eheader,comments;
+
+    Eigen::MatrixXi F,E;
+
+    REQUIRE (igl::readPLY(test_common::data_path("bunny.ply"), V, F, E, N, UV, VD,Vheader, FD,Fheader, ED,Eheader,comments));
+    REQUIRE (V.rows() == 35947);
+    REQUIRE (V.cols() == 3);
+    REQUIRE (F.rows() == 69451);
+    REQUIRE (F.cols() == 3);
+    // no edge data
+    REQUIRE (E.rows() == 0);
+    REQUIRE (E.cols() == 0);
+
+    // no normals or texture coordinates
+    REQUIRE (N.rows() == 0);
+    REQUIRE (N.cols() == 0);
+    REQUIRE (UV.rows() == 0);
+    REQUIRE (UV.cols() == 0);
+
+    // this bunny have additonal data
+    REQUIRE (VD.rows() == 35947);
+    REQUIRE (VD.cols() == 2);
+
+    REQUIRE (Vheader.size() == 2);
+    REQUIRE (Vheader[0] == "confidence" );
+    REQUIRE (Vheader[1] == "intensity" );
+
+    // no Face data or edge data
+    REQUIRE (FD.rows() == 0);
+    REQUIRE (FD.cols() == 0);
+    REQUIRE (Fheader.size() == 0);
+
+    REQUIRE (ED.rows() == 0);
+    REQUIRE (ED.cols() == 0);
+    REQUIRE (Eheader.size() == 0);
+
+    // there are comments
+    REQUIRE (comments.size() == 2);
+}
+
+TEST_CASE("readPLY: mesh_error.ply", "[igl]")
+{   
+    // test on a non-existent file
+    std::ifstream f(test_common::data_path("mesh_error.ply"));
+    REQUIRE (f.good() == false);
+    f.close();
+
+    Eigen::MatrixXd V;
+    Eigen::MatrixXi F;
+    REQUIRE (igl::readPLY(test_common::data_path("mesh_error.ply"), V, F) == false);
+    REQUIRE (V.rows() == 0);
+    REQUIRE (F.rows() == 0);
+}
+
+TEST_CASE("readPLY: quad_cube.ply", "[igl]")
+{
+    // small qube from blender
+    const char *ply_quad_cube=
+"ply\n"
+"format ascii 1.0\n"
+"comment Created by Blender 2.81 (sub 16) - www.blender.org, source file: ''\n"
+"element vertex 24\n"
+"property float x\n"
+"property float y\n"
+"property float z\n"
+"property float nx\n"
+"property float ny\n"
+"property float nz\n"
+"property float s\n"
+"property float t\n"
+"element face 6\n"
+"property list uchar uint vertex_indices\n"
+"end_header\n"
+"1.000000 1.000000 1.000000 0.000000 -0.000000 1.000000 0.625000 0.500000\n"
+"-1.000000 1.000000 1.000000 0.000000 -0.000000 1.000000 0.875000 0.500000\n"
+"-1.000000 -1.000000 1.000000 0.000000 -0.000000 1.000000 0.875000 0.750000\n"
+"1.000000 -1.000000 1.000000 0.000000 -0.000000 1.000000 0.625000 0.750000\n"
+"1.000000 -1.000000 -1.000000 0.000000 -1.000000 0.000000 0.375000 0.750000\n"
+"1.000000 -1.000000 1.000000 0.000000 -1.000000 0.000000 0.625000 0.750000\n"
+"-1.000000 -1.000000 1.000000 0.000000 -1.000000 0.000000 0.625000 1.000000\n"
+"-1.000000 -1.000000 -1.000000 0.000000 -1.000000 0.000000 0.375000 1.000000\n"
+"-1.000000 -1.000000 -1.000000 -1.000000 -0.000000 0.000000 0.375000 0.000000\n"
+"-1.000000 -1.000000 1.000000 -1.000000 -0.000000 0.000000 0.625000 0.000000\n"
+"-1.000000 1.000000 1.000000 -1.000000 -0.000000 0.000000 0.625000 0.250000\n"
+"-1.000000 1.000000 -1.000000 -1.000000 -0.000000 0.000000 0.375000 0.250000\n"
+"-1.000000 1.000000 -1.000000 0.000000 0.000000 -1.000000 0.125000 0.500000\n"
+"1.000000 1.000000 -1.000000 0.000000 0.000000 -1.000000 0.375000 0.500000\n"
+"1.000000 -1.000000 -1.000000 0.000000 0.000000 -1.000000 0.375000 0.750000\n"
+"-1.000000 -1.000000 -1.000000 0.000000 0.000000 -1.000000 0.125000 0.750000\n"
+"1.000000 1.000000 -1.000000 1.000000 -0.000000 0.000000 0.375000 0.500000\n"
+"1.000000 1.000000 1.000000 1.000000 -0.000000 0.000000 0.625000 0.500000\n"
+"1.000000 -1.000000 1.000000 1.000000 -0.000000 0.000000 0.625000 0.750000\n"
+"1.000000 -1.000000 -1.000000 1.000000 -0.000000 0.000000 0.375000 0.750000\n"
+"-1.000000 1.000000 -1.000000 0.000000 1.000000 0.000000 0.375000 0.250000\n"
+"-1.000000 1.000000 1.000000 0.000000 1.000000 0.000000 0.625000 0.250000\n"
+"1.000000 1.000000 1.000000 0.000000 1.000000 0.000000 0.625000 0.500000\n"
+"1.000000 1.000000 -1.000000 0.000000 1.000000 0.000000 0.375000 0.500000\n"
+"4 0 1 2 3\n"
+"4 4 5 6 7\n"
+"4 8 9 10 11\n"
+"4 12 13 14 15\n"
+"4 16 17 18 19\n"
+"4 20 21 22 23\n";
+
+    std::ofstream f("quad_cube.ply");
+    f.write(ply_quad_cube,strlen(ply_quad_cube));
+    f.close();
+
+    Eigen::MatrixXd V;
+    Eigen::MatrixXi F;
+    REQUIRE (igl::readPLY("quad_cube.ply", V, F));
+    REQUIRE (V.rows() == 24);
+    REQUIRE (V.cols() == 3);
+    REQUIRE (F.rows() == 6);
+    REQUIRE (F.cols() == 4);
+}

+ 79 - 0
tests/include/igl/writePLY.cpp

@@ -0,0 +1,79 @@
+#include <test_common.h>
+#include <igl/readPLY.h>
+#include <igl/writePLY.h>
+#include <fstream>
+#include <string>
+#include <vector>
+
+TEST_CASE("writePLY: bunny.ply", "[igl]")
+{
+    std::ifstream f(test_common::data_path("bunny.ply"));
+    REQUIRE (f.good());
+    f.close();
+    
+    Eigen::MatrixXd V1,N1,UV1,VD1,FD1,ED1;
+    std::vector<std::string> Vheader1,Fheader1,Eheader1,comments1;
+
+    Eigen::MatrixXi F1,E1;
+
+    // load test data first
+    REQUIRE (igl::readPLY(test_common::data_path("bunny.ply"), V1, F1, E1, N1, UV1, VD1,Vheader1, FD1,Fheader1, ED1,Eheader1,comments1));
+
+    // add more data 
+    Vheader1.push_back("dummy_data");
+    Eigen::VectorXd dummy_data(V1.rows());
+    for(size_t i=0;i<V1.rows();++i)
+        dummy_data(i)=(double)i;
+    Eigen::MatrixXd VD2(V1.rows(),VD1.cols()+1);
+    VD2<<VD1,dummy_data;
+
+
+    // test that saving preserves all the data, including new data column
+    REQUIRE (igl::writePLY("test_bunny.ply", V1, F1, E1, N1, UV1, VD2, Vheader1, FD1,Fheader1, ED1, Eheader1, comments1, true));
+
+    Eigen::MatrixXd V,N,UV,VD,FD,ED;
+    Eigen::MatrixXi F,E;
+    std::vector<std::string> Vheader,Fheader,Eheader,comments;
+
+    // test that saving preserves all the data
+    REQUIRE (igl::readPLY("test_bunny.ply", V, F, E, N, UV, VD,Vheader, FD,Fheader, ED,Eheader, comments));
+
+    REQUIRE (V.rows() == 35947);
+    REQUIRE (V.cols() == 3);
+    REQUIRE (F.rows() == 69451);
+    REQUIRE (F.cols() == 3);
+    // no edge data
+    REQUIRE (E.rows() == 0);
+    REQUIRE (E.cols() == 0);
+
+    // no normals or texture coordinates
+    REQUIRE (N.rows() == 0);
+    REQUIRE (N.cols() == 0);
+    REQUIRE (UV.rows() == 0);
+    REQUIRE (UV.cols() == 0);
+
+    // this bunny have additonal data
+    REQUIRE (VD.rows() == 35947);
+    REQUIRE (VD.cols() == 3);
+
+    // the dummy column contents check
+    for(size_t i=0;i<V.rows();++i)
+        REQUIRE (VD(i,2) == (double)i);
+
+    REQUIRE (Vheader.size() == 3);
+    REQUIRE (Vheader[0] == "confidence" );
+    REQUIRE (Vheader[1] == "intensity" );
+    REQUIRE (Vheader[2] == "dummy_data" );
+
+    // no Face data or edge data
+    REQUIRE (FD.rows() == 0);
+    REQUIRE (FD.cols() == 0);
+    REQUIRE (Fheader.size() == 0);
+
+    REQUIRE (ED.rows() == 0);
+    REQUIRE (ED.cols() == 0);
+    REQUIRE (Eheader.size() == 0);
+
+    // there are comments
+    REQUIRE (comments.size() == 2);
+}