Blender V4.3
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
9#include <optional>
10
11#include "MEM_guardedalloc.h"
12
13#include "BLI_bitmap.h"
14#include "BLI_heap.h"
15#include "BLI_linklist.h"
16#include "BLI_listbase.h"
17#include "BLI_math_bits.h"
18#include "BLI_math_geom.h"
19#include "BLI_math_matrix.h"
20#include "BLI_math_rotation.h"
21#include "BLI_math_vector.h"
22#include "BLI_rand.h"
23#include "BLI_string.h"
25#include "BLI_vector.hh"
26
27#include "BKE_attribute.hh"
28#include "BKE_context.hh"
29#include "BKE_customdata.hh"
30#include "BKE_deform.hh"
31#include "BKE_editmesh.hh"
32#include "BKE_layer.hh"
33#include "BKE_mesh.hh"
34#include "BKE_mesh_wrapper.hh"
35#include "BKE_object.hh"
36#include "BKE_report.hh"
37
38#include "WM_api.hh"
39#include "WM_types.hh"
40
41#include "RNA_access.hh"
42#include "RNA_define.hh"
43#include "RNA_enum_types.hh"
44
45#include "ED_mesh.hh"
46#include "ED_object.hh"
47#include "ED_screen.hh"
48#include "ED_select_utils.hh"
49#include "ED_transform.hh"
50#include "ED_view3d.hh"
51
52#include "BLT_translation.hh"
53
54#include "DNA_mesh_types.h"
55#include "DNA_meshdata_types.h"
56#include "DNA_object_types.h"
57
58#include "UI_resources.hh"
59
60#include "bmesh_tools.hh"
61
62#include "DEG_depsgraph.hh"
64
65#include "DRW_select_buffer.hh"
66
67#include "mesh_intern.hh" /* own include */
68
69/* use bmesh operator flags for a few operators */
70#define BMO_ELE_TAG 1
71
72using blender::float3;
73using blender::Span;
74using blender::Vector;
75
76/* -------------------------------------------------------------------- */
81{
82 Object *obedit = CTX_data_edit_object(C);
83 if (obedit && obedit->type == OB_MESH) {
84 const BMEditMesh *em = BKE_editmesh_from_object(obedit);
85 if (em) {
87 return true;
88 }
89 }
90 }
91
92 CTX_wm_operator_poll_msg_set(C, "An edit-mesh with vertex or edge selection mode is required");
93
94 return false;
95}
96
99/* -------------------------------------------------------------------- */
104 const Mesh *mesh,
105 const int axis,
106 const bool extend,
107 int *r_totmirr,
108 int *r_totfail)
109{
110 BMesh *bm = em->bm;
111 BMIter iter;
112 int totmirr = 0;
113 int totfail = 0;
114 bool use_topology = mesh->editflag & ME_EDIT_MIRROR_TOPO;
115
116 *r_totmirr = *r_totfail = 0;
117
118 /* select -> tag */
120 BMVert *v;
121 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
123 }
124 }
125 else if (em->selectmode & SCE_SELECT_EDGE) {
126 BMEdge *e;
127 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
129 }
130 }
131 else {
132 BMFace *f;
133 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
135 }
136 }
137
138 EDBM_verts_mirror_cache_begin(em, axis, true, true, false, use_topology);
139
140 if (!extend) {
142 }
143
145 BMVert *v;
146 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
148 BMVert *v_mirr = EDBM_verts_mirror_get(em, v);
149 if (v_mirr && !BM_elem_flag_test(v_mirr, BM_ELEM_HIDDEN)) {
150 BM_vert_select_set(bm, v_mirr, true);
151 totmirr++;
152 }
153 else {
154 totfail++;
155 }
156 }
157 }
158 }
159 else if (em->selectmode & SCE_SELECT_EDGE) {
160 BMEdge *e;
161 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
163 BMEdge *e_mirr = EDBM_verts_mirror_get_edge(em, e);
164 if (e_mirr && !BM_elem_flag_test(e_mirr, BM_ELEM_HIDDEN)) {
165 BM_edge_select_set(bm, e_mirr, true);
166 totmirr++;
167 }
168 else {
169 totfail++;
170 }
171 }
172 }
173 }
174 else {
175 BMFace *f;
176 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
178 BMFace *f_mirr = EDBM_verts_mirror_get_face(em, f);
179 if (f_mirr && !BM_elem_flag_test(f_mirr, BM_ELEM_HIDDEN)) {
180 BM_face_select_set(bm, f_mirr, true);
181 totmirr++;
182 }
183 else {
184 totfail++;
185 }
186 }
187 }
188 }
189
191
192 *r_totmirr = totmirr;
193 *r_totfail = totfail;
194}
195
198/* -------------------------------------------------------------------- */
203 const uint sel_id,
204 uint *r_base_index)
205{
206 uint elem_id;
207 char elem_type = 0;
208 bool success = DRW_select_buffer_elem_get(sel_id, &elem_id, r_base_index, &elem_type);
209
210 if (success) {
211 Object *obedit = bases[*r_base_index]->object;
213
214 switch (elem_type) {
215 case SCE_SELECT_FACE:
216 return (BMElem *)BM_face_at_index_find_or_table(em->bm, elem_id);
217 case SCE_SELECT_EDGE:
218 return (BMElem *)BM_edge_at_index_find_or_table(em->bm, elem_id);
220 return (BMElem *)BM_vert_at_index_find_or_table(em->bm, elem_id);
221 default:
222 BLI_assert(0);
223 return nullptr;
224 }
225 }
226
227 return nullptr;
228}
229
232/* -------------------------------------------------------------------- */
243#define FIND_NEAR_SELECT_BIAS 5
244#define FIND_NEAR_CYCLE_THRESHOLD_MIN 3
245
252
262
263static void findnearestvert__doClosest(void *user_data,
264 BMVert *eve,
265 const float screen_co[2],
266 int index)
267{
268 NearestVertUserData *data = static_cast<NearestVertUserData *>(user_data);
269 float dist_test, dist_test_bias;
270
271 dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co);
272
273 if (data->use_select_bias && BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
274 dist_test_bias += FIND_NEAR_SELECT_BIAS;
275 }
276
277 if (dist_test_bias < data->hit.dist_bias) {
278 data->hit.dist_bias = dist_test_bias;
279 data->hit.dist = dist_test;
280 data->hit.index = index;
281 data->hit.vert = eve;
282 }
283
284 if (data->use_cycle) {
285 if ((data->hit_cycle.vert == nullptr) && (index > data->cycle_index_prev) &&
286 (dist_test_bias < FIND_NEAR_CYCLE_THRESHOLD_MIN))
287 {
288 data->hit_cycle.dist_bias = dist_test_bias;
289 data->hit_cycle.dist = dist_test;
290 data->hit_cycle.index = index;
291 data->hit_cycle.vert = eve;
292 }
293 }
294}
295
297 float *dist_px_manhattan_p,
298 const bool use_select_bias,
299 bool use_cycle,
300 const Span<Base *> bases,
301 uint *r_base_index)
302{
303 uint base_index = 0;
304
305 if (!XRAY_FLAG_ENABLED(vc->v3d)) {
306 uint dist_px_manhattan_test = uint(
307 ED_view3d_backbuf_sample_size_clamp(vc->region, *dist_px_manhattan_p));
308 uint index;
309 BMVert *eve;
310
311 /* No after-queue (yet), so we check it now, otherwise the bm_xxxofs indices are bad. */
312 {
314
316 vc->depsgraph, vc->region, vc->v3d, vc->mval, 1, UINT_MAX, &dist_px_manhattan_test);
317
318 if (index) {
319 eve = (BMVert *)edbm_select_id_bm_elem_get(bases, index, &base_index);
320 }
321 else {
322 eve = nullptr;
323 }
324 }
325
326 if (eve) {
327 if (dist_px_manhattan_test < *dist_px_manhattan_p) {
328 if (r_base_index) {
329 *r_base_index = base_index;
330 }
331 *dist_px_manhattan_p = dist_px_manhattan_test;
332 return eve;
333 }
334 }
335 return nullptr;
336 }
337
338 NearestVertUserData data = {{0}};
339 const NearestVertUserData_Hit *hit = nullptr;
340 const eV3DProjTest clip_flag = RV3D_CLIPPING_ENABLED(vc->v3d, vc->rv3d) ?
342 V3D_PROJ_TEST_CLIP_DEFAULT & ~V3D_PROJ_TEST_CLIP_BB;
343 BMesh *prev_select_bm = nullptr;
344
345 static struct {
346 int index;
347 const BMVert *elem;
348 const BMesh *bm;
349 } prev_select = {0};
350
351 data.mval_fl[0] = vc->mval[0];
352 data.mval_fl[1] = vc->mval[1];
353 data.use_select_bias = use_select_bias;
354 data.use_cycle = use_cycle;
355
356 for (; base_index < bases.size(); base_index++) {
357 Base *base_iter = bases[base_index];
359 if (use_cycle && prev_select.bm == vc->em->bm &&
360 prev_select.elem == BM_vert_at_index_find_or_table(vc->em->bm, prev_select.index))
361 {
362 data.cycle_index_prev = prev_select.index;
363 /* No need to compare in the rest of the loop. */
364 use_cycle = false;
365 }
366 else {
367 data.cycle_index_prev = 0;
368 }
369
370 data.hit.dist = data.hit_cycle.dist = data.hit.dist_bias = data.hit_cycle.dist_bias =
371 *dist_px_manhattan_p;
372
375
376 hit = (data.use_cycle && data.hit_cycle.vert) ? &data.hit_cycle : &data.hit;
377
378 if (hit->dist < *dist_px_manhattan_p) {
379 if (r_base_index) {
380 *r_base_index = base_index;
381 }
382 *dist_px_manhattan_p = hit->dist;
383 prev_select_bm = vc->em->bm;
384 }
385 }
386
387 if (hit == nullptr) {
388 return nullptr;
389 }
390
391 prev_select.index = hit->index;
392 prev_select.elem = hit->vert;
393 prev_select.bm = prev_select_bm;
394
395 return hit->vert;
396}
397
398BMVert *EDBM_vert_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
399{
402 return EDBM_vert_find_nearest_ex(vc, dist_px_manhattan_p, false, false, {base}, nullptr);
403}
404
405/* find the distance to the edge we already have */
407 float mval_fl[2];
408 float dist;
410};
411
412static void find_nearest_edge_center__doZBuf(void *user_data,
413 BMEdge *eed,
414 const float screen_co_a[2],
415 const float screen_co_b[2],
416 int /*index*/)
417{
418 NearestEdgeUserData_ZBuf *data = static_cast<NearestEdgeUserData_ZBuf *>(user_data);
419
420 if (eed == data->edge_test) {
421 float dist_test;
422 float screen_co_mid[2];
423
424 mid_v2_v2v2(screen_co_mid, screen_co_a, screen_co_b);
425 dist_test = len_manhattan_v2v2(data->mval_fl, screen_co_mid);
426
427 if (dist_test < data->dist) {
428 data->dist = dist_test;
429 }
430 }
431}
432
434 float dist;
436 int index;
438
439 /* edges only, un-biased manhattan distance to which ever edge we pick
440 * (not used for choosing) */
442};
443
454
455/* NOTE: uses v3d, so needs active 3d window. */
456static void find_nearest_edge__doClosest(void *user_data,
457 BMEdge *eed,
458 const float screen_co_a[2],
459 const float screen_co_b[2],
460 int index)
461{
462 NearestEdgeUserData *data = static_cast<NearestEdgeUserData *>(user_data);
463 float dist_test, dist_test_bias;
464
465 float fac = line_point_factor_v2(data->mval_fl, screen_co_a, screen_co_b);
466 float screen_co[2];
467
468 if (fac <= 0.0f) {
469 fac = 0.0f;
470 copy_v2_v2(screen_co, screen_co_a);
471 }
472 else if (fac >= 1.0f) {
473 fac = 1.0f;
474 copy_v2_v2(screen_co, screen_co_b);
475 }
476 else {
477 interp_v2_v2v2(screen_co, screen_co_a, screen_co_b, fac);
478 }
479
480 dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co);
481
482 if (data->use_select_bias && BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
483 dist_test_bias += FIND_NEAR_SELECT_BIAS;
484 }
485
486 if (data->vc.rv3d->rflag & RV3D_CLIPPING) {
487 float vec[3];
488
489 interp_v3_v3v3(vec, eed->v1->co, eed->v2->co, fac);
490 if (ED_view3d_clipping_test(data->vc.rv3d, vec, true)) {
491 return;
492 }
493 }
494
495 if (dist_test_bias < data->hit.dist_bias) {
496 float screen_co_mid[2];
497
498 data->hit.dist_bias = dist_test_bias;
499 data->hit.dist = dist_test;
500 data->hit.index = index;
501 data->hit.edge = eed;
502
503 mid_v2_v2v2(screen_co_mid, screen_co_a, screen_co_b);
504 data->hit.dist_center_px_manhattan = len_manhattan_v2v2(data->mval_fl, screen_co_mid);
505 }
506
507 if (data->use_cycle) {
508 if ((data->hit_cycle.edge == nullptr) && (index > data->cycle_index_prev) &&
509 (dist_test_bias < FIND_NEAR_CYCLE_THRESHOLD_MIN))
510 {
511 float screen_co_mid[2];
512
513 data->hit_cycle.dist_bias = dist_test_bias;
514 data->hit_cycle.dist = dist_test;
515 data->hit_cycle.index = index;
516 data->hit_cycle.edge = eed;
517
518 mid_v2_v2v2(screen_co_mid, screen_co_a, screen_co_b);
519 data->hit_cycle.dist_center_px_manhattan = len_manhattan_v2v2(data->mval_fl, screen_co_mid);
520 }
521 }
522}
523
525 float *dist_px_manhattan_p,
526 float *r_dist_center_px_manhattan,
527 const bool use_select_bias,
528 bool use_cycle,
529 BMEdge **r_eed_zbuf,
530 const Span<Base *> bases,
531 uint *r_base_index)
532{
533 uint base_index = 0;
534
535 if (!XRAY_FLAG_ENABLED(vc->v3d)) {
536 uint dist_px_manhattan_test = uint(
537 ED_view3d_backbuf_sample_size_clamp(vc->region, *dist_px_manhattan_p));
538 uint index;
539 BMEdge *eed;
540
541 /* No after-queue (yet), so we check it now, otherwise the bm_xxxofs indices are bad. */
542 {
544
546 vc->depsgraph, vc->region, vc->v3d, vc->mval, 1, UINT_MAX, &dist_px_manhattan_test);
547
548 if (index) {
549 eed = (BMEdge *)edbm_select_id_bm_elem_get(bases, index, &base_index);
550 }
551 else {
552 eed = nullptr;
553 }
554 }
555
556 if (r_eed_zbuf) {
557 *r_eed_zbuf = eed;
558 }
559
560 /* exception for faces (verts don't need this) */
561 if (r_dist_center_px_manhattan && eed) {
563
564 data.mval_fl[0] = vc->mval[0];
565 data.mval_fl[1] = vc->mval[1];
566 data.dist = FLT_MAX;
567 data.edge_test = eed;
568
570
573 &data,
575
576 *r_dist_center_px_manhattan = data.dist;
577 }
578 /* end exception */
579
580 if (eed) {
581 if (dist_px_manhattan_test < *dist_px_manhattan_p) {
582 if (r_base_index) {
583 *r_base_index = base_index;
584 }
585 *dist_px_manhattan_p = dist_px_manhattan_test;
586 return eed;
587 }
588 }
589 return nullptr;
590 }
591
592 NearestEdgeUserData data = {{nullptr}};
593 const NearestEdgeUserData_Hit *hit = nullptr;
594 /* interpolate along the edge before doing a clipping plane test */
595 const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_DEFAULT & ~V3D_PROJ_TEST_CLIP_BB;
596 BMesh *prev_select_bm = nullptr;
597
598 static struct {
599 int index;
600 const BMEdge *elem;
601 const BMesh *bm;
602 } prev_select = {0};
603
604 data.vc = *vc;
605 data.mval_fl[0] = vc->mval[0];
606 data.mval_fl[1] = vc->mval[1];
607 data.use_select_bias = use_select_bias;
608 data.use_cycle = use_cycle;
609
610 for (; base_index < bases.size(); base_index++) {
611 Base *base_iter = bases[base_index];
613 if (use_cycle && prev_select.bm == vc->em->bm &&
614 prev_select.elem == BM_edge_at_index_find_or_table(vc->em->bm, prev_select.index))
615 {
616 data.cycle_index_prev = prev_select.index;
617 /* No need to compare in the rest of the loop. */
618 use_cycle = false;
619 }
620 else {
621 data.cycle_index_prev = 0;
622 }
623
624 data.hit.dist = data.hit_cycle.dist = data.hit.dist_bias = data.hit_cycle.dist_bias =
625 *dist_px_manhattan_p;
626
630
631 hit = (data.use_cycle && data.hit_cycle.edge) ? &data.hit_cycle : &data.hit;
632
633 if (hit->dist < *dist_px_manhattan_p) {
634 if (r_base_index) {
635 *r_base_index = base_index;
636 }
637 *dist_px_manhattan_p = hit->dist;
638 prev_select_bm = vc->em->bm;
639 }
640 }
641
642 if (hit == nullptr) {
643 return nullptr;
644 }
645
646 if (r_dist_center_px_manhattan) {
647 *r_dist_center_px_manhattan = hit->dist_center_px_manhattan;
648 }
649
650 prev_select.index = hit->index;
651 prev_select.elem = hit->edge;
652 prev_select.bm = prev_select_bm;
653
654 return hit->edge;
655}
656
657BMEdge *EDBM_edge_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
658{
662 vc, dist_px_manhattan_p, nullptr, false, false, nullptr, {base}, nullptr);
663}
664
665/* find the distance to the face we already have */
671
672static void find_nearest_face_center__doZBuf(void *user_data,
673 BMFace *efa,
674 const float screen_co[2],
675 int /*index*/)
676{
677 NearestFaceUserData_ZBuf *data = static_cast<NearestFaceUserData_ZBuf *>(user_data);
678
679 if (efa == data->face_test) {
680 const float dist_test = len_manhattan_v2v2(data->mval_fl, screen_co);
681
682 if (dist_test < data->dist_px_manhattan) {
683 data->dist_px_manhattan = dist_test;
684 }
685 }
686}
687
694
704
705static void findnearestface__doClosest(void *user_data,
706 BMFace *efa,
707 const float screen_co[2],
708 int index)
709{
710 NearestFaceUserData *data = static_cast<NearestFaceUserData *>(user_data);
711 float dist_test, dist_test_bias;
712
713 dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co);
714
715 if (data->use_select_bias && BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
716 dist_test_bias += FIND_NEAR_SELECT_BIAS;
717 }
718
719 if (dist_test_bias < data->hit.dist_bias) {
720 data->hit.dist_bias = dist_test_bias;
721 data->hit.dist = dist_test;
722 data->hit.index = index;
723 data->hit.face = efa;
724 }
725
726 if (data->use_cycle) {
727 if ((data->hit_cycle.face == nullptr) && (index > data->cycle_index_prev) &&
728 (dist_test_bias < FIND_NEAR_CYCLE_THRESHOLD_MIN))
729 {
730 data->hit_cycle.dist_bias = dist_test_bias;
731 data->hit_cycle.dist = dist_test;
732 data->hit_cycle.index = index;
733 data->hit_cycle.face = efa;
734 }
735 }
736}
737
739 float *dist_px_manhattan_p,
740 float *r_dist_center,
741 const bool use_zbuf_single_px,
742 const bool use_select_bias,
743 bool use_cycle,
744 BMFace **r_efa_zbuf,
745 const Span<Base *> bases,
746 uint *r_base_index)
747{
748 uint base_index = 0;
749
750 if (!XRAY_FLAG_ENABLED(vc->v3d)) {
751 float dist_test;
752 uint index;
753 BMFace *efa;
754
755 {
756 uint dist_px_manhattan_test = 0;
757 if (*dist_px_manhattan_p != 0.0f && (use_zbuf_single_px == false)) {
758 dist_px_manhattan_test = uint(
759 ED_view3d_backbuf_sample_size_clamp(vc->region, *dist_px_manhattan_p));
760 }
761
763
764 if (dist_px_manhattan_test == 0) {
765 index = DRW_select_buffer_sample_point(vc->depsgraph, vc->region, vc->v3d, vc->mval);
766 dist_test = 0.0f;
767 }
768 else {
770 vc->depsgraph, vc->region, vc->v3d, vc->mval, 1, UINT_MAX, &dist_px_manhattan_test);
771 dist_test = dist_px_manhattan_test;
772 }
773
774 if (index) {
775 efa = (BMFace *)edbm_select_id_bm_elem_get(bases, index, &base_index);
776 }
777 else {
778 efa = nullptr;
779 }
780 }
781
782 if (r_efa_zbuf) {
783 *r_efa_zbuf = efa;
784 }
785
786 /* exception for faces (verts don't need this) */
787 if (r_dist_center && efa) {
789
790 data.mval_fl[0] = vc->mval[0];
791 data.mval_fl[1] = vc->mval[1];
792 data.dist_px_manhattan = FLT_MAX;
793 data.face_test = efa;
794
796
799
800 *r_dist_center = data.dist_px_manhattan;
801 }
802 /* end exception */
803
804 if (efa) {
805 if (dist_test < *dist_px_manhattan_p) {
806 if (r_base_index) {
807 *r_base_index = base_index;
808 }
809 *dist_px_manhattan_p = dist_test;
810 return efa;
811 }
812 }
813 return nullptr;
814 }
815
816 NearestFaceUserData data = {{0}};
817 const NearestFaceUserData_Hit *hit = nullptr;
819 BMesh *prev_select_bm = nullptr;
820
821 static struct {
822 int index;
823 const BMFace *elem;
824 const BMesh *bm;
825 } prev_select = {0};
826
827 data.mval_fl[0] = vc->mval[0];
828 data.mval_fl[1] = vc->mval[1];
829 data.use_select_bias = use_select_bias;
830 data.use_cycle = use_cycle;
831
832 for (; base_index < bases.size(); base_index++) {
833 Base *base_iter = bases[base_index];
835 if (use_cycle && prev_select.bm == vc->em->bm &&
836 prev_select.elem == BM_face_at_index_find_or_table(vc->em->bm, prev_select.index))
837 {
838 data.cycle_index_prev = prev_select.index;
839 /* No need to compare in the rest of the loop. */
840 use_cycle = false;
841 }
842 else {
843 data.cycle_index_prev = 0;
844 }
845
846 data.hit.dist = data.hit_cycle.dist = data.hit.dist_bias = data.hit_cycle.dist_bias =
847 *dist_px_manhattan_p;
848
851
852 hit = (data.use_cycle && data.hit_cycle.face) ? &data.hit_cycle : &data.hit;
853
854 if (hit->dist < *dist_px_manhattan_p) {
855 if (r_base_index) {
856 *r_base_index = base_index;
857 }
858 *dist_px_manhattan_p = hit->dist;
859 prev_select_bm = vc->em->bm;
860 }
861 }
862
863 if (hit == nullptr) {
864 return nullptr;
865 }
866
867 if (r_dist_center) {
868 *r_dist_center = hit->dist;
869 }
870
871 prev_select.index = hit->index;
872 prev_select.elem = hit->face;
873 prev_select.bm = prev_select_bm;
874
875 return hit->face;
876}
877
878BMFace *EDBM_face_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
879{
883 vc, dist_px_manhattan_p, nullptr, false, false, false, nullptr, {base}, nullptr);
884}
885
886#undef FIND_NEAR_SELECT_BIAS
887#undef FIND_NEAR_CYCLE_THRESHOLD_MIN
888
889/* best distance based on screen coords.
890 * use em->selectmode to define how to use
891 * selected vertices and edges get disadvantage
892 * return 1 if found one
893 */
895 const Span<Base *> bases,
896 int *r_base_index,
897 BMVert **r_eve,
898 BMEdge **r_eed,
899 BMFace **r_efa)
900{
901 BMEditMesh *em = vc->em;
902
903 const bool use_cycle = !WM_cursor_test_motion_and_update(vc->mval);
904 const float dist_init = ED_view3d_select_dist_px();
905 /* since edges select lines, we give dots advantage of ~20 pix */
906 const float dist_margin = (dist_init / 2);
907 float dist = dist_init;
908
909 struct {
910 struct {
911 BMVert *ele;
912 int base_index;
913 } v;
914 struct {
915 BMEdge *ele;
916 int base_index;
917 } e, e_zbuf;
918 struct {
919 BMFace *ele;
920 int base_index;
921 } f, f_zbuf;
922 } hit = {{nullptr}};
923
924 /* No after-queue (yet), so we check it now, otherwise the em_xxxofs indices are bad. */
925
926 if ((dist > 0.0f) && (em->selectmode & SCE_SELECT_FACE)) {
927 float dist_center = 0.0f;
928 float *dist_center_p = (em->selectmode & (SCE_SELECT_EDGE | SCE_SELECT_VERTEX)) ?
929 &dist_center :
930 nullptr;
931
932 uint base_index = 0;
933 BMFace *efa_zbuf = nullptr;
935 vc, &dist, dist_center_p, true, true, use_cycle, &efa_zbuf, bases, &base_index);
936
937 if (efa_test && dist_center_p) {
938 dist = min_ff(dist_margin, dist_center);
939 }
940 if (efa_test) {
941 hit.f.base_index = base_index;
942 hit.f.ele = efa_test;
943 }
944 if (efa_zbuf) {
945 hit.f_zbuf.base_index = base_index;
946 hit.f_zbuf.ele = efa_zbuf;
947 }
948 }
949
950 if ((dist > 0.0f) && (em->selectmode & SCE_SELECT_EDGE)) {
951 float dist_center = 0.0f;
952 float *dist_center_p = (em->selectmode & SCE_SELECT_VERTEX) ? &dist_center : nullptr;
953
954 uint base_index = 0;
955 BMEdge *eed_zbuf = nullptr;
957 vc, &dist, dist_center_p, true, use_cycle, &eed_zbuf, bases, &base_index);
958
959 if (eed_test && dist_center_p) {
960 dist = min_ff(dist_margin, dist_center);
961 }
962 if (eed_test) {
963 hit.e.base_index = base_index;
964 hit.e.ele = eed_test;
965 }
966 if (eed_zbuf) {
967 hit.e_zbuf.base_index = base_index;
968 hit.e_zbuf.ele = eed_zbuf;
969 }
970 }
971
972 if ((dist > 0.0f) && (em->selectmode & SCE_SELECT_VERTEX)) {
973 uint base_index = 0;
974 BMVert *eve_test = EDBM_vert_find_nearest_ex(vc, &dist, true, use_cycle, bases, &base_index);
975
976 if (eve_test) {
977 hit.v.base_index = base_index;
978 hit.v.ele = eve_test;
979 }
980 }
981
982 /* Return only one of 3 pointers, for front-buffer redraws. */
983 if (hit.v.ele) {
984 hit.f.ele = nullptr;
985 hit.e.ele = nullptr;
986 }
987 else if (hit.e.ele) {
988 hit.f.ele = nullptr;
989 }
990
991 /* there may be a face under the cursor, who's center if too far away
992 * use this if all else fails, it makes sense to select this */
993 if ((hit.v.ele || hit.e.ele || hit.f.ele) == 0) {
994 if (hit.e_zbuf.ele) {
995 hit.e.base_index = hit.e_zbuf.base_index;
996 hit.e.ele = hit.e_zbuf.ele;
997 }
998 else if (hit.f_zbuf.ele) {
999 hit.f.base_index = hit.f_zbuf.base_index;
1000 hit.f.ele = hit.f_zbuf.ele;
1001 }
1002 }
1003
1004 /* Only one element type will be non-null. */
1005 BLI_assert(((hit.v.ele != nullptr) + (hit.e.ele != nullptr) + (hit.f.ele != nullptr)) <= 1);
1006
1007 if (hit.v.ele) {
1008 *r_base_index = hit.v.base_index;
1009 }
1010 if (hit.e.ele) {
1011 *r_base_index = hit.e.base_index;
1012 }
1013 if (hit.f.ele) {
1014 *r_base_index = hit.f.base_index;
1015 }
1016
1017 *r_eve = hit.v.ele;
1018 *r_eed = hit.e.ele;
1019 *r_efa = hit.f.ele;
1020
1021 return (hit.v.ele || hit.e.ele || hit.f.ele);
1022}
1023
1024#undef FAKE_SELECT_MODE_BEGIN
1025#undef FAKE_SELECT_MODE_END
1026
1028 const Span<Base *> bases,
1029 int *r_base_index,
1030 BMVert **r_eve,
1031 BMEdge **r_eed,
1032 BMFace **r_efa)
1033{
1034 return unified_findnearest(vc, bases, r_base_index, r_eve, r_eed, r_efa);
1035}
1036
1039/* -------------------------------------------------------------------- */
1047 const Span<Base *> bases,
1048 bool use_boundary_vertices,
1049 bool use_boundary_edges,
1050 int *r_base_index_vert,
1051 int *r_base_index_edge,
1052 int *r_base_index_face,
1053 BMVert **r_eve,
1054 BMEdge **r_eed,
1055 BMFace **r_efa)
1056{
1057 const float mval_fl[2] = {float(vc->mval[0]), float(vc->mval[1])};
1058 float ray_origin[3], ray_direction[3];
1059
1060 struct {
1061 uint base_index;
1062 BMElem *ele;
1063 } best = {0, nullptr};
1064 /* Currently unused, keep since we may want to pick the best. */
1065 UNUSED_VARS(best);
1066
1067 struct {
1068 uint base_index;
1069 BMElem *ele;
1070 } best_vert = {0, nullptr};
1071
1072 struct {
1073 uint base_index;
1074 BMElem *ele;
1075 } best_edge = {0, nullptr};
1076
1077 struct {
1078 uint base_index;
1079 BMElem *ele;
1080 } best_face = {0, nullptr};
1081
1083 vc->depsgraph, vc->region, vc->v3d, mval_fl, ray_origin, ray_direction, true))
1084 {
1085 float dist_sq_best = FLT_MAX;
1086 float dist_sq_best_vert = FLT_MAX;
1087 float dist_sq_best_edge = FLT_MAX;
1088 float dist_sq_best_face = FLT_MAX;
1089
1090 const bool use_vert = (r_eve != nullptr);
1091 const bool use_edge = (r_eed != nullptr);
1092 const bool use_face = (r_efa != nullptr);
1093
1094 for (const int base_index : bases.index_range()) {
1095 Base *base_iter = bases[base_index];
1096 Object *obedit = base_iter->object;
1097
1099 BMesh *bm = em->bm;
1100 float imat3[3][3];
1101
1103 copy_m3_m4(imat3, obedit->object_to_world().ptr());
1104 invert_m3(imat3);
1105
1106 Span<float3> vert_positions;
1107 {
1108 const Object *obedit_eval = DEG_get_evaluated_object(vc->depsgraph, obedit);
1109 const Mesh *mesh_eval = BKE_object_get_editmesh_eval_cage(obedit_eval);
1110 if (BKE_mesh_wrapper_vert_len(mesh_eval) == bm->totvert) {
1111 vert_positions = BKE_mesh_wrapper_vert_coords(mesh_eval);
1112 }
1113 }
1114
1115 if (!vert_positions.is_empty()) {
1117 }
1118
1119 if ((use_boundary_vertices || use_boundary_edges) && (use_vert || use_edge)) {
1120 BMEdge *e;
1121 BMIter eiter;
1122 BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
1124 if (use_vert && use_boundary_vertices) {
1125 for (uint j = 0; j < 2; j++) {
1126 BMVert *v = *((&e->v1) + j);
1127 float point[3];
1128 mul_v3_m4v3(point,
1129 obedit->object_to_world().ptr(),
1130 !vert_positions.is_empty() ? vert_positions[BM_elem_index_get(v)] :
1131 v->co);
1132 const float dist_sq_test = dist_squared_to_ray_v3_normalized(
1133 ray_origin, ray_direction, point);
1134 if (dist_sq_test < dist_sq_best_vert) {
1135 dist_sq_best_vert = dist_sq_test;
1136 best_vert.base_index = base_index;
1137 best_vert.ele = (BMElem *)v;
1138 }
1139 if (dist_sq_test < dist_sq_best) {
1140 dist_sq_best = dist_sq_test;
1141 best.base_index = base_index;
1142 best.ele = (BMElem *)v;
1143 }
1144 }
1145 }
1146
1147 if (use_edge && use_boundary_edges) {
1148 float point[3];
1149#if 0
1150 const float dist_sq_test = dist_squared_ray_to_seg_v3(
1151 ray_origin, ray_direction, e->v1->co, e->v2->co, point, &depth);
1152#else
1153 if (!vert_positions.is_empty()) {
1154 mid_v3_v3v3(point,
1155 vert_positions[BM_elem_index_get(e->v1)],
1156 vert_positions[BM_elem_index_get(e->v2)]);
1157 }
1158 else {
1159 mid_v3_v3v3(point, e->v1->co, e->v2->co);
1160 }
1161 mul_m4_v3(obedit->object_to_world().ptr(), point);
1162 const float dist_sq_test = dist_squared_to_ray_v3_normalized(
1163 ray_origin, ray_direction, point);
1164 if (dist_sq_test < dist_sq_best_edge) {
1165 dist_sq_best_edge = dist_sq_test;
1166 best_edge.base_index = base_index;
1167 best_edge.ele = (BMElem *)e;
1168 }
1169 if (dist_sq_test < dist_sq_best) {
1170 dist_sq_best = dist_sq_test;
1171 best.base_index = base_index;
1172 best.ele = (BMElem *)e;
1173 }
1174#endif
1175 }
1176 }
1177 }
1178 }
1179 /* Non boundary case. */
1180 if (use_vert && !use_boundary_vertices) {
1181 BMVert *v;
1182 BMIter viter;
1183 BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
1184 if (BM_elem_flag_test(v, BM_ELEM_HIDDEN) == false) {
1185 float point[3];
1186 mul_v3_m4v3(point,
1187 obedit->object_to_world().ptr(),
1188 !vert_positions.is_empty() ? vert_positions[BM_elem_index_get(v)] : v->co);
1189 const float dist_sq_test = dist_squared_to_ray_v3_normalized(
1190 ray_origin, ray_direction, point);
1191 if (dist_sq_test < dist_sq_best_vert) {
1192 dist_sq_best_vert = dist_sq_test;
1193 best_vert.base_index = base_index;
1194 best_vert.ele = (BMElem *)v;
1195 }
1196 if (dist_sq_test < dist_sq_best) {
1197 dist_sq_best = dist_sq_test;
1198 best.base_index = base_index;
1199 best.ele = (BMElem *)v;
1200 }
1201 }
1202 }
1203 }
1204
1205 if (use_edge && !use_boundary_edges) {
1206 BMEdge *e;
1207 BMIter eiter;
1208 BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
1209 if (BM_elem_flag_test(e, BM_ELEM_HIDDEN) == false) {
1210 float point[3];
1211 if (!vert_positions.is_empty()) {
1212 mid_v3_v3v3(point,
1213 vert_positions[BM_elem_index_get(e->v1)],
1214 vert_positions[BM_elem_index_get(e->v2)]);
1215 }
1216 else {
1217 mid_v3_v3v3(point, e->v1->co, e->v2->co);
1218 }
1219 mul_m4_v3(obedit->object_to_world().ptr(), point);
1220 const float dist_sq_test = dist_squared_to_ray_v3_normalized(
1221 ray_origin, ray_direction, point);
1222 if (dist_sq_test < dist_sq_best_edge) {
1223 dist_sq_best_edge = dist_sq_test;
1224 best_edge.base_index = base_index;
1225 best_edge.ele = (BMElem *)e;
1226 }
1227 if (dist_sq_test < dist_sq_best) {
1228 dist_sq_best = dist_sq_test;
1229 best.base_index = base_index;
1230 best.ele = (BMElem *)e;
1231 }
1232 }
1233 }
1234 }
1235
1236 if (use_face) {
1237 BMFace *f;
1238 BMIter fiter;
1239 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
1240 if (BM_elem_flag_test(f, BM_ELEM_HIDDEN) == false) {
1241 float point[3];
1242 if (!vert_positions.is_empty()) {
1243 BM_face_calc_center_median_vcos(bm, f, point, vert_positions);
1244 }
1245 else {
1247 }
1248 mul_m4_v3(obedit->object_to_world().ptr(), point);
1249 const float dist_sq_test = dist_squared_to_ray_v3_normalized(
1250 ray_origin, ray_direction, point);
1251 if (dist_sq_test < dist_sq_best_face) {
1252 dist_sq_best_face = dist_sq_test;
1253 best_face.base_index = base_index;
1254 best_face.ele = (BMElem *)f;
1255 }
1256 if (dist_sq_test < dist_sq_best) {
1257 dist_sq_best = dist_sq_test;
1258 best.base_index = base_index;
1259 best.ele = (BMElem *)f;
1260 }
1261 }
1262 }
1263 }
1264 }
1265 }
1266
1267 *r_base_index_vert = best_vert.base_index;
1268 *r_base_index_edge = best_edge.base_index;
1269 *r_base_index_face = best_face.base_index;
1270
1271 if (r_eve) {
1272 *r_eve = nullptr;
1273 }
1274 if (r_eed) {
1275 *r_eed = nullptr;
1276 }
1277 if (r_efa) {
1278 *r_efa = nullptr;
1279 }
1280
1281 if (best_vert.ele) {
1282 *r_eve = (BMVert *)best_vert.ele;
1283 }
1284 if (best_edge.ele) {
1285 *r_eed = (BMEdge *)best_edge.ele;
1286 }
1287 if (best_face.ele) {
1288 *r_efa = (BMFace *)best_face.ele;
1289 }
1290
1291 return (best_vert.ele != nullptr || best_edge.ele != nullptr || best_face.ele != nullptr);
1292}
1293
1296/* -------------------------------------------------------------------- */
1301{
1302 Object *obedit = CTX_data_edit_object(C);
1304 BMesh *bm = em->bm;
1305 bool changed = false;
1306
1307 /* group vars */
1308 int(*group_index)[2];
1309 int group_tot;
1310 int i;
1311
1312 if (bm->totfacesel < 2) {
1313 BKE_report(op->reports, RPT_ERROR, "No face regions selected");
1314 return OPERATOR_CANCELLED;
1315 }
1316
1317 int *groups_array = static_cast<int *>(
1318 MEM_mallocN(sizeof(*groups_array) * bm->totfacesel, __func__));
1319 group_tot = BM_mesh_calc_face_groups(
1320 bm, groups_array, &group_index, nullptr, nullptr, nullptr, BM_ELEM_SELECT, BM_VERT);
1321
1323
1324 for (i = 0; i < group_tot; i++) {
1325 ListBase faces_regions;
1326 int tot;
1327
1328 const int fg_sta = group_index[i][0];
1329 const int fg_len = group_index[i][1];
1330 int j;
1331 BMFace **fg = static_cast<BMFace **>(MEM_mallocN(sizeof(*fg) * fg_len, __func__));
1332
1333 for (j = 0; j < fg_len; j++) {
1334 fg[j] = BM_face_at_index(bm, groups_array[fg_sta + j]);
1335 }
1336
1337 tot = BM_mesh_region_match(bm, fg, fg_len, &faces_regions);
1338
1339 MEM_freeN(fg);
1340
1341 if (tot) {
1342 while (LinkData *link = static_cast<LinkData *>(BLI_pophead(&faces_regions))) {
1343 BMFace **faces = static_cast<BMFace **>(link->data);
1344 while (BMFace *f = *(faces++)) {
1345 BM_face_select_set(bm, f, true);
1346 }
1347 MEM_freeN(link->data);
1348 MEM_freeN(link);
1349
1350 changed = true;
1351 }
1352 }
1353 }
1354
1355 MEM_freeN(groups_array);
1356 MEM_freeN(group_index);
1357
1358 if (changed) {
1359 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
1361 }
1362 else {
1363 BKE_report(op->reports, RPT_WARNING, "No matching face regions found");
1364 }
1365
1366 return OPERATOR_FINISHED;
1367}
1368
1370{
1371 /* identifiers */
1372 ot->name = "Select Similar Regions";
1373 ot->idname = "MESH_OT_select_similar_region";
1374 ot->description = "Select similar face regions to the current selection";
1375
1376 /* api callbacks */
1379
1380 /* flags */
1382}
1383
1386/* -------------------------------------------------------------------- */
1391{
1392 const int type = RNA_enum_get(op->ptr, "type");
1393 const int action = RNA_enum_get(op->ptr, "action");
1394 const bool use_extend = RNA_boolean_get(op->ptr, "use_extend");
1395 const bool use_expand = RNA_boolean_get(op->ptr, "use_expand");
1396
1397 if (EDBM_selectmode_toggle_multi(C, type, action, use_extend, use_expand)) {
1398 return OPERATOR_FINISHED;
1399 }
1400 return OPERATOR_CANCELLED;
1401}
1402
1403static int edbm_select_mode_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1404{
1405 /* Bypass when in UV non sync-select mode, fall through to keymap that edits. */
1406 if (CTX_wm_space_image(C)) {
1408 if ((ts->uv_flag & UV_SYNC_SELECTION) == 0) {
1409 return OPERATOR_PASS_THROUGH;
1410 }
1411 /* Bypass when no action is needed. */
1412 if (!RNA_struct_property_is_set(op->ptr, "type")) {
1413 return OPERATOR_CANCELLED;
1414 }
1415 }
1416
1417 /* detecting these options based on shift/ctrl here is weak, but it's done
1418 * to make this work when clicking buttons or menus */
1419 if (!RNA_struct_property_is_set(op->ptr, "use_extend")) {
1420 RNA_boolean_set(op->ptr, "use_extend", event->modifier & KM_SHIFT);
1421 }
1422 if (!RNA_struct_property_is_set(op->ptr, "use_expand")) {
1423 RNA_boolean_set(op->ptr, "use_expand", event->modifier & KM_CTRL);
1424 }
1425
1426 return edbm_select_mode_exec(C, op);
1427}
1428
1430 wmOperatorType * /*ot*/,
1431 PointerRNA *ptr)
1432{
1433 const int type = RNA_enum_get(ptr, "type");
1434
1435 /* Because the special behavior for shift and ctrl click depend on user input, they may be
1436 * incorrect if the operator is used from a script or from a special button. So only return the
1437 * specialized descriptions if only the "type" is set, which conveys that the operator is meant
1438 * to be used with the logic in the `invoke` method. */
1439 if (RNA_struct_property_is_set(ptr, "type") && !RNA_struct_property_is_set(ptr, "use_extend") &&
1440 !RNA_struct_property_is_set(ptr, "use_expand") && !RNA_struct_property_is_set(ptr, "action"))
1441 {
1442 switch (type) {
1443 case SCE_SELECT_VERTEX:
1444 return TIP_(
1445 "Vertex select - Shift-Click for multiple modes, Ctrl-Click contracts selection");
1446 case SCE_SELECT_EDGE:
1447 return TIP_(
1448 "Edge select - Shift-Click for multiple modes, "
1449 "Ctrl-Click expands/contracts selection depending on the current mode");
1450 case SCE_SELECT_FACE:
1451 return TIP_("Face select - Shift-Click for multiple modes, Ctrl-Click expands selection");
1452 }
1453 }
1454
1455 return "";
1456}
1457
1459{
1460 PropertyRNA *prop;
1461
1462 static const EnumPropertyItem actions_items[] = {
1463 {0, "DISABLE", false, "Disable", "Disable selected markers"},
1464 {1, "ENABLE", false, "Enable", "Enable selected markers"},
1465 {2, "TOGGLE", false, "Toggle", "Toggle disabled flag for selected markers"},
1466 {0, nullptr, 0, nullptr, nullptr},
1467 };
1468
1469 /* identifiers */
1470 ot->name = "Select Mode";
1471 ot->idname = "MESH_OT_select_mode";
1472 ot->description = "Change selection mode";
1473
1474 /* api callbacks */
1479
1480 /* flags */
1482
1483 /* properties */
1484 /* Hide all, not to show redo panel. */
1485 prop = RNA_def_boolean(ot->srna, "use_extend", false, "Extend", "");
1487 prop = RNA_def_boolean(ot->srna, "use_expand", false, "Expand", "");
1489 ot->prop = prop = RNA_def_enum(ot->srna, "type", rna_enum_mesh_select_mode_items, 0, "Type", "");
1491
1492 prop = RNA_def_enum(
1493 ot->srna, "action", actions_items, 2, "Action", "Selection action to execute");
1495}
1496
1499/* -------------------------------------------------------------------- */
1504 int walkercode,
1505 void *start,
1506 int r_count_by_select[2])
1507{
1508 BMesh *bm = em->bm;
1509 BMElem *ele;
1510 BMWalker walker;
1511
1512 r_count_by_select[0] = r_count_by_select[1] = 0;
1513
1514 BMW_init(&walker,
1515 bm,
1516 walkercode,
1521 BMW_NIL_LAY);
1522
1523 for (ele = static_cast<BMElem *>(BMW_begin(&walker, start)); ele;
1524 ele = static_cast<BMElem *>(BMW_step(&walker)))
1525 {
1526 r_count_by_select[BM_elem_flag_test(ele, BM_ELEM_SELECT) ? 1 : 0] += 1;
1527
1528 /* Early exit when mixed (could be optional if needed. */
1529 if (r_count_by_select[0] && r_count_by_select[1]) {
1530 r_count_by_select[0] = r_count_by_select[1] = -1;
1531 break;
1532 }
1533 }
1534
1535 BMW_end(&walker);
1536}
1537
1538static void walker_select(BMEditMesh *em, int walkercode, void *start, const bool select)
1539{
1540 BMesh *bm = em->bm;
1541 BMElem *ele;
1542 BMWalker walker;
1543
1544 BMW_init(&walker,
1545 bm,
1546 walkercode,
1551 BMW_NIL_LAY);
1552
1553 for (ele = static_cast<BMElem *>(BMW_begin(&walker, start)); ele;
1554 ele = static_cast<BMElem *>(BMW_step(&walker)))
1555 {
1556 if (!select) {
1558 }
1560 }
1561 BMW_end(&walker);
1562}
1563
1565{
1566 const bool is_ring = RNA_boolean_get(op->ptr, "ring");
1567 const Scene *scene = CTX_data_scene(C);
1568 ViewLayer *view_layer = CTX_data_view_layer(C);
1570 scene, view_layer, CTX_wm_view3d(C));
1571 for (Object *obedit : objects) {
1573
1574 if (em->bm->totedgesel == 0) {
1575 continue;
1576 }
1577
1578 BMEdge *eed;
1579 int edindex;
1580 BMIter iter;
1581 int totedgesel = 0;
1582
1583 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
1585 totedgesel++;
1586 }
1587 }
1588
1589 BMEdge **edarray = static_cast<BMEdge **>(
1590 MEM_mallocN(sizeof(BMEdge *) * totedgesel, "edge array"));
1591 edindex = 0;
1592
1593 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
1595 edarray[edindex] = eed;
1596 edindex++;
1597 }
1598 }
1599
1600 if (is_ring) {
1601 for (edindex = 0; edindex < totedgesel; edindex += 1) {
1602 eed = edarray[edindex];
1603 walker_select(em, BMW_EDGERING, eed, true);
1604 }
1606 }
1607 else {
1608 for (edindex = 0; edindex < totedgesel; edindex += 1) {
1609 eed = edarray[edindex];
1610 bool non_manifold = BM_edge_face_count_is_over(eed, 2);
1611 if (non_manifold) {
1613 }
1614 else {
1615 walker_select(em, BMW_EDGELOOP, eed, true);
1616 }
1617 }
1619 }
1620 MEM_freeN(edarray);
1621 // if (EM_texFaceCheck())
1622
1623 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
1624 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
1625 }
1626
1627 return OPERATOR_FINISHED;
1628}
1629
1631{
1632 /* identifiers */
1633 ot->name = "Multi Select Loops";
1634 ot->idname = "MESH_OT_loop_multi_select";
1635 ot->description = "Select a loop of connected edges by connection type";
1636
1637 /* api callbacks */
1640
1641 /* flags */
1643
1644 /* properties */
1645 RNA_def_boolean(ot->srna, "ring", false, "Ring", "");
1646}
1647
1650/* -------------------------------------------------------------------- */
1654static void mouse_mesh_loop_face(BMEditMesh *em, BMEdge *eed, bool select, bool select_clear)
1655{
1656 if (select_clear) {
1658 }
1659
1661}
1662
1663static void mouse_mesh_loop_edge_ring(BMEditMesh *em, BMEdge *eed, bool select, bool select_clear)
1664{
1665 if (select_clear) {
1667 }
1668
1670}
1671
1673 BMEditMesh *em, BMEdge *eed, bool select, bool select_clear, bool select_cycle)
1674{
1675 bool edge_boundary = false;
1676 bool non_manifold = BM_edge_face_count_is_over(eed, 2);
1677
1678 /* Cycle between BMW_EDGELOOP / BMW_EDGEBOUNDARY. */
1679 if (select_cycle && BM_edge_is_boundary(eed)) {
1680 int count_by_select[2];
1681
1682 /* If the loops selected toggle the boundaries. */
1683 walker_select_count(em, BMW_EDGELOOP, eed, count_by_select);
1684 if (count_by_select[!select] == 0) {
1685 edge_boundary = true;
1686
1687 /* If the boundaries selected, toggle back to the loop. */
1688 walker_select_count(em, BMW_EDGEBOUNDARY, eed, count_by_select);
1689 if (count_by_select[!select] == 0) {
1690 edge_boundary = false;
1691 }
1692 }
1693 }
1694
1695 if (select_clear) {
1697 }
1698
1699 if (edge_boundary) {
1701 }
1702 else if (non_manifold) {
1704 }
1705 else {
1707 }
1708}
1709
1711 bContext *C, const int mval[2], bool extend, bool deselect, bool toggle, bool ring)
1712{
1713 Base *basact = nullptr;
1714 BMVert *eve = nullptr;
1715 BMEdge *eed = nullptr;
1716 BMFace *efa = nullptr;
1717
1718 BMEditMesh *em;
1719 bool select = true;
1720 bool select_clear = false;
1721 bool select_cycle = true;
1722 float mvalf[2];
1723
1725 mvalf[0] = float(vc.mval[0] = mval[0]);
1726 mvalf[1] = float(vc.mval[1] = mval[1]);
1727
1728 BMEditMesh *em_original = vc.em;
1729 const short selectmode = em_original->selectmode;
1730 em_original->selectmode = SCE_SELECT_EDGE;
1731
1733 vc.scene, vc.view_layer, vc.v3d);
1734
1735 {
1736 int base_index = -1;
1737 if (EDBM_unified_findnearest(&vc, bases, &base_index, &eve, &eed, &efa)) {
1738 basact = bases[base_index];
1740 em = vc.em;
1741 }
1742 else {
1743 em = nullptr;
1744 }
1745 }
1746
1747 em_original->selectmode = selectmode;
1748
1749 if (em == nullptr || eed == nullptr) {
1750 return false;
1751 }
1752
1753 if (extend == false && deselect == false && toggle == false) {
1754 select_clear = true;
1755 }
1756
1757 if (extend) {
1758 select = true;
1759 }
1760 else if (deselect) {
1761 select = false;
1762 }
1763 else if (select_clear || (BM_elem_flag_test(eed, BM_ELEM_SELECT) == 0)) {
1764 select = true;
1765 }
1766 else if (toggle) {
1767 select = false;
1768 select_cycle = false;
1769 }
1770
1771 if (select_clear) {
1772 for (Base *base_iter : bases) {
1773 Object *ob_iter = base_iter->object;
1774 BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter);
1775
1776 if (em_iter->bm->totvertsel == 0) {
1777 continue;
1778 }
1779
1780 if (em_iter == em) {
1781 continue;
1782 }
1783
1785 DEG_id_tag_update(static_cast<ID *>(ob_iter->data), ID_RECALC_SELECT);
1786 }
1787 }
1788
1789 if (em->selectmode & SCE_SELECT_FACE) {
1790 mouse_mesh_loop_face(em, eed, select, select_clear);
1791 }
1792 else {
1793 if (ring) {
1794 mouse_mesh_loop_edge_ring(em, eed, select, select_clear);
1795 }
1796 else {
1797 mouse_mesh_loop_edge(em, eed, select, select_clear, select_cycle);
1798 }
1799 }
1800
1802
1803 /* sets as active, useful for other tools */
1804 if (select) {
1805 if (em->selectmode & SCE_SELECT_VERTEX) {
1806 /* Find nearest vert from mouse
1807 * (initialize to large values in case only one vertex can be projected) */
1808 float v1_co[2], v2_co[2];
1809 float length_1 = FLT_MAX;
1810 float length_2 = FLT_MAX;
1811
1812 /* We can't be sure this has already been set... */
1814
1817 {
1818 length_1 = len_squared_v2v2(mvalf, v1_co);
1819 }
1820
1823 {
1824 length_2 = len_squared_v2v2(mvalf, v2_co);
1825 }
1826#if 0
1827 printf("mouse to v1: %f\nmouse to v2: %f\n",
1828 len_squared_v2v2(mvalf, v1_co),
1829 len_squared_v2v2(mvalf, v2_co));
1830#endif
1831 BM_select_history_store(em->bm, (length_1 < length_2) ? eed->v1 : eed->v2);
1832 }
1833 else if (em->selectmode & SCE_SELECT_EDGE) {
1834 BM_select_history_store(em->bm, eed);
1835 }
1836 else if (em->selectmode & SCE_SELECT_FACE) {
1837 /* Select the face of eed which is the nearest of mouse. */
1838 BMFace *f;
1839 BMIter iterf;
1840 float best_dist = FLT_MAX;
1841 efa = nullptr;
1842
1843 /* We can't be sure this has already been set... */
1845
1846 BM_ITER_ELEM (f, &iterf, eed, BM_FACES_OF_EDGE) {
1848 float cent[3];
1849 float co[2], tdist;
1850
1854 {
1855 tdist = len_squared_v2v2(mvalf, co);
1856 if (tdist < best_dist) {
1857 // printf("Best face: %p (%f)\n", f, tdist);
1858 best_dist = tdist;
1859 efa = f;
1860 }
1861 }
1862 }
1863 }
1864 if (efa) {
1865 BM_mesh_active_face_set(em->bm, efa);
1866 BM_select_history_store(em->bm, efa);
1867 }
1868 }
1869 }
1870
1871 DEG_id_tag_update(static_cast<ID *>(vc.obedit->data), ID_RECALC_SELECT);
1873
1874 return true;
1875}
1876
1877static int edbm_select_loop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1878{
1879
1881
1882 if (mouse_mesh_loop(C,
1883 event->mval,
1884 RNA_boolean_get(op->ptr, "extend"),
1885 RNA_boolean_get(op->ptr, "deselect"),
1886 RNA_boolean_get(op->ptr, "toggle"),
1887 RNA_boolean_get(op->ptr, "ring")))
1888 {
1889 return OPERATOR_FINISHED;
1890 }
1891 return OPERATOR_CANCELLED;
1892}
1893
1895{
1896 /* identifiers */
1897 ot->name = "Loop Select";
1898 ot->idname = "MESH_OT_loop_select";
1899 ot->description = "Select a loop of connected edges";
1900
1901 /* api callbacks */
1904
1905 /* flags */
1906 ot->flag = OPTYPE_UNDO;
1907
1908 /* properties */
1909 PropertyRNA *prop;
1910
1911 prop = RNA_def_boolean(ot->srna, "extend", false, "Extend Select", "Extend the selection");
1913 prop = RNA_def_boolean(ot->srna, "deselect", false, "Deselect", "Remove from the selection");
1915 prop = RNA_def_boolean(ot->srna, "toggle", false, "Toggle Select", "Toggle the selection");
1917 prop = RNA_def_boolean(ot->srna, "ring", false, "Select Ring", "Select ring");
1919}
1920
1922{
1923 /* description */
1924 ot->name = "Edge Ring Select";
1925 ot->idname = "MESH_OT_edgering_select";
1926 ot->description = "Select an edge ring";
1927
1928 /* callbacks */
1931
1932 /* flags */
1933 ot->flag = OPTYPE_UNDO;
1934
1935 /* Properties. */
1936 PropertyRNA *prop;
1937 prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
1939 prop = RNA_def_boolean(ot->srna, "deselect", false, "Deselect", "Remove from the selection");
1941 prop = RNA_def_boolean(ot->srna, "toggle", false, "Toggle Select", "Toggle the selection");
1943 prop = RNA_def_boolean(ot->srna, "ring", true, "Select Ring", "Select ring");
1945}
1946
1949/* -------------------------------------------------------------------- */
1954{
1955 const Scene *scene = CTX_data_scene(C);
1956 ViewLayer *view_layer = CTX_data_view_layer(C);
1957 int action = RNA_enum_get(op->ptr, "action");
1958
1960 scene, view_layer, CTX_wm_view3d(C));
1961
1962 if (action == SEL_TOGGLE) {
1963 action = SEL_SELECT;
1964 for (Object *obedit : objects) {
1966 if (em->bm->totvertsel || em->bm->totedgesel || em->bm->totfacesel) {
1967 action = SEL_DESELECT;
1968 break;
1969 }
1970 }
1971 }
1972
1973 for (Object *obedit : objects) {
1975 switch (action) {
1976 case SEL_SELECT:
1978 break;
1979 case SEL_DESELECT:
1981 break;
1982 case SEL_INVERT:
1983 EDBM_select_swap(em);
1985 break;
1986 }
1987 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
1988 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
1989 }
1990
1991 return OPERATOR_FINISHED;
1992}
1993
1995{
1996 /* identifiers */
1997 ot->name = "(De)select All";
1998 ot->idname = "MESH_OT_select_all";
1999 ot->description = "(De)select all vertices, edges or faces";
2000
2001 /* api callbacks */
2004
2005 /* flags */
2007
2009}
2010
2013/* -------------------------------------------------------------------- */
2018{
2019 const Scene *scene = CTX_data_scene(C);
2020 ViewLayer *view_layer = CTX_data_view_layer(C);
2022 scene, view_layer, CTX_wm_view3d(C));
2023
2024 for (Object *obedit : objects) {
2026
2027 if (!EDBM_select_interior_faces(em)) {
2028 continue;
2029 }
2030
2031 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
2032 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2033 }
2034
2035 return OPERATOR_FINISHED;
2036}
2037
2039{
2040 /* identifiers */
2041 ot->name = "Select Interior Faces";
2042 ot->idname = "MESH_OT_select_interior_faces";
2043 ot->description = "Select faces where all edges have more than 2 face users";
2044
2045 /* api callbacks */
2048
2049 /* flags */
2051}
2052
2055/* -------------------------------------------------------------------- */
2062bool EDBM_select_pick(bContext *C, const int mval[2], const SelectPick_Params *params)
2063{
2064 int base_index_active = -1;
2065 BMVert *eve = nullptr;
2066 BMEdge *eed = nullptr;
2067 BMFace *efa = nullptr;
2068
2069 /* setup view context for argument to callbacks */
2071 vc.mval[0] = mval[0];
2072 vc.mval[1] = mval[1];
2073
2075 vc.scene, vc.view_layer, vc.v3d);
2076
2077 bool changed = false;
2078 bool found = unified_findnearest(&vc, bases, &base_index_active, &eve, &eed, &efa);
2079
2080 if (params->sel_op == SEL_OP_SET) {
2081 BMElem *ele = efa ? (BMElem *)efa : (eed ? (BMElem *)eed : (BMElem *)eve);
2082 if ((found && params->select_passthrough) && BM_elem_flag_test(ele, BM_ELEM_SELECT)) {
2083 found = false;
2084 }
2085 else if (found || params->deselect_all) {
2086 /* Deselect everything. */
2087 for (Base *base_iter : bases) {
2088 Object *ob_iter = base_iter->object;
2090 DEG_id_tag_update(static_cast<ID *>(ob_iter->data), ID_RECALC_SELECT);
2092 }
2093 changed = true;
2094 }
2095 }
2096
2097 if (found) {
2098 Base *basact = bases[base_index_active];
2100
2101 if (efa) {
2102 switch (params->sel_op) {
2103 case SEL_OP_ADD: {
2104 BM_mesh_active_face_set(vc.em->bm, efa);
2105
2106 /* Work-around: deselect first, so we can guarantee it will
2107 * be active even if it was already selected. */
2108 BM_select_history_remove(vc.em->bm, efa);
2109 BM_face_select_set(vc.em->bm, efa, false);
2110 BM_select_history_store(vc.em->bm, efa);
2111 BM_face_select_set(vc.em->bm, efa, true);
2112 break;
2113 }
2114 case SEL_OP_SUB: {
2115 BM_select_history_remove(vc.em->bm, efa);
2116 BM_face_select_set(vc.em->bm, efa, false);
2117 break;
2118 }
2119 case SEL_OP_XOR: {
2120 BM_mesh_active_face_set(vc.em->bm, efa);
2121 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
2122 BM_select_history_store(vc.em->bm, efa);
2123 BM_face_select_set(vc.em->bm, efa, true);
2124 }
2125 else {
2126 BM_select_history_remove(vc.em->bm, efa);
2127 BM_face_select_set(vc.em->bm, efa, false);
2128 }
2129 break;
2130 }
2131 case SEL_OP_SET: {
2132 BM_mesh_active_face_set(vc.em->bm, efa);
2133 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
2134 BM_select_history_store(vc.em->bm, efa);
2135 BM_face_select_set(vc.em->bm, efa, true);
2136 }
2137 break;
2138 }
2139 case SEL_OP_AND: {
2140 BLI_assert_unreachable(); /* Doesn't make sense for picking. */
2141 break;
2142 }
2143 }
2144 }
2145 else if (eed) {
2146
2147 switch (params->sel_op) {
2148 case SEL_OP_ADD: {
2149 /* Work-around: deselect first, so we can guarantee it will
2150 * be active even if it was already selected. */
2151 BM_select_history_remove(vc.em->bm, eed);
2152 BM_edge_select_set(vc.em->bm, eed, false);
2153 BM_select_history_store(vc.em->bm, eed);
2154 BM_edge_select_set(vc.em->bm, eed, true);
2155 break;
2156 }
2157 case SEL_OP_SUB: {
2158 BM_select_history_remove(vc.em->bm, eed);
2159 BM_edge_select_set(vc.em->bm, eed, false);
2160 break;
2161 }
2162 case SEL_OP_XOR: {
2163 if (!BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
2164 BM_select_history_store(vc.em->bm, eed);
2165 BM_edge_select_set(vc.em->bm, eed, true);
2166 }
2167 else {
2168 BM_select_history_remove(vc.em->bm, eed);
2169 BM_edge_select_set(vc.em->bm, eed, false);
2170 }
2171 break;
2172 }
2173 case SEL_OP_SET: {
2174 if (!BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
2175 BM_select_history_store(vc.em->bm, eed);
2176 BM_edge_select_set(vc.em->bm, eed, true);
2177 }
2178 break;
2179 }
2180 case SEL_OP_AND: {
2181 BLI_assert_unreachable(); /* Doesn't make sense for picking. */
2182 break;
2183 }
2184 }
2185 }
2186 else if (eve) {
2187 switch (params->sel_op) {
2188 case SEL_OP_ADD: {
2189 /* Work-around: deselect first, so we can guarantee it will
2190 * be active even if it was already selected. */
2191 BM_select_history_remove(vc.em->bm, eve);
2192 BM_vert_select_set(vc.em->bm, eve, false);
2193 BM_select_history_store(vc.em->bm, eve);
2194 BM_vert_select_set(vc.em->bm, eve, true);
2195 break;
2196 }
2197 case SEL_OP_SUB: {
2198 BM_select_history_remove(vc.em->bm, eve);
2199 BM_vert_select_set(vc.em->bm, eve, false);
2200 break;
2201 }
2202 case SEL_OP_XOR: {
2203 if (!BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
2204 BM_select_history_store(vc.em->bm, eve);
2205 BM_vert_select_set(vc.em->bm, eve, true);
2206 }
2207 else {
2208 BM_select_history_remove(vc.em->bm, eve);
2209 BM_vert_select_set(vc.em->bm, eve, false);
2210 }
2211 break;
2212 }
2213 case SEL_OP_SET: {
2214 if (!BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
2215 BM_select_history_store(vc.em->bm, eve);
2216 BM_vert_select_set(vc.em->bm, eve, true);
2217 }
2218 break;
2219 }
2220 case SEL_OP_AND: {
2221 BLI_assert_unreachable(); /* Doesn't make sense for picking. */
2222 break;
2223 }
2224 }
2225 }
2226
2228
2229 if (efa) {
2230 /* Change active material on object. */
2231 if (efa->mat_nr != vc.obedit->actcol - 1) {
2232 vc.obedit->actcol = efa->mat_nr + 1;
2233 vc.em->mat_nr = efa->mat_nr;
2235 }
2236 }
2237
2238 /* Changing active object is handy since it allows us to
2239 * switch UV layers, vgroups for eg. */
2241 if (BKE_view_layer_active_base_get(vc.view_layer) != basact) {
2243 }
2244
2245 DEG_id_tag_update(static_cast<ID *>(vc.obedit->data), ID_RECALC_SELECT);
2247
2248 changed = true;
2249 }
2250
2251 return changed;
2252}
2253
2256/* -------------------------------------------------------------------- */
2261{
2262 BMEditSelection *ese, *nextese;
2263
2264 if (!(em->selectmode & SCE_SELECT_VERTEX)) {
2265 ese = static_cast<BMEditSelection *>(em->bm->selected.first);
2266 while (ese) {
2267 nextese = ese->next;
2268 if (ese->htype == BM_VERT) {
2269 BLI_freelinkN(&(em->bm->selected), ese);
2270 }
2271 ese = nextese;
2272 }
2273 }
2274 if (!(em->selectmode & SCE_SELECT_EDGE)) {
2275 ese = static_cast<BMEditSelection *>(em->bm->selected.first);
2276 while (ese) {
2277 nextese = ese->next;
2278 if (ese->htype == BM_EDGE) {
2279 BLI_freelinkN(&(em->bm->selected), ese);
2280 }
2281 ese = nextese;
2282 }
2283 }
2284 if (!(em->selectmode & SCE_SELECT_FACE)) {
2285 ese = static_cast<BMEditSelection *>(em->bm->selected.first);
2286 while (ese) {
2287 nextese = ese->next;
2288 if (ese->htype == BM_FACE) {
2289 BLI_freelinkN(&(em->bm->selected), ese);
2290 }
2291 ese = nextese;
2292 }
2293 }
2294}
2295
2297{
2298 BMVert *eve;
2299 BMEdge *eed;
2300 BMFace *efa;
2301 BMIter iter;
2302
2303 em->bm->selectmode = em->selectmode;
2304
2305 /* strip BMEditSelections from em->selected that are not relevant to new mode */
2307
2308 if (em->bm->totvertsel == 0 && em->bm->totedgesel == 0 && em->bm->totfacesel == 0) {
2309 return;
2310 }
2311
2312 if (em->selectmode & SCE_SELECT_VERTEX) {
2313 if (em->bm->totvertsel) {
2315 }
2316 }
2317 else if (em->selectmode & SCE_SELECT_EDGE) {
2318 /* deselect vertices, and select again based on edge select */
2319 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
2320 BM_vert_select_set(em->bm, eve, false);
2321 }
2322
2323 if (em->bm->totedgesel) {
2324 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
2326 BM_edge_select_set(em->bm, eed, true);
2327 }
2328 }
2329
2330 /* selects faces based on edge status */
2332 }
2333 }
2334 else if (em->selectmode & SCE_SELECT_FACE) {
2335 /* Deselect edges, and select again based on face select. */
2336 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
2337 BM_edge_select_set(em->bm, eed, false);
2338 }
2339
2340 if (em->bm->totfacesel) {
2341 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2343 BM_face_select_set(em->bm, efa, true);
2344 }
2345 }
2346 }
2347 }
2348}
2349
2351 const short selectmode_old,
2352 const short selectmode_new)
2353{
2354 BMesh *bm = em->bm;
2355
2356 BMVert *eve;
2357 BMEdge *eed;
2358 BMFace *efa;
2359 BMIter iter;
2360
2361 /* first tag-to-select, then select --- this avoids a feedback loop */
2362
2363 /* Have to find out what the selection-mode was previously. */
2364 if (selectmode_old == SCE_SELECT_VERTEX) {
2365 if (bm->totvertsel == 0) {
2366 /* pass */
2367 }
2368 else if (selectmode_new == SCE_SELECT_EDGE) {
2369 /* flush up (vert -> edge) */
2370
2371 /* select all edges associated with every selected vert */
2372 BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
2374 }
2375
2376 BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
2377 if (BM_elem_flag_test(eed, BM_ELEM_TAG)) {
2378 BM_edge_select_set(bm, eed, true);
2379 }
2380 }
2381 }
2382 else if (selectmode_new == SCE_SELECT_FACE) {
2383 /* flush up (vert -> face) */
2384
2385 /* select all faces associated with every selected vert */
2386 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2388 }
2389
2390 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2391 if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
2392 BM_face_select_set(bm, efa, true);
2393 }
2394 }
2395 }
2396 }
2397 else if (selectmode_old == SCE_SELECT_EDGE) {
2398 if (bm->totedgesel == 0) {
2399 /* pass */
2400 }
2401 else if (selectmode_new == SCE_SELECT_FACE) {
2402 /* flush up (edge -> face) */
2403
2404 /* select all faces associated with every selected edge */
2405 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2407 }
2408
2409 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2410 if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
2411 BM_face_select_set(bm, efa, true);
2412 }
2413 }
2414 }
2415 else if (selectmode_new == SCE_SELECT_VERTEX) {
2416 /* flush down (edge -> vert) */
2417
2418 BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
2420 BM_vert_select_set(bm, eve, false);
2421 }
2422 }
2423 /* deselect edges without both verts selected */
2425 }
2426 }
2427 else if (selectmode_old == SCE_SELECT_FACE) {
2428 if (bm->totfacesel == 0) {
2429 /* pass */
2430 }
2431 else if (selectmode_new == SCE_SELECT_EDGE) {
2432 /* flush down (face -> edge) */
2433
2434 BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
2436 BM_edge_select_set(bm, eed, false);
2437 }
2438 }
2439 /* Deselect faces without edges selected. */
2441 }
2442 else if (selectmode_new == SCE_SELECT_VERTEX) {
2443 /* flush down (face -> vert) */
2444
2445 BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
2447 BM_vert_select_set(bm, eve, false);
2448 }
2449 }
2450 /* deselect faces without verts selected */
2452 }
2453 }
2454}
2455
2457 const short selectmode_new,
2458 const int action,
2459 const bool use_extend,
2460 const bool use_expand)
2461{
2462 Scene *scene = CTX_data_scene(C);
2463 ViewLayer *view_layer = CTX_data_view_layer(C);
2465 Object *obedit = CTX_data_edit_object(C);
2466 BMEditMesh *em = nullptr;
2467 bool ret = false;
2468
2469 if (obedit && obedit->type == OB_MESH) {
2470 em = BKE_editmesh_from_object(obedit);
2471 }
2472
2473 if (em == nullptr) {
2474 return ret;
2475 }
2476
2477 bool only_update = false;
2478 switch (action) {
2479 case -1:
2480 /* already set */
2481 break;
2482 case 0: /* disable */
2483 /* check we have something to do */
2484 if ((em->selectmode & selectmode_new) == 0) {
2485 only_update = true;
2486 break;
2487 }
2488 em->selectmode &= ~selectmode_new;
2489 break;
2490 case 1: /* enable */
2491 /* check we have something to do */
2492 if ((em->selectmode & selectmode_new) != 0) {
2493 only_update = true;
2494 break;
2495 }
2496 em->selectmode |= selectmode_new;
2497 break;
2498 case 2: /* toggle */
2499 /* can't disable this flag if its the only one set */
2500 if (em->selectmode == selectmode_new) {
2501 only_update = true;
2502 break;
2503 }
2504 em->selectmode ^= selectmode_new;
2505 break;
2506 default:
2507 BLI_assert(0);
2508 break;
2509 }
2510
2512 scene, view_layer, CTX_wm_view3d(C));
2513
2514 for (Object *ob_iter : objects) {
2515 BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter);
2516 if (em_iter != em) {
2517 em_iter->selectmode = em->selectmode;
2518 }
2519 }
2520
2521 if (only_update) {
2522 return false;
2523 }
2524
2525 if (use_extend == 0 || em->selectmode == 0) {
2526 if (use_expand) {
2527 const short selmode_max = highest_order_bit_s(ts->selectmode);
2528 for (Object *ob_iter : objects) {
2529 BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter);
2530 EDBM_selectmode_convert(em_iter, selmode_max, selectmode_new);
2531 }
2532 }
2533 }
2534
2535 switch (selectmode_new) {
2536 case SCE_SELECT_VERTEX:
2537 if (use_extend == 0 || em->selectmode == 0) {
2539 }
2540 ret = true;
2541 break;
2542 case SCE_SELECT_EDGE:
2543 if (use_extend == 0 || em->selectmode == 0) {
2545 }
2546 ret = true;
2547 break;
2548 case SCE_SELECT_FACE:
2549 if (use_extend == 0 || em->selectmode == 0) {
2551 }
2552 ret = true;
2553 break;
2554 default:
2555 BLI_assert(0);
2556 break;
2557 }
2558
2559 if (ret == true) {
2560 ts->selectmode = em->selectmode;
2561 em = nullptr;
2562 for (Object *ob_iter : objects) {
2563 BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter);
2564 em_iter->selectmode = ts->selectmode;
2565 EDBM_selectmode_set(em_iter);
2566 DEG_id_tag_update(static_cast<ID *>(ob_iter->data),
2568 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob_iter->data);
2569 }
2572 }
2573
2574 return ret;
2575}
2576
2577bool EDBM_selectmode_set_multi(bContext *C, const short selectmode)
2578{
2579 BLI_assert(selectmode != 0);
2580 bool changed = false;
2581
2582 {
2583 Object *obedit = CTX_data_edit_object(C);
2584 BMEditMesh *em = nullptr;
2585 if (obedit && obedit->type == OB_MESH) {
2586 em = BKE_editmesh_from_object(obedit);
2587 }
2588 if (em == nullptr) {
2589 return changed;
2590 }
2591 }
2592
2593 ViewLayer *view_layer = CTX_data_view_layer(C);
2594 Scene *scene = CTX_data_scene(C);
2595 ToolSettings *ts = scene->toolsettings;
2596
2597 if (ts->selectmode != selectmode) {
2598 ts->selectmode = selectmode;
2599 changed = true;
2600 }
2601
2603 scene, view_layer, CTX_wm_view3d(C));
2604
2605 for (Object *ob_iter : objects) {
2606 BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter);
2607 if (em_iter->selectmode != ts->selectmode) {
2608 em_iter->selectmode = ts->selectmode;
2609 EDBM_selectmode_set(em_iter);
2610 DEG_id_tag_update(static_cast<ID *>(ob_iter->data),
2612 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob_iter->data);
2613 changed = true;
2614 }
2615 }
2616
2617 if (changed) {
2620 }
2621 return changed;
2622}
2623
2637{
2638 if (objects.size() <= 1) {
2639 return false;
2640 }
2641
2642 bool changed = false;
2643 BMEditMesh *em_active = BKE_editmesh_from_object(objects[0]);
2644 for (Object *obedit : objects) {
2646 if (em_active->selectmode == em->selectmode) {
2647 continue;
2648 }
2649 em->selectmode = em_active->selectmode;
2651 changed = true;
2652
2653 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SYNC_TO_EVAL | ID_RECALC_SELECT);
2654 WM_main_add_notifier(NC_GEOM | ND_SELECT, obedit->data);
2655 }
2656
2657 return changed;
2658}
2659
2661 BMEditMesh *em,
2662 const short selectmode_disable,
2663 const short selectmode_fallback)
2664{
2665 /* note essential, but switch out of vertex mode since the
2666 * selected regions won't be nicely isolated after flushing */
2667 if (em->selectmode & selectmode_disable) {
2668 if (em->selectmode == selectmode_disable) {
2669 em->selectmode = selectmode_fallback;
2670 }
2671 else {
2672 em->selectmode &= ~selectmode_disable;
2673 }
2674 scene->toolsettings->selectmode = em->selectmode;
2676
2678
2679 return true;
2680 }
2681 return false;
2682}
2683
2686/* -------------------------------------------------------------------- */
2690bool EDBM_deselect_by_material(BMEditMesh *em, const short index, const bool select)
2691{
2692 BMIter iter;
2693 BMFace *efa;
2694 bool changed = false;
2695
2696 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2698 continue;
2699 }
2700 if (efa->mat_nr == index) {
2701 changed = true;
2702 BM_face_select_set(em->bm, efa, select);
2703 }
2704 }
2705 return changed;
2706}
2707
2708void EDBM_select_toggle_all(BMEditMesh *em) /* exported for UV */
2709{
2710 if (em->bm->totvertsel || em->bm->totedgesel || em->bm->totfacesel) {
2712 }
2713 else {
2715 }
2716}
2717
2718void EDBM_select_swap(BMEditMesh *em) /* exported for UV */
2719{
2720 BMIter iter;
2721 BMVert *eve;
2722 BMEdge *eed;
2723 BMFace *efa;
2724
2725 if (em->bm->selectmode & SCE_SELECT_VERTEX) {
2726 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
2728 continue;
2729 }
2731 }
2732 }
2733 else if (em->selectmode & SCE_SELECT_EDGE) {
2734 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
2736 continue;
2737 }
2739 }
2740 }
2741 else {
2742 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2744 continue;
2745 }
2747 }
2748 }
2749}
2750
2752{
2753 bool changed_multi = false;
2754 for (Base *base_iter : bases) {
2755 Object *ob_iter = base_iter->object;
2756 BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter);
2757
2758 if (em_iter->bm->totvertsel == 0) {
2759 continue;
2760 }
2761
2763 DEG_id_tag_update(static_cast<ID *>(ob_iter->data), ID_RECALC_SELECT);
2764 changed_multi = true;
2765 }
2766 return changed_multi;
2767}
2768
2777
2779 const Span<Base *> bases,
2780 const short selectmode_disable,
2781 const short selectmode_fallback)
2782{
2783 bool changed_multi = false;
2784 for (Base *base_iter : bases) {
2785 Object *ob_iter = base_iter->object;
2786 BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter);
2787
2788 if (EDBM_selectmode_disable(scene, em_iter, selectmode_disable, selectmode_fallback)) {
2789 changed_multi = true;
2790 }
2791 }
2792 return changed_multi;
2793}
2794
2796 const short selectmode_disable,
2797 const short selectmode_fallback)
2798{
2800 Scene *scene = CTX_data_scene(C);
2803 vc.scene, vc.view_layer, nullptr);
2804 return EDBM_selectmode_disable_multi_ex(scene, bases, selectmode_disable, selectmode_fallback);
2805}
2806
2809/* -------------------------------------------------------------------- */
2826
2827static bool bm_interior_loop_filter_fn(const BMLoop *l, void * /*user_data*/)
2828{
2830 return false;
2831 }
2832 return true;
2833}
2835 int face_index,
2836 BMLoop *r_l_pair[2])
2837{
2838
2839 BMLoop *l_iter = e->l;
2840 int loop_index = 0;
2841 do {
2842 BMFace *f = l_iter->f;
2843 int i = BM_elem_index_get(f);
2844 if (!ELEM(i, -1, face_index)) {
2845 if (loop_index == 2) {
2846 return false;
2847 }
2848 r_l_pair[loop_index++] = l_iter;
2849 }
2850 } while ((l_iter = l_iter->radial_next) != e->l);
2851 return (loop_index == 2);
2852}
2853
2858static float bm_interior_face_group_calc_cost(ListBase *ls, const float *edge_lengths)
2859{
2860 /* Dividing by the area is important so larger face groups (which will become the outer shell)
2861 * aren't detected as having a high cost. */
2862 float area = 0.0f;
2863 float cost = 0.0f;
2864 bool found = false;
2865 LISTBASE_FOREACH (BMFaceLink *, f_link, ls) {
2866 BMFace *f = f_link->face;
2867 area += f_link->area;
2868 int i = BM_elem_index_get(f);
2869 BLI_assert(i != -1);
2870 BMLoop *l_iter, *l_first;
2871 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
2872 do {
2873 if (BM_elem_flag_test(l_iter->e, BM_ELEM_TAG)) {
2874 float cost_test = 0.0f;
2875 int cost_count = 0;
2876 /* All other faces. */
2877 BMLoop *l_radial_iter = l_iter;
2878 do {
2879 int i_other = BM_elem_index_get(l_radial_iter->f);
2880 if (!ELEM(i_other, -1, i)) {
2881 float angle = angle_normalized_v3v3(f->no, l_radial_iter->f->no);
2882 /* Ignore face direction since in the case on non-manifold faces connecting edges,
2883 * the face flipping may not be meaningful. */
2884 if (angle > DEG2RADF(90)) {
2885 angle = DEG2RADF(180) - angle;
2886 }
2887 /* Avoid calculating it inline, pass in pre-calculated edge lengths. */
2888#if 0
2889 cost_test += BM_edge_calc_length(l_iter->e) * angle;
2890#else
2891 BLI_assert(edge_lengths[BM_elem_index_get(l_iter->e)] != -1.0f);
2892 cost_test += edge_lengths[BM_elem_index_get(l_iter->e)] * angle;
2893#endif
2894 cost_count += 1;
2895 }
2896 } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter);
2897
2898 if (cost_count >= 2) {
2899 cost += cost_test;
2900 found = true;
2901 }
2902 }
2903 } while ((l_iter = l_iter->next) != l_first);
2904 }
2905 return found ? cost / area : FLT_MAX;
2906}
2907
2909{
2910 BMesh *bm = em->bm;
2911 BMIter iter;
2912 bool changed = false;
2913
2914 float *edge_lengths = static_cast<float *>(
2915 MEM_mallocN(sizeof(*edge_lengths) * bm->totedge, __func__));
2916
2917 {
2918 bool has_nonmanifold = false;
2919 BMEdge *e;
2920 int i;
2921 BM_ITER_MESH_INDEX (e, &iter, bm, BM_EDGES_OF_MESH, i) {
2922 const bool is_over = BM_edge_face_count_is_over(e, 2);
2923 if (is_over) {
2925 has_nonmanifold = true;
2926 edge_lengths[i] = BM_edge_calc_length(e);
2927 }
2928 else {
2930 edge_lengths[i] = -1.0;
2931 }
2932
2933 BM_elem_index_set(e, i); /* set_inline */
2934 }
2935 bm->elem_index_dirty &= ~BM_EDGE;
2936
2937 if (has_nonmanifold == false) {
2938 MEM_freeN(edge_lengths);
2939 return false;
2940 }
2941 }
2942
2943 /* group vars */
2944 int(*fgroup_index)[2];
2945 int fgroup_len;
2946
2947 int *fgroup_array = static_cast<int *>(
2948 MEM_mallocN(sizeof(*fgroup_array) * bm->totface, __func__));
2949 fgroup_len = BM_mesh_calc_face_groups(
2950 bm, fgroup_array, &fgroup_index, bm_interior_loop_filter_fn, nullptr, nullptr, 0, BM_EDGE);
2951
2952 int *fgroup_recalc_stack = static_cast<int *>(
2953 MEM_mallocN(sizeof(*fgroup_recalc_stack) * fgroup_len, __func__));
2954 STACK_DECLARE(fgroup_recalc_stack);
2955 STACK_INIT(fgroup_recalc_stack, fgroup_len);
2956
2958
2959 {
2960 BMFace *f;
2961 BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
2962 BM_elem_index_set(f, -1); /* set_dirty! */
2963 }
2964 }
2966
2967 ListBase *fgroup_listbase = static_cast<ListBase *>(
2968 MEM_callocN(sizeof(*fgroup_listbase) * fgroup_len, __func__));
2969 BMFaceLink *f_link_array = static_cast<BMFaceLink *>(
2970 MEM_callocN(sizeof(*f_link_array) * bm->totface, __func__));
2971
2972 for (int i = 0; i < fgroup_len; i++) {
2973 const int fg_sta = fgroup_index[i][0];
2974 const int fg_len = fgroup_index[i][1];
2975 for (int j = 0; j < fg_len; j++) {
2976 const int face_index = fgroup_array[fg_sta + j];
2977 BMFace *f = BM_face_at_index(bm, face_index);
2978 BM_elem_index_set(f, i);
2979
2980 BMFaceLink *f_link = &f_link_array[face_index];
2981 f_link->face = f;
2982 f_link->area = BM_face_calc_area(f);
2983 BLI_addtail(&fgroup_listbase[i], f_link);
2984 }
2985 }
2986
2987 MEM_freeN(fgroup_array);
2988 MEM_freeN(fgroup_index);
2989
2990 Heap *fgroup_heap = BLI_heap_new_ex(fgroup_len);
2991 HeapNode **fgroup_table = static_cast<HeapNode **>(
2992 MEM_mallocN(sizeof(*fgroup_table) * fgroup_len, __func__));
2993 bool *fgroup_dirty = static_cast<bool *>(
2994 MEM_callocN(sizeof(*fgroup_dirty) * fgroup_len, __func__));
2995
2996 for (int i = 0; i < fgroup_len; i++) {
2997 const float cost = bm_interior_face_group_calc_cost(&fgroup_listbase[i], edge_lengths);
2998 if (cost != FLT_MAX) {
2999 fgroup_table[i] = BLI_heap_insert(fgroup_heap, -cost, POINTER_FROM_INT(i));
3000 }
3001 else {
3002 fgroup_table[i] = nullptr;
3003 }
3004 }
3005
3006 /* Avoid re-running cost calculations for large face-groups which will end up forming the
3007 * outer shell and not be considered interior.
3008 * As these face groups become increasingly bigger - their chance of being considered
3009 * interior reduces as does the time to calculate their cost.
3010 *
3011 * This delays recalculating them until they are considered can dates to remove
3012 * which becomes less and less likely as they increase in area. */
3013
3014#define USE_DELAY_FACE_GROUP_COST_CALC
3015
3016 while (true) {
3017
3018#if defined(USE_DELAY_FACE_GROUP_COST_CALC)
3019 while (!BLI_heap_is_empty(fgroup_heap)) {
3020 HeapNode *node_min = BLI_heap_top(fgroup_heap);
3021 const int i = POINTER_AS_INT(BLI_heap_node_ptr(node_min));
3022 if (fgroup_dirty[i]) {
3023 const float cost = bm_interior_face_group_calc_cost(&fgroup_listbase[i], edge_lengths);
3024 if (cost != FLT_MAX) {
3025 /* The cost may have improves (we may be able to skip this),
3026 * however the cost should _never_ make this a choice. */
3027 BLI_assert(-BLI_heap_node_value(node_min) >= cost);
3028 BLI_heap_node_value_update(fgroup_heap, fgroup_table[i], -cost);
3029 }
3030 else {
3031 BLI_heap_remove(fgroup_heap, fgroup_table[i]);
3032 fgroup_table[i] = nullptr;
3033 }
3034 fgroup_dirty[i] = false;
3035 }
3036 else {
3037 break;
3038 }
3039 }
3040#endif
3041
3042 if (BLI_heap_is_empty(fgroup_heap)) {
3043 break;
3044 }
3045
3046 const int i_min = POINTER_AS_INT(BLI_heap_pop_min(fgroup_heap));
3047 BLI_assert(fgroup_table[i_min] != nullptr);
3048 BLI_assert(fgroup_dirty[i_min] == false);
3049 fgroup_table[i_min] = nullptr;
3050 changed = true;
3051
3052 while (BMFaceLink *f_link = static_cast<BMFaceLink *>(BLI_pophead(&fgroup_listbase[i_min]))) {
3053 BMFace *f = f_link->face;
3054 BM_face_select_set(bm, f, true);
3055 BM_elem_index_set(f, -1); /* set-dirty */
3056
3057 BMLoop *l_iter, *l_first;
3058
3059 /* Loop over edges face edges, merging groups which are no longer separated
3060 * by non-manifold edges (when manifold check ignores faces from this group). */
3061 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
3062 do {
3063 BMLoop *l_pair[2];
3064 if (bm_interior_edge_is_manifold_except_face_index(l_iter->e, i_min, l_pair)) {
3066
3067 int i_a = BM_elem_index_get(l_pair[0]->f);
3068 int i_b = BM_elem_index_get(l_pair[1]->f);
3069 if (i_a != i_b) {
3070 /* Only for predictable results that don't depend on the order of radial loops,
3071 * not essential. */
3072 if (i_a > i_b) {
3073 std::swap(i_a, i_b);
3074 }
3075
3076 /* Merge the groups. */
3077 LISTBASE_FOREACH (LinkData *, n, &fgroup_listbase[i_b]) {
3078 BMFace *f_iter = static_cast<BMFace *>(n->data);
3079 BM_elem_index_set(f_iter, i_a);
3080 }
3081 BLI_movelisttolist(&fgroup_listbase[i_a], &fgroup_listbase[i_b]);
3082
3083 /* This may have been added to 'fgroup_recalc_stack', instead of removing it,
3084 * just check the heap node isn't nullptr before recalculating. */
3085 BLI_heap_remove(fgroup_heap, fgroup_table[i_b]);
3086 fgroup_table[i_b] = nullptr;
3087 /* Keep the dirty flag as-is for 'i_b', because it may be in the 'fgroup_recalc_stack'
3088 * and we don't want to add it again.
3089 * Instead rely on the 'fgroup_table[i_b]' being nullptr as a secondary check. */
3090
3091 if (fgroup_dirty[i_a] == false) {
3092 BLI_assert(fgroup_table[i_a] != nullptr);
3093 STACK_PUSH(fgroup_recalc_stack, i_a);
3094 fgroup_dirty[i_a] = true;
3095 }
3096 }
3097 }
3098
3099 /* Mark all connected groups for re-calculation. */
3100 BMLoop *l_radial_iter = l_iter->radial_next;
3101 if (l_radial_iter != l_iter) {
3102 do {
3103 int i_other = BM_elem_index_get(l_radial_iter->f);
3104 if (!ELEM(i_other, -1, i_min)) {
3105 if ((fgroup_table[i_other] != nullptr) && (fgroup_dirty[i_other] == false)) {
3106#if !defined(USE_DELAY_FACE_GROUP_COST_CALC)
3107 STACK_PUSH(fgroup_recalc_stack, i_other);
3108#endif
3109 fgroup_dirty[i_other] = true;
3110 }
3111 }
3112 } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter);
3113 }
3114
3115 } while ((l_iter = l_iter->next) != l_first);
3116 }
3117
3118 for (int index = 0; index < STACK_SIZE(fgroup_recalc_stack); index++) {
3119 const int i = fgroup_recalc_stack[index];
3120 if (fgroup_table[i] != nullptr && fgroup_dirty[i] == true) {
3121 /* First update edge tags. */
3122 const float cost = bm_interior_face_group_calc_cost(&fgroup_listbase[i], edge_lengths);
3123 if (cost != FLT_MAX) {
3124 BLI_heap_node_value_update(fgroup_heap, fgroup_table[i], -cost);
3125 }
3126 else {
3127 BLI_heap_remove(fgroup_heap, fgroup_table[i]);
3128 fgroup_table[i] = nullptr;
3129 }
3130 }
3131 fgroup_dirty[i] = false;
3132 }
3133 STACK_CLEAR(fgroup_recalc_stack);
3134 }
3135
3136 MEM_freeN(edge_lengths);
3137 MEM_freeN(f_link_array);
3138 MEM_freeN(fgroup_listbase);
3139 MEM_freeN(fgroup_recalc_stack);
3140 MEM_freeN(fgroup_table);
3141 MEM_freeN(fgroup_dirty);
3142
3143 BLI_heap_free(fgroup_heap, nullptr);
3144
3145 return changed;
3146}
3147
3150/* -------------------------------------------------------------------- */
3156/* so we can have last-used default depend on selection mode (rare exception!) */
3157#define USE_LINKED_SELECT_DEFAULT_HACK
3158
3159struct DelimitData {
3161 int cd_loop_offset;
3162};
3163
3164static bool select_linked_delimit_test(BMEdge *e, int delimit, const DelimitData *delimit_data)
3165{
3166 BLI_assert(delimit);
3167
3168 if (delimit & BMO_DELIM_SEAM) {
3170 return true;
3171 }
3172 }
3173
3174 if (delimit & BMO_DELIM_SHARP) {
3175 if (BM_elem_flag_test(e, BM_ELEM_SMOOTH) == 0) {
3176 return true;
3177 }
3178 }
3179
3180 if (delimit & BMO_DELIM_NORMAL) {
3181 if (!BM_edge_is_contiguous(e)) {
3182 return true;
3183 }
3184 }
3185
3186 if (delimit & BMO_DELIM_MATERIAL) {
3187 if (e->l && e->l->radial_next != e->l) {
3188 const short mat_nr = e->l->f->mat_nr;
3189 BMLoop *l_iter = e->l->radial_next;
3190 do {
3191 if (l_iter->f->mat_nr != mat_nr) {
3192 return true;
3193 }
3194 } while ((l_iter = l_iter->radial_next) != e->l);
3195 }
3196 }
3197
3198 if (delimit & BMO_DELIM_UV) {
3200 e, delimit_data->cd_loop_type, delimit_data->cd_loop_offset) == 0)
3201 {
3202 return true;
3203 }
3204 }
3205
3206 return false;
3207}
3208
3209#ifdef USE_LINKED_SELECT_DEFAULT_HACK
3214static int select_linked_delimit_default_from_op(wmOperator *op, const int select_mode)
3215{
3216 static char delimit_last_store[2] = {0, BMO_DELIM_SEAM};
3217 int delimit_last_index = (select_mode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) == 0;
3218 char *delimit_last = &delimit_last_store[delimit_last_index];
3219 PropertyRNA *prop_delimit = RNA_struct_find_property(op->ptr, "delimit");
3220 int delimit;
3221
3222 if (RNA_property_is_set(op->ptr, prop_delimit)) {
3223 delimit = RNA_property_enum_get(op->ptr, prop_delimit);
3224 *delimit_last = delimit;
3225 }
3226 else {
3227 delimit = *delimit_last;
3228 RNA_property_enum_set(op->ptr, prop_delimit, delimit);
3229 }
3230 return delimit;
3231}
3232#endif
3233
3234static void select_linked_delimit_validate(BMesh *bm, int *delimit)
3235{
3236 if ((*delimit) & BMO_DELIM_UV) {
3238 (*delimit) &= ~BMO_DELIM_UV;
3239 }
3240 }
3241}
3242
3243static void select_linked_delimit_begin(BMesh *bm, int delimit)
3244{
3245 DelimitData delimit_data{};
3246
3247 if (delimit & BMO_DELIM_UV) {
3248 delimit_data.cd_loop_type = CD_PROP_FLOAT2;
3249 delimit_data.cd_loop_offset = CustomData_get_offset(&bm->ldata, delimit_data.cd_loop_type);
3250 if (delimit_data.cd_loop_offset == -1) {
3251 delimit &= ~BMO_DELIM_UV;
3252 }
3253 }
3254
3255 /* Shouldn't need to allocated BMO flags here (sigh). */
3257
3258 {
3259 BMIter iter;
3260 BMEdge *e;
3261
3262 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
3263 const bool is_walk_ok = (select_linked_delimit_test(e, delimit, &delimit_data) == false);
3264
3265 BMO_edge_flag_set(bm, e, BMO_ELE_TAG, is_walk_ok);
3266 }
3267 }
3268}
3269
3271{
3272 BMesh *bm = em->bm;
3273
3275}
3276
3278{
3279 Scene *scene = CTX_data_scene(C);
3280 ViewLayer *view_layer = CTX_data_view_layer(C);
3281
3282#ifdef USE_LINKED_SELECT_DEFAULT_HACK
3283 const int delimit_init = select_linked_delimit_default_from_op(op,
3284 scene->toolsettings->selectmode);
3285#else
3286 const int delimit_init = RNA_enum_get(op->ptr, "delimit");
3287#endif
3288
3290 scene, view_layer, CTX_wm_view3d(C));
3291
3292 for (Object *obedit : objects) {
3293
3295 BMesh *bm = em->bm;
3296 BMIter iter;
3297 BMWalker walker;
3298
3299 int delimit = delimit_init;
3300
3302
3303 if (delimit) {
3304 select_linked_delimit_begin(em->bm, delimit);
3305 }
3306
3307 if (em->selectmode & SCE_SELECT_VERTEX) {
3308 BMVert *v;
3309
3310 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
3312 }
3313
3314 /* exclude all delimited verts */
3315 if (delimit) {
3316 BMEdge *e;
3317 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
3319 /* Check the edge for selected faces,
3320 * this supports stepping off isolated vertices which would otherwise be ignored. */
3324 }
3325 }
3326 }
3327 }
3328
3329 BMW_init(&walker,
3330 em->bm,
3333 delimit ? BMO_ELE_TAG : BMW_MASK_NOP,
3336 BMW_NIL_LAY);
3337
3338 if (delimit) {
3339 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
3341 BMElem *ele_walk;
3342 BMW_ITER (ele_walk, &walker, v) {
3343 if (ele_walk->head.htype == BM_LOOP) {
3344 BMVert *v_step = ((BMLoop *)ele_walk)->v;
3345 BM_vert_select_set(em->bm, v_step, true);
3347 }
3348 else {
3349 BMEdge *e_step = (BMEdge *)ele_walk;
3350 BLI_assert(ele_walk->head.htype == BM_EDGE);
3351 BM_edge_select_set(em->bm, e_step, true);
3354 }
3355 }
3356 }
3357 }
3358 }
3359 else {
3360 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
3362 BMEdge *e_walk;
3363 BMW_ITER (e_walk, &walker, v) {
3364 BM_edge_select_set(em->bm, e_walk, true);
3366 }
3367 }
3368 }
3369 }
3370
3371 BMW_end(&walker);
3372
3374 }
3375 else if (em->selectmode & SCE_SELECT_EDGE) {
3376 BMEdge *e;
3377
3378 if (delimit) {
3379 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
3380 /* Check the edge for selected faces,
3381 * this supports stepping off isolated edges which would otherwise be ignored. */
3387 }
3388 }
3389 else {
3390 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
3392 }
3393 }
3394
3395 BMW_init(&walker,
3396 em->bm,
3399 delimit ? BMO_ELE_TAG : BMW_MASK_NOP,
3402 BMW_NIL_LAY);
3403
3404 if (delimit) {
3405 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
3407 BMElem *ele_walk;
3408 BMW_ITER (ele_walk, &walker, e) {
3409 if (ele_walk->head.htype == BM_LOOP) {
3410 BMLoop *l_step = (BMLoop *)ele_walk;
3411 BM_edge_select_set(em->bm, l_step->e, true);
3412 BM_edge_select_set(em->bm, l_step->prev->e, 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);
3420 }
3421 }
3422 }
3423 }
3424 }
3425 else {
3426 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
3428 BMEdge *e_walk;
3429 BMW_ITER (e_walk, &walker, e) {
3430 BM_edge_select_set(em->bm, e_walk, true);
3432 }
3433 }
3434 }
3435 }
3436
3437 BMW_end(&walker);
3438
3440 }
3441 else {
3442 BMFace *f;
3443
3444 BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
3446 }
3447
3448 BMW_init(&walker,
3449 bm,
3450 BMW_ISLAND,
3452 delimit ? BMO_ELE_TAG : BMW_MASK_NOP,
3455 BMW_NIL_LAY);
3456
3457 BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
3459 BMFace *f_walk;
3460 BMW_ITER (f_walk, &walker, f) {
3461 BM_face_select_set(bm, f_walk, true);
3463 }
3464 }
3465 }
3466
3467 BMW_end(&walker);
3468 }
3469
3470 if (delimit) {
3472 }
3473
3474 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
3475 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
3476 }
3477
3478 return OPERATOR_FINISHED;
3479}
3480
3482{
3483 PropertyRNA *prop;
3484
3485 /* identifiers */
3486 ot->name = "Select Linked All";
3487 ot->idname = "MESH_OT_select_linked";
3488 ot->description = "Select all vertices connected to the current selection";
3489
3490 /* api callbacks */
3493
3494 /* flags */
3496
3497 prop = RNA_def_enum_flag(ot->srna,
3498 "delimit",
3501 "Delimit",
3502 "Delimit selected region");
3503#ifdef USE_LINKED_SELECT_DEFAULT_HACK
3505#else
3506 UNUSED_VARS(prop);
3507#endif
3508}
3509
3512/* -------------------------------------------------------------------- */
3517
3518static void edbm_select_linked_pick_ex(BMEditMesh *em, BMElem *ele, bool sel, int delimit)
3519{
3520 BMesh *bm = em->bm;
3521 BMWalker walker;
3522
3524
3525 if (delimit) {
3527 }
3528
3529 /* NOTE: logic closely matches #edbm_select_linked_exec, keep in sync. */
3530
3531 if (ele->head.htype == BM_VERT) {
3532 BMVert *eve = (BMVert *)ele;
3533
3534 BMW_init(&walker,
3535 bm,
3538 delimit ? BMO_ELE_TAG : BMW_MASK_NOP,
3541 BMW_NIL_LAY);
3542
3543 if (delimit) {
3544 BMElem *ele_walk;
3545 BMW_ITER (ele_walk, &walker, eve) {
3546 if (ele_walk->head.htype == BM_LOOP) {
3547 BMVert *v_step = ((BMLoop *)ele_walk)->v;
3548 BM_vert_select_set(bm, v_step, sel);
3549 }
3550 else {
3551 BMEdge *e_step = (BMEdge *)ele_walk;
3552 BLI_assert(ele_walk->head.htype == BM_EDGE);
3553 BM_edge_select_set(bm, e_step, sel);
3554 }
3555 }
3556 }
3557 else {
3558 BMEdge *e_walk;
3559 BMW_ITER (e_walk, &walker, eve) {
3560 BM_edge_select_set(bm, e_walk, sel);
3561 }
3562 }
3563
3564 BMW_end(&walker);
3565
3567 }
3568 else if (ele->head.htype == BM_EDGE) {
3569 BMEdge *eed = (BMEdge *)ele;
3570
3571 BMW_init(&walker,
3572 bm,
3575 delimit ? BMO_ELE_TAG : BMW_MASK_NOP,
3578 BMW_NIL_LAY);
3579
3580 if (delimit) {
3581 BMElem *ele_walk;
3582 BMW_ITER (ele_walk, &walker, eed) {
3583 if (ele_walk->head.htype == BM_LOOP) {
3584 BMEdge *e_step = ((BMLoop *)ele_walk)->e;
3585 BM_edge_select_set(bm, e_step, sel);
3586 }
3587 else {
3588 BMEdge *e_step = (BMEdge *)ele_walk;
3589 BLI_assert(ele_walk->head.htype == BM_EDGE);
3590 BM_edge_select_set(bm, e_step, sel);
3591 }
3592 }
3593 }
3594 else {
3595 BMEdge *e_walk;
3596 BMW_ITER (e_walk, &walker, eed) {
3597 BM_edge_select_set(bm, e_walk, sel);
3598 }
3599 }
3600
3601 BMW_end(&walker);
3602
3604 }
3605 else if (ele->head.htype == BM_FACE) {
3606 BMFace *efa = (BMFace *)ele;
3607
3608 BMW_init(&walker,
3609 bm,
3610 BMW_ISLAND,
3612 delimit ? BMO_ELE_TAG : BMW_MASK_NOP,
3615 BMW_NIL_LAY);
3616
3617 {
3618 BMFace *f_walk;
3619 BMW_ITER (f_walk, &walker, efa) {
3620 BM_face_select_set(bm, f_walk, sel);
3622 }
3623 }
3624
3625 BMW_end(&walker);
3626 }
3627
3628 if (delimit) {
3630 }
3631}
3632
3634{
3635 Base *basact = nullptr;
3636 BMVert *eve;
3637 BMEdge *eed;
3638 BMFace *efa;
3639 const bool sel = !RNA_boolean_get(op->ptr, "deselect");
3640 int index;
3641
3642 if (RNA_struct_property_is_set(op->ptr, "index")) {
3643 return edbm_select_linked_pick_exec(C, op);
3644 }
3645
3646 /* #unified_findnearest needs OpenGL. */
3648
3649 /* setup view context for argument to callbacks */
3651
3653 vc.scene, vc.view_layer, vc.v3d);
3654
3655 {
3656 bool has_edges = false;
3657 for (Base *base : bases) {
3658 Object *ob_iter = base->object;
3660 if (vc.em->bm->totedge) {
3661 has_edges = true;
3662 }
3663 }
3664 if (has_edges == false) {
3665 return OPERATOR_CANCELLED;
3666 }
3667 }
3668
3669 vc.mval[0] = event->mval[0];
3670 vc.mval[1] = event->mval[1];
3671
3672 /* return warning! */
3673 {
3674 int base_index = -1;
3675 const bool ok = unified_findnearest(&vc, bases, &base_index, &eve, &eed, &efa);
3676 if (!ok) {
3677 return OPERATOR_CANCELLED;
3678 }
3679 basact = bases[base_index];
3680 }
3681
3683 BMEditMesh *em = vc.em;
3684 BMesh *bm = em->bm;
3685
3686#ifdef USE_LINKED_SELECT_DEFAULT_HACK
3688#else
3689 int delimit = RNA_enum_get(op->ptr, "delimit");
3690#endif
3691
3692 BMElem *ele = EDBM_elem_from_selectmode(em, eve, eed, efa);
3693
3694 edbm_select_linked_pick_ex(em, ele, sel, delimit);
3695
3696 /* To support redo. */
3697 {
3698 /* Note that the `base_index` can't be used as the index depends on the 3D Viewport
3699 * which might not be available on redo. */
3701 int object_index;
3702 index = EDBM_elem_to_index_any_multi(vc.scene, vc.view_layer, em, ele, &object_index);
3703 BLI_assert(object_index >= 0);
3704 RNA_int_set(op->ptr, "object_index", object_index);
3705 RNA_int_set(op->ptr, "index", index);
3706 }
3707
3708 DEG_id_tag_update(static_cast<ID *>(basact->object->data), ID_RECALC_SELECT);
3710
3711 return OPERATOR_FINISHED;
3712}
3713
3715{
3716 Object *obedit = nullptr;
3717 BMElem *ele;
3718
3719 {
3720 const Scene *scene = CTX_data_scene(C);
3721 ViewLayer *view_layer = CTX_data_view_layer(C);
3722 /* Intentionally wrap negative values so the lookup fails. */
3723 const uint object_index = uint(RNA_int_get(op->ptr, "object_index"));
3724 const uint index = uint(RNA_int_get(op->ptr, "index"));
3725 ele = EDBM_elem_from_index_any_multi(scene, view_layer, object_index, index, &obedit);
3726 }
3727
3728 if (ele == nullptr) {
3729 return OPERATOR_CANCELLED;
3730 }
3731
3733 const bool sel = !RNA_boolean_get(op->ptr, "deselect");
3734
3735#ifdef USE_LINKED_SELECT_DEFAULT_HACK
3737#else
3738 int delimit = RNA_enum_get(op->ptr, "delimit");
3739#endif
3740
3741 edbm_select_linked_pick_ex(em, ele, sel, delimit);
3742
3743 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
3745
3746 return OPERATOR_FINISHED;
3747}
3748
3750{
3751 PropertyRNA *prop;
3752
3753 /* identifiers */
3754 ot->name = "Select Linked";
3755 ot->idname = "MESH_OT_select_linked_pick";
3756 ot->description = "(De)select all vertices linked to the edge under the mouse cursor";
3757
3758 /* api callbacks */
3762
3763 /* flags */
3765
3766 RNA_def_boolean(ot->srna, "deselect", false, "Deselect", "");
3767 prop = RNA_def_enum_flag(ot->srna,
3768 "delimit",
3771 "Delimit",
3772 "Delimit selected region");
3773#ifdef USE_LINKED_SELECT_DEFAULT_HACK
3775#endif
3776
3777 /* use for redo */
3778 prop = RNA_def_int(ot->srna, "object_index", -1, -1, INT_MAX, "", "", 0, INT_MAX);
3780 prop = RNA_def_int(ot->srna, "index", -1, -1, INT_MAX, "", "", 0, INT_MAX);
3782}
3783
3786/* -------------------------------------------------------------------- */
3791{
3792 const Scene *scene = CTX_data_scene(C);
3793 ViewLayer *view_layer = CTX_data_view_layer(C);
3794 const bool extend = RNA_boolean_get(op->ptr, "extend");
3795 const int numverts = RNA_int_get(op->ptr, "number");
3796 const int type = RNA_enum_get(op->ptr, "type");
3798 scene, view_layer, CTX_wm_view3d(C));
3799
3800 for (Object *obedit : objects) {
3802 BMFace *efa;
3803 BMIter iter;
3804
3805 if (!extend) {
3807 }
3808
3809 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3810 bool select;
3811
3812 switch (type) {
3813 case 0:
3814 select = (efa->len < numverts);
3815 break;
3816 case 1:
3817 select = (efa->len == numverts);
3818 break;
3819 case 2:
3820 select = (efa->len > numverts);
3821 break;
3822 case 3:
3823 select = (efa->len != numverts);
3824 break;
3825 default:
3826 BLI_assert(0);
3827 select = false;
3828 break;
3829 }
3830
3831 if (select) {
3832 BM_face_select_set(em->bm, efa, true);
3833 }
3834 }
3835
3837
3838 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
3839 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
3840 }
3841
3842 return OPERATOR_FINISHED;
3843}
3844
3846{
3847 static const EnumPropertyItem type_items[] = {
3848 {0, "LESS", false, "Less Than", ""},
3849 {1, "EQUAL", false, "Equal To", ""},
3850 {2, "GREATER", false, "Greater Than", ""},
3851 {3, "NOTEQUAL", false, "Not Equal To", ""},
3852 {0, nullptr, 0, nullptr, nullptr},
3853 };
3854
3855 /* identifiers */
3856 ot->name = "Select Faces by Sides";
3857 ot->description = "Select vertices or faces by the number of face sides";
3858 ot->idname = "MESH_OT_select_face_by_sides";
3859
3860 /* api callbacks */
3863
3864 /* flags */
3866
3867 /* properties */
3868 RNA_def_int(ot->srna, "number", 4, 3, INT_MAX, "Number of Vertices", "", 3, INT_MAX);
3869 RNA_def_enum(ot->srna, "type", type_items, 1, "Type", "Type of comparison to make");
3870 RNA_def_boolean(ot->srna, "extend", true, "Extend", "Extend the selection");
3871}
3872
3875/* -------------------------------------------------------------------- */
3880{
3881 const Scene *scene = CTX_data_scene(C);
3882 ViewLayer *view_layer = CTX_data_view_layer(C);
3883 const bool extend = RNA_boolean_get(op->ptr, "extend");
3884
3886 scene, view_layer, CTX_wm_view3d(C));
3887
3888 for (Object *obedit : objects) {
3890 BMesh *bm = em->bm;
3891 BMIter iter;
3892
3893 if (!extend) {
3895 }
3896
3897 if (em->selectmode & SCE_SELECT_VERTEX) {
3898 BMVert *eve;
3899 BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
3900 if (!eve->e) {
3901 BM_vert_select_set(bm, eve, true);
3902 }
3903 }
3904 }
3905
3906 if (em->selectmode & SCE_SELECT_EDGE) {
3907 BMEdge *eed;
3908 BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
3909 if (BM_edge_is_wire(eed)) {
3910 BM_edge_select_set(bm, eed, true);
3911 }
3912 }
3913 }
3914
3915 if (em->selectmode & SCE_SELECT_FACE) {
3916 BMFace *efa;
3917 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
3918 BMIter liter;
3919 BMLoop *l;
3920 bool is_loose = true;
3921 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3922 if (!BM_edge_is_boundary(l->e)) {
3923 is_loose = false;
3924 break;
3925 }
3926 }
3927 if (is_loose) {
3928 BM_face_select_set(bm, efa, true);
3929 }
3930 }
3931 }
3932
3934
3935 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
3936 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
3937 }
3938
3939 return OPERATOR_FINISHED;
3940}
3941
3943{
3944 /* identifiers */
3945 ot->name = "Select Loose Geometry";
3946 ot->description = "Select loose geometry based on the selection mode";
3947 ot->idname = "MESH_OT_select_loose";
3948
3949 /* api callbacks */
3952
3953 /* flags */
3955
3956 /* props */
3957 RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
3958}
3959
3962/* -------------------------------------------------------------------- */
3967{
3968 const Scene *scene = CTX_data_scene(C);
3969 ViewLayer *view_layer = CTX_data_view_layer(C);
3970 const int axis_flag = RNA_enum_get(op->ptr, "axis");
3971 const bool extend = RNA_boolean_get(op->ptr, "extend");
3972 Object *obedit_active = CTX_data_edit_object(C);
3973 BMEditMesh *em_active = BKE_editmesh_from_object(obedit_active);
3974 const int select_mode = em_active->bm->selectmode;
3975 int tot_mirr = 0, tot_fail = 0;
3976
3978 scene, view_layer, CTX_wm_view3d(C));
3979
3980 for (Object *obedit : objects) {
3982
3983 if (em->bm->totvertsel == 0) {
3984 continue;
3985 }
3986
3987 int tot_mirr_iter = 0, tot_fail_iter = 0;
3988
3989 for (int axis = 0; axis < 3; axis++) {
3990 if ((1 << axis) & axis_flag) {
3992 static_cast<const Mesh *>(obedit->data),
3993 axis,
3994 extend,
3995 &tot_mirr_iter,
3996 &tot_fail_iter);
3997 }
3998 }
3999
4000 if (tot_mirr_iter) {
4002
4003 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
4004 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
4005 }
4006
4007 tot_fail += tot_fail_iter;
4008 tot_mirr += tot_mirr_iter;
4009 }
4010
4011 if (tot_mirr || tot_fail) {
4012 ED_mesh_report_mirror_ex(op, tot_mirr, tot_fail, select_mode);
4013 }
4014 return OPERATOR_FINISHED;
4015}
4016
4018{
4019 /* identifiers */
4020 ot->name = "Select Mirror";
4021 ot->description = "Select mesh items at mirrored locations";
4022 ot->idname = "MESH_OT_select_mirror";
4023
4024 /* api callbacks */
4027
4028 /* flags */
4030
4031 /* props */
4032 RNA_def_enum_flag(ot->srna, "axis", rna_enum_axis_flag_xyz_items, (1 << 0), "Axis", "");
4033
4034 RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the existing selection");
4035}
4036
4039/* -------------------------------------------------------------------- */
4044{
4045 const Scene *scene = CTX_data_scene(C);
4046 ViewLayer *view_layer = CTX_data_view_layer(C);
4047 const bool use_face_step = RNA_boolean_get(op->ptr, "use_face_step");
4048
4050 scene, view_layer, CTX_wm_view3d(C));
4051 for (Object *obedit : objects) {
4053 BMesh *bm = em->bm;
4054
4055 if ((bm->totvertsel == 0) && (bm->totedgesel == 0) && (bm->totfacesel == 0)) {
4056 continue;
4057 }
4058
4059 EDBM_select_more(em, use_face_step);
4060 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
4061 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
4062 }
4063
4064 return OPERATOR_FINISHED;
4065}
4066
4068{
4069 /* identifiers */
4070 ot->name = "Select More";
4071 ot->idname = "MESH_OT_select_more";
4072 ot->description = "Select more vertices, edges or faces connected to initial selection";
4073
4074 /* api callbacks */
4077
4078 /* flags */
4080
4082 ot->srna, "use_face_step", true, "Face Step", "Connected faces (instead of edges)");
4083}
4084
4087/* -------------------------------------------------------------------- */
4092{
4093 const Scene *scene = CTX_data_scene(C);
4094 ViewLayer *view_layer = CTX_data_view_layer(C);
4095 const bool use_face_step = RNA_boolean_get(op->ptr, "use_face_step");
4096
4098 scene, view_layer, CTX_wm_view3d(C));
4099 for (Object *obedit : objects) {
4101 BMesh *bm = em->bm;
4102
4103 if ((bm->totvertsel == 0) && (bm->totedgesel == 0) && (bm->totfacesel == 0)) {
4104 continue;
4105 }
4106
4107 EDBM_select_less(em, use_face_step);
4108 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
4109 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
4110 }
4111
4112 return OPERATOR_FINISHED;
4113}
4114
4116{
4117 /* identifiers */
4118 ot->name = "Select Less";
4119 ot->idname = "MESH_OT_select_less";
4120 ot->description = "Deselect vertices, edges or faces at the boundary of each selection region";
4121
4122 /* api callbacks */
4125
4126 /* flags */
4128
4130 ot->srna, "use_face_step", true, "Face Step", "Connected faces (instead of edges)");
4131}
4132
4135/* -------------------------------------------------------------------- */
4143{
4144 BMIter viter;
4145 BMVert *v;
4146
4147 BM_ITER_ELEM (v, &viter, e, BM_VERTS_OF_EDGE) {
4148 BMIter eiter;
4149 BMEdge *e_other;
4150
4151 BM_ITER_ELEM (e_other, &eiter, v, BM_EDGES_OF_VERT) {
4152 if ((e_other != e) && BM_elem_flag_test(e_other, BM_ELEM_SELECT)) {
4153 return false;
4154 }
4155 }
4156 }
4157 return true;
4158}
4159
4160/* Walk all reachable elements of the same type as h_act in breadth-first
4161 * order, starting from h_act. Deselects elements if the depth when they
4162 * are reached is not a multiple of "nth". */
4164 const CheckerIntervalParams *op_params,
4165 BMHeader *h_act)
4166{
4167 BMElem *ele;
4168 BMesh *bm = em->bm;
4169 BMWalker walker;
4170 BMIter iter;
4171 int walktype = 0, itertype = 0, flushtype = 0;
4172 short mask_vert = 0, mask_edge = 0, mask_face = 0;
4173
4174 /* No active element from which to start - nothing to do */
4175 if (h_act == nullptr) {
4176 return;
4177 }
4178
4179 /* Determine which type of iter, walker, and select flush to use
4180 * based on type of the elements being deselected */
4181 switch (h_act->htype) {
4182 case BM_VERT:
4183 itertype = BM_VERTS_OF_MESH;
4184 walktype = BMW_CONNECTED_VERTEX;
4185 flushtype = SCE_SELECT_VERTEX;
4186 mask_vert = BMO_ELE_TAG;
4187 break;
4188 case BM_EDGE:
4189 /* When an edge has no connected-selected edges,
4190 * use face-stepping (supports edge-rings) */
4191 itertype = BM_EDGES_OF_MESH;
4193 flushtype = SCE_SELECT_EDGE;
4194 mask_edge = BMO_ELE_TAG;
4195 break;
4196 case BM_FACE:
4197 itertype = BM_FACES_OF_MESH;
4198 walktype = BMW_ISLAND;
4199 flushtype = SCE_SELECT_FACE;
4200 mask_face = BMO_ELE_TAG;
4201 break;
4202 }
4203
4204 /* Shouldn't need to allocate BMO flags here (sigh). */
4206
4207 /* Walker restrictions uses BMO flags, not header flags,
4208 * so transfer BM_ELEM_SELECT from HFlags onto a BMO flag layer. */
4209 BMO_push(bm, nullptr);
4210 BM_ITER_MESH (ele, &iter, bm, itertype) {
4213 }
4214 }
4215
4216 /* Walk over selected elements starting at active */
4217 BMW_init(&walker,
4218 bm,
4219 walktype,
4220 mask_vert,
4221 mask_edge,
4222 mask_face,
4223 BMW_FLAG_NOP, /* Don't use #BMW_FLAG_TEST_HIDDEN here since we want to deselect all. */
4224 BMW_NIL_LAY);
4225
4226 /* use tag to avoid touching the same verts twice */
4227 BM_ITER_MESH (ele, &iter, bm, itertype) {
4229 }
4230
4232 for (ele = static_cast<BMElem *>(BMW_begin(&walker, h_act)); ele != nullptr;
4233 ele = static_cast<BMElem *>(BMW_step(&walker)))
4234 {
4235 if (!BM_elem_flag_test(ele, BM_ELEM_TAG)) {
4236 /* Deselect elements that aren't at "nth" depth from active */
4237 const int depth = BMW_current_depth(&walker) - 1;
4238 if (!WM_operator_properties_checker_interval_test(op_params, depth)) {
4239 BM_elem_select_set(bm, ele, false);
4240 }
4242 }
4243 }
4244 BMW_end(&walker);
4245
4246 BMO_pop(bm);
4247
4248 /* Flush selection up */
4249 EDBM_selectmode_flush_ex(em, flushtype);
4250}
4251
4252static void deselect_nth_active(BMEditMesh *em, BMVert **r_eve, BMEdge **r_eed, BMFace **r_efa)
4253{
4254 BMIter iter;
4255 BMElem *ele;
4256
4257 *r_eve = nullptr;
4258 *r_eed = nullptr;
4259 *r_efa = nullptr;
4260
4262 ele = BM_mesh_active_elem_get(em->bm);
4263
4264 if (ele && BM_elem_flag_test(ele, BM_ELEM_SELECT)) {
4265 switch (ele->head.htype) {
4266 case BM_VERT:
4267 *r_eve = (BMVert *)ele;
4268 return;
4269 case BM_EDGE:
4270 *r_eed = (BMEdge *)ele;
4271 return;
4272 case BM_FACE:
4273 *r_efa = (BMFace *)ele;
4274 return;
4275 }
4276 }
4277
4278 if (em->selectmode & SCE_SELECT_VERTEX) {
4279 BMVert *v;
4280 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
4282 *r_eve = v;
4283 return;
4284 }
4285 }
4286 }
4287 else if (em->selectmode & SCE_SELECT_EDGE) {
4288 BMEdge *e;
4289 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
4291 *r_eed = e;
4292 return;
4293 }
4294 }
4295 }
4296 else if (em->selectmode & SCE_SELECT_FACE) {
4297 BMFace *f = BM_mesh_active_face_get(em->bm, true, false);
4298 if (f && BM_elem_flag_test(f, BM_ELEM_SELECT)) {
4299 *r_efa = f;
4300 return;
4301 }
4302 }
4303}
4304
4305static bool edbm_deselect_nth(BMEditMesh *em, const CheckerIntervalParams *op_params)
4306{
4307 BMVert *v;
4308 BMEdge *e;
4309 BMFace *f;
4310
4311 deselect_nth_active(em, &v, &e, &f);
4312
4313 if (v) {
4314 walker_deselect_nth(em, op_params, &v->head);
4315 return true;
4316 }
4317 if (e) {
4318 walker_deselect_nth(em, op_params, &e->head);
4319 return true;
4320 }
4321 if (f) {
4322 walker_deselect_nth(em, op_params, &f->head);
4323 return true;
4324 }
4325
4326 return false;
4327}
4328
4330{
4331 const Scene *scene = CTX_data_scene(C);
4332 ViewLayer *view_layer = CTX_data_view_layer(C);
4333 CheckerIntervalParams op_params;
4335 bool found_active_elt = false;
4336
4338 scene, view_layer, CTX_wm_view3d(C));
4339
4340 for (Object *obedit : objects) {
4342
4343 if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) {
4344 continue;
4345 }
4346
4347 if (edbm_deselect_nth(em, &op_params) == true) {
4348 found_active_elt = true;
4350 params.calc_looptris = false;
4351 params.calc_normals = false;
4352 params.is_destructive = false;
4353 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
4354 }
4355 }
4356
4357 if (!found_active_elt) {
4358 BKE_report(op->reports, RPT_ERROR, "Mesh object(s) have no active vertex/edge/face");
4359 return OPERATOR_CANCELLED;
4360 }
4361
4362 return OPERATOR_FINISHED;
4363}
4364
4366{
4367 /* identifiers */
4368 ot->name = "Checker Deselect";
4369 ot->idname = "MESH_OT_select_nth";
4370 ot->description = "Deselect every Nth element starting from the active vertex, edge or face";
4371
4372 /* api callbacks */
4375
4376 /* flags */
4378
4380}
4381
4383{
4386
4387 if (vc.obedit) {
4389 }
4390 return vc;
4391}
4392
4395/* -------------------------------------------------------------------- */
4400{
4401 /* Find edges that have exactly two neighboring faces,
4402 * check the angle between those faces, and if angle is
4403 * small enough, select the edge
4404 */
4405 const float angle_limit_cos = cosf(RNA_float_get(op->ptr, "sharpness"));
4406
4407 const Scene *scene = CTX_data_scene(C);
4408 ViewLayer *view_layer = CTX_data_view_layer(C);
4410 scene, view_layer, CTX_wm_view3d(C));
4411
4412 for (Object *obedit : objects) {
4414 BMIter iter;
4415 BMEdge *e;
4416 BMLoop *l1, *l2;
4417
4418 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
4419 if (BM_elem_flag_test(e, BM_ELEM_HIDDEN) == false && BM_edge_loop_pair(e, &l1, &l2)) {
4420 /* edge has exactly two neighboring faces, check angle */
4421 const float angle_cos = dot_v3v3(l1->f->no, l2->f->no);
4422
4423 if (angle_cos < angle_limit_cos) {
4424 BM_edge_select_set(em->bm, e, true);
4425 }
4426 }
4427 }
4428
4429 if ((em->bm->selectmode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) == 0) {
4430 /* Since we can't select individual edges, select faces connected to them. */
4432 }
4433 else {
4435 }
4436 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
4437 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
4438 }
4439
4440 return OPERATOR_FINISHED;
4441}
4442
4444{
4445 PropertyRNA *prop;
4446
4447 /* identifiers */
4448 ot->name = "Select Sharp Edges";
4449 ot->description = "Select all sharp enough edges";
4450 ot->idname = "MESH_OT_edges_select_sharp";
4451
4452 /* api callbacks */
4455
4456 /* flags */
4458
4459 /* props */
4461 "sharpness",
4462 0,
4463 nullptr,
4464 DEG2RADF(0.01f),
4465 DEG2RADF(180.0f),
4466 "Sharpness",
4467 "",
4468 DEG2RADF(1.0f),
4469 DEG2RADF(180.0f));
4471}
4472
4475/* -------------------------------------------------------------------- */
4480{
4481 const Scene *scene = CTX_data_scene(C);
4482 ViewLayer *view_layer = CTX_data_view_layer(C);
4484 scene, view_layer, CTX_wm_view3d(C));
4485 const float angle_limit_cos = cosf(RNA_float_get(op->ptr, "sharpness"));
4486
4487 for (Object *obedit : objects) {
4489 BMesh *bm = em->bm;
4490
4491 if (bm->totfacesel == 0) {
4492 continue;
4493 }
4494
4496
4497 BMIter iter, liter, liter2;
4498 BMFace *f;
4499 BMLoop *l, *l2;
4500
4502
4503 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
4504 if ((BM_elem_flag_test(f, BM_ELEM_HIDDEN) != 0) ||
4506 {
4507 continue;
4508 }
4509
4510 BLI_assert(stack.is_empty());
4511
4512 do {
4513 BM_face_select_set(bm, f, true);
4514
4516
4517 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
4518 BM_ITER_ELEM (l2, &liter2, l, BM_LOOPS_OF_LOOP) {
4519 float angle_cos;
4520
4522 {
4523 continue;
4524 }
4525
4526 angle_cos = dot_v3v3(f->no, l2->f->no);
4527
4528 if (angle_cos > angle_limit_cos) {
4529 stack.append(l2->f);
4530 }
4531 }
4532 }
4533 } while (!stack.is_empty() && (f = stack.pop_last()));
4534 }
4535
4536 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
4537 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
4538 }
4539
4540 return OPERATOR_FINISHED;
4541}
4542
4544{
4545 PropertyRNA *prop;
4546
4547 /* identifiers */
4548 ot->name = "Select Linked Flat Faces";
4549 ot->description = "Select linked faces by angle";
4550 ot->idname = "MESH_OT_faces_select_linked_flat";
4551
4552 /* api callbacks */
4555
4556 /* flags */
4558
4559 /* props */
4561 "sharpness",
4562 0,
4563 nullptr,
4564 DEG2RADF(0.01f),
4565 DEG2RADF(180.0f),
4566 "Sharpness",
4567 "",
4568 DEG2RADF(1.0f),
4569 DEG2RADF(180.0f));
4571}
4572
4575/* -------------------------------------------------------------------- */
4580{
4581 const bool use_extend = RNA_boolean_get(op->ptr, "extend");
4582 const bool use_wire = RNA_boolean_get(op->ptr, "use_wire");
4583 const bool use_boundary = RNA_boolean_get(op->ptr, "use_boundary");
4584 const bool use_multi_face = RNA_boolean_get(op->ptr, "use_multi_face");
4585 const bool use_non_contiguous = RNA_boolean_get(op->ptr, "use_non_contiguous");
4586 const bool use_verts = RNA_boolean_get(op->ptr, "use_verts");
4587
4588 const Scene *scene = CTX_data_scene(C);
4589 ViewLayer *view_layer = CTX_data_view_layer(C);
4591 scene, view_layer, CTX_wm_view3d(C));
4592
4594
4595 for (Object *obedit : objects) {
4597 BMVert *v;
4598 BMEdge *e;
4599 BMIter iter;
4600
4601 if (!use_extend) {
4603 }
4604
4605 /* Selects isolated verts, and edges that do not have 2 neighboring faces. */
4606 if (use_verts) {
4607 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
4609 if (!BM_vert_is_manifold(v)) {
4610 BM_vert_select_set(em->bm, v, true);
4611 }
4612 }
4613 }
4614 }
4615
4616 if (use_wire || use_boundary || use_multi_face || use_non_contiguous) {
4617 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
4619 if ((use_wire && BM_edge_is_wire(e)) || (use_boundary && BM_edge_is_boundary(e)) ||
4620 (use_non_contiguous && (BM_edge_is_manifold(e) && !BM_edge_is_contiguous(e))) ||
4621 (use_multi_face && BM_edge_face_count_is_over(e, 2)))
4622 {
4623 /* check we never select perfect edge (in test above) */
4625
4626 BM_edge_select_set(em->bm, e, true);
4627 }
4628 }
4629 }
4630 }
4631
4632 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
4633 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
4634
4636 }
4637
4638 return OPERATOR_FINISHED;
4639}
4640
4642{
4643 /* identifiers */
4644 ot->name = "Select Non-Manifold";
4645 ot->description = "Select all non-manifold vertices or edges";
4646 ot->idname = "MESH_OT_select_non_manifold";
4647
4648 /* api callbacks */
4651
4652 /* flags */
4654
4655 /* props */
4656 RNA_def_boolean(ot->srna, "extend", true, "Extend", "Extend the selection");
4657 /* edges */
4658 RNA_def_boolean(ot->srna, "use_wire", true, "Wire", "Wire edges");
4659 RNA_def_boolean(ot->srna, "use_boundary", true, "Boundaries", "Boundary edges");
4661 ot->srna, "use_multi_face", true, "Multiple Faces", "Edges shared by more than two faces");
4663 "use_non_contiguous",
4664 true,
4665 "Non Contiguous",
4666 "Edges between faces pointing in alternate directions");
4667 /* verts */
4669 ot->srna, "use_verts", true, "Vertices", "Vertices connecting multiple face regions");
4670}
4671
4674/* -------------------------------------------------------------------- */
4679{
4680 const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT);
4681 const float randfac = RNA_float_get(op->ptr, "ratio");
4683
4684 const Scene *scene = CTX_data_scene(C);
4685 ViewLayer *view_layer = CTX_data_view_layer(C);
4686
4688 scene, view_layer, CTX_wm_view3d(C));
4689 for (const int ob_index : objects.index_range()) {
4690 Object *obedit = objects[ob_index];
4692 BMIter iter;
4693 int seed_iter = seed;
4694
4695 /* This gives a consistent result regardless of object order. */
4696 if (ob_index) {
4697 seed_iter += BLI_ghashutil_strhash_p(obedit->id.name);
4698 }
4699
4700 if (em->selectmode & SCE_SELECT_VERTEX) {
4701 int elem_map_len = 0;
4702 BMVert **elem_map = static_cast<BMVert **>(
4703 MEM_mallocN(sizeof(*elem_map) * em->bm->totvert, __func__));
4704 BMVert *eve;
4705 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
4706 if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
4707 elem_map[elem_map_len++] = eve;
4708 }
4709 }
4710
4711 BLI_array_randomize(elem_map, sizeof(*elem_map), elem_map_len, seed_iter);
4712 const int count_select = elem_map_len * randfac;
4713 for (int i = 0; i < count_select; i++) {
4714 BM_vert_select_set(em->bm, elem_map[i], select);
4715 }
4716 MEM_freeN(elem_map);
4717 }
4718 else if (em->selectmode & SCE_SELECT_EDGE) {
4719 int elem_map_len = 0;
4720 BMEdge **elem_map = static_cast<BMEdge **>(
4721 MEM_mallocN(sizeof(*elem_map) * em->bm->totedge, __func__));
4722 BMEdge *eed;
4723 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
4724 if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) {
4725 elem_map[elem_map_len++] = eed;
4726 }
4727 }
4728 BLI_array_randomize(elem_map, sizeof(*elem_map), elem_map_len, seed_iter);
4729 const int count_select = elem_map_len * randfac;
4730 for (int i = 0; i < count_select; i++) {
4731 BM_edge_select_set(em->bm, elem_map[i], select);
4732 }
4733 MEM_freeN(elem_map);
4734 }
4735 else {
4736 int elem_map_len = 0;
4737 BMFace **elem_map = static_cast<BMFace **>(
4738 MEM_mallocN(sizeof(*elem_map) * em->bm->totface, __func__));
4739 BMFace *efa;
4740 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
4741 if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
4742 elem_map[elem_map_len++] = efa;
4743 }
4744 }
4745 BLI_array_randomize(elem_map, sizeof(*elem_map), elem_map_len, seed_iter);
4746 const int count_select = elem_map_len * randfac;
4747 for (int i = 0; i < count_select; i++) {
4748 BM_face_select_set(em->bm, elem_map[i], select);
4749 }
4750 MEM_freeN(elem_map);
4751 }
4752
4753 if (select) {
4754 /* was EDBM_select_flush, but it over select in edge/face mode */
4756 }
4757 else {
4759 }
4760
4761 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
4763 }
4764
4765 return OPERATOR_FINISHED;
4766}
4767
4769{
4770 /* identifiers */
4771 ot->name = "Select Random";
4772 ot->description = "Randomly select vertices";
4773 ot->idname = "MESH_OT_select_random";
4774
4775 /* api callbacks */
4778
4779 /* flags */
4781
4782 /* props */
4784}
4785
4788/* -------------------------------------------------------------------- */
4793{
4794 if (ED_operator_editmesh(C)) {
4795 Object *obedit = CTX_data_edit_object(C);
4797 const int cd_dvert_offset = CustomData_get_offset(&em->bm->vdata, CD_MDEFORMVERT);
4798
4799 const ListBase *defbase = BKE_object_defgroup_list(obedit);
4800 if ((em->selectmode & SCE_SELECT_VERTEX) == 0) {
4801 CTX_wm_operator_poll_msg_set(C, "Must be in vertex selection mode");
4802 }
4803 else if (BLI_listbase_is_empty(defbase) || cd_dvert_offset == -1) {
4804 CTX_wm_operator_poll_msg_set(C, "No weights/vertex groups on object");
4805 }
4806 else {
4807 return true;
4808 }
4809 }
4810 return false;
4811}
4812
4814{
4815 const bool extend = RNA_boolean_get(op->ptr, "extend");
4816 const Scene *scene = CTX_data_scene(C);
4817 ViewLayer *view_layer = CTX_data_view_layer(C);
4818
4820 scene, view_layer, CTX_wm_view3d(C));
4821
4822 for (Object *obedit : objects) {
4824
4825 const int cd_dvert_offset = CustomData_get_offset(&em->bm->vdata, CD_MDEFORMVERT);
4826
4827 if (cd_dvert_offset == -1) {
4828 continue;
4829 }
4830
4831 BMVert *eve;
4832 BMIter iter;
4833
4834 bool changed = false;
4835
4836 if (!extend) {
4837 if (em->bm->totvertsel) {
4839 changed = true;
4840 }
4841 }
4842
4843 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
4844 if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
4845 MDeformVert *dv = static_cast<MDeformVert *>(BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset));
4846 /* no dv or dv set with no weight */
4847 if (ELEM(nullptr, dv, dv->dw)) {
4848 BM_vert_select_set(em->bm, eve, true);
4849 changed = true;
4850 }
4851 }
4852 }
4853
4854 if (changed) {
4856 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
4857 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
4858 }
4859 }
4860 return OPERATOR_FINISHED;
4861}
4862
4864{
4865 /* identifiers */
4866 ot->name = "Select Ungrouped";
4867 ot->idname = "MESH_OT_select_ungrouped";
4868 ot->description = "Select vertices without a group";
4869
4870 /* api callbacks */
4873
4874 /* flags */
4876
4877 RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
4878}
4879
4882/* -------------------------------------------------------------------- */
4886enum {
4890};
4891
4893{
4894 Scene *scene = CTX_data_scene(C);
4895 ViewLayer *view_layer = CTX_data_view_layer(C);
4896 Object *obedit = CTX_data_edit_object(C);
4898 BMVert *v_act = BM_mesh_active_vert_get(em->bm);
4899 const int orientation = RNA_enum_get(op->ptr, "orientation");
4900 const int axis = RNA_enum_get(op->ptr, "axis");
4901 const int sign = RNA_enum_get(op->ptr, "sign");
4902
4903 if (v_act == nullptr) {
4904 BKE_report(
4905 op->reports, RPT_WARNING, "This operator requires an active vertex (last selected)");
4906 return OPERATOR_CANCELLED;
4907 }
4908
4909 const float limit = RNA_float_get(op->ptr, "threshold");
4910
4911 float value;
4912 float axis_mat[3][3];
4913
4914 /* 3D view variables may be nullptr, (no need to check in poll function). */
4916 view_layer,
4917 CTX_wm_view3d(C),
4919 obedit,
4920 obedit,
4921 orientation,
4923 axis_mat);
4924
4925 const float *axis_vector = axis_mat[axis];
4926
4927 {
4928 float vertex_world[3];
4929 mul_v3_m4v3(vertex_world, obedit->object_to_world().ptr(), v_act->co);
4930 value = dot_v3v3(axis_vector, vertex_world);
4931 }
4932
4933 if (sign == SELECT_AXIS_NEG) {
4934 value += limit;
4935 }
4936 else if (sign == SELECT_AXIS_POS) {
4937 value -= limit;
4938 }
4939
4941 scene, view_layer, CTX_wm_view3d(C));
4942 for (Object *obedit_iter : objects) {
4943 BMEditMesh *em_iter = BKE_editmesh_from_object(obedit_iter);
4944 BMesh *bm = em_iter->bm;
4945
4946 if (bm->totvert == bm->totvertsel) {
4947 continue;
4948 }
4949
4950 BMIter iter;
4951 BMVert *v;
4952 bool changed = false;
4953
4954 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
4956 float v_iter_world[3];
4957 mul_v3_m4v3(v_iter_world, obedit_iter->object_to_world().ptr(), v->co);
4958 const float value_iter = dot_v3v3(axis_vector, v_iter_world);
4959 switch (sign) {
4960 case SELECT_AXIS_ALIGN:
4961 if (fabsf(value_iter - value) < limit) {
4962 BM_vert_select_set(bm, v, true);
4963 changed = true;
4964 }
4965 break;
4966 case SELECT_AXIS_NEG:
4967 if (value_iter < value) {
4968 BM_vert_select_set(bm, v, true);
4969 changed = true;
4970 }
4971 break;
4972 case SELECT_AXIS_POS:
4973 if (value_iter > value) {
4974 BM_vert_select_set(bm, v, true);
4975 changed = true;
4976 }
4977 break;
4978 }
4979 }
4980 }
4981 if (changed) {
4982 EDBM_selectmode_flush(em_iter);
4983 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit_iter->data);
4984 DEG_id_tag_update(static_cast<ID *>(obedit_iter->data), ID_RECALC_SELECT);
4985 }
4986 }
4987 return OPERATOR_FINISHED;
4988}
4989
4991{
4992 static const EnumPropertyItem axis_sign_items[] = {
4993 {SELECT_AXIS_POS, "POS", false, "Positive Axis", ""},
4994 {SELECT_AXIS_NEG, "NEG", false, "Negative Axis", ""},
4995 {SELECT_AXIS_ALIGN, "ALIGN", false, "Aligned Axis", ""},
4996 {0, nullptr, 0, nullptr, nullptr},
4997 };
4998
4999 /* identifiers */
5000 ot->name = "Select Axis";
5001 ot->description = "Select all data in the mesh on a single axis";
5002 ot->idname = "MESH_OT_select_axis";
5003
5004 /* api callbacks */
5007
5008 /* flags */
5010
5011 /* properties */
5013 "orientation",
5016 "Axis Mode",
5017 "Axis orientation");
5018 RNA_def_enum(ot->srna, "sign", axis_sign_items, SELECT_AXIS_POS, "Axis Sign", "Side to select");
5020 "axis",
5022 0,
5023 "Axis",
5024 "Select the axis to compare each vertex on");
5026 ot->srna, "threshold", 0.0001f, 0.000001f, 50.0f, "Threshold", "", 0.00001f, 10.0f);
5027}
5028
5031/* -------------------------------------------------------------------- */
5036{
5037 const Scene *scene = CTX_data_scene(C);
5038 ViewLayer *view_layer = CTX_data_view_layer(C);
5040 scene, view_layer, CTX_wm_view3d(C));
5041 for (Object *obedit : objects) {
5043
5044 if (em->bm->totfacesel == 0) {
5045 continue;
5046 }
5047 BMFace *f;
5048 BMEdge *e;
5049 BMIter iter;
5050
5052
5053 BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
5054 BMLoop *l1, *l2;
5055 BMIter liter1, liter2;
5056
5057 BM_ITER_ELEM (l1, &liter1, f, BM_LOOPS_OF_FACE) {
5058 int tot = 0, totsel = 0;
5059
5060 BM_ITER_ELEM (l2, &liter2, l1->e, BM_LOOPS_OF_EDGE) {
5061 tot++;
5062 totsel += BM_elem_flag_test(l2->f, BM_ELEM_SELECT) != 0;
5063 }
5064
5065 if ((tot != totsel && totsel > 0) || (totsel == 1 && tot == 1)) {
5067 }
5068 }
5069 }
5070
5072
5073 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
5075 BM_edge_select_set(em->bm, e, true);
5076 }
5077 }
5078
5079 /* If in face-only select mode, switch to edge select mode so that
5080 * an edge-only selection is not inconsistent state */
5081 if (em->selectmode == SCE_SELECT_FACE) {
5085 }
5086
5088 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
5089 }
5090
5091 return OPERATOR_FINISHED;
5092}
5093
5095{
5096 /* identifiers */
5097 ot->name = "Select Boundary Loop";
5098 ot->idname = "MESH_OT_region_to_loop";
5099 ot->description = "Select boundary edges around the selected faces";
5100
5101 /* api callbacks */
5104
5105 /* flags */
5107}
5108
5111/* -------------------------------------------------------------------- */
5115static int loop_find_region(BMLoop *l, int flag, GSet *visit_face_set, BMFace ***region_out)
5116{
5119
5120 stack.append(l->f);
5121 BLI_gset_insert(visit_face_set, l->f);
5122
5123 while (!stack.is_empty()) {
5124 BMIter liter1, liter2;
5125 BMLoop *l1, *l2;
5126
5127 BMFace *f = stack.pop_last();
5128 region.append(f);
5129
5130 BM_ITER_ELEM (l1, &liter1, f, BM_LOOPS_OF_FACE) {
5131 if (BM_elem_flag_test(l1->e, flag)) {
5132 continue;
5133 }
5134
5135 BM_ITER_ELEM (l2, &liter2, l1->e, BM_LOOPS_OF_EDGE) {
5136 /* avoids finding same region twice
5137 * (otherwise) the logic works fine without */
5138 if (BM_elem_flag_test(l2->f, BM_ELEM_TAG)) {
5139 continue;
5140 }
5141
5142 if (BLI_gset_add(visit_face_set, l2->f)) {
5143 stack.append(l2->f);
5144 }
5145 }
5146 }
5147 }
5148
5149 BMFace **region_alloc = static_cast<BMFace **>(
5150 MEM_malloc_arrayN(region.size(), sizeof(BMFace *), __func__));
5151 memcpy(region_alloc, region.data(), region.as_span().size_in_bytes());
5152 *region_out = region_alloc;
5153 return region.size();
5154}
5155
5156static int verg_radial(const void *va, const void *vb)
5157{
5158 const BMEdge *e_a = *((const BMEdge **)va);
5159 const BMEdge *e_b = *((const BMEdge **)vb);
5160
5161 const int a = BM_edge_face_count(e_a);
5162 const int b = BM_edge_face_count(e_b);
5163
5164 if (a > b) {
5165 return -1;
5166 }
5167 if (a < b) {
5168 return 1;
5169 }
5170 return 0;
5171}
5172
5179static int loop_find_regions(BMEditMesh *em, const bool selbigger)
5180{
5181 GSet *visit_face_set;
5182 BMIter iter;
5183 const int edges_len = em->bm->totedgesel;
5184 BMEdge *e;
5185 int count = 0, i;
5186
5187 visit_face_set = BLI_gset_ptr_new_ex(__func__, edges_len);
5188 BMEdge **edges = static_cast<BMEdge **>(MEM_mallocN(sizeof(*edges) * edges_len, __func__));
5189
5190 i = 0;
5191 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
5193 edges[i++] = e;
5195 }
5196 else {
5198 }
5199 }
5200
5201 /* sort edges by radial cycle length */
5202 qsort(edges, edges_len, sizeof(*edges), verg_radial);
5203
5204 for (i = 0; i < edges_len; i++) {
5205 BMIter liter;
5206 BMLoop *l;
5207 BMFace **region = nullptr, **region_out;
5208 int c, tot = 0;
5209
5210 e = edges[i];
5211
5213 continue;
5214 }
5215
5216 BM_ITER_ELEM (l, &liter, e, BM_LOOPS_OF_EDGE) {
5217 if (BLI_gset_haskey(visit_face_set, l->f)) {
5218 continue;
5219 }
5220
5221 c = loop_find_region(l, BM_ELEM_SELECT, visit_face_set, &region_out);
5222
5223 if (!region || (selbigger ? c >= tot : c < tot)) {
5224 /* this region is the best seen so far */
5225 tot = c;
5226 if (region) {
5227 /* free the previous best */
5228 MEM_freeN(region);
5229 }
5230 /* track the current region as the new best */
5231 region = region_out;
5232 }
5233 else {
5234 /* this region is not as good as best so far, just free it */
5235 MEM_freeN(region_out);
5236 }
5237 }
5238
5239 if (region) {
5240 int j;
5241
5242 for (j = 0; j < tot; j++) {
5243 BM_elem_flag_enable(region[j], BM_ELEM_TAG);
5244 BM_ITER_ELEM (l, &liter, region[j], BM_LOOPS_OF_FACE) {
5246 }
5247 }
5248
5249 count += tot;
5250
5251 MEM_freeN(region);
5252 }
5253 }
5254
5255 MEM_freeN(edges);
5256 BLI_gset_free(visit_face_set, nullptr);
5257
5258 return count;
5259}
5260
5262{
5263 const bool select_bigger = RNA_boolean_get(op->ptr, "select_bigger");
5264
5265 const Scene *scene = CTX_data_scene(C);
5266 ViewLayer *view_layer = CTX_data_view_layer(C);
5268 scene, view_layer, CTX_wm_view3d(C));
5269 for (Object *obedit : objects) {
5271
5272 if (em->bm->totedgesel == 0) {
5273 continue;
5274 }
5275
5276 BMIter iter;
5277 BMFace *f;
5278
5279 /* find the set of regions with smallest number of total faces */
5281 const int a = loop_find_regions(em, select_bigger);
5282 const int b = loop_find_regions(em, !select_bigger);
5283
5285 loop_find_regions(em, ((a <= b) != select_bigger) ? select_bigger : !select_bigger);
5286
5288
5289 BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
5291 BM_face_select_set(em->bm, f, true);
5292 }
5293 }
5294
5296
5297 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
5298 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
5299 }
5300
5301 return OPERATOR_FINISHED;
5302}
5303
5305{
5306 /* identifiers */
5307 ot->name = "Select Loop Inner-Region";
5308 ot->idname = "MESH_OT_loop_to_region";
5309 ot->description = "Select region of faces inside of a selected loop of edges";
5310
5311 /* api callbacks */
5314
5315 /* flags */
5317
5319 "select_bigger",
5320 false,
5321 "Select Bigger",
5322 "Select bigger regions instead of smaller ones");
5323}
5324
5326{
5327 using namespace blender;
5328 if (!ED_operator_editmesh(C)) {
5329 return false;
5330 }
5331 Object *obedit = CTX_data_edit_object(C);
5332 const Mesh *mesh = static_cast<const Mesh *>(obedit->data);
5333 AttributeOwner owner = AttributeOwner::from_id(&const_cast<ID &>(mesh->id));
5334 const CustomDataLayer *layer = BKE_attributes_active_get(owner);
5335 if (!layer) {
5336 CTX_wm_operator_poll_msg_set(C, "There must be an active attribute");
5337 return false;
5338 }
5339 if (layer->type != CD_PROP_BOOL) {
5340 CTX_wm_operator_poll_msg_set(C, "The active attribute must have a boolean type");
5341 return false;
5342 }
5343 if (BKE_attribute_domain(owner, layer) == bke::AttrDomain::Corner) {
5345 C, "The active attribute must be on the vertex, edge, or face domain");
5346 return false;
5347 }
5348 return true;
5349}
5350
5351static std::optional<BMIterType> domain_to_iter_type(const blender::bke::AttrDomain domain)
5352{
5353 using namespace blender;
5354 switch (domain) {
5355 case bke::AttrDomain::Point:
5356 return BM_VERTS_OF_MESH;
5357 case bke::AttrDomain::Edge:
5358 return BM_EDGES_OF_MESH;
5359 case bke::AttrDomain::Face:
5360 return BM_FACES_OF_MESH;
5361 default:
5362 return std::nullopt;
5363 }
5364}
5365
5367{
5368 using namespace blender;
5369 const Scene *scene = CTX_data_scene(C);
5370 ViewLayer *view_layer = CTX_data_view_layer(C);
5372 scene, view_layer, CTX_wm_view3d(C));
5373 for (Object *obedit : objects) {
5374 Mesh *mesh = static_cast<Mesh *>(obedit->data);
5376 BMesh *bm = em->bm;
5377 AttributeOwner owner = AttributeOwner::from_id(&mesh->id);
5378 const CustomDataLayer *layer = BKE_attributes_active_get(owner);
5379 if (!layer) {
5380 continue;
5381 }
5382 if (layer->type != CD_PROP_BOOL) {
5383 continue;
5384 }
5385 if (BKE_attribute_domain(owner, layer) == bke::AttrDomain::Corner) {
5386 continue;
5387 }
5388 const std::optional<BMIterType> iter_type = domain_to_iter_type(
5389 BKE_attribute_domain(owner, layer));
5390 if (!iter_type) {
5391 continue;
5392 }
5393
5394 bool changed = false;
5395 BMElem *elem;
5396 BMIter iter;
5397 BM_ITER_MESH (elem, &iter, bm, *iter_type) {
5399 continue;
5400 }
5401 if (BM_ELEM_CD_GET_BOOL(elem, layer->offset)) {
5402 BM_elem_select_set(bm, elem, true);
5403 changed = true;
5404 }
5405 }
5406
5407 if (changed) {
5409
5410 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
5411 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
5412 }
5413 }
5414
5415 return OPERATOR_FINISHED;
5416}
5417
5419{
5420 ot->name = "Select by Attribute";
5421 ot->idname = "MESH_OT_select_by_attribute";
5422 ot->description = "Select elements based on the active boolean attribute";
5423
5426
5428}
5429
blender::bke::AttrDomain BKE_attribute_domain(const AttributeOwner &owner, const struct CustomDataLayer *layer)
struct CustomDataLayer * BKE_attributes_active_get(AttributeOwner &owner)
Definition attribute.cc:781
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:579
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:63
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)
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:125
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
struct GSet GSet
Definition BLI_ghash.h:341
bool BLI_gset_haskey(const GSet *gs, const void *key) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:1004
unsigned int BLI_ghashutil_strhash_p(const void *ptr)
void BLI_gset_insert(GSet *gs, void *key)
Definition BLI_ghash.c:959
void BLI_gset_free(GSet *gs, GSetKeyFreeFP keyfreefp)
Definition BLI_ghash.c:1034
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.c:966
A min-heap / priority queue ADT.
HeapNode * BLI_heap_top(const Heap *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition BLI_heap.c:279
void BLI_heap_free(Heap *heap, HeapFreeFP ptrfreefp) ATTR_NONNULL(1)
Definition BLI_heap.c:192
void void float BLI_heap_node_value(const HeapNode *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition BLI_heap.c:347
Heap * BLI_heap_new_ex(unsigned int reserve_num) ATTR_WARN_UNUSED_RESULT
Definition BLI_heap.c:172
void void bool BLI_heap_is_empty(const Heap *heap) ATTR_NONNULL(1)
Definition BLI_heap.c: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.c:291
void * BLI_heap_node_ptr(const HeapNode *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition BLI_heap.c:352
HeapNode * BLI_heap_insert(Heap *heap, float value, void *ptr) ATTR_NONNULL(1)
Definition BLI_heap.c:235
void void BLI_heap_remove(Heap *heap, HeapNode *node) ATTR_NONNULL(1
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
#define LISTBASE_FOREACH(type, var, list)
void BLI_freelinkN(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:269
void void void BLI_movelisttolist(struct ListBase *dst, struct ListBase *src) ATTR_NONNULL(1
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
void * BLI_pophead(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:251
MINLINE float min_ff(float a, float b)
MINLINE unsigned short highest_order_bit_s(unsigned short n)
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:611
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:595
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])
#define DEG2RADF(_deg)
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)
Definition math_vector.c:21
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)
Definition math_vector.c:36
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:188
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)
Object * DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object)
@ ID_RECALC_SELECT
Definition DNA_ID.h:1068
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1085
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
@ CD_MDEFORMVERT
@ CD_PROP_FLOAT2
@ ME_EDIT_MIRROR_TOPO
Object is a sort of wrapper for general info.
@ OB_MESH
@ SCE_SELECT_FACE
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
@ UV_SYNC_SELECTION
@ RV3D_CLIPPING
@ V3D_AROUND_ACTIVE
#define RV3D_CLIPPING_ENABLED(v3d, rv3d)
@ V3D_ORIENT_LOCAL
@ 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)
void EDBM_selectmode_to_scene(bContext *C)
bool ED_operator_editmesh_region_view3d(bContext *C)
bool ED_operator_editmesh(bContext *C)
@ SEL_SELECT
@ SEL_INVERT
@ SEL_DESELECT
@ SEL_TOGGLE
@ SEL_OP_ADD
@ SEL_OP_SUB
@ SEL_OP_SET
@ SEL_OP_AND
@ SEL_OP_XOR
short ED_transform_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])
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:302
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:265
@ V3D_PROJ_TEST_CLIP_NEAR
Definition ED_view3d.hh:269
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:243
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 view3d_operator_needs_opengl(const bContext *C)
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:296
#define XRAY_FLAG_ENABLED(v3d)
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:125
Read Guarded memory(de)allocation.
PropertyFlag
Definition RNA_types.hh:201
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
@ PROP_HIDDEN
Definition RNA_types.hh:239
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DATA
Definition WM_types.hh:475
#define NC_SCENE
Definition WM_types.hh:345
#define ND_TOOLSETTINGS
Definition WM_types.hh:416
#define NC_MATERIAL
Definition WM_types.hh:347
#define ND_SELECT
Definition WM_types.hh:474
@ KM_CTRL
Definition WM_types.hh:256
@ KM_SHIFT
Definition WM_types.hh:255
#define ND_SHADING_LINKS
Definition WM_types.hh:446
#define BM_ELEM_CD_GET_BOOL(ele, offset)
@ BM_LOOP
@ BM_ELEM_HIDDEN
@ BM_ELEM_SEAM
@ BM_ELEM_SELECT
@ BM_ELEM_SMOOTH
@ BM_ELEM_TAG
#define BM_FACE_FIRST_LOOP(p)
#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_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
ATTR_WARN_UNUSED_RESULT 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:81
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_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 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)
Init 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
#define BMW_NIL_LAY
@ BMW_FLAG_NOP
@ BMW_FLAG_TEST_HIDDEN
#define BMW_MASK_NOP
@ 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_ITER(ele, walker, data)
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:253
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
constexpr bool is_empty() const
Definition BLI_span.hh:261
void append(const T &value)
bool is_empty() const
local_group_size(16, 16) .push_constant(Type b
#define printf
const Depsgraph * depsgraph
#define cosf(x)
#define fabsf(x)
static int edbm_select_all_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_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 int edbm_select_random_exec(bContext *C, wmOperator *op)
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 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)
BMEdge * EDBM_edge_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
static int edbm_select_linked_flat_faces_exec(bContext *C, wmOperator *op)
BMVert * EDBM_vert_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
void MESH_OT_loop_to_region(wmOperatorType *ot)
void MESH_OT_select_mode(wmOperatorType *ot)
void EDBM_select_toggle_all(BMEditMesh *em)
static int edbm_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
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 int edbm_select_mode_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 int edbm_select_linked_pick_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 int edbm_select_ungrouped_exec(bContext *C, wmOperator *op)
static bool edbm_select_by_attribute_poll(bContext *C)
static int edbm_select_more_exec(bContext *C, wmOperator *op)
static int loop_find_region(BMLoop *l, int flag, GSet *visit_face_set, BMFace ***region_out)
void EDBM_select_swap(BMEditMesh *em)
static int edbm_loop_to_region_exec(bContext *C, wmOperator *op)
static void mouse_mesh_loop_edge_ring(BMEditMesh *em, BMEdge *eed, bool select, bool select_clear)
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)
void EDBM_selectmode_set(BMEditMesh *em)
static bool mouse_mesh_loop(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle, bool ring)
static BMElem * edbm_select_id_bm_elem_get(const Span< Base * > bases, const uint sel_id, uint *r_base_index)
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)
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 int edbm_select_mirror_exec(bContext *C, wmOperator *op)
static int edbm_select_loose_exec(bContext *C, wmOperator *op)
void MESH_OT_loop_multi_select(wmOperatorType *ot)
ViewContext em_setup_viewcontext(bContext *C)
#define BMO_ELE_TAG
static int edbm_select_loop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void EDBM_selectmode_convert(BMEditMesh *em, const short selectmode_old, const short selectmode_new)
static int edbm_select_by_attribute_exec(bContext *C, wmOperator *)
static bool bm_interior_loop_filter_fn(const BMLoop *l, void *)
static void select_linked_delimit_validate(BMesh *bm, int *delimit)
static int edbm_select_less_exec(bContext *C, wmOperator *op)
bool EDBM_selectmode_toggle_multi(bContext *C, const short selectmode_new, const int action, const bool use_extend, const bool use_expand)
static int edbm_select_face_by_sides_exec(bContext *C, wmOperator *op)
bool EDBM_unified_findnearest(ViewContext *vc, const Span< Base * > bases, int *r_base_index, BMVert **r_eve, BMEdge **r_eed, BMFace **r_efa)
static int edbm_select_axis_exec(bContext *C, wmOperator *op)
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_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 void walker_select(BMEditMesh *em, int walkercode, void *start, const bool select)
void MESH_OT_select_mirror(wmOperatorType *ot)
static int edbm_region_to_loop_exec(bContext *C, wmOperator *)
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 int edbm_loop_multiselect_exec(bContext *C, wmOperator *op)
bool EDBM_mesh_deselect_all_multi(bContext *C)
static int edbm_select_non_manifold_exec(bContext *C, wmOperator *op)
static int edbm_select_similar_region_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 void select_linked_delimit_begin(BMesh *bm, int delimit)
static int edbm_select_mode_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static bool bm_interior_edge_is_manifold_except_face_index(BMEdge *e, int face_index, BMLoop *r_l_pair[2])
static int edbm_select_linked_exec(bContext *C, wmOperator *op)
bool EDBM_select_pick(bContext *C, const int mval[2], const SelectPick_Params *params)
static void findnearestvert__doClosest(void *user_data, BMVert *eve, const float screen_co[2], int index)
void MESH_OT_select_all(wmOperatorType *ot)
@ SELECT_AXIS_ALIGN
@ SELECT_AXIS_POS
@ SELECT_AXIS_NEG
void MESH_OT_select_more(wmOperatorType *ot)
static int edbm_select_sharp_edges_exec(bContext *C, wmOperator *op)
#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 int edbm_select_nth_exec(bContext *C, wmOperator *op)
bool EDBM_selectmode_set_multi(bContext *C, const short selectmode)
static int edbm_faces_select_interior_exec(bContext *C, wmOperator *)
void MESH_OT_select_ungrouped(wmOperatorType *ot)
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)
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)
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
#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:44
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
Definition mallocn.cc:45
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
ccl_device_inline float4 select(const int4 mask, const float4 a, const float4 b)
void base_activate(bContext *C, Base *base)
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:37
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:126
const EnumPropertyItem rna_enum_transform_orientation_items[]
Definition rna_scene.cc:592
#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
BMHeader head
BMWOrder order
int totvert
int totfacesel
char elem_index_dirty
CustomData vdata
int totedge
ListBase selected
int totvertsel
short selectmode
int totedgesel
CustomData ldata
int totface
struct Object * object
eCustomDataType cd_loop_type
Definition DNA_ID.h:413
char name[66]
Definition DNA_ID.h:425
void * first
struct MDeformWeight * dw
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:76
ARegion * region
Definition ED_view3d.hh:73
int mval[2]
Definition ED_view3d.hh:78
Scene * scene
Definition ED_view3d.hh:69
BMEditMesh * em
Definition ED_view3d.hh:77
ViewLayer * view_layer
Definition ED_view3d.hh:70
View3D * v3d
Definition ED_view3d.hh:74
Object * obact
Definition ED_view3d.hh:71
Object * obedit
Definition ED_view3d.hh:72
Depsgraph * depsgraph
Definition ED_view3d.hh:68
int mval[2]
Definition WM_types.hh:728
uint8_t modifier
Definition WM_types.hh:739
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
std::string(* get_description)(bContext *C, wmOperatorType *ot, PointerRNA *ptr)
Definition WM_types.hh:1074
const char * idname
Definition WM_types.hh:992
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
PropertyRNA * prop
Definition WM_types.hh:1092
StructRNA * srna
Definition WM_types.hh:1080
struct ReportList * reports
struct PointerRNA * ptr
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:4126
wmOperatorType * ot
Definition wm_files.cc:4125
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:138