2つのジオメトリを接続するCGAL


11

現在、接続されていないメッシュのさまざまな部分を結合しようとしています。例からこれを見つけました(blobby_3cc.off)。

keep_large_connected_componentsし、keep_largest_connected_components私はすべての小さなコンポーネントを削除します。これら3つを下に保持します。

ドキュメントでそれらを結合し、不足している部分を埋める方法が見つかりません。1つの解決策は、1つの三角形を作成して穴を埋めることです(それ以降は1つのオブジェクトであり、巨大な穴があります)。しかし、私はこれらを結合する方法を見つけることができません。

誰かがこれに対する解決策を持っていますか?

C ++にはCGALを使用しています。

ここに画像の説明を入力してください

回答:


3

CGALを使い始めたとき、私はほとんどすぐにこの問題に遭遇しました。ポリゴンメッシュのドキュメントを注意深く読んだ後、解決策を見つけることができました。基本的に、Corefinementの修正バージョンを使用すると、ポリゴンの数や形状に関係なく、2つの個別のジオメトリをスムーズにメッシュ化できます(ただし、ポリゴンの差が大きいほど、効果が低下します)。

まず、ジオメトリが自己交差しないことを確認してください。次に、それCGAL::Polygon_mesh_processing::clip()が2つのジオメトリでアクティブであることを確認します(を使用することをお勧めしますclose_volumes=false)。次に、2つの新しいメッシュの結合を計算します。

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

そして、任意の穴を埋めるために、参照の組合せの修復及び穴埋め
死のワルツ

お返事ありがとうございます。私はあなたのコードを理解しようとしますが、私が理解していないように見えるいくつかの関数corefine_and_compute_unioncorefine_and_compute_intersection。私はドキュメントを明確に理解していません。少し説明してもらえますか?
Niels

基本的に、corefine_and_compute_union重なり合うメッシュのセグメントを計算し、削除してポリゴンフィルで置き換える必要があります。corefine_and_compute_intersectionはほぼ同じですが、滑らかなメッシュフィルを生成する代わりに、既存のメッシュを使用してカットを塗りつぶします。最初の関数は通常、正確な入力が必要ですが、2番目の関数はそれ自体をパラメーターとして渡すことができます。
死のワルツ

私は今週末にそれをチェックして結果を見て、それがどのように機能するかを知る必要があります。バウンティがなくなる前に、私はこの答えを正解として受け入れます。
Niels

うまくいかない場合はお知らせください
死のワルツ

0

メッシュは元々どのように見えますか?最小の部品を削除するのではなく、異なるコンポーネントをマージすることは可能でしょうか?詳細については、CGALの組み合わせによる修復を参照してください。

異なるコンポーネントを接続することは、かなり難しい問題です。通常の穴埋めアルゴリズムは、境界のある穴でのみ機能する、つまり、穴の周りを通り、最初に終わる開いたエッジがあると思います。

私の推奨は、メッシュを分析して、接続する必要のある開いたエッジリスト、つまり赤、緑、青、紫の線を見つけることです。これらを相互にペアリングする方法を見つけてください。つまり、reg-greenとblue-purpleです。この例では、ペアリングにエッジの平均を使用するだけで十分です。

次に、エッジ間のギャップを三角測量するいくつかの方法が必要になります。あなたが言及したように、パーツを接続するために三角形(または2つ)を作成し、CGAL :: Polygon_mesh_processing :: triangulate_refine_and_fair_holeなどを使用して残りを埋めるだけで十分です。

これを行うには、互いに近い各リストの2つのエッジを見つけようとします。つまり、ポイント距離の合計は可能な限り小さくなります。したがって、1つのリストから1つのエッジを選択し、もう一方の最も近いエッジを見つけます。2つのエッジがある場合は、三角形のペアを追加し、CGALを使用して残りを塗りつぶします。これが機能するためには、さまざまなパーツの表面方向が同じである必要がありますが、おそらくそれが当てはまります。

別のアプローチは、頂点を使用して点群からメッシュ作成することですが、これは現在のメッシュとの一致が保証されているわけではありません。最も簡単な解決策は、おそらく問題を完全に回避することです。つまり、メッシュのソースが明確に定義された接続メッシュを生成するようにします。

接続するエッジの例


お返事ありがとうございます。これは確かに私がしばらく取り組んできたアプローチです。プログラミングはほぼ終了し、現在、面の向きが間違っているため、穴埋めが失敗します。
Niels
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.