Blender V4.3
bmesh_core.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#include "MEM_guardedalloc.h"
12
13#include "BLI_alloca.h"
14#include "BLI_linklist_stack.h"
15#include "BLI_math_vector.h"
17#include "BLI_vector.hh"
18
19#include "BLT_translation.hh"
20
21#include "BKE_customdata.hh"
22#include "BKE_mesh.hh"
23
24#include "bmesh.hh"
26
27using blender::Vector;
28
29/* use so valgrinds memcheck alerts us when undefined index is used.
30 * TESTING ONLY! */
31// #define USE_DEBUG_INDEX_MEMCHECK
32
33#ifdef USE_DEBUG_INDEX_MEMCHECK
34# define DEBUG_MEMCHECK_INDEX_INVALIDATE(ele) \
35 { \
36 int undef_idx; \
37 BM_elem_index_set(ele, undef_idx); /* set_ok_invalid */ \
38 } \
39 (void)0
40
41#endif
42
44 const float co[3],
45 const BMVert *v_example,
46 const eBMCreateFlag create_flag)
47{
48 BMVert *v = static_cast<BMVert *>(BLI_mempool_alloc(bm->vpool));
49
50 BLI_assert((v_example == nullptr) || (v_example->head.htype == BM_VERT));
51 BLI_assert(!(create_flag & 1));
52
53 /* --- assign all members --- */
54 v->head.data = nullptr;
55
56#ifdef USE_DEBUG_INDEX_MEMCHECK
57 DEBUG_MEMCHECK_INDEX_INVALIDATE(v);
58#else
59 BM_elem_index_set(v, -1); /* set_ok_invalid */
60#endif
61
63 v->head.hflag = 0;
64 v->head.api_flag = 0;
65
66 /* allocate flags */
67 if (bm->use_toolflags) {
68 ((BMVert_OFlag *)v)->oflags = static_cast<BMFlagLayer *>(
70 }
71
72 /* 'v->no' is handled by BM_elem_attrs_copy */
73 if (co) {
74 copy_v3_v3(v->co, co);
75 }
76 else {
77 zero_v3(v->co);
78 }
79 /* 'v->no' set below */
80
81 v->e = nullptr;
82 /* --- done --- */
83
84 /* disallow this flag for verts - its meaningless */
85 BLI_assert((create_flag & BM_CREATE_NO_DOUBLE) == 0);
86
87 /* may add to middle of the pool */
91
92 bm->totvert++;
93
94 if (!(create_flag & BM_CREATE_SKIP_CD)) {
95 if (v_example) {
96 int *keyi;
97
98 /* handles 'v->no' too */
99 BM_elem_attrs_copy(bm, v_example, v);
100
101 /* Exception: don't copy the original shape-key index. */
102 keyi = static_cast<int *>(CustomData_bmesh_get(&bm->vdata, v->head.data, CD_SHAPE_KEYINDEX));
103 if (keyi) {
104 *keyi = ORIGINDEX_NONE;
105 }
106 }
107 else {
109 zero_v3(v->no);
110 }
111 }
112 else {
113 if (v_example) {
114 copy_v3_v3(v->no, v_example->no);
115 }
116 else {
117 zero_v3(v->no);
118 }
119 }
120
122
123 return v;
124}
125
127 BMesh *bm, BMVert *v1, BMVert *v2, const BMEdge *e_example, const eBMCreateFlag create_flag)
128{
129 BMEdge *e;
130
131 BLI_assert(v1 != v2);
133 BLI_assert((e_example == nullptr) || (e_example->head.htype == BM_EDGE));
134 BLI_assert(!(create_flag & 1));
135
136 if ((create_flag & BM_CREATE_NO_DOUBLE) && (e = BM_edge_exists(v1, v2))) {
137 return e;
138 }
139
140 e = static_cast<BMEdge *>(BLI_mempool_alloc(bm->epool));
141
142 /* --- assign all members --- */
143 e->head.data = nullptr;
144
145#ifdef USE_DEBUG_INDEX_MEMCHECK
146 DEBUG_MEMCHECK_INDEX_INVALIDATE(e);
147#else
148 BM_elem_index_set(e, -1); /* set_ok_invalid */
149#endif
150
151 e->head.htype = BM_EDGE;
153 e->head.api_flag = 0;
154
155 /* allocate flags */
156 if (bm->use_toolflags) {
157 ((BMEdge_OFlag *)e)->oflags = static_cast<BMFlagLayer *>(
159 }
160
161 e->v1 = v1;
162 e->v2 = v2;
163 e->l = nullptr;
164
165 memset(&e->v1_disk_link, 0, sizeof(BMDiskLink[2]));
166 /* --- done --- */
167
170
171 /* may add to middle of the pool */
175
176 bm->totedge++;
177
178 if (!(create_flag & BM_CREATE_SKIP_CD)) {
179 if (e_example) {
180 BM_elem_attrs_copy(bm, e_example, e);
181 }
182 else {
184 }
185 }
186
188
189 return e;
190}
191
198 BMVert *v,
199 BMEdge *e,
200 BMFace *f,
201 const BMLoop *l_example,
202 const eBMCreateFlag create_flag)
203{
204 BMLoop *l = nullptr;
205
206 l = static_cast<BMLoop *>(BLI_mempool_alloc(bm->lpool));
207
208 BLI_assert((l_example == nullptr) || (l_example->head.htype == BM_LOOP));
209 BLI_assert(!(create_flag & 1));
210
211#ifndef NDEBUG
212 if (l_example) {
213 /* ensure passing a loop is either sharing the same vertex, or entirely disconnected
214 * use to catch mistake passing in loop offset-by-one. */
215 BLI_assert((v == l_example->v) || !ELEM(v, l_example->prev->v, l_example->next->v));
216 }
217#endif
218
219 /* --- assign all members --- */
220 l->head.data = nullptr;
221
222#ifdef USE_DEBUG_INDEX_MEMCHECK
223 DEBUG_MEMCHECK_INDEX_INVALIDATE(l);
224#else
225 BM_elem_index_set(l, -1); /* set_ok_invalid */
226#endif
227
228 l->head.htype = BM_LOOP;
229 l->head.hflag = 0;
230 l->head.api_flag = 0;
231
232 l->v = v;
233 l->e = e;
234 l->f = f;
235
236 l->radial_next = nullptr;
237 l->radial_prev = nullptr;
238 l->next = nullptr;
239 l->prev = nullptr;
240 /* --- done --- */
241
242 /* may add to middle of the pool */
245
246 bm->totloop++;
247
248 if (!(create_flag & BM_CREATE_SKIP_CD)) {
249 if (l_example) {
250 /* no need to copy attrs, just handle customdata */
252 }
253 else {
255 }
256 }
257
258 return l;
259}
260
262 BMesh *bm, BMFace *f, BMVert *startv, BMEdge *starte, const eBMCreateFlag create_flag)
263{
264#ifdef USE_BMESH_HOLES
265 BMLoopList *lst = BLI_mempool_calloc(bm->looplistpool);
266#endif
267 BMLoop *l = bm_loop_create(bm, startv, starte, f, nullptr /* starte->l */, create_flag);
268
270
271#ifdef USE_BMESH_HOLES
272 lst->first = lst->last = l;
273 BLI_addtail(&f->loops, lst);
274#else
275 f->l_first = l;
276#endif
277
278 return l;
279}
280
282 BMFace *f,
283 const bool copy_verts,
284 const bool copy_edges)
285{
287 BMEdge **edges = BLI_array_alloca(edges, f->len);
288 BMLoop *l_iter;
289 BMLoop *l_first;
290 BMFace *f_copy;
291 int i;
292
293 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
294 i = 0;
295 do {
296 if (copy_verts) {
297 verts[i] = BM_vert_create(bm_dst, l_iter->v->co, l_iter->v, BM_CREATE_NOP);
298 }
299 else {
300 verts[i] = l_iter->v;
301 }
302 i++;
303 } while ((l_iter = l_iter->next) != l_first);
304
305 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
306 i = 0;
307 do {
308 if (copy_edges) {
309 BMVert *v1, *v2;
310
311 if (l_iter->e->v1 == verts[i]) {
312 v1 = verts[i];
313 v2 = verts[(i + 1) % f->len];
314 }
315 else {
316 v2 = verts[i];
317 v1 = verts[(i + 1) % f->len];
318 }
319
320 edges[i] = BM_edge_create(bm_dst, v1, v2, l_iter->e, BM_CREATE_NOP);
321 }
322 else {
323 edges[i] = l_iter->e;
324 }
325 i++;
326 } while ((l_iter = l_iter->next) != l_first);
327
328 f_copy = BM_face_create(bm_dst, verts, edges, f->len, nullptr, BM_CREATE_SKIP_CD);
329
330 return f_copy;
331}
332
334 const BMCustomDataCopyMap &cd_face_map,
335 const BMCustomDataCopyMap &cd_loop_map,
336 BMFace *f,
337 const bool copy_verts,
338 const bool copy_edges)
339{
340 BMFace *f_copy = bm_face_copy_impl(bm_dst, f, copy_verts, copy_edges);
341
342 /* Copy custom-data. */
343 BM_elem_attrs_copy(bm_dst, cd_face_map, f, f_copy);
344
345 BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
346 BMLoop *l_copy = BM_FACE_FIRST_LOOP(f_copy);
347 BMLoop *l_iter = l_first;
348 do {
349 BM_elem_attrs_copy(bm_dst, cd_loop_map, l_iter, l_copy);
350 l_copy = l_copy->next;
351 } while ((l_iter = l_iter->next) != l_first);
352 return f_copy;
353}
354
355BMFace *BM_face_copy(BMesh *bm_dst, BMFace *f, const bool copy_verts, const bool copy_edges)
356{
357 BMFace *f_copy = bm_face_copy_impl(bm_dst, f, copy_verts, copy_edges);
358
359 /* Copy custom-data. */
360 BM_elem_attrs_copy(bm_dst, f, f_copy);
361
362 BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
363 BMLoop *l_copy = BM_FACE_FIRST_LOOP(f_copy);
364 BMLoop *l_iter = l_first;
365 do {
366 BM_elem_attrs_copy(bm_dst, l_iter, l_copy);
367 l_copy = l_copy->next;
368 } while ((l_iter = l_iter->next) != l_first);
369 return f_copy;
370}
371
379{
380 BMFace *f;
381
382 f = static_cast<BMFace *>(BLI_mempool_alloc(bm->fpool));
383
384 /* --- assign all members --- */
385 f->head.data = nullptr;
386#ifdef USE_DEBUG_INDEX_MEMCHECK
387 DEBUG_MEMCHECK_INDEX_INVALIDATE(f);
388#else
389 BM_elem_index_set(f, -1); /* set_ok_invalid */
390#endif
391
392 f->head.htype = BM_FACE;
393 f->head.hflag = 0;
394 f->head.api_flag = 0;
395
396 /* allocate flags */
397 if (bm->use_toolflags) {
398 ((BMFace_OFlag *)f)->oflags = static_cast<BMFlagLayer *>(
400 }
401
402#ifdef USE_BMESH_HOLES
403 BLI_listbase_clear(&f->loops);
404#else
405 f->l_first = nullptr;
406#endif
407 f->len = 0;
408 /* caller must initialize */
409 // zero_v3(f->no);
410 f->mat_nr = 0;
411 /* --- done --- */
412
413 /* may add to middle of the pool */
417
418 bm->totface++;
419
420#ifdef USE_BMESH_HOLES
421 f->totbounds = 0;
422#endif
423
424 return f;
425}
426
428 BMVert *const *verts,
429 BMEdge *const *edges,
430 const int len,
431 const BMFace *f_example,
432 const eBMCreateFlag create_flag)
433{
434 BMFace *f = nullptr;
435 BMLoop *l, *startl, *lastl;
436 int i;
437
438 BLI_assert((f_example == nullptr) || (f_example->head.htype == BM_FACE));
439 BLI_assert(!(create_flag & 1));
440
441 if (len == 0) {
442 /* just return nullptr for now */
443 return nullptr;
444 }
445
446 if (create_flag & BM_CREATE_NO_DOUBLE) {
447 /* Check if face already exists */
449 if (f != nullptr) {
450 return f;
451 }
452 }
453
455
456 startl = lastl = bm_face_boundary_add(bm, f, verts[0], edges[0], create_flag);
457
458 for (i = 1; i < len; i++) {
459 l = bm_loop_create(bm, verts[i], edges[i], f, nullptr /* edges[i]->l */, create_flag);
460
461 bmesh_radial_loop_append(edges[i], l);
462
463 l->prev = lastl;
464 lastl->next = l;
465 lastl = l;
466 }
467
468 startl->prev = lastl;
469 lastl->next = startl;
470
471 f->len = len;
472
473 if (!(create_flag & BM_CREATE_SKIP_CD)) {
474 if (f_example) {
475 BM_elem_attrs_copy(bm, f_example, f);
476 }
477 else {
479 zero_v3(f->no);
480 }
481 }
482 else {
483 if (f_example) {
484 copy_v3_v3(f->no, f_example->no);
485 }
486 else {
487 zero_v3(f->no);
488 }
489 }
490
492
493 return f;
494}
495
497 BMVert **vert_arr,
498 const int len,
499 const BMFace *f_example,
500 const eBMCreateFlag create_flag,
501 const bool create_edges)
502{
503 BMEdge **edge_arr = BLI_array_alloca(edge_arr, len);
504
505 if (create_edges) {
506 BM_edges_from_verts_ensure(bm, edge_arr, vert_arr, len);
507 }
508 else {
509 if (BM_edges_from_verts(edge_arr, vert_arr, len) == false) {
510 return nullptr;
511 }
512 }
513
514 return BM_face_create(bm, vert_arr, edge_arr, len, f_example, create_flag);
515}
516
517#ifndef NDEBUG
518
553
554int bmesh_elem_check(void *element, const char htype)
555{
556 BMHeader *head = static_cast<BMHeader *>(element);
558
559 if (!element) {
560 return IS_NULL;
561 }
562
563 if (head->htype != htype) {
564 return IS_WRONG_TYPE;
565 }
566
567 switch (htype) {
568 case BM_VERT: {
569 BMVert *v = static_cast<BMVert *>(element);
570 if (v->e && v->e->head.htype != BM_EDGE) {
572 }
573 break;
574 }
575 case BM_EDGE: {
576 BMEdge *e = static_cast<BMEdge *>(element);
577 if (e->v1_disk_link.prev == nullptr || e->v2_disk_link.prev == nullptr ||
578 e->v1_disk_link.next == nullptr || e->v2_disk_link.next == nullptr)
579 {
581 }
582
583 if (e->l && e->l->head.htype != BM_LOOP) {
585 }
586 if (e->l && e->l->f->head.htype != BM_FACE) {
588 }
589 if (e->l && (e->l->radial_next == nullptr || e->l->radial_prev == nullptr)) {
591 }
592 if (e->l && e->l->f->len <= 0) {
594 }
595 break;
596 }
597 case BM_LOOP: {
598 BMLoop *l = static_cast<BMLoop *>(element);
599 BMLoop *l2;
600 int i;
601
602 if (l->f->head.htype != BM_FACE) {
604 }
605 if (l->e->head.htype != BM_EDGE) {
607 }
608 if (l->v->head.htype != BM_VERT) {
610 }
611 if (!BM_vert_in_edge(l->e, l->v)) {
612 fprintf(stderr,
613 "%s: fatal bmesh error (vert not in edge)! (bmesh internal error)\n",
614 __func__);
616 }
617
618 if (l->radial_next == nullptr || l->radial_prev == nullptr) {
620 }
621 if (l->f->len <= 0) {
623 }
624
625 /* validate boundary loop -- invalid for hole loops, of course,
626 * but we won't be allowing those for a while yet */
627 l2 = l;
628 i = 0;
629 do {
630 if (i >= BM_NGON_MAX) {
631 break;
632 }
633
634 i++;
635 } while ((l2 = l2->next) != l);
636
637 if (i != l->f->len || l2 != l) {
639 }
640
643 }
644
645 break;
646 }
647 case BM_FACE: {
648 BMFace *f = static_cast<BMFace *>(element);
649 BMLoop *l_iter;
650 BMLoop *l_first;
651 int len = 0;
652
653# ifdef USE_BMESH_HOLES
654 if (!f->loops.first)
655# else
656 if (!f->l_first)
657# endif
658 {
659 err |= IS_FACE_NULL_LOOP;
660 }
661 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
662 do {
663 if (l_iter->f != f) {
664 fprintf(stderr,
665 "%s: loop inside one face points to another! (bmesh internal error)\n",
666 __func__);
668 }
669
670 if (!l_iter->e) {
671 err |= IS_FACE_NULL_EDGE;
672 }
673 if (!l_iter->v) {
674 err |= IS_FACE_NULL_VERT;
675 }
676 if (l_iter->e && l_iter->v) {
677 if (!BM_vert_in_edge(l_iter->e, l_iter->v) ||
678 !BM_vert_in_edge(l_iter->e, l_iter->next->v))
679 {
681 }
682
683 if (!bmesh_radial_validate(bmesh_radial_length(l_iter), l_iter)) {
685 }
686
687 if (bmesh_disk_count_at_most(l_iter->v, 2) < 2) {
689 }
690 }
691
692 /* check for duplicates */
695 }
697 if (l_iter->v) {
700 }
702 }
703 if (l_iter->e) {
706 }
708 }
709
710 len++;
711 } while ((l_iter = l_iter->next) != l_first);
712
713 /* cleanup duplicates flag */
714 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
715 do {
717 if (l_iter->v) {
719 }
720 if (l_iter->e) {
722 }
723 } while ((l_iter = l_iter->next) != l_first);
724
725 if (len != f->len) {
727 }
728 break;
729 }
730 default:
731 BLI_assert(0);
732 break;
733 }
734
735 BMESH_ASSERT(err == 0);
736
737 return err;
738}
739
740#endif /* !NDEBUG */
741
764
787
793{
794 if (bm->act_face == f) {
795 bm->act_face = nullptr;
796 }
797
798 bm->totface--;
802
804
805 if (f->head.data) {
807 }
808
809 if (bm->ftoolflagpool) {
811 }
813}
814
820{
821 bm->totloop--;
824
825 if (l->head.data) {
827 }
828
830}
831
833{
834 BMEdge **edges = BLI_array_alloca(edges, f->len);
835 BMLoop *l_iter;
836 BMLoop *l_first;
837 int i = 0;
838
839 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
840 do {
841 edges[i++] = l_iter->e;
842 } while ((l_iter = l_iter->next) != l_first);
843
844 for (i = 0; i < f->len; i++) {
845 BM_edge_kill(bm, edges[i]);
846 }
847}
848
850{
852 BMLoop *l_iter;
853 BMLoop *l_first;
854 int i = 0;
855
856 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
857 do {
858 verts[i++] = l_iter->v;
859 } while ((l_iter = l_iter->next) != l_first);
860
861 for (i = 0; i < f->len; i++) {
862 BM_vert_kill(bm, verts[i]);
863 }
864}
865
867{
868#ifdef USE_BMESH_HOLES
869 BMLoopList *ls, *ls_next;
870#endif
871
872#ifdef NDEBUG
873 /* check length since we may be removing degenerate faces */
874 if (f->len >= 3) {
876 }
877#endif
878
879#ifdef USE_BMESH_HOLES
880 for (ls = f->loops.first; ls; ls = ls_next)
881#else
882 if (f->l_first)
883#endif
884 {
885 BMLoop *l_iter, *l_next, *l_first;
886
887#ifdef USE_BMESH_HOLES
888 ls_next = ls->next;
889 l_iter = l_first = ls->first;
890#else
891 l_iter = l_first = f->l_first;
892#endif
893
894 do {
895 l_next = l_iter->next;
896
897 bmesh_radial_loop_remove(l_iter->e, l_iter);
898 bm_kill_only_loop(bm, l_iter);
899
900 } while ((l_iter = l_next) != l_first);
901
902#ifdef USE_BMESH_HOLES
903 BLI_mempool_free(bm->looplistpool, ls);
904#endif
905 }
906
908}
909
911{
912#ifdef USE_BMESH_HOLES
913 BMLoopList *ls, *ls_next;
914#endif
915
917
918#ifdef USE_BMESH_HOLES
919 for (ls = f->loops.first; ls; ls = ls_next)
920#else
921 if (f->l_first)
922#endif
923 {
924 BMLoop *l_iter, *l_next, *l_first;
925
926#ifdef USE_BMESH_HOLES
927 ls_next = ls->next;
928 l_iter = l_first = ls->first;
929#else
930 l_iter = l_first = f->l_first;
931#endif
932
933 do {
934 BMEdge *e;
935 l_next = l_iter->next;
936
937 e = l_iter->e;
939 bm_kill_only_loop(bm, l_iter);
940
941 if (e->l == nullptr) {
942 BMVert *v1 = e->v1, *v2 = e->v2;
943
947
948 if (v1->e == nullptr) {
950 }
951 if (v2->e == nullptr) {
953 }
954 }
955 } while ((l_iter = l_next) != l_first);
956
957#ifdef USE_BMESH_HOLES
958 BLI_mempool_free(bm->looplistpool, ls);
959#endif
960 }
961
963}
964
966{
967 while (e->l) {
968 BM_face_kill(bm, e->l->f);
969 }
970
973
975}
976
978{
979 while (v->e) {
980 BM_edge_kill(bm, v->e);
981 }
982
984}
985
986/********** private disk and radial cycle functions ********** */
987
992{
993 BMLoop *l_first = l;
994 int i = 0;
995
996 do {
997 i++;
998 } while ((l = l->next) != l_first);
999
1000 return i;
1001}
1002
1004 BMFace *f,
1005 const int cd_loop_mdisp_offset,
1006 const bool use_loop_mdisp_flip)
1007{
1008 BMLoop *l_first = f->l_first;
1009
1010 /* track previous cycles radial state */
1011 BMEdge *e_prev = l_first->prev->e;
1012 BMLoop *l_prev_radial_next = l_first->prev->radial_next;
1013 BMLoop *l_prev_radial_prev = l_first->prev->radial_prev;
1014 bool is_prev_boundary = l_prev_radial_next == l_prev_radial_next->radial_next;
1015
1016 BMLoop *l_iter = l_first;
1017 do {
1018 BMEdge *e_iter = l_iter->e;
1019 BMLoop *l_iter_radial_next = l_iter->radial_next;
1020 BMLoop *l_iter_radial_prev = l_iter->radial_prev;
1021 bool is_iter_boundary = l_iter_radial_next == l_iter_radial_next->radial_next;
1022
1023#if 0
1024 bmesh_radial_loop_remove(e_iter, l_iter);
1025 bmesh_radial_loop_append(e_prev, l_iter);
1026#else
1027 /* inline loop reversal */
1028 if (is_prev_boundary) {
1029 /* boundary */
1030 l_iter->radial_next = l_iter;
1031 l_iter->radial_prev = l_iter;
1032 }
1033 else {
1034 /* non-boundary, replace radial links */
1035 l_iter->radial_next = l_prev_radial_next;
1036 l_iter->radial_prev = l_prev_radial_prev;
1037 l_prev_radial_next->radial_prev = l_iter;
1038 l_prev_radial_prev->radial_next = l_iter;
1039 }
1040
1041 if (e_iter->l == l_iter) {
1042 e_iter->l = l_iter->next;
1043 }
1044 l_iter->e = e_prev;
1045#endif
1046
1047 std::swap(l_iter->next, l_iter->prev);
1048
1049 if (cd_loop_mdisp_offset != -1) {
1050 MDisps *md = static_cast<MDisps *>(BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_mdisp_offset));
1051 BKE_mesh_mdisp_flip(md, use_loop_mdisp_flip);
1052 }
1053
1054 e_prev = e_iter;
1055 l_prev_radial_next = l_iter_radial_next;
1056 l_prev_radial_prev = l_iter_radial_prev;
1057 is_prev_boundary = is_iter_boundary;
1058
1059 /* step to next (now swapped) */
1060 } while ((l_iter = l_iter->prev) != l_first);
1061
1062#ifndef NDEBUG
1063 /* validate radial */
1064 int i;
1065 for (i = 0, l_iter = l_first; i < f->len; i++, l_iter = l_iter->next) {
1066 BM_CHECK_ELEMENT(l_iter);
1067 BM_CHECK_ELEMENT(l_iter->e);
1068 BM_CHECK_ELEMENT(l_iter->v);
1069 BM_CHECK_ELEMENT(l_iter->f);
1070 }
1071
1073#endif
1074
1075 /* Loop indices are no more valid! */
1077}
1078
1079static void bm_elements_systag_enable(void *veles, int tot, const char api_flag)
1080{
1081 BMHeader **eles = static_cast<BMHeader **>(veles);
1082 int i;
1083
1084 for (i = 0; i < tot; i++) {
1085 BM_ELEM_API_FLAG_ENABLE((BMElemF *)eles[i], api_flag);
1086 }
1087}
1088
1089static void bm_elements_systag_disable(void *veles, int tot, const char api_flag)
1090{
1091 BMHeader **eles = static_cast<BMHeader **>(veles);
1092 int i;
1093
1094 for (i = 0; i < tot; i++) {
1095 BM_ELEM_API_FLAG_DISABLE((BMElemF *)eles[i], api_flag);
1096 }
1097}
1098
1099static int bm_loop_systag_count_radial(BMLoop *l, const char api_flag)
1100{
1101 BMLoop *l_iter = l;
1102 int i = 0;
1103 do {
1104 i += BM_ELEM_API_FLAG_TEST(l_iter->f, api_flag) ? 1 : 0;
1105 } while ((l_iter = l_iter->radial_next) != l);
1106
1107 return i;
1108}
1109
1110static int UNUSED_FUNCTION(bm_vert_systag_count_disk)(BMVert *v, const char api_flag)
1111{
1112 BMEdge *e = v->e;
1113 int i = 0;
1114
1115 if (!e) {
1116 return 0;
1117 }
1118
1119 do {
1120 i += BM_ELEM_API_FLAG_TEST(e, api_flag) ? 1 : 0;
1121 } while ((e = bmesh_disk_edge_next(e, v)) != v->e);
1122
1123 return i;
1124}
1125
1130static bool bm_vert_is_manifold_flagged(BMVert *v, const char api_flag)
1131{
1132 BMEdge *e = v->e;
1133
1134 if (!e) {
1135 return false;
1136 }
1137
1138 do {
1139 BMLoop *l = e->l;
1140
1141 if (!l) {
1142 return false;
1143 }
1144
1145 if (BM_edge_is_boundary(l->e)) {
1146 return false;
1147 }
1148
1149 do {
1150 if (!BM_ELEM_API_FLAG_TEST(l->f, api_flag)) {
1151 return false;
1152 }
1153 } while ((l = l->radial_next) != e->l);
1154 } while ((e = bmesh_disk_edge_next(e, v)) != v->e);
1155
1156 return true;
1157}
1158
1159/* Mid-level Topology Manipulation Functions */
1160
1161BMFace *BM_faces_join(BMesh *bm, BMFace **faces, int totface, const bool do_del)
1162{
1163 BMFace *f, *f_new;
1164#ifdef USE_BMESH_HOLES
1165 BMLoopList *lst;
1166 ListBase holes = {nullptr, nullptr};
1167#endif
1168 BMLoop *l_iter;
1169 BMLoop *l_first;
1170 BMVert *v1 = nullptr, *v2 = nullptr;
1171 int i;
1172 const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
1173
1174 if (UNLIKELY(!totface)) {
1175 BMESH_ASSERT(0);
1176 return nullptr;
1177 }
1178
1179 if (totface == 1) {
1180 return faces[0];
1181 }
1182
1183 bm_elements_systag_enable(faces, totface, _FLAG_JF);
1184
1188
1189 for (i = 0; i < totface; i++) {
1190 f = faces[i];
1191 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
1192 do {
1193 int rlen = bm_loop_systag_count_radial(l_iter, _FLAG_JF);
1194
1195 if (rlen > 2) {
1196 /* Input faces do not form a contiguous manifold region */
1197 goto error;
1198 }
1199 else if (rlen == 1) {
1200 edges.append(l_iter->e);
1201
1202 if (!v1) {
1203 v1 = l_iter->v;
1204 v2 = BM_edge_other_vert(l_iter->e, l_iter->v);
1205 }
1206 }
1207 else if (rlen == 2) {
1208 const bool d1 = bm_vert_is_manifold_flagged(l_iter->e->v1, _FLAG_JF);
1209 const bool d2 = bm_vert_is_manifold_flagged(l_iter->e->v2, _FLAG_JF);
1210
1211 if (!d1 && !d2 && !BM_ELEM_API_FLAG_TEST(l_iter->e, _FLAG_JF)) {
1212 /* don't remove an edge it makes up the side of another face
1213 * else this will remove the face as well - campbell */
1214 if (!BM_edge_face_count_is_over(l_iter->e, 2)) {
1215 if (do_del) {
1216 deledges.append(l_iter->e);
1217 }
1219 }
1220 }
1221 else {
1222 if (d1 && !BM_ELEM_API_FLAG_TEST(l_iter->e->v1, _FLAG_JF)) {
1223 if (do_del) {
1224 delverts.append(l_iter->e->v1);
1225 }
1227 }
1228
1229 if (d2 && !BM_ELEM_API_FLAG_TEST(l_iter->e->v2, _FLAG_JF)) {
1230 if (do_del) {
1231 delverts.append(l_iter->e->v2);
1232 }
1234 }
1235 }
1236 }
1237 } while ((l_iter = l_iter->next) != l_first);
1238
1239#ifdef USE_BMESH_HOLES
1240 for (lst = f->loops.first; lst; lst = lst->next) {
1241 if (lst == f->loops.first) {
1242 continue;
1243 }
1244
1245 BLI_remlink(&f->loops, lst);
1246 BLI_addtail(&holes, lst);
1247 }
1248#endif
1249 }
1250
1251 /* create region face */
1252 f_new = !edges.is_empty() ?
1254 bm, v1, v2, edges.data(), edges.size(), faces[0], BM_CREATE_NOP) :
1255 nullptr;
1256 if (UNLIKELY(f_new == nullptr)) {
1257 /* Invalid boundary region to join faces */
1258 goto error;
1259 }
1260
1261 /* copy over loop data */
1262 l_iter = l_first = BM_FACE_FIRST_LOOP(f_new);
1263 do {
1264 BMLoop *l2 = l_iter->radial_next;
1265
1266 do {
1267 if (BM_ELEM_API_FLAG_TEST(l2->f, _FLAG_JF)) {
1268 break;
1269 }
1270 l2 = l2->radial_next;
1271 } while (l2 != l_iter);
1272
1273 if (l2 != l_iter) {
1274 /* loops share an edge, shared vert depends on winding */
1275 if (l2->v != l_iter->v) {
1276 l2 = l2->next;
1277 }
1278 BLI_assert(l_iter->v == l2->v);
1279
1280 BM_elem_attrs_copy(bm, l2, l_iter);
1281 }
1282 } while ((l_iter = l_iter->next) != l_first);
1283
1284#ifdef USE_BMESH_HOLES
1285 /* add holes */
1286 BLI_movelisttolist(&f_new->loops, &holes);
1287
1288 /* update loop face pointer */
1289 for (lst = f_new->loops.first; lst; lst = lst->next) {
1290 l_iter = l_first = lst->first;
1291 do {
1292 l_iter->f = f_new;
1293 } while ((l_iter = l_iter->next) != l_first);
1294 }
1295#endif
1296
1297 bm_elements_systag_disable(faces, totface, _FLAG_JF);
1299
1300 /* handle multi-res data */
1301 if (cd_loop_mdisp_offset != -1) {
1302 float f_center[3];
1303 float(*faces_center)[3] = BLI_array_alloca(faces_center, totface);
1304
1305 BM_face_calc_center_median(f_new, f_center);
1306 for (i = 0; i < totface; i++) {
1307 BM_face_calc_center_median(faces[i], faces_center[i]);
1308 }
1309
1310 l_iter = l_first = BM_FACE_FIRST_LOOP(f_new);
1311 do {
1312 for (i = 0; i < totface; i++) {
1314 bm, l_iter, faces[i], f_center, faces_center[i], cd_loop_mdisp_offset);
1315 }
1316 } while ((l_iter = l_iter->next) != l_first);
1317 }
1318
1319 /* delete old geometry */
1320 if (do_del) {
1321 for (BMEdge *edge : deledges) {
1322 BM_edge_kill(bm, edge);
1323 }
1324
1325 for (BMVert *vert : delverts) {
1326 BM_vert_kill(bm, vert);
1327 }
1328 }
1329 else {
1330 /* otherwise we get both old and new faces */
1331 for (i = 0; i < totface; i++) {
1332 BM_face_kill(bm, faces[i]);
1333 }
1334 }
1335
1336 BM_CHECK_ELEMENT(f_new);
1337 return f_new;
1338
1339error:
1340 bm_elements_systag_disable(faces, totface, _FLAG_JF);
1341
1342 return nullptr;
1343}
1344
1346{
1347 BMFace *f;
1348#ifdef USE_BMESH_HOLES
1349 BMLoopList *lst;
1350#endif
1351
1353
1354#ifdef USE_BMESH_HOLES
1355 lst = BLI_mempool_calloc(bm->looplistpool);
1356 BLI_addtail(&f->loops, lst);
1357#endif
1358
1359#ifdef USE_BMESH_HOLES
1360 f->totbounds = 1;
1361#endif
1362
1363 BM_elem_attrs_copy(bm, f_example, f);
1364
1365 return f;
1366}
1367
1369 BMFace *f,
1370 BMLoop *l_v1,
1371 BMLoop *l_v2,
1372 BMLoop **r_l,
1373#ifdef USE_BMESH_HOLES
1374 ListBase *holes,
1375#endif
1376 BMEdge *e_example,
1377 const bool no_double)
1378{
1379#ifdef USE_BMESH_HOLES
1380 BMLoopList *lst, *lst2;
1381#else
1382 int first_loop_f1;
1383#endif
1384
1385 BMFace *f2;
1386 BMLoop *l_iter, *l_first;
1387 BMLoop *l_f1 = nullptr, *l_f2 = nullptr;
1388 BMEdge *e;
1389 BMVert *v1 = l_v1->v, *v2 = l_v2->v;
1390 int f1len, f2len;
1391
1392 BLI_assert(f == l_v1->f && f == l_v2->f);
1393
1394 /* allocate new edge between v1 and v2 */
1395 e = BM_edge_create(bm, v1, v2, e_example, no_double ? BM_CREATE_NO_DOUBLE : BM_CREATE_NOP);
1396
1397 f2 = bm_face_create__sfme(bm, f);
1398 l_f1 = bm_loop_create(bm, v2, e, f, l_v2, eBMCreateFlag(0));
1399 l_f2 = bm_loop_create(bm, v1, e, f2, l_v1, eBMCreateFlag(0));
1400
1401 l_f1->prev = l_v2->prev;
1402 l_f2->prev = l_v1->prev;
1403 l_v2->prev->next = l_f1;
1404 l_v1->prev->next = l_f2;
1405
1406 l_f1->next = l_v1;
1407 l_f2->next = l_v2;
1408 l_v1->prev = l_f1;
1409 l_v2->prev = l_f2;
1410
1411#ifdef USE_BMESH_HOLES
1412 lst = f->loops.first;
1413 lst2 = f2->loops.first;
1414
1415 lst2->first = lst2->last = l_f2;
1416 lst->first = lst->last = l_f1;
1417#else
1418 /* find which of the faces the original first loop is in */
1419 l_iter = l_first = l_f1;
1420 first_loop_f1 = 0;
1421 do {
1422 if (l_iter == f->l_first) {
1423 first_loop_f1 = 1;
1424 }
1425 } while ((l_iter = l_iter->next) != l_first);
1426
1427 if (first_loop_f1) {
1428 /* Original first loop was in f1, find a suitable first loop for f2
1429 * which is as similar as possible to f1. the order matters for tools
1430 * such as dupli-faces. */
1431 if (f->l_first->prev == l_f1) {
1432 f2->l_first = l_f2->prev;
1433 }
1434 else if (f->l_first->next == l_f1) {
1435 f2->l_first = l_f2->next;
1436 }
1437 else {
1438 f2->l_first = l_f2;
1439 }
1440 }
1441 else {
1442 /* original first loop was in f2, further do same as above */
1443 f2->l_first = f->l_first;
1444
1445 if (f->l_first->prev == l_f2) {
1446 f->l_first = l_f1->prev;
1447 }
1448 else if (f->l_first->next == l_f2) {
1449 f->l_first = l_f1->next;
1450 }
1451 else {
1452 f->l_first = l_f1;
1453 }
1454 }
1455#endif
1456
1457 /* validate both loop */
1458 /* I don't know how many loops are supposed to be in each face at this point! FIXME */
1459
1460 /* go through all of f2's loops and make sure they point to it properly */
1461 l_iter = l_first = BM_FACE_FIRST_LOOP(f2);
1462 f2len = 0;
1463 do {
1464 l_iter->f = f2;
1465 f2len++;
1466 } while ((l_iter = l_iter->next) != l_first);
1467
1468 /* link up the new loops into the new edges radial */
1471
1472 f2->len = f2len;
1473
1474 f1len = 0;
1475 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
1476 do {
1477 f1len++;
1478 } while ((l_iter = l_iter->next) != l_first);
1479
1480 f->len = f1len;
1481
1482 if (r_l) {
1483 *r_l = l_f2;
1484 }
1485
1486#ifdef USE_BMESH_HOLES
1487 if (holes) {
1488 BLI_movelisttolist(&f2->loops, holes);
1489 }
1490 else {
1491 /* this code is not significant until holes actually work */
1492 // printf("WARNING: call to split face euler without holes argument; holes will be tossed.\n");
1493 for (lst = f->loops.last; lst != f->loops.first; lst = lst2) {
1494 lst2 = lst->prev;
1495 BLI_mempool_free(bm->looplistpool, lst);
1496 }
1497 }
1498#endif
1499
1502 BM_CHECK_ELEMENT(f2);
1503
1504 return f2;
1505}
1506
1508{
1509 BMLoop *l_next;
1510 BMEdge *e_new;
1511 BMVert *v_new, *v_old;
1512#ifndef NDEBUG
1513 int valence1, valence2;
1514 bool edok;
1515 int i;
1516#endif
1517
1518 BLI_assert(BM_vert_in_edge(e, tv) != false);
1519
1520 v_old = BM_edge_other_vert(e, tv);
1521
1522#ifndef NDEBUG
1523 valence1 = bmesh_disk_count(v_old);
1524 valence2 = bmesh_disk_count(tv);
1525#endif
1526
1527 /* order of 'e_new' verts should match 'e'
1528 * (so extruded faces don't flip) */
1529 v_new = BM_vert_create(bm, tv->co, tv, BM_CREATE_NOP);
1530 e_new = BM_edge_create(bm, tv, v_new, e, BM_CREATE_NOP);
1531
1532 bmesh_disk_edge_remove(e_new, tv);
1533 bmesh_disk_edge_remove(e_new, v_new);
1534
1535 bmesh_disk_vert_replace(e, v_new, tv);
1536
1537 /* add e_new to v_new's disk cycle */
1538 bmesh_disk_edge_append(e_new, v_new);
1539
1540 /* add e_new to tv's disk cycle */
1541 bmesh_disk_edge_append(e_new, tv);
1542
1543#ifndef NDEBUG
1544 /* verify disk cycles */
1545 edok = bmesh_disk_validate(valence1, v_old->e, v_old);
1546 BMESH_ASSERT(edok != false);
1547 edok = bmesh_disk_validate(valence2, tv->e, tv);
1548 BMESH_ASSERT(edok != false);
1549 edok = bmesh_disk_validate(2, v_new->e, v_new);
1550 BMESH_ASSERT(edok != false);
1551#endif
1552
1553 /* Split the radial cycle if present */
1554 l_next = e->l;
1555 e->l = nullptr;
1556 if (l_next) {
1557 BMLoop *l_new, *l;
1558#ifndef NDEBUG
1559 int radlen = bmesh_radial_length(l_next);
1560#endif
1561 bool is_first = true;
1562
1563 /* Take the next loop. Remove it from radial. Split it. Append to appropriate radials */
1564 while (l_next) {
1565 l = l_next;
1566 l->f->len++;
1567 l_next = l_next != l_next->radial_next ? l_next->radial_next : nullptr;
1569
1570 l_new = bm_loop_create(bm, nullptr, nullptr, l->f, l, eBMCreateFlag(0));
1571 l_new->prev = l;
1572 l_new->next = l->next;
1573 l_new->prev->next = l_new;
1574 l_new->next->prev = l_new;
1575 l_new->v = v_new;
1576
1577 /* assign the correct edge to the correct loop */
1578 if (BM_verts_in_edge(l_new->v, l_new->next->v, e)) {
1579 l_new->e = e;
1580 l->e = e_new;
1581
1582 /* append l into e_new's rad cycle */
1583 if (is_first) {
1584 is_first = false;
1585 l->radial_next = l->radial_prev = nullptr;
1586 }
1587
1588 bmesh_radial_loop_append(l_new->e, l_new);
1590 }
1591 else if (BM_verts_in_edge(l_new->v, l_new->next->v, e_new)) {
1592 l_new->e = e_new;
1593 l->e = e;
1594
1595 /* append l into e_new's rad cycle */
1596 if (is_first) {
1597 is_first = false;
1598 l->radial_next = l->radial_prev = nullptr;
1599 }
1600
1601 bmesh_radial_loop_append(l_new->e, l_new);
1603 }
1604 }
1605
1606#ifndef NDEBUG
1607 /* verify length of radial cycle */
1608 edok = bmesh_radial_validate(radlen, e->l);
1609 BMESH_ASSERT(edok != false);
1610 edok = bmesh_radial_validate(radlen, e_new->l);
1611 BMESH_ASSERT(edok != false);
1612
1613 /* verify loop->v and loop->next->v pointers for e */
1614 for (i = 0, l = e->l; i < radlen; i++, l = l->radial_next) {
1615 BMESH_ASSERT(l->e == e);
1616 // BMESH_ASSERT(l->radial_next == l);
1617 BMESH_ASSERT(!(l->prev->e != e_new && l->next->e != e_new));
1618
1619 edok = BM_verts_in_edge(l->v, l->next->v, e);
1620 BMESH_ASSERT(edok != false);
1621 BMESH_ASSERT(l->v != l->next->v);
1622 BMESH_ASSERT(l->e != l->next->e);
1623
1624 /* verify loop cycle for kloop->f */
1629 }
1630 /* verify loop->v and loop->next->v pointers for e_new */
1631 for (i = 0, l = e_new->l; i < radlen; i++, l = l->radial_next) {
1632 BMESH_ASSERT(l->e == e_new);
1633 // BMESH_ASSERT(l->radial_next == l);
1634 BMESH_ASSERT(!(l->prev->e != e && l->next->e != e));
1635 edok = BM_verts_in_edge(l->v, l->next->v, e_new);
1636 BMESH_ASSERT(edok != false);
1637 BMESH_ASSERT(l->v != l->next->v);
1638 BMESH_ASSERT(l->e != l->next->e);
1639
1644 }
1645#endif
1646 }
1647
1648 BM_CHECK_ELEMENT(e_new);
1649 BM_CHECK_ELEMENT(v_new);
1650 BM_CHECK_ELEMENT(v_old);
1652 BM_CHECK_ELEMENT(tv);
1653
1654 if (r_e) {
1655 *r_e = e_new;
1656 }
1657 return v_new;
1658}
1659
1661 BMEdge *e_kill,
1662 BMVert *v_kill,
1663 const bool do_del,
1664 const bool check_edge_exists,
1665 const bool kill_degenerate_faces,
1666 const bool kill_duplicate_faces)
1667{
1668 BMEdge *e_old;
1669 BMVert *v_old, *v_target;
1670 BMLoop *l_kill;
1671#ifndef NDEBUG
1672 int radlen, i;
1673 bool edok;
1674#endif
1675
1676 BLI_assert(BM_vert_in_edge(e_kill, v_kill));
1677
1678 if (BM_vert_in_edge(e_kill, v_kill) == 0) {
1679 return nullptr;
1680 }
1681
1682 if (bmesh_disk_count_at_most(v_kill, 3) == 2) {
1683#ifndef NDEBUG
1684 int valence1, valence2;
1685 BMLoop *l;
1686#endif
1687
1688 e_old = bmesh_disk_edge_next(e_kill, v_kill);
1689 v_target = BM_edge_other_vert(e_kill, v_kill);
1690 v_old = BM_edge_other_vert(e_old, v_kill);
1691
1692 /* check for double edges */
1693 if (BM_verts_in_edge(v_kill, v_target, e_old)) {
1694 return nullptr;
1695 }
1696
1697 BMEdge *e_splice;
1698 BLI_SMALLSTACK_DECLARE(faces_degenerate, BMFace *);
1699 BMLoop *l_kill_next;
1700
1701 /* Candidates for being duplicate. */
1702 BLI_SMALLSTACK_DECLARE(faces_duplicate_candidate, BMFace *);
1703
1704#ifndef NDEBUG
1705 /* For verification later, count valence of 'v_old' and 'v_target' */
1706 valence1 = bmesh_disk_count(v_old);
1707 valence2 = bmesh_disk_count(v_target);
1708#endif
1709
1710 if (check_edge_exists) {
1711 e_splice = BM_edge_exists(v_target, v_old);
1712 }
1713
1714 bmesh_disk_vert_replace(e_old, v_target, v_kill);
1715
1716 /* remove e_kill from 'v_target's disk cycle */
1717 bmesh_disk_edge_remove(e_kill, v_target);
1718
1719#ifndef NDEBUG
1720 /* deal with radial cycle of e_kill */
1721 radlen = bmesh_radial_length(e_kill->l);
1722#endif
1723 if (e_kill->l) {
1724
1725 /* fix the neighboring loops of all loops in e_kill's radial cycle */
1726 l_kill = e_kill->l;
1727 do {
1728 /* relink loops and fix vertex pointer */
1729 if (l_kill->next->v == v_kill) {
1730 l_kill->next->v = v_target;
1731 }
1732
1733 l_kill->next->prev = l_kill->prev;
1734 l_kill->prev->next = l_kill->next;
1735 if (BM_FACE_FIRST_LOOP(l_kill->f) == l_kill) {
1736 BM_FACE_FIRST_LOOP(l_kill->f) = l_kill->next;
1737 }
1738
1739 /* fix len attribute of face */
1740 l_kill->f->len--;
1741 if (kill_degenerate_faces && (l_kill->f->len < 3)) {
1742 BLI_SMALLSTACK_PUSH(faces_degenerate, l_kill->f);
1743 }
1744 else {
1745 /* The duplicate test isn't reliable at this point as `e_splice` might be set,
1746 * so the duplicate test needs to run once the edge has been spliced. */
1747 if (kill_duplicate_faces) {
1748 BLI_SMALLSTACK_PUSH(faces_duplicate_candidate, l_kill->f);
1749 }
1750 }
1751 l_kill_next = l_kill->radial_next;
1752
1753 bm_kill_only_loop(bm, l_kill);
1754
1755 } while ((l_kill = l_kill_next) != e_kill->l);
1756/* `e_kill->l` is invalid but the edge is freed next. */
1757#ifndef NDEBUG
1758 /* Validate radial cycle of e_old */
1759 edok = bmesh_radial_validate(radlen, e_old->l);
1760 BMESH_ASSERT(edok != false);
1761#endif
1762 }
1763 /* deallocate edge */
1764 bm_kill_only_edge(bm, e_kill);
1765
1766 /* deallocate vertex */
1767 if (do_del) {
1768 bm_kill_only_vert(bm, v_kill);
1769 }
1770 else {
1771 v_kill->e = nullptr;
1772 }
1773
1774#ifndef NDEBUG
1775 /* Validate disk cycle lengths of 'v_old', 'v_target' are unchanged */
1776 edok = bmesh_disk_validate(valence1, v_old->e, v_old);
1777 BMESH_ASSERT(edok != false);
1778 edok = bmesh_disk_validate(valence2, v_target->e, v_target);
1779 BMESH_ASSERT(edok != false);
1780
1781 /* Validate loop cycle of all faces attached to 'e_old' */
1782 for (i = 0, l = e_old->l; i < radlen; i++, l = l->radial_next) {
1783 BMESH_ASSERT(l->e == e_old);
1784 edok = BM_verts_in_edge(l->v, l->next->v, e_old);
1785 BMESH_ASSERT(edok != false);
1786 edok = bmesh_loop_validate(l->f);
1787 BMESH_ASSERT(edok != false);
1788
1793 }
1794#endif
1795 if (check_edge_exists) {
1796 if (e_splice) {
1797 /* removes e_splice */
1798 BM_edge_splice(bm, e_old, e_splice);
1799 }
1800 }
1801
1802 if (kill_degenerate_faces) {
1803 BMFace *f_kill;
1804 while ((f_kill = static_cast<BMFace *>(BLI_SMALLSTACK_POP(faces_degenerate)))) {
1805 BM_face_kill(bm, f_kill);
1806 }
1807 }
1808
1809 if (kill_duplicate_faces) {
1810 BMFace *f_kill;
1811 while ((f_kill = static_cast<BMFace *>(BLI_SMALLSTACK_POP(faces_duplicate_candidate)))) {
1812 if (BM_face_find_double(f_kill)) {
1813 BM_face_kill(bm, f_kill);
1814 }
1815 }
1816 }
1817
1818 BM_CHECK_ELEMENT(v_old);
1819 BM_CHECK_ELEMENT(v_target);
1820 BM_CHECK_ELEMENT(e_old);
1821
1822 return e_old;
1823 }
1824 return nullptr;
1825}
1826
1828 BMEdge *e_kill,
1829 BMVert *v_kill,
1830 const bool do_del,
1831 const bool check_edge_exists,
1832 const bool kill_degenerate_faces)
1833{
1834 BLI_SMALLSTACK_DECLARE(faces_degenerate, BMFace *);
1835 BMVert *v_target = BM_edge_other_vert(e_kill, v_kill);
1836
1837 BLI_assert(BM_vert_in_edge(e_kill, v_kill));
1838
1839 if (e_kill->l) {
1840 BMLoop *l_kill, *l_first, *l_kill_next;
1841 l_kill = l_first = e_kill->l;
1842 do {
1843 /* relink loops and fix vertex pointer */
1844 if (l_kill->next->v == v_kill) {
1845 l_kill->next->v = v_target;
1846 }
1847
1848 l_kill->next->prev = l_kill->prev;
1849 l_kill->prev->next = l_kill->next;
1850 if (BM_FACE_FIRST_LOOP(l_kill->f) == l_kill) {
1851 BM_FACE_FIRST_LOOP(l_kill->f) = l_kill->next;
1852 }
1853
1854 /* fix len attribute of face */
1855 l_kill->f->len--;
1856 if (kill_degenerate_faces) {
1857 if (l_kill->f->len < 3) {
1858 BLI_SMALLSTACK_PUSH(faces_degenerate, l_kill->f);
1859 }
1860 }
1861 l_kill_next = l_kill->radial_next;
1862
1863 bm_kill_only_loop(bm, l_kill);
1864
1865 } while ((l_kill = l_kill_next) != l_first);
1866
1867 e_kill->l = nullptr;
1868 }
1869
1870 BM_edge_kill(bm, e_kill);
1871 BM_CHECK_ELEMENT(v_kill);
1872 BM_CHECK_ELEMENT(v_target);
1873
1874 if (v_target->e && v_kill->e) {
1875 /* Inline `BM_vert_splice(bm, v_target, v_kill)`. */
1876 BMEdge *e;
1877 while ((e = v_kill->e)) {
1878 BMEdge *e_target;
1879
1880 if (check_edge_exists) {
1881 e_target = BM_edge_exists(v_target, BM_edge_other_vert(e, v_kill));
1882 }
1883
1884 bmesh_edge_vert_swap(e, v_target, v_kill);
1885 BLI_assert(e->v1 != e->v2);
1886
1887 if (check_edge_exists) {
1888 if (e_target) {
1889 BM_edge_splice(bm, e_target, e);
1890 }
1891 }
1892 }
1893 }
1894
1895 if (kill_degenerate_faces) {
1896 BMFace *f_kill;
1897 while ((f_kill = static_cast<BMFace *>(BLI_SMALLSTACK_POP(faces_degenerate)))) {
1898 BM_face_kill(bm, f_kill);
1899 }
1900 }
1901
1902 if (do_del) {
1903 BLI_assert(v_kill->e == nullptr);
1904 bm_kill_only_vert(bm, v_kill);
1905 }
1906
1907 return v_target;
1908}
1909
1911{
1912 BMLoop *l_iter, *l_f1 = nullptr, *l_f2 = nullptr;
1913 int newlen = 0, i, f1len = 0, f2len = 0;
1914 bool edok;
1915 /* can't join a face to itself */
1916 if (f1 == f2) {
1917 return nullptr;
1918 }
1919
1920 /* validate that edge is 2-manifold edge */
1921 if (!BM_edge_is_manifold(e)) {
1922 return nullptr;
1923 }
1924
1925 /* verify that e is in both f1 and f2 */
1926 f1len = f1->len;
1927 f2len = f2->len;
1928
1929 if (!((l_f1 = BM_face_edge_share_loop(f1, e)) && (l_f2 = BM_face_edge_share_loop(f2, e)))) {
1930 return nullptr;
1931 }
1932
1933 /* validate direction of f2's loop cycle is compatible */
1934 if (l_f1->v == l_f2->v) {
1935 return nullptr;
1936 }
1937
1938 /* validate that for each face, each vertex has another edge in its disk cycle that is
1939 * not e, and not shared. */
1940 if (BM_edge_in_face(l_f1->next->e, f2) || BM_edge_in_face(l_f1->prev->e, f2) ||
1941 BM_edge_in_face(l_f2->next->e, f1) || BM_edge_in_face(l_f2->prev->e, f1))
1942 {
1943 return nullptr;
1944 }
1945
1946 /* validate only one shared edge */
1947 if (BM_face_share_edge_count(f1, f2) > 1) {
1948 return nullptr;
1949 }
1950
1951 /* validate no internal join */
1952 {
1953 bool is_dupe = false;
1954
1955 /* TODO: skip clearing once this is ensured. */
1956 for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f2); i < f2len; i++, l_iter = l_iter->next) {
1958 }
1959
1960 for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f1); i < f1len; i++, l_iter = l_iter->next) {
1961 BM_elem_flag_set(l_iter->v, BM_ELEM_INTERNAL_TAG, l_iter != l_f1);
1962 }
1963 for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f2); i < f2len; i++, l_iter = l_iter->next) {
1964 if (l_iter != l_f2) {
1965 /* as soon as a duplicate is found, bail out */
1966 if (BM_elem_flag_test(l_iter->v, BM_ELEM_INTERNAL_TAG)) {
1967 is_dupe = true;
1968 break;
1969 }
1970 }
1971 }
1972 /* Cleanup tags. */
1973 for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f1); i < f1len; i++, l_iter = l_iter->next) {
1975 }
1976 if (is_dupe) {
1977 return nullptr;
1978 }
1979 }
1980
1981 /* join the two loop */
1982 l_f1->prev->next = l_f2->next;
1983 l_f2->next->prev = l_f1->prev;
1984
1985 l_f1->next->prev = l_f2->prev;
1986 l_f2->prev->next = l_f1->next;
1987
1988 /* If `l_f1` was base-loop, make `l_f1->next` the base. */
1989 if (BM_FACE_FIRST_LOOP(f1) == l_f1) {
1990 BM_FACE_FIRST_LOOP(f1) = l_f1->next;
1991 }
1992
1993 /* increase length of f1 */
1994 f1->len += (f2->len - 2);
1995
1996 /* make sure each loop points to the proper face */
1997 newlen = f1->len;
1998 for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f1); i < newlen; i++, l_iter = l_iter->next) {
1999 l_iter->f = f1;
2000 }
2001
2002 /* remove edge from the disk cycle of its two vertices */
2003 bmesh_disk_edge_remove(l_f1->e, l_f1->e->v1);
2004 bmesh_disk_edge_remove(l_f1->e, l_f1->e->v2);
2005
2006 /* deallocate edge and its two loops as well as f2 */
2007 if (bm->etoolflagpool) {
2008 BLI_mempool_free(bm->etoolflagpool, ((BMEdge_OFlag *)l_f1->e)->oflags);
2009 }
2010 BLI_mempool_free(bm->epool, l_f1->e);
2011 bm->totedge--;
2012 BLI_mempool_free(bm->lpool, l_f1);
2013 bm->totloop--;
2014 BLI_mempool_free(bm->lpool, l_f2);
2015 bm->totloop--;
2016 if (bm->ftoolflagpool) {
2017 BLI_mempool_free(bm->ftoolflagpool, ((BMFace_OFlag *)f2)->oflags);
2018 }
2020 bm->totface--;
2021 /* account for both above */
2023
2024 BM_CHECK_ELEMENT(f1);
2025
2026 /* validate the new loop cycle */
2027 edok = bmesh_loop_validate(f1);
2028 BMESH_ASSERT(edok != false);
2029
2030 return f1;
2031}
2032
2034{
2035 bool is_double = false;
2036
2037 BLI_assert(BM_edge_exists(v_a, v_b) == nullptr);
2038
2039 if (v_a->e && v_b->e) {
2040 BMEdge *e, *e_first;
2041
2042#define VERT_VISIT _FLAG_WALK
2043
2044 /* tag 'v_a' */
2045 e = e_first = v_a->e;
2046 do {
2047 BMVert *v_other = BM_edge_other_vert(e, v_a);
2050 } while ((e = BM_DISK_EDGE_NEXT(e, v_a)) != e_first);
2051
2052 /* check 'v_b' connects to 'v_a' edges */
2053 e = e_first = v_b->e;
2054 do {
2055 BMVert *v_other = BM_edge_other_vert(e, v_b);
2056 if (BM_ELEM_API_FLAG_TEST(v_other, VERT_VISIT)) {
2057 is_double = true;
2058 break;
2059 }
2060 } while ((e = BM_DISK_EDGE_NEXT(e, v_b)) != e_first);
2061
2062 /* cleanup */
2063 e = e_first = v_a->e;
2064 do {
2065 BMVert *v_other = BM_edge_other_vert(e, v_a);
2068 } while ((e = BM_DISK_EDGE_NEXT(e, v_a)) != e_first);
2069
2070#undef VERT_VISIT
2071 }
2072
2073 return is_double;
2074}
2075
2076bool BM_vert_splice(BMesh *bm, BMVert *v_dst, BMVert *v_src)
2077{
2078 BMEdge *e;
2079
2080 /* verts already spliced */
2081 if (v_src == v_dst) {
2082 return false;
2083 }
2084
2085 BLI_assert(BM_vert_pair_share_face_check(v_src, v_dst) == false);
2086
2087 /* move all the edges from 'v_src' disk to 'v_dst' */
2088 while ((e = v_src->e)) {
2089 bmesh_edge_vert_swap(e, v_dst, v_src);
2090 BLI_assert(e->v1 != e->v2);
2091 }
2092
2093 BM_CHECK_ELEMENT(v_src);
2094 BM_CHECK_ELEMENT(v_dst);
2095
2096 /* 'v_src' is unused now, and can be killed */
2097 BM_vert_kill(bm, v_src);
2098
2099 return true;
2100}
2101
2102/* -------------------------------------------------------------------- */
2106/* BM_edge_face_count(e) >= 1 */
2108{
2109 return (e->l && e->l->radial_next != e->l);
2110}
2111
2113 BMesh *bm, BMVert *v, BMVert ***r_vout, int *r_vout_len, const bool copy_select)
2114{
2115 int v_edges_num = 0;
2116
2117 /* Detailed notes on array use since this is stack memory, we have to be careful */
2118
2119 /* newly created vertices, only use when 'r_vout' is set
2120 * (total size will be number of fans) */
2121 BLI_SMALLSTACK_DECLARE(verts_new, BMVert *);
2122 /* fill with edges from the face-fan, clearing on completion
2123 * (total size will be max fan edge count) */
2125 /* temp store edges to walk over when filling 'edges',
2126 * (total size will be max radial edges of any edge) */
2127 BLI_SMALLSTACK_DECLARE(edges_search, BMEdge *);
2128
2129 /* number of resulting verts, include self */
2130 int verts_num = 1;
2131 /* track the total number of edges handled, so we know when we've found the last fan */
2132 int edges_found = 0;
2133
2134#define EDGE_VISIT _FLAG_WALK
2135
2136 /* count and flag at once */
2137 if (v->e) {
2138 BMEdge *e_first, *e_iter;
2139 e_iter = e_first = v->e;
2140 do {
2141 v_edges_num += 1;
2142
2145 } while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first);
2146
2147 while (true) {
2148 /* Considering only edges and faces incident on vertex v, walk
2149 * the edges & collect in the 'edges' list for splitting */
2150
2151 BMEdge *e = v->e;
2153
2154 do {
2156 BLI_SMALLSTACK_PUSH(edges, e);
2157 edges_found += 1;
2158
2159 if (e->l) {
2160 BMLoop *l_iter, *l_first;
2161 l_iter = l_first = e->l;
2162 do {
2163 BMLoop *l_adjacent = (l_iter->v == v) ? l_iter->prev : l_iter->next;
2164 BLI_assert(BM_vert_in_edge(l_adjacent->e, v));
2165 if (BM_ELEM_API_FLAG_TEST(l_adjacent->e, EDGE_VISIT)) {
2167 BLI_SMALLSTACK_PUSH(edges_search, l_adjacent->e);
2168 }
2169 } while ((l_iter = l_iter->radial_next) != l_first);
2170 }
2171 } while ((e = static_cast<BMEdge *>(BLI_SMALLSTACK_POP(edges_search))));
2172
2173 /* now we have all edges connected to 'v->e' */
2174
2175 BLI_assert(edges_found <= v_edges_num);
2176
2177 if (edges_found == v_edges_num) {
2178 /* We're done! The remaining edges in 'edges' form the last fan,
2179 * which can be left as is.
2180 * if 'edges' were allocated it'd be freed here. */
2181 break;
2182 }
2183
2184 BMVert *v_new;
2185
2186 v_new = BM_vert_create(bm, v->co, v, BM_CREATE_NOP);
2187 if (copy_select) {
2188 BM_elem_select_copy(bm, v_new, v);
2189 }
2190
2191 while ((e = static_cast<BMEdge *>(BLI_SMALLSTACK_POP(edges)))) {
2192 bmesh_edge_vert_swap(e, v_new, v);
2193 }
2194
2195 if (r_vout) {
2196 BLI_SMALLSTACK_PUSH(verts_new, v_new);
2197 }
2198 verts_num += 1;
2199 }
2200 }
2201
2202#undef EDGE_VISIT
2203
2204 /* flags are clean now, handle return values */
2205
2206 if (r_vout_len != nullptr) {
2207 *r_vout_len = verts_num;
2208 }
2209
2210 if (r_vout != nullptr) {
2211 BMVert **verts;
2212
2213 verts = static_cast<BMVert **>(MEM_mallocN(sizeof(BMVert *) * verts_num, __func__));
2214 *r_vout = verts;
2215
2216 verts[0] = v;
2217 BLI_SMALLSTACK_AS_TABLE(verts_new, &verts[1]);
2218 }
2219}
2220
2241{
2242 do {
2243 LinkNode *n_orig = static_cast<LinkNode *>(edges_separate->link);
2244 do {
2245 LinkNode *n_prev = n_orig;
2246 LinkNode *n_step = n_orig->next;
2247 BMEdge *e_orig = static_cast<BMEdge *>(n_orig->link);
2248 do {
2249 BMEdge *e = static_cast<BMEdge *>(n_step->link);
2250 BLI_assert(e != e_orig);
2251 if ((e->v1 == e_orig->v1) && (e->v2 == e_orig->v2) && BM_edge_splice(bm, e_orig, e)) {
2252 /* don't visit again */
2253 n_prev->next = n_step->next;
2254 }
2255 else {
2256 n_prev = n_step;
2257 }
2258 } while ((n_step = n_step->next));
2259
2260 } while ((n_orig = n_orig->next) && n_orig->next);
2261 } while ((edges_separate = edges_separate->next));
2262}
2263
2265 BMVert *v,
2266 BMEdge **e_in,
2267 int e_in_len,
2268 const bool copy_select,
2269 BMVert ***r_vout,
2270 int *r_vout_len)
2271{
2272 LinkNode *edges_separate = nullptr;
2273 int i;
2274
2275 for (i = 0; i < e_in_len; i++) {
2276 BMEdge *e = e_in[i];
2278 LinkNode *edges_orig = nullptr;
2279 do {
2280 BMLoop *l_sep = e->l;
2281 bmesh_kernel_edge_separate(bm, e, l_sep, copy_select);
2282 BLI_linklist_prepend_alloca(&edges_orig, l_sep->e);
2283 BLI_assert(e != l_sep->e);
2284 } while (bm_edge_supports_separate(e));
2285 BLI_linklist_prepend_alloca(&edges_orig, e);
2286 BLI_linklist_prepend_alloca(&edges_separate, edges_orig);
2287 }
2288 }
2289
2290 bmesh_kernel_vert_separate(bm, v, r_vout, r_vout_len, copy_select);
2291
2292 if (edges_separate) {
2293 bmesh_kernel_vert_separate__cleanup(bm, edges_separate);
2294 }
2295}
2296
2298 BMVert *v,
2299 const char hflag,
2300 const bool copy_select,
2301 BMVert ***r_vout,
2302 int *r_vout_len)
2303{
2304 LinkNode *edges_separate = nullptr;
2305 BMEdge *e_iter, *e_first;
2306
2307 e_iter = e_first = v->e;
2308 do {
2309 if (BM_elem_flag_test(e_iter, hflag)) {
2310 BMEdge *e = e_iter;
2312 LinkNode *edges_orig = nullptr;
2313 do {
2314 BMLoop *l_sep = e->l;
2315 bmesh_kernel_edge_separate(bm, e, l_sep, copy_select);
2316 /* trick to avoid looping over separated edges */
2317 if (edges_separate == nullptr && edges_orig == nullptr) {
2318 e_first = l_sep->e;
2319 }
2320 BLI_linklist_prepend_alloca(&edges_orig, l_sep->e);
2321 BLI_assert(e != l_sep->e);
2322 } while (bm_edge_supports_separate(e));
2323 BLI_linklist_prepend_alloca(&edges_orig, e);
2324 BLI_linklist_prepend_alloca(&edges_separate, edges_orig);
2325 }
2326 }
2327 } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first);
2328
2329 bmesh_kernel_vert_separate(bm, v, r_vout, r_vout_len, copy_select);
2330
2331 if (edges_separate) {
2332 bmesh_kernel_vert_separate__cleanup(bm, edges_separate);
2333 }
2334}
2335
2337 BMesh * /*bm*/, BMVert *v_dst, BMVert *v_src, bool (*testfn)(BMEdge *, void *arg), void *arg)
2338{
2339 LinkNode *edges_hflag = nullptr;
2340 BMEdge *e_iter, *e_first;
2341
2342 e_iter = e_first = v_src->e;
2343 do {
2344 if (testfn(e_iter, arg)) {
2345 BLI_linklist_prepend_alloca(&edges_hflag, e_iter);
2346 }
2347 } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v_src)) != e_first);
2348
2349 if (edges_hflag) {
2350 do {
2351 e_iter = static_cast<BMEdge *>(edges_hflag->link);
2352 bmesh_disk_vert_replace(e_iter, v_dst, v_src);
2353 } while ((edges_hflag = edges_hflag->next));
2354 }
2355}
2356
2359bool BM_edge_splice(BMesh *bm, BMEdge *e_dst, BMEdge *e_src)
2360{
2361 BMLoop *l;
2362
2363 if (!BM_vert_in_edge(e_src, e_dst->v1) || !BM_vert_in_edge(e_src, e_dst->v2)) {
2364 /* not the same vertices can't splice */
2365
2366 /* the caller should really make sure this doesn't happen ever
2367 * so assert on release builds */
2368 BLI_assert(0);
2369
2370 return false;
2371 }
2372
2373 while (e_src->l) {
2374 l = e_src->l;
2375 BLI_assert(BM_vert_in_edge(e_dst, l->v));
2376 BLI_assert(BM_vert_in_edge(e_dst, l->next->v));
2379 }
2380
2381 BLI_assert(bmesh_radial_length(e_src->l) == 0);
2382
2383 BM_CHECK_ELEMENT(e_src);
2384 BM_CHECK_ELEMENT(e_dst);
2385
2386 /* removes from disks too */
2387 BM_edge_kill(bm, e_src);
2388
2389 return true;
2390}
2391
2392void bmesh_kernel_edge_separate(BMesh *bm, BMEdge *e, BMLoop *l_sep, const bool copy_select)
2393{
2394 BMEdge *e_new;
2395#ifndef NDEBUG
2396 const int radlen = bmesh_radial_length(e->l);
2397#endif
2398
2399 BLI_assert(l_sep->e == e);
2400 BLI_assert(e->l);
2401
2402 if (BM_edge_is_boundary(e)) {
2403 BLI_assert(0); /* no cut required */
2404 return;
2405 }
2406
2407 if (l_sep == e->l) {
2408 e->l = l_sep->radial_next;
2409 }
2410
2411 e_new = BM_edge_create(bm, e->v1, e->v2, e, BM_CREATE_NOP);
2413 bmesh_radial_loop_append(e_new, l_sep);
2414 l_sep->e = e_new;
2415
2416 if (copy_select) {
2417 BM_elem_select_copy(bm, e_new, e);
2418 }
2419
2420 BLI_assert(bmesh_radial_length(e->l) == radlen - 1);
2421 BLI_assert(bmesh_radial_length(e_new->l) == 1);
2422
2423 BM_CHECK_ELEMENT(e_new);
2425}
2426
2428{
2429 BMVert *v_new = nullptr;
2430 BMVert *v_sep = l_sep->v;
2431 BMEdge *e_iter;
2432 BMEdge *edges[2];
2433 int i;
2434
2435 /* peel the face from the edge radials on both sides of the
2436 * loop vert, disconnecting the face from its fan */
2437 if (!BM_edge_is_boundary(l_sep->e)) {
2438 bmesh_kernel_edge_separate(bm, l_sep->e, l_sep, false);
2439 }
2440 if (!BM_edge_is_boundary(l_sep->prev->e)) {
2441 bmesh_kernel_edge_separate(bm, l_sep->prev->e, l_sep->prev, false);
2442 }
2443
2444/* do inline, below */
2445#if 0
2446 if (BM_vert_edge_count_is_equal(v_sep, 2)) {
2447 return v_sep;
2448 }
2449#endif
2450
2451 /* Search for an edge unattached to this loop */
2452 e_iter = v_sep->e;
2453 while (!ELEM(e_iter, l_sep->e, l_sep->prev->e)) {
2454 e_iter = bmesh_disk_edge_next(e_iter, v_sep);
2455
2456 /* We've come back around to the initial edge, all touch this loop.
2457 * If there are still only two edges out of v_sep,
2458 * then this whole URMV was just a no-op, so exit now. */
2459 if (e_iter == v_sep->e) {
2461 return v_sep;
2462 }
2463 }
2464
2465 v_sep->e = l_sep->e;
2466
2467 v_new = BM_vert_create(bm, v_sep->co, v_sep, BM_CREATE_NOP);
2468
2469 edges[0] = l_sep->e;
2470 edges[1] = l_sep->prev->e;
2471
2472 for (i = 0; i < ARRAY_SIZE(edges); i++) {
2473 BMEdge *e = edges[i];
2474 bmesh_edge_vert_swap(e, v_new, v_sep);
2475 }
2476
2477 BLI_assert(v_sep != l_sep->v);
2478 BLI_assert(v_sep->e != l_sep->v->e);
2479
2480 BM_CHECK_ELEMENT(l_sep);
2481 BM_CHECK_ELEMENT(v_sep);
2482 BM_CHECK_ELEMENT(edges[0]);
2483 BM_CHECK_ELEMENT(edges[1]);
2484 BM_CHECK_ELEMENT(v_new);
2485
2486 return v_new;
2487}
2488
2490{
2491 BMVert *v_sep = larr[0]->v;
2492 BMVert *v_new;
2493 int edges_len = 0;
2494 int i;
2495 /* any edges not owned by 'larr' loops connected to 'v_sep'? */
2496 bool is_mixed_edge_any = false;
2497 /* any loops not owned by 'larr' radially connected to 'larr' loop edges? */
2498 bool is_mixed_loop_any = false;
2499
2500#define LOOP_VISIT _FLAG_WALK
2501#define EDGE_VISIT _FLAG_WALK
2502
2503 for (i = 0; i < larr_len; i++) {
2504 BMLoop *l_sep = larr[i];
2505
2506 /* all must be from the same vert! */
2507 BLI_assert(v_sep == l_sep->v);
2508
2511
2512 /* weak! but it makes it simpler to check for edges to split
2513 * while doing a radial loop (where loops may be adjacent) */
2516
2517 BMLoop *loop_pair[2] = {l_sep, l_sep->prev};
2518 for (int j = 0; j < ARRAY_SIZE(loop_pair); j++) {
2519 BMEdge *e = loop_pair[j]->e;
2522 edges_len += 1;
2523 }
2524 }
2525 }
2526
2527 BMEdge **edges = BLI_array_alloca(edges, edges_len);
2528 STACK_DECLARE(edges);
2529
2530 STACK_INIT(edges, edges_len);
2531
2532 {
2533 BMEdge *e_first, *e_iter;
2534 e_iter = e_first = v_sep->e;
2535 do {
2536 if (BM_ELEM_API_FLAG_TEST(e_iter, EDGE_VISIT)) {
2537 BMLoop *l_iter, *l_first;
2538 bool is_mixed_loop = false;
2539
2540 l_iter = l_first = e_iter->l;
2541 do {
2542 if (!BM_ELEM_API_FLAG_TEST(l_iter, LOOP_VISIT)) {
2543 is_mixed_loop = true;
2544 break;
2545 }
2546 } while ((l_iter = l_iter->radial_next) != l_first);
2547
2548 if (is_mixed_loop) {
2549 /* ensure the first loop is one we don't own so we can do a quick check below
2550 * on the edge's loop-flag to see if the edge is mixed or not. */
2551 e_iter->l = l_iter;
2552
2553 is_mixed_loop_any = true;
2554 }
2555
2556 STACK_PUSH(edges, e_iter);
2557 }
2558 else {
2559 /* at least one edge attached isn't connected to our loops */
2560 is_mixed_edge_any = true;
2561 }
2562 } while ((e_iter = bmesh_disk_edge_next(e_iter, v_sep)) != e_first);
2563 }
2564
2565 BLI_assert(edges_len == STACK_SIZE(edges));
2566
2567 if (is_mixed_loop_any == false && is_mixed_edge_any == false) {
2568 /* all loops in 'larr' are the sole owners of their edges.
2569 * nothing to split away from, this is a no-op */
2570 v_new = v_sep;
2571 }
2572 else {
2573 v_new = BM_vert_create(bm, v_sep->co, v_sep, BM_CREATE_NOP);
2574
2575 for (i = 0; i < STACK_SIZE(edges); i++) {
2576 BMEdge *e = edges[i];
2577 BMLoop *l_iter, *l_first, *l_next;
2578 BMEdge *e_new;
2579
2580 /* disable so copied edge isn't left dirty (loop edges are cleared last too) */
2582
2583 /* will always be false when (is_mixed_loop_any == false) */
2585 /* edge has some loops owned by us, some owned by other loops */
2586 BMVert *e_new_v_pair[2];
2587
2588 if (e->v1 == v_sep) {
2589 e_new_v_pair[0] = v_new;
2590 e_new_v_pair[1] = e->v2;
2591 }
2592 else {
2593 BLI_assert(v_sep == e->v2);
2594 e_new_v_pair[0] = e->v1;
2595 e_new_v_pair[1] = v_new;
2596 }
2597
2598 e_new = BM_edge_create(bm, UNPACK2(e_new_v_pair), e, BM_CREATE_NOP);
2599
2600 /* now moved all loops from 'larr' to this newly created edge */
2601 l_iter = l_first = e->l;
2602 do {
2603 l_next = l_iter->radial_next;
2604 if (BM_ELEM_API_FLAG_TEST(l_iter, LOOP_VISIT)) {
2605 bmesh_radial_loop_remove(e, l_iter);
2606 bmesh_radial_loop_append(e_new, l_iter);
2607 l_iter->e = e_new;
2608 }
2609 } while ((l_iter = l_next) != l_first);
2610 }
2611 else {
2612 /* we own the edge entirely, replace the vert */
2613 bmesh_disk_vert_replace(e, v_new, v_sep);
2614 }
2615
2616 /* loop vert is handled last! */
2617 }
2618 }
2619
2620 for (i = 0; i < larr_len; i++) {
2621 BMLoop *l_sep = larr[i];
2622
2623 l_sep->v = v_new;
2624
2631
2634 }
2635
2636#undef LOOP_VISIT
2637#undef EDGE_VISIT
2638
2639 return v_new;
2640}
2641
2643{
2644 BMLoop *l_iter, *l_first;
2645
2646 BLI_assert(ELEM(v_src, e->v1, e->v2));
2647 bmesh_disk_vert_replace(e, v_dst, v_src);
2648
2649 l_iter = l_first = e->l;
2650 do {
2651 if (l_iter->v == v_src) {
2652 l_iter->v = v_dst;
2653 if (BM_vert_in_edge(l_iter->prev->e, v_src)) {
2654 bmesh_edge_vert_swap__recursive(l_iter->prev->e, v_dst, v_src);
2655 }
2656 }
2657 else if (l_iter->next->v == v_src) {
2658 l_iter->next->v = v_dst;
2659 if (BM_vert_in_edge(l_iter->next->e, v_src)) {
2660 bmesh_edge_vert_swap__recursive(l_iter->next->e, v_dst, v_src);
2661 }
2662 }
2663 else {
2664 BLI_assert(l_iter->prev->v != v_src);
2665 }
2666 } while ((l_iter = l_iter->radial_next) != l_first);
2667}
2668
2670{
2671 BMVert *v_new = BM_vert_create(bm, l_sep->v->co, l_sep->v, BM_CREATE_NOP);
2672 /* passing either 'l_sep->e', 'l_sep->prev->e' will work */
2673 bmesh_edge_vert_swap__recursive(l_sep->e, v_new, l_sep->v);
2674 BLI_assert(l_sep->v == v_new);
2675 return v_new;
2676}
2677
2679{
2680 BMLoop *l_iter, *l_first;
2681
2682 BLI_assert(f_a != f_b);
2683
2684 l_iter = l_first = BM_FACE_FIRST_LOOP(f_a);
2685 do {
2686 l_iter->f = f_b;
2687 } while ((l_iter = l_iter->next) != l_first);
2688
2689 l_iter = l_first = BM_FACE_FIRST_LOOP(f_b);
2690 do {
2691 l_iter->f = f_a;
2692 } while ((l_iter = l_iter->next) != l_first);
2693
2694 std::swap((*f_a), (*f_b));
2695
2696 /* swap back */
2697 std::swap(f_a->head.data, f_b->head.data);
2698 std::swap(f_a->head.index, f_b->head.index);
2699}
CustomData interface, see also DNA_customdata_types.h.
int CustomData_get_offset(const CustomData *data, eCustomDataType type)
void CustomData_bmesh_free_block(CustomData *data, void **block)
void * CustomData_bmesh_get(const CustomData *data, void *block, eCustomDataType type)
void CustomData_bmesh_set_default(CustomData *data, void **block)
#define ORIGINDEX_NONE
void CustomData_bmesh_copy_block(CustomData &data, void *src_block, void **dst_block)
void BKE_mesh_mdisp_flip(MDisps *md, bool use_loop_mdisp_flip)
#define BLI_array_alloca(arr, realsize)
Definition BLI_alloca.h:25
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_INLINE
BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
void void void BLI_movelisttolist(struct ListBase *dst, struct ListBase *src) ATTR_NONNULL(1
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:130
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void zero_v3(float r[3])
void * BLI_mempool_alloc(BLI_mempool *pool) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1)
void BLI_mempool_free(BLI_mempool *pool, void *addr) ATTR_NONNULL(1
void * BLI_mempool_calloc(BLI_mempool *pool) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1)
#define UNPACK2(a)
#define UNUSED_FUNCTION(x)
#define ARRAY_SIZE(arr)
#define ENUM_OPERATORS(_type, _max)
#define UNLIKELY(x)
#define ELEM(...)
#define STACK_PUSH(stack, val)
#define STACK_DECLARE(stack)
#define STACK_SIZE(stack)
#define STACK_INIT(stack, stack_num)
@ CD_SHAPE_KEYINDEX
Read Guarded memory(de)allocation.
@ BM_LOOP
#define BM_DISK_EDGE_NEXT(e, v)
#define BM_NGON_MAX
@ BM_SPACEARR_DIRTY_ALL
@ BM_ELEM_SMOOTH
@ BM_ELEM_INTERNAL_TAG
@ BM_ELEM_DRAW
#define BM_FACE_FIRST_LOOP(p)
#define BM_ELEM_CD_GET_VOID_P(ele, offset)
void BM_elem_select_copy(BMesh *bm_dst, void *ele_dst_v, const void *ele_src_v)
void BM_elem_attrs_copy(BMesh *bm, const BMCustomDataCopyMap &map, const BMVert *src, BMVert *dst)
void BM_edges_from_verts_ensure(BMesh *bm, BMEdge **edge_arr, BMVert **vert_arr, const int len)
bool BM_edges_from_verts(BMEdge **edge_arr, BMVert **vert_arr, const int len)
BMFace * BM_face_create_ngon(BMesh *bm, BMVert *v1, BMVert *v2, BMEdge **edges, const int len, const BMFace *f_example, const eBMCreateFlag create_flag)
Make NGon.
void bmesh_face_swap_data(BMFace *f_a, BMFace *f_b)
static int UNUSED_FUNCTION bm_loop_length(BMLoop *l)
BMVert * bmesh_kernel_unglue_region_make_vert_multi_isolated(BMesh *bm, BMLoop *l_sep)
BMFace * BM_faces_join(BMesh *bm, BMFace **faces, int totface, const bool do_del)
Join Connected Faces.
BMVert * bmesh_kernel_split_edge_make_vert(BMesh *bm, BMVert *tv, BMEdge *e, BMEdge **r_e)
Split Edge Make Vert (SEMV)
static void bmesh_edge_vert_swap__recursive(BMEdge *e, BMVert *v_dst, BMVert *v_src)
void BM_face_verts_kill(BMesh *bm, BMFace *f)
static BMLoop * bm_face_boundary_add(BMesh *bm, BMFace *f, BMVert *startv, BMEdge *starte, const eBMCreateFlag create_flag)
bool BM_edge_splice(BMesh *bm, BMEdge *e_dst, BMEdge *e_src)
Splice Edge.
void BM_vert_separate(BMesh *bm, BMVert *v, BMEdge **e_in, int e_in_len, const bool copy_select, BMVert ***r_vout, int *r_vout_len)
static void bm_elements_systag_disable(void *veles, int tot, const char api_flag)
static void bm_kill_only_loop(BMesh *bm, BMLoop *l)
void bmesh_kernel_edge_separate(BMesh *bm, BMEdge *e, BMLoop *l_sep, const bool copy_select)
Separate Edge.
bool BM_vert_splice(BMesh *bm, BMVert *v_dst, BMVert *v_src)
Splice Vert.
#define LOOP_VISIT
void BM_vert_kill(BMesh *bm, BMVert *v)
static void bmesh_kernel_vert_separate__cleanup(BMesh *bm, LinkNode *edges_separate)
static BMFace * bm_face_create__sfme(BMesh *bm, BMFace *f_example)
static BMFace * bm_face_copy_impl(BMesh *bm_dst, BMFace *f, const bool copy_verts, const bool copy_edges)
void BM_face_kill(BMesh *bm, BMFace *f)
BMFace * bmesh_kernel_join_face_kill_edge(BMesh *bm, BMFace *f1, BMFace *f2, BMEdge *e)
Join Face Kill Edge (JFKE)
BMFace * BM_face_create_verts(BMesh *bm, BMVert **vert_arr, const int len, const BMFace *f_example, const eBMCreateFlag create_flag, const bool create_edges)
void BM_vert_separate_hflag(BMesh *bm, BMVert *v, const char hflag, const bool copy_select, BMVert ***r_vout, int *r_vout_len)
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)
void BM_face_kill_loose(BMesh *bm, BMFace *f)
static void bm_kill_only_vert(BMesh *bm, BMVert *v)
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)
static void bm_kill_only_edge(BMesh *bm, BMEdge *e)
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)
void BM_face_edges_kill(BMesh *bm, BMFace *f)
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)
BMFace * BM_face_create(BMesh *bm, BMVert *const *verts, BMEdge *const *edges, const int len, const BMFace *f_example, const eBMCreateFlag create_flag)
#define EDGE_VISIT
static BMLoop * bm_loop_create(BMesh *bm, BMVert *v, BMEdge *e, BMFace *f, const BMLoop *l_example, const eBMCreateFlag create_flag)
BLI_INLINE BMFace * bm_face_create__internal(BMesh *bm)
static bool bm_vert_is_manifold_flagged(BMVert *v, const char api_flag)
void bmesh_kernel_loop_reverse(BMesh *bm, BMFace *f, const int cd_loop_mdisp_offset, const bool use_loop_mdisp_flip)
Loop Reverse.
void BM_edge_kill(BMesh *bm, BMEdge *e)
BMVert * BM_vert_create(BMesh *bm, const float co[3], const BMVert *v_example, const eBMCreateFlag create_flag)
Main function for creating a new vertex.
Definition bmesh_core.cc:43
int bmesh_elem_check(void *element, const char htype)
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)
void BM_vert_separate_tested_edges(BMesh *, BMVert *v_dst, BMVert *v_src, bool(*testfn)(BMEdge *, void *arg), void *arg)
BMeshElemErrorFlag
@ IS_FACE_LOOP_DUPE_LOOP
@ IS_LOOP_NULL_CYCLE_LINK
@ IS_LOOP_WRONG_FACE_LENGTH
@ IS_FACE_LOOP_WRONG_RADIAL_LENGTH
@ IS_FACE_LOOP_VERT_NOT_IN_EDGE
@ IS_EDGE_WRONG_LOOP_TYPE
@ IS_FACE_LOOP_DUPE_VERT
@ IS_LOOP_WRONG_FACE_TYPE
@ IS_FACE_NULL_VERT
@ IS_EDGE_NULL_RADIAL_LINK
@ IS_VERT_WRONG_EDGE_TYPE
@ IS_LOOP_VERT_NOT_IN_EDGE
@ IS_NULL
@ IS_FACE_LOOP_DUPE_EDGE
@ IS_LOOP_WRONG_VERT_TYPE
@ IS_WRONG_TYPE
@ IS_FACE_WRONG_LENGTH
@ IS_EDGE_WRONG_FACE_TYPE
@ IS_EDGE_NULL_DISK_LINK
@ IS_FACE_NULL_LOOP
@ IS_LOOP_WRONG_RADIAL_LENGTH
@ IS_FACE_WRONG_LOOP_FACE
@ IS_EDGE_ZERO_FACE_LENGTH
@ IS_LOOP_WRONG_EDGE_TYPE
@ IS_FACE_LOOP_WRONG_DISK_LENGTH
@ IS_LOOP_ZERO_FACE_LENGTH
@ IS_FACE_NULL_EDGE
static void bm_elements_systag_enable(void *veles, int tot, const char api_flag)
#define VERT_VISIT
static int bm_loop_systag_count_radial(BMLoop *l, const char api_flag)
void bmesh_kernel_vert_separate(BMesh *bm, BMVert *v, BMVert ***r_vout, int *r_vout_len, const bool copy_select)
Separate Vert.
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.
BLI_INLINE bool bm_edge_supports_separate(const BMEdge *e)
static int UNUSED_FUNCTION bm_vert_systag_count_disk(BMVert *v, const char api_flag)
static void bm_kill_only_face(BMesh *bm, BMFace *f)
bool BM_vert_splice_check_double(BMVert *v_a, BMVert *v_b)
eBMCreateFlag
Definition bmesh_core.hh:23
@ BM_CREATE_NOP
Definition bmesh_core.hh:24
@ BM_CREATE_SKIP_CD
Definition bmesh_core.hh:32
@ BM_CREATE_NO_DOUBLE
Definition bmesh_core.hh:26
#define BMESH_ASSERT(a)
#define BM_elem_flag_disable(ele, hflag)
#define BM_elem_flag_set(ele, hflag, val)
#define BM_elem_index_set(ele, index)
#define BM_elem_flag_test(ele, hflag)
void BM_loop_interp_multires_ex(BMesh *, BMLoop *l_dst, const BMFace *f_src, const float f_dst_center[3], const float f_src_center[3], const int cd_loop_mdisp_offset)
ATTR_WARN_UNUSED_RESULT BMesh * bm
#define BM_select_history_remove(bm, ele)
#define BM_FACE
#define BM_EDGE
#define BM_VERT
ATTR_WARN_UNUSED_RESULT const void * element
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_CHECK_ELEMENT(el)
int bmesh_radial_length(const BMLoop *l)
@ _FLAG_JF
@ _FLAG_ELEM_CHECK
int bmesh_disk_count_at_most(const BMVert *v, int count_max)
#define BM_ELEM_API_FLAG_ENABLE(element, f)
int bmesh_disk_count(const BMVert *v)
bool BM_vert_pair_share_face_check(BMVert *v_a, BMVert *v_b)
bool BM_edge_in_face(const BMEdge *e, const BMFace *f)
BMFace * BM_face_exists(BMVert *const *varr, int len)
int BM_face_share_edge_count(BMFace *f_a, BMFace *f_b)
BMEdge * BM_edge_exists(BMVert *v_a, BMVert *v_b)
BMFace * BM_face_find_double(BMFace *f)
BMLoop * BM_face_edge_share_loop(BMFace *f, BMEdge *e)
Return the Loop Shared by Face and Edge.
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()
#define BM_edge_face_count_is_over(e, n)
#define BM_vert_edge_count_is_equal(v, n)
BLI_INLINE BMVert * BM_edge_other_vert(BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE bool BM_verts_in_edge(const BMVert *v1, const BMVert *v2, 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 BMVert * v
void bmesh_disk_edge_remove(BMEdge *e, BMVert *v)
bool bmesh_loop_validate(BMFace *f)
void bmesh_disk_edge_append(BMEdge *e, BMVert *v)
void bmesh_edge_vert_swap(BMEdge *e, BMVert *v_dst, BMVert *v_src)
void bmesh_disk_vert_replace(BMEdge *e, BMVert *v_dst, BMVert *v_src)
void bmesh_radial_loop_unlink(BMLoop *l)
void bmesh_radial_loop_append(BMEdge *e, BMLoop *l)
void bmesh_radial_loop_remove(BMEdge *e, BMLoop *l)
BMESH RADIAL REMOVE LOOP.
bool bmesh_disk_validate(int len, BMEdge *e, BMVert *v)
bool bmesh_radial_validate(int radlen, BMLoop *l)
BLI_INLINE BMEdge * bmesh_disk_edge_next(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
void append(const T &value)
int len
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
static float verts[][3]
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
static void error(const char *str)
BMHeader head
BMVert * v1
BMVert * v2
struct BMLoop * l
short mat_nr
BMHeader head
float no[3]
BMLoop * l_first
void * data
char api_flag
BMHeader head
struct BMVert * v
struct BMEdge * e
struct BMLoop * radial_prev
struct BMLoop * radial_next
struct BMLoop * prev
struct BMFace * f
struct BMLoop * next
float co[3]
struct BMEdge * e
float no[3]
BMHeader head
int totvert
struct BLI_mempool * epool
char elem_index_dirty
CustomData vdata
int totedge
char elem_table_dirty
struct BLI_mempool * vtoolflagpool
CustomData edata
uint use_toolflags
int totloop
struct BLI_mempool * etoolflagpool
BMFace * act_face
struct BLI_mempool * ftoolflagpool
char spacearr_dirty
CustomData pdata
CustomData ldata
int totface
struct BLI_mempool * fpool
struct BLI_mempool * vpool
struct BLI_mempool * lpool
void * link
struct LinkNode * next