Continue reorganization of CGAL I/O

This commit is contained in:
Mael Rouxel-Labbé 2020-01-23 16:33:23 +01:00
parent 3c833674fb
commit 6a5972cc3b
22 changed files with 2220 additions and 2106 deletions

View File

@ -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 <class TriangleMesh>
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<typename boost::property_map<TriangleMesh, CGAL::vertex_point_t>::type>::value_type Point_3;
STL_internal::STL_builder<TriangleMesh, Point_3> 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 Facegraph, class P>
class OBJ_builder : public CGAL::internal::IO::Generic_facegraph_builder<Facegraph, P, OBJ_builder<Facegraph, P> >

View File

@ -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).
<th colspan="1">\ref IOStreamOFF "OFF"</th>
<th colspan="1">\ref IOStreamSTL "STL"</th>
<th colspan="1">\ref IOStreamOBJ "OBJ"</th>
<th colspan="1">\ref IOStreamPLY "PLY"</th>
</tr>
<tr>
<th colspan="1"></th>
<th colspan="1">ASCII</th>
<th colspan="1">ASCII</th>
<th colspan="1">BINARY</th>
<th colspan="1">ASCII / BINARY</th>
</tr>
<tr>
<td>Export</td>
<td>write_OFF()</td>
<td> \ref IOstreamFunctions "write_STL()" </td>
<td>n/a</td>
<td>write_STL()</td>
<td> - </td>
<td>write_PLY()</td>
</tr>
<tr>
<td>Import</td>
<td>read_OFF()</td>
<td>\ref IOstreamFunctions "read_STL()"</td>
<td>\ref IOstreamFunctions "read_OBJ()"</td>
<td>read_STL()</td>
<td>read_OBJ()</td>
<td>read_PLY()</td>
</tr>
</table>
\subsection IOstreamPointSetIO Point Set IO

View File

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

View File

