diff --git a/Stream_support/include/CGAL/IO/read_3mf.h b/Stream_support/include/CGAL/IO/read_3mf.h new file mode 100644 index 00000000000..5c6cf0798d2 --- /dev/null +++ b/Stream_support/include/CGAL/IO/read_3mf.h @@ -0,0 +1,186 @@ +#ifndef READ_3MF_H +#define READ_3MF_H +#include +#include +#include +#include "Model/COM/NMR_DLLInterfaces.h" +namespace CGAL{ + +/*! + * \brief read_soups_from_3mf extracts ranges of points and triangles from the + * `MeshObject`s contained in `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 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 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 meshes processed in `file_name`. + */ +template +int read_soups_from_3mf(const std::string& file_name, PointRanges& all_points, + PolygonRanges& all_polygons, std::vector& names) +{ + typedef typename PointRanges::value_type PointRange; + typedef typename PointRange::value_type Point_3; + typedef typename PolygonRanges::value_type PolygonRange; + typedef typename PolygonRange::value_type Polygon; + 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::cout << "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::cout << "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::cout << "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::cout << "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::cout << "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::cout << "could not get next object: " << std::hex << hResult << std::endl; + NMR::lib3mf_release(pResourceIterator); + NMR::lib3mf_release(pModel); + return -1; + } + 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::cout << "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::cout << "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) && (bIsMeshObject)) { + std::cout << "------------------------------------------------------" << std::endl; + std::cout << "mesh object #" << ResourceID << ": " << std::endl; + + pMeshObject = pResource; + NMR::lib3mf_meshobject_getvertexcount(pMeshObject, &nbVertices); + NMR::lib3mf_meshobject_gettrianglecount(pMeshObject, &nbPolygons); + PointRange points (nbVertices); + PolygonRange triangles(nbPolygons); + DWORD nNeededChars; + std::vector pBuffer; + // Retrieve Mesh Name Length + hResult = NMR::lib3mf_object_getnameutf8(pMeshObject, NULL, 0, &nNeededChars); + if (hResult != LIB3MF_OK) + return hResult; + + // Retrieve Mesh Name + if (nNeededChars > 0) { + pBuffer.resize(nNeededChars + 1); + hResult = NMR::lib3mf_object_getnameutf8(pMeshObject, &pBuffer[0], nNeededChars + 1, NULL); + pBuffer[nNeededChars] = 0; + names.push_back(std::string(&pBuffer[0])); + } + else + names.push_back(std::string("Unknown Mesh")); + for(DWORD vid = 0; vid < nbVertices; ++vid) + { + NMR::MODELMESHVERTEX pVertex; + NMR::lib3mf_meshobject_getvertex(pMeshObject, vid, &pVertex); + points[vid] = + Point_3(pVertex.m_fPosition[0], + pVertex.m_fPosition[1], + pVertex.m_fPosition[2]); + } + for(DWORD pid = 0; pid < nbPolygons; ++pid) + { + NMR::MODELMESHTRIANGLE pTriangle; + NMR::lib3mf_meshobject_gettriangle(pMeshObject, pid, &pTriangle); + Polygon triangle(3); + for(DWORD i = 0; i< 3; ++i) + triangle[i] = pTriangle.m_nIndices[i]; + triangles[pid] = triangle; + } + all_points.push_back(points); + all_polygons.push_back(triangles); + } + // free instances + NMR::lib3mf_release(pResource); + hResult = NMR::lib3mf_resourceiterator_movenext(pResourceIterator, &pbHasNext); + if (hResult != LIB3MF_OK) { + std::cout << "could not get next object: " << std::hex << hResult << std::endl; + return -1; + } + } + return all_points.size(); +} + +}//end CGAL + +#endif // READ_3MF_H + diff --git a/Stream_support/include/CGAL/IO/write_3mf.h b/Stream_support/include/CGAL/IO/write_3mf.h new file mode 100644 index 00000000000..322d54495c3 --- /dev/null +++ b/Stream_support/include/CGAL/IO/write_3mf.h @@ -0,0 +1,303 @@ +// Copyright (c) 2019 Geometry Factory +// All rights reserved. +// +// This file is part of CGAL (www.cgal.org); you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public License as +// published by the Free Software Foundation; either version 3 of the License, +// or (at your option) any later version. +// +// Licensees holding a valid commercial license may use this file in +// accordance with the commercial license agreement provided with the software. +// +// This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE +// WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. +// +// $URL$ +// $Id$ +// SPDX-License-Identifier: LGPL-3.0+ +// +// Author(s) : Maxime Gimeno + +#ifndef WRITE_3MF_H +#define WRITE_3MF_H +#include +#include +#include +#include "Model/COM/NMR_DLLInterfaces.h" +namespace CGAL{ +namespace tmf_internal{ +// Utility functions to create vertices and triangles +NMR::MODELMESHVERTEX fnCreateVertex(float x, float y, float z) +{ + NMR::MODELMESHVERTEX result; + result.m_fPosition[0] = x; + result.m_fPosition[1] = y; + result.m_fPosition[2] = z; + return result; +} +NMR::MODELMESHTRIANGLE fnCreateTriangle(int v0, int v1, int v2) +{ + NMR::MODELMESHTRIANGLE result; + result.m_nIndices[0] = v0; + result.m_nIndices[1] = v1; + result.m_nIndices[2] = v2; + return result; +} + +} //end internal + +bool add_build_item(NMR::PLib3MFModel * pModel, + NMR::PLib3MFModelMeshObject* pMeshObject) +{ + HRESULT hResult; + DWORD nErrorMessage; + LPCSTR pszErrorMessage; + // Add Build Item for Mesh + NMR::PLib3MFModelBuildItem * pBuildItem; + hResult = NMR::lib3mf_model_addbuilditem(pModel, pMeshObject, NULL, &pBuildItem); + if (hResult != LIB3MF_OK) { + std::cout << "could not create build item: " + << std::hex << hResult << std::endl; + NMR::lib3mf_getlasterror(pModel, &nErrorMessage, &pszErrorMessage); + std::cout << "error #" << std::hex << nErrorMessage + << ": " << pszErrorMessage << std::endl; + NMR::lib3mf_release(pMeshObject); + NMR::lib3mf_release(pModel); + return false; + } + // Release BuildItem and Mesh + NMR::lib3mf_release(pMeshObject); + NMR::lib3mf_release(pBuildItem); + return true; +} + +bool export_model_to_file(const std::string& file_name, + NMR::PLib3MFModel * pModel) +{ + HRESULT hResult; + DWORD nErrorMessage; + LPCSTR pszErrorMessage; + // Output mesh as 3MF + // Create Model Writer for 3MF + NMR::PLib3MFModelWriter * p3MFWriter; + hResult = NMR::lib3mf_model_querywriter(pModel, "3mf", &p3MFWriter); + if (hResult != LIB3MF_OK) { + std::cout << "could not create model reader: " + << std::hex << hResult << std::endl; + NMR::lib3mf_getlasterror(pModel, &nErrorMessage, &pszErrorMessage); + std::cout << "error #" << std::hex << nErrorMessage + << ": " << pszErrorMessage << std::endl; + NMR::lib3mf_release(pModel); + return false; + } + // Export Model into File + hResult = NMR::lib3mf_writer_writetofileutf8(p3MFWriter, file_name.c_str()); + if (hResult != LIB3MF_OK) { + std::cout << "could not write file: " << std::hex << hResult << std::endl; + NMR::lib3mf_getlasterror(p3MFWriter, &nErrorMessage, &pszErrorMessage); + std::cout << "error #" << std::hex << nErrorMessage << ": " + << pszErrorMessage << std::endl; + NMR::lib3mf_release(pModel); + NMR::lib3mf_release(p3MFWriter); + return false; + } + + // Release Model Writer + NMR::lib3mf_release(p3MFWriter); + return true; +} + +template +bool write_mesh_to_model( const PointRange& points, + const PolygonRange& polygons, + const std::string& name, + NMR::PLib3MFModelMeshObject** pMeshObject, + NMR::PLib3MFModel * pModel + ) +{ + DWORD nErrorMessage; + LPCSTR pszErrorMessage; + HRESULT hResult; + // Create mesh structure + std::vector pVertices; + std::vector pTriangles; + // Create Mesh Object + hResult = NMR::lib3mf_model_addmeshobject(pModel, pMeshObject); + if (hResult != LIB3MF_OK) { + std::cout << "could not add mesh object: " << std::hex + << hResult << std::endl; + NMR::lib3mf_getlasterror(pModel, &nErrorMessage, &pszErrorMessage); + std::cout << "error #" << std::hex << nErrorMessage << ": " + << pszErrorMessage << std::endl; + NMR::lib3mf_release(pModel); + return false; + } + for( auto point : points) + { + pVertices.push_back(tmf_internal::fnCreateVertex(point.x(), point.y(), point.z())); + } + + for( auto triangle : polygons) + { + pTriangles.push_back(tmf_internal::fnCreateTriangle(triangle[0], triangle[1], triangle[2])); + } + + hResult = NMR::lib3mf_meshobject_setgeometry(*pMeshObject, pVertices.data(), + pVertices.size(), pTriangles.data(), + pTriangles.size()); + if (hResult != LIB3MF_OK) { + std::cout << "could not set mesh geometry: " + << std::hex << hResult << std::endl; + NMR::lib3mf_getlasterror(*pMeshObject, &nErrorMessage, &pszErrorMessage); + std::cout << "error #" << std::hex << nErrorMessage + << ": " << pszErrorMessage << std::endl; + NMR::lib3mf_release(*pMeshObject); + NMR::lib3mf_release(pModel); + return false; + } + // Set name + hResult = NMR::lib3mf_object_setnameutf8(*pMeshObject, name.c_str()); + if (hResult != LIB3MF_OK) { + std::cout << "could not set object name: " << std::hex << hResult << std::endl; + NMR::lib3mf_getlasterror(*pMeshObject, &nErrorMessage, &pszErrorMessage); + std::cout << "error #" << std::hex << nErrorMessage << ": " << pszErrorMessage << std::endl; + NMR::lib3mf_release(*pMeshObject); + NMR::lib3mf_release(pModel); + return -1; + } +} + +//remember that it adds 3 demmy vertices in the beginning, and a dummy triangle to be ignored. +template +bool write_points(const PointRange& points, + const std::string& name, + NMR::PLib3MFModelMeshObject** pMeshObject, + NMR::PLib3MFModel * pModel + ) +{ + DWORD nErrorMessage; + LPCSTR pszErrorMessage; + HRESULT hResult; + // Create mesh structure + std::vector pVertices; + // Create Mesh Object + hResult = NMR::lib3mf_model_addmeshobject(pModel, pMeshObject); + if (hResult != LIB3MF_OK) { + std::cout << "could not add mesh object: " << std::hex + << hResult << std::endl; + NMR::lib3mf_getlasterror(pModel, &nErrorMessage, &pszErrorMessage); + std::cout << "error #" << std::hex << nErrorMessage << ": " + << pszErrorMessage << std::endl; + NMR::lib3mf_release(pModel); + return false; + } + //add 3 demmy vertices to be sure to have a valid triangle and accept point sets with less than 3 vertices. + for(int i = 0; i< 3; ++i) + pVertices.push_back(tmf_internal::fnCreateVertex(0,0,0)); + for( auto point : points) + { + pVertices.push_back(tmf_internal::fnCreateVertex(point.x(), point.y(), point.z())); + } + NMR::MODELMESHTRIANGLE dummy_triangle = tmf_internal::fnCreateTriangle(0,1,2); //add a triangle to avoid lib error. + hResult = NMR::lib3mf_meshobject_setgeometry(*pMeshObject, pVertices.data(), + pVertices.size(), &dummy_triangle, 1); + if (hResult != LIB3MF_OK) { + std::cout << "could not set mesh geometry: " + << std::hex << hResult << std::endl; + NMR::lib3mf_getlasterror(*pMeshObject, &nErrorMessage, &pszErrorMessage); + std::cout << "error #" << std::hex << nErrorMessage + << ": " << pszErrorMessage << std::endl; + NMR::lib3mf_release(*pMeshObject); + NMR::lib3mf_release(pModel); + return false; + } + // Set name + hResult = NMR::lib3mf_object_setnameutf8(*pMeshObject, name.c_str()); + if (hResult != LIB3MF_OK) { + std::cout << "could not set object name: " << std::hex << hResult << std::endl; + NMR::lib3mf_getlasterror(*pMeshObject, &nErrorMessage, &pszErrorMessage); + std::cout << "error #" << std::hex << nErrorMessage << ": " << pszErrorMessage << std::endl; + NMR::lib3mf_release(*pMeshObject); + NMR::lib3mf_release(pModel); + return -1; + } +} + +template +bool write_point_cloud_to_model(const PointRange& points, + const std::string& name, + NMR::PLib3MFModelMeshObject** pMeshObject, + NMR::PLib3MFModel * pModel + ) +{ + std::string pc_name = name; + pc_name.append("_cgal_pc"); + write_points(points, pc_name, pMeshObject, pModel); +} + +template +bool write_polyline_to_model(const PointRange& points, + const std::string& name, + NMR::PLib3MFModelMeshObject** pMeshObject, + NMR::PLib3MFModel * pModel + ) +{ + std::string pc_name = name; + pc_name.append("_cgal_pl"); + write_points(points, pc_name, pMeshObject, pModel); +} + +/*! + * \brief write_soups_to_3mf will write the polygon soups in all_points and + * all_polygons in `file_name`, in the 3mf format. + * \tparam PointRanges a model of the concepts `RandomAccessContainer` and + * `BackInsertionSequence` whose `value type` is + * a model of the concepts `RandomAccessContainer` and `BackInsertionSequence` + * whose `value type` is the point type. + * \tparam PolygonRanges a model of the concept `RandomAccessContainer` whose + * `value_type` is a model of the concept `RandomAccessContainer` + * whose `value_type` is a model of the concept `RandomAccessContainer` whose + * `value_type` is std::size_t. + * \param file_name the name of the 3mf file to write. + * \param all_points a `PointRanges` that contains the points of the soups + * to write. + * \param all_polygons a `PolygonRanges` that contains the triangles of the + * soups in `file_name`. + * \param names will contains the name of each mesh in `file_name`. + * \return `true` if the writing is successful, `false` otherwise. + */ +template +bool write_soups_to_3mf(const std::string& file_name, + const PointRanges& all_points, + const PolygonRanges& all_polygons, + const std::vector& names) +{ + DWORD nErrorMessage; + LPCSTR pszErrorMessage; + HRESULT hResult; + + // Create Model Instance + NMR::PLib3MFModel * pModel; + hResult = NMR::lib3mf_createmodel(&pModel); + if (hResult != LIB3MF_OK) { + std::cout << "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(""); + write_mesh_to_model(all_points[id], all_polygons[id], name, &pMeshObject, pModel); + //write_mesh_object_to_model(pModel, pMeshObject); + } + return export_model_to_file(file_name, pModel); +} +}//end CGAL +#endif // WRITE_3MF_H diff --git a/Stream_support/test/Stream_support/CMakeLists.txt b/Stream_support/test/Stream_support/CMakeLists.txt index 5eb7092e2b7..ca3d81698c4 100644 --- a/Stream_support/test/Stream_support/CMakeLists.txt +++ b/Stream_support/test/Stream_support/CMakeLists.txt @@ -7,13 +7,49 @@ project( Stream_support_Tests ) cmake_minimum_required(VERSION 3.1) find_package(CGAL QUIET) - if ( CGAL_FOUND ) - + set(3MF_INCLUDE_DIR "" CACHE PATH "Path to lib3MF headers") + set(3MF_LIBRARY_DIR "" CACHE PATH "Path to lib3MF library files") + if(IS_DIRECTORY "${3MF_INCLUDE_DIR}/Common") + set(3MF_FOUND true) + endif() + # create a target per cppfile file(GLOB cppfiles RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) foreach(cppfile ${cppfiles}) - create_single_source_cgal_program( "${cppfile}" ) + if ( "${cppfile}" STREQUAL "test_3mf_to_sm.cpp" ) + + if(3MF_FOUND) + if (WIN32) + set(LSUFFIX "dll") + set(LSUFFIXOUT ".dll") + elseif(UNIX AND NOT APPLE) + set(LSUFFIX "so") + set(LSUFFIXOUT ".so.1") + else() + set(LSUFFIX "dylib") + set(LSUFFIXOUT ".1.dylib") + endif() + include_directories(${3MF_INCLUDE_DIR}) + link_directories(${3MF_LIBRARY_DIR}) + create_single_source_cgal_program( "${cppfile}" ) + + if (WIN32) + target_link_libraries(test_3mf_to_sm PRIVATE lib3MF) + else() + # Unix prefixes the name of the library with "lib" anyway + target_link_libraries(test_3mf_to_sm PRIVATE 3MF) + if(APPLE) + target_link_directories(test_3mf_to_sm PRIVATE ${3MF_LIBRARY_DIR}/..) + endif() + endif() + + else() + message(STATUS "NOTICE: This program requires the lib3MF library, and will not be compiled.") + endif() + else() + create_single_source_cgal_program( "${cppfile}" ) + endif() endforeach() else() @@ -22,3 +58,4 @@ else() endif() + diff --git a/Stream_support/test/Stream_support/test_3mf_to_sm.cpp b/Stream_support/test/Stream_support/test_3mf_to_sm.cpp new file mode 100644 index 00000000000..ee86814d079 --- /dev/null +++ b/Stream_support/test/Stream_support/test_3mf_to_sm.cpp @@ -0,0 +1,147 @@ +//needed by functions +#include +#include +#include +#include "Model/COM/NMR_DLLInterfaces.h" +//needed by example +#include +#include +#include +#include +#include +#include +#include +#include +#include +// Use NMR namespace for the interfaces +using namespace NMR; +namespace PMP = CGAL::Polygon_mesh_processing; +typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; + +typedef Kernel::Point_3 Point_3; +typedef CGAL::Surface_mesh Mesh; +typedef std::vector PointRange; +typedef std::vector Polygon; +typedef std::vector PolygonRange; + +int main(int argc, char** argv) +{ +/* if( argc != 2) + { + std::cerr<<"please give an input 3mf file."; + return 1; + } + std::vector all_points; + std::vector all_polygons; + std::vector names; + std::size_t nb_meshes = + CGAL::read_soups_from_3mf(argv[1], all_points, all_polygons, names); + if(nb_meshes <0) + return 1; + for(std::size_t i = 0; i< nb_meshes; ++i) + { + PolygonRange triangles = all_polygons[i]; + PointRange points = all_points[i]; + bool ok = true; + if(!PMP::is_polygon_soup_a_polygon_mesh(triangles)) + ok = PMP::orient_polygon_soup(points, triangles); + if(!ok) + { + std::cerr<<"Object is not orientable. Skipped."<(sphere); + CGAL::make_regular_prism(10, tube, Point_3(0,-10,0), 10); + /* + all_points.clear(); + all_polygons.clear(); + names.clear(); + */ + PointRange points; + //PolygonRange triangles; + typedef boost::property_map::type VPMap; + VPMap vpm = get(boost::vertex_point, sphere); + std::unordered_map::vertex_descriptor, + std::size_t> vertex_id_map; + std::size_t i = 0; + for(auto v : sphere.vertices()) + { + points.push_back(get(vpm, v)); + vertex_id_map[v] = i++; + } + //all_points.push_back(points); + /*for(auto f : sphere.faces()) + { + Polygon triangle; + for(auto vert : CGAL::vertices_around_face(halfedge(f, sphere), sphere)) + { + triangle.push_back(vertex_id_map[vert]); + } + triangles.push_back(triangle); + } + all_polygons.push_back(triangles); + points.clear(); + triangles.clear(); + vertex_id_map.clear(); + i = 0; + + vpm = get(boost::vertex_point, tube); + for(auto v : tube.vertices()) + { + points.push_back(get(vpm, v)); + vertex_id_map[v] = i++; + } + all_points.push_back(points); + for(auto f : tube.faces()) + { + Polygon triangle; + for(auto vert : CGAL::vertices_around_face(halfedge(f, tube), tube)) + { + triangle.push_back(vertex_id_map[vert]); + } + triangles.push_back(triangle); + } + all_polygons.push_back(triangles); + names.push_back(std::string("sphere")); + names.push_back(std::string("tube")); + if(!CGAL::write_soups_to_3mf("micro.3mf", all_points, all_polygons, names)){ + std::cerr<<"an error has occured in final writing."<