You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2790 lines
85 KiB
2790 lines
85 KiB
/**************************************************************************** |
|
* 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 <cstring> |
|
#include <limits> |
|
#include <fstream> |
|
#include <sstream> |
|
#include <stdint.h> |
|
#include <string> |
|
#include <iomanip> |
|
|
|
|
|
// 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", "tx" }) }, |
|
{ PlyEntity::NNP_TEXTUREV, NameVector({ "texture_v", "v", "t", "ty" }) }, |
|
{ 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_indices", "vertex_index" }) }, |
|
{ 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({ "unknown" }) }, |
|
{ 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.find("end_header") != std::string::npos) |
|
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; |
|
} |
|
|
|
template <> |
|
inline bool PlyFile::WriteAsciiData<double>(const double & src) |
|
{ |
|
if (mode != 1) |
|
return false; |
|
const auto precision = fileStream.precision(); |
|
fileStream << std::setprecision(std::numeric_limits<double>::max_digits10) |
|
<< src |
|
<< std::setprecision(precision); |
|
return true; |
|
} |
|
|
|
template <> |
|
inline bool PlyFile::WriteAsciiData<float>(const float & src) |
|
{ |
|
if (mode != 1) |
|
return false; |
|
const auto precision = fileStream.precision(); |
|
fileStream << std::setprecision(std::numeric_limits<float>::max_digits10) |
|
<< src |
|
<< std::setprecision(precision); |
|
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). */ |
|
|
|
|
|
inline PlyProperty() {} |
|
|
|
|
|
/** |
|
* 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) : name(PlyPropertyName(_e)[0]), type(_t), elem(_e), 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) : name(_n), type(_t), elem(_e), 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) :name(_n), type(_t), elem(PlyEntity::NNP_UNKNOWN_ENTITY), 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; |
|
} |
|
default: assert(0); |
|
} |
|
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 (size_t 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() : cnt(0), 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(const std::string& _name, std::vector<PlyProperty> &prop, size_t nElem) :name(_name), plyElem(PlyElemEntity::NNP_UNKNOWN_ELEM), cnt(nElem), propVec(prop), 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]), plyElem(ent), cnt(nElem), propVec(prop), 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\r\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\r\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 (size_t i = 0; i < propVec.size(); i++) |
|
ok = propVec[i].WriteHeader(file); |
|
} |
|
else |
|
return false; |
|
return ok; |
|
} |
|
|
|
|
|
inline bool PlyElement::SkipAsciiElementsInFile(PlyFile &file) |
|
{ |
|
for (size_t i = 0; i < this->cnt; ++i) |
|
for (size_t j = 0; j < this->propVec.size(); ++j) |
|
this->propVec[j].SkipAsciiPropertyInFile(file); |
|
return true; |
|
} |
|
|
|
|
|
inline bool PlyElement::SkipBinaryElementsInFile(PlyFile &file) |
|
{ |
|
for (size_t i = 0; i < this->cnt; ++i) |
|
for (size_t 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 (size_t 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(const 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(const 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); |
|
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 (size_t i = 0; i < this->textureFile.size(); i++) |
|
ok = file.WriteHeaderLine(std::string("comment TextureFile ") + this->textureFile[i] + "\n"); |
|
for (size_t 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(const 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 0; |
|
} |
|
|
|
|
|
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(const std::string& name) |
|
{ |
|
for (size_t i = 0; i < elemVec.size(); i++) |
|
{ |
|
if (elemVec[i].name == name) |
|
return &elemVec[i]; |
|
} |
|
return NULL; |
|
} |
|
|
|
|
|
inline PlyElement* Info::GetElement(PlyElemEntity e) |
|
{ |
|
for (size_t 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), base(_b), elem(_e), 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(const std::string& _s, void *_b) :curPos(0), base(_b), elem(PlyEntity::NNP_UNKNOWN_ENTITY), name(_s){} |
|
|
|
/** |
|
* Virtual destructor |
|
*/ |
|
virtual ~DescriptorInterface() {} |
|
|
|
/** |
|
* 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) : name(PlyElementName(_e)[0]), elem(_e){} |
|
|
|
/** |
|
* Constructor of the Ply element descriptor. |
|
* |
|
* @param _s Name of the Ply element managed by the descriptor. |
|
*/ |
|
inline ElementDescriptor(const std::string &_s) : name(_s), elem(PlyElemEntity::NNP_UNKNOWN_ELEM){} |
|
|
|
/** |
|
* 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 (size_t j = 0; j < elem.propVec.size(); j++) |
|
{ |
|
PlyProperty& prop = elem.propVec[j]; |
|
size_t 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; |
|
} |
|
} |
|
} |
|
else if ((dataDescriptor[i]->elem == PlyEntity::NNP_CRGB && prop.elem == PlyEntity::NNP_CRGBA) || |
|
(dataDescriptor[i]->elem == PlyEntity::NNP_CRGBA && prop.elem == PlyEntity::NNP_CRGB)) |
|
{ |
|
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 (size_t i = 0; i < elem.cnt; i++) |
|
{ |
|
for (size_t 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 (size_t i = 0; i < elem.cnt; i++) |
|
{ |
|
for (size_t 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 (size_t i = 0; i < elem.cnt; i++) |
|
{ |
|
for (size_t 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 (size_t i = 0; i < elem.cnt; i++) |
|
{ |
|
bool first = true; |
|
for (size_t 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 (size_t j = 0; j < elem.propVec.size(); j++) |
|
{ |
|
if (descr[j] != NULL) |
|
elem.propVec[j].validToWrite = true; |
|
} |
|
} |
|
|
|
|
|
|
|
template<class ContainerType, int VectorSize, typename ScalarType> |
|
class DescriptorHelper |
|
{ |
|
|
|
public: |
|
|
|
template<typename C> |
|
static void ReadBinary(DescriptorInterface& descr, 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*)descr.base + descr.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]); |
|
} |
|
++(descr.curPos); |
|
} |
|
|
|
template<typename C> |
|
static void ReadAscii(DescriptorInterface& descr, 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*)descr.base + descr.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; |
|
++(descr.curPos); |
|
} |
|
|
|
template<typename C> |
|
static void WriteBinary(DescriptorInterface& descr, 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*)descr.base + descr.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)); |
|
++(descr.curPos); |
|
} |
|
|
|
template<typename C> |
|
static void WriteAscii(DescriptorInterface& descr, 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*)descr.base + descr.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(" ")); |
|
} |
|
++(descr.curPos); |
|
} |
|
}; |
|
|
|
|
|
|
|
|
|
template<class ContainerType, typename ScalarType> |
|
class DescriptorHelper<ContainerType, 0, ScalarType> |
|
{ |
|
|
|
public: |
|
|
|
template<typename C> |
|
static void ReadBinary(DescriptorInterface& descr, 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); |
|
|
|
C* temp = (C*)buffer; |
|
|
|
ContainerType* container = (ContainerType*)((unsigned char*)descr.base + descr.curPos * sizeof(ContainerType)); |
|
(*container).resize(count); |
|
for (int i = 0; i < count; i++) |
|
(*container)[i] = (ScalarType)(temp[i]); |
|
++(descr.curPos); |
|
} |
|
|
|
template<typename C> |
|
static void ReadAscii(DescriptorInterface& descr, 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]); |
|
|
|
ContainerType* container = (ContainerType*)((unsigned char*)descr.base + descr.curPos * sizeof(ContainerType)); |
|
(*container).resize(count); |
|
for (int i = 0; i < count; i++) |
|
(*container)[i] = (ScalarType)(temp[i]); |
|
delete[] temp; |
|
++(descr.curPos); |
|
} |
|
|
|
template<typename C> |
|
static void WriteBinary(DescriptorInterface& descr, PlyFile &file, PlyProperty &prop, bool fixEndian) |
|
{ |
|
size_t count = prop.CountValue(); |
|
ContainerType* list = (ContainerType*)((unsigned char*)descr.base + descr.curPos * sizeof(ContainerType)); |
|
if (prop.type >= NNP_LIST_UINT8_UINT32) |
|
{ |
|
if (prop.IsSigned()) |
|
{ |
|
char listSize = (char)list->size(); |
|
file.WriteBinaryData(&listSize, 1); |
|
count = list->size(); |
|
} |
|
else |
|
{ |
|
unsigned char listSize = (unsigned char)list->size(); |
|
file.WriteBinaryData(&listSize, 1); |
|
count = list->size(); |
|
} |
|
} |
|
|
|
std::vector<C> data(count); |
|
|
|
for (size_t i = 0; i < std::min(count, list->size()); i++) |
|
data[i] = (C)((*list)[i]); |
|
|
|
if (sizeof(C) > 1 && fixEndian) |
|
adjustEndianess((unsigned char*)data.data(), sizeof(C), std::min(count, list->size())); |
|
|
|
file.WriteBinaryData(data.data(), sizeof(C)*std::min(count, list->size())); |
|
C temp = 0; |
|
for (size_t i = 0; i < (count - list->size()); i++) |
|
file.WriteBinaryData(&temp, sizeof(C)); |
|
++(descr.curPos); |
|
} |
|
|
|
template<typename C> |
|
static void WriteAscii(DescriptorInterface& descr, PlyFile &file, PlyProperty &prop) |
|
{ |
|
size_t count = prop.CountValue(); |
|
ContainerType* list = (ContainerType*)((unsigned char*)descr.base + descr.curPos * sizeof(ContainerType)); |
|
if (prop.type >= NNP_LIST_UINT8_UINT32) |
|
{ |
|
if (prop.IsSigned()) |
|
{ |
|
int listSize = (int)list->size(); |
|
file.WriteAsciiData(listSize); |
|
count = list->size(); |
|
} |
|
else |
|
{ |
|
unsigned int listSize = (unsigned int)list->size(); |
|
file.WriteAsciiData(listSize); |
|
count = list->size(); |
|
} |
|
file.WriteAsciiData(std::string(" ")); |
|
} |
|
|
|
std::vector<C> data(count); |
|
for (size_t i = 0; i < std::min(count, list->size()); i++) |
|
data[i] = (C)((*list)[i]); |
|
|
|
for (size_t i = 0; i < (count - list->size()); i++) |
|
data[i] = 0; |
|
|
|
for (size_t i = 0; i < count; i++) |
|
{ |
|
file.WriteAsciiData(data[i]); |
|
if (i < count - 1) |
|
file.WriteAsciiData(std::string(" ")); |
|
} |
|
++(descr.curPos); |
|
} |
|
}; |
|
|
|
|
|
|
|
|
|
/** Memory descriptor of a vector of properties. |
|
* The class defines how a vector of PlyProperty is saved in memory. |
|
* |
|
* @tparam ContainerType 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 ContainerType, int VectorSize, typename ScalarType> |
|
class DataDescriptor : public DescriptorInterface |
|
{ |
|
public: |
|
|
|
DescriptorHelper<ContainerType, VectorSize, ScalarType> helper; |
|
|
|
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(const 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); |
|
}; |
|
|
|
|
|
template<class ContainerType, int VectorSize, typename ScalarType> |
|
void DataDescriptor<ContainerType, VectorSize, ScalarType>::Restart() |
|
{ |
|
this->curPos = 0; |
|
} |
|
|
|
|
|
template<class ContainerType, int VectorSize, typename ScalarType> |
|
bool DataDescriptor<ContainerType, VectorSize, ScalarType>::ReadElemBinary(PlyFile &file, PlyProperty &prop, bool fixEndian) |
|
{ |
|
if (prop.elem != elem && ((prop.elem != PlyEntity::NNP_CRGB && prop.elem != PlyEntity::NNP_CRGBA) || (prop.elem != PlyEntity::NNP_CRGBA && prop.elem != PlyEntity::NNP_CRGB))) |
|
return false; |
|
switch (prop.type) |
|
{ |
|
case NNP_LIST_INT8_INT8: |
|
case NNP_LIST_UINT8_INT8: |
|
case NNP_INT8: |
|
helper.template ReadBinary<char>(*this, file, prop, fixEndian); break; |
|
case NNP_LIST_INT8_UINT8: |
|
case NNP_LIST_UINT8_UINT8: |
|
case NNP_UINT8: |
|
helper.template ReadBinary<unsigned char>(*this, file, prop, fixEndian); break; |
|
case NNP_LIST_INT8_INT16: |
|
case NNP_LIST_UINT8_INT16: |
|
case NNP_INT16: |
|
helper.template ReadBinary<short>(*this, file, prop, fixEndian); break; |
|
case NNP_LIST_INT8_UINT16: |
|
case NNP_LIST_UINT8_UINT16: |
|
case NNP_UINT16: |
|
helper.template ReadBinary<unsigned short>(*this, file, prop, fixEndian); break; |
|
case NNP_LIST_INT8_FLOAT32: |
|
case NNP_LIST_UINT8_FLOAT32: |
|
case NNP_FLOAT32: |
|
helper.template ReadBinary<float>(*this, file, prop, fixEndian); break; |
|
case NNP_LIST_UINT8_INT32: |
|
case NNP_LIST_INT8_INT32: |
|
case NNP_INT32: |
|
helper.template ReadBinary<int>(*this, file, prop, fixEndian); break; |
|
case NNP_LIST_UINT8_UINT32: |
|
case NNP_LIST_INT8_UINT32: |
|
case NNP_UINT32: |
|
helper.template ReadBinary<unsigned int>(*this, file, prop, fixEndian); break; |
|
case NNP_LIST_INT8_FLOAT64: |
|
case NNP_LIST_UINT8_FLOAT64: |
|
case NNP_FLOAT64: |
|
helper.template ReadBinary<double>(*this, file, prop, fixEndian); break; |
|
case NNP_UNKNOWN_TYPE: |
|
break; |
|
} |
|
return true; |
|
} |
|
|
|
|
|
template<class ContainerType, int VectorSize, typename ScalarType> |
|
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: |
|
helper.template ReadAscii<int>(*this, file, prop); break; |
|
case NNP_LIST_UINT8_UINT8: |
|
case NNP_LIST_INT8_UINT8: |
|
case NNP_UINT8: |
|
helper.template ReadAscii<unsigned int>(*this, file, prop); break; |
|
case NNP_LIST_UINT8_INT16: |
|
case NNP_LIST_INT8_INT16: |
|
case NNP_INT16: |
|
helper.template ReadAscii<short>(*this, file, prop); break; |
|
case NNP_LIST_UINT8_UINT16: |
|
case NNP_LIST_INT8_UINT16: |
|
case NNP_UINT16: |
|
helper.template ReadAscii<unsigned short>(*this, file, prop); break; |
|
case NNP_LIST_UINT8_FLOAT32: |
|
case NNP_LIST_INT8_FLOAT32: |
|
case NNP_FLOAT32: |
|
helper.template ReadAscii<float>(*this, file, prop); break; |
|
case NNP_LIST_UINT8_INT32: |
|
case NNP_LIST_INT8_INT32: |
|
case NNP_INT32: |
|
helper.template ReadAscii<int>(*this, file, prop); break; |
|
case NNP_LIST_UINT8_UINT32: |
|
case NNP_LIST_INT8_UINT32: |
|
case NNP_UINT32: |
|
helper.template ReadAscii<unsigned int>(*this, file, prop); break; |
|
case NNP_LIST_UINT8_FLOAT64: |
|
case NNP_LIST_INT8_FLOAT64: |
|
case NNP_FLOAT64: |
|
helper.template ReadAscii<double>(*this, file, prop); break; |
|
case NNP_UNKNOWN_TYPE: |
|
break; |
|
} |
|
return true; |
|
} |
|
|
|
|
|
template<class ContainerType, int VectorSize, typename ScalarType> |
|
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: |
|
helper.template WriteBinary<char>(*this, file, prop, fixEndian); break; |
|
case NNP_LIST_INT8_UINT8: |
|
case NNP_LIST_UINT8_UINT8: |
|
case NNP_UINT8: |
|
helper.template WriteBinary<unsigned char>(*this, file, prop, fixEndian); break; |
|
case NNP_LIST_INT8_INT16: |
|
case NNP_LIST_UINT8_INT16: |
|
case NNP_INT16: |
|
helper.template WriteBinary<short>(*this, file, prop, fixEndian); break; |
|
case NNP_LIST_INT8_UINT16: |
|
case NNP_LIST_UINT8_UINT16: |
|
case NNP_UINT16: |
|
helper.template WriteBinary<unsigned short>(*this, file, prop, fixEndian); break; |
|
case NNP_LIST_INT8_FLOAT32: |
|
case NNP_LIST_UINT8_FLOAT32: |
|
case NNP_FLOAT32: |
|
helper.template WriteBinary<float>(*this, file, prop, fixEndian); break; |
|
case NNP_LIST_UINT8_INT32: |
|
case NNP_LIST_INT8_INT32: |
|
case NNP_INT32: |
|
helper.template WriteBinary<int>(*this, file, prop, fixEndian); break; |
|
case NNP_LIST_UINT8_UINT32: |
|
case NNP_LIST_INT8_UINT32: |
|
case NNP_UINT32: |
|
helper.template WriteBinary<unsigned int>(*this, file, prop, fixEndian); break; |
|
case NNP_LIST_INT8_FLOAT64: |
|
case NNP_LIST_UINT8_FLOAT64: |
|
case NNP_FLOAT64: |
|
helper.template WriteBinary<double>(*this, file, prop, fixEndian); break; |
|
case NNP_UNKNOWN_TYPE: |
|
break; |
|
} |
|
return true; |
|
} |
|
|
|
|
|
template<class ContainerType, int VectorSize, typename ScalarType> |
|
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: |
|
helper.template WriteAscii<int>(*this, file, prop); break; |
|
case NNP_LIST_UINT8_UINT8: |
|
case NNP_LIST_INT8_UINT8: |
|
case NNP_UINT8: |
|
helper.template WriteAscii<unsigned int>(*this, file, prop); break; |
|
case NNP_LIST_UINT8_INT16: |
|
case NNP_LIST_INT8_INT16: |
|
case NNP_INT16: |
|
helper.template WriteAscii<short>(*this, file, prop); break; |
|
case NNP_LIST_UINT8_UINT16: |
|
case NNP_LIST_INT8_UINT16: |
|
case NNP_UINT16: |
|
helper.template WriteAscii<unsigned short>(*this, file, prop); break; |
|
case NNP_LIST_UINT8_FLOAT32: |
|
case NNP_LIST_INT8_FLOAT32: |
|
case NNP_FLOAT32: |
|
helper.template WriteAscii<float>(*this, file, prop); break; |
|
case NNP_LIST_UINT8_INT32: |
|
case NNP_LIST_INT8_INT32: |
|
case NNP_INT32: |
|
helper.template WriteAscii<int>(*this, file, prop); break; |
|
case NNP_LIST_UINT8_UINT32: |
|
case NNP_LIST_INT8_UINT32: |
|
case NNP_UINT32: |
|
helper.template WriteAscii<unsigned int>(*this, file, prop); break; |
|
case NNP_LIST_UINT8_FLOAT64: |
|
case NNP_LIST_INT8_FLOAT64: |
|
case NNP_FLOAT64: |
|
helper.template WriteAscii<double>(*this, file, prop); break; |
|
case NNP_UNKNOWN_TYPE: |
|
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 (size_t i = 0; i < info.elemVec.size(); ++i) |
|
{ |
|
PlyElement& pe = info.elemVec[i]; |
|
size_t 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 (size_t i = 0; i < info.elemVec.size(); ++i) |
|
{ |
|
PlyElement& pe = info.elemVec[i]; |
|
size_t 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 (size_t i = 0; i < info.elemVec.size(); ++i) |
|
{ |
|
PlyElement& pe = info.elemVec[i]; |
|
for (size_t 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; |
|
} |
|
(void)fixEndian; |
|
|
|
if (info.binary) |
|
{ |
|
for (size_t i = 0; i < info.elemVec.size(); ++i) |
|
{ |
|
PlyElement& pe = info.elemVec[i]; |
|
if (pe.validToWrite) |
|
{ |
|
for (size_t j = 0; j < meshElements.size(); j++) |
|
if (ElemProcessing<2>(*meshElements[j], pe, file, false)) |
|
break; |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
for (size_t i = 0; i < info.elemVec.size(); ++i) |
|
{ |
|
PlyElement& pe = info.elemVec[i]; |
|
if (pe.validToWrite) |
|
{ |
|
for (size_t 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) |
|
{ |
|
(void)tuple; (void)elem; (void)file; (void)fixEndian; (void)t; (void)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) |
|
{ |
|
(void)t; |
|
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
|
|
|