diff --git a/Lab/demo/Lab/Plugins/PMP/Isotropic_remeshing_dialog.ui b/Lab/demo/Lab/Plugins/PMP/Isotropic_remeshing_dialog.ui index fd2e9c6ab6c3..610b9b7f64f0 100644 --- a/Lab/demo/Lab/Plugins/PMP/Isotropic_remeshing_dialog.ui +++ b/Lab/demo/Lab/Plugins/PMP/Isotropic_remeshing_dialog.ui @@ -22,7 +22,6 @@ 15 - 75 true @@ -59,121 +58,78 @@ Isotropic remeshing - - - - - + + + + + + 110 + 0 + - - - - - - Minimum edge length + + 1 - - + + + + 0.00 + - - - - 0 + + + + -1 - - 100 + + false - - 1 + + + + + + Minimum edge length - + Error tolerance - - + + - + Target edge length - - true + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - + + - + Ball radius - - 0.00 + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - -1 - - - QLayout::SetDefaultConstraint - - - 11 - - - 14 - - - - - Edge sizing - - - - - - - - 0 - 0 - - - - - 168 - 16777215 - - - - - Uniform - - - - - Adaptive - - - - - - - Qt::Vertical + Qt::Orientation::Vertical @@ -183,53 +139,66 @@ - - - - Allow 1D smoothing along borders - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - + + - Protect borders/selected edges - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Maximum edge length + + true + + + + + 0 + + + 100 + + + 1 + + + Preserve duplicated edges - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - + + + + Qt::Orientation::Vertical + + + QSizePolicy::Policy::Maximum + + - 110 - 0 + 20 + 24 - - 1 + + + + + + @@ -239,43 +208,68 @@ Number of Main iterations - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter nbIterations_spinbox - - - - Qt::Vertical + + + + 0.00 - - QSizePolicy::Maximum + + + + + + 0.00 - - - 20 - 24 - + + + + + + - + + true + + - - + + - Target edge length + Curvature smoothing - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter + + + + + + + + + + + + + + Protect borders/selected edges + + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - Qt::ImhNone + Qt::InputMethodHint::ImhNone @@ -288,26 +282,64 @@ - - + + - Maximum edge length + Allow 1D smoothing along borders - - - - - - 0.00 + + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - 0.00 + + + + 6 - + + QLayout::SizeConstraint::SetDefaultConstraint + + + 11 + + + 14 + + + + + Edge sizing + + + + + + + + 0 + 0 + + + + + 168 + 16777215 + + + + + Uniform + + + + + Adaptive + + + + + @@ -315,37 +347,30 @@ Number of Smoothing iterations - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter - - - - Ball radius + + + + false - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + - - + + + + Qt::LayoutDirection::LeftToRight + - Curvature smoothing + Do not triangulate faces after split - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - -1 - - - false + Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter @@ -355,15 +380,12 @@ - Qt::Vertical - - - QSizePolicy::MinimumExpanding + Qt::Orientation::Vertical - 0 - 0 + 20 + 40 @@ -371,10 +393,10 @@ - Qt::Horizontal + Qt::Orientation::Horizontal - QDialogButtonBox::Cancel|QDialogButtonBox::Ok + QDialogButtonBox::StandardButton::Cancel|QDialogButtonBox::StandardButton::Ok @@ -388,7 +410,6 @@ - splitEdgesOnly_checkbox edgeLength_dspinbox nbIterations_spinbox nbSmoothing_spinbox diff --git a/Lab/demo/Lab/Plugins/PMP/Isotropic_remeshing_plugin.cpp b/Lab/demo/Lab/Plugins/PMP/Isotropic_remeshing_plugin.cpp index 948c76652473..f12e049c840d 100644 --- a/Lab/demo/Lab/Plugins/PMP/Isotropic_remeshing_plugin.cpp +++ b/Lab/demo/Lab/Plugins/PMP/Isotropic_remeshing_plugin.cpp @@ -301,7 +301,8 @@ class CGAL_Lab_isotropic_remeshing_plugin : const double error_tol, const double min_length, const double max_length, - const double curv_ball_r) + const double curv_ball_r, + const bool do_not_triangulate) { std::vector p_edges; for(edge_descriptor e : edges(pmesh)) @@ -327,7 +328,8 @@ class CGAL_Lab_isotropic_remeshing_plugin : , target_length , *selection_item->polyhedron() , CGAL::parameters::geom_traits(EPICK()) - .edge_is_constrained_map(selection_item->constrained_edges_pmap())); + .edge_is_constrained_map(selection_item->constrained_edges_pmap()) + .do_not_triangulate_faces(do_not_triangulate)); } else if (edge_sizing_type == 1) { @@ -343,7 +345,8 @@ class CGAL_Lab_isotropic_remeshing_plugin : , adaptive_sizing , *selection_item->polyhedron() , CGAL::parameters::geom_traits(EPICK()) - .edge_is_constrained_map(selection_item->constrained_edges_pmap())); + .edge_is_constrained_map(selection_item->constrained_edges_pmap()) + .do_not_triangulate_faces(do_not_triangulate)); } } else @@ -387,6 +390,7 @@ public Q_SLOTS: int edge_sizing_type = ui.edgeSizing_type_combo_box->currentIndex(); bool edges_only = ui.splitEdgesOnly_checkbox->isChecked(); + bool do_not_triangulate = ui.doNotTriangulate_checkbox->isChecked(); bool preserve_duplicates = ui.preserveDuplicates_checkbox->isChecked(); double target_length = ui.edgeLength_dspinbox->value(); double error_tol = ui.errorTol_edit->value(); @@ -429,7 +433,8 @@ public Q_SLOTS: if (edges_only) { do_split_edges(edge_sizing_type, selection_item, pmesh, - target_length, error_tol, min_length, max_length, curv_ball_r); + target_length, error_tol, min_length, max_length, curv_ball_r, + do_not_triangulate); } else //not edges_only { @@ -465,7 +470,8 @@ public Q_SLOTS: else { do_split_edges(edge_sizing_type, selection_item, pmesh, - target_length, error_tol, min_length, max_length, curv_ball_r); + target_length, error_tol, min_length, max_length, curv_ball_r, + do_not_triangulate); } } @@ -675,7 +681,8 @@ public Q_SLOTS: , pmesh , CGAL::parameters::geom_traits(EPICK()) . edge_is_constrained_map(eif) - . face_patch_map(fpmap)); + . face_patch_map(fpmap) + . do_not_triangulate_faces(do_not_triangulate)); } else if (edge_sizing_type == 1) { @@ -691,6 +698,7 @@ public Q_SLOTS: , pmesh , CGAL::parameters::geom_traits(EPICK()) . edge_is_constrained_map(eif) + . do_not_triangulate_faces(do_not_triangulate) . face_patch_map(fpmap)); } } @@ -703,6 +711,7 @@ public Q_SLOTS: , target_length , pmesh , CGAL::parameters::geom_traits(EPICK()) + . do_not_triangulate_faces(do_not_triangulate) . edge_is_constrained_map(eif)); } else if (edge_sizing_type == 1) @@ -862,6 +871,7 @@ public Q_SLOTS: { // Remeshing parameters bool edges_only = false, preserve_duplicates = false; + bool do_not_triangulate = false; int edge_sizing_type = 0; double target_length = 0.; double error_tol = 0.; @@ -901,6 +911,7 @@ public Q_SLOTS: } edges_only = ui.splitEdgesOnly_checkbox->isChecked(); + do_not_triangulate = ui.doNotTriangulate_checkbox->isChecked(); preserve_duplicates = ui.preserveDuplicates_checkbox->isChecked(); edge_sizing_type = ui.edgeSizing_type_combo_box->currentIndex(); target_length = ui.edgeLength_dspinbox->value(); @@ -963,6 +974,7 @@ public Q_SLOTS: tbb::blocked_range(0, selection.size()), Remesh_polyhedron_item_for_parallel_for( selection, edges_to_protect, edges_only + , do_not_triangulate , edge_sizing_type, target_length, error_tol , min_length , max_length, nb_iter , protect, smooth_features) @@ -1012,6 +1024,7 @@ public Q_SLOTS: int edge_sizing_type_; bool edges_only_; + bool do_not_triangulate_; double target_length_; double error_tol_; double min_length_; @@ -1043,7 +1056,8 @@ public Q_SLOTS: CGAL::Polygon_mesh_processing::split_long_edges( border_edges , target_length_ - , *poly_item->polyhedron()); + , *poly_item->polyhedron() + , CGAL::parameters::do_not_triangulate_faces(do_not_triangulate_)); } else if (edge_sizing_type_ == 1) { @@ -1056,7 +1070,8 @@ public Q_SLOTS: CGAL::Polygon_mesh_processing::split_long_edges( border_edges , target_length_ - , *poly_item->polyhedron()); + , *poly_item->polyhedron() + , CGAL::parameters::do_not_triangulate_faces(do_not_triangulate_)); } } else @@ -1102,6 +1117,7 @@ public Q_SLOTS: public: Remesh_polyhedron_item( const bool edges_only, + const bool do_not_triangulate, const int edge_sizing_type, const double target_length, const double error_tol, @@ -1112,6 +1128,7 @@ public Q_SLOTS: const bool smooth_features) : edge_sizing_type_(edge_sizing_type) , edges_only_(edges_only) + , do_not_triangulate_(do_not_triangulate) , target_length_(target_length) , error_tol_(error_tol) , min_length_(min_length) @@ -1124,6 +1141,7 @@ public Q_SLOTS: Remesh_polyhedron_item(const Remesh_polyhedron_item& remesh) : edge_sizing_type_(remesh.edge_sizing_type_) , edges_only_(remesh.edges_only_) + , do_not_triangulate_(remesh.do_not_triangulate_) , target_length_(remesh.target_length_) , error_tol_(remesh.error_tol_) , min_length_(remesh.min_length_) @@ -1154,6 +1172,7 @@ public Q_SLOTS: const std::vector& selection, std::map& edges_to_protect, const bool edges_only, + const bool do_not_triangulate, const int edge_sizing_type, const double target_length, const double error_tol, @@ -1164,7 +1183,8 @@ public Q_SLOTS: const bool smooth_features) : RemeshFunctor(edges_only, edge_sizing_type, target_length , error_tol, min_length, max_length - , nb_iter, protect, smooth_features) + , nb_iter, protect, smooth_features + , do_not_triangulate) , selection_(selection), edges_to_protect_(edges_to_protect) {} @@ -1205,6 +1225,8 @@ public Q_SLOTS: { if(ui.splitEdgesOnly_checkbox->isChecked()) { + ui.doNotTriangulate_checkbox->setEnabled(true); + ui.nbIterations_label->setEnabled(false); ui.nbIterations_spinbox->setEnabled(false); ui.nbSmoothing_label->setEnabled(false); @@ -1220,6 +1242,8 @@ public Q_SLOTS: } else { + ui.doNotTriangulate_checkbox->setEnabled(false); + ui.nbIterations_label->setEnabled(true); ui.nbIterations_spinbox->setEnabled(true); ui.nbSmoothing_label->setEnabled(true); diff --git a/PMP_Remeshing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h b/PMP_Remeshing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h index ae2c5712bfd9..555321850d98 100644 --- a/PMP_Remeshing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h +++ b/PMP_Remeshing/include/CGAL/Polygon_mesh_processing/internal/Isotropic_remeshing/remesh_impl.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -254,6 +255,47 @@ namespace internal { return true; } + template + struct Triangulate_face_visitor + : public CGAL::Polygon_mesh_processing::Triangulate_faces::Default_visitor + { + using face_descriptor = typename boost::graph_traits::face_descriptor; + using halfedge_descriptor = typename boost::graph_traits::halfedge_descriptor; + + const Mesh& m_mesh; + std::unordered_set& m_created_halfedges; + std::unordered_set& m_created_faces; + + std::unordered_set m_face_halfedges; + + Triangulate_face_visitor(const Mesh& mesh, + std::unordered_set& created_faces, + std::unordered_set& created_halfedges) + : m_mesh(mesh) + , m_created_faces(created_faces) + , m_created_halfedges(created_halfedges) + {} + + void before_subface_creations(face_descriptor f) + { + for(halfedge_descriptor h : halfedges_around_face(halfedge(f, m_mesh), m_mesh)) + m_face_halfedges.insert(h); + } + void after_subface_creations() + { + for(face_descriptor f : m_created_faces) + { + for(halfedge_descriptor h : halfedges_around_face(halfedge(f, m_mesh), m_mesh)) + if(m_face_halfedges.count(h) == 0) + m_created_halfedges.insert(h); + } + } + void after_subface_created(face_descriptor f) + { + m_created_faces.insert(f); + } + }; + template void split_long_edges(const EdgeRange& edge_range, - SizingFunction& sizing) + SizingFunction& sizing, + const bool do_not_triangulate_faces) { #ifdef CGAL_PMP_REMESHING_VERBOSE @@ -417,7 +460,7 @@ namespace internal { long_edges.erase(eit); //split edge - Point refinement_point = this->midpoint(he); + const Point refinement_point = sizing.split_placement(he, mesh_); halfedge_descriptor hnew = CGAL::Euler::split_edge(he, mesh_); // propagate the constrained status put(ecmap_, edge(hnew, mesh_), get(ecmap_, edge(he, mesh_))); @@ -447,26 +490,29 @@ namespace internal { long_edges.emplace(hnext, sqlen_new.value()); //insert new edges to keep triangular faces, and update long_edges - if (!is_border(hnew, mesh_)) + if(!do_not_triangulate_faces) { - Patch_id patch_id = get_patch_id(face(hnew, mesh_)); - halfedge_descriptor hnew2 = - CGAL::Euler::split_face(hnew, next(next(hnew, mesh_), mesh_), mesh_); - put(ecmap_, edge(hnew2, mesh_), false); - set_patch_id(face(hnew2, mesh_), patch_id); - set_patch_id(face(opposite(hnew2, mesh_), mesh_), patch_id); - } + if (!is_border(hnew, mesh_)) + { + Patch_id patch_id = get_patch_id(face(hnew, mesh_)); + halfedge_descriptor hnew2 = + CGAL::Euler::split_face(hnew, next(next(hnew, mesh_), mesh_), mesh_); + put(ecmap_, edge(hnew2, mesh_), false); + set_patch_id(face(hnew2, mesh_), patch_id); + set_patch_id(face(opposite(hnew2, mesh_), mesh_), patch_id); + } - //do it again on the other side if we're not on boundary - halfedge_descriptor hnew_opp = opposite(hnew, mesh_); - if (!is_border(hnew_opp, mesh_)) - { - Patch_id patch_id = get_patch_id(face(hnew_opp, mesh_)); - halfedge_descriptor hnew2 = - CGAL::Euler::split_face(prev(hnew_opp, mesh_), next(hnew_opp, mesh_), mesh_); - put(ecmap_, edge(hnew2, mesh_), false); - set_patch_id(face(hnew2, mesh_), patch_id); - set_patch_id(face(opposite(hnew2, mesh_), mesh_), patch_id); + //do it again on the other side if we're not on boundary + halfedge_descriptor hnew_opp = opposite(hnew, mesh_); + if (!is_border(hnew_opp, mesh_)) + { + Patch_id patch_id = get_patch_id(face(hnew_opp, mesh_)); + halfedge_descriptor hnew2 = + CGAL::Euler::split_face(prev(hnew_opp, mesh_), next(hnew_opp, mesh_), mesh_); + put(ecmap_, edge(hnew2, mesh_), false); + set_patch_id(face(hnew2, mesh_), patch_id); + set_patch_id(face(opposite(hnew2, mesh_), mesh_), patch_id); + } } } #ifdef CGAL_PMP_REMESHING_VERBOSE @@ -505,107 +551,107 @@ namespace internal { long_edges.emplace(halfedge(e, mesh_), sqlen.value()); } + auto emplace_if_too_long = [&](halfedge_descriptor h) { + std::optional sqlen_new = sizing.is_too_long(source(h, mesh_), target(h, mesh_), mesh_); + if(sqlen_new != std::nullopt) + long_edges.emplace(h, sqlen_new.value()); + }; + //split long edges #ifdef CGAL_PMP_REMESHING_VERBOSE unsigned int nb_splits = 0; #endif + while (!long_edges.empty()) { - //the edge with longest length - auto eit = long_edges.begin(); - halfedge_descriptor he = eit->first; - long_edges.erase(eit); + // split existing long edges until there is none left, + // as splitting an edge can leave remaining "half-length" long edges + while(!long_edges.empty()) + { + //the edge with longest length + auto eit = long_edges.begin(); + halfedge_descriptor he = eit->first; + long_edges.erase(eit); #ifdef CGAL_PMP_REMESHING_VERBOSE_PROGRESS - std::cout << "\r\t(" << long_edges.size() << " long edges, "; - std::cout << nb_splits << " splits)"; - std::cout.flush(); + std::cout << "\r\t(" << long_edges.size() << " long edges, "; + std::cout << nb_splits << " splits)"; + std::cout.flush(); #endif - if (protect_constraints_ && !is_longest_on_faces(edge(he, mesh_))) - continue; - - //collect patch_ids - Patch_id patch_id = get_patch_id(face(he, mesh_)); - Patch_id patch_id_opp = get_patch_id(face(opposite(he, mesh_), mesh_)); + if (protect_constraints_ && !is_longest_on_faces(edge(he, mesh_))) + continue; - //split edge - Point refinement_point = sizing.split_placement(he, mesh_); - halfedge_descriptor hnew = CGAL::Euler::split_edge(he, mesh_); - CGAL_assertion(he == next(hnew, mesh_)); - put(ecmap_, edge(hnew, mesh_), get(ecmap_, edge(he, mesh_)) ); + //split edge + Point refinement_point = sizing.split_placement(he, mesh_); + halfedge_descriptor hnew = CGAL::Euler::split_edge(he, mesh_); + CGAL_assertion(he == next(hnew, mesh_)); + put(ecmap_, edge(hnew, mesh_), get(ecmap_, edge(he, mesh_)) ); #ifdef CGAL_PMP_REMESHING_VERBOSE - ++nb_splits; + ++nb_splits; #endif - //move refinement point - vertex_descriptor vnew = target(hnew, mesh_); - put(vpmap_, vnew, refinement_point); + //move refinement point + vertex_descriptor vnew = target(hnew, mesh_); + put(vpmap_, vnew, refinement_point); #ifdef CGAL_PMP_REMESHING_VERY_VERBOSE - std::cout << " Refinement point : " << refinement_point << std::endl; + std::cout << " Refinement point : " << refinement_point << std::endl; #endif - //after splitting - halfedge_descriptor hnew_opp = opposite(hnew, mesh_); - halfedge_added(hnew, status(he)); - halfedge_added(hnew_opp, status(opposite(he, mesh_))); + //after split + halfedge_descriptor hnew_opp = opposite(hnew, mesh_); + halfedge_added(hnew, status(he)); + halfedge_added(hnew_opp, status(opposite(he, mesh_))); - //update sizing field with the new point - sizing.register_split_vertex(vnew, mesh_); + //update sizing field with the new point + sizing.register_split_vertex(vnew, mesh_); - //check sub-edges - //if it was more than twice the "long" threshold, insert them - std::optional sqlen_new = sizing.is_too_long(source(hnew, mesh_), target(hnew, mesh_), mesh_); - if(sqlen_new != std::nullopt) - long_edges.emplace(hnew, sqlen_new.value()); + //check sub-edges + const halfedge_descriptor hnext = next(hnew, mesh_); + emplace_if_too_long(hnew); + emplace_if_too_long(hnext); - const halfedge_descriptor hnext = next(hnew, mesh_); - sqlen_new = sizing.is_too_long(source(hnext, mesh_), target(hnext, mesh_), mesh_); - if (sqlen_new != std::nullopt) - long_edges.emplace(hnext, sqlen_new.value()); + } // end while long_edges - //insert new edges to keep triangular faces, and update long_edges - if (!is_on_border(hnew)) + // triangulate faces that have a long edge on their border, and update long_edges + std::unordered_set to_triangulate; + for(halfedge_descriptor he : halfedges(mesh_)) { - halfedge_descriptor hnew2 = CGAL::Euler::split_face(hnew, - next(next(hnew, mesh_), mesh_), - mesh_); - put(ecmap_, edge(hnew2, mesh_), false); - Halfedge_status snew = (is_on_patch(hnew) || is_on_patch_border(hnew)) - ? PATCH - : MESH; - halfedge_added(hnew2, snew); - halfedge_added(opposite(hnew2, mesh_), snew); - set_patch_id(face(hnew2, mesh_), patch_id); - set_patch_id(face(opposite(hnew2, mesh_), mesh_), patch_id); - - if (snew == PATCH) - { - std::optional sql = sizing.is_too_long(source(hnew2, mesh_), target(hnew2, mesh_), mesh_); - if(sql != std::nullopt) - long_edges.emplace(hnew2, sql.value()); - } + if(is_on_border(he) || is_triangle(he, mesh_)) + continue; + if(is_on_patch(he) || is_on_patch_border(he)) + to_triangulate.insert(he); } - //do it again on the other side if we're not on boundary - if (!is_on_border(hnew_opp)) + while(!to_triangulate.empty()) { - halfedge_descriptor hnew2 = CGAL::Euler::split_face(prev(hnew_opp, mesh_), - next(hnew_opp, mesh_), - mesh_); - put(ecmap_, edge(hnew2, mesh_), false); - Halfedge_status snew = (is_on_patch(hnew_opp) || is_on_patch_border(hnew_opp)) - ? PATCH - : MESH; - halfedge_added(hnew2, snew); - halfedge_added(opposite(hnew2, mesh_), snew); - set_patch_id(face(hnew2, mesh_), patch_id_opp); - set_patch_id(face(opposite(hnew2, mesh_), mesh_), patch_id_opp); - - if (snew == PATCH) + halfedge_descriptor he = *to_triangulate.begin(); + to_triangulate.erase(he); + + const Patch_id patch_id = get_patch_id(face(he, mesh_)); + + std::unordered_set created_faces; + std::unordered_set created_halfedges; + Triangulate_face_visitor visitor(mesh_, created_faces, created_halfedges); + CGAL::Polygon_mesh_processing::triangulate_face(face(he, mesh_), + mesh_, + parameters::visitor(visitor) + .geom_traits(gt_) + .vertex_point_map(vpmap_)); + + for(auto f : created_faces) + set_patch_id(f, patch_id); + + for(auto hnew2 : created_halfedges) { - std::optional sql = sizing.is_too_long(source(hnew2, mesh_), target(hnew2, mesh_), mesh_); - if (sql != std::nullopt) - long_edges.emplace(hnew2, sql.value()); + put(ecmap_, edge(hnew2, mesh_), false); + + // update status : it must be PATCH because we only split faces that have a long edge + // on their border, and border edges are PATCH or PATCH_BORDER + halfedge_added(hnew2, PATCH); + halfedge_added(opposite(hnew2, mesh_), PATCH); + + // put new edges in long_edges if needed + emplace_if_too_long(hnew2); } } } diff --git a/PMP_Remeshing/include/CGAL/Polygon_mesh_processing/remesh.h b/PMP_Remeshing/include/CGAL/Polygon_mesh_processing/remesh.h index 59f9fd4c4553..ffd2630faee3 100644 --- a/PMP_Remeshing/include/CGAL/Polygon_mesh_processing/remesh.h +++ b/PMP_Remeshing/include/CGAL/Polygon_mesh_processing/remesh.h @@ -441,6 +441,12 @@ void isotropic_remeshing(const FaceRange& faces * \cgalParamDefault{a default property map where no edge is constrained} * \cgalParamExtra{A constrained edge can be split or collapsed, but not flipped, nor its endpoints moved by smoothing.} * \cgalParamNEnd +* +* \cgalParamNBegin{do_not_triangulate_faces} +* \cgalParamDescription{If `true`, the faces created by splitting are not triangulated.} +* \cgalParamType{`bool`} +* \cgalParamDefault{`false`} +* \cgalParamNEnd * \cgalNamedParamsEnd * * @sa `isotropic_remeshing()` @@ -492,6 +498,9 @@ void split_long_edges(const EdgeRange& edges get_parameter(np, internal_np::face_patch), internal::Connected_components_pmap(faces(pmesh), pmesh, ecmap, fimap, false)); + const bool do_not_triangulate + = choose_parameter(get_parameter(np, internal_np::do_not_triangulate_faces), false); + typename internal::Incremental_remesher, // no constraint pmap FPMap,FIMap @@ -502,7 +511,7 @@ void split_long_edges(const EdgeRange& edges fimap, false/*need aabb_tree*/); - remesher.split_long_edges(edges, sizing); + remesher.split_long_edges(edges, sizing, do_not_triangulate); } // Convenience overload when using max_length for sizing