Blender V4.5
editmesh_select.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2004 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <algorithm>
10#include <optional>
11
12#include "MEM_guardedalloc.h"
13
14#include "BLI_heap.h"
15#include "BLI_listbase.h"
16#include "BLI_math_bits.h"
17#include "BLI_math_geom.h"
18#include "BLI_math_matrix.h"
19#include "BLI_math_rotation.h"
20#include "BLI_math_vector.h"
21#include "BLI_rand.h"
23#include "BLI_vector.hh"
24
25#include "BKE_attribute.hh"
26#include "BKE_context.hh"
27#include "BKE_customdata.hh"
28#include "BKE_deform.hh"
29#include "BKE_editmesh.hh"
30#include "BKE_layer.hh"
31#include "BKE_mesh.hh"
32#include "BKE_mesh_wrapper.hh"
33#include "BKE_object.hh"
34#include "BKE_report.hh"
35
36#include "WM_api.hh"
37#include "WM_types.hh"
38
39#include "RNA_access.hh"
40#include "RNA_define.hh"
41#include "RNA_enum_types.hh"
42
43#include "ED_mesh.hh"
44#include "ED_object.hh"
45#include "ED_screen.hh"
46#include "ED_select_utils.hh"
47#include "ED_transform.hh"
48#include "ED_view3d.hh"
49
50#include "BLT_translation.hh"
51
52#include "DNA_mesh_types.h"
53#include "DNA_meshdata_types.h"
54#include "DNA_object_types.h"
55
56#include "bmesh_tools.hh"
57
58#include "DEG_depsgraph.hh"
60
61#include "DRW_select_buffer.hh"
62
63#include "mesh_intern.hh" /* Own include. */
64
66#define BMO_ELE_TAG 1
67
68using blender::float3;
69using blender::Span;
70using blender::Vector;
71
72/* -------------------------------------------------------------------- */
75
77{
78 Object *obedit = CTX_data_edit_object(C);
79 if (obedit && obedit->type == OB_MESH) {
80 const BMEditMesh *em = BKE_editmesh_from_object(obedit);
81 if (em) {
83 return true;
84 }
85 }
86 }
87
88 CTX_wm_operator_poll_msg_set(C, "An edit-mesh with vertex or edge selection mode is required");
89
90 return false;
91}
92
94
95/* -------------------------------------------------------------------- */
98
105
107 {ELEM_COUNT_LESS, "LESS", false, "Less Than", ""},
108 {ELEM_COUNT_EQUAL, "EQUAL", false, "Equal To", ""},
109 {ELEM_COUNT_GREATER, "GREATER", false, "Greater Than", ""},
110 {ELEM_COUNT_NOT_EQUAL, "NOTEQUAL", false, "Not Equal To", ""},
111 {0, nullptr, 0, nullptr, nullptr},
112};
113
114static inline bool is_count_a_match(const eElemCountType type,
115 const int value_test,
116 const int value_reference)
117{
118 switch (type) {
119 case ELEM_COUNT_LESS:
120 return (value_test < value_reference);
121 case ELEM_COUNT_EQUAL:
122 return (value_test == value_reference);
124 return (value_test > value_reference);
126 return (value_test != value_reference);
127 default:
128 BLI_assert_unreachable(); /* Bad value of selection `type`. */
129 return false;
130 }
131}
132
134
135/* -------------------------------------------------------------------- */
138
140 const Mesh *mesh,
141 const int axis,
142 const bool extend,
143 int *r_totmirr,
144 int *r_totfail)
145{
146 BMesh *bm = em->bm;
147 BMIter iter;
148 int totmirr = 0;
149 int totfail = 0;
150 bool use_topology = mesh->editflag & ME_EDIT_MIRROR_TOPO;
151
152 *r_totmirr = *r_totfail = 0;
153
154 /* Flush (select -> tag). */
155 if (bm->selectmode & SCE_SELECT_VERTEX) {
156 BMVert *v;
157 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
159 }
160 }
161 else if (em->selectmode & SCE_SELECT_EDGE) {
162 BMEdge *e;
163 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
165 }
166 }
167 else {
168 BMFace *f;
169 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
171 }
172 }
173
174 EDBM_verts_mirror_cache_begin(em, axis, true, true, false, use_topology);
175
176 if (!extend) {
178 }
179
180 if (bm->selectmode & SCE_SELECT_VERTEX) {
181 BMVert *v;
182 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
184 continue;
185 }
186
188 BMVert *v_mirr = EDBM_verts_mirror_get(em, v);
189 if (v_mirr && !BM_elem_flag_test(v_mirr, BM_ELEM_HIDDEN)) {
190 BM_vert_select_set(bm, v_mirr, true);
191 totmirr++;
192 }
193 else {
194 totfail++;
195 }
196 }
197 }
198 }
199 else if (em->selectmode & SCE_SELECT_EDGE) {
200 BMEdge *e;
201 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
203 continue;
204 }
205
207 BMEdge *e_mirr = EDBM_verts_mirror_get_edge(em, e);
208 if (e_mirr && !BM_elem_flag_test(e_mirr, BM_ELEM_HIDDEN)) {
209 BM_edge_select_set(bm, e_mirr, true);
210 totmirr++;
211 }
212 else {
213 totfail++;
214 }
215 }
216 }
217 }
218 else {
219 BMFace *f;
220 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
222 continue;
223 }
224
226 BMFace *f_mirr = EDBM_verts_mirror_get_face(em, f);
227 if (f_mirr && !BM_elem_flag_test(f_mirr, BM_ELEM_HIDDEN)) {
228 BM_face_select_set(bm, f_mirr, true);
229 totmirr++;
230 }
231 else {
232 totfail++;
233 }
234 }
235 }
236 }
237
239
240 *r_totmirr = totmirr;
241 *r_totfail = totfail;
242}
243
245
246/* -------------------------------------------------------------------- */
249
251 const uint sel_id,
252 uint &r_base_index)
253{
254 uint elem_id;
255 char elem_type = 0;
256 bool success = DRW_select_buffer_elem_get(sel_id, elem_id, r_base_index, elem_type);
257
258 if (success) {
259 Object *obedit = bases[r_base_index]->object;
261
262 switch (elem_type) {
263 case SCE_SELECT_FACE:
264 return (BMElem *)BM_face_at_index_find_or_table(em->bm, elem_id);
265 case SCE_SELECT_EDGE:
266 return (BMElem *)BM_edge_at_index_find_or_table(em->bm, elem_id);
268 return (BMElem *)BM_vert_at_index_find_or_table(em->bm, elem_id);
269 default:
270 BLI_assert(0);
271 return nullptr;
272 }
273 }
274
275 return nullptr;
276}
277
279
280/* -------------------------------------------------------------------- */
290
291#define FIND_NEAR_SELECT_BIAS 5
292#define FIND_NEAR_CYCLE_THRESHOLD_MIN 3
293
300
310
311static void findnearestvert__doClosest(void *user_data,
312 BMVert *eve,
313 const float screen_co[2],
314 int index)
315{
316 NearestVertUserData *data = static_cast<NearestVertUserData *>(user_data);
317 float dist_test, dist_test_bias;
318
319 dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co);
320
321 if (data->use_select_bias && BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
322 dist_test_bias += FIND_NEAR_SELECT_BIAS;
323 }
324
325 if (dist_test_bias < data->hit.dist_bias) {
326 data->hit.dist_bias = dist_test_bias;
327 data->hit.dist = dist_test;
328 data->hit.index = index;
329 data->hit.vert = eve;
330 }
331
332 if (data->use_cycle) {
333 if ((data->hit_cycle.vert == nullptr) && (index > data->cycle_index_prev) &&
334 (dist_test_bias < FIND_NEAR_CYCLE_THRESHOLD_MIN))
335 {
336 data->hit_cycle.dist_bias = dist_test_bias;
337 data->hit_cycle.dist = dist_test;
338 data->hit_cycle.index = index;
339 data->hit_cycle.vert = eve;
340 }
341 }
342}
343
345 float *dist_px_manhattan_p,
346 const bool use_select_bias,
347 bool use_cycle,
348 const Span<Base *> bases,
349 uint *r_base_index)
350{
351 uint base_index = 0;
352
353 if (!XRAY_FLAG_ENABLED(vc->v3d)) {
354 uint dist_px_manhattan_test = uint(
355 ED_view3d_backbuf_sample_size_clamp(vc->region, *dist_px_manhattan_p));
356 uint index;
357 BMVert *eve;
358
359 /* No after-queue (yet), so we check it now, otherwise the bm_xxxofs indices are bad. */
360 {
362
364 vc->depsgraph, vc->region, vc->v3d, vc->mval, 1, UINT_MAX, &dist_px_manhattan_test);
365
366 if (index) {
367 eve = (BMVert *)edbm_select_id_bm_elem_get(bases, index, base_index);
368 }
369 else {
370 eve = nullptr;
371 }
372 }
373
374 if (eve) {
375 if (dist_px_manhattan_test < *dist_px_manhattan_p) {
376 if (r_base_index) {
377 *r_base_index = base_index;
378 }
379 *dist_px_manhattan_p = dist_px_manhattan_test;
380 return eve;
381 }
382 }
383 return nullptr;
384 }
385
387 const NearestVertUserData_Hit *hit = nullptr;
388 const eV3DProjTest clip_flag = RV3D_CLIPPING_ENABLED(vc->v3d, vc->rv3d) ?
391 BMesh *prev_select_bm = nullptr;
392
393 static struct {
394 int index;
395 const BMVert *elem;
396 const BMesh *bm;
397 } prev_select = {0};
398
399 data.mval_fl[0] = vc->mval[0];
400 data.mval_fl[1] = vc->mval[1];
401 data.use_select_bias = use_select_bias;
402 data.use_cycle = use_cycle;
403
404 for (; base_index < bases.size(); base_index++) {
405 Base *base_iter = bases[base_index];
407 if (use_cycle && prev_select.bm == vc->em->bm &&
408 prev_select.elem == BM_vert_at_index_find_or_table(vc->em->bm, prev_select.index))
409 {
410 data.cycle_index_prev = prev_select.index;
411 /* No need to compare in the rest of the loop. */
412 use_cycle = false;
413 }
414 else {
415 data.cycle_index_prev = 0;
416 }
417
418 data.hit.dist = data.hit_cycle.dist = data.hit.dist_bias = data.hit_cycle.dist_bias =
419 *dist_px_manhattan_p;
420
423
424 hit = (data.use_cycle && data.hit_cycle.vert) ? &data.hit_cycle : &data.hit;
425
426 if (hit->dist < *dist_px_manhattan_p) {
427 if (r_base_index) {
428 *r_base_index = base_index;
429 }
430 *dist_px_manhattan_p = hit->dist;
431 prev_select_bm = vc->em->bm;
432 }
433 }
434
435 if (hit == nullptr) {
436 return nullptr;
437 }
438
439 prev_select.index = hit->index;
440 prev_select.elem = hit->vert;
441 prev_select.bm = prev_select_bm;
442
443 return hit->vert;
444}
445
446BMVert *EDBM_vert_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
447{
450 return EDBM_vert_find_nearest_ex(vc, dist_px_manhattan_p, false, false, {base}, nullptr);
451}
452
455 float mval_fl[2];
456 float dist;
458};
459
460static void find_nearest_edge_center__doZBuf(void *user_data,
461 BMEdge *eed,
462 const float screen_co_a[2],
463 const float screen_co_b[2],
464 int /*index*/)
465{
466 NearestEdgeUserData_ZBuf *data = static_cast<NearestEdgeUserData_ZBuf *>(user_data);
467
468 if (eed == data->edge_test) {
469 float dist_test;
470 float screen_co_mid[2];
471
472 mid_v2_v2v2(screen_co_mid, screen_co_a, screen_co_b);
473 dist_test = len_manhattan_v2v2(data->mval_fl, screen_co_mid);
474
475 data->dist = std::min(dist_test, data->dist);
476 }
477}
478
491
502
503/* NOTE: uses v3d, so needs active 3d window. */
504static void find_nearest_edge__doClosest(void *user_data,
505 BMEdge *eed,
506 const float screen_co_a[2],
507 const float screen_co_b[2],
508 int index)
509{
510 NearestEdgeUserData *data = static_cast<NearestEdgeUserData *>(user_data);
511 float dist_test, dist_test_bias;
512
513 float fac = line_point_factor_v2(data->mval_fl, screen_co_a, screen_co_b);
514 float screen_co[2];
515
516 if (fac <= 0.0f) {
517 fac = 0.0f;
518 copy_v2_v2(screen_co, screen_co_a);
519 }
520 else if (fac >= 1.0f) {
521 fac = 1.0f;
522 copy_v2_v2(screen_co, screen_co_b);
523 }
524 else {
525 interp_v2_v2v2(screen_co, screen_co_a, screen_co_b, fac);
526 }
527
528 dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co);
529
530 if (data->use_select_bias && BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
531 dist_test_bias += FIND_NEAR_SELECT_BIAS;
532 }
533
534 if (data->vc.rv3d->rflag & RV3D_CLIPPING) {
535 float vec[3];
536
537 interp_v3_v3v3(vec, eed->v1->co, eed->v2->co, fac);
538 if (ED_view3d_clipping_test(data->vc.rv3d, vec, true)) {
539 return;
540 }
541 }
542
543 if (dist_test_bias < data->hit.dist_bias) {
544 float screen_co_mid[2];
545
546 data->hit.dist_bias = dist_test_bias;
547 data->hit.dist = dist_test;
548 data->hit.index = index;
549 data->hit.edge = eed;
550
551 mid_v2_v2v2(screen_co_mid, screen_co_a, screen_co_b);
552 data->hit.dist_center_px_manhattan = len_manhattan_v2v2(data->mval_fl, screen_co_mid);
553 }
554
555 if (data->use_cycle) {
556 if ((data->hit_cycle.edge == nullptr) && (index > data->cycle_index_prev) &&
557 (dist_test_bias < FIND_NEAR_CYCLE_THRESHOLD_MIN))
558 {
559 float screen_co_mid[2];
560
561 data->hit_cycle.dist_bias = dist_test_bias;
562 data->hit_cycle.dist = dist_test;
563 data->hit_cycle.index = index;
564 data->hit_cycle.edge = eed;
565
566 mid_v2_v2v2(screen_co_mid, screen_co_a, screen_co_b);
567 data->hit_cycle.dist_center_px_manhattan = len_manhattan_v2v2(data->mval_fl, screen_co_mid);
568 }
569 }
570}
571
573 float *dist_px_manhattan_p,
574 float *r_dist_center_px_manhattan,
575 const bool use_select_bias,
576 bool use_cycle,
577 BMEdge **r_eed_zbuf,
578 const Span<Base *> bases,
579 uint *r_base_index)
580{
581 uint base_index = 0;
582
583 if (!XRAY_FLAG_ENABLED(vc->v3d)) {
584 uint dist_px_manhattan_test = uint(
585 ED_view3d_backbuf_sample_size_clamp(vc->region, *dist_px_manhattan_p));
586 uint index;
587 BMEdge *eed;
588
589 /* No after-queue (yet), so we check it now, otherwise the bm_xxxofs indices are bad. */
590 {
592
594 vc->depsgraph, vc->region, vc->v3d, vc->mval, 1, UINT_MAX, &dist_px_manhattan_test);
595
596 if (index) {
597 eed = (BMEdge *)edbm_select_id_bm_elem_get(bases, index, base_index);
598 }
599 else {
600 eed = nullptr;
601 }
602 }
603
604 if (r_eed_zbuf) {
605 *r_eed_zbuf = eed;
606 }
607
608 /* Exception for faces (verts don't need this). */
609 if (r_dist_center_px_manhattan && eed) {
611
612 data.mval_fl[0] = vc->mval[0];
613 data.mval_fl[1] = vc->mval[1];
614 data.dist = FLT_MAX;
615 data.edge_test = eed;
616
618
621 &data,
623
624 *r_dist_center_px_manhattan = data.dist;
625 }
626 /* End exception. */
627
628 if (eed) {
629 if (dist_px_manhattan_test < *dist_px_manhattan_p) {
630 if (r_base_index) {
631 *r_base_index = base_index;
632 }
633 *dist_px_manhattan_p = dist_px_manhattan_test;
634 return eed;
635 }
636 }
637 return nullptr;
638 }
639
640 NearestEdgeUserData data = {{nullptr}};
641 const NearestEdgeUserData_Hit *hit = nullptr;
642 /* Interpolate along the edge before doing a clipping plane test. */
644 BMesh *prev_select_bm = nullptr;
645
646 static struct {
647 int index;
648 const BMEdge *elem;
649 const BMesh *bm;
650 } prev_select = {0};
651
652 data.vc = *vc;
653 data.mval_fl[0] = vc->mval[0];
654 data.mval_fl[1] = vc->mval[1];
655 data.use_select_bias = use_select_bias;
656 data.use_cycle = use_cycle;
657
658 for (; base_index < bases.size(); base_index++) {
659 Base *base_iter = bases[base_index];
661 if (use_cycle && prev_select.bm == vc->em->bm &&
662 prev_select.elem == BM_edge_at_index_find_or_table(vc->em->bm, prev_select.index))
663 {
664 data.cycle_index_prev = prev_select.index;
665 /* No need to compare in the rest of the loop. */
666 use_cycle = false;
667 }
668 else {
669 data.cycle_index_prev = 0;
670 }
671
672 data.hit.dist = data.hit_cycle.dist = data.hit.dist_bias = data.hit_cycle.dist_bias =
673 *dist_px_manhattan_p;
674
678
679 hit = (data.use_cycle && data.hit_cycle.edge) ? &data.hit_cycle : &data.hit;
680
681 if (hit->dist < *dist_px_manhattan_p) {
682 if (r_base_index) {
683 *r_base_index = base_index;
684 }
685 *dist_px_manhattan_p = hit->dist;
686 prev_select_bm = vc->em->bm;
687 }
688 }
689
690 if (hit == nullptr) {
691 return nullptr;
692 }
693
694 if (r_dist_center_px_manhattan) {
695 *r_dist_center_px_manhattan = hit->dist_center_px_manhattan;
696 }
697
698 prev_select.index = hit->index;
699 prev_select.elem = hit->edge;
700 prev_select.bm = prev_select_bm;
701
702 return hit->edge;
703}
704
705BMEdge *EDBM_edge_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
706{
710 vc, dist_px_manhattan_p, nullptr, false, false, nullptr, {base}, nullptr);
711}
712
719
720static void find_nearest_face_center__doZBuf(void *user_data,
721 BMFace *efa,
722 const float screen_co[2],
723 int /*index*/)
724{
725 NearestFaceUserData_ZBuf *data = static_cast<NearestFaceUserData_ZBuf *>(user_data);
726
727 if (efa == data->face_test) {
728 const float dist_test = len_manhattan_v2v2(data->mval_fl, screen_co);
729
730 data->dist_px_manhattan = std::min(dist_test, data->dist_px_manhattan);
731 }
732}
733
740
750
751static void findnearestface__doClosest(void *user_data,
752 BMFace *efa,
753 const float screen_co[2],
754 int index)
755{
756 NearestFaceUserData *data = static_cast<NearestFaceUserData *>(user_data);
757 float dist_test, dist_test_bias;
758
759 dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co);
760
761 if (data->use_select_bias && BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
762 dist_test_bias += FIND_NEAR_SELECT_BIAS;
763 }
764
765 if (dist_test_bias < data->hit.dist_bias) {
766 data->hit.dist_bias = dist_test_bias;
767 data->hit.dist = dist_test;
768 data->hit.index = index;
769 data->hit.face = efa;
770 }
771
772 if (data->use_cycle) {
773 if ((data->hit_cycle.face == nullptr) && (index > data->cycle_index_prev) &&
774 (dist_test_bias < FIND_NEAR_CYCLE_THRESHOLD_MIN))
775 {
776 data->hit_cycle.dist_bias = dist_test_bias;
777 data->hit_cycle.dist = dist_test;
778 data->hit_cycle.index = index;
779 data->hit_cycle.face = efa;
780 }
781 }
782}
783
785 float *dist_px_manhattan_p,
786 float *r_dist_center,
787 const bool use_zbuf_single_px,
788 const bool use_select_bias,
789 bool use_cycle,
790 BMFace **r_efa_zbuf,
791 const Span<Base *> bases,
792 uint *r_base_index)
793{
794 uint base_index = 0;
795
796 if (!XRAY_FLAG_ENABLED(vc->v3d)) {
797 float dist_test;
798 uint index;
799 BMFace *efa;
800
801 {
802 uint dist_px_manhattan_test = 0;
803 if (*dist_px_manhattan_p != 0.0f && (use_zbuf_single_px == false)) {
804 dist_px_manhattan_test = uint(
805 ED_view3d_backbuf_sample_size_clamp(vc->region, *dist_px_manhattan_p));
806 }
807
809
810 if (dist_px_manhattan_test == 0) {
811 index = DRW_select_buffer_sample_point(vc->depsgraph, vc->region, vc->v3d, vc->mval);
812 dist_test = 0.0f;
813 }
814 else {
816 vc->depsgraph, vc->region, vc->v3d, vc->mval, 1, UINT_MAX, &dist_px_manhattan_test);
817 dist_test = dist_px_manhattan_test;
818 }
819
820 if (index) {
821 efa = (BMFace *)edbm_select_id_bm_elem_get(bases, index, base_index);
822 }
823 else {
824 efa = nullptr;
825 }
826 }
827
828 if (r_efa_zbuf) {
829 *r_efa_zbuf = efa;
830 }
831
832 /* Exception for faces (verts don't need this). */
833 if (r_dist_center && efa) {
835
836 data.mval_fl[0] = vc->mval[0];
837 data.mval_fl[1] = vc->mval[1];
838 data.dist_px_manhattan = FLT_MAX;
839 data.face_test = efa;
840
842
845
846 *r_dist_center = data.dist_px_manhattan;
847 }
848 /* End exception. */
849
850 if (efa) {
851 if (dist_test < *dist_px_manhattan_p) {
852 if (r_base_index) {
853 *r_base_index = base_index;
854 }
855 *dist_px_manhattan_p = dist_test;
856 return efa;
857 }
858 }
859 return nullptr;
860 }
861
863 const NearestFaceUserData_Hit *hit = nullptr;
865 BMesh *prev_select_bm = nullptr;
866
867 static struct {
868 int index;
869 const BMFace *elem;
870 const BMesh *bm;
871 } prev_select = {0};
872
873 data.mval_fl[0] = vc->mval[0];
874 data.mval_fl[1] = vc->mval[1];
875 data.use_select_bias = use_select_bias;
876 data.use_cycle = use_cycle;
877
878 for (; base_index < bases.size(); base_index++) {
879 Base *base_iter = bases[base_index];
881 if (use_cycle && prev_select.bm == vc->em->bm &&
882 prev_select.elem == BM_face_at_index_find_or_table(vc->em->bm, prev_select.index))
883 {
884 data.cycle_index_prev = prev_select.index;
885 /* No need to compare in the rest of the loop. */
886 use_cycle = false;
887 }
888 else {
889 data.cycle_index_prev = 0;
890 }
891
892 data.hit.dist = data.hit_cycle.dist = data.hit.dist_bias = data.hit_cycle.dist_bias =
893 *dist_px_manhattan_p;
894
897
898 hit = (data.use_cycle && data.hit_cycle.face) ? &data.hit_cycle : &data.hit;
899
900 if (hit->dist < *dist_px_manhattan_p) {
901 if (r_base_index) {
902 *r_base_index = base_index;
903 }
904 *dist_px_manhattan_p = hit->dist;
905 prev_select_bm = vc->em->bm;
906 }
907 }
908
909 if (hit == nullptr) {
910 return nullptr;
911 }
912
913 if (r_dist_center) {
914 *r_dist_center = hit->dist;
915 }
916
917 prev_select.index = hit->index;
918 prev_select.elem = hit->face;
919 prev_select.bm = prev_select_bm;
920
921 return hit->face;
922}
923
924BMFace *EDBM_face_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
925{
929 vc, dist_px_manhattan_p, nullptr, false, false, false, nullptr, {base}, nullptr);
930}
931
932#undef FIND_NEAR_SELECT_BIAS
933#undef FIND_NEAR_CYCLE_THRESHOLD_MIN
934
942 const Span<Base *> bases,
943 int *r_base_index,
944 BMVert **r_eve,
945 BMEdge **r_eed,
946 BMFace **r_efa)
947{
948 BMEditMesh *em = vc->em;
949
950 const bool use_cycle = !WM_cursor_test_motion_and_update(vc->mval);
951 const float dist_init = ED_view3d_select_dist_px();
952 /* Since edges select lines, we give dots advantage of ~20 pix. */
953 const float dist_margin = (dist_init / 2);
954 float dist = dist_init;
955
956 struct {
957 struct {
958 BMVert *ele;
959 int base_index;
960 } v;
961 struct {
962 BMEdge *ele;
963 int base_index;
964 } e, e_zbuf;
965 struct {
966 BMFace *ele;
967 int base_index;
968 } f, f_zbuf;
969 } hit = {{nullptr}};
970
971 /* No after-queue (yet), so we check it now, otherwise the em_xxxofs indices are bad. */
972
973 if ((dist > 0.0f) && (em->selectmode & SCE_SELECT_FACE)) {
974 float dist_center = 0.0f;
975 float *dist_center_p = (em->selectmode & (SCE_SELECT_EDGE | SCE_SELECT_VERTEX)) ?
976 &dist_center :
977 nullptr;
978
979 uint base_index = 0;
980 BMFace *efa_zbuf = nullptr;
982 vc, &dist, dist_center_p, true, true, use_cycle, &efa_zbuf, bases, &base_index);
983
984 if (efa_test && dist_center_p) {
985 dist = min_ff(dist_margin, dist_center);
986 }
987 if (efa_test) {
988 hit.f.base_index = base_index;
989 hit.f.ele = efa_test;
990 }
991 if (efa_zbuf) {
992 hit.f_zbuf.base_index = base_index;
993 hit.f_zbuf.ele = efa_zbuf;
994 }
995 }
996
997 if ((dist > 0.0f) && (em->selectmode & SCE_SELECT_EDGE)) {
998 float dist_center = 0.0f;
999 float *dist_center_p = (em->selectmode & SCE_SELECT_VERTEX) ? &dist_center : nullptr;
1000
1001 uint base_index = 0;
1002 BMEdge *eed_zbuf = nullptr;
1004 vc, &dist, dist_center_p, true, use_cycle, &eed_zbuf, bases, &base_index);
1005
1006 if (eed_test && dist_center_p) {
1007 dist = min_ff(dist_margin, dist_center);
1008 }
1009 if (eed_test) {
1010 hit.e.base_index = base_index;
1011 hit.e.ele = eed_test;
1012 }
1013 if (eed_zbuf) {
1014 hit.e_zbuf.base_index = base_index;
1015 hit.e_zbuf.ele = eed_zbuf;
1016 }
1017 }
1018
1019 if ((dist > 0.0f) && (em->selectmode & SCE_SELECT_VERTEX)) {
1020 uint base_index = 0;
1021 BMVert *eve_test = EDBM_vert_find_nearest_ex(vc, &dist, true, use_cycle, bases, &base_index);
1022
1023 if (eve_test) {
1024 hit.v.base_index = base_index;
1025 hit.v.ele = eve_test;
1026 }
1027 }
1028
1029 /* Return only one of 3 pointers, for front-buffer redraws. */
1030 if (hit.v.ele) {
1031 hit.f.ele = nullptr;
1032 hit.e.ele = nullptr;
1033 }
1034 else if (hit.e.ele) {
1035 hit.f.ele = nullptr;
1036 }
1037
1038 /* There may be a face under the cursor, who's center if too far away
1039 * use this if all else fails, it makes sense to select this. */
1040 if ((hit.v.ele || hit.e.ele || hit.f.ele) == 0) {
1041 if (hit.e_zbuf.ele) {
1042 hit.e.base_index = hit.e_zbuf.base_index;
1043 hit.e.ele = hit.e_zbuf.ele;
1044 }
1045 else if (hit.f_zbuf.ele) {
1046 hit.f.base_index = hit.f_zbuf.base_index;
1047 hit.f.ele = hit.f_zbuf.ele;
1048 }
1049 }
1050
1051 /* Only one element type will be non-null. */
1052 BLI_assert(((hit.v.ele != nullptr) + (hit.e.ele != nullptr) + (hit.f.ele != nullptr)) <= 1);
1053
1054 if (hit.v.ele) {
1055 *r_base_index = hit.v.base_index;
1056 }
1057 if (hit.e.ele) {
1058 *r_base_index = hit.e.base_index;
1059 }
1060 if (hit.f.ele) {
1061 *r_base_index = hit.f.base_index;
1062 }
1063
1064 *r_eve = hit.v.ele;
1065 *r_eed = hit.e.ele;
1066 *r_efa = hit.f.ele;
1067
1068 return (hit.v.ele || hit.e.ele || hit.f.ele);
1069}
1070
1071#undef FAKE_SELECT_MODE_BEGIN
1072#undef FAKE_SELECT_MODE_END
1073
1075 const Span<Base *> bases,
1076 int *r_base_index,
1077 BMVert **r_eve,
1078 BMEdge **r_eed,
1079 BMFace **r_efa)
1080{
1081 return unified_findnearest(vc, bases, r_base_index, r_eve, r_eed, r_efa);
1082}
1083
1085
1086/* -------------------------------------------------------------------- */
1092
1094 const Span<Base *> bases,
1095 bool use_boundary_vertices,
1096 bool use_boundary_edges,
1097 int *r_base_index_vert,
1098 int *r_base_index_edge,
1099 int *r_base_index_face,
1100 BMVert **r_eve,
1101 BMEdge **r_eed,
1102 BMFace **r_efa)
1103{
1104 const float mval_fl[2] = {float(vc->mval[0]), float(vc->mval[1])};
1105 float ray_origin[3], ray_direction[3];
1106
1107 struct {
1108 uint base_index;
1109 BMElem *ele;
1110 } best = {0, nullptr};
1111 /* Currently unused, keep since we may want to pick the best. */
1112 UNUSED_VARS(best);
1113
1114 struct {
1115 uint base_index;
1116 BMElem *ele;
1117 } best_vert = {0, nullptr};
1118
1119 struct {
1120 uint base_index;
1121 BMElem *ele;
1122 } best_edge = {0, nullptr};
1123
1124 struct {
1125 uint base_index;
1126 BMElem *ele;
1127 } best_face = {0, nullptr};
1128
1130 vc->depsgraph, vc->region, vc->v3d, mval_fl, ray_origin, ray_direction, true))
1131 {
1132 float dist_sq_best = FLT_MAX;
1133 float dist_sq_best_vert = FLT_MAX;
1134 float dist_sq_best_edge = FLT_MAX;
1135 float dist_sq_best_face = FLT_MAX;
1136
1137 const bool use_vert = (r_eve != nullptr);
1138 const bool use_edge = (r_eed != nullptr);
1139 const bool use_face = (r_efa != nullptr);
1140
1141 for (const int base_index : bases.index_range()) {
1142 Base *base_iter = bases[base_index];
1143 Object *obedit = base_iter->object;
1144
1146 BMesh *bm = em->bm;
1147 float imat3[3][3];
1148
1150 copy_m3_m4(imat3, obedit->object_to_world().ptr());
1151 invert_m3(imat3);
1152
1153 Span<float3> vert_positions;
1154 {
1155 const Object *obedit_eval = DEG_get_evaluated(vc->depsgraph, obedit);
1156 const Mesh *mesh_eval = BKE_object_get_editmesh_eval_cage(obedit_eval);
1157 if (BKE_mesh_wrapper_vert_len(mesh_eval) == bm->totvert) {
1158 vert_positions = BKE_mesh_wrapper_vert_coords(mesh_eval);
1159 }
1160 }
1161
1162 if (!vert_positions.is_empty()) {
1164 }
1165
1166 if ((use_boundary_vertices || use_boundary_edges) && (use_vert || use_edge)) {
1167 BMEdge *e;
1168 BMIter eiter;
1169 BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
1171 continue;
1172 }
1173
1174 if (BM_edge_is_boundary(e)) {
1175 if (use_vert && use_boundary_vertices) {
1176 for (uint j = 0; j < 2; j++) {
1177 BMVert *v = *((&e->v1) + j);
1178 float point[3];
1179 mul_v3_m4v3(point,
1180 obedit->object_to_world().ptr(),
1181 !vert_positions.is_empty() ? vert_positions[BM_elem_index_get(v)] :
1182 v->co);
1183 const float dist_sq_test = dist_squared_to_ray_v3_normalized(
1184 ray_origin, ray_direction, point);
1185 if (dist_sq_test < dist_sq_best_vert) {
1186 dist_sq_best_vert = dist_sq_test;
1187 best_vert.base_index = base_index;
1188 best_vert.ele = (BMElem *)v;
1189 }
1190 if (dist_sq_test < dist_sq_best) {
1191 dist_sq_best = dist_sq_test;
1192 best.base_index = base_index;
1193 best.ele = (BMElem *)v;
1194 }
1195 }
1196 }
1197
1198 if (use_edge && use_boundary_edges) {
1199 float point[3];
1200#if 0
1201 const float dist_sq_test = dist_squared_ray_to_seg_v3(
1202 ray_origin, ray_direction, e->v1->co, e->v2->co, point, &depth);
1203#else
1204 if (!vert_positions.is_empty()) {
1205 mid_v3_v3v3(point,
1206 vert_positions[BM_elem_index_get(e->v1)],
1207 vert_positions[BM_elem_index_get(e->v2)]);
1208 }
1209 else {
1210 mid_v3_v3v3(point, e->v1->co, e->v2->co);
1211 }
1212 mul_m4_v3(obedit->object_to_world().ptr(), point);
1213 const float dist_sq_test = dist_squared_to_ray_v3_normalized(
1214 ray_origin, ray_direction, point);
1215 if (dist_sq_test < dist_sq_best_edge) {
1216 dist_sq_best_edge = dist_sq_test;
1217 best_edge.base_index = base_index;
1218 best_edge.ele = (BMElem *)e;
1219 }
1220 if (dist_sq_test < dist_sq_best) {
1221 dist_sq_best = dist_sq_test;
1222 best.base_index = base_index;
1223 best.ele = (BMElem *)e;
1224 }
1225#endif
1226 }
1227 }
1228 }
1229 }
1230 /* Non boundary case. */
1231 if (use_vert && !use_boundary_vertices) {
1232 BMVert *v;
1233 BMIter viter;
1234 BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
1236 continue;
1237 }
1238
1239 float point[3];
1240 mul_v3_m4v3(point,
1241 obedit->object_to_world().ptr(),
1242 !vert_positions.is_empty() ? vert_positions[BM_elem_index_get(v)] : v->co);
1243 const float dist_sq_test = dist_squared_to_ray_v3_normalized(
1244 ray_origin, ray_direction, point);
1245 if (dist_sq_test < dist_sq_best_vert) {
1246 dist_sq_best_vert = dist_sq_test;
1247 best_vert.base_index = base_index;
1248 best_vert.ele = (BMElem *)v;
1249 }
1250 if (dist_sq_test < dist_sq_best) {
1251 dist_sq_best = dist_sq_test;
1252 best.base_index = base_index;
1253 best.ele = (BMElem *)v;
1254 }
1255 }
1256 }
1257
1258 if (use_edge && !use_boundary_edges) {
1259 BMEdge *e;
1260 BMIter eiter;
1261 BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
1263 continue;
1264 }
1265
1266 float point[3];
1267 if (!vert_positions.is_empty()) {
1268 mid_v3_v3v3(point,
1269 vert_positions[BM_elem_index_get(e->v1)],
1270 vert_positions[BM_elem_index_get(e->v2)]);
1271 }
1272 else {
1273 mid_v3_v3v3(point, e->v1->co, e->v2->co);
1274 }
1275 mul_m4_v3(obedit->object_to_world().ptr(), point);
1276 const float dist_sq_test = dist_squared_to_ray_v3_normalized(
1277 ray_origin, ray_direction, point);
1278 if (dist_sq_test < dist_sq_best_edge) {
1279 dist_sq_best_edge = dist_sq_test;
1280 best_edge.base_index = base_index;
1281 best_edge.ele = (BMElem *)e;
1282 }
1283 if (dist_sq_test < dist_sq_best) {
1284 dist_sq_best = dist_sq_test;
1285 best.base_index = base_index;
1286 best.ele = (BMElem *)e;
1287 }
1288 }
1289 }
1290
1291 if (use_face) {
1292 BMFace *f;
1293 BMIter fiter;
1294 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
1296 continue;
1297 }
1298
1299 float point[3];
1300 if (!vert_positions.is_empty()) {
1301 BM_face_calc_center_median_vcos(bm, f, point, vert_positions);
1302 }
1303 else {
1305 }
1306 mul_m4_v3(obedit->object_to_world().ptr(), point);
1307 const float dist_sq_test = dist_squared_to_ray_v3_normalized(
1308 ray_origin, ray_direction, point);
1309 if (dist_sq_test < dist_sq_best_face) {
1310 dist_sq_best_face = dist_sq_test;
1311 best_face.base_index = base_index;
1312 best_face.ele = (BMElem *)f;
1313 }
1314 if (dist_sq_test < dist_sq_best) {
1315 dist_sq_best = dist_sq_test;
1316 best.base_index = base_index;
1317 best.ele = (BMElem *)f;
1318 }
1319 }
1320 }
1321 }
1322 }
1323
1324 *r_base_index_vert = best_vert.base_index;
1325 *r_base_index_edge = best_edge.base_index;
1326 *r_base_index_face = best_face.base_index;
1327
1328 if (r_eve) {
1329 *r_eve = nullptr;
1330 }
1331 if (r_eed) {
1332 *r_eed = nullptr;
1333 }
1334 if (r_efa) {
1335 *r_efa = nullptr;
1336 }
1337
1338 if (best_vert.ele) {
1339 *r_eve = (BMVert *)best_vert.ele;
1340 }
1341 if (best_edge.ele) {
1342 *r_eed = (BMEdge *)best_edge.ele;
1343 }
1344 if (best_face.ele) {
1345 *r_efa = (BMFace *)best_face.ele;
1346 }
1347
1348 return (best_vert.ele != nullptr || best_edge.ele != nullptr || best_face.ele != nullptr);
1349}
1350
1352
1353/* -------------------------------------------------------------------- */
1356
1358{
1359 Object *obedit = CTX_data_edit_object(C);
1361 BMesh *bm = em->bm;
1362 bool changed = false;
1363
1364 /* Group variables. */
1365 int(*group_index)[2];
1366 int group_tot;
1367 int i;
1368
1369 if (bm->totfacesel < 2) {
1370 BKE_report(op->reports, RPT_ERROR, "No face regions selected");
1371 return OPERATOR_CANCELLED;
1372 }
1373
1374 int *groups_array = MEM_malloc_arrayN<int>(bm->totfacesel, __func__);
1375 group_tot = BM_mesh_calc_face_groups(
1376 bm, groups_array, &group_index, nullptr, nullptr, nullptr, BM_ELEM_SELECT, BM_VERT);
1377
1379
1380 for (i = 0; i < group_tot; i++) {
1381 ListBase faces_regions;
1382 int tot;
1383
1384 const int fg_sta = group_index[i][0];
1385 const int fg_len = group_index[i][1];
1386 int j;
1387 BMFace **fg = MEM_malloc_arrayN<BMFace *>(fg_len, __func__);
1388
1389 for (j = 0; j < fg_len; j++) {
1390 fg[j] = BM_face_at_index(bm, groups_array[fg_sta + j]);
1391 }
1392
1393 tot = BM_mesh_region_match(bm, fg, fg_len, &faces_regions);
1394
1395 MEM_freeN(fg);
1396
1397 if (tot) {
1398 while (LinkData *link = static_cast<LinkData *>(BLI_pophead(&faces_regions))) {
1399 BMFace **faces = static_cast<BMFace **>(link->data);
1400 while (BMFace *f = *(faces++)) {
1401 BM_face_select_set(bm, f, true);
1402 }
1403 MEM_freeN(link->data);
1404 MEM_freeN(link);
1405
1406 changed = true;
1407 }
1408 }
1409 }
1410
1411 MEM_freeN(groups_array);
1412 MEM_freeN(group_index);
1413
1414 if (changed) {
1415 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
1417 }
1418 else {
1419 BKE_report(op->reports, RPT_WARNING, "No matching face regions found");
1420 }
1421
1422 return OPERATOR_FINISHED;
1423}
1424
1426{
1427 /* Identifiers. */
1428 ot->name = "Select Similar Regions";
1429 ot->idname = "MESH_OT_select_similar_region";
1430 ot->description = "Select similar face regions to the current selection";
1431
1432 /* API callbacks. */
1434 ot->poll = ED_operator_editmesh;
1435
1436 /* Flags. */
1437 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1438}
1439
1441
1442/* -------------------------------------------------------------------- */
1445
1447{
1448 const int type = RNA_enum_get(op->ptr, "type");
1449 const int action = RNA_enum_get(op->ptr, "action");
1450 const bool use_extend = RNA_boolean_get(op->ptr, "use_extend");
1451 const bool use_expand = RNA_boolean_get(op->ptr, "use_expand");
1452
1453 if (EDBM_selectmode_toggle_multi(C, type, action, use_extend, use_expand)) {
1454 return OPERATOR_FINISHED;
1455 }
1456 return OPERATOR_CANCELLED;
1457}
1458
1460{
1461 /* Bypass when in UV non sync-select mode, fall through to keymap that edits. */
1462 if (CTX_wm_space_image(C)) {
1464 if ((ts->uv_flag & UV_SYNC_SELECTION) == 0) {
1465 return OPERATOR_PASS_THROUGH;
1466 }
1467 /* Bypass when no action is needed. */
1468 if (!RNA_struct_property_is_set(op->ptr, "type")) {
1469 return OPERATOR_CANCELLED;
1470 }
1471 }
1472
1473 /* Detecting these options based on shift/control here is weak, but it's done
1474 * to make this work when clicking buttons or menus. */
1475 if (!RNA_struct_property_is_set(op->ptr, "use_extend")) {
1476 RNA_boolean_set(op->ptr, "use_extend", event->modifier & KM_SHIFT);
1477 }
1478 if (!RNA_struct_property_is_set(op->ptr, "use_expand")) {
1479 RNA_boolean_set(op->ptr, "use_expand", event->modifier & KM_CTRL);
1480 }
1481
1482 return edbm_select_mode_exec(C, op);
1483}
1484
1486 wmOperatorType * /*ot*/,
1487 PointerRNA *ptr)
1488{
1489 const int type = RNA_enum_get(ptr, "type");
1490
1491 /* Because the special behavior for shift and ctrl click depend on user input, they may be
1492 * incorrect if the operator is used from a script or from a special button. So only return the
1493 * specialized descriptions if only the "type" is set, which conveys that the operator is meant
1494 * to be used with the logic in the `invoke` method. */
1495 if (RNA_struct_property_is_set(ptr, "type") && !RNA_struct_property_is_set(ptr, "use_extend") &&
1496 !RNA_struct_property_is_set(ptr, "use_expand") && !RNA_struct_property_is_set(ptr, "action"))
1497 {
1498 switch (type) {
1499 case SCE_SELECT_VERTEX:
1500 return TIP_(
1501 "Vertex select - Shift-Click for multiple modes, Ctrl-Click contracts selection");
1502 case SCE_SELECT_EDGE:
1503 return TIP_(
1504 "Edge select - Shift-Click for multiple modes, "
1505 "Ctrl-Click expands/contracts selection depending on the current mode");
1506 case SCE_SELECT_FACE:
1507 return TIP_("Face select - Shift-Click for multiple modes, Ctrl-Click expands selection");
1508 }
1509 }
1510
1511 return "";
1512}
1513
1515{
1516 PropertyRNA *prop;
1517
1518 static const EnumPropertyItem actions_items[] = {
1519 {0, "DISABLE", false, "Disable", "Disable selected markers"},
1520 {1, "ENABLE", false, "Enable", "Enable selected markers"},
1521 {2, "TOGGLE", false, "Toggle", "Toggle disabled flag for selected markers"},
1522 {0, nullptr, 0, nullptr, nullptr},
1523 };
1524
1525 /* Identifiers. */
1526 ot->name = "Select Mode";
1527 ot->idname = "MESH_OT_select_mode";
1528 ot->description = "Change selection mode";
1529
1530 /* API callbacks. */
1531 ot->invoke = edbm_select_mode_invoke;
1532 ot->exec = edbm_select_mode_exec;
1533 ot->poll = ED_operator_editmesh;
1534 ot->get_description = edbm_select_mode_get_description;
1535
1536 /* Flags. */
1537 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1538
1539 /* Properties. */
1540 /* Hide all, not to show redo panel. */
1541 prop = RNA_def_boolean(ot->srna, "use_extend", false, "Extend", "");
1543 prop = RNA_def_boolean(ot->srna, "use_expand", false, "Expand", "");
1545 ot->prop = prop = RNA_def_enum(ot->srna, "type", rna_enum_mesh_select_mode_items, 0, "Type", "");
1547
1548 prop = RNA_def_enum(
1549 ot->srna, "action", actions_items, 2, "Action", "Selection action to execute");
1551}
1552
1554
1555/* -------------------------------------------------------------------- */
1558
1560 int walkercode,
1561 void *start,
1562 int r_count_by_select[2])
1563{
1564 BMesh *bm = em->bm;
1565 BMElem *ele;
1566 BMWalker walker;
1567
1568 r_count_by_select[0] = r_count_by_select[1] = 0;
1569
1570 BMW_init(&walker,
1571 bm,
1572 walkercode,
1577 BMW_NIL_LAY);
1578
1579 for (ele = static_cast<BMElem *>(BMW_begin(&walker, start)); ele;
1580 ele = static_cast<BMElem *>(BMW_step(&walker)))
1581 {
1582 r_count_by_select[BM_elem_flag_test(ele, BM_ELEM_SELECT) ? 1 : 0] += 1;
1583
1584 /* Early exit when mixed (could be optional if needed. */
1585 if (r_count_by_select[0] && r_count_by_select[1]) {
1586 r_count_by_select[0] = r_count_by_select[1] = -1;
1587 break;
1588 }
1589 }
1590
1591 BMW_end(&walker);
1592}
1593
1594static bool walker_select(BMEditMesh *em, int walkercode, void *start, const bool select)
1595{
1596 BMesh *bm = em->bm;
1597 BMElem *ele;
1598 BMWalker walker;
1599 bool changed = false;
1600
1601 BMW_init(&walker,
1602 bm,
1603 walkercode,
1608 BMW_NIL_LAY);
1609
1610 for (ele = static_cast<BMElem *>(BMW_begin(&walker, start)); ele;
1611 ele = static_cast<BMElem *>(BMW_step(&walker)))
1612 {
1613 if (!select) {
1615 }
1617 changed = true;
1618 }
1619 BMW_end(&walker);
1620 return changed;
1621}
1622
1624{
1625 const bool is_ring = RNA_boolean_get(op->ptr, "ring");
1626 const Scene *scene = CTX_data_scene(C);
1627 ViewLayer *view_layer = CTX_data_view_layer(C);
1629 scene, view_layer, CTX_wm_view3d(C));
1630 for (Object *obedit : objects) {
1632
1633 if (em->bm->totedgesel == 0) {
1634 continue;
1635 }
1636
1637 BMEdge *eed;
1638 int edindex;
1639 BMIter iter;
1640 int totedgesel = 0;
1641
1642 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
1644 totedgesel++;
1645 }
1646 }
1647
1648 BMEdge **edarray = MEM_malloc_arrayN<BMEdge *>(totedgesel, "edge array");
1649 edindex = 0;
1650
1651 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
1653 edarray[edindex] = eed;
1654 edindex++;
1655 }
1656 }
1657
1658 bool changed = false;
1659 if (is_ring) {
1660 for (edindex = 0; edindex < totedgesel; edindex += 1) {
1661 eed = edarray[edindex];
1662 changed |= walker_select(em, BMW_EDGERING, eed, true);
1663 }
1664 if (changed) {
1666 }
1667 }
1668 else {
1669 for (edindex = 0; edindex < totedgesel; edindex += 1) {
1670 eed = edarray[edindex];
1671 bool non_manifold = BM_edge_face_count_is_over(eed, 2);
1672 if (non_manifold) {
1673 changed |= walker_select(em, BMW_EDGELOOP_NONMANIFOLD, eed, true);
1674 }
1675 else {
1676 changed |= walker_select(em, BMW_EDGELOOP, eed, true);
1677 }
1678 }
1679 if (changed) {
1681 }
1682 }
1683 MEM_freeN(edarray);
1684
1685 if (changed) {
1686 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
1687 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
1688 }
1689 }
1690
1691 return OPERATOR_FINISHED;
1692}
1693
1695{
1696 /* Identifiers. */
1697 ot->name = "Multi Select Loops";
1698 ot->idname = "MESH_OT_loop_multi_select";
1699 ot->description = "Select a loop of connected edges by connection type";
1700
1701 /* API callbacks. */
1703 ot->poll = ED_operator_editmesh;
1704
1705 /* Flags. */
1706 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1707
1708 /* Properties. */
1709 RNA_def_boolean(ot->srna, "ring", false, "Ring", "");
1710}
1711
1713
1714/* -------------------------------------------------------------------- */
1717
1718static void mouse_mesh_loop_face(BMEditMesh *em, BMEdge *eed, bool select, bool select_clear)
1719{
1720 if (select_clear) {
1722 }
1723
1725}
1726
1727static void mouse_mesh_loop_edge_ring(BMEditMesh *em, BMEdge *eed, bool select, bool select_clear)
1728{
1729 if (select_clear) {
1731 }
1732
1734}
1735
1737 BMEditMesh *em, BMEdge *eed, bool select, bool select_clear, bool select_cycle)
1738{
1739 bool edge_boundary = false;
1740 bool non_manifold = BM_edge_face_count_is_over(eed, 2);
1741
1742 /* Cycle between BMW_EDGELOOP / BMW_EDGEBOUNDARY. */
1743 if (select_cycle && BM_edge_is_boundary(eed)) {
1744 int count_by_select[2];
1745
1746 /* If the loops selected toggle the boundaries. */
1747 walker_select_count(em, BMW_EDGELOOP, eed, count_by_select);
1748 if (count_by_select[!select] == 0) {
1749 edge_boundary = true;
1750
1751 /* If the boundaries selected, toggle back to the loop. */
1752 walker_select_count(em, BMW_EDGEBOUNDARY, eed, count_by_select);
1753 if (count_by_select[!select] == 0) {
1754 edge_boundary = false;
1755 }
1756 }
1757 }
1758
1759 if (select_clear) {
1761 }
1762
1763 if (edge_boundary) {
1765 }
1766 else if (non_manifold) {
1768 }
1769 else {
1771 }
1772}
1773
1775 bContext *C, const int mval[2], bool extend, bool deselect, bool toggle, bool ring)
1776{
1777 Base *basact = nullptr;
1778 BMVert *eve = nullptr;
1779 BMEdge *eed = nullptr;
1780 BMFace *efa = nullptr;
1781
1782 BMEditMesh *em;
1783 bool select = true;
1784 bool select_clear = false;
1785 bool select_cycle = true;
1786 float mvalf[2];
1787
1789 mvalf[0] = float(vc.mval[0] = mval[0]);
1790 mvalf[1] = float(vc.mval[1] = mval[1]);
1791
1792 BMEditMesh *em_original = vc.em;
1793 const short selectmode = em_original->selectmode;
1794 em_original->selectmode = SCE_SELECT_EDGE;
1795
1797 vc.scene, vc.view_layer, vc.v3d);
1798
1799 {
1800 int base_index = -1;
1801 if (EDBM_unified_findnearest(&vc, bases, &base_index, &eve, &eed, &efa)) {
1802 basact = bases[base_index];
1804 em = vc.em;
1805 }
1806 else {
1807 em = nullptr;
1808 }
1809 }
1810
1811 em_original->selectmode = selectmode;
1812
1813 if (em == nullptr || eed == nullptr) {
1814 return false;
1815 }
1816
1817 if (extend == false && deselect == false && toggle == false) {
1818 select_clear = true;
1819 }
1820
1821 if (extend) {
1822 select = true;
1823 }
1824 else if (deselect) {
1825 select = false;
1826 }
1827 else if (select_clear || (BM_elem_flag_test(eed, BM_ELEM_SELECT) == 0)) {
1828 select = true;
1829 }
1830 else if (toggle) {
1831 select = false;
1832 select_cycle = false;
1833 }
1834
1835 if (select_clear) {
1836 for (Base *base_iter : bases) {
1837 Object *ob_iter = base_iter->object;
1838 BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter);
1839
1840 if (em_iter->bm->totvertsel == 0) {
1841 continue;
1842 }
1843
1844 if (em_iter == em) {
1845 continue;
1846 }
1847
1849 DEG_id_tag_update(static_cast<ID *>(ob_iter->data), ID_RECALC_SELECT);
1850 }
1851 }
1852
1853 if (em->selectmode & SCE_SELECT_FACE) {
1854 mouse_mesh_loop_face(em, eed, select, select_clear);
1855 }
1856 else {
1857 if (ring) {
1858 mouse_mesh_loop_edge_ring(em, eed, select, select_clear);
1859 }
1860 else {
1861 mouse_mesh_loop_edge(em, eed, select, select_clear, select_cycle);
1862 }
1863 }
1864
1866
1867 /* Sets as active, useful for other tools. */
1868 if (select) {
1869 if (em->selectmode & SCE_SELECT_VERTEX) {
1870 /* Find nearest vert from mouse
1871 * (initialize to large values in case only one vertex can be projected). */
1872 float v1_co[2], v2_co[2];
1873 float length_1 = FLT_MAX;
1874 float length_2 = FLT_MAX;
1875
1876 /* We can't be sure this has already been set... */
1878
1881 {
1882 length_1 = len_squared_v2v2(mvalf, v1_co);
1883 }
1884
1887 {
1888 length_2 = len_squared_v2v2(mvalf, v2_co);
1889 }
1890#if 0
1891 printf("mouse to v1: %f\nmouse to v2: %f\n",
1892 len_squared_v2v2(mvalf, v1_co),
1893 len_squared_v2v2(mvalf, v2_co));
1894#endif
1895 BM_select_history_store(em->bm, (length_1 < length_2) ? eed->v1 : eed->v2);
1896 }
1897 else if (em->selectmode & SCE_SELECT_EDGE) {
1898 BM_select_history_store(em->bm, eed);
1899 }
1900 else if (em->selectmode & SCE_SELECT_FACE) {
1901 /* Select the face of eed which is the nearest of mouse. */
1902 BMFace *f;
1903 BMIter iterf;
1904 float best_dist = FLT_MAX;
1905 efa = nullptr;
1906
1907 /* We can't be sure this has already been set... */
1909
1910 BM_ITER_ELEM (f, &iterf, eed, BM_FACES_OF_EDGE) {
1912 float cent[3];
1913 float co[2], tdist;
1914
1918 {
1919 tdist = len_squared_v2v2(mvalf, co);
1920 if (tdist < best_dist) {
1921 // printf("Best face: %p (%f)\n", f, tdist);
1922 best_dist = tdist;
1923 efa = f;
1924 }
1925 }
1926 }
1927 }
1928 if (efa) {
1929 BM_mesh_active_face_set(em->bm, efa);
1930 BM_select_history_store(em->bm, efa);
1931 }
1932 }
1933 }
1934
1935 DEG_id_tag_update(static_cast<ID *>(vc.obedit->data), ID_RECALC_SELECT);
1937
1938 return true;
1939}
1940
1942{
1943
1945
1946 if (mouse_mesh_loop(C,
1947 event->mval,
1948 RNA_boolean_get(op->ptr, "extend"),
1949 RNA_boolean_get(op->ptr, "deselect"),
1950 RNA_boolean_get(op->ptr, "toggle"),
1951 RNA_boolean_get(op->ptr, "ring")))
1952 {
1953 return OPERATOR_FINISHED;
1954 }
1955 return OPERATOR_CANCELLED;
1956}
1957
1959{
1960 /* Identifiers. */
1961 ot->name = "Loop Select";
1962 ot->idname = "MESH_OT_loop_select";
1963 ot->description = "Select a loop of connected edges";
1964
1965 /* API callbacks. */
1966 ot->invoke = edbm_select_loop_invoke;
1968
1969 /* Flags. */
1970 ot->flag = OPTYPE_UNDO;
1971
1972 /* Properties. */
1973 PropertyRNA *prop;
1974
1975 prop = RNA_def_boolean(ot->srna, "extend", false, "Extend Select", "Extend the selection");
1977 prop = RNA_def_boolean(ot->srna, "deselect", false, "Deselect", "Remove from the selection");
1979 prop = RNA_def_boolean(ot->srna, "toggle", false, "Toggle Select", "Toggle the selection");
1981 prop = RNA_def_boolean(ot->srna, "ring", false, "Select Ring", "Select ring");
1983}
1984
1986{
1987 /* Description. */
1988 ot->name = "Edge Ring Select";
1989 ot->idname = "MESH_OT_edgering_select";
1990 ot->description = "Select an edge ring";
1991
1992 /* Callbacks. */
1993 ot->invoke = edbm_select_loop_invoke;
1995
1996 /* Flags. */
1997 ot->flag = OPTYPE_UNDO;
1998
1999 /* Properties. */
2000 PropertyRNA *prop;
2001 prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
2003 prop = RNA_def_boolean(ot->srna, "deselect", false, "Deselect", "Remove from the selection");
2005 prop = RNA_def_boolean(ot->srna, "toggle", false, "Toggle Select", "Toggle the selection");
2007 prop = RNA_def_boolean(ot->srna, "ring", true, "Select Ring", "Select ring");
2009}
2010
2012
2013/* -------------------------------------------------------------------- */
2016
2018{
2019 const Scene *scene = CTX_data_scene(C);
2020 ViewLayer *view_layer = CTX_data_view_layer(C);
2021 int action = RNA_enum_get(op->ptr, "action");
2022
2024 scene, view_layer, CTX_wm_view3d(C));
2025
2026 if (action == SEL_TOGGLE) {
2027 action = SEL_SELECT;
2028 for (Object *obedit : objects) {
2030 if (em->bm->totvertsel || em->bm->totedgesel || em->bm->totfacesel) {
2031 action = SEL_DESELECT;
2032 break;
2033 }
2034 }
2035 }
2036
2037 for (Object *obedit : objects) {
2039 switch (action) {
2040 case SEL_SELECT:
2042 break;
2043 case SEL_DESELECT:
2045 break;
2046 case SEL_INVERT:
2047 EDBM_select_swap(em);
2049 break;
2050 }
2051 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
2052 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2053 }
2054
2055 return OPERATOR_FINISHED;
2056}
2057
2059{
2060 /* Identifiers. */
2061 ot->name = "(De)select All";
2062 ot->idname = "MESH_OT_select_all";
2063 ot->description = "(De)select all vertices, edges or faces";
2064
2065 /* API callbacks. */
2066 ot->exec = edbm_select_all_exec;
2067 ot->poll = ED_operator_editmesh;
2068
2069 /* Flags. */
2070 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2071
2073}
2074
2076
2077/* -------------------------------------------------------------------- */
2080
2082{
2083 const Scene *scene = CTX_data_scene(C);
2084 ViewLayer *view_layer = CTX_data_view_layer(C);
2086 scene, view_layer, CTX_wm_view3d(C));
2087
2088 for (Object *obedit : objects) {
2090
2091 if (!EDBM_select_interior_faces(em)) {
2092 continue;
2093 }
2094
2095 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
2096 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2097 }
2098
2099 return OPERATOR_FINISHED;
2100}
2101
2103{
2104 /* Identifiers. */
2105 ot->name = "Select Interior Faces";
2106 ot->idname = "MESH_OT_select_interior_faces";
2107 ot->description = "Select faces where all edges have more than 2 face users";
2108
2109 /* API callbacks. */
2111 ot->poll = ED_operator_editmesh;
2112
2113 /* Flags. */
2114 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2115}
2116
2118
2119/* -------------------------------------------------------------------- */
2125
2126bool EDBM_select_pick(bContext *C, const int mval[2], const SelectPick_Params &params)
2127{
2128 int base_index_active = -1;
2129 BMVert *eve = nullptr;
2130 BMEdge *eed = nullptr;
2131 BMFace *efa = nullptr;
2132
2133 /* Setup view context for argument to callbacks. */
2135 vc.mval[0] = mval[0];
2136 vc.mval[1] = mval[1];
2137
2139 vc.scene, vc.view_layer, vc.v3d);
2140
2141 bool changed = false;
2142 bool found = unified_findnearest(&vc, bases, &base_index_active, &eve, &eed, &efa);
2143
2144 if (params.sel_op == SEL_OP_SET) {
2145 BMElem *ele = efa ? (BMElem *)efa : (eed ? (BMElem *)eed : (BMElem *)eve);
2146 if ((found && params.select_passthrough) && BM_elem_flag_test(ele, BM_ELEM_SELECT)) {
2147 found = false;
2148 }
2149 else if (found || params.deselect_all) {
2150 /* Deselect everything. */
2151 for (Base *base_iter : bases) {
2152 Object *ob_iter = base_iter->object;
2154 DEG_id_tag_update(static_cast<ID *>(ob_iter->data), ID_RECALC_SELECT);
2156 }
2157 changed = true;
2158 }
2159 }
2160
2161 if (found) {
2162 Base *basact = bases[base_index_active];
2164 Object *obedit = vc.obedit;
2165 BMEditMesh *em = vc.em;
2166 BMesh *bm = em->bm;
2167
2168 if (efa) {
2169 switch (params.sel_op) {
2170 case SEL_OP_ADD: {
2172
2173 /* Work-around: deselect first, so we can guarantee it will
2174 * be active even if it was already selected. */
2176 BM_face_select_set(bm, efa, false);
2178 BM_face_select_set(bm, efa, true);
2179 break;
2180 }
2181 case SEL_OP_SUB: {
2183 BM_face_select_set(bm, efa, false);
2184 break;
2185 }
2186 case SEL_OP_XOR: {
2188 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
2190 BM_face_select_set(bm, efa, true);
2191 }
2192 else {
2194 BM_face_select_set(bm, efa, false);
2195 }
2196 break;
2197 }
2198 case SEL_OP_SET: {
2200 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
2202 BM_face_select_set(bm, efa, true);
2203 }
2204 break;
2205 }
2206 case SEL_OP_AND: {
2207 BLI_assert_unreachable(); /* Doesn't make sense for picking. */
2208 break;
2209 }
2210 }
2211 }
2212 else if (eed) {
2213
2214 switch (params.sel_op) {
2215 case SEL_OP_ADD: {
2216 /* Work-around: deselect first, so we can guarantee it will
2217 * be active even if it was already selected. */
2219 BM_edge_select_set(bm, eed, false);
2221 BM_edge_select_set(bm, eed, true);
2222 break;
2223 }
2224 case SEL_OP_SUB: {
2226 BM_edge_select_set(bm, eed, false);
2227 break;
2228 }
2229 case SEL_OP_XOR: {
2230 if (!BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
2232 BM_edge_select_set(bm, eed, true);
2233 }
2234 else {
2236 BM_edge_select_set(bm, eed, false);
2237 }
2238 break;
2239 }
2240 case SEL_OP_SET: {
2241 if (!BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
2243 BM_edge_select_set(bm, eed, true);
2244 }
2245 break;
2246 }
2247 case SEL_OP_AND: {
2248 BLI_assert_unreachable(); /* Doesn't make sense for picking. */
2249 break;
2250 }
2251 }
2252 }
2253 else if (eve) {
2254 switch (params.sel_op) {
2255 case SEL_OP_ADD: {
2256 /* Work-around: deselect first, so we can guarantee it will
2257 * be active even if it was already selected. */
2259 BM_vert_select_set(bm, eve, false);
2261 BM_vert_select_set(bm, eve, true);
2262 break;
2263 }
2264 case SEL_OP_SUB: {
2266 BM_vert_select_set(bm, eve, false);
2267 break;
2268 }
2269 case SEL_OP_XOR: {
2270 if (!BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
2272 BM_vert_select_set(bm, eve, true);
2273 }
2274 else {
2276 BM_vert_select_set(bm, eve, false);
2277 }
2278 break;
2279 }
2280 case SEL_OP_SET: {
2281 if (!BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
2283 BM_vert_select_set(bm, eve, true);
2284 }
2285 break;
2286 }
2287 case SEL_OP_AND: {
2288 BLI_assert_unreachable(); /* Doesn't make sense for picking. */
2289 break;
2290 }
2291 }
2292 }
2293
2295
2296 if (efa) {
2298 em->mat_nr = efa->mat_nr;
2299 }
2300
2301 /* Changing active object is handy since it allows us to
2302 * switch UV layers, vgroups for eg. */
2304 if (BKE_view_layer_active_base_get(vc.view_layer) != basact) {
2306 }
2307
2308 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
2310
2311 changed = true;
2312 }
2313
2314 return changed;
2315}
2316
2318
2319/* -------------------------------------------------------------------- */
2322
2324{
2325 BMEditSelection *ese, *nextese;
2326
2327 if (!(em->selectmode & SCE_SELECT_VERTEX)) {
2328 ese = static_cast<BMEditSelection *>(em->bm->selected.first);
2329 while (ese) {
2330 nextese = ese->next;
2331 if (ese->htype == BM_VERT) {
2332 BLI_freelinkN(&(em->bm->selected), ese);
2333 }
2334 ese = nextese;
2335 }
2336 }
2337 if (!(em->selectmode & SCE_SELECT_EDGE)) {
2338 ese = static_cast<BMEditSelection *>(em->bm->selected.first);
2339 while (ese) {
2340 nextese = ese->next;
2341 if (ese->htype == BM_EDGE) {
2342 BLI_freelinkN(&(em->bm->selected), ese);
2343 }
2344 ese = nextese;
2345 }
2346 }
2347 if (!(em->selectmode & SCE_SELECT_FACE)) {
2348 ese = static_cast<BMEditSelection *>(em->bm->selected.first);
2349 while (ese) {
2350 nextese = ese->next;
2351 if (ese->htype == BM_FACE) {
2352 BLI_freelinkN(&(em->bm->selected), ese);
2353 }
2354 ese = nextese;
2355 }
2356 }
2357}
2358
2359void EDBM_selectmode_set(BMEditMesh *em, const short selectmode)
2360{
2361 BMVert *eve;
2362 BMEdge *eed;
2363 BMFace *efa;
2364 BMIter iter;
2365
2366 em->selectmode = selectmode;
2367 em->bm->selectmode = selectmode;
2368
2369 /* Strip stored selection isn't relevant to the new mode. */
2371
2372 if (em->bm->totvertsel == 0 && em->bm->totedgesel == 0 && em->bm->totfacesel == 0) {
2373 return;
2374 }
2375
2376 if (em->selectmode & SCE_SELECT_VERTEX) {
2377 if (em->bm->totvertsel) {
2379 }
2380 }
2381 else if (em->selectmode & SCE_SELECT_EDGE) {
2382 /* Deselect vertices, and select again based on edge select. */
2383 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
2384 BM_vert_select_set(em->bm, eve, false);
2385 }
2386
2387 if (em->bm->totedgesel) {
2388 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
2390 BM_edge_select_set(em->bm, eed, true);
2391 }
2392 }
2393
2394 /* Selects faces based on edge status. */
2396 }
2397 }
2398 else if (em->selectmode & SCE_SELECT_FACE) {
2399 /* Deselect edges, and select again based on face select. */
2400 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
2401 BM_edge_select_set(em->bm, eed, false);
2402 }
2403
2404 if (em->bm->totfacesel) {
2405 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2407 BM_face_select_set(em->bm, efa, true);
2408 }
2409 }
2410 }
2411 }
2412}
2413
2415 const short selectmode_old,
2416 const short selectmode_new)
2417{
2418 /* NOTE: it's important only the selection modes passed in a re used,
2419 * not the meshes current selection mode because this is called when the
2420 * selection mode is being manipulated (see: #EDBM_selectmode_toggle_multi). */
2421
2422 BMesh *bm = em->bm;
2423
2424 BMVert *eve;
2425 BMEdge *eed;
2426 BMFace *efa;
2427 BMIter iter;
2428
2429 /* First tag-to-select, then select.
2430 * This avoids a feedback loop. */
2431
2432 /* Have to find out what the selection-mode was previously. */
2433 if (selectmode_old == SCE_SELECT_VERTEX) {
2434 if (bm->totvertsel == 0) {
2435 /* Pass. */
2436 }
2437 else if (selectmode_new == SCE_SELECT_EDGE) {
2438 /* Flush up (vert -> edge). */
2439
2440 /* Select all edges associated with every selected vert. */
2441 BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
2443 }
2444
2445 BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
2446 if (BM_elem_flag_test(eed, BM_ELEM_TAG)) {
2447 BM_edge_select_set(bm, eed, true);
2448 }
2449 }
2450 }
2451 else if (selectmode_new == SCE_SELECT_FACE) {
2452 /* Flush up (vert -> face). */
2453
2454 /* Select all faces associated with every selected vert. */
2455 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2457 }
2458
2459 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2460 if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
2461 BM_face_select_set(bm, efa, true);
2462 }
2463 }
2464 }
2465 }
2466 else if (selectmode_old == SCE_SELECT_EDGE) {
2467 if (bm->totedgesel == 0) {
2468 /* Pass. */
2469 }
2470 else if (selectmode_new == SCE_SELECT_FACE) {
2471 /* Flush up (edge -> face). */
2472
2473 /* Select all faces associated with every selected edge. */
2474 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2476 }
2477
2478 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2479 if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
2480 BM_face_select_set(bm, efa, true);
2481 }
2482 }
2483 }
2484 else if (selectmode_new == SCE_SELECT_VERTEX) {
2485 /* Flush down (edge -> vert). */
2486
2487 BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
2489 BM_vert_select_set(bm, eve, false);
2490 }
2491 }
2492 /* Deselect edges without both verts selected. */
2494 }
2495 }
2496 else if (selectmode_old == SCE_SELECT_FACE) {
2497 if (bm->totfacesel == 0) {
2498 /* Pass. */
2499 }
2500 else if (selectmode_new == SCE_SELECT_EDGE) {
2501 /* Flush down (face -> edge). */
2502
2503 BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
2505 BM_edge_select_set(bm, eed, false);
2506 }
2507 }
2508 /* Deselect faces without edges selected. */
2510 }
2511 else if (selectmode_new == SCE_SELECT_VERTEX) {
2512 /* Flush down (face -> vert). */
2513
2514 BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
2516 BM_vert_select_set(bm, eve, false);
2517 }
2518 }
2519 /* Deselect faces without verts selected. */
2521 }
2522 }
2523}
2524
2526 const short selectmode_toggle,
2527 const int action,
2528 const bool use_extend,
2529 const bool use_expand)
2530{
2532 Scene *scene = CTX_data_scene(C);
2533 ViewLayer *view_layer = CTX_data_view_layer(C);
2535 bool ret = false;
2536
2537 short selectmode_new;
2538 /* Avoid mixing up the active/iterable edit-mesh by limiting its scope. */
2539 {
2540 Object *obedit = CTX_data_edit_object(C);
2541 BMEditMesh *em = nullptr;
2542
2543 if (obedit && obedit->type == OB_MESH) {
2544 em = BKE_editmesh_from_object(obedit);
2545 }
2546
2547 if (em == nullptr) {
2548 return ret;
2549 }
2550
2551 selectmode_new = em->selectmode;
2552 }
2553 /* Assign before the new value is modified. */
2554 const short selectmode_old = selectmode_new;
2555
2556 bool only_update = false;
2557 switch (action) {
2558 case -1:
2559 /* Already set. */
2560 break;
2561 case 0: /* Disable. */
2562 /* Check we have something to do. */
2563 if ((selectmode_old & selectmode_toggle) == 0) {
2564 only_update = true;
2565 break;
2566 }
2567 selectmode_new &= ~selectmode_toggle;
2568 break;
2569 case 1: /* Enable. */
2570 /* Check we have something to do. */
2571 if ((selectmode_old & selectmode_toggle) != 0) {
2572 only_update = true;
2573 break;
2574 }
2575 selectmode_new |= selectmode_toggle;
2576 break;
2577 case 2: /* Toggle. */
2578 /* Can't disable this flag if its the only one set. */
2579 if (selectmode_old == selectmode_toggle) {
2580 only_update = true;
2581 break;
2582 }
2583 selectmode_new ^= selectmode_toggle;
2584 break;
2585 default:
2586 BLI_assert(0);
2587 break;
2588 }
2589
2591 scene, view_layer, CTX_wm_view3d(C));
2592
2593 if (only_update) {
2594 for (Object *ob_iter : objects) {
2595 BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter);
2596 em_iter->selectmode = selectmode_new;
2597 }
2598
2599 return false;
2600 }
2601
2602 if (use_extend == false || selectmode_new == 0) {
2603 if (use_expand) {
2604 const short selectmode_max = highest_order_bit_s(selectmode_old);
2605 for (Object *ob_iter : objects) {
2606 BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter);
2607 EDBM_selectmode_convert(em_iter, selectmode_max, selectmode_toggle);
2608 }
2609 }
2610 }
2611
2612 switch (selectmode_toggle) {
2613 case SCE_SELECT_VERTEX:
2614 if (use_extend == false || selectmode_new == 0) {
2615 selectmode_new = SCE_SELECT_VERTEX;
2616 }
2617 ret = true;
2618 break;
2619 case SCE_SELECT_EDGE:
2620 if (use_extend == false || selectmode_new == 0) {
2621 selectmode_new = SCE_SELECT_EDGE;
2622 }
2623 ret = true;
2624 break;
2625 case SCE_SELECT_FACE:
2626 if (use_extend == false || selectmode_new == 0) {
2627 selectmode_new = SCE_SELECT_FACE;
2628 }
2629 ret = true;
2630 break;
2631 default:
2632 BLI_assert(0);
2633 break;
2634 }
2635
2636 if (ret == true) {
2637 BLI_assert(selectmode_new != 0);
2638 ts->selectmode = selectmode_new;
2639 for (Object *ob_iter : objects) {
2640 BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter);
2641 EDBM_selectmode_set(em_iter, selectmode_new);
2642 DEG_id_tag_update(static_cast<ID *>(ob_iter->data),
2644 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob_iter->data);
2645 }
2648 }
2649
2650 return ret;
2651}
2652
2653bool EDBM_selectmode_set_multi_ex(Scene *scene, Span<Object *> objects, const short selectmode)
2654{
2655 ToolSettings *ts = scene->toolsettings;
2656 bool changed = false;
2657 bool changed_toolsettings = false;
2658
2659 if (ts->selectmode != selectmode) {
2660 ts->selectmode = selectmode;
2661 changed_toolsettings = true;
2662 }
2663
2664 for (Object *ob_iter : objects) {
2665 BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter);
2666 if (em_iter->selectmode == selectmode) {
2667 continue;
2668 }
2669 EDBM_selectmode_set(em_iter, selectmode);
2670 DEG_id_tag_update(static_cast<ID *>(ob_iter->data), ID_RECALC_SYNC_TO_EVAL | ID_RECALC_SELECT);
2671 WM_main_add_notifier(NC_GEOM | ND_SELECT, ob_iter->data);
2672 changed = true;
2673 }
2674
2675 if (changed_toolsettings) {
2678 }
2679
2680 return changed || changed_toolsettings;
2681}
2682
2683bool EDBM_selectmode_set_multi(bContext *C, const short selectmode)
2684{
2685 BLI_assert(selectmode != 0);
2686 Scene *scene = CTX_data_scene(C);
2687 ViewLayer *view_layer = CTX_data_view_layer(C);
2688 BKE_view_layer_synced_ensure(scene, view_layer);
2689 Object *obact = BKE_view_layer_active_object_get(view_layer);
2690 if (!(obact && (obact->type == OB_MESH) && (obact->mode & OB_MODE_EDIT) &&
2691 (BKE_editmesh_from_object(obact) != nullptr)))
2692 {
2693 return false;
2694 }
2695
2697 scene, view_layer, CTX_wm_view3d(C));
2698
2699 return EDBM_selectmode_set_multi_ex(scene, objects, selectmode);
2700}
2701
2715{
2716 if (objects.size() <= 1) {
2717 return false;
2718 }
2719
2720 bool changed = false;
2721 BMEditMesh *em_active = BKE_editmesh_from_object(objects[0]);
2722 for (Object *obedit : objects) {
2724 if (em_active->selectmode == em->selectmode) {
2725 continue;
2726 }
2727 EDBM_selectmode_set(em, em_active->selectmode);
2728 changed = true;
2729
2730 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SYNC_TO_EVAL | ID_RECALC_SELECT);
2731 WM_main_add_notifier(NC_GEOM | ND_SELECT, obedit->data);
2732 }
2733
2734 return changed;
2735}
2736
2738 BMEditMesh *em,
2739 const short selectmode_disable,
2740 const short selectmode_fallback)
2741{
2742 /* Not essential, but switch out of vertex mode since the
2743 * selected regions won't be nicely isolated after flushing. */
2744 if (em->selectmode & selectmode_disable) {
2745 const short selectmode = (em->selectmode == selectmode_disable) ?
2746 selectmode_fallback :
2747 (em->selectmode & ~selectmode_disable);
2748 scene->toolsettings->selectmode = selectmode;
2749 EDBM_selectmode_set(em, selectmode);
2750
2752
2753 return true;
2754 }
2755 return false;
2756}
2757
2759
2760/* -------------------------------------------------------------------- */
2763
2764bool EDBM_deselect_by_material(BMEditMesh *em, const short index, const bool select)
2765{
2766 BMIter iter;
2767 BMFace *efa;
2768 bool changed = false;
2769
2770 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2772 continue;
2773 }
2774 if (efa->mat_nr == index) {
2775 changed = true;
2776 BM_face_select_set(em->bm, efa, select);
2777 }
2778 }
2779 return changed;
2780}
2781
2782void EDBM_select_toggle_all(BMEditMesh *em) /* Exported for UV. */
2783{
2784 if (em->bm->totvertsel || em->bm->totedgesel || em->bm->totfacesel) {
2786 }
2787 else {
2789 }
2790}
2791
2792void EDBM_select_swap(BMEditMesh *em) /* Exported for UV. */
2793{
2794 BMIter iter;
2795 BMVert *eve;
2796 BMEdge *eed;
2797 BMFace *efa;
2798
2799 if (em->bm->selectmode & SCE_SELECT_VERTEX) {
2800 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
2802 continue;
2803 }
2805 }
2806 }
2807 else if (em->selectmode & SCE_SELECT_EDGE) {
2808 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
2810 continue;
2811 }
2813 }
2814 }
2815 else {
2816 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2818 continue;
2819 }
2821 }
2822 }
2823}
2824
2826{
2827 bool changed_multi = false;
2828 for (Base *base_iter : bases) {
2829 Object *ob_iter = base_iter->object;
2830 BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter);
2831
2832 if (em_iter->bm->totvertsel == 0) {
2833 continue;
2834 }
2835
2837 DEG_id_tag_update(static_cast<ID *>(ob_iter->data), ID_RECALC_SELECT);
2838 changed_multi = true;
2839 }
2840 return changed_multi;
2841}
2842
2851
2853 const Span<Base *> bases,
2854 const short selectmode_disable,
2855 const short selectmode_fallback)
2856{
2857 bool changed_multi = false;
2858 for (Base *base_iter : bases) {
2859 Object *ob_iter = base_iter->object;
2860 BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter);
2861
2862 if (EDBM_selectmode_disable(scene, em_iter, selectmode_disable, selectmode_fallback)) {
2863 changed_multi = true;
2864 }
2865 }
2866 return changed_multi;
2867}
2868
2870 const short selectmode_disable,
2871 const short selectmode_fallback)
2872{
2874 Scene *scene = CTX_data_scene(C);
2877 vc.scene, vc.view_layer, nullptr);
2878 return EDBM_selectmode_disable_multi_ex(scene, bases, selectmode_disable, selectmode_fallback);
2879}
2880
2882
2883/* -------------------------------------------------------------------- */
2894
2900
2901static bool bm_interior_loop_filter_fn(const BMLoop *l, void * /*user_data*/)
2902{
2903 if (BM_elem_flag_test(l->e, BM_ELEM_TAG)) {
2904 return false;
2905 }
2906 return true;
2907}
2909 int face_index,
2910 BMLoop *r_l_pair[2])
2911{
2912
2913 BMLoop *l_iter = e->l;
2914 int loop_index = 0;
2915 do {
2916 BMFace *f = l_iter->f;
2917 int i = BM_elem_index_get(f);
2918 if (!ELEM(i, -1, face_index)) {
2919 if (loop_index == 2) {
2920 return false;
2921 }
2922 r_l_pair[loop_index++] = l_iter;
2923 }
2924 } while ((l_iter = l_iter->radial_next) != e->l);
2925 return (loop_index == 2);
2926}
2927
2932static float bm_interior_face_group_calc_cost(ListBase *ls, const float *edge_lengths)
2933{
2934 /* Dividing by the area is important so larger face groups (which will become the outer shell)
2935 * aren't detected as having a high cost. */
2936 float area = 0.0f;
2937 float cost = 0.0f;
2938 bool found = false;
2939 LISTBASE_FOREACH (BMFaceLink *, f_link, ls) {
2940 BMFace *f = f_link->face;
2941 area += f_link->area;
2942 int i = BM_elem_index_get(f);
2943 BLI_assert(i != -1);
2944 BMLoop *l_iter, *l_first;
2945 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
2946 do {
2947 if (BM_elem_flag_test(l_iter->e, BM_ELEM_TAG)) {
2948 float cost_test = 0.0f;
2949 int cost_count = 0;
2950 /* All other faces. */
2951 BMLoop *l_radial_iter = l_iter;
2952 do {
2953 int i_other = BM_elem_index_get(l_radial_iter->f);
2954 if (!ELEM(i_other, -1, i)) {
2955 float angle = angle_normalized_v3v3(f->no, l_radial_iter->f->no);
2956 /* Ignore face direction since in the case on non-manifold faces connecting edges,
2957 * the face flipping may not be meaningful. */
2958 if (angle > DEG2RADF(90)) {
2959 angle = DEG2RADF(180) - angle;
2960 }
2961 /* Avoid calculating it inline, pass in pre-calculated edge lengths. */
2962#if 0
2963 cost_test += BM_edge_calc_length(l_iter->e) * angle;
2964#else
2965 BLI_assert(edge_lengths[BM_elem_index_get(l_iter->e)] != -1.0f);
2966 cost_test += edge_lengths[BM_elem_index_get(l_iter->e)] * angle;
2967#endif
2968 cost_count += 1;
2969 }
2970 } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter);
2971
2972 if (cost_count >= 2) {
2973 cost += cost_test;
2974 found = true;
2975 }
2976 }
2977 } while ((l_iter = l_iter->next) != l_first);
2978 }
2979 return found ? cost / area : FLT_MAX;
2980}
2981
2983{
2984 BMesh *bm = em->bm;
2985 BMIter iter;
2986 bool changed = false;
2987
2988 float *edge_lengths = MEM_malloc_arrayN<float>(bm->totedge, __func__);
2989
2990 {
2991 bool has_nonmanifold = false;
2992 BMEdge *e;
2993 int i;
2995 const bool is_over = BM_edge_face_count_is_over(e, 2);
2996 if (is_over) {
2998 has_nonmanifold = true;
2999 edge_lengths[i] = BM_edge_calc_length(e);
3000 }
3001 else {
3003 edge_lengths[i] = -1.0;
3004 }
3005
3006 BM_elem_index_set(e, i); /* set_inline */
3007 }
3008 bm->elem_index_dirty &= ~BM_EDGE;
3009
3010 if (has_nonmanifold == false) {
3011 MEM_freeN(edge_lengths);
3012 return false;
3013 }
3014 }
3015
3016 /* Group variables. */
3017 int(*fgroup_index)[2];
3018 int fgroup_len;
3019
3020 int *fgroup_array = MEM_malloc_arrayN<int>(bm->totface, __func__);
3021 fgroup_len = BM_mesh_calc_face_groups(
3022 bm, fgroup_array, &fgroup_index, bm_interior_loop_filter_fn, nullptr, nullptr, 0, BM_EDGE);
3023
3024 int *fgroup_recalc_stack = MEM_malloc_arrayN<int>(fgroup_len, __func__);
3025 STACK_DECLARE(fgroup_recalc_stack);
3026 STACK_INIT(fgroup_recalc_stack, fgroup_len);
3027
3029
3030 {
3031 BMFace *f;
3032 BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
3033 BM_elem_index_set(f, -1); /* set_dirty! */
3034 }
3035 }
3036 bm->elem_index_dirty |= BM_FACE;
3037
3038 ListBase *fgroup_listbase = MEM_calloc_arrayN<ListBase>(fgroup_len, __func__);
3039 BMFaceLink *f_link_array = MEM_calloc_arrayN<BMFaceLink>(bm->totface, __func__);
3040
3041 for (int i = 0; i < fgroup_len; i++) {
3042 const int fg_sta = fgroup_index[i][0];
3043 const int fg_len = fgroup_index[i][1];
3044 for (int j = 0; j < fg_len; j++) {
3045 const int face_index = fgroup_array[fg_sta + j];
3046 BMFace *f = BM_face_at_index(bm, face_index);
3047 BM_elem_index_set(f, i);
3048
3049 BMFaceLink *f_link = &f_link_array[face_index];
3050 f_link->face = f;
3051 f_link->area = BM_face_calc_area(f);
3052 BLI_addtail(&fgroup_listbase[i], f_link);
3053 }
3054 }
3055
3056 MEM_freeN(fgroup_array);
3057 MEM_freeN(fgroup_index);
3058
3059 Heap *fgroup_heap = BLI_heap_new_ex(fgroup_len);
3060 HeapNode **fgroup_table = MEM_malloc_arrayN<HeapNode *>(fgroup_len, __func__);
3061 bool *fgroup_dirty = MEM_calloc_arrayN<bool>(fgroup_len, __func__);
3062
3063 for (int i = 0; i < fgroup_len; i++) {
3064 const float cost = bm_interior_face_group_calc_cost(&fgroup_listbase[i], edge_lengths);
3065 if (cost != FLT_MAX) {
3066 fgroup_table[i] = BLI_heap_insert(fgroup_heap, -cost, POINTER_FROM_INT(i));
3067 }
3068 else {
3069 fgroup_table[i] = nullptr;
3070 }
3071 }
3072
3073 /* Avoid re-running cost calculations for large face-groups which will end up forming the
3074 * outer shell and not be considered interior.
3075 * As these face groups become increasingly bigger - their chance of being considered
3076 * interior reduces as does the time to calculate their cost.
3077 *
3078 * This delays recalculating them until they are considered can dates to remove
3079 * which becomes less and less likely as they increase in area. */
3080
3081#define USE_DELAY_FACE_GROUP_COST_CALC
3082
3083 while (true) {
3084
3085#if defined(USE_DELAY_FACE_GROUP_COST_CALC)
3086 while (!BLI_heap_is_empty(fgroup_heap)) {
3087 HeapNode *node_min = BLI_heap_top(fgroup_heap);
3088 const int i = POINTER_AS_INT(BLI_heap_node_ptr(node_min));
3089 if (fgroup_dirty[i]) {
3090 const float cost = bm_interior_face_group_calc_cost(&fgroup_listbase[i], edge_lengths);
3091 if (cost != FLT_MAX) {
3092 /* The cost may have improves (we may be able to skip this),
3093 * however the cost should _never_ make this a choice. */
3094 BLI_assert(-BLI_heap_node_value(node_min) >= cost);
3095 BLI_heap_node_value_update(fgroup_heap, fgroup_table[i], -cost);
3096 }
3097 else {
3098 BLI_heap_remove(fgroup_heap, fgroup_table[i]);
3099 fgroup_table[i] = nullptr;
3100 }
3101 fgroup_dirty[i] = false;
3102 }
3103 else {
3104 break;
3105 }
3106 }
3107#endif
3108
3109 if (BLI_heap_is_empty(fgroup_heap)) {
3110 break;
3111 }
3112
3113 const int i_min = POINTER_AS_INT(BLI_heap_pop_min(fgroup_heap));
3114 BLI_assert(fgroup_table[i_min] != nullptr);
3115 BLI_assert(fgroup_dirty[i_min] == false);
3116 fgroup_table[i_min] = nullptr;
3117 changed = true;
3118
3119 while (BMFaceLink *f_link = static_cast<BMFaceLink *>(BLI_pophead(&fgroup_listbase[i_min]))) {
3120 BMFace *f = f_link->face;
3121 BM_face_select_set(bm, f, true);
3122 BM_elem_index_set(f, -1); /* set_dirty */
3123
3124 BMLoop *l_iter, *l_first;
3125
3126 /* Loop over edges face edges, merging groups which are no longer separated
3127 * by non-manifold edges (when manifold check ignores faces from this group). */
3128 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
3129 do {
3130 BMLoop *l_pair[2];
3131 if (bm_interior_edge_is_manifold_except_face_index(l_iter->e, i_min, l_pair)) {
3133
3134 int i_a = BM_elem_index_get(l_pair[0]->f);
3135 int i_b = BM_elem_index_get(l_pair[1]->f);
3136 if (i_a != i_b) {
3137 /* Only for predictable results that don't depend on the order of radial loops,
3138 * not essential. */
3139 if (i_a > i_b) {
3140 std::swap(i_a, i_b);
3141 }
3142
3143 /* Merge the groups. */
3144 LISTBASE_FOREACH (LinkData *, n, &fgroup_listbase[i_b]) {
3145 BMFace *f_iter = static_cast<BMFace *>(n->data);
3146 BM_elem_index_set(f_iter, i_a);
3147 }
3148 BLI_movelisttolist(&fgroup_listbase[i_a], &fgroup_listbase[i_b]);
3149
3150 /* This may have been added to 'fgroup_recalc_stack', instead of removing it,
3151 * just check the heap node isn't nullptr before recalculating. */
3152 BLI_heap_remove(fgroup_heap, fgroup_table[i_b]);
3153 fgroup_table[i_b] = nullptr;
3154 /* Keep the dirty flag as-is for 'i_b', because it may be in the 'fgroup_recalc_stack'
3155 * and we don't want to add it again.
3156 * Instead rely on the 'fgroup_table[i_b]' being nullptr as a secondary check. */
3157
3158 if (fgroup_dirty[i_a] == false) {
3159 BLI_assert(fgroup_table[i_a] != nullptr);
3160 STACK_PUSH(fgroup_recalc_stack, i_a);
3161 fgroup_dirty[i_a] = true;
3162 }
3163 }
3164 }
3165
3166 /* Mark all connected groups for re-calculation. */
3167 BMLoop *l_radial_iter = l_iter->radial_next;
3168 if (l_radial_iter != l_iter) {
3169 do {
3170 int i_other = BM_elem_index_get(l_radial_iter->f);
3171 if (!ELEM(i_other, -1, i_min)) {
3172 if ((fgroup_table[i_other] != nullptr) && (fgroup_dirty[i_other] == false)) {
3173#if !defined(USE_DELAY_FACE_GROUP_COST_CALC)
3174 STACK_PUSH(fgroup_recalc_stack, i_other);
3175#endif
3176 fgroup_dirty[i_other] = true;
3177 }
3178 }
3179 } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter);
3180 }
3181
3182 } while ((l_iter = l_iter->next) != l_first);
3183 }
3184
3185 for (int index = 0; index < STACK_SIZE(fgroup_recalc_stack); index++) {
3186 const int i = fgroup_recalc_stack[index];
3187 if (fgroup_table[i] != nullptr && fgroup_dirty[i] == true) {
3188 /* First update edge tags. */
3189 const float cost = bm_interior_face_group_calc_cost(&fgroup_listbase[i], edge_lengths);
3190 if (cost != FLT_MAX) {
3191 BLI_heap_node_value_update(fgroup_heap, fgroup_table[i], -cost);
3192 }
3193 else {
3194 BLI_heap_remove(fgroup_heap, fgroup_table[i]);
3195 fgroup_table[i] = nullptr;
3196 }
3197 }
3198 fgroup_dirty[i] = false;
3199 }
3200 STACK_CLEAR(fgroup_recalc_stack);
3201 }
3202
3203 MEM_freeN(edge_lengths);
3204 MEM_freeN(f_link_array);
3205 MEM_freeN(fgroup_listbase);
3206 MEM_freeN(fgroup_recalc_stack);
3207 MEM_freeN(fgroup_table);
3208 MEM_freeN(fgroup_dirty);
3209
3210 BLI_heap_free(fgroup_heap, nullptr);
3211
3212 return changed;
3213}
3214
3216
3217/* -------------------------------------------------------------------- */
3222
3224#define USE_LINKED_SELECT_DEFAULT_HACK
3225
3230
3231static bool select_linked_delimit_test(BMEdge *e, int delimit, const DelimitData *delimit_data)
3232{
3233 BLI_assert(delimit);
3234
3235 if (delimit & BMO_DELIM_SEAM) {
3237 return true;
3238 }
3239 }
3240
3241 if (delimit & BMO_DELIM_SHARP) {
3242 if (BM_elem_flag_test(e, BM_ELEM_SMOOTH) == 0) {
3243 return true;
3244 }
3245 }
3246
3247 if (delimit & BMO_DELIM_NORMAL) {
3248 if (!BM_edge_is_contiguous(e)) {
3249 return true;
3250 }
3251 }
3252
3253 if (delimit & BMO_DELIM_MATERIAL) {
3254 if (e->l && e->l->radial_next != e->l) {
3255 const short mat_nr = e->l->f->mat_nr;
3256 BMLoop *l_iter = e->l->radial_next;
3257 do {
3258 if (l_iter->f->mat_nr != mat_nr) {
3259 return true;
3260 }
3261 } while ((l_iter = l_iter->radial_next) != e->l);
3262 }
3263 }
3264
3265 if (delimit & BMO_DELIM_UV) {
3267 e, delimit_data->cd_loop_type, delimit_data->cd_loop_offset) == 0)
3268 {
3269 return true;
3270 }
3271 }
3272
3273 return false;
3274}
3275
3276#ifdef USE_LINKED_SELECT_DEFAULT_HACK
3281static int select_linked_delimit_default_from_op(wmOperator *op, const int select_mode)
3282{
3283 static char delimit_last_store[2] = {0, BMO_DELIM_SEAM};
3284 int delimit_last_index = (select_mode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) == 0;
3285 char *delimit_last = &delimit_last_store[delimit_last_index];
3286 PropertyRNA *prop_delimit = RNA_struct_find_property(op->ptr, "delimit");
3287 int delimit;
3288
3289 if (RNA_property_is_set(op->ptr, prop_delimit)) {
3290 delimit = RNA_property_enum_get(op->ptr, prop_delimit);
3291 *delimit_last = delimit;
3292 }
3293 else {
3294 delimit = *delimit_last;
3295 RNA_property_enum_set(op->ptr, prop_delimit, delimit);
3296 }
3297 return delimit;
3298}
3299#endif
3300
3301static void select_linked_delimit_validate(BMesh *bm, int *delimit)
3302{
3303 if ((*delimit) & BMO_DELIM_UV) {
3304 if (!CustomData_has_layer(&bm->ldata, CD_PROP_FLOAT2)) {
3305 (*delimit) &= ~BMO_DELIM_UV;
3306 }
3307 }
3308}
3309
3310static void select_linked_delimit_begin(BMesh *bm, int delimit)
3311{
3312 DelimitData delimit_data{};
3313
3314 if (delimit & BMO_DELIM_UV) {
3315 delimit_data.cd_loop_type = CD_PROP_FLOAT2;
3316 delimit_data.cd_loop_offset = CustomData_get_offset(&bm->ldata, delimit_data.cd_loop_type);
3317 if (delimit_data.cd_loop_offset == -1) {
3318 delimit &= ~BMO_DELIM_UV;
3319 }
3320 }
3321
3322 /* Shouldn't need to allocated BMO flags here (sigh). */
3324
3325 {
3326 BMIter iter;
3327 BMEdge *e;
3328
3329 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
3330 const bool is_walk_ok = (select_linked_delimit_test(e, delimit, &delimit_data) == false);
3331
3332 BMO_edge_flag_set(bm, e, BMO_ELE_TAG, is_walk_ok);
3333 }
3334 }
3335}
3336
3338{
3339 BMesh *bm = em->bm;
3340
3342}
3343
3345{
3346 Scene *scene = CTX_data_scene(C);
3347 ViewLayer *view_layer = CTX_data_view_layer(C);
3348
3349#ifdef USE_LINKED_SELECT_DEFAULT_HACK
3350 const int delimit_init = select_linked_delimit_default_from_op(op,
3351 scene->toolsettings->selectmode);
3352#else
3353 const int delimit_init = RNA_enum_get(op->ptr, "delimit");
3354#endif
3355
3357 scene, view_layer, CTX_wm_view3d(C));
3358
3359 for (Object *obedit : objects) {
3360
3362 BMesh *bm = em->bm;
3363 BMIter iter;
3364 BMWalker walker;
3365
3366 int delimit = delimit_init;
3367
3369
3370 if (delimit) {
3371 select_linked_delimit_begin(em->bm, delimit);
3372 }
3373
3374 if (em->selectmode & SCE_SELECT_VERTEX) {
3375 BMVert *v;
3376
3377 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
3379 }
3380
3381 /* Exclude all delimited verts. */
3382 if (delimit) {
3383 BMEdge *e;
3384 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
3386 /* Check the edge for selected faces,
3387 * this supports stepping off isolated vertices which would otherwise be ignored. */
3391 }
3392 }
3393 }
3394 }
3395
3396 BMW_init(&walker,
3397 em->bm,
3400 delimit ? BMO_ELE_TAG : BMW_MASK_NOP,
3403 BMW_NIL_LAY);
3404
3405 if (delimit) {
3406 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
3408 BMElem *ele_walk;
3409 BMW_ITER (ele_walk, &walker, v) {
3410 if (ele_walk->head.htype == BM_LOOP) {
3411 BMVert *v_step = ((BMLoop *)ele_walk)->v;
3412 BM_vert_select_set(em->bm, v_step, true);
3414 }
3415 else {
3416 BMEdge *e_step = (BMEdge *)ele_walk;
3417 BLI_assert(ele_walk->head.htype == BM_EDGE);
3418 BM_edge_select_set(em->bm, e_step, true);
3421 }
3422 }
3423 }
3424 }
3425 }
3426 else {
3427 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
3429 BMEdge *e_walk;
3430 BMW_ITER (e_walk, &walker, v) {
3431 BM_edge_select_set(em->bm, e_walk, true);
3433 }
3434 }
3435 }
3436 }
3437
3438 BMW_end(&walker);
3439
3441 }
3442 else if (em->selectmode & SCE_SELECT_EDGE) {
3443 BMEdge *e;
3444
3445 if (delimit) {
3446 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
3447 /* Check the edge for selected faces,
3448 * this supports stepping off isolated edges which would otherwise be ignored. */
3454 }
3455 }
3456 else {
3457 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
3459 }
3460 }
3461
3462 BMW_init(&walker,
3463 em->bm,
3466 delimit ? BMO_ELE_TAG : BMW_MASK_NOP,
3469 BMW_NIL_LAY);
3470
3471 if (delimit) {
3472 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
3474 BMElem *ele_walk;
3475 BMW_ITER (ele_walk, &walker, e) {
3476 if (ele_walk->head.htype == BM_LOOP) {
3477 BMLoop *l_step = (BMLoop *)ele_walk;
3478 BM_edge_select_set(em->bm, l_step->e, true);
3479 BM_edge_select_set(em->bm, l_step->prev->e, true);
3481 }
3482 else {
3483 BMEdge *e_step = (BMEdge *)ele_walk;
3484 BLI_assert(ele_walk->head.htype == BM_EDGE);
3485 BM_edge_select_set(em->bm, e_step, true);
3487 }
3488 }
3489 }
3490 }
3491 }
3492 else {
3493 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
3495 BMEdge *e_walk;
3496 BMW_ITER (e_walk, &walker, e) {
3497 BM_edge_select_set(em->bm, e_walk, true);
3499 }
3500 }
3501 }
3502 }
3503
3504 BMW_end(&walker);
3505
3507 }
3508 else {
3509 BMFace *f;
3510
3511 BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
3513 }
3514
3515 BMW_init(&walker,
3516 bm,
3517 BMW_ISLAND,
3519 delimit ? BMO_ELE_TAG : BMW_MASK_NOP,
3522 BMW_NIL_LAY);
3523
3524 BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
3526 BMFace *f_walk;
3527 BMW_ITER (f_walk, &walker, f) {
3528 BM_face_select_set(bm, f_walk, true);
3530 }
3531 }
3532 }
3533
3534 BMW_end(&walker);
3535 }
3536
3537 if (delimit) {
3539 }
3540
3541 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
3542 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
3543 }
3544
3545 return OPERATOR_FINISHED;
3546}
3547
3549{
3550 PropertyRNA *prop;
3551
3552 /* Identifiers. */
3553 ot->name = "Select Linked All";
3554 ot->idname = "MESH_OT_select_linked";
3555 ot->description = "Select all vertices connected to the current selection";
3556
3557 /* API callbacks. */
3559 ot->poll = ED_operator_editmesh;
3560
3561 /* Flags. */
3562 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3563
3564 prop = RNA_def_enum_flag(ot->srna,
3565 "delimit",
3568 "Delimit",
3569 "Delimit selected region");
3570#ifdef USE_LINKED_SELECT_DEFAULT_HACK
3572#else
3573 UNUSED_VARS(prop);
3574#endif
3575}
3576
3578
3579/* -------------------------------------------------------------------- */
3582
3584
3585static void edbm_select_linked_pick_ex(BMEditMesh *em, BMElem *ele, bool sel, int delimit)
3586{
3587 BMesh *bm = em->bm;
3588 BMWalker walker;
3589
3591
3592 if (delimit) {
3594 }
3595
3596 /* NOTE: logic closely matches #edbm_select_linked_exec, keep in sync. */
3597
3598 if (ele->head.htype == BM_VERT) {
3599 BMVert *eve = (BMVert *)ele;
3600
3601 BMW_init(&walker,
3602 bm,
3605 delimit ? BMO_ELE_TAG : BMW_MASK_NOP,
3608 BMW_NIL_LAY);
3609
3610 if (delimit) {
3611 BMElem *ele_walk;
3612 BMW_ITER (ele_walk, &walker, eve) {
3613 if (ele_walk->head.htype == BM_LOOP) {
3614 BMVert *v_step = ((BMLoop *)ele_walk)->v;
3615 BM_vert_select_set(bm, v_step, sel);
3616 }
3617 else {
3618 BMEdge *e_step = (BMEdge *)ele_walk;
3619 BLI_assert(ele_walk->head.htype == BM_EDGE);
3620 BM_edge_select_set(bm, e_step, sel);
3621 }
3622 }
3623 }
3624 else {
3625 BMEdge *e_walk;
3626 BMW_ITER (e_walk, &walker, eve) {
3627 BM_edge_select_set(bm, e_walk, sel);
3628 }
3629 }
3630
3631 BMW_end(&walker);
3632
3634 }
3635 else if (ele->head.htype == BM_EDGE) {
3636 BMEdge *eed = (BMEdge *)ele;
3637
3638 BMW_init(&walker,
3639 bm,
3642 delimit ? BMO_ELE_TAG : BMW_MASK_NOP,
3645 BMW_NIL_LAY);
3646
3647 if (delimit) {
3648 BMElem *ele_walk;
3649 BMW_ITER (ele_walk, &walker, eed) {
3650 if (ele_walk->head.htype == BM_LOOP) {
3651 BMEdge *e_step = ((BMLoop *)ele_walk)->e;
3652 BM_edge_select_set(bm, e_step, sel);
3653 }
3654 else {
3655 BMEdge *e_step = (BMEdge *)ele_walk;
3656 BLI_assert(ele_walk->head.htype == BM_EDGE);
3657 BM_edge_select_set(bm, e_step, sel);
3658 }
3659 }
3660 }
3661 else {
3662 BMEdge *e_walk;
3663 BMW_ITER (e_walk, &walker, eed) {
3664 BM_edge_select_set(bm, e_walk, sel);
3665 }
3666 }
3667
3668 BMW_end(&walker);
3669
3671 }
3672 else if (ele->head.htype == BM_FACE) {
3673 BMFace *efa = (BMFace *)ele;
3674
3675 BMW_init(&walker,
3676 bm,
3677 BMW_ISLAND,
3679 delimit ? BMO_ELE_TAG : BMW_MASK_NOP,
3682 BMW_NIL_LAY);
3683
3684 {
3685 BMFace *f_walk;
3686 BMW_ITER (f_walk, &walker, efa) {
3687 BM_face_select_set(bm, f_walk, sel);
3689 }
3690 }
3691
3692 BMW_end(&walker);
3693 }
3694
3695 if (delimit) {
3697 }
3698}
3699
3701 wmOperator *op,
3702 const wmEvent *event)
3703{
3704 Base *basact = nullptr;
3705 BMVert *eve;
3706 BMEdge *eed;
3707 BMFace *efa;
3708 const bool sel = !RNA_boolean_get(op->ptr, "deselect");
3709 int index;
3710
3711 if (RNA_struct_property_is_set(op->ptr, "index")) {
3712 return edbm_select_linked_pick_exec(C, op);
3713 }
3714
3715 /* #unified_findnearest needs OpenGL. */
3717
3718 /* Setup view context for argument to callbacks. */
3720
3722 vc.scene, vc.view_layer, vc.v3d);
3723
3724 {
3725 bool has_edges = false;
3726 for (Base *base : bases) {
3727 Object *ob_iter = base->object;
3729 if (vc.em->bm->totedge) {
3730 has_edges = true;
3731 }
3732 }
3733 if (has_edges == false) {
3734 return OPERATOR_CANCELLED;
3735 }
3736 }
3737
3738 vc.mval[0] = event->mval[0];
3739 vc.mval[1] = event->mval[1];
3740
3741 /* Return warning. */
3742 {
3743 int base_index = -1;
3744 const bool ok = unified_findnearest(&vc, bases, &base_index, &eve, &eed, &efa);
3745 if (!ok) {
3746 return OPERATOR_CANCELLED;
3747 }
3748 basact = bases[base_index];
3749 }
3750
3752 BMEditMesh *em = vc.em;
3753 BMesh *bm = em->bm;
3754
3755#ifdef USE_LINKED_SELECT_DEFAULT_HACK
3757#else
3758 int delimit = RNA_enum_get(op->ptr, "delimit");
3759#endif
3760
3761 BMElem *ele = EDBM_elem_from_selectmode(em, eve, eed, efa);
3762
3763 edbm_select_linked_pick_ex(em, ele, sel, delimit);
3764
3765 /* To support redo. */
3766 {
3767 /* Note that the `base_index` can't be used as the index depends on the 3D Viewport
3768 * which might not be available on redo. */
3770 int object_index;
3771 index = EDBM_elem_to_index_any_multi(vc.scene, vc.view_layer, em, ele, &object_index);
3772 BLI_assert(object_index >= 0);
3773 RNA_int_set(op->ptr, "object_index", object_index);
3774 RNA_int_set(op->ptr, "index", index);
3775 }
3776
3777 DEG_id_tag_update(static_cast<ID *>(basact->object->data), ID_RECALC_SELECT);
3779
3780 return OPERATOR_FINISHED;
3781}
3782
3784{
3785 Object *obedit = nullptr;
3786 BMElem *ele;
3787
3788 {
3789 const Scene *scene = CTX_data_scene(C);
3790 ViewLayer *view_layer = CTX_data_view_layer(C);
3791 /* Intentionally wrap negative values so the lookup fails. */
3792 const uint object_index = uint(RNA_int_get(op->ptr, "object_index"));
3793 const uint index = uint(RNA_int_get(op->ptr, "index"));
3794 ele = EDBM_elem_from_index_any_multi(scene, view_layer, object_index, index, &obedit);
3795 }
3796
3797 if (ele == nullptr) {
3798 return OPERATOR_CANCELLED;
3799 }
3800
3802 const bool sel = !RNA_boolean_get(op->ptr, "deselect");
3803
3804#ifdef USE_LINKED_SELECT_DEFAULT_HACK
3806#else
3807 int delimit = RNA_enum_get(op->ptr, "delimit");
3808#endif
3809
3810 edbm_select_linked_pick_ex(em, ele, sel, delimit);
3811
3812 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
3814
3815 return OPERATOR_FINISHED;
3816}
3817
3819{
3820 PropertyRNA *prop;
3821
3822 /* Identifiers. */
3823 ot->name = "Select Linked";
3824 ot->idname = "MESH_OT_select_linked_pick";
3825 ot->description = "(De)select all vertices linked to the edge under the mouse cursor";
3826
3827 /* API callbacks. */
3830 ot->poll = ED_operator_editmesh;
3831
3832 /* Flags. */
3833 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3834
3835 RNA_def_boolean(ot->srna, "deselect", false, "Deselect", "");
3836 prop = RNA_def_enum_flag(ot->srna,
3837 "delimit",
3840 "Delimit",
3841 "Delimit selected region");
3842#ifdef USE_LINKED_SELECT_DEFAULT_HACK
3844#endif
3845
3846 /* Use for redo. */
3847 prop = RNA_def_int(ot->srna, "object_index", -1, -1, INT_MAX, "", "", 0, INT_MAX);
3849 prop = RNA_def_int(ot->srna, "index", -1, -1, INT_MAX, "", "", 0, INT_MAX);
3851}
3852
3854
3855/* -------------------------------------------------------------------- */
3858
3860{
3861 const Scene *scene = CTX_data_scene(C);
3862 ViewLayer *view_layer = CTX_data_view_layer(C);
3863 const bool extend = RNA_boolean_get(op->ptr, "extend");
3864 const bool exclude_nonmanifold = RNA_boolean_get(op->ptr, "exclude_nonmanifold");
3865 const int pole_count = RNA_int_get(op->ptr, "pole_count");
3866 const eElemCountType type = eElemCountType(RNA_enum_get(op->ptr, "type"));
3868 scene, view_layer, CTX_wm_view3d(C));
3869
3870 for (Object *obedit : objects) {
3872 bool changed = false;
3873
3874 BMIter iter;
3875 BMVert *v;
3876
3877 if (!extend) {
3879 changed = true;
3880 }
3881
3882 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
3884 continue;
3885 }
3886
3887 const int v_edge_count = BM_vert_edge_count_at_most(v, pole_count + 1);
3888 if (!is_count_a_match(type, v_edge_count, pole_count)) {
3889 continue;
3890 }
3891
3892 if (exclude_nonmanifold) {
3893 /* Exclude non-manifold vertices (no edges). */
3894 if (BM_vert_is_manifold(v) == false) {
3895 continue;
3896 }
3897
3898 /* Exclude vertices connected to non-manifold edges. */
3899 BMIter eiter;
3900 BMEdge *e;
3901 bool all_edges_manifold = true;
3902 BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
3903 if (BM_edge_is_manifold(e) == false) {
3904 all_edges_manifold = false;
3905 break;
3906 }
3907 }
3908
3909 if (all_edges_manifold == false) {
3910 continue;
3911 }
3912 }
3913
3914 /* All tests passed, perform the selection. */
3915
3916 /* Multiple selection modes may be active.
3917 * Select elements per the finest-grained choice. */
3918 changed = true;
3919
3920 if (em->selectmode & SCE_SELECT_VERTEX) {
3921 BM_vert_select_set(em->bm, v, true);
3922 }
3923 else if (em->selectmode & SCE_SELECT_EDGE) {
3924 BMIter eiter;
3925 BMEdge *e;
3926 BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
3927 BM_edge_select_set(em->bm, e, true);
3928 }
3929 }
3930 else if (em->selectmode & SCE_SELECT_FACE) {
3931 BMIter fiter;
3932 BMFace *f;
3933 BM_ITER_ELEM (f, &fiter, v, BM_FACES_OF_VERT) {
3934 BM_face_select_set(em->bm, f, true);
3935 }
3936 }
3937 else {
3939 }
3940 }
3941
3942 if (changed) {
3944 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
3945 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
3946 }
3947 }
3948
3949 return OPERATOR_FINISHED;
3950}
3951
3953{
3954 /* Identifiers. */
3955 ot->name = "Select By Pole Count";
3956 ot->description =
3957 "Select vertices at poles by the number of connected edges. "
3958 "In edge and face mode the geometry connected to the vertices is selected";
3959 ot->idname = "MESH_OT_select_by_pole_count";
3960
3961 /* API callbacks. */
3963 ot->poll = ED_operator_editmesh;
3964
3965 /* Flags. */
3966 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3967
3968 /* Properties. */
3969 RNA_def_int(ot->srna, "pole_count", 4, 0, INT_MAX, "Pole Count", "", 0, INT_MAX);
3970 RNA_def_enum(ot->srna,
3971 "type",
3974 "Type",
3975 "Type of comparison to make");
3976 RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
3978 ot->srna, "exclude_nonmanifold", true, "Exclude Non Manifold", "Exclude non-manifold poles");
3979}
3980
3982
3983/* -------------------------------------------------------------------- */
3986
3988{
3989 const Scene *scene = CTX_data_scene(C);
3990 ViewLayer *view_layer = CTX_data_view_layer(C);
3991 const bool extend = RNA_boolean_get(op->ptr, "extend");
3992 const int numverts = RNA_int_get(op->ptr, "number");
3993 const eElemCountType type = eElemCountType(RNA_enum_get(op->ptr, "type"));
3995 scene, view_layer, CTX_wm_view3d(C));
3996
3997 for (Object *obedit : objects) {
3999 bool changed = false;
4000
4001 BMFace *efa;
4002 BMIter iter;
4003
4004 if (!extend) {
4006 changed = true;
4007 }
4008
4009 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
4011 continue;
4012 }
4013
4014 if (is_count_a_match(type, efa->len, numverts)) {
4015 changed = true;
4016 BM_face_select_set(em->bm, efa, true);
4017 }
4018 }
4019
4020 if (changed) {
4022 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
4023 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
4024 }
4025 }
4026
4027 return OPERATOR_FINISHED;
4028}
4029
4031{
4032
4033 /* Identifiers. */
4034 ot->name = "Select Faces by Sides";
4035 ot->description = "Select vertices or faces by the number of face sides";
4036 ot->idname = "MESH_OT_select_face_by_sides";
4037
4038 /* API callbacks. */
4040 ot->poll = ED_operator_editmesh;
4041
4042 /* Flags. */
4043 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
4044
4045 /* Properties. */
4046 RNA_def_int(ot->srna, "number", 4, 3, INT_MAX, "Number of Vertices", "", 3, INT_MAX);
4047 RNA_def_enum(ot->srna,
4048 "type",
4051 "Type",
4052 "Type of comparison to make");
4053 RNA_def_boolean(ot->srna, "extend", true, "Extend", "Extend the selection");
4054}
4055
4057
4058/* -------------------------------------------------------------------- */
4061
4063{
4064 const Scene *scene = CTX_data_scene(C);
4065 ViewLayer *view_layer = CTX_data_view_layer(C);
4066 const bool extend = RNA_boolean_get(op->ptr, "extend");
4067
4069 scene, view_layer, CTX_wm_view3d(C));
4070
4071 for (Object *obedit : objects) {
4073 BMesh *bm = em->bm;
4074 BMIter iter;
4075
4076 bool changed = false;
4077
4078 if (!extend) {
4080 changed = true;
4081 }
4082
4083 if (em->selectmode & SCE_SELECT_VERTEX) {
4084 BMVert *eve;
4085 BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
4087 continue;
4088 }
4089 if (!eve->e) {
4090 BM_vert_select_set(bm, eve, true);
4091 changed = true;
4092 }
4093 }
4094 }
4095
4096 if (em->selectmode & SCE_SELECT_EDGE) {
4097 BMEdge *eed;
4098 BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
4100 continue;
4101 }
4102 if (BM_edge_is_wire(eed)) {
4103 BM_edge_select_set(bm, eed, true);
4104 changed = true;
4105 }
4106 }
4107 }
4108
4109 if (em->selectmode & SCE_SELECT_FACE) {
4110 BMFace *efa;
4111 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
4113 continue;
4114 }
4115 BMIter liter;
4116 BMLoop *l;
4117 bool is_loose = true;
4118 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
4119 if (!BM_edge_is_boundary(l->e)) {
4120 is_loose = false;
4121 break;
4122 }
4123 }
4124 if (is_loose) {
4125 BM_face_select_set(bm, efa, true);
4126 changed = true;
4127 }
4128 }
4129 }
4130
4131 if (changed) {
4133
4134 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
4135 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
4136 }
4137 }
4138
4139 return OPERATOR_FINISHED;
4140}
4141
4143{
4144 /* Identifiers. */
4145 ot->name = "Select Loose Geometry";
4146 ot->description = "Select loose geometry based on the selection mode";
4147 ot->idname = "MESH_OT_select_loose";
4148
4149 /* API callbacks. */
4150 ot->exec = edbm_select_loose_exec;
4151 ot->poll = ED_operator_editmesh;
4152
4153 /* Flags. */
4154 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
4155
4156 /* Props. */
4157 RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
4158}
4159
4161
4162/* -------------------------------------------------------------------- */
4165
4167{
4168 const Scene *scene = CTX_data_scene(C);
4169 ViewLayer *view_layer = CTX_data_view_layer(C);
4170 const int axis_flag = RNA_enum_get(op->ptr, "axis");
4171 const bool extend = RNA_boolean_get(op->ptr, "extend");
4172 Object *obedit_active = CTX_data_edit_object(C);
4173 BMEditMesh *em_active = BKE_editmesh_from_object(obedit_active);
4174 const int select_mode = em_active->bm->selectmode;
4175 int tot_mirr = 0, tot_fail = 0;
4176
4178 scene, view_layer, CTX_wm_view3d(C));
4179
4180 for (Object *obedit : objects) {
4182
4183 if (em->bm->totvertsel == 0) {
4184 continue;
4185 }
4186
4187 int tot_mirr_iter = 0, tot_fail_iter = 0;
4188
4189 for (int axis = 0; axis < 3; axis++) {
4190 if ((1 << axis) & axis_flag) {
4192 static_cast<const Mesh *>(obedit->data),
4193 axis,
4194 extend,
4195 &tot_mirr_iter,
4196 &tot_fail_iter);
4197 }
4198 }
4199
4200 if (tot_mirr_iter) {
4202
4203 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
4204 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
4205 }
4206
4207 tot_fail += tot_fail_iter;
4208 tot_mirr += tot_mirr_iter;
4209 }
4210
4211 if (tot_mirr || tot_fail) {
4212 ED_mesh_report_mirror_ex(op, tot_mirr, tot_fail, select_mode);
4213 }
4214 return OPERATOR_FINISHED;
4215}
4216
4218{
4219 /* Identifiers. */
4220 ot->name = "Select Mirror";
4221 ot->description = "Select mesh items at mirrored locations";
4222 ot->idname = "MESH_OT_select_mirror";
4223
4224 /* API callbacks. */
4226 ot->poll = ED_operator_editmesh;
4227
4228 /* Flags. */
4229 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
4230
4231 /* Props. */
4232 RNA_def_enum_flag(ot->srna, "axis", rna_enum_axis_flag_xyz_items, (1 << 0), "Axis", "");
4233
4234 RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the existing selection");
4235}
4236
4238
4239/* -------------------------------------------------------------------- */
4242
4244{
4245 const Scene *scene = CTX_data_scene(C);
4246 ViewLayer *view_layer = CTX_data_view_layer(C);
4247 const bool use_face_step = RNA_boolean_get(op->ptr, "use_face_step");
4248
4250 scene, view_layer, CTX_wm_view3d(C));
4251 for (Object *obedit : objects) {
4253 BMesh *bm = em->bm;
4254
4255 if ((bm->totvertsel == 0) && (bm->totedgesel == 0) && (bm->totfacesel == 0)) {
4256 continue;
4257 }
4258
4259 EDBM_select_more(em, use_face_step);
4260 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
4261 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
4262 }
4263
4264 return OPERATOR_FINISHED;
4265}
4266
4268{
4269 /* Identifiers. */
4270 ot->name = "Select More";
4271 ot->idname = "MESH_OT_select_more";
4272 ot->description = "Select more vertices, edges or faces connected to initial selection";
4273
4274 /* API callbacks */
4275 ot->exec = edbm_select_more_exec;
4276 ot->poll = ED_operator_editmesh;
4277
4278 /* Flags. */
4279 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
4280
4282 ot->srna, "use_face_step", true, "Face Step", "Connected faces (instead of edges)");
4283}
4284
4286
4287/* -------------------------------------------------------------------- */
4290
4292{
4293 const Scene *scene = CTX_data_scene(C);
4294 ViewLayer *view_layer = CTX_data_view_layer(C);
4295 const bool use_face_step = RNA_boolean_get(op->ptr, "use_face_step");
4296
4298 scene, view_layer, CTX_wm_view3d(C));
4299 for (Object *obedit : objects) {
4301 BMesh *bm = em->bm;
4302
4303 if ((bm->totvertsel == 0) && (bm->totedgesel == 0) && (bm->totfacesel == 0)) {
4304 continue;
4305 }
4306
4307 EDBM_select_less(em, use_face_step);
4308 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
4309 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
4310 }
4311
4312 return OPERATOR_FINISHED;
4313}
4314
4316{
4317 /* Identifiers. */
4318 ot->name = "Select Less";
4319 ot->idname = "MESH_OT_select_less";
4320 ot->description = "Deselect vertices, edges or faces at the boundary of each selection region";
4321
4322 /* API callbacks */
4323 ot->exec = edbm_select_less_exec;
4324 ot->poll = ED_operator_editmesh;
4325
4326 /* Flags. */
4327 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
4328
4330 ot->srna, "use_face_step", true, "Face Step", "Connected faces (instead of edges)");
4331}
4332
4334
4335/* -------------------------------------------------------------------- */
4338
4343{
4344 BMIter viter;
4345 BMVert *v;
4346
4347 BM_ITER_ELEM (v, &viter, e, BM_VERTS_OF_EDGE) {
4348 BMIter eiter;
4349 BMEdge *e_other;
4350
4351 BM_ITER_ELEM (e_other, &eiter, v, BM_EDGES_OF_VERT) {
4352 if ((e_other != e) && BM_elem_flag_test(e_other, BM_ELEM_SELECT)) {
4353 return false;
4354 }
4355 }
4356 }
4357 return true;
4358}
4359
4360/* Walk all reachable elements of the same type as h_act in breadth-first
4361 * order, starting from h_act. Deselects elements if the depth when they
4362 * are reached is not a multiple of "nth". */
4364 const CheckerIntervalParams *op_params,
4365 BMHeader *h_act)
4366{
4367 BMElem *ele;
4368 BMesh *bm = em->bm;
4369 BMWalker walker;
4370 BMIter iter;
4371 int walktype = 0, itertype = 0, flushtype = 0;
4372 short mask_vert = 0, mask_edge = 0, mask_face = 0;
4373
4374 /* No active element from which to start - nothing to do. */
4375 if (h_act == nullptr) {
4376 return;
4377 }
4378
4379 /* Determine which type of iterator, walker, and select flush to use
4380 * based on type of the elements being deselected. */
4381 switch (h_act->htype) {
4382 case BM_VERT:
4383 itertype = BM_VERTS_OF_MESH;
4384 walktype = BMW_CONNECTED_VERTEX;
4385 flushtype = SCE_SELECT_VERTEX;
4386 mask_vert = BMO_ELE_TAG;
4387 break;
4388 case BM_EDGE:
4389 /* When an edge has no connected-selected edges,
4390 * use face-stepping (supports edge-rings). */
4391 itertype = BM_EDGES_OF_MESH;
4393 flushtype = SCE_SELECT_EDGE;
4394 mask_edge = BMO_ELE_TAG;
4395 break;
4396 case BM_FACE:
4397 itertype = BM_FACES_OF_MESH;
4398 walktype = BMW_ISLAND;
4399 flushtype = SCE_SELECT_FACE;
4400 mask_face = BMO_ELE_TAG;
4401 break;
4402 }
4403
4404 /* Shouldn't need to allocate BMO flags here (sigh). */
4406
4407 /* Walker restrictions uses BMO flags, not header flags,
4408 * so transfer BM_ELEM_SELECT from HFlags onto a BMO flag layer. */
4409 BMO_push(bm, nullptr);
4410 BM_ITER_MESH (ele, &iter, bm, itertype) {
4413 }
4414 }
4415
4416 /* Walk over selected elements starting at active. */
4417 BMW_init(&walker,
4418 bm,
4419 walktype,
4420 mask_vert,
4421 mask_edge,
4422 mask_face,
4423 BMW_FLAG_NOP, /* Don't use #BMW_FLAG_TEST_HIDDEN here since we want to deselect all. */
4424 BMW_NIL_LAY);
4425
4426 /* Use tag to avoid touching the same verts twice. */
4427 BM_ITER_MESH (ele, &iter, bm, itertype) {
4429 }
4430
4432 for (ele = static_cast<BMElem *>(BMW_begin(&walker, h_act)); ele != nullptr;
4433 ele = static_cast<BMElem *>(BMW_step(&walker)))
4434 {
4435 if (!BM_elem_flag_test(ele, BM_ELEM_TAG)) {
4436 /* Deselect elements that aren't at "nth" depth from active. */
4437 const int depth = BMW_current_depth(&walker) - 1;
4438 if (!WM_operator_properties_checker_interval_test(op_params, depth)) {
4439 BM_elem_select_set(bm, ele, false);
4440 }
4442 }
4443 }
4444 BMW_end(&walker);
4445
4446 BMO_pop(bm);
4447
4448 /* Flush selection up. */
4449 EDBM_selectmode_flush_ex(em, flushtype);
4450}
4451
4452static void deselect_nth_active(BMEditMesh *em, BMVert **r_eve, BMEdge **r_eed, BMFace **r_efa)
4453{
4454 BMIter iter;
4455 BMElem *ele;
4456
4457 *r_eve = nullptr;
4458 *r_eed = nullptr;
4459 *r_efa = nullptr;
4460
4462 ele = BM_mesh_active_elem_get(em->bm);
4463
4464 if (ele && BM_elem_flag_test(ele, BM_ELEM_SELECT)) {
4465 switch (ele->head.htype) {
4466 case BM_VERT:
4467 *r_eve = (BMVert *)ele;
4468 return;
4469 case BM_EDGE:
4470 *r_eed = (BMEdge *)ele;
4471 return;
4472 case BM_FACE:
4473 *r_efa = (BMFace *)ele;
4474 return;
4475 }
4476 }
4477
4478 if (em->selectmode & SCE_SELECT_VERTEX) {
4479 BMVert *v;
4480 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
4482 *r_eve = v;
4483 return;
4484 }
4485 }
4486 }
4487 else if (em->selectmode & SCE_SELECT_EDGE) {
4488 BMEdge *e;
4489 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
4491 *r_eed = e;
4492 return;
4493 }
4494 }
4495 }
4496 else if (em->selectmode & SCE_SELECT_FACE) {
4497 BMFace *f = BM_mesh_active_face_get(em->bm, true, false);
4498 if (f && BM_elem_flag_test(f, BM_ELEM_SELECT)) {
4499 *r_efa = f;
4500 return;
4501 }
4502 }
4503}
4504
4505static bool edbm_deselect_nth(BMEditMesh *em, const CheckerIntervalParams *op_params)
4506{
4507 BMVert *v;
4508 BMEdge *e;
4509 BMFace *f;
4510
4511 deselect_nth_active(em, &v, &e, &f);
4512
4513 if (v) {
4514 walker_deselect_nth(em, op_params, &v->head);
4515 return true;
4516 }
4517 if (e) {
4518 walker_deselect_nth(em, op_params, &e->head);
4519 return true;
4520 }
4521 if (f) {
4522 walker_deselect_nth(em, op_params, &f->head);
4523 return true;
4524 }
4525
4526 return false;
4527}
4528
4530{
4531 const Scene *scene = CTX_data_scene(C);
4532 ViewLayer *view_layer = CTX_data_view_layer(C);
4533 CheckerIntervalParams op_params;
4535 bool found_active_elt = false;
4536
4538 scene, view_layer, CTX_wm_view3d(C));
4539
4540 for (Object *obedit : objects) {
4542
4543 if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) {
4544 continue;
4545 }
4546
4547 if (edbm_deselect_nth(em, &op_params) == true) {
4548 found_active_elt = true;
4550 params.calc_looptris = false;
4551 params.calc_normals = false;
4552 params.is_destructive = false;
4553 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
4554 }
4555 }
4556
4557 if (!found_active_elt) {
4558 BKE_report(op->reports, RPT_ERROR, "Mesh object(s) have no active vertex/edge/face");
4559 return OPERATOR_CANCELLED;
4560 }
4561
4562 return OPERATOR_FINISHED;
4563}
4564
4566{
4567 /* Identifiers. */
4568 ot->name = "Checker Deselect";
4569 ot->idname = "MESH_OT_select_nth";
4570 ot->description = "Deselect every Nth element starting from the active vertex, edge or face";
4571
4572 /* API callbacks. */
4573 ot->exec = edbm_select_nth_exec;
4574 ot->poll = ED_operator_editmesh;
4575
4576 /* Flags. */
4577 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
4578
4580}
4581
4583{
4586
4587 if (vc.obedit) {
4589 }
4590 return vc;
4591}
4592
4594
4595/* -------------------------------------------------------------------- */
4598
4600{
4601 /* Find edges that have exactly two neighboring faces,
4602 * check the angle between those faces, and if angle is
4603 * small enough, select the edge. */
4604 const float angle_limit_cos = cosf(RNA_float_get(op->ptr, "sharpness"));
4605
4606 const Scene *scene = CTX_data_scene(C);
4607 ViewLayer *view_layer = CTX_data_view_layer(C);
4609 scene, view_layer, CTX_wm_view3d(C));
4610
4611 for (Object *obedit : objects) {
4613 BMIter iter;
4614 BMEdge *e;
4615
4616 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
4618 continue;
4619 }
4620
4621 BMLoop *l_a, *l_b;
4622 if (BM_edge_loop_pair(e, &l_a, &l_b)) {
4623 /* Edge has exactly two neighboring faces, check angle. */
4624 const float angle_cos = dot_v3v3(l_a->f->no, l_b->f->no);
4625
4626 if (angle_cos < angle_limit_cos) {
4627 BM_edge_select_set(em->bm, e, true);
4628 }
4629 }
4630 }
4631
4632 if ((em->bm->selectmode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) == 0) {
4633 /* Since we can't select individual edges, select faces connected to them. */
4635 }
4636 else {
4638 }
4639 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
4640 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
4641 }
4642
4643 return OPERATOR_FINISHED;
4644}
4645
4647{
4648 PropertyRNA *prop;
4649
4650 /* Identifiers. */
4651 ot->name = "Select Sharp Edges";
4652 ot->description = "Select all sharp enough edges";
4653 ot->idname = "MESH_OT_edges_select_sharp";
4654
4655 /* API callbacks. */
4657 ot->poll = ED_operator_editmesh;
4658
4659 /* Flags. */
4660 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
4661
4662 /* Props. */
4663 prop = RNA_def_float_rotation(ot->srna,
4664 "sharpness",
4665 0,
4666 nullptr,
4667 DEG2RADF(0.01f),
4668 DEG2RADF(180.0f),
4669 "Sharpness",
4670 "",
4671 DEG2RADF(1.0f),
4672 DEG2RADF(180.0f));
4674}
4675
4677
4678/* -------------------------------------------------------------------- */
4681
4683{
4684 const Scene *scene = CTX_data_scene(C);
4685 ViewLayer *view_layer = CTX_data_view_layer(C);
4687 scene, view_layer, CTX_wm_view3d(C));
4688 const float angle_limit_cos = cosf(RNA_float_get(op->ptr, "sharpness"));
4689
4690 for (Object *obedit : objects) {
4692 BMesh *bm = em->bm;
4693
4694 if (bm->totfacesel == 0) {
4695 continue;
4696 }
4697
4699
4700 BMIter iter, liter, liter2;
4701 BMFace *f;
4702 BMLoop *l, *l2;
4703
4705
4706 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
4707 if ((BM_elem_flag_test(f, BM_ELEM_HIDDEN) != 0) ||
4709 {
4710 continue;
4711 }
4712
4713 BLI_assert(stack.is_empty());
4714
4715 do {
4716 BM_face_select_set(bm, f, true);
4717
4719
4720 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
4721 BM_ITER_ELEM (l2, &liter2, l, BM_LOOPS_OF_LOOP) {
4722 float angle_cos;
4723
4725 {
4726 continue;
4727 }
4728
4729 angle_cos = dot_v3v3(f->no, l2->f->no);
4730
4731 if (angle_cos > angle_limit_cos) {
4732 stack.append(l2->f);
4733 }
4734 }
4735 }
4736 } while (!stack.is_empty() && (f = stack.pop_last()));
4737 }
4738
4739 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
4740 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
4741 }
4742
4743 return OPERATOR_FINISHED;
4744}
4745
4747{
4748 PropertyRNA *prop;
4749
4750 /* Identifiers. */
4751 ot->name = "Select Linked Flat Faces";
4752 ot->description = "Select linked faces by angle";
4753 ot->idname = "MESH_OT_faces_select_linked_flat";
4754
4755 /* API callbacks. */
4757 ot->poll = ED_operator_editmesh;
4758
4759 /* Flags. */
4760 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
4761
4762 /* Props. */
4763 prop = RNA_def_float_rotation(ot->srna,
4764 "sharpness",
4765 0,
4766 nullptr,
4767 DEG2RADF(0.01f),
4768 DEG2RADF(180.0f),
4769 "Sharpness",
4770 "",
4771 DEG2RADF(1.0f),
4772 DEG2RADF(180.0f));
4774}
4775
4777
4778/* -------------------------------------------------------------------- */
4781
4783{
4784 const bool use_extend = RNA_boolean_get(op->ptr, "extend");
4785 const bool use_wire = RNA_boolean_get(op->ptr, "use_wire");
4786 const bool use_boundary = RNA_boolean_get(op->ptr, "use_boundary");
4787 const bool use_multi_face = RNA_boolean_get(op->ptr, "use_multi_face");
4788 const bool use_non_contiguous = RNA_boolean_get(op->ptr, "use_non_contiguous");
4789 const bool use_verts = RNA_boolean_get(op->ptr, "use_verts");
4790
4791 const Scene *scene = CTX_data_scene(C);
4792 ViewLayer *view_layer = CTX_data_view_layer(C);
4794 scene, view_layer, CTX_wm_view3d(C));
4795
4797
4798 for (Object *obedit : objects) {
4800 BMVert *v;
4801 BMEdge *e;
4802 BMIter iter;
4803
4804 bool changed = false;
4805
4806 if (!use_extend) {
4808 changed = true;
4809 }
4810
4811 /* Selects isolated verts, and edges that do not have 2 neighboring faces. */
4812 if (use_verts) {
4813 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
4815 continue;
4816 }
4817
4818 if (!BM_vert_is_manifold(v)) {
4819 BM_vert_select_set(em->bm, v, true);
4820 changed = true;
4821 }
4822 }
4823 }
4824
4825 if (use_wire || use_boundary || use_multi_face || use_non_contiguous) {
4826 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
4828 continue;
4829 }
4830 if ((use_wire && BM_edge_is_wire(e)) || (use_boundary && BM_edge_is_boundary(e)) ||
4831 (use_non_contiguous && (BM_edge_is_manifold(e) && !BM_edge_is_contiguous(e))) ||
4832 (use_multi_face && BM_edge_face_count_is_over(e, 2)))
4833 {
4834 /* Check we never select perfect edge (in test above). */
4836
4837 BM_edge_select_set(em->bm, e, true);
4838 changed = true;
4839 }
4840 }
4841 }
4842
4843 if (changed) {
4844 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
4845 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
4846
4848 }
4849 }
4850
4851 return OPERATOR_FINISHED;
4852}
4853
4855{
4856 /* Identifiers. */
4857 ot->name = "Select Non-Manifold";
4858 ot->description = "Select all non-manifold vertices or edges";
4859 ot->idname = "MESH_OT_select_non_manifold";
4860
4861 /* API callbacks */
4864
4865 /* Flags. */
4866 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
4867
4868 /* Props. */
4869 RNA_def_boolean(ot->srna, "extend", true, "Extend", "Extend the selection");
4870 /* Edges. */
4871 RNA_def_boolean(ot->srna, "use_wire", true, "Wire", "Wire edges");
4872 RNA_def_boolean(ot->srna, "use_boundary", true, "Boundaries", "Boundary edges");
4874 ot->srna, "use_multi_face", true, "Multiple Faces", "Edges shared by more than two faces");
4875 RNA_def_boolean(ot->srna,
4876 "use_non_contiguous",
4877 true,
4878 "Non Contiguous",
4879 "Edges between faces pointing in alternate directions");
4880 /* Verts. */
4882 ot->srna, "use_verts", true, "Vertices", "Vertices connecting multiple face regions");
4883}
4884
4886
4887/* -------------------------------------------------------------------- */
4890
4892{
4893 const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT);
4894 const float randfac = RNA_float_get(op->ptr, "ratio");
4896
4897 const Scene *scene = CTX_data_scene(C);
4898 ViewLayer *view_layer = CTX_data_view_layer(C);
4899
4901 scene, view_layer, CTX_wm_view3d(C));
4902 for (const int ob_index : objects.index_range()) {
4903 Object *obedit = objects[ob_index];
4905 BMIter iter;
4906 int seed_iter = seed;
4907
4908 /* This gives a consistent result regardless of object order. */
4909 if (ob_index) {
4910 seed_iter += BLI_ghashutil_strhash_p(obedit->id.name);
4911 }
4912
4913 if (em->selectmode & SCE_SELECT_VERTEX) {
4914 int elem_map_len = 0;
4915 BMVert **elem_map = static_cast<BMVert **>(
4916 MEM_mallocN(sizeof(*elem_map) * em->bm->totvert, __func__));
4917 BMVert *eve;
4918 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
4919 if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
4920 elem_map[elem_map_len++] = eve;
4921 }
4922 }
4923
4924 BLI_array_randomize(elem_map, sizeof(*elem_map), elem_map_len, seed_iter);
4925 const int count_select = elem_map_len * randfac;
4926 for (int i = 0; i < count_select; i++) {
4927 BM_vert_select_set(em->bm, elem_map[i], select);
4928 }
4929 MEM_freeN(elem_map);
4930 }
4931 else if (em->selectmode & SCE_SELECT_EDGE) {
4932 int elem_map_len = 0;
4933 BMEdge **elem_map = static_cast<BMEdge **>(
4934 MEM_mallocN(sizeof(*elem_map) * em->bm->totedge, __func__));
4935 BMEdge *eed;
4936 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
4937 if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) {
4938 elem_map[elem_map_len++] = eed;
4939 }
4940 }
4941 BLI_array_randomize(elem_map, sizeof(*elem_map), elem_map_len, seed_iter);
4942 const int count_select = elem_map_len * randfac;
4943 for (int i = 0; i < count_select; i++) {
4944 BM_edge_select_set(em->bm, elem_map[i], select);
4945 }
4946 MEM_freeN(elem_map);
4947 }
4948 else {
4949 int elem_map_len = 0;
4950 BMFace **elem_map = static_cast<BMFace **>(
4951 MEM_mallocN(sizeof(*elem_map) * em->bm->totface, __func__));
4952 BMFace *efa;
4953 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
4954 if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
4955 elem_map[elem_map_len++] = efa;
4956 }
4957 }
4958 BLI_array_randomize(elem_map, sizeof(*elem_map), elem_map_len, seed_iter);
4959 const int count_select = elem_map_len * randfac;
4960 for (int i = 0; i < count_select; i++) {
4961 BM_face_select_set(em->bm, elem_map[i], select);
4962 }
4963 MEM_freeN(elem_map);
4964 }
4965
4966 if (select) {
4967 /* Was #EDBM_select_flush, but it over selects in edge/face mode. */
4969 }
4970 else {
4972 }
4973
4974 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
4976 }
4977
4978 return OPERATOR_FINISHED;
4979}
4980
4982{
4983 /* Identifiers. */
4984 ot->name = "Select Random";
4985 ot->description = "Randomly select vertices";
4986 ot->idname = "MESH_OT_select_random";
4987
4988 /* API callbacks */
4990 ot->poll = ED_operator_editmesh;
4991
4992 /* Flags. */
4993 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
4994
4995 /* Props. */
4997}
4998
5000
5001/* -------------------------------------------------------------------- */
5004
5006{
5007 if (ED_operator_editmesh(C)) {
5008 Object *obedit = CTX_data_edit_object(C);
5010 const int cd_dvert_offset = CustomData_get_offset(&em->bm->vdata, CD_MDEFORMVERT);
5011
5012 const ListBase *defbase = BKE_object_defgroup_list(obedit);
5013 if ((em->selectmode & SCE_SELECT_VERTEX) == 0) {
5014 CTX_wm_operator_poll_msg_set(C, "Must be in vertex selection mode");
5015 }
5016 else if (BLI_listbase_is_empty(defbase) || cd_dvert_offset == -1) {
5017 CTX_wm_operator_poll_msg_set(C, "No weights/vertex groups on object");
5018 }
5019 else {
5020 return true;
5021 }
5022 }
5023 return false;
5024}
5025
5027{
5028 const bool extend = RNA_boolean_get(op->ptr, "extend");
5029 const Scene *scene = CTX_data_scene(C);
5030 ViewLayer *view_layer = CTX_data_view_layer(C);
5031
5033 scene, view_layer, CTX_wm_view3d(C));
5034
5035 for (Object *obedit : objects) {
5037
5038 const int cd_dvert_offset = CustomData_get_offset(&em->bm->vdata, CD_MDEFORMVERT);
5039
5040 if (cd_dvert_offset == -1) {
5041 continue;
5042 }
5043
5044 BMVert *eve;
5045 BMIter iter;
5046
5047 bool changed = false;
5048
5049 if (!extend) {
5050 if (em->bm->totvertsel) {
5052 changed = true;
5053 }
5054 }
5055
5056 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
5057 if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
5058 MDeformVert *dv = static_cast<MDeformVert *>(BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset));
5059 /* Skip `dv` or `dv` set with zero weight. */
5060 if (ELEM(nullptr, dv, dv->dw)) {
5061 BM_vert_select_set(em->bm, eve, true);
5062 changed = true;
5063 }
5064 }
5065 }
5066
5067 if (changed) {
5069 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
5070 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
5071 }
5072 }
5073 return OPERATOR_FINISHED;
5074}
5075
5077{
5078 /* Identifiers. */
5079 ot->name = "Select Ungrouped";
5080 ot->idname = "MESH_OT_select_ungrouped";
5081 ot->description = "Select vertices without a group";
5082
5083 /* API callbacks. */
5086
5087 /* Flags. */
5088 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5089
5090 RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
5091}
5092
5094
5095/* -------------------------------------------------------------------- */
5098
5099enum {
5103};
5104
5106{
5107 Scene *scene = CTX_data_scene(C);
5108 ViewLayer *view_layer = CTX_data_view_layer(C);
5109 Object *obedit = CTX_data_edit_object(C);
5111 BMVert *v_act = BM_mesh_active_vert_get(em->bm);
5112 const int orientation = RNA_enum_get(op->ptr, "orientation");
5113 const int axis = RNA_enum_get(op->ptr, "axis");
5114 const int sign = RNA_enum_get(op->ptr, "sign");
5115
5116 if (v_act == nullptr) {
5117 BKE_report(
5118 op->reports, RPT_WARNING, "This operator requires an active vertex (last selected)");
5119 return OPERATOR_CANCELLED;
5120 }
5121
5122 const float limit = RNA_float_get(op->ptr, "threshold");
5123
5124 float value;
5125 float axis_mat[3][3];
5126
5127 /* 3D view variables may be nullptr, (no need to check in poll function). */
5129 view_layer,
5132 obedit,
5133 obedit,
5134 orientation,
5136 axis_mat);
5137
5138 const float *axis_vector = axis_mat[axis];
5139
5140 {
5141 float vertex_world[3];
5142 mul_v3_m4v3(vertex_world, obedit->object_to_world().ptr(), v_act->co);
5143 value = dot_v3v3(axis_vector, vertex_world);
5144 }
5145
5146 if (sign == SELECT_AXIS_NEG) {
5147 value += limit;
5148 }
5149 else if (sign == SELECT_AXIS_POS) {
5150 value -= limit;
5151 }
5152
5154 scene, view_layer, CTX_wm_view3d(C));
5155 for (Object *obedit_iter : objects) {
5156 BMEditMesh *em_iter = BKE_editmesh_from_object(obedit_iter);
5157 BMesh *bm = em_iter->bm;
5158
5159 if (bm->totvert == bm->totvertsel) {
5160 continue;
5161 }
5162
5163 BMIter iter;
5164 BMVert *v;
5165 bool changed = false;
5166
5167 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
5169 float v_iter_world[3];
5170 mul_v3_m4v3(v_iter_world, obedit_iter->object_to_world().ptr(), v->co);
5171 const float value_iter = dot_v3v3(axis_vector, v_iter_world);
5172 switch (sign) {
5173 case SELECT_AXIS_ALIGN:
5174 if (fabsf(value_iter - value) < limit) {
5175 BM_vert_select_set(bm, v, true);
5176 changed = true;
5177 }
5178 break;
5179 case SELECT_AXIS_NEG:
5180 if (value_iter < value) {
5181 BM_vert_select_set(bm, v, true);
5182 changed = true;
5183 }
5184 break;
5185 case SELECT_AXIS_POS:
5186 if (value_iter > value) {
5187 BM_vert_select_set(bm, v, true);
5188 changed = true;
5189 }
5190 break;
5191 }
5192 }
5193 }
5194 if (changed) {
5195 EDBM_selectmode_flush(em_iter);
5196 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit_iter->data);
5197 DEG_id_tag_update(static_cast<ID *>(obedit_iter->data), ID_RECALC_SELECT);
5198 }
5199 }
5200 return OPERATOR_FINISHED;
5201}
5202
5204{
5205 static const EnumPropertyItem axis_sign_items[] = {
5206 {SELECT_AXIS_POS, "POS", false, "Positive Axis", ""},
5207 {SELECT_AXIS_NEG, "NEG", false, "Negative Axis", ""},
5208 {SELECT_AXIS_ALIGN, "ALIGN", false, "Aligned Axis", ""},
5209 {0, nullptr, 0, nullptr, nullptr},
5210 };
5211
5212 /* Identifiers. */
5213 ot->name = "Select Axis";
5214 ot->description = "Select all data in the mesh on a single axis";
5215 ot->idname = "MESH_OT_select_axis";
5216
5217 /* API callbacks. */
5218 ot->exec = edbm_select_axis_exec;
5219 ot->poll = ED_operator_editmesh;
5220
5221 /* Flags. */
5222 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5223
5224 /* Properties. */
5225 RNA_def_enum(ot->srna,
5226 "orientation",
5229 "Axis Mode",
5230 "Axis orientation");
5231 RNA_def_enum(ot->srna, "sign", axis_sign_items, SELECT_AXIS_POS, "Axis Sign", "Side to select");
5232 RNA_def_enum(ot->srna,
5233 "axis",
5235 0,
5236 "Axis",
5237 "Select the axis to compare each vertex on");
5239 ot->srna, "threshold", 0.0001f, 0.000001f, 50.0f, "Threshold", "", 0.00001f, 10.0f);
5240}
5241
5243
5244/* -------------------------------------------------------------------- */
5247
5249{
5250 const Scene *scene = CTX_data_scene(C);
5251 ViewLayer *view_layer = CTX_data_view_layer(C);
5253 scene, view_layer, CTX_wm_view3d(C));
5254 bool changed = false;
5255 for (Object *obedit : objects) {
5257
5258 if (em->bm->totfacesel == 0) {
5259 continue;
5260 }
5261 BMFace *f;
5262 BMEdge *e;
5263 BMIter iter;
5264
5266
5267 BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
5268 BMLoop *l1, *l2;
5269 BMIter liter1, liter2;
5270
5271 BM_ITER_ELEM (l1, &liter1, f, BM_LOOPS_OF_FACE) {
5272 int tot = 0, totsel = 0;
5273
5274 BM_ITER_ELEM (l2, &liter2, l1->e, BM_LOOPS_OF_EDGE) {
5275 tot++;
5276 totsel += BM_elem_flag_test(l2->f, BM_ELEM_SELECT) != 0;
5277 }
5278
5279 if ((tot != totsel && totsel > 0) || (totsel == 1 && tot == 1)) {
5281 }
5282 }
5283 }
5284
5286
5287 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
5289 BM_edge_select_set(em->bm, e, true);
5290 changed = true;
5291 }
5292 }
5293
5295 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
5296 }
5297
5298 if (changed) {
5299 /* If in face-only select mode, switch to edge select mode so that
5300 * an edge-only selection is not inconsistent state. Do this for all meshes in multi-object
5301 * editmode so their selectmode is in sync for following operators. */
5303 }
5304
5305 return OPERATOR_FINISHED;
5306}
5307
5309{
5310 /* Identifiers. */
5311 ot->name = "Select Boundary Loop";
5312 ot->idname = "MESH_OT_region_to_loop";
5313 ot->description = "Select boundary edges around the selected faces";
5314
5315 /* API callbacks. */
5317 ot->poll = ED_operator_editmesh;
5318
5319 /* Flags. */
5320 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5321}
5322
5324
5325/* -------------------------------------------------------------------- */
5328
5329static int loop_find_region(BMLoop *l, int flag, GSet *visit_face_set, BMFace ***region_out)
5330{
5333
5334 stack.append(l->f);
5335 BLI_gset_insert(visit_face_set, l->f);
5336
5337 while (!stack.is_empty()) {
5338 BMIter liter1, liter2;
5339 BMLoop *l1, *l2;
5340
5341 BMFace *f = stack.pop_last();
5342 region.append(f);
5343
5344 BM_ITER_ELEM (l1, &liter1, f, BM_LOOPS_OF_FACE) {
5345 if (BM_elem_flag_test(l1->e, flag)) {
5346 continue;
5347 }
5348
5349 BM_ITER_ELEM (l2, &liter2, l1->e, BM_LOOPS_OF_EDGE) {
5350 /* Avoids finding same region twice
5351 * (otherwise) the logic works fine without. */
5352 if (BM_elem_flag_test(l2->f, BM_ELEM_TAG)) {
5353 continue;
5354 }
5355
5356 if (BLI_gset_add(visit_face_set, l2->f)) {
5357 stack.append(l2->f);
5358 }
5359 }
5360 }
5361 }
5362
5363 BMFace **region_alloc = MEM_malloc_arrayN<BMFace *>(region.size(), __func__);
5364 memcpy(region_alloc, region.data(), region.as_span().size_in_bytes());
5365 *region_out = region_alloc;
5366 return region.size();
5367}
5368
5369static int verg_radial(const void *va, const void *vb)
5370{
5371 const BMEdge *e_a = *((const BMEdge **)va);
5372 const BMEdge *e_b = *((const BMEdge **)vb);
5373
5374 const int a = BM_edge_face_count(e_a);
5375 const int b = BM_edge_face_count(e_b);
5376
5377 if (a > b) {
5378 return -1;
5379 }
5380 if (a < b) {
5381 return 1;
5382 }
5383 return 0;
5384}
5385
5392static int loop_find_regions(BMEditMesh *em, const bool selbigger)
5393{
5394 GSet *visit_face_set;
5395 BMIter iter;
5396 const int edges_len = em->bm->totedgesel;
5397 BMEdge *e;
5398 int count = 0, i;
5399
5400 visit_face_set = BLI_gset_ptr_new_ex(__func__, edges_len);
5401 BMEdge **edges = MEM_malloc_arrayN<BMEdge *>(edges_len, __func__);
5402
5403 i = 0;
5404 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
5406 edges[i++] = e;
5408 }
5409 else {
5411 }
5412 }
5413
5414 /* Sort edges by radial cycle length. */
5415 qsort(edges, edges_len, sizeof(*edges), verg_radial);
5416
5417 for (i = 0; i < edges_len; i++) {
5418 BMIter liter;
5419 BMLoop *l;
5420 BMFace **region = nullptr, **region_out;
5421 int c, tot = 0;
5422
5423 e = edges[i];
5424
5426 continue;
5427 }
5428
5429 BM_ITER_ELEM (l, &liter, e, BM_LOOPS_OF_EDGE) {
5430 if (BLI_gset_haskey(visit_face_set, l->f)) {
5431 continue;
5432 }
5433
5434 c = loop_find_region(l, BM_ELEM_SELECT, visit_face_set, &region_out);
5435
5436 if (!region || (selbigger ? c >= tot : c < tot)) {
5437 /* This region is the best seen so far. */
5438 tot = c;
5439 if (region) {
5440 /* Free the previous best. */
5441 MEM_freeN(region);
5442 }
5443 /* Track the current region as the new best. */
5444 region = region_out;
5445 }
5446 else {
5447 /* This region is not as good as best so far, just free it. */
5448 MEM_freeN(region_out);
5449 }
5450 }
5451
5452 if (region) {
5453 int j;
5454
5455 for (j = 0; j < tot; j++) {
5456 BM_elem_flag_enable(region[j], BM_ELEM_TAG);
5457 BM_ITER_ELEM (l, &liter, region[j], BM_LOOPS_OF_FACE) {
5459 }
5460 }
5461
5462 count += tot;
5463
5464 MEM_freeN(region);
5465 }
5466 }
5467
5468 MEM_freeN(edges);
5469 BLI_gset_free(visit_face_set, nullptr);
5470
5471 return count;
5472}
5473
5475{
5476 const bool select_bigger = RNA_boolean_get(op->ptr, "select_bigger");
5477
5478 const Scene *scene = CTX_data_scene(C);
5479 ViewLayer *view_layer = CTX_data_view_layer(C);
5481 scene, view_layer, CTX_wm_view3d(C));
5482 for (Object *obedit : objects) {
5484
5485 if (em->bm->totedgesel == 0) {
5486 continue;
5487 }
5488
5489 BMIter iter;
5490 BMFace *f;
5491
5492 /* Find the set of regions with smallest number of total faces. */
5494 const int a = loop_find_regions(em, select_bigger);
5495 const int b = loop_find_regions(em, !select_bigger);
5496
5498 loop_find_regions(em, ((a <= b) != select_bigger) ? select_bigger : !select_bigger);
5499
5500 /* Unlike most operators, always de-select all. */
5501 bool changed = true;
5503
5504 BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
5506 continue;
5507 }
5509 BM_face_select_set(em->bm, f, true);
5510 }
5511 }
5512
5513 if (changed) {
5515
5516 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
5517 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
5518 }
5519 }
5520
5521 return OPERATOR_FINISHED;
5522}
5523
5525{
5526 /* Identifiers. */
5527 ot->name = "Select Loop Inner-Region";
5528 ot->idname = "MESH_OT_loop_to_region";
5529 ot->description = "Select region of faces inside of a selected loop of edges";
5530
5531 /* API callbacks. */
5533 ot->poll = ED_operator_editmesh;
5534
5535 /* Flags. */
5536 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5537
5538 RNA_def_boolean(ot->srna,
5539 "select_bigger",
5540 false,
5541 "Select Bigger",
5542 "Select bigger regions instead of smaller ones");
5543}
5544
5546{
5547 using namespace blender;
5548 if (!ED_operator_editmesh(C)) {
5549 return false;
5550 }
5551 Object *obedit = CTX_data_edit_object(C);
5552 const Mesh *mesh = static_cast<const Mesh *>(obedit->data);
5553 AttributeOwner owner = AttributeOwner::from_id(&const_cast<ID &>(mesh->id));
5554 const std::optional<StringRef> name = BKE_attributes_active_name_get(owner);
5555 if (!name) {
5556 CTX_wm_operator_poll_msg_set(C, "There must be an active attribute");
5557 return false;
5558 }
5560 owner, *name, CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL);
5561 if (layer->type != CD_PROP_BOOL) {
5562 CTX_wm_operator_poll_msg_set(C, "The active attribute must have a boolean type");
5563 return false;
5564 }
5565 if (BKE_attribute_domain(owner, layer) == bke::AttrDomain::Corner) {
5567 C, "The active attribute must be on the vertex, edge, or face domain");
5568 return false;
5569 }
5570 return true;
5571}
5572
5573static std::optional<BMIterType> domain_to_iter_type(const blender::bke::AttrDomain domain)
5574{
5575 using namespace blender;
5576 switch (domain) {
5578 return BM_VERTS_OF_MESH;
5580 return BM_EDGES_OF_MESH;
5582 return BM_FACES_OF_MESH;
5583 default:
5584 return std::nullopt;
5585 }
5586}
5587
5589{
5590 using namespace blender;
5591 const Scene *scene = CTX_data_scene(C);
5592 ViewLayer *view_layer = CTX_data_view_layer(C);
5594 scene, view_layer, CTX_wm_view3d(C));
5595 for (Object *obedit : objects) {
5596 Mesh *mesh = static_cast<Mesh *>(obedit->data);
5598 BMesh *bm = em->bm;
5600 const std::optional<StringRef> name = BKE_attributes_active_name_get(owner);
5601 if (!name) {
5602 continue;
5603 }
5605 owner, *name, CD_MASK_PROP_ALL, ATTR_DOMAIN_MASK_ALL);
5606 if (!layer) {
5607 continue;
5608 }
5609 if (layer->type != CD_PROP_BOOL) {
5610 continue;
5611 }
5612 if (BKE_attribute_domain(owner, layer) == bke::AttrDomain::Corner) {
5613 continue;
5614 }
5615 const std::optional<BMIterType> iter_type = domain_to_iter_type(
5616 BKE_attribute_domain(owner, layer));
5617 if (!iter_type) {
5618 continue;
5619 }
5620
5621 bool changed = false;
5622 BMElem *elem;
5623 BMIter iter;
5624 BM_ITER_MESH (elem, &iter, bm, *iter_type) {
5626 continue;
5627 }
5628 if (BM_ELEM_CD_GET_BOOL(elem, layer->offset)) {
5629 BM_elem_select_set(bm, elem, true);
5630 changed = true;
5631 }
5632 }
5633
5634 if (changed) {
5636
5637 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
5638 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
5639 }
5640 }
5641
5642 return OPERATOR_FINISHED;
5643}
5644
5646{
5647 ot->name = "Select by Attribute";
5648 ot->idname = "MESH_OT_select_by_attribute";
5649 ot->description = "Select elements based on the active boolean attribute";
5650
5653
5654 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5655}
5656
std::optional< blender::StringRefNull > BKE_attributes_active_name_get(AttributeOwner &owner)
Definition attribute.cc:802
blender::bke::AttrDomain BKE_attribute_domain(const AttributeOwner &owner, const struct CustomDataLayer *layer)
@ ATTR_DOMAIN_MASK_ALL
const struct CustomDataLayer * BKE_attribute_search(const AttributeOwner &owner, blender::StringRef name, eCustomDataMask type, AttrDomainMask domain_mask)
Definition attribute.cc:657
SpaceImage * CTX_wm_space_image(const bContext *C)
void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg)
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Object * CTX_data_edit_object(const bContext *C)
ToolSettings * CTX_data_tool_settings(const bContext *C)
RegionView3D * CTX_wm_region_view3d(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
CustomData interface, see also DNA_customdata_types.h.
int CustomData_get_offset(const CustomData *data, eCustomDataType type)
bool CustomData_has_layer(const CustomData *data, eCustomDataType type)
support for deformation groups and hooks.
const ListBase * BKE_object_defgroup_list(const Object *ob)
Definition deform.cc:574
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:61
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
blender::Vector< Base * > BKE_view_layer_array_from_bases_in_edit_mode(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
Base * BKE_view_layer_active_base_get(ViewLayer *view_layer)
blender::Vector< Object * > BKE_view_layer_array_from_objects_in_edit_mode(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
Object * BKE_view_layer_active_object_get(const ViewLayer *view_layer)
Base * BKE_view_layer_base_find(ViewLayer *view_layer, Object *ob)
blender::Vector< Base * > BKE_view_layer_array_from_bases_in_edit_mode_unique_data(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
blender::Vector< Object * > BKE_view_layer_array_from_objects_in_edit_mode_unique_data(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
blender::Span< blender::float3 > BKE_mesh_wrapper_vert_coords(const Mesh *mesh)
int BKE_mesh_wrapper_vert_len(const Mesh *mesh)
General operations, lookup, etc. for blender objects.
const Mesh * BKE_object_get_editmesh_eval_cage(const Object *object)
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:126
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
struct GSet GSet
Definition BLI_ghash.h:337
bool BLI_gset_haskey(const GSet *gs, const void *key) ATTR_WARN_UNUSED_RESULT
unsigned int BLI_ghashutil_strhash_p(const void *ptr)
void BLI_gset_insert(GSet *gs, void *key)
Definition BLI_ghash.cc:959
void BLI_gset_free(GSet *gs, GSetKeyFreeFP keyfreefp)
GSet * BLI_gset_ptr_new_ex(const char *info, unsigned int nentries_reserve) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
bool BLI_gset_add(GSet *gs, void *key)
Definition BLI_ghash.cc:966
A min-heap / priority queue ADT.
HeapNode * BLI_heap_top(const Heap *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition BLI_heap.cc:279
void BLI_heap_free(Heap *heap, HeapFreeFP ptrfreefp) ATTR_NONNULL(1)
Definition BLI_heap.cc:191
void void float BLI_heap_node_value(const HeapNode *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition BLI_heap.cc:347
Heap * BLI_heap_new_ex(unsigned int reserve_num) ATTR_WARN_UNUSED_RESULT
Definition BLI_heap.cc:171
void void bool BLI_heap_is_empty(const Heap *heap) ATTR_NONNULL(1)
Definition BLI_heap.cc:269
void BLI_heap_node_value_update(Heap *heap, HeapNode *node, float value) ATTR_NONNULL(1
void * BLI_heap_pop_min(Heap *heap) ATTR_NONNULL(1)
Definition BLI_heap.cc:291
void * BLI_heap_node_ptr(const HeapNode *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition BLI_heap.cc:352
HeapNode * BLI_heap_insert(Heap *heap, float value, void *ptr) ATTR_NONNULL(1)
Definition BLI_heap.cc:234
void void BLI_heap_remove(Heap *heap, HeapNode *node) ATTR_NONNULL(1
void void void BLI_movelisttolist(ListBase *dst, ListBase *src) ATTR_NONNULL(1
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
void BLI_freelinkN(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:270
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void * BLI_pophead(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:252
MINLINE float min_ff(float a, float b)
MINLINE unsigned short highest_order_bit_s(unsigned short n)
#define DEG2RADF(_deg)
float line_point_factor_v2(const float p[2], const float l1[2], const float l2[2])
float dist_squared_ray_to_seg_v3(const float ray_origin[3], const float ray_direction[3], const float v0[3], const float v1[3], float r_point[3], float *r_depth)
Definition math_geom.cc:613
float dist_squared_to_ray_v3_normalized(const float ray_origin[3], const float ray_direction[3], const float co[3])
Definition math_geom.cc:597
void copy_m3_m4(float m1[3][3], const float m2[4][4])
void mul_m4_v3(const float M[4][4], float r[3])
void mul_v3_m4v3(float r[3], const float mat[4][4], const float vec[3])
bool invert_m3(float mat[3][3])
MINLINE float len_squared_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
void interp_v2_v2v2(float r[2], const float a[2], const float b[2], float t)
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], float t)
void mid_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE float len_manhattan_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
void mid_v3_v3v3(float r[3], const float a[3], const float b[3])
float angle_normalized_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT
Random number functions.
void BLI_array_randomize(void *data, unsigned int elem_size, unsigned int elem_num, unsigned int seed)
Definition rand.cc:156
unsigned int uint
#define UNUSED_VARS(...)
#define POINTER_FROM_INT(i)
#define POINTER_AS_INT(i)
#define ELEM(...)
#define STACK_CLEAR(stack)
#define STACK_PUSH(stack, val)
#define STACK_DECLARE(stack)
#define STACK_SIZE(stack)
#define STACK_INIT(stack, stack_num)
#define TIP_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
@ ID_RECALC_SELECT
Definition DNA_ID.h:1009
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1026
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:982
@ CD_MDEFORMVERT
@ CD_PROP_FLOAT2
@ ME_EDIT_MIRROR_TOPO
@ OB_MODE_EDIT
Object is a sort of wrapper for general info.
@ OB_MESH
@ UV_SYNC_SELECTION
@ SCE_SELECT_FACE
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
@ V3D_ORIENT_LOCAL
#define RV3D_CLIPPING_ENABLED(v3d, rv3d)
@ V3D_AROUND_ACTIVE
@ RV3D_CLIPPING
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_PASS_THROUGH
void DRW_select_buffer_context_create(Depsgraph *depsgraph, blender::Span< Base * > bases, short select_mode)
uint DRW_select_buffer_sample_point(Depsgraph *depsgraph, ARegion *region, View3D *v3d, const int center[2])
bool DRW_select_buffer_elem_get(uint sel_id, uint &r_elem, uint &r_base_index, char &r_elem_type)
uint DRW_select_buffer_find_nearest_to_point(Depsgraph *depsgraph, ARegion *region, View3D *v3d, const int center[2], uint id_min, uint id_max, uint *dist)
void EDBM_flag_disable_all(BMEditMesh *em, char hflag)
void ED_mesh_report_mirror_ex(wmOperator *op, int totmirr, int totfail, char selectmode)
void EDBM_update(Mesh *mesh, const EDBMUpdate_Params *params)
BMVert * EDBM_verts_mirror_get(BMEditMesh *em, BMVert *v)
void EDBM_select_more(BMEditMesh *em, bool use_face_step)
void EDBM_verts_mirror_cache_begin(BMEditMesh *em, int axis, bool use_self, bool use_select, bool respecthide, bool use_topology)
void EDBM_deselect_flush(BMEditMesh *em)
void EDBM_selectmode_flush(BMEditMesh *em)
void EDBM_selectmode_flush_ex(BMEditMesh *em, short selectmode)
void EDBM_verts_mirror_cache_end(BMEditMesh *em)
BMEdge * EDBM_verts_mirror_get_edge(BMEditMesh *em, BMEdge *e)
BMFace * EDBM_verts_mirror_get_face(BMEditMesh *em, BMFace *f)
void EDBM_flag_enable_all(BMEditMesh *em, char hflag)
void EDBM_select_less(BMEditMesh *em, bool use_face_step)
void EDBM_select_flush(BMEditMesh *em)
bool ED_operator_editmesh_region_view3d(bContext *C)
bool ED_operator_editmesh(bContext *C)
@ SEL_OP_ADD
@ SEL_OP_SUB
@ SEL_OP_SET
@ SEL_OP_AND
@ SEL_OP_XOR
@ SEL_SELECT
@ SEL_INVERT
@ SEL_DESELECT
@ SEL_TOGGLE
bool ED_view3d_win_to_ray_clipped(Depsgraph *depsgraph, const ARegion *region, const View3D *v3d, const float mval[2], float r_ray_start[3], float r_ray_normal[3], bool do_clip_planes)
float ED_view3d_select_dist_px()
#define V3D_PROJ_TEST_CLIP_CONTENT_DEFAULT
Definition ED_view3d.hh:315
void mesh_foreachScreenVert(const ViewContext *vc, void(*func)(void *user_data, BMVert *eve, const float screen_co[2], int index), void *user_data, eV3DProjTest clip_flag)
eV3DProjTest
Definition ED_view3d.hh:278
@ V3D_PROJ_TEST_CLIP_NEAR
Definition ED_view3d.hh:282
@ V3D_PROJ_TEST_CLIP_BB
Definition ED_view3d.hh:280
eV3DProjStatus ED_view3d_project_float_object(const ARegion *region, const float co[3], float r_co[2], eV3DProjTest flag)
void ED_view3d_init_mats_rv3d(const Object *ob, RegionView3D *rv3d)
@ V3D_PROJ_RET_OK
Definition ED_view3d.hh:256
void mesh_foreachScreenEdge(const ViewContext *vc, void(*func)(void *user_data, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int index), void *user_data, eV3DProjTest clip_flag)
ViewContext ED_view3d_viewcontext_init(bContext *C, Depsgraph *depsgraph)
int ED_view3d_backbuf_sample_size_clamp(ARegion *region, float dist)
void mesh_foreachScreenFace(const ViewContext *vc, void(*func)(void *user_data, BMFace *efa, const float screen_co[2], int index), void *user_data, eV3DProjTest clip_flag)
#define V3D_PROJ_TEST_CLIP_DEFAULT
Definition ED_view3d.hh:309
#define XRAY_FLAG_ENABLED(v3d)
void view3d_operator_needs_gpu(const bContext *C)
bool ED_view3d_clipping_test(const RegionView3D *rv3d, const float co[3], bool is_local)
void ED_view3d_viewcontext_init_object(ViewContext *vc, Object *obact)
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
Read Guarded memory(de)allocation.
@ PROP_SKIP_SAVE
Definition RNA_types.hh:330
@ PROP_HIDDEN
Definition RNA_types.hh:324
#define C
Definition RandGen.cpp:29
#define NC_GEOM
Definition WM_types.hh:390
@ KM_CTRL
Definition WM_types.hh:276
@ KM_SHIFT
Definition WM_types.hh:275
#define ND_DATA
Definition WM_types.hh:506
#define NC_SCENE
Definition WM_types.hh:375
#define ND_TOOLSETTINGS
Definition WM_types.hh:446
#define ND_SELECT
Definition WM_types.hh:505
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define BM_ELEM_CD_GET_BOOL(ele, offset)
@ BM_ELEM_HIDDEN
@ BM_ELEM_SEAM
@ BM_ELEM_SELECT
@ BM_ELEM_SMOOTH
@ BM_ELEM_TAG
#define BM_FACE_FIRST_LOOP(p)
@ BM_LOOP
#define BM_ELEM_CD_GET_VOID_P(ele, offset)
#define BM_elem_index_get(ele)
#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)
#define BM_elem_flag_enable(ele, hflag)
#define BM_ITER_ELEM(ele, iter, data, itype)
#define BM_ITER_MESH(ele, iter, bm, itype)
#define BM_ITER_MESH_INDEX(ele, iter, bm, itype, indexvar)
@ BM_LOOPS_OF_LOOP
@ BM_FACES_OF_EDGE
@ BM_FACES_OF_VERT
@ BM_EDGES_OF_MESH
@ BM_VERTS_OF_MESH
@ BM_VERTS_OF_EDGE
@ BM_FACES_OF_MESH
@ BM_LOOPS_OF_EDGE
@ BM_EDGES_OF_VERT
@ BM_LOOPS_OF_FACE
BMesh const char void * data
BMesh * bm
BMVert * BM_mesh_active_vert_get(BMesh *bm)
void BM_face_select_set(BMesh *bm, BMFace *f, const bool select)
Select Face.
void BM_elem_select_set(BMesh *bm, BMElem *ele, const bool select)
BMFace * BM_mesh_active_face_get(BMesh *bm, const bool is_sloppy, const bool is_selected)
BMElem * BM_mesh_active_elem_get(BMesh *bm)
void BM_vert_select_set(BMesh *bm, BMVert *v, const bool select)
Select Vert.
void BM_edge_select_set(BMesh *bm, BMEdge *e, const bool select)
Select Edge.
void BM_mesh_deselect_flush(BMesh *bm)
void BM_mesh_elem_hflag_disable_all(BMesh *bm, const char htype, const char hflag, const bool respecthide)
void BM_mesh_active_face_set(BMesh *bm, BMFace *f)
#define BM_select_history_store(bm, ele)
#define BM_select_history_remove(bm, ele)
void BM_mesh_elem_toolflags_clear(BMesh *bm)
void BM_mesh_elem_toolflags_ensure(BMesh *bm)
Definition bmesh_mesh.cc:82
BMVert * BM_vert_at_index_find_or_table(BMesh *bm, const int index)
void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
BMEdge * BM_edge_at_index_find_or_table(BMesh *bm, const int index)
BMFace * BM_face_at_index_find_or_table(BMesh *bm, const int index)
BLI_INLINE BMFace * BM_face_at_index(BMesh *bm, const int index)
#define BM_FACE
#define BM_EDGE
#define BM_VERT
#define BMO_edge_flag_test(bm, e, oflag)
void BMO_pop(BMesh *bm)
BMESH OPSTACK POP.
#define BMO_edge_flag_set(bm, e, oflag, val)
void BMO_push(BMesh *bm, BMOperator *op)
BMESH OPSTACK PUSH.
#define BMO_elem_flag_enable(bm, ele, oflag)
@ BMO_DELIM_NORMAL
@ BMO_DELIM_MATERIAL
@ BMO_DELIM_SEAM
@ BMO_DELIM_SHARP
@ BMO_DELIM_UV
void BM_face_calc_center_median_vcos(const BMesh *bm, const BMFace *f, float r_cent[3], const blender::Span< blender::float3 > vert_positions)
float BM_face_calc_area(const BMFace *f)
void BM_face_calc_center_median(const BMFace *f, float r_cent[3])
bool BM_edge_is_contiguous_loop_cd(const BMEdge *e, const int cd_loop_type, const int cd_loop_offset)
bool BM_edge_is_all_face_flag_test(const BMEdge *e, const char hflag, const bool respect_hide)
int BM_vert_edge_count_at_most(const BMVert *v, const int count_max)
int BM_mesh_calc_face_groups(BMesh *bm, int *r_groups_array, int(**r_group_index)[2], BMLoopFilterFunc filter_fn, BMLoopPairFilterFunc filter_pair_fn, void *user_data, const char hflag_test, const char htype_step)
bool BM_face_is_any_edge_flag_test(const BMFace *f, const char hflag)
bool BM_face_is_any_vert_flag_test(const BMFace *f, const char hflag)
bool BM_edge_loop_pair(BMEdge *e, BMLoop **r_la, BMLoop **r_lb)
bool BM_vert_is_all_edge_flag_test(const BMVert *v, const char hflag, const bool respect_hide)
bool BM_vert_is_manifold(const BMVert *v)
int BM_edge_face_count(const BMEdge *e)
bool BM_edge_is_any_vert_flag_test(const BMEdge *e, const char hflag)
float BM_edge_calc_length(const BMEdge *e)
bool BM_vert_is_all_face_flag_test(const BMVert *v, const char hflag, const bool respect_hide)
bool BM_edge_is_any_face_flag_test(const BMEdge *e, const char hflag)
BLI_INLINE bool BM_edge_is_contiguous(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE bool BM_edge_is_manifold(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE bool BM_edge_is_boundary(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
#define BM_edge_face_count_is_over(e, n)
BLI_INLINE bool BM_edge_is_wire(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMLoop * l_b
ATTR_WARN_UNUSED_RESULT const BMVert * v
int BM_mesh_region_match(BMesh *bm, BMFace **faces_region, uint faces_region_len, ListBase *r_face_regions)
void * BMW_begin(BMWalker *walker, void *start)
void BMW_init(BMWalker *walker, BMesh *bm, int type, short mask_vert, short mask_edge, short mask_face, BMWFlag flag, int layer)
Initialize Walker.
void BMW_end(BMWalker *walker)
End Walker.
void * BMW_step(BMWalker *walker)
Step Walker.
int BMW_current_depth(BMWalker *walker)
Walker Current Depth.
@ BMW_BREADTH_FIRST
@ BMW_EDGERING
@ BMW_CONNECTED_VERTEX
@ BMW_FACELOOP
@ BMW_EDGELOOP
@ BMW_FACE_SHELL
@ BMW_EDGELOOP_NONMANIFOLD
@ BMW_VERT_SHELL
@ BMW_LOOP_SHELL_WIRE
@ BMW_ISLAND
@ BMW_EDGEBOUNDARY
#define BMW_NIL_LAY
@ BMW_FLAG_NOP
@ BMW_FLAG_TEST_HIDDEN
#define BMW_MASK_NOP
#define BMW_ITER(ele, walker, data)
BPy_StructRNA * depsgraph
static unsigned long seed
Definition btSoftBody.h:39
static AttributeOwner from_id(ID *id)
Definition attribute.cc:43
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool is_empty() const
Definition BLI_span.hh:260
int64_t size() const
void append(const T &value)
bool is_empty() const
IndexRange index_range() const
Span< T > as_span() const
#define cosf(x)
#define fabsf(x)
#define BMO_ELE_TAG
static wmOperatorStatus edbm_select_loose_exec(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_select_similar_region_exec(bContext *C, wmOperator *op)
static void walker_select_count(BMEditMesh *em, int walkercode, void *start, int r_count_by_select[2])
BMVert * EDBM_vert_find_nearest_ex(ViewContext *vc, float *dist_px_manhattan_p, const bool use_select_bias, bool use_cycle, const Span< Base * > bases, uint *r_base_index)
void MESH_OT_select_less(wmOperatorType *ot)
bool EDBM_selectmode_toggle_multi(bContext *C, const short selectmode_toggle, const int action, const bool use_extend, const bool use_expand)
bool EDBM_unified_findnearest_from_raycast(ViewContext *vc, const Span< Base * > bases, bool use_boundary_vertices, bool use_boundary_edges, int *r_base_index_vert, int *r_base_index_edge, int *r_base_index_face, BMVert **r_eve, BMEdge **r_eed, BMFace **r_efa)
static void findnearestface__doClosest(void *user_data, BMFace *efa, const float screen_co[2], int index)
static void walker_deselect_nth(BMEditMesh *em, const CheckerIntervalParams *op_params, BMHeader *h_act)
static wmOperatorStatus edbm_select_by_attribute_exec(bContext *C, wmOperator *)
static void find_nearest_edge__doClosest(void *user_data, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int index)
static float bm_interior_face_group_calc_cost(ListBase *ls, const float *edge_lengths)
static wmOperatorStatus edbm_select_linked_pick_exec(bContext *C, wmOperator *op)
BMEdge * EDBM_edge_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
BMVert * EDBM_vert_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
static bool walker_select(BMEditMesh *em, int walkercode, void *start, const bool select)
static wmOperatorStatus edbm_select_mode_exec(bContext *C, wmOperator *op)
void MESH_OT_loop_to_region(wmOperatorType *ot)
void MESH_OT_select_mode(wmOperatorType *ot)
void EDBM_select_toggle_all(BMEditMesh *em)
static bool edbm_deselect_nth(BMEditMesh *em, const CheckerIntervalParams *op_params)
static void select_linked_delimit_end(BMEditMesh *em)
void MESH_OT_select_similar_region(wmOperatorType *ot)
static void find_nearest_face_center__doZBuf(void *user_data, BMFace *efa, const float screen_co[2], int)
#define FIND_NEAR_SELECT_BIAS
static wmOperatorStatus edbm_select_by_pole_count_exec(bContext *C, wmOperator *op)
static bool unified_findnearest(ViewContext *vc, const Span< Base * > bases, int *r_base_index, BMVert **r_eve, BMEdge **r_eed, BMFace **r_efa)
BMEdge * EDBM_edge_find_nearest_ex(ViewContext *vc, float *dist_px_manhattan_p, float *r_dist_center_px_manhattan, const bool use_select_bias, bool use_cycle, BMEdge **r_eed_zbuf, const Span< Base * > bases, uint *r_base_index)
static wmOperatorStatus edbm_select_less_exec(bContext *C, wmOperator *op)
BMFace * EDBM_face_find_nearest_ex(ViewContext *vc, float *dist_px_manhattan_p, float *r_dist_center, const bool use_zbuf_single_px, const bool use_select_bias, bool use_cycle, BMFace **r_efa_zbuf, const Span< Base * > bases, uint *r_base_index)
void MESH_OT_select_linked_pick(wmOperatorType *ot)
static bool edbm_select_by_attribute_poll(bContext *C)
eElemCountType
@ ELEM_COUNT_EQUAL
@ ELEM_COUNT_NOT_EQUAL
@ ELEM_COUNT_GREATER
@ ELEM_COUNT_LESS
static bool is_count_a_match(const eElemCountType type, const int value_test, const int value_reference)
static int loop_find_region(BMLoop *l, int flag, GSet *visit_face_set, BMFace ***region_out)
void EDBM_select_swap(BMEditMesh *em)
static void mouse_mesh_loop_edge_ring(BMEditMesh *em, BMEdge *eed, bool select, bool select_clear)
static wmOperatorStatus edbm_region_to_loop_exec(bContext *C, wmOperator *)
static void mouse_mesh_loop_face(BMEditMesh *em, BMEdge *eed, bool select, bool select_clear)
BMFace * EDBM_face_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
static bool mouse_mesh_loop(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle, bool ring)
static wmOperatorStatus edbm_select_linked_flat_faces_exec(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void MESH_OT_select_random(wmOperatorType *ot)
static int verg_radial(const void *va, const void *vb)
static void find_nearest_edge_center__doZBuf(void *user_data, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int)
static int loop_find_regions(BMEditMesh *em, const bool selbigger)
static wmOperatorStatus edbm_select_more_exec(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_select_all_exec(bContext *C, wmOperator *op)
void EDBM_select_mirrored(BMEditMesh *em, const Mesh *mesh, const int axis, const bool extend, int *r_totmirr, int *r_totfail)
void MESH_OT_select_nth(wmOperatorType *ot)
static wmOperatorStatus edbm_select_non_manifold_exec(bContext *C, wmOperator *op)
void MESH_OT_loop_multi_select(wmOperatorType *ot)
ViewContext em_setup_viewcontext(bContext *C)
void EDBM_selectmode_convert(BMEditMesh *em, const short selectmode_old, const short selectmode_new)
static bool bm_interior_loop_filter_fn(const BMLoop *l, void *)
static wmOperatorStatus edbm_select_axis_exec(bContext *C, wmOperator *op)
static void select_linked_delimit_validate(BMesh *bm, int *delimit)
static wmOperatorStatus edbm_faces_select_interior_exec(bContext *C, wmOperator *)
bool EDBM_unified_findnearest(ViewContext *vc, const Span< Base * > bases, int *r_base_index, BMVert **r_eve, BMEdge **r_eed, BMFace **r_efa)
static bool edbm_selectmode_sync_multi_ex(Span< Object * > objects)
void MESH_OT_select_loose(wmOperatorType *ot)
static bool select_linked_delimit_test(BMEdge *e, int delimit, const DelimitData *delimit_data)
bool EDBM_deselect_by_material(BMEditMesh *em, const short index, const bool select)
void MESH_OT_select_by_pole_count(wmOperatorType *ot)
void MESH_OT_select_by_attribute(wmOperatorType *ot)
static bool edbm_vert_or_edge_select_mode_poll(bContext *C)
void MESH_OT_edgering_select(wmOperatorType *ot)
static bool edbm_select_ungrouped_poll(bContext *C)
static void edbm_select_linked_pick_ex(BMEditMesh *em, BMElem *ele, bool sel, int delimit)
void MESH_OT_select_face_by_sides(wmOperatorType *ot)
void MESH_OT_select_axis(wmOperatorType *ot)
static wmOperatorStatus edbm_select_face_by_sides_exec(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_loop_multiselect_exec(bContext *C, wmOperator *op)
void MESH_OT_select_mirror(wmOperatorType *ot)
static wmOperatorStatus edbm_select_sharp_edges_exec(bContext *C, wmOperator *op)
@ SELECT_AXIS_ALIGN
@ SELECT_AXIS_POS
@ SELECT_AXIS_NEG
static wmOperatorStatus edbm_select_mirror_exec(bContext *C, wmOperator *op)
static void deselect_nth_active(BMEditMesh *em, BMVert **r_eve, BMEdge **r_eed, BMFace **r_efa)
void MESH_OT_faces_select_linked_flat(wmOperatorType *ot)
void MESH_OT_edges_select_sharp(wmOperatorType *ot)
static void mouse_mesh_loop_edge(BMEditMesh *em, BMEdge *eed, bool select, bool select_clear, bool select_cycle)
static int select_linked_delimit_default_from_op(wmOperator *op, const int select_mode)
bool EDBM_selectmode_disable_multi_ex(Scene *scene, const Span< Base * > bases, const short selectmode_disable, const short selectmode_fallback)
static wmOperatorStatus edbm_select_loop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
bool EDBM_mesh_deselect_all_multi(bContext *C)
void EDBM_selectmode_set(BMEditMesh *em, const short selectmode)
static wmOperatorStatus edbm_select_random_exec(bContext *C, wmOperator *op)
static bool bm_edge_is_select_isolated(BMEdge *e)
void MESH_OT_select_interior_faces(wmOperatorType *ot)
bool EDBM_selectmode_disable_multi(bContext *C, const short selectmode_disable, const short selectmode_fallback)
void MESH_OT_select_linked(wmOperatorType *ot)
static wmOperatorStatus edbm_select_linked_exec(bContext *C, wmOperator *op)
static void select_linked_delimit_begin(BMesh *bm, int delimit)
static bool bm_interior_edge_is_manifold_except_face_index(BMEdge *e, int face_index, BMLoop *r_l_pair[2])
static void findnearestvert__doClosest(void *user_data, BMVert *eve, const float screen_co[2], int index)
void MESH_OT_select_all(wmOperatorType *ot)
void MESH_OT_select_more(wmOperatorType *ot)
#define FIND_NEAR_CYCLE_THRESHOLD_MIN
static std::string edbm_select_mode_get_description(bContext *, wmOperatorType *, PointerRNA *ptr)
bool EDBM_select_interior_faces(BMEditMesh *em)
void MESH_OT_region_to_loop(wmOperatorType *ot)
static void edbm_strip_selections(BMEditMesh *em)
void MESH_OT_loop_select(wmOperatorType *ot)
static wmOperatorStatus edbm_loop_to_region_exec(bContext *C, wmOperator *op)
static const EnumPropertyItem elem_count_compare_items[]
bool EDBM_selectmode_set_multi(bContext *C, const short selectmode)
static BMElem * edbm_select_id_bm_elem_get(const Span< Base * > bases, const uint sel_id, uint &r_base_index)
static wmOperatorStatus edbm_select_nth_exec(bContext *C, wmOperator *op)
void MESH_OT_select_ungrouped(wmOperatorType *ot)
static wmOperatorStatus edbm_select_mode_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static std::optional< BMIterType > domain_to_iter_type(const blender::bke::AttrDomain domain)
bool EDBM_selectmode_disable(Scene *scene, BMEditMesh *em, const short selectmode_disable, const short selectmode_fallback)
bool EDBM_mesh_deselect_all_multi_ex(const Span< Base * > bases)
static wmOperatorStatus edbm_select_ungrouped_exec(bContext *C, wmOperator *op)
bool EDBM_select_pick(bContext *C, const int mval[2], const SelectPick_Params &params)
bool EDBM_selectmode_set_multi_ex(Scene *scene, Span< Object * > objects, const short selectmode)
void MESH_OT_select_non_manifold(wmOperatorType *ot)
BMElem * EDBM_elem_from_selectmode(BMEditMesh *em, BMVert *eve, BMEdge *eed, BMFace *efa)
BMElem * EDBM_elem_from_index_any_multi(const Scene *scene, ViewLayer *view_layer, uint object_index, uint elem_index, Object **r_obedit)
int EDBM_elem_to_index_any_multi(const Scene *scene, ViewLayer *view_layer, BMEditMesh *em, BMElem *ele, int *r_object_index)
constexpr T sign(T) RET
#define select(A, B, C)
#define printf(...)
#define CD_MASK_PROP_ALL
#define UINT_MAX
Definition hash_md5.cc:44
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
int count
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static char faces[256]
void base_activate(bContext *C, Base *base)
bool material_active_index_set(Object *ob, int index)
short calc_orientation_from_type_ex(const Scene *scene, ViewLayer *view_layer, const View3D *v3d, const RegionView3D *rv3d, Object *ob, Object *obedit, short orientation_index, int pivot_point, float r_mat[3][3])
VecBase< float, 3 > float3
return ret
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
void RNA_property_enum_set(PointerRNA *ptr, PropertyRNA *prop, int value)
int RNA_int_get(PointerRNA *ptr, const char *name)
float RNA_float_get(PointerRNA *ptr, const char *name)
int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop)
bool RNA_struct_property_is_set(PointerRNA *ptr, const char *identifier)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
void RNA_def_property_float_default(PropertyRNA *prop, float value)
PropertyRNA * RNA_def_float_rotation(StructOrFunctionRNA *cont_, const char *identifier, const int len, const float *default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_enum_flag(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
const EnumPropertyItem rna_enum_mesh_delimit_mode_items[]
Definition rna_mesh.cc:26
const EnumPropertyItem rna_enum_axis_flag_xyz_items[]
const EnumPropertyItem rna_enum_axis_xyz_items[]
const EnumPropertyItem rna_enum_mesh_select_mode_items[]
Definition rna_scene.cc:124
const EnumPropertyItem rna_enum_transform_orientation_items[]
Definition rna_scene.cc:587
#define FLT_MAX
Definition stdcycles.h:14
BMVert * v1
BMVert * v2
short selectmode
struct BMEditSelection * next
BMHeader head
short mat_nr
BMHeader head
float no[3]
struct BMEdge * e
struct BMLoop * radial_next
struct BMLoop * prev
struct BMFace * f
struct BMLoop * next
float co[3]
struct BMEdge * e
BMWOrder order
int totvert
int totfacesel
CustomData vdata
int totedge
ListBase selected
int totvertsel
short selectmode
int totedgesel
int totface
struct Object * object
eCustomDataType cd_loop_type
Definition DNA_ID.h:404
char name[66]
Definition DNA_ID.h:415
void * first
struct MDeformWeight * dw
char editflag
NearestEdgeUserData_Hit hit
NearestEdgeUserData_Hit hit_cycle
NearestFaceUserData_Hit hit
NearestFaceUserData_Hit hit_cycle
NearestVertUserData_Hit hit_cycle
NearestVertUserData_Hit hit
struct ToolSettings * toolsettings
RegionView3D * rv3d
Definition ED_view3d.hh:80
ARegion * region
Definition ED_view3d.hh:77
int mval[2]
Definition ED_view3d.hh:82
Scene * scene
Definition ED_view3d.hh:73
BMEditMesh * em
Definition ED_view3d.hh:81
ViewLayer * view_layer
Definition ED_view3d.hh:74
View3D * v3d
Definition ED_view3d.hh:78
Object * obact
Definition ED_view3d.hh:75
Object * obedit
Definition ED_view3d.hh:76
Depsgraph * depsgraph
Definition ED_view3d.hh:72
wmEventModifierFlag modifier
Definition WM_types.hh:771
int mval[2]
Definition WM_types.hh:760
struct ReportList * reports
struct PointerRNA * ptr
i
Definition text_draw.cc:230
bool WM_cursor_test_motion_and_update(const int mval[2])
void WM_main_add_notifier(uint type, void *reference)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
PointerRNA * ptr
Definition wm_files.cc:4227
wmOperatorType * ot
Definition wm_files.cc:4226
void WM_operator_properties_checker_interval_from_op(wmOperator *op, CheckerIntervalParams *op_params)
int WM_operator_properties_select_random_seed_increment_get(wmOperator *op)
void WM_operator_properties_select_random(wmOperatorType *ot)
void WM_operator_properties_select_all(wmOperatorType *ot)
void WM_operator_properties_checker_interval(wmOperatorType *ot, bool nth_can_disable)
bool WM_operator_properties_checker_interval_test(const CheckerIntervalParams *op_params, int depth)
uint8_t flag
Definition wm_window.cc:139