@ -11,7 +11,567 @@
#ifndef CGAL_IO_3MF_H
#define CGAL_IO_3MF_H
#include <CGAL/boost/graph/iterator.h>
#include <CGAL/IO/Color.h>
#include <CGAL/IO/3MF/read_3mf.h>
#include <CGAL/IO/3MF/write_3mf.h>
#include <Model/COM/NMR_DLLInterfaces.h>
#include <functional>
#include <iostream>
#include <string>
#include <vector>
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<typename PointRanges, typename PolygonRanges, typename ColorRanges>
int read_triangle_soups_from_3mf(const std::string& file_name,
PointRanges& all_points,
PolygonRanges& all_polygons,
ColorRanges& all_colors,
std::vector<std::string>& names)
{
typedef typename PointRanges::value_type PointRange;
typedef typename PolygonRanges::value_type PolygonRange;
typedef typename ColorRanges::value_type ColorRange;
return read_from_3mf<PointRanges,PolygonRanges,ColorRanges,
PointRange, PolygonRange, ColorRange>
(file_name, all_points, all_polygons, all_colors, names,
extract_soups<PointRange, PolygonRange, ColorRange>);
}
template<typename PointRanges, typename ColorRanges>
int read_polylines_from_3mf(const std::string& file_name,
PointRanges& all_points,
ColorRanges& all_colors,
std::vector<std::string>& names)
{
typedef typename PointRanges::value_type PointRange;
typedef std::vector<std::size_t> Polygon;
typedef std::vector<Polygon> PolygonRange;
typedef std::vector<CGAL::Color> ColorRange;
std::vector<PolygonRange> all_polygons;
return read_from_3mf<PointRanges, std::vector<PolygonRange>,
std::vector<ColorRange>, PointRange, PolygonRange, ColorRange>
(file_name, all_points, all_polygons, all_colors, names,
extract_polylines<PointRange, PolygonRange, ColorRange>);
}
template<typename PointRanges, typename ColorRanges>
int read_point_clouds_from_3mf(const std::string& file_name,
PointRanges& all_points,
ColorRanges& all_colors,
std::vector<std::string>& names)
{
typedef typename PointRanges::value_type PointRange;
typedef std::vector<std::size_t> Polygon;
typedef std::vector<Polygon> PolygonRange;
typedef std::vector<CGAL::Color> ColorRange;
std::vector<PolygonRange> all_polygons;
return read_from_3mf<PointRanges, std::vector<PolygonRange>,
std::vector<ColorRange>, PointRange, PolygonRange, ColorRange>
(file_name, all_points, all_polygons, all_colors, names,
extract_point_clouds<PointRange, PolygonRange, ColorRange>);
}
template<typename PointRanges, typename PolygonRanges, typename ColorRanges,
typename PointRange, typename PolygonRange, typename ColorRange>
int read_from_3mf(const std::string& file_name,
PointRanges& all_points,
PolygonRanges& all_polygons,
ColorRanges& all_colors,
std::vector<std::string>& names,
std::function<bool(NMR::PLib3MFModelMeshObject*,
const NMR::MODELTRANSFORM&,
PointRange&,
PolygonRange&,
ColorRange&,
std::string&)> 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<typename PointRanges, typename PolygonRanges>
bool write_triangle_soups_to_3mf(const std::string& file_name,
const PointRanges& all_points,
const PolygonRanges& all_polygons,
const std::vector<std::string>& 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<CGAL::Color> 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<typename TriangleMeshRange>
bool write_triangle_meshes_to_3mf(const std::string& file_name,
const TriangleMeshRange& tms,
const std::vector<std::string>& names)
{
typedef typename TriangleMeshRange::value_type Mesh;
typedef typename boost::property_map<Mesh, boost::vertex_point_t>::type VPMap;
typedef typename boost::property_traits<VPMap>::value_type Point_3;
typedef boost::graph_traits<Mesh>::vertex_descriptor vertex_descriptor;
typedef boost::graph_traits<Mesh>::face_descriptor face_descriptor;
typedef std::vector<std::size_t> Polygon;
typedef std::vector<Polygon> PolygonRange;
typedef std::vector<Point_3> PointRange;
std::vector<PointRange> all_points;
std::vector<PolygonRange> 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<typename boost::graph_traits<Mesh>::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

View File

@ -285,412 +285,6 @@ bool extract_point_clouds (NMR::PLib3MFModelMeshObject *pMeshObject,
return true;
}
template<typename PointRanges, typename PolygonRanges, typename ColorRanges,
typename PointRange, typename PolygonRange, typename ColorRange>
int read_from_3mf(const std::string& file_name, PointRanges& all_points,
PolygonRanges& all_polygons, ColorRanges& all_colors,
std::vector<std::string>& names,
std::function<bool(NMR::PLib3MFModelMeshObject*,
const NMR::MODELTRANSFORM&,
PointRange&,
PolygonRange&,
ColorRange&,
std::string&)> 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<typename PointRanges, typename PolygonRanges, typename ColorRanges>
int read_triangle_soups_from_3mf(const std::string& file_name, PointRanges& all_points,
PolygonRanges& all_polygons, ColorRanges& all_colors,
std::vector<std::string>& names)
{
typedef typename PointRanges::value_type PointRange;
typedef typename PolygonRanges::value_type PolygonRange;
typedef typename ColorRanges::value_type ColorRange;
return read_from_3mf<PointRanges,PolygonRanges,ColorRanges,
PointRange, PolygonRange, ColorRange>
(file_name, all_points, all_polygons, all_colors, names,
extract_soups<PointRange, PolygonRange, ColorRange>);
}
template<typename PointRanges, typename ColorRanges>
int read_polylines_from_3mf(const std::string& file_name,
PointRanges& all_points,
ColorRanges& all_colors,
std::vector<std::string>& names)
{
typedef typename PointRanges::value_type PointRange;
typedef std::vector<std::size_t> Polygon;
typedef std::vector<Polygon> PolygonRange;
typedef std::vector<CGAL::Color> ColorRange;
std::vector<PolygonRange> all_polygons;
return read_from_3mf<PointRanges,std::vector<PolygonRange>,
std::vector<ColorRange>, PointRange, PolygonRange, ColorRange>
(file_name, all_points, all_polygons, all_colors, names,
extract_polylines<PointRange, PolygonRange, ColorRange>);
}
template<typename PointRanges, typename ColorRanges>
int read_point_clouds_from_3mf(const std::string& file_name,
PointRanges& all_points,
ColorRanges& all_colors,
std::vector<std::string>& names)
{
typedef typename PointRanges::value_type PointRange;
typedef std::vector<std::size_t> Polygon;
typedef std::vector<Polygon> PolygonRange;
typedef std::vector<CGAL::Color> ColorRange;
std::vector<PolygonRange> all_polygons;
return read_from_3mf<PointRanges,std::vector<PolygonRange>,
std::vector<ColorRange>, PointRange, PolygonRange, ColorRange>
(file_name, all_points, all_polygons, all_colors, names,
extract_point_clouds<PointRange, PolygonRange, ColorRange>);
}
} // namespace CGAL
#endif // CGAL_IO_READ_3MF_H

View File

@ -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<typename PointRanges, typename PolygonRanges>
bool write_triangle_soups_to_3mf(const std::string& file_name,
const PointRanges& all_points,
const PolygonRanges& all_polygons,
const std::vector<std::string>& 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<CGAL::Color> 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<typename TriangleMeshRange>
bool write_triangle_meshes_to_3mf(const std::string& file_name,
const TriangleMeshRange& tms,
const std::vector<std::string>& names)
{
typedef typename TriangleMeshRange::value_type Mesh;
typedef typename boost::property_map<Mesh, boost::vertex_point_t>::type VPMap;
typedef typename boost::property_traits<VPMap>::value_type Point_3;
typedef boost::graph_traits<Mesh>::vertex_descriptor vertex_descriptor;
typedef boost::graph_traits<Mesh>::face_descriptor face_descriptor;
typedef std::vector<std::size_t> Polygon;
typedef std::vector<Polygon> PolygonRange;
typedef std::vector<Point_3> PointRange;
std::vector<PointRange> all_points;
std::vector<PolygonRange> 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<typename boost::graph_traits<Mesh>::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

View File

@ -15,7 +15,7 @@
// Author(s) : Lutz Kettner <kettner@mpi-sb.mpg.de>
#ifndef CGAL_IO_GENERIC_WRITER_H
#define CGAL_IO_GENERIC_WRITER_H 1
#define CGAL_IO_GENERIC_WRITER_H
#include <CGAL/basic.h>
#include <iterator>

View File

@ -15,20 +15,79 @@
#define CGAL_IO_OBJ_H
#include <CGAL/IO/OBJ/File_writer_wavefront.h>
#include <CGAL/IO/OBJ/OBJ_reader.h>
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 <class Points_3, class Faces>
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<std::size_t>());
while(iss >> i)
{
if(i < 1)
{
faces.back().push_back(points.size()+i);//negative indices are relative references
if(i<mini)
mini=i;
}
else
{
faces.back().push_back(i-1);
if(i-1 > 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<int>(points.size()))
{
std::cerr << "a face index is invalid " << std::endl;
return false;
}
return true;
}
} // namespace CGAL

View File

@ -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 <istream>
#include <sstream>
#include <vector>
#include <limits>
namespace CGAL {
// Points_3 a RandomAccessContainer of Point_3,
// Faces a RandomAccessContainer of RandomAccessContainer of std::size_t
template <class Points_3, class 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<std::size_t>());
while(iss >> i)
{
if(i < 1)
{
faces.back().push_back(points.size()+i);//negative indices are relative references
if(i<mini)
mini=i;
}
else
{
faces.back().push_back(i-1);
if(i-1 > 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<int>(points.size()))
{
std::cerr<<"a face index is invalid "<<std::endl;
return false;
}
return true;
}
} // namespace CGAL
#endif // CGAL_IO_OBJ_OBJ_READER_H

File diff suppressed because it is too large Load Diff

View File

@ -11,11 +11,668 @@
#ifndef CGAL_IO_PLY_PLY_READER_H
#define CGAL_IO_PLY_PLY_READER_H
#include <CGAL/IO/PLY.h>
#include <CGAL/Kernel_traits.h>
#include <CGAL/IO/io.h>
#include <CGAL/property_map.h>
#include <boost/cstdint.hpp>
#include <iostream>
#include <sstream>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#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 <typename T>
struct PLY_property
{
typedef T type;
const char* name;
PLY_property(const char* name) : name(name) { }
};
// Use a double property for all kernels...
template <typename FT> struct Convert_FT { typedef double type; };
// ...except if kernel uses type float
template <> struct Convert_FT<float> { typedef float type; };
template <typename PointOrVectorMap>
struct Get_FT_from_map
{
typedef typename Convert_FT
<typename Kernel_traits
<typename boost::property_traits
<PointOrVectorMap>::value_type>::Kernel::FT>::type type;
};
template <typename PointMap>
std::tuple<PointMap,
typename Kernel_traits<typename PointMap::value_type>::Kernel::Construct_point_3,
PLY_property<typename Get_FT_from_map<PointMap>::type>,
PLY_property<typename Get_FT_from_map<PointMap>::type>,
PLY_property<typename Get_FT_from_map<PointMap>::type> >
make_ply_point_reader(PointMap point_map)
{
return std::make_tuple(point_map, typename Kernel_traits<typename PointMap::value_type>::Kernel::Construct_point_3(),
PLY_property<typename Get_FT_from_map<PointMap>::type>("x"),
PLY_property<typename Get_FT_from_map<PointMap>::type>("y"),
PLY_property<typename Get_FT_from_map<PointMap>::type>("z"));
}
template <typename VectorMap>
std::tuple<VectorMap,
typename Kernel_traits<typename VectorMap::value_type>::Kernel::Construct_vector_3,
PLY_property<typename Get_FT_from_map<VectorMap>::type>,
PLY_property<typename Get_FT_from_map<VectorMap>::type>,
PLY_property<typename Get_FT_from_map<VectorMap>::type> >
make_ply_normal_reader(VectorMap normal_map)
{
return std::make_tuple(normal_map, typename Kernel_traits<typename VectorMap::value_type>::Kernel::Construct_vector_3(),
PLY_property<typename Get_FT_from_map<VectorMap>::type>("nx"),
PLY_property<typename Get_FT_from_map<VectorMap>::type>("ny"),
PLY_property<typename Get_FT_from_map<VectorMap>::type>("nz"));
}
template <typename PointMap>
std::tuple<PointMap,
PLY_property<typename Get_FT_from_map<PointMap>::type>,
PLY_property<typename Get_FT_from_map<PointMap>::type>,
PLY_property<typename Get_FT_from_map<PointMap>::type> >
make_ply_point_writer(PointMap point_map)
{
return std::make_tuple(point_map,
PLY_property<typename Get_FT_from_map<PointMap>::type>("x"),
PLY_property<typename Get_FT_from_map<PointMap>::type>("y"),
PLY_property<typename Get_FT_from_map<PointMap>::type>("z"));
}
template <typename VectorMap>
std::tuple<VectorMap,
PLY_property<typename Get_FT_from_map<VectorMap>::type>,
PLY_property<typename Get_FT_from_map<VectorMap>::type>,
PLY_property<typename Get_FT_from_map<VectorMap>::type> >
make_ply_normal_writer(VectorMap normal_map)
{
return std::make_tuple(normal_map,
PLY_property<typename Get_FT_from_map<VectorMap>::type>("nx"),
PLY_property<typename Get_FT_from_map<VectorMap>::type>("ny"),
PLY_property<typename Get_FT_from_map<VectorMap>::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<char>(s);
}
void read_ascii(std::istream& stream, signed char& c) const
{
short s;
stream >> s;
c = static_cast<signed char>(s);
}
void read_ascii(std::istream& stream, unsigned char& c) const
{
unsigned short s;
stream >> s;
c = static_cast<unsigned char>(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 <typename Type>
void read_ascii(std::istream& stream, Type& t) const
{
stream >> t;
}
template <typename Type>
Type read(std::istream& stream) const
{
if(m_format == 0) // Ascii
{
Type t;
read_ascii(stream, t);
return t;
}
else // Binary (2 = little endian)
{
union
{
char uChar[sizeof(Type)];
Type type;
} buffer;
std::size_t size = sizeof(Type);
stream.read(buffer.uChar, size);
if(m_format == 2) // Big endian
{
for(std::size_t i = 0; i < size / 2; ++ i)
{
unsigned char tmp = buffer.uChar[i];
buffer.uChar[i] = buffer.uChar[size - 1 - i];
buffer.uChar[size - 1 - i] = tmp;
}
}
return buffer.type;
}
return Type();
}
};
template <typename Type>
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<Type>(stream));
}
const Type& buffer() const
{
return m_buffer;
}
};
template <typename Type>
class PLY_read_typed_list : public PLY_read_number
{
protected:
mutable std::vector<Type> 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<Type>& buffer() const
{
return m_buffer;
}
};
template <typename SizeType, typename IndexType>
class PLY_read_typed_list_with_typed_size
: public PLY_read_typed_list<IndexType>
{
public:
PLY_read_typed_list_with_typed_size(std::string name, std::size_t format)
: PLY_read_typed_list<IndexType>(name, format)
{
}
void get(std::istream& stream) const
{
std::size_t size = static_cast<std::size_t>(this->template read<SizeType>(stream));
this->m_buffer.resize(size);
for(std::size_t i = 0; i < size; ++ i)
this->m_buffer[i] = this->template read<IndexType>(stream);
}
};
class PLY_element
{
std::string m_name;
std::size_t m_number;
std::vector<PLY_read_number*> 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<PLY_element&>(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<PLY_element&>(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 <typename Type>
bool has_property(const char* tag)
{
return has_property(tag, Type());
}
template <typename Type>
bool has_property(const char* tag, const std::vector<Type>&)
{
for(std::size_t i = 0; i < number_of_properties(); ++ i)
if(m_properties[i]->name() == tag)
return (dynamic_cast<PLY_read_typed_list<Type>*>(m_properties[i]) != nullptr);
return false;
}
template <typename Type>
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<PLY_read_typed_number<Type>*>(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<PLY_read_typed_number<double>*>(m_properties[i]) != nullptr
|| dynamic_cast<PLY_read_typed_number<float>*>(m_properties[i]) != nullptr);
return false;
}
template <typename Type>
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<Type>*
property = dynamic_cast<PLY_read_typed_number<Type>*>(m_properties[i]);
CGAL_assertion(property != nullptr);
t = property->buffer();
return;
}
t = {};
}
template <typename Type>
void assign(std::vector<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_list<Type>*
property = dynamic_cast<PLY_read_typed_list<Type>*>(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<double>*
property_double = dynamic_cast<PLY_read_typed_number<double>*>(m_properties[i]);
if(property_double == nullptr)
{
PLY_read_typed_number<float>*
property_float = dynamic_cast<PLY_read_typed_number<float>*>(m_properties[i]);
CGAL_assertion(property_float != nullptr);
t = property_float->buffer();
}
else
t = property_double->buffer();
return;
}
t = {};
}
};
class PLY_reader
{
std::vector<PLY_element> 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 <typename Stream>
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 <class Reader, class T>
void get_value(Reader& r, T& v, PLY_property<T>& wrapper)
{
return r.assign(v, wrapper.name);
}
template <std::size_t N>
struct Filler
{
template <class Reader, class Value_tuple, class PLY_property_tuple>
static void fill(Reader& r, Value_tuple& values, PLY_property_tuple wrappers)
{
get_value(r, std::get<N>(values), std::get<N+2>(wrappers));
Filler<N-1>::fill(r, values, wrappers);
}
};
template<int ...>
struct seq { };
template<int N, int ...S>
struct gens : gens<N-1, N-1, S...> { };
template<int ...S>
struct gens<0, S...> {
typedef seq<S...> type;
};
template<class ValueType, class Functor, class Tuple, int ...S>
ValueType call_functor(Functor f, Tuple t, seq<S...>) {
return f(std::get<S>(t) ...);
}
template <class ValueType, class Functor, typename ... T>
ValueType call_functor(Functor f, std::tuple<T...>& t)
{
return call_functor<ValueType>(f, t, typename gens<sizeof...(T)>::type());
}
template<>
struct Filler<0>
{
template <class Reader, class Value_tuple, class PLY_property_tuple>
static void fill(Reader& r, Value_tuple& values, PLY_property_tuple wrappers)
{
get_value(r, std::get<0>(values), std::get<2>(wrappers));
}
};
template <typename OutputValueType,
typename PropertyMap,
typename Constructor,
typename ... T>
void process_properties(PLY_element& element, OutputValueType& new_element,
std::tuple<PropertyMap, Constructor, PLY_property<T>...>&& current)
{
typedef typename PropertyMap::value_type PmapValueType;
std::tuple<T...> values;
Filler<sizeof...(T)-1>::fill(element, values, current);
PmapValueType new_value = call_functor<PmapValueType>(std::get<1>(current), values);
put(std::get<0>(current), new_element, new_value);
}
template <typename OutputValueType,
typename PropertyMap,
typename Constructor,
typename ... T,
typename NextPropertyBinder,
typename ... PropertyMapBinders>
void process_properties(PLY_element& element, OutputValueType& new_element,
std::tuple<PropertyMap, Constructor, PLY_property<T>...>&& current,
NextPropertyBinder&& next,
PropertyMapBinders&& ... properties)
{
typedef typename PropertyMap::value_type PmapValueType;
std::tuple<T...> values;
Filler<sizeof...(T)-1>::fill(element, values, current);
PmapValueType new_value = call_functor<PmapValueType>(std::get<1>(current), values);
put(std::get<0>(current), new_element, new_value);
process_properties(element, new_element, std::forward<NextPropertyBinder>(next),
std::forward<PropertyMapBinders>(properties)...);
}
template <typename OutputValueType, typename PropertyMap, typename T>
void process_properties(PLY_element& element, OutputValueType& new_element,
std::pair<PropertyMap, PLY_property<T> >&& current)
{
T new_value = T();
element.assign(new_value, current.second.name);
put(current.first, new_element, new_value);
}
template <typename OutputValueType, typename PropertyMap, typename T,
typename NextPropertyBinder, typename ... PropertyMapBinders>
void process_properties(PLY_element& element, OutputValueType& new_element,
std::pair<PropertyMap, PLY_property<T> >&& 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<NextPropertyBinder>(next),
std::forward<PropertyMapBinders>(properties)...);
}
template <typename Integer, class Polygon_3, class Color_rgb>
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 <class Point_3, class Polygon_3>
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<Point_3>()));
points.push_back(get<0>(new_vertex));
}
}
else if(element.name() == "face" || element.name() == "faces")
{
std::vector<CGAL::Color> dummy;
if(element.has_property<std::vector<boost::int32_t> >("vertex_indices"))
internal::read_PLY_faces<boost::int32_t>(in, element, polygons, dummy, "vertex_indices");
else if(element.has_property<std::vector<boost::uint32_t> >("vertex_indices"))
internal::read_PLY_faces<boost::uint32_t>(in, element, polygons, dummy, "vertex_indices");
else if(element.has_property<std::vector<boost::int32_t> >("vertex_index"))
internal::read_PLY_faces<boost::int32_t>(in, element, polygons, dummy, "vertex_index");
else if(element.has_property<std::vector<boost::uint32_t> >("vertex_index"))
internal::read_PLY_faces<boost::uint32_t>(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 <class Point_3, class Polygon_3, class Color_rgb>
bool read_PLY(std::istream& in,
std::vector< Point_3 >& points,
std::vector< Polygon_3 >& polygons,
std::vector<std::pair<unsigned int, unsigned int> >& hedges,
std::vector<Color_rgb>& fcolors,
std::vector<Color_rgb>& vcolors,
std::vector<std::pair<float, float> >& 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<boost::uint8_t>("red") || element.has_property<boost::uint8_t>("r")) &&
(element.has_property<boost::uint8_t>("green") || element.has_property<boost::uint8_t>("g")) &&
(element.has_property<boost::uint8_t>("blue") || element.has_property<boost::uint8_t>("b")))
{
has_colors = true;
if(element.has_property<boost::uint8_t>("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<Point_3, boost::uint8_t, boost::uint8_t, boost::uint8_t> 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<boost::uint8_t>(rtag.c_str())),
std::make_pair(CGAL::make_nth_of_tuple_property_map<2>(new_vertex),
PLY_property<boost::uint8_t>(gtag.c_str())),
std::make_pair(CGAL::make_nth_of_tuple_property_map<3>(new_vertex),
PLY_property<boost::uint8_t>(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<std::vector<boost::int32_t> >("vertex_indices"))
internal::read_PLY_faces<boost::int32_t>(in, element, polygons, fcolors, "vertex_indices");
else if(element.has_property<std::vector<boost::uint32_t> >("vertex_indices"))
internal::read_PLY_faces<boost::uint32_t>(in, element, polygons, fcolors, "vertex_indices");
else if(element.has_property<std::vector<boost::int32_t> >("vertex_index"))
internal::read_PLY_faces<boost::int32_t>(in, element, polygons, fcolors, "vertex_index");
else if(element.has_property<std::vector<boost::uint32_t> >("vertex_index"))
internal::read_PLY_faces<boost::uint32_t>(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<unsigned int>("source") &&
element.has_property<unsigned int>("target") &&
element.has_property<float>("u") &&
element.has_property<float>("v"))
{
has_uv = true;
}
cpp11::tuple<unsigned int, unsigned int, float, float, float> 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<unsigned int>(stag.c_str())),
std::make_pair(CGAL::make_nth_of_tuple_property_map<1>(new_hedge),
PLY_property<unsigned int>(ttag.c_str())),
std::make_pair(CGAL::make_nth_of_tuple_property_map<2>(new_hedge),
PLY_property<float>(utag.c_str())),
std::make_pair(CGAL::make_nth_of_tuple_property_map<3>(new_hedge),
PLY_property<float>(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<unsigned int>(stag.c_str())),
std::make_pair(CGAL::make_nth_of_tuple_property_map<1>(new_hedge),
PLY_property<unsigned int>(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 <class Point_3, class Polygon_3, class Color_rgb>
bool read_PLY(std::istream& in,
std::vector< Point_3 >& points,
std::vector< Polygon_3 >& polygons,
std::vector<Color_rgb>& fcolors,
std::vector<Color_rgb>& vcolors,
bool /* verbose */ = false)
{
std::vector<std::pair<unsigned int, unsigned int> > dummy_pui;
std::vector<std::pair<float, float> > dummy_pf;
return read_PLY<Point_3, Polygon_3, Color_rgb>(in, points, polygons, dummy_pui, fcolors, vcolors, dummy_pf);
}
} // namespace Stream_support
} // namespace CGAL
#endif // CGAL_IO_PLY_PLY_READER_H

View File

@ -11,146 +11,293 @@
#ifndef CGAL_IO_PLY_PLY_WRITER_H
#define CGAL_IO_PLY_PLY_WRITER_H
#include <CGAL/IO/PLY.h>
#include <CGAL/IO/io.h>
namespace CGAL {
namespace internal {
namespace PLY {
template <class Point_3, class Polygon_3>
bool write_PLY(std::ostream& out,
const std::vector< Point_3 >& points,
const std::vector< Polygon_3 >& polygons,
bool /* verbose */ = false)
template <typename T> inline void property_header_type (std::ostream& stream)
{
if(!out)
{
std::cerr << "Error: cannot open file" << std::endl;
return false;
CGAL_assertion_msg (false, "Unknown PLY type");
stream << "undefined_type";
}
// 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;
template <> inline void property_header_type<char> (std::ostream& stream) { stream << "char"; }
template <> inline void property_header_type<signed char> (std::ostream& stream) { stream << "char"; }
template <> inline void property_header_type<unsigned char> (std::ostream& stream) { stream << "uchar"; }
template <> inline void property_header_type<short> (std::ostream& stream) { stream << "short"; }
template <> inline void property_header_type<unsigned short> (std::ostream& stream) { stream << "ushort"; }
template <> inline void property_header_type<int> (std::ostream& stream) { stream << "int"; }
template <> inline void property_header_type<unsigned int> (std::ostream& stream) { stream << "uint"; }
template <> inline void property_header_type<float> (std::ostream& stream) { stream << "float"; }
template <> inline void property_header_type<double> (std::ostream& stream) { stream << "double"; }
internal::PLY::output_property_header(out, make_ply_point_writer (CGAL::Identity_property_map<Point_3>()));
out << "element face " << polygons.size() << std::endl;
internal::PLY::output_property_header(out, std::make_pair(CGAL::Identity_property_map<Polygon_3>(),
PLY_property<std::vector<int> >("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<Point_3>()));
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<Polygon_3>(),
PLY_property<std::vector<int> >("vertex_indices")));
return out.good();
template <typename T>
void property_header (std::ostream& stream, const PLY_property<T>& prop)
{
stream << "property ";
property_header_type<T>(stream);
stream << " " << prop.name << std::endl;
}
template <class SurfaceMesh>
bool write_PLY(std::ostream& out,
const SurfaceMesh& mesh,
bool /* verbose */ = false)
template <typename T>
void property_header (std::ostream& stream, const PLY_property<std::vector<T> >& prop)
{
typedef typename boost::graph_traits<SurfaceMesh>::face_descriptor face_descriptor;
typedef typename boost::graph_traits<SurfaceMesh>::halfedge_descriptor halfedge_descriptor;
typedef typename boost::graph_traits<SurfaceMesh>::vertex_descriptor vertex_descriptor;
typedef typename boost::property_map<SurfaceMesh, boost::vertex_point_t>::type::value_type Point_3;
typedef typename SurfaceMesh::template Property_map<halfedge_descriptor,std::pair<float, float> > UV_map;
UV_map h_uv;
bool has_texture;
boost::tie(h_uv, has_texture) = mesh.template property_map<halfedge_descriptor,std::pair<float, float> >("h:uv");
if(!out)
{
std::cerr << "Error: cannot open file" << std::endl;
return false;
stream << "property list uchar ";
property_header_type<T>(stream);
stream << " " << prop.name << std::endl;
}
// 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<Point_3>()));
out << "element face " << num_faces(mesh) << std::endl;
internal::PLY::output_property_header(out,
std::make_pair(CGAL::Identity_property_map<std::vector<std::size_t> >(),
PLY_property<std::vector<int> >("vertex_indices")));
if(has_texture)
template <std::size_t N>
struct Properties_header
{
out << "element halfedge " << num_halfedges(mesh) << std::endl;
internal::PLY::output_property_header(out,
std::make_pair(CGAL::Identity_property_map<std::size_t >(),
PLY_property<unsigned int >("source")));
internal::PLY::output_property_header(out,
std::make_pair(CGAL::Identity_property_map<std::size_t >(),
PLY_property<unsigned int >("target")));
internal::PLY::output_property_header(out,
std::make_tuple (h_uv,
PLY_property<float>("u"),
PLY_property<float>("v")));
template <class PLY_property_tuple>
static void write(std::ostream& stream, PLY_property_tuple& wrappers)
{
Properties_header<N-1>::write(stream, wrappers);
property_header (stream, std::get<N+1>(wrappers));
}
out << "end_header" << std::endl;
for(vertex_descriptor vd : vertices(mesh))
};
template <>
struct Properties_header<0>
{
Point_3 p = get(get(CGAL::vertex_point, mesh), vd);
internal::PLY::output_properties(out, &p,
make_ply_point_writer (CGAL::Identity_property_map<Point_3>()));
template <class PLY_property_tuple>
static void write(std::ostream& stream, PLY_property_tuple& wrappers)
{
property_header (stream, std::get<1>(wrappers));
}
};
template <typename PropertyMap,
typename ... T>
void output_property_header(std::ostream& stream,
std::tuple<PropertyMap, PLY_property<T>... >&& current)
{
Properties_header<sizeof...(T)-1>::write(stream, current);
}
std::vector<std::size_t> polygon;
for(face_descriptor fd : faces(mesh))
template <typename PropertyMap,
typename T>
void output_property_header(std::ostream& stream,
std::pair<PropertyMap, PLY_property<T> >&& current)
{
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<std::vector<std::size_t> >(),
PLY_property<std::vector<int> >("vertex_indices")));
property_header(stream, current.second);
}
if(has_texture)
template <typename PropertyMap,
typename T,
typename NextPropertyHandler,
typename ... PropertyHandler>
void output_property_header(std::ostream& stream,
std::pair<PropertyMap, PLY_property<T> >&& current,
NextPropertyHandler&& next,
PropertyHandler&& ... properties)
{
for(halfedge_descriptor hd : halfedges(mesh))
property_header(stream, current.second);
output_property_header(stream, std::forward<NextPropertyHandler>(next),
std::forward<PropertyHandler>(properties)...);
}
template <typename PropertyMap,
typename ... T,
typename NextPropertyHandler,
typename ... PropertyHandler>
void output_property_header(std::ostream& stream,
std::tuple<PropertyMap, PLY_property<T>... >&& current,
NextPropertyHandler&& next,
PropertyHandler&& ... properties)
{
typedef std::tuple<unsigned int, unsigned int, float, float> Super_tuple;
Super_tuple t = std::make_tuple(source(hd, mesh),target(hd, mesh),
h_uv[hd].first,
h_uv[hd].second);
Properties_header<sizeof...(T)-1>::write(stream, current);
output_property_header(stream, std::forward<NextPropertyHandler>(next),
std::forward<PropertyHandler>(properties)...);
}
internal::PLY::output_properties(out, &t,
std::make_pair(Nth_of_tuple_property_map<0,Super_tuple>(),
PLY_property<unsigned int >("source")),
std::make_pair(Nth_of_tuple_property_map<1,Super_tuple>(),
PLY_property<unsigned int >("target")),
std::make_pair(Nth_of_tuple_property_map<2,Super_tuple>(),
PLY_property<float>("u")),
std::make_pair(Nth_of_tuple_property_map<3,Super_tuple>(),
PLY_property<float>("v")));
template <typename ForwardIterator,
typename PropertyMap>
void property_write(std::ostream& stream, ForwardIterator it, PropertyMap map)
{
stream << CGAL::oformat(get(map, *it));
}
template <typename T>
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 <typename ForwardIterator,
typename PropertyMap,
typename T>
void simple_property_write(std::ostream& stream,
ForwardIterator it,
std::pair<PropertyMap, PLY_property<T> > 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<char*>(&value), sizeof(value));
}
}
return out.good();
template <typename ForwardIterator,
typename PropertyMap,
typename T>
void simple_property_write(std::ostream& stream,
ForwardIterator it,
std::pair<PropertyMap, PLY_property<std::vector<T> > > 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<unsigned char>(value.size());
stream.write(reinterpret_cast<char*>(&size), sizeof(size));
for(std::size_t i = 0; i < value.size(); ++ i)
{
T t = T(value[i]);
stream.write(reinterpret_cast<char*>(&t), sizeof(t));
}
}
}
template <typename ForwardIterator,
typename PropertyMap,
typename ... T>
void output_properties(std::ostream& stream,
ForwardIterator it,
std::tuple<PropertyMap, PLY_property<T>... >&& current)
{
property_write(stream, it, std::get<0>(current));
if(get_mode(stream) == CGAL::IO::ASCII)
stream << std::endl;
}
template <typename ForwardIterator,
typename PropertyMap,
typename T>
void output_properties(std::ostream& stream,
ForwardIterator it,
std::pair<PropertyMap, PLY_property<T> >&& current)
{
simple_property_write(stream, it, std::forward<std::pair<PropertyMap, PLY_property<T> > >(current));
if(get_mode(stream) == CGAL::IO::ASCII)
stream << std::endl;
}
template <typename ForwardIterator,
typename PropertyMap,
typename T,
typename NextPropertyHandler,
typename ... PropertyHandler>
void output_properties(std::ostream& stream,
ForwardIterator it,
std::pair<PropertyMap, PLY_property<T> >&& 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<NextPropertyHandler>(next),
std::forward<PropertyHandler>(properties)...);
}
template <typename ForwardIterator,
typename PropertyMap,
typename ... T,
typename NextPropertyHandler,
typename ... PropertyHandler>
void output_properties(std::ostream& stream,
ForwardIterator it,
std::tuple<PropertyMap, PLY_property<T>... >&& 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<NextPropertyHandler>(next),
std::forward<PropertyHandler>(properties)...);
}
// Printer classes used by Point_set_3 and Surface_mesh(translate a
// property map to a PLY property)
template <typename Index>
class Abstract_property_printer
{
public:
virtual ~Abstract_property_printer() { }
virtual void print(std::ostream& stream, const Index& index) = 0;
};
template <typename Index, typename PropertyMap>
class Property_printer
: public Abstract_property_printer<Index>
{
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 <typename Index,
typename PropertyMap,
typename Type = typename PropertyMap::value_type>
class Simple_property_printer
: public Abstract_property_printer<Index>
{
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<char*>(&t), sizeof(t));
}
}
};
template <typename Index, typename PropertyMap>
class Char_property_printer
: public Abstract_property_printer<Index>
{
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<char*>(&t), sizeof(t));
}
}
};
} // namespace PLY
} // namespace internal
} // namespace CGAL
#endif // CGAL_IO_PLY_PLY_WRITER_H

