vcglib/wrap/nanoply/include/nanoply.hpp

1695 lines
55 KiB
C++

/****************************************************************************
* NanoPLY *
* NanoPLY is a C++11 header-only library to read and write PLY file *
* *
* Copyright(C) 2014 *
* Visual Computing Lab *
* ISTI - Italian National Research Council *
* *
* 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 NANOPLY_HPP
#define NANOPLY_HPP
#include <vector>
#include <tuple>
#include <cassert>
#include <algorithm>
#include <stdexcept>
#include <cstdio>
#include <cmath>
#include <limits>
#include <stdint.h>
// Avoid conflicting declaration of min/max macros in windows headers
#if !defined(NOMINMAX) && (defined(_WIN32) || defined(_WIN32_) || defined(WIN32) || defined(_WIN64))
# define NOMINMAX
# ifdef max
# undef max
# undef min
# endif
#endif
#define USE_H4D_OUTPUT
namespace nanoply
{
/**
* @cond HIDDEN_SYMBOLS
*/
template < size_t T> struct SizeT {};
/**
* @endcond
*/
/** Error Type.
* Error type returned by the open of a PLY file.
*/
typedef enum NNP_ERROR
{
NNP_OK = 0x0000, /**< No error. */
NNP_UNABLE_TO_OPEN = 0x0001, /**< The file cannot be opend. */
NNP_MISSING_HEADER = 0x0002, /**< The file does not contain a valid PLY header. */
NNP_MISSING_FORMAT = 0x0004 /**< The file has an invalid internal format. */
} ErrorCode;
/** PLY Entity.
* Property that can be saved in a PLY file.
*/
typedef enum NNP_ENTITY
{
NNP_UNKNOWN_ENTITY = 0x0000, /**< Unknown property. */
NNP_PX = 0x0001, /**< Position x cordinate. */
NNP_PY = 0x0002, /**< Position y cordinate. */
NNP_PZ = 0x0004, /**< Position z cordinate. */
NNP_PXYZ = 0x0007, /**< Position (x, y, z). */
NNP_NX = 0x0010, /**< Normal x component. */
NNP_NY = 0x0020, /**< Normal y component. */
NNP_NZ = 0x0040, /**< Normal z component. */
NNP_NXYZ = 0x0070, /**< Normal (x, y, z). */
NNP_CR = 0x0100, /**< Color red component. */
NNP_CG = 0x0200, /**< Color green component. */
NNP_CB = 0x0400, /**< Color blue component. */
NNP_CRGB = 0x0700, /**< Color RGB. */
NNP_CA = 0x0800, /**< Color alpha component. */
NNP_CRGBA = 0x0F00, /**< Color RGBA. */
NNP_DENSITY = 0x1000, /**< Density or Radius property. */
NNP_SCALE = 0x2000, /**< Scale property. */
NNP_QUALITY = 0x4000, /**< Quality property. */
NNP_REFLECTANCE = 0x8000, /**< Refelctance property. */
NNP_BITFLAG = 0x10000, /**< Bit flags. */
NNP_VERTEX_LIST = 0x20000 /**< List of vertec index. */
} PlyEntity;
/** PLY Type.
* Type of a PLY property.
*/
typedef enum NNP_PLYTYPE
{
NNP_UNKNOWN_TYPE = 0x0000, /**< Unknown type. */
NNP_FLOAT32 = 0x0001, /**< Float. */
NNP_FLOAT64 = 0x0002, /**< Double. */
NNP_INT8 = 0x0010, /**< Char. */
NNP_INT16 = 0x0020, /**< Short. */
NNP_INT32 = 0x0040, /**< Int. */
NNP_UINT8 = 0x0100, /**< Unsigned Char. */
NNP_UINT16 = 0x0200, /**< Unsigned Short. */
NNP_UINT32 = 0x0400, /**< Unsigned Int. */
NNP_LIST_UINT8_UINT32 = 0x1000, /**< List (size Unsigned Char) of Unsigned Int. */
NNP_LIST_INT8_UINT32 = 0x2000, /**< List (size Char) of Unsigned Int. */
NNP_LIST_UINT8_INT32 = 0x4000, /**< List (size Unsigned Char) of Int. */
NNP_LIST_INT8_INT32 = 0x8000 /**< List (size Char) of Int. */
} PlyType;
/** PLY Property.
* Define a PLY property (entity and type).
*/
class PlyProperty
{
public:
PlyType type; /**< Property type. */
PlyEntity elem; /**< Property entity. */
/**
* Constructor that sets the type and the entity of the PLY property.
*
* @param _t Property type.
* @param _e Property entity.
*/
PlyProperty(PlyType _t, PlyEntity _e):type(_t),elem(_e){}
/**
* Get the description string of the property entity.
*
* @return Description string of the property entity.
*/
const char* EntityStr();
/**
* Get the name of the property entity.
*
* @return Name of the property entity.
*/
const char* EntityName();
/**
* Get the description string of the property type.
*
* @return Description string of the property type.
*/
const char* TypeStr();
/**
* Get the size in byte of the property type.
*
* @return Size in byte of the property type.
*/
int TypeSize();
/**
* Skip the property in an Ascii file.
*
* @param *fp Pointer to the opened file.
* @return If successful returns true. Otherwise, it returns false.
*/
bool SkipAsciiPropertyInFile(FILE *fp);
/**
* Skip the property in a binary file.
*
* @param *fp Pointer to the opened file.
* @return If successful returns true. Otherwise, it returns false.
*/
bool SkipBinaryPropertyInFile(FILE *fp);
/**
* Write the property in the header of the PLY file.
*
* @param *fp Pointer to the file.
* @return If successful returns true. Otherwise, it returns false.
*/
bool WriteHeader(FILE *fp);
};
const char* PlyProperty::EntityStr()
{
switch (this->elem)
{
case NNP_UNKNOWN_ENTITY : return "NNP_UNKNOWN_ENTITY";
case NNP_PX : return "NNP_PX ";
case NNP_PY : return "NNP_PY ";
case NNP_PZ : return "NNP_PZ ";
case NNP_PXYZ : return "NNP_PXYZ ";
case NNP_NX : return "NNP_NX ";
case NNP_NY : return "NNP_NY ";
case NNP_NZ : return "NNP_NZ ";
case NNP_NXYZ : return "NNP_NXYZ ";
case NNP_CR : return "NNP_CR ";
case NNP_CG : return "NNP_CG ";
case NNP_CB : return "NNP_CB ";
case NNP_CRGB : return "NNP_CRGB ";
case NNP_CA : return "NNP_CA ";
case NNP_CRGBA : return "NNP_CRGBA ";
case NNP_DENSITY : return "NNP_DENSITY ";
case NNP_SCALE : return "NNP_SCALE ";
case NNP_QUALITY : return "NNP_QUALITY ";
case NNP_REFLECTANCE : return "NNP_REFLECTANCE ";
case NNP_VERTEX_LIST : return "NNP_VERTEX_LIST ";
default: assert(0);
break;
}
return 0;
}
const char* PlyProperty::EntityName()
{
#ifdef USE_H4D_OUTPUT
switch (this->elem)
{
case NNP_UNKNOWN_ENTITY : return "unknown";
case NNP_PX : return "x";
case NNP_PY : return "y";
case NNP_PZ : return "z";
case NNP_PXYZ : return "x y z";
case NNP_NX : return "nx";
case NNP_NY : return "ny";
case NNP_NZ : return "nz";
case NNP_NXYZ : return "nx ny nz";
case NNP_CR : return "red";
case NNP_CG : return "green";
case NNP_CB : return "blue";
case NNP_CRGB : return "rgb";
case NNP_CA : return "alpha";
case NNP_CRGBA : return "rgba";
case NNP_DENSITY : return "radius";
case NNP_SCALE : return "scale";
case NNP_QUALITY : return "quality";
case NNP_REFLECTANCE : return "reflectance";
case NNP_VERTEX_LIST : return "vertex_indices";
default: assert(0);
break;
}
#else
switch (this->elem)
{
case NNP_UNKNOWN_ENTITY : return "unknown";
case NNP_PX : return "x";
case NNP_PY : return "y";
case NNP_PZ : return "z";
case NNP_PXYZ : return "x y z";
case NNP_NX : return "nx";
case NNP_NY : return "ny";
case NNP_NZ : return "nz";
case NNP_NXYZ : return "nx ny nz";
case NNP_CR : return "diffuse_red";
case NNP_CG : return "diffuse_green";
case NNP_CB : return "diffuse_blue";
case NNP_CRGB : return "diffuse_rgb";
case NNP_CA : return "alpha";
case NNP_CRGBA : return "diffuse_rgba";
case NNP_DENSITY : return "radius";
case NNP_SCALE : return "value";
case NNP_QUALITY : return "confidence";
case NNP_REFLECTANCE : return "reflectance";
case NNP_VERTEX_LIST : return "vertex_indices";
default: assert(0);
break;
}
#endif
return 0;
}
const char* PlyProperty::TypeStr()
{
switch (this->type)
{
case NNP_UNKNOWN_TYPE : return "NNP_UNKNOWN_TYPE ";
case NNP_FLOAT32 : return "NNP_FLOAT32 ";
case NNP_FLOAT64 : return "NNP_FLOAT64 ";
case NNP_INT8 : return "NNP_INT8 ";
case NNP_INT16 : return "NNP_INT16 ";
case NNP_INT32 : return "NNP_INT32 ";
case NNP_UINT8 : return "NNP_UINT8 ";
case NNP_UINT16 : return "NNP_UINT16 ";
case NNP_UINT32 : return "NNP_UINT32 ";
case NNP_LIST_UINT8_UINT32: return "NNP_LIST_UINT8_UINT32";
case NNP_LIST_INT8_UINT32 : return "NNP_LIST_INT8_UINT32 ";
case NNP_LIST_UINT8_INT32 : return "NNP_LIST_UINT8_INT32 ";
case NNP_LIST_INT8_INT32 : return "NNP_LIST_INT8_INT32 ";
default: assert(0);
break;
}
return 0;
}
int PlyProperty::TypeSize()
{
switch (this->type)
{
case NNP_UNKNOWN_TYPE:
return 0;
case NNP_INT8:
case NNP_UINT8:
return 1;
case NNP_INT16:
case NNP_UINT16:
return 2;
case NNP_FLOAT32:
case NNP_INT32:
case NNP_UINT32:
return 4;
case NNP_FLOAT64:
return 8;
case NNP_LIST_UINT8_UINT32:
case NNP_LIST_INT8_UINT32 :
case NNP_LIST_UINT8_INT32 :
case NNP_LIST_INT8_INT32 :
return 1;
default: assert(0);
break;
}
return 0;
}
bool PlyProperty::SkipAsciiPropertyInFile(FILE *fp)
{
int count = 1;
if (this->elem == NNP_CRGB || this->elem == NNP_NXYZ || this->elem == NNP_PXYZ)
count = 3;
else if (this->elem == NNP_CRGBA)
count = 4;
switch(type)
{
case NNP_INT8:
case NNP_INT16:
case NNP_INT32:
{
int* temp = new int[count];
for (int i = 0; i < count ; i++)
fscanf(fp, "%d", &temp[i]);
delete[] temp;
break;
}
case NNP_UINT8:
case NNP_UINT16:
case NNP_UINT32:
{
unsigned int* temp = new unsigned int[count];
for (int i = 0; i < count ; i++)
fscanf(fp, "%u", &temp[i]);
delete[] temp;
break;
}
case NNP_FLOAT32:
{
float* temp = new float[count];
for (int i = 0; i < count ; i++)
fscanf(fp, "%f", &temp[i]);
delete[] temp;
break;
}
case NNP_FLOAT64:
{
double* temp = new double[count];
for (int i = 0; i < count ; i++)
fscanf(fp, "%f", &temp[i]);
delete[] temp;
break;
}
case NNP_LIST_UINT8_UINT32:
case NNP_LIST_INT8_UINT32 :
{
fscanf(fp, "%d", &count);
unsigned int* temp = new unsigned int[count];
for (int i = 0; i < count ; i++)
fscanf(fp, "%u", &temp[i]);
delete[] temp;
break;
}
case NNP_LIST_UINT8_INT32 :
case NNP_LIST_INT8_INT32 :
{
fscanf(fp, "%d", &count);
int* temp = new int[count];
for (int i = 0; i < count ; i++)
fscanf(fp, "%d", &temp[i]);
delete[] temp;
break;
}
}
return true;
}
bool PlyProperty::SkipBinaryPropertyInFile(FILE *fp)
{
int count = 1;
if (this->elem == NNP_CRGB || this->elem == NNP_NXYZ || this->elem == NNP_PXYZ)
count = 3;
else if (this->elem == NNP_CRGBA)
count = 4;
if (this->type >= NNP_LIST_UINT8_UINT32)
{
unsigned char cntList = 0;
fread(&cntList, sizeof(char), 1, fp);
fseek(fp, 4 * cntList, SEEK_CUR);
}
else
fseek(fp, this->TypeSize() * count, SEEK_CUR);
return true;
}
bool PlyProperty::WriteHeader(FILE *fp)
{
std::string name, type;
switch (this->type)
{
case NNP_UNKNOWN_TYPE : type = "unknonw"; break;
case NNP_FLOAT32 : type = "float"; break;
case NNP_FLOAT64 : type = "double"; break;
case NNP_INT8 : type = "char"; break;
case NNP_INT16 : type = "short"; break;
case NNP_INT32 : type = "int"; break;
case NNP_UINT8 : type = "uchar"; break;
case NNP_UINT16 : type = "ushort"; break;
case NNP_UINT32 : type = "uint"; break;
case NNP_LIST_UINT8_UINT32: type = "list uchar uint"; break;
case NNP_LIST_INT8_UINT32 : type = "list char uint"; break;
case NNP_LIST_UINT8_INT32 : type = "list uchar int"; break;
case NNP_LIST_INT8_INT32 : type = "list char int"; break;
}
name = this->EntityName();
switch (this->elem)
{
case NNP_PXYZ :
{
name = PlyProperty(nanoply::NNP_FLOAT32, nanoply::NNP_PX).EntityName();
fprintf(fp, "property %s %s\n", type.c_str(), name.c_str());
name = PlyProperty(nanoply::NNP_FLOAT32, nanoply::NNP_PY).EntityName();
fprintf(fp, "property %s %s\n", type.c_str(), name.c_str());
name = PlyProperty(nanoply::NNP_FLOAT32, nanoply::NNP_PZ).EntityName();
break;
}
case NNP_NXYZ :
{
name = PlyProperty(nanoply::NNP_FLOAT32, nanoply::NNP_NX).EntityName();
fprintf(fp, "property %s %s\n", type.c_str(), name.c_str());
name = PlyProperty(nanoply::NNP_FLOAT32, nanoply::NNP_NY).EntityName();
fprintf(fp, "property %s %s\n", type.c_str(), name.c_str());
name = PlyProperty(nanoply::NNP_FLOAT32, nanoply::NNP_NZ).EntityName();
break;
}
case NNP_CRGB :
{
name = PlyProperty(nanoply::NNP_FLOAT32, nanoply::NNP_CR).EntityName();
fprintf(fp, "property %s %s\n", type.c_str(), name.c_str());
name = PlyProperty(nanoply::NNP_FLOAT32, nanoply::NNP_CG).EntityName();
fprintf(fp, "property %s %s\n", type.c_str(), name.c_str());
name = PlyProperty(nanoply::NNP_FLOAT32, nanoply::NNP_CB).EntityName();
break;
}
case NNP_CRGBA:
{
name = PlyProperty(nanoply::NNP_FLOAT32, nanoply::NNP_CR).EntityName();
fprintf(fp, "property %s %s\n", type.c_str(), name.c_str());
name = PlyProperty(nanoply::NNP_FLOAT32, nanoply::NNP_CG).EntityName();
fprintf(fp, "property %s %s\n", type.c_str(), name.c_str());
name = PlyProperty(nanoply::NNP_FLOAT32, nanoply::NNP_CB).EntityName();
fprintf(fp, "property %s %s\n", type.c_str(), name.c_str());
name = PlyProperty(nanoply::NNP_FLOAT32, nanoply::NNP_CA).EntityName();;
break;
}
}
fprintf(fp, "property %s %s\n", type.c_str(), name.c_str());
return true;
}
/** PLY Element.
* Define a PLY Element as a collection of properties.
*/
class PlyElement
{
public:
std::string name; /**< Name of the elment in the PLY header (for example "vertex" or "face") */
int cnt; /**< Number of instances of the elment in the PLY file */
std::vector<PlyProperty> propVec; /**< Collection of properties that define the element */
/**
* Default Constructor
*/
PlyElement();
/**
* Constructor that sets the name, the properties and the number of instances of the element.
*
* @param *name Name of the element.
* @param &prop Vector of properties.
* @param nElem Number of instances.
*/
PlyElement(const char *name, std::vector<PlyProperty> &prop, int nElem);
/**
* Parse the input line and add the properties to the element.
* It assumes that the passed line has the folowing structure: "property PLYTYPE PLYELEMENT"
*
* @param *line Input line.
* @return If successful returns true. Otherwise, it returns false.
*/
bool AddProperty(const char *line);
/**
* Initialize an element from an header file.
* The first line of the buf is passed into line and must start with the 'element' keyword
*
* @param *fp Pointer to the opened file.
* @param *line First buffer line.
* @return If successful returns true. Otherwise, it returns false.
*/
bool InitFromHeader(FILE *fp, char *line);
/**
* Write the element descriport in the header file.
*
* @param *fp Pointer to the file.
* @return If successful returns true. Otherwise, it returns false.
*/
bool WriteHeader(FILE *fp);
/**
* Skip the element in an Ascii file.
*
* @param *fp Pointer to the opened file.
* @return If successful returns true. Otherwise, it returns false.
*/
bool SkipAsciiElementsInFile(FILE *fp);
/**
* Skip the element in a binary file.
*
* @param *fp Pointer to the opened file.
* @return If successful returns true. Otherwise, it returns false.
*/
bool SkipBinaryElementsInFile(FILE *fp);
/**
* Check if the input entity is in the property of the element.
*
* @param entity Input entity.
* @return If successful returns true. Otherwise, it returns false.
*/
bool Contains(NNP_ENTITY entity);
};
PlyElement::PlyElement(){}
PlyElement::PlyElement(const char *_name, std::vector<PlyProperty> &prop, int nElem):name(_name), cnt(nElem), propVec(prop)
{
}
bool PlyElement::InitFromHeader(FILE *fp, char *line)
{
char buf[4096];
for(int i=0;line[i]!=0;++i) buf[i]=tolower(line[i]);
strtok(buf," \t"); // property
assert(strstr(buf,"element"));
char *el = strtok(0," \t\n");
name = std::string(el);
sscanf(line,"%*s %*s %i",&cnt);
//printf("Adding Element '%s' (%i) \n",name.c_str(),cnt);
fgets(line,4096,fp);
while(strstr(line,"property"))
{
AddProperty(line);
fgets(line,4096,fp);
}
unsigned int mask = 0;
for (int i = 0; i < propVec.size(); i++)
mask |= propVec[i].elem;
std::vector<PlyProperty> compactPropVec;
for (int i = 0; i < propVec.size(); i++)
{
switch (propVec[i].elem)
{
case NNP_NX:
case NNP_NY:
if ((mask & NNP_NXYZ) != NNP_NXYZ)
compactPropVec.push_back(propVec[i]);
break;
case NNP_NZ:
if ((mask & NNP_NXYZ) != NNP_NXYZ)
compactPropVec.push_back(propVec[i]);
else
compactPropVec.push_back(PlyProperty(propVec[i].type, NNP_NXYZ));
break;
case NNP_PX:
case NNP_PY:
if ((mask & NNP_PXYZ) != NNP_PXYZ)
compactPropVec.push_back(propVec[i]);
break;
case NNP_PZ:
if ((mask & NNP_PXYZ) != NNP_PXYZ)
compactPropVec.push_back(propVec[i]);
else
compactPropVec.push_back(PlyProperty(propVec[i].type, NNP_PXYZ));
break;
case NNP_CR:
case NNP_CG:
if ((mask & NNP_CRGB) != NNP_CRGB & (mask & NNP_CRGBA) != NNP_CRGBA)
compactPropVec.push_back(propVec[i]);
break;
case NNP_CB:
if ((mask & NNP_CRGB) != NNP_CRGB & (mask & NNP_CRGBA) != NNP_CRGBA)
compactPropVec.push_back(propVec[i]);
else if ((mask & NNP_CRGB) == NNP_CRGB & (mask & NNP_CRGBA) != NNP_CRGBA)
compactPropVec.push_back(PlyProperty(propVec[i].type, NNP_CRGB));
break;
case NNP_CA:
if ((mask & NNP_CRGB) != NNP_CRGB & (mask & NNP_CRGBA) != NNP_CRGBA)
compactPropVec.push_back(propVec[i]);
else if ((mask & NNP_CRGBA) == NNP_CRGBA)
compactPropVec.push_back(PlyProperty(propVec[i].type, NNP_CRGBA));
break;
default:
compactPropVec.push_back(propVec[i]);
break;
}
}
propVec.clear();
propVec = compactPropVec;
return true;
}
bool PlyElement::WriteHeader(FILE *fp)
{
fprintf(fp, "element %s %d\n", name.c_str(), cnt);
for(int i = 0; i < propVec.size(); i++)
propVec[i].WriteHeader(fp);
return true;
}
bool PlyElement::SkipAsciiElementsInFile(FILE *fp)
{
char line[4096];
for(int i = 0; i < this->cnt; ++i)
fgets(line, 4096, fp);
return true;
}
bool PlyElement::SkipBinaryElementsInFile(FILE *fp)
{
for(int i = 0; i < this->cnt; ++i)
for(int j = 0; j < this->propVec.size(); ++j)
this->propVec[j].SkipBinaryPropertyInFile(fp);
return true;
}
bool PlyElement::AddProperty(const char *line)
{
char buf[128];
for(int i=0;line[i]!=0;++i) buf[i]=tolower(line[i]);
strtok(buf," \t"); // property
char *ty = strtok(0," \t\n"); // float
PlyType type = NNP_UNKNOWN_TYPE;
if(strcmp(ty,"float") == 0 || strcmp(ty,"float32") == 0) type = NNP_FLOAT32;
if(strcmp(ty,"double") == 0 || strcmp(ty,"float64") == 0) type = NNP_FLOAT64;
if(strcmp(ty,"char") == 0 || strcmp(ty,"int8") == 0) type = NNP_INT8;
if(strcmp(ty,"short") == 0 || strcmp(ty,"int16") == 0) type = NNP_INT16;
if(strcmp(ty,"int") == 0 || strcmp(ty,"int32") == 0) type = NNP_INT32;
if(strcmp(ty,"uchar") == 0 || strcmp(ty,"uint8") == 0 ) type = NNP_UINT8;
if(strcmp(ty,"ushort") == 0|| strcmp(ty,"uint16") == 0 ) type = NNP_UINT16;
if(strcmp(ty,"uint") == 0 || strcmp(ty,"uint32") == 0 ) type = NNP_UINT32;
if(strcmp(ty,"list") == 0 ) {
char *ty1 = strtok(0," \t\n");
char *ty2 = strtok(0," \t\n");
if( (strcmp(ty1,"uchar") == 0 ||strcmp(ty1,"uint8") == 0 ) && (strcmp(ty2,"uint") == 0 || strcmp(ty2,"uint32") == 0) )
type = NNP_LIST_UINT8_UINT32;
if( (strcmp(ty1,"char") == 0 || strcmp(ty1,"int8") == 0) && (strcmp(ty2,"uint") == 0|| strcmp(ty2,"uint32") == 0) )
type = NNP_LIST_INT8_UINT32;
if( (strcmp(ty1,"uchar") == 0 ||strcmp(ty1,"uint8") == 0) && (strcmp(ty2,"int") == 0 || strcmp(ty2,"int32") == 0) )
type = NNP_LIST_UINT8_INT32;
if( (strcmp(ty1,"char") == 0 || strcmp(ty1,"int8") == 0) && (strcmp(ty2,"int") == 0 || strcmp(ty2,"int32") ) )
type = NNP_LIST_INT8_INT32;
}
assert(type != NNP_UNKNOWN_TYPE);
char *el = strtok(0," \t\n"); // x
PlyEntity ent = NNP_UNKNOWN_ENTITY;
if(strstr(el,"x")) ent = NNP_PX;
if(strstr(el,"y")) ent = NNP_PY;
if(strstr(el,"z")) ent = NNP_PZ;
if(strstr(el,"nx")) ent = NNP_NX;
if(strstr(el,"ny")) ent = NNP_NY;
if(strstr(el,"nz")) ent = NNP_NZ;
if (strstr(el,"red")) ent = NNP_CR;
if (strstr(el,"diffuse_red")) ent = NNP_CR;
if (strstr(el,"green")) ent = NNP_CG;
if (strstr(el,"diffuse_green")) ent = NNP_CG;
if (strstr(el,"blue")) ent = NNP_CB;
if (strstr(el,"diffuse_blue")) ent = NNP_CB;
if(strstr(el,"alpha")) ent = NNP_CA;
if (strstr(el,"scale")) ent = NNP_SCALE;
if (strstr(el,"value")) ent = NNP_SCALE;
if(strstr(el,"density")) ent = NNP_DENSITY;
if(strstr(el,"radius")) ent = NNP_DENSITY;
if (strstr(el,"quality")) ent = NNP_QUALITY;
if (strstr(el,"confidence")) ent = NNP_QUALITY;
if(strstr(el,"reflectance")) ent = NNP_REFLECTANCE;
if(strstr(el,"vertex_index")) ent = NNP_VERTEX_LIST;
if(strstr(el,"vertex_indices")) ent = NNP_VERTEX_LIST;
//assert(ent != NNP_UNKNOWN_ENTITY);
propVec.push_back(PlyProperty(type,ent));
//printf("Adding Property %s %s\n",propVec.back().TypeStr(),propVec.back().EntityStr());
return true;
}
bool PlyElement::Contains(PlyEntity entity)
{
for (int i = 0; i < propVec.size(); i++)
{
if (propVec[i].elem == entity)
return true;
}
return false;
}
/** PLY header info.
* Define the data of the PLY header
*/
class Info
{
public:
ErrorCode errInfo; /**< Error code returned by the reading of a PLY file */
bool binary; /**< Boolean about the file format (Binary = true, Ascci = false) */
std::vector<PlyElement> elemVec; /**< Elements defided in the header */
bool bigEndian; /**< Endianess of the binary file */
/**
* Default Constructor
*/
Info();
/**
* Constructor that reads the header info from a file.
*
* @param *filename Path of the file to read.
*/
Info(const char *filename);
/**
* Constructor that creates the header info for a mesh.
*
* @param &vertex Vertex element object.
* @param &face Face element object.
* @param binary File format (binary = true, ascci = false).
*/
Info(PlyElement &vertex, PlyElement &face, bool binary);
/**
* Constructor that creates the header info for a point cloud.
*
* @param &vertex Vertex element object.
* @param binary File format (binary = true, ascci = false).
*/
Info(PlyElement &vertex, bool binary);
/**
* Clear the object.
*/
void Clear() { errInfo=NNP_OK; }
/**
* Return the number of vertex instances
*
* @return The number of vertex instances
*/
int GetVertexCount() const;
/**
* Return the number of face instances
*
* @return The number of face instances
*/
int GetFaceCount() const;
/**
* Return a reference to the vertex element
*
* @return The reference to the vertex element
*/
PlyElement* GetVertexElement();
/**
* Return a reference to the face element
*
* @return The reference to the face element
*/
PlyElement* GetFaceElement();
};
Info::Info(){}
Info::Info(const char *filename)
{
this->errInfo = NNP_OK;
FILE *fp=fopen(filename,"r");
if(!fp)
{
this->errInfo = NNP_UNABLE_TO_OPEN;
return;
}
char buf[4096];
fgets(buf,4096,fp);
if( (strncmp(buf,"PLY",3)!=0) && (strncmp(buf,"ply",3)!=0) )
{
this->errInfo = NNP_MISSING_HEADER;
return;
}
fgets(buf,4096,fp);
if(strncmp(buf,"format",strlen("format"))!=0)
{
this->errInfo = NNP_MISSING_FORMAT;
return;
}
if(strstr(buf,"ascii") || strstr(buf,"ASCII"))
{
this->binary=false;
}
else if(strstr(buf,"binary") || strstr(buf,"BINARY"))
{
this->binary=true;
if (strstr(buf, "binary_big") || strstr(buf, "BINARY_BIG"))
this->bigEndian = true;
else
this->bigEndian = false;
}
else
{
this->errInfo = NNP_MISSING_FORMAT;
return;
}
fgets(buf,4096,fp);
while(strncmp(buf,"end_header",strlen("end_header")))
{
if(strstr(buf,"comment") || strstr(buf,"COMMENT") )
{
fgets(buf,4096,fp);
continue;
}
if(strstr(buf,"element"))
{
PlyElement pe;
pe.InitFromHeader(fp,buf);
this->elemVec.push_back(pe);
}
}
fclose(fp);
}
Info::Info(PlyElement &vertex, PlyElement &face, bool binary)
{
elemVec.push_back(vertex);
elemVec.push_back(face);
this->binary = binary;
}
Info::Info(PlyElement &vertex, bool binary)
{
elemVec.push_back(vertex);
this->binary = binary;
}
int Info::GetVertexCount() const
{
for (int i = 0; i < elemVec.size(); i++)
{
if (elemVec[i].name.compare(std::string("vertex")) == 0)
return elemVec[i].cnt;
}
return -1;
}
int Info::GetFaceCount() const
{
for (int i = 0; i < elemVec.size(); i++)
{
if (elemVec[i].name.compare(std::string("face")) == 0)
return elemVec[i].cnt;
}
return -1;
}
PlyElement* Info::GetVertexElement()
{
for (int i = 0; i < elemVec.size(); i++)
{
if (elemVec[i].name.compare(std::string("vertex")) == 0)
return &elemVec[i];
}
return NULL;
}
PlyElement* Info::GetFaceElement()
{
for (int i = 0; i < elemVec.size(); i++)
{
if (elemVec[i].name.compare(std::string("face")) == 0)
return &elemVec[i];
}
return NULL;
}
/**
* @cond HIDDEN_SYMBOLS
*/
void adjustEndianess(unsigned char* buffer, int typeSize, int count)
{
for (int i = 0; i < count; i++)
{
int offset = i*typeSize;
for (int j = 0; j < typeSize/2; j++)
{
unsigned char temp = buffer[offset+j];
buffer[offset+j] = buffer[offset + typeSize - 1 - j];
buffer[offset + typeSize - 1 - j] = temp;
}
}
}
/**
* @endcond
*/
/** Memory descriptor of a vector of properties.
* The class defines how a vector of PlyProperty is saved in memory.
*
* @tparam CointainerType Type of the container of the property
* @tparam VectorSize Number of values stored in the property.
* @tparam ScalarType Type of the values stored in the property.
*/
template<class CointainerType, int VectorSize, typename ScalarType>
class VectorDescriptor
{
int64_t curPos; /**< Position of the next property to read or to write. */
PlyEntity elem; /**< Ply entity managed by the descriptor. */
void *base; /**< Pointer to the memory location that contains the data of the property. */
public:
/**
* Void constructor.
*
*/
VectorDescriptor();
/**
* Constructor of the descriptor.
*
* @param _e Ply entity managed by the descriptor.
* @param *_b Pointer to the memory location that contains the data of the property.
*/
VectorDescriptor(PlyEntity _e, void *_b);
/**
* Restart the descriptor.
*/
void Restart();
/**
* Read the property data from the binary file if the entity of the property prop is equals to the entity of the descriptor.
*
* @param *fp Pointer to the opened file.
* @param &prop Next PLY property to read from the file.
* @param bigEndian Endianess of the binary data (true = big endian, false = little endian).
* @return If successful returns true. Otherwise, it returns false.
*/
bool ReadElemBinary(FILE *fp, PlyProperty &prop, bool bigEndian);
/**
* Read the property data from the ascii file if the entity of the property prop is equals to the entity of the descriptor.
*
* @param *fp Pointer to the opened file.
* @param &prop Next PLY property to read from the file.
* @return If successful returns true. Otherwise, it returns false.
*/
bool ReadElemAscii(FILE *fp, PlyProperty &prop);
/**
* Write the property data in the binary file if the entity of the property prop is equals to the entity of the descriptor.
*
* @param *fp Pointer to the file.
* @param &prop Next PLY property to write from the file.
* @return If successful returns true. Otherwise, it returns false.
*/
bool WriteElemBinary(FILE *fp, PlyProperty &prop);
/**
* Write the property data in the ascii file if the entity of the property prop is equals to the entity of the descriptor.
*
* @param *fp Pointer to the file.
* @param &prop Next PLY property to write from the file.
* @return If successful returns true. Otherwise, it returns false.
*/
bool WriteElemAscii(FILE *fp, PlyProperty &prop);
private:
template<typename C>
void ReadBinary(FILE *fp, PlyProperty &prop, bool bigEndian);
template<typename C>
void ReadAscii(FILE *fp, PlyProperty &prop, const char *format);
template<typename C>
void WriteBinary(FILE *fp, PlyProperty &prop);
template<typename C>
void WriteAscii(FILE *fp, PlyProperty &prop, const char *format);
};
template<class CointainerType, int VectorSize, typename ScalarType>
VectorDescriptor<CointainerType, VectorSize, ScalarType>::VectorDescriptor()
{
}
template<class CointainerType, int VectorSize, typename ScalarType>
VectorDescriptor<CointainerType, VectorSize, ScalarType>::VectorDescriptor(PlyEntity _e, void *_b):curPos(0),elem(_e),base(_b)
{
}
template<class CointainerType, int VectorSize, typename ScalarType>
void VectorDescriptor<CointainerType, VectorSize, ScalarType>::Restart()
{
this->curPos=0;
}
template<class ContainerType, int VectorSize, typename ScalarType>
template<typename C>
void VectorDescriptor<ContainerType, VectorSize, ScalarType>::ReadBinary(FILE *fp, PlyProperty &prop, bool bigEndian)
{
int count = 1;
if (prop.elem == NNP_CRGB || prop.elem == NNP_NXYZ || prop.elem == NNP_PXYZ)
count = 3;
else if (prop.elem == NNP_CRGBA)
count = 4;
int typeSize = prop.TypeSize();
unsigned char* buffer = new unsigned char[count*typeSize];
fread(buffer, typeSize, count, fp);
if (typeSize > 1 && bigEndian)
adjustEndianess(buffer, typeSize, count);
if (prop.type == NNP_LIST_UINT8_UINT32 || prop.type == NNP_LIST_UINT8_INT32)
{
count = buffer[0];
delete[] buffer;
buffer = new unsigned char[count*4];
fread(buffer, 4, count, fp);
if (bigEndian)
adjustEndianess(buffer, 4, count);
}
else if (prop.type == NNP_LIST_INT8_UINT32 || prop.type == NNP_LIST_INT8_INT32)
{
count = char(buffer[0]);
delete[] buffer;
buffer = new unsigned char[count*4];
fread(buffer, 4, count, fp);
if (bigEndian)
adjustEndianess(buffer, 4, count);
}
C* temp = (C*)buffer;
unsigned char* baseProp = (unsigned char*)base + this->curPos*sizeof(ContainerType);
for (int i = 0; i < std::min(VectorSize, count); i++)
*(ScalarType *)(baseProp + i*sizeof(ScalarType)) = ScalarType(temp[i]);
++(this->curPos);
delete[] buffer;
}
template<class ContainerType, int VectorSize, typename ScalarType>
bool VectorDescriptor<ContainerType, VectorSize, ScalarType>::ReadElemBinary(FILE *fp, PlyProperty &prop, bool bigEndian)
{
if (prop.elem != elem)
return false;
switch(prop.type)
{
case NNP_INT8: this->ReadBinary<char>(fp, prop, bigEndian); break;
case NNP_UINT8: this->ReadBinary<unsigned char>(fp, prop, bigEndian); break;
case NNP_INT16: this->ReadBinary<short>(fp, prop, bigEndian); break;
case NNP_UINT16: this->ReadBinary<unsigned short>(fp, prop, bigEndian); break;
case NNP_FLOAT32: this->ReadBinary<float>(fp, prop, bigEndian); break;
case NNP_INT32: this->ReadBinary<int>(fp, prop, bigEndian); break;
case NNP_UINT32: this->ReadBinary<unsigned int>(fp, prop, bigEndian); break;
case NNP_FLOAT64: this->ReadBinary<double>(fp, prop, bigEndian); break;
case NNP_LIST_UINT8_UINT32:
case NNP_LIST_INT8_UINT32: this->ReadBinary<unsigned int>(fp, prop, bigEndian); break;
case NNP_LIST_UINT8_INT32:
case NNP_LIST_INT8_INT32: this->ReadBinary<int>(fp, prop, bigEndian); break;
}
return true;
}
template<class ContainerType, int VectorSize, typename ScalarType>
template<typename C>
void VectorDescriptor<ContainerType, VectorSize, ScalarType>::ReadAscii(FILE *fp, PlyProperty &prop, const char *format)
{
int count = 1;
if (prop.elem == NNP_CRGB || prop.elem == NNP_NXYZ || prop.elem == NNP_PXYZ)
count = 3;
else if (prop.elem == NNP_CRGBA)
count = 4;
if (prop.type == NNP_LIST_UINT8_UINT32 || prop.type == NNP_LIST_UINT8_INT32)
{
unsigned char listSize;
fscanf(fp, "%u", &listSize);
count = listSize;
}
else if (prop.type == NNP_LIST_INT8_UINT32 || prop.type == NNP_LIST_INT8_INT32)
{
char listSize;
fscanf(fp, "%d", &listSize);
count = listSize;
}
C* temp = new C[count];
for (int i = 0; i < count ; i++)
fscanf(fp, format, &temp[i]);
unsigned char* baseProp = (unsigned char*)base + this->curPos*sizeof(ContainerType);
for (int i = 0; i < std::min(VectorSize,count); i++)
*(ScalarType *)(baseProp + i*sizeof(ScalarType)) = ScalarType(temp[i]);
delete[] temp;
++(this->curPos);
}
template<class ContainerType, int VectorSize, typename ScalarType>
bool VectorDescriptor<ContainerType, VectorSize, ScalarType>::ReadElemAscii(FILE *fp, PlyProperty &prop)
{
if (prop.elem != elem)
return false;
switch(prop.type)
{
case NNP_INT8: this->ReadAscii<int>(fp, prop, "%d"); break;
case NNP_UINT8: this->ReadAscii<unsigned int>(fp, prop, "%u"); break;
case NNP_INT16: this->ReadAscii<short>(fp, prop, "%d"); break;
case NNP_UINT16: this->ReadAscii<unsigned short>(fp, prop, "%u"); break;
case NNP_FLOAT32: this->ReadAscii<float>(fp, prop, "%f"); break;
case NNP_INT32: this->ReadAscii<int>(fp, prop, "%d"); break;
case NNP_UINT32: this->ReadAscii<unsigned int>(fp, prop, "%u"); break;
case NNP_FLOAT64: this->ReadAscii<double>(fp, prop, "%f"); break;
case NNP_LIST_UINT8_UINT32:
case NNP_LIST_INT8_UINT32: this->ReadAscii<unsigned int>(fp, prop, "%u"); break;
case NNP_LIST_UINT8_INT32:
case NNP_LIST_INT8_INT32: this->ReadAscii<int>(fp, prop, "%d"); break;
}
return true;
}
template<class ContainerType, int VectorSize, typename ScalarType>
template<typename C>
void VectorDescriptor<ContainerType, VectorSize, ScalarType>::WriteBinary(FILE *fp, PlyProperty &prop)
{
int count = 1;
if (prop.elem == NNP_CRGB || prop.elem == NNP_NXYZ || prop.elem == NNP_PXYZ)
count = 3;
else if (prop.elem == NNP_CRGBA)
count = 4;
C data[VectorSize];
if (prop.type == NNP_LIST_UINT8_UINT32 || prop.type == NNP_LIST_UINT8_INT32)
{
unsigned char listSize = (unsigned char) VectorSize;
fwrite(&listSize, sizeof(unsigned char), 1, fp);
count = VectorSize;
}
else if (prop.type == NNP_LIST_INT8_UINT32 || prop.type == NNP_LIST_INT8_INT32)
{
char listSize = (char) VectorSize;
fwrite(&listSize, sizeof(char), 1, fp);
count = VectorSize;
}
C temp = 0;
unsigned char* baseProp = (unsigned char*)base + this->curPos*sizeof(ContainerType);
for (int i = 0; i < std::min(VectorSize, count); i++)
data[i] = (C)(*(ScalarType*)(baseProp + i*sizeof(ScalarType)));
fwrite(data, sizeof(C), std::min(VectorSize, count), fp);
for (int i = 0; i < (count - VectorSize); i++)
fwrite(&temp, sizeof(C), 1, fp);
++(this->curPos);
}
template<class ContainerType, int VectorSize, typename ScalarType>
bool VectorDescriptor<ContainerType, VectorSize, ScalarType>::WriteElemBinary(FILE *fp, PlyProperty &prop)
{
if (prop.elem != elem)
return false;
switch(prop.type)
{
case NNP_INT8: this->WriteBinary<char>(fp, prop); break;
case NNP_UINT8: this->WriteBinary<unsigned char>(fp, prop); break;
case NNP_INT16: this->WriteBinary<short>(fp, prop); break;
case NNP_UINT16: this->WriteBinary<unsigned short>(fp, prop); break;
case NNP_FLOAT32: this->WriteBinary<float>(fp, prop); break;
case NNP_INT32: this->WriteBinary<int>(fp, prop); break;
case NNP_UINT32: this->WriteBinary<unsigned int>(fp, prop); break;
case NNP_FLOAT64: this->WriteBinary<double>(fp, prop); break;
case NNP_LIST_UINT8_UINT32:
case NNP_LIST_INT8_UINT32: this->WriteBinary<unsigned int>(fp, prop); break;
case NNP_LIST_UINT8_INT32:
case NNP_LIST_INT8_INT32: this->WriteBinary<int>(fp, prop); break;
}
return true;
}
template<class ContainerType, int VectorSize, typename ScalarType>
template<typename C>
void VectorDescriptor<ContainerType, VectorSize, ScalarType>::WriteAscii(FILE *fp, PlyProperty &prop, const char *format)
{
int count = 1;
if (prop.elem == NNP_CRGB || prop.elem == NNP_NXYZ || prop.elem == NNP_PXYZ)
count = 3;
else if (prop.elem == NNP_CRGBA)
count = 4;
if (prop.type == NNP_LIST_UINT8_UINT32 || prop.type == NNP_LIST_UINT8_INT32)
{
fprintf(fp, "%u ", (unsigned char)(VectorSize));
count = VectorSize;
}
else if (prop.type == NNP_LIST_INT8_UINT32 || prop.type == NNP_LIST_INT8_INT32)
{
fprintf(fp, "%d ", (char)(VectorSize));
count = VectorSize;
}
unsigned char* baseProp = (unsigned char*)base + this->curPos*sizeof(ContainerType);
for (int i = 0; i < std::min(VectorSize, count); i++)
fprintf(fp, format, (C)(*(ScalarType*)(baseProp + i*sizeof(ScalarType))));
for (int i = 0; i < (count - VectorSize); i++)
fprintf(fp, format, (C)(0));
++(this->curPos);
}
template<class ContainerType, int VectorSize, typename ScalarType>
bool VectorDescriptor<ContainerType, VectorSize, ScalarType>::WriteElemAscii(FILE *fp, PlyProperty& prop)
{
if (prop.elem != elem)
return false;
switch(prop.type)
{
case NNP_INT8: this->WriteAscii<char>(fp, prop, "%d "); break;
case NNP_UINT8: this->WriteAscii<unsigned char>(fp, prop, "%u "); break;
case NNP_INT16: this->WriteAscii<short>(fp, prop, "%d "); break;
case NNP_UINT16: this->WriteAscii<unsigned short>(fp, prop, "%u "); break;
case NNP_FLOAT32: this->WriteAscii<float>(fp, prop, "%f "); break;
case NNP_INT32: this->WriteAscii<int>(fp, prop, "%d "); break;
case NNP_UINT32: this->WriteAscii<unsigned int>(fp, prop, "%u "); break;
case NNP_FLOAT64: this->WriteAscii<double>(fp, prop, "%f "); break;
case NNP_LIST_UINT8_UINT32:
case NNP_LIST_INT8_UINT32: this->WriteAscii<unsigned int>(fp, prop, "%u "); break;
case NNP_LIST_UINT8_INT32:
case NNP_LIST_INT8_INT32: this->WriteAscii<int>(fp, prop, "%d "); break;
}
return true;
}
/**
* @cond HIDDEN_SYMBOLS
*/
template <class TupleType>
void ReadBinaryElement(TupleType &adaptor, PlyElement &elem, FILE *fp, bool bigEndian)
{
for (int i = 0 ; i < elem.cnt; i++)
{
for (int j = 0; j < elem.propVec.size(); j++)
{
PlyProperty& prop = elem.propVec[j];
if (!TupleForEach(adaptor, fp, prop, bigEndian, SizeT<0>()))
prop.SkipBinaryPropertyInFile(fp);
}
}
}
template <class TupleType>
void ReadAsciiElement(TupleType &adaptor, PlyElement &elem, FILE *fp)
{
for (int i = 0 ; i < elem.cnt; i++)
{
for (int j = 0; j < elem.propVec.size(); j++)
{
PlyProperty& prop = elem.propVec[j];
if (!TupleForEach(adaptor, fp, prop, false, SizeT<1>()))
prop.SkipAsciiPropertyInFile(fp);
}
}
}
/**
* @endcond
*/
/**
* Read a point cloud from a PLY file.
*
* @tparam VertexAdaptorTuple Type that defines the management of the vertex data in memory
*
* @param filename Path to the file to read
* @param vertexAdaptor std::tuple that defines how to manage the vertex data in memory
*/
template <class VertexAdaptorTuple>
bool OpenPointCloud(const char *filename, VertexAdaptorTuple vertexAdaptor)
{
nanoply::Info info(filename);
if (info.errInfo != NNP_OK)
return false;
FILE *fp=fopen(filename,"rb");
if(!fp)
{
return false;
}
char buf[4096];
do
{
fgets(buf,4096,fp);
}
while(strncmp(buf,"end_header",strlen("end_header")) );
// Now start the real reading!
if (info.binary)
{
for(int i = 0; i < info.elemVec.size();++i)
if (strcmp(info.elemVec[i].name.c_str(), "vertex") == 0)
ReadBinaryElement<VertexAdaptorTuple>(vertexAdaptor, info.elemVec[i], fp, info.bigEndian);
else
info.elemVec[i].SkipBinaryElementsInFile(fp);
}
else
{
for(int i = 0; i < info.elemVec.size();++i)
if (strcmp(info.elemVec[i].name.c_str(), "vertex") == 0)
ReadAsciiElement<VertexAdaptorTuple>(vertexAdaptor, info.elemVec[i], fp);
else
info.elemVec[i].SkipAsciiElementsInFile(fp);
}
return true;
}
/**
* Read a mesh from a PLY file.
*
* @tparam VertexAdaptorTuple Type that defines the management of the vertex data in memory
* @tparam FaceAdaptorTuple Type that defines the management of the face data in memory
*
* @param filename Path to the file to read
* @param vertexAdaptor std::tuple that defines how to manage the vertex data in memory
* @param faceAdaptor std::tuple that defines how to manage the face data in memory
*/
template <class VertexAdaptorTuple, class FaceAdaptorTuple>
bool OpenMesh(const char *filename, VertexAdaptorTuple vertexAdaptor, FaceAdaptorTuple faceAdaptor)
{
nanoply::Info info(filename);
if (info.errInfo != NNP_OK)
return false;
FILE *fp=fopen(filename,"rb");
if(!fp)
{
return false;
}
char buf[4096];
do
{
fgets(buf,4096,fp);
}
while(strncmp(buf,"end_header",strlen("end_header")) );
// Now start the real reading!
if (info.binary)
{
for(int i = 0; i < info.elemVec.size();++i)
{
if (strcmp(info.elemVec[i].name.c_str(), "vertex") == 0)
ReadBinaryElement<VertexAdaptorTuple>(vertexAdaptor, info.elemVec[i], fp, info.bigEndian);
else if (strcmp(info.elemVec[i].name.c_str(), "face") == 0)
ReadBinaryElement<FaceAdaptorTuple>(faceAdaptor, info.elemVec[i], fp, info.bigEndian);
else
info.elemVec[i].SkipBinaryElementsInFile(fp);
}
}
else
{
for(int i = 0; i < info.elemVec.size();++i)
{
if (strcmp(info.elemVec[i].name.c_str(), "vertex") == 0)
ReadAsciiElement<VertexAdaptorTuple>(vertexAdaptor, info.elemVec[i], fp);
else if (strcmp(info.elemVec[i].name.c_str(), "face") == 0)
ReadAsciiElement<FaceAdaptorTuple>(faceAdaptor, info.elemVec[i], fp);
else
info.elemVec[i].SkipAsciiElementsInFile(fp);
}
}
}
/**
* @cond HIDDEN_SYMBOLS
*/
template <class TupleType>
void WriteBinaryElement(TupleType &adaptor, PlyElement &elem, FILE *fp)
{
for (int i = 0 ; i < elem.cnt; i++)
{
for (int j = 0; j < elem.propVec.size(); j++)
{
PlyProperty& prop = elem.propVec[j];
TupleForEach(adaptor, fp, prop, false, SizeT<2>());
}
}
}
template <class TupleType>
void WriteAsciiElement(TupleType &adaptor, PlyElement &elem, FILE *fp)
{
for (int i = 0 ; i < elem.cnt; i++)
{
for (int j = 0; j < elem.propVec.size(); j++)
{
PlyProperty& prop = elem.propVec[j];
TupleForEach(adaptor, fp, prop, false, SizeT<3>());
}
fprintf(fp, "\n");
}
}
/**
* @endcond
*/
/**
* Save a point cloud from a PLY file.
*
* @tparam VertexAdaptorTuple Type that defines the management of the vertex data in memory
*
* @param filename Path to the file to save
* @param vertexAdaptor std::tuple that defines how to manage the vertex data in memory
* @param info Info to saved in the PLY header
*/
template <class VertexAdaptorTuple>
bool SavePointCloud(const char *filename, VertexAdaptorTuple vertexAdaptor, nanoply::Info &info)
{
FILE *fp=fopen(filename,"wb");
if(!fp)
{
return false;
}
fprintf(fp, "ply\n");
if (info.binary)
fprintf(fp, "format binary_little_endian 1.0\n");
else
fprintf(fp, "format ascii 1.0\n");
for (int i = 0; i < info.elemVec.size(); i++)
{
if (strcmp(info.elemVec[i].name.c_str(), "vertex") != 0)
{
info.elemVec[i].cnt = 0;
info.elemVec[i].WriteHeader(fp);
}
else
info.elemVec[i].WriteHeader(fp);
}
fprintf(fp, "end_header\n");
if (info.binary)
{
for(int i = 0; i < info.elemVec.size();++i)
if (strcmp(info.elemVec[i].name.c_str(), "vertex") == 0)
WriteBinaryElement<VertexAdaptorTuple>(vertexAdaptor, info.elemVec[i], fp);
}
else
{
for(int i = 0; i < info.elemVec.size();++i)
if (strcmp(info.elemVec[i].name.c_str(), "vertex") == 0)
WriteAsciiElement<VertexAdaptorTuple>(vertexAdaptor, info.elemVec[i], fp);
}
fclose(fp);
return true;
}
/**
* Save a mesh from a PLY file.
*
* @tparam VertexAdaptorTuple Type that defines the management of the vertex data in memory
* @tparam FaceAdaptorTuple Type that defines the management of the face data in memory
*
* @param filename Path to the file to save
* @param vertexAdaptor std::tuple that defines how to manage the vertex data in memory
* @param faceAdaptor std::tuple that defines how to manage the face data in memory
* @param info Info to saved in the PLY header
*/
template <class VertexAdaptorTuple, class FaceAdaptorTuple>
bool SaveMesh(const char *filename, VertexAdaptorTuple vertexAdaptor, FaceAdaptorTuple faceAdaptor, nanoply::Info &info)
{
FILE *fp=fopen(filename,"wb");
if(!fp)
{
return false;
}
fprintf(fp, "ply\n");
if (info.binary)
fprintf(fp, "format binary_little_endian 1.0\n");
else
fprintf(fp, "format ascii 1.0\n");
for (int i = 0; i < info.elemVec.size(); i++)
{
if (strcmp(info.elemVec[i].name.c_str(), "vertex") != 0 && strcmp(info.elemVec[i].name.c_str(), "face") != 0)
{
info.elemVec[i].cnt = 0;
info.elemVec[i].WriteHeader(fp);
}
else
info.elemVec[i].WriteHeader(fp);
}
fprintf(fp, "end_header\n");
if (info.binary)
{
for(int i = 0; i < info.elemVec.size();++i)
{
if (strcmp(info.elemVec[i].name.c_str(), "vertex") == 0)
WriteBinaryElement<VertexAdaptorTuple>(vertexAdaptor, info.elemVec[i], fp);
else if (strcmp(info.elemVec[i].name.c_str(), "face") == 0)
WriteBinaryElement<FaceAdaptorTuple>(faceAdaptor, info.elemVec[i], fp);
}
}
else
{
for(int i = 0; i < info.elemVec.size();++i)
{
if (strcmp(info.elemVec[i].name.c_str(), "vertex") == 0)
WriteAsciiElement<VertexAdaptorTuple>(vertexAdaptor, info.elemVec[i], fp);
else if (strcmp(info.elemVec[i].name.c_str(), "face") == 0)
WriteAsciiElement<FaceAdaptorTuple>(faceAdaptor, info.elemVec[i], fp);
}
}
fclose(fp);
return true;
}
/**
* @cond HIDDEN_SYMBOLS
*/
template < typename TupleType, size_t ActionType>
inline bool TupleForEach( TupleType &tuple, FILE *fp, PlyProperty &pro, bool bigEndian, SizeT<ActionType> a)
{
return TupleForEach( tuple, pro, fp, bigEndian, SizeT<std::tuple_size<TupleType>::value>(), a);
}
template < typename TupleType, size_t ActionType>
inline bool TupleForEach( TupleType &tuple, PlyProperty &pro, FILE *fp, bool bigEndian, SizeT<0> t, SizeT<ActionType> a) {return false; }
template < typename TupleType, size_t N, size_t ActionType>
inline bool TupleForEach( TupleType &tuple, PlyProperty &pro, FILE *fp, bool bigEndian, SizeT<N> t, SizeT<ActionType> a)
{
typename std::tuple_element<N - 1, TupleType>::type &dataDescr = std::get<N -1>(tuple);
if (ActionType == 0)
{
if (dataDescr.ReadElemBinary(fp, pro, bigEndian))
return true;
}
else if (ActionType == 1)
{
if (dataDescr.ReadElemAscii(fp, pro))
return true;
}
else if (ActionType == 2)
{
if (dataDescr.WriteElemBinary(fp, pro))
return true;
}
else if (ActionType == 3)
{
if (dataDescr.WriteElemAscii(fp, pro))
return true;
}
return TupleForEach( tuple, pro, fp, bigEndian, SizeT<N-1>(), a);
}
/**
* @endcond
*/
} // end namespace nanoply
#endif // NANOPLY_HPP