From e35d5d0f1fe4ec07c30ac5e2f1b1620481cd0d5b Mon Sep 17 00:00:00 2001 From: Maxime Gimeno Date: Mon, 2 Sep 2019 14:58:57 +0200 Subject: [PATCH 1/4] Fix the detect_boundaries operation to not use is_border(vertex). Add to the doc of Surface_mesh that is_border(vertex_index) is not guaranteed to work if the SM is not a 2-manifold. --- .../PMP/Polyhedron_stitching_plugin.cpp | 39 +++++-------------- .../include/CGAL/Surface_mesh/Surface_mesh.h | 2 + 2 files changed, 11 insertions(+), 30 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Polyhedron_stitching_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Polyhedron_stitching_plugin.cpp index 011d5e9dc79..9c88a3eba28 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Polyhedron_stitching_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Polyhedron_stitching_plugin.cpp @@ -14,25 +14,6 @@ #include #include -template -struct Is_border { - const G& g; - Is_border(const G& g) - : g(g) - {} - - template - bool operator()(const Descriptor& d) const { - return is_border(d,g); - } - - bool operator()(typename boost::graph_traits::vertex_descriptor d) const { - return is_border(d,g) != boost::none; - } - -}; - - using namespace CGAL::Three; class Polyhedron_demo_polyhedron_stitching_plugin : public QObject, @@ -127,18 +108,16 @@ void Polyhedron_demo_polyhedron_stitching_plugin::on_actionDetectBorders_trigger FaceGraph* pMesh = item->polyhedron(); normalize_border(*pMesh); + for(auto ed : edges(*pMesh)) + { + if(pMesh->is_border(ed)) + { + new_item->polylines.push_back(Scene_polylines_item::Polyline()); + new_item->polylines.back().push_back(pMesh->point(pMesh->source(pMesh->halfedge(ed)))); + new_item->polylines.back().push_back(pMesh->point(pMesh->target(pMesh->halfedge(ed)))); + } + } - - typedef boost::filtered_graph, Is_border > BorderGraph; - - Is_border ib(*pMesh); - BorderGraph bg(*pMesh,ib,ib); - Polyline_visitor polyline_visitor(*pMesh, new_item); - CGAL::split_graph_into_polylines( bg, - polyline_visitor, - CGAL::internal::IsTerminalDefault() ); - - if (new_item->polylines.empty()) { delete new_item; diff --git a/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h b/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h index 63e3eeb4a63..daf7f836b56 100644 --- a/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h +++ b/Surface_mesh/include/CGAL/Surface_mesh/Surface_mesh.h @@ -1758,6 +1758,8 @@ public: /// With `check_all_incident_halfedges == false` the function returns `true`, if the incident /// halfedge associated to vertex `v` is a border halfedge, or if the vertex is isolated. /// \cgalAdvancedEnd + /// \attention If the data contained in the `Surface_mesh` is not a 2-manifold, then + /// this operation is not guaranteed to return the right result. bool is_border(Vertex_index v, bool check_all_incident_halfedges = true) const { Halfedge_index h(halfedge(v)); From 9db7bea1f67ca4d7a33226a68750e50b7d3e105d Mon Sep 17 00:00:00 2001 From: Maxime Gimeno Date: Mon, 2 Sep 2019 15:43:42 +0200 Subject: [PATCH 2/4] Add a nm_vertices stat to the surface_mesh_item --- .../PMP/Polyhedron_stitching_plugin.cpp | 24 ------------------- .../Polyhedron/Scene_surface_mesh_item.cpp | 23 +++++++++++++++--- .../demo/Polyhedron/Scene_surface_mesh_item.h | 1 + 3 files changed, 21 insertions(+), 27 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Polyhedron_stitching_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Polyhedron_stitching_plugin.cpp index 9c88a3eba28..79cb4dbec8c 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Polyhedron_stitching_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Polyhedron_stitching_plugin.cpp @@ -71,30 +71,6 @@ public Q_SLOTS: }; // end Polyhedron_demo_polyhedron_stitching_plugin -template -struct Polyline_visitor -{ - Scene_polylines_item* new_item; - typename boost::property_map::const_type vpm; - - Polyline_visitor(const Poly& poly, Scene_polylines_item* new_item) - : new_item(new_item), vpm(get(CGAL::vertex_point,poly)) - {} - - void start_new_polyline() - { - new_item->polylines.push_back( Scene_polylines_item::Polyline() ); - } - - void add_node(typename boost::graph_traits::vertex_descriptor vd) - { - - new_item->polylines.back().push_back(get(vpm,vd)); - } - - void end_polyline(){} -}; - template void Polyhedron_demo_polyhedron_stitching_plugin::on_actionDetectBorders_triggered(Scene_interface::Item_id index) diff --git a/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp b/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp index 4b80cdaee92..fd0c7a8b293 100644 --- a/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp +++ b/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp @@ -260,6 +260,7 @@ struct Scene_surface_mesh_item_priv{ double volume, area; unsigned int number_of_null_length_edges; unsigned int number_of_degenerated_faces; + unsigned int number_of_nm_vertices; int genus; bool self_intersect; mutable QSlider* alphaSlider; @@ -1515,6 +1516,7 @@ invalidate_stats() { number_of_degenerated_faces = (unsigned int)(-1); number_of_null_length_edges = (unsigned int)(-1); + number_of_nm_vertices = (unsigned int)(-1); volume = -std::numeric_limits::infinity(); area = -std::numeric_limits::infinity(); self_intersect = false; @@ -1568,11 +1570,25 @@ QString Scene_surface_mesh_item::computeStats(int type) } faces_aspect_ratio(d->smesh_, min_altitude, min_ar, max_ar, mean_ar); } + if(type == NB_NM_VERTICES) + { + d->number_of_nm_vertices = 0; + for(SMesh::Vertex_index v : d->smesh_->vertices()) + { + if(CGAL::Polygon_mesh_processing::is_non_manifold_vertex(v, *d->smesh_)) + ++d->number_of_nm_vertices; + } + } switch(type) { case NB_VERTICES: return QString::number(num_vertices(*d->smesh_)); + case NB_NM_VERTICES: + { + + return QString::number(d->number_of_nm_vertices); + } case NB_FACETS: return QString::number(num_faces(*d->smesh_)); @@ -1721,14 +1737,15 @@ CGAL::Three::Scene_item::Header_data Scene_surface_mesh_item::header() const CGAL::Three::Scene_item::Header_data data; //categories - data.categories.append(std::pair(QString("Properties"),9)); + data.categories.append(std::pair(QString("Properties"),11)); data.categories.append(std::pair(QString("Faces"),10)); - data.categories.append(std::pair(QString("Edges"),7)); - data.categories.append(std::pair(QString("Angles"),2)); + data.categories.append(std::pair(QString("Edges"),6)); + data.categories.append(std::pair(QString("Angles"),3)); //titles data.titles.append(QString("#Vertices")); + data.titles.append(QString("#Non-manifold Vertices")); data.titles.append(QString("#Connected Components")); data.titles.append(QString("#Border Edges")); data.titles.append(QString("Pure Triangle")); diff --git a/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.h b/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.h index 0306acce865..045b50cbe0e 100644 --- a/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.h +++ b/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.h @@ -106,6 +106,7 @@ public: //statistics enum STATS { NB_VERTICES = 0, + NB_NM_VERTICES, NB_CONNECTED_COMPOS, NB_BORDER_EDGES, IS_PURE_TRIANGLE, From a115e976b0459a5fe5fce2b9d760f0df1e122a53 Mon Sep 17 00:00:00 2001 From: Maxime Gimeno Date: Mon, 2 Sep 2019 15:47:37 +0200 Subject: [PATCH 3/4] Add a pop up when loading an OFF that contains nm vertices. --- .../Polyhedron/Plugins/IO/OFF_io_plugin.cpp | 47 ++++++++++++------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/IO/OFF_io_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/IO/OFF_io_plugin.cpp index 76d53b4014b..89a5cf8726d 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/IO/OFF_io_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/IO/OFF_io_plugin.cpp @@ -2,6 +2,7 @@ #include "Scene_polygon_soup_item.h" #include "Scene_points_with_normal_item.h" #include +#include #include @@ -25,17 +26,17 @@ class Polyhedron_demo_off_plugin : Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.IOPluginInterface/1.90" FILE "off_io_plugin.json") public: - bool isDefaultLoader(const Scene_item *item) const - { + bool isDefaultLoader(const Scene_item *item) const + { if(qobject_cast(item) - || qobject_cast(item)) - return true; + || qobject_cast(item)) + return true; return false; } - bool isDefaultLoader(const QString& name) const - { - if(name == QString("off")) - return true; + bool isDefaultLoader(const QString& name) const + { + if(name == QString("off")) + return true; return false; } QString name() const { return "off_plugin"; } @@ -44,7 +45,7 @@ public: QList load(QFileInfo fileinfo, bool& ok, bool add_to_scene=true); CGAL::Three::Scene_item* load_off(QFileInfo fileinfo); CGAL::Three::Scene_item* load_obj(QFileInfo fileinfo); - + bool canSave(const CGAL::Three::Scene_item*); bool save(QFileInfo fileinfo,QList& ); }; @@ -55,7 +56,7 @@ bool Polyhedron_demo_off_plugin::canLoad(QFileInfo) const { QList Polyhedron_demo_off_plugin:: load(QFileInfo fileinfo, bool& ok, bool add_to_scene) { - + if(fileinfo.size() == 0) { CGAL::Three::Three::warning( tr("The file you are trying to load is empty.")); @@ -108,10 +109,10 @@ Polyhedron_demo_off_plugin::load_off(QFileInfo fileinfo) { std::cerr << "Error! Cannot open file " << (const char*)fileinfo.filePath().toUtf8() << std::endl; return NULL; } - - + + CGAL::File_scanner_OFF scanner( in, false); - + // Try to read .off in a point set if (scanner.size_of_facets() == 0) { @@ -124,10 +125,10 @@ Polyhedron_demo_off_plugin::load_off(QFileInfo fileinfo) { delete item; return 0; } - + return item; } - + in.seekg(0); // Try to read .off in a surface_mesh SMesh *surface_mesh = new SMesh(); @@ -164,12 +165,15 @@ Polyhedron_demo_off_plugin::load_off(QFileInfo fileinfo) { Scene_surface_mesh_item* item = new Scene_surface_mesh_item(surface_mesh); item->setName(fileinfo.completeBaseName()); std::size_t isolated_v = 0; + std::size_t nm_v = 0; for(vertex_descriptor v : vertices(*surface_mesh)) { if(surface_mesh->is_isolated(v)) { ++isolated_v; } + if(CGAL::Polygon_mesh_processing::is_non_manifold_vertex(v, *surface_mesh)) + ++nm_v; } if(isolated_v >0) { @@ -181,6 +185,15 @@ Polyhedron_demo_off_plugin::load_off(QFileInfo fileinfo) { tr("%1 isolated vertices found") .arg(item->getNbIsolatedvertices())); } + if(nm_v >0) + { + //needs two restore, it's not a typo + QApplication::restoreOverrideCursor(); + QMessageBox::warning((QWidget*)NULL, + tr("Non Manifold Vertices"), + tr("%1 non-manifold vertices found") + .arg(nm_v)); + } if(item->isItemMulticolor()) item->computeItemColorVectorAutomatically(true); return item; @@ -218,9 +231,9 @@ save(QFileInfo fileinfo,QList& items) // This plugin supports point sets, surface_meshes and polygon soups const Scene_points_with_normal_item* points_item = qobject_cast(item); - const Scene_surface_mesh_item* sm_item = + const Scene_surface_mesh_item* sm_item = qobject_cast(item); - const Scene_polygon_soup_item* soup_item = + const Scene_polygon_soup_item* soup_item = qobject_cast(item); if(!sm_item && !soup_item && !points_item) From e7b01b6d4547cf938eb0a005a09d3b66139411b3 Mon Sep 17 00:00:00 2001 From: Maxime Gimeno Date: Mon, 2 Sep 2019 16:25:11 +0200 Subject: [PATCH 4/4] Don't count the nm vertices. --- .../Polyhedron/Plugins/IO/OFF_io_plugin.cpp | 15 +++++----- .../Polyhedron/Scene_surface_mesh_item.cpp | 29 +++++++++++-------- .../demo/Polyhedron/Scene_surface_mesh_item.h | 2 +- 3 files changed, 26 insertions(+), 20 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/IO/OFF_io_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/IO/OFF_io_plugin.cpp index 89a5cf8726d..580023022f7 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/IO/OFF_io_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/IO/OFF_io_plugin.cpp @@ -165,15 +165,12 @@ Polyhedron_demo_off_plugin::load_off(QFileInfo fileinfo) { Scene_surface_mesh_item* item = new Scene_surface_mesh_item(surface_mesh); item->setName(fileinfo.completeBaseName()); std::size_t isolated_v = 0; - std::size_t nm_v = 0; for(vertex_descriptor v : vertices(*surface_mesh)) { if(surface_mesh->is_isolated(v)) { ++isolated_v; } - if(CGAL::Polygon_mesh_processing::is_non_manifold_vertex(v, *surface_mesh)) - ++nm_v; } if(isolated_v >0) { @@ -185,15 +182,19 @@ Polyhedron_demo_off_plugin::load_off(QFileInfo fileinfo) { tr("%1 isolated vertices found") .arg(item->getNbIsolatedvertices())); } - if(nm_v >0) + typedef boost::function_output_iterator OutputIterator; + try{ + CGAL::Polygon_mesh_processing::non_manifold_vertices(*surface_mesh, OutputIterator()); + } + catch( CGAL::internal::Throw_at_output::Throw_at_output_exception& ) { - //needs two restore, it's not a typo + QApplication::restoreOverrideCursor(); QMessageBox::warning((QWidget*)NULL, tr("Non Manifold Vertices"), - tr("%1 non-manifold vertices found") - .arg(nm_v)); + tr("Non-manifold vertices have been found")); } + if(item->isItemMulticolor()) item->computeItemColorVectorAutomatically(true); return item; diff --git a/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp b/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp index fd0c7a8b293..09dd204eff6 100644 --- a/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp +++ b/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.cpp @@ -260,7 +260,7 @@ struct Scene_surface_mesh_item_priv{ double volume, area; unsigned int number_of_null_length_edges; unsigned int number_of_degenerated_faces; - unsigned int number_of_nm_vertices; + bool has_nm_vertices; int genus; bool self_intersect; mutable QSlider* alphaSlider; @@ -1516,7 +1516,7 @@ invalidate_stats() { number_of_degenerated_faces = (unsigned int)(-1); number_of_null_length_edges = (unsigned int)(-1); - number_of_nm_vertices = (unsigned int)(-1); + has_nm_vertices = false; volume = -std::numeric_limits::infinity(); area = -std::numeric_limits::infinity(); self_intersect = false; @@ -1570,24 +1570,29 @@ QString Scene_surface_mesh_item::computeStats(int type) } faces_aspect_ratio(d->smesh_, min_altitude, min_ar, max_ar, mean_ar); } - if(type == NB_NM_VERTICES) + if(type == HAS_NM_VERTICES) { - d->number_of_nm_vertices = 0; - for(SMesh::Vertex_index v : d->smesh_->vertices()) - { - if(CGAL::Polygon_mesh_processing::is_non_manifold_vertex(v, *d->smesh_)) - ++d->number_of_nm_vertices; + d->has_nm_vertices = false; + typedef boost::function_output_iterator OutputIterator; + try{ + CGAL::Polygon_mesh_processing::non_manifold_vertices(*d->smesh_, OutputIterator()); } + catch( CGAL::internal::Throw_at_output::Throw_at_output_exception& ) + { + d->has_nm_vertices = true; + } + } switch(type) { case NB_VERTICES: return QString::number(num_vertices(*d->smesh_)); - case NB_NM_VERTICES: + case HAS_NM_VERTICES: { - - return QString::number(d->number_of_nm_vertices); + if(d->has_nm_vertices) + return QString("Yes"); + return QString("No"); } case NB_FACETS: @@ -1745,7 +1750,7 @@ CGAL::Three::Scene_item::Header_data Scene_surface_mesh_item::header() const //titles data.titles.append(QString("#Vertices")); - data.titles.append(QString("#Non-manifold Vertices")); + data.titles.append(QString("Has Non-manifold Vertices")); data.titles.append(QString("#Connected Components")); data.titles.append(QString("#Border Edges")); data.titles.append(QString("Pure Triangle")); diff --git a/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.h b/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.h index 045b50cbe0e..ddd39a9f616 100644 --- a/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.h +++ b/Polyhedron/demo/Polyhedron/Scene_surface_mesh_item.h @@ -106,7 +106,7 @@ public: //statistics enum STATS { NB_VERTICES = 0, - NB_NM_VERTICES, + HAS_NM_VERTICES, NB_CONNECTED_COMPOS, NB_BORDER_EDGES, IS_PURE_TRIANGLE,