vcglib/wrap/nanoply/include/nanoply.hpp

2583 lines
79 KiB
C++

/****************************************************************************
* NanoPLY *
* NanoPLY is a C++11 header-only library to read and write PLY file *
* *
* Copyright(C) 2014-2015 *
* 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 <unordered_map>
#include <tuple>
#include <cassert>
#include <algorithm>
#include <stdexcept>
#include <cstdio>
#include <cmath>
#include <limits>
#include <fstream>
#include <sstream>
#include <stdint.h>
#include <string>
// 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
namespace nanoply
{
/** 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. */
NNP_INVALID_ELEMENT = 0x0008, /**< The file has an invalid element. */
NNP_INVALID_PROPERTY = 0x0010 /**< The file has an invalid property. */
} ErrorCode;
/** PLY Element Entity.
* Element that can be saved in PLY file.
*/
typedef enum NNP_ELEM
{
NNP_UNKNOWN_ELEM = 0x0, /**< Unknown element. */
NNP_VERTEX_ELEM = 0x1, /**< Vertex element. */
NNP_EDGE_ELEM = 0x2, /**< Edge element. */
NNP_FACE_ELEM = 0x4 /**< Face element. */
} PlyElemEntity;
/** PLY Entity.
* Property that can be saved in a PLY file.
*/
typedef enum NNP_ENTITY
{
NNP_UNKNOWN_ENTITY = 0x00000000, /**< Unknown property. */
NNP_PX = 0x00000001, /**< Position x cordinate. */
NNP_PY = 0x00000002, /**< Position y cordinate. */
NNP_PZ = 0x00000004, /**< Position z cordinate. */
NNP_PXYZ = 0x00000007, /**< Position (x, y, z). */
NNP_NX = 0x00000010, /**< Normal x component. */
NNP_NY = 0x00000020, /**< Normal y component. */
NNP_NZ = 0x00000040, /**< Normal z component. */
NNP_NXYZ = 0x00000070, /**< Normal (x, y, z). */
NNP_CR = 0x00000100, /**< Color red component. */
NNP_CG = 0x00000200, /**< Color green component. */
NNP_CB = 0x00000400, /**< Color blue component. */
NNP_CRGB = 0x00000700, /**< Color RGB. */
NNP_CA = 0x00000800, /**< Color alpha component. */
NNP_CRGBA = 0x00000F00, /**< Color RGBA. */
NNP_DENSITY = 0x00000008, /**< Density or Radius property. */
NNP_SCALE = 0x00000080, /**< Scale property. */
NNP_TEXTUREU = 0x00001000, /**< Texture coordinate u. */
NNP_TEXTUREV = 0x00002000, /**< Texture coordinate v. */
NNP_TEXTURE2D = 0x00003000, /**< Texture coordinate 2D. */
NNP_TEXTUREW = 0x00004000, /**< Texture coordinate w. */
NNP_TEXTURE3D = 0x00007000, /**< Texture coordinate 3D. */
NNP_TEXTUREINDEX = 0x00008000, /**< Texture index. */
NNP_QUALITY = 0x00010000, /**< Quality property. */
NNP_REFLECTANCE = 0x00020000, /**< Reflectance property. */
NNP_BITFLAG = 0x00040000, /**< Bit flags. */
NNP_K1 = 0x00080000, /**< Main curvaure value k1. */
NNP_K2 = 0x00100000, /**< Main curvaure value k2. */
NNP_KG = 0x00200000, /**< Gaussian curvature value. */
NNP_KH = 0x00400000, /**< Mean curvature value. */
NNP_K1DIR = 0x00800000, /**< Curvature direction k1. */
NNP_K2DIR = 0x01000000, /**< Curvature direction k2. */
NNP_EDGE_V1 = 0x02000000, /**< Index of the first vertex of the edge. */
NNP_EDGE_V2 = 0x04000000, /**< Index of the second vertex of the edge. */
NNP_FACE_VERTEX_LIST = 0x08000000, /**< List of vertex indices for the face. */
NNP_FACE_WEDGE_COLOR = 0x10000000, /**< List of colors for wedge. */
NNP_FACE_WEDGE_NORMAL = 0x20000000, /**< List of normals for wedge. */
NNP_FACE_WEDGE_TEX = 0x40000000 /**< List of texture coordinates for wedge. */
} PlyEntity;
/** PLY Type.
* Type of a PLY property.
*/
typedef enum NNP_PLYTYPE
{
NNP_UNKNOWN_TYPE = 0x000000, /**< Unknown type. */
NNP_FLOAT32 = 0x000001, /**< Float. */
NNP_FLOAT64 = 0x000002, /**< Double. */
NNP_INT8 = 0x000004, /**< Char. */
NNP_INT16 = 0x000008, /**< Short. */
NNP_INT32 = 0x000010, /**< Int. */
NNP_UINT8 = 0x000020, /**< Unsigned Char. */
NNP_UINT16 = 0x000040, /**< Unsigned Short. */
NNP_UINT32 = 0x000080, /**< Unsigned Int. */
NNP_LIST_UINT8_UINT32 = 0x000100, /**< List (size Unsigned Char) of Unsigned Int. */
NNP_LIST_INT8_UINT32 = 0x000200, /**< List (size Char) of Unsigned Int. */
NNP_LIST_UINT8_INT32 = 0x000400, /**< List (size Unsigned Char) of Int. */
NNP_LIST_INT8_INT32 = 0x000800, /**< List (size Char) of Int. */
NNP_LIST_UINT8_FLOAT32 = 0x001000, /**< List (size Unsigned Char) of Float. */
NNP_LIST_INT8_FLOAT32 = 0x002000, /**< List (size Char) of Float. */
NNP_LIST_UINT8_FLOAT64 = 0x004000, /**< List (size Unsigned Char) of Double. */
NNP_LIST_INT8_FLOAT64 = 0x008000, /**< List (size Char) of Double. */
NNP_LIST_UINT8_UINT8 = 0x010000, /**< List (size Unsigned Char) of Unsigned Char. */
NNP_LIST_INT8_UINT8 = 0x020000, /**< List (size Char) of Unsigned Char. */
NNP_LIST_UINT8_INT8 = 0x040000, /**< List (size Unsigned Char) of Char. */
NNP_LIST_INT8_INT8 = 0x080000, /**< List (size Char) of Char. */
NNP_LIST_UINT8_UINT16 = 0x100000, /**< List (size Unsigned Char) of Unsigned Short. */
NNP_LIST_INT8_UINT16 = 0x200000, /**< List (size Char) of Unsigned Short. */
NNP_LIST_UINT8_INT16 = 0x400000, /**< List (size Unsigned Char) of Short. */
NNP_LIST_INT8_INT16 = 0x800000 /**< List (size Char) of Short. */
} PlyType;
template <typename TEnum>
inline std::size_t hashEnum(const TEnum & value)
{
const std::hash<unsigned int> h{};
return h((unsigned int)value);
}
} // end namespace nanoply
// Cast of an enum to an unsigned int (maybe only for Android)
namespace std
{
template <> struct hash<nanoply::PlyElemEntity> { std::size_t operator () (const nanoply::PlyElemEntity & t) const { return nanoply::hashEnum(t); } };
template <> struct hash<nanoply::PlyEntity > { std::size_t operator () (const nanoply::PlyEntity & t) const { return nanoply::hashEnum(t); } };
template <> struct hash<nanoply::PlyType > { std::size_t operator () (const nanoply::PlyType & t) const { return nanoply::hashEnum(t); } };
} // end namespace std
namespace nanoply
{
/**
* @cond HIDDEN_SYMBOLS
*/
template < size_t T> struct SizeT {};
typedef std::vector<std::string> NameVector;
typedef std::unordered_map<PlyType, NameVector> TypeMap;
typedef std::unordered_map<PlyType, NameVector>::iterator TypeMapIterator;
typedef std::unordered_map<PlyEntity, NameVector> EntityMap;
typedef std::unordered_map<PlyEntity, NameVector>::iterator EntityMapIterator;
typedef std::unordered_map<PlyElemEntity, NameVector> ElementMap;
typedef std::unordered_map<PlyElemEntity, NameVector>::iterator ElementMapIterator;
/* Names used for the PlyType */
static TypeMap mapType({
{ PlyType::NNP_UNKNOWN_TYPE, NameVector({ "unknonw" }) },
{ PlyType::NNP_FLOAT32, NameVector({ "float", "float32" }) },
{ PlyType::NNP_FLOAT64, NameVector({ "double", "float64" }) },
{ PlyType::NNP_INT8, NameVector({ "char", "int8" }) },
{ PlyType::NNP_INT16, NameVector({ "short", "int16" }) },
{ PlyType::NNP_INT32, NameVector({ "int", "int32" }) },
{ PlyType::NNP_UINT8, NameVector({ "uchar", "uint8" }) },
{ PlyType::NNP_UINT16, NameVector({ "ushort", "uint16" }) },
{ PlyType::NNP_UINT32, NameVector({ "uint", "uint32" }) },
{ PlyType::NNP_LIST_UINT8_UINT32, NameVector({ "list uchar uint", "list uint8 uint32" }) },
{ PlyType::NNP_LIST_INT8_UINT32, NameVector({ "list char uint", "list int8 uint32" }) },
{ PlyType::NNP_LIST_UINT8_INT32, NameVector({ "list uchar int", "list uint8 int32" }) },
{ PlyType::NNP_LIST_INT8_INT32, NameVector({ "list char int", "list int8 int32" }) },
{ PlyType::NNP_LIST_UINT8_FLOAT32, NameVector({ "list uchar float", "list uint8 float32" }) },
{ PlyType::NNP_LIST_INT8_FLOAT32, NameVector({ "list char float", "list int8 float32" }) },
{ PlyType::NNP_LIST_UINT8_FLOAT64, NameVector({ "list uchar double", "list uint8 float64" }) },
{ PlyType::NNP_LIST_INT8_FLOAT64, NameVector({ "list char double", "list int8 float64" }) },
{ PlyType::NNP_LIST_UINT8_UINT8, NameVector({ "list uchar uchar", "list uint8 uint8" }) },
{ PlyType::NNP_LIST_INT8_UINT8, NameVector({ "list char uchar", "list int8 uint8" }) },
{ PlyType::NNP_LIST_UINT8_INT8, NameVector({ "list uchar char", "list uint8 int8" }) },
{ PlyType::NNP_LIST_INT8_INT8, NameVector({ "list char char", "list int8 int8" }) },
{ PlyType::NNP_LIST_UINT8_UINT16, NameVector({ "list uchar ushort", "list uint8 uint16" }) },
{ PlyType::NNP_LIST_INT8_UINT16, NameVector({ "list char ushort", "list int8 uint16" }) },
{ PlyType::NNP_LIST_UINT8_INT16, NameVector({ "list uchar short", "list uint8 int16" }) },
{ PlyType::NNP_LIST_INT8_INT16, NameVector({ "list char short", "list int8 int16" }) }
});
/* Names used for the PlyProperty */
static EntityMap mapProp({
{ PlyEntity::NNP_UNKNOWN_ENTITY, NameVector({ "unknonw" }) },
{ PlyEntity::NNP_PX, NameVector({ "x", "px", "posx" }) },
{ PlyEntity::NNP_PY, NameVector({ "y", "py", "posy" }) },
{ PlyEntity::NNP_PZ, NameVector({ "z", "pz", "posz" }) },
{ PlyEntity::NNP_PXYZ, NameVector({ "x y z", "px py pz", "posx posy posz" }) },
{ PlyEntity::NNP_NX, NameVector({ "nx", "normalx" }) },
{ PlyEntity::NNP_NY, NameVector({ "ny", "normaly" }) },
{ PlyEntity::NNP_NZ, NameVector({ "nz", "normalz" }) },
{ PlyEntity::NNP_NXYZ, NameVector({ "nx ny nz", "normalx normaly normalz" }) },
{ PlyEntity::NNP_CR, NameVector({ "red", "diffuse_red", "r", "diffuse_r" }) },
{ PlyEntity::NNP_CG, NameVector({ "green", "diffuse_green", "g", "diffuse_g" }) },
{ PlyEntity::NNP_CB, NameVector({ "blue", "diffuse_blue", "b", "diffuse_b" }) },
{ PlyEntity::NNP_CA, NameVector({ "alpha", "diffuse_alpha", "a", "diffuse_a" }) },
{ PlyEntity::NNP_CRGB, NameVector({ "rgb", "diffuse_rgb" }) },
{ PlyEntity::NNP_CRGBA, NameVector({ "rgba", "diffuse_rgba" }) },
{ PlyEntity::NNP_DENSITY, NameVector({ "radius", "density" }) },
{ PlyEntity::NNP_SCALE, NameVector({ "scale", "value" }) },
{ PlyEntity::NNP_TEXTUREU, NameVector({ "texture_u", "u", "s" }) },
{ PlyEntity::NNP_TEXTUREV, NameVector({ "texture_v", "v", "t" }) },
{ PlyEntity::NNP_TEXTURE2D, NameVector({ "texture_uv", "uv" }) },
{ PlyEntity::NNP_TEXTUREW, NameVector({ "texture_w", "w" }) },
{ PlyEntity::NNP_TEXTURE3D, NameVector({ "texture_uvw", "uvw" }) },
{ PlyEntity::NNP_TEXTUREINDEX, NameVector({ "texnumber", "texid" }) },
{ PlyEntity::NNP_QUALITY, NameVector({ "quality", "confidence" }) },
{ PlyEntity::NNP_REFLECTANCE, NameVector({ "reflectance" }) },
{ PlyEntity::NNP_BITFLAG, NameVector({ "flags" }) },
{ PlyEntity::NNP_K1, NameVector({ "k1" }) },
{ PlyEntity::NNP_K2, NameVector({ "k2" }) },
{ PlyEntity::NNP_KG, NameVector({ "k" }) },
{ PlyEntity::NNP_KH, NameVector({ "h" }) },
{ PlyEntity::NNP_K1DIR, NameVector({ "k1dir" }) },
{ PlyEntity::NNP_K2DIR, NameVector({ "k2dir" }) },
{ PlyEntity::NNP_EDGE_V1, NameVector({ "vertex1", "v1" }) },
{ PlyEntity::NNP_EDGE_V2, NameVector({ "vertex2", "v2" }) },
{ PlyEntity::NNP_FACE_VERTEX_LIST, NameVector({ "vertex_index", "vertex_indices" }) },
{ PlyEntity::NNP_FACE_WEDGE_COLOR, NameVector({ "color" }) },
{ PlyEntity::NNP_FACE_WEDGE_NORMAL, NameVector({ "normal" }) },
{ PlyEntity::NNP_FACE_WEDGE_TEX, NameVector({ "texcoord" }) }
});
/* Names used for the PlyElement */
static ElementMap mapElem({
{ PlyElemEntity::NNP_UNKNOWN_ELEM, NameVector({ "unknonw" }) },
{ PlyElemEntity::NNP_VERTEX_ELEM, NameVector({ "vertex" }) },
{ PlyElemEntity::NNP_EDGE_ELEM, NameVector({ "edge" }) },
{ PlyElemEntity::NNP_FACE_ELEM, NameVector({ "face" }) },
});
/* Returns the vector of possible name for the input PlyEntity */
static inline const NameVector& PlyPropertyName(PlyEntity ent)
{
static NameVector emptyVec;
if (mapProp.find(ent) != mapProp.end())
return mapProp[ent];
return emptyVec;
};
/* Returns the vector of possible name for the input PlyType */
static inline const NameVector& PlyTypeName(PlyType ent)
{
static NameVector emptyVec;
if (mapType.find(ent) != mapType.end())
return mapType[ent];
return emptyVec;
};
/* Returns the vector of possible name for the input PlyElement */
static inline const NameVector& PlyElementName(PlyElemEntity ent)
{
static NameVector emptyVec;
if (mapElem.find(ent) != mapElem.end())
return mapElem[ent];
return emptyVec;
};
/* Return 1 for little endian, 0 for big endian*/
inline int checkEndianness()
{
unsigned int x = 1;
char *c = (char*)&x;
return (int)*c;
};
/* Adjust the endianess of the input buffer*/
inline 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
*/
/** PLY File.
* Class to manage the read and write of a PLY file using a memory buffer.
*/
class PlyFile
{
private:
std::fstream fileStream; /**< Stream. */
char mode; /**< Mode of the stream (0 = read, 1 = write). */
int64_t bufferSize; /**< Size of the buffer. */
int64_t bufferOffset; /**< Offset for the next operation. */
int64_t maxSize; /**< Maximum size of the stream. */
char* buffer; /**< Buffer. */
public:
PlyFile();
~PlyFile();
/**
* Open the file in read mode.
*
* @param filename name of the file.
* @return If successful returns true. Otherwise, it returns false.
*/
inline bool OpenFileToRead(const std::string &filename);
/**
* Open the file in write mode.
*
* @param filename name of the file.
* @return If successful returns true. Otherwise, it returns false.
*/
inline bool OpenFileToWrite(const std::string &filename);
/**
* Read the next header line.
*
* @param line read line.
* @param last true if the line is the last of header.
* @return If successful returns true. Otherwise, it returns false.
*/
inline bool NextHeaderLine(std::string &line, bool &last);
/**
* Write the line in the header.
*
* @param line line to write.
* @return If successful returns true. Otherwise, it returns false.
*/
inline bool WriteHeaderLine(const std::string &line);
/**
* Read binary data from the file.
*
* @param dest pointer where to copy the data.
* @param nByte number of byte to read.
* @return If successful returns true. Otherwise, it returns false.
*/
inline bool ReadBinaryData(char * & dest, int nByte);
/**
* Read ASCII data from the file.
*
* @param dest reference to the container of the data.
* @return If successful returns true. Otherwise, it returns false.
*/
template <typename T>
inline bool ReadAsciiData(T &dest);
/**
* Write binary data in the file.
*
* @param src pointer to the data to write.
* @param nByte number of byte to write.
* @return If successful returns true. Otherwise, it returns false.
*/
inline bool WriteBinaryData(void *src, int nByte);
/**
* Write ASCII data in the file.
*
* @param src reference to the container of the data.
* @return If successful returns true. Otherwise, it returns false.
*/
template <typename T>
inline bool WriteAsciiData(const T &src);
/**
* Set the maximum size of the buffer.
*
* @return size size of the buffer.
*/
inline void SetBufferSize(int64_t size);
/**
* Force the write of the buffer in the file.
*/
inline void Flush();
};
inline PlyFile::PlyFile()
{
buffer = NULL;
maxSize = 10 * (1 << 20);
mode = -1;
}
inline PlyFile::~PlyFile()
{
Flush();
if (buffer != NULL)
delete[] buffer;
if (fileStream.is_open())
fileStream.close();
}
inline bool PlyFile::OpenFileToRead(const std::string& filename)
{
if (fileStream.is_open())
fileStream.close();
mode = 0;
fileStream.open(filename, std::fstream::in | std::fstream::binary);
if (fileStream.fail())
return false;
bufferOffset = 0;
return true;
}
inline bool PlyFile::OpenFileToWrite(const std::string& filename)
{
if (fileStream.is_open())
fileStream.close();
mode = 1;
fileStream.open(filename, std::fstream::out | std::fstream::binary);
if (fileStream.fail())
return false;
bufferOffset = 0;
//fileStream.setf(std::ios::fixed, std::ios::floatfield);
//fileStream.precision(7);
return true;
}
inline bool PlyFile::NextHeaderLine(std::string& line, bool& last)
{
if (mode != 0)
return false;
std::getline(fileStream, line);
std::transform(line.begin(), line.end(), line.begin(), ::tolower);
last = false;
if (line == "end_header")
last = true;
return true;
}
inline bool PlyFile::WriteHeaderLine(const std::string& line)
{
if (mode != 1)
return false;
fileStream << line;
return true;
}
inline bool PlyFile::ReadBinaryData(char * & dest, int nByte)
{
if (mode != 0)
return false;
if (buffer == NULL)
{
buffer = new char[maxSize];
fileStream.read(buffer, maxSize);
bufferSize = fileStream.gcount();
bufferOffset = 0;
}
else if (bufferOffset + nByte > bufferSize)
{
const size_t lastByte = bufferSize - bufferOffset;
memcpy(buffer, &buffer[bufferOffset], lastByte);
fileStream.read(&buffer[lastByte], maxSize - lastByte);
bufferSize = fileStream.gcount() + lastByte;
bufferOffset = 0;
}
//memcpy(dest, &buffer[bufferOffset], nByte);
dest = buffer + bufferOffset;
bufferOffset += nByte;
return true;
}
template <typename T>
inline bool PlyFile::ReadAsciiData(T& dest)
{
if (mode != 0)
return false;
fileStream >> dest;
return true;
}
inline bool PlyFile::WriteBinaryData(void* src, int nByte)
{
if (mode != 1)
return false;
if (buffer == NULL)
{
buffer = new char[maxSize];
bufferSize = maxSize;
bufferOffset = 0;
}
else if (bufferOffset + nByte > bufferSize)
{
fileStream.write(buffer, bufferOffset);
bufferOffset = 0;
}
memcpy(&buffer[bufferOffset], src, nByte);
bufferOffset += nByte;
return true;
}
template <typename T>
inline bool PlyFile::WriteAsciiData(const T& src)
{
if (mode != 1)
return false;
fileStream << src;
return true;
}
inline void PlyFile::SetBufferSize(int64_t size)
{
maxSize = size;
}
inline void PlyFile::Flush()
{
if (mode == 1)
fileStream.write(buffer, bufferOffset);
}
/** PLY Property.
* Define a PLY property (entity and type).
*/
class PlyProperty
{
public:
std::string name; /**< Property name. */
PlyType type; /**< Property type. */
PlyEntity elem; /**< Property entity. */
bool validToWrite; /**< Property validity (necessary to write the header). */
/**
* Constructor that sets the type and the entity of a standard PLY property.
*
* @param _t Property type.
* @param _e Property entity.
*/
inline PlyProperty(PlyType _t, PlyEntity _e) :type(_t), elem(_e), name(PlyPropertyName(_e)[0]), validToWrite(false){}
/**
* Constructor that sets the type, the entity and the name of a standard PLY property.
*
* @param _t Property type.
* @param _e Property entity.
* @param _n Property name.
*/
inline PlyProperty(PlyType _t, PlyEntity _e, std::string _n) :type(_t), elem(_e), name(_n), validToWrite(false){}
/**
* Constructor that sets the type and the name of a custom PLY property.
*
* @param _t Property type.
* @param _n Property name.
*/
inline PlyProperty(PlyType _t, std::string _n) :type(_t), elem(PlyEntity::NNP_UNKNOWN_ENTITY), name(_n), validToWrite(false){}
/**
* Get the description string of the property entity.
*
* @return Description string of the property entity.
*/
inline const char* EntityStr();
/**
* Get the name of the property entity.
*
* @return Name of the property entity.
*/
inline const char* EntityName();
/**
* Get the description string of the property type.
*
* @return Description string of the property type.
*/
inline const char* TypeStr();
/**
* Get the size in byte of the property type.
*
* @return Size in byte of the property type.
*/
inline int TypeSize();
/**
* Get the number of component of the property entity.
*
* @return Number of component.
*/
inline int CountValue();
/**
* Check if the property type is signed or unsigned.
*
* @return true = signed, false = unsigned.
*/
inline bool IsSigned();
/**
* Skip the property in an Ascii file.
*
* @param file Opened file.
* @return If successful returns true. Otherwise, it returns false.
*/
inline bool SkipAsciiPropertyInFile(PlyFile &file);
/**
* Skip the property in a binary file.
*
* @param file Opened file.
* @return If successful returns true. Otherwise, it returns false.
*/
inline bool SkipBinaryPropertyInFile(PlyFile &file);
/**
* Write the property string in the header of the PLY file.
*
* @param file Opened file.
* @return If successful returns true. Otherwise, it returns false.
*/
inline bool WriteHeader(PlyFile &file);
};
inline 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_TEXTUREU: return "NNP_TEXTUREU ";
case NNP_TEXTUREV: return "NNP_TEXTUREV ";
case NNP_TEXTURE2D: return "NNP_TEXTURE2D ";
case NNP_TEXTUREW: return "NNP_TEXTUREW ";
case NNP_TEXTURE3D: return "NNP_TEXTURE3D ";
case NNP_TEXTUREINDEX: return "NNP_TEXTUREINDEX ";
case NNP_BITFLAG: return "NNP_BITFLAG ";
case NNP_K1: return "NNP_K1 ";
case NNP_K2: return "NNP_K2 ";
case NNP_KG: return "NNP_K ";
case NNP_KH: return "NNP_H ";
case NNP_K1DIR: return "NNP_K1DIR ";
case NNP_K2DIR: return "NNP_K2DIR ";
case NNP_EDGE_V1: return "NNP_EDGE_V1 ";
case NNP_EDGE_V2: return "NNP_EDGE_V2 ";
case NNP_FACE_VERTEX_LIST: return "NNP_FACE_VERTEX_LIST ";
case NNP_FACE_WEDGE_COLOR: return "NNP_FACE_WEDGE_COLOR ";
case NNP_FACE_WEDGE_NORMAL: return "NNP_FACE_WEDGE_NORMAL";
case NNP_FACE_WEDGE_TEX: return "NNP_FACE_WEDGE_TEX ";
default: assert(0);
break;
}
return 0;
}
inline const char* PlyProperty::EntityName()
{
if (this->elem == PlyEntity::NNP_UNKNOWN_ENTITY)
return (*this).name.c_str();
#ifdef USE_NOSTANDARDPLY_OUTPUT
if (this->elem == PlyEntity::NNP_SCALE)
return PlyPropertyName(this->elem)[1].c_str();
if (this->elem == PlyEntity::NNP_QUALITY)
return PlyPropertyName(this->elem)[1].c_str();
if (this->elem == PlyEntity::NNP_CR)
return PlyPropertyName(this->elem)[1].c_str();
if (this->elem == PlyEntity::NNP_CB)
return PlyPropertyName(this->elem)[1].c_str();
if (this->elem == PlyEntity::NNP_CG)
return PlyPropertyName(this->elem)[1].c_str();
if (this->elem == PlyEntity::NNP_CA)
return PlyPropertyName(this->elem)[1].c_str();
#endif
const std::vector<std::string>& vect = PlyPropertyName(this->elem);
if (vect.size() > 0)
return vect[0].c_str();
assert(0);
return 0;
}
inline 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 ";
case NNP_LIST_UINT8_FLOAT32: return "NNP_LIST_UINT8_FLOAT32 ";
case NNP_LIST_INT8_FLOAT32: return "NNP_LIST_INT8_FLOAT32 ";
case NNP_LIST_UINT8_FLOAT64: return "NNP_LIST_UINT8_FLOAT64 ";
case NNP_LIST_INT8_FLOAT64: return "NNP_LIST_INT8_FLOAT64 ";
case NNP_LIST_UINT8_UINT8: return "NNP_LIST_UINT8_UINT8 ";
case NNP_LIST_INT8_UINT8: return "NNP_LIST_INT8_UINT8 ";
case NNP_LIST_UINT8_INT8: return "NNP_LIST_UINT8_INT8 ";
case NNP_LIST_INT8_INT8: return "NNP_LIST_INT8_INT8 ";
case NNP_LIST_UINT8_UINT16: return "NNP_LIST_UINT8_UINT16 ";
case NNP_LIST_INT8_UINT16: return "NNP_LIST_INT8_UINT16 ";
case NNP_LIST_UINT8_INT16: return "NNP_LIST_UINT8_INT16 ";
case NNP_LIST_INT8_INT16: return "NNP_LIST_INT8_INT16 ";
default: assert(0);
break;
}
return 0;
}
inline int PlyProperty::TypeSize()
{
switch (this->type)
{
case NNP_UNKNOWN_TYPE:
return 0;
case NNP_INT8:
case NNP_UINT8:
return sizeof(char);
case NNP_INT16:
case NNP_UINT16:
return sizeof(short);
case NNP_FLOAT32:
case NNP_INT32:
case NNP_UINT32:
return sizeof(int);
case NNP_FLOAT64:
return sizeof(double);
case NNP_LIST_UINT8_UINT32:
case NNP_LIST_INT8_UINT32:
case NNP_LIST_UINT8_INT32:
case NNP_LIST_INT8_INT32:
return sizeof(int);
case NNP_LIST_UINT8_UINT16:
case NNP_LIST_INT8_UINT16:
case NNP_LIST_UINT8_INT16:
case NNP_LIST_INT8_INT16:
return sizeof(short);
case NNP_LIST_UINT8_UINT8:
case NNP_LIST_INT8_UINT8:
case NNP_LIST_UINT8_INT8:
case NNP_LIST_INT8_INT8:
return sizeof(char);
case NNP_LIST_UINT8_FLOAT32:
case NNP_LIST_INT8_FLOAT32:
return sizeof(float);
case NNP_LIST_UINT8_FLOAT64:
case NNP_LIST_INT8_FLOAT64:
return sizeof(double);
default: assert(0);
break;
}
return 0;
}
inline bool PlyProperty::IsSigned()
{
switch (this->type)
{
case NNP_INT8:
case NNP_INT16:
case NNP_INT32:
case NNP_FLOAT32:
case NNP_FLOAT64:
case NNP_LIST_INT8_UINT32:
case NNP_LIST_INT8_INT32:
case NNP_LIST_INT8_UINT16:
case NNP_LIST_INT8_INT16:
case NNP_LIST_INT8_UINT8:
case NNP_LIST_INT8_INT8:
case NNP_LIST_INT8_FLOAT32:
case NNP_LIST_INT8_FLOAT64:
return true;
case NNP_UINT8:
case NNP_UINT16:
case NNP_UINT32:
case NNP_LIST_UINT8_UINT32:
case NNP_LIST_UINT8_INT32:
case NNP_LIST_UINT8_UINT16:
case NNP_LIST_UINT8_INT16:
case NNP_LIST_UINT8_UINT8:
case NNP_LIST_UINT8_INT8:
case NNP_LIST_UINT8_FLOAT32:
case NNP_LIST_UINT8_FLOAT64:
return false;
default:
return false;
}
}
inline int PlyProperty::CountValue()
{
if (this->elem == NNP_CRGB || this->elem == NNP_NXYZ || this->elem == NNP_PXYZ || this->elem == NNP_TEXTURE3D)
return 3;
else if (this->elem == NNP_CRGBA)
return 4;
else if (this->elem == NNP_TEXTURE2D)
return 2;
return 1;
}
inline bool PlyProperty::SkipAsciiPropertyInFile(PlyFile &file)
{
int count = CountValue();
if (this->type >= NNP_LIST_UINT8_UINT32)
file.ReadAsciiData(count);
switch (type)
{
case NNP_INT8:
case NNP_INT16:
case NNP_INT32:
case NNP_LIST_UINT8_INT32:
case NNP_LIST_INT8_INT32:
case NNP_LIST_UINT8_INT16:
case NNP_LIST_INT8_INT16:
case NNP_LIST_UINT8_INT8:
case NNP_LIST_INT8_INT8:
{
int* temp = new int[count];
for (int i = 0; i < count; i++)
file.ReadAsciiData(temp[i]);
delete[] temp;
break;
}
case NNP_UINT8:
case NNP_UINT16:
case NNP_UINT32:
case NNP_LIST_UINT8_UINT32:
case NNP_LIST_INT8_UINT32:
case NNP_LIST_UINT8_UINT16:
case NNP_LIST_INT8_UINT16:
case NNP_LIST_UINT8_UINT8:
case NNP_LIST_INT8_UINT8:
{
unsigned int* temp = new unsigned int[count];
for (int i = 0; i < count; i++)
file.ReadAsciiData(temp[i]);
delete[] temp;
break;
}
case NNP_FLOAT32:
case NNP_LIST_UINT8_FLOAT32:
case NNP_LIST_INT8_FLOAT32:
{
float* temp = new float[count];
for (int i = 0; i < count; i++)
file.ReadAsciiData(temp[i]);
delete[] temp;
break;
}
case NNP_FLOAT64:
case NNP_LIST_UINT8_FLOAT64:
case NNP_LIST_INT8_FLOAT64:
{
double* temp = new double[count];
for (int i = 0; i < count; i++)
file.ReadAsciiData(temp[i]);
delete[] temp;
break;
}
}
return true;
}
inline bool PlyProperty::SkipBinaryPropertyInFile(PlyFile& file)
{
char * temp = nullptr;
int count = CountValue();
if (this->type >= NNP_LIST_UINT8_UINT32)
{
int size;
if (this->IsSigned())
{
file.ReadBinaryData(temp, sizeof(char));
size = this->TypeSize() * int(*(reinterpret_cast<unsigned char *>(temp)));
}
else
{
file.ReadBinaryData(temp, sizeof(char));
size = this->TypeSize() * int(*(reinterpret_cast<unsigned char *>(temp)));
}
file.ReadBinaryData(temp, size);
}
else
{
int size = this->TypeSize() * count;
file.ReadBinaryData(temp, size);
}
return true;
}
inline bool PlyProperty::WriteHeader(PlyFile& file)
{
if (!validToWrite)
return true;
std::string name, type;
type = PlyTypeName(this->type)[0];
name = this->EntityName();
std::vector<std::string> v;
switch (this->elem)
{
case NNP_PXYZ:
{
v.push_back(PlyPropertyName(NNP_PX)[0]);
v.push_back(PlyPropertyName(NNP_PY)[0]);
v.push_back(PlyPropertyName(NNP_PZ)[0]);
break;
}
case NNP_NXYZ:
{
v.push_back(PlyPropertyName(NNP_NX)[0]);
v.push_back(PlyPropertyName(NNP_NY)[0]);
v.push_back(PlyPropertyName(NNP_NZ)[0]);
break;
}
case NNP_CRGB:
{
v.push_back(PlyPropertyName(NNP_CR)[0]);
v.push_back(PlyPropertyName(NNP_CG)[0]);
v.push_back(PlyPropertyName(NNP_CB)[0]);
break;
}
case NNP_CRGBA:
{
v.push_back(PlyPropertyName(NNP_CR)[0]);
v.push_back(PlyPropertyName(NNP_CG)[0]);
v.push_back(PlyPropertyName(NNP_CB)[0]);
v.push_back(PlyPropertyName(NNP_CA)[0]);
break;
}
case NNP_TEXTURE2D:
{
v.push_back(PlyPropertyName(NNP_TEXTUREU)[0]);
v.push_back(PlyPropertyName(NNP_TEXTUREV)[0]);
break;
}
case NNP_TEXTURE3D:
{
v.push_back(PlyPropertyName(NNP_TEXTUREU)[0]);
v.push_back(PlyPropertyName(NNP_TEXTUREV)[0]);
v.push_back(PlyPropertyName(NNP_TEXTUREW)[0]);
break;
}
default:
v.push_back(name);
}
for (int i = 0; i < v.size(); i++)
{
std::stringstream s;
s << "property " << type << " " << v[i] << "\n";
if (!file.WriteHeaderLine(s.str()))
return false;
}
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", "face", ect.). */
PlyElemEntity plyElem; /**< Ply element entity. */
size_t cnt; /**< Number of instances of the elment in the PLY file. */
std::vector<PlyProperty> propVec; /**< Collection of properties that define the element. */
bool validToWrite; /**< Element validity (necessary to write the header). */
/**
* Default Constructor
*/
inline PlyElement() :validToWrite(false){};
/**
* 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.
*/
inline PlyElement(std::string& _name, std::vector<PlyProperty> &prop, size_t nElem) :name(_name), cnt(nElem), propVec(prop), plyElem(PlyElemEntity::NNP_UNKNOWN_ELEM), validToWrite(false){};
/**
* Constructor that sets the entity, the properties and the number of instances of the element.
*
* @param ent Element entity.
* @param prop Vector of properties.
* @param nElem Number of instances.
*/
inline PlyElement(PlyElemEntity ent, std::vector<PlyProperty> &prop, size_t nElem) :name(PlyElementName(ent)[0]), cnt(nElem), propVec(prop), plyElem(ent), validToWrite(false){};
/**
* 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.
*/
inline bool AddProperty(std::string &line);
/**
* Initialize an element from the header line.
*
* @param elemStr String with the element definition.
* @param propStr Strings with the property definition.
* @return If successful returns true. Otherwise, it returns false.
*/
inline bool InitFromHeader(std::string &elemStr, std::vector<std::string> &propStr);
/**
* Write the element descriport in the file header.
*
* @param file Ply file.
* @return If successful returns true. Otherwise, it returns false.
*/
inline bool WriteHeader(PlyFile &file);
/**
* Skip the element in an Ascii file.
*
* @param file Ply file
* @return If successful returns true. Otherwise, it returns false.
*/
inline bool SkipAsciiElementsInFile(PlyFile &file);
/**
* Skip the element in a binary file.
*
* @param file Ply file.
* @return If successful returns true. Otherwise, it returns false.
*/
inline bool SkipBinaryElementsInFile(PlyFile &file);
/**
* 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.
*/
inline bool Contains(NNP_ENTITY entity);
};
inline bool PlyElement::InitFromHeader(std::string &elemStr, std::vector<std::string> &propStr)
{
char* token;
char* tempStr = &elemStr[0];
token = strtok(tempStr, " \t");
if (strstr(token, "element") == NULL)
return false;
token = strtok(0, " \t\n");
name = std::string(token);
plyElem = PlyElemEntity::NNP_UNKNOWN_ELEM;
ElementMapIterator iter = mapElem.begin();
bool found = false;
while (iter != mapElem.end())
{
NameVector& v = (*iter).second;
for (size_t i = 0; i < v.size(); i++)
{
if (v[i] == name)
{
found = true;
break;
}
}
if (found)
{
plyElem = (*iter).first;
break;
}
iter++;
}
token = strtok(0, " \t\n");
cnt = atoi(token);
for (size_t i = 0; i < propStr.size(); i++)
if (!AddProperty(propStr[i]))
return false;
unsigned int mask = 0;
for (size_t i = 0; i < propVec.size(); i++)
mask |= propVec[i].elem;
std::vector<PlyProperty> compactPropVec;
for (size_t 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;
case NNP_TEXTUREU:
if (((mask & NNP_TEXTURE2D) != NNP_TEXTURE2D) & ((mask & NNP_TEXTURE3D) != NNP_TEXTURE3D))
compactPropVec.push_back(propVec[i]);
break;
case NNP_TEXTUREV:
if (((mask & NNP_TEXTURE2D) != NNP_TEXTURE2D) & ((mask & NNP_TEXTURE3D) != NNP_TEXTURE3D))
compactPropVec.push_back(propVec[i]);
else if (((mask & NNP_TEXTURE2D) == NNP_TEXTURE2D) & ((mask & NNP_TEXTURE3D) != NNP_TEXTURE3D))
compactPropVec.push_back(PlyProperty(propVec[i].type, NNP_TEXTURE2D));
break;
case NNP_TEXTUREW:
if (((mask & NNP_TEXTURE2D) != NNP_TEXTURE2D) & ((mask & NNP_TEXTURE3D) != NNP_TEXTURE3D))
compactPropVec.push_back(propVec[i]);
else if ((mask & NNP_TEXTURE3D) == NNP_TEXTURE3D)
compactPropVec.push_back(PlyProperty(propVec[i].type, NNP_TEXTURE3D));
break;
default:
compactPropVec.push_back(propVec[i]);
break;
}
}
propVec.clear();
propVec = compactPropVec;
return true;
}
inline bool PlyElement::WriteHeader(PlyFile &file)
{
if (!validToWrite || cnt == 0)
return true;
bool ok = true;
std::stringstream temp;
temp << "element " << name << " " << cnt << "\n";
if (file.WriteHeaderLine(temp.str()))
{
for (int i = 0; i < propVec.size(); i++)
ok = propVec[i].WriteHeader(file);
}
else
return false;
return ok;
}
inline bool PlyElement::SkipAsciiElementsInFile(PlyFile &file)
{
for (int i = 0; i < this->cnt; ++i)
for (int j = 0; j < this->propVec.size(); ++j)
this->propVec[j].SkipAsciiPropertyInFile(file);
return true;
}
inline bool PlyElement::SkipBinaryElementsInFile(PlyFile &file)
{
for (int i = 0; i < this->cnt; ++i)
for (int j = 0; j < this->propVec.size(); ++j)
this->propVec[j].SkipBinaryPropertyInFile(file);
return true;
}
inline bool PlyElement::AddProperty(std::string &line)
{
char* token;
char* tempStr = &line[0];
token = strtok(tempStr, " \t");
if (strstr(token, "property") == NULL)
return false;
char* typeStr = strtok(0, " \t\n");
char *ty1, *ty2;
std::string type;
type.append(typeStr);
if (strcmp(typeStr, "list") == 0)
{
ty1 = strtok(0, " \t\n");
ty2 = strtok(0, " \t\n");
type.append(" ");
type.append(ty1);
type.append(" ");
type.append(ty2);
}
PlyType plyType = PlyType::NNP_UNKNOWN_TYPE;
TypeMapIterator iterType = mapType.begin();
bool found = false;
while (iterType != mapType.end())
{
NameVector& v = (*iterType).second;
for (size_t i = 0; i < v.size(); i++)
{
if (v[i] == type)
{
found = true;
break;
}
}
if (found)
{
plyType = (*iterType).first;
break;
}
iterType++;
}
if (plyType == PlyType::NNP_UNKNOWN_TYPE)
return false;
char* nameStr = strtok(0, " \t\n");
PlyEntity plyEntity = PlyEntity::NNP_UNKNOWN_ENTITY;
EntityMapIterator iterEnt = mapProp.begin();
found = false;
while (iterEnt != mapProp.end())
{
NameVector& v = (*iterEnt).second;
for (size_t i = 0; i < v.size(); i++)
{
if (v[i] == nameStr)
{
found = true;
break;
}
}
if (found)
{
plyEntity = (*iterEnt).first;
break;
}
iterEnt++;
}
if (plyEntity != PlyEntity::NNP_UNKNOWN_ENTITY)
propVec.push_back(PlyProperty(plyType, plyEntity, nameStr));
else
propVec.push_back(PlyProperty(plyType, nameStr));
return true;
}
inline 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, ASCII = false). */
std::vector<PlyElement> elemVec; /**< Elements defined in the header. */
bool bigEndian; /**< Endianess of the binary file. */
std::string filename; /**< Filename. */
std::vector<std::string> textureFile; /**< Texture file names. */
/**
* Default Constructor
*/
inline Info();
/**
* Constructor that reads the header info from a file.
*
* @param filename Path of the file to read.
*/
inline Info(const std::string& filename);
/**
* Load the ply info from the header of the input filename.
*
* @param filename Path of the file to read.
* @return If successful returns true. Otherwise, it returns false.
*/
inline bool LoadHeader(const std::string& filename);
/**
* Write the ply info in the header of the input file.
*
* @param file File to write.
* @return If successful returns true. Otherwise, it returns false.
*/
inline bool WriteHeader(PlyFile& file);
/**
* Add the ply element to the header.
*
* @param pe Ply element to write in the header.
*/
inline void AddPlyElement(PlyElement& pe);
/**
* Clear the error code.
*/
inline void Clear() { errInfo = NNP_OK; }
/**
* Return the number of instances of the element with the input name
*
* @param name Name of the element.
* @return The number of instances
*/
inline size_t GetElementCount(std::string& name);
/**
* Return the number of instances of the element with the input element type
*
* @param e Element type.
* @return The number of instances
*/
inline size_t GetElementCount(PlyElemEntity e);
/**
* Return the number of vertex instances
*
* @return The number of vertex instances
*/
inline size_t GetVertexCount();
/**
* Return the number of face instances
*
* @return The number of face instances
*/
inline size_t GetFaceCount();
/**
* Return the number of edge instances
*
* @return The number of edge instances
*/
inline size_t GetEdgeCount();
/**
* Return a reference to the element with a specific name
*
* @param name Name of the element.
* @return The reference to the element
*/
inline PlyElement* GetElement(std::string& name);
/**
* Return a reference to the element with a specific element type
*
* @param e Element type.
* @return The reference to the element
*/
inline PlyElement* GetElement(PlyElemEntity e);
/**
* Return a reference to the vertex element
*
* @return The reference to the vertex element
*/
inline PlyElement* GetVertexElement();
/**
* Return a reference to the face element
*
* @return The reference to the face element
*/
inline PlyElement* GetFaceElement();
/**
* Return a reference to the edge element
*
* @return The reference to the edge element
*/
inline PlyElement* GetEdgeElement();
};
inline Info::Info()
{
this->binary = true;
this->bigEndian = true;
this->Clear();
}
inline Info::Info(const std::string& filename)
{
this->LoadHeader(filename);
}
inline bool Info::LoadHeader(const std::string& filename)
{
this->filename = filename;
this->errInfo = NNP_OK;
std::ifstream input(filename, std::ios::binary);
if (!input.good())
{
this->errInfo = NNP_UNABLE_TO_OPEN;
input.close();
return false;
}
std::string buffer;
std::getline(input, buffer);
std::transform(buffer.begin(), buffer.end(), buffer.begin(), ::tolower);
if (buffer != "ply")
{
this->errInfo = NNP_MISSING_HEADER;
input.close();
return false;
}
std::getline(input, buffer);
std::transform(buffer.begin(), buffer.end(), buffer.begin(), ::tolower);
std::size_t pos = buffer.find("format");
if (pos == std::string::npos)
{
this->errInfo = NNP_MISSING_FORMAT;
input.close();
return false;
}
if (buffer.find("binary_lit") != std::string::npos)
{
this->binary = true;
this->bigEndian = false;
}
else if (buffer.find("binary_big") != std::string::npos)
{
this->binary = true;
this->bigEndian = true;
}
else if (buffer.find("ascii") != std::string::npos)
{
this->binary = false;
this->bigEndian = false;
}
else
{
this->errInfo = NNP_MISSING_FORMAT;
return false;
}
std::getline(input, buffer);
std::string lowBuffer;
lowBuffer.resize(buffer.size());
std::transform(buffer.begin(), buffer.end(), lowBuffer.begin(), ::tolower);
while (lowBuffer != "end_header")
{
if (lowBuffer.find("element") != std::string::npos)
{
std::string elemStr = lowBuffer;
std::vector<std::string> propStr;
do
{
std::getline(input, buffer);
lowBuffer.clear();
lowBuffer.resize(buffer.size());
std::transform(buffer.begin(), buffer.end(), lowBuffer.begin(), ::tolower);
pos = lowBuffer.find("property");
if (pos != std::string::npos)
propStr.push_back(lowBuffer);
} while (pos != std::string::npos);
PlyElement pe;
if (!pe.InitFromHeader(elemStr, propStr))
{
this->errInfo = NNP_INVALID_ELEMENT;
input.close();
return false;
}
elemVec.push_back(pe);
}
else
{
if (lowBuffer.find("comment texture") != std::string::npos)
textureFile.push_back(buffer.substr(buffer.find(" ", 10) + 1));
std::getline(input, buffer);
lowBuffer.clear();
lowBuffer.resize(buffer.size());
std::transform(buffer.begin(), buffer.end(), lowBuffer.begin(), ::tolower);
}
}
input.close();
return true;
}
inline bool Info::WriteHeader(PlyFile& file)
{
bool ok = true;
ok = file.WriteHeaderLine(std::string("ply\n"));
if (this->binary)
ok = file.WriteHeaderLine(std::string("format binary_little_endian 1.0\n"));
else
ok = file.WriteHeaderLine(std::string("format ascii 1.0\n"));
ok = file.WriteHeaderLine(std::string("comment nanoply generated\n"));
for (int i = 0; i < this->textureFile.size(); i++)
ok = file.WriteHeaderLine(std::string("comment TextureFile ") + this->textureFile[i] + "\n");
for (int i = 0; i < this->elemVec.size(); i++)
ok = this->elemVec[i].WriteHeader(file);
ok = file.WriteHeaderLine(std::string("end_header\n"));
return ok;
}
inline void Info::AddPlyElement(PlyElement& pe)
{
elemVec.push_back(pe);
}
inline size_t Info::GetElementCount(std::string& name)
{
PlyElement* pe = GetElement(name);
if (pe != NULL)
return pe->cnt;
return -1;
}
inline size_t Info::GetElementCount(PlyElemEntity e)
{
PlyElement* pe = GetElement(e);
if (pe != NULL)
return pe->cnt;
return -1;
}
inline size_t Info::GetVertexCount()
{
return GetElementCount(PlyElemEntity::NNP_VERTEX_ELEM);
}
inline size_t Info::GetFaceCount()
{
return GetElementCount(PlyElemEntity::NNP_FACE_ELEM);
}
inline size_t Info::GetEdgeCount()
{
return GetElementCount(PlyElemEntity::NNP_EDGE_ELEM);
}
inline PlyElement* Info::GetElement(std::string& name)
{
for (int i = 0; i < elemVec.size(); i++)
{
if (elemVec[i].name == name)
return &elemVec[i];
}
return NULL;
}
inline PlyElement* Info::GetElement(PlyElemEntity e)
{
for (int i = 0; i < elemVec.size(); i++)
{
if (elemVec[i].plyElem == e)
return &elemVec[i];
}
return NULL;
}
inline PlyElement* Info::GetVertexElement()
{
return GetElement(PlyElemEntity::NNP_VERTEX_ELEM);
}
inline PlyElement* Info::GetFaceElement()
{
return GetElement(PlyElemEntity::NNP_FACE_ELEM);
}
inline PlyElement* Info::GetEdgeElement()
{
return GetElement(PlyElemEntity::NNP_EDGE_ELEM);
}
/** Abstract class for the descriptor of a Ply propertie.
* The class defines how a PlyProperty is saved in memory.
*/
class DescriptorInterface
{
public:
int64_t curPos; /**< Position of the next property to read or to write. */
void *base; /**< Pointer to the memory location that contains the data of the property. */
PlyEntity elem; /**< Ply entity managed by the descriptor. */
std::string name; /**< Name of the PlyProperty*/
/**
* 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.
*/
inline DescriptorInterface(PlyEntity _e, void *_b) :curPos(0), elem(_e), base(_b), name(PlyPropertyName(_e)[0]){};
/**
* Constructor of the descriptor.
*
* @param _s Name of the PlyProperty.
* @param _b Pointer to the memory location that contains the data of the property.
*/
inline DescriptorInterface(std::string& _s, void *_b) :curPos(0), elem(PlyEntity::NNP_UNKNOWN_ENTITY), name(_s), base(_b){};
/**
* Restart the descriptor.
*/
virtual void Restart() = 0;
/**
* Read the property data from the binary file.
*
* @param file Input file.
* @param prop PLY property to read from the file.
* @param fixEndian If true the method adjust the endianess of the data.
* @return If successful returns true. Otherwise, it returns false.
*/
virtual bool ReadElemBinary(PlyFile &file, PlyProperty &prop, bool fixEndian) = 0;
/**
* Read the property data from the ascii file.
*
* @param file Input file.
* @param prop PLY property to read from the file.
* @return If successful returns true. Otherwise, it returns false.
*/
virtual bool ReadElemAscii(PlyFile &file, PlyProperty &prop) = 0;
/**
* Write the property data in the binary file.
*
* @param file Input file.
* @param prop PLY property to write in the file.
* @param fixEndian If true the method adjust the endianess of the data.
* @return If successful returns true. Otherwise, it returns false.
*/
virtual bool WriteElemBinary(PlyFile &file, PlyProperty &prop, bool fixEndian) = 0;
/**
* Write the property data in the ascii file.
*
* @param file Input file.
* @param prop PLY property to write in the file.
* @return If successful returns true. Otherwise, it returns false.
*/
virtual bool WriteElemAscii(PlyFile &file, PlyProperty &prop) = 0;
};
/** Memory descriptor of a Ply element.
* The class defines how a PlyElement is saved in memory.
*/
class ElementDescriptor
{
public:
typedef std::vector<nanoply::DescriptorInterface*> PropertyDescriptor;
std::string name; /**< Name of the Ply element. */
PlyElemEntity elem; /**< PLY Element Entity. */
PropertyDescriptor dataDescriptor; /**< Vector of property descriptor. */
/**
* Constructor of the Ply element descriptor.
*
* @param _e Ply Element entity managed by the descriptor.
*/
inline ElementDescriptor(PlyElemEntity _e) : elem(_e), name(PlyElementName(_e)[0]){};
/**
* Constructor of the Ply element descriptor.
*
* @param _s Name of the Ply element managed by the descriptor.
*/
inline ElementDescriptor(std::string &_s) : elem(PlyElemEntity::NNP_UNKNOWN_ELEM), name(_s){};
/**
* Read all the properties of the element from the binary file.
*
* @param file Input file.
* @param elem PLY element to read from the file.
* @param fixEndian If true the method adjust the endianess of the data.
* @return If successful returns true. Otherwise, it returns false.
*/
inline bool ReadElemBinary(PlyFile &file, PlyElement &elem, bool fixEndian);
/**
* Read all the property data of the element from the ascii file.
*
* @param file Input file.
* @param elem PLY element to read from the file.
* @return If successful returns true. Otherwise, it returns false.
*/
inline bool ReadElemAscii(PlyFile &file, PlyElement &elem);
/**
* Write all the property data of the element in the binary file.
*
* @param file Input file.
* @param elem PLY element to write from the file.
* @param fixEndian If true the method adjust the endianess of the data.
* @return If successful returns true. Otherwise, it returns false.
*/
inline bool WriteElemBinary(PlyFile &file, PlyElement &elem, bool fixEndian);
/**
* Write all the property data of the element in the ascii file.
*
* @param file Input file.
* @param elem PLY element to write from the file.
* @return If successful returns true. Otherwise, it returns false.
*/
inline bool WriteElemAscii(PlyFile &file, PlyElement &elem);
/**
* Check if the properties defined in input element have a proper data descriport to write in the file.
* It sets the variable "validToWrite" for all the Ply properties with a data descriport.
*
* @param elem PLY element to write from the file.
*/
inline void CheckDescriptor(PlyElement &elem);
private:
inline void ExtractDescriptor(PropertyDescriptor &descr, PlyElement &elem);
};
inline void ElementDescriptor::ExtractDescriptor(PropertyDescriptor& descr, PlyElement &elem)
{
for (int j = 0; j < elem.propVec.size(); j++)
{
PlyProperty& prop = elem.propVec[j];
int i = 0;
for (; i < dataDescriptor.size(); i++)
{
if (dataDescriptor[i]->elem == prop.elem)
{
if (prop.elem != PlyEntity::NNP_UNKNOWN_ENTITY)
{
descr.push_back(dataDescriptor[i]);
break;
}
else //if (dataDescriptor[i]->name == prop.name)
{
std::string name1(dataDescriptor[i]->name);
std::string name2(prop.name);
std::transform(name1.begin(), name1.end(), name1.begin(), ::tolower);
std::transform(name2.begin(), name2.end(), name2.begin(), ::tolower);
if (name1 == name2)
{
descr.push_back(dataDescriptor[i]);
break;
}
}
}
}
if (i == dataDescriptor.size())
descr.push_back(NULL);
}
}
inline bool ElementDescriptor::ReadElemBinary(PlyFile &file, PlyElement &elem, bool fixEndian)
{
PropertyDescriptor descr;
ExtractDescriptor(descr, elem);
for (int i = 0; i < elem.cnt; i++)
{
for (int j = 0; j < elem.propVec.size(); j++)
{
PlyProperty& prop = elem.propVec[j];
if (descr[j] != NULL)
(*descr[j]).ReadElemBinary(file, prop, fixEndian);
else
prop.SkipBinaryPropertyInFile(file);
}
}
return true;
}
inline bool ElementDescriptor::ReadElemAscii(PlyFile &file, PlyElement &elem)
{
PropertyDescriptor descr;
ExtractDescriptor(descr, elem);
for (int i = 0; i < elem.cnt; i++)
{
for (int j = 0; j < elem.propVec.size(); j++)
{
PlyProperty& prop = elem.propVec[j];
if (descr[j] != NULL)
(*descr[j]).ReadElemAscii(file, prop);
else
prop.SkipAsciiPropertyInFile(file);
}
}
return true;
}
inline bool ElementDescriptor::WriteElemBinary(PlyFile &file, PlyElement &elem, bool fixEndian)
{
PropertyDescriptor descr;
ExtractDescriptor(descr, elem);
for (int i = 0; i < elem.cnt; i++)
{
for (int j = 0; j < elem.propVec.size(); j++)
{
if (descr[j] != NULL)
(*descr[j]).WriteElemBinary(file, elem.propVec[j], fixEndian);
}
}
return true;
}
inline bool ElementDescriptor::WriteElemAscii(PlyFile &file, PlyElement &elem)
{
PropertyDescriptor descr;
ExtractDescriptor(descr, elem);
for (int i = 0; i < elem.cnt; i++)
{
bool first = true;
for (int j = 0; j < elem.propVec.size(); j++)
{
if (descr[j] != NULL)
{
if (!first)
file.WriteAsciiData(std::string(" "));
else
first = false;
(*descr[j]).WriteElemAscii(file, elem.propVec[j]);
}
}
file.WriteAsciiData(std::string("\n"));
}
return true;
}
inline void ElementDescriptor::CheckDescriptor(PlyElement &elem)
{
if (elem.propVec.size() == 0)
{
elem.validToWrite = false;
return;
}
elem.validToWrite = true;
PropertyDescriptor descr;
ExtractDescriptor(descr, elem);
for (int j = 0; j < elem.propVec.size(); j++)
{
if (descr[j] != NULL)
elem.propVec[j].validToWrite = true;
}
}
/** 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 DataDescriptor : public DescriptorInterface
{
public:
inline DataDescriptor();
/**
* 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.
*/
inline DataDescriptor(PlyEntity _e, void *_b) :DescriptorInterface(_e, _b){};
/**
* Constructor of the descriptor.
*
* @param _s Name of the PlyProperty.
* @param _b Pointer to the memory location that contains the data of the property.
*/
inline DataDescriptor(std::string& _s, void *_b) :DescriptorInterface(_s, _b){};
inline void Restart();
inline bool ReadElemBinary(PlyFile &file, PlyProperty &prop, bool fixEndian);
inline bool ReadElemAscii(PlyFile &file, PlyProperty &prop);
inline bool WriteElemBinary(PlyFile &file, PlyProperty &prop, bool fixEndian);
inline bool WriteElemAscii(PlyFile &file, PlyProperty &prop);
private:
template<typename C>
inline void ReadBinary(PlyFile &file, PlyProperty &prop, bool fixEndian);
template<typename C>
inline void ReadAscii(PlyFile &file, PlyProperty &prop);
template<typename C>
inline void WriteBinary(PlyFile &file, PlyProperty &prop, bool fixEndian);
template<typename C>
inline void WriteAscii(PlyFile &file, PlyProperty &prop);
};
template<class CointainerType, int VectorSize, typename ScalarType>
inline void DataDescriptor<CointainerType, VectorSize, ScalarType>::Restart()
{
this->curPos = 0;
}
template<class ContainerType, int VectorSize, typename ScalarType>
template<typename C>
inline void DataDescriptor<ContainerType, VectorSize, ScalarType>::ReadBinary(PlyFile &file, PlyProperty &prop, bool fixEndian)
{
char * buffer = nullptr;
int size;
int count = prop.CountValue();
int typeSize = prop.TypeSize();
if (prop.type >= NNP_LIST_UINT8_UINT32)
{
file.ReadBinaryData(buffer, sizeof(char));
const int cntList = int(*(reinterpret_cast<unsigned char *>(buffer)));
size = typeSize * cntList;
count = cntList;
}
else
size = typeSize * count;
file.ReadBinaryData(buffer, size);
if (typeSize > 1 && fixEndian)
adjustEndianess(reinterpret_cast<unsigned char *>(buffer), typeSize, count);
unsigned char* baseProp = (unsigned char*)base + this->curPos*sizeof(ContainerType);
C* temp = (C*)buffer;
if ((prop.elem == NNP_CRGB || prop.elem == NNP_CRGBA))
{
float norm = 1.0f;
if (std::is_same<ScalarType, float>::value && std::is_same<C, unsigned char>::value)
norm = 1.0f / 255.0f;
else if (std::is_same<ScalarType, unsigned char>::value && std::is_same<C, float>::value)
norm = 255.0f;
for (int i = 0; i < std::min(VectorSize, count); i++)
*((ScalarType *)(baseProp + i*sizeof(ScalarType))) = ScalarType(temp[i] * norm);
}
else
{
for (int i = 0; i < std::min(VectorSize, count); i++)
*((ScalarType *)(baseProp + i*sizeof(ScalarType))) = ScalarType(temp[i]);
}
++(this->curPos);
}
template<class ContainerType, int VectorSize, typename ScalarType>
inline bool DataDescriptor<ContainerType, VectorSize, ScalarType>::ReadElemBinary(PlyFile &file, PlyProperty &prop, bool fixEndian)
{
if (prop.elem != elem)
return false;
switch (prop.type)
{
case NNP_LIST_INT8_INT8:
case NNP_LIST_UINT8_INT8:
case NNP_INT8: this->ReadBinary<char>(file, prop, fixEndian); break;
case NNP_LIST_INT8_UINT8:
case NNP_LIST_UINT8_UINT8:
case NNP_UINT8: this->ReadBinary<unsigned char>(file, prop, fixEndian); break;
case NNP_LIST_INT8_INT16:
case NNP_LIST_UINT8_INT16:
case NNP_INT16: this->ReadBinary<short>(file, prop, fixEndian); break;
case NNP_LIST_INT8_UINT16:
case NNP_LIST_UINT8_UINT16:
case NNP_UINT16: this->ReadBinary<unsigned short>(file, prop, fixEndian); break;
case NNP_LIST_INT8_FLOAT32:
case NNP_LIST_UINT8_FLOAT32:
case NNP_FLOAT32: this->ReadBinary<float>(file, prop, fixEndian); break;
case NNP_LIST_UINT8_INT32:
case NNP_LIST_INT8_INT32:
case NNP_INT32: this->ReadBinary<int>(file, prop, fixEndian); break;
case NNP_LIST_UINT8_UINT32:
case NNP_LIST_INT8_UINT32:
case NNP_UINT32: this->ReadBinary<unsigned int>(file, prop, fixEndian); break;
case NNP_LIST_INT8_FLOAT64:
case NNP_LIST_UINT8_FLOAT64:
case NNP_FLOAT64: this->ReadBinary<double>(file, prop, fixEndian); break;
}
return true;
}
template<class ContainerType, int VectorSize, typename ScalarType>
template<typename C>
inline void DataDescriptor<ContainerType, VectorSize, ScalarType>::ReadAscii(PlyFile &file, PlyProperty &prop)
{
int count = prop.CountValue();
if (prop.type >= NNP_LIST_UINT8_UINT32)
file.ReadAsciiData(count);
C* temp = new C[count];
for (int i = 0; i < count; i++)
file.ReadAsciiData(temp[i]);
unsigned char* baseProp = (unsigned char*)base + this->curPos*sizeof(ContainerType);
if ((prop.elem == NNP_CRGB || prop.elem == NNP_CRGBA))
{
float norm = 1.0f;
if (std::is_same<ScalarType, float>::value && prop.type == NNP_UINT8)
norm = 1.0f / 255.0f;
else if (std::is_same<ScalarType, unsigned char>::value && prop.type == NNP_FLOAT32)
norm = 255.0f;
for (int i = 0; i < std::min(VectorSize, count); i++)
*((ScalarType *)(baseProp + i*sizeof(ScalarType))) = ScalarType(temp[i] * norm);
}
else
{
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>
inline bool DataDescriptor<ContainerType, VectorSize, ScalarType>::ReadElemAscii(PlyFile &file, PlyProperty &prop)
{
if (prop.elem != elem)
return false;
switch (prop.type)
{
case NNP_LIST_UINT8_INT8:
case NNP_LIST_INT8_INT8:
case NNP_INT8: this->ReadAscii<int>(file, prop); break;
case NNP_LIST_UINT8_UINT8:
case NNP_LIST_INT8_UINT8:
case NNP_UINT8: this->ReadAscii<unsigned int>(file, prop); break;
case NNP_LIST_UINT8_INT16:
case NNP_LIST_INT8_INT16:
case NNP_INT16: this->ReadAscii<short>(file, prop); break;
case NNP_LIST_UINT8_UINT16:
case NNP_LIST_INT8_UINT16:
case NNP_UINT16: this->ReadAscii<unsigned short>(file, prop); break;
case NNP_LIST_UINT8_FLOAT32:
case NNP_LIST_INT8_FLOAT32:
case NNP_FLOAT32: this->ReadAscii<float>(file, prop); break;
case NNP_LIST_UINT8_INT32:
case NNP_LIST_INT8_INT32:
case NNP_INT32: this->ReadAscii<int>(file, prop); break;
case NNP_LIST_UINT8_UINT32:
case NNP_LIST_INT8_UINT32:
case NNP_UINT32: this->ReadAscii<unsigned int>(file, prop); break;
case NNP_LIST_UINT8_FLOAT64:
case NNP_LIST_INT8_FLOAT64:
case NNP_FLOAT64: this->ReadAscii<double>(file, prop); break;
}
return true;
}
template<class ContainerType, int VectorSize, typename ScalarType>
template<typename C>
inline void DataDescriptor<ContainerType, VectorSize, ScalarType>::WriteBinary(PlyFile &file, PlyProperty &prop, bool fixEndian)
{
(void)fixEndian;
int count = prop.CountValue();
C data[VectorSize];
if (prop.type >= NNP_LIST_UINT8_UINT32)
{
if (prop.IsSigned())
{
char listSize = (char)VectorSize;
file.WriteBinaryData(&listSize, 1);
count = VectorSize;
}
else
{
unsigned char listSize = (unsigned char)VectorSize;
file.WriteBinaryData(&listSize, 1);
count = VectorSize;
}
}
C temp = 0;
unsigned char* baseProp = (unsigned char*)base + this->curPos*sizeof(ContainerType);
if ((prop.elem == NNP_CRGB || prop.elem == NNP_CRGBA))
{
float norm = 1.0f;
if (std::is_same<ScalarType, float>::value && std::is_same<C, unsigned char>::value)
norm = 255.0f;
else if (std::is_same<ScalarType, unsigned char>::value && std::is_same<C, float>::value)
norm = 1.0f / 255.0f;
for (int i = 0; i < std::min(VectorSize, count); i++)
data[i] = (C)((*(ScalarType*)(baseProp + i*sizeof(ScalarType))) * norm);
}
else
{
for (int i = 0; i < std::min(VectorSize, count); i++)
data[i] = (C)((*(ScalarType*)(baseProp + i*sizeof(ScalarType))));
}
if (sizeof(C) > 1 && fixEndian)
adjustEndianess((unsigned char*)data, sizeof(C), std::min(VectorSize, count));
file.WriteBinaryData(data, sizeof(C)*std::min(VectorSize, count));
for (int i = 0; i < (count - VectorSize); i++)
file.WriteBinaryData(&temp, sizeof(C));
++(this->curPos);
}
template<class ContainerType, int VectorSize, typename ScalarType>
inline bool DataDescriptor<ContainerType, VectorSize, ScalarType>::WriteElemBinary(PlyFile &file, PlyProperty &prop, bool fixEndian)
{
if (prop.elem != elem)
return false;
switch (prop.type)
{
case NNP_LIST_INT8_INT8:
case NNP_LIST_UINT8_INT8:
case NNP_INT8: this->WriteBinary<char>(file, prop, fixEndian); break;
case NNP_LIST_INT8_UINT8:
case NNP_LIST_UINT8_UINT8:
case NNP_UINT8: this->WriteBinary<unsigned char>(file, prop, fixEndian); break;
case NNP_LIST_INT8_INT16:
case NNP_LIST_UINT8_INT16:
case NNP_INT16: this->WriteBinary<short>(file, prop, fixEndian); break;
case NNP_LIST_INT8_UINT16:
case NNP_LIST_UINT8_UINT16:
case NNP_UINT16: this->WriteBinary<unsigned short>(file, prop, fixEndian); break;
case NNP_LIST_INT8_FLOAT32:
case NNP_LIST_UINT8_FLOAT32:
case NNP_FLOAT32: this->WriteBinary<float>(file, prop, fixEndian); break;
case NNP_LIST_UINT8_INT32:
case NNP_LIST_INT8_INT32:
case NNP_INT32: this->WriteBinary<int>(file, prop, fixEndian); break;
case NNP_LIST_UINT8_UINT32:
case NNP_LIST_INT8_UINT32:
case NNP_UINT32: this->WriteBinary<unsigned int>(file, prop, fixEndian); break;
case NNP_LIST_INT8_FLOAT64:
case NNP_LIST_UINT8_FLOAT64:
case NNP_FLOAT64: this->WriteBinary<double>(file, prop, fixEndian); break;
}
return true;
}
template<class ContainerType, int VectorSize, typename ScalarType>
template<typename C>
inline void DataDescriptor<ContainerType, VectorSize, ScalarType>::WriteAscii(PlyFile &file, PlyProperty &prop)
{
int count = prop.CountValue();
if (prop.type >= NNP_LIST_UINT8_UINT32)
{
if (prop.IsSigned())
{
int listSize = (int)VectorSize;
file.WriteAsciiData(listSize);
count = VectorSize;
}
else
{
unsigned int listSize = (unsigned int)VectorSize;
file.WriteAsciiData(listSize);
count = VectorSize;
}
file.WriteAsciiData(std::string(" "));
}
C data[VectorSize];
unsigned char* baseProp = (unsigned char*)base + this->curPos*sizeof(ContainerType);
if ((prop.elem == NNP_CRGB || prop.elem == NNP_CRGBA))
{
float norm = 1.0;
if (std::is_same<ScalarType, float>::value && prop.type == NNP_UINT8)
norm = 255.0f;
else if (std::is_same<ScalarType, unsigned char>::value && prop.type == NNP_FLOAT32)
norm = 1.0f / 255.0f;
for (int i = 0; i < std::min(VectorSize, count); i++)
data[i] = (C)((*(ScalarType*)(baseProp + i*sizeof(ScalarType))) * norm);
}
else
{
for (int i = 0; i < std::min(VectorSize, count); i++)
data[i] = (C)((*(ScalarType*)(baseProp + i*sizeof(ScalarType))));
}
for (int i = 0; i < (count - VectorSize); i++)
data[i] = 0;
for (int i = 0; i < count; i++)
{
file.WriteAsciiData(data[i]);
if (i < count - 1)
file.WriteAsciiData(std::string(" "));
}
++(this->curPos);
}
template<class ContainerType, int VectorSize, typename ScalarType>
inline bool DataDescriptor<ContainerType, VectorSize, ScalarType>::WriteElemAscii(PlyFile &file, PlyProperty& prop)
{
if (prop.elem != elem)
return false;
if (prop.elem == PlyEntity::NNP_UNKNOWN_ENTITY && prop.name != name)
return false;
switch (prop.type)
{
case NNP_LIST_UINT8_INT8:
case NNP_LIST_INT8_INT8:
case NNP_INT8: this->WriteAscii<int>(file, prop); break;
case NNP_LIST_UINT8_UINT8:
case NNP_LIST_INT8_UINT8:
case NNP_UINT8: this->WriteAscii<unsigned int>(file, prop); break;
case NNP_LIST_UINT8_INT16:
case NNP_LIST_INT8_INT16:
case NNP_INT16: this->WriteAscii<short>(file, prop); break;
case NNP_LIST_UINT8_UINT16:
case NNP_LIST_INT8_UINT16:
case NNP_UINT16: this->WriteAscii<unsigned short>(file, prop); break;
case NNP_LIST_UINT8_FLOAT32:
case NNP_LIST_INT8_FLOAT32:
case NNP_FLOAT32: this->WriteAscii<float>(file, prop); break;
case NNP_LIST_UINT8_INT32:
case NNP_LIST_INT8_INT32:
case NNP_INT32: this->WriteAscii<int>(file, prop); break;
case NNP_LIST_UINT8_UINT32:
case NNP_LIST_INT8_UINT32:
case NNP_UINT32: this->WriteAscii<unsigned int>(file, prop); break;
case NNP_LIST_UINT8_FLOAT64:
case NNP_LIST_INT8_FLOAT64:
case NNP_FLOAT64: this->WriteAscii<double>(file, prop); break;
}
return true;
}
template <size_t ActionType>
inline bool ElemProcessing(ElementDescriptor& elemDescr, PlyElement &elem, PlyFile& file, bool fixEndian)
{
if ((elemDescr.elem != PlyElemEntity::NNP_UNKNOWN_ELEM && elemDescr.elem == elem.plyElem) ||
(elemDescr.elem == PlyElemEntity::NNP_UNKNOWN_ELEM && elemDescr.name == elem.name))
{
if (ActionType == 0)
elemDescr.ReadElemBinary(file, elem, fixEndian);
else if (ActionType == 1)
elemDescr.ReadElemAscii(file, elem);
else if (ActionType == 2)
elemDescr.WriteElemBinary(file, elem, fixEndian);
else if (ActionType == 3)
elemDescr.WriteElemAscii(file, elem);
else if (ActionType == 4)
elemDescr.CheckDescriptor(elem);
return true;
}
return false;
}
typedef std::vector<ElementDescriptor*> MeshDescriptor;
/**
* Load a 3D model from a PLY file.
*
* @param meshElements Vector that defines how to manage the ply element data in memory.
* @param info Info of the file to load.
*/
inline bool OpenModel(Info& info, MeshDescriptor& meshElements)
{
PlyFile file;
if (!file.OpenFileToRead(info.filename))
{
info.errInfo = NNP_UNABLE_TO_OPEN;
return false;
}
bool last;
std::string line;
do
{
file.NextHeaderLine(line, last);
} while (!last);
bool fixEndian = false;
if (checkEndianness() == 1)
{
if (info.bigEndian)
fixEndian = true;
}
else
{
if (!info.bigEndian)
fixEndian = true;
}
if (info.binary)
{
for (int i = 0; i < info.elemVec.size(); ++i)
{
PlyElement& pe = info.elemVec[i];
int j = 0;
for (; j < meshElements.size(); j++)
if (ElemProcessing<0>(*meshElements[j], pe, file, fixEndian))
break;
if (j == meshElements.size())
pe.SkipBinaryElementsInFile(file);
//if (!TupleForEach(meshElements, pe, file, fixEndian, SizeT<0>()))
// pe.SkipBinaryElementsInFile(file);
}
}
else
{
for (int i = 0; i < info.elemVec.size(); ++i)
{
PlyElement& pe = info.elemVec[i];
int j = 0;
for (; j < meshElements.size(); j++)
if (ElemProcessing<1>(*meshElements[j], pe, file, false))
break;
if (j == meshElements.size())
pe.SkipAsciiElementsInFile(file);
//if (!TupleForEach(meshElements, pe, file, fixEndian, SizeT<1>()))
// pe.SkipAsciiElementsInFile(file);
}
}
return true;
}
/**
* Save a 3D model in a PLY file.
*
* @param filename Path to the file to save.
* @param meshElements Vector that defines how to manage the ply element data in memory.
* @param info Info to saved in the PLY header.
*/
inline bool SaveModel(std::string& filename, MeshDescriptor& meshElements, Info& info)
{
PlyFile file;
if (!file.OpenFileToWrite(filename))
{
info.errInfo = NNP_UNABLE_TO_OPEN;
return false;
}
for (int i = 0; i < info.elemVec.size(); ++i)
{
PlyElement& pe = info.elemVec[i];
for (int j = 0; j < meshElements.size(); j++)
if (ElemProcessing<4>(*meshElements[j], pe, file, false))
break;
}
info.WriteHeader(file);
bool fixEndian = false;
if (checkEndianness() == 1)
{
if (info.bigEndian)
fixEndian = true;
}
else
{
if (!info.bigEndian)
fixEndian = true;
}
if (info.binary)
{
for (int i = 0; i < info.elemVec.size(); ++i)
{
PlyElement& pe = info.elemVec[i];
if (pe.validToWrite)
{
for (int j = 0; j < meshElements.size(); j++)
if (ElemProcessing<2>(*meshElements[j], pe, file, false))
break;
}
}
}
else
{
for (int i = 0; i < info.elemVec.size(); ++i)
{
PlyElement& pe = info.elemVec[i];
if (pe.validToWrite)
{
for (int j = 0; j < meshElements.size(); j++)
if (ElemProcessing<3>(*meshElements[j], pe, file, false))
break;
}
}
}
file.Flush();
return true;
}
/**
* @cond HIDDEN_SYMBOLS
*/
template < typename TupleType, size_t ActionType>
inline bool TupleForEach(TupleType &tuple, PlyElement &elem, PlyFile& file, bool fixEndian, SizeT<ActionType> a)
{
return TupleForEach(tuple, elem, file, fixEndian, SizeT<std::tuple_size<TupleType>::value>(), a);
}
template < typename TupleType, size_t ActionType>
inline bool TupleForEach(TupleType &tuple, PlyElement &elem, PlyFile& file, bool fixEndian, SizeT<0> t, SizeT<ActionType> a) { return false; }
template < typename TupleType, size_t N, size_t ActionType>
inline bool TupleForEach(TupleType &tuple, PlyElement &elem, PlyFile& file, bool fixEndian, SizeT<N> t, SizeT<ActionType> a)
{
typename std::tuple_element<N - 1, TupleType>::type &elemDescr = std::get<N - 1>(tuple);
if ((elemDescr.elem != PlyElemEntity::NNP_UNKNOWN_ELEM && elemDescr.elem == elem.plyElem) ||
(elemDescr.elem == PlyElemEntity::NNP_UNKNOWN_ELEM && elemDescr.name == elem.name))
{
if (ActionType == 0)
elemDescr.ReadElemBinary(file, elem, fixEndian);
else if (ActionType == 1)
elemDescr.ReadElemAscii(file, elem);
else if (ActionType == 2)
elemDescr.WriteElemBinary(file, elem, fixEndian);
else if (ActionType == 3)
elemDescr.WriteElemAscii(file, elem);
else if (ActionType == 4)
elemDescr.CheckDescriptor(elem);
return true;
}
return TupleForEach(tuple, elem, file, fixEndian, SizeT<N - 1>(), a);
}
/**
* @endcond
*/
} // end namespace nanoply
#endif // NANOPLY_HPP