Blender V4.3
bmesh_marking.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
16#include <cstddef>
17
18#include "MEM_guardedalloc.h"
19
20#include "DNA_scene_types.h"
21
22#include "BLI_listbase.h"
23#include "BLI_math_vector.h"
24#include "BLI_task.h"
25
26#include "bmesh.hh"
27#include "bmesh_structure.hh"
28
29/* For '_FLAG_OVERLAP'. */
30#include "bmesh_private.hh"
31
32/* -------------------------------------------------------------------- */
39
40static void recount_totsels_range_vert_func(void * /*userdata*/,
41 MempoolIterData *iter,
42 const TaskParallelTLS *__restrict tls)
43{
44 SelectionCountChunkData *count = static_cast<SelectionCountChunkData *>(tls->userdata_chunk);
45 const BMVert *eve = (const BMVert *)iter;
47 count->selection_len += 1;
48 }
49}
50
51static void recount_totsels_range_edge_func(void * /*userdata*/,
52 MempoolIterData *iter,
53 const TaskParallelTLS *__restrict tls)
54{
55 SelectionCountChunkData *count = static_cast<SelectionCountChunkData *>(tls->userdata_chunk);
56 const BMEdge *eed = (const BMEdge *)iter;
58 count->selection_len += 1;
59 }
60}
61
62static void recount_totsels_range_face_func(void * /*userdata*/,
63 MempoolIterData *iter,
64 const TaskParallelTLS *__restrict tls)
65{
66 SelectionCountChunkData *count = static_cast<SelectionCountChunkData *>(tls->userdata_chunk);
67 const BMFace *efa = (const BMFace *)iter;
69 count->selection_len += 1;
70 }
71}
72
73static void recount_totsels_reduce(const void *__restrict /*userdata*/,
74 void *__restrict chunk_join,
75 void *__restrict chunk)
76{
77 SelectionCountChunkData *dst = static_cast<SelectionCountChunkData *>(chunk_join);
78 const SelectionCountChunkData *src = static_cast<const SelectionCountChunkData *>(chunk);
79 dst->selection_len += src->selection_len;
80}
81
83{
85
86 TaskParallelMempoolFunc range_func = nullptr;
87 if (iter_type == BM_VERTS_OF_MESH) {
89 }
90 else if (iter_type == BM_EDGES_OF_MESH) {
92 }
93 else if (iter_type == BM_FACES_OF_MESH) {
95 }
96 return range_func;
97}
98
99static int recount_totsel(BMesh *bm, BMIterType iter_type)
100{
101 const int MIN_ITER_SIZE = 1024;
102
103 TaskParallelSettings settings;
105 settings.func_reduce = recount_totsels_reduce;
106 settings.min_iter_per_thread = MIN_ITER_SIZE;
107
109 settings.userdata_chunk = &count;
110 settings.userdata_chunk_size = sizeof(count);
111
113 BM_iter_parallel(bm, iter_type, range_func, nullptr, &settings);
114 return count.selection_len;
115}
116
121
126
131
138
139#ifndef NDEBUG
146#endif
147
150/* -------------------------------------------------------------------- */
154static bool bm_vert_is_edge_select_any_other(const BMVert *v, const BMEdge *e_first)
155{
156 const BMEdge *e_iter = e_first;
157
158 /* start by stepping over the current edge */
159 while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first) {
160 if (BM_elem_flag_test(e_iter, BM_ELEM_SELECT)) {
161 return true;
162 }
163 }
164 return false;
165}
166
167#if 0
168static bool bm_vert_is_edge_select_any(const BMVert *v)
169{
170 if (v->e) {
171 const BMEdge *e_iter, *e_first;
172 e_iter = e_first = v->e;
173 do {
174 if (BM_elem_flag_test(e_iter, BM_ELEM_SELECT)) {
175 return true;
176 }
177 } while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first);
178 }
179 return false;
180}
181#endif
182
184{
185 if (v->e) {
186 const BMEdge *e_iter, *e_first;
187 e_iter = e_first = v->e;
188 do {
189 if (!BM_elem_flag_test(e_iter, BM_ELEM_HIDDEN)) {
190 return true;
191 }
192 } while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first);
193 }
194 return false;
195}
196
198{
199 const BMLoop *l_iter = l_first;
200
201 /* start by stepping over the current face */
202 while ((l_iter = l_iter->radial_next) != l_first) {
203 if (BM_elem_flag_test(l_iter->f, BM_ELEM_SELECT)) {
204 return true;
205 }
206 }
207 return false;
208}
209
210#if 0
211static bool bm_edge_is_face_select_any(const BMEdge *e)
212{
213 if (e->l) {
214 const BMLoop *l_iter, *l_first;
215 l_iter = l_first = e->l;
216 do {
217 if (BM_elem_flag_test(l_iter->f, BM_ELEM_SELECT)) {
218 return true;
219 }
220 } while ((l_iter = l_iter->radial_next) != l_first);
221 }
222 return false;
223}
224#endif
225
227{
228 if (e->l) {
229 const BMLoop *l_iter, *l_first;
230 l_iter = l_first = e->l;
231 do {
232 if (!BM_elem_flag_test(l_iter->f, BM_ELEM_HIDDEN)) {
233 return true;
234 }
235 } while ((l_iter = l_iter->radial_next) != l_first);
236 }
237 return false;
238}
239
242void BM_mesh_select_mode_clean_ex(BMesh *bm, const short selectmode)
243{
244 if (selectmode & SCE_SELECT_VERTEX) {
245 /* pass */
246 }
247 else if (selectmode & SCE_SELECT_EDGE) {
248 BMIter iter;
249
250 if (bm->totvertsel) {
251 BMVert *v;
252 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
254 }
255 bm->totvertsel = 0;
256 }
257
258 if (bm->totedgesel) {
259 BMEdge *e;
260 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
262 BM_vert_select_set(bm, e->v1, true);
263 BM_vert_select_set(bm, e->v2, true);
264 }
265 }
266 }
267 }
268 else if (selectmode & SCE_SELECT_FACE) {
269 BMIter iter;
270
271 if (bm->totvertsel) {
272 BMVert *v;
273 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
275 }
276 bm->totvertsel = 0;
277 }
278
279 if (bm->totedgesel) {
280 BMEdge *e;
281 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
283 }
284 bm->totedgesel = 0;
285 }
286
287 if (bm->totfacesel) {
288 BMFace *f;
289 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
291 BMLoop *l_iter, *l_first;
292 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
293 do {
294 BM_edge_select_set(bm, l_iter->e, true);
295 } while ((l_iter = l_iter->next) != l_first);
296 }
297 }
298 }
299 }
300}
301
306
307/* -------------------------------------------------------------------- */
314
316 MempoolIterData *iter,
317 const TaskParallelTLS *__restrict tls)
318{
319 SelectionFlushChunkData *chunk_data = static_cast<SelectionFlushChunkData *>(
320 tls->userdata_chunk);
321 BMEdge *e = (BMEdge *)iter;
322 const bool is_selected = BM_elem_flag_test(e, BM_ELEM_SELECT);
323 const bool is_hidden = BM_elem_flag_test(e, BM_ELEM_HIDDEN);
324 if (!is_hidden &&
326 {
328 chunk_data->delta_selection_len += is_selected ? 0 : 1;
329 }
330 else {
332 chunk_data->delta_selection_len += is_selected ? -1 : 0;
333 }
334}
335
337 MempoolIterData *iter,
338 const TaskParallelTLS *__restrict tls)
339{
340 SelectionFlushChunkData *chunk_data = static_cast<SelectionFlushChunkData *>(
341 tls->userdata_chunk);
342 BMFace *f = (BMFace *)iter;
343 BMLoop *l_iter;
344 BMLoop *l_first;
345 const bool is_selected = BM_elem_flag_test(f, BM_ELEM_SELECT);
346 bool ok = true;
348 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
349 do {
350 if (!BM_elem_flag_test(l_iter->e, BM_ELEM_SELECT)) {
351 ok = false;
352 break;
353 }
354 } while ((l_iter = l_iter->next) != l_first);
355 }
356 else {
357 ok = false;
358 }
359
361 if (is_selected && !ok) {
362 chunk_data->delta_selection_len -= 1;
363 }
364 else if (ok && !is_selected) {
365 chunk_data->delta_selection_len += 1;
366 }
367}
368
369static void bm_mesh_select_mode_flush_reduce_fn(const void *__restrict /*userdata*/,
370 void *__restrict chunk_join,
371 void *__restrict chunk)
372{
373 SelectionFlushChunkData *dst = static_cast<SelectionFlushChunkData *>(chunk_join);
374 const SelectionFlushChunkData *src = static_cast<const SelectionFlushChunkData *>(chunk);
376}
377
379{
380 SelectionFlushChunkData chunk_data = {0};
381
382 TaskParallelSettings settings;
384 settings.use_threading = bm->totedge >= BM_THREAD_LIMIT;
385 settings.userdata_chunk = &chunk_data;
386 settings.userdata_chunk_size = sizeof(chunk_data);
387 settings.func_reduce = bm_mesh_select_mode_flush_reduce_fn;
388
389 BM_iter_parallel(
391 bm->totedgesel += chunk_data.delta_selection_len;
392}
393
395{
396 SelectionFlushChunkData chunk_data = {0};
397
398 TaskParallelSettings settings;
400 settings.use_threading = bm->totface >= BM_THREAD_LIMIT;
401 settings.userdata_chunk = &chunk_data;
402 settings.userdata_chunk_size = sizeof(chunk_data);
403 settings.func_reduce = bm_mesh_select_mode_flush_reduce_fn;
404
405 BM_iter_parallel(
407 bm->totfacesel += chunk_data.delta_selection_len;
408}
409
410void BM_mesh_select_mode_flush_ex(BMesh *bm, const short selectmode, eBMSelectionFlushFLags flags)
411{
412 if (selectmode & SCE_SELECT_VERTEX) {
414 }
415
416 if (selectmode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) {
418 }
419
420 /* Remove any deselected elements from the BMEditSelection */
422
425 }
428 }
431 }
433}
434
439
443{
444 BMIter eiter;
445 BMEdge *e;
446
447 BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
451 {
453 }
454 }
455
456 if (e->l && !BM_elem_flag_test(e, BM_ELEM_SELECT)) {
457 BMLoop *l_iter;
458 BMLoop *l_first;
459
460 l_iter = l_first = e->l;
461 do {
463 } while ((l_iter = l_iter->radial_next) != l_first);
464 }
465 }
466 }
467
468 /* Remove any deselected elements from the BMEditSelection */
470
472}
473
475{
476 BMEdge *e;
477 BMLoop *l_iter;
478 BMLoop *l_first;
479 BMFace *f;
480
481 BMIter eiter;
482 BMIter fiter;
483
484 bool ok;
485
486 BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
489 {
491 }
492 }
493 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
494 ok = true;
496 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
497 do {
498 if (!BM_elem_flag_test(l_iter->v, BM_ELEM_SELECT)) {
499 ok = false;
500 break;
501 }
502 } while ((l_iter = l_iter->next) != l_first);
503 }
504 else {
505 ok = false;
506 }
507
508 if (ok) {
510 }
511 }
512
514}
515
517{
519
521 return;
522 }
523
524 if (select) {
527 bm->totvertsel += 1;
528 }
529 }
530 else {
532 bm->totvertsel -= 1;
534 }
535 }
536}
537
539{
541
543 return;
544 }
545
546 if (select) {
549 bm->totedgesel += 1;
550 }
551 BM_vert_select_set(bm, e->v1, true);
552 BM_vert_select_set(bm, e->v2, true);
553 }
554 else {
557 bm->totedgesel -= 1;
558 }
559
560 if ((bm->selectmode & SCE_SELECT_VERTEX) == 0) {
561 int i;
562
563 /* check if the vert is used by a selected edge */
564 for (i = 0; i < 2; i++) {
565 BMVert *v = *((&e->v1) + i);
566 if (bm_vert_is_edge_select_any_other(v, e) == false) {
567 BM_vert_select_set(bm, v, false);
568 }
569 }
570 }
571 else {
572 BM_vert_select_set(bm, e->v1, false);
573 BM_vert_select_set(bm, e->v2, false);
574 }
575 }
576}
577
579{
580 BMLoop *l_iter;
581 BMLoop *l_first;
582
584
586 return;
587 }
588
589 if (select) {
592 bm->totfacesel += 1;
593 }
594
595 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
596 do {
597 BM_vert_select_set(bm, l_iter->v, true);
598 BM_edge_select_set(bm, l_iter->e, true);
599 } while ((l_iter = l_iter->next) != l_first);
600 }
601 else {
602
605 bm->totfacesel -= 1;
606 }
616 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
617 do {
618 BM_vert_select_set(bm, l_iter->v, false);
619 BM_edge_select_set_noflush(bm, l_iter->e, false);
620 } while ((l_iter = l_iter->next) != l_first);
621 }
622 else {
628 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
629 do {
630 BM_edge_select_set_noflush(bm, l_iter->e, false);
631 } while ((l_iter = l_iter->next) != l_first);
632 }
633 else {
634 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
635 do {
636 if (bm_edge_is_face_select_any_other(l_iter) == false) {
637 BM_edge_select_set_noflush(bm, l_iter->e, false);
638 }
639 } while ((l_iter = l_iter->next) != l_first);
640 }
641
642 /* flush down to verts */
643 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
644 do {
645 if (bm_vert_is_edge_select_any_other(l_iter->v, l_iter->e) == false) {
646 BM_vert_select_set(bm, l_iter->v, false);
647 }
648 } while ((l_iter = l_iter->next) != l_first);
649 }
650 }
651}
652
653/* -------------------------------------------------------------------- */
658{
660
662 return;
663 }
664
665 if (select) {
668 bm->totedgesel += 1;
669 }
670 }
671 else {
674 bm->totedgesel -= 1;
675 }
676 }
677}
678
680{
682
684 return;
685 }
686
687 if (select) {
690 bm->totfacesel += 1;
691 }
692 }
693 else {
696 bm->totfacesel -= 1;
697 }
698 }
699}
700
703void BM_mesh_select_mode_set(BMesh *bm, int selectmode)
704{
705 BMIter iter;
706 BMElem *ele;
707
708 bm->selectmode = selectmode;
709
711/* disabled because selection flushing handles these */
712#if 0
713 BM_ITER_MESH (ele, &iter, bm, BM_EDGES_OF_MESH) {
715 }
716 BM_ITER_MESH (ele, &iter, bm, BM_FACES_OF_MESH) {
718 }
719#endif
721 }
722 else if (bm->selectmode & SCE_SELECT_EDGE) {
723/* disabled because selection flushing handles these */
724#if 0
725 BM_ITER_MESH (ele, &iter, bm, BM_VERTS_OF_MESH) {
727 }
728#endif
729
730 BM_ITER_MESH (ele, &iter, bm, BM_EDGES_OF_MESH) {
732 BM_edge_select_set(bm, (BMEdge *)ele, true);
733 }
734 }
736 }
737 else if (bm->selectmode & SCE_SELECT_FACE) {
738/* disabled because selection flushing handles these */
739#if 0
740 BM_ITER_MESH (ele, &iter, bm, BM_EDGES_OF_MESH) {
742 }
743#endif
744 BM_ITER_MESH (ele, &iter, bm, BM_FACES_OF_MESH) {
746 BM_face_select_set(bm, (BMFace *)ele, true);
747 }
748 }
750 }
751}
752
757 const char htype,
758 const char hflag,
759 const bool respecthide,
760 const bool test_for_enabled)
761{
762 BMElem *ele;
763 BMIter iter;
764 int tot = 0;
765
766 BLI_assert((htype & ~BM_ALL_NOLOOP) == 0);
767
768 if (htype & BM_VERT) {
769 BM_ITER_MESH (ele, &iter, bm, BM_VERTS_OF_MESH) {
770 if (respecthide && BM_elem_flag_test(ele, BM_ELEM_HIDDEN)) {
771 continue;
772 }
773 if (BM_elem_flag_test_bool(ele, hflag) == test_for_enabled) {
774 tot++;
775 }
776 }
777 }
778 if (htype & BM_EDGE) {
779 BM_ITER_MESH (ele, &iter, bm, BM_EDGES_OF_MESH) {
780 if (respecthide && BM_elem_flag_test(ele, BM_ELEM_HIDDEN)) {
781 continue;
782 }
783 if (BM_elem_flag_test_bool(ele, hflag) == test_for_enabled) {
784 tot++;
785 }
786 }
787 }
788 if (htype & BM_FACE) {
789 BM_ITER_MESH (ele, &iter, bm, BM_FACES_OF_MESH) {
790 if (respecthide && BM_elem_flag_test(ele, BM_ELEM_HIDDEN)) {
791 continue;
792 }
793 if (BM_elem_flag_test_bool(ele, hflag) == test_for_enabled) {
794 tot++;
795 }
796 }
797 }
798
799 return tot;
800}
801
803 const char htype,
804 const char hflag,
805 const bool respecthide)
806{
807 return bm_mesh_flag_count(bm, htype, hflag, respecthide, true);
808}
809
811 const char htype,
812 const char hflag,
813 const bool respecthide)
814{
815 return bm_mesh_flag_count(bm, htype, hflag, respecthide, false);
816}
817
818void BM_elem_select_set(BMesh *bm, BMElem *ele, const bool select)
819{
820 switch (ele->head.htype) {
821 case BM_VERT:
823 break;
824 case BM_EDGE:
826 break;
827 case BM_FACE:
829 break;
830 default:
831 BLI_assert(0);
832 break;
833 }
834}
835
837{
838 bm->act_face = f;
839}
840
841int BM_mesh_active_face_index_get(BMesh *bm, bool is_sloppy, bool is_selected)
842{
843 const BMFace *f = BM_mesh_active_face_get(bm, is_sloppy, is_selected);
844 return f ? BM_elem_index_get(f) : -1;
845}
846
848{
850 return e ? BM_elem_index_get(e) : -1;
851}
852
854{
856 return v ? BM_elem_index_get(v) : -1;
857}
858
860{
862 return e ? BM_elem_index_get(e) : -1;
863}
864
865BMFace *BM_mesh_active_face_get(BMesh *bm, const bool is_sloppy, const bool is_selected)
866{
867 if (bm->act_face && (!is_selected || BM_elem_flag_test(bm->act_face, BM_ELEM_SELECT))) {
868 return bm->act_face;
869 }
870 if (is_sloppy) {
871 BMIter iter;
872 BMFace *f = nullptr;
873 BMEditSelection *ese;
874
875 /* Find the latest non-hidden face from the BMEditSelection */
876 ese = static_cast<BMEditSelection *>(bm->selected.last);
877 for (; ese; ese = ese->prev) {
878 if (ese->htype == BM_FACE) {
879 f = (BMFace *)ese->ele;
880
882 f = nullptr;
883 }
884 else if (is_selected && !BM_elem_flag_test(f, BM_ELEM_SELECT)) {
885 f = nullptr;
886 }
887 else {
888 break;
889 }
890 }
891 }
892 /* Last attempt: try to find any selected face */
893 if (f == nullptr) {
894 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
896 break;
897 }
898 }
899 }
900 return f; /* can still be null */
901 }
902 return nullptr;
903}
904
906{
907 if (bm->selected.last) {
908 BMEditSelection *ese = static_cast<BMEditSelection *>(bm->selected.last);
909
910 if (ese && ese->htype == BM_EDGE) {
911 return (BMEdge *)ese->ele;
912 }
913 }
914
915 return nullptr;
916}
917
919{
920 if (bm->selected.last) {
921 BMEditSelection *ese = static_cast<BMEditSelection *>(bm->selected.last);
922
923 if (ese && ese->htype == BM_VERT) {
924 return (BMVert *)ese->ele;
925 }
926 }
927
928 return nullptr;
929}
930
932{
933 if (bm->selected.last) {
934 BMEditSelection *ese = static_cast<BMEditSelection *>(bm->selected.last);
935
936 if (ese) {
937 return ese->ele;
938 }
939 }
940
941 return nullptr;
942}
943
944void BM_editselection_center(BMEditSelection *ese, float r_center[3])
945{
946 if (ese->htype == BM_VERT) {
947 BMVert *eve = (BMVert *)ese->ele;
948 copy_v3_v3(r_center, eve->co);
949 }
950 else if (ese->htype == BM_EDGE) {
951 BMEdge *eed = (BMEdge *)ese->ele;
952 mid_v3_v3v3(r_center, eed->v1->co, eed->v2->co);
953 }
954 else if (ese->htype == BM_FACE) {
955 BMFace *efa = (BMFace *)ese->ele;
956 BM_face_calc_center_median(efa, r_center);
957 }
958}
959
960void BM_editselection_normal(BMEditSelection *ese, float r_normal[3])
961{
962 if (ese->htype == BM_VERT) {
963 BMVert *eve = (BMVert *)ese->ele;
964 copy_v3_v3(r_normal, eve->no);
965 }
966 else if (ese->htype == BM_EDGE) {
967 BMEdge *eed = (BMEdge *)ese->ele;
968 float plane[3]; /* need a plane to correct the normal */
969 float vec[3]; /* temp vec storage */
970
971 add_v3_v3v3(r_normal, eed->v1->no, eed->v2->no);
972 sub_v3_v3v3(plane, eed->v2->co, eed->v1->co);
973
974 /* the 2 vertex normals will be close but not at right angles to the edge
975 * for rotate about edge we want them to be at right angles, so we need to
976 * do some extra calculation to correct the vert normals,
977 * we need the plane for this */
978 cross_v3_v3v3(vec, r_normal, plane);
979 cross_v3_v3v3(r_normal, plane, vec);
980 normalize_v3(r_normal);
981 }
982 else if (ese->htype == BM_FACE) {
983 BMFace *efa = (BMFace *)ese->ele;
984 copy_v3_v3(r_normal, efa->no);
985 }
986}
987
988void BM_editselection_plane(BMEditSelection *ese, float r_plane[3])
989{
990 if (ese->htype == BM_VERT) {
991 BMVert *eve = (BMVert *)ese->ele;
992 float vec[3] = {0.0f, 0.0f, 0.0f};
993
994 if (ese->prev) { /* use previously selected data to make a useful vertex plane */
995 BM_editselection_center(ese->prev, vec);
996 sub_v3_v3v3(r_plane, vec, eve->co);
997 }
998 else {
999 /* make a fake plane that's at right-angles to the normal
1000 * we can't make a cross-vector from a vec that's the same as the vec
1001 * unlikely but possible, so make sure if the normal is (0, 0, 1)
1002 * that vec isn't the same or in the same direction even. */
1003 if (eve->no[0] < 0.5f) {
1004 vec[0] = 1.0f;
1005 }
1006 else if (eve->no[1] < 0.5f) {
1007 vec[1] = 1.0f;
1008 }
1009 else {
1010 vec[2] = 1.0f;
1011 }
1012 cross_v3_v3v3(r_plane, eve->no, vec);
1013 }
1014 normalize_v3(r_plane);
1015 }
1016 else if (ese->htype == BM_EDGE) {
1017 BMEdge *eed = (BMEdge *)ese->ele;
1018
1019 if (BM_edge_is_boundary(eed)) {
1020 sub_v3_v3v3(r_plane, eed->l->v->co, eed->l->next->v->co);
1021 }
1022 else {
1023 /* the plane is simple, it runs along the edge
1024 * however selecting different edges can swap the direction of the y axis.
1025 * this makes it less likely for the y axis of the gizmo
1026 * (running along the edge).. to flip less often.
1027 * at least its more predictable */
1028 if (eed->v2->co[1] > eed->v1->co[1]) { /* check which to do first */
1029 sub_v3_v3v3(r_plane, eed->v2->co, eed->v1->co);
1030 }
1031 else {
1032 sub_v3_v3v3(r_plane, eed->v1->co, eed->v2->co);
1033 }
1034 }
1035
1036 normalize_v3(r_plane);
1037 }
1038 else if (ese->htype == BM_FACE) {
1039 BMFace *efa = (BMFace *)ese->ele;
1040 BM_face_calc_tangent_auto(efa, r_plane);
1041 }
1042}
1043
1045{
1047 "BMEdit Selection");
1048 ese->htype = ele->htype;
1049 ese->ele = (BMElem *)ele;
1050 return ese;
1051}
1052
1053/* --- Macro wrapped functions. --- */
1054
1056{
1057 return (BLI_findptr(&bm->selected, ele, offsetof(BMEditSelection, ele)) != nullptr);
1058}
1059
1061{
1062 BMEditSelection *ese = static_cast<BMEditSelection *>(
1064 if (ese) {
1065 BLI_freelinkN(&bm->selected, ese);
1066 return true;
1067 }
1068 return false;
1069}
1070
1076
1082
1084{
1085 if (!BM_select_history_check(bm, (BMElem *)ele)) {
1087 }
1088}
1089
1096
1102
1104{
1105 if (!BM_select_history_check(bm, (BMElem *)ele)) {
1107 }
1108}
1109/* --- End macro wrapped functions --- */
1110
1115
1117{
1118 BMEditSelection *ese, *ese_next;
1119
1120 for (ese = static_cast<BMEditSelection *>(bm->selected.first); ese; ese = ese_next) {
1121 ese_next = ese->next;
1122 if (!BM_elem_flag_test(ese->ele, BM_ELEM_SELECT)) {
1123 BLI_freelinkN(&(bm->selected), ese);
1124 }
1125 }
1126}
1127
1129{
1130 BMEditSelection *ese_last = static_cast<BMEditSelection *>(bm->selected.last);
1131 BMFace *efa = BM_mesh_active_face_get(bm, false, true);
1132
1133 ese->next = ese->prev = nullptr;
1134
1135 if (ese_last) {
1136 /* If there is an active face, use it over the last selected face. */
1137 if (ese_last->htype == BM_FACE) {
1138 if (efa) {
1139 ese->ele = (BMElem *)efa;
1140 }
1141 else {
1142 ese->ele = ese_last->ele;
1143 }
1144 ese->htype = BM_FACE;
1145 }
1146 else {
1147 ese->ele = ese_last->ele;
1148 ese->htype = ese_last->htype;
1149 }
1150 }
1151 else if (efa) {
1152 /* no edit-selection, fallback to active face */
1153 ese->ele = (BMElem *)efa;
1154 ese->htype = BM_FACE;
1155 }
1156 else {
1157 ese->ele = nullptr;
1158 return false;
1159 }
1160
1161 return true;
1162}
1163
1165{
1167 return nullptr;
1168 }
1169
1170 GHash *map = BLI_ghash_ptr_new(__func__);
1171
1173 BLI_ghash_insert(map, ese->ele, ese);
1174 }
1175
1176 return map;
1177}
1178
1180 BMesh *bm, GHash *vert_map, GHash *edge_map, GHash *face_map, const bool use_chain)
1181{
1182
1183#ifndef NDEBUG
1186 }
1187#endif
1188
1191
1192 /* Only loop when (use_chain == true). */
1193 GHash *map = nullptr;
1194 switch (ese->ele->head.htype) {
1195 case BM_VERT:
1196 map = vert_map;
1197 break;
1198 case BM_EDGE:
1199 map = edge_map;
1200 break;
1201 case BM_FACE:
1202 map = face_map;
1203 break;
1204 default:
1205 BMESH_ASSERT(0);
1206 break;
1207 }
1208 if (map != nullptr) {
1209 BMElem *ele_dst = ese->ele;
1210 while (true) {
1211 BMElem *ele_dst_next = static_cast<BMElem *>(BLI_ghash_lookup(map, ele_dst));
1212 BLI_assert(ele_dst != ele_dst_next);
1213 if (ele_dst_next == nullptr) {
1214 break;
1215 }
1216 ele_dst = ele_dst_next;
1217 /* Break loop on circular reference (should never happen). */
1218 if (UNLIKELY(ele_dst == ese->ele)) {
1219 BLI_assert(0);
1220 break;
1221 }
1222 if (use_chain == false) {
1223 break;
1224 }
1225 }
1226 ese->ele = ele_dst;
1227 }
1228 }
1229
1230 /* Remove overlapping duplicates. */
1231 for (BMEditSelection *ese = static_cast<BMEditSelection *>(bm->selected.first), *ese_next; ese;
1232 ese = ese_next)
1233 {
1234 ese_next = ese->next;
1235 if (BM_ELEM_API_FLAG_TEST(ese->ele, _FLAG_OVERLAP)) {
1237 }
1238 else {
1239 BLI_freelinkN(&bm->selected, ese);
1240 }
1241 }
1242}
1243
1245 const char htype,
1246 const char hflag,
1247 const bool respecthide,
1248 const bool overwrite,
1249 const char hflag_test)
1250{
1251 const char iter_types[3] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, BM_FACES_OF_MESH};
1252
1253 const char flag_types[3] = {BM_VERT, BM_EDGE, BM_FACE};
1254
1255 const char hflag_nosel = hflag & ~BM_ELEM_SELECT;
1256
1257 int i;
1258
1259 BLI_assert((htype & ~BM_ALL_NOLOOP) == 0);
1260
1261 if (hflag & BM_ELEM_SELECT) {
1263 }
1264
1265 if ((htype == (BM_VERT | BM_EDGE | BM_FACE)) && (hflag == BM_ELEM_SELECT) &&
1266 (respecthide == false) && (hflag_test == 0))
1267 {
1268 /* fast path for deselect all, avoid topology loops
1269 * since we know all will be de-selected anyway. */
1270 for (i = 0; i < 3; i++) {
1271 BMIter iter;
1272 BMElem *ele;
1273
1274 ele = static_cast<BMElem *>(BM_iter_new(&iter, bm, iter_types[i], nullptr));
1275 for (; ele; ele = static_cast<BMElem *>(BM_iter_step(&iter))) {
1277 }
1278 }
1279
1281 }
1282 else {
1283 for (i = 0; i < 3; i++) {
1284 BMIter iter;
1285 BMElem *ele;
1286
1287 if (htype & flag_types[i]) {
1288 ele = static_cast<BMElem *>(BM_iter_new(&iter, bm, iter_types[i], nullptr));
1289 for (; ele; ele = static_cast<BMElem *>(BM_iter_step(&iter))) {
1290
1291 if (UNLIKELY(respecthide && BM_elem_flag_test(ele, BM_ELEM_HIDDEN))) {
1292 /* pass */
1293 }
1294 else if (!hflag_test || BM_elem_flag_test(ele, hflag_test)) {
1295 if (hflag & BM_ELEM_SELECT) {
1296 BM_elem_select_set(bm, ele, false);
1297 }
1298 BM_elem_flag_disable(ele, hflag);
1299 }
1300 else if (overwrite) {
1301 /* no match! */
1302 if (hflag & BM_ELEM_SELECT) {
1303 BM_elem_select_set(bm, ele, true);
1304 }
1305 BM_elem_flag_enable(ele, hflag_nosel);
1306 }
1307 }
1308 }
1309 }
1310 }
1311}
1312
1314 const char htype,
1315 const char hflag,
1316 const bool respecthide,
1317 const bool overwrite,
1318 const char hflag_test)
1319{
1320 const char iter_types[3] = {BM_VERTS_OF_MESH, BM_EDGES_OF_MESH, BM_FACES_OF_MESH};
1321
1322 const char flag_types[3] = {BM_VERT, BM_EDGE, BM_FACE};
1323
1324 /* use the nosel version when setting so under no
1325 * condition may a hidden face become selected.
1326 * Applying other flags to hidden faces is OK. */
1327 const char hflag_nosel = hflag & ~BM_ELEM_SELECT;
1328
1329 BMIter iter;
1330 BMElem *ele;
1331 int i;
1332
1333 BLI_assert((htype & ~BM_ALL_NOLOOP) == 0);
1334
1335 /* NOTE: better not attempt a fast path for selection as done with de-select
1336 * because hidden geometry and different selection modes can give different results,
1337 * we could of course check for no hidden faces and then use
1338 * quicker method but its not worth it. */
1339
1340 for (i = 0; i < 3; i++) {
1341 if (htype & flag_types[i]) {
1342 ele = static_cast<BMElem *>(BM_iter_new(&iter, bm, iter_types[i], nullptr));
1343 for (; ele; ele = static_cast<BMElem *>(BM_iter_step(&iter))) {
1344
1345 if (UNLIKELY(respecthide && BM_elem_flag_test(ele, BM_ELEM_HIDDEN))) {
1346 /* pass */
1347 }
1348 else if (!hflag_test || BM_elem_flag_test(ele, hflag_test)) {
1349 /* match! */
1350 if (hflag & BM_ELEM_SELECT) {
1351 BM_elem_select_set(bm, ele, true);
1352 }
1353 BM_elem_flag_enable(ele, hflag_nosel);
1354 }
1355 else if (overwrite) {
1356 /* no match! */
1357 if (hflag & BM_ELEM_SELECT) {
1358 BM_elem_select_set(bm, ele, false);
1359 }
1360 BM_elem_flag_disable(ele, hflag);
1361 }
1362 }
1363 }
1364 }
1365}
1366
1368 const char htype,
1369 const char hflag,
1370 const bool respecthide)
1371{
1372 /* call with 0 hflag_test */
1373 BM_mesh_elem_hflag_disable_test(bm, htype, hflag, respecthide, false, 0);
1374}
1375
1377 const char htype,
1378 const char hflag,
1379 const bool respecthide)
1380{
1381 /* call with 0 hflag_test */
1382 BM_mesh_elem_hflag_enable_test(bm, htype, hflag, respecthide, false, 0);
1383}
1384
1385/***************** Mesh Hiding stuff *********** */
1386
1395
1404
1405void BM_vert_hide_set(BMVert *v, const bool hide)
1406{
1407 /* vert hiding: vert + surrounding edges and faces */
1409 if (hide) {
1411 }
1412
1414
1415 if (v->e) {
1416 BMEdge *e_iter, *e_first;
1417 e_iter = e_first = v->e;
1418 do {
1419 BM_elem_flag_set(e_iter, BM_ELEM_HIDDEN, hide);
1420 if (e_iter->l) {
1421 const BMLoop *l_radial_iter, *l_radial_first;
1422 l_radial_iter = l_radial_first = e_iter->l;
1423 do {
1424 BM_elem_flag_set(l_radial_iter->f, BM_ELEM_HIDDEN, hide);
1425 } while ((l_radial_iter = l_radial_iter->radial_next) != l_radial_first);
1426 }
1427 } while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first);
1428 }
1429}
1430
1431void BM_edge_hide_set(BMEdge *e, const bool hide)
1432{
1434 if (hide) {
1436 }
1437
1438 /* edge hiding: faces around the edge */
1439 if (e->l) {
1440 const BMLoop *l_iter, *l_first;
1441 l_iter = l_first = e->l;
1442 do {
1443 BM_elem_flag_set(l_iter->f, BM_ELEM_HIDDEN, hide);
1444 } while ((l_iter = l_iter->radial_next) != l_first);
1445 }
1446
1448
1449 /* hide vertices if necessary */
1450 if (hide) {
1453 }
1454 else {
1457 }
1458}
1459
1460void BM_face_hide_set(BMFace *f, const bool hide)
1461{
1463 if (hide) {
1465 }
1466
1468
1469 if (hide) {
1470 BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
1471 BMLoop *l_iter;
1472
1473 l_iter = l_first;
1474 do {
1475 edge_flush_hide_set(l_iter->e);
1476 } while ((l_iter = l_iter->next) != l_first);
1477
1478 l_iter = l_first;
1479 do {
1480 vert_flush_hide_set(l_iter->v);
1481 } while ((l_iter = l_iter->next) != l_first);
1482 }
1483 else {
1484 BMLoop *l_first = BM_FACE_FIRST_LOOP(f);
1485 BMLoop *l_iter;
1486
1487 l_iter = l_first;
1488 do {
1491 } while ((l_iter = l_iter->next) != l_first);
1492 }
1493}
1494
1495void _bm_elem_hide_set(BMesh *bm, BMHeader *head, const bool hide)
1496{
1497 /* Follow convention of always deselecting before
1498 * hiding an element */
1499 switch (head->htype) {
1500 case BM_VERT:
1501 if (hide) {
1502 BM_vert_select_set(bm, (BMVert *)head, false);
1503 }
1504 BM_vert_hide_set((BMVert *)head, hide);
1505 break;
1506 case BM_EDGE:
1507 if (hide) {
1508 BM_edge_select_set(bm, (BMEdge *)head, false);
1509 }
1510 BM_edge_hide_set((BMEdge *)head, hide);
1511 break;
1512 case BM_FACE:
1513 if (hide) {
1514 BM_face_select_set(bm, (BMFace *)head, false);
1515 }
1516 BM_face_hide_set((BMFace *)head, hide);
1517 break;
1518 default:
1519 BMESH_ASSERT(0);
1520 break;
1521 }
1522}
#define BLI_assert(a)
Definition BLI_assert.h:50
GHash * BLI_ghash_ptr_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
void * BLI_ghash_lookup(const GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:731
void BLI_ghash_insert(GHash *gh, void *key, void *val)
Definition BLI_ghash.c:707
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
void BLI_addhead(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:90
#define LISTBASE_FOREACH(type, var, list)
void BLI_freelinkN(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:269
void BLI_insertlinkafter(struct ListBase *listbase, void *vprevlink, void *vnewlink) ATTR_NONNULL(1)
Definition listbase.cc:331
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:496
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
void * BLI_findptr(const struct ListBase *listbase, const void *ptr, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void add_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void cross_v3_v3v3(float r[3], const float a[3], const float b[3])
void mid_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE float normalize_v3(float n[3])
struct MempoolIterData MempoolIterData
Definition BLI_task.h:209
BLI_INLINE void BLI_parallel_range_settings_defaults(TaskParallelSettings *settings)
Definition BLI_task.h:230
void(* TaskParallelMempoolFunc)(void *userdata, MempoolIterData *iter, const TaskParallelTLS *__restrict tls)
Definition BLI_task.h:211
#define UNLIKELY(x)
#define ELEM(...)
@ SCE_SELECT_FACE
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
Read Guarded memory(de)allocation.
#define BM_ALL_NOLOOP
@ BM_ELEM_HIDDEN
@ BM_ELEM_SELECT
#define BM_FACE_FIRST_LOOP(p)
#define BM_THREAD_LIMIT
#define BMESH_ASSERT(a)
#define BM_elem_index_get(ele)
#define BM_elem_flag_disable(ele, hflag)
#define BM_elem_flag_set(ele, hflag, val)
#define BM_elem_flag_test(ele, hflag)
#define BM_elem_flag_test_bool(ele, hflag)
#define BM_elem_flag_enable(ele, hflag)
#define BM_ITER_MESH(ele, iter, bm, itype)
BMIterType
BMesh Iterators.
@ BM_EDGES_OF_MESH
@ BM_VERTS_OF_MESH
@ BM_FACES_OF_MESH
#define BM_iter_new(iter, bm, itype, data)
ATTR_WARN_UNUSED_RESULT BMesh * bm
BMVert * BM_mesh_active_vert_get(BMesh *bm)
static TaskParallelMempoolFunc recount_totsels_get_range_func(BMIterType iter_type)
void BM_select_history_clear(BMesh *bm)
static void vert_flush_hide_set(BMVert *v)
void _bm_elem_hide_set(BMesh *bm, BMHeader *head, const bool hide)
bool _bm_select_history_check(BMesh *bm, const BMHeader *ele)
static void recount_totsels_range_vert_func(void *, MempoolIterData *iter, const TaskParallelTLS *__restrict tls)
void _bm_select_history_store_head_notest(BMesh *bm, BMHeader *ele)
int BM_mesh_active_face_index_get(BMesh *bm, bool is_sloppy, bool is_selected)
void BM_vert_hide_set(BMVert *v, const bool hide)
void BM_mesh_elem_hflag_enable_all(BMesh *bm, const char htype, const char hflag, const bool respecthide)
int BM_mesh_active_edge_index_get(BMesh *bm)
static bool bm_edge_is_face_visible_any(const BMEdge *e)
void BM_mesh_select_mode_flush(BMesh *bm)
int BM_mesh_elem_hflag_count_disabled(BMesh *bm, const char htype, const char hflag, const bool respecthide)
static void recount_totsels_range_face_func(void *, MempoolIterData *iter, const TaskParallelTLS *__restrict tls)
void BM_mesh_select_mode_set(BMesh *bm, int selectmode)
static void recount_totsels_reduce(const void *__restrict, void *__restrict chunk_join, void *__restrict chunk)
void BM_face_select_set(BMesh *bm, BMFace *f, const bool select)
Select Face.
static bool bm_vert_is_edge_visible_any(const BMVert *v)
BMEdge * BM_mesh_active_edge_get(BMesh *bm)
void BM_elem_select_set(BMesh *bm, BMElem *ele, const bool select)
void BM_mesh_select_flush(BMesh *bm)
static bool recount_totsels_are_ok(BMesh *bm)
bool _bm_select_history_remove(BMesh *bm, BMHeader *ele)
static void recount_totsels(BMesh *bm)
void _bm_select_history_store_head(BMesh *bm, BMHeader *ele)
void BM_mesh_select_mode_clean_ex(BMesh *bm, const short selectmode)
Select Mode Clean.
static void bm_mesh_select_mode_flush_vert_to_edge(BMesh *bm)
static bool bm_vert_is_edge_select_any_other(const BMVert *v, const BMEdge *e_first)
int BM_mesh_active_elem_index_get(BMesh *bm)
void _bm_select_history_store_after(BMesh *bm, BMEditSelection *ese_ref, BMHeader *ele)
void BM_mesh_select_mode_flush_ex(BMesh *bm, const short selectmode, eBMSelectionFlushFLags flags)
Select Mode Flush.
static bool bm_edge_is_face_select_any_other(BMLoop *l_first)
void _bm_select_history_store_notest(BMesh *bm, BMHeader *ele)
BMFace * BM_mesh_active_face_get(BMesh *bm, const bool is_sloppy, const bool is_selected)
int BM_mesh_active_vert_index_get(BMesh *bm)
static int recount_totsel(BMesh *bm, BMIterType iter_type)
BMElem * BM_mesh_active_elem_get(BMesh *bm)
void BM_mesh_select_mode_clean(BMesh *bm)
static void bm_mesh_select_mode_flush_vert_to_edge_iter_fn(void *, MempoolIterData *iter, const TaskParallelTLS *__restrict tls)
void BM_editselection_center(BMEditSelection *ese, float r_center[3])
void BM_vert_select_set(BMesh *bm, BMVert *v, const bool select)
Select Vert.
void BM_mesh_elem_hflag_disable_test(BMesh *bm, const char htype, const char hflag, const bool respecthide, const bool overwrite, const char hflag_test)
void _bm_select_history_store_after_notest(BMesh *bm, BMEditSelection *ese_ref, BMHeader *ele)
void BM_edge_select_set(BMesh *bm, BMEdge *e, const bool select)
Select Edge.
void BM_select_history_validate(BMesh *bm)
void BM_editselection_plane(BMEditSelection *ese, float r_plane[3])
void BM_edge_select_set_noflush(BMesh *bm, BMEdge *e, const bool select)
void BM_editselection_normal(BMEditSelection *ese, float r_normal[3])
static void bm_mesh_select_mode_flush_edge_to_face_iter_fn(void *, MempoolIterData *iter, const TaskParallelTLS *__restrict tls)
void BM_mesh_deselect_flush(BMesh *bm)
static void edge_flush_hide_set(BMEdge *e)
GHash * BM_select_history_map_create(BMesh *bm)
void BM_edge_hide_set(BMEdge *e, const bool hide)
static void bm_mesh_select_mode_flush_edge_to_face(BMesh *bm)
void BM_mesh_elem_hflag_disable_all(BMesh *bm, const char htype, const char hflag, const bool respecthide)
static int bm_mesh_flag_count(BMesh *bm, const char htype, const char hflag, const bool respecthide, const bool test_for_enabled)
void BM_mesh_active_face_set(BMesh *bm, BMFace *f)
void _bm_select_history_store(BMesh *bm, BMHeader *ele)
static void recount_totsels_range_edge_func(void *, MempoolIterData *iter, const TaskParallelTLS *__restrict tls)
static void recount_totvertsel(BMesh *bm)
void BM_select_history_merge_from_targetmap(BMesh *bm, GHash *vert_map, GHash *edge_map, GHash *face_map, const bool use_chain)
void BM_face_select_set_noflush(BMesh *bm, BMFace *f, const bool select)
static void bm_mesh_select_mode_flush_reduce_fn(const void *__restrict, void *__restrict chunk_join, void *__restrict chunk)
static void recount_totedgesel(BMesh *bm)
bool BM_select_history_active_get(BMesh *bm, BMEditSelection *ese)
void BM_face_hide_set(BMFace *f, const bool hide)
static BMEditSelection * bm_select_history_create(BMHeader *ele)
int BM_mesh_elem_hflag_count_enabled(BMesh *bm, const char htype, const char hflag, const bool respecthide)
void BM_mesh_elem_hflag_enable_test(BMesh *bm, const char htype, const char hflag, const bool respecthide, const bool overwrite, const char hflag_test)
static void recount_totfacesel(BMesh *bm)
#define BM_select_history_store_notest(bm, ele)
#define BM_select_history_store_head_notest(bm, ele)
#define BM_select_history_store_after_notest(bm, ese_ref, ele)
#define BM_select_history_check(bm, ele)
eBMSelectionFlushFLags
@ BM_SELECT_LEN_FLUSH_RECALC_ALL
@ BM_SELECT_LEN_FLUSH_RECALC_EDGE
@ BM_SELECT_LEN_FLUSH_RECALC_FACE
@ BM_SELECT_LEN_FLUSH_RECALC_VERT
#define BM_FACE
#define BM_EDGE
#define BM_VERT
void BM_face_calc_center_median(const BMFace *f, float r_cent[3])
void BM_face_calc_tangent_auto(const BMFace *f, float r_tangent[3])
#define BM_ELEM_API_FLAG_DISABLE(element, f)
#define BM_ELEM_API_FLAG_TEST(element, f)
@ _FLAG_OVERLAP
#define BM_ELEM_API_FLAG_ENABLE(element, f)
BLI_INLINE bool BM_edge_is_boundary(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
BLI_INLINE BMEdge * bmesh_disk_edge_next(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
#define offsetof(t, d)
int count
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
ccl_device_inline float4 select(const int4 mask, const float4 a, const float4 b)
BMVert * v1
BMVert * v2
struct BMLoop * l
struct BMEditSelection * next
struct BMEditSelection * prev
BMHeader head
BMHeader head
float no[3]
struct BMVert * v
struct BMEdge * e
struct BMLoop * radial_next
struct BMFace * f
struct BMLoop * next
float co[3]
struct BMEdge * e
float no[3]
BMHeader head
int totfacesel
int totedge
ListBase selected
int totvertsel
BMFace * act_face
short selectmode
int totedgesel
int totface
void * last
void * first