Blender V5.0
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
10
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 "BKE_customdata.hh"
20#include "BKE_mesh.hh"
21
22#include "bmesh.hh"
24
25using blender::Vector;
26
27/* use so valgrinds memcheck alerts us when undefined index is used.
28 * TESTING ONLY! */
29// #define USE_DEBUG_INDEX_MEMCHECK
30
31#ifdef USE_DEBUG_INDEX_MEMCHECK
32# define DEBUG_MEMCHECK_INDEX_INVALIDATE(ele) \
33 { \
34 int undef_idx; \
35 BM_elem_index_set(ele, undef_idx); /* set_ok_invalid */ \
36 } \
37 (void)0
38
39#endif
40
42 const float co[3],
43 const BMVert *v_example,
44 const eBMCreateFlag create_flag)
45{
46 BMVert *v = static_cast<BMVert *>(BLI_mempool_alloc(bm->vpool));
47
48 BLI_assert((v_example == nullptr) || (v_example->head.htype == BM_VERT));
49 BLI_assert(!(create_flag & 1));
50
51 /* --- assign all members --- */
52 v->head.data = nullptr;
53
54#ifdef USE_DEBUG_INDEX_MEMCHECK
55 DEBUG_MEMCHECK_INDEX_INVALIDATE(v);
56#else
57 BM_elem_index_set(v, -1); /* set_ok_invalid */
58#endif
59
60 v->head.htype = BM_VERT;
61 v->head.hflag = 0;
62 v->head.api_flag = 0;
63
64 /* allocate flags */
65 if (bm->use_toolflags) {
66 ((BMVert_OFlag *)v)->oflags = static_cast<BMFlagLayer *>(
67 bm->vtoolflagpool ? BLI_mempool_calloc(bm->vtoolflagpool) : nullptr);
68 }
69
70 /* 'v->no' is handled by BM_elem_attrs_copy */
71 if (co) {
72 copy_v3_v3(v->co, co);
73 }
74 else {
75 zero_v3(v->co);
76 }
77 /* 'v->no' set below */
78
79 v->e = nullptr;
80 /* --- done --- */
81
82 /* disallow this flag for verts - its meaningless */
83 BLI_assert((create_flag & BM_CREATE_NO_DOUBLE) == 0);
84
85 /* may add to middle of the pool */
86 bm->elem_index_dirty |= BM_VERT;
87 bm->elem_table_dirty |= BM_VERT;
88 bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
89
90 bm->totvert++;
91
92 if (!(create_flag & BM_CREATE_SKIP_CD)) {
93 if (v_example) {
94 int *keyi;
95
96 /* handles 'v->no' too */
97 BM_elem_attrs_copy(bm, v_example, v);
98
99 /* Exception: don't copy the original shape-key index. */
100 keyi = static_cast<int *>(CustomData_bmesh_get(&bm->vdata, v->head.data, CD_SHAPE_KEYINDEX));
101 if (keyi) {
102 *keyi = ORIGINDEX_NONE;
103 }
104 }
105 else {
106 CustomData_bmesh_set_default(&bm->vdata, &v->head.data);
107 zero_v3(v->no);
108 }
109 }
110 else {
111 if (v_example) {
112 copy_v3_v3(v->no, v_example->no);
113 }
114 else {
115 zero_v3(v->no);
116 }
117 }
118
120
121 return v;
122}
123
125 BMesh *bm, BMVert *v1, BMVert *v2, const BMEdge *e_example, const eBMCreateFlag create_flag)
126{
127 BMEdge *e;
128
129 BLI_assert(v1 != v2);
130 BLI_assert(v1->head.htype == BM_VERT && v2->head.htype == BM_VERT);
131 BLI_assert((e_example == nullptr) || (e_example->head.htype == BM_EDGE));
132 BLI_assert(!(create_flag & 1));
133
134 if ((create_flag & BM_CREATE_NO_DOUBLE) && (e = BM_edge_exists(v1, v2))) {
135 return e;
136 }
137
138 e = static_cast<BMEdge *>(BLI_mempool_alloc(bm->epool));
139
140 /* --- assign all members --- */
141 e->head.data = nullptr;
142
143#ifdef USE_DEBUG_INDEX_MEMCHECK
144 DEBUG_MEMCHECK_INDEX_INVALIDATE(e);
145#else
146 BM_elem_index_set(e, -1); /* set_ok_invalid */
147#endif
148
149 e->head.htype = BM_EDGE;
150 e->head.hflag = BM_ELEM_SMOOTH;
151 e->head.api_flag = 0;
152
153 /* allocate flags */
154 if (bm->use_toolflags) {
155 ((BMEdge_OFlag *)e)->oflags = static_cast<BMFlagLayer *>(
156 bm->etoolflagpool ? BLI_mempool_calloc(bm->etoolflagpool) : nullptr);
157 }
158
159 e->v1 = v1;
160 e->v2 = v2;
161 e->l = nullptr;
162
163 memset(&e->v1_disk_link, 0, sizeof(BMDiskLink));
164 memset(&e->v2_disk_link, 0, sizeof(BMDiskLink));
165
166 /* --- done --- */
167
170
171 /* may add to middle of the pool */
172 bm->elem_index_dirty |= BM_EDGE;
173 bm->elem_table_dirty |= BM_EDGE;
174 bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
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 {
183 CustomData_bmesh_set_default(&bm->edata, &e->head.data);
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 */
243 bm->elem_index_dirty |= BM_LOOP;
244 bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
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 */
251 CustomData_bmesh_copy_block(bm->ldata, l_example->head.data, &l->head.data);
252 }
253 else {
254 CustomData_bmesh_set_default(&bm->ldata, &l->head.data);
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 *>(
399 bm->ftoolflagpool ? BLI_mempool_calloc(bm->ftoolflagpool) : nullptr);
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 */
414 bm->elem_index_dirty |= BM_FACE;
415 bm->elem_table_dirty |= BM_FACE;
416 bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
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
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
747{
748 bm->totvert--;
749 bm->elem_index_dirty |= BM_VERT;
750 bm->elem_table_dirty |= BM_VERT;
751 bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
752
754
755 if (v->head.data) {
756 CustomData_bmesh_free_block(&bm->vdata, &v->head.data);
757 }
758
759 if (bm->vtoolflagpool) {
760 BLI_mempool_free(bm->vtoolflagpool, ((BMVert_OFlag *)v)->oflags);
761 }
762 BLI_mempool_free(bm->vpool, v);
763}
764
770{
771 bm->totedge--;
772 bm->elem_index_dirty |= BM_EDGE;
773 bm->elem_table_dirty |= BM_EDGE;
774 bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
775
777
778 if (e->head.data) {
779 CustomData_bmesh_free_block(&bm->edata, &e->head.data);
780 }
781
782 if (bm->etoolflagpool) {
783 BLI_mempool_free(bm->etoolflagpool, ((BMEdge_OFlag *)e)->oflags);
784 }
785 BLI_mempool_free(bm->epool, e);
786}
787
793{
794 if (bm->act_face == f) {
795 bm->act_face = nullptr;
796 }
797
798 bm->totface--;
799 bm->elem_index_dirty |= BM_FACE;
800 bm->elem_table_dirty |= BM_FACE;
801 bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
802
804
805 if (f->head.data) {
807 }
808
809 if (bm->ftoolflagpool) {
810 BLI_mempool_free(bm->ftoolflagpool, ((BMFace_OFlag *)f)->oflags);
811 }
812 BLI_mempool_free(bm->fpool, f);
813}
814
820{
821 bm->totloop--;
822 bm->elem_index_dirty |= BM_LOOP;
823 bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
824
825 if (l->head.data) {
826 CustomData_bmesh_free_block(&bm->ldata, &l->head.data);
827 }
828
829 BLI_mempool_free(bm->lpool, l);
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++) {
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! */
1076 bm->elem_index_dirty |= BM_LOOP;
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, BMFace **r_double)
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 BMFace *f_existing;
1174 const bool had_active_face = (bm->act_face != nullptr);
1175
1176 /* Initialize the return value if provided. This ensures it will be nullptr if the join fails. */
1177 if (r_double) {
1178 *r_double = nullptr;
1179 }
1180
1181 if (UNLIKELY(!totface)) {
1182 BMESH_ASSERT(0);
1183 return nullptr;
1184 }
1185
1186 if (totface == 1) {
1187 return faces[0];
1188 }
1189
1191
1195
1196 for (i = 0; i < totface; i++) {
1197 f = faces[i];
1198 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
1199 do {
1200 int rlen = bm_loop_systag_count_radial(l_iter, _FLAG_JF);
1201
1202 if (rlen > 2) {
1203 /* Input faces do not form a contiguous manifold region.
1204 * Clean up flags and fail. */
1206 return nullptr;
1207 }
1208 if (rlen == 1) {
1209 edges.append(l_iter->e);
1210
1211 if (!v1) {
1212 v1 = l_iter->v;
1213 v2 = BM_edge_other_vert(l_iter->e, l_iter->v);
1214 }
1215 }
1216 else if (rlen == 2) {
1217 const bool d1 = bm_vert_is_manifold_flagged(l_iter->e->v1, _FLAG_JF);
1218 const bool d2 = bm_vert_is_manifold_flagged(l_iter->e->v2, _FLAG_JF);
1219
1220 if (!d1 && !d2 && !BM_ELEM_API_FLAG_TEST(l_iter->e, _FLAG_JF)) {
1221 /* don't remove an edge it makes up the side of another face
1222 * else this will remove the face as well - campbell */
1223 if (!BM_edge_face_count_is_over(l_iter->e, 2)) {
1224 if (do_del) {
1225 deledges.append(l_iter->e);
1226 }
1228 }
1229 }
1230 else {
1231 if (d1 && !BM_ELEM_API_FLAG_TEST(l_iter->e->v1, _FLAG_JF)) {
1232 if (do_del) {
1233 delverts.append(l_iter->e->v1);
1234 }
1236 }
1237
1238 if (d2 && !BM_ELEM_API_FLAG_TEST(l_iter->e->v2, _FLAG_JF)) {
1239 if (do_del) {
1240 delverts.append(l_iter->e->v2);
1241 }
1243 }
1244 }
1245 }
1246 } while ((l_iter = l_iter->next) != l_first);
1247
1248#ifdef USE_BMESH_HOLES
1249 for (lst = f->loops.first; lst; lst = lst->next) {
1250 if (lst == f->loops.first) {
1251 continue;
1252 }
1253
1254 BLI_remlink(&f->loops, lst);
1255 BLI_addtail(&holes, lst);
1256 }
1257#endif
1258 }
1259
1260 /* create region face */
1261 f_new = !edges.is_empty() ?
1263 bm, v1, v2, edges.data(), edges.size(), faces[0], BM_CREATE_NOP) :
1264 nullptr;
1265 if (UNLIKELY(f_new == nullptr)) {
1266 /* Invalid boundary region to join faces
1267 * Clean up flags and fail */
1269 return nullptr;
1270 }
1271
1272 /* If a new face was created, check whether it is a double of an existing face. */
1273 f_existing = BM_face_find_double(f_new);
1274 if (f_existing) {
1275
1276 /* Return the double to the calling function if that was requested. */
1277 if (r_double) {
1278 *r_double = f_existing;
1279 }
1280
1281 /* Otherwise, automatically reuse the existing face. */
1282 else {
1283 BM_face_kill(bm, f_new);
1284 f_new = f_existing;
1285 }
1286 }
1287
1288 bool reusing_face = (f_existing && r_double == nullptr);
1289
1290 /* If we are *not* reusing an existing face, we need to transfer data from the faces being joined
1291 * to the newly created joined face. */
1292 if (LIKELY(reusing_face == false)) {
1293
1294 /* copy over loop data */
1295 l_iter = l_first = BM_FACE_FIRST_LOOP(f_new);
1296 do {
1297 BMLoop *l2 = l_iter->radial_next;
1298
1299 do {
1300 if (BM_ELEM_API_FLAG_TEST(l2->f, _FLAG_JF)) {
1301 break;
1302 }
1303 l2 = l2->radial_next;
1304 } while (l2 != l_iter);
1305
1306 if (l2 != l_iter) {
1307 /* loops share an edge, shared vert depends on winding */
1308 if (l2->v != l_iter->v) {
1309 l2 = l2->next;
1310 }
1311 BLI_assert(l_iter->v == l2->v);
1312
1313 BM_elem_attrs_copy(bm, l2, l_iter);
1314 }
1315 } while ((l_iter = l_iter->next) != l_first);
1316
1317#ifdef USE_BMESH_HOLES
1318 /* add holes */
1319 BLI_movelisttolist(&f_new->loops, &holes);
1320
1321 /* update loop face pointer */
1322 for (lst = f_new->loops.first; lst; lst = lst->next) {
1323 l_iter = l_first = lst->first;
1324 do {
1325 l_iter->f = f_new;
1326 } while ((l_iter = l_iter->next) != l_first);
1327 }
1328#endif
1329
1330 /* handle multi-res data */
1331 if (cd_loop_mdisp_offset != -1) {
1332 float f_center[3];
1333 float (*faces_center)[3] = BLI_array_alloca(faces_center, totface);
1334
1335 BM_face_calc_center_median(f_new, f_center);
1336 for (i = 0; i < totface; i++) {
1337 BM_face_calc_center_median(faces[i], faces_center[i]);
1338 }
1339
1340 l_iter = l_first = BM_FACE_FIRST_LOOP(f_new);
1341 do {
1342 for (i = 0; i < totface; i++) {
1344 bm, l_iter, faces[i], f_center, faces_center[i], cd_loop_mdisp_offset);
1345 }
1346 } while ((l_iter = l_iter->next) != l_first);
1347 }
1348 }
1349
1350 /* Clean up the internal flags. */
1353
1354 if (do_del) {
1355 /* If `do_del`, delete all the edges and verts that were identified while walking the mesh. */
1356 for (BMEdge *edge : deledges) {
1357 BM_edge_kill(bm, edge);
1358 }
1359
1360 for (BMVert *vert : delverts) {
1361 BM_vert_kill(bm, vert);
1362 }
1363 }
1364 else {
1365 /* Otherwise, delete only the faces that were merged
1366 * (do not leave the mesh with both the old and new faces). */
1367 for (i = 0; i < totface; i++) {
1369 }
1370 }
1371
1372 /* If the mesh started with an active face, but no longer has one, then the active face was one
1373 * of the faces that was joined then deleted. Set the active face to preserve it. */
1374 if (had_active_face && bm->act_face == nullptr) {
1375 bm->act_face = f_new;
1376 }
1377
1378 BM_CHECK_ELEMENT(f_new);
1379 return f_new;
1380}
1381
1383{
1384 BMFace *f;
1385#ifdef USE_BMESH_HOLES
1386 BMLoopList *lst;
1387#endif
1388
1390
1391#ifdef USE_BMESH_HOLES
1392 lst = BLI_mempool_calloc(bm->looplistpool);
1393 BLI_addtail(&f->loops, lst);
1394#endif
1395
1396#ifdef USE_BMESH_HOLES
1397 f->totbounds = 1;
1398#endif
1399
1400 BM_elem_attrs_copy(bm, f_example, f);
1401
1402 return f;
1403}
1404
1406 BMFace *f,
1407 BMLoop *l_v1,
1408 BMLoop *l_v2,
1409 BMLoop **r_l,
1410#ifdef USE_BMESH_HOLES
1411 ListBase *holes,
1412#endif
1413 BMEdge *e_example,
1414 const bool no_double)
1415{
1416#ifdef USE_BMESH_HOLES
1417 BMLoopList *lst, *lst2;
1418#else
1419 int first_loop_f1;
1420#endif
1421
1422 BMFace *f2;
1423 BMLoop *l_iter, *l_first;
1424 BMLoop *l_f1 = nullptr, *l_f2 = nullptr;
1425 BMEdge *e;
1426 BMVert *v1 = l_v1->v, *v2 = l_v2->v;
1427 int f1len, f2len;
1428
1429 BLI_assert(f == l_v1->f && f == l_v2->f);
1430
1431 /* allocate new edge between v1 and v2 */
1432 e = BM_edge_create(bm, v1, v2, e_example, no_double ? BM_CREATE_NO_DOUBLE : BM_CREATE_NOP);
1433
1434 f2 = bm_face_create__sfme(bm, f);
1435 l_f1 = bm_loop_create(bm, v2, e, f, l_v2, eBMCreateFlag(0));
1436 l_f2 = bm_loop_create(bm, v1, e, f2, l_v1, eBMCreateFlag(0));
1437
1438 l_f1->prev = l_v2->prev;
1439 l_f2->prev = l_v1->prev;
1440 l_v2->prev->next = l_f1;
1441 l_v1->prev->next = l_f2;
1442
1443 l_f1->next = l_v1;
1444 l_f2->next = l_v2;
1445 l_v1->prev = l_f1;
1446 l_v2->prev = l_f2;
1447
1448#ifdef USE_BMESH_HOLES
1449 lst = f->loops.first;
1450 lst2 = f2->loops.first;
1451
1452 lst2->first = lst2->last = l_f2;
1453 lst->first = lst->last = l_f1;
1454#else
1455 /* find which of the faces the original first loop is in */
1456 l_iter = l_first = l_f1;
1457 first_loop_f1 = 0;
1458 do {
1459 if (l_iter == f->l_first) {
1460 first_loop_f1 = 1;
1461 }
1462 } while ((l_iter = l_iter->next) != l_first);
1463
1464 if (first_loop_f1) {
1465 /* Original first loop was in f1, find a suitable first loop for f2
1466 * which is as similar as possible to f1. the order matters for tools
1467 * such as dupli-faces. */
1468 if (f->l_first->prev == l_f1) {
1469 f2->l_first = l_f2->prev;
1470 }
1471 else if (f->l_first->next == l_f1) {
1472 f2->l_first = l_f2->next;
1473 }
1474 else {
1475 f2->l_first = l_f2;
1476 }
1477 }
1478 else {
1479 /* original first loop was in f2, further do same as above */
1480 f2->l_first = f->l_first;
1481
1482 if (f->l_first->prev == l_f2) {
1483 f->l_first = l_f1->prev;
1484 }
1485 else if (f->l_first->next == l_f2) {
1486 f->l_first = l_f1->next;
1487 }
1488 else {
1489 f->l_first = l_f1;
1490 }
1491 }
1492#endif
1493
1494 /* validate both loop */
1495 /* I don't know how many loops are supposed to be in each face at this point! FIXME */
1496
1497 /* go through all of f2's loops and make sure they point to it properly */
1498 l_iter = l_first = BM_FACE_FIRST_LOOP(f2);
1499 f2len = 0;
1500 do {
1501 l_iter->f = f2;
1502 f2len++;
1503 } while ((l_iter = l_iter->next) != l_first);
1504
1505 /* link up the new loops into the new edges radial */
1508
1509 f2->len = f2len;
1510
1511 f1len = 0;
1512 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
1513 do {
1514 f1len++;
1515 } while ((l_iter = l_iter->next) != l_first);
1516
1517 f->len = f1len;
1518
1519 if (r_l) {
1520 *r_l = l_f2;
1521 }
1522
1523#ifdef USE_BMESH_HOLES
1524 if (holes) {
1525 BLI_movelisttolist(&f2->loops, holes);
1526 }
1527 else {
1528 /* this code is not significant until holes actually work */
1529 // printf("WARNING: call to split face euler without holes argument; holes will be tossed.\n");
1530 for (lst = f->loops.last; lst != f->loops.first; lst = lst2) {
1531 lst2 = lst->prev;
1532 BLI_mempool_free(bm->looplistpool, lst);
1533 }
1534 }
1535#endif
1536
1539 BM_CHECK_ELEMENT(f2);
1540
1541 return f2;
1542}
1543
1545{
1546 BMLoop *l_next;
1547 BMEdge *e_new;
1548 BMVert *v_new, *v_old;
1549#ifndef NDEBUG
1550 int valence1, valence2;
1551 bool edok;
1552 int i;
1553#endif
1554
1555 BLI_assert(BM_vert_in_edge(e, tv) != false);
1556
1557 v_old = BM_edge_other_vert(e, tv);
1558
1559#ifndef NDEBUG
1560 valence1 = bmesh_disk_count(v_old);
1561 valence2 = bmesh_disk_count(tv);
1562#endif
1563
1564 /* order of 'e_new' verts should match 'e'
1565 * (so extruded faces don't flip) */
1566 v_new = BM_vert_create(bm, tv->co, tv, BM_CREATE_NOP);
1567 e_new = BM_edge_create(bm, tv, v_new, e, BM_CREATE_NOP);
1568
1569 bmesh_disk_edge_remove(e_new, tv);
1570 bmesh_disk_edge_remove(e_new, v_new);
1571
1572 bmesh_disk_vert_replace(e, v_new, tv);
1573
1574 /* add e_new to v_new's disk cycle */
1575 bmesh_disk_edge_append(e_new, v_new);
1576
1577 /* add e_new to tv's disk cycle */
1578 bmesh_disk_edge_append(e_new, tv);
1579
1580#ifndef NDEBUG
1581 /* verify disk cycles */
1582 edok = bmesh_disk_validate(valence1, v_old->e, v_old);
1583 BMESH_ASSERT(edok != false);
1584 edok = bmesh_disk_validate(valence2, tv->e, tv);
1585 BMESH_ASSERT(edok != false);
1586 edok = bmesh_disk_validate(2, v_new->e, v_new);
1587 BMESH_ASSERT(edok != false);
1588#endif
1589
1590 /* Split the radial cycle if present */
1591 l_next = e->l;
1592 e->l = nullptr;
1593 if (l_next) {
1594 BMLoop *l_new, *l;
1595#ifndef NDEBUG
1596 int radlen = bmesh_radial_length(l_next);
1597#endif
1598 bool is_first = true;
1599
1600 /* Take the next loop. Remove it from radial. Split it. Append to appropriate radials */
1601 while (l_next) {
1602 l = l_next;
1603 l->f->len++;
1604 l_next = l_next != l_next->radial_next ? l_next->radial_next : nullptr;
1606
1607 l_new = bm_loop_create(bm, nullptr, nullptr, l->f, l, eBMCreateFlag(0));
1608 l_new->prev = l;
1609 l_new->next = l->next;
1610 l_new->prev->next = l_new;
1611 l_new->next->prev = l_new;
1612 l_new->v = v_new;
1613
1614 /* assign the correct edge to the correct loop */
1615 if (BM_verts_in_edge(l_new->v, l_new->next->v, e)) {
1616 l_new->e = e;
1617 l->e = e_new;
1618
1619 /* append l into e_new's rad cycle */
1620 if (is_first) {
1621 is_first = false;
1622 l->radial_next = l->radial_prev = nullptr;
1623 }
1624
1625 bmesh_radial_loop_append(l_new->e, l_new);
1627 }
1628 else if (BM_verts_in_edge(l_new->v, l_new->next->v, e_new)) {
1629 l_new->e = e_new;
1630 l->e = e;
1631
1632 /* append l into e_new's rad cycle */
1633 if (is_first) {
1634 is_first = false;
1635 l->radial_next = l->radial_prev = nullptr;
1636 }
1637
1638 bmesh_radial_loop_append(l_new->e, l_new);
1640 }
1641 }
1642
1643#ifndef NDEBUG
1644 /* verify length of radial cycle */
1645 edok = bmesh_radial_validate(radlen, e->l);
1646 BMESH_ASSERT(edok != false);
1647 edok = bmesh_radial_validate(radlen, e_new->l);
1648 BMESH_ASSERT(edok != false);
1649
1650 /* verify loop->v and loop->next->v pointers for e */
1651 for (i = 0, l = e->l; i < radlen; i++, l = l->radial_next) {
1652 BMESH_ASSERT(l->e == e);
1653 // BMESH_ASSERT(l->radial_next == l);
1654 BMESH_ASSERT(!(l->prev->e != e_new && l->next->e != e_new));
1655
1656 edok = BM_verts_in_edge(l->v, l->next->v, e);
1657 BMESH_ASSERT(edok != false);
1658 BMESH_ASSERT(l->v != l->next->v);
1659 BMESH_ASSERT(l->e != l->next->e);
1660
1661 /* verify loop cycle for kloop->f */
1663 BM_CHECK_ELEMENT(l->v);
1664 BM_CHECK_ELEMENT(l->e);
1665 BM_CHECK_ELEMENT(l->f);
1666 }
1667 /* verify loop->v and loop->next->v pointers for e_new */
1668 for (i = 0, l = e_new->l; i < radlen; i++, l = l->radial_next) {
1669 BMESH_ASSERT(l->e == e_new);
1670 // BMESH_ASSERT(l->radial_next == l);
1671 BMESH_ASSERT(!(l->prev->e != e && l->next->e != e));
1672 edok = BM_verts_in_edge(l->v, l->next->v, e_new);
1673 BMESH_ASSERT(edok != false);
1674 BMESH_ASSERT(l->v != l->next->v);
1675 BMESH_ASSERT(l->e != l->next->e);
1676
1678 BM_CHECK_ELEMENT(l->v);
1679 BM_CHECK_ELEMENT(l->e);
1680 BM_CHECK_ELEMENT(l->f);
1681 }
1682#endif
1683 }
1684
1685 BM_CHECK_ELEMENT(e_new);
1686 BM_CHECK_ELEMENT(v_new);
1687 BM_CHECK_ELEMENT(v_old);
1689 BM_CHECK_ELEMENT(tv);
1690
1691 if (r_e) {
1692 *r_e = e_new;
1693 }
1694 return v_new;
1695}
1696
1698 BMEdge *e_kill,
1699 BMVert *v_kill,
1700 const bool do_del,
1701 const bool check_edge_exists,
1702 const bool kill_degenerate_faces,
1703 const bool kill_duplicate_faces)
1704{
1705 BMEdge *e_old;
1706 BMVert *v_old, *v_target;
1707 BMLoop *l_kill;
1708#ifndef NDEBUG
1709 int radlen, i;
1710 bool edok;
1711#endif
1712
1713 BLI_assert(BM_vert_in_edge(e_kill, v_kill));
1714
1715 if (BM_vert_in_edge(e_kill, v_kill) == 0) {
1716 return nullptr;
1717 }
1718
1719 if (bmesh_disk_count_at_most(v_kill, 3) == 2) {
1720#ifndef NDEBUG
1721 int valence1, valence2;
1722 BMLoop *l;
1723#endif
1724
1725 e_old = bmesh_disk_edge_next(e_kill, v_kill);
1726 v_target = BM_edge_other_vert(e_kill, v_kill);
1727 v_old = BM_edge_other_vert(e_old, v_kill);
1728
1729 /* check for double edges */
1730 if (BM_verts_in_edge(v_kill, v_target, e_old)) {
1731 return nullptr;
1732 }
1733
1734 BMEdge *e_splice;
1735 BLI_SMALLSTACK_DECLARE(faces_degenerate, BMFace *);
1736 BMLoop *l_kill_next;
1737
1738 /* Candidates for being duplicate. */
1739 BLI_SMALLSTACK_DECLARE(faces_duplicate_candidate, BMFace *);
1740
1741#ifndef NDEBUG
1742 /* For verification later, count valence of 'v_old' and 'v_target' */
1743 valence1 = bmesh_disk_count(v_old);
1744 valence2 = bmesh_disk_count(v_target);
1745#endif
1746
1747 if (check_edge_exists) {
1748 e_splice = BM_edge_exists(v_target, v_old);
1749 }
1750
1751 bmesh_disk_vert_replace(e_old, v_target, v_kill);
1752
1753 /* remove e_kill from 'v_target's disk cycle */
1754 bmesh_disk_edge_remove(e_kill, v_target);
1755
1756#ifndef NDEBUG
1757 /* deal with radial cycle of e_kill */
1758 radlen = bmesh_radial_length(e_kill->l);
1759#endif
1760 if (e_kill->l) {
1761
1762 /* fix the neighboring loops of all loops in e_kill's radial cycle */
1763 l_kill = e_kill->l;
1764 do {
1765 /* relink loops and fix vertex pointer */
1766 if (l_kill->next->v == v_kill) {
1767 l_kill->next->v = v_target;
1768 }
1769
1770 l_kill->next->prev = l_kill->prev;
1771 l_kill->prev->next = l_kill->next;
1772 if (BM_FACE_FIRST_LOOP(l_kill->f) == l_kill) {
1773 BM_FACE_FIRST_LOOP(l_kill->f) = l_kill->next;
1774 }
1775
1776 /* fix len attribute of face */
1777 l_kill->f->len--;
1778 if (kill_degenerate_faces && (l_kill->f->len < 3)) {
1779 BLI_SMALLSTACK_PUSH(faces_degenerate, l_kill->f);
1780 }
1781 else {
1782 /* The duplicate test isn't reliable at this point as `e_splice` might be set,
1783 * so the duplicate test needs to run once the edge has been spliced. */
1784 if (kill_duplicate_faces) {
1785 BLI_SMALLSTACK_PUSH(faces_duplicate_candidate, l_kill->f);
1786 }
1787 }
1788 l_kill_next = l_kill->radial_next;
1789
1790 bm_kill_only_loop(bm, l_kill);
1791
1792 } while ((l_kill = l_kill_next) != e_kill->l);
1793/* `e_kill->l` is invalid but the edge is freed next. */
1794#ifndef NDEBUG
1795 /* Validate radial cycle of e_old */
1796 edok = bmesh_radial_validate(radlen, e_old->l);
1797 BMESH_ASSERT(edok != false);
1798#endif
1799 }
1800 /* deallocate edge */
1801 bm_kill_only_edge(bm, e_kill);
1802
1803 /* deallocate vertex */
1804 if (do_del) {
1805 bm_kill_only_vert(bm, v_kill);
1806 }
1807 else {
1808 v_kill->e = nullptr;
1809 }
1810
1811#ifndef NDEBUG
1812 /* Validate disk cycle lengths of 'v_old', 'v_target' are unchanged */
1813 edok = bmesh_disk_validate(valence1, v_old->e, v_old);
1814 BMESH_ASSERT(edok != false);
1815 edok = bmesh_disk_validate(valence2, v_target->e, v_target);
1816 BMESH_ASSERT(edok != false);
1817
1818 /* Validate loop cycle of all faces attached to 'e_old' */
1819 for (i = 0, l = e_old->l; i < radlen; i++, l = l->radial_next) {
1820 BMESH_ASSERT(l->e == e_old);
1821 edok = BM_verts_in_edge(l->v, l->next->v, e_old);
1822 BMESH_ASSERT(edok != false);
1823 edok = bmesh_loop_validate(l->f);
1824 BMESH_ASSERT(edok != false);
1825
1827 BM_CHECK_ELEMENT(l->v);
1828 BM_CHECK_ELEMENT(l->e);
1829 BM_CHECK_ELEMENT(l->f);
1830 }
1831#endif
1832 if (check_edge_exists) {
1833 if (e_splice) {
1834 /* removes e_splice */
1835 BM_edge_splice(bm, e_old, e_splice);
1836 }
1837 }
1838
1839 if (kill_degenerate_faces) {
1840 BMFace *f_kill;
1841 while ((f_kill = static_cast<BMFace *>(BLI_SMALLSTACK_POP(faces_degenerate)))) {
1842 BM_face_kill(bm, f_kill);
1843 }
1844 }
1845
1846 if (kill_duplicate_faces) {
1847 BMFace *f_kill;
1848 while ((f_kill = static_cast<BMFace *>(BLI_SMALLSTACK_POP(faces_duplicate_candidate)))) {
1849 if (BM_face_find_double(f_kill)) {
1850 BM_face_kill(bm, f_kill);
1851 }
1852 }
1853 }
1854
1855 BM_CHECK_ELEMENT(v_old);
1856 BM_CHECK_ELEMENT(v_target);
1857 BM_CHECK_ELEMENT(e_old);
1858
1859 return e_old;
1860 }
1861 return nullptr;
1862}
1863
1865 BMEdge *e_kill,
1866 BMVert *v_kill,
1867 const bool do_del,
1868 const bool check_edge_exists,
1869 const bool kill_degenerate_faces)
1870{
1871 BLI_SMALLSTACK_DECLARE(faces_degenerate, BMFace *);
1872 BMVert *v_target = BM_edge_other_vert(e_kill, v_kill);
1873
1874 BLI_assert(BM_vert_in_edge(e_kill, v_kill));
1875
1876 if (e_kill->l) {
1877 BMLoop *l_kill, *l_first, *l_kill_next;
1878 l_kill = l_first = e_kill->l;
1879 do {
1880 /* relink loops and fix vertex pointer */
1881 if (l_kill->next->v == v_kill) {
1882 l_kill->next->v = v_target;
1883 }
1884
1885 l_kill->next->prev = l_kill->prev;
1886 l_kill->prev->next = l_kill->next;
1887 if (BM_FACE_FIRST_LOOP(l_kill->f) == l_kill) {
1888 BM_FACE_FIRST_LOOP(l_kill->f) = l_kill->next;
1889 }
1890
1891 /* fix len attribute of face */
1892 l_kill->f->len--;
1893 if (kill_degenerate_faces) {
1894 if (l_kill->f->len < 3) {
1895 BLI_SMALLSTACK_PUSH(faces_degenerate, l_kill->f);
1896 }
1897 }
1898 l_kill_next = l_kill->radial_next;
1899
1900 bm_kill_only_loop(bm, l_kill);
1901
1902 } while ((l_kill = l_kill_next) != l_first);
1903
1904 e_kill->l = nullptr;
1905 }
1906
1907 BM_edge_kill(bm, e_kill);
1908 BM_CHECK_ELEMENT(v_kill);
1909 BM_CHECK_ELEMENT(v_target);
1910
1911 if (v_target->e && v_kill->e) {
1912 /* Inline `BM_vert_splice(bm, v_target, v_kill)`. */
1913 BMEdge *e;
1914 while ((e = v_kill->e)) {
1915 BMEdge *e_target;
1916
1917 if (check_edge_exists) {
1918 e_target = BM_edge_exists(v_target, BM_edge_other_vert(e, v_kill));
1919 }
1920
1921 bmesh_edge_vert_swap(e, v_target, v_kill);
1922 BLI_assert(e->v1 != e->v2);
1923
1924 if (check_edge_exists) {
1925 if (e_target) {
1926 BM_edge_splice(bm, e_target, e);
1927 }
1928 }
1929 }
1930 }
1931
1932 if (kill_degenerate_faces) {
1933 BMFace *f_kill;
1934 while ((f_kill = static_cast<BMFace *>(BLI_SMALLSTACK_POP(faces_degenerate)))) {
1935 BM_face_kill(bm, f_kill);
1936 }
1937 }
1938
1939 if (do_del) {
1940 BLI_assert(v_kill->e == nullptr);
1941 bm_kill_only_vert(bm, v_kill);
1942 }
1943
1944 return v_target;
1945}
1946
1948{
1949 BMLoop *l_iter, *l_f1 = nullptr, *l_f2 = nullptr;
1950 int newlen = 0, i, f1len = 0, f2len = 0;
1951 bool edok;
1952 /* can't join a face to itself */
1953 if (f1 == f2) {
1954 return nullptr;
1955 }
1956
1957 /* validate that edge is 2-manifold edge */
1958 if (!BM_edge_is_manifold(e)) {
1959 return nullptr;
1960 }
1961
1962 /* verify that e is in both f1 and f2 */
1963 f1len = f1->len;
1964 f2len = f2->len;
1965
1966 if (!((l_f1 = BM_face_edge_share_loop(f1, e)) && (l_f2 = BM_face_edge_share_loop(f2, e)))) {
1967 return nullptr;
1968 }
1969
1970 /* validate direction of f2's loop cycle is compatible */
1971 if (l_f1->v == l_f2->v) {
1972 return nullptr;
1973 }
1974
1975 /* validate that for each face, each vertex has another edge in its disk cycle that is
1976 * not e, and not shared. */
1977 if (BM_edge_in_face(l_f1->next->e, f2) || BM_edge_in_face(l_f1->prev->e, f2) ||
1978 BM_edge_in_face(l_f2->next->e, f1) || BM_edge_in_face(l_f2->prev->e, f1))
1979 {
1980 return nullptr;
1981 }
1982
1983 /* validate only one shared edge */
1984 if (BM_face_share_edge_count(f1, f2) > 1) {
1985 return nullptr;
1986 }
1987
1988 /* validate no internal join */
1989 {
1990 bool is_dupe = false;
1991
1992 /* TODO: skip clearing once this is ensured. */
1993 for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f2); i < f2len; i++, l_iter = l_iter->next) {
1995 }
1996
1997 for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f1); i < f1len; i++, l_iter = l_iter->next) {
1998 BM_elem_flag_set(l_iter->v, BM_ELEM_INTERNAL_TAG, l_iter != l_f1);
1999 }
2000 for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f2); i < f2len; i++, l_iter = l_iter->next) {
2001 if (l_iter != l_f2) {
2002 /* as soon as a duplicate is found, bail out */
2003 if (BM_elem_flag_test(l_iter->v, BM_ELEM_INTERNAL_TAG)) {
2004 is_dupe = true;
2005 break;
2006 }
2007 }
2008 }
2009 /* Cleanup tags. */
2010 for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f1); i < f1len; i++, l_iter = l_iter->next) {
2012 }
2013 if (is_dupe) {
2014 return nullptr;
2015 }
2016 }
2017
2018 /* join the two loop */
2019 l_f1->prev->next = l_f2->next;
2020 l_f2->next->prev = l_f1->prev;
2021
2022 l_f1->next->prev = l_f2->prev;
2023 l_f2->prev->next = l_f1->next;
2024
2025 /* If `l_f1` was base-loop, make `l_f1->next` the base. */
2026 if (BM_FACE_FIRST_LOOP(f1) == l_f1) {
2027 BM_FACE_FIRST_LOOP(f1) = l_f1->next;
2028 }
2029
2030 /* increase length of f1 */
2031 f1->len += (f2->len - 2);
2032
2033 /* make sure each loop points to the proper face */
2034 newlen = f1->len;
2035 for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f1); i < newlen; i++, l_iter = l_iter->next) {
2036 l_iter->f = f1;
2037 }
2038
2039 /* remove edge from the disk cycle of its two vertices */
2040 bmesh_disk_edge_remove(l_f1->e, l_f1->e->v1);
2041 bmesh_disk_edge_remove(l_f1->e, l_f1->e->v2);
2042
2043 /* deallocate edge and its two loops as well as f2 */
2044 if (bm->etoolflagpool) {
2045 BLI_mempool_free(bm->etoolflagpool, ((BMEdge_OFlag *)l_f1->e)->oflags);
2046 }
2047 BLI_mempool_free(bm->epool, l_f1->e);
2048 bm->totedge--;
2049 BLI_mempool_free(bm->lpool, l_f1);
2050 bm->totloop--;
2051 BLI_mempool_free(bm->lpool, l_f2);
2052 bm->totloop--;
2053 if (bm->ftoolflagpool) {
2054 BLI_mempool_free(bm->ftoolflagpool, ((BMFace_OFlag *)f2)->oflags);
2055 }
2056 BLI_mempool_free(bm->fpool, f2);
2057 bm->totface--;
2058 /* account for both above */
2059 bm->elem_index_dirty |= BM_EDGE | BM_LOOP | BM_FACE;
2060
2061 BM_CHECK_ELEMENT(f1);
2062
2063 /* validate the new loop cycle */
2064 edok = bmesh_loop_validate(f1);
2065 BMESH_ASSERT(edok != false);
2066
2067 return f1;
2068}
2069
2071{
2072 bool is_double = false;
2073
2074 BLI_assert(BM_edge_exists(v_a, v_b) == nullptr);
2075
2076 if (v_a->e && v_b->e) {
2077 BMEdge *e, *e_first;
2078
2079#define VERT_VISIT _FLAG_WALK
2080
2081 /* tag 'v_a' */
2082 e = e_first = v_a->e;
2083 do {
2084 BMVert *v_other = BM_edge_other_vert(e, v_a);
2087 } while ((e = BM_DISK_EDGE_NEXT(e, v_a)) != e_first);
2088
2089 /* check 'v_b' connects to 'v_a' edges */
2090 e = e_first = v_b->e;
2091 do {
2092 BMVert *v_other = BM_edge_other_vert(e, v_b);
2093 if (BM_ELEM_API_FLAG_TEST(v_other, VERT_VISIT)) {
2094 is_double = true;
2095 break;
2096 }
2097 } while ((e = BM_DISK_EDGE_NEXT(e, v_b)) != e_first);
2098
2099 /* cleanup */
2100 e = e_first = v_a->e;
2101 do {
2102 BMVert *v_other = BM_edge_other_vert(e, v_a);
2105 } while ((e = BM_DISK_EDGE_NEXT(e, v_a)) != e_first);
2106
2107#undef VERT_VISIT
2108 }
2109
2110 return is_double;
2111}
2112
2113bool BM_vert_splice(BMesh *bm, BMVert *v_dst, BMVert *v_src)
2114{
2115 BMEdge *e;
2116
2117 /* verts already spliced */
2118 if (v_src == v_dst) {
2119 return false;
2120 }
2121
2122 BLI_assert(BM_vert_pair_share_face_check(v_src, v_dst) == false);
2123
2124 /* move all the edges from 'v_src' disk to 'v_dst' */
2125 while ((e = v_src->e)) {
2126 bmesh_edge_vert_swap(e, v_dst, v_src);
2127 BLI_assert(e->v1 != e->v2);
2128 }
2129
2130 BM_CHECK_ELEMENT(v_src);
2131 BM_CHECK_ELEMENT(v_dst);
2132
2133 /* 'v_src' is unused now, and can be killed */
2134 BM_vert_kill(bm, v_src);
2135
2136 return true;
2137}
2138
2139/* -------------------------------------------------------------------- */
2142
2143/* BM_edge_face_count(e) >= 1 */
2145{
2146 return (e->l && e->l->radial_next != e->l);
2147}
2148
2150 BMesh *bm, BMVert *v, BMVert ***r_vout, int *r_vout_len, const bool copy_select)
2151{
2152 int v_edges_num = 0;
2153
2154 /* Detailed notes on array use since this is stack memory, we have to be careful */
2155
2156 /* newly created vertices, only use when 'r_vout' is set
2157 * (total size will be number of fans) */
2158 BLI_SMALLSTACK_DECLARE(verts_new, BMVert *);
2159 /* fill with edges from the face-fan, clearing on completion
2160 * (total size will be max fan edge count) */
2162 /* temp store edges to walk over when filling 'edges',
2163 * (total size will be max radial edges of any edge) */
2164 BLI_SMALLSTACK_DECLARE(edges_search, BMEdge *);
2165
2166 /* number of resulting verts, include self */
2167 int verts_num = 1;
2168 /* track the total number of edges handled, so we know when we've found the last fan */
2169 int edges_found = 0;
2170
2171#define EDGE_VISIT _FLAG_WALK
2172
2173 /* count and flag at once */
2174 if (v->e) {
2175 BMEdge *e_first, *e_iter;
2176 e_iter = e_first = v->e;
2177 do {
2178 v_edges_num += 1;
2179
2182 } while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first);
2183
2184 while (true) {
2185 /* Considering only edges and faces incident on vertex v, walk
2186 * the edges & collect in the 'edges' list for splitting */
2187
2188 BMEdge *e = v->e;
2190
2191 do {
2193 BLI_SMALLSTACK_PUSH(edges, e);
2194 edges_found += 1;
2195
2196 if (e->l) {
2197 BMLoop *l_iter, *l_first;
2198 l_iter = l_first = e->l;
2199 do {
2200 BMLoop *l_adjacent = (l_iter->v == v) ? l_iter->prev : l_iter->next;
2201 BLI_assert(BM_vert_in_edge(l_adjacent->e, v));
2202 if (BM_ELEM_API_FLAG_TEST(l_adjacent->e, EDGE_VISIT)) {
2204 BLI_SMALLSTACK_PUSH(edges_search, l_adjacent->e);
2205 }
2206 } while ((l_iter = l_iter->radial_next) != l_first);
2207 }
2208 } while ((e = static_cast<BMEdge *>(BLI_SMALLSTACK_POP(edges_search))));
2209
2210 /* now we have all edges connected to 'v->e' */
2211
2212 BLI_assert(edges_found <= v_edges_num);
2213
2214 if (edges_found == v_edges_num) {
2215 /* We're done! The remaining edges in 'edges' form the last fan,
2216 * which can be left as is.
2217 * if 'edges' were allocated it'd be freed here. */
2218 break;
2219 }
2220
2221 BMVert *v_new;
2222
2223 v_new = BM_vert_create(bm, v->co, v, BM_CREATE_NOP);
2224 if (copy_select) {
2225 BM_elem_select_copy(bm, v_new, v);
2226 }
2227
2228 while ((e = static_cast<BMEdge *>(BLI_SMALLSTACK_POP(edges)))) {
2229 bmesh_edge_vert_swap(e, v_new, v);
2230 }
2231
2232 if (r_vout) {
2233 BLI_SMALLSTACK_PUSH(verts_new, v_new);
2234 }
2235 verts_num += 1;
2236 }
2237 }
2238
2239#undef EDGE_VISIT
2240
2241 /* flags are clean now, handle return values */
2242
2243 if (r_vout_len != nullptr) {
2244 *r_vout_len = verts_num;
2245 }
2246
2247 if (r_vout != nullptr) {
2248 BMVert **verts;
2249
2250 verts = MEM_malloc_arrayN<BMVert *>(verts_num, __func__);
2251 *r_vout = verts;
2252
2253 verts[0] = v;
2254 BLI_SMALLSTACK_AS_TABLE(verts_new, &verts[1]);
2255 }
2256}
2257
2278{
2279 do {
2280 LinkNode *n_orig = static_cast<LinkNode *>(edges_separate->link);
2281 do {
2282 LinkNode *n_prev = n_orig;
2283 LinkNode *n_step = n_orig->next;
2284 BMEdge *e_orig = static_cast<BMEdge *>(n_orig->link);
2285 do {
2286 BMEdge *e = static_cast<BMEdge *>(n_step->link);
2287 BLI_assert(e != e_orig);
2288 if ((e->v1 == e_orig->v1) && (e->v2 == e_orig->v2) && BM_edge_splice(bm, e_orig, e)) {
2289 /* don't visit again */
2290 n_prev->next = n_step->next;
2291 }
2292 else {
2293 n_prev = n_step;
2294 }
2295 } while ((n_step = n_step->next));
2296
2297 } while ((n_orig = n_orig->next) && n_orig->next);
2298 } while ((edges_separate = edges_separate->next));
2299}
2300
2302 BMVert *v,
2303 BMEdge **e_in,
2304 int e_in_len,
2305 const bool copy_select,
2306 BMVert ***r_vout,
2307 int *r_vout_len)
2308{
2309 LinkNode *edges_separate = nullptr;
2310 int i;
2311
2312 for (i = 0; i < e_in_len; i++) {
2313 BMEdge *e = e_in[i];
2315 LinkNode *edges_orig = nullptr;
2316 do {
2317 BMLoop *l_sep = e->l;
2318 bmesh_kernel_edge_separate(bm, e, l_sep, copy_select);
2319 BLI_linklist_prepend_alloca(&edges_orig, l_sep->e);
2320 BLI_assert(e != l_sep->e);
2321 } while (bm_edge_supports_separate(e));
2322 BLI_linklist_prepend_alloca(&edges_orig, e);
2323 BLI_linklist_prepend_alloca(&edges_separate, edges_orig);
2324 }
2325 }
2326
2327 bmesh_kernel_vert_separate(bm, v, r_vout, r_vout_len, copy_select);
2328
2329 if (edges_separate) {
2330 bmesh_kernel_vert_separate__cleanup(bm, edges_separate);
2331 }
2332}
2333
2335 BMVert *v,
2336 const char hflag,
2337 const bool copy_select,
2338 BMVert ***r_vout,
2339 int *r_vout_len)
2340{
2341 LinkNode *edges_separate = nullptr;
2342 BMEdge *e_iter, *e_first;
2343
2344 e_iter = e_first = v->e;
2345 do {
2346 if (BM_elem_flag_test(e_iter, hflag)) {
2347 BMEdge *e = e_iter;
2349 LinkNode *edges_orig = nullptr;
2350 do {
2351 BMLoop *l_sep = e->l;
2352 bmesh_kernel_edge_separate(bm, e, l_sep, copy_select);
2353 /* trick to avoid looping over separated edges */
2354 if (edges_separate == nullptr && edges_orig == nullptr) {
2355 e_first = l_sep->e;
2356 }
2357 BLI_linklist_prepend_alloca(&edges_orig, l_sep->e);
2358 BLI_assert(e != l_sep->e);
2359 } while (bm_edge_supports_separate(e));
2360 BLI_linklist_prepend_alloca(&edges_orig, e);
2361 BLI_linklist_prepend_alloca(&edges_separate, edges_orig);
2362 }
2363 }
2364 } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first);
2365
2366 bmesh_kernel_vert_separate(bm, v, r_vout, r_vout_len, copy_select);
2367
2368 if (edges_separate) {
2369 bmesh_kernel_vert_separate__cleanup(bm, edges_separate);
2370 }
2371}
2372
2374 BMesh * /*bm*/, BMVert *v_dst, BMVert *v_src, bool (*testfn)(BMEdge *, void *arg), void *arg)
2375{
2376 LinkNode *edges_hflag = nullptr;
2377 BMEdge *e_iter, *e_first;
2378
2379 e_iter = e_first = v_src->e;
2380 do {
2381 if (testfn(e_iter, arg)) {
2382 BLI_linklist_prepend_alloca(&edges_hflag, e_iter);
2383 }
2384 } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v_src)) != e_first);
2385
2386 if (edges_hflag) {
2387 do {
2388 e_iter = static_cast<BMEdge *>(edges_hflag->link);
2389 bmesh_disk_vert_replace(e_iter, v_dst, v_src);
2390 } while ((edges_hflag = edges_hflag->next));
2391 }
2392}
2393
2395
2396bool BM_edge_splice(BMesh *bm, BMEdge *e_dst, BMEdge *e_src)
2397{
2398 BMLoop *l;
2399
2400 if (!BM_vert_in_edge(e_src, e_dst->v1) || !BM_vert_in_edge(e_src, e_dst->v2)) {
2401 /* not the same vertices can't splice */
2402
2403 /* the caller should really make sure this doesn't happen ever
2404 * so assert on release builds */
2405 BLI_assert(0);
2406
2407 return false;
2408 }
2409
2410 while (e_src->l) {
2411 l = e_src->l;
2412 BLI_assert(BM_vert_in_edge(e_dst, l->v));
2413 BLI_assert(BM_vert_in_edge(e_dst, l->next->v));
2416 }
2417
2418 BLI_assert(bmesh_radial_length(e_src->l) == 0);
2419
2420 BM_CHECK_ELEMENT(e_src);
2421 BM_CHECK_ELEMENT(e_dst);
2422
2423 /* removes from disks too */
2424 BM_edge_kill(bm, e_src);
2425
2426 return true;
2427}
2428
2429void bmesh_kernel_edge_separate(BMesh *bm, BMEdge *e, BMLoop *l_sep, const bool copy_select)
2430{
2431 BMEdge *e_new;
2432#ifndef NDEBUG
2433 const int radlen = bmesh_radial_length(e->l);
2434#endif
2435
2436 BLI_assert(l_sep->e == e);
2437 BLI_assert(e->l);
2438
2439 if (BM_edge_is_boundary(e)) {
2440 BLI_assert(0); /* no cut required */
2441 return;
2442 }
2443
2444 if (l_sep == e->l) {
2445 e->l = l_sep->radial_next;
2446 }
2447
2448 e_new = BM_edge_create(bm, e->v1, e->v2, e, BM_CREATE_NOP);
2450 bmesh_radial_loop_append(e_new, l_sep);
2451 l_sep->e = e_new;
2452
2453 if (copy_select) {
2454 BM_elem_select_copy(bm, e_new, e);
2455 }
2456
2457 BLI_assert(bmesh_radial_length(e->l) == radlen - 1);
2458 BLI_assert(bmesh_radial_length(e_new->l) == 1);
2459
2460 BM_CHECK_ELEMENT(e_new);
2462}
2463
2465{
2466 BMVert *v_new = nullptr;
2467 BMVert *v_sep = l_sep->v;
2468 BMEdge *e_iter;
2469 BMEdge *edges[2];
2470 int i;
2471
2472 /* peel the face from the edge radials on both sides of the
2473 * loop vert, disconnecting the face from its fan */
2474 if (!BM_edge_is_boundary(l_sep->e)) {
2475 bmesh_kernel_edge_separate(bm, l_sep->e, l_sep, false);
2476 }
2477 if (!BM_edge_is_boundary(l_sep->prev->e)) {
2478 bmesh_kernel_edge_separate(bm, l_sep->prev->e, l_sep->prev, false);
2479 }
2480
2481/* do inline, below */
2482#if 0
2483 if (BM_vert_edge_count_is_equal(v_sep, 2)) {
2484 return v_sep;
2485 }
2486#endif
2487
2488 /* Search for an edge unattached to this loop */
2489 e_iter = v_sep->e;
2490 while (!ELEM(e_iter, l_sep->e, l_sep->prev->e)) {
2491 e_iter = bmesh_disk_edge_next(e_iter, v_sep);
2492
2493 /* We've come back around to the initial edge, all touch this loop.
2494 * If there are still only two edges out of v_sep,
2495 * then this whole URMV was just a no-op, so exit now. */
2496 if (e_iter == v_sep->e) {
2498 return v_sep;
2499 }
2500 }
2501
2502 v_sep->e = l_sep->e;
2503
2504 v_new = BM_vert_create(bm, v_sep->co, v_sep, BM_CREATE_NOP);
2505
2506 edges[0] = l_sep->e;
2507 edges[1] = l_sep->prev->e;
2508
2509 for (i = 0; i < ARRAY_SIZE(edges); i++) {
2510 BMEdge *e = edges[i];
2511 bmesh_edge_vert_swap(e, v_new, v_sep);
2512 }
2513
2514 BLI_assert(v_sep != l_sep->v);
2515 BLI_assert(v_sep->e != l_sep->v->e);
2516
2517 BM_CHECK_ELEMENT(l_sep);
2518 BM_CHECK_ELEMENT(v_sep);
2519 BM_CHECK_ELEMENT(edges[0]);
2520 BM_CHECK_ELEMENT(edges[1]);
2521 BM_CHECK_ELEMENT(v_new);
2522
2523 return v_new;
2524}
2525
2527{
2528 BMVert *v_sep = larr[0]->v;
2529 BMVert *v_new;
2530 int edges_len = 0;
2531 int i;
2532 /* any edges not owned by 'larr' loops connected to 'v_sep'? */
2533 bool is_mixed_edge_any = false;
2534 /* any loops not owned by 'larr' radially connected to 'larr' loop edges? */
2535 bool is_mixed_loop_any = false;
2536
2537#define LOOP_VISIT _FLAG_WALK
2538#define EDGE_VISIT _FLAG_WALK
2539
2540 for (i = 0; i < larr_len; i++) {
2541 BMLoop *l_sep = larr[i];
2542
2543 /* all must be from the same vert! */
2544 BLI_assert(v_sep == l_sep->v);
2545
2548
2549 /* weak! but it makes it simpler to check for edges to split
2550 * while doing a radial loop (where loops may be adjacent) */
2553
2554 BMLoop *loop_pair[2] = {l_sep, l_sep->prev};
2555 for (int j = 0; j < ARRAY_SIZE(loop_pair); j++) {
2556 BMEdge *e = loop_pair[j]->e;
2559 edges_len += 1;
2560 }
2561 }
2562 }
2563
2564 BMEdge **edges = BLI_array_alloca(edges, edges_len);
2565 STACK_DECLARE(edges);
2566
2567 STACK_INIT(edges, edges_len);
2568
2569 {
2570 BMEdge *e_first, *e_iter;
2571 e_iter = e_first = v_sep->e;
2572 do {
2573 if (BM_ELEM_API_FLAG_TEST(e_iter, EDGE_VISIT)) {
2574 BMLoop *l_iter, *l_first;
2575 bool is_mixed_loop = false;
2576
2577 l_iter = l_first = e_iter->l;
2578 do {
2579 if (!BM_ELEM_API_FLAG_TEST(l_iter, LOOP_VISIT)) {
2580 is_mixed_loop = true;
2581 break;
2582 }
2583 } while ((l_iter = l_iter->radial_next) != l_first);
2584
2585 if (is_mixed_loop) {
2586 /* ensure the first loop is one we don't own so we can do a quick check below
2587 * on the edge's loop-flag to see if the edge is mixed or not. */
2588 e_iter->l = l_iter;
2589
2590 is_mixed_loop_any = true;
2591 }
2592
2593 STACK_PUSH(edges, e_iter);
2594 }
2595 else {
2596 /* at least one edge attached isn't connected to our loops */
2597 is_mixed_edge_any = true;
2598 }
2599 } while ((e_iter = bmesh_disk_edge_next(e_iter, v_sep)) != e_first);
2600 }
2601
2602 BLI_assert(edges_len == STACK_SIZE(edges));
2603
2604 if (is_mixed_loop_any == false && is_mixed_edge_any == false) {
2605 /* all loops in 'larr' are the sole owners of their edges.
2606 * nothing to split away from, this is a no-op */
2607 v_new = v_sep;
2608 }
2609 else {
2610 v_new = BM_vert_create(bm, v_sep->co, v_sep, BM_CREATE_NOP);
2611
2612 for (i = 0; i < STACK_SIZE(edges); i++) {
2613 BMEdge *e = edges[i];
2614 BMLoop *l_iter, *l_first, *l_next;
2615 BMEdge *e_new;
2616
2617 /* disable so copied edge isn't left dirty (loop edges are cleared last too) */
2619
2620 /* will always be false when (is_mixed_loop_any == false) */
2622 /* edge has some loops owned by us, some owned by other loops */
2623 BMVert *e_new_v_pair[2];
2624
2625 if (e->v1 == v_sep) {
2626 e_new_v_pair[0] = v_new;
2627 e_new_v_pair[1] = e->v2;
2628 }
2629 else {
2630 BLI_assert(v_sep == e->v2);
2631 e_new_v_pair[0] = e->v1;
2632 e_new_v_pair[1] = v_new;
2633 }
2634
2635 e_new = BM_edge_create(bm, UNPACK2(e_new_v_pair), e, BM_CREATE_NOP);
2636
2637 /* now moved all loops from 'larr' to this newly created edge */
2638 l_iter = l_first = e->l;
2639 do {
2640 l_next = l_iter->radial_next;
2641 if (BM_ELEM_API_FLAG_TEST(l_iter, LOOP_VISIT)) {
2642 bmesh_radial_loop_remove(e, l_iter);
2643 bmesh_radial_loop_append(e_new, l_iter);
2644 l_iter->e = e_new;
2645 }
2646 } while ((l_iter = l_next) != l_first);
2647 }
2648 else {
2649 /* we own the edge entirely, replace the vert */
2650 bmesh_disk_vert_replace(e, v_new, v_sep);
2651 }
2652
2653 /* loop vert is handled last! */
2654 }
2655 }
2656
2657 for (i = 0; i < larr_len; i++) {
2658 BMLoop *l_sep = larr[i];
2659
2660 l_sep->v = v_new;
2661
2668
2671 }
2672
2673#undef LOOP_VISIT
2674#undef EDGE_VISIT
2675
2676 return v_new;
2677}
2678
2680{
2681 BMLoop *l_iter, *l_first;
2682
2683 BLI_assert(ELEM(v_src, e->v1, e->v2));
2684 bmesh_disk_vert_replace(e, v_dst, v_src);
2685
2686 l_iter = l_first = e->l;
2687 do {
2688 if (l_iter->v == v_src) {
2689 l_iter->v = v_dst;
2690 if (BM_vert_in_edge(l_iter->prev->e, v_src)) {
2691 bmesh_edge_vert_swap__recursive(l_iter->prev->e, v_dst, v_src);
2692 }
2693 }
2694 else if (l_iter->next->v == v_src) {
2695 l_iter->next->v = v_dst;
2696 if (BM_vert_in_edge(l_iter->next->e, v_src)) {
2697 bmesh_edge_vert_swap__recursive(l_iter->next->e, v_dst, v_src);
2698 }
2699 }
2700 else {
2701 BLI_assert(l_iter->prev->v != v_src);
2702 }
2703 } while ((l_iter = l_iter->radial_next) != l_first);
2704}
2705
2707{
2708 BMVert *v_new = BM_vert_create(bm, l_sep->v->co, l_sep->v, BM_CREATE_NOP);
2709 /* passing either 'l_sep->e', 'l_sep->prev->e' will work */
2710 bmesh_edge_vert_swap__recursive(l_sep->e, v_new, l_sep->v);
2711 BLI_assert(l_sep->v == v_new);
2712 return v_new;
2713}
2714
2716{
2717 BMLoop *l_iter, *l_first;
2718
2719 BLI_assert(f_a != f_b);
2720
2721 l_iter = l_first = BM_FACE_FIRST_LOOP(f_a);
2722 do {
2723 l_iter->f = f_b;
2724 } while ((l_iter = l_iter->next) != l_first);
2725
2726 l_iter = l_first = BM_FACE_FIRST_LOOP(f_b);
2727 do {
2728 l_iter->f = f_a;
2729 } while ((l_iter = l_iter->next) != l_first);
2730
2731 std::swap((*f_a), (*f_b));
2732
2733 /* swap back */
2734 std::swap(f_a->head.data, f_b->head.data);
2735 std::swap(f_a->head.index, f_b->head.index);
2736}
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:18
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_INLINE
void void void BLI_movelisttolist(ListBase *dst, ListBase *src) ATTR_NONNULL(1
BLI_INLINE void BLI_listbase_clear(ListBase *lb)
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void BLI_remlink(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:131
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 LIKELY(x)
#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_SPACEARR_DIRTY_ALL
#define BM_DISK_EDGE_NEXT(e, v)
#define BM_NGON_MAX
#define BM_FACE_FIRST_LOOP(p)
@ BM_ELEM_SMOOTH
@ BM_ELEM_INTERNAL_TAG
@ BM_LOOP
#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)
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)
BMFace * BM_faces_join(BMesh *bm, BMFace **faces, int totface, const bool do_del, BMFace **r_double)
Join Connected Faces.
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:41
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:27
@ BM_CREATE_NOP
Definition bmesh_core.hh:28
@ BM_CREATE_SKIP_CD
Definition bmesh_core.hh:36
@ BM_CREATE_NO_DOUBLE
Definition bmesh_core.hh:30
#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)
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)
int bmesh_disk_count_at_most(const BMVert *v, int count_max)
#define BM_ELEM_API_FLAG_ENABLE(element, f)
@ _FLAG_JF
@ _FLAG_ELEM_CHECK
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()
int64_t size() const
void append(const T &value)
bool is_empty() const
nullptr float
static float verts[][3]
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
static char faces[256]
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
void * link
struct LinkNode * next
i
Definition text_draw.cc:230
uint len