Blender V5.0
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_utf8.h"
35#include "BLI_task.hh"
36#include "BLI_utildefines.h"
37#include "BLI_vector.hh"
38
39#include "BLT_translation.hh"
40
41#include "BKE_action.hh"
42#include "BKE_armature.hh"
43#include "BKE_attribute.hh"
44#include "BKE_context.hh"
45#include "BKE_crazyspace.hh"
46#include "BKE_curve.hh"
47#include "BKE_curves.hh"
48#include "BKE_editmesh.hh"
49#include "BKE_global.hh"
50#include "BKE_grease_pencil.hh"
51#include "BKE_layer.hh"
52#include "BKE_main.hh"
53#include "BKE_mball.hh"
54#include "BKE_mesh.hh"
55#include "BKE_object.hh"
56#include "BKE_paint.hh"
57#include "BKE_scene.hh"
58#include "BKE_tracking.h"
59#include "BKE_workspace.hh"
60
61#include "WM_api.hh"
62#include "WM_toolsystem.hh"
63#include "WM_types.hh"
64
65#include "RNA_access.hh"
66#include "RNA_define.hh"
67#include "RNA_enum_types.hh"
68
69#include "ED_armature.hh"
70#include "ED_curve.hh"
71#include "ED_curves.hh"
72#include "ED_gpencil_legacy.hh"
73#include "ED_grease_pencil.hh"
74#include "ED_lattice.hh"
75#include "ED_mball.hh"
76#include "ED_mesh.hh"
77#include "ED_object.hh"
78#include "ED_outliner.hh"
79#include "ED_particle.hh"
80#include "ED_pointcloud.hh"
81#include "ED_screen.hh"
82#include "ED_sculpt.hh"
83#include "ED_select_utils.hh"
84#include "ED_uvedit.hh"
85
86#include "UI_interface.hh"
87#include "UI_resources.hh"
88
89#include "GPU_matrix.hh"
90#include "GPU_select.hh"
91
92#include "DEG_depsgraph.hh"
94
95#include "DRW_engine.hh"
96#include "DRW_select_buffer.hh"
97
98#include "ANIM_armature.hh"
100
101#include "view3d_intern.hh" /* own include */
102
103// #include "BLI_time_utildefines.h"
104
105using blender::Array;
106using blender::int2;
107using blender::Span;
108using blender::Vector;
110
111/* -------------------------------------------------------------------- */
114
116{
117 return 75.0f * U.pixelsize;
118}
119
121{
122 /* TODO: should return whether there is valid context to continue. */
123 ViewContext vc = {};
124 vc.C = C;
125 vc.region = CTX_wm_region(C);
126 vc.bmain = CTX_data_main(C);
127 vc.depsgraph = depsgraph;
128 vc.scene = CTX_data_scene(C);
130 vc.v3d = CTX_wm_view3d(C);
131 vc.win = CTX_wm_window(C);
135 return vc;
136}
137
139{
140 vc->obact = obact;
141 /* See public doc-string for rationale on checking the existing values first. */
142 if (vc->obedit) {
144 vc->obedit = obact;
145 if (vc->em) {
147 }
148 }
149}
150
152
153/* -------------------------------------------------------------------- */
156
157static bool object_deselect_all_visible(const Scene *scene, ViewLayer *view_layer, View3D *v3d)
158{
159 bool changed = false;
160 BKE_view_layer_synced_ensure(scene, view_layer);
162 if (base->flag & BASE_SELECTED) {
163 if (BASE_SELECTABLE(v3d, base)) {
165 changed = true;
166 }
167 }
168 }
169 return changed;
170}
171
172/* deselect all except b */
173static bool object_deselect_all_except(const Scene *scene, ViewLayer *view_layer, Base *b)
174{
175 bool changed = false;
176 BKE_view_layer_synced_ensure(scene, view_layer);
178 if (base->flag & BASE_SELECTED) {
179 if (b != base) {
181 changed = true;
182 }
183 }
184 }
185 return changed;
186}
187
189
190/* -------------------------------------------------------------------- */
200
204
205static void editselect_buf_cache_init(const ViewContext *vc, short select_mode)
206{
207 if (vc->obedit) {
209 vc->scene, vc->view_layer, vc->v3d);
210
211 DRW_select_buffer_context_create(vc->depsgraph, bases, select_mode);
212 }
213 else {
214 /* Use for paint modes, currently only a single object at a time. */
215 if (vc->obact) {
218 DRW_select_buffer_context_create(vc->depsgraph, {base}, select_mode);
219 }
220 }
221}
222
227
228static void editselect_buf_cache_free_voidp(void *esel_voidp)
229{
230 editselect_buf_cache_free(static_cast<EditSelectBuf_Cache *>(esel_voidp));
231 MEM_freeN(static_cast<EditSelectBuf_Cache *>(esel_voidp));
232}
233
235 const ViewContext *vc,
236 short select_mode)
237{
239 wm_userdata->data = esel;
241 wm_userdata->use_free = true;
242 editselect_buf_cache_init(vc, select_mode);
243}
244
246
247/* -------------------------------------------------------------------- */
250
252 Depsgraph *depsgraph,
253 Object *ob,
254 BMEditMesh *em,
255 UVSyncSelectFromMesh *uv_selctx,
256 const eSelectOp sel_op)
257{
258 BMVert *eve;
259 BMIter iter;
260 bool changed = false;
261
262 const BLI_bitmap *select_bitmap = esel->select_bitmap;
264 if (index == 0) {
265 return false;
266 }
267
268 index -= 1;
269 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
271 const bool is_select = BM_elem_flag_test(eve, BM_ELEM_SELECT);
272 const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index);
273 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
274 if (sel_op_result != -1) {
275 BM_vert_select_set(em->bm, eve, sel_op_result);
276 if (uv_selctx) {
277 uv_selctx->vert_select_set(eve, sel_op_result);
278 }
279
280 changed = true;
281 }
282 }
283 index++;
284 }
285 return changed;
286}
287
289 Depsgraph *depsgraph,
290 Object *ob,
291 BMEditMesh *em,
292 UVSyncSelectFromMesh *uv_selctx,
293 const eSelectOp sel_op)
294{
295 BMEdge *eed;
296 BMIter iter;
297 bool changed = false;
298
299 const BLI_bitmap *select_bitmap = esel->select_bitmap;
301 if (index == 0) {
302 return false;
303 }
304
305 index -= 1;
306 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
308 const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT);
309 const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index);
310 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
311 if (sel_op_result != -1) {
312 BM_edge_select_set(em->bm, eed, sel_op_result);
313 changed = true;
314
315 if (uv_selctx) {
316 uv_selctx->edge_select_set(eed, sel_op_result);
317 }
318 }
319 }
320 index++;
321 }
322 return changed;
323}
324
326 Depsgraph *depsgraph,
327 Object *ob,
328 BMEditMesh *em,
329 UVSyncSelectFromMesh *uv_selctx,
330 const eSelectOp sel_op)
331{
332 BMFace *efa;
333 BMIter iter;
334 bool changed = false;
335
336 const BLI_bitmap *select_bitmap = esel->select_bitmap;
338 if (index == 0) {
339 return false;
340 }
341
342 index -= 1;
343 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
345 const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT);
346 const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index);
347 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
348 if (sel_op_result != -1) {
349 BM_face_select_set(em->bm, efa, sel_op_result);
350 if (uv_selctx) {
351 uv_selctx->face_select_set(efa, sel_op_result);
352 }
353
354 changed = true;
355 }
356 }
357 index++;
358 }
359 return changed;
360}
361
362/* object mode, edbm_ prefix is confusing here, rename? */
365 const eSelectOp sel_op)
366{
367 using namespace blender;
368 bool changed = false;
369
370 const BLI_bitmap *select_bitmap = esel->select_bitmap;
371
372 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
374 ".select_vert", bke::AttrDomain::Point);
375 const VArray<bool> hide_vert = *attributes.lookup_or_default<bool>(
376 ".hide_vert", bke::AttrDomain::Point, false);
377
378 for (int index = 0; index < mesh->verts_num; index++) {
379 if (!hide_vert[index]) {
380 const bool is_select = select_vert.span[index];
381 const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index);
382 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
383 if (sel_op_result != -1) {
384 select_vert.span[index] = sel_op_result == 1;
385 changed = true;
386 }
387 }
388 }
389 select_vert.finish();
390 return changed;
391}
392
393/* object mode, edbm_ prefix is confusing here, rename? */
396 const eSelectOp sel_op)
397{
398 using namespace blender;
399 bool changed = false;
400
401 const BLI_bitmap *select_bitmap = esel->select_bitmap;
402
403 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
405 ".select_poly", bke::AttrDomain::Face);
406 const VArray<bool> hide_poly = *attributes.lookup_or_default<bool>(
407 ".hide_poly", bke::AttrDomain::Face, false);
408
409 for (int index = 0; index < mesh->faces_num; index++) {
410 if (!hide_poly[index]) {
411 const bool is_select = select_poly.span[index];
412 const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index);
413 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
414 if (sel_op_result != -1) {
415 select_poly.span[index] = sel_op_result == 1;
416 changed = true;
417 }
418 }
419 }
420 select_poly.finish();
421 return changed;
422}
423
425
426/* -------------------------------------------------------------------- */
429
447
449 const ViewContext *vc,
450 const rcti *rect,
451 const Span<int2> mcoords,
452 const eSelectOp sel_op)
453{
454 r_data->vc = vc;
455
456 r_data->rect = rect;
457 r_data->rect_fl = &r_data->_rect_fl;
458 BLI_rctf_rcti_copy(&r_data->_rect_fl, rect);
459
460 r_data->mcoords = mcoords;
461 r_data->sel_op = sel_op;
462 /* SELECT by default, but can be changed if needed (only few cases use and respect this). */
464
465 /* runtime */
466 r_data->pass = 0;
467 r_data->is_done = false;
468 r_data->is_changed = false;
469}
470
472{
474 return false;
475 }
476 if (Object *ob = CTX_data_active_object(C)) {
477 if (ob->mode & OB_MODE_EDIT) {
478 return ob->type != OB_FONT;
479 }
482 }
483 if (ob->mode & OB_MODE_WEIGHT_PAINT) {
485 }
486 }
487
488 return true;
489}
490
491/* helper also for box_select */
492static bool edge_fully_inside_rect(const rctf *rect, const float v1[2], const float v2[2])
493{
494 return BLI_rctf_isect_pt_v(rect, v1) && BLI_rctf_isect_pt_v(rect, v2);
495}
496
497static bool edge_inside_rect(const rctf *rect, const float v1[2], const float v2[2])
498{
499 int d1, d2, d3, d4;
500
501 /* check points in rect */
502 if (edge_fully_inside_rect(rect, v1, v2)) {
503 return true;
504 }
505
506 /* check points completely out rect */
507 if (v1[0] < rect->xmin && v2[0] < rect->xmin) {
508 return false;
509 }
510 if (v1[0] > rect->xmax && v2[0] > rect->xmax) {
511 return false;
512 }
513 if (v1[1] < rect->ymin && v2[1] < rect->ymin) {
514 return false;
515 }
516 if (v1[1] > rect->ymax && v2[1] > rect->ymax) {
517 return false;
518 }
519
520 /* simple check lines intersecting. */
521 d1 = (v1[1] - v2[1]) * (v1[0] - rect->xmin) + (v2[0] - v1[0]) * (v1[1] - rect->ymin);
522 d2 = (v1[1] - v2[1]) * (v1[0] - rect->xmin) + (v2[0] - v1[0]) * (v1[1] - rect->ymax);
523 d3 = (v1[1] - v2[1]) * (v1[0] - rect->xmax) + (v2[0] - v1[0]) * (v1[1] - rect->ymax);
524 d4 = (v1[1] - v2[1]) * (v1[0] - rect->xmax) + (v2[0] - v1[0]) * (v1[1] - rect->ymin);
525
526 if (d1 < 0 && d2 < 0 && d3 < 0 && d4 < 0) {
527 return false;
528 }
529 if (d1 > 0 && d2 > 0 && d3 > 0 && d4 > 0) {
530 return false;
531 }
532
533 return true;
534}
535
536static void do_lasso_select_pose__do_tag(void *user_data,
537 bPoseChannel *pchan,
538 const float screen_co_a[2],
539 const float screen_co_b[2])
540{
541 LassoSelectUserData *data = static_cast<LassoSelectUserData *>(user_data);
542 const bArmature *arm = static_cast<bArmature *>(data->vc->obact->data);
543 if (!blender::animrig::bone_is_selectable(arm, pchan)) {
544 return;
545 }
546
547 if (BLI_rctf_isect_segment(data->rect_fl, screen_co_a, screen_co_b) &&
548 BLI_lasso_is_edge_inside(data->mcoords, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX))
549 {
551 data->is_changed = true;
552 }
553}
554static void do_lasso_tag_pose(const ViewContext *vc, const Span<int2> mcoords)
555{
557 rcti rect;
558
559 if ((vc->obact->type != OB_ARMATURE) || (vc->obact->pose == nullptr)) {
560 return;
561 }
562
563 BLI_lasso_boundbox(&rect, mcoords);
564
565 view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, static_cast<eSelectOp>(0));
566
568
569 /* Treat bones as clipped segments (no joints). */
572 &data,
574}
575
577 const Span<int2> mcoords,
578 const eSelectOp sel_op)
579{
580 View3D *v3d = vc->v3d;
581
582 bool changed = false;
583 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
584 changed |= object_deselect_all_visible(vc->scene, vc->view_layer, vc->v3d);
585 }
588 if (BASE_SELECTABLE(v3d, base)) { /* Use this to avoid unnecessary lasso look-ups. */
589 float region_co[2];
590 const bool is_select = base->flag & BASE_SELECTED;
591 const bool is_inside = (ED_view3d_project_base(vc->region, base, region_co) ==
594 int(region_co[0]),
595 int(region_co[1]),
596 /* Dummy value. */
597 INT_MAX);
598 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
599 if (sel_op_result != -1) {
601 sel_op_result ? blender::ed::object::BA_SELECT :
603 changed = true;
604 }
605 }
606 }
607
608 if (changed) {
611 }
612 return changed;
613}
614
619{
620 auto bases_tag_and_append_fn = [](blender::Vector<Base *> &bases, Base *base) {
621 Object *ob = base->object;
622 bArmature *arm = static_cast<bArmature *>(ob->data);
623 LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
625 }
626 arm->id.tag |= ID_TAG_DOIT;
627 ob->id.tag &= ~ID_TAG_DOIT;
628 bases.append(base);
629 };
630
632
633 /* Special case, pose + weight paint mode. */
634 if (vc->obact && (vc->obact->mode & OB_MODE_WEIGHT_PAINT)) {
636 BLI_assert(ob_pose != nullptr); /* Caller is expected to check. */
637 Base *base = BKE_view_layer_base_find(vc->view_layer, ob_pose);
638 if (base) {
639 bases_tag_and_append_fn(bases, base);
640 }
641 }
642 else {
644 vc->scene, vc->view_layer, vc->v3d, OB_ARMATURE, OB_MODE_POSE, base_iter)
645 {
646 bases_tag_and_append_fn(bases, base_iter);
647 }
649 }
650 return bases;
651}
652
654{
655 bool changed_multi = false;
656
657 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
658 for (const int i : bases.index_range()) {
659 Base *base_iter = bases[i];
660 Object *ob_iter = base_iter->object;
661 if (ED_pose_deselect_all(ob_iter, SEL_DESELECT, false)) {
663 changed_multi = true;
664 }
665 }
666 }
667
668 for (const int i : bases.index_range()) {
669 Base *base_iter = bases[i];
670 Object *ob_iter = base_iter->object;
671 bArmature *arm = static_cast<bArmature *>(ob_iter->data);
672
673 bool changed = false;
674 LISTBASE_FOREACH (bPoseChannel *, pchan, &ob_iter->pose->chanbase) {
675 Bone *bone = pchan->bone;
676 if ((bone->flag & BONE_UNSELECTABLE) == 0) {
677 const bool is_select = pchan->flag & POSE_SELECTED;
678 const bool is_inside = pchan->runtime.flag & POSE_RUNTIME_IN_SELECTION_AREA;
679 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
680 if (sel_op_result != -1) {
681 SET_FLAG_FROM_TEST(pchan->flag, sel_op_result, POSE_SELECTED);
682 if (sel_op_result == 0) {
683 if (arm->act_bone == bone) {
684 arm->act_bone = nullptr;
685 }
686 }
687 changed = true;
688 }
689 }
690 }
691 if (changed) {
693 changed_multi = true;
694 }
695 }
696 return changed_multi;
697}
698
699static bool do_lasso_select_pose(const ViewContext *vc,
700 const Span<int2> mcoords,
701 const eSelectOp sel_op)
702{
704
705 ViewContext vc_temp = *vc;
706
707 for (const int i : bases.index_range()) {
708 Base *base_iter = bases[i];
709 Object *ob_iter = base_iter->object;
710 ED_view3d_viewcontext_init_object(&vc_temp, ob_iter);
711 do_lasso_tag_pose(&vc_temp, mcoords);
712 }
713
714 const bool changed_multi = do_pose_tag_select_op_exec(bases, sel_op);
715 if (changed_multi) {
718 }
719
720 return changed_multi;
721}
722
723static void do_lasso_select_mesh__doSelectVert(void *user_data,
724 BMVert *eve,
725 const float screen_co[2],
726 int /*index*/)
727{
728 LassoSelectUserData *data = static_cast<LassoSelectUserData *>(user_data);
729 const bool is_select = BM_elem_flag_test(eve, BM_ELEM_SELECT);
730 const bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
732 data->mcoords, screen_co[0], screen_co[1], IS_CLIPPED));
733 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
734 if (sel_op_result != -1) {
735 BM_vert_select_set(data->vc->em->bm, eve, sel_op_result);
736 if (data->uv_selctx) {
737 data->uv_selctx->vert_select_set(eve, sel_op_result);
738 }
739
740 data->is_changed = true;
741 }
742}
749 BMEdge *eed,
750 const float screen_co_a[2],
751 const float screen_co_b[2],
752 int index)
753{
755 user_data);
756 LassoSelectUserData *data = data_for_edge->data;
757 bool is_visible = true;
758 if (data_for_edge->backbuf_offset) {
759 uint bitmap_inedx = data_for_edge->backbuf_offset + index - 1;
760 is_visible = BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, bitmap_inedx);
761 }
762
763 const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT);
764 const bool is_inside =
765 (is_visible && edge_fully_inside_rect(data->rect_fl, screen_co_a, screen_co_b) &&
766 BLI_lasso_is_point_inside(data->mcoords, UNPACK2(screen_co_a), IS_CLIPPED) &&
767 BLI_lasso_is_point_inside(data->mcoords, UNPACK2(screen_co_b), IS_CLIPPED));
768 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
769 if (sel_op_result != -1) {
770 BM_edge_select_set(data->vc->em->bm, eed, sel_op_result);
771 if (data->uv_selctx) {
772 data->uv_selctx->edge_select_set(eed, sel_op_result);
773 }
774
775 data->is_done = true;
776 data->is_changed = true;
777 }
778}
780 BMEdge *eed,
781 const float screen_co_a[2],
782 const float screen_co_b[2],
783 int index)
784{
786 user_data);
787 LassoSelectUserData *data = data_for_edge->data;
788 bool is_visible = true;
789 if (data_for_edge->backbuf_offset) {
790 uint bitmap_inedx = data_for_edge->backbuf_offset + index - 1;
791 is_visible = BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, bitmap_inedx);
792 }
793
794 const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT);
795 const bool is_inside = (is_visible && BLI_lasso_is_edge_inside(data->mcoords,
796 UNPACK2(screen_co_a),
797 UNPACK2(screen_co_b),
798 IS_CLIPPED));
799 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
800 if (sel_op_result != -1) {
801 BM_edge_select_set(data->vc->em->bm, eed, sel_op_result);
802 if (data->uv_selctx) {
803 data->uv_selctx->edge_select_set(eed, sel_op_result);
804 }
805
806 data->is_changed = true;
807 }
808}
809
810static void do_lasso_select_mesh__doSelectFace(void *user_data,
811 BMFace *efa,
812 const float screen_co[2],
813 int /*index*/)
814{
815 LassoSelectUserData *data = static_cast<LassoSelectUserData *>(user_data);
816 const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT);
817 const bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
819 data->mcoords, screen_co[0], screen_co[1], IS_CLIPPED));
820 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
821 if (sel_op_result != -1) {
822 BM_face_select_set(data->vc->em->bm, efa, sel_op_result);
823 if (data->uv_selctx) {
824 data->uv_selctx->face_select_set(efa, sel_op_result);
825 }
826
827 data->is_changed = true;
828 }
829}
830
831static bool do_lasso_select_mesh(const ViewContext *vc,
832 wmGenericUserData *wm_userdata,
833 const Span<int2> mcoords,
834 const eSelectOp sel_op)
835{
838 rcti rect;
839
841
842 BLI_lasso_boundbox(&rect, mcoords);
843
844 view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, sel_op);
845
846 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
847 if (vc->em->bm->totvertsel) {
849 data.is_changed = true;
850 }
851 }
852
853 std::unique_ptr<UVSyncSelectFromMesh> uv_selctx = UVSyncSelectFromMesh::create_if_needed(
854 *ts, *vc->em->bm);
855 data.uv_selctx = uv_selctx.get();
856
857 /* for non zbuf projections, don't change the GL state */
859
861
862 const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d);
863
864 EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
865 if (use_zbuf) {
866 if (wm_userdata->data == nullptr) {
868 esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
870 vc->depsgraph, vc->region, vc->v3d, mcoords, &rect, nullptr);
871 }
872 }
873
874 if (ts->selectmode & SCE_SELECT_VERTEX) {
875 if (use_zbuf) {
877 esel, vc->depsgraph, vc->obedit, vc->em, data.uv_selctx, sel_op);
878 }
879 else {
882 }
883 }
884 if (ts->selectmode & SCE_SELECT_EDGE) {
885 /* Does both use_zbuf and non-use_zbuf versions (need screen cos for both) */
886 LassoSelectUserData_ForMeshEdge data_for_edge{};
887 data_for_edge.data = &data;
888 data_for_edge.esel = use_zbuf ? esel : nullptr;
890 vc->depsgraph, vc->obedit, SCE_SELECT_EDGE) :
891 0;
892
893 const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_NEAR |
894 (use_zbuf ? (eV3DProjTest)0 : V3D_PROJ_TEST_CLIP_BB);
895 /* Fully inside. */
897 vc, do_lasso_select_mesh__doSelectEdge_pass0, &data_for_edge, clip_flag);
898 if (data.is_done == false) {
899 /* Fall back to partially inside.
900 * Clip content to account for edges partially behind the view. */
903 &data_for_edge,
905 }
906 }
907
908 if (ts->selectmode & SCE_SELECT_FACE) {
909 if (use_zbuf) {
911 esel, vc->depsgraph, vc->obedit, vc->em, data.uv_selctx, sel_op);
912 }
913 else {
916 }
917 }
918
919 if (data.is_changed) {
921 }
922
923 if (data.uv_selctx) {
924 data.uv_selctx->apply();
925 }
926
927 return data.is_changed;
928}
929
930static void do_lasso_select_curve__doSelect(void *user_data,
931 Nurb * /*nu*/,
932 BPoint *bp,
933 BezTriple *bezt,
934 int beztindex,
935 bool handles_visible,
936 const float screen_co[2])
937{
938 LassoSelectUserData *data = static_cast<LassoSelectUserData *>(user_data);
939
941 data->mcoords, screen_co[0], screen_co[1], IS_CLIPPED);
942 if (bp) {
943 const bool is_select = bp->f1 & SELECT;
944 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
945 if (sel_op_result != -1) {
946 SET_FLAG_FROM_TEST(bp->f1, sel_op_result, data->select_flag);
947 data->is_changed = true;
948 }
949 }
950 else {
951 if (!handles_visible) {
952 /* can only be (beztindex == 1) here since handles are hidden */
953 const bool is_select = bezt->f2 & SELECT;
954 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
955 if (sel_op_result != -1) {
956 SET_FLAG_FROM_TEST(bezt->f2, sel_op_result, data->select_flag);
957 }
958 bezt->f1 = bezt->f3 = bezt->f2;
959 data->is_changed = true;
960 }
961 else {
962 uint8_t *flag_p = (&bezt->f1) + beztindex;
963 const bool is_select = *flag_p & SELECT;
964 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
965 if (sel_op_result != -1) {
966 SET_FLAG_FROM_TEST(*flag_p, sel_op_result, data->select_flag);
967 data->is_changed = true;
968 }
969 }
970 }
971}
972
973static bool do_lasso_select_curve(const ViewContext *vc,
974 const Span<int2> mcoords,
975 const eSelectOp sel_op)
976{
977 const bool deselect_all = (sel_op == SEL_OP_SET);
979 rcti rect;
980
981 BLI_lasso_boundbox(&rect, mcoords);
982
983 view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, sel_op);
984
985 Curve *curve = (Curve *)vc->obedit->data;
986 ListBase *nurbs = BKE_curve_editNurbs_get(curve);
987
988 /* For deselect all, items to be selected are tagged with temp flag. Clear that first. */
989 if (deselect_all) {
991 data.select_flag = BEZT_FLAG_TEMP_TAG;
992 }
993
994 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
996
997 /* Deselect items that were not added to selection (indicated by temp flag). */
998 if (deselect_all) {
1000 }
1001
1002 if (data.is_changed) {
1004 }
1005 return data.is_changed;
1006}
1007
1008static void do_lasso_select_lattice__doSelect(void *user_data,
1009 BPoint *bp,
1010 const float screen_co[2])
1011{
1012 LassoSelectUserData *data = static_cast<LassoSelectUserData *>(user_data);
1013 const bool is_select = bp->f1 & SELECT;
1014 const bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
1016 data->mcoords, screen_co[0], screen_co[1], IS_CLIPPED));
1017 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
1018 if (sel_op_result != -1) {
1019 SET_FLAG_FROM_TEST(bp->f1, sel_op_result, SELECT);
1020 data->is_changed = true;
1021 }
1022}
1024 const Span<int2> mcoords,
1025 const eSelectOp sel_op)
1026{
1028 rcti rect;
1029
1030 BLI_lasso_boundbox(&rect, mcoords);
1031
1032 view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, sel_op);
1033
1034 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1035 data.is_changed |= ED_lattice_flags_set(vc->obedit, 0);
1036 }
1037
1038 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
1041 return data.is_changed;
1042}
1043
1044static void do_lasso_select_armature__doSelectBone(void *user_data,
1045 EditBone *ebone,
1046 const float screen_co_a[2],
1047 const float screen_co_b[2])
1048{
1049 LassoSelectUserData *data = static_cast<LassoSelectUserData *>(user_data);
1050 const bArmature *arm = static_cast<const bArmature *>(data->vc->obedit->data);
1051 if (!blender::animrig::bone_is_visible(arm, ebone)) {
1052 return;
1053 }
1054
1055 int is_ignore_flag = 0;
1056 int is_inside_flag = 0;
1057
1058 if (screen_co_a[0] != IS_CLIPPED) {
1059 if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_a)) &&
1060 BLI_lasso_is_point_inside(data->mcoords, UNPACK2(screen_co_a), INT_MAX))
1061 {
1062 is_inside_flag |= BONESEL_ROOT;
1063 }
1064 }
1065 else {
1066 is_ignore_flag |= BONESEL_ROOT;
1067 }
1068
1069 if (screen_co_b[0] != IS_CLIPPED) {
1070 if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_b)) &&
1071 BLI_lasso_is_point_inside(data->mcoords, UNPACK2(screen_co_b), INT_MAX))
1072 {
1073 is_inside_flag |= BONESEL_TIP;
1074 }
1075 }
1076 else {
1077 is_ignore_flag |= BONESEL_TIP;
1078 }
1079
1080 if (is_ignore_flag == 0) {
1081 if (is_inside_flag == (BONE_ROOTSEL | BONE_TIPSEL) ||
1083 data->mcoords, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX))
1084 {
1085 is_inside_flag |= BONESEL_BONE;
1086 }
1087 }
1088
1089 ebone->temp.i = is_inside_flag | (is_ignore_flag >> 16);
1090}
1092 EditBone *ebone,
1093 const float screen_co_a[2],
1094 const float screen_co_b[2])
1095{
1096 LassoSelectUserData *data = static_cast<LassoSelectUserData *>(user_data);
1097 bArmature *arm = static_cast<bArmature *>(data->vc->obedit->data);
1098 if (!blender::animrig::bone_is_visible(arm, ebone)) {
1099 return;
1100 }
1101
1102 const int is_ignore_flag = ebone->temp.i << 16;
1103 int is_inside_flag = ebone->temp.i & ~0xFFFF;
1104
1105 /* - When #BONESEL_BONE is set, there is nothing to do.
1106 * - When #BONE_ROOTSEL or #BONE_TIPSEL have been set - they take priority over bone selection.
1107 */
1108 if (is_inside_flag & (BONESEL_BONE | BONE_ROOTSEL | BONE_TIPSEL)) {
1109 return;
1110 }
1111
1112 if (BLI_lasso_is_edge_inside(data->mcoords, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX))
1113 {
1114 is_inside_flag |= BONESEL_BONE;
1115 }
1116
1117 ebone->temp.i = is_inside_flag | (is_ignore_flag >> 16);
1118}
1119
1121 const Span<int2> mcoords,
1122 const eSelectOp sel_op)
1123{
1125 rcti rect;
1126
1127 BLI_lasso_boundbox(&rect, mcoords);
1128
1129 view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, sel_op);
1130
1131 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1133 }
1134
1135 bArmature *arm = static_cast<bArmature *>(vc->obedit->data);
1136
1138
1140
1141 /* Operate on fully visible (non-clipped) points. */
1144
1145 /* Operate on bones as segments clipped to the viewport bounds
1146 * (needed to handle bones with both points outside the view).
1147 * A separate pass is needed since clipped coordinates can't be used for selecting joints. */
1150 &data,
1152
1153 data.is_changed |= ED_armature_edit_select_op_from_tagged(arm, sel_op);
1154
1155 if (data.is_changed) {
1157 }
1158 return data.is_changed;
1159}
1160
1161static void do_lasso_select_mball__doSelectElem(void *user_data,
1162 MetaElem *ml,
1163 const float screen_co[2])
1164{
1165 LassoSelectUserData *data = static_cast<LassoSelectUserData *>(user_data);
1166 const bool is_select = ml->flag & SELECT;
1167 const bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
1169 data->mcoords, screen_co[0], screen_co[1], INT_MAX));
1170 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
1171 if (sel_op_result != -1) {
1172 SET_FLAG_FROM_TEST(ml->flag, sel_op_result, SELECT);
1173 data->is_changed = true;
1174 }
1175}
1176static bool do_lasso_select_meta(const ViewContext *vc,
1177 const Span<int2> mcoords,
1178 const eSelectOp sel_op)
1179{
1181 rcti rect;
1182
1183 MetaBall *mb = (MetaBall *)vc->obedit->data;
1184
1185 BLI_lasso_boundbox(&rect, mcoords);
1186
1187 view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, sel_op);
1188
1189 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1190 data.is_changed |= BKE_mball_deselect_all(mb);
1191 }
1192
1194
1197
1198 return data.is_changed;
1199}
1200
1202 const Span<int2> mcoords,
1203 const eSelectOp sel_op)
1204{
1205 using namespace blender;
1206 Object *object = (vc->obedit ? vc->obedit : vc->obact);
1207 const Object *ob_eval = DEG_get_evaluated(vc->depsgraph, object);
1208 const GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
1210 vc->scene->toolsettings, object);
1211
1213 vc,
1214 sel_op,
1216 const IndexMask &mask,
1217 const StringRef attribute_name,
1218 IndexMaskMemory &memory) {
1220 const bke::greasepencil::Layer &layer = grease_pencil.layer(info.layer_index);
1221 const bke::crazyspace::GeometryDeformation deformation =
1223 ob_eval, *object, info.drawing);
1224 const IndexMask visible_handle_elements =
1226 *object,
1227 info.drawing,
1228 info.layer_index,
1229 selection_domain,
1231 memory);
1232 const float4x4 layer_to_world = layer.to_world_space(*ob_eval);
1234 layer_to_world);
1235
1237 curves,
1238 deformation,
1239 projection,
1240 mask,
1241 visible_handle_elements,
1242 selection_domain,
1243 attribute_name,
1244 mcoords,
1245 memory);
1246 });
1247}
1248
1254 const float screen_co[2],
1255 int index)
1256{
1257 using namespace blender;
1259 static_cast<LassoSelectUserData_ForMeshObjectVert *>(user_data);
1260 LassoSelectUserData *data = &mesh_data->lasso_data;
1261 const bool is_select = mesh_data->select_vert[index];
1262 const bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
1264 data->mcoords, screen_co[0], screen_co[1], IS_CLIPPED));
1265 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
1266 if (sel_op_result != -1) {
1267 mesh_data->select_vert[index] = sel_op_result == 1;
1268 data->is_changed = true;
1269 }
1270}
1272 wmGenericUserData *wm_userdata,
1273 const Span<int2> mcoords,
1274 const eSelectOp sel_op)
1275{
1276 using namespace blender;
1277 const bool use_zbuf = !XRAY_ENABLED(vc->v3d);
1278 Object *ob = vc->obact;
1279 Mesh *mesh = static_cast<Mesh *>(ob->data);
1280 rcti rect;
1281
1282 if (mesh == nullptr || mesh->verts_num == 0) {
1283 return false;
1284 }
1285
1286 bool changed = false;
1287 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1288 /* flush selection at the end */
1289 changed |= paintvert_deselect_all_visible(ob, SEL_DESELECT, false);
1290 }
1291
1292 BLI_lasso_boundbox(&rect, mcoords);
1293
1294 EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
1295 if (use_zbuf) {
1296 if (wm_userdata->data == nullptr) {
1298 esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
1300 vc->depsgraph, vc->region, vc->v3d, mcoords, &rect, nullptr);
1301 }
1302 }
1303
1304 if (use_zbuf) {
1305 if (esel->select_bitmap != nullptr) {
1306 changed |= edbm_backbuf_check_and_select_verts_obmode(mesh, esel, sel_op);
1307 }
1308 }
1309 else {
1310 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
1311 bke::SpanAttributeWriter<bool> select_vert = attributes.lookup_or_add_for_write_span<bool>(
1312 ".select_vert", bke::AttrDomain::Point);
1313
1315 data.select_vert = select_vert.span;
1316
1317 view3d_userdata_lassoselect_init(&data.lasso_data, vc, &rect, mcoords, sel_op);
1318
1320
1323
1324 changed |= data.lasso_data.is_changed;
1325 select_vert.finish();
1326 }
1327
1328 if (changed) {
1329 if (SEL_OP_CAN_DESELECT(sel_op)) {
1331 }
1334 }
1335
1336 return changed;
1337}
1339 wmGenericUserData *wm_userdata,
1340 const Span<int2> mcoords,
1341 const eSelectOp sel_op)
1342{
1343 Object *ob = vc->obact;
1344 Mesh *mesh = static_cast<Mesh *>(ob->data);
1345 rcti rect;
1346
1347 if (mesh == nullptr || mesh->faces_num == 0) {
1348 return false;
1349 }
1350
1351 bool changed = false;
1352 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1353 /* flush selection at the end */
1354 changed |= paintface_deselect_all_visible(vc->C, ob, SEL_DESELECT, false);
1355 }
1356
1357 BLI_lasso_boundbox(&rect, mcoords);
1358
1359 EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
1360 if (esel == nullptr) {
1362 esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
1364 vc->depsgraph, vc->region, vc->v3d, mcoords, &rect, nullptr);
1365 }
1366
1367 if (esel->select_bitmap) {
1368 changed |= edbm_backbuf_check_and_select_faces_obmode(mesh, esel, sel_op);
1369 }
1370
1371 if (changed) {
1372 paintface_flush_flags(vc->C, ob, true, false);
1373 }
1374 return changed;
1375}
1376
1378 ViewContext *vc,
1379 const Span<int2> mcoords,
1380 const eSelectOp sel_op)
1381{
1382 using namespace blender;
1384 bool changed_multi = false;
1385
1386 wmGenericUserData wm_userdata_buf = {nullptr, nullptr, false};
1387 wmGenericUserData *wm_userdata = &wm_userdata_buf;
1388
1389 if (vc->obedit == nullptr) { /* Object Mode */
1391 changed_multi |= do_lasso_select_paintface(vc, wm_userdata, mcoords, sel_op);
1392 }
1393 else if (BKE_paint_select_vert_test(ob)) {
1394 changed_multi |= do_lasso_select_paintvert(vc, wm_userdata, mcoords, sel_op);
1395 }
1397 changed_multi |= do_lasso_select_grease_pencil(vc, mcoords, sel_op);
1398 }
1399 else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT)) {
1400 changed_multi |= PE_lasso_select(C,
1401 reinterpret_cast<const int (*)[2]>(mcoords.data()),
1402 mcoords.size(),
1403 sel_op) != OPERATOR_CANCELLED;
1404 }
1405 else if (ob &&
1406 ((ob->mode & OB_MODE_POSE) | ((ob->mode & OB_MODE_WEIGHT_PAINT) &&
1408 {
1409 changed_multi |= do_lasso_select_pose(vc, mcoords, sel_op);
1410 if (changed_multi) {
1412 }
1413 }
1414 else if (ob &&
1416 {
1417 /* pass */
1418 }
1419 else {
1420 changed_multi |= do_lasso_select_objects(vc, mcoords, sel_op);
1421 if (changed_multi) {
1423 }
1424 }
1425 }
1426 else { /* Edit Mode */
1427 if (vc->obedit->type == OB_MESH) {
1429 }
1430
1431 FOREACH_OBJECT_IN_MODE_BEGIN (vc->scene, vc->view_layer, vc->v3d, ob->type, ob->mode, ob_iter)
1432 {
1434 bool changed = false;
1435
1436 switch (vc->obedit->type) {
1437 case OB_MESH:
1438 changed = do_lasso_select_mesh(vc, wm_userdata, mcoords, sel_op);
1439 break;
1440 case OB_CURVES_LEGACY:
1441 case OB_SURF:
1442 changed = do_lasso_select_curve(vc, mcoords, sel_op);
1443 break;
1444 case OB_LATTICE:
1445 changed = do_lasso_select_lattice(vc, mcoords, sel_op);
1446 break;
1447 case OB_ARMATURE:
1448 changed = do_lasso_select_armature(vc, mcoords, sel_op);
1449 if (changed) {
1451 }
1452 break;
1453 case OB_MBALL:
1454 changed = do_lasso_select_meta(vc, mcoords, sel_op);
1455 break;
1456 case OB_CURVES: {
1457 Curves &curves_id = *static_cast<Curves *>(vc->obedit->data);
1458 bke::CurvesGeometry &curves = curves_id.geometry.wrap();
1461 const bke::AttrDomain selection_domain = bke::AttrDomain(curves_id.selection_domain);
1462 const IndexRange elements(curves.attributes().domain_size(selection_domain));
1463 const float4x4 projection = ED_view3d_ob_project_mat_get(vc->rv3d, vc->obedit);
1464 changed = ed::curves::select_lasso(*vc,
1465 curves,
1466 deformation,
1467 projection,
1468 elements,
1469 elements,
1470 selection_domain,
1471 mcoords,
1472 sel_op);
1473 if (changed) {
1474 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
1475 * generic attribute for now. */
1476 DEG_id_tag_update(static_cast<ID *>(vc->obedit->data), ID_RECALC_GEOMETRY);
1478 }
1479 break;
1480 }
1481 case OB_POINTCLOUD: {
1482 PointCloud &pointcloud = *static_cast<PointCloud *>(vc->obedit->data);
1483 const float4x4 projection = ED_view3d_ob_project_mat_get(vc->rv3d, vc->obedit);
1485 pointcloud, *vc->region, projection, mcoords, sel_op);
1486 if (changed) {
1487 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
1488 * generic attribute for now. */
1489 DEG_id_tag_update(static_cast<ID *>(vc->obedit->data), ID_RECALC_GEOMETRY);
1491 }
1492 break;
1493 }
1494 case OB_GREASE_PENCIL: {
1495 changed = do_lasso_select_grease_pencil(vc, mcoords, sel_op);
1496 break;
1497 }
1498 default:
1499 BLI_assert_msg(0, "lasso select on incorrect object type");
1500 break;
1501 }
1502
1503 if (changed) {
1504 DEG_id_tag_update(static_cast<ID *>(vc->obedit->data), ID_RECALC_SELECT);
1506 changed_multi = true;
1507 }
1508 }
1510 }
1511
1512 WM_generic_user_data_free(wm_userdata);
1513
1514 return changed_multi;
1515}
1516
1517/* lasso operator gives properties, but since old code works
1518 * with short array we convert */
1520{
1522 if (mcoords.is_empty()) {
1523 return OPERATOR_PASS_THROUGH;
1524 }
1525
1529
1530 /* setup view context for argument to callbacks */
1532
1533 eSelectOp sel_op = static_cast<eSelectOp>(RNA_enum_get(op->ptr, "mode"));
1534 bool changed_multi = view3d_lasso_select(C, &vc, mcoords, sel_op);
1535
1536 if (changed_multi) {
1537 return OPERATOR_FINISHED;
1538 }
1539 return OPERATOR_CANCELLED;
1540}
1541
1543{
1544 ot->name = "Lasso Select";
1545 ot->description = "Select items using lasso selection";
1546 ot->idname = "VIEW3D_OT_select_lasso";
1547
1548 ot->invoke = WM_gesture_lasso_invoke;
1549 ot->modal = WM_gesture_lasso_modal;
1551 ot->poll = view3d_selectable_data;
1552 ot->cancel = WM_gesture_lasso_cancel;
1553
1554 /* flags */
1556
1557 /* properties */
1560}
1561
1563
1564/* -------------------------------------------------------------------- */
1567
1568/* The max number of menu items in an object select menu */
1575
1576#define SEL_MENU_SIZE 22
1578
1579/* special (crappy) operator only for menu select */
1581 PointerRNA * /*ptr*/,
1582 PropertyRNA * /*prop*/,
1583 bool *r_free)
1584{
1585 EnumPropertyItem *item = nullptr, item_tmp = {0};
1586 int totitem = 0;
1587 int i = 0;
1588
1589 /* Don't need context but avoid API doc-generation using this. */
1590 if (C == nullptr || object_mouse_select_menu_data[i].idname[0] == '\0') {
1592 }
1593
1594 for (; i < SEL_MENU_SIZE && object_mouse_select_menu_data[i].idname[0] != '\0'; i++) {
1595 item_tmp.name = object_mouse_select_menu_data[i].idname;
1596 item_tmp.identifier = object_mouse_select_menu_data[i].idname;
1597 item_tmp.value = i;
1598 item_tmp.icon = object_mouse_select_menu_data[i].icon;
1599 RNA_enum_item_add(&item, &totitem, &item_tmp);
1600 }
1601
1602 RNA_enum_item_end(&item, &totitem);
1603 *r_free = true;
1604
1605 return item;
1606}
1607
1609{
1610 const int name_index = RNA_enum_get(op->ptr, "name");
1611 const bool extend = RNA_boolean_get(op->ptr, "extend");
1612 const bool deselect = RNA_boolean_get(op->ptr, "deselect");
1613 const bool toggle = RNA_boolean_get(op->ptr, "toggle");
1614 bool changed = false;
1615 const char *name = object_mouse_select_menu_data[name_index].idname;
1616
1617 View3D *v3d = CTX_wm_view3d(C);
1618 Scene *scene = CTX_data_scene(C);
1619 ViewLayer *view_layer = CTX_data_view_layer(C);
1620 BKE_view_layer_synced_ensure(scene, view_layer);
1621 const Base *oldbasact = BKE_view_layer_active_base_get(view_layer);
1622
1623 Base *basact = nullptr;
1624 CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
1625 /* This is a bit dodgy, there should only be ONE object with this name,
1626 * but library objects can mess this up. */
1627 if (STREQ(name, base->object->id.name + 2)) {
1628 basact = base;
1629 break;
1630 }
1631 }
1633
1634 if (basact == nullptr) {
1635 return OPERATOR_CANCELLED;
1636 }
1637 UNUSED_VARS_NDEBUG(v3d);
1638 BLI_assert(BASE_SELECTABLE(v3d, basact));
1639
1640 if (extend) {
1642 changed = true;
1643 }
1644 else if (deselect) {
1646 changed = true;
1647 }
1648 else if (toggle) {
1649 if (basact->flag & BASE_SELECTED) {
1650 if (basact == oldbasact) {
1652 changed = true;
1653 }
1654 }
1655 else {
1657 changed = true;
1658 }
1659 }
1660 else {
1661 object_deselect_all_except(scene, view_layer, basact);
1663 changed = true;
1664 }
1665
1666 if (oldbasact != basact) {
1668 }
1669
1670 /* weak but ensures we activate menu again before using the enum */
1672
1673 /* undo? */
1674 if (changed) {
1675 Scene *scene = CTX_data_scene(C);
1678
1680
1681 return OPERATOR_FINISHED;
1682 }
1683 return OPERATOR_CANCELLED;
1684}
1685
1687{
1688 if (RNA_boolean_get(ptr, "deselect")) {
1689 return CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Deselect Object");
1690 }
1691 if (RNA_boolean_get(ptr, "toggle")) {
1692 return CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Toggle Object Selection");
1693 }
1694 return CTX_IFACE_(BLT_I18NCONTEXT_OPERATOR_DEFAULT, "Select Object");
1695}
1696
1698{
1699 PropertyRNA *prop;
1700
1701 /* identifiers */
1702 ot->name = "Select Menu";
1703 ot->description = "Menu object selection";
1704 ot->idname = "VIEW3D_OT_select_menu";
1705
1706 /* API callbacks. */
1707 ot->invoke = WM_menu_invoke;
1709 ot->get_name = object_select_menu_get_name;
1710
1711 /* flags */
1712 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1713
1714 /* #Object.id.name to select (dynamic enum). */
1715 prop = RNA_def_enum(ot->srna, "name", rna_enum_dummy_NULL_items, 0, "Object Name", "");
1718 ot->prop = prop;
1719
1720 prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "");
1722 prop = RNA_def_boolean(ot->srna, "deselect", false, "Deselect", "");
1724 prop = RNA_def_boolean(ot->srna, "toggle", false, "Toggle", "");
1726}
1727
1732 const ViewContext *vc,
1733 const blender::Span<GPUSelectResult> hit_results,
1734 const int mval[2],
1736 Base **r_basact)
1737{
1738
1739 const float mval_fl[2] = {float(mval[0]), float(mval[1])};
1740 /* Distance from object center to use for selection. */
1741 const float dist_threshold_sq = square_f(15 * U.pixelsize);
1742 int base_count = 0;
1743
1744 struct BaseRefWithDepth {
1745 BaseRefWithDepth *next, *prev;
1746 Base *base;
1748 uint depth_id;
1749 };
1750 ListBase base_ref_list = {nullptr, nullptr}; /* List of #BaseRefWithDepth. */
1751
1752 /* handle base->object->select_id */
1753 CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
1754 bool ok = false;
1755 uint depth_id;
1756
1757 /* two selection methods, the CTRL select uses max dist of 15 */
1758 if (!hit_results.is_empty()) {
1759 for (const GPUSelectResult &hit_result : hit_results) {
1760 /* index was converted */
1761 if (base->object->runtime->select_id == (hit_result.id & ~0xFFFF0000)) {
1762 ok = true;
1763 depth_id = hit_result.depth;
1764 break;
1765 }
1766 }
1767 }
1768 else {
1769 float region_co[2];
1770 if (ED_view3d_project_base(vc->region, base, region_co) == V3D_PROJ_RET_OK) {
1771 const float dist_test_sq = len_squared_v2v2(mval_fl, region_co);
1772 if (dist_test_sq < dist_threshold_sq) {
1773 ok = true;
1774 /* Match GPU depth logic, as the float is always positive, it can be sorted as an int. */
1775 depth_id = float_as_uint(dist_test_sq);
1776 }
1777 }
1778 }
1779
1780 if (ok) {
1781 base_count++;
1782 BaseRefWithDepth *base_ref = MEM_callocN<BaseRefWithDepth>(__func__);
1783 base_ref->base = base;
1784 base_ref->depth_id = depth_id;
1785 BLI_addtail(&base_ref_list, (void *)base_ref);
1786 }
1787 }
1789
1790 *r_basact = nullptr;
1791
1792 if (base_count == 0) {
1793 return false;
1794 }
1795 if (base_count == 1) {
1796 Base *base = ((BaseRefWithDepth *)base_ref_list.first)->base;
1797 BLI_freelistN(&base_ref_list);
1798 *r_basact = base;
1799 return false;
1800 }
1801
1802 /* Sort by depth or distance to cursor. */
1803 BLI_listbase_sort(&base_ref_list, [](const void *a, const void *b) {
1804 return int(static_cast<const BaseRefWithDepth *>(a)->depth_id >
1805 static_cast<const BaseRefWithDepth *>(b)->depth_id);
1806 });
1807
1808 while (base_count > SEL_MENU_SIZE) {
1809 BLI_freelinkN(&base_ref_list, base_ref_list.last);
1810 base_count -= 1;
1811 }
1812
1813 /* UI, full in static array values that we later use in an enum function */
1814
1816
1817 int i;
1818 LISTBASE_FOREACH_INDEX (BaseRefWithDepth *, base_ref, &base_ref_list, i) {
1819 Base *base = base_ref->base;
1820 Object *ob = base->object;
1821 const char *name = ob->id.name + 2;
1822
1825 }
1826
1827 wmOperatorType *ot = WM_operatortype_find("VIEW3D_OT_select_menu", false);
1829
1831 RNA_boolean_set(&ptr, "extend", params.sel_op == SEL_OP_ADD);
1832 RNA_boolean_set(&ptr, "deselect", params.sel_op == SEL_OP_SUB);
1833 RNA_boolean_set(&ptr, "toggle", params.sel_op == SEL_OP_XOR);
1836
1837 BLI_freelistN(&base_ref_list);
1838 return true;
1839}
1840
1842{
1843 const int name_index = RNA_enum_get(op->ptr, "name");
1844
1847
1848 View3D *v3d = CTX_wm_view3d(C);
1849 Scene *scene = CTX_data_scene(C);
1850 ViewLayer *view_layer = CTX_data_view_layer(C);
1851 BKE_view_layer_synced_ensure(scene, view_layer);
1852 const Base *oldbasact = BKE_view_layer_active_base_get(view_layer);
1853
1854 Base *basact = object_mouse_select_menu_data[name_index].base_ptr;
1855
1856 if (basact == nullptr) {
1857 return OPERATOR_CANCELLED;
1858 }
1859
1860 BLI_assert(BASE_SELECTABLE(v3d, basact));
1861
1862 if (basact->object->mode & OB_MODE_EDIT) {
1863 EditBone *ebone = (EditBone *)object_mouse_select_menu_data[name_index].item_ptr;
1865 }
1866 else {
1867 bPoseChannel *pchan = (bPoseChannel *)object_mouse_select_menu_data[name_index].item_ptr;
1868 ED_armature_pose_select_pick_bone(scene, view_layer, v3d, basact->object, pchan, params);
1869 }
1870
1871 /* Weak but ensures we activate the menu again before using the enum. */
1873
1874 /* We make the armature selected:
1875 * Not-selected active object in pose-mode won't work well for tools. */
1877
1880
1881 /* In weight-paint, we use selected bone to select vertex-group,
1882 * so don't switch to new active object. */
1883 if (oldbasact) {
1884 if (basact->object->mode & OB_MODE_EDIT) {
1885 /* Pass. */
1886 }
1887 else if (oldbasact->object->mode & OB_MODE_ALL_WEIGHT_PAINT) {
1888 /* Prevent activating.
1889 * Selection causes this to be considered the 'active' pose in weight-paint mode.
1890 * Eventually this limitation may be removed.
1891 * For now, de-select all other pose objects deforming this mesh. */
1892 ED_armature_pose_select_in_wpaint_mode(scene, view_layer, basact);
1893 }
1894 else {
1895 if (oldbasact != basact) {
1897 }
1898 }
1899 }
1900
1901 /* Undo? */
1905
1907
1908 return OPERATOR_FINISHED;
1909}
1910
1912{
1913 PropertyRNA *prop;
1914
1915 /* identifiers */
1916 ot->name = "Select Menu";
1917 ot->description = "Menu bone selection";
1918 ot->idname = "VIEW3D_OT_bone_select_menu";
1919
1920 /* API callbacks. */
1921 ot->invoke = WM_menu_invoke;
1922 ot->exec = bone_select_menu_exec;
1923
1924 /* flags */
1925 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1926
1927 /* #Object.id.name to select (dynamic enum). */
1928 prop = RNA_def_enum(ot->srna, "name", rna_enum_dummy_NULL_items, 0, "Bone Name", "");
1931 ot->prop = prop;
1932
1933 prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "");
1935 prop = RNA_def_boolean(ot->srna, "deselect", false, "Deselect", "");
1937 prop = RNA_def_boolean(ot->srna, "toggle", false, "Toggle", "");
1939}
1940
1945 const blender::Span<GPUSelectResult> hit_results,
1946 const bool is_editmode,
1948{
1949 int bone_count = 0;
1950
1951 struct BoneRefWithDepth {
1952 BoneRefWithDepth *next, *prev;
1953 Base *base;
1954 union {
1955 EditBone *ebone;
1956 bPoseChannel *pchan;
1957 void *bone_ptr;
1958 };
1960 uint depth_id;
1961 };
1962 ListBase bone_ref_list = {nullptr, nullptr};
1963
1964 GSet *added_bones = BLI_gset_ptr_new("Bone mouse select menu");
1965
1966 /* Select logic taken from #ed_armature_pick_bone_from_selectbuffer_impl
1967 * in `armature_select.cc`. */
1968 for (const GPUSelectResult &hit_result : hit_results) {
1969 void *bone_ptr = nullptr;
1970 Base *bone_base = nullptr;
1971 uint select_id = hit_result.id;
1972
1973 if (!(select_id & BONESEL_ANY)) {
1974 /* To avoid including objects in selection. */
1975 continue;
1976 }
1977
1978 select_id &= ~BONESEL_ANY;
1979 const uint hit_object = select_id & 0xFFFF;
1980
1981 /* Find the hit bone base (armature object). */
1982 CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
1983 if (base->object->runtime->select_id == hit_object) {
1984 bone_base = base;
1985 break;
1986 }
1987 }
1989
1990 if (!bone_base) {
1991 continue;
1992 }
1993
1994 /* Determine what the current bone is */
1995 if (is_editmode) {
1996 const uint hit_bone = (select_id & ~BONESEL_ANY) >> 16;
1997 bArmature *arm = static_cast<bArmature *>(bone_base->object->data);
1998 EditBone *ebone = static_cast<EditBone *>(BLI_findlink(arm->edbo, hit_bone));
1999 if (ebone && !(ebone->flag & BONE_UNSELECTABLE)) {
2000 bone_ptr = ebone;
2001 }
2002 }
2003 else {
2004 const uint hit_bone = (select_id & ~BONESEL_ANY) >> 16;
2005 bPoseChannel *pchan = static_cast<bPoseChannel *>(
2006 BLI_findlink(&bone_base->object->pose->chanbase, hit_bone));
2007 if (pchan && !(pchan->bone->flag & BONE_UNSELECTABLE)) {
2008 bone_ptr = pchan;
2009 }
2010 }
2011
2012 if (!bone_ptr) {
2013 continue;
2014 }
2015 /* We can hit a bone multiple times, so make sure we are not adding an already included bone
2016 * to the list. */
2017 const bool is_duplicate_bone = BLI_gset_haskey(added_bones, bone_ptr);
2018
2019 if (!is_duplicate_bone) {
2020 bone_count++;
2021 BoneRefWithDepth *bone_ref = MEM_callocN<BoneRefWithDepth>(__func__);
2022 bone_ref->base = bone_base;
2023 bone_ref->bone_ptr = bone_ptr;
2024 bone_ref->depth_id = hit_result.depth;
2025 BLI_addtail(&bone_ref_list, (void *)bone_ref);
2026
2027 BLI_gset_insert(added_bones, bone_ptr);
2028 }
2029 }
2030
2031 BLI_gset_free(added_bones, nullptr);
2032
2033 if (bone_count == 0) {
2034 return false;
2035 }
2036 if (bone_count == 1) {
2037 BLI_freelistN(&bone_ref_list);
2038 return false;
2039 }
2040
2041 /* Sort by depth or distance to cursor. */
2042 BLI_listbase_sort(&bone_ref_list, [](const void *a, const void *b) {
2043 return int(static_cast<const BoneRefWithDepth *>(a)->depth_id >
2044 static_cast<const BoneRefWithDepth *>(b)->depth_id);
2045 });
2046
2047 while (bone_count > SEL_MENU_SIZE) {
2048 BLI_freelinkN(&bone_ref_list, bone_ref_list.last);
2049 bone_count -= 1;
2050 }
2051
2052 /* UI, full in static array values that we later use in an enum function */
2054
2055 int i;
2056 LISTBASE_FOREACH_INDEX (BoneRefWithDepth *, bone_ref, &bone_ref_list, i) {
2057 char *name;
2058
2059 object_mouse_select_menu_data[i].base_ptr = bone_ref->base;
2060
2061 if (is_editmode) {
2062 EditBone *ebone = bone_ref->ebone;
2063 object_mouse_select_menu_data[i].item_ptr = static_cast<void *>(ebone);
2064 name = ebone->name;
2065 }
2066 else {
2067 bPoseChannel *pchan = bone_ref->pchan;
2068 object_mouse_select_menu_data[i].item_ptr = static_cast<void *>(pchan);
2069 name = pchan->name;
2070 }
2071
2073 object_mouse_select_menu_data[i].icon = ICON_BONE_DATA;
2074 }
2075
2076 wmOperatorType *ot = WM_operatortype_find("VIEW3D_OT_bone_select_menu", false);
2078
2080 RNA_boolean_set(&ptr, "extend", params.sel_op == SEL_OP_ADD);
2081 RNA_boolean_set(&ptr, "deselect", params.sel_op == SEL_OP_SUB);
2082 RNA_boolean_set(&ptr, "toggle", params.sel_op == SEL_OP_XOR);
2085
2086 BLI_freelistN(&bone_ref_list);
2087 return true;
2088}
2089
2091{
2092 for (const GPUSelectResult &hit_result : hit_results) {
2093 if (hit_result.id & 0xFFFF0000) {
2094 return true;
2095 }
2096 }
2097 return false;
2098}
2099
2100/* utility function for mixed_bones_object_selectbuffer */
2102 const int hits15)
2103{
2104 return hits15;
2105}
2106
2108 const int hits15,
2109 const int hits9)
2110{
2111 const int ofs = hits15;
2112 /* Shift results to beginning. */
2113 hit_results.slice(0, hits9).copy_from(hit_results.slice(ofs, hits9));
2114 return hits9;
2115}
2116
2118 const int hits15,
2119 const int hits9,
2120 const int hits5)
2121{
2122 const int ofs = hits15 + hits9;
2123 /* Shift results to beginning. */
2124 hit_results.slice(0, hits5).copy_from(hit_results.slice(ofs, hits5));
2125 return hits5;
2126}
2127
2137 GPUSelectBuffer *buffer,
2138 const int mval[2],
2139 eV3DSelectObjectFilter select_filter,
2140 bool do_nearest,
2141 bool do_nearest_xray,
2142 const bool do_material_slot_selection)
2143{
2144 rcti rect;
2145 int hits15, hits9 = 0, hits5 = 0;
2146 bool has_bones15 = false, has_bones9 = false, has_bones5 = false;
2147
2149 int hits = 0;
2150
2151 if (do_nearest_xray) {
2152 select_mode = VIEW3D_SELECT_PICK_ALL;
2153 }
2154
2155 /* we _must_ end cache before return, use 'goto finally' */
2157
2158 GPUSelectStorage &storage = buffer->storage;
2159 BLI_rcti_init_pt_radius(&rect, mval, 14);
2160 hits15 = view3d_gpu_select_ex(
2161 vc, buffer, &rect, select_mode, select_filter, do_material_slot_selection);
2162 if (hits15 == 1) {
2163 hits = selectbuffer_ret_hits_15(storage.as_mutable_span(), hits15);
2164 goto finally;
2165 }
2166 else if (hits15 > 0) {
2167 int ofs;
2168 has_bones15 = selectbuffer_has_bones(storage.as_span().slice(0, hits15));
2169
2170 ofs = hits15;
2171 BLI_rcti_init_pt_radius(&rect, mval, 9);
2172 hits9 = view3d_gpu_select(vc, buffer, &rect, select_mode, select_filter);
2173 if (hits9 == 1) {
2174 hits = selectbuffer_ret_hits_9(storage.as_mutable_span(), hits15, hits9);
2175 goto finally;
2176 }
2177 else if (hits9 > 0) {
2178 has_bones9 = selectbuffer_has_bones(storage.as_span().slice(ofs, hits9));
2179
2180 ofs += hits9;
2181 BLI_rcti_init_pt_radius(&rect, mval, 5);
2182 hits5 = view3d_gpu_select(vc, buffer, &rect, select_mode, select_filter);
2183 if (hits5 == 1) {
2184 hits = selectbuffer_ret_hits_5(storage.as_mutable_span(), hits15, hits9, hits5);
2185 goto finally;
2186 }
2187 else if (hits5 > 0) {
2188 has_bones5 = selectbuffer_has_bones(storage.as_span().slice(ofs, hits5));
2189 }
2190 }
2191
2192 if (has_bones5) {
2193 hits = selectbuffer_ret_hits_5(storage.as_mutable_span(), hits15, hits9, hits5);
2194 goto finally;
2195 }
2196 else if (has_bones9) {
2197 hits = selectbuffer_ret_hits_9(storage.as_mutable_span(), hits15, hits9);
2198 goto finally;
2199 }
2200 else if (has_bones15) {
2201 hits = selectbuffer_ret_hits_15(storage.as_mutable_span(), hits15);
2202 goto finally;
2203 }
2204
2205 if (hits5 > 0) {
2206 hits = selectbuffer_ret_hits_5(storage.as_mutable_span(), hits15, hits9, hits5);
2207 goto finally;
2208 }
2209 else if (hits9 > 0) {
2210 hits = selectbuffer_ret_hits_9(storage.as_mutable_span(), hits15, hits9);
2211 goto finally;
2212 }
2213 else {
2214 hits = selectbuffer_ret_hits_15(storage.as_mutable_span(), hits15);
2215 goto finally;
2216 }
2217 }
2218
2219finally:
2221 return hits;
2222}
2223
2225 GPUSelectBuffer *buffer,
2226 const int mval[2],
2227 eV3DSelectObjectFilter select_filter,
2228 bool use_cycle,
2229 bool enumerate,
2230 bool *r_do_nearest)
2231{
2232 bool do_nearest = false;
2233 View3D *v3d = vc->v3d;
2234
2235 /* define if we use solid nearest select or not */
2236 if (use_cycle) {
2237 /* Update the coordinates (even if the return value isn't used). */
2238 const bool has_motion = WM_cursor_test_motion_and_update(mval);
2239 if (!XRAY_ACTIVE(v3d)) {
2240 do_nearest = has_motion;
2241 }
2242 }
2243 else {
2244 if (!XRAY_ACTIVE(v3d)) {
2245 do_nearest = true;
2246 }
2247 }
2248
2249 if (r_do_nearest) {
2250 *r_do_nearest = do_nearest;
2251 }
2252
2253 do_nearest = do_nearest && !enumerate;
2254
2256 vc, buffer, mval, select_filter, do_nearest, true, false);
2257
2258 return hits;
2259}
2260
2265static int gpu_select_buffer_depth_id_cmp(const void *sel_a_p, const void *sel_b_p)
2266{
2267 GPUSelectResult *a = (GPUSelectResult *)sel_a_p;
2268 GPUSelectResult *b = (GPUSelectResult *)sel_b_p;
2269
2270 if (a->depth < b->depth) {
2271 return -1;
2272 }
2273 if (a->depth > b->depth) {
2274 return 1;
2275 }
2276
2277 /* Depths match, sort by id. */
2278 /* NOTE: this is endianness-sensitive.
2279 * GPUSelectResult values are always expected to be little-endian. */
2280 uint sel_a = a->id;
2281 uint sel_b = b->id;
2282
2283 if (sel_a < sel_b) {
2284 return -1;
2285 }
2286 if (sel_a > sel_b) {
2287 return 1;
2288 }
2289 return 0;
2290}
2291
2300 const GPUSelectBuffer &buffer,
2301 int hits,
2302 bool do_nearest,
2303 bool has_bones,
2304 bool do_bones_get_priotity,
2305 int *r_select_id_subelem)
2306{
2307 Scene *scene = vc->scene;
2308 ViewLayer *view_layer = vc->view_layer;
2309 View3D *v3d = vc->v3d;
2310 int a;
2311
2312 bool found = false;
2313 int select_id = 0;
2314 int select_id_subelem = 0;
2315
2316 if (do_nearest) {
2317 uint min = 0xFFFFFFFF;
2318 int hit_index = -1;
2319
2320 if (has_bones && do_bones_get_priotity) {
2321 /* we skip non-bone hits */
2322 for (a = 0; a < hits; a++) {
2323 if (min > buffer.storage[a].depth && (buffer.storage[a].id & 0xFFFF0000)) {
2324 min = buffer.storage[a].depth;
2325 hit_index = a;
2326 }
2327 }
2328 }
2329 else {
2330
2331 for (a = 0; a < hits; a++) {
2332 /* Any object. */
2333 if (min > buffer.storage[a].depth) {
2334 min = buffer.storage[a].depth;
2335 hit_index = a;
2336 }
2337 }
2338 }
2339
2340 if (hit_index != -1) {
2341 select_id = buffer.storage[hit_index].id & 0xFFFF;
2342 select_id_subelem = (buffer.storage[hit_index].id & 0xFFFF0000) >> 16;
2343 found = true;
2344 /* No need to set `min` to `buffer.storage[hit_index].depth`, it's not used from now on. */
2345 }
2346 }
2347 else {
2348
2349 GPUSelectStorage buffer_sorted = buffer.storage;
2350 {
2351 buffer_sorted.resize(hits);
2352 /* Remove non-bone objects. */
2353 if (has_bones && do_bones_get_priotity) {
2354 /* Loop backwards to reduce re-ordering. */
2355 for (a = hits - 1; a >= 0; a--) {
2356 if ((buffer_sorted[a].id & 0xFFFF0000) == 0) {
2357 buffer_sorted[a] = buffer_sorted[--hits];
2358 }
2359 }
2360 }
2361 qsort(buffer_sorted.data(), hits, sizeof(GPUSelectResult), gpu_select_buffer_depth_id_cmp);
2362 }
2363
2364 int hit_index = -1;
2365
2366 /* It's possible there are no hits (all objects contained bones). */
2367 if (hits > 0) {
2368 /* Only exclude active object when it is selected. */
2369 BKE_view_layer_synced_ensure(scene, view_layer);
2370 Base *base = BKE_view_layer_active_base_get(view_layer);
2371 if (base && (base->flag & BASE_SELECTED)) {
2372 const int select_id_active = base->object->runtime->select_id;
2373 for (int i_next = 0, i_prev = hits - 1; i_next < hits; i_prev = i_next++) {
2374 if ((select_id_active == (buffer_sorted[i_prev].id & 0xFFFF)) &&
2375 (select_id_active != (buffer_sorted[i_next].id & 0xFFFF)))
2376 {
2377 hit_index = i_next;
2378 break;
2379 }
2380 }
2381 }
2382
2383 /* When the active object is unselected or not in `buffer`, use the nearest. */
2384 if (hit_index == -1) {
2385 /* Just pick the nearest. */
2386 hit_index = 0;
2387 }
2388 }
2389
2390 if (hit_index != -1) {
2391 select_id = buffer_sorted[hit_index].id & 0xFFFF;
2392 select_id_subelem = (buffer_sorted[hit_index].id & 0xFFFF0000) >> 16;
2393 found = true;
2394 }
2395 }
2396
2397 Base *basact = nullptr;
2398 if (found) {
2399 BKE_view_layer_synced_ensure(scene, view_layer);
2401 if (has_bones ? BASE_VISIBLE(v3d, base) : BASE_SELECTABLE(v3d, base)) {
2402 if (base->object->runtime->select_id == select_id) {
2403 basact = base;
2404 break;
2405 }
2406 }
2407 }
2408
2409 if (basact && r_select_id_subelem) {
2410 *r_select_id_subelem = select_id_subelem;
2411 }
2412 }
2413
2414 return basact;
2415}
2416
2417static Base *mouse_select_object_center(const ViewContext *vc, Base *startbase, const int mval[2])
2418{
2419 ARegion *region = vc->region;
2420 Scene *scene = vc->scene;
2421 ViewLayer *view_layer = vc->view_layer;
2422 View3D *v3d = vc->v3d;
2423
2424 BKE_view_layer_synced_ensure(scene, view_layer);
2425 Base *oldbasact = BKE_view_layer_active_base_get(view_layer);
2426
2427 const float mval_fl[2] = {float(mval[0]), float(mval[1])};
2428 float dist = ED_view3d_select_dist_px() * 1.3333f;
2429 Base *basact = nullptr;
2430
2431 /* Put the active object at a disadvantage to cycle through other objects. */
2432 const float penalty_dist = 10.0f * UI_SCALE_FAC;
2433 Base *base = startbase;
2434 while (base) {
2435 if (BASE_SELECTABLE(v3d, base)) {
2436 float screen_co[2];
2438 base->object->object_to_world().location(),
2439 screen_co,
2441 {
2442 float dist_test = len_manhattan_v2v2(mval_fl, screen_co);
2443 if (base == oldbasact) {
2444 dist_test += penalty_dist;
2445 }
2446 if (dist_test < dist) {
2447 dist = dist_test;
2448 basact = base;
2449 }
2450 }
2451 }
2452 base = base->next;
2453
2454 if (base == nullptr) {
2455 base = static_cast<Base *>(BKE_view_layer_object_bases_get(view_layer)->first);
2456 }
2457 if (base == startbase) {
2458 break;
2459 }
2460 }
2461 return basact;
2462}
2463
2465 const int mval[2],
2466 int *r_material_slot)
2467{
2469 Base *basact = nullptr;
2470 GPUSelectBuffer buffer;
2471
2472 /* setup view context for argument to callbacks */
2475
2477
2478 const bool do_nearest = !XRAY_ACTIVE(vc.v3d);
2479 const bool do_material_slot_selection = r_material_slot != nullptr;
2480 const int hits = mixed_bones_object_selectbuffer(
2481 &vc, &buffer, mval, VIEW3D_SELECT_FILTER_NOP, do_nearest, false, do_material_slot_selection);
2482
2483 if (hits > 0) {
2484 const bool has_bones = (r_material_slot == nullptr) &&
2485 selectbuffer_has_bones(buffer.storage.as_span().slice(0, hits));
2486 basact = mouse_select_eval_buffer(
2487 &vc, buffer, hits, do_nearest, has_bones, true, r_material_slot);
2488 }
2489
2490 return basact;
2491}
2492
2494{
2495 return ed_view3d_give_base_under_cursor_ex(C, mval, nullptr);
2496}
2497
2499{
2501 if (base) {
2502 return base->object;
2503 }
2504 return nullptr;
2505}
2506
2508 const int mval[2],
2509 int *r_material_slot)
2510{
2511 Base *base = ed_view3d_give_base_under_cursor_ex(C, mval, r_material_slot);
2512 if (base) {
2513 return base->object;
2514 }
2515 return nullptr;
2516}
2517
2519{
2520 return ED_view3d_give_object_under_cursor(C, mval) != nullptr;
2521}
2522
2524{
2525 LISTBASE_FOREACH (MovieTrackingObject *, tracking_object, &tracking->objects) {
2526 LISTBASE_FOREACH (MovieTrackingTrack *, track, &tracking_object->tracks) {
2528 }
2529 }
2530}
2531
2533 Scene *scene,
2534 Base *basact,
2535 MovieClip *clip,
2536 const GPUSelectBuffer &buffer,
2537 const short hits,
2539{
2540 bool changed = false;
2541 bool found = false;
2542
2543 MovieTracking *tracking = &clip->tracking;
2544 ListBase *tracksbase = nullptr;
2545 MovieTrackingTrack *track = nullptr;
2546
2547 for (int i = 0; i < hits; i++) {
2548 const int select_id = buffer.storage[i].id;
2549
2550 /* If there's bundles in buffer select bundles first,
2551 * so non-camera elements should be ignored in buffer. */
2552 if (basact->object->runtime->select_id != (select_id & 0xFFFF)) {
2553 continue;
2554 }
2555 /* Index of bundle is 1<<16-based. if there's no "bone" index
2556 * in height word, this buffer value belongs to camera. not to bundle. */
2557 if ((select_id & 0xFFFF0000) == 0) {
2558 continue;
2559 }
2560
2562 &clip->tracking, select_id >> 16, &tracksbase);
2563 found = true;
2564 break;
2565 }
2566
2567 /* Note `params.deselect_all` is ignored for tracks as in this case
2568 * all objects will be de-selected (not tracks). */
2569 if (params.sel_op == SEL_OP_SET) {
2570 if ((found && params.select_passthrough) && TRACK_SELECTED(track)) {
2571 found = false;
2572 }
2573 else if (found /* `|| params.deselect_all` */) {
2574 /* Deselect everything. */
2575 deselect_all_tracks(tracking);
2576 changed = true;
2577 }
2578 }
2579
2580 if (found) {
2581 switch (params.sel_op) {
2582 case SEL_OP_ADD: {
2583 BKE_tracking_track_select(tracksbase, track, TRACK_AREA_ALL, true);
2584 break;
2585 }
2586 case SEL_OP_SUB: {
2588 break;
2589 }
2590 case SEL_OP_XOR: {
2591 if (TRACK_SELECTED(track)) {
2593 }
2594 else {
2595 BKE_tracking_track_select(tracksbase, track, TRACK_AREA_ALL, true);
2596 }
2597 break;
2598 }
2599 case SEL_OP_SET: {
2600 BKE_tracking_track_select(tracksbase, track, TRACK_AREA_ALL, false);
2601 break;
2602 }
2603 case SEL_OP_AND: {
2604 BLI_assert_unreachable(); /* Doesn't make sense for picking. */
2605 break;
2606 }
2607 }
2608
2613
2614 changed = true;
2615 }
2616
2617 return changed || found;
2618}
2619
2633 const int mval[2],
2635 const bool center,
2636 const bool enumerate,
2637 const bool object_only)
2638{
2640 /* Setup view context for argument to callbacks. */
2642
2643 Scene *scene = vc.scene;
2644 View3D *v3d = vc.v3d;
2645
2646 /* Menu activation may find a base to make active (if it only finds a single item to select). */
2647 Base *basact_override = nullptr;
2648
2649 const bool is_obedit = (vc.obedit != nullptr);
2650 if (object_only) {
2651 /* Signal for #view3d_gpu_select to skip edit-mode objects. */
2652 vc.obedit = nullptr;
2653 }
2654
2655 /* Set for GPU depth buffer picking, leave null when selecting by center. */
2656 struct GPUData {
2657 GPUSelectBuffer buffer;
2658 int hits;
2659 bool do_nearest;
2660 bool has_bones;
2661 } *gpu = nullptr;
2662
2663 /* First handle menu selection, early exit if a menu opens
2664 * since this takes ownership of the selection action.
2665 *
2666 * Even when there is no menu `basact_override` may be set to avoid having to re-find
2667 * the item under the cursor. */
2668
2669 if (center == false) {
2670 gpu = MEM_new<GPUData>(__func__);
2671 gpu->do_nearest = false;
2672 gpu->has_bones = false;
2673
2674 /* If objects have pose-mode set, the bones are in the same selection buffer. */
2675 const eV3DSelectObjectFilter select_filter = ((object_only == false) ?
2677 vc.obact) :
2680 &vc, &gpu->buffer, mval, select_filter, true, enumerate, &gpu->do_nearest);
2681 gpu->has_bones = (object_only && gpu->hits > 0) ?
2682 false :
2683 selectbuffer_has_bones(gpu->buffer.storage.as_span().slice(0, gpu->hits));
2684 }
2685
2686 /* First handle menu selection, early exit when a menu was opened.
2687 * Otherwise fall through to regular selection. */
2688 if (enumerate) {
2689 bool has_menu = false;
2690 if (center) {
2691 if (object_mouse_select_menu(C, &vc, {}, mval, params, &basact_override)) {
2692 has_menu = true;
2693 }
2694 }
2695 else {
2696 if (gpu->hits != 0) {
2697 const blender::Span<GPUSelectResult> hit_results = gpu->buffer.storage.as_span().slice(
2698 0, gpu->hits);
2699 if (gpu->has_bones && bone_mouse_select_menu(C, hit_results, false, params)) {
2700 has_menu = true;
2701 }
2702 else if (object_mouse_select_menu(C, &vc, hit_results, mval, params, &basact_override)) {
2703 has_menu = true;
2704 }
2705 }
2706 }
2707
2708 /* Let the menu handle any further actions. */
2709 if (has_menu) {
2710 if (gpu != nullptr) {
2711 MEM_delete(gpu);
2712 }
2713 return false;
2714 }
2715 }
2716
2717 /* No menu, continue with selection. */
2718
2719 ViewLayer *view_layer = vc.view_layer;
2720 BKE_view_layer_synced_ensure(scene, view_layer);
2721 /* Don't set when the context has no active object (hidden), see: #60807. */
2722 const Base *oldbasact = vc.obact ? BKE_view_layer_active_base_get(view_layer) : nullptr;
2723 /* Always start list from `basact` when cycling the selection. */
2724 Base *startbase = (oldbasact && oldbasact->next) ?
2725 oldbasact->next :
2726 static_cast<Base *>(BKE_view_layer_object_bases_get(view_layer)->first);
2727
2728 /* The next object's base to make active. */
2729 Base *basact = nullptr;
2730 const eObjectMode object_mode = oldbasact ? static_cast<eObjectMode>(oldbasact->object->mode) :
2732 /* For the most part this is equivalent to `(object_mode & OB_MODE_POSE) != 0`
2733 * however this logic should also run with weight-paint + pose selection.
2734 * Without this, selection in weight-paint mode can de-select armatures which isn't useful,
2735 * see: #101686. */
2736 const bool has_pose_old = (oldbasact &&
2738
2739 /* When enabled, don't attempt any further selection. */
2740 bool handled = false;
2741
2742 /* Split `changed` into data-types so their associated updates can be properly performed.
2743 * This is also needed as multiple changes may happen at once.
2744 * Selecting a pose-bone or track can also select the object for example */
2745 bool changed_object = false;
2746 bool changed_pose = false;
2747 bool changed_track = false;
2748
2749 /* Handle setting the new base active (even when `handled == true`). */
2750 bool use_activate_selected_base = false;
2751
2752 if (center) {
2753 if (basact_override) {
2754 basact = basact_override;
2755 }
2756 else {
2757 basact = mouse_select_object_center(&vc, startbase, mval);
2758 }
2759 }
2760 else {
2761 if (basact_override) {
2762 basact = basact_override;
2763 }
2764 else {
2765 /* Regarding bone priority.
2766 *
2767 * - When in pose-bone, it's useful that any selection containing a bone
2768 * gets priority over other geometry (background scenery for example).
2769 *
2770 * - When in object-mode, don't prioritize bones as it would cause
2771 * pose-objects behind other objects to get priority
2772 * (mainly noticeable when #SCE_OBJECT_MODE_LOCK is disabled).
2773 *
2774 * This way prioritizing based on pose-mode has a bias to stay in pose-mode
2775 * without having to enforce this through locking the object mode. */
2776 bool do_bones_get_priotity = has_pose_old;
2777
2778 basact = (gpu->hits > 0) ? mouse_select_eval_buffer(&vc,
2779 gpu->buffer,
2780 gpu->hits,
2781 gpu->do_nearest,
2782 gpu->has_bones,
2783 do_bones_get_priotity,
2784 nullptr) :
2785 nullptr;
2786 }
2787
2788 /* See comment for `has_pose_old`, the same rationale applies here. */
2789 const bool has_pose_new = (basact &&
2791
2792 /* Select pose-bones or camera-tracks. */
2793 if (((gpu->hits > 0) && gpu->has_bones) ||
2794 /* Special case, even when there are no hits, pose logic may de-select all bones. */
2795 ((gpu->hits == 0) && has_pose_old))
2796 {
2797 /* Regarding the `basact` null checks.
2798 * While it's unlikely there are GPU hits *without* `basact` being found,
2799 * it's possible looking up the selection index fails, see: #143161. */
2800
2801 if (basact && (gpu->has_bones && (basact->object->type == OB_CAMERA))) {
2802 MovieClip *clip = BKE_object_movieclip_get(scene, basact->object, false);
2803 if (clip != nullptr) {
2805 C, scene, basact, clip, gpu->buffer, gpu->hits, params))
2806 {
2808 /* Don't set `handled` here as the object activation may be necessary. */
2809 changed_object = true;
2810
2811 changed_track = true;
2812 }
2813 else {
2814 /* Fallback to regular object selection if no new bundles were selected,
2815 * allows to select object parented to reconstruction object. */
2816 basact = mouse_select_eval_buffer(
2817 &vc, gpu->buffer, gpu->hits, gpu->do_nearest, false, false, nullptr);
2818 }
2819 }
2820 }
2821 else if ((basact || oldbasact) &&
2823 view_layer,
2824 v3d,
2825 basact ? basact : (Base *)oldbasact,
2826 gpu->buffer.storage.data(),
2827 gpu->hits,
2828 params,
2829 gpu->do_nearest))
2830 {
2831
2832 changed_pose = true;
2833
2834 /* When there is no `baseact` this will have operated on `oldbasact`,
2835 * allowing #SelectPick_Params.deselect_all work in pose-mode.
2836 * In this case no object operations are needed. */
2837 if (basact == nullptr) {
2838 handled = true;
2839 }
2840 else {
2841 /* By convention the armature-object is selected when in pose-mode.
2842 * While leaving it unselected will work, leaving pose-mode would leave the object
2843 * active + unselected which isn't ideal when performing other actions on the object. */
2845 changed_object = true;
2846
2849
2850 /* In weight-paint, we use selected bone to select vertex-group.
2851 * In this case the active object mustn't change as it would leave weight-paint mode. */
2852 if (oldbasact) {
2853 if (oldbasact->object->mode & OB_MODE_ALL_WEIGHT_PAINT) {
2854 /* Prevent activating.
2855 * Selection causes this to be considered the 'active' pose in weight-paint mode.
2856 * Eventually this limitation may be removed.
2857 * For now, de-select all other pose objects deforming this mesh. */
2858 ED_armature_pose_select_in_wpaint_mode(scene, view_layer, basact);
2859
2860 handled = true;
2861 }
2862 else if (has_pose_old && has_pose_new) {
2863 /* Within pose-mode, keep the current selection when switching pose bones,
2864 * this is noticeable when in pose mode with multiple objects at once.
2865 * Where selecting the bone of a different object would de-select this one.
2866 * After that, exiting pose-mode would only have the active armature selected.
2867 * This matches multi-object edit-mode behavior. */
2868 handled = true;
2869
2870 if (oldbasact != basact) {
2871 use_activate_selected_base = true;
2872 }
2873 }
2874 else {
2875 /* Don't set `handled` here as the object selection may be necessary
2876 * when starting out in object-mode and moving into pose-mode,
2877 * when moving from pose to object-mode using object selection also makes sense. */
2878 }
2879 }
2880 }
2881 }
2882 /* Prevent bone/track selecting to pass on to object selecting. */
2883 if (basact == oldbasact) {
2884 handled = true;
2885 }
2886 }
2887 }
2888
2889 bool changed_object_mode = false;
2890
2891 if (handled == false) {
2893 /* No special logic in edit-mode. */
2894 if (is_obedit == false) {
2895 if (basact && !BKE_object_is_mode_compat(basact->object, object_mode)) {
2896 if (object_mode == OB_MODE_OBJECT) {
2897 Main *bmain = vc.bmain;
2898 blender::ed::object::mode_generic_exit(bmain, vc.depsgraph, scene, basact->object);
2899 }
2900 if (!BKE_object_is_mode_compat(basact->object, object_mode)) {
2901 basact = nullptr;
2902 }
2903 }
2904
2905 /* Disallow switching modes,
2906 * special exception for edit-mode - vertex-parent operator. */
2907 if (basact && oldbasact) {
2908 if ((oldbasact->object->mode != basact->object->mode) &&
2909 (oldbasact->object->mode & basact->object->mode) == 0)
2910 {
2911 basact = nullptr;
2912 }
2913 }
2914 }
2915 }
2916 else {
2917 if (basact) {
2918 if (!BKE_object_is_mode_compat(basact->object, object_mode)) {
2919 changed_object_mode = true;
2920 }
2921 }
2922 else if (object_mode != OB_MODE_OBJECT) {
2923 changed_object_mode = true;
2924 }
2925 }
2926 }
2927
2928 /* Ensure code above doesn't change the active base. This code is already fairly involved,
2929 * it's best if changing the active object is localized to a single place. */
2930 BLI_assert(oldbasact == (vc.obact ? BKE_view_layer_active_base_get(view_layer) : nullptr));
2931
2932 if (handled) {
2933 /* Pass. */
2934 }
2935 else if (basact && vc.obedit) {
2936 /* Only do the select (use for setting vertex parents & hooks).
2937 * In edit-mode do not activate. */
2938 object_deselect_all_except(scene, view_layer, basact);
2940
2941 changed_object = true;
2942 }
2943 else {
2944 /* Object-mode (pose mode will have been handled already)
2945 * unless entering pose-mode from object selection (handled by `changed_object_mode`). */
2946
2947 /* NOTE(@ideasman42): When select changes object-mode it doesn't make sense to use
2948 * pass-through.
2949 *
2950 * - When object-mode locking is disabled:
2951 * Selecting another already selected object does not need to make it active,
2952 * allowing a cursor-drag to move this objects as well as other selected objects.
2953 * - When object-mode locking is enabled:
2954 * Selecting an object in a different mode (typically pose-mode) must prioritize
2955 * entering that mode which requires making the object active, further, the selection
2956 * before switching modes wont make sense in the newly entered mode,
2957 * so it makes sense to disable pass-through logic in this case.
2958 *
2959 * See: #115181 for details. */
2960 const bool select_passthrough = params.select_passthrough && (changed_object_mode == false);
2961
2962 bool found = (basact != nullptr) && BASE_SELECTABLE(v3d, basact);
2963 if (params.sel_op == SEL_OP_SET) {
2964 if ((found && select_passthrough) && (basact->flag & BASE_SELECTED)) {
2965 found = false;
2966 }
2967 else if (found || params.deselect_all) {
2968 /* Deselect everything. */
2969 /* `basact` may be nullptr. */
2970 if (object_deselect_all_except(scene, view_layer, basact)) {
2971 changed_object = true;
2972 }
2973 }
2974 }
2975
2976 if (found) {
2977 use_activate_selected_base |= (oldbasact != basact) && (is_obedit == false);
2978
2979 switch (params.sel_op) {
2980 case SEL_OP_ADD: {
2982 break;
2983 }
2984 case SEL_OP_SUB: {
2986 break;
2987 }
2988 case SEL_OP_XOR: {
2989 if (basact->flag & BASE_SELECTED) {
2990 /* Keep selected if the base is to be activated. */
2991 if (use_activate_selected_base == false) {
2993 }
2994 }
2995 else {
2997 }
2998 break;
2999 }
3000 case SEL_OP_SET: {
3001 /* Deselect has already been performed. */
3003 break;
3004 }
3005 case SEL_OP_AND: {
3006 BLI_assert_unreachable(); /* Doesn't make sense for picking. */
3007 break;
3008 }
3009 }
3010
3011 changed_object = true;
3012 }
3013 }
3014
3015 /* Perform the activation even when 'handled', since this is used to ensure
3016 * the object from the pose-bone selected is also activated. */
3017 if (use_activate_selected_base && (basact != nullptr)) {
3018 changed_object = true;
3019 blender::ed::object::base_activate(C, basact); /* adds notifier */
3020 if ((scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) == 0) {
3022 }
3023 }
3024
3025 if (changed_object) {
3028
3030 }
3031
3032 if (changed_pose) {
3034 }
3035
3036 if (gpu != nullptr) {
3037 MEM_delete(gpu);
3038 }
3039
3040 return (changed_object || changed_pose || changed_track);
3041}
3042
3050 const int mval[2],
3052 Object *obact)
3053{
3054 using namespace blender;
3055 View3D *v3d = CTX_wm_view3d(C);
3056 const bool use_zbuf = !XRAY_ENABLED(v3d);
3057
3058 Mesh *mesh = static_cast<Mesh *>(obact->data); /* already checked for nullptr */
3059 uint index = 0;
3060 bool changed = false;
3061
3062 bool found = ED_mesh_pick_vert(C, obact, mval, ED_MESH_PICK_DEFAULT_VERT_DIST, use_zbuf, &index);
3063
3064 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
3065 bke::AttributeWriter<bool> select_vert = attributes.lookup_or_add_for_write<bool>(
3066 ".select_vert", bke::AttrDomain::Point);
3067
3068 if (params.sel_op == SEL_OP_SET) {
3069 if ((found && params.select_passthrough) && select_vert.varray[index]) {
3070 found = false;
3071 }
3072 else if (found || params.deselect_all) {
3073 /* Deselect everything. */
3074 changed |= paintface_deselect_all_visible(C, obact, SEL_DESELECT, false);
3075 }
3076 }
3077
3078 if (found) {
3079 switch (params.sel_op) {
3080 case SEL_OP_ADD: {
3081 select_vert.varray.set(index, true);
3082 break;
3083 }
3084 case SEL_OP_SUB: {
3085 select_vert.varray.set(index, false);
3086 break;
3087 }
3088 case SEL_OP_XOR: {
3089 select_vert.varray.set(index, !select_vert.varray[index]);
3090 break;
3091 }
3092 case SEL_OP_SET: {
3094 select_vert.varray.set(index, true);
3095 break;
3096 }
3097 case SEL_OP_AND: {
3098 BLI_assert_unreachable(); /* Doesn't make sense for picking. */
3099 break;
3100 }
3101 }
3102
3103 /* update mselect */
3104 if (select_vert.varray[index]) {
3106 }
3107 else {
3109 }
3110
3111 select_vert.finish();
3112
3113 paintvert_flush_flags(obact);
3114
3115 changed = true;
3116 }
3117 else {
3118 select_vert.finish();
3119 }
3120
3121 if (changed) {
3123 }
3124
3125 return changed || found;
3126}
3127
3132
3139{
3140 using namespace blender;
3141 using namespace blender::ed;
3143 /* Setup view context for argument to callbacks. */
3145
3147 vc.scene, vc.view_layer, vc.v3d);
3148
3150 bases.index_range(),
3151 1L,
3153 [&](const IndexRange range, const ClosestPointCloud &init) {
3154 ClosestPointCloud new_closest = init;
3155 for (Base *base : bases.as_span().slice(range)) {
3156 Object &object = *base->object;
3157 PointCloud &pointcloud = *static_cast<PointCloud *>(object.data);
3158 const float4x4 projection = ED_view3d_ob_project_mat_get(vc.rv3d, &object);
3159
3160 std::optional<pointcloud::FindClosestData> new_closest_elem =
3162 pointcloud.positions(),
3163 projection,
3164 IndexMask(pointcloud.totpoint),
3165 float2(mval),
3167 new_closest.elem);
3168 if (new_closest_elem) {
3169 new_closest.elem = *new_closest_elem;
3170 new_closest.pointcloud = &pointcloud;
3171 }
3172 }
3173 return new_closest;
3174 },
3175 [](const ClosestPointCloud &a, const ClosestPointCloud &b) {
3176 return (a.elem.distance_sq < b.elem.distance_sq) ? a : b;
3177 });
3178
3179 Array<bool> changed(bases.size(), false);
3180 if (params.deselect_all || params.sel_op == SEL_OP_SET) {
3181 threading::parallel_for(bases.index_range(), 1L, [&](const IndexRange range) {
3182 for (const int i : range) {
3183 PointCloud &pointcloud = *static_cast<PointCloud *>(bases[i]->object->data);
3184 if (!pointcloud::has_anything_selected(pointcloud)) {
3185 continue;
3186 }
3187
3188 bke::GSpanAttributeWriter selection = pointcloud::ensure_selection_attribute(
3189 pointcloud, bke::AttrType::Bool);
3190 pointcloud::fill_selection_false(selection.span, IndexMask(pointcloud.totpoint));
3191 selection.finish();
3192
3193 changed[i] = true;
3194 }
3195 });
3196
3197 for (const int i : bases.index_range()) {
3198 if (changed[i]) {
3199 PointCloud &pointcloud = *static_cast<PointCloud *>(bases[i]->object->data);
3200 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
3201 * generic attribute for now. */
3204 }
3205 }
3206 }
3207
3208 if (!closest.pointcloud) {
3209 return changed.as_span().contains(true);
3210 }
3211
3213 *closest.pointcloud, bke::AttrType::Bool);
3214 curves::apply_selection_operation_at_index(selection.span, closest.elem.index, params.sel_op);
3215 selection.finish();
3216
3217 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
3218 * generic attribute for now. */
3221
3222 return true;
3223}
3224
3230
3236static bool ed_curves_select_pick(bContext &C, const int mval[2], const SelectPick_Params &params)
3237{
3238 using namespace blender;
3240 /* Setup view context for argument to callbacks. */
3242
3244 vc.scene, vc.view_layer, vc.v3d);
3245
3246 Curves &active_curves_id = *static_cast<Curves *>(vc.obedit->data);
3247 const bke::AttrDomain selection_domain = bke::AttrDomain(active_curves_id.selection_domain);
3248
3250 bases.index_range(),
3251 1L,
3253 [&](const IndexRange range, const ClosestCurveDataBlock &init) {
3254 ClosestCurveDataBlock new_closest = init;
3255 for (Base *base : bases.as_span().slice(range)) {
3256 Object &curves_ob = *base->object;
3257 Curves &curves_id = *static_cast<Curves *>(curves_ob.data);
3260 const bke::CurvesGeometry &curves = curves_id.geometry.wrap();
3261 const float4x4 projection = ED_view3d_ob_project_mat_get(vc.rv3d, &curves_ob);
3262 const IndexMask elements(curves.attributes().domain_size(selection_domain));
3263 const auto range_consumer =
3264 [&](IndexRange range, Span<float3> positions, StringRef selection_attribute_name) {
3265 IndexMask mask = elements.slice_content(range);
3266
3267 std::optional<ed::curves::FindClosestData> new_closest_elem =
3269 curves.points_by_curve(),
3270 positions,
3271 curves.cyclic(),
3272 projection,
3273 mask,
3274 selection_domain,
3275 mval,
3276 new_closest.elem);
3277 if (new_closest_elem) {
3278 new_closest.selection_attribute_name = selection_attribute_name;
3279 new_closest.elem = *new_closest_elem;
3280 new_closest.curves_id = &curves_id;
3281 }
3282 };
3283
3284 if (selection_domain == bke::AttrDomain::Point) {
3286 curves,
3287 deformation,
3289 range_consumer);
3290 }
3291 else if (selection_domain == bke::AttrDomain::Curve) {
3293 curves,
3294 deformation,
3296 range_consumer);
3297 };
3298 }
3299 return new_closest;
3300 },
3301 [](const ClosestCurveDataBlock &a, const ClosestCurveDataBlock &b) {
3302 return (a.elem.distance_sq < b.elem.distance_sq) ? a : b;
3303 });
3304
3305 Array<bool> changed(bases.size(), false);
3306 if (params.deselect_all || params.sel_op == SEL_OP_SET) {
3307 threading::parallel_for(bases.index_range(), 1L, [&](const IndexRange range) {
3308 for (const int i : range) {
3309 Curves &curves_id = *static_cast<Curves *>(bases[i]->object->data);
3310 bke::CurvesGeometry &curves = curves_id.geometry.wrap();
3311 if (!ed::curves::has_anything_selected(curves, selection_domain)) {
3312 continue;
3313 }
3314
3315 ed::curves::foreach_selection_attribute_writer(
3316 curves, selection_domain, [](bke::GSpanAttributeWriter &selection) {
3317 ed::curves::fill_selection_false(selection.span);
3318 });
3319
3320 changed[i] = true;
3321 }
3322 });
3323
3324 for (const int i : bases.index_range()) {
3325 if (changed[i]) {
3326 Curves &curves_id = *static_cast<Curves *>(bases[i]->object->data);
3327 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
3328 * generic attribute for now. */
3330 WM_event_add_notifier(&C, NC_GEOM | ND_DATA, &curves_id);
3331 }
3332 }
3333 }
3334
3335 if (!closest.curves_id) {
3336 return changed.as_span().contains(true);
3337 }
3338
3339 if (selection_domain == bke::AttrDomain::Point) {
3341 closest.curves_id->geometry.wrap(),
3344 closest.selection_attribute_name);
3346 selection.span, closest.elem.index, params.sel_op);
3347 selection.finish();
3348 }
3349 else if (selection_domain == bke::AttrDomain::Curve) {
3351 closest.curves_id->geometry.wrap(),
3353 [&](bke::GSpanAttributeWriter &selection) {
3354 ed::curves::apply_selection_operation_at_index(
3355 selection.span, closest.elem.index, params.sel_op);
3356 });
3357 }
3358
3359 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
3360 * generic attribute for now. */
3363
3364 return true;
3365}
3366
3373
3380 const int mval[2],
3382{
3383 using namespace blender;
3385 /* Setup view context for argument to callbacks. */
3387 Object *object = (vc.obedit ? vc.obedit : vc.obact);
3388
3389 /* Collect editable drawings. */
3390 const Object *ob_eval = DEG_get_evaluated(vc.depsgraph, object);
3391 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
3394
3395 /* Get selection domain from tool settings. */
3397 vc.scene->toolsettings, object);
3398
3400 drawings.index_range(),
3401 1L,
3403 [&](const IndexRange range, const ClosestGreasePencilDrawing &init) {
3404 ClosestGreasePencilDrawing new_closest = init;
3405 for (const int i : range) {
3406 ed::greasepencil::MutableDrawingInfo info = drawings[i];
3407 const bke::greasepencil::Layer &layer = grease_pencil.layer(info.layer_index);
3408 /* Get deformation by modifiers. */
3411 ob_eval, *object, info.drawing);
3412
3413 IndexMaskMemory memory;
3415 *object, info, selection_domain, memory);
3416 if (elements.is_empty()) {
3417 continue;
3418 }
3419 const IndexMask visible_handle_elements =
3421 *object,
3422 info.drawing,
3423 info.layer_index,
3424 selection_domain,
3426 memory);
3427 const bke::CurvesGeometry &curves = info.drawing.strokes();
3428 const float4x4 layer_to_world = layer.to_world_space(*ob_eval);
3430 layer_to_world);
3431 const auto range_consumer = [&](const IndexRange range,
3432 const Span<float3> positions,
3433 const StringRef selection_attribute_name) {
3434 const IndexMask mask = ((selection_attribute_name == ".selection") ?
3435 elements :
3436 visible_handle_elements)
3437 .slice_content(range);
3438
3439 std::optional<ed::curves::FindClosestData> new_closest_elem =
3441 curves.points_by_curve(),
3442 positions,
3443 curves.cyclic(),
3444 projection,
3445 mask,
3446 selection_domain,
3447 mval,
3448 new_closest.elem);
3449 if (new_closest_elem) {
3450 new_closest.selection_attribute_name = selection_attribute_name;
3451 new_closest.elem = *new_closest_elem;
3452 new_closest.info_index = i;
3453 new_closest.drawing = &info.drawing;
3454 }
3455 };
3456
3457 if (selection_domain == bke::AttrDomain::Point) {
3459 curves,
3460 deformation,
3462 range_consumer);
3463 }
3464 else if (selection_domain == bke::AttrDomain::Curve) {
3466 curves,
3467 deformation,
3469 range_consumer);
3470 };
3471 }
3472 return new_closest;
3473 },
3475 return (a.elem.distance_sq < b.elem.distance_sq) ? a : b;
3476 });
3477
3478 std::atomic<bool> deselected = false;
3479 if (params.deselect_all || params.sel_op == SEL_OP_SET) {
3480 threading::parallel_for(drawings.index_range(), 1L, [&](const IndexRange range) {
3481 for (const int i : range) {
3482 ed::greasepencil::MutableDrawingInfo info = drawings[i];
3483 IndexMaskMemory memory;
3484 const IndexMask elements = ed::greasepencil::retrieve_editable_elements(
3485 *object, info, selection_domain, memory);
3486 if (elements.is_empty()) {
3487 continue;
3488 }
3489 bke::CurvesGeometry &curves = info.drawing.strokes_for_write();
3490 if (!ed::curves::has_anything_selected(curves, selection_domain, elements)) {
3491 continue;
3492 }
3493
3494 ed::curves::foreach_selection_attribute_writer(
3495 curves, selection_domain, [](bke::GSpanAttributeWriter &selection) {
3496 ed::curves::fill_selection_false(selection.span);
3497 });
3498
3499 deselected = true;
3500 }
3501 });
3502 }
3503
3504 if (!closest.drawing) {
3505 if (deselected) {
3506 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
3507 * generic attribute for now. */
3508 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
3509 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
3510 }
3511 return deselected;
3512 }
3513
3514 const IndexMask selection_mask = IndexRange::from_single(closest.elem.index);
3515
3517 params.sel_op,
3519 const IndexMask & /*universe*/,
3520 StringRef attribute_name,
3521 IndexMaskMemory & /*memory*/) -> IndexMask {
3522 /* Selection update mask is already known, but only applies
3523 * to a specific drawing. */
3524 if (&info.drawing == closest.drawing &&
3525 attribute_name == closest.selection_attribute_name) {
3526 return selection_mask;
3527 }
3528 return {};
3529 });
3530
3531 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
3532 * generic attribute for now. */
3533 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
3534 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
3535
3536 return true;
3537}
3538
3540{
3541 Scene *scene = CTX_data_scene(C);
3542 Object *obedit = CTX_data_edit_object(C);
3544
3547
3549
3550 bool center = RNA_boolean_get(op->ptr, "center");
3551 bool enumerate = RNA_boolean_get(op->ptr, "enumerate");
3552 /* Only force object select for edit-mode to support vertex parenting,
3553 * or paint-select to allow pose bone select with vert/face select. */
3554 bool object_only = (RNA_boolean_get(op->ptr, "object") &&
3555 (obedit || BKE_paint_select_elem_test(obact) ||
3556 /* so its possible to select bones in weight-paint mode (LMB select) */
3557 (obact && (obact->mode & OB_MODE_ALL_WEIGHT_PAINT) &&
3559
3560 /* This could be called "changed_or_found" since this is true when there is an element
3561 * under the cursor to select, even if it happens that the selection & active state doesn't
3562 * actually change. This is important so undo pushes are predictable. */
3563 bool changed = false;
3564 int mval[2];
3565
3566 if (object_only) {
3567 obedit = nullptr;
3568 obact = nullptr;
3569
3570 /* ack, this is incorrect but to do this correctly we would need an
3571 * alternative edit-mode/object-mode keymap, this copies the functionality
3572 * from 2.4x where Ctrl+Select in edit-mode does object select only. */
3573 center = false;
3574 }
3575
3576 if (obedit && enumerate) {
3577 /* Enumerate makes no sense in edit-mode unless also explicitly picking objects or bones.
3578 * Pass the event through so the event may be handled by loop-select for example. See: #100204.
3579 */
3580 if (obedit->type != OB_ARMATURE) {
3582 }
3583 }
3584
3585 RNA_int_get_array(op->ptr, "location", mval);
3586
3589
3590 if (obedit && object_only == false) {
3591 if (obedit->type == OB_MESH) {
3592 changed = EDBM_select_pick(C, mval, params);
3593 }
3594 else if (obedit->type == OB_ARMATURE) {
3595 if (enumerate) {
3596 GPUSelectBuffer buffer;
3597 const int hits = mixed_bones_object_selectbuffer(
3598 &vc, &buffer, mval, VIEW3D_SELECT_FILTER_NOP, false, true, false);
3599 changed = bone_mouse_select_menu(
3600 C, buffer.storage.as_span().take_front(hits), true, params);
3601 }
3602 if (!changed) {
3603 changed = ED_armature_edit_select_pick(C, mval, params);
3604 }
3605 }
3606 else if (obedit->type == OB_LATTICE) {
3607 changed = ED_lattice_select_pick(C, mval, params);
3608 }
3609 else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) {
3611 }
3612 else if (obedit->type == OB_MBALL) {
3613 changed = ED_mball_select_pick(C, mval, params);
3614 }
3615 else if (obedit->type == OB_FONT) {
3616 changed = ED_curve_editfont_select_pick(C, mval, params);
3617 }
3618 else if (obedit->type == OB_POINTCLOUD) {
3619 changed = pointcloud_select_pick(*C, mval, params);
3620 }
3621 else if (obedit->type == OB_CURVES) {
3622 changed = ed_curves_select_pick(*C, mval, params);
3623 }
3624 else if (obedit->type == OB_GREASE_PENCIL) {
3625 changed = ed_grease_pencil_select_pick(C, mval, params);
3626 }
3627 }
3628 else if (obact && obact->mode & OB_MODE_PARTICLE_EDIT) {
3629 changed = PE_mouse_particles(C, mval, params);
3630 }
3631 else if (obact && BKE_paint_select_face_test(obact)) {
3632 changed = paintface_mouse_select(C, mval, params, obact);
3633 }
3634 else if (BKE_paint_select_vert_test(obact)) {
3635 changed = ed_wpaint_vertex_select_pick(C, mval, params, obact);
3636 }
3637 else if (BKE_paint_select_grease_pencil_test(obact)) {
3638 changed = ed_grease_pencil_select_pick(C, mval, params);
3639 }
3640 else {
3641 changed = ed_object_select_pick(C, mval, params, center, enumerate, object_only);
3642 }
3643
3644 /* Pass-through flag may be cleared, see #WM_operator_flag_only_pass_through_on_press. */
3645
3646 /* Pass-through allows tweaks
3647 * FINISHED to signal one operator worked */
3648 if (changed) {
3651 }
3652 /* Nothing selected, just passthrough. */
3654}
3655
3657{
3658 RNA_int_set_array(op->ptr, "location", event->mval);
3659
3660 const wmOperatorStatus retval = view3d_select_exec(C, op);
3661
3662 return WM_operator_flag_only_pass_through_on_press(retval, event);
3663}
3664
3666{
3667 PropertyRNA *prop;
3668
3669 /* identifiers */
3670 ot->name = "Select";
3671 ot->description = "Select and activate item(s)";
3672 ot->idname = "VIEW3D_OT_select";
3673
3674 /* API callbacks. */
3675 ot->invoke = view3d_select_invoke;
3676 ot->exec = view3d_select_exec;
3678 ot->get_name = ED_select_pick_get_name;
3679
3680 /* flags */
3681 ot->flag = OPTYPE_UNDO;
3682
3683 /* properties */
3685
3686 prop = RNA_def_boolean(
3687 ot->srna,
3688 "center",
3689 false,
3690 "Center",
3691 "Use the object center when selecting, in edit mode used to extend object selection");
3693 prop = RNA_def_boolean(ot->srna,
3694 "enumerate",
3695 false,
3696 "Enumerate",
3697 "List objects under the mouse (object mode only)");
3699 prop = RNA_def_boolean(
3700 ot->srna, "object", false, "Object", "Use object selection (edit mode only)");
3702
3703 prop = RNA_def_int_vector(ot->srna,
3704 "location",
3705 2,
3706 nullptr,
3707 INT_MIN,
3708 INT_MAX,
3709 "Location",
3710 "Mouse location",
3711 INT_MIN,
3712 INT_MAX);
3714}
3715
3717
3718/* -------------------------------------------------------------------- */
3721
3737
3739 const ViewContext *vc,
3740 const rcti *rect,
3741 const eSelectOp sel_op)
3742{
3743 r_data->vc = vc;
3744
3745 r_data->rect = rect;
3746 r_data->rect_fl = &r_data->_rect_fl;
3747 BLI_rctf_rcti_copy(&r_data->_rect_fl, rect);
3748
3749 r_data->sel_op = sel_op;
3750 /* SELECT by default, but can be changed if needed (only few cases use and respect this). */
3752
3753 /* runtime */
3754 r_data->is_done = false;
3755 r_data->is_changed = false;
3756}
3757
3758bool edge_inside_circle(const float cent[2],
3759 float radius,
3760 const float screen_co_a[2],
3761 const float screen_co_b[2])
3762{
3763 const float radius_squared = radius * radius;
3764 return (dist_squared_to_line_segment_v2(cent, screen_co_a, screen_co_b) < radius_squared);
3765}
3766
3771static void do_paintvert_box_select__doSelectVert(void *user_data,
3772 const float screen_co[2],
3773 int index)
3774{
3776 static_cast<BoxSelectUserData_ForMeshObjectVert *>(user_data);
3777 BoxSelectUserData *data = &mesh_data->box_data;
3778 const bool is_select = mesh_data->select_vert[index];
3779 const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co);
3780 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
3781 if (sel_op_result != -1) {
3782 mesh_data->select_vert[index] = sel_op_result == 1;
3783 data->is_changed = true;
3784 }
3785}
3787 wmGenericUserData *wm_userdata,
3788 const rcti *rect,
3789 const eSelectOp sel_op)
3790{
3791 using namespace blender;
3792 const bool use_zbuf = !XRAY_ENABLED(vc->v3d);
3793
3794 Mesh *mesh = static_cast<Mesh *>(vc->obact->data);
3795 if ((mesh == nullptr) || (mesh->verts_num == 0)) {
3796 return false;
3797 }
3798
3799 bool changed = false;
3800 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
3801 changed |= paintvert_deselect_all_visible(vc->obact, SEL_DESELECT, false);
3802 }
3803
3804 if (BLI_rcti_is_empty(rect)) {
3805 /* pass */
3806 }
3807 else if (use_zbuf) {
3808 EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
3809 if (wm_userdata->data == nullptr) {
3811 esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
3813 vc->depsgraph, vc->region, vc->v3d, rect, nullptr);
3814 }
3815 if (esel->select_bitmap != nullptr) {
3816 changed |= edbm_backbuf_check_and_select_verts_obmode(mesh, esel, sel_op);
3817 }
3818 }
3819 else {
3820 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
3821 bke::SpanAttributeWriter<bool> select_vert = attributes.lookup_or_add_for_write_span<bool>(
3822 ".select_vert", bke::AttrDomain::Point);
3823
3825 data.select_vert = select_vert.span;
3826
3827 view3d_userdata_boxselect_init(&data.box_data, vc, rect, sel_op);
3828
3830
3833 changed |= data.box_data.is_changed;
3834 select_vert.finish();
3835 }
3836
3837 if (changed) {
3838 if (SEL_OP_CAN_DESELECT(sel_op)) {
3840 }
3843 }
3844 return changed;
3845}
3846
3848 wmGenericUserData *wm_userdata,
3849 const rcti *rect,
3850 eSelectOp sel_op)
3851{
3852 Object *ob = vc->obact;
3853 Mesh *mesh;
3854
3856 if ((mesh == nullptr) || (mesh->faces_num == 0)) {
3857 return false;
3858 }
3859
3860 bool changed = false;
3861 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
3862 changed |= paintface_deselect_all_visible(vc->C, vc->obact, SEL_DESELECT, false);
3863 }
3864
3865 if (BLI_rcti_is_empty(rect)) {
3866 /* pass */
3867 }
3868 else {
3869 EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
3870 if (wm_userdata->data == nullptr) {
3872 esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
3874 vc->depsgraph, vc->region, vc->v3d, rect, nullptr);
3875 }
3876 if (esel->select_bitmap != nullptr) {
3877 changed |= edbm_backbuf_check_and_select_faces_obmode(mesh, esel, sel_op);
3878 }
3879 }
3880
3881 if (changed) {
3882 paintface_flush_flags(vc->C, vc->obact, true, false);
3883 }
3884 return changed;
3885}
3886
3887static void do_nurbs_box_select__doSelect(void *user_data,
3888 Nurb * /*nu*/,
3889 BPoint *bp,
3890 BezTriple *bezt,
3891 int beztindex,
3892 bool handles_visible,
3893 const float screen_co[2])
3894{
3895 BoxSelectUserData *data = static_cast<BoxSelectUserData *>(user_data);
3896
3897 const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co);
3898 if (bp) {
3899 const bool is_select = bp->f1 & SELECT;
3900 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
3901 if (sel_op_result != -1) {
3902 SET_FLAG_FROM_TEST(bp->f1, sel_op_result, data->select_flag);
3903 data->is_changed = true;
3904 }
3905 }
3906 else {
3907 if (!handles_visible) {
3908 /* can only be (beztindex == 1) here since handles are hidden */
3909 const bool is_select = bezt->f2 & SELECT;
3910 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
3911 if (sel_op_result != -1) {
3912 SET_FLAG_FROM_TEST(bezt->f2, sel_op_result, data->select_flag);
3913 data->is_changed = true;
3914 }
3915 bezt->f1 = bezt->f3 = bezt->f2;
3916 }
3917 else {
3918 uint8_t *flag_p = (&bezt->f1) + beztindex;
3919 const bool is_select = *flag_p & SELECT;
3920 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
3921 if (sel_op_result != -1) {
3922 SET_FLAG_FROM_TEST(*flag_p, sel_op_result, data->select_flag);
3923 data->is_changed = true;
3924 }
3925 }
3926 }
3927}
3928static bool do_nurbs_box_select(const ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
3929{
3930 const bool deselect_all = (sel_op == SEL_OP_SET);
3932
3933 view3d_userdata_boxselect_init(&data, vc, rect, sel_op);
3934
3935 Curve *curve = (Curve *)vc->obedit->data;
3936 ListBase *nurbs = BKE_curve_editNurbs_get(curve);
3937
3938 /* For deselect all, items to be selected are tagged with temp flag. Clear that first. */
3939 if (deselect_all) {
3941 data.select_flag = BEZT_FLAG_TEMP_TAG;
3942 }
3943
3944 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
3946
3947 /* Deselect items that were not added to selection (indicated by temp flag). */
3948 if (deselect_all) {
3950 }
3951
3953
3954 return data.is_changed;
3955}
3956
3957static void do_lattice_box_select__doSelect(void *user_data, BPoint *bp, const float screen_co[2])
3958{
3959 BoxSelectUserData *data = static_cast<BoxSelectUserData *>(user_data);
3960 const bool is_select = bp->f1 & SELECT;
3961 const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co);
3962 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
3963 if (sel_op_result != -1) {
3964 SET_FLAG_FROM_TEST(bp->f1, sel_op_result, SELECT);
3965 data->is_changed = true;
3966 }
3967}
3968static bool do_lattice_box_select(const ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
3969{
3971
3972 view3d_userdata_boxselect_init(&data, vc, rect, sel_op);
3973
3974 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
3975 data.is_changed |= ED_lattice_flags_set(vc->obedit, 0);
3976 }
3977
3978 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
3981
3982 return data.is_changed;
3983}
3984
3985static void do_mesh_box_select__doSelectVert(void *user_data,
3986 BMVert *eve,
3987 const float screen_co[2],
3988 int /*index*/)
3989{
3990 BoxSelectUserData *data = static_cast<BoxSelectUserData *>(user_data);
3991 const bool is_select = BM_elem_flag_test(eve, BM_ELEM_SELECT);
3992 const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co);
3993 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
3994 if (sel_op_result != -1) {
3995 BM_vert_select_set(data->vc->em->bm, eve, sel_op_result);
3996 if (data->uv_selctx) {
3997 data->uv_selctx->vert_select_set(eve, sel_op_result);
3998 }
3999
4000 data->is_changed = true;
4001 }
4002}
4008
4011static void do_mesh_box_select__doSelectEdge_pass0(void *user_data,
4012 BMEdge *eed,
4013 const float screen_co_a[2],
4014 const float screen_co_b[2],
4015 int index)
4016{
4017 BoxSelectUserData_ForMeshEdge *data_for_edge = static_cast<BoxSelectUserData_ForMeshEdge *>(
4018 user_data);
4019 BoxSelectUserData *data = data_for_edge->data;
4020 bool is_visible = true;
4021 if (data_for_edge->backbuf_offset) {
4022 uint bitmap_inedx = data_for_edge->backbuf_offset + index - 1;
4023 is_visible = BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, bitmap_inedx);
4024 }
4025
4026 const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT);
4027 const bool is_inside = (is_visible &&
4028 edge_fully_inside_rect(data->rect_fl, screen_co_a, screen_co_b));
4029 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
4030 if (sel_op_result != -1) {
4031 BM_edge_select_set(data->vc->em->bm, eed, sel_op_result);
4032 if (data->uv_selctx) {
4033 data->uv_selctx->edge_select_set(eed, sel_op_result);
4034 }
4035
4036 data->is_done = true;
4037 data->is_changed = true;
4038 }
4039}
4040
4043static void do_mesh_box_select__doSelectEdge_pass1(void *user_data,
4044 BMEdge *eed,
4045 const float screen_co_a[2],
4046 const float screen_co_b[2],
4047 int index)
4048{
4049 BoxSelectUserData_ForMeshEdge *data_for_edge = static_cast<BoxSelectUserData_ForMeshEdge *>(
4050 user_data);
4051 BoxSelectUserData *data = data_for_edge->data;
4052 bool is_visible = true;
4053 if (data_for_edge->backbuf_offset) {
4054 uint bitmap_inedx = data_for_edge->backbuf_offset + index - 1;
4055 is_visible = BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, bitmap_inedx);
4056 }
4057
4058 const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT);
4059 const bool is_inside = (is_visible && edge_inside_rect(data->rect_fl, screen_co_a, screen_co_b));
4060 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
4061 if (sel_op_result != -1) {
4062 BM_edge_select_set(data->vc->em->bm, eed, sel_op_result);
4063 if (data->uv_selctx) {
4064 data->uv_selctx->edge_select_set(eed, sel_op_result);
4065 }
4066
4067 data->is_changed = true;
4068 }
4069}
4070static void do_mesh_box_select__doSelectFace(void *user_data,
4071 BMFace *efa,
4072 const float screen_co[2],
4073 int /*index*/)
4074{
4075 BoxSelectUserData *data = static_cast<BoxSelectUserData *>(user_data);
4076 const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT);
4077 const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co);
4078 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
4079 if (sel_op_result != -1) {
4080 BM_face_select_set(data->vc->em->bm, efa, sel_op_result);
4081 if (data->uv_selctx) {
4082 data->uv_selctx->face_select_set(efa, sel_op_result);
4083 }
4084
4085 data->is_changed = true;
4086 }
4087}
4088static bool do_mesh_box_select(const ViewContext *vc,
4089 wmGenericUserData *wm_userdata,
4090 const rcti *rect,
4091 const eSelectOp sel_op)
4092{
4094 ToolSettings *ts = vc->scene->toolsettings;
4095
4096 view3d_userdata_boxselect_init(&data, vc, rect, sel_op);
4097
4098 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
4099 if (vc->em->bm->totvertsel) {
4101 data.is_changed = true;
4102 }
4103 }
4104
4105 std::unique_ptr<UVSyncSelectFromMesh> uv_selctx = UVSyncSelectFromMesh::create_if_needed(
4106 *ts, *vc->em->bm);
4107 data.uv_selctx = uv_selctx.get();
4108
4109 /* for non zbuf projections, don't change the GL state */
4111
4113
4114 const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d);
4115
4116 EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
4117 if (use_zbuf) {
4118 if (wm_userdata->data == nullptr) {
4120 esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
4122 vc->depsgraph, vc->region, vc->v3d, rect, nullptr);
4123 }
4124 }
4125
4126 if (ts->selectmode & SCE_SELECT_VERTEX) {
4127 if (use_zbuf) {
4129 esel, vc->depsgraph, vc->obedit, vc->em, data.uv_selctx, sel_op);
4130 }
4131 else {
4134 }
4135 }
4136 if (ts->selectmode & SCE_SELECT_EDGE) {
4137 /* Does both use_zbuf and non-use_zbuf versions (need screen cos for both) */
4139 cb_data.data = &data;
4140 cb_data.esel = use_zbuf ? esel : nullptr;
4142 vc->depsgraph, vc->obedit, SCE_SELECT_EDGE) :
4143 0;
4144
4145 const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_NEAR |
4146 (use_zbuf ? (eV3DProjTest)0 : V3D_PROJ_TEST_CLIP_BB);
4147 /* Fully inside. */
4149 vc, do_mesh_box_select__doSelectEdge_pass0, &cb_data, clip_flag);
4150 if (data.is_done == false) {
4151 /* Fall back to partially inside.
4152 * Clip content to account for edges partially behind the view. */
4155 &cb_data,
4157 }
4158 }
4159
4160 if (ts->selectmode & SCE_SELECT_FACE) {
4161 if (use_zbuf) {
4163 esel, vc->depsgraph, vc->obedit, vc->em, data.uv_selctx, sel_op);
4164 }
4165 else {
4168 }
4169 }
4170
4171 if (data.is_changed) {
4173 }
4174
4175 if (data.uv_selctx) {
4176 data.uv_selctx->apply();
4177 }
4178
4179 return data.is_changed;
4180}
4181
4182static bool do_meta_box_select(const ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
4183{
4184 Object *ob = vc->obedit;
4185 MetaBall *mb = (MetaBall *)ob->data;
4186 MetaElem *ml;
4187 int a;
4188 bool changed = false;
4189
4190 GPUSelectBuffer buffer;
4191 int hits;
4192
4194
4195 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
4196 changed |= BKE_mball_deselect_all(mb);
4197 }
4198
4199 int metaelem_id = 0;
4200 for (ml = static_cast<MetaElem *>(mb->editelems->first); ml;
4201 ml = ml->next, metaelem_id += 0x10000)
4202 {
4203 bool is_inside_radius = false;
4204 bool is_inside_stiff = false;
4205
4206 for (a = 0; a < hits; a++) {
4207 const int select_id = buffer.storage[a].id;
4208
4209 if (select_id == -1) {
4210 continue;
4211 }
4212
4213 const uint hit_object = select_id & 0xFFFF;
4214 if (vc->obedit->runtime->select_id != hit_object) {
4215 continue;
4216 }
4217
4218 if (metaelem_id != (select_id & 0xFFFF0000 & ~MBALLSEL_ANY)) {
4219 continue;
4220 }
4221
4222 if (select_id & MBALLSEL_RADIUS) {
4223 is_inside_radius = true;
4224 break;
4225 }
4226
4227 if (select_id & MBALLSEL_STIFF) {
4228 is_inside_stiff = true;
4229 break;
4230 }
4231 }
4232 const int flag_prev = ml->flag;
4233 if (is_inside_radius) {
4234 ml->flag |= MB_SCALE_RAD;
4235 }
4236 if (is_inside_stiff) {
4237 ml->flag &= ~MB_SCALE_RAD;
4238 }
4239
4240 const bool is_select = (ml->flag & SELECT);
4241 const bool is_inside = is_inside_radius || is_inside_stiff;
4242
4243 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
4244 if (sel_op_result != -1) {
4245 SET_FLAG_FROM_TEST(ml->flag, sel_op_result, SELECT);
4246 }
4247 changed |= (flag_prev != ml->flag);
4248 }
4249
4250 return changed;
4251}
4252
4253static bool do_armature_box_select(const ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
4254{
4255 bool changed = false;
4256 int a;
4257
4258 GPUSelectBuffer buffer;
4259 int hits;
4260
4262
4264 vc->scene, vc->view_layer, vc->v3d);
4265
4266 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
4268 }
4269
4270 for (Base *base : bases) {
4271 Object *obedit = base->object;
4272 obedit->id.tag &= ~ID_TAG_DOIT;
4273
4274 bArmature *arm = static_cast<bArmature *>(obedit->data);
4276 }
4277
4278 /* first we only check points inside the border */
4279 for (a = 0; a < hits; a++) {
4280 const int select_id = buffer.storage[a].id;
4281 if (select_id != -1) {
4282 if ((select_id & 0xFFFF0000) == 0) {
4283 continue;
4284 }
4285
4286 EditBone *ebone;
4287 Base *base_edit = ED_armature_base_and_ebone_from_select_buffer(bases, select_id, &ebone);
4288 ebone->temp.i |= select_id & BONESEL_ANY;
4289 base_edit->object->id.tag |= ID_TAG_DOIT;
4290 }
4291 }
4292
4293 for (Base *base : bases) {
4294 Object *obedit = base->object;
4295 if (obedit->id.tag & ID_TAG_DOIT) {
4296 obedit->id.tag &= ~ID_TAG_DOIT;
4297 changed |= ED_armature_edit_select_op_from_tagged(static_cast<bArmature *>(obedit->data),
4298 sel_op);
4299 }
4300 }
4301
4302 return changed;
4303}
4304
4309static int gpu_bone_select_buffer_cmp(const void *sel_a_p, const void *sel_b_p)
4310{
4311 /* NOTE: this is endianness-sensitive.
4312 * GPUSelectResult values are always expected to be little-endian. */
4313 uint sel_a = ((GPUSelectResult *)sel_a_p)->id;
4314 uint sel_b = ((GPUSelectResult *)sel_b_p)->id;
4315
4316 if (sel_a < sel_b) {
4317 return -1;
4318 }
4319 if (sel_a > sel_b) {
4320 return 1;
4321 }
4322 return 0;
4323}
4324
4330
4332 const ViewContext *vc,
4333 const rcti *rect,
4334 const eSelectOp sel_op)
4335{
4336 View3D *v3d = vc->v3d;
4337
4338 GPUSelectBuffer buffer;
4340 vc->obact);
4341 const int hits = view3d_gpu_select(vc, &buffer, rect, VIEW3D_SELECT_ALL, select_filter);
4344 base->object->id.tag &= ~ID_TAG_DOIT;
4345 }
4346
4347 bool changed = false;
4348 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
4349 changed |= object_deselect_all_visible(vc->scene, vc->view_layer, vc->v3d);
4350 }
4351
4353 if ((hits == -1) && !SEL_OP_USE_OUTSIDE(sel_op)) {
4354 if (changed) {
4356 return true;
4357 }
4358 }
4359
4360 blender::Map<uint32_t, Base *> base_by_object_select_id;
4361 LISTBASE_FOREACH (Base *, base, object_bases) {
4362 if (BASE_SELECTABLE(v3d, base)) {
4363 const uint32_t select_id = base->object->runtime->select_id;
4364 if ((select_id & 0x0000FFFF) != 0) {
4365 const uint hit_object = select_id & 0xFFFF;
4366 base_by_object_select_id.add(hit_object, base);
4367 }
4368 }
4369 }
4370
4371 /* The draw order doesn't always match the order we populate the engine, see: #51695. */
4372 qsort(buffer.storage.data(), hits, sizeof(GPUSelectResult), gpu_bone_select_buffer_cmp);
4373
4374 blender::Set<Base *> bases_inside;
4375 for (const GPUSelectResult *buf_iter = buffer.storage.data(), *buf_end = buf_iter + hits;
4376 buf_iter < buf_end;
4377 buf_iter++)
4378 {
4379 const uint32_t select_id = buf_iter->id;
4380 const uint32_t hit_object = select_id & 0xFFFF;
4381 if (Base *base = base_by_object_select_id.lookup_default(hit_object, nullptr)) {
4382 bases_inside.add(base);
4383 }
4384 }
4385
4386 for (Base *base = static_cast<Base *>(object_bases->first); base && hits; base = base->next) {
4387 if (BASE_SELECTABLE(v3d, base)) {
4388 const bool is_select = base->flag & BASE_SELECTED;
4389 const bool is_inside = bases_inside.contains(base);
4390 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
4391 if (sel_op_result != -1) {
4393 sel_op_result ? blender::ed::object::BA_SELECT :
4395 changed = true;
4396 }
4397 }
4398 }
4399
4400 if (changed) {
4402 }
4403 return changed;
4404}
4405
4407 const ViewContext *vc,
4408 const rcti *rect,
4409 const eSelectOp sel_op)
4410{
4412
4413 /* Selection buffer has bones potentially too. */
4414 GPUSelectBuffer buffer;
4416 vc->obact);
4417 const int hits = view3d_gpu_select(vc, &buffer, rect, VIEW3D_SELECT_ALL, select_filter);
4418 /*
4419 * NOTE(@theeth): Regarding the logic use here.
4420 * The buffer and #ListBase have the same relative order, which makes the selection
4421 * very simple. Loop through both data sets at the same time, if the color
4422 * is the same as the object, we have a hit and can move to the next color
4423 * and object pair, if not, just move to the next object,
4424 * keeping the same color until we have a hit. */
4425
4426 if (hits > 0) {
4427 /* no need to loop if there's no hit */
4428
4429 /* The draw order doesn't always match the order we populate the engine, see: #51695. */
4430 qsort(buffer.storage.data(), hits, sizeof(GPUSelectResult), gpu_bone_select_buffer_cmp);
4431
4432 for (const GPUSelectResult *buf_iter = buffer.storage.data(), *buf_end = buf_iter + hits;
4433 buf_iter < buf_end;
4434 buf_iter++)
4435 {
4436 bPoseChannel *pose_bone;
4437 Base *base = ED_armature_base_and_pchan_from_select_buffer(bases, buf_iter->id, &pose_bone);
4438
4439 if (base == nullptr) {
4440 continue;
4441 }
4442
4443 /* Loop over contiguous bone hits for 'base'. */
4444 for (; buf_iter != buf_end; buf_iter++) {
4445 /* should never fail */
4446 if (pose_bone != nullptr) {
4447 base->object->id.tag |= ID_TAG_DOIT;
4449 }
4450
4451 /* Select the next bone if we're not switching bases. */
4452 if (buf_iter + 1 != buf_end) {
4453 const GPUSelectResult *col_next = buf_iter + 1;
4454 if ((base->object->runtime->select_id & 0x0000FFFF) != (col_next->id & 0x0000FFFF)) {
4455 break;
4456 }
4457 if (base->object->pose != nullptr) {
4458 const uint hit_bone = (col_next->id & ~BONESEL_ANY) >> 16;
4459 bPoseChannel *next = static_cast<bPoseChannel *>(
4460 BLI_findlink(&base->object->pose->chanbase, hit_bone));
4461 pose_bone = next;
4462 }
4463 else {
4464 pose_bone = nullptr;
4465 }
4466 }
4467 }
4468 }
4469 }
4470
4471 const bool changed_multi = do_pose_tag_select_op_exec(bases, sel_op);
4472 if (changed_multi) {
4475 }
4476
4477 return changed_multi;
4478}
4479
4481 const rcti *rect,
4482 const eSelectOp sel_op)
4483{
4484 using namespace blender;
4485 Object *object = (vc->obedit ? vc->obedit : vc->obact);
4486 const Object *ob_eval = DEG_get_evaluated(vc->depsgraph, object);
4487 const GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
4489 vc->scene->toolsettings, object);
4490
4492 vc,
4493 sel_op,
4495 const IndexMask &mask,
4496 const StringRef attribute_name,
4497 IndexMaskMemory &memory) {
4499 const bke::greasepencil::Layer &layer = grease_pencil.layer(info.layer_index);
4500 const bke::crazyspace::GeometryDeformation deformation =
4502 ob_eval, *object, info.drawing);
4503 const IndexMask visible_handle_elements =
4505 *object,
4506 info.drawing,
4507 info.layer_index,
4508 selection_domain,
4510 memory);
4511 const float4x4 layer_to_world = layer.to_world_space(*ob_eval);
4513 layer_to_world);
4514
4515 return ed::curves::select_box_mask(*vc,
4516 curves,
4517 deformation,
4518 projection,
4519 mask,
4520 visible_handle_elements,
4521 selection_domain,
4522 attribute_name,
4523 *rect,
4524 memory);
4525 });
4526}
4527
4529{
4530 using namespace blender;
4532 rcti rect;
4533 bool changed_multi = false;
4534
4535 wmGenericUserData wm_userdata_buf = {nullptr, nullptr, false};
4536 wmGenericUserData *wm_userdata = &wm_userdata_buf;
4537
4540
4541 /* setup view context for argument to callbacks */
4543
4544 eSelectOp sel_op = static_cast<eSelectOp>(RNA_enum_get(op->ptr, "mode"));
4546
4547 if (vc.obedit) {
4549 vc.scene, vc.view_layer, vc.v3d, vc.obedit->type, vc.obedit->mode, ob_iter)
4550 {
4552 bool changed = false;
4553
4554 switch (vc.obedit->type) {
4555 case OB_MESH:
4557 changed = do_mesh_box_select(&vc, wm_userdata, &rect, sel_op);
4558 if (changed) {
4559 DEG_id_tag_update(static_cast<ID *>(vc.obedit->data), ID_RECALC_SELECT);
4561 }
4562 break;
4563 case OB_CURVES_LEGACY:
4564 case OB_SURF:
4565 changed = do_nurbs_box_select(&vc, &rect, sel_op);
4566 if (changed) {
4567 DEG_id_tag_update(static_cast<ID *>(vc.obedit->data), ID_RECALC_SELECT);
4569 }
4570 break;
4571 case OB_MBALL:
4572 changed = do_meta_box_select(&vc, &rect, sel_op);
4573 if (changed) {
4574 DEG_id_tag_update(static_cast<ID *>(vc.obedit->data), ID_RECALC_SELECT);
4576 }
4577 break;
4578 case OB_ARMATURE:
4579 changed = do_armature_box_select(&vc, &rect, sel_op);
4580 if (changed) {
4584 }
4585 break;
4586 case OB_LATTICE:
4587 changed = do_lattice_box_select(&vc, &rect, sel_op);
4588 if (changed) {
4589 DEG_id_tag_update(static_cast<ID *>(vc.obedit->data), ID_RECALC_SELECT);
4591 }
4592 break;
4593 case OB_CURVES: {
4594 Curves &curves_id = *static_cast<Curves *>(vc.obedit->data);
4595 bke::CurvesGeometry &curves = curves_id.geometry.wrap();
4598 const bke::AttrDomain selection_domain = bke::AttrDomain(curves_id.selection_domain);
4599 const float4x4 projection = ED_view3d_ob_project_mat_get(vc.rv3d, vc.obedit);
4600 const IndexRange elements(curves.attributes().domain_size(selection_domain));
4601 changed = ed::curves::select_box(vc,
4602 curves,
4603 deformation,
4604 projection,
4605 elements,
4606 elements,
4607 selection_domain,
4608 rect,
4609 sel_op);
4610 if (changed) {
4611 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
4612 * generic attribute for now. */
4613 DEG_id_tag_update(static_cast<ID *>(vc.obedit->data), ID_RECALC_GEOMETRY);
4615 }
4616 break;
4617 }
4618 case OB_POINTCLOUD: {
4619 PointCloud &pointcloud = *static_cast<PointCloud *>(vc.obedit->data);
4620 const float4x4 projection = ED_view3d_ob_project_mat_get(vc.rv3d, vc.obedit);
4621 changed = ed::pointcloud::select_box(pointcloud, *vc.region, projection, rect, sel_op);
4622 if (changed) {
4623 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
4624 * generic attribute for now. */
4625 DEG_id_tag_update(static_cast<ID *>(vc.obedit->data), ID_RECALC_GEOMETRY);
4627 }
4628 break;
4629 }
4630 case OB_GREASE_PENCIL: {
4631 changed = do_grease_pencil_box_select(&vc, &rect, sel_op);
4632 break;
4633 }
4634 default:
4635 BLI_assert_msg(0, "box select on incorrect object type");
4636 break;
4637 }
4638 changed_multi |= changed;
4639 }
4641 }
4642 else { /* No edit-mode, unified for bones and objects. */
4643 if (vc.obact && BKE_paint_select_face_test(vc.obact)) {
4644 changed_multi = do_paintface_box_select(&vc, wm_userdata, &rect, sel_op);
4645 }
4646 else if (vc.obact && BKE_paint_select_vert_test(vc.obact)) {
4647 changed_multi = do_paintvert_box_select(&vc, wm_userdata, &rect, sel_op);
4648 }
4649 else if (vc.obact && BKE_paint_select_grease_pencil_test(vc.obact)) {
4650 changed_multi = do_grease_pencil_box_select(&vc, &rect, sel_op);
4651 }
4652 else if (vc.obact && vc.obact->mode & OB_MODE_PARTICLE_EDIT) {
4653 changed_multi = PE_box_select(C, &rect, sel_op);
4654 }
4655 else if (vc.obact && ((vc.obact->mode & OB_MODE_POSE) ||
4656 ((vc.obact->mode & OB_MODE_WEIGHT_PAINT) &&
4658 {
4659 changed_multi = do_pose_box_select(C, &vc, &rect, sel_op);
4660 if (changed_multi) {
4662 }
4663 }
4664 else { /* object mode with none active */
4665 changed_multi = do_object_box_select(C, &vc, &rect, sel_op);
4666 if (changed_multi) {
4668 }
4669 }
4670 }
4671
4672 WM_generic_user_data_free(wm_userdata);
4673
4674 if (changed_multi) {
4675 return OPERATOR_FINISHED;
4676 }
4677 return OPERATOR_CANCELLED;
4678}
4679
4681{
4682 /* identifiers */
4683 ot->name = "Box Select";
4684 ot->description = "Select items using box selection";
4685 ot->idname = "VIEW3D_OT_select_box";
4686
4687 /* API callbacks. */
4688 ot->invoke = WM_gesture_box_invoke;
4689 ot->exec = view3d_box_select_exec;
4690 ot->modal = WM_gesture_box_modal;
4691 ot->poll = view3d_selectable_data;
4692 ot->cancel = WM_gesture_box_cancel;
4693
4694 /* flags */
4695 ot->flag = OPTYPE_UNDO;
4696
4697 /* rna */
4700}
4701
4703
4704/* -------------------------------------------------------------------- */
4707
4723
4725 const ViewContext *vc,
4726 const bool select,
4727 const int mval[2],
4728 const float rad)
4729{
4730 r_data->vc = vc;
4731 r_data->select = select;
4732 copy_v2_v2_int(r_data->mval, mval);
4733 r_data->mval_fl[0] = mval[0];
4734 r_data->mval_fl[1] = mval[1];
4735
4736 r_data->radius = rad;
4737 r_data->radius_squared = rad * rad;
4738
4739 /* SELECT by default, but can be changed if needed (only few cases use and respect this). */
4741
4742 /* runtime */
4743 r_data->is_changed = false;
4744}
4745
4746static void mesh_circle_doSelectVert(void *user_data,
4747 BMVert *eve,
4748 const float screen_co[2],
4749 int /*index*/)
4750{
4751 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
4752
4753 if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
4754 BM_vert_select_set(data->vc->em->bm, eve, data->select);
4755 if (data->uv_selctx) {
4756 data->uv_selctx->vert_select_set(eve, data->select);
4757 }
4758
4759 data->is_changed = true;
4760 }
4761}
4762static void mesh_circle_doSelectEdge(void *user_data,
4763 BMEdge *eed,
4764 const float screen_co_a[2],
4765 const float screen_co_b[2],
4766 int /*index*/)
4767{
4768 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
4769
4770 if (edge_inside_circle(data->mval_fl, data->radius, screen_co_a, screen_co_b)) {
4771 BM_edge_select_set(data->vc->em->bm, eed, data->select);
4772 if (data->uv_selctx) {
4773 data->uv_selctx->edge_select_set(eed, data->select);
4774 }
4775
4776 data->is_changed = true;
4777 }
4778}
4779static void mesh_circle_doSelectFace(void *user_data,
4780 BMFace *efa,
4781 const float screen_co[2],
4782 int /*index*/)
4783{
4784 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
4785
4786 if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
4787 BM_face_select_set(data->vc->em->bm, efa, data->select);
4788 if (data->uv_selctx) {
4789 data->uv_selctx->face_select_set(efa, data->select);
4790 }
4791
4792 data->is_changed = true;
4793 }
4794}
4795
4796static bool mesh_circle_select(const ViewContext *vc,
4797 wmGenericUserData *wm_userdata,
4798 eSelectOp sel_op,
4799 const int mval[2],
4800 float rad)
4801{
4802 ToolSettings *ts = vc->scene->toolsettings;
4805
4806 bool changed = false;
4807 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
4808 if (vc->em->bm->totvertsel) {
4810 vc->em->bm->totvertsel = 0;
4811 vc->em->bm->totedgesel = 0;
4812 vc->em->bm->totfacesel = 0;
4813 changed = true;
4814 }
4815 }
4816
4817 std::unique_ptr<UVSyncSelectFromMesh> uv_selctx = UVSyncSelectFromMesh::create_if_needed(
4818 *ts, *vc->em->bm);
4819 data.uv_selctx = uv_selctx.get();
4820
4821 const bool select = (sel_op != SEL_OP_SUB);
4822
4823 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
4824
4826
4827 const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d);
4828
4829 if (use_zbuf) {
4830 if (wm_userdata->data == nullptr) {
4832 }
4833 }
4834 EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
4835
4836 if (use_zbuf) {
4837 if (esel->select_bitmap == nullptr) {
4839 vc->depsgraph, vc->region, vc->v3d, mval, int(rad + 1.0f), nullptr);
4840 }
4841 }
4842
4843 if (ts->selectmode & SCE_SELECT_VERTEX) {
4844 if (use_zbuf) {
4845 if (esel->select_bitmap != nullptr) {
4847 vc->depsgraph,
4848 vc->obedit,
4849 vc->em,
4850 data.uv_selctx,
4852 }
4853 }
4854 else {
4856 }
4857 }
4858
4859 if (ts->selectmode & SCE_SELECT_EDGE) {
4860 if (use_zbuf) {
4861 if (esel->select_bitmap != nullptr) {
4863 vc->depsgraph,
4864 vc->obedit,
4865 vc->em,
4866 data.uv_selctx,
4868 }
4869 }
4870 else {
4872 vc,
4874 &data,
4876 }
4877 }
4878
4879 if (ts->selectmode & SCE_SELECT_FACE) {
4880 if (use_zbuf) {
4881 if (esel->select_bitmap != nullptr) {
4883 vc->depsgraph,
4884 vc->obedit,
4885 vc->em,
4886 data.uv_selctx,
4888 }
4889 }
4890 else {
4892 }
4893 }
4894
4895 changed |= data.is_changed;
4896
4897 if (changed) {
4899 }
4900
4901 if (data.uv_selctx) {
4902 data.uv_selctx->apply();
4903 }
4904
4905 return changed;
4906}
4907
4909 wmGenericUserData *wm_userdata,
4910 const eSelectOp sel_op,
4911 const int mval[2],
4912 float rad)
4913{
4915 Object *ob = vc->obact;
4916 Mesh *mesh = static_cast<Mesh *>(ob->data);
4917
4918 bool changed = false;
4919 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
4920 /* flush selection at the end */
4921 changed |= paintface_deselect_all_visible(vc->C, ob, SEL_DESELECT, false);
4922 }
4923
4924 if (wm_userdata->data == nullptr) {
4926 }
4927
4928 {
4929 EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
4931 vc->depsgraph, vc->region, vc->v3d, mval, int(rad + 1.0f), nullptr);
4932 if (esel->select_bitmap != nullptr) {
4933 changed |= edbm_backbuf_check_and_select_faces_obmode(mesh, esel, sel_op);
4934 MEM_freeN(esel->select_bitmap);
4935 esel->select_bitmap = nullptr;
4936 }
4937 }
4938
4939 if (changed) {
4940 paintface_flush_flags(vc->C, ob, true, false);
4941 }
4942 return changed;
4943}
4944
4950 const float screen_co[2],
4951 int index)
4952{
4954 static_cast<CircleSelectUserData_ForMeshObjectVert *>(user_data);
4955 CircleSelectUserData *data = &mesh_data->circle_data;
4956
4957 if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
4958 mesh_data->select_vert[index] = data->select;
4959 data->is_changed = true;
4960 }
4961}
4963 wmGenericUserData *wm_userdata,
4964 const eSelectOp sel_op,
4965 const int mval[2],
4966 float rad)
4967{
4968 using namespace blender;
4970 const bool use_zbuf = !XRAY_ENABLED(vc->v3d);
4971 Object *ob = vc->obact;
4972 Mesh *mesh = static_cast<Mesh *>(ob->data);
4973 // CircleSelectUserData data = {nullptr}; /* UNUSED. */
4974
4975 bool changed = false;
4976 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
4977 /* Flush selection at the end. */
4978 changed |= paintvert_deselect_all_visible(ob, SEL_DESELECT, false);
4979 }
4980
4981 const bool select = (sel_op != SEL_OP_SUB);
4982
4983 if (use_zbuf) {
4984 if (wm_userdata->data == nullptr) {
4986 }
4987 }
4988
4989 if (use_zbuf) {
4990 EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
4992 vc->depsgraph, vc->region, vc->v3d, mval, int(rad + 1.0f), nullptr);
4993 if (esel->select_bitmap != nullptr) {
4994 changed |= edbm_backbuf_check_and_select_verts_obmode(mesh, esel, sel_op);
4995 MEM_freeN(esel->select_bitmap);
4996 esel->select_bitmap = nullptr;
4997 }
4998 }
4999 else {
5000 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
5001 bke::SpanAttributeWriter<bool> select_vert = attributes.lookup_or_add_for_write_span<bool>(
5002 ".select_vert", bke::AttrDomain::Point);
5003
5005 data.select_vert = select_vert.span;
5006
5007 ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d); /* for foreach's screen/vert projection */
5008
5009 view3d_userdata_circleselect_init(&data.circle_data, vc, select, mval, rad);
5012 changed |= data.circle_data.is_changed;
5013 select_vert.finish();
5014 }
5015
5016 if (changed) {
5017 if (sel_op == SEL_OP_SUB) {
5019 }
5022 }
5023 return changed;
5024}
5025
5026static void nurbscurve_circle_doSelect(void *user_data,
5027 Nurb * /*nu*/,
5028 BPoint *bp,
5029 BezTriple *bezt,
5030 int beztindex,
5031 bool /*handles_visible*/,
5032 const float screen_co[2])
5033{
5034 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
5035
5036 if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
5037 if (bp) {
5038 SET_FLAG_FROM_TEST(bp->f1, data->select, data->select_flag);
5039 }
5040 else {
5041 if (beztindex == 0) {
5042 SET_FLAG_FROM_TEST(bezt->f1, data->select, data->select_flag);
5043 }
5044 else if (beztindex == 1) {
5045 SET_FLAG_FROM_TEST(bezt->f2, data->select, data->select_flag);
5046 }
5047 else {
5048 SET_FLAG_FROM_TEST(bezt->f3, data->select, data->select_flag);
5049 }
5050 }
5051 data->is_changed = true;
5052 }
5053}
5055 const eSelectOp sel_op,
5056 const int mval[2],
5057 float rad)
5058{
5059 const bool select = (sel_op != SEL_OP_SUB);
5060 const bool deselect_all = (sel_op == SEL_OP_SET);
5062
5064
5065 Curve *curve = (Curve *)vc->obedit->data;
5066 ListBase *nurbs = BKE_curve_editNurbs_get(curve);
5067
5068 /* For deselect all, items to be selected are tagged with temp flag. Clear that first. */
5069 if (deselect_all) {
5071 data.select_flag = BEZT_FLAG_TEMP_TAG;
5072 }
5073
5074 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
5076
5077 /* Deselect items that were not added to selection (indicated by temp flag). */
5078 if (deselect_all) {
5080 }
5081
5083
5084 return data.is_changed;
5085}
5086
5087static void latticecurve_circle_doSelect(void *user_data, BPoint *bp, const float screen_co[2])
5088{
5089 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
5090
5091 if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
5092 bp->f1 = data->select ? (bp->f1 | SELECT) : (bp->f1 & ~SELECT);
5093 data->is_changed = true;
5094 }
5095}
5097 const eSelectOp sel_op,
5098 const int mval[2],
5099 float rad)
5100{
5102 const bool select = (sel_op != SEL_OP_SUB);
5103
5105
5106 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
5107 data.is_changed |= ED_lattice_flags_set(vc->obedit, 0);
5108 }
5109 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
5110
5112
5113 return data.is_changed;
5114}
5115
5119static bool pchan_circle_doSelectJoint(void *user_data,
5120 bPoseChannel *pchan,
5121 const float screen_co[2])
5122{
5123 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
5124
5125 if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
5126 if (data->select) {
5127 pchan->flag |= POSE_SELECTED;
5128 }
5129 else {
5130 pchan->flag &= ~POSE_SELECTED;
5131 }
5132 return true;
5133 }
5134 return false;
5135}
5136static void do_circle_select_pose__doSelectBone(void *user_data,
5137 bPoseChannel *pchan,
5138 const float screen_co_a[2],
5139 const float screen_co_b[2])
5140{
5141 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
5142 bArmature *arm = static_cast<bArmature *>(data->vc->obact->data);
5143 if (!blender::animrig::bone_is_selectable(arm, pchan)) {
5144 return;
5145 }
5146
5147 bool is_point_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 (pchan_circle_doSelectJoint(data, pchan, screen_co_a)) {
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 (pchan_circle_doSelectJoint(data, pchan, screen_co_b)) {
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 {
5176 if (data->select) {
5177 pchan->flag |= POSE_SELECTED;
5178 }
5179 else {
5180 pchan->flag &= ~POSE_SELECTED;
5181 }
5182 data->is_changed = true;
5183 }
5184
5185 data->is_changed |= is_point_done;
5186}
5187static bool pose_circle_select(const ViewContext *vc,
5188 const eSelectOp sel_op,
5189 const int mval[2],
5190 float rad)
5191{
5194 const bool select = (sel_op != SEL_OP_SUB);
5195
5197
5198 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
5199 data.is_changed |= ED_pose_deselect_all(vc->obact, SEL_DESELECT, false);
5200 }
5201
5202 ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d); /* for foreach's screen/vert projection */
5203
5204 /* Treat bones as clipped segments (no joints). */
5207 &data,
5209
5210 if (data.is_changed) {
5212 }
5213 return data.is_changed;
5214}
5215
5219static bool armature_circle_doSelectJoint(void *user_data,
5220 EditBone *ebone,
5221 const float screen_co[2],
5222 bool head)
5223{
5224 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
5225
5226 if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
5227 if (head) {
5228 if (data->select) {
5229 ebone->flag |= BONE_ROOTSEL;
5230 }
5231 else {
5232 ebone->flag &= ~BONE_ROOTSEL;
5233 }
5234 }
5235 else {
5236 if (data->select) {
5237 ebone->flag |= BONE_TIPSEL;
5238 }
5239 else {
5240 ebone->flag &= ~BONE_TIPSEL;
5241 }
5242 }
5243 return true;
5244 }
5245 return false;
5246}
5248 EditBone *ebone,
5249 const float screen_co_a[2],
5250 const float screen_co_b[2])
5251{
5252 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
5253 const bArmature *arm = static_cast<const bArmature *>(data->vc->obedit->data);
5254 if (!(data->select ? EBONE_SELECTABLE(arm, ebone) :
5256 {
5257 return;
5258 }
5259
5260 /* When true, ignore in the next pass. */
5261 ebone->temp.i = false;
5262
5263 bool is_point_done = false;
5264 bool is_edge_done = false;
5265 int points_proj_tot = 0;
5266
5267 /* Project head location to screen-space. */
5268 if (screen_co_a[0] != IS_CLIPPED) {
5269 points_proj_tot++;
5270 if (armature_circle_doSelectJoint(data, ebone, screen_co_a, true)) {
5271 is_point_done = true;
5272 }
5273 }
5274
5275 /* Project tail location to screen-space. */
5276 if (screen_co_b[0] != IS_CLIPPED) {
5277 points_proj_tot++;
5278 if (armature_circle_doSelectJoint(data, ebone, screen_co_b, false)) {
5279 is_point_done = true;
5280 }
5281 }
5282
5283 /* check if the head and/or tail is in the circle
5284 * - the call to check also does the selection already
5285 */
5286
5287 /* only if the endpoints didn't get selected, deal with the middle of the bone too
5288 * It works nicer to only do this if the head or tail are not in the circle,
5289 * otherwise there is no way to circle select joints alone */
5290 if ((is_point_done == false) && (points_proj_tot == 2) &&
5291 edge_inside_circle(data->mval_fl, data->radius, screen_co_a, screen_co_b))
5292 {
5294 is_edge_done = true;
5295 data->is_changed = true;
5296 }
5297
5298 if (is_point_done || is_edge_done) {
5299 ebone->temp.i = true;
5300 }
5301
5302 data->is_changed |= is_point_done;
5303}
5305 EditBone *ebone,
5306 const float screen_co_a[2],
5307 const float screen_co_b[2])
5308{
5309 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
5310 bArmature *arm = static_cast<bArmature *>(data->vc->obedit->data);
5311
5312 if (!(data->select ? EBONE_SELECTABLE(arm, ebone) :
5314 {
5315 return;
5316 }
5317
5318 /* Set in the first pass, needed so circle select prioritizes joints. */
5319 if (ebone->temp.i != 0) {
5320 return;
5321 }
5322
5323 if (edge_inside_circle(data->mval_fl, data->radius, screen_co_a, screen_co_b)) {
5325 data->is_changed = true;
5326 }
5327}
5329 const eSelectOp sel_op,
5330 const int mval[2],
5331 float rad)
5332{
5334 bArmature *arm = static_cast<bArmature *>(vc->obedit->data);
5335
5336 const bool select = (sel_op != SEL_OP_SUB);
5337
5339
5340 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
5342 }
5343
5345
5346 /* Operate on fully visible (non-clipped) points. */
5349
5350 /* Operate on bones as segments clipped to the viewport bounds
5351 * (needed to handle bones with both points outside the view).
5352 * A separate pass is needed since clipped coordinates can't be used for selecting joints. */
5355 &data,
5357
5358 if (data.is_changed) {
5361 }
5362 return data.is_changed;
5363}
5364
5365static void do_circle_select_mball__doSelectElem(void *user_data,
5366 MetaElem *ml,
5367 const float screen_co[2])
5368{
5369 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
5370
5371 if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
5372 if (data->select) {
5373 ml->flag |= SELECT;
5374 }
5375 else {
5376 ml->flag &= ~SELECT;
5377 }
5378 data->is_changed = true;
5379 }
5380}
5381static bool mball_circle_select(const ViewContext *vc,
5382 const eSelectOp sel_op,
5383 const int mval[2],
5384 float rad)
5385{
5387
5388 const bool select = (sel_op != SEL_OP_SUB);
5389
5391
5392 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
5393 data.is_changed |= BKE_mball_deselect_all(static_cast<MetaBall *>(vc->obedit->data));
5394 }
5395
5397
5400 return data.is_changed;
5401}
5402
5404 const eSelectOp sel_op,
5405 const int mval[2],
5406 const float rad)
5407{
5408 using namespace blender;
5409 Object *object = (vc->obedit ? vc->obedit : vc->obact);
5410 const Object *ob_eval = DEG_get_evaluated(vc->depsgraph, object);
5411 const GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
5413 vc->scene->toolsettings, object);
5414
5416 vc,
5417 sel_op,
5419 const IndexMask &mask,
5420 const StringRef attribute_name,
5421 IndexMaskMemory &memory) {
5423 const bke::greasepencil::Layer &layer = grease_pencil.layer(info.layer_index);
5424 const bke::crazyspace::GeometryDeformation deformation =
5426 ob_eval, *object, info.drawing);
5427 const IndexMask visible_handle_elements =
5429 *object,
5430 info.drawing,
5431 info.layer_index,
5432 selection_domain,
5434 memory);
5435 const float4x4 layer_to_world = layer.to_world_space(*ob_eval);
5437 layer_to_world);
5438
5440 curves,
5441 deformation,
5442 projection,
5443 mask,
5444 visible_handle_elements,
5445 selection_domain,
5446 attribute_name,
5447 int2(mval),
5448 rad,
5449 memory);
5450 });
5451}
5452
5457 const ViewContext *vc,
5458 wmGenericUserData *wm_userdata,
5459 const eSelectOp sel_op,
5460 const int mval[2],
5461 float rad)
5462{
5463 using namespace blender;
5464 bool changed = false;
5466 switch (vc->obedit->type) {
5467 case OB_MESH:
5468 changed = mesh_circle_select(vc, wm_userdata, sel_op, mval, rad);
5469 break;
5470 case OB_CURVES_LEGACY:
5471 case OB_SURF:
5472 changed = nurbscurve_circle_select(vc, sel_op, mval, rad);
5473 break;
5474 case OB_LATTICE:
5475 changed = lattice_circle_select(vc, sel_op, mval, rad);
5476 break;
5477 case OB_ARMATURE:
5478 changed = armature_circle_select(vc, sel_op, mval, rad);
5479 if (changed) {
5481 }
5482 break;
5483 case OB_MBALL:
5484 changed = mball_circle_select(vc, sel_op, mval, rad);
5485 break;
5486 case OB_CURVES: {
5487 Curves &curves_id = *static_cast<Curves *>(vc->obedit->data);
5488 bke::CurvesGeometry &curves = curves_id.geometry.wrap();
5491 const bke::AttrDomain selection_domain = bke::AttrDomain(curves_id.selection_domain);
5492 const float4x4 projection = ED_view3d_ob_project_mat_get(vc->rv3d, vc->obedit);
5493 const IndexRange elements(curves.attributes().domain_size(selection_domain));
5494 changed = ed::curves::select_circle(*vc,
5495 curves,
5496 deformation,
5497 projection,
5498 elements,
5499 elements,
5500 selection_domain,
5501 mval,
5502 rad,
5503 sel_op);
5504 if (changed) {
5505 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
5506 * generic attribute for now. */
5507 DEG_id_tag_update(static_cast<ID *>(vc->obedit->data), ID_RECALC_GEOMETRY);
5509 }
5510 break;
5511 }
5512 case OB_POINTCLOUD: {
5513 PointCloud &pointcloud = *static_cast<PointCloud *>(vc->obedit->data);
5514 const float4x4 projection = ED_view3d_ob_project_mat_get(vc->rv3d, vc->obedit);
5516 pointcloud, *vc->region, projection, mval, rad, sel_op);
5517 if (changed) {
5518 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
5519 * generic attribute for now. */
5520 DEG_id_tag_update(static_cast<ID *>(vc->obedit->data), ID_RECALC_GEOMETRY);
5522 }
5523 break;
5524 }
5525 case OB_GREASE_PENCIL:
5526 changed = grease_pencil_circle_select(vc, sel_op, mval, rad);
5527 break;
5528
5529 default:
5530 BLI_assert(0);
5531 break;
5532 }
5533
5534 if (changed) {
5535 DEG_id_tag_update(static_cast<ID *>(vc->obact->data), ID_RECALC_SELECT);
5537 }
5538 return changed;
5539}
5540
5541static bool object_circle_select(const ViewContext *vc,
5542 const eSelectOp sel_op,
5543 const int mval[2],
5544 float rad)
5545{
5547 Scene *scene = vc->scene;
5548 ViewLayer *view_layer = vc->view_layer;
5549 View3D *v3d = vc->v3d;
5550
5551 const float radius_squared = rad * rad;
5552 const float mval_fl[2] = {float(mval[0]), float(mval[1])};
5553
5554 bool changed = false;
5555 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
5556 changed |= object_deselect_all_visible(vc->scene, vc->view_layer, vc->v3d);
5557 }
5558 const bool select = (sel_op != SEL_OP_SUB);
5559 const int select_flag = select ? BASE_SELECTED : 0;
5560 BKE_view_layer_synced_ensure(scene, view_layer);
5562 if (BASE_SELECTABLE(v3d, base) && ((base->flag & BASE_SELECTED) != select_flag)) {
5563 float screen_co[2];
5565 base->object->object_to_world().location(),
5566 screen_co,
5568 {
5569 if (len_squared_v2v2(mval_fl, screen_co) <= radius_squared) {
5572 changed = true;
5573 }
5574 }
5575 }
5576 }
5577
5578 return changed;
5579}
5580
5581/* not a real operator, only for circle test */
5582static void view3d_circle_select_recalc(void *user_data)
5583{
5584 bContext *C = static_cast<bContext *>(user_data);
5585 Object *obedit_active = CTX_data_edit_object(C);
5586
5587 if (obedit_active) {
5588 switch (obedit_active->type) {
5589 case OB_MESH: {
5592 vc.scene, vc.view_layer, vc.v3d, vc.obact->type, vc.obact->mode, ob_iter)
5593 {
5596 }
5598 break;
5599 }
5600
5601 default: {
5602 /* TODO: investigate if this is needed for other object types. */
5604 break;
5605 }
5606 }
5607 }
5608}
5609
5611 wmOperator *op,
5612 const wmEvent *event)
5613{
5615 if (result & OPERATOR_FINISHED) {
5617 }
5618 return result;
5619}
5620
5626
5628{
5630 const int radius = RNA_int_get(op->ptr, "radius");
5631 const int mval[2] = {RNA_int_get(op->ptr, "x"), RNA_int_get(op->ptr, "y")};
5632
5633 /* Allow each selection type to allocate their own data that's used between executions. */
5634 wmGesture *gesture = static_cast<wmGesture *>(op->customdata); /* nullptr when non-modal. */
5635 wmGenericUserData wm_userdata_buf = {nullptr, nullptr, false};
5636 wmGenericUserData *wm_userdata = gesture ? &gesture->user_data : &wm_userdata_buf;
5637
5638 const eSelectOp sel_op = ED_select_op_modal(
5639 static_cast<eSelectOp>(RNA_enum_get(op->ptr, "mode")), WM_gesture_is_modal_first(gesture));
5640
5642
5643 Object *obact = vc.obact;
5644 Object *obedit = vc.obedit;
5645
5646 if (obedit || BKE_paint_select_elem_test(obact) || (obact && (obact->mode & OB_MODE_POSE))) {
5648 if (obedit == nullptr) {
5650 }
5651 else {
5652 if (vc.obedit->type == OB_MESH) {
5654 }
5655 }
5656
5658 vc.scene, vc.view_layer, vc.v3d, obact->type, obact->mode, ob_iter)
5659 {
5661
5662 obact = vc.obact;
5663 obedit = vc.obedit;
5664
5665 if (obedit) {
5666 obedit_circle_select(C, &vc, wm_userdata, sel_op, mval, float(radius));
5667 }
5668 else if (BKE_paint_select_face_test(obact)) {
5669 paint_facesel_circle_select(&vc, wm_userdata, sel_op, mval, float(radius));
5670 }
5671 else if (BKE_paint_select_vert_test(obact)) {
5672 paint_vertsel_circle_select(&vc, wm_userdata, sel_op, mval, float(radius));
5673 }
5674 else if (BKE_paint_select_grease_pencil_test(obact)) {
5675 grease_pencil_circle_select(&vc, sel_op, mval, float(radius));
5676 }
5677 else if (obact->mode & OB_MODE_POSE) {
5678 pose_circle_select(&vc, sel_op, mval, float(radius));
5680 }
5681 else {
5682 BLI_assert(0);
5683 }
5684 }
5686 }
5687 else if (obact && (obact->mode & OB_MODE_PARTICLE_EDIT)) {
5688 if (PE_circle_select(C, wm_userdata, sel_op, mval, float(radius))) {
5689 return OPERATOR_FINISHED;
5690 }
5691 return OPERATOR_CANCELLED;
5692 }
5693 else if (obact && obact->mode & OB_MODE_SCULPT) {
5694 return OPERATOR_CANCELLED;
5695 }
5696 else if (Object *obact_pose = (obact && (obact->mode & OB_MODE_WEIGHT_PAINT)) ?
5698 nullptr)
5699 {
5700 ED_view3d_viewcontext_init_object(&vc, obact_pose);
5701 pose_circle_select(&vc, sel_op, mval, float(radius));
5703 }
5704 else {
5705 if (object_circle_select(&vc, sel_op, mval, float(radius))) {
5708
5710 }
5711 }
5712
5713 /* Otherwise this is freed by the gesture. */
5714 if (wm_userdata == &wm_userdata_buf) {
5715 WM_generic_user_data_free(wm_userdata);
5716 }
5717 else {
5718 EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
5719 if (esel && esel->select_bitmap) {
5720 MEM_freeN(esel->select_bitmap);
5721 esel->select_bitmap = nullptr;
5722 }
5723 }
5724
5725 return OPERATOR_FINISHED;
5726}
5727
5729{
5730 ot->name = "Circle Select";
5731 ot->description = "Select items using circle selection";
5732 ot->idname = "VIEW3D_OT_select_circle";
5733
5734 ot->invoke = WM_gesture_circle_invoke;
5737 ot->poll = view3d_selectable_data;
5739 ot->get_name = ED_select_circle_get_name;
5740
5741 /* flags */
5742 ot->flag = OPTYPE_UNDO;
5743
5744 /* properties */
5747}
5748
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 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:4376
ListBase * BKE_curve_editNurbs_get(Curve *cu)
Definition curve.cc:419
void BKE_curve_nurb_vert_active_validate(Curve *cu)
Definition curve.cc:5058
void BKE_nurbList_flag_set(ListBase *editnurb, uint8_t flag, bool set)
Definition curve.cc:4341
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:603
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:1654
bool BKE_paint_select_elem_test(const Object *ob)
Definition paint.cc:1665
bool BKE_paint_select_vert_test(const Object *ob)
Definition paint.cc:1647
bool BKE_paint_select_face_test(const Object *ob)
Definition paint.cc:1640
struct MovieTrackingTrack * BKE_tracking_track_get_for_selection_index(struct MovieTracking *tracking, int selection_index, struct ListBase **r_tracksbase)
Definition tracking.cc:1025
void BKE_tracking_track_deselect(struct MovieTrackingTrack *track, int area)
Definition tracking.cc:1217
@ 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:1190
#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
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_utf8(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_TAG_DOIT
Definition DNA_ID.h:1036
@ ID_RECALC_SELECT
Definition DNA_ID.h:1101
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ ID_RECALC_BASE_FLAGS
Definition DNA_ID.h:1104
#define MAX_ID_NAME
Definition DNA_ID.h:373
@ POSE_RUNTIME_IN_SELECTION_AREA
@ POSE_SELECTED
@ BONE_ROOTSEL
@ BONE_SELECTED
@ BONE_UNSELECTABLE
@ 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_SELECT_FACE
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
@ SCE_OBJECT_MODE_LOCK
#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:654
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)
Definition meshtools.cc:780
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:979
@ VIEW3D_SELECT_PICK_ALL
Definition ED_view3d.hh:983
@ VIEW3D_SELECT_PICK_NEAREST
Definition ED_view3d.hh:985
@ VIEW3D_SELECT_ALL
Definition ED_view3d.hh:981
#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:988
@ VIEW3D_SELECT_FILTER_NOP
Definition ED_view3d.hh:990
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.
#define MEM_SAFE_FREE(v)
@ PROP_ENUM_NO_TRANSLATE
Definition RNA_types.hh:432
@ PROP_SKIP_SAVE
Definition RNA_types.hh:344
@ PROP_HIDDEN
Definition RNA_types.hh:338
#define C
Definition RandGen.cpp:29
int UI_icon_from_id(const ID *id)
#define NC_GEOM
Definition WM_types.hh:393
#define ND_DATA
Definition WM_types.hh:509
#define NC_MOVIECLIP
Definition WM_types.hh:397
#define ND_OB_SELECT
Definition WM_types.hh:442
#define NC_SCENE
Definition WM_types.hh:378
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:218
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define ND_SELECT
Definition WM_types.hh:508
#define ND_BONE_ACTIVE
Definition WM_types.hh:459
#define ND_BONE_SELECT
Definition WM_types.hh:460
#define NC_OBJECT
Definition WM_types.hh:379
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_pchan_from_select_buffer(const Span< Base * > bases, const uint select_id, bPoseChannel **r_pchan)
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, BMSelectFlushFlag flag)
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.
#define BMSelectFlushFlag_All
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)
static std::unique_ptr< UVSyncSelectFromMesh > create_if_needed(const ToolSettings &ts, BMesh &bm)
bool is_empty() const
Definition BLI_array.hh:264
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
int64_t size() const
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, AttrType 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, AttrType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
GAttributeWriter lookup_or_add_for_write(StringRef attribute_id, AttrDomain domain, AttrType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
float4x4 to_world_space(const Object &object) const
void vert_select_set(BMVert *v, bool value)
void face_select_set(BMFace *f, bool value)
void edge_select_set(BMEdge *f, bool value)
nullptr float
#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:2570
static bool is_inside(int x, int y, int cols, int rows)
Definition filesel.cc:764
#define select(A, B, C)
MatBase< 4, 4 > float4x4
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(const bArmature *armature, const Bone *bone)
bool bone_is_selectable(const bArmature *armature, const bPoseChannel *pchan)
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)
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)
bke::GSpanAttributeWriter ensure_selection_attribute(bke::CurvesGeometry &curves, bke::AttrDomain selection_domain, bke::AttrType create_type, StringRef attribute_name)
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, const int handle_display, 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:163
bool select_lasso(PointCloud &pointcloud, const ARegion &region, const float4x4 &projection, const Span< int2 > lasso_coords, const eSelectOp sel_op)
Definition selection.cc:182
bool select_circle(PointCloud &pointcloud, const ARegion &region, const float4x4 &projection, const int2 coord, const float radius, const eSelectOp sel_op)
Definition selection.cc:211
bke::GSpanAttributeWriter ensure_selection_attribute(PointCloud &pointcloud, bke::AttrType create_type)
Definition selection.cc:31
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:241
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, bPoseChannel *pchan, 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)
const char * name
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
UVSyncSelectFromMesh * uv_selctx
eBezTriple_Flag select_flag
blender::MutableSpan< bool > select_vert
const ViewContext * vc
UVSyncSelectFromMesh * uv_selctx
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::@275371335250266324235150226366250166246037204077 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:414
int tag
Definition DNA_ID.h:442
char name[258]
Definition DNA_ID.h:432
blender::MutableSpan< bool > select_vert
eBezTriple_Flag select_flag
const ViewContext * vc
UVSyncSelectFromMesh * uv_selctx
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 bArmature_Runtime runtime
struct Bone * bone
struct bPoseChannel_Runtime runtime
ListBase chanbase
float xmax
float xmin
float ymax
float ymin
int mval[2]
Definition WM_types.hh:763
wmGenericUserDataFreeFn free_fn
Definition WM_types.hh:143
wmGenericUserData user_data
Definition WM_types.hh:666
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 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 edbm_backbuf_check_and_select_edges(EditSelectBuf_Cache *esel, Depsgraph *depsgraph, Object *ob, BMEditMesh *em, UVSyncSelectFromMesh *uv_selctx, const eSelectOp sel_op)
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 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 bool edbm_backbuf_check_and_select_verts(EditSelectBuf_Cache *esel, Depsgraph *depsgraph, Object *ob, BMEditMesh *em, UVSyncSelectFromMesh *uv_selctx, const eSelectOp sel_op)
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 bool edbm_backbuf_check_and_select_faces(EditSelectBuf_Cache *esel, Depsgraph *depsgraph, Object *ob, BMEditMesh *em, UVSyncSelectFromMesh *uv_selctx, const eSelectOp sel_op)
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])
wmOperatorStatus WM_operator_name_call_ptr(bContext *C, wmOperatorType *ot, blender::wm::OpCallContext context, PointerRNA *properties, const wmEvent *event)
void WM_main_add_notifier(uint type, void *reference)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
PointerRNA * ptr
Definition wm_files.cc:4238
wmOperatorType * ot
Definition wm_files.cc:4237
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