From 61e71d41b88f20f3774e6eddf40fe43b941bf091 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Loriot?= Date: Tue, 11 Feb 2025 19:19:21 +0100 Subject: [PATCH] WIP make it manifold --- .../Polygon_mesh_processing/acvd_example.cpp | 1 + .../CGAL/Polygon_mesh_processing/acvd/acvd.h | 197 ++++++++++++++++-- 2 files changed, 176 insertions(+), 22 deletions(-) diff --git a/Polygon_mesh_processing/examples/Polygon_mesh_processing/acvd_example.cpp b/Polygon_mesh_processing/examples/Polygon_mesh_processing/acvd_example.cpp index df7b18058eb..7e1325d2538 100644 --- a/Polygon_mesh_processing/examples/Polygon_mesh_processing/acvd_example.cpp +++ b/Polygon_mesh_processing/examples/Polygon_mesh_processing/acvd_example.cpp @@ -26,6 +26,7 @@ int main(int argc, char* argv[]) //CGAL::get_default_random() = CGAL::Random( 1739197120 ); //connexity constraint issue + infinite loop with cheese //CGAL::get_default_random() = CGAL::Random( 1739199762 ); //one small edge //CGAL::get_default_random() = CGAL::Random( 1739264620 ); //one very small edge + CGAL::get_default_random() = CGAL::Random( 1739293586 ); // seed elephant non-manifold with 300 clusters std::cout << "Seed : " << CGAL::get_default_random().get_seed() << std::endl; const std::string filename = (argc > 1) ? diff --git a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/acvd/acvd.h b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/acvd/acvd.h index 25ab906ed2d..b07d4f1f1a6 100644 --- a/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/acvd/acvd.h +++ b/Polygon_mesh_processing/include/CGAL/Polygon_mesh_processing/acvd/acvd.h @@ -35,6 +35,9 @@ #include #include +#include +#include + #include #include #include @@ -61,7 +64,6 @@ void dump_mesh_with_cluster_colors(TriangleMesh pmesh, ClusterMap cluster_map, s CGAL::IO::orange(), CGAL::IO::deep_blue(), CGAL::IO::yellow(), - CGAL::IO::black(), CGAL::IO::violet(), CGAL::IO::gray(), CGAL::IO::white() }}; @@ -368,10 +370,10 @@ template std::pair< std::vector::type::Point_3>, - std::vector> + std::vector> > acvd_impl( TriangleMesh& pmesh, - const int nb_clusters, + int nb_clusters, const NamedParameters& np = parameters::default_values() ) { @@ -610,19 +612,26 @@ std::pair< int nb_loops = 0; QEMClusterData cluster1_v1_to_c2, cluster2_v1_to_c2, cluster1_v2_to_c1, cluster2_v2_to_c1; - + std::vector frozen_clusters(nb_clusters, false); + do + { do { // reset cluster data - for ( auto &cluster : clusters) cluster = QEMClusterData(); + for (int ci=0; ci(); for ( Vertex_descriptor v : vertices( pmesh ) ) { typename GT::FT v_weight = get(vertex_weight_pmap, v); Matrix4x4 v_qem = get (vertex_quadric_pmap, v); int cluster_id = get(vertex_cluster_pmap, v ); - if ( cluster_id != -1 ) + if ( cluster_id != -1 && !frozen_clusters[cluster_id]) clusters[ cluster_id ].add_vertex( get(vpm, v), v_weight, v_qem); } + for (int ci=0; ci 0 || nb_loops < 3 ); /// Construct new Mesh - std::vector valid_cluster_map(nb_clusters, -1); + std::vector valid_cluster_map(nb_clusters, -1); std::vector points; - std::vector> polygons; + std::vector> polygons; TriangleMesh simplified_mesh; for (int i = 0; i < nb_clusters; ++i) { - if (clusters[i].weight_sum > 0) + // if (clusters[i].weight_sum > 0) { valid_cluster_map[i] = points.size(); points.push_back(clusters[i].representative_point(qem_energy_minimization)); @@ -991,17 +1009,16 @@ std::pair< points.push_back(vb_p); - int cb = points.size() - 1; + std::size_t cb = points.size() - 1; if (cb_first == -1) cb_first = cb; - int ct_mapped = valid_cluster_map[ct], cs_mapped = valid_cluster_map[cs]; + std::size_t ct_mapped = valid_cluster_map[ct], cs_mapped = valid_cluster_map[cs]; - if (ct_mapped != -1 && cs_mapped != -1) + if (ct_mapped != std::size_t(-1) && cs_mapped != std::size_t(-1)) { - std::vector - polygon = {ct_mapped, cb, cs_mapped}; + std::array polygon = {ct_mapped, cb, cs_mapped}; polygons.push_back(polygon); // after the loop, the last cb+1 should be modified to the first cb @@ -1030,21 +1047,157 @@ std::pair< if (c1 != c2 && c1 != c3 && c2 != c3) { - int c1_mapped = valid_cluster_map[c1], c2_mapped = valid_cluster_map[c2], c3_mapped = valid_cluster_map[c3]; - if (c1_mapped != -1 && c2_mapped != -1 && c3_mapped != -1) + std::size_t c1_mapped = valid_cluster_map[c1], c2_mapped = valid_cluster_map[c2], c3_mapped = valid_cluster_map[c3]; + if (c1_mapped != std::size_t(-1) && c2_mapped != std::size_t(-1) && c3_mapped != std::size_t(-1)) { - std::vector polygon = {c1_mapped, c2_mapped, c3_mapped}; + std::array polygon = {c1_mapped, c2_mapped, c3_mapped}; polygons.push_back(polygon); } } } - //dump_mesh_with_cluster_colors(pmesh, vertex_cluster_pmap, "/tmp/debug.ply"); - //CGAL::IO::write_polygon_soup("/tmp/debug.off", points, polygons); +static int kkk=-1; +CGAL::IO::write_polygon_soup("/tmp/soup_"+std::to_string(++kkk)+".off", points, polygons); +dump_mesh_with_cluster_colors(pmesh, vertex_cluster_pmap, "/tmp/cluster_"+std::to_string(kkk)+".ply"); - orient_polygon_soup(points, polygons); + std::vector > edge_map(points.size()); + for (const std::array & p : polygons) + { + for (int i=0; i<3; ++i) + { + std::pair edge = make_sorted_pair(p[i], p[(i+1)%3]); + edge_map[edge.first].emplace(edge.second,0).first->second+=1; + } + } - return std::make_pair(points, polygons); + std::vector nm_clusters; + for (std::size_t i=0; i2) + { + std::cout << "non-manifold edge : " << i << " " << j << "\n"; + nm_clusters.push_back(i); + nm_clusters.push_back(j); + } + } + + + // detect isolated graph edges + for (Edge_descriptor ed : edges(pmesh)) + { + int c1 = get(vertex_cluster_pmap, source(ed, pmesh)); + int c2 = get(vertex_cluster_pmap, target(ed, pmesh)); + if (c1==-1 || c2 ==-1) throw std::runtime_error("non assigned vertex"); + + if (c1==c2) continue; + if (c2 > > link_edges(points.size()); + for (const std::array & p : polygons) + { + for (int i=0; i<3; ++i) + //TODO: skip if in nm_clusters + link_edges[p[i]].emplace_back(p[(i+1)%3], p[(i+2)%3]); + } + + using Graph = boost::adjacency_list ; + for (std::size_t i=0; i< points.size(); ++i) + { + if (std::binary_search(nm_clusters.begin(), nm_clusters.end(), i)) continue; + std::vector descriptors(points.size(), Graph::null_vertex()); + + + Graph graph; + for (const auto& p : link_edges[i]) + { + if (descriptors[p.first] == Graph::null_vertex()) descriptors[p.first] = add_vertex(graph); + if (descriptors[p.second] == Graph::null_vertex()) descriptors[p.second] = add_vertex(graph); + add_edge(descriptors[p.first], descriptors[p.second], graph); + } + + std::map the_map; + + if (boost::connected_components(graph, boost::make_assoc_property_map(the_map)) > 1) + { + std::cout << "non-manifold vertex " << i << "\n"; + nm_clusters.push_back(i); + } + } + + if (nm_clusters.empty()) + { + //dump_mesh_with_cluster_colors(pmesh, vertex_cluster_pmap, "/tmp/debug.ply"); + //CGAL::IO::write_polygon_soup("/tmp/soup.off", points, polygons); + + // orient_polygon_soup(points, polygons); + + return std::make_pair(points, polygons); + } + + + std::vector one_ring; + for (std::size_t nmi : nm_clusters) + { + for ( auto [n, s] : edge_map[nmi] ) + one_ring.push_back(n); + } + //~ nm_clusters.insert(nm_clusters.end(), one_ring.begin(), one_ring.end()); + //~ std::sort(nm_clusters.begin(), nm_clusters.end()); + //~ nm_clusters.erase(std::unique(nm_clusters.begin(), nm_clusters.end()), nm_clusters.end()); + + + std::set nm_clusters_picked; // TODO: use cluster data instead? + + for (Vertex_descriptor v : vertices(pmesh)) + { + int c = get(vertex_cluster_pmap, v); + if (std::binary_search(nm_clusters.begin(), nm_clusters.end(), c)) + { + if (!nm_clusters_picked.insert(c).second) continue; + + if (clusters[c].nb_vertices==1) continue; + + + std::cout << "putting " << v << " from " << c << " to " << clusters.size() << "(" << clusters[c].nb_vertices << ")" << "\n"; + put(vertex_cluster_pmap, v, clusters.size()); + if (get(vertex_cluster_pmap, v) != clusters.size()) throw std::runtime_error("BOOM"); + clusters.emplace_back(); + } + + if (nm_clusters_picked.size()==nm_clusters.size()) break; + } + + //~ if (kkk==0) + frozen_clusters = std::vector(nb_clusters, true); + for (std::size_t nmi : nm_clusters) + frozen_clusters[nmi]=false; + for (std::size_t nmi : one_ring) + frozen_clusters[nmi]=false; + + nb_clusters=clusters.size(); + frozen_clusters.resize(nb_clusters, false); + nb_loops=0; + qem_energy_minimization=false; + + } while(true); }