59 #include <type_traits>
68 enum class DataFormat { ASCII, Binary, BinaryBigEndian };
72 template <
typename T> std::string typeName() {
return "unknown"; }
73 template<>
inline std::string typeName<int8_t>() {
return "char"; }
74 template<>
inline std::string typeName<uint8_t>() {
return "uchar"; }
75 template<>
inline std::string typeName<int16_t>() {
return "short"; }
76 template<>
inline std::string typeName<uint16_t>() {
return "ushort"; }
77 template<>
inline std::string typeName<int32_t>() {
return "int"; }
78 template<>
inline std::string typeName<uint32_t>() {
return "uint"; }
79 template<>
inline std::string typeName<float>() {
return "float"; }
80 template<>
inline std::string typeName<double>() {
return "double"; }
87 template <
class T>
struct TypeChain {
bool hasChildType =
false;
typedef T type; };
88 template <>
struct TypeChain<int64_t> {
bool hasChildType =
true;
typedef int32_t type; };
89 template <>
struct TypeChain<int32_t> {
bool hasChildType =
true;
typedef int16_t type; };
90 template <>
struct TypeChain<int16_t> {
bool hasChildType =
true;
typedef int8_t type; };
91 template <>
struct TypeChain<uint64_t> {
bool hasChildType =
true;
typedef uint32_t type; };
92 template <>
struct TypeChain<uint32_t> {
bool hasChildType =
true;
typedef uint16_t type; };
93 template <>
struct TypeChain<uint16_t> {
bool hasChildType =
true;
typedef uint8_t type; };
94 template <>
struct TypeChain<double> {
bool hasChildType =
true;
typedef float type; };
96 template <
class T>
struct CanonicalName {
typedef T type; };
97 template <>
struct CanonicalName<char> {
typedef int8_t type; };
98 template <>
struct CanonicalName<unsigned char> {
typedef uint8_t type; };
99 template <>
struct CanonicalName<size_t> {
typedef std::conditional<std::is_same<std::make_signed<size_t>::type,
int>::value, uint32_t, uint64_t>::type type; };
102 template <
class T>
struct SerializeType {
typedef T type; };
103 template <>
struct SerializeType<uint8_t> {
typedef int32_t type; };
104 template <>
struct SerializeType< int8_t> {
typedef int32_t type; };
108 template <
typename S,
typename T>
109 S* addressIfSame(T&,
char) {
110 throw std::runtime_error(
"tried to take address for types that are not same");
112 template <
typename S>
113 S* addressIfSame(S& t,
int) {
return &t;}
131 Property(
const std::string& name_) : name(name_){};
149 virtual void parseNext(
const std::vector<std::string>& tokens,
size_t& currEntry) = 0;
219 bool isLittleEndian() {
220 int32_t oneVal = 0x1;
221 char* numPtr = (
char*)&oneVal;
222 return (numPtr[0] == 1);
232 template <
typename T>
233 T swapEndian(T val) {
234 char* bytes =
reinterpret_cast<char*
>(&val);
235 for (
unsigned int i = 0; i <
sizeof(val) / 2; i++) {
236 std::swap(bytes[
sizeof(val) - 1 - i], bytes[i]);
243 template <
typename T>
244 std::vector<std::vector<T>> unflattenList(
const std::vector<T>& flatList,
const std::vector<size_t> flatListStarts) {
245 size_t outerCount = flatListStarts.size() - 1;
248 std::vector<std::vector<T>> outLists(outerCount);
250 if (outerCount == 0) {
255 for (
size_t iOuter = 0; iOuter < outerCount; iOuter++) {
256 size_t iFlatStart = flatListStarts[iOuter];
257 size_t iFlatEnd = flatListStarts[iOuter + 1];
258 outLists[iOuter].insert(outLists[iOuter].begin(), flatList.begin() + iFlatStart, flatList.begin() + iFlatEnd);
281 if (typeName<T>() ==
"unknown") {
283 throw std::runtime_error(
"Attempted property type does not match any type defined by the .ply format.");
294 if (typeName<T>() ==
"unknown") {
295 throw std::runtime_error(
"Attempted property type does not match any type defined by the .ply format.");
306 virtual void reserve(
size_t capacity)
override {
data.reserve(capacity); }
314 virtual void parseNext(
const std::vector<std::string>& tokens,
size_t& currEntry)
override {
316 std::istringstream iss(tokens[currEntry]);
317 typename SerializeType<T>::type tmp;
328 virtual void readNext(std::istream& stream)
override {
330 stream.read((
char*)&
data.back(),
sizeof(T));
340 stream.read((
char*)&
data.back(),
sizeof(T));
341 data.back() = swapEndian(
data.back());
350 outStream <<
"property " << typeName<T>() <<
" " << name <<
"\n";
360 outStream.precision(std::numeric_limits<T>::max_digits10);
361 outStream << static_cast<typename SerializeType<T>::type>(
data[iElement]);
371 outStream.write((
char*)&
data[iElement],
sizeof(T));
381 auto value = swapEndian(
data[iElement]);
382 outStream.write((
char*)&value,
sizeof(T));
390 virtual size_t size()
override {
return data.size(); }
420 if (typeName<T>() ==
"unknown") {
421 throw std::runtime_error(
"Attempted property type does not match any type defined by the .ply format.");
434 if (typeName<T>() ==
"unknown") {
435 throw std::runtime_error(
"Attempted property type does not match any type defined by the .ply format.");
440 for (
const std::vector<T>& vec : data_) {
441 for (
const T& val : vec) {
455 virtual void reserve(
size_t capacity)
override {
466 virtual void parseNext(
const std::vector<std::string>& tokens,
size_t& currEntry)
override {
468 std::istringstream iss(tokens[currEntry]);
474 size_t afterSize = currSize + count;
476 for (
size_t iFlat = currSize; iFlat < afterSize; iFlat++) {
477 std::istringstream iss(tokens[currEntry]);
478 typename SerializeType<T>::type tmp;
491 virtual void readNext(std::istream& stream)
override {
499 size_t afterSize = currSize + count;
502 stream.read((
char*)&
flattenedData[currSize], count *
sizeof(T));
518 count = (size_t)swapEndian((uint64_t)count);
520 count = (size_t)swapEndian((uint32_t)count);
522 count = (size_t)swapEndian((uint16_t)count);
527 size_t afterSize = currSize + count;
530 stream.read((
char*)&
flattenedData[currSize], count *
sizeof(T));
535 for (
size_t iFlat = currSize; iFlat < afterSize; iFlat++) {
547 outStream <<
"property list uchar " << typeName<T>() <<
" " << name <<
"\n";
561 size_t dataCount = dataEnd - dataStart;
562 if (dataCount > std::numeric_limits<uint8_t>::max()) {
563 throw std::runtime_error(
564 "List property has an element with more entries than fit in a uchar. See note in README.");
567 outStream << dataCount;
568 outStream.precision(std::numeric_limits<T>::max_digits10);
569 for (
size_t iFlat = dataStart; iFlat < dataEnd; iFlat++) {
570 outStream <<
" " <<
static_cast<typename SerializeType<T>::type
>(
flattenedData[iFlat]);
585 size_t dataCount = dataEnd - dataStart;
586 if (dataCount > std::numeric_limits<uint8_t>::max()) {
587 throw std::runtime_error(
588 "List property has an element with more entries than fit in a uchar. See note in README.");
590 uint8_t count =
static_cast<uint8_t
>(dataCount);
592 outStream.write((
char*)&count,
sizeof(uint8_t));
593 outStream.write((
char*)&
flattenedData[dataStart], count *
sizeof(T));
607 size_t dataCount = dataEnd - dataStart;
608 if (dataCount > std::numeric_limits<uint8_t>::max()) {
609 throw std::runtime_error(
610 "List property has an element with more entries than fit in a uchar. See note in README.");
612 uint8_t count =
static_cast<uint8_t
>(dataCount);
614 outStream.write((
char*)&count,
sizeof(uint8_t));
615 for (
size_t iFlat = dataStart; iFlat < dataEnd; iFlat++) {
617 outStream.write((
char*)&value,
sizeof(T));
665 inline std::unique_ptr<Property> createPropertyWithType(
const std::string& name,
const std::string& typeStr,
666 bool isList,
const std::string& listCountTypeStr) {
670 int listCountBytes = -1;
672 if (listCountTypeStr ==
"uchar" || listCountTypeStr ==
"uint8" || listCountTypeStr ==
"char" ||
673 listCountTypeStr ==
"int8") {
675 }
else if (listCountTypeStr ==
"ushort" || listCountTypeStr ==
"uint16" || listCountTypeStr ==
"short" ||
676 listCountTypeStr ==
"int16") {
678 }
else if (listCountTypeStr ==
"uint" || listCountTypeStr ==
"uint32" || listCountTypeStr ==
"int" ||
679 listCountTypeStr ==
"int32") {
682 throw std::runtime_error(
"Unrecognized list count type: " + listCountTypeStr);
689 if (typeStr ==
"uchar" || typeStr ==
"uint8") {
691 return std::unique_ptr<Property>(
new TypedListProperty<uint8_t>(name, listCountBytes));
693 return std::unique_ptr<Property>(
new TypedProperty<uint8_t>(name));
698 else if (typeStr ==
"ushort" || typeStr ==
"uint16") {
700 return std::unique_ptr<Property>(
new TypedListProperty<uint16_t>(name, listCountBytes));
702 return std::unique_ptr<Property>(
new TypedProperty<uint16_t>(name));
707 else if (typeStr ==
"uint" || typeStr ==
"uint32") {
709 return std::unique_ptr<Property>(
new TypedListProperty<uint32_t>(name, listCountBytes));
711 return std::unique_ptr<Property>(
new TypedProperty<uint32_t>(name));
718 if (typeStr ==
"char" || typeStr ==
"int8") {
720 return std::unique_ptr<Property>(
new TypedListProperty<int8_t>(name, listCountBytes));
722 return std::unique_ptr<Property>(
new TypedProperty<int8_t>(name));
727 else if (typeStr ==
"short" || typeStr ==
"int16") {
729 return std::unique_ptr<Property>(
new TypedListProperty<int16_t>(name, listCountBytes));
731 return std::unique_ptr<Property>(
new TypedProperty<int16_t>(name));
736 else if (typeStr ==
"int" || typeStr ==
"int32") {
738 return std::unique_ptr<Property>(
new TypedListProperty<int32_t>(name, listCountBytes));
740 return std::unique_ptr<Property>(
new TypedProperty<int32_t>(name));
747 else if (typeStr ==
"float" || typeStr ==
"float32") {
749 return std::unique_ptr<Property>(
new TypedListProperty<float>(name, listCountBytes));
751 return std::unique_ptr<Property>(
new TypedProperty<float>(name));
756 else if (typeStr ==
"double" || typeStr ==
"float64") {
758 return std::unique_ptr<Property>(
new TypedListProperty<double>(name, listCountBytes));
760 return std::unique_ptr<Property>(
new TypedProperty<double>(name));
765 throw std::runtime_error(
"Data type: " + typeStr +
" cannot be mapped to .ply format");
783 Element(
const std::string& name_,
size_t count_) : name(name_), count(count_) {}
787 std::vector<std::unique_ptr<Property>> properties;
797 for (std::unique_ptr<Property>& prop : properties) {
798 if (prop->name == target) {
815 for (std::unique_ptr<Property>& prop : properties) {
816 if (prop->name == target) {
833 std::vector<std::string> names;
834 for (std::unique_ptr<Property>& p : properties) {
835 names.push_back(p->name);
848 for (std::unique_ptr<Property>& prop : properties) {
849 if (prop->name == target) {
853 throw std::runtime_error(
"PLY parser: element " + name +
" does not have property " + target);
864 void addProperty(
const std::string& propertyName,
const std::vector<T>& data) {
866 if (data.size() != count) {
867 throw std::runtime_error(
"PLY write: new property " + propertyName +
" has size which does not match element");
871 for (
size_t i = 0; i < properties.size(); i++) {
872 if (properties[i]->name == propertyName) {
873 properties.erase(properties.begin() + i);
879 std::vector<typename CanonicalName<T>::type> canonicalVec(data.begin(), data.end());
881 properties.push_back(
882 std::unique_ptr<Property>(
new TypedProperty<
typename CanonicalName<T>::type>(propertyName, canonicalVec)));
893 void addListProperty(
const std::string& propertyName,
const std::vector<std::vector<T>>& data) {
895 if (data.size() != count) {
896 throw std::runtime_error(
"PLY write: new property " + propertyName +
" has size which does not match element");
900 for (
size_t i = 0; i < properties.size(); i++) {
901 if (properties[i]->name == propertyName) {
902 properties.erase(properties.begin() + i);
908 std::vector<std::vector<typename CanonicalName<T>::type>> canonicalListVec;
909 for (
const std::vector<T>& subList : data) {
910 canonicalListVec.emplace_back(subList.begin(), subList.end());
913 properties.push_back(std::unique_ptr<Property>(
914 new TypedListProperty<
typename CanonicalName<T>::type>(propertyName, canonicalListVec)));
933 return getDataFromPropertyRecursive<T, T>(prop.get());
952 return castedProp->
data;
956 throw std::runtime_error(
"PLY parser: property " + prop->name +
" is not of type type " + typeName<T>() +
957 ". Has type " + prop->propertyTypeName());
976 return getDataFromListPropertyRecursive<T, T>(prop.get());
999 throw std::runtime_error(
"PLY parser: list property " + prop->name +
" is not of type " + typeName<T>() +
1000 ". Has type " + prop->propertyTypeName());
1025 return getDataFromListPropertyRecursive<T, T>(prop.get());
1026 }
catch (
const std::runtime_error& orig_e) {
1032 typedef typename CanonicalName<T>::type Tcan;
1033 typedef typename std::conditional<std::is_signed<Tcan>::value,
typename std::make_unsigned<Tcan>::type,
1034 typename std::make_signed<Tcan>::type>::type OppsignType;
1036 return getDataFromListPropertyRecursive<T, OppsignType>(prop.get());
1038 }
catch (
const std::runtime_error&) {
1053 for (
size_t iP = 0; iP < properties.size(); iP++) {
1054 for (
char c : properties[iP]->name) {
1055 if (std::isspace(c)) {
1056 throw std::runtime_error(
"Ply validate: illegal whitespace in name " + properties[iP]->name);
1059 for (
size_t jP = iP + 1; jP < properties.size(); jP++) {
1060 if (properties[iP]->name == properties[jP]->name) {
1061 throw std::runtime_error(
"Ply validate: multiple properties with name " + properties[iP]->name);
1067 for (
size_t iP = 0; iP < properties.size(); iP++) {
1068 if (properties[iP]->size() != count) {
1069 throw std::runtime_error(
"Ply validate: property has wrong size. " + properties[iP]->name +
1070 " does not match element size.");
1082 outStream <<
"element " << name <<
" " << count <<
"\n";
1084 for (std::unique_ptr<Property>& p : properties) {
1085 p->writeHeader(outStream);
1098 for (
size_t iE = 0; iE < count; iE++) {
1099 for (
size_t iP = 0; iP < properties.size(); iP++) {
1100 properties[iP]->writeDataASCII(outStream, iE);
1101 if (iP < properties.size() - 1) {
1117 for (
size_t iE = 0; iE < count; iE++) {
1118 for (
size_t iP = 0; iP < properties.size(); iP++) {
1119 properties[iP]->writeDataBinary(outStream, iE);
1132 for (
size_t iE = 0; iE < count; iE++) {
1133 for (
size_t iP = 0; iP < properties.size(); iP++) {
1134 properties[iP]->writeDataBinaryBigEndian(outStream, iE);
1150 template <
class D,
class T>
1153 typedef typename CanonicalName<T>::type Tcan;
1159 std::vector<D> castedVec;
1161 for (Tcan& v : castedProp->
data) {
1162 castedVec.push_back(
static_cast<D
>(v));
1168 TypeChain<Tcan> chainType;
1169 if (chainType.hasChildType) {
1170 return getDataFromPropertyRecursive<D, typename TypeChain<Tcan>::type>(prop);
1173 throw std::runtime_error(
"PLY parser: property " + prop->name +
" cannot be coerced to requested type " +
1189 template <
class D,
class T>
1191 typedef typename CanonicalName<T>::type Tcan;
1198 std::vector<D>* castedFlatVec =
nullptr;
1199 std::vector<D> castedFlatVecCopy;
1201 if (std::is_same<std::vector<D>, std::vector<Tcan>>::value) {
1203 castedFlatVec = addressIfSame<std::vector<D>>(castedProp->
flattenedData, 0 );
1208 castedFlatVecCopy.push_back(
static_cast<D
>(v));
1210 castedFlatVec = &castedFlatVecCopy;
1217 TypeChain<Tcan> chainType;
1218 if (chainType.hasChildType) {
1219 return getDataFromListPropertyRecursive<D, typename TypeChain<Tcan>::type>(prop);
1222 throw std::runtime_error(
"PLY parser: list property " + prop->name +
1223 " cannot be coerced to requested type list " + typeName<D>() +
". Has type list " +
1233 inline std::string trimSpaces(
const std::string& input) {
1235 while (start < input.size() && input[start] ==
' ') start++;
1236 size_t end = input.size();
1237 while (end > start && (input[end - 1] ==
' ' || input[end - 1] ==
'\n' || input[end - 1] ==
'\r')) end--;
1238 return input.substr(start, end - start);
1241 inline std::vector<std::string> tokenSplit(
const std::string& input) {
1242 std::vector<std::string> result;
1245 while ((found = input.find_first_of(
' ', curr)) != std::string::npos) {
1246 std::string token = input.substr(curr, found - curr);
1247 token = trimSpaces(token);
1248 if (token.size() > 0) {
1249 result.push_back(token);
1253 std::string token = input.substr(curr);
1254 token = trimSpaces(token);
1255 if (token.size() > 0) {
1256 result.push_back(token);
1262 inline bool startsWith(
const std::string& input,
const std::string& query) {
1263 return input.compare(0, query.length(), query) == 0;
1285 PLYData(
const std::string& filename,
bool verbose =
false) {
1292 if (verbose) cout <<
"PLY parser: Reading ply file: " << filename << endl;
1295 std::ifstream inStream(filename, std::ios::binary);
1296 if (inStream.fail()) {
1297 throw std::runtime_error(
"PLY parser: Could not open file " + filename);
1300 parsePLY(inStream, verbose);
1303 cout <<
" - Finished parsing file." << endl;
1313 PLYData(std::istream& inStream,
bool verbose =
false) {
1318 if (verbose) cout <<
"PLY parser: Reading ply file from stream" << endl;
1320 parsePLY(inStream, verbose);
1323 cout <<
" - Finished parsing stream." << endl;
1332 for (
size_t iE = 0; iE < elements.size(); iE++) {
1333 for (
char c : elements[iE].name) {
1334 if (std::isspace(c)) {
1335 throw std::runtime_error(
"Ply validate: illegal whitespace in element name " + elements[iE].name);
1338 for (
size_t jE = iE + 1; jE < elements.size(); jE++) {
1339 if (elements[iE].name == elements[jE].name) {
1340 throw std::runtime_error(
"Ply validate: duplcate element name " + elements[iE].name);
1357 void write(
const std::string& filename, DataFormat format = DataFormat::ASCII) {
1358 outputDataFormat = format;
1363 std::ofstream outStream(filename, std::ios::out | std::ios::binary);
1364 if (!outStream.good()) {
1365 throw std::runtime_error(
"Ply writer: Could not open output file " + filename +
" for writing");
1368 writePLY(outStream);
1377 void write(std::ostream& outStream, DataFormat format = DataFormat::ASCII) {
1378 outputDataFormat = format;
1382 writePLY(outStream);
1394 if (e.name == target)
return e;
1396 throw std::runtime_error(
"PLY parser: no element with name: " + target);
1409 if (e.name == target)
return true;
1421 std::vector<std::string> names;
1423 names.push_back(e.name);
1435 void addElement(
const std::string& name,
size_t count) { elements.emplace_back(name, count); }
1453 std::vector<std::array<double, 3>> result(xPos.size());
1454 for (
size_t i = 0; i < result.size(); i++) {
1455 result[i][0] = xPos[i];
1456 result[i][1] = yPos[i];
1457 result[i][2] = zPos[i];
1470 std::vector<std::array<unsigned char, 3>>
getVertexColors(
const std::string& vertexElementName =
"vertex") {
1476 std::vector<std::array<unsigned char, 3>> result(r.size());
1477 for (
size_t i = 0; i < result.size(); i++) {
1478 result[i][0] = r[i];
1479 result[i][1] = g[i];
1480 result[i][2] = b[i];
1493 template <
typename T =
size_t>
1496 for (
const std::string& f : std::vector<std::string>{
"face"}) {
1497 for (
const std::string& p : std::vector<std::string>{
"vertex_indices",
"vertex_index"}) {
1500 }
catch (
const std::runtime_error&) {
1505 throw std::runtime_error(
"PLY parser: could not find face vertex indices attribute under any common name.");
1516 std::string vertexName =
"vertex";
1517 size_t N = vertexPositions.size();
1525 std::vector<double> xPos(N);
1526 std::vector<double> yPos(N);
1527 std::vector<double> zPos(N);
1528 for (
size_t i = 0; i < vertexPositions.size(); i++) {
1529 xPos[i] = vertexPositions[i][0];
1530 yPos[i] = vertexPositions[i][1];
1531 zPos[i] = vertexPositions[i][2];
1547 std::string vertexName =
"vertex";
1548 size_t N = colors.size();
1556 std::vector<unsigned char> r(N);
1557 std::vector<unsigned char> g(N);
1558 std::vector<unsigned char> b(N);
1559 for (
size_t i = 0; i < colors.size(); i++) {
1560 r[i] = colors[i][0];
1561 g[i] = colors[i][1];
1562 b[i] = colors[i][2];
1578 std::string vertexName =
"vertex";
1579 size_t N = colors.size();
1586 auto toChar = [](
double v) {
1587 if (v < 0.0) v = 0.0;
1588 if (v > 1.0) v = 1.0;
1589 return static_cast<unsigned char>(v * 255.);
1593 std::vector<unsigned char> r(N);
1594 std::vector<unsigned char> g(N);
1595 std::vector<unsigned char> b(N);
1596 for (
size_t i = 0; i < colors.size(); i++) {
1597 r[i] = toChar(colors[i][0]);
1598 g[i] = toChar(colors[i][1]);
1599 b[i] = toChar(colors[i][2]);
1615 template <
typename T>
1618 std::string faceName =
"face";
1619 size_t N = indices.size();
1627 typedef typename std::conditional<std::is_signed<T>::value, int32_t, uint32_t>::type IndType;
1628 std::vector<std::vector<IndType>> intInds;
1629 for (std::vector<T>& l : indices) {
1630 std::vector<IndType> thisInds;
1632 IndType valConverted =
static_cast<IndType
>(val);
1633 if (valConverted != val) {
1634 throw std::runtime_error(
"Index value " + std::to_string(val) +
1635 " could not be converted to a .ply integer without loss of data. Note that .ply "
1636 "only supports 32-bit ints.");
1638 thisInds.push_back(valConverted);
1640 intInds.push_back(thisInds);
1660 std::vector<Element> elements;
1661 const int majorVersion = 1;
1662 const int minorVersion = 0;
1664 DataFormat inputDataFormat = DataFormat::ASCII;
1665 DataFormat outputDataFormat = DataFormat::ASCII;
1676 void parsePLY(std::istream& inStream,
bool verbose) {
1679 parseHeader(inStream, verbose);
1683 if (inputDataFormat == DataFormat::Binary) {
1684 parseBinary(inStream, verbose);
1687 else if (inputDataFormat == DataFormat::BinaryBigEndian) {
1688 parseBinaryBigEndian(inStream, verbose);
1691 else if (inputDataFormat == DataFormat::ASCII) {
1692 parseASCII(inStream, verbose);
1702 void parseHeader(std::istream& inStream,
bool verbose) {
1712 std::getline(inStream, plyLine);
1713 if (trimSpaces(plyLine) !=
"ply") {
1714 throw std::runtime_error(
"PLY parser: File does not appear to be ply file. First line should be 'ply'");
1720 std::getline(inStream, styleLine);
1721 vector<string> tokens = tokenSplit(styleLine);
1722 if (tokens.size() != 3)
throw std::runtime_error(
"PLY parser: bad format line");
1723 std::string formatStr = tokens[0];
1724 std::string typeStr = tokens[1];
1725 std::string versionStr = tokens[2];
1728 if (formatStr !=
"format")
throw std::runtime_error(
"PLY parser: bad format line");
1731 if (typeStr ==
"ascii") {
1732 inputDataFormat = DataFormat::ASCII;
1733 if (verbose) cout <<
" - Type: ascii" << endl;
1734 }
else if (typeStr ==
"binary_little_endian") {
1735 inputDataFormat = DataFormat::Binary;
1736 if (verbose) cout <<
" - Type: binary" << endl;
1737 }
else if (typeStr ==
"binary_big_endian") {
1738 inputDataFormat = DataFormat::BinaryBigEndian;
1739 if (verbose) cout <<
" - Type: binary big endian" << endl;
1741 throw std::runtime_error(
"PLY parser: bad format line");
1745 if (versionStr !=
"1.0") {
1746 throw std::runtime_error(
"PLY parser: encountered file with version != 1.0. Don't know how to parse that");
1748 if (verbose) cout <<
" - Version: " << versionStr << endl;
1752 while (inStream.good()) {
1754 std::getline(inStream, line);
1757 if (startsWith(line,
"comment")) {
1758 string comment = line.substr(8);
1759 if (verbose) cout <<
" - Comment: " << comment << endl;
1765 if (startsWith(line,
"obj_info")) {
1766 string infoComment = line.substr(9);
1767 if (verbose) cout <<
" - obj_info: " << infoComment << endl;
1773 else if (startsWith(line,
"element")) {
1774 vector<string> tokens = tokenSplit(line);
1775 if (tokens.size() != 3)
throw std::runtime_error(
"PLY parser: Invalid element line");
1776 string name = tokens[1];
1778 std::istringstream iss(tokens[2]);
1780 elements.emplace_back(name, count);
1781 if (verbose) cout <<
" - Found element: " << name <<
" (count = " << count <<
")" << endl;
1786 else if (startsWith(line,
"property list")) {
1787 vector<string> tokens = tokenSplit(line);
1788 if (tokens.size() != 5)
throw std::runtime_error(
"PLY parser: Invalid property list line");
1789 if (elements.size() == 0)
throw std::runtime_error(
"PLY parser: Found property list without previous element");
1790 string countType = tokens[2];
1791 string type = tokens[3];
1792 string name = tokens[4];
1793 elements.back().properties.push_back(createPropertyWithType(name, type,
true, countType));
1795 cout <<
" - Found list property: " << name <<
" (count type = " << countType <<
", data type = " << type
1801 else if (startsWith(line,
"property")) {
1802 vector<string> tokens = tokenSplit(line);
1803 if (tokens.size() != 3)
throw std::runtime_error(
"PLY parser: Invalid property line");
1804 if (elements.size() == 0)
throw std::runtime_error(
"PLY parser: Found property without previous element");
1805 string type = tokens[1];
1806 string name = tokens[2];
1807 elements.back().properties.push_back(createPropertyWithType(name, type,
false,
""));
1808 if (verbose) cout <<
" - Found property: " << name <<
" (type = " << type <<
")" << endl;
1813 else if (startsWith(line,
"end_header")) {
1819 throw std::runtime_error(
"Unrecognized header line: " + line);
1830 void parseASCII(std::istream& inStream,
bool verbose) {
1836 for (Element& elem : elements) {
1839 std::cout <<
" - Processing element: " << elem.name << std::endl;
1842 for (
size_t iP = 0; iP < elem.properties.size(); iP++) {
1843 elem.properties[iP]->reserve(elem.count);
1845 for (
size_t iEntry = 0; iEntry < elem.count; iEntry++) {
1848 std::getline(inStream, line);
1852 if (!elem.properties.empty()) {
1853 while (line.empty()) {
1854 std::getline(inStream, line);
1858 vector<string> tokens = tokenSplit(line);
1860 for (
size_t iP = 0; iP < elem.properties.size(); iP++) {
1861 elem.properties[iP]->parseNext(tokens, iTok);
1873 void parseBinary(std::istream& inStream,
bool verbose) {
1875 if (!isLittleEndian()) {
1876 throw std::runtime_error(
"binary reading assumes little endian system");
1883 for (Element& elem : elements) {
1886 std::cout <<
" - Processing element: " << elem.name << std::endl;
1889 for (
size_t iP = 0; iP < elem.properties.size(); iP++) {
1890 elem.properties[iP]->reserve(elem.count);
1892 for (
size_t iEntry = 0; iEntry < elem.count; iEntry++) {
1893 for (
size_t iP = 0; iP < elem.properties.size(); iP++) {
1894 elem.properties[iP]->readNext(inStream);
1906 void parseBinaryBigEndian(std::istream& inStream,
bool verbose) {
1908 if (!isLittleEndian()) {
1909 throw std::runtime_error(
"binary reading assumes little endian system");
1916 for (Element& elem : elements) {
1919 std::cout <<
" - Processing element: " << elem.name << std::endl;
1922 for (
size_t iP = 0; iP < elem.properties.size(); iP++) {
1923 elem.properties[iP]->reserve(elem.count);
1925 for (
size_t iEntry = 0; iEntry < elem.count; iEntry++) {
1926 for (
size_t iP = 0; iP < elem.properties.size(); iP++) {
1927 elem.properties[iP]->readNextBigEndian(inStream);
1941 void writePLY(std::ostream& outStream) {
1943 writeHeader(outStream);
1946 for (Element& e : elements) {
1947 if (outputDataFormat == DataFormat::Binary) {
1948 if (!isLittleEndian()) {
1949 throw std::runtime_error(
"binary writing assumes little endian system");
1951 e.writeDataBinary(outStream);
1952 }
else if (outputDataFormat == DataFormat::BinaryBigEndian) {
1953 if (!isLittleEndian()) {
1954 throw std::runtime_error(
"binary writing assumes little endian system");
1956 e.writeDataBinaryBigEndian(outStream);
1957 }
else if (outputDataFormat == DataFormat::ASCII) {
1958 e.writeDataASCII(outStream);
1969 void writeHeader(std::ostream& outStream) {
1972 outStream <<
"ply\n";
1975 outStream <<
"format ";
1976 if (outputDataFormat == DataFormat::Binary) {
1977 outStream <<
"binary_little_endian ";
1978 }
else if (outputDataFormat == DataFormat::BinaryBigEndian) {
1979 outStream <<
"binary_big_endian ";
1980 }
else if (outputDataFormat == DataFormat::ASCII) {
1981 outStream <<
"ascii ";
1985 outStream << majorVersion <<
"." << minorVersion <<
"\n";
1988 bool hasHapplyComment =
false;
1989 std::string happlyComment =
"Written with hapPLY (https://github.com/nmwsharp/happly)";
1990 for (
const std::string& comment :
comments) {
1991 if (comment == happlyComment) hasHapplyComment =
true;
1992 outStream <<
"comment " << comment <<
"\n";
1994 if (!hasHapplyComment) {
1995 outStream <<
"comment " << happlyComment <<
"\n";
2000 outStream <<
"obj_info " << comment <<
"\n";
2004 for (Element& e : elements) {
2005 e.writeHeader(outStream);
2009 outStream <<
"end_header\n";