CGAL verbindet 2 Geometrien

11

Derzeit versuche ich, verschiedene Teile des Netzes zu verbinden, die nicht verbunden sind. Aus dem Beispiel habe ich dies gefunden (blobby_3cc.off).

Mit dem keep_large_connected_componentsund dem keep_largest_connected_componentsentferne ich alle kleineren Komponenten. Welches hält diese 3 unten.

Ich kann in der Dokumentation keinen Weg finden, sie zusammenzufügen und die fehlenden Teile zu füllen. Eine Lösung besteht darin, 1 Dreieck zu erstellen und die Löcher zu füllen (seitdem ist es 1 Objekt mit riesigen Löchern). Aber ich kann keinen Weg finden, diese zusammenzufügen.

Hat jemand eine Lösung dafür?

Ich benutze CGAL für C ++.

Geben Sie hier die Bildbeschreibung ein

Niels
quelle

Antworten:

3

Als ich mit CGAL anfing, stieß ich fast sofort auf dieses Problem. Nachdem ich die Dokumentation zum Polygonnetz sorgfältig gelesen hatte, konnte ich eine Lösung finden . Im Wesentlichen können Sie durch eine modifizierte Version von Corefinement zwei separate Geometrien unabhängig von ihrer Polyanzahl oder -form nahtlos miteinander verbinden (je größer der Unterschied der Polygone ist, desto weniger effektiv wird sie).

Stellen Sie zunächst sicher, dass sich die Geometrie nicht selbst schneidet. Stellen Sie zweitens sicher, dass CGAL::Polygon_mesh_processing::clip()die beiden Geometrien aktiv sind (ich schlage vor, sie zu verwenden close_volumes=false). Berechnen Sie als Nächstes die Vereinigung der beiden neuen Netze:

#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;
}

Anstatt ein Netz mit einem Punkt aus einem Kernel mit genauen Konstruktionen zu verwenden, sind die genauen Punkte eine Eigenschaft der Netzscheitelpunkte, die wir in späteren Operationen wiederverwenden können. Mit dieser Eigenschaft können wir ein Netz mit Punkten mit Gleitkommakoordinaten bearbeiten, aber von der Robustheit profitieren, die die genauen Konstruktionen bieten:

#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;
}
Todeswalzer
quelle
Und alle Löcher zu füllen, siehe Combinatorial Reparieren und Lochfüllung
Tod Waltz
Danke für Ihre Antwort. Ich versuche , den Code zu verstehen, aber einige Funktionen , die ich scheinen nicht zu verstehen corefine_and_compute_union, corefine_and_compute_intersection. Ich habe kein klares Verständnis in den Dokumenten. Kannst du ein bisschen erklären?
Niels
corefine_and_compute_unionBerechnet im Wesentlichen Segmente des Netzes, die sich überlappen und entfernt und durch eine Polygonfüllung ersetzt werden müssen. corefine_and_compute_intersectionist in der Nähe der gleichen Sache, verwendet jedoch vorhandenes Netz, um den Schnitt zu füllen, anstatt eine glatte Netzfüllung zu erzeugen. Die erste Funktion erfordert normalerweise eine genaue Eingabe, um zu funktionieren, aber die zweite erlaubt es, sich selbst als Parameter zu übergeben.
Tod Waltz
Ich muss es mir dieses Wochenende ansehen und das Ergebnis sehen, damit ich weiß, wie es funktioniert. Ich werde diese Antwort als die richtige Antwort akzeptieren, bevor das Kopfgeld ausgeht.
Niels
Okay, wenn es nicht funktioniert, lass es mich wissen
Death Waltz
0

Wie sieht das Netz ursprünglich aus? Wäre es möglich, die verschiedenen Komponenten zusammenzuführen, anstatt die kleinsten Teile zu entfernen? Weitere Informationen finden Sie unter Kombinatorische CGAL-Reparatur .

Das Anschließen verschiedener Komponenten ist ein ziemlich schwieriges Problem. Ich glaube, die regulären Algorithmen zum Füllen von Löchern funktionieren nur bei Löchern, die begrenzt sind, dh es gibt eine offene Kante, die um das Loch herum verläuft und am Anfang endet.

Meine Empfehlung wäre, das Netz zu analysieren, um die offenen Kantenlisten zu finden, die verbunden werden müssen, dh die roten, grünen, blauen und violetten Linien. Finden Sie einen Weg, diese miteinander zu koppeln, dh reg-grün und blau-lila. Im Beispiel sollte es ausreichen, nur den Durchschnitt der Kanten für die Paarung zu verwenden.

Dann würden Sie eine Methode benötigen, um die Lücke zwischen den Kanten zu triangulieren. Wie Sie bereits erwähnt haben, sollte es ausreichen, ein Dreieck (oder zwei) zum Verbinden der Teile zu erstellen und den Rest mit CGAL :: Polygon_mesh_processing :: triangulate_refine_and_fair_hole zu füllen.

Zu diesem Zweck können Sie versuchen, die beiden Kanten jeder Liste zu finden, die nahe beieinander liegen. Dh die Summe der Punktabstände ist so klein wie möglich. Wählen Sie also eine Kante aus einer Liste aus und suchen Sie die nächstgelegene Kante in der anderen. Wenn Sie zwei Kanten haben, fügen Sie ein Paar Dreiecke hinzu und füllen Sie den Rest mit CGAL. Die verschiedenen Teile sollten die gleiche Oberflächenorientierung haben, damit dies funktioniert, aber das ist wahrscheinlich der Fall.

Ein anderer Ansatz wäre, nur die Eckpunkte zu verwenden, um ein Netz aus einer Punktwolke zu erstellen. Es ist jedoch nicht garantiert, dass dies mit Ihrem aktuellen Netz übereinstimmt. Die einfachste Lösung besteht wahrscheinlich darin, zu versuchen, das Problem insgesamt zu vermeiden, dh sicherzustellen, dass die Quelle der Netze gut definierte verbundene Netze erzeugt.

Beispiel für zu verbindende Kanten

JonasH
quelle
Vielen Dank für Ihre Antwort. Dies ist in der Tat der Ansatz, an dem ich eine Weile gearbeitet habe. Ich habe die Programmierung fast abgeschlossen und habe derzeit Probleme mit Gesichtern in die falsche Richtung, sodass die Fülllöcher fehlschlagen.
Niels