999 lines
34 KiB
C
999 lines
34 KiB
C
|
// Copyright (c) 2015 Geometry Factory
|
||
|
// All rights reserved.
|
||
|
//
|
||
|
// This file is part of CGAL (www.cgal.org).
|
||
|
// You can redistribute it and/or modify it under the terms of the GNU
|
||
|
// General Public License as published by the Free Software Foundation,
|
||
|
// either version 3 of the License, or (at your option) any later version.
|
||
|
//
|
||
|
// Licensees holding a valid commercial license may use this file in
|
||
|
// accordance with the commercial license agreement provided with the software.
|
||
|
//
|
||
|
// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
||
|
// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
||
|
//
|
||
|
// $URL$
|
||
|
// $Id$
|
||
|
// SPDX-License-Identifier: GPL-3.0+
|
||
|
//
|
||
|
// Author(s) : Simon Giraudot
|
||
|
|
||
|
#ifndef CGAL_READ_PLY_POINTS_H
|
||
|
#define CGAL_READ_PLY_POINTS_H
|
||
|
|
||
|
#include <CGAL/license/Point_set_processing_3.h>
|
||
|
|
||
|
#include <CGAL/config.h>
|
||
|
#if defined(CGAL_CFG_NO_CPP0X_RVALUE_REFERENCE) || defined(CGAL_CFG_NO_CPP0X_VARIADIC_TEMPLATES)
|
||
|
#error CGAL PLY reader requires a C++11 compiler
|
||
|
#endif
|
||
|
|
||
|
#include <tuple>
|
||
|
|
||
|
#include <CGAL/property_map.h>
|
||
|
#include <CGAL/value_type_traits.h>
|
||
|
#include <CGAL/point_set_processing_assertions.h>
|
||
|
#include <CGAL/Kernel_traits.h>
|
||
|
#include <CGAL/IO/io.h>
|
||
|
|
||
|
#include <CGAL/boost/graph/named_function_params.h>
|
||
|
#include <CGAL/boost/graph/named_params_helper.h>
|
||
|
|
||
|
#include <boost/version.hpp>
|
||
|
#include <boost/cstdint.hpp>
|
||
|
|
||
|
#include <iostream>
|
||
|
#include <sstream>
|
||
|
#include <string>
|
||
|
|
||
|
#define TRY_TO_GENERATE_PROPERTY(STD_TYPE, T_TYPE, TYPE) \
|
||
|
if (type == STD_TYPE || type == T_TYPE) \
|
||
|
m_properties->push_back (new PLY_read_typed_number< TYPE > (name, format))
|
||
|
|
||
|
#define TRY_TO_GENERATE_SIZED_LIST_PROPERTY(STD_SIZE_TYPE, T_SIZE_TYPE, SIZE_TYPE, STD_INDEX_TYPE, T_INDEX_TYPE, INDEX_TYPE) \
|
||
|
if ((size_type == STD_SIZE_TYPE || size_type == T_SIZE_TYPE) && \
|
||
|
(index_type == STD_INDEX_TYPE || index_type == T_INDEX_TYPE)) \
|
||
|
m_properties->push_back (new PLY_read_typed_list_with_typed_size< SIZE_TYPE , INDEX_TYPE > (name, format))
|
||
|
|
||
|
#define TRY_TO_GENERATE_LIST_PROPERTY(STD_INDEX_TYPE, T_INDEX_TYPE, INDEX_TYPE) \
|
||
|
TRY_TO_GENERATE_SIZED_LIST_PROPERTY("uchar", "uint8", boost::uint8_t, STD_INDEX_TYPE, T_INDEX_TYPE, INDEX_TYPE); \
|
||
|
else TRY_TO_GENERATE_SIZED_LIST_PROPERTY("ushort", "uint16", boost::uint16_t, STD_INDEX_TYPE, T_INDEX_TYPE, INDEX_TYPE); \
|
||
|
else TRY_TO_GENERATE_SIZED_LIST_PROPERTY("uint", "uint32", boost::uint32_t, STD_INDEX_TYPE, T_INDEX_TYPE, INDEX_TYPE)
|
||
|
|
||
|
|
||
|
namespace CGAL {
|
||
|
|
||
|
|
||
|
// PLY types:
|
||
|
// name type number of bytes
|
||
|
// ---------------------------------------
|
||
|
// char character 1
|
||
|
// uchar unsigned character 1
|
||
|
// short short integer 2
|
||
|
// ushort unsigned short integer 2
|
||
|
// int integer 4
|
||
|
// uint unsigned integer 4
|
||
|
// float single-precision float 4
|
||
|
// double double-precision float 8
|
||
|
|
||
|
/**
|
||
|
\ingroup PkgPointSetProcessingIOPly
|
||
|
|
||
|
Class used to identify a %PLY property as a type and a name.
|
||
|
|
||
|
\sa `read_ply_points_with_properties()`
|
||
|
*/
|
||
|
template <typename T>
|
||
|
struct PLY_property
|
||
|
{
|
||
|
typedef T type;
|
||
|
const char* name;
|
||
|
PLY_property (const char* name) : name (name) { }
|
||
|
};
|
||
|
|
||
|
/// \cond SKIP_IN_MANUAL
|
||
|
template <typename PointOrVectorMap>
|
||
|
struct GetFTFromMap
|
||
|
{
|
||
|
typedef typename Kernel_traits<typename boost::property_traits<PointOrVectorMap>::value_type>::Kernel::FT type;
|
||
|
};
|
||
|
/// \endcond
|
||
|
|
||
|
/**
|
||
|
\ingroup PkgPointSetProcessingIOPly
|
||
|
|
||
|
Generates a %PLY property handler to read 3D points. Points are
|
||
|
constructed from the input using 3 %PLY properties of type `FT`
|
||
|
and named `x`, `y` and `z`. `FT` is `float` if the points use
|
||
|
`CGAL::Simple_cartesian<float>` and `double` otherwise.
|
||
|
|
||
|
\sa `read_ply_points_with_properties()`
|
||
|
|
||
|
\tparam PointMap the property map used to store points.
|
||
|
*/
|
||
|
template <typename PointMap>
|
||
|
#ifdef DOXYGEN_RUNNING
|
||
|
std::tuple<PointMap,
|
||
|
typename Kernel_traits<typename PointMap::value_type>::Kernel::Construct_point_3,
|
||
|
PLY_property<FT>, PLY_property<FT>, PLY_property<FT> >
|
||
|
#else
|
||
|
std::tuple<PointMap,
|
||
|
typename Kernel_traits<typename PointMap::value_type>::Kernel::Construct_point_3,
|
||
|
PLY_property<typename GetFTFromMap<PointMap>::type>,
|
||
|
PLY_property<typename GetFTFromMap<PointMap>::type>,
|
||
|
PLY_property<typename GetFTFromMap<PointMap>::type> >
|
||
|
#endif
|
||
|
make_ply_point_reader(PointMap point_map)
|
||
|
{
|
||
|
return std::make_tuple (point_map, typename Kernel_traits<typename PointMap::value_type>::Kernel::Construct_point_3(),
|
||
|
PLY_property<typename GetFTFromMap<PointMap>::type>("x"),
|
||
|
PLY_property<typename GetFTFromMap<PointMap>::type>("y"),
|
||
|
PLY_property<typename GetFTFromMap<PointMap>::type>("z"));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
\ingroup PkgPointSetProcessingIOPly
|
||
|
|
||
|
Generates a %PLY property handler to read 3D normal
|
||
|
vectors. Vectors are constructed from the input using 3 PLY
|
||
|
properties of type `FT` and named `nx`, `ny` and `nz`. `FT`
|
||
|
is `float` if the points use `CGAL::Simple_cartesian<float>` and
|
||
|
`double` otherwise.
|
||
|
|
||
|
\sa `read_ply_points_with_properties()`
|
||
|
|
||
|
\tparam VectorMap the property map used to store vectors.
|
||
|
*/
|
||
|
template <typename VectorMap>
|
||
|
#ifdef DOXYGEN_RUNNING
|
||
|
std::tuple<VectorMap,
|
||
|
typename Kernel_traits<typename VectorMap::value_type>::Kernel::Construct_vector_3,
|
||
|
PLY_property<FT>, PLY_property<FT>, PLY_property<FT> >
|
||
|
#else
|
||
|
std::tuple<VectorMap,
|
||
|
typename Kernel_traits<typename VectorMap::value_type>::Kernel::Construct_vector_3,
|
||
|
PLY_property<typename GetFTFromMap<VectorMap>::type>,
|
||
|
PLY_property<typename GetFTFromMap<VectorMap>::type>,
|
||
|
PLY_property<typename GetFTFromMap<VectorMap>::type> >
|
||
|
#endif
|
||
|
make_ply_normal_reader(VectorMap normal_map)
|
||
|
{
|
||
|
return std::make_tuple (normal_map, typename Kernel_traits<typename VectorMap::value_type>::Kernel::Construct_vector_3(),
|
||
|
PLY_property<typename GetFTFromMap<VectorMap>::type>("nx"),
|
||
|
PLY_property<typename GetFTFromMap<VectorMap>::type>("ny"),
|
||
|
PLY_property<typename GetFTFromMap<VectorMap>::type>("nz"));
|
||
|
}
|
||
|
|
||
|
/// \cond SKIP_IN_MANUAL
|
||
|
|
||
|
namespace internal {
|
||
|
|
||
|
namespace PLY {
|
||
|
|
||
|
class PLY_read_number
|
||
|
{
|
||
|
protected:
|
||
|
std::string m_name;
|
||
|
std::size_t m_format;
|
||
|
|
||
|
public:
|
||
|
PLY_read_number (std::string name, std::size_t format)
|
||
|
: m_name (name), m_format (format) { }
|
||
|
virtual ~PLY_read_number() { }
|
||
|
|
||
|
const std::string& name () const { return m_name; }
|
||
|
|
||
|
virtual void get (std::istream& stream) const = 0;
|
||
|
|
||
|
// The two following functions prevent the stream to only extract
|
||
|
// ONE character (= what the types char imply) by requiring
|
||
|
// explicitely an integer object when reading the stream
|
||
|
void read_ascii (std::istream& stream, char& c) const
|
||
|
{
|
||
|
short s;
|
||
|
stream >> s;
|
||
|
c = static_cast<char>(s);
|
||
|
}
|
||
|
void read_ascii (std::istream& stream, signed char& c) const
|
||
|
{
|
||
|
short s;
|
||
|
stream >> s;
|
||
|
c = static_cast<signed char>(s);
|
||
|
}
|
||
|
void read_ascii (std::istream& stream, unsigned char& c) const
|
||
|
{
|
||
|
unsigned short s;
|
||
|
stream >> s;
|
||
|
c = static_cast<unsigned char>(s);
|
||
|
}
|
||
|
|
||
|
void read_ascii (std::istream& stream, float& t) const
|
||
|
{
|
||
|
stream >> iformat(t);
|
||
|
}
|
||
|
|
||
|
void read_ascii (std::istream& stream, double& t) const
|
||
|
{
|
||
|
stream >> iformat(t);
|
||
|
}
|
||
|
|
||
|
// Default template when Type is not a char type
|
||
|
template <typename Type>
|
||
|
void read_ascii (std::istream& stream, Type& t) const
|
||
|
{
|
||
|
stream >> t;
|
||
|
}
|
||
|
|
||
|
|
||
|
template <typename Type>
|
||
|
Type read (std::istream& stream) const
|
||
|
{
|
||
|
if (m_format == 0) // Ascii
|
||
|
{
|
||
|
Type t;
|
||
|
read_ascii (stream, t);
|
||
|
return t;
|
||
|
}
|
||
|
else // Binary (2 = little endian)
|
||
|
{
|
||
|
union
|
||
|
{
|
||
|
char uChar[sizeof (Type)];
|
||
|
Type type;
|
||
|
} buffer;
|
||
|
|
||
|
std::size_t size = sizeof (Type);
|
||
|
|
||
|
stream.read(buffer.uChar, size);
|
||
|
|
||
|
if (m_format == 2) // Big endian
|
||
|
{
|
||
|
for (std::size_t i = 0; i < size / 2; ++ i)
|
||
|
{
|
||
|
unsigned char tmp = buffer.uChar[i];
|
||
|
buffer.uChar[i] = buffer.uChar[size - 1 - i];
|
||
|
buffer.uChar[size - 1 - i] = tmp;
|
||
|
}
|
||
|
}
|
||
|
return buffer.type;
|
||
|
}
|
||
|
return Type();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <typename Type>
|
||
|
class PLY_read_typed_number : public PLY_read_number
|
||
|
{
|
||
|
mutable Type m_buffer;
|
||
|
public:
|
||
|
PLY_read_typed_number (std::string name, std::size_t format)
|
||
|
: PLY_read_number (name, format)
|
||
|
{
|
||
|
}
|
||
|
void get (std::istream& stream) const
|
||
|
{
|
||
|
m_buffer = (this->read<Type> (stream));
|
||
|
}
|
||
|
const Type& buffer() const
|
||
|
{
|
||
|
return m_buffer;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <typename Type>
|
||
|
class PLY_read_typed_list : public PLY_read_number
|
||
|
{
|
||
|
protected:
|
||
|
mutable std::vector<Type> m_buffer;
|
||
|
public:
|
||
|
PLY_read_typed_list (std::string name, std::size_t format)
|
||
|
: PLY_read_number (name, format)
|
||
|
{
|
||
|
}
|
||
|
virtual void get (std::istream& stream) const = 0;
|
||
|
|
||
|
const std::vector<Type>& buffer() const
|
||
|
{
|
||
|
return m_buffer;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <typename SizeType, typename IndexType>
|
||
|
class PLY_read_typed_list_with_typed_size
|
||
|
: public PLY_read_typed_list<IndexType>
|
||
|
{
|
||
|
|
||
|
public:
|
||
|
PLY_read_typed_list_with_typed_size (std::string name, std::size_t format)
|
||
|
: PLY_read_typed_list<IndexType> (name, format)
|
||
|
{
|
||
|
}
|
||
|
void get (std::istream& stream) const
|
||
|
{
|
||
|
std::size_t size = static_cast<std::size_t>(this->template read<SizeType>(stream));
|
||
|
this->m_buffer.resize (size);
|
||
|
for (std::size_t i = 0; i < size; ++ i)
|
||
|
this->m_buffer[i] = this->template read<IndexType> (stream);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
class PLY_reader
|
||
|
{
|
||
|
std::vector<PLY_read_number*> m_point_properties;
|
||
|
std::vector<PLY_read_number*> m_face_properties;
|
||
|
std::vector<PLY_read_number*>* m_properties;
|
||
|
std::string m_comments;
|
||
|
|
||
|
public:
|
||
|
std::size_t m_nb_points;
|
||
|
std::size_t m_nb_faces;
|
||
|
|
||
|
PLY_reader () : m_properties (&m_point_properties), m_nb_points (0), m_nb_faces(0) { }
|
||
|
|
||
|
const std::vector<PLY_read_number*>& readers() const { return *m_properties; }
|
||
|
void read_faces()
|
||
|
{
|
||
|
m_properties = &m_face_properties;
|
||
|
}
|
||
|
|
||
|
const std::string& comments() const { return m_comments; }
|
||
|
|
||
|
template <typename Stream>
|
||
|
bool init (Stream& stream)
|
||
|
{
|
||
|
std::size_t lineNumber = 0; // current line number
|
||
|
enum Format { ASCII = 0, BINARY_LITTLE_ENDIAN = 1, BINARY_BIG_ENDIAN = 2};
|
||
|
Format format = ASCII;
|
||
|
|
||
|
std::string line;
|
||
|
std::istringstream iss;
|
||
|
|
||
|
// Check the order of the properties of the point set
|
||
|
bool reading_vertices = false, reading_faces = false;
|
||
|
|
||
|
while (getline (stream,line))
|
||
|
{
|
||
|
iss.clear();
|
||
|
iss.str (line);
|
||
|
++ lineNumber;
|
||
|
|
||
|
// Reads file signature on first line
|
||
|
if (lineNumber == 1)
|
||
|
{
|
||
|
std::string signature;
|
||
|
if (!(iss >> signature) || (signature != "ply"))
|
||
|
{
|
||
|
// if wrong file format
|
||
|
std::cerr << "Error: incorrect file format line " << lineNumber << " of file" << std::endl;
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Reads format on 2nd line
|
||
|
else if (lineNumber == 2)
|
||
|
{
|
||
|
std::string tag, format_string, version;
|
||
|
if ( !(iss >> tag >> format_string >> version) )
|
||
|
{
|
||
|
std::cerr << "Error line " << lineNumber << " of file" << std::endl;
|
||
|
return false;
|
||
|
}
|
||
|
if (format_string == "ascii") format = ASCII;
|
||
|
else if (format_string == "binary_little_endian") format = BINARY_LITTLE_ENDIAN;
|
||
|
else if (format_string == "binary_big_endian") format = BINARY_BIG_ENDIAN;
|
||
|
else
|
||
|
{
|
||
|
std::cerr << "Error: unknown file format \"" << format_string << "\" line " << lineNumber << std::endl;
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Comments and vertex properties
|
||
|
else
|
||
|
{
|
||
|
std::string keyword;
|
||
|
if (!(iss >> keyword))
|
||
|
{
|
||
|
std::cerr << "Error line " << lineNumber << " of file" << std::endl;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (keyword == "property")
|
||
|
{
|
||
|
if (!reading_vertices && !reading_faces)
|
||
|
continue;
|
||
|
|
||
|
std::string type, name;
|
||
|
if (!(iss >> type >> name))
|
||
|
{
|
||
|
std::cerr << "Error line " << lineNumber << " of file" << std::endl;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
|
||
|
if (type == "list") // Special case
|
||
|
{
|
||
|
std::string size_type = name;
|
||
|
std::string index_type;
|
||
|
name.clear();
|
||
|
if (!(iss >> index_type >> name))
|
||
|
{
|
||
|
std::cerr << "Error line " << lineNumber << " of file" << std::endl;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
TRY_TO_GENERATE_LIST_PROPERTY ("char", "int8", boost::int8_t);
|
||
|
else TRY_TO_GENERATE_LIST_PROPERTY ("uchar", "uint8", boost::uint8_t);
|
||
|
else TRY_TO_GENERATE_LIST_PROPERTY ("short", "int16", boost::int16_t);
|
||
|
else TRY_TO_GENERATE_LIST_PROPERTY ("ushort", "uint16", boost::uint16_t);
|
||
|
else TRY_TO_GENERATE_LIST_PROPERTY ("int", "int32", boost::int32_t);
|
||
|
else TRY_TO_GENERATE_LIST_PROPERTY ("uint", "uint32", boost::uint32_t);
|
||
|
else TRY_TO_GENERATE_LIST_PROPERTY ("float", "float32", float);
|
||
|
else TRY_TO_GENERATE_LIST_PROPERTY ("double", "float64", double);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TRY_TO_GENERATE_PROPERTY ("char", "int8", boost::int8_t);
|
||
|
else TRY_TO_GENERATE_PROPERTY ("uchar", "uint8", boost::uint8_t);
|
||
|
else TRY_TO_GENERATE_PROPERTY ("short", "int16", boost::int16_t);
|
||
|
else TRY_TO_GENERATE_PROPERTY ("ushort", "uint16", boost::uint16_t);
|
||
|
else TRY_TO_GENERATE_PROPERTY ("int", "int32", boost::int32_t);
|
||
|
else TRY_TO_GENERATE_PROPERTY ("uint", "uint32", boost::uint32_t);
|
||
|
else TRY_TO_GENERATE_PROPERTY ("float", "float32", float);
|
||
|
else TRY_TO_GENERATE_PROPERTY ("double", "float64", double);
|
||
|
}
|
||
|
|
||
|
continue;
|
||
|
}
|
||
|
else if (keyword == "comment")
|
||
|
{
|
||
|
std::string str = iss.str();
|
||
|
if (str.size() > 8)
|
||
|
{
|
||
|
std::copy (str.begin() + 8, str.end(), std::back_inserter (m_comments));
|
||
|
m_comments += "\n";
|
||
|
}
|
||
|
}
|
||
|
// When end_header is reached, stop loop and begin reading points
|
||
|
else if (keyword == "end_header")
|
||
|
break;
|
||
|
else if (keyword == "element")
|
||
|
{
|
||
|
std::string type;
|
||
|
std::size_t number;
|
||
|
if (!(iss >> type >> number))
|
||
|
{
|
||
|
std::cerr << "Error line " << lineNumber << " of file" << std::endl;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (type == "vertex")
|
||
|
{
|
||
|
m_nb_points = number;
|
||
|
reading_vertices = true;
|
||
|
reading_faces = false;
|
||
|
}
|
||
|
|
||
|
else if (type == "face")
|
||
|
{
|
||
|
m_nb_faces = number;
|
||
|
reading_faces = true;
|
||
|
reading_vertices = false;
|
||
|
m_properties = &m_face_properties;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
reading_faces = false;
|
||
|
reading_vertices = false;
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
}
|
||
|
}
|
||
|
m_properties = &m_point_properties;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
~PLY_reader ()
|
||
|
{
|
||
|
for (std::size_t i = 0; i < m_point_properties.size (); ++ i)
|
||
|
delete m_point_properties[i];
|
||
|
m_point_properties.clear();
|
||
|
}
|
||
|
|
||
|
template <typename Type>
|
||
|
bool does_tag_exist (const char* tag)
|
||
|
{
|
||
|
return does_tag_exist (tag, Type());
|
||
|
}
|
||
|
|
||
|
template <typename Type>
|
||
|
void assign (Type& t, const char* tag)
|
||
|
{
|
||
|
for (std::size_t i = 0; i < m_properties->size (); ++ i)
|
||
|
if ((*m_properties)[i]->name () == tag)
|
||
|
{
|
||
|
PLY_read_typed_number<Type>*
|
||
|
reader = dynamic_cast<PLY_read_typed_number<Type>*>((*m_properties)[i]);
|
||
|
CGAL_assertion (reader != NULL);
|
||
|
t = reader->buffer();
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template <typename Type>
|
||
|
void assign (std::vector<Type>& t, const char* tag)
|
||
|
{
|
||
|
for (std::size_t i = 0; i < m_properties->size (); ++ i)
|
||
|
if ((*m_properties)[i]->name () == tag)
|
||
|
{
|
||
|
PLY_read_typed_list<Type>*
|
||
|
reader = dynamic_cast<PLY_read_typed_list<Type>*>((*m_properties)[i]);
|
||
|
CGAL_assertion (reader != NULL);
|
||
|
t = reader->buffer();
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template <typename Type>
|
||
|
bool does_tag_exist (const char* tag, const std::vector<Type>&)
|
||
|
{
|
||
|
for (std::size_t i = 0; i < m_properties->size (); ++ i)
|
||
|
if ((*m_properties)[i]->name () == tag)
|
||
|
return (dynamic_cast<PLY_read_typed_list<Type>*>((*m_properties)[i]) != NULL);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
template <typename Type>
|
||
|
bool does_tag_exist (const char* tag, Type)
|
||
|
{
|
||
|
for (std::size_t i = 0; i < m_properties->size (); ++ i)
|
||
|
if ((*m_properties)[i]->name () == tag)
|
||
|
return (dynamic_cast<PLY_read_typed_number<Type>*>((*m_properties)[i]) != NULL);
|
||
|
return false;
|
||
|
}
|
||
|
bool does_tag_exist (const char* tag, double)
|
||
|
{
|
||
|
for (std::size_t i = 0; i < m_properties->size (); ++ i)
|
||
|
if ((*m_properties)[i]->name () == tag)
|
||
|
return (dynamic_cast<PLY_read_typed_number<double>*>((*m_properties)[i]) != NULL
|
||
|
|| dynamic_cast<PLY_read_typed_number<float>*>((*m_properties)[i]) != NULL);
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
void assign (double& t, const char* tag)
|
||
|
{
|
||
|
for (std::size_t i = 0; i < m_properties->size (); ++ i)
|
||
|
if ((*m_properties)[i]->name () == tag)
|
||
|
{
|
||
|
PLY_read_typed_number<double>*
|
||
|
reader_double = dynamic_cast<PLY_read_typed_number<double>*>((*m_properties)[i]);
|
||
|
if (reader_double == NULL)
|
||
|
{
|
||
|
PLY_read_typed_number<float>*
|
||
|
reader_float = dynamic_cast<PLY_read_typed_number<float>*>((*m_properties)[i]);
|
||
|
CGAL_assertion (reader_float != NULL);
|
||
|
t = reader_float->buffer();
|
||
|
}
|
||
|
else
|
||
|
t = reader_double->buffer();
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
};
|
||
|
|
||
|
template <class Reader, class T>
|
||
|
void get_value(Reader& r, T& v, PLY_property<T>& wrapper)
|
||
|
{
|
||
|
return r.assign(v, wrapper.name);
|
||
|
}
|
||
|
|
||
|
|
||
|
template <std::size_t N>
|
||
|
struct Filler
|
||
|
{
|
||
|
template <class Reader, class Value_tuple, class PLY_property_tuple>
|
||
|
static void fill(Reader& r, Value_tuple& values, PLY_property_tuple wrappers)
|
||
|
{
|
||
|
get_value(r, std::get<N>(values), std::get<N+2>(wrappers));
|
||
|
Filler<N-1>::fill(r, values, wrappers);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template<int ...>
|
||
|
struct seq { };
|
||
|
|
||
|
template<int N, int ...S>
|
||
|
struct gens : gens<N-1, N-1, S...> { };
|
||
|
|
||
|
template<int ...S>
|
||
|
struct gens<0, S...> {
|
||
|
typedef seq<S...> type;
|
||
|
};
|
||
|
|
||
|
template<class ValueType, class Functor, class Tuple, int ...S>
|
||
|
ValueType call_functor(Functor f, Tuple t, seq<S...>) {
|
||
|
return f(std::get<S>(t) ...);
|
||
|
}
|
||
|
|
||
|
template <class ValueType, class Functor, typename ... T>
|
||
|
ValueType call_functor(Functor f, std::tuple<T...>& t)
|
||
|
{
|
||
|
return call_functor<ValueType>(f, t, typename gens<sizeof...(T)>::type());
|
||
|
}
|
||
|
|
||
|
template<>
|
||
|
struct Filler<0>
|
||
|
{
|
||
|
template <class Reader, class Value_tuple, class PLY_property_tuple>
|
||
|
static void fill(Reader& r, Value_tuple& values, PLY_property_tuple wrappers)
|
||
|
{
|
||
|
get_value(r, std::get<0>(values), std::get<2>(wrappers));
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <typename OutputValueType,
|
||
|
typename PropertyMap,
|
||
|
typename Constructor,
|
||
|
typename ... T>
|
||
|
void process_properties (PLY_reader& reader, OutputValueType& new_element,
|
||
|
std::tuple<PropertyMap, Constructor, PLY_property<T>...>&& current)
|
||
|
{
|
||
|
typedef typename PropertyMap::value_type PmapValueType;
|
||
|
std::tuple<T...> values;
|
||
|
Filler<sizeof...(T)-1>::fill(reader, values, current);
|
||
|
PmapValueType new_value = call_functor<PmapValueType>(std::get<1>(current), values);
|
||
|
put (std::get<0>(current), new_element, new_value);
|
||
|
}
|
||
|
|
||
|
template <typename OutputValueType,
|
||
|
typename PropertyMap,
|
||
|
typename Constructor,
|
||
|
typename ... T,
|
||
|
typename NextPropertyBinder,
|
||
|
typename ... PropertyMapBinders>
|
||
|
void process_properties (PLY_reader& reader, OutputValueType& new_element,
|
||
|
std::tuple<PropertyMap, Constructor, PLY_property<T>...>&& current,
|
||
|
NextPropertyBinder&& next,
|
||
|
PropertyMapBinders&& ... properties)
|
||
|
{
|
||
|
typedef typename PropertyMap::value_type PmapValueType;
|
||
|
std::tuple<T...> values;
|
||
|
Filler<sizeof...(T)-1>::fill(reader, values, current);
|
||
|
PmapValueType new_value = call_functor<PmapValueType>(std::get<1>(current), values);
|
||
|
put (std::get<0>(current), new_element, new_value);
|
||
|
|
||
|
process_properties (reader, new_element, std::forward<NextPropertyBinder>(next),
|
||
|
std::forward<PropertyMapBinders>(properties)...);
|
||
|
}
|
||
|
|
||
|
|
||
|
template <typename OutputValueType, typename PropertyMap, typename T>
|
||
|
void process_properties (PLY_reader& reader, OutputValueType& new_element,
|
||
|
std::pair<PropertyMap, PLY_property<T> >&& current)
|
||
|
{
|
||
|
T new_value = T();
|
||
|
reader.assign (new_value, current.second.name);
|
||
|
put (current.first, new_element, new_value);
|
||
|
}
|
||
|
|
||
|
template <typename OutputValueType, typename PropertyMap, typename T,
|
||
|
typename NextPropertyBinder, typename ... PropertyMapBinders>
|
||
|
void process_properties (PLY_reader& reader, OutputValueType& new_element,
|
||
|
std::pair<PropertyMap, PLY_property<T> >&& current,
|
||
|
NextPropertyBinder&& next,
|
||
|
PropertyMapBinders&& ... properties)
|
||
|
{
|
||
|
T new_value = T();
|
||
|
reader.assign (new_value, current.second.name);
|
||
|
put (current.first, new_element, new_value);
|
||
|
process_properties (reader, new_element, std::forward<NextPropertyBinder>(next),
|
||
|
std::forward<PropertyMapBinders>(properties)...);
|
||
|
}
|
||
|
|
||
|
} // namespace PLY
|
||
|
|
||
|
} // namespace internal
|
||
|
|
||
|
|
||
|
/// \endcond
|
||
|
|
||
|
|
||
|
/*
|
||
|
\ingroup PkgPointSetProcessingIOPly
|
||
|
|
||
|
Reads user-selected points properties from a .ply stream (ASCII or
|
||
|
binary).
|
||
|
Potential additional point properties and faces are ignored.
|
||
|
|
||
|
Properties are handled through a variadic list of property
|
||
|
handlers. A `PropertyHandler` can either be:
|
||
|
|
||
|
- A `std::pair<PropertyMap, PLY_property<T> >` if the user wants
|
||
|
to read a %PLY property as a scalar value T (for example, storing
|
||
|
an `int` %PLY property into an `int` variable).
|
||
|
|
||
|
- A `std::tuple<PropertyMap, Constructor,
|
||
|
PLY_property<T>...>` if the user wants to use one or several PLY
|
||
|
properties to construct a complex object (for example, storing 3
|
||
|
`uchar` %PLY properties into a %Color object that can for example
|
||
|
be a `CGAL::cpp11::array<unsigned char, 3>`). In that case, the
|
||
|
second element of the tuple should be a functor that constructs
|
||
|
the value type of `PropertyMap` from N objects of types `T`.
|
||
|
|
||
|
\sa `make_ply_point_reader()`
|
||
|
\sa `make_ply_normal_reader()`
|
||
|
|
||
|
\cgalRequiresCPP11
|
||
|
|
||
|
\tparam OutputIteratorValueType type of objects that can be put in `OutputIterator`.
|
||
|
It is default to `value_type_traits<OutputIterator>::%type` and can be omitted when the default is fine.
|
||
|
\tparam OutputIterator iterator over output points.
|
||
|
\tparam PropertyHandler handlers to recover properties.
|
||
|
|
||
|
\return `true` on success.
|
||
|
*/
|
||
|
template <typename OutputIteratorValueType,
|
||
|
typename OutputIterator,
|
||
|
typename ... PropertyHandler>
|
||
|
bool read_ply_points_with_properties (std::istream& stream,
|
||
|
OutputIterator output,
|
||
|
PropertyHandler&& ... properties)
|
||
|
{
|
||
|
typedef typename value_type_traits<OutputIterator>::type OutputValueType;
|
||
|
|
||
|
if(!stream)
|
||
|
{
|
||
|
std::cerr << "Error: cannot open file" << std::endl;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
internal::PLY::PLY_reader reader;
|
||
|
|
||
|
if (!(reader.init (stream)))
|
||
|
return false;
|
||
|
|
||
|
std::size_t points_read = 0;
|
||
|
|
||
|
while (!(stream.eof()) && points_read < reader.m_nb_points)
|
||
|
{
|
||
|
for (std::size_t i = 0; i < reader.readers().size (); ++ i)
|
||
|
reader.readers()[i]->get (stream);
|
||
|
|
||
|
OutputValueType new_element;
|
||
|
|
||
|
internal::PLY::process_properties (reader, new_element, std::forward<PropertyHandler>(properties)...);
|
||
|
|
||
|
*(output ++) = new_element;
|
||
|
|
||
|
++ points_read;
|
||
|
}
|
||
|
// Skip remaining lines
|
||
|
|
||
|
return (points_read == reader.m_nb_points);
|
||
|
}
|
||
|
|
||
|
/// \cond SKIP_IN_MANUAL
|
||
|
template <typename OutputIterator,
|
||
|
typename ... PropertyHandler>
|
||
|
bool read_ply_points_with_properties (std::istream& stream,
|
||
|
OutputIterator output,
|
||
|
PropertyHandler&& ... properties)
|
||
|
{
|
||
|
typedef typename value_type_traits<OutputIterator>::type OutputValueType;
|
||
|
|
||
|
return read_ply_points_with_properties<OutputValueType>
|
||
|
(stream, output, std::forward<PropertyHandler>(properties)...);
|
||
|
}
|
||
|
/// \endcond
|
||
|
|
||
|
/**
|
||
|
\ingroup PkgPointSetProcessingIOPly
|
||
|
Reads points (positions + normals, if available) from a .ply
|
||
|
stream (ASCII or binary).
|
||
|
Potential additional point properties and faces are ignored.
|
||
|
|
||
|
\tparam OutputIteratorValueType type of objects that can be put in `OutputIterator`.
|
||
|
It is default to `value_type_traits<OutputIterator>::%type` and can be omitted when the default is fine.
|
||
|
\tparam OutputIterator iterator over output points.
|
||
|
|
||
|
\param stream input stream.
|
||
|
\param output output iterator over points.
|
||
|
\param np optional sequence of \ref psp_namedparameters "Named Parameters" among the ones listed below.
|
||
|
|
||
|
\cgalNamedParamsBegin
|
||
|
\cgalParamBegin{point_map} a model of `WritablePropertyMap` with value type `geom_traits::Point_3`.
|
||
|
If this parameter is omitted, `CGAL::Identity_property_map<geom_traits::Point_3>` is used.\cgalParamEnd
|
||
|
\cgalParamBegin{normal_map} a model of `ReadWritePropertyMap` with value type
|
||
|
`geom_traits::Vector_3`. If this parameter is omitted, normals in the input stream are
|
||
|
ignored.\cgalParamEnd
|
||
|
\cgalParamBegin{geom_traits} an instance of a geometric traits class, model of `Kernel`\cgalParamEnd
|
||
|
\cgalNamedParamsEnd
|
||
|
|
||
|
\return true on success.
|
||
|
|
||
|
\cgalRequiresCPP11
|
||
|
*/
|
||
|
template < typename OutputIteratorValueType,
|
||
|
typename OutputIterator,
|
||
|
#ifdef DOXYGEN_RUNNING
|
||
|
typename NamedParameters
|
||
|
#else
|
||
|
typename CGAL_BGL_NP_TEMPLATE_PARAMETERS
|
||
|
#endif
|
||
|
>
|
||
|
bool read_ply_points(std::istream& stream,
|
||
|
OutputIterator output,
|
||
|
#ifdef DOXYGEN_RUNNING
|
||
|
const NamedParameters& np)
|
||
|
#else
|
||
|
const CGAL_BGL_NP_CLASS& np)
|
||
|
#endif
|
||
|
{
|
||
|
using boost::choose_param;
|
||
|
|
||
|
typedef Point_set_processing_3::Fake_point_range<OutputIteratorValueType> PointRange;
|
||
|
|
||
|
// basic geometric types
|
||
|
typedef typename Point_set_processing_3::GetPointMap<PointRange, CGAL_BGL_NP_CLASS>::type PointMap;
|
||
|
typedef typename Point_set_processing_3::GetNormalMap<PointRange, CGAL_BGL_NP_CLASS>::type NormalMap;
|
||
|
|
||
|
bool has_normals = !(boost::is_same<NormalMap,
|
||
|
typename Point_set_processing_3::GetNormalMap<PointRange, CGAL_BGL_NP_CLASS>::NoMap>::value);
|
||
|
|
||
|
PointMap point_map = choose_param(get_param(np, internal_np::point_map), PointMap());
|
||
|
NormalMap normal_map = choose_param(get_param(np, internal_np::normal_map), NormalMap());
|
||
|
|
||
|
if (has_normals)
|
||
|
return read_ply_points_with_properties (stream, output,
|
||
|
make_ply_point_reader (point_map),
|
||
|
make_ply_normal_reader (normal_map));
|
||
|
// else
|
||
|
return read_ply_points_with_properties (stream, output,
|
||
|
make_ply_point_reader (point_map));
|
||
|
}
|
||
|
|
||
|
|
||
|
/// \cond SKIP_IN_MANUAL
|
||
|
// variant with default NP
|
||
|
template <typename OutputIteratorValueType,
|
||
|
typename OutputIterator>
|
||
|
bool
|
||
|
read_ply_points(
|
||
|
std::istream& stream, ///< input stream.
|
||
|
OutputIterator output) ///< output iterator over points.
|
||
|
{
|
||
|
return read_ply_points<OutputIteratorValueType>
|
||
|
(stream, output, CGAL::parameters::all_default());
|
||
|
}
|
||
|
|
||
|
// variant with default output iterator value type
|
||
|
template <typename OutputIterator,
|
||
|
typename CGAL_BGL_NP_TEMPLATE_PARAMETERS>
|
||
|
bool
|
||
|
read_ply_points(
|
||
|
std::istream& stream, ///< input stream.
|
||
|
OutputIterator output,
|
||
|
const CGAL_BGL_NP_CLASS& np)
|
||
|
{
|
||
|
return read_ply_points<typename value_type_traits<OutputIterator>::type>
|
||
|
(stream, output, np);
|
||
|
}
|
||
|
|
||
|
// variant with default NP and output iterator value type
|
||
|
template <typename OutputIterator>
|
||
|
bool
|
||
|
read_ply_points(
|
||
|
std::istream& stream, ///< input stream.
|
||
|
OutputIterator output)
|
||
|
{
|
||
|
return read_ply_points<typename value_type_traits<OutputIterator>::type>
|
||
|
(stream, output, CGAL::parameters::all_default());
|
||
|
}
|
||
|
|
||
|
#ifndef CGAL_NO_DEPRECATED_CODE
|
||
|
// deprecated API
|
||
|
template < typename OutputIteratorValueType,
|
||
|
typename OutputIterator,
|
||
|
typename PointMap,
|
||
|
typename NormalMap >
|
||
|
CGAL_DEPRECATED_MSG("you are using the deprecated V1 API of CGAL::read_ply_points_and_normals(), please update your code")
|
||
|
bool read_ply_points_and_normals(std::istream& stream, ///< input stream.
|
||
|
OutputIterator output, ///< output iterator over points.
|
||
|
PointMap point_map, ///< property map: value_type of OutputIterator -> Point_3.
|
||
|
NormalMap normal_map) ///< property map: value_type of OutputIterator -> Vector_3.
|
||
|
{
|
||
|
return read_ply_points<OutputIteratorValueType>
|
||
|
(stream, output,
|
||
|
CGAL::parameters::point_map (point_map).
|
||
|
normal_map (normal_map));
|
||
|
}
|
||
|
|
||
|
// deprecated API
|
||
|
template < typename OutputIterator,
|
||
|
typename PointMap,
|
||
|
typename NormalMap >
|
||
|
CGAL_DEPRECATED_MSG("you are using the deprecated V1 API of CGAL::read_ply_points_and_normals(), please update your code")
|
||
|
bool read_ply_points_and_normals(std::istream& stream, ///< input stream.
|
||
|
OutputIterator output, ///< output iterator over points.
|
||
|
PointMap point_map, ///< property map: value_type of OutputIterator -> Point_3.
|
||
|
NormalMap normal_map) ///< property map: value_type of OutputIterator -> Vector_3.
|
||
|
{
|
||
|
return read_ply_points<typename value_type_traits<OutputIterator>::type>
|
||
|
(stream, output,
|
||
|
CGAL::parameters::point_map (point_map).
|
||
|
normal_map (normal_map));
|
||
|
}
|
||
|
|
||
|
// deprecated API
|
||
|
template < typename OutputIteratorValueType,
|
||
|
typename OutputIterator,
|
||
|
typename NormalMap >
|
||
|
CGAL_DEPRECATED_MSG("you are using the deprecated V1 API of CGAL::read_ply_points_and_normals(), please update your code")
|
||
|
bool read_ply_points_and_normals(std::istream& stream, ///< input stream.
|
||
|
OutputIterator output, ///< output iterator over points.
|
||
|
NormalMap normal_map) ///< property map: value_type of OutputIterator -> Vector_3.
|
||
|
{
|
||
|
return read_ply_points<OutputIteratorValueType>
|
||
|
(stream, output,
|
||
|
CGAL::parameters::normal_map (normal_map));
|
||
|
}
|
||
|
|
||
|
// deprecated API
|
||
|
template < typename OutputIterator,
|
||
|
typename NormalMap >
|
||
|
CGAL_DEPRECATED_MSG("you are using the deprecated V1 API of CGAL::read_ply_points_and_normals(), please update your code")
|
||
|
bool read_ply_points_and_normals(std::istream& stream, ///< input stream.
|
||
|
OutputIterator output, ///< output iterator over points.
|
||
|
NormalMap normal_map) ///< property map: value_type of OutputIterator -> Vector_3.
|
||
|
{
|
||
|
return read_ply_points<typename value_type_traits<OutputIterator>::type>
|
||
|
(stream, output,
|
||
|
CGAL::parameters::normal_map (normal_map));
|
||
|
}
|
||
|
|
||
|
// deprecated API
|
||
|
template <typename OutputIteratorValueType,
|
||
|
typename OutputIterator,
|
||
|
typename PointMap
|
||
|
>
|
||
|
CGAL_DEPRECATED_MSG("you are using the deprecated V1 API of CGAL::read_ply_points(), please update your code")
|
||
|
bool
|
||
|
read_ply_points(
|
||
|
std::istream& stream, ///< input stream.
|
||
|
OutputIterator output, ///< output iterator over points.
|
||
|
PointMap point_map) ///< property map: value_type of OutputIterator -> Point_3.
|
||
|
{
|
||
|
return read_ply_points<OutputIteratorValueType>
|
||
|
(stream, output,
|
||
|
CGAL::parameters::point_map (point_map));
|
||
|
}
|
||
|
|
||
|
// deprecated API
|
||
|
template < typename OutputIterator,
|
||
|
typename PointMap >
|
||
|
CGAL_DEPRECATED_MSG("you are using the deprecated V1 API of CGAL::read_ply_points(), please update your code")
|
||
|
bool read_ply_points(std::istream& stream, ///< input stream.
|
||
|
OutputIterator output, ///< output iterator over points.
|
||
|
PointMap point_map) ///< property map: value_type of OutputIterator -> Point_3.
|
||
|
{
|
||
|
return read_ply_points<typename value_type_traits<OutputIterator>::type>
|
||
|
(stream, output,
|
||
|
CGAL::parameters::point_map (point_map));
|
||
|
}
|
||
|
#endif // CGAL_NO_DEPRECATED_CODE
|
||
|
/// \endcond
|
||
|
|
||
|
|
||
|
} //namespace CGAL
|
||
|
|
||
|
#undef TRY_TO_GENERATE_POINT_PROPERTY
|
||
|
#undef TRY_TO_GENERATE_SIZED_FACE_PROPERTY
|
||
|
#undef TRY_TO_GENERATE_FACE_PROPERTY
|
||
|
|
||
|
#endif // CGAL_READ_PLY_POINTS_H
|