Blender V4.5
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 | BM_ELEM_DRAW;
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[2]));
164 /* --- done --- */
165
168
169 /* may add to middle of the pool */
170 bm->elem_index_dirty |= BM_EDGE;
171 bm->elem_table_dirty |= BM_EDGE;
172 bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
173
174 bm->totedge++;
175
176 if (!(create_flag & BM_CREATE_SKIP_CD)) {
177 if (e_example) {
178 BM_elem_attrs_copy(bm, e_example, e);
179 }
180 else {
181 CustomData_bmesh_set_default(&bm->edata, &e->head.data);
182 }
183 }
184
186
187 return e;
188}
189
196 BMVert *v,
197 BMEdge *e,
198 BMFace *f,
199 const BMLoop *l_example,
200 const eBMCreateFlag create_flag)
201{
202 BMLoop *l = nullptr;
203
204 l = static_cast<BMLoop *>(BLI_mempool_alloc(bm->lpool));
205
206 BLI_assert((l_example == nullptr) || (l_example->head.htype == BM_LOOP));
207 BLI_assert(!(create_flag & 1));
208
209#ifndef NDEBUG
210 if (l_example) {
211 /* ensure passing a loop is either sharing the same vertex, or entirely disconnected
212 * use to catch mistake passing in loop offset-by-one. */
213 BLI_assert((v == l_example->v) || !ELEM(v, l_example->prev->v, l_example->next->v));
214 }
215#endif
216
217 /* --- assign all members --- */
218 l->head.data = nullptr;
219
220#ifdef USE_DEBUG_INDEX_MEMCHECK
221 DEBUG_MEMCHECK_INDEX_INVALIDATE(l);
222#else
223 BM_elem_index_set(l, -1); /* set_ok_invalid */
224#endif
225
226 l->head.htype = BM_LOOP;
227 l->head.hflag = 0;
228 l->head.api_flag = 0;
229
230 l->v = v;
231 l->e = e;
232 l->f = f;
233
234 l->radial_next = nullptr;
235 l->radial_prev = nullptr;
236 l->next = nullptr;
237 l->prev = nullptr;
238 /* --- done --- */
239
240 /* may add to middle of the pool */
241 bm->elem_index_dirty |= BM_LOOP;
242 bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
243
244 bm->totloop++;
245
246 if (!(create_flag & BM_CREATE_SKIP_CD)) {
247 if (l_example) {
248 /* no need to copy attrs, just handle customdata */
249 CustomData_bmesh_copy_block(bm->ldata, l_example->head.data, &l->head.data);
250 }
251 else {
252 CustomData_bmesh_set_default(&bm->ldata, &l->head.data);
253 }
254 }
255
256 return l;
257}
258
260 BMesh *bm, BMFace *f, BMVert *startv, BMEdge *starte, const eBMCreateFlag create_flag)
261{
262#ifdef USE_BMESH_HOLES
263 BMLoopList *lst = BLI_mempool_calloc(bm->looplistpool);
264#endif
265 BMLoop *l = bm_loop_create(bm, startv, starte, f, nullptr /* starte->l */, create_flag);
266
268
269#ifdef USE_BMESH_HOLES
270 lst->first = lst->last = l;
271 BLI_addtail(&f->loops, lst);
272#else
273 f->l_first = l;
274#endif
275
276 return l;
277}
278
280 BMFace *f,
281 const bool copy_verts,
282 const bool copy_edges)
283{
285 BMEdge **edges = BLI_array_alloca(edges, f->len);
286 BMLoop *l_iter;
287 BMLoop *l_first;
288 BMFace *f_copy;
289 int i;
290
291 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
292 i = 0;
293 do {
294 if (copy_verts) {
295 verts[i] = BM_vert_create(bm_dst, l_iter->v->co, l_iter->v, BM_CREATE_NOP);
296 }
297 else {
298 verts[i] = l_iter->v;
299 }
300 i++;
301 } while ((l_iter = l_iter->next) != l_first);
302
303 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
304 i = 0;
305 do {
306 if (copy_edges) {
307 BMVert *v1, *v2;
308
309 if (l_iter->e->v1 == verts[i]) {
310 v1 = verts[i];
311 v2 = verts[(i + 1) % f->len];
312 }
313 else {
314 v2 = verts[i];
315 v1 = verts[(i + 1) % f->len];
316 }
317
318 edges[i] = BM_edge_create(bm_dst, v1, v2, l_iter->e, BM_CREATE_NOP);
319 }
320 else {
321 edges[i] = l_iter->e;
322 }
323 i++;
324 } while ((l_iter = l_iter->next) != l_first);
325
326 f_copy = BM_face_create(bm_dst, verts, edges, f->len, nullptr, BM_CREATE_SKIP_CD);
327
328 return f_copy;
329}
330
332 const BMCustomDataCopyMap &cd_face_map,
333 const BMCustomDataCopyMap &cd_loop_map,
334 BMFace *f,
335 const bool copy_verts,
336 const bool copy_edges)
337{
338 BMFace *f_copy = bm_face_copy_impl(bm_dst, f, copy_verts, copy_edges);
339
340 /* Copy custom-data. */
341 BM_elem_attrs_copy(bm_dst, cd_face_map, f, f_copy);
342
343 BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
344 BMLoop *l_copy = BM_FACE_FIRST_LOOP(f_copy);
345 BMLoop *l_iter = l_first;
346 do {
347 BM_elem_attrs_copy(bm_dst, cd_loop_map, l_iter, l_copy);
348 l_copy = l_copy->next;
349 } while ((l_iter = l_iter->next) != l_first);
350 return f_copy;
351}
352
353BMFace *BM_face_copy(BMesh *bm_dst, BMFace *f, const bool copy_verts, const bool copy_edges)
354{
355 BMFace *f_copy = bm_face_copy_impl(bm_dst, f, copy_verts, copy_edges);
356
357 /* Copy custom-data. */
358 BM_elem_attrs_copy(bm_dst, f, f_copy);
359
360 BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
361 BMLoop *l_copy = BM_FACE_FIRST_LOOP(f_copy);
362 BMLoop *l_iter = l_first;
363 do {
364 BM_elem_attrs_copy(bm_dst, l_iter, l_copy);
365 l_copy = l_copy->next;
366 } while ((l_iter = l_iter->next) != l_first);
367 return f_copy;
368}
369
377{
378 BMFace *f;
379
380 f = static_cast<BMFace *>(BLI_mempool_alloc(bm->fpool));
381
382 /* --- assign all members --- */
383 f->head.data = nullptr;
384#ifdef USE_DEBUG_INDEX_MEMCHECK
385 DEBUG_MEMCHECK_INDEX_INVALIDATE(f);
386#else
387 BM_elem_index_set(f, -1); /* set_ok_invalid */
388#endif
389
390 f->head.htype = BM_FACE;
391 f->head.hflag = 0;
392 f->head.api_flag = 0;
393
394 /* allocate flags */
395 if (bm->use_toolflags) {
396 ((BMFace_OFlag *)f)->oflags = static_cast<BMFlagLayer *>(
397 bm->ftoolflagpool ? BLI_mempool_calloc(bm->ftoolflagpool) : nullptr);
398 }
399
400#ifdef USE_BMESH_HOLES
401 BLI_listbase_clear(&f->loops);
402#else
403 f->l_first = nullptr;
404#endif
405 f->len = 0;
406 /* caller must initialize */
407 // zero_v3(f->no);
408 f->mat_nr = 0;
409 /* --- done --- */
410
411 /* may add to middle of the pool */
412 bm->elem_index_dirty |= BM_FACE;
413 bm->elem_table_dirty |= BM_FACE;
414 bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
415
416 bm->totface++;
417
418#ifdef USE_BMESH_HOLES
419 f->totbounds = 0;
420#endif
421
422 return f;
423}
424
426 BMVert *const *verts,
427 BMEdge *const *edges,
428 const int len,
429 const BMFace *f_example,
430 const eBMCreateFlag create_flag)
431{
432 BMFace *f = nullptr;
433 BMLoop *l, *startl, *lastl;
434 int i;
435
436 BLI_assert((f_example == nullptr) || (f_example->head.htype == BM_FACE));
437 BLI_assert(!(create_flag & 1));
438
439 if (len == 0) {
440 /* just return nullptr for now */
441 return nullptr;
442 }
443
444 if (create_flag & BM_CREATE_NO_DOUBLE) {
445 /* Check if face already exists */
447 if (f != nullptr) {
448 return f;
449 }
450 }
451
453
454 startl = lastl = bm_face_boundary_add(bm, f, verts[0], edges[0], create_flag);
455
456 for (i = 1; i < len; i++) {
457 l = bm_loop_create(bm, verts[i], edges[i], f, nullptr /* edges[i]->l */, create_flag);
458
460
461 l->prev = lastl;
462 lastl->next = l;
463 lastl = l;
464 }
465
466 startl->prev = lastl;
467 lastl->next = startl;
468
469 f->len = len;
470
471 if (!(create_flag & BM_CREATE_SKIP_CD)) {
472 if (f_example) {
473 BM_elem_attrs_copy(bm, f_example, f);
474 }
475 else {
477 zero_v3(f->no);
478 }
479 }
480 else {
481 if (f_example) {
482 copy_v3_v3(f->no, f_example->no);
483 }
484 else {
485 zero_v3(f->no);
486 }
487 }
488
490
491 return f;
492}
493
495 BMVert **vert_arr,
496 const int len,
497 const BMFace *f_example,
498 const eBMCreateFlag create_flag,
499 const bool create_edges)
500{
501 BMEdge **edge_arr = BLI_array_alloca(edge_arr, len);
502
503 if (create_edges) {
504 BM_edges_from_verts_ensure(bm, edge_arr, vert_arr, len);
505 }
506 else {
507 if (BM_edges_from_verts(edge_arr, vert_arr, len) == false) {
508 return nullptr;
509 }
510 }
511
512 return BM_face_create(bm, vert_arr, edge_arr, len, f_example, create_flag);
513}
514
515#ifndef NDEBUG
516
551
552int bmesh_elem_check(void *element, const char htype)
553{
554 BMHeader *head = static_cast<BMHeader *>(element);
556
557 if (!element) {
558 return IS_NULL;
559 }
560
561 if (head->htype != htype) {
562 return IS_WRONG_TYPE;
563 }
564
565 switch (htype) {
566 case BM_VERT: {
567 BMVert *v = static_cast<BMVert *>(element);
568 if (v->e && v->e->head.htype != BM_EDGE) {
570 }
571 break;
572 }
573 case BM_EDGE: {
574 BMEdge *e = static_cast<BMEdge *>(element);
575 if (e->v1_disk_link.prev == nullptr || e->v2_disk_link.prev == nullptr ||
576 e->v1_disk_link.next == nullptr || e->v2_disk_link.next == nullptr)
577 {
579 }
580
581 if (e->l && e->l->head.htype != BM_LOOP) {
583 }
584 if (e->l && e->l->f->head.htype != BM_FACE) {
586 }
587 if (e->l && (e->l->radial_next == nullptr || e->l->radial_prev == nullptr)) {
589 }
590 if (e->l && e->l->f->len <= 0) {
592 }
593 break;
594 }
595 case BM_LOOP: {
596 BMLoop *l = static_cast<BMLoop *>(element);
597 BMLoop *l2;
598 int i;
599
600 if (l->f->head.htype != BM_FACE) {
602 }
603 if (l->e->head.htype != BM_EDGE) {
605 }
606 if (l->v->head.htype != BM_VERT) {
608 }
609 if (!BM_vert_in_edge(l->e, l->v)) {
610 fprintf(stderr,
611 "%s: fatal bmesh error (vert not in edge)! (bmesh internal error)\n",
612 __func__);
614 }
615
616 if (l->radial_next == nullptr || l->radial_prev == nullptr) {
618 }
619 if (l->f->len <= 0) {
621 }
622
623 /* validate boundary loop -- invalid for hole loops, of course,
624 * but we won't be allowing those for a while yet */
625 l2 = l;
626 i = 0;
627 do {
628 if (i >= BM_NGON_MAX) {
629 break;
630 }
631
632 i++;
633 } while ((l2 = l2->next) != l);
634
635 if (i != l->f->len || l2 != l) {
637 }
638
641 }
642
643 break;
644 }
645 case BM_FACE: {
646 BMFace *f = static_cast<BMFace *>(element);
647 BMLoop *l_iter;
648 BMLoop *l_first;
649 int len = 0;
650
651# ifdef USE_BMESH_HOLES
652 if (!f->loops.first)
653# else
654 if (!f->l_first)
655# endif
656 {
657 err |= IS_FACE_NULL_LOOP;
658 }
659 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
660 do {
661 if (l_iter->f != f) {
662 fprintf(stderr,
663 "%s: loop inside one face points to another! (bmesh internal error)\n",
664 __func__);
666 }
667
668 if (!l_iter->e) {
669 err |= IS_FACE_NULL_EDGE;
670 }
671 if (!l_iter->v) {
672 err |= IS_FACE_NULL_VERT;
673 }
674 if (l_iter->e && l_iter->v) {
675 if (!BM_vert_in_edge(l_iter->e, l_iter->v) ||
676 !BM_vert_in_edge(l_iter->e, l_iter->next->v))
677 {
679 }
680
681 if (!bmesh_radial_validate(bmesh_radial_length(l_iter), l_iter)) {
683 }
684
685 if (bmesh_disk_count_at_most(l_iter->v, 2) < 2) {
687 }
688 }
689
690 /* check for duplicates */
693 }
695 if (l_iter->v) {
698 }
700 }
701 if (l_iter->e) {
704 }
706 }
707
708 len++;
709 } while ((l_iter = l_iter->next) != l_first);
710
711 /* cleanup duplicates flag */
712 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
713 do {
715 if (l_iter->v) {
717 }
718 if (l_iter->e) {
720 }
721 } while ((l_iter = l_iter->next) != l_first);
722
723 if (len != f->len) {
725 }
726 break;
727 }
728 default:
729 BLI_assert(0);
730 break;
731 }
732
733 BMESH_ASSERT(err == 0);
734
735 return err;
736}
737
738#endif /* !NDEBUG */
739
745{
746 bm->totvert--;
747 bm->elem_index_dirty |= BM_VERT;
748 bm->elem_table_dirty |= BM_VERT;
749 bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
750
752
753 if (v->head.data) {
754 CustomData_bmesh_free_block(&bm->vdata, &v->head.data);
755 }
756
757 if (bm->vtoolflagpool) {
758 BLI_mempool_free(bm->vtoolflagpool, ((BMVert_OFlag *)v)->oflags);
759 }
760 BLI_mempool_free(bm->vpool, v);
761}
762
768{
769 bm->totedge--;
770 bm->elem_index_dirty |= BM_EDGE;
771 bm->elem_table_dirty |= BM_EDGE;
772 bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
773
775
776 if (e->head.data) {
777 CustomData_bmesh_free_block(&bm->edata, &e->head.data);
778 }
779
780 if (bm->etoolflagpool) {
781 BLI_mempool_free(bm->etoolflagpool, ((BMEdge_OFlag *)e)->oflags);
782 }
783 BLI_mempool_free(bm->epool, e);
784}
785
791{
792 if (bm->act_face == f) {
793 bm->act_face = nullptr;
794 }
795
796 bm->totface--;
797 bm->elem_index_dirty |= BM_FACE;
798 bm->elem_table_dirty |= BM_FACE;
799 bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
800
802
803 if (f->head.data) {
805 }
806
807 if (bm->ftoolflagpool) {
808 BLI_mempool_free(bm->ftoolflagpool, ((BMFace_OFlag *)f)->oflags);
809 }
810 BLI_mempool_free(bm->fpool, f);
811}
812
818{
819 bm->totloop--;
820 bm->elem_index_dirty |= BM_LOOP;
821 bm->spacearr_dirty |= BM_SPACEARR_DIRTY_ALL;
822
823 if (l->head.data) {
824 CustomData_bmesh_free_block(&bm->ldata, &l->head.data);
825 }
826
827 BLI_mempool_free(bm->lpool, l);
828}
829
831{
832 BMEdge **edges = BLI_array_alloca(edges, f->len);
833 BMLoop *l_iter;
834 BMLoop *l_first;
835 int i = 0;
836
837 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
838 do {
839 edges[i++] = l_iter->e;
840 } while ((l_iter = l_iter->next) != l_first);
841
842 for (i = 0; i < f->len; i++) {
843 BM_edge_kill(bm, edges[i]);
844 }
845}
846
848{
850 BMLoop *l_iter;
851 BMLoop *l_first;
852 int i = 0;
853
854 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
855 do {
856 verts[i++] = l_iter->v;
857 } while ((l_iter = l_iter->next) != l_first);
858
859 for (i = 0; i < f->len; i++) {
861 }
862}
863
865{
866#ifdef USE_BMESH_HOLES
867 BMLoopList *ls, *ls_next;
868#endif
869
870#ifdef NDEBUG
871 /* check length since we may be removing degenerate faces */
872 if (f->len >= 3) {
874 }
875#endif
876
877#ifdef USE_BMESH_HOLES
878 for (ls = f->loops.first; ls; ls = ls_next)
879#else
880 if (f->l_first)
881#endif
882 {
883 BMLoop *l_iter, *l_next, *l_first;
884
885#ifdef USE_BMESH_HOLES
886 ls_next = ls->next;
887 l_iter = l_first = ls->first;
888#else
889 l_iter = l_first = f->l_first;
890#endif
891
892 do {
893 l_next = l_iter->next;
894
895 bmesh_radial_loop_remove(l_iter->e, l_iter);
896 bm_kill_only_loop(bm, l_iter);
897
898 } while ((l_iter = l_next) != l_first);
899
900#ifdef USE_BMESH_HOLES
901 BLI_mempool_free(bm->looplistpool, ls);
902#endif
903 }
904
906}
907
909{
910#ifdef USE_BMESH_HOLES
911 BMLoopList *ls, *ls_next;
912#endif
913
915
916#ifdef USE_BMESH_HOLES
917 for (ls = f->loops.first; ls; ls = ls_next)
918#else
919 if (f->l_first)
920#endif
921 {
922 BMLoop *l_iter, *l_next, *l_first;
923
924#ifdef USE_BMESH_HOLES
925 ls_next = ls->next;
926 l_iter = l_first = ls->first;
927#else
928 l_iter = l_first = f->l_first;
929#endif
930
931 do {
932 BMEdge *e;
933 l_next = l_iter->next;
934
935 e = l_iter->e;
937 bm_kill_only_loop(bm, l_iter);
938
939 if (e->l == nullptr) {
940 BMVert *v1 = e->v1, *v2 = e->v2;
941
945
946 if (v1->e == nullptr) {
948 }
949 if (v2->e == nullptr) {
951 }
952 }
953 } while ((l_iter = l_next) != l_first);
954
955#ifdef USE_BMESH_HOLES
956 BLI_mempool_free(bm->looplistpool, ls);
957#endif
958 }
959
961}
962
964{
965 while (e->l) {
966 BM_face_kill(bm, e->l->f);
967 }
968
971
973}
974
976{
977 while (v->e) {
978 BM_edge_kill(bm, v->e);
979 }
980
982}
983
984/********** private disk and radial cycle functions ********** */
985
990{
991 BMLoop *l_first = l;
992 int i = 0;
993
994 do {
995 i++;
996 } while ((l = l->next) != l_first);
997
998 return i;
999}
1000
1002 BMFace *f,
1003 const int cd_loop_mdisp_offset,
1004 const bool use_loop_mdisp_flip)
1005{
1006 BMLoop *l_first = f->l_first;
1007
1008 /* track previous cycles radial state */
1009 BMEdge *e_prev = l_first->prev->e;
1010 BMLoop *l_prev_radial_next = l_first->prev->radial_next;
1011 BMLoop *l_prev_radial_prev = l_first->prev->radial_prev;
1012 bool is_prev_boundary = l_prev_radial_next == l_prev_radial_next->radial_next;
1013
1014 BMLoop *l_iter = l_first;
1015 do {
1016 BMEdge *e_iter = l_iter->e;
1017 BMLoop *l_iter_radial_next = l_iter->radial_next;
1018 BMLoop *l_iter_radial_prev = l_iter->radial_prev;
1019 bool is_iter_boundary = l_iter_radial_next == l_iter_radial_next->radial_next;
1020
1021#if 0
1022 bmesh_radial_loop_remove(e_iter, l_iter);
1023 bmesh_radial_loop_append(e_prev, l_iter);
1024#else
1025 /* inline loop reversal */
1026 if (is_prev_boundary) {
1027 /* boundary */
1028 l_iter->radial_next = l_iter;
1029 l_iter->radial_prev = l_iter;
1030 }
1031 else {
1032 /* non-boundary, replace radial links */
1033 l_iter->radial_next = l_prev_radial_next;
1034 l_iter->radial_prev = l_prev_radial_prev;
1035 l_prev_radial_next->radial_prev = l_iter;
1036 l_prev_radial_prev->radial_next = l_iter;
1037 }
1038
1039 if (e_iter->l == l_iter) {
1040 e_iter->l = l_iter->next;
1041 }
1042 l_iter->e = e_prev;
1043#endif
1044
1045 std::swap(l_iter->next, l_iter->prev);
1046
1047 if (cd_loop_mdisp_offset != -1) {
1048 MDisps *md = static_cast<MDisps *>(BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_mdisp_offset));
1049 BKE_mesh_mdisp_flip(md, use_loop_mdisp_flip);
1050 }
1051
1052 e_prev = e_iter;
1053 l_prev_radial_next = l_iter_radial_next;
1054 l_prev_radial_prev = l_iter_radial_prev;
1055 is_prev_boundary = is_iter_boundary;
1056
1057 /* step to next (now swapped) */
1058 } while ((l_iter = l_iter->prev) != l_first);
1059
1060#ifndef NDEBUG
1061 /* validate radial */
1062 int i;
1063 for (i = 0, l_iter = l_first; i < f->len; i++, l_iter = l_iter->next) {
1064 BM_CHECK_ELEMENT(l_iter);
1065 BM_CHECK_ELEMENT(l_iter->e);
1066 BM_CHECK_ELEMENT(l_iter->v);
1067 BM_CHECK_ELEMENT(l_iter->f);
1068 }
1069
1071#endif
1072
1073 /* Loop indices are no more valid! */
1074 bm->elem_index_dirty |= BM_LOOP;
1075}
1076
1077static void bm_elements_systag_enable(void *veles, int tot, const char api_flag)
1078{
1079 BMHeader **eles = static_cast<BMHeader **>(veles);
1080 int i;
1081
1082 for (i = 0; i < tot; i++) {
1083 BM_ELEM_API_FLAG_ENABLE((BMElemF *)eles[i], api_flag);
1084 }
1085}
1086
1087static void bm_elements_systag_disable(void *veles, int tot, const char api_flag)
1088{
1089 BMHeader **eles = static_cast<BMHeader **>(veles);
1090 int i;
1091
1092 for (i = 0; i < tot; i++) {
1093 BM_ELEM_API_FLAG_DISABLE((BMElemF *)eles[i], api_flag);
1094 }
1095}
1096
1097static int bm_loop_systag_count_radial(BMLoop *l, const char api_flag)
1098{
1099 BMLoop *l_iter = l;
1100 int i = 0;
1101 do {
1102 i += BM_ELEM_API_FLAG_TEST(l_iter->f, api_flag) ? 1 : 0;
1103 } while ((l_iter = l_iter->radial_next) != l);
1104
1105 return i;
1106}
1107
1108static int UNUSED_FUNCTION(bm_vert_systag_count_disk)(BMVert *v, const char api_flag)
1109{
1110 BMEdge *e = v->e;
1111 int i = 0;
1112
1113 if (!e) {
1114 return 0;
1115 }
1116
1117 do {
1118 i += BM_ELEM_API_FLAG_TEST(e, api_flag) ? 1 : 0;
1119 } while ((e = bmesh_disk_edge_next(e, v)) != v->e);
1120
1121 return i;
1122}
1123
1128static bool bm_vert_is_manifold_flagged(BMVert *v, const char api_flag)
1129{
1130 BMEdge *e = v->e;
1131
1132 if (!e) {
1133 return false;
1134 }
1135
1136 do {
1137 BMLoop *l = e->l;
1138
1139 if (!l) {
1140 return false;
1141 }
1142
1143 if (BM_edge_is_boundary(l->e)) {
1144 return false;
1145 }
1146
1147 do {
1148 if (!BM_ELEM_API_FLAG_TEST(l->f, api_flag)) {
1149 return false;
1150 }
1151 } while ((l = l->radial_next) != e->l);
1152 } while ((e = bmesh_disk_edge_next(e, v)) != v->e);
1153
1154 return true;
1155}
1156
1157/* Mid-level Topology Manipulation Functions */
1158
1159BMFace *BM_faces_join(BMesh *bm, BMFace **faces, int totface, const bool do_del, BMFace **r_double)
1160{
1161 BMFace *f, *f_new;
1162#ifdef USE_BMESH_HOLES
1163 BMLoopList *lst;
1164 ListBase holes = {nullptr, nullptr};
1165#endif
1166 BMLoop *l_iter;
1167 BMLoop *l_first;
1168 BMVert *v1 = nullptr, *v2 = nullptr;
1169 int i;
1170 const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
1171 BMFace *f_existing;
1172 const bool had_active_face = (bm->act_face != nullptr);
1173
1174 /* Initialize the return value if provided. This ensures it will be nullptr if the join fails. */
1175 if (r_double) {
1176 *r_double = nullptr;
1177 }
1178
1179 if (UNLIKELY(!totface)) {
1180 BMESH_ASSERT(0);
1181 return nullptr;
1182 }
1183
1184 if (totface == 1) {
1185 return faces[0];
1186 }
1187
1189
1193
1194 for (i = 0; i < totface; i++) {
1195 f = faces[i];
1196 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
1197 do {
1198 int rlen = bm_loop_systag_count_radial(l_iter, _FLAG_JF);
1199
1200 if (rlen > 2) {
1201 /* Input faces do not form a contiguous manifold region.
1202 * Clean up flags and fail. */
1204 return nullptr;
1205 }
1206 if (rlen == 1) {
1207 edges.append(l_iter->e);
1208
1209 if (!v1) {
1210 v1 = l_iter->v;
1211 v2 = BM_edge_other_vert(l_iter->e, l_iter->v);
1212 }
1213 }
1214 else if (rlen == 2) {
1215 const bool d1 = bm_vert_is_manifold_flagged(l_iter->e->v1, _FLAG_JF);
1216 const bool d2 = bm_vert_is_manifold_flagged(l_iter->e->v2, _FLAG_JF);
1217
1218 if (!d1 && !d2 && !BM_ELEM_API_FLAG_TEST(l_iter->e, _FLAG_JF)) {
1219 /* don't remove an edge it makes up the side of another face
1220 * else this will remove the face as well - campbell */
1221 if (!BM_edge_face_count_is_over(l_iter->e, 2)) {
1222 if (do_del) {
1223 deledges.append(l_iter->e);
1224 }
1226 }
1227 }
1228 else {
1229 if (d1 && !BM_ELEM_API_FLAG_TEST(l_iter->e->v1, _FLAG_JF)) {
1230 if (do_del) {
1231 delverts.append(l_iter->e->v1);
1232 }
1234 }
1235
1236 if (d2 && !BM_ELEM_API_FLAG_TEST(l_iter->e->v2, _FLAG_JF)) {
1237 if (do_del) {
1238 delverts.append(l_iter->e->v2);
1239 }
1241 }
1242 }
1243 }
1244 } while ((l_iter = l_iter->next) != l_first);
1245
1246#ifdef USE_BMESH_HOLES
1247 for (lst = f->loops.first; lst; lst = lst->next) {
1248 if (lst == f->loops.first) {
1249 continue;
1250 }
1251
1252 BLI_remlink(&f->loops, lst);
1253 BLI_addtail(&holes, lst);
1254 }
1255#endif
1256 }
1257
1258 /* create region face */
1259 f_new = !edges.is_empty() ?
1261 bm, v1, v2, edges.data(), edges.size(), faces[0], BM_CREATE_NOP) :
1262 nullptr;
1263 if (UNLIKELY(f_new == nullptr)) {
1264 /* Invalid boundary region to join faces
1265 * Clean up flags and fail */
1267 return nullptr;
1268 }
1269
1270 /* If a new face was created, check whether it is a double of an existing face. */
1271 f_existing = BM_face_find_double(f_new);
1272 if (f_existing) {
1273
1274 /* Return the double to the calling function if that was requested. */
1275 if (r_double) {
1276 *r_double = f_existing;
1277 }
1278
1279 /* Otherwise, automatically reuse the existing face. */
1280 else {
1281 BM_face_kill(bm, f_new);
1282 f_new = f_existing;
1283 }
1284 }
1285
1286 bool reusing_face = (f_existing && r_double == nullptr);
1287
1288 /* If we are *not* reusing an existing face, we need to transfer data from the faces being joined
1289 * to the newly created joined face. */
1290 if (LIKELY(reusing_face == false)) {
1291
1292 /* copy over loop data */
1293 l_iter = l_first = BM_FACE_FIRST_LOOP(f_new);
1294 do {
1295 BMLoop *l2 = l_iter->radial_next;
1296
1297 do {
1298 if (BM_ELEM_API_FLAG_TEST(l2->f, _FLAG_JF)) {
1299 break;
1300 }
1301 l2 = l2->radial_next;
1302 } while (l2 != l_iter);
1303
1304 if (l2 != l_iter) {
1305 /* loops share an edge, shared vert depends on winding */
1306 if (l2->v != l_iter->v) {
1307 l2 = l2->next;
1308 }
1309 BLI_assert(l_iter->v == l2->v);
1310
1311 BM_elem_attrs_copy(bm, l2, l_iter);
1312 }
1313 } while ((l_iter = l_iter->next) != l_first);
1314
1315#ifdef USE_BMESH_HOLES
1316 /* add holes */
1317 BLI_movelisttolist(&f_new->loops, &holes);
1318
1319 /* update loop face pointer */
1320 for (lst = f_new->loops.first; lst; lst = lst->next) {
1321 l_iter = l_first = lst->first;
1322 do {
1323 l_iter->f = f_new;
1324 } while ((l_iter = l_iter->next) != l_first);
1325 }
1326#endif
1327
1328 /* handle multi-res data */
1329 if (cd_loop_mdisp_offset != -1) {
1330 float f_center[3];
1331 float(*faces_center)[3] = BLI_array_alloca(faces_center, totface);
1332
1333 BM_face_calc_center_median(f_new, f_center);
1334 for (i = 0; i < totface; i++) {
1335 BM_face_calc_center_median(faces[i], faces_center[i]);
1336 }
1337
1338 l_iter = l_first = BM_FACE_FIRST_LOOP(f_new);
1339 do {
1340 for (i = 0; i < totface; i++) {
1342 bm, l_iter, faces[i], f_center, faces_center[i], cd_loop_mdisp_offset);
1343 }
1344 } while ((l_iter = l_iter->next) != l_first);
1345 }
1346 }
1347
1348 /* Clean up the internal flags. */
1351
1352 if (do_del) {
1353 /* If `do_del`, delete all the edges and verts that were identified while walking the mesh. */
1354 for (BMEdge *edge : deledges) {
1355 BM_edge_kill(bm, edge);
1356 }
1357
1358 for (BMVert *vert : delverts) {
1359 BM_vert_kill(bm, vert);
1360 }
1361 }
1362 else {
1363 /* Otherwise, delete only the faces that were merged
1364 * (do not leave the mesh with both the old and new faces). */
1365 for (i = 0; i < totface; i++) {
1367 }
1368 }
1369
1370 /* If the mesh started with an active face, but no longer has one, then the active face was one
1371 * of the faces that was joined then deleted. Set the active face to preserve it. */
1372 if (had_active_face && bm->act_face == nullptr) {
1373 bm->act_face = f_new;
1374 }
1375
1376 BM_CHECK_ELEMENT(f_new);
1377 return f_new;
1378}
1379
1381{
1382 BMFace *f;
1383#ifdef USE_BMESH_HOLES
1384 BMLoopList *lst;
1385#endif
1386
1388
1389#ifdef USE_BMESH_HOLES
1390 lst = BLI_mempool_calloc(bm->looplistpool);
1391 BLI_addtail(&f->loops, lst);
1392#endif
1393
1394#ifdef USE_BMESH_HOLES
1395 f->totbounds = 1;
1396#endif
1397
1398 BM_elem_attrs_copy(bm, f_example, f);
1399
1400 return f;
1401}
1402
1404 BMFace *f,
1405 BMLoop *l_v1,
1406 BMLoop *l_v2,
1407 BMLoop **r_l,
1408#ifdef USE_BMESH_HOLES
1409 ListBase *holes,
1410#endif
1411 BMEdge *e_example,
1412 const bool no_double)
1413{
1414#ifdef USE_BMESH_HOLES
1415 BMLoopList *lst, *lst2;
1416#else
1417 int first_loop_f1;
1418#endif
1419
1420 BMFace *f2;
1421 BMLoop *l_iter, *l_first;
1422 BMLoop *l_f1 = nullptr, *l_f2 = nullptr;
1423 BMEdge *e;
1424 BMVert *v1 = l_v1->v, *v2 = l_v2->v;
1425 int f1len, f2len;
1426
1427 BLI_assert(f == l_v1->f && f == l_v2->f);
1428
1429 /* allocate new edge between v1 and v2 */
1430 e = BM_edge_create(bm, v1, v2, e_example, no_double ? BM_CREATE_NO_DOUBLE : BM_CREATE_NOP);
1431
1432 f2 = bm_face_create__sfme(bm, f);
1433 l_f1 = bm_loop_create(bm, v2, e, f, l_v2, eBMCreateFlag(0));
1434 l_f2 = bm_loop_create(bm, v1, e, f2, l_v1, eBMCreateFlag(0));
1435
1436 l_f1->prev = l_v2->prev;
1437 l_f2->prev = l_v1->prev;
1438 l_v2->prev->next = l_f1;
1439 l_v1->prev->next = l_f2;
1440
1441 l_f1->next = l_v1;
1442 l_f2->next = l_v2;
1443 l_v1->prev = l_f1;
1444 l_v2->prev = l_f2;
1445
1446#ifdef USE_BMESH_HOLES
1447 lst = f->loops.first;
1448 lst2 = f2->loops.first;
1449
1450 lst2->first = lst2->last = l_f2;
1451 lst->first = lst->last = l_f1;
1452#else
1453 /* find which of the faces the original first loop is in */
1454 l_iter = l_first = l_f1;
1455 first_loop_f1 = 0;
1456 do {
1457 if (l_iter == f->l_first) {
1458 first_loop_f1 = 1;
1459 }
1460 } while ((l_iter = l_iter->next) != l_first);
1461
1462 if (first_loop_f1) {
1463 /* Original first loop was in f1, find a suitable first loop for f2
1464 * which is as similar as possible to f1. the order matters for tools
1465 * such as dupli-faces. */
1466 if (f->l_first->prev == l_f1) {
1467 f2->l_first = l_f2->prev;
1468 }
1469 else if (f->l_first->next == l_f1) {
1470 f2->l_first = l_f2->next;
1471 }
1472 else {
1473 f2->l_first = l_f2;
1474 }
1475 }
1476 else {
1477 /* original first loop was in f2, further do same as above */
1478 f2->l_first = f->l_first;
1479
1480 if (f->l_first->prev == l_f2) {
1481 f->l_first = l_f1->prev;
1482 }
1483 else if (f->l_first->next == l_f2) {
1484 f->l_first = l_f1->next;
1485 }
1486 else {
1487 f->l_first = l_f1;
1488 }
1489 }
1490#endif
1491
1492 /* validate both loop */
1493 /* I don't know how many loops are supposed to be in each face at this point! FIXME */
1494
1495 /* go through all of f2's loops and make sure they point to it properly */
1496 l_iter = l_first = BM_FACE_FIRST_LOOP(f2);
1497 f2len = 0;
1498 do {
1499 l_iter->f = f2;
1500 f2len++;
1501 } while ((l_iter = l_iter->next) != l_first);
1502
1503 /* link up the new loops into the new edges radial */
1506
1507 f2->len = f2len;
1508
1509 f1len = 0;
1510 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
1511 do {
1512 f1len++;
1513 } while ((l_iter = l_iter->next) != l_first);
1514
1515 f->len = f1len;
1516
1517 if (r_l) {
1518 *r_l = l_f2;
1519 }
1520
1521#ifdef USE_BMESH_HOLES
1522 if (holes) {
1523 BLI_movelisttolist(&f2->loops, holes);
1524 }
1525 else {
1526 /* this code is not significant until holes actually work */
1527 // printf("WARNING: call to split face euler without holes argument; holes will be tossed.\n");
1528 for (lst = f->loops.last; lst != f->loops.first; lst = lst2) {
1529 lst2 = lst->prev;
1530 BLI_mempool_free(bm->looplistpool, lst);
1531 }
1532 }
1533#endif
1534
1537 BM_CHECK_ELEMENT(f2);
1538
1539 return f2;
1540}
1541
1543{
1544 BMLoop *l_next;
1545 BMEdge *e_new;
1546 BMVert *v_new, *v_old;
1547#ifndef NDEBUG
1548 int valence1, valence2;
1549 bool edok;
1550 int i;
1551#endif
1552
1553 BLI_assert(BM_vert_in_edge(e, tv) != false);
1554
1555 v_old = BM_edge_other_vert(e, tv);
1556
1557#ifndef NDEBUG
1558 valence1 = bmesh_disk_count(v_old);
1559 valence2 = bmesh_disk_count(tv);
1560#endif
1561
1562 /* order of 'e_new' verts should match 'e'
1563 * (so extruded faces don't flip) */
1564 v_new = BM_vert_create(bm, tv->co, tv, BM_CREATE_NOP);
1565 e_new = BM_edge_create(bm, tv, v_new, e, BM_CREATE_NOP);
1566
1567 bmesh_disk_edge_remove(e_new, tv);
1568 bmesh_disk_edge_remove(e_new, v_new);
1569
1570 bmesh_disk_vert_replace(e, v_new, tv);
1571
1572 /* add e_new to v_new's disk cycle */
1573 bmesh_disk_edge_append(e_new, v_new);
1574
1575 /* add e_new to tv's disk cycle */
1576 bmesh_disk_edge_append(e_new, tv);
1577
1578#ifndef NDEBUG
1579 /* verify disk cycles */
1580 edok = bmesh_disk_validate(valence1, v_old->e, v_old);
1581 BMESH_ASSERT(edok != false);
1582 edok = bmesh_disk_validate(valence2, tv->e, tv);
1583 BMESH_ASSERT(edok != false);
1584 edok = bmesh_disk_validate(2, v_new->e, v_new);
1585 BMESH_ASSERT(edok != false);
1586#endif
1587
1588 /* Split the radial cycle if present */
1589 l_next = e->l;
1590 e->l = nullptr;
1591 if (l_next) {
1592 BMLoop *l_new, *l;
1593#ifndef NDEBUG
1594 int radlen = bmesh_radial_length(l_next);
1595#endif
1596 bool is_first = true;
1597
1598 /* Take the next loop. Remove it from radial. Split it. Append to appropriate radials */
1599 while (l_next) {
1600 l = l_next;
1601 l->f->len++;
1602 l_next = l_next != l_next->radial_next ? l_next->radial_next : nullptr;
1604
1605 l_new = bm_loop_create(bm, nullptr, nullptr, l->f, l, eBMCreateFlag(0));
1606 l_new->prev = l;
1607 l_new->next = l->next;
1608 l_new->prev->next = l_new;
1609 l_new->next->prev = l_new;
1610 l_new->v = v_new;
1611
1612 /* assign the correct edge to the correct loop */
1613 if (BM_verts_in_edge(l_new->v, l_new->next->v, e)) {
1614 l_new->e = e;
1615 l->e = e_new;
1616
1617 /* append l into e_new's rad cycle */
1618 if (is_first) {
1619 is_first = false;
1620 l->radial_next = l->radial_prev = nullptr;
1621 }
1622
1623 bmesh_radial_loop_append(l_new->e, l_new);
1625 }
1626 else if (BM_verts_in_edge(l_new->v, l_new->next->v, e_new)) {
1627 l_new->e = e_new;
1628 l->e = e;
1629
1630 /* append l into e_new's rad cycle */
1631 if (is_first) {
1632 is_first = false;
1633 l->radial_next = l->radial_prev = nullptr;
1634 }
1635
1636 bmesh_radial_loop_append(l_new->e, l_new);
1638 }
1639 }
1640
1641#ifndef NDEBUG
1642 /* verify length of radial cycle */
1643 edok = bmesh_radial_validate(radlen, e->l);
1644 BMESH_ASSERT(edok != false);
1645 edok = bmesh_radial_validate(radlen, e_new->l);
1646 BMESH_ASSERT(edok != false);
1647
1648 /* verify loop->v and loop->next->v pointers for e */
1649 for (i = 0, l = e->l; i < radlen; i++, l = l->radial_next) {
1650 BMESH_ASSERT(l->e == e);
1651 // BMESH_ASSERT(l->radial_next == l);
1652 BMESH_ASSERT(!(l->prev->e != e_new && l->next->e != e_new));
1653
1654 edok = BM_verts_in_edge(l->v, l->next->v, e);
1655 BMESH_ASSERT(edok != false);
1656 BMESH_ASSERT(l->v != l->next->v);
1657 BMESH_ASSERT(l->e != l->next->e);
1658
1659 /* verify loop cycle for kloop->f */
1661 BM_CHECK_ELEMENT(l->v);
1662 BM_CHECK_ELEMENT(l->e);
1663 BM_CHECK_ELEMENT(l->f);
1664 }
1665 /* verify loop->v and loop->next->v pointers for e_new */
1666 for (i = 0, l = e_new->l; i < radlen; i++, l = l->radial_next) {
1667 BMESH_ASSERT(l->e == e_new);
1668 // BMESH_ASSERT(l->radial_next == l);
1669 BMESH_ASSERT(!(l->prev->e != e && l->next->e != e));
1670 edok = BM_verts_in_edge(l->v, l->next->v, e_new);
1671 BMESH_ASSERT(edok != false);
1672 BMESH_ASSERT(l->v != l->next->v);
1673 BMESH_ASSERT(l->e != l->next->e);
1674
1676 BM_CHECK_ELEMENT(l->v);
1677 BM_CHECK_ELEMENT(l->e);
1678 BM_CHECK_ELEMENT(l->f);
1679 }
1680#endif
1681 }
1682
1683 BM_CHECK_ELEMENT(e_new);
1684 BM_CHECK_ELEMENT(v_new);
1685 BM_CHECK_ELEMENT(v_old);
1687 BM_CHECK_ELEMENT(tv);
1688
1689 if (r_e) {
1690 *r_e = e_new;
1691 }
1692 return v_new;
1693}
1694
1696 BMEdge *e_kill,
1697 BMVert *v_kill,
1698 const bool do_del,
1699 const bool check_edge_exists,
1700 const bool kill_degenerate_faces,
1701 const bool kill_duplicate_faces)
1702{
1703 BMEdge *e_old;
1704 BMVert *v_old, *v_target;
1705 BMLoop *l_kill;
1706#ifndef NDEBUG
1707 int radlen, i;
1708 bool edok;
1709#endif
1710
1711 BLI_assert(BM_vert_in_edge(e_kill, v_kill));
1712
1713 if (BM_vert_in_edge(e_kill, v_kill) == 0) {
1714 return nullptr;
1715 }
1716
1717 if (bmesh_disk_count_at_most(v_kill, 3) == 2) {
1718#ifndef NDEBUG
1719 int valence1, valence2;
1720 BMLoop *l;
1721#endif
1722
1723 e_old = bmesh_disk_edge_next(e_kill, v_kill);
1724 v_target = BM_edge_other_vert(e_kill, v_kill);
1725 v_old = BM_edge_other_vert(e_old, v_kill);
1726
1727 /* check for double edges */
1728 if (BM_verts_in_edge(v_kill, v_target, e_old)) {
1729 return nullptr;
1730 }
1731
1732 BMEdge *e_splice;
1733 BLI_SMALLSTACK_DECLARE(faces_degenerate, BMFace *);
1734 BMLoop *l_kill_next;
1735
1736 /* Candidates for being duplicate. */
1737 BLI_SMALLSTACK_DECLARE(faces_duplicate_candidate, BMFace *);
1738
1739#ifndef NDEBUG
1740 /* For verification later, count valence of 'v_old' and 'v_target' */
1741 valence1 = bmesh_disk_count(v_old);
1742 valence2 = bmesh_disk_count(v_target);
1743#endif
1744
1745 if (check_edge_exists) {
1746 e_splice = BM_edge_exists(v_target, v_old);
1747 }
1748
1749 bmesh_disk_vert_replace(e_old, v_target, v_kill);
1750
1751 /* remove e_kill from 'v_target's disk cycle */
1752 bmesh_disk_edge_remove(e_kill, v_target);
1753
1754#ifndef NDEBUG
1755 /* deal with radial cycle of e_kill */
1756 radlen = bmesh_radial_length(e_kill->l);
1757#endif
1758 if (e_kill->l) {
1759
1760 /* fix the neighboring loops of all loops in e_kill's radial cycle */
1761 l_kill = e_kill->l;
1762 do {
1763 /* relink loops and fix vertex pointer */
1764 if (l_kill->next->v == v_kill) {
1765 l_kill->next->v = v_target;
1766 }
1767
1768 l_kill->next->prev = l_kill->prev;
1769 l_kill->prev->next = l_kill->next;
1770 if (BM_FACE_FIRST_LOOP(l_kill->f) == l_kill) {
1771 BM_FACE_FIRST_LOOP(l_kill->f) = l_kill->next;
1772 }
1773
1774 /* fix len attribute of face */
1775 l_kill->f->len--;
1776 if (kill_degenerate_faces && (l_kill->f->len < 3)) {
1777 BLI_SMALLSTACK_PUSH(faces_degenerate, l_kill->f);
1778 }
1779 else {
1780 /* The duplicate test isn't reliable at this point as `e_splice` might be set,
1781 * so the duplicate test needs to run once the edge has been spliced. */
1782 if (kill_duplicate_faces) {
1783 BLI_SMALLSTACK_PUSH(faces_duplicate_candidate, l_kill->f);
1784 }
1785 }
1786 l_kill_next = l_kill->radial_next;
1787
1788 bm_kill_only_loop(bm, l_kill);
1789
1790 } while ((l_kill = l_kill_next) != e_kill->l);
1791/* `e_kill->l` is invalid but the edge is freed next. */
1792#ifndef NDEBUG
1793 /* Validate radial cycle of e_old */
1794 edok = bmesh_radial_validate(radlen, e_old->l);
1795 BMESH_ASSERT(edok != false);
1796#endif
1797 }
1798 /* deallocate edge */
1799 bm_kill_only_edge(bm, e_kill);
1800
1801 /* deallocate vertex */
1802 if (do_del) {
1803 bm_kill_only_vert(bm, v_kill);
1804 }
1805 else {
1806 v_kill->e = nullptr;
1807 }
1808
1809#ifndef NDEBUG
1810 /* Validate disk cycle lengths of 'v_old', 'v_target' are unchanged */
1811 edok = bmesh_disk_validate(valence1, v_old->e, v_old);
1812 BMESH_ASSERT(edok != false);
1813 edok = bmesh_disk_validate(valence2, v_target->e, v_target);
1814 BMESH_ASSERT(edok != false);
1815
1816 /* Validate loop cycle of all faces attached to 'e_old' */
1817 for (i = 0, l = e_old->l; i < radlen; i++, l = l->radial_next) {
1818 BMESH_ASSERT(l->e == e_old);
1819 edok = BM_verts_in_edge(l->v, l->next->v, e_old);
1820 BMESH_ASSERT(edok != false);
1821 edok = bmesh_loop_validate(l->f);
1822 BMESH_ASSERT(edok != false);
1823
1825 BM_CHECK_ELEMENT(l->v);
1826 BM_CHECK_ELEMENT(l->e);
1827 BM_CHECK_ELEMENT(l->f);
1828 }
1829#endif
1830 if (check_edge_exists) {
1831 if (e_splice) {
1832 /* removes e_splice */
1833 BM_edge_splice(bm, e_old, e_splice);
1834 }
1835 }
1836
1837 if (kill_degenerate_faces) {
1838 BMFace *f_kill;
1839 while ((f_kill = static_cast<BMFace *>(BLI_SMALLSTACK_POP(faces_degenerate)))) {
1840 BM_face_kill(bm, f_kill);
1841 }
1842 }
1843
1844 if (kill_duplicate_faces) {
1845 BMFace *f_kill;
1846 while ((f_kill = static_cast<BMFace *>(BLI_SMALLSTACK_POP(faces_duplicate_candidate)))) {
1847 if (BM_face_find_double(f_kill)) {
1848 BM_face_kill(bm, f_kill);
1849 }
1850 }
1851 }
1852
1853 BM_CHECK_ELEMENT(v_old);
1854 BM_CHECK_ELEMENT(v_target);
1855 BM_CHECK_ELEMENT(e_old);
1856
1857 return e_old;
1858 }
1859 return nullptr;
1860}
1861
1863 BMEdge *e_kill,
1864 BMVert *v_kill,
1865 const bool do_del,
1866 const bool check_edge_exists,
1867 const bool kill_degenerate_faces)
1868{
1869 BLI_SMALLSTACK_DECLARE(faces_degenerate, BMFace *);
1870 BMVert *v_target = BM_edge_other_vert(e_kill, v_kill);
1871
1872 BLI_assert(BM_vert_in_edge(e_kill, v_kill));
1873
1874 if (e_kill->l) {
1875 BMLoop *l_kill, *l_first, *l_kill_next;
1876 l_kill = l_first = e_kill->l;
1877 do {
1878 /* relink loops and fix vertex pointer */
1879 if (l_kill->next->v == v_kill) {
1880 l_kill->next->v = v_target;
1881 }
1882
1883 l_kill->next->prev = l_kill->prev;
1884 l_kill->prev->next = l_kill->next;
1885 if (BM_FACE_FIRST_LOOP(l_kill->f) == l_kill) {
1886 BM_FACE_FIRST_LOOP(l_kill->f) = l_kill->next;
1887 }
1888
1889 /* fix len attribute of face */
1890 l_kill->f->len--;
1891 if (kill_degenerate_faces) {
1892 if (l_kill->f->len < 3) {
1893 BLI_SMALLSTACK_PUSH(faces_degenerate, l_kill->f);
1894 }
1895 }
1896 l_kill_next = l_kill->radial_next;
1897
1898 bm_kill_only_loop(bm, l_kill);
1899
1900 } while ((l_kill = l_kill_next) != l_first);
1901
1902 e_kill->l = nullptr;
1903 }
1904
1905 BM_edge_kill(bm, e_kill);
1906 BM_CHECK_ELEMENT(v_kill);
1907 BM_CHECK_ELEMENT(v_target);
1908
1909 if (v_target->e && v_kill->e) {
1910 /* Inline `BM_vert_splice(bm, v_target, v_kill)`. */
1911 BMEdge *e;
1912 while ((e = v_kill->e)) {
1913 BMEdge *e_target;
1914
1915 if (check_edge_exists) {
1916 e_target = BM_edge_exists(v_target, BM_edge_other_vert(e, v_kill));
1917 }
1918
1919 bmesh_edge_vert_swap(e, v_target, v_kill);
1920 BLI_assert(e->v1 != e->v2);
1921
1922 if (check_edge_exists) {
1923 if (e_target) {
1924 BM_edge_splice(bm, e_target, e);
1925 }
1926 }
1927 }
1928 }
1929
1930 if (kill_degenerate_faces) {
1931 BMFace *f_kill;
1932 while ((f_kill = static_cast<BMFace *>(BLI_SMALLSTACK_POP(faces_degenerate)))) {
1933 BM_face_kill(bm, f_kill);
1934 }
1935 }
1936
1937 if (do_del) {
1938 BLI_assert(v_kill->e == nullptr);
1939 bm_kill_only_vert(bm, v_kill);
1940 }
1941
1942 return v_target;
1943}
1944
1946{
1947 BMLoop *l_iter, *l_f1 = nullptr, *l_f2 = nullptr;
1948 int newlen = 0, i, f1len = 0, f2len = 0;
1949 bool edok;
1950 /* can't join a face to itself */
1951 if (f1 == f2) {
1952 return nullptr;
1953 }
1954
1955 /* validate that edge is 2-manifold edge */
1956 if (!BM_edge_is_manifold(e)) {
1957 return nullptr;
1958 }
1959
1960 /* verify that e is in both f1 and f2 */
1961 f1len = f1->len;
1962 f2len = f2->len;
1963
1964 if (!((l_f1 = BM_face_edge_share_loop(f1, e)) && (l_f2 = BM_face_edge_share_loop(f2, e)))) {
1965 return nullptr;
1966 }
1967
1968 /* validate direction of f2's loop cycle is compatible */
1969 if (l_f1->v == l_f2->v) {
1970 return nullptr;
1971 }
1972
1973 /* validate that for each face, each vertex has another edge in its disk cycle that is
1974 * not e, and not shared. */
1975 if (BM_edge_in_face(l_f1->next->e, f2) || BM_edge_in_face(l_f1->prev->e, f2) ||
1976 BM_edge_in_face(l_f2->next->e, f1) || BM_edge_in_face(l_f2->prev->e, f1))
1977 {
1978 return nullptr;
1979 }
1980
1981 /* validate only one shared edge */
1982 if (BM_face_share_edge_count(f1, f2) > 1) {
1983 return nullptr;
1984 }
1985
1986 /* validate no internal join */
1987 {
1988 bool is_dupe = false;
1989
1990 /* TODO: skip clearing once this is ensured. */
1991 for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f2); i < f2len; i++, l_iter = l_iter->next) {
1993 }
1994
1995 for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f1); i < f1len; i++, l_iter = l_iter->next) {
1996 BM_elem_flag_set(l_iter->v, BM_ELEM_INTERNAL_TAG, l_iter != l_f1);
1997 }
1998 for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f2); i < f2len; i++, l_iter = l_iter->next) {
1999 if (l_iter != l_f2) {
2000 /* as soon as a duplicate is found, bail out */
2001 if (BM_elem_flag_test(l_iter->v, BM_ELEM_INTERNAL_TAG)) {
2002 is_dupe = true;
2003 break;
2004 }
2005 }
2006 }
2007 /* Cleanup tags. */
2008 for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f1); i < f1len; i++, l_iter = l_iter->next) {
2010 }
2011 if (is_dupe) {
2012 return nullptr;
2013 }
2014 }
2015
2016 /* join the two loop */
2017 l_f1->prev->next = l_f2->next;
2018 l_f2->next->prev = l_f1->prev;
2019
2020 l_f1->next->prev = l_f2->prev;
2021 l_f2->prev->next = l_f1->next;
2022
2023 /* If `l_f1` was base-loop, make `l_f1->next` the base. */
2024 if (BM_FACE_FIRST_LOOP(f1) == l_f1) {
2025 BM_FACE_FIRST_LOOP(f1) = l_f1->next;
2026 }
2027
2028 /* increase length of f1 */
2029 f1->len += (f2->len - 2);
2030
2031 /* make sure each loop points to the proper face */
2032 newlen = f1->len;
2033 for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f1); i < newlen; i++, l_iter = l_iter->next) {
2034 l_iter->f = f1;
2035 }
2036
2037 /* remove edge from the disk cycle of its two vertices */
2038 bmesh_disk_edge_remove(l_f1->e, l_f1->e->v1);
2039 bmesh_disk_edge_remove(l_f1->e, l_f1->e->v2);
2040
2041 /* deallocate edge and its two loops as well as f2 */
2042 if (bm->etoolflagpool) {
2043 BLI_mempool_free(bm->etoolflagpool, ((BMEdge_OFlag *)l_f1->e)->oflags);
2044 }
2045 BLI_mempool_free(bm->epool, l_f1->e);
2046 bm->totedge--;
2047 BLI_mempool_free(bm->lpool, l_f1);
2048 bm->totloop--;
2049 BLI_mempool_free(bm->lpool, l_f2);
2050 bm->totloop--;
2051 if (bm->ftoolflagpool) {
2052 BLI_mempool_free(bm->ftoolflagpool, ((BMFace_OFlag *)f2)->oflags);
2053 }
2054 BLI_mempool_free(bm->fpool, f2);
2055 bm->totface--;
2056 /* account for both above */
2057 bm->elem_index_dirty |= BM_EDGE | BM_LOOP | BM_FACE;
2058
2059 BM_CHECK_ELEMENT(f1);
2060
2061 /* validate the new loop cycle */
2062 edok = bmesh_loop_validate(f1);
2063 BMESH_ASSERT(edok != false);
2064
2065 return f1;
2066}
2067
2069{
2070 bool is_double = false;
2071
2072 BLI_assert(BM_edge_exists(v_a, v_b) == nullptr);
2073
2074 if (v_a->e && v_b->e) {
2075 BMEdge *e, *e_first;
2076
2077#define VERT_VISIT _FLAG_WALK
2078
2079 /* tag 'v_a' */
2080 e = e_first = v_a->e;
2081 do {
2082 BMVert *v_other = BM_edge_other_vert(e, v_a);
2085 } while ((e = BM_DISK_EDGE_NEXT(e, v_a)) != e_first);
2086
2087 /* check 'v_b' connects to 'v_a' edges */
2088 e = e_first = v_b->e;
2089 do {
2090 BMVert *v_other = BM_edge_other_vert(e, v_b);
2091 if (BM_ELEM_API_FLAG_TEST(v_other, VERT_VISIT)) {
2092 is_double = true;
2093 break;
2094 }
2095 } while ((e = BM_DISK_EDGE_NEXT(e, v_b)) != e_first);
2096
2097 /* cleanup */
2098 e = e_first = v_a->e;
2099 do {
2100 BMVert *v_other = BM_edge_other_vert(e, v_a);
2103 } while ((e = BM_DISK_EDGE_NEXT(e, v_a)) != e_first);
2104
2105#undef VERT_VISIT
2106 }
2107
2108 return is_double;
2109}
2110
2111bool BM_vert_splice(BMesh *bm, BMVert *v_dst, BMVert *v_src)
2112{
2113 BMEdge *e;
2114
2115 /* verts already spliced */
2116 if (v_src == v_dst) {
2117 return false;
2118 }
2119
2120 BLI_assert(BM_vert_pair_share_face_check(v_src, v_dst) == false);
2121
2122 /* move all the edges from 'v_src' disk to 'v_dst' */
2123 while ((e = v_src->e)) {
2124 bmesh_edge_vert_swap(e, v_dst, v_src);
2125 BLI_assert(e->v1 != e->v2);
2126 }
2127
2128 BM_CHECK_ELEMENT(v_src);
2129 BM_CHECK_ELEMENT(v_dst);
2130
2131 /* 'v_src' is unused now, and can be killed */
2132 BM_vert_kill(bm, v_src);
2133
2134 return true;
2135}
2136
2137/* -------------------------------------------------------------------- */
2140
2141/* BM_edge_face_count(e) >= 1 */
2143{
2144 return (e->l && e->l->radial_next != e->l);
2145}
2146
2148 BMesh *bm, BMVert *v, BMVert ***r_vout, int *r_vout_len, const bool copy_select)
2149{
2150 int v_edges_num = 0;
2151
2152 /* Detailed notes on array use since this is stack memory, we have to be careful */
2153
2154 /* newly created vertices, only use when 'r_vout' is set
2155 * (total size will be number of fans) */
2156 BLI_SMALLSTACK_DECLARE(verts_new, BMVert *);
2157 /* fill with edges from the face-fan, clearing on completion
2158 * (total size will be max fan edge count) */
2160 /* temp store edges to walk over when filling 'edges',
2161 * (total size will be max radial edges of any edge) */
2162 BLI_SMALLSTACK_DECLARE(edges_search, BMEdge *);
2163
2164 /* number of resulting verts, include self */
2165 int verts_num = 1;
2166 /* track the total number of edges handled, so we know when we've found the last fan */
2167 int edges_found = 0;
2168
2169#define EDGE_VISIT _FLAG_WALK
2170
2171 /* count and flag at once */
2172 if (v->e) {
2173 BMEdge *e_first, *e_iter;
2174 e_iter = e_first = v->e;
2175 do {
2176 v_edges_num += 1;
2177
2180 } while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first);
2181
2182 while (true) {
2183 /* Considering only edges and faces incident on vertex v, walk
2184 * the edges & collect in the 'edges' list for splitting */
2185
2186 BMEdge *e = v->e;
2188
2189 do {
2191 BLI_SMALLSTACK_PUSH(edges, e);
2192 edges_found += 1;
2193
2194 if (e->l) {
2195 BMLoop *l_iter, *l_first;
2196 l_iter = l_first = e->l;
2197 do {
2198 BMLoop *l_adjacent = (l_iter->v == v) ? l_iter->prev : l_iter->next;
2199 BLI_assert(BM_vert_in_edge(l_adjacent->e, v));
2200 if (BM_ELEM_API_FLAG_TEST(l_adjacent->e, EDGE_VISIT)) {
2202 BLI_SMALLSTACK_PUSH(edges_search, l_adjacent->e);
2203 }
2204 } while ((l_iter = l_iter->radial_next) != l_first);
2205 }
2206 } while ((e = static_cast<BMEdge *>(BLI_SMALLSTACK_POP(edges_search))));
2207
2208 /* now we have all edges connected to 'v->e' */
2209
2210 BLI_assert(edges_found <= v_edges_num);
2211
2212 if (edges_found == v_edges_num) {
2213 /* We're done! The remaining edges in 'edges' form the last fan,
2214 * which can be left as is.
2215 * if 'edges' were allocated it'd be freed here. */
2216 break;
2217 }
2218
2219 BMVert *v_new;
2220
2221 v_new = BM_vert_create(bm, v->co, v, BM_CREATE_NOP);
2222 if (copy_select) {
2223 BM_elem_select_copy(bm, v_new, v);
2224 }
2225
2226 while ((e = static_cast<BMEdge *>(BLI_SMALLSTACK_POP(edges)))) {
2227 bmesh_edge_vert_swap(e, v_new, v);
2228 }
2229
2230 if (r_vout) {
2231 BLI_SMALLSTACK_PUSH(verts_new, v_new);
2232 }
2233 verts_num += 1;
2234 }
2235 }
2236
2237#undef EDGE_VISIT
2238
2239 /* flags are clean now, handle return values */
2240
2241 if (r_vout_len != nullptr) {
2242 *r_vout_len = verts_num;
2243 }
2244
2245 if (r_vout != nullptr) {
2246 BMVert **verts;
2247
2248 verts = MEM_malloc_arrayN<BMVert *>(verts_num, __func__);
2249 *r_vout = verts;
2250
2251 verts[0] = v;
2252 BLI_SMALLSTACK_AS_TABLE(verts_new, &verts[1]);
2253 }
2254}
2255
2276{
2277 do {
2278 LinkNode *n_orig = static_cast<LinkNode *>(edges_separate->link);
2279 do {
2280 LinkNode *n_prev = n_orig;
2281 LinkNode *n_step = n_orig->next;
2282 BMEdge *e_orig = static_cast<BMEdge *>(n_orig->link);
2283 do {
2284 BMEdge *e = static_cast<BMEdge *>(n_step->link);
2285 BLI_assert(e != e_orig);
2286 if ((e->v1 == e_orig->v1) && (e->v2 == e_orig->v2) && BM_edge_splice(bm, e_orig, e)) {
2287 /* don't visit again */
2288 n_prev->next = n_step->next;
2289 }
2290 else {
2291 n_prev = n_step;
2292 }
2293 } while ((n_step = n_step->next));
2294
2295 } while ((n_orig = n_orig->next) && n_orig->next);
2296 } while ((edges_separate = edges_separate->next));
2297}
2298
2300 BMVert *v,
2301 BMEdge **e_in,
2302 int e_in_len,
2303 const bool copy_select,
2304 BMVert ***r_vout,
2305 int *r_vout_len)
2306{
2307 LinkNode *edges_separate = nullptr;
2308 int i;
2309
2310 for (i = 0; i < e_in_len; i++) {
2311 BMEdge *e = e_in[i];
2313 LinkNode *edges_orig = nullptr;
2314 do {
2315 BMLoop *l_sep = e->l;
2316 bmesh_kernel_edge_separate(bm, e, l_sep, copy_select);
2317 BLI_linklist_prepend_alloca(&edges_orig, l_sep->e);
2318 BLI_assert(e != l_sep->e);
2319 } while (bm_edge_supports_separate(e));
2320 BLI_linklist_prepend_alloca(&edges_orig, e);
2321 BLI_linklist_prepend_alloca(&edges_separate, edges_orig);
2322 }
2323 }
2324
2325 bmesh_kernel_vert_separate(bm, v, r_vout, r_vout_len, copy_select);
2326
2327 if (edges_separate) {
2328 bmesh_kernel_vert_separate__cleanup(bm, edges_separate);
2329 }
2330}
2331
2333 BMVert *v,
2334 const char hflag,
2335 const bool copy_select,
2336 BMVert ***r_vout,
2337 int *r_vout_len)
2338{
2339 LinkNode *edges_separate = nullptr;
2340 BMEdge *e_iter, *e_first;
2341
2342 e_iter = e_first = v->e;
2343 do {
2344 if (BM_elem_flag_test(e_iter, hflag)) {
2345 BMEdge *e = e_iter;
2347 LinkNode *edges_orig = nullptr;
2348 do {
2349 BMLoop *l_sep = e->l;
2350 bmesh_kernel_edge_separate(bm, e, l_sep, copy_select);
2351 /* trick to avoid looping over separated edges */
2352 if (edges_separate == nullptr && edges_orig == nullptr) {
2353 e_first = l_sep->e;
2354 }
2355 BLI_linklist_prepend_alloca(&edges_orig, l_sep->e);
2356 BLI_assert(e != l_sep->e);
2357 } while (bm_edge_supports_separate(e));
2358 BLI_linklist_prepend_alloca(&edges_orig, e);
2359 BLI_linklist_prepend_alloca(&edges_separate, edges_orig);
2360 }
2361 }
2362 } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first);
2363
2364 bmesh_kernel_vert_separate(bm, v, r_vout, r_vout_len, copy_select);
2365
2366 if (edges_separate) {
2367 bmesh_kernel_vert_separate__cleanup(bm, edges_separate);
2368 }
2369}
2370
2372 BMesh * /*bm*/, BMVert *v_dst, BMVert *v_src, bool (*testfn)(BMEdge *, void *arg), void *arg)
2373{
2374 LinkNode *edges_hflag = nullptr;
2375 BMEdge *e_iter, *e_first;
2376
2377 e_iter = e_first = v_src->e;
2378 do {
2379 if (testfn(e_iter, arg)) {
2380 BLI_linklist_prepend_alloca(&edges_hflag, e_iter);
2381 }
2382 } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v_src)) != e_first);
2383
2384 if (edges_hflag) {
2385 do {
2386 e_iter = static_cast<BMEdge *>(edges_hflag->link);
2387 bmesh_disk_vert_replace(e_iter, v_dst, v_src);
2388 } while ((edges_hflag = edges_hflag->next));
2389 }
2390}
2391
2393
2394bool BM_edge_splice(BMesh *bm, BMEdge *e_dst, BMEdge *e_src)
2395{
2396 BMLoop *l;
2397
2398 if (!BM_vert_in_edge(e_src, e_dst->v1) || !BM_vert_in_edge(e_src, e_dst->v2)) {
2399 /* not the same vertices can't splice */
2400
2401 /* the caller should really make sure this doesn't happen ever
2402 * so assert on release builds */
2403 BLI_assert(0);
2404
2405 return false;
2406 }
2407
2408 while (e_src->l) {
2409 l = e_src->l;
2410 BLI_assert(BM_vert_in_edge(e_dst, l->v));
2411 BLI_assert(BM_vert_in_edge(e_dst, l->next->v));
2414 }
2415
2416 BLI_assert(bmesh_radial_length(e_src->l) == 0);
2417
2418 BM_CHECK_ELEMENT(e_src);
2419 BM_CHECK_ELEMENT(e_dst);
2420
2421 /* removes from disks too */
2422 BM_edge_kill(bm, e_src);
2423
2424 return true;
2425}
2426
2427void bmesh_kernel_edge_separate(BMesh *bm, BMEdge *e, BMLoop *l_sep, const bool copy_select)
2428{
2429 BMEdge *e_new;
2430#ifndef NDEBUG
2431 const int radlen = bmesh_radial_length(e->l);
2432#endif
2433
2434 BLI_assert(l_sep->e == e);
2435 BLI_assert(e->l);
2436
2437 if (BM_edge_is_boundary(e)) {
2438 BLI_assert(0); /* no cut required */
2439 return;
2440 }
2441
2442 if (l_sep == e->l) {
2443 e->l = l_sep->radial_next;
2444 }
2445
2446 e_new = BM_edge_create(bm, e->v1, e->v2, e, BM_CREATE_NOP);
2448 bmesh_radial_loop_append(e_new, l_sep);
2449 l_sep->e = e_new;
2450
2451 if (copy_select) {
2452 BM_elem_select_copy(bm, e_new, e);
2453 }
2454
2455 BLI_assert(bmesh_radial_length(e->l) == radlen - 1);
2456 BLI_assert(bmesh_radial_length(e_new->l) == 1);
2457
2458 BM_CHECK_ELEMENT(e_new);
2460}
2461
2463{
2464 BMVert *v_new = nullptr;
2465 BMVert *v_sep = l_sep->v;
2466 BMEdge *e_iter;
2467 BMEdge *edges[2];
2468 int i;
2469
2470 /* peel the face from the edge radials on both sides of the
2471 * loop vert, disconnecting the face from its fan */
2472 if (!BM_edge_is_boundary(l_sep->e)) {
2473 bmesh_kernel_edge_separate(bm, l_sep->e, l_sep, false);
2474 }
2475 if (!BM_edge_is_boundary(l_sep->prev->e)) {
2476 bmesh_kernel_edge_separate(bm, l_sep->prev->e, l_sep->prev, false);
2477 }
2478
2479/* do inline, below */
2480#if 0
2481 if (BM_vert_edge_count_is_equal(v_sep, 2)) {
2482 return v_sep;
2483 }
2484#endif
2485
2486 /* Search for an edge unattached to this loop */
2487 e_iter = v_sep->e;
2488 while (!ELEM(e_iter, l_sep->e, l_sep->prev->e)) {
2489 e_iter = bmesh_disk_edge_next(e_iter, v_sep);
2490
2491 /* We've come back around to the initial edge, all touch this loop.
2492 * If there are still only two edges out of v_sep,
2493 * then this whole URMV was just a no-op, so exit now. */
2494 if (e_iter == v_sep->e) {
2496 return v_sep;
2497 }
2498 }
2499
2500 v_sep->e = l_sep->e;
2501
2502 v_new = BM_vert_create(bm, v_sep->co, v_sep, BM_CREATE_NOP);
2503
2504 edges[0] = l_sep->e;
2505 edges[1] = l_sep->prev->e;
2506
2507 for (i = 0; i < ARRAY_SIZE(edges); i++) {
2508 BMEdge *e = edges[i];
2509 bmesh_edge_vert_swap(e, v_new, v_sep);
2510 }
2511
2512 BLI_assert(v_sep != l_sep->v);
2513 BLI_assert(v_sep->e != l_sep->v->e);
2514
2515 BM_CHECK_ELEMENT(l_sep);
2516 BM_CHECK_ELEMENT(v_sep);
2517 BM_CHECK_ELEMENT(edges[0]);
2518 BM_CHECK_ELEMENT(edges[1]);
2519 BM_CHECK_ELEMENT(v_new);
2520
2521 return v_new;
2522}
2523
2525{
2526 BMVert *v_sep = larr[0]->v;
2527 BMVert *v_new;
2528 int edges_len = 0;
2529 int i;
2530 /* any edges not owned by 'larr' loops connected to 'v_sep'? */
2531 bool is_mixed_edge_any = false;
2532 /* any loops not owned by 'larr' radially connected to 'larr' loop edges? */
2533 bool is_mixed_loop_any = false;
2534
2535#define LOOP_VISIT _FLAG_WALK
2536#define EDGE_VISIT _FLAG_WALK
2537
2538 for (i = 0; i < larr_len; i++) {
2539 BMLoop *l_sep = larr[i];
2540
2541 /* all must be from the same vert! */
2542 BLI_assert(v_sep == l_sep->v);
2543
2546
2547 /* weak! but it makes it simpler to check for edges to split
2548 * while doing a radial loop (where loops may be adjacent) */
2551
2552 BMLoop *loop_pair[2] = {l_sep, l_sep->prev};
2553 for (int j = 0; j < ARRAY_SIZE(loop_pair); j++) {
2554 BMEdge *e = loop_pair[j]->e;
2557 edges_len += 1;
2558 }
2559 }
2560 }
2561
2562 BMEdge **edges = BLI_array_alloca(edges, edges_len);
2563 STACK_DECLARE(edges);
2564
2565 STACK_INIT(edges, edges_len);
2566
2567 {
2568 BMEdge *e_first, *e_iter;
2569 e_iter = e_first = v_sep->e;
2570 do {
2571 if (BM_ELEM_API_FLAG_TEST(e_iter, EDGE_VISIT)) {
2572 BMLoop *l_iter, *l_first;
2573 bool is_mixed_loop = false;
2574
2575 l_iter = l_first = e_iter->l;
2576 do {
2577 if (!BM_ELEM_API_FLAG_TEST(l_iter, LOOP_VISIT)) {
2578 is_mixed_loop = true;
2579 break;
2580 }
2581 } while ((l_iter = l_iter->radial_next) != l_first);
2582
2583 if (is_mixed_loop) {
2584 /* ensure the first loop is one we don't own so we can do a quick check below
2585 * on the edge's loop-flag to see if the edge is mixed or not. */
2586 e_iter->l = l_iter;
2587
2588 is_mixed_loop_any = true;
2589 }
2590
2591 STACK_PUSH(edges, e_iter);
2592 }
2593 else {
2594 /* at least one edge attached isn't connected to our loops */
2595 is_mixed_edge_any = true;
2596 }
2597 } while ((e_iter = bmesh_disk_edge_next(e_iter, v_sep)) != e_first);
2598 }
2599
2600 BLI_assert(edges_len == STACK_SIZE(edges));
2601
2602 if (is_mixed_loop_any == false && is_mixed_edge_any == false) {
2603 /* all loops in 'larr' are the sole owners of their edges.
2604 * nothing to split away from, this is a no-op */
2605 v_new = v_sep;
2606 }
2607 else {
2608 v_new = BM_vert_create(bm, v_sep->co, v_sep, BM_CREATE_NOP);
2609
2610 for (i = 0; i < STACK_SIZE(edges); i++) {
2611 BMEdge *e = edges[i];
2612 BMLoop *l_iter, *l_first, *l_next;
2613 BMEdge *e_new;
2614
2615 /* disable so copied edge isn't left dirty (loop edges are cleared last too) */
2617
2618 /* will always be false when (is_mixed_loop_any == false) */
2620 /* edge has some loops owned by us, some owned by other loops */
2621 BMVert *e_new_v_pair[2];
2622
2623 if (e->v1 == v_sep) {
2624 e_new_v_pair[0] = v_new;
2625 e_new_v_pair[1] = e->v2;
2626 }
2627 else {
2628 BLI_assert(v_sep == e->v2);
2629 e_new_v_pair[0] = e->v1;
2630 e_new_v_pair[1] = v_new;
2631 }
2632
2633 e_new = BM_edge_create(bm, UNPACK2(e_new_v_pair), e, BM_CREATE_NOP);
2634
2635 /* now moved all loops from 'larr' to this newly created edge */
2636 l_iter = l_first = e->l;
2637 do {
2638 l_next = l_iter->radial_next;
2639 if (BM_ELEM_API_FLAG_TEST(l_iter, LOOP_VISIT)) {
2640 bmesh_radial_loop_remove(e, l_iter);
2641 bmesh_radial_loop_append(e_new, l_iter);
2642 l_iter->e = e_new;
2643 }
2644 } while ((l_iter = l_next) != l_first);
2645 }
2646 else {
2647 /* we own the edge entirely, replace the vert */
2648 bmesh_disk_vert_replace(e, v_new, v_sep);
2649 }
2650
2651 /* loop vert is handled last! */
2652 }
2653 }
2654
2655 for (i = 0; i < larr_len; i++) {
2656 BMLoop *l_sep = larr[i];
2657
2658 l_sep->v = v_new;
2659
2666
2669 }
2670
2671#undef LOOP_VISIT
2672#undef EDGE_VISIT
2673
2674 return v_new;
2675}
2676
2678{
2679 BMLoop *l_iter, *l_first;
2680
2681 BLI_assert(ELEM(v_src, e->v1, e->v2));
2682 bmesh_disk_vert_replace(e, v_dst, v_src);
2683
2684 l_iter = l_first = e->l;
2685 do {
2686 if (l_iter->v == v_src) {
2687 l_iter->v = v_dst;
2688 if (BM_vert_in_edge(l_iter->prev->e, v_src)) {
2689 bmesh_edge_vert_swap__recursive(l_iter->prev->e, v_dst, v_src);
2690 }
2691 }
2692 else if (l_iter->next->v == v_src) {
2693 l_iter->next->v = v_dst;
2694 if (BM_vert_in_edge(l_iter->next->e, v_src)) {
2695 bmesh_edge_vert_swap__recursive(l_iter->next->e, v_dst, v_src);
2696 }
2697 }
2698 else {
2699 BLI_assert(l_iter->prev->v != v_src);
2700 }
2701 } while ((l_iter = l_iter->radial_next) != l_first);
2702}
2703
2705{
2706 BMVert *v_new = BM_vert_create(bm, l_sep->v->co, l_sep->v, BM_CREATE_NOP);
2707 /* passing either 'l_sep->e', 'l_sep->prev->e' will work */
2708 bmesh_edge_vert_swap__recursive(l_sep->e, v_new, l_sep->v);
2709 BLI_assert(l_sep->v == v_new);
2710 return v_new;
2711}
2712
2714{
2715 BMLoop *l_iter, *l_first;
2716
2717 BLI_assert(f_a != f_b);
2718
2719 l_iter = l_first = BM_FACE_FIRST_LOOP(f_a);
2720 do {
2721 l_iter->f = f_b;
2722 } while ((l_iter = l_iter->next) != l_first);
2723
2724 l_iter = l_first = BM_FACE_FIRST_LOOP(f_b);
2725 do {
2726 l_iter->f = f_a;
2727 } while ((l_iter = l_iter->next) != l_first);
2728
2729 std::swap((*f_a), (*f_b));
2730
2731 /* swap back */
2732 std::swap(f_a->head.data, f_b->head.data);
2733 std::swap(f_a->head.index, f_b->head.index);
2734}
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.
#define BM_DISK_EDGE_NEXT(e, v)
#define BM_NGON_MAX
#define BM_FACE_FIRST_LOOP(p)
@ BM_SPACEARR_DIRTY_ALL
@ BM_LOOP
@ BM_ELEM_SMOOTH
@ BM_ELEM_INTERNAL_TAG
@ BM_ELEM_DRAW
#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)
@ _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()
int64_t size() const
void append(const T &value)
bool is_empty() const
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