CGAL łączący 2 geometrie

11

Obecnie próbuję dołączyć do różnych części siatki, które nie są ze sobą połączone. Z przykładu to znalazłem (blobby_3cc.off).

Za pomocą keep_large_connected_componentsi keep_largest_connected_componentsusuwam wszystkie mniejsze elementy. Co utrzymuje te 3 poniżej.

Nie mogę znaleźć w dokumentacji sposobu na połączenie ich i uzupełnienie brakujących części. Jednym z rozwiązań jest utworzenie 1 trójkąta i wypełnienie otworów (od tego czasu jest to 1 obiekt z ogromnymi otworami). Ale nie mogę znaleźć sposobu, aby połączyć je razem.

Czy ktoś ma na to rozwiązanie?

Używam CGAL dla C ++.

wprowadź opis zdjęcia tutaj

Niels
źródło

Odpowiedzi:

3

Kiedy zaczynałem od CGAL, prawie natychmiast napotkałem ten problem. Po dokładnym przeczytaniu dokumentacji siatki wielokątów udało mi się znaleźć rozwiązanie . Zasadniczo poprzez zmodyfikowaną wersję Corefinement , możesz płynnie ze sobą łączyć dwie oddzielne geometrie, bez względu na ich liczbę i kształt poli (jednak im większa różnica wielokątów, tym mniej będzie skuteczna).

Musisz najpierw upewnić się, że geometria się nie przecina. Po drugie, upewnij się, że CGAL::Polygon_mesh_processing::clip()jest aktywny na dwóch geometriach (sugeruję użycie close_volumes=false). Następnie obliczyć połączenie dwóch nowych siatek:

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Polygon_mesh_processing/corefinement.h>
#include <fstream>
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Surface_mesh<K::Point_3>             Mesh;
namespace PMP = CGAL::Polygon_mesh_processing;
int main(int argc, char* argv[])
{
  const char* filename1 = (argc > 1) ? argv[1] : "data/blobby.off";
  const char* filename2 = (argc > 2) ? argv[2] : "data/eight.off";
  std::ifstream input(filename1);
  Mesh mesh1, mesh2;
  if (!input || !(input >> mesh1))
  {
    std::cerr << "First mesh is not a valid off file." << std::endl;
    return 1;
  }
  input.close();
  input.open(filename2);
  if (!input || !(input >> mesh2))
  {
    std::cerr << "Second mesh is not a valid off file." << std::endl;
    return 1;
  }
  Mesh out;
  bool valid_union = PMP::corefine_and_compute_union(mesh1,mesh2, out);
  if (valid_union)
  {
    std::cout << "Union was successfully computed\n";
    std::ofstream output("union.off");
    output << out;
    return 0;
  }
  std::cout << "Union could not be computed\n";
  return 1;
}

Zamiast używać siatki z punktem z jądra o dokładnej konstrukcji, dokładne punkty są właściwością wierzchołków siatki, które możemy ponownie wykorzystać w późniejszych operacjach. Dzięki tej właściwości możemy manipulować siatką za pomocą punktów o współrzędnych zmiennoprzecinkowych, ale czerpać korzyści z solidności zapewnianej przez dokładne konstrukcje .:

