Rework Surface_mesh IO

This commit is contained in:
Mael Rouxel-Labbé 2020-01-28 18:36:30 +01:00
parent 65c1a4f81b
commit b1da380064
22 changed files with 799 additions and 1025 deletions

View File

@ -7,6 +7,7 @@
// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial // SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial
// //
// Author(s) : Maxime Gimeno // Author(s) : Maxime Gimeno
// Mael Rouxel-Labbé
#ifndef CGAL_BGL_IO_GENERIC_FACEGRAPH_BUILDER_H #ifndef CGAL_BGL_IO_GENERIC_FACEGRAPH_BUILDER_H
#define CGAL_BGL_IO_GENERIC_FACEGRAPH_BUILDER_H #define CGAL_BGL_IO_GENERIC_FACEGRAPH_BUILDER_H

View File

@ -136,7 +136,7 @@ public:
if(has_face_colors) if(has_face_colors)
{ {
const CGAL::Color& fc = get(fcm, f); const CGAL::Color& fc = get(fcm, f);
m_writer.write_face_normal(fc.red(), fc.green(), fc.blue()); m_writer.write_face_color(fc.red(), fc.green(), fc.blue());
} }
m_writer.write_facet_end(); m_writer.write_facet_end();

View File

@ -60,30 +60,12 @@ public:
} }
}; };
} // namespace internal // Because some packages can provide overloads with the same signature to automatically initialize
} // namespace IO // property maps (see Surface_mesh/IO/ for example)
/*!
\ingroup PkgBGLIOFct
reads the graph `g` from data in the OFF format. Ignores comment lines which start with a hash, and lines with whitespace.
\cgalNamedParamsBegin
\cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `g`.
If this parameter is omitted, an internal property map for
`CGAL::vertex_point_t` should be available in `FaceGraph`\cgalParamEnd
\cgalNamedParamsEnd
\sa Overloads of this function for specific models of the concept `FaceGraph`.
\pre The data must represent a 2-manifold
\see \ref IOStreamOFF
*/
template <typename FaceGraph, typename CGAL_BGL_NP_TEMPLATE_PARAMETERS> template <typename FaceGraph, typename CGAL_BGL_NP_TEMPLATE_PARAMETERS>
bool read_OFF(std::istream& in, bool read_OFF_BGL(std::istream& in,
FaceGraph& g, FaceGraph& g,
const CGAL_BGL_NP_CLASS& np) const CGAL_BGL_NP_CLASS& np)
{ {
typedef typename CGAL::GetVertexPointMap<FaceGraph, CGAL_BGL_NP_CLASS>::type VPM; typedef typename CGAL::GetVertexPointMap<FaceGraph, CGAL_BGL_NP_CLASS>::type VPM;
typedef typename boost::property_traits<VPM>::value_type Point; typedef typename boost::property_traits<VPM>::value_type Point;
@ -92,24 +74,36 @@ bool read_OFF(std::istream& in,
return builder(g, np); return builder(g, np);
} }
} // namespace internal
} // namespace IO
/*! /*!
\ingroup PkgBGLIOFct \ingroup PkgBGLIOFct
reads the graph `g` from data in the OFF format. Ignores comment lines which start with a hash, reads the graph `g` from data in the OFF format. Ignores comment lines which start with a hash,
and lines with whitespace. and lines with whitespace.
\sa Overloads of this function for specific models of the concept `FaceGraph`. \cgalNamedParamsBegin
\cgalParamBegin{vertex_point_map} the property map with the points associated to the vertices of `g`.
If this parameter is omitted, an internal property map for
`CGAL::vertex_point_t` should be available in `FaceGraph`\cgalParamEnd
\cgalNamedParamsEnd
\pre The data must represent a 2-manifold \pre The data must represent a 2-manifold
\attention The graph `g` is not cleared, and the data from the stream are added. \sa Overloads of this function for specific models of the concept `FaceGraph`.
\see \ref IOStreamOFF \see \ref IOStreamOFF
*/ */
template <typename FaceGraph, typename CGAL_BGL_NP_TEMPLATE_PARAMETERS> template <typename FaceGraph, typename CGAL_BGL_NP_TEMPLATE_PARAMETERS>
bool read_OFF(const char* fname, bool read_OFF(std::istream& in, FaceGraph& g, const CGAL_BGL_NP_CLASS& np)
FaceGraph& g, {
const CGAL_BGL_NP_CLASS& np) return IO::internal::read_OFF_BGL(in, g, np);
}
// document that too
template <typename FaceGraph, typename CGAL_BGL_NP_TEMPLATE_PARAMETERS>
bool read_OFF(const char* fname, FaceGraph& g, const CGAL_BGL_NP_CLASS& np)
{ {
std::ifstream in(fname); std::ifstream in(fname);
return read_OFF(in, g, np); return read_OFF(in, g, np);
@ -132,6 +126,21 @@ bool read_OFF(const std::string& fname, FaceGraph& g) { return read_OFF(fname, g
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
/// Write /// Write
namespace IO {
namespace internal {
template <typename FaceGraph, typename CGAL_BGL_NP_TEMPLATE_PARAMETERS>
bool write_OFF_BGL(std::ostream& os,
const FaceGraph& g,
const CGAL_BGL_NP_CLASS& np)
{
IO::internal::Generic_facegraph_printer<std::ostream, FaceGraph, CGAL::File_writer_OFF> printer(os);
return printer(g, np);
}
} // namespace internal
} // namespace IO
/*! /*!
\ingroup PkgBGLIOFct \ingroup PkgBGLIOFct
@ -149,27 +158,14 @@ bool read_OFF(const std::string& fname, FaceGraph& g) { return read_OFF(fname, g
\see \ref IOStreamOFF \see \ref IOStreamOFF
*/ */
template <typename FaceGraph, typename CGAL_BGL_NP_TEMPLATE_PARAMETERS> template <typename FaceGraph, typename CGAL_BGL_NP_TEMPLATE_PARAMETERS>
bool write_OFF(std::ostream& os, bool write_OFF(std::ostream& os, const FaceGraph& g, const CGAL_BGL_NP_CLASS& np)
const FaceGraph& g,
const CGAL_BGL_NP_CLASS& np)
{ {
IO::internal::Generic_facegraph_printer<std::ostream, FaceGraph, CGAL::File_writer_OFF> printer(os); return IO::internal::write_OFF_BGL(os, g, np);
return printer(g, np);
} }
/*! // document that too
\ingroup PkgBGLIOFct
writes the graph `g` in the OFF format into a file named `fname`.
\sa Overloads of this function for specific models of the concept `FaceGraph`.
\see \ref IOStreamOFF
*/
template <typename FaceGraph, typename CGAL_BGL_NP_TEMPLATE_PARAMETERS> template <typename FaceGraph, typename CGAL_BGL_NP_TEMPLATE_PARAMETERS>
bool write_OFF(const char* fname, bool write_OFF(const char* fname, const FaceGraph& g, const CGAL_BGL_NP_CLASS& np)
const FaceGraph& g,
const CGAL_BGL_NP_CLASS& np)
{ {
std::ofstream out(fname); std::ofstream out(fname);
return write_OFF(out, g, np); return write_OFF(out, g, np);
@ -181,7 +177,7 @@ bool write_OFF(const std::string& fname, const FaceGraph& g, const CGAL_BGL_NP_C
return write_OFF(fname.c_str(), g, np); return write_OFF(fname.c_str(), g, np);
} }
template <typename Input, typename FaceGraph> template <typename FaceGraph>
bool write_OFF(std::ostream& os, const FaceGraph& g) { return write_OFF(os, g, parameters::all_default()); } bool write_OFF(std::ostream& os, const FaceGraph& g) { return write_OFF(os, g, parameters::all_default()); }
template <typename FaceGraph> template <typename FaceGraph>
bool write_OFF(const char* fname, const FaceGraph& g) { return write_OFF(fname, g, parameters::all_default()); } bool write_OFF(const char* fname, const FaceGraph& g) { return write_OFF(fname, g, parameters::all_default()); }

View File

@ -99,6 +99,8 @@ create_single_source_cgal_program( "test_Properties.cpp" )
create_single_source_cgal_program( "test_wrl.cpp" ) create_single_source_cgal_program( "test_wrl.cpp" )
create_single_source_cgal_program( "test_io.cpp" )
if(OpenMesh_FOUND) if(OpenMesh_FOUND)
target_link_libraries( test_clear PRIVATE ${OPENMESH_LIBRARIES}) target_link_libraries( test_clear PRIVATE ${OPENMESH_LIBRARIES})
target_link_libraries( test_Euler_operations PRIVATE ${OPENMESH_LIBRARIES}) target_link_libraries( test_Euler_operations PRIVATE ${OPENMESH_LIBRARIES})

View File

@ -9,7 +9,6 @@
#include <CGAL/IO/OFF.h> #include <CGAL/IO/OFF.h>
#include <CGAL/IO/OBJ.h> #include <CGAL/IO/OBJ.h>
#include <CGAL/Polygon_mesh_processing/repair.h> #include <CGAL/Polygon_mesh_processing/repair.h>
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
#include <QMessageBox> #include <QMessageBox>
#include <QApplication> #include <QApplication>

View File

@ -4,7 +4,6 @@
#include "Scene_points_with_normal_item.h" #include "Scene_points_with_normal_item.h"
#include <CGAL/IO/PLY.h> #include <CGAL/IO/PLY.h>
#include <CGAL/Polygon_mesh_processing/polygon_soup_to_polygon_mesh.h>
#include <CGAL/Three/Polyhedron_demo_io_plugin_interface.h> #include <CGAL/Three/Polyhedron_demo_io_plugin_interface.h>
#include <CGAL/Three/Three.h> #include <CGAL/Three/Three.h>

View File

@ -1510,9 +1510,14 @@ Scene_surface_mesh_item::load_obj(std::istream& in)
bool bool
Scene_surface_mesh_item::save_obj(std::ostream& out) const Scene_surface_mesh_item::save_obj(std::ostream& out) const
{ {
CGAL::File_writer_wavefront writer; typename SMesh::template Property_map<typename SMesh::Vertex_index, EPICK::Vector_3> vnormals;
CGAL::generic_print_surface_mesh(out, *(d->smesh_), writer); bool has_normals = false;
return out.good(); boost::tie(vnormals, has_normals) = M.template property_map<typename SMesh::Vertex_index, EPICK::Vector_3>("v:normal");
if(has_normals)
return CGAL::write_OBJ(out, *(d->smesh_), parameters::normal_map(vnormals));
else
return CGAL::write_OBJ(out, *(d->smesh_));
} }
void void

View File

@ -461,12 +461,12 @@ struct Constant_property_map
Constant_property_map(const value_type& default_value = value_type()) : default_value (default_value) { } Constant_property_map(const value_type& default_value = value_type()) : default_value (default_value) { }
/// Free function that returns `pm.default_value`. /// Free function that returns `pm.default_value`.
inline friend value_type inline friend
get (const Constant_property_map& pm, const key_type&){ return pm.default_value; } const value_type& get (const Constant_property_map& pm, const key_type&) { return pm.default_value; }
/// Free function that does nothing. /// Free function that does nothing.
inline friend void inline friend
put (const Constant_property_map&, const key_type&, const value_type&) { } void put (const Constant_property_map&, const key_type&, const value_type&) { }
}; };
/// \ingroup PkgPropertyMapRef /// \ingroup PkgPropertyMapRef

View File

@ -38,6 +38,9 @@ public:
bool operator()(std::vector<Point_3>& points, bool operator()(std::vector<Point_3>& points,
std::vector<Polygon_3>& polygons) std::vector<Polygon_3>& polygons)
{ {
if(m_out.fail())
return false;
m_writer.write_header(m_out, points.size(), 0, polygons.size()); m_writer.write_header(m_out, points.size(), 0, polygons.size());
for(std::size_t i=0, end=points.size(); i<end; ++i) for(std::size_t i=0, end=points.size(); i<end; ++i)
{ {

View File

@ -33,13 +33,13 @@ namespace CGAL {
namespace IO { namespace IO {
namespace internal { namespace internal {
template <typename Points, typename Faces, typename VertexNormalOutputIterator> template <typename PointRange, typename PolygonRange, typename VertexNormalOutputIterator>
bool read_OBJ(std::istream& input, bool read_OBJ(std::istream& is,
Points& points, PointRange& points,
Faces& faces, PolygonRange& faces,
VertexNormalOutputIterator vn_out) VertexNormalOutputIterator vn_out)
{ {
typedef typename boost::range_value<Points>::type Point; typedef typename boost::range_value<PointRange>::type Point;
typedef typename CGAL::Kernel_traits<Point>::Kernel Kernel; typedef typename CGAL::Kernel_traits<Point>::Kernel Kernel;
typedef typename Kernel::Vector_3 Normal; typedef typename Kernel::Vector_3 Normal;
@ -48,7 +48,7 @@ bool read_OBJ(std::istream& input,
Point p; Point p;
std::string line; std::string line;
while(getline(input, line)) while(getline(is, line))
{ {
if(line[0] == 'v' && line[1] == ' ') if(line[0] == 'v' && line[1] == ' ')
{ {
@ -106,7 +106,7 @@ bool read_OBJ(std::istream& input,
return false; return false;
} }
return !input.fail(); return !is.fail();
} }
} // namespace internal } // namespace internal
@ -114,16 +114,16 @@ bool read_OBJ(std::istream& input,
//! \ingroup IOstreamFunctions //! \ingroup IOstreamFunctions
//! //!
/// reads the content of `input` into `points` and `faces`, using the `OBJ` format. /// reads the content of `is` into `points` and `faces`, using the `OBJ` format.
/// ///
/// \tparam Points a `RandomAccessContainer` of `Point_3, /// \tparam Points a `RandomAccessContainer` of `Point_3,
/// \tparam Faces a `RandomAccessContainer` of `RandomAccessContainer` of `std::size_t` /// \tparam Faces a `RandomAccessContainer` of `RandomAccessContainer` of `std::size_t`
/// ///
/// \see \ref IOStreamOBJ /// \see \ref IOStreamOBJ
template <typename Points, typename Faces, typename CGAL_BGL_NP_TEMPLATE_PARAMETERS> template <typename PointRange, typename PolygonRange, typename CGAL_BGL_NP_TEMPLATE_PARAMETERS>
bool read_OBJ(std::istream& is, bool read_OBJ(std::istream& is,
Points& points, PointRange& points,
Faces& faces, PolygonRange& faces,
const CGAL_BGL_NP_CLASS& np) const CGAL_BGL_NP_CLASS& np)
{ {
using parameters::choose_parameter; using parameters::choose_parameter;
@ -134,11 +134,44 @@ bool read_OBJ(std::istream& is,
CGAL::Emptyset_iterator())); CGAL::Emptyset_iterator()));
} }
template <typename PointRange, typename PolygonRange, typename CGAL_BGL_NP_TEMPLATE_PARAMETERS>
bool read_OBJ(const char* fname,
PointRange& points,
PolygonRange& polygons,
const CGAL_BGL_NP_CLASS& np)
{
std::ifstream in(fname);
return read_OBJ(in, points, polygons, np);
}
template <typename PointRange, typename PolygonRange, typename CGAL_BGL_NP_TEMPLATE_PARAMETERS>
bool read_OBJ(const std::string& fname, PointRange& points, PolygonRange& polygons, const CGAL_BGL_NP_CLASS& np)
{
return read_OBJ(fname.c_str(), points, polygons, np);
}
template <typename PointRange, typename PolygonRange>
bool read_OBJ(std::istream& is, PointRange& points, PolygonRange& polygons)
{
return read_OBJ(is, points, polygons, parameters::all_default());
}
template <typename PointRange, typename PolygonRange>
bool read_OBJ(const char* fname, PointRange& points, PolygonRange& polygons)
{
return read_OBJ(fname, points, polygons, parameters::all_default());
}
template <typename PointRange, typename PolygonRange>
bool read_OBJ(const std::string& fname, PointRange& points, PolygonRange& polygons)
{
return read_OBJ(fname, points, polygons, parameters::all_default());
}
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
/// Write /// Write
/*! /*!
* \ingroup IOstreamFunctions * \ingroup IOstreamFunctions
* *
@ -146,15 +179,28 @@ bool read_OBJ(std::istream& is,
* *
* \see \ref IOStreamOBJ * \see \ref IOStreamOBJ
*/ */
template <class Point_3, class Polygon_3> template <typename PointRange, typename PolygonRange>
bool write_OBJ(std::ostream& os, bool write_OBJ(std::ostream& os,
std::vector<Point_3>& points, PointRange& points,
std::vector<Polygon_3>& polygons) PolygonRange& polygons)
{ {
Generic_writer<std::ostream, File_writer_wavefront> writer(os); // @fixme uniformize os and out Generic_writer<std::ostream, File_writer_wavefront> writer(os);
return writer(points, polygons); return writer(points, polygons);
} }
template <typename PointRange, typename PolygonRange>
bool write_OBJ(const char* fname, PointRange& points, PolygonRange& polygons)
{
std::ofstream out(fname);
return write_OBJ(out, points, polygons);
}
template <typename PointRange, typename PolygonRange>
bool write_OBJ(const std::string& fname, PointRange& points, PolygonRange& polygons)
{
return write_OBJ(fname.c_str(), points, polygons);
}
} // namespace CGAL } // namespace CGAL
#endif // CGAL_IO_OBJ_H #endif // CGAL_IO_OBJ_H

View File

@ -69,7 +69,7 @@ public:
} }
void write_facet_begin(std::size_t) { out() << "f "; } void write_facet_begin(std::size_t) { out() << "f "; }
void write_facet_vertex_index(std::size_t idx) { out() << ' ' << idx+1; } void write_facet_vertex_index(std::size_t idx) { out() << ' ' << idx+1; }
void write_face_normal(const double, const double, const double) { } void write_face_color(const double, const double, const double) { }
void write_facet_end() { out() << '\n'; } void write_facet_end() { out() << '\n'; }
}; };

View File

@ -23,6 +23,8 @@
#include <CGAL/Kernel_traits.h> #include <CGAL/Kernel_traits.h>
#include <CGAL/use.h> #include <CGAL/use.h>
#include <boost/range/value_type.hpp>
#include <fstream> #include <fstream>
#include <iostream> #include <iostream>
#include <vector> #include <vector>
@ -36,26 +38,29 @@ namespace CGAL {
namespace IO { namespace IO {
namespace internal { namespace internal {
template <typename Point_3, typename Polygon_3, template <typename PointRange, typename PolygonRange,
typename VertexNormalOutputIterator, typename VertexNormalOutputIterator,
typename VertexColorOutputIterator, typename VertexColorOutputIterator,
typename VertexTextureOutputIterator, typename VertexTextureOutputIterator,
typename FaceColorOutputIterator> typename FaceColorOutputIterator>
bool read_OFF(std::istream& in, bool read_OFF(std::istream& is,
std::vector<Point_3>& points, PointRange& points,
std::vector<Polygon_3>& polygons, PolygonRange& polygons,
VertexNormalOutputIterator vn_out, VertexNormalOutputIterator vn_out,
VertexColorOutputIterator vc_out, VertexColorOutputIterator vc_out,
VertexTextureOutputIterator vt_out, VertexTextureOutputIterator vt_out,
FaceColorOutputIterator fc_out) FaceColorOutputIterator fc_out)
{ {
typedef typename CGAL::Kernel_traits<Point_3>::Kernel Kernel; typedef typename boost::range_value<PointRange>::type Point;
typedef typename CGAL::Kernel_traits<Point>::Kernel Kernel;
typedef typename Kernel::Point_2 Texture; typedef typename Kernel::Point_2 Texture;
typedef typename Kernel::Vector_3 Normal; typedef typename Kernel::Vector_3 Normal;
typedef CGAL::Color Color; typedef CGAL::Color Color;
CGAL::File_scanner_OFF scanner(in); if(!is.good())
return false;
CGAL::File_scanner_OFF scanner(is);
points.resize(scanner.size_of_vertices()); points.resize(scanner.size_of_vertices());
polygons.resize(scanner.size_of_facets()); polygons.resize(scanner.size_of_facets());
@ -82,7 +87,6 @@ bool read_OFF(std::istream& in,
*vc_out++ = Color(r,g,b); *vc_out++ = Color(r,g,b);
} }
// @fixme
if(scanner.has_textures()) if(scanner.has_textures())
{ {
double nx, ny, nw; double nx, ny, nw;
@ -93,7 +97,7 @@ bool read_OFF(std::istream& in,
scanner.skip_to_next_vertex(i); scanner.skip_to_next_vertex(i);
if(!in.good()) if(!is.good())
return false; return false;
} }
@ -103,7 +107,7 @@ bool read_OFF(std::istream& in,
std::size_t no; std::size_t no;
scanner.scan_facet(no, i); scanner.scan_facet(no, i);
if(!in.good()) if(!is.good())
return false; return false;
IO::internal::resize(polygons[i], no); IO::internal::resize(polygons[i], no);
@ -120,7 +124,7 @@ bool read_OFF(std::istream& in,
if(i == 0) if(i == 0)
{ {
std::string col; std::string col;
std::getline(in, col); std::getline(is, col);
std::istringstream iss(col); std::istringstream iss(col);
char ci =' '; char ci =' ';
@ -139,7 +143,7 @@ bool read_OFF(std::istream& in,
} }
} }
return !in.fail(); return !is.fail();
} }
} // namespace internal } // namespace internal
@ -148,22 +152,22 @@ bool read_OFF(std::istream& in,
/*! /*!
* \ingroup IOstreamFunctions * \ingroup IOstreamFunctions
* *
* reads the content of `in` into `points` and `polygons`, in the OFF format. * reads the content of `is` into `points` and `polygons`, in the OFF format.
* *
* @fixme re-add support for STOFF * @fixme re-add support for STOFF
* *
* \see \ref IOStreamOFF * \see \ref IOStreamOFF
*/ */
template <typename Point_3, typename Polygon_3, typename NamedParameters> template <typename PointRange, typename PolygonRange, typename NamedParameters>
bool read_OFF(std::istream& in, bool read_OFF(std::istream& is,
std::vector<Point_3>& points, // @fixme doesn't need a vector specifically PointRange& points,
std::vector<Polygon_3>& polygons, PolygonRange& polygons,
const NamedParameters& np) const NamedParameters& np)
{ {
using parameters::choose_parameter; using parameters::choose_parameter;
using parameters::get_parameter; using parameters::get_parameter;
return IO::internal::read_OFF(in, points, polygons, return IO::internal::read_OFF(is, points, polygons,
choose_parameter(get_parameter(np, internal_np::vertex_normal_output_iterator), choose_parameter(get_parameter(np, internal_np::vertex_normal_output_iterator),
CGAL::Emptyset_iterator()), CGAL::Emptyset_iterator()),
choose_parameter(get_parameter(np, internal_np::vertex_color_output_iterator), choose_parameter(get_parameter(np, internal_np::vertex_color_output_iterator),
@ -174,45 +178,36 @@ bool read_OFF(std::istream& in,
CGAL::Emptyset_iterator())); CGAL::Emptyset_iterator()));
} }
template <typename Point_3, typename Polygon_3, typename CGAL_BGL_NP_TEMPLATE_PARAMETERS> template <typename PointRange, typename PolygonRange, typename CGAL_BGL_NP_TEMPLATE_PARAMETERS>
bool read_OFF(const char* fname, bool read_OFF(const char* fname,
std::vector<Point_3>& points, PointRange& points,
std::vector<Polygon_3>& polygons, PolygonRange& polygons,
const CGAL_BGL_NP_CLASS& np) const CGAL_BGL_NP_CLASS& np)
{ {
std::ifstream in(fname); std::ifstream in(fname);
return read_OFF(in, points, polygons, np); return read_OFF(in, points, polygons, np);
} }
template <typename Point_3, typename Polygon_3, typename CGAL_BGL_NP_TEMPLATE_PARAMETERS> template <typename PointRange, typename PolygonRange, typename CGAL_BGL_NP_TEMPLATE_PARAMETERS>
bool read_OFF(const std::string& fname, bool read_OFF(const std::string& fname, PointRange& points, PolygonRange& polygons, const CGAL_BGL_NP_CLASS& np)
std::vector<Point_3>& points,
std::vector<Polygon_3>& polygons,
const CGAL_BGL_NP_CLASS& np)
{ {
return read_OFF(fname.c_str(), points, polygons, np); return read_OFF(fname.c_str(), points, polygons, np);
} }
template <typename Point_3, typename Polygon_3> template <typename PointRange, typename PolygonRange>
bool read_OFF(std::istream& is, bool read_OFF(std::istream& is, PointRange& points, PolygonRange& polygons)
std::vector<Point_3>& points,
std::vector<Polygon_3>& polygons)
{ {
return read_OFF(is, points, polygons, parameters::all_default()); return read_OFF(is, points, polygons, parameters::all_default());
} }
template <typename Point_3, typename Polygon_3> template <typename PointRange, typename PolygonRange>
bool read_OFF(const char* fname, bool read_OFF(const char* fname, PointRange& points, PolygonRange& polygons)
std::vector<Point_3>& points,
std::vector<Polygon_3>& polygons)
{ {
return read_OFF(fname, points, polygons, parameters::all_default()); return read_OFF(fname, points, polygons, parameters::all_default());
} }
template <typename Point_3, typename Polygon_3> template <typename PointRange, typename PolygonRange>
bool read_OFF(const std::string& fname, bool read_OFF(const std::string& fname, PointRange& points, PolygonRange& polygons)
std::vector<Point_3>& points,
std::vector<Polygon_3>& polygons)
{ {
return read_OFF(fname, points, polygons, parameters::all_default()); return read_OFF(fname, points, polygons, parameters::all_default());
} }
@ -228,20 +223,20 @@ bool read_OFF(const std::string& fname,
* *
* \see \ref IOStreamOFF * \see \ref IOStreamOFF
*/ */
template <typename Point_3, typename Polygon_3, typename NamedParameters> template <typename PointRange, typename PolygonRange, typename CGAL_BGL_NP_TEMPLATE_PARAMETERS>
bool write_OFF(std::ostream& os, bool write_OFF(std::ostream& os,
std::vector<Point_3>& points, PointRange& points,
std::vector<Polygon_3>& polygons, PolygonRange& polygons,
const NamedParameters& np) const CGAL_BGL_NP_CLASS& np)
{ {
Generic_writer<std::ostream, File_writer_OFF> writer(os); Generic_writer<std::ostream, File_writer_OFF> writer(os);
return writer(points, polygons, np); return writer(points, polygons, np);
} }
template <class Point_3, class Polygon_3> template <typename PointRange, typename PolygonRange>
bool write_OFF(std::ostream& os, bool write_OFF(std::ostream& os,
std::vector<Point_3>& points, PointRange& points,
std::vector<Polygon_3>& polygons) PolygonRange& polygons)
{ {
return write_OFF(os, points, polygons, parameters::all_default()); return write_OFF(os, points, polygons, parameters::all_default());
} }

View File

@ -91,14 +91,29 @@ public:
void write_vertex_color(const double r, const double g, const double b) void write_vertex_color(const double r, const double g, const double b)
{ {
// @todo if(m_header.binary())
CGAL_assertion(false); {
I_Binary_write_big_endian_float32(out(), float(r));
I_Binary_write_big_endian_float32(out(), float(g));
I_Binary_write_big_endian_float32(out(), float(b));
}
else
{
out() << ' ' << ' ' << r << ' ' << g << ' ' << b;
}
} }
void write_vertex_texture(const double, const double) void write_vertex_texture(const double tx, const double ty)
{ {
// @todo if(m_header.binary())
CGAL_assertion(false); {
I_Binary_write_big_endian_float32(out(), float(tx));
I_Binary_write_big_endian_float32(out(), float(ty));
}
else
{
out() << ' ' << ' ' << tx << ' ' << ty;
}
} }
void write_facet_header() void write_facet_header()
@ -135,7 +150,7 @@ public:
out() << ' ' << index; out() << ' ' << index;
} }
void write_face_normal(const double, const double, const double) { } void write_face_color(const double, const double, const double) { }
void write_facet_end() void write_facet_end()
{ {

View File

@ -397,7 +397,10 @@ returns the printing mode of the %IO stream `s`.
\sa `CGAL::is_binary()` \sa `CGAL::is_binary()`
\sa `CGAL::is_pretty()` \sa `CGAL::is_pretty()`
*/ */
CGAL_EXPORT inline IO::Mode get_mode(std::ios& i) { return static_cast<IO::Mode>(i.iword(IO::Static::get_mode())); } inline IO::Mode get_mode(std::ios& i)
{
return static_cast<IO::Mode>(i.iword(IO::Static::get_mode()));
}
/*! /*!
\ingroup PkgStreamSupportRef \ingroup PkgStreamSupportRef
@ -414,7 +417,7 @@ Returns the previous mode of `s`.
\sa `CGAL::is_binary()` \sa `CGAL::is_binary()`
\sa `CGAL::is_pretty()` \sa `CGAL::is_pretty()`
*/ */
CGAL_EXPORT inline IO::Mode set_ascii_mode(std::ios& i) inline IO::Mode set_ascii_mode(std::ios& i)
{ {
IO::Mode m = get_mode(i); IO::Mode m = get_mode(i);
i.iword(IO::Static::get_mode()) = IO::ASCII; i.iword(IO::Static::get_mode()) = IO::ASCII;
@ -436,7 +439,7 @@ Returns the previous mode of `s`.
\sa `CGAL::is_binary()` \sa `CGAL::is_binary()`
\sa `CGAL::is_pretty()` \sa `CGAL::is_pretty()`
*/ */
CGAL_EXPORT inline IO::Mode set_binary_mode(std::ios& i) inline IO::Mode set_binary_mode(std::ios& i)
{ {
IO::Mode m = get_mode(i); IO::Mode m = get_mode(i);
i.iword(IO::Static::get_mode()) = IO::BINARY; i.iword(IO::Static::get_mode()) = IO::BINARY;
@ -458,7 +461,7 @@ Returns the previous mode of `s`.
\sa `CGAL::is_binary()` \sa `CGAL::is_binary()`
\sa `CGAL::is_pretty()` \sa `CGAL::is_pretty()`
*/ */
CGAL_EXPORT inline IO::Mode set_pretty_mode(std::ios& i) //@todo export ? inline IO::Mode set_pretty_mode(std::ios& i)
{ {
IO::Mode m = get_mode(i); IO::Mode m = get_mode(i);
i.iword(IO::Static::get_mode()) = IO::PRETTY; i.iword(IO::Static::get_mode()) = IO::PRETTY;
@ -479,7 +482,7 @@ sets the printing mode of the %IO stream `s`.
\sa `CGAL::is_binary()` \sa `CGAL::is_binary()`
\sa `CGAL::is_pretty()` \sa `CGAL::is_pretty()`
*/ */
CGAL_EXPORT inline IO::Mode set_mode(std::ios& i, IO::Mode m) inline IO::Mode set_mode(std::ios& i, IO::Mode m)
{ {
IO::Mode old = get_mode(i); IO::Mode old = get_mode(i);
i.iword(IO::Static::get_mode()) = m; i.iword(IO::Static::get_mode()) = m;

View File

@ -21,7 +21,7 @@
namespace CGAL { namespace CGAL {
// fwdS for the public interface // fwdS for the public interface
template<typename K> template<typename P>
class Surface_mesh; class Surface_mesh;
} }

View File

@ -15,12 +15,15 @@
#include <CGAL/license/Surface_mesh.h> #include <CGAL/license/Surface_mesh.h>
#include <CGAL/assertions.h>
#include <CGAL/use.h>
#include <CGAL/Surface_mesh/Surface_mesh.h> #include <CGAL/Surface_mesh/Surface_mesh.h>
#include <CGAL/Surface_mesh/Properties.h>
#include <CGAL/Surface_mesh/IO/3mf.h>
#include <CGAL/Surface_mesh/IO/OFF.h>
#include <CGAL/Surface_mesh/IO/PLY.h>
#include <CGAL/assertions.h>
#include <CGAL/Kernel_traits.h> #include <CGAL/Kernel_traits.h>
#include <CGAL/boost/graph/graph_traits_Surface_mesh.h> #include <CGAL/use.h>
#include <boost/array.hpp> #include <boost/array.hpp>
@ -34,194 +37,7 @@
namespace CGAL { namespace CGAL {
namespace internal { // @todo move that to read_polygon_mesh in BGL
// helper function
template <typename T> void read(std::istream& in, T& t)
{
in.read(reinterpret_cast<char*>(&t), sizeof(t));
}
template <typename Point_3>
bool read_off_binary(Surface_mesh<Point_3>& mesh,
std::istream& in,
const bool has_normals,
const bool has_texcoords)
{
typedef Surface_mesh<Point_3> Mesh;
typedef typename Kernel_traits<Point_3>::Kernel K;
typedef typename K::Vector_3 Vector_3;
typedef typename K::Vector_2 Vector_2;
typedef typename K::Vector_3 Normal;
typedef typename K::Vector_3 Texture_coordinate;
unsigned int i, j, idx;
unsigned int nV, nF, nE;
Point_3 p;
Vector_3 n, c;
Vector_2 t;
typename Mesh::Vertex_index v;
// properties
typename Mesh::template Property_map<typename Mesh::Vertex_index, Normal> normals;
typename Mesh::template Property_map<typename Mesh::Vertex_index, Texture_coordinate> texcoords;
if(has_normals) normals = mesh.template add_property_map<typename Mesh::Vertex_index, Normal>("v:normal").first;
if(has_texcoords) texcoords = mesh.template add_property_map<typename Mesh::Vertex_index, Texture_coordinate>("v:texcoord").first;
// #Vertice, #Faces, #Edges
internal::read(in, nV);
internal::read(in, nF);
internal::read(in, nE);
mesh.clear();
mesh.reserve(nV, (std::max)(3*nV, nE), nF);
// read vertices: pos [normal] [color] [texcoord]
for(i=0; i<nV && in.good(); ++i)
{
// position
internal::read(in, p);
v = mesh.add_vertex(p);
// normal
if(has_normals)
{
internal::read(in, n);
normals[v] = n;
}
// tex coord
if(has_texcoords)
{
internal::read(in, t);
texcoords[v] = Vector_3(t[0], t[1], 0.0);
}
}
// read faces: #N v[1] v[2] ... v[n-1]
std::vector<typename Mesh::Vertex_index> vertices;
for(i=0; i<nF; ++i)
{
internal::read(in, nV);
vertices.resize(nV);
for(j=0; j<nV; ++j)
{
internal::read(in, idx);
vertices[j] = typename Mesh::Vertex_index(idx);
}
if(!mesh.add_face(vertices).is_valid()) {
// adding a face did not succeed, stop reading the rest
return false;
}
}
return true;
}
template <typename Point_3>
bool read_off_ascii(Surface_mesh<Point_3>& mesh,
std::istream& in,
const bool has_normals,
const bool has_texcoords)
{
typedef Surface_mesh<Point_3> Mesh;
typedef typename Kernel_traits<Point_3>::Kernel K;
typedef typename K::Vector_3 Vector_3;
typedef typename K::Vector_3 Normal;
typedef typename K::Vector_3 Texture_coordinate;
boost::array<double, 3> buffer;
std::string line;
unsigned int i, j, idx;
unsigned int nV, nF, nE;
typename Mesh::Vertex_index v;
// properties
typename Mesh::template Property_map<typename Mesh::Vertex_index, Normal> normals;
typename Mesh::template Property_map<typename Mesh::Vertex_index, Texture_coordinate> texcoords;
if(has_normals) normals = mesh.template add_property_map<typename Mesh::Vertex_index, Normal>("v:normal").first;
if(has_texcoords) texcoords = mesh.template add_property_map<typename Mesh::Vertex_index, Texture_coordinate>("v:texcoord").first;
char c;
do {
c = in.get();
if(c == '#'){
getline(in,line);
} else {
in.putback(c);
break;
}
}while(1);
// #Vertice, #Faces, #Edges
in >> nV >> nF >> nE;
getline(in,line); // reads eol
mesh.clear();
mesh.reserve(nV, (std::max)(3*nV, nE), nF);
// read vertices: pos [normal] [color] [texcoord]
for(i=0; i<nV && in.good(); ++i)
{
// read line
getline(in, line);
if(line[0] == '#') // if the first column is a # we are looking at a comment line
{
--i;
continue;
}
// position
std::istringstream iss(line);
iss >> iformat(buffer[0]) >> iformat(buffer[1]) >> iformat(buffer[2]);
v = mesh.add_vertex(Point_3(buffer[0], buffer[1], buffer[2]));
// normal
if(has_normals)
{
iss >> iformat(buffer[0]) >> iformat(buffer[1]) >> iformat(buffer[2]);
}
// tex coord
if(has_texcoords)
{
iss >> iformat(buffer[0]) >> iformat(buffer[1]);
texcoords[v] = Vector_3(buffer[0], buffer[1], 0.0);
}
}
// read faces: #N v[1] v[2] ... v[n-1]
std::vector<typename Mesh::Vertex_index> vertices;
for(i=0; i<nF; ++i)
{
// read line
getline(in, line);
if(line[0] == '#') // if the first column is a # we are looking at a comment line
{
--i;
continue;
}
// #vertices
std::istringstream iss(line);
iss >> nV;
vertices.resize(nV);
// indices
for(j=0; j<nV; ++j)
{
iss >> idx;
vertices[j] = typename Mesh::Vertex_index(idx);
}
if(!mesh.add_face(vertices).is_valid()) {
// adding a face did not succeed, stop reading the rest
return false;
}
}
return true;
}
}
/// \addtogroup PkgSurfaceMeshIO /// \addtogroup PkgSurfaceMeshIO
/// ///
@ -232,90 +48,6 @@ bool read_off_ascii(Surface_mesh<Point_3>& mesh,
/// ///
/// @{ /// @{
/// This function reads an `OFF` file into a `Surface_mesh`. It
/// supports the `OFF` vertex properties normal, color, and vertex
/// coordinates. If a property is detected in the `OFF` file, it will be
/// read into the `mesh` as a vertex property with the name
/// `"v:normal"`, `"v:color"`, and `"v:texcoord"`, respectivly.
///
/// @param mesh The mesh that should contain the file contents.
/// @param filename The name of the file to be read.
///
/// @returns `true`, if reading succeeded, `false` otherwise
///
template <typename K>
bool read_off(Surface_mesh<K>& mesh, const std::string& filename)
{
std::string line;
bool has_texcoords = false;
bool has_normals = false;
bool has_hcoords = false;
bool has_dim = false;
bool is_binary = false;
// open file (in ASCII mode)
std::ifstream in(filename.c_str());
if(!in) return false;
// read header: [ST][C][N][4][n]OFF BINARY
std::getline(in,line);
const char *c = line.c_str();
if(c[0] == 'S' && c[1] == 'T') { has_texcoords = true; c += 2; }
if(c[0] == 'N') { has_normals = true; ++c; }
if(c[0] == '4') { has_hcoords = true; ++c; }
if(c[0] == 'n') { has_dim = true; ++c; }
if(strncmp(c, "OFF", 3) != 0) { in.close(); return false; } // no OFF
if(strncmp(c+4, "BINARY", 6) == 0) is_binary = true;
// homogeneous coords, and vertex dimension != 3 are not supported
if(has_hcoords || has_dim)
{
in.close();
return false;
}
// if binary: reopen file in binary mode
if(is_binary)
{
in.close();
in.open(filename.c_str(), std::ios::binary);
std::getline(in,line);
}
// read as ASCII or binary
bool ok = (is_binary ?
internal::read_off_binary(mesh, in, has_normals, has_texcoords) :
internal::read_off_ascii(mesh, in, has_normals, has_texcoords));
in.close();
return ok;
}
#if 0
/// This function writes a `Surface_mesh` into an ASCII `OFF`
/// file. It does not support properties.
///
/// @param mesh The mesh that should be written.
/// @param filename The name of the file to be written.
///
/// @returns `true`, if reading succeeded, `false` otherwise
///
#endif
template <typename K>
bool write_off(const Surface_mesh<K>& mesh, const std::string& filename)
{
std::ofstream out(filename.c_str());
if(out.fail())
return false;
out << std::setprecision(17);
write_off(out, mesh);
return !out.fail();
}
#if 0 #if 0
/// Read a file into a `Surface_mesh`. The extension of the /// Read a file into a `Surface_mesh`. The extension of the
/// filename determines which reader is used. /// filename determines which reader is used.
@ -330,7 +62,8 @@ bool write_off(const Surface_mesh<K>& mesh, const std::string& filename)
/// ///
#endif #endif
template <typename K> template <typename K>
bool read_mesh(Surface_mesh<K>& mesh, const std::string& filename) { bool read_mesh(Surface_mesh<K>& mesh, const std::string& filename)
{
// clear mesh before reading from file // clear mesh before reading from file
mesh.clear(); mesh.clear();
@ -382,6 +115,8 @@ bool write_mesh(const Surface_mesh<K>& mesh, const std::string& filename)
return false; return false;
} }
/// @}
} // namespace CGAL } // namespace CGAL
#endif // CGAL_SURFACE_MESH_IO_H #endif // CGAL_SURFACE_MESH_IO_H

View File

@ -13,13 +13,18 @@
#ifndef CGAL_SURFACE_MESH_IO_3MF_H #ifndef CGAL_SURFACE_MESH_IO_3MF_H
#define CGAL_SURFACE_MESH_IO_3MF_H #define CGAL_SURFACE_MESH_IO_3MF_H
#ifdef CGAL_LINKED_WITH_3MF
#include <CGAL/Surface_mesh.h> #include <CGAL/Surface_mesh.h>
#include <CGAL/IO/3MF.h> #include <CGAL/IO/3MF.h>
#include <iostream> #include <iostream>
#include <string>
#include <vector>
namespace CGAL {
namespace CGAL{
/*! /*!
* Extracts the surface meshes from an input 3mf file and appends it to `output`. * Extracts the surface meshes from an input 3mf file and appends it to `output`.
*\tparam Point the Point type of the output meshes. *\tparam Point the Point type of the output meshes.
@ -27,55 +32,52 @@ namespace CGAL{
* \param output a `std::vector` containing the `CGAL::Surface_mesh`s that will be filled by this function. * \param output a `std::vector` containing the `CGAL::Surface_mesh`s that will be filled by this function.
* \return the number of extracted meshes. * \return the number of extracted meshes.
*/ */
template<typename Point> template<typename Point>
int read_3mf(const std::string& file_name, int read_3mf(const std::string& file_name,
std::vector<CGAL::Surface_mesh<Point> >& output) std::vector<CGAL::Surface_mesh<Point> >& output)
{ {
typedef std::vector<Point> PointRange; typedef std::vector<Point> PointRange;
typedef std::vector<std::size_t> Polygon; typedef std::vector<std::size_t> Polygon;
typedef std::vector<Polygon> PolygonRange; typedef std::vector<Polygon> PolygonRange;
typedef CGAL::Surface_mesh<Point> SMesh; typedef CGAL::Surface_mesh<Point> SMesh;
typedef typename SMesh::Vertex_index Vertex_index; typedef typename SMesh::Vertex_index Vertex_index;
typedef typename SMesh::Face_index Face_index; typedef typename SMesh::Face_index Face_index;
std::vector<PointRange> all_points; std::vector<PointRange> all_points;
std::vector<PolygonRange> all_polygons; std::vector<PolygonRange> all_polygons;
std::vector<std::string> names; std::vector<std::string> names;
std::vector<std::vector<CGAL::Color> > all_colors; std::vector<std::vector<CGAL::Color> > all_colors;
int result = 0; int result = 0;
int nb_meshes =
CGAL::read_triangle_soups_from_3mf(file_name, int nb_meshes = CGAL::read_triangle_soups_from_3mf(file_name, all_points, all_polygons, all_colors, names);
all_points, all_polygons, all_colors, names); if(nb_meshes < 0)
if(nb_meshes < 0 )
{ {
std::cerr << "Error in reading meshes."<<std::endl; std::cerr << "Error in reading meshes." << std::endl;
return -1; return -1;
} }
output.reserve(nb_meshes); output.reserve(nb_meshes);
for(int i = 0; i< nb_meshes; ++i) for(int i=0; i<nb_meshes; ++i)
{ {
bool skip = false; bool skip = false;
SMesh sm; SMesh sm;
PolygonRange triangles = all_polygons[i]; PolygonRange triangles = all_polygons[i];
PointRange points = all_points[i]; PointRange points = all_points[i];
std::vector<CGAL::Color> colors = all_colors[i]; std::vector<CGAL::Color> colors = all_colors[i];
//Create the surface mesh from scratch
// Create the surface mesh from scratch
std::size_t n(points.size()); std::size_t n(points.size());
sm.reserve(n,0, triangles.size()); sm.reserve(n, 0, triangles.size());
for(const Point& p : points) for(const Point& p : points)
{
sm.add_vertex(p); sm.add_vertex(p);
}
for(Polygon& triangle : triangles) for(Polygon& triangle : triangles)
{ {
std::vector<Vertex_index> face; std::vector<Vertex_index> face;
face.reserve(triangle.size()); face.reserve(triangle.size());
for(auto index : triangle) for(std::size_t index : triangle)
{
face.push_back(Vertex_index(index)); face.push_back(Vertex_index(index));
}
Face_index fi = sm.add_face(face); Face_index fi = sm.add_face(face);
if(fi == sm.null_face()) if(fi == sm.null_face())
{ {
@ -84,34 +86,39 @@ int read_3mf(const std::string& file_name,
break; break;
} }
} }
if(skip) if(skip)
continue; continue;
//end constructin the surface mesh from scratch
CGAL::Color first = colors.front(); const Color& first = colors.front();
bool need_pmap = false; bool need_pmap = false;
for(auto color : colors) for(const Color& color : colors)
{ {
if (color != first) if(color != first)
{ {
need_pmap = true; need_pmap = true;
break; break;
} }
} }
if(need_pmap) if(need_pmap)
{ {
typename SMesh::template Property_map<Face_index, CGAL::Color> fcolor = typename SMesh::template Property_map<Face_index, CGAL::Color> fcolor =
sm.template add_property_map<Face_index,CGAL::Color>("f:color",first).first; sm.template add_property_map<Face_index,CGAL::Color>("f:color", first).first;
for(std::size_t pid = 0; pid < colors.size(); ++pid)
{ for(std::size_t pid=0, cs=colors.size(); pid<cs; ++pid)
put(fcolor, Face_index(pid), colors[pid]);//should work bc mesh is just created and shouldn't have any destroyed face. put(fcolor, Face_index(pid), colors[pid]); // there can't have any deleted face yet
}
} }
output.push_back(sm); output.push_back(sm);
++result; ++result;
} }
return result; return result;
} }
}//end CGAL } // namespace CGAL
#endif // CGAL_SURFACE_MESH_3MF_H
#endif // CGAL_LINKED_WITH_3MF
#endif // CGAL_SURFACE_MESH_IO_3MF_H

View File

@ -0,0 +1,202 @@
// Copyright (c) 2018 GeometryFactory Sarl (France).
// All rights reserved.
//
// This file is part of CGAL (www.cgal.org).
//
// $URL$
// $Id$
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
//
//
// Author(s) : Mael Rouxel-Labbé
#ifndef CGAL_SURFACE_MESH_IO_OFF_H
#define CGAL_SURFACE_MESH_IO_OFF_H
#include <CGAL/license/Surface_mesh.h>
#include <CGAL/Surface_mesh/Surface_mesh_fwd.h>
#include <CGAL/boost/graph/IO/OFF.h>
#include <CGAL/boost/graph/Named_function_parameters.h>
#include <CGAL/Kernel_traits.h>
#include <CGAL/IO/Color.h>
#include <iostream>
#include <tuple>
namespace CGAL {
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Read
/// \relates Surface_mesh
///
/// Extracts the surface mesh from an input stream in Ascii OFF, COFF, NOFF, CNOFF
/// format and appends it to the surface mesh `sm`.
///
/// The operator reads the point property as well as "v:normal", "v:color", `"v:texcoord"`, and "f:color".
/// If an alternative vertex_point map is given through `np`,
/// then it will be used instead of the default one.
///
/// \pre The data in the stream must represent a two-manifold. If this is not the case
/// the `failbit` of `is` is set and the mesh cleared.
///
template <typename Point, typename CGAL_BGL_NP_TEMPLATE_PARAMETERS>
bool read_OFF(std::istream& is,
Surface_mesh<Point>& sm,
const CGAL_BGL_NP_CLASS& np)
{
typedef Surface_mesh<Point> Mesh;
typedef typename Mesh::Vertex_index Vertex_index;
typedef typename Mesh::Face_index Face_index;
typedef typename Kernel_traits<Point>::Kernel K;
typedef typename K::Vector_3 Normal;
typedef typename K::Point_2 Texture;
typedef CGAL::Color Color;
using parameters::choose_parameter;
using parameters::get_parameter;
typename CGAL::GetVertexPointMap<Mesh, CGAL_BGL_NP_CLASS>::type
vpm = choose_parameter(get_parameter(np, internal_np::vertex_point),
get_property_map(CGAL::vertex_point, sm));
typename Mesh::template Property_map<Vertex_index, Normal> vnormals;
typename Mesh::template Property_map<Vertex_index, Texture> vtextures;
typename Mesh::template Property_map<Vertex_index, Color> vcolors;
typename Mesh::template Property_map<Face_index, Color> fcolors;
bool created;
std::tie(vnormals, created) = sm.template add_property_map<Vertex_index, Normal>("v:normal");
CGAL_assertion(created);
std::tie(vcolors, created) = sm.template add_property_map<Vertex_index, Color>("v:color", Color(0,0,0));
CGAL_assertion(created);
std::tie(vtextures, created) = sm.template add_property_map<Vertex_index, Texture>("v:texcoord");
CGAL_assertion(created);
std::tie(fcolors, created) = sm.template add_property_map<Face_index, Color>("f:color", Color(0,0,0));
CGAL_assertion(created);
return IO::internal::read_OFF_BGL(is, sm, CGAL::parameters::vertex_point_map(vpm)
.vertex_normal_map(vnormals)
.vertex_color_map(vcolors)
.vertex_texture_map(vtextures)
.face_color_map(fcolors));
}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Write
namespace IO {
namespace internal {
template <typename Point, typename CGAL_BGL_NP_TEMPLATE_PARAMETERS>
bool write_OFF_with_or_without_fcolors(std::ostream& os,
const Surface_mesh<Point>& sm,
const CGAL_BGL_NP_CLASS& np)
{
typedef Surface_mesh<Point> Mesh;
typedef typename Mesh::Face_index Face_index;
typedef CGAL::Color Color;
typename Mesh::template Property_map<Face_index, Color> fcolors;
bool has_fcolors;
std::tie(fcolors, has_fcolors) = sm.template property_map<Face_index, CGAL::Color>("f:color");
if(has_fcolors)
return write_OFF_BGL(os, sm, np.face_color_map(fcolors));
else
return write_OFF_BGL(os, sm, np);
}
template <typename Point, typename CGAL_BGL_NP_TEMPLATE_PARAMETERS>
bool write_OFF_with_or_without_vtextures(std::ostream& os,
const Surface_mesh<Point>& sm,
const CGAL_BGL_NP_CLASS& np)
{
typedef Surface_mesh<Point> Mesh;
typedef typename Mesh::Vertex_index Vertex_index;
typedef typename Kernel_traits<Point>::Kernel K;
typedef typename K::Point_2 Texture;
typename Mesh::template Property_map<Vertex_index, Texture> vtextures;
bool has_vtextures;
std::tie(vtextures, has_vtextures) = sm.template property_map<Vertex_index, Texture>("v:texcoord");
if(has_vtextures)
return write_OFF_with_or_without_fcolors(os, sm, np.vertex_texture_map(vtextures));
else
return write_OFF_with_or_without_fcolors(os, sm, np);
}
template <typename Point, typename CGAL_BGL_NP_TEMPLATE_PARAMETERS>
bool write_OFF_with_or_without_vcolors(std::ostream& os,
const Surface_mesh<Point>& sm,
const CGAL_BGL_NP_CLASS& np)
{
typedef Surface_mesh<Point> Mesh;
typedef typename Mesh::Vertex_index Vertex_index;
typedef CGAL::Color Color;
typename Mesh::template Property_map<Vertex_index, Color> vcolors;
bool has_vcolors;
std::tie(vcolors, has_vcolors) = sm.template property_map<Vertex_index, CGAL::Color>("v:color");
if(has_vcolors)
return write_OFF_with_or_without_vtextures(os, sm, np.vertex_color_map(vcolors));
else
return write_OFF_with_or_without_vtextures(os, sm, np);
}
template <typename Point, typename CGAL_BGL_NP_TEMPLATE_PARAMETERS>
bool write_OFF_with_or_without_vnormals(std::ostream& os,
const Surface_mesh<Point>& sm,
const CGAL_BGL_NP_CLASS& np)
{
typedef Surface_mesh<Point> Mesh;
typedef typename Mesh::Vertex_index Vertex_index;
typedef typename Kernel_traits<Point>::Kernel K;
typedef typename K::Vector_3 Normal;
typename Mesh::template Property_map<Vertex_index, Normal> vnormals;
bool has_vnormals;
std::tie(vnormals, has_vnormals) = sm.template property_map<Vertex_index, Normal>("v:normal");
if(has_vnormals)
return write_OFF_with_or_without_vcolors(os, sm, np.vertex_normal_map(vnormals));
else
return write_OFF_with_or_without_vcolors(os, sm, np);
}
} // namespace internal
} // namespace IO
/// \relates Surface_mesh
///
/// Inserts the surface mesh in an output stream.
///
/// \note The <A HREF="https://en.cppreference.com/w/cpp/io/ios_base/precision" >`precision()`</A>
/// of the output stream might not be sufficient depending on the data to be written.
///
template <typename Point, typename CGAL_BGL_NP_TEMPLATE_PARAMETERS>
bool write_OFF(std::ostream& os,
const Surface_mesh<Point>& sm,
const CGAL_BGL_NP_CLASS& np)
{
// Just to discard any excess named parameters
typename CGAL::GetVertexPointMap<Surface_mesh<Point>, CGAL_BGL_NP_CLASS>::const_type
vpm = parameters::choose_parameter(parameters::get_parameter(np, internal_np::vertex_point),
get_const_property_map(CGAL::vertex_point, sm));
return IO::internal::write_OFF_with_or_without_vnormals(os, sm, parameters::vertex_point_map(vpm));
}
} // namespace CGAL
#endif // CGAL_SURFACE_MESH_IO_OFF_H

View File

@ -13,14 +13,14 @@
#ifndef CGAL_SURFACE_MESH_IO_PLY #ifndef CGAL_SURFACE_MESH_IO_PLY
#define CGAL_SURFACE_MESH_IO_PLY #define CGAL_SURFACE_MESH_IO_PLY
#if !defined(CGAL_CFG_NO_CPP0X_RVALUE_REFERENCE) && !defined(CGAL_CFG_NO_CPP0X_VARIADIC_TEMPLATES)
#include <CGAL/IO/PLY.h> #include <CGAL/IO/PLY.h>
namespace CGAL { namespace CGAL {
namespace IO { namespace IO {
namespace internal { namespace internal {
#if !defined(CGAL_CFG_NO_CPP0X_RVALUE_REFERENCE) && !defined(CGAL_CFG_NO_CPP0X_VARIADIC_TEMPLATES)
template <typename Point> template <typename Point>
class Surface_mesh_filler class Surface_mesh_filler
{ {
@ -394,7 +394,7 @@ template <typename Point>
bool fill_simplex_specific_header(std::ostream& os, bool fill_simplex_specific_header(std::ostream& os,
const Surface_mesh<Point>& sm, const Surface_mesh<Point>& sm,
std::vector<Abstract_property_printer< std::vector<Abstract_property_printer<
typename Surface_mesh<Point>::Vertex_index>*>& printers, typename Surface_mesh<Point>::Vertex_index>*>& printers,
const std::string& prop) const std::string& prop)
{ {
typedef Surface_mesh<Point> SMesh; typedef Surface_mesh<Point> SMesh;
@ -695,10 +695,331 @@ void fill_header(std::ostream& os, const Surface_mesh<Point>& sm,
} }
} }
#endif
} // namespace internal } // namespace internal
} // namespace IO } // namespace IO
/// \relates Surface_mesh
/// Inserts the surface mesh in an output stream in PLY format.
/// If found, "v:normal", "v:color" and "f:color" are inserted in the stream.
/// All other vertex and face properties with simple types are inserted in the stream.
/// Edges are only inserted in the stream if they have at least one
/// property with simple type: if they do, all edge properties with
/// simple types are inserted in the stream. The halfedges follow
/// the same behavior.
///
/// If provided, the `comments` string is included line by line in
/// the header of the PLY stream (each line will be precedeed by
/// "comment ").
///
template <typename P>
bool read_PLY(std::ostream& os, const Surface_mesh<P>& sm, const std::string& comments = std::string())
{
typedef Surface_mesh<P> SMesh;
typedef typename SMesh::Vertex_index VIndex;
typedef typename SMesh::Face_index FIndex;
typedef typename SMesh::Edge_index EIndex;
typedef typename SMesh::Halfedge_index HIndex;
os << "ply" << std::endl
<<((get_mode(os) == IO::BINARY) ? "format binary_little_endian 1.0" : "format ascii 1.0") << std::endl
<< "comment Generated by the CGAL library" << std::endl;
if(comments != std::string())
{
std::istringstream iss(comments);
std::string line;
while(getline(iss, line))
{
if(line != "Generated by the CGAL library") // Avoid repeating the line if multiple savings
os << "comment " << line << std::endl;
}
}
os << "element vertex " << sm.number_of_vertices() << std::endl;
std::vector<IO::internal::Abstract_property_printer<VIndex>*> vprinters;
IO::internal::fill_header(os, sm, vprinters);
os << "element face " << sm.number_of_faces() << std::endl;
os << "property list uchar int vertex_indices" << std::endl;
std::vector<IO::internal::Abstract_property_printer<FIndex>*> fprinters;
IO::internal::fill_header(os, sm, fprinters);
std::vector<IO::internal::Abstract_property_printer<EIndex>*> eprinters;
if(sm.template properties<EIndex>().size() > 1)
{
std::ostringstream oss;
IO::internal::fill_header(oss, sm, eprinters);
if(!eprinters.empty())
{
os << "element edge " << sm.number_of_edges() << std::endl;
os << "property int v0" << std::endl;
os << "property int v1" << std::endl;
os << oss.str();
}
}
std::vector<IO::internal::Abstract_property_printer<HIndex>*> hprinters;
if(sm.template properties<HIndex>().size() > 1)
{
std::ostringstream oss;
IO::internal::fill_header(oss, sm, hprinters);
if(!hprinters.empty())
{
os << "element halfedge " << sm.number_of_halfedges() << std::endl;
os << "property int source" << std::endl;
os << "property int target" << std::endl;
os << oss.str();
}
}
os << "end_header" << std::endl;
for(VIndex vi : sm.vertices())
{
for(std::size_t i = 0; i < vprinters.size(); ++ i)
{
vprinters[i]->print(os, vi);
if(get_mode(os) == IO::ASCII)
os << " ";
}
if(get_mode(os) == IO::ASCII)
os << std::endl;
}
std::vector<VIndex> polygon;
for(FIndex fi : sm.faces())
{
// Get list of vertex indices
polygon.clear();
for(HIndex hi : halfedges_around_face(halfedge(fi, sm), sm))
polygon.push_back(sm.target(hi));
if(get_mode(os) == IO::ASCII)
{
os << polygon.size() << " ";
for(std::size_t i = 0; i < polygon.size(); ++ i)
os << int(polygon[i]) << " ";
}
else
{
unsigned char size =(unsigned char)(polygon.size());
os.write(reinterpret_cast<char*>(&size), sizeof(size));
for(std::size_t i = 0; i < polygon.size(); ++ i)
{
int idx = int(polygon[i]);
os.write(reinterpret_cast<char*>(&idx), sizeof(idx));
}
}
for(std::size_t i = 0; i < fprinters.size(); ++ i)
{
fprinters[i]->print(os, fi);
if(get_mode(os) == IO::ASCII)
os << " ";
}
if(get_mode(os) == IO::ASCII)
os << std::endl;
}
if(!eprinters.empty())
{
for(EIndex ei : sm.edges())
{
if(get_mode(os) == IO::ASCII)
os << int(sm.vertex(ei,0)) << " " << int(sm.vertex(ei,1)) << " ";
else
{
int v0 = int(sm.vertex(ei,0));
int v1 = int(sm.vertex(ei,1));
os.write(reinterpret_cast<char*>(&v0), sizeof(v0));
os.write(reinterpret_cast<char*>(&v1), sizeof(v1));
}
for(std::size_t i = 0; i < eprinters.size(); ++ i)
{
eprinters[i]->print(os, ei);
if(get_mode(os) == IO::ASCII)
os << " ";
}
if(get_mode(os) == IO::ASCII)
os << std::endl;
}
}
if(!hprinters.empty())
{
for(HIndex hi : sm.halfedges())
{
if(get_mode(os) == IO::ASCII)
os << int(sm.source(hi)) << " " << int(sm.target(hi)) << " ";
else
{
int source = int(sm.source(hi));
int target = int(sm.target(hi));
os.write(reinterpret_cast<char*>(&source), sizeof(source));
os.write(reinterpret_cast<char*>(&target), sizeof(target));
}
for(std::size_t i = 0; i < hprinters.size(); ++ i)
{
hprinters[i]->print(os, hi);
if(get_mode(os) == IO::ASCII)
os << " ";
}
if(get_mode(os) == IO::ASCII)
os << std::endl;
}
}
for(std::size_t i = 0; i < vprinters.size(); ++ i)
delete vprinters[i];
for(std::size_t i = 0; i < fprinters.size(); ++ i)
delete fprinters[i];
for(std::size_t i = 0; i < eprinters.size(); ++ i)
delete eprinters[i];
for(std::size_t i = 0; i < hprinters.size(); ++ i)
delete hprinters[i];
return true;
}
/// \cond SKIP_IN_MANUAL
template <typename P>
bool read_PLY(std::istream& is, Surface_mesh<P>& sm)
{
std::string dummy;
return read_PLY(is, sm, dummy);
}
/// \endcond
/// Extracts the surface mesh from an input stream in Ascii or
/// Binary PLY format and appends it to the surface mesh `sm`.
///
/// - the operator reads the vertex `point` property and the face
/// `vertex_index` (or `vertex_indices`) property;
/// - if three PLY properties `nx`, `ny` and `nz` with type `float`
/// or `double` are found for vertices, a "v:normal" vertex
/// property map is added;
/// - if three PLY properties `red`, `green` and `blue` with type
/// `uchar` are found for vertices, a "v:color" vertex property
/// map is added;
/// - if three PLY properties `red`, `green` and `blue` with type
/// `uchar` are found for faces, a "f:color" face property map is
/// added;
/// - if any other PLY property is found, a "[s]:[name]" property map is
/// added, where `[s]` is `v` for vertex and `f` for face, and
/// `[name]` is the name of the PLY property.
///
/// The `comments` parameter can be omitted. If provided, it will be
/// used to store the potential comments found in the PLY
/// header. Each line starting by "comment " in the header is
/// appended to the `comments` string (without the "comment " word).
///
/// \pre The data in the stream must represent a two-manifold. If this is not the case
/// the `failbit` of `is` is set and the mesh cleared.
/// \relates Surface_mesh
template <typename P>
bool read_PLY(std::istream& is,
Surface_mesh<P>& sm,
std::string& comments)
{
typedef typename Surface_mesh<P>::size_type size_type;
if(!is.good())
{
std::cerr << "Error: cannot open file" << std::endl;
return false;
}
IO::internal::PLY_reader reader;
IO::internal::Surface_mesh_filler<P> filler(sm);
if(!(reader.init(is)))
{
is.setstate(std::ios::failbit);
return false;
}
comments = reader.comments();
for(std::size_t i = 0; i < reader.number_of_elements(); ++ i)
{
IO::internal::PLY_element& element = reader.element(i);
bool is_vertex =(element.name() == "vertex" || element.name() == "vertices");
bool is_face = false;
bool is_edge = false;
bool is_halfedge = false;
if(is_vertex)
{
sm.reserve(sm.number_of_vertices() + size_type(element.number_of_items()),
sm.number_of_edges(),
sm.number_of_faces());
filler.instantiate_vertex_properties(element);
}
else
is_face =(element.name() == "face" || element.name() == "faces");
if(is_face)
{
sm.reserve(sm.number_of_vertices(),
sm.number_of_edges(),
sm.number_of_faces() + size_type(element.number_of_items()));
filler.instantiate_face_properties(element);
}
else
is_edge =(element.name() == "edge");
if(is_edge)
filler.instantiate_edge_properties(element);
else
is_halfedge =(element.name() == "halfedge");
if(is_halfedge)
filler.instantiate_halfedge_properties(element);
for(std::size_t j = 0; j < element.number_of_items(); ++ j)
{
for(std::size_t k = 0; k < element.number_of_properties(); ++ k)
{
IO::internal::PLY_read_number* property = element.property(k);
property->get(is);
if(is.fail())
return false;
}
if(is_vertex)
filler.process_vertex_line(element);
else if(is_face)
{
if(!filler.process_face_line(element))
{
is.setstate(std::ios::failbit);
return false;
}
}
else if(is_edge)
filler.process_edge_line(element);
else if(is_halfedge)
filler.process_halfedge_line(element);
}
}
return true;
}
} // namespace CGAL } // namespace CGAL
#endif // !defined(CGAL_CFG_NO_CPP0X_RVALUE_REFERENCE) && !defined(CGAL_CFG_NO_CPP0X_VARIADIC_TEMPLATES)
#endif // CGAL_SURFACE_MESH_IO_PLY #endif // CGAL_SURFACE_MESH_IO_PLY

View File

@ -10,7 +10,6 @@
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial // SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-Commercial
// //
#ifndef CGAL_SURFACE_MESH_PROPERTY_H #ifndef CGAL_SURFACE_MESH_PROPERTY_H
#define CGAL_SURFACE_MESH_PROPERTY_H #define CGAL_SURFACE_MESH_PROPERTY_H
@ -18,13 +17,13 @@
#ifndef DOXYGEN_RUNNING #ifndef DOXYGEN_RUNNING
#include <vector>
#include <string>
#include <algorithm>
#include <typeinfo>
#include <CGAL/property_map.h>
#include <CGAL/assertions.h> #include <CGAL/assertions.h>
#include <CGAL/property_map.h>
#include <algorithm>
#include <string>
#include <typeinfo>
#include <vector>
namespace CGAL { namespace CGAL {

View File

@ -49,7 +49,6 @@
namespace CGAL { namespace CGAL {
#ifndef DOXYGEN_RUNNING #ifndef DOXYGEN_RUNNING
/// Base class for vertex, halfedge, edge, and face index. /// Base class for vertex, halfedge, edge, and face index.
/// ///
@ -2126,577 +2125,23 @@ private: //------------------------------------------------------- private data
return sm; return sm;
} }
/// \relates Surface_mesh
/// Inserts the surface mesh in an output stream in Ascii OFF format.
/// Only the \em point property is inserted in the stream.
/// If an alternative vertex_point map is given through `np`,
/// then it will be used instead of the default one.
/// \pre `operator<<(std::ostream&,const P&)` must be defined.
/// \note The <A HREF="https://en.cppreference.com/w/cpp/io/ios_base/precision">`precision()`</A>
/// of the output stream might not be sufficient depending on the data to be written.
template <typename P, typename NamedParameters>
bool write_off(std::ostream& os, const Surface_mesh<P>& sm, const NamedParameters& np) {
typedef Surface_mesh<P> Mesh;
typedef typename Mesh::Vertex_index Vertex_index;
typedef typename Mesh::Face_index Face_index;
typename Mesh::template Property_map<typename Mesh::Vertex_index, CGAL::Color> vcolors;
bool has_vcolors;
boost::tie(vcolors, has_vcolors) = sm.template property_map<typename Mesh::Vertex_index, CGAL::Color >("v:color");
typename Mesh::template Property_map<typename Mesh::Face_index, CGAL::Color> fcolors;
bool has_fcolors;
boost::tie(fcolors, has_fcolors) = sm.template property_map<typename Mesh::Face_index, CGAL::Color >("f:color");
using parameters::choose_parameter;
using parameters::get_parameter;
if(!has_fcolors && !has_vcolors)
os << "OFF\n" << sm.number_of_vertices() << " " << sm.number_of_faces() << " 0\n";
else
os << "COFF\n" << sm.number_of_vertices() << " " << sm.number_of_faces() << " 0\n";
std::vector<int> reindex;
typename CGAL::GetVertexPointMap<Surface_mesh<P>, NamedParameters>::const_type
vpm = choose_parameter(get_parameter(np, internal_np::vertex_point),
get_const_property_map(CGAL::vertex_point, sm));
reindex.resize(sm.num_vertices());
int n = 0;
for(Vertex_index v : sm.vertices()){
P p = get(vpm, v);
os << p.x() << " " << p.y() << " " << p.z();
if(has_vcolors)
{
CGAL::Color color = vcolors[v];
os <<" "<< static_cast<int>(color.r())<<" "<< static_cast<int>(color.g())<<" "<< static_cast<int>(color.b());
}
os << '\n';
reindex[v]=n++;
}
for(Face_index f : sm.faces()){
os << sm.degree(f);
for(Vertex_index v : CGAL::vertices_around_face(sm.halfedge(f),sm)){
os << " " << reindex[v];
}
if(has_fcolors)
{
CGAL::Color color = fcolors[f];
os <<" "<< static_cast<int>(color.r())<<" "<< static_cast<int>(color.g())<<" "<< static_cast<int>(color.b());
}
os << '\n';
}
return os.good();
}
template <typename P>
bool write_off(std::ostream& os, const Surface_mesh<P>& sm) {
return write_off(os, sm, CGAL::parameters::all_default());
}
/// \relates Surface_mesh /// \relates Surface_mesh
/// ///
/// This operator calls `write_off(std::ostream& os, const CGAL::Surface_mesh& sm)`. /// This operator calls `write_OFF(std::ostream& os, const CGAL::Surface_mesh& sm)`.
template <typename P> template <typename P>
std::ostream& operator<<(std::ostream& os, const Surface_mesh<P>& sm) std::ostream& operator<<(std::ostream& os, const Surface_mesh<P>& sm)
{ {
write_off(os, sm, CGAL::parameters::all_default()); write_OFF(os, sm);
return os; return os;
} }
#if !defined(CGAL_CFG_NO_CPP0X_RVALUE_REFERENCE) && !defined(CGAL_CFG_NO_CPP0X_VARIADIC_TEMPLATES)
/// \relates Surface_mesh /// \relates Surface_mesh
/// Inserts the surface mesh in an output stream in PLY format.
/// If found, "v:normal", "v:color" and "f:color" are inserted in the stream.
/// All other vertex and face properties with simple types are inserted in the stream.
/// Edges are only inserted in the stream if they have at least one
/// property with simple type: if they do, all edge properties with
/// simple types are inserted in the stream. The halfedges follow
/// the same behavior.
/// ///
/// If provided, the `comments` string is included line by line in /// This operator calls `read_OFF(std::istream& is, CGAL::Surface_mesh& sm)`.
/// the header of the PLY stream (each line will be precedeed by
/// "comment ").
///
template <typename P>
bool write_ply(std::ostream& os, const Surface_mesh<P>& sm, const std::string& comments = std::string())
{
typedef Surface_mesh<P> SMesh;
typedef typename SMesh::Vertex_index VIndex;
typedef typename SMesh::Face_index FIndex;
typedef typename SMesh::Edge_index EIndex;
typedef typename SMesh::Halfedge_index HIndex;
os << "ply" << std::endl
<< ((get_mode(os) == IO::BINARY) ? "format binary_little_endian 1.0" : "format ascii 1.0") << std::endl
<< "comment Generated by the CGAL library" << std::endl;
if (comments != std::string())
{
std::istringstream iss (comments);
std::string line;
while (getline(iss, line))
{
if (line != "Generated by the CGAL library") // Avoid repeating the line if multiple savings
os << "comment " << line << std::endl;
}
}
os << "element vertex " << sm.number_of_vertices() << std::endl;
std::vector<IO::internal::Abstract_property_printer<VIndex>*> vprinters;
IO::internal::fill_header (os, sm, vprinters);
os << "element face " << sm.number_of_faces() << std::endl;
os << "property list uchar int vertex_indices" << std::endl;
std::vector<IO::internal::Abstract_property_printer<FIndex>*> fprinters;
IO::internal::fill_header (os, sm, fprinters);
std::vector<IO::internal::Abstract_property_printer<EIndex>*> eprinters;
if (sm.template properties<EIndex>().size() > 1)
{
std::ostringstream oss;
IO::internal::fill_header (oss, sm, eprinters);
if (!eprinters.empty())
{
os << "element edge " << sm.number_of_edges() << std::endl;
os << "property int v0" << std::endl;
os << "property int v1" << std::endl;
os << oss.str();
}
}
std::vector<IO::internal::Abstract_property_printer<HIndex>*> hprinters;
if (sm.template properties<HIndex>().size() > 1)
{
std::ostringstream oss;
IO::internal::fill_header (oss, sm, hprinters);
if (!hprinters.empty())
{
os << "element halfedge " << sm.number_of_halfedges() << std::endl;
os << "property int source" << std::endl;
os << "property int target" << std::endl;
os << oss.str();
}
}
os << "end_header" << std::endl;
for(VIndex vi : sm.vertices())
{
for (std::size_t i = 0; i < vprinters.size(); ++ i)
{
vprinters[i]->print(os, vi);
if (get_mode (os) == IO::ASCII)
os << " ";
}
if (get_mode (os) == IO::ASCII)
os << std::endl;
}
std::vector<VIndex> polygon;
for(FIndex fi : sm.faces())
{
// Get list of vertex indices
polygon.clear();
for(HIndex hi : halfedges_around_face(halfedge(fi, sm), sm))
polygon.push_back (sm.target(hi));
if (get_mode (os) == IO::ASCII)
{
os << polygon.size() << " ";
for (std::size_t i = 0; i < polygon.size(); ++ i)
os << int(polygon[i]) << " ";
}
else
{
unsigned char size = (unsigned char)(polygon.size());
os.write (reinterpret_cast<char*>(&size), sizeof(size));
for (std::size_t i = 0; i < polygon.size(); ++ i)
{
int idx = int(polygon[i]);
os.write (reinterpret_cast<char*>(&idx), sizeof(idx));
}
}
for (std::size_t i = 0; i < fprinters.size(); ++ i)
{
fprinters[i]->print(os, fi);
if (get_mode (os) == IO::ASCII)
os << " ";
}
if (get_mode (os) == IO::ASCII)
os << std::endl;
}
if (!eprinters.empty())
{
for(EIndex ei : sm.edges())
{
if (get_mode (os) == IO::ASCII)
os << int(sm.vertex(ei,0)) << " " << int(sm.vertex(ei,1)) << " ";
else
{
int v0 = int(sm.vertex(ei,0));
int v1 = int(sm.vertex(ei,1));
os.write (reinterpret_cast<char*>(&v0), sizeof(v0));
os.write (reinterpret_cast<char*>(&v1), sizeof(v1));
}
for (std::size_t i = 0; i < eprinters.size(); ++ i)
{
eprinters[i]->print(os, ei);
if (get_mode (os) == IO::ASCII)
os << " ";
}
if (get_mode (os) == IO::ASCII)
os << std::endl;
}
}
if (!hprinters.empty())
{
for(HIndex hi : sm.halfedges())
{
if (get_mode (os) == IO::ASCII)
os << int(sm.source(hi)) << " " << int(sm.target(hi)) << " ";
else
{
int source = int(sm.source(hi));
int target = int(sm.target(hi));
os.write (reinterpret_cast<char*>(&source), sizeof(source));
os.write (reinterpret_cast<char*>(&target), sizeof(target));
}
for (std::size_t i = 0; i < hprinters.size(); ++ i)
{
hprinters[i]->print(os, hi);
if (get_mode (os) == IO::ASCII)
os << " ";
}
if (get_mode (os) == IO::ASCII)
os << std::endl;
}
}
for (std::size_t i = 0; i < vprinters.size(); ++ i)
delete vprinters[i];
for (std::size_t i = 0; i < fprinters.size(); ++ i)
delete fprinters[i];
for (std::size_t i = 0; i < eprinters.size(); ++ i)
delete eprinters[i];
for (std::size_t i = 0; i < hprinters.size(); ++ i)
delete hprinters[i];
return true;
}
#endif
/// @cond CGAL_DOCUMENT_INTERNALS
inline std::istream& sm_skip_comments( std::istream& in) {
char c;
in >> c;
while(c == '#')
{
in.ignore((std::numeric_limits<std::streamsize>::max)(), '\n');
in >> c;
}
in.putback(c);
return in;
}
/// @endcond
/// \relates Surface_mesh
/// Extracts the surface mesh from an input stream in Ascii OFF, COFF, NOFF, CNOFF
/// format and appends it to the surface mesh `sm`.
/// The operator reads the point property as well as "v:normal", "v:color", and "f:color".
/// Vertex texture coordinates are ignored.
/// If an alternative vertex_point map is given through `np`,
/// then it will be used instead of the default one.
/// \pre `operator>>(std::istream&,const P&)` must be defined.
/// \pre The data in the stream must represent a two-manifold. If this is not the case
/// the `failbit` of `is` is set and the mesh cleared.
template <typename P, typename NamedParameters>
bool read_off(std::istream& is, Surface_mesh<P>& sm, const NamedParameters& np)
{
typedef Surface_mesh<P> Mesh;
typedef typename Kernel_traits<P>::Kernel K;
typedef typename K::Vector_3 Vector_3;
typedef typename Mesh::Face_index Face_index;
typedef typename Mesh::Vertex_index Vertex_index;
typedef typename Mesh::size_type size_type;
using parameters::choose_parameter;
using parameters::get_parameter;
typename CGAL::GetVertexPointMap<Surface_mesh<P>, NamedParameters>::type
vpm = choose_parameter(get_parameter(np, CGAL::internal_np::vertex_point),
get_property_map(CGAL::vertex_point, sm));
int n, f, e;
std::string off;
is >> sm_skip_comments;
is >> off;
if(! (
(off == "OFF") || (off == "COFF") || (off == "NOFF") || (off == "CNOFF")
)
)
{
is.setstate(std::ios::failbit);
return false;
}
is >> n >> f >> e;
if(!is){
return false;
}
sm.reserve(sm.num_vertices()+n, sm.num_edges()+e, sm.num_faces()+f);
std::vector<Vertex_index> vertexmap(n);
Vector_3 v;
typename Mesh::template Property_map<Vertex_index,CGAL::Color> vcolor;
typename Mesh::template Property_map<Vertex_index,Vector_3> vnormal;
bool vcolored = false, v_has_normals = false;
if((off == "NOFF") || (off == "CNOFF")){
bool created;
boost::tie(vnormal, created) = sm.template add_property_map<Vertex_index,Vector_3>("v:normal",Vector_3(0,0,0));
v_has_normals = true;
}
char ci;
for(int i=0; i < n; i++){
is >> sm_skip_comments;
double x, y, z;
is >> iformat(x) >> iformat(y) >> iformat(z);
Vertex_index vi = sm.add_vertex();
put(vpm, vi, P(x, y, z));
vertexmap[i] = vi;
if(v_has_normals){
is >> v;
vnormal[vi] = v;
}
if(i == 0 && ((off == "COFF") || (off == "CNOFF"))){
std::string col;
std::getline(is, col);
std::istringstream iss(col);
if(iss >> ci){
bool created;
boost::tie(vcolor, created) = sm.template add_property_map<Vertex_index,CGAL::Color>("v:color",CGAL::Color(0,0,0));
std::istringstream iss2(col);
vcolor[vi] = File_scanner_OFF::get_color_from_line(iss2);
vcolored = true;
}
}else{
if(vcolored){
//stores the RGB value
vcolor[vi] = File_scanner_OFF::get_color_from_line(is);
}
}
}
std::vector<Vertex_index> vr;
size_type d, vi;
bool fcolored = false;
typename Mesh::template Property_map<Face_index,CGAL::Color> fcolor;
for(int i=0; i < f; i++){
is >> sm_skip_comments;
is >> d;
if(!is){
sm.clear();
return false;
}
vr.resize(d);
for(std::size_t j=0; j<d; j++){
is >> vi;
vr[j] = vertexmap[vi];
}
Face_index fi = sm.add_face(vr);
if(fi == sm.null_face())
{
is.setstate(std::ios::failbit);
sm.clear();
return false;
}
// the first face will tell us if faces have a color map
// TODO: extend this to RGBA
if(i == 0 ){
std::string col;
std::getline(is, col);
std::istringstream iss(col);
if(iss >> ci){
bool created;
boost::tie(fcolor, created) = sm.template add_property_map<Face_index,CGAL::Color>("f:color",CGAL::Color(0,0,0));
fcolored = true;
std::istringstream iss2(col);
fcolor[fi] = File_scanner_OFF::get_color_from_line(iss2);
}
} else {
if(fcolored){
fcolor[fi] = File_scanner_OFF::get_color_from_line(is);
}
}
}
return is.good();
}
template <typename P>
bool read_off(std::istream& is, Surface_mesh<P>& sm)
{
return read_off(is, sm, parameters::all_default());
}
#if !defined(CGAL_CFG_NO_CPP0X_RVALUE_REFERENCE) && !defined(CGAL_CFG_NO_CPP0X_VARIADIC_TEMPLATES)
/// \cond SKIP_IN_MANUAL
template <typename P>
bool read_ply(std::istream& is, Surface_mesh<P>& sm)
{
std::string dummy;
return read_ply (is, sm, dummy);
}
/// \endcond
/// Extracts the surface mesh from an input stream in Ascii or
/// Binary PLY format and appends it to the surface mesh `sm`.
///
/// - the operator reads the vertex `point` property and the face
/// `vertex_index` (or `vertex_indices`) property;
/// - if three PLY properties `nx`, `ny` and `nz` with type `float`
/// or `double` are found for vertices, a "v:normal" vertex
/// property map is added;
/// - if three PLY properties `red`, `green` and `blue` with type
/// `uchar` are found for vertices, a "v:color" vertex property
/// map is added;
/// - if three PLY properties `red`, `green` and `blue` with type
/// `uchar` are found for faces, a "f:color" face property map is
/// added;
/// - if any other PLY property is found, a "[s]:[name]" property map is
/// added, where `[s]` is `v` for vertex and `f` for face, and
/// `[name]` is the name of the PLY property.
///
/// The `comments` parameter can be omitted. If provided, it will be
/// used to store the potential comments found in the PLY
/// header. Each line starting by "comment " in the header is
/// appended to the `comments` string (without the "comment " word).
///
/// \pre The data in the stream must represent a two-manifold. If this is not the case
/// the `failbit` of `is` is set and the mesh cleared.
/// \relates Surface_mesh
template <typename P>
bool read_ply(std::istream& is, Surface_mesh<P>& sm, std::string& comments)
{
typedef typename Surface_mesh<P>::size_type size_type;
if(!is)
{
std::cerr << "Error: cannot open file" << std::endl;
return false;
}
IO::internal::PLY_reader reader;
IO::internal::Surface_mesh_filler<P> filler(sm);
if (!(reader.init (is)))
{
is.setstate(std::ios::failbit);
return false;
}
comments = reader.comments();
for (std::size_t i = 0; i < reader.number_of_elements(); ++ i)
{
IO::internal::PLY_element& element = reader.element(i);
bool is_vertex = (element.name() == "vertex" || element.name() == "vertices");
bool is_face = false;
bool is_edge = false;
bool is_halfedge = false;
if (is_vertex)
{
sm.reserve(sm.number_of_vertices() + size_type(element.number_of_items()),
sm.number_of_edges(),
sm.number_of_faces());
filler.instantiate_vertex_properties (element);
}
else
is_face = (element.name() == "face" || element.name() == "faces");
if (is_face)
{
sm.reserve(sm.number_of_vertices(),
sm.number_of_edges(),
sm.number_of_faces() + size_type(element.number_of_items()));
filler.instantiate_face_properties (element);
}
else
is_edge = (element.name() == "edge");
if (is_edge)
filler.instantiate_edge_properties (element);
else
is_halfedge = (element.name() == "halfedge");
if (is_halfedge)
filler.instantiate_halfedge_properties (element);
for (std::size_t j = 0; j < element.number_of_items(); ++ j)
{
for (std::size_t k = 0; k < element.number_of_properties(); ++ k)
{
IO::internal::PLY_read_number* property = element.property(k);
property->get (is);
if (is.fail())
return false;
}
if (is_vertex)
filler.process_vertex_line (element);
else if (is_face)
{
if (!filler.process_face_line (element))
{
is.setstate(std::ios::failbit);
return false;
}
}
else if (is_edge)
filler.process_edge_line (element);
else if (is_halfedge)
filler.process_halfedge_line (element);
}
}
return true;
}
#endif
/// \relates Surface_mesh
/// This operator calls `read_off(std::istream& is, CGAL::Surface_mesh& sm)`.
/// \attention Up to %CGAL 4.10 this operator called `sm.clear()`.
template <typename P> template <typename P>
std::istream& operator>>(std::istream& is, Surface_mesh<P>& sm) std::istream& operator>>(std::istream& is, Surface_mesh<P>& sm)
{ {
read_off(is, sm); read_OFF(is, sm);
return is; return is;
} }
@ -3180,8 +2625,8 @@ namespace internal{
} }
} }
} // CGAL } // namespace CGAL
#ifndef DOXYGEN_RUNNING #ifndef DOXYGEN_RUNNING
namespace std { namespace std {

View File

@ -1,33 +1,34 @@
#include <CGAL/Surface_mesh/Surface_mesh.h> #include <CGAL/Surface_mesh/Surface_mesh.h>
#include <CGAL/Exact_predicates_inexact_constructions_kernel.h> #include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <cstring> #include <cstring>
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel;
typedef Kernel::Point_3 Point; typedef Kernel::Point_3 Point;
typedef CGAL::Surface_mesh<Point> SMesh;
typedef boost::graph_traits<SMesh>::face_descriptor face_descriptor;
typedef boost::graph_traits<SMesh>::vertex_descriptor vertex_descriptor;
typedef CGAL::Surface_mesh<Point> SMesh;
typedef boost::graph_traits<SMesh>::vertex_descriptor vertex_descriptor;
typedef boost::graph_traits<SMesh>::face_descriptor face_descriptor;
int main() int main()
{ {
std::ifstream in ("colored_tetra.ply"); std::ifstream in("colored_tetra.ply");
SMesh mesh; SMesh mesh;
CGAL::read_ply (in, mesh); CGAL::read_PLY(in, mesh);
std::cerr << "Read mesh with " << mesh.number_of_vertices() << " vertices and " std::cerr << "Read mesh with " << mesh.number_of_vertices() << " vertices and "
<< mesh.number_of_faces() << " faces" << std::endl; << mesh.number_of_faces() << " faces" << std::endl;
std::cerr << "Properties associated with vertices:" << std::endl; std::cerr << "Properties associated with vertices:" << std::endl;
std::vector<std::string> properties = mesh.properties<SMesh::Vertex_index>(); std::vector<std::string> properties = mesh.properties<SMesh::Vertex_index>();
for (std::size_t i = 0; i < properties.size(); ++ i) for(std::size_t i = 0; i < properties.size(); ++ i)
std::cerr << " * " << properties[i] << std::endl; std::cerr << " * " << properties[i] << std::endl;
std::cerr << "Properties associated with faces:" << std::endl; std::cerr << "Properties associated with faces:" << std::endl;
properties = mesh.properties<SMesh::Face_index>(); properties = mesh.properties<SMesh::Face_index>();
for (std::size_t i = 0; i < properties.size(); ++ i) for(std::size_t i = 0; i < properties.size(); ++ i)
std::cerr << " * " << properties[i] << std::endl; std::cerr << " * " << properties[i] << std::endl;
mesh.add_property_map<SMesh::Edge_index, short>("id", 42); mesh.add_property_map<SMesh::Edge_index, short>("id", 42);
@ -35,12 +36,12 @@ int main()
mesh.add_property_map<SMesh::Halfedge_index, float>("v", 37.f); mesh.add_property_map<SMesh::Halfedge_index, float>("v", 37.f);
// Append second mesh // Append second mesh
std::ifstream in2 ("tetra.ply"); std::ifstream in2("tetra.ply");
CGAL::read_ply (in2, mesh); CGAL::read_PLY(in2, mesh);
std::ofstream out ("out.ply"); std::ofstream out("out.ply");
// CGAL::set_binary_mode(out); // CGAL::set_binary_mode(out);
CGAL::write_ply (out, mesh); CGAL::write_PLY(out, mesh);
return 0; return 0;
} }