diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/isotropic_remeshing_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/isotropic_remeshing_example.cpp index 048739c136f..159ff7cd708 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/isotropic_remeshing_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/isotropic_remeshing_example.cpp @@ -43,7 +43,7 @@ int main(int argc, char* argv[]) } double target_edge_length = (argc > 2) ? std::stod(std::string(argv[2])) : 0.04; - unsigned int nb_iter = 3; + unsigned int nb_iter = (argc > 3) ? std::stoi(std::string(argv[3])) : 10; std::cout << "Split border..."; @@ -59,6 +59,8 @@ int main(int argc, char* argv[]) CGAL::parameters::number_of_iterations(nb_iter) .protect_constraints(true)); //i.e. protect border, here + CGAL::IO::write_polygon_mesh("out.off", mesh, CGAL::parameters::stream_precision(17)); + std::cout << "Remeshing done." << std::endl; return 0; diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/refine_impl.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/refine_impl.h index 54369aee910..06ddc868f0d 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/refine_impl.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/internal/refine_impl.h @@ -15,12 +15,6 @@ #include - -#include -#include -#include -#include - #include #ifdef CGAL_PMP_FAIR_DEBUG #include @@ -30,8 +24,14 @@ #include #include #include +#include #include +#include +#include +#include +#include + namespace CGAL { namespace Polygon_mesh_processing { @@ -49,15 +49,28 @@ class Refine_Polyhedron_3 { typedef Halfedge_around_face_circulator Halfedge_around_facet_circulator; typedef Halfedge_around_target_circulator Halfedge_around_vertex_circulator; + typedef typename CGAL::Kernel_traits::type Traits; + private: PolygonMesh& pmesh; VertexPointMap vpmap; + Traits traits = {}; - bool flippable(halfedge_descriptor h) { + bool flippable(halfedge_descriptor h) + { // this check is added so that edge flip does not break manifoldness // it might happen when there is an edge where flip_edge(h) will be placed (i.e. two edges collide after flip) vertex_descriptor v_tip_0 = target(next(h,pmesh),pmesh); vertex_descriptor v_tip_1 = target(next(opposite(h,pmesh),pmesh),pmesh); + +#ifdef CGAL_PMP_REFINE_DEBUG_PP + std::cout << "flippable() " << h << std::endl; + std::cout << "\t" << source(h, pmesh) << ": " << pmesh.point(source(h, pmesh)) << std::endl; + std::cout << "\t" << target(h, pmesh) << ": " << pmesh.point(target(h, pmesh)) << std::endl; + std::cout << "\t" << v_tip_0 << ": " << pmesh.point(v_tip_0) << std::endl; + std::cout << "\t" << v_tip_1 << ": " << pmesh.point(v_tip_1) << std::endl; +#endif + Halfedge_around_vertex_circulator v_cir(next(h,pmesh), pmesh), v_end(v_cir); do { if(target(opposite(*v_cir, pmesh),pmesh) == v_tip_1) { return false; } @@ -74,13 +87,21 @@ private: bool relax(halfedge_descriptor h) { +#ifdef CGAL_PMP_REFINE_DEBUG_PP typedef typename boost::property_traits::reference Point_3_ref; - Point_3_ref p = get(vpmap, target(h,pmesh)); - Point_3_ref q = get(vpmap, target(opposite(h,pmesh),pmesh)); + Point_3_ref p = get(vpmap, source(h,pmesh)); + Point_3_ref q = get(vpmap, target(h,pmesh)); Point_3_ref r = get(vpmap, target(next(h,pmesh),pmesh)); Point_3_ref s = get(vpmap, target(next(opposite(h,pmesh),pmesh),pmesh)); - if( (CGAL::ON_UNBOUNDED_SIDE != CGAL::side_of_bounded_sphere(p,q,r,s)) || - (CGAL::ON_UNBOUNDED_SIDE != CGAL::side_of_bounded_sphere(p,q,s,r)) ) + + std::cout << "relax() " << h << std::endl; + std::cout << "\t" << source(h, pmesh) << ": " << p << std::endl; + std::cout << "\t" << target(h, pmesh) << ": " << q << std::endl; + std::cout << "\t" << target(next(h,pmesh),pmesh) << ": " << r << std::endl; + std::cout << "\t" << target(next(opposite(h,pmesh),pmesh),pmesh) << ": " << s << std::endl; +#endif + + if(internal::should_flip(edge(h, pmesh), pmesh, vpmap, traits)) { if(flippable(h)) { Euler::flip_edge(h,pmesh); @@ -238,8 +259,7 @@ private: Halfedge_around_face_circulator circ(halfedge(fd,pmesh),pmesh), done(circ); do { vertex_descriptor v = target(*circ,pmesh); - std::pair::iterator, bool> v_insert - = scale_attribute.insert(std::make_pair(v, 0)); + auto v_insert = scale_attribute.emplace(v, 0); if(!v_insert.second) { continue; } // already calculated v_insert.first->second = average_length(v, interior_map, accept_internal_facets); } while(++circ != done); diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair_degeneracies.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair_degeneracies.h index bb7e48292a6..798499f430b 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair_degeneracies.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/repair_degeneracies.h @@ -294,7 +294,6 @@ get_best_edge_orientation(typename boost::graph_traits::edge_descr return boost::graph_traits::null_halfedge(); } -// adapted from triangulate_faces template bool should_flip(typename boost::graph_traits::edge_descriptor e, const TriangleMesh& tmesh, @@ -303,49 +302,23 @@ bool should_flip(typename boost::graph_traits::edge_descriptor e, { typedef typename boost::graph_traits::halfedge_descriptor halfedge_descriptor; - typedef typename Traits::FT FT; + typedef typename Traits:: FT FT; typedef typename boost::property_traits::reference Point_ref; - typedef typename Traits::Vector_3 Vector_3; CGAL_precondition(!is_border(e, tmesh)); - halfedge_descriptor h = halfedge(e, tmesh); + typename Traits::Compute_approximate_angle_3 angle = gt.compute_approximate_angle_3_object(); - Point_ref p0 = get(vpm, target(h, tmesh)); - Point_ref p1 = get(vpm, target(next(h, tmesh), tmesh)); - Point_ref p2 = get(vpm, source(h, tmesh)); - Point_ref p3 = get(vpm, target(next(opposite(h, tmesh), tmesh), tmesh)); + const halfedge_descriptor h = halfedge(e, tmesh); - /* Chooses the diagonal that will split the quad in two triangles that maximize - * the scalar product of of the un-normalized normals of the two triangles. - * The lengths of the un-normalized normals (computed using cross-products of two vectors) - * are proportional to the area of the triangles. - * Maximize the scalar product of the two normals will avoid skinny triangles, - * and will also taken into account the cosine of the angle between the two normals. - * In particular, if the two triangles are oriented in different directions, - * the scalar product will be negative. - */ + const Point_ref p0 = get(vpm, target(h, tmesh)); + const Point_ref p1 = get(vpm, target(next(h, tmesh), tmesh)); + const Point_ref p2 = get(vpm, source(h, tmesh)); + const Point_ref p3 = get(vpm, target(next(opposite(h, tmesh), tmesh), tmesh)); -// CGAL::cross_product(p2-p1, p3-p2) * CGAL::cross_product(p0-p3, p1-p0); -// CGAL::cross_product(p1-p0, p1-p2) * CGAL::cross_product(p3-p2, p3-p0); - - const Vector_3 v01 = gt.construct_vector_3_object()(p0, p1); - const Vector_3 v12 = gt.construct_vector_3_object()(p1, p2); - const Vector_3 v23 = gt.construct_vector_3_object()(p2, p3); - const Vector_3 v30 = gt.construct_vector_3_object()(p3, p0); - - const FT p1p3 = gt.compute_scalar_product_3_object()( - gt.construct_cross_product_vector_3_object()(v12, v23), - gt.construct_cross_product_vector_3_object()(v30, v01)); - - const Vector_3 v21 = gt.construct_opposite_vector_3_object()(v12); - const Vector_3 v03 = gt.construct_opposite_vector_3_object()(v30); - - const FT p0p2 = gt.compute_scalar_product_3_object()( - gt.construct_cross_product_vector_3_object()(v01, v21), - gt.construct_cross_product_vector_3_object()(v23, v03)); - - return p0p2 <= p1p3; + const FT ap1 = angle(p0,p1,p2); + const FT ap3 = angle(p2,p3,p0); + return (ap1 + ap3 > FT(180)); } template diff --git a/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp b/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp index ee9c3fa0e69..a15e1f14dfd 100644 --- a/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp +++ b/Polyhedron/demo/Polyhedron/Plugins/PMP/Isotropic_remeshing_plugin.cpp @@ -347,8 +347,7 @@ public Q_SLOTS: } // Create dialog box QDialog dialog(mw); - Ui::Isotropic_remeshing_dialog ui - = remeshing_dialog(&dialog, poly_item, selection_item); + initialize_remeshing_dialog(&dialog, poly_item, selection_item); // Get values int i = dialog.exec(); @@ -357,6 +356,7 @@ public Q_SLOTS: std::cout << "Remeshing aborted" << std::endl; return; } + bool edges_only = ui.splitEdgesOnly_checkbox->isChecked(); bool preserve_duplicates = ui.preserveDuplicates_checkbox->isChecked(); double target_length = ui.edgeLength_dspinbox->value(); @@ -710,7 +710,7 @@ public Q_SLOTS: if (target_length == 0.)//parameters have not been set yet { QDialog dialog(mw); - Ui::Isotropic_remeshing_dialog ui = remeshing_dialog(&dialog, poly_item); + initialize_remeshing_dialog(&dialog, poly_item); ui.objectName->setText(QString::number(scene->selectionIndices().size()) .append(QString(" items to be remeshed"))); int i = dialog.exec(); @@ -937,32 +937,73 @@ private: }; #endif - Ui::Isotropic_remeshing_dialog - remeshing_dialog(QDialog* dialog, - Scene_facegraph_item* poly_item, - Scene_polyhedron_selection_item* selection_item = nullptr) +public Q_SLOTS: + void update_after_protect_checkbox_click() + { + if(ui.protect_checkbox->isChecked()) + { + ui.smooth1D_label->setEnabled(false); + ui.smooth1D_checkbox->setEnabled(false); + ui.smooth1D_checkbox->setChecked(false); + } + else + { + ui.smooth1D_label->setEnabled(true); + ui.smooth1D_checkbox->setEnabled(true); + } + } + + void update_after_splitEdgesOnly_click() + { + if(ui.splitEdgesOnly_checkbox->isChecked()) + { + ui.nbIterations_label->setEnabled(false); + ui.nbIterations_spinbox->setEnabled(false); + ui.nbSmoothing_label->setEnabled(false); + ui.nbSmoothing_spinbox->setEnabled(false); + + ui.protect_label->setEnabled(false); + ui.protect_checkbox->setEnabled(false); + ui.protect_checkbox->setChecked(false); + + ui.smooth1D_label->setEnabled(false); + ui.smooth1D_checkbox->setEnabled(false); + ui.smooth1D_checkbox->setChecked(false); + } + else + { + ui.nbIterations_label->setEnabled(true); + ui.nbIterations_spinbox->setEnabled(true); + ui.nbSmoothing_label->setEnabled(true); + ui.nbSmoothing_spinbox->setEnabled(true); + + ui.protect_label->setEnabled(true); + ui.protect_checkbox->setEnabled(true); + + ui.smooth1D_label->setEnabled(true); + ui.smooth1D_checkbox->setEnabled(true); + } + } + +public: + void + initialize_remeshing_dialog(QDialog* dialog, + Scene_facegraph_item* poly_item, + Scene_polyhedron_selection_item* selection_item = nullptr) { - Ui::Isotropic_remeshing_dialog ui; ui.setupUi(dialog); connect(ui.buttonBox, SIGNAL(accepted()), dialog, SLOT(accept())); connect(ui.buttonBox, SIGNAL(rejected()), dialog, SLOT(reject())); //connect checkbox to spinbox - connect(ui.splitEdgesOnly_checkbox, SIGNAL(toggled(bool)), - ui.nbIterations_spinbox, SLOT(setDisabled(bool))); - connect(ui.splitEdgesOnly_checkbox, SIGNAL(toggled(bool)), - ui.protect_checkbox, SLOT(setDisabled(bool))); - connect(ui.splitEdgesOnly_checkbox, SIGNAL(toggled(bool)), - ui.smooth1D_checkbox, SLOT(setDisabled(bool))); - connect(ui.splitEdgesOnly_checkbox, SIGNAL(toggled(bool)), - ui.nbSmoothing_spinbox, SLOT(setDisabled(bool))); - connect(ui.protect_checkbox, SIGNAL(toggled(bool)), - ui.smooth1D_checkbox, SLOT(setDisabled(bool))); connect(ui.preserveDuplicates_checkbox, SIGNAL(toggled(bool)), ui.protect_checkbox, SLOT(setChecked(bool))); connect(ui.preserveDuplicates_checkbox, SIGNAL(toggled(bool)), ui.protect_checkbox, SLOT(setDisabled(bool))); + connect(ui.protect_checkbox, SIGNAL(clicked(bool)), this, SLOT(update_after_protect_checkbox_click())); + connect(ui.splitEdgesOnly_checkbox, SIGNAL(clicked(bool)), this, SLOT(update_after_splitEdgesOnly_click())); + //Set default parameters Scene_interface::Bbox bbox = poly_item != nullptr ? poly_item->bbox() : (selection_item != nullptr ? selection_item->bbox() @@ -1003,14 +1044,12 @@ private: ui.preserveDuplicates_checkbox->setDisabled(true); ui.preserveDuplicates_checkbox->setChecked(false); } - - return ui; } private: QAction* actionIsotropicRemeshing_; - + Ui::Isotropic_remeshing_dialog ui; }; // end Polyhedron_demo_isotropic_remeshing_plugin #include "Isotropic_remeshing_plugin.moc"