mirror of https://github.com/CGAL/cgal
Continue reorganization of CGAL I/O
This commit is contained in:
parent
3c833674fb
commit
6a5972cc3b
|
|
@ -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> >
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()`
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
// Write header
|
||||
out << "ply" << std::endl
|
||||
<< ((get_mode(out) == IO::BINARY) ? "format binary_little_endian 1.0" : "format ascii 1.0") << std::endl
|
||||
<< "comment Generated by the CGAL library" << std::endl
|
||||
<< "element vertex " << points.size() << std::endl;
|
||||
|
||||
internal::PLY::output_property_header(out, make_ply_point_writer (CGAL::Identity_property_map<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();
|
||||
CGAL_assertion_msg (false, "Unknown PLY type");
|
||||
stream << "undefined_type";
|
||||
}
|
||||
|
||||
template <class SurfaceMesh>
|
||||
bool write_PLY(std::ostream& out,
|
||||
const SurfaceMesh& mesh,
|
||||
bool /* verbose */ = false)
|
||||
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"; }
|
||||
|
||||
template <typename T>
|
||||
void property_header (std::ostream& stream, const PLY_property<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;
|
||||
stream << "property ";
|
||||
property_header_type<T>(stream);
|
||||
stream << " " << prop.name << std::endl;
|
||||
}
|
||||
|
||||
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");
|
||||
template <typename T>
|
||||
void property_header (std::ostream& stream, const PLY_property<std::vector<T> >& prop)
|
||||
{
|
||||
stream << "property list uchar ";
|
||||
property_header_type<T>(stream);
|
||||
stream << " " << prop.name << std::endl;
|
||||
}
|
||||
|
||||
if(!out)
|
||||
template <std::size_t N>
|
||||
struct Properties_header
|
||||
{
|
||||
template <class PLY_property_tuple>
|
||||
static void write(std::ostream& stream, PLY_property_tuple& wrappers)
|
||||
{
|
||||
std::cerr << "Error: cannot open file" << std::endl;
|
||||
return false;
|
||||
Properties_header<N-1>::write(stream, wrappers);
|
||||
property_header (stream, std::get<N+1>(wrappers));
|
||||
}
|
||||
|
||||
// Write header
|
||||
out << "ply" << std::endl
|
||||
<< ((get_mode(out) == IO::BINARY) ? "format binary_little_endian 1.0" : "format ascii 1.0") << std::endl
|
||||
<< "comment Generated by the CGAL library" << std::endl
|
||||
<< "element vertex " << num_vertices(mesh) << std::endl;
|
||||
|
||||
internal::PLY::output_property_header(out,
|
||||
make_ply_point_writer (CGAL::Identity_property_map<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 <>
|
||||
struct Properties_header<0>
|
||||
{
|
||||
template <class PLY_property_tuple>
|
||||
static void write(std::ostream& stream, PLY_property_tuple& wrappers)
|
||||
{
|
||||
out << "element halfedge " << num_halfedges(mesh) << std::endl;
|
||||
|
||||
internal::PLY::output_property_header(out,
|
||||
std::make_pair(CGAL::Identity_property_map<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")));
|
||||
property_header (stream, std::get<1>(wrappers));
|
||||
}
|
||||
out << "end_header" << std::endl;
|
||||
};
|
||||
|
||||
for(vertex_descriptor vd : vertices(mesh))
|
||||
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);
|
||||
}
|
||||
|
||||
template <typename PropertyMap,
|
||||
typename T>
|
||||
void output_property_header(std::ostream& stream,
|
||||
std::pair<PropertyMap, PLY_property<T> >&& current)
|
||||
{
|
||||
property_header(stream, current.second);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
Properties_header<sizeof...(T)-1>::write(stream, current);
|
||||
output_property_header(stream, std::forward<NextPropertyHandler>(next),
|
||||
std::forward<PropertyHandler>(properties)...);
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
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>()));
|
||||
typename PropertyMap::value_type value = get(map.first, *it);
|
||||
stream.write(reinterpret_cast<char*>(&value), sizeof(value));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::size_t> polygon;
|
||||
for(face_descriptor fd : faces(mesh))
|
||||
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)
|
||||
{
|
||||
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")));
|
||||
stream << value.size();
|
||||
for(std::size_t i = 0; i < value.size(); ++ i)
|
||||
stream << " " << no_char_character(value[i]);
|
||||
}
|
||||
|
||||
if(has_texture)
|
||||
else
|
||||
{
|
||||
for(halfedge_descriptor hd : halfedges(mesh))
|
||||
unsigned char size = static_cast<unsigned char>(value.size());
|
||||
stream.write(reinterpret_cast<char*>(&size), sizeof(size));
|
||||
for(std::size_t i = 0; i < value.size(); ++ i)
|
||||
{
|
||||
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);
|
||||
|
||||
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")));
|
||||
T t = T(value[i]);
|
||||
stream.write(reinterpret_cast<char*>(&t), sizeof(t));
|
||||
}
|
||||
}
|
||||
|
||||
return out.good();
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ namespace boost {
|
|||
namespace geometry {
|
||||
namespace traits {
|
||||
|
||||
//WKT traits for Points
|
||||
// WKT traits for Points
|
||||
template< typename K >
|
||||
struct tag<CGAL::Point_3<K> >
|
||||
{
|
||||
|
|
|
|||
|
|
@ -47,14 +47,14 @@ public:
|
|||
};
|
||||
|
||||
/*!
|
||||
\ingroup PkgStreamSupportRef
|
||||
\ingroup PkgStreamSupportRef
|
||||
|
||||
All classes in the \cgal `Kernel` provide input and output operators for
|
||||
IOStreams. The basic task of such an operator is to produce a
|
||||
representation of an object that can be written as a sequence of
|
||||
characters on devices as a console, a file, or a pipe. The enum `Mode` distinguish between three different printing formats.
|
||||
|
||||
In `ASCII` mode, numbers
|
||||
In `ASCII` mode, numbers
|
||||
e.g. the coordinates of a point or
|
||||
the coefficients of a line, are written
|
||||
in a machine independent format.
|
||||
|
|
|
|||
Loading…
Reference in New Issue