diff --git a/Point_set_3/include/CGAL/Point_set_3/IO.h b/Point_set_3/include/CGAL/Point_set_3/IO.h index fe07f22690d..d149751c23b 100644 --- a/Point_set_3/include/CGAL/Point_set_3/IO.h +++ b/Point_set_3/include/CGAL/Point_set_3/IO.h @@ -35,87 +35,12 @@ #include #include #endif // LAS -#include -#include +#include namespace CGAL { namespace internal { - - template - class Abstract_property_printer - { - public: - virtual ~Abstract_property_printer() { } - virtual void print (std::ostream& stream, const typename CGAL::Point_set_3::Index& index) = 0; - }; - - template - class Property_printer : public Abstract_property_printer - { - typedef typename CGAL::Point_set_3 Point_set; - typedef typename Point_set::template Property_map Pmap; - Pmap m_pmap; - public: - Property_printer (const Pmap& pmap) : m_pmap (pmap) - { - - } - - virtual void print(std::ostream& stream, const typename CGAL::Point_set_3::Index& index) - { - stream << get(m_pmap, index); - } - }; - - template - class Simple_property_printer : public Abstract_property_printer - { - typedef typename CGAL::Point_set_3 Point_set; - typedef typename Point_set::template Property_map Pmap; - Pmap m_pmap; - public: - Simple_property_printer (const Pmap& pmap) : m_pmap (pmap) - { - - } - - virtual void print(std::ostream& stream, const typename CGAL::Point_set_3::Index& index) - { - if (get_mode(stream) == IO::ASCII) - stream << get(m_pmap, index); - else - { - Type t = get (m_pmap, index); - stream.write (reinterpret_cast(&t), sizeof(t)); - } - } - }; - - template - class Char_property_printer : public Abstract_property_printer - { - typedef typename CGAL::Point_set_3 Point_set; - typedef typename Point_set::template Property_map Pmap; - Pmap m_pmap; - public: - Char_property_printer (const Pmap& pmap) : m_pmap (pmap) - { - - } - - virtual void print(std::ostream& stream, const typename CGAL::Point_set_3::Index& index) - { - if (get_mode(stream) == IO::ASCII) - stream << int(get(m_pmap, index)); - else - { - Type t = get (m_pmap, index); - stream.write (reinterpret_cast(&t), sizeof(t)); - } - } - }; namespace PLY { @@ -365,19 +290,42 @@ read_off_point_set( } +/// \cond SKIP_IN_MANUAL +template +bool +read_ply_point_set( + std::istream& stream, ///< input stream. + CGAL::Point_set_3& point_set) ///< point set +{ + std::string dummy; + return read_ply_point_set (stream, point_set, dummy); +} + +/// \endcond + /*! \ingroup PkgPointSet3IO + + Reads a point set with properties from an input stream in Ascii or + Binary PLY format. + + - the operator reads the vertex `point` property; + - if three PLY properties `nx`, `ny` and `nz` with type `float` + or `double` are found, the normal map is added; + - if any other PLY property is found, a "[name]" property map is + added, where `[name]` is the name of the PLY property. + + The `comments` parameter can be omitted. If provided, it will be + used to store the potential comments found in the PLY + header. Each line starting by "comment " in the header is + appended to the `comments` string (without the "comment " word). */ template bool read_ply_point_set( std::istream& stream, ///< input stream. -#ifdef DOXYGEN_RUNNING - CGAL::Point_set_3& point_set) ///< point set -#else CGAL::Point_set_3& point_set, ///< point set - std::string* comments = NULL) ///< recover PLY comments -#endif + std::string& comments) ///< PLY comments. { if(!stream) { @@ -394,14 +342,14 @@ read_ply_point_set( return false; } - if (comments != NULL) - *comments = reader.comments(); + comments = reader.comments(); for (std::size_t i = 0; i < reader.number_of_elements(); ++ i) { internal::PLY::PLY_element& element = reader.element(i); - if (element.name() == "vertex" || element.name() == "vertices") + bool is_vertex = (element.name() == "vertex" || element.name() == "vertices"); + if (is_vertex) { point_set.reserve (element.number_of_items()); filler.instantiate_properties (element); @@ -417,7 +365,7 @@ read_ply_point_set( return false; } - if (element.name() == "vertex" || element.name() == "vertices") + if (is_vertex) filler.process_line (element); } } @@ -427,27 +375,46 @@ read_ply_point_set( /*! \ingroup PkgPointSet3IO + + Writes a point set with properties in an output stream in PLY + format. + + If found, the normal map is inserted to the stream. All other + properties with simple types are inserted in the stream. + + If provided, the `comments` string is included line by line in + the header of the PLY stream (each line will be precedeed by + "comment "). */ template bool write_ply_point_set( std::ostream& stream, ///< output stream. -#ifdef DOXYGEN_RUNNING - const CGAL::Point_set_3& point_set) ///< point set -#else - const CGAL::Point_set_3& point_set, ///< point set - std::string* comments = NULL) ///< write PLY comments -#endif + const CGAL::Point_set_3& point_set, ///< point set. + const std::string& comments = std::string()) ///< PLY comments. { typedef CGAL::Point_set_3 Point_set; + typedef typename Point_set::Index Index; + typedef typename Point_set::Point_map Point_map; + typedef typename Point_set::Vector_map Vector_map; + typedef typename Point_set::template Property_map Int8_map; + typedef typename Point_set::template Property_map Uint8_map; + typedef typename Point_set::template Property_map Int16_map; + typedef typename Point_set::template Property_map Uint16_map; + typedef typename Point_set::template Property_map Int32_map; + typedef typename Point_set::template Property_map Uint32_map; + typedef typename Point_set::template Property_map Int64_map; + typedef typename Point_set::template Property_map Uint64_map; + typedef typename Point_set::template Property_map Float_map; + typedef typename Point_set::template Property_map Double_map; stream << "ply" << std::endl << ((get_mode(stream) == IO::BINARY) ? "format binary_little_endian 1.0" : "format ascii 1.0") << std::endl << "comment Generated by the CGAL library" << std::endl; - if (comments != NULL) + if (comments != std::string()) { - std::istringstream iss (*comments); + std::istringstream iss (comments); std::string line; while (getline(iss, line)) { @@ -459,7 +426,7 @@ write_ply_point_set( stream << "element vertex " << point_set.number_of_points() << std::endl; std::vector prop = point_set.base().properties(); - std::vector*> printers; + std::vector*> printers; for (std::size_t i = 0; i < prop.size(); ++ i) { @@ -468,7 +435,7 @@ write_ply_point_set( if (prop[i] == "point") { - if (boost::is_same::type, float>::value) + if (boost::is_same::type, float>::value) { stream << "property float x" << std::endl << "property float y" << std::endl @@ -480,86 +447,125 @@ write_ply_point_set( << "property double y" << std::endl << "property double z" << std::endl; } - printers.push_back (new internal::Property_printer(point_set.point_map())); + printers.push_back (new internal::PLY::Property_printer(point_set.point_map())); continue; } if (prop[i] == "normal") { - stream << "property double nx" << std::endl - << "property double ny" << std::endl - << "property double nz" << std::endl; - printers.push_back (new internal::Property_printer(point_set.normal_map())); + if (boost::is_same::type, float>::value) + { + stream << "property float nx" << std::endl + << "property float ny" << std::endl + << "property float nz" << std::endl; + } + else + { + stream << "property double nx" << std::endl + << "property double ny" << std::endl + << "property double nz" << std::endl; + } + printers.push_back (new internal::PLY::Property_printer(point_set.normal_map())); continue; } bool okay = false; { - typename Point_set::template Property_map pmap; + Int8_map pmap; boost::tie (pmap, okay) = point_set.template property_map(prop[i]); if (okay) { stream << "property char " << prop[i] << std::endl; - printers.push_back (new internal::Char_property_printer(pmap)); + printers.push_back (new internal::PLY::Char_property_printer(pmap)); continue; } } { - typename Point_set::template Property_map pmap; + Uint8_map pmap; boost::tie (pmap, okay) = point_set.template property_map(prop[i]); if (okay) { stream << "property uchar " << prop[i] << std::endl; - printers.push_back (new internal::Char_property_printer(pmap)); + printers.push_back (new internal::PLY::Char_property_printer(pmap)); continue; } } { - typename Point_set::template Property_map pmap; + Int16_map pmap; boost::tie (pmap, okay) = point_set.template property_map(prop[i]); if (okay) { stream << "property short " << prop[i] << std::endl; - printers.push_back (new internal::Simple_property_printer(pmap)); + printers.push_back (new internal::PLY::Simple_property_printer(pmap)); continue; } } { - typename Point_set::template Property_map pmap; + Uint16_map pmap; boost::tie (pmap, okay) = point_set.template property_map(prop[i]); if (okay) { stream << "property ushort " << prop[i] << std::endl; - printers.push_back (new internal::Simple_property_printer(pmap)); + printers.push_back (new internal::PLY::Simple_property_printer(pmap)); continue; } } { - typename Point_set::template Property_map pmap; + Int32_map pmap; boost::tie (pmap, okay) = point_set.template property_map(prop[i]); if (okay) { stream << "property int " << prop[i] << std::endl; - printers.push_back (new internal::Simple_property_printer(pmap)); + printers.push_back (new internal::PLY::Simple_property_printer(pmap)); continue; } } { - typename Point_set::template Property_map pmap; + Uint32_map pmap; + boost::tie (pmap, okay) = point_set.template property_map(prop[i]); + if (okay) + { + stream << "property uint " << prop[i] << std::endl; + printers.push_back (new internal::PLY::Simple_property_printer(pmap)); + continue; + } + } + { + Int64_map pmap; + boost::tie (pmap, okay) = point_set.template property_map(prop[i]); + if (okay) + { + stream << "property int " << prop[i] << std::endl; + printers.push_back (new internal::PLY::Simple_property_printer(pmap)); + continue; + } + } + { + Uint64_map pmap; + boost::tie (pmap, okay) = point_set.template property_map(prop[i]); + if (okay) + { + stream << "property uint " << prop[i] << std::endl; + printers.push_back (new internal::PLY::Simple_property_printer(pmap)); + continue; + } + } + { + Float_map pmap; boost::tie (pmap, okay) = point_set.template property_map(prop[i]); if (okay) { stream << "property float " << prop[i] << std::endl; - printers.push_back (new internal::Simple_property_printer(pmap)); + printers.push_back (new internal::PLY::Simple_property_printer(pmap)); continue; } } { - typename Point_set::template Property_map pmap; + Double_map pmap; boost::tie (pmap, okay) = point_set.template property_map(prop[i]); if (okay) { stream << "property double " << prop[i] << std::endl; - printers.push_back (new internal::Simple_property_printer(pmap)); + printers.push_back (new internal::PLY::Simple_property_printer(pmap)); continue; } } diff --git a/Point_set_processing_3/include/CGAL/IO/read_ply_points.h b/Point_set_processing_3/include/CGAL/IO/read_ply_points.h index 988a69cc9bd..f993f17a766 100644 --- a/Point_set_processing_3/include/CGAL/IO/read_ply_points.h +++ b/Point_set_processing_3/include/CGAL/IO/read_ply_points.h @@ -27,6 +27,7 @@ #include +#include #include #include #include @@ -43,36 +44,9 @@ #include #include -#define TRY_TO_GENERATE_PROPERTY(STD_TYPE, T_TYPE, TYPE) \ - if (type == STD_TYPE || type == T_TYPE) \ - m_elements.back().add_property (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_elements.back().add_property (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 - +#ifdef DOXYGEN_RUNNING // Document some parts from Stream_support here for convenience /** \ingroup PkgPointSetProcessing3IOPly @@ -88,23 +62,6 @@ namespace CGAL { PLY_property (const char* name) : name (name) { } }; - /// \cond SKIP_IN_MANUAL - - // Use a double property for all kernels... - template struct Convert_FT { typedef double type; }; - // ...except if kernel uses type float - template <> struct Convert_FT { typedef float type; }; - - template - struct GetFTFromMap - { - typedef typename Convert_FT - ::value_type>::Kernel::FT>::type type; - }; - /// \endcond - /** \ingroup PkgPointSetProcessing3IOPly @@ -118,24 +75,10 @@ namespace CGAL { \tparam PointMap the property map used to store points. */ template -#ifdef DOXYGEN_RUNNING std::tuple::Kernel::Construct_point_3, PLY_property, PLY_property, PLY_property > -#else - std::tuple::Kernel::Construct_point_3, - PLY_property::type>, - PLY_property::type>, - PLY_property::type> > -#endif - make_ply_point_reader(PointMap point_map) - { - return std::make_tuple (point_map, typename Kernel_traits::Kernel::Construct_point_3(), - PLY_property::type>("x"), - PLY_property::type>("y"), - PLY_property::type>("z")); - } + make_ply_point_reader(PointMap point_map); /** \ingroup PkgPointSetProcessing3IOPly @@ -151,573 +94,11 @@ namespace CGAL { \tparam VectorMap the property map used to store vectors. */ template -#ifdef DOXYGEN_RUNNING std::tuple::Kernel::Construct_vector_3, PLY_property, PLY_property, PLY_property > -#else - std::tuple::Kernel::Construct_vector_3, - PLY_property::type>, - PLY_property::type>, - PLY_property::type> > -#endif - make_ply_normal_reader(VectorMap normal_map) - { - return std::make_tuple (normal_map, typename Kernel_traits::Kernel::Construct_vector_3(), - PLY_property::type>("nx"), - PLY_property::type>("ny"), - PLY_property::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(s); - } - void read_ascii (std::istream& stream, signed char& c) const - { - short s; - stream >> s; - c = static_cast(s); - } - void read_ascii (std::istream& stream, unsigned char& c) const - { - unsigned short s; - stream >> s; - c = static_cast(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 - void read_ascii (std::istream& stream, Type& t) const - { - stream >> t; - } - - - template - 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 - 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 (stream)); - } - const Type& buffer() const - { - return m_buffer; - } - }; - - template - class PLY_read_typed_list : public PLY_read_number - { - protected: - mutable std::vector 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& buffer() const - { - return m_buffer; - } - }; - - template - class PLY_read_typed_list_with_typed_size - : public PLY_read_typed_list - { - - public: - PLY_read_typed_list_with_typed_size (std::string name, std::size_t format) - : PLY_read_typed_list (name, format) - { - } - void get (std::istream& stream) const - { - std::size_t size = static_cast(this->template read(stream)); - this->m_buffer.resize (size); - for (std::size_t i = 0; i < size; ++ i) - this->m_buffer[i] = this->template read (stream); - } - }; - - class PLY_element - { - std::string m_name; - std::size_t m_number; - - std::vector m_properties; - public: - - PLY_element (const std::string& name, std::size_t number) - : m_name (name), m_number (number) - { } - - PLY_element (const PLY_element& other) - : m_name (other.m_name), m_number (other.m_number), m_properties (other.m_properties) - { - const_cast(other).m_properties.clear(); - } - - PLY_element& operator= (const PLY_element& other) - { - m_name = other.m_name; - m_number = other.m_number; - m_properties = other.m_properties; - const_cast(other).m_properties.clear(); - return *this; - } - - ~PLY_element() - { - for (std::size_t i = 0; i < m_properties.size(); ++ i) - delete m_properties[i]; - } - - const std::string& name() const { return m_name; } - std::size_t number_of_items() const { return m_number; } - std::size_t number_of_properties() const { return m_properties.size(); } - - PLY_read_number* property (std::size_t idx) { return m_properties[idx]; } - - void add_property (PLY_read_number* read_number) - { - m_properties.push_back (read_number); - } - - template - bool has_property (const char* tag) - { - return has_property (tag, Type()); - } - template - bool has_property (const char* tag, const std::vector&) - { - for (std::size_t i = 0; i < number_of_properties(); ++ i) - if (m_properties[i]->name () == tag) - return (dynamic_cast*>(m_properties[i]) != NULL); - return false; - } - - template - bool has_property (const char* tag, Type) - { - for (std::size_t i = 0; i < number_of_properties(); ++ i) - if (m_properties[i]->name () == tag) - return (dynamic_cast*>(m_properties[i]) != NULL); - return false; - } - bool has_property (const char* tag, double) - { - for (std::size_t i = 0; i < number_of_properties(); ++ i) - if (m_properties[i]->name () == tag) - return (dynamic_cast*>(m_properties[i]) != NULL - || dynamic_cast*>(m_properties[i]) != NULL); - - return false; - } - - template - void assign (Type& t, const char* tag) - { - for (std::size_t i = 0; i < number_of_properties (); ++ i) - if (m_properties[i]->name () == tag) - { - PLY_read_typed_number* - property = dynamic_cast*>(m_properties[i]); - CGAL_assertion (property != NULL); - t = property->buffer(); - return; - } - } - - template - void assign (std::vector& t, const char* tag) - { - for (std::size_t i = 0; i < number_of_properties (); ++ i) - if (m_properties[i]->name () == tag) - { - PLY_read_typed_list* - property = dynamic_cast*>(m_properties[i]); - CGAL_assertion (property != NULL); - t = property->buffer(); - return; - } - } - - void assign (double& t, const char* tag) - { - for (std::size_t i = 0; i < number_of_properties (); ++ i) - if (m_properties[i]->name () == tag) - { - PLY_read_typed_number* - property_double = dynamic_cast*>(m_properties[i]); - if (property_double == NULL) - { - PLY_read_typed_number* - property_float = dynamic_cast*>(m_properties[i]); - CGAL_assertion (property_float != NULL); - t = property_float->buffer(); - } - else - t = property_double->buffer(); - - return; - } - } - - }; - - class PLY_reader - { - std::vector m_elements; - std::string m_comments; - - public: - PLY_reader () { } - - std::size_t number_of_elements() const { return m_elements.size(); } - PLY_element& element (std::size_t idx) - { - return m_elements[idx]; - } - - const std::string& comments() const { return m_comments; } - - template - 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; - - 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") - { - 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"; - } - } - 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; - } - - m_elements.push_back (PLY_element(type, number)); - } - // When end_header is reached, stop loop and begin reading points - else if (keyword == "end_header") - break; - } - } - return true; - } - - ~PLY_reader () - { - } - - }; - - template - void get_value(Reader& r, T& v, PLY_property& wrapper) - { - return r.assign(v, wrapper.name); - } - - - template - struct Filler - { - template - static void fill(Reader& r, Value_tuple& values, PLY_property_tuple wrappers) - { - get_value(r, std::get(values), std::get(wrappers)); - Filler::fill(r, values, wrappers); - } - }; - - template - struct seq { }; - - template - struct gens : gens { }; - - template - struct gens<0, S...> { - typedef seq type; - }; - - template - ValueType call_functor(Functor f, Tuple t, seq) { - return f(std::get(t) ...); - } - - template - ValueType call_functor(Functor f, std::tuple& t) - { - return call_functor(f, t, typename gens::type()); - } - - template<> - struct Filler<0> - { - template - static void fill(Reader& r, Value_tuple& values, PLY_property_tuple wrappers) - { - get_value(r, std::get<0>(values), std::get<2>(wrappers)); - } - }; - - template - void process_properties (PLY_element& element, OutputValueType& new_element, - std::tuple...>&& current) - { - typedef typename PropertyMap::value_type PmapValueType; - std::tuple values; - Filler::fill(element, values, current); - PmapValueType new_value = call_functor(std::get<1>(current), values); - put (std::get<0>(current), new_element, new_value); - } - - template - void process_properties (PLY_element& element, OutputValueType& new_element, - std::tuple...>&& current, - NextPropertyBinder&& next, - PropertyMapBinders&& ... properties) - { - typedef typename PropertyMap::value_type PmapValueType; - std::tuple values; - Filler::fill(element, values, current); - PmapValueType new_value = call_functor(std::get<1>(current), values); - put (std::get<0>(current), new_element, new_value); - - process_properties (element, new_element, std::forward(next), - std::forward(properties)...); - } - - - template - void process_properties (PLY_element& element, OutputValueType& new_element, - std::pair >&& current) - { - T new_value = T(); - element.assign (new_value, current.second.name); - put (current.first, new_element, new_value); - } - - template - void process_properties (PLY_element& element, OutputValueType& new_element, - std::pair >&& current, - NextPropertyBinder&& next, - PropertyMapBinders&& ... properties) - { - T new_value = T(); - element.assign (new_value, current.second.name); - put (current.first, new_element, new_value); - process_properties (element, new_element, std::forward(next), - std::forward(properties)...); - } - - } // namespace PLY - -} // namespace internal - - - /// \endcond - + make_ply_normal_reader(VectorMap normal_map); +#endif // DOXYGEN_RUNNING /* \ingroup PkgPointSetProcessing3IOPly diff --git a/Point_set_processing_3/include/CGAL/IO/write_ply_points.h b/Point_set_processing_3/include/CGAL/IO/write_ply_points.h index e20d211252f..d8558d10b7a 100644 --- a/Point_set_processing_3/include/CGAL/IO/write_ply_points.h +++ b/Point_set_processing_3/include/CGAL/IO/write_ply_points.h @@ -27,9 +27,9 @@ #include +#include #include #include -#include #include #include @@ -42,6 +42,7 @@ namespace CGAL { +#ifdef DOXYGEN_RUNNING // Document some parts from Stream_support here for convenience /** \ingroup PkgPointSetProcessing3IOPly @@ -55,21 +56,8 @@ namespace CGAL { \tparam PointMap the property map used to store points. */ template -#ifdef DOXYGEN_RUNNING std::tuple, PLY_property, PLY_property > -#else - std::tuple::type>, - PLY_property::type>, - PLY_property::type> > -#endif - make_ply_point_writer(PointMap point_map) - { - return std::make_tuple (point_map, - PLY_property::type>("x"), - PLY_property::type>("y"), - PLY_property::type>("z")); - } + make_ply_point_writer(PointMap point_map); /** \ingroup PkgPointSetProcessing3IOPly @@ -84,21 +72,9 @@ namespace CGAL { \tparam VectorMap the property map used to store vectors. */ template -#ifdef DOXYGEN_RUNNING std::tuple, PLY_property, PLY_property > -#else - std::tuple::type>, - PLY_property::type>, - PLY_property::type> > + make_ply_normal_writer(VectorMap normal_map); #endif - make_ply_normal_writer(VectorMap normal_map) - { - return std::make_tuple (normal_map, - PLY_property::type>("nx"), - PLY_property::type>("ny"), - PLY_property::type>("nz")); - } /// \cond SKIP_IN_MANUAL @@ -106,219 +82,6 @@ namespace internal { namespace PLY { - template void property_header_type (std::ostream& stream) - { - CGAL_assertion_msg (false, "Unknown PLY type"); - stream << "undefined_type"; - } - - template <> void property_header_type (std::ostream& stream) { stream << "char"; } - template <> void property_header_type (std::ostream& stream) { stream << "char"; } - template <> void property_header_type (std::ostream& stream) { stream << "uchar"; } - template <> void property_header_type (std::ostream& stream) { stream << "short"; } - template <> void property_header_type (std::ostream& stream) { stream << "ushort"; } - template <> void property_header_type (std::ostream& stream) { stream << "int"; } - template <> void property_header_type (std::ostream& stream) { stream << "uint"; } - template <> void property_header_type (std::ostream& stream) { stream << "float"; } - template <> void property_header_type (std::ostream& stream) { stream << "double"; } - - template - void property_header (std::ostream& stream, const PLY_property& prop) - { - stream << "property "; - property_header_type(stream); - stream << " " << prop.name << std::endl; - } - - template - void property_header (std::ostream& stream, const PLY_property >& prop) - { - stream << "property list uchar "; - property_header_type(stream); - stream << " " << prop.name << std::endl; - } - - - template - struct Properties_header - { - template - static void write(std::ostream& stream, PLY_property_tuple& wrappers) - { - Properties_header::write(stream, wrappers); - property_header (stream, std::get(wrappers)); - } - }; - template <> - struct Properties_header<0> - { - template - static void write(std::ostream& stream, PLY_property_tuple& wrappers) - { - property_header (stream, std::get<1>(wrappers)); - } - }; - - template - void output_property_header (std::ostream& stream, - std::tuple... >&& current) - { - Properties_header::write(stream, current); - } - - - template - void output_property_header (std::ostream& stream, - std::pair >&& current) - { - property_header (stream, current.second); - } - - template - void output_property_header (std::ostream& stream, - std::pair >&& current, - NextPropertyHandler&& next, - PropertyHandler&& ... properties) - { - property_header (stream, current.second); - output_property_header (stream, std::forward(next), - std::forward(properties)...); - } - template - void output_property_header (std::ostream& stream, - std::tuple... >&& current, - NextPropertyHandler&& next, - PropertyHandler&& ... properties) - { - Properties_header::write(stream, current); - output_property_header (stream, std::forward(next), - std::forward(properties)...); - } - - - template - void property_write (std::ostream& stream, ForwardIterator it, PropertyMap map) - { - stream << CGAL::oformat(get (map, *it)); - } - - template - T no_char_character (const T& t) { return t; } - int no_char_character (const char& t) { return int(t); } - int no_char_character (const signed char& t) { return int(t); } - int no_char_character (const unsigned char& t) { return int(t); } - - template - void simple_property_write (std::ostream& stream, ForwardIterator it, - std::pair > map) - { - if (CGAL::get_mode(stream) == IO::ASCII) - stream << no_char_character(get (map.first, *it)); - else - { - typename PropertyMap::value_type value = get(map.first, *it); - stream.write (reinterpret_cast(&value), sizeof(value)); - } - } - - template - void simple_property_write (std::ostream& stream, ForwardIterator it, - std::pair > > map) - { - const typename PropertyMap::reference value = get(map.first, *it); - - if (CGAL::get_mode(stream) == IO::ASCII) - { - stream << value.size(); - for (std::size_t i = 0; i < value.size(); ++ i) - stream << " " << no_char_character(value[i]); - } - else - { - unsigned char size = static_cast(value.size()); - stream.write (reinterpret_cast(&size), sizeof(size)); - for (std::size_t i = 0; i < value.size(); ++ i) - { - T t = T(value[i]); - stream.write (reinterpret_cast(&t), sizeof(t)); - } - } - } - - - template - void output_properties (std::ostream& stream, - ForwardIterator it, - std::tuple... >&& current) - { - property_write (stream, it, std::get<0>(current)); - if (get_mode(stream) == IO::ASCII) - stream << std::endl; - } - - - template - void output_properties (std::ostream& stream, - ForwardIterator it, - std::pair >&& current) - { - simple_property_write (stream, it, std::forward > >(current)); - if (get_mode(stream) == IO::ASCII) - stream << std::endl; - } - - template - void output_properties (std::ostream& stream, - ForwardIterator it, - std::pair >&& current, - NextPropertyHandler&& next, - PropertyHandler&& ... properties) - { - simple_property_write (stream, it, current); - if (get_mode(stream) == IO::ASCII) - stream << " "; - output_properties (stream, it, std::forward(next), - std::forward(properties)...); - } - - template - void output_properties (std::ostream& stream, - ForwardIterator it, - std::tuple... >&& current, - NextPropertyHandler&& next, - PropertyHandler&& ... properties) - { - property_write (stream, it, std::get<0>(current)); - if (get_mode(stream) == IO::ASCII) - stream << " "; - output_properties (stream, it, std::forward(next), - std::forward(properties)...); - } } // namespace PLY diff --git a/Polyhedron/demo/Polyhedron/Plugins/IO/PLY_io_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/IO/PLY_io_plugin.cpp index d6ff0c8da28..7f4f6aa2bca 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/IO/PLY_io_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/IO/PLY_io_plugin.cpp @@ -36,34 +36,6 @@ public: bool canSave(const CGAL::Three::Scene_item*); bool save(const CGAL::Three::Scene_item*, QFileInfo fileinfo); -private: - void set_vcolors(SMesh* smesh, const std::vector& colors) - { - typedef SMesh SMesh; - typedef boost::graph_traits::vertex_descriptor vertex_descriptor; - SMesh::Property_map vcolors = - smesh->property_map("v:color").first; - bool created; - boost::tie(vcolors, created) = smesh->add_property_map("v:color",CGAL::Color(0,0,0)); - assert(colors.size()==smesh->number_of_vertices()); - int color_id = 0; - for(vertex_descriptor vd : vertices(*smesh)) - vcolors[vd] = colors[color_id++]; - } - - void set_fcolors(SMesh* smesh, const std::vector& colors) - { - typedef SMesh SMesh; - typedef boost::graph_traits::face_descriptor face_descriptor; - SMesh::Property_map fcolors = - smesh->property_map("f:color").first; - bool created; - boost::tie(fcolors, created) = smesh->add_property_map("f:color",CGAL::Color(0,0,0)); - assert(colors.size()==smesh->number_of_faces()); - int color_id = 0; - for(face_descriptor fd : faces(*smesh)) - fcolors[fd] = colors[color_id++]; - } }; bool Polyhedron_demo_ply_plugin::canLoad() const { @@ -116,6 +88,23 @@ Polyhedron_demo_ply_plugin::load(QFileInfo fileinfo) { if (input_is_mesh) // Open mesh or polygon soup { + // First try mesh + SMesh *surface_mesh = new SMesh(); + std::string comments; + + if (CGAL::read_ply (in, *surface_mesh, comments)) + { + Scene_surface_mesh_item* sm_item = new Scene_surface_mesh_item(surface_mesh); + sm_item->setName(fileinfo.completeBaseName()); + sm_item->comments() = comments; + QApplication::restoreOverrideCursor(); + return sm_item; + } + + in.clear(); + in.seekg(0); + + // else try polygon soup std::vector points; std::vector > polygons; std::vector fcolors; @@ -127,29 +116,11 @@ Polyhedron_demo_ply_plugin::load(QFileInfo fileinfo) { return NULL; } - if (CGAL::Polygon_mesh_processing::is_polygon_soup_a_polygon_mesh (polygons)) - { - SMesh *surface_mesh = new SMesh(); - CGAL::Polygon_mesh_processing::polygon_soup_to_polygon_mesh (points, polygons, - *surface_mesh); - if(!(vcolors.empty())) - set_vcolors(surface_mesh, vcolors); - if(!(fcolors.empty())) - set_fcolors(surface_mesh, fcolors); - - Scene_surface_mesh_item* sm_item = new Scene_surface_mesh_item(surface_mesh); - sm_item->setName(fileinfo.completeBaseName()); - QApplication::restoreOverrideCursor(); - return sm_item; - } - else - { - Scene_polygon_soup_item* soup_item = new Scene_polygon_soup_item; - soup_item->setName(fileinfo.completeBaseName()); - soup_item->load (points, polygons, fcolors, vcolors); - QApplication::restoreOverrideCursor(); - return soup_item; - } + Scene_polygon_soup_item* soup_item = new Scene_polygon_soup_item; + soup_item->setName(fileinfo.completeBaseName()); + soup_item->load (points, polygons, fcolors, vcolors); + QApplication::restoreOverrideCursor(); + return soup_item; } else // Open point set { @@ -212,10 +183,11 @@ bool Polyhedron_demo_ply_plugin::save(const CGAL::Three::Scene_item* item, QFile return CGAL::write_PLY (out, soup_item->points(), soup_item->polygons()); // This plugin supports surface meshes - const Scene_surface_mesh_item* sm_item = - qobject_cast(item); + Scene_surface_mesh_item* sm_item = + const_cast(qobject_cast(item)); if (sm_item) - return CGAL::write_PLY (out, *(sm_item->polyhedron())); + return CGAL::write_ply (out, *(sm_item->polyhedron()), sm_item->comments()); + return false; } diff --git a/Polyhedron/demo/Polyhedron/Scene_points_with_normal_item.cpp b/Polyhedron/demo/Polyhedron/Scene_points_with_normal_item.cpp index b3cd0312bc8..5e449e47f78 100644 --- a/Polyhedron/demo/Polyhedron/Scene_points_with_normal_item.cpp +++ b/Polyhedron/demo/Polyhedron/Scene_points_with_normal_item.cpp @@ -619,7 +619,7 @@ bool Scene_points_with_normal_item::read_ply_point_set(std::istream& stream) d->m_points->clear(); bool ok = stream && - CGAL::read_ply_point_set (stream, *(d->m_points), &(d->m_comments)) && + CGAL::read_ply_point_set (stream, *(d->m_points), d->m_comments) && !isEmpty(); d->point_Slider->setValue(CGAL::Three::Three::getDefaultPointSize()); std::cerr << d->m_points->info(); @@ -653,7 +653,7 @@ bool Scene_points_with_normal_item::write_ply_point_set(std::ostream& stream, bo if (binary) CGAL::set_binary_mode (stream); - CGAL::write_ply_point_set (stream, *(d->m_points), &(d->m_comments)); + CGAL::write_ply_point_set (stream, *(d->m_points), d->m_comments); return true; } diff --git a/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp b/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp index bc3d5b6a551..bc212838599 100644 --- a/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp +++ b/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp @@ -211,6 +211,8 @@ struct Scene_surface_mesh_item_priv{ mutable QList text_ids; mutable std::vector targeted_id; + std::string comments; + mutable bool has_fpatch_id; mutable bool has_feature_edges; mutable bool floated; @@ -1050,6 +1052,9 @@ Scene_surface_mesh_item::~Scene_surface_mesh_item() SMesh* Scene_surface_mesh_item::polyhedron() { return d->smesh_; } const SMesh* Scene_surface_mesh_item::polyhedron() const { return d->smesh_; } +std::string& Scene_surface_mesh_item::comments() { return d->comments; } +const std::string& Scene_surface_mesh_item::comments() const { return d->comments; } + void Scene_surface_mesh_item::compute_bbox()const { SMesh::Property_map pprop = d->smesh_->points(); diff --git a/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.h b/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.h index 6f2bc777a61..375b57b1380 100644 --- a/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.h +++ b/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.h @@ -89,6 +89,10 @@ public: Face_graph* face_graph() { return polyhedron(); } const Face_graph* face_graph() const { return polyhedron(); } + // Gets PLY comments (empty if mesh not originated from PLY input) + std::string& comments(); + const std::string& comments() const; + void invalidate_aabb_tree(); void invalidateOpenGLBuffers()Q_DECL_OVERRIDE; void invalidate(Gl_data_names name); diff --git a/Polyhedron_IO/include/CGAL/IO/PLY_reader.h b/Polyhedron_IO/include/CGAL/IO/PLY_reader.h index 26e04dea6cc..904a00f29b6 100644 --- a/Polyhedron_IO/include/CGAL/IO/PLY_reader.h +++ b/Polyhedron_IO/include/CGAL/IO/PLY_reader.h @@ -20,7 +20,7 @@ #ifndef CGAL_IO_PLY_READER_H #define CGAL_IO_PLY_READER_H -#include +#include namespace CGAL{ diff --git a/Polyhedron_IO/include/CGAL/IO/PLY_writer.h b/Polyhedron_IO/include/CGAL/IO/PLY_writer.h index 525b940ebd3..6b563d7ca29 100644 --- a/Polyhedron_IO/include/CGAL/IO/PLY_writer.h +++ b/Polyhedron_IO/include/CGAL/IO/PLY_writer.h @@ -20,7 +20,7 @@ #ifndef CGAL_IO_PLY_WRITER_H #define CGAL_IO_PLY_WRITER_H -#include +#include namespace CGAL{ diff --git a/Stream_support/include/CGAL/IO/PLY.h b/Stream_support/include/CGAL/IO/PLY.h new file mode 100644 index 00000000000..47d89ea9422 --- /dev/null +++ b/Stream_support/include/CGAL/IO/PLY.h @@ -0,0 +1,973 @@ +// 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_IO_PLY_H +#define CGAL_IO_PLY_H + +#include +#include + +#include +#include +#include +#include + +#define TRY_TO_GENERATE_PROPERTY(STD_TYPE, T_TYPE, TYPE) \ + if (type == STD_TYPE || type == T_TYPE) \ + m_elements.back().add_property (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_elements.back().add_property (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) + + +/// \cond SKIP_IN_MANUAL + +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 + +template +struct PLY_property +{ + typedef T type; + const char* name; + PLY_property (const char* name) : name (name) { } +}; + +// Use a double property for all kernels... +template struct Convert_FT { typedef double type; }; +// ...except if kernel uses type float +template <> struct Convert_FT { typedef float type; }; + +template +struct Get_FT_from_map +{ + typedef typename Convert_FT + ::value_type>::Kernel::FT>::type type; +}; + +template +std::tuple::Kernel::Construct_point_3, + PLY_property::type>, + PLY_property::type>, + PLY_property::type> > +make_ply_point_reader(PointMap point_map) +{ + return std::make_tuple (point_map, typename Kernel_traits::Kernel::Construct_point_3(), + PLY_property::type>("x"), + PLY_property::type>("y"), + PLY_property::type>("z")); +} + +template +std::tuple::Kernel::Construct_vector_3, + PLY_property::type>, + PLY_property::type>, + PLY_property::type> > +make_ply_normal_reader(VectorMap normal_map) +{ + return std::make_tuple (normal_map, typename Kernel_traits::Kernel::Construct_vector_3(), + PLY_property::type>("nx"), + PLY_property::type>("ny"), + PLY_property::type>("nz")); +} + +template +std::tuple::type>, + PLY_property::type>, + PLY_property::type> > +make_ply_point_writer(PointMap point_map) +{ + return std::make_tuple (point_map, + PLY_property::type>("x"), + PLY_property::type>("y"), + PLY_property::type>("z")); +} + +template +std::tuple::type>, + PLY_property::type>, + PLY_property::type> > +make_ply_normal_writer(VectorMap normal_map) +{ + return std::make_tuple (normal_map, + PLY_property::type>("nx"), + PLY_property::type>("ny"), + PLY_property::type>("nz")); +} + +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(s); + } + void read_ascii (std::istream& stream, signed char& c) const + { + short s; + stream >> s; + c = static_cast(s); + } + void read_ascii (std::istream& stream, unsigned char& c) const + { + unsigned short s; + stream >> s; + c = static_cast(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 + void read_ascii (std::istream& stream, Type& t) const + { + stream >> t; + } + + + template + 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 +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 (stream)); + } + const Type& buffer() const + { + return m_buffer; + } +}; + +template +class PLY_read_typed_list : public PLY_read_number +{ +protected: + mutable std::vector 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& buffer() const + { + return m_buffer; + } +}; + +template +class PLY_read_typed_list_with_typed_size + : public PLY_read_typed_list +{ + +public: + PLY_read_typed_list_with_typed_size (std::string name, std::size_t format) + : PLY_read_typed_list (name, format) + { + } + void get (std::istream& stream) const + { + std::size_t size = static_cast(this->template read(stream)); + this->m_buffer.resize (size); + for (std::size_t i = 0; i < size; ++ i) + this->m_buffer[i] = this->template read (stream); + } +}; + +class PLY_element +{ + std::string m_name; + std::size_t m_number; + + std::vector m_properties; +public: + + PLY_element (const std::string& name, std::size_t number) + : m_name (name), m_number (number) + { } + + PLY_element (const PLY_element& other) + : m_name (other.m_name), m_number (other.m_number), m_properties (other.m_properties) + { + const_cast(other).m_properties.clear(); + } + + PLY_element& operator= (const PLY_element& other) + { + m_name = other.m_name; + m_number = other.m_number; + m_properties = other.m_properties; + const_cast(other).m_properties.clear(); + return *this; + } + + ~PLY_element() + { + for (std::size_t i = 0; i < m_properties.size(); ++ i) + delete m_properties[i]; + } + + const std::string& name() const { return m_name; } + std::size_t number_of_items() const { return m_number; } + std::size_t number_of_properties() const { return m_properties.size(); } + + PLY_read_number* property (std::size_t idx) { return m_properties[idx]; } + + void add_property (PLY_read_number* read_number) + { + m_properties.push_back (read_number); + } + + template + bool has_property (const char* tag) + { + return has_property (tag, Type()); + } + template + bool has_property (const char* tag, const std::vector&) + { + for (std::size_t i = 0; i < number_of_properties(); ++ i) + if (m_properties[i]->name () == tag) + return (dynamic_cast*>(m_properties[i]) != NULL); + return false; + } + + template + bool has_property (const char* tag, Type) + { + for (std::size_t i = 0; i < number_of_properties(); ++ i) + if (m_properties[i]->name () == tag) + return (dynamic_cast*>(m_properties[i]) != NULL); + return false; + } + bool has_property (const char* tag, double) + { + for (std::size_t i = 0; i < number_of_properties(); ++ i) + if (m_properties[i]->name () == tag) + return (dynamic_cast*>(m_properties[i]) != NULL + || dynamic_cast*>(m_properties[i]) != NULL); + + return false; + } + + template + void assign (Type& t, const char* tag) + { + for (std::size_t i = 0; i < number_of_properties (); ++ i) + if (m_properties[i]->name () == tag) + { + PLY_read_typed_number* + property = dynamic_cast*>(m_properties[i]); + CGAL_assertion (property != NULL); + t = property->buffer(); + return; + } + } + + template + void assign (std::vector& t, const char* tag) + { + for (std::size_t i = 0; i < number_of_properties (); ++ i) + if (m_properties[i]->name () == tag) + { + PLY_read_typed_list* + property = dynamic_cast*>(m_properties[i]); + CGAL_assertion (property != NULL); + t = property->buffer(); + return; + } + } + + void assign (double& t, const char* tag) + { + for (std::size_t i = 0; i < number_of_properties (); ++ i) + if (m_properties[i]->name () == tag) + { + PLY_read_typed_number* + property_double = dynamic_cast*>(m_properties[i]); + if (property_double == NULL) + { + PLY_read_typed_number* + property_float = dynamic_cast*>(m_properties[i]); + CGAL_assertion (property_float != NULL); + t = property_float->buffer(); + } + else + t = property_double->buffer(); + + return; + } + } + +}; + +class PLY_reader +{ + std::vector m_elements; + std::string m_comments; + +public: + PLY_reader () { } + + std::size_t number_of_elements() const { return m_elements.size(); } + PLY_element& element (std::size_t idx) + { + return m_elements[idx]; + } + + const std::string& comments() const { return m_comments; } + + template + 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; + + 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") + { + 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"; + } + } + 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; + } + + m_elements.push_back (PLY_element(type, number)); + } + // When end_header is reached, stop loop and begin reading points + else if (keyword == "end_header") + break; + } + } + return true; + } + + ~PLY_reader () + { + } + +}; + +template +void get_value(Reader& r, T& v, PLY_property& wrapper) +{ + return r.assign(v, wrapper.name); +} + + +template +struct Filler +{ + template + static void fill(Reader& r, Value_tuple& values, PLY_property_tuple wrappers) + { + get_value(r, std::get(values), std::get(wrappers)); + Filler::fill(r, values, wrappers); + } +}; + +template +struct seq { }; + +template +struct gens : gens { }; + +template +struct gens<0, S...> { + typedef seq type; +}; + +template +ValueType call_functor(Functor f, Tuple t, seq) { + return f(std::get(t) ...); +} + +template +ValueType call_functor(Functor f, std::tuple& t) +{ + return call_functor(f, t, typename gens::type()); +} + +template<> +struct Filler<0> +{ + template + static void fill(Reader& r, Value_tuple& values, PLY_property_tuple wrappers) + { + get_value(r, std::get<0>(values), std::get<2>(wrappers)); + } +}; + +template +void process_properties (PLY_element& element, OutputValueType& new_element, + std::tuple...>&& current) +{ + typedef typename PropertyMap::value_type PmapValueType; + std::tuple values; + Filler::fill(element, values, current); + PmapValueType new_value = call_functor(std::get<1>(current), values); + put (std::get<0>(current), new_element, new_value); +} + +template +void process_properties (PLY_element& element, OutputValueType& new_element, + std::tuple...>&& current, + NextPropertyBinder&& next, + PropertyMapBinders&& ... properties) +{ + typedef typename PropertyMap::value_type PmapValueType; + std::tuple values; + Filler::fill(element, values, current); + PmapValueType new_value = call_functor(std::get<1>(current), values); + put (std::get<0>(current), new_element, new_value); + + process_properties (element, new_element, std::forward(next), + std::forward(properties)...); +} + + +template +void process_properties (PLY_element& element, OutputValueType& new_element, + std::pair >&& current) +{ + T new_value = T(); + element.assign (new_value, current.second.name); + put (current.first, new_element, new_value); +} + +template +void process_properties (PLY_element& element, OutputValueType& new_element, + std::pair >&& current, + NextPropertyBinder&& next, + PropertyMapBinders&& ... properties) +{ + T new_value = T(); + element.assign (new_value, current.second.name); + put (current.first, new_element, new_value); + process_properties (element, new_element, std::forward(next), + std::forward(properties)...); +} + +template inline void property_header_type (std::ostream& stream) +{ + CGAL_assertion_msg (false, "Unknown PLY type"); + stream << "undefined_type"; +} + +template <> inline void property_header_type (std::ostream& stream) { stream << "char"; } +template <> inline void property_header_type (std::ostream& stream) { stream << "char"; } +template <> inline void property_header_type (std::ostream& stream) { stream << "uchar"; } +template <> inline void property_header_type (std::ostream& stream) { stream << "short"; } +template <> inline void property_header_type (std::ostream& stream) { stream << "ushort"; } +template <> inline void property_header_type (std::ostream& stream) { stream << "int"; } +template <> inline void property_header_type (std::ostream& stream) { stream << "uint"; } +template <> inline void property_header_type (std::ostream& stream) { stream << "float"; } +template <> inline void property_header_type (std::ostream& stream) { stream << "double"; } + +template +void property_header (std::ostream& stream, const PLY_property& prop) +{ + stream << "property "; + property_header_type(stream); + stream << " " << prop.name << std::endl; +} + +template +void property_header (std::ostream& stream, const PLY_property >& prop) +{ + stream << "property list uchar "; + property_header_type(stream); + stream << " " << prop.name << std::endl; +} + + +template +struct Properties_header +{ + template + static void write(std::ostream& stream, PLY_property_tuple& wrappers) + { + Properties_header::write(stream, wrappers); + property_header (stream, std::get(wrappers)); + } +}; +template <> +struct Properties_header<0> +{ + template + static void write(std::ostream& stream, PLY_property_tuple& wrappers) + { + property_header (stream, std::get<1>(wrappers)); + } +}; + +template +void output_property_header (std::ostream& stream, + std::tuple... >&& current) +{ + Properties_header::write(stream, current); +} + + +template +void output_property_header (std::ostream& stream, + std::pair >&& current) +{ + property_header (stream, current.second); +} + +template +void output_property_header (std::ostream& stream, + std::pair >&& current, + NextPropertyHandler&& next, + PropertyHandler&& ... properties) +{ + property_header (stream, current.second); + output_property_header (stream, std::forward(next), + std::forward(properties)...); +} +template +void output_property_header (std::ostream& stream, + std::tuple... >&& current, + NextPropertyHandler&& next, + PropertyHandler&& ... properties) +{ + Properties_header::write(stream, current); + output_property_header (stream, std::forward(next), + std::forward(properties)...); +} + + +template +void property_write (std::ostream& stream, ForwardIterator it, PropertyMap map) +{ + stream << CGAL::oformat(get (map, *it)); +} + +template +inline T no_char_character (const T& t) { return t; } +inline int no_char_character (const char& t) { return int(t); } +inline int no_char_character (const signed char& t) { return int(t); } +inline int no_char_character (const unsigned char& t) { return int(t); } + +template +void simple_property_write (std::ostream& stream, ForwardIterator it, + std::pair > map) +{ + if (CGAL::get_mode(stream) == IO::ASCII) + stream << no_char_character(get (map.first, *it)); + else + { + typename PropertyMap::value_type value = get(map.first, *it); + stream.write (reinterpret_cast(&value), sizeof(value)); + } +} + +template +void simple_property_write (std::ostream& stream, ForwardIterator it, + std::pair > > map) +{ + const typename PropertyMap::reference value = get(map.first, *it); + + if (CGAL::get_mode(stream) == IO::ASCII) + { + stream << value.size(); + for (std::size_t i = 0; i < value.size(); ++ i) + stream << " " << no_char_character(value[i]); + } + else + { + unsigned char size = static_cast(value.size()); + stream.write (reinterpret_cast(&size), sizeof(size)); + for (std::size_t i = 0; i < value.size(); ++ i) + { + T t = T(value[i]); + stream.write (reinterpret_cast(&t), sizeof(t)); + } + } +} + + +template +void output_properties (std::ostream& stream, + ForwardIterator it, + std::tuple... >&& current) +{ + property_write (stream, it, std::get<0>(current)); + if (get_mode(stream) == IO::ASCII) + stream << std::endl; +} + + +template +void output_properties (std::ostream& stream, + ForwardIterator it, + std::pair >&& current) +{ + simple_property_write (stream, it, std::forward > >(current)); + if (get_mode(stream) == IO::ASCII) + stream << std::endl; +} + +template +void output_properties (std::ostream& stream, + ForwardIterator it, + std::pair >&& current, + NextPropertyHandler&& next, + PropertyHandler&& ... properties) +{ + simple_property_write (stream, it, current); + if (get_mode(stream) == IO::ASCII) + stream << " "; + output_properties (stream, it, std::forward(next), + std::forward(properties)...); +} + +template +void output_properties (std::ostream& stream, + ForwardIterator it, + std::tuple... >&& current, + NextPropertyHandler&& next, + PropertyHandler&& ... properties) +{ + property_write (stream, it, std::get<0>(current)); + if (get_mode(stream) == IO::ASCII) + stream << " "; + output_properties (stream, it, std::forward(next), + std::forward(properties)...); +} + + +// Printer classes used by Point_set_3 and Surface_mesh (translate a +// property map to a PLY property) + +template +class Abstract_property_printer +{ +public: + virtual ~Abstract_property_printer() { } + virtual void print (std::ostream& stream, const Index& index) = 0; +}; + +template +class Property_printer : public Abstract_property_printer +{ + PropertyMap m_pmap; +public: + Property_printer (const PropertyMap& pmap) : m_pmap (pmap) + { + + } + + virtual void print(std::ostream& stream, const Index& index) + { + stream << get(m_pmap, index); + } +}; + +template +class Simple_property_printer : public Abstract_property_printer +{ + PropertyMap m_pmap; +public: + Simple_property_printer (const PropertyMap& pmap) : m_pmap (pmap) + { + + } + + virtual void print(std::ostream& stream, const Index& index) + { + if (get_mode(stream) == IO::ASCII) + stream << get(m_pmap, index); + else + { + Type t = Type(get (m_pmap, index)); + stream.write (reinterpret_cast(&t), sizeof(t)); + } + } +}; + +template +class Char_property_printer : public Abstract_property_printer +{ + typedef typename PropertyMap::value_type Type; + PropertyMap m_pmap; +public: + Char_property_printer (const PropertyMap& pmap) : m_pmap (pmap) + { + + } + + virtual void print(std::ostream& stream, const Index& index) + { + if (get_mode(stream) == IO::ASCII) + stream << int(get(m_pmap, index)); + else + { + Type t = get (m_pmap, index); + stream.write (reinterpret_cast(&t), sizeof(t)); + } + } +}; + +} // namespace PLY + +} // namespace internal + +} // namespace CGAL + + +#endif // CGAL_IO_PLY_H diff --git a/Surface_mesh/include/CGAL/Surface_mesh/IO/PLY.h b/Surface_mesh/include/CGAL/Surface_mesh/IO/PLY.h new file mode 100644 index 00000000000..f4a9cb8bb63 --- /dev/null +++ b/Surface_mesh/include/CGAL/Surface_mesh/IO/PLY.h @@ -0,0 +1,727 @@ +// Copyright (c) 2018 GeometryFactory Sarl (France). +// 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_SURFACE_MESH_IO_PLY +#define CGAL_SURFACE_MESH_IO_PLY + +#include + +namespace CGAL { + +namespace internal { + +#if !defined(CGAL_CFG_NO_CPP0X_RVALUE_REFERENCE) && !defined(CGAL_CFG_NO_CPP0X_VARIADIC_TEMPLATES) +namespace PLY { + +template +class Surface_mesh_filler +{ +public: + typedef typename Kernel_traits::Kernel Kernel; + typedef typename Kernel::Vector_3 Vector; + typedef Surface_mesh Surface_mesh; + typedef typename Surface_mesh::Vertex_index Vertex_index; + typedef typename Surface_mesh::Face_index Face_index; + typedef typename Surface_mesh::Edge_index Edge_index; + typedef typename Surface_mesh::Halfedge_index Halfedge_index; + +private: + + struct Abstract_ply_property_to_surface_mesh_property + { + virtual ~Abstract_ply_property_to_surface_mesh_property() { } + virtual void assign (PLY_element& element, std::size_t index) = 0; + }; + + template + class PLY_property_to_surface_mesh_property : public Abstract_ply_property_to_surface_mesh_property + { + typedef typename Surface_mesh::template Property_map Map; + Map m_map; + std::string m_name; + public: + PLY_property_to_surface_mesh_property (Surface_mesh& sm, const std::string& name) + : m_name (name) + { + m_map = sm.template add_property_map(prefix(Simplex()) + name).first; + } + + virtual void assign (PLY_element& element, std::size_t index) + { + Type t{}; + element.assign (t, m_name.c_str()); + put(m_map, Simplex(index), t); + } + + std::string prefix(Vertex_index) const { return "v:"; } + std::string prefix(Face_index) const { return "f:"; } + std::string prefix(Edge_index) const { return "e:"; } + std::string prefix(Halfedge_index) const { return "h:"; } + }; + + Surface_mesh& m_mesh; + std::vector m_map_v2v; + bool m_use_floats; + int m_normals; + typename Surface_mesh::template Property_map m_normal_map; + int m_vcolors; + typename Surface_mesh::template Property_map m_vcolor_map; + int m_fcolors; + typename Surface_mesh::template Property_map m_fcolor_map; + bool m_use_int32_t; + std::string m_index_tag; + std::vector m_vertex_properties; + std::vector m_face_properties; + std::vector m_edge_properties; + std::vector m_halfedge_properties; + +public: + + Surface_mesh_filler (Surface_mesh& mesh) + : m_mesh (mesh), m_use_floats (false), m_normals(0), m_vcolors(0), m_fcolors(0) + { } + + ~Surface_mesh_filler() + { + for (std::size_t i = 0; i < m_vertex_properties.size(); ++ i) + delete m_vertex_properties[i]; + for (std::size_t i = 0; i < m_face_properties.size(); ++ i) + delete m_face_properties[i]; + for (std::size_t i = 0; i < m_edge_properties.size(); ++ i) + delete m_edge_properties[i]; + for (std::size_t i = 0; i < m_halfedge_properties.size(); ++ i) + delete m_halfedge_properties[i]; + } + + bool has_simplex_specific_property (internal::PLY::PLY_read_number* property, Vertex_index) + { + const std::string& name = property->name(); + if (name == "x" || + name == "y" || + name == "z") + { + if (dynamic_cast*>(property)) + m_use_floats = true; + return true; + } + if (name == "nx" || + name == "ny" || + name == "nz") + { + ++ m_normals; + if (m_normals == 3) + m_normal_map = m_mesh.template add_property_map("v:normal").first; + return true; + } + if (name == "red" || + name == "green" || + name == "blue") + { + ++ m_vcolors; + if (m_vcolors == 3) + m_vcolor_map = m_mesh.template add_property_map("v:color").first; + return true; + } + return false; + } + + bool has_simplex_specific_property (internal::PLY::PLY_read_number* property, Face_index) + { + const std::string& name = property->name(); + if (name == "vertex_indices" || name == "vertex_index") + { + m_index_tag = name; + m_use_int32_t = dynamic_cast*>(property); + CGAL_assertion (dynamic_cast*>(property)); + return true; + } + if (name == "red" || + name == "green" || + name == "blue") + { + ++ m_fcolors; + if (m_fcolors == 3) + m_fcolor_map = m_mesh.template add_property_map("f:color").first; + return true; + } + + return false; + } + + bool has_simplex_specific_property (internal::PLY::PLY_read_number* property, Edge_index) + { + const std::string& name = property->name(); + if (name == "v0" || name == "v1") + return true; + return false; + } + + bool has_simplex_specific_property (internal::PLY::PLY_read_number* property, Halfedge_index) + { + const std::string& name = property->name(); + if (name == "source" || name == "target") + return true; + return false; + } + + void instantiate_vertex_properties (PLY_element& element) + { + m_map_v2v.reserve(element.number_of_items()); + instantiate_properties (element, m_vertex_properties); + } + + void instantiate_face_properties (PLY_element& element) + { + instantiate_properties (element, m_face_properties); + } + + void instantiate_edge_properties (PLY_element& element) + { + instantiate_properties (element, m_edge_properties); + } + + void instantiate_halfedge_properties (PLY_element& element) + { + instantiate_properties (element, m_halfedge_properties); + } + + template + void instantiate_properties (PLY_element& element, + std::vector& properties) + { + for (std::size_t j = 0; j < element.number_of_properties(); ++ j) + { + internal::PLY::PLY_read_number* property = element.property(j); + + if (has_simplex_specific_property (property, Simplex())) + continue; + + const std::string& name = property->name(); + + if (dynamic_cast*>(property)) + { + properties.push_back + (new PLY_property_to_surface_mesh_property(m_mesh, + name)); + } + else if (dynamic_cast*>(property)) + { + properties.push_back + (new PLY_property_to_surface_mesh_property(m_mesh, + name)); + } + else if (dynamic_cast*>(property)) + { + properties.push_back + (new PLY_property_to_surface_mesh_property(m_mesh, + name)); + } + else if (dynamic_cast*>(property)) + { + properties.push_back + (new PLY_property_to_surface_mesh_property(m_mesh, + name)); + } + else if (dynamic_cast*>(property)) + { + properties.push_back + (new PLY_property_to_surface_mesh_property(m_mesh, + name)); + } + else if (dynamic_cast*>(property)) + { + properties.push_back + (new PLY_property_to_surface_mesh_property(m_mesh, + name)); + } + else if (dynamic_cast*>(property)) + { + properties.push_back + (new PLY_property_to_surface_mesh_property(m_mesh, + name)); + } + else if (dynamic_cast*>(property)) + { + properties.push_back + (new PLY_property_to_surface_mesh_property(m_mesh, + name)); + } + } + } + + void process_vertex_line (PLY_element& element) + { + Vertex_index vi; + + if (m_use_floats) + process_line(element, vi); + else + process_line(element, vi); + + for (std::size_t i = 0; i < m_vertex_properties.size(); ++ i) + m_vertex_properties[i]->assign (element, vi); + } + + template + void process_line (PLY_element& element, Vertex_index& vi) + { + FT x = (FT)0.,y = (FT)0., z = (FT)0., + nx = (FT)0., ny = (FT)0., nz = (FT)0.; + element.assign (x, "x"); + element.assign (y, "y"); + element.assign (z, "z"); + Point point (x, y, z); + vi = m_mesh.add_vertex(point); + m_map_v2v.push_back(vi); + + if (m_normals == 3) + { + element.assign (nx, "nx"); + element.assign (ny, "ny"); + element.assign (nz, "nz"); + Vector normal (nx, ny, nz); + m_normal_map[vi] = normal; + } + + if (m_vcolors == 3) + { + unsigned char r, g, b; + element.assign (r, "red"); + element.assign (g, "green"); + element.assign (b, "blue"); + m_vcolor_map[vi] = CGAL::Color (r, g, b); + } + } + + bool process_face_line (PLY_element& element) + { + Face_index fi = m_mesh.null_face(); + + if (m_use_int32_t) + process_line(element, fi); + else + process_line(element, fi); + + if (fi == Surface_mesh::null_face()) + return false; + + for (std::size_t i = 0; i < m_face_properties.size(); ++ i) + m_face_properties[i]->assign (element, fi); + + return true; + } + + template + void process_line (PLY_element& element, Face_index& fi) + { + std::vector indices; + element.assign (indices, m_index_tag.c_str()); + std::vector vertices; + vertices.reserve(indices.size()); + for (std::size_t i = 0; i < indices.size(); ++ i) + vertices.push_back (m_map_v2v[std::size_t(indices[i])]); + + fi = m_mesh.add_face(vertices); + if (fi == m_mesh.null_face()) + return; + + if (m_fcolors == 3) + { + unsigned char r, g, b; + element.assign (r, "red"); + element.assign (g, "green"); + element.assign (b, "blue"); + m_fcolor_map[fi] = CGAL::Color (r, g, b); + } + } + + bool process_edge_line (PLY_element& element) + { + Edge_index ei = m_mesh.null_edge(); + + if (m_use_int32_t) + process_line(element, ei); + else + process_line(element, ei); + + if (ei == Surface_mesh::null_edge()) + return false; + + for (std::size_t i = 0; i < m_edge_properties.size(); ++ i) + m_edge_properties[i]->assign (element, ei); + + return true; + } + + template + void process_line (PLY_element& element, Edge_index& ei) + { + IntType v0, v1; + element.assign (v0, "v0"); + element.assign (v1, "v1"); + + Halfedge_index hi = m_mesh.halfedge(m_map_v2v[std::size_t(v0)], + m_map_v2v[std::size_t(v1)]); + if (hi == m_mesh.null_halfedge()) + return; + + ei = m_mesh.edge (hi); + } + + bool process_halfedge_line (PLY_element& element) + { + Halfedge_index hi = m_mesh.null_halfedge(); + + if (m_use_int32_t) + process_line(element, hi); + else + process_line(element, hi); + + if (hi == Surface_mesh::null_halfedge()) + return false; + + for (std::size_t i = 0; i < m_halfedge_properties.size(); ++ i) + m_halfedge_properties[i]->assign (element, hi); + + return true; + } + + template + void process_line (PLY_element& element, Halfedge_index& hi) + { + IntType source, target; + element.assign (source, "source"); + element.assign (target, "target"); + hi = m_mesh.halfedge(m_map_v2v[std::size_t(source)], m_map_v2v[std::size_t(target)]); + } + +}; + + +template +bool fill_simplex_specific_header +(std::ostream& os, const Surface_mesh& sm, + std::vector::Vertex_index>*>& printers, + const std::string& prop) +{ + typedef Surface_mesh SMesh; + typedef typename SMesh::Vertex_index VIndex; + typedef typename Kernel_traits::Kernel Kernel; + typedef typename Kernel::FT FT; + typedef typename Kernel::Vector_3 Vector; + typedef typename SMesh::template Property_map Point_map; + typedef typename SMesh::template Property_map Vector_map; + typedef typename SMesh::template Property_map Vcolor_map; + + if (prop == "v:connectivity" || + prop == "v:removed") + return true; + + if (prop == "v:point") + { + if (boost::is_same::value) + { + os << "property float x" << std::endl + << "property float y" << std::endl + << "property float z" << std::endl; + } + else + { + os << "property double x" << std::endl + << "property double y" << std::endl + << "property double z" << std::endl; + } + printers.push_back (new Property_printer(sm.points())); + return true; + } + + bool okay = false; + if (prop == "v:normal") + { + Vector_map pmap; + boost::tie (pmap, okay) = sm.template property_map(prop); + if (okay) + { + if (boost::is_same::value) + { + os << "property float nx" << std::endl + << "property float ny" << std::endl + << "property float nz" << std::endl; + } + else + { + os << "property double nx" << std::endl + << "property double ny" << std::endl + << "property double nz" << std::endl; + } + printers.push_back (new Property_printer(pmap)); + return true; + } + } + + if (prop == "v:color") + { + Vcolor_map pmap; + boost::tie (pmap, okay) = sm.template property_map(prop); + if (okay) + { + os << "property uchar red" << std::endl + << "property uchar green" << std::endl + << "property uchar blue" << std::endl + << "property uchar alpha" << std::endl; + + printers.push_back (new Property_printer(pmap)); + return true; + } + } + + return false; +} + +template +bool fill_simplex_specific_header +(std::ostream& os, const Surface_mesh& sm, + std::vector::Face_index>*>& printers, + const std::string& prop) +{ + typedef Surface_mesh SMesh; + typedef typename SMesh::Face_index FIndex; + typedef typename SMesh::template Property_map Fcolor_map; + + if (prop == "f:connectivity" || + prop == "f:removed") + return true; + + bool okay = false; + if (prop == "f:color") + { + Fcolor_map pmap; + boost::tie (pmap, okay) = sm.template property_map(prop); + if (okay) + { + os << "property uchar red" << std::endl + << "property uchar green" << std::endl + << "property uchar blue" << std::endl + << "property uchar alpha" << std::endl; + + printers.push_back (new Property_printer(pmap)); + return true; + } + } + return false; +} + +template +bool fill_simplex_specific_header +(std::ostream& , const Surface_mesh& , + std::vector::Edge_index>*>& , + const std::string& prop) +{ + if (prop == "e:removed") + return true; + return false; +} + +template +bool fill_simplex_specific_header +(std::ostream& , const Surface_mesh& , + std::vector::Halfedge_index>*>& , + const std::string& prop) +{ + if (prop == "h:connectivity") + return true; + return false; +} + +template +std::string get_property_raw_name (const std::string& prop, typename Surface_mesh::Vertex_index) +{ + std::string name = prop; + if (name.rfind("v:",0) == 0) + name = std::string (prop.begin() + 2, prop.end()); + return name; +} + +template +std::string get_property_raw_name (const std::string& prop, typename Surface_mesh::Face_index) +{ + std::string name = prop; + if (name.rfind("f:",0) == 0) + name = std::string (prop.begin() + 2, prop.end()); + return name; +} + +template +std::string get_property_raw_name (const std::string& prop, typename Surface_mesh::Edge_index) +{ + std::string name = prop; + if (name.rfind("e:",0) == 0) + name = std::string (prop.begin() + 2, prop.end()); + return name; +} + +template +std::string get_property_raw_name (const std::string& prop, typename Surface_mesh::Halfedge_index) +{ + std::string name = prop; + if (name.rfind("h:",0) == 0) + name = std::string (prop.begin() + 2, prop.end()); + return name; +} + +template +void fill_header (std::ostream& os, const Surface_mesh& sm, + std::vector*>& printers) +{ + typedef Surface_mesh SMesh; + typedef typename SMesh::template Property_map Int8_map; + typedef typename SMesh::template Property_map Uint8_map; + typedef typename SMesh::template Property_map Int16_map; + typedef typename SMesh::template Property_map Uint16_map; + typedef typename SMesh::template Property_map Int32_map; + typedef typename SMesh::template Property_map Uint32_map; + typedef typename SMesh::template Property_map Int64_map; + typedef typename SMesh::template Property_map Uint64_map; + typedef typename SMesh::template Property_map Float_map; + typedef typename SMesh::template Property_map Double_map; + std::vector prop = sm.template properties(); + + for (std::size_t i = 0; i < prop.size(); ++ i) + { + if (fill_simplex_specific_header(os, sm, printers, prop[i])) + continue; + + // Cut the "v:" prefix + std::string name = get_property_raw_name (prop[i], Simplex()); + + bool okay = false; + { + Int8_map pmap; + boost::tie (pmap, okay) = sm.template property_map(prop[i]); + if (okay) + { + os << "property char " << name << std::endl; + printers.push_back (new internal::PLY::Char_property_printer(pmap)); + continue; + } + } + { + Uint8_map pmap; + boost::tie (pmap, okay) = sm.template property_map(prop[i]); + if (okay) + { + os << "property uchar " << name << std::endl; + printers.push_back (new internal::PLY::Char_property_printer(pmap)); + continue; + } + } + { + Int16_map pmap; + boost::tie (pmap, okay) = sm.template property_map(prop[i]); + if (okay) + { + os << "property short " << name << std::endl; + printers.push_back (new internal::PLY::Simple_property_printer(pmap)); + continue; + } + } + { + Uint16_map pmap; + boost::tie (pmap, okay) = sm.template property_map(prop[i]); + if (okay) + { + os << "property ushort " << name << std::endl; + printers.push_back (new internal::PLY::Simple_property_printer(pmap)); + continue; + } + } + { + Int32_map pmap; + boost::tie (pmap, okay) = sm.template property_map(prop[i]); + if (okay) + { + os << "property int " << name << std::endl; + printers.push_back (new internal::PLY::Simple_property_printer(pmap)); + continue; + } + } + { + Uint32_map pmap; + boost::tie (pmap, okay) = sm.template property_map(prop[i]); + if (okay) + { + os << "property uint " << name << std::endl; + printers.push_back (new internal::PLY::Simple_property_printer(pmap)); + continue; + } + } + { + Int64_map pmap; + boost::tie (pmap, okay) = sm.template property_map(prop[i]); + if (okay) + { + os << "property int " << name << std::endl; + printers.push_back (new internal::PLY::Simple_property_printer(pmap)); + continue; + } + } + { + Uint64_map pmap; + boost::tie (pmap, okay) = sm.template property_map(prop[i]); + if (okay) + { + os << "property uint " << name << std::endl; + printers.push_back (new internal::PLY::Simple_property_printer(pmap)); + continue; + } + } + { + Float_map pmap; + boost::tie (pmap, okay) = sm.template property_map(prop[i]); + if (okay) + { + os << "property float " << name << std::endl; + printers.push_back (new internal::PLY::Simple_property_printer(pmap)); + continue; + } + } + { + Double_map pmap; + boost::tie (pmap, okay) = sm.template property_map(prop[i]); + if (okay) + { + os << "property double " << name << std::endl; + printers.push_back (new internal::PLY::Simple_property_printer(pmap)); + continue; + } + } + } +} + +} // namespace PLY +#endif + +} // namespace internal + +} // namespace CGAL + + +#endif // CGAL_SURFACE_MESH_IO_PLY diff --git a/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h b/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h index 00472da2823..8c0f3d087c8 100644 --- a/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h +++ b/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h @@ -53,6 +53,7 @@ #include #include #include +#include #include #include #include @@ -2160,6 +2161,204 @@ private: //------------------------------------------------------- private data return os; } +#if !defined(CGAL_CFG_NO_CPP0X_RVALUE_REFERENCE) && !defined(CGAL_CFG_NO_CPP0X_VARIADIC_TEMPLATES) + + /// \relates Surface_mesh + /// Inserts the surface mesh in an output stream in PLY format. + /// If found, "v:normal", "v:color" and "f:color" are inserted in the stream. + /// All other vertex and face properties with simple types are inserted in the stream. + /// Edges are only inserted in the stream if they have at least one + /// property with simple type: if they do, all edge properties with + /// simple types are inserted in the stream. The halfedges follow + /// the same behavior. + /// + /// If provided, the `comments` string is included line by line in + /// the header of the PLY stream (each line will be precedeed by + /// "comment "). + /// + /// \relates Surface_mesh + template + bool write_ply(std::ostream& os, const Surface_mesh

& sm, const std::string& comments = std::string()) + { + typedef Surface_mesh

SMesh; + typedef typename Kernel_traits

::Kernel K; + typedef typename K::Vector_3 Vector; + typedef typename SMesh::Vertex_index VIndex; + typedef typename SMesh::Face_index FIndex; + typedef typename SMesh::Edge_index EIndex; + typedef typename SMesh::Halfedge_index HIndex; + + typedef typename SMesh::template Property_map Fcolor_map; + + typedef typename SMesh::template Property_map Int8_map_f; + typedef typename SMesh::template Property_map Uint8_map_f; + typedef typename SMesh::template Property_map Int16_map_f; + typedef typename SMesh::template Property_map Uint16_map_f; + typedef typename SMesh::template Property_map Int32_map_f; + typedef typename SMesh::template Property_map Uint32_map_f; + typedef typename SMesh::template Property_map Int64_map_f; + typedef typename SMesh::template Property_map Uint64_map_f; + typedef typename SMesh::template Property_map Float_map_f; + typedef typename SMesh::template Property_map Double_map_f; + + os << "ply" << std::endl + << ((get_mode(os) == IO::BINARY) ? "format binary_little_endian 1.0" : "format ascii 1.0") << std::endl + << "comment Generated by the CGAL library" << std::endl; + + if (comments != std::string()) + { + std::istringstream iss (comments); + std::string line; + while (getline(iss, line)) + { + if (line != "Generated by the CGAL library") // Avoid repeating the line if multiple savings + os << "comment " << line << std::endl; + } + } + + os << "element vertex " << sm.number_of_vertices() << std::endl; + + std::vector*> vprinters; + internal::PLY::fill_header (os, sm, vprinters); + + os << "element face " << sm.number_of_faces() << std::endl; + os << "property list uchar int vertex_indices" << std::endl; + std::vector*> fprinters; + internal::PLY::fill_header (os, sm, fprinters); + + std::vector*> eprinters; + if (sm.template properties().size() > 1) + { + os << "element edge " << sm.number_of_edges() << std::endl; + os << "property int v0" << std::endl; + os << "property int v1" << std::endl; + internal::PLY::fill_header (os, sm, eprinters); + } + + std::vector*> hprinters; + if (sm.template properties().size() > 1) + { + os << "element halfedge " << sm.number_of_halfedges() << std::endl; + os << "property int source" << std::endl; + os << "property int target" << std::endl; + internal::PLY::fill_header (os, sm, hprinters); + } + + os << "end_header" << std::endl; + + BOOST_FOREACH(VIndex vi, sm.vertices()) + { + for (std::size_t i = 0; i < vprinters.size(); ++ i) + { + vprinters[i]->print(os, vi); + if (get_mode (os) == IO::ASCII) + os << " "; + } + if (get_mode (os) == IO::ASCII) + os << std::endl; + } + + std::vector polygon; + + BOOST_FOREACH(FIndex fi, sm.faces()) + { + // Get list of vertex indices + polygon.clear(); + BOOST_FOREACH(HIndex hi, halfedges_around_face(halfedge(fi, sm), sm)) + polygon.push_back (sm.target(hi)); + + if (get_mode (os) == IO::ASCII) + { + os << polygon.size() << " "; + for (std::size_t i = 0; i < polygon.size(); ++ i) + os << int(polygon[i]) << " "; + } + else + { + unsigned char size = (unsigned char)(polygon.size()); + os.write (reinterpret_cast(&size), sizeof(size)); + for (std::size_t i = 0; i < polygon.size(); ++ i) + { + int idx = int(polygon[i]); + os.write (reinterpret_cast(&idx), sizeof(idx)); + } + } + + for (std::size_t i = 0; i < fprinters.size(); ++ i) + { + fprinters[i]->print(os, fi); + if (get_mode (os) == IO::ASCII) + os << " "; + } + + if (get_mode (os) == IO::ASCII) + os << std::endl; + } + + if (!eprinters.empty()) + { + BOOST_FOREACH(EIndex ei, sm.edges()) + { + if (get_mode (os) == IO::ASCII) + os << int(sm.vertex(ei,0)) << " " << int(sm.vertex(ei,1)) << " "; + else + { + int v0 = int(sm.vertex(ei,0)); + int v1 = int(sm.vertex(ei,1)); + os.write (reinterpret_cast(&v0), sizeof(v0)); + os.write (reinterpret_cast(&v1), sizeof(v1)); + } + + for (std::size_t i = 0; i < eprinters.size(); ++ i) + { + eprinters[i]->print(os, ei); + if (get_mode (os) == IO::ASCII) + os << " "; + } + + if (get_mode (os) == IO::ASCII) + os << std::endl; + } + } + + if (!hprinters.empty()) + { + BOOST_FOREACH(HIndex hi, sm.halfedges()) + { + if (get_mode (os) == IO::ASCII) + os << int(sm.source(hi)) << " " << int(sm.target(hi)) << " "; + else + { + int source = int(sm.source(hi)); + int target = int(sm.target(hi)); + os.write (reinterpret_cast(&source), sizeof(source)); + os.write (reinterpret_cast(&target), sizeof(target)); + } + + for (std::size_t i = 0; i < hprinters.size(); ++ i) + { + hprinters[i]->print(os, hi); + if (get_mode (os) == IO::ASCII) + os << " "; + } + + if (get_mode (os) == IO::ASCII) + os << std::endl; + } + } + + for (std::size_t i = 0; i < vprinters.size(); ++ i) + delete vprinters[i]; + for (std::size_t i = 0; i < fprinters.size(); ++ i) + delete fprinters[i]; + for (std::size_t i = 0; i < eprinters.size(); ++ i) + delete eprinters[i]; + for (std::size_t i = 0; i < hprinters.size(); ++ i) + delete hprinters[i]; + + return true; + } +#endif /// @cond CGAL_DOCUMENT_INTERNALS @@ -2218,7 +2417,7 @@ private: //------------------------------------------------------- private data if(!is){ return false; } - sm.reserve(sm.num_vertices()+n, sm.num_faces()+2*f, sm.num_edges()+e); + sm.reserve(sm.num_vertices()+n, sm.num_edges()+e, sm.num_faces()+f); std::vector vertexmap(n); P p; Vector_3 v; @@ -2320,6 +2519,131 @@ private: //------------------------------------------------------- private data return read_off(is, sm, parameters::all_default()); } +#if !defined(CGAL_CFG_NO_CPP0X_RVALUE_REFERENCE) && !defined(CGAL_CFG_NO_CPP0X_VARIADIC_TEMPLATES) + + /// \cond SKIP_IN_MANUAL + template + bool read_ply(std::istream& is, Surface_mesh

& sm) + { + std::string dummy; + return read_ply (is, sm, dummy); + } + /// \endcond + + /// Extracts the surface mesh from an input stream in Ascii or + /// Binary PLY format and appends it to the surface mesh `sm`. + /// + /// - the operator reads the vertex `point` property and the face + /// `vertex_index` (or `vertex_indices`) property; + /// - if three PLY properties `nx`, `ny` and `nz` with type `float` + /// or `double` are found for vertices, a "v:normal" vertex + /// property map is added; + /// - if three PLY properties `red`, `green` and `blue` with type + /// `uchar` are found for vertices, a "v:color" vertex property + /// map is added; + /// - if three PLY properties `red`, `green` and `blue` with type + /// `uchar` are found for faces, a "f:color" face property map is + /// added; + /// - if any other PLY property is found, a "[s]:[name]" property map is + /// added, where `[s]` is `v` for vertex and `f` for face, and + /// `[name]` is the name of the PLY property. + /// + /// The `comments` parameter can be omitted. If provided, it will be + /// used to store the potential comments found in the PLY + /// header. Each line starting by "comment " in the header is + /// appended to the `comments` string (without the "comment " word). + /// + /// \pre The data in the stream must represent a two-manifold. If this is not the case + /// the `failbit` of `is` is set and the mesh cleared. + /// \relates Surface_mesh + + template + bool read_ply(std::istream& is, Surface_mesh

& sm, std::string& comments) + { + if(!is) + { + std::cerr << "Error: cannot open file" << std::endl; + return false; + } + + internal::PLY::PLY_reader reader; + internal::PLY::Surface_mesh_filler

filler(sm); + + if (!(reader.init (is))) + { + is.setstate(std::ios::failbit); + return false; + } + + comments = reader.comments(); + + for (std::size_t i = 0; i < reader.number_of_elements(); ++ i) + { + internal::PLY::PLY_element& element = reader.element(i); + + bool is_vertex = (element.name() == "vertex" || element.name() == "vertices"); + bool is_face = false; + bool is_edge = false; + bool is_halfedge = false; + if (is_vertex) + { + sm.reserve(sm.number_of_vertices() + element.number_of_items(), + sm.number_of_edges(), + sm.number_of_faces()); + filler.instantiate_vertex_properties (element); + } + else + is_face = (element.name() == "face" || element.name() == "faces"); + + if (is_face) + { + sm.reserve(sm.number_of_vertices(), + sm.number_of_edges(), + sm.number_of_faces() + element.number_of_items()); + filler.instantiate_face_properties (element); + } + else + is_edge = (element.name() == "edge"); + + if (is_edge) + filler.instantiate_edge_properties (element); + else + is_halfedge = (element.name() == "halfedge"); + + if (is_halfedge) + filler.instantiate_halfedge_properties (element); + + for (std::size_t j = 0; j < element.number_of_items(); ++ j) + { + for (std::size_t k = 0; k < element.number_of_properties(); ++ k) + { + internal::PLY::PLY_read_number* property = element.property(k); + property->get (is); + if (is.fail()) + return false; + } + + if (is_vertex) + filler.process_vertex_line (element); + else if (is_face) + { + if (!filler.process_face_line (element)) + { + is.setstate(std::ios::failbit); + return false; + } + } + else if (is_edge) + filler.process_edge_line (element); + else if (is_halfedge) + filler.process_halfedge_line (element); + } + } + + return true; + } +#endif + /// \relates Surface_mesh /// This operator calls `read_off(std::istream& is, CGAL::Surface_mesh& sm)`. /// \attention Up to %CGAL 4.10 this operator called `sm.clear()`. diff --git a/Surface_mesh/test/Surface_mesh/colored_tetra.ply b/Surface_mesh/test/Surface_mesh/colored_tetra.ply new file mode 100644 index 00000000000..7ec622125d1 --- /dev/null +++ b/Surface_mesh/test/Surface_mesh/colored_tetra.ply @@ -0,0 +1,38 @@ +ply +format ascii 1.0 +element vertex 4 +property double x +property double y +property double z +property double nx +property double ny +property double nz +property uchar red +property uchar green +property uchar blue +property int id +element face 4 +property list uchar int vertex_indices +property uchar red +property uchar green +property uchar blue +property int label +element edge 6 +property int v0 +property int v1 +property float confidence +end_header +0 0 0 -0.5 -0.5 -0.5 255 255 0 0 +0 0 1 -0.5 -0.5 0 0 255 255 1 +0 1 0 -0.5 0 -0.5 128 0 255 2 +1 0 0 0 -0.5 -0.5 255 128 0 3 +3 0 1 2 255 0 0 -1 +3 0 3 1 0 255 0 1 +3 1 3 2 0 0 255 -1 +3 0 2 3 255 0 255 0 +0 1 0.1 +0 2 0.2 +0 3 0.3 +1 2 0.4 +1 3 0.5 +2 3 0.6 diff --git a/Surface_mesh/test/Surface_mesh/sm_ply_io.cpp b/Surface_mesh/test/Surface_mesh/sm_ply_io.cpp new file mode 100644 index 00000000000..c18b6523b74 --- /dev/null +++ b/Surface_mesh/test/Surface_mesh/sm_ply_io.cpp @@ -0,0 +1,46 @@ +#include +#include +#include +#include +#include + +typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; +typedef Kernel::Point_3 Point; +typedef CGAL::Surface_mesh SMesh; +typedef boost::graph_traits::face_descriptor face_descriptor; +typedef boost::graph_traits::vertex_descriptor vertex_descriptor; + + +int main() +{ + std::ifstream in ("colored_tetra.ply"); + SMesh mesh; + CGAL::read_ply (in, mesh); + + std::cerr << "Read mesh with " << mesh.number_of_vertices() << " vertices and " + << mesh.number_of_faces() << " faces" << std::endl; + + std::cerr << "Properties associated with vertices:" << std::endl; + std::vector properties = mesh.properties(); + for (std::size_t i = 0; i < properties.size(); ++ i) + std::cerr << " * " << properties[i] << std::endl; + + std::cerr << "Properties associated with faces:" << std::endl; + properties = mesh.properties(); + for (std::size_t i = 0; i < properties.size(); ++ i) + std::cerr << " * " << properties[i] << std::endl; + + mesh.add_property_map("id", 42); + mesh.add_property_map("u", 13.f); + mesh.add_property_map("v", 37.f); + + // Append second mesh + std::ifstream in2 ("tetra.ply"); + CGAL::read_ply (in2, mesh); + + std::ofstream out ("out.ply"); +// CGAL::set_binary_mode(out); + CGAL::write_ply (out, mesh); + + return 0; +} diff --git a/Surface_mesh/test/Surface_mesh/tetra.ply b/Surface_mesh/test/Surface_mesh/tetra.ply new file mode 100644 index 00000000000..6b2a697893f --- /dev/null +++ b/Surface_mesh/test/Surface_mesh/tetra.ply @@ -0,0 +1,14 @@ +ply +format ascii 1.0 +element vertex 3 +property double x +property double y +property double z +property int blabla +element face 1 +property list uchar int vertex_indices +end_header +0 0 2 1000 +0 1 2 200 +1 0 2 30 +3 0 1 2