Wildmeshing Toolkit
tetwild_simplification.cpp
Go to the documentation of this file.
1 // This file is part of fTetWild, a software for generating tetrahedral meshes.
2 //
3 // Copyright (C) 2019 Yixin Hu <yixin.hu@nyu.edu>
4 // This Source Code Form is subject to the terms of the Mozilla Public License
5 // v. 2.0. If a copy of the MPL was not distributed with this file, You can
6 // obtain one at http://mozilla.org/MPL/2.0/.
7 //
8 
10 
11 #include <wmtk/Mesh.hpp>
12 #include <wmtk/TriMesh.hpp>
14 #include <wmtk/utils/Logger.hpp>
16 
17 
18 #include <igl/remove_duplicate_vertices.h>
19 #include <igl/unique_rows.h>
20 
21 #include <fastenvelope/FastEnvelope.h>
22 #include <SimpleBVH/BVH.hpp>
23 
24 #include <polysolve/Utils.hpp>
25 
26 #include <queue>
27 #include <random>
28 #include <unordered_set>
29 
30 #ifdef WMTK_WITH_TBB
31 #include <tbb/concurrent_unordered_set.h>
32 #include <tbb/concurrent_vector.h>
33 #include <tbb/parallel_for.h>
34 #include <tbb/parallel_sort.h>
35 #endif
36 
37 namespace wmtk::components {
38 
41 
42 
43 class Random
44 {
45 public:
46  static int next(int min, int max)
47  {
48  static std::mt19937 gen(42);
49  int res = (gen() - std::mt19937::min()) /
50  double(std::mt19937::max() - std::mt19937::min()) * (max - min) +
51  min;
52 
53  return res;
54  }
55 
56  template <typename T>
57  static void shuffle(std::vector<T>& vec)
58  {
59  for (int i = vec.size() - 1; i > 0; --i) {
60  using std::swap;
61  const int index = next(0, i + 1);
62  swap(vec[i], vec[index]);
63  }
64  }
65 };
66 
67 inline int mod3(int j)
68 {
69  return j % 3;
70 }
71 
73 {
74 public:
76  const std::vector<Eigen::Vector3d>& vertices,
77  const std::vector<Eigen::Vector3i>& faces,
78  double envelope_size,
79  double sampling_dist,
80  bool use_sampling)
81  : m_envelope_size2(envelope_size * envelope_size)
82  , m_sampling_dist(sampling_dist)
83  , m_use_sampling(use_sampling)
84  {
85  m_envelope = std::make_shared<fastEnvelope::FastEnvelope>(vertices, faces, envelope_size);
86  m_bvh = std::make_shared<SimpleBVH::BVH>();
87 
88  Eigen::MatrixXd V(vertices.size(), 3);
89  Eigen::MatrixXi F(faces.size(), 3);
90  for (int i = 0; i < vertices.size(); i++) V.row(i) = vertices[i];
91  for (int i = 0; i < faces.size(); i++) {
92  F.row(i) = faces[i];
93  m_facets.push_back({{V.row(F(i, 0)), V.row(F(i, 1)), V.row(F(i, 2))}});
94  }
95 
96  m_bvh->init(V, F, SCALAR_ZERO);
97  }
98 
99  bool is_out(const std::array<Vector3, 3>& triangle) const
100  {
101  if (m_use_sampling)
102  return is_out_using_sampling(triangle);
103  else
104  return m_envelope->is_outside(triangle);
105  }
106 
107  bool is_out_envelope(const SimpleBVH::VectorMax3d& p, int& prev_facet) const
108  {
109  SimpleBVH::VectorMax3d nearest_point;
110  double sq_dist = std::numeric_limits<double>::max();
111 
112  if (prev_facet >= 0) {
113  m_bvh->point_facet_distance(p, prev_facet, nearest_point, sq_dist);
114 
115  if (sq_dist < m_envelope_size2) return false;
116  }
117 
118 
119  // prev_facet = m_bvh->facet_in_envelope(p, m_envelope_size2, nearest_point, sq_dist);
120  m_bvh->facet_in_envelope_with_hint(p, m_envelope_size2, prev_facet, nearest_point, sq_dist);
121 
122  return sq_dist > m_envelope_size2;
123  }
124 
125  bool is_out_using_sampling(const std::array<Vector3, 3>& vs) const
126  {
127  static const double sqrt3_2 = std::sqrt(3) / 2;
128 
129  int prev_facet = -1;
130 
131  // first check 3 verts
132  if (is_out_envelope(vs[0], prev_facet)) return true;
133  if (is_out_envelope(vs[1], prev_facet)) return true;
134  if (is_out_envelope(vs[2], prev_facet)) return true;
136 
137  std::array<double, 3> ls;
138  for (int i = 0; i < 3; i++) {
139  ls[i] = (vs[i] - vs[mod3(i + 1)]).squaredNorm();
140  }
141 
142  auto min_max = std::minmax_element(ls.begin(), ls.end());
143  const int min_i = min_max.first - ls.begin();
144  const int max_i = min_max.second - ls.begin();
145 
146  double N = sqrt(ls[max_i]) / m_sampling_dist;
147 
148  // no sampling, we alrady checked the vertices
149  if (N <= 1) {
150  return false;
151  }
152 
153  if (N == int(N)) N -= 1;
154 
155  const Eigen::Vector3d& v0 = vs[max_i];
156  const Eigen::Vector3d& v1 = vs[mod3(max_i + 1)];
157  const Eigen::Vector3d& v2 = vs[mod3(max_i + 2)];
158 
159  const Eigen::Vector3d v1v0 = v1 - v0;
160  const Eigen::Vector3d v2v0 = v2 - v0;
161  const Eigen::Vector3d v2v1 = v2 - v1;
162  const Eigen::Vector3d n_v0v1 = v1v0.normalized();
163 
164  // start from 1, we already checked the vertices
165  for (int n = 1; n < N; n++) {
166  const Eigen::Vector3d p = v0 + n_v0v1 * m_sampling_dist * n;
167  if (is_out_envelope(p, prev_facet)) return true;
168  }
169 
170 
171  // const double h = (v2v0.dot(v1v0) * v1v0 / ls[max_i] + v0 - v2).norm();
172  const double h = (v2v0.dot(v1v0) * v1v0 / ls[max_i] - v2v0).norm();
173  const int M = h / (sqrt3_2 * m_sampling_dist);
174  // we already checked the vertices
175  if (M < 1) {
176  return false;
177  }
178 
179  const Eigen::Vector3d n_v0v2 = v2v0.normalized();
180  const Eigen::Vector3d n_v1v2 = v2v1.normalized();
181 
182  const double sin_v0 = (v2v0.cross(v1v0)).norm() / (v2v0.norm() * v1v0.norm());
183  const double tan_v0 = (v2v0.cross(v1v0)).norm() / v2v0.dot(v1v0);
184  // const double tan_v1 = ((v2 - v1).cross(v0 - v1)).norm() / (v2 - v1).dot(v0 - v1);
185  const double tan_v1 = -(v2v1.cross(v1v0)).norm() / (v2v1).dot(v1v0);
186  // const double sin_v1 = (v2v1.cross(v0 - v1)).norm() / ((v1 - v2).norm() * (v0 - v1).norm());
187  const double sin_v1 = (v2v1.cross(v1v0)).norm() / (v2v1.norm() * v1v0.norm());
188 
189  for (int m = 1; m <= M; m++) {
190  int n = sqrt3_2 / tan_v0 * m + 0.5;
191  int n1 = sqrt3_2 / tan_v0 * m;
192  if (m % 2 == 0 && n == n1) {
193  n += 1;
194  }
195  const Eigen::Vector3d v0_m = v0 + m * sqrt3_2 * m_sampling_dist / sin_v0 * n_v0v2;
196  const Eigen::Vector3d v1_m = v1 + m * sqrt3_2 * m_sampling_dist / sin_v1 * n_v1v2;
197  if ((v0_m - v1_m).norm() <= m_sampling_dist) break;
198 
199  double delta_d = ((n + (m % 2) / 2.0) - m * sqrt3_2 / tan_v0) * m_sampling_dist;
200  const Eigen::Vector3d v = v0_m + delta_d * n_v0v1;
201  int N1 = (v - v1_m).norm() / m_sampling_dist;
202  for (int i = 0; i <= N1; i++) {
203  const Eigen::Vector3d p = v + i * n_v0v1 * m_sampling_dist;
204 
205  if (is_out_envelope(p, prev_facet)) return true;
206  }
207  }
208 
209  // sample edges
210  N = sqrt(ls[mod3(max_i + 1)]) / m_sampling_dist;
211  if (N > 1) {
212  if (N == int(N)) N -= 1;
213 
214  for (int n = 1; n <= N; n++) {
215  const Eigen::Vector3d p = v1 + n_v1v2 * m_sampling_dist * n;
216 
217  if (is_out_envelope(p, prev_facet)) return true;
218  }
219  }
220 
221  N = sqrt(ls[mod3(max_i + 2)]) / m_sampling_dist;
222  if (N > 1) {
223  if (N == int(N)) N -= 1;
224 
225  const Eigen::Vector3d n_v2v0 = -n_v0v2;
226  for (int n = 1; n <= N; n++) {
227  const Eigen::Vector3d p = v2 + n_v2v0 * m_sampling_dist * n;
228 
229  if (is_out_envelope(p, prev_facet)) return true;
230  }
231  }
232 
233  return false;
234  }
235 
236  inline void project_to_sf(Vector3& p) const
237  {
238  SimpleBVH::VectorMax3d nearest_point;
239  double sq_dist;
240 
241  m_bvh->nearest_facet(p, nearest_point, sq_dist);
242  p[0] = nearest_point[0];
243  p[1] = nearest_point[1];
244  p[2] = nearest_point[2];
245  }
246 
247 private:
248  std::shared_ptr<fastEnvelope::FastEnvelope> m_envelope = nullptr;
249  std::shared_ptr<SimpleBVH::BVH> m_bvh = nullptr;
250  const double m_envelope_size2;
251  const double m_sampling_dist;
252  const bool m_use_sampling;
253  std::vector<std::array<SimpleBVH::VectorMax3d, 3>> m_facets;
254 };
255 
257 {
258 public:
259  std::array<int, 2> v_ids;
260  double weight;
261 
263  ElementInQueue(const std::array<int, 2>& ids, double w)
264  : v_ids(ids)
265  , weight(w)
266  {}
267 };
268 
269 struct cmp_s
270 {
271  bool operator()(const ElementInQueue& e1, const ElementInQueue& e2)
272  {
273  if (e1.weight == e2.weight) return e1.v_ids < e2.v_ids;
274  return e1.weight > e2.weight;
275  }
276 };
277 
278 struct cmp_l
279 {
280  bool operator()(const ElementInQueue& e1, const ElementInQueue& e2)
281  {
282  if (e1.weight == e2.weight) return e1.v_ids > e2.v_ids;
283  return e1.weight < e2.weight;
284  }
285 };
286 
287 #ifdef WMTK_WITH_TBB
288 void one_ring_edge_set(
289  const std::vector<std::array<int, 2>>& edges,
290  const std::vector<bool>& v_is_removed,
291  const std::vector<bool>& f_is_removed,
292  const std::vector<std::unordered_set<int>>& conn_fs,
293  const std::vector<Vector3>& input_vertices,
294  std::vector<int>& safe_set)
295 {
296  std::vector<int> indices(edges.size());
297  std::iota(std::begin(indices), std::end(indices), 0);
298  Random::shuffle(indices);
299 
300  std::vector<bool> unsafe_face(f_is_removed.size(), false);
301  safe_set.clear();
302  for (const int e_id : indices) {
303  const auto e = edges[e_id];
304  if (v_is_removed[e[0]] || v_is_removed[e[1]]) continue;
305 
306  bool ok = true;
307 
308 
309  for (const int f : conn_fs[e[0]]) {
310  if (f_is_removed[f]) continue;
311 
312  if (unsafe_face[f]) {
313  ok = false;
314  break;
315  }
316  }
317  if (!ok) continue;
318  for (const int f : conn_fs[e[1]]) {
319  if (f_is_removed[f]) continue;
320 
321  if (unsafe_face[f]) {
322  ok = false;
323  break;
324  }
325  }
326  if (!ok) continue;
327 
328  safe_set.push_back(e_id);
329 
330  for (const int f : conn_fs[e[0]]) {
331  if (f_is_removed[f]) continue;
332 
333  assert(!unsafe_face[f]);
334  unsafe_face[f] = true;
335  }
336  for (const int f : conn_fs[e[1]]) {
337  if (f_is_removed[f]) continue;
338 
339  // assert(!unsafe_face[f]);
340  unsafe_face[f] = true;
341  }
342  }
343 }
344 #endif
345 
347  const std::unordered_set<int>& s1,
348  const std::unordered_set<int>& s2,
349  std::vector<int>& v)
350 {
351  if (s2.size() < s1.size()) {
352  set_intersection(s2, s1, v);
353  return;
354  }
355  v.clear();
356  v.reserve(std::min(s1.size(), s2.size()));
357  for (int x : s1) {
358  if (s2.count(x)) {
359  v.push_back(x);
360  }
361  }
362  // std::sort(v.begin(), v.end());
363 }
364 
365 template <typename T>
366 void vector_unique(std::vector<T>& v)
367 {
368  std::sort(v.begin(), v.end());
369  v.erase(std::unique(v.begin(), v.end()), v.end());
370 }
371 
372 bool is_out_envelope(const std::array<Vector3, 3>& vs, const AABBWrapper& tree)
373 {
374  return tree.is_out(vs);
375 }
376 
377 double get_angle_cos(const Vector3& p, const Vector3& p1, const Vector3& p2)
378 {
379  Vector3 v1 = p1 - p;
380  Vector3 v2 = p2 - p;
381  double res = v1.dot(v2) / (v1.norm() * v2.norm());
382  if (res > 1) return 1;
383  if (res < -1) return -1;
384  return res;
385 }
386 
387 
389  std::vector<Vector3>& input_vertices,
390  std::vector<Vector3i>& input_faces,
391  const double duplicate_tol)
392 {
393  Eigen::MatrixXd V_tmp(input_vertices.size(), 3), V_in;
394  Eigen::MatrixXi F_tmp(input_faces.size(), 3), F_in;
395  for (int i = 0; i < input_vertices.size(); i++) V_tmp.row(i) = input_vertices[i];
396  for (int i = 0; i < input_faces.size(); i++) F_tmp.row(i) = input_faces[i];
397 
398  Eigen::VectorXi IV, _;
399  igl::remove_duplicate_vertices(V_tmp, F_tmp, duplicate_tol, V_in, IV, _, F_in);
400 
401  for (int i = 0; i < F_in.rows(); i++) {
402  int j_min = 0;
403  for (int j = 1; j < 3; j++) {
404  if (F_in(i, j) < F_in(i, j_min)) j_min = j;
405  }
406  if (j_min == 0) continue;
407  int v0_id = F_in(i, j_min);
408  int v1_id = F_in(i, (j_min + 1) % 3);
409  int v2_id = F_in(i, (j_min + 2) % 3);
410  F_in.row(i) << v0_id, v1_id, v2_id;
411  }
412  F_tmp.resize(0, 0);
413  Eigen::VectorXi IF;
414  igl::unique_rows(F_in, F_tmp, IF, _);
415  F_in = F_tmp;
416 
417  if (V_in.rows() == 0 || F_in.rows() == 0) return;
418 
419  logger().trace("remove duplicates: ");
420  logger().trace("#v: {} -> {}", input_vertices.size(), V_in.rows());
421  logger().trace("#f: {} -> {}", input_faces.size(), F_in.rows());
422 
423  input_vertices.resize(V_in.rows());
424  input_faces.clear();
425  input_faces.reserve(F_in.rows());
426  for (int i = 0; i < V_in.rows(); i++) input_vertices[i] = V_in.row(i);
427  for (int i = 0; i < F_in.rows(); i++) {
428  if (F_in(i, 0) == F_in(i, 1) || F_in(i, 0) == F_in(i, 2) || F_in(i, 2) == F_in(i, 1))
429  continue;
430  if (i > 0 && (F_in(i, 0) == F_in(i - 1, 0) && F_in(i, 1) == F_in(i - 1, 2) &&
431  F_in(i, 2) == F_in(i - 1, 1)))
432  continue;
433  // check area
434  Vector3 u = V_in.row(F_in(i, 1)) - V_in.row(F_in(i, 0));
435  Vector3 v = V_in.row(F_in(i, 2)) - V_in.row(F_in(i, 0));
436  Vector3 area = u.cross(v);
437  if (area.norm() / 2 <= duplicate_tol) continue;
438  input_faces.push_back(F_in.row(i));
439  }
440 }
441 
443  std::vector<Vector3>& input_vertices,
444  std::vector<Vector3i>& input_faces,
445  const AABBWrapper& tree,
446  std::vector<bool>& v_is_removed,
447  std::vector<bool>& f_is_removed,
448  std::vector<std::unordered_set<int>>& conn_fs)
449 {
450 #ifdef WMTK_WITH_TBB
451  std::vector<std::array<int, 2>> edges;
452  tbb::concurrent_vector<std::array<int, 2>> edges_tbb;
453 
454  const auto build_edges = [&]() {
455  edges.clear();
456  edges.reserve(input_faces.size() * 3);
457 
458  edges_tbb.clear();
459  edges_tbb.reserve(input_faces.size() * 3);
460 
461  tbb::parallel_for(size_t(0), input_faces.size(), [&](size_t f_id) {
462  if (f_is_removed[f_id]) return;
463 
464  for (int j = 0; j < 3; j++) {
465  std::array<int, 2> e = {{input_faces[f_id][j], input_faces[f_id][mod3(j + 1)]}};
466  if (e[0] > e[1]) std::swap(e[0], e[1]);
467  edges_tbb.push_back(e);
468  }
469  });
470 
471  edges.reserve(edges_tbb.size());
472  edges.insert(edges.end(), edges_tbb.begin(), edges_tbb.end());
473  assert(edges_tbb.size() == edges.size());
474  tbb::parallel_sort(edges.begin(), edges.end());
475 
476  edges.erase(std::unique(edges.begin(), edges.end()), edges.end());
477  };
478 #else
479  std::vector<std::array<int, 2>> edges;
480  edges.reserve(input_faces.size() * 3);
481  for (size_t f_id = 0; f_id < input_faces.size(); ++f_id) {
482  if (f_is_removed[f_id]) continue;
483 
484  const auto& f = input_faces[f_id];
485  for (int j = 0; j < 3; j++) {
486  std::array<int, 2> e = {{f[j], f[mod3(j + 1)]}};
487  if (e[0] > e[1]) std::swap(e[0], e[1]);
488  edges.push_back(e);
489  }
490  }
492 
493  std::priority_queue<ElementInQueue, std::vector<ElementInQueue>, cmp_s> sm_queue;
494  for (auto& e : edges) {
495  double weight = (input_vertices[e[0]] - input_vertices[e[1]]).squaredNorm();
496  sm_queue.push(ElementInQueue(e, weight));
497  sm_queue.push(ElementInQueue(std::array<int, 2>({{e[1], e[0]}}), weight));
498  }
499 #endif
500 
501  auto is_onering_clean = [&](int v_id) {
502  std::vector<int> v_ids;
503  v_ids.reserve(conn_fs[v_id].size() * 2);
504  for (const auto& f_id : conn_fs[v_id]) {
505  for (int j = 0; j < 3; j++) {
506  if (input_faces[f_id][j] != v_id) v_ids.push_back(input_faces[f_id][j]);
507  }
508  }
509  std::sort(v_ids.begin(), v_ids.end());
510 
511  if (v_ids.size() % 2 != 0) return false;
512  for (int i = 0; i < v_ids.size(); i += 2) {
513  if (v_ids[i] != v_ids[i + 1]) return false;
514  }
515 
516  return true;
517  };
518 
519  static const int SUC = 1;
520  static const int FAIL_CLEAN = 0;
521  static const int FAIL_FLIP = -1;
522  static const int FAIL_ENV = -2;
523 
524  auto remove_an_edge = [&](int v1_id, int v2_id, const std::vector<int>& n12_f_ids) {
525  if (!is_onering_clean(v1_id) || !is_onering_clean(v2_id)) return FAIL_CLEAN;
526 
527  // std::unordered_set<int> new_f_ids;
528  std::vector<int> new_f_ids;
529  for (int f_id : conn_fs[v1_id]) {
530  if (f_id != n12_f_ids[0] && f_id != n12_f_ids[1]) new_f_ids.push_back(f_id);
531  }
532  for (int f_id : conn_fs[v2_id]) {
533  if (f_id != n12_f_ids[0] && f_id != n12_f_ids[1]) new_f_ids.push_back(f_id);
534  }
535  vector_unique(new_f_ids);
536 
537  // compute new point
538  Vector3 p = (input_vertices[v1_id] + input_vertices[v2_id]) / 2;
539  tree.project_to_sf(p);
540  // GEO::vec3 geo_p(p[0], p[1], p[2]);
541  // GEO::vec3 nearest_p;
542  // double _;
543  // tree.nearest_facet(geo_p, nearest_p, _);
544  // p[0] = nearest_p[0];
545  // p[1] = nearest_p[1];
546  // p[2] = nearest_p[2];
547 
548  // computing normal for checking flipping
549  for (int f_id : new_f_ids) {
550  Vector3 old_nv =
551  (input_vertices[input_faces[f_id][1]] - input_vertices[input_faces[f_id][2]])
552  .cross(
553  input_vertices[input_faces[f_id][0]] -
554  input_vertices[input_faces[f_id][2]]);
555 
556  for (int j = 0; j < 3; j++) {
557  if (input_faces[f_id][j] == v1_id || input_faces[f_id][j] == v2_id) {
558  Vector3 new_nv = (input_vertices[input_faces[f_id][mod3(j + 1)]] -
559  input_vertices[input_faces[f_id][mod3(j + 2)]])
560  .cross(p - input_vertices[input_faces[f_id][mod3(j + 2)]]);
561  if (old_nv.dot(new_nv) <= 0) return FAIL_FLIP;
562  // check new tris' area
563  if (new_nv.norm() / 2 <= SCALAR_ZERO_2) return FAIL_FLIP;
564  break;
565  }
566  }
567  }
568 
569  // check if go outside of envelop
570  for (int f_id : new_f_ids) {
571  for (int j = 0; j < 3; j++) {
572  if (input_faces[f_id][j] == v1_id || input_faces[f_id][j] == v2_id) {
573  const std::array<Vector3, 3> tri = {
574  {p,
575  input_vertices[input_faces[f_id][mod3(j + 1)]],
576  input_vertices[input_faces[f_id][mod3(j + 2)]]}};
577  if (is_out_envelope(tri, tree)) return FAIL_ENV;
578  break;
579  }
580  }
581  }
582 
583  // real update
584  // std::unordered_set<int> n_v_ids;//get this info before real update for later usage
585  std::vector<int> n_v_ids; // get this info before real update for later usage
586  for (int f_id : new_f_ids) {
587  for (int j = 0; j < 3; j++) {
588  if (input_faces[f_id][j] != v1_id && input_faces[f_id][j] != v2_id)
589  n_v_ids.push_back(input_faces[f_id][j]);
590  }
591  }
592  vector_unique(n_v_ids);
593 
594  v_is_removed[v1_id] = true;
595  input_vertices[v2_id] = p;
596  for (int f_id : n12_f_ids) {
597  f_is_removed[f_id] = true;
598 #ifndef WMTK_WITH_TBB
599  for (int j = 0; j < 3; j++) { // rm conn_fs
600  if (input_faces[f_id][j] != v1_id) {
601  conn_fs[input_faces[f_id][j]].erase(f_id);
602  }
603  }
604 #endif
605  }
606  for (int f_id : conn_fs[v1_id]) { // add conn_fs
607  if (f_is_removed[f_id]) continue;
608  conn_fs[v2_id].insert(f_id);
609  for (int j = 0; j < 3; j++) {
610  if (input_faces[f_id][j] == v1_id) input_faces[f_id][j] = v2_id;
611  }
612  }
613 
614 #ifndef WMTK_WITH_TBB
615  // push new edges into the queue
616  for (int v_id : n_v_ids) {
617  double weight = (input_vertices[v2_id] - input_vertices[v_id]).squaredNorm();
618  sm_queue.push(ElementInQueue(std::array<int, 2>({{v2_id, v_id}}), weight));
619  sm_queue.push(ElementInQueue(std::array<int, 2>({{v_id, v2_id}}), weight));
620  }
621 #endif
622  return SUC;
623  };
624 
625 
626 #ifdef WMTK_WITH_TBB
627  std::atomic<int> cnt(0);
628  int cnt_suc = 0;
629  int fail_clean = -1;
630  int fail_flip = -1;
631  int fail_env = -1;
632 
633  const int stopping = input_vertices.size() / 10000.;
634 
635  std::vector<int> safe_set;
636  do {
637  build_edges();
638  one_ring_edge_set(edges, v_is_removed, f_is_removed, conn_fs, input_vertices, safe_set);
639  cnt = 0;
640 
641  tbb::parallel_for(size_t(0), safe_set.size(), [&](size_t i) {
642  // for (int i = 0; i < safe_set.size(); i++) {
643  std::array<int, 2>& v_ids = edges[safe_set[i]];
644 
645  // if (v_is_removed[v_ids[0]] || v_is_removed[v_ids[1]])
646  // return;
647 
648  std::vector<int> n12_f_ids;
649  set_intersection(conn_fs[v_ids[0]], conn_fs[v_ids[1]], n12_f_ids);
650 
651  if (n12_f_ids.size() != 2) return;
652  // continue;
653 
654  int res = remove_an_edge(v_ids[0], v_ids[1], n12_f_ids);
655  if (res == SUC) cnt++;
656  });
657  // }
658 
659  // cleanup conn_fs
660  tbb::parallel_for(size_t(0), conn_fs.size(), [&](size_t i) {
661  // for (int i = 0; i < conn_fs.size(); i++) {
662  if (v_is_removed[i])
663  // continue;
664  return;
665  std::vector<int> r_f_ids;
666  for (int f_id : conn_fs[i]) {
667  if (f_is_removed[f_id]) r_f_ids.push_back(f_id);
668  }
669  for (int f_id : r_f_ids) conn_fs[i].erase(f_id);
670  // }
671  });
672 
673  cnt_suc += cnt;
674  } while (cnt > stopping);
675 
676 #else
677  int cnt_suc = 0;
678  int fail_clean = 0;
679  int fail_flip = 0;
680  int fail_env = 0;
681 
682  while (!sm_queue.empty()) {
683  std::array<int, 2> v_ids = sm_queue.top().v_ids;
684  double old_weight = sm_queue.top().weight;
685  sm_queue.pop();
686 
687  if (v_is_removed[v_ids[0]] || v_is_removed[v_ids[1]]) continue;
688  if (old_weight != (input_vertices[v_ids[0]] - input_vertices[v_ids[1]]).squaredNorm())
689  continue;
690 
691  std::vector<int> n12_f_ids;
692  set_intersection(conn_fs[v_ids[0]], conn_fs[v_ids[1]], n12_f_ids);
693  if (n12_f_ids.size() != 2) continue;
694 
695  int res = remove_an_edge(v_ids[0], v_ids[1], n12_f_ids);
696  if (res == SUC)
697  cnt_suc++;
698  else if (res == FAIL_CLEAN)
699  fail_clean++;
700  else if (res == FAIL_FLIP)
701  fail_flip++;
702  else if (res == FAIL_ENV)
703  fail_env++;
704  }
705 #endif
706 
707  logger().trace(
708  "{} success, {} fail, {} flip, {} out of envelope",
709  cnt_suc,
710  fail_clean,
711  fail_flip,
712  fail_env);
713 }
714 
715 void swapping(
716  std::vector<Vector3>& input_vertices,
717  std::vector<Vector3i>& input_faces,
718  const AABBWrapper& tree,
719  std::vector<bool>& v_is_removed,
720  std::vector<bool>& f_is_removed,
721  std::vector<std::unordered_set<int>>& conn_fs)
722 {
723  std::vector<std::array<int, 2>> edges;
724  edges.reserve(input_faces.size() * 6);
725  for (int i = 0; i < input_faces.size(); i++) {
726  if (f_is_removed[i]) continue;
727  auto& f = input_faces[i];
728  for (int j = 0; j < 3; j++) {
729  std::array<int, 2> e = {{f[j], f[mod3(j + 1)]}};
730  if (e[0] > e[1]) std::swap(e[0], e[1]);
731  edges.push_back(e);
732  }
733  }
735 
736  std::priority_queue<ElementInQueue, std::vector<ElementInQueue>, cmp_l> sm_queue;
737  for (auto& e : edges) {
738  double weight = (input_vertices[e[0]] - input_vertices[e[1]]).squaredNorm();
739  sm_queue.push(ElementInQueue(e, weight));
740  sm_queue.push(ElementInQueue(std::array<int, 2>({{e[1], e[0]}}), weight));
741  }
742 
743  int cnt = 0;
744  while (!sm_queue.empty()) {
745  int v1_id = sm_queue.top().v_ids[0];
746  int v2_id = sm_queue.top().v_ids[1];
747  sm_queue.pop();
748 
749  std::vector<int> n12_f_ids;
750  set_intersection(conn_fs[v1_id], conn_fs[v2_id], n12_f_ids);
751  if (n12_f_ids.size() != 2) continue;
752 
753  std::array<int, 2> n_v_ids = {{-1, -1}};
754  for (int j = 0; j < 3; j++) {
755  if (n_v_ids[0] < 0 && input_faces[n12_f_ids[0]][j] != v1_id &&
756  input_faces[n12_f_ids[0]][j] != v2_id)
757  n_v_ids[0] = input_faces[n12_f_ids[0]][j];
758 
759  if (n_v_ids[1] < 0 && input_faces[n12_f_ids[1]][j] != v1_id &&
760  input_faces[n12_f_ids[1]][j] != v2_id)
761  n_v_ids[1] = input_faces[n12_f_ids[1]][j];
762  }
763 
764  // check coplanar
765  double cos_a0 =
766  get_angle_cos(input_vertices[n_v_ids[0]], input_vertices[v1_id], input_vertices[v2_id]);
767  double cos_a1 =
768  get_angle_cos(input_vertices[n_v_ids[1]], input_vertices[v1_id], input_vertices[v2_id]);
769  std::array<Vector3, 2> old_nvs;
770  for (int f = 0; f < 2; f++) {
771  auto& a = input_vertices[input_faces[n12_f_ids[f]][0]];
772  auto& b = input_vertices[input_faces[n12_f_ids[f]][1]];
773  auto& c = input_vertices[input_faces[n12_f_ids[f]][2]];
774  old_nvs[f] = ((b - c).cross(a - c)).normalized();
775  }
776  if (cos_a0 > -0.999) { // maybe it's for avoiding numerical issue
777  if (old_nvs[0].dot(old_nvs[1]) < 1 - 1e-6) // not coplanar
778  continue;
779  }
780 
781  // check inversion
782  auto& old_nv = cos_a1 < cos_a0 ? old_nvs[0] : old_nvs[1];
783  bool is_filp = false;
784  for (int f_id : n12_f_ids) {
785  auto& a = input_vertices[input_faces[f_id][0]];
786  auto& b = input_vertices[input_faces[f_id][1]];
787  auto& c = input_vertices[input_faces[f_id][2]];
788  if (old_nv.dot(((b - c).cross(a - c)).normalized()) < 0) {
789  is_filp = true;
790  break;
791  }
792  }
793  if (is_filp) continue;
794 
795  // check quality
796  double cos_a0_new = get_angle_cos(
797  input_vertices[v1_id],
798  input_vertices[n_v_ids[0]],
799  input_vertices[n_v_ids[1]]);
800  double cos_a1_new = get_angle_cos(
801  input_vertices[v2_id],
802  input_vertices[n_v_ids[0]],
803  input_vertices[n_v_ids[1]]);
804  if (std::min(cos_a0_new, cos_a1_new) <= std::min(cos_a0, cos_a1)) continue;
805 
806  if (is_out_envelope(
807  {{input_vertices[v1_id], input_vertices[n_v_ids[0]], input_vertices[n_v_ids[1]]}},
808  tree) ||
810  {{input_vertices[v2_id], input_vertices[n_v_ids[0]], input_vertices[n_v_ids[1]]}},
811  tree)) {
812  continue;
813  }
814 
815  // real update
816  for (int j = 0; j < 3; j++) {
817  if (input_faces[n12_f_ids[0]][j] == v2_id) input_faces[n12_f_ids[0]][j] = n_v_ids[1];
818  if (input_faces[n12_f_ids[1]][j] == v1_id) input_faces[n12_f_ids[1]][j] = n_v_ids[0];
819  }
820  conn_fs[v1_id].erase(n12_f_ids[1]);
821  conn_fs[v2_id].erase(n12_f_ids[0]);
822  conn_fs[n_v_ids[0]].insert(n12_f_ids[1]);
823  conn_fs[n_v_ids[1]].insert(n12_f_ids[0]);
824  cnt++;
825  }
826 
827  logger().trace("{} faces are swapped!!", cnt);
828  return;
829 }
830 
831 
833  std::vector<Vector3>& input_vertices,
834  std::vector<Vector3i>& input_faces,
835  const AABBWrapper& tree,
836  const double duplicate_tol)
837 {
838  remove_duplicates(input_vertices, input_faces, duplicate_tol);
839 
840  std::vector<bool> v_is_removed(input_vertices.size(), false);
841  std::vector<bool> f_is_removed(input_faces.size(), false);
842 
843  std::vector<std::unordered_set<int>> conn_fs(input_vertices.size());
844 
845  for (int i = 0; i < input_faces.size(); i++) {
846  for (int j = 0; j < 3; j++) conn_fs[input_faces[i][j]].insert(i);
847  }
848 
849  double collapsing_time, swapping_time, cleanup_time;
850 
851  {
852  POLYSOLVE_SCOPED_STOPWATCH("Collapsing", collapsing_time, logger());
853  collapsing(input_vertices, input_faces, tree, v_is_removed, f_is_removed, conn_fs);
854  }
855 
856  {
857  POLYSOLVE_SCOPED_STOPWATCH("Swapping", swapping_time, logger());
858  swapping(input_vertices, input_faces, tree, v_is_removed, f_is_removed, conn_fs);
859  }
860 
861  // clean up vs, fs, v
862  {
863  POLYSOLVE_SCOPED_STOPWATCH("Cleanup", cleanup_time, logger());
864  std::vector<int> map_v_ids(input_vertices.size(), -1);
865  int cnt = 0;
866  for (int i = 0; i < input_vertices.size(); i++) {
867  if (v_is_removed[i] || conn_fs[i].empty()) continue;
868  map_v_ids[i] = cnt;
869  cnt++;
870  }
871 
872  std::vector<Vector3> new_input_vertices(cnt);
873  cnt = 0;
874  for (int i = 0; i < input_vertices.size(); i++) {
875  if (v_is_removed[i] || conn_fs[i].empty()) continue;
876  new_input_vertices[cnt++] = input_vertices[i];
877  }
878  input_vertices = new_input_vertices;
879 
880  // f
881  cnt = 0;
882  for (int i = 0; i < input_faces.size(); i++) {
883  if (f_is_removed[i]) continue;
884  for (int j = 0; j < 3; j++) input_faces[i][j] = map_v_ids[input_faces[i][j]];
885  cnt++;
886  }
887 
888  std::vector<Vector3i> new_input_faces(cnt);
889  cnt = 0;
890  for (int i = 0; i < input_faces.size(); i++) {
891  if (f_is_removed[i]) continue;
892  new_input_faces[cnt] = input_faces[i];
893  cnt++;
894  }
895  input_faces = new_input_faces;
896  }
897 
898  remove_duplicates(input_vertices, input_faces, duplicate_tol);
899 
900  logger().debug("#v = {}", input_vertices.size());
901  logger().debug("#f = {}", input_faces.size());
902 
903  nlohmann::json out;
904  out["collapsing_time"] = collapsing_time;
905  out["swapping_time"] = swapping_time;
906  out["cleanup_time"] = cleanup_time;
907 
908  return out;
909 }
910 
911 std::tuple<std::shared_ptr<TriMesh>, nlohmann::json> tetwild_simplification(
912  const TriMesh& mesh,
913  const std::string& postion_attr_name,
914  double main_eps,
915  bool relative,
916  double duplicate_tol,
917  bool use_sampling)
918 {
919  using namespace internal;
920 
922  mesh.serialize(writer);
923 
924  Eigen::MatrixXd V;
925  Eigen::MatrixX<int64_t> F;
926 
927  writer.get_double_matrix(postion_attr_name, PrimitiveType::Vertex, V);
928  writer.get_FV_matrix(F);
929 
930  Eigen::VectorXd bmin(V.cols());
931  bmin.setConstant(std::numeric_limits<double>::max());
932  Eigen::VectorXd bmax(V.cols());
933  bmax.setConstant(std::numeric_limits<double>::lowest());
934 
935  std::vector<Eigen::Vector3d> vertices(V.rows());
936  std::vector<Eigen::Vector3i> faces(F.rows());
937  for (int64_t i = 0; i < V.rows(); i++) {
938  vertices[i] = V.row(i);
939  for (int64_t d = 0; d < bmax.size(); ++d) {
940  bmin[d] = std::min(bmin[d], vertices[i][d]);
941  bmax[d] = std::max(bmax[d], vertices[i][d]);
942  }
943  }
944  for (int64_t i = 0; i < F.rows(); i++) faces[i] = F.row(i).cast<int>();
945 
946  const double bbdiag = (bmax - bmin).norm();
947 
948  if (relative) main_eps *= bbdiag;
949 
950  if (duplicate_tol < 0) duplicate_tol = SCALAR_ZERO;
951  if (relative) duplicate_tol *= bbdiag;
952 
953  const double envelope_size = 2 * main_eps * (9 - 2 * sqrt(3)) / 25.0;
954  const double sampling_dist = (8.0 / 15.0) * main_eps;
955 
956  AABBWrapper tree(vertices, faces, envelope_size, sampling_dist, use_sampling);
957 
958  auto out = simplify(vertices, faces, tree, duplicate_tol);
959 
960  V.resize(vertices.size(), 3);
961  F.resize(faces.size(), 3);
962  for (int64_t i = 0; i < V.rows(); i++) V.row(i) = vertices[i];
963  for (int64_t i = 0; i < F.rows(); i++) F.row(i) = faces[i].cast<int64_t>();
964 
965  auto res = std::make_shared<TriMesh>();
966  res->initialize(F);
967  mesh_utils::set_matrix_attribute(V, postion_attr_name, PrimitiveType::Vertex, *res);
968 
969  return {res, out};
970 }
971 
972 
973 } // namespace wmtk::components
void serialize(MeshWriter &writer, const Mesh *local_root=nullptr) const
Definition: Mesh.cpp:92
bool is_out_using_sampling(const std::array< Vector3, 3 > &vs) const
bool is_out_envelope(const SimpleBVH::VectorMax3d &p, int &prev_facet) const
AABBWrapper(const std::vector< Eigen::Vector3d > &vertices, const std::vector< Eigen::Vector3i > &faces, double envelope_size, double sampling_dist, bool use_sampling)
bool is_out(const std::array< Vector3, 3 > &triangle) const
std::vector< std::array< SimpleBVH::VectorMax3d, 3 > > m_facets
std::shared_ptr< SimpleBVH::BVH > m_bvh
std::shared_ptr< fastEnvelope::FastEnvelope > m_envelope
ElementInQueue(const std::array< int, 2 > &ids, double w)
static void shuffle(std::vector< T > &vec)
static int next(int min, int max)
void get_FV_matrix(MatrixX< int64_t > &matrix)
void get_double_matrix(const std::string &name, const PrimitiveType type, MatrixX< double > &matrix)
void collapsing(std::vector< Vector3 > &input_vertices, std::vector< Vector3i > &input_faces, const AABBWrapper &tree, std::vector< bool > &v_is_removed, std::vector< bool > &f_is_removed, std::vector< std::unordered_set< int >> &conn_fs)
void swapping(std::vector< Vector3 > &input_vertices, std::vector< Vector3i > &input_faces, const AABBWrapper &tree, std::vector< bool > &v_is_removed, std::vector< bool > &f_is_removed, std::vector< std::unordered_set< int >> &conn_fs)
void vector_unique(std::vector< T > &v)
std::tuple< std::shared_ptr< TriMesh >, nlohmann::json > tetwild_simplification(const TriMesh &mesh, const std::string &postion_attr_name, double main_eps, bool relative, double duplicate_tol, bool use_sampling)
Perform the simplification from the original tetwild.
double get_angle_cos(const Vector3 &p, const Vector3 &p1, const Vector3 &p2)
void set_intersection(const std::unordered_set< int > &s1, const std::unordered_set< int > &s2, std::vector< int > &v)
nlohmann::json simplify(std::vector< Vector3 > &input_vertices, std::vector< Vector3i > &input_faces, const AABBWrapper &tree, const double duplicate_tol)
bool is_out_envelope(const std::array< Vector3, 3 > &vs, const AABBWrapper &tree)
void remove_duplicates(std::vector< Vector3 > &input_vertices, std::vector< Vector3i > &input_faces, const double duplicate_tol)
attribute::MeshAttributeHandle set_matrix_attribute(const Mat &data, const std::string &name, const PrimitiveType &type, Mesh &mesh)
Definition: mesh_utils.hpp:9
std::vector< Tuple > vertices(const Mesh &m, const Simplex &simplex)
std::vector< Tuple > edges(const Mesh &m, const Simplex &simplex)
SimplexCollection faces(const Mesh &mesh, const Simplex &simplex, const bool sort_and_clean)
Returns all faces of a simplex.
Definition: faces.cpp:10
spdlog::logger & logger()
Retrieves the current logger.
Definition: Logger.cpp:58
Vector< double, 3 > Vector3d
Definition: Types.hpp:39
bool operator()(const ElementInQueue &e1, const ElementInQueue &e2)
bool operator()(const ElementInQueue &e1, const ElementInQueue &e2)
nlohmann::json json
Definition: input.cpp:9