diff --git a/Installation/changes.html b/Installation/changes.html
index 91a27548f8b..877960f897b 100644
--- a/Installation/changes.html
+++ b/Installation/changes.html
@@ -161,6 +161,10 @@ and src/ directories).
automatically adapt the local density of points to the local
variation of the input computed by principal component analysis.
+
New IO functions for PLY format (Polygon File
+ Format): CGAL::read_ply_points(),
+ CGAL::read_ply_points_and_normals(), CGAL::write_ply_points()
+ and CGAL::write_ply_points_and_normals().
Surface Mesh Parameterization
diff --git a/Point_set_processing_3/doc/Point_set_processing_3/PackageDescription.txt b/Point_set_processing_3/doc/Point_set_processing_3/PackageDescription.txt
index fc99524025a..9236e434369 100644
--- a/Point_set_processing_3/doc/Point_set_processing_3/PackageDescription.txt
+++ b/Point_set_processing_3/doc/Point_set_processing_3/PackageDescription.txt
@@ -25,6 +25,7 @@
## Functions ##
- `CGAL::read_off_points()`
+- `CGAL::read_ply_points()`
- `CGAL::read_xyz_points()`
- `CGAL::compute_average_spacing()`
- `CGAL::remove_outliers()`
@@ -42,6 +43,7 @@
- `CGAL::vcm_estimate_normals()`
- `CGAL::vcm_is_on_feature_edge()`
- `CGAL::write_off_points()`
+- `CGAL::write_ply_points()`
- `CGAL::write_xyz_points()`
*/
diff --git a/Point_set_processing_3/doc/Point_set_processing_3/Point_set_processing_3.txt b/Point_set_processing_3/doc/Point_set_processing_3/Point_set_processing_3.txt
index 70762bab334..011cea1f97e 100644
--- a/Point_set_processing_3/doc/Point_set_processing_3/Point_set_processing_3.txt
+++ b/Point_set_processing_3/doc/Point_set_processing_3/Point_set_processing_3.txt
@@ -80,13 +80,19 @@ a property map.
We provide functions to read and write sets of points or sets of
points with normals from the following ASCII file formats: XYZ (three
point coordinates `x y z` per line or three point coordinates and
-three normal vector coordinates `x y z nx ny nz` per line), and OFF
-(%Object File Format) \cgalCite{cgal:p-gmgv16-96}.
+three normal vector coordinates `x y z nx ny nz` per line), OFF
+(%Object File Format) \cgalCite{cgal:p-gmgv16-96} and PLY (Polygon
+File Format).
- `read_xyz_points()`
-- `read_off_points()`
+- `read_off_points()`
+- `read_ply_points()`
- `write_off_points()`
-- `write_xyz_points()`
+- `write_xyz_points()`
+- `write_ply_points()`
+
+Note that `read_ply_points()` also accepts binary PLY format in
+addition of ASCII.
\subsection Point_set_processing_3Example Example
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
new file mode 100644
index 00000000000..ad772f205c9
--- /dev/null
+++ b/Point_set_processing_3/include/CGAL/IO/read_ply_points.h
@@ -0,0 +1,615 @@
+// 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
+
+
+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
+
+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 double operator() (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();
+ }
+ };
+
+ class Ply_read_char : public Ply_read_number
+ {
+ public:
+ Ply_read_char (std::string name, std::size_t format) : Ply_read_number (name, format) { }
+ double operator() (std::istream& stream) const
+ { return static_cast (this->read (stream)); }
+ };
+ class Ply_read_uchar : public Ply_read_number
+ {
+ public:
+ Ply_read_uchar (std::string name, std::size_t format) : Ply_read_number (name, format) { }
+ double operator() (std::istream& stream) const
+ { return static_cast (this->read (stream)); }
+ };
+ class Ply_read_short : public Ply_read_number
+ {
+ public:
+ Ply_read_short (std::string name, std::size_t format) : Ply_read_number (name, format) { }
+ double operator() (std::istream& stream) const
+ { return static_cast (this->read (stream)); }
+ };
+ class Ply_read_ushort : public Ply_read_number
+ {
+ public:
+ Ply_read_ushort (std::string name, std::size_t format) : Ply_read_number (name, format) { }
+ double operator() (std::istream& stream) const
+ { return static_cast (this->read (stream)); }
+ };
+ class Ply_read_int : public Ply_read_number
+ {
+ public:
+ Ply_read_int (std::string name, std::size_t format) : Ply_read_number (name, format) { }
+ double operator() (std::istream& stream) const
+ { return static_cast (this->read (stream)); }
+ };
+ class Ply_read_uint : public Ply_read_number
+ {
+ public:
+ Ply_read_uint (std::string name, std::size_t format) : Ply_read_number (name, format) { }
+ double operator() (std::istream& stream) const
+ { return static_cast (this->read (stream)); }
+ };
+ class Ply_read_float : public Ply_read_number
+ {
+ public:
+ Ply_read_float (std::string name, std::size_t format) : Ply_read_number (name, format) { }
+ double operator() (std::istream& stream) const
+ { return static_cast (this->read (stream)); }
+ };
+ class Ply_read_double : public Ply_read_number
+ {
+ public:
+ Ply_read_double (std::string name, std::size_t format) : Ply_read_number (name, format) { }
+ double operator() (std::istream& stream) const
+ { return this->read (stream); }
+ };
+
+
+
+} //namespace CGAL
+
+//===================================================================================
+/// \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.
+{
+ // 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::Point_3 Point;
+ typedef typename Kernel::Vector_3 Vector;
+ typedef typename Kernel::FT FT;
+
+ if(!stream)
+ {
+ std::cerr << "Error: cannot open file" << std::endl;
+ return false;
+ }
+
+ // scan points
+ std::size_t pointsCount = 0, // number of items in file
+ pointsRead = 0, // current number of points read
+ 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;
+ std::vector readers;
+
+ 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 << "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 << "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")
+ readers.push_back (new internal::Ply_read_char (name, format));
+ else if (type == "uchar" || type == "uint8")
+ readers.push_back (new internal::Ply_read_uchar (name, format));
+ else if (type == "short" || type == "int16")
+ readers.push_back (new internal::Ply_read_short (name, format));
+ else if (type == "ushort" || type == "uint16")
+ readers.push_back (new internal::Ply_read_ushort (name, format));
+ else if (type == "int" || type == "int32")
+ readers.push_back (new internal::Ply_read_int (name, format));
+ else if (type == "uint" || type == "uint32")
+ readers.push_back (new internal::Ply_read_uint (name, format));
+ else if (type == "float" || type == "float32")
+ readers.push_back (new internal::Ply_read_float (name, format));
+ else if (type == "double" || type == "float64")
+ readers.push_back (new internal::Ply_read_double (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")
+ {
+ pointsCount = number;
+ reading_properties = true;
+ }
+ else
+ continue;
+ }
+
+ }
+ }
+
+ while (!(stream.eof()) && pointsRead < pointsCount)
+ {
+
+ FT x = 0., y = 0., z = 0., nx = 0., ny = 0., nz = 0.;
+ for (std::size_t i = 0; i < readers.size (); ++ i)
+ {
+ FT value = (*readers[i])(stream);
+ if (readers[i]->name () == "x") x = value;
+ else if (readers[i]->name () == "y") y = value;
+ else if (readers[i]->name () == "z") z = value;
+ else if (readers[i]->name () == "nx") nx = value;
+ else if (readers[i]->name () == "ny") ny = value;
+ else if (readers[i]->name () == "nz") nz = value;
+ }
+ Point point(x,y,z);
+ Vector normal(nx,ny,nz);
+ Enriched_point pwn;
+
+#ifdef CGAL_USE_PROPERTY_MAPS_API_V1
+ put(point_pmap, &pwn, point); // point_pmap[&pwn] = point
+ put(normal_pmap, &pwn, normal); // normal_pmap[&pwn] = normal
+#else
+ put(point_pmap, pwn, point); // point_pmap[&pwn] = point
+ put(normal_pmap, pwn, normal); // normal_pmap[&pwn] = normal
+#endif
+ *output++ = pwn;
+ pointsRead++;
+ }
+ // Skip remaining lines
+
+ for (std::size_t i = 0; i < readers.size (); ++ i)
+ delete readers[i];
+
+ return true;
+}
+
+/// @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,
+#ifdef CGAL_USE_PROPERTY_MAPS_API_V1
+ make_dereference_property_map(output),
+#else
+ make_identity_property_map(OutputIteratorValueType()),
+#endif
+ 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,
+#ifdef CGAL_USE_PROPERTY_MAPS_API_V1
+ make_dereference_property_map(output)
+#else
+ make_identity_property_map(OutputIteratorValueType())
+#endif
+ );
+}
+
+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
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
new file mode 100644
index 00000000000..274713ec6a6
--- /dev/null
+++ b/Point_set_processing_3/include/CGAL/IO/write_ply_points.h
@@ -0,0 +1,260 @@
+// 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_WRITE_PLY_POINTS_H
+#define CGAL_WRITE_PLY_POINTS_H
+
+#include
+#include
+
+#include
+
+#include
+#include
+
+namespace CGAL {
+
+
+//===================================================================================
+/// \ingroup PkgPointSetProcessing
+/// Saves the [first, beyond) range of points (positions + normals) to a .ply ASCII stream.
+///
+/// \pre normals must be unit vectors
+///
+/// @tparam ForwardIterator iterator over input points.
+/// @tparam PointPMap is a model of `ReadablePropertyMap` with value type `Point_3`.
+/// It can be omitted if the value type of `ForwardIterator` is convertible to `Point_3`.
+/// @tparam NormalPMap is a model of `ReadablePropertyMap` with a 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 ForwardIterator,
+ typename PointPMap,
+ typename NormalPMap,
+ typename Kernel >
+bool
+write_ply_points_and_normals(
+ std::ostream& stream, ///< output stream.
+ ForwardIterator first, ///< iterator over the first input point.
+ ForwardIterator beyond, ///< past-the-end iterator over the input points.
+ PointPMap point_pmap, ///< property map: value_type of ForwardIterator -> Point_3.
+ NormalPMap normal_pmap, ///< property map: value_type of ForwardIterator -> Vector_3.
+ const Kernel& /*kernel*/) ///< geometric traits.
+{
+ // basic geometric types
+ typedef typename Kernel::Point_3 Point;
+ typedef typename Kernel::Vector_3 Vector;
+
+ CGAL_point_set_processing_precondition(first != beyond);
+
+ if(!stream)
+ {
+ std::cerr << "Error: cannot open file" << std::endl;
+ return false;
+ }
+
+ // Write header
+ stream << "ply" << std::endl
+ << "format ascii 1.0" << std::endl
+ << "comment Generated by the CGAL library" << std::endl
+ << "element vertex " << std::distance (first, beyond) << std::endl
+ << "property double x" << std::endl
+ << "property double y" << std::endl
+ << "property double z" << std::endl
+ << "property double nx" << std::endl
+ << "property double ny" << std::endl
+ << "property double nz" << std::endl
+ << "end_header" << std::endl;
+
+
+ // Write positions + normals
+ for(ForwardIterator it = first; it != beyond; it++)
+ {
+#ifdef CGAL_USE_PROPERTY_MAPS_API_V1
+ Point p = get(point_pmap, it);
+ Vector n = get(normal_pmap, it);
+#else
+ Point p = get(point_pmap, *it);
+ Vector n = get(normal_pmap, *it);
+#endif
+ stream << p << " " << n << std::endl;
+ }
+
+ return ! stream.fail();
+}
+
+/// @cond SKIP_IN_MANUAL
+// This variant deduces the kernel from the point property map.
+template < typename ForwardIterator,
+ typename PointPMap,
+ typename NormalPMap >
+bool
+write_ply_points_and_normals(
+ std::ostream& stream, ///< output stream.
+ ForwardIterator first, ///< first input point.
+ ForwardIterator beyond, ///< past-the-end input point.
+ 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 write_ply_points_and_normals(
+ stream,
+ first, beyond,
+ point_pmap,
+ normal_pmap,
+ Kernel());
+}
+/// @endcond
+
+/// @cond SKIP_IN_MANUAL
+// This variant creates a default point property map = Identity_property_map.
+template
+bool
+write_ply_points_and_normals(
+ std::ostream& stream, ///< output stream.
+ ForwardIterator first, ///< first input point.
+ ForwardIterator beyond, ///< past-the-end input point.
+ NormalPMap normal_pmap) ///< property map: value_type of OutputIterator -> Vector_3.
+{
+ return write_ply_points_and_normals(
+ stream,
+ first, beyond,
+#ifdef CGAL_USE_PROPERTY_MAPS_API_V1
+ make_dereference_property_map(first),
+#else
+ make_identity_property_map(
+ typename std::iterator_traits::value_type()),
+#endif
+ normal_pmap);
+}
+/// @endcond
+
+
+//===================================================================================
+/// \ingroup PkgPointSetProcessing
+/// Saves the [first, beyond) range of points (positions only) to a .ply ASCII stream.
+///
+/// @tparam ForwardIterator iterator over input points.
+/// @tparam PointPMap is a model of `ReadablePropertyMap` with a value_type = `Point_3`.
+/// It can be omitted if the value type of `ForwardIterator` 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 ForwardIterator,
+ typename PointPMap,
+ typename Kernel >
+bool
+write_ply_points(
+ std::ostream& stream, ///< output stream.
+ ForwardIterator first, ///< iterator over the first input point.
+ ForwardIterator beyond, ///< past-the-end iterator over the input points.
+ PointPMap point_pmap, ///< property map: value_type of ForwardIterator -> Point_3.
+ const Kernel& ) ///< geometric traits.
+{
+ // basic geometric types
+ typedef typename Kernel::Point_3 Point;
+
+ CGAL_point_set_processing_precondition(first != beyond);
+
+ if(!stream)
+ {
+ std::cerr << "Error: cannot open file" << std::endl;
+ return false;
+ }
+
+ // Write header
+ stream << "ply" << std::endl
+ << "format ascii 1.0" << std::endl
+ << "comment Generated by the CGAL library" << std::endl
+ << "element vertex " << std::distance (first, beyond) << std::endl
+ << "property double x" << std::endl
+ << "property double y" << std::endl
+ << "property double z" << std::endl
+ << "end_header" << std::endl;
+
+ // Write positions
+ for(ForwardIterator it = first; it != beyond; it++)
+ {
+#ifdef CGAL_USE_PROPERTY_MAPS_API_V1
+ Point p = get(point_pmap, it);
+#else
+ Point p = get(point_pmap, *it);
+#endif
+ stream << p << std::endl;
+ }
+
+ return ! stream.fail();
+}
+
+/// @cond SKIP_IN_MANUAL
+// This variant deduces the kernel from the point property map.
+template < typename ForwardIterator,
+ typename PointPMap >
+bool
+write_ply_points(
+ std::ostream& stream, ///< output stream.
+ ForwardIterator first, ///< first input point.
+ ForwardIterator beyond, ///< past-the-end input point.
+ 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 write_ply_points(
+ stream,
+ first, beyond,
+ point_pmap,
+ Kernel());
+}
+/// @endcond
+
+/// @cond SKIP_IN_MANUAL
+// This variant creates a default point property map = Identity_property_map.
+template < typename ForwardIterator >
+bool
+write_ply_points(
+ std::ostream& stream, ///< output stream.
+ ForwardIterator first, ///< first input point.
+ ForwardIterator beyond) ///< past-the-end input point.
+{
+ return write_ply_points(
+ stream,
+ first, beyond,
+#ifdef CGAL_USE_PROPERTY_MAPS_API_V1
+ make_dereference_property_map(first)
+#else
+ make_identity_property_map(
+ typename std::iterator_traits::value_type())
+#endif
+ );
+}
+/// @endcond
+
+
+} //namespace CGAL
+
+#endif // CGAL_WRITE_PLY_POINTS_H
diff --git a/Point_set_processing_3/test/Point_set_processing_3/data/read_test/simple.off b/Point_set_processing_3/test/Point_set_processing_3/data/read_test/simple.off
index 71f3a1ccc32..462dc6fb8e6 100644
--- a/Point_set_processing_3/test/Point_set_processing_3/data/read_test/simple.off
+++ b/Point_set_processing_3/test/Point_set_processing_3/data/read_test/simple.off
@@ -1,4 +1,4 @@
-OFF
+NOFF
3 0 0
1 1 1 2 2 2
3 3 3 4 4 4
diff --git a/Point_set_processing_3/test/Point_set_processing_3/data/read_test/simple.ply b/Point_set_processing_3/test/Point_set_processing_3/data/read_test/simple.ply
new file mode 100644
index 00000000000..1f55c3277c0
Binary files /dev/null and b/Point_set_processing_3/test/Point_set_processing_3/data/read_test/simple.ply differ
diff --git a/Point_set_processing_3/test/Point_set_processing_3/data/read_test/simple_ascii.ply b/Point_set_processing_3/test/Point_set_processing_3/data/read_test/simple_ascii.ply
new file mode 100644
index 00000000000..5bc4a19d34f
--- /dev/null
+++ b/Point_set_processing_3/test/Point_set_processing_3/data/read_test/simple_ascii.ply
@@ -0,0 +1,16 @@
+ply
+format ascii 1.0
+comment VCGLIB generated
+element vertex 3
+property float x
+property float y
+property float z
+property float nx
+property float ny
+property float nz
+element face 0
+property list uchar int vertex_indices
+end_header
+1 1 1 2 2 2
+3 3 3 4 4 4
+5 5 5 6 6 6
diff --git a/Point_set_processing_3/test/Point_set_processing_3/data/read_test/simple_with_flag.ply b/Point_set_processing_3/test/Point_set_processing_3/data/read_test/simple_with_flag.ply
new file mode 100644
index 00000000000..55661dd36bc
Binary files /dev/null and b/Point_set_processing_3/test/Point_set_processing_3/data/read_test/simple_with_flag.ply differ
diff --git a/Point_set_processing_3/test/Point_set_processing_3/read_test.cpp b/Point_set_processing_3/test/Point_set_processing_3/read_test.cpp
index 297faf29399..5d32d7d3094 100644
--- a/Point_set_processing_3/test/Point_set_processing_3/read_test.cpp
+++ b/Point_set_processing_3/test/Point_set_processing_3/read_test.cpp
@@ -2,6 +2,7 @@
#include
#include
#include
+#include
#include
#include
@@ -47,6 +48,17 @@ bool read_off(std::string s,
CGAL::Second_of_pair_property_map());
}
+bool read_ply (std::string s,
+ std::vector& pv_pairs)
+{
+ std::ifstream fs(s.c_str());
+
+ return CGAL::read_ply_points_and_normals (fs,
+ back_inserter(pv_pairs),
+ CGAL::First_of_pair_property_map(),
+ CGAL::Second_of_pair_property_map());
+}
+
int main()
{
@@ -78,6 +90,23 @@ int main()
assert(pv_pairs[2] == std::make_pair(Point_3(4,5,6), Vector_3(0,0,0)));
assert(pv_pairs[3] == std::make_pair(Point_3(7,8,9), Vector_3(0,0,0)));
+ pv_pairs.clear ();
+ assert(read_ply("data/read_test/simple.ply", pv_pairs));
+ assert(pv_pairs[0] == std::make_pair(Point_3(1,1,1), Vector_3(2,2,2)));
+ assert(pv_pairs[1] == std::make_pair(Point_3(3,3,3), Vector_3(4,4,4)));
+ assert(pv_pairs[2] == std::make_pair(Point_3(5,5,5), Vector_3(6,6,6)));
+
+ pv_pairs.clear ();
+ assert(read_ply("data/read_test/simple_ascii.ply", pv_pairs));
+ assert(pv_pairs[0] == std::make_pair(Point_3(1,1,1), Vector_3(2,2,2)));
+ assert(pv_pairs[1] == std::make_pair(Point_3(3,3,3), Vector_3(4,4,4)));
+ assert(pv_pairs[2] == std::make_pair(Point_3(5,5,5), Vector_3(6,6,6)));
+
+ pv_pairs.clear ();
+ assert(read_ply("data/read_test/simple_with_flag.ply", pv_pairs));
+ assert(pv_pairs[0] == std::make_pair(Point_3(1,1,1), Vector_3(2,2,2)));
+ assert(pv_pairs[1] == std::make_pair(Point_3(3,3,3), Vector_3(4,4,4)));
+ assert(pv_pairs[2] == std::make_pair(Point_3(5,5,5), Vector_3(6,6,6)));
return 0;
}
diff --git a/Polyhedron/demo/Polyhedron/CMakeLists.txt b/Polyhedron/demo/Polyhedron/CMakeLists.txt
index 2df990619b6..5b54bcc5c22 100644
--- a/Polyhedron/demo/Polyhedron/CMakeLists.txt
+++ b/Polyhedron/demo/Polyhedron/CMakeLists.txt
@@ -319,6 +319,9 @@ if(CGAL_Qt5_FOUND AND Qt5_FOUND AND OPENGL_FOUND AND QGLVIEWER_FOUND)
polyhedron_demo_plugin(off_to_xyz_plugin Polyhedron_demo_off_to_xyz_plugin)
target_link_libraries(off_to_xyz_plugin scene_points_with_normal_item)
+ polyhedron_demo_plugin(ply_to_xyz_plugin Polyhedron_demo_ply_to_xyz_plugin)
+ target_link_libraries(ply_to_xyz_plugin scene_points_with_normal_item)
+
polyhedron_demo_plugin(convex_hull_plugin Polyhedron_demo_convex_hull_plugin)
target_link_libraries(convex_hull_plugin scene_polyhedron_item scene_points_with_normal_item scene_polylines_item scene_polyhedron_selection_item)
diff --git a/Polyhedron/demo/Polyhedron/Polyhedron_demo_off_to_xyz_plugin.cpp b/Polyhedron/demo/Polyhedron/Polyhedron_demo_off_to_xyz_plugin.cpp
index cc32c58e245..375adf00f29 100644
--- a/Polyhedron/demo/Polyhedron/Polyhedron_demo_off_to_xyz_plugin.cpp
+++ b/Polyhedron/demo/Polyhedron/Polyhedron_demo_off_to_xyz_plugin.cpp
@@ -56,14 +56,28 @@ Polyhedron_demo_off_to_xyz_plugin::load(QFileInfo fileinfo) {
return item;
}
-bool Polyhedron_demo_off_to_xyz_plugin::canSave(const Scene_item*)
+bool Polyhedron_demo_off_to_xyz_plugin::canSave(const Scene_item* item)
{
- return false;
+ // This plugin supports point sets
+ return qobject_cast(item);
}
-bool Polyhedron_demo_off_to_xyz_plugin::save(const Scene_item*, QFileInfo)
+bool Polyhedron_demo_off_to_xyz_plugin::save(const Scene_item* item, QFileInfo fileinfo)
{
- return false;
+ // Check extension (quietly)
+ std::string extension = fileinfo.suffix().toUtf8().data();
+ if (extension != "off" && extension != "OFF")
+ return false;
+
+ // This plugin supports point sets
+ const Scene_points_with_normal_item* point_set_item =
+ qobject_cast(item);
+ if(!point_set_item)
+ return false;
+
+ // Save point set as .xyz
+ std::ofstream out(fileinfo.filePath().toUtf8().data());
+ return point_set_item->write_off_point_set(out);
}
diff --git a/Polyhedron/demo/Polyhedron/Polyhedron_demo_ply_to_xyz_plugin.cpp b/Polyhedron/demo/Polyhedron/Polyhedron_demo_ply_to_xyz_plugin.cpp
new file mode 100644
index 00000000000..91ddfec4c63
--- /dev/null
+++ b/Polyhedron/demo/Polyhedron/Polyhedron_demo_ply_to_xyz_plugin.cpp
@@ -0,0 +1,72 @@
+#include "Scene_points_with_normal_item.h"
+#include "Polyhedron_demo_io_plugin_interface.h"
+
+#include
+
+class Polyhedron_demo_ply_to_xyz_plugin :
+ public QObject,
+ public Polyhedron_demo_io_plugin_interface
+{
+ Q_OBJECT
+ Q_INTERFACES(Polyhedron_demo_io_plugin_interface)
+ Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.IOPluginInterface/1.0")
+
+public:
+ QString name() const { return "ply_to_xyz_plugin"; }
+ QString nameFilters() const { return "PLY files as Point set (*.ply)"; }
+ bool canLoad() const;
+ Scene_item* load(QFileInfo fileinfo);
+
+ bool canSave(const Scene_item*);
+ bool save(const Scene_item*, QFileInfo fileinfo);
+};
+
+bool Polyhedron_demo_ply_to_xyz_plugin::canLoad() const {
+ return true;
+}
+
+Scene_item*
+Polyhedron_demo_ply_to_xyz_plugin::load(QFileInfo fileinfo) {
+ std::ifstream in(fileinfo.filePath().toUtf8());
+
+ if(!in)
+ std::cerr << "Error!\n";
+
+ Scene_points_with_normal_item* item;
+ item = new Scene_points_with_normal_item();
+ if(!item->read_ply_point_set(in))
+ {
+ delete item;
+ return 0;
+ }
+
+ item->setName(fileinfo.baseName());
+ return item;
+}
+
+bool Polyhedron_demo_ply_to_xyz_plugin::canSave(const Scene_item* item)
+{
+ // This plugin supports point sets
+ return qobject_cast(item);
+}
+
+bool Polyhedron_demo_ply_to_xyz_plugin::save(const Scene_item* item, QFileInfo fileinfo)
+{
+ // Check extension (quietly)
+ std::string extension = fileinfo.suffix().toUtf8().data();
+ if (extension != "ply" && extension != "PLY")
+ return false;
+
+ // This plugin supports point sets
+ const Scene_points_with_normal_item* point_set_item =
+ qobject_cast(item);
+ if(!point_set_item)
+ return false;
+
+ // Save point set as .xyz
+ std::ofstream out(fileinfo.filePath().toUtf8().data());
+ return point_set_item->write_ply_point_set(out);
+}
+
+
+#include "Polyhedron_demo_ply_to_xyz_plugin.moc"
diff --git a/Polyhedron/demo/Polyhedron/Scene_points_with_normal_item.cpp b/Polyhedron/demo/Polyhedron/Scene_points_with_normal_item.cpp
index e583cbb6cf5..8c9e3a33584 100644
--- a/Polyhedron/demo/Polyhedron/Scene_points_with_normal_item.cpp
+++ b/Polyhedron/demo/Polyhedron/Scene_points_with_normal_item.cpp
@@ -2,6 +2,8 @@
#include "Polyhedron_type.h"
#include
+#include
+#include
#include
#include
#include
@@ -317,6 +319,45 @@ void Scene_points_with_normal_item::selectDuplicates()
Q_EMIT itemChanged();
}
+// Loads point set from .PLY file
+bool Scene_points_with_normal_item::read_ply_point_set(std::istream& stream)
+{
+ Q_ASSERT(m_points != NULL);
+
+ m_points->clear();
+ bool ok = stream &&
+ CGAL::read_ply_points_and_normals(stream,
+ std::back_inserter(*m_points),
+ CGAL::make_normal_of_point_with_normal_pmap(Point_set::value_type())) &&
+ !isEmpty();
+ if (ok)
+ {
+ for (Point_set::iterator it=m_points->begin(),
+ end=m_points->end();it!=end; ++it)
+ {
+ if (it->normal() != CGAL::NULL_VECTOR)
+ {
+ m_has_normals=true;
+ setRenderingMode(PointsPlusNormals);
+ break;
+ }
+ }
+ }
+ invalidate_buffers();
+ return ok;
+}
+
+// Write point set to .PLY file
+bool Scene_points_with_normal_item::write_ply_point_set(std::ostream& stream) const
+{
+ Q_ASSERT(m_points != NULL);
+
+ return stream &&
+ CGAL::write_ply_points_and_normals(stream,
+ m_points->begin(), m_points->end(),
+ CGAL::make_normal_of_point_with_normal_pmap(Point_set::value_type()));
+}
+
// Loads point set from .OFF file
bool Scene_points_with_normal_item::read_off_point_set(std::istream& stream)
{
diff --git a/Polyhedron/demo/Polyhedron/Scene_points_with_normal_item.h b/Polyhedron/demo/Polyhedron/Scene_points_with_normal_item.h
index 4e4f6b82e0e..02c726b7c1b 100644
--- a/Polyhedron/demo/Polyhedron/Scene_points_with_normal_item.h
+++ b/Polyhedron/demo/Polyhedron/Scene_points_with_normal_item.h
@@ -36,6 +36,8 @@ public:
QMenu* contextMenu();
// IO
+ bool read_ply_point_set(std::istream& in);
+ bool write_ply_point_set(std::ostream& out) const;
bool read_off_point_set(std::istream& in);
bool write_off_point_set(std::ostream& out) const;
bool read_xyz_point_set(std::istream& in);
diff --git a/Polyhedron/demo/Polyhedron/cgal_test_with_cmake b/Polyhedron/demo/Polyhedron/cgal_test_with_cmake
index 2f49750a143..203a417cde9 100755
--- a/Polyhedron/demo/Polyhedron/cgal_test_with_cmake
+++ b/Polyhedron/demo/Polyhedron/cgal_test_with_cmake
@@ -126,6 +126,7 @@ else
orient_soup_plugin \
parameterization_plugin \
pca_plugin \
+ ply_to_xyz_plugin \
point_dialog \
point_inside_polyhedron_plugin \
point_set_average_spacing_plugin \