diff --git a/BGL/include/CGAL/boost/graph/IO/PLY.h b/BGL/include/CGAL/boost/graph/IO/PLY.h index 290543ecdef2..1191f94c2ea7 100644 --- a/BGL/include/CGAL/boost/graph/IO/PLY.h +++ b/BGL/include/CGAL/boost/graph/IO/PLY.h @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -44,7 +45,7 @@ class PLY_builder typedef typename Base::Face_container Face_container; public: - PLY_builder(std::istream& is) : Base(is) { } + PLY_builder(std::istream& is, std::string& comments) : Base(is), comments(comments) { } template bool read(std::istream& is, @@ -52,19 +53,22 @@ class PLY_builder Face_container& faces, const NamedParameters& np) { - return read_PLY(is, points, faces, np); + return read_PLY(is, points, faces, comments, np); } + + std::string& comments; }; template bool read_PLY_BGL(std::istream& is, Graph& g, + std::string& comments, const CGAL_NP_CLASS& np = parameters::default_values()) { typedef typename CGAL::GetVertexPointMap::type VPM; typedef typename boost::property_traits::value_type Point; - internal::PLY_builder builder(is); + internal::PLY_builder builder(is, comments); return builder(g, np); } @@ -84,6 +88,7 @@ bool read_PLY_BGL(std::istream& is, \param is the input stream \param g the graph to be built from the input data + \param comments a string included line by line in the header of the PLY stream (each line will be precedeed by "comment ") \param np optional \ref bgl_namedparameters "Named Parameters" described below \cgalNamedParamsBegin @@ -128,6 +133,20 @@ bool read_PLY_BGL(std::istream& is, \sa Overloads of this function for specific models of the concept `FaceGraph`. */ +template +bool read_PLY(std::istream& is, + Graph& g, + std::string& comments, + const CGAL_NP_CLASS& np = parameters::default_values() +#ifndef DOXYGEN_RUNNING + , std::enable_if_t::value>* = nullptr +#endif + ) +{ + return internal::read_PLY_BGL(is, g, comments, np); +} + template bool read_PLY(std::istream& is, @@ -138,9 +157,11 @@ bool read_PLY(std::istream& is, #endif ) { - return internal::read_PLY_BGL(is, g, np); + std::string unused_comments; + return internal::read_PLY_BGL(is, g, unused_comments, np); } + /*! \ingroup PkgBGLIoFuncsPLY @@ -153,6 +174,7 @@ bool read_PLY(std::istream& is, \param fname the name of the input file \param g the graph to be built from the input data + \param comments a string included line by line in the header of the PLY stream (each line will be precedeed by "comment" ) \param np optional \ref bgl_namedparameters "Named Parameters" described below \cgalNamedParamsBegin @@ -207,6 +229,7 @@ template bool read_PLY(const std::string& fname, Graph& g, + std::string& comments, const CGAL_NP_CLASS& np = parameters::default_values() #ifndef DOXYGEN_RUNNING , std::enable_if_t::value>* = nullptr @@ -218,16 +241,29 @@ bool read_PLY(const std::string& fname, { std::ifstream is(fname, std::ios::binary); CGAL::IO::set_mode(is, CGAL::IO::BINARY); - return internal::read_PLY_BGL(is, g, np); + return read_PLY(is, g, comments, np); } else { std::ifstream is(fname); CGAL::IO::set_mode(is, CGAL::IO::ASCII); - return internal::read_PLY_BGL(is, g, np); + return read_PLY(is, g, comments, np); } } +template +bool read_PLY(const std::string& fname, + Graph& g, + const CGAL_NP_CLASS& np = parameters::default_values() +#ifndef DOXYGEN_RUNNING + , std::enable_if_t::value>* = nullptr +#endif + ) +{ + std::string unused_comment; + return read_PLY(fname, g, unused_comment, np); +} //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// // Write @@ -259,6 +295,15 @@ bool read_PLY(const std::string& fname, must be available in `Graph`.} \cgalParamNEnd + \cgalParamNBegin{vertex_normal_map} + \cgalParamDescription{a property map associating normals to the vertices of `g`} + \cgalParamType{a class model of `ReadablePropertyMap` with `boost::graph_traits::%vertex_descriptor` + as key type and `%Vector_3` as value type} + \cgalParamDefault{`boost::get(CGAL::vertex_point, g)`} + \cgalParamExtra{If this parameter is omitted, an internal property map for `CGAL::vertex_point_t` + must be available in `Graph`.} + \cgalParamNEnd + \cgalParamNBegin{vertex_index_map} \cgalParamDescription{a property map associating to each vertex of `graph` a unique index} \cgalParamType{a class model of `WritablePropertyMap` with `boost::graph_traits::%vertex_descriptor` @@ -326,6 +371,8 @@ bool write_PLY(std::ostream& os, bool has_vcolor = !is_default_parameter::value; bool has_fcolor = !is_default_parameter::value; + constexpr bool has_vnormal = !is_default_parameter::value; + VIMap vim = CGAL::get_initialized_vertex_index_map(g, np); Vpm vpm = choose_parameter(get_parameter(np, internal_np::vertex_point), get_const_property_map(boost::vertex_point, g)); @@ -351,8 +398,20 @@ bool write_PLY(std::ostream& os, } } + os << "element vertex " << vertices(g).size() << std::endl; - internal::output_property_header(os, make_ply_point_writer (CGAL::Identity_property_map())); + if constexpr (std::is_same::Kernel::FT, float>::value) + { + internal::output_property_header(os, make_ply_point_writer (CGAL::Identity_property_map())); + } + else + { + typedef typename Kernel_traits::Kernel K; + typedef decltype(std::declval >().operator()(std::declval())) Target_point; + auto fvpm = CGAL::make_cartesian_converter_property_map(vpm); + internal::output_property_header(os, make_ply_point_writer (fvpm)); + } + //if vcm is not default add v:color property if(has_vcolor) { @@ -362,10 +421,30 @@ bool write_PLY(std::ostream& os, << "property uchar alpha" << std::endl; } + if constexpr (has_vnormal) + { + auto vnm = get_parameter(np, internal_np::vertex_normal_map); + typedef decltype(vnm) Normal_map; + typedef typename Normal_map::value_type Vector_3; + typedef typename Kernel_traits::Kernel K; + typedef typename K::FT FT; + if constexpr (std::is_same::value) + { + internal::output_property_header(os, make_ply_normal_writer (CGAL::Identity_property_map())); + } + else + { + typedef decltype(std::declval >().operator()(std::declval())) Target_vector; + auto fvnm = CGAL::make_cartesian_converter_property_map(vnm); + internal::output_property_header(os, make_ply_normal_writer (fvnm)); + } + } + os << "element face " << faces(g).size() << std::endl; internal::output_property_header( os, std::make_pair(CGAL::Identity_property_map >(), PLY_property >("vertex_indices"))); + //if fcm is not default add f:color property if(has_fcolor) { @@ -378,8 +457,42 @@ bool write_PLY(std::ostream& os, for(vertex_descriptor vd : vertices(g)) { - const Point_3& p = get(vpm, vd); - internal::output_properties(os, &p, make_ply_point_writer (CGAL::Identity_property_map())); + if constexpr (std::is_same::Kernel::FT, float>::value) + { + decltype(auto) p = get(vpm, vd); + internal::output_properties(os, &p, make_ply_point_writer (CGAL::Identity_property_map())); + } + else + { + typedef typename Kernel_traits::Kernel K; + typedef CGAL::cpp20::remove_cvref_t >().operator()(std::declval()))> Target_point; + CGAL::Cartesian_converter_property_map fvpm = CGAL::make_cartesian_converter_property_map(vpm); + decltype(auto) fp = get(fvpm, vd); + internal::output_properties(os, &fp, make_ply_point_writer (CGAL::Identity_property_map())); + } + + std::cout << "using generic writer" << std::endl; + + if constexpr (!parameters::is_default_parameter::value) + { + auto vnm = get_parameter(np, internal_np::vertex_normal_map); + typedef decltype(vnm) Normal_map; + typedef typename Normal_map::value_type Vector_3; + + if constexpr (std::is_same::Kernel::FT, float>::value) + { + decltype(auto) vec = get(vnm,vd); + internal::output_properties(os, &vec, make_ply_normal_writer (CGAL::Identity_property_map())); + } + else + { + typedef typename Kernel_traits::Kernel K; + typedef CGAL::cpp20::remove_cvref_t >().operator()(std::declval()))> Target_vector; + auto fvnm = CGAL::make_cartesian_converter_property_map(vnm); + decltype(auto) fvec = get(fvnm, vd); + internal::output_properties(os, &fvec, make_ply_normal_writer (CGAL::Identity_property_map())); + } + } if(has_vcolor) { const CGAL::IO::Color& c = get(vcm, vd); @@ -455,6 +568,15 @@ bool write_PLY(std::ostream& os, const Graph& g, const CGAL_NP_CLASS& np = param must be available in `Graph`.} \cgalParamNEnd + \cgalParamNBegin{vertex_normal_map} + \cgalParamDescription{a property map associating normals to the vertices of `g`} + \cgalParamType{a class model of `ReadablePropertyMap` with `boost::graph_traits::%vertex_descriptor` + as key type and `%Vector_3` as value type} + \cgalParamDefault{`boost::get(CGAL::vertex_point, g)`} + \cgalParamExtra{If this parameter is omitted, an internal property map for `CGAL::vertex_point_t` + must be available in `Graph`.} + \cgalParamNEnd + \cgalParamNBegin{vertex_index_map} \cgalParamDescription{a property map associating to each vertex of `graph` a unique index between `0` and `num_vertices(graph) - 1`} \cgalParamType{a class model of `ReadablePropertyMap` with `boost::graph_traits::%vertex_descriptor` diff --git a/BGL/package_info/BGL/dependencies b/BGL/package_info/BGL/dependencies index 7cf027b2c486..351c042ea63b 100644 --- a/BGL/package_info/BGL/dependencies +++ b/BGL/package_info/BGL/dependencies @@ -1,13 +1,19 @@ Algebraic_foundations +Arithmetic_kernel BGL Cartesian_kernel Circulator Distance_2 Distance_3 +Filtered_kernel +Homogeneous_kernel Hash_map Installation +Intersections_2 +Intersections_3 Interval_support Kernel_23 +Kernel_d Modular_arithmetic Number_types Profiling_tools @@ -15,3 +21,4 @@ Property_map Random_numbers STL_Extension Stream_support +CGAL_Core diff --git a/Lab/demo/Lab/Plugins/IO/PLY_io_plugin.cpp b/Lab/demo/Lab/Plugins/IO/PLY_io_plugin.cpp index 63abb1e52695..eec3cbb8cd00 100644 --- a/Lab/demo/Lab/Plugins/IO/PLY_io_plugin.cpp +++ b/Lab/demo/Lab/Plugins/IO/PLY_io_plugin.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -132,8 +133,9 @@ load(QFileInfo fileinfo, bool& ok, bool add_to_scene) { std::vector fcolors; std::vector vcolors; - if (!(CGAL::IO::read_PLY (in, points, polygons, fcolors, vcolors))) + if (!(CGAL::IO::read_PLY (in, points, polygons, comments, fcolors, vcolors))) { + CGAL_USE(comments); QApplication::restoreOverrideCursor(); ok = false; return QList(); diff --git a/Property_map/include/CGAL/property_map.h b/Property_map/include/CGAL/property_map.h index 250630763b7e..1306cf24c2c0 100644 --- a/Property_map/include/CGAL/property_map.h +++ b/Property_map/include/CGAL/property_map.h @@ -708,7 +708,6 @@ struct Cartesian_converter_property_map { return CGAL::Cartesian_converter()(get(pm.vpm, k)); } - friend void put(Cartesian_converter_property_map& pm, const key_type& k, const value_type& v) { put(pm.vpm, k, CGAL::Cartesian_converter()(v)); diff --git a/Stream_support/doc/Stream_support/IOstream.txt b/Stream_support/doc/Stream_support/IOstream.txt index 6235996baec9..0c4ac7f891de 100644 --- a/Stream_support/doc/Stream_support/IOstream.txt +++ b/Stream_support/doc/Stream_support/IOstream.txt @@ -94,25 +94,25 @@ bool is_pretty(std::ios& s); \subsection IOstreamInput Input Operator \cgal defines input operators for classes that are derived -from the class `istream`. This allows to read from istreams +from the class `std::istream`. This allows to read from istreams as `std::cin`, as well as from `std::istringstream` and `std::ifstream`. The input operator is defined for all classes in the \cgal `Kernel`. Let `is` be an input stream. \code{.cpp} // Extracts object `c` from the stream `is`. Returns `is`. -istream& operator>>(istream& is, Class c); +istream& operator>>(istream& is, Class& c); \endcode \code{.cpp} #include #include -#include +#include #include -typedef CGAL::Point_2< CGAL::Cartesian > Point; -typedef CGAL::Segment_2< CGAL::Cartesian > Segment; +typedef CGAL::Point_2< CGAL::Simple_cartesian > Point; +typedef CGAL::Segment_2< CGAL::Simple_cartesian > Segment; int main() @@ -123,7 +123,7 @@ main() CGAL::IO::set_ascii_mode(std::cin); std::cin >> p >> q; - std::ifstream f("data.txt"); + std::ifstream f("data.txt", std::ios::binary); CGAL::IO::set_binary_mode(f); f >> s >> p; @@ -150,11 +150,11 @@ ostream& operator<<(ostream& os, Class c); #include #include -#include +#include #include -typedef CGAL::Point_2< CGAL::Cartesian > Point; -typedef CGAL::Segment_2< CGAL::Cartesian > Segment; +typedef CGAL::Point_2< CGAL::Simple_cartesian > Point; +typedef CGAL::Segment_2< CGAL::Simple_cartesian > Segment; int main() { diff --git a/Stream_support/include/CGAL/IO/PLY.h b/Stream_support/include/CGAL/IO/PLY.h index 7f72b37a3f59..229094602691 100644 --- a/Stream_support/include/CGAL/IO/PLY.h +++ b/Stream_support/include/CGAL/IO/PLY.h @@ -47,6 +47,7 @@ template ::value>* = nullptr) { - return internal::read_PLY(is, points, polygons, + return internal::read_PLY(is, points, polygons, comments, std::back_inserter(hedges), std::back_inserter(fcolors), std::back_inserter(vcolors), @@ -247,6 +251,7 @@ template bool read_PLY(std::istream& is, PointRange& points, PolygonRange& polygons, + std::string& comments, ColorRange& fcolors, ColorRange& vcolors, const bool verbose = false) @@ -254,7 +259,7 @@ bool read_PLY(std::istream& is, std::vector > dummy_pui; std::vector > dummy_pf; - return internal::read_PLY(is, points, polygons, + return internal::read_PLY(is, points, polygons, comments, std::back_inserter(dummy_pui), std::back_inserter(fcolors), std::back_inserter(vcolors), @@ -284,7 +289,8 @@ bool read_PLY(std::istream& is, * \param is the input stream * \param points points of the soup of polygons * \param polygons a range of polygons. Each element in it describes a polygon - * using the indices of the points in `points`. + * using the indices of the points in `points` + * \param comments a string that will contain all the comments found in the PLY file * \param np optional \ref bgl_namedparameters "Named Parameters" described below * * \cgalNamedParamsBegin @@ -307,6 +313,7 @@ template ::value>* = nullptr @@ -319,7 +326,7 @@ bool read_PLY(std::istream& is, std::vector > dummy_pui; std::vector > dummy_pf; - return internal::read_PLY(is, points, polygons, std::back_inserter(dummy_pui), + return internal::read_PLY(is, points, polygons, comments, std::back_inserter(dummy_pui), choose_parameter(get_parameter(np, internal_np::face_color_output_iterator), CGAL::Emptyset_iterator()), choose_parameter(get_parameter(np, internal_np::vertex_color_output_iterator), @@ -328,6 +335,20 @@ bool read_PLY(std::istream& is, choose_parameter(get_parameter(np, internal_np::verbose), true)); } +template +bool read_PLY(std::istream& is, + PointRange& points, + PolygonRange& polygons, + const CGAL_NP_CLASS& np = parameters::default_values() +#ifndef DOXYGEN_RUNNING + , std::enable_if_t::value>* = nullptr +#endif + ) +{ + std::string unused_comments; + return read_PLY(is, points, polygons, unused_comments, np); +} + /*! * \ingroup PkgStreamSupportIoFuncsPLY * @@ -344,7 +365,8 @@ bool read_PLY(std::istream& is, * \param fname the path to the input file * \param points points of the soup of polygons * \param polygons a range of polygons. Each element in it describes a polygon - * using the indices of the points in `points`. + * using the indices of the points in `points` + * \param comments a string that will contain all the comments found in the PLY file * \param np optional \ref bgl_namedparameters "Named Parameters" described below * * \cgalNamedParamsBegin @@ -367,6 +389,7 @@ template ::value>* = nullptr @@ -378,16 +401,30 @@ bool read_PLY(const std::string& fname, { std::ifstream is(fname, std::ios::binary); CGAL::IO::set_mode(is, CGAL::IO::BINARY); - return read_PLY(is, points, polygons, np); + return read_PLY(is, points, polygons, comments, np); } else { std::ifstream is(fname); CGAL::IO::set_mode(is, CGAL::IO::ASCII); - return read_PLY(is, points, polygons, np); + return read_PLY(is, points, polygons, comments, np); } } +template +bool read_PLY(const std::string& fname, + PointRange& points, + PolygonRange& polygons, + const CGAL_NP_CLASS& np = parameters::default_values() +#ifndef DOXYGEN_RUNNING + , std::enable_if_t::value>* = nullptr +#endif + ) +{ + std::string unused_comments; + return read_PLY(fname, points, polygons, unused_comments, np); +} + //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// // Write diff --git a/Stream_support/include/CGAL/IO/PLY/PLY_writer.h b/Stream_support/include/CGAL/IO/PLY/PLY_writer.h index 126c12ab7739..0bd1df89d2d2 100644 --- a/Stream_support/include/CGAL/IO/PLY/PLY_writer.h +++ b/Stream_support/include/CGAL/IO/PLY/PLY_writer.h @@ -12,6 +12,7 @@ #define CGAL_IO_PLY_PLY_WRITER_H #include +#include namespace CGAL { namespace IO { diff --git a/Stream_support/include/CGAL/IO/STL.h b/Stream_support/include/CGAL/IO/STL.h index dfa3ecded7ea..5b4f2de052d2 100644 --- a/Stream_support/include/CGAL/IO/STL.h +++ b/Stream_support/include/CGAL/IO/STL.h @@ -317,7 +317,7 @@ bool write_STL(std::ostream& os, const Point& q = get(point_map, points[face[1]]); const Point& r = get(point_map, points[face[2]]); - const Vector_3 n = collinear(p,q,r) ? Vector_3(1,0,0) : unit_normal(p,q,r); + const Vector_3 n = internal::construct_normal_of_STL_face(p, q, r, k); const float coords[12] = { static_cast(n.x()), static_cast(n.y()), static_cast(n.z()), static_cast(p.x()), static_cast(p.y()), static_cast(p.z()), diff --git a/Stream_support/include/CGAL/IO/io.h b/Stream_support/include/CGAL/IO/io.h index 5669aeb05279..86041bfaec45 100644 --- a/Stream_support/include/CGAL/IO/io.h +++ b/Stream_support/include/CGAL/IO/io.h @@ -91,7 +91,7 @@ enum Mode {ASCII = 0, PRETTY, BINARY}; \brief Inserts object `c` in the stream `os`. Returns `os`. \cgal defines output operators for classes that are derived from the class `ostream`. This allows to write to ostreams -as `cout` or `cerr`, as well as to `std::ostringstream` +as `std::cout` or `std::cerr`, as well as to `std::ostringstream` and `std::ofstream`. The output operator is defined for all classes in the \cgal `Kernel` and for the class `Color` as well. @@ -233,8 +233,8 @@ class Output_rep \relates Output_rep \brief stream output of the \c Output_rep calls its \c operator(). - \cgal defines output operators for classes that are derived from the class `ostream`. - This enables to write to ostreams as `cout` or `cerr`, as well as to `std::ostringstream` + \cgal defines output operators for classes that are derived from the class `std::ostream`. + This enables to write to output streams as `std::cout` or `std::cerr`, as well as to `std::ostringstream` and `std::ofstream`. The output operator is defined for all classes in the \cgal `Kernel` and for the class `Color` as well. */ @@ -455,7 +455,7 @@ class Input_rep \brief stream input to the \c Input_rep calls its \c operator(). \brief \cgal defines input operators for classes that are derived -from the class `istream`. This allows to read from istreams +from the class `std::istream`. This allows to read from input streams as `std::cin`, as well as from `std::istringstream` and `std::ifstream`. The input operator is defined for all classes in the \cgal `Kernel`. */ diff --git a/Stream_support/test/Stream_support/issue_7874.cpp b/Stream_support/test/Stream_support/issue_7874.cpp new file mode 100644 index 000000000000..4700957c822f --- /dev/null +++ b/Stream_support/test/Stream_support/issue_7874.cpp @@ -0,0 +1,169 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// typedef CGAL::Exact_predicates_exact_constructions_kernel Epeck; +typedef CGAL::Simple_cartesian Epeck; +typedef CGAL::Surface_mesh Surface_mesh; +typedef boost::graph_traits::vertex_descriptor vertex_descriptor; +typedef CGAL::Polyhedron_3 Polyhedron_3; +typedef boost::graph_traits::vertex_descriptor pvertex_descriptor; +typedef Epeck::Point_3 Point_3; +typedef Epeck::Vector_3 Vector_3; +typedef CGAL::Simple_cartesian SC; +typedef Surface_mesh::template Property_map VNMap; +typedef CGAL::Cartesian_converter_property_map< SC::Vector_3, VNMap> ScSmVertexNormalMap; + +typedef std::map PolyhedronVN; +typedef boost::associative_property_map PolyhedronVNMap; +typedef CGAL::Cartesian_converter_property_map< SC::Vector_3, PolyhedronVNMap> ScPolyhedronVertexNormalMap; + +typedef boost::property_map::type SmVertexPointMap; +typedef CGAL::Cartesian_converter_property_map< SC::Point_3, SmVertexPointMap> ScSmVertexPointMap; + +typedef boost::property_map::type PolyhedronVertexPointMap; +typedef CGAL::Cartesian_converter_property_map< SC::Point_3, PolyhedronVertexPointMap> ScPolyhedronVertexPointMap; + +int main() +{ + Point_3 p(0.3, 0.3454, 0), q(0.6694, 0,0), r(0,0,0); + Surface_mesh sm; + Polyhedron_3 po; + CGAL::make_triangle(p,q,r,sm); + CGAL::make_triangle(p,q,r,po); + + VNMap smvnpm = sm.template add_property_map("v:normal", CGAL::NULL_VECTOR).first; + + CGAL::Polygon_mesh_processing::compute_vertex_normals(sm, smvnpm); + + ScSmVertexNormalMap scsmvnpm(smvnpm); + + SmVertexPointMap smvpm = get(CGAL::vertex_point_t(), sm); + ScSmVertexPointMap scsmvpm(smvpm); + PolyhedronVertexPointMap povpm = get(CGAL::vertex_point_t(), po); + ScPolyhedronVertexPointMap scpovpm(povpm); + + // from + // https://doc.cgal.org/latest/BGL/group__PkgBGLIoFuncsPLY.html#ga959dcd88ca979d3b6b0806d883a0247f + CGAL::IO::write_polygon_mesh("write_polygon_mesh_sm.ply", sm, + CGAL::parameters::stream_precision(17).use_binary_mode(true).vertex_point_map(scsmvpm).vertex_normal_map(scsmvnpm) ); + +{ + Surface_mesh osm; + + if(!CGAL::IO::read_polygon_mesh("write_polygon_mesh_sm.ply", osm)) + { + std::cerr << "Error: failed to read 'write_polygon_mesh_sm.ply'" << std::endl; + return EXIT_FAILURE; + }else{ + for(const std::string& s : osm.properties()){ + std::cout << s << std::endl; + } + } +} + +{ + PolyhedronVN vn; + PolyhedronVNMap povnpm(vn); + CGAL::Polygon_mesh_processing::compute_vertex_normals(po, povnpm); + + CGAL::IO::write_polygon_mesh("write_polygon_mesh_po.ply", po, + CGAL::parameters::stream_precision(17).use_binary_mode(true).vertex_point_map(povpm).vertex_normal_map(povnpm)) ; + if (!CGAL::IO::read_polygon_mesh("write_polygon_mesh_po.ply", po)) + { + std::cerr << "Error: failed to read 'write_polygon_mesh_po.ply'" << std::endl; + return EXIT_FAILURE; + } + + ScPolyhedronVertexNormalMap scpovnpm(povnpm); + CGAL::IO::write_polygon_mesh("write_polygon_mesh_po_sc.ply", po, + CGAL::parameters::stream_precision(17).use_binary_mode(true).vertex_point_map(scpovpm).vertex_normal_map(scpovnpm)) ; + if (!CGAL::IO::read_polygon_mesh("write_polygon_mesh_po_sc.ply", po)) + { + std::cerr << "Error: failed to read 'write_polygon_mesh_po_sc.ply'" << std::endl; + return EXIT_FAILURE; + } +} + +{ + Surface_mesh osm; + + if(!CGAL::IO::read_polygon_mesh("write_polygon_mesh_po.ply", osm)) + { + std::cerr << "Error: failed to read 'write_polygon_mesh_po.ply'" << std::endl; + return EXIT_FAILURE; + }else{ + for(const std::string& s : osm.properties()){ + std::cout << s << std::endl; + } + } +} + // OK + // from #include + // https://doc.cgal.org/latest/BGL/group__PkgBGLIoFuncsPLY.html#ga959dcd88ca979d3b6b0806d883a0247f + CGAL::IO::write_PLY("generic_write_PLY_sm.ply", sm, "generic write_PLY(Surface_mesh)", + CGAL::parameters::stream_precision(17).use_binary_mode(true).vertex_point_map(scsmvpm).vertex_normal_map(scsmvnpm)); + +{ + Surface_mesh osm; + + if(!CGAL::IO::read_polygon_mesh("generic_write_PLY_sm.ply", osm)) + { + std::cerr << "Error: failed to read 'generic_write_PLY_sm.ply'" << std::endl; + return EXIT_FAILURE; + }else{ + for(const std::string& s : osm.properties()){ + std::cout << s << std::endl; + } + } +} + + // ERROR this produces an invalid plyfile + CGAL::IO::write_PLY("generic_write_PLY_po.ply", po, "generic write_PLY(Polyhedron)", + CGAL::parameters::stream_precision(17).use_binary_mode(true).vertex_point_map(povpm)); + +{ + Surface_mesh osm; + + if(!CGAL::IO::read_polygon_mesh("generic_write_PLY_po.ply", osm)) + { + std::cerr << "Error: failed to read 'generic_write_PLY_po.ply'" << std::endl; + return EXIT_FAILURE; + }else{ + for(const std::string& s : osm.properties()){ + std::cout << s << std::endl; + } + } +} +{ + // OK + // from #include + // https://doc.cgal.org/latest/Surface_mesh/group__PkgSurfaceMeshIOFuncPLY.html#ga50f0e9f2b293855d2c7f1a62939cbe8d + std::ofstream out("overloaded_write_PLY_sm.ply", std::ios::binary); + CGAL::IO::set_binary_mode(out); + CGAL::IO::write_PLY(out, sm, "overloaded_write_PLY(Surface_mesh)",CGAL::parameters::stream_precision(17).use_binary_mode(true) + .vertex_point_map(scsmvpm).vertex_normal_map(scsmvnpm) ); +} +{ + Surface_mesh osm; + + if(!CGAL::IO::read_polygon_mesh("overloaded_write_PLY_sm.ply", osm)) + { + std::cerr << "Error: failed to read 'overloaded_write_PLY_sm.ply'" << std::endl; + return EXIT_FAILURE; + }else{ + for(const std::string& s : osm.properties()){ + std::cout << s << std::endl; + } + } +} + + return 0; +} diff --git a/Surface_mesh/include/CGAL/Surface_mesh/IO/PLY.h b/Surface_mesh/include/CGAL/Surface_mesh/IO/PLY.h index 0f0056a486a8..4eafcdbf1377 100644 --- a/Surface_mesh/include/CGAL/Surface_mesh/IO/PLY.h +++ b/Surface_mesh/include/CGAL/Surface_mesh/IO/PLY.h @@ -20,6 +20,7 @@ #include #include #include +#include #include @@ -420,44 +421,77 @@ class Surface_mesh_filler } }; -template +template bool fill_simplex_specific_header(std::ostream& os, const Surface_mesh& sm, std::vector::Vertex_index>*>& printers, - const std::string& prop) + const std::string& prop, + const NamedParameter& np) { + using CGAL::parameters::choose_parameter; + using CGAL::parameters::get_parameter; + typedef Surface_mesh SMesh; typedef typename SMesh::Vertex_index VIndex; typedef typename Kernel_traits::Kernel Kernel; typedef typename Kernel::FT FT; typedef typename Kernel::Vector_3 Vector; - typedef typename SMesh::template Property_map Point_map; + + typedef typename GetVertexPointMap::const_type Point_map; typedef typename SMesh::template Property_map Vector_map; typedef typename SMesh::template Property_map Vcolor_map; + Point_map vpm = choose_parameter(get_parameter(np, internal_np::vertex_point), + get_const_property_map(CGAL::vertex_point, sm)); if(prop == "v:connectivity" || prop == "v:removed") return true; if(prop == "v:point") { - if(std::is_same::value) + if constexpr(std::is_same::value) { os << "property float x" << std::endl << "property float y" << std::endl << "property float z" << std::endl; + printers.push_back(new Property_printer(vpm)); } else { os << "property double x" << std::endl << "property double y" << std::endl << "property double z" << std::endl; + auto fvpm = CGAL::make_cartesian_converter_property_map(vpm); + printers.push_back(new Property_printer(fvpm)); } - printers.push_back(new Property_printer(sm.points())); + return true; } - if(prop == "v:normal") + if constexpr (!parameters::is_default_parameter::value) + { + auto vnm = get_parameter(np, internal_np::vertex_normal_map); + typedef decltype(vnm) Normal_map; + typedef typename Kernel_traits::Kernel::FT FloatDouble; + if constexpr (std::is_same::value) + { + os << "property float nx" << std::endl + << "property float ny" << std::endl + << "property float nz" << std::endl; + printers.push_back(new Property_printer(vnm)); + } + else + { + os << "property double nx" << std::endl + << "property double ny" << std::endl + << "property double nz" << std::endl; + auto fvnm = CGAL::make_cartesian_converter_property_map(vnm); + printers.push_back(new Property_printer(fvnm)); + } + return true; + } + + else if(prop == "v:normal") { auto pmap = sm.template property_map(prop); if(pmap.has_value()) @@ -467,14 +501,16 @@ bool fill_simplex_specific_header(std::ostream& os, os << "property float nx" << std::endl << "property float ny" << std::endl << "property float nz" << std::endl; + printers.push_back(new Property_printer(*pmap)); } else { os << "property double nx" << std::endl << "property double ny" << std::endl << "property double nz" << std::endl; + auto fvnm = CGAL::make_cartesian_converter_property_map(*pmap); + printers.push_back(new Property_printer(fvnm)); } - printers.push_back(new Property_printer(*pmap)); return true; } } @@ -497,12 +533,13 @@ bool fill_simplex_specific_header(std::ostream& os, return false; } -template +template bool fill_simplex_specific_header(std::ostream& os, const Surface_mesh& sm, std::vector::Face_index>*>& printers, - const std::string& prop) + const std::string& prop, + const NamedParameter&) { typedef typename Surface_mesh::Face_index FIndex; typedef CGAL::IO::Color Color; @@ -528,12 +565,13 @@ bool fill_simplex_specific_header(std::ostream& os, return false; } -template +template bool fill_simplex_specific_header(std::ostream&, const Surface_mesh& , std::vector::Edge_index>*>& , - const std::string& prop) + const std::string& prop, + const NamedParameter&) { if(prop == "e:removed") return true; @@ -541,12 +579,13 @@ bool fill_simplex_specific_header(std::ostream&, return false; } -template +template bool fill_simplex_specific_header(std::ostream&, const Surface_mesh& , std::vector::Halfedge_index>*>& , - const std::string& prop) + const std::string& prop, + const NamedParameter&) { if(prop == "h:connectivity") return true; @@ -733,7 +772,7 @@ void fill_header(std::ostream& os, const Surface_mesh& sm, if (has_fcolor && prop[i] == "f:color") continue; - if(fill_simplex_specific_header(os, sm, printers, prop[i])) + if(fill_simplex_specific_header(os, sm, printers, prop[i], np)) continue; fill_header_impl::value>(Type_tuple(), type_strings, sm, prop[i], os, printers); @@ -773,21 +812,37 @@ void fill_header(std::ostream& os, const Surface_mesh& sm, /// \param comments a string 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). -/// \param verbose whether extra information is printed when an incident occurs during reading +/// \param np optional \ref bgl_namedparameters "Named Parameters" described below +/// +/// \cgalNamedParamsBegin +/// \cgalParamNBegin{verbose} +/// \cgalParamDescription{whether extra information is printed when an incident occurs during reading} +/// \cgalParamType{Boolean} +/// \cgalParamDefault{`false`} +/// \cgalParamNEnd +/// \cgalNamedParamsEnd /// /// \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. /// /// \returns `true` if reading was successful, `false` otherwise. /// -template +template bool read_PLY(std::istream& is, Surface_mesh

& sm, std::string& comments, - bool verbose = true) + const CGAL_NP_CLASS& np = parameters::default_values()) { typedef typename Surface_mesh

::size_type size_type; + // not yet supported: this function only uses the Surface_mesh's internal pmaps + // to make it work, it'd be sufficient to modify Surface_mesh_filler's maps + // static_assert(CGAL::parameters::is_default_parameter::value); + // static_assert(CGAL::parameters::is_default_parameter::value); + // static_assert(CGAL::parameters::is_default_parameter::value); + + const bool verbose = CGAL::parameters::choose_parameter(CGAL::parameters::get_parameter(np, internal_np::verbose), true); + if(!is.good()) { if(verbose) @@ -874,6 +929,18 @@ bool read_PLY(std::istream& is, /// \cond SKIP_IN_MANUAL +#ifndef CGAL_NO_DEPRECATED_CODE +// for backward compatibility +template +bool read_PLY(std::istream& is, + Surface_mesh

& sm, + std::string& comments, + bool verbose) +{ + return read_PLY(is, sm, comments, CGAL::parameters::verbose(verbose)); +} +#endif + template bool read_PLY(std::istream& is, Surface_mesh

& sm) { @@ -927,6 +994,7 @@ namespace IO { /// \cgalNamedParamsEnd /// /// \returns `true` if writing was successful, `false` otherwise. + template bool write_PLY(std::ostream& os, diff --git a/Surface_mesh/test/Surface_mesh/issue_7327.cpp b/Surface_mesh/test/Surface_mesh/issue_7327.cpp new file mode 100644 index 000000000000..c147a7c6a869 --- /dev/null +++ b/Surface_mesh/test/Surface_mesh/issue_7327.cpp @@ -0,0 +1,126 @@ +#include +#include + +#include +#include +#include + +typedef CGAL::Exact_predicates_exact_constructions_kernel Kernel; +typedef Kernel::Point_3 Point; + +typedef CGAL::Surface_mesh SMesh; +typedef boost::graph_traits::vertex_descriptor vertex_descriptor; +typedef boost::graph_traits::face_descriptor face_descriptor; + +int main() +{ + std::ifstream in(CGAL::data_file_path("meshes/colored_tetra.ply")); + SMesh mesh; + CGAL::IO::read_PLY(in, mesh); + + std::cerr << "Read mesh with " << mesh.number_of_vertices() << " vertices and " + << mesh.number_of_faces() << " faces" << std::endl; + + std::cerr << "Properties associated with vertices:" << std::endl; + std::vector properties = mesh.properties(); + for(std::size_t i = 0; i < properties.size(); ++ i) + std::cerr << " * " << properties[i] << std::endl; + + std::cerr << "Properties associated with faces:" << std::endl; + properties = mesh.properties(); + for(std::size_t i = 0; i < properties.size(); ++ i) + std::cerr << " * " << properties[i] << std::endl; + + mesh.add_property_map("id", 42); + mesh.add_property_map("u", 13.f); + mesh.add_property_map("v", 37.f); + + // Append second mesh + std::ifstream in2("epeck_tetra.ply"); + CGAL::IO::read_PLY(in2, mesh); + + std::ofstream out("epeck_out.ply"); +// CGAL::IO::set_binary_mode(out); + CGAL::IO::write_PLY(out, mesh); + + // extra test for read/write of properties + mesh = SMesh(); + in.close(); + in.open(CGAL::data_file_path("meshes/colored_tetra.ply")); + CGAL::IO::read_PLY(in, mesh); + float fvalue=1001; + auto v_uvmap = mesh.add_property_map>("v:uv").first; + auto v_umap = mesh.add_property_map("v:u").first; + auto v_vmap = mesh.add_property_map("v:v").first; + for (SMesh::Vertex_index v : vertices(mesh)) + { + v_uvmap[v]={fvalue, -fvalue}; + v_umap[v]=fvalue; + v_vmap[v]=-fvalue; + ++fvalue; + } + + double dvalue=2001; + auto f_uvmap = mesh.add_property_map>("f:uv").first; + auto f_umap = mesh.add_property_map("f:u").first; + auto f_vmap = mesh.add_property_map("f:v").first; + for (SMesh::Face_index f : faces(mesh)) + { + f_uvmap[f]={dvalue, -dvalue}; + f_umap[f]=dvalue; + f_vmap[f]=-dvalue; + ++dvalue; + } + + out.close(); + out.open("epeck_out_ascii.ply"); + CGAL::IO::write_PLY(out, mesh); + out.close(); + out.open("epeck_out_binary.ply", std::ios::binary); + CGAL::IO::set_binary_mode(out); + CGAL::IO::write_PLY(out, mesh); + out.close(); + mesh.clear(); + + const std::array fnames = {"epeck_out_ascii.ply", "epeck_out_binary.ply"}; + for (std::string fn : fnames) + { + std::cout << "Reading " << fn << "\n"; + in.close(); + in.open(fn, std::ios::binary); + SMesh mesh_bis; + CGAL::IO::read_PLY(in, mesh_bis); + + v_uvmap = mesh_bis.property_map>("v:uv").value(); + v_umap = mesh_bis.property_map("v:u").value(); + v_vmap = mesh_bis.property_map("v:v").value(); + + fvalue=1001; + for (SMesh::Vertex_index v : vertices(mesh_bis)) + { + assert(v_uvmap[v].size()==2); + assert(v_uvmap[v][0]==fvalue); + assert(v_uvmap[v][1]==-fvalue); + assert(v_umap[v]==fvalue); + assert(v_vmap[v]==-fvalue); + ++fvalue; + } + + f_uvmap = mesh_bis.property_map>("f:uv").value(); + f_umap = mesh_bis.property_map("f:u").value(); + f_vmap = mesh_bis.property_map("f:v").value(); + + dvalue=2001; + for (SMesh::Face_index f : faces(mesh_bis)) + { + assert(f_uvmap[f].size()==2); + assert(f_uvmap[f][0]==dvalue); + assert(f_uvmap[f][1]==-dvalue); + assert(f_umap[f]==dvalue); + assert(f_vmap[f]==-dvalue); + ++dvalue; + } + } + + return 0; +} diff --git a/Surface_mesh/test/Surface_mesh/sm_ply_io.cpp b/Surface_mesh/test/Surface_mesh/sm_ply_io.cpp index d982630c89bd..e0de7b67aa96 100644 --- a/Surface_mesh/test/Surface_mesh/sm_ply_io.cpp +++ b/Surface_mesh/test/Surface_mesh/sm_ply_io.cpp @@ -76,7 +76,7 @@ int main() out.open("out_ascii.ply"); CGAL::IO::write_PLY(out, mesh); out.close(); - out.open("out_binary.ply"); + out.open("out_binary.ply", std::ios::binary); CGAL::IO::set_binary_mode(out); CGAL::IO::write_PLY(out, mesh); out.close(); @@ -87,7 +87,7 @@ int main() { std::cout << "Reading " << fn << "\n"; in.close(); - in.open(fn); + in.open(fn, std::ios::binary); SMesh mesh_bis; CGAL::IO::read_PLY(in, mesh_bis);