Wildmeshing Toolkit
axis_aligned_fusion.cpp
Go to the documentation of this file.
2 
3 #include <wmtk/Mesh.hpp>
4 #include <wmtk/TetMesh.hpp>
5 #include <wmtk/TriMesh.hpp>
6 
7 #include <wmtk/Types.hpp>
9 #include <wmtk/utils/Logger.hpp>
11 
13 
14 
16 
17 std::shared_ptr<Mesh>
18 axis_aligned_fusion(const Mesh& mesh, const std::vector<bool>& operating_axis, double eps)
19 {
20  // get mesh dimension and checks
21  int64_t mesh_dim = mesh.top_cell_dimension();
22 
23 
24  std::map<std::string, std::vector<int64_t>> names;
25 
26  // TODO: a lot of matrix copies to remove
27 
28  switch (mesh_dim) {
29  case (2): {
30  if (operating_axis[2]) {
31  wmtk::logger().warn("Fusion on Z axis is not supported for 2D mesh.");
32  }
33 
37  mesh.serialize(writer);
38 
39  writer.get_position_matrix(V);
40  writer.get_FV_matrix(FV);
41 
42  assert(V.rows() == mesh.get_all(PrimitiveType::Vertex).size());
43  assert(FV.rows() == mesh.get_all(PrimitiveType::Triangle).size());
44 
45  // rescale to [0, 1]
46  Eigen::MatrixXd V_rescale(V.rows(), V.cols());
47 
48  Eigen::VectorXd min_pos = V.colwise().minCoeff();
49 
50  // if (abs(min_pos[2] - 0.0) > eps)
51  // throw std::runtime_error("has non-zero z coordinate on 2d mesh");
52 
53  for (int64_t i = 0; i < V_rescale.rows(); ++i) {
54  V_rescale.row(i) = V.row(i) - min_pos.transpose();
55  }
56 
57  Eigen::VectorXd max_pos = V_rescale.colwise().maxCoeff();
58  for (int64_t i = 0; i < V_rescale.rows(); ++i) {
59  V_rescale(i, 0) /= max_pos[0];
60  V_rescale(i, 1) /= max_pos[1];
61  }
62 
63 
64  // debug code
65  // std::cout << FV << std::endl;
66  // std::cout << V << std::endl;
67 
68  std::map<int64_t, int64_t> vertex_map;
69 
70  std::array<std::vector<std::pair<int64_t, Eigen::VectorXd>>, 2> vertices_on_zero;
71  std::array<std::vector<std::pair<int64_t, Eigen::VectorXd>>, 2> vertices_on_one;
72 
73  int64_t v00 = -1;
74  int64_t v01 = -1;
75  int64_t v10 = -1;
76  int64_t v11 = -1;
77 
78  for (int64_t i = 0; i < V_rescale.rows(); ++i) {
79  const auto& pos = V_rescale.row(i);
80 
81  if (abs(pos[0] - 0.0) < eps) {
82  vertices_on_zero[0].emplace_back(std::make_pair(i, pos));
83  }
84  if (abs(pos[1] - 0.0) < eps) {
85  vertices_on_zero[1].emplace_back(std::make_pair(i, pos));
86  }
87  if (abs(pos[0] - 1.0) < eps) {
88  vertices_on_one[0].emplace_back(std::make_pair(i, pos));
89  }
90  if (abs(pos[1] - 1.0) < eps) {
91  vertices_on_one[1].emplace_back(std::make_pair(i, pos));
92  }
93 
94  // corners
95  if (abs(pos[0] - 0.0) < eps && abs(pos[1] - 0.0) < eps) {
96  if (v00 != -1) throw std::runtime_error("More than 1 vertices on corner 00.");
97  v00 = i;
98  }
99  if (abs(pos[0] - 0.0) < eps && abs(pos[1] - 1.0) < eps) {
100  if (v01 != -1) throw std::runtime_error("More than 1 vertices on corner 01.");
101  v01 = i;
102  }
103  if (abs(pos[0] - 1.0) < eps && abs(pos[1] - 0.0) < eps) {
104  if (v10 != -1) throw std::runtime_error("More than 1 vertices on corner 10.");
105  v10 = i;
106  }
107  if (abs(pos[0] - 1.0) < eps && abs(pos[1] - 1.0) < eps) {
108  if (v11 != -1) throw std::runtime_error("More than 1 vertices on corner 11.");
109  v11 = i;
110  }
111  }
112 
113  // merge vertices
114  for (int axis = 0; axis < 2; ++axis) {
115  if (!operating_axis[axis]) continue;
116 
117  if (vertices_on_zero[axis].size() != vertices_on_one[axis].size()) {
118  throw std::runtime_error("vertices size on the fusion axis does not match!");
119  }
120 
121  auto cmp = [&](const std::pair<int64_t, Eigen::VectorXd>& a,
122  const std::pair<int64_t, Eigen::VectorXd>& b) {
123  return a.second[1 - axis] < b.second[1 - axis];
124  };
125 
126  std::sort(vertices_on_zero[axis].begin(), vertices_on_zero[axis].end(), cmp);
127  std::sort(vertices_on_one[axis].begin(), vertices_on_one[axis].end(), cmp);
128 
129  for (int64_t i = 0; i < vertices_on_zero[axis].size(); ++i) {
130  assert(
131  abs(vertices_on_zero[axis][i].second[1 - axis] -
132  vertices_on_one[axis][i].second[1 - axis]) < eps);
133 
134  vertex_map[vertices_on_one[axis][i].first] = vertices_on_zero[axis][i].first;
135  }
136  }
137 
138  // special case for fusion all axis
139  if (operating_axis[0] && operating_axis[1]) {
140  vertex_map[v00] = v00;
141  vertex_map[v01] = v00;
142  vertex_map[v10] = v00;
143  vertex_map[v11] = v00;
144  }
145 
146  // create periodic mesh
147  RowVectors3l FV_new(FV.rows(), 3);
148 
149  for (int64_t i = 0; i < FV.rows(); ++i) {
150  for (int64_t k = 0; k < 3; ++k) {
151  if (vertex_map.find(FV(i, k)) != vertex_map.end()) {
152  FV_new(i, k) = vertex_map[FV(i, k)];
153  } else {
154  FV_new(i, k) = FV(i, k);
155  }
156  }
157  }
158 
159  Eigen::MatrixXd V_new_tmp(V.rows(), V.cols());
160 
161  // remove unused vertices
162  std::map<int64_t, int64_t> v_consolidate_map;
163 
164  int64_t v_valid_cnt = 0;
165  for (int64_t i = 0; i < V.rows(); ++i) {
166  if (vertex_map.find(i) == vertex_map.end() || vertex_map[i] == i) {
167  for (int k = 0; k < V_rescale.cols(); ++k) {
168  V_new_tmp(v_valid_cnt, k) = V(i, k);
169  }
170  v_consolidate_map[i] = v_valid_cnt++;
171 
172  // std::cout << V_new_tmp << std::endl << std::endl;
173  }
174  }
175 
176  Eigen::MatrixXd V_new(v_valid_cnt, V.cols());
177  V_new = V_new_tmp.block(0, 0, v_valid_cnt, V.cols());
178 
179  // std::cout << V_new << std::endl << std::endl;
180 
181 
182  // update FV_new
183  for (int64_t i = 0; i < FV_new.rows(); ++i) {
184  for (int64_t k = 0; k < FV_new.cols(); ++k) {
185  FV_new(i, k) = v_consolidate_map[FV_new(i, k)];
186  }
187  }
188 
189 
190  TriMesh fusion_mesh;
191  fusion_mesh.initialize(FV_new);
192  // mesh_utils::set_matrix_attribute(V_new, "vertices", PrimitiveType::Vertex, fusion_mesh);
193 
194  // TriMesh& m = dynamic_cast<TriMesh&>(*mesh);
195 
196  // make a copy
197  // TriMesh child_mesh(std::move(m));
198  // std::shared_ptr<TriMesh> child_ptr = std::make_shared<TriMesh>(std::move(m));
199 
200  TriMesh position_mesh;
201  position_mesh.initialize(FV);
203  V_rescale,
204  "vertices",
206  position_mesh);
207 
208  // std::shared_ptr<TriMesh> child_ptr = std::make_shared<TriMesh>(std::move(position_mesh));
209 
210  // auto child_map = multimesh::same_simplex_dimension_bijection(fusion_mesh, *child_ptr);
211 
212 
213  // fusion_mesh.register_child_mesh(child_ptr, child_map);
214 
215  // names["periodic"] = fusion_mesh.absolute_multi_mesh_id();
216  // names["position"] = child_ptr->absolute_multi_mesh_id();
217 
218  // cache.write_mesh(fusion_mesh, options.name, names);
219 
220  // break;
221 
222  std::shared_ptr<TriMesh> child_ptr = std::make_shared<TriMesh>(std::move(position_mesh));
223  std::shared_ptr<TriMesh> parent_ptr = std::make_shared<TriMesh>(std::move(fusion_mesh));
224 
225  auto child_map = wmtk::multimesh::same_simplex_dimension_bijection(*parent_ptr, *child_ptr);
226  parent_ptr->register_child_mesh(child_ptr, child_map);
227 
228  return parent_ptr;
229  }
230  case (3): {
231  MatrixX<double> V;
232  MatrixX<int64_t> TV;
233 
235  mesh.serialize(writer);
236 
237  writer.get_position_matrix(V);
238  writer.get_TV_matrix(TV);
239 
240  assert(V.rows() == mesh.get_all(PrimitiveType::Vertex).size());
241  assert(TV.rows() == mesh.get_all(PrimitiveType::Tetrahedron).size());
242 
243  // rescale to [0, 1]
244  RowVectors3d V_rescale(V.rows(), 3);
245 
246  Vector3d min_pos = V.colwise().minCoeff();
247 
248  for (int64_t i = 0; i < V_rescale.rows(); ++i) {
249  V_rescale.row(i) = V.row(i) - min_pos.transpose();
250  }
251 
252  Vector3d max_pos = V_rescale.colwise().maxCoeff();
253  for (int64_t i = 0; i < V_rescale.rows(); ++i) {
254  V_rescale(i, 0) /= max_pos[0];
255  V_rescale(i, 1) /= max_pos[1];
256  V_rescale(i, 2) /= max_pos[2];
257  }
258 
259  std::map<int64_t, int64_t> vertex_map;
260 
261  std::array<std::vector<std::pair<int64_t, Eigen::VectorXd>>, 3> vertices_on_zero;
262  std::array<std::vector<std::pair<int64_t, Eigen::VectorXd>>, 3> vertices_on_one;
263 
264  for (int64_t i = 0; i < V_rescale.rows(); ++i) {
265  const auto& pos = V_rescale.row(i);
266 
267  for (int k = 0; k < 3; ++k) {
268  if (abs(pos[k] - 0.0) < eps) {
269  vertices_on_zero[k].push_back(std::make_pair(i, pos));
270  }
271  if (abs(pos[k] - 1.0) < eps) {
272  vertices_on_one[k].push_back(std::make_pair(i, pos));
273  }
274  }
275  }
276 
277  // merge vertices
278  for (int axis = 0; axis < 3; ++axis) {
279  if (!operating_axis[axis]) continue;
280 
281  if (vertices_on_zero[axis].size() != vertices_on_one[axis].size()) {
282  throw std::runtime_error("vertices size on the fusion axis does not match!");
283  }
284 
285  auto cmp = [&](const std::pair<int64_t, Eigen::VectorXd>& a,
286  const std::pair<int64_t, Eigen::VectorXd>& b) {
287  if (abs(a.second[(axis + 2) % 3] - b.second[(axis + 2) % 3]) < eps) {
288  return a.second[(axis + 1) % 3] < b.second[(axis + 1) % 3];
289  } else {
290  return a.second[(axis + 2) % 3] < b.second[(axis + 2) % 3];
291  }
292  };
293 
294  std::sort(vertices_on_zero[axis].begin(), vertices_on_zero[axis].end(), cmp);
295  std::sort(vertices_on_one[axis].begin(), vertices_on_one[axis].end(), cmp);
296 
297  for (int64_t i = 0; i < vertices_on_zero[axis].size(); ++i) {
298  assert(
299  abs(vertices_on_zero[axis][i].second[(axis + 1) % 3] -
300  vertices_on_one[axis][i].second[(axis + 1) % 3]) < eps &&
301  abs(vertices_on_zero[axis][i].second[(axis + 2) % 3] -
302  vertices_on_one[axis][i].second[(axis + 2) % 3]) < eps);
303 
304  vertex_map[vertices_on_one[axis][i].first] = vertices_on_zero[axis][i].first;
305  }
306  }
307 
308  // // create periodic mesh
309  RowVectors4l TV_new(TV.rows(), 4);
310 
311  for (int64_t i = 0; i < TV.rows(); ++i) {
312  for (int64_t k = 0; k < 4; ++k) {
313  if (vertex_map.find(TV(i, k)) != vertex_map.end()) {
314  int64_t v_root = vertex_map[TV(i, k)];
315  while (vertex_map.find(v_root) != vertex_map.end() &&
316  vertex_map[v_root] != v_root) {
317  v_root = vertex_map[v_root];
318  }
319  TV_new(i, k) = v_root;
320  } else {
321  TV_new(i, k) = TV(i, k);
322  }
323  }
324  }
325 
326  // std::cout << TV_new << std::endl << std::endl;
327 
328 
329  RowVectors3d V_new_tmp(V.rows(), 3);
330 
331  // remove unused vertices
332  std::map<int64_t, int64_t> v_consolidate_map;
333 
334  int64_t v_valid_cnt = 0;
335  for (int64_t i = 0; i < V.rows(); ++i) {
336  if (vertex_map.find(i) == vertex_map.end() || vertex_map[i] == i) {
337  V_new_tmp(v_valid_cnt, 0) = V(i, 0);
338  V_new_tmp(v_valid_cnt, 1) = V(i, 1);
339  V_new_tmp(v_valid_cnt, 2) = V(i, 2);
340  v_consolidate_map[i] = v_valid_cnt++;
341 
342  // std::cout << V_new_tmp << std::endl << std::endl;
343  }
344  }
345 
346  RowVectors3d V_new(v_valid_cnt, 3);
347  V_new = V_new_tmp.block(0, 0, v_valid_cnt, 3);
348 
349  // std::cout << V_new << std::endl << std::endl;
350 
351 
352  // update TV_new
353  for (int64_t i = 0; i < TV_new.rows(); ++i) {
354  for (int64_t k = 0; k < TV_new.cols(); ++k) {
355  TV_new(i, k) = v_consolidate_map[TV_new(i, k)];
356  }
357  }
358 
359  // std::cout << TV_new << std::endl << std::endl;
360 
361 
362  TetMesh fusion_mesh;
363  fusion_mesh.initialize(TV_new);
364  // mesh_utils::set_matrix_attribute(V_new, "vertices", PrimitiveType::Vertex, fusion_mesh);
365 
366  TetMesh position_mesh;
367  position_mesh.initialize(TV);
369  V_rescale,
370  "vertices",
372  position_mesh);
373 
374  std::shared_ptr<TetMesh> child_ptr = std::make_shared<TetMesh>(std::move(position_mesh));
375  std::shared_ptr<TetMesh> parent_ptr = std::make_shared<TetMesh>(std::move(fusion_mesh));
376 
377  auto child_map = wmtk::multimesh::same_simplex_dimension_bijection(*parent_ptr, *child_ptr);
378  parent_ptr->register_child_mesh(child_ptr, child_map);
379 
380  return parent_ptr;
381  }
382  default: {
383  throw std::runtime_error("mesh dimension not supported");
384  }
385  }
386  return {};
387 }
388 } // namespace wmtk::components::multimesh
void serialize(MeshWriter &writer, const Mesh *local_root=nullptr) const
Definition: Mesh.cpp:93
std::vector< Tuple > get_all(PrimitiveType type) const
Generate a vector of Tuples from global vertex/edge/triangle/tetrahedron index.
Definition: Mesh.cpp:18
int64_t top_cell_dimension() const
Definition: Mesh.hpp:993
void initialize(Eigen::Ref< const RowVectors4l > TV, Eigen::Ref< const RowVectors6l > TE, Eigen::Ref< const RowVectors4l > TF, Eigen::Ref< const RowVectors4l > TT, Eigen::Ref< const VectorXl > VT, Eigen::Ref< const VectorXl > ET, Eigen::Ref< const VectorXl > FT)
Definition: TetMesh.cpp:79
void initialize(Eigen::Ref< const RowVectors3l > FV, Eigen::Ref< const RowVectors3l > FE, Eigen::Ref< const RowVectors3l > FF, Eigen::Ref< const VectorXl > VF, Eigen::Ref< const VectorXl > EF)
Definition: TriMesh.cpp:176
void get_position_matrix(MatrixX< double > &matrix)
void get_TV_matrix(MatrixX< int64_t > &matrix)
void get_FV_matrix(MatrixX< int64_t > &matrix)
std::shared_ptr< Mesh > axis_aligned_fusion(const Mesh &mesh, const std::vector< bool > &operating_axis, double eps)
attribute::MeshAttributeHandle set_matrix_attribute(const Mat &data, const std::string &name, const PrimitiveType &type, Mesh &mesh)
Definition: mesh_utils.hpp:9
std::vector< std::array< Tuple, 2 > > same_simplex_dimension_bijection(const Mesh &parent, const Mesh &child)
int cmp(const Rational &r, const Rational &r1)
Definition: Rational.cpp:259
RowVectors< int64_t, 3 > RowVectors3l
Definition: Types.hpp:47
Rational abs(const Rational &r0)
Definition: Rational.cpp:328
RowVectors< int64_t, 4 > RowVectors4l
Definition: Types.hpp:48
spdlog::logger & logger()
Retrieves the current logger.
Definition: Logger.cpp:58
Vector< double, 3 > Vector3d
Definition: Types.hpp:39
RowVectors< double, 3 > RowVectors3d
Definition: Types.hpp:51
Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > MatrixX
Definition: Types.hpp:14