View File

@ -16,6 +16,166 @@
#define CGAL_IO_STL_H
#include <CGAL/IO/STL/STL_reader.h>
#include <CGAL/IO/STL/STL_writer.h>
namespace CGAL {
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Read
/*!
* \ingroup IOstreamFunctions
*
* reads the content of `input` into `points` and `facets`, in the STL format.
*
* \see \ref IOStreamSTL
*/
template <class PointRange, class TriangleRange>
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<char*>(&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<char*>(&word[0]), sizeof(c)) &&
input.read(reinterpret_cast<char*>(&word[1]), sizeof(c)) &&
input.read(reinterpret_cast<char*>(&word[2]), sizeof(c)) &&
input.read(reinterpret_cast<char*>(&word[3]), sizeof(c)) &&
input.read(reinterpret_cast<char*>(&word[4]), sizeof(c)) &&
input.read(reinterpret_cast<char*>(&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 <class PointRange, class TriangleRange>
std::ostream& write_STL(const PointRange& points,
const TriangleRange& facets,
std::ostream& out)
{
typedef typename PointRange::value_type Point;
typedef typename CGAL::Kernel_traits<Point>::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<boost::uint32_t>(facets.size());
out.write(reinterpret_cast<const char *>(&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<float>(n.x()), static_cast<float>(n.y()), static_cast<float>(n.z()),
static_cast<float>(p.x()), static_cast<float>(p.y()), static_cast<float>(p.z()),
static_cast<float>(q.x()), static_cast<float>(q.y()), static_cast<float>(q.z()),
static_cast<float>(r.x()), static_cast<float>(r.y()), static_cast<float>(r.z()) };
for (int i=0; i<12; ++i)
out.write(reinterpret_cast<const char *>(&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

View File

@ -24,6 +24,8 @@
#include <vector>
namespace CGAL {
namespace Stream_support {
namespace internal {
template <class PointRange, class TriangleRange, typename IndexMap>
bool read_ASCII_facet(std::istream& input,
@ -282,87 +284,8 @@ bool parse_binary_STL(std::istream& input,
return true;
}
template <class PointRange, class TriangleRange>
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<char*>(&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<char*>(&word[0]), sizeof(c)) &&
input.read(reinterpret_cast<char*>(&word[1]), sizeof(c)) &&
input.read(reinterpret_cast<char*>(&word[2]), sizeof(c)) &&
input.read(reinterpret_cast<char*>(&word[3]), sizeof(c)) &&
input.read(reinterpret_cast<char*>(&word[4]), sizeof(c)) &&
input.read(reinterpret_cast<char*>(&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

View File

@ -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 <CGAL/Kernel_traits.h>
#include <CGAL/Kernel/global_functions.h>
#include <boost/cstdint.hpp>
namespace CGAL {
template <class PointRange, class TriangleRange>
std::ostream& write_STL(const PointRange& points,
const TriangleRange& facets,
std::ostream& out)
{
typedef typename PointRange::value_type Point;
typedef typename CGAL::Kernel_traits<Point>::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<boost::uint32_t>(facets.size());
out.write(reinterpret_cast<const char *>(&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<float>(n.x()), static_cast<float>(n.y()), static_cast<float>(n.z()),
static_cast<float>(p.x()), static_cast<float>(p.y()), static_cast<float>(p.z()),
static_cast<float>(q.x()), static_cast<float>(q.y()), static_cast<float>(q.z()),
static_cast<float>(r.x()), static_cast<float>(r.y()), static_cast<float>(r.z()) };
for (int i=0; i<12; ++i)
out.write(reinterpret_cast<const char *>(&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

View File

@ -16,7 +16,6 @@
#define CGAL_IO_VRML_H
#include <CGAL/IO/VRML/File_writer_VRML_2.h>
#include <CGAL/IO/VRML/Inventor_ostream.h>
#include <CGAL/IO/VRML/VRML_1_ostream.h>
#include <CGAL/IO/VRML/VRML_2_ostream.h>

View File

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

View File

@ -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 <class FT>
void write_vector(std::ostream& os,
const std::vector<FT>& vect)
{
const char* buffer = reinterpret_cast<const char*>(&(vect[0]));
std::size_t size = vect.size()*sizeof(FT);
os.write(reinterpret_cast<const char *>(&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<char *>(calldata);
this->Error = true;
break;
case vtkCommand::WarningEvent:
WarningMessage = static_cast<char *>(calldata);
this->Warning = true;
break;
}
}
private:
bool Error;
bool Warning;
std::string ErrorMessage;
std::string WarningMessage;
};
template <class vtkReader>
vtkSmartPointer<vtkReader>
read_vtk_file(const std::string& input_filename,
vtkSmartPointer<CGAL::ErrorObserverVtk> errorObserver)
{
vtkSmartPointer<vtkReader> reader = vtkSmartPointer<vtkReader>::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

View File

@ -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 <CGAL/license/Polyhedron.h>
#include <fstream>
#include <vector>
template <class FT>
void write_vector(std::ostream& os,
const std::vector<FT>& vect)
{
const char* buffer = reinterpret_cast<const char*>(&(vect[0]));
std::size_t size = vect.size()*sizeof(FT);
os.write(reinterpret_cast<const char *>(&size), sizeof(std::size_t)); // number of bytes encoded
os.write(buffer, vect.size()*sizeof(FT)); // encoded data
}
#endif

View File

@ -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 <CGAL/Point_2.h>
#include <CGAL/Point_3.h>
#include <CGAL/Polygon_2.h>
#include <CGAL/Polygon_with_holes_2.h>
#if BOOST_VERSION >= 105600 && (! defined(BOOST_GCC) || BOOST_GCC >= 40500)
#include <CGAL/IO/WKT/traits_point.h>
#include <CGAL/IO/WKT/traits_point_3.h>
#include <CGAL/IO/WKT/traits_linestring.h>
@ -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