CGAL conectando 2 geometrías

11

Actualmente trato de unir diferentes partes de la malla, que no están conectadas. Del ejemplo encontré esto (blobby_3cc.off).

Con el keep_large_connected_componentsy el keep_largest_connected_componentselimino todos los componentes más pequeños. Lo que mantiene estos 3 a continuación.

No puedo encontrar una manera en la documentación para unirlos y completar las partes que faltan. Una solución es crear 1 triángulo y llenar los agujeros (desde entonces es 1 objeto, con agujeros enormes). Pero no puedo encontrar una manera de unirlos.

¿Alguien tiene una solución para esto?

Estoy usando CGAL para C ++.

ingrese la descripción de la imagen aquí

Niels
fuente

Respuestas:

3

Cuando comencé con CGAL, casi inmediatamente me encontré con este problema. Pude encontrar una solución después de leer detenidamente la documentación de la malla poligonal . Esencialmente, a través de una versión modificada de Corefinement , puede suavemente dos geometrías separadas, sin importar su conteo o forma de polietileno (sin embargo, cuanto mayor sea la diferencia de polígonos, menos efectiva será).

Lo que tienes que hacer es primero, asegúrate de que la geometría no se auto-intersecte. En segundo lugar, asegúrese de que CGAL::Polygon_mesh_processing::clip()esté activo en las dos geometrías (sugiero usar close_volumes=false). A continuación, calcule la unión de las dos nuevas mallas:

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

En lugar de usar una malla con un punto de un núcleo con construcciones exactas, los puntos exactos son una propiedad de los vértices de la malla que podemos reutilizar en operaciones posteriores. Con esa propiedad, podemos manipular una malla con puntos que tienen coordenadas de punto flotante, pero se benefician de la robustez proporcionada por las construcciones exactas .:

#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;
}
Vals de la muerte
fuente
Y para llenar cualquier agujero, vea Reparación combinatoria y llenado de agujeros
Death Waltz
Gracias por su respuesta. Trato de entender el código, pero algunas funciones no parecen entender corefine_and_compute_union, corefine_and_compute_intersection. No entiendo nada claro en los documentos. ¿Puedes explicar un poco?
Niels
Básicamente, corefine_and_compute_unioncalcula los segmentos de la malla que se superponen y deben eliminarse y reemplazarse con un relleno de polígono. corefine_and_compute_intersectionestá cerca de lo mismo, pero usa una malla existente para rellenar el corte en lugar de generar un relleno de malla suave. La primera función generalmente requiere una entrada exacta para funcionar, pero la segunda le permite pasar como un parámetro.
Death Waltz
Tengo que echarle un vistazo este fin de semana y ver el resultado para saber cómo funciona. Aceptaré esta respuesta como la respuesta correcta antes de que se acabe la recompensa.
Niels
Muy bien, si no funciona, hágamelo saber
Death Waltz
0

¿Cómo se ve la malla originalmente? ¿Sería posible fusionar los diferentes componentes en lugar de eliminar las partes más pequeñas? Ver reparación combinatoria CGAL para más información.

Conectar diferentes componentes es un problema bastante difícil. Creo que los algoritmos regulares de relleno de agujeros solo funcionan en agujeros que están delimitados, es decir, hay un borde abierto que rodea el agujero y termina al principio.

Mi recomendación sería analizar la malla para encontrar las listas de bordes abiertos que deben conectarse, es decir, las líneas roja, verde, azul y morada. Encuentre una manera de emparejarlos entre sí, es decir, verde reg y azul púrpura. En el ejemplo, debería ser suficiente usar el promedio de los bordes para el emparejamiento.

Entonces necesitarías algún método para triangular el espacio entre los bordes. Como mencionas, debería ser suficiente crear un triángulo (o dos) para conectar las partes, y usar algo como CGAL :: Polygon_mesh_processing :: triangulate_refine_and_fair_hole para llenar el resto.

Para hacer esto, puede intentar encontrar los dos bordes de cada lista que estén cerca uno del otro. Es decir, la suma de las distancias de puntos es lo más pequeña posible. Así que elige un borde de una lista y encuentra el borde más cercano en el otro. Cuando tenga dos aristas, agregue un par de triángulos y use CGAL para llenar el resto. Las diferentes partes deben tener la misma orientación de superficie para que esto funcione, pero ese es probablemente el caso.

Otro enfoque sería usar los vértices para crear una malla a partir de una nube de puntos , pero no se garantiza que coincida con su malla actual. La solución más fácil es probablemente tratar de evitar el problema por completo, es decir, asegurar que la fuente de las mallas produzca mallas conectadas bien definidas.

ejemplo de bordes para conectar

JonasH
fuente
Gracias por su respuesta, este es el enfoque en el que he estado trabajando durante un tiempo, casi termino la programación, actualmente tengo problemas con las caras en la dirección incorrecta, por lo que los agujeros de relleno fallan.
Niels