Blender V5.0
editmesh_select.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2004 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <algorithm>
10#include <optional>
11
12#include "MEM_guardedalloc.h"
13
14#include "BLI_heap.h"
15#include "BLI_listbase.h"
16#include "BLI_math_bits.h"
17#include "BLI_math_geom.h"
18#include "BLI_math_matrix.h"
19#include "BLI_math_rotation.h"
20#include "BLI_math_vector.h"
21#include "BLI_rand.h"
23#include "BLI_vector.hh"
24
25#include "BKE_attribute.hh"
26#include "BKE_context.hh"
27#include "BKE_customdata.hh"
28#include "BKE_deform.hh"
29#include "BKE_editmesh.hh"
30#include "BKE_layer.hh"
31#include "BKE_mesh.hh"
32#include "BKE_mesh_wrapper.hh"
33#include "BKE_object.hh"
34#include "BKE_report.hh"
35
36#include "WM_api.hh"
37#include "WM_types.hh"
38
39#include "RNA_access.hh"
40#include "RNA_define.hh"
41#include "RNA_enum_types.hh"
42
43#include "ED_mesh.hh"
44#include "ED_object.hh"
45#include "ED_screen.hh"
46#include "ED_select_utils.hh"
47#include "ED_transform.hh"
48#include "ED_uvedit.hh"
49#include "ED_view3d.hh"
50
51#include "BLT_translation.hh"
52
53#include "DNA_mesh_types.h"
54#include "DNA_meshdata_types.h"
55#include "DNA_object_types.h"
56
57#include "bmesh_tools.hh"
58
59#include "DEG_depsgraph.hh"
61
62#include "DRW_select_buffer.hh"
63
64#include "mesh_intern.hh" /* Own include. */
65
67#define BMO_ELE_TAG 1
68
69using blender::float3;
70using blender::Span;
71using blender::Vector;
72
73/* -------------------------------------------------------------------- */
76
78{
79 Object *obedit = CTX_data_edit_object(C);
80 if (obedit && obedit->type == OB_MESH) {
81 const BMEditMesh *em = BKE_editmesh_from_object(obedit);
82 if (em) {
84 return true;
85 }
86 }
87 }
88
89 CTX_wm_operator_poll_msg_set(C, "An edit-mesh with vertex or edge selection mode is required");
90
91 return false;
92}
93
95
96/* -------------------------------------------------------------------- */
99
106
108 {ELEM_COUNT_LESS, "LESS", false, "Less Than", ""},
109 {ELEM_COUNT_EQUAL, "EQUAL", false, "Equal To", ""},
110 {ELEM_COUNT_GREATER, "GREATER", false, "Greater Than", ""},
111 {ELEM_COUNT_NOT_EQUAL, "NOTEQUAL", false, "Not Equal To", ""},
112 {0, nullptr, 0, nullptr, nullptr},
113};
114
115static inline bool is_count_a_match(const eElemCountType type,
116 const int value_test,
117 const int value_reference)
118{
119 switch (type) {
120 case ELEM_COUNT_LESS:
121 return (value_test < value_reference);
122 case ELEM_COUNT_EQUAL:
123 return (value_test == value_reference);
125 return (value_test > value_reference);
127 return (value_test != value_reference);
128 default:
129 BLI_assert_unreachable(); /* Bad value of selection `type`. */
130 return false;
131 }
132}
133
135
136/* -------------------------------------------------------------------- */
139
141 const Mesh *mesh,
142 const int axis,
143 const bool extend,
144 int *r_totmirr,
145 int *r_totfail)
146{
147 BMesh *bm = em->bm;
148 BMIter iter;
149 int totmirr = 0;
150 int totfail = 0;
151 bool use_topology = mesh->editflag & ME_EDIT_MIRROR_TOPO;
152
153 *r_totmirr = *r_totfail = 0;
154
155 /* Flush (select -> tag). */
156 if (bm->selectmode & SCE_SELECT_VERTEX) {
157 BMVert *v;
158 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
160 }
161 }
162 else if (em->selectmode & SCE_SELECT_EDGE) {
163 BMEdge *e;
164 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
166 }
167 }
168 else {
169 BMFace *f;
170 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
172 }
173 }
174
175 EDBM_verts_mirror_cache_begin(em, axis, true, true, false, use_topology);
176
177 if (!extend) {
179 }
180
181 if (bm->selectmode & SCE_SELECT_VERTEX) {
182 BMVert *v;
183 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
185 continue;
186 }
187
189 BMVert *v_mirr = EDBM_verts_mirror_get(em, v);
190 if (v_mirr && !BM_elem_flag_test(v_mirr, BM_ELEM_HIDDEN)) {
191 BM_vert_select_set(bm, v_mirr, true);
192 totmirr++;
193 }
194 else {
195 totfail++;
196 }
197 }
198 }
199 }
200 else if (em->selectmode & SCE_SELECT_EDGE) {
201 BMEdge *e;
202 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
204 continue;
205 }
206
208 BMEdge *e_mirr = EDBM_verts_mirror_get_edge(em, e);
209 if (e_mirr && !BM_elem_flag_test(e_mirr, BM_ELEM_HIDDEN)) {
210 BM_edge_select_set(bm, e_mirr, true);
211 totmirr++;
212 }
213 else {
214 totfail++;
215 }
216 }
217 }
218 }
219 else {
220 BMFace *f;
221 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
223 continue;
224 }
225
227 BMFace *f_mirr = EDBM_verts_mirror_get_face(em, f);
228 if (f_mirr && !BM_elem_flag_test(f_mirr, BM_ELEM_HIDDEN)) {
229 BM_face_select_set(bm, f_mirr, true);
230 totmirr++;
231 }
232 else {
233 totfail++;
234 }
235 }
236 }
237 }
238
240
241 *r_totmirr = totmirr;
242 *r_totfail = totfail;
243}
244
246{
247 BMesh *bm = em->bm;
248 int selectmode = em->selectmode;
249 bool changed = false;
250
251 if (bm->totfacesel == 0) {
252 selectmode &= ~SCE_SELECT_FACE;
253 }
254 if (bm->totedgesel == 0) {
255 selectmode &= ~SCE_SELECT_EDGE;
256 }
257 if (bm->totvertsel == 0) {
258 selectmode &= ~SCE_SELECT_VERTEX;
259 }
260
261 if (selectmode == 0) {
262 return changed;
263 }
264
265 char symmetry_htype = 0;
266 if (selectmode & SCE_SELECT_FACE) {
267 symmetry_htype |= BM_FACE;
268 }
269 if (selectmode & SCE_SELECT_EDGE) {
270 symmetry_htype |= BM_EDGE;
271 }
272 if (selectmode & SCE_SELECT_VERTEX) {
273 symmetry_htype |= BM_VERT;
274 }
275 if (std::optional<EditMeshSymmetryHelper> symmetry_helper =
276 EditMeshSymmetryHelper::create_if_needed(obedit, symmetry_htype))
277 {
278 const char hflag = BM_ELEM_SELECT;
279 BMIter iter;
280
281 if (selectmode & SCE_SELECT_FACE) {
282 blender::Vector<BMFace *> source_faces;
283 source_faces.reserve(bm->totfacesel);
284 BMFace *f;
285 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
286 if (BM_elem_flag_test(f, hflag)) {
287 source_faces.append(f);
288 }
289 }
290 const int totfacesel_prev = bm->totfacesel;
291 for (BMFace *f_orig : source_faces) {
292 symmetry_helper->set_hflag_on_mirror_faces(f_orig, hflag, true);
293 }
294 if (bm->totfacesel != totfacesel_prev) {
295 changed = true;
296 }
297 }
298 if (selectmode & SCE_SELECT_EDGE) {
299 blender::Vector<BMEdge *> source_edges;
300 source_edges.reserve(bm->totedgesel);
301 BMEdge *e;
302 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
303 if (BM_elem_flag_test(e, hflag)) {
304 source_edges.append(e);
305 }
306 }
307 const int totedgesel_prev = bm->totedgesel;
308 for (BMEdge *e_orig : source_edges) {
309 symmetry_helper->set_hflag_on_mirror_edges(e_orig, hflag, true);
310 }
311 if (bm->totedgesel != totedgesel_prev) {
312 changed = true;
313 }
314 }
315 if (selectmode & SCE_SELECT_VERTEX) {
316 blender::Vector<BMVert *> source_verts;
317 source_verts.reserve(bm->totvertsel);
318 BMVert *v;
319 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
320 if (BM_elem_flag_test(v, hflag)) {
321 source_verts.append(v);
322 }
323 }
324 const int totvertsel_prev = bm->totvertsel;
325 for (BMVert *v_orig : source_verts) {
326 symmetry_helper->set_hflag_on_mirror_verts(v_orig, hflag, true);
327 }
328 if (bm->totvertsel != totvertsel_prev) {
329 changed = true;
330 }
331 }
332 if (changed) {
334 }
335 }
336
337 return changed;
338}
339
341
342/* -------------------------------------------------------------------- */
345
347 const uint sel_id,
348 uint &r_base_index)
349{
350 uint elem_id;
351 char elem_type = 0;
352 bool success = DRW_select_buffer_elem_get(sel_id, elem_id, r_base_index, elem_type);
353
354 if (success) {
355 Object *obedit = bases[r_base_index]->object;
357
358 switch (elem_type) {
359 case SCE_SELECT_FACE:
360 return (BMElem *)BM_face_at_index_find_or_table(em->bm, elem_id);
361 case SCE_SELECT_EDGE:
362 return (BMElem *)BM_edge_at_index_find_or_table(em->bm, elem_id);
364 return (BMElem *)BM_vert_at_index_find_or_table(em->bm, elem_id);
365 default:
366 BLI_assert(0);
367 return nullptr;
368 }
369 }
370
371 return nullptr;
372}
373
375
376/* -------------------------------------------------------------------- */
386
387#define FIND_NEAR_SELECT_BIAS 5
388#define FIND_NEAR_CYCLE_THRESHOLD_MIN 3
389
396
406
407static void findnearestvert__doClosest(void *user_data,
408 BMVert *eve,
409 const float screen_co[2],
410 int index)
411{
412 NearestVertUserData *data = static_cast<NearestVertUserData *>(user_data);
413 float dist_test, dist_test_bias;
414
415 dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co);
416
417 if (data->use_select_bias && BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
418 dist_test_bias += FIND_NEAR_SELECT_BIAS;
419 }
420
421 if (dist_test_bias < data->hit.dist_bias) {
422 data->hit.dist_bias = dist_test_bias;
423 data->hit.dist = dist_test;
424 data->hit.index = index;
425 data->hit.vert = eve;
426 }
427
428 if (data->use_cycle) {
429 if ((data->hit_cycle.vert == nullptr) && (index > data->cycle_index_prev) &&
430 (dist_test_bias < FIND_NEAR_CYCLE_THRESHOLD_MIN))
431 {
432 data->hit_cycle.dist_bias = dist_test_bias;
433 data->hit_cycle.dist = dist_test;
434 data->hit_cycle.index = index;
435 data->hit_cycle.vert = eve;
436 }
437 }
438}
439
441 float *dist_px_manhattan_p,
442 const bool use_select_bias,
443 bool use_cycle,
444 const Span<Base *> bases,
445 uint *r_base_index)
446{
447 uint base_index = 0;
448
449 if (!XRAY_FLAG_ENABLED(vc->v3d)) {
450 uint dist_px_manhattan_test = uint(
451 ED_view3d_backbuf_sample_size_clamp(vc->region, *dist_px_manhattan_p));
452 uint index;
453 BMVert *eve;
454
455 /* No after-queue (yet), so we check it now, otherwise the bm_xxxofs indices are bad. */
456 {
458
460 vc->depsgraph, vc->region, vc->v3d, vc->mval, 1, UINT_MAX, &dist_px_manhattan_test);
461
462 if (index) {
463 eve = (BMVert *)edbm_select_id_bm_elem_get(bases, index, base_index);
464 }
465 else {
466 eve = nullptr;
467 }
468 }
469
470 if (eve) {
471 if (dist_px_manhattan_test < *dist_px_manhattan_p) {
472 if (r_base_index) {
473 *r_base_index = base_index;
474 }
475 *dist_px_manhattan_p = dist_px_manhattan_test;
476 return eve;
477 }
478 }
479 return nullptr;
480 }
481
483 const NearestVertUserData_Hit *hit = nullptr;
484 const eV3DProjTest clip_flag = RV3D_CLIPPING_ENABLED(vc->v3d, vc->rv3d) ?
487 BMesh *prev_select_bm = nullptr;
488
489 static struct {
490 int index;
491 const BMVert *elem;
492 const BMesh *bm;
493 } prev_select = {0};
494
495 data.mval_fl[0] = vc->mval[0];
496 data.mval_fl[1] = vc->mval[1];
497 data.use_select_bias = use_select_bias;
498 data.use_cycle = use_cycle;
499
500 for (; base_index < bases.size(); base_index++) {
501 Base *base_iter = bases[base_index];
503 if (use_cycle && prev_select.bm == vc->em->bm &&
504 prev_select.elem == BM_vert_at_index_find_or_table(vc->em->bm, prev_select.index))
505 {
506 data.cycle_index_prev = prev_select.index;
507 /* No need to compare in the rest of the loop. */
508 use_cycle = false;
509 }
510 else {
511 data.cycle_index_prev = 0;
512 }
513
514 data.hit.dist = data.hit_cycle.dist = data.hit.dist_bias = data.hit_cycle.dist_bias =
515 *dist_px_manhattan_p;
516
519
520 hit = (data.use_cycle && data.hit_cycle.vert) ? &data.hit_cycle : &data.hit;
521
522 if (hit->dist < *dist_px_manhattan_p) {
523 if (r_base_index) {
524 *r_base_index = base_index;
525 }
526 *dist_px_manhattan_p = hit->dist;
527 prev_select_bm = vc->em->bm;
528 }
529 }
530
531 if (hit == nullptr) {
532 return nullptr;
533 }
534
535 prev_select.index = hit->index;
536 prev_select.elem = hit->vert;
537 prev_select.bm = prev_select_bm;
538
539 return hit->vert;
540}
541
542BMVert *EDBM_vert_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
543{
546 return EDBM_vert_find_nearest_ex(vc, dist_px_manhattan_p, false, false, {base}, nullptr);
547}
548
551 float mval_fl[2];
552 float dist;
554};
555
556static void find_nearest_edge_center__doZBuf(void *user_data,
557 BMEdge *eed,
558 const float screen_co_a[2],
559 const float screen_co_b[2],
560 int /*index*/)
561{
562 NearestEdgeUserData_ZBuf *data = static_cast<NearestEdgeUserData_ZBuf *>(user_data);
563
564 if (eed == data->edge_test) {
565 float dist_test;
566 float screen_co_mid[2];
567
568 mid_v2_v2v2(screen_co_mid, screen_co_a, screen_co_b);
569 dist_test = len_manhattan_v2v2(data->mval_fl, screen_co_mid);
570
571 data->dist = std::min(dist_test, data->dist);
572 }
573}
574
587
598
599/* NOTE: uses v3d, so needs active 3d window. */
600static void find_nearest_edge__doClosest(void *user_data,
601 BMEdge *eed,
602 const float screen_co_a[2],
603 const float screen_co_b[2],
604 int index)
605{
606 NearestEdgeUserData *data = static_cast<NearestEdgeUserData *>(user_data);
607 float dist_test, dist_test_bias;
608
609 float fac = line_point_factor_v2(data->mval_fl, screen_co_a, screen_co_b);
610 float screen_co[2];
611
612 if (fac <= 0.0f) {
613 fac = 0.0f;
614 copy_v2_v2(screen_co, screen_co_a);
615 }
616 else if (fac >= 1.0f) {
617 fac = 1.0f;
618 copy_v2_v2(screen_co, screen_co_b);
619 }
620 else {
621 interp_v2_v2v2(screen_co, screen_co_a, screen_co_b, fac);
622 }
623
624 dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co);
625
626 if (data->use_select_bias && BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
627 dist_test_bias += FIND_NEAR_SELECT_BIAS;
628 }
629
630 if (data->vc.rv3d->rflag & RV3D_CLIPPING) {
631 float vec[3];
632
633 interp_v3_v3v3(vec, eed->v1->co, eed->v2->co, fac);
634 if (ED_view3d_clipping_test(data->vc.rv3d, vec, true)) {
635 return;
636 }
637 }
638
639 if (dist_test_bias < data->hit.dist_bias) {
640 float screen_co_mid[2];
641
642 data->hit.dist_bias = dist_test_bias;
643 data->hit.dist = dist_test;
644 data->hit.index = index;
645 data->hit.edge = eed;
646
647 mid_v2_v2v2(screen_co_mid, screen_co_a, screen_co_b);
648 data->hit.dist_center_px_manhattan = len_manhattan_v2v2(data->mval_fl, screen_co_mid);
649 }
650
651 if (data->use_cycle) {
652 if ((data->hit_cycle.edge == nullptr) && (index > data->cycle_index_prev) &&
653 (dist_test_bias < FIND_NEAR_CYCLE_THRESHOLD_MIN))
654 {
655 float screen_co_mid[2];
656
657 data->hit_cycle.dist_bias = dist_test_bias;
658 data->hit_cycle.dist = dist_test;
659 data->hit_cycle.index = index;
660 data->hit_cycle.edge = eed;
661
662 mid_v2_v2v2(screen_co_mid, screen_co_a, screen_co_b);
663 data->hit_cycle.dist_center_px_manhattan = len_manhattan_v2v2(data->mval_fl, screen_co_mid);
664 }
665 }
666}
667
669 float *dist_px_manhattan_p,
670 float *r_dist_center_px_manhattan,
671 const bool use_select_bias,
672 bool use_cycle,
673 BMEdge **r_eed_zbuf,
674 const Span<Base *> bases,
675 uint *r_base_index)
676{
677 uint base_index = 0;
678
679 if (!XRAY_FLAG_ENABLED(vc->v3d)) {
680 uint dist_px_manhattan_test = uint(
681 ED_view3d_backbuf_sample_size_clamp(vc->region, *dist_px_manhattan_p));
682 uint index;
683 BMEdge *eed;
684
685 /* No after-queue (yet), so we check it now, otherwise the bm_xxxofs indices are bad. */
686 {
688
690 vc->depsgraph, vc->region, vc->v3d, vc->mval, 1, UINT_MAX, &dist_px_manhattan_test);
691
692 if (index) {
693 eed = (BMEdge *)edbm_select_id_bm_elem_get(bases, index, base_index);
694 }
695 else {
696 eed = nullptr;
697 }
698 }
699
700 if (r_eed_zbuf) {
701 *r_eed_zbuf = eed;
702 }
703
704 /* Exception for faces (verts don't need this). */
705 if (r_dist_center_px_manhattan && eed) {
707
708 data.mval_fl[0] = vc->mval[0];
709 data.mval_fl[1] = vc->mval[1];
710 data.dist = FLT_MAX;
711 data.edge_test = eed;
712
714
717 &data,
719
720 *r_dist_center_px_manhattan = data.dist;
721 }
722 /* End exception. */
723
724 if (eed) {
725 if (dist_px_manhattan_test < *dist_px_manhattan_p) {
726 if (r_base_index) {
727 *r_base_index = base_index;
728 }
729 *dist_px_manhattan_p = dist_px_manhattan_test;
730 return eed;
731 }
732 }
733 return nullptr;
734 }
735
736 NearestEdgeUserData data = {{nullptr}};
737 const NearestEdgeUserData_Hit *hit = nullptr;
738 /* Interpolate along the edge before doing a clipping plane test. */
740 BMesh *prev_select_bm = nullptr;
741
742 static struct {
743 int index;
744 const BMEdge *elem;
745 const BMesh *bm;
746 } prev_select = {0};
747
748 data.vc = *vc;
749 data.mval_fl[0] = vc->mval[0];
750 data.mval_fl[1] = vc->mval[1];
751 data.use_select_bias = use_select_bias;
752 data.use_cycle = use_cycle;
753
754 for (; base_index < bases.size(); base_index++) {
755 Base *base_iter = bases[base_index];
757 if (use_cycle && prev_select.bm == vc->em->bm &&
758 prev_select.elem == BM_edge_at_index_find_or_table(vc->em->bm, prev_select.index))
759 {
760 data.cycle_index_prev = prev_select.index;
761 /* No need to compare in the rest of the loop. */
762 use_cycle = false;
763 }
764 else {
765 data.cycle_index_prev = 0;
766 }
767
768 data.hit.dist = data.hit_cycle.dist = data.hit.dist_bias = data.hit_cycle.dist_bias =
769 *dist_px_manhattan_p;
770
774
775 hit = (data.use_cycle && data.hit_cycle.edge) ? &data.hit_cycle : &data.hit;
776
777 if (hit->dist < *dist_px_manhattan_p) {
778 if (r_base_index) {
779 *r_base_index = base_index;
780 }
781 *dist_px_manhattan_p = hit->dist;
782 prev_select_bm = vc->em->bm;
783 }
784 }
785
786 if (hit == nullptr) {
787 return nullptr;
788 }
789
790 if (r_dist_center_px_manhattan) {
791 *r_dist_center_px_manhattan = hit->dist_center_px_manhattan;
792 }
793
794 prev_select.index = hit->index;
795 prev_select.elem = hit->edge;
796 prev_select.bm = prev_select_bm;
797
798 return hit->edge;
799}
800
801BMEdge *EDBM_edge_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
802{
806 vc, dist_px_manhattan_p, nullptr, false, false, nullptr, {base}, nullptr);
807}
808
815
816static void find_nearest_face_center__doZBuf(void *user_data,
817 BMFace *efa,
818 const float screen_co[2],
819 int /*index*/)
820{
821 NearestFaceUserData_ZBuf *data = static_cast<NearestFaceUserData_ZBuf *>(user_data);
822
823 if (efa == data->face_test) {
824 const float dist_test = len_manhattan_v2v2(data->mval_fl, screen_co);
825
826 data->dist_px_manhattan = std::min(dist_test, data->dist_px_manhattan);
827 }
828}
829
836
846
847static void findnearestface__doClosest(void *user_data,
848 BMFace *efa,
849 const float screen_co[2],
850 int index)
851{
852 NearestFaceUserData *data = static_cast<NearestFaceUserData *>(user_data);
853 float dist_test, dist_test_bias;
854
855 dist_test = dist_test_bias = len_manhattan_v2v2(data->mval_fl, screen_co);
856
857 if (data->use_select_bias && BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
858 dist_test_bias += FIND_NEAR_SELECT_BIAS;
859 }
860
861 if (dist_test_bias < data->hit.dist_bias) {
862 data->hit.dist_bias = dist_test_bias;
863 data->hit.dist = dist_test;
864 data->hit.index = index;
865 data->hit.face = efa;
866 }
867
868 if (data->use_cycle) {
869 if ((data->hit_cycle.face == nullptr) && (index > data->cycle_index_prev) &&
870 (dist_test_bias < FIND_NEAR_CYCLE_THRESHOLD_MIN))
871 {
872 data->hit_cycle.dist_bias = dist_test_bias;
873 data->hit_cycle.dist = dist_test;
874 data->hit_cycle.index = index;
875 data->hit_cycle.face = efa;
876 }
877 }
878}
879
881 float *dist_px_manhattan_p,
882 float *r_dist_center,
883 const bool use_zbuf_single_px,
884 const bool use_select_bias,
885 bool use_cycle,
886 BMFace **r_efa_zbuf,
887 const Span<Base *> bases,
888 uint *r_base_index)
889{
890 uint base_index = 0;
891
892 if (!XRAY_FLAG_ENABLED(vc->v3d)) {
893 float dist_test;
894 uint index;
895 BMFace *efa;
896
897 {
898 uint dist_px_manhattan_test = 0;
899 if (*dist_px_manhattan_p != 0.0f && (use_zbuf_single_px == false)) {
900 dist_px_manhattan_test = uint(
901 ED_view3d_backbuf_sample_size_clamp(vc->region, *dist_px_manhattan_p));
902 }
903
905
906 if (dist_px_manhattan_test == 0) {
907 index = DRW_select_buffer_sample_point(vc->depsgraph, vc->region, vc->v3d, vc->mval);
908 dist_test = 0.0f;
909 }
910 else {
912 vc->depsgraph, vc->region, vc->v3d, vc->mval, 1, UINT_MAX, &dist_px_manhattan_test);
913 dist_test = dist_px_manhattan_test;
914 }
915
916 if (index) {
917 efa = (BMFace *)edbm_select_id_bm_elem_get(bases, index, base_index);
918 }
919 else {
920 efa = nullptr;
921 }
922 }
923
924 if (r_efa_zbuf) {
925 *r_efa_zbuf = efa;
926 }
927
928 /* Exception for faces (verts don't need this). */
929 if (r_dist_center && efa) {
931
932 data.mval_fl[0] = vc->mval[0];
933 data.mval_fl[1] = vc->mval[1];
934 data.dist_px_manhattan = FLT_MAX;
935 data.face_test = efa;
936
938
941
942 *r_dist_center = data.dist_px_manhattan;
943 }
944 /* End exception. */
945
946 if (efa) {
947 if (dist_test < *dist_px_manhattan_p) {
948 if (r_base_index) {
949 *r_base_index = base_index;
950 }
951 *dist_px_manhattan_p = dist_test;
952 return efa;
953 }
954 }
955 return nullptr;
956 }
957
959 const NearestFaceUserData_Hit *hit = nullptr;
961 BMesh *prev_select_bm = nullptr;
962
963 static struct {
964 int index;
965 const BMFace *elem;
966 const BMesh *bm;
967 } prev_select = {0};
968
969 data.mval_fl[0] = vc->mval[0];
970 data.mval_fl[1] = vc->mval[1];
971 data.use_select_bias = use_select_bias;
972 data.use_cycle = use_cycle;
973
974 for (; base_index < bases.size(); base_index++) {
975 Base *base_iter = bases[base_index];
977 if (use_cycle && prev_select.bm == vc->em->bm &&
978 prev_select.elem == BM_face_at_index_find_or_table(vc->em->bm, prev_select.index))
979 {
980 data.cycle_index_prev = prev_select.index;
981 /* No need to compare in the rest of the loop. */
982 use_cycle = false;
983 }
984 else {
985 data.cycle_index_prev = 0;
986 }
987
988 data.hit.dist = data.hit_cycle.dist = data.hit.dist_bias = data.hit_cycle.dist_bias =
989 *dist_px_manhattan_p;
990
993
994 hit = (data.use_cycle && data.hit_cycle.face) ? &data.hit_cycle : &data.hit;
995
996 if (hit->dist < *dist_px_manhattan_p) {
997 if (r_base_index) {
998 *r_base_index = base_index;
999 }
1000 *dist_px_manhattan_p = hit->dist;
1001 prev_select_bm = vc->em->bm;
1002 }
1003 }
1004
1005 if (hit == nullptr) {
1006 return nullptr;
1007 }
1008
1009 if (r_dist_center) {
1010 *r_dist_center = hit->dist;
1011 }
1012
1013 prev_select.index = hit->index;
1014 prev_select.elem = hit->face;
1015 prev_select.bm = prev_select_bm;
1016
1017 return hit->face;
1018}
1019
1020BMFace *EDBM_face_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
1021{
1025 vc, dist_px_manhattan_p, nullptr, false, false, false, nullptr, {base}, nullptr);
1026}
1027
1028#undef FIND_NEAR_SELECT_BIAS
1029#undef FIND_NEAR_CYCLE_THRESHOLD_MIN
1030
1038 const Span<Base *> bases,
1039 int *r_base_index,
1040 BMVert **r_eve,
1041 BMEdge **r_eed,
1042 BMFace **r_efa)
1043{
1044 BMEditMesh *em = vc->em;
1045
1046 const bool use_cycle = !WM_cursor_test_motion_and_update(vc->mval);
1047 const float dist_init = ED_view3d_select_dist_px();
1048 /* Since edges select lines, we give dots advantage of ~20 pix. */
1049 const float dist_margin = (dist_init / 2);
1050 float dist = dist_init;
1051
1052 struct {
1053 struct {
1054 BMVert *ele;
1055 int base_index;
1056 } v;
1057 struct {
1058 BMEdge *ele;
1059 int base_index;
1060 } e, e_zbuf;
1061 struct {
1062 BMFace *ele;
1063 int base_index;
1064 } f, f_zbuf;
1065 } hit = {{nullptr}};
1066
1067 /* No after-queue (yet), so we check it now, otherwise the em_xxxofs indices are bad. */
1068
1069 if ((dist > 0.0f) && (em->selectmode & SCE_SELECT_FACE)) {
1070 float dist_center = 0.0f;
1071 float *dist_center_p = (em->selectmode & (SCE_SELECT_EDGE | SCE_SELECT_VERTEX)) ?
1072 &dist_center :
1073 nullptr;
1074
1075 uint base_index = 0;
1076 BMFace *efa_zbuf = nullptr;
1078 vc, &dist, dist_center_p, true, true, use_cycle, &efa_zbuf, bases, &base_index);
1079
1080 if (efa_test && dist_center_p) {
1081 dist = min_ff(dist_margin, dist_center);
1082 }
1083 if (efa_test) {
1084 hit.f.base_index = base_index;
1085 hit.f.ele = efa_test;
1086 }
1087 if (efa_zbuf) {
1088 hit.f_zbuf.base_index = base_index;
1089 hit.f_zbuf.ele = efa_zbuf;
1090 }
1091 }
1092
1093 if ((dist > 0.0f) && (em->selectmode & SCE_SELECT_EDGE)) {
1094 float dist_center = 0.0f;
1095 float *dist_center_p = (em->selectmode & SCE_SELECT_VERTEX) ? &dist_center : nullptr;
1096
1097 uint base_index = 0;
1098 BMEdge *eed_zbuf = nullptr;
1100 vc, &dist, dist_center_p, true, use_cycle, &eed_zbuf, bases, &base_index);
1101
1102 if (eed_test && dist_center_p) {
1103 dist = min_ff(dist_margin, dist_center);
1104 }
1105 if (eed_test) {
1106 hit.e.base_index = base_index;
1107 hit.e.ele = eed_test;
1108 }
1109 if (eed_zbuf) {
1110 hit.e_zbuf.base_index = base_index;
1111 hit.e_zbuf.ele = eed_zbuf;
1112 }
1113 }
1114
1115 if ((dist > 0.0f) && (em->selectmode & SCE_SELECT_VERTEX)) {
1116 uint base_index = 0;
1117 BMVert *eve_test = EDBM_vert_find_nearest_ex(vc, &dist, true, use_cycle, bases, &base_index);
1118
1119 if (eve_test) {
1120 hit.v.base_index = base_index;
1121 hit.v.ele = eve_test;
1122 }
1123 }
1124
1125 /* Return only one of 3 pointers, for front-buffer redraws. */
1126 if (hit.v.ele) {
1127 hit.f.ele = nullptr;
1128 hit.e.ele = nullptr;
1129 }
1130 else if (hit.e.ele) {
1131 hit.f.ele = nullptr;
1132 }
1133
1134 /* There may be a face under the cursor, who's center if too far away
1135 * use this if all else fails, it makes sense to select this. */
1136 if ((hit.v.ele || hit.e.ele || hit.f.ele) == 0) {
1137 if (hit.e_zbuf.ele) {
1138 hit.e.base_index = hit.e_zbuf.base_index;
1139 hit.e.ele = hit.e_zbuf.ele;
1140 }
1141 else if (hit.f_zbuf.ele) {
1142 hit.f.base_index = hit.f_zbuf.base_index;
1143 hit.f.ele = hit.f_zbuf.ele;
1144 }
1145 }
1146
1147 /* Only one element type will be non-null. */
1148 BLI_assert(((hit.v.ele != nullptr) + (hit.e.ele != nullptr) + (hit.f.ele != nullptr)) <= 1);
1149
1150 if (hit.v.ele) {
1151 *r_base_index = hit.v.base_index;
1152 }
1153 if (hit.e.ele) {
1154 *r_base_index = hit.e.base_index;
1155 }
1156 if (hit.f.ele) {
1157 *r_base_index = hit.f.base_index;
1158 }
1159
1160 *r_eve = hit.v.ele;
1161 *r_eed = hit.e.ele;
1162 *r_efa = hit.f.ele;
1163
1164 return (hit.v.ele || hit.e.ele || hit.f.ele);
1165}
1166
1167#undef FAKE_SELECT_MODE_BEGIN
1168#undef FAKE_SELECT_MODE_END
1169
1171 const Span<Base *> bases,
1172 int *r_base_index,
1173 BMVert **r_eve,
1174 BMEdge **r_eed,
1175 BMFace **r_efa)
1176{
1177 return unified_findnearest(vc, bases, r_base_index, r_eve, r_eed, r_efa);
1178}
1179
1181
1182/* -------------------------------------------------------------------- */
1188
1190 const Span<Base *> bases,
1191 bool use_boundary_vertices,
1192 bool use_boundary_edges,
1193 int *r_base_index_vert,
1194 int *r_base_index_edge,
1195 int *r_base_index_face,
1196 BMVert **r_eve,
1197 BMEdge **r_eed,
1198 BMFace **r_efa)
1199{
1200 const float mval_fl[2] = {float(vc->mval[0]), float(vc->mval[1])};
1201 float ray_origin[3], ray_direction[3];
1202
1203 struct {
1204 uint base_index;
1205 BMElem *ele;
1206 } best = {0, nullptr};
1207 /* Currently unused, keep since we may want to pick the best. */
1208 UNUSED_VARS(best);
1209
1210 struct {
1211 uint base_index;
1212 BMElem *ele;
1213 } best_vert = {0, nullptr};
1214
1215 struct {
1216 uint base_index;
1217 BMElem *ele;
1218 } best_edge = {0, nullptr};
1219
1220 struct {
1221 uint base_index;
1222 BMElem *ele;
1223 } best_face = {0, nullptr};
1224
1226 vc->depsgraph, vc->region, vc->v3d, mval_fl, ray_origin, ray_direction, true))
1227 {
1228 float dist_sq_best = FLT_MAX;
1229 float dist_sq_best_vert = FLT_MAX;
1230 float dist_sq_best_edge = FLT_MAX;
1231 float dist_sq_best_face = FLT_MAX;
1232
1233 const bool use_vert = (r_eve != nullptr);
1234 const bool use_edge = (r_eed != nullptr);
1235 const bool use_face = (r_efa != nullptr);
1236
1237 for (const int base_index : bases.index_range()) {
1238 Base *base_iter = bases[base_index];
1239 Object *obedit = base_iter->object;
1240
1242 BMesh *bm = em->bm;
1243 float imat3[3][3];
1244
1246 copy_m3_m4(imat3, obedit->object_to_world().ptr());
1247 invert_m3(imat3);
1248
1249 Span<float3> vert_positions;
1250 {
1251 const Object *obedit_eval = DEG_get_evaluated(vc->depsgraph, obedit);
1252 const Mesh *mesh_eval = BKE_object_get_editmesh_eval_cage(obedit_eval);
1253 if (BKE_mesh_wrapper_vert_len(mesh_eval) == bm->totvert) {
1254 vert_positions = BKE_mesh_wrapper_vert_coords(mesh_eval);
1255 }
1256 }
1257
1258 if (!vert_positions.is_empty()) {
1260 }
1261
1262 if ((use_boundary_vertices || use_boundary_edges) && (use_vert || use_edge)) {
1263 BMEdge *e;
1264 BMIter eiter;
1265 BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
1267 continue;
1268 }
1269
1270 if (BM_edge_is_boundary(e)) {
1271 if (use_vert && use_boundary_vertices) {
1272 for (uint j = 0; j < 2; j++) {
1273 BMVert *v = *((&e->v1) + j);
1274 float point[3];
1275 mul_v3_m4v3(point,
1276 obedit->object_to_world().ptr(),
1277 !vert_positions.is_empty() ? vert_positions[BM_elem_index_get(v)] :
1278 v->co);
1279 const float dist_sq_test = dist_squared_to_ray_v3_normalized(
1280 ray_origin, ray_direction, point);
1281 if (dist_sq_test < dist_sq_best_vert) {
1282 dist_sq_best_vert = dist_sq_test;
1283 best_vert.base_index = base_index;
1284 best_vert.ele = (BMElem *)v;
1285 }
1286 if (dist_sq_test < dist_sq_best) {
1287 dist_sq_best = dist_sq_test;
1288 best.base_index = base_index;
1289 best.ele = (BMElem *)v;
1290 }
1291 }
1292 }
1293
1294 if (use_edge && use_boundary_edges) {
1295 float point[3];
1296#if 0
1297 const float dist_sq_test = dist_squared_ray_to_seg_v3(
1298 ray_origin, ray_direction, e->v1->co, e->v2->co, point, &depth);
1299#else
1300 if (!vert_positions.is_empty()) {
1301 mid_v3_v3v3(point,
1302 vert_positions[BM_elem_index_get(e->v1)],
1303 vert_positions[BM_elem_index_get(e->v2)]);
1304 }
1305 else {
1306 mid_v3_v3v3(point, e->v1->co, e->v2->co);
1307 }
1308 mul_m4_v3(obedit->object_to_world().ptr(), point);
1309 const float dist_sq_test = dist_squared_to_ray_v3_normalized(
1310 ray_origin, ray_direction, point);
1311 if (dist_sq_test < dist_sq_best_edge) {
1312 dist_sq_best_edge = dist_sq_test;
1313 best_edge.base_index = base_index;
1314 best_edge.ele = (BMElem *)e;
1315 }
1316 if (dist_sq_test < dist_sq_best) {
1317 dist_sq_best = dist_sq_test;
1318 best.base_index = base_index;
1319 best.ele = (BMElem *)e;
1320 }
1321#endif
1322 }
1323 }
1324 }
1325 }
1326 /* Non boundary case. */
1327 if (use_vert && !use_boundary_vertices) {
1328 BMVert *v;
1329 BMIter viter;
1330 BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
1332 continue;
1333 }
1334
1335 float point[3];
1336 mul_v3_m4v3(point,
1337 obedit->object_to_world().ptr(),
1338 !vert_positions.is_empty() ? vert_positions[BM_elem_index_get(v)] : v->co);
1339 const float dist_sq_test = dist_squared_to_ray_v3_normalized(
1340 ray_origin, ray_direction, point);
1341 if (dist_sq_test < dist_sq_best_vert) {
1342 dist_sq_best_vert = dist_sq_test;
1343 best_vert.base_index = base_index;
1344 best_vert.ele = (BMElem *)v;
1345 }
1346 if (dist_sq_test < dist_sq_best) {
1347 dist_sq_best = dist_sq_test;
1348 best.base_index = base_index;
1349 best.ele = (BMElem *)v;
1350 }
1351 }
1352 }
1353
1354 if (use_edge && !use_boundary_edges) {
1355 BMEdge *e;
1356 BMIter eiter;
1357 BM_ITER_MESH (e, &eiter, bm, BM_EDGES_OF_MESH) {
1359 continue;
1360 }
1361
1362 float point[3];
1363 if (!vert_positions.is_empty()) {
1364 mid_v3_v3v3(point,
1365 vert_positions[BM_elem_index_get(e->v1)],
1366 vert_positions[BM_elem_index_get(e->v2)]);
1367 }
1368 else {
1369 mid_v3_v3v3(point, e->v1->co, e->v2->co);
1370 }
1371 mul_m4_v3(obedit->object_to_world().ptr(), point);
1372 const float dist_sq_test = dist_squared_to_ray_v3_normalized(
1373 ray_origin, ray_direction, point);
1374 if (dist_sq_test < dist_sq_best_edge) {
1375 dist_sq_best_edge = dist_sq_test;
1376 best_edge.base_index = base_index;
1377 best_edge.ele = (BMElem *)e;
1378 }
1379 if (dist_sq_test < dist_sq_best) {
1380 dist_sq_best = dist_sq_test;
1381 best.base_index = base_index;
1382 best.ele = (BMElem *)e;
1383 }
1384 }
1385 }
1386
1387 if (use_face) {
1388 BMFace *f;
1389 BMIter fiter;
1390 BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
1392 continue;
1393 }
1394
1395 float point[3];
1396 if (!vert_positions.is_empty()) {
1397 BM_face_calc_center_median_vcos(bm, f, point, vert_positions);
1398 }
1399 else {
1401 }
1402 mul_m4_v3(obedit->object_to_world().ptr(), point);
1403 const float dist_sq_test = dist_squared_to_ray_v3_normalized(
1404 ray_origin, ray_direction, point);
1405 if (dist_sq_test < dist_sq_best_face) {
1406 dist_sq_best_face = dist_sq_test;
1407 best_face.base_index = base_index;
1408 best_face.ele = (BMElem *)f;
1409 }
1410 if (dist_sq_test < dist_sq_best) {
1411 dist_sq_best = dist_sq_test;
1412 best.base_index = base_index;
1413 best.ele = (BMElem *)f;
1414 }
1415 }
1416 }
1417 }
1418 }
1419
1420 *r_base_index_vert = best_vert.base_index;
1421 *r_base_index_edge = best_edge.base_index;
1422 *r_base_index_face = best_face.base_index;
1423
1424 if (r_eve) {
1425 *r_eve = nullptr;
1426 }
1427 if (r_eed) {
1428 *r_eed = nullptr;
1429 }
1430 if (r_efa) {
1431 *r_efa = nullptr;
1432 }
1433
1434 if (best_vert.ele) {
1435 *r_eve = (BMVert *)best_vert.ele;
1436 }
1437 if (best_edge.ele) {
1438 *r_eed = (BMEdge *)best_edge.ele;
1439 }
1440 if (best_face.ele) {
1441 *r_efa = (BMFace *)best_face.ele;
1442 }
1443
1444 return (best_vert.ele != nullptr || best_edge.ele != nullptr || best_face.ele != nullptr);
1445}
1446
1448
1449/* -------------------------------------------------------------------- */
1452
1454{
1455 Object *obedit = CTX_data_edit_object(C);
1457 BMesh *bm = em->bm;
1458 bool changed = false;
1459
1460 /* Group variables. */
1461 int (*group_index)[2];
1462 int group_tot;
1463 int i;
1464
1465 if (bm->totfacesel < 2) {
1466 BKE_report(op->reports, RPT_ERROR, "No face regions selected");
1467 return OPERATOR_CANCELLED;
1468 }
1469
1470 int *groups_array = MEM_malloc_arrayN<int>(bm->totfacesel, __func__);
1471 group_tot = BM_mesh_calc_face_groups(
1472 bm, groups_array, &group_index, nullptr, nullptr, nullptr, BM_ELEM_SELECT, BM_VERT);
1473
1475
1476 for (i = 0; i < group_tot; i++) {
1477 ListBase faces_regions;
1478 int tot;
1479
1480 const int fg_sta = group_index[i][0];
1481 const int fg_len = group_index[i][1];
1482 int j;
1483 BMFace **fg = MEM_malloc_arrayN<BMFace *>(fg_len, __func__);
1484
1485 for (j = 0; j < fg_len; j++) {
1486 fg[j] = BM_face_at_index(bm, groups_array[fg_sta + j]);
1487 }
1488
1489 tot = BM_mesh_region_match(bm, fg, fg_len, &faces_regions);
1490
1491 MEM_freeN(fg);
1492
1493 if (tot) {
1494 while (LinkData *link = static_cast<LinkData *>(BLI_pophead(&faces_regions))) {
1495 BMFace **faces = static_cast<BMFace **>(link->data);
1496 while (BMFace *f = *(faces++)) {
1497 BM_face_select_set(bm, f, true);
1498 }
1499 MEM_freeN(link->data);
1500 MEM_freeN(link);
1501
1502 changed = true;
1503 }
1504 }
1505 }
1506
1507 MEM_freeN(groups_array);
1508 MEM_freeN(group_index);
1509
1510 if (changed) {
1511 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
1513 }
1514 else {
1515 BKE_report(op->reports, RPT_WARNING, "No matching face regions found");
1516 }
1517
1518 return OPERATOR_FINISHED;
1519}
1520
1522{
1523 /* Identifiers. */
1524 ot->name = "Select Similar Regions";
1525 ot->idname = "MESH_OT_select_similar_region";
1526 ot->description = "Select similar face regions to the current selection";
1527
1528 /* API callbacks. */
1530 ot->poll = ED_operator_editmesh;
1531
1532 /* Flags. */
1533 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1534}
1535
1537
1538/* -------------------------------------------------------------------- */
1541
1543{
1544 const int type = RNA_enum_get(op->ptr, "type");
1545 const int action = RNA_enum_get(op->ptr, "action");
1546 const bool use_extend = RNA_boolean_get(op->ptr, "use_extend");
1547 const bool use_expand = RNA_boolean_get(op->ptr, "use_expand");
1548
1549 if (EDBM_selectmode_toggle_multi(C, type, action, use_extend, use_expand)) {
1550 return OPERATOR_FINISHED;
1551 }
1552 return OPERATOR_CANCELLED;
1553}
1554
1556{
1557 /* Bypass when in UV non sync-select mode, fall through to keymap that edits. */
1558 if (CTX_wm_space_image(C)) {
1560 if ((ts->uv_flag & UV_FLAG_SELECT_SYNC) == 0) {
1561 return OPERATOR_PASS_THROUGH;
1562 }
1563 /* Bypass when no action is needed. */
1564 if (!RNA_struct_property_is_set(op->ptr, "type")) {
1565 return OPERATOR_CANCELLED;
1566 }
1567 }
1568
1569 /* Detecting these options based on shift/control here is weak, but it's done
1570 * to make this work when clicking buttons or menus. */
1571 if (!RNA_struct_property_is_set(op->ptr, "use_extend")) {
1572 RNA_boolean_set(op->ptr, "use_extend", event->modifier & KM_SHIFT);
1573 }
1574 if (!RNA_struct_property_is_set(op->ptr, "use_expand")) {
1575 RNA_boolean_set(op->ptr, "use_expand", event->modifier & KM_CTRL);
1576 }
1577
1578 return edbm_select_mode_exec(C, op);
1579}
1580
1582 wmOperatorType * /*ot*/,
1583 PointerRNA *ptr)
1584{
1585 const int type = RNA_enum_get(ptr, "type");
1586
1587 /* Because the special behavior for shift and ctrl click depend on user input, they may be
1588 * incorrect if the operator is used from a script or from a special button. So only return the
1589 * specialized descriptions if only the "type" is set, which conveys that the operator is meant
1590 * to be used with the logic in the `invoke` method. */
1591 if (RNA_struct_property_is_set(ptr, "type") && !RNA_struct_property_is_set(ptr, "use_extend") &&
1592 !RNA_struct_property_is_set(ptr, "use_expand") && !RNA_struct_property_is_set(ptr, "action"))
1593 {
1594 switch (type) {
1595 case SCE_SELECT_VERTEX:
1596 return TIP_(
1597 "Vertex select - Shift-Click for multiple modes, Ctrl-Click contracts selection");
1598 case SCE_SELECT_EDGE:
1599 return TIP_(
1600 "Edge select - Shift-Click for multiple modes, "
1601 "Ctrl-Click expands/contracts selection depending on the current mode");
1602 case SCE_SELECT_FACE:
1603 return TIP_("Face select - Shift-Click for multiple modes, Ctrl-Click expands selection");
1604 }
1605 }
1606
1607 return "";
1608}
1609
1611{
1612 PropertyRNA *prop;
1613
1614 static const EnumPropertyItem actions_items[] = {
1615 {0, "DISABLE", false, "Disable", "Disable selected markers"},
1616 {1, "ENABLE", false, "Enable", "Enable selected markers"},
1617 {2, "TOGGLE", false, "Toggle", "Toggle disabled flag for selected markers"},
1618 {0, nullptr, 0, nullptr, nullptr},
1619 };
1620
1621 /* Identifiers. */
1622 ot->name = "Select Mode";
1623 ot->idname = "MESH_OT_select_mode";
1624 ot->description = "Change selection mode";
1625
1626 /* API callbacks. */
1627 ot->invoke = edbm_select_mode_invoke;
1628 ot->exec = edbm_select_mode_exec;
1629 ot->poll = ED_operator_editmesh;
1630 ot->get_description = edbm_select_mode_get_description;
1631
1632 /* Flags. */
1633 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1634
1635 /* Properties. */
1636 /* Hide all, not to show redo panel. */
1637 prop = RNA_def_boolean(ot->srna, "use_extend", false, "Extend", "");
1639 prop = RNA_def_boolean(ot->srna, "use_expand", false, "Expand", "");
1641 ot->prop = prop = RNA_def_enum(ot->srna, "type", rna_enum_mesh_select_mode_items, 0, "Type", "");
1643
1644 prop = RNA_def_enum(
1645 ot->srna, "action", actions_items, 2, "Action", "Selection action to execute");
1647}
1648
1650
1651/* -------------------------------------------------------------------- */
1654
1656 int walkercode,
1657 void *start,
1658 int r_count_by_select[2])
1659{
1660 BMesh *bm = em->bm;
1661 BMElem *ele;
1662 BMWalker walker;
1663
1664 r_count_by_select[0] = r_count_by_select[1] = 0;
1665
1666 BMW_init(&walker,
1667 bm,
1668 walkercode,
1673 BMW_NIL_LAY);
1674
1675 for (ele = static_cast<BMElem *>(BMW_begin(&walker, start)); ele;
1676 ele = static_cast<BMElem *>(BMW_step(&walker)))
1677 {
1678 r_count_by_select[BM_elem_flag_test(ele, BM_ELEM_SELECT) ? 1 : 0] += 1;
1679
1680 /* Early exit when mixed (could be optional if needed. */
1681 if (r_count_by_select[0] && r_count_by_select[1]) {
1682 r_count_by_select[0] = r_count_by_select[1] = -1;
1683 break;
1684 }
1685 }
1686
1687 BMW_end(&walker);
1688}
1689
1690static bool walker_select(BMEditMesh *em, int walkercode, void *start, const bool select)
1691{
1692 BMesh *bm = em->bm;
1693 BMElem *ele;
1694 BMWalker walker;
1695 bool changed = false;
1696
1697 BMW_init(&walker,
1698 bm,
1699 walkercode,
1704 BMW_NIL_LAY);
1705
1706 for (ele = static_cast<BMElem *>(BMW_begin(&walker, start)); ele;
1707 ele = static_cast<BMElem *>(BMW_step(&walker)))
1708 {
1709 if (!select) {
1711 }
1713 changed = true;
1714 }
1715 BMW_end(&walker);
1716 return changed;
1717}
1718
1720{
1721 const bool is_ring = RNA_boolean_get(op->ptr, "ring");
1722 const Scene *scene = CTX_data_scene(C);
1723 ViewLayer *view_layer = CTX_data_view_layer(C);
1725 scene, view_layer, CTX_wm_view3d(C));
1726 for (Object *obedit : objects) {
1728
1729 if (em->bm->totedgesel == 0) {
1730 continue;
1731 }
1732
1733 BMEdge *eed;
1734 int edindex;
1735 BMIter iter;
1736 int totedgesel = 0;
1737
1738 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
1740 totedgesel++;
1741 }
1742 }
1743
1744 BMEdge **edarray = MEM_malloc_arrayN<BMEdge *>(totedgesel, "edge array");
1745 edindex = 0;
1746
1747 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
1749 edarray[edindex] = eed;
1750 edindex++;
1751 }
1752 }
1753
1754 bool changed = false;
1755 if (is_ring) {
1756 for (edindex = 0; edindex < totedgesel; edindex += 1) {
1757 eed = edarray[edindex];
1758 changed |= walker_select(em, BMW_EDGERING, eed, true);
1759 }
1760 if (changed) {
1763 }
1764 }
1765 else {
1766 for (edindex = 0; edindex < totedgesel; edindex += 1) {
1767 eed = edarray[edindex];
1768 bool non_manifold = BM_edge_face_count_is_over(eed, 2);
1769 if (non_manifold) {
1770 changed |= walker_select(em, BMW_EDGELOOP_NONMANIFOLD, eed, true);
1771 }
1772 else {
1773 changed |= walker_select(em, BMW_EDGELOOP, eed, true);
1774 }
1775 }
1776 if (changed) {
1779 }
1780 }
1781 MEM_freeN(edarray);
1782
1783 if (changed) {
1784 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
1785 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
1786 }
1787 }
1788
1789 return OPERATOR_FINISHED;
1790}
1791
1793{
1794 /* Identifiers. */
1795 ot->name = "Multi Select Loops";
1796 ot->idname = "MESH_OT_loop_multi_select";
1797 ot->description = "Select a loop of connected edges by connection type";
1798
1799 /* API callbacks. */
1801 ot->poll = ED_operator_editmesh;
1802
1803 /* Flags. */
1804 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1805
1806 /* Properties. */
1807 RNA_def_boolean(ot->srna, "ring", false, "Ring", "");
1808}
1809
1811
1812/* -------------------------------------------------------------------- */
1815
1816static void mouse_mesh_loop_face(BMEditMesh *em, BMEdge *eed, bool select, bool select_clear)
1817{
1818 if (select_clear) {
1820 }
1821
1823}
1824
1825static void mouse_mesh_loop_edge_ring(BMEditMesh *em, BMEdge *eed, bool select, bool select_clear)
1826{
1827 if (select_clear) {
1829 }
1830
1832}
1833
1835 BMEditMesh *em, BMEdge *eed, bool select, bool select_clear, bool select_cycle)
1836{
1837 bool edge_boundary = false;
1838 bool non_manifold = BM_edge_face_count_is_over(eed, 2);
1839
1840 /* Cycle between BMW_EDGELOOP / BMW_EDGEBOUNDARY. */
1841 if (select_cycle && BM_edge_is_boundary(eed)) {
1842 int count_by_select[2];
1843
1844 /* If the loops selected toggle the boundaries. */
1845 walker_select_count(em, BMW_EDGELOOP, eed, count_by_select);
1846 if (count_by_select[!select] == 0) {
1847 edge_boundary = true;
1848
1849 /* If the boundaries selected, toggle back to the loop. */
1850 walker_select_count(em, BMW_EDGEBOUNDARY, eed, count_by_select);
1851 if (count_by_select[!select] == 0) {
1852 edge_boundary = false;
1853 }
1854 }
1855 }
1856
1857 if (select_clear) {
1859 }
1860
1861 if (edge_boundary) {
1863 }
1864 else if (non_manifold) {
1866 }
1867 else {
1869 }
1870}
1871
1873 bContext *C, const int mval[2], bool extend, bool deselect, bool toggle, bool ring)
1874{
1875 Base *basact = nullptr;
1876 BMVert *eve = nullptr;
1877 BMEdge *eed = nullptr;
1878 BMFace *efa = nullptr;
1879
1880 BMEditMesh *em;
1881 bool select = true;
1882 bool select_clear = false;
1883 bool select_cycle = true;
1884 float mvalf[2];
1885
1887 mvalf[0] = float(vc.mval[0] = mval[0]);
1888 mvalf[1] = float(vc.mval[1] = mval[1]);
1889
1890 BMEditMesh *em_original = vc.em;
1891 const short selectmode = em_original->selectmode;
1892 em_original->selectmode = SCE_SELECT_EDGE;
1893
1895 vc.scene, vc.view_layer, vc.v3d);
1896
1897 {
1898 int base_index = -1;
1899 if (EDBM_unified_findnearest(&vc, bases, &base_index, &eve, &eed, &efa)) {
1900 basact = bases[base_index];
1902 em = vc.em;
1903 }
1904 else {
1905 em = nullptr;
1906 }
1907 }
1908
1909 em_original->selectmode = selectmode;
1910
1911 if (em == nullptr || eed == nullptr) {
1912 return false;
1913 }
1914
1915 if (extend == false && deselect == false && toggle == false) {
1916 select_clear = true;
1917 }
1918
1919 if (extend) {
1920 select = true;
1921 }
1922 else if (deselect) {
1923 select = false;
1924 }
1925 else if (select_clear || (BM_elem_flag_test(eed, BM_ELEM_SELECT) == 0)) {
1926 select = true;
1927 }
1928 else if (toggle) {
1929 select = false;
1930 select_cycle = false;
1931 }
1932
1933 if (select_clear) {
1934 for (Base *base_iter : bases) {
1935 Object *ob_iter = base_iter->object;
1936 BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter);
1937
1938 if (em_iter->bm->totvertsel == 0) {
1939 continue;
1940 }
1941
1942 if (em_iter == em) {
1943 continue;
1944 }
1945
1947 DEG_id_tag_update(static_cast<ID *>(ob_iter->data), ID_RECALC_SELECT);
1948 }
1949 }
1950
1951 if (em->selectmode & SCE_SELECT_FACE) {
1952 mouse_mesh_loop_face(em, eed, select, select_clear);
1953 }
1954 else {
1955 if (ring) {
1956 mouse_mesh_loop_edge_ring(em, eed, select, select_clear);
1957 }
1958 else {
1959 mouse_mesh_loop_edge(em, eed, select, select_clear, select_cycle);
1960 }
1961 }
1962
1965
1966 /* Sets as active, useful for other tools. */
1967 if (select) {
1968 if (em->selectmode & SCE_SELECT_VERTEX) {
1969 /* Find nearest vert from mouse
1970 * (initialize to large values in case only one vertex can be projected). */
1971 float v1_co[2], v2_co[2];
1972 float length_1 = FLT_MAX;
1973 float length_2 = FLT_MAX;
1974
1975 /* We can't be sure this has already been set... */
1977
1980 {
1981 length_1 = len_squared_v2v2(mvalf, v1_co);
1982 }
1983
1986 {
1987 length_2 = len_squared_v2v2(mvalf, v2_co);
1988 }
1989#if 0
1990 printf("mouse to v1: %f\nmouse to v2: %f\n",
1991 len_squared_v2v2(mvalf, v1_co),
1992 len_squared_v2v2(mvalf, v2_co));
1993#endif
1994 BM_select_history_store(em->bm, (length_1 < length_2) ? eed->v1 : eed->v2);
1995 }
1996 else if (em->selectmode & SCE_SELECT_EDGE) {
1997 BM_select_history_store(em->bm, eed);
1998 }
1999 else if (em->selectmode & SCE_SELECT_FACE) {
2000 /* Select the face of eed which is the nearest of mouse. */
2001 BMFace *f;
2002 BMIter iterf;
2003 float best_dist = FLT_MAX;
2004 efa = nullptr;
2005
2006 /* We can't be sure this has already been set... */
2008
2009 BM_ITER_ELEM (f, &iterf, eed, BM_FACES_OF_EDGE) {
2011 float cent[3];
2012 float co[2], tdist;
2013
2017 {
2018 tdist = len_squared_v2v2(mvalf, co);
2019 if (tdist < best_dist) {
2020 // printf("Best face: %p (%f)\n", f, tdist);
2021 best_dist = tdist;
2022 efa = f;
2023 }
2024 }
2025 }
2026 }
2027 if (efa) {
2028 BM_mesh_active_face_set(em->bm, efa);
2029 BM_select_history_store(em->bm, efa);
2030 }
2031 }
2032 }
2033
2034 DEG_id_tag_update(static_cast<ID *>(vc.obedit->data), ID_RECALC_SELECT);
2036
2037 return true;
2038}
2039
2041{
2042
2044
2045 if (mouse_mesh_loop(C,
2046 event->mval,
2047 RNA_boolean_get(op->ptr, "extend"),
2048 RNA_boolean_get(op->ptr, "deselect"),
2049 RNA_boolean_get(op->ptr, "toggle"),
2050 RNA_boolean_get(op->ptr, "ring")))
2051 {
2052 return OPERATOR_FINISHED;
2053 }
2054 return OPERATOR_CANCELLED;
2055}
2056
2058{
2059 /* Identifiers. */
2060 ot->name = "Loop Select";
2061 ot->idname = "MESH_OT_loop_select";
2062 ot->description = "Select a loop of connected edges";
2063
2064 /* API callbacks. */
2065 ot->invoke = edbm_select_loop_invoke;
2067
2068 /* Flags. */
2069 ot->flag = OPTYPE_UNDO;
2070
2071 /* Properties. */
2072 PropertyRNA *prop;
2073
2074 prop = RNA_def_boolean(ot->srna, "extend", false, "Extend Select", "Extend the selection");
2076 prop = RNA_def_boolean(ot->srna, "deselect", false, "Deselect", "Remove from the selection");
2078 prop = RNA_def_boolean(ot->srna, "toggle", false, "Toggle Select", "Toggle the selection");
2080 prop = RNA_def_boolean(ot->srna, "ring", false, "Select Ring", "Select ring");
2082}
2083
2085{
2086 /* Description. */
2087 ot->name = "Edge Ring Select";
2088 ot->idname = "MESH_OT_edgering_select";
2089 ot->description = "Select an edge ring";
2090
2091 /* Callbacks. */
2092 ot->invoke = edbm_select_loop_invoke;
2094
2095 /* Flags. */
2096 ot->flag = OPTYPE_UNDO;
2097
2098 /* Properties. */
2099 PropertyRNA *prop;
2100 prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
2102 prop = RNA_def_boolean(ot->srna, "deselect", false, "Deselect", "Remove from the selection");
2104 prop = RNA_def_boolean(ot->srna, "toggle", false, "Toggle Select", "Toggle the selection");
2106 prop = RNA_def_boolean(ot->srna, "ring", true, "Select Ring", "Select ring");
2108}
2109
2111
2112/* -------------------------------------------------------------------- */
2115
2117{
2118 const Scene *scene = CTX_data_scene(C);
2119 ViewLayer *view_layer = CTX_data_view_layer(C);
2120 int action = RNA_enum_get(op->ptr, "action");
2121
2123 scene, view_layer, CTX_wm_view3d(C));
2124
2125 if (action == SEL_TOGGLE) {
2126 action = SEL_SELECT;
2127 for (Object *obedit : objects) {
2129 if (em->bm->totvertsel || em->bm->totedgesel || em->bm->totfacesel) {
2130 action = SEL_DESELECT;
2131 break;
2132 }
2133 }
2134 }
2135
2136 for (Object *obedit : objects) {
2138 switch (action) {
2139 case SEL_SELECT:
2141 break;
2142 case SEL_DESELECT:
2144 break;
2145 case SEL_INVERT:
2146 if (em->bm->uv_select_sync_valid) {
2147 ED_uvedit_deselect_all(scene, obedit, SEL_INVERT);
2148 }
2149 else {
2150 EDBM_select_swap(em);
2152 }
2153 break;
2154 }
2155
2156 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
2157 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2158 }
2159
2160 return OPERATOR_FINISHED;
2161}
2162
2164{
2165 /* Identifiers. */
2166 ot->name = "(De)select All";
2167 ot->idname = "MESH_OT_select_all";
2168 ot->description = "(De)select all vertices, edges or faces";
2169
2170 /* API callbacks. */
2171 ot->exec = edbm_select_all_exec;
2172 ot->poll = ED_operator_editmesh;
2173
2174 /* Flags. */
2175 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2176
2178}
2179
2181
2182/* -------------------------------------------------------------------- */
2185
2187{
2188 const Scene *scene = CTX_data_scene(C);
2189 ViewLayer *view_layer = CTX_data_view_layer(C);
2191 scene, view_layer, CTX_wm_view3d(C));
2192
2193 for (Object *obedit : objects) {
2195
2196 if (!EDBM_select_interior_faces(em)) {
2197 continue;
2198 }
2199
2200 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
2201 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2202 }
2203
2204 return OPERATOR_FINISHED;
2205}
2206
2208{
2209 /* Identifiers. */
2210 ot->name = "Select Interior Faces";
2211 ot->idname = "MESH_OT_select_interior_faces";
2212 ot->description = "Select faces where all edges have more than 2 face users";
2213
2214 /* API callbacks. */
2216 ot->poll = ED_operator_editmesh;
2217
2218 /* Flags. */
2219 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2220}
2221
2223
2224/* -------------------------------------------------------------------- */
2230
2231bool EDBM_select_pick(bContext *C, const int mval[2], const SelectPick_Params &params)
2232{
2233 int base_index_active = -1;
2234 BMVert *eve = nullptr;
2235 BMEdge *eed = nullptr;
2236 BMFace *efa = nullptr;
2237
2238 /* Setup view context for argument to callbacks. */
2240 vc.mval[0] = mval[0];
2241 vc.mval[1] = mval[1];
2242
2244 vc.scene, vc.view_layer, vc.v3d);
2245
2246 bool changed = false;
2247 bool found = unified_findnearest(&vc, bases, &base_index_active, &eve, &eed, &efa);
2248
2249 if (params.sel_op == SEL_OP_SET) {
2250 BMElem *ele = efa ? (BMElem *)efa : (eed ? (BMElem *)eed : (BMElem *)eve);
2251 if ((found && params.select_passthrough) && BM_elem_flag_test(ele, BM_ELEM_SELECT)) {
2252 found = false;
2253 }
2254 else if (found || params.deselect_all) {
2255 /* Deselect everything. */
2256 for (Base *base_iter : bases) {
2257 Object *ob_iter = base_iter->object;
2259 DEG_id_tag_update(static_cast<ID *>(ob_iter->data), ID_RECALC_SELECT);
2261 }
2262 changed = true;
2263 }
2264 }
2265
2266 if (found) {
2267 Base *basact = bases[base_index_active];
2269 Object *obedit = vc.obedit;
2270 BMEditMesh *em = vc.em;
2271 BMesh *bm = em->bm;
2272
2273 const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_PROP_FLOAT2);
2274 const BMUVSelectPickParams uv_pick_params = {
2275 /*cd_loop_uv_offset*/ cd_loop_uv_offset,
2277 };
2278
2279 if (efa) {
2280 switch (params.sel_op) {
2281 case SEL_OP_ADD: {
2283
2284 /* Work-around: deselect first, so we can guarantee it will
2285 * be active even if it was already selected. */
2287 BM_face_select_set(bm, efa, false);
2289 BM_face_select_set(bm, efa, true);
2290 if (bm->uv_select_sync_valid) {
2291 BM_face_uvselect_set_pick(bm, efa, true, uv_pick_params);
2292 }
2293 break;
2294 }
2295 case SEL_OP_SUB: {
2297 BM_face_select_set(bm, efa, false);
2298 break;
2299 }
2300 case SEL_OP_XOR: {
2302 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
2304 BM_face_select_set(bm, efa, true);
2305 if (bm->uv_select_sync_valid) {
2306 BM_face_uvselect_set_pick(bm, efa, true, uv_pick_params);
2307 }
2308 }
2309 else {
2311 BM_face_select_set(bm, efa, false);
2312 if (bm->uv_select_sync_valid) {
2313 BM_face_uvselect_set_pick(bm, efa, false, uv_pick_params);
2314 }
2315 }
2316 break;
2317 }
2318 case SEL_OP_SET: {
2320 if (!BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
2322 BM_face_select_set(bm, efa, true);
2323 }
2324 /* UV select will have been cleared. */
2325 break;
2326 }
2327 case SEL_OP_AND: {
2328 BLI_assert_unreachable(); /* Doesn't make sense for picking. */
2329 break;
2330 }
2331 }
2332 }
2333 else if (eed) {
2334
2335 switch (params.sel_op) {
2336 case SEL_OP_ADD: {
2337 /* Work-around: deselect first, so we can guarantee it will
2338 * be active even if it was already selected. */
2340 BM_edge_select_set(bm, eed, false);
2342 BM_edge_select_set(bm, eed, true);
2343 if (bm->uv_select_sync_valid) {
2344 BM_edge_uvselect_set_pick(bm, eed, true, uv_pick_params);
2345 }
2346 break;
2347 }
2348 case SEL_OP_SUB: {
2350 BM_edge_select_set(bm, eed, false);
2351 if (bm->uv_select_sync_valid) {
2352 BM_edge_uvselect_set_pick(bm, eed, false, uv_pick_params);
2353 }
2354 break;
2355 }
2356 case SEL_OP_XOR: {
2357 if (!BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
2359 BM_edge_select_set(bm, eed, true);
2360 if (bm->uv_select_sync_valid) {
2361 BM_edge_uvselect_set_pick(bm, eed, true, uv_pick_params);
2362 }
2363 }
2364 else {
2366 BM_edge_select_set(bm, eed, false);
2367 if (bm->uv_select_sync_valid) {
2368 BM_edge_uvselect_set_pick(bm, eed, false, uv_pick_params);
2369 }
2370 }
2371 break;
2372 }
2373 case SEL_OP_SET: {
2374 if (!BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
2376 BM_edge_select_set(bm, eed, true);
2377 }
2378 break;
2379 }
2380 case SEL_OP_AND: {
2381 BLI_assert_unreachable(); /* Doesn't make sense for picking. */
2382 break;
2383 }
2384 }
2385 }
2386 else if (eve) {
2387 switch (params.sel_op) {
2388 case SEL_OP_ADD: {
2389 /* Work-around: deselect first, so we can guarantee it will
2390 * be active even if it was already selected. */
2392 BM_vert_select_set(bm, eve, false);
2394 BM_vert_select_set(bm, eve, true);
2395 if (bm->uv_select_sync_valid) {
2396 BM_vert_uvselect_set_pick(bm, eve, true, uv_pick_params);
2397 }
2398 break;
2399 }
2400 case SEL_OP_SUB: {
2402 BM_vert_select_set(bm, eve, false);
2403 if (bm->uv_select_sync_valid) {
2404 BM_vert_uvselect_set_pick(bm, eve, false, uv_pick_params);
2405 }
2406 break;
2407 }
2408 case SEL_OP_XOR: {
2409 if (!BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
2411 BM_vert_select_set(bm, eve, true);
2412 if (bm->uv_select_sync_valid) {
2413 BM_vert_uvselect_set_pick(bm, eve, true, uv_pick_params);
2414 }
2415 }
2416 else {
2418 BM_vert_select_set(bm, eve, false);
2419 if (bm->uv_select_sync_valid) {
2420 BM_vert_uvselect_set_pick(bm, eve, false, uv_pick_params);
2421 }
2422 }
2423 break;
2424 }
2425 case SEL_OP_SET: {
2426 if (!BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
2428 BM_vert_select_set(bm, eve, true);
2429 }
2430 break;
2431 }
2432 case SEL_OP_AND: {
2433 BLI_assert_unreachable(); /* Doesn't make sense for picking. */
2434 break;
2435 }
2436 }
2437 }
2438
2440
2441 if (efa) {
2443 em->mat_nr = efa->mat_nr;
2444 }
2445
2446 /* Changing active object is handy since it allows us to
2447 * switch UV layers, vgroups for eg. */
2449 if (BKE_view_layer_active_base_get(vc.view_layer) != basact) {
2451 }
2452
2453 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
2455
2456 changed = true;
2457 }
2458
2459 return changed;
2460}
2461
2463
2464/* -------------------------------------------------------------------- */
2467
2469{
2470 BMEditSelection *ese, *nextese;
2471
2472 if (!(em->selectmode & SCE_SELECT_VERTEX)) {
2473 ese = static_cast<BMEditSelection *>(em->bm->selected.first);
2474 while (ese) {
2475 nextese = ese->next;
2476 if (ese->htype == BM_VERT) {
2477 BLI_freelinkN(&(em->bm->selected), ese);
2478 }
2479 ese = nextese;
2480 }
2481 }
2482 if (!(em->selectmode & SCE_SELECT_EDGE)) {
2483 ese = static_cast<BMEditSelection *>(em->bm->selected.first);
2484 while (ese) {
2485 nextese = ese->next;
2486 if (ese->htype == BM_EDGE) {
2487 BLI_freelinkN(&(em->bm->selected), ese);
2488 }
2489 ese = nextese;
2490 }
2491 }
2492 if (!(em->selectmode & SCE_SELECT_FACE)) {
2493 ese = static_cast<BMEditSelection *>(em->bm->selected.first);
2494 while (ese) {
2495 nextese = ese->next;
2496 if (ese->htype == BM_FACE) {
2497 BLI_freelinkN(&(em->bm->selected), ese);
2498 }
2499 ese = nextese;
2500 }
2501 }
2502}
2503
2504void EDBM_selectmode_set(BMEditMesh *em, const short selectmode)
2505{
2506 BMVert *eve;
2507 BMEdge *eed;
2508 BMFace *efa;
2509 BMIter iter;
2510
2511 const short selectmode_prev = em->selectmode;
2512 em->selectmode = selectmode;
2513 em->bm->selectmode = selectmode;
2514
2515 /* Strip stored selection isn't relevant to the new mode. */
2517
2518 if (em->bm->totvertsel == 0 && em->bm->totedgesel == 0 && em->bm->totfacesel == 0) {
2519 return;
2520 }
2521
2522 if (em->selectmode & SCE_SELECT_VERTEX) {
2523 if (em->bm->totvertsel) {
2525 }
2526 }
2527 else if (em->selectmode & SCE_SELECT_EDGE) {
2528 /* Deselect vertices, and select again based on edge select. */
2529 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
2530 BM_vert_select_set(em->bm, eve, false);
2531 }
2532
2533 if (em->bm->totedgesel) {
2534 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
2536 BM_edge_select_set(em->bm, eed, true);
2537 }
2538 }
2539
2540 /* Selects faces based on edge status. */
2542 }
2543 }
2544 else if (em->selectmode & SCE_SELECT_FACE) {
2545 /* Deselect edges, and select again based on face select. */
2546 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
2547 BM_edge_select_set(em->bm, eed, false);
2548 }
2549
2550 if (em->bm->totfacesel) {
2551 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2553 BM_face_select_set(em->bm, efa, true);
2554 }
2555 }
2556 }
2557 }
2558
2559 if (em->bm->uv_select_sync_valid) {
2560 /* NOTE(@ideasman42): this could/should use the "sticky" tool setting.
2561 * Although in practice it's OK to assume "connected" sticky in this case. */
2562 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_PROP_FLOAT2);
2563 BM_mesh_uvselect_mode_flush_update(em->bm, selectmode_prev, selectmode, cd_loop_uv_offset);
2564 }
2565}
2566
2568 const short selectmode_old,
2569 const short selectmode_new)
2570{
2571 /* NOTE: it's important only the selection modes passed in a re used,
2572 * not the meshes current selection mode because this is called when the
2573 * selection mode is being manipulated (see: #EDBM_selectmode_toggle_multi). */
2574
2575 BMesh *bm = em->bm;
2576
2577 BMVert *eve;
2578 BMEdge *eed;
2579 BMFace *efa;
2580 BMIter iter;
2581
2582 /* First tag-to-select, then select.
2583 * This avoids a feedback loop. */
2584
2585 /* Have to find out what the selection-mode was previously. */
2586 if (selectmode_old == SCE_SELECT_VERTEX) {
2587 if (bm->totvertsel == 0) {
2588 /* Pass. */
2589 }
2590 else if (selectmode_new == SCE_SELECT_EDGE) {
2591 /* Flush up (vert -> edge). */
2592
2593 /* Select all edges associated with every selected vert. */
2594 BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
2596 }
2597
2598 BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
2599 if (BM_elem_flag_test(eed, BM_ELEM_TAG)) {
2600 BM_edge_select_set(bm, eed, true);
2601 }
2602 }
2603 }
2604 else if (selectmode_new == SCE_SELECT_FACE) {
2605 /* Flush up (vert -> face). */
2606
2607 /* Select all faces associated with every selected vert. */
2608 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2610 }
2611
2612 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2613 if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
2614 BM_face_select_set(bm, efa, true);
2615 }
2616 }
2617 }
2618 }
2619 else if (selectmode_old == SCE_SELECT_EDGE) {
2620 if (bm->totedgesel == 0) {
2621 /* Pass. */
2622 }
2623 else if (selectmode_new == SCE_SELECT_FACE) {
2624 /* Flush up (edge -> face). */
2625
2626 /* Select all faces associated with every selected edge. */
2627 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2629 }
2630
2631 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2632 if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
2633 BM_face_select_set(bm, efa, true);
2634 }
2635 }
2636 }
2637 else if (selectmode_new == SCE_SELECT_VERTEX) {
2638 /* Flush down (edge -> vert). */
2639
2640 BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
2642 BM_vert_select_set(bm, eve, false);
2643 }
2644 }
2645 /* Deselect edges without both verts selected. */
2647 }
2648 }
2649 else if (selectmode_old == SCE_SELECT_FACE) {
2650 if (bm->totfacesel == 0) {
2651 /* Pass. */
2652 }
2653 else if (selectmode_new == SCE_SELECT_EDGE) {
2654 /* Flush down (face -> edge). */
2655
2656 BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
2658 BM_edge_select_set(bm, eed, false);
2659 }
2660 }
2661 /* Deselect faces without edges selected. */
2663 }
2664 else if (selectmode_new == SCE_SELECT_VERTEX) {
2665 /* Flush down (face -> vert). */
2666
2667 BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
2669 BM_vert_select_set(bm, eve, false);
2670 }
2671 }
2672 /* Deselect faces without verts selected. */
2674 }
2675 }
2676}
2677
2679 const short selectmode_toggle,
2680 const int action,
2681 const bool use_extend,
2682 const bool use_expand)
2683{
2685 Scene *scene = CTX_data_scene(C);
2686 ViewLayer *view_layer = CTX_data_view_layer(C);
2688 bool ret = false;
2689
2690 short selectmode_new;
2691 /* Avoid mixing up the active/iterable edit-mesh by limiting its scope. */
2692 {
2693 Object *obedit = CTX_data_edit_object(C);
2694 BMEditMesh *em = nullptr;
2695
2696 if (obedit && obedit->type == OB_MESH) {
2697 em = BKE_editmesh_from_object(obedit);
2698 }
2699
2700 if (em == nullptr) {
2701 return ret;
2702 }
2703
2704 selectmode_new = em->selectmode;
2705 }
2706 /* Assign before the new value is modified. */
2707 const short selectmode_old = selectmode_new;
2708
2709 bool only_update = false;
2710 switch (action) {
2711 case -1:
2712 /* Already set. */
2713 break;
2714 case 0: /* Disable. */
2715 /* Check we have something to do. */
2716 if ((selectmode_old & selectmode_toggle) == 0) {
2717 only_update = true;
2718 break;
2719 }
2720 selectmode_new &= ~selectmode_toggle;
2721 break;
2722 case 1: /* Enable. */
2723 /* Check we have something to do. */
2724 if ((selectmode_old & selectmode_toggle) != 0) {
2725 only_update = true;
2726 break;
2727 }
2728 selectmode_new |= selectmode_toggle;
2729 break;
2730 case 2: /* Toggle. */
2731 /* Can't disable this flag if its the only one set. */
2732 if (selectmode_old == selectmode_toggle) {
2733 only_update = true;
2734 break;
2735 }
2736 selectmode_new ^= selectmode_toggle;
2737 break;
2738 default:
2739 BLI_assert(0);
2740 break;
2741 }
2742
2744 scene, view_layer, CTX_wm_view3d(C));
2745
2746 if (only_update) {
2747 for (Object *ob_iter : objects) {
2748 BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter);
2749 em_iter->selectmode = selectmode_new;
2750 }
2751
2752 return false;
2753 }
2754
2755 /* WARNING: unfortunately failing to ensure this causes problems in *some* cases.
2756 * Adding UV data has negative performance impacts, but failing to do this means
2757 * switching to the UV editor *might* should strange selection.
2758 * Since we can't know if users will proceed to do UV editing after switching modes,
2759 * ensure the UV data.
2760 *
2761 * Even though the data is added, it's only added if it's needed,
2762 * so selecting all/none or when there are no UV's.
2763 *
2764 * Failing to do this means switching from face to vertex selection modes
2765 * will leave vertices on adjacent islands selected - which seems like a bug. */
2766 bool use_uv_select_ensure = false;
2767
2768 /* Only do this when sync-select is enabled so users can have better
2769 * performance when editing high poly meshes. */
2770 if (ts->uv_flag & UV_FLAG_SELECT_SYNC) {
2771 /* Only when flushing down. */
2772 if ((bitscan_forward_i(selectmode_new) < bitscan_forward_i(selectmode_old))) {
2773 use_uv_select_ensure = true;
2774 }
2775 }
2776
2777 if (use_extend == false || selectmode_new == 0) {
2778 if (use_expand) {
2779 const short selectmode_max = highest_order_bit_s(selectmode_old);
2780 for (Object *ob_iter : objects) {
2781 BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter);
2782 EDBM_selectmode_convert(em_iter, selectmode_max, selectmode_toggle);
2783 /* NOTE: This could be supported, but converting UV's too is reasonably complicated.
2784 * This can be considered a low priority TODO. */
2785 EDBM_uvselect_clear(em_iter);
2786 }
2787 use_uv_select_ensure = false;
2788 }
2789 }
2790
2791 switch (selectmode_toggle) {
2792 case SCE_SELECT_VERTEX:
2793 if (use_extend == false || selectmode_new == 0) {
2794 selectmode_new = SCE_SELECT_VERTEX;
2795 }
2796 ret = true;
2797 break;
2798 case SCE_SELECT_EDGE:
2799 if (use_extend == false || selectmode_new == 0) {
2800 selectmode_new = SCE_SELECT_EDGE;
2801 }
2802 ret = true;
2803 break;
2804 case SCE_SELECT_FACE:
2805 if (use_extend == false || selectmode_new == 0) {
2806 selectmode_new = SCE_SELECT_FACE;
2807 }
2808 ret = true;
2809 break;
2810 default:
2811 BLI_assert(0);
2812 break;
2813 }
2814
2815 if (ret == true) {
2816 BLI_assert(selectmode_new != 0);
2817 for (Object *ob_iter : objects) {
2818 BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter);
2819
2820 if (use_uv_select_ensure) {
2821 if (BM_mesh_select_is_mixed(em_iter->bm)) {
2823 }
2824 else {
2825 EDBM_uvselect_clear(em_iter);
2826 }
2827 }
2828
2829 EDBM_selectmode_set(em_iter, selectmode_new);
2830 DEG_id_tag_update(static_cast<ID *>(ob_iter->data),
2832 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob_iter->data);
2833 }
2834
2835 ts->selectmode = selectmode_new;
2838 }
2839
2840 return ret;
2841}
2842
2843bool EDBM_selectmode_set_multi_ex(Scene *scene, Span<Object *> objects, const short selectmode)
2844{
2845 ToolSettings *ts = scene->toolsettings;
2846 bool changed = false;
2847 bool changed_toolsettings = false;
2848
2849 if (ts->selectmode != selectmode) {
2850 ts->selectmode = selectmode;
2851 changed_toolsettings = true;
2852 }
2853
2854 for (Object *ob_iter : objects) {
2855 BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter);
2856 if (em_iter->selectmode == selectmode) {
2857 continue;
2858 }
2859 EDBM_selectmode_set(em_iter, selectmode);
2860 DEG_id_tag_update(static_cast<ID *>(ob_iter->data), ID_RECALC_SYNC_TO_EVAL | ID_RECALC_SELECT);
2861 WM_main_add_notifier(NC_GEOM | ND_SELECT, ob_iter->data);
2862 changed = true;
2863 }
2864
2865 if (changed_toolsettings) {
2868 }
2869
2870 return changed || changed_toolsettings;
2871}
2872
2873bool EDBM_selectmode_set_multi(bContext *C, const short selectmode)
2874{
2875 BLI_assert(selectmode != 0);
2876 Scene *scene = CTX_data_scene(C);
2877 ViewLayer *view_layer = CTX_data_view_layer(C);
2878 BKE_view_layer_synced_ensure(scene, view_layer);
2879 Object *obact = BKE_view_layer_active_object_get(view_layer);
2880 if (!(obact && (obact->type == OB_MESH) && (obact->mode & OB_MODE_EDIT) &&
2881 (BKE_editmesh_from_object(obact) != nullptr)))
2882 {
2883 return false;
2884 }
2885
2887 scene, view_layer, CTX_wm_view3d(C));
2888
2889 return EDBM_selectmode_set_multi_ex(scene, objects, selectmode);
2890}
2891
2905{
2906 if (objects.size() <= 1) {
2907 return false;
2908 }
2909
2910 bool changed = false;
2911 BMEditMesh *em_active = BKE_editmesh_from_object(objects[0]);
2912 for (Object *obedit : objects) {
2914 if (em_active->selectmode == em->selectmode) {
2915 continue;
2916 }
2917 EDBM_selectmode_set(em, em_active->selectmode);
2918 changed = true;
2919
2920 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SYNC_TO_EVAL | ID_RECALC_SELECT);
2921 WM_main_add_notifier(NC_GEOM | ND_SELECT, obedit->data);
2922 }
2923
2924 return changed;
2925}
2926
2928 BMEditMesh *em,
2929 const short selectmode_disable,
2930 const short selectmode_fallback)
2931{
2932 /* Not essential, but switch out of vertex mode since the
2933 * selected regions won't be nicely isolated after flushing. */
2934 if (em->selectmode & selectmode_disable) {
2935 const short selectmode = (em->selectmode == selectmode_disable) ?
2936 selectmode_fallback :
2937 (em->selectmode & ~selectmode_disable);
2938 scene->toolsettings->selectmode = selectmode;
2939 EDBM_selectmode_set(em, selectmode);
2940
2942
2943 return true;
2944 }
2945 return false;
2946}
2947
2949
2950/* -------------------------------------------------------------------- */
2953
2954bool EDBM_deselect_by_material(BMEditMesh *em, const short index, const bool select)
2955{
2956 BMIter iter;
2957 BMFace *efa;
2958 bool changed = false;
2959
2960 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2962 continue;
2963 }
2964 if (efa->mat_nr == index) {
2965 changed = true;
2966 BM_face_select_set(em->bm, efa, select);
2967 }
2968 }
2969 return changed;
2970}
2971
2972void EDBM_select_toggle_all(BMEditMesh *em) /* Exported for UV. */
2973{
2974 if (em->bm->totvertsel || em->bm->totedgesel || em->bm->totfacesel) {
2976 }
2977 else {
2979 }
2980}
2981
2982void EDBM_select_swap(BMEditMesh *em) /* Exported for UV. */
2983{
2984 BMIter iter;
2985 BMVert *eve;
2986 BMEdge *eed;
2987 BMFace *efa;
2988
2989 if (em->bm->selectmode & SCE_SELECT_VERTEX) {
2990 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
2992 continue;
2993 }
2995 }
2996 }
2997 else if (em->selectmode & SCE_SELECT_EDGE) {
2998 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
3000 continue;
3001 }
3003 }
3004 }
3005 else {
3006 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3008 continue;
3009 }
3011 }
3012 }
3013}
3014
3016{
3017 bool changed_multi = false;
3018 for (Base *base_iter : bases) {
3019 Object *ob_iter = base_iter->object;
3020 BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter);
3021
3022 if (em_iter->bm->totvertsel == 0) {
3023 continue;
3024 }
3025
3027 DEG_id_tag_update(static_cast<ID *>(ob_iter->data), ID_RECALC_SELECT);
3028 changed_multi = true;
3029 }
3030 return changed_multi;
3031}
3032
3041
3043 const Span<Base *> bases,
3044 const short selectmode_disable,
3045 const short selectmode_fallback)
3046{
3047 bool changed_multi = false;
3048 for (Base *base_iter : bases) {
3049 Object *ob_iter = base_iter->object;
3050 BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter);
3051
3052 if (EDBM_selectmode_disable(scene, em_iter, selectmode_disable, selectmode_fallback)) {
3053 changed_multi = true;
3054 }
3055 }
3056 return changed_multi;
3057}
3058
3060 const short selectmode_disable,
3061 const short selectmode_fallback)
3062{
3064 Scene *scene = CTX_data_scene(C);
3067 vc.scene, vc.view_layer, nullptr);
3068 return EDBM_selectmode_disable_multi_ex(scene, bases, selectmode_disable, selectmode_fallback);
3069}
3070
3072
3073/* -------------------------------------------------------------------- */
3084
3090
3091static bool bm_interior_loop_filter_fn(const BMLoop *l, void * /*user_data*/)
3092{
3093 if (BM_elem_flag_test(l->e, BM_ELEM_TAG)) {
3094 return false;
3095 }
3096 return true;
3097}
3099 int face_index,
3100 BMLoop *r_l_pair[2])
3101{
3102
3103 BMLoop *l_iter = e->l;
3104 int loop_index = 0;
3105 do {
3106 BMFace *f = l_iter->f;
3107 int i = BM_elem_index_get(f);
3108 if (!ELEM(i, -1, face_index)) {
3109 if (loop_index == 2) {
3110 return false;
3111 }
3112 r_l_pair[loop_index++] = l_iter;
3113 }
3114 } while ((l_iter = l_iter->radial_next) != e->l);
3115 return (loop_index == 2);
3116}
3117
3122static float bm_interior_face_group_calc_cost(ListBase *ls, const float *edge_lengths)
3123{
3124 /* Dividing by the area is important so larger face groups (which will become the outer shell)
3125 * aren't detected as having a high cost. */
3126 float area = 0.0f;
3127 float cost = 0.0f;
3128 bool found = false;
3129 LISTBASE_FOREACH (BMFaceLink *, f_link, ls) {
3130 BMFace *f = f_link->face;
3131 area += f_link->area;
3132 int i = BM_elem_index_get(f);
3133 BLI_assert(i != -1);
3134 BMLoop *l_iter, *l_first;
3135 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
3136 do {
3137 if (BM_elem_flag_test(l_iter->e, BM_ELEM_TAG)) {
3138 float cost_test = 0.0f;
3139 int cost_count = 0;
3140 /* All other faces. */
3141 BMLoop *l_radial_iter = l_iter;
3142 do {
3143 int i_other = BM_elem_index_get(l_radial_iter->f);
3144 if (!ELEM(i_other, -1, i)) {
3145 float angle = angle_normalized_v3v3(f->no, l_radial_iter->f->no);
3146 /* Ignore face direction since in the case on non-manifold faces connecting edges,
3147 * the face flipping may not be meaningful. */
3148 if (angle > DEG2RADF(90)) {
3149 angle = DEG2RADF(180) - angle;
3150 }
3151 /* Avoid calculating it inline, pass in pre-calculated edge lengths. */
3152#if 0
3153 cost_test += BM_edge_calc_length(l_iter->e) * angle;
3154#else
3155 BLI_assert(edge_lengths[BM_elem_index_get(l_iter->e)] != -1.0f);
3156 cost_test += edge_lengths[BM_elem_index_get(l_iter->e)] * angle;
3157#endif
3158 cost_count += 1;
3159 }
3160 } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter);
3161
3162 if (cost_count >= 2) {
3163 cost += cost_test;
3164 found = true;
3165 }
3166 }
3167 } while ((l_iter = l_iter->next) != l_first);
3168 }
3169 return found ? cost / area : FLT_MAX;
3170}
3171
3173{
3174 BMesh *bm = em->bm;
3175 BMIter iter;
3176 bool changed = false;
3177
3178 float *edge_lengths = MEM_malloc_arrayN<float>(bm->totedge, __func__);
3179
3180 {
3181 bool has_nonmanifold = false;
3182 BMEdge *e;
3183 int i;
3185 const bool is_over = BM_edge_face_count_is_over(e, 2);
3186 if (is_over) {
3188 has_nonmanifold = true;
3189 edge_lengths[i] = BM_edge_calc_length(e);
3190 }
3191 else {
3193 edge_lengths[i] = -1.0;
3194 }
3195
3196 BM_elem_index_set(e, i); /* set_inline */
3197 }
3198 bm->elem_index_dirty &= ~BM_EDGE;
3199
3200 if (has_nonmanifold == false) {
3201 MEM_freeN(edge_lengths);
3202 return false;
3203 }
3204 }
3205
3206 /* Group variables. */
3207 int (*fgroup_index)[2];
3208 int fgroup_len;
3209
3210 int *fgroup_array = MEM_malloc_arrayN<int>(bm->totface, __func__);
3211 fgroup_len = BM_mesh_calc_face_groups(
3212 bm, fgroup_array, &fgroup_index, bm_interior_loop_filter_fn, nullptr, nullptr, 0, BM_EDGE);
3213
3214 int *fgroup_recalc_stack = MEM_malloc_arrayN<int>(fgroup_len, __func__);
3215 STACK_DECLARE(fgroup_recalc_stack);
3216 STACK_INIT(fgroup_recalc_stack, fgroup_len);
3217
3219
3220 {
3221 BMFace *f;
3222 BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
3223 BM_elem_index_set(f, -1); /* set_dirty! */
3224 }
3225 }
3226 bm->elem_index_dirty |= BM_FACE;
3227
3228 ListBase *fgroup_listbase = MEM_calloc_arrayN<ListBase>(fgroup_len, __func__);
3229 BMFaceLink *f_link_array = MEM_calloc_arrayN<BMFaceLink>(bm->totface, __func__);
3230
3231 for (int i = 0; i < fgroup_len; i++) {
3232 const int fg_sta = fgroup_index[i][0];
3233 const int fg_len = fgroup_index[i][1];
3234 for (int j = 0; j < fg_len; j++) {
3235 const int face_index = fgroup_array[fg_sta + j];
3236 BMFace *f = BM_face_at_index(bm, face_index);
3237 BM_elem_index_set(f, i);
3238
3239 BMFaceLink *f_link = &f_link_array[face_index];
3240 f_link->face = f;
3241 f_link->area = BM_face_calc_area(f);
3242 BLI_addtail(&fgroup_listbase[i], f_link);
3243 }
3244 }
3245
3246 MEM_freeN(fgroup_array);
3247 MEM_freeN(fgroup_index);
3248
3249 Heap *fgroup_heap = BLI_heap_new_ex(fgroup_len);
3250 HeapNode **fgroup_table = MEM_malloc_arrayN<HeapNode *>(fgroup_len, __func__);
3251 bool *fgroup_dirty = MEM_calloc_arrayN<bool>(fgroup_len, __func__);
3252
3253 for (int i = 0; i < fgroup_len; i++) {
3254 const float cost = bm_interior_face_group_calc_cost(&fgroup_listbase[i], edge_lengths);
3255 if (cost != FLT_MAX) {
3256 fgroup_table[i] = BLI_heap_insert(fgroup_heap, -cost, POINTER_FROM_INT(i));
3257 }
3258 else {
3259 fgroup_table[i] = nullptr;
3260 }
3261 }
3262
3263 /* Avoid re-running cost calculations for large face-groups which will end up forming the
3264 * outer shell and not be considered interior.
3265 * As these face groups become increasingly bigger - their chance of being considered
3266 * interior reduces as does the time to calculate their cost.
3267 *
3268 * This delays recalculating them until they are considered can dates to remove
3269 * which becomes less and less likely as they increase in area. */
3270
3271#define USE_DELAY_FACE_GROUP_COST_CALC
3272
3273 while (true) {
3274
3275#if defined(USE_DELAY_FACE_GROUP_COST_CALC)
3276 while (!BLI_heap_is_empty(fgroup_heap)) {
3277 HeapNode *node_min = BLI_heap_top(fgroup_heap);
3278 const int i = POINTER_AS_INT(BLI_heap_node_ptr(node_min));
3279 if (fgroup_dirty[i]) {
3280 const float cost = bm_interior_face_group_calc_cost(&fgroup_listbase[i], edge_lengths);
3281 if (cost != FLT_MAX) {
3282 /* The cost may have improves (we may be able to skip this),
3283 * however the cost should _never_ make this a choice. */
3284 BLI_assert(-BLI_heap_node_value(node_min) >= cost);
3285 BLI_heap_node_value_update(fgroup_heap, fgroup_table[i], -cost);
3286 }
3287 else {
3288 BLI_heap_remove(fgroup_heap, fgroup_table[i]);
3289 fgroup_table[i] = nullptr;
3290 }
3291 fgroup_dirty[i] = false;
3292 }
3293 else {
3294 break;
3295 }
3296 }
3297#endif
3298
3299 if (BLI_heap_is_empty(fgroup_heap)) {
3300 break;
3301 }
3302
3303 const int i_min = POINTER_AS_INT(BLI_heap_pop_min(fgroup_heap));
3304 BLI_assert(fgroup_table[i_min] != nullptr);
3305 BLI_assert(fgroup_dirty[i_min] == false);
3306 fgroup_table[i_min] = nullptr;
3307 changed = true;
3308
3309 while (BMFaceLink *f_link = static_cast<BMFaceLink *>(BLI_pophead(&fgroup_listbase[i_min]))) {
3310 BMFace *f = f_link->face;
3311 BM_face_select_set(bm, f, true);
3312 BM_elem_index_set(f, -1); /* set_dirty */
3313
3314 BMLoop *l_iter, *l_first;
3315
3316 /* Loop over edges face edges, merging groups which are no longer separated
3317 * by non-manifold edges (when manifold check ignores faces from this group). */
3318 l_iter = l_first = BM_FACE_FIRST_LOOP(f);
3319 do {
3320 BMLoop *l_pair[2];
3321 if (bm_interior_edge_is_manifold_except_face_index(l_iter->e, i_min, l_pair)) {
3323
3324 int i_a = BM_elem_index_get(l_pair[0]->f);
3325 int i_b = BM_elem_index_get(l_pair[1]->f);
3326 if (i_a != i_b) {
3327 /* Only for predictable results that don't depend on the order of radial loops,
3328 * not essential. */
3329 if (i_a > i_b) {
3330 std::swap(i_a, i_b);
3331 }
3332
3333 /* Merge the groups. */
3334 LISTBASE_FOREACH (LinkData *, n, &fgroup_listbase[i_b]) {
3335 BMFace *f_iter = static_cast<BMFace *>(n->data);
3336 BM_elem_index_set(f_iter, i_a);
3337 }
3338 BLI_movelisttolist(&fgroup_listbase[i_a], &fgroup_listbase[i_b]);
3339
3340 /* This may have been added to 'fgroup_recalc_stack', instead of removing it,
3341 * just check the heap node isn't nullptr before recalculating. */
3342 BLI_heap_remove(fgroup_heap, fgroup_table[i_b]);
3343 fgroup_table[i_b] = nullptr;
3344 /* Keep the dirty flag as-is for 'i_b', because it may be in the 'fgroup_recalc_stack'
3345 * and we don't want to add it again.
3346 * Instead rely on the 'fgroup_table[i_b]' being nullptr as a secondary check. */
3347
3348 if (fgroup_dirty[i_a] == false) {
3349 BLI_assert(fgroup_table[i_a] != nullptr);
3350 STACK_PUSH(fgroup_recalc_stack, i_a);
3351 fgroup_dirty[i_a] = true;
3352 }
3353 }
3354 }
3355
3356 /* Mark all connected groups for re-calculation. */
3357 BMLoop *l_radial_iter = l_iter->radial_next;
3358 if (l_radial_iter != l_iter) {
3359 do {
3360 int i_other = BM_elem_index_get(l_radial_iter->f);
3361 if (!ELEM(i_other, -1, i_min)) {
3362 if ((fgroup_table[i_other] != nullptr) && (fgroup_dirty[i_other] == false)) {
3363#if !defined(USE_DELAY_FACE_GROUP_COST_CALC)
3364 STACK_PUSH(fgroup_recalc_stack, i_other);
3365#endif
3366 fgroup_dirty[i_other] = true;
3367 }
3368 }
3369 } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter);
3370 }
3371
3372 } while ((l_iter = l_iter->next) != l_first);
3373 }
3374
3375 for (int index = 0; index < STACK_SIZE(fgroup_recalc_stack); index++) {
3376 const int i = fgroup_recalc_stack[index];
3377 if (fgroup_table[i] != nullptr && fgroup_dirty[i] == true) {
3378 /* First update edge tags. */
3379 const float cost = bm_interior_face_group_calc_cost(&fgroup_listbase[i], edge_lengths);
3380 if (cost != FLT_MAX) {
3381 BLI_heap_node_value_update(fgroup_heap, fgroup_table[i], -cost);
3382 }
3383 else {
3384 BLI_heap_remove(fgroup_heap, fgroup_table[i]);
3385 fgroup_table[i] = nullptr;
3386 }
3387 }
3388 fgroup_dirty[i] = false;
3389 }
3390 STACK_CLEAR(fgroup_recalc_stack);
3391 }
3392
3393 MEM_freeN(edge_lengths);
3394 MEM_freeN(f_link_array);
3395 MEM_freeN(fgroup_listbase);
3396 MEM_freeN(fgroup_recalc_stack);
3397 MEM_freeN(fgroup_table);
3398 MEM_freeN(fgroup_dirty);
3399
3400 BLI_heap_free(fgroup_heap, nullptr);
3401
3402 return changed;
3403}
3404
3406
3407/* -------------------------------------------------------------------- */
3412
3414#define USE_LINKED_SELECT_DEFAULT_HACK
3415
3420
3421static bool select_linked_delimit_test(BMEdge *e, int delimit, const DelimitData *delimit_data)
3422{
3423 BLI_assert(delimit);
3424
3425 if (delimit & BMO_DELIM_SEAM) {
3427 return true;
3428 }
3429 }
3430
3431 if (delimit & BMO_DELIM_SHARP) {
3432 if (BM_elem_flag_test(e, BM_ELEM_SMOOTH) == 0) {
3433 return true;
3434 }
3435 }
3436
3437 if (delimit & BMO_DELIM_NORMAL) {
3438 if (!BM_edge_is_contiguous(e)) {
3439 return true;
3440 }
3441 }
3442
3443 if (delimit & BMO_DELIM_MATERIAL) {
3444 if (e->l && e->l->radial_next != e->l) {
3445 const short mat_nr = e->l->f->mat_nr;
3446 BMLoop *l_iter = e->l->radial_next;
3447 do {
3448 if (l_iter->f->mat_nr != mat_nr) {
3449 return true;
3450 }
3451 } while ((l_iter = l_iter->radial_next) != e->l);
3452 }
3453 }
3454
3455 if (delimit & BMO_DELIM_UV) {
3457 e, delimit_data->cd_loop_type, delimit_data->cd_loop_offset) == 0)
3458 {
3459 return true;
3460 }
3461 }
3462
3463 return false;
3464}
3465
3466#ifdef USE_LINKED_SELECT_DEFAULT_HACK
3471static int select_linked_delimit_default_from_op(wmOperator *op, const int select_mode)
3472{
3473 static char delimit_last_store[2] = {0, BMO_DELIM_SEAM};
3474 int delimit_last_index = (select_mode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) == 0;
3475 char *delimit_last = &delimit_last_store[delimit_last_index];
3476 PropertyRNA *prop_delimit = RNA_struct_find_property(op->ptr, "delimit");
3477 int delimit;
3478
3479 if (RNA_property_is_set(op->ptr, prop_delimit)) {
3480 delimit = RNA_property_enum_get(op->ptr, prop_delimit);
3481 *delimit_last = delimit;
3482 }
3483 else {
3484 delimit = *delimit_last;
3485 RNA_property_enum_set(op->ptr, prop_delimit, delimit);
3486 }
3487 return delimit;
3488}
3489#endif
3490
3491static void select_linked_delimit_validate(BMesh *bm, int *delimit)
3492{
3493 if ((*delimit) & BMO_DELIM_UV) {
3494 if (!CustomData_has_layer(&bm->ldata, CD_PROP_FLOAT2)) {
3495 (*delimit) &= ~BMO_DELIM_UV;
3496 }
3497 }
3498}
3499
3500static void select_linked_delimit_begin(BMesh *bm, int delimit)
3501{
3502 DelimitData delimit_data{};
3503
3504 if (delimit & BMO_DELIM_UV) {
3505 delimit_data.cd_loop_type = CD_PROP_FLOAT2;
3506 delimit_data.cd_loop_offset = CustomData_get_offset(&bm->ldata, delimit_data.cd_loop_type);
3507 if (delimit_data.cd_loop_offset == -1) {
3508 delimit &= ~BMO_DELIM_UV;
3509 }
3510 }
3511
3512 /* Shouldn't need to allocated BMO flags here (sigh). */
3514
3515 {
3516 BMIter iter;
3517 BMEdge *e;
3518
3519 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
3520 const bool is_walk_ok = (select_linked_delimit_test(e, delimit, &delimit_data) == false);
3521
3522 BMO_edge_flag_set(bm, e, BMO_ELE_TAG, is_walk_ok);
3523 }
3524 }
3525}
3526
3528{
3529 BMesh *bm = em->bm;
3530
3532}
3533
3535{
3536 Scene *scene = CTX_data_scene(C);
3537 ViewLayer *view_layer = CTX_data_view_layer(C);
3538
3539#ifdef USE_LINKED_SELECT_DEFAULT_HACK
3540 const int delimit_init = select_linked_delimit_default_from_op(op,
3541 scene->toolsettings->selectmode);
3542#else
3543 const int delimit_init = RNA_enum_get(op->ptr, "delimit");
3544#endif
3545
3547 scene, view_layer, CTX_wm_view3d(C));
3548
3549 for (Object *obedit : objects) {
3550
3552 BMesh *bm = em->bm;
3553 BMIter iter;
3554 BMWalker walker;
3555
3556 int delimit = delimit_init;
3557
3559
3560 if (delimit) {
3561 select_linked_delimit_begin(em->bm, delimit);
3562 }
3563
3564 if (em->selectmode & SCE_SELECT_VERTEX) {
3565 BMVert *v;
3566
3567 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
3569 }
3570
3571 /* Exclude all delimited verts. */
3572 if (delimit) {
3573 BMEdge *e;
3574 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
3576 /* Check the edge for selected faces,
3577 * this supports stepping off isolated vertices which would otherwise be ignored. */
3581 }
3582 }
3583 }
3584 }
3585
3586 BMW_init(&walker,
3587 em->bm,
3590 delimit ? BMO_ELE_TAG : BMW_MASK_NOP,
3593 BMW_NIL_LAY);
3594
3595 if (delimit) {
3596 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
3598 BMElem *ele_walk;
3599 BMW_ITER (ele_walk, &walker, v) {
3600 if (ele_walk->head.htype == BM_LOOP) {
3601 BMVert *v_step = ((BMLoop *)ele_walk)->v;
3602 BM_vert_select_set(em->bm, v_step, true);
3604 }
3605 else {
3606 BMEdge *e_step = (BMEdge *)ele_walk;
3607 BLI_assert(ele_walk->head.htype == BM_EDGE);
3608 BM_edge_select_set(em->bm, e_step, true);
3611 }
3612 }
3613 }
3614 }
3615 }
3616 else {
3617 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
3619 BMEdge *e_walk;
3620 BMW_ITER (e_walk, &walker, v) {
3621 BM_edge_select_set(em->bm, e_walk, true);
3623 }
3624 }
3625 }
3626 }
3627
3628 BMW_end(&walker);
3629
3631 }
3632 else if (em->selectmode & SCE_SELECT_EDGE) {
3633 BMEdge *e;
3634
3635 if (delimit) {
3636 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
3637 /* Check the edge for selected faces,
3638 * this supports stepping off isolated edges which would otherwise be ignored. */
3644 }
3645 }
3646 else {
3647 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
3649 }
3650 }
3651
3652 BMW_init(&walker,
3653 em->bm,
3656 delimit ? BMO_ELE_TAG : BMW_MASK_NOP,
3659 BMW_NIL_LAY);
3660
3661 if (delimit) {
3662 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
3664 BMElem *ele_walk;
3665 BMW_ITER (ele_walk, &walker, e) {
3666 if (ele_walk->head.htype == BM_LOOP) {
3667 BMLoop *l_step = (BMLoop *)ele_walk;
3668 BM_edge_select_set(em->bm, l_step->e, true);
3669 BM_edge_select_set(em->bm, l_step->prev->e, true);
3671 }
3672 else {
3673 BMEdge *e_step = (BMEdge *)ele_walk;
3674 BLI_assert(ele_walk->head.htype == BM_EDGE);
3675 BM_edge_select_set(em->bm, e_step, true);
3677 }
3678 }
3679 }
3680 }
3681 }
3682 else {
3683 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
3685 BMEdge *e_walk;
3686 BMW_ITER (e_walk, &walker, e) {
3687 BM_edge_select_set(em->bm, e_walk, true);
3689 }
3690 }
3691 }
3692 }
3693
3694 BMW_end(&walker);
3695
3697 }
3698 else {
3699 BMFace *f;
3700
3701 BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
3703 }
3704
3705 BMW_init(&walker,
3706 bm,
3707 BMW_ISLAND,
3709 delimit ? BMO_ELE_TAG : BMW_MASK_NOP,
3712 BMW_NIL_LAY);
3713
3714 BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
3716 BMFace *f_walk;
3717 BMW_ITER (f_walk, &walker, f) {
3718 BM_face_select_set(bm, f_walk, true);
3720 }
3721 }
3722 }
3723
3724 BMW_end(&walker);
3725 }
3726
3727 if (delimit) {
3729 }
3730
3732
3733 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
3734 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
3735 }
3736
3737 return OPERATOR_FINISHED;
3738}
3739
3741{
3742 PropertyRNA *prop;
3743
3744 /* Identifiers. */
3745 ot->name = "Select Linked All";
3746 ot->idname = "MESH_OT_select_linked";
3747 ot->description = "Select all vertices connected to the current selection";
3748
3749 /* API callbacks. */
3751 ot->poll = ED_operator_editmesh;
3752
3753 /* Flags. */
3754 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3755
3756 prop = RNA_def_enum_flag(ot->srna,
3757 "delimit",
3760 "Delimit",
3761 "Delimit selected region");
3762#ifdef USE_LINKED_SELECT_DEFAULT_HACK
3764#else
3765 UNUSED_VARS(prop);
3766#endif
3767}
3768
3770
3771/* -------------------------------------------------------------------- */
3774
3776
3777static void edbm_select_linked_pick_ex(BMEditMesh *em, BMElem *ele, bool sel, int delimit)
3778{
3779 BMesh *bm = em->bm;
3780 BMWalker walker;
3781
3783
3784 if (delimit) {
3786 }
3787
3788 /* NOTE: logic closely matches #edbm_select_linked_exec, keep in sync. */
3789
3790 if (ele->head.htype == BM_VERT) {
3791 BMVert *eve = (BMVert *)ele;
3792
3793 BMW_init(&walker,
3794 bm,
3797 delimit ? BMO_ELE_TAG : BMW_MASK_NOP,
3800 BMW_NIL_LAY);
3801
3802 if (delimit) {
3803 BMElem *ele_walk;
3804 BMW_ITER (ele_walk, &walker, eve) {
3805 if (ele_walk->head.htype == BM_LOOP) {
3806 BMVert *v_step = ((BMLoop *)ele_walk)->v;
3807 BM_vert_select_set(bm, v_step, sel);
3808 }
3809 else {
3810 BMEdge *e_step = (BMEdge *)ele_walk;
3811 BLI_assert(ele_walk->head.htype == BM_EDGE);
3812 BM_edge_select_set(bm, e_step, sel);
3813 }
3814 }
3815 }
3816 else {
3817 BMEdge *e_walk;
3818 BMW_ITER (e_walk, &walker, eve) {
3819 BM_edge_select_set(bm, e_walk, sel);
3820 }
3821 }
3822
3823 BMW_end(&walker);
3824
3826 }
3827 else if (ele->head.htype == BM_EDGE) {
3828 BMEdge *eed = (BMEdge *)ele;
3829
3830 BMW_init(&walker,
3831 bm,
3834 delimit ? BMO_ELE_TAG : BMW_MASK_NOP,
3837 BMW_NIL_LAY);
3838
3839 if (delimit) {
3840 BMElem *ele_walk;
3841 BMW_ITER (ele_walk, &walker, eed) {
3842 if (ele_walk->head.htype == BM_LOOP) {
3843 BMEdge *e_step = ((BMLoop *)ele_walk)->e;
3844 BM_edge_select_set(bm, e_step, sel);
3845 }
3846 else {
3847 BMEdge *e_step = (BMEdge *)ele_walk;
3848 BLI_assert(ele_walk->head.htype == BM_EDGE);
3849 BM_edge_select_set(bm, e_step, sel);
3850 }
3851 }
3852 }
3853 else {
3854 BMEdge *e_walk;
3855 BMW_ITER (e_walk, &walker, eed) {
3856 BM_edge_select_set(bm, e_walk, sel);
3857 }
3858 }
3859
3860 BMW_end(&walker);
3861
3863 }
3864 else if (ele->head.htype == BM_FACE) {
3865 BMFace *efa = (BMFace *)ele;
3866
3867 BMW_init(&walker,
3868 bm,
3869 BMW_ISLAND,
3871 delimit ? BMO_ELE_TAG : BMW_MASK_NOP,
3874 BMW_NIL_LAY);
3875
3876 {
3877 BMFace *f_walk;
3878 BMW_ITER (f_walk, &walker, efa) {
3879 BM_face_select_set(bm, f_walk, sel);
3881 }
3882 }
3883
3884 BMW_end(&walker);
3885 }
3886
3888
3889 if (delimit) {
3891 }
3892}
3893
3895 wmOperator *op,
3896 const wmEvent *event)
3897{
3898 Base *basact = nullptr;
3899 BMVert *eve;
3900 BMEdge *eed;
3901 BMFace *efa;
3902 const bool sel = !RNA_boolean_get(op->ptr, "deselect");
3903 int index;
3904
3905 if (RNA_struct_property_is_set(op->ptr, "index")) {
3906 return edbm_select_linked_pick_exec(C, op);
3907 }
3908
3909 /* #unified_findnearest needs OpenGL. */
3911
3912 /* Setup view context for argument to callbacks. */
3914
3916 vc.scene, vc.view_layer, vc.v3d);
3917
3918 {
3919 bool has_edges = false;
3920 for (Base *base : bases) {
3921 Object *ob_iter = base->object;
3923 if (vc.em->bm->totedge) {
3924 has_edges = true;
3925 }
3926 }
3927 if (has_edges == false) {
3928 return OPERATOR_CANCELLED;
3929 }
3930 }
3931
3932 vc.mval[0] = event->mval[0];
3933 vc.mval[1] = event->mval[1];
3934
3935 /* Return warning. */
3936 {
3937 int base_index = -1;
3938 const bool ok = unified_findnearest(&vc, bases, &base_index, &eve, &eed, &efa);
3939 if (!ok) {
3940 return OPERATOR_CANCELLED;
3941 }
3942 basact = bases[base_index];
3943 }
3944
3946 BMEditMesh *em = vc.em;
3947 BMesh *bm = em->bm;
3948
3949#ifdef USE_LINKED_SELECT_DEFAULT_HACK
3951#else
3952 int delimit = RNA_enum_get(op->ptr, "delimit");
3953#endif
3954
3955 BMElem *ele = EDBM_elem_from_selectmode(em, eve, eed, efa);
3956
3957 edbm_select_linked_pick_ex(em, ele, sel, delimit);
3958
3959 /* To support redo. */
3960 {
3961 /* Note that the `base_index` can't be used as the index depends on the 3D Viewport
3962 * which might not be available on redo. */
3964 int object_index;
3965 index = EDBM_elem_to_index_any_multi(vc.scene, vc.view_layer, em, ele, &object_index);
3966 BLI_assert(object_index >= 0);
3967 RNA_int_set(op->ptr, "object_index", object_index);
3968 RNA_int_set(op->ptr, "index", index);
3969 }
3970
3971 DEG_id_tag_update(static_cast<ID *>(basact->object->data), ID_RECALC_SELECT);
3973
3974 return OPERATOR_FINISHED;
3975}
3976
3978{
3979 Object *obedit = nullptr;
3980 BMElem *ele;
3981
3982 {
3983 const Scene *scene = CTX_data_scene(C);
3984 ViewLayer *view_layer = CTX_data_view_layer(C);
3985 /* Intentionally wrap negative values so the lookup fails. */
3986 const uint object_index = uint(RNA_int_get(op->ptr, "object_index"));
3987 const uint index = uint(RNA_int_get(op->ptr, "index"));
3988 ele = EDBM_elem_from_index_any_multi(scene, view_layer, object_index, index, &obedit);
3989 }
3990
3991 if (ele == nullptr) {
3992 return OPERATOR_CANCELLED;
3993 }
3994
3996 const bool sel = !RNA_boolean_get(op->ptr, "deselect");
3997
3998#ifdef USE_LINKED_SELECT_DEFAULT_HACK
4000#else
4001 int delimit = RNA_enum_get(op->ptr, "delimit");
4002#endif
4003
4004 edbm_select_linked_pick_ex(em, ele, sel, delimit);
4005
4006 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
4008
4009 return OPERATOR_FINISHED;
4010}
4011
4013{
4014 PropertyRNA *prop;
4015
4016 /* Identifiers. */
4017 ot->name = "Select Linked";
4018 ot->idname = "MESH_OT_select_linked_pick";
4019 ot->description = "(De)select all vertices linked to the edge under the mouse cursor";
4020
4021 /* API callbacks. */
4024 ot->poll = ED_operator_editmesh;
4025
4026 /* Flags. */
4027 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
4028
4029 RNA_def_boolean(ot->srna, "deselect", false, "Deselect", "");
4030 prop = RNA_def_enum_flag(ot->srna,
4031 "delimit",
4034 "Delimit",
4035 "Delimit selected region");
4036#ifdef USE_LINKED_SELECT_DEFAULT_HACK
4038#endif
4039
4040 /* Use for redo. */
4041 prop = RNA_def_int(ot->srna, "object_index", -1, -1, INT_MAX, "", "", 0, INT_MAX);
4043 prop = RNA_def_int(ot->srna, "index", -1, -1, INT_MAX, "", "", 0, INT_MAX);
4045}
4046
4048
4049/* -------------------------------------------------------------------- */
4052
4054{
4055 const Scene *scene = CTX_data_scene(C);
4056 ViewLayer *view_layer = CTX_data_view_layer(C);
4057 const bool extend = RNA_boolean_get(op->ptr, "extend");
4058 const bool exclude_nonmanifold = RNA_boolean_get(op->ptr, "exclude_nonmanifold");
4059 const int pole_count = RNA_int_get(op->ptr, "pole_count");
4060 const eElemCountType type = eElemCountType(RNA_enum_get(op->ptr, "type"));
4062 scene, view_layer, CTX_wm_view3d(C));
4063
4064 for (Object *obedit : objects) {
4066 bool changed = false;
4067
4068 BMIter iter;
4069 BMVert *v;
4070
4071 if (!extend) {
4073 changed = true;
4074 }
4075
4076 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
4078 continue;
4079 }
4080
4081 const int v_edge_count = BM_vert_edge_count_at_most(v, pole_count + 1);
4082 if (!is_count_a_match(type, v_edge_count, pole_count)) {
4083 continue;
4084 }
4085
4086 if (exclude_nonmanifold) {
4087 /* Exclude non-manifold vertices (no edges). */
4088 if (BM_vert_is_manifold(v) == false) {
4089 continue;
4090 }
4091
4092 /* Exclude vertices connected to non-manifold edges. */
4093 BMIter eiter;
4094 BMEdge *e;
4095 bool all_edges_manifold = true;
4096 BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
4097 if (BM_edge_is_manifold(e) == false) {
4098 all_edges_manifold = false;
4099 break;
4100 }
4101 }
4102
4103 if (all_edges_manifold == false) {
4104 continue;
4105 }
4106 }
4107
4108 /* All tests passed, perform the selection. */
4109
4110 /* Multiple selection modes may be active.
4111 * Select elements per the finest-grained choice. */
4112 changed = true;
4113
4114 if (em->selectmode & SCE_SELECT_VERTEX) {
4115 BM_vert_select_set(em->bm, v, true);
4116 }
4117 else if (em->selectmode & SCE_SELECT_EDGE) {
4118 BMIter eiter;
4119 BMEdge *e;
4120 BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) {
4121 BM_edge_select_set(em->bm, e, true);
4122 }
4123 }
4124 else if (em->selectmode & SCE_SELECT_FACE) {
4125 BMIter fiter;
4126 BMFace *f;
4127 BM_ITER_ELEM (f, &fiter, v, BM_FACES_OF_VERT) {
4128 BM_face_select_set(em->bm, f, true);
4129 }
4130 }
4131 else {
4133 }
4134 }
4135
4136 if (changed) {
4139
4140 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
4141 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
4142 }
4143 }
4144
4145 return OPERATOR_FINISHED;
4146}
4147
4149{
4150 /* Identifiers. */
4151 ot->name = "Select By Pole Count";
4152 ot->description =
4153 "Select vertices at poles by the number of connected edges. "
4154 "In edge and face mode the geometry connected to the vertices is selected";
4155 ot->idname = "MESH_OT_select_by_pole_count";
4156
4157 /* API callbacks. */
4159 ot->poll = ED_operator_editmesh;
4160
4161 /* Flags. */
4162 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
4163
4164 /* Properties. */
4165 RNA_def_int(ot->srna, "pole_count", 4, 0, INT_MAX, "Pole Count", "", 0, INT_MAX);
4166 RNA_def_enum(ot->srna,
4167 "type",
4170 "Type",
4171 "Type of comparison to make");
4172 RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
4174 ot->srna, "exclude_nonmanifold", true, "Exclude Non Manifold", "Exclude non-manifold poles");
4175}
4176
4178
4179/* -------------------------------------------------------------------- */
4182
4184{
4185 const Scene *scene = CTX_data_scene(C);
4186 ViewLayer *view_layer = CTX_data_view_layer(C);
4187 const bool extend = RNA_boolean_get(op->ptr, "extend");
4188 const int numverts = RNA_int_get(op->ptr, "number");
4189 const eElemCountType type = eElemCountType(RNA_enum_get(op->ptr, "type"));
4191 scene, view_layer, CTX_wm_view3d(C));
4192
4193 for (Object *obedit : objects) {
4195 bool changed = false;
4196
4197 BMFace *efa;
4198 BMIter iter;
4199
4200 if (!extend) {
4202 changed = true;
4203 }
4204
4205 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
4207 continue;
4208 }
4209
4210 if (is_count_a_match(type, efa->len, numverts)) {
4211 changed = true;
4212 BM_face_select_set(em->bm, efa, true);
4213 }
4214 }
4215
4216 if (changed) {
4219
4220 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
4221 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
4222 }
4223 }
4224
4225 return OPERATOR_FINISHED;
4226}
4227
4229{
4230
4231 /* Identifiers. */
4232 ot->name = "Select Faces by Sides";
4233 ot->description = "Select vertices or faces by the number of face sides";
4234 ot->idname = "MESH_OT_select_face_by_sides";
4235
4236 /* API callbacks. */
4238 ot->poll = ED_operator_editmesh;
4239
4240 /* Flags. */
4241 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
4242
4243 /* Properties. */
4244 RNA_def_int(ot->srna, "number", 4, 3, INT_MAX, "Number of Vertices", "", 3, INT_MAX);
4245 RNA_def_enum(ot->srna,
4246 "type",
4249 "Type",
4250 "Type of comparison to make");
4251 RNA_def_boolean(ot->srna, "extend", true, "Extend", "Extend the selection");
4252}
4253
4255
4256/* -------------------------------------------------------------------- */
4259
4261{
4262 const Scene *scene = CTX_data_scene(C);
4263 ViewLayer *view_layer = CTX_data_view_layer(C);
4264 const bool extend = RNA_boolean_get(op->ptr, "extend");
4265
4267 scene, view_layer, CTX_wm_view3d(C));
4268
4269 for (Object *obedit : objects) {
4271 BMesh *bm = em->bm;
4272 BMIter iter;
4273
4274 bool changed = false;
4275
4276 if (!extend) {
4278 changed = true;
4279 }
4280
4281 if (em->selectmode & SCE_SELECT_VERTEX) {
4282 BMVert *eve;
4283 BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
4285 continue;
4286 }
4287 if (!eve->e) {
4288 BM_vert_select_set(bm, eve, true);
4289 changed = true;
4290 }
4291 }
4292 }
4293
4294 if (em->selectmode & SCE_SELECT_EDGE) {
4295 BMEdge *eed;
4296 BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
4298 continue;
4299 }
4300 if (BM_edge_is_wire(eed)) {
4301 BM_edge_select_set(bm, eed, true);
4302 changed = true;
4303 }
4304 }
4305 }
4306
4307 if (em->selectmode & SCE_SELECT_FACE) {
4308 BMFace *efa;
4309 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
4311 continue;
4312 }
4313 BMIter liter;
4314 BMLoop *l;
4315 bool is_loose = true;
4316 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
4317 if (!BM_edge_is_boundary(l->e)) {
4318 is_loose = false;
4319 break;
4320 }
4321 }
4322 if (is_loose) {
4323 BM_face_select_set(bm, efa, true);
4324 changed = true;
4325 }
4326 }
4327 }
4328
4329 if (changed) {
4332
4333 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
4334 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
4335 }
4336 }
4337
4338 return OPERATOR_FINISHED;
4339}
4340
4342{
4343 /* Identifiers. */
4344 ot->name = "Select Loose Geometry";
4345 ot->description = "Select loose geometry based on the selection mode";
4346 ot->idname = "MESH_OT_select_loose";
4347
4348 /* API callbacks. */
4349 ot->exec = edbm_select_loose_exec;
4350 ot->poll = ED_operator_editmesh;
4351
4352 /* Flags. */
4353 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
4354
4355 /* Props. */
4356 RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
4357}
4358
4360
4361/* -------------------------------------------------------------------- */
4364
4366{
4367 const Scene *scene = CTX_data_scene(C);
4368 ViewLayer *view_layer = CTX_data_view_layer(C);
4369 const int axis_flag = RNA_enum_get(op->ptr, "axis");
4370 const bool extend = RNA_boolean_get(op->ptr, "extend");
4371 Object *obedit_active = CTX_data_edit_object(C);
4372 BMEditMesh *em_active = BKE_editmesh_from_object(obedit_active);
4373 const int select_mode = em_active->bm->selectmode;
4374 int tot_mirr = 0, tot_fail = 0;
4375
4377 scene, view_layer, CTX_wm_view3d(C));
4378
4379 for (Object *obedit : objects) {
4381
4382 if (em->bm->totvertsel == 0) {
4383 continue;
4384 }
4385
4386 int tot_mirr_iter = 0, tot_fail_iter = 0;
4387
4388 for (int axis = 0; axis < 3; axis++) {
4389 if ((1 << axis) & axis_flag) {
4391 static_cast<const Mesh *>(obedit->data),
4392 axis,
4393 extend,
4394 &tot_mirr_iter,
4395 &tot_fail_iter);
4396 }
4397 }
4398
4399 if (tot_mirr_iter) {
4402
4403 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
4404 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
4405 }
4406
4407 tot_fail += tot_fail_iter;
4408 tot_mirr += tot_mirr_iter;
4409 }
4410
4411 if (tot_mirr || tot_fail) {
4412 ED_mesh_report_mirror_ex(*op->reports, tot_mirr, tot_fail, select_mode);
4413 }
4414 return OPERATOR_FINISHED;
4415}
4416
4418{
4419 /* Identifiers. */
4420 ot->name = "Select Mirror";
4421 ot->description = "Select mesh items at mirrored locations";
4422 ot->idname = "MESH_OT_select_mirror";
4423
4424 /* API callbacks. */
4426 ot->poll = ED_operator_editmesh;
4427
4428 /* Flags. */
4429 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
4430
4431 /* Props. */
4432 RNA_def_enum_flag(ot->srna, "axis", rna_enum_axis_flag_xyz_items, (1 << 0), "Axis", "");
4433
4434 RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the existing selection");
4435}
4436
4438
4439/* -------------------------------------------------------------------- */
4442
4444{
4445 const Scene *scene = CTX_data_scene(C);
4446 ViewLayer *view_layer = CTX_data_view_layer(C);
4447 const bool use_face_step = RNA_boolean_get(op->ptr, "use_face_step");
4448
4450 scene, view_layer, CTX_wm_view3d(C));
4451 for (Object *obedit : objects) {
4453 BMesh *bm = em->bm;
4454
4455 if ((bm->totvertsel == 0) && (bm->totedgesel == 0) && (bm->totfacesel == 0)) {
4456 continue;
4457 }
4458
4459 EDBM_select_more(em, use_face_step);
4460 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
4461 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
4462 }
4463
4464 return OPERATOR_FINISHED;
4465}
4466
4468{
4469 /* Identifiers. */
4470 ot->name = "Select More";
4471 ot->idname = "MESH_OT_select_more";
4472 ot->description = "Select more vertices, edges or faces connected to initial selection";
4473
4474 /* API callbacks */
4475 ot->exec = edbm_select_more_exec;
4476 ot->poll = ED_operator_editmesh;
4477
4478 /* Flags. */
4479 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
4480
4482 ot->srna, "use_face_step", true, "Face Step", "Connected faces (instead of edges)");
4483}
4484
4486
4487/* -------------------------------------------------------------------- */
4490
4492{
4493 const Scene *scene = CTX_data_scene(C);
4494 ViewLayer *view_layer = CTX_data_view_layer(C);
4495 const bool use_face_step = RNA_boolean_get(op->ptr, "use_face_step");
4496
4498 scene, view_layer, CTX_wm_view3d(C));
4499 for (Object *obedit : objects) {
4501 BMesh *bm = em->bm;
4502
4503 if ((bm->totvertsel == 0) && (bm->totedgesel == 0) && (bm->totfacesel == 0)) {
4504 continue;
4505 }
4506
4507 EDBM_select_less(em, use_face_step);
4508 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
4509 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
4510 }
4511
4512 return OPERATOR_FINISHED;
4513}
4514
4516{
4517 /* Identifiers. */
4518 ot->name = "Select Less";
4519 ot->idname = "MESH_OT_select_less";
4520 ot->description = "Deselect vertices, edges or faces at the boundary of each selection region";
4521
4522 /* API callbacks */
4523 ot->exec = edbm_select_less_exec;
4524 ot->poll = ED_operator_editmesh;
4525
4526 /* Flags. */
4527 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
4528
4530 ot->srna, "use_face_step", true, "Face Step", "Connected faces (instead of edges)");
4531}
4532
4534
4535/* -------------------------------------------------------------------- */
4538
4543{
4544 BMIter viter;
4545 BMVert *v;
4546
4547 BM_ITER_ELEM (v, &viter, e, BM_VERTS_OF_EDGE) {
4548 BMIter eiter;
4549 BMEdge *e_other;
4550
4551 BM_ITER_ELEM (e_other, &eiter, v, BM_EDGES_OF_VERT) {
4552 if ((e_other != e) && BM_elem_flag_test(e_other, BM_ELEM_SELECT)) {
4553 return false;
4554 }
4555 }
4556 }
4557 return true;
4558}
4559
4561{
4562 BMIter eiter;
4563 BMEdge *e_other, *e_next = nullptr;
4564 int count = 0;
4565 const int count_expected = 1;
4566
4567 BM_ITER_ELEM (e_other, &eiter, v, BM_EDGES_OF_VERT) {
4568 if (e_other == e_curr || !BM_elem_flag_test(e_other, BM_ELEM_SELECT)) {
4569 continue;
4570 }
4571 if (++count > count_expected) {
4572 return nullptr;
4573 }
4574 e_next = e_other;
4575 }
4576 return (count == count_expected) ? e_next : nullptr;
4577}
4578
4580{
4581 BMIter eiter;
4582 BMEdge *e;
4583 BMVert *v_next = nullptr;
4584 int count = 0;
4585 const int count_expected = v_prev ? 1 : 2;
4586
4587 BM_ITER_ELEM (e, &eiter, v_curr, BM_EDGES_OF_VERT) {
4588 BMVert *v_other = BM_edge_other_vert(e, v_curr);
4589 if (v_other == v_prev || !BM_elem_flag_test(v_other, BM_ELEM_SELECT)) {
4590 continue;
4591 }
4592 if (++count > count_expected) {
4593 return nullptr;
4594 }
4595 v_next = v_other;
4596 }
4597 return (count == count_expected) ? v_next : nullptr;
4598}
4599
4601 BMFace *f_prev)
4602{
4603 BMIter liter;
4604 BMLoop *l;
4605 BMFace *f_next = nullptr;
4606 int count = 0;
4607 const int count_expected = f_prev ? 1 : 2;
4608
4609 BM_ITER_ELEM (l, &liter, f_curr, BM_LOOPS_OF_FACE) {
4610 BMIter fiter;
4611 BMFace *f_other;
4612 BM_ITER_ELEM (f_other, &fiter, l->e, BM_FACES_OF_EDGE) {
4613 if (ELEM(f_other, f_curr, f_prev) || !BM_elem_flag_test(f_other, BM_ELEM_SELECT)) {
4614 continue;
4615 }
4616 if (++count > count_expected) {
4617 return nullptr;
4618 }
4619 f_next = f_other;
4620 }
4621 }
4622 return (count == count_expected) ? f_next : nullptr;
4623}
4624
4629{
4630 BMVert *v_prev = nullptr, *v_curr = v_start;
4631
4632 do {
4633 int selected_neighbor_count = 0;
4634 BMIter eiter;
4635 BMEdge *e;
4636 BM_ITER_ELEM (e, &eiter, v_curr, BM_EDGES_OF_VERT) {
4637 BMVert *v_other = BM_edge_other_vert(e, v_curr);
4638 if (BM_elem_flag_test(v_other, BM_ELEM_SELECT)) {
4639 if (++selected_neighbor_count > 2) {
4640 return false;
4641 }
4642 }
4643 }
4644 if (selected_neighbor_count != 2) {
4645 return false;
4646 }
4647
4648 BMVert *v_next = bm_step_to_next_selected_vert_in_chain(v_curr, v_prev);
4649 if (v_next == nullptr) {
4650 return false;
4651 }
4652 v_prev = v_curr;
4653 v_curr = v_next;
4654 } while (v_curr != v_start);
4655
4656 return true;
4657}
4658
4663{
4664 BMEdge *e_curr = e_start;
4665 BMVert *v_through = e_start->v1;
4666
4667 do {
4668 BMEdge *e_next = bm_step_over_vert_to_next_selected_edge_in_chain(e_curr, v_through);
4669 if (e_next == nullptr) {
4670 return false;
4671 }
4672 v_through = BM_edge_other_vert(e_next, v_through);
4673 e_curr = e_next;
4674
4675 } while (e_curr != e_start);
4676
4677 return true;
4678}
4679
4684{
4685 BMFace *f_prev = nullptr;
4686 BMFace *f_curr = f_start;
4687
4688 do {
4689 int selected_neighbor_count = 0;
4690 BMIter liter;
4691 BMLoop *l;
4692
4693 BM_ITER_ELEM (l, &liter, f_curr, BM_LOOPS_OF_FACE) {
4694 BMIter fiter;
4695 BMFace *f_other;
4696 BM_ITER_ELEM (f_other, &fiter, l->e, BM_FACES_OF_EDGE) {
4697 if (f_other != f_curr && BM_elem_flag_test(f_other, BM_ELEM_SELECT)) {
4698 selected_neighbor_count++;
4699 }
4700 }
4701 }
4702 if (selected_neighbor_count != 2) {
4703 return false;
4704 }
4705
4707 if (f_next == nullptr) {
4708 return false;
4709 }
4710
4711 f_prev = f_curr;
4712 f_curr = f_next;
4713 } while (f_curr != f_start);
4714
4715 return true;
4716}
4717
4719 const CheckerIntervalParams *op_params,
4720 BMVert *v_start)
4721{
4722 BMesh *bm = em->bm;
4723 BMVert *v_prev = nullptr;
4724 BMVert *v_curr = v_start;
4725 int index = 0;
4726
4727 /* Mark all vertices as unvisited. */
4729
4730 while (v_curr && !BM_elem_flag_test(v_curr, BM_ELEM_TAG)) {
4731 /* Mark as visited. */
4733
4734 /* Apply checker pattern based on position in loop. */
4735 if (!WM_operator_properties_checker_interval_test(op_params, index)) {
4736 BM_elem_select_set(bm, (BMElem *)v_curr, false);
4737 }
4738
4739 /* Find next vertex in the loop. */
4740 BMVert *v_next = bm_step_to_next_selected_vert_in_chain(v_curr, v_prev);
4741 if (ELEM(v_next, nullptr, v_start)) {
4742 break;
4743 }
4744
4745 v_prev = v_curr;
4746 v_curr = v_next;
4747 index++;
4748 }
4749}
4750
4752 const CheckerIntervalParams *op_params,
4753 BMEdge *e_start)
4754{
4755 BMesh *bm = em->bm;
4756 BMEdge *e_curr = e_start;
4757 BMVert *v_through = e_start->v1;
4758 int index = 0;
4759
4760 /* Mark all edges as unvisited. */
4762
4763 while (e_curr && !BM_elem_flag_test(e_curr, BM_ELEM_TAG)) {
4764 /* Mark as visited. */
4766
4767 /* Apply checker pattern based on position in loop. */
4768 if (!WM_operator_properties_checker_interval_test(op_params, index)) {
4769 BM_elem_select_set(bm, (BMElem *)e_curr, false);
4770 }
4771
4772 /* Find next edge in the loop. */
4773 BMEdge *e_next = bm_step_over_vert_to_next_selected_edge_in_chain(e_curr, v_through);
4774 if (ELEM(e_next, nullptr, e_start)) {
4775 break;
4776 }
4777
4778 v_through = BM_edge_other_vert(e_next, v_through);
4779 e_curr = e_next;
4780 index++;
4781 }
4782}
4783
4785 const CheckerIntervalParams *op_params,
4786 BMFace *f_start)
4787{
4788 BMesh *bm = em->bm;
4789 BMFace *f_prev = nullptr;
4790 BMFace *f_curr = f_start;
4791 int index = 0;
4792
4793 /* Mark all faces as unvisited. */
4795
4796 while (f_curr && !BM_elem_flag_test(f_curr, BM_ELEM_TAG)) {
4798
4800
4801 /* Apply checker pattern to current face. */
4802 if (!WM_operator_properties_checker_interval_test(op_params, index)) {
4803 BM_elem_select_set(bm, (BMElem *)f_curr, false);
4804 }
4805
4806 if (ELEM(f_next, nullptr, f_start)) {
4807 break;
4808 }
4809
4810 f_prev = f_curr;
4811 f_curr = f_next;
4812 index++;
4813 }
4814}
4815
4816/* Walk all reachable elements of the same type as h_act in breadth-first
4817 * order, starting from h_act. Deselects elements if the depth when they
4818 * are reached is not a multiple of "nth". */
4820 const CheckerIntervalParams *op_params,
4821 BMHeader *h_act)
4822{
4823 BMElem *ele;
4824 BMesh *bm = em->bm;
4825 BMWalker walker;
4826 BMIter iter;
4827 int walktype = 0, itertype = 0, flushtype = 0;
4828 short mask_vert = 0, mask_edge = 0, mask_face = 0;
4829
4830 /* No active element from which to start - nothing to do. */
4831 if (h_act == nullptr) {
4832 return;
4833 }
4834
4835 /* Note on cyclic-chain handling here:
4836 *
4837 * The use of a breadth first search to determine element order
4838 * causes problems with cyclic topology.
4839 *
4840 * The walker ordered vertices by their graph depth from the active element.
4841 * This approach was failing on loops like a circle because the breadth first
4842 * search expanded in two directions simultaneously, creating a symmetrical
4843 * but non sequential depth map, see: #126909. */
4844 if (h_act->htype == BM_VERT) {
4845 BMVert *v_start = (BMVert *)h_act;
4846 if (bm_verts_form_cyclic_chain(v_start)) {
4847 walker_deselect_nth_vertex_chain(em, op_params, v_start);
4849 return;
4850 }
4851 }
4852 else if (h_act->htype == BM_EDGE) {
4853 BMEdge *e_start = (BMEdge *)h_act;
4854 if (bm_edges_form_cyclic_chain(e_start)) {
4855 walker_deselect_nth_edge_chain(em, op_params, e_start);
4857 return;
4858 }
4859 }
4860 else if (h_act->htype == BM_FACE) {
4861 BMFace *f_start = (BMFace *)h_act;
4862 if (bm_faces_form_cyclic_chain(f_start)) {
4863 walker_deselect_nth_face_chain(em, op_params, f_start);
4865 return;
4866 }
4867 }
4868
4869 /* Determine which type of iterator, walker, and select flush to use
4870 * based on type of the elements being deselected. */
4871 switch (h_act->htype) {
4872 case BM_VERT:
4873 itertype = BM_VERTS_OF_MESH;
4874 walktype = BMW_CONNECTED_VERTEX;
4875 flushtype = SCE_SELECT_VERTEX;
4876 mask_vert = BMO_ELE_TAG;
4877 break;
4878 case BM_EDGE:
4879 /* When an edge has no connected-selected edges,
4880 * use face-stepping (supports edge-rings). */
4881 itertype = BM_EDGES_OF_MESH;
4883 flushtype = SCE_SELECT_EDGE;
4884 mask_edge = BMO_ELE_TAG;
4885 break;
4886 case BM_FACE:
4887 itertype = BM_FACES_OF_MESH;
4888 walktype = BMW_ISLAND;
4889 flushtype = SCE_SELECT_FACE;
4890 mask_face = BMO_ELE_TAG;
4891 break;
4892 }
4893
4894 /* Shouldn't need to allocate BMO flags here (sigh). */
4896
4897 /* Walker restrictions uses BMO flags, not header flags,
4898 * so transfer BM_ELEM_SELECT from HFlags onto a BMO flag layer. */
4899 BMO_push(bm, nullptr);
4900 BM_ITER_MESH (ele, &iter, bm, itertype) {
4903 }
4904 }
4905
4906 /* Walk over selected elements starting at active. */
4907 BMW_init(&walker,
4908 bm,
4909 walktype,
4910 mask_vert,
4911 mask_edge,
4912 mask_face,
4913 BMW_FLAG_NOP, /* Don't use #BMW_FLAG_TEST_HIDDEN here since we want to deselect all. */
4914 BMW_NIL_LAY);
4915
4916 /* Use tag to avoid touching the same elems twice. */
4917 BM_ITER_MESH (ele, &iter, bm, itertype) {
4919 }
4920
4922 for (ele = static_cast<BMElem *>(BMW_begin(&walker, h_act)); ele != nullptr;
4923 ele = static_cast<BMElem *>(BMW_step(&walker)))
4924 {
4925 if (!BM_elem_flag_test(ele, BM_ELEM_TAG)) {
4926 /* Deselect elements that aren't at "nth" depth from active. */
4927 const int depth = BMW_current_depth(&walker) - 1;
4928 if (!WM_operator_properties_checker_interval_test(op_params, depth)) {
4929 BM_elem_select_set(bm, ele, false);
4930 }
4932 }
4933 }
4934 BMW_end(&walker);
4935
4936 BMO_pop(bm);
4937
4938 /* Flush selection up. */
4939 EDBM_selectmode_flush_ex(em, flushtype);
4940}
4941
4942static void deselect_nth_active(BMEditMesh *em, BMVert **r_eve, BMEdge **r_eed, BMFace **r_efa)
4943{
4944 BMIter iter;
4945 BMElem *ele;
4946
4947 *r_eve = nullptr;
4948 *r_eed = nullptr;
4949 *r_efa = nullptr;
4950
4952 ele = BM_mesh_active_elem_get(em->bm);
4953
4954 if (ele && BM_elem_flag_test(ele, BM_ELEM_SELECT)) {
4955 switch (ele->head.htype) {
4956 case BM_VERT:
4957 *r_eve = (BMVert *)ele;
4958 return;
4959 case BM_EDGE:
4960 *r_eed = (BMEdge *)ele;
4961 return;
4962 case BM_FACE:
4963 *r_efa = (BMFace *)ele;
4964 return;
4965 }
4966 }
4967
4968 if (em->selectmode & SCE_SELECT_VERTEX) {
4969 BMVert *v;
4970 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
4972 *r_eve = v;
4973 return;
4974 }
4975 }
4976 }
4977 else if (em->selectmode & SCE_SELECT_EDGE) {
4978 BMEdge *e;
4979 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
4981 *r_eed = e;
4982 return;
4983 }
4984 }
4985 }
4986 else if (em->selectmode & SCE_SELECT_FACE) {
4987 BMFace *f = BM_mesh_active_face_get(em->bm, true, false);
4988 if (f && BM_elem_flag_test(f, BM_ELEM_SELECT)) {
4989 *r_efa = f;
4990 return;
4991 }
4992 }
4993}
4994
4995static bool edbm_deselect_nth(BMEditMesh *em, const CheckerIntervalParams *op_params)
4996{
4997 BMVert *v;
4998 BMEdge *e;
4999 BMFace *f;
5000
5001 deselect_nth_active(em, &v, &e, &f);
5002
5003 if (v) {
5004 walker_deselect_nth(em, op_params, &v->head);
5005 return true;
5006 }
5007 if (e) {
5008 walker_deselect_nth(em, op_params, &e->head);
5009 return true;
5010 }
5011 if (f) {
5012 walker_deselect_nth(em, op_params, &f->head);
5013 return true;
5014 }
5015
5016 return false;
5017}
5018
5020{
5021 const Scene *scene = CTX_data_scene(C);
5022 ViewLayer *view_layer = CTX_data_view_layer(C);
5023 CheckerIntervalParams op_params;
5025 bool found_active_elt = false;
5026
5028 scene, view_layer, CTX_wm_view3d(C));
5029
5030 for (Object *obedit : objects) {
5032
5033 if ((em->bm->totvertsel == 0) && (em->bm->totedgesel == 0) && (em->bm->totfacesel == 0)) {
5034 continue;
5035 }
5036
5037 if (edbm_deselect_nth(em, &op_params) == true) {
5039
5040 found_active_elt = true;
5042 params.calc_looptris = false;
5043 params.calc_normals = false;
5044 params.is_destructive = false;
5045 EDBM_update(static_cast<Mesh *>(obedit->data), &params);
5046 }
5047 }
5048
5049 if (!found_active_elt) {
5050 BKE_report(op->reports, RPT_ERROR, "Mesh object(s) have no active vertex/edge/face");
5051 return OPERATOR_CANCELLED;
5052 }
5053
5054 return OPERATOR_FINISHED;
5055}
5056
5058{
5059 /* Identifiers. */
5060 ot->name = "Checker Deselect";
5061 ot->idname = "MESH_OT_select_nth";
5062 ot->description = "Deselect every Nth element starting from the active vertex, edge or face";
5063
5064 /* API callbacks. */
5065 ot->exec = edbm_select_nth_exec;
5066 ot->poll = ED_operator_editmesh;
5067
5068 /* Flags. */
5069 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5070
5072}
5073
5075{
5078
5079 if (vc.obedit) {
5081 }
5082 return vc;
5083}
5084
5086
5087/* -------------------------------------------------------------------- */
5090
5092{
5093 /* Find edges that have exactly two neighboring faces,
5094 * check the angle between those faces, and if angle is
5095 * small enough, select the edge. */
5096 const float angle_limit_cos = cosf(RNA_float_get(op->ptr, "sharpness"));
5097
5098 const Scene *scene = CTX_data_scene(C);
5099 ViewLayer *view_layer = CTX_data_view_layer(C);
5101 scene, view_layer, CTX_wm_view3d(C));
5102
5103 for (Object *obedit : objects) {
5105 BMIter iter;
5106 BMEdge *e;
5107
5108 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
5110 continue;
5111 }
5112
5113 BMLoop *l_a, *l_b;
5114 if (BM_edge_loop_pair(e, &l_a, &l_b)) {
5115 /* Edge has exactly two neighboring faces, check angle. */
5116 const float angle_cos = dot_v3v3(l_a->f->no, l_b->f->no);
5117
5118 if (angle_cos < angle_limit_cos) {
5119 BM_edge_select_set(em->bm, e, true);
5120 }
5121 }
5122 }
5123
5124 if ((em->bm->selectmode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) == 0) {
5125 /* Since we can't select individual edges, select faces connected to them. */
5127 }
5128 else {
5130 }
5132
5133 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
5134 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
5135 }
5136
5137 return OPERATOR_FINISHED;
5138}
5139
5141{
5142 PropertyRNA *prop;
5143
5144 /* Identifiers. */
5145 ot->name = "Select Sharp Edges";
5146 ot->description = "Select all sharp enough edges";
5147 ot->idname = "MESH_OT_edges_select_sharp";
5148
5149 /* API callbacks. */
5151 ot->poll = ED_operator_editmesh;
5152
5153 /* Flags. */
5154 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5155
5156 /* Props. */
5157 prop = RNA_def_float_rotation(ot->srna,
5158 "sharpness",
5159 0,
5160 nullptr,
5161 DEG2RADF(0.01f),
5162 DEG2RADF(180.0f),
5163 "Sharpness",
5164 "",
5165 DEG2RADF(1.0f),
5166 DEG2RADF(180.0f));
5168}
5169
5171
5172/* -------------------------------------------------------------------- */
5175
5177{
5178 const Scene *scene = CTX_data_scene(C);
5179 ViewLayer *view_layer = CTX_data_view_layer(C);
5181 scene, view_layer, CTX_wm_view3d(C));
5182 const float angle_limit_cos = cosf(RNA_float_get(op->ptr, "sharpness"));
5183
5184 for (Object *obedit : objects) {
5186 BMesh *bm = em->bm;
5187
5188 if (bm->totfacesel == 0) {
5189 continue;
5190 }
5191
5193
5194 BMIter iter, liter, liter2;
5195 BMFace *f;
5196 BMLoop *l, *l2;
5197
5199
5200 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
5201 if ((BM_elem_flag_test(f, BM_ELEM_HIDDEN) != 0) ||
5203 {
5204 continue;
5205 }
5206
5207 BLI_assert(stack.is_empty());
5208
5209 do {
5210 BM_face_select_set(bm, f, true);
5211
5213
5214 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
5215 BM_ITER_ELEM (l2, &liter2, l, BM_LOOPS_OF_LOOP) {
5216 float angle_cos;
5217
5219 {
5220 continue;
5221 }
5222
5223 angle_cos = dot_v3v3(f->no, l2->f->no);
5224
5225 if (angle_cos > angle_limit_cos) {
5226 stack.append(l2->f);
5227 }
5228 }
5229 }
5230 } while (!stack.is_empty() && (f = stack.pop_last()));
5231 }
5232
5233 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
5234 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
5235 }
5236
5237 return OPERATOR_FINISHED;
5238}
5239
5241{
5242 PropertyRNA *prop;
5243
5244 /* Identifiers. */
5245 ot->name = "Select Linked Flat Faces";
5246 ot->description = "Select linked faces by angle";
5247 ot->idname = "MESH_OT_faces_select_linked_flat";
5248
5249 /* API callbacks. */
5251 ot->poll = ED_operator_editmesh;
5252
5253 /* Flags. */
5254 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5255
5256 /* Props. */
5257 prop = RNA_def_float_rotation(ot->srna,
5258 "sharpness",
5259 0,
5260 nullptr,
5261 DEG2RADF(0.01f),
5262 DEG2RADF(180.0f),
5263 "Sharpness",
5264 "",
5265 DEG2RADF(1.0f),
5266 DEG2RADF(180.0f));
5268}
5269
5271
5272/* -------------------------------------------------------------------- */
5275
5277{
5278 const bool use_extend = RNA_boolean_get(op->ptr, "extend");
5279 const bool use_wire = RNA_boolean_get(op->ptr, "use_wire");
5280 const bool use_boundary = RNA_boolean_get(op->ptr, "use_boundary");
5281 const bool use_multi_face = RNA_boolean_get(op->ptr, "use_multi_face");
5282 const bool use_non_contiguous = RNA_boolean_get(op->ptr, "use_non_contiguous");
5283 const bool use_verts = RNA_boolean_get(op->ptr, "use_verts");
5284
5285 const Scene *scene = CTX_data_scene(C);
5286 ViewLayer *view_layer = CTX_data_view_layer(C);
5288 scene, view_layer, CTX_wm_view3d(C));
5289
5291
5292 for (Object *obedit : objects) {
5294 BMVert *v;
5295 BMEdge *e;
5296 BMIter iter;
5297
5298 bool changed = false;
5299
5300 if (!use_extend) {
5302 changed = true;
5303 }
5304
5305 /* Selects isolated verts, and edges that do not have 2 neighboring faces. */
5306 if (use_verts) {
5307 BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
5309 continue;
5310 }
5311
5312 if (!BM_vert_is_manifold(v)) {
5313 BM_vert_select_set(em->bm, v, true);
5314 changed = true;
5315 }
5316 }
5317 }
5318
5319 if (use_wire || use_boundary || use_multi_face || use_non_contiguous) {
5320 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
5322 continue;
5323 }
5324 if ((use_wire && BM_edge_is_wire(e)) || (use_boundary && BM_edge_is_boundary(e)) ||
5325 (use_non_contiguous && (BM_edge_is_manifold(e) && !BM_edge_is_contiguous(e))) ||
5326 (use_multi_face && BM_edge_face_count_is_over(e, 2)))
5327 {
5328 /* Check we never select perfect edge (in test above). */
5330
5331 BM_edge_select_set(em->bm, e, true);
5332 changed = true;
5333 }
5334 }
5335 }
5336
5337 if (changed) {
5338 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
5339 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
5340
5343 }
5344 }
5345
5346 return OPERATOR_FINISHED;
5347}
5348
5350{
5351 /* Identifiers. */
5352 ot->name = "Select Non-Manifold";
5353 ot->description = "Select all non-manifold vertices or edges";
5354 ot->idname = "MESH_OT_select_non_manifold";
5355
5356 /* API callbacks */
5359
5360 /* Flags. */
5361 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5362
5363 /* Props. */
5364 RNA_def_boolean(ot->srna, "extend", true, "Extend", "Extend the selection");
5365 /* Edges. */
5366 RNA_def_boolean(ot->srna, "use_wire", true, "Wire", "Wire edges");
5367 RNA_def_boolean(ot->srna, "use_boundary", true, "Boundaries", "Boundary edges");
5369 ot->srna, "use_multi_face", true, "Multiple Faces", "Edges shared by more than two faces");
5370 RNA_def_boolean(ot->srna,
5371 "use_non_contiguous",
5372 true,
5373 "Non Contiguous",
5374 "Edges between faces pointing in alternate directions");
5375 /* Verts. */
5377 ot->srna, "use_verts", true, "Vertices", "Vertices connecting multiple face regions");
5378}
5379
5381
5382/* -------------------------------------------------------------------- */
5385
5387{
5388 const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT);
5389 const float randfac = RNA_float_get(op->ptr, "ratio");
5391
5392 const Scene *scene = CTX_data_scene(C);
5393 ViewLayer *view_layer = CTX_data_view_layer(C);
5394
5396 scene, view_layer, CTX_wm_view3d(C));
5397 for (const int ob_index : objects.index_range()) {
5398 Object *obedit = objects[ob_index];
5400 BMIter iter;
5401 int seed_iter = seed;
5402
5403 /* This gives a consistent result regardless of object order. */
5404 if (ob_index) {
5405 seed_iter += BLI_ghashutil_strhash_p(obedit->id.name);
5406 }
5407
5408 if (em->selectmode & SCE_SELECT_VERTEX) {
5409 int elem_map_len = 0;
5410 BMVert **elem_map = static_cast<BMVert **>(
5411 MEM_mallocN(sizeof(*elem_map) * em->bm->totvert, __func__));
5412 BMVert *eve;
5413 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
5414 if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
5415 elem_map[elem_map_len++] = eve;
5416 }
5417 }
5418
5419 BLI_array_randomize(elem_map, sizeof(*elem_map), elem_map_len, seed_iter);
5420 const int count_select = elem_map_len * randfac;
5421 for (int i = 0; i < count_select; i++) {
5422 BM_vert_select_set(em->bm, elem_map[i], select);
5423 }
5424 MEM_freeN(elem_map);
5425 }
5426 else if (em->selectmode & SCE_SELECT_EDGE) {
5427 int elem_map_len = 0;
5428 BMEdge **elem_map = static_cast<BMEdge **>(
5429 MEM_mallocN(sizeof(*elem_map) * em->bm->totedge, __func__));
5430 BMEdge *eed;
5431 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
5432 if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) {
5433 elem_map[elem_map_len++] = eed;
5434 }
5435 }
5436 BLI_array_randomize(elem_map, sizeof(*elem_map), elem_map_len, seed_iter);
5437 const int count_select = elem_map_len * randfac;
5438 for (int i = 0; i < count_select; i++) {
5439 BM_edge_select_set(em->bm, elem_map[i], select);
5440 }
5441 MEM_freeN(elem_map);
5442 }
5443 else {
5444 int elem_map_len = 0;
5445 BMFace **elem_map = static_cast<BMFace **>(
5446 MEM_mallocN(sizeof(*elem_map) * em->bm->totface, __func__));
5447 BMFace *efa;
5448 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
5449 if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
5450 elem_map[elem_map_len++] = efa;
5451 }
5452 }
5453 BLI_array_randomize(elem_map, sizeof(*elem_map), elem_map_len, seed_iter);
5454 const int count_select = elem_map_len * randfac;
5455 for (int i = 0; i < count_select; i++) {
5456 BM_face_select_set(em->bm, elem_map[i], select);
5457 }
5458 MEM_freeN(elem_map);
5459 }
5460
5461 if (select) {
5462 /* Was #EDBM_select_flush_from_verts, but it over selects in edge/face mode. */
5464 }
5465 else {
5467 }
5469
5470 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
5472 }
5473
5474 return OPERATOR_FINISHED;
5475}
5476
5478{
5479 /* Identifiers. */
5480 ot->name = "Select Random";
5481 ot->description = "Randomly select vertices";
5482 ot->idname = "MESH_OT_select_random";
5483
5484 /* API callbacks */
5486 ot->poll = ED_operator_editmesh;
5487
5488 /* Flags. */
5489 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5490
5491 /* Props. */
5493}
5494
5496
5497/* -------------------------------------------------------------------- */
5500
5502{
5503 if (ED_operator_editmesh(C)) {
5504 Object *obedit = CTX_data_edit_object(C);
5506 const int cd_dvert_offset = CustomData_get_offset(&em->bm->vdata, CD_MDEFORMVERT);
5507
5508 const ListBase *defbase = BKE_object_defgroup_list(obedit);
5509 if ((em->selectmode & SCE_SELECT_VERTEX) == 0) {
5510 CTX_wm_operator_poll_msg_set(C, "Must be in vertex selection mode");
5511 }
5512 else if (BLI_listbase_is_empty(defbase) || cd_dvert_offset == -1) {
5513 CTX_wm_operator_poll_msg_set(C, "No weights/vertex groups on object");
5514 }
5515 else {
5516 return true;
5517 }
5518 }
5519 return false;
5520}
5521
5523{
5524 const bool extend = RNA_boolean_get(op->ptr, "extend");
5525 const Scene *scene = CTX_data_scene(C);
5526 ViewLayer *view_layer = CTX_data_view_layer(C);
5527
5529 scene, view_layer, CTX_wm_view3d(C));
5530
5531 for (Object *obedit : objects) {
5533
5534 const int cd_dvert_offset = CustomData_get_offset(&em->bm->vdata, CD_MDEFORMVERT);
5535
5536 if (cd_dvert_offset == -1) {
5537 continue;
5538 }
5539
5540 BMVert *eve;
5541 BMIter iter;
5542
5543 bool changed = false;
5544
5545 if (!extend) {
5546 if (em->bm->totvertsel) {
5548 changed = true;
5549 }
5550 }
5551
5552 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
5553 if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
5554 MDeformVert *dv = static_cast<MDeformVert *>(BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset));
5555 /* Skip `dv` or `dv` set with zero weight. */
5556 if (ELEM(nullptr, dv, dv->dw)) {
5557 BM_vert_select_set(em->bm, eve, true);
5558 changed = true;
5559 }
5560 }
5561 }
5562
5563 if (changed) {
5566
5567 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
5568 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
5569 }
5570 }
5571 return OPERATOR_FINISHED;
5572}
5573
5575{
5576 /* Identifiers. */
5577 ot->name = "Select Ungrouped";
5578 ot->idname = "MESH_OT_select_ungrouped";
5579 ot->description = "Select vertices without a group";
5580
5581 /* API callbacks. */
5584
5585 /* Flags. */
5586 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5587
5588 RNA_def_boolean(ot->srna, "extend", false, "Extend", "Extend the selection");
5589}
5590
5592
5593/* -------------------------------------------------------------------- */
5596
5597enum {
5601};
5602
5604{
5605 Scene *scene = CTX_data_scene(C);
5606 ViewLayer *view_layer = CTX_data_view_layer(C);
5607 Object *obedit = CTX_data_edit_object(C);
5609 BMVert *v_act = BM_mesh_active_vert_get(em->bm);
5610 const int orientation = RNA_enum_get(op->ptr, "orientation");
5611 const int axis = RNA_enum_get(op->ptr, "axis");
5612 const int sign = RNA_enum_get(op->ptr, "sign");
5613
5614 if (v_act == nullptr) {
5615 BKE_report(
5616 op->reports, RPT_WARNING, "This operator requires an active vertex (last selected)");
5617 return OPERATOR_CANCELLED;
5618 }
5619
5620 const float limit = RNA_float_get(op->ptr, "threshold");
5621
5622 float value;
5623 float axis_mat[3][3];
5624
5625 /* 3D view variables may be nullptr, (no need to check in poll function). */
5627 view_layer,
5630 obedit,
5631 obedit,
5632 orientation,
5634 axis_mat);
5635
5636 const float *axis_vector = axis_mat[axis];
5637
5638 {
5639 float vertex_world[3];
5640 mul_v3_m4v3(vertex_world, obedit->object_to_world().ptr(), v_act->co);
5641 value = dot_v3v3(axis_vector, vertex_world);
5642 }
5643
5644 if (sign == SELECT_AXIS_NEG) {
5645 value += limit;
5646 }
5647 else if (sign == SELECT_AXIS_POS) {
5648 value -= limit;
5649 }
5650
5652 scene, view_layer, CTX_wm_view3d(C));
5653 for (Object *obedit_iter : objects) {
5654 BMEditMesh *em_iter = BKE_editmesh_from_object(obedit_iter);
5655 BMesh *bm = em_iter->bm;
5656
5657 if (bm->totvert == bm->totvertsel) {
5658 continue;
5659 }
5660
5661 BMIter iter;
5662 BMVert *v;
5663 bool changed = false;
5664
5665 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
5667 float v_iter_world[3];
5668 mul_v3_m4v3(v_iter_world, obedit_iter->object_to_world().ptr(), v->co);
5669 const float value_iter = dot_v3v3(axis_vector, v_iter_world);
5670 switch (sign) {
5671 case SELECT_AXIS_ALIGN:
5672 if (fabsf(value_iter - value) < limit) {
5673 BM_vert_select_set(bm, v, true);
5674 changed = true;
5675 }
5676 break;
5677 case SELECT_AXIS_NEG:
5678 if (value_iter < value) {
5679 BM_vert_select_set(bm, v, true);
5680 changed = true;
5681 }
5682 break;
5683 case SELECT_AXIS_POS:
5684 if (value_iter > value) {
5685 BM_vert_select_set(bm, v, true);
5686 changed = true;
5687 }
5688 break;
5689 }
5690 }
5691 }
5692 if (changed) {
5693 EDBM_selectmode_flush(em_iter);
5695
5696 WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit_iter->data);
5697 DEG_id_tag_update(static_cast<ID *>(obedit_iter->data), ID_RECALC_SELECT);
5698 }
5699 }
5700 return OPERATOR_FINISHED;
5701}
5702
5704{
5705 static const EnumPropertyItem axis_sign_items[] = {
5706 {SELECT_AXIS_POS, "POS", false, "Positive Axis", ""},
5707 {SELECT_AXIS_NEG, "NEG", false, "Negative Axis", ""},
5708 {SELECT_AXIS_ALIGN, "ALIGN", false, "Aligned Axis", ""},
5709 {0, nullptr, 0, nullptr, nullptr},
5710 };
5711
5712 /* Identifiers. */
5713 ot->name = "Select Axis";
5714 ot->description = "Select all data in the mesh on a single axis";
5715 ot->idname = "MESH_OT_select_axis";
5716
5717 /* API callbacks. */
5718 ot->exec = edbm_select_axis_exec;
5719 ot->poll = ED_operator_editmesh;
5720
5721 /* Flags. */
5722 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5723
5724 /* Properties. */
5725 RNA_def_enum(ot->srna,
5726 "orientation",
5729 "Axis Mode",
5730 "Axis orientation");
5731 RNA_def_enum(ot->srna, "sign", axis_sign_items, SELECT_AXIS_POS, "Axis Sign", "Side to select");
5732 RNA_def_enum(ot->srna,
5733 "axis",
5735 0,
5736 "Axis",
5737 "Select the axis to compare each vertex on");
5739 ot->srna, "threshold", 0.0001f, 0.000001f, 50.0f, "Threshold", "", 0.00001f, 10.0f);
5740}
5741
5743
5744/* -------------------------------------------------------------------- */
5747
5749{
5750 const Scene *scene = CTX_data_scene(C);
5751 ViewLayer *view_layer = CTX_data_view_layer(C);
5753 scene, view_layer, CTX_wm_view3d(C));
5754 bool changed = false;
5755 for (Object *obedit : objects) {
5757
5758 if (em->bm->totfacesel == 0) {
5759 continue;
5760 }
5761 BMFace *f;
5762 BMEdge *e;
5763 BMIter iter;
5764
5766
5767 BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
5768 BMLoop *l1, *l2;
5769 BMIter liter1, liter2;
5770
5771 BM_ITER_ELEM (l1, &liter1, f, BM_LOOPS_OF_FACE) {
5772 int tot = 0, totsel = 0;
5773
5774 BM_ITER_ELEM (l2, &liter2, l1->e, BM_LOOPS_OF_EDGE) {
5775 tot++;
5776 totsel += BM_elem_flag_test(l2->f, BM_ELEM_SELECT) != 0;
5777 }
5778
5779 if ((tot != totsel && totsel > 0) || (totsel == 1 && tot == 1)) {
5781 }
5782 }
5783 }
5784
5786
5787 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
5789 BM_edge_select_set(em->bm, e, true);
5790 changed = true;
5791 }
5792 }
5793
5795 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
5796 }
5797
5798 if (changed) {
5799 /* If in face-only select mode, switch to edge select mode so that
5800 * an edge-only selection is not inconsistent state. Do this for all meshes in multi-object
5801 * editmode so their selectmode is in sync for following operators. */
5803 }
5804
5805 return OPERATOR_FINISHED;
5806}
5807
5809{
5810 /* Identifiers. */
5811 ot->name = "Select Boundary Loop";
5812 ot->idname = "MESH_OT_region_to_loop";
5813 ot->description = "Select boundary edges around the selected faces";
5814
5815 /* API callbacks. */
5817 ot->poll = ED_operator_editmesh;
5818
5819 /* Flags. */
5820 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
5821}
5822
5824
5825/* -------------------------------------------------------------------- */
5828
5829static int loop_find_region(BMLoop *l, int flag, GSet *visit_face_set, BMFace ***region_out)
5830{
5833
5834 stack.append(l->f);
5835 BLI_gset_insert(visit_face_set, l->f);
5836
5837 while (!stack.is_empty()) {
5838 BMIter liter1, liter2;
5839 BMLoop *l1, *l2;
5840
5841 BMFace *f = stack.pop_last();
5842 region.append(f);
5843
5844 BM_ITER_ELEM (l1, &liter1, f, BM_LOOPS_OF_FACE) {
5845 if (BM_elem_flag_test(l1->e, flag)) {
5846 continue;
5847 }
5848
5849 BM_ITER_ELEM (l2, &liter2, l1->e, BM_LOOPS_OF_EDGE) {
5850 /* Avoids finding same region twice
5851 * (otherwise) the logic works fine without. */
5852 if (BM_elem_flag_test(l2->f, BM_ELEM_TAG)) {
5853 continue;
5854 }
5855
5856 if (BLI_gset_add(visit_face_set, l2->f)) {
5857 stack.append(l2->f);
5858 }
5859 }
5860 }
5861 }
5862
5863 BMFace **region_alloc = MEM_malloc_arrayN<BMFace *>(region.size(), __func__);
5864 memcpy(region_alloc, region.data(), region.as_span().size_in_bytes());
5865 *region_out = region_alloc;
5866 return region.size();
5867}
5868
5869static int verg_radial(const void *va, const void *vb)
5870{
5871 const BMEdge *e_a = *((const BMEdge **)va);
5872 const BMEdge *e_b = *((const BMEdge **)vb);
5873
5874 const int a = BM_edge_face_count(e_a);
5875 const int b = BM_edge_face_count(e_b);
5876
5877 if (a > b) {
5878 return -1;
5879 }
5880 if (a < b) {
5881 return 1;
5882 }
5883 return 0;
5884}
5885
5892static int loop_find_regions(BMEditMesh *em, const bool selbigger)
5893{
5894 GSet *visit_face_set;
5895 BMIter iter;
5896 const int edges_len = em->bm->totedgesel;
5897 BMEdge *e;
5898 int count = 0, i;
5899
5900 visit_face_set = BLI_gset_ptr_new_ex(__func__, edges_len);
5901 BMEdge **edges = MEM_malloc_arrayN<BMEdge *>(edges_len, __func__);
5902
5903 i = 0;
5904 BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
5906 edges[i++] = e;
5908 }
5909 else {
5911 }
5912 }
5913
5914 /* Sort edges by radial cycle length. */
5915 qsort(edges, edges_len, sizeof(*edges), verg_radial);
5916
5917 for (i = 0; i < edges_len; i++) {
5918 BMIter liter;
5919 BMLoop *l;
5920 BMFace **region = nullptr, **region_out;
5921 int c, tot = 0;
5922
5923 e = edges[i];
5924
5926 continue;
5927 }
5928
5929 BM_ITER_ELEM (l, &liter, e, BM_LOOPS_OF_EDGE) {
5930 if (BLI_gset_haskey(visit_face_set, l->f)) {
5931 continue;
5932 }
5933
5934 c = loop_find_region(l, BM_ELEM_SELECT, visit_face_set, &region_out);
5935
5936 if (!region || (selbigger ? c >= tot : c < tot)) {
5937 /* This region is the best seen so far. */
5938 tot = c;
5939 if (region) {
5940 /* Free the previous best. */
5941 MEM_freeN(region);
5942 }
5943 /* Track the current region as the new best. */
5944 region = region_out;
5945 }
5946 else {
5947 /* This region is not as good as best so far, just free it. */
5948 MEM_freeN(region_out);
5949 }
5950 }
5951
5952 if (region) {
5953 int j;
5954
5955 for (j = 0; j < tot; j++) {
5956 BM_elem_flag_enable(region[j], BM_ELEM_TAG);
5957 BM_ITER_ELEM (l, &liter, region[j], BM_LOOPS_OF_FACE) {
5959 }
5960 }
5961
5962 count += tot;
5963
5964 MEM_freeN(region);
5965 }
5966 }
5967
5968 MEM_freeN(edges);
5969 BLI_gset_free(visit_face_set, nullptr);
5970
5971 return count;
5972}
5973
5975{
5976 const bool select_bigger = RNA_boolean_get(op->ptr, "select_bigger");
5977
5978 const Scene *scene = CTX_data_scene(C);
5979 ViewLayer *view_layer = CTX_data_view_layer(C);
5981 scene, view_layer, CTX_wm_view3d(C));
5982 for (Object *obedit : objects) {
5984
5985 if (em->bm->totedgesel == 0) {
5986 continue;
5987 }
5988
5989 BMIter iter;
5990 BMFace *f;
5991
5992 /* Find the set of regions with smallest number of total faces. */
5994 const int a = loop_find_regions(em, select_bigger);
5995 const int b = loop_find_regions(em, !select_bigger);
5996
5998 loop_find_regions(em, ((a <= b) != select_bigger) ? select_bigger : !select_bigger);
5999
6000 /* Unlike most operators, always de-select all. */
6001 bool changed = true;
6003
6004 BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
6006 continue;
6007 }
6009 BM_face_select_set(em->bm, f, true);
6010 }
6011 }
6012
6013 if (changed) {
6016
6017 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
6018 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
6019 }
6020 }
6021
6022 return OPERATOR_FINISHED;
6023}
6024
6026{
6027 /* Identifiers. */
6028 ot->name = "Select Loop Inner-Region";
6029 ot->idname = "MESH_OT_loop_to_region";
6030 ot->description = "Select region of faces inside of a selected loop of edges";
6031
6032 /* API callbacks. */
6034 ot->poll = ED_operator_editmesh;
6035
6036 /* Flags. */
6037 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
6038
6039 RNA_def_boolean(ot->srna,
6040 "select_bigger",
6041 false,
6042 "Select Bigger",
6043 "Select bigger regions instead of smaller ones");
6044}
6045
6047{
6048 using namespace blender;
6049 if (!ED_operator_editmesh(C)) {
6050 return false;
6051 }
6052 Object *obedit = CTX_data_edit_object(C);
6053 const Mesh *mesh = static_cast<const Mesh *>(obedit->data);
6054 AttributeOwner owner = AttributeOwner::from_id(&const_cast<ID &>(mesh->id));
6055 const std::optional<StringRef> name = BKE_attributes_active_name_get(owner);
6056 if (!name) {
6057 CTX_wm_operator_poll_msg_set(C, "There must be an active attribute");
6058 return false;
6059 }
6062 if (layer->type != CD_PROP_BOOL) {
6063 CTX_wm_operator_poll_msg_set(C, "The active attribute must have a boolean type");
6064 return false;
6065 }
6066 if (BKE_attribute_domain(owner, layer) == bke::AttrDomain::Corner) {
6068 C, "The active attribute must be on the vertex, edge, or face domain");
6069 return false;
6070 }
6071 return true;
6072}
6073
6074static std::optional<BMIterType> domain_to_iter_type(const blender::bke::AttrDomain domain)
6075{
6076 using namespace blender;
6077 switch (domain) {
6079 return BM_VERTS_OF_MESH;
6081 return BM_EDGES_OF_MESH;
6083 return BM_FACES_OF_MESH;
6084 default:
6085 return std::nullopt;
6086 }
6087}
6088
6090{
6091 using namespace blender;
6092 const Scene *scene = CTX_data_scene(C);
6093 ViewLayer *view_layer = CTX_data_view_layer(C);
6095 scene, view_layer, CTX_wm_view3d(C));
6096 for (Object *obedit : objects) {
6097 Mesh *mesh = static_cast<Mesh *>(obedit->data);
6099 BMesh *bm = em->bm;
6101 const std::optional<StringRef> name = BKE_attributes_active_name_get(owner);
6102 if (!name) {
6103 continue;
6104 }
6107 if (!layer) {
6108 continue;
6109 }
6110 if (layer->type != CD_PROP_BOOL) {
6111 continue;
6112 }
6113 if (BKE_attribute_domain(owner, layer) == bke::AttrDomain::Corner) {
6114 continue;
6115 }
6116 const std::optional<BMIterType> iter_type = domain_to_iter_type(
6117 BKE_attribute_domain(owner, layer));
6118 if (!iter_type) {
6119 continue;
6120 }
6121
6122 bool changed = false;
6123 BMElem *elem;
6124 BMIter iter;
6125 BM_ITER_MESH (elem, &iter, bm, *iter_type) {
6127 continue;
6128 }
6129 if (BM_ELEM_CD_GET_BOOL(elem, layer->offset)) {
6130 BM_elem_select_set(bm, elem, true);
6131 changed = true;
6132 }
6133 }
6134
6135 if (changed) {
6138
6139 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
6140 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
6141 }
6142 }
6143
6144 return OPERATOR_FINISHED;
6145}
6146
6148{
6149 ot->name = "Select by Attribute";
6150 ot->idname = "MESH_OT_select_by_attribute";
6151 ot->description = "Select elements based on the active boolean attribute";
6152
6155
6156 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
6157}
6158
std::optional< blender::StringRefNull > BKE_attributes_active_name_get(AttributeOwner &owner)
Definition attribute.cc:784
blender::bke::AttrDomain BKE_attribute_domain(const AttributeOwner &owner, const struct CustomDataLayer *layer)
@ ATTR_DOMAIN_MASK_ALL
const struct CustomDataLayer * BKE_attribute_search(const AttributeOwner &owner, blender::StringRef name, eCustomDataMask type, AttrDomainMask domain_mask)
Definition attribute.cc:639
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:585
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:61
void BKE_view_layer_synced_ensure(const Scene *scene, ViewLayer *view_layer)
blender::Vector< Base * > BKE_view_layer_array_from_bases_in_edit_mode(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
Base * BKE_view_layer_active_base_get(ViewLayer *view_layer)
blender::Vector< Object * > BKE_view_layer_array_from_objects_in_edit_mode(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
Object * BKE_view_layer_active_object_get(const ViewLayer *view_layer)
Base * BKE_view_layer_base_find(ViewLayer *view_layer, Object *ob)
blender::Vector< Base * > BKE_view_layer_array_from_bases_in_edit_mode_unique_data(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
blender::Vector< Object * > BKE_view_layer_array_from_objects_in_edit_mode_unique_data(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
blender::Span< blender::float3 > BKE_mesh_wrapper_vert_coords(const Mesh *mesh)
int BKE_mesh_wrapper_vert_len(const Mesh *mesh)
General operations, lookup, etc. for blender objects.
const Mesh * BKE_object_get_editmesh_eval_cage(const Object *object)
@ RPT_ERROR
Definition BKE_report.hh:39
@ RPT_WARNING
Definition BKE_report.hh:38
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
struct GSet GSet
Definition BLI_ghash.h:337
bool BLI_gset_haskey(const GSet *gs, const void *key) ATTR_WARN_UNUSED_RESULT
unsigned int BLI_ghashutil_strhash_p(const void *ptr)
void BLI_gset_insert(GSet *gs, void *key)
Definition BLI_ghash.cc:959
void BLI_gset_free(GSet *gs, GSetKeyFreeFP keyfreefp)
GSet * BLI_gset_ptr_new_ex(const char *info, unsigned int nentries_reserve) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
bool BLI_gset_add(GSet *gs, void *key)
Definition BLI_ghash.cc:966
A min-heap / priority queue ADT.
HeapNode * BLI_heap_top(const Heap *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition BLI_heap.cc:279
void BLI_heap_free(Heap *heap, HeapFreeFP ptrfreefp) ATTR_NONNULL(1)
Definition BLI_heap.cc:191
void void float BLI_heap_node_value(const HeapNode *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition BLI_heap.cc:347
Heap * BLI_heap_new_ex(unsigned int reserve_num) ATTR_WARN_UNUSED_RESULT
Definition BLI_heap.cc:171
void void bool BLI_heap_is_empty(const Heap *heap) ATTR_NONNULL(1)
Definition BLI_heap.cc:269
void BLI_heap_node_value_update(Heap *heap, HeapNode *node, float value) ATTR_NONNULL(1
void * BLI_heap_pop_min(Heap *heap) ATTR_NONNULL(1)
Definition BLI_heap.cc:291
void * BLI_heap_node_ptr(const HeapNode *heap) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition BLI_heap.cc:352
HeapNode * BLI_heap_insert(Heap *heap, float value, void *ptr) ATTR_NONNULL(1)
Definition BLI_heap.cc:234
void void BLI_heap_remove(Heap *heap, HeapNode *node) ATTR_NONNULL(1
void void void BLI_movelisttolist(ListBase *dst, ListBase *src) ATTR_NONNULL(1
#define LISTBASE_FOREACH(type, var, list)
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
void BLI_freelinkN(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:270
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
void * BLI_pophead(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:252
MINLINE float min_ff(float a, float b)
MINLINE unsigned short highest_order_bit_s(unsigned short n)
MINLINE int bitscan_forward_i(int a)
#define DEG2RADF(_deg)
float line_point_factor_v2(const float p[2], const float l1[2], const float l2[2])
float dist_squared_ray_to_seg_v3(const float ray_origin[3], const float ray_direction[3], const float v0[3], const float v1[3], float r_point[3], float *r_depth)
Definition math_geom.cc:613
float dist_squared_to_ray_v3_normalized(const float ray_origin[3], const float ray_direction[3], const float co[3])
Definition math_geom.cc:597
void copy_m3_m4(float m1[3][3], const float m2[4][4])
void mul_m4_v3(const float M[4][4], float r[3])
void mul_v3_m4v3(float r[3], const float mat[4][4], const float vec[3])
bool invert_m3(float mat[3][3])
MINLINE float len_squared_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
void interp_v2_v2v2(float r[2], const float a[2], const float b[2], float t)
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], float t)
void mid_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE float len_manhattan_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
void mid_v3_v3v3(float r[3], const float a[3], const float b[3])
float angle_normalized_v3v3(const float v1[3], const float v2[3]) ATTR_WARN_UNUSED_RESULT
Random number functions.
void BLI_array_randomize(void *data, unsigned int elem_size, unsigned int elem_num, unsigned int seed)
Definition rand.cc:156
unsigned int uint
#define UNUSED_FUNCTION(x)
#define UNUSED_VARS(...)
#define POINTER_FROM_INT(i)
#define POINTER_AS_INT(i)
#define ELEM(...)
#define STACK_CLEAR(stack)
#define STACK_PUSH(stack, val)
#define STACK_DECLARE(stack)
#define STACK_SIZE(stack)
#define STACK_INIT(stack, stack_num)
#define TIP_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
@ ID_RECALC_SELECT
Definition DNA_ID.h:1101
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1118
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
#define CD_MASK_PROP_ALL
@ CD_MDEFORMVERT
@ CD_PROP_FLOAT2
@ ME_EDIT_MIRROR_TOPO
@ OB_MODE_EDIT
Object is a sort of wrapper for general info.
@ OB_MESH
@ UV_STICKY_LOCATION
@ UV_FLAG_SELECT_SYNC
@ SCE_SELECT_FACE
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
@ RV3D_CLIPPING
#define RV3D_CLIPPING_ENABLED(v3d, rv3d)
@ V3D_AROUND_ACTIVE
@ V3D_ORIENT_LOCAL
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_PASS_THROUGH
void DRW_select_buffer_context_create(Depsgraph *depsgraph, blender::Span< Base * > bases, short select_mode)
uint DRW_select_buffer_sample_point(Depsgraph *depsgraph, ARegion *region, View3D *v3d, const int center[2])
bool DRW_select_buffer_elem_get(uint sel_id, uint &r_elem, uint &r_base_index, char &r_elem_type)
uint DRW_select_buffer_find_nearest_to_point(Depsgraph *depsgraph, ARegion *region, View3D *v3d, const int center[2], uint id_min, uint id_max, uint *dist)
void EDBM_flag_disable_all(BMEditMesh *em, char hflag)
void EDBM_select_flush_from_verts(BMEditMesh *em, bool select)
void EDBM_update(Mesh *mesh, const EDBMUpdate_Params *params)
bool EDBM_uvselect_clear(BMEditMesh *em)
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_selectmode_flush(BMEditMesh *em)
void EDBM_selectmode_flush_ex(BMEditMesh *em, short selectmode)
void ED_mesh_report_mirror_ex(ReportList &reports, int totmirr, int totfail, char 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)
bool ED_operator_editmesh_region_view3d(bContext *C)
bool ED_operator_editmesh(bContext *C)
@ SEL_OP_ADD
@ SEL_OP_SUB
@ SEL_OP_SET
@ SEL_OP_AND
@ SEL_OP_XOR
@ SEL_SELECT
@ SEL_INVERT
@ SEL_DESELECT
@ SEL_TOGGLE
void ED_uvedit_sync_uvselect_ensure_if_needed(const ToolSettings *ts, BMesh *bm)
void ED_uvedit_deselect_all(const Scene *scene, Object *obedit, int action)
bool ED_view3d_win_to_ray_clipped(Depsgraph *depsgraph, const ARegion *region, const View3D *v3d, const float mval[2], float r_ray_start[3], float r_ray_normal[3], bool do_clip_planes)
float ED_view3d_select_dist_px()
#define V3D_PROJ_TEST_CLIP_CONTENT_DEFAULT
Definition ED_view3d.hh:315
void mesh_foreachScreenVert(const ViewContext *vc, void(*func)(void *user_data, BMVert *eve, const float screen_co[2], int index), void *user_data, eV3DProjTest clip_flag)
eV3DProjTest
Definition ED_view3d.hh:278
@ V3D_PROJ_TEST_CLIP_NEAR
Definition ED_view3d.hh:282
@ V3D_PROJ_TEST_CLIP_BB
Definition ED_view3d.hh:280
eV3DProjStatus ED_view3d_project_float_object(const ARegion *region, const float co[3], float r_co[2], eV3DProjTest flag)
void ED_view3d_init_mats_rv3d(const Object *ob, RegionView3D *rv3d)
@ V3D_PROJ_RET_OK
Definition ED_view3d.hh:256
void mesh_foreachScreenEdge(const ViewContext *vc, void(*func)(void *user_data, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int index), void *user_data, eV3DProjTest clip_flag)
ViewContext ED_view3d_viewcontext_init(bContext *C, Depsgraph *depsgraph)
int ED_view3d_backbuf_sample_size_clamp(ARegion *region, float dist)
void mesh_foreachScreenFace(const ViewContext *vc, void(*func)(void *user_data, BMFace *efa, const float screen_co[2], int index), void *user_data, eV3DProjTest clip_flag)
#define V3D_PROJ_TEST_CLIP_DEFAULT
Definition ED_view3d.hh:309
#define XRAY_FLAG_ENABLED(v3d)
void view3d_operator_needs_gpu(const bContext *C)
bool ED_view3d_clipping_test(const RegionView3D *rv3d, const float co[3], bool is_local)
void ED_view3d_viewcontext_init_object(ViewContext *vc, Object *obact)
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
Read Guarded memory(de)allocation.
@ PROP_SKIP_SAVE
Definition RNA_types.hh:344
@ PROP_HIDDEN
Definition RNA_types.hh:338
#define C
Definition RandGen.cpp:29
#define NC_GEOM
Definition WM_types.hh:393
@ KM_CTRL
Definition WM_types.hh:279
@ KM_SHIFT
Definition WM_types.hh:278
#define ND_DATA
Definition WM_types.hh:509
#define NC_SCENE
Definition WM_types.hh:378
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_TOOLSETTINGS
Definition WM_types.hh:449
#define ND_SELECT
Definition WM_types.hh:508
#define BM_ELEM_CD_GET_BOOL(ele, offset)
#define BM_FACE_FIRST_LOOP(p)
@ BM_ELEM_HIDDEN
@ BM_ELEM_SEAM
@ BM_ELEM_SELECT
@ BM_ELEM_SMOOTH
@ BM_ELEM_TAG
@ BM_LOOP
#define BM_ELEM_CD_GET_VOID_P(ele, offset)
#define BM_elem_index_get(ele)
#define BM_elem_flag_disable(ele, hflag)
#define BM_elem_flag_set(ele, hflag, val)
#define BM_elem_index_set(ele, index)
#define BM_elem_flag_test(ele, hflag)
#define BM_elem_flag_enable(ele, hflag)
#define BM_ITER_ELEM(ele, iter, data, itype)
#define BM_ITER_MESH(ele, iter, bm, itype)
#define BM_ITER_MESH_INDEX(ele, iter, bm, itype, indexvar)
@ BM_LOOPS_OF_LOOP
@ BM_FACES_OF_EDGE
@ BM_FACES_OF_VERT
@ BM_EDGES_OF_MESH
@ BM_VERTS_OF_MESH
@ BM_VERTS_OF_EDGE
@ BM_FACES_OF_MESH
@ BM_LOOPS_OF_EDGE
@ BM_EDGES_OF_VERT
@ BM_LOOPS_OF_FACE
BMesh const char void * data
BMesh * bm
BMVert * BM_mesh_active_vert_get(BMesh *bm)
void BM_face_select_set(BMesh *bm, BMFace *f, const bool select)
Select Face.
void BM_elem_select_set(BMesh *bm, BMElem *ele, const bool select)
BMFace * BM_mesh_active_face_get(BMesh *bm, const bool is_sloppy, const bool is_selected)
BMElem * BM_mesh_active_elem_get(BMesh *bm)
void BM_vert_select_set(BMesh *bm, BMVert *v, const bool select)
Select Vert.
void BM_edge_select_set(BMesh *bm, BMEdge *e, const bool select)
Select Edge.
void BM_mesh_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)
bool BM_mesh_select_is_mixed(const BMesh *bm)
void BM_mesh_select_flush_from_verts(BMesh *bm, const bool select)
#define BM_select_history_store(bm, ele)
#define BM_select_history_remove(bm, ele)
void BM_mesh_elem_toolflags_clear(BMesh *bm)
void BM_mesh_elem_toolflags_ensure(BMesh *bm)
Definition bmesh_mesh.cc:82
BMVert * BM_vert_at_index_find_or_table(BMesh *bm, const int index)
void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
BMEdge * BM_edge_at_index_find_or_table(BMesh *bm, const int index)
BMFace * BM_face_at_index_find_or_table(BMesh *bm, const int index)
BLI_INLINE BMFace * BM_face_at_index(BMesh *bm, const int index)
#define BM_FACE
#define BM_EDGE
#define BM_VERT
#define BMO_edge_flag_test(bm, e, oflag)
void BMO_pop(BMesh *bm)
BMESH OPSTACK POP.
#define BMO_edge_flag_set(bm, e, oflag, val)
void BMO_push(BMesh *bm, BMOperator *op)
BMESH OPSTACK PUSH.
#define BMO_elem_flag_enable(bm, ele, oflag)
@ BMO_DELIM_NORMAL
@ BMO_DELIM_MATERIAL
@ BMO_DELIM_SEAM
@ BMO_DELIM_SHARP
@ BMO_DELIM_UV
void BM_face_calc_center_median_vcos(const BMesh *bm, const BMFace *f, float r_cent[3], const blender::Span< blender::float3 > vert_positions)
float BM_face_calc_area(const BMFace *f)
void BM_face_calc_center_median(const BMFace *f, float r_cent[3])
bool BM_edge_is_contiguous_loop_cd(const BMEdge *e, const int cd_loop_type, const int cd_loop_offset)
bool BM_edge_is_all_face_flag_test(const BMEdge *e, const char hflag, const bool respect_hide)
int BM_vert_edge_count_at_most(const BMVert *v, const int count_max)
int BM_mesh_calc_face_groups(BMesh *bm, int *r_groups_array, int(**r_group_index)[2], BMLoopFilterFunc filter_fn, BMLoopPairFilterFunc filter_pair_fn, void *user_data, const char hflag_test, const char htype_step)
bool BM_face_is_any_edge_flag_test(const BMFace *f, const char hflag)
bool BM_face_is_any_vert_flag_test(const BMFace *f, const char hflag)
bool BM_edge_loop_pair(BMEdge *e, BMLoop **r_la, BMLoop **r_lb)
bool BM_vert_is_all_edge_flag_test(const BMVert *v, const char hflag, const bool respect_hide)
bool BM_vert_is_manifold(const BMVert *v)
int BM_edge_face_count(const BMEdge *e)
bool BM_edge_is_any_vert_flag_test(const BMEdge *e, const char hflag)
float BM_edge_calc_length(const BMEdge *e)
bool BM_vert_is_all_face_flag_test(const BMVert *v, const char hflag, const bool respect_hide)
bool BM_edge_is_any_face_flag_test(const BMEdge *e, const char hflag)
BLI_INLINE bool BM_edge_is_contiguous(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE bool BM_edge_is_manifold(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE bool BM_edge_is_boundary(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
#define BM_edge_face_count_is_over(e, n)
BLI_INLINE BMVert * BM_edge_other_vert(BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE bool BM_edge_is_wire(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMLoop * l_b
ATTR_WARN_UNUSED_RESULT const BMVert * v
int BM_mesh_region_match(BMesh *bm, BMFace **faces_region, uint faces_region_len, ListBase *r_face_regions)
void BM_face_uvselect_set_pick(BMesh *bm, BMFace *f, bool select, const BMUVSelectPickParams &params)
void BM_mesh_uvselect_mode_flush_update(BMesh *bm, const short selectmode_old, const short selectmode_new, const int cd_loop_uv_offset)
void BM_edge_uvselect_set_pick(BMesh *bm, BMEdge *e, bool select, const BMUVSelectPickParams &params)
void BM_vert_uvselect_set_pick(BMesh *bm, BMVert *v, bool select, const BMUVSelectPickParams &params)
void * BMW_begin(BMWalker *walker, void *start)
void BMW_init(BMWalker *walker, BMesh *bm, int type, short mask_vert, short mask_edge, short mask_face, BMWFlag flag, int layer)
Initialize Walker.
void BMW_end(BMWalker *walker)
End Walker.
void * BMW_step(BMWalker *walker)
Step Walker.
int BMW_current_depth(BMWalker *walker)
Walker Current Depth.
@ BMW_BREADTH_FIRST
#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)
BPy_StructRNA * depsgraph
static unsigned long seed
Definition btSoftBody.h:39
static AttributeOwner from_id(ID *id)
Definition attribute.cc:44
static std::optional< EditMeshSymmetryHelper > create_if_needed(Object *ob, uchar htype)
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr bool is_empty() const
Definition BLI_span.hh:260
int64_t size() const
void append(const T &value)
bool is_empty() const
IndexRange index_range() const
void reserve(const int64_t min_capacity)
Span< T > as_span() const
nullptr float
#define BMO_ELE_TAG
static wmOperatorStatus edbm_select_loose_exec(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_select_similar_region_exec(bContext *C, wmOperator *op)
static void walker_select_count(BMEditMesh *em, int walkercode, void *start, int r_count_by_select[2])
BMVert * EDBM_vert_find_nearest_ex(ViewContext *vc, float *dist_px_manhattan_p, const bool use_select_bias, bool use_cycle, const Span< Base * > bases, uint *r_base_index)
void MESH_OT_select_less(wmOperatorType *ot)
bool EDBM_selectmode_toggle_multi(bContext *C, const short selectmode_toggle, const int action, const bool use_extend, const bool use_expand)
bool EDBM_unified_findnearest_from_raycast(ViewContext *vc, const Span< Base * > bases, bool use_boundary_vertices, bool use_boundary_edges, int *r_base_index_vert, int *r_base_index_edge, int *r_base_index_face, BMVert **r_eve, BMEdge **r_eed, BMFace **r_efa)
static BMFace * bm_step_over_shared_edge_to_next_selected_face_in_chain(BMFace *f_curr, BMFace *f_prev)
static void findnearestface__doClosest(void *user_data, BMFace *efa, const float screen_co[2], int index)
static void walker_deselect_nth(BMEditMesh *em, const CheckerIntervalParams *op_params, BMHeader *h_act)
static wmOperatorStatus edbm_select_by_attribute_exec(bContext *C, wmOperator *)
static void find_nearest_edge__doClosest(void *user_data, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int index)
static float bm_interior_face_group_calc_cost(ListBase *ls, const float *edge_lengths)
static wmOperatorStatus edbm_select_linked_pick_exec(bContext *C, wmOperator *op)
BMEdge * EDBM_edge_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
BMVert * EDBM_vert_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
static bool walker_select(BMEditMesh *em, int walkercode, void *start, const bool select)
static wmOperatorStatus edbm_select_mode_exec(bContext *C, wmOperator *op)
void MESH_OT_loop_to_region(wmOperatorType *ot)
static void walker_deselect_nth_edge_chain(BMEditMesh *em, const CheckerIntervalParams *op_params, BMEdge *e_start)
void MESH_OT_select_mode(wmOperatorType *ot)
void EDBM_select_toggle_all(BMEditMesh *em)
static bool edbm_deselect_nth(BMEditMesh *em, const CheckerIntervalParams *op_params)
static void select_linked_delimit_end(BMEditMesh *em)
void MESH_OT_select_similar_region(wmOperatorType *ot)
static void find_nearest_face_center__doZBuf(void *user_data, BMFace *efa, const float screen_co[2], int)
#define FIND_NEAR_SELECT_BIAS
static wmOperatorStatus edbm_select_by_pole_count_exec(bContext *C, wmOperator *op)
static bool unified_findnearest(ViewContext *vc, const Span< Base * > bases, int *r_base_index, BMVert **r_eve, BMEdge **r_eed, BMFace **r_efa)
BMEdge * EDBM_edge_find_nearest_ex(ViewContext *vc, float *dist_px_manhattan_p, float *r_dist_center_px_manhattan, const bool use_select_bias, bool use_cycle, BMEdge **r_eed_zbuf, const Span< Base * > bases, uint *r_base_index)
static wmOperatorStatus edbm_select_less_exec(bContext *C, wmOperator *op)
BMFace * EDBM_face_find_nearest_ex(ViewContext *vc, float *dist_px_manhattan_p, float *r_dist_center, const bool use_zbuf_single_px, const bool use_select_bias, bool use_cycle, BMFace **r_efa_zbuf, const Span< Base * > bases, uint *r_base_index)
@ SELECT_AXIS_ALIGN
@ SELECT_AXIS_POS
@ SELECT_AXIS_NEG
void MESH_OT_select_linked_pick(wmOperatorType *ot)
static bool edbm_select_by_attribute_poll(bContext *C)
eElemCountType
@ ELEM_COUNT_EQUAL
@ ELEM_COUNT_NOT_EQUAL
@ ELEM_COUNT_GREATER
@ ELEM_COUNT_LESS
static bool is_count_a_match(const eElemCountType type, const int value_test, const int value_reference)
static int loop_find_region(BMLoop *l, int flag, GSet *visit_face_set, BMFace ***region_out)
void EDBM_select_swap(BMEditMesh *em)
static void mouse_mesh_loop_edge_ring(BMEditMesh *em, BMEdge *eed, bool select, bool select_clear)
static void walker_deselect_nth_face_chain(BMEditMesh *em, const CheckerIntervalParams *op_params, BMFace *f_start)
static wmOperatorStatus edbm_region_to_loop_exec(bContext *C, wmOperator *)
static void mouse_mesh_loop_face(BMEditMesh *em, BMEdge *eed, bool select, bool select_clear)
BMFace * EDBM_face_find_nearest(ViewContext *vc, float *dist_px_manhattan_p)
static bool mouse_mesh_loop(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle, bool ring)
static wmOperatorStatus edbm_select_linked_flat_faces_exec(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void MESH_OT_select_random(wmOperatorType *ot)
static int verg_radial(const void *va, const void *vb)
static void find_nearest_edge_center__doZBuf(void *user_data, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int)
static int loop_find_regions(BMEditMesh *em, const bool selbigger)
static wmOperatorStatus edbm_select_more_exec(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_select_all_exec(bContext *C, wmOperator *op)
static BMVert * bm_step_to_next_selected_vert_in_chain(BMVert *v_curr, BMVert *v_prev)
void EDBM_select_mirrored(BMEditMesh *em, const Mesh *mesh, const int axis, const bool extend, int *r_totmirr, int *r_totfail)
void MESH_OT_select_nth(wmOperatorType *ot)
static wmOperatorStatus edbm_select_non_manifold_exec(bContext *C, wmOperator *op)
void MESH_OT_loop_multi_select(wmOperatorType *ot)
ViewContext em_setup_viewcontext(bContext *C)
void EDBM_selectmode_convert(BMEditMesh *em, const short selectmode_old, const short selectmode_new)
static bool bm_interior_loop_filter_fn(const BMLoop *l, void *)
static wmOperatorStatus edbm_select_axis_exec(bContext *C, wmOperator *op)
static void select_linked_delimit_validate(BMesh *bm, int *delimit)
static wmOperatorStatus edbm_faces_select_interior_exec(bContext *C, wmOperator *)
bool EDBM_unified_findnearest(ViewContext *vc, const Span< Base * > bases, int *r_base_index, BMVert **r_eve, BMEdge **r_eed, BMFace **r_efa)
static bool UNUSED_FUNCTION EDBM_select_mirrored_extend_all(Object *obedit, BMEditMesh *em)
static bool edbm_selectmode_sync_multi_ex(Span< Object * > objects)
void MESH_OT_select_loose(wmOperatorType *ot)
static bool select_linked_delimit_test(BMEdge *e, int delimit, const DelimitData *delimit_data)
bool EDBM_deselect_by_material(BMEditMesh *em, const short index, const bool select)
void MESH_OT_select_by_pole_count(wmOperatorType *ot)
void MESH_OT_select_by_attribute(wmOperatorType *ot)
static bool edbm_vert_or_edge_select_mode_poll(bContext *C)
void MESH_OT_edgering_select(wmOperatorType *ot)
static bool edbm_select_ungrouped_poll(bContext *C)
static void walker_deselect_nth_vertex_chain(BMEditMesh *em, const CheckerIntervalParams *op_params, BMVert *v_start)
static void edbm_select_linked_pick_ex(BMEditMesh *em, BMElem *ele, bool sel, int delimit)
void MESH_OT_select_face_by_sides(wmOperatorType *ot)
void MESH_OT_select_axis(wmOperatorType *ot)
static wmOperatorStatus edbm_select_face_by_sides_exec(bContext *C, wmOperator *op)
static wmOperatorStatus edbm_loop_multiselect_exec(bContext *C, wmOperator *op)
void MESH_OT_select_mirror(wmOperatorType *ot)
static wmOperatorStatus edbm_select_sharp_edges_exec(bContext *C, wmOperator *op)
static BMEdge * bm_step_over_vert_to_next_selected_edge_in_chain(const BMEdge *e_curr, BMVert *v)
static wmOperatorStatus edbm_select_mirror_exec(bContext *C, wmOperator *op)
static bool bm_verts_form_cyclic_chain(BMVert *v_start)
static void deselect_nth_active(BMEditMesh *em, BMVert **r_eve, BMEdge **r_eed, BMFace **r_efa)
void MESH_OT_faces_select_linked_flat(wmOperatorType *ot)
void MESH_OT_edges_select_sharp(wmOperatorType *ot)
static void mouse_mesh_loop_edge(BMEditMesh *em, BMEdge *eed, bool select, bool select_clear, bool select_cycle)
static int select_linked_delimit_default_from_op(wmOperator *op, const int select_mode)
bool EDBM_selectmode_disable_multi_ex(Scene *scene, const Span< Base * > bases, const short selectmode_disable, const short selectmode_fallback)
static wmOperatorStatus edbm_select_loop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
bool EDBM_mesh_deselect_all_multi(bContext *C)
void EDBM_selectmode_set(BMEditMesh *em, const short selectmode)
static wmOperatorStatus edbm_select_random_exec(bContext *C, wmOperator *op)
static bool bm_edge_is_select_isolated(BMEdge *e)
void MESH_OT_select_interior_faces(wmOperatorType *ot)
bool EDBM_selectmode_disable_multi(bContext *C, const short selectmode_disable, const short selectmode_fallback)
void MESH_OT_select_linked(wmOperatorType *ot)
static wmOperatorStatus edbm_select_linked_exec(bContext *C, wmOperator *op)
static void select_linked_delimit_begin(BMesh *bm, int delimit)
static bool bm_edges_form_cyclic_chain(BMEdge *e_start)
static bool bm_interior_edge_is_manifold_except_face_index(BMEdge *e, int face_index, BMLoop *r_l_pair[2])
static void findnearestvert__doClosest(void *user_data, BMVert *eve, const float screen_co[2], int index)
void MESH_OT_select_all(wmOperatorType *ot)
void MESH_OT_select_more(wmOperatorType *ot)
#define FIND_NEAR_CYCLE_THRESHOLD_MIN
static std::string edbm_select_mode_get_description(bContext *, wmOperatorType *, PointerRNA *ptr)
bool EDBM_select_interior_faces(BMEditMesh *em)
void MESH_OT_region_to_loop(wmOperatorType *ot)
static void edbm_strip_selections(BMEditMesh *em)
void MESH_OT_loop_select(wmOperatorType *ot)
static wmOperatorStatus edbm_loop_to_region_exec(bContext *C, wmOperator *op)
static const EnumPropertyItem elem_count_compare_items[]
bool EDBM_selectmode_set_multi(bContext *C, const short selectmode)
static BMElem * edbm_select_id_bm_elem_get(const Span< Base * > bases, const uint sel_id, uint &r_base_index)
static bool bm_faces_form_cyclic_chain(BMFace *f_start)
static wmOperatorStatus edbm_select_nth_exec(bContext *C, wmOperator *op)
void MESH_OT_select_ungrouped(wmOperatorType *ot)
static wmOperatorStatus edbm_select_mode_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static std::optional< BMIterType > domain_to_iter_type(const blender::bke::AttrDomain domain)
bool EDBM_selectmode_disable(Scene *scene, BMEditMesh *em, const short selectmode_disable, const short selectmode_fallback)
bool EDBM_mesh_deselect_all_multi_ex(const Span< Base * > bases)
static wmOperatorStatus edbm_select_ungrouped_exec(bContext *C, wmOperator *op)
bool EDBM_select_pick(bContext *C, const int mval[2], const SelectPick_Params &params)
bool EDBM_selectmode_set_multi_ex(Scene *scene, Span< Object * > objects, const short selectmode)
void MESH_OT_select_non_manifold(wmOperatorType *ot)
BMElem * EDBM_elem_from_selectmode(BMEditMesh *em, BMVert *eve, BMEdge *eed, BMFace *efa)
BMElem * EDBM_elem_from_index_any_multi(const Scene *scene, ViewLayer *view_layer, uint object_index, uint elem_index, Object **r_obedit)
int EDBM_elem_to_index_any_multi(const Scene *scene, ViewLayer *view_layer, BMEditMesh *em, BMElem *ele, int *r_object_index)
#define printf(...)
constexpr T sign(T) RET
#define select(A, B, C)
#define UINT_MAX
Definition hash_md5.cc:44
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
int count
void * MEM_mallocN(size_t len, const char *str)
Definition mallocn.cc:128
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static char faces[256]
void base_activate(bContext *C, Base *base)
bool material_active_index_set(Object *ob, int index)
short calc_orientation_from_type_ex(const Scene *scene, ViewLayer *view_layer, const View3D *v3d, const RegionView3D *rv3d, Object *ob, Object *obedit, short orientation_index, int pivot_point, float r_mat[3][3])
VecBase< float, 3 > float3
const char * name
return ret
#define fabsf
#define cosf
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
void RNA_property_enum_set(PointerRNA *ptr, PropertyRNA *prop, int value)
int RNA_int_get(PointerRNA *ptr, const char *name)
float RNA_float_get(PointerRNA *ptr, const char *name)
int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop)
bool RNA_struct_property_is_set(PointerRNA *ptr, const char *identifier)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
void RNA_def_property_float_default(PropertyRNA *prop, float value)
PropertyRNA * RNA_def_float_rotation(StructOrFunctionRNA *cont_, const char *identifier, const int len, const float *default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_enum_flag(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
const EnumPropertyItem rna_enum_mesh_delimit_mode_items[]
Definition rna_mesh.cc:26
const EnumPropertyItem rna_enum_axis_flag_xyz_items[]
const EnumPropertyItem rna_enum_axis_xyz_items[]
const EnumPropertyItem rna_enum_mesh_select_mode_items[]
Definition rna_scene.cc:124
const EnumPropertyItem rna_enum_transform_orientation_items[]
Definition rna_scene.cc:605
#define FLT_MAX
Definition stdcycles.h:14
BMVert * v1
BMVert * v2
short selectmode
struct BMEditSelection * next
BMHeader head
short mat_nr
BMHeader head
float no[3]
struct BMEdge * e
struct BMLoop * radial_next
struct BMLoop * prev
struct BMFace * f
struct BMLoop * next
float co[3]
struct BMEdge * e
BMWOrder order
int totvert
bool uv_select_sync_valid
int totfacesel
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:414
char name[258]
Definition DNA_ID.h:432
void * first
struct MDeformWeight * dw
char editflag
NearestEdgeUserData_Hit hit
NearestEdgeUserData_Hit hit_cycle
NearestFaceUserData_Hit hit
NearestFaceUserData_Hit hit_cycle
NearestVertUserData_Hit hit_cycle
NearestVertUserData_Hit hit
struct ToolSettings * toolsettings
RegionView3D * rv3d
Definition ED_view3d.hh:80
ARegion * region
Definition ED_view3d.hh:77
int mval[2]
Definition ED_view3d.hh:82
Scene * scene
Definition ED_view3d.hh:73
BMEditMesh * em
Definition ED_view3d.hh:81
ViewLayer * view_layer
Definition ED_view3d.hh:74
View3D * v3d
Definition ED_view3d.hh:78
Object * obact
Definition ED_view3d.hh:75
Object * obedit
Definition ED_view3d.hh:76
Depsgraph * depsgraph
Definition ED_view3d.hh:72
wmEventModifierFlag modifier
Definition WM_types.hh:774
int mval[2]
Definition WM_types.hh:763
struct ReportList * reports
struct PointerRNA * ptr
i
Definition text_draw.cc:230
bool WM_cursor_test_motion_and_update(const int mval[2])
void WM_main_add_notifier(uint type, void *reference)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
PointerRNA * ptr
Definition wm_files.cc:4238
wmOperatorType * ot
Definition wm_files.cc:4237
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:145