// 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$ // // Author(s) : Simon Giraudot #ifndef CGAL_READ_PLY_POINTS_H #define CGAL_READ_PLY_POINTS_H #include #include #include #include #include #include #include #include #include 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 /// \cond SKIP_IN_MANUAL namespace internal { 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, boost::int8_t& c) const { short s; stream >> s; c = static_cast(s); } void read_ascii (std::istream& stream, boost::uint8_t& c) const { unsigned short s; stream >> s; c = static_cast(s); } // 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; } }; } // namespace internal /// \endcond //=================================================================================== /// \ingroup PkgPointSetProcessing /// /// The PLY reader is initialized with the correct set of properties (along /// with their name tags and number types) based on the header of the PLY input /// file. It loads all the points with their specific properties of the PLY /// input and delegates the interpretation to a user-specified `PlyInterpreter`. /// //----------------------------------------------------------------------------------- class Ply_reader { std::vector m_readers; std::size_t m_nb_points; public: /// \cond SKIP_IN_MANUAL Ply_reader () : m_nb_points (0) { } 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; // Check the order of the properties of the point set bool reading_properties = false; while (getline (stream,line)) { iss.clear(); iss.str (line); ++ lineNumber; // Reads file signature on first line if (lineNumber == 1) { std::string signature; if (!(iss >> signature) || (signature != "ply")) { // if wrong file format std::cerr << "Error: incorrect file format line " << lineNumber << " of file" << std::endl; return false; } } // Reads format on 2nd line else if (lineNumber == 2) { std::string tag, format_string, version; if ( !(iss >> tag >> format_string >> version) ) { std::cerr << "Error line " << lineNumber << " of file" << std::endl; return false; } if (format_string == "ascii") format = ASCII; else if (format_string == "binary_little_endian") format = BINARY_LITTLE_ENDIAN; else if (format_string == "binary_big_endian") format = BINARY_BIG_ENDIAN; else { std::cerr << "Error: unknown file format \"" << format_string << "\" line " << lineNumber << std::endl; return false; } } // Comments and vertex properties else { std::string keyword; if (!(iss >> keyword)) { std::cerr << "Error line " << lineNumber << " of file" << std::endl; return false; } if (keyword == "property") { if (!reading_properties) continue; std::string type, name; if (!(iss >> type >> name)) { std::cerr << "Error line " << lineNumber << " of file" << std::endl; return false; } if ( type == "char" || type == "int8") m_readers.push_back (new internal::Ply_read_typed_number (name, format)); else if (type == "uchar" || type == "uint8") m_readers.push_back (new internal::Ply_read_typed_number (name, format)); else if (type == "short" || type == "int16") m_readers.push_back (new internal::Ply_read_typed_number (name, format)); else if (type == "ushort" || type == "uint16") m_readers.push_back (new internal::Ply_read_typed_number (name, format)); else if (type == "int" || type == "int32") m_readers.push_back (new internal::Ply_read_typed_number (name, format)); else if (type == "uint" || type == "uint32") m_readers.push_back (new internal::Ply_read_typed_number (name, format)); else if (type == "float" || type == "float32") m_readers.push_back (new internal::Ply_read_typed_number (name, format)); else if (type == "double" || type == "float64") m_readers.push_back (new internal::Ply_read_typed_number (name, format)); continue; } else reading_properties = false; // ignore comments and properties (if not in element // vertex - cf below - properties are useless in our case) if (keyword == "comment" || keyword == "property") continue; // When end_header is reached, stop loop and begin reading points if (keyword == "end_header") break; if (keyword == "element") { std::string type; std::size_t number; if (!(iss >> type >> number)) { std::cerr << "Error line " << lineNumber << " of file" << std::endl; return false; } if (type == "vertex") { m_nb_points = number; reading_properties = true; } else continue; } } } return true; } ~Ply_reader () { for (std::size_t i = 0; i < m_readers.size (); ++ i) delete m_readers[i]; m_readers.clear(); } template bool read_content (Stream& stream, PlyInterpreter& interpreter) { std::size_t points_read = 0; while (!(stream.eof()) && points_read < m_nb_points) { for (std::size_t i = 0; i < m_readers.size (); ++ i) m_readers[i]->get (stream); interpreter.process_line (*this); ++ points_read; } // Skip remaining lines return (points_read == m_nb_points); } /// \endcond /*! \tparam Type type of the property (ex: double, float, unsigned char, etc.) \param tag name of the property (ex: _nx_ for x normal coordinate) \return true if points inside the PLY input contain the property `tag` with type `Type`, false otherwise */ template bool does_tag_exist (const char* tag) { return does_tag_exist (tag, Type()); } /*! \param t reference to store last read value of the property `tag` \param tag name of the required property */ template void assign (Type& t, const char* tag) { for (std::size_t i = 0; i < m_readers.size (); ++ i) if (m_readers[i]->name () == tag) { internal::Ply_read_typed_number* reader = dynamic_cast*>(m_readers[i]); CGAL_assertion (reader != NULL); t = reader->buffer(); return; } } /// \cond SKIP_IN_MANUAL // Convenience functions: a float property in the PLY input can be // directly interpreted as a double without loss of information. template bool does_tag_exist (const char* tag, Type) { for (std::size_t i = 0; i < m_readers.size (); ++ i) if (m_readers[i]->name () == tag) return (dynamic_cast*>(m_readers[i]) != NULL); return false; } bool does_tag_exist (const char* tag, double) { for (std::size_t i = 0; i < m_readers.size (); ++ i) if (m_readers[i]->name () == tag) return (dynamic_cast*>(m_readers[i]) != NULL || dynamic_cast*>(m_readers[i]) != NULL); return false; } void assign (double& t, const char* tag) { for (std::size_t i = 0; i < m_readers.size (); ++ i) if (m_readers[i]->name () == tag) { internal::Ply_read_typed_number* reader_double = dynamic_cast*>(m_readers[i]); if (reader_double == NULL) { internal::Ply_read_typed_number* reader_float = dynamic_cast*>(m_readers[i]); CGAL_assertion (reader_float != NULL); t = reader_float->buffer(); } else t = reader_double->buffer(); return; } } /// \endcond }; //=================================================================================== /// \ingroup PkgPointSetProcessing /// /// PLY interpreter designed to fill an output iterator of points and /// normals based on given property maps. It is used internally by the /// functions `read_ply_points()` and `read_ply_points_and_normals()`. /// /// @tparam OutputIteratorValueType type of objects that can be put in `OutputIterator`. /// @tparam OutputIterator iterator over output points. /// @tparam PointPMap is a model of `WritablePropertyMap` with value type `Point_3`. /// @tparam NormalPMap is a model of `WritablePropertyMap` with value type `Vector_3`. /// @tparam Kernel Geometric traits class. /// /// \cgalModels `PlyInterpreter` //----------------------------------------------------------------------------------- template class Ply_interpreter_points_and_normals_3 { // value_type_traits is a workaround as back_insert_iterator's value_type is void // typedef typename value_type_traits::type Enriched_point; typedef OutputIteratorValueType Enriched_point; typedef typename Kernel::FT FT; typedef typename Kernel::Point_3 Point; typedef typename Kernel::Vector_3 Vector; OutputIterator m_output; PointPMap& m_point_pmap; NormalPMap& m_normal_pmap; public: /*! Constructs a PLY interpreter for points and normals. */ Ply_interpreter_points_and_normals_3 (OutputIterator output, PointPMap& point_pmap, NormalPMap& normal_pmap) : m_output (output), m_point_pmap (point_pmap), m_normal_pmap (normal_pmap) { } bool is_applicable (Ply_reader& reader) { return reader.does_tag_exist ("x") && reader.does_tag_exist ("y") && reader.does_tag_exist ("z"); } void process_line (Ply_reader& reader) { FT x = (FT)0.,y = (FT)0., z = (FT)0., nx = (FT)0., ny = (FT)0., nz = (FT)0.; reader.assign (x, "x"); reader.assign (y, "y"); reader.assign (z, "z"); reader.assign (nx, "nx"); reader.assign (ny, "ny"); reader.assign (nz, "nz"); Point point (x, y, z); Vector normal (nx, ny, nz); Enriched_point pwn; put(m_point_pmap, pwn, point); // point_pmap[&pwn] = point put(m_normal_pmap, pwn, normal); // normal_pmap[&pwn] = normal *m_output++ = pwn; } }; //=================================================================================== /// \ingroup PkgPointSetProcessing /// Reads points from a .ply stream (ASCII or binary) using a custom /// interpreter provided by the user. /// /// @tparam PlyInterpreter Interpreter of Ply input, must be a model of `PlyInterpreter` /// @tparam Kernel Geometric traits class. /// /// @return true on success. //----------------------------------------------------------------------------------- template < typename PlyInterpreter, typename Kernel > bool read_ply_custom_points(std::istream& stream, ///< input stream. PlyInterpreter& interpreter, ///< Custom PLY interpreter const Kernel& /* kernel */) ///< geometric traits. { if(!stream) { std::cerr << "Error: cannot open file" << std::endl; return false; } Ply_reader reader; if (!(reader.init (stream))) return false; if (!(interpreter.is_applicable (reader))) { std::cerr << "Error: PLY interpreter is not applicable to input file" << std::endl; return false; } return reader.read_content (stream, interpreter); } //=================================================================================== /// \ingroup PkgPointSetProcessing /// Reads points (positions + normals, if available) from a .ply /// stream (ASCII or binary). /// Potential additional point properties and faces are ignored. /// /// @tparam OutputIteratorValueType type of objects that can be put in `OutputIterator`. /// It is default to `value_type_traits::%type` and can be omitted when the default is fine. /// @tparam OutputIterator iterator over output points. /// @tparam PointPMap is a model of `WritablePropertyMap` with value type `Point_3`. /// It can be omitted if the value type of `OutputIterator` is convertible to `Point_3`. /// @tparam NormalPMap is a model of `WritablePropertyMap` with value type `Vector_3`. /// @tparam Kernel Geometric traits class. /// It can be omitted and deduced automatically from the value type of `PointPMap`. /// /// @return true on success. // This variant requires all parameters. //----------------------------------------------------------------------------------- template < typename OutputIteratorValueType, typename OutputIterator, typename PointPMap, typename NormalPMap, typename Kernel > bool read_ply_points_and_normals(std::istream& stream, ///< input stream. OutputIterator output, ///< output iterator over points. PointPMap point_pmap, ///< property map: value_type of OutputIterator -> Point_3. NormalPMap normal_pmap, ///< property map: value_type of OutputIterator -> Vector_3. const Kernel& kernel) ///< geometric traits. { Ply_interpreter_points_and_normals_3 interpreter (output, point_pmap, normal_pmap); return read_ply_custom_points (stream, interpreter, kernel); } /// @cond SKIP_IN_MANUAL template < typename OutputIterator, typename PointPMap, typename NormalPMap, typename Kernel > bool read_ply_points_and_normals(std::istream& stream, ///< input stream. OutputIterator output, ///< output iterator over points. PointPMap point_pmap, ///< property map: value_type of OutputIterator -> Point_3. NormalPMap normal_pmap, ///< property map: value_type of OutputIterator -> Vector_3. const Kernel& kernel) ///< geometric traits. { // just deduce value_type of OutputIterator return read_ply_points_and_normals ::type>(stream, output, point_pmap, normal_pmap, kernel); } //----------------------------------------------------------------------------------- /// @endcond /// @cond SKIP_IN_MANUAL // This variant deduces the kernel from the point property map. //----------------------------------------------------------------------------------- template < typename OutputIteratorValueType, typename OutputIterator, typename PointPMap, typename NormalPMap > bool read_ply_points_and_normals(std::istream& stream, ///< input stream. OutputIterator output, ///< output iterator over points. PointPMap point_pmap, ///< property map: value_type of OutputIterator -> Point_3. NormalPMap normal_pmap) ///< property map: value_type of OutputIterator -> Vector_3. { typedef typename boost::property_traits::value_type Point; typedef typename Kernel_traits::Kernel Kernel; return read_ply_points_and_normals (stream, output, point_pmap, normal_pmap, Kernel()); } template < typename OutputIterator, typename PointPMap, typename NormalPMap > bool read_ply_points_and_normals(std::istream& stream, ///< input stream. OutputIterator output, ///< output iterator over points. PointPMap point_pmap, ///< property map: value_type of OutputIterator -> Point_3. NormalPMap normal_pmap) ///< property map: value_type of OutputIterator -> Vector_3. { // just deduce value_type of OutputIterator return read_ply_points_and_normals ::type>(stream, output, point_pmap, normal_pmap); } //----------------------------------------------------------------------------------- /// @endcond /// @cond SKIP_IN_MANUAL // This variant creates a default point property map = Identity_property_map. //----------------------------------------------------------------------------------- template < typename OutputIteratorValueType, typename OutputIterator, typename NormalPMap > bool read_ply_points_and_normals(std::istream& stream, ///< input stream. OutputIterator output, ///< output iterator over points. NormalPMap normal_pmap) ///< property map: value_type of OutputIterator -> Vector_3. { return read_ply_points_and_normals (stream, output, make_identity_property_map(OutputIteratorValueType()), normal_pmap); } template < typename OutputIterator, typename NormalPMap > bool read_ply_points_and_normals(std::istream& stream, ///< input stream. OutputIterator output, ///< output iterator over points. NormalPMap normal_pmap) ///< property map: value_type of OutputIterator -> Vector_3. { // just deduce value_type of OutputIterator return read_ply_points_and_normals ::type>(stream, output, normal_pmap); } //----------------------------------------------------------------------------------- /// @endcond //=================================================================================== /// \ingroup PkgPointSetProcessing /// Reads points (position only) from a .ply stream (ASCII or binary). /// Potential additional point properties (including normals) and faces are ignored. /// /// @tparam OutputIteratorValueType type of objects that can be put in `OutputIterator`. /// It is default to `value_type_traits::%type` and can be omitted when the default is fine. /// @tparam OutputIterator iterator over output points. /// @tparam PointPMap is a model of `WritablePropertyMap` with value_type `Point_3`. /// It can be omitted if the value type of `OutputIterator` is convertible to `Point_3`. /// @tparam Kernel Geometric traits class. /// It can be omitted and deduced automatically from the value type of `PointPMap`. /// /// @return `true` on success. // This variant requires all parameters. //----------------------------------------------------------------------------------- template < typename OutputIteratorValueType, typename OutputIterator, typename PointPMap, typename Kernel > bool read_ply_points(std::istream& stream, ///< input stream. OutputIterator output, ///< output iterator over points. PointPMap point_pmap, ///< property map: value_type of OutputIterator -> Point_3. const Kernel& kernel) ///< geometric traits. { // Calls read_ply_points_and_normals() with a normal property map = boost::dummy_property_map return read_ply_points_and_normals (stream, output, point_pmap, boost::dummy_property_map(), kernel); } /// @cond SKIP_IN_MANUAL template < typename OutputIterator, typename PointPMap, typename Kernel > bool read_ply_points(std::istream& stream, ///< input stream. OutputIterator output, ///< output iterator over points. PointPMap point_pmap, ///< property map: value_type of OutputIterator -> Point_3. const Kernel& kernel) ///< geometric traits. { // just deduce value_type of OutputIterator return read_ply_points ::type>(stream, output, point_pmap, kernel); } //----------------------------------------------------------------------------------- /// @endcond /// @cond SKIP_IN_MANUAL // This variant deduces the kernel from the point property map. //----------------------------------------------------------------------------------- template < typename OutputIteratorValueType, typename OutputIterator, typename PointPMap > bool read_ply_points(std::istream& stream, ///< input stream. OutputIterator output, ///< output iterator over points. PointPMap point_pmap) ///< property map: value_type of OutputIterator -> Point_3. { typedef typename boost::property_traits::value_type Point; typedef typename Kernel_traits::Kernel Kernel; return read_ply_points (stream, output, point_pmap, Kernel()); } template < typename OutputIterator, typename PointPMap > bool read_ply_points(std::istream& stream, ///< input stream. OutputIterator output, ///< output iterator over points. PointPMap point_pmap) ///< property map: value_type of OutputIterator -> Point_3. { // just deduce value_type of OutputIterator return read_ply_points ::type>(stream, output, point_pmap); } //----------------------------------------------------------------------------------- /// @endcond /// @cond SKIP_IN_MANUAL // This variant creates a default point property map = Identity_property_map. //----------------------------------------------------------------------------------- template < typename OutputIteratorValueType, typename OutputIterator > bool read_ply_points(std::istream& stream, ///< input stream. OutputIterator output) ///< output iterator over points. { return read_ply_points (stream, output, make_identity_property_map(OutputIteratorValueType()) ); } template < typename OutputIterator> bool read_ply_points(std::istream& stream, ///< input stream. OutputIterator output) ///< output iterator over points. { // just deduce value_type of OutputIterator return read_ply_points ::type>(stream, output); } //----------------------------------------------------------------------------------- /// @endcond } //namespace CGAL #endif // CGAL_READ_PLY_POINTS_H