CGAL conectando 2 geometrias

11

Atualmente, tento juntar diferentes partes da malha, que não estão conectadas. No exemplo, eu encontrei isso (blobby_3cc.off).

Com o keep_large_connected_componentse keep_largest_connected_componentsremover todos os componentes menores. O que mantém esses 3 abaixo.

Não consigo encontrar na documentação uma maneira de juntá-los e preencher as partes ausentes. Uma solução é criar um triângulo e preencher os buracos (desde então, é um objeto, com buracos enormes). Mas não consigo encontrar uma maneira de juntar essas coisas.

Alguém tem uma solução para isso?

Estou usando o CGAL para C ++.

insira a descrição da imagem aqui

Niels
fonte

Respostas:

3

Quando comecei com a CGAL, quase imediatamente encontrei esse problema. Consegui encontrar uma solução depois de ler atentamente a documentação da malha poligonal . Essencialmente, por meio de uma versão modificada do Corefinement , você pode mesclar suavemente duas geometrias separadas, independentemente da sua contagem ou forma de polis (no entanto, quanto maior a diferença de polígonos, menos eficaz ele se tornará).

O que você precisa fazer é primeiro, verifique se a geometria não se intercepta. Em segundo lugar, verifique se CGAL::Polygon_mesh_processing::clip()está ativo nas duas geometrias (sugiro usar close_volumes=false). Em seguida, calcule a união das duas novas malhas:

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

Em vez de usar uma malha com um ponto de um kernel com construções exatas, os pontos exatos são uma propriedade dos vértices da malha que podemos reutilizar em operações posteriores. Com essa propriedade, podemos manipular uma malha com pontos com coordenadas de ponto flutuante, mas nos beneficiamos da robustez fornecida pelas construções exatas .:

#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
fonte
E para preencher todos os buracos, consulte Reparo combinatório e preenchimento de buracos
Death Waltz
Obrigado por sua resposta. Eu tento entender seu código, mas algumas funções não parecem entender corefine_and_compute_union, corefine_and_compute_intersection. Não entendo claramente os documentos. Você pode explicar um pouco?
Niels
Essencialmente, corefine_and_compute_unioncalcula segmentos da malha que se sobrepõem e precisam ser removidos e substituídos por um preenchimento de polígono. corefine_and_compute_intersectionestá próximo da mesma coisa, mas usa a malha existente para preencher o corte em vez de gerar um preenchimento de malha suave. A primeira função geralmente requer uma entrada exata para funcionar, mas a segunda permite que ela passe como um parâmetro.
Death Waltz
Eu tenho que dar uma olhada neste fim de semana e ver o resultado para que eu saiba como funciona. Aceitarei esta resposta como a resposta correta antes que a recompensa acabe.
Niels
Tudo bem, se não funcionar, avise-me
Death Waltz
0

Como é a malha originalmente? seria possível mesclar os diferentes componentes em vez de remover as peças menores? Consulte Reparação combinatória da CGAL para obter mais informações.

Conectar diferentes componentes é um problema bastante difícil. Eu acredito que os algoritmos regulares de preenchimento de orifícios só funcionam em orifícios delimitados, ou seja, existe uma borda aberta que circula o orifício e termina no início.

Minha recomendação seria analisar a malha para encontrar as listas de arestas abertas que precisam ser conectadas, ou seja, as linhas vermelha, verde, azul e roxa. Encontre uma maneira de emparelhá-los um com o outro, ou seja, reg-green e blue-purple. No exemplo, deve ser suficiente usar apenas a média das arestas para o emparelhamento.

Então você precisaria de algum método para triangular o espaço entre as bordas. Como você mencionou, deve ser suficiente criar um triângulo (ou dois) para conectar as partes e usar algo como CGAL :: Polygon_mesh_processing :: triangulate_refine_and_fair_hole para preencher o restante.

Para fazer isso, você pode tentar encontrar as duas arestas de cada lista próximas umas das outras. Ou seja, a soma das distâncias em pontos é a menor possível. Portanto, escolha uma aresta de uma lista e encontre a aresta mais próxima da outra. Quando você tiver duas arestas, adicione um par de triângulos e use CGAL para preencher o restante. As diferentes partes devem ter a mesma orientação de superfície para que isso funcione, mas esse provavelmente é o caso.

Outra abordagem seria usar apenas os vértices para criar uma malha a partir de uma nuvem de pontos , mas isso não garante que corresponda à sua malha atual. A solução mais fácil é provavelmente tentar evitar o problema completamente, ou seja, garantir que a fonte das malhas produza malhas conectadas bem definidas.

exemplo de arestas para conectar

JonasH
fonte
Obrigado pela sua resposta, esta é realmente a abordagem na qual trabalho há algum tempo, quase terminei de programar, atualmente tendo problemas com rostos na direção errada para que os furos de preenchimento falhem.
Niels