Merge branch 'Point_set_processing_3-IO_ply-GF-old' into Point_set_processing_3-IO_ply-GF

This commit is contained in:
Simon Giraudot 2015-10-15 14:27:57 +02:00
commit 99d4fc84bd
16 changed files with 1074 additions and 9 deletions

View File

@ -161,6 +161,10 @@ and <code>src/</code> directories).
automatically adapt the local density of points to the local automatically adapt the local density of points to the local
variation of the input computed by principal component analysis. variation of the input computed by principal component analysis.
</li> </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> </ul>
<h3>Surface Mesh Parameterization</h3> <h3>Surface Mesh Parameterization</h3>
<ul> <ul>

View File

@ -25,6 +25,7 @@
## Functions ## ## Functions ##
- `CGAL::read_off_points()` - `CGAL::read_off_points()`
- `CGAL::read_ply_points()`
- `CGAL::read_xyz_points()` - `CGAL::read_xyz_points()`
- `CGAL::compute_average_spacing()` - `CGAL::compute_average_spacing()`
- `CGAL::remove_outliers()` - `CGAL::remove_outliers()`
@ -42,6 +43,7 @@
- `CGAL::vcm_estimate_normals()` - `CGAL::vcm_estimate_normals()`
- `CGAL::vcm_is_on_feature_edge()` - `CGAL::vcm_is_on_feature_edge()`
- `CGAL::write_off_points()` - `CGAL::write_off_points()`
- `CGAL::write_ply_points()`
- `CGAL::write_xyz_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 We provide functions to read and write sets of points or sets of
points with normals from the following ASCII file formats: XYZ (three points with normals from the following ASCII file formats: XYZ (three
point coordinates `x y z` per line or three point coordinates and 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 three normal vector coordinates `x y z nx ny nz` per line), OFF
(%Object File Format) \cgalCite{cgal:p-gmgv16-96}. (%Object File Format) \cgalCite{cgal:p-gmgv16-96} and PLY (Polygon
File Format).
- `read_xyz_points()` - `read_xyz_points()`
- `read_off_points()` - `read_off_points()`
- `read_ply_points()`
- `write_off_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 \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 3 0 0
1 1 1 2 2 2 1 1 1 2 2 2
3 3 3 4 4 4 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/Simple_cartesian.h>
#include <CGAL/property_map.h> #include <CGAL/property_map.h>
#include <CGAL/IO/read_off_points.h> #include <CGAL/IO/read_off_points.h>
#include <CGAL/IO/read_ply_points.h>
#include <CGAL/IO/read_xyz_points.h> #include <CGAL/IO/read_xyz_points.h>
#include <vector> #include <vector>
@ -47,6 +48,17 @@ bool read_off(std::string s,
CGAL::Second_of_pair_property_map<PointVectorPair>()); 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() 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[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))); 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; return 0;
} }

View File

@ -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) 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) 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) 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) 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; 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)
{ {
// Check extension (quietly)
std::string extension = fileinfo.suffix().toUtf8().data();
if (extension != "off" && extension != "OFF")
return false; 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 "Polyhedron_type.h"
#include <CGAL/Polygon_mesh_processing/compute_normal.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/read_off_points.h>
#include <CGAL/IO/write_off_points.h> #include <CGAL/IO/write_off_points.h>
#include <CGAL/IO/read_xyz_points.h> #include <CGAL/IO/read_xyz_points.h>
@ -317,6 +319,45 @@ void Scene_points_with_normal_item::selectDuplicates()
Q_EMIT itemChanged(); 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 // Loads point set from .OFF file
bool Scene_points_with_normal_item::read_off_point_set(std::istream& stream) bool Scene_points_with_normal_item::read_off_point_set(std::istream& stream)
{ {

View File

@ -36,6 +36,8 @@ public:
QMenu* contextMenu(); QMenu* contextMenu();
// IO // 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 read_off_point_set(std::istream& in);
bool write_off_point_set(std::ostream& out) const; bool write_off_point_set(std::ostream& out) const;
bool read_xyz_point_set(std::istream& in); bool read_xyz_point_set(std::istream& in);

View File

@ -126,6 +126,7 @@ else
orient_soup_plugin \ orient_soup_plugin \
parameterization_plugin \ parameterization_plugin \
pca_plugin \ pca_plugin \
ply_to_xyz_plugin \
point_dialog \ point_dialog \
point_inside_polyhedron_plugin \ point_inside_polyhedron_plugin \
point_set_average_spacing_plugin \ point_set_average_spacing_plugin \