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;
}
corefine_and_compute_union
,corefine_and_compute_intersection
. Não entendo claramente os documentos. Você pode explicar um pouco?corefine_and_compute_union
calcula segmentos da malha que se sobrepõem e precisam ser removidos e substituídos por um preenchimento de polígono.corefine_and_compute_intersection
está 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.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.
fonte