#include <CGAL/Exact_predicates_inexact_constructions_kernel.h>
#include <CGAL/Exact_predicates_exact_constructions_kernel.h>
#include <CGAL/Surface_mesh.h>
#include <CGAL/Polygon_mesh_processing/corefinement.h>
#include <fstream>
typedef CGAL::Exact_predicates_inexact_constructions_kernel K;
typedef CGAL::Exact_predicates_exact_constructions_kernel EK;
typedef CGAL::Surface_mesh<K::Point_3> Mesh;
typedef boost::graph_traits<Mesh>::vertex_descriptor vertex_descriptor;
typedef Mesh::Property_map<vertex_descriptor,EK::Point_3> Exact_point_map;
typedef Mesh::Property_map<vertex_descriptor,bool> Exact_point_computed;
namespace PMP = CGAL::Polygon_mesh_processing;
namespace params = PMP::parameters;
struct Coref_point_map
{
  // typedef for the property map
  typedef boost::property_traits<Exact_point_map>::value_type value_type;
  typedef boost::property_traits<Exact_point_map>::reference reference;
  typedef boost::property_traits<Exact_point_map>::category category;
  typedef boost::property_traits<Exact_point_map>::key_type key_type;
  // exterior references
  Exact_point_computed* exact_point_computed_ptr;
  Exact_point_map* exact_point_ptr;
  Mesh* mesh_ptr;
  Exact_point_computed& exact_point_computed() const
  {
    CGAL_assertion(exact_point_computed_ptr!=NULL);
    return *exact_point_computed_ptr;
  }
  Exact_point_map& exact_point() const
  {
    CGAL_assertion(exact_point_ptr!=NULL);
    return *exact_point_ptr;
  }
  Mesh& mesh() const
  {
    CGAL_assertion(mesh_ptr!=NULL);
    return *mesh_ptr;
  }
  // Converters
  CGAL::Cartesian_converter<K, EK> to_exact;
  CGAL::Cartesian_converter<EK, K> to_input;
  Coref_point_map()
    : exact_point_computed_ptr(NULL)
    , exact_point_ptr(NULL)
    , mesh_ptr(NULL)
  {}
  Coref_point_map(Exact_point_map& ep,
                  Exact_point_computed& epc,
                  Mesh& m)
    : exact_point_computed_ptr(&epc)
    , exact_point_ptr(&ep)
    , mesh_ptr(&m)
  {}
  friend
  reference get(const Coref_point_map& map, key_type k)
  {
    // create exact point if it does not exist
    if (!map.exact_point_computed()[k]){
      map.exact_point()[k]=map.to_exact(map.mesh().point(k));
      map.exact_point_computed()[k]=true;
    }
    return map.exact_point()[k];
  }
  friend
  void put(const Coref_point_map& map, key_type k, const EK::Point_3& p)
  {
    map.exact_point_computed()[k]=true;
    map.exact_point()[k]=p;
    // create the input point from the exact one
    map.mesh().point(k)=map.to_input(p);
  }
};
int main(int argc, char* argv[])
{
  const char* filename1 = (argc > 1) ? argv[1] : "data/blobby.off";
  const char* filename2 = (argc > 2) ? argv[2] : "data/eight.off";
  std::ifstream input(filename1);
  Mesh mesh1, mesh2;
  if (!input || !(input >> mesh1))
  {
    std::cerr << "First mesh is not a valid off file." << std::endl;
    return 1;
  }
  input.close();
  input.open(filename2);
  if (!input || !(input >> mesh2))
  {
    std::cerr << "Second mesh is not a valid off file." << std::endl;
    return 1;
  }
  Exact_point_map mesh1_exact_points =
    mesh1.add_property_map<vertex_descriptor,EK::Point_3>("e:exact_point").first;
  Exact_point_computed mesh1_exact_points_computed =
    mesh1.add_property_map<vertex_descriptor,bool>("e:exact_points_computed").first;
  Exact_point_map mesh2_exact_points =
    mesh2.add_property_map<vertex_descriptor,EK::Point_3>("e:exact_point").first;
  Exact_point_computed mesh2_exact_points_computed =
    mesh2.add_property_map<vertex_descriptor,bool>("e:exact_points_computed").first;
  Coref_point_map mesh1_pm(mesh1_exact_points, mesh1_exact_points_computed, mesh1);
  Coref_point_map mesh2_pm(mesh2_exact_points, mesh2_exact_points_computed, mesh2);
  if ( PMP::corefine_and_compute_intersection(mesh1,
                                              mesh2,
                                              mesh1,
                                              params::vertex_point_map(mesh1_pm),
                                              params::vertex_point_map(mesh2_pm),
                                              params::vertex_point_map(mesh1_pm) ) )
  {
    if ( PMP::corefine_and_compute_union(mesh1,
                                         mesh2,
                                         mesh2,
                                         params::vertex_point_map(mesh1_pm),
                                         params::vertex_point_map(mesh2_pm),
                                         params::vertex_point_map(mesh2_pm) ) )
    {
      std::cout << "Intersection and union were successfully computed\n";
      std::ofstream output("inter_union.off");
      output << mesh2;
      return 0;
    }
    std::cout << "Union could not be computed\n";
    return 1;
  }
  std::cout << "Intersection could not be computed\n";
  return 1;
}
Death Waltz
źródło
Aby wypełnić wszelkie dziury, zobacz Naprawa kombinatoryczna i wypełnianie dziur
Death Waltz
Dziękuję za odpowiedź. Staram się zrozumieć swój kod, ale niektóre funkcje nie wydaje się zrozumieć corefine_and_compute_union, corefine_and_compute_intersection. Nie rozumiem w dokumentach. Czy możesz coś wyjaśnić?
Niels
Zasadniczo corefine_and_compute_unionoblicza segmenty siatki, które zachodzą na siebie, i należy je usunąć i zastąpić wypełnieniem wielokąta. corefine_and_compute_intersectionjest zbliżony do tej samej rzeczy, ale wykorzystuje istniejącą siatkę do wypełnienia cięcia zamiast generowania gładkiego wypełnienia siatki. Pierwsza funkcja zwykle wymaga dokładnych danych wejściowych do działania, ale druga pozwala jej przekazać się jako parametr.
Death Waltz
Muszę to sprawdzić w ten weekend i zobaczyć wynik, więc wiem, jak to działa. Zaakceptuję tę odpowiedź jako prawidłową, zanim skończy się nagroda.
Niels
W porządku, jeśli to nie zadziała, daj mi znać
Death Waltz
0

Jak oryginalnie wygląda siatka? czy wykonalne byłoby scalenie różnych komponentów zamiast usunięcie najmniejszych części? Patrz kombinatoryczna naprawa CGAL więcej informacji, .

Łączenie różnych komponentów jest dość trudnym problemem. uważam, że zwykłe algorytmy wypełniania dziur działają tylko na ograniczonych otworach, tj. istnieje otwarta krawędź, która przechodzi wokół dziury i kończy na początku.

Moim zaleceniem byłoby przeanalizowanie siatki w celu znalezienia otwartych list krawędzi, które należy połączyć, tj. Linii czerwonej, zielonej, niebieskiej i fioletowej. Znajdź sposób na powiązanie ich ze sobą, tj. Reg-zielony i niebiesko-fioletowy. W tym przykładzie powinno wystarczyć użycie średniej krawędzi do parowania.

Następnie potrzebujesz metody triangulacji odstępu między krawędziami. Jak wspomniałeś, wystarczy połączyć trójkąt (lub dwa), aby połączyć części i użyć czegoś takiego jak CGAL :: Polygon_mesh_processing :: triangulate_refine_and_fair_hole, aby wypełnić resztę.

Aby to zrobić, możesz spróbować znaleźć dwie krawędzie każdej listy, które są blisko siebie. Czyli suma odległości punktowych jest tak mała, jak to możliwe. Wybierz jedną krawędź z jednej listy i znajdź najbliższą krawędź w drugiej. Gdy masz dwie krawędzie, dodaj parę trójkątów i użyj CGAL, aby wypełnić resztę. Różne części powinny mieć tę samą orientację powierzchni, aby to działało, ale prawdopodobnie tak jest.

Innym podejściem byłoby po prostu użycie wierzchołków do utworzenia siatki z chmury punktów , ale nie ma gwarancji, że będzie pasować do bieżącej siatki. Najłatwiejszym rozwiązaniem jest prawdopodobnie całkowite uniknięcie problemu, tzn. Upewnienie się, że źródło siatek tworzy dobrze zdefiniowane połączone siatki.

przykład krawędzi do połączenia

JonasH
źródło
Dziękuję za odpowiedź, to jest rzeczywiście podejście, nad którym pracowałem od jakiegoś czasu, prawie skończyłem programować, obecnie mam problemy z twarzami w złym kierunku, więc dziury wypełniania nie działają.
Niels