Merge pull request #400 from sgiraudot/Point_set_processing_3-IO_ply-GF

Read and write PLY point clouds
This commit is contained in:
Sebastien Loriot 2015-10-22 08:56:10 +02:00
commit 1dbac9df32
16 changed files with 1074 additions and 9 deletions

View File

@ -174,6 +174,10 @@ and <code>src/</code> directories).
automatically adapt the local density of points to the local
variation of the input computed by principal component analysis.
</li>
<li> New IO functions for PLY format (Polygon File
Format): <code>CGAL::read_ply_points()</code>,
<code>CGAL::read_ply_points_and_normals()</code>, <code>CGAL::write_ply_points()</code>
and <code>CGAL::write_ply_points_and_normals()</code>.</li>
</ul>
<h3>Surface Mesh Parameterization</h3>
<ul>

View File

@ -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()`
*/

View File

@ -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

View File

@ -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 <CGAL/property_map.h>
#include <CGAL/value_type_traits.h>
#include <CGAL/point_set_processing_assertions.h>
#include <boost/version.hpp>
#include <boost/cstdint.hpp>
#include <iostream>
#include <sstream>
#include <string>
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<char>(s);
}
void read_ascii (std::istream& stream, boost::uint8_t& c) const
{
unsigned short s;
stream >> s;
c = static_cast<unsigned char>(s);
}
// Default template when Type is not a char type
template <typename Type>
void read_ascii (std::istream& stream, Type& t) const
{
stream >> t;
}
template <typename Type>
Type read (std::istream& stream) const
{
if (m_format == 0) // Ascii
{
Type t;
read_ascii (stream, t);
return t;
}
else // Binary (2 = little endian)
{
union
{
char uChar[sizeof (Type)];
Type type;
} buffer;
std::size_t size = sizeof (Type);
stream.read(buffer.uChar, size);
if (m_format == 2) // Big endian
{
for (std::size_t i = 0; i < size / 2; ++ i)
{
unsigned char tmp = buffer.uChar[i];
buffer.uChar[i] = buffer.uChar[size - 1 - i];
buffer.uChar[size - 1 - i] = tmp;
}
}
return buffer.type;
}
return Type();
}
};
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<double> (this->read<boost::int8_t> (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<double> (this->read<boost::uint8_t> (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<double> (this->read<boost::int16_t> (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<double> (this->read<boost::uint16_t> (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<double> (this->read<boost::int32_t> (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<double> (this->read<boost::uint32_t> (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<double> (this->read<float> (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<float> (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<OutputIterator>::%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<Kernel>`.
/// It can be omitted if the value type of `OutputIterator` is convertible to `Point_3<Kernel>`.
/// @tparam NormalPMap is a model of `WritablePropertyMap` with value type `Vector_3<Kernel>`.
/// @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<OutputIterator>::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<internal::Ply_read_number*> 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
<typename value_type_traits<OutputIterator>::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<PointPMap>::value_type Point;
typedef typename Kernel_traits<Point>::Kernel Kernel;
return read_ply_points_and_normals
<OutputIteratorValueType>(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
<typename value_type_traits<OutputIterator>::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
<OutputIteratorValueType>(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
<typename value_type_traits<OutputIterator>::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<OutputIterator>::%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<Kernel>`.
/// It can be omitted if the value type of `OutputIterator` is convertible to `Point_3<Kernel>`.
/// @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
<OutputIteratorValueType>(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
<typename value_type_traits<OutputIterator>::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<PointPMap>::value_type Point;
typedef typename Kernel_traits<Point>::Kernel Kernel;
return read_ply_points
<OutputIteratorValueType>(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
<typename value_type_traits<OutputIterator>::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
<OutputIteratorValueType>(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
<typename value_type_traits<OutputIterator>::type>(stream,
output);
}
//-----------------------------------------------------------------------------------
/// @endcond
} //namespace CGAL
#endif // CGAL_READ_PLY_POINTS_H

View File

@ -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 <CGAL/property_map.h>
#include <CGAL/point_set_processing_assertions.h>
#include <boost/version.hpp>
#include <iostream>
#include <iterator>
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<Kernel>`.
/// It can be omitted if the value type of `ForwardIterator` is convertible to `Point_3<Kernel>`.
/// @tparam NormalPMap is a model of `ReadablePropertyMap` with a value type `Vector_3<Kernel>`.
/// @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<PointPMap>::value_type Point;
typedef typename Kernel_traits<Point>::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 <typename ForwardIterator,
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.
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<ForwardIterator>::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<Kernel>`.
/// It can be omitted if the value type of `ForwardIterator` is convertible to `Point_3<Kernel>`.
/// @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<PointPMap>::value_type Point;
typedef typename Kernel_traits<Point>::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<ForwardIterator>::value_type())
#endif
);
}
/// @endcond
} //namespace CGAL
#endif // CGAL_WRITE_PLY_POINTS_H

View File

@ -1,4 +1,4 @@
OFF
NOFF
3 0 0
1 1 1 2 2 2
3 3 3 4 4 4

View File

@ -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

View File

@ -2,6 +2,7 @@
#include <CGAL/Simple_cartesian.h>
#include <CGAL/property_map.h>
#include <CGAL/IO/read_off_points.h>
#include <CGAL/IO/read_ply_points.h>
#include <CGAL/IO/read_xyz_points.h>
#include <vector>
@ -47,6 +48,17 @@ bool read_off(std::string s,
CGAL::Second_of_pair_property_map<PointVectorPair>());
}
bool read_ply (std::string s,
std::vector<PointVectorPair>& 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<PointVectorPair>(),
CGAL::Second_of_pair_property_map<PointVectorPair>());
}
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;
}

View File

@ -332,6 +332,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)

View File

@ -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<const Scene_points_with_normal_item*>(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<const Scene_points_with_normal_item*>(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);
}

View File

@ -0,0 +1,72 @@
#include "Scene_points_with_normal_item.h"
#include "Polyhedron_demo_io_plugin_interface.h"
#include <fstream>
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<const Scene_points_with_normal_item*>(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<const Scene_points_with_normal_item*>(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"

View File

@ -2,6 +2,8 @@
#include "Polyhedron_type.h"
#include <CGAL/Polygon_mesh_processing/compute_normal.h>
#include <CGAL/IO/read_ply_points.h>
#include <CGAL/IO/write_ply_points.h>
#include <CGAL/IO/read_off_points.h>
#include <CGAL/IO/write_off_points.h>
#include <CGAL/IO/read_xyz_points.h>
@ -271,6 +273,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)
{

View File

@ -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);

View File

@ -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 \