Wildmeshing Toolkit
edge_insertion.cpp
Go to the documentation of this file.
1 #include "edge_insertion.hpp"
2 
4 #include <wmtk/utils/Logger.hpp>
5 #include <wmtk/utils/orient.hpp>
6 
9 
10 #include <wmtk/EdgeMesh.hpp>
11 #include <wmtk/Mesh.hpp>
12 #include <wmtk/TriMesh.hpp>
16 
17 #include <igl/edges.h>
18 #include <igl/remove_duplicate_vertices.h>
19 
20 #include <array>
21 #include <numeric>
22 #include <tuple>
23 #include <vector>
24 
26 
28 {
29  return a[0] * b[1] - a[1] * b[0];
30 }
31 
33  const wmtk::Vector2r& P,
34  const wmtk::Vector2r& A,
35  const wmtk::Vector2r& B,
36  const wmtk::Vector2r& C)
37 {
38  Vector2r AP = P - A;
39  Vector2r BP = P - B;
40  Vector2r CP = P - C;
41  Vector2r AB = B - A;
42  Vector2r BC = C - B;
43  Vector2r CA = A - C;
44 
45  auto S_pab = abs(det(AP, AB)) / 2;
46  auto S_pbc = abs(det(BP, BC)) / 2;
47  auto S_pca = abs(det(CP, CA)) / 2;
48  auto S_abc = abs(det(AB, -CA)) / 2;
49 
50  if (S_pab + S_pbc + S_pca != S_abc) {
51  // outside
52  return -1;
53  }
54 
55  if (S_pab * S_pbc * S_pca > 0) {
56  // inside
57  return 0;
58  }
59 
60  if (S_pab == 0) {
61  if (S_pbc * S_pca > 0) {
62  // on AB
63  return 1;
64  } else if (S_pca == 0) {
65  // is A
66  return 4;
67  } else {
68  // is B
69  return 5;
70  }
71  }
72 
73  if (S_pbc == 0) {
74  if (S_pca * S_pab > 0) {
75  // on BC
76  return 2;
77  } else if (S_pab == 0) {
78  // is B
79  return 5;
80  } else {
81  // is C
82  return 6;
83  }
84  }
85 
86  if (S_pca == 0) {
87  if (S_pab * S_pbc > 0) {
88  // on CA
89  return 3;
90  } else if (S_pbc == 0) {
91  // is C
92  return 6;
93  } else {
94  // is A
95  return 4;
96  }
97  }
98 
99  return -1;
100 }
101 
103  const Vector2r& s0,
104  const Vector2r& e0,
105  const Vector2r& s1,
106  const Vector2r& e1,
107  Vector2r& res,
108  Rational& t0,
109  Rational& t1)
110 {
111  Rational dd = e0[0] * e1[1] - e0[0] * s1[1] - e0[1] * e1[0] + e0[1] * s1[0] + e1[0] * s0[1] -
112  e1[1] * s0[0] + s0[0] * s1[1] - s0[1] * s1[0];
113 
114  if (dd.get_sign() == 0) {
115  return false;
116  }
117 
118  t0 = (e1[0] * s0[1] - e1[0] * s1[1] - e1[1] * s0[0] + e1[1] * s1[0] + s0[0] * s1[1] -
119  s0[1] * s1[0]) /
120  dd;
121  t1 = (e0[0] * s0[1] - e0[0] * s1[1] - e0[1] * s0[0] + e0[1] * s1[0] + s0[0] * s1[1] -
122  s0[1] * s1[0]) /
123  dd;
124 
125  if (t0 < 0 || t0 > 1 || t1 < 0 || t1 > 1) {
126  return false;
127  }
128 
129  res = (1 - t0) * s0 + t0 * e0;
130 #ifndef NDEBUG
131  const Vector2r p1 = (1 - t1) * s1 + t1 * e1;
132 
133  assert(res[0] == p1[0] && res[1] == p1[1]);
134 #endif
135  return true;
136 }
137 
138 bool is_point_in_bbox(const Vector2r& point, const Vector2r& bbox_min, const Vector2r& bbox_max)
139 {
140  if (point[0] >= bbox_min[0] && point[0] <= bbox_max[0] && point[1] >= bbox_min[1] &&
141  point[1] <= bbox_max[1]) {
142  return true;
143  }
144  return false;
145 }
146 
148  const Vector2r& bbox_min_0,
149  const Vector2r& bbox_max_0,
150  const Vector2r& bbox_min_1,
151  const Vector2r& bbox_max_1)
152 {
153  if (bbox_min_0[0] > bbox_max_1[0] || bbox_max_0[0] < bbox_min_1[0]) {
154  return false;
155  }
156  if (bbox_min_0[1] > bbox_max_1[1] || bbox_max_0[1] < bbox_min_1[1]) {
157  return false;
158  }
159  return true;
160 }
161 
162 std::array<wmtk::Vector2r, 2>
164 {
165  wmtk::Rational x_min, x_max, y_min, y_max;
166  if (p0[0] < p1[0]) {
167  x_min = p0[0];
168  x_max = p1[0];
169  } else {
170  x_min = p1[0];
171  x_max = p0[0];
172  }
173 
174  if (p0[1] < p1[1]) {
175  y_min = p0[1];
176  y_max = p1[1];
177  } else {
178  y_min = p1[1];
179  y_max = p0[1];
180  }
181 
182  x_min = (x_min > p2[0]) ? p2[0] : x_min;
183  x_max = (x_max < p2[0]) ? p2[0] : x_max;
184  y_min = (y_min > p2[1]) ? p2[1] : y_min;
185  y_max = (y_max < p2[1]) ? p2[1] : y_max;
186 
187  return {{wmtk::Vector2r(x_min, y_min), wmtk::Vector2r(x_max, y_max)}};
188 }
189 
190 std::array<wmtk::Vector2r, 2> compute_bbox(const wmtk::Vector2r& p0, const wmtk::Vector2r& p1)
191 {
192  wmtk::Rational x_min, x_max, y_min, y_max;
193  if (p0[0] < p1[0]) {
194  x_min = p0[0];
195  x_max = p1[0];
196  } else {
197  x_min = p1[0];
198  x_max = p0[0];
199  }
200 
201  if (p0[1] < p1[1]) {
202  y_min = p0[1];
203  y_max = p1[1];
204  } else {
205  y_min = p1[1];
206  y_max = p0[1];
207  }
208 
209  return {{wmtk::Vector2r(x_min, y_min), wmtk::Vector2r(x_max, y_max)}};
210 }
211 
212 bool is_colinear(const Vector2r& p0, const Vector2r& p1, const Vector2r& q0, const Vector2r& q1)
213 {
214  if (wmtk::utils::wmtk_orient2d(p0, p1, q0) == 0 &&
215  wmtk::utils::wmtk_orient2d(p0, p1, q1) == 0) {
216  return true;
217  }
218  return false;
219 }
220 
221 std::vector<int64_t> cyclic_order(const std::vector<Vector2r>& V)
222 {
223  constexpr static std::array<int, 4> __quadrants{{4, 1, 3, 2}};
224 
225  size_t size = V.size();
226  // sort indices by quadrant and cross product
227  std::vector<char> quadrants(size);
228  for (size_t i = 0; i < size; ++i) {
229  auto b = V[i];
230  // ++ +- -+ -- => 1 4 2 3
231  int64_t sign_x, sign_y;
232  sign_x = (b[0] < 0) ? 1 : 0;
233  sign_y = (b[1] < 0) ? 1 : 0;
234  quadrants[i] = __quadrants[2 * sign_y + sign_x];
235  }
236  // sort by quadrant and then by cross product volume
237  auto comp = [&](int64_t ai, int64_t bi) -> bool {
238  const char qa = quadrants[ai];
239  const char qb = quadrants[bi];
240  if (qa == qb) {
241  auto a = V[ai];
242  auto b = V[bi];
243  return b[0] * a[1] > a[0] * b[1];
244  } else {
245  return qa > qb;
246  }
247  };
248  // we need to sort D and quadrants simultaneously, easier to just sort
249  // indices into both.
250  std::vector<int64_t> ordered_indices(size);
251  // spit the initial indices of indices configuration
252  std::iota(ordered_indices.begin(), ordered_indices.end(), 0);
253  // sort the indices of indices
254  std::sort(ordered_indices.begin(), ordered_indices.end(), comp);
255  return ordered_indices;
256 }
257 
258 
260  int64_t v,
261  std::vector<std::vector<std::tuple<int64_t, bool, bool>>>& VV,
262  std::vector<Vector2r>& V_pos,
263  std::vector<int64_t>& stack,
264  std::vector<int>& in_stack,
265  std::vector<int>& is_on_input,
266  std::vector<int>& is_used,
267  std::vector<std::vector<int64_t>>& in_stack_idx,
268  const Vector2r& prev_vector,
269  const int64_t v_prev,
270  std::vector<std::array<int64_t, 3>>& FV,
271  std::vector<std::array<int, 3>>& local_e_on_input)
272 {
273  if (in_stack[v] == 1) {
274  // got a polygon, triangulate it
275  const int64_t begin_idx = in_stack_idx[v].back();
276  const int64_t end_idx = stack.size() - 1;
277 
278  bool new_poly = true;
279  for (int64_t i = begin_idx; i < end_idx; ++i) {
280  if (is_used[i]) {
281  new_poly = false;
282  break;
283  }
284  }
285 
286  if (new_poly) {
287  assert(end_idx - begin_idx >= 2);
288 
289  // set used
290  for (int64_t i = begin_idx; i < end_idx; ++i) {
291  is_used[i] = true;
292  }
293 
294  if (end_idx - begin_idx == 2) {
295  // triangle case
296  FV.push_back({{stack[begin_idx], stack[begin_idx + 1], stack[end_idx]}});
297  local_e_on_input.push_back(
298  {{is_on_input[begin_idx + 1], is_on_input[end_idx], is_on_input[begin_idx]}});
299  } else {
300  // polygon case
301  // add vertex in the center
302  Vector2r centroid(0, 0);
303  int64_t centroid_idx = V_pos.size();
304  for (int64_t i = begin_idx; i < end_idx + 1; ++i) {
305  centroid = centroid + V_pos[stack[i]];
306  int64_t next_stack_idx = (i == end_idx) ? begin_idx : i + 1;
307  FV.push_back({{stack[i], stack[next_stack_idx], centroid_idx}});
308  local_e_on_input.push_back({{0, 0, is_on_input[i]}});
309  }
310 
311  V_pos.push_back(centroid / double(end_idx - begin_idx + 1));
312  }
313 
314  return;
315  }
316  }
317 
318  stack.push_back(v);
319  in_stack[v] = 1;
320  in_stack_idx[v].push_back(stack.size() - 1);
321 
322  std::map<int64_t, int64_t> global_to_local_idx;
323  for (int64_t i = 0; i < VV[v].size(); ++i) {
324  global_to_local_idx[std::get<0>(VV[v][i])] = i;
325  }
326 
327 
328  std::vector<Vector2r> out_vec;
329  std::vector<int64_t> out_vertices;
330 
331  // debug use
332  std::vector<std::array<double, 2>> out_vec_double;
333  std::array<double, 2> prev_vec_double = {
334  {prev_vector[0].to_double(), prev_vector[1].to_double()}};
335 
336  // compute cyclic order (cw)
337  for (int64_t i = 0; i < VV[v].size(); ++i) {
338  if (std::get<0>(VV[v][i]) == v_prev) {
339  // skip prev vertex
340  continue;
341  }
342  out_vec.push_back(V_pos[std::get<0>(VV[v][i])] - V_pos[v]);
343  out_vec_double.push_back(
344  {{(V_pos[std::get<0>(VV[v][i])] - V_pos[v])[0].to_double(),
345  (V_pos[std::get<0>(VV[v][i])] - V_pos[v])[1].to_double()}});
346  out_vertices.push_back(std::get<0>(VV[v][i]));
347  }
348 
349  // also send prev_vec into sort
350  out_vec.push_back(prev_vector);
351  out_vertices.push_back(-1);
352 
353  int64_t base_idx = out_vec.size() - 1;
354 
355  auto cyclic_order_idx = cyclic_order(out_vec);
356 
357  // get all ccw vertices
358  std::vector<int64_t> ccw_vertices;
359  std::vector<Vector2r> ccw_vec;
360  std::vector<int64_t> ccw_local_idx;
361 
362  int64_t start_idx = -1;
363  for (int64_t i = 0; i < cyclic_order_idx.size(); ++i) {
364  if (cyclic_order_idx[i] == base_idx) {
365  start_idx = i;
366  break;
367  }
368  }
369  assert(start_idx > -1);
370 
371  // handle colinear out, should be at most one
372 
373  bool has_colinear_out = false;
374  int64_t colinear_out_idx = -1;
375  int64_t iter = (start_idx + 1) % out_vec.size();
376 
377  if (prev_vector[0] * out_vec[cyclic_order_idx[iter]][1] ==
378  prev_vector[1] * out_vec[cyclic_order_idx[iter]][0]) {
379  has_colinear_out = true;
380  colinear_out_idx = iter;
381  iter = (iter + 1) % out_vec.size();
382  }
383 
384  while (iter != start_idx) {
385  if (prev_vector[0] * out_vec[cyclic_order_idx[iter]][1] >=
386  prev_vector[1] * out_vec[cyclic_order_idx[iter]][0]) {
387  ccw_vertices.push_back(out_vertices[cyclic_order_idx[iter]]);
388  ccw_vec.push_back(out_vec[cyclic_order_idx[iter]]);
389  }
390  iter = (iter + 1) % out_vec.size();
391  }
392 
393  if (has_colinear_out) {
394  ccw_vertices.push_back(out_vertices[cyclic_order_idx[colinear_out_idx]]);
395  ccw_vec.push_back(out_vec[cyclic_order_idx[colinear_out_idx]]);
396  }
397 
398  // dfs on all ccw vertices
399 
400  for (int64_t i = 0; i < ccw_vertices.size(); ++i) {
401  if (!std::get<2>(VV[v][global_to_local_idx[ccw_vertices[i]]]) &&
402  ccw_vertices[i] != v_prev) {
403  is_on_input.push_back(
404  (std::get<1>(VV[v][global_to_local_idx[ccw_vertices[i]]])) ? 1 : 0);
405  is_used.push_back(0);
406  std::get<2>(VV[v][global_to_local_idx[ccw_vertices[i]]]) = true;
408  ccw_vertices[i],
409  VV,
410  V_pos,
411  stack,
412  in_stack,
413  is_on_input,
414  is_used,
415  in_stack_idx,
416  ccw_vec[i],
417  v,
418  FV,
419  local_e_on_input);
420  // std::get<2>(VV[v][ccw_vertices[i]]) = true;
421  is_on_input.pop_back();
422  is_used.pop_back();
423  }
424  }
425 
426  stack.pop_back();
427  in_stack[v] = 0;
428  in_stack_idx[v].pop_back();
429 }
430 
432  std::vector<std::vector<std::tuple<int64_t, bool, bool>>>& VV,
433  std::vector<Vector2r>& V_pos,
434  std::vector<std::array<int64_t, 3>>& FV,
435  std::vector<std::array<int, 3>>& local_e_on_input)
436 {
437  std::vector<int> in_stack(VV.size(), 0);
438  std::vector<std::vector<int64_t>> in_stack_idx(VV.size());
439 
440  std::vector<int64_t> stack;
441  std::vector<int> is_on_input;
442  std::vector<int> is_used;
443 
444  Vector2r prev_vector(1, 0);
445  int64_t v_cur = -1;
446  int64_t v_prev = -1;
447 
448  for (int64_t i = 0; i < VV.size(); ++i) {
449  stack.push_back(i);
450  in_stack[i] = 1;
451  in_stack_idx[i].push_back(stack.size() - 1);
452 
453  for (int64_t j = 0; j < VV[i].size(); ++j) {
454  if (!std::get<2>(VV[i][j])) {
455  is_on_input.push_back((std::get<1>(VV[i][j])) ? 1 : 0);
456  is_used.push_back(0);
457  std::get<2>(VV[i][j]) = true;
459  std::get<0>(VV[i][j]),
460  VV,
461  V_pos,
462  stack,
463  in_stack,
464  is_on_input,
465  is_used,
466  in_stack_idx,
467  V_pos[std::get<0>(VV[i][j])] - V_pos[i],
468  i,
469  FV,
470  local_e_on_input);
471  // std::get<2>(VV[i][j]) = true;
472  is_on_input.pop_back();
473  is_used.pop_back();
474  }
475  }
476 
477  stack.pop_back();
478  in_stack[i] = 0;
479  in_stack_idx[i].pop_back();
480  }
481 }
482 
484  TriMesh& _trimesh,
485  EdgeMesh& edgemesh,
486  std::vector<Vector2r>& v_final,
487  std::vector<std::array<int64_t, 3>>& FV_new,
488  std::vector<std::array<int, 3>>& local_e_on_input)
489 {
490  TriMesh trimesh(
491  std::move(_trimesh)); // make a full copy here. we may not want to change the input
492 
493  // code is assuming edgemesh doesn't have duplicated vertices
494 
495  wmtk::utils::EigenMatrixWriter writer_edge;
496  edgemesh.serialize(writer_edge);
497 
498  Eigen::MatrixX<int64_t> EV_tmp;
499 
500  writer_edge.get_EV_matrix(EV_tmp);
501  // writer_edge.get_position_matrix(V_edge);
502 
503 
504  // hack here for double input
505  Eigen::MatrixX<double> V_edge_double;
506  writer_edge.get_position_matrix(V_edge_double);
507 
508  // cleanup duplicated vertices for edge mesh
509  Eigen::VectorX<int64_t> _SVI, _SVJ;
510  Eigen::MatrixX<int64_t> EV_in;
511  Eigen::MatrixX<double> V_edge_tmp;
512 
513  igl::remove_duplicate_vertices(V_edge_double, EV_tmp, 0, V_edge_tmp, _SVI, _SVJ, EV_in);
514 
515  Eigen::MatrixX<Rational> V_edge;
516 
517  V_edge.resize(V_edge_tmp.rows(), V_edge_tmp.cols());
518  for (int64_t i = 0; i < V_edge_tmp.rows(); ++i) {
519  for (int64_t j = 0; j < V_edge_tmp.cols(); ++j) {
520  V_edge(i, j) = Rational(V_edge_tmp(i, j));
521  }
522  }
523 
524 
525  // // merge colinear and overlapping segments
526  // // skipped now, not necessary
527  // std::vector<std::array<int64_t, 2>> EV;
528  // for (int64_t i = 0; i < EV_tmp.rows(); ++i) {
529  // const auto& p0 = V_edge.row(EV_tmp(i, 0));
530  // const auto& p1 = V_edge.row(EV_tmp(i, 1));
531  // auto bbox_p = compute_bbox(p0, p1);
532 
533  // bool overlapped = false;
534  // for (auto& e : EV) {
535  // const auto& q0 = V_edge.row(e[0]);
536  // const auto& q1 = V_edge.row(e[1]);
537  // auto bbox_q = compute_bbox(q0, q1);
538 
539  // if (is_bbox_intersect(bbox_p[0], bbox_p[1], bbox_q[0], bbox_q[1]) &&
540  // is_colinear(p0, p1, q0, q1)) {
541  // overlapped = true;
542 
543  // // merge two segments
544  // int64_t endpoint0 = EV_tmp(i, 0);
545  // int64_t endpoint1 = EV_tmp(i, 1);
546 
547  // const Vector2r p0p1 = p1 - p0;
548  // const Vector2r p0q0 = q0 - p0;
549  // const Vector2r p0q1 = q1 - p0;
550  // const Vector2r p1q0 = q0 - p1;
551  // const Vector2r p1q1 = q1 - p1;
552 
553  // if (p0p1[0] * p0q0[0] < 0 || p0p1[1] * p0q0[1] < 0) {
554  // endpoint0 = e[0];
555  // } else if (p0p1[0] * p0q1[0] < 0 || p0p1[1] * p0q1[1] < 0) {
556  // endpoint0 = e[1];
557  // }
558 
559  // if (-p0p1[0] * p1q0[0] < 0 || -p0p1[1] * p1q0[1] < 0) {
560  // endpoint1 = e[0];
561  // } else if (-p0p1[0] * p1q1[0] < 0 || -p0p1[1] * p1q1[1] < 0) {
562  // endpoint1 = e[1];
563  // }
564 
565  // e[0] = endpoint0;
566  // e[1] = endpoint1;
567 
568  // break;
569  // }
570  // }
571  // if (!overlapped) {
572  // EV.push_back({{EV_tmp(i, 0), EV_tmp(i, 1)}});
573  // }
574  // }
575 
576  std::vector<std::array<int64_t, 2>> EV;
577  for (int64_t i = 0; i < EV_in.rows(); ++i) {
578  EV.push_back({{EV_in(i, 0), EV_in(i, 1)}});
579  }
580 
581 
582  // add segment vertices into tris and split the tris
583 
584  // add bbox attribute
585  auto bbox_min_handle =
586  trimesh.register_attribute<Rational>("bbox_min", PrimitiveType::Triangle, 2);
587  auto bbox_min_accessor = trimesh.create_accessor<Rational>(bbox_min_handle);
588  auto bbox_max_handle =
589  trimesh.register_attribute<Rational>("bbox_max", PrimitiveType::Triangle, 2);
590  auto bbox_max_accessor = trimesh.create_accessor<Rational>(bbox_max_handle);
591 
592  auto position_handle =
594  auto position_accessor = trimesh.create_accessor<Rational>(position_handle);
595 
596  auto segment_index_handle =
597  trimesh.register_attribute<int64_t>("segment_index", PrimitiveType::Vertex, 1, false, -1);
598  auto segment_index_accessor = trimesh.create_accessor<int64_t>(segment_index_handle);
599 
600  std::vector<attribute::MeshAttributeHandle> pass_through_attributes;
601  pass_through_attributes.push_back(bbox_min_handle);
602  pass_through_attributes.push_back(bbox_max_handle);
603  pass_through_attributes.push_back(position_handle);
604  // pass_through_attributes.push_back(segment_index_handle);
605 
606  for (const auto& f : trimesh.get_all(PrimitiveType::Triangle)) {
607  // compute bounding box
608  const auto v0 = f;
609  const auto v1 = trimesh.switch_tuple(f, PrimitiveType::Vertex);
610  const auto v2 = trimesh.switch_tuples(f, {PrimitiveType::Edge, PrimitiveType::Vertex});
611 
612  const Vector2r& p0 = position_accessor.const_vector_attribute(v0);
613  const Vector2r& p1 = position_accessor.const_vector_attribute(v1);
614  const Vector2r& p2 = position_accessor.const_vector_attribute(v2);
615 
616  const auto bbox = compute_bbox(p0, p1, p2);
617 
618  bbox_min_accessor.vector_attribute(f) = bbox[0];
619  bbox_max_accessor.vector_attribute(f) = bbox[1];
620  }
621 
622  // add segment vertices in to trimesh
623  for (int64_t i = 0; i < V_edge.rows(); ++i) {
624  const Vector2r p = V_edge.row(i);
625 
626  bool found = false;
627 
628  for (const auto& f : trimesh.get_all(PrimitiveType::Triangle)) {
629  // TODO: add hash
630  if (!is_point_in_bbox(
631  p,
632  bbox_min_accessor.const_vector_attribute(f),
633  bbox_max_accessor.const_vector_attribute(f))) {
634  // check bbox
635  continue;
636  }
637 
638  const auto v0 = f;
639  const auto v1 = trimesh.switch_tuple(f, PrimitiveType::Vertex);
640  const auto v2 = trimesh.switch_tuples(f, {PrimitiveType::Edge, PrimitiveType::Vertex});
641 
642  const Vector2r& p0 = position_accessor.const_vector_attribute(v0);
643  const Vector2r& p1 = position_accessor.const_vector_attribute(v1);
644  const Vector2r& p2 = position_accessor.const_vector_attribute(v2);
645 
646  const std::array<Vector2r, 3> ps = {{p0, p1, p2}};
647 
648  int in_tri_case = is_point_inside_triangle(p, p0, p1, p2);
649 
650  if (in_tri_case == -1) {
651  continue;
652  } else {
653  switch (in_tri_case) {
654  case 0: {
655  // inside
657 
658  for (const auto& attr : pass_through_attributes) {
659  facesplit.split().set_new_attribute_strategy(
660  attr,
663  }
664 
665  facesplit.split().set_new_attribute_strategy(
666  segment_index_handle,
669 
670  for (const auto& attr : pass_through_attributes) {
672  attr,
674  }
675 
677  segment_index_handle,
679 
680  const auto new_v =
681  facesplit(wmtk::simplex::Simplex::face(trimesh, f)).front().tuple();
682 
683  // assign position
684  position_accessor.vector_attribute(new_v) = p;
685 
686  // compute bbox for new tris
687  const Tuple new_f0 = new_v;
688  const Tuple new_f1 = trimesh.switch_tuple(new_f0, PrimitiveType::Triangle);
689  const Tuple new_f2 = trimesh.switch_tuples(
690  new_f0,
692 
693  const auto bbox_0 = compute_bbox(p0, p1, p);
694  const auto bbox_1 = compute_bbox(p0, p2, p);
695  const auto bbox_2 = compute_bbox(p1, p2, p);
696 
697  bbox_min_accessor.vector_attribute(new_f0) = bbox_0[0];
698  bbox_max_accessor.vector_attribute(new_f0) = bbox_0[1];
699  bbox_min_accessor.vector_attribute(new_f1) = bbox_1[0];
700  bbox_max_accessor.vector_attribute(new_f1) = bbox_1[1];
701  bbox_min_accessor.vector_attribute(new_f2) = bbox_2[0];
702  bbox_max_accessor.vector_attribute(new_f2) = bbox_2[1];
703 
704  // resurrect neighbor bbox
705  const auto original_edge =
707  if (!trimesh.is_boundary(PrimitiveType::Edge, original_edge)) {
708  const Tuple neighbor_f =
709  trimesh.switch_tuple(original_edge, PrimitiveType::Triangle);
710  const Tuple oppo_v = trimesh.switch_tuples(
711  neighbor_f,
713  const Vector2r& pn = position_accessor.const_vector_attribute(oppo_v);
714 
715  const auto bbox_n = compute_bbox(p0, p1, pn);
716  bbox_min_accessor.vector_attribute(neighbor_f) = bbox_n[0];
717  bbox_max_accessor.vector_attribute(neighbor_f) = bbox_n[1];
718  }
719 
720 
721  // record id
722  segment_index_accessor.scalar_attribute(new_v) = i;
723 
724  break;
725  }
726  case 1: {
727  // on 01
728  wmtk::operations::EdgeSplit split(trimesh);
729 
730  for (const auto& attr : pass_through_attributes) {
732  attr,
735  }
736 
738  segment_index_handle,
741 
742  const auto new_v =
743  split(wmtk::simplex::Simplex::edge(trimesh, f)).front().tuple();
744 
745 
746  // assign position
747  position_accessor.vector_attribute(new_v) = p;
748 
749  // compute bbox for new tris
750  const Tuple new_f0 = new_v;
751  const Tuple new_f1 = trimesh.switch_tuples(
752  new_f0,
754 
755  const auto bbox_0 = compute_bbox(p1, p2, p);
756  const auto bbox_1 = compute_bbox(p0, p2, p);
757 
758  bbox_min_accessor.vector_attribute(new_f0) = bbox_0[0];
759  bbox_max_accessor.vector_attribute(new_f0) = bbox_0[1];
760  bbox_min_accessor.vector_attribute(new_f1) = bbox_1[0];
761  bbox_max_accessor.vector_attribute(new_f1) = bbox_1[1];
762 
763  if (!trimesh.is_boundary(PrimitiveType::Edge, new_v)) {
764  const Tuple new_f2 = trimesh.switch_tuple(new_v, PrimitiveType::Triangle);
765  const Tuple new_f3 = trimesh.switch_tuples(
766  new_f2,
768 
769  const auto& p4 =
770  position_accessor.const_vector_attribute(trimesh.switch_tuples(
771  new_f2,
772  {PrimitiveType::Edge, PrimitiveType::Vertex}));
773 
774  const auto bbox_2 = compute_bbox(p1, p4, p);
775  const auto bbox_3 = compute_bbox(p0, p4, p);
776 
777  bbox_min_accessor.vector_attribute(new_f2) = bbox_2[0];
778  bbox_max_accessor.vector_attribute(new_f2) = bbox_2[1];
779  bbox_min_accessor.vector_attribute(new_f3) = bbox_3[0];
780  bbox_max_accessor.vector_attribute(new_f3) = bbox_3[1];
781  }
782 
783  segment_index_accessor.scalar_attribute(new_v) = i;
784 
785  break;
786  }
787  case 2: {
788  // on 12
789 
790  wmtk::operations::EdgeSplit split(trimesh);
791  for (const auto& attr : pass_through_attributes) {
793  attr,
796  }
797 
799  segment_index_handle,
802 
803  const auto new_v = split(wmtk::simplex::Simplex::edge(
804  trimesh,
805  trimesh.switch_tuples(
806  f,
807  {PrimitiveType::Vertex, PrimitiveType::Edge})))
808  .front()
809  .tuple();
810 
811  // assign position
812  position_accessor.vector_attribute(new_v) = p;
813 
814  // compute bbox for new tris
815  const Tuple new_f0 = new_v;
816  const Tuple new_f1 = trimesh.switch_tuples(
817  new_f0,
819 
820  const auto bbox_0 = compute_bbox(p0, p2, p);
821  const auto bbox_1 = compute_bbox(p0, p1, p);
822 
823  bbox_min_accessor.vector_attribute(new_f0) = bbox_0[0];
824  bbox_max_accessor.vector_attribute(new_f0) = bbox_0[1];
825  bbox_min_accessor.vector_attribute(new_f1) = bbox_1[0];
826  bbox_max_accessor.vector_attribute(new_f1) = bbox_1[1];
827 
828  if (!trimesh.is_boundary(PrimitiveType::Edge, new_v)) {
829  const Tuple new_f2 = trimesh.switch_tuple(new_v, PrimitiveType::Triangle);
830  const Tuple new_f3 = trimesh.switch_tuples(
831  new_f2,
833 
834  const auto& p4 =
835  position_accessor.const_vector_attribute(trimesh.switch_tuples(
836  new_f2,
837  {PrimitiveType::Edge, PrimitiveType::Vertex}));
838 
839  const auto bbox_2 = compute_bbox(p2, p4, p);
840  const auto bbox_3 = compute_bbox(p1, p4, p);
841 
842  bbox_min_accessor.vector_attribute(new_f2) = bbox_2[0];
843  bbox_max_accessor.vector_attribute(new_f2) = bbox_2[1];
844  bbox_min_accessor.vector_attribute(new_f3) = bbox_3[0];
845  bbox_max_accessor.vector_attribute(new_f3) = bbox_3[1];
846  }
847 
848  segment_index_accessor.scalar_attribute(new_v) = i;
849 
850  break;
851  }
852  case 3: {
853  // on 20
854  wmtk::operations::EdgeSplit split(trimesh);
855 
856  for (const auto& attr : pass_through_attributes) {
858  attr,
861  }
862 
864  segment_index_handle,
867 
868  const auto new_v = split(wmtk::simplex::Simplex::edge(
869  trimesh,
870  trimesh.switch_tuple(f, PrimitiveType::Edge)))
871  .front()
872  .tuple();
873 
874  // assign position
875  position_accessor.vector_attribute(new_v) = p;
876 
877  // compute bbox for new tris
878  const Tuple new_f0 = new_v;
879  const Tuple new_f1 = trimesh.switch_tuples(
880  new_f0,
882 
883  const auto bbox_0 = compute_bbox(p1, p2, p);
884  const auto bbox_1 = compute_bbox(p0, p1, p);
885 
886  bbox_min_accessor.vector_attribute(new_f0) = bbox_0[0];
887  bbox_max_accessor.vector_attribute(new_f0) = bbox_0[1];
888  bbox_min_accessor.vector_attribute(new_f1) = bbox_1[0];
889  bbox_max_accessor.vector_attribute(new_f1) = bbox_1[1];
890 
891  if (!trimesh.is_boundary(PrimitiveType::Edge, new_v)) {
892  const Tuple new_f2 = trimesh.switch_tuple(new_v, PrimitiveType::Triangle);
893  const Tuple new_f3 = trimesh.switch_tuples(
894  new_f2,
896 
897  const auto& p4 =
898  position_accessor.const_vector_attribute(trimesh.switch_tuples(
899  new_f2,
900  {PrimitiveType::Edge, PrimitiveType::Vertex}));
901 
902  const auto bbox_2 = compute_bbox(p2, p4, p);
903  const auto bbox_3 = compute_bbox(p0, p4, p);
904 
905  bbox_min_accessor.vector_attribute(new_f2) = bbox_2[0];
906  bbox_max_accessor.vector_attribute(new_f2) = bbox_2[1];
907  bbox_min_accessor.vector_attribute(new_f3) = bbox_3[0];
908  bbox_max_accessor.vector_attribute(new_f3) = bbox_3[1];
909  }
910 
911  segment_index_accessor.scalar_attribute(new_v) = i;
912 
913  break;
914  }
915  case 4: {
916  // is 0
917  segment_index_accessor.scalar_attribute(v0) = i;
918  break;
919  }
920  case 5: {
921  // is 1
922  segment_index_accessor.scalar_attribute(v1) = i;
923  break;
924  }
925  case 6: {
926  // is 2
927  segment_index_accessor.scalar_attribute(v2) = i;
928  break;
929  }
930  default: {
931  throw std::runtime_error("wrong inside triangle case");
932  }
933  }
934 
935  // found, break, go add next vertex
936  found = true;
937  break;
938  }
939  }
940  assert(found);
941  }
942 
943  trimesh.consolidate();
944 
945  // get vertex map segment -> trimesh
946  std::vector<int64_t> v_map_seg_to_tri(V_edge.rows());
947 
948  const auto& vs = trimesh.get_all(PrimitiveType::Vertex);
949  for (int64_t i = 0; i < vs.size(); ++i) {
950  int64_t seg_idx = segment_index_accessor.const_scalar_attribute(vs[i]);
951 
952  if (seg_idx < 0) {
953  continue;
954  }
955 
956  assert(seg_idx < V_edge.rows());
957  v_map_seg_to_tri[seg_idx] = i;
958  }
959 
960  // get all edges from trimesh
962  trimesh.serialize(writer_tri);
963 
964  Eigen::MatrixX<int64_t> FV;
965  Eigen::MatrixX<Rational> V_tris;
966 
967  writer_tri.get_FV_matrix(FV);
968  writer_tri.get_position_matrix(V_tris);
969 
970  Eigen::MatrixX<int64_t> edges;
971  igl::edges(FV, edges);
972 
973  // compute intersections, store in each segment
974  v_final.resize(V_tris.rows());
975 
976  for (int64_t i = 0; i < V_tris.rows(); ++i) {
977  v_final[i] = V_tris.row(i);
978  }
979 
980  std::vector<Segment> segments;
981  for (int64_t i = 0; i < edges.rows(); ++i) {
982  segments.emplace_back(v_final[edges(i, 0)], v_final[edges(i, 1)], edges(i, 0), edges(i, 1));
983  }
984 
985  // remap EV
986  for (int i = 0; i < EV.size(); ++i) {
987  EV[i][0] = v_map_seg_to_tri[EV[i][0]];
988  EV[i][1] = v_map_seg_to_tri[EV[i][1]];
989  }
990 
991  for (int64_t i = 0; i < EV.size(); ++i) {
992  const int64_t idx0 = EV[i][0];
993  const int64_t idx1 = EV[i][1];
994  const Vector2r p0 = v_final[idx0];
995  const Vector2r p1 = v_final[idx1];
996 
997  Segment s(p0, p1, idx0, idx1);
998  s.is_on_input = true;
999 
1000  std::vector<Segment> new_segments;
1001  bool s_already_exist = false;
1002 
1003  for (int64_t j = 0; j < segments.size(); ++j) {
1004  auto& seg = segments[j];
1005 
1006  if (seg.deprecated) continue;
1007  // check bbox intersection
1008  if (!is_bbox_intersect(s.bbox_min, s.bbox_max, seg.bbox_min, seg.bbox_max)) {
1009  continue;
1010  }
1011 
1012  // check colinear and merge/split segments
1013  if (is_colinear(s.p0, s.p1, seg.p0, seg.p1)) {
1014  if ((s.p0 == seg.p0 && s.p1 == seg.p1) || (s.p0 == seg.p1 && s.p1 == seg.p0)) {
1015  // s exists in segments, break
1016  s_already_exist = true;
1017  seg.is_on_input = true;
1018  break;
1019  }
1020 
1021  // deprecate seg, adding new segments
1022  seg.deprecated = true;
1023 
1024  std::vector<std::pair<int64_t, Vector2r>> all_points;
1025  all_points.reserve(s.points_on_segment.size() + seg.points_on_segment.size());
1026 
1027  for (const auto& p : s.points_on_segment) {
1028  all_points.push_back(p);
1029  }
1030  for (const auto& p : seg.points_on_segment) {
1031  all_points.push_back(p);
1032  }
1033 
1034  auto comp = [](const std::pair<int64_t, Vector2r>& v0,
1035  const std::pair<int64_t, Vector2r>& v1) {
1036  if (v0.second[0] == v1.second[0]) {
1037  return v0.second[1] < v1.second[1];
1038  }
1039  return v0.second[0] < v1.second[0];
1040  };
1041 
1042  auto equal = [](const std::pair<int64_t, Vector2r>& v0,
1043  const std::pair<int64_t, Vector2r>& v1) {
1044  return v0.second[0] == v1.second[0] && v0.second[1] == v1.second[1];
1045  };
1046 
1047  // sort and unique
1048  std::sort(all_points.begin(), all_points.end(), comp);
1049  all_points.erase(
1050  std::unique(all_points.begin(), all_points.end(), equal),
1051  all_points.end());
1052 
1053  int64_t p0_idx = -1, p1_idx = -1;
1054 
1055  for (int64_t k = 0; k < all_points.size(); ++k) {
1056  if (idx0 == all_points[k].first) {
1057  p0_idx = k;
1058  }
1059  if (idx1 == all_points[k].first) {
1060  p1_idx = k;
1061  }
1062  }
1063 
1064  assert(p0_idx != -1 && p1_idx != -1);
1065 
1066  // find the start and end idx on input s
1067  int64_t start_idx, end_idx;
1068 
1069  if (p0_idx < p1_idx) {
1070  start_idx = p0_idx;
1071  end_idx = p1_idx;
1072  } else {
1073  start_idx = p1_idx;
1074  end_idx = p0_idx;
1075  }
1076 
1077  // construct new segments and update s
1078  if (start_idx != 0) {
1079  Segment s0(
1080  all_points[0].second,
1081  all_points[start_idx].second,
1082  all_points[0].first,
1083  all_points[start_idx].first);
1084  for (int64_t k = 1; k < start_idx; ++k) {
1085  s0.points_on_segment.push_back(all_points[k]);
1086  }
1087 
1088  s0.is_on_input = seg.is_on_input;
1089 
1090  new_segments.push_back(s0);
1091  }
1092 
1093  s.points_on_segment.clear();
1094  for (int64_t k = start_idx; k <= end_idx; ++k) {
1095  s.points_on_segment.push_back(all_points[k]);
1096  }
1097 
1098  if (end_idx != all_points.size() - 1) {
1099  Segment s1(
1100  all_points[end_idx].second,
1101  all_points[all_points.size() - 1].second,
1102  all_points[end_idx].first,
1103  all_points[all_points.size() - 1].first);
1104  for (int64_t k = end_idx + 1; k < all_points.size() - 1; ++k) {
1105  s1.points_on_segment.push_back(all_points[k]);
1106  }
1107 
1108  s1.is_on_input = seg.is_on_input;
1109 
1110  new_segments.push_back(s1);
1111  }
1112 
1113  continue;
1114  }
1115 
1116  // not colinear
1117  // get real intersection
1118  Vector2r res;
1119  Rational t0, t1;
1120  if (!segment_segment_inter(p0, p1, seg.p0, seg.p1, res, t0, t1)) {
1121  continue;
1122  }
1123 
1124  assert(t0 >= 0 && t0 <= 1);
1125  assert(t1 >= 0 && t1 <= 1);
1126 
1127  // add point to segment
1128  bool v_exist_on_0 = false;
1129  bool v_exist_on_1 = false;
1130  int64_t new_v_idx = -1;
1131 
1132  for (int64_t k = 0; k < s.points_on_segment.size(); ++k) {
1133  if (res == s.points_on_segment[k].second) {
1134  v_exist_on_0 = true;
1135  new_v_idx = s.points_on_segment[k].first;
1136  break;
1137  }
1138  }
1139 
1140  for (int64_t k = 0; k < seg.points_on_segment.size(); ++k) {
1141  if (res == seg.points_on_segment[k].second) {
1142  v_exist_on_1 = true;
1143  new_v_idx = seg.points_on_segment[k].first;
1144  break;
1145  }
1146  }
1147 
1148  if (!v_exist_on_0 && !v_exist_on_1) {
1149  v_final.push_back(res);
1150  new_v_idx = v_final.size() - 1;
1151  }
1152 
1153  assert(new_v_idx > -1);
1154 
1155  if (!v_exist_on_0) {
1156  // for (int64_t k = 0; k < s.points_on_segment.size() - 1; ++k) {
1157  // // insert by order of t
1158  // if (t0 > s.points_on_segment[k].second &&
1159  // t0 < s.points_on_segment[k + 1].second) {
1160  // s.points_on_segment.insert(
1161  // std::next(s.points_on_segment.begin(), k + 1),
1162  // std::make_pair(new_v_idx, t0));
1163  // break;
1164  // }
1165  // }
1166  s.points_on_segment.push_back(std::make_pair(new_v_idx, res));
1167  }
1168 
1169  if (!v_exist_on_1) {
1170  // for (int64_t k = 0; k < seg.points_on_segment.size() - 1; ++k) {
1171  // // insert by order of t
1172  // if (t1 > seg.points_on_segment[k].second &&
1173  // t1 < seg.points_on_segment[k + 1].second) {
1174  // seg.points_on_segment.insert(
1175  // std::next(seg.points_on_segment.begin(), k + 1),
1176  // std::make_pair(new_v_idx, t1));
1177  // break;
1178  // }
1179  // }
1180  seg.points_on_segment.push_back(std::make_pair(new_v_idx, res));
1181  }
1182  }
1183 
1184  // segments.push_back new segments
1185  for (const auto& seg : new_segments) {
1186  segments.push_back(seg);
1187  }
1188 
1189  if (!s_already_exist) {
1190  segments.push_back(s);
1191  }
1192  }
1193 
1194  // sort points_on_segment
1195 
1196  auto comp = [](const std::pair<int64_t, Vector2r>& v0, const std::pair<int64_t, Vector2r>& v1) {
1197  if (v0.second[0] == v1.second[0]) {
1198  return v0.second[1] < v1.second[1];
1199  }
1200  return v0.second[0] < v1.second[0];
1201  };
1202 
1203  for (int64_t i = 0; i < segments.size(); ++i) {
1204  if (segments[i].deprecated) {
1205  continue;
1206  }
1207  std::sort(segments[i].points_on_segment.begin(), segments[i].points_on_segment.end(), comp);
1208  }
1209 
1210  // get all v-v connectivity
1211  // bool 1: is on input, bool 2: visited
1212  std::vector<std::vector<std::tuple<int64_t, bool, bool>>> VV(v_final.size());
1213 
1214  for (int64_t i = 0; i < segments.size(); ++i) {
1215  if (segments[i].deprecated) {
1216  continue;
1217  }
1218  for (int64_t j = 0; j < segments[i].points_on_segment.size() - 1; ++j) {
1219  VV[segments[i].points_on_segment[j].first].push_back(std::make_tuple(
1220  segments[i].points_on_segment[j + 1].first,
1221  segments[i].is_on_input,
1222  false));
1223  VV[segments[i].points_on_segment[j + 1].first].push_back(std::make_tuple(
1224  segments[i].points_on_segment[j].first,
1225  segments[i].is_on_input,
1226  false));
1227  }
1228  }
1229 
1230 
1231  // dfs get faces
1232  // std::vector<std::array<int64_t, 3>> FV_new;
1233  // std::vector<std::array<bool, 3>> local_e_on_input;
1234 
1235  dfs_triangulate(VV, v_final, FV_new, local_e_on_input);
1236 }
1237 
1238 } // namespace wmtk::components::internal
attribute::Accessor< T, Derived, Dim > create_accessor(const TypedAttributeHandle< T > &handle)
constructs an accessor that is aware of the derived mesh's type
Definition: MeshCRTP.hpp:67
Tuple switch_tuples(const Tuple &tuple, const ContainerType &op_sequence) const
Performs a sequence of switch_tuple operations in the order specified in op_sequence.
Definition: MeshCRTP.hpp:135
attribute::MeshAttributeHandle register_attribute(const std::string &name, PrimitiveType type, int64_t size, bool replace=false, T default_value=T(0))
void serialize(MeshWriter &writer, const Mesh *local_root=nullptr) const
Definition: Mesh.cpp:92
attribute::MeshAttributeHandle get_attribute_handle(const std::string &name, const PrimitiveType ptype) const
Definition: Mesh.hpp:917
std::vector< Tuple > get_all(PrimitiveType type) const
Generate a vector of Tuples from global vertex/edge/triangle/tetrahedron index.
Definition: Mesh.cpp:18
virtual std::tuple< std::vector< std::vector< int64_t > >, std::vector< std::vector< int64_t > > > consolidate()
Consolidate the attributes, moving all valid simplexes at the beginning of the corresponding vector.
int get_sign() const
Definition: Rational.cpp:15
Tuple switch_tuple(const Tuple &tuple, PrimitiveType type) const final override
switch the orientation of the Tuple of the given dimension
Definition: TriMesh.cpp:98
bool is_boundary(PrimitiveType pt, const Tuple &tuple) const final override
check if a simplex (encoded as a tuple/primitive pair) lies on a boundary or not
Definition: TriMesh.cpp:57
std::vector< std::pair< int64_t, Vector2r > > points_on_segment
void set_new_attribute_strategy(const attribute::MeshAttributeHandle &attribute, const std::shared_ptr< const operations::BaseCollapseNewAttributeStrategy > &other)
void set_new_attribute_strategy(const attribute::MeshAttributeHandle &attribute, const std::shared_ptr< const operations::BaseSplitNewAttributeStrategy > &other)
Definition: EdgeSplit.cpp:85
The return tuple is the new vertex, pointing to the original vertex.
static Simplex face(const Mesh &m, const Tuple &t)
Definition: Simplex.hpp:63
static Simplex edge(const Mesh &m, const Tuple &t)
Definition: Simplex.hpp:61
void get_position_matrix(MatrixX< double > &matrix)
void get_EV_matrix(MatrixX< int64_t > &matrix)
void get_FV_matrix(MatrixX< int64_t > &matrix)
void dfs_triangulate(std::vector< std::vector< std::tuple< int64_t, bool, bool >>> &VV, std::vector< Vector2r > &V_pos, std::vector< std::array< int64_t, 3 >> &FV, std::vector< std::array< int, 3 >> &local_e_on_input)
std::array< wmtk::Vector2r, 2 > compute_bbox(const wmtk::Vector2r &p0, const wmtk::Vector2r &p1, const wmtk::Vector2r &p2)
bool is_colinear(const Vector2r &p0, const Vector2r &p1, const Vector2r &q0, const Vector2r &q1)
wmtk::Rational det(const wmtk::Vector2r &a, const wmtk::Vector2r &b)
std::vector< int64_t > cyclic_order(const std::vector< Vector2r > &V)
void dfs_triangulate_aux(int64_t v, std::vector< std::vector< std::tuple< int64_t, bool, bool >>> &VV, std::vector< Vector2r > &V_pos, std::vector< int64_t > &stack, std::vector< int > &in_stack, std::vector< int > &is_on_input, std::vector< int > &is_used, std::vector< std::vector< int64_t >> &in_stack_idx, const Vector2r &prev_vector, const int64_t v_prev, std::vector< std::array< int64_t, 3 >> &FV, std::vector< std::array< int, 3 >> &local_e_on_input)
void edge_insertion(TriMesh &_trimesh, EdgeMesh &edgemesh, std::vector< Vector2r > &v_final, std::vector< std::array< int64_t, 3 >> &FV_new, std::vector< std::array< int, 3 >> &local_e_on_input)
bool segment_segment_inter(const Vector2r &s0, const Vector2r &e0, const Vector2r &s1, const Vector2r &e1, Vector2r &res, Rational &t0, Rational &t1)
int is_point_inside_triangle(const wmtk::Vector2r &P, const wmtk::Vector2r &A, const wmtk::Vector2r &B, const wmtk::Vector2r &C)
bool is_bbox_intersect(const Vector2r &bbox_min_0, const Vector2r &bbox_max_0, const Vector2r &bbox_min_1, const Vector2r &bbox_max_1)
bool is_point_in_bbox(const Vector2r &point, const Vector2r &bbox_min, const Vector2r &bbox_max)
std::vector< Tuple > edges(const Mesh &m, const Simplex &simplex)
int wmtk_orient2d(double p0x, double p0y, double p1x, double p1y, double p2x, double p2y)
Definition: orient.cpp:178
Rational abs(const Rational &r0)
Definition: Rational.cpp:328
Vector< Rational, 2 > Vector2r
Definition: Types.hpp:42