Blender V4.5
view3d_select.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2008 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cfloat>
10#include <cstring>
11#include <optional>
12
13#include "DNA_action_types.h"
14#include "DNA_armature_types.h"
15#include "DNA_curve_types.h"
16#include "DNA_mesh_types.h"
17#include "DNA_meshdata_types.h"
18#include "DNA_meta_types.h"
19#include "DNA_object_types.h"
21#include "DNA_scene_types.h"
22#include "DNA_tracking_types.h"
23
24#include "MEM_guardedalloc.h"
25
26#include "BLI_bitmap.h"
27#include "BLI_function_ref.hh"
28#include "BLI_lasso_2d.hh"
29#include "BLI_listbase.h"
30#include "BLI_math_bits.h"
31#include "BLI_math_geom.h"
32#include "BLI_rect.h"
33#include "BLI_span.hh"
34#include "BLI_string.h"
35#include "BLI_task.hh"
36#include "BLI_utildefines.h"
37#include "BLI_vector.hh"
38
39#include "BLT_translation.hh"
40
41#ifdef __BIG_ENDIAN__
42# include "BLI_endian_switch.h"
43#endif
44
45#include "BKE_action.hh"
46#include "BKE_armature.hh"
47#include "BKE_attribute.hh"
48#include "BKE_context.hh"
49#include "BKE_crazyspace.hh"
50#include "BKE_curve.hh"
51#include "BKE_curves.hh"
52#include "BKE_editmesh.hh"
53#include "BKE_global.hh"
54#include "BKE_grease_pencil.hh"
55#include "BKE_layer.hh"
56#include "BKE_main.hh"
57#include "BKE_mball.hh"
58#include "BKE_mesh.hh"
59#include "BKE_object.hh"
60#include "BKE_paint.hh"
61#include "BKE_scene.hh"
62#include "BKE_tracking.h"
63#include "BKE_workspace.hh"
64
65#include "WM_api.hh"
66#include "WM_toolsystem.hh"
67#include "WM_types.hh"
68
69#include "RNA_access.hh"
70#include "RNA_define.hh"
71#include "RNA_enum_types.hh"
72
73#include "ED_armature.hh"
74#include "ED_curve.hh"
75#include "ED_curves.hh"
76#include "ED_gpencil_legacy.hh"
77#include "ED_grease_pencil.hh"
78#include "ED_lattice.hh"
79#include "ED_mball.hh"
80#include "ED_mesh.hh"
81#include "ED_object.hh"
82#include "ED_outliner.hh"
83#include "ED_particle.hh"
84#include "ED_pointcloud.hh"
85#include "ED_screen.hh"
86#include "ED_sculpt.hh"
87#include "ED_select_utils.hh"
88
89#include "UI_interface.hh"
90#include "UI_resources.hh"
91
92#include "GPU_matrix.hh"
93#include "GPU_select.hh"
94
95#include "DEG_depsgraph.hh"
97
98#include "DRW_engine.hh"
99#include "DRW_select_buffer.hh"
100
101#include "ANIM_armature.hh"
103
104#include "view3d_intern.hh" /* own include */
105
106// #include "BLI_time_utildefines.h"
107
108using blender::Array;
109using blender::int2;
110using blender::Span;
111using blender::Vector;
112
113/* -------------------------------------------------------------------- */
116
118{
119 return 75.0f * U.pixelsize;
120}
121
123{
124 /* TODO: should return whether there is valid context to continue. */
125 ViewContext vc = {};
126 vc.C = C;
127 vc.region = CTX_wm_region(C);
128 vc.bmain = CTX_data_main(C);
129 vc.depsgraph = depsgraph;
130 vc.scene = CTX_data_scene(C);
132 vc.v3d = CTX_wm_view3d(C);
133 vc.win = CTX_wm_window(C);
137 return vc;
138}
139
141{
142 vc->obact = obact;
143 /* See public doc-string for rationale on checking the existing values first. */
144 if (vc->obedit) {
146 vc->obedit = obact;
147 if (vc->em) {
149 }
150 }
151}
152
154
155/* -------------------------------------------------------------------- */
158
159static bool object_deselect_all_visible(const Scene *scene, ViewLayer *view_layer, View3D *v3d)
160{
161 bool changed = false;
162 BKE_view_layer_synced_ensure(scene, view_layer);
164 if (base->flag & BASE_SELECTED) {
165 if (BASE_SELECTABLE(v3d, base)) {
167 changed = true;
168 }
169 }
170 }
171 return changed;
172}
173
174/* deselect all except b */
175static bool object_deselect_all_except(const Scene *scene, ViewLayer *view_layer, Base *b)
176{
177 bool changed = false;
178 BKE_view_layer_synced_ensure(scene, view_layer);
180 if (base->flag & BASE_SELECTED) {
181 if (b != base) {
183 changed = true;
184 }
185 }
186 }
187 return changed;
188}
189
191
192/* -------------------------------------------------------------------- */
202
206
207static void editselect_buf_cache_init(const ViewContext *vc, short select_mode)
208{
209 if (vc->obedit) {
211 vc->scene, vc->view_layer, vc->v3d);
212
213 DRW_select_buffer_context_create(vc->depsgraph, bases, select_mode);
214 }
215 else {
216 /* Use for paint modes, currently only a single object at a time. */
217 if (vc->obact) {
220 DRW_select_buffer_context_create(vc->depsgraph, {base}, select_mode);
221 }
222 }
223}
224
229
230static void editselect_buf_cache_free_voidp(void *esel_voidp)
231{
232 editselect_buf_cache_free(static_cast<EditSelectBuf_Cache *>(esel_voidp));
233 MEM_freeN(static_cast<EditSelectBuf_Cache *>(esel_voidp));
234}
235
237 const ViewContext *vc,
238 short select_mode)
239{
241 wm_userdata->data = esel;
243 wm_userdata->use_free = true;
244 editselect_buf_cache_init(vc, select_mode);
245}
246
248
249/* -------------------------------------------------------------------- */
252
254 Depsgraph *depsgraph,
255 Object *ob,
256 BMEditMesh *em,
257 const eSelectOp sel_op)
258{
259 BMVert *eve;
260 BMIter iter;
261 bool changed = false;
262
263 const BLI_bitmap *select_bitmap = esel->select_bitmap;
265 if (index == 0) {
266 return false;
267 }
268
269 index -= 1;
270 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
272 const bool is_select = BM_elem_flag_test(eve, BM_ELEM_SELECT);
273 const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index);
274 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
275 if (sel_op_result != -1) {
276 BM_vert_select_set(em->bm, eve, sel_op_result);
277 changed = true;
278 }
279 }
280 index++;
281 }
282 return changed;
283}
284
286 Depsgraph *depsgraph,
287 Object *ob,
288 BMEditMesh *em,
289 const eSelectOp sel_op)
290{
291 BMEdge *eed;
292 BMIter iter;
293 bool changed = false;
294
295 const BLI_bitmap *select_bitmap = esel->select_bitmap;
297 if (index == 0) {
298 return false;
299 }
300
301 index -= 1;
302 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
304 const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT);
305 const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index);
306 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
307 if (sel_op_result != -1) {
308 BM_edge_select_set(em->bm, eed, sel_op_result);
309 changed = true;
310 }
311 }
312 index++;
313 }
314 return changed;
315}
316
318 Depsgraph *depsgraph,
319 Object *ob,
320 BMEditMesh *em,
321 const eSelectOp sel_op)
322{
323 BMFace *efa;
324 BMIter iter;
325 bool changed = false;
326
327 const BLI_bitmap *select_bitmap = esel->select_bitmap;
329 if (index == 0) {
330 return false;
331 }
332
333 index -= 1;
334 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
336 const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT);
337 const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index);
338 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
339 if (sel_op_result != -1) {
340 BM_face_select_set(em->bm, efa, sel_op_result);
341 changed = true;
342 }
343 }
344 index++;
345 }
346 return changed;
347}
348
349/* object mode, edbm_ prefix is confusing here, rename? */
352 const eSelectOp sel_op)
353{
354 using namespace blender;
355 bool changed = false;
356
357 const BLI_bitmap *select_bitmap = esel->select_bitmap;
358
359 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
361 ".select_vert", bke::AttrDomain::Point);
362 const VArray<bool> hide_vert = *attributes.lookup_or_default<bool>(
363 ".hide_vert", bke::AttrDomain::Point, false);
364
365 for (int index = 0; index < mesh->verts_num; index++) {
366 if (!hide_vert[index]) {
367 const bool is_select = select_vert.span[index];
368 const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index);
369 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
370 if (sel_op_result != -1) {
371 select_vert.span[index] = sel_op_result == 1;
372 changed = true;
373 }
374 }
375 }
376 select_vert.finish();
377 return changed;
378}
379
380/* object mode, edbm_ prefix is confusing here, rename? */
383 const eSelectOp sel_op)
384{
385 using namespace blender;
386 bool changed = false;
387
388 const BLI_bitmap *select_bitmap = esel->select_bitmap;
389
390 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
392 ".select_poly", bke::AttrDomain::Face);
393 const VArray<bool> hide_poly = *attributes.lookup_or_default<bool>(
394 ".hide_poly", bke::AttrDomain::Face, false);
395
396 for (int index = 0; index < mesh->faces_num; index++) {
397 if (!hide_poly[index]) {
398 const bool is_select = select_poly.span[index];
399 const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index);
400 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
401 if (sel_op_result != -1) {
402 select_poly.span[index] = sel_op_result == 1;
403 changed = true;
404 }
405 }
406 }
407 select_poly.finish();
408 return changed;
409}
410
412
413/* -------------------------------------------------------------------- */
416
431
433 const ViewContext *vc,
434 const rcti *rect,
435 const Span<int2> mcoords,
436 const eSelectOp sel_op)
437{
438 r_data->vc = vc;
439
440 r_data->rect = rect;
441 r_data->rect_fl = &r_data->_rect_fl;
442 BLI_rctf_rcti_copy(&r_data->_rect_fl, rect);
443
444 r_data->mcoords = mcoords;
445 r_data->sel_op = sel_op;
446 /* SELECT by default, but can be changed if needed (only few cases use and respect this). */
448
449 /* runtime */
450 r_data->pass = 0;
451 r_data->is_done = false;
452 r_data->is_changed = false;
453}
454
456{
458 return false;
459 }
460 if (Object *ob = CTX_data_active_object(C)) {
461 if (ob->mode & OB_MODE_EDIT) {
462 return ob->type != OB_FONT;
463 }
466 }
467 if (ob->mode & OB_MODE_WEIGHT_PAINT) {
469 }
470 }
471
472 return true;
473}
474
475/* helper also for box_select */
476static bool edge_fully_inside_rect(const rctf *rect, const float v1[2], const float v2[2])
477{
478 return BLI_rctf_isect_pt_v(rect, v1) && BLI_rctf_isect_pt_v(rect, v2);
479}
480
481static bool edge_inside_rect(const rctf *rect, const float v1[2], const float v2[2])
482{
483 int d1, d2, d3, d4;
484
485 /* check points in rect */
486 if (edge_fully_inside_rect(rect, v1, v2)) {
487 return true;
488 }
489
490 /* check points completely out rect */
491 if (v1[0] < rect->xmin && v2[0] < rect->xmin) {
492 return false;
493 }
494 if (v1[0] > rect->xmax && v2[0] > rect->xmax) {
495 return false;
496 }
497 if (v1[1] < rect->ymin && v2[1] < rect->ymin) {
498 return false;
499 }
500 if (v1[1] > rect->ymax && v2[1] > rect->ymax) {
501 return false;
502 }
503
504 /* simple check lines intersecting. */
505 d1 = (v1[1] - v2[1]) * (v1[0] - rect->xmin) + (v2[0] - v1[0]) * (v1[1] - rect->ymin);
506 d2 = (v1[1] - v2[1]) * (v1[0] - rect->xmin) + (v2[0] - v1[0]) * (v1[1] - rect->ymax);
507 d3 = (v1[1] - v2[1]) * (v1[0] - rect->xmax) + (v2[0] - v1[0]) * (v1[1] - rect->ymax);
508 d4 = (v1[1] - v2[1]) * (v1[0] - rect->xmax) + (v2[0] - v1[0]) * (v1[1] - rect->ymin);
509
510 if (d1 < 0 && d2 < 0 && d3 < 0 && d4 < 0) {
511 return false;
512 }
513 if (d1 > 0 && d2 > 0 && d3 > 0 && d4 > 0) {
514 return false;
515 }
516
517 return true;
518}
519
520static void do_lasso_select_pose__do_tag(void *user_data,
521 bPoseChannel *pchan,
522 const float screen_co_a[2],
523 const float screen_co_b[2])
524{
525 LassoSelectUserData *data = static_cast<LassoSelectUserData *>(user_data);
526 const bArmature *arm = static_cast<bArmature *>(data->vc->obact->data);
527 if (!PBONE_SELECTABLE(arm, pchan->bone)) {
528 return;
529 }
530
531 if (BLI_rctf_isect_segment(data->rect_fl, screen_co_a, screen_co_b) &&
532 BLI_lasso_is_edge_inside(data->mcoords, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX))
533 {
534 pchan->bone->flag |= BONE_DONE;
535 data->is_changed = true;
536 }
537}
538static void do_lasso_tag_pose(const ViewContext *vc, const Span<int2> mcoords)
539{
541 rcti rect;
542
543 if ((vc->obact->type != OB_ARMATURE) || (vc->obact->pose == nullptr)) {
544 return;
545 }
546
547 BLI_lasso_boundbox(&rect, mcoords);
548
549 view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, static_cast<eSelectOp>(0));
550
552
553 /* Treat bones as clipped segments (no joints). */
556 &data,
558}
559
561 const Span<int2> mcoords,
562 const eSelectOp sel_op)
563{
564 View3D *v3d = vc->v3d;
565
566 bool changed = false;
567 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
568 changed |= object_deselect_all_visible(vc->scene, vc->view_layer, vc->v3d);
569 }
572 if (BASE_SELECTABLE(v3d, base)) { /* Use this to avoid unnecessary lasso look-ups. */
573 float region_co[2];
574 const bool is_select = base->flag & BASE_SELECTED;
575 const bool is_inside = (ED_view3d_project_base(vc->region, base, region_co) ==
578 int(region_co[0]),
579 int(region_co[1]),
580 /* Dummy value. */
581 INT_MAX);
582 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
583 if (sel_op_result != -1) {
585 sel_op_result ? blender::ed::object::BA_SELECT :
587 changed = true;
588 }
589 }
590 }
591
592 if (changed) {
595 }
596 return changed;
597}
598
603{
604 auto bases_tag_and_append_fn = [](blender::Vector<Base *> &bases, Base *base) {
605 Object *ob = base->object;
606 bArmature *arm = static_cast<bArmature *>(ob->data);
607 LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
608 Bone *bone = pchan->bone;
609 bone->flag &= ~BONE_DONE;
610 }
611 arm->id.tag |= ID_TAG_DOIT;
612 ob->id.tag &= ~ID_TAG_DOIT;
613 bases.append(base);
614 };
615
617
618 /* Special case, pose + weight paint mode. */
619 if (vc->obact && (vc->obact->mode & OB_MODE_WEIGHT_PAINT)) {
621 BLI_assert(ob_pose != nullptr); /* Caller is expected to check. */
622 Base *base = BKE_view_layer_base_find(vc->view_layer, ob_pose);
623 if (base) {
624 bases_tag_and_append_fn(bases, base);
625 }
626 }
627 else {
629 vc->scene, vc->view_layer, vc->v3d, OB_ARMATURE, OB_MODE_POSE, base_iter)
630 {
631 bases_tag_and_append_fn(bases, base_iter);
632 }
634 }
635 return bases;
636}
637
639{
640 bool changed_multi = false;
641
642 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
643 for (const int i : bases.index_range()) {
644 Base *base_iter = bases[i];
645 Object *ob_iter = base_iter->object;
646 if (ED_pose_deselect_all(ob_iter, SEL_DESELECT, false)) {
648 changed_multi = true;
649 }
650 }
651 }
652
653 for (const int i : bases.index_range()) {
654 Base *base_iter = bases[i];
655 Object *ob_iter = base_iter->object;
656 bArmature *arm = static_cast<bArmature *>(ob_iter->data);
657
658 /* Don't handle twice. */
659 if (arm->id.tag & ID_TAG_DOIT) {
660 arm->id.tag &= ~ID_TAG_DOIT;
661 }
662 else {
663 continue;
664 }
665
666 bool changed = true;
667 LISTBASE_FOREACH (bPoseChannel *, pchan, &ob_iter->pose->chanbase) {
668 Bone *bone = pchan->bone;
669 if ((bone->flag & BONE_UNSELECTABLE) == 0) {
670 const bool is_select = bone->flag & BONE_SELECTED;
671 const bool is_inside = bone->flag & BONE_DONE;
672 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
673 if (sel_op_result != -1) {
674 SET_FLAG_FROM_TEST(bone->flag, sel_op_result, BONE_SELECTED);
675 if (sel_op_result == 0) {
676 if (arm->act_bone == bone) {
677 arm->act_bone = nullptr;
678 }
679 }
680 changed = true;
681 }
682 }
683 }
684 if (changed) {
686 changed_multi = true;
687 }
688 }
689 return changed_multi;
690}
691
692static bool do_lasso_select_pose(const ViewContext *vc,
693 const Span<int2> mcoords,
694 const eSelectOp sel_op)
695{
697
698 ViewContext vc_temp = *vc;
699
700 for (const int i : bases.index_range()) {
701 Base *base_iter = bases[i];
702 Object *ob_iter = base_iter->object;
703 ED_view3d_viewcontext_init_object(&vc_temp, ob_iter);
704 do_lasso_tag_pose(&vc_temp, mcoords);
705 }
706
707 const bool changed_multi = do_pose_tag_select_op_exec(bases, sel_op);
708 if (changed_multi) {
711 }
712
713 return changed_multi;
714}
715
716static void do_lasso_select_mesh__doSelectVert(void *user_data,
717 BMVert *eve,
718 const float screen_co[2],
719 int /*index*/)
720{
721 LassoSelectUserData *data = static_cast<LassoSelectUserData *>(user_data);
722 const bool is_select = BM_elem_flag_test(eve, BM_ELEM_SELECT);
723 const bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
725 data->mcoords, screen_co[0], screen_co[1], IS_CLIPPED));
726 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
727 if (sel_op_result != -1) {
728 BM_vert_select_set(data->vc->em->bm, eve, sel_op_result);
729 data->is_changed = true;
730 }
731}
738 BMEdge *eed,
739 const float screen_co_a[2],
740 const float screen_co_b[2],
741 int index)
742{
744 user_data);
745 LassoSelectUserData *data = data_for_edge->data;
746 bool is_visible = true;
747 if (data_for_edge->backbuf_offset) {
748 uint bitmap_inedx = data_for_edge->backbuf_offset + index - 1;
749 is_visible = BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, bitmap_inedx);
750 }
751
752 const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT);
753 const bool is_inside =
754 (is_visible && edge_fully_inside_rect(data->rect_fl, screen_co_a, screen_co_b) &&
755 BLI_lasso_is_point_inside(data->mcoords, UNPACK2(screen_co_a), IS_CLIPPED) &&
756 BLI_lasso_is_point_inside(data->mcoords, UNPACK2(screen_co_b), IS_CLIPPED));
757 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
758 if (sel_op_result != -1) {
759 BM_edge_select_set(data->vc->em->bm, eed, sel_op_result);
760 data->is_done = true;
761 data->is_changed = true;
762 }
763}
765 BMEdge *eed,
766 const float screen_co_a[2],
767 const float screen_co_b[2],
768 int index)
769{
771 user_data);
772 LassoSelectUserData *data = data_for_edge->data;
773 bool is_visible = true;
774 if (data_for_edge->backbuf_offset) {
775 uint bitmap_inedx = data_for_edge->backbuf_offset + index - 1;
776 is_visible = BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, bitmap_inedx);
777 }
778
779 const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT);
780 const bool is_inside = (is_visible && BLI_lasso_is_edge_inside(data->mcoords,
781 UNPACK2(screen_co_a),
782 UNPACK2(screen_co_b),
783 IS_CLIPPED));
784 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
785 if (sel_op_result != -1) {
786 BM_edge_select_set(data->vc->em->bm, eed, sel_op_result);
787 data->is_changed = true;
788 }
789}
790
791static void do_lasso_select_mesh__doSelectFace(void *user_data,
792 BMFace *efa,
793 const float screen_co[2],
794 int /*index*/)
795{
796 LassoSelectUserData *data = static_cast<LassoSelectUserData *>(user_data);
797 const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT);
798 const bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
800 data->mcoords, screen_co[0], screen_co[1], IS_CLIPPED));
801 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
802 if (sel_op_result != -1) {
803 BM_face_select_set(data->vc->em->bm, efa, sel_op_result);
804 data->is_changed = true;
805 }
806}
807
808static bool do_lasso_select_mesh(const ViewContext *vc,
809 wmGenericUserData *wm_userdata,
810 const Span<int2> mcoords,
811 const eSelectOp sel_op)
812{
815 rcti rect;
816
818
819 BLI_lasso_boundbox(&rect, mcoords);
820
821 view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, sel_op);
822
823 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
824 if (vc->em->bm->totvertsel) {
826 data.is_changed = true;
827 }
828 }
829
830 /* for non zbuf projections, don't change the GL state */
832
834
835 const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d);
836
837 EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
838 if (use_zbuf) {
839 if (wm_userdata->data == nullptr) {
841 esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
843 vc->depsgraph, vc->region, vc->v3d, mcoords, &rect, nullptr);
844 }
845 }
846
847 if (ts->selectmode & SCE_SELECT_VERTEX) {
848 if (use_zbuf) {
850 esel, vc->depsgraph, vc->obedit, vc->em, sel_op);
851 }
852 else {
855 }
856 }
857 if (ts->selectmode & SCE_SELECT_EDGE) {
858 /* Does both use_zbuf and non-use_zbuf versions (need screen cos for both) */
859 LassoSelectUserData_ForMeshEdge data_for_edge{};
860 data_for_edge.data = &data;
861 data_for_edge.esel = use_zbuf ? esel : nullptr;
863 vc->depsgraph, vc->obedit, SCE_SELECT_EDGE) :
864 0;
865
866 const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_NEAR |
867 (use_zbuf ? (eV3DProjTest)0 : V3D_PROJ_TEST_CLIP_BB);
868 /* Fully inside. */
870 vc, do_lasso_select_mesh__doSelectEdge_pass0, &data_for_edge, clip_flag);
871 if (data.is_done == false) {
872 /* Fall back to partially inside.
873 * Clip content to account for edges partially behind the view. */
876 &data_for_edge,
878 }
879 }
880
881 if (ts->selectmode & SCE_SELECT_FACE) {
882 if (use_zbuf) {
884 esel, vc->depsgraph, vc->obedit, vc->em, sel_op);
885 }
886 else {
889 }
890 }
891
892 if (data.is_changed) {
894 }
895 return data.is_changed;
896}
897
898static void do_lasso_select_curve__doSelect(void *user_data,
899 Nurb * /*nu*/,
900 BPoint *bp,
901 BezTriple *bezt,
902 int beztindex,
903 bool handles_visible,
904 const float screen_co[2])
905{
906 LassoSelectUserData *data = static_cast<LassoSelectUserData *>(user_data);
907
909 data->mcoords, screen_co[0], screen_co[1], IS_CLIPPED);
910 if (bp) {
911 const bool is_select = bp->f1 & SELECT;
912 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
913 if (sel_op_result != -1) {
914 SET_FLAG_FROM_TEST(bp->f1, sel_op_result, data->select_flag);
915 data->is_changed = true;
916 }
917 }
918 else {
919 if (!handles_visible) {
920 /* can only be (beztindex == 1) here since handles are hidden */
921 const bool is_select = bezt->f2 & SELECT;
922 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
923 if (sel_op_result != -1) {
924 SET_FLAG_FROM_TEST(bezt->f2, sel_op_result, data->select_flag);
925 }
926 bezt->f1 = bezt->f3 = bezt->f2;
927 data->is_changed = true;
928 }
929 else {
930 uint8_t *flag_p = (&bezt->f1) + beztindex;
931 const bool is_select = *flag_p & SELECT;
932 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
933 if (sel_op_result != -1) {
934 SET_FLAG_FROM_TEST(*flag_p, sel_op_result, data->select_flag);
935 data->is_changed = true;
936 }
937 }
938 }
939}
940
941static bool do_lasso_select_curve(const ViewContext *vc,
942 const Span<int2> mcoords,
943 const eSelectOp sel_op)
944{
945 const bool deselect_all = (sel_op == SEL_OP_SET);
947 rcti rect;
948
949 BLI_lasso_boundbox(&rect, mcoords);
950
951 view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, sel_op);
952
953 Curve *curve = (Curve *)vc->obedit->data;
954 ListBase *nurbs = BKE_curve_editNurbs_get(curve);
955
956 /* For deselect all, items to be selected are tagged with temp flag. Clear that first. */
957 if (deselect_all) {
959 data.select_flag = BEZT_FLAG_TEMP_TAG;
960 }
961
962 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
964
965 /* Deselect items that were not added to selection (indicated by temp flag). */
966 if (deselect_all) {
968 }
969
970 if (data.is_changed) {
972 }
973 return data.is_changed;
974}
975
976static void do_lasso_select_lattice__doSelect(void *user_data,
977 BPoint *bp,
978 const float screen_co[2])
979{
980 LassoSelectUserData *data = static_cast<LassoSelectUserData *>(user_data);
981 const bool is_select = bp->f1 & SELECT;
982 const bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
984 data->mcoords, screen_co[0], screen_co[1], IS_CLIPPED));
985 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
986 if (sel_op_result != -1) {
987 SET_FLAG_FROM_TEST(bp->f1, sel_op_result, SELECT);
988 data->is_changed = true;
989 }
990}
992 const Span<int2> mcoords,
993 const eSelectOp sel_op)
994{
996 rcti rect;
997
998 BLI_lasso_boundbox(&rect, mcoords);
999
1000 view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, sel_op);
1001
1002 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1003 data.is_changed |= ED_lattice_flags_set(vc->obedit, 0);
1004 }
1005
1006 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
1009 return data.is_changed;
1010}
1011
1012static void do_lasso_select_armature__doSelectBone(void *user_data,
1013 EditBone *ebone,
1014 const float screen_co_a[2],
1015 const float screen_co_b[2])
1016{
1017 LassoSelectUserData *data = static_cast<LassoSelectUserData *>(user_data);
1018 const bArmature *arm = static_cast<const bArmature *>(data->vc->obedit->data);
1020 return;
1021 }
1022
1023 int is_ignore_flag = 0;
1024 int is_inside_flag = 0;
1025
1026 if (screen_co_a[0] != IS_CLIPPED) {
1027 if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_a)) &&
1028 BLI_lasso_is_point_inside(data->mcoords, UNPACK2(screen_co_a), INT_MAX))
1029 {
1030 is_inside_flag |= BONESEL_ROOT;
1031 }
1032 }
1033 else {
1034 is_ignore_flag |= BONESEL_ROOT;
1035 }
1036
1037 if (screen_co_b[0] != IS_CLIPPED) {
1038 if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_b)) &&
1039 BLI_lasso_is_point_inside(data->mcoords, UNPACK2(screen_co_b), INT_MAX))
1040 {
1041 is_inside_flag |= BONESEL_TIP;
1042 }
1043 }
1044 else {
1045 is_ignore_flag |= BONESEL_TIP;
1046 }
1047
1048 if (is_ignore_flag == 0) {
1049 if (is_inside_flag == (BONE_ROOTSEL | BONE_TIPSEL) ||
1051 data->mcoords, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX))
1052 {
1053 is_inside_flag |= BONESEL_BONE;
1054 }
1055 }
1056
1057 ebone->temp.i = is_inside_flag | (is_ignore_flag >> 16);
1058}
1060 EditBone *ebone,
1061 const float screen_co_a[2],
1062 const float screen_co_b[2])
1063{
1064 LassoSelectUserData *data = static_cast<LassoSelectUserData *>(user_data);
1065 bArmature *arm = static_cast<bArmature *>(data->vc->obedit->data);
1067 return;
1068 }
1069
1070 const int is_ignore_flag = ebone->temp.i << 16;
1071 int is_inside_flag = ebone->temp.i & ~0xFFFF;
1072
1073 /* - When #BONESEL_BONE is set, there is nothing to do.
1074 * - When #BONE_ROOTSEL or #BONE_TIPSEL have been set - they take priority over bone selection.
1075 */
1076 if (is_inside_flag & (BONESEL_BONE | BONE_ROOTSEL | BONE_TIPSEL)) {
1077 return;
1078 }
1079
1080 if (BLI_lasso_is_edge_inside(data->mcoords, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX))
1081 {
1082 is_inside_flag |= BONESEL_BONE;
1083 }
1084
1085 ebone->temp.i = is_inside_flag | (is_ignore_flag >> 16);
1086}
1087
1089 const Span<int2> mcoords,
1090 const eSelectOp sel_op)
1091{
1093 rcti rect;
1094
1095 BLI_lasso_boundbox(&rect, mcoords);
1096
1097 view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, sel_op);
1098
1099 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1101 }
1102
1103 bArmature *arm = static_cast<bArmature *>(vc->obedit->data);
1104
1106
1108
1109 /* Operate on fully visible (non-clipped) points. */
1112
1113 /* Operate on bones as segments clipped to the viewport bounds
1114 * (needed to handle bones with both points outside the view).
1115 * A separate pass is needed since clipped coordinates can't be used for selecting joints. */
1118 &data,
1120
1121 data.is_changed |= ED_armature_edit_select_op_from_tagged(arm, sel_op);
1122
1123 if (data.is_changed) {
1125 }
1126 return data.is_changed;
1127}
1128
1129static void do_lasso_select_mball__doSelectElem(void *user_data,
1130 MetaElem *ml,
1131 const float screen_co[2])
1132{
1133 LassoSelectUserData *data = static_cast<LassoSelectUserData *>(user_data);
1134 const bool is_select = ml->flag & SELECT;
1135 const bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
1137 data->mcoords, screen_co[0], screen_co[1], INT_MAX));
1138 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
1139 if (sel_op_result != -1) {
1140 SET_FLAG_FROM_TEST(ml->flag, sel_op_result, SELECT);
1141 data->is_changed = true;
1142 }
1143}
1144static bool do_lasso_select_meta(const ViewContext *vc,
1145 const Span<int2> mcoords,
1146 const eSelectOp sel_op)
1147{
1149 rcti rect;
1150
1151 MetaBall *mb = (MetaBall *)vc->obedit->data;
1152
1153 BLI_lasso_boundbox(&rect, mcoords);
1154
1155 view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, sel_op);
1156
1157 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1158 data.is_changed |= BKE_mball_deselect_all(mb);
1159 }
1160
1162
1165
1166 return data.is_changed;
1167}
1168
1170 const Span<int2> mcoords,
1171 const eSelectOp sel_op)
1172{
1173 using namespace blender;
1174 Object *object = (vc->obedit ? vc->obedit : vc->obact);
1175 const Object *ob_eval = DEG_get_evaluated(vc->depsgraph, object);
1176 const GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
1178 vc->scene->toolsettings, object);
1179
1181 vc,
1182 sel_op,
1184 const IndexMask &mask,
1185 const StringRef attribute_name,
1186 IndexMaskMemory &memory) {
1188 const bke::greasepencil::Layer &layer = grease_pencil.layer(info.layer_index);
1189 const bke::crazyspace::GeometryDeformation deformation =
1191 ob_eval, *object, info.drawing);
1192 const IndexMask visible_handle_elements =
1194 *object, info.drawing, info.layer_index, selection_domain, memory);
1195 const float4x4 layer_to_world = layer.to_world_space(*ob_eval);
1197 layer_to_world);
1198
1200 curves,
1201 deformation,
1202 projection,
1203 mask,
1204 visible_handle_elements,
1205 selection_domain,
1206 attribute_name,
1207 mcoords,
1208 memory);
1209 });
1210}
1211
1217 const float screen_co[2],
1218 int index)
1219{
1220 using namespace blender;
1222 static_cast<LassoSelectUserData_ForMeshObjectVert *>(user_data);
1223 LassoSelectUserData *data = &mesh_data->lasso_data;
1224 const bool is_select = mesh_data->select_vert[index];
1225 const bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
1227 data->mcoords, screen_co[0], screen_co[1], IS_CLIPPED));
1228 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
1229 if (sel_op_result != -1) {
1230 mesh_data->select_vert[index] = sel_op_result == 1;
1231 data->is_changed = true;
1232 }
1233}
1235 wmGenericUserData *wm_userdata,
1236 const Span<int2> mcoords,
1237 const eSelectOp sel_op)
1238{
1239 using namespace blender;
1240 const bool use_zbuf = !XRAY_ENABLED(vc->v3d);
1241 Object *ob = vc->obact;
1242 Mesh *mesh = static_cast<Mesh *>(ob->data);
1243 rcti rect;
1244
1245 if (mesh == nullptr || mesh->verts_num == 0) {
1246 return false;
1247 }
1248
1249 bool changed = false;
1250 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1251 /* flush selection at the end */
1252 changed |= paintvert_deselect_all_visible(ob, SEL_DESELECT, false);
1253 }
1254
1255 BLI_lasso_boundbox(&rect, mcoords);
1256
1257 EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
1258 if (use_zbuf) {
1259 if (wm_userdata->data == nullptr) {
1261 esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
1263 vc->depsgraph, vc->region, vc->v3d, mcoords, &rect, nullptr);
1264 }
1265 }
1266
1267 if (use_zbuf) {
1268 if (esel->select_bitmap != nullptr) {
1269 changed |= edbm_backbuf_check_and_select_verts_obmode(mesh, esel, sel_op);
1270 }
1271 }
1272 else {
1273 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
1274 bke::SpanAttributeWriter<bool> select_vert = attributes.lookup_or_add_for_write_span<bool>(
1275 ".select_vert", bke::AttrDomain::Point);
1276
1278 data.select_vert = select_vert.span;
1279
1280 view3d_userdata_lassoselect_init(&data.lasso_data, vc, &rect, mcoords, sel_op);
1281
1283
1286
1287 changed |= data.lasso_data.is_changed;
1288 select_vert.finish();
1289 }
1290
1291 if (changed) {
1292 if (SEL_OP_CAN_DESELECT(sel_op)) {
1294 }
1297 }
1298
1299 return changed;
1300}
1302 wmGenericUserData *wm_userdata,
1303 const Span<int2> mcoords,
1304 const eSelectOp sel_op)
1305{
1306 Object *ob = vc->obact;
1307 Mesh *mesh = static_cast<Mesh *>(ob->data);
1308 rcti rect;
1309
1310 if (mesh == nullptr || mesh->faces_num == 0) {
1311 return false;
1312 }
1313
1314 bool changed = false;
1315 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1316 /* flush selection at the end */
1317 changed |= paintface_deselect_all_visible(vc->C, ob, SEL_DESELECT, false);
1318 }
1319
1320 BLI_lasso_boundbox(&rect, mcoords);
1321
1322 EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
1323 if (esel == nullptr) {
1325 esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
1327 vc->depsgraph, vc->region, vc->v3d, mcoords, &rect, nullptr);
1328 }
1329
1330 if (esel->select_bitmap) {
1331 changed |= edbm_backbuf_check_and_select_faces_obmode(mesh, esel, sel_op);
1332 }
1333
1334 if (changed) {
1335 paintface_flush_flags(vc->C, ob, true, false);
1336 }
1337 return changed;
1338}
1339
1341 ViewContext *vc,
1342 const Span<int2> mcoords,
1343 const eSelectOp sel_op)
1344{
1345 using namespace blender;
1347 bool changed_multi = false;
1348
1349 wmGenericUserData wm_userdata_buf = {nullptr, nullptr, false};
1350 wmGenericUserData *wm_userdata = &wm_userdata_buf;
1351
1352 if (vc->obedit == nullptr) { /* Object Mode */
1354 changed_multi |= do_lasso_select_paintface(vc, wm_userdata, mcoords, sel_op);
1355 }
1356 else if (BKE_paint_select_vert_test(ob)) {
1357 changed_multi |= do_lasso_select_paintvert(vc, wm_userdata, mcoords, sel_op);
1358 }
1360 changed_multi |= do_lasso_select_grease_pencil(vc, mcoords, sel_op);
1361 }
1362 else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT)) {
1363 changed_multi |= PE_lasso_select(C,
1364 reinterpret_cast<const int(*)[2]>(mcoords.data()),
1365 mcoords.size(),
1366 sel_op) != OPERATOR_CANCELLED;
1367 }
1368 else if (ob &&
1369 ((ob->mode & OB_MODE_POSE) | ((ob->mode & OB_MODE_WEIGHT_PAINT) &&
1371 {
1372 changed_multi |= do_lasso_select_pose(vc, mcoords, sel_op);
1373 if (changed_multi) {
1375 }
1376 }
1377 else if (ob &&
1379 {
1380 /* pass */
1381 }
1382 else {
1383 changed_multi |= do_lasso_select_objects(vc, mcoords, sel_op);
1384 if (changed_multi) {
1386 }
1387 }
1388 }
1389 else { /* Edit Mode */
1390 if (vc->obedit->type == OB_MESH) {
1392 }
1393
1394 FOREACH_OBJECT_IN_MODE_BEGIN (vc->scene, vc->view_layer, vc->v3d, ob->type, ob->mode, ob_iter)
1395 {
1397 bool changed = false;
1398
1399 switch (vc->obedit->type) {
1400 case OB_MESH:
1401 changed = do_lasso_select_mesh(vc, wm_userdata, mcoords, sel_op);
1402 break;
1403 case OB_CURVES_LEGACY:
1404 case OB_SURF:
1405 changed = do_lasso_select_curve(vc, mcoords, sel_op);
1406 break;
1407 case OB_LATTICE:
1408 changed = do_lasso_select_lattice(vc, mcoords, sel_op);
1409 break;
1410 case OB_ARMATURE:
1411 changed = do_lasso_select_armature(vc, mcoords, sel_op);
1412 if (changed) {
1414 }
1415 break;
1416 case OB_MBALL:
1417 changed = do_lasso_select_meta(vc, mcoords, sel_op);
1418 break;
1419 case OB_CURVES: {
1420 Curves &curves_id = *static_cast<Curves *>(vc->obedit->data);
1421 bke::CurvesGeometry &curves = curves_id.geometry.wrap();
1424 const bke::AttrDomain selection_domain = bke::AttrDomain(curves_id.selection_domain);
1425 const IndexRange elements(curves.attributes().domain_size(selection_domain));
1426 const float4x4 projection = ED_view3d_ob_project_mat_get(vc->rv3d, vc->obedit);
1427 changed = ed::curves::select_lasso(*vc,
1428 curves,
1429 deformation,
1430 projection,
1431 elements,
1432 elements,
1433 selection_domain,
1434 mcoords,
1435 sel_op);
1436 if (changed) {
1437 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
1438 * generic attribute for now. */
1439 DEG_id_tag_update(static_cast<ID *>(vc->obedit->data), ID_RECALC_GEOMETRY);
1441 }
1442 break;
1443 }
1444 case OB_POINTCLOUD: {
1445 PointCloud &pointcloud = *static_cast<PointCloud *>(vc->obedit->data);
1446 const float4x4 projection = ED_view3d_ob_project_mat_get(vc->rv3d, vc->obedit);
1448 pointcloud, *vc->region, projection, mcoords, sel_op);
1449 if (changed) {
1450 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
1451 * generic attribute for now. */
1452 DEG_id_tag_update(static_cast<ID *>(vc->obedit->data), ID_RECALC_GEOMETRY);
1454 }
1455 break;
1456 }
1457 case OB_GREASE_PENCIL: {
1458 changed = do_lasso_select_grease_pencil(vc, mcoords, sel_op);
1459 break;
1460 }
1461 default:
1462 BLI_assert_msg(0, "lasso select on incorrect object type");
1463 break;
1464 }
1465
1466 if (changed) {
1467 DEG_id_tag_update(static_cast<ID *>(vc->obedit->data), ID_RECALC_SELECT);
1469 changed_multi = true;
1470 }
1471 }
1473 }
1474
1475 WM_generic_user_data_free(wm_userdata);
1476
1477 return changed_multi;
1478}
1479
1480/* lasso operator gives properties, but since old code works
1481 * with short array we convert */
1483{
1485 if (mcoords.is_empty()) {
1486 return OPERATOR_PASS_THROUGH;
1487 }
1488
1492
1493 /* setup view context for argument to callbacks */
1495
1496 eSelectOp sel_op = static_cast<eSelectOp>(RNA_enum_get(op->ptr, "mode"));
1497 bool changed_multi = view3d_lasso_select(C, &vc, mcoords, sel_op);
1498
1499 if (changed_multi) {
1500 return OPERATOR_FINISHED;
1501 }
1502 return OPERATOR_CANCELLED;
1503}
1504
1506{
1507 ot->name = "Lasso Select";
1508 ot->description = "Select items using lasso selection";
1509 ot->idname = "VIEW3D_OT_select_lasso";
1510
1511 ot->invoke = WM_gesture_lasso_invoke;
1512 ot->modal = WM_gesture_lasso_modal;
1514 ot->poll = view3d_selectable_data;
1515 ot->cancel = WM_gesture_lasso_cancel;
1516
1517 /* flags */
1519
1520 /* properties */
1523}
1524
1526
1527/* -------------------------------------------------------------------- */
1530
1531/* The max number of menu items in an object select menu */
1538
1539#define SEL_MENU_SIZE 22
1541
1542/* special (crappy) operator only for menu select */
1544 PointerRNA * /*ptr*/,
1545 PropertyRNA * /*prop*/,
1546 bool *r_free)
1547{
1548 EnumPropertyItem *item = nullptr, item_tmp = {0};
1549 int totitem = 0;
1550 int i = 0;
1551
1552 /* Don't need context but avoid API doc-generation using this. */
1553 if (C == nullptr || object_mouse_select_menu_data[i].idname[0] == '\0') {
1555 }
1556
1557 for (; i < SEL_MENU_SIZE && object_mouse_select_menu_data[i].idname[0] != '\0'; i++) {
1558 item_tmp.name = object_mouse_select_menu_data[i].idname;
1559 item_tmp.identifier = object_mouse_select_menu_data[i].idname;
1560 item_tmp.value = i;
1561 item_tmp.icon = object_mouse_select_menu_data[i].icon;
1562 RNA_enum_item_add(&item, &totitem, &item_tmp);
1563 }
1564
1565 RNA_enum_item_end(&item, &totitem);
1566 *r_free = true;
1567
1568 return item;
1569}
1570
1572{
1573 const int name_index = RNA_enum_get(op->ptr, "name");
1574 const bool extend = RNA_boolean_get(op->ptr, "extend");
1575 const bool deselect = RNA_boolean_get(op->ptr, "deselect");
1576 const bool toggle = RNA_boolean_get(op->ptr, "toggle");
1577 bool changed = false;
1578 const char *name = object_mouse_select_menu_data[name_index].idname;
1579
1580 View3D *v3d = CTX_wm_view3d(C);
1581 Scene *scene = CTX_data_scene(C);
1582 ViewLayer *view_layer = CTX_data_view_layer(C);
1583 BKE_view_layer_synced_ensure(scene, view_layer);
1584 const Base *oldbasact = BKE_view_layer_active_base_get(view_layer);
1585
1586 Base *basact = nullptr;
1587 CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
1588 /* This is a bit dodgy, there should only be ONE object with this name,
1589 * but library objects can mess this up. */
1590 if (STREQ(name, base->object->id.name + 2)) {
1591 basact = base;
1592 break;
1593 }
1594 }
1596
1597 if (basact == nullptr) {
1598 return OPERATOR_CANCELLED;
1599 }
1600 UNUSED_VARS_NDEBUG(v3d);
1601 BLI_assert(BASE_SELECTABLE(v3d, basact));
1602
1603 if (extend) {
1605 changed = true;
1606 }
1607 else if (deselect) {
1609 changed = true;
1610 }
1611 else if (toggle) {
1612 if (basact->flag & BASE_SELECTED) {
1613 if (basact == oldbasact) {
1615 changed = true;
1616 }
1617 }
1618 else {
1620 changed = true;
1621 }
1622 }
1623 else {
1624 object_deselect_all_except(scene, view_layer, basact);
1626 changed = true;
1627 }
1628
1629 if (oldbasact != basact) {
1631 }
1632
1633 /* weak but ensures we activate menu again before using the enum */
1635
1636 /* undo? */
1637 if (changed) {
1638 Scene *scene = CTX_data_scene(C);
1641
1643
1644 return OPERATOR_FINISHED;
1645 }
1646 return OPERATOR_CANCELLED;
1647}
1648
1650{
1651 if (RNA_boolean_get(ptr, "deselect")) {
1652 return CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Deselect Object");
1653 }
1654 if (RNA_boolean_get(ptr, "toggle")) {
1655 return CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Toggle Object Selection");
1656 }
1657 return CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Select Object");
1658}
1659
1661{
1662 PropertyRNA *prop;
1663
1664 /* identifiers */
1665 ot->name = "Select Menu";
1666 ot->description = "Menu object selection";
1667 ot->idname = "VIEW3D_OT_select_menu";
1668
1669 /* API callbacks. */
1670 ot->invoke = WM_menu_invoke;
1672 ot->get_name = object_select_menu_get_name;
1673
1674 /* flags */
1675 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1676
1677 /* #Object.id.name to select (dynamic enum). */
1678 prop = RNA_def_enum(ot->srna, "name", rna_enum_dummy_NULL_items, 0, "Object Name", "");
1681 ot->prop = prop;
1682
1683 prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "");
1685 prop = RNA_def_boolean(ot->srna, "deselect", false, "Deselect", "");
1687 prop = RNA_def_boolean(ot->srna, "toggle", false, "Toggle", "");
1689}
1690
1695 const ViewContext *vc,
1696 const blender::Span<GPUSelectResult> hit_results,
1697 const int mval[2],
1699 Base **r_basact)
1700{
1701
1702 const float mval_fl[2] = {float(mval[0]), float(mval[1])};
1703 /* Distance from object center to use for selection. */
1704 const float dist_threshold_sq = square_f(15 * U.pixelsize);
1705 int base_count = 0;
1706
1707 struct BaseRefWithDepth {
1708 BaseRefWithDepth *next, *prev;
1709 Base *base;
1711 uint depth_id;
1712 };
1713 ListBase base_ref_list = {nullptr, nullptr}; /* List of #BaseRefWithDepth. */
1714
1715 /* handle base->object->select_id */
1716 CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
1717 bool ok = false;
1718 uint depth_id;
1719
1720 /* two selection methods, the CTRL select uses max dist of 15 */
1721 if (!hit_results.is_empty()) {
1722 for (const GPUSelectResult &hit_result : hit_results) {
1723 /* index was converted */
1724 if (base->object->runtime->select_id == (hit_result.id & ~0xFFFF0000)) {
1725 ok = true;
1726 depth_id = hit_result.depth;
1727 break;
1728 }
1729 }
1730 }
1731 else {
1732 float region_co[2];
1733 if (ED_view3d_project_base(vc->region, base, region_co) == V3D_PROJ_RET_OK) {
1734 const float dist_test_sq = len_squared_v2v2(mval_fl, region_co);
1735 if (dist_test_sq < dist_threshold_sq) {
1736 ok = true;
1737 /* Match GPU depth logic, as the float is always positive, it can be sorted as an int. */
1738 depth_id = float_as_uint(dist_test_sq);
1739 }
1740 }
1741 }
1742
1743 if (ok) {
1744 base_count++;
1745 BaseRefWithDepth *base_ref = MEM_callocN<BaseRefWithDepth>(__func__);
1746 base_ref->base = base;
1747 base_ref->depth_id = depth_id;
1748 BLI_addtail(&base_ref_list, (void *)base_ref);
1749 }
1750 }
1752
1753 *r_basact = nullptr;
1754
1755 if (base_count == 0) {
1756 return false;
1757 }
1758 if (base_count == 1) {
1759 Base *base = ((BaseRefWithDepth *)base_ref_list.first)->base;
1760 BLI_freelistN(&base_ref_list);
1761 *r_basact = base;
1762 return false;
1763 }
1764
1765 /* Sort by depth or distance to cursor. */
1766 BLI_listbase_sort(&base_ref_list, [](const void *a, const void *b) {
1767 return int(static_cast<const BaseRefWithDepth *>(a)->depth_id >
1768 static_cast<const BaseRefWithDepth *>(b)->depth_id);
1769 });
1770
1771 while (base_count > SEL_MENU_SIZE) {
1772 BLI_freelinkN(&base_ref_list, base_ref_list.last);
1773 base_count -= 1;
1774 }
1775
1776 /* UI, full in static array values that we later use in an enum function */
1777
1779
1780 int i;
1781 LISTBASE_FOREACH_INDEX (BaseRefWithDepth *, base_ref, &base_ref_list, i) {
1782 Base *base = base_ref->base;
1783 Object *ob = base->object;
1784 const char *name = ob->id.name + 2;
1785
1788 }
1789
1790 wmOperatorType *ot = WM_operatortype_find("VIEW3D_OT_select_menu", false);
1792
1794 RNA_boolean_set(&ptr, "extend", params.sel_op == SEL_OP_ADD);
1795 RNA_boolean_set(&ptr, "deselect", params.sel_op == SEL_OP_SUB);
1796 RNA_boolean_set(&ptr, "toggle", params.sel_op == SEL_OP_XOR);
1799
1800 BLI_freelistN(&base_ref_list);
1801 return true;
1802}
1803
1805{
1806 const int name_index = RNA_enum_get(op->ptr, "name");
1807
1810
1811 View3D *v3d = CTX_wm_view3d(C);
1812 Scene *scene = CTX_data_scene(C);
1813 ViewLayer *view_layer = CTX_data_view_layer(C);
1814 BKE_view_layer_synced_ensure(scene, view_layer);
1815 const Base *oldbasact = BKE_view_layer_active_base_get(view_layer);
1816
1817 Base *basact = object_mouse_select_menu_data[name_index].base_ptr;
1818
1819 if (basact == nullptr) {
1820 return OPERATOR_CANCELLED;
1821 }
1822
1823 BLI_assert(BASE_SELECTABLE(v3d, basact));
1824
1825 if (basact->object->mode & OB_MODE_EDIT) {
1826 EditBone *ebone = (EditBone *)object_mouse_select_menu_data[name_index].item_ptr;
1828 }
1829 else {
1830 bPoseChannel *pchan = (bPoseChannel *)object_mouse_select_menu_data[name_index].item_ptr;
1831 ED_armature_pose_select_pick_bone(scene, view_layer, v3d, basact->object, pchan->bone, params);
1832 }
1833
1834 /* Weak but ensures we activate the menu again before using the enum. */
1836
1837 /* We make the armature selected:
1838 * Not-selected active object in pose-mode won't work well for tools. */
1840
1843
1844 /* In weight-paint, we use selected bone to select vertex-group,
1845 * so don't switch to new active object. */
1846 if (oldbasact) {
1847 if (basact->object->mode & OB_MODE_EDIT) {
1848 /* Pass. */
1849 }
1850 else if (oldbasact->object->mode & OB_MODE_ALL_WEIGHT_PAINT) {
1851 /* Prevent activating.
1852 * Selection causes this to be considered the 'active' pose in weight-paint mode.
1853 * Eventually this limitation may be removed.
1854 * For now, de-select all other pose objects deforming this mesh. */
1855 ED_armature_pose_select_in_wpaint_mode(scene, view_layer, basact);
1856 }
1857 else {
1858 if (oldbasact != basact) {
1860 }
1861 }
1862 }
1863
1864 /* Undo? */
1868
1870
1871 return OPERATOR_FINISHED;
1872}
1873
1875{
1876 PropertyRNA *prop;
1877
1878 /* identifiers */
1879 ot->name = "Select Menu";
1880 ot->description = "Menu bone selection";
1881 ot->idname = "VIEW3D_OT_bone_select_menu";
1882
1883 /* API callbacks. */
1884 ot->invoke = WM_menu_invoke;
1885 ot->exec = bone_select_menu_exec;
1886
1887 /* flags */
1888 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1889
1890 /* #Object.id.name to select (dynamic enum). */
1891 prop = RNA_def_enum(ot->srna, "name", rna_enum_dummy_NULL_items, 0, "Bone Name", "");
1894 ot->prop = prop;
1895
1896 prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "");
1898 prop = RNA_def_boolean(ot->srna, "deselect", false, "Deselect", "");
1900 prop = RNA_def_boolean(ot->srna, "toggle", false, "Toggle", "");
1902}
1903
1908 const blender::Span<GPUSelectResult> hit_results,
1909 const bool is_editmode,
1911{
1912 int bone_count = 0;
1913
1914 struct BoneRefWithDepth {
1915 BoneRefWithDepth *next, *prev;
1916 Base *base;
1917 union {
1918 EditBone *ebone;
1919 bPoseChannel *pchan;
1920 void *bone_ptr;
1921 };
1923 uint depth_id;
1924 };
1925 ListBase bone_ref_list = {nullptr, nullptr};
1926
1927 GSet *added_bones = BLI_gset_ptr_new("Bone mouse select menu");
1928
1929 /* Select logic taken from #ed_armature_pick_bone_from_selectbuffer_impl
1930 * in `armature_select.cc`. */
1931 for (const GPUSelectResult &hit_result : hit_results) {
1932 void *bone_ptr = nullptr;
1933 Base *bone_base = nullptr;
1934 uint select_id = hit_result.id;
1935
1936 if (!(select_id & BONESEL_ANY)) {
1937 /* To avoid including objects in selection. */
1938 continue;
1939 }
1940
1941 select_id &= ~BONESEL_ANY;
1942 const uint hit_object = select_id & 0xFFFF;
1943
1944 /* Find the hit bone base (armature object). */
1945 CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
1946 if (base->object->runtime->select_id == hit_object) {
1947 bone_base = base;
1948 break;
1949 }
1950 }
1952
1953 if (!bone_base) {
1954 continue;
1955 }
1956
1957 /* Determine what the current bone is */
1958 if (is_editmode) {
1959 const uint hit_bone = (select_id & ~BONESEL_ANY) >> 16;
1960 bArmature *arm = static_cast<bArmature *>(bone_base->object->data);
1961 EditBone *ebone = static_cast<EditBone *>(BLI_findlink(arm->edbo, hit_bone));
1962 if (ebone && !(ebone->flag & BONE_UNSELECTABLE)) {
1963 bone_ptr = ebone;
1964 }
1965 }
1966 else {
1967 const uint hit_bone = (select_id & ~BONESEL_ANY) >> 16;
1968 bPoseChannel *pchan = static_cast<bPoseChannel *>(
1969 BLI_findlink(&bone_base->object->pose->chanbase, hit_bone));
1970 if (pchan && !(pchan->bone->flag & BONE_UNSELECTABLE)) {
1971 bone_ptr = pchan;
1972 }
1973 }
1974
1975 if (!bone_ptr) {
1976 continue;
1977 }
1978 /* We can hit a bone multiple times, so make sure we are not adding an already included bone
1979 * to the list. */
1980 const bool is_duplicate_bone = BLI_gset_haskey(added_bones, bone_ptr);
1981
1982 if (!is_duplicate_bone) {
1983 bone_count++;
1984 BoneRefWithDepth *bone_ref = MEM_callocN<BoneRefWithDepth>(__func__);
1985 bone_ref->base = bone_base;
1986 bone_ref->bone_ptr = bone_ptr;
1987 bone_ref->depth_id = hit_result.depth;
1988 BLI_addtail(&bone_ref_list, (void *)bone_ref);
1989
1990 BLI_gset_insert(added_bones, bone_ptr);
1991 }
1992 }
1993
1994 BLI_gset_free(added_bones, nullptr);
1995
1996 if (bone_count == 0) {
1997 return false;
1998 }
1999 if (bone_count == 1) {
2000 BLI_freelistN(&bone_ref_list);
2001 return false;
2002 }
2003
2004 /* Sort by depth or distance to cursor. */
2005 BLI_listbase_sort(&bone_ref_list, [](const void *a, const void *b) {
2006 return int(static_cast<const BoneRefWithDepth *>(a)->depth_id >
2007 static_cast<const BoneRefWithDepth *>(b)->depth_id);
2008 });
2009
2010 while (bone_count > SEL_MENU_SIZE) {
2011 BLI_freelinkN(&bone_ref_list, bone_ref_list.last);
2012 bone_count -= 1;
2013 }
2014
2015 /* UI, full in static array values that we later use in an enum function */
2017
2018 int i;
2019 LISTBASE_FOREACH_INDEX (BoneRefWithDepth *, bone_ref, &bone_ref_list, i) {
2020 char *name;
2021
2022 object_mouse_select_menu_data[i].base_ptr = bone_ref->base;
2023
2024 if (is_editmode) {
2025 EditBone *ebone = bone_ref->ebone;
2026 object_mouse_select_menu_data[i].item_ptr = static_cast<void *>(ebone);
2027 name = ebone->name;
2028 }
2029 else {
2030 bPoseChannel *pchan = bone_ref->pchan;
2031 object_mouse_select_menu_data[i].item_ptr = static_cast<void *>(pchan);
2032 name = pchan->name;
2033 }
2034
2036 object_mouse_select_menu_data[i].icon = ICON_BONE_DATA;
2037 }
2038
2039 wmOperatorType *ot = WM_operatortype_find("VIEW3D_OT_bone_select_menu", false);
2041
2043 RNA_boolean_set(&ptr, "extend", params.sel_op == SEL_OP_ADD);
2044 RNA_boolean_set(&ptr, "deselect", params.sel_op == SEL_OP_SUB);
2045 RNA_boolean_set(&ptr, "toggle", params.sel_op == SEL_OP_XOR);
2048
2049 BLI_freelistN(&bone_ref_list);
2050 return true;
2051}
2052
2054{
2055 for (const GPUSelectResult &hit_result : hit_results) {
2056 if (hit_result.id & 0xFFFF0000) {
2057 return true;
2058 }
2059 }
2060 return false;
2061}
2062
2063/* utility function for mixed_bones_object_selectbuffer */
2065 const int hits15)
2066{
2067 return hits15;
2068}
2069
2071 const int hits15,
2072 const int hits9)
2073{
2074 const int ofs = hits15;
2075 /* Shift results to beginning. */
2076 hit_results.slice(0, hits9).copy_from(hit_results.slice(ofs, hits9));
2077 return hits9;
2078}
2079
2081 const int hits15,
2082 const int hits9,
2083 const int hits5)
2084{
2085 const int ofs = hits15 + hits9;
2086 /* Shift results to beginning. */
2087 hit_results.slice(0, hits5).copy_from(hit_results.slice(ofs, hits5));
2088 return hits5;
2089}
2090
2100 GPUSelectBuffer *buffer,
2101 const int mval[2],
2102 eV3DSelectObjectFilter select_filter,
2103 bool do_nearest,
2104 bool do_nearest_xray,
2105 const bool do_material_slot_selection)
2106{
2107 rcti rect;
2108 int hits15, hits9 = 0, hits5 = 0;
2109 bool has_bones15 = false, has_bones9 = false, has_bones5 = false;
2110
2112 int hits = 0;
2113
2114 if (do_nearest_xray) {
2115 select_mode = VIEW3D_SELECT_PICK_ALL;
2116 }
2117
2118 /* we _must_ end cache before return, use 'goto finally' */
2120
2121 GPUSelectStorage &storage = buffer->storage;
2122 BLI_rcti_init_pt_radius(&rect, mval, 14);
2123 hits15 = view3d_gpu_select_ex(
2124 vc, buffer, &rect, select_mode, select_filter, do_material_slot_selection);
2125 if (hits15 == 1) {
2126 hits = selectbuffer_ret_hits_15(storage.as_mutable_span(), hits15);
2127 goto finally;
2128 }
2129 else if (hits15 > 0) {
2130 int ofs;
2131 has_bones15 = selectbuffer_has_bones(storage.as_span().slice(0, hits15));
2132
2133 ofs = hits15;
2134 BLI_rcti_init_pt_radius(&rect, mval, 9);
2135 hits9 = view3d_gpu_select(vc, buffer, &rect, select_mode, select_filter);
2136 if (hits9 == 1) {
2137 hits = selectbuffer_ret_hits_9(storage.as_mutable_span(), hits15, hits9);
2138 goto finally;
2139 }
2140 else if (hits9 > 0) {
2141 has_bones9 = selectbuffer_has_bones(storage.as_span().slice(ofs, hits9));
2142
2143 ofs += hits9;
2144 BLI_rcti_init_pt_radius(&rect, mval, 5);
2145 hits5 = view3d_gpu_select(vc, buffer, &rect, select_mode, select_filter);
2146 if (hits5 == 1) {
2147 hits = selectbuffer_ret_hits_5(storage.as_mutable_span(), hits15, hits9, hits5);
2148 goto finally;
2149 }
2150 else if (hits5 > 0) {
2151 has_bones5 = selectbuffer_has_bones(storage.as_span().slice(ofs, hits5));
2152 }
2153 }
2154
2155 if (has_bones5) {
2156 hits = selectbuffer_ret_hits_5(storage.as_mutable_span(), hits15, hits9, hits5);
2157 goto finally;
2158 }
2159 else if (has_bones9) {
2160 hits = selectbuffer_ret_hits_9(storage.as_mutable_span(), hits15, hits9);
2161 goto finally;
2162 }
2163 else if (has_bones15) {
2164 hits = selectbuffer_ret_hits_15(storage.as_mutable_span(), hits15);
2165 goto finally;
2166 }
2167
2168 if (hits5 > 0) {
2169 hits = selectbuffer_ret_hits_5(storage.as_mutable_span(), hits15, hits9, hits5);
2170 goto finally;
2171 }
2172 else if (hits9 > 0) {
2173 hits = selectbuffer_ret_hits_9(storage.as_mutable_span(), hits15, hits9);
2174 goto finally;
2175 }
2176 else {
2177 hits = selectbuffer_ret_hits_15(storage.as_mutable_span(), hits15);
2178 goto finally;
2179 }
2180 }
2181
2182finally:
2184 return hits;
2185}
2186
2188 GPUSelectBuffer *buffer,
2189 const int mval[2],
2190 eV3DSelectObjectFilter select_filter,
2191 bool use_cycle,
2192 bool enumerate,
2193 bool *r_do_nearest)
2194{
2195 bool do_nearest = false;
2196 View3D *v3d = vc->v3d;
2197
2198 /* define if we use solid nearest select or not */
2199 if (use_cycle) {
2200 /* Update the coordinates (even if the return value isn't used). */
2201 const bool has_motion = WM_cursor_test_motion_and_update(mval);
2202 if (!XRAY_ACTIVE(v3d)) {
2203 do_nearest = has_motion;
2204 }
2205 }
2206 else {
2207 if (!XRAY_ACTIVE(v3d)) {
2208 do_nearest = true;
2209 }
2210 }
2211
2212 if (r_do_nearest) {
2213 *r_do_nearest = do_nearest;
2214 }
2215
2216 do_nearest = do_nearest && !enumerate;
2217
2219 vc, buffer, mval, select_filter, do_nearest, true, false);
2220
2221 return hits;
2222}
2223
2228static int gpu_select_buffer_depth_id_cmp(const void *sel_a_p, const void *sel_b_p)
2229{
2230 GPUSelectResult *a = (GPUSelectResult *)sel_a_p;
2231 GPUSelectResult *b = (GPUSelectResult *)sel_b_p;
2232
2233 if (a->depth < b->depth) {
2234 return -1;
2235 }
2236 if (a->depth > b->depth) {
2237 return 1;
2238 }
2239
2240 /* Depths match, sort by id. */
2241 uint sel_a = a->id;
2242 uint sel_b = b->id;
2243
2244#ifdef __BIG_ENDIAN__
2247#endif
2248
2249 if (sel_a < sel_b) {
2250 return -1;
2251 }
2252 if (sel_a > sel_b) {
2253 return 1;
2254 }
2255 return 0;
2256}
2257
2266 const GPUSelectBuffer &buffer,
2267 int hits,
2268 bool do_nearest,
2269 bool has_bones,
2270 bool do_bones_get_priotity,
2271 int *r_select_id_subelem)
2272{
2273 Scene *scene = vc->scene;
2274 ViewLayer *view_layer = vc->view_layer;
2275 View3D *v3d = vc->v3d;
2276 int a;
2277
2278 bool found = false;
2279 int select_id = 0;
2280 int select_id_subelem = 0;
2281
2282 if (do_nearest) {
2283 uint min = 0xFFFFFFFF;
2284 int hit_index = -1;
2285
2286 if (has_bones && do_bones_get_priotity) {
2287 /* we skip non-bone hits */
2288 for (a = 0; a < hits; a++) {
2289 if (min > buffer.storage[a].depth && (buffer.storage[a].id & 0xFFFF0000)) {
2290 min = buffer.storage[a].depth;
2291 hit_index = a;
2292 }
2293 }
2294 }
2295 else {
2296
2297 for (a = 0; a < hits; a++) {
2298 /* Any object. */
2299 if (min > buffer.storage[a].depth) {
2300 min = buffer.storage[a].depth;
2301 hit_index = a;
2302 }
2303 }
2304 }
2305
2306 if (hit_index != -1) {
2307 select_id = buffer.storage[hit_index].id & 0xFFFF;
2308 select_id_subelem = (buffer.storage[hit_index].id & 0xFFFF0000) >> 16;
2309 found = true;
2310 /* No need to set `min` to `buffer.storage[hit_index].depth`, it's not used from now on. */
2311 }
2312 }
2313 else {
2314
2315 GPUSelectStorage buffer_sorted = buffer.storage;
2316 {
2317 buffer_sorted.resize(hits);
2318 /* Remove non-bone objects. */
2319 if (has_bones && do_bones_get_priotity) {
2320 /* Loop backwards to reduce re-ordering. */
2321 for (a = hits - 1; a >= 0; a--) {
2322 if ((buffer_sorted[a].id & 0xFFFF0000) == 0) {
2323 buffer_sorted[a] = buffer_sorted[--hits];
2324 }
2325 }
2326 }
2327 qsort(buffer_sorted.data(), hits, sizeof(GPUSelectResult), gpu_select_buffer_depth_id_cmp);
2328 }
2329
2330 int hit_index = -1;
2331
2332 /* It's possible there are no hits (all objects contained bones). */
2333 if (hits > 0) {
2334 /* Only exclude active object when it is selected. */
2335 BKE_view_layer_synced_ensure(scene, view_layer);
2336 Base *base = BKE_view_layer_active_base_get(view_layer);
2337 if (base && (base->flag & BASE_SELECTED)) {
2338 const int select_id_active = base->object->runtime->select_id;
2339 for (int i_next = 0, i_prev = hits - 1; i_next < hits; i_prev = i_next++) {
2340 if ((select_id_active == (buffer_sorted[i_prev].id & 0xFFFF)) &&
2341 (select_id_active != (buffer_sorted[i_next].id & 0xFFFF)))
2342 {
2343 hit_index = i_next;
2344 break;
2345 }
2346 }
2347 }
2348
2349 /* When the active object is unselected or not in `buffer`, use the nearest. */
2350 if (hit_index == -1) {
2351 /* Just pick the nearest. */
2352 hit_index = 0;
2353 }
2354 }
2355
2356 if (hit_index != -1) {
2357 select_id = buffer_sorted[hit_index].id & 0xFFFF;
2358 select_id_subelem = (buffer_sorted[hit_index].id & 0xFFFF0000) >> 16;
2359 found = true;
2360 }
2361 }
2362
2363 Base *basact = nullptr;
2364 if (found) {
2365 BKE_view_layer_synced_ensure(scene, view_layer);
2367 if (has_bones ? BASE_VISIBLE(v3d, base) : BASE_SELECTABLE(v3d, base)) {
2368 if (base->object->runtime->select_id == select_id) {
2369 basact = base;
2370 break;
2371 }
2372 }
2373 }
2374
2375 if (basact && r_select_id_subelem) {
2376 *r_select_id_subelem = select_id_subelem;
2377 }
2378 }
2379
2380 return basact;
2381}
2382
2383static Base *mouse_select_object_center(const ViewContext *vc, Base *startbase, const int mval[2])
2384{
2385 ARegion *region = vc->region;
2386 Scene *scene = vc->scene;
2387 ViewLayer *view_layer = vc->view_layer;
2388 View3D *v3d = vc->v3d;
2389
2390 BKE_view_layer_synced_ensure(scene, view_layer);
2391 Base *oldbasact = BKE_view_layer_active_base_get(view_layer);
2392
2393 const float mval_fl[2] = {float(mval[0]), float(mval[1])};
2394 float dist = ED_view3d_select_dist_px() * 1.3333f;
2395 Base *basact = nullptr;
2396
2397 /* Put the active object at a disadvantage to cycle through other objects. */
2398 const float penalty_dist = 10.0f * UI_SCALE_FAC;
2399 Base *base = startbase;
2400 while (base) {
2401 if (BASE_SELECTABLE(v3d, base)) {
2402 float screen_co[2];
2404 base->object->object_to_world().location(),
2405 screen_co,
2407 {
2408 float dist_test = len_manhattan_v2v2(mval_fl, screen_co);
2409 if (base == oldbasact) {
2410 dist_test += penalty_dist;
2411 }
2412 if (dist_test < dist) {
2413 dist = dist_test;
2414 basact = base;
2415 }
2416 }
2417 }
2418 base = base->next;
2419
2420 if (base == nullptr) {
2421 base = static_cast<Base *>(BKE_view_layer_object_bases_get(view_layer)->first);
2422 }
2423 if (base == startbase) {
2424 break;
2425 }
2426 }
2427 return basact;
2428}
2429
2431 const int mval[2],
2432 int *r_material_slot)
2433{
2435 Base *basact = nullptr;
2436 GPUSelectBuffer buffer;
2437
2438 /* setup view context for argument to callbacks */
2441
2443
2444 const bool do_nearest = !XRAY_ACTIVE(vc.v3d);
2445 const bool do_material_slot_selection = r_material_slot != nullptr;
2446 const int hits = mixed_bones_object_selectbuffer(
2447 &vc, &buffer, mval, VIEW3D_SELECT_FILTER_NOP, do_nearest, false, do_material_slot_selection);
2448
2449 if (hits > 0) {
2450 const bool has_bones = (r_material_slot == nullptr) &&
2451 selectbuffer_has_bones(buffer.storage.as_span().slice(0, hits));
2452 basact = mouse_select_eval_buffer(
2453 &vc, buffer, hits, do_nearest, has_bones, true, r_material_slot);
2454 }
2455
2456 return basact;
2457}
2458
2460{
2461 return ed_view3d_give_base_under_cursor_ex(C, mval, nullptr);
2462}
2463
2465{
2467 if (base) {
2468 return base->object;
2469 }
2470 return nullptr;
2471}
2472
2474 const int mval[2],
2475 int *r_material_slot)
2476{
2477 Base *base = ed_view3d_give_base_under_cursor_ex(C, mval, r_material_slot);
2478 if (base) {
2479 return base->object;
2480 }
2481 return nullptr;
2482}
2483
2485{
2486 return ED_view3d_give_object_under_cursor(C, mval) != nullptr;
2487}
2488
2490{
2491 LISTBASE_FOREACH (MovieTrackingObject *, tracking_object, &tracking->objects) {
2492 LISTBASE_FOREACH (MovieTrackingTrack *, track, &tracking_object->tracks) {
2494 }
2495 }
2496}
2497
2499 Scene *scene,
2500 Base *basact,
2501 MovieClip *clip,
2502 const GPUSelectBuffer &buffer,
2503 const short hits,
2505{
2506 bool changed = false;
2507 bool found = false;
2508
2509 MovieTracking *tracking = &clip->tracking;
2510 ListBase *tracksbase = nullptr;
2511 MovieTrackingTrack *track = nullptr;
2512
2513 for (int i = 0; i < hits; i++) {
2514 const int select_id = buffer.storage[i].id;
2515
2516 /* If there's bundles in buffer select bundles first,
2517 * so non-camera elements should be ignored in buffer. */
2518 if (basact->object->runtime->select_id != (select_id & 0xFFFF)) {
2519 continue;
2520 }
2521 /* Index of bundle is 1<<16-based. if there's no "bone" index
2522 * in height word, this buffer value belongs to camera. not to bundle. */
2523 if ((select_id & 0xFFFF0000) == 0) {
2524 continue;
2525 }
2526
2528 &clip->tracking, select_id >> 16, &tracksbase);
2529 found = true;
2530 break;
2531 }
2532
2533 /* Note `params.deselect_all` is ignored for tracks as in this case
2534 * all objects will be de-selected (not tracks). */
2535 if (params.sel_op == SEL_OP_SET) {
2536 if ((found && params.select_passthrough) && TRACK_SELECTED(track)) {
2537 found = false;
2538 }
2539 else if (found /* `|| params.deselect_all` */) {
2540 /* Deselect everything. */
2541 deselect_all_tracks(tracking);
2542 changed = true;
2543 }
2544 }
2545
2546 if (found) {
2547 switch (params.sel_op) {
2548 case SEL_OP_ADD: {
2549 BKE_tracking_track_select(tracksbase, track, TRACK_AREA_ALL, true);
2550 break;
2551 }
2552 case SEL_OP_SUB: {
2554 break;
2555 }
2556 case SEL_OP_XOR: {
2557 if (TRACK_SELECTED(track)) {
2559 }
2560 else {
2561 BKE_tracking_track_select(tracksbase, track, TRACK_AREA_ALL, true);
2562 }
2563 break;
2564 }
2565 case SEL_OP_SET: {
2566 BKE_tracking_track_select(tracksbase, track, TRACK_AREA_ALL, false);
2567 break;
2568 }
2569 case SEL_OP_AND: {
2570 BLI_assert_unreachable(); /* Doesn't make sense for picking. */
2571 break;
2572 }
2573 }
2574
2579
2580 changed = true;
2581 }
2582
2583 return changed || found;
2584}
2585
2599 const int mval[2],
2601 const bool center,
2602 const bool enumerate,
2603 const bool object_only)
2604{
2606 /* Setup view context for argument to callbacks. */
2608
2609 Scene *scene = vc.scene;
2610 View3D *v3d = vc.v3d;
2611
2612 /* Menu activation may find a base to make active (if it only finds a single item to select). */
2613 Base *basact_override = nullptr;
2614
2615 const bool is_obedit = (vc.obedit != nullptr);
2616 if (object_only) {
2617 /* Signal for #view3d_gpu_select to skip edit-mode objects. */
2618 vc.obedit = nullptr;
2619 }
2620
2621 /* Set for GPU depth buffer picking, leave null when selecting by center. */
2622 struct GPUData {
2623 GPUSelectBuffer buffer;
2624 int hits;
2625 bool do_nearest;
2626 bool has_bones;
2627 } *gpu = nullptr;
2628
2629 /* First handle menu selection, early exit if a menu opens
2630 * since this takes ownership of the selection action.
2631 *
2632 * Even when there is no menu `basact_override` may be set to avoid having to re-find
2633 * the item under the cursor. */
2634
2635 if (center == false) {
2636 gpu = MEM_new<GPUData>(__func__);
2637 gpu->do_nearest = false;
2638 gpu->has_bones = false;
2639
2640 /* If objects have pose-mode set, the bones are in the same selection buffer. */
2641 const eV3DSelectObjectFilter select_filter = ((object_only == false) ?
2643 vc.obact) :
2646 &vc, &gpu->buffer, mval, select_filter, true, enumerate, &gpu->do_nearest);
2647 gpu->has_bones = (object_only && gpu->hits > 0) ?
2648 false :
2649 selectbuffer_has_bones(gpu->buffer.storage.as_span().slice(0, gpu->hits));
2650 }
2651
2652 /* First handle menu selection, early exit when a menu was opened.
2653 * Otherwise fall through to regular selection. */
2654 if (enumerate) {
2655 bool has_menu = false;
2656 if (center) {
2657 if (object_mouse_select_menu(C, &vc, {}, mval, params, &basact_override)) {
2658 has_menu = true;
2659 }
2660 }
2661 else {
2662 if (gpu->hits != 0) {
2663 const blender::Span<GPUSelectResult> hit_results = gpu->buffer.storage.as_span().slice(
2664 0, gpu->hits);
2665 if (gpu->has_bones && bone_mouse_select_menu(C, hit_results, false, params)) {
2666 has_menu = true;
2667 }
2668 else if (object_mouse_select_menu(C, &vc, hit_results, mval, params, &basact_override)) {
2669 has_menu = true;
2670 }
2671 }
2672 }
2673
2674 /* Let the menu handle any further actions. */
2675 if (has_menu) {
2676 if (gpu != nullptr) {
2677 MEM_delete(gpu);
2678 }
2679 return false;
2680 }
2681 }
2682
2683 /* No menu, continue with selection. */
2684
2685 ViewLayer *view_layer = vc.view_layer;
2686 BKE_view_layer_synced_ensure(scene, view_layer);
2687 /* Don't set when the context has no active object (hidden), see: #60807. */
2688 const Base *oldbasact = vc.obact ? BKE_view_layer_active_base_get(view_layer) : nullptr;
2689 /* Always start list from `basact` when cycling the selection. */
2690 Base *startbase = (oldbasact && oldbasact->next) ?
2691 oldbasact->next :
2692 static_cast<Base *>(BKE_view_layer_object_bases_get(view_layer)->first);
2693
2694 /* The next object's base to make active. */
2695 Base *basact = nullptr;
2696 const eObjectMode object_mode = oldbasact ? static_cast<eObjectMode>(oldbasact->object->mode) :
2698 /* For the most part this is equivalent to `(object_mode & OB_MODE_POSE) != 0`
2699 * however this logic should also run with weight-paint + pose selection.
2700 * Without this, selection in weight-paint mode can de-select armatures which isn't useful,
2701 * see: #101686. */
2702 const bool has_pose_old = (oldbasact &&
2704
2705 /* When enabled, don't attempt any further selection. */
2706 bool handled = false;
2707
2708 /* Split `changed` into data-types so their associated updates can be properly performed.
2709 * This is also needed as multiple changes may happen at once.
2710 * Selecting a pose-bone or track can also select the object for example */
2711 bool changed_object = false;
2712 bool changed_pose = false;
2713 bool changed_track = false;
2714
2715 /* Handle setting the new base active (even when `handled == true`). */
2716 bool use_activate_selected_base = false;
2717
2718 if (center) {
2719 if (basact_override) {
2720 basact = basact_override;
2721 }
2722 else {
2723 basact = mouse_select_object_center(&vc, startbase, mval);
2724 }
2725 }
2726 else {
2727 if (basact_override) {
2728 basact = basact_override;
2729 }
2730 else {
2731 /* Regarding bone priority.
2732 *
2733 * - When in pose-bone, it's useful that any selection containing a bone
2734 * gets priority over other geometry (background scenery for example).
2735 *
2736 * - When in object-mode, don't prioritize bones as it would cause
2737 * pose-objects behind other objects to get priority
2738 * (mainly noticeable when #SCE_OBJECT_MODE_LOCK is disabled).
2739 *
2740 * This way prioritizing based on pose-mode has a bias to stay in pose-mode
2741 * without having to enforce this through locking the object mode. */
2742 bool do_bones_get_priotity = has_pose_old;
2743
2744 basact = (gpu->hits > 0) ? mouse_select_eval_buffer(&vc,
2745 gpu->buffer,
2746 gpu->hits,
2747 gpu->do_nearest,
2748 gpu->has_bones,
2749 do_bones_get_priotity,
2750 nullptr) :
2751 nullptr;
2752 }
2753
2754 /* See comment for `has_pose_old`, the same rationale applies here. */
2755 const bool has_pose_new = (basact &&
2757
2758 /* Select pose-bones or camera-tracks. */
2759 if (((gpu->hits > 0) && gpu->has_bones) ||
2760 /* Special case, even when there are no hits, pose logic may de-select all bones. */
2761 ((gpu->hits == 0) && has_pose_old))
2762 {
2763 /* Regarding the `basact` null checks.
2764 * While it's unlikely there are GPU hits *without* `basact` being found,
2765 * it's possible looking up the selection index fails, see: #143161. */
2766
2767 if (basact && (gpu->has_bones && (basact->object->type == OB_CAMERA))) {
2768 MovieClip *clip = BKE_object_movieclip_get(scene, basact->object, false);
2769 if (clip != nullptr) {
2771 C, scene, basact, clip, gpu->buffer, gpu->hits, params))
2772 {
2774 /* Don't set `handled` here as the object activation may be necessary. */
2775 changed_object = true;
2776
2777 changed_track = true;
2778 }
2779 else {
2780 /* Fallback to regular object selection if no new bundles were selected,
2781 * allows to select object parented to reconstruction object. */
2782 basact = mouse_select_eval_buffer(
2783 &vc, gpu->buffer, gpu->hits, gpu->do_nearest, false, false, nullptr);
2784 }
2785 }
2786 }
2787 else if ((basact || oldbasact) &&
2789 view_layer,
2790 v3d,
2791 basact ? basact : (Base *)oldbasact,
2792 gpu->buffer.storage.data(),
2793 gpu->hits,
2794 params,
2795 gpu->do_nearest))
2796 {
2797
2798 changed_pose = true;
2799
2800 /* When there is no `baseact` this will have operated on `oldbasact`,
2801 * allowing #SelectPick_Params.deselect_all work in pose-mode.
2802 * In this case no object operations are needed. */
2803 if (basact == nullptr) {
2804 handled = true;
2805 }
2806 else {
2807 /* By convention the armature-object is selected when in pose-mode.
2808 * While leaving it unselected will work, leaving pose-mode would leave the object
2809 * active + unselected which isn't ideal when performing other actions on the object. */
2811 changed_object = true;
2812
2815
2816 /* In weight-paint, we use selected bone to select vertex-group.
2817 * In this case the active object mustn't change as it would leave weight-paint mode. */
2818 if (oldbasact) {
2819 if (oldbasact->object->mode & OB_MODE_ALL_WEIGHT_PAINT) {
2820 /* Prevent activating.
2821 * Selection causes this to be considered the 'active' pose in weight-paint mode.
2822 * Eventually this limitation may be removed.
2823 * For now, de-select all other pose objects deforming this mesh. */
2824 ED_armature_pose_select_in_wpaint_mode(scene, view_layer, basact);
2825
2826 handled = true;
2827 }
2828 else if (has_pose_old && has_pose_new) {
2829 /* Within pose-mode, keep the current selection when switching pose bones,
2830 * this is noticeable when in pose mode with multiple objects at once.
2831 * Where selecting the bone of a different object would de-select this one.
2832 * After that, exiting pose-mode would only have the active armature selected.
2833 * This matches multi-object edit-mode behavior. */
2834 handled = true;
2835
2836 if (oldbasact != basact) {
2837 use_activate_selected_base = true;
2838 }
2839 }
2840 else {
2841 /* Don't set `handled` here as the object selection may be necessary
2842 * when starting out in object-mode and moving into pose-mode,
2843 * when moving from pose to object-mode using object selection also makes sense. */
2844 }
2845 }
2846 }
2847 }
2848 /* Prevent bone/track selecting to pass on to object selecting. */
2849 if (basact == oldbasact) {
2850 handled = true;
2851 }
2852 }
2853 }
2854
2855 bool changed_object_mode = false;
2856
2857 if (handled == false) {
2859 /* No special logic in edit-mode. */
2860 if (is_obedit == false) {
2861 if (basact && !BKE_object_is_mode_compat(basact->object, object_mode)) {
2862 if (object_mode == OB_MODE_OBJECT) {
2863 Main *bmain = vc.bmain;
2864 blender::ed::object::mode_generic_exit(bmain, vc.depsgraph, scene, basact->object);
2865 }
2866 if (!BKE_object_is_mode_compat(basact->object, object_mode)) {
2867 basact = nullptr;
2868 }
2869 }
2870
2871 /* Disallow switching modes,
2872 * special exception for edit-mode - vertex-parent operator. */
2873 if (basact && oldbasact) {
2874 if ((oldbasact->object->mode != basact->object->mode) &&
2875 (oldbasact->object->mode & basact->object->mode) == 0)
2876 {
2877 basact = nullptr;
2878 }
2879 }
2880 }
2881 }
2882 else {
2883 if (basact) {
2884 if (!BKE_object_is_mode_compat(basact->object, object_mode)) {
2885 changed_object_mode = true;
2886 }
2887 }
2888 else if (object_mode != OB_MODE_OBJECT) {
2889 changed_object_mode = true;
2890 }
2891 }
2892 }
2893
2894 /* Ensure code above doesn't change the active base. This code is already fairly involved,
2895 * it's best if changing the active object is localized to a single place. */
2896 BLI_assert(oldbasact == (vc.obact ? BKE_view_layer_active_base_get(view_layer) : nullptr));
2897
2898 if (handled) {
2899 /* Pass. */
2900 }
2901 else if (basact && vc.obedit) {
2902 /* Only do the select (use for setting vertex parents & hooks).
2903 * In edit-mode do not activate. */
2904 object_deselect_all_except(scene, view_layer, basact);
2906
2907 changed_object = true;
2908 }
2909 else {
2910 /* Object-mode (pose mode will have been handled already)
2911 * unless entering pose-mode from object selection (handled by `changed_object_mode`). */
2912
2913 /* NOTE(@ideasman42): When select changes object-mode it doesn't make sense to use
2914 * pass-through.
2915 *
2916 * - When object-mode locking is disabled:
2917 * Selecting another already selected object does not need to make it active,
2918 * allowing a cursor-drag to move this objects as well as other selected objects.
2919 * - When object-mode locking is enabled:
2920 * Selecting an object in a different mode (typically pose-mode) must prioritize
2921 * entering that mode which requires making the object active, further, the selection
2922 * before switching modes wont make sense in the newly entered mode,
2923 * so it makes sense to disable pass-through logic in this case.
2924 *
2925 * See: #115181 for details. */
2926 const bool select_passthrough = params.select_passthrough && (changed_object_mode == false);
2927
2928 bool found = (basact != nullptr) && BASE_SELECTABLE(v3d, basact);
2929 if (params.sel_op == SEL_OP_SET) {
2930 if ((found && select_passthrough) && (basact->flag & BASE_SELECTED)) {
2931 found = false;
2932 }
2933 else if (found || params.deselect_all) {
2934 /* Deselect everything. */
2935 /* `basact` may be nullptr. */
2936 if (object_deselect_all_except(scene, view_layer, basact)) {
2937 changed_object = true;
2938 }
2939 }
2940 }
2941
2942 if (found) {
2943 use_activate_selected_base |= (oldbasact != basact) && (is_obedit == false);
2944
2945 switch (params.sel_op) {
2946 case SEL_OP_ADD: {
2948 break;
2949 }
2950 case SEL_OP_SUB: {
2952 break;
2953 }
2954 case SEL_OP_XOR: {
2955 if (basact->flag & BASE_SELECTED) {
2956 /* Keep selected if the base is to be activated. */
2957 if (use_activate_selected_base == false) {
2959 }
2960 }
2961 else {
2963 }
2964 break;
2965 }
2966 case SEL_OP_SET: {
2967 /* Deselect has already been performed. */
2969 break;
2970 }
2971 case SEL_OP_AND: {
2972 BLI_assert_unreachable(); /* Doesn't make sense for picking. */
2973 break;
2974 }
2975 }
2976
2977 changed_object = true;
2978 }
2979 }
2980
2981 /* Perform the activation even when 'handled', since this is used to ensure
2982 * the object from the pose-bone selected is also activated. */
2983 if (use_activate_selected_base && (basact != nullptr)) {
2984 changed_object = true;
2985 blender::ed::object::base_activate(C, basact); /* adds notifier */
2986 if ((scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) == 0) {
2988 }
2989 }
2990
2991 if (changed_object) {
2994
2996 }
2997
2998 if (changed_pose) {
3000 }
3001
3002 if (gpu != nullptr) {
3003 MEM_delete(gpu);
3004 }
3005
3006 return (changed_object || changed_pose || changed_track);
3007}
3008
3016 const int mval[2],
3018 Object *obact)
3019{
3020 using namespace blender;
3021 View3D *v3d = CTX_wm_view3d(C);
3022 const bool use_zbuf = !XRAY_ENABLED(v3d);
3023
3024 Mesh *mesh = static_cast<Mesh *>(obact->data); /* already checked for nullptr */
3025 uint index = 0;
3026 bool changed = false;
3027
3028 bool found = ED_mesh_pick_vert(C, obact, mval, ED_MESH_PICK_DEFAULT_VERT_DIST, use_zbuf, &index);
3029
3030 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
3031 bke::AttributeWriter<bool> select_vert = attributes.lookup_or_add_for_write<bool>(
3032 ".select_vert", bke::AttrDomain::Point);
3033
3034 if (params.sel_op == SEL_OP_SET) {
3035 if ((found && params.select_passthrough) && select_vert.varray[index]) {
3036 found = false;
3037 }
3038 else if (found || params.deselect_all) {
3039 /* Deselect everything. */
3040 changed |= paintface_deselect_all_visible(C, obact, SEL_DESELECT, false);
3041 }
3042 }
3043
3044 if (found) {
3045 switch (params.sel_op) {
3046 case SEL_OP_ADD: {
3047 select_vert.varray.set(index, true);
3048 break;
3049 }
3050 case SEL_OP_SUB: {
3051 select_vert.varray.set(index, false);
3052 break;
3053 }
3054 case SEL_OP_XOR: {
3055 select_vert.varray.set(index, !select_vert.varray[index]);
3056 break;
3057 }
3058 case SEL_OP_SET: {
3060 select_vert.varray.set(index, true);
3061 break;
3062 }
3063 case SEL_OP_AND: {
3064 BLI_assert_unreachable(); /* Doesn't make sense for picking. */
3065 break;
3066 }
3067 }
3068
3069 /* update mselect */
3070 if (select_vert.varray[index]) {
3072 }
3073 else {
3075 }
3076
3077 select_vert.finish();
3078
3079 paintvert_flush_flags(obact);
3080
3081 changed = true;
3082 }
3083 else {
3084 select_vert.finish();
3085 }
3086
3087 if (changed) {
3089 }
3090
3091 return changed || found;
3092}
3093
3098
3105{
3106 using namespace blender;
3107 using namespace blender::ed;
3109 /* Setup view context for argument to callbacks. */
3111
3113 vc.scene, vc.view_layer, vc.v3d);
3114
3116 bases.index_range(),
3117 1L,
3119 [&](const IndexRange range, const ClosestPointCloud &init) {
3120 ClosestPointCloud new_closest = init;
3121 for (Base *base : bases.as_span().slice(range)) {
3122 Object &object = *base->object;
3123 PointCloud &pointcloud = *static_cast<PointCloud *>(object.data);
3124 const float4x4 projection = ED_view3d_ob_project_mat_get(vc.rv3d, &object);
3125
3126 std::optional<pointcloud::FindClosestData> new_closest_elem =
3128 pointcloud.positions(),
3129 projection,
3130 IndexMask(pointcloud.totpoint),
3131 float2(mval),
3133 new_closest.elem);
3134 if (new_closest_elem) {
3135 new_closest.elem = *new_closest_elem;
3136 new_closest.pointcloud = &pointcloud;
3137 }
3138 }
3139 return new_closest;
3140 },
3141 [](const ClosestPointCloud &a, const ClosestPointCloud &b) {
3142 return (a.elem.distance_sq < b.elem.distance_sq) ? a : b;
3143 });
3144
3145 std::atomic<bool> deselected = false;
3146 if (params.deselect_all || params.sel_op == SEL_OP_SET) {
3147 threading::parallel_for(bases.index_range(), 1L, [&](const IndexRange range) {
3148 for (Base *base : bases.as_span().slice(range)) {
3149 PointCloud &pointcloud = *static_cast<PointCloud *>(base->object->data);
3150 if (!pointcloud::has_anything_selected(pointcloud)) {
3151 continue;
3152 }
3153
3154 bke::GSpanAttributeWriter selection = pointcloud::ensure_selection_attribute(pointcloud,
3155 CD_PROP_BOOL);
3156 pointcloud::fill_selection_false(selection.span, IndexMask(pointcloud.totpoint));
3157 selection.finish();
3158
3159 deselected = true;
3160 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
3161 * generic attribute for now. */
3162 DEG_id_tag_update(&pointcloud.id, ID_RECALC_GEOMETRY);
3163 WM_event_add_notifier(&C, NC_GEOM | ND_DATA, &pointcloud);
3164 }
3165 });
3166 }
3167
3168 if (!closest.pointcloud) {
3169 return deselected;
3170 }
3171
3173 CD_PROP_BOOL);
3174 curves::apply_selection_operation_at_index(selection.span, closest.elem.index, params.sel_op);
3175 selection.finish();
3176
3177 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
3178 * generic attribute for now. */
3181
3182 return true;
3183}
3184
3190
3196static bool ed_curves_select_pick(bContext &C, const int mval[2], const SelectPick_Params &params)
3197{
3198 using namespace blender;
3200 /* Setup view context for argument to callbacks. */
3202
3204 vc.scene, vc.view_layer, vc.v3d);
3205
3206 Curves &active_curves_id = *static_cast<Curves *>(vc.obedit->data);
3207 const bke::AttrDomain selection_domain = bke::AttrDomain(active_curves_id.selection_domain);
3208
3210 bases.index_range(),
3211 1L,
3213 [&](const IndexRange range, const ClosestCurveDataBlock &init) {
3214 ClosestCurveDataBlock new_closest = init;
3215 for (Base *base : bases.as_span().slice(range)) {
3216 Object &curves_ob = *base->object;
3217 Curves &curves_id = *static_cast<Curves *>(curves_ob.data);
3220 const bke::CurvesGeometry &curves = curves_id.geometry.wrap();
3221 const float4x4 projection = ED_view3d_ob_project_mat_get(vc.rv3d, &curves_ob);
3222 const IndexMask elements(curves.attributes().domain_size(selection_domain));
3223 const auto range_consumer =
3224 [&](IndexRange range, Span<float3> positions, StringRef selection_attribute_name) {
3225 IndexMask mask = elements.slice_content(range);
3226
3227 std::optional<ed::curves::FindClosestData> new_closest_elem =
3229 curves.points_by_curve(),
3230 positions,
3231 curves.cyclic(),
3232 projection,
3233 mask,
3234 selection_domain,
3235 mval,
3236 new_closest.elem);
3237 if (new_closest_elem) {
3238 new_closest.selection_attribute_name = selection_attribute_name;
3239 new_closest.elem = *new_closest_elem;
3240 new_closest.curves_id = &curves_id;
3241 }
3242 };
3243
3244 if (selection_domain == bke::AttrDomain::Point) {
3246 curves,
3247 deformation,
3249 range_consumer);
3250 }
3251 else if (selection_domain == bke::AttrDomain::Curve) {
3253 curves,
3254 deformation,
3256 range_consumer);
3257 };
3258 }
3259 return new_closest;
3260 },
3261 [](const ClosestCurveDataBlock &a, const ClosestCurveDataBlock &b) {
3262 return (a.elem.distance_sq < b.elem.distance_sq) ? a : b;
3263 });
3264
3265 std::atomic<bool> deselected = false;
3266 if (params.deselect_all || params.sel_op == SEL_OP_SET) {
3267 threading::parallel_for(bases.index_range(), 1L, [&](const IndexRange range) {
3268 for (Base *base : bases.as_span().slice(range)) {
3269 Curves &curves_id = *static_cast<Curves *>(base->object->data);
3270 bke::CurvesGeometry &curves = curves_id.geometry.wrap();
3271 if (!ed::curves::has_anything_selected(curves, selection_domain)) {
3272 continue;
3273 }
3274
3275 ed::curves::foreach_selection_attribute_writer(
3276 curves, selection_domain, [](bke::GSpanAttributeWriter &selection) {
3277 ed::curves::fill_selection_false(selection.span);
3278 });
3279
3280 deselected = true;
3281 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
3282 * generic attribute for now. */
3283 DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY);
3284 WM_event_add_notifier(&C, NC_GEOM | ND_DATA, &curves_id);
3285 }
3286 });
3287 }
3288
3289 if (!closest.curves_id) {
3290 return deselected;
3291 }
3292
3293 if (selection_domain == bke::AttrDomain::Point) {
3295 closest.curves_id->geometry.wrap(),
3298 closest.selection_attribute_name);
3300 selection.span, closest.elem.index, params.sel_op);
3301 selection.finish();
3302 }
3303 else if (selection_domain == bke::AttrDomain::Curve) {
3305 closest.curves_id->geometry.wrap(),
3307 [&](bke::GSpanAttributeWriter &selection) {
3308 ed::curves::apply_selection_operation_at_index(
3309 selection.span, closest.elem.index, params.sel_op);
3310 });
3311 }
3312
3313 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
3314 * generic attribute for now. */
3317
3318 return true;
3319}
3320
3327
3334 const int mval[2],
3336{
3337 using namespace blender;
3339 /* Setup view context for argument to callbacks. */
3341 Object *object = (vc.obedit ? vc.obedit : vc.obact);
3342
3343 /* Collect editable drawings. */
3344 const Object *ob_eval = DEG_get_evaluated(vc.depsgraph, object);
3345 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
3348
3349 /* Get selection domain from tool settings. */
3351 vc.scene->toolsettings, object);
3352
3354 drawings.index_range(),
3355 1L,
3357 [&](const IndexRange range, const ClosestGreasePencilDrawing &init) {
3358 ClosestGreasePencilDrawing new_closest = init;
3359 for (const int i : range) {
3360 ed::greasepencil::MutableDrawingInfo info = drawings[i];
3361 const bke::greasepencil::Layer &layer = grease_pencil.layer(info.layer_index);
3362 /* Get deformation by modifiers. */
3365 ob_eval, *object, info.drawing);
3366
3367 IndexMaskMemory memory;
3369 *object, info, selection_domain, memory);
3370 if (elements.is_empty()) {
3371 continue;
3372 }
3373 const IndexMask visible_handle_elements =
3375 *object, info.drawing, info.layer_index, selection_domain, memory);
3376 const bke::CurvesGeometry &curves = info.drawing.strokes();
3377 const float4x4 layer_to_world = layer.to_world_space(*ob_eval);
3379 layer_to_world);
3380 const auto range_consumer = [&](const IndexRange range,
3381 const Span<float3> positions,
3382 const StringRef selection_attribute_name) {
3383 const IndexMask mask = ((selection_attribute_name == ".selection") ?
3384 elements :
3385 visible_handle_elements)
3386 .slice_content(range);
3387
3388 std::optional<ed::curves::FindClosestData> new_closest_elem =
3390 curves.points_by_curve(),
3391 positions,
3392 curves.cyclic(),
3393 projection,
3394 mask,
3395 selection_domain,
3396 mval,
3397 new_closest.elem);
3398 if (new_closest_elem) {
3399 new_closest.selection_attribute_name = selection_attribute_name;
3400 new_closest.elem = *new_closest_elem;
3401 new_closest.info_index = i;
3402 new_closest.drawing = &info.drawing;
3403 }
3404 };
3405
3406 if (selection_domain == bke::AttrDomain::Point) {
3408 curves,
3409 deformation,
3411 range_consumer);
3412 }
3413 else if (selection_domain == bke::AttrDomain::Curve) {
3415 curves,
3416 deformation,
3418 range_consumer);
3419 };
3420 }
3421 return new_closest;
3422 },
3424 return (a.elem.distance_sq < b.elem.distance_sq) ? a : b;
3425 });
3426
3427 std::atomic<bool> deselected = false;
3428 if (params.deselect_all || params.sel_op == SEL_OP_SET) {
3429 threading::parallel_for(drawings.index_range(), 1L, [&](const IndexRange range) {
3430 for (const int i : range) {
3431 ed::greasepencil::MutableDrawingInfo info = drawings[i];
3432 IndexMaskMemory memory;
3433 const IndexMask elements = ed::greasepencil::retrieve_editable_elements(
3434 *object, info, selection_domain, memory);
3435 if (elements.is_empty()) {
3436 continue;
3437 }
3438 bke::CurvesGeometry &curves = info.drawing.strokes_for_write();
3439 if (!ed::curves::has_anything_selected(curves, selection_domain, elements)) {
3440 continue;
3441 }
3442
3443 ed::curves::foreach_selection_attribute_writer(
3444 curves, selection_domain, [](bke::GSpanAttributeWriter &selection) {
3445 ed::curves::fill_selection_false(selection.span);
3446 });
3447
3448 deselected = true;
3449 }
3450 });
3451 }
3452
3453 if (!closest.drawing) {
3454 if (deselected) {
3455 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
3456 * generic attribute for now. */
3457 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
3458 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
3459 }
3460 return deselected;
3461 }
3462
3463 const IndexMask selection_mask = IndexRange::from_single(closest.elem.index);
3464
3466 params.sel_op,
3468 const IndexMask & /*universe*/,
3469 StringRef attribute_name,
3470 IndexMaskMemory & /*memory*/) -> IndexMask {
3471 /* Selection update mask is already known, but only applies
3472 * to a specific drawing. */
3473 if (&info.drawing == closest.drawing &&
3474 attribute_name == closest.selection_attribute_name) {
3475 return selection_mask;
3476 }
3477 return {};
3478 });
3479
3480 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
3481 * generic attribute for now. */
3482 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
3483 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
3484
3485 return true;
3486}
3487
3489{
3490 Scene *scene = CTX_data_scene(C);
3491 Object *obedit = CTX_data_edit_object(C);
3493
3496
3498
3499 bool center = RNA_boolean_get(op->ptr, "center");
3500 bool enumerate = RNA_boolean_get(op->ptr, "enumerate");
3501 /* Only force object select for edit-mode to support vertex parenting,
3502 * or paint-select to allow pose bone select with vert/face select. */
3503 bool object_only = (RNA_boolean_get(op->ptr, "object") &&
3504 (obedit || BKE_paint_select_elem_test(obact) ||
3505 /* so its possible to select bones in weight-paint mode (LMB select) */
3506 (obact && (obact->mode & OB_MODE_ALL_WEIGHT_PAINT) &&
3508
3509 /* This could be called "changed_or_found" since this is true when there is an element
3510 * under the cursor to select, even if it happens that the selection & active state doesn't
3511 * actually change. This is important so undo pushes are predictable. */
3512 bool changed = false;
3513 int mval[2];
3514
3515 if (object_only) {
3516 obedit = nullptr;
3517 obact = nullptr;
3518
3519 /* ack, this is incorrect but to do this correctly we would need an
3520 * alternative edit-mode/object-mode keymap, this copies the functionality
3521 * from 2.4x where Ctrl+Select in edit-mode does object select only. */
3522 center = false;
3523 }
3524
3525 if (obedit && enumerate) {
3526 /* Enumerate makes no sense in edit-mode unless also explicitly picking objects or bones.
3527 * Pass the event through so the event may be handled by loop-select for example. See: #100204.
3528 */
3529 if (obedit->type != OB_ARMATURE) {
3531 }
3532 }
3533
3534 RNA_int_get_array(op->ptr, "location", mval);
3535
3538
3539 if (obedit && object_only == false) {
3540 if (obedit->type == OB_MESH) {
3541 changed = EDBM_select_pick(C, mval, params);
3542 }
3543 else if (obedit->type == OB_ARMATURE) {
3544 if (enumerate) {
3545 GPUSelectBuffer buffer;
3546 const int hits = mixed_bones_object_selectbuffer(
3547 &vc, &buffer, mval, VIEW3D_SELECT_FILTER_NOP, false, true, false);
3548 changed = bone_mouse_select_menu(
3549 C, buffer.storage.as_span().take_front(hits), true, params);
3550 }
3551 if (!changed) {
3552 changed = ED_armature_edit_select_pick(C, mval, params);
3553 }
3554 }
3555 else if (obedit->type == OB_LATTICE) {
3556 changed = ED_lattice_select_pick(C, mval, params);
3557 }
3558 else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) {
3560 }
3561 else if (obedit->type == OB_MBALL) {
3562 changed = ED_mball_select_pick(C, mval, params);
3563 }
3564 else if (obedit->type == OB_FONT) {
3565 changed = ED_curve_editfont_select_pick(C, mval, params);
3566 }
3567 else if (obedit->type == OB_POINTCLOUD) {
3568 changed = pointcloud_select_pick(*C, mval, params);
3569 }
3570 else if (obedit->type == OB_CURVES) {
3571 changed = ed_curves_select_pick(*C, mval, params);
3572 }
3573 else if (obedit->type == OB_GREASE_PENCIL) {
3574 changed = ed_grease_pencil_select_pick(C, mval, params);
3575 }
3576 }
3577 else if (obact && obact->mode & OB_MODE_PARTICLE_EDIT) {
3578 changed = PE_mouse_particles(C, mval, params);
3579 }
3580 else if (obact && BKE_paint_select_face_test(obact)) {
3581 changed = paintface_mouse_select(C, mval, params, obact);
3582 }
3583 else if (BKE_paint_select_vert_test(obact)) {
3584 changed = ed_wpaint_vertex_select_pick(C, mval, params, obact);
3585 }
3586 else if (BKE_paint_select_grease_pencil_test(obact)) {
3587 changed = ed_grease_pencil_select_pick(C, mval, params);
3588 }
3589 else {
3590 changed = ed_object_select_pick(C, mval, params, center, enumerate, object_only);
3591 }
3592
3593 /* Pass-through flag may be cleared, see #WM_operator_flag_only_pass_through_on_press. */
3594
3595 /* Pass-through allows tweaks
3596 * FINISHED to signal one operator worked */
3597 if (changed) {
3600 }
3601 /* Nothing selected, just passthrough. */
3603}
3604
3606{
3607 RNA_int_set_array(op->ptr, "location", event->mval);
3608
3609 const wmOperatorStatus retval = view3d_select_exec(C, op);
3610
3611 return WM_operator_flag_only_pass_through_on_press(retval, event);
3612}
3613
3615{
3616 PropertyRNA *prop;
3617
3618 /* identifiers */
3619 ot->name = "Select";
3620 ot->description = "Select and activate item(s)";
3621 ot->idname = "VIEW3D_OT_select";
3622
3623 /* API callbacks. */
3624 ot->invoke = view3d_select_invoke;
3625 ot->exec = view3d_select_exec;
3627 ot->get_name = ED_select_pick_get_name;
3628
3629 /* flags */
3630 ot->flag = OPTYPE_UNDO;
3631
3632 /* properties */
3634
3635 prop = RNA_def_boolean(
3636 ot->srna,
3637 "center",
3638 false,
3639 "Center",
3640 "Use the object center when selecting, in edit mode used to extend object selection");
3642 prop = RNA_def_boolean(ot->srna,
3643 "enumerate",
3644 false,
3645 "Enumerate",
3646 "List objects under the mouse (object mode only)");
3648 prop = RNA_def_boolean(
3649 ot->srna, "object", false, "Object", "Use object selection (edit mode only)");
3651
3652 prop = RNA_def_int_vector(ot->srna,
3653 "location",
3654 2,
3655 nullptr,
3656 INT_MIN,
3657 INT_MAX,
3658 "Location",
3659 "Mouse location",
3660 INT_MIN,
3661 INT_MAX);
3663}
3664
3666
3667/* -------------------------------------------------------------------- */
3670
3683
3685 const ViewContext *vc,
3686 const rcti *rect,
3687 const eSelectOp sel_op)
3688{
3689 r_data->vc = vc;
3690
3691 r_data->rect = rect;
3692 r_data->rect_fl = &r_data->_rect_fl;
3693 BLI_rctf_rcti_copy(&r_data->_rect_fl, rect);
3694
3695 r_data->sel_op = sel_op;
3696 /* SELECT by default, but can be changed if needed (only few cases use and respect this). */
3698
3699 /* runtime */
3700 r_data->is_done = false;
3701 r_data->is_changed = false;
3702}
3703
3704bool edge_inside_circle(const float cent[2],
3705 float radius,
3706 const float screen_co_a[2],
3707 const float screen_co_b[2])
3708{
3709 const float radius_squared = radius * radius;
3710 return (dist_squared_to_line_segment_v2(cent, screen_co_a, screen_co_b) < radius_squared);
3711}
3712
3717static void do_paintvert_box_select__doSelectVert(void *user_data,
3718 const float screen_co[2],
3719 int index)
3720{
3722 static_cast<BoxSelectUserData_ForMeshObjectVert *>(user_data);
3723 BoxSelectUserData *data = &mesh_data->box_data;
3724 const bool is_select = mesh_data->select_vert[index];
3725 const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co);
3726 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
3727 if (sel_op_result != -1) {
3728 mesh_data->select_vert[index] = sel_op_result == 1;
3729 data->is_changed = true;
3730 }
3731}
3733 wmGenericUserData *wm_userdata,
3734 const rcti *rect,
3735 const eSelectOp sel_op)
3736{
3737 using namespace blender;
3738 const bool use_zbuf = !XRAY_ENABLED(vc->v3d);
3739
3740 Mesh *mesh = static_cast<Mesh *>(vc->obact->data);
3741 if ((mesh == nullptr) || (mesh->verts_num == 0)) {
3742 return false;
3743 }
3744
3745 bool changed = false;
3746 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
3747 changed |= paintvert_deselect_all_visible(vc->obact, SEL_DESELECT, false);
3748 }
3749
3750 if (BLI_rcti_is_empty(rect)) {
3751 /* pass */
3752 }
3753 else if (use_zbuf) {
3754 EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
3755 if (wm_userdata->data == nullptr) {
3757 esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
3759 vc->depsgraph, vc->region, vc->v3d, rect, nullptr);
3760 }
3761 if (esel->select_bitmap != nullptr) {
3762 changed |= edbm_backbuf_check_and_select_verts_obmode(mesh, esel, sel_op);
3763 }
3764 }
3765 else {
3766 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
3767 bke::SpanAttributeWriter<bool> select_vert = attributes.lookup_or_add_for_write_span<bool>(
3768 ".select_vert", bke::AttrDomain::Point);
3769
3771 data.select_vert = select_vert.span;
3772
3773 view3d_userdata_boxselect_init(&data.box_data, vc, rect, sel_op);
3774
3776
3779 changed |= data.box_data.is_changed;
3780 select_vert.finish();
3781 }
3782
3783 if (changed) {
3784 if (SEL_OP_CAN_DESELECT(sel_op)) {
3786 }
3789 }
3790 return changed;
3791}
3792
3794 wmGenericUserData *wm_userdata,
3795 const rcti *rect,
3796 eSelectOp sel_op)
3797{
3798 Object *ob = vc->obact;
3799 Mesh *mesh;
3800
3802 if ((mesh == nullptr) || (mesh->faces_num == 0)) {
3803 return false;
3804 }
3805
3806 bool changed = false;
3807 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
3808 changed |= paintface_deselect_all_visible(vc->C, vc->obact, SEL_DESELECT, false);
3809 }
3810
3811 if (BLI_rcti_is_empty(rect)) {
3812 /* pass */
3813 }
3814 else {
3815 EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
3816 if (wm_userdata->data == nullptr) {
3818 esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
3820 vc->depsgraph, vc->region, vc->v3d, rect, nullptr);
3821 }
3822 if (esel->select_bitmap != nullptr) {
3823 changed |= edbm_backbuf_check_and_select_faces_obmode(mesh, esel, sel_op);
3824 }
3825 }
3826
3827 if (changed) {
3828 paintface_flush_flags(vc->C, vc->obact, true, false);
3829 }
3830 return changed;
3831}
3832
3833static void do_nurbs_box_select__doSelect(void *user_data,
3834 Nurb * /*nu*/,
3835 BPoint *bp,
3836 BezTriple *bezt,
3837 int beztindex,
3838 bool handles_visible,
3839 const float screen_co[2])
3840{
3841 BoxSelectUserData *data = static_cast<BoxSelectUserData *>(user_data);
3842
3843 const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co);
3844 if (bp) {
3845 const bool is_select = bp->f1 & SELECT;
3846 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
3847 if (sel_op_result != -1) {
3848 SET_FLAG_FROM_TEST(bp->f1, sel_op_result, data->select_flag);
3849 data->is_changed = true;
3850 }
3851 }
3852 else {
3853 if (!handles_visible) {
3854 /* can only be (beztindex == 1) here since handles are hidden */
3855 const bool is_select = bezt->f2 & SELECT;
3856 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
3857 if (sel_op_result != -1) {
3858 SET_FLAG_FROM_TEST(bezt->f2, sel_op_result, data->select_flag);
3859 data->is_changed = true;
3860 }
3861 bezt->f1 = bezt->f3 = bezt->f2;
3862 }
3863 else {
3864 uint8_t *flag_p = (&bezt->f1) + beztindex;
3865 const bool is_select = *flag_p & SELECT;
3866 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
3867 if (sel_op_result != -1) {
3868 SET_FLAG_FROM_TEST(*flag_p, sel_op_result, data->select_flag);
3869 data->is_changed = true;
3870 }
3871 }
3872 }
3873}
3874static bool do_nurbs_box_select(const ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
3875{
3876 const bool deselect_all = (sel_op == SEL_OP_SET);
3878
3879 view3d_userdata_boxselect_init(&data, vc, rect, sel_op);
3880
3881 Curve *curve = (Curve *)vc->obedit->data;
3882 ListBase *nurbs = BKE_curve_editNurbs_get(curve);
3883
3884 /* For deselect all, items to be selected are tagged with temp flag. Clear that first. */
3885 if (deselect_all) {
3887 data.select_flag = BEZT_FLAG_TEMP_TAG;
3888 }
3889
3890 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
3892
3893 /* Deselect items that were not added to selection (indicated by temp flag). */
3894 if (deselect_all) {
3896 }
3897
3899
3900 return data.is_changed;
3901}
3902
3903static void do_lattice_box_select__doSelect(void *user_data, BPoint *bp, const float screen_co[2])
3904{
3905 BoxSelectUserData *data = static_cast<BoxSelectUserData *>(user_data);
3906 const bool is_select = bp->f1 & SELECT;
3907 const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co);
3908 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
3909 if (sel_op_result != -1) {
3910 SET_FLAG_FROM_TEST(bp->f1, sel_op_result, SELECT);
3911 data->is_changed = true;
3912 }
3913}
3914static bool do_lattice_box_select(const ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
3915{
3917
3918 view3d_userdata_boxselect_init(&data, vc, rect, sel_op);
3919
3920 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
3921 data.is_changed |= ED_lattice_flags_set(vc->obedit, 0);
3922 }
3923
3924 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
3927
3928 return data.is_changed;
3929}
3930
3931static void do_mesh_box_select__doSelectVert(void *user_data,
3932 BMVert *eve,
3933 const float screen_co[2],
3934 int /*index*/)
3935{
3936 BoxSelectUserData *data = static_cast<BoxSelectUserData *>(user_data);
3937 const bool is_select = BM_elem_flag_test(eve, BM_ELEM_SELECT);
3938 const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co);
3939 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
3940 if (sel_op_result != -1) {
3941 BM_vert_select_set(data->vc->em->bm, eve, sel_op_result);
3942 data->is_changed = true;
3943 }
3944}
3950
3953static void do_mesh_box_select__doSelectEdge_pass0(void *user_data,
3954 BMEdge *eed,
3955 const float screen_co_a[2],
3956 const float screen_co_b[2],
3957 int index)
3958{
3959 BoxSelectUserData_ForMeshEdge *data_for_edge = static_cast<BoxSelectUserData_ForMeshEdge *>(
3960 user_data);
3961 BoxSelectUserData *data = data_for_edge->data;
3962 bool is_visible = true;
3963 if (data_for_edge->backbuf_offset) {
3964 uint bitmap_inedx = data_for_edge->backbuf_offset + index - 1;
3965 is_visible = BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, bitmap_inedx);
3966 }
3967
3968 const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT);
3969 const bool is_inside = (is_visible &&
3970 edge_fully_inside_rect(data->rect_fl, screen_co_a, screen_co_b));
3971 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
3972 if (sel_op_result != -1) {
3973 BM_edge_select_set(data->vc->em->bm, eed, sel_op_result);
3974 data->is_done = true;
3975 data->is_changed = true;
3976 }
3977}
3978
3981static void do_mesh_box_select__doSelectEdge_pass1(void *user_data,
3982 BMEdge *eed,
3983 const float screen_co_a[2],
3984 const float screen_co_b[2],
3985 int index)
3986{
3987 BoxSelectUserData_ForMeshEdge *data_for_edge = static_cast<BoxSelectUserData_ForMeshEdge *>(
3988 user_data);
3989 BoxSelectUserData *data = data_for_edge->data;
3990 bool is_visible = true;
3991 if (data_for_edge->backbuf_offset) {
3992 uint bitmap_inedx = data_for_edge->backbuf_offset + index - 1;
3993 is_visible = BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, bitmap_inedx);
3994 }
3995
3996 const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT);
3997 const bool is_inside = (is_visible && edge_inside_rect(data->rect_fl, screen_co_a, screen_co_b));
3998 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
3999 if (sel_op_result != -1) {
4000 BM_edge_select_set(data->vc->em->bm, eed, sel_op_result);
4001 data->is_changed = true;
4002 }
4003}
4004static void do_mesh_box_select__doSelectFace(void *user_data,
4005 BMFace *efa,
4006 const float screen_co[2],
4007 int /*index*/)
4008{
4009 BoxSelectUserData *data = static_cast<BoxSelectUserData *>(user_data);
4010 const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT);
4011 const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co);
4012 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
4013 if (sel_op_result != -1) {
4014 BM_face_select_set(data->vc->em->bm, efa, sel_op_result);
4015 data->is_changed = true;
4016 }
4017}
4018static bool do_mesh_box_select(const ViewContext *vc,
4019 wmGenericUserData *wm_userdata,
4020 const rcti *rect,
4021 const eSelectOp sel_op)
4022{
4024 ToolSettings *ts = vc->scene->toolsettings;
4025
4026 view3d_userdata_boxselect_init(&data, vc, rect, sel_op);
4027
4028 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
4029 if (vc->em->bm->totvertsel) {
4031 data.is_changed = true;
4032 }
4033 }
4034
4035 /* for non zbuf projections, don't change the GL state */
4037
4039
4040 const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d);
4041
4042 EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
4043 if (use_zbuf) {
4044 if (wm_userdata->data == nullptr) {
4046 esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
4048 vc->depsgraph, vc->region, vc->v3d, rect, nullptr);
4049 }
4050 }
4051
4052 if (ts->selectmode & SCE_SELECT_VERTEX) {
4053 if (use_zbuf) {
4055 esel, vc->depsgraph, vc->obedit, vc->em, sel_op);
4056 }
4057 else {
4060 }
4061 }
4062 if (ts->selectmode & SCE_SELECT_EDGE) {
4063 /* Does both use_zbuf and non-use_zbuf versions (need screen cos for both) */
4065 cb_data.data = &data;
4066 cb_data.esel = use_zbuf ? esel : nullptr;
4068 vc->depsgraph, vc->obedit, SCE_SELECT_EDGE) :
4069 0;
4070
4071 const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_NEAR |
4072 (use_zbuf ? (eV3DProjTest)0 : V3D_PROJ_TEST_CLIP_BB);
4073 /* Fully inside. */
4075 vc, do_mesh_box_select__doSelectEdge_pass0, &cb_data, clip_flag);
4076 if (data.is_done == false) {
4077 /* Fall back to partially inside.
4078 * Clip content to account for edges partially behind the view. */
4081 &cb_data,
4083 }
4084 }
4085
4086 if (ts->selectmode & SCE_SELECT_FACE) {
4087 if (use_zbuf) {
4089 esel, vc->depsgraph, vc->obedit, vc->em, sel_op);
4090 }
4091 else {
4094 }
4095 }
4096
4097 if (data.is_changed) {
4099 }
4100 return data.is_changed;
4101}
4102
4103static bool do_meta_box_select(const ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
4104{
4105 Object *ob = vc->obedit;
4106 MetaBall *mb = (MetaBall *)ob->data;
4107 MetaElem *ml;
4108 int a;
4109 bool changed = false;
4110
4111 GPUSelectBuffer buffer;
4112 int hits;
4113
4115
4116 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
4117 changed |= BKE_mball_deselect_all(mb);
4118 }
4119
4120 int metaelem_id = 0;
4121 for (ml = static_cast<MetaElem *>(mb->editelems->first); ml;
4122 ml = ml->next, metaelem_id += 0x10000)
4123 {
4124 bool is_inside_radius = false;
4125 bool is_inside_stiff = false;
4126
4127 for (a = 0; a < hits; a++) {
4128 const int select_id = buffer.storage[a].id;
4129
4130 if (select_id == -1) {
4131 continue;
4132 }
4133
4134 const uint hit_object = select_id & 0xFFFF;
4135 if (vc->obedit->runtime->select_id != hit_object) {
4136 continue;
4137 }
4138
4139 if (metaelem_id != (select_id & 0xFFFF0000 & ~MBALLSEL_ANY)) {
4140 continue;
4141 }
4142
4143 if (select_id & MBALLSEL_RADIUS) {
4144 is_inside_radius = true;
4145 break;
4146 }
4147
4148 if (select_id & MBALLSEL_STIFF) {
4149 is_inside_stiff = true;
4150 break;
4151 }
4152 }
4153 const int flag_prev = ml->flag;
4154 if (is_inside_radius) {
4155 ml->flag |= MB_SCALE_RAD;
4156 }
4157 if (is_inside_stiff) {
4158 ml->flag &= ~MB_SCALE_RAD;
4159 }
4160
4161 const bool is_select = (ml->flag & SELECT);
4162 const bool is_inside = is_inside_radius || is_inside_stiff;
4163
4164 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
4165 if (sel_op_result != -1) {
4166 SET_FLAG_FROM_TEST(ml->flag, sel_op_result, SELECT);
4167 }
4168 changed |= (flag_prev != ml->flag);
4169 }
4170
4171 return changed;
4172}
4173
4174static bool do_armature_box_select(const ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
4175{
4176 bool changed = false;
4177 int a;
4178
4179 GPUSelectBuffer buffer;
4180 int hits;
4181
4183
4185 vc->scene, vc->view_layer, vc->v3d);
4186
4187 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
4189 }
4190
4191 for (Base *base : bases) {
4192 Object *obedit = base->object;
4193 obedit->id.tag &= ~ID_TAG_DOIT;
4194
4195 bArmature *arm = static_cast<bArmature *>(obedit->data);
4197 }
4198
4199 /* first we only check points inside the border */
4200 for (a = 0; a < hits; a++) {
4201 const int select_id = buffer.storage[a].id;
4202 if (select_id != -1) {
4203 if ((select_id & 0xFFFF0000) == 0) {
4204 continue;
4205 }
4206
4207 EditBone *ebone;
4208 Base *base_edit = ED_armature_base_and_ebone_from_select_buffer(bases, select_id, &ebone);
4209 ebone->temp.i |= select_id & BONESEL_ANY;
4210 base_edit->object->id.tag |= ID_TAG_DOIT;
4211 }
4212 }
4213
4214 for (Base *base : bases) {
4215 Object *obedit = base->object;
4216 if (obedit->id.tag & ID_TAG_DOIT) {
4217 obedit->id.tag &= ~ID_TAG_DOIT;
4218 changed |= ED_armature_edit_select_op_from_tagged(static_cast<bArmature *>(obedit->data),
4219 sel_op);
4220 }
4221 }
4222
4223 return changed;
4224}
4225
4230static int gpu_bone_select_buffer_cmp(const void *sel_a_p, const void *sel_b_p)
4231{
4232 uint sel_a = ((GPUSelectResult *)sel_a_p)->id;
4233 uint sel_b = ((GPUSelectResult *)sel_b_p)->id;
4234
4235#ifdef __BIG_ENDIAN__
4238#endif
4239
4240 if (sel_a < sel_b) {
4241 return -1;
4242 }
4243 if (sel_a > sel_b) {
4244 return 1;
4245 }
4246 return 0;
4247}
4248
4254
4256 const ViewContext *vc,
4257 const rcti *rect,
4258 const eSelectOp sel_op)
4259{
4260 View3D *v3d = vc->v3d;
4261
4262 GPUSelectBuffer buffer;
4264 vc->obact);
4265 const int hits = view3d_gpu_select(vc, &buffer, rect, VIEW3D_SELECT_ALL, select_filter);
4268 base->object->id.tag &= ~ID_TAG_DOIT;
4269 }
4270
4271 bool changed = false;
4272 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
4273 changed |= object_deselect_all_visible(vc->scene, vc->view_layer, vc->v3d);
4274 }
4275
4277 if ((hits == -1) && !SEL_OP_USE_OUTSIDE(sel_op)) {
4278 if (changed) {
4280 return true;
4281 }
4282 }
4283
4284 blender::Map<uint32_t, Base *> base_by_object_select_id;
4285 LISTBASE_FOREACH (Base *, base, object_bases) {
4286 if (BASE_SELECTABLE(v3d, base)) {
4287 const uint32_t select_id = base->object->runtime->select_id;
4288 if ((select_id & 0x0000FFFF) != 0) {
4289 const uint hit_object = select_id & 0xFFFF;
4290 base_by_object_select_id.add(hit_object, base);
4291 }
4292 }
4293 }
4294
4295 /* The draw order doesn't always match the order we populate the engine, see: #51695. */
4296 qsort(buffer.storage.data(), hits, sizeof(GPUSelectResult), gpu_bone_select_buffer_cmp);
4297
4298 blender::Set<Base *> bases_inside;
4299 for (const GPUSelectResult *buf_iter = buffer.storage.data(), *buf_end = buf_iter + hits;
4300 buf_iter < buf_end;
4301 buf_iter++)
4302 {
4303 const uint32_t select_id = buf_iter->id;
4304 const uint32_t hit_object = select_id & 0xFFFF;
4305 if (Base *base = base_by_object_select_id.lookup_default(hit_object, nullptr)) {
4306 bases_inside.add(base);
4307 }
4308 }
4309
4310 for (Base *base = static_cast<Base *>(object_bases->first); base && hits; base = base->next) {
4311 if (BASE_SELECTABLE(v3d, base)) {
4312 const bool is_select = base->flag & BASE_SELECTED;
4313 const bool is_inside = bases_inside.contains(base);
4314 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
4315 if (sel_op_result != -1) {
4317 sel_op_result ? blender::ed::object::BA_SELECT :
4319 changed = true;
4320 }
4321 }
4322 }
4323
4324 if (changed) {
4326 }
4327 return changed;
4328}
4329
4331 const ViewContext *vc,
4332 const rcti *rect,
4333 const eSelectOp sel_op)
4334{
4336
4337 /* Selection buffer has bones potentially too. */
4338 GPUSelectBuffer buffer;
4340 vc->obact);
4341 const int hits = view3d_gpu_select(vc, &buffer, rect, VIEW3D_SELECT_ALL, select_filter);
4342 /*
4343 * NOTE(@theeth): Regarding the logic use here.
4344 * The buffer and #ListBase have the same relative order, which makes the selection
4345 * very simple. Loop through both data sets at the same time, if the color
4346 * is the same as the object, we have a hit and can move to the next color
4347 * and object pair, if not, just move to the next object,
4348 * keeping the same color until we have a hit. */
4349
4350 if (hits > 0) {
4351 /* no need to loop if there's no hit */
4352
4353 /* The draw order doesn't always match the order we populate the engine, see: #51695. */
4354 qsort(buffer.storage.data(), hits, sizeof(GPUSelectResult), gpu_bone_select_buffer_cmp);
4355
4356 for (const GPUSelectResult *buf_iter = buffer.storage.data(), *buf_end = buf_iter + hits;
4357 buf_iter < buf_end;
4358 buf_iter++)
4359 {
4360 Bone *bone;
4361 Base *base = ED_armature_base_and_bone_from_select_buffer(bases, buf_iter->id, &bone);
4362
4363 if (base == nullptr) {
4364 continue;
4365 }
4366
4367 /* Loop over contiguous bone hits for 'base'. */
4368 for (; buf_iter != buf_end; buf_iter++) {
4369 /* should never fail */
4370 if (bone != nullptr) {
4371 base->object->id.tag |= ID_TAG_DOIT;
4372 bone->flag |= BONE_DONE;
4373 }
4374
4375 /* Select the next bone if we're not switching bases. */
4376 if (buf_iter + 1 != buf_end) {
4377 const GPUSelectResult *col_next = buf_iter + 1;
4378 if ((base->object->runtime->select_id & 0x0000FFFF) != (col_next->id & 0x0000FFFF)) {
4379 break;
4380 }
4381 if (base->object->pose != nullptr) {
4382 const uint hit_bone = (col_next->id & ~BONESEL_ANY) >> 16;
4383 bPoseChannel *pchan = static_cast<bPoseChannel *>(
4384 BLI_findlink(&base->object->pose->chanbase, hit_bone));
4385 bone = pchan ? pchan->bone : nullptr;
4386 }
4387 else {
4388 bone = nullptr;
4389 }
4390 }
4391 }
4392 }
4393 }
4394
4395 const bool changed_multi = do_pose_tag_select_op_exec(bases, sel_op);
4396 if (changed_multi) {
4399 }
4400
4401 return changed_multi;
4402}
4403
4405 const rcti *rect,
4406 const eSelectOp sel_op)
4407{
4408 using namespace blender;
4409 Object *object = (vc->obedit ? vc->obedit : vc->obact);
4410 const Object *ob_eval = DEG_get_evaluated(vc->depsgraph, object);
4411 const GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
4413 vc->scene->toolsettings, object);
4414
4416 vc,
4417 sel_op,
4419 const IndexMask &mask,
4420 const StringRef attribute_name,
4421 IndexMaskMemory &memory) {
4423 const bke::greasepencil::Layer &layer = grease_pencil.layer(info.layer_index);
4424 const bke::crazyspace::GeometryDeformation deformation =
4426 ob_eval, *object, info.drawing);
4427 const IndexMask visible_handle_elements =
4429 *object, info.drawing, info.layer_index, selection_domain, memory);
4430 const float4x4 layer_to_world = layer.to_world_space(*ob_eval);
4432 layer_to_world);
4433
4434 return ed::curves::select_box_mask(*vc,
4435 curves,
4436 deformation,
4437 projection,
4438 mask,
4439 visible_handle_elements,
4440 selection_domain,
4441 attribute_name,
4442 *rect,
4443 memory);
4444 });
4445}
4446
4448{
4449 using namespace blender;
4451 rcti rect;
4452 bool changed_multi = false;
4453
4454 wmGenericUserData wm_userdata_buf = {nullptr, nullptr, false};
4455 wmGenericUserData *wm_userdata = &wm_userdata_buf;
4456
4459
4460 /* setup view context for argument to callbacks */
4462
4463 eSelectOp sel_op = static_cast<eSelectOp>(RNA_enum_get(op->ptr, "mode"));
4465
4466 if (vc.obedit) {
4468 vc.scene, vc.view_layer, vc.v3d, vc.obedit->type, vc.obedit->mode, ob_iter)
4469 {
4471 bool changed = false;
4472
4473 switch (vc.obedit->type) {
4474 case OB_MESH:
4476 changed = do_mesh_box_select(&vc, wm_userdata, &rect, sel_op);
4477 if (changed) {
4478 DEG_id_tag_update(static_cast<ID *>(vc.obedit->data), ID_RECALC_SELECT);
4480 }
4481 break;
4482 case OB_CURVES_LEGACY:
4483 case OB_SURF:
4484 changed = do_nurbs_box_select(&vc, &rect, sel_op);
4485 if (changed) {
4486 DEG_id_tag_update(static_cast<ID *>(vc.obedit->data), ID_RECALC_SELECT);
4488 }
4489 break;
4490 case OB_MBALL:
4491 changed = do_meta_box_select(&vc, &rect, sel_op);
4492 if (changed) {
4493 DEG_id_tag_update(static_cast<ID *>(vc.obedit->data), ID_RECALC_SELECT);
4495 }
4496 break;
4497 case OB_ARMATURE:
4498 changed = do_armature_box_select(&vc, &rect, sel_op);
4499 if (changed) {
4503 }
4504 break;
4505 case OB_LATTICE:
4506 changed = do_lattice_box_select(&vc, &rect, sel_op);
4507 if (changed) {
4508 DEG_id_tag_update(static_cast<ID *>(vc.obedit->data), ID_RECALC_SELECT);
4510 }
4511 break;
4512 case OB_CURVES: {
4513 Curves &curves_id = *static_cast<Curves *>(vc.obedit->data);
4514 bke::CurvesGeometry &curves = curves_id.geometry.wrap();
4517 const bke::AttrDomain selection_domain = bke::AttrDomain(curves_id.selection_domain);
4518 const float4x4 projection = ED_view3d_ob_project_mat_get(vc.rv3d, vc.obedit);
4519 const IndexRange elements(curves.attributes().domain_size(selection_domain));
4520 changed = ed::curves::select_box(vc,
4521 curves,
4522 deformation,
4523 projection,
4524 elements,
4525 elements,
4526 selection_domain,
4527 rect,
4528 sel_op);
4529 if (changed) {
4530 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
4531 * generic attribute for now. */
4532 DEG_id_tag_update(static_cast<ID *>(vc.obedit->data), ID_RECALC_GEOMETRY);
4534 }
4535 break;
4536 }
4537 case OB_POINTCLOUD: {
4538 PointCloud &pointcloud = *static_cast<PointCloud *>(vc.obedit->data);
4539 const float4x4 projection = ED_view3d_ob_project_mat_get(vc.rv3d, vc.obedit);
4540 changed = ed::pointcloud::select_box(pointcloud, *vc.region, projection, rect, sel_op);
4541 if (changed) {
4542 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
4543 * generic attribute for now. */
4544 DEG_id_tag_update(static_cast<ID *>(vc.obedit->data), ID_RECALC_GEOMETRY);
4546 }
4547 break;
4548 }
4549 case OB_GREASE_PENCIL: {
4550 changed = do_grease_pencil_box_select(&vc, &rect, sel_op);
4551 break;
4552 }
4553 default:
4554 BLI_assert_msg(0, "box select on incorrect object type");
4555 break;
4556 }
4557 changed_multi |= changed;
4558 }
4560 }
4561 else { /* No edit-mode, unified for bones and objects. */
4562 if (vc.obact && BKE_paint_select_face_test(vc.obact)) {
4563 changed_multi = do_paintface_box_select(&vc, wm_userdata, &rect, sel_op);
4564 }
4565 else if (vc.obact && BKE_paint_select_vert_test(vc.obact)) {
4566 changed_multi = do_paintvert_box_select(&vc, wm_userdata, &rect, sel_op);
4567 }
4568 else if (vc.obact && BKE_paint_select_grease_pencil_test(vc.obact)) {
4569 changed_multi = do_grease_pencil_box_select(&vc, &rect, sel_op);
4570 }
4571 else if (vc.obact && vc.obact->mode & OB_MODE_PARTICLE_EDIT) {
4572 changed_multi = PE_box_select(C, &rect, sel_op);
4573 }
4574 else if (vc.obact && ((vc.obact->mode & OB_MODE_POSE) ||
4575 ((vc.obact->mode & OB_MODE_WEIGHT_PAINT) &&
4577 {
4578 changed_multi = do_pose_box_select(C, &vc, &rect, sel_op);
4579 if (changed_multi) {
4581 }
4582 }
4583 else { /* object mode with none active */
4584 changed_multi = do_object_box_select(C, &vc, &rect, sel_op);
4585 if (changed_multi) {
4587 }
4588 }
4589 }
4590
4591 WM_generic_user_data_free(wm_userdata);
4592
4593 if (changed_multi) {
4594 return OPERATOR_FINISHED;
4595 }
4596 return OPERATOR_CANCELLED;
4597}
4598
4600{
4601 /* identifiers */
4602 ot->name = "Box Select";
4603 ot->description = "Select items using box selection";
4604 ot->idname = "VIEW3D_OT_select_box";
4605
4606 /* API callbacks. */
4607 ot->invoke = WM_gesture_box_invoke;
4608 ot->exec = view3d_box_select_exec;
4609 ot->modal = WM_gesture_box_modal;
4610 ot->poll = view3d_selectable_data;
4611 ot->cancel = WM_gesture_box_cancel;
4612
4613 /* flags */
4614 ot->flag = OPTYPE_UNDO;
4615
4616 /* rna */
4619}
4620
4622
4623/* -------------------------------------------------------------------- */
4626
4630 int mval[2];
4631 float mval_fl[2];
4632 float radius;
4635
4636 /* runtime */
4638};
4639
4641 const ViewContext *vc,
4642 const bool select,
4643 const int mval[2],
4644 const float rad)
4645{
4646 r_data->vc = vc;
4647 r_data->select = select;
4648 copy_v2_v2_int(r_data->mval, mval);
4649 r_data->mval_fl[0] = mval[0];
4650 r_data->mval_fl[1] = mval[1];
4651
4652 r_data->radius = rad;
4653 r_data->radius_squared = rad * rad;
4654
4655 /* SELECT by default, but can be changed if needed (only few cases use and respect this). */
4657
4658 /* runtime */
4659 r_data->is_changed = false;
4660}
4661
4662static void mesh_circle_doSelectVert(void *user_data,
4663 BMVert *eve,
4664 const float screen_co[2],
4665 int /*index*/)
4666{
4667 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
4668
4669 if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
4670 BM_vert_select_set(data->vc->em->bm, eve, data->select);
4671 data->is_changed = true;
4672 }
4673}
4674static void mesh_circle_doSelectEdge(void *user_data,
4675 BMEdge *eed,
4676 const float screen_co_a[2],
4677 const float screen_co_b[2],
4678 int /*index*/)
4679{
4680 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
4681
4682 if (edge_inside_circle(data->mval_fl, data->radius, screen_co_a, screen_co_b)) {
4683 BM_edge_select_set(data->vc->em->bm, eed, data->select);
4684 data->is_changed = true;
4685 }
4686}
4687static void mesh_circle_doSelectFace(void *user_data,
4688 BMFace *efa,
4689 const float screen_co[2],
4690 int /*index*/)
4691{
4692 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
4693
4694 if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
4695 BM_face_select_set(data->vc->em->bm, efa, data->select);
4696 data->is_changed = true;
4697 }
4698}
4699
4700static bool mesh_circle_select(const ViewContext *vc,
4701 wmGenericUserData *wm_userdata,
4702 eSelectOp sel_op,
4703 const int mval[2],
4704 float rad)
4705{
4706 ToolSettings *ts = vc->scene->toolsettings;
4709
4710 bool changed = false;
4711 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
4712 if (vc->em->bm->totvertsel) {
4714 vc->em->bm->totvertsel = 0;
4715 vc->em->bm->totedgesel = 0;
4716 vc->em->bm->totfacesel = 0;
4717 changed = true;
4718 }
4719 }
4720 const bool select = (sel_op != SEL_OP_SUB);
4721
4722 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
4723
4725
4726 const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d);
4727
4728 if (use_zbuf) {
4729 if (wm_userdata->data == nullptr) {
4731 }
4732 }
4733 EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
4734
4735 if (use_zbuf) {
4736 if (esel->select_bitmap == nullptr) {
4738 vc->depsgraph, vc->region, vc->v3d, mval, int(rad + 1.0f), nullptr);
4739 }
4740 }
4741
4742 if (ts->selectmode & SCE_SELECT_VERTEX) {
4743 if (use_zbuf) {
4744 if (esel->select_bitmap != nullptr) {
4746 esel, vc->depsgraph, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB);
4747 }
4748 }
4749 else {
4751 }
4752 }
4753
4754 if (ts->selectmode & SCE_SELECT_EDGE) {
4755 if (use_zbuf) {
4756 if (esel->select_bitmap != nullptr) {
4758 esel, vc->depsgraph, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB);
4759 }
4760 }
4761 else {
4763 vc,
4765 &data,
4767 }
4768 }
4769
4770 if (ts->selectmode & SCE_SELECT_FACE) {
4771 if (use_zbuf) {
4772 if (esel->select_bitmap != nullptr) {
4774 esel, vc->depsgraph, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB);
4775 }
4776 }
4777 else {
4779 }
4780 }
4781
4782 changed |= data.is_changed;
4783
4784 if (changed) {
4787 }
4788 return changed;
4789}
4790
4792 wmGenericUserData *wm_userdata,
4793 const eSelectOp sel_op,
4794 const int mval[2],
4795 float rad)
4796{
4798 Object *ob = vc->obact;
4799 Mesh *mesh = static_cast<Mesh *>(ob->data);
4800
4801 bool changed = false;
4802 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
4803 /* flush selection at the end */
4804 changed |= paintface_deselect_all_visible(vc->C, ob, SEL_DESELECT, false);
4805 }
4806
4807 if (wm_userdata->data == nullptr) {
4809 }
4810
4811 {
4812 EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
4814 vc->depsgraph, vc->region, vc->v3d, mval, int(rad + 1.0f), nullptr);
4815 if (esel->select_bitmap != nullptr) {
4816 changed |= edbm_backbuf_check_and_select_faces_obmode(mesh, esel, sel_op);
4817 MEM_freeN(esel->select_bitmap);
4818 esel->select_bitmap = nullptr;
4819 }
4820 }
4821
4822 if (changed) {
4823 paintface_flush_flags(vc->C, ob, true, false);
4824 }
4825 return changed;
4826}
4827
4833 const float screen_co[2],
4834 int index)
4835{
4837 static_cast<CircleSelectUserData_ForMeshObjectVert *>(user_data);
4838 CircleSelectUserData *data = &mesh_data->circle_data;
4839
4840 if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
4841 mesh_data->select_vert[index] = data->select;
4842 data->is_changed = true;
4843 }
4844}
4846 wmGenericUserData *wm_userdata,
4847 const eSelectOp sel_op,
4848 const int mval[2],
4849 float rad)
4850{
4851 using namespace blender;
4853 const bool use_zbuf = !XRAY_ENABLED(vc->v3d);
4854 Object *ob = vc->obact;
4855 Mesh *mesh = static_cast<Mesh *>(ob->data);
4856 // CircleSelectUserData data = {nullptr}; /* UNUSED. */
4857
4858 bool changed = false;
4859 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
4860 /* Flush selection at the end. */
4861 changed |= paintvert_deselect_all_visible(ob, SEL_DESELECT, false);
4862 }
4863
4864 const bool select = (sel_op != SEL_OP_SUB);
4865
4866 if (use_zbuf) {
4867 if (wm_userdata->data == nullptr) {
4869 }
4870 }
4871
4872 if (use_zbuf) {
4873 EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
4875 vc->depsgraph, vc->region, vc->v3d, mval, int(rad + 1.0f), nullptr);
4876 if (esel->select_bitmap != nullptr) {
4877 changed |= edbm_backbuf_check_and_select_verts_obmode(mesh, esel, sel_op);
4878 MEM_freeN(esel->select_bitmap);
4879 esel->select_bitmap = nullptr;
4880 }
4881 }
4882 else {
4883 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
4884 bke::SpanAttributeWriter<bool> select_vert = attributes.lookup_or_add_for_write_span<bool>(
4885 ".select_vert", bke::AttrDomain::Point);
4886
4888 data.select_vert = select_vert.span;
4889
4890 ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d); /* for foreach's screen/vert projection */
4891
4892 view3d_userdata_circleselect_init(&data.circle_data, vc, select, mval, rad);
4895 changed |= data.circle_data.is_changed;
4896 select_vert.finish();
4897 }
4898
4899 if (changed) {
4900 if (sel_op == SEL_OP_SUB) {
4902 }
4905 }
4906 return changed;
4907}
4908
4909static void nurbscurve_circle_doSelect(void *user_data,
4910 Nurb * /*nu*/,
4911 BPoint *bp,
4912 BezTriple *bezt,
4913 int beztindex,
4914 bool /*handles_visible*/,
4915 const float screen_co[2])
4916{
4917 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
4918
4919 if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
4920 if (bp) {
4921 SET_FLAG_FROM_TEST(bp->f1, data->select, data->select_flag);
4922 }
4923 else {
4924 if (beztindex == 0) {
4925 SET_FLAG_FROM_TEST(bezt->f1, data->select, data->select_flag);
4926 }
4927 else if (beztindex == 1) {
4928 SET_FLAG_FROM_TEST(bezt->f2, data->select, data->select_flag);
4929 }
4930 else {
4931 SET_FLAG_FROM_TEST(bezt->f3, data->select, data->select_flag);
4932 }
4933 }
4934 data->is_changed = true;
4935 }
4936}
4938 const eSelectOp sel_op,
4939 const int mval[2],
4940 float rad)
4941{
4942 const bool select = (sel_op != SEL_OP_SUB);
4943 const bool deselect_all = (sel_op == SEL_OP_SET);
4945
4947
4948 Curve *curve = (Curve *)vc->obedit->data;
4949 ListBase *nurbs = BKE_curve_editNurbs_get(curve);
4950
4951 /* For deselect all, items to be selected are tagged with temp flag. Clear that first. */
4952 if (deselect_all) {
4954 data.select_flag = BEZT_FLAG_TEMP_TAG;
4955 }
4956
4957 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
4959
4960 /* Deselect items that were not added to selection (indicated by temp flag). */
4961 if (deselect_all) {
4963 }
4964
4966
4967 return data.is_changed;
4968}
4969
4970static void latticecurve_circle_doSelect(void *user_data, BPoint *bp, const float screen_co[2])
4971{
4972 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
4973
4974 if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
4975 bp->f1 = data->select ? (bp->f1 | SELECT) : (bp->f1 & ~SELECT);
4976 data->is_changed = true;
4977 }
4978}
4980 const eSelectOp sel_op,
4981 const int mval[2],
4982 float rad)
4983{
4985 const bool select = (sel_op != SEL_OP_SUB);
4986
4988
4989 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
4990 data.is_changed |= ED_lattice_flags_set(vc->obedit, 0);
4991 }
4992 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
4993
4995
4996 return data.is_changed;
4997}
4998
5002static bool pchan_circle_doSelectJoint(void *user_data,
5003 bPoseChannel *pchan,
5004 const float screen_co[2])
5005{
5006 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
5007
5008 if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
5009 if (data->select) {
5010 pchan->bone->flag |= BONE_SELECTED;
5011 }
5012 else {
5013 pchan->bone->flag &= ~BONE_SELECTED;
5014 }
5015 return true;
5016 }
5017 return false;
5018}
5019static void do_circle_select_pose__doSelectBone(void *user_data,
5020 bPoseChannel *pchan,
5021 const float screen_co_a[2],
5022 const float screen_co_b[2])
5023{
5024 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
5025 bArmature *arm = static_cast<bArmature *>(data->vc->obact->data);
5026 if (!PBONE_SELECTABLE(arm, pchan->bone)) {
5027 return;
5028 }
5029
5030 bool is_point_done = false;
5031 int points_proj_tot = 0;
5032
5033 /* Project head location to screen-space. */
5034 if (screen_co_a[0] != IS_CLIPPED) {
5035 points_proj_tot++;
5036 if (pchan_circle_doSelectJoint(data, pchan, screen_co_a)) {
5037 is_point_done = true;
5038 }
5039 }
5040
5041 /* Project tail location to screen-space. */
5042 if (screen_co_b[0] != IS_CLIPPED) {
5043 points_proj_tot++;
5044 if (pchan_circle_doSelectJoint(data, pchan, screen_co_b)) {
5045 is_point_done = true;
5046 }
5047 }
5048
5049 /* check if the head and/or tail is in the circle
5050 * - the call to check also does the selection already
5051 */
5052
5053 /* only if the endpoints didn't get selected, deal with the middle of the bone too
5054 * It works nicer to only do this if the head or tail are not in the circle,
5055 * otherwise there is no way to circle select joints alone */
5056 if ((is_point_done == false) && (points_proj_tot == 2) &&
5057 edge_inside_circle(data->mval_fl, data->radius, screen_co_a, screen_co_b))
5058 {
5059 if (data->select) {
5060 pchan->bone->flag |= BONE_SELECTED;
5061 }
5062 else {
5063 pchan->bone->flag &= ~BONE_SELECTED;
5064 }
5065 data->is_changed = true;
5066 }
5067
5068 data->is_changed |= is_point_done;
5069}
5070static bool pose_circle_select(const ViewContext *vc,
5071 const eSelectOp sel_op,
5072 const int mval[2],
5073 float rad)
5074{
5077 const bool select = (sel_op != SEL_OP_SUB);
5078
5080
5081 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
5082 data.is_changed |= ED_pose_deselect_all(vc->obact, SEL_DESELECT, false);
5083 }
5084
5085 ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d); /* for foreach's screen/vert projection */
5086
5087 /* Treat bones as clipped segments (no joints). */
5090 &data,
5092
5093 if (data.is_changed) {
5095 }
5096 return data.is_changed;
5097}
5098
5102static bool armature_circle_doSelectJoint(void *user_data,
5103 EditBone *ebone,
5104 const float screen_co[2],
5105 bool head)
5106{
5107 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
5108
5109 if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
5110 if (head) {
5111 if (data->select) {
5112 ebone->flag |= BONE_ROOTSEL;
5113 }
5114 else {
5115 ebone->flag &= ~BONE_ROOTSEL;
5116 }
5117 }
5118 else {
5119 if (data->select) {
5120 ebone->flag |= BONE_TIPSEL;
5121 }
5122 else {
5123 ebone->flag &= ~BONE_TIPSEL;
5124 }
5125 }
5126 return true;
5127 }
5128 return false;
5129}
5131 EditBone *ebone,
5132 const float screen_co_a[2],
5133 const float screen_co_b[2])
5134{
5135 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
5136 const bArmature *arm = static_cast<const bArmature *>(data->vc->obedit->data);
5137 if (!(data->select ? EBONE_SELECTABLE(arm, ebone) :
5139 {
5140 return;
5141 }
5142
5143 /* When true, ignore in the next pass. */
5144 ebone->temp.i = false;
5145
5146 bool is_point_done = false;
5147 bool is_edge_done = false;
5148 int points_proj_tot = 0;
5149
5150 /* Project head location to screen-space. */
5151 if (screen_co_a[0] != IS_CLIPPED) {
5152 points_proj_tot++;
5153 if (armature_circle_doSelectJoint(data, ebone, screen_co_a, true)) {
5154 is_point_done = true;
5155 }
5156 }
5157
5158 /* Project tail location to screen-space. */
5159 if (screen_co_b[0] != IS_CLIPPED) {
5160 points_proj_tot++;
5161 if (armature_circle_doSelectJoint(data, ebone, screen_co_b, false)) {
5162 is_point_done = true;
5163 }
5164 }
5165
5166 /* check if the head and/or tail is in the circle
5167 * - the call to check also does the selection already
5168 */
5169
5170 /* only if the endpoints didn't get selected, deal with the middle of the bone too
5171 * It works nicer to only do this if the head or tail are not in the circle,
5172 * otherwise there is no way to circle select joints alone */
5173 if ((is_point_done == false) && (points_proj_tot == 2) &&
5174 edge_inside_circle(data->mval_fl, data->radius, screen_co_a, screen_co_b))
5175 {
5177 is_edge_done = true;
5178 data->is_changed = true;
5179 }
5180
5181 if (is_point_done || is_edge_done) {
5182 ebone->temp.i = true;
5183 }
5184
5185 data->is_changed |= is_point_done;
5186}
5188 EditBone *ebone,
5189 const float screen_co_a[2],
5190 const float screen_co_b[2])
5191{
5192 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
5193 bArmature *arm = static_cast<bArmature *>(data->vc->obedit->data);
5194
5195 if (!(data->select ? EBONE_SELECTABLE(arm, ebone) :
5197 {
5198 return;
5199 }
5200
5201 /* Set in the first pass, needed so circle select prioritizes joints. */
5202 if (ebone->temp.i != 0) {
5203 return;
5204 }
5205
5206 if (edge_inside_circle(data->mval_fl, data->radius, screen_co_a, screen_co_b)) {
5208 data->is_changed = true;
5209 }
5210}
5212 const eSelectOp sel_op,
5213 const int mval[2],
5214 float rad)
5215{
5217 bArmature *arm = static_cast<bArmature *>(vc->obedit->data);
5218
5219 const bool select = (sel_op != SEL_OP_SUB);
5220
5222
5223 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
5225 }
5226
5228
5229 /* Operate on fully visible (non-clipped) points. */
5232
5233 /* Operate on bones as segments clipped to the viewport bounds
5234 * (needed to handle bones with both points outside the view).
5235 * A separate pass is needed since clipped coordinates can't be used for selecting joints. */
5238 &data,
5240
5241 if (data.is_changed) {
5244 }
5245 return data.is_changed;
5246}
5247
5248static void do_circle_select_mball__doSelectElem(void *user_data,
5249 MetaElem *ml,
5250 const float screen_co[2])
5251{
5252 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
5253
5254 if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
5255 if (data->select) {
5256 ml->flag |= SELECT;
5257 }
5258 else {
5259 ml->flag &= ~SELECT;
5260 }
5261 data->is_changed = true;
5262 }
5263}
5264static bool mball_circle_select(const ViewContext *vc,
5265 const eSelectOp sel_op,
5266 const int mval[2],
5267 float rad)
5268{
5270
5271 const bool select = (sel_op != SEL_OP_SUB);
5272
5274
5275 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
5276 data.is_changed |= BKE_mball_deselect_all(static_cast<MetaBall *>(vc->obedit->data));
5277 }
5278
5280
5283 return data.is_changed;
5284}
5285
5287 const eSelectOp sel_op,
5288 const int mval[2],
5289 const float rad)
5290{
5291 using namespace blender;
5292 Object *object = (vc->obedit ? vc->obedit : vc->obact);
5293 const Object *ob_eval = DEG_get_evaluated(vc->depsgraph, object);
5294 const GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
5296 vc->scene->toolsettings, object);
5297
5299 vc,
5300 sel_op,
5302 const IndexMask &mask,
5303 const StringRef attribute_name,
5304 IndexMaskMemory &memory) {
5306 const bke::greasepencil::Layer &layer = grease_pencil.layer(info.layer_index);
5307 const bke::crazyspace::GeometryDeformation deformation =
5309 ob_eval, *object, info.drawing);
5310 const IndexMask visible_handle_elements =
5312 *object, info.drawing, info.layer_index, selection_domain, memory);
5313 const float4x4 layer_to_world = layer.to_world_space(*ob_eval);
5315 layer_to_world);
5316
5318 curves,
5319 deformation,
5320 projection,
5321 mask,
5322 visible_handle_elements,
5323 selection_domain,
5324 attribute_name,
5325 int2(mval),
5326 rad,
5327 memory);
5328 });
5329}
5330
5335 const ViewContext *vc,
5336 wmGenericUserData *wm_userdata,
5337 const eSelectOp sel_op,
5338 const int mval[2],
5339 float rad)
5340{
5341 using namespace blender;
5342 bool changed = false;
5344 switch (vc->obedit->type) {
5345 case OB_MESH:
5346 changed = mesh_circle_select(vc, wm_userdata, sel_op, mval, rad);
5347 break;
5348 case OB_CURVES_LEGACY:
5349 case OB_SURF:
5350 changed = nurbscurve_circle_select(vc, sel_op, mval, rad);
5351 break;
5352 case OB_LATTICE:
5353 changed = lattice_circle_select(vc, sel_op, mval, rad);
5354 break;
5355 case OB_ARMATURE:
5356 changed = armature_circle_select(vc, sel_op, mval, rad);
5357 if (changed) {
5359 }
5360 break;
5361 case OB_MBALL:
5362 changed = mball_circle_select(vc, sel_op, mval, rad);
5363 break;
5364 case OB_CURVES: {
5365 Curves &curves_id = *static_cast<Curves *>(vc->obedit->data);
5366 bke::CurvesGeometry &curves = curves_id.geometry.wrap();
5369 const bke::AttrDomain selection_domain = bke::AttrDomain(curves_id.selection_domain);
5370 const float4x4 projection = ED_view3d_ob_project_mat_get(vc->rv3d, vc->obedit);
5371 const IndexRange elements(curves.attributes().domain_size(selection_domain));
5372 changed = ed::curves::select_circle(*vc,
5373 curves,
5374 deformation,
5375 projection,
5376 elements,
5377 elements,
5378 selection_domain,
5379 mval,
5380 rad,
5381 sel_op);
5382 if (changed) {
5383 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
5384 * generic attribute for now. */
5385 DEG_id_tag_update(static_cast<ID *>(vc->obedit->data), ID_RECALC_GEOMETRY);
5387 }
5388 break;
5389 }
5390 case OB_POINTCLOUD: {
5391 PointCloud &pointcloud = *static_cast<PointCloud *>(vc->obedit->data);
5392 const float4x4 projection = ED_view3d_ob_project_mat_get(vc->rv3d, vc->obedit);
5394 pointcloud, *vc->region, projection, mval, rad, sel_op);
5395 if (changed) {
5396 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
5397 * generic attribute for now. */
5398 DEG_id_tag_update(static_cast<ID *>(vc->obedit->data), ID_RECALC_GEOMETRY);
5400 }
5401 break;
5402 }
5403 case OB_GREASE_PENCIL:
5404 changed = grease_pencil_circle_select(vc, sel_op, mval, rad);
5405 break;
5406
5407 default:
5408 BLI_assert(0);
5409 break;
5410 }
5411
5412 if (changed) {
5413 DEG_id_tag_update(static_cast<ID *>(vc->obact->data), ID_RECALC_SELECT);
5415 }
5416 return changed;
5417}
5418
5419static bool object_circle_select(const ViewContext *vc,
5420 const eSelectOp sel_op,
5421 const int mval[2],
5422 float rad)
5423{
5425 Scene *scene = vc->scene;
5426 ViewLayer *view_layer = vc->view_layer;
5427 View3D *v3d = vc->v3d;
5428
5429 const float radius_squared = rad * rad;
5430 const float mval_fl[2] = {float(mval[0]), float(mval[1])};
5431
5432 bool changed = false;
5433 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
5434 changed |= object_deselect_all_visible(vc->scene, vc->view_layer, vc->v3d);
5435 }
5436 const bool select = (sel_op != SEL_OP_SUB);
5437 const int select_flag = select ? BASE_SELECTED : 0;
5438 BKE_view_layer_synced_ensure(scene, view_layer);
5440 if (BASE_SELECTABLE(v3d, base) && ((base->flag & BASE_SELECTED) != select_flag)) {
5441 float screen_co[2];
5443 base->object->object_to_world().location(),
5444 screen_co,
5446 {
5447 if (len_squared_v2v2(mval_fl, screen_co) <= radius_squared) {
5450 changed = true;
5451 }
5452 }
5453 }
5454 }
5455
5456 return changed;
5457}
5458
5459/* not a real operator, only for circle test */
5460static void view3d_circle_select_recalc(void *user_data)
5461{
5462 bContext *C = static_cast<bContext *>(user_data);
5463 Object *obedit_active = CTX_data_edit_object(C);
5464
5465 if (obedit_active) {
5466 switch (obedit_active->type) {
5467 case OB_MESH: {
5470 vc.scene, vc.view_layer, vc.v3d, vc.obact->type, vc.obact->mode, ob_iter)
5471 {
5475 }
5477 break;
5478 }
5479
5480 default: {
5481 /* TODO: investigate if this is needed for other object types. */
5483 break;
5484 }
5485 }
5486 }
5487}
5488
5490 wmOperator *op,
5491 const wmEvent *event)
5492{
5494 if (result & OPERATOR_FINISHED) {
5496 }
5497 return result;
5498}
5499
5505
5507{
5509 const int radius = RNA_int_get(op->ptr, "radius");
5510 const int mval[2] = {RNA_int_get(op->ptr, "x"), RNA_int_get(op->ptr, "y")};
5511
5512 /* Allow each selection type to allocate their own data that's used between executions. */
5513 wmGesture *gesture = static_cast<wmGesture *>(op->customdata); /* nullptr when non-modal. */
5514 wmGenericUserData wm_userdata_buf = {nullptr, nullptr, false};
5515 wmGenericUserData *wm_userdata = gesture ? &gesture->user_data : &wm_userdata_buf;
5516
5517 const eSelectOp sel_op = ED_select_op_modal(
5518 static_cast<eSelectOp>(RNA_enum_get(op->ptr, "mode")), WM_gesture_is_modal_first(gesture));
5519
5521
5522 Object *obact = vc.obact;
5523 Object *obedit = vc.obedit;
5524
5525 if (obedit || BKE_paint_select_elem_test(obact) || (obact && (obact->mode & OB_MODE_POSE))) {
5527 if (obedit == nullptr) {
5529 }
5530 else {
5531 if (vc.obedit->type == OB_MESH) {
5533 }
5534 }
5535
5537 vc.scene, vc.view_layer, vc.v3d, obact->type, obact->mode, ob_iter)
5538 {
5540
5541 obact = vc.obact;
5542 obedit = vc.obedit;
5543
5544 if (obedit) {
5545 obedit_circle_select(C, &vc, wm_userdata, sel_op, mval, float(radius));
5546 }
5547 else if (BKE_paint_select_face_test(obact)) {
5548 paint_facesel_circle_select(&vc, wm_userdata, sel_op, mval, float(radius));
5549 }
5550 else if (BKE_paint_select_vert_test(obact)) {
5551 paint_vertsel_circle_select(&vc, wm_userdata, sel_op, mval, float(radius));
5552 }
5553 else if (BKE_paint_select_grease_pencil_test(obact)) {
5554 grease_pencil_circle_select(&vc, sel_op, mval, float(radius));
5555 }
5556 else if (obact->mode & OB_MODE_POSE) {
5557 pose_circle_select(&vc, sel_op, mval, float(radius));
5559 }
5560 else {
5561 BLI_assert(0);
5562 }
5563 }
5565 }
5566 else if (obact && (obact->mode & OB_MODE_PARTICLE_EDIT)) {
5567 if (PE_circle_select(C, wm_userdata, sel_op, mval, float(radius))) {
5568 return OPERATOR_FINISHED;
5569 }
5570 return OPERATOR_CANCELLED;
5571 }
5572 else if (obact && obact->mode & OB_MODE_SCULPT) {
5573 return OPERATOR_CANCELLED;
5574 }
5575 else if (Object *obact_pose = (obact && (obact->mode & OB_MODE_WEIGHT_PAINT)) ?
5577 nullptr)
5578 {
5579 ED_view3d_viewcontext_init_object(&vc, obact_pose);
5580 pose_circle_select(&vc, sel_op, mval, float(radius));
5582 }
5583 else {
5584 if (object_circle_select(&vc, sel_op, mval, float(radius))) {
5587
5589 }
5590 }
5591
5592 /* Otherwise this is freed by the gesture. */
5593 if (wm_userdata == &wm_userdata_buf) {
5594 WM_generic_user_data_free(wm_userdata);
5595 }
5596 else {
5597 EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
5598 if (esel && esel->select_bitmap) {
5599 MEM_freeN(esel->select_bitmap);
5600 esel->select_bitmap = nullptr;
5601 }
5602 }
5603
5604 return OPERATOR_FINISHED;
5605}
5606
5608{
5609 ot->name = "Circle Select";
5610 ot->description = "Select items using circle selection";
5611 ot->idname = "VIEW3D_OT_select_circle";
5612
5613 ot->invoke = WM_gesture_circle_invoke;
5616 ot->poll = view3d_selectable_data;
5618 ot->get_name = ED_select_circle_get_name;
5619
5620 /* flags */
5621 ot->flag = OPTYPE_UNDO;
5622
5623 /* properties */
5626}
5627
Functions to deal with Armatures.
C++ functions to deal with Armature collections (i.e. the successor of bone layers).
Blender kernel action and pose functionality.
#define PBONE_SELECTABLE(arm, bone)
#define CTX_DATA_BEGIN(C, Type, instance, member)
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Object * CTX_data_edit_object(const bContext *C)
Main * CTX_data_main(const bContext *C)
RegionView3D * CTX_wm_region_view3d(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
#define CTX_DATA_END
View3D * CTX_wm_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
bool BKE_nurbList_flag_set_from_flag(ListBase *editnurb, uint8_t from_flag, uint8_t flag)
Definition curve.cc:4380
ListBase * BKE_curve_editNurbs_get(Curve *cu)
Definition curve.cc:423
void BKE_curve_nurb_vert_active_validate(Curve *cu)
Definition curve.cc:5062
void BKE_nurbList_flag_set(ListBase *editnurb, uint8_t flag, bool set)
Definition curve.cc:4345
Low-level operations for curves.
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:61
Low-level operations for grease pencil.
#define FOREACH_BASE_IN_MODE_END
Definition BKE_layer.hh:367
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)
#define FOREACH_BASE_IN_MODE_BEGIN(_scene, _view_layer, _v3d, _object_type, _object_mode, _instance)
Definition BKE_layer.hh:349
#define FOREACH_OBJECT_IN_MODE_END
Definition BKE_layer.hh:382
#define FOREACH_OBJECT_IN_MODE_BEGIN(_scene, _view_layer, _v3d, _object_type, _object_mode, _instance)
Definition BKE_layer.hh:377
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)
ListBase * BKE_view_layer_object_bases_get(ViewLayer *view_layer)
bool BKE_mball_deselect_all(MetaBall *mb)
Definition mball.cc:608
void BKE_mesh_mselect_active_set(Mesh *mesh, int index, int type)
void BKE_mesh_mselect_validate(Mesh *mesh)
Mesh * BKE_mesh_from_object(Object *ob)
General operations, lookup, etc. for blender objects.
MovieClip * BKE_object_movieclip_get(Scene *scene, const Object *ob, bool use_default)
bool BKE_object_is_in_editmode(const Object *ob)
void BKE_object_update_select_id(Main *bmain)
Object * BKE_object_pose_armature_get(Object *ob)
Object * BKE_object_pose_armature_get_with_wpaint_check(Object *ob)
bool BKE_object_is_mode_compat(const Object *ob, eObjectMode object_mode)
bool BKE_paint_select_grease_pencil_test(const Object *ob)
Definition paint.cc:1613
bool BKE_paint_select_elem_test(const Object *ob)
Definition paint.cc:1624
bool BKE_paint_select_vert_test(const Object *ob)
Definition paint.cc:1606
bool BKE_paint_select_face_test(const Object *ob)
Definition paint.cc:1599
struct MovieTrackingTrack * BKE_tracking_track_get_for_selection_index(struct MovieTracking *tracking, int selection_index, struct ListBase **r_tracksbase)
Definition tracking.cc:1024
void BKE_tracking_track_deselect(struct MovieTrackingTrack *track, int area)
Definition tracking.cc:1216
@ TRACK_AREA_ALL
#define TRACK_SELECTED(track)
void BKE_tracking_track_select(struct ListBase *tracksbase, struct MovieTrackingTrack *track, int area, bool extend)
Definition tracking.cc:1189
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:53
#define BLI_BITMAP_TEST_BOOL(_bitmap, _index)
Definition BLI_bitmap.h:71
unsigned int BLI_bitmap
Definition BLI_bitmap.h:13
BLI_INLINE void BLI_endian_switch_uint32(unsigned int *val) ATTR_NONNULL(1)
struct GSet GSet
Definition BLI_ghash.h:337
GSet * BLI_gset_ptr_new(const char *info)
bool BLI_gset_haskey(const GSet *gs, const void *key) ATTR_WARN_UNUSED_RESULT
void BLI_gset_insert(GSet *gs, void *key)
Definition BLI_ghash.cc:959
void BLI_gset_free(GSet *gs, GSetKeyFreeFP keyfreefp)
bool BLI_lasso_is_point_inside(blender::Span< blender::int2 > mcoords, int sx, int sy, int error_value)
void BLI_lasso_boundbox(rcti *rect, blender::Span< blender::int2 > mcoords)
bool BLI_lasso_is_edge_inside(blender::Span< blender::int2 > mcoords, int x0, int y0, int x1, int y1, int error_value)
void * BLI_findlink(const ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition listbase.cc:534
#define LISTBASE_FOREACH(type, var, list)
void BLI_freelinkN(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:270
void void BLI_freelistN(ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:497
void BLI_addtail(ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:111
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
void void BLI_listbase_sort(ListBase *listbase, int(*cmp)(const void *, const void *)) ATTR_NONNULL(1
MINLINE float square_f(float a)
MINLINE unsigned int float_as_uint(float f)
float dist_squared_to_line_segment_v2(const float p[2], const float l1[2], const float l2[2])
Definition math_geom.cc:291
MINLINE float len_squared_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void copy_v2_v2_int(int r[2], const int a[2])
MINLINE float len_manhattan_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
bool BLI_rctf_isect_pt_v(const struct rctf *rect, const float xy[2])
void BLI_rcti_init_pt_radius(struct rcti *rect, const int xy[2], int size)
Definition rct.cc:466
bool BLI_rcti_isect_pt(const struct rcti *rect, int x, int y)
bool BLI_rctf_isect_segment(const struct rctf *rect, const float s1[2], const float s2[2])
void BLI_rctf_rcti_copy(struct rctf *dst, const struct rcti *src)
bool BLI_rcti_is_empty(const struct rcti *rect)
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
unsigned int uint
#define UNPACK2(a)
#define UNUSED_VARS_NDEBUG(...)
#define SET_FLAG_FROM_TEST(value, test, flag)
#define ELEM(...)
#define STREQ(a, b)
#define CTX_IFACE_(context, msgid)
#define BLT_I18NCONTEXT_OPERATOR_DEFAULT
void DEG_id_tag_update(ID *id, unsigned int flags)
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
@ ID_RECALC_SELECT
Definition DNA_ID.h:1009
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:982
@ ID_RECALC_BASE_FLAGS
Definition DNA_ID.h:1012
@ ID_TAG_DOIT
Definition DNA_ID.h:944
@ BONE_ROOTSEL
@ BONE_SELECTED
@ BONE_UNSELECTABLE
@ BONE_DONE
@ BONE_TIPSEL
eBezTriple_Flag
@ BEZT_FLAG_TEMP_TAG
@ ME_VSEL
@ MB_SCALE_RAD
#define OB_MODE_ALL_WEIGHT_PAINT
eObjectMode
@ OB_MODE_PARTICLE_EDIT
@ OB_MODE_EDIT
@ OB_MODE_WEIGHT_PAINT
@ OB_MODE_SCULPT
@ OB_MODE_SCULPT_GREASE_PENCIL
@ OB_MODE_POSE
@ OB_MODE_TEXTURE_PAINT
@ OB_MODE_OBJECT
@ OB_MODE_VERTEX_PAINT
Object is a sort of wrapper for general info.
@ OB_LATTICE
@ OB_MBALL
@ OB_SURF
@ OB_CAMERA
@ OB_FONT
@ OB_GREASE_PENCIL
@ OB_ARMATURE
@ OB_MESH
@ OB_POINTCLOUD
@ OB_CURVES_LEGACY
@ OB_CURVES
#define BASE_SELECTED(v3d, base)
@ SCE_OBJECT_MODE_LOCK
@ SCE_SELECT_FACE
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
#define BASE_SELECTABLE(v3d, base)
#define BASE_VISIBLE(v3d, base)
#define UI_SCALE_FAC
eHandleDisplay
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_PASS_THROUGH
uint * DRW_select_buffer_bitmap_from_rect(Depsgraph *depsgraph, ARegion *region, View3D *v3d, const rcti *rect, uint *r_bitmap_len)
uint * DRW_select_buffer_bitmap_from_circle(Depsgraph *depsgraph, ARegion *region, View3D *v3d, const int center[2], int radius, uint *r_bitmap_len)
void DRW_select_buffer_context_create(Depsgraph *depsgraph, blender::Span< Base * > bases, short select_mode)
uint DRW_select_buffer_context_offset_for_object_elem(Depsgraph *depsgraph, Object *object, char elem_type)
uint * DRW_select_buffer_bitmap_from_poly(Depsgraph *depsgraph, ARegion *region, View3D *v3d, blender::Span< blender::int2 > poly, const rcti *rect, uint *r_bitmap_len)
#define BONESEL_ANY
#define BONESEL_ROOT
#define BONESEL_TIP
#define BONESEL_BONE
#define EBONE_SELECTABLE(arm, ebone)
bool ED_lattice_select_pick(bContext *C, const int mval[2], const SelectPick_Params &params)
bool ED_lattice_flags_set(Object *obedit, int flag)
bool ED_mball_select_pick(bContext *C, const int mval[2], const SelectPick_Params &params)
#define MBALLSEL_ANY
Definition ED_mball.hh:64
#define MBALLSEL_STIFF
Definition ED_mball.hh:62
#define MBALLSEL_RADIUS
Definition ED_mball.hh:63
void EDBM_flag_disable_all(BMEditMesh *em, char hflag)
#define ED_MESH_PICK_DEFAULT_VERT_DIST
Definition ED_mesh.hh:602
void paintvert_flush_flags(Object *ob)
Definition editface.cc:787
void paintvert_tag_select_update(bContext *C, Object *ob)
Definition editface.cc:1046
ViewContext em_setup_viewcontext(bContext *C)
void EDBM_selectmode_flush(BMEditMesh *em)
void paintface_flush_flags(bContext *C, Object *ob, bool flush_selection, bool flush_hidden)
Definition editface.cc:40
bool paintface_mouse_select(bContext *C, const int mval[2], const SelectPick_Params &params, Object *ob)
Definition editface.cc:721
bool paintvert_deselect_all_visible(Object *ob, int action, bool flush_flags)
Definition editface.cc:1052
bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool flush_flags)
Definition editface.cc:623
bool ED_mesh_pick_vert(bContext *C, Object *ob, const int mval[2], uint dist_px, bool use_zbuf, uint *r_index)
bool EDBM_select_pick(bContext *C, const int mval[2], const SelectPick_Params &params)
void ED_outliner_select_sync_from_object_tag(bContext *C)
void ED_outliner_select_sync_from_edit_bone_tag(bContext *C)
void ED_outliner_select_sync_from_pose_bone_tag(bContext *C)
bool PE_circle_select(bContext *C, wmGenericUserData *wm_userdata, int sel_op, const int mval[2], float rad)
bool PE_box_select(bContext *C, const rcti *rect, int sel_op)
bool PE_mouse_particles(bContext *C, const int mval[2], const SelectPick_Params &params)
int PE_lasso_select(bContext *C, const int mcoords[][2], int mcoords_len, int sel_op)
bool ED_operator_view3d_active(bContext *C)
bool ED_operator_region_view3d_active(bContext *C)
eSelectOp
@ SEL_OP_ADD
@ SEL_OP_SUB
@ SEL_OP_SET
@ SEL_OP_AND
@ SEL_OP_XOR
int ED_select_op_action_deselected(eSelectOp sel_op, bool is_select, bool is_inside)
eSelectOp ED_select_op_modal(eSelectOp sel_op, bool is_first)
@ SEL_DESELECT
eSelectOp ED_select_op_from_operator(PointerRNA *ptr) ATTR_WARN_UNUSED_RESULT
#define SEL_OP_USE_PRE_DESELECT(sel_op)
std::string ED_select_circle_get_name(wmOperatorType *ot, PointerRNA *ptr)
#define SEL_OP_CAN_DESELECT(sel_op)
SelectPick_Params ED_select_pick_params_from_operator(PointerRNA *ptr) ATTR_NONNULL(1)
#define SEL_OP_USE_OUTSIDE(sel_op)
std::string ED_select_pick_get_name(wmOperatorType *ot, PointerRNA *ptr)
#define V3D_PROJ_TEST_CLIP_CONTENT_DEFAULT
Definition ED_view3d.hh:315
#define XRAY_ENABLED(v3d)
void armature_foreachScreenBone(const ViewContext *vc, void(*func)(void *user_data, EditBone *ebone, const float screen_co_a[2], const float screen_co_b[2]), void *user_data, eV3DProjTest clip_flag)
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)
int view3d_gpu_select(const ViewContext *vc, GPUSelectBuffer *buffer, const rcti *input, eV3DSelectMode select_mode, eV3DSelectObjectFilter select_filter)
eV3DProjTest
Definition ED_view3d.hh:278
@ V3D_PROJ_TEST_CLIP_NEAR
Definition ED_view3d.hh:282
@ V3D_PROJ_TEST_CLIP_BB
Definition ED_view3d.hh:280
void nurbs_foreachScreenVert(const ViewContext *vc, void(*func)(void *user_data, Nurb *nu, BPoint *bp, BezTriple *bezt, int beztindex, bool handle_visible, const float screen_co[2]), void *user_data, eV3DProjTest clip_flag)
void view3d_gpu_select_cache_end()
void ED_view3d_init_mats_rv3d(const Object *ob, RegionView3D *rv3d)
int view3d_gpu_select_ex(const ViewContext *vc, GPUSelectBuffer *buffer, const rcti *input, eV3DSelectMode select_mode, eV3DSelectObjectFilter select_filter, bool do_material_slot_selection)
@ V3D_PROJ_RET_OK
Definition ED_view3d.hh:256
void lattice_foreachScreenVert(const ViewContext *vc, void(*func)(void *user_data, BPoint *bp, const float screen_co[2]), void *user_data, eV3DProjTest clip_flag)
#define XRAY_ACTIVE(v3d)
blender::float4x4 ED_view3d_ob_project_mat_get(const RegionView3D *rv3d, const Object *ob)
#define IS_CLIPPED
Definition ED_view3d.hh:252
void pose_foreachScreenBone(const ViewContext *vc, void(*func)(void *user_data, bPoseChannel *pchan, const float screen_co_a[2], const float screen_co_b[2]), void *user_data, eV3DProjTest clip_flag)
eV3DSelectObjectFilter ED_view3d_select_filter_from_mode(const Scene *scene, const Object *obact)
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)
void mball_foreachScreenElem(const ViewContext *vc, void(*func)(void *user_data, MetaElem *ml, const float screen_co[2]), void *user_data, eV3DProjTest clip_flag)
#define V3D_PROJ_TEST_CLIP_DEFAULT
Definition ED_view3d.hh:309
void view3d_gpu_select_cache_begin()
eV3DProjStatus ED_view3d_project_base(const ARegion *region, Base *base, float r_co[2])
eV3DSelectMode
Definition ED_view3d.hh:977
@ VIEW3D_SELECT_PICK_ALL
Definition ED_view3d.hh:981
@ VIEW3D_SELECT_PICK_NEAREST
Definition ED_view3d.hh:983
@ VIEW3D_SELECT_ALL
Definition ED_view3d.hh:979
#define XRAY_FLAG_ENABLED(v3d)
void view3d_operator_needs_gpu(const bContext *C)
blender::float4x4 ED_view3d_ob_project_mat_get_from_obmat(const RegionView3D *rv3d, const blender::float4x4 &obmat)
void mesh_foreachScreenEdge_clip_bb_segment(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)
eV3DSelectObjectFilter
Definition ED_view3d.hh:986
@ VIEW3D_SELECT_FILTER_NOP
Definition ED_view3d.hh:988
void meshobject_foreachScreenVert(const ViewContext *vc, void(*func)(void *user_data, const float screen_co[2], int index), void *user_data, eV3DProjTest clip_flag)
eV3DProjStatus ED_view3d_project_float_global(const ARegion *region, const float co[3], float r_co[2], eV3DProjTest flag)
#define GPU_matrix_set(x)
blender::Vector< GPUSelectResult, 2500 > GPUSelectStorage
Definition GPU_select.hh:45
Read Guarded memory(de)allocation.
@ PROP_ENUM_NO_TRANSLATE
Definition RNA_types.hh:406
@ PROP_SKIP_SAVE
Definition RNA_types.hh:330
@ PROP_HIDDEN
Definition RNA_types.hh:324
#define C
Definition RandGen.cpp:29
int UI_icon_from_id(const ID *id)
#define NC_GEOM
Definition WM_types.hh:390
#define ND_DATA
Definition WM_types.hh:506
#define NC_MOVIECLIP
Definition WM_types.hh:394
#define ND_OB_SELECT
Definition WM_types.hh:439
#define NC_SCENE
Definition WM_types.hh:375
#define ND_SELECT
Definition WM_types.hh:505
#define ND_BONE_ACTIVE
Definition WM_types.hh:456
@ WM_OP_INVOKE_DEFAULT
Definition WM_types.hh:238
#define ND_BONE_SELECT
Definition WM_types.hh:457
#define NC_OBJECT
Definition WM_types.hh:376
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:218
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
bool ED_armature_edit_deselect_all_visible_multi_ex(const Span< Base * > bases)
bool ED_armature_edit_deselect_all_visible(Object *obedit)
Base * ED_armature_base_and_bone_from_select_buffer(const Span< Base * > bases, const uint select_id, Bone **r_bone)
Base * ED_armature_base_and_ebone_from_select_buffer(const Span< Base * > bases, const uint select_id, EditBone **r_ebone)
bool ED_armature_edit_select_pick_bone(bContext *C, Base *basact, EditBone *ebone, const int selmask, const SelectPick_Params &params)
bool ED_armature_edit_select_op_from_tagged(bArmature *arm, const int sel_op)
bool ED_armature_edit_select_pick(bContext *C, const int mval[2], const SelectPick_Params &params)
void ED_armature_ebone_listbase_temp_clear(ListBase *lb)
void ED_armature_edit_sync_selection(ListBase *edbo)
#define U
@ BM_ELEM_HIDDEN
@ BM_ELEM_SELECT
#define BM_elem_flag_test(ele, hflag)
#define BM_ITER_MESH(ele, iter, bm, itype)
@ BM_EDGES_OF_MESH
@ BM_VERTS_OF_MESH
@ BM_FACES_OF_MESH
BMesh const char void * data
void BM_face_select_set(BMesh *bm, BMFace *f, const bool select)
Select Face.
void BM_mesh_select_mode_flush_ex(BMesh *bm, const short selectmode, eBMSelectionFlushFLags flags)
Select Mode Flush.
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.
@ BM_SELECT_LEN_FLUSH_RECALC_ALL
@ BM_SELECT_LEN_FLUSH_RECALC_NOTHING
ATTR_WARN_UNUSED_RESULT const BMVert * v2
BPy_StructRNA * depsgraph
bool closest(btVector3 &v)
IndexMask slice_content(IndexRange range) const
bool is_empty() const
static constexpr IndexRange from_single(const int64_t index)
bool is_empty() const
Definition BLI_array.hh:253
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
Value lookup_default(const Key &key, const Value &default_value) const
Definition BLI_map.hh:570
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
constexpr IndexRange index_range() const
Definition BLI_span.hh:670
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:739
bool contains(const Key &key) const
Definition BLI_set.hh:310
bool add(const Key &key)
Definition BLI_set.hh:248
constexpr const T * data() const
Definition BLI_span.hh:215
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr bool is_empty() const
Definition BLI_span.hh:260
void append(const T &value)
IndexRange index_range() const
void resize(const int64_t new_size)
MutableSpan< T > as_mutable_span()
Span< T > as_span() const
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const void *default_value=nullptr) const
int domain_size(const AttrDomain domain) const
AttributeAccessor attributes() const
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
GAttributeWriter lookup_or_add_for_write(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
float4x4 to_world_space(const Object &object) const
#define SELECT
bool ED_curve_editnurb_select_pick(bContext *C, const int mval[2], const int dist_px, const SelectPick_Params &params)
bool ED_curve_editfont_select_pick(bContext *C, const int mval[2], const SelectPick_Params &params)
Definition editfont.cc:2596
static bool is_inside(int x, int y, int cols, int rows)
Definition filesel.cc:772
MatBase< 4, 4 > float4x4
#define select(A, B, C)
#define MEM_SAFE_FREE(v)
#define MAX_ID_NAME
blender::bke::AttrDomain ED_grease_pencil_selection_domain_get(const ToolSettings *tool_settings, const Object *object)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
static bool handles_visible(KeyframeEditData *ked, BezTriple *bezt)
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
ccl_device_inline float2 mask(const MaskType mask, const float2 a)
static ulong * next
bool bone_is_visible_editbone(const bArmature *armature, const EditBone *ebone)
GeometryDeformation get_evaluated_grease_pencil_drawing_deformation(const Object *ob_eval, const Object &ob_orig, const bke::greasepencil::Drawing &drawing_orig)
GeometryDeformation get_evaluated_curves_deformation(const Object *ob_eval, const Object &ob_orig)
void apply_selection_operation_at_index(GMutableSpan selection, const int index, const eSelectOp sel_op)
void foreach_selectable_point_range(const bke::CurvesGeometry &curves, const bke::crazyspace::GeometryDeformation &deformation, eHandleDisplay handle_display, SelectionRangeFn range_consumer)
IndexMask select_circle_mask(const ViewContext &vc, const bke::CurvesGeometry &curves, const bke::crazyspace::GeometryDeformation &deformation, const float4x4 &projection, const IndexMask &selection_mask, const IndexMask &bezier_mask, const bke::AttrDomain selection_domain, const StringRef attribute_name, const int2 coord, const float radius, IndexMaskMemory &memory)
void foreach_selectable_curve_range(const bke::CurvesGeometry &curves, const bke::crazyspace::GeometryDeformation &deformation, eHandleDisplay handle_display, SelectionRangeFn range_consumer)
void foreach_selection_attribute_writer(bke::CurvesGeometry &curves, bke::AttrDomain selection_domain, blender::FunctionRef< void(bke::GSpanAttributeWriter &selection)> fn)
IndexMask select_lasso_mask(const ViewContext &vc, const bke::CurvesGeometry &curves, const bke::crazyspace::GeometryDeformation &deformation, const float4x4 &projection, const IndexMask &selection_mask, const IndexMask &bezier_mask, const bke::AttrDomain selection_domain, const StringRef attribute_name, const Span< int2 > lasso_coords, IndexMaskMemory &memory)
bool select_circle(const ViewContext &vc, bke::CurvesGeometry &curves, const bke::crazyspace::GeometryDeformation &deformation, const float4x4 &projection, const IndexMask &selection_mask, const IndexMask &bezier_mask, const bke::AttrDomain selection_domain, const int2 coord, const float radius, const eSelectOp sel_op)
std::optional< FindClosestData > closest_elem_find_screen_space(const ViewContext &vc, const OffsetIndices< int > points_by_curve, const Span< float3 > positions, const VArray< bool > &cyclic, const float4x4 &projection, const IndexMask &mask, const bke::AttrDomain domain, const int2 coord, const FindClosestData &initial_closest)
IndexMask select_box_mask(const ViewContext &vc, const bke::CurvesGeometry &curves, const bke::crazyspace::GeometryDeformation &deformation, const float4x4 &projection, const IndexMask &selection_mask, const IndexMask &bezier_mask, const bke::AttrDomain selection_domain, const StringRef attribute_name, const rcti &rect, IndexMaskMemory &memory)
bke::GSpanAttributeWriter ensure_selection_attribute(bke::CurvesGeometry &curves, bke::AttrDomain selection_domain, eCustomDataType create_type, StringRef attribute_name)
bool select_lasso(const ViewContext &vc, bke::CurvesGeometry &curves, const bke::crazyspace::GeometryDeformation &deformation, const float4x4 &projection, const IndexMask &selection_mask, const IndexMask &bezier_mask, const bke::AttrDomain selection_domain, const Span< int2 > lasso_coords, const eSelectOp sel_op)
bool select_box(const ViewContext &vc, bke::CurvesGeometry &curves, const bke::crazyspace::GeometryDeformation &deformation, const float4x4 &projection, const IndexMask &selection_mask, const IndexMask &bezier_mask, const bke::AttrDomain selection_domain, const rcti &rect, const eSelectOp sel_op)
bool selection_update(const ViewContext *vc, const eSelectOp sel_op, SelectionUpdateFunc select_operation)
IndexMask retrieve_editable_elements(Object &object, const MutableDrawingInfo &info, const bke::AttrDomain selection_domain, IndexMaskMemory &memory)
IndexMask retrieve_visible_bezier_handle_elements(Object &object, const bke::greasepencil::Drawing &drawing, const int layer_index, const bke::AttrDomain selection_domain, IndexMaskMemory &memory)
Vector< MutableDrawingInfo > retrieve_editable_drawings(const Scene &scene, GreasePencil &grease_pencil)
void base_activate(bContext *C, Base *base)
void base_select(Base *base, eObjectSelect_Mode mode)
void mode_generic_exit(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob)
bool select_box(PointCloud &pointcloud, const ARegion &region, const float4x4 &projection, const rcti &rect, const eSelectOp sel_op)
Definition selection.cc:226
bool select_lasso(PointCloud &pointcloud, const ARegion &region, const float4x4 &projection, const Span< int2 > lasso_coords, const eSelectOp sel_op)
Definition selection.cc:245
bool select_circle(PointCloud &pointcloud, const ARegion &region, const float4x4 &projection, const int2 coord, const float radius, const eSelectOp sel_op)
Definition selection.cc:274
bke::GSpanAttributeWriter ensure_selection_attribute(PointCloud &pointcloud, eCustomDataType create_type)
Definition selection.cc:96
std::optional< FindClosestData > find_closest_point_to_screen_co(const ARegion &region, const Span< float3 > positions, const float4x4 &projection, const IndexMask &points_mask, const float2 mouse_pos, const float radius, const FindClosestData &initial_closest)
Definition selection.cc:304
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:93
Value parallel_reduce(IndexRange range, int64_t grain_size, const Value &identity, const Function &function, const Reduction &reduction)
Definition BLI_task.hh:151
VecBase< int32_t, 2 > int2
static void init(bNodeTree *, bNode *node)
void ED_armature_pose_select_in_wpaint_mode(const Scene *scene, ViewLayer *view_layer, Base *base_select)
bool ED_armature_pose_select_pick_bone(const Scene *scene, ViewLayer *view_layer, View3D *v3d, Object *ob, Bone *bone, const SelectPick_Params &params)
void ED_pose_bone_select_tag_update(Object *ob)
bool ED_pose_deselect_all(Object *ob, int select_mode, const bool ignore_visibility)
bool ED_armature_pose_select_pick_with_buffer(const Scene *scene, ViewLayer *view_layer, View3D *v3d, Base *base, const GPUSelectResult *hit_results, const int hits, const SelectPick_Params &params, bool do_nearest)
void RNA_int_set_array(PointerRNA *ptr, const char *name, const int *values)
void RNA_int_get_array(PointerRNA *ptr, const char *name, int *values)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
int RNA_int_get(PointerRNA *ptr, const char *name)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
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_int_vector(StructOrFunctionRNA *cont_, const char *identifier, const int len, const int *default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
void RNA_enum_item_end(EnumPropertyItem **items, int *totitem)
void RNA_enum_item_add(EnumPropertyItem **items, int *totitem, const EnumPropertyItem *item)
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)
void RNA_def_enum_funcs(PropertyRNA *prop, EnumPropertyItemFunc itemfunc)
const EnumPropertyItem rna_enum_dummy_NULL_items[]
Definition rna_rna.cc:26
#define min(a, b)
Definition sort.cc:36
short selectmode
int totfacesel
int totvertsel
int totedgesel
uint8_t f1
struct Base * next
short flag
struct Object * object
blender::MutableSpan< bool > select_vert
const ViewContext * vc
eBezTriple_Flag select_flag
blender::MutableSpan< bool > select_vert
const ViewContext * vc
eBezTriple_Flag select_flag
blender::StringRef selection_attribute_name
blender::ed::curves::FindClosestData elem
blender::ed::curves::FindClosestData elem
blender::bke::greasepencil::Drawing * drawing
blender::StringRef selection_attribute_name
PointCloud * pointcloud
blender::ed::pointcloud::FindClosestData elem
CurvesGeometry geometry
char selection_domain
char name[64]
union EditBone::@313026223344046157307162027212134045115206003172 temp
EditBone * ebone
BLI_bitmap * select_bitmap
GPUSelectStorage storage
Definition GPU_select.hh:47
unsigned int id
Definition GPU_select.hh:34
unsigned int depth
Definition GPU_select.hh:42
Definition DNA_ID.h:404
int tag
Definition DNA_ID.h:424
char name[66]
Definition DNA_ID.h:415
blender::MutableSpan< bool > select_vert
eBezTriple_Flag select_flag
const ViewContext * vc
void * last
void * first
int faces_num
int verts_num
ListBase * editelems
struct MetaElem * next
struct MovieTracking tracking
struct bPose * pose
ObjectRuntimeHandle * runtime
float viewmat[4][4]
struct ToolSettings * toolsettings
char idname[MAX_ID_NAME - 2]
View3DOverlay overlay
RegionView3D * rv3d
Definition ED_view3d.hh:80
ARegion * region
Definition ED_view3d.hh:77
Scene * scene
Definition ED_view3d.hh:73
Main * bmain
Definition ED_view3d.hh:67
BMEditMesh * em
Definition ED_view3d.hh:81
wmWindow * win
Definition ED_view3d.hh:79
ViewLayer * view_layer
Definition ED_view3d.hh:74
bContext * C
Definition ED_view3d.hh:66
View3D * v3d
Definition ED_view3d.hh:78
Object * obact
Definition ED_view3d.hh:75
Object * obedit
Definition ED_view3d.hh:76
Depsgraph * depsgraph
Definition ED_view3d.hh:72
ListBase * edbo
struct Bone * bone
ListBase chanbase
float xmax
float xmin
float ymax
float ymin
int mval[2]
Definition WM_types.hh:760
wmGenericUserDataFreeFn free_fn
Definition WM_types.hh:143
wmGenericUserData user_data
Definition WM_types.hh:663
struct PointerRNA * ptr
i
Definition text_draw.cc:230
static bool edbm_backbuf_check_and_select_faces_obmode(Mesh *mesh, EditSelectBuf_Cache *esel, const eSelectOp sel_op)
static wmOperatorStatus view3d_box_select_exec(bContext *C, wmOperator *op)
static void do_lasso_select_armature__doSelectBone(void *user_data, EditBone *ebone, const float screen_co_a[2], const float screen_co_b[2])
void VIEW3D_OT_select(wmOperatorType *ot)
float ED_view3d_select_dist_px()
static bool do_lasso_select_pose(const ViewContext *vc, const Span< int2 > mcoords, const eSelectOp sel_op)
static bool do_paintvert_box_select(const ViewContext *vc, wmGenericUserData *wm_userdata, const rcti *rect, const eSelectOp sel_op)
static void do_lasso_tag_pose(const ViewContext *vc, const Span< int2 > mcoords)
static bool edbm_backbuf_check_and_select_edges(EditSelectBuf_Cache *esel, Depsgraph *depsgraph, Object *ob, BMEditMesh *em, const eSelectOp sel_op)
static bool edbm_backbuf_check_and_select_faces(EditSelectBuf_Cache *esel, Depsgraph *depsgraph, Object *ob, BMEditMesh *em, const eSelectOp sel_op)
static wmOperatorStatus view3d_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static blender::Vector< Base * > do_pose_tag_select_op_prepare(const ViewContext *vc)
static bool paint_facesel_circle_select(const ViewContext *vc, wmGenericUserData *wm_userdata, const eSelectOp sel_op, const int mval[2], float rad)
static void do_lasso_select_mesh__doSelectVert(void *user_data, BMVert *eve, const float screen_co[2], int)
static bool do_lasso_select_objects(const ViewContext *vc, const Span< int2 > mcoords, const eSelectOp sel_op)
static Base * mouse_select_object_center(const ViewContext *vc, Base *startbase, const int mval[2])
static void do_paintvert_box_select__doSelectVert(void *user_data, const float screen_co[2], int index)
static void do_mesh_box_select__doSelectFace(void *user_data, BMFace *efa, const float screen_co[2], int)
static bool grease_pencil_circle_select(const ViewContext *vc, const eSelectOp sel_op, const int mval[2], const float rad)
static bool do_lasso_select_grease_pencil(const ViewContext *vc, const Span< int2 > mcoords, const eSelectOp sel_op)
static bool do_pose_box_select(bContext *C, const ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
static void do_circle_select_armature__doSelectBone(void *user_data, EditBone *ebone, const float screen_co_a[2], const float screen_co_b[2])
static bool pose_circle_select(const ViewContext *vc, const eSelectOp sel_op, const int mval[2], float rad)
static bool bone_mouse_select_menu(bContext *C, const blender::Span< GPUSelectResult > hit_results, const bool is_editmode, const SelectPick_Params &params)
static void do_mesh_box_select__doSelectEdge_pass0(void *user_data, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int index)
static bool do_nurbs_box_select(const ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
static bool do_lasso_select_mesh(const ViewContext *vc, wmGenericUserData *wm_userdata, const Span< int2 > mcoords, const eSelectOp sel_op)
static bool do_lasso_select_lattice(const ViewContext *vc, const Span< int2 > mcoords, const eSelectOp sel_op)
static void do_lattice_box_select__doSelect(void *user_data, BPoint *bp, const float screen_co[2])
static void view3d_userdata_lassoselect_init(LassoSelectUserData *r_data, const ViewContext *vc, const rcti *rect, const Span< int2 > mcoords, const eSelectOp sel_op)
static bool do_lattice_box_select(const ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
static bool object_circle_select(const ViewContext *vc, const eSelectOp sel_op, const int mval[2], float rad)
void VIEW3D_OT_bone_select_menu(wmOperatorType *ot)
static bool object_mouse_select_menu(bContext *C, const ViewContext *vc, const blender::Span< GPUSelectResult > hit_results, const int mval[2], const SelectPick_Params &params, Base **r_basact)
void VIEW3D_OT_select_box(wmOperatorType *ot)
static int gpu_select_buffer_depth_id_cmp(const void *sel_a_p, const void *sel_b_p)
static Base * mouse_select_eval_buffer(const ViewContext *vc, const GPUSelectBuffer &buffer, int hits, bool do_nearest, bool has_bones, bool do_bones_get_priotity, int *r_select_id_subelem)
static void mesh_circle_doSelectEdge(void *user_data, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int)
static void do_lasso_select_armature__doSelectBone_clip_content(void *user_data, EditBone *ebone, const float screen_co_a[2], const float screen_co_b[2])
static bool edge_fully_inside_rect(const rctf *rect, const float v1[2], const float v2[2])
static bool do_lasso_select_curve(const ViewContext *vc, const Span< int2 > mcoords, const eSelectOp sel_op)
static bool do_grease_pencil_box_select(const ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
static bool ed_object_select_pick(bContext *C, const int mval[2], const SelectPick_Params &params, const bool center, const bool enumerate, const bool object_only)
static int gpu_bone_select_buffer_cmp(const void *sel_a_p, const void *sel_b_p)
static void view3d_userdata_circleselect_init(CircleSelectUserData *r_data, const ViewContext *vc, const bool select, const int mval[2], const float rad)
static bool do_lasso_select_armature(const ViewContext *vc, const Span< int2 > mcoords, const eSelectOp sel_op)
static wmOperatorStatus bone_select_menu_exec(bContext *C, wmOperator *op)
static wmOperatorStatus object_select_menu_exec(bContext *C, wmOperator *op)
static bool selectbuffer_has_bones(const blender::Span< GPUSelectResult > hit_results)
Object * ED_view3d_give_material_slot_under_cursor(bContext *C, const int mval[2], int *r_material_slot)
static bool edge_inside_rect(const rctf *rect, const float v1[2], const float v2[2])
static bool do_lasso_select_paintface(const ViewContext *vc, wmGenericUserData *wm_userdata, const Span< int2 > mcoords, const eSelectOp sel_op)
static SelMenuItemF object_mouse_select_menu_data[SEL_MENU_SIZE]
Object * ED_view3d_give_object_under_cursor(bContext *C, const int mval[2])
static void do_mesh_box_select__doSelectEdge_pass1(void *user_data, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int index)
static bool mball_circle_select(const ViewContext *vc, const eSelectOp sel_op, const int mval[2], float rad)
static void object_select_tag_updates(bContext &C, Scene &scene)
static void do_lasso_select_mesh__doSelectEdge_pass1(void *user_data, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int index)
static void mesh_circle_doSelectFace(void *user_data, BMFace *efa, const float screen_co[2], int)
static void editselect_buf_cache_free(EditSelectBuf_Cache *esel)
static bool armature_circle_doSelectJoint(void *user_data, EditBone *ebone, const float screen_co[2], bool head)
static bool do_mesh_box_select(const ViewContext *vc, wmGenericUserData *wm_userdata, const rcti *rect, const eSelectOp sel_op)
ViewContext ED_view3d_viewcontext_init(bContext *C, Depsgraph *depsgraph)
static bool do_lasso_select_meta(const ViewContext *vc, const Span< int2 > mcoords, const eSelectOp sel_op)
static wmOperatorStatus view3d_circle_select_exec(bContext *C, wmOperator *op)
static void do_mesh_box_select__doSelectVert(void *user_data, BMVert *eve, const float screen_co[2], int)
static const EnumPropertyItem * object_select_menu_enum_itemf(bContext *C, PointerRNA *, PropertyRNA *, bool *r_free)
static bool nurbscurve_circle_select(const ViewContext *vc, const eSelectOp sel_op, const int mval[2], float rad)
static void do_lasso_select_mball__doSelectElem(void *user_data, MetaElem *ml, const float screen_co[2])
static bool ed_curves_select_pick(bContext &C, const int mval[2], const SelectPick_Params &params)
#define SEL_MENU_SIZE
void VIEW3D_OT_select_menu(wmOperatorType *ot)
static bool object_deselect_all_except(const Scene *scene, ViewLayer *view_layer, Base *b)
static bool view3d_lasso_select(bContext *C, ViewContext *vc, const Span< int2 > mcoords, const eSelectOp sel_op)
static bool do_meta_box_select(const ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
static std::string object_select_menu_get_name(wmOperatorType *, PointerRNA *ptr)
static int selectbuffer_ret_hits_15(blender::MutableSpan< GPUSelectResult >, const int hits15)
static void paint_vertsel_circle_select_doSelectVert(void *user_data, const float screen_co[2], int index)
static void do_lasso_select_pose__do_tag(void *user_data, bPoseChannel *pchan, const float screen_co_a[2], const float screen_co_b[2])
static bool do_object_box_select(bContext *C, const ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
static bool object_deselect_all_visible(const Scene *scene, ViewLayer *view_layer, View3D *v3d)
static Base * ed_view3d_give_base_under_cursor_ex(bContext *C, const int mval[2], int *r_material_slot)
bool ED_view3d_is_object_under_cursor(bContext *C, const int mval[2])
static bool do_paintface_box_select(const ViewContext *vc, wmGenericUserData *wm_userdata, const rcti *rect, eSelectOp sel_op)
static void mesh_circle_doSelectVert(void *user_data, BMVert *eve, const float screen_co[2], int)
static void do_circle_select_pose__doSelectBone(void *user_data, bPoseChannel *pchan, const float screen_co_a[2], const float screen_co_b[2])
static void view3d_circle_select_cancel(bContext *C, wmOperator *op)
static bool edbm_backbuf_check_and_select_verts_obmode(Mesh *mesh, EditSelectBuf_Cache *esel, const eSelectOp sel_op)
static bool do_pose_tag_select_op_exec(blender::MutableSpan< Base * > bases, const eSelectOp sel_op)
static bool do_armature_box_select(const ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
static bool armature_circle_select(const ViewContext *vc, const eSelectOp sel_op, const int mval[2], float rad)
static void editselect_buf_cache_init(const ViewContext *vc, short select_mode)
static int mixed_bones_object_selectbuffer_extended(const ViewContext *vc, GPUSelectBuffer *buffer, const int mval[2], eV3DSelectObjectFilter select_filter, bool use_cycle, bool enumerate, bool *r_do_nearest)
static bool edbm_backbuf_check_and_select_verts(EditSelectBuf_Cache *esel, Depsgraph *depsgraph, Object *ob, BMEditMesh *em, const eSelectOp sel_op)
static void do_lasso_select_meshobject__doSelectVert(void *user_data, const float screen_co[2], int index)
static void do_lasso_select_mesh__doSelectFace(void *user_data, BMFace *efa, const float screen_co[2], int)
static bool ed_wpaint_vertex_select_pick(bContext *C, const int mval[2], const SelectPick_Params &params, Object *obact)
static void latticecurve_circle_doSelect(void *user_data, BPoint *bp, const float screen_co[2])
static void view3d_userdata_boxselect_init(BoxSelectUserData *r_data, const ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
static bool do_lasso_select_paintvert(const ViewContext *vc, wmGenericUserData *wm_userdata, const Span< int2 > mcoords, const eSelectOp sel_op)
Base * ED_view3d_give_base_under_cursor(bContext *C, const int mval[2])
static void do_lasso_select_mesh__doSelectEdge_pass0(void *user_data, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int index)
static void deselect_all_tracks(MovieTracking *tracking)
static void editselect_buf_cache_free_voidp(void *esel_voidp)
static void do_circle_select_mball__doSelectElem(void *user_data, MetaElem *ml, const float screen_co[2])
static wmOperatorStatus view3d_select_exec(bContext *C, wmOperator *op)
static bool mesh_circle_select(const ViewContext *vc, wmGenericUserData *wm_userdata, eSelectOp sel_op, const int mval[2], float rad)
static void view3d_circle_select_recalc(void *user_data)
static void nurbscurve_circle_doSelect(void *user_data, Nurb *, BPoint *bp, BezTriple *bezt, int beztindex, bool, const float screen_co[2])
static void do_lasso_select_lattice__doSelect(void *user_data, BPoint *bp, const float screen_co[2])
static wmOperatorStatus view3d_circle_select_modal(bContext *C, wmOperator *op, const wmEvent *event)
bool edge_inside_circle(const float cent[2], float radius, const float screen_co_a[2], const float screen_co_b[2])
void VIEW3D_OT_select_circle(wmOperatorType *ot)
static bool ed_grease_pencil_select_pick(bContext *C, const int mval[2], const SelectPick_Params &params)
void ED_view3d_viewcontext_init_object(ViewContext *vc, Object *obact)
static void do_circle_select_armature__doSelectBone_clip_content(void *user_data, EditBone *ebone, const float screen_co_a[2], const float screen_co_b[2])
void VIEW3D_OT_select_lasso(wmOperatorType *ot)
static wmOperatorStatus view3d_lasso_select_exec(bContext *C, wmOperator *op)
static void do_lasso_select_curve__doSelect(void *user_data, Nurb *, BPoint *bp, BezTriple *bezt, int beztindex, bool handles_visible, const float screen_co[2])
static int mixed_bones_object_selectbuffer(const ViewContext *vc, GPUSelectBuffer *buffer, const int mval[2], eV3DSelectObjectFilter select_filter, bool do_nearest, bool do_nearest_xray, const bool do_material_slot_selection)
static bool ed_object_select_pick_camera_track(bContext *C, Scene *scene, Base *basact, MovieClip *clip, const GPUSelectBuffer &buffer, const short hits, const SelectPick_Params &params)
static bool obedit_circle_select(bContext *C, const ViewContext *vc, wmGenericUserData *wm_userdata, const eSelectOp sel_op, const int mval[2], float rad)
static int selectbuffer_ret_hits_9(blender::MutableSpan< GPUSelectResult > hit_results, const int hits15, const int hits9)
static void do_nurbs_box_select__doSelect(void *user_data, Nurb *, BPoint *bp, BezTriple *bezt, int beztindex, bool handles_visible, const float screen_co[2])
static bool pointcloud_select_pick(bContext &C, const int2 mval, const SelectPick_Params &params)
static bool paint_vertsel_circle_select(const ViewContext *vc, wmGenericUserData *wm_userdata, const eSelectOp sel_op, const int mval[2], float rad)
static bool lattice_circle_select(const ViewContext *vc, const eSelectOp sel_op, const int mval[2], float rad)
static bool view3d_selectable_data(bContext *C)
static int selectbuffer_ret_hits_5(blender::MutableSpan< GPUSelectResult > hit_results, const int hits15, const int hits9, const int hits5)
static bool pchan_circle_doSelectJoint(void *user_data, bPoseChannel *pchan, const float screen_co[2])
static void editselect_buf_cache_init_with_generic_userdata(wmGenericUserData *wm_userdata, const ViewContext *vc, short select_mode)
bool WM_cursor_test_motion_and_update(const int mval[2])
void WM_main_add_notifier(uint type, void *reference)
wmOperatorStatus WM_operator_name_call_ptr(bContext *C, wmOperatorType *ot, wmOperatorCallContext context, PointerRNA *properties, const wmEvent *event)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
PointerRNA * ptr
Definition wm_files.cc:4227
wmOperatorType * ot
Definition wm_files.cc:4226
bool WM_gesture_is_modal_first(const wmGesture *gesture)
void WM_gesture_box_cancel(bContext *C, wmOperator *op)
wmOperatorStatus WM_gesture_circle_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Array< int2 > WM_gesture_lasso_path_to_array(bContext *, wmOperator *op)
wmOperatorStatus WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void WM_gesture_circle_cancel(bContext *C, wmOperator *op)
void WM_gesture_lasso_cancel(bContext *C, wmOperator *op)
wmOperatorStatus WM_gesture_circle_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_lasso_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void WM_operator_properties_border_to_rcti(wmOperator *op, rcti *r_rect)
void WM_operator_properties_gesture_box(wmOperatorType *ot)
void WM_operator_properties_select_operation_simple(wmOperatorType *ot)
void WM_operator_properties_select_operation(wmOperatorType *ot)
void WM_operator_properties_gesture_lasso(wmOperatorType *ot)
void WM_operator_properties_gesture_circle(wmOperatorType *ot)
void WM_operator_properties_mouse_select(wmOperatorType *ot)
wmOperatorType * WM_operatortype_find(const char *idname, bool quiet)
wmOperatorStatus WM_operator_flag_only_pass_through_on_press(wmOperatorStatus retval, const wmEvent *event)
wmOperatorStatus WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)
void WM_operator_properties_create_ptr(PointerRNA *ptr, wmOperatorType *ot)
void WM_operator_properties_free(PointerRNA *ptr)
void WM_toolsystem_update_from_context_view3d(bContext *C)
void WM_generic_user_data_free(wmGenericUserData *wm_userdata)
Definition wm_utils.cc:45