Wildmeshing Toolkit
Loading...
Searching...
No Matches
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
37namespace wmtk::components {
38
39using Vector3 = Eigen::Vector3d;
40using Vector3i = Eigen::Vector3i;
41
42
43class Random
44{
45public:
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
67inline int mod3(int j)
68{
69 return j % 3;
70}
71
73{
74public:
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
247private:
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{
258public:
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
269struct 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
278struct 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
288void 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
365template <typename T>
366void 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
372bool is_out_envelope(const std::array<Vector3, 3>& vs, const AABBWrapper& tree)
373{
374 return tree.is_out(vs);
375}
376
377double 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
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
832nlohmann::json simplify(
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
911std::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{
920 mesh.serialize(writer);
921
922 Eigen::MatrixXd V;
923 Eigen::MatrixX<int64_t> F;
924
925 writer.get_double_matrix(postion_attr_name, PrimitiveType::Vertex, V);
926 writer.get_FV_matrix(F);
927
928 Eigen::VectorXd bmin(V.cols());
929 bmin.setConstant(std::numeric_limits<double>::max());
930 Eigen::VectorXd bmax(V.cols());
931 bmax.setConstant(std::numeric_limits<double>::lowest());
932
933 std::vector<Eigen::Vector3d> vertices(V.rows());
934 std::vector<Eigen::Vector3i> faces(F.rows());
935 for (int64_t i = 0; i < V.rows(); i++) {
936 vertices[i] = V.row(i);
937 for (int64_t d = 0; d < bmax.size(); ++d) {
938 bmin[d] = std::min(bmin[d], vertices[i][d]);
939 bmax[d] = std::max(bmax[d], vertices[i][d]);
940 }
941 }
942 for (int64_t i = 0; i < F.rows(); i++) faces[i] = F.row(i).cast<int>();
943
944 const double bbdiag = (bmax - bmin).norm();
945
946 if (relative) main_eps *= bbdiag;
947
948 if (duplicate_tol < 0) duplicate_tol = SCALAR_ZERO;
949 if (relative) duplicate_tol *= bbdiag;
950
951 const double envelope_size = 2 * main_eps * (9 - 2 * sqrt(3)) / 25.0;
952 const double sampling_dist = (8.0 / 15.0) * main_eps;
953
954 AABBWrapper tree(vertices, faces, envelope_size, sampling_dist, use_sampling);
955
956 auto out = simplify(vertices, faces, tree, duplicate_tol);
957
958 V.resize(vertices.size(), 3);
959 F.resize(faces.size(), 3);
960 for (int64_t i = 0; i < V.rows(); i++) V.row(i) = vertices[i];
961 for (int64_t i = 0; i < F.rows(); i++) F.row(i) = faces[i].cast<int64_t>();
962
963 auto res = std::make_shared<TriMesh>();
964 res->initialize(F);
965 mesh_utils::set_matrix_attribute(V, postion_attr_name, PrimitiveType::Vertex, *res);
966
967 return {res, out};
968}
969
970
971} // namespace wmtk::components
void serialize(MeshWriter &writer, const Mesh *local_root=nullptr) const
Definition Mesh.cpp:93
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)
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
bool operator()(const ElementInQueue &e1, const ElementInQueue &e2)
bool operator()(const ElementInQueue &e1, const ElementInQueue &e2)