From 6a5972cc3bc39b643aa890457f6658ee1a7be491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mael=20Rouxel-Labb=C3=A9?= Date: Thu, 23 Jan 2020 16:33:23 +0100 Subject: [PATCH] Continue reorganization of CGAL I/O --- BGL/include/CGAL/boost/graph/io.h | 17 +- .../doc/Stream_support/IOstream.txt | 13 +- .../doc/Stream_support/PackageDescription.txt | 13 +- Stream_support/include/CGAL/IO/3MF.h | 560 ++++++++ Stream_support/include/CGAL/IO/3MF/read_3mf.h | 406 ------ .../include/CGAL/IO/3MF/write_3mf.h | 124 +- .../include/CGAL/IO/Generic_writer.h | 2 +- Stream_support/include/CGAL/IO/OBJ.h | 71 +- .../include/CGAL/IO/OBJ/OBJ_reader.h | 88 -- Stream_support/include/CGAL/IO/PLY.h | 1262 +++++------------ .../include/CGAL/IO/PLY/PLY_reader.h | 913 ++++++++---- .../include/CGAL/IO/PLY/PLY_writer.h | 377 +++-- Stream_support/include/CGAL/IO/STL.h | 162 ++- .../include/CGAL/IO/STL/STL_reader.h | 85 +- .../include/CGAL/IO/STL/STL_writer.h | 78 - Stream_support/include/CGAL/IO/VRML.h | 1 - .../include/CGAL/IO/VTK/VTK_reader.h | 16 + .../include/CGAL/IO/VTK/VTK_writer.h | 95 ++ .../include/CGAL/IO/VTK/write_vtk.h | 31 - Stream_support/include/CGAL/IO/WKT.h | 6 +- .../include/CGAL/IO/WKT/traits_point_3.h | 2 +- Stream_support/include/CGAL/IO/io.h | 4 +- 22 files changed, 2220 insertions(+), 2106 deletions(-) delete mode 100644 Stream_support/include/CGAL/IO/OBJ/OBJ_reader.h delete mode 100644 Stream_support/include/CGAL/IO/STL/STL_writer.h create mode 100644 Stream_support/include/CGAL/IO/VTK/VTK_reader.h create mode 100644 Stream_support/include/CGAL/IO/VTK/VTK_writer.h delete mode 100644 Stream_support/include/CGAL/IO/VTK/write_vtk.h diff --git a/BGL/include/CGAL/boost/graph/io.h b/BGL/include/CGAL/boost/graph/io.h index 9226436acee..2eb5d55878f 100644 --- a/BGL/include/CGAL/boost/graph/io.h +++ b/BGL/include/CGAL/boost/graph/io.h @@ -1068,16 +1068,19 @@ public: /*! \ingroup PkgBGLIOFct + reads the graph `tm` from the stream `in` in the STL format. + \returns `true` if the resulting mesh is valid. + \pre The data must represent a 2-manifold + \see \ref IOStreamSTL */ template -bool -read_STL(TriangleMesh& tm, std::istream& in) +bool read_STL(std::istream& in, + TriangleMesh& tm) { - //typedef typename Polyhedron::HalfedgeDS HDS; typedef typename boost::property_traits::type>::value_type Point_3; STL_internal::STL_builder builder(in); @@ -1087,14 +1090,8 @@ read_STL(TriangleMesh& tm, std::istream& in) return ok; } +namespace OBJ_internal { - - - - - -namespace OBJ_internal -{ //Use CRTP to gain access to the protected members without getters/setters. template class OBJ_builder : public CGAL::internal::IO::Generic_facegraph_builder > diff --git a/Stream_support/doc/Stream_support/IOstream.txt b/Stream_support/doc/Stream_support/IOstream.txt index ed09e42f9c2..598d2504ce8 100644 --- a/Stream_support/doc/Stream_support/IOstream.txt +++ b/Stream_support/doc/Stream_support/IOstream.txt @@ -74,6 +74,7 @@ with some other restrictions. A more precise definition can be found \ref PMPDef The table above only lists the functions that work with any Polygon Mesh. More functions are available for more specific classes, and they can be found \link IOStreamSupportedFileFormats here\endlink. + \subsection IOstreamPolygonSoupIO Polygon Soup IO A polygon soup is a set of polygons with no global combinatorial information, stored in a two containers: one storing the points, and the other one storing @@ -85,24 +86,28 @@ their indices per face (i.e a vector of 3 integers represent a triangle face). \ref IOStreamOFF "OFF" \ref IOStreamSTL "STL" \ref IOStreamOBJ "OBJ" + \ref IOStreamPLY "PLY" ASCII ASCII BINARY + ASCII / BINARY Export write_OFF() - \ref IOstreamFunctions "write_STL()" - n/a + write_STL() + - + write_PLY() Import read_OFF() - \ref IOstreamFunctions "read_STL()" - \ref IOstreamFunctions "read_OBJ()" + read_STL() + read_OBJ() + read_PLY() \subsection IOstreamPointSetIO Point Set IO diff --git a/Stream_support/doc/Stream_support/PackageDescription.txt b/Stream_support/doc/Stream_support/PackageDescription.txt index 9d414cb39ba..48761b6a5a3 100644 --- a/Stream_support/doc/Stream_support/PackageDescription.txt +++ b/Stream_support/doc/Stream_support/PackageDescription.txt @@ -43,9 +43,18 @@ the printing mode. - \link IOstreamOperators `CGAL::operator<<()` \endlink - `CGAL::iformat()` - `CGAL::oformat()` -- `CGAL::read_STL()` -\cgalCRPSection{WKT I/O Functions} +\cgalCRPSection{File I/O Functions} +- `CGAL::read_STL()` +- `CGAL::write_STL()` +- `CGAL::read_PLY()` +- `CGAL::write_PLY()` +- `CGAL::read_OBJ()` +- `CGAL::read_OFF()` +- `CGAL::write_OFF()` +- `CGAL::read_VTK()` +- `CGAL::write_VTK()` + - `CGAL::read_point_WKT()` - `GCAL::read_multi_point_WKT()` - `GCAL::read_linestring_WKT()` diff --git a/Stream_support/include/CGAL/IO/3MF.h b/Stream_support/include/CGAL/IO/3MF.h index f04020dc7e8..28c2fa3b794 100644 --- a/Stream_support/include/CGAL/IO/3MF.h +++ b/Stream_support/include/CGAL/IO/3MF.h @@ -11,7 +11,567 @@ #ifndef CGAL_IO_3MF_H #define CGAL_IO_3MF_H +#include +#include #include #include +#include + +#include +#include +#include +#include + +namespace CGAL { + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +/// Read + +/*! + * \brief extracts ranges of points and triangles from a 3mf file. + * + * \tparam PointRanges a model of the concepts `RandomAccessContainer` and + * `BackInsertionSequence` whose `value type` is + * a model of the concepts `RandomAccessContainer` and `BackInsertionSequence` + * whose `value type` is the point type. + * \tparam PolygonRanges a model of the concept `RandomAccessContainer` whose + * `value_type` is a model of the concept `RandomAccessContainer` + * whose `value_type` is a model of the concept `RandomAccessContainer` whose + * `value_type` is std::size_t. + * \tparam ColorRanges a model of the concepts `RandomAccessContainer` and + * `BackInsertionSequence` whose `value type` is + * a model of the concepts `RandomAccessContainer` and `BackInsertionSequence` + * whose `value type` is `CGAL::Color`. + * + * \param file_name the name of the 3mf file to read. + * \param all_points a `PointRanges` that will contain the points of the meshes in `file_name`. + * Each of these meshes will add a range of its points. + * \param all_polygons a `PolygonRanges` that will contain the triangles of the meshes in `file_name`. + * Each of these meshes will add a range of its triangles. A `triangle` of + * `all_polygons[i]` contains the indices of its points in `all_points[i]`. + * \param all_colors will contain the color of each triangle for each soup. + * \param names will contain the name of each mesh in `file_name` if any. + * If the i'th mesh has no name, it will be called "Unknown Mesh" in names. + * + * \return the number of soups read. + */ +template +int read_triangle_soups_from_3mf(const std::string& file_name, + PointRanges& all_points, + PolygonRanges& all_polygons, + ColorRanges& all_colors, + std::vector& names) +{ + typedef typename PointRanges::value_type PointRange; + typedef typename PolygonRanges::value_type PolygonRange; + typedef typename ColorRanges::value_type ColorRange; + return read_from_3mf + (file_name, all_points, all_polygons, all_colors, names, + extract_soups); +} + +template +int read_polylines_from_3mf(const std::string& file_name, + PointRanges& all_points, + ColorRanges& all_colors, + std::vector& names) +{ + typedef typename PointRanges::value_type PointRange; + typedef std::vector Polygon; + typedef std::vector PolygonRange; + typedef std::vector ColorRange; + + std::vector all_polygons; + return read_from_3mf, + std::vector, PointRange, PolygonRange, ColorRange> + (file_name, all_points, all_polygons, all_colors, names, + extract_polylines); +} + +template +int read_point_clouds_from_3mf(const std::string& file_name, + PointRanges& all_points, + ColorRanges& all_colors, + std::vector& names) +{ + typedef typename PointRanges::value_type PointRange; + typedef std::vector Polygon; + typedef std::vector PolygonRange; + typedef std::vector ColorRange; + + std::vector all_polygons; + return read_from_3mf, + std::vector, PointRange, PolygonRange, ColorRange> + (file_name, all_points, all_polygons, all_colors, names, + extract_point_clouds); +} + +template +int read_from_3mf(const std::string& file_name, + PointRanges& all_points, + PolygonRanges& all_polygons, + ColorRanges& all_colors, + std::vector& names, + std::function func) +{ + DWORD nInterfaceVersionMajor, nInterfaceVersionMinor, nInterfaceVersionMicro, nbVertices, nbPolygons; + HRESULT hResult; + NMR::PLib3MFModel * pModel; + NMR::PLib3MFModelReader * pReader; + + // Extract Extension of filename + std::string sReaderName("3mf"); + + hResult = NMR::lib3mf_getinterfaceversion(&nInterfaceVersionMajor, &nInterfaceVersionMinor, &nInterfaceVersionMicro); + if(hResult != LIB3MF_OK) + { + std::cerr << "could not get 3MF Library version: " << std::hex << hResult << std::endl; + return -1; + } + + // Create Model Instance + hResult = NMR::lib3mf_createmodel(&pModel); + if(hResult != LIB3MF_OK) + { + std::cerr << "could not create model: " << std::hex << hResult << std::endl; + return -1; + } + + // Create Model Reader + hResult = NMR::lib3mf_model_queryreader(pModel, sReaderName.c_str(), &pReader); + if(hResult != LIB3MF_OK) + { + std::cerr << "could not create model reader: " << std::hex << hResult << std::endl; + NMR::lib3mf_release(pModel); + return -1; + } + + // Import Model from File + hResult = NMR::lib3mf_reader_readfromfileutf8(pReader, file_name.c_str()); + if(hResult != LIB3MF_OK) + { + std::cerr << "could not parse file: " << std::hex << hResult << std::endl; + NMR::lib3mf_release(pReader); + NMR::lib3mf_release(pModel); + return -1; + } + + // Release Model Reader + NMR::lib3mf_release(pReader); + + //Iterate Model + BOOL pbHasNext; + NMR::PLib3MFModelResourceIterator * pResourceIterator; + + hResult = NMR::lib3mf_model_getobjects(pModel, &pResourceIterator); + if(hResult != LIB3MF_OK) + { + std::cerr << "could not get object: " << std::hex << hResult << std::endl; + NMR::lib3mf_release(pModel); + return -1; + } + + hResult = NMR::lib3mf_resourceiterator_movenext(pResourceIterator, &pbHasNext); + if(hResult != LIB3MF_OK) + { + std::cerr << "could not get next object: " << std::hex << hResult << std::endl; + NMR::lib3mf_release(pResourceIterator); + NMR::lib3mf_release(pModel); + return -1; + } + + /************************************************** + **** Iterate Resources To Find Meshes ************ + **************************************************/ + + while (pbHasNext) + { + NMR::PLib3MFModelResource * pResource; + NMR::PLib3MFModelMeshObject * pMeshObject; + NMR::PLib3MFModelComponentsObject * pComponentsObject; + NMR::ModelResourceID ResourceID; + + // get current resource + hResult = NMR::lib3mf_resourceiterator_getcurrent(pResourceIterator, &pResource); + if(hResult != LIB3MF_OK) + { + std::cerr << "could not get resource: " << std::hex << hResult << std::endl; + NMR::lib3mf_release(pResourceIterator); + NMR::lib3mf_release(pModel); + return -1; + } + + // get resource ID + hResult = NMR::lib3mf_resource_getresourceid(pResource, &ResourceID); + if(hResult != LIB3MF_OK) + { + std::cerr << "could not get resource id: " << std::hex << hResult << std::endl; + NMR::lib3mf_release(pResource); + NMR::lib3mf_release(pResourceIterator); + NMR::lib3mf_release(pModel); + return -1; + } + + // Query mesh interface + BOOL bIsMeshObject; + hResult = NMR::lib3mf_object_ismeshobject(pResource, &bIsMeshObject); + if(hResult == LIB3MF_OK) + { + if(bIsMeshObject) + { + //skip it. Only get it through the components and buildItems. + } + else + { + BOOL bIsComponentsObject; + hResult = NMR::lib3mf_object_iscomponentsobject(pResource, &bIsComponentsObject); + if((hResult == LIB3MF_OK) && (bIsComponentsObject)) + { + pComponentsObject = (NMR::PLib3MFModelComponentsObject*)pResource; + DWORD nComponentCount; + hResult = NMR::lib3mf_componentsobject_getcomponentcount(pComponentsObject, &nComponentCount); + if(hResult != LIB3MF_OK) + return -1; + + //for each component + DWORD nIndex; + for(nIndex = 0; nIndex < nComponentCount; ++nIndex) + { + NMR::PLib3MFModelResource * compResource; + NMR::PLib3MFModelComponent * pComponent; + hResult = NMR::lib3mf_componentsobject_getcomponent(pComponentsObject, nIndex, &pComponent); + if(hResult != LIB3MF_OK) + return -1; + + hResult = NMR::lib3mf_component_getobjectresource(pComponent, &compResource); + if(hResult != LIB3MF_OK) + { + NMR::lib3mf_release(pComponent); + return -1; + } + + hResult = NMR::lib3mf_object_ismeshobject(compResource, &bIsMeshObject); + if(hResult == LIB3MF_OK) + { + if(bIsMeshObject) + { + BOOL bHasTransform; + NMR::MODELTRANSFORM Transform; + hResult = NMR::lib3mf_component_hastransform(pComponent, &bHasTransform); + if(hResult != LIB3MF_OK) + { + NMR::lib3mf_release(pComponent); + return -1; + } + + if(bHasTransform) + { + // Retrieve Transform + hResult = NMR::lib3mf_component_gettransform(pComponent, &Transform); + if(hResult != LIB3MF_OK) + { + NMR::lib3mf_release(pComponent); + return -1; + } + } + else + { + Transform = transform_nmr_internal::initMatrix(); + } + + pMeshObject = compResource; + NMR::lib3mf_meshobject_getvertexcount(pMeshObject, &nbVertices); + NMR::lib3mf_meshobject_gettrianglecount(pMeshObject, &nbPolygons); + PointRange points (nbVertices); + PolygonRange triangles(nbPolygons); + ColorRange colors(nbPolygons); + std::string name; + + if(func(pMeshObject, Transform, points, triangles, colors, name)) + { + all_points.push_back(points); + all_polygons.push_back(triangles); + all_colors.push_back(colors); + names.push_back(name); + } + } + } + } + //end component + } + } + } + + // free instances + NMR::lib3mf_release(pResource); + hResult = NMR::lib3mf_resourceiterator_movenext(pResourceIterator, &pbHasNext); + if(hResult != LIB3MF_OK) + { + std::cerr << "could not get next object: " << std::hex << hResult << std::endl; + return -1; + } + } + + /******************************************************** + **** Iterate Build items To Find Transformed Meshes **** + ********************************************************/ + + // Iterate through all the Build items + NMR::PLib3MFModelBuildItemIterator * pBuildItemIterator; + hResult = NMR::lib3mf_model_getbuilditems(pModel, &pBuildItemIterator); + if(hResult != LIB3MF_OK) + { + std::cout << "could not get build items: " << std::hex << hResult << std::endl; + NMR::lib3mf_release(pBuildItemIterator); + NMR::lib3mf_release(pModel); + return -1; + } + + hResult = NMR::lib3mf_builditemiterator_movenext(pBuildItemIterator, &pbHasNext); + if(hResult != LIB3MF_OK) + { + std::cout << "could not get next build item: " << std::hex << hResult << std::endl; + NMR::lib3mf_release(pBuildItemIterator); + NMR::lib3mf_release(pModel); + return -1; + } + + while (pbHasNext) + { + NMR::PLib3MFModelMeshObject * pMeshObject; + NMR::MODELTRANSFORM Transform; + NMR::PLib3MFModelBuildItem * pBuildItem; + // Retrieve Build Item + hResult = NMR::lib3mf_builditemiterator_getcurrent(pBuildItemIterator, &pBuildItem); + if(hResult != LIB3MF_OK) + { + std::cout << "could not get build item: " << std::hex << hResult << std::endl; + NMR::lib3mf_release(pBuildItemIterator); + NMR::lib3mf_release(pModel); + return -1; + } + + // Retrieve Resource + NMR::PLib3MFModelObjectResource * pObjectResource; + hResult = NMR::lib3mf_builditem_getobjectresource(pBuildItem, &pObjectResource); + if(hResult != LIB3MF_OK) + { + std::cout << "could not get build item resource: " << std::hex << hResult << std::endl; + NMR::lib3mf_release(pBuildItem); + NMR::lib3mf_release(pBuildItemIterator); + NMR::lib3mf_release(pModel); + return -1; + } + + BOOL bIsMeshObject; + hResult = NMR::lib3mf_object_ismeshobject(pObjectResource, &bIsMeshObject); + if(hResult == LIB3MF_OK) + { + if(bIsMeshObject) + { + pMeshObject = pObjectResource; + NMR::lib3mf_meshobject_getvertexcount(pMeshObject, &nbVertices); + NMR::lib3mf_meshobject_gettrianglecount(pMeshObject, &nbPolygons); + PointRange points (nbVertices); + PolygonRange triangles(nbPolygons); + ColorRange colors(nbPolygons); + std::string name; + + // Check Object Transform + BOOL bHasTransform; + hResult = NMR::lib3mf_builditem_hasobjecttransform(pBuildItem, &bHasTransform); + if(hResult != LIB3MF_OK) + { + NMR::lib3mf_release(pBuildItem); + NMR::lib3mf_release(pBuildItemIterator); + NMR::lib3mf_release(pModel); + std::cerr << "could not check object transform: " << std::hex << hResult << std::endl; + return -1; + } + + if(bHasTransform) + { + // Retrieve Transform + hResult = NMR::lib3mf_builditem_getobjecttransform(pBuildItem, &Transform); + if(hResult != LIB3MF_OK) + { + NMR::lib3mf_release(pBuildItem); + NMR::lib3mf_release(pBuildItemIterator); + NMR::lib3mf_release(pModel); + std::cerr << "could not get object transform: " << std::hex << hResult << std::endl; + return -1; + } + } + else + { + Transform = transform_nmr_internal::initMatrix(); + } + + if(func(pMeshObject, Transform, points, triangles, colors, name)){ + all_points.push_back(points); + all_polygons.push_back(triangles); + all_colors.push_back(colors); + names.push_back(name); + } + } + } + + // Release Object Resource ID + NMR::lib3mf_release(pObjectResource); + // Release Build Item + NMR::lib3mf_release(pBuildItem); + + // Move to next Item + hResult = NMR::lib3mf_builditemiterator_movenext(pBuildItemIterator, &pbHasNext); + if(hResult != LIB3MF_OK) + { + std::cerr << "could not get next build item: " << std::hex << hResult << std::endl; + return -1; + } + } + + // Release Build Item Iterator + NMR::lib3mf_release(pBuildItemIterator); + return all_points.size(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +/// Write + +/*! + * \brief writes the triangle soups contained in `all_points` and + * `all_polygons` into the 3mf file `file_name`. + * + * \tparam PointRanges a model of the concepts `RandomAccessContainer` and + * `BackInsertionSequence` whose `value type` is + * a model of the concepts `RandomAccessContainer` and `BackInsertionSequence` + * whose `value type` is the point type. + * \tparam PolygonRanges a model of the concept `RandomAccessContainer` whose + * `value_type` is a model of the concept `RandomAccessContainer` + * whose `value_type` is a model of the concept `RandomAccessContainer` whose + * `value_type` is std::size_t. + * + * \param file_name the name of the 3mf file to write. + * \param all_points a `PointRanges` that contains the points of the soups to write. + * \param all_polygons a `PolygonRanges` that contains the triangles of the soups in `file_name`. + * \param names will contains the name of each mesh in `file_name`. + * + * \return `true` if the writing is successful, `false` otherwise. + */ +template +bool write_triangle_soups_to_3mf(const std::string& file_name, + const PointRanges& all_points, + const PolygonRanges& all_polygons, + const std::vector& names) +{ + // Create Model Instance + NMR::PLib3MFModel * pModel; + HRESULT hResult = NMR::lib3mf_createmodel(&pModel); + if(hResult != LIB3MF_OK) + { + std::cerr << "could not create model: " << std::hex << hResult << std::endl; + return false; + } + + for(std::size_t id = 0; id < all_points.size(); ++id) + { + NMR::PLib3MFModelMeshObject* pMeshObject; + std::string name; + if(names.size() > id && ! names[id].empty()) + { + name=names[id]; + } + else + { + name = std::string(""); + } + + std::vector colors(all_polygons[id].size()); + write_mesh_to_model(all_points[id], all_polygons[id], colors, name, &pMeshObject, pModel); + } + + return export_model_to_file(file_name, pModel); +} + + +/*! + * \brief writes the triangle meshes contained in `tms` into the 3mf file `file_name`. + * + * \tparam TriangleMeshRange a model of the concepts `RandomAccessContainer` + * and `BackInsertionSequence` whose `value type` is + * a model of the concepts `FaceListGraph` and `HalfedgeListGraph` + * that has only triangle faces. + * + * \param file_name the name of the 3mf file to write. + * \param tms a `TriangleMeshRange` that contains the meshes + * to write. An internal property map for `CGAL::vertex_point_t` + * must be available for each mesh. + * \param names will contains the name of each mesh in `file_name`. + * + * \return `true` if the writing is successful, `false` otherwise. + */ +template +bool write_triangle_meshes_to_3mf(const std::string& file_name, + const TriangleMeshRange& tms, + const std::vector& names) +{ + typedef typename TriangleMeshRange::value_type Mesh; + typedef typename boost::property_map::type VPMap; + typedef typename boost::property_traits::value_type Point_3; + + typedef boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef boost::graph_traits::face_descriptor face_descriptor; + + typedef std::vector Polygon; + typedef std::vector PolygonRange; + typedef std::vector PointRange; + + std::vector all_points; + std::vector all_polygons; + + for(const auto& tm : tms) + { + PointRange points; + points.reserve(num_vertices(tm)); + PolygonRange triangles; + triangles.reserve(num_faces(tm)); + + VPMap vpm = get(boost::vertex_point, tm); + std::unordered_map::vertex_descriptor, std::size_t> vertex_id_map; + + std::size_t i = 0; + for(vertex_descriptor v : vertices(tm)) + { + points.push_back(get(vpm, v)); + vertex_id_map[v] = i++; + } + + all_points.push_back(points); + for(face_descriptor f : faces(tm)) + { + Polygon triangle; + for(vertex_descriptor vert : CGAL::vertices_around_face(halfedge(f, tm), tm)) + triangle.push_back(vertex_id_map[vert]); + + triangles.push_back(triangle); + } + + all_polygons.push_back(triangles); + } + + return write_triangle_soups_to_3mf(file_name, all_points, all_polygons, names); +} + +} // namespace CGAL + #endif // CGAL_IO_3MF_H diff --git a/Stream_support/include/CGAL/IO/3MF/read_3mf.h b/Stream_support/include/CGAL/IO/3MF/read_3mf.h index 7c1dae4b040..13b88be5878 100644 --- a/Stream_support/include/CGAL/IO/3MF/read_3mf.h +++ b/Stream_support/include/CGAL/IO/3MF/read_3mf.h @@ -285,412 +285,6 @@ bool extract_point_clouds (NMR::PLib3MFModelMeshObject *pMeshObject, return true; } - -template -int read_from_3mf(const std::string& file_name, PointRanges& all_points, - PolygonRanges& all_polygons, ColorRanges& all_colors, - std::vector& names, - std::function func) -{ - DWORD nInterfaceVersionMajor, nInterfaceVersionMinor, nInterfaceVersionMicro, nbVertices, nbPolygons; - HRESULT hResult; - NMR::PLib3MFModel * pModel; - NMR::PLib3MFModelReader * pReader; - - // Extract Extension of filename - std::string sReaderName("3mf"); - - hResult = NMR::lib3mf_getinterfaceversion(&nInterfaceVersionMajor, &nInterfaceVersionMinor, &nInterfaceVersionMicro); - if(hResult != LIB3MF_OK) - { - std::cerr << "could not get 3MF Library version: " << std::hex << hResult << std::endl; - return -1; - } - - // Create Model Instance - hResult = NMR::lib3mf_createmodel(&pModel); - if(hResult != LIB3MF_OK) { - std::cerr << "could not create model: " << std::hex << hResult << std::endl; - return -1; - } - - // Create Model Reader - hResult = NMR::lib3mf_model_queryreader(pModel, sReaderName.c_str(), &pReader); - if(hResult != LIB3MF_OK) - { - std::cerr << "could not create model reader: " << std::hex << hResult << std::endl; - NMR::lib3mf_release(pModel); - return -1; - } - - // Import Model from File - hResult = NMR::lib3mf_reader_readfromfileutf8(pReader, file_name.c_str()); - if(hResult != LIB3MF_OK) - { - std::cerr << "could not parse file: " << std::hex << hResult << std::endl; - NMR::lib3mf_release(pReader); - NMR::lib3mf_release(pModel); - return -1; - } - - // Release Model Reader - NMR::lib3mf_release(pReader); - - //Iterate Model - - BOOL pbHasNext; - NMR::PLib3MFModelResourceIterator * pResourceIterator; - - hResult = NMR::lib3mf_model_getobjects(pModel, &pResourceIterator); - if(hResult != LIB3MF_OK) - { - std::cerr << "could not get object: " << std::hex << hResult << std::endl; - NMR::lib3mf_release(pModel); - return -1; - } - - hResult = NMR::lib3mf_resourceiterator_movenext(pResourceIterator, &pbHasNext); - if(hResult != LIB3MF_OK) - { - std::cerr << "could not get next object: " << std::hex << hResult << std::endl; - NMR::lib3mf_release(pResourceIterator); - NMR::lib3mf_release(pModel); - return -1; - } - - /************************************************** - **** Iterate Resources To Find Meshes ************ - **************************************************/ - - while (pbHasNext) { - NMR::PLib3MFModelResource * pResource; - NMR::PLib3MFModelMeshObject * pMeshObject; - NMR::PLib3MFModelComponentsObject * pComponentsObject; - NMR::ModelResourceID ResourceID; - - // get current resource - hResult = NMR::lib3mf_resourceiterator_getcurrent(pResourceIterator, &pResource); - if(hResult != LIB3MF_OK) - { - std::cerr << "could not get resource: " << std::hex << hResult << std::endl; - NMR::lib3mf_release(pResourceIterator); - NMR::lib3mf_release(pModel); - return -1; - } - - // get resource ID - hResult = NMR::lib3mf_resource_getresourceid(pResource, &ResourceID); - if(hResult != LIB3MF_OK) - { - std::cerr << "could not get resource id: " << std::hex << hResult << std::endl; - NMR::lib3mf_release(pResource); - NMR::lib3mf_release(pResourceIterator); - NMR::lib3mf_release(pModel); - return -1; - } - - // Query mesh interface - BOOL bIsMeshObject; - hResult = NMR::lib3mf_object_ismeshobject(pResource, &bIsMeshObject); - if(hResult == LIB3MF_OK) - { - if(bIsMeshObject) - { - //skip it. Only get it through the components and buildItems. - } - else - { - BOOL bIsComponentsObject; - hResult = NMR::lib3mf_object_iscomponentsobject(pResource, &bIsComponentsObject); - if((hResult == LIB3MF_OK) && (bIsComponentsObject)) { - pComponentsObject = (NMR::PLib3MFModelComponentsObject*)pResource; - DWORD nComponentCount; - hResult = NMR::lib3mf_componentsobject_getcomponentcount(pComponentsObject, &nComponentCount); - if(hResult != LIB3MF_OK) - return -1; - //for each component - DWORD nIndex; - for(nIndex = 0; nIndex < nComponentCount; ++nIndex) { - NMR::PLib3MFModelResource * compResource; - NMR::PLib3MFModelComponent * pComponent; - hResult = NMR::lib3mf_componentsobject_getcomponent(pComponentsObject, nIndex, &pComponent); - if(hResult != LIB3MF_OK) - return -1; - - hResult = NMR::lib3mf_component_getobjectresource(pComponent, &compResource); - if(hResult != LIB3MF_OK) - { - NMR::lib3mf_release(pComponent); - return -1; - } - - hResult = NMR::lib3mf_object_ismeshobject(compResource, &bIsMeshObject); - if(hResult == LIB3MF_OK) - { - if(bIsMeshObject) - { - BOOL bHasTransform; - NMR::MODELTRANSFORM Transform; - hResult = NMR::lib3mf_component_hastransform(pComponent, &bHasTransform); - if(hResult != LIB3MF_OK) - { - NMR::lib3mf_release(pComponent); - return -1; - } - - if(bHasTransform) - { - // Retrieve Transform - hResult = NMR::lib3mf_component_gettransform(pComponent, &Transform); - if(hResult != LIB3MF_OK) - { - NMR::lib3mf_release(pComponent); - return -1; - } - } - else - { - Transform = transform_nmr_internal::initMatrix(); - } - - pMeshObject = compResource; - NMR::lib3mf_meshobject_getvertexcount(pMeshObject, &nbVertices); - NMR::lib3mf_meshobject_gettrianglecount(pMeshObject, &nbPolygons); - PointRange points (nbVertices); - PolygonRange triangles(nbPolygons); - ColorRange colors(nbPolygons); - std::string name; - - if(func(pMeshObject, Transform, points, triangles, colors, name)) - { - all_points.push_back(points); - all_polygons.push_back(triangles); - all_colors.push_back(colors); - names.push_back(name); - } - } - } - } - //end component - } - } - } - - // free instances - NMR::lib3mf_release(pResource); - hResult = NMR::lib3mf_resourceiterator_movenext(pResourceIterator, &pbHasNext); - if(hResult != LIB3MF_OK) - { - std::cerr << "could not get next object: " << std::hex << hResult << std::endl; - return -1; - } - } - - /******************************************************** - **** Iterate Build items To Find Transformed Meshes **** - ********************************************************/ - - // Iterate through all the Build items - NMR::PLib3MFModelBuildItemIterator * pBuildItemIterator; - hResult = NMR::lib3mf_model_getbuilditems(pModel, &pBuildItemIterator); - if(hResult != LIB3MF_OK) - { - std::cout << "could not get build items: " << std::hex << hResult << std::endl; - NMR::lib3mf_release(pBuildItemIterator); - NMR::lib3mf_release(pModel); - return -1; - } - - hResult = NMR::lib3mf_builditemiterator_movenext(pBuildItemIterator, &pbHasNext); - if(hResult != LIB3MF_OK) - { - std::cout << "could not get next build item: " << std::hex << hResult << std::endl; - NMR::lib3mf_release(pBuildItemIterator); - NMR::lib3mf_release(pModel); - return -1; - } - - while (pbHasNext) - { - NMR::PLib3MFModelMeshObject * pMeshObject; - NMR::MODELTRANSFORM Transform; - NMR::PLib3MFModelBuildItem * pBuildItem; - // Retrieve Build Item - hResult = NMR::lib3mf_builditemiterator_getcurrent(pBuildItemIterator, &pBuildItem); - if(hResult != LIB3MF_OK) - { - std::cout << "could not get build item: " << std::hex << hResult << std::endl; - NMR::lib3mf_release(pBuildItemIterator); - NMR::lib3mf_release(pModel); - return -1; - } - - // Retrieve Resource - NMR::PLib3MFModelObjectResource * pObjectResource; - hResult = NMR::lib3mf_builditem_getobjectresource(pBuildItem, &pObjectResource); - if(hResult != LIB3MF_OK) - { - std::cout << "could not get build item resource: " << std::hex << hResult << std::endl; - NMR::lib3mf_release(pBuildItem); - NMR::lib3mf_release(pBuildItemIterator); - NMR::lib3mf_release(pModel); - return -1; - } - - BOOL bIsMeshObject; - hResult = NMR::lib3mf_object_ismeshobject(pObjectResource, &bIsMeshObject); - if(hResult == LIB3MF_OK) - { - if(bIsMeshObject) - { - pMeshObject = pObjectResource; - NMR::lib3mf_meshobject_getvertexcount(pMeshObject, &nbVertices); - NMR::lib3mf_meshobject_gettrianglecount(pMeshObject, &nbPolygons); - PointRange points (nbVertices); - PolygonRange triangles(nbPolygons); - ColorRange colors(nbPolygons); - std::string name; - - // Check Object Transform - BOOL bHasTransform; - hResult = NMR::lib3mf_builditem_hasobjecttransform(pBuildItem, &bHasTransform); - if(hResult != LIB3MF_OK) - { - NMR::lib3mf_release(pBuildItem); - NMR::lib3mf_release(pBuildItemIterator); - NMR::lib3mf_release(pModel); - std::cerr << "could not check object transform: " << std::hex << hResult << std::endl; - return -1; - } - - if(bHasTransform) - { - // Retrieve Transform - hResult = NMR::lib3mf_builditem_getobjecttransform(pBuildItem, &Transform); - if(hResult != LIB3MF_OK) { - NMR::lib3mf_release(pBuildItem); - NMR::lib3mf_release(pBuildItemIterator); - NMR::lib3mf_release(pModel); - std::cerr << "could not get object transform: " << std::hex << hResult << std::endl; - return -1; - } - } - else - { - Transform = transform_nmr_internal::initMatrix(); - } - - if(func(pMeshObject, Transform, points, triangles, colors, name)){ - all_points.push_back(points); - all_polygons.push_back(triangles); - all_colors.push_back(colors); - names.push_back(name); - } - } - } - - // Release Object Resource ID - NMR::lib3mf_release(pObjectResource); - // Release Build Item - NMR::lib3mf_release(pBuildItem); - - // Move to next Item - hResult = NMR::lib3mf_builditemiterator_movenext(pBuildItemIterator, &pbHasNext); - if(hResult != LIB3MF_OK) - { - std::cerr << "could not get next build item: " << std::hex << hResult << std::endl; - return -1; - } - } - - // Release Build Item Iterator - NMR::lib3mf_release(pBuildItemIterator); - return all_points.size(); -} - -/*! - * \brief extracts ranges of points and triangles from a 3mf file. - * \tparam PointRanges a model of the concepts `RandomAccessContainer` and - * `BackInsertionSequence` whose `value type` is - * a model of the concepts `RandomAccessContainer` and `BackInsertionSequence` - * whose `value type` is the point type. - * \tparam PolygonRanges a model of the concept `RandomAccessContainer` whose - * `value_type` is a model of the concept `RandomAccessContainer` - * whose `value_type` is a model of the concept `RandomAccessContainer` whose - * `value_type` is std::size_t. - * \tparam ColorRanges a model of the concepts `RandomAccessContainer` and - * `BackInsertionSequence` whose `value type` is - * a model of the concepts `RandomAccessContainer` and `BackInsertionSequence` - * whose `value type` is `CGAL::Color`. - * \param file_name the name of the 3mf file to read. - * \param all_points a `PointRanges` that will contain the points of the meshes - * in `file_name`. - * Each of these meshes will add a range of its points. - * \param all_polygons a `PolygonRanges` that will contain the triangles of the - * meshes in `file_name`. - * Each of these meshes will add a range of its triangles. A `triangle` of - * `all_polygons[i]` contains the indices of its points in `all_points[i]`. - * \param all_colors will contain the color of each triangle for each soup. - * \param names will contain the name of each mesh in `file_name` if any. - * If the i'th mesh has no name, it will be called "Unknown Mesh" in names. - * \return the number of soups read. - */ -template -int read_triangle_soups_from_3mf(const std::string& file_name, PointRanges& all_points, - PolygonRanges& all_polygons, ColorRanges& all_colors, - std::vector& names) -{ - typedef typename PointRanges::value_type PointRange; - typedef typename PolygonRanges::value_type PolygonRange; - typedef typename ColorRanges::value_type ColorRange; - return read_from_3mf - (file_name, all_points, all_polygons, all_colors, names, - extract_soups); -} - -template -int read_polylines_from_3mf(const std::string& file_name, - PointRanges& all_points, - ColorRanges& all_colors, - std::vector& names) -{ - typedef typename PointRanges::value_type PointRange; - typedef std::vector Polygon; - typedef std::vector PolygonRange; - typedef std::vector ColorRange; - - std::vector all_polygons; - return read_from_3mf, - std::vector, PointRange, PolygonRange, ColorRange> - (file_name, all_points, all_polygons, all_colors, names, - extract_polylines); -} - -template -int read_point_clouds_from_3mf(const std::string& file_name, - PointRanges& all_points, - ColorRanges& all_colors, - std::vector& names) -{ - typedef typename PointRanges::value_type PointRange; - typedef std::vector Polygon; - typedef std::vector PolygonRange; - typedef std::vector ColorRange; - - std::vector all_polygons; - return read_from_3mf, - std::vector, PointRange, PolygonRange, ColorRange> - (file_name, all_points, all_polygons, all_colors, names, - extract_point_clouds); -} - } // namespace CGAL #endif // CGAL_IO_READ_3MF_H diff --git a/Stream_support/include/CGAL/IO/3MF/write_3mf.h b/Stream_support/include/CGAL/IO/3MF/write_3mf.h index 49cdb583796..bcf10ebde29 100644 --- a/Stream_support/include/CGAL/IO/3MF/write_3mf.h +++ b/Stream_support/include/CGAL/IO/3MF/write_3mf.h @@ -56,6 +56,8 @@ NMR::MODELMESHCOLOR_SRGB fnCreateColor(unsigned char red, unsigned char green, } // namespace tmf_internal +namespace 3MF { + bool add_build_item(NMR::PLib3MFModel * pModel, NMR::PLib3MFModelMeshObject* pMeshObject) { @@ -352,127 +354,7 @@ bool write_polyline_to_model(const PointRange& points, return write_points(points, color, pc_name, pMeshObject, pModel); } -/*! - * \brief writes the triangle soups contained in `all_points` and - * `all_polygons` into the 3mf file `file_name`. - * \tparam PointRanges a model of the concepts `RandomAccessContainer` and - * `BackInsertionSequence` whose `value type` is - * a model of the concepts `RandomAccessContainer` and `BackInsertionSequence` - * whose `value type` is the point type. - * \tparam PolygonRanges a model of the concept `RandomAccessContainer` whose - * `value_type` is a model of the concept `RandomAccessContainer` - * whose `value_type` is a model of the concept `RandomAccessContainer` whose - * `value_type` is std::size_t. - * \param file_name the name of the 3mf file to write. - * \param all_points a `PointRanges` that contains the points of the soups - * to write. - * \param all_polygons a `PolygonRanges` that contains the triangles of the - * soups in `file_name`. - * \param names will contains the name of each mesh in `file_name`. - * \return `true` if the writing is successful, `false` otherwise. - */ -template -bool write_triangle_soups_to_3mf(const std::string& file_name, - const PointRanges& all_points, - const PolygonRanges& all_polygons, - const std::vector& names) -{ - // Create Model Instance - NMR::PLib3MFModel * pModel; - HRESULT hResult = NMR::lib3mf_createmodel(&pModel); - if(hResult != LIB3MF_OK) - { - std::cerr << "could not create model: " << std::hex << hResult << std::endl; - return false; - } - - for(std::size_t id = 0; id < all_points.size(); ++id) - { - NMR::PLib3MFModelMeshObject* pMeshObject; - std::string name; - if(names.size() > id && ! names[id].empty()) - { - name=names[id]; - } - else - { - name = std::string(""); - } - - std::vector colors(all_polygons[id].size()); - write_mesh_to_model(all_points[id], all_polygons[id], colors, name, &pMeshObject, pModel); - } - - return export_model_to_file(file_name, pModel); -} - - -/*! - * \brief writes the triangle meshes contained in `tms` - * into the 3mf file `file_name`. - * \tparam TriangleMeshRange a model of the concepts `RandomAccessContainer` - * and `BackInsertionSequence` whose `value type` is - * a model of the concepts `FaceListGraph` and `HalfedgeListGraph` - * that has only triangle faces. - * \param file_name the name of the 3mf file to write. - * \param tms a `TriangleMeshRange` that contains the meshes - * to write. An internal property map for `CGAL::vertex_point_t` - * must be available for each mesh. - * \param names will contains the name of each mesh in `file_name`. - * \return `true` if the writing is successful, `false` otherwise. - */ -template -bool write_triangle_meshes_to_3mf(const std::string& file_name, - const TriangleMeshRange& tms, - const std::vector& names) -{ - typedef typename TriangleMeshRange::value_type Mesh; - typedef typename boost::property_map::type VPMap; - typedef typename boost::property_traits::value_type Point_3; - - typedef boost::graph_traits::vertex_descriptor vertex_descriptor; - typedef boost::graph_traits::face_descriptor face_descriptor; - - typedef std::vector Polygon; - typedef std::vector PolygonRange; - typedef std::vector PointRange; - - std::vector all_points; - std::vector all_polygons; - - for(const auto& tm : tms) - { - PointRange points; - points.reserve(num_vertices(tm)); - PolygonRange triangles; - triangles.reserve(num_faces(tm)); - - VPMap vpm = get(boost::vertex_point, tm); - std::unordered_map::vertex_descriptor, std::size_t> vertex_id_map; - - std::size_t i = 0; - for(vertex_descriptor v : vertices(tm)) - { - points.push_back(get(vpm, v)); - vertex_id_map[v] = i++; - } - - all_points.push_back(points); - for(face_descriptor f : faces(tm)) - { - Polygon triangle; - for(vertex_descriptor vert : CGAL::vertices_around_face(halfedge(f, tm), tm)) - triangle.push_back(vertex_id_map[vert]); - - triangles.push_back(triangle); - } - - all_polygons.push_back(triangles); - } - - return write_triangle_soups_to_3mf(file_name, all_points, all_polygons, names); -} - +} // namespace 3MF } // namespace CGAL #endif // CGAL_IO_WRITE_3MF_H diff --git a/Stream_support/include/CGAL/IO/Generic_writer.h b/Stream_support/include/CGAL/IO/Generic_writer.h index c0153dcbe3c..d2d67e2e87e 100644 --- a/Stream_support/include/CGAL/IO/Generic_writer.h +++ b/Stream_support/include/CGAL/IO/Generic_writer.h @@ -15,7 +15,7 @@ // Author(s) : Lutz Kettner #ifndef CGAL_IO_GENERIC_WRITER_H -#define CGAL_IO_GENERIC_WRITER_H 1 +#define CGAL_IO_GENERIC_WRITER_H #include #include diff --git a/Stream_support/include/CGAL/IO/OBJ.h b/Stream_support/include/CGAL/IO/OBJ.h index 9a9f389263f..3928a2cb1aa 100644 --- a/Stream_support/include/CGAL/IO/OBJ.h +++ b/Stream_support/include/CGAL/IO/OBJ.h @@ -15,20 +15,79 @@ #define CGAL_IO_OBJ_H #include -#include namespace CGAL { //! \ingroup IOstreamFunctions //! -///reads a file in .obj format. +/// reads the content of `input` into `points` and `faces`, using the `OBJ` format. /// -///\tparam Points_3 a RandomAccessContainer of Point_3, -///\tparam Faces a RandomAccessContainer of RandomAccessContainer of std::size_t +/// \tparam Points_3 a `RandomAccessContainer` of `Point_3, +/// \tparam Faces a `RandomAccessContainer` of `RandomAccessContainer` of `std::size_t` /// -/// \see IOStreamOBJ +/// \see \ref IOStreamOBJ template -bool read_OBJ(std::istream& input, Points_3 &points, Faces &faces); +bool read_OBJ(std::istream& input, + Points_3& points, + Faces& faces) +{ + typedef typename Points_3::value_type Point_3; + + int mini(1), + maxi(-INT_MAX); + Point_3 p; + std::string line; + + while(getline(input, line)) + { + if(line[0] == 'v' && line[1] == ' ') + { + std::istringstream iss(line.substr(1)); + iss >> p; + if(!iss) + return false; + points.push_back(p); + } + else if(line[0] == 'f') + { + std::istringstream iss(line.substr(1)); + int i; + faces.push_back(std::vector()); + while(iss >> i) + { + if(i < 1) + { + faces.back().push_back(points.size()+i);//negative indices are relative references + if(i maxi) + maxi = i-1; + } + iss.ignore(256, ' '); + } + + if(!iss.good() && !iss.eof()) + return false; + } + else + { + //std::cerr << "ERROR : Cannnot read line beginning with " << line[0] << std::endl; + continue; + } + } + + if(maxi > points.size() || mini < -static_cast(points.size())) + { + std::cerr << "a face index is invalid " << std::endl; + return false; + } + + return true; +} } // namespace CGAL diff --git a/Stream_support/include/CGAL/IO/OBJ/OBJ_reader.h b/Stream_support/include/CGAL/IO/OBJ/OBJ_reader.h deleted file mode 100644 index bf06610a2a4..00000000000 --- a/Stream_support/include/CGAL/IO/OBJ/OBJ_reader.h +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright (c) 2016 GeometryFactory -// -// This file is part of CGAL (www.cgal.org); -// -// $URL$ -// $Id$ -// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial -// -// Author(s) : Andreas Fabri and Maxime Gimeno - -#ifndef CGAL_IO_OBJ_OBJ_READER_H -#define CGAL_IO_OBJ_OBJ_READER_H - -#include -#include -#include -#include - -namespace CGAL { - -// Points_3 a RandomAccessContainer of Point_3, -// Faces a RandomAccessContainer of RandomAccessContainer of std::size_t -template -bool read_OBJ(std::istream& input, - Points_3 &points, - Faces &faces) -{ - typedef typename Points_3::value_type Point_3; - - int mini(1), - maxi(-INT_MAX); - Point_3 p; - std::string line; - - while(getline(input, line)) - { - if(line[0] == 'v' && line[1] == ' ') - { - std::istringstream iss(line.substr(1)); - iss >> p; - if(!iss) - return false; - points.push_back(p); - } - else if(line[0] == 'f') - { - std::istringstream iss(line.substr(1)); - int i; - faces.push_back(std::vector()); - while(iss >> i) - { - if(i < 1) - { - faces.back().push_back(points.size()+i);//negative indices are relative references - if(i maxi) - maxi = i-1; - } - iss.ignore(256, ' '); - } - - if(!iss.good() && !iss.eof()) - return false; - } - else - { - //std::cerr<<"ERROR : Cannnot read line beginning with "< points.size() || mini < -static_cast(points.size())) - { - std::cerr<<"a face index is invalid "< -#include +#include +#include #include -#include - -#include -#include -#include -#include -#include -#include - -#define TRY_TO_GENERATE_PROPERTY(STD_TYPE, T_TYPE, TYPE) \ - if (type == STD_TYPE || type == T_TYPE) \ - m_elements.back().add_property (new PLY_read_typed_number< TYPE > (name, format)) - -#define TRY_TO_GENERATE_SIZED_LIST_PROPERTY(STD_SIZE_TYPE, T_SIZE_TYPE, SIZE_TYPE, STD_INDEX_TYPE, T_INDEX_TYPE, INDEX_TYPE) \ - if ((size_type == STD_SIZE_TYPE || size_type == T_SIZE_TYPE) && \ - (index_type == STD_INDEX_TYPE || index_type == T_INDEX_TYPE)) \ - m_elements.back().add_property (new PLY_read_typed_list_with_typed_size< SIZE_TYPE , INDEX_TYPE > (name, format)) - -#define TRY_TO_GENERATE_LIST_PROPERTY(STD_INDEX_TYPE, T_INDEX_TYPE, INDEX_TYPE) \ - TRY_TO_GENERATE_SIZED_LIST_PROPERTY("uchar", "uint8", boost::uint8_t, STD_INDEX_TYPE, T_INDEX_TYPE, INDEX_TYPE); \ - else TRY_TO_GENERATE_SIZED_LIST_PROPERTY("ushort", "uint16", boost::uint16_t, STD_INDEX_TYPE, T_INDEX_TYPE, INDEX_TYPE); \ - else TRY_TO_GENERATE_SIZED_LIST_PROPERTY("uint", "uint32", boost::uint32_t, STD_INDEX_TYPE, T_INDEX_TYPE, INDEX_TYPE) - namespace CGAL { -/// \cond SKIP_IN_MANUAL +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +/// Read -// PLY types: -// name type number of bytes -// --------------------------------------- -// char character 1 -// uchar unsigned character 1 -// short short integer 2 -// ushort unsigned short integer 2 -// int integer 4 -// uint unsigned integer 4 -// float single-precision float 4 -// double double-precision float 8 - -template -struct PLY_property +template +bool read_PLY(std::istream& in, + std::vector& points, + std::vector& polygons, + std::vector >& hedges, + std::vector& fcolors, + std::vector& vcolors, + std::vector >& huvs, + bool /* verbose */ = false) { - typedef T type; - const char* name; - PLY_property (const char* name) : name (name) { } -}; - -// Use a double property for all kernels... -template struct Convert_FT { typedef double type; }; -// ...except if kernel uses type float -template <> struct Convert_FT { typedef float type; }; - -template -struct Get_FT_from_map -{ - typedef typename Convert_FT - ::value_type>::Kernel::FT>::type type; -}; - -template -std::tuple::Kernel::Construct_point_3, - PLY_property::type>, - PLY_property::type>, - PLY_property::type> > -make_ply_point_reader(PointMap point_map) -{ - return std::make_tuple (point_map, typename Kernel_traits::Kernel::Construct_point_3(), - PLY_property::type>("x"), - PLY_property::type>("y"), - PLY_property::type>("z")); -} - -template -std::tuple::Kernel::Construct_vector_3, - PLY_property::type>, - PLY_property::type>, - PLY_property::type> > -make_ply_normal_reader(VectorMap normal_map) -{ - return std::make_tuple (normal_map, typename Kernel_traits::Kernel::Construct_vector_3(), - PLY_property::type>("nx"), - PLY_property::type>("ny"), - PLY_property::type>("nz")); -} - -template -std::tuple::type>, - PLY_property::type>, - PLY_property::type> > -make_ply_point_writer(PointMap point_map) -{ - return std::make_tuple (point_map, - PLY_property::type>("x"), - PLY_property::type>("y"), - PLY_property::type>("z")); -} - -template -std::tuple::type>, - PLY_property::type>, - PLY_property::type> > -make_ply_normal_writer(VectorMap normal_map) -{ - return std::make_tuple (normal_map, - PLY_property::type>("nx"), - PLY_property::type>("ny"), - PLY_property::type>("nz")); -} - -/// \endcond - -namespace internal { - -namespace PLY { - -class PLY_read_number -{ -protected: - std::string m_name; - std::size_t m_format; - -public: - PLY_read_number (std::string name, std::size_t format) - : m_name (name), m_format (format) { } - virtual ~PLY_read_number() { } - - const std::string& name () const { return m_name; } - - virtual void get (std::istream& stream) const = 0; - - // The two following functions prevent the stream to only extract - // ONE character (= what the types char imply) by requiring - // explicitely an integer object when reading the stream - void read_ascii (std::istream& stream, char& c) const + if(!in) { - short s; - stream >> s; - c = static_cast(s); - } - void read_ascii (std::istream& stream, signed char& c) const - { - short s; - stream >> s; - c = static_cast(s); - } - void read_ascii (std::istream& stream, unsigned char& c) const - { - unsigned short s; - stream >> s; - c = static_cast(s); - } - - void read_ascii (std::istream& stream, float& t) const - { - stream >> iformat(t); + std::cerr << "Error: cannot open file" << std::endl; + return false; } + internal::PLY::PLY_reader reader; - void read_ascii (std::istream& stream, double& t) const + if(!(reader.init(in))) { - stream >> iformat(t); + in.setstate(std::ios::failbit); + return false; } - - // Default template when Type is not a char type - template - void read_ascii (std::istream& stream, Type& t) const + for(std::size_t i = 0; i < reader.number_of_elements(); ++ i) { - stream >> t; - } - - - template - Type read (std::istream& stream) const - { - if (m_format == 0) // Ascii + internal::PLY::PLY_element& element = reader.element(i); + + if(element.name() == "vertex" || element.name() == "vertices") { - Type t; - read_ascii (stream, t); - return t; - } - else // Binary (2 = little endian) - { - union + bool has_colors = false; + std::string rtag = "r", gtag = "g", btag = "b"; + if((element.has_property("red") || element.has_property("r")) && + (element.has_property("green") || element.has_property("g")) && + (element.has_property("blue") || element.has_property("b"))) { - char uChar[sizeof (Type)]; - Type type; - } buffer; - - std::size_t size = sizeof (Type); - - stream.read(buffer.uChar, size); - - if (m_format == 2) // Big endian - { - for (std::size_t i = 0; i < size / 2; ++ i) + has_colors = true; + if(element.has_property("red")) { - unsigned char tmp = buffer.uChar[i]; - buffer.uChar[i] = buffer.uChar[size - 1 - i]; - buffer.uChar[size - 1 - i] = tmp; + rtag = "red"; gtag = "green"; btag = "blue"; } } - return buffer.type; - } - return Type(); - } -}; -template -class PLY_read_typed_number : public PLY_read_number -{ - mutable Type m_buffer; -public: - PLY_read_typed_number (std::string name, std::size_t format) - : PLY_read_number (name, format) - { - } - void get (std::istream& stream) const - { - m_buffer = (this->read (stream)); - } - const Type& buffer() const - { - return m_buffer; - } -}; - -template -class PLY_read_typed_list : public PLY_read_number -{ -protected: - mutable std::vector m_buffer; -public: - PLY_read_typed_list (std::string name, std::size_t format) - : PLY_read_number (name, format) - { - } - virtual void get (std::istream& stream) const = 0; - - const std::vector& buffer() const - { - return m_buffer; - } -}; - -template -class PLY_read_typed_list_with_typed_size - : public PLY_read_typed_list -{ - -public: - PLY_read_typed_list_with_typed_size (std::string name, std::size_t format) - : PLY_read_typed_list (name, format) - { - } - void get (std::istream& stream) const - { - std::size_t size = static_cast(this->template read(stream)); - this->m_buffer.resize (size); - for (std::size_t i = 0; i < size; ++ i) - this->m_buffer[i] = this->template read (stream); - } -}; - -class PLY_element -{ - std::string m_name; - std::size_t m_number; - - std::vector m_properties; -public: - - PLY_element (const std::string& name, std::size_t number) - : m_name (name), m_number (number) - { } - - PLY_element (const PLY_element& other) - : m_name (other.m_name), m_number (other.m_number), m_properties (other.m_properties) - { - const_cast(other).m_properties.clear(); - } - - PLY_element& operator= (const PLY_element& other) - { - m_name = other.m_name; - m_number = other.m_number; - m_properties = other.m_properties; - const_cast(other).m_properties.clear(); - return *this; - } - - ~PLY_element() - { - for (std::size_t i = 0; i < m_properties.size(); ++ i) - delete m_properties[i]; - } - - const std::string& name() const { return m_name; } - std::size_t number_of_items() const { return m_number; } - std::size_t number_of_properties() const { return m_properties.size(); } - - PLY_read_number* property (std::size_t idx) { return m_properties[idx]; } - - void add_property (PLY_read_number* read_number) - { - m_properties.push_back (read_number); - } - - template - bool has_property (const char* tag) - { - return has_property (tag, Type()); - } - template - bool has_property (const char* tag, const std::vector&) - { - for (std::size_t i = 0; i < number_of_properties(); ++ i) - if (m_properties[i]->name () == tag) - return (dynamic_cast*>(m_properties[i]) != nullptr); - return false; - } - - template - bool has_property (const char* tag, Type) - { - for (std::size_t i = 0; i < number_of_properties(); ++ i) - if (m_properties[i]->name () == tag) - return (dynamic_cast*>(m_properties[i]) != nullptr); - return false; - } - bool has_property (const char* tag, double) - { - for (std::size_t i = 0; i < number_of_properties(); ++ i) - if (m_properties[i]->name () == tag) - return (dynamic_cast*>(m_properties[i]) != nullptr - || dynamic_cast*>(m_properties[i]) != nullptr); - - return false; - } - - template - void assign (Type& t, const char* tag) - { - for (std::size_t i = 0; i < number_of_properties (); ++ i) - if (m_properties[i]->name () == tag) + for(std::size_t j = 0; j < element.number_of_items(); ++ j) { - PLY_read_typed_number* - property = dynamic_cast*>(m_properties[i]); - CGAL_assertion (property != nullptr); - t = property->buffer(); - return; - } - t = {}; - } - - template - void assign (std::vector& t, const char* tag) - { - for (std::size_t i = 0; i < number_of_properties (); ++ i) - if (m_properties[i]->name () == tag) - { - PLY_read_typed_list* - property = dynamic_cast*>(m_properties[i]); - CGAL_assertion (property != nullptr); - t = property->buffer(); - return; - } - t = {}; - } - - void assign (double& t, const char* tag) - { - for (std::size_t i = 0; i < number_of_properties (); ++ i) - if (m_properties[i]->name () == tag) - { - PLY_read_typed_number* - property_double = dynamic_cast*>(m_properties[i]); - if (property_double == nullptr) + for(std::size_t k = 0; k < element.number_of_properties(); ++ k) { - PLY_read_typed_number* - property_float = dynamic_cast*>(m_properties[i]); - CGAL_assertion (property_float != nullptr); - t = property_float->buffer(); + internal::PLY::PLY_read_number* property = element.property(k); + property->get(in); + + if(in.fail()) + return false; + } + + std::tuple new_vertex; + + if(has_colors) + { + internal::PLY::process_properties(element, new_vertex, + make_ply_point_reader(CGAL::make_nth_of_tuple_property_map<0>(new_vertex)), + std::make_pair(CGAL::make_nth_of_tuple_property_map<1>(new_vertex), + PLY_property(rtag.c_str())), + std::make_pair(CGAL::make_nth_of_tuple_property_map<2>(new_vertex), + PLY_property(gtag.c_str())), + std::make_pair(CGAL::make_nth_of_tuple_property_map<3>(new_vertex), + PLY_property(btag.c_str()))); + + vcolors.push_back(Color_rgb(get<1>(new_vertex), get<2>(new_vertex), get<3>(new_vertex))); } else - t = property_double->buffer(); - - return; + internal::PLY::process_properties(element, new_vertex, + make_ply_point_reader(CGAL::make_nth_of_tuple_property_map<0>(new_vertex))); + + points.push_back(get<0>(new_vertex)); } - t = {}; - } - -}; - -class PLY_reader -{ - std::vector m_elements; - std::string m_comments; - -public: - PLY_reader () { } - - std::size_t number_of_elements() const { return m_elements.size(); } - PLY_element& element (std::size_t idx) - { - return m_elements[idx]; - } - - const std::string& comments() const { return m_comments; } - - template - bool init (Stream& stream) - { - std::size_t lineNumber = 0; // current line number - enum Format { ASCII = 0, BINARY_LITTLE_ENDIAN = 1, BINARY_BIG_ENDIAN = 2}; - Format format = ASCII; - - std::string line; - std::istringstream iss; - - while (getline (stream,line)) + } + else if(element.name() == "face" || element.name() == "faces") { - iss.clear(); - iss.str (line); - ++ lineNumber; - - // Reads file signature on first line - if (lineNumber == 1) - { - std::string signature; - if (!(iss >> signature) || (signature != "ply")) - { - // if wrong file format - std::cerr << "Error: incorrect file format line " << lineNumber << " of file" << std::endl; - return false; - } - } - - // Reads format on 2nd line - else if (lineNumber == 2) - { - std::string tag, format_string, version; - if ( !(iss >> tag >> format_string >> version) ) - { - std::cerr << "Error line " << lineNumber << " of file" << std::endl; - return false; - } - if (format_string == "ascii") format = ASCII; - else if (format_string == "binary_little_endian") format = BINARY_LITTLE_ENDIAN; - else if (format_string == "binary_big_endian") format = BINARY_BIG_ENDIAN; - else - { - std::cerr << "Error: unknown file format \"" << format_string << "\" line " << lineNumber << std::endl; - return false; - } - } - - // Comments and vertex properties + if(element.has_property >("vertex_indices")) + internal::read_PLY_faces(in, element, polygons, fcolors, "vertex_indices"); + else if(element.has_property >("vertex_indices")) + internal::read_PLY_faces(in, element, polygons, fcolors, "vertex_indices"); + else if(element.has_property >("vertex_index")) + internal::read_PLY_faces(in, element, polygons, fcolors, "vertex_index"); + else if(element.has_property >("vertex_index")) + internal::read_PLY_faces(in, element, polygons, fcolors, "vertex_index"); else { - std::string keyword; - if (!(iss >> keyword)) - { - std::cerr << "Error line " << lineNumber << " of file" << std::endl; - return false; - } - - if (keyword == "property") - { - std::string type, name; - if (!(iss >> type >> name)) - { - std::cerr << "Error line " << lineNumber << " of file" << std::endl; - return false; - } - - - if (type == "list") // Special case - { - std::string size_type = name; - std::string index_type; - name.clear(); - if (!(iss >> index_type >> name)) - { - std::cerr << "Error line " << lineNumber << " of file" << std::endl; - return false; - } - - TRY_TO_GENERATE_LIST_PROPERTY ("char", "int8", boost::int8_t); - else TRY_TO_GENERATE_LIST_PROPERTY ("uchar", "uint8", boost::uint8_t); - else TRY_TO_GENERATE_LIST_PROPERTY ("short", "int16", boost::int16_t); - else TRY_TO_GENERATE_LIST_PROPERTY ("ushort", "uint16", boost::uint16_t); - else TRY_TO_GENERATE_LIST_PROPERTY ("int", "int32", boost::int32_t); - else TRY_TO_GENERATE_LIST_PROPERTY ("uint", "uint32", boost::uint32_t); - else TRY_TO_GENERATE_LIST_PROPERTY ("float", "float32", float); - else TRY_TO_GENERATE_LIST_PROPERTY ("double", "float64", double); - } - else - { - TRY_TO_GENERATE_PROPERTY ("char", "int8", boost::int8_t); - else TRY_TO_GENERATE_PROPERTY ("uchar", "uint8", boost::uint8_t); - else TRY_TO_GENERATE_PROPERTY ("short", "int16", boost::int16_t); - else TRY_TO_GENERATE_PROPERTY ("ushort", "uint16", boost::uint16_t); - else TRY_TO_GENERATE_PROPERTY ("int", "int32", boost::int32_t); - else TRY_TO_GENERATE_PROPERTY ("uint", "uint32", boost::uint32_t); - else TRY_TO_GENERATE_PROPERTY ("float", "float32", float); - else TRY_TO_GENERATE_PROPERTY ("double", "float64", double); - } - - continue; - } - else if (keyword == "comment") - { - std::string str = iss.str(); - if (str.size() > 8) - { - std::copy (str.begin() + 8, str.end(), std::back_inserter (m_comments)); - m_comments += "\n"; - } - } - else if (keyword == "element") - { - std::string type; - std::size_t number; - if (!(iss >> type >> number)) - { - std::cerr << "Error line " << lineNumber << " of file" << std::endl; - return false; - } - - m_elements.push_back (PLY_element(type, number)); - } - // When end_header is reached, stop loop and begin reading points - else if (keyword == "end_header") - break; + std::cerr << "Error: can't find vertex indices in PLY input" << std::endl; + return false; } } - return true; - } - - ~PLY_reader () - { - } - -}; - -template -void get_value(Reader& r, T& v, PLY_property& wrapper) -{ - return r.assign(v, wrapper.name); -} - - -template -struct Filler -{ - template - static void fill(Reader& r, Value_tuple& values, PLY_property_tuple wrappers) - { - get_value(r, std::get(values), std::get(wrappers)); - Filler::fill(r, values, wrappers); - } -}; - -template -struct seq { }; - -template -struct gens : gens { }; - -template -struct gens<0, S...> { - typedef seq type; -}; - -template -ValueType call_functor(Functor f, Tuple t, seq) { - return f(std::get(t) ...); -} - -template -ValueType call_functor(Functor f, std::tuple& t) -{ - return call_functor(f, t, typename gens::type()); -} - -template<> -struct Filler<0> -{ - template - static void fill(Reader& r, Value_tuple& values, PLY_property_tuple wrappers) - { - get_value(r, std::get<0>(values), std::get<2>(wrappers)); - } -}; - -template -void process_properties (PLY_element& element, OutputValueType& new_element, - std::tuple...>&& current) -{ - typedef typename PropertyMap::value_type PmapValueType; - std::tuple values; - Filler::fill(element, values, current); - PmapValueType new_value = call_functor(std::get<1>(current), values); - put (std::get<0>(current), new_element, new_value); -} - -template -void process_properties (PLY_element& element, OutputValueType& new_element, - std::tuple...>&& current, - NextPropertyBinder&& next, - PropertyMapBinders&& ... properties) -{ - typedef typename PropertyMap::value_type PmapValueType; - std::tuple values; - Filler::fill(element, values, current); - PmapValueType new_value = call_functor(std::get<1>(current), values); - put (std::get<0>(current), new_element, new_value); - - process_properties (element, new_element, std::forward(next), - std::forward(properties)...); -} - - -template -void process_properties (PLY_element& element, OutputValueType& new_element, - std::pair >&& current) -{ - T new_value = T(); - element.assign (new_value, current.second.name); - put (current.first, new_element, new_value); -} - -template -void process_properties (PLY_element& element, OutputValueType& new_element, - std::pair >&& current, - NextPropertyBinder&& next, - PropertyMapBinders&& ... properties) -{ - T new_value = T(); - element.assign (new_value, current.second.name); - put (current.first, new_element, new_value); - process_properties (element, new_element, std::forward(next), - std::forward(properties)...); -} - -template inline void property_header_type (std::ostream& stream) -{ - CGAL_assertion_msg (false, "Unknown PLY type"); - stream << "undefined_type"; -} - -template <> inline void property_header_type (std::ostream& stream) { stream << "char"; } -template <> inline void property_header_type (std::ostream& stream) { stream << "char"; } -template <> inline void property_header_type (std::ostream& stream) { stream << "uchar"; } -template <> inline void property_header_type (std::ostream& stream) { stream << "short"; } -template <> inline void property_header_type (std::ostream& stream) { stream << "ushort"; } -template <> inline void property_header_type (std::ostream& stream) { stream << "int"; } -template <> inline void property_header_type (std::ostream& stream) { stream << "uint"; } -template <> inline void property_header_type (std::ostream& stream) { stream << "float"; } -template <> inline void property_header_type (std::ostream& stream) { stream << "double"; } - -template -void property_header (std::ostream& stream, const PLY_property& prop) -{ - stream << "property "; - property_header_type(stream); - stream << " " << prop.name << std::endl; -} - -template -void property_header (std::ostream& stream, const PLY_property >& prop) -{ - stream << "property list uchar "; - property_header_type(stream); - stream << " " << prop.name << std::endl; -} - - -template -struct Properties_header -{ - template - static void write(std::ostream& stream, PLY_property_tuple& wrappers) - { - Properties_header::write(stream, wrappers); - property_header (stream, std::get(wrappers)); - } -}; -template <> -struct Properties_header<0> -{ - template - static void write(std::ostream& stream, PLY_property_tuple& wrappers) - { - property_header (stream, std::get<1>(wrappers)); - } -}; - -template -void output_property_header (std::ostream& stream, - std::tuple... >&& current) -{ - Properties_header::write(stream, current); -} - - -template -void output_property_header (std::ostream& stream, - std::pair >&& current) -{ - property_header (stream, current.second); -} - -template -void output_property_header (std::ostream& stream, - std::pair >&& current, - NextPropertyHandler&& next, - PropertyHandler&& ... properties) -{ - property_header (stream, current.second); - output_property_header (stream, std::forward(next), - std::forward(properties)...); -} -template -void output_property_header (std::ostream& stream, - std::tuple... >&& current, - NextPropertyHandler&& next, - PropertyHandler&& ... properties) -{ - Properties_header::write(stream, current); - output_property_header (stream, std::forward(next), - std::forward(properties)...); -} - - -template -void property_write (std::ostream& stream, ForwardIterator it, PropertyMap map) -{ - stream << CGAL::oformat(get (map, *it)); -} - -template -inline T no_char_character (const T& t) { return t; } -inline int no_char_character (const char& t) { return int(t); } -inline int no_char_character (const signed char& t) { return int(t); } -inline int no_char_character (const unsigned char& t) { return int(t); } - -template -void simple_property_write (std::ostream& stream, ForwardIterator it, - std::pair > map) -{ - if (CGAL::get_mode(stream) == CGAL::IO::ASCII) - stream << no_char_character(get (map.first, *it)); - else - { - typename PropertyMap::value_type value = get(map.first, *it); - stream.write (reinterpret_cast(&value), sizeof(value)); - } -} - -template -void simple_property_write (std::ostream& stream, ForwardIterator it, - std::pair > > map) -{ - const typename PropertyMap::reference value = get(map.first, *it); - - if (CGAL::get_mode(stream) == CGAL::IO::ASCII) - { - stream << value.size(); - for (std::size_t i = 0; i < value.size(); ++ i) - stream << " " << no_char_character(value[i]); - } - else - { - unsigned char size = static_cast(value.size()); - stream.write (reinterpret_cast(&size), sizeof(size)); - for (std::size_t i = 0; i < value.size(); ++ i) + else if(element.name() == "halfedge" ) { - T t = T(value[i]); - stream.write (reinterpret_cast(&t), sizeof(t)); + bool has_uv = false; + std::string stag = "source", ttag = "target", utag = "u", vtag = "v"; + if( element.has_property("source") && + element.has_property("target") && + element.has_property("u") && + element.has_property("v")) + { + has_uv = true; + } + cpp11::tuple new_hedge; + for(std::size_t j = 0; j < element.number_of_items(); ++ j) + { + for(std::size_t k = 0; k < element.number_of_properties(); ++ k) + { + internal::PLY::PLY_read_number* property = element.property(k); + property->get(in); + + if(in.eof()) + return false; + } + + if(has_uv) + { + internal::PLY::process_properties(element, new_hedge, + std::make_pair(CGAL::make_nth_of_tuple_property_map<0>(new_hedge), + PLY_property(stag.c_str())), + std::make_pair(CGAL::make_nth_of_tuple_property_map<1>(new_hedge), + PLY_property(ttag.c_str())), + std::make_pair(CGAL::make_nth_of_tuple_property_map<2>(new_hedge), + PLY_property(utag.c_str())), + std::make_pair(CGAL::make_nth_of_tuple_property_map<3>(new_hedge), + PLY_property(vtag.c_str()))); + hedges.push_back(std::make_pair(get<0>(new_hedge), get<1>(new_hedge))); + huvs.push_back(std::make_pair(get<2>(new_hedge), get<3>(new_hedge))); + } + else + { + internal::PLY::process_properties(element, new_hedge, + std::make_pair(CGAL::make_nth_of_tuple_property_map<0>(new_hedge), + PLY_property(stag.c_str())), + std::make_pair(CGAL::make_nth_of_tuple_property_map<1>(new_hedge), + PLY_property(ttag.c_str())) + ); + } + } + } + else // Read other elements and ignore + { + for(std::size_t j = 0; j < element.number_of_items(); ++ j) + { + for(std::size_t k = 0; k < element.number_of_properties(); ++ k) + { + internal::PLY::PLY_read_number* property = element.property(k); + property->get(in); + if(in.fail()) + return false; + } + } } } + return !in.bad(); } - -template -void output_properties (std::ostream& stream, - ForwardIterator it, - std::tuple... >&& current) +template +bool read_PLY(std::istream& in, + std::vector< Point_3 >& points, + std::vector< Polygon_3 >& polygons, + std::vector& fcolors, + std::vector& vcolors, + bool /* verbose */ = false) { - property_write (stream, it, std::get<0>(current)); - if (get_mode(stream) == CGAL::IO::ASCII) - stream << std::endl; + std::vector > dummy_pui; + std::vector > dummy_pf; + + return read_PLY(in, points, polygons, dummy_pui, fcolors, vcolors, dummy_pf); } - -template -void output_properties (std::ostream& stream, - ForwardIterator it, - std::pair >&& current) +/*! + * \ingroup IOstreamFunctions + * + * reads the content of `in` into `points` and `polygons`, in the PLY format. + * + * \see \ref IOStreamPLY + */ +template +bool +read_PLY(std::istream& in, + std::vector< Point_3 >& points, + std::vector< Polygon_3 >& polygons, + bool /* verbose */ = false) { - simple_property_write (stream, it, std::forward > >(current)); - if (get_mode(stream) == CGAL::IO::ASCII) - stream << std::endl; -} - -template -void output_properties (std::ostream& stream, - ForwardIterator it, - std::pair >&& current, - NextPropertyHandler&& next, - PropertyHandler&& ... properties) -{ - simple_property_write (stream, it, current); - if (get_mode(stream) == CGAL::IO::ASCII) - stream << " "; - output_properties (stream, it, std::forward(next), - std::forward(properties)...); -} - -template -void output_properties (std::ostream& stream, - ForwardIterator it, - std::tuple... >&& current, - NextPropertyHandler&& next, - PropertyHandler&& ... properties) -{ - property_write (stream, it, std::get<0>(current)); - if (get_mode(stream) == CGAL::IO::ASCII) - stream << " "; - output_properties (stream, it, std::forward(next), - std::forward(properties)...); -} - - -// Printer classes used by Point_set_3 and Surface_mesh (translate a -// property map to a PLY property) - -template -class Abstract_property_printer -{ -public: - virtual ~Abstract_property_printer() { } - virtual void print (std::ostream& stream, const Index& index) = 0; -}; - -template -class Property_printer : public Abstract_property_printer -{ - PropertyMap m_pmap; -public: - Property_printer (const PropertyMap& pmap) : m_pmap (pmap) + if(!in) { - + std::cerr << "Error: cannot open file" << std::endl; + return false; } - - virtual void print(std::ostream& stream, const Index& index) - { - stream << get(m_pmap, index); - } -}; -template -class Simple_property_printer : public Abstract_property_printer -{ - PropertyMap m_pmap; -public: - Simple_property_printer (const PropertyMap& pmap) : m_pmap (pmap) - { + internal::PLY::PLY_reader reader; - } - - virtual void print(std::ostream& stream, const Index& index) + if(!(reader.init(in))) { - if (get_mode(stream) == CGAL::IO::ASCII) - stream << get(m_pmap, index); - else + in.setstate(std::ios::failbit); + return false; + } + + for(std::size_t i = 0; i < reader.number_of_elements(); ++ i) + { + internal::PLY::PLY_element& element = reader.element(i); + + if(element.name() == "vertex" || element.name() == "vertices") { - Type t = Type(get (m_pmap, index)); - stream.write (reinterpret_cast(&t), sizeof(t)); + for(std::size_t j = 0; j < element.number_of_items(); ++ j) + { + for(std::size_t k = 0; k < element.number_of_properties(); ++ k) + { + internal::PLY::PLY_read_number* property = element.property(k); + property->get(in); + + if(in.fail()) + return false; + } + + Point_3 new_vertex; + + internal::PLY::process_properties(element, new_vertex, + make_ply_point_reader(CGAL::Identity_property_map())); + + points.push_back(get<0>(new_vertex)); + } + } + else if(element.name() == "face" || element.name() == "faces") + { + std::vector dummy; + + if(element.has_property >("vertex_indices")) + internal::read_PLY_faces(in, element, polygons, dummy, "vertex_indices"); + else if(element.has_property >("vertex_indices")) + internal::read_PLY_faces(in, element, polygons, dummy, "vertex_indices"); + else if(element.has_property >("vertex_index")) + internal::read_PLY_faces(in, element, polygons, dummy, "vertex_index"); + else if(element.has_property >("vertex_index")) + internal::read_PLY_faces(in, element, polygons, dummy, "vertex_index"); + else + { + std::cerr << "Error: can't find vertex indices in PLY input" << std::endl; + return false; + } + } + else // Read other elements and ignore + { + for(std::size_t j = 0; j < element.number_of_items(); ++ j) + { + for(std::size_t k = 0; k < element.number_of_properties(); ++ k) + { + internal::PLY::PLY_read_number* property = element.property(k); + property->get(in); + + if(in.fail()) + return false; + } + } } } -}; -template -class Char_property_printer : public Abstract_property_printer + return !in.bad(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +/// Write + +/*! + * \ingroup IOstreamFunctions + * + * writes the content of `points` and `polygons` in `out`, in the OFF format. + * + * \see \ref IOStreamOFF + */ +template +bool write_PLY(std::ostream& out, + const std::vector< Point_3 >& points, + const std::vector< Polygon_3 >& polygons, + bool /* verbose */ = false) { - typedef typename PropertyMap::value_type Type; - PropertyMap m_pmap; -public: - Char_property_printer (const PropertyMap& pmap) : m_pmap (pmap) + if(!out) { - + std::cerr << "Error: cannot open file" << std::endl; + return false; } - - virtual void print(std::ostream& stream, const Index& index) + + // Write header + out << "ply" << std::endl + << ((get_mode(out) == IO::BINARY) ? "format binary_little_endian 1.0" : "format ascii 1.0") << std::endl + << "comment Generated by the CGAL library" << std::endl + << "element vertex " << points.size() << std::endl; + + internal::PLY::output_property_header(out, make_ply_point_writer (CGAL::Identity_property_map())); + + out << "element face " << polygons.size() << std::endl; + + internal::PLY::output_property_header(out, std::make_pair(CGAL::Identity_property_map(), + PLY_property >("vertex_indices"))); + + out << "end_header" << std::endl; + + for (std::size_t i = 0; i < points.size(); ++ i) + internal::PLY::output_properties(out, points.begin() + i, + make_ply_point_writer (CGAL::Identity_property_map())); + + for (std::size_t i = 0; i < polygons.size(); ++ i) + internal::PLY::output_properties(out, polygons.begin() + i, + std::make_pair(CGAL::Identity_property_map(), + PLY_property >("vertex_indices"))); + + return out.good(); +} + +template +bool write_PLY(std::ostream& out, + const SurfaceMesh& mesh, + bool /* verbose */ = false) +{ + typedef typename boost::graph_traits::face_descriptor face_descriptor; + typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; + typedef typename boost::property_map::type::value_type Point_3; + typedef typename SurfaceMesh::template Property_map > UV_map; + + UV_map h_uv; + bool has_texture; + boost::tie(h_uv, has_texture) = mesh.template property_map >("h:uv"); + + if(!out) { - if (get_mode(stream) == CGAL::IO::ASCII) - stream << int(get(m_pmap, index)); - else + std::cerr << "Error: cannot open file" << std::endl; + return false; + } + + // Write header + out << "ply" << std::endl + << ((get_mode(out) == IO::BINARY) ? "format binary_little_endian 1.0" : "format ascii 1.0") << std::endl + << "comment Generated by the CGAL library" << std::endl + << "element vertex " << num_vertices(mesh) << std::endl; + + internal::PLY::output_property_header(out, + make_ply_point_writer (CGAL::Identity_property_map())); + + out << "element face " << num_faces(mesh) << std::endl; + + internal::PLY::output_property_header(out, + std::make_pair(CGAL::Identity_property_map >(), + PLY_property >("vertex_indices"))); + + if(has_texture) + { + out << "element halfedge " << num_halfedges(mesh) << std::endl; + + internal::PLY::output_property_header(out, + std::make_pair(CGAL::Identity_property_map(), + PLY_property("source"))); + + internal::PLY::output_property_header(out, + std::make_pair(CGAL::Identity_property_map(), + PLY_property("target"))); + internal::PLY::output_property_header(out, + std::make_tuple (h_uv, + PLY_property("u"), + PLY_property("v"))); + } + out << "end_header" << std::endl; + + for(vertex_descriptor vd : vertices(mesh)) + { + Point_3 p = get(get(CGAL::vertex_point, mesh), vd); + internal::PLY::output_properties(out, &p, + make_ply_point_writer (CGAL::Identity_property_map())); + } + + std::vector polygon; + for(face_descriptor fd : faces(mesh)) + { + polygon.clear(); + for(halfedge_descriptor hd : halfedges_around_face(halfedge(fd, mesh), mesh)) + polygon.push_back(get(get(boost::vertex_index, mesh), target(hd,mesh))); + + internal::PLY::output_properties(out, &polygon, + std::make_pair(CGAL::Identity_property_map >(), + PLY_property >("vertex_indices"))); + } + + if(has_texture) + { + for(halfedge_descriptor hd : halfedges(mesh)) { - Type t = get (m_pmap, index); - stream.write (reinterpret_cast(&t), sizeof(t)); + typedef std::tuple Super_tuple; + Super_tuple t = std::make_tuple(source(hd, mesh),target(hd, mesh), + h_uv[hd].first, + h_uv[hd].second); + + internal::PLY::output_properties(out, &t, + std::make_pair(Nth_of_tuple_property_map<0,Super_tuple>(), + PLY_property("source")), + std::make_pair(Nth_of_tuple_property_map<1,Super_tuple>(), + PLY_property("target")), + std::make_pair(Nth_of_tuple_property_map<2,Super_tuple>(), + PLY_property("u")), + std::make_pair(Nth_of_tuple_property_map<3,Super_tuple>(), + PLY_property("v"))); } } -}; -} // namespace PLY - -} // namespace internal + return out.good(); +} } // namespace CGAL - #endif // CGAL_IO_PLY_H diff --git a/Stream_support/include/CGAL/IO/PLY/PLY_reader.h b/Stream_support/include/CGAL/IO/PLY/PLY_reader.h index 40c6a7f5643..ff8bdb9da51 100644 --- a/Stream_support/include/CGAL/IO/PLY/PLY_reader.h +++ b/Stream_support/include/CGAL/IO/PLY/PLY_reader.h @@ -11,11 +11,668 @@ #ifndef CGAL_IO_PLY_PLY_READER_H #define CGAL_IO_PLY_PLY_READER_H -#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#define TRY_TO_GENERATE_PROPERTY(STD_TYPE, T_TYPE, TYPE) \ + if(type == STD_TYPE || type == T_TYPE) \ + m_elements.back().add_property(new PLY_read_typed_number< TYPE >(name, format)) + +#define TRY_TO_GENERATE_SIZED_LIST_PROPERTY(STD_SIZE_TYPE, T_SIZE_TYPE, SIZE_TYPE, STD_INDEX_TYPE, T_INDEX_TYPE, INDEX_TYPE) \ + if((size_type == STD_SIZE_TYPE || size_type == T_SIZE_TYPE) && \ + (index_type == STD_INDEX_TYPE || index_type == T_INDEX_TYPE)) \ + m_elements.back().add_property(new PLY_read_typed_list_with_typed_size< SIZE_TYPE , INDEX_TYPE >(name, format)) + +#define TRY_TO_GENERATE_LIST_PROPERTY(STD_INDEX_TYPE, T_INDEX_TYPE, INDEX_TYPE) \ + TRY_TO_GENERATE_SIZED_LIST_PROPERTY("uchar", "uint8", boost::uint8_t, STD_INDEX_TYPE, T_INDEX_TYPE, INDEX_TYPE); \ + else TRY_TO_GENERATE_SIZED_LIST_PROPERTY("ushort", "uint16", boost::uint16_t, STD_INDEX_TYPE, T_INDEX_TYPE, INDEX_TYPE); \ + else TRY_TO_GENERATE_SIZED_LIST_PROPERTY("uint", "uint32", boost::uint32_t, STD_INDEX_TYPE, T_INDEX_TYPE, INDEX_TYPE) namespace CGAL { + +/// \cond SKIP_IN_MANUAL + +// PLY types: +// name type number of bytes +// --------------------------------------- +// char character 1 +// uchar unsigned character 1 +// short short integer 2 +// ushort unsigned short integer 2 +// int integer 4 +// uint unsigned integer 4 +// float single-precision float 4 +// double double-precision float 8 + +template +struct PLY_property +{ + typedef T type; + const char* name; + PLY_property(const char* name) : name(name) { } +}; + +// Use a double property for all kernels... +template struct Convert_FT { typedef double type; }; +// ...except if kernel uses type float +template <> struct Convert_FT { typedef float type; }; + +template +struct Get_FT_from_map +{ + typedef typename Convert_FT + ::value_type>::Kernel::FT>::type type; +}; + +template +std::tuple::Kernel::Construct_point_3, +PLY_property::type>, +PLY_property::type>, +PLY_property::type> > +make_ply_point_reader(PointMap point_map) +{ + return std::make_tuple(point_map, typename Kernel_traits::Kernel::Construct_point_3(), + PLY_property::type>("x"), + PLY_property::type>("y"), + PLY_property::type>("z")); +} + +template +std::tuple::Kernel::Construct_vector_3, +PLY_property::type>, +PLY_property::type>, +PLY_property::type> > +make_ply_normal_reader(VectorMap normal_map) +{ + return std::make_tuple(normal_map, typename Kernel_traits::Kernel::Construct_vector_3(), + PLY_property::type>("nx"), + PLY_property::type>("ny"), + PLY_property::type>("nz")); +} + +template +std::tuple::type>, +PLY_property::type>, +PLY_property::type> > +make_ply_point_writer(PointMap point_map) +{ + return std::make_tuple(point_map, + PLY_property::type>("x"), + PLY_property::type>("y"), + PLY_property::type>("z")); +} + +template +std::tuple::type>, +PLY_property::type>, +PLY_property::type> > +make_ply_normal_writer(VectorMap normal_map) +{ + return std::make_tuple(normal_map, + PLY_property::type>("nx"), + PLY_property::type>("ny"), + PLY_property::type>("nz")); +} + +/// \endcond + +namespace Stream_support { + namespace internal { +class PLY_read_number +{ +protected: + std::string m_name; + std::size_t m_format; + +public: + PLY_read_number(std::string name, std::size_t format) + : m_name(name), m_format(format) { } + virtual ~PLY_read_number() { } + + const std::string& name() const { return m_name; } + + virtual void get(std::istream& stream) const = 0; + + // The two following functions prevent the stream to only extract + // ONE character (= what the types char imply) by requiring + // explicitely an integer object when reading the stream + void read_ascii(std::istream& stream, char& c) const + { + short s; + stream >> s; + c = static_cast(s); + } + void read_ascii(std::istream& stream, signed char& c) const + { + short s; + stream >> s; + c = static_cast(s); + } + void read_ascii(std::istream& stream, unsigned char& c) const + { + unsigned short s; + stream >> s; + c = static_cast(s); + } + + void read_ascii(std::istream& stream, float& t) const + { + stream >> iformat(t); + } + + void read_ascii(std::istream& stream, double& t) const + { + stream >> iformat(t); + } + + // Default template when Type is not a char type + template + void read_ascii(std::istream& stream, Type& t) const + { + stream >> t; + } + + + template + Type read(std::istream& stream) const + { + if(m_format == 0) // Ascii + { + Type t; + read_ascii(stream, t); + return t; + } + else // Binary (2 = little endian) + { + union + { + char uChar[sizeof(Type)]; + Type type; + } buffer; + + std::size_t size = sizeof(Type); + + stream.read(buffer.uChar, size); + + if(m_format == 2) // Big endian + { + for(std::size_t i = 0; i < size / 2; ++ i) + { + unsigned char tmp = buffer.uChar[i]; + buffer.uChar[i] = buffer.uChar[size - 1 - i]; + buffer.uChar[size - 1 - i] = tmp; + } + } + return buffer.type; + } + return Type(); + } +}; + +template +class PLY_read_typed_number : public PLY_read_number +{ + mutable Type m_buffer; +public: + PLY_read_typed_number(std::string name, std::size_t format) + : PLY_read_number(name, format) + { + } + void get(std::istream& stream) const + { + m_buffer =(this->read(stream)); + } + const Type& buffer() const + { + return m_buffer; + } +}; + +template +class PLY_read_typed_list : public PLY_read_number +{ +protected: + mutable std::vector m_buffer; +public: + PLY_read_typed_list(std::string name, std::size_t format) + : PLY_read_number(name, format) + { + } + virtual void get(std::istream& stream) const = 0; + + const std::vector& buffer() const + { + return m_buffer; + } +}; + +template +class PLY_read_typed_list_with_typed_size + : public PLY_read_typed_list +{ + +public: + PLY_read_typed_list_with_typed_size(std::string name, std::size_t format) + : PLY_read_typed_list(name, format) + { + } + void get(std::istream& stream) const + { + std::size_t size = static_cast(this->template read(stream)); + this->m_buffer.resize(size); + for(std::size_t i = 0; i < size; ++ i) + this->m_buffer[i] = this->template read(stream); + } +}; + +class PLY_element +{ + std::string m_name; + std::size_t m_number; + + std::vector m_properties; +public: + + PLY_element(const std::string& name, std::size_t number) + : m_name(name), m_number(number) + { } + + PLY_element(const PLY_element& other) + : m_name(other.m_name), m_number(other.m_number), m_properties(other.m_properties) + { + const_cast(other).m_properties.clear(); + } + + PLY_element& operator=(const PLY_element& other) + { + m_name = other.m_name; + m_number = other.m_number; + m_properties = other.m_properties; + const_cast(other).m_properties.clear(); + return *this; + } + + ~PLY_element() + { + for(std::size_t i = 0; i < m_properties.size(); ++ i) + delete m_properties[i]; + } + + const std::string& name() const { return m_name; } + std::size_t number_of_items() const { return m_number; } + std::size_t number_of_properties() const { return m_properties.size(); } + + PLY_read_number* property(std::size_t idx) { return m_properties[idx]; } + + void add_property(PLY_read_number* read_number) + { + m_properties.push_back(read_number); + } + + template + bool has_property(const char* tag) + { + return has_property(tag, Type()); + } + template + bool has_property(const char* tag, const std::vector&) + { + for(std::size_t i = 0; i < number_of_properties(); ++ i) + if(m_properties[i]->name() == tag) + return (dynamic_cast*>(m_properties[i]) != nullptr); + return false; + } + + template + bool has_property(const char* tag, Type) + { + for(std::size_t i = 0; i < number_of_properties(); ++ i) + if(m_properties[i]->name() == tag) + return (dynamic_cast*>(m_properties[i]) != nullptr); + return false; + } + bool has_property(const char* tag, double) + { + for(std::size_t i = 0; i < number_of_properties(); ++ i) + if(m_properties[i]->name() == tag) + return (dynamic_cast*>(m_properties[i]) != nullptr + || dynamic_cast*>(m_properties[i]) != nullptr); + + return false; + } + + template + void assign(Type& t, const char* tag) + { + for(std::size_t i = 0; i < number_of_properties(); ++ i) + if(m_properties[i]->name() == tag) + { + PLY_read_typed_number* + property = dynamic_cast*>(m_properties[i]); + CGAL_assertion(property != nullptr); + t = property->buffer(); + return; + } + t = {}; + } + + template + void assign(std::vector& t, const char* tag) + { + for(std::size_t i = 0; i < number_of_properties(); ++ i) + if(m_properties[i]->name() == tag) + { + PLY_read_typed_list* + property = dynamic_cast*>(m_properties[i]); + CGAL_assertion(property != nullptr); + t = property->buffer(); + return; + } + t = {}; + } + + void assign(double& t, const char* tag) + { + for(std::size_t i = 0; i < number_of_properties(); ++ i) + if(m_properties[i]->name() == tag) + { + PLY_read_typed_number* + property_double = dynamic_cast*>(m_properties[i]); + if(property_double == nullptr) + { + PLY_read_typed_number* + property_float = dynamic_cast*>(m_properties[i]); + CGAL_assertion(property_float != nullptr); + t = property_float->buffer(); + } + else + t = property_double->buffer(); + + return; + } + t = {}; + } +}; + +class PLY_reader +{ + std::vector m_elements; + std::string m_comments; + +public: + PLY_reader() { } + + std::size_t number_of_elements() const { return m_elements.size(); } + PLY_element& element(std::size_t idx) + { + return m_elements[idx]; + } + + const std::string& comments() const { return m_comments; } + + template + bool init(Stream& stream) + { + std::size_t lineNumber = 0; // current line number + enum Format { ASCII = 0, BINARY_LITTLE_ENDIAN = 1, BINARY_BIG_ENDIAN = 2}; + Format format = ASCII; + + std::string line; + std::istringstream iss; + + while(getline(stream,line)) + { + iss.clear(); + iss.str(line); + ++ lineNumber; + + // Reads file signature on first line + if(lineNumber == 1) + { + std::string signature; + if(!(iss >> signature) || (signature != "ply")) + { + // if wrong file format + std::cerr << "Error: incorrect file format line " << lineNumber << " of file" << std::endl; + return false; + } + } + + // Reads format on 2nd line + else if(lineNumber == 2) + { + std::string tag, format_string, version; + if( !(iss >> tag >> format_string >> version) ) + { + std::cerr << "Error line " << lineNumber << " of file" << std::endl; + return false; + } + if(format_string == "ascii") format = ASCII; + else if(format_string == "binary_little_endian") format = BINARY_LITTLE_ENDIAN; + else if(format_string == "binary_big_endian") format = BINARY_BIG_ENDIAN; + else + { + std::cerr << "Error: unknown file format \"" << format_string << "\" line " << lineNumber << std::endl; + return false; + } + } + + // Comments and vertex properties + else + { + std::string keyword; + if(!(iss >> keyword)) + { + std::cerr << "Error line " << lineNumber << " of file" << std::endl; + return false; + } + + if(keyword == "property") + { + std::string type, name; + if(!(iss >> type >> name)) + { + std::cerr << "Error line " << lineNumber << " of file" << std::endl; + return false; + } + + + if(type == "list") // Special case + { + std::string size_type = name; + std::string index_type; + name.clear(); + if(!(iss >> index_type >> name)) + { + std::cerr << "Error line " << lineNumber << " of file" << std::endl; + return false; + } + + TRY_TO_GENERATE_LIST_PROPERTY("char", "int8", boost::int8_t); + else TRY_TO_GENERATE_LIST_PROPERTY("uchar", "uint8", boost::uint8_t); + else TRY_TO_GENERATE_LIST_PROPERTY("short", "int16", boost::int16_t); + else TRY_TO_GENERATE_LIST_PROPERTY("ushort", "uint16", boost::uint16_t); + else TRY_TO_GENERATE_LIST_PROPERTY("int", "int32", boost::int32_t); + else TRY_TO_GENERATE_LIST_PROPERTY("uint", "uint32", boost::uint32_t); + else TRY_TO_GENERATE_LIST_PROPERTY("float", "float32", float); + else TRY_TO_GENERATE_LIST_PROPERTY("double", "float64", double); + } + else + { + TRY_TO_GENERATE_PROPERTY("char", "int8", boost::int8_t); + else TRY_TO_GENERATE_PROPERTY("uchar", "uint8", boost::uint8_t); + else TRY_TO_GENERATE_PROPERTY("short", "int16", boost::int16_t); + else TRY_TO_GENERATE_PROPERTY("ushort", "uint16", boost::uint16_t); + else TRY_TO_GENERATE_PROPERTY("int", "int32", boost::int32_t); + else TRY_TO_GENERATE_PROPERTY("uint", "uint32", boost::uint32_t); + else TRY_TO_GENERATE_PROPERTY("float", "float32", float); + else TRY_TO_GENERATE_PROPERTY("double", "float64", double); + } + + continue; + } + else if(keyword == "comment") + { + std::string str = iss.str(); + if(str.size() > 8) + { + std::copy(str.begin() + 8, str.end(), std::back_inserter(m_comments)); + m_comments += "\n"; + } + } + else if(keyword == "element") + { + std::string type; + std::size_t number; + if(!(iss >> type >> number)) + { + std::cerr << "Error line " << lineNumber << " of file" << std::endl; + return false; + } + + m_elements.push_back(PLY_element(type, number)); + } + // When end_header is reached, stop loop and begin reading points + else if(keyword == "end_header") + break; + } + } + return true; + } + + ~PLY_reader() + { + } + +}; + +template +void get_value(Reader& r, T& v, PLY_property& wrapper) +{ + return r.assign(v, wrapper.name); +} + + +template +struct Filler +{ + template + static void fill(Reader& r, Value_tuple& values, PLY_property_tuple wrappers) + { + get_value(r, std::get(values), std::get(wrappers)); + Filler::fill(r, values, wrappers); + } +}; + +template +struct seq { }; + +template +struct gens : gens { }; + +template +struct gens<0, S...> { + typedef seq type; +}; + +template +ValueType call_functor(Functor f, Tuple t, seq) { + return f(std::get(t) ...); +} + +template +ValueType call_functor(Functor f, std::tuple& t) +{ + return call_functor(f, t, typename gens::type()); +} + +template<> +struct Filler<0> +{ + template + static void fill(Reader& r, Value_tuple& values, PLY_property_tuple wrappers) + { + get_value(r, std::get<0>(values), std::get<2>(wrappers)); + } +}; + +template +void process_properties(PLY_element& element, OutputValueType& new_element, + std::tuple...>&& current) +{ + typedef typename PropertyMap::value_type PmapValueType; + std::tuple values; + Filler::fill(element, values, current); + PmapValueType new_value = call_functor(std::get<1>(current), values); + put(std::get<0>(current), new_element, new_value); +} + +template +void process_properties(PLY_element& element, OutputValueType& new_element, + std::tuple...>&& current, + NextPropertyBinder&& next, + PropertyMapBinders&& ... properties) +{ + typedef typename PropertyMap::value_type PmapValueType; + std::tuple values; + Filler::fill(element, values, current); + PmapValueType new_value = call_functor(std::get<1>(current), values); + put(std::get<0>(current), new_element, new_value); + + process_properties(element, new_element, std::forward(next), + std::forward(properties)...); +} + + +template +void process_properties(PLY_element& element, OutputValueType& new_element, + std::pair >&& current) +{ + T new_value = T(); + element.assign(new_value, current.second.name); + put(current.first, new_element, new_value); +} + +template +void process_properties(PLY_element& element, OutputValueType& new_element, + std::pair >&& current, + NextPropertyBinder&& next, + PropertyMapBinders&& ... properties) +{ + T new_value = T(); + element.assign(new_value, current.second.name); + put(current.first, new_element, new_value); + process_properties(element, new_element, std::forward(next), + std::forward(properties)...); +} + template bool read_PLY_faces(std::istream& in, internal::PLY::PLY_element& element, @@ -78,259 +735,7 @@ bool read_PLY_faces(std::istream& in, } } // namespace internal - -template -bool -read_PLY(std::istream& in, - std::vector< Point_3 >& points, - std::vector< Polygon_3 >& polygons, - bool /* verbose */ = false) -{ - if(!in) - { - std::cerr << "Error: cannot open file" << std::endl; - return false; - } - - internal::PLY::PLY_reader reader; - - if(!(reader.init(in))) - { - in.setstate(std::ios::failbit); - return false; - } - - for(std::size_t i = 0; i < reader.number_of_elements(); ++ i) - { - internal::PLY::PLY_element& element = reader.element(i); - - if(element.name() == "vertex" || element.name() == "vertices") - { - for(std::size_t j = 0; j < element.number_of_items(); ++ j) - { - for(std::size_t k = 0; k < element.number_of_properties(); ++ k) - { - internal::PLY::PLY_read_number* property = element.property(k); - property->get(in); - - if(in.fail()) - return false; - } - - Point_3 new_vertex; - - internal::PLY::process_properties(element, new_vertex, - make_ply_point_reader(CGAL::Identity_property_map())); - - points.push_back(get<0>(new_vertex)); - } - } - else if(element.name() == "face" || element.name() == "faces") - { - std::vector dummy; - - if(element.has_property >("vertex_indices")) - internal::read_PLY_faces(in, element, polygons, dummy, "vertex_indices"); - else if(element.has_property >("vertex_indices")) - internal::read_PLY_faces(in, element, polygons, dummy, "vertex_indices"); - else if(element.has_property >("vertex_index")) - internal::read_PLY_faces(in, element, polygons, dummy, "vertex_index"); - else if(element.has_property >("vertex_index")) - internal::read_PLY_faces(in, element, polygons, dummy, "vertex_index"); - else - { - std::cerr << "Error: can't find vertex indices in PLY input" << std::endl; - return false; - } - } - else // Read other elements and ignore - { - for(std::size_t j = 0; j < element.number_of_items(); ++ j) - { - for(std::size_t k = 0; k < element.number_of_properties(); ++ k) - { - internal::PLY::PLY_read_number* property = element.property(k); - property->get(in); - - if(in.fail()) - return false; - } - } - } - } - - return !in.bad(); -} - -template -bool read_PLY(std::istream& in, - std::vector< Point_3 >& points, - std::vector< Polygon_3 >& polygons, - std::vector >& hedges, - std::vector& fcolors, - std::vector& vcolors, - std::vector >& huvs, - bool /* verbose */ = false) -{ - if(!in) - { - std::cerr << "Error: cannot open file" << std::endl; - return false; - } - internal::PLY::PLY_reader reader; - - if(!(reader.init(in))) - { - in.setstate(std::ios::failbit); - return false; - } - for(std::size_t i = 0; i < reader.number_of_elements(); ++ i) - { - internal::PLY::PLY_element& element = reader.element(i); - - if(element.name() == "vertex" || element.name() == "vertices") - { - bool has_colors = false; - std::string rtag = "r", gtag = "g", btag = "b"; - if((element.has_property("red") || element.has_property("r")) && - (element.has_property("green") || element.has_property("g")) && - (element.has_property("blue") || element.has_property("b"))) - { - has_colors = true; - if(element.has_property("red")) - { - rtag = "red"; gtag = "green"; btag = "blue"; - } - } - - for(std::size_t j = 0; j < element.number_of_items(); ++ j) - { - for(std::size_t k = 0; k < element.number_of_properties(); ++ k) - { - internal::PLY::PLY_read_number* property = element.property(k); - property->get(in); - - if(in.fail()) - return false; - } - - std::tuple new_vertex; - - if(has_colors) - { - internal::PLY::process_properties(element, new_vertex, - make_ply_point_reader(CGAL::make_nth_of_tuple_property_map<0>(new_vertex)), - std::make_pair(CGAL::make_nth_of_tuple_property_map<1>(new_vertex), - PLY_property(rtag.c_str())), - std::make_pair(CGAL::make_nth_of_tuple_property_map<2>(new_vertex), - PLY_property(gtag.c_str())), - std::make_pair(CGAL::make_nth_of_tuple_property_map<3>(new_vertex), - PLY_property(btag.c_str()))); - - vcolors.push_back(Color_rgb(get<1>(new_vertex), get<2>(new_vertex), get<3>(new_vertex))); - } - else - internal::PLY::process_properties(element, new_vertex, - make_ply_point_reader(CGAL::make_nth_of_tuple_property_map<0>(new_vertex))); - - points.push_back(get<0>(new_vertex)); - } - } - else if(element.name() == "face" || element.name() == "faces") - { - if(element.has_property >("vertex_indices")) - internal::read_PLY_faces(in, element, polygons, fcolors, "vertex_indices"); - else if(element.has_property >("vertex_indices")) - internal::read_PLY_faces(in, element, polygons, fcolors, "vertex_indices"); - else if(element.has_property >("vertex_index")) - internal::read_PLY_faces(in, element, polygons, fcolors, "vertex_index"); - else if(element.has_property >("vertex_index")) - internal::read_PLY_faces(in, element, polygons, fcolors, "vertex_index"); - else - { - std::cerr << "Error: can't find vertex indices in PLY input" << std::endl; - return false; - } - } - else if(element.name() == "halfedge" ) - { - bool has_uv = false; - std::string stag = "source", ttag = "target", utag = "u", vtag = "v"; - if( element.has_property("source") && - element.has_property("target") && - element.has_property("u") && - element.has_property("v")) - { - has_uv = true; - } - cpp11::tuple new_hedge; - for(std::size_t j = 0; j < element.number_of_items(); ++ j) - { - for(std::size_t k = 0; k < element.number_of_properties(); ++ k) - { - internal::PLY::PLY_read_number* property = element.property(k); - property->get(in); - - if(in.eof()) - return false; - } - - if(has_uv) - { - internal::PLY::process_properties(element, new_hedge, - std::make_pair(CGAL::make_nth_of_tuple_property_map<0>(new_hedge), - PLY_property(stag.c_str())), - std::make_pair(CGAL::make_nth_of_tuple_property_map<1>(new_hedge), - PLY_property(ttag.c_str())), - std::make_pair(CGAL::make_nth_of_tuple_property_map<2>(new_hedge), - PLY_property(utag.c_str())), - std::make_pair(CGAL::make_nth_of_tuple_property_map<3>(new_hedge), - PLY_property(vtag.c_str()))); - hedges.push_back(std::make_pair(get<0>(new_hedge), get<1>(new_hedge))); - huvs.push_back(std::make_pair(get<2>(new_hedge), get<3>(new_hedge))); - } - else - { - internal::PLY::process_properties(element, new_hedge, - std::make_pair(CGAL::make_nth_of_tuple_property_map<0>(new_hedge), - PLY_property(stag.c_str())), - std::make_pair(CGAL::make_nth_of_tuple_property_map<1>(new_hedge), - PLY_property(ttag.c_str())) - ); - } - } - } - else // Read other elements and ignore - { - for(std::size_t j = 0; j < element.number_of_items(); ++ j) - { - for(std::size_t k = 0; k < element.number_of_properties(); ++ k) - { - internal::PLY::PLY_read_number* property = element.property(k); - property->get(in); - if(in.fail()) - return false; - } - } - } - } - return !in.bad(); -} - -template -bool read_PLY(std::istream& in, - std::vector< Point_3 >& points, - std::vector< Polygon_3 >& polygons, - std::vector& fcolors, - std::vector& vcolors, - bool /* verbose */ = false) -{ - std::vector > dummy_pui; - std::vector > dummy_pf; - - return read_PLY(in, points, polygons, dummy_pui, fcolors, vcolors, dummy_pf); -} - +} // namespace Stream_support } // namespace CGAL #endif // CGAL_IO_PLY_PLY_READER_H diff --git a/Stream_support/include/CGAL/IO/PLY/PLY_writer.h b/Stream_support/include/CGAL/IO/PLY/PLY_writer.h index 2e4edbbb761..02e166046ed 100644 --- a/Stream_support/include/CGAL/IO/PLY/PLY_writer.h +++ b/Stream_support/include/CGAL/IO/PLY/PLY_writer.h @@ -11,146 +11,293 @@ #ifndef CGAL_IO_PLY_PLY_WRITER_H #define CGAL_IO_PLY_PLY_WRITER_H -#include +#include namespace CGAL { +namespace internal { +namespace PLY { -template -bool write_PLY(std::ostream& out, - const std::vector< Point_3 >& points, - const std::vector< Polygon_3 >& polygons, - bool /* verbose */ = false) +template inline void property_header_type (std::ostream& stream) { - if(!out) - { - std::cerr << "Error: cannot open file" << std::endl; - return false; - } - - // Write header - out << "ply" << std::endl - << ((get_mode(out) == IO::BINARY) ? "format binary_little_endian 1.0" : "format ascii 1.0") << std::endl - << "comment Generated by the CGAL library" << std::endl - << "element vertex " << points.size() << std::endl; - - internal::PLY::output_property_header(out, make_ply_point_writer (CGAL::Identity_property_map())); - - out << "element face " << polygons.size() << std::endl; - - internal::PLY::output_property_header(out, std::make_pair(CGAL::Identity_property_map(), - PLY_property >("vertex_indices"))); - - out << "end_header" << std::endl; - - for (std::size_t i = 0; i < points.size(); ++ i) - internal::PLY::output_properties(out, points.begin() + i, - make_ply_point_writer (CGAL::Identity_property_map())); - - for (std::size_t i = 0; i < polygons.size(); ++ i) - internal::PLY::output_properties(out, polygons.begin() + i, - std::make_pair(CGAL::Identity_property_map(), - PLY_property >("vertex_indices"))); - - return out.good(); + CGAL_assertion_msg (false, "Unknown PLY type"); + stream << "undefined_type"; } -template -bool write_PLY(std::ostream& out, - const SurfaceMesh& mesh, - bool /* verbose */ = false) +template <> inline void property_header_type (std::ostream& stream) { stream << "char"; } +template <> inline void property_header_type (std::ostream& stream) { stream << "char"; } +template <> inline void property_header_type (std::ostream& stream) { stream << "uchar"; } +template <> inline void property_header_type (std::ostream& stream) { stream << "short"; } +template <> inline void property_header_type (std::ostream& stream) { stream << "ushort"; } +template <> inline void property_header_type (std::ostream& stream) { stream << "int"; } +template <> inline void property_header_type (std::ostream& stream) { stream << "uint"; } +template <> inline void property_header_type (std::ostream& stream) { stream << "float"; } +template <> inline void property_header_type (std::ostream& stream) { stream << "double"; } + +template +void property_header (std::ostream& stream, const PLY_property& prop) { - typedef typename boost::graph_traits::face_descriptor face_descriptor; - typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - typedef typename boost::graph_traits::vertex_descriptor vertex_descriptor; - typedef typename boost::property_map::type::value_type Point_3; - typedef typename SurfaceMesh::template Property_map > UV_map; + stream << "property "; + property_header_type(stream); + stream << " " << prop.name << std::endl; +} - UV_map h_uv; - bool has_texture; - boost::tie(h_uv, has_texture) = mesh.template property_map >("h:uv"); +template +void property_header (std::ostream& stream, const PLY_property >& prop) +{ + stream << "property list uchar "; + property_header_type(stream); + stream << " " << prop.name << std::endl; +} - if(!out) +template +struct Properties_header +{ + template + static void write(std::ostream& stream, PLY_property_tuple& wrappers) { - std::cerr << "Error: cannot open file" << std::endl; - return false; + Properties_header::write(stream, wrappers); + property_header (stream, std::get(wrappers)); } - - // Write header - out << "ply" << std::endl - << ((get_mode(out) == IO::BINARY) ? "format binary_little_endian 1.0" : "format ascii 1.0") << std::endl - << "comment Generated by the CGAL library" << std::endl - << "element vertex " << num_vertices(mesh) << std::endl; - - internal::PLY::output_property_header(out, - make_ply_point_writer (CGAL::Identity_property_map())); - - out << "element face " << num_faces(mesh) << std::endl; - - internal::PLY::output_property_header(out, - std::make_pair(CGAL::Identity_property_map >(), - PLY_property >("vertex_indices"))); - - if(has_texture) +}; +template <> +struct Properties_header<0> +{ + template + static void write(std::ostream& stream, PLY_property_tuple& wrappers) { - out << "element halfedge " << num_halfedges(mesh) << std::endl; - - internal::PLY::output_property_header(out, - std::make_pair(CGAL::Identity_property_map(), - PLY_property("source"))); - - internal::PLY::output_property_header(out, - std::make_pair(CGAL::Identity_property_map(), - PLY_property("target"))); - internal::PLY::output_property_header(out, - std::make_tuple (h_uv, - PLY_property("u"), - PLY_property("v"))); + property_header (stream, std::get<1>(wrappers)); } - out << "end_header" << std::endl; +}; - for(vertex_descriptor vd : vertices(mesh)) +template +void output_property_header(std::ostream& stream, + std::tuple... >&& current) +{ + Properties_header::write(stream, current); +} + +template +void output_property_header(std::ostream& stream, + std::pair >&& current) +{ + property_header(stream, current.second); +} + +template +void output_property_header(std::ostream& stream, + std::pair >&& current, + NextPropertyHandler&& next, + PropertyHandler&& ... properties) +{ + property_header(stream, current.second); + output_property_header(stream, std::forward(next), + std::forward(properties)...); +} +template +void output_property_header(std::ostream& stream, + std::tuple... >&& current, + NextPropertyHandler&& next, + PropertyHandler&& ... properties) +{ + Properties_header::write(stream, current); + output_property_header(stream, std::forward(next), + std::forward(properties)...); +} + +template +void property_write(std::ostream& stream, ForwardIterator it, PropertyMap map) +{ + stream << CGAL::oformat(get(map, *it)); +} + +template +inline T no_char_character(const T& t) { return t; } +inline int no_char_character(const char& t) { return int(t); } +inline int no_char_character(const signed char& t) { return int(t); } +inline int no_char_character(const unsigned char& t) { return int(t); } + +template +void simple_property_write(std::ostream& stream, + ForwardIterator it, + std::pair > map) +{ + if(CGAL::get_mode(stream) == CGAL::IO::ASCII) + stream << no_char_character(get(map.first, *it)); + else { - Point_3 p = get(get(CGAL::vertex_point, mesh), vd); - internal::PLY::output_properties(out, &p, - make_ply_point_writer (CGAL::Identity_property_map())); + typename PropertyMap::value_type value = get(map.first, *it); + stream.write(reinterpret_cast(&value), sizeof(value)); } +} - std::vector polygon; - for(face_descriptor fd : faces(mesh)) +template +void simple_property_write(std::ostream& stream, + ForwardIterator it, + std::pair > > map) +{ + const typename PropertyMap::reference value = get(map.first, *it); + + if(CGAL::get_mode(stream) == CGAL::IO::ASCII) { - polygon.clear(); - for(halfedge_descriptor hd : halfedges_around_face(halfedge(fd, mesh), mesh)) - polygon.push_back(get(get(boost::vertex_index, mesh), target(hd,mesh))); - - internal::PLY::output_properties(out, &polygon, - std::make_pair(CGAL::Identity_property_map >(), - PLY_property >("vertex_indices"))); + stream << value.size(); + for(std::size_t i = 0; i < value.size(); ++ i) + stream << " " << no_char_character(value[i]); } - - if(has_texture) + else { - for(halfedge_descriptor hd : halfedges(mesh)) + unsigned char size = static_cast(value.size()); + stream.write(reinterpret_cast(&size), sizeof(size)); + for(std::size_t i = 0; i < value.size(); ++ i) { - typedef std::tuple Super_tuple; - Super_tuple t = std::make_tuple(source(hd, mesh),target(hd, mesh), - h_uv[hd].first, - h_uv[hd].second); - - internal::PLY::output_properties(out, &t, - std::make_pair(Nth_of_tuple_property_map<0,Super_tuple>(), - PLY_property("source")), - std::make_pair(Nth_of_tuple_property_map<1,Super_tuple>(), - PLY_property("target")), - std::make_pair(Nth_of_tuple_property_map<2,Super_tuple>(), - PLY_property("u")), - std::make_pair(Nth_of_tuple_property_map<3,Super_tuple>(), - PLY_property("v"))); + T t = T(value[i]); + stream.write(reinterpret_cast(&t), sizeof(t)); } } - - return out.good(); } +template +void output_properties(std::ostream& stream, + ForwardIterator it, + std::tuple... >&& current) +{ + property_write(stream, it, std::get<0>(current)); + if(get_mode(stream) == CGAL::IO::ASCII) + stream << std::endl; +} + +template +void output_properties(std::ostream& stream, + ForwardIterator it, + std::pair >&& current) +{ + simple_property_write(stream, it, std::forward > >(current)); + if(get_mode(stream) == CGAL::IO::ASCII) + stream << std::endl; +} + +template +void output_properties(std::ostream& stream, + ForwardIterator it, + std::pair >&& current, + NextPropertyHandler&& next, + PropertyHandler&& ... properties) +{ + simple_property_write(stream, it, current); + if(get_mode(stream) == CGAL::IO::ASCII) + stream << " "; + output_properties(stream, it, std::forward(next), + std::forward(properties)...); +} + +template +void output_properties(std::ostream& stream, + ForwardIterator it, + std::tuple... >&& current, + NextPropertyHandler&& next, + PropertyHandler&& ... properties) +{ + property_write(stream, it, std::get<0>(current)); + if(get_mode(stream) == CGAL::IO::ASCII) + stream << " "; + output_properties(stream, it, std::forward(next), + std::forward(properties)...); +} + +// Printer classes used by Point_set_3 and Surface_mesh(translate a +// property map to a PLY property) + +template +class Abstract_property_printer +{ +public: + virtual ~Abstract_property_printer() { } + virtual void print(std::ostream& stream, const Index& index) = 0; +}; + +template +class Property_printer + : public Abstract_property_printer +{ + PropertyMap m_pmap; +public: + Property_printer(const PropertyMap& pmap) : m_pmap(pmap) { } + + virtual void print(std::ostream& stream, const Index& index) + { + stream << get(m_pmap, index); + } +}; + +template +class Simple_property_printer + : public Abstract_property_printer +{ + PropertyMap m_pmap; +public: + Simple_property_printer(const PropertyMap& pmap) : m_pmap(pmap) { } + + virtual void print(std::ostream& stream, const Index& index) + { + if(get_mode(stream) == CGAL::IO::ASCII) + stream << get(m_pmap, index); + else + { + Type t = Type(get(m_pmap, index)); + stream.write(reinterpret_cast(&t), sizeof(t)); + } + } +}; + +template +class Char_property_printer + : public Abstract_property_printer +{ + typedef typename PropertyMap::value_type Type; + PropertyMap m_pmap; +public: + Char_property_printer(const PropertyMap& pmap) : m_pmap(pmap) { } + + virtual void print(std::ostream& stream, const Index& index) + { + if(get_mode(stream) == CGAL::IO::ASCII) + stream << int(get(m_pmap, index)); + else + { + Type t = get(m_pmap, index); + stream.write(reinterpret_cast(&t), sizeof(t)); + } + } +}; + +} // namespace PLY +} // namespace internal } // namespace CGAL #endif // CGAL_IO_PLY_PLY_WRITER_H diff --git a/Stream_support/include/CGAL/IO/STL.h b/Stream_support/include/CGAL/IO/STL.h index 663db0a261a..fa504efb174 100644 --- a/Stream_support/include/CGAL/IO/STL.h +++ b/Stream_support/include/CGAL/IO/STL.h @@ -16,6 +16,166 @@ #define CGAL_IO_STL_H #include -#include + +namespace CGAL { + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +/// Read + +/*! + * \ingroup IOstreamFunctions + * + * reads the content of `input` into `points` and `facets`, in the STL format. + * + * \see \ref IOStreamSTL + */ +template +bool read_STL(std::istream& input, + PointRange& points, + TriangleRange& facets, + bool verbose = false) +{ + int pos = 0; + + // Ignore all initial whitespace + unsigned char c; + + while(input.read(reinterpret_cast(&c), sizeof(c))) + { + if(!isspace(c)) + { + input.unget(); // move back to the first interesting char + break; + } + ++pos; + } + + if(!input.good()) // reached the end + return true; + + // If we have gone beyond 80 characters and have not read anything yet, + // then this must be an ASCII file. + if(pos > 80) + return parse_ASCII_STL(input, points, facets, verbose); + + // We are within the first 80 characters, both ASCII and binary are possible + + // Read the 5 first characters to check if the first word is "solid" + std::string s; + + char word[6]; + if(input.read(reinterpret_cast(&word[0]), sizeof(c)) && + input.read(reinterpret_cast(&word[1]), sizeof(c)) && + input.read(reinterpret_cast(&word[2]), sizeof(c)) && + input.read(reinterpret_cast(&word[3]), sizeof(c)) && + input.read(reinterpret_cast(&word[4]), sizeof(c)) && + input.read(reinterpret_cast(&word[5]), sizeof(c))) + { + s = std::string(word, 5); + pos += 5; + } + else + return true; // empty file + + // If the first word is not 'solid', the file must be binary + if(s != "solid" || (word[5] !='\n' && word[5] != ' ')) + { + if(parse_binary_STL(input, points, facets, verbose)) + { + return true; + } + else + { + // If we failed to read it as a binary, try as ASCII just in case... + // The file does not start with 'solid' anyway, so it's fine to reset it. + input.clear(); + input.seekg(0, std::ios::beg); + return parse_ASCII_STL(input, points, facets, verbose); + } + } + + // Now, we have found the keyword "solid" which is supposed to indicate that the file is ASCII + input.clear(); + input.seekg(0, std::ios::beg); //the parser needs to read all "solid" to work correctly. + if(parse_ASCII_STL(input, points, facets, verbose)) + { + // correctly read the input as an ASCII file + return true; + } + else // Failed to read the ASCII file + { + // It might have actually have been a binary file... ? + return parse_binary_STL(input, points, facets, verbose); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////////// +/// Write + +/*! + * \ingroup IOstreamFunctions + * + * writes the content of `points` and `facets` in `out`, in the STL format. + * + * \see \ref IOStreamSTL + */ +template +std::ostream& write_STL(const PointRange& points, + const TriangleRange& facets, + std::ostream& out) +{ + typedef typename PointRange::value_type Point; + typedef typename CGAL::Kernel_traits::Kernel K; + typedef typename K::Vector_3 Vector_3; + + if (get_mode(out) == IO::BINARY) + { + out << "FileType: Binary "; + const boost::uint32_t N32 = static_cast(facets.size()); + out.write(reinterpret_cast(&N32), sizeof(N32)); + + for(auto face : facets) + { + const Point& p = points[face[0]]; + const Point& q = points[face[1]]; + const Point& r = points[face[2]]; + + Vector_3 n = collinear(p,q,r) ? Vector_3(1,0,0) : unit_normal(p,q,r); + + 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()), + static_cast(q.x()), static_cast(q.y()), static_cast(q.z()), + static_cast(r.x()), static_cast(r.y()), static_cast(r.z()) }; + + for (int i=0; i<12; ++i) + out.write(reinterpret_cast(&coords[i]), sizeof(coords[i])); + out << " "; + } + } + else + { + out << "solid\n"; + for(auto face : facets) + { + const Point& p = points[face[0]]; + const Point& q = points[face[1]]; + const Point& r = points[face[2]]; + + Vector_3 n = collinear(p,q,r) ? Vector_3(1,0,0) : unit_normal(p,q,r); + out << "facet normal " << n << "\nouter loop\n"; + out << "vertex " << p << "\n"; + out << "vertex " << q << "\n"; + out << "vertex " << r << "\n"; + out << "endloop\nendfacet\n"; + } + out << "endsolid\n"; + } + + return out; +} + +} // namespace CGAL #endif // CGAL_IO_STL_H diff --git a/Stream_support/include/CGAL/IO/STL/STL_reader.h b/Stream_support/include/CGAL/IO/STL/STL_reader.h index 9a74f1b5afd..844c07b7213 100644 --- a/Stream_support/include/CGAL/IO/STL/STL_reader.h +++ b/Stream_support/include/CGAL/IO/STL/STL_reader.h @@ -24,6 +24,8 @@ #include namespace CGAL { +namespace Stream_support { +namespace internal { template bool read_ASCII_facet(std::istream& input, @@ -282,87 +284,8 @@ bool parse_binary_STL(std::istream& input, return true; } - -template -bool read_STL(std::istream& input, - PointRange& points, - TriangleRange& facets, - bool verbose = false) -{ - int pos = 0; - - // Ignore all initial whitespace - unsigned char c; - - while(input.read(reinterpret_cast(&c), sizeof(c))) - { - if(!isspace(c)) - { - input.unget(); // move back to the first interesting char - break; - } - ++pos; - } - - if(!input.good()) // reached the end - return true; - - // If we have gone beyond 80 characters and have not read anything yet, - // then this must be an ASCII file. - if(pos > 80) - return parse_ASCII_STL(input, points, facets, verbose); - - // We are within the first 80 characters, both ASCII and binary are possible - - // Read the 5 first characters to check if the first word is "solid" - std::string s; - - char word[6]; - if(input.read(reinterpret_cast(&word[0]), sizeof(c)) && - input.read(reinterpret_cast(&word[1]), sizeof(c)) && - input.read(reinterpret_cast(&word[2]), sizeof(c)) && - input.read(reinterpret_cast(&word[3]), sizeof(c)) && - input.read(reinterpret_cast(&word[4]), sizeof(c)) && - input.read(reinterpret_cast(&word[5]), sizeof(c))) - { - s = std::string(word, 5); - pos += 5; - } - else - return true; // empty file - - // If the first word is not 'solid', the file must be binary - if(s != "solid" || (word[5] !='\n' && word[5] != ' ')) - { - if(parse_binary_STL(input, points, facets, verbose)) - { - return true; - } - else - { - // If we failed to read it as a binary, try as ASCII just in case... - // The file does not start with 'solid' anyway, so it's fine to reset it. - input.clear(); - input.seekg(0, std::ios::beg); - return parse_ASCII_STL(input, points, facets, verbose); - } - } - - // Now, we have found the keyword "solid" which is supposed to indicate that the file is ASCII - input.clear(); - input.seekg(0, std::ios::beg); //the parser needs to read all "solid" to work correctly. - if(parse_ASCII_STL(input, points, facets, verbose)) - { - // correctly read the input as an ASCII file - return true; - } - else // Failed to read the ASCII file - { - // It might have actually have been a binary file... ? - return parse_binary_STL(input, points, facets, verbose); - } -} - +} // namespace internal +} // namespace Stream_support } // namespace CGAL #endif // CGAL_IO_STL_STL_READER_H diff --git a/Stream_support/include/CGAL/IO/STL/STL_writer.h b/Stream_support/include/CGAL/IO/STL/STL_writer.h deleted file mode 100644 index c60ec20f8d6..00000000000 --- a/Stream_support/include/CGAL/IO/STL/STL_writer.h +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright (c) 2017 GeometryFactory -// -// This file is part of CGAL (www.cgal.org); -// -// $URL$ -// $Id$ -// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial -// -// Author(s) : Sebastien Loriot - -#ifndef CGAL_IO_STL_STL_WRITER_H -#define CGAL_IO_STL_STL_WRITER_H - -#include -#include - -#include - -namespace CGAL { - -template -std::ostream& write_STL(const PointRange& points, - const TriangleRange& facets, - std::ostream& out) -{ - typedef typename PointRange::value_type Point; - typedef typename CGAL::Kernel_traits::Kernel K; - typedef typename K::Vector_3 Vector_3; - - if (get_mode(out) == IO::BINARY) - { - out << "FileType: Binary "; - const boost::uint32_t N32 = static_cast(facets.size()); - out.write(reinterpret_cast(&N32), sizeof(N32)); - - for(auto face : facets) - { - const Point& p = points[face[0]]; - const Point& q = points[face[1]]; - const Point& r = points[face[2]]; - - Vector_3 n = collinear(p,q,r) ? Vector_3(1,0,0) : unit_normal(p,q,r); - - 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()), - static_cast(q.x()), static_cast(q.y()), static_cast(q.z()), - static_cast(r.x()), static_cast(r.y()), static_cast(r.z()) }; - - for (int i=0; i<12; ++i) - out.write(reinterpret_cast(&coords[i]), sizeof(coords[i])); - out << " "; - } - } - else - { - out << "solid\n"; - for(auto face : facets) - { - const Point& p = points[face[0]]; - const Point& q = points[face[1]]; - const Point& r = points[face[2]]; - - Vector_3 n = collinear(p,q,r) ? Vector_3(1,0,0) : unit_normal(p,q,r); - out << "facet normal " << n << "\nouter loop\n"; - out << "vertex " << p << "\n"; - out << "vertex " << q << "\n"; - out << "vertex " << r << "\n"; - out << "endloop\nendfacet\n"; - } - out << "endsolid\n"; - } - - return out; -} - -} // namespace CGAL - -#endif // CGAL_IO_STL_STL_WRITER_H diff --git a/Stream_support/include/CGAL/IO/VRML.h b/Stream_support/include/CGAL/IO/VRML.h index d7f969256d4..77618cfa208 100644 --- a/Stream_support/include/CGAL/IO/VRML.h +++ b/Stream_support/include/CGAL/IO/VRML.h @@ -16,7 +16,6 @@ #define CGAL_IO_VRML_H #include -#include #include #include diff --git a/Stream_support/include/CGAL/IO/VTK/VTK_reader.h b/Stream_support/include/CGAL/IO/VTK/VTK_reader.h new file mode 100644 index 00000000000..0a2078ed705 --- /dev/null +++ b/Stream_support/include/CGAL/IO/VTK/VTK_reader.h @@ -0,0 +1,16 @@ +// Copyright (c) 2018 +// GeometryFactory (France) All rights reserved. +// +// This file is part of CGAL (www.cgal.org) +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Stephane Tayeb + +#ifndef CGAL_IO_VTK_READER_H +#define CGAL_IO_VTK_READER_H + +#endif // CGAL_IO_VTK_READER_H diff --git a/Stream_support/include/CGAL/IO/VTK/VTK_writer.h b/Stream_support/include/CGAL/IO/VTK/VTK_writer.h new file mode 100644 index 00000000000..6271cdde7e9 --- /dev/null +++ b/Stream_support/include/CGAL/IO/VTK/VTK_writer.h @@ -0,0 +1,95 @@ +// Copyright (c) 2018 +// GeometryFactory (France) All rights reserved. +// +// This file is part of CGAL (www.cgal.org) +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial +// +// +// Author(s) : Stephane Tayeb + +#ifndef CGAL_IO_VTK_WRITER_H +#define CGAL_IO_VTK_WRITER_H + +namespace CGAL { +namespace Stream_support { +namespace internal { + +template +void write_vector(std::ostream& os, + const std::vector& vect) +{ + const char* buffer = reinterpret_cast(&(vect[0])); + std::size_t size = vect.size()*sizeof(FT); + + os.write(reinterpret_cast(&size), sizeof(std::size_t)); // number of bytes encoded + os.write(buffer, vect.size()*sizeof(FT)); // encoded data +} + +class ErrorObserverVtk + : public vtkCommand +{ +public: + ErrorObserverVtk() + : Error(false), Warning(false), ErrorMessage(""), WarningMessage("") + { } + + static ErrorObserverVtk *New() { return new ErrorObserverVtk; } + + bool GetError() const { return this->Error; } + bool GetWarning() const { return this->Warning; } + std::string GetErrorMessage() { return ErrorMessage; } + std::string GetWarningMessage() { return WarningMessage; } + + void Clear() + { + this->Error = false; + this->Warning = false; + this->ErrorMessage = ""; + this->WarningMessage = ""; + } + + virtual void Execute(vtkObject *vtkNotUsed(caller), + unsigned long event, + void *calldata) + { + switch (event) + { + case vtkCommand::ErrorEvent: + ErrorMessage = static_cast(calldata); + this->Error = true; + break; + case vtkCommand::WarningEvent: + WarningMessage = static_cast(calldata); + this->Warning = true; + break; + } + } + +private: + bool Error; + bool Warning; + std::string ErrorMessage; + std::string WarningMessage; +}; + +template +vtkSmartPointer +read_vtk_file(const std::string& input_filename, + vtkSmartPointer errorObserver) +{ + vtkSmartPointer reader = vtkSmartPointer::New(); + reader->AddObserver(vtkCommand::ErrorEvent, errorObserver); + reader->AddObserver(vtkCommand::WarningEvent, errorObserver); + reader->SetFileName(input_filename.data()); + reader->Update(); + return reader; +} + +} // namespace internal +} // namespace Stream_support +} // namespace CGAL + +#endif // CGAL_IO_VTK_WRITER_H diff --git a/Stream_support/include/CGAL/IO/VTK/write_vtk.h b/Stream_support/include/CGAL/IO/VTK/write_vtk.h deleted file mode 100644 index 4adeaae958c..00000000000 --- a/Stream_support/include/CGAL/IO/VTK/write_vtk.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) 2018 -// GeometryFactory (France) All rights reserved. -// -// This file is part of CGAL (www.cgal.org) -// -// $URL$ -// $Id$ -// SPDX-License-Identifier: LGPL-3.0-or-later OR LicenseRef-Commercial -// -// -// Author(s) : Stephane Tayeb - -#ifndef CGAL_WRITE_VTK_IO_H -#define CGAL_WRITE_VTK_IO_H - -#include - -#include -#include - -template -void write_vector(std::ostream& os, - const std::vector& vect) -{ - const char* buffer = reinterpret_cast(&(vect[0])); - std::size_t size = vect.size()*sizeof(FT); - - os.write(reinterpret_cast(&size), sizeof(std::size_t)); // number of bytes encoded - os.write(buffer, vect.size()*sizeof(FT)); // encoded data -} -#endif diff --git a/Stream_support/include/CGAL/IO/WKT.h b/Stream_support/include/CGAL/IO/WKT.h index a9707d7c904..20d103034d8 100644 --- a/Stream_support/include/CGAL/IO/WKT.h +++ b/Stream_support/include/CGAL/IO/WKT.h @@ -15,13 +15,13 @@ #ifndef CGAL_IO_WKT_H #define CGAL_IO_WKT_H +#if defined(DOXYGEN_RUNNING) || (BOOST_VERSION >= 105600 && (!defined(BOOST_GCC) || BOOST_GCC >= 40500)) + #include #include #include #include -#if BOOST_VERSION >= 105600 && (! defined(BOOST_GCC) || BOOST_GCC >= 40500) - #include #include #include @@ -614,7 +614,7 @@ std::istream& read_WKT(std::istream& input, return input; } -}//end CGAL +} // namespace CGAL CGAL #endif // BOOST VERSION CHECKS #endif // CGAL_IO_WKT_H diff --git a/Stream_support/include/CGAL/IO/WKT/traits_point_3.h b/Stream_support/include/CGAL/IO/WKT/traits_point_3.h index 9aaa8b852d3..20030835d45 100644 --- a/Stream_support/include/CGAL/IO/WKT/traits_point_3.h +++ b/Stream_support/include/CGAL/IO/WKT/traits_point_3.h @@ -27,7 +27,7 @@ namespace boost { namespace geometry { namespace traits { -//WKT traits for Points +// WKT traits for Points template< typename K > struct tag > { diff --git a/Stream_support/include/CGAL/IO/io.h b/Stream_support/include/CGAL/IO/io.h index 5441dcd33a7..06922eacbb7 100644 --- a/Stream_support/include/CGAL/IO/io.h +++ b/Stream_support/include/CGAL/IO/io.h @@ -47,14 +47,14 @@ public: }; /*! - \ingroup PkgStreamSupportRef +\ingroup PkgStreamSupportRef All classes in the \cgal `Kernel` provide input and output operators for IOStreams. The basic task of such an operator is to produce a representation of an object that can be written as a sequence of characters on devices as a console, a file, or a pipe. The enum `Mode` distinguish between three different printing formats. -In `ASCII` mode, numbers +In `ASCII` mode, numbers e.g. the coordinates of a point or the coefficients of a line, are written in a machine independent format.