From a4cb1c2a97c882ba28f6f773c608cc51eeebdba1 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Wed, 16 Mar 2016 18:09:55 +0100 Subject: [PATCH 01/11] Add protection of identical edges in different polyhedra. WIP: with an #undef and std::cerr --- .../PMP/Isotropic_remeshing_plugin.cpp | 44 ++++++++++++++++--- 1 file changed, 39 insertions(+), 5 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp index e2549ee3fcf..cdbe028a8a5 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp @@ -30,7 +30,7 @@ #include #include #include - +#undef CGAL_LINKED_WITH_TBB #ifdef CGAL_LINKED_WITH_TBB #include "tbb/parallel_for.h" #include "tbb/blocked_range.h" @@ -300,7 +300,36 @@ public Q_SLOTS: selection, edges_only, target_length, nb_iter, protect, smooth_features)); total_time = time.elapsed(); + #else + typedef boost::graph_traits::edge_descriptor edge_descriptor; + std::map > edges_to_protect; + + if(selection.size()>1){ + typedef Polyhedron::Point_3 Point_3; + typedef std::pair Segment_3; + + std::map > duplicates; + + BOOST_FOREACH(Scene_polyhedron_item* poly_item, selection){ + const Polyhedron& pmesh = *poly_item->polyhedron(); + BOOST_FOREACH(edge_descriptor ed, edges(pmesh)){ + Point_3 p = source(ed,pmesh)->point(), q = target(ed,pmesh)->point(); + Segment_3 s = (p < q)? std::make_pair(p,q): std::make_pair(q,p); + + std::map >::iterator + it = duplicates.find(s); + if(it == duplicates.end()){ + duplicates[s] = std::make_pair(poly_item,ed); + }else{ + std::cerr << p << " "<< q << std::endl; + edges_to_protect[it->second.first].insert(it->second.second); + edges_to_protect[poly_item].insert(ed); + } + } + } + } + Remesh_polyhedron_item remesher(edges_only, target_length, nb_iter, protect, smooth_features); BOOST_FOREACH(Scene_polyhedron_item* poly_item, selection) @@ -308,7 +337,7 @@ public Q_SLOTS: QTime time; time.start(); - remesher(poly_item); + remesher(poly_item, edges_to_protect[poly_item]); total_time += time.elapsed(); std::cout << "Remeshing of " << poly_item->name().data() @@ -342,7 +371,8 @@ private: bool smooth_features_; protected: - void remesh(Scene_polyhedron_item* poly_item) const + void remesh(Scene_polyhedron_item* poly_item, + std::set& edges_to_protect) const { //fill face_index property map boost::property_map::type fim @@ -369,12 +399,15 @@ private: } else { + std::cerr << edges_to_protect.size() << " edges to protect" < > ecm(edges_to_protect); CGAL::Polygon_mesh_processing::isotropic_remeshing( faces(*poly_item->polyhedron()) , target_length_ , *poly_item->polyhedron() , CGAL::Polygon_mesh_processing::parameters::number_of_iterations(nb_iter_) .protect_constraints(protect_) + .edge_is_constrained_map(ecm) .smooth_along_features(smooth_features_)); } } @@ -401,9 +434,10 @@ private: , smooth_features_(remesh.smooth_features_) {} - void operator()(Scene_polyhedron_item* poly_item) const + void operator()(Scene_polyhedron_item* poly_item, + std::set& edges_to_protect) const { - remesh(poly_item); + remesh(poly_item, edges_to_protect); } }; From 53d4328d3636a692fb90be597bfe9608269ecd3c Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Thu, 17 Mar 2016 08:29:28 +0100 Subject: [PATCH 02/11] Improve wording in UI --- .../Plugins/Operations_on_polyhedra/Clip_polyhedron_plugin.ui | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/Operations_on_polyhedra/Clip_polyhedron_plugin.ui b/Polyhedron/demo/Polyhedron/Plugins/Operations_on_polyhedra/Clip_polyhedron_plugin.ui index 3f001f77f78..7c5473bb039 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/Operations_on_polyhedra/Clip_polyhedron_plugin.ui +++ b/Polyhedron/demo/Polyhedron/Plugins/Operations_on_polyhedra/Clip_polyhedron_plugin.ui @@ -11,7 +11,7 @@ - Clip polyhedra + Clip Polyhedra @@ -20,7 +20,7 @@ - <html><head/><body><p>This function allows to clip all the selected polyhedra by an half-space. The blue side of the clipping plane will be clipped out, and the yellow side will be kept.<br/><br/>If the option <span style=" font-style:italic;">keep closed</span> is checked, the clipped part of each polyhedron will be closed. Otherwise, it will be left open.</p><p><br/></p></body></html> + <html><head/><body><p>This function allows to clip all the selected polyhedra against a halfspace. What is on the blue side of the clipping plane will be clipped, and what is on the yellow side will be kept.<br/><br/>If the option <span style=" font-style:italic;">keep closed</span> is checked, the clipped part of each polyhedron will be closed, if it has a closed contour on the clipping plane. Otherwise, it will be left open.</p><p><br/></p></body></html> true From 2468889c517f3d4697d459bb596559f7201e8ab9 Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Thu, 17 Mar 2016 09:41:45 +0100 Subject: [PATCH 03/11] Also deal with TBB --- .../PMP/Isotropic_remeshing_plugin.cpp | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp index cdbe028a8a5..442ee6de062 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp @@ -30,7 +30,7 @@ #include #include #include -#undef CGAL_LINKED_WITH_TBB + #ifdef CGAL_LINKED_WITH_TBB #include "tbb/parallel_for.h" #include "tbb/blocked_range.h" @@ -48,6 +48,8 @@ class Polyhedron_demo_isotropic_remeshing_plugin : Q_INTERFACES(CGAL::Three::Polyhedron_demo_plugin_interface) Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0") + typedef boost::graph_traits::edge_descriptor edge_descriptor; + public: void init(QMainWindow* mainWindow, Scene_interface* scene_interface) { @@ -290,19 +292,8 @@ public Q_SLOTS: QApplication::setOverrideCursor(Qt::WaitCursor); int total_time = 0; -#ifdef CGAL_LINKED_WITH_TBB - QTime time; - time.start(); - tbb::parallel_for( - tbb::blocked_range(0, selection.size()), - Remesh_polyhedron_item_for_parallel_for( - selection, edges_only, target_length, nb_iter, protect, smooth_features)); - - total_time = time.elapsed(); - -#else - typedef boost::graph_traits::edge_descriptor edge_descriptor; + typedef boost::graph_traits::edge_descriptor edge_descriptor; std::map > edges_to_protect; if(selection.size()>1){ @@ -322,7 +313,6 @@ public Q_SLOTS: if(it == duplicates.end()){ duplicates[s] = std::make_pair(poly_item,ed); }else{ - std::cerr << p << " "<< q << std::endl; edges_to_protect[it->second.first].insert(it->second.second); edges_to_protect[poly_item].insert(ed); } @@ -330,6 +320,19 @@ public Q_SLOTS: } } +#ifdef CGAL_LINKED_WITH_TBB + QTime time; + time.start(); + + tbb::parallel_for( + tbb::blocked_range(0, selection.size()), + Remesh_polyhedron_item_for_parallel_for( + selection, edges_to_protect, edges_only, target_length, nb_iter, protect, smooth_features)); + + total_time = time.elapsed(); + +#else + Remesh_polyhedron_item remesher(edges_only, target_length, nb_iter, protect, smooth_features); BOOST_FOREACH(Scene_polyhedron_item* poly_item, selection) @@ -399,7 +402,6 @@ private: } else { - std::cerr << edges_to_protect.size() << " edges to protect" < > ecm(edges_to_protect); CGAL::Polygon_mesh_processing::isotropic_remeshing( faces(*poly_item->polyhedron()) @@ -447,34 +449,35 @@ private: : RemeshFunctor { const std::vector& selection_; + std::map >& edges_to_protect_; public: // Constructor Remesh_polyhedron_item_for_parallel_for( const std::vector& selection, + std::map >& edges_to_protect, const bool edges_only, const double target_length, const unsigned int nb_iter, const bool protect, const bool smooth_features) : RemeshFunctor(edges_only, target_length, nb_iter, protect, smooth_features) - , selection_(selection) - { - ; - } + , selection_(selection), edges_to_protect_(edges_to_protect) + {} // Constructor Remesh_polyhedron_item_for_parallel_for( const Remesh_polyhedron_item_for_parallel_for &remesh) : RemeshFunctor(remesh) , selection_(remesh.selection_) + , edges_to_protect_(remesh.edges_to_protect_) {} // operator() void operator()(const tbb::blocked_range& r) const { for (size_t i = r.begin(); i != r.end(); ++i) - RemeshFunctor::remesh(selection_[i]); + RemeshFunctor::remesh(selection_[i], edges_to_protect_[selection_[i]]); } }; #endif From 6823336c17f93527ce43d132374f20fc05e5603f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 17 Mar 2016 09:53:37 +0100 Subject: [PATCH 04/11] add an option to constrain duplicated edges --- .../Plugins/PMP/Isotropic_remeshing_dialog.ui | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_dialog.ui b/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_dialog.ui index 51a81169f3d..3dd76af1348 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_dialog.ui +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_dialog.ui @@ -162,6 +162,23 @@ + + + + + + + + + + + Constrain duplicated edges + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + From 716e1ea9a611bdb8baed91397293c0a96bc5f81d Mon Sep 17 00:00:00 2001 From: Andreas Fabri Date: Thu, 17 Mar 2016 14:51:48 +0100 Subject: [PATCH 05/11] Detect duplicates also for a single connected component or a selection --- .../PMP/Isotropic_remeshing_plugin.cpp | 111 +++++++++++++----- Property_map/include/CGAL/property_map.h | 34 ++++++ 2 files changed, 113 insertions(+), 32 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp index 442ee6de062..e208a262063 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp @@ -16,6 +16,7 @@ #include #include +#include #include #include @@ -50,6 +51,9 @@ class Polyhedron_demo_isotropic_remeshing_plugin : typedef boost::graph_traits::edge_descriptor edge_descriptor; + typedef boost::unordered_set Edge_set; + typedef Scene_polyhedron_selection_item::Is_constrained_map Edge_constrained_pmap; + public: void init(QMainWindow* mainWindow, Scene_interface* scene_interface) { @@ -85,6 +89,56 @@ public: return false; } + + + void detect_duplicates(const Polyhedron& pmesh, + Edge_set& edges_to_protect) + { + typedef Polyhedron::Point_3 Point_3; + typedef std::pair Segment_3; + + std::map duplicates; + + BOOST_FOREACH(edge_descriptor ed, edges(pmesh)){ + Point_3 p = source(ed,pmesh)->point(), q = target(ed,pmesh)->point(); + Segment_3 s = (p < q)? std::make_pair(p,q): std::make_pair(q,p); + + std::map::iterator + it = duplicates.find(s); + if(it == duplicates.end()){ + duplicates[s] = ed; + }else{ + edges_to_protect.insert(it->second); + edges_to_protect.insert(ed); + } + } + } + + void detect_duplicates(std::vector& selection, + std::map& edges_to_protect) + { + typedef Polyhedron::Point_3 Point_3; + typedef std::pair Segment_3; + + std::map > duplicates; + + BOOST_FOREACH(Scene_polyhedron_item* poly_item, selection){ + const Polyhedron& pmesh = *poly_item->polyhedron(); + BOOST_FOREACH(edge_descriptor ed, edges(pmesh)){ + Point_3 p = source(ed,pmesh)->point(), q = target(ed,pmesh)->point(); + Segment_3 s = (p < q)? std::make_pair(p,q): std::make_pair(q,p); + std::map >::iterator + it = duplicates.find(s); + if(it == duplicates.end()){ + duplicates[s] = std::make_pair(poly_item,ed); + }else{ + edges_to_protect[it->second.first].insert(it->second.second); + edges_to_protect[poly_item].insert(ed); + } + } + } + } + public Q_SLOTS: void isotropic_remeshing() { @@ -116,6 +170,7 @@ public Q_SLOTS: return; } bool edges_only = ui.splitEdgesOnly_checkbox->isChecked(); + bool constrain_duplicates = ui.constrainDuplicates_checkbox->isChecked(); double target_length = ui.edgeLength_dspinbox->value(); unsigned int nb_iter = ui.nbIterations_spinbox->value(); bool protect = ui.protect_checkbox->isChecked(); @@ -134,6 +189,12 @@ public Q_SLOTS: const Polyhedron& pmesh = (poly_item != NULL) ? *poly_item->polyhedron() : *selection_item->polyhedron(); + + Edge_set edges_to_protect; + if(constrain_duplicates){ + detect_duplicates(pmesh, edges_to_protect); + } + boost::property_map::type fim = get(CGAL::face_index, pmesh); unsigned int id = 0; @@ -176,13 +237,16 @@ public Q_SLOTS: } else { + Edge_constrained_pmap ecm(edges_to_protect); + CGAL::OR_property_map etp(ecm, selection_item->constrained_edges_pmap()); + CGAL::Polygon_mesh_processing::isotropic_remeshing( selection_item->selected_facets , target_length , *selection_item->polyhedron() , CGAL::Polygon_mesh_processing::parameters::number_of_iterations(nb_iter) .protect_constraints(protect) - .edge_is_constrained_map(selection_item->constrained_edges_pmap()) + .edge_is_constrained_map(etp) .smooth_along_features(smooth_features) .vertex_is_constrained_map(selection_item->constrained_vertices_pmap())); } @@ -214,12 +278,14 @@ public Q_SLOTS: } else { + Scene_polyhedron_selection_item::Is_constrained_map ecm(edges_to_protect); CGAL::Polygon_mesh_processing::isotropic_remeshing( faces(*poly_item->polyhedron()) , target_length , *poly_item->polyhedron() , CGAL::Polygon_mesh_processing::parameters::number_of_iterations(nb_iter) .protect_constraints(protect) + .edge_is_constrained_map(ecm) .smooth_along_features(smooth_features)); } poly_item->invalidateOpenGLBuffers(); @@ -238,7 +304,7 @@ public Q_SLOTS: void isotropic_remeshing_of_several_polyhedra() { // Remeshing parameters - bool edges_only = false; + bool edges_only = false, constrain_duplicates = false; double target_length = 0.; unsigned int nb_iter = 1; bool protect = false; @@ -274,6 +340,7 @@ public Q_SLOTS: } edges_only = ui.splitEdgesOnly_checkbox->isChecked(); + constrain_duplicates = ui.constrainDuplicates_checkbox->isChecked(); target_length = ui.edgeLength_dspinbox->value(); nb_iter = ui.nbIterations_spinbox->value(); protect = ui.protect_checkbox->isChecked(); @@ -293,31 +360,11 @@ public Q_SLOTS: int total_time = 0; - typedef boost::graph_traits::edge_descriptor edge_descriptor; - std::map > edges_to_protect; + // typedef boost::graph_traits::edge_descriptor edge_descriptor; + std::map edges_to_protect; - if(selection.size()>1){ - typedef Polyhedron::Point_3 Point_3; - typedef std::pair Segment_3; - - std::map > duplicates; - - BOOST_FOREACH(Scene_polyhedron_item* poly_item, selection){ - const Polyhedron& pmesh = *poly_item->polyhedron(); - BOOST_FOREACH(edge_descriptor ed, edges(pmesh)){ - Point_3 p = source(ed,pmesh)->point(), q = target(ed,pmesh)->point(); - Segment_3 s = (p < q)? std::make_pair(p,q): std::make_pair(q,p); - - std::map >::iterator - it = duplicates.find(s); - if(it == duplicates.end()){ - duplicates[s] = std::make_pair(poly_item,ed); - }else{ - edges_to_protect[it->second.first].insert(it->second.second); - edges_to_protect[poly_item].insert(ed); - } - } - } + if(constrain_duplicates){ + detect_duplicates(selection, edges_to_protect); } #ifdef CGAL_LINKED_WITH_TBB @@ -375,7 +422,7 @@ private: protected: void remesh(Scene_polyhedron_item* poly_item, - std::set& edges_to_protect) const + Edge_set& edges_to_protect) const { //fill face_index property map boost::property_map::type fim @@ -402,14 +449,14 @@ private: } else { - Scene_polyhedron_selection_item::Is_constrained_map > ecm(edges_to_protect); + Scene_polyhedron_selection_item::Is_constrained_map ecm(edges_to_protect); CGAL::Polygon_mesh_processing::isotropic_remeshing( faces(*poly_item->polyhedron()) , target_length_ , *poly_item->polyhedron() , CGAL::Polygon_mesh_processing::parameters::number_of_iterations(nb_iter_) .protect_constraints(protect_) - .edge_is_constrained_map(ecm) + .edge_is_constrained_map(ecm) .smooth_along_features(smooth_features_)); } } @@ -437,7 +484,7 @@ private: {} void operator()(Scene_polyhedron_item* poly_item, - std::set& edges_to_protect) const + Edge_set& edges_to_protect) const { remesh(poly_item, edges_to_protect); } @@ -449,13 +496,13 @@ private: : RemeshFunctor { const std::vector& selection_; - std::map >& edges_to_protect_; + std::map& edges_to_protect_; public: // Constructor Remesh_polyhedron_item_for_parallel_for( const std::vector& selection, - std::map >& edges_to_protect, + std::map& edges_to_protect, const bool edges_only, const double target_length, const unsigned int nb_iter, diff --git a/Property_map/include/CGAL/property_map.h b/Property_map/include/CGAL/property_map.h index fa41272e98a..a4a8f871169 100644 --- a/Property_map/include/CGAL/property_map.h +++ b/Property_map/include/CGAL/property_map.h @@ -38,6 +38,40 @@ namespace CGAL { /// \cond SKIP_DOXYGEN + + +template +class OR_property_map { + typedef typename PM1::key_type key_type; + typedef typename PM1::value_type value_type; + typedef typename PM1::reference reference; + typedef boost::read_write_property_map_tag category; + + PM1 pm1; + PM2 pm2; + + public: + OR_property_map(PM1 pm1, PM2 pm2) + : pm1(pm1),pm2(pm2) + {} + + inline friend + value_type + get(const OR_property_map& pm, const key_type& k) + { + return get(pm.pm1,k) || get(pm.pm2,k); + } + + inline friend + void + put(OR_property_map& pm, const key_type& k, const value_type& v) + { + put(pm.pm1,k, v); + put(pm.pm2,k, v); + } + +}; + /// Property map that accesses a value from an iterator /// /// \cgalModels `ReadablePropertyMap` From 315a2e91848c270f0b4572ca65e759a2fb34464c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 17 Mar 2016 17:57:34 +0100 Subject: [PATCH 06/11] connect constraining of duplicated edge to the protection of constraints --- .../Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp index e208a262063..fa50ac339cf 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp @@ -548,6 +548,10 @@ private: ui.smooth1D_checkbox, SLOT(setDisabled(bool))); connect(ui.splitEdgesOnly_checkbox, SIGNAL(toggled(bool)), ui.smooth1D_checkbox, SLOT(setDisabled(bool))); + connect(ui.constrainDuplicates_checkbox, SIGNAL(toggled(bool)), + ui.protect_checkbox, SLOT(setChecked(bool))); + connect(ui.constrainDuplicates_checkbox, SIGNAL(toggled(bool)), + ui.protect_checkbox, SLOT(setDisabled(bool))); //Set default parameters Scene_interface::Bbox bbox = poly_item != NULL ? poly_item->bbox() From f2b3a4dd2400b85bdc2b4ae3c39d20c1c63197dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 18 Mar 2016 18:25:10 +0100 Subject: [PATCH 07/11] remeshing with duplicated constraints now splits the constrained edges this ensures the remeshing will always work --- .../Plugins/PMP/Isotropic_remeshing_dialog.ui | 6 +- .../PMP/Isotropic_remeshing_plugin.cpp | 241 +++++++++++++----- 2 files changed, 186 insertions(+), 61 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_dialog.ui b/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_dialog.ui index 3dd76af1348..3ddde77db51 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_dialog.ui +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_dialog.ui @@ -163,16 +163,16 @@ - + - + - Constrain duplicated edges + Preserve duplicated edges Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp index fa50ac339cf..dca62a081c3 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp @@ -14,6 +14,9 @@ #include #include #include +#include +#include +#include #include #include @@ -40,6 +43,122 @@ #include "ui_Isotropic_remeshing_dialog.h" +// give a halfedge and a target edge length, put in `out` points +// which the edge equally spaced such that splitting the edge +// using the sequence of points make the edges shorter than +// `target_length` +template +PointOutputIterator +sample_edge( + typename boost::graph_traits::halfedge_descriptor hd, + TriangleMesh& triangle_mesh, + double target_length, + const PointPMap& pmap, + PointOutputIterator out) +{ + typedef typename boost::property_traits::value_type Point_3; + typedef typename CGAL::Kernel_traits::Kernel::Vector_3 Vector_3; + typename boost::property_traits::reference src=get(pmap, source(hd,triangle_mesh) ); + typename boost::property_traits::reference tgt=get(pmap, target(hd,triangle_mesh) ); + + double length = std::sqrt( CGAL::squared_distance(src, tgt) ); + if ( length <= target_length ) return out; + + double nb_points = std::floor( length / target_length ); + Vector_3 unit = (tgt-src) / (nb_points+1); + + for(double i=0; i +EdgeOutputIterator +split_identical_edges( + typename boost::graph_traits::halfedge_descriptor hd, + TriangleMesh& tm, + const PointPMap& pmap, + const PointRange& points, + EdgeOutputIterator out) +{ + typedef typename PointRange::value_type Point_3; + typedef boost::graph_traits GT; + typedef typename GT::halfedge_descriptor halfedge_descriptor; + + halfedge_descriptor opposite_hd=opposite(hd, tm); + + BOOST_FOREACH(const Point_3& p, points) + { + // split the edge + halfedge_descriptor new_hd=CGAL::Euler::split_edge(hd,tm); + // set the vertex point + put(pmap, target(new_hd, tm), p); + *out++=edge(new_hd, tm); + } + *out++=edge(hd, tm); + return out; +} + +// HedgeRange is expected to be a range with value type being +// std::pair +// Given a set of halfedges representing different edges +// but with identical endpoints, and a target edge length +// we split all edges identically so that subedges are +// or length <= length +template +void split_long_duplicated_edge(const HedgeRange& hedge_range, + double target_length, + Edges_to_protect& edges_to_protect) +{ + typedef typename HedgeRange::value_type Pair; + typedef typename Pair::first_type halfedge_descriptor; + typedef typename boost::remove_pointer< + typename Pair::second_type>::type TriangleMesh; + typedef typename boost::property_map::type PointPMap; + typedef typename boost::property_traits::value_type Point_3; + typedef boost::graph_traits GT; + typedef typename GT::face_descriptor face_descriptor; + + if (hedge_range.empty()) return; + + const Pair& p = *hedge_range.begin(); + PointPMap pmap = get(boost::vertex_point, *p.second); + + std::vector points; + halfedge_descriptor hd = p.first; + + // collect points to be add inside the edges + sample_edge(hd, *p.second, target_length, pmap, std::back_inserter(points) ); + + CGAL_assertion_code(Point_3 src = get(pmap, source(hd, *p.second));) + CGAL_assertion_code(Point_3 tgt = get(pmap, target(hd, *p.second));) + + // split the edges and collect faces to triangulate + BOOST_FOREACH(const Pair& h_and_p, hedge_range) + { + halfedge_descriptor hc=h_and_p.first; + TriangleMesh* polyc = h_and_p.second; + + PointPMap pmap_2 = get(boost::vertex_point, *polyc); + //make sure halfedge are consistently oriented + CGAL_assertion( get(pmap_2, source(hc, *polyc)) == src ); + CGAL_assertion( get(pmap_2, target(hc, *polyc)) == tgt ); + + typedef typename Edges_to_protect::value_type::second_type Edge_set; + Edge_set& edge_set = edges_to_protect[polyc]; + + // now split the halfedge and incident faces + split_identical_edges(hc,*polyc,pmap_2, points, + std::inserter( edge_set, edge_set.begin())); + } +} + using namespace CGAL::Three; class Polyhedron_demo_isotropic_remeshing_plugin : public QObject, @@ -50,6 +169,8 @@ class Polyhedron_demo_isotropic_remeshing_plugin : Q_PLUGIN_METADATA(IID "com.geometryfactory.PolyhedronDemo.PluginInterface/1.0") typedef boost::graph_traits::edge_descriptor edge_descriptor; + typedef boost::graph_traits::halfedge_descriptor halfedge_descriptor; + typedef boost::graph_traits::face_descriptor face_descriptor; typedef boost::unordered_set Edge_set; typedef Scene_polyhedron_selection_item::Is_constrained_map Edge_constrained_pmap; @@ -89,56 +210,58 @@ public: return false; } - - - void detect_duplicates(const Polyhedron& pmesh, - Edge_set& edges_to_protect) + void detect_and_split_duplicates(std::vector& selection, + std::map& edges_to_protect, + double target_length) { typedef Polyhedron::Point_3 Point_3; typedef std::pair Segment_3; - std::map duplicates; + typedef std::map< Segment_3, + std::vector< std::pair > > MapType; + MapType duplicated_edges; - BOOST_FOREACH(edge_descriptor ed, edges(pmesh)){ - Point_3 p = source(ed,pmesh)->point(), q = target(ed,pmesh)->point(); - Segment_3 s = (p < q)? std::make_pair(p,q): std::make_pair(q,p); - - std::map::iterator - it = duplicates.find(s); - if(it == duplicates.end()){ - duplicates[s] = ed; - }else{ - edges_to_protect.insert(it->second); - edges_to_protect.insert(ed); - } - } - } - - void detect_duplicates(std::vector& selection, - std::map& edges_to_protect) - { - typedef Polyhedron::Point_3 Point_3; - typedef std::pair Segment_3; - - std::map > duplicates; BOOST_FOREACH(Scene_polyhedron_item* poly_item, selection){ - const Polyhedron& pmesh = *poly_item->polyhedron(); + Polyhedron& pmesh = *poly_item->polyhedron(); BOOST_FOREACH(edge_descriptor ed, edges(pmesh)){ - Point_3 p = source(ed,pmesh)->point(), q = target(ed,pmesh)->point(); - Segment_3 s = (p < q)? std::make_pair(p,q): std::make_pair(q,p); - std::map >::iterator - it = duplicates.find(s); - if(it == duplicates.end()){ - duplicates[s] = std::make_pair(poly_item,ed); - }else{ - edges_to_protect[it->second.first].insert(it->second.second); - edges_to_protect[poly_item].insert(ed); - } + halfedge_descriptor hd = halfedge(ed,pmesh); + Point_3 p = source(hd,pmesh)->point(), q = target(hd,pmesh)->point(); + Segment_3 s = CGAL::make_sorted_pair(p,q); + if (s.first==q) hd=opposite(hd,pmesh); // make sure the halfedges are consistently oriented + + duplicated_edges[s].push_back( std::make_pair(hd,&pmesh) ); } } + + // consistently split duplicate edges and triangulate incident faces + typedef std::pair Face_and_poly; + std::set< Face_and_poly > faces_to_triangulate; + BOOST_FOREACH(const MapType::value_type& p, duplicated_edges) + if (p.second.size()>1){ + //collect faces to retriangulate + typedef std::pair Pair_type; + BOOST_FOREACH(const Pair_type& h_and_p, p.second) + { + halfedge_descriptor hc=h_and_p.first; + Polyhedron* polyc = h_and_p.second; + + if ( !is_border(hc, *polyc) ) + faces_to_triangulate.insert( Face_and_poly(face(hc,*polyc), polyc) ); + if ( !is_border(opposite(hc, *polyc), *polyc) ) + faces_to_triangulate.insert( + Face_and_poly(face(opposite(hc, *polyc),*polyc), polyc) ); + } + // split the edges + split_long_duplicated_edge(p.second, target_length, edges_to_protect); + } + // now retriangulate + namespace PMP=CGAL::Polygon_mesh_processing; + BOOST_FOREACH(Face_and_poly f_and_p, faces_to_triangulate) + PMP::triangulate_face(f_and_p.first, *f_and_p.second); } + public Q_SLOTS: void isotropic_remeshing() { @@ -170,7 +293,7 @@ public Q_SLOTS: return; } bool edges_only = ui.splitEdgesOnly_checkbox->isChecked(); - bool constrain_duplicates = ui.constrainDuplicates_checkbox->isChecked(); + bool preserve_duplicates = ui.preserveDuplicates_checkbox->isChecked(); double target_length = ui.edgeLength_dspinbox->value(); unsigned int nb_iter = ui.nbIterations_spinbox->value(); bool protect = ui.protect_checkbox->isChecked(); @@ -190,11 +313,14 @@ public Q_SLOTS: ? *poly_item->polyhedron() : *selection_item->polyhedron(); - Edge_set edges_to_protect; - if(constrain_duplicates){ - detect_duplicates(pmesh, edges_to_protect); - } - + // tricks to use the function detect_and_split_duplicates + // that uses several poly items + std::map edges_to_protect_map; + std::vector poly_items(1,poly_item); + Edge_set& edges_to_protect=edges_to_protect_map[poly_item->polyhedron()]; + if(preserve_duplicates) + detect_and_split_duplicates(poly_items, edges_to_protect_map, target_length); + boost::property_map::type fim = get(CGAL::face_index, pmesh); unsigned int id = 0; @@ -304,7 +430,7 @@ public Q_SLOTS: void isotropic_remeshing_of_several_polyhedra() { // Remeshing parameters - bool edges_only = false, constrain_duplicates = false; + bool edges_only = false, preserve_duplicates = false; double target_length = 0.; unsigned int nb_iter = 1; bool protect = false; @@ -340,7 +466,7 @@ public Q_SLOTS: } edges_only = ui.splitEdgesOnly_checkbox->isChecked(); - constrain_duplicates = ui.constrainDuplicates_checkbox->isChecked(); + preserve_duplicates = ui.preserveDuplicates_checkbox->isChecked(); target_length = ui.edgeLength_dspinbox->value(); nb_iter = ui.nbIterations_spinbox->value(); protect = ui.protect_checkbox->isChecked(); @@ -361,11 +487,10 @@ public Q_SLOTS: // typedef boost::graph_traits::edge_descriptor edge_descriptor; - std::map edges_to_protect; + std::map edges_to_protect; - if(constrain_duplicates){ - detect_duplicates(selection, edges_to_protect); - } + if(preserve_duplicates) + detect_and_split_duplicates(selection, edges_to_protect, target_length); #ifdef CGAL_LINKED_WITH_TBB QTime time; @@ -379,7 +504,7 @@ public Q_SLOTS: total_time = time.elapsed(); #else - + Remesh_polyhedron_item remesher(edges_only, target_length, nb_iter, protect, smooth_features); BOOST_FOREACH(Scene_polyhedron_item* poly_item, selection) @@ -387,7 +512,7 @@ public Q_SLOTS: QTime time; time.start(); - remesher(poly_item, edges_to_protect[poly_item]); + remesher(poly_item, edges_to_protect[poly_item->polyhedron()]); total_time += time.elapsed(); std::cout << "Remeshing of " << poly_item->name().data() @@ -402,7 +527,7 @@ public Q_SLOTS: poly_item->invalidateOpenGLBuffers(); Q_EMIT poly_item->itemChanged(); } - + // default cursor QApplication::restoreOverrideCursor(); } @@ -496,13 +621,13 @@ private: : RemeshFunctor { const std::vector& selection_; - std::map& edges_to_protect_; + std::map& edges_to_protect_; public: // Constructor Remesh_polyhedron_item_for_parallel_for( const std::vector& selection, - std::map& edges_to_protect, + std::map& edges_to_protect, const bool edges_only, const double target_length, const unsigned int nb_iter, @@ -524,7 +649,7 @@ private: void operator()(const tbb::blocked_range& r) const { for (size_t i = r.begin(); i != r.end(); ++i) - RemeshFunctor::remesh(selection_[i], edges_to_protect_[selection_[i]]); + RemeshFunctor::remesh(selection_[i], edges_to_protect_[selection_[i]->polyhedron()]); } }; #endif @@ -548,9 +673,9 @@ private: ui.smooth1D_checkbox, SLOT(setDisabled(bool))); connect(ui.splitEdgesOnly_checkbox, SIGNAL(toggled(bool)), ui.smooth1D_checkbox, SLOT(setDisabled(bool))); - connect(ui.constrainDuplicates_checkbox, SIGNAL(toggled(bool)), + connect(ui.preserveDuplicates_checkbox, SIGNAL(toggled(bool)), ui.protect_checkbox, SLOT(setChecked(bool))); - connect(ui.constrainDuplicates_checkbox, SIGNAL(toggled(bool)), + connect(ui.preserveDuplicates_checkbox, SIGNAL(toggled(bool)), ui.protect_checkbox, SLOT(setDisabled(bool))); //Set default parameters From 9e82d734c2002d52d72986974783c0789421e0c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Thu, 24 Mar 2016 17:26:19 +0100 Subject: [PATCH 08/11] remove unused typedef and variable --- .../demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp index dca62a081c3..1652b80338f 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp @@ -90,8 +90,6 @@ split_identical_edges( typedef boost::graph_traits GT; typedef typename GT::halfedge_descriptor halfedge_descriptor; - halfedge_descriptor opposite_hd=opposite(hd, tm); - BOOST_FOREACH(const Point_3& p, points) { // split the edge @@ -123,7 +121,6 @@ void split_long_duplicated_edge(const HedgeRange& hedge_range, CGAL::vertex_point_t>::type PointPMap; typedef typename boost::property_traits::value_type Point_3; typedef boost::graph_traits GT; - typedef typename GT::face_descriptor face_descriptor; if (hedge_range.empty()) return; From f7848d686ae896b5070b5df07a56cdb04e25f019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Fri, 25 Mar 2016 10:16:21 +0100 Subject: [PATCH 09/11] remove unused type --- .../demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp index 1652b80338f..8861170d0d4 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp @@ -120,7 +120,6 @@ void split_long_duplicated_edge(const HedgeRange& hedge_range, typedef typename boost::property_map::type PointPMap; typedef typename boost::property_traits::value_type Point_3; - typedef boost::graph_traits GT; if (hedge_range.empty()) return; From cc28746e18d754165b0cdffc5caefb6bc8fb9227 Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Fri, 25 Mar 2016 10:39:01 +0100 Subject: [PATCH 10/11] fix the case where both faces incident to h are degenerate before the flip and none of them is degenerate after the flip --- .../Isotropic_remeshing/remesh_impl.h | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index bc1e7cd1728..1d77f283dc0 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -1280,19 +1281,23 @@ private: const double& sq_low) { CGAL_assertion_code(std::size_t nb_done = 0); - std::vector degenerate_faces; + boost::unordered_set degenerate_faces; BOOST_FOREACH(halfedge_descriptor h, halfedges_around_target(halfedge(v, mesh_), mesh_)) { if (is_border(h, mesh_)) continue; if (PMP::is_degenerated(h, mesh_, vpmap_, GeomTraits())) - degenerate_faces.push_back(h); + degenerate_faces.insert(h); } while(!degenerate_faces.empty()) { - halfedge_descriptor h = degenerate_faces.back(); - degenerate_faces.pop_back(); + halfedge_descriptor h = *(degenerate_faces.begin()); + degenerate_faces.erase(degenerate_faces.begin()); + + //check that opposite is not also degenerate + if (degenerate_faces.find(opposite(h, mesh_)) != degenerate_faces.end()) + degenerate_faces.erase(opposite(h, mesh_)); CGAL_assertion(PMP::is_degenerated(h, mesh_, vpmap_, GeomTraits())); if (face(h, mesh_) == boost::graph_traits::null_face()) @@ -1338,13 +1343,22 @@ private: short_edges.insert(typename Bimap::value_type(hf, sqlen)); } + std::size_t nb_degen = degenerate_faces.size(); if (!is_border(hf, mesh_) && PMP::is_degenerated(hf, mesh_, vpmap_, GeomTraits())) - degenerate_faces.push_back(hf); + degenerate_faces.insert(hf); if (!is_border(hfo, mesh_) && PMP::is_degenerated(hfo, mesh_, vpmap_, GeomTraits())) - degenerate_faces.push_back(hfo); + degenerate_faces.insert(hfo); + if (degenerate_faces.size() == nb_degen + 2) + { + //process has failed to remove degeneracies + degenerate_faces.erase(hf); + degenerate_faces.erase(hfo); + std::cerr << "Warning : possible degeneracies remaining " + << "after the edge collapse step" << std::endl; + } break; } } From 2bc034dee1263a8c21ac1a49bd6c6c882a81ca4a Mon Sep 17 00:00:00 2001 From: Jane Tournois Date: Thu, 24 Mar 2016 16:25:00 +0100 Subject: [PATCH 11/11] don't forget to map boundary halfedges --- BGL/include/CGAL/boost/graph/convert_surface_mesh.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/BGL/include/CGAL/boost/graph/convert_surface_mesh.h b/BGL/include/CGAL/boost/graph/convert_surface_mesh.h index 69457c6ad72..7a964e7ccfd 100644 --- a/BGL/include/CGAL/boost/graph/convert_surface_mesh.h +++ b/BGL/include/CGAL/boost/graph/convert_surface_mesh.h @@ -70,6 +70,10 @@ namespace CGAL { } do { h2h.insert(std::make_pair(shd, thd)); + + if (face(opposite(shd, sm), sm) == boost::graph_traits::null_face()) + h2h.insert(std::make_pair(opposite(shd, sm), opposite(thd, tm))); + shd = next(shd,sm); thd = next(thd,tm); }while(shd != done);