Merge pull request #7125 from MaelRL/PMP-flip_criterion-GF

Modify flipping criteria in PMP::isotropic_remeshing and PMP::refine
This commit is contained in:
Laurent Rineau 2023-02-10 15:40:49 +01:00
commit d33d71a4e0
4 changed files with 106 additions and 72 deletions

View File

@ -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;

View File

@ -15,12 +15,6 @@
#include <CGAL/license/Polygon_mesh_processing/meshing_hole_filling.h>
#include <cmath>
#include <map>
#include <set>
#include <list>
#include <CGAL/assertions.h>
#ifdef CGAL_PMP_FAIR_DEBUG
#include <CGAL/Timer.h>
@ -30,8 +24,14 @@
#include <CGAL/boost/graph/iterator.h>
#include <CGAL/boost/graph/Euler_operations.h>
#include <CGAL/boost/graph/properties.h>
#include <CGAL/Polygon_mesh_processing/repair_degeneracies.h>
#include <CGAL/property_map.h>
#include <cmath>
#include <map>
#include <set>
#include <list>
namespace CGAL {
namespace Polygon_mesh_processing {
@ -49,15 +49,28 @@ class Refine_Polyhedron_3 {
typedef Halfedge_around_face_circulator<PolygonMesh> Halfedge_around_facet_circulator;
typedef Halfedge_around_target_circulator<PolygonMesh> Halfedge_around_vertex_circulator;
typedef typename CGAL::Kernel_traits<Point_3>::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<VertexPointMap>::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<PolygonMesh> circ(halfedge(fd,pmesh),pmesh), done(circ);
do {
vertex_descriptor v = target(*circ,pmesh);
std::pair<typename std::map<vertex_descriptor, double>::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);

View File

@ -294,7 +294,6 @@ get_best_edge_orientation(typename boost::graph_traits<TriangleMesh>::edge_descr
return boost::graph_traits<TriangleMesh>::null_halfedge();
}
// adapted from triangulate_faces
template <typename TriangleMesh, typename VPM, typename Traits>
bool should_flip(typename boost::graph_traits<TriangleMesh>::edge_descriptor e,
const TriangleMesh& tmesh,
@ -303,49 +302,23 @@ bool should_flip(typename boost::graph_traits<TriangleMesh>::edge_descriptor e,
{
typedef typename boost::graph_traits<TriangleMesh>::halfedge_descriptor halfedge_descriptor;
typedef typename Traits::FT FT;
typedef typename Traits:: FT FT;
typedef typename boost::property_traits<VPM>::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 <class TriangleMesh, class VPM, class Traits, class Functor>

View File

@ -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"