diff --git a/BGL/examples/BGL_OpenMesh/CMakeLists.txt b/BGL/examples/BGL_OpenMesh/CMakeLists.txt index f461d55efb10..a5091363a51e 100644 --- a/BGL/examples/BGL_OpenMesh/CMakeLists.txt +++ b/BGL/examples/BGL_OpenMesh/CMakeLists.txt @@ -12,6 +12,9 @@ if(OpenMesh_FOUND) include(CGAL_OpenMesh_support) create_single_source_cgal_program("TriMesh.cpp") target_link_libraries(TriMesh PRIVATE CGAL::OpenMesh_support) + create_single_source_cgal_program("PolyMesh.cpp") + target_link_libraries(PolyMesh PRIVATE CGAL::OpenMesh_support) + else() message("NOTICE: This project requires OpenMesh and will not be compiled.") endif() diff --git a/BGL/examples/BGL_OpenMesh/PolyMesh.cpp b/BGL/examples/BGL_OpenMesh/PolyMesh.cpp new file mode 100644 index 000000000000..3dde7d168456 --- /dev/null +++ b/BGL/examples/BGL_OpenMesh/PolyMesh.cpp @@ -0,0 +1,71 @@ +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + + +typedef CGAL::Exact_predicates_inexact_constructions_kernel Kernel; + +typedef OpenMesh::PolyMesh_ArrayKernelT OMesh; +typedef CGAL::Surface_mesh SM; +typedef boost::graph_traits::vertex_descriptor vertex_descriptor; +typedef boost::graph_traits::halfedge_descriptor halfedge_descriptor; + +typedef boost::graph_traits::vertex_descriptor sm_vertex_descriptor; +typedef boost::graph_traits::edge_descriptor sm_edge_descriptor; + +int main(int argc, char** argv ) +{ + OMesh omesh; + + const std::string filename = (argc>1)?argv[1]:CGAL::data_file_path("meshes/in.off"); + const char* outname= (argc>2)?argv[2]:"out.om"; + CGAL::IO::read_polygon_mesh(filename, omesh); + + omesh.request_vertex_status(); + omesh.request_edge_status(); + + int i = 0; + for(auto v : vertices(omesh)){ + omesh.status(v).set_selected((i%2) == 0); + ++i; + } + + i = 0; + for(auto eit = omesh.edges_begin(); eit != omesh.edges_end(); ++eit){ + omesh.status(*eit).set_feature(i > 2); + ++i; + } + + OpenMesh::IO::write_mesh(omesh, outname, OpenMesh::IO::Options::Status); + + SM sm; + + std::map sm_selected_map; + auto sm_selected_pmap = boost::make_assoc_property_map(sm_selected_map); + + std::map sm_feature_map; + auto sm_feature_pmap = boost::make_assoc_property_map(sm_feature_map); + + CGAL::IO::read_OM(outname, sm, sm_selected_pmap, sm_feature_pmap); + + std::cout << "vertex selection values:\n"; + for(auto v : vertices(sm)){ + std::cout << std::boolalpha << get(sm_selected_pmap, v) << std::endl; + } + + std::cout << "edge feature values:\n"; + for(auto e : edges(sm)){ + std::cout << std::boolalpha << get(sm_feature_pmap, e) << std::endl; + } + return 0; +} diff --git a/BGL/examples/BGL_OpenMesh/read_OM.h b/BGL/examples/BGL_OpenMesh/read_OM.h new file mode 100644 index 000000000000..6c350bad03f9 --- /dev/null +++ b/BGL/examples/BGL_OpenMesh/read_OM.h @@ -0,0 +1,62 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace CGAL { +namespace IO { + +template +bool read_OM(std::string fname, SM& sm, VSelectionPM vspm, EFeaturePM efpm) +{ + typedef OpenMesh::PolyMesh_ArrayKernelT<> OMesh; + typedef boost::graph_traits::vertex_descriptor om_vertex_descriptor; + typedef boost::graph_traits::vertex_descriptor sm_vertex_descriptor; + typedef boost::graph_traits::halfedge_descriptor om_halfedge_descriptor; + typedef boost::graph_traits::halfedge_descriptor sm_halfedge_descriptor; + + OMesh omesh; + OpenMesh::IO::Options options = OpenMesh::IO::Options::Status; + bool ok = OpenMesh::IO::read_mesh(omesh, fname, options); + if(! ok){ + return false; + } + + std::map v2v; + auto v2vpmap = boost::make_assoc_property_map(v2v); + + std::map h2h; + auto h2hpmap = boost::make_assoc_property_map(h2h); + + CGAL::copy_face_graph(omesh, sm, CGAL::parameters::vertex_to_vertex_map(v2vpmap).halfedge_to_halfedge_map(h2hpmap)); + + if(options.vertex_has_status()){ + for(auto v : vertices(omesh)){ + put(vspm, v2v[v], omesh.status(v).selected()); + } + }else{ + std::cout << "no vertex status" << std::endl; + } + + if(options.edge_has_status()){ + for(auto e : edges(omesh)){ + auto sme = edge(h2h[halfedge(e,omesh)], sm); + put(efpm, sme , omesh.status(OpenMesh::EdgeHandle(e.idx())).feature()); + } + }else{ + std::cout << "no edge status" << std::endl; + } + return true; +} + +} // namespace IO +} // namespace CGAL + + diff --git a/Lab/demo/Lab/Plugins/IO/CMakeLists.txt b/Lab/demo/Lab/Plugins/IO/CMakeLists.txt index 760a9c328972..6486ebbedd07 100644 --- a/Lab/demo/Lab/Plugins/IO/CMakeLists.txt +++ b/Lab/demo/Lab/Plugins/IO/CMakeLists.txt @@ -47,6 +47,16 @@ target_link_libraries(surf_io_plugin PUBLIC scene_surface_mesh_item) cgal_lab_plugin(lcc_io_plugin lcc_io_plugin KEYWORDS Viewer) target_link_libraries(lcc_io_plugin PUBLIC scene_lcc_item) +find_package(OpenMesh) +if(OpenMesh_FOUND) + include(CGAL_OpenMesh_support) + cgal_lab_plugin(om_plugin OM_io_plugin KEYWORDS Viewer PMP) + target_link_libraries(om_plugin PUBLIC scene_surface_mesh_item scene_polygon_soup_item scene_selection_item) + target_link_libraries(om_plugin PRIVATE CGAL::OpenMesh_support) +else() + message(STATUS "NOTICE: the OM IO plugin needs OpenMesh libraries and will not be compiled.") +endif() + find_package(VTK 9.0 QUIET COMPONENTS CommonCore IOCore IOLegacy IOXML FiltersCore FiltersSources) set_package_properties( VTK PROPERTIES diff --git a/Lab/demo/Lab/Plugins/IO/OM_io_plugin.cpp b/Lab/demo/Lab/Plugins/IO/OM_io_plugin.cpp new file mode 100644 index 000000000000..040cc1ced7f7 --- /dev/null +++ b/Lab/demo/Lab/Plugins/IO/OM_io_plugin.cpp @@ -0,0 +1,208 @@ +#include "SMesh_type.h" +#include "Scene_surface_mesh_item.h" +#include "Scene_polygon_soup_item.h" +#include "Kernel_type.h" +#include "Scene.h" + +#include +#include +#include "Scene_polyhedron_selection_item.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +using namespace CGAL::Three; +class CGAL_Lab_om_plugin : + public QObject, + public CGAL_Lab_io_plugin_interface +{ + Q_OBJECT + Q_INTERFACES(CGAL::Three::CGAL_Lab_io_plugin_interface) + Q_PLUGIN_METADATA(IID "com.geometryfactory.CGALLab.IOPluginInterface/1.90" FILE "om_io_plugin.json") + +public: + QString nameFilters() const; + QString name() const { return "om_plugin"; } + bool canLoad(QFileInfo fileinfo) const; + QList load(QFileInfo fileinfo, bool& ok, bool add_to_scene=true); + + bool canSave(const CGAL::Three::Scene_item*); + bool save(QFileInfo fileinfo,QList&); +}; + +QString CGAL_Lab_om_plugin::nameFilters() const { + return "om files (*.om)"; +} + +bool CGAL_Lab_om_plugin::canLoad(QFileInfo) const { + return true; +} + + + +QList +CGAL_Lab_om_plugin:: +load(QFileInfo fileinfo, bool& ok, bool add_to_scene){ + + // Open file + std::ifstream in(fileinfo.filePath().toUtf8(), std::ios::in | std::ios::binary); + if(!in) { + std::cerr << "Error! Cannot open file " << (const char*)fileinfo.filePath().toUtf8() << std::endl; + ok = false; + return QList(); + } + in.close(); + if(fileinfo.size() == 0) + { + CGAL::Three::Three::warning( tr("The file you are trying to load is empty.")); + Scene_surface_mesh_item* item = new Scene_surface_mesh_item(); + item->setName(fileinfo.completeBaseName()); + ok = true; + if(add_to_scene) + CGAL::Three::Three::scene()->addItem(item); + return QList()<::vertex_descriptor vertex_descriptor; + typedef boost::graph_traits::edge_descriptor edge_descriptor; + std::map sm_vfeature_map; + auto sm_vfeature_pmap = boost::make_assoc_property_map(sm_vfeature_map); + + std::map sm_efeature_map; + auto sm_efeature_pmap = boost::make_assoc_property_map(sm_efeature_map); + + // Try building a surface_mesh + SMesh* sm = new SMesh(); + ok = CGAL::IO::read_OM((const char*)fileinfo.filePath().toUtf8(), *sm, sm_vfeature_pmap, sm_efeature_pmap); + + if(!ok || !sm->is_valid() || sm->is_empty()){ + std::cerr << "Error: Invalid facegraph" << std::endl; + } + else{ + Scene_surface_mesh_item* item = new Scene_surface_mesh_item(sm); + + Scene_polyhedron_selection_item* selection_item = new Scene_polyhedron_selection_item(item, CGAL::Three::Three::mainWindow()); + bool eselected = false; + bool vselected = false; + for(auto v : vertices(*sm)){ + if(get(sm_vfeature_pmap, v)){ + selection_item->selected_vertices.insert(v); + vselected = true; + } + } + for(auto e : edges(*sm)){ + if(get(sm_efeature_pmap, e)){ + selection_item->selected_edges.insert(e); + eselected = true; + } + } + item->setName(fileinfo.completeBaseName()); + ok = true; + if(add_to_scene) + { + CGAL::Three::Three::scene()->addItem(item); + if(!selection_item->isEmpty()) + CGAL::Three::Three::scene()->addItem(selection_item); + } + + QList res; + res << item; + if(!selection_item->isEmpty()) + res << selection_item; + return res; + } + } + catch(...){} + + ok = false; + return QList(); +} + +bool CGAL_Lab_om_plugin::canSave(const CGAL::Three::Scene_item* item) +{ + return qobject_cast(item) + || qobject_cast(item); +} + +bool CGAL_Lab_om_plugin:: +save(QFileInfo fileinfo, QList& items) +{ + Scene_item* item = items.front(); + + Scene_surface_mesh_item* sm_item = nullptr; + Scene_polyhedron_selection_item* selection_item = nullptr; + + for (Scene_item* item : items) + { + if (sm_item == nullptr) + { + sm_item = qobject_cast(item); + if (sm_item != nullptr) //surface_mesh_item found + continue; + } + if (selection_item == nullptr) + { + selection_item = qobject_cast(item); + } + } + + if (sm_item == nullptr && selection_item == nullptr) + return false; + + if (selection_item != nullptr) + { + if (sm_item == nullptr) + sm_item = selection_item->polyhedron_item(); + + if (sm_item != selection_item->polyhedron_item()) + { + std::cerr << "Warning! Selection is not associated to the surface_mesh. Ignoring selection." << std::endl; + selection_item = nullptr; + } + } + + bool res = false; + if (selection_item != nullptr) + { + res = CGAL::IO::write_OM((const char*)fileinfo.filePath().toUtf8() + , *sm_item->face_graph() + , selection_item->constrained_vertices_pmap() + , selection_item->constrained_edges_pmap()); + } + else + { + using edge_descriptor = typename boost::graph_traits::edge_descriptor; + using vertex_descriptor = typename boost::graph_traits::vertex_descriptor; + + res = CGAL::IO::write_OM((const char*)fileinfo.filePath().toUtf8() + , *sm_item->face_graph() + , CGAL::Constant_property_map(false) + , CGAL::Constant_property_map(false)); + } + + if (res) + { + items.removeAll(sm_item); + if (selection_item != nullptr) + items.removeAll(selection_item); + } + return res; +} + +#include "om_io_plugin.moc" diff --git a/Stream_support/include/CGAL/IO/OM.h b/Stream_support/include/CGAL/IO/OM.h new file mode 100644 index 000000000000..ba648f9ab0c8 --- /dev/null +++ b/Stream_support/include/CGAL/IO/OM.h @@ -0,0 +1,121 @@ +// Copyright (c) 2024 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 + +#ifndef CGAL_IO_OM_H +#define CGAL_IO_OM_H + +#if defined(CGAL_USE_OPENMESH) || defined(DOXYGEN_RUNNING) + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace CGAL { +namespace IO { + +template +bool read_OM(std::string fname, SM& sm, VFeaturePM vfpm, EFeaturePM efpm) +{ + typedef OpenMesh::PolyMesh_ArrayKernelT<> OMesh; + typedef boost::graph_traits::vertex_descriptor om_vertex_descriptor; + typedef boost::graph_traits::vertex_descriptor sm_vertex_descriptor; + typedef boost::graph_traits::halfedge_descriptor om_halfedge_descriptor; + typedef boost::graph_traits::halfedge_descriptor sm_halfedge_descriptor; + + OMesh omesh; + OpenMesh::IO::Options options = OpenMesh::IO::Options::Status; + bool ok = OpenMesh::IO::read_mesh(omesh, fname, options); + if(! ok){ + return false; + } + + std::map v2v; + auto v2vpmap = boost::make_assoc_property_map(v2v); + + std::map h2h; + auto h2hpmap = boost::make_assoc_property_map(h2h); + + CGAL::copy_face_graph(omesh, sm, CGAL::parameters::vertex_to_vertex_map(v2vpmap).halfedge_to_halfedge_map(h2hpmap)); + + if(options.vertex_has_status()){ + for(auto v : vertices(omesh)){ + put(vfpm, v2v[v], omesh.status(v).feature()); + } + }else{ + std::cout << "no vertex status" << std::endl; + } + + if(options.edge_has_status()){ + for(auto e : edges(omesh)){ + auto sme = edge(h2h[halfedge(e,omesh)], sm); + put(efpm, sme , omesh.status(OpenMesh::EdgeHandle(e.idx())).feature()); + } + }else{ + std::cout << "no edge status" << std::endl; + } + return true; +} + +template +bool write_OM(std::string fname, SM& sm, VFeaturePM vfpm, EFeaturePM efpm) +{ + typedef OpenMesh::PolyMesh_ArrayKernelT<> OMesh; + typedef boost::graph_traits::vertex_descriptor om_vertex_descriptor; + typedef boost::graph_traits::vertex_descriptor sm_vertex_descriptor; + typedef boost::graph_traits::halfedge_descriptor om_halfedge_descriptor; + typedef boost::graph_traits::halfedge_descriptor sm_halfedge_descriptor; + + std::map v2v; + auto v2vpmap = boost::make_assoc_property_map(v2v); + + std::map h2h; + auto h2hpmap = boost::make_assoc_property_map(h2h); + + OMesh omesh; + CGAL::copy_face_graph(sm, omesh, + CGAL::parameters::vertex_to_vertex_map(v2vpmap).halfedge_to_halfedge_map(h2hpmap)); + + omesh.request_edge_status(); + omesh.request_vertex_status(); + + std::size_t nbe = 0; + std::size_t nbv = 0; + for (auto h : halfedges(sm)) + { + om_halfedge_descriptor omh = h2h.at(h); + const bool isfeature = get(efpm, edge(h, sm)); + if (isfeature) nbe++; + omesh.status(omesh.edge_handle(omh)).set_feature(isfeature); + } + for (auto v : vertices(sm)) + { + auto omv = v2v.at(v); + const bool isfeature = get(vfpm, v); + if (isfeature) nbv++; + omesh.status(omv).set_feature(isfeature); + } + + std::cout << nbv << " feature vertices" << std::endl; + std::cout << nbe << " feature edges" << std::endl; + return OpenMesh::IO::write_mesh(omesh, fname, OpenMesh::IO::Options::Status); +} + +} // namespace IO +} // namespace CGAL + +#endif // CGAL_USE_OPENMESH || DOXYGEN_RUNNING +#endif // CGAL_IO_OM \ No newline at end of file