Blender V5.0
bmesh_mods.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11
12#include "BLI_math_vector.h"
13#include "BLI_vector.hh"
14
15#include "BKE_customdata.hh"
16
17#include "bmesh.hh"
19
20using blender::Vector;
21
23{
24 /* logic for 3 or more is identical */
25 const int len = BM_vert_edge_count_at_most(v, 3);
26
27 if (len == 1) {
28 BM_vert_kill(bm, v); /* will kill edges too */
29 return true;
30 }
31 if (!BM_vert_is_manifold(v)) {
32 if (!v->e) {
34 return true;
35 }
36 if (!v->e->l) {
37 if (len == 2) {
38 return (BM_vert_collapse_edge(bm, v->e, v, true, true, true) != nullptr);
39 }
40 /* used to kill the vertex here, but it may be connected to faces.
41 * so better do nothing */
42 return false;
43 }
44 return false;
45 }
46 if (len == 2 && BM_vert_face_count_is_equal(v, 1)) {
47 /* boundary vertex on a face */
48 return (BM_vert_collapse_edge(bm, v->e, v, true, true, true) != nullptr);
49 }
50 return BM_disk_dissolve(bm, v);
51}
52
54{
55 BMEdge *e, *keepedge = nullptr, *baseedge = nullptr;
56 int len = 0;
57
58 if (!BM_vert_is_manifold(v)) {
59 return false;
60 }
61
62 if (v->e) {
63 /* v->e we keep, what else */
64 e = v->e;
65 do {
67 if (!BM_edge_share_face_check(e, v->e)) {
68 keepedge = e;
69 baseedge = v->e;
70 break;
71 }
72 len++;
73 } while (e != v->e);
74 }
75
76 /* this code for handling 2 and 3-valence verts
77 * may be totally bad */
78 if (keepedge == nullptr && len == 3) {
79#if 0
80 /* handle specific case for three-valence. solve it by
81 * increasing valence to four. this may be hackish. */
82 BMLoop *l_a = BM_face_vert_share_loop(e->l->f, v);
83 BMLoop *l_b = (e->l->v == v) ? e->l->next : e->l;
84
85 if (!BM_face_split(bm, e->l->f, l_a, l_b, nullptr, nullptr, false)) {
86 return false;
87 }
88
89 if (!BM_disk_dissolve(bm, v)) {
90 return false;
91 }
92#else
93 BMFace *f_double;
94
95 if (UNLIKELY(!BM_faces_join_pair(bm, e->l, e->l->radial_next, true, &f_double))) {
96 return false;
97 }
98
99 /* See #BM_faces_join note on callers asserting when `r_double` is non-null. */
100 BLI_assert_msg(f_double == nullptr,
101 "Doubled face detected at " AT ". Resulting mesh may be corrupt.");
102
103 if (UNLIKELY(!BM_vert_collapse_faces(bm, v->e, v, 1.0, true, false, true, true))) {
104 return false;
105 }
106#endif
107 return true;
108 }
109 if (keepedge == nullptr && len == 2) {
110 /* collapse the vertex */
111 e = BM_vert_collapse_faces(bm, v->e, v, 1.0, true, true, true, true);
112
113 if (!e) {
114 return false;
115 }
116
117 /* handle two-valence */
118 if (e->l != e->l->radial_next) {
119 BMFace *f_double;
120
121 if (!BM_faces_join_pair(bm, e->l, e->l->radial_next, true, &f_double)) {
122 return false;
123 }
124
125 /* See #BM_faces_join note on callers asserting when `r_double` is non-null. */
126 BLI_assert_msg(f_double == nullptr,
127 "Doubled face detected at " AT ". Resulting mesh may be corrupt.");
128 }
129
130 return true;
131 }
132
133 if (keepedge) {
134 bool done = false;
135
136 while (!done) {
137 done = true;
138 e = v->e;
139 do {
140 BMFace *f = nullptr;
141 if (BM_edge_is_manifold(e) && (e != baseedge) && (e != keepedge)) {
142 BMFace *f_double;
143
144 f = BM_faces_join_pair(bm, e->l, e->l->radial_next, true, &f_double);
145 /* return if couldn't join faces in manifold
146 * conditions */
147 /* !disabled for testing why bad things happen */
148 if (!f) {
149 return false;
150 }
151
152 /* See #BM_faces_join note on callers asserting when `r_double` is non-null. */
153 BLI_assert_msg(f_double == nullptr,
154 "Doubled face detected at " AT ". Resulting mesh may be corrupt.");
155 }
156
157 if (f) {
158 done = false;
159 break;
160 }
161 } while ((e = bmesh_disk_edge_next(e, v)) != v->e);
162 }
163
164 /* collapse the vertex */
165 /* NOTE: the baseedge can be a boundary of manifold, use this as join_faces arg. */
167 bm, baseedge, v, 1.0, true, !BM_edge_is_boundary(baseedge), true, true);
168
169 if (!e) {
170 return false;
171 }
172
173 if (e->l) {
174 /* get remaining two faces */
175 if (e->l != e->l->radial_next) {
176 BMFace *f_double;
177
178 /* join two remaining faces */
179 if (!BM_faces_join_pair(bm, e->l, e->l->radial_next, true, &f_double)) {
180 return false;
181 }
182
183 /* See #BM_faces_join note on callers asserting when `r_double` is non-null. */
184 BLI_assert_msg(f_double == nullptr,
185 "Doubled face detected at " AT ". Resulting mesh may be corrupt.");
186 }
187 }
188 }
189
190 return true;
191}
192
194 BMesh *bm, BMLoop *l_a, BMLoop *l_b, const bool do_del, BMFace **r_double)
195{
196 BLI_assert((l_a != l_b) && (l_a->e == l_b->e));
197
198 if (l_a->v == l_b->v) {
199 const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
200 bmesh_kernel_loop_reverse(bm, l_b->f, cd_loop_mdisp_offset, true);
201 }
202
203 BMFace *faces[2] = {l_a->f, l_b->f};
204 return BM_faces_join(bm, faces, 2, do_del, r_double);
205}
206
208 BMFace *f,
209 BMLoop *l_a,
210 BMLoop *l_b,
211 BMLoop **r_l,
212 BMEdge *example,
213 const bool no_double)
214{
215 const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
216 BMFace *f_new, *f_tmp;
217
218 BLI_assert(l_a != l_b);
219 BLI_assert(f == l_a->f && f == l_b->f);
221
222 /* could be an assert */
223 if (UNLIKELY(BM_loop_is_adjacent(l_a, l_b)) || UNLIKELY(f != l_a->f || f != l_b->f)) {
224 if (r_l) {
225 *r_l = nullptr;
226 }
227 return nullptr;
228 }
229
230 /* do we have a multires layer? */
231 if (cd_loop_mdisp_offset != -1) {
232 f_tmp = BM_face_copy(bm, f, false, false);
233 }
234
235#ifdef USE_BMESH_HOLES
236 f_new = bmesh_kernel_split_face_make_edge(bm, f, l_a, l_b, r_l, nullptr, example, no_double);
237#else
238 f_new = bmesh_kernel_split_face_make_edge(bm, f, l_a, l_b, r_l, example, no_double);
239#endif
240
241 if (f_new) {
242 /* handle multires update */
243 if (cd_loop_mdisp_offset != -1) {
244 float f_dst_center[3];
245 float f_src_center[3];
246
247 BM_face_calc_center_median(f_tmp, f_src_center);
248
249 BM_face_calc_center_median(f, f_dst_center);
250 BM_face_interp_multires_ex(bm, f, f_tmp, f_dst_center, f_src_center, cd_loop_mdisp_offset);
251
252 BM_face_calc_center_median(f_new, f_dst_center);
254 bm, f_new, f_tmp, f_dst_center, f_src_center, cd_loop_mdisp_offset);
255
256#if 0
257 /* BM_face_multires_bounds_smooth doesn't flip displacement correct */
260#endif
261 }
262 }
263
264 if (cd_loop_mdisp_offset != -1) {
265 BM_face_kill(bm, f_tmp);
266 }
267
268 return f_new;
269}
270
272 BMFace *f,
273 BMLoop *l_a,
274 BMLoop *l_b,
275 float cos[][3],
276 int n,
277 BMLoop **r_l,
278 BMEdge *example)
279{
280 BMFace *f_new, *f_tmp;
281 BMLoop *l_new;
282 BMEdge *e, *e_new;
283 BMVert *v_new;
284 // BMVert *v_a = l_a->v; /* UNUSED */
285 BMVert *v_b = l_b->v;
286 int i, j;
287
288 BLI_assert(l_a != l_b);
289 BLI_assert(f == l_a->f && f == l_b->f);
290 BLI_assert(!((n == 0) && BM_loop_is_adjacent(l_a, l_b)));
291
292 /* could be an assert */
293 if (UNLIKELY((n == 0) && BM_loop_is_adjacent(l_a, l_b)) || UNLIKELY(l_a->f != l_b->f)) {
294 if (r_l) {
295 *r_l = nullptr;
296 }
297 return nullptr;
298 }
299
300 f_tmp = BM_face_copy(bm, f, true, true);
301
302#ifdef USE_BMESH_HOLES
303 f_new = bmesh_kernel_split_face_make_edge(bm, f, l_a, l_b, &l_new, nullptr, example, false);
304#else
305 f_new = bmesh_kernel_split_face_make_edge(bm, f, l_a, l_b, &l_new, example, false);
306#endif
307 /* bmesh_kernel_split_face_make_edge returns in 'l_new'
308 * a Loop for f_new going from 'v_a' to 'v_b'.
309 * The radial_next is for 'f' and goes from 'v_b' to 'v_a'. */
310
311 if (f_new) {
312 e = l_new->e;
313 for (i = 0; i < n; i++) {
314 v_new = bmesh_kernel_split_edge_make_vert(bm, v_b, e, &e_new);
315 BLI_assert(v_new != nullptr);
316 /* bmesh_kernel_split_edge_make_vert returns in 'e_new'
317 * the edge going from 'v_new' to 'v_b'. */
318 copy_v3_v3(v_new->co, cos[i]);
319
320 /* interpolate the loop data for the loops with (v == v_new), using orig face */
321 for (j = 0; j < 2; j++) {
322 BMEdge *e_iter = (j == 0) ? e : e_new;
323 BMLoop *l_iter = e_iter->l;
324 do {
325 if (l_iter->v == v_new) {
326 /* this interpolates both loop and vertex data */
327 BM_loop_interp_from_face(bm, l_iter, f_tmp, true, true);
328 }
329 } while ((l_iter = l_iter->radial_next) != e_iter->l);
330 }
331 e = e_new;
332 }
333 }
334
335 BM_face_verts_kill(bm, f_tmp);
336
337 if (r_l) {
338 *r_l = l_new;
339 }
340
341 return f_new;
342}
343
345 BMEdge *e_kill,
346 BMVert *v_kill,
347 float fac,
348 const bool do_del,
349 const bool join_faces,
350 const bool kill_degenerate_faces,
351 const bool kill_duplicate_faces)
352{
353 BMEdge *e_new = nullptr;
354 BMVert *tv = BM_edge_other_vert(e_kill, v_kill);
355
356 BMEdge *e2;
357 BMVert *tv2;
358
359 /* Only intended to be called for 2-valence vertices */
360 BLI_assert(bmesh_disk_count(v_kill) <= 2);
361
362 /* first modify the face loop data */
363
364 if (e_kill->l) {
365 BMLoop *l_iter;
366 const float w[2] = {1.0f - fac, fac};
367
368 l_iter = e_kill->l;
369 do {
370 if (l_iter->v == tv && l_iter->next->v == v_kill) {
371 const void *src[2];
372 BMLoop *tvloop = l_iter;
373 BMLoop *kvloop = l_iter->next;
374
375 src[0] = kvloop->head.data;
376 src[1] = tvloop->head.data;
377 CustomData_bmesh_interp(&bm->ldata, src, w, 2, kvloop->head.data);
378 }
379 } while ((l_iter = l_iter->radial_next) != e_kill->l);
380 }
381
382 /* now interpolate the vertex data */
383 BM_data_interp_from_verts(bm, v_kill, tv, v_kill, fac);
384
385 e2 = bmesh_disk_edge_next(e_kill, v_kill);
386 tv2 = BM_edge_other_vert(e2, v_kill);
387
388 if (join_faces) {
389 BMIter fiter;
390 BMFace *f;
391
393 BM_ITER_ELEM (f, &fiter, v_kill, BM_FACES_OF_VERT) {
394 faces.append(f);
395 }
396
397 if (faces.size() >= 2) {
398 BMFace *f_double;
399
400 BMFace *f2 = BM_faces_join(bm, faces.data(), faces.size(), true, &f_double);
401
402 /* See #BM_faces_join note on callers asserting when `r_double` is non-null. */
403 BLI_assert_msg(f_double == nullptr,
404 "Doubled face detected at " AT ". Resulting mesh may be corrupt.");
405
406 if (f2) {
407 BMLoop *l_a, *l_b;
408
409 if ((l_a = BM_face_vert_share_loop(f2, tv)) && (l_b = BM_face_vert_share_loop(f2, tv2))) {
410 BMLoop *l_new;
411
412 if (BM_face_split(bm, f2, l_a, l_b, &l_new, nullptr, false)) {
413 e_new = l_new->e;
414 }
415 }
416 }
417 }
418 }
419 else {
420 /* single face or no faces */
421 /* same as BM_vert_collapse_edge() however we already
422 * have vars to perform this operation so don't call. */
424 bm, e_kill, v_kill, do_del, true, kill_degenerate_faces, kill_duplicate_faces);
425
426 // e_new = BM_edge_exists(tv, tv2); /* Same as return above. */
427 }
428
429 return e_new;
430}
431
433 BMEdge *e_kill,
434 BMVert *v_kill,
435 const bool do_del,
436 const bool kill_degenerate_faces,
437 const bool kill_duplicate_faces)
438{
439/* nice example implementation but we want loops to have their customdata
440 * accounted for */
441#if 0
442 BMEdge *e_new = nullptr;
443
444 /* Collapse between 2 edges */
445
446 /* in this case we want to keep all faces and not join them,
447 * rather just get rid of the vertex - see bug #28645. */
448 BMVert *tv = BM_edge_other_vert(e_kill, v_kill);
449 if (tv) {
450 BMEdge *e2 = bmesh_disk_edge_next(e_kill, v_kill);
451 if (e2) {
452 BMVert *tv2 = BM_edge_other_vert(e2, v_kill);
453 if (tv2) {
454 /* only action, other calls here only get the edge to return */
456 bm, e_kill, v_kill, do_del, true, kill_degenerate_faces);
457 }
458 }
459 }
460
461 return e_new;
462#else
463 /* with these args faces are never joined, same as above
464 * but account for loop customdata */
466 bm, e_kill, v_kill, 1.0f, do_del, false, kill_degenerate_faces, kill_duplicate_faces);
467#endif
468}
469
470#undef DO_V_INTERP
471
473 BMesh *bm, BMEdge *e_kill, BMVert *v_kill, const bool do_del, const bool kill_degenerate_faces)
474{
475 return bmesh_kernel_join_vert_kill_edge(bm, e_kill, v_kill, do_del, true, kill_degenerate_faces);
476}
477
479{
480 BMVert *v_new, *v_other;
481 BMEdge *e_new;
482
483 Vector<BMFace *, 32> oldfaces;
484 const int cd_loop_mdisp_offset = BM_edge_is_wire(e) ?
485 -1 :
487
488 BLI_assert(BM_vert_in_edge(e, v) == true);
489
490 /* do we have a multi-res layer? */
491 if (cd_loop_mdisp_offset != -1) {
492 BMLoop *l = e->l;
493 do {
494 oldfaces.append(l->f);
495 l = l->radial_next;
496 } while (l != e->l);
497
498 /* flag existing faces so we can differentiate oldfaces from new faces */
499 for (int64_t i = 0; i < oldfaces.size(); i++) {
501 oldfaces[i] = BM_face_copy(bm, oldfaces[i], true, true);
503 }
504 }
505
506 v_other = BM_edge_other_vert(e, v);
507 v_new = bmesh_kernel_split_edge_make_vert(bm, v, e, &e_new);
508 if (r_e != nullptr) {
509 *r_e = e_new;
510 }
511
512 BLI_assert(v_new != nullptr);
513 BLI_assert(BM_vert_in_edge(e_new, v) && BM_vert_in_edge(e_new, v_new));
514 BLI_assert(BM_vert_in_edge(e, v_new) && BM_vert_in_edge(e, v_other));
515
516 sub_v3_v3v3(v_new->co, v_other->co, v->co);
517 madd_v3_v3v3fl(v_new->co, v->co, v_new->co, fac);
518
519 e_new->head.hflag = e->head.hflag;
520 BM_elem_attrs_copy(bm, e, e_new);
521
522 /* v->v_new->v2 */
523 BM_data_interp_face_vert_edge(bm, v_other, v, v_new, e, fac);
524 BM_data_interp_from_verts(bm, v, v_other, v_new, fac);
525
526 if (cd_loop_mdisp_offset != -1) {
527 /* interpolate new/changed loop data from copied old faces */
528 for (BMFace *oldface : oldfaces) {
529 float f_center_old[3];
530
531 BM_face_calc_center_median(oldface, f_center_old);
532
533 for (int j = 0; j < 2; j++) {
534 BMEdge *e1 = j ? e_new : e;
535 BMLoop *l = e1->l;
536
537 if (UNLIKELY(!l)) {
538 BMESH_ASSERT(0);
539 break;
540 }
541
542 do {
543 /* check this is an old face */
545 float f_center[3];
546
547 BM_face_calc_center_median(l->f, f_center);
549 bm, l->f, oldface, f_center, f_center_old, cd_loop_mdisp_offset);
550 }
551 l = l->radial_next;
552 } while (l != e1->l);
553 }
554 }
555
556 /* destroy the old faces */
557 for (BMFace *oldface : oldfaces) {
558 BM_face_verts_kill(bm, oldface);
559 }
560
561/* fix boundaries a bit, doesn't work too well quite yet */
562#if 0
563 for (int j = 0; j < 2; j++) {
564 BMEdge *e1 = j ? e_new : e;
565 BMLoop *l, *l2;
566
567 l = e1->l;
568 if (UNLIKELY(!l)) {
569 BMESH_ASSERT(0);
570 break;
571 }
572
573 do {
575 l = l->radial_next;
576 } while (l != e1->l);
577 }
578#endif
579 }
580
581 return v_new;
582}
583
584BMVert *BM_edge_split_n(BMesh *bm, BMEdge *e, int numcuts, BMVert **r_varr)
585{
586 int i;
587 float percent;
588 BMVert *v_new = nullptr;
589
590 for (i = 0; i < numcuts; i++) {
591 percent = 1.0f / float(numcuts + 1 - i);
592 v_new = BM_edge_split(bm, e, e->v2, nullptr, percent);
593 if (r_varr) {
594 /* fill in reverse order (v1 -> v2) */
595 r_varr[numcuts - i - 1] = v_new;
596 }
597 }
598 return v_new;
599}
600
602{
603 std::swap(e->v1, e->v2);
604 std::swap(e->v1_disk_link, e->v2_disk_link);
605}
606
607void BM_edge_calc_rotate(BMEdge *e, const bool ccw, BMLoop **r_l1, BMLoop **r_l2)
608{
609 BMVert *v1, *v2;
610 BMFace *fa, *fb;
611
612 /* this should have already run */
614
615 /* we know this will work */
616 BM_edge_face_pair(e, &fa, &fb);
617
618 /* so we can use `ccw` variable correctly,
619 * otherwise we could use the edges verts direct */
620 BM_edge_ordered_verts(e, &v1, &v2);
621
622 /* we could swap the verts _or_ the faces, swapping faces
623 * gives more predictable results since that way the next vert
624 * just stitches from face fa / fb */
625 if (!ccw) {
626 std::swap(fa, fb);
627 }
628
629 *r_l1 = BM_face_other_vert_loop(fb, v2, v1);
630 *r_l2 = BM_face_other_vert_loop(fa, v1, v2);
631}
632
634{
635 BMFace *fa, *fb;
636 if (BM_edge_face_pair(e, &fa, &fb)) {
637 BMLoop *la, *lb;
638
639 la = BM_face_other_vert_loop(fa, e->v2, e->v1);
640 lb = BM_face_other_vert_loop(fb, e->v2, e->v1);
641
642 /* check that the next vert in both faces isn't the same
643 * (ie - the next edge doesn't share the same faces).
644 * since we can't rotate usefully in this case. */
645 if (la->v == lb->v) {
646 return false;
647 }
648
649 /* mirror of the check above but in the opposite direction */
650 la = BM_face_other_vert_loop(fa, e->v1, e->v2);
651 lb = BM_face_other_vert_loop(fb, e->v1, e->v2);
652
653 if (la->v == lb->v) {
654 return false;
655 }
656
657 return true;
658 }
659 return false;
660}
661
663{
664 /* NOTE: for these vars 'old' just means initial edge state. */
665
666 float ed_dir_old[3]; /* edge vector */
667 float ed_dir_new[3]; /* edge vector */
668 float ed_dir_new_flip[3]; /* edge vector */
669
670 float ed_dir_v1_old[3];
671 float ed_dir_v2_old[3];
672
673 float ed_dir_v1_new[3];
674 float ed_dir_v2_new[3];
675
676 float cross_old[3];
677 float cross_new[3];
678
679 /* original verts - these will be in the edge 'e' */
680 BMVert *v1_old, *v2_old;
681
682 /* verts from the loops passed */
683
684 BMVert *v1, *v2;
685 /* These are the opposite verts - the verts that _would_ be used if `ccw` was inverted. */
686 BMVert *v1_alt, *v2_alt;
687
688 /* this should have already run */
690
691 BM_edge_ordered_verts(e, &v1_old, &v2_old);
692
693 v1 = l1->v;
694 v2 = l2->v;
695
696 /* get the next vert along */
697 v1_alt = BM_face_other_vert_loop(l1->f, v1_old, v1)->v;
698 v2_alt = BM_face_other_vert_loop(l2->f, v2_old, v2)->v;
699
700 /* normalize all so comparisons are scale independent */
701
702 BLI_assert(BM_edge_exists(v1_old, v1));
703 BLI_assert(BM_edge_exists(v1, v1_alt));
704
705 BLI_assert(BM_edge_exists(v2_old, v2));
706 BLI_assert(BM_edge_exists(v2, v2_alt));
707
708 /* old and new edge vecs */
709 sub_v3_v3v3(ed_dir_old, v1_old->co, v2_old->co);
710 sub_v3_v3v3(ed_dir_new, v1->co, v2->co);
711 normalize_v3(ed_dir_old);
712 normalize_v3(ed_dir_new);
713
714 /* old edge corner vecs */
715 sub_v3_v3v3(ed_dir_v1_old, v1_old->co, v1->co);
716 sub_v3_v3v3(ed_dir_v2_old, v2_old->co, v2->co);
717 normalize_v3(ed_dir_v1_old);
718 normalize_v3(ed_dir_v2_old);
719
720 /* old edge corner vecs */
721 sub_v3_v3v3(ed_dir_v1_new, v1->co, v1_alt->co);
722 sub_v3_v3v3(ed_dir_v2_new, v2->co, v2_alt->co);
723 normalize_v3(ed_dir_v1_new);
724 normalize_v3(ed_dir_v2_new);
725
726 /* compare */
727 cross_v3_v3v3(cross_old, ed_dir_old, ed_dir_v1_old);
728 cross_v3_v3v3(cross_new, ed_dir_new, ed_dir_v1_new);
729 if (dot_v3v3(cross_old, cross_new) < 0.0f) { /* does this flip? */
730 return false;
731 }
732 cross_v3_v3v3(cross_old, ed_dir_old, ed_dir_v2_old);
733 cross_v3_v3v3(cross_new, ed_dir_new, ed_dir_v2_new);
734 if (dot_v3v3(cross_old, cross_new) < 0.0f) { /* does this flip? */
735 return false;
736 }
737
738 negate_v3_v3(ed_dir_new_flip, ed_dir_new);
739
740 /* result is zero area corner */
741 if ((dot_v3v3(ed_dir_new, ed_dir_v1_new) > 0.999f) ||
742 (dot_v3v3(ed_dir_new_flip, ed_dir_v2_new) > 0.999f))
743 {
744 return false;
745 }
746
747 return true;
748}
749
751{
752 /* Stupid check for now:
753 * Could compare angles of surrounding edges
754 * before & after, but this is OK. */
755 return (len_squared_v3v3(e->v1->co, e->v2->co) > len_squared_v3v3(l1->v->co, l2->v->co));
756}
757
758BMEdge *BM_edge_rotate(BMesh *bm, BMEdge *e, const bool ccw, const short check_flag)
759{
760 BMVert *v1, *v2;
761 BMLoop *l1, *l2;
762 BMFace *f;
763 BMEdge *e_new = nullptr;
764 char f_active_prev = 0;
765 char f_hflag_prev_1;
766 char f_hflag_prev_2;
767
768 if (!BM_edge_rotate_check(e)) {
769 return nullptr;
770 }
771
772 BM_edge_calc_rotate(e, ccw, &l1, &l2);
773
774 /* the loops will be freed so assign verts */
775 v1 = l1->v;
776 v2 = l2->v;
777
778 /* --------------------------------------- */
779 /* Checking Code - make sure we can rotate */
780
781 if (check_flag & BM_EDGEROT_CHECK_BEAUTY) {
782 if (!BM_edge_rotate_check_beauty(e, l1, l2)) {
783 return nullptr;
784 }
785 }
786
787 /* check before applying */
788 if (check_flag & BM_EDGEROT_CHECK_EXISTS) {
789 if (BM_edge_exists(v1, v2)) {
790 return nullptr;
791 }
792 }
793
794 /* slowest, check last */
795 if (check_flag & BM_EDGEROT_CHECK_DEGENERATE) {
796 if (!BM_edge_rotate_check_degenerate(e, l1, l2)) {
797 return nullptr;
798 }
799 }
800 /* Done Checking */
801 /* ------------- */
802
803 /* --------------- */
804 /* Rotate The Edge */
805
806 /* first create the new edge, this is so we can copy the customdata from the old one
807 * if splice if disabled, always add in a new edge even if there's one there. */
808 e_new = BM_edge_create(
810
811 f_hflag_prev_1 = l1->f->head.hflag;
812 f_hflag_prev_2 = l2->f->head.hflag;
813
814 /* maintain active face */
815 if (bm->act_face == l1->f) {
816 f_active_prev = 1;
817 }
818 else if (bm->act_face == l2->f) {
819 f_active_prev = 2;
820 }
821
822 const bool is_flipped = !BM_edge_is_contiguous(e);
823
824 BMFace *f_double;
825
826 /* don't delete the edge, manually remove the edge after so we can copy its attributes */
828 bm, BM_face_edge_share_loop(l1->f, e), BM_face_edge_share_loop(l2->f, e), true, &f_double);
829
830 /* See #BM_faces_join note on callers asserting when `r_double` is non-null. */
831 BLI_assert_msg(f_double == nullptr,
832 "Doubled face detected at " AT ". Resulting mesh may be corrupt.");
833
834 if (f == nullptr) {
835 return nullptr;
836 }
837
838 /* NOTE: this assumes joining the faces _didnt_ also remove the verts.
839 * the #BM_edge_rotate_check will ensure this, but its possibly corrupt state or future edits
840 * break this */
841 if ((l1 = BM_face_vert_share_loop(f, v1)) && (l2 = BM_face_vert_share_loop(f, v2)) &&
842 BM_face_split(bm, f, l1, l2, nullptr, nullptr, true))
843 {
844 /* we should really be able to know the faces some other way,
845 * rather than fetching them back from the edge, but this is predictable
846 * where using the return values from face split isn't. - campbell */
847 BMFace *fa, *fb;
848 if (BM_edge_face_pair(e_new, &fa, &fb)) {
849 fa->head.hflag = f_hflag_prev_1;
850 fb->head.hflag = f_hflag_prev_2;
851
852 if (f_active_prev == 1) {
853 bm->act_face = fa;
854 }
855 else if (f_active_prev == 2) {
856 bm->act_face = fb;
857 }
858
859 if (is_flipped) {
861
862 if (ccw) {
863 /* Needed otherwise `ccw` toggles direction */
864 e_new->l = e_new->l->radial_next;
865 }
866 }
867 }
868 }
869 else {
870 return nullptr;
871 }
872
873 return e_new;
874}
875
880
885
887{
888 return bmesh_kernel_unglue_region_make_vert_multi(bm, larr, larr_len);
889}
CustomData interface, see also DNA_customdata_types.h.
int CustomData_get_offset(const CustomData *data, eCustomDataType type)
void CustomData_bmesh_interp(CustomData *data, const void **src_blocks, const float *weights, int count, void *dst_block)
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void negate_v3_v3(float r[3], const float a[3])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void cross_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void madd_v3_v3v3fl(float r[3], const float a[3], const float b[3], float f)
MINLINE float normalize_v3(float n[3])
#define UNLIKELY(x)
#define AT
void BM_elem_attrs_copy(BMesh *bm, const BMCustomDataCopyMap &map, const BMVert *src, BMVert *dst)
BMVert * bmesh_kernel_unglue_region_make_vert_multi_isolated(BMesh *bm, BMLoop *l_sep)
BMVert * bmesh_kernel_split_edge_make_vert(BMesh *bm, BMVert *tv, BMEdge *e, BMEdge **r_e)
Split Edge Make Vert (SEMV).
void BM_face_verts_kill(BMesh *bm, BMFace *f)
BMFace * BM_faces_join(BMesh *bm, BMFace **faces, int totface, const bool do_del, BMFace **r_double)
Join Connected Faces.
void BM_vert_kill(BMesh *bm, BMVert *v)
void BM_face_kill(BMesh *bm, BMFace *f)
BMEdge * bmesh_kernel_join_edge_kill_vert(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, const bool do_del, const bool check_edge_exists, const bool kill_degenerate_faces, const bool kill_duplicate_faces)
Join Edge Kill Vert (JEKV).
BMVert * bmesh_kernel_unglue_region_make_vert_multi(BMesh *bm, BMLoop **larr, int larr_len)
BMVert * bmesh_kernel_unglue_region_make_vert(BMesh *bm, BMLoop *l_sep)
Un-glue Region Make Vert (URMV).
BMFace * BM_face_copy(BMesh *bm_dst, const BMCustomDataCopyMap &cd_face_map, const BMCustomDataCopyMap &cd_loop_map, BMFace *f, const bool copy_verts, const bool copy_edges)
BMVert * bmesh_kernel_join_vert_kill_edge(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, const bool do_del, const bool check_edge_exists, const bool kill_degenerate_faces)
Join Vert Kill Edge (JVKE).
void bmesh_kernel_loop_reverse(BMesh *bm, BMFace *f, const int cd_loop_mdisp_offset, const bool use_loop_mdisp_flip)
Loop Reverse.
BMFace * bmesh_kernel_split_face_make_edge(BMesh *bm, BMFace *f, BMLoop *l_v1, BMLoop *l_v2, BMLoop **r_l, BMEdge *e_example, const bool no_double)
Split Face Make Edge (SFME).
BMEdge * BM_edge_create(BMesh *bm, BMVert *v1, BMVert *v2, const BMEdge *e_example, const eBMCreateFlag create_flag)
Main function for creating a new edge.
@ BM_CREATE_NOP
Definition bmesh_core.hh:28
@ BM_CREATE_NO_DOUBLE
Definition bmesh_core.hh:30
#define BMESH_ASSERT(a)
void BM_face_interp_multires_ex(BMesh *bm, BMFace *f_dst, const BMFace *f_src, const float f_dst_center[3], const float f_src_center[3], const int cd_loop_mdisp_offset)
void BM_face_multires_bounds_smooth(BMesh *bm, BMFace *f)
void BM_loop_interp_from_face(BMesh *bm, BMLoop *l_dst, const BMFace *f_src, const bool do_vertex, const bool do_multires)
void BM_data_interp_face_vert_edge(BMesh *bm, const BMVert *v_src_1, const BMVert *, BMVert *v, BMEdge *e, const float fac)
Data Face-Vert Edge Interpolate.
void BM_data_interp_from_verts(BMesh *bm, const BMVert *v_src_1, const BMVert *v_src_2, BMVert *v_dst, const float fac)
Data, Interpolate From Verts.
#define BM_ITER_ELEM(ele, iter, data, itype)
@ BM_FACES_OF_VERT
BMesh * bm
BMFace * BM_face_split_n(BMesh *bm, BMFace *f, BMLoop *l_a, BMLoop *l_b, float cos[][3], int n, BMLoop **r_l, BMEdge *example)
Face Split with intermediate points.
BMVert * BM_face_loop_separate(BMesh *bm, BMLoop *l_sep)
Rip a single face from a vertex fan.
void BM_edge_calc_rotate(BMEdge *e, const bool ccw, BMLoop **r_l1, BMLoop **r_l2)
BMVert * BM_face_loop_separate_multi(BMesh *bm, BMLoop **larr, int larr_len)
bool BM_disk_dissolve(BMesh *bm, BMVert *v)
Definition bmesh_mods.cc:53
BMEdge * BM_vert_collapse_faces(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, float fac, const bool do_del, const bool join_faces, const bool kill_degenerate_faces, const bool kill_duplicate_faces)
Vert Collapse Faces.
bool BM_edge_rotate_check_degenerate(BMEdge *e, BMLoop *l1, BMLoop *l2)
Check if Edge Rotate Gives Degenerate Faces.
bool BM_vert_dissolve(BMesh *bm, BMVert *v)
Dissolve Vert.
Definition bmesh_mods.cc:22
bool BM_edge_rotate_check_beauty(BMEdge *e, BMLoop *l1, BMLoop *l2)
BMVert * BM_edge_split(BMesh *bm, BMEdge *e, BMVert *v, BMEdge **r_e, float fac)
Edge Split.
BMFace * BM_faces_join_pair(BMesh *bm, BMLoop *l_a, BMLoop *l_b, const bool do_del, BMFace **r_double)
Faces Join Pair.
BMEdge * BM_vert_collapse_edge(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, const bool do_del, const bool kill_degenerate_faces, const bool kill_duplicate_faces)
Vert Collapse Faces.
bool BM_edge_rotate_check(BMEdge *e)
Check if Rotate Edge is OK.
BMEdge * BM_edge_rotate(BMesh *bm, BMEdge *e, const bool ccw, const short check_flag)
Rotate Edge.
void BM_edge_verts_swap(BMEdge *e)
BMVert * BM_face_loop_separate_multi_isolated(BMesh *bm, BMLoop *l_sep)
BMFace * BM_face_split(BMesh *bm, BMFace *f, BMLoop *l_a, BMLoop *l_b, BMLoop **r_l, BMEdge *example, const bool no_double)
Face Split.
BMVert * BM_edge_collapse(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, const bool do_del, const bool kill_degenerate_faces)
BMVert * BM_edge_split_n(BMesh *bm, BMEdge *e, int numcuts, BMVert **r_varr)
Split an edge multiple times evenly.
@ BM_EDGEROT_CHECK_EXISTS
@ BM_EDGEROT_CHECK_BEAUTY
@ BM_EDGEROT_CHECK_SPLICE
@ BM_EDGEROT_CHECK_DEGENERATE
void BM_face_normal_flip(BMesh *bm, BMFace *f)
void BM_face_calc_center_median(const BMFace *f, float r_cent[3])
#define BM_ELEM_API_FLAG_DISABLE(element, f)
#define BM_ELEM_API_FLAG_TEST(element, f)
#define BM_ELEM_API_FLAG_ENABLE(element, f)
@ _FLAG_OVERLAP
int bmesh_disk_count(const BMVert *v)
void BM_edge_ordered_verts(const BMEdge *edge, BMVert **r_v1, BMVert **r_v2)
int BM_vert_edge_count_at_most(const BMVert *v, const int count_max)
bool BM_vert_is_manifold(const BMVert *v)
bool BM_edge_face_pair(BMEdge *e, BMFace **r_fa, BMFace **r_fb)
BMEdge * BM_edge_exists(BMVert *v_a, BMVert *v_b)
BMLoop * BM_face_other_vert_loop(BMFace *f, BMVert *v_prev, BMVert *v)
Other Loop in Face Sharing a Vertex.
bool BM_edge_share_face_check(BMEdge *e1, BMEdge *e2)
BMLoop * BM_face_edge_share_loop(BMFace *f, BMEdge *e)
Return the Loop Shared by Face and Edge.
BMLoop * BM_face_vert_share_loop(BMFace *f, BMVert *v)
Return the Loop Shared by Face and Vertex.
BLI_INLINE bool BM_edge_is_contiguous(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE bool BM_edge_is_manifold(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE bool BM_edge_is_boundary(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE bool BM_loop_is_adjacent(const BMLoop *l_a, const BMLoop *l_b) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE BMVert * BM_edge_other_vert(BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
#define BM_vert_face_count_is_equal(v, n)
BLI_INLINE bool BM_edge_is_wire(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE bool BM_vert_in_edge(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMLoop * l_b
ATTR_WARN_UNUSED_RESULT const BMVert * v
BLI_INLINE BMEdge * bmesh_disk_edge_next(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
long long int int64_t
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
int64_t size() const
void append(const T &value)
nullptr float
#define cos
BLI_INLINE float fb(float length, float L)
static char faces[256]
BMHeader head
struct BMLoop * l
BMHeader head
void * data
BMHeader head
struct BMVert * v
struct BMEdge * e
struct BMLoop * radial_next
struct BMFace * f
struct BMLoop * next
float co[3]
i
Definition text_draw.cc:230
uint len