Blender V4.3
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
9#include <cfloat>
10#include <cmath>
11#include <cstdio>
12#include <cstring>
13#include <optional>
14
15#include "DNA_action_types.h"
16#include "DNA_armature_types.h"
17#include "DNA_curve_types.h"
19#include "DNA_mesh_types.h"
20#include "DNA_meshdata_types.h"
21#include "DNA_meta_types.h"
22#include "DNA_object_types.h"
23#include "DNA_scene_types.h"
24#include "DNA_tracking_types.h"
25
26#include "MEM_guardedalloc.h"
27
28#include "BLI_bitmap.h"
29#include "BLI_function_ref.hh"
30#include "BLI_lasso_2d.hh"
31#include "BLI_linklist.h"
32#include "BLI_listbase.h"
33#include "BLI_math_bits.h"
34#include "BLI_math_geom.h"
35#include "BLI_rect.h"
36#include "BLI_span.hh"
37#include "BLI_string.h"
38#include "BLI_task.hh"
39#include "BLI_utildefines.h"
40#include "BLI_vector.hh"
41
42#ifdef __BIG_ENDIAN__
43# include "BLI_endian_switch.h"
44#endif
45
46/* vertex box select */
47#include "IMB_imbuf.hh"
48#include "IMB_imbuf_types.hh"
49
50#include "BKE_action.hh"
51#include "BKE_armature.hh"
52#include "BKE_attribute.hh"
53#include "BKE_context.hh"
54#include "BKE_crazyspace.hh"
55#include "BKE_curve.hh"
56#include "BKE_curves.hh"
57#include "BKE_editmesh.hh"
58#include "BKE_global.hh"
59#include "BKE_grease_pencil.hh"
60#include "BKE_layer.hh"
61#include "BKE_main.hh"
62#include "BKE_mball.hh"
63#include "BKE_mesh.hh"
64#include "BKE_object.hh"
65#include "BKE_object_types.hh"
66#include "BKE_paint.hh"
67#include "BKE_scene.hh"
68#include "BKE_tracking.h"
69#include "BKE_workspace.hh"
70
71#include "WM_api.hh"
72#include "WM_toolsystem.hh"
73#include "WM_types.hh"
74
75#include "RNA_access.hh"
76#include "RNA_define.hh"
77#include "RNA_enum_types.hh"
78
79#include "ED_armature.hh"
80#include "ED_curve.hh"
81#include "ED_curves.hh"
82#include "ED_gpencil_legacy.hh"
83#include "ED_grease_pencil.hh"
84#include "ED_lattice.hh"
85#include "ED_mball.hh"
86#include "ED_mesh.hh"
87#include "ED_object.hh"
88#include "ED_outliner.hh"
89#include "ED_particle.hh"
90#include "ED_screen.hh"
91#include "ED_sculpt.hh"
92#include "ED_select_utils.hh"
93
94#include "UI_interface.hh"
95#include "UI_resources.hh"
96
97#include "GPU_matrix.hh"
98#include "GPU_platform.hh"
99#include "GPU_select.hh"
100
101#include "DEG_depsgraph.hh"
102#include "DEG_depsgraph_query.hh"
103
104#include "DRW_engine.hh"
105#include "DRW_select_buffer.hh"
106
108
109#include "view3d_intern.hh" /* own include */
110
111// #include "BLI_time_utildefines.h"
112
113using blender::Array;
114using blender::int2;
115using blender::Span;
116using blender::Vector;
117
118/* -------------------------------------------------------------------- */
123{
124 return 75.0f * U.pixelsize;
125}
126
128{
129 /* TODO: should return whether there is valid context to continue. */
130 ViewContext vc = {};
131 vc.C = C;
132 vc.region = CTX_wm_region(C);
133 vc.bmain = CTX_data_main(C);
134 vc.depsgraph = depsgraph;
135 vc.scene = CTX_data_scene(C);
137 vc.v3d = CTX_wm_view3d(C);
138 vc.win = CTX_wm_window(C);
142 return vc;
143}
144
146{
147 vc->obact = obact;
148 /* See public doc-string for rationale on checking the existing values first. */
149 if (vc->obedit) {
151 vc->obedit = obact;
152 if (vc->em) {
154 }
155 }
156}
157
160/* -------------------------------------------------------------------- */
164static bool object_deselect_all_visible(const Scene *scene, ViewLayer *view_layer, View3D *v3d)
165{
166 bool changed = false;
167 BKE_view_layer_synced_ensure(scene, view_layer);
169 if (base->flag & BASE_SELECTED) {
170 if (BASE_SELECTABLE(v3d, base)) {
172 changed = true;
173 }
174 }
175 }
176 return changed;
177}
178
179/* deselect all except b */
180static bool object_deselect_all_except(const Scene *scene, ViewLayer *view_layer, Base *b)
181{
182 bool changed = false;
183 BKE_view_layer_synced_ensure(scene, view_layer);
185 if (base->flag & BASE_SELECTED) {
186 if (b != base) {
188 changed = true;
189 }
190 }
191 }
192 return changed;
193}
194
197/* -------------------------------------------------------------------- */
211
212static void editselect_buf_cache_init(const ViewContext *vc, short select_mode)
213{
214 if (vc->obedit) {
216 vc->scene, vc->view_layer, vc->v3d);
217
218 DRW_select_buffer_context_create(vc->depsgraph, bases, select_mode);
219 }
220 else {
221 /* Use for paint modes, currently only a single object at a time. */
222 if (vc->obact) {
225 DRW_select_buffer_context_create(vc->depsgraph, {base}, select_mode);
226 }
227 }
228}
229
234
235static void editselect_buf_cache_free_voidp(void *esel_voidp)
236{
237 editselect_buf_cache_free(static_cast<EditSelectBuf_Cache *>(esel_voidp));
238 MEM_freeN(esel_voidp);
239}
240
242 const ViewContext *vc,
243 short select_mode)
244{
245 EditSelectBuf_Cache *esel = MEM_cnew<EditSelectBuf_Cache>(__func__);
246 wm_userdata->data = esel;
248 wm_userdata->use_free = true;
249 editselect_buf_cache_init(vc, select_mode);
250}
251
254/* -------------------------------------------------------------------- */
259 Depsgraph *depsgraph,
260 Object *ob,
261 BMEditMesh *em,
262 const eSelectOp sel_op)
263{
264 BMVert *eve;
265 BMIter iter;
266 bool changed = false;
267
268 const BLI_bitmap *select_bitmap = esel->select_bitmap;
270 if (index == 0) {
271 return false;
272 }
273
274 index -= 1;
275 BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
277 const bool is_select = BM_elem_flag_test(eve, BM_ELEM_SELECT);
278 const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index);
279 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
280 if (sel_op_result != -1) {
281 BM_vert_select_set(em->bm, eve, sel_op_result);
282 changed = true;
283 }
284 }
285 index++;
286 }
287 return changed;
288}
289
291 Depsgraph *depsgraph,
292 Object *ob,
293 BMEditMesh *em,
294 const eSelectOp sel_op)
295{
296 BMEdge *eed;
297 BMIter iter;
298 bool changed = false;
299
300 const BLI_bitmap *select_bitmap = esel->select_bitmap;
302 if (index == 0) {
303 return false;
304 }
305
306 index -= 1;
307 BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
309 const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT);
310 const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index);
311 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
312 if (sel_op_result != -1) {
313 BM_edge_select_set(em->bm, eed, sel_op_result);
314 changed = true;
315 }
316 }
317 index++;
318 }
319 return changed;
320}
321
323 Depsgraph *depsgraph,
324 Object *ob,
325 BMEditMesh *em,
326 const eSelectOp sel_op)
327{
328 BMFace *efa;
329 BMIter iter;
330 bool changed = false;
331
332 const BLI_bitmap *select_bitmap = esel->select_bitmap;
334 if (index == 0) {
335 return false;
336 }
337
338 index -= 1;
339 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
341 const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT);
342 const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index);
343 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
344 if (sel_op_result != -1) {
345 BM_face_select_set(em->bm, efa, sel_op_result);
346 changed = true;
347 }
348 }
349 index++;
350 }
351 return changed;
352}
353
354/* object mode, edbm_ prefix is confusing here, rename? */
357 const eSelectOp sel_op)
358{
359 using namespace blender;
360 bool changed = false;
361
362 const BLI_bitmap *select_bitmap = esel->select_bitmap;
363
364 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
365 bke::SpanAttributeWriter<bool> select_vert = attributes.lookup_or_add_for_write_span<bool>(
366 ".select_vert", bke::AttrDomain::Point);
367 const VArray<bool> hide_vert = *attributes.lookup_or_default<bool>(
368 ".hide_vert", bke::AttrDomain::Point, false);
369
370 for (int index = 0; index < mesh->verts_num; index++) {
371 if (!hide_vert[index]) {
372 const bool is_select = select_vert.span[index];
373 const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index);
374 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
375 if (sel_op_result != -1) {
376 select_vert.span[index] = sel_op_result == 1;
377 changed = true;
378 }
379 }
380 }
381 select_vert.finish();
382 return changed;
383}
384
385/* object mode, edbm_ prefix is confusing here, rename? */
388 const eSelectOp sel_op)
389{
390 using namespace blender;
391 bool changed = false;
392
393 const BLI_bitmap *select_bitmap = esel->select_bitmap;
394
395 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
396 bke::SpanAttributeWriter<bool> select_poly = attributes.lookup_or_add_for_write_span<bool>(
397 ".select_poly", bke::AttrDomain::Face);
398 const VArray<bool> hide_poly = *attributes.lookup_or_default<bool>(
399 ".hide_poly", bke::AttrDomain::Face, false);
400
401 for (int index = 0; index < mesh->faces_num; index++) {
402 if (!hide_poly[index]) {
403 const bool is_select = select_poly.span[index];
404 const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index);
405 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
406 if (sel_op_result != -1) {
407 select_poly.span[index] = sel_op_result == 1;
408 changed = true;
409 }
410 }
411 }
412 select_poly.finish();
413 return changed;
414}
415
418/* -------------------------------------------------------------------- */
436
438 const ViewContext *vc,
439 const rcti *rect,
440 const Span<int2> mcoords,
441 const eSelectOp sel_op)
442{
443 r_data->vc = vc;
444
445 r_data->rect = rect;
446 r_data->rect_fl = &r_data->_rect_fl;
447 BLI_rctf_rcti_copy(&r_data->_rect_fl, rect);
448
449 r_data->mcoords = mcoords;
450 r_data->sel_op = sel_op;
451 /* SELECT by default, but can be changed if needed (only few cases use and respect this). */
453
454 /* runtime */
455 r_data->pass = 0;
456 r_data->is_done = false;
457 r_data->is_changed = false;
458}
459
461{
463 return false;
464 }
465 if (Object *ob = CTX_data_active_object(C)) {
466 if (ob->mode & OB_MODE_EDIT) {
467 return ob->type != OB_FONT;
468 }
471 }
472 if (ob->mode & OB_MODE_WEIGHT_PAINT) {
474 }
475 }
476
477 return true;
478}
479
480/* helper also for box_select */
481static bool edge_fully_inside_rect(const rctf *rect, const float v1[2], const float v2[2])
482{
483 return BLI_rctf_isect_pt_v(rect, v1) && BLI_rctf_isect_pt_v(rect, v2);
484}
485
486static bool edge_inside_rect(const rctf *rect, const float v1[2], const float v2[2])
487{
488 int d1, d2, d3, d4;
489
490 /* check points in rect */
491 if (edge_fully_inside_rect(rect, v1, v2)) {
492 return true;
493 }
494
495 /* check points completely out rect */
496 if (v1[0] < rect->xmin && v2[0] < rect->xmin) {
497 return false;
498 }
499 if (v1[0] > rect->xmax && v2[0] > rect->xmax) {
500 return false;
501 }
502 if (v1[1] < rect->ymin && v2[1] < rect->ymin) {
503 return false;
504 }
505 if (v1[1] > rect->ymax && v2[1] > rect->ymax) {
506 return false;
507 }
508
509 /* simple check lines intersecting. */
510 d1 = (v1[1] - v2[1]) * (v1[0] - rect->xmin) + (v2[0] - v1[0]) * (v1[1] - rect->ymin);
511 d2 = (v1[1] - v2[1]) * (v1[0] - rect->xmin) + (v2[0] - v1[0]) * (v1[1] - rect->ymax);
512 d3 = (v1[1] - v2[1]) * (v1[0] - rect->xmax) + (v2[0] - v1[0]) * (v1[1] - rect->ymax);
513 d4 = (v1[1] - v2[1]) * (v1[0] - rect->xmax) + (v2[0] - v1[0]) * (v1[1] - rect->ymin);
514
515 if (d1 < 0 && d2 < 0 && d3 < 0 && d4 < 0) {
516 return false;
517 }
518 if (d1 > 0 && d2 > 0 && d3 > 0 && d4 > 0) {
519 return false;
520 }
521
522 return true;
523}
524
525static void do_lasso_select_pose__do_tag(void *user_data,
526 bPoseChannel *pchan,
527 const float screen_co_a[2],
528 const float screen_co_b[2])
529{
530 LassoSelectUserData *data = static_cast<LassoSelectUserData *>(user_data);
531 const bArmature *arm = static_cast<bArmature *>(data->vc->obact->data);
532 if (!PBONE_SELECTABLE(arm, pchan->bone)) {
533 return;
534 }
535
536 if (BLI_rctf_isect_segment(data->rect_fl, screen_co_a, screen_co_b) &&
537 BLI_lasso_is_edge_inside(data->mcoords, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX))
538 {
539 pchan->bone->flag |= BONE_DONE;
540 data->is_changed = true;
541 }
542}
543static void do_lasso_tag_pose(const ViewContext *vc, const Span<int2> mcoords)
544{
546 rcti rect;
547
548 if ((vc->obact->type != OB_ARMATURE) || (vc->obact->pose == nullptr)) {
549 return;
550 }
551
552 BLI_lasso_boundbox(&rect, mcoords);
553
554 view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, static_cast<eSelectOp>(0));
555
557
558 /* Treat bones as clipped segments (no joints). */
561 &data,
563}
564
566 const Span<int2> mcoords,
567 const eSelectOp sel_op)
568{
569 View3D *v3d = vc->v3d;
570
571 bool changed = false;
572 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
573 changed |= object_deselect_all_visible(vc->scene, vc->view_layer, vc->v3d);
574 }
577 if (BASE_SELECTABLE(v3d, base)) { /* Use this to avoid unnecessary lasso look-ups. */
578 float region_co[2];
579 const bool is_select = base->flag & BASE_SELECTED;
580 const bool is_inside = (ED_view3d_project_base(vc->region, base, region_co) ==
583 int(region_co[0]),
584 int(region_co[1]),
585 /* Dummy value. */
586 INT_MAX);
587 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
588 if (sel_op_result != -1) {
590 sel_op_result ? blender::ed::object::BA_SELECT :
592 changed = true;
593 }
594 }
595 }
596
597 if (changed) {
600 }
601 return changed;
602}
603
608{
609 auto bases_tag_and_append_fn = [](blender::Vector<Base *> &bases, Base *base) {
610 Object *ob = base->object;
611 bArmature *arm = static_cast<bArmature *>(ob->data);
612 LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
613 Bone *bone = pchan->bone;
614 bone->flag &= ~BONE_DONE;
615 }
616 arm->id.tag |= ID_TAG_DOIT;
617 ob->id.tag &= ~ID_TAG_DOIT;
618 bases.append(base);
619 };
620
622
623 /* Special case, pose + weight paint mode. */
624 if (vc->obact && (vc->obact->mode & OB_MODE_WEIGHT_PAINT)) {
626 BLI_assert(ob_pose != nullptr); /* Caller is expected to check. */
627 Base *base = BKE_view_layer_base_find(vc->view_layer, ob_pose);
628 if (base) {
629 bases_tag_and_append_fn(bases, base);
630 }
631 }
632 else {
634 vc->scene, vc->view_layer, vc->v3d, OB_ARMATURE, OB_MODE_POSE, base_iter)
635 {
636 bases_tag_and_append_fn(bases, base_iter);
637 }
639 }
640 return bases;
641}
642
644{
645 bool changed_multi = false;
646
647 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
648 for (const int i : bases.index_range()) {
649 Base *base_iter = bases[i];
650 Object *ob_iter = base_iter->object;
651 if (ED_pose_deselect_all(ob_iter, SEL_DESELECT, false)) {
653 changed_multi = true;
654 }
655 }
656 }
657
658 for (const int i : bases.index_range()) {
659 Base *base_iter = bases[i];
660 Object *ob_iter = base_iter->object;
661 bArmature *arm = static_cast<bArmature *>(ob_iter->data);
662
663 /* Don't handle twice. */
664 if (arm->id.tag & ID_TAG_DOIT) {
665 arm->id.tag &= ~ID_TAG_DOIT;
666 }
667 else {
668 continue;
669 }
670
671 bool changed = true;
672 LISTBASE_FOREACH (bPoseChannel *, pchan, &ob_iter->pose->chanbase) {
673 Bone *bone = pchan->bone;
674 if ((bone->flag & BONE_UNSELECTABLE) == 0) {
675 const bool is_select = bone->flag & BONE_SELECTED;
676 const bool is_inside = bone->flag & BONE_DONE;
677 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
678 if (sel_op_result != -1) {
679 SET_FLAG_FROM_TEST(bone->flag, sel_op_result, BONE_SELECTED);
680 if (sel_op_result == 0) {
681 if (arm->act_bone == bone) {
682 arm->act_bone = nullptr;
683 }
684 }
685 changed = true;
686 }
687 }
688 }
689 if (changed) {
691 changed_multi = true;
692 }
693 }
694 return changed_multi;
695}
696
697static bool do_lasso_select_pose(const ViewContext *vc,
698 const Span<int2> mcoords,
699 const eSelectOp sel_op)
700{
702
703 ViewContext vc_temp = *vc;
704
705 for (const int i : bases.index_range()) {
706 Base *base_iter = bases[i];
707 Object *ob_iter = base_iter->object;
708 ED_view3d_viewcontext_init_object(&vc_temp, ob_iter);
709 do_lasso_tag_pose(&vc_temp, mcoords);
710 }
711
712 const bool changed_multi = do_pose_tag_select_op_exec(bases, sel_op);
713 if (changed_multi) {
716 }
717
718 return changed_multi;
719}
720
721static void do_lasso_select_mesh__doSelectVert(void *user_data,
722 BMVert *eve,
723 const float screen_co[2],
724 int /*index*/)
725{
726 LassoSelectUserData *data = static_cast<LassoSelectUserData *>(user_data);
727 const bool is_select = BM_elem_flag_test(eve, BM_ELEM_SELECT);
728 const bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
730 data->mcoords, screen_co[0], screen_co[1], IS_CLIPPED));
731 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
732 if (sel_op_result != -1) {
733 BM_vert_select_set(data->vc->em->bm, eve, sel_op_result);
734 data->is_changed = true;
735 }
736}
743 BMEdge *eed,
744 const float screen_co_a[2],
745 const float screen_co_b[2],
746 int index)
747{
749 user_data);
750 LassoSelectUserData *data = data_for_edge->data;
751 bool is_visible = true;
752 if (data_for_edge->backbuf_offset) {
753 uint bitmap_inedx = data_for_edge->backbuf_offset + index - 1;
754 is_visible = BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, bitmap_inedx);
755 }
756
757 const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT);
758 const bool is_inside =
759 (is_visible && edge_fully_inside_rect(data->rect_fl, screen_co_a, screen_co_b) &&
760 BLI_lasso_is_point_inside(data->mcoords, UNPACK2(screen_co_a), IS_CLIPPED) &&
761 BLI_lasso_is_point_inside(data->mcoords, UNPACK2(screen_co_b), IS_CLIPPED));
762 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
763 if (sel_op_result != -1) {
764 BM_edge_select_set(data->vc->em->bm, eed, sel_op_result);
765 data->is_done = true;
766 data->is_changed = true;
767 }
768}
770 BMEdge *eed,
771 const float screen_co_a[2],
772 const float screen_co_b[2],
773 int index)
774{
776 user_data);
777 LassoSelectUserData *data = data_for_edge->data;
778 bool is_visible = true;
779 if (data_for_edge->backbuf_offset) {
780 uint bitmap_inedx = data_for_edge->backbuf_offset + index - 1;
781 is_visible = BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, bitmap_inedx);
782 }
783
784 const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT);
785 const bool is_inside = (is_visible && BLI_lasso_is_edge_inside(data->mcoords,
786 UNPACK2(screen_co_a),
787 UNPACK2(screen_co_b),
788 IS_CLIPPED));
789 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
790 if (sel_op_result != -1) {
791 BM_edge_select_set(data->vc->em->bm, eed, sel_op_result);
792 data->is_changed = true;
793 }
794}
795
796static void do_lasso_select_mesh__doSelectFace(void *user_data,
797 BMFace *efa,
798 const float screen_co[2],
799 int /*index*/)
800{
801 LassoSelectUserData *data = static_cast<LassoSelectUserData *>(user_data);
802 const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT);
803 const bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
805 data->mcoords, screen_co[0], screen_co[1], IS_CLIPPED));
806 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
807 if (sel_op_result != -1) {
808 BM_face_select_set(data->vc->em->bm, efa, sel_op_result);
809 data->is_changed = true;
810 }
811}
812
813static bool do_lasso_select_mesh(const ViewContext *vc,
814 wmGenericUserData *wm_userdata,
815 const Span<int2> mcoords,
816 const eSelectOp sel_op)
817{
820 rcti rect;
821
823
824 BLI_lasso_boundbox(&rect, mcoords);
825
826 view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, sel_op);
827
828 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
829 if (vc->em->bm->totvertsel) {
831 data.is_changed = true;
832 }
833 }
834
835 /* for non zbuf projections, don't change the GL state */
837
839
840 const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d);
841
842 EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
843 if (use_zbuf) {
844 if (wm_userdata->data == nullptr) {
846 esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
848 vc->depsgraph, vc->region, vc->v3d, mcoords, &rect, nullptr);
849 }
850 }
851
852 if (ts->selectmode & SCE_SELECT_VERTEX) {
853 if (use_zbuf) {
854 data.is_changed |= edbm_backbuf_check_and_select_verts(
855 esel, vc->depsgraph, vc->obedit, vc->em, sel_op);
856 }
857 else {
860 }
861 }
862 if (ts->selectmode & SCE_SELECT_EDGE) {
863 /* Does both use_zbuf and non-use_zbuf versions (need screen cos for both) */
864 LassoSelectUserData_ForMeshEdge data_for_edge{};
865 data_for_edge.data = &data;
866 data_for_edge.esel = use_zbuf ? esel : nullptr;
867 data_for_edge.backbuf_offset = use_zbuf ? DRW_select_buffer_context_offset_for_object_elem(
868 vc->depsgraph, vc->obedit, SCE_SELECT_EDGE) :
869 0;
870
871 const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_NEAR |
872 (use_zbuf ? (eV3DProjTest)0 : V3D_PROJ_TEST_CLIP_BB);
873 /* Fully inside. */
875 vc, do_lasso_select_mesh__doSelectEdge_pass0, &data_for_edge, clip_flag);
876 if (data.is_done == false) {
877 /* Fall back to partially inside.
878 * Clip content to account for edges partially behind the view. */
881 &data_for_edge,
883 }
884 }
885
886 if (ts->selectmode & SCE_SELECT_FACE) {
887 if (use_zbuf) {
888 data.is_changed |= edbm_backbuf_check_and_select_faces(
889 esel, vc->depsgraph, vc->obedit, vc->em, sel_op);
890 }
891 else {
894 }
895 }
896
897 if (data.is_changed) {
899 }
900 return data.is_changed;
901}
902
903static void do_lasso_select_curve__doSelect(void *user_data,
904 Nurb * /*nu*/,
905 BPoint *bp,
906 BezTriple *bezt,
907 int beztindex,
908 bool handles_visible,
909 const float screen_co[2])
910{
911 LassoSelectUserData *data = static_cast<LassoSelectUserData *>(user_data);
912
914 data->mcoords, screen_co[0], screen_co[1], IS_CLIPPED);
915 if (bp) {
916 const bool is_select = bp->f1 & SELECT;
917 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
918 if (sel_op_result != -1) {
919 SET_FLAG_FROM_TEST(bp->f1, sel_op_result, data->select_flag);
920 data->is_changed = true;
921 }
922 }
923 else {
924 if (!handles_visible) {
925 /* can only be (beztindex == 1) here since handles are hidden */
926 const bool is_select = bezt->f2 & SELECT;
927 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
928 if (sel_op_result != -1) {
929 SET_FLAG_FROM_TEST(bezt->f2, sel_op_result, data->select_flag);
930 }
931 bezt->f1 = bezt->f3 = bezt->f2;
932 data->is_changed = true;
933 }
934 else {
935 uint8_t *flag_p = (&bezt->f1) + beztindex;
936 const bool is_select = *flag_p & SELECT;
937 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
938 if (sel_op_result != -1) {
939 SET_FLAG_FROM_TEST(*flag_p, sel_op_result, data->select_flag);
940 data->is_changed = true;
941 }
942 }
943 }
944}
945
946static bool do_lasso_select_curve(const ViewContext *vc,
947 const Span<int2> mcoords,
948 const eSelectOp sel_op)
949{
950 const bool deselect_all = (sel_op == SEL_OP_SET);
952 rcti rect;
953
954 BLI_lasso_boundbox(&rect, mcoords);
955
956 view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, sel_op);
957
958 Curve *curve = (Curve *)vc->obedit->data;
959 ListBase *nurbs = BKE_curve_editNurbs_get(curve);
960
961 /* For deselect all, items to be selected are tagged with temp flag. Clear that first. */
962 if (deselect_all) {
964 data.select_flag = BEZT_FLAG_TEMP_TAG;
965 }
966
967 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
969
970 /* Deselect items that were not added to selection (indicated by temp flag). */
971 if (deselect_all) {
973 }
974
975 if (data.is_changed) {
977 }
978 return data.is_changed;
979}
980
981static void do_lasso_select_lattice__doSelect(void *user_data,
982 BPoint *bp,
983 const float screen_co[2])
984{
985 LassoSelectUserData *data = static_cast<LassoSelectUserData *>(user_data);
986 const bool is_select = bp->f1 & SELECT;
987 const bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
989 data->mcoords, screen_co[0], screen_co[1], IS_CLIPPED));
990 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
991 if (sel_op_result != -1) {
992 SET_FLAG_FROM_TEST(bp->f1, sel_op_result, SELECT);
993 data->is_changed = true;
994 }
995}
997 const Span<int2> mcoords,
998 const eSelectOp sel_op)
999{
1001 rcti rect;
1002
1003 BLI_lasso_boundbox(&rect, mcoords);
1004
1005 view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, sel_op);
1006
1007 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1008 data.is_changed |= ED_lattice_flags_set(vc->obedit, 0);
1009 }
1010
1011 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
1014 return data.is_changed;
1015}
1016
1017static void do_lasso_select_armature__doSelectBone(void *user_data,
1018 EditBone *ebone,
1019 const float screen_co_a[2],
1020 const float screen_co_b[2])
1021{
1022 LassoSelectUserData *data = static_cast<LassoSelectUserData *>(user_data);
1023 const bArmature *arm = static_cast<const bArmature *>(data->vc->obedit->data);
1024 if (!EBONE_VISIBLE(arm, ebone)) {
1025 return;
1026 }
1027
1028 int is_ignore_flag = 0;
1029 int is_inside_flag = 0;
1030
1031 if (screen_co_a[0] != IS_CLIPPED) {
1032 if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_a)) &&
1033 BLI_lasso_is_point_inside(data->mcoords, UNPACK2(screen_co_a), INT_MAX))
1034 {
1035 is_inside_flag |= BONESEL_ROOT;
1036 }
1037 }
1038 else {
1039 is_ignore_flag |= BONESEL_ROOT;
1040 }
1041
1042 if (screen_co_b[0] != IS_CLIPPED) {
1043 if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_b)) &&
1044 BLI_lasso_is_point_inside(data->mcoords, UNPACK2(screen_co_b), INT_MAX))
1045 {
1046 is_inside_flag |= BONESEL_TIP;
1047 }
1048 }
1049 else {
1050 is_ignore_flag |= BONESEL_TIP;
1051 }
1052
1053 if (is_ignore_flag == 0) {
1054 if (is_inside_flag == (BONE_ROOTSEL | BONE_TIPSEL) ||
1056 data->mcoords, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX))
1057 {
1058 is_inside_flag |= BONESEL_BONE;
1059 }
1060 }
1061
1062 ebone->temp.i = is_inside_flag | (is_ignore_flag >> 16);
1063}
1065 EditBone *ebone,
1066 const float screen_co_a[2],
1067 const float screen_co_b[2])
1068{
1069 LassoSelectUserData *data = static_cast<LassoSelectUserData *>(user_data);
1070 bArmature *arm = static_cast<bArmature *>(data->vc->obedit->data);
1071 if (!EBONE_VISIBLE(arm, ebone)) {
1072 return;
1073 }
1074
1075 const int is_ignore_flag = ebone->temp.i << 16;
1076 int is_inside_flag = ebone->temp.i & ~0xFFFF;
1077
1078 /* - When #BONESEL_BONE is set, there is nothing to do.
1079 * - When #BONE_ROOTSEL or #BONE_TIPSEL have been set - they take priority over bone selection.
1080 */
1081 if (is_inside_flag & (BONESEL_BONE | BONE_ROOTSEL | BONE_TIPSEL)) {
1082 return;
1083 }
1084
1085 if (BLI_lasso_is_edge_inside(data->mcoords, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX))
1086 {
1087 is_inside_flag |= BONESEL_BONE;
1088 }
1089
1090 ebone->temp.i = is_inside_flag | (is_ignore_flag >> 16);
1091}
1092
1094 const Span<int2> mcoords,
1095 const eSelectOp sel_op)
1096{
1098 rcti rect;
1099
1100 BLI_lasso_boundbox(&rect, mcoords);
1101
1102 view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, sel_op);
1103
1104 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1105 data.is_changed |= ED_armature_edit_deselect_all_visible(vc->obedit);
1106 }
1107
1108 bArmature *arm = static_cast<bArmature *>(vc->obedit->data);
1109
1111
1113
1114 /* Operate on fully visible (non-clipped) points. */
1117
1118 /* Operate on bones as segments clipped to the viewport bounds
1119 * (needed to handle bones with both points outside the view).
1120 * A separate pass is needed since clipped coordinates can't be used for selecting joints. */
1123 &data,
1125
1126 data.is_changed |= ED_armature_edit_select_op_from_tagged(arm, sel_op);
1127
1128 if (data.is_changed) {
1130 }
1131 return data.is_changed;
1132}
1133
1134static void do_lasso_select_mball__doSelectElem(void *user_data,
1135 MetaElem *ml,
1136 const float screen_co[2])
1137{
1138 LassoSelectUserData *data = static_cast<LassoSelectUserData *>(user_data);
1139 const bool is_select = ml->flag & SELECT;
1140 const bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
1142 data->mcoords, screen_co[0], screen_co[1], INT_MAX));
1143 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
1144 if (sel_op_result != -1) {
1145 SET_FLAG_FROM_TEST(ml->flag, sel_op_result, SELECT);
1146 data->is_changed = true;
1147 }
1148}
1149static bool do_lasso_select_meta(const ViewContext *vc,
1150 const Span<int2> mcoords,
1151 const eSelectOp sel_op)
1152{
1154 rcti rect;
1155
1156 MetaBall *mb = (MetaBall *)vc->obedit->data;
1157
1158 BLI_lasso_boundbox(&rect, mcoords);
1159
1160 view3d_userdata_lassoselect_init(&data, vc, &rect, mcoords, sel_op);
1161
1162 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1163 data.is_changed |= BKE_mball_deselect_all(mb);
1164 }
1165
1167
1170
1171 return data.is_changed;
1172}
1173
1175 const Span<int2> mcoords,
1176 const eSelectOp sel_op)
1177{
1178 using namespace blender;
1179 Object *object = (vc->obedit ? vc->obedit : vc->obact);
1180 const Object *ob_eval = DEG_get_evaluated_object(vc->depsgraph, const_cast<Object *>(object));
1181 const GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
1183 vc->scene->toolsettings, object);
1184
1185 return ed::greasepencil::selection_update(
1186 vc,
1187 sel_op,
1189 const IndexMask &mask,
1190 const StringRef attribute_name,
1191 IndexMaskMemory &memory) {
1193 const bke::greasepencil::Layer &layer = grease_pencil.layer(info.layer_index);
1194 const bke::crazyspace::GeometryDeformation deformation =
1195 bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
1196 ob_eval, *object, info.layer_index, info.frame_number);
1197 const IndexMask visible_handle_elements =
1198 ed::greasepencil::retrieve_visible_bezier_handle_elements(
1199 *object, info.drawing, info.layer_index, selection_domain, memory);
1200 const float4x4 layer_to_world = layer.to_world_space(*ob_eval);
1202 layer_to_world);
1203
1204 return ed::curves::select_lasso_mask(*vc,
1205 curves,
1206 deformation,
1207 projection,
1208 mask,
1209 visible_handle_elements,
1210 selection_domain,
1211 attribute_name,
1212 mcoords,
1213 memory);
1214 });
1215}
1216
1222 const float screen_co[2],
1223 int index)
1224{
1225 using namespace blender;
1227 user_data);
1228 LassoSelectUserData *data = &mesh_data->lasso_data;
1229 const bool is_select = mesh_data->select_vert[index];
1230 const bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
1232 data->mcoords, screen_co[0], screen_co[1], IS_CLIPPED));
1233 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
1234 if (sel_op_result != -1) {
1235 mesh_data->select_vert[index] = sel_op_result == 1;
1236 data->is_changed = true;
1237 }
1238}
1240 wmGenericUserData *wm_userdata,
1241 const Span<int2> mcoords,
1242 const eSelectOp sel_op)
1243{
1244 using namespace blender;
1245 const bool use_zbuf = !XRAY_ENABLED(vc->v3d);
1246 Object *ob = vc->obact;
1247 Mesh *mesh = static_cast<Mesh *>(ob->data);
1248 rcti rect;
1249
1250 if (mesh == nullptr || mesh->verts_num == 0) {
1251 return false;
1252 }
1253
1254 bool changed = false;
1255 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1256 /* flush selection at the end */
1257 changed |= paintvert_deselect_all_visible(ob, SEL_DESELECT, false);
1258 }
1259
1260 BLI_lasso_boundbox(&rect, mcoords);
1261
1262 EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
1263 if (use_zbuf) {
1264 if (wm_userdata->data == nullptr) {
1266 esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
1268 vc->depsgraph, vc->region, vc->v3d, mcoords, &rect, nullptr);
1269 }
1270 }
1271
1272 if (use_zbuf) {
1273 if (esel->select_bitmap != nullptr) {
1274 changed |= edbm_backbuf_check_and_select_verts_obmode(mesh, esel, sel_op);
1275 }
1276 }
1277 else {
1278 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
1279 bke::SpanAttributeWriter<bool> select_vert = attributes.lookup_or_add_for_write_span<bool>(
1280 ".select_vert", bke::AttrDomain::Point);
1281
1283 data.select_vert = select_vert.span;
1284
1285 view3d_userdata_lassoselect_init(&data.lasso_data, vc, &rect, mcoords, sel_op);
1286
1288
1291
1292 changed |= data.lasso_data.is_changed;
1293 select_vert.finish();
1294 }
1295
1296 if (changed) {
1297 if (SEL_OP_CAN_DESELECT(sel_op)) {
1299 }
1302 }
1303
1304 return changed;
1305}
1307 wmGenericUserData *wm_userdata,
1308 const Span<int2> mcoords,
1309 const eSelectOp sel_op)
1310{
1311 Object *ob = vc->obact;
1312 Mesh *mesh = static_cast<Mesh *>(ob->data);
1313 rcti rect;
1314
1315 if (mesh == nullptr || mesh->faces_num == 0) {
1316 return false;
1317 }
1318
1319 bool changed = false;
1320 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1321 /* flush selection at the end */
1322 changed |= paintface_deselect_all_visible(vc->C, ob, SEL_DESELECT, false);
1323 }
1324
1325 BLI_lasso_boundbox(&rect, mcoords);
1326
1327 EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
1328 if (esel == nullptr) {
1330 esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
1332 vc->depsgraph, vc->region, vc->v3d, mcoords, &rect, nullptr);
1333 }
1334
1335 if (esel->select_bitmap) {
1336 changed |= edbm_backbuf_check_and_select_faces_obmode(mesh, esel, sel_op);
1337 }
1338
1339 if (changed) {
1340 paintface_flush_flags(vc->C, ob, true, false);
1341 }
1342 return changed;
1343}
1344
1346 ViewContext *vc,
1347 const Span<int2> mcoords,
1348 const eSelectOp sel_op)
1349{
1350 using namespace blender;
1352 bool changed_multi = false;
1353
1354 wmGenericUserData wm_userdata_buf = {nullptr, nullptr, false};
1355 wmGenericUserData *wm_userdata = &wm_userdata_buf;
1356
1357 if (vc->obedit == nullptr) { /* Object Mode */
1359 changed_multi |= do_lasso_select_paintface(vc, wm_userdata, mcoords, sel_op);
1360 }
1361 else if (BKE_paint_select_vert_test(ob)) {
1362 changed_multi |= do_lasso_select_paintvert(vc, wm_userdata, mcoords, sel_op);
1363 }
1365 changed_multi |= do_lasso_select_grease_pencil(vc, mcoords, sel_op);
1366 }
1367 else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT)) {
1368 changed_multi |= PE_lasso_select(C,
1369 reinterpret_cast<const int(*)[2]>(mcoords.data()),
1370 mcoords.size(),
1371 sel_op) != OPERATOR_CANCELLED;
1372 }
1373 else if (ob &&
1374 ((ob->mode & OB_MODE_POSE) | ((ob->mode & OB_MODE_WEIGHT_PAINT) &&
1376 {
1377 changed_multi |= do_lasso_select_pose(vc, mcoords, sel_op);
1378 if (changed_multi) {
1380 }
1381 }
1382 else if (ob &&
1384 {
1385 /* pass */
1386 }
1387 else {
1388 changed_multi |= do_lasso_select_objects(vc, mcoords, sel_op);
1389 if (changed_multi) {
1391 }
1392 }
1393 }
1394 else { /* Edit Mode */
1395 if (vc->obedit->type == OB_MESH) {
1397 }
1398
1399 FOREACH_OBJECT_IN_MODE_BEGIN (vc->scene, vc->view_layer, vc->v3d, ob->type, ob->mode, ob_iter)
1400 {
1402 bool changed = false;
1403
1404 switch (vc->obedit->type) {
1405 case OB_MESH:
1406 changed = do_lasso_select_mesh(vc, wm_userdata, mcoords, sel_op);
1407 break;
1408 case OB_CURVES_LEGACY:
1409 case OB_SURF:
1410 changed = do_lasso_select_curve(vc, mcoords, sel_op);
1411 break;
1412 case OB_LATTICE:
1413 changed = do_lasso_select_lattice(vc, mcoords, sel_op);
1414 break;
1415 case OB_ARMATURE:
1416 changed = do_lasso_select_armature(vc, mcoords, sel_op);
1417 if (changed) {
1419 }
1420 break;
1421 case OB_MBALL:
1422 changed = do_lasso_select_meta(vc, mcoords, sel_op);
1423 break;
1424 case OB_CURVES: {
1425 Curves &curves_id = *static_cast<Curves *>(vc->obedit->data);
1426 bke::CurvesGeometry &curves = curves_id.geometry.wrap();
1428 bke::crazyspace::get_evaluated_curves_deformation(*vc->depsgraph, *vc->obedit);
1429 const bke::AttrDomain selection_domain = bke::AttrDomain(curves_id.selection_domain);
1430 const IndexRange elements(curves.attributes().domain_size(selection_domain));
1431 const float4x4 projection = ED_view3d_ob_project_mat_get(vc->rv3d, vc->obedit);
1432 changed = ed::curves::select_lasso(*vc,
1433 curves,
1434 deformation,
1435 projection,
1436 elements,
1437 elements,
1438 selection_domain,
1439 mcoords,
1440 sel_op);
1441 if (changed) {
1442 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
1443 * generic attribute for now. */
1444 DEG_id_tag_update(static_cast<ID *>(vc->obedit->data), ID_RECALC_GEOMETRY);
1446 }
1447 break;
1448 }
1449 case OB_GREASE_PENCIL: {
1450 changed = do_lasso_select_grease_pencil(vc, mcoords, sel_op);
1451 break;
1452 }
1453 default:
1454 BLI_assert_msg(0, "lasso select on incorrect object type");
1455 break;
1456 }
1457
1458 if (changed) {
1459 DEG_id_tag_update(static_cast<ID *>(vc->obedit->data), ID_RECALC_SELECT);
1461 changed_multi = true;
1462 }
1463 }
1465 }
1466
1467 WM_generic_user_data_free(wm_userdata);
1468
1469 return changed_multi;
1470}
1471
1472/* lasso operator gives properties, but since old code works
1473 * with short array we convert */
1475{
1477 if (mcoords.is_empty()) {
1478 return OPERATOR_PASS_THROUGH;
1479 }
1480
1484
1485 /* setup view context for argument to callbacks */
1487
1488 eSelectOp sel_op = static_cast<eSelectOp>(RNA_enum_get(op->ptr, "mode"));
1489 bool changed_multi = view3d_lasso_select(C, &vc, mcoords, sel_op);
1490
1491 if (changed_multi) {
1492 return OPERATOR_FINISHED;
1493 }
1494 return OPERATOR_CANCELLED;
1495}
1496
1498{
1499 ot->name = "Lasso Select";
1500 ot->description = "Select items using lasso selection";
1501 ot->idname = "VIEW3D_OT_select_lasso";
1502
1508
1509 /* flags */
1511
1512 /* properties */
1515}
1516
1519/* -------------------------------------------------------------------- */
1523/* The max number of menu items in an object select menu */
1530
1531#define SEL_MENU_SIZE 22
1533
1534/* special (crappy) operator only for menu select */
1536 PointerRNA * /*ptr*/,
1537 PropertyRNA * /*prop*/,
1538 bool *r_free)
1539{
1540 EnumPropertyItem *item = nullptr, item_tmp = {0};
1541 int totitem = 0;
1542 int i = 0;
1543
1544 /* Don't need context but avoid API doc-generation using this. */
1545 if (C == nullptr || object_mouse_select_menu_data[i].idname[0] == '\0') {
1547 }
1548
1549 for (; i < SEL_MENU_SIZE && object_mouse_select_menu_data[i].idname[0] != '\0'; i++) {
1551 item_tmp.identifier = object_mouse_select_menu_data[i].idname;
1552 item_tmp.value = i;
1553 item_tmp.icon = object_mouse_select_menu_data[i].icon;
1554 RNA_enum_item_add(&item, &totitem, &item_tmp);
1555 }
1556
1557 RNA_enum_item_end(&item, &totitem);
1558 *r_free = true;
1559
1560 return item;
1561}
1562
1564{
1565 const int name_index = RNA_enum_get(op->ptr, "name");
1566 const bool extend = RNA_boolean_get(op->ptr, "extend");
1567 const bool deselect = RNA_boolean_get(op->ptr, "deselect");
1568 const bool toggle = RNA_boolean_get(op->ptr, "toggle");
1569 bool changed = false;
1570 const char *name = object_mouse_select_menu_data[name_index].idname;
1571
1572 View3D *v3d = CTX_wm_view3d(C);
1573 Scene *scene = CTX_data_scene(C);
1574 ViewLayer *view_layer = CTX_data_view_layer(C);
1575 BKE_view_layer_synced_ensure(scene, view_layer);
1576 const Base *oldbasact = BKE_view_layer_active_base_get(view_layer);
1577
1578 Base *basact = nullptr;
1579 CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
1580 /* This is a bit dodgy, there should only be ONE object with this name,
1581 * but library objects can mess this up. */
1582 if (STREQ(name, base->object->id.name + 2)) {
1583 basact = base;
1584 break;
1585 }
1586 }
1588
1589 if (basact == nullptr) {
1590 return OPERATOR_CANCELLED;
1591 }
1592 UNUSED_VARS_NDEBUG(v3d);
1593 BLI_assert(BASE_SELECTABLE(v3d, basact));
1594
1595 if (extend) {
1597 changed = true;
1598 }
1599 else if (deselect) {
1601 changed = true;
1602 }
1603 else if (toggle) {
1604 if (basact->flag & BASE_SELECTED) {
1605 if (basact == oldbasact) {
1607 changed = true;
1608 }
1609 }
1610 else {
1612 changed = true;
1613 }
1614 }
1615 else {
1616 object_deselect_all_except(scene, view_layer, basact);
1618 changed = true;
1619 }
1620
1621 if (oldbasact != basact) {
1623 }
1624
1625 /* weak but ensures we activate menu again before using the enum */
1627
1628 /* undo? */
1629 if (changed) {
1630 Scene *scene = CTX_data_scene(C);
1633
1635
1636 return OPERATOR_FINISHED;
1637 }
1638 return OPERATOR_CANCELLED;
1639}
1640
1642{
1643 PropertyRNA *prop;
1644
1645 /* identifiers */
1646 ot->name = "Select Menu";
1647 ot->description = "Menu object selection";
1648 ot->idname = "VIEW3D_OT_select_menu";
1649
1650 /* api callbacks */
1653
1654 /* flags */
1656
1657 /* #Object.id.name to select (dynamic enum). */
1658 prop = RNA_def_enum(ot->srna, "name", rna_enum_dummy_NULL_items, 0, "Object Name", "");
1661 ot->prop = prop;
1662
1663 prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "");
1665 prop = RNA_def_boolean(ot->srna, "deselect", false, "Deselect", "");
1667 prop = RNA_def_boolean(ot->srna, "toggle", false, "Toggle", "");
1669}
1670
1675 const ViewContext *vc,
1676 const blender::Span<GPUSelectResult> hit_results,
1677 const int mval[2],
1679 Base **r_basact)
1680{
1681
1682 const float mval_fl[2] = {float(mval[0]), float(mval[1])};
1683 /* Distance from object center to use for selection. */
1684 const float dist_threshold_sq = square_f(15 * U.pixelsize);
1685 int base_count = 0;
1686
1687 struct BaseRefWithDepth {
1688 BaseRefWithDepth *next, *prev;
1689 Base *base;
1691 uint depth_id;
1692 };
1693 ListBase base_ref_list = {nullptr, nullptr}; /* List of #BaseRefWithDepth. */
1694
1695 /* handle base->object->select_id */
1696 CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
1697 bool ok = false;
1698 uint depth_id;
1699
1700 /* two selection methods, the CTRL select uses max dist of 15 */
1701 if (!hit_results.is_empty()) {
1702 for (const GPUSelectResult &hit_result : hit_results) {
1703 /* index was converted */
1704 if (base->object->runtime->select_id == (hit_result.id & ~0xFFFF0000)) {
1705 ok = true;
1706 depth_id = hit_result.depth;
1707 break;
1708 }
1709 }
1710 }
1711 else {
1712 float region_co[2];
1713 if (ED_view3d_project_base(vc->region, base, region_co) == V3D_PROJ_RET_OK) {
1714 const float dist_test_sq = len_squared_v2v2(mval_fl, region_co);
1715 if (dist_test_sq < dist_threshold_sq) {
1716 ok = true;
1717 /* Match GPU depth logic, as the float is always positive, it can be sorted as an int. */
1718 depth_id = float_as_uint(dist_test_sq);
1719 }
1720 }
1721 }
1722
1723 if (ok) {
1724 base_count++;
1725 BaseRefWithDepth *base_ref = MEM_cnew<BaseRefWithDepth>(__func__);
1726 base_ref->base = base;
1727 base_ref->depth_id = depth_id;
1728 BLI_addtail(&base_ref_list, (void *)base_ref);
1729 }
1730 }
1732
1733 *r_basact = nullptr;
1734
1735 if (base_count == 0) {
1736 return false;
1737 }
1738 if (base_count == 1) {
1739 Base *base = ((BaseRefWithDepth *)base_ref_list.first)->base;
1740 BLI_freelistN(&base_ref_list);
1741 *r_basact = base;
1742 return false;
1743 }
1744
1745 /* Sort by depth or distance to cursor. */
1746 BLI_listbase_sort(&base_ref_list, [](const void *a, const void *b) {
1747 return int(static_cast<const BaseRefWithDepth *>(a)->depth_id >
1748 static_cast<const BaseRefWithDepth *>(b)->depth_id);
1749 });
1750
1751 while (base_count > SEL_MENU_SIZE) {
1752 BLI_freelinkN(&base_ref_list, base_ref_list.last);
1753 base_count -= 1;
1754 }
1755
1756 /* UI, full in static array values that we later use in an enum function */
1757
1759
1760 int i;
1761 LISTBASE_FOREACH_INDEX (BaseRefWithDepth *, base_ref, &base_ref_list, i) {
1762 Base *base = base_ref->base;
1763 Object *ob = base->object;
1764 const char *name = ob->id.name + 2;
1765
1768 }
1769
1770 wmOperatorType *ot = WM_operatortype_find("VIEW3D_OT_select_menu", false);
1772
1774 RNA_boolean_set(&ptr, "extend", params->sel_op == SEL_OP_ADD);
1775 RNA_boolean_set(&ptr, "deselect", params->sel_op == SEL_OP_SUB);
1776 RNA_boolean_set(&ptr, "toggle", params->sel_op == SEL_OP_XOR);
1779
1780 BLI_freelistN(&base_ref_list);
1781 return true;
1782}
1783
1785{
1786 const int name_index = RNA_enum_get(op->ptr, "name");
1787
1790
1791 View3D *v3d = CTX_wm_view3d(C);
1792 Scene *scene = CTX_data_scene(C);
1793 ViewLayer *view_layer = CTX_data_view_layer(C);
1794 BKE_view_layer_synced_ensure(scene, view_layer);
1795 const Base *oldbasact = BKE_view_layer_active_base_get(view_layer);
1796
1797 Base *basact = object_mouse_select_menu_data[name_index].base_ptr;
1798
1799 if (basact == nullptr) {
1800 return OPERATOR_CANCELLED;
1801 }
1802
1803 BLI_assert(BASE_SELECTABLE(v3d, basact));
1804
1805 if (basact->object->mode & OB_MODE_EDIT) {
1806 EditBone *ebone = (EditBone *)object_mouse_select_menu_data[name_index].item_ptr;
1808 }
1809 else {
1810 bPoseChannel *pchan = (bPoseChannel *)object_mouse_select_menu_data[name_index].item_ptr;
1812 scene, view_layer, v3d, basact->object, pchan->bone, &params);
1813 }
1814
1815 /* Weak but ensures we activate the menu again before using the enum. */
1817
1818 /* We make the armature selected:
1819 * Not-selected active object in pose-mode won't work well for tools. */
1821
1824
1825 /* In weight-paint, we use selected bone to select vertex-group,
1826 * so don't switch to new active object. */
1827 if (oldbasact) {
1828 if (basact->object->mode & OB_MODE_EDIT) {
1829 /* Pass. */
1830 }
1831 else if (oldbasact->object->mode & OB_MODE_ALL_WEIGHT_PAINT) {
1832 /* Prevent activating.
1833 * Selection causes this to be considered the 'active' pose in weight-paint mode.
1834 * Eventually this limitation may be removed.
1835 * For now, de-select all other pose objects deforming this mesh. */
1836 ED_armature_pose_select_in_wpaint_mode(scene, view_layer, basact);
1837 }
1838 else {
1839 if (oldbasact != basact) {
1841 }
1842 }
1843 }
1844
1845 /* Undo? */
1849
1851
1852 return OPERATOR_FINISHED;
1853}
1854
1856{
1857 PropertyRNA *prop;
1858
1859 /* identifiers */
1860 ot->name = "Select Menu";
1861 ot->description = "Menu bone selection";
1862 ot->idname = "VIEW3D_OT_bone_select_menu";
1863
1864 /* api callbacks */
1867
1868 /* flags */
1870
1871 /* #Object.id.name to select (dynamic enum). */
1872 prop = RNA_def_enum(ot->srna, "name", rna_enum_dummy_NULL_items, 0, "Bone Name", "");
1875 ot->prop = prop;
1876
1877 prop = RNA_def_boolean(ot->srna, "extend", false, "Extend", "");
1879 prop = RNA_def_boolean(ot->srna, "deselect", false, "Deselect", "");
1881 prop = RNA_def_boolean(ot->srna, "toggle", false, "Toggle", "");
1883}
1884
1889 const blender::Span<GPUSelectResult> hit_results,
1890 const bool is_editmode,
1892{
1893 int bone_count = 0;
1894
1895 struct BoneRefWithDepth {
1896 BoneRefWithDepth *next, *prev;
1897 Base *base;
1898 union {
1899 EditBone *ebone;
1900 bPoseChannel *pchan;
1901 void *bone_ptr;
1902 };
1904 uint depth_id;
1905 };
1906 ListBase bone_ref_list = {nullptr, nullptr};
1907
1908 GSet *added_bones = BLI_gset_ptr_new("Bone mouse select menu");
1909
1910 /* Select logic taken from #ed_armature_pick_bone_from_selectbuffer_impl
1911 * in `armature_select.cc`. */
1912 for (const GPUSelectResult &hit_result : hit_results) {
1913 void *bone_ptr = nullptr;
1914 Base *bone_base = nullptr;
1915 uint hitresult = hit_result.id;
1916
1917 if (!(hitresult & BONESEL_ANY)) {
1918 /* To avoid including objects in selection. */
1919 continue;
1920 }
1921
1922 hitresult &= ~BONESEL_ANY;
1923 const uint hit_object = hitresult & 0xFFFF;
1924
1925 /* Find the hit bone base (armature object). */
1926 CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
1927 if (base->object->runtime->select_id == hit_object) {
1928 bone_base = base;
1929 break;
1930 }
1931 }
1933
1934 if (!bone_base) {
1935 continue;
1936 }
1937
1938 /* Determine what the current bone is */
1939 if (is_editmode) {
1940 const uint hit_bone = (hitresult & ~BONESEL_ANY) >> 16;
1941 bArmature *arm = static_cast<bArmature *>(bone_base->object->data);
1942 EditBone *ebone = static_cast<EditBone *>(BLI_findlink(arm->edbo, hit_bone));
1943 if (ebone && !(ebone->flag & BONE_UNSELECTABLE)) {
1944 bone_ptr = ebone;
1945 }
1946 }
1947 else {
1948 const uint hit_bone = (hitresult & ~BONESEL_ANY) >> 16;
1949 bPoseChannel *pchan = static_cast<bPoseChannel *>(
1950 BLI_findlink(&bone_base->object->pose->chanbase, hit_bone));
1951 if (pchan && !(pchan->bone->flag & BONE_UNSELECTABLE)) {
1952 bone_ptr = pchan;
1953 }
1954 }
1955
1956 if (!bone_ptr) {
1957 continue;
1958 }
1959 /* We can hit a bone multiple times, so make sure we are not adding an already included bone
1960 * to the list. */
1961 const bool is_duplicate_bone = BLI_gset_haskey(added_bones, bone_ptr);
1962
1963 if (!is_duplicate_bone) {
1964 bone_count++;
1965 BoneRefWithDepth *bone_ref = MEM_cnew<BoneRefWithDepth>(__func__);
1966 bone_ref->base = bone_base;
1967 bone_ref->bone_ptr = bone_ptr;
1968 bone_ref->depth_id = hit_result.depth;
1969 BLI_addtail(&bone_ref_list, (void *)bone_ref);
1970
1971 BLI_gset_insert(added_bones, bone_ptr);
1972 }
1973 }
1974
1975 BLI_gset_free(added_bones, nullptr);
1976
1977 if (bone_count == 0) {
1978 return false;
1979 }
1980 if (bone_count == 1) {
1981 BLI_freelistN(&bone_ref_list);
1982 return false;
1983 }
1984
1985 /* Sort by depth or distance to cursor. */
1986 BLI_listbase_sort(&bone_ref_list, [](const void *a, const void *b) {
1987 return int(static_cast<const BoneRefWithDepth *>(a)->depth_id >
1988 static_cast<const BoneRefWithDepth *>(b)->depth_id);
1989 });
1990
1991 while (bone_count > SEL_MENU_SIZE) {
1992 BLI_freelinkN(&bone_ref_list, bone_ref_list.last);
1993 bone_count -= 1;
1994 }
1995
1996 /* UI, full in static array values that we later use in an enum function */
1998
1999 int i;
2000 LISTBASE_FOREACH_INDEX (BoneRefWithDepth *, bone_ref, &bone_ref_list, i) {
2001 char *name;
2002
2003 object_mouse_select_menu_data[i].base_ptr = bone_ref->base;
2004
2005 if (is_editmode) {
2006 EditBone *ebone = bone_ref->ebone;
2007 object_mouse_select_menu_data[i].item_ptr = static_cast<void *>(ebone);
2008 name = ebone->name;
2009 }
2010 else {
2011 bPoseChannel *pchan = bone_ref->pchan;
2012 object_mouse_select_menu_data[i].item_ptr = static_cast<void *>(pchan);
2013 name = pchan->name;
2014 }
2015
2017 object_mouse_select_menu_data[i].icon = ICON_BONE_DATA;
2018 }
2019
2020 wmOperatorType *ot = WM_operatortype_find("VIEW3D_OT_bone_select_menu", false);
2022
2024 RNA_boolean_set(&ptr, "extend", params->sel_op == SEL_OP_ADD);
2025 RNA_boolean_set(&ptr, "deselect", params->sel_op == SEL_OP_SUB);
2026 RNA_boolean_set(&ptr, "toggle", params->sel_op == SEL_OP_XOR);
2029
2030 BLI_freelistN(&bone_ref_list);
2031 return true;
2032}
2033
2035{
2036 for (const GPUSelectResult &hit_result : hit_results) {
2037 if (hit_result.id & 0xFFFF0000) {
2038 return true;
2039 }
2040 }
2041 return false;
2042}
2043
2044/* utility function for mixed_bones_object_selectbuffer */
2046 const int hits15)
2047{
2048 return hits15;
2049}
2050
2052 const int hits15,
2053 const int hits9)
2054{
2055 const int ofs = hits15;
2056 /* Shift results to beginning. */
2057 hit_results.slice(0, hits9).copy_from(hit_results.slice(ofs, hits9));
2058 return hits9;
2059}
2060
2062 const int hits15,
2063 const int hits9,
2064 const int hits5)
2065{
2066 const int ofs = hits15 + hits9;
2067 /* Shift results to beginning. */
2068 hit_results.slice(0, hits5).copy_from(hit_results.slice(ofs, hits5));
2069 return hits5;
2070}
2071
2081 GPUSelectBuffer *buffer,
2082 const int mval[2],
2083 eV3DSelectObjectFilter select_filter,
2084 bool do_nearest,
2085 bool do_nearest_xray_if_supported,
2086 const bool do_material_slot_selection)
2087{
2088 rcti rect;
2089 int hits15, hits9 = 0, hits5 = 0;
2090 bool has_bones15 = false, has_bones9 = false, has_bones5 = false;
2091
2093 int hits = 0;
2094
2095 if (do_nearest_xray_if_supported) {
2096 if ((U.gpu_flag & USER_GPU_FLAG_NO_DEPT_PICK) == 0) {
2097 select_mode = VIEW3D_SELECT_PICK_ALL;
2098 }
2099 }
2100 /* WORKAROUND: GPU depth picking is not working on AMD/NVIDIA official Vulkan drivers. */
2103 {
2104 select_mode = VIEW3D_SELECT_PICK_ALL;
2105 }
2106
2107 /* we _must_ end cache before return, use 'goto finally' */
2109
2110 GPUSelectStorage &storage = buffer->storage;
2111 BLI_rcti_init_pt_radius(&rect, mval, 14);
2112 hits15 = view3d_opengl_select_ex(
2113 vc, buffer, &rect, select_mode, select_filter, do_material_slot_selection);
2114 if (hits15 == 1) {
2115 hits = selectbuffer_ret_hits_15(storage.as_mutable_span(), hits15);
2116 goto finally;
2117 }
2118 else if (hits15 > 0) {
2119 int ofs;
2120 has_bones15 = selectbuffer_has_bones(storage.as_span().slice(0, hits15));
2121
2122 ofs = hits15;
2123 BLI_rcti_init_pt_radius(&rect, mval, 9);
2124 hits9 = view3d_opengl_select(vc, buffer, &rect, select_mode, select_filter);
2125 if (hits9 == 1) {
2126 hits = selectbuffer_ret_hits_9(storage.as_mutable_span(), hits15, hits9);
2127 goto finally;
2128 }
2129 else if (hits9 > 0) {
2130 has_bones9 = selectbuffer_has_bones(storage.as_span().slice(ofs, hits9));
2131
2132 ofs += hits9;
2133 BLI_rcti_init_pt_radius(&rect, mval, 5);
2134 hits5 = view3d_opengl_select(vc, buffer, &rect, select_mode, select_filter);
2135 if (hits5 == 1) {
2136 hits = selectbuffer_ret_hits_5(storage.as_mutable_span(), hits15, hits9, hits5);
2137 goto finally;
2138 }
2139 else if (hits5 > 0) {
2140 has_bones5 = selectbuffer_has_bones(storage.as_span().slice(ofs, hits5));
2141 }
2142 }
2143
2144 if (has_bones5) {
2145 hits = selectbuffer_ret_hits_5(storage.as_mutable_span(), hits15, hits9, hits5);
2146 goto finally;
2147 }
2148 else if (has_bones9) {
2149 hits = selectbuffer_ret_hits_9(storage.as_mutable_span(), hits15, hits9);
2150 goto finally;
2151 }
2152 else if (has_bones15) {
2153 hits = selectbuffer_ret_hits_15(storage.as_mutable_span(), hits15);
2154 goto finally;
2155 }
2156
2157 if (hits5 > 0) {
2158 hits = selectbuffer_ret_hits_5(storage.as_mutable_span(), hits15, hits9, hits5);
2159 goto finally;
2160 }
2161 else if (hits9 > 0) {
2162 hits = selectbuffer_ret_hits_9(storage.as_mutable_span(), hits15, hits9);
2163 goto finally;
2164 }
2165 else {
2166 hits = selectbuffer_ret_hits_15(storage.as_mutable_span(), hits15);
2167 goto finally;
2168 }
2169 }
2170
2171finally:
2173 return hits;
2174}
2175
2177 GPUSelectBuffer *buffer,
2178 const int mval[2],
2179 eV3DSelectObjectFilter select_filter,
2180 bool use_cycle,
2181 bool enumerate,
2182 bool *r_do_nearest)
2183{
2184 bool do_nearest = false;
2185 View3D *v3d = vc->v3d;
2186
2187 /* define if we use solid nearest select or not */
2188 if (use_cycle) {
2189 /* Update the coordinates (even if the return value isn't used). */
2190 const bool has_motion = WM_cursor_test_motion_and_update(mval);
2191 if (!XRAY_ACTIVE(v3d)) {
2192 do_nearest = has_motion;
2193 }
2194 }
2195 else {
2196 if (!XRAY_ACTIVE(v3d)) {
2197 do_nearest = true;
2198 }
2199 }
2200
2201 if (r_do_nearest) {
2202 *r_do_nearest = do_nearest;
2203 }
2204
2205 do_nearest = do_nearest && !enumerate;
2206
2208 vc, buffer, mval, select_filter, do_nearest, true, false);
2209
2210 return hits;
2211}
2212
2217static int gpu_select_buffer_depth_id_cmp(const void *sel_a_p, const void *sel_b_p)
2218{
2219 GPUSelectResult *a = (GPUSelectResult *)sel_a_p;
2220 GPUSelectResult *b = (GPUSelectResult *)sel_b_p;
2221
2222 if (a->depth < b->depth) {
2223 return -1;
2224 }
2225 if (a->depth > b->depth) {
2226 return 1;
2227 }
2228
2229 /* Depths match, sort by id. */
2230 uint sel_a = a->id;
2231 uint sel_b = b->id;
2232
2233#ifdef __BIG_ENDIAN__
2236#endif
2237
2238 if (sel_a < sel_b) {
2239 return -1;
2240 }
2241 if (sel_a > sel_b) {
2242 return 1;
2243 }
2244 return 0;
2245}
2246
2255 const GPUSelectBuffer &buffer,
2256 int hits,
2257 bool do_nearest,
2258 bool has_bones,
2259 bool do_bones_get_priotity,
2260 int *r_select_id_subelem)
2261{
2262 Scene *scene = vc->scene;
2263 ViewLayer *view_layer = vc->view_layer;
2264 View3D *v3d = vc->v3d;
2265 int a;
2266
2267 bool found = false;
2268 int select_id = 0;
2269 int select_id_subelem = 0;
2270
2271 if (do_nearest) {
2272 uint min = 0xFFFFFFFF;
2273 int hit_index = -1;
2274
2275 if (has_bones && do_bones_get_priotity) {
2276 /* we skip non-bone hits */
2277 for (a = 0; a < hits; a++) {
2278 if (min > buffer.storage[a].depth && (buffer.storage[a].id & 0xFFFF0000)) {
2279 min = buffer.storage[a].depth;
2280 hit_index = a;
2281 }
2282 }
2283 }
2284 else {
2285
2286 for (a = 0; a < hits; a++) {
2287 /* Any object. */
2288 if (min > buffer.storage[a].depth) {
2289 min = buffer.storage[a].depth;
2290 hit_index = a;
2291 }
2292 }
2293 }
2294
2295 if (hit_index != -1) {
2296 select_id = buffer.storage[hit_index].id & 0xFFFF;
2297 select_id_subelem = (buffer.storage[hit_index].id & 0xFFFF0000) >> 16;
2298 found = true;
2299 /* No need to set `min` to `buffer.storage[hit_index].depth`, it's not used from now on. */
2300 }
2301 }
2302 else {
2303
2304 GPUSelectStorage buffer_sorted = buffer.storage;
2305 {
2306 buffer_sorted.resize(hits);
2307 /* Remove non-bone objects. */
2308 if (has_bones && do_bones_get_priotity) {
2309 /* Loop backwards to reduce re-ordering. */
2310 for (a = hits - 1; a >= 0; a--) {
2311 if ((buffer_sorted[a].id & 0xFFFF0000) == 0) {
2312 buffer_sorted[a] = buffer_sorted[--hits];
2313 }
2314 }
2315 }
2316 qsort(buffer_sorted.data(), hits, sizeof(GPUSelectResult), gpu_select_buffer_depth_id_cmp);
2317 }
2318
2319 int hit_index = -1;
2320
2321 /* It's possible there are no hits (all objects contained bones). */
2322 if (hits > 0) {
2323 /* Only exclude active object when it is selected. */
2324 BKE_view_layer_synced_ensure(scene, view_layer);
2325 Base *base = BKE_view_layer_active_base_get(view_layer);
2326 if (base && (base->flag & BASE_SELECTED)) {
2327 const int select_id_active = base->object->runtime->select_id;
2328 for (int i_next = 0, i_prev = hits - 1; i_next < hits; i_prev = i_next++) {
2329 if ((select_id_active == (buffer_sorted[i_prev].id & 0xFFFF)) &&
2330 (select_id_active != (buffer_sorted[i_next].id & 0xFFFF)))
2331 {
2332 hit_index = i_next;
2333 break;
2334 }
2335 }
2336 }
2337
2338 /* When the active object is unselected or not in `buffer`, use the nearest. */
2339 if (hit_index == -1) {
2340 /* Just pick the nearest. */
2341 hit_index = 0;
2342 }
2343 }
2344
2345 if (hit_index != -1) {
2346 select_id = buffer_sorted[hit_index].id & 0xFFFF;
2347 select_id_subelem = (buffer_sorted[hit_index].id & 0xFFFF0000) >> 16;
2348 found = true;
2349 }
2350 }
2351
2352 Base *basact = nullptr;
2353 if (found) {
2354 BKE_view_layer_synced_ensure(scene, view_layer);
2356 if (has_bones ? BASE_VISIBLE(v3d, base) : BASE_SELECTABLE(v3d, base)) {
2357 if (base->object->runtime->select_id == select_id) {
2358 basact = base;
2359 break;
2360 }
2361 }
2362 }
2363
2364 if (basact && r_select_id_subelem) {
2365 *r_select_id_subelem = select_id_subelem;
2366 }
2367 }
2368
2369 return basact;
2370}
2371
2372static Base *mouse_select_object_center(const ViewContext *vc, Base *startbase, const int mval[2])
2373{
2374 ARegion *region = vc->region;
2375 Scene *scene = vc->scene;
2376 ViewLayer *view_layer = vc->view_layer;
2377 View3D *v3d = vc->v3d;
2378
2379 BKE_view_layer_synced_ensure(scene, view_layer);
2380 Base *oldbasact = BKE_view_layer_active_base_get(view_layer);
2381
2382 const float mval_fl[2] = {float(mval[0]), float(mval[1])};
2383 float dist = ED_view3d_select_dist_px() * 1.3333f;
2384 Base *basact = nullptr;
2385
2386 /* Put the active object at a disadvantage to cycle through other objects. */
2387 const float penalty_dist = 10.0f * UI_SCALE_FAC;
2388 Base *base = startbase;
2389 while (base) {
2390 if (BASE_SELECTABLE(v3d, base)) {
2391 float screen_co[2];
2393 base->object->object_to_world().location(),
2394 screen_co,
2396 {
2397 float dist_test = len_manhattan_v2v2(mval_fl, screen_co);
2398 if (base == oldbasact) {
2399 dist_test += penalty_dist;
2400 }
2401 if (dist_test < dist) {
2402 dist = dist_test;
2403 basact = base;
2404 }
2405 }
2406 }
2407 base = base->next;
2408
2409 if (base == nullptr) {
2410 base = static_cast<Base *>(BKE_view_layer_object_bases_get(view_layer)->first);
2411 }
2412 if (base == startbase) {
2413 break;
2414 }
2415 }
2416 return basact;
2417}
2418
2420 const int mval[2],
2421 int *r_material_slot)
2422{
2424 Base *basact = nullptr;
2425 GPUSelectBuffer buffer;
2426
2427 /* setup view context for argument to callbacks */
2430
2432
2433 const bool do_nearest = !XRAY_ACTIVE(vc.v3d);
2434 const bool do_material_slot_selection = r_material_slot != nullptr;
2435 const int hits = mixed_bones_object_selectbuffer(
2436 &vc, &buffer, mval, VIEW3D_SELECT_FILTER_NOP, do_nearest, false, do_material_slot_selection);
2437
2438 if (hits > 0) {
2439 const bool has_bones = (r_material_slot == nullptr) &&
2440 selectbuffer_has_bones(buffer.storage.as_span().slice(0, hits));
2441 basact = mouse_select_eval_buffer(
2442 &vc, buffer, hits, do_nearest, has_bones, true, r_material_slot);
2443 }
2444
2445 return basact;
2446}
2447
2449{
2450 return ed_view3d_give_base_under_cursor_ex(C, mval, nullptr);
2451}
2452
2454{
2455 Base *base = ED_view3d_give_base_under_cursor(C, mval);
2456 if (base) {
2457 return base->object;
2458 }
2459 return nullptr;
2460}
2461
2463 const int mval[2],
2464 int *r_material_slot)
2465{
2466 Base *base = ed_view3d_give_base_under_cursor_ex(C, mval, r_material_slot);
2467 if (base) {
2468 return base->object;
2469 }
2470 return nullptr;
2471}
2472
2474{
2475 return ED_view3d_give_object_under_cursor(C, mval) != nullptr;
2476}
2477
2479{
2480 LISTBASE_FOREACH (MovieTrackingObject *, tracking_object, &tracking->objects) {
2481 LISTBASE_FOREACH (MovieTrackingTrack *, track, &tracking_object->tracks) {
2483 }
2484 }
2485}
2486
2488 Scene *scene,
2489 Base *basact,
2490 MovieClip *clip,
2491 const GPUSelectBuffer &buffer,
2492 const short hits,
2494{
2495 bool changed = false;
2496 bool found = false;
2497
2498 MovieTracking *tracking = &clip->tracking;
2499 ListBase *tracksbase = nullptr;
2500 MovieTrackingTrack *track = nullptr;
2501
2502 for (int i = 0; i < hits; i++) {
2503 const int hitresult = buffer.storage[i].id;
2504
2505 /* If there's bundles in buffer select bundles first,
2506 * so non-camera elements should be ignored in buffer. */
2507 if (basact->object->runtime->select_id != (hitresult & 0xFFFF)) {
2508 continue;
2509 }
2510 /* Index of bundle is 1<<16-based. if there's no "bone" index
2511 * in height word, this buffer value belongs to camera. not to bundle. */
2512 if ((hitresult & 0xFFFF0000) == 0) {
2513 continue;
2514 }
2515
2517 &clip->tracking, hitresult >> 16, &tracksbase);
2518 found = true;
2519 break;
2520 }
2521
2522 /* Note `params->deselect_all` is ignored for tracks as in this case
2523 * all objects will be de-selected (not tracks). */
2524 if (params->sel_op == SEL_OP_SET) {
2525 if ((found && params->select_passthrough) && TRACK_SELECTED(track)) {
2526 found = false;
2527 }
2528 else if (found /* `|| params->deselect_all` */) {
2529 /* Deselect everything. */
2530 deselect_all_tracks(tracking);
2531 changed = true;
2532 }
2533 }
2534
2535 if (found) {
2536 switch (params->sel_op) {
2537 case SEL_OP_ADD: {
2538 BKE_tracking_track_select(tracksbase, track, TRACK_AREA_ALL, true);
2539 break;
2540 }
2541 case SEL_OP_SUB: {
2543 break;
2544 }
2545 case SEL_OP_XOR: {
2546 if (TRACK_SELECTED(track)) {
2548 }
2549 else {
2550 BKE_tracking_track_select(tracksbase, track, TRACK_AREA_ALL, true);
2551 }
2552 break;
2553 }
2554 case SEL_OP_SET: {
2555 BKE_tracking_track_select(tracksbase, track, TRACK_AREA_ALL, false);
2556 break;
2557 }
2558 case SEL_OP_AND: {
2559 BLI_assert_unreachable(); /* Doesn't make sense for picking. */
2560 break;
2561 }
2562 }
2563
2568
2569 changed = true;
2570 }
2571
2572 return changed || found;
2573}
2574
2588 const int mval[2],
2590 const bool center,
2591 const bool enumerate,
2592 const bool object_only)
2593{
2595 /* Setup view context for argument to callbacks. */
2597
2598 Scene *scene = vc.scene;
2599 View3D *v3d = vc.v3d;
2600
2601 /* Menu activation may find a base to make active (if it only finds a single item to select). */
2602 Base *basact_override = nullptr;
2603
2604 const bool is_obedit = (vc.obedit != nullptr);
2605 if (object_only) {
2606 /* Signal for #view3d_opengl_select to skip edit-mode objects. */
2607 vc.obedit = nullptr;
2608 }
2609
2610 /* Set for GPU depth buffer picking, leave null when selecting by center. */
2611 struct GPUData {
2612 GPUSelectBuffer buffer;
2613 int hits;
2614 bool do_nearest;
2615 bool has_bones;
2616 } *gpu = nullptr;
2617
2618 /* First handle menu selection, early exit if a menu opens
2619 * since this takes ownership of the selection action.
2620 *
2621 * Even when there is no menu `basact_override` may be set to avoid having to re-find
2622 * the item under the cursor. */
2623
2624 if (center == false) {
2625 gpu = MEM_new<GPUData>(__func__);
2626 gpu->do_nearest = false;
2627 gpu->has_bones = false;
2628
2629 /* If objects have pose-mode set, the bones are in the same selection buffer. */
2630 const eV3DSelectObjectFilter select_filter = ((object_only == false) ?
2632 vc.obact) :
2635 &vc, &gpu->buffer, mval, select_filter, true, enumerate, &gpu->do_nearest);
2636 gpu->has_bones = (object_only && gpu->hits > 0) ?
2637 false :
2638 selectbuffer_has_bones(gpu->buffer.storage.as_span().slice(0, gpu->hits));
2639 }
2640
2641 /* First handle menu selection, early exit when a menu was opened.
2642 * Otherwise fall through to regular selection. */
2643 if (enumerate) {
2644 bool has_menu = false;
2645 if (center) {
2646 if (object_mouse_select_menu(C, &vc, {}, mval, params, &basact_override)) {
2647 has_menu = true;
2648 }
2649 }
2650 else {
2651 if (gpu->hits != 0) {
2652 const blender::Span<GPUSelectResult> hit_results = gpu->buffer.storage.as_span().slice(
2653 0, gpu->hits);
2654 if (gpu->has_bones && bone_mouse_select_menu(C, hit_results, false, params)) {
2655 has_menu = true;
2656 }
2657 else if (object_mouse_select_menu(C, &vc, hit_results, mval, params, &basact_override)) {
2658 has_menu = true;
2659 }
2660 }
2661 }
2662
2663 /* Let the menu handle any further actions. */
2664 if (has_menu) {
2665 if (gpu != nullptr) {
2666 MEM_delete(gpu);
2667 }
2668 return false;
2669 }
2670 }
2671
2672 /* No menu, continue with selection. */
2673
2674 ViewLayer *view_layer = vc.view_layer;
2675 BKE_view_layer_synced_ensure(scene, view_layer);
2676 /* Don't set when the context has no active object (hidden), see: #60807. */
2677 const Base *oldbasact = vc.obact ? BKE_view_layer_active_base_get(view_layer) : nullptr;
2678 /* Always start list from `basact` when cycling the selection. */
2679 Base *startbase = (oldbasact && oldbasact->next) ?
2680 oldbasact->next :
2681 static_cast<Base *>(BKE_view_layer_object_bases_get(view_layer)->first);
2682
2683 /* The next object's base to make active. */
2684 Base *basact = nullptr;
2685 const eObjectMode object_mode = oldbasact ? static_cast<eObjectMode>(oldbasact->object->mode) :
2687 /* For the most part this is equivalent to `(object_mode & OB_MODE_POSE) != 0`
2688 * however this logic should also run with weight-paint + pose selection.
2689 * Without this, selection in weight-paint mode can de-select armatures which isn't useful,
2690 * see: #101686. */
2691 const bool has_pose_old = (oldbasact &&
2693
2694 /* When enabled, don't attempt any further selection. */
2695 bool handled = false;
2696
2697 /* Split `changed` into data-types so their associated updates can be properly performed.
2698 * This is also needed as multiple changes may happen at once.
2699 * Selecting a pose-bone or track can also select the object for e.g. */
2700 bool changed_object = false;
2701 bool changed_pose = false;
2702 bool changed_track = false;
2703
2704 /* Handle setting the new base active (even when `handled == true`). */
2705 bool use_activate_selected_base = false;
2706
2707 if (center) {
2708 if (basact_override) {
2709 basact = basact_override;
2710 }
2711 else {
2712 basact = mouse_select_object_center(&vc, startbase, mval);
2713 }
2714 }
2715 else {
2716 if (basact_override) {
2717 basact = basact_override;
2718 }
2719 else {
2720 /* Regarding bone priority.
2721 *
2722 * - When in pose-bone, it's useful that any selection containing a bone
2723 * gets priority over other geometry (background scenery for example).
2724 *
2725 * - When in object-mode, don't prioritize bones as it would cause
2726 * pose-objects behind other objects to get priority
2727 * (mainly noticeable when #SCE_OBJECT_MODE_LOCK is disabled).
2728 *
2729 * This way prioritizing based on pose-mode has a bias to stay in pose-mode
2730 * without having to enforce this through locking the object mode. */
2731 bool do_bones_get_priotity = has_pose_old;
2732
2733 basact = (gpu->hits > 0) ? mouse_select_eval_buffer(&vc,
2734 gpu->buffer,
2735 gpu->hits,
2736 gpu->do_nearest,
2737 gpu->has_bones,
2738 do_bones_get_priotity,
2739 nullptr) :
2740 nullptr;
2741 }
2742
2743 /* See comment for `has_pose_old`, the same rationale applies here. */
2744 const bool has_pose_new = (basact &&
2746
2747 /* Select pose-bones or camera-tracks. */
2748 if (((gpu->hits > 0) && gpu->has_bones) ||
2749 /* Special case, even when there are no hits, pose logic may de-select all bones. */
2750 ((gpu->hits == 0) && has_pose_old))
2751 {
2752
2753 if (basact && (gpu->has_bones && (basact->object->type == OB_CAMERA))) {
2754 MovieClip *clip = BKE_object_movieclip_get(scene, basact->object, false);
2755 if (clip != nullptr) {
2757 C, scene, basact, clip, gpu->buffer, gpu->hits, params))
2758 {
2760 /* Don't set `handled` here as the object activation may be necessary. */
2761 changed_object = true;
2762
2763 changed_track = true;
2764 }
2765 else {
2766 /* Fallback to regular object selection if no new bundles were selected,
2767 * allows to select object parented to reconstruction object. */
2768 basact = mouse_select_eval_buffer(
2769 &vc, gpu->buffer, gpu->hits, gpu->do_nearest, false, false, nullptr);
2770 }
2771 }
2772 }
2774 view_layer,
2775 v3d,
2776 basact ? basact : (Base *)oldbasact,
2777 gpu->buffer.storage.data(),
2778 gpu->hits,
2779 params,
2780 gpu->do_nearest))
2781 {
2782
2783 changed_pose = true;
2784
2785 /* When there is no `baseact` this will have operated on `oldbasact`,
2786 * allowing #SelectPick_Params.deselect_all work in pose-mode.
2787 * In this case no object operations are needed. */
2788 if (basact == nullptr) {
2789 handled = true;
2790 }
2791 else {
2792 /* By convention the armature-object is selected when in pose-mode.
2793 * While leaving it unselected will work, leaving pose-mode would leave the object
2794 * active + unselected which isn't ideal when performing other actions on the object. */
2796 changed_object = true;
2797
2800
2801 /* In weight-paint, we use selected bone to select vertex-group.
2802 * In this case the active object mustn't change as it would leave weight-paint mode. */
2803 if (oldbasact) {
2804 if (oldbasact->object->mode & OB_MODE_ALL_WEIGHT_PAINT) {
2805 /* Prevent activating.
2806 * Selection causes this to be considered the 'active' pose in weight-paint mode.
2807 * Eventually this limitation may be removed.
2808 * For now, de-select all other pose objects deforming this mesh. */
2809 ED_armature_pose_select_in_wpaint_mode(scene, view_layer, basact);
2810
2811 handled = true;
2812 }
2813 else if (has_pose_old && has_pose_new) {
2814 /* Within pose-mode, keep the current selection when switching pose bones,
2815 * this is noticeable when in pose mode with multiple objects at once.
2816 * Where selecting the bone of a different object would de-select this one.
2817 * After that, exiting pose-mode would only have the active armature selected.
2818 * This matches multi-object edit-mode behavior. */
2819 handled = true;
2820
2821 if (oldbasact != basact) {
2822 use_activate_selected_base = true;
2823 }
2824 }
2825 else {
2826 /* Don't set `handled` here as the object selection may be necessary
2827 * when starting out in object-mode and moving into pose-mode,
2828 * when moving from pose to object-mode using object selection also makes sense. */
2829 }
2830 }
2831 }
2832 }
2833 /* Prevent bone/track selecting to pass on to object selecting. */
2834 if (basact == oldbasact) {
2835 handled = true;
2836 }
2837 }
2838 }
2839
2840 bool changed_object_mode = false;
2841
2842 if (handled == false) {
2843 if (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) {
2844 /* No special logic in edit-mode. */
2845 if (is_obedit == false) {
2846 if (basact && !BKE_object_is_mode_compat(basact->object, object_mode)) {
2847 if (object_mode == OB_MODE_OBJECT) {
2848 Main *bmain = vc.bmain;
2849 blender::ed::object::mode_generic_exit(bmain, vc.depsgraph, scene, basact->object);
2850 }
2851 if (!BKE_object_is_mode_compat(basact->object, object_mode)) {
2852 basact = nullptr;
2853 }
2854 }
2855
2856 /* Disallow switching modes,
2857 * special exception for edit-mode - vertex-parent operator. */
2858 if (basact && oldbasact) {
2859 if ((oldbasact->object->mode != basact->object->mode) &&
2860 (oldbasact->object->mode & basact->object->mode) == 0)
2861 {
2862 basact = nullptr;
2863 }
2864 }
2865 }
2866 }
2867 else {
2868 if (basact) {
2869 if (!BKE_object_is_mode_compat(basact->object, object_mode)) {
2870 changed_object_mode = true;
2871 }
2872 }
2873 else if (object_mode != OB_MODE_OBJECT) {
2874 changed_object_mode = true;
2875 }
2876 }
2877 }
2878
2879 /* Ensure code above doesn't change the active base. This code is already fairly involved,
2880 * it's best if changing the active object is localized to a single place. */
2881 BLI_assert(oldbasact == (vc.obact ? BKE_view_layer_active_base_get(view_layer) : nullptr));
2882
2883 if (handled) {
2884 /* Pass. */
2885 }
2886 else if (basact && vc.obedit) {
2887 /* Only do the select (use for setting vertex parents & hooks).
2888 * In edit-mode do not activate. */
2889 object_deselect_all_except(scene, view_layer, basact);
2891
2892 changed_object = true;
2893 }
2894 else {
2895 /* Object-mode (pose mode will have been handled already)
2896 * unless entering pose-mode from object selection (handled by `changed_object_mode`). */
2897
2898 /* NOTE(@ideasman42): When select changes object-mode it doesn't make sense to use
2899 * pass-through.
2900 *
2901 * - When object-mode locking is disabled:
2902 * Selecting another already selected object does not need to make it active,
2903 * allowing a cursor-drag to move this objects as well as other selected objects.
2904 * - When object-mode locking is enabled:
2905 * Selecting an object in a different mode (typically pose-mode) must prioritize
2906 * entering that mode which requires making the object active, further, the selection
2907 * before switching modes wont make sense in the newly entered mode,
2908 * so it makes sense to disable pass-through logic in this case.
2909 *
2910 * See: #115181 for details. */
2911 const bool select_passthrough = params->select_passthrough && (changed_object_mode == false);
2912
2913 bool found = (basact != nullptr) && BASE_SELECTABLE(v3d, basact);
2914 if (params->sel_op == SEL_OP_SET) {
2915 if ((found && select_passthrough) && (basact->flag & BASE_SELECTED)) {
2916 found = false;
2917 }
2918 else if (found || params->deselect_all) {
2919 /* Deselect everything. */
2920 /* `basact` may be nullptr. */
2921 if (object_deselect_all_except(scene, view_layer, basact)) {
2922 changed_object = true;
2923 }
2924 }
2925 }
2926
2927 if (found) {
2928 use_activate_selected_base |= (oldbasact != basact) && (is_obedit == false);
2929
2930 switch (params->sel_op) {
2931 case SEL_OP_ADD: {
2933 break;
2934 }
2935 case SEL_OP_SUB: {
2937 break;
2938 }
2939 case SEL_OP_XOR: {
2940 if (basact->flag & BASE_SELECTED) {
2941 /* Keep selected if the base is to be activated. */
2942 if (use_activate_selected_base == false) {
2944 }
2945 }
2946 else {
2948 }
2949 break;
2950 }
2951 case SEL_OP_SET: {
2952 /* Deselect has already been performed. */
2954 break;
2955 }
2956 case SEL_OP_AND: {
2957 BLI_assert_unreachable(); /* Doesn't make sense for picking. */
2958 break;
2959 }
2960 }
2961
2962 changed_object = true;
2963 }
2964 }
2965
2966 /* Perform the activation even when 'handled', since this is used to ensure
2967 * the object from the pose-bone selected is also activated. */
2968 if (use_activate_selected_base && (basact != nullptr)) {
2969 changed_object = true;
2970 blender::ed::object::base_activate(C, basact); /* adds notifier */
2971 if ((scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) == 0) {
2973 }
2974 }
2975
2976 if (changed_object) {
2979
2981 }
2982
2983 if (changed_pose) {
2985 }
2986
2987 if (gpu != nullptr) {
2988 MEM_delete(gpu);
2989 }
2990
2991 return (changed_object || changed_pose || changed_track);
2992}
2993
3001 const int mval[2],
3003 Object *obact)
3004{
3005 using namespace blender;
3006 View3D *v3d = CTX_wm_view3d(C);
3007 const bool use_zbuf = !XRAY_ENABLED(v3d);
3008
3009 Mesh *mesh = static_cast<Mesh *>(obact->data); /* already checked for nullptr */
3010 uint index = 0;
3011 bool changed = false;
3012
3013 bool found = ED_mesh_pick_vert(C, obact, mval, ED_MESH_PICK_DEFAULT_VERT_DIST, use_zbuf, &index);
3014
3015 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
3016 bke::AttributeWriter<bool> select_vert = attributes.lookup_or_add_for_write<bool>(
3017 ".select_vert", bke::AttrDomain::Point);
3018
3019 if (params->sel_op == SEL_OP_SET) {
3020 if ((found && params->select_passthrough) && select_vert.varray[index]) {
3021 found = false;
3022 }
3023 else if (found || params->deselect_all) {
3024 /* Deselect everything. */
3025 changed |= paintface_deselect_all_visible(C, obact, SEL_DESELECT, false);
3026 }
3027 }
3028
3029 if (found) {
3030 switch (params->sel_op) {
3031 case SEL_OP_ADD: {
3032 select_vert.varray.set(index, true);
3033 break;
3034 }
3035 case SEL_OP_SUB: {
3036 select_vert.varray.set(index, false);
3037 break;
3038 }
3039 case SEL_OP_XOR: {
3040 select_vert.varray.set(index, !select_vert.varray[index]);
3041 break;
3042 }
3043 case SEL_OP_SET: {
3045 select_vert.varray.set(index, true);
3046 break;
3047 }
3048 case SEL_OP_AND: {
3049 BLI_assert_unreachable(); /* Doesn't make sense for picking. */
3050 break;
3051 }
3052 }
3053
3054 /* update mselect */
3055 if (select_vert.varray[index]) {
3057 }
3058 else {
3060 }
3061
3062 select_vert.finish();
3063
3064 paintvert_flush_flags(obact);
3065
3066 changed = true;
3067 }
3068 else {
3069 select_vert.finish();
3070 }
3071
3072 if (changed) {
3074 }
3075
3076 return changed || found;
3077}
3078
3084
3090static bool ed_curves_select_pick(bContext &C, const int mval[2], const SelectPick_Params &params)
3091{
3092 using namespace blender;
3094 /* Setup view context for argument to callbacks. */
3096
3098 vc.scene, vc.view_layer, vc.v3d);
3099
3100 Curves &active_curves_id = *static_cast<Curves *>(vc.obedit->data);
3101 const bke::AttrDomain selection_domain = bke::AttrDomain(active_curves_id.selection_domain);
3102
3103 const ClosestCurveDataBlock closest = threading::parallel_reduce(
3104 bases.index_range(),
3105 1L,
3107 [&](const IndexRange range, const ClosestCurveDataBlock &init) {
3108 ClosestCurveDataBlock new_closest = init;
3109 for (Base *base : bases.as_span().slice(range)) {
3110 Object &curves_ob = *base->object;
3111 Curves &curves_id = *static_cast<Curves *>(curves_ob.data);
3113 bke::crazyspace::get_evaluated_curves_deformation(*vc.depsgraph, curves_ob);
3114 const bke::CurvesGeometry &curves = curves_id.geometry.wrap();
3115 const float4x4 projection = ED_view3d_ob_project_mat_get(vc.rv3d, &curves_ob);
3116 const IndexMask elements(curves.attributes().domain_size(selection_domain));
3117 const auto range_consumer =
3118 [&](IndexRange range, Span<float3> positions, StringRef selection_attribute_name) {
3119 IndexMask mask = elements.slice_content(range);
3120
3121 std::optional<ed::curves::FindClosestData> new_closest_elem =
3122 ed::curves::closest_elem_find_screen_space(vc,
3123 curves.points_by_curve(),
3124 positions,
3125 curves.cyclic(),
3126 projection,
3127 mask,
3128 selection_domain,
3129 mval,
3130 new_closest.elem);
3131 if (new_closest_elem) {
3132 new_closest.selection_attribute_name = selection_attribute_name;
3133 new_closest.elem = *new_closest_elem;
3134 new_closest.curves_id = &curves_id;
3135 }
3136 };
3137
3138 if (selection_domain == bke::AttrDomain::Point) {
3139 ed::curves::foreach_selectable_point_range(curves, deformation, range_consumer);
3140 }
3141 else if (selection_domain == bke::AttrDomain::Curve) {
3142 ed::curves::foreach_selectable_curve_range(curves, deformation, range_consumer);
3143 };
3144 }
3145 return new_closest;
3146 },
3147 [](const ClosestCurveDataBlock &a, const ClosestCurveDataBlock &b) {
3148 return (a.elem.distance < b.elem.distance) ? a : b;
3149 });
3150
3151 std::atomic<bool> deselected = false;
3152 if (params.deselect_all || params.sel_op == SEL_OP_SET) {
3153 threading::parallel_for(bases.index_range(), 1L, [&](const IndexRange range) {
3154 for (Base *base : bases.as_span().slice(range)) {
3155 Curves &curves_id = *static_cast<Curves *>(base->object->data);
3156 bke::CurvesGeometry &curves = curves_id.geometry.wrap();
3157 if (!ed::curves::has_anything_selected(curves, selection_domain)) {
3158 continue;
3159 }
3160
3161 ed::curves::foreach_selection_attribute_writer(
3162 curves, selection_domain, [](bke::GSpanAttributeWriter &selection) {
3163 ed::curves::fill_selection_false(selection.span);
3164 });
3165
3166 deselected = true;
3167 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
3168 * generic attribute for now. */
3169 DEG_id_tag_update(&curves_id.id, ID_RECALC_GEOMETRY);
3170 WM_event_add_notifier(&C, NC_GEOM | ND_DATA, &curves_id);
3171 }
3172 });
3173 }
3174
3175 if (!closest.curves_id) {
3176 return deselected;
3177 }
3178
3179 if (selection_domain == bke::AttrDomain::Point) {
3180 bke::GSpanAttributeWriter selection = ed::curves::ensure_selection_attribute(
3181 closest.curves_id->geometry.wrap(),
3182 bke::AttrDomain::Point,
3184 closest.selection_attribute_name);
3185 ed::curves::apply_selection_operation_at_index(
3186 selection.span, closest.elem.index, params.sel_op);
3187 selection.finish();
3188 }
3189 else if (selection_domain == bke::AttrDomain::Curve) {
3190 ed::curves::foreach_selection_attribute_writer(
3191 closest.curves_id->geometry.wrap(),
3192 bke::AttrDomain::Curve,
3193 [&](bke::GSpanAttributeWriter &selection) {
3194 ed::curves::apply_selection_operation_at_index(
3195 selection.span, closest.elem.index, params.sel_op);
3196 });
3197 }
3198
3199 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
3200 * generic attribute for now. */
3201 DEG_id_tag_update(&closest.curves_id->id, ID_RECALC_GEOMETRY);
3202 WM_event_add_notifier(&C, NC_GEOM | ND_DATA, closest.curves_id);
3203
3204 return true;
3205}
3206
3213
3220 const int mval[2],
3222{
3223 using namespace blender;
3225 /* Setup view context for argument to callbacks. */
3227 Object *object = (vc.obedit ? vc.obedit : vc.obact);
3228
3229 /* Collect editable drawings. */
3230 const Object *ob_eval = DEG_get_evaluated_object(vc.depsgraph, const_cast<Object *>(object));
3231 GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
3233 ed::greasepencil::retrieve_editable_drawings(*vc.scene, grease_pencil);
3234
3235 /* Get selection domain from tool settings. */
3237 vc.scene->toolsettings, object);
3238
3239 const ClosestGreasePencilDrawing closest = threading::parallel_reduce(
3240 drawings.index_range(),
3241 1L,
3243 [&](const IndexRange range, const ClosestGreasePencilDrawing &init) {
3244 ClosestGreasePencilDrawing new_closest = init;
3245 for (const int i : range) {
3246 ed::greasepencil::MutableDrawingInfo info = drawings[i];
3247 const bke::greasepencil::Layer &layer = grease_pencil.layer(info.layer_index);
3248 /* Get deformation by modifiers. */
3250 bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
3251 ob_eval, *object, info.layer_index, info.frame_number);
3252
3253 IndexMaskMemory memory;
3254 const IndexMask elements = ed::greasepencil::retrieve_editable_elements(
3255 *object, info, selection_domain, memory);
3256 if (elements.is_empty()) {
3257 continue;
3258 }
3259 const IndexMask visible_handle_elements =
3260 ed::greasepencil::retrieve_visible_bezier_handle_elements(
3261 *object, info.drawing, info.layer_index, selection_domain, memory);
3262 const bke::CurvesGeometry &curves = info.drawing.strokes();
3263 const float4x4 layer_to_world = layer.to_world_space(*ob_eval);
3265 layer_to_world);
3266 const auto range_consumer = [&](const IndexRange range,
3267 const Span<float3> positions,
3268 const StringRef selection_attribute_name) {
3269 const IndexMask mask = ((selection_attribute_name == ".selection") ?
3270 elements :
3271 visible_handle_elements)
3272 .slice_content(range);
3273
3274 std::optional<ed::curves::FindClosestData> new_closest_elem =
3275 ed::curves::closest_elem_find_screen_space(vc,
3276 curves.points_by_curve(),
3277 positions,
3278 curves.cyclic(),
3279 projection,
3280 mask,
3281 selection_domain,
3282 mval,
3283 new_closest.elem);
3284 if (new_closest_elem) {
3285 new_closest.selection_attribute_name = selection_attribute_name;
3286 new_closest.elem = *new_closest_elem;
3287 new_closest.info_index = i;
3288 new_closest.drawing = &info.drawing;
3289 }
3290 };
3291
3292 if (selection_domain == bke::AttrDomain::Point) {
3293 ed::curves::foreach_selectable_point_range(curves, deformation, range_consumer);
3294 }
3295 else if (selection_domain == bke::AttrDomain::Curve) {
3296 ed::curves::foreach_selectable_curve_range(curves, deformation, range_consumer);
3297 };
3298 }
3299 return new_closest;
3300 },
3302 return (a.elem.distance < b.elem.distance) ? a : b;
3303 });
3304
3305 std::atomic<bool> deselected = false;
3306 if (params.deselect_all || params.sel_op == SEL_OP_SET) {
3307 threading::parallel_for(drawings.index_range(), 1L, [&](const IndexRange range) {
3308 for (const int i : range) {
3309 ed::greasepencil::MutableDrawingInfo info = drawings[i];
3310 IndexMaskMemory memory;
3311 const IndexMask elements = ed::greasepencil::retrieve_editable_elements(
3312 *object, info, selection_domain, memory);
3313 if (elements.is_empty()) {
3314 continue;
3315 }
3316 bke::CurvesGeometry &curves = info.drawing.strokes_for_write();
3317 if (!ed::curves::has_anything_selected(curves, selection_domain, elements)) {
3318 continue;
3319 }
3320
3321 ed::curves::foreach_selection_attribute_writer(
3322 curves, selection_domain, [](bke::GSpanAttributeWriter &selection) {
3323 ed::curves::fill_selection_false(selection.span);
3324 });
3325
3326 deselected = true;
3327 }
3328 });
3329 }
3330
3331 if (!closest.drawing) {
3332 if (deselected) {
3333 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
3334 * generic attribute for now. */
3335 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
3336 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
3337 }
3338 return deselected;
3339 }
3340
3341 const IndexMask selection_mask = IndexRange::from_single(closest.elem.index);
3342
3343 ed::greasepencil::selection_update(&vc,
3344 params.sel_op,
3346 const IndexMask & /*universe*/,
3347 StringRef attribute_name,
3348 IndexMaskMemory & /*memory*/) -> IndexMask {
3349 /* Selection update mask is already known, but only applies
3350 * to a specific drawing. */
3351 if (&info.drawing == closest.drawing &&
3352 attribute_name == closest.selection_attribute_name) {
3353 return selection_mask;
3354 }
3355 return {};
3356 });
3357
3358 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
3359 * generic attribute for now. */
3360 DEG_id_tag_update(&grease_pencil.id, ID_RECALC_GEOMETRY);
3361 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &grease_pencil);
3362
3363 return true;
3364}
3365
3367{
3368 Scene *scene = CTX_data_scene(C);
3369 Object *obedit = CTX_data_edit_object(C);
3370 Object *obact = CTX_data_active_object(C);
3371
3372 if (obact && obact->type == OB_GPENCIL_LEGACY && GPENCIL_ANY_MODE((bGPdata *)obact->data) &&
3374 {
3375 /* Prevent acting on Grease Pencil (when not in object mode -- or not in weight-paint + pose
3376 * selection), it implements its own selection operator in other modes. We might still fall
3377 * trough to here (because that operator uses OPERATOR_PASS_THROUGH to make tweak work) but if
3378 * we don't stop here code below assumes we are in object mode it might falsely toggle object
3379 * selection. Alternatively, this could be put in the poll function instead. */
3381 }
3382
3385
3388
3389 bool center = RNA_boolean_get(op->ptr, "center");
3390 bool enumerate = RNA_boolean_get(op->ptr, "enumerate");
3391 /* Only force object select for edit-mode to support vertex parenting,
3392 * or paint-select to allow pose bone select with vert/face select. */
3393 bool object_only = (RNA_boolean_get(op->ptr, "object") &&
3394 (obedit || BKE_paint_select_elem_test(obact) ||
3395 /* so its possible to select bones in weight-paint mode (LMB select) */
3396 (obact && (obact->mode & OB_MODE_ALL_WEIGHT_PAINT) &&
3398
3399 /* This could be called "changed_or_found" since this is true when there is an element
3400 * under the cursor to select, even if it happens that the selection & active state doesn't
3401 * actually change. This is important so undo pushes are predictable. */
3402 bool changed = false;
3403 int mval[2];
3404
3405 if (object_only) {
3406 obedit = nullptr;
3407 obact = nullptr;
3408
3409 /* ack, this is incorrect but to do this correctly we would need an
3410 * alternative edit-mode/object-mode keymap, this copies the functionality
3411 * from 2.4x where Ctrl+Select in edit-mode does object select only. */
3412 center = false;
3413 }
3414
3415 if (obedit && enumerate) {
3416 /* Enumerate makes no sense in edit-mode unless also explicitly picking objects or bones.
3417 * Pass the event through so the event may be handled by loop-select for e.g. see: #100204.
3418 */
3419 if (obedit->type != OB_ARMATURE) {
3421 }
3422 }
3423
3424 RNA_int_get_array(op->ptr, "location", mval);
3425
3428
3429 if (obedit && object_only == false) {
3430 if (obedit->type == OB_MESH) {
3431 changed = EDBM_select_pick(C, mval, &params);
3432 }
3433 else if (obedit->type == OB_ARMATURE) {
3434 if (enumerate) {
3435 GPUSelectBuffer buffer;
3436 const int hits = mixed_bones_object_selectbuffer(
3437 &vc, &buffer, mval, VIEW3D_SELECT_FILTER_NOP, false, true, false);
3438 changed = bone_mouse_select_menu(
3439 C, buffer.storage.as_span().take_front(hits), true, &params);
3440 }
3441 if (!changed) {
3442 changed = ED_armature_edit_select_pick(C, mval, &params);
3443 }
3444 }
3445 else if (obedit->type == OB_LATTICE) {
3446 changed = ED_lattice_select_pick(C, mval, &params);
3447 }
3448 else if (ELEM(obedit->type, OB_CURVES_LEGACY, OB_SURF)) {
3450 }
3451 else if (obedit->type == OB_MBALL) {
3452 changed = ED_mball_select_pick(C, mval, &params);
3453 }
3454 else if (obedit->type == OB_FONT) {
3455 changed = ED_curve_editfont_select_pick(C, mval, &params);
3456 }
3457 else if (obedit->type == OB_CURVES) {
3458 changed = ed_curves_select_pick(*C, mval, params);
3459 }
3460 else if (obedit->type == OB_GREASE_PENCIL) {
3461 changed = ed_grease_pencil_select_pick(C, mval, params);
3462 }
3463 }
3464 else if (obact && obact->mode & OB_MODE_PARTICLE_EDIT) {
3465 changed = PE_mouse_particles(C, mval, &params);
3466 }
3467 else if (obact && BKE_paint_select_face_test(obact)) {
3468 changed = paintface_mouse_select(C, mval, &params, obact);
3469 }
3470 else if (BKE_paint_select_vert_test(obact)) {
3471 changed = ed_wpaint_vertex_select_pick(C, mval, &params, obact);
3472 }
3473 else if (BKE_paint_select_grease_pencil_test(obact)) {
3474 changed = ed_grease_pencil_select_pick(C, mval, params);
3475 }
3476 else {
3477 changed = ed_object_select_pick(C, mval, &params, center, enumerate, object_only);
3478 }
3479
3480 /* Pass-through flag may be cleared, see #WM_operator_flag_only_pass_through_on_press. */
3481
3482 /* Pass-through allows tweaks
3483 * FINISHED to signal one operator worked */
3484 if (changed) {
3487 }
3488 /* Nothing selected, just passthrough. */
3490}
3491
3492static int view3d_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
3493{
3494 RNA_int_set_array(op->ptr, "location", event->mval);
3495
3496 const int retval = view3d_select_exec(C, op);
3497
3498 return WM_operator_flag_only_pass_through_on_press(retval, event);
3499}
3500
3502{
3503 PropertyRNA *prop;
3504
3505 /* identifiers */
3506 ot->name = "Select";
3507 ot->description = "Select and activate item(s)";
3508 ot->idname = "VIEW3D_OT_select";
3509
3510 /* api callbacks */
3515
3516 /* flags */
3517 ot->flag = OPTYPE_UNDO;
3518
3519 /* properties */
3521
3522 prop = RNA_def_boolean(
3523 ot->srna,
3524 "center",
3525 false,
3526 "Center",
3527 "Use the object center when selecting, in edit mode used to extend object selection");
3529 prop = RNA_def_boolean(ot->srna,
3530 "enumerate",
3531 false,
3532 "Enumerate",
3533 "List objects under the mouse (object mode only)");
3535 prop = RNA_def_boolean(
3536 ot->srna, "object", false, "Object", "Use object selection (edit mode only)");
3538
3539 prop = RNA_def_int_vector(ot->srna,
3540 "location",
3541 2,
3542 nullptr,
3543 INT_MIN,
3544 INT_MAX,
3545 "Location",
3546 "Mouse location",
3547 INT_MIN,
3548 INT_MAX);
3550}
3551
3554/* -------------------------------------------------------------------- */
3570
3572 const ViewContext *vc,
3573 const rcti *rect,
3574 const eSelectOp sel_op)
3575{
3576 r_data->vc = vc;
3577
3578 r_data->rect = rect;
3579 r_data->rect_fl = &r_data->_rect_fl;
3580 BLI_rctf_rcti_copy(&r_data->_rect_fl, rect);
3581
3582 r_data->sel_op = sel_op;
3583 /* SELECT by default, but can be changed if needed (only few cases use and respect this). */
3585
3586 /* runtime */
3587 r_data->is_done = false;
3588 r_data->is_changed = false;
3589}
3590
3591bool edge_inside_circle(const float cent[2],
3592 float radius,
3593 const float screen_co_a[2],
3594 const float screen_co_b[2])
3595{
3596 const float radius_squared = radius * radius;
3597 return (dist_squared_to_line_segment_v2(cent, screen_co_a, screen_co_b) < radius_squared);
3598}
3599
3604static void do_paintvert_box_select__doSelectVert(void *user_data,
3605 const float screen_co[2],
3606 int index)
3607{
3609 user_data);
3610 BoxSelectUserData *data = &mesh_data->box_data;
3611 const bool is_select = mesh_data->select_vert[index];
3612 const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co);
3613 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
3614 if (sel_op_result != -1) {
3615 mesh_data->select_vert[index] = sel_op_result == 1;
3616 data->is_changed = true;
3617 }
3618}
3620 wmGenericUserData *wm_userdata,
3621 const rcti *rect,
3622 const eSelectOp sel_op)
3623{
3624 using namespace blender;
3625 const bool use_zbuf = !XRAY_ENABLED(vc->v3d);
3626
3627 Mesh *mesh = static_cast<Mesh *>(vc->obact->data);
3628 if ((mesh == nullptr) || (mesh->verts_num == 0)) {
3629 return false;
3630 }
3631
3632 bool changed = false;
3633 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
3634 changed |= paintvert_deselect_all_visible(vc->obact, SEL_DESELECT, false);
3635 }
3636
3637 if (BLI_rcti_is_empty(rect)) {
3638 /* pass */
3639 }
3640 else if (use_zbuf) {
3641 EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
3642 if (wm_userdata->data == nullptr) {
3644 esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
3646 vc->depsgraph, vc->region, vc->v3d, rect, nullptr);
3647 }
3648 if (esel->select_bitmap != nullptr) {
3649 changed |= edbm_backbuf_check_and_select_verts_obmode(mesh, esel, sel_op);
3650 }
3651 }
3652 else {
3653 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
3654 bke::SpanAttributeWriter<bool> select_vert = attributes.lookup_or_add_for_write_span<bool>(
3655 ".select_vert", bke::AttrDomain::Point);
3656
3658 data.select_vert = select_vert.span;
3659
3660 view3d_userdata_boxselect_init(&data.box_data, vc, rect, sel_op);
3661
3663
3666 changed |= data.box_data.is_changed;
3667 select_vert.finish();
3668 }
3669
3670 if (changed) {
3671 if (SEL_OP_CAN_DESELECT(sel_op)) {
3673 }
3676 }
3677 return changed;
3678}
3679
3681 wmGenericUserData *wm_userdata,
3682 const rcti *rect,
3683 eSelectOp sel_op)
3684{
3685 Object *ob = vc->obact;
3686 Mesh *mesh;
3687
3688 mesh = BKE_mesh_from_object(ob);
3689 if ((mesh == nullptr) || (mesh->faces_num == 0)) {
3690 return false;
3691 }
3692
3693 bool changed = false;
3694 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
3695 changed |= paintface_deselect_all_visible(vc->C, vc->obact, SEL_DESELECT, false);
3696 }
3697
3698 if (BLI_rcti_is_empty(rect)) {
3699 /* pass */
3700 }
3701 else {
3702 EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
3703 if (wm_userdata->data == nullptr) {
3705 esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
3707 vc->depsgraph, vc->region, vc->v3d, rect, nullptr);
3708 }
3709 if (esel->select_bitmap != nullptr) {
3710 changed |= edbm_backbuf_check_and_select_faces_obmode(mesh, esel, sel_op);
3711 }
3712 }
3713
3714 if (changed) {
3715 paintface_flush_flags(vc->C, vc->obact, true, false);
3716 }
3717 return changed;
3718}
3719
3720static void do_nurbs_box_select__doSelect(void *user_data,
3721 Nurb * /*nu*/,
3722 BPoint *bp,
3723 BezTriple *bezt,
3724 int beztindex,
3725 bool handles_visible,
3726 const float screen_co[2])
3727{
3728 BoxSelectUserData *data = static_cast<BoxSelectUserData *>(user_data);
3729
3730 const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co);
3731 if (bp) {
3732 const bool is_select = bp->f1 & SELECT;
3733 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
3734 if (sel_op_result != -1) {
3735 SET_FLAG_FROM_TEST(bp->f1, sel_op_result, data->select_flag);
3736 data->is_changed = true;
3737 }
3738 }
3739 else {
3740 if (!handles_visible) {
3741 /* can only be (beztindex == 1) here since handles are hidden */
3742 const bool is_select = bezt->f2 & SELECT;
3743 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
3744 if (sel_op_result != -1) {
3745 SET_FLAG_FROM_TEST(bezt->f2, sel_op_result, data->select_flag);
3746 data->is_changed = true;
3747 }
3748 bezt->f1 = bezt->f3 = bezt->f2;
3749 }
3750 else {
3751 uint8_t *flag_p = (&bezt->f1) + beztindex;
3752 const bool is_select = *flag_p & SELECT;
3753 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
3754 if (sel_op_result != -1) {
3755 SET_FLAG_FROM_TEST(*flag_p, sel_op_result, data->select_flag);
3756 data->is_changed = true;
3757 }
3758 }
3759 }
3760}
3761static bool do_nurbs_box_select(const ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
3762{
3763 const bool deselect_all = (sel_op == SEL_OP_SET);
3765
3766 view3d_userdata_boxselect_init(&data, vc, rect, sel_op);
3767
3768 Curve *curve = (Curve *)vc->obedit->data;
3769 ListBase *nurbs = BKE_curve_editNurbs_get(curve);
3770
3771 /* For deselect all, items to be selected are tagged with temp flag. Clear that first. */
3772 if (deselect_all) {
3774 data.select_flag = BEZT_FLAG_TEMP_TAG;
3775 }
3776
3777 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
3779
3780 /* Deselect items that were not added to selection (indicated by temp flag). */
3781 if (deselect_all) {
3782 data.is_changed |= BKE_nurbList_flag_set_from_flag(nurbs, BEZT_FLAG_TEMP_TAG, SELECT);
3783 }
3784
3786
3787 return data.is_changed;
3788}
3789
3790static void do_lattice_box_select__doSelect(void *user_data, BPoint *bp, const float screen_co[2])
3791{
3792 BoxSelectUserData *data = static_cast<BoxSelectUserData *>(user_data);
3793 const bool is_select = bp->f1 & SELECT;
3794 const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co);
3795 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
3796 if (sel_op_result != -1) {
3797 SET_FLAG_FROM_TEST(bp->f1, sel_op_result, SELECT);
3798 data->is_changed = true;
3799 }
3800}
3801static bool do_lattice_box_select(const ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
3802{
3804
3805 view3d_userdata_boxselect_init(&data, vc, rect, sel_op);
3806
3807 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
3808 data.is_changed |= ED_lattice_flags_set(vc->obedit, 0);
3809 }
3810
3811 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
3814
3815 return data.is_changed;
3816}
3817
3818static void do_mesh_box_select__doSelectVert(void *user_data,
3819 BMVert *eve,
3820 const float screen_co[2],
3821 int /*index*/)
3822{
3823 BoxSelectUserData *data = static_cast<BoxSelectUserData *>(user_data);
3824 const bool is_select = BM_elem_flag_test(eve, BM_ELEM_SELECT);
3825 const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co);
3826 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
3827 if (sel_op_result != -1) {
3828 BM_vert_select_set(data->vc->em->bm, eve, sel_op_result);
3829 data->is_changed = true;
3830 }
3831}
3840static void do_mesh_box_select__doSelectEdge_pass0(void *user_data,
3841 BMEdge *eed,
3842 const float screen_co_a[2],
3843 const float screen_co_b[2],
3844 int index)
3845{
3846 BoxSelectUserData_ForMeshEdge *data_for_edge = static_cast<BoxSelectUserData_ForMeshEdge *>(
3847 user_data);
3848 BoxSelectUserData *data = data_for_edge->data;
3849 bool is_visible = true;
3850 if (data_for_edge->backbuf_offset) {
3851 uint bitmap_inedx = data_for_edge->backbuf_offset + index - 1;
3852 is_visible = BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, bitmap_inedx);
3853 }
3854
3855 const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT);
3856 const bool is_inside = (is_visible &&
3857 edge_fully_inside_rect(data->rect_fl, screen_co_a, screen_co_b));
3858 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
3859 if (sel_op_result != -1) {
3860 BM_edge_select_set(data->vc->em->bm, eed, sel_op_result);
3861 data->is_done = true;
3862 data->is_changed = true;
3863 }
3864}
3868static void do_mesh_box_select__doSelectEdge_pass1(void *user_data,
3869 BMEdge *eed,
3870 const float screen_co_a[2],
3871 const float screen_co_b[2],
3872 int index)
3873{
3874 BoxSelectUserData_ForMeshEdge *data_for_edge = static_cast<BoxSelectUserData_ForMeshEdge *>(
3875 user_data);
3876 BoxSelectUserData *data = data_for_edge->data;
3877 bool is_visible = true;
3878 if (data_for_edge->backbuf_offset) {
3879 uint bitmap_inedx = data_for_edge->backbuf_offset + index - 1;
3880 is_visible = BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap, bitmap_inedx);
3881 }
3882
3883 const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT);
3884 const bool is_inside = (is_visible && edge_inside_rect(data->rect_fl, screen_co_a, screen_co_b));
3885 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
3886 if (sel_op_result != -1) {
3887 BM_edge_select_set(data->vc->em->bm, eed, sel_op_result);
3888 data->is_changed = true;
3889 }
3890}
3891static void do_mesh_box_select__doSelectFace(void *user_data,
3892 BMFace *efa,
3893 const float screen_co[2],
3894 int /*index*/)
3895{
3896 BoxSelectUserData *data = static_cast<BoxSelectUserData *>(user_data);
3897 const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT);
3898 const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co);
3899 const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
3900 if (sel_op_result != -1) {
3901 BM_face_select_set(data->vc->em->bm, efa, sel_op_result);
3902 data->is_changed = true;
3903 }
3904}
3905static bool do_mesh_box_select(const ViewContext *vc,
3906 wmGenericUserData *wm_userdata,
3907 const rcti *rect,
3908 const eSelectOp sel_op)
3909{
3911 ToolSettings *ts = vc->scene->toolsettings;
3912
3913 view3d_userdata_boxselect_init(&data, vc, rect, sel_op);
3914
3915 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
3916 if (vc->em->bm->totvertsel) {
3918 data.is_changed = true;
3919 }
3920 }
3921
3922 /* for non zbuf projections, don't change the GL state */
3924
3926
3927 const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d);
3928
3929 EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
3930 if (use_zbuf) {
3931 if (wm_userdata->data == nullptr) {
3933 esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
3935 vc->depsgraph, vc->region, vc->v3d, rect, nullptr);
3936 }
3937 }
3938
3939 if (ts->selectmode & SCE_SELECT_VERTEX) {
3940 if (use_zbuf) {
3941 data.is_changed |= edbm_backbuf_check_and_select_verts(
3942 esel, vc->depsgraph, vc->obedit, vc->em, sel_op);
3943 }
3944 else {
3947 }
3948 }
3949 if (ts->selectmode & SCE_SELECT_EDGE) {
3950 /* Does both use_zbuf and non-use_zbuf versions (need screen cos for both) */
3952 cb_data.data = &data;
3953 cb_data.esel = use_zbuf ? esel : nullptr;
3954 cb_data.backbuf_offset = use_zbuf ? DRW_select_buffer_context_offset_for_object_elem(
3955 vc->depsgraph, vc->obedit, SCE_SELECT_EDGE) :
3956 0;
3957
3958 const eV3DProjTest clip_flag = V3D_PROJ_TEST_CLIP_NEAR |
3959 (use_zbuf ? (eV3DProjTest)0 : V3D_PROJ_TEST_CLIP_BB);
3960 /* Fully inside. */
3962 vc, do_mesh_box_select__doSelectEdge_pass0, &cb_data, clip_flag);
3963 if (data.is_done == false) {
3964 /* Fall back to partially inside.
3965 * Clip content to account for edges partially behind the view. */
3968 &cb_data,
3970 }
3971 }
3972
3973 if (ts->selectmode & SCE_SELECT_FACE) {
3974 if (use_zbuf) {
3975 data.is_changed |= edbm_backbuf_check_and_select_faces(
3976 esel, vc->depsgraph, vc->obedit, vc->em, sel_op);
3977 }
3978 else {
3981 }
3982 }
3983
3984 if (data.is_changed) {
3986 }
3987 return data.is_changed;
3988}
3989
3990static bool do_meta_box_select(const ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
3991{
3992 Object *ob = vc->obedit;
3993 MetaBall *mb = (MetaBall *)ob->data;
3994 MetaElem *ml;
3995 int a;
3996 bool changed = false;
3997
3998 GPUSelectBuffer buffer;
3999 int hits;
4000
4002
4003 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
4004 changed |= BKE_mball_deselect_all(mb);
4005 }
4006
4007 int metaelem_id = 0;
4008 for (ml = static_cast<MetaElem *>(mb->editelems->first); ml;
4009 ml = ml->next, metaelem_id += 0x10000)
4010 {
4011 bool is_inside_radius = false;
4012 bool is_inside_stiff = false;
4013
4014 for (a = 0; a < hits; a++) {
4015 const int hitresult = buffer.storage[a].id;
4016
4017 if (hitresult == -1) {
4018 continue;
4019 }
4020
4021 const uint hit_object = hitresult & 0xFFFF;
4022 if (vc->obedit->runtime->select_id != hit_object) {
4023 continue;
4024 }
4025
4026 if (metaelem_id != (hitresult & 0xFFFF0000 & ~MBALLSEL_ANY)) {
4027 continue;
4028 }
4029
4030 if (hitresult & MBALLSEL_RADIUS) {
4031 is_inside_radius = true;
4032 break;
4033 }
4034
4035 if (hitresult & MBALLSEL_STIFF) {
4036 is_inside_stiff = true;
4037 break;
4038 }
4039 }
4040 const int flag_prev = ml->flag;
4041 if (is_inside_radius) {
4042 ml->flag |= MB_SCALE_RAD;
4043 }
4044 if (is_inside_stiff) {
4045 ml->flag &= ~MB_SCALE_RAD;
4046 }
4047
4048 const bool is_select = (ml->flag & SELECT);
4049 const bool is_inside = is_inside_radius || is_inside_stiff;
4050
4051 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
4052 if (sel_op_result != -1) {
4053 SET_FLAG_FROM_TEST(ml->flag, sel_op_result, SELECT);
4054 }
4055 changed |= (flag_prev != ml->flag);
4056 }
4057
4058 return changed;
4059}
4060
4061static bool do_armature_box_select(const ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
4062{
4063 bool changed = false;
4064 int a;
4065
4066 GPUSelectBuffer buffer;
4067 int hits;
4068
4070
4072 vc->scene, vc->view_layer, vc->v3d);
4073
4074 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
4076 }
4077
4078 for (Base *base : bases) {
4079 Object *obedit = base->object;
4080 obedit->id.tag &= ~ID_TAG_DOIT;
4081
4082 bArmature *arm = static_cast<bArmature *>(obedit->data);
4084 }
4085
4086 /* first we only check points inside the border */
4087 for (a = 0; a < hits; a++) {
4088 const int select_id = buffer.storage[a].id;
4089 if (select_id != -1) {
4090 if ((select_id & 0xFFFF0000) == 0) {
4091 continue;
4092 }
4093
4094 EditBone *ebone;
4095 Base *base_edit = ED_armature_base_and_ebone_from_select_buffer(bases, select_id, &ebone);
4096 ebone->temp.i |= select_id & BONESEL_ANY;
4097 base_edit->object->id.tag |= ID_TAG_DOIT;
4098 }
4099 }
4100
4101 for (Base *base : bases) {
4102 Object *obedit = base->object;
4103 if (obedit->id.tag & ID_TAG_DOIT) {
4104 obedit->id.tag &= ~ID_TAG_DOIT;
4105 changed |= ED_armature_edit_select_op_from_tagged(static_cast<bArmature *>(obedit->data),
4106 sel_op);
4107 }
4108 }
4109
4110 return changed;
4111}
4112
4117static int opengl_bone_select_buffer_cmp(const void *sel_a_p, const void *sel_b_p)
4118{
4119 uint sel_a = ((GPUSelectResult *)sel_a_p)->id;
4120 uint sel_b = ((GPUSelectResult *)sel_b_p)->id;
4121
4122#ifdef __BIG_ENDIAN__
4125#endif
4126
4127 if (sel_a < sel_b) {
4128 return -1;
4129 }
4130 if (sel_a > sel_b) {
4131 return 1;
4132 }
4133 return 0;
4134}
4135
4137 const ViewContext *vc,
4138 const rcti *rect,
4139 const eSelectOp sel_op)
4140{
4141 View3D *v3d = vc->v3d;
4142
4143 GPUSelectBuffer buffer;
4145 vc->obact);
4146 const int hits = view3d_opengl_select(vc, &buffer, rect, VIEW3D_SELECT_ALL, select_filter);
4149 base->object->id.tag &= ~ID_TAG_DOIT;
4150 }
4151
4153
4154 bool changed = false;
4155 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
4156 changed |= object_deselect_all_visible(vc->scene, vc->view_layer, vc->v3d);
4157 }
4158
4160 if ((hits == -1) && !SEL_OP_USE_OUTSIDE(sel_op)) {
4161 goto finally;
4162 }
4163
4164 LISTBASE_FOREACH (Base *, base, object_bases) {
4165 if (BASE_SELECTABLE(v3d, base)) {
4166 if ((base->object->runtime->select_id & 0x0000FFFF) != 0) {
4167 bases.append(base);
4168 }
4169 }
4170 }
4171
4172 /* The draw order doesn't always match the order we populate the engine, see: #51695. */
4173 qsort(buffer.storage.data(), hits, sizeof(GPUSelectResult), opengl_bone_select_buffer_cmp);
4174
4175 for (const GPUSelectResult *buf_iter = buffer.storage.data(), *buf_end = buf_iter + hits;
4176 buf_iter < buf_end;
4177 buf_iter++)
4178 {
4179 bPoseChannel *pchan_dummy;
4180 Base *base = ED_armature_base_and_pchan_from_select_buffer(bases, buf_iter->id, &pchan_dummy);
4181 if (base != nullptr) {
4182 base->object->id.tag |= ID_TAG_DOIT;
4183 }
4184 }
4185
4186 for (Base *base = static_cast<Base *>(object_bases->first); base && hits; base = base->next) {
4187 if (BASE_SELECTABLE(v3d, base)) {
4188 const bool is_select = base->flag & BASE_SELECTED;
4189 const bool is_inside = base->object->id.tag & ID_TAG_DOIT;
4190 const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
4191 if (sel_op_result != -1) {
4193 sel_op_result ? blender::ed::object::BA_SELECT :
4195 changed = true;
4196 }
4197 }
4198 }
4199
4200finally:
4201
4202 if (changed) {
4205 }
4206 return changed;
4207}
4208
4210 const ViewContext *vc,
4211 const rcti *rect,
4212 const eSelectOp sel_op)
4213{
4215
4216 /* Selection buffer has bones potentially too. */
4217 GPUSelectBuffer buffer;
4219 vc->obact);
4220 const int hits = view3d_opengl_select(vc, &buffer, rect, VIEW3D_SELECT_ALL, select_filter);
4221 /*
4222 * NOTE(@theeth): Regarding the logic use here.
4223 * The buffer and #ListBase have the same relative order, which makes the selection
4224 * very simple. Loop through both data sets at the same time, if the color
4225 * is the same as the object, we have a hit and can move to the next color
4226 * and object pair, if not, just move to the next object,
4227 * keeping the same color until we have a hit. */
4228
4229 if (hits > 0) {
4230 /* no need to loop if there's no hit */
4231
4232 /* The draw order doesn't always match the order we populate the engine, see: #51695. */
4233 qsort(buffer.storage.data(), hits, sizeof(GPUSelectResult), opengl_bone_select_buffer_cmp);
4234
4235 for (const GPUSelectResult *buf_iter = buffer.storage.data(), *buf_end = buf_iter + hits;
4236 buf_iter < buf_end;
4237 buf_iter++)
4238 {
4239 Bone *bone;
4240 Base *base = ED_armature_base_and_bone_from_select_buffer(bases, buf_iter->id, &bone);
4241
4242 if (base == nullptr) {
4243 continue;
4244 }
4245
4246 /* Loop over contiguous bone hits for 'base'. */
4247 for (; buf_iter != buf_end; buf_iter++) {
4248 /* should never fail */
4249 if (bone != nullptr) {
4250 base->object->id.tag |= ID_TAG_DOIT;
4251 bone->flag |= BONE_DONE;
4252 }
4253
4254 /* Select the next bone if we're not switching bases. */
4255 if (buf_iter + 1 != buf_end) {
4256 const GPUSelectResult *col_next = buf_iter + 1;
4257 if ((base->object->runtime->select_id & 0x0000FFFF) != (col_next->id & 0x0000FFFF)) {
4258 break;
4259 }
4260 if (base->object->pose != nullptr) {
4261 const uint hit_bone = (col_next->id & ~BONESEL_ANY) >> 16;
4262 bPoseChannel *pchan = static_cast<bPoseChannel *>(
4263 BLI_findlink(&base->object->pose->chanbase, hit_bone));
4264 bone = pchan ? pchan->bone : nullptr;
4265 }
4266 else {
4267 bone = nullptr;
4268 }
4269 }
4270 }
4271 }
4272 }
4273
4274 const bool changed_multi = do_pose_tag_select_op_exec(bases, sel_op);
4275 if (changed_multi) {
4278 }
4279
4280 return changed_multi;
4281}
4282
4284 const rcti *rect,
4285 const eSelectOp sel_op)
4286{
4287 using namespace blender;
4288 Object *object = (vc->obedit ? vc->obedit : vc->obact);
4289 const Object *ob_eval = DEG_get_evaluated_object(vc->depsgraph, const_cast<Object *>(object));
4290 const GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
4292 vc->scene->toolsettings, object);
4293
4294 return ed::greasepencil::selection_update(
4295 vc,
4296 sel_op,
4298 const IndexMask &mask,
4299 const StringRef attribute_name,
4300 IndexMaskMemory &memory) {
4302 const bke::greasepencil::Layer &layer = grease_pencil.layer(info.layer_index);
4303 const bke::crazyspace::GeometryDeformation deformation =
4304 bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
4305 ob_eval, *object, info.layer_index, info.frame_number);
4306 const IndexMask visible_handle_elements =
4307 ed::greasepencil::retrieve_visible_bezier_handle_elements(
4308 *object, info.drawing, info.layer_index, selection_domain, memory);
4309 const float4x4 layer_to_world = layer.to_world_space(*ob_eval);
4311 layer_to_world);
4312
4313 return ed::curves::select_box_mask(*vc,
4314 curves,
4315 deformation,
4316 projection,
4317 mask,
4318 visible_handle_elements,
4319 selection_domain,
4320 attribute_name,
4321 *rect,
4322 memory);
4323 });
4324}
4325
4327{
4328 using namespace blender;
4330 rcti rect;
4331 bool changed_multi = false;
4332
4333 wmGenericUserData wm_userdata_buf = {nullptr, nullptr, false};
4334 wmGenericUserData *wm_userdata = &wm_userdata_buf;
4335
4338
4339 /* setup view context for argument to callbacks */
4341
4342 eSelectOp sel_op = static_cast<eSelectOp>(RNA_enum_get(op->ptr, "mode"));
4344
4345 if (vc.obedit) {
4347 vc.scene, vc.view_layer, vc.v3d, vc.obedit->type, vc.obedit->mode, ob_iter)
4348 {
4350 bool changed = false;
4351
4352 switch (vc.obedit->type) {
4353 case OB_MESH:
4355 changed = do_mesh_box_select(&vc, wm_userdata, &rect, sel_op);
4356 if (changed) {
4357 DEG_id_tag_update(static_cast<ID *>(vc.obedit->data), ID_RECALC_SELECT);
4359 }
4360 break;
4361 case OB_CURVES_LEGACY:
4362 case OB_SURF:
4363 changed = do_nurbs_box_select(&vc, &rect, sel_op);
4364 if (changed) {
4365 DEG_id_tag_update(static_cast<ID *>(vc.obedit->data), ID_RECALC_SELECT);
4367 }
4368 break;
4369 case OB_MBALL:
4370 changed = do_meta_box_select(&vc, &rect, sel_op);
4371 if (changed) {
4372 DEG_id_tag_update(static_cast<ID *>(vc.obedit->data), ID_RECALC_SELECT);
4374 }
4375 break;
4376 case OB_ARMATURE:
4377 changed = do_armature_box_select(&vc, &rect, sel_op);
4378 if (changed) {
4382 }
4383 break;
4384 case OB_LATTICE:
4385 changed = do_lattice_box_select(&vc, &rect, sel_op);
4386 if (changed) {
4387 DEG_id_tag_update(static_cast<ID *>(vc.obedit->data), ID_RECALC_SELECT);
4389 }
4390 break;
4391 case OB_CURVES: {
4392 Curves &curves_id = *static_cast<Curves *>(vc.obedit->data);
4393 bke::CurvesGeometry &curves = curves_id.geometry.wrap();
4395 bke::crazyspace::get_evaluated_curves_deformation(*vc.depsgraph, *vc.obedit);
4396 const bke::AttrDomain selection_domain = bke::AttrDomain(curves_id.selection_domain);
4397 const float4x4 projection = ED_view3d_ob_project_mat_get(vc.rv3d, vc.obedit);
4398 const IndexRange elements(curves.attributes().domain_size(selection_domain));
4399 changed = ed::curves::select_box(vc,
4400 curves,
4401 deformation,
4402 projection,
4403 elements,
4404 elements,
4405 selection_domain,
4406 rect,
4407 sel_op);
4408 if (changed) {
4409 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
4410 * generic attribute for now. */
4411 DEG_id_tag_update(static_cast<ID *>(vc.obedit->data), ID_RECALC_GEOMETRY);
4413 }
4414 break;
4415 }
4416 case OB_GREASE_PENCIL: {
4417 changed = do_grease_pencil_box_select(&vc, &rect, sel_op);
4418 break;
4419 }
4420 default:
4421 BLI_assert_msg(0, "box select on incorrect object type");
4422 break;
4423 }
4424 changed_multi |= changed;
4425 }
4427 }
4428 else { /* No edit-mode, unified for bones and objects. */
4429 if (vc.obact && BKE_paint_select_face_test(vc.obact)) {
4430 changed_multi = do_paintface_box_select(&vc, wm_userdata, &rect, sel_op);
4431 }
4432 else if (vc.obact && BKE_paint_select_vert_test(vc.obact)) {
4433 changed_multi = do_paintvert_box_select(&vc, wm_userdata, &rect, sel_op);
4434 }
4435 else if (vc.obact && BKE_paint_select_grease_pencil_test(vc.obact)) {
4436 changed_multi = do_grease_pencil_box_select(&vc, &rect, sel_op);
4437 }
4438 else if (vc.obact && vc.obact->mode & OB_MODE_PARTICLE_EDIT) {
4439 changed_multi = PE_box_select(C, &rect, sel_op);
4440 }
4441 else if (vc.obact && ((vc.obact->mode & OB_MODE_POSE) ||
4442 ((vc.obact->mode & OB_MODE_WEIGHT_PAINT) &&
4444 {
4445 changed_multi = do_pose_box_select(C, &vc, &rect, sel_op);
4446 if (changed_multi) {
4448 }
4449 }
4450 else { /* object mode with none active */
4451 changed_multi = do_object_box_select(C, &vc, &rect, sel_op);
4452 if (changed_multi) {
4454 }
4455 }
4456 }
4457
4458 WM_generic_user_data_free(wm_userdata);
4459
4460 if (changed_multi) {
4461 return OPERATOR_FINISHED;
4462 }
4463 return OPERATOR_CANCELLED;
4464}
4465
4467{
4468 /* identifiers */
4469 ot->name = "Box Select";
4470 ot->description = "Select items using box selection";
4471 ot->idname = "VIEW3D_OT_select_box";
4472
4473 /* api callbacks */
4479
4480 /* flags */
4481 ot->flag = OPTYPE_UNDO;
4482
4483 /* rna */
4486}
4487
4490/* -------------------------------------------------------------------- */
4497 int mval[2];
4498 float mval_fl[2];
4499 float radius;
4502
4503 /* runtime */
4505};
4506
4508 const ViewContext *vc,
4509 const bool select,
4510 const int mval[2],
4511 const float rad)
4512{
4513 r_data->vc = vc;
4514 r_data->select = select;
4515 copy_v2_v2_int(r_data->mval, mval);
4516 r_data->mval_fl[0] = mval[0];
4517 r_data->mval_fl[1] = mval[1];
4518
4519 r_data->radius = rad;
4520 r_data->radius_squared = rad * rad;
4521
4522 /* SELECT by default, but can be changed if needed (only few cases use and respect this). */
4524
4525 /* runtime */
4526 r_data->is_changed = false;
4527}
4528
4529static void mesh_circle_doSelectVert(void *user_data,
4530 BMVert *eve,
4531 const float screen_co[2],
4532 int /*index*/)
4533{
4534 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
4535
4536 if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
4537 BM_vert_select_set(data->vc->em->bm, eve, data->select);
4538 data->is_changed = true;
4539 }
4540}
4541static void mesh_circle_doSelectEdge(void *user_data,
4542 BMEdge *eed,
4543 const float screen_co_a[2],
4544 const float screen_co_b[2],
4545 int /*index*/)
4546{
4547 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
4548
4549 if (edge_inside_circle(data->mval_fl, data->radius, screen_co_a, screen_co_b)) {
4550 BM_edge_select_set(data->vc->em->bm, eed, data->select);
4551 data->is_changed = true;
4552 }
4553}
4554static void mesh_circle_doSelectFace(void *user_data,
4555 BMFace *efa,
4556 const float screen_co[2],
4557 int /*index*/)
4558{
4559 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
4560
4561 if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
4562 BM_face_select_set(data->vc->em->bm, efa, data->select);
4563 data->is_changed = true;
4564 }
4565}
4566
4567static bool mesh_circle_select(const ViewContext *vc,
4568 wmGenericUserData *wm_userdata,
4569 eSelectOp sel_op,
4570 const int mval[2],
4571 float rad)
4572{
4573 ToolSettings *ts = vc->scene->toolsettings;
4576
4577 bool changed = false;
4578 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
4579 if (vc->em->bm->totvertsel) {
4581 vc->em->bm->totvertsel = 0;
4582 vc->em->bm->totedgesel = 0;
4583 vc->em->bm->totfacesel = 0;
4584 changed = true;
4585 }
4586 }
4587 const bool select = (sel_op != SEL_OP_SUB);
4588
4589 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
4590
4591 view3d_userdata_circleselect_init(&data, vc, select, mval, rad);
4592
4593 const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d);
4594
4595 if (use_zbuf) {
4596 if (wm_userdata->data == nullptr) {
4598 }
4599 }
4600 EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
4601
4602 if (use_zbuf) {
4603 if (esel->select_bitmap == nullptr) {
4605 vc->depsgraph, vc->region, vc->v3d, mval, int(rad + 1.0f), nullptr);
4606 }
4607 }
4608
4609 if (ts->selectmode & SCE_SELECT_VERTEX) {
4610 if (use_zbuf) {
4611 if (esel->select_bitmap != nullptr) {
4613 esel, vc->depsgraph, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB);
4614 }
4615 }
4616 else {
4618 }
4619 }
4620
4621 if (ts->selectmode & SCE_SELECT_EDGE) {
4622 if (use_zbuf) {
4623 if (esel->select_bitmap != nullptr) {
4625 esel, vc->depsgraph, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB);
4626 }
4627 }
4628 else {
4630 vc,
4632 &data,
4634 }
4635 }
4636
4637 if (ts->selectmode & SCE_SELECT_FACE) {
4638 if (use_zbuf) {
4639 if (esel->select_bitmap != nullptr) {
4641 esel, vc->depsgraph, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB);
4642 }
4643 }
4644 else {
4646 }
4647 }
4648
4649 changed |= data.is_changed;
4650
4651 if (changed) {
4654 }
4655 return changed;
4656}
4657
4659 wmGenericUserData *wm_userdata,
4660 const eSelectOp sel_op,
4661 const int mval[2],
4662 float rad)
4663{
4665 Object *ob = vc->obact;
4666 Mesh *mesh = static_cast<Mesh *>(ob->data);
4667
4668 bool changed = false;
4669 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
4670 /* flush selection at the end */
4671 changed |= paintface_deselect_all_visible(vc->C, ob, SEL_DESELECT, false);
4672 }
4673
4674 if (wm_userdata->data == nullptr) {
4676 }
4677
4678 {
4679 EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
4681 vc->depsgraph, vc->region, vc->v3d, mval, int(rad + 1.0f), nullptr);
4682 if (esel->select_bitmap != nullptr) {
4683 changed |= edbm_backbuf_check_and_select_faces_obmode(mesh, esel, sel_op);
4684 MEM_freeN(esel->select_bitmap);
4685 esel->select_bitmap = nullptr;
4686 }
4687 }
4688
4689 if (changed) {
4690 paintface_flush_flags(vc->C, ob, true, false);
4691 }
4692 return changed;
4693}
4694
4700 const float screen_co[2],
4701 int index)
4702{
4704 user_data);
4705 CircleSelectUserData *data = &mesh_data->circle_data;
4706
4707 if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
4708 mesh_data->select_vert[index] = data->select;
4709 data->is_changed = true;
4710 }
4711}
4713 wmGenericUserData *wm_userdata,
4714 const eSelectOp sel_op,
4715 const int mval[2],
4716 float rad)
4717{
4718 using namespace blender;
4720 const bool use_zbuf = !XRAY_ENABLED(vc->v3d);
4721 Object *ob = vc->obact;
4722 Mesh *mesh = static_cast<Mesh *>(ob->data);
4723 // CircleSelectUserData data = {nullptr}; /* UNUSED. */
4724
4725 bool changed = false;
4726 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
4727 /* Flush selection at the end. */
4728 changed |= paintvert_deselect_all_visible(ob, SEL_DESELECT, false);
4729 }
4730
4731 const bool select = (sel_op != SEL_OP_SUB);
4732
4733 if (use_zbuf) {
4734 if (wm_userdata->data == nullptr) {
4736 }
4737 }
4738
4739 if (use_zbuf) {
4740 EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
4742 vc->depsgraph, vc->region, vc->v3d, mval, int(rad + 1.0f), nullptr);
4743 if (esel->select_bitmap != nullptr) {
4744 changed |= edbm_backbuf_check_and_select_verts_obmode(mesh, esel, sel_op);
4745 MEM_freeN(esel->select_bitmap);
4746 esel->select_bitmap = nullptr;
4747 }
4748 }
4749 else {
4750 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
4751 bke::SpanAttributeWriter<bool> select_vert = attributes.lookup_or_add_for_write_span<bool>(
4752 ".select_vert", bke::AttrDomain::Point);
4753
4755 data.select_vert = select_vert.span;
4756
4757 ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d); /* for foreach's screen/vert projection */
4758
4759 view3d_userdata_circleselect_init(&data.circle_data, vc, select, mval, rad);
4762 changed |= data.circle_data.is_changed;
4763 select_vert.finish();
4764 }
4765
4766 if (changed) {
4767 if (sel_op == SEL_OP_SUB) {
4769 }
4772 }
4773 return changed;
4774}
4775
4776static void nurbscurve_circle_doSelect(void *user_data,
4777 Nurb * /*nu*/,
4778 BPoint *bp,
4779 BezTriple *bezt,
4780 int beztindex,
4781 bool /*handles_visible*/,
4782 const float screen_co[2])
4783{
4784 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
4785
4786 if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
4787 if (bp) {
4788 SET_FLAG_FROM_TEST(bp->f1, data->select, data->select_flag);
4789 }
4790 else {
4791 if (beztindex == 0) {
4792 SET_FLAG_FROM_TEST(bezt->f1, data->select, data->select_flag);
4793 }
4794 else if (beztindex == 1) {
4795 SET_FLAG_FROM_TEST(bezt->f2, data->select, data->select_flag);
4796 }
4797 else {
4798 SET_FLAG_FROM_TEST(bezt->f3, data->select, data->select_flag);
4799 }
4800 }
4801 data->is_changed = true;
4802 }
4803}
4805 const eSelectOp sel_op,
4806 const int mval[2],
4807 float rad)
4808{
4809 const bool select = (sel_op != SEL_OP_SUB);
4810 const bool deselect_all = (sel_op == SEL_OP_SET);
4812
4813 view3d_userdata_circleselect_init(&data, vc, select, mval, rad);
4814
4815 Curve *curve = (Curve *)vc->obedit->data;
4816 ListBase *nurbs = BKE_curve_editNurbs_get(curve);
4817
4818 /* For deselect all, items to be selected are tagged with temp flag. Clear that first. */
4819 if (deselect_all) {
4821 data.select_flag = BEZT_FLAG_TEMP_TAG;
4822 }
4823
4824 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
4826
4827 /* Deselect items that were not added to selection (indicated by temp flag). */
4828 if (deselect_all) {
4829 data.is_changed |= BKE_nurbList_flag_set_from_flag(nurbs, BEZT_FLAG_TEMP_TAG, SELECT);
4830 }
4831
4833
4834 return data.is_changed;
4835}
4836
4837static void latticecurve_circle_doSelect(void *user_data, BPoint *bp, const float screen_co[2])
4838{
4839 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
4840
4841 if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
4842 bp->f1 = data->select ? (bp->f1 | SELECT) : (bp->f1 & ~SELECT);
4843 data->is_changed = true;
4844 }
4845}
4847 const eSelectOp sel_op,
4848 const int mval[2],
4849 float rad)
4850{
4852 const bool select = (sel_op != SEL_OP_SUB);
4853
4854 view3d_userdata_circleselect_init(&data, vc, select, mval, rad);
4855
4856 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
4857 data.is_changed |= ED_lattice_flags_set(vc->obedit, 0);
4858 }
4859 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
4860
4862
4863 return data.is_changed;
4864}
4865
4869static bool pchan_circle_doSelectJoint(void *user_data,
4870 bPoseChannel *pchan,
4871 const float screen_co[2])
4872{
4873 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
4874
4875 if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
4876 if (data->select) {
4877 pchan->bone->flag |= BONE_SELECTED;
4878 }
4879 else {
4880 pchan->bone->flag &= ~BONE_SELECTED;
4881 }
4882 return true;
4883 }
4884 return false;
4885}
4886static void do_circle_select_pose__doSelectBone(void *user_data,
4887 bPoseChannel *pchan,
4888 const float screen_co_a[2],
4889 const float screen_co_b[2])
4890{
4891 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
4892 bArmature *arm = static_cast<bArmature *>(data->vc->obact->data);
4893 if (!PBONE_SELECTABLE(arm, pchan->bone)) {
4894 return;
4895 }
4896
4897 bool is_point_done = false;
4898 int points_proj_tot = 0;
4899
4900 /* Project head location to screen-space. */
4901 if (screen_co_a[0] != IS_CLIPPED) {
4902 points_proj_tot++;
4903 if (pchan_circle_doSelectJoint(data, pchan, screen_co_a)) {
4904 is_point_done = true;
4905 }
4906 }
4907
4908 /* Project tail location to screen-space. */
4909 if (screen_co_b[0] != IS_CLIPPED) {
4910 points_proj_tot++;
4911 if (pchan_circle_doSelectJoint(data, pchan, screen_co_b)) {
4912 is_point_done = true;
4913 }
4914 }
4915
4916 /* check if the head and/or tail is in the circle
4917 * - the call to check also does the selection already
4918 */
4919
4920 /* only if the endpoints didn't get selected, deal with the middle of the bone too
4921 * It works nicer to only do this if the head or tail are not in the circle,
4922 * otherwise there is no way to circle select joints alone */
4923 if ((is_point_done == false) && (points_proj_tot == 2) &&
4924 edge_inside_circle(data->mval_fl, data->radius, screen_co_a, screen_co_b))
4925 {
4926 if (data->select) {
4927 pchan->bone->flag |= BONE_SELECTED;
4928 }
4929 else {
4930 pchan->bone->flag &= ~BONE_SELECTED;
4931 }
4932 data->is_changed = true;
4933 }
4934
4935 data->is_changed |= is_point_done;
4936}
4937static bool pose_circle_select(const ViewContext *vc,
4938 const eSelectOp sel_op,
4939 const int mval[2],
4940 float rad)
4941{
4944 const bool select = (sel_op != SEL_OP_SUB);
4945
4946 view3d_userdata_circleselect_init(&data, vc, select, mval, rad);
4947
4948 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
4949 data.is_changed |= ED_pose_deselect_all(vc->obact, SEL_DESELECT, false);
4950 }
4951
4952 ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d); /* for foreach's screen/vert projection */
4953
4954 /* Treat bones as clipped segments (no joints). */
4957 &data,
4959
4960 if (data.is_changed) {
4962 }
4963 return data.is_changed;
4964}
4965
4969static bool armature_circle_doSelectJoint(void *user_data,
4970 EditBone *ebone,
4971 const float screen_co[2],
4972 bool head)
4973{
4974 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
4975
4976 if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
4977 if (head) {
4978 if (data->select) {
4979 ebone->flag |= BONE_ROOTSEL;
4980 }
4981 else {
4982 ebone->flag &= ~BONE_ROOTSEL;
4983 }
4984 }
4985 else {
4986 if (data->select) {
4987 ebone->flag |= BONE_TIPSEL;
4988 }
4989 else {
4990 ebone->flag &= ~BONE_TIPSEL;
4991 }
4992 }
4993 return true;
4994 }
4995 return false;
4996}
4998 EditBone *ebone,
4999 const float screen_co_a[2],
5000 const float screen_co_b[2])
5001{
5002 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
5003 const bArmature *arm = static_cast<const bArmature *>(data->vc->obedit->data);
5004 if (!(data->select ? EBONE_SELECTABLE(arm, ebone) : EBONE_VISIBLE(arm, ebone))) {
5005 return;
5006 }
5007
5008 /* When true, ignore in the next pass. */
5009 ebone->temp.i = false;
5010
5011 bool is_point_done = false;
5012 bool is_edge_done = false;
5013 int points_proj_tot = 0;
5014
5015 /* Project head location to screen-space. */
5016 if (screen_co_a[0] != IS_CLIPPED) {
5017 points_proj_tot++;
5018 if (armature_circle_doSelectJoint(data, ebone, screen_co_a, true)) {
5019 is_point_done = true;
5020 }
5021 }
5022
5023 /* Project tail location to screen-space. */
5024 if (screen_co_b[0] != IS_CLIPPED) {
5025 points_proj_tot++;
5026 if (armature_circle_doSelectJoint(data, ebone, screen_co_b, false)) {
5027 is_point_done = true;
5028 }
5029 }
5030
5031 /* check if the head and/or tail is in the circle
5032 * - the call to check also does the selection already
5033 */
5034
5035 /* only if the endpoints didn't get selected, deal with the middle of the bone too
5036 * It works nicer to only do this if the head or tail are not in the circle,
5037 * otherwise there is no way to circle select joints alone */
5038 if ((is_point_done == false) && (points_proj_tot == 2) &&
5039 edge_inside_circle(data->mval_fl, data->radius, screen_co_a, screen_co_b))
5040 {
5042 is_edge_done = true;
5043 data->is_changed = true;
5044 }
5045
5046 if (is_point_done || is_edge_done) {
5047 ebone->temp.i = true;
5048 }
5049
5050 data->is_changed |= is_point_done;
5051}
5053 EditBone *ebone,
5054 const float screen_co_a[2],
5055 const float screen_co_b[2])
5056{
5057 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
5058 bArmature *arm = static_cast<bArmature *>(data->vc->obedit->data);
5059
5060 if (!(data->select ? EBONE_SELECTABLE(arm, ebone) : EBONE_VISIBLE(arm, ebone))) {
5061 return;
5062 }
5063
5064 /* Set in the first pass, needed so circle select prioritizes joints. */
5065 if (ebone->temp.i != 0) {
5066 return;
5067 }
5068
5069 if (edge_inside_circle(data->mval_fl, data->radius, screen_co_a, screen_co_b)) {
5071 data->is_changed = true;
5072 }
5073}
5075 const eSelectOp sel_op,
5076 const int mval[2],
5077 float rad)
5078{
5080 bArmature *arm = static_cast<bArmature *>(vc->obedit->data);
5081
5082 const bool select = (sel_op != SEL_OP_SUB);
5083
5084 view3d_userdata_circleselect_init(&data, vc, select, mval, rad);
5085
5086 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
5087 data.is_changed |= ED_armature_edit_deselect_all_visible(vc->obedit);
5088 }
5089
5091
5092 /* Operate on fully visible (non-clipped) points. */
5095
5096 /* Operate on bones as segments clipped to the viewport bounds
5097 * (needed to handle bones with both points outside the view).
5098 * A separate pass is needed since clipped coordinates can't be used for selecting joints. */
5101 &data,
5103
5104 if (data.is_changed) {
5107 }
5108 return data.is_changed;
5109}
5110
5111static void do_circle_select_mball__doSelectElem(void *user_data,
5112 MetaElem *ml,
5113 const float screen_co[2])
5114{
5115 CircleSelectUserData *data = static_cast<CircleSelectUserData *>(user_data);
5116
5117 if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
5118 if (data->select) {
5119 ml->flag |= SELECT;
5120 }
5121 else {
5122 ml->flag &= ~SELECT;
5123 }
5124 data->is_changed = true;
5125 }
5126}
5127static bool mball_circle_select(const ViewContext *vc,
5128 const eSelectOp sel_op,
5129 const int mval[2],
5130 float rad)
5131{
5133
5134 const bool select = (sel_op != SEL_OP_SUB);
5135
5136 view3d_userdata_circleselect_init(&data, vc, select, mval, rad);
5137
5138 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
5139 data.is_changed |= BKE_mball_deselect_all(static_cast<MetaBall *>(vc->obedit->data));
5140 }
5141
5143
5146 return data.is_changed;
5147}
5148
5150 const eSelectOp sel_op,
5151 const int mval[2],
5152 const float rad)
5153{
5154 using namespace blender;
5155 Object *object = (vc->obedit ? vc->obedit : vc->obact);
5156 const Object *ob_eval = DEG_get_evaluated_object(vc->depsgraph, const_cast<Object *>(object));
5157 const GreasePencil &grease_pencil = *static_cast<GreasePencil *>(object->data);
5159 vc->scene->toolsettings, object);
5160
5161 return ed::greasepencil::selection_update(
5162 vc,
5163 sel_op,
5165 const IndexMask &mask,
5166 const StringRef attribute_name,
5167 IndexMaskMemory &memory) {
5169 const bke::greasepencil::Layer &layer = grease_pencil.layer(info.layer_index);
5170 const bke::crazyspace::GeometryDeformation deformation =
5171 bke::crazyspace::get_evaluated_grease_pencil_drawing_deformation(
5172 ob_eval, *object, info.layer_index, info.frame_number);
5173 const IndexMask visible_handle_elements =
5174 ed::greasepencil::retrieve_visible_bezier_handle_elements(
5175 *object, info.drawing, info.layer_index, selection_domain, memory);
5176 const float4x4 layer_to_world = layer.to_world_space(*ob_eval);
5178 layer_to_world);
5179
5180 return ed::curves::select_circle_mask(*vc,
5181 curves,
5182 deformation,
5183 projection,
5184 mask,
5185 visible_handle_elements,
5186 selection_domain,
5187 attribute_name,
5188 int2(mval),
5189 rad,
5190 memory);
5191 });
5192}
5193
5198 const ViewContext *vc,
5199 wmGenericUserData *wm_userdata,
5200 const eSelectOp sel_op,
5201 const int mval[2],
5202 float rad)
5203{
5204 using namespace blender;
5205 bool changed = false;
5207 switch (vc->obedit->type) {
5208 case OB_MESH:
5209 changed = mesh_circle_select(vc, wm_userdata, sel_op, mval, rad);
5210 break;
5211 case OB_CURVES_LEGACY:
5212 case OB_SURF:
5213 changed = nurbscurve_circle_select(vc, sel_op, mval, rad);
5214 break;
5215 case OB_LATTICE:
5216 changed = lattice_circle_select(vc, sel_op, mval, rad);
5217 break;
5218 case OB_ARMATURE:
5219 changed = armature_circle_select(vc, sel_op, mval, rad);
5220 if (changed) {
5222 }
5223 break;
5224 case OB_MBALL:
5225 changed = mball_circle_select(vc, sel_op, mval, rad);
5226 break;
5227 case OB_CURVES: {
5228 Curves &curves_id = *static_cast<Curves *>(vc->obedit->data);
5229 bke::CurvesGeometry &curves = curves_id.geometry.wrap();
5231 bke::crazyspace::get_evaluated_curves_deformation(*vc->depsgraph, *vc->obedit);
5232 const bke::AttrDomain selection_domain = bke::AttrDomain(curves_id.selection_domain);
5233 const float4x4 projection = ED_view3d_ob_project_mat_get(vc->rv3d, vc->obedit);
5234 const IndexRange elements(curves.attributes().domain_size(selection_domain));
5235 changed = ed::curves::select_circle(*vc,
5236 curves,
5237 deformation,
5238 projection,
5239 elements,
5240 elements,
5241 selection_domain,
5242 mval,
5243 rad,
5244 sel_op);
5245 if (changed) {
5246 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a
5247 * generic attribute for now. */
5248 DEG_id_tag_update(static_cast<ID *>(vc->obedit->data), ID_RECALC_GEOMETRY);
5250 }
5251 break;
5252 }
5253 case OB_GREASE_PENCIL:
5254 changed = grease_pencil_circle_select(vc, sel_op, mval, rad);
5255 break;
5256
5257 default:
5258 BLI_assert(0);
5259 break;
5260 }
5261
5262 if (changed) {
5263 DEG_id_tag_update(static_cast<ID *>(vc->obact->data), ID_RECALC_SELECT);
5265 }
5266 return changed;
5267}
5268
5269static bool object_circle_select(const ViewContext *vc,
5270 const eSelectOp sel_op,
5271 const int mval[2],
5272 float rad)
5273{
5275 Scene *scene = vc->scene;
5276 ViewLayer *view_layer = vc->view_layer;
5277 View3D *v3d = vc->v3d;
5278
5279 const float radius_squared = rad * rad;
5280 const float mval_fl[2] = {float(mval[0]), float(mval[1])};
5281
5282 bool changed = false;
5283 if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
5284 changed |= object_deselect_all_visible(vc->scene, vc->view_layer, vc->v3d);
5285 }
5286 const bool select = (sel_op != SEL_OP_SUB);
5287 const int select_flag = select ? BASE_SELECTED : 0;
5288 BKE_view_layer_synced_ensure(scene, view_layer);
5290 if (BASE_SELECTABLE(v3d, base) && ((base->flag & BASE_SELECTED) != select_flag)) {
5291 float screen_co[2];
5293 base->object->object_to_world().location(),
5294 screen_co,
5296 {
5297 if (len_squared_v2v2(mval_fl, screen_co) <= radius_squared) {
5300 changed = true;
5301 }
5302 }
5303 }
5304 }
5305
5306 return changed;
5307}
5308
5309/* not a real operator, only for circle test */
5310static void view3d_circle_select_recalc(void *user_data)
5311{
5312 bContext *C = static_cast<bContext *>(user_data);
5313 Object *obedit_active = CTX_data_edit_object(C);
5314
5315 if (obedit_active) {
5316 switch (obedit_active->type) {
5317 case OB_MESH: {
5320 vc.scene, vc.view_layer, vc.v3d, vc.obact->type, vc.obact->mode, ob_iter)
5321 {
5325 }
5327 break;
5328 }
5329
5330 default: {
5331 /* TODO: investigate if this is needed for other object types. */
5333 break;
5334 }
5335 }
5336 }
5337}
5338
5340{
5341 int result = WM_gesture_circle_modal(C, op, event);
5342 if (result & OPERATOR_FINISHED) {
5344 }
5345 return result;
5346}
5347
5353
5355{
5357 const int radius = RNA_int_get(op->ptr, "radius");
5358 const int mval[2] = {RNA_int_get(op->ptr, "x"), RNA_int_get(op->ptr, "y")};
5359
5360 /* Allow each selection type to allocate their own data that's used between executions. */
5361 wmGesture *gesture = static_cast<wmGesture *>(op->customdata); /* nullptr when non-modal. */
5362 wmGenericUserData wm_userdata_buf = {nullptr, nullptr, false};
5363 wmGenericUserData *wm_userdata = gesture ? &gesture->user_data : &wm_userdata_buf;
5364
5365 const eSelectOp sel_op = ED_select_op_modal(
5366 static_cast<eSelectOp>(RNA_enum_get(op->ptr, "mode")), WM_gesture_is_modal_first(gesture));
5367
5369
5370 Object *obact = vc.obact;
5371 Object *obedit = vc.obedit;
5372
5373 if (obedit || BKE_paint_select_elem_test(obact) || (obact && (obact->mode & OB_MODE_POSE))) {
5375 if (obedit == nullptr) {
5377 }
5378 else {
5379 if (vc.obedit->type == OB_MESH) {
5381 }
5382 }
5383
5385 vc.scene, vc.view_layer, vc.v3d, obact->type, obact->mode, ob_iter)
5386 {
5388
5389 obact = vc.obact;
5390 obedit = vc.obedit;
5391
5392 if (obedit) {
5393 obedit_circle_select(C, &vc, wm_userdata, sel_op, mval, float(radius));
5394 }
5395 else if (BKE_paint_select_face_test(obact)) {
5396 paint_facesel_circle_select(&vc, wm_userdata, sel_op, mval, float(radius));
5397 }
5398 else if (BKE_paint_select_vert_test(obact)) {
5399 paint_vertsel_circle_select(&vc, wm_userdata, sel_op, mval, float(radius));
5400 }
5401 else if (BKE_paint_select_grease_pencil_test(obact)) {
5402 grease_pencil_circle_select(&vc, sel_op, mval, float(radius));
5403 }
5404 else if (obact->mode & OB_MODE_POSE) {
5405 pose_circle_select(&vc, sel_op, mval, float(radius));
5407 }
5408 else {
5409 BLI_assert(0);
5410 }
5411 }
5413 }
5414 else if (obact && (obact->mode & OB_MODE_PARTICLE_EDIT)) {
5415 if (PE_circle_select(C, wm_userdata, sel_op, mval, float(radius))) {
5416 return OPERATOR_FINISHED;
5417 }
5418 return OPERATOR_CANCELLED;
5419 }
5420 else if (obact && obact->mode & OB_MODE_SCULPT) {
5421 return OPERATOR_CANCELLED;
5422 }
5423 else if (Object *obact_pose = (obact && (obact->mode & OB_MODE_WEIGHT_PAINT)) ?
5425 nullptr)
5426 {
5427 ED_view3d_viewcontext_init_object(&vc, obact_pose);
5428 pose_circle_select(&vc, sel_op, mval, float(radius));
5430 }
5431 else {
5432 if (object_circle_select(&vc, sel_op, mval, float(radius))) {
5435
5437 }
5438 }
5439
5440 /* Otherwise this is freed by the gesture. */
5441 if (wm_userdata == &wm_userdata_buf) {
5442 WM_generic_user_data_free(wm_userdata);
5443 }
5444 else {
5445 EditSelectBuf_Cache *esel = static_cast<EditSelectBuf_Cache *>(wm_userdata->data);
5446 if (esel && esel->select_bitmap) {
5447 MEM_freeN(esel->select_bitmap);
5448 esel->select_bitmap = nullptr;
5449 }
5450 }
5451
5452 return OPERATOR_FINISHED;
5453}
5454
5456{
5457 ot->name = "Circle Select";
5458 ot->description = "Select items using circle selection";
5459 ot->idname = "VIEW3D_OT_select_circle";
5460
5467
5468 /* flags */
5469 ot->flag = OPTYPE_UNDO;
5470
5471 /* properties */
5474}
5475
C++ functions to deal with Armature collections (i.e. the successor of bone layers).
Blender kernel action and pose functionality.
#define PBONE_SELECTABLE(arm, bone)
#define CTX_DATA_BEGIN(C, Type, instance, member)
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
wmWindow * CTX_wm_window(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Object * CTX_data_edit_object(const bContext *C)
Main * CTX_data_main(const bContext *C)
RegionView3D * CTX_wm_region_view3d(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
#define CTX_DATA_END
View3D * CTX_wm_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
bool BKE_nurbList_flag_set_from_flag(ListBase *editnurb, uint8_t from_flag, uint8_t flag)
Definition curve.cc:4374
ListBase * BKE_curve_editNurbs_get(Curve *cu)
Definition curve.cc:398
void BKE_curve_nurb_vert_active_validate(Curve *cu)
Definition curve.cc:5065
void BKE_nurbList_flag_set(ListBase *editnurb, uint8_t flag, bool set)
Definition curve.cc:4339
Low-level operations for curves.
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:63
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:578
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:1621
bool BKE_paint_select_elem_test(const Object *ob)
Definition paint.cc:1632
bool BKE_paint_select_vert_test(const Object *ob)
Definition paint.cc:1614
bool BKE_paint_select_face_test(const Object *ob)
Definition paint.cc:1607
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:97
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
#define BLI_BITMAP_TEST_BOOL(_bitmap, _index)
Definition BLI_bitmap.h:75
unsigned int BLI_bitmap
Definition BLI_bitmap.h:17
BLI_INLINE void BLI_endian_switch_uint32(unsigned int *val) ATTR_NONNULL(1)
struct GSet GSet
Definition BLI_ghash.h:341
GSet * BLI_gset_ptr_new(const char *info)
bool BLI_gset_haskey(const GSet *gs, const void *key) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:1004
void BLI_gset_insert(GSet *gs, void *key)
Definition BLI_ghash.c:959
void BLI_gset_free(GSet *gs, GSetKeyFreeFP keyfreefp)
Definition BLI_ghash.c:1034
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)
#define LISTBASE_FOREACH(type, var, list)
void BLI_freelinkN(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:269
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
void void BLI_freelistN(struct ListBase *listbase) ATTR_NONNULL(1)
Definition listbase.cc:496
void void BLI_listbase_sort(struct ListBase *listbase, int(*cmp)(const void *, const void *)) ATTR_NONNULL(1
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
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:289
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.c:470
bool BLI_rcti_isect_pt(const struct rcti *rect, int x, int y)
bool BLI_rctf_isect_segment(const struct rctf *rect, const float s1[2], const float s2[2])
void BLI_rctf_rcti_copy(struct rctf *dst, const struct rcti *src)
bool BLI_rcti_is_empty(const struct rcti *rect)
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
unsigned int uint
#define UNPACK2(a)
#define UNUSED_VARS_NDEBUG(...)
#define SET_FLAG_FROM_TEST(value, test, flag)
#define ELEM(...)
#define STREQ(a, b)
void DEG_id_tag_update(ID *id, unsigned int flags)
Object * DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object)
@ ID_RECALC_SELECT
Definition DNA_ID.h:1068
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
@ ID_RECALC_BASE_FLAGS
Definition DNA_ID.h:1071
#define MAX_ID_NAME
Definition DNA_ID.h:377
@ ID_TAG_DOIT
Definition DNA_ID.h:1003
@ BONE_ROOTSEL
@ BONE_SELECTED
@ BONE_UNSELECTABLE
@ BONE_DONE
@ BONE_TIPSEL
eBezTriple_Flag
@ BEZT_FLAG_TEMP_TAG
#define GPENCIL_ANY_MODE(gpd)
@ 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_CURVES_LEGACY
@ OB_GPENCIL_LEGACY
@ OB_CURVES
#define BASE_SELECTED(v3d, base)
@ SCE_OBJECT_MODE_LOCK
@ SCE_SELECT_FACE
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
#define BASE_SELECTABLE(v3d, base)
#define BASE_VISIBLE(v3d, base)
@ USER_GPU_FLAG_NO_DEPT_PICK
#define UI_SCALE_FAC
@ 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 EBONE_VISIBLE(arm, ebone)
#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:582
void paintvert_flush_flags(Object *ob)
Definition editface.cc:822
void paintvert_tag_select_update(bContext *C, Object *ob)
Definition editface.cc:1081
ViewContext em_setup_viewcontext(bContext *C)
void EDBM_selectmode_flush(BMEditMesh *em)
bool paintface_mouse_select(bContext *C, const int mval[2], const SelectPick_Params *params, Object *ob)
Definition editface.cc:756
void paintface_flush_flags(bContext *C, Object *ob, bool flush_selection, bool flush_hidden)
Definition editface.cc:49
bool paintvert_deselect_all_visible(Object *ob, int action, bool flush_flags)
Definition editface.cc:1087
bool paintface_deselect_all_visible(bContext *C, Object *ob, int action, bool flush_flags)
Definition editface.cc:658
bool ED_mesh_pick_vert(bContext *C, Object *ob, const int mval[2], uint dist_px, bool use_zbuf, uint *r_index)
bool EDBM_select_pick(bContext *C, const int mval[2], const SelectPick_Params *params)
void ED_outliner_select_sync_from_object_tag(bContext *C)
void ED_outliner_select_sync_from_edit_bone_tag(bContext *C)
void ED_outliner_select_sync_from_pose_bone_tag(bContext *C)
bool PE_mouse_particles(bContext *C, const int mval[2], const SelectPick_Params *params)
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)
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)
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)
#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)
@ SEL_DESELECT
void ED_select_pick_params_from_operator(PointerRNA *ptr, SelectPick_Params *params) ATTR_NONNULL(1
eSelectOp ED_select_op_from_operator(PointerRNA *ptr) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
#define SEL_OP_USE_OUTSIDE(sel_op)
void std::string ED_select_pick_get_name(wmOperatorType *ot, PointerRNA *ptr)
eSelectOp
@ SEL_OP_ADD
@ SEL_OP_SUB
@ SEL_OP_SET
@ SEL_OP_AND
@ SEL_OP_XOR
void view3d_opengl_select_cache_begin()
#define V3D_PROJ_TEST_CLIP_CONTENT_DEFAULT
Definition ED_view3d.hh:302
#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)
eV3DProjTest
Definition ED_view3d.hh:265
@ V3D_PROJ_TEST_CLIP_NEAR
Definition ED_view3d.hh:269
@ V3D_PROJ_TEST_CLIP_BB
Definition ED_view3d.hh:267
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 ED_view3d_init_mats_rv3d(const Object *ob, RegionView3D *rv3d)
@ V3D_PROJ_RET_OK
Definition ED_view3d.hh:243
void lattice_foreachScreenVert(const ViewContext *vc, void(*func)(void *user_data, BPoint *bp, const float screen_co[2]), void *user_data, eV3DProjTest clip_flag)
int view3d_opengl_select(const ViewContext *vc, GPUSelectBuffer *buffer, const rcti *input, eV3DSelectMode select_mode, eV3DSelectObjectFilter select_filter)
#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:239
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)
void view3d_operator_needs_opengl(const bContext *C)
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:296
eV3DProjStatus ED_view3d_project_base(const ARegion *region, Base *base, float r_co[2])
eV3DSelectMode
Definition ED_view3d.hh:914
@ VIEW3D_SELECT_PICK_ALL
Definition ED_view3d.hh:918
@ VIEW3D_SELECT_PICK_NEAREST
Definition ED_view3d.hh:920
@ VIEW3D_SELECT_ALL
Definition ED_view3d.hh:916
int view3d_opengl_select_ex(const ViewContext *vc, GPUSelectBuffer *buffer, const rcti *input, eV3DSelectMode select_mode, eV3DSelectObjectFilter select_filter, bool do_material_slot_selection)
#define XRAY_FLAG_ENABLED(v3d)
blender::float4x4 ED_view3d_ob_project_mat_get_from_obmat(const RegionView3D *rv3d, const blender::float4x4 &obmat)
void view3d_opengl_select_cache_end()
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:923
@ VIEW3D_SELECT_FILTER_NOP
Definition ED_view3d.hh:925
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)
@ GPU_DRIVER_OFFICIAL
bool GPU_type_matches_ex(eGPUDeviceType device, eGPUOSType os, eGPUDriverType driver, eGPUBackendType backend)
@ GPU_OS_ANY
@ GPU_DEVICE_ATI
@ GPU_DEVICE_NVIDIA
Contains defines and structs used throughout the imbuf module.
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
PropertyFlag
Definition RNA_types.hh:201
@ PROP_ENUM_NO_TRANSLATE
Definition RNA_types.hh:321
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
@ PROP_HIDDEN
Definition RNA_types.hh:239
#define C
Definition RandGen.cpp:29
int UI_icon_from_id(const ID *id)
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:198
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DATA
Definition WM_types.hh:475
#define NC_MOVIECLIP
Definition WM_types.hh:364
#define ND_OB_SELECT
Definition WM_types.hh:409
#define NC_SCENE
Definition WM_types.hh:345
#define ND_SELECT
Definition WM_types.hh:474
#define ND_BONE_ACTIVE
Definition WM_types.hh:426
@ WM_OP_INVOKE_DEFAULT
Definition WM_types.hh:218
#define ND_BONE_SELECT
Definition WM_types.hh:427
#define NC_OBJECT
Definition WM_types.hh:346
bool ED_armature_edit_deselect_all_visible_multi_ex(const Span< Base * > bases)
bool ED_armature_edit_deselect_all_visible(Object *obedit)
Base * ED_armature_base_and_bone_from_select_buffer(const Span< Base * > bases, const uint select_id, Bone **r_bone)
bool ED_armature_edit_select_pick_bone(bContext *C, Base *basact, EditBone *ebone, const int selmask, const SelectPick_Params *params)
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_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)
@ 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
void BM_face_select_set(BMesh *bm, BMFace *f, const bool select)
Select Face.
void BM_mesh_select_mode_flush_ex(BMesh *bm, const short selectmode, eBMSelectionFlushFLags flags)
Select Mode Flush.
void BM_vert_select_set(BMesh *bm, BMVert *v, const bool select)
Select Vert.
void BM_edge_select_set(BMesh *bm, BMEdge *e, const bool select)
Select Edge.
@ BM_SELECT_LEN_FLUSH_RECALC_ALL
@ BM_SELECT_LEN_FLUSH_RECALC_NOTHING
ATTR_WARN_UNUSED_RESULT const BMVert * v2
void init()
unsigned int U
Definition btGjkEpa3.h:78
bool is_empty() const
Definition BLI_array.hh:253
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:574
constexpr IndexRange index_range() const
Definition BLI_span.hh:671
constexpr void copy_from(Span< T > values) const
Definition BLI_span.hh:726
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:138
constexpr const T * data() const
Definition BLI_span.hh:216
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr bool is_empty() const
Definition BLI_span.hh:261
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
bke::CurvesGeometry & strokes_for_write()
const bke::CurvesGeometry & strokes() const
local_group_size(16, 16) .push_constant(Type b
#define SELECT
const Depsgraph * depsgraph
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:2526
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
static bool is_inside(int x, int y, int cols, int rows)
Definition filesel.cc:768
IndexRange range
blender::bke::AttrDomain ED_grease_pencil_selection_domain_get(const ToolSettings *tool_settings, const Object *object)
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
ccl_device_inline float4 select(const int4 mask, const float4 a, const float4 b)
static ulong * next
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)
void ED_armature_pose_select_in_wpaint_mode(const Scene *scene, ViewLayer *view_layer, Base *base_select)
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)
bool ED_armature_pose_select_pick_bone(const Scene *scene, ViewLayer *view_layer, View3D *v3d, Object *ob, Bone *bone, const SelectPick_Params *params)
void ED_pose_bone_select_tag_update(Object *ob)
bool ED_pose_deselect_all(Object *ob, int select_mode, const bool ignore_visibility)
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:29
#define min(a, b)
Definition sort.c:32
unsigned char uint8_t
Definition stdint.h:78
short selectmode
int totfacesel
int totvertsel
int totedgesel
uint8_t f1
struct Base * next
short flag
struct Object * object
blender::MutableSpan< bool > select_vert
const ViewContext * vc
eBezTriple_Flag select_flag
blender::MutableSpan< bool > select_vert
const ViewContext * vc
eBezTriple_Flag select_flag
blender::StringRef selection_attribute_name
blender::ed::curves::FindClosestData elem
blender::ed::curves::FindClosestData elem
blender::bke::greasepencil::Drawing * drawing
blender::StringRef selection_attribute_name
CurvesGeometry geometry
char selection_domain
char name[64]
union EditBone::@4 temp
EditBone * ebone
BLI_bitmap * select_bitmap
const char * name
Definition RNA_types.hh:510
GPUSelectStorage storage
Definition GPU_select.hh:46
unsigned int id
Definition GPU_select.hh:33
Definition DNA_ID.h:413
int tag
Definition DNA_ID.h:434
char name[66]
Definition DNA_ID.h:425
blender::MutableSpan< bool > select_vert
eBezTriple_Flag select_flag
const ViewContext * vc
void * last
void * first
ListBase * editelems
struct MetaElem * next
struct bPose * pose
ObjectRuntimeHandle * runtime
float viewmat[4][4]
struct ToolSettings * toolsettings
char idname[MAX_ID_NAME - 2]
RegionView3D * rv3d
Definition ED_view3d.hh:76
ARegion * region
Definition ED_view3d.hh:73
Scene * scene
Definition ED_view3d.hh:69
Main * bmain
Definition ED_view3d.hh:63
BMEditMesh * em
Definition ED_view3d.hh:77
wmWindow * win
Definition ED_view3d.hh:75
ViewLayer * view_layer
Definition ED_view3d.hh:70
bContext * C
Definition ED_view3d.hh:62
View3D * v3d
Definition ED_view3d.hh:74
Object * obact
Definition ED_view3d.hh:71
Object * obedit
Definition ED_view3d.hh:72
Depsgraph * depsgraph
Definition ED_view3d.hh:68
ListBase * edbo
struct Bone * bone
ListBase chanbase
float xmax
float xmin
float ymax
float ymin
int mval[2]
Definition WM_types.hh:728
wmGenericUserDataFreeFn free_fn
Definition WM_types.hh:143
wmGenericUserData user_data
Definition WM_types.hh:632
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1036
std::string(* get_name)(wmOperatorType *ot, PointerRNA *ptr)
Definition WM_types.hh:1068
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
PropertyRNA * prop
Definition WM_types.hh:1092
StructRNA * srna
Definition WM_types.hh:1080
void(* cancel)(bContext *C, wmOperator *op)
Definition WM_types.hh:1028
struct PointerRNA * ptr
static bool edbm_backbuf_check_and_select_faces_obmode(Mesh *mesh, EditSelectBuf_Cache *esel, const eSelectOp sel_op)
static void do_lasso_select_armature__doSelectBone(void *user_data, EditBone *ebone, const float screen_co_a[2], const float screen_co_b[2])
void VIEW3D_OT_select(wmOperatorType *ot)
float ED_view3d_select_dist_px()
static bool do_lasso_select_pose(const ViewContext *vc, const Span< int2 > mcoords, const eSelectOp sel_op)
static bool do_paintvert_box_select(const ViewContext *vc, wmGenericUserData *wm_userdata, const rcti *rect, const eSelectOp sel_op)
static void do_lasso_tag_pose(const ViewContext *vc, const Span< int2 > mcoords)
static bool edbm_backbuf_check_and_select_edges(EditSelectBuf_Cache *esel, Depsgraph *depsgraph, Object *ob, BMEditMesh *em, const eSelectOp sel_op)
static bool edbm_backbuf_check_and_select_faces(EditSelectBuf_Cache *esel, Depsgraph *depsgraph, Object *ob, BMEditMesh *em, const eSelectOp sel_op)
static 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 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 bone_mouse_select_menu(bContext *C, const blender::Span< GPUSelectResult > hit_results, const bool is_editmode, const SelectPick_Params *params)
static bool pose_circle_select(const ViewContext *vc, const eSelectOp sel_op, const int mval[2], float rad)
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 int view3d_box_select_exec(bContext *C, wmOperator *op)
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 int view3d_lasso_select_exec(bContext *C, wmOperator *op)
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)
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 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)
static int view3d_circle_select_exec(bContext *C, wmOperator *op)
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 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 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 int view3d_circle_select_modal(bContext *C, wmOperator *op, const wmEvent *event)
static SelMenuItemF object_mouse_select_menu_data[SEL_MENU_SIZE]
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_if_supported, const bool do_material_slot_selection)
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 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 void do_mesh_box_select__doSelectVert(void *user_data, BMVert *eve, const float screen_co[2], int)
static const EnumPropertyItem * object_select_menu_enum_itemf(bContext *C, PointerRNA *, PropertyRNA *, bool *r_free)
static bool nurbscurve_circle_select(const ViewContext *vc, const eSelectOp sel_op, const int mval[2], float rad)
static void do_lasso_select_mball__doSelectElem(void *user_data, MetaElem *ml, const float screen_co[2])
static bool ed_curves_select_pick(bContext &C, const int mval[2], const SelectPick_Params &params)
#define SEL_MENU_SIZE
void VIEW3D_OT_select_menu(wmOperatorType *ot)
static bool object_deselect_all_except(const Scene *scene, ViewLayer *view_layer, Base *b)
static bool view3d_lasso_select(bContext *C, ViewContext *vc, const Span< int2 > mcoords, const eSelectOp sel_op)
static bool do_meta_box_select(const ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
static int selectbuffer_ret_hits_15(blender::MutableSpan< GPUSelectResult >, const int hits15)
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 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 int object_select_menu_exec(bContext *C, wmOperator *op)
static bool do_pose_tag_select_op_exec(blender::MutableSpan< Base * > bases, const eSelectOp sel_op)
static bool do_armature_box_select(const ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
static bool armature_circle_select(const ViewContext *vc, const eSelectOp sel_op, const int mval[2], float rad)
static void editselect_buf_cache_init(const ViewContext *vc, short select_mode)
static int mixed_bones_object_selectbuffer_extended(const ViewContext *vc, GPUSelectBuffer *buffer, const int mval[2], eV3DSelectObjectFilter select_filter, bool use_cycle, bool enumerate, bool *r_do_nearest)
static bool edbm_backbuf_check_and_select_verts(EditSelectBuf_Cache *esel, Depsgraph *depsgraph, Object *ob, BMEditMesh *em, const eSelectOp sel_op)
static void do_lasso_select_meshobject__doSelectVert(void *user_data, const float screen_co[2], int index)
static void do_lasso_select_mesh__doSelectFace(void *user_data, BMFace *efa, const float screen_co[2], int)
static void latticecurve_circle_doSelect(void *user_data, BPoint *bp, const float screen_co[2])
static void view3d_userdata_boxselect_init(BoxSelectUserData *r_data, const ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
static bool do_lasso_select_paintvert(const ViewContext *vc, wmGenericUserData *wm_userdata, const Span< int2 > mcoords, const eSelectOp sel_op)
Base * ED_view3d_give_base_under_cursor(bContext *C, const int mval[2])
static void do_lasso_select_mesh__doSelectEdge_pass0(void *user_data, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int index)
static void deselect_all_tracks(MovieTracking *tracking)
static void editselect_buf_cache_free_voidp(void *esel_voidp)
static void do_circle_select_mball__doSelectElem(void *user_data, MetaElem *ml, const float screen_co[2])
static bool mesh_circle_select(const ViewContext *vc, wmGenericUserData *wm_userdata, eSelectOp sel_op, const int mval[2], float rad)
static bool ed_wpaint_vertex_select_pick(bContext *C, const int mval[2], const SelectPick_Params *params, Object *obact)
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])
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 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 opengl_bone_select_buffer_cmp(const void *sel_a_p, const void *sel_b_p)
static int view3d_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
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 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 int bone_select_menu_exec(bContext *C, wmOperator *op)
static bool pchan_circle_doSelectJoint(void *user_data, bPoseChannel *pchan, const float screen_co[2])
static int view3d_select_exec(bContext *C, wmOperator *op)
static void editselect_buf_cache_init_with_generic_userdata(wmGenericUserData *wm_userdata, const ViewContext *vc, short select_mode)
bool WM_cursor_test_motion_and_update(const int mval[2])
void WM_main_add_notifier(uint type, void *reference)
int WM_operator_name_call_ptr(bContext *C, wmOperatorType *ot, wmOperatorCallContext context, PointerRNA *properties, const wmEvent *event)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
PointerRNA * ptr
Definition wm_files.cc:4126
wmOperatorType * ot
Definition wm_files.cc:4125
bool WM_gesture_is_modal_first(const wmGesture *gesture)
void WM_gesture_box_cancel(bContext *C, wmOperator *op)
int WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_circle_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Array< int2 > WM_gesture_lasso_path_to_array(bContext *, wmOperator *op)
void WM_gesture_circle_cancel(bContext *C, wmOperator *op)
int WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_circle_modal(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event)
void WM_gesture_lasso_cancel(bContext *C, wmOperator *op)
int 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)
int WM_operator_flag_only_pass_through_on_press(int retval, const wmEvent *event)
int 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