CGAL соединяющий 2 геометрии

11

В настоящее время я пытаюсь объединить разные части сетки, которые не связаны между собой. Из примера я нашел это (blobby_3cc.off).

С keep_large_connected_componentsи keep_largest_connected_componentsя удаляю все мелкие компоненты. Который держит эти 3 ниже.

Я не могу найти способ в документации объединить их и заполнить недостающие части. Одним из решений является создание 1 треугольника и заполнение отверстий (с тех пор это 1 объект с огромными отверстиями). Но я не могу найти способ объединить их.

У кого-нибудь есть решение для этого?

Я использую CGAL для C ++.

введите описание изображения здесь

Нильс
источник

Ответы:

3

Когда я начал с CGAL, я почти сразу столкнулся с этой проблемой. Я смог найти решение после тщательного прочтения документации по полигональной сетке. . По сути, с помощью модифицированной версии Corefinement вы можете плавно связать воедино две отдельные геометрии, независимо от их числа или формы (однако, чем больше разница между полигонами, тем менее эффективной она станет).

Сначала нужно убедиться, что геометрия не пересекается. Во-вторых, убедитесь, что CGAL::Polygon_mesh_processing::clip()активен на двух геометриях (я предлагаю использоватьclose_volumes=false ). Затем вычислите объединение двух новых сеток:

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

Вместо использования сетки с точкой из ядра с точными конструкциями точные точки являются свойством вершин сетки, которые мы можем использовать в более поздних операциях. С этим свойством мы можем манипулировать сеткой с точками, имеющими координаты с плавающей точкой, но выиграть от надежности, обеспечиваемой точными конструкциями.

#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
Спасибо за ваш ответ. Я пытаюсь понять ваш код, но некоторые функции я , кажется, не понимают corefine_and_compute_union, corefine_and_compute_intersection. У меня нет четкого понимания в документах. Можешь немного объяснить?
Нильс
По сути, corefine_and_compute_unionвычисляет сегменты сетки, которые перекрываются и должны быть удалены и заменены полигональной заливкой. corefine_and_compute_intersectionблизок к тому же, но использует существующую сетку, чтобы заполнить разрез вместо генерации гладкой заливки сетки. Первая функция обычно требует точного ввода для работы, но вторая позволяет ей передавать себя в качестве параметра.
Смертельный вальс
Я должен проверить это в эти выходные и увидеть результат, чтобы я знал, как это работает. Я приму этот ответ как правильный ответ до того, как закончится щедрость.
Нильс
Хорошо, если это не сработает, дайте мне знать
Смертельный вальс
0

Как выглядит сетка изначально? Было бы целесообразно объединить различные компоненты, а не удалить самые маленькие части? Видеть CGAL комбинаторный ремонт для получения дополнительной информации.

Соединение различных компонентов является довольно сложной проблемой. Я полагаю, что обычные алгоритмы заполнения отверстий работают только на отверстиях, которые ограничены, то есть есть открытый край, который проходит вокруг отверстия и заканчивается в начале.

Я бы порекомендовал проанализировать сетку, чтобы найти открытые списки ребер, которые необходимо соединить, то есть красные, зеленые, синие и фиолетовые линии. Найдите способ соединить их друг с другом, например, зеленый и синий с фиолетовым. В примере должно быть достаточно просто использовать среднее значение ребер для спаривания.

Тогда вам понадобится какой-нибудь метод для триангуляции зазора между краями. Как вы упомянули, должно быть достаточно создать треугольник (или два) для соединения частей и использовать что-то вроде CGAL :: Polygon_mesh_processing :: triangulate_refine_and_fair_hole для заполнения остальных.

Для этого вы можете попытаться найти два края каждого списка, которые находятся близко друг к другу. Т.е. сумма точечных расстояний как можно меньше. Так что выберите один край из одного списка и найдите ближайший край в другом. Если у вас есть два ребра, добавьте пару треугольников и используйте CGAL, чтобы заполнить остальные. Различные части должны иметь одинаковую ориентацию поверхности, чтобы это работало, но это, вероятно, имеет место.

Другой подход заключается в том, чтобы просто использовать вершины для создания сетки из облака точек , но это не гарантирует совпадения с вашей текущей сеткой. Самое простое решение, вероятно, состоит в том, чтобы попытаться полностью избежать этой проблемы, т.е. обеспечить, чтобы источник ячеек создавал четко определенные связанные ячейки.

пример ребер для соединения

JonasH
источник
Спасибо за ваш ответ, это действительно тот подход, над которым я работал некоторое время, я почти закончил программирование, в настоящее время возникают проблемы с гранями в неправильном направлении, поэтому заполнение отверстий завершается неудачей.
Нильс