Wildmeshing Toolkit
Loading...
Searching...
No Matches
edge_insertion.cpp
Go to the documentation of this file.
1#include "edge_insertion.hpp"
2
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
138bool 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
162std::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
190std::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
212bool 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
221std::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
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) {
660 attr,
663 }
664
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,
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:136
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:93
attribute::MeshAttributeHandle get_attribute_handle(const std::string &name, const PrimitiveType ptype) const
Definition Mesh.hpp:903
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:99
bool is_boundary(PrimitiveType pt, const Tuple &tuple) const final override
returns if a simplex is on the boundary of hte mesh. For anything but dimension - 1 this checks if th...
Definition TriMesh.cpp:58
The Tuple is the basic navigation tool in our mesh data structure.
Definition Tuple.hpp:19
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)
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::array< wmtk::Vector2r, 2 > compute_bbox(const wmtk::Vector2r &p0, const wmtk::Vector2r &p1, const wmtk::Vector2r &p2)
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)
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)
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)
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)
EdgeInsertionMeshes edge_insertion(EdgeMesh &input_mesh, TriMesh &bg_mesh)
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