marching_tets.cpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. // This file is part of libigl, a simple c++ geometry processing library.
  2. //
  3. // Copyright (C) 2018 Francis Williams <[email protected]>
  4. //
  5. // This Source Code Form is subject to the terms of the Mozilla Public License
  6. // v. 2.0. If a copy of the MPL was not distributed with this file, You can
  7. // obtain one at http://mozilla.org/MPL/2.0/.
  8. #include "marching_tets.h"
  9. #include <unordered_map>
  10. #include <vector>
  11. #include <utility>
  12. #include <cstdint>
  13. #include <iostream>
  14. #include <algorithm>
  15. #include <utility>
  16. #include <cmath>
  17. template <typename DerivedTV,
  18. typename DerivedTT,
  19. typename DerivedS,
  20. typename DerivedSV,
  21. typename DerivedSF,
  22. typename DerivedJ,
  23. typename BCType>
  24. void igl::marching_tets(
  25. const Eigen::MatrixBase<DerivedTV>& TV,
  26. const Eigen::MatrixBase<DerivedTT>& TT,
  27. const Eigen::MatrixBase<DerivedS>& isovals,
  28. const typename DerivedS::Scalar isovalue,
  29. Eigen::PlainObjectBase<DerivedSV>& outV,
  30. Eigen::PlainObjectBase<DerivedSF>& outF,
  31. Eigen::PlainObjectBase<DerivedJ>& J,
  32. Eigen::SparseMatrix<BCType>& BC)
  33. {
  34. // We're hashing edges to deduplicate using 64 bit ints. The upper and lower
  35. // 32 bits of a key are the indices of vertices in the mesh. The implication is
  36. // that you can only have 2^32 vertices which I have deemed sufficient for
  37. // anything reasonable.
  38. const auto make_edge_key = [](const std::pair<int32_t, int32_t>& p) -> std::int64_t
  39. {
  40. std::int64_t ret = 0;
  41. ret |= p.first;
  42. ret |= static_cast<std::int64_t>(p.second) << 32;
  43. return ret;
  44. };
  45. const int mt_cell_lookup[16][4] =
  46. {
  47. { -1, -1, -1, -1 },
  48. { 0, 2, 1, -1 },
  49. { 0, 3, 4, -1 },
  50. { 2, 1, 3, 4 },
  51. { 5, 3, 1, -1 },
  52. { 0, 2, 5, 3 },
  53. { 0, 1, 5, 4 },
  54. { 2, 5, 4, -1 },
  55. { 4, 5, 2, -1 },
  56. { 0, 4, 5, 1 },
  57. { 0, 3, 5, 2 },
  58. { 1, 3, 5, -1 },
  59. { 4, 3, 1, 2 },
  60. { 0, 4, 3, -1 },
  61. { 0, 1, 2, -1 },
  62. { -1, -1, -1, -1 },
  63. };
  64. const int mt_edge_lookup[6][2] =
  65. {
  66. {0, 1},
  67. {0, 2},
  68. {0, 3},
  69. {1, 2},
  70. {1, 3},
  71. {2, 3},
  72. };
  73. // Store the faces and the tet they are in
  74. std::vector<std::pair<Eigen::RowVector3i, int>> faces;
  75. // Store the edges in the tet mesh which we add vertices on
  76. // so we can deduplicate
  77. std::vector<std::pair<int, int>> edge_table;
  78. assert(TT.cols() == 4 && TT.rows() >= 1);
  79. assert(TV.cols() == 3 && TV.rows() >= 4);
  80. assert(isovals.cols() == 1);
  81. // For each tet
  82. for (int i = 0; i < TT.rows(); i++)
  83. {
  84. uint8_t key = 0;
  85. for (int v = 0; v < 4; v++)
  86. {
  87. const int vid = TT(i, v);
  88. const uint8_t flag = isovals(vid, 0) > isovalue;
  89. key |= flag << v;
  90. }
  91. // This will contain the index in TV of each vertex in the tet
  92. int v_ids[4] = {-1, -1, -1, -1};
  93. // Insert any vertices if the tet intersects the level surface
  94. for (int e = 0; e < 4 && mt_cell_lookup[key][e] != -1; e++)
  95. {
  96. const int tv1_idx = TT(i, mt_edge_lookup[mt_cell_lookup[key][e]][0]);
  97. const int tv2_idx = TT(i, mt_edge_lookup[mt_cell_lookup[key][e]][1]);
  98. const int vertex_id = edge_table.size();
  99. edge_table.push_back(std::make_pair(std::min(tv1_idx, tv2_idx), std::max(tv1_idx, tv2_idx)));
  100. v_ids[e] = vertex_id;
  101. }
  102. // Insert the corresponding faces
  103. if (v_ids[0] != -1)
  104. {
  105. bool is_quad = mt_cell_lookup[key][3] != -1;
  106. if (is_quad)
  107. {
  108. const Eigen::RowVector3i f1(v_ids[0], v_ids[1], v_ids[3]);
  109. const Eigen::RowVector3i f2(v_ids[1], v_ids[2], v_ids[3]);
  110. faces.push_back(std::make_pair(f1, i));
  111. faces.push_back(std::make_pair(f2, i));
  112. }
  113. else
  114. {
  115. const Eigen::RowVector3i f(v_ids[0], v_ids[1], v_ids[2]);
  116. faces.push_back(std::make_pair(f, i));
  117. }
  118. }
  119. }
  120. int num_unique = 0;
  121. outV.resize(edge_table.size(), 3);
  122. outF.resize(faces.size(), 3);
  123. J.resize(faces.size());
  124. // Sparse matrix triplets for BC
  125. std::vector<Eigen::Triplet<BCType>> bc_triplets;
  126. bc_triplets.reserve(edge_table.size());
  127. // Deduplicate vertices
  128. std::unordered_map<std::int64_t, int> emap;
  129. emap.max_load_factor(0.5);
  130. emap.reserve(edge_table.size());
  131. for (int f = 0; f < faces.size(); f++)
  132. {
  133. const int ti = faces[f].second;
  134. assert(ti>=0);
  135. assert(ti<TT.rows());
  136. J(f) = ti;
  137. for (int v = 0; v < 3; v++)
  138. {
  139. const int vi = faces[f].first[v];
  140. const std::pair<int32_t, int32_t> edge = edge_table[vi];
  141. const std::int64_t key = make_edge_key(edge);
  142. auto it = emap.find(key);
  143. if (it == emap.end()) // New unique vertex, insert it
  144. {
  145. // Typedef to make sure we handle floats properly
  146. typedef Eigen::Matrix<typename DerivedTV::Scalar, 1, 3, Eigen::RowMajor, 1, 3> RowVector;
  147. using Scalar = typename DerivedS::Scalar;
  148. const RowVector v1 = TV.row(edge.first).template cast<Scalar>();
  149. const RowVector v2 = TV.row(edge.second).template cast<Scalar>();
  150. const Scalar a = abs(isovals(edge.first, 0) - isovalue);
  151. const Scalar b = abs(isovals(edge.second, 0) - isovalue);
  152. const Scalar w = a / (a+b);
  153. // Create a casted copy in case BCType is a float and we need to downcast
  154. const BCType bc_w = static_cast<BCType>(w);
  155. bc_triplets.push_back(Eigen::Triplet<BCType>(num_unique, edge.first, 1-bc_w));
  156. bc_triplets.push_back(Eigen::Triplet<BCType>(num_unique, edge.second, bc_w));
  157. // Create a casted copy in case DerivedTV::Scalar is a float and we need to downcast
  158. const typename DerivedTV::Scalar v_w = static_cast<typename DerivedTV::Scalar>(w);
  159. outV.row(num_unique) = (1-v_w)*v1 + v_w*v2;
  160. outF(f, v) = num_unique;
  161. emap.emplace(key, num_unique);
  162. num_unique += 1;
  163. } else {
  164. outF(f, v) = it->second;
  165. }
  166. }
  167. }
  168. outV.conservativeResize(num_unique, 3);
  169. BC.resize(num_unique, TV.rows());
  170. BC.setFromTriplets(bc_triplets.begin(), bc_triplets.end());
  171. }
  172. #ifdef IGL_STATIC_LIBRARY
  173. template void igl::marching_tets<Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, -1, 0, -1, -1>, Eigen::Matrix<int, -1, 1, 0, -1, 1>, double>(Eigen::MatrixBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> > const&, Eigen::MatrixBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> > const&, const double, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<int, -1, 1, 0, -1, 1> >&, Eigen::SparseMatrix<double, 0, int>&);
  174. #endif // IGL_STATIC_LIBRARY