389 std::vector<Vector3>& input_vertices,
390 std::vector<Vector3i>& input_faces,
391 const double duplicate_tol)
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];
398 Eigen::VectorXi IV, _;
399 igl::remove_duplicate_vertices(V_tmp, F_tmp, duplicate_tol, V_in, IV, _, F_in);
401 for (
int i = 0; i < F_in.rows(); i++) {
403 for (
int j = 1; j < 3; j++) {
404 if (F_in(i, j) < F_in(i, j_min)) j_min = j;
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;
414 igl::unique_rows(F_in, F_tmp, IF, _);
417 if (V_in.rows() == 0 || F_in.rows() == 0)
return;
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());
423 input_vertices.resize(V_in.rows());
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))
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)))
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));
437 if (area.norm() / 2 <= duplicate_tol)
continue;
438 input_faces.push_back(F_in.row(i));
443 std::vector<Vector3>& input_vertices,
444 std::vector<Vector3i>& input_faces,
446 std::vector<bool>& v_is_removed,
447 std::vector<bool>& f_is_removed,
448 std::vector<std::unordered_set<int>>& conn_fs)
451 std::vector<std::array<int, 2>>
edges;
452 tbb::concurrent_vector<std::array<int, 2>> edges_tbb;
454 const auto build_edges = [&]() {
456 edges.reserve(input_faces.size() * 3);
459 edges_tbb.reserve(input_faces.size() * 3);
461 tbb::parallel_for(
size_t(0), input_faces.size(), [&](
size_t f_id) {
462 if (f_is_removed[f_id]) return;
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);
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());
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;
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]);
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));
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]);
509 std::sort(v_ids.begin(), v_ids.end());
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;
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;
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;
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);
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);
538 Vector3 p = (input_vertices[v1_id] + input_vertices[v2_id]) / 2;
539 tree.project_to_sf(p);
549 for (
int f_id : new_f_ids) {
551 (input_vertices[input_faces[f_id][1]] - input_vertices[input_faces[f_id][2]])
553 input_vertices[input_faces[f_id][0]] -
554 input_vertices[input_faces[f_id][2]]);
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;
563 if (new_nv.norm() / 2 <= SCALAR_ZERO_2)
return FAIL_FLIP;
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 = {
575 input_vertices[input_faces[f_id][
mod3(j + 1)]],
576 input_vertices[input_faces[f_id][
mod3(j + 2)]]}};
585 std::vector<int> n_v_ids;
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]);
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;
599 for (
int j = 0; j < 3; j++) {
600 if (input_faces[f_id][j] != v1_id) {
601 conn_fs[input_faces[f_id][j]].erase(f_id);
606 for (
int f_id : conn_fs[v1_id]) {
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;
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));
627 std::atomic<int> cnt(0);
633 const int stopping = input_vertices.size() / 10000.;
635 std::vector<int> safe_set;
638 one_ring_edge_set(
edges, v_is_removed, f_is_removed, conn_fs, input_vertices, safe_set);
641 tbb::parallel_for(
size_t(0), safe_set.size(), [&](
size_t i) {
643 std::array<int, 2>& v_ids = edges[safe_set[i]];
648 std::vector<int> n12_f_ids;
649 set_intersection(conn_fs[v_ids[0]], conn_fs[v_ids[1]], n12_f_ids);
651 if (n12_f_ids.size() != 2) return;
654 int res = remove_an_edge(v_ids[0], v_ids[1], n12_f_ids);
655 if (res == SUC) cnt++;
660 tbb::parallel_for(
size_t(0), conn_fs.size(), [&](
size_t i) {
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);
669 for (
int f_id : r_f_ids) conn_fs[i].erase(f_id);
674 }
while (cnt > stopping);
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;
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())
691 std::vector<int> n12_f_ids;
693 if (n12_f_ids.size() != 2)
continue;
695 int res = remove_an_edge(v_ids[0], v_ids[1], n12_f_ids);
698 else if (res == FAIL_CLEAN)
700 else if (res == FAIL_FLIP)
702 else if (res == FAIL_ENV)
708 "{} success, {} fail, {} flip, {} out of envelope",
716 std::vector<Vector3>& input_vertices,
717 std::vector<Vector3i>& input_faces,
719 std::vector<bool>& v_is_removed,
720 std::vector<bool>& f_is_removed,
721 std::vector<std::unordered_set<int>>& conn_fs)
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]);
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();
740 sm_queue.push(
ElementInQueue(std::array<int, 2>({{e[1], e[0]}}), weight));
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];
749 std::vector<int> n12_f_ids;
751 if (n12_f_ids.size() != 2)
continue;
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];
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];
766 get_angle_cos(input_vertices[n_v_ids[0]], input_vertices[v1_id], input_vertices[v2_id]);
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();
776 if (cos_a0 > -0.999) {
777 if (old_nvs[0].dot(old_nvs[1]) < 1 - 1e-6)
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) {
793 if (is_filp)
continue;
797 input_vertices[v1_id],
798 input_vertices[n_v_ids[0]],
799 input_vertices[n_v_ids[1]]);
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;
807 {{input_vertices[v1_id], input_vertices[n_v_ids[0]], input_vertices[n_v_ids[1]]}},
810 {{input_vertices[v2_id], input_vertices[n_v_ids[0]], input_vertices[n_v_ids[1]]}},
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];
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]);
827 logger().trace(
"{} faces are swapped!!", cnt);
833 std::vector<Vector3>& input_vertices,
834 std::vector<Vector3i>& input_faces,
836 const double duplicate_tol)
840 std::vector<bool> v_is_removed(input_vertices.size(),
false);
841 std::vector<bool> f_is_removed(input_faces.size(),
false);
843 std::vector<std::unordered_set<int>> conn_fs(input_vertices.size());
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);
849 double collapsing_time, swapping_time, cleanup_time;
852 POLYSOLVE_SCOPED_STOPWATCH(
"Collapsing", collapsing_time,
logger());
853 collapsing(input_vertices, input_faces, tree, v_is_removed, f_is_removed, conn_fs);
857 POLYSOLVE_SCOPED_STOPWATCH(
"Swapping", swapping_time,
logger());
858 swapping(input_vertices, input_faces, tree, v_is_removed, f_is_removed, conn_fs);
863 POLYSOLVE_SCOPED_STOPWATCH(
"Cleanup", cleanup_time,
logger());
864 std::vector<int> map_v_ids(input_vertices.size(), -1);
866 for (
int i = 0; i < input_vertices.size(); i++) {
867 if (v_is_removed[i] || conn_fs[i].empty())
continue;
872 std::vector<Vector3> new_input_vertices(cnt);
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];
878 input_vertices = new_input_vertices;
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]];
888 std::vector<Vector3i> new_input_faces(cnt);
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];
895 input_faces = new_input_faces;
900 logger().debug(
"#v = {}", input_vertices.size());
901 logger().debug(
"#f = {}", input_faces.size());
904 out[
"collapsing_time"] = collapsing_time;
905 out[
"swapping_time"] = swapping_time;
906 out[
"cleanup_time"] = cleanup_time;
913 const std::string& postion_attr_name,
916 double duplicate_tol,
923 Eigen::MatrixX<int64_t> F;
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());
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++) {
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]);
942 for (int64_t i = 0; i < F.rows(); i++)
faces[i] = F.row(i).cast<
int>();
944 const double bbdiag = (bmax - bmin).norm();
946 if (relative) main_eps *= bbdiag;
948 if (duplicate_tol < 0) duplicate_tol = SCALAR_ZERO;
949 if (relative) duplicate_tol *= bbdiag;
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;
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>();
963 auto res = std::make_shared<TriMesh>();
965 mesh_utils::set_matrix_attribute(V, postion_attr_name, PrimitiveType::Vertex, *res);