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