3D View: re-use select id buffer for circle select on cursor-motion
[blender.git] / source / blender / editors / space_view3d / view3d_select.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2008 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup spview3d
22  */
23
24 #include <string.h>
25 #include <stdio.h>
26 #include <math.h>
27 #include <float.h>
28 #include <assert.h>
29
30 #include "DNA_action_types.h"
31 #include "DNA_armature_types.h"
32 #include "DNA_curve_types.h"
33 #include "DNA_meta_types.h"
34 #include "DNA_mesh_types.h"
35 #include "DNA_meshdata_types.h"
36 #include "DNA_object_types.h"
37 #include "DNA_scene_types.h"
38 #include "DNA_tracking_types.h"
39 #include "DNA_gpencil_types.h"
40
41 #include "MEM_guardedalloc.h"
42
43 #include "BLI_array.h"
44 #include "BLI_bitmap.h"
45 #include "BLI_math.h"
46 #include "BLI_lasso_2d.h"
47 #include "BLI_rect.h"
48 #include "BLI_linklist.h"
49 #include "BLI_listbase.h"
50 #include "BLI_string.h"
51 #include "BLI_utildefines.h"
52
53 #ifdef __BIG_ENDIAN__
54 #  include "BLI_endian_switch.h"
55 #endif
56
57 /* vertex box select */
58 #include "IMB_imbuf_types.h"
59 #include "IMB_imbuf.h"
60 #include "BKE_global.h"
61 #include "BKE_main.h"
62
63 #include "BKE_armature.h"
64 #include "BKE_context.h"
65 #include "BKE_curve.h"
66 #include "BKE_layer.h"
67 #include "BKE_mball.h"
68 #include "BKE_mesh.h"
69 #include "BKE_object.h"
70 #include "BKE_paint.h"
71 #include "BKE_editmesh.h"
72 #include "BKE_scene.h"
73 #include "BKE_tracking.h"
74 #include "BKE_workspace.h"
75
76 #include "DEG_depsgraph.h"
77
78 #include "WM_api.h"
79 #include "WM_types.h"
80
81 #include "RNA_access.h"
82 #include "RNA_define.h"
83 #include "RNA_enum_types.h"
84
85 #include "ED_armature.h"
86 #include "ED_curve.h"
87 #include "ED_lattice.h"
88 #include "ED_particle.h"
89 #include "ED_mesh.h"
90 #include "ED_object.h"
91 #include "ED_screen.h"
92 #include "ED_select_buffer_utils.h"
93 #include "ED_select_utils.h"
94 #include "ED_sculpt.h"
95 #include "ED_mball.h"
96 #include "ED_gpencil.h"
97
98 #include "UI_interface.h"
99
100 #include "GPU_draw.h"
101 #include "GPU_glew.h"
102 #include "GPU_matrix.h"
103
104 #include "view3d_intern.h" /* own include */
105
106 // #include "PIL_time_utildefines.h"
107
108 /* -------------------------------------------------------------------- */
109 /** \name Public Utilities
110  * \{ */
111
112 float ED_view3d_select_dist_px(void)
113 {
114   return 75.0f * U.pixelsize;
115 }
116
117 /* TODO: should return whether there is valid context to continue */
118 void ED_view3d_viewcontext_init(bContext *C, ViewContext *vc)
119 {
120   memset(vc, 0, sizeof(ViewContext));
121   vc->C = C;
122   vc->ar = CTX_wm_region(C);
123   vc->bmain = CTX_data_main(C);
124   vc->depsgraph = CTX_data_depsgraph(C);
125   vc->scene = CTX_data_scene(C);
126   vc->view_layer = CTX_data_view_layer(C);
127   vc->v3d = CTX_wm_view3d(C);
128   vc->win = CTX_wm_window(C);
129   vc->rv3d = CTX_wm_region_view3d(C);
130   vc->obact = CTX_data_active_object(C);
131   vc->obedit = CTX_data_edit_object(C);
132 }
133
134 void ED_view3d_viewcontext_init_object(ViewContext *vc, Object *obact)
135 {
136   vc->obact = obact;
137   if (vc->obedit) {
138     BLI_assert(BKE_object_is_in_editmode(obact));
139     vc->obedit = obact;
140     if (vc->em) {
141       vc->em = BKE_editmesh_from_object(vc->obedit);
142     }
143   }
144 }
145
146 /** \} */
147
148 /* -------------------------------------------------------------------- */
149 /** \name Internal Object Utilities
150  * \{ */
151
152 static bool object_deselect_all_visible(ViewLayer *view_layer, View3D *v3d)
153 {
154   bool changed = false;
155   for (Base *base = view_layer->object_bases.first; base; base = base->next) {
156     if (base->flag & BASE_SELECTED) {
157       if (BASE_SELECTABLE(v3d, base)) {
158         ED_object_base_select(base, BA_DESELECT);
159         changed = true;
160       }
161     }
162   }
163   return changed;
164 }
165
166 /* deselect all except b */
167 static bool object_deselect_all_except(ViewLayer *view_layer, Base *b)
168 {
169   bool changed = false;
170   for (Base *base = view_layer->object_bases.first; base; base = base->next) {
171     if (base->flag & BASE_SELECTED) {
172       if (b != base) {
173         ED_object_base_select(base, BA_DESELECT);
174         changed = true;
175       }
176     }
177   }
178   return changed;
179 }
180
181 /** \} */
182
183 /* -------------------------------------------------------------------- */
184 /** \name Internal Edit-Mesh Select Buffer Wrapper
185  *
186  * Avoid duplicate code when using edit-mode selection,
187  * actual logic is handled outside of this function.
188  *
189  * \note Currently this #EDBMSelectID_Context which is mesh specific
190  * however the logic could also be used for non-meshes too.
191  *
192  * \{ */
193
194 struct EditSelectBuf_Cache {
195   Base **bases;
196   uint bases_len;
197   struct EDBMSelectID_Context *sel_id_ctx;
198   BLI_bitmap *select_bitmap;
199 };
200
201 static void editselect_buf_cache_init(struct EditSelectBuf_Cache *esel, ViewContext *vc)
202 {
203   if (vc->obedit) {
204     esel->bases = BKE_view_layer_array_from_bases_in_edit_mode(
205         vc->view_layer, vc->v3d, &esel->bases_len);
206   }
207   else {
208     /* Use for paint modes, currently only a single object at a time. */
209     if (vc->obact) {
210       esel->bases = MEM_mallocN(sizeof(esel->bases), __func__);
211       esel->bases[0] = BKE_view_layer_base_find(vc->view_layer, vc->obact);
212       esel->bases_len = 1;
213     }
214     else {
215       esel->bases = NULL;
216       esel->bases_len = 0;
217     }
218   }
219   esel->sel_id_ctx = EDBM_select_id_context_create(
220       vc, esel->bases, esel->bases_len, vc->scene->toolsettings->selectmode);
221   for (int i = 0; i < esel->bases_len; i++) {
222     esel->bases[i]->object->runtime.select_id = i;
223   }
224 }
225
226 static void editselect_buf_cache_free(struct EditSelectBuf_Cache *esel)
227 {
228   if (esel->sel_id_ctx) {
229     EDBM_select_id_context_destroy(esel->sel_id_ctx);
230   }
231   MEM_SAFE_FREE(esel->select_bitmap);
232   MEM_SAFE_FREE(esel->bases);
233 }
234
235 static void editselect_buf_cache_free_voidp(void *esel_voidp)
236 {
237   editselect_buf_cache_free(esel_voidp);
238   MEM_freeN(esel_voidp);
239 }
240
241 static void editselect_buf_cache_init_with_generic_userdata(wmGenericUserData *wm_userdata,
242                                                             ViewContext *vc)
243 {
244   struct EditSelectBuf_Cache *esel = MEM_callocN(sizeof(*esel), __func__);
245   wm_userdata->data = esel;
246   wm_userdata->free_fn = editselect_buf_cache_free_voidp;
247   wm_userdata->use_free = true;
248   editselect_buf_cache_init(esel, vc);
249 }
250
251 /** \} */
252
253 /* -------------------------------------------------------------------- */
254 /** \name Internal Edit-Mesh Utilities
255  * \{ */
256
257 static bool edbm_backbuf_check_and_select_verts(struct EditSelectBuf_Cache *esel,
258                                                 Object *ob,
259                                                 BMEditMesh *em,
260                                                 const eSelectOp sel_op)
261 {
262   BMVert *eve;
263   BMIter iter;
264   bool changed = false;
265
266   const BLI_bitmap *select_bitmap = esel->select_bitmap;
267   uint index = EDBM_select_id_context_offset_for_object_elem(
268       esel->sel_id_ctx, ob->runtime.select_id, BM_VERT);
269
270   BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
271     if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
272       const bool is_select = BM_elem_flag_test(eve, BM_ELEM_SELECT);
273       const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index);
274       const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
275       if (sel_op_result != -1) {
276         BM_vert_select_set(em->bm, eve, sel_op_result);
277         changed = true;
278       }
279     }
280     index++;
281   }
282   return changed;
283 }
284
285 static bool edbm_backbuf_check_and_select_edges(struct EditSelectBuf_Cache *esel,
286                                                 Object *ob,
287                                                 BMEditMesh *em,
288                                                 const eSelectOp sel_op)
289 {
290   BMEdge *eed;
291   BMIter iter;
292   bool changed = false;
293
294   const BLI_bitmap *select_bitmap = esel->select_bitmap;
295   uint index = EDBM_select_id_context_offset_for_object_elem(
296       esel->sel_id_ctx, ob->runtime.select_id, BM_EDGE);
297
298   BM_ITER_MESH (eed, &iter, em->bm, BM_EDGES_OF_MESH) {
299     if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) {
300       const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT);
301       const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index);
302       const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
303       if (sel_op_result != -1) {
304         BM_edge_select_set(em->bm, eed, sel_op_result);
305         changed = true;
306       }
307     }
308     index++;
309   }
310   return changed;
311 }
312
313 static bool edbm_backbuf_check_and_select_faces(struct EditSelectBuf_Cache *esel,
314                                                 Object *ob,
315                                                 BMEditMesh *em,
316                                                 const eSelectOp sel_op)
317 {
318   BMFace *efa;
319   BMIter iter;
320   bool changed = false;
321
322   const BLI_bitmap *select_bitmap = esel->select_bitmap;
323   uint index = EDBM_select_id_context_offset_for_object_elem(
324       esel->sel_id_ctx, ob->runtime.select_id, BM_FACE);
325
326   BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
327     if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
328       const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT);
329       const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index);
330       const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
331       if (sel_op_result != -1) {
332         BM_face_select_set(em->bm, efa, sel_op_result);
333         changed = true;
334       }
335     }
336     index++;
337   }
338   return changed;
339 }
340
341 /* object mode, edbm_ prefix is confusing here, rename? */
342 static bool edbm_backbuf_check_and_select_verts_obmode(Mesh *me,
343                                                        struct EditSelectBuf_Cache *esel,
344                                                        const eSelectOp sel_op)
345 {
346   MVert *mv = me->mvert;
347   uint index;
348   bool changed = false;
349
350   const BLI_bitmap *select_bitmap = esel->select_bitmap;
351
352   if (mv) {
353     for (index = 0; index < me->totvert; index++, mv++) {
354       if (!(mv->flag & ME_HIDE)) {
355         const bool is_select = mv->flag & SELECT;
356         const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index);
357         const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
358         if (sel_op_result != -1) {
359           SET_FLAG_FROM_TEST(mv->flag, sel_op_result, SELECT);
360           changed = true;
361         }
362       }
363     }
364   }
365   return changed;
366 }
367
368 /* object mode, edbm_ prefix is confusing here, rename? */
369 static bool edbm_backbuf_check_and_select_faces_obmode(Mesh *me,
370                                                        struct EditSelectBuf_Cache *esel,
371                                                        const eSelectOp sel_op)
372 {
373   MPoly *mpoly = me->mpoly;
374   uint index;
375   bool changed = false;
376
377   const BLI_bitmap *select_bitmap = esel->select_bitmap;
378
379   if (mpoly) {
380     for (index = 0; index < me->totpoly; index++, mpoly++) {
381       if (!(mpoly->flag & ME_HIDE)) {
382         const bool is_select = mpoly->flag & ME_FACE_SEL;
383         const bool is_inside = BLI_BITMAP_TEST_BOOL(select_bitmap, index);
384         const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
385         if (sel_op_result != -1) {
386           SET_FLAG_FROM_TEST(mpoly->flag, sel_op_result, ME_FACE_SEL);
387           changed = true;
388         }
389       }
390     }
391   }
392   return changed;
393 }
394
395 /** \} */
396
397 /* -------------------------------------------------------------------- */
398 /** \name Lasso Select
399  * \{ */
400
401 typedef struct LassoSelectUserData {
402   ViewContext *vc;
403   const rcti *rect;
404   const rctf *rect_fl;
405   rctf _rect_fl;
406   const int (*mcords)[2];
407   int moves;
408   eSelectOp sel_op;
409
410   /* runtime */
411   int pass;
412   bool is_done;
413   bool is_changed;
414 } LassoSelectUserData;
415
416 static void view3d_userdata_lassoselect_init(LassoSelectUserData *r_data,
417                                              ViewContext *vc,
418                                              const rcti *rect,
419                                              const int (*mcords)[2],
420                                              const int moves,
421                                              const eSelectOp sel_op)
422 {
423   r_data->vc = vc;
424
425   r_data->rect = rect;
426   r_data->rect_fl = &r_data->_rect_fl;
427   BLI_rctf_rcti_copy(&r_data->_rect_fl, rect);
428
429   r_data->mcords = mcords;
430   r_data->moves = moves;
431   r_data->sel_op = sel_op;
432
433   /* runtime */
434   r_data->pass = 0;
435   r_data->is_done = false;
436   r_data->is_changed = false;
437 }
438
439 static bool view3d_selectable_data(bContext *C)
440 {
441   Object *ob = CTX_data_active_object(C);
442
443   if (!ED_operator_region_view3d_active(C)) {
444     return 0;
445   }
446
447   if (ob) {
448     if (ob->mode & OB_MODE_EDIT) {
449       if (ob->type == OB_FONT) {
450         return 0;
451       }
452     }
453     else {
454       if ((ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)) &&
455           !BKE_paint_select_elem_test(ob)) {
456         return 0;
457       }
458     }
459   }
460
461   return 1;
462 }
463
464 /* helper also for box_select */
465 static bool edge_fully_inside_rect(const rctf *rect, const float v1[2], const float v2[2])
466 {
467   return BLI_rctf_isect_pt_v(rect, v1) && BLI_rctf_isect_pt_v(rect, v2);
468 }
469
470 static bool edge_inside_rect(const rctf *rect, const float v1[2], const float v2[2])
471 {
472   int d1, d2, d3, d4;
473
474   /* check points in rect */
475   if (edge_fully_inside_rect(rect, v1, v2)) {
476     return 1;
477   }
478
479   /* check points completely out rect */
480   if (v1[0] < rect->xmin && v2[0] < rect->xmin) {
481     return 0;
482   }
483   if (v1[0] > rect->xmax && v2[0] > rect->xmax) {
484     return 0;
485   }
486   if (v1[1] < rect->ymin && v2[1] < rect->ymin) {
487     return 0;
488   }
489   if (v1[1] > rect->ymax && v2[1] > rect->ymax) {
490     return 0;
491   }
492
493   /* simple check lines intersecting. */
494   d1 = (v1[1] - v2[1]) * (v1[0] - rect->xmin) + (v2[0] - v1[0]) * (v1[1] - rect->ymin);
495   d2 = (v1[1] - v2[1]) * (v1[0] - rect->xmin) + (v2[0] - v1[0]) * (v1[1] - rect->ymax);
496   d3 = (v1[1] - v2[1]) * (v1[0] - rect->xmax) + (v2[0] - v1[0]) * (v1[1] - rect->ymax);
497   d4 = (v1[1] - v2[1]) * (v1[0] - rect->xmax) + (v2[0] - v1[0]) * (v1[1] - rect->ymin);
498
499   if (d1 < 0 && d2 < 0 && d3 < 0 && d4 < 0) {
500     return 0;
501   }
502   if (d1 > 0 && d2 > 0 && d3 > 0 && d4 > 0) {
503     return 0;
504   }
505
506   return 1;
507 }
508
509 static void do_lasso_select_pose__do_tag(void *userData,
510                                          struct bPoseChannel *pchan,
511                                          const float screen_co_a[2],
512                                          const float screen_co_b[2])
513 {
514   LassoSelectUserData *data = userData;
515   bArmature *arm = data->vc->obact->data;
516
517   if (PBONE_SELECTABLE(arm, pchan->bone)) {
518     bool is_point_done = false;
519     int points_proj_tot = 0;
520
521     /* project head location to screenspace */
522     if (screen_co_a[0] != IS_CLIPPED) {
523       points_proj_tot++;
524       if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_a)) &&
525           BLI_lasso_is_point_inside(data->mcords, data->moves, UNPACK2(screen_co_a), INT_MAX)) {
526         is_point_done = true;
527       }
528     }
529
530     /* project tail location to screenspace */
531     if (screen_co_b[0] != IS_CLIPPED) {
532       points_proj_tot++;
533       if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_b)) &&
534           BLI_lasso_is_point_inside(data->mcords, data->moves, UNPACK2(screen_co_b), INT_MAX)) {
535         is_point_done = true;
536       }
537     }
538
539     /* if one of points selected, we skip the bone itself */
540     if ((is_point_done == true) ||
541         ((is_point_done == false) && (points_proj_tot == 2) &&
542          BLI_lasso_is_edge_inside(
543              data->mcords, data->moves, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX))) {
544       pchan->bone->flag |= BONE_DONE;
545     }
546     data->is_changed |= is_point_done;
547   }
548 }
549 static void do_lasso_tag_pose(ViewContext *vc, Object *ob, const int mcords[][2], short moves)
550 {
551   ViewContext vc_tmp;
552   LassoSelectUserData data;
553   rcti rect;
554
555   if ((ob->type != OB_ARMATURE) || (ob->pose == NULL)) {
556     return;
557   }
558
559   vc_tmp = *vc;
560   vc_tmp.obact = ob;
561
562   BLI_lasso_boundbox(&rect, mcords, moves);
563
564   view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, 0);
565
566   ED_view3d_init_mats_rv3d(vc_tmp.obact, vc->rv3d);
567
568   pose_foreachScreenBone(&vc_tmp, do_lasso_select_pose__do_tag, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
569 }
570
571 static bool do_lasso_select_objects(ViewContext *vc,
572                                     const int mcords[][2],
573                                     const short moves,
574                                     const eSelectOp sel_op)
575 {
576   View3D *v3d = vc->v3d;
577   Base *base;
578
579   bool changed = false;
580   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
581     changed |= object_deselect_all_visible(vc->view_layer, vc->v3d);
582   }
583
584   for (base = vc->view_layer->object_bases.first; base; base = base->next) {
585     if (BASE_SELECTABLE(v3d, base)) { /* use this to avoid un-needed lasso lookups */
586       const bool is_select = base->flag & BASE_SELECTED;
587       const bool is_inside = ((ED_view3d_project_base(vc->ar, base) == V3D_PROJ_RET_OK) &&
588                               BLI_lasso_is_point_inside(
589                                   mcords, moves, base->sx, base->sy, IS_CLIPPED));
590       const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
591       if (sel_op_result != -1) {
592         ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT);
593         changed = true;
594       }
595     }
596   }
597
598   if (changed) {
599     DEG_id_tag_update(&vc->scene->id, ID_RECALC_SELECT);
600     WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, vc->scene);
601   }
602   return changed;
603 }
604
605 /**
606  * Use for lasso & box select.
607  */
608 static Base **do_pose_tag_select_op_prepare(ViewContext *vc, uint *r_bases_len)
609 {
610   Base **bases = NULL;
611   BLI_array_declare(bases);
612   FOREACH_BASE_IN_MODE_BEGIN (vc->view_layer, vc->v3d, OB_ARMATURE, OB_MODE_POSE, base_iter) {
613     Object *ob_iter = base_iter->object;
614     bArmature *arm = ob_iter->data;
615     for (bPoseChannel *pchan = ob_iter->pose->chanbase.first; pchan; pchan = pchan->next) {
616       Bone *bone = pchan->bone;
617       bone->flag &= ~BONE_DONE;
618     }
619     arm->id.tag |= LIB_TAG_DOIT;
620     ob_iter->id.tag &= ~LIB_TAG_DOIT;
621     BLI_array_append(bases, base_iter);
622   }
623   FOREACH_BASE_IN_MODE_END;
624   *r_bases_len = BLI_array_len(bases);
625   return bases;
626 }
627
628 static bool do_pose_tag_select_op_exec(Base **bases, const uint bases_len, const eSelectOp sel_op)
629 {
630   bool changed_multi = false;
631
632   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
633     for (int i = 0; i < bases_len; i++) {
634       Base *base_iter = bases[i];
635       Object *ob_iter = base_iter->object;
636       if (ED_pose_deselect_all(ob_iter, SEL_DESELECT, false)) {
637         ED_pose_bone_select_tag_update(ob_iter);
638         changed_multi = true;
639       }
640     }
641   }
642
643   for (int i = 0; i < bases_len; i++) {
644     Base *base_iter = bases[i];
645     Object *ob_iter = base_iter->object;
646     bArmature *arm = ob_iter->data;
647
648     /* Don't handle twice. */
649     if (arm->id.tag & LIB_TAG_DOIT) {
650       arm->id.tag &= ~LIB_TAG_DOIT;
651     }
652     else {
653       continue;
654     }
655
656     bool changed = true;
657     for (bPoseChannel *pchan = ob_iter->pose->chanbase.first; pchan; pchan = pchan->next) {
658       Bone *bone = pchan->bone;
659       if ((bone->flag & BONE_UNSELECTABLE) == 0) {
660         const bool is_select = bone->flag & BONE_SELECTED;
661         const bool is_inside = bone->flag & BONE_DONE;
662         const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
663         if (sel_op_result != -1) {
664           SET_FLAG_FROM_TEST(bone->flag, sel_op_result, BONE_SELECTED);
665           if (sel_op_result == 0) {
666             if (arm->act_bone == bone) {
667               arm->act_bone = NULL;
668             }
669           }
670           changed = true;
671         }
672       }
673     }
674     if (changed) {
675       ED_pose_bone_select_tag_update(ob_iter);
676       changed_multi = true;
677     }
678   }
679   return changed_multi;
680 }
681
682 static bool do_lasso_select_pose(ViewContext *vc,
683                                  const int mcords[][2],
684                                  const short moves,
685                                  const eSelectOp sel_op)
686 {
687   uint bases_len;
688   Base **bases = do_pose_tag_select_op_prepare(vc, &bases_len);
689
690   for (int i = 0; i < bases_len; i++) {
691     Base *base_iter = bases[i];
692     Object *ob_iter = base_iter->object;
693     do_lasso_tag_pose(vc, ob_iter, mcords, moves);
694   }
695
696   const bool changed_multi = do_pose_tag_select_op_exec(bases, bases_len, sel_op);
697   if (changed_multi) {
698     DEG_id_tag_update(&vc->scene->id, ID_RECALC_SELECT);
699     WM_main_add_notifier(NC_SCENE | ND_OB_SELECT, vc->scene);
700   }
701
702   MEM_freeN(bases);
703   return changed_multi;
704 }
705
706 static void do_lasso_select_mesh__doSelectVert(void *userData,
707                                                BMVert *eve,
708                                                const float screen_co[2],
709                                                int UNUSED(index))
710 {
711   LassoSelectUserData *data = userData;
712   const bool is_select = BM_elem_flag_test(eve, BM_ELEM_SELECT);
713   const bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
714                           BLI_lasso_is_point_inside(
715                               data->mcords, data->moves, screen_co[0], screen_co[1], IS_CLIPPED));
716   const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
717   if (sel_op_result != -1) {
718     BM_vert_select_set(data->vc->em->bm, eve, sel_op_result);
719     data->is_changed = true;
720   }
721 }
722 struct LassoSelectUserData_ForMeshEdge {
723   LassoSelectUserData *data;
724   struct EditSelectBuf_Cache *esel;
725   uint backbuf_offset;
726 };
727 static void do_lasso_select_mesh__doSelectEdge_pass0(void *user_data,
728                                                      BMEdge *eed,
729                                                      const float screen_co_a[2],
730                                                      const float screen_co_b[2],
731                                                      int index)
732 {
733   struct LassoSelectUserData_ForMeshEdge *data_for_edge = user_data;
734   LassoSelectUserData *data = data_for_edge->data;
735   const bool is_visible = (data_for_edge->esel ?
736                                BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap,
737                                                     data_for_edge->backbuf_offset + index) :
738                                true);
739   const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT);
740   const bool is_inside =
741       (is_visible && edge_fully_inside_rect(data->rect_fl, screen_co_a, screen_co_b) &&
742        BLI_lasso_is_point_inside(data->mcords, data->moves, UNPACK2(screen_co_a), IS_CLIPPED) &&
743        BLI_lasso_is_point_inside(data->mcords, data->moves, UNPACK2(screen_co_b), IS_CLIPPED));
744   const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
745   if (sel_op_result != -1) {
746     BM_edge_select_set(data->vc->em->bm, eed, sel_op_result);
747     data->is_done = true;
748     data->is_changed = true;
749   }
750 }
751 static void do_lasso_select_mesh__doSelectEdge_pass1(void *user_data,
752                                                      BMEdge *eed,
753                                                      const float screen_co_a[2],
754                                                      const float screen_co_b[2],
755                                                      int index)
756 {
757   struct LassoSelectUserData_ForMeshEdge *data_for_edge = user_data;
758   LassoSelectUserData *data = data_for_edge->data;
759   const bool is_visible = (data_for_edge->esel ?
760                                BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap,
761                                                     data_for_edge->backbuf_offset + index) :
762                                true);
763   const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT);
764   const bool is_inside = (is_visible && BLI_lasso_is_edge_inside(data->mcords,
765                                                                  data->moves,
766                                                                  UNPACK2(screen_co_a),
767                                                                  UNPACK2(screen_co_b),
768                                                                  IS_CLIPPED));
769   const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
770   if (sel_op_result != -1) {
771     BM_edge_select_set(data->vc->em->bm, eed, sel_op_result);
772     data->is_changed = true;
773   }
774 }
775
776 static void do_lasso_select_mesh__doSelectFace(void *userData,
777                                                BMFace *efa,
778                                                const float screen_co[2],
779                                                int UNUSED(index))
780 {
781   LassoSelectUserData *data = userData;
782   const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT);
783   const bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
784                           BLI_lasso_is_point_inside(
785                               data->mcords, data->moves, screen_co[0], screen_co[1], IS_CLIPPED));
786   const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
787   if (sel_op_result != -1) {
788     BM_face_select_set(data->vc->em->bm, efa, sel_op_result);
789     data->is_changed = true;
790   }
791 }
792
793 static bool do_lasso_select_mesh(ViewContext *vc,
794                                  wmGenericUserData *wm_userdata,
795                                  const int mcords[][2],
796                                  short moves,
797                                  const eSelectOp sel_op)
798 {
799   LassoSelectUserData data;
800   ToolSettings *ts = vc->scene->toolsettings;
801   rcti rect;
802
803   /* set editmesh */
804   vc->em = BKE_editmesh_from_object(vc->obedit);
805
806   BLI_lasso_boundbox(&rect, mcords, moves);
807
808   view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, sel_op);
809
810   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
811     if (vc->em->bm->totvertsel) {
812       EDBM_flag_disable_all(vc->em, BM_ELEM_SELECT);
813       data.is_changed = true;
814     }
815   }
816
817   /* for non zbuf projections, don't change the GL state */
818   ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
819
820   GPU_matrix_set(vc->rv3d->viewmat);
821
822   const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d);
823
824   struct EditSelectBuf_Cache *esel = wm_userdata->data;
825   if (use_zbuf) {
826     if (wm_userdata->data == NULL) {
827       editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc);
828       esel = wm_userdata->data;
829       const uint buffer_len = EDBM_select_id_context_elem_len(esel->sel_id_ctx);
830       esel->select_bitmap = ED_select_buffer_bitmap_from_poly(buffer_len, mcords, moves, &rect);
831     }
832   }
833
834   if (ts->selectmode & SCE_SELECT_VERTEX) {
835     if (use_zbuf) {
836       data.is_changed |= edbm_backbuf_check_and_select_verts(esel, vc->obedit, vc->em, sel_op);
837     }
838     else {
839       mesh_foreachScreenVert(
840           vc, do_lasso_select_mesh__doSelectVert, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
841     }
842   }
843   if (ts->selectmode & SCE_SELECT_EDGE) {
844     /* Does both use_zbuf and non-use_zbuf versions (need screen cos for both) */
845     struct LassoSelectUserData_ForMeshEdge data_for_edge = {
846         .data = &data,
847         .esel = use_zbuf ? esel : NULL,
848         .backbuf_offset = use_zbuf ?
849                               EDBM_select_id_context_offset_for_object_elem(
850                                   esel->sel_id_ctx, vc->obedit->runtime.select_id, BM_EDGE) :
851                               0,
852     };
853     mesh_foreachScreenEdge(
854         vc, do_lasso_select_mesh__doSelectEdge_pass0, &data_for_edge, V3D_PROJ_TEST_CLIP_NEAR);
855     if (data.is_done == false) {
856       mesh_foreachScreenEdge(
857           vc, do_lasso_select_mesh__doSelectEdge_pass1, &data_for_edge, V3D_PROJ_TEST_CLIP_NEAR);
858     }
859   }
860
861   if (ts->selectmode & SCE_SELECT_FACE) {
862     if (use_zbuf) {
863       data.is_changed |= edbm_backbuf_check_and_select_faces(esel, vc->obedit, vc->em, sel_op);
864     }
865     else {
866       mesh_foreachScreenFace(
867           vc, do_lasso_select_mesh__doSelectFace, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
868     }
869   }
870
871   if (data.is_changed) {
872     EDBM_selectmode_flush(vc->em);
873   }
874   return data.is_changed;
875 }
876
877 static void do_lasso_select_curve__doSelect(void *userData,
878                                             Nurb *UNUSED(nu),
879                                             BPoint *bp,
880                                             BezTriple *bezt,
881                                             int beztindex,
882                                             const float screen_co[2])
883 {
884   LassoSelectUserData *data = userData;
885
886   const bool is_inside = BLI_lasso_is_point_inside(
887       data->mcords, data->moves, screen_co[0], screen_co[1], IS_CLIPPED);
888   if (bp) {
889     const bool is_select = bp->f1 & SELECT;
890     const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
891     if (sel_op_result != -1) {
892       SET_FLAG_FROM_TEST(bp->f1, sel_op_result, SELECT);
893       data->is_changed = true;
894     }
895   }
896   else {
897     if ((data->vc->v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_CU_HANDLES) == 0) {
898       /* can only be (beztindex == 0) here since handles are hidden */
899       const bool is_select = bezt->f2 & SELECT;
900       const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
901       if (sel_op_result != -1) {
902         SET_FLAG_FROM_TEST(bezt->f2, sel_op_result, SELECT);
903       }
904       bezt->f1 = bezt->f3 = bezt->f2;
905       data->is_changed = true;
906     }
907     else {
908       char *flag_p = (&bezt->f1) + beztindex;
909       const bool is_select = *flag_p & SELECT;
910       const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
911       if (sel_op_result != -1) {
912         SET_FLAG_FROM_TEST(*flag_p, sel_op_result, SELECT);
913         data->is_changed = true;
914       }
915     }
916   }
917 }
918
919 static bool do_lasso_select_curve(ViewContext *vc,
920                                   const int mcords[][2],
921                                   short moves,
922                                   const eSelectOp sel_op)
923 {
924   LassoSelectUserData data;
925   rcti rect;
926
927   BLI_lasso_boundbox(&rect, mcords, moves);
928
929   view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, sel_op);
930
931   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
932     Curve *curve = (Curve *)vc->obedit->data;
933     data.is_changed |= ED_curve_deselect_all(curve->editnurb);
934   }
935
936   ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
937   nurbs_foreachScreenVert(vc, do_lasso_select_curve__doSelect, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
938   if (data.is_changed) {
939     BKE_curve_nurb_vert_active_validate(vc->obedit->data);
940   }
941   return data.is_changed;
942 }
943
944 static void do_lasso_select_lattice__doSelect(void *userData, BPoint *bp, const float screen_co[2])
945 {
946   LassoSelectUserData *data = userData;
947   const bool is_select = bp->f1 & SELECT;
948   const bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
949                           BLI_lasso_is_point_inside(
950                               data->mcords, data->moves, screen_co[0], screen_co[1], IS_CLIPPED));
951   const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
952   if (sel_op_result != -1) {
953     SET_FLAG_FROM_TEST(bp->f1, sel_op_result, SELECT);
954     data->is_changed = true;
955   }
956 }
957 static bool do_lasso_select_lattice(ViewContext *vc,
958                                     const int mcords[][2],
959                                     short moves,
960                                     const eSelectOp sel_op)
961 {
962   LassoSelectUserData data;
963   rcti rect;
964
965   BLI_lasso_boundbox(&rect, mcords, moves);
966
967   view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, sel_op);
968
969   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
970     data.is_changed |= ED_lattice_flags_set(vc->obedit, 0);
971   }
972
973   ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
974   lattice_foreachScreenVert(
975       vc, do_lasso_select_lattice__doSelect, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
976   return data.is_changed;
977 }
978
979 static void do_lasso_select_armature__doSelectBone(void *userData,
980                                                    EditBone *ebone,
981                                                    const float screen_co_a[2],
982                                                    const float screen_co_b[2])
983 {
984   LassoSelectUserData *data = userData;
985   bArmature *arm = data->vc->obedit->data;
986   if (EBONE_VISIBLE(arm, ebone)) {
987     int is_ignore_flag = 0;
988     int is_inside_flag = 0;
989
990     if (screen_co_a[0] != IS_CLIPPED) {
991       if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_a)) &&
992           BLI_lasso_is_point_inside(data->mcords, data->moves, UNPACK2(screen_co_a), INT_MAX)) {
993         is_inside_flag |= BONESEL_ROOT;
994       }
995     }
996     else {
997       is_ignore_flag |= BONESEL_ROOT;
998     }
999
1000     if (screen_co_b[0] != IS_CLIPPED) {
1001       if (BLI_rcti_isect_pt(data->rect, UNPACK2(screen_co_b)) &&
1002           BLI_lasso_is_point_inside(data->mcords, data->moves, UNPACK2(screen_co_b), INT_MAX)) {
1003         is_inside_flag |= BONESEL_TIP;
1004       }
1005     }
1006     else {
1007       is_ignore_flag |= BONESEL_TIP;
1008     }
1009
1010     if (is_inside_flag == (BONE_ROOTSEL | BONE_TIPSEL) ||
1011         BLI_lasso_is_edge_inside(
1012             data->mcords, data->moves, UNPACK2(screen_co_a), UNPACK2(screen_co_b), INT_MAX)) {
1013       is_inside_flag |= BONESEL_BONE;
1014     }
1015
1016     ebone->temp.i = is_inside_flag | (is_ignore_flag >> 16);
1017   }
1018 }
1019
1020 static bool do_lasso_select_armature(ViewContext *vc,
1021                                      const int mcords[][2],
1022                                      short moves,
1023                                      const eSelectOp sel_op)
1024 {
1025   LassoSelectUserData data;
1026   rcti rect;
1027
1028   BLI_lasso_boundbox(&rect, mcords, moves);
1029
1030   view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, sel_op);
1031
1032   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1033     data.is_changed |= ED_armature_edit_deselect_all_visible(vc->obedit);
1034   }
1035
1036   bArmature *arm = vc->obedit->data;
1037
1038   ED_armature_ebone_listbase_temp_clear(arm->edbo);
1039
1040   ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
1041
1042   armature_foreachScreenBone(
1043       vc, do_lasso_select_armature__doSelectBone, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
1044
1045   data.is_changed |= ED_armature_edit_select_op_from_tagged(vc->obedit->data, sel_op);
1046
1047   if (data.is_changed) {
1048     WM_main_add_notifier(NC_OBJECT | ND_BONE_SELECT, vc->obedit);
1049   }
1050   return data.is_changed;
1051 }
1052
1053 static void do_lasso_select_mball__doSelectElem(void *userData,
1054                                                 struct MetaElem *ml,
1055                                                 const float screen_co[2])
1056 {
1057   LassoSelectUserData *data = userData;
1058   const bool is_select = ml->flag & SELECT;
1059   const bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
1060                           BLI_lasso_is_point_inside(
1061                               data->mcords, data->moves, screen_co[0], screen_co[1], INT_MAX));
1062   const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
1063   if (sel_op_result != -1) {
1064     SET_FLAG_FROM_TEST(ml->flag, sel_op_result, SELECT);
1065     data->is_changed = true;
1066   }
1067 }
1068 static bool do_lasso_select_meta(ViewContext *vc,
1069                                  const int mcords[][2],
1070                                  short moves,
1071                                  const eSelectOp sel_op)
1072 {
1073   LassoSelectUserData data;
1074   rcti rect;
1075
1076   MetaBall *mb = (MetaBall *)vc->obedit->data;
1077
1078   BLI_lasso_boundbox(&rect, mcords, moves);
1079
1080   view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, sel_op);
1081
1082   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1083     data.is_changed |= BKE_mball_deselect_all(mb);
1084   }
1085
1086   ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
1087
1088   mball_foreachScreenElem(
1089       vc, do_lasso_select_mball__doSelectElem, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
1090
1091   return data.is_changed;
1092 }
1093
1094 static void do_lasso_select_meshobject__doSelectVert(void *userData,
1095                                                      MVert *mv,
1096                                                      const float screen_co[2],
1097                                                      int UNUSED(index))
1098 {
1099   LassoSelectUserData *data = userData;
1100   const bool is_select = mv->flag & SELECT;
1101   const bool is_inside = (BLI_rctf_isect_pt_v(data->rect_fl, screen_co) &&
1102                           BLI_lasso_is_point_inside(
1103                               data->mcords, data->moves, screen_co[0], screen_co[1], IS_CLIPPED));
1104   const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
1105   if (sel_op_result != -1) {
1106     SET_FLAG_FROM_TEST(mv->flag, sel_op_result, SELECT);
1107     data->is_changed = true;
1108   }
1109 }
1110 static bool do_lasso_select_paintvert(ViewContext *vc,
1111                                       wmGenericUserData *wm_userdata,
1112                                       const int mcords[][2],
1113                                       short moves,
1114                                       const eSelectOp sel_op)
1115 {
1116   const bool use_zbuf = !XRAY_ENABLED(vc->v3d);
1117   Object *ob = vc->obact;
1118   Mesh *me = ob->data;
1119   rcti rect;
1120
1121   if (me == NULL || me->totvert == 0) {
1122     return false;
1123   }
1124
1125   bool changed = false;
1126   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1127     /* flush selection at the end */
1128     changed |= paintvert_deselect_all_visible(ob, SEL_DESELECT, false);
1129   }
1130
1131   BLI_lasso_boundbox(&rect, mcords, moves);
1132
1133   struct EditSelectBuf_Cache *esel = wm_userdata->data;
1134   if (use_zbuf) {
1135     if (wm_userdata->data == NULL) {
1136       editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc);
1137       esel = wm_userdata->data;
1138       const uint buffer_len = EDBM_select_id_context_elem_len(esel->sel_id_ctx);
1139       esel->select_bitmap = ED_select_buffer_bitmap_from_poly(buffer_len, mcords, moves, &rect);
1140     }
1141   }
1142
1143   if (use_zbuf) {
1144     if (esel->select_bitmap != NULL) {
1145       changed |= edbm_backbuf_check_and_select_verts_obmode(me, esel, sel_op);
1146     }
1147   }
1148   else {
1149     LassoSelectUserData data;
1150
1151     view3d_userdata_lassoselect_init(&data, vc, &rect, mcords, moves, sel_op);
1152
1153     ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d);
1154
1155     meshobject_foreachScreenVert(
1156         vc, do_lasso_select_meshobject__doSelectVert, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
1157
1158     changed |= data.is_changed;
1159   }
1160
1161   if (changed) {
1162     if (SEL_OP_CAN_DESELECT(sel_op)) {
1163       BKE_mesh_mselect_validate(me);
1164     }
1165     paintvert_flush_flags(ob);
1166     paintvert_tag_select_update(vc->C, ob);
1167   }
1168
1169   return changed;
1170 }
1171 static bool do_lasso_select_paintface(ViewContext *vc,
1172                                       wmGenericUserData *wm_userdata,
1173                                       const int mcords[][2],
1174                                       short moves,
1175                                       const eSelectOp sel_op)
1176 {
1177   Object *ob = vc->obact;
1178   Mesh *me = ob->data;
1179   rcti rect;
1180
1181   if (me == NULL || me->totpoly == 0) {
1182     return false;
1183   }
1184
1185   bool changed = false;
1186   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1187     /* flush selection at the end */
1188     changed |= paintface_deselect_all_visible(vc->C, ob, SEL_DESELECT, false);
1189   }
1190
1191   BLI_lasso_boundbox(&rect, mcords, moves);
1192
1193   struct EditSelectBuf_Cache *esel = wm_userdata->data;
1194   if (esel == NULL) {
1195     editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc);
1196     esel = wm_userdata->data;
1197     const uint buffer_len = EDBM_select_id_context_elem_len(esel->sel_id_ctx);
1198     esel->select_bitmap = ED_select_buffer_bitmap_from_poly(buffer_len, mcords, moves, &rect);
1199   }
1200
1201   if (esel->select_bitmap) {
1202     changed |= edbm_backbuf_check_and_select_faces_obmode(me, esel, sel_op);
1203   }
1204
1205   if (changed) {
1206     paintface_flush_flags(vc->C, ob, SELECT);
1207   }
1208   return changed;
1209 }
1210
1211 #if 0
1212 static void do_lasso_select_node(int mcords[][2], short moves, const eSelectOp sel_op)
1213 {
1214   SpaceNode *snode = sa->spacedata.first;
1215
1216   bNode *node;
1217   rcti rect;
1218   int node_cent[2];
1219   float node_centf[2];
1220   bool changed = false;
1221
1222   BLI_lasso_boundbox(&rect, mcords, moves);
1223
1224   /* store selection in temp test flag */
1225   for (node = snode->edittree->nodes.first; node; node = node->next) {
1226     node_centf[0] = BLI_RCT_CENTER_X(&node->totr);
1227     node_centf[1] = BLI_RCT_CENTER_Y(&node->totr);
1228
1229     ipoco_to_areaco_noclip(G.v2d, node_centf, node_cent);
1230     const bool is_select = node->flag & SELECT;
1231     const bool is_inside = (BLI_rcti_isect_pt_v(&rect, node_cent) &&
1232                             BLI_lasso_is_point_inside(mcords, moves, node_cent[0], node_cent[1]));
1233     const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
1234     if (sel_op_result != -1) {
1235       SET_FLAG_FROM_TEST(node->flag, sel_op_result, SELECT);
1236       changed = true;
1237     }
1238   }
1239   if (changed) {
1240     BIF_undo_push("Lasso select nodes");
1241   }
1242 }
1243 #endif
1244
1245 static bool view3d_lasso_select(
1246     bContext *C, ViewContext *vc, const int mcords[][2], short moves, const eSelectOp sel_op)
1247 {
1248   Object *ob = CTX_data_active_object(C);
1249   bool changed_multi = false;
1250
1251   wmGenericUserData wm_userdata_buf = {0};
1252   wmGenericUserData *wm_userdata = &wm_userdata_buf;
1253
1254   if (vc->obedit == NULL) { /* Object Mode */
1255     if (BKE_paint_select_face_test(ob)) {
1256       changed_multi |= do_lasso_select_paintface(vc, wm_userdata, mcords, moves, sel_op);
1257     }
1258     else if (BKE_paint_select_vert_test(ob)) {
1259       changed_multi |= do_lasso_select_paintvert(vc, wm_userdata, mcords, moves, sel_op);
1260     }
1261     else if (ob &&
1262              (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT))) {
1263       /* pass */
1264     }
1265     else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT)) {
1266       changed_multi |= PE_lasso_select(C, mcords, moves, sel_op);
1267     }
1268     else if (ob && (ob->mode & OB_MODE_POSE)) {
1269       changed_multi |= do_lasso_select_pose(vc, mcords, moves, sel_op);
1270     }
1271     else {
1272       changed_multi |= do_lasso_select_objects(vc, mcords, moves, sel_op);
1273     }
1274   }
1275   else { /* Edit Mode */
1276     FOREACH_OBJECT_IN_MODE_BEGIN (vc->view_layer, vc->v3d, ob->type, ob->mode, ob_iter) {
1277       ED_view3d_viewcontext_init_object(vc, ob_iter);
1278       bool changed = false;
1279
1280       switch (vc->obedit->type) {
1281         case OB_MESH:
1282           changed = do_lasso_select_mesh(vc, wm_userdata, mcords, moves, sel_op);
1283           break;
1284         case OB_CURVE:
1285         case OB_SURF:
1286           changed = do_lasso_select_curve(vc, mcords, moves, sel_op);
1287           break;
1288         case OB_LATTICE:
1289           changed = do_lasso_select_lattice(vc, mcords, moves, sel_op);
1290           break;
1291         case OB_ARMATURE:
1292           changed = do_lasso_select_armature(vc, mcords, moves, sel_op);
1293           break;
1294         case OB_MBALL:
1295           changed = do_lasso_select_meta(vc, mcords, moves, sel_op);
1296           break;
1297         default:
1298           assert(!"lasso select on incorrect object type");
1299           break;
1300       }
1301
1302       if (changed) {
1303         DEG_id_tag_update(vc->obedit->data, ID_RECALC_SELECT);
1304         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc->obedit->data);
1305         changed_multi = true;
1306       }
1307     }
1308     FOREACH_OBJECT_IN_MODE_END;
1309   }
1310
1311   WM_generic_user_data_free(wm_userdata);
1312
1313   return changed_multi;
1314 }
1315
1316 /* lasso operator gives properties, but since old code works
1317  * with short array we convert */
1318 static int view3d_lasso_select_exec(bContext *C, wmOperator *op)
1319 {
1320   ViewContext vc;
1321   int mcords_tot;
1322   const int(*mcords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcords_tot);
1323
1324   if (mcords) {
1325     view3d_operator_needs_opengl(C);
1326     BKE_object_update_select_id(CTX_data_main(C));
1327
1328     /* setup view context for argument to callbacks */
1329     ED_view3d_viewcontext_init(C, &vc);
1330
1331     eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
1332     bool changed_multi = view3d_lasso_select(C, &vc, mcords, mcords_tot, sel_op);
1333
1334     MEM_freeN((void *)mcords);
1335
1336     if (changed_multi) {
1337       return OPERATOR_FINISHED;
1338     }
1339     else {
1340       return OPERATOR_CANCELLED;
1341     }
1342   }
1343   return OPERATOR_PASS_THROUGH;
1344 }
1345
1346 void VIEW3D_OT_select_lasso(wmOperatorType *ot)
1347 {
1348   ot->name = "Lasso Select";
1349   ot->description = "Select items using lasso selection";
1350   ot->idname = "VIEW3D_OT_select_lasso";
1351
1352   ot->invoke = WM_gesture_lasso_invoke;
1353   ot->modal = WM_gesture_lasso_modal;
1354   ot->exec = view3d_lasso_select_exec;
1355   ot->poll = view3d_selectable_data;
1356   ot->cancel = WM_gesture_lasso_cancel;
1357
1358   /* flags */
1359   ot->flag = OPTYPE_UNDO;
1360
1361   /* properties */
1362   WM_operator_properties_gesture_lasso(ot);
1363   WM_operator_properties_select_operation(ot);
1364 }
1365
1366 /** \} */
1367
1368 /* -------------------------------------------------------------------- */
1369 /** \name Cursor Picking
1370  * \{ */
1371
1372 /* The max number of menu items in an object select menu */
1373 typedef struct SelMenuItemF {
1374   char idname[MAX_ID_NAME - 2];
1375   int icon;
1376 } SelMenuItemF;
1377
1378 #define SEL_MENU_SIZE 22
1379 static SelMenuItemF object_mouse_select_menu_data[SEL_MENU_SIZE];
1380
1381 /* special (crappy) operator only for menu select */
1382 static const EnumPropertyItem *object_select_menu_enum_itemf(bContext *C,
1383                                                              PointerRNA *UNUSED(ptr),
1384                                                              PropertyRNA *UNUSED(prop),
1385                                                              bool *r_free)
1386 {
1387   EnumPropertyItem *item = NULL, item_tmp = {0};
1388   int totitem = 0;
1389   int i = 0;
1390
1391   /* don't need context but avoid docgen using this */
1392   if (C == NULL || object_mouse_select_menu_data[i].idname[0] == '\0') {
1393     return DummyRNA_NULL_items;
1394   }
1395
1396   for (; i < SEL_MENU_SIZE && object_mouse_select_menu_data[i].idname[0] != '\0'; i++) {
1397     item_tmp.name = object_mouse_select_menu_data[i].idname;
1398     item_tmp.identifier = object_mouse_select_menu_data[i].idname;
1399     item_tmp.value = i;
1400     item_tmp.icon = object_mouse_select_menu_data[i].icon;
1401     RNA_enum_item_add(&item, &totitem, &item_tmp);
1402   }
1403
1404   RNA_enum_item_end(&item, &totitem);
1405   *r_free = true;
1406
1407   return item;
1408 }
1409
1410 static int object_select_menu_exec(bContext *C, wmOperator *op)
1411 {
1412   const int name_index = RNA_enum_get(op->ptr, "name");
1413   const bool toggle = RNA_boolean_get(op->ptr, "toggle");
1414   bool changed = false;
1415   const char *name = object_mouse_select_menu_data[name_index].idname;
1416
1417   if (!toggle) {
1418     CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
1419       if ((base->flag & BASE_SELECTED) != 0) {
1420         ED_object_base_select(base, BA_DESELECT);
1421         changed = true;
1422       }
1423     }
1424     CTX_DATA_END;
1425   }
1426
1427   CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
1428     /* This is a bit dodgy, there should only be ONE object with this name,
1429      * but library objects can mess this up. */
1430     if (STREQ(name, base->object->id.name + 2)) {
1431       ED_object_base_activate(C, base);
1432       ED_object_base_select(base, BA_SELECT);
1433       changed = true;
1434     }
1435   }
1436   CTX_DATA_END;
1437
1438   /* weak but ensures we activate menu again before using the enum */
1439   memset(object_mouse_select_menu_data, 0, sizeof(object_mouse_select_menu_data));
1440
1441   /* undo? */
1442   if (changed) {
1443     Scene *scene = CTX_data_scene(C);
1444     DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
1445     WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
1446     return OPERATOR_FINISHED;
1447   }
1448   else {
1449     return OPERATOR_CANCELLED;
1450   }
1451 }
1452
1453 void VIEW3D_OT_select_menu(wmOperatorType *ot)
1454 {
1455   PropertyRNA *prop;
1456
1457   /* identifiers */
1458   ot->name = "Select Menu";
1459   ot->description = "Menu object selection";
1460   ot->idname = "VIEW3D_OT_select_menu";
1461
1462   /* api callbacks */
1463   ot->invoke = WM_menu_invoke;
1464   ot->exec = object_select_menu_exec;
1465
1466   /* flags */
1467   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1468
1469   /* keyingset to use (dynamic enum) */
1470   prop = RNA_def_enum(ot->srna, "name", DummyRNA_NULL_items, 0, "Object Name", "");
1471   RNA_def_enum_funcs(prop, object_select_menu_enum_itemf);
1472   RNA_def_property_flag(prop, PROP_HIDDEN | PROP_ENUM_NO_TRANSLATE);
1473   ot->prop = prop;
1474
1475   RNA_def_boolean(
1476       ot->srna, "toggle", 0, "Toggle", "Toggle selection instead of deselecting everything first");
1477 }
1478
1479 static Base *object_mouse_select_menu(
1480     bContext *C, ViewContext *vc, uint *buffer, int hits, const int mval[2], bool toggle)
1481 {
1482   short baseCount = 0;
1483   bool ok;
1484   LinkNode *linklist = NULL;
1485
1486   /* handle base->object->select_id */
1487   CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
1488     ok = false;
1489
1490     /* two selection methods, the CTRL select uses max dist of 15 */
1491     if (buffer) {
1492       for (int a = 0; a < hits; a++) {
1493         /* index was converted */
1494         if (base->object->runtime.select_id == (buffer[(4 * a) + 3] & ~0xFFFF0000)) {
1495           ok = true;
1496           break;
1497         }
1498       }
1499     }
1500     else {
1501       const int dist = 15 * U.pixelsize;
1502       if (ED_view3d_project_base(vc->ar, base) == V3D_PROJ_RET_OK) {
1503         const int delta_px[2] = {base->sx - mval[0], base->sy - mval[1]};
1504         if (len_manhattan_v2_int(delta_px) < dist) {
1505           ok = true;
1506         }
1507       }
1508     }
1509
1510     if (ok) {
1511       baseCount++;
1512       BLI_linklist_prepend(&linklist, base);
1513
1514       if (baseCount == SEL_MENU_SIZE) {
1515         break;
1516       }
1517     }
1518   }
1519   CTX_DATA_END;
1520
1521   if (baseCount == 0) {
1522     return NULL;
1523   }
1524   if (baseCount == 1) {
1525     Base *base = (Base *)linklist->link;
1526     BLI_linklist_free(linklist, NULL);
1527     return base;
1528   }
1529   else {
1530     /* UI, full in static array values that we later use in an enum function */
1531     LinkNode *node;
1532     int i;
1533
1534     memset(object_mouse_select_menu_data, 0, sizeof(object_mouse_select_menu_data));
1535
1536     for (node = linklist, i = 0; node; node = node->next, i++) {
1537       Base *base = node->link;
1538       Object *ob = base->object;
1539       const char *name = ob->id.name + 2;
1540
1541       BLI_strncpy(object_mouse_select_menu_data[i].idname, name, MAX_ID_NAME - 2);
1542       object_mouse_select_menu_data[i].icon = UI_icon_from_id(&ob->id);
1543     }
1544
1545     {
1546       wmOperatorType *ot = WM_operatortype_find("VIEW3D_OT_select_menu", false);
1547       PointerRNA ptr;
1548
1549       WM_operator_properties_create_ptr(&ptr, ot);
1550       RNA_boolean_set(&ptr, "toggle", toggle);
1551       WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &ptr);
1552       WM_operator_properties_free(&ptr);
1553     }
1554
1555     BLI_linklist_free(linklist, NULL);
1556     return NULL;
1557   }
1558 }
1559
1560 static bool selectbuffer_has_bones(const uint *buffer, const uint hits)
1561 {
1562   uint i;
1563   for (i = 0; i < hits; i++) {
1564     if (buffer[(4 * i) + 3] & 0xFFFF0000) {
1565       return true;
1566     }
1567   }
1568   return false;
1569 }
1570
1571 /* utility function for mixed_bones_object_selectbuffer */
1572 static int selectbuffer_ret_hits_15(uint *UNUSED(buffer), const int hits15)
1573 {
1574   return hits15;
1575 }
1576
1577 static int selectbuffer_ret_hits_9(uint *buffer, const int hits15, const int hits9)
1578 {
1579   const int offs = 4 * hits15;
1580   memcpy(buffer, buffer + offs, 4 * hits9 * sizeof(uint));
1581   return hits9;
1582 }
1583
1584 static int selectbuffer_ret_hits_5(uint *buffer,
1585                                    const int hits15,
1586                                    const int hits9,
1587                                    const int hits5)
1588 {
1589   const int offs = 4 * hits15 + 4 * hits9;
1590   memcpy(buffer, buffer + offs, 4 * hits5 * sizeof(uint));
1591   return hits5;
1592 }
1593
1594 /**
1595  * Populate a select buffer with objects and bones, if there are any.
1596  * Checks three selection levels and compare.
1597  */
1598 static int mixed_bones_object_selectbuffer(ViewContext *vc,
1599                                            uint *buffer,
1600                                            const int mval[2],
1601                                            eV3DSelectObjectFilter select_filter,
1602                                            bool do_nearest)
1603 {
1604   rcti rect;
1605   int hits15, hits9 = 0, hits5 = 0;
1606   bool has_bones15 = false, has_bones9 = false, has_bones5 = false;
1607
1608   const int select_mode = (do_nearest ? VIEW3D_SELECT_PICK_NEAREST : VIEW3D_SELECT_PICK_ALL);
1609   int hits = 0;
1610
1611   /* we _must_ end cache before return, use 'goto finally' */
1612   view3d_opengl_select_cache_begin();
1613
1614   BLI_rcti_init_pt_radius(&rect, mval, 14);
1615   hits15 = view3d_opengl_select(vc, buffer, MAXPICKBUF, &rect, select_mode, select_filter);
1616   if (hits15 == 1) {
1617     hits = selectbuffer_ret_hits_15(buffer, hits15);
1618     goto finally;
1619   }
1620   else if (hits15 > 0) {
1621     int offs;
1622     has_bones15 = selectbuffer_has_bones(buffer, hits15);
1623
1624     offs = 4 * hits15;
1625     BLI_rcti_init_pt_radius(&rect, mval, 9);
1626     hits9 = view3d_opengl_select(
1627         vc, buffer + offs, MAXPICKBUF - offs, &rect, select_mode, select_filter);
1628     if (hits9 == 1) {
1629       hits = selectbuffer_ret_hits_9(buffer, hits15, hits9);
1630       goto finally;
1631     }
1632     else if (hits9 > 0) {
1633       has_bones9 = selectbuffer_has_bones(buffer + offs, hits9);
1634
1635       offs += 4 * hits9;
1636       BLI_rcti_init_pt_radius(&rect, mval, 5);
1637       hits5 = view3d_opengl_select(
1638           vc, buffer + offs, MAXPICKBUF - offs, &rect, select_mode, select_filter);
1639       if (hits5 == 1) {
1640         hits = selectbuffer_ret_hits_5(buffer, hits15, hits9, hits5);
1641         goto finally;
1642       }
1643       else if (hits5 > 0) {
1644         has_bones5 = selectbuffer_has_bones(buffer + offs, hits5);
1645       }
1646     }
1647
1648     if (has_bones5) {
1649       hits = selectbuffer_ret_hits_5(buffer, hits15, hits9, hits5);
1650       goto finally;
1651     }
1652     else if (has_bones9) {
1653       hits = selectbuffer_ret_hits_9(buffer, hits15, hits9);
1654       goto finally;
1655     }
1656     else if (has_bones15) {
1657       hits = selectbuffer_ret_hits_15(buffer, hits15);
1658       goto finally;
1659     }
1660
1661     if (hits5 > 0) {
1662       hits = selectbuffer_ret_hits_5(buffer, hits15, hits9, hits5);
1663       goto finally;
1664     }
1665     else if (hits9 > 0) {
1666       hits = selectbuffer_ret_hits_9(buffer, hits15, hits9);
1667       goto finally;
1668     }
1669     else {
1670       hits = selectbuffer_ret_hits_15(buffer, hits15);
1671       goto finally;
1672     }
1673   }
1674
1675 finally:
1676   view3d_opengl_select_cache_end();
1677   return hits;
1678 }
1679
1680 static int mixed_bones_object_selectbuffer_extended(ViewContext *vc,
1681                                                     uint *buffer,
1682                                                     const int mval[2],
1683                                                     eV3DSelectObjectFilter select_filter,
1684                                                     bool use_cycle,
1685                                                     bool enumerate,
1686                                                     bool *r_do_nearest)
1687 {
1688   static int last_mval[2] = {-100, -100};
1689   bool do_nearest = false;
1690   View3D *v3d = vc->v3d;
1691
1692   /* define if we use solid nearest select or not */
1693   if (use_cycle) {
1694     if (!XRAY_ACTIVE(v3d)) {
1695       do_nearest = true;
1696       if (len_manhattan_v2v2_int(mval, last_mval) <= WM_EVENT_CURSOR_MOTION_THRESHOLD) {
1697         do_nearest = false;
1698       }
1699     }
1700     copy_v2_v2_int(last_mval, mval);
1701   }
1702   else {
1703     if (!XRAY_ACTIVE(v3d)) {
1704       do_nearest = true;
1705     }
1706   }
1707
1708   if (r_do_nearest) {
1709     *r_do_nearest = do_nearest;
1710   }
1711
1712   do_nearest = do_nearest && !enumerate;
1713
1714   int hits = mixed_bones_object_selectbuffer(vc, buffer, mval, select_filter, do_nearest);
1715
1716   if (vc->scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) {
1717     const bool is_pose_mode = ((vc->obact && vc->obact->mode & OB_MODE_POSE) ||
1718                                (select_filter == VIEW3D_SELECT_FILTER_WPAINT_POSE_MODE_LOCK));
1719     struct {
1720       uint data[4];
1721     } *buffer4 = (void *)buffer;
1722     uint j = 0;
1723     for (uint i = 0; i < hits; i++) {
1724       if (((buffer4[i].data[3] & 0xFFFF0000) != 0) == is_pose_mode) {
1725         if (i != j) {
1726           buffer4[j] = buffer4[i];
1727         }
1728         j++;
1729       }
1730     }
1731     hits = j;
1732   }
1733
1734   return hits;
1735 }
1736
1737 /* returns basact */
1738 static Base *mouse_select_eval_buffer(ViewContext *vc,
1739                                       const uint *buffer,
1740                                       int hits,
1741                                       Base *startbase,
1742                                       bool has_bones,
1743                                       bool do_nearest)
1744 {
1745   ViewLayer *view_layer = vc->view_layer;
1746   View3D *v3d = vc->v3d;
1747   Base *base, *basact = NULL;
1748   int a;
1749
1750   if (do_nearest) {
1751     uint min = 0xFFFFFFFF;
1752     int selcol = 0, notcol = 0;
1753
1754     if (has_bones) {
1755       /* we skip non-bone hits */
1756       for (a = 0; a < hits; a++) {
1757         if (min > buffer[4 * a + 1] && (buffer[4 * a + 3] & 0xFFFF0000)) {
1758           min = buffer[4 * a + 1];
1759           selcol = buffer[4 * a + 3] & 0xFFFF;
1760         }
1761       }
1762     }
1763     else {
1764       /* only exclude active object when it is selected... */
1765       if (BASACT(view_layer) && (BASACT(view_layer)->flag & BASE_SELECTED) && hits > 1) {
1766         notcol = BASACT(view_layer)->object->runtime.select_id;
1767       }
1768
1769       for (a = 0; a < hits; a++) {
1770         if (min > buffer[4 * a + 1] && notcol != (buffer[4 * a + 3] & 0xFFFF)) {
1771           min = buffer[4 * a + 1];
1772           selcol = buffer[4 * a + 3] & 0xFFFF;
1773         }
1774       }
1775     }
1776
1777     base = FIRSTBASE(view_layer);
1778     while (base) {
1779       if (BASE_SELECTABLE(v3d, base)) {
1780         if (base->object->runtime.select_id == selcol) {
1781           break;
1782         }
1783       }
1784       base = base->next;
1785     }
1786     if (base) {
1787       basact = base;
1788     }
1789   }
1790   else {
1791
1792     base = startbase;
1793     while (base) {
1794       /* skip objects with select restriction, to prevent prematurely ending this loop
1795        * with an un-selectable choice */
1796       if ((base->flag & BASE_SELECTABLE) == 0) {
1797         base = base->next;
1798         if (base == NULL) {
1799           base = FIRSTBASE(view_layer);
1800         }
1801         if (base == startbase) {
1802           break;
1803         }
1804       }
1805
1806       if (BASE_SELECTABLE(v3d, base)) {
1807         for (a = 0; a < hits; a++) {
1808           if (has_bones) {
1809             /* skip non-bone objects */
1810             if ((buffer[4 * a + 3] & 0xFFFF0000)) {
1811               if (base->object->runtime.select_id == (buffer[(4 * a) + 3] & 0xFFFF)) {
1812                 basact = base;
1813               }
1814             }
1815           }
1816           else {
1817             if (base->object->runtime.select_id == (buffer[(4 * a) + 3] & 0xFFFF)) {
1818               basact = base;
1819             }
1820           }
1821         }
1822       }
1823
1824       if (basact) {
1825         break;
1826       }
1827
1828       base = base->next;
1829       if (base == NULL) {
1830         base = FIRSTBASE(view_layer);
1831       }
1832       if (base == startbase) {
1833         break;
1834       }
1835     }
1836   }
1837
1838   return basact;
1839 }
1840
1841 /* mval comes from event->mval, only use within region handlers */
1842 Base *ED_view3d_give_base_under_cursor(bContext *C, const int mval[2])
1843 {
1844   ViewContext vc;
1845   Base *basact = NULL;
1846   uint buffer[MAXPICKBUF];
1847
1848   /* setup view context for argument to callbacks */
1849   view3d_operator_needs_opengl(C);
1850   BKE_object_update_select_id(CTX_data_main(C));
1851
1852   ED_view3d_viewcontext_init(C, &vc);
1853
1854   const bool do_nearest = !XRAY_ACTIVE(vc.v3d);
1855   const int hits = mixed_bones_object_selectbuffer(
1856       &vc, buffer, mval, VIEW3D_SELECT_FILTER_NOP, do_nearest);
1857
1858   if (hits > 0) {
1859     const bool has_bones = selectbuffer_has_bones(buffer, hits);
1860     basact = mouse_select_eval_buffer(
1861         &vc, buffer, hits, vc.view_layer->object_bases.first, has_bones, do_nearest);
1862   }
1863
1864   return basact;
1865 }
1866
1867 Object *ED_view3d_give_object_under_cursor(bContext *C, const int mval[2])
1868 {
1869   Base *base = ED_view3d_give_base_under_cursor(C, mval);
1870   if (base) {
1871     return base->object;
1872   }
1873   return NULL;
1874 }
1875
1876 bool ED_view3d_is_object_under_cursor(bContext *C, const int mval[2])
1877 {
1878   return ED_view3d_give_object_under_cursor(C, mval) != NULL;
1879 }
1880
1881 static void deselect_all_tracks(MovieTracking *tracking)
1882 {
1883   MovieTrackingObject *object;
1884
1885   object = tracking->objects.first;
1886   while (object) {
1887     ListBase *tracksbase = BKE_tracking_object_get_tracks(tracking, object);
1888     MovieTrackingTrack *track = tracksbase->first;
1889
1890     while (track) {
1891       BKE_tracking_track_deselect(track, TRACK_AREA_ALL);
1892
1893       track = track->next;
1894     }
1895
1896     object = object->next;
1897   }
1898 }
1899
1900 /* mval is region coords */
1901 static bool ed_object_select_pick(bContext *C,
1902                                   const int mval[2],
1903                                   bool extend,
1904                                   bool deselect,
1905                                   bool toggle,
1906                                   bool obcenter,
1907                                   bool enumerate,
1908                                   bool object)
1909 {
1910   ViewContext vc;
1911   /* setup view context for argument to callbacks */
1912   ED_view3d_viewcontext_init(C, &vc);
1913
1914   ARegion *ar = CTX_wm_region(C);
1915   Scene *scene = CTX_data_scene(C);
1916   ViewLayer *view_layer = CTX_data_view_layer(C);
1917   View3D *v3d = CTX_wm_view3d(C);
1918   /* Don't set when the context has no active object (hidden), see: T60807. */
1919   const Base *oldbasact = vc.obact ? BASACT(view_layer) : NULL;
1920   Base *base, *startbase = NULL, *basact = NULL;
1921   const eObjectMode object_mode = oldbasact ? oldbasact->object->mode : OB_MODE_OBJECT;
1922   bool is_obedit;
1923   float dist = ED_view3d_select_dist_px() * 1.3333f;
1924   bool retval = false;
1925   int hits;
1926   const float mval_fl[2] = {(float)mval[0], (float)mval[1]};
1927
1928   is_obedit = (vc.obedit != NULL);
1929   if (object) {
1930     /* signal for view3d_opengl_select to skip editmode objects */
1931     vc.obedit = NULL;
1932   }
1933
1934   /* In pose mode we don't want to mess with object selection. */
1935   const bool is_pose_mode = (vc.obact && vc.obact->mode & OB_MODE_POSE);
1936
1937   /* always start list from basact in wire mode */
1938   startbase = FIRSTBASE(view_layer);
1939   if (oldbasact && oldbasact->next) {
1940     startbase = oldbasact->next;
1941   }
1942
1943   /* This block uses the control key to make the object selected
1944    * by its center point rather than its contents */
1945
1946   /* in editmode do not activate */
1947   if (obcenter) {
1948
1949     /* note; shift+alt goes to group-flush-selecting */
1950     if (enumerate) {
1951       basact = object_mouse_select_menu(C, &vc, NULL, 0, mval, toggle);
1952     }
1953     else {
1954       base = startbase;
1955       while (base) {
1956         if (BASE_SELECTABLE(v3d, base)) {
1957           float screen_co[2];
1958           if (ED_view3d_project_float_global(ar,
1959                                              base->object->obmat[3],
1960                                              screen_co,
1961                                              V3D_PROJ_TEST_CLIP_BB | V3D_PROJ_TEST_CLIP_WIN |
1962                                                  V3D_PROJ_TEST_CLIP_NEAR) == V3D_PROJ_RET_OK) {
1963             float dist_temp = len_manhattan_v2v2(mval_fl, screen_co);
1964             if (base == oldbasact) {
1965               dist_temp += 10.0f;
1966             }
1967             if (dist_temp < dist) {
1968               dist = dist_temp;
1969               basact = base;
1970             }
1971           }
1972         }
1973         base = base->next;
1974
1975         if (base == NULL) {
1976           base = FIRSTBASE(view_layer);
1977         }
1978         if (base == startbase) {
1979           break;
1980         }
1981       }
1982     }
1983     if (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) {
1984       if (is_obedit == false) {
1985         if (basact && !BKE_object_is_mode_compat(basact->object, object_mode)) {
1986           if (object_mode == OB_MODE_OBJECT) {
1987             struct Main *bmain = CTX_data_main(C);
1988             ED_object_mode_generic_exit(bmain, vc.depsgraph, scene, basact->object);
1989           }
1990           if (!BKE_object_is_mode_compat(basact->object, object_mode)) {
1991             basact = NULL;
1992           }
1993         }
1994       }
1995     }
1996   }
1997   else {
1998     uint buffer[MAXPICKBUF];
1999     bool do_nearest;
2000
2001     // TIMEIT_START(select_time);
2002
2003     /* if objects have posemode set, the bones are in the same selection buffer */
2004     const eV3DSelectObjectFilter select_filter = ((object == false) ?
2005                                                       ED_view3d_select_filter_from_mode(scene,
2006                                                                                         vc.obact) :
2007                                                       VIEW3D_SELECT_FILTER_NOP);
2008     hits = mixed_bones_object_selectbuffer_extended(
2009         &vc, buffer, mval, select_filter, true, enumerate, &do_nearest);
2010
2011     // TIMEIT_END(select_time);
2012
2013     if (hits > 0) {
2014       /* note: bundles are handling in the same way as bones */
2015       const bool has_bones = selectbuffer_has_bones(buffer, hits);
2016
2017       /* note; shift+alt goes to group-flush-selecting */
2018       if (enumerate) {
2019         basact = object_mouse_select_menu(C, &vc, buffer, hits, mval, toggle);
2020       }
2021       else {
2022         basact = mouse_select_eval_buffer(&vc, buffer, hits, startbase, has_bones, do_nearest);
2023       }
2024
2025       if (has_bones && basact) {
2026         if (basact->object->type == OB_CAMERA) {
2027           if (oldbasact == basact) {
2028             int i, hitresult;
2029             bool changed = false;
2030
2031             for (i = 0; i < hits; i++) {
2032               hitresult = buffer[3 + (i * 4)];
2033
2034               /* if there's bundles in buffer select bundles first,
2035                * so non-camera elements should be ignored in buffer */
2036               if (basact->object->runtime.select_id != (hitresult & 0xFFFF)) {
2037                 continue;
2038               }
2039
2040               /* index of bundle is 1<<16-based. if there's no "bone" index
2041                * in height word, this buffer value belongs to camera. not to bundle
2042                */
2043               if (buffer[4 * i + 3] & 0xFFFF0000) {
2044                 MovieClip *clip = BKE_object_movieclip_get(scene, basact->object, false);
2045                 MovieTracking *tracking = &clip->tracking;
2046                 ListBase *tracksbase;
2047                 MovieTrackingTrack *track;
2048
2049                 track = BKE_tracking_track_get_indexed(
2050                     &clip->tracking, hitresult >> 16, &tracksbase);
2051
2052                 if (TRACK_SELECTED(track) && extend) {
2053                   changed = false;
2054                   BKE_tracking_track_deselect(track, TRACK_AREA_ALL);
2055                 }
2056                 else {
2057                   int oldsel = TRACK_SELECTED(track) ? 1 : 0;
2058                   if (!extend) {
2059                     deselect_all_tracks(tracking);
2060                   }
2061
2062                   BKE_tracking_track_select(tracksbase, track, TRACK_AREA_ALL, extend);
2063
2064                   if (oldsel != (TRACK_SELECTED(track) ? 1 : 0)) {
2065                     changed = true;
2066                   }
2067                 }
2068
2069                 ED_object_base_select(basact, BA_SELECT);
2070
2071                 retval = true;
2072
2073                 DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
2074                 DEG_id_tag_update(&clip->id, ID_RECALC_SELECT);
2075                 WM_event_add_notifier(C, NC_MOVIECLIP | ND_SELECT, track);
2076                 WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
2077
2078                 break;
2079               }
2080             }
2081
2082             if (!changed) {
2083               /* fallback to regular object selection if no new bundles were selected,
2084                * allows to select object parented to reconstruction object */
2085               basact = mouse_select_eval_buffer(&vc, buffer, hits, startbase, 0, do_nearest);
2086             }
2087           }
2088         }
2089         else if (ED_armature_pose_select_pick_with_buffer(view_layer,
2090                                                           v3d,
2091                                                           basact,
2092                                                           buffer,
2093                                                           hits,
2094                                                           extend,
2095                                                           deselect,
2096                                                           toggle,
2097                                                           do_nearest)) {
2098           /* then bone is found */
2099
2100           /* we make the armature selected:
2101            * not-selected active object in posemode won't work well for tools */
2102           ED_object_base_select(basact, BA_SELECT);
2103
2104           retval = true;
2105           WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, basact->object);
2106           WM_event_add_notifier(C, NC_OBJECT | ND_BONE_ACTIVE, basact->object);
2107
2108           /* in weightpaint, we use selected bone to select vertexgroup,
2109            * so no switch to new active object */
2110           if (oldbasact && (oldbasact->object->mode & OB_MODE_WEIGHT_PAINT)) {
2111             /* prevent activating */
2112             basact = NULL;
2113           }
2114         }
2115         /* prevent bone selecting to pass on to object selecting */
2116         if (basact == oldbasact) {
2117           basact = NULL;
2118         }
2119       }
2120
2121       if (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) {
2122         if (is_obedit == false) {
2123           if (basact && !BKE_object_is_mode_compat(basact->object, object_mode)) {
2124             if (object_mode == OB_MODE_OBJECT) {
2125               struct Main *bmain = CTX_data_main(C);
2126               ED_object_mode_generic_exit(bmain, vc.depsgraph, scene, basact->object);
2127             }
2128             if (!BKE_object_is_mode_compat(basact->object, object_mode)) {
2129               basact = NULL;
2130             }
2131           }
2132         }
2133       }
2134     }
2135   }
2136
2137   if (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) {
2138     /* Disallow switching modes,
2139      * special exception for edit-mode - vertex-parent operator. */
2140     if (is_obedit == false) {
2141       if (oldbasact && basact) {
2142         if ((oldbasact->object->mode != basact->object->mode) &&
2143             (oldbasact->object->mode & basact->object->mode) == 0) {
2144           basact = NULL;
2145         }
2146       }
2147     }
2148   }
2149
2150   /* Ensure code above doesn't change the active base. */
2151   BLI_assert(oldbasact == (vc.obact ? BASACT(view_layer) : NULL));
2152
2153   /* so, do we have something selected? */
2154   if (basact) {
2155     retval = true;
2156
2157     if (vc.obedit) {
2158       /* only do select */
2159       object_deselect_all_except(view_layer, basact);
2160       ED_object_base_select(basact, BA_SELECT);
2161     }
2162     /* also prevent making it active on mouse selection */
2163     else if (BASE_SELECTABLE(v3d, basact)) {
2164       if (extend) {
2165         ED_object_base_select(basact, BA_SELECT);
2166       }
2167       else if (deselect) {
2168         ED_object_base_select(basact, BA_DESELECT);
2169       }
2170       else if (toggle) {
2171         if (basact->flag & BASE_SELECTED) {
2172           if (basact == oldbasact) {
2173             ED_object_base_select(basact, BA_DESELECT);
2174           }
2175         }
2176         else {
2177           ED_object_base_select(basact, BA_SELECT);
2178         }
2179       }
2180       else {
2181         /* When enabled, this puts other objects out of multi pose-mode. */
2182         if (is_pose_mode == false) {
2183           object_deselect_all_except(view_layer, basact);
2184           ED_object_base_select(basact, BA_SELECT);
2185         }
2186       }
2187
2188       if ((oldbasact != basact) && (is_obedit == false)) {
2189         ED_object_base_activate(C, basact); /* adds notifier */
2190       }
2191
2192       /* Set special modes for grease pencil
2193        * The grease pencil modes are not real modes, but a hack to make the interface
2194        * consistent, so need some tricks to keep UI synchronized */
2195       // XXX: This stuff needs reviewing (Aligorith)
2196       if (false && (((oldbasact) && oldbasact->object->type == OB_GPENCIL) ||
2197                     (basact->object->type == OB_GPENCIL))) {
2198         /* set cursor */
2199         if (ELEM(basact->object->mode,
2200                  OB_MODE_PAINT_GPENCIL,
2201                  OB_MODE_SCULPT_GPENCIL,
2202                  OB_MODE_WEIGHT_GPENCIL)) {
2203           ED_gpencil_toggle_brush_cursor(C, true, NULL);
2204         }
2205         else {
2206           /* TODO: maybe is better use restore */
2207           ED_gpencil_toggle_brush_cursor(C, false, NULL);
2208         }
2209       }
2210     }
2211
2212     DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
2213     WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
2214   }
2215
2216   return retval;
2217 }
2218
2219 /* mouse selection in weight paint */
2220 /* gets called via generic mouse select operator */
2221 static bool ed_wpaint_vertex_select_pick(
2222     bContext *C, const int mval[2], bool extend, bool deselect, bool toggle, Object *obact)
2223 {
2224   View3D *v3d = CTX_wm_view3d(C);
2225   const bool use_zbuf = !XRAY_ENABLED(v3d);
2226
2227   Mesh *me = obact->data; /* already checked for NULL */
2228   uint index = 0;
2229   MVert *mv;
2230
2231   if (ED_mesh_pick_vert(C, obact, mval, ED_MESH_PICK_DEFAULT_VERT_DIST, use_zbuf, &index)) {
2232     mv = &me->mvert[index];
2233     if (extend) {
2234       mv->flag |= SELECT;
2235     }
2236     else if (deselect) {
2237       mv->flag &= ~SELECT;
2238     }
2239     else if (toggle) {
2240       mv->flag ^= SELECT;
2241     }
2242     else {
2243       paintvert_deselect_all_visible(obact, SEL_DESELECT, false);
2244       mv->flag |= SELECT;
2245     }
2246
2247     /* update mselect */
2248     if (mv->flag & SELECT) {
2249       BKE_mesh_mselect_active_set(me, index, ME_VSEL);
2250     }
2251     else {
2252       BKE_mesh_mselect_validate(me);
2253     }
2254
2255     paintvert_flush_flags(obact);
2256     paintvert_tag_select_update(C, obact);
2257     return true;
2258   }
2259   return false;
2260 }
2261
2262 static int view3d_select_exec(bContext *C, wmOperator *op)
2263 {
2264   Scene *scene = CTX_data_scene(C);
2265   Object *obedit = CTX_data_edit_object(C);
2266   Object *obact = CTX_data_active_object(C);
2267   bool extend = RNA_boolean_get(op->ptr, "extend");
2268   bool deselect = RNA_boolean_get(op->ptr, "deselect");
2269   bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all");
2270   bool toggle = RNA_boolean_get(op->ptr, "toggle");
2271   bool center = RNA_boolean_get(op->ptr, "center");
2272   bool enumerate = RNA_boolean_get(op->ptr, "enumerate");
2273   /* only force object select for editmode to support vertex parenting,
2274    * or paint-select to allow pose bone select with vert/face select */
2275   bool object = (RNA_boolean_get(op->ptr, "object") &&
2276                  (obedit || BKE_paint_select_elem_test(obact) ||
2277                   /* so its possible to select bones in weightpaint mode (LMB select) */
2278                   (obact && (obact->mode & OB_MODE_WEIGHT_PAINT) &&
2279                    BKE_object_pose_armature_get(obact))));
2280
2281   bool retval = false;
2282   int location[2];
2283
2284   RNA_int_get_array(op->ptr, "location", location);
2285
2286   view3d_operator_needs_opengl(C);
2287   BKE_object_update_select_id(CTX_data_main(C));
2288
2289   if (object) {
2290     obedit = NULL;
2291     obact = NULL;
2292
2293     /* ack, this is incorrect but to do this correctly we would need an
2294      * alternative editmode/objectmode keymap, this copies the functionality
2295      * from 2.4x where Ctrl+Select in editmode does object select only */
2296     center = false;
2297   }
2298
2299   if (obedit && object == false) {
2300     if (obedit->type == OB_MESH) {
2301       retval = EDBM_select_pick(C, location, extend, deselect, toggle);
2302       if (!retval && deselect_all) {
2303         retval = EDBM_mesh_deselect_all_multi(C);
2304       }
2305     }
2306     else if (obedit->type == OB_ARMATURE) {
2307       retval = ED_armature_edit_select_pick(C, location, extend, deselect, toggle);
2308       if (!retval && deselect_all) {
2309         retval = ED_armature_edit_deselect_all_visible_multi(C);
2310       }
2311     }
2312     else if (obedit->type == OB_LATTICE) {
2313       retval = ED_lattice_select_pick(C, location, extend, deselect, toggle);
2314       if (!retval && deselect_all) {
2315         retval = ED_lattice_deselect_all_multi(C);
2316       }
2317     }
2318     else if (ELEM(obedit->type, OB_CURVE, OB_SURF)) {
2319       retval = ED_curve_editnurb_select_pick(C, location, extend, deselect, toggle);
2320       if (!retval && deselect_all) {
2321         retval = ED_curve_deselect_all_multi(C);
2322       }
2323     }
2324     else if (obedit->type == OB_MBALL) {
2325       retval = ED_mball_select_pick(C, location, extend, deselect, toggle);
2326       if (!retval && deselect_all) {
2327         retval = ED_mball_deselect_all_multi(C);
2328       }
2329     }
2330     else if (obedit->type == OB_FONT) {
2331       retval = ED_curve_editfont_select_pick(C, location, extend, deselect, toggle);
2332       if (!retval && deselect_all) {
2333         /* pass */
2334       }
2335     }
2336   }
2337   else if (obact && obact->mode & OB_MODE_PARTICLE_EDIT) {
2338     retval = PE_mouse_particles(C, location, extend, deselect, toggle);
2339     if (!retval && deselect_all) {
2340       retval = PE_deselect_all_visible(C);
2341     }
2342   }
2343   else if (obact && BKE_paint_select_face_test(obact)) {
2344     retval = paintface_mouse_select(C, obact, location, extend, deselect, toggle);
2345     if (!retval && deselect_all) {
2346       retval = paintface_deselect_all_visible(C, CTX_data_active_object(C), SEL_DESELECT, false);
2347     }
2348   }
2349   else if (BKE_paint_select_vert_test(obact)) {
2350     retval = ed_wpaint_vertex_select_pick(C, location, extend, deselect, toggle, obact);
2351     if (!retval && deselect_all) {
2352       retval = paintvert_deselect_all_visible(obact, SEL_DESELECT, false);
2353     }
2354   }
2355   else {
2356     retval = ed_object_select_pick(
2357         C, location, extend, deselect, toggle, center, enumerate, object);
2358     if (!retval && deselect_all) {
2359       if (ED_pose_object_from_context(C)) {
2360         retval = ED_pose_deselect_all_multi(C, SEL_DESELECT, false);
2361       }
2362       else {
2363         retval = ED_object_base_deselect_all(
2364             CTX_data_view_layer(C), CTX_wm_view3d(C), SEL_DESELECT);
2365         DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
2366       }
2367     }
2368   }
2369
2370   /* Pass-through allows tweaks
2371    * FINISHED to signal one operator worked
2372    * */
2373   if (retval) {
2374     WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
2375     return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED;
2376   }
2377   else {
2378     return OPERATOR_PASS_THROUGH; /* nothing selected, just passthrough */
2379   }
2380 }
2381
2382 static int view3d_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2383 {
2384   RNA_int_set_array(op->ptr, "location", event->mval);
2385
2386   return view3d_select_exec(C, op);
2387 }
2388
2389 void VIEW3D_OT_select(wmOperatorType *ot)
2390 {
2391   PropertyRNA *prop;
2392
2393   /* identifiers */
2394   ot->name = "Select";
2395   ot->description = "Select and activate item(s)";
2396   ot->idname = "VIEW3D_OT_select";
2397
2398   /* api callbacks */
2399   ot->invoke = view3d_select_invoke;
2400   ot->exec = view3d_select_exec;
2401   ot->poll = ED_operator_view3d_active;
2402
2403   /* flags */
2404   ot->flag = OPTYPE_UNDO;
2405
2406   /* properties */
2407   WM_operator_properties_mouse_select(ot);
2408
2409   prop = RNA_def_boolean(
2410       ot->srna,
2411       "center",
2412       0,
2413       "Center",
2414       "Use the object center when selecting, in editmode used to extend object selection");
2415   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2416   prop = RNA_def_boolean(
2417       ot->srna, "enumerate", 0, "Enumerate", "List objects under the mouse (object mode only)");
2418   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2419   prop = RNA_def_boolean(ot->srna, "object", 0, "Object", "Use object selection (editmode only)");
2420   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2421
2422   prop = RNA_def_int_vector(ot->srna,
2423                             "location",
2424                             2,
2425                             NULL,
2426                             INT_MIN,
2427                             INT_MAX,
2428                             "Location",
2429                             "Mouse location",
2430                             INT_MIN,
2431                             INT_MAX);
2432   RNA_def_property_flag(prop, PROP_HIDDEN);
2433 }
2434
2435 /** \} */
2436
2437 /* -------------------------------------------------------------------- */
2438 /** \name Box Select
2439  * \{ */
2440
2441 typedef struct BoxSelectUserData {
2442   ViewContext *vc;
2443   const rcti *rect;
2444   const rctf *rect_fl;
2445   rctf _rect_fl;
2446   eSelectOp sel_op;
2447
2448   /* runtime */
2449   bool is_done;
2450   bool is_changed;
2451 } BoxSelectUserData;
2452
2453 static void view3d_userdata_boxselect_init(BoxSelectUserData *r_data,
2454                                            ViewContext *vc,
2455                                            const rcti *rect,
2456                                            const eSelectOp sel_op)
2457 {
2458   r_data->vc = vc;
2459
2460   r_data->rect = rect;
2461   r_data->rect_fl = &r_data->_rect_fl;
2462   BLI_rctf_rcti_copy(&r_data->_rect_fl, rect);
2463
2464   r_data->sel_op = sel_op;
2465
2466   /* runtime */
2467   r_data->is_done = false;
2468   r_data->is_changed = false;
2469 }
2470
2471 bool edge_inside_circle(const float cent[2],
2472                         float radius,
2473                         const float screen_co_a[2],
2474                         const float screen_co_b[2])
2475 {
2476   const float radius_squared = radius * radius;
2477   return (dist_squared_to_line_segment_v2(cent, screen_co_a, screen_co_b) < radius_squared);
2478 }
2479
2480 static void do_paintvert_box_select__doSelectVert(void *userData,
2481                                                   MVert *mv,
2482                                                   const float screen_co[2],
2483                                                   int UNUSED(index))
2484 {
2485   BoxSelectUserData *data = userData;
2486   const bool is_select = mv->flag & SELECT;
2487   const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co);
2488   const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
2489   if (sel_op_result != -1) {
2490     SET_FLAG_FROM_TEST(mv->flag, sel_op_result, SELECT);
2491     data->is_changed = true;
2492   }
2493 }
2494 static bool do_paintvert_box_select(ViewContext *vc,
2495                                     wmGenericUserData *wm_userdata,
2496                                     const rcti *rect,
2497                                     const eSelectOp sel_op)
2498 {
2499   const bool use_zbuf = !XRAY_ENABLED(vc->v3d);
2500
2501   Mesh *me;
2502
2503   me = vc->obact->data;
2504   if ((me == NULL) || (me->totvert == 0)) {
2505     return OPERATOR_CANCELLED;
2506   }
2507
2508   bool changed = false;
2509   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
2510     changed |= paintvert_deselect_all_visible(vc->obact, SEL_DESELECT, false);
2511   }
2512
2513   if (BLI_rcti_is_empty(rect)) {
2514     /* pass */
2515   }
2516   else if (use_zbuf) {
2517     struct EditSelectBuf_Cache *esel = wm_userdata->data;
2518     if (wm_userdata->data == NULL) {
2519       editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc);
2520       esel = wm_userdata->data;
2521       const uint buffer_len = EDBM_select_id_context_elem_len(esel->sel_id_ctx);
2522       esel->select_bitmap = ED_select_buffer_bitmap_from_rect(buffer_len, rect);
2523     }
2524     if (esel->select_bitmap != NULL) {
2525       changed |= edbm_backbuf_check_and_select_verts_obmode(me, esel, sel_op);
2526     }
2527   }
2528   else {
2529     BoxSelectUserData data;
2530
2531     view3d_userdata_boxselect_init(&data, vc, rect, sel_op);
2532
2533     ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d);
2534
2535     meshobject_foreachScreenVert(
2536         vc, do_paintvert_box_select__doSelectVert, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
2537     changed |= data.is_changed;
2538   }
2539
2540   if (changed) {
2541     if (SEL_OP_CAN_DESELECT(sel_op)) {
2542       BKE_mesh_mselect_validate(me);
2543     }
2544     paintvert_flush_flags(vc->obact);
2545     paintvert_tag_select_update(vc->C, vc->obact);
2546   }
2547   return changed;
2548 }
2549
2550 static bool do_paintface_box_select(ViewContext *vc,
2551                                     wmGenericUserData *wm_userdata,
2552                                     const rcti *rect,
2553                                     int sel_op)
2554 {
2555   Object *ob = vc->obact;
2556   Mesh *me;
2557
2558   me = BKE_mesh_from_object(ob);
2559   if ((me == NULL) || (me->totpoly == 0)) {
2560     return false;
2561   }
2562
2563   bool changed = false;
2564   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
2565     changed |= paintface_deselect_all_visible(vc->C, vc->obact, SEL_DESELECT, false);
2566   }
2567
2568   if (BLI_rcti_is_empty(rect)) {
2569     /* pass */
2570   }
2571   else {
2572     struct EditSelectBuf_Cache *esel = wm_userdata->data;
2573     if (wm_userdata->data == NULL) {
2574       editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc);
2575       esel = wm_userdata->data;
2576       const uint buffer_len = EDBM_select_id_context_elem_len(esel->sel_id_ctx);
2577       esel->select_bitmap = ED_select_buffer_bitmap_from_rect(buffer_len, rect);
2578     }
2579     if (esel->select_bitmap != NULL) {
2580       changed |= edbm_backbuf_check_and_select_faces_obmode(me, esel, sel_op);
2581     }
2582   }
2583
2584   if (changed) {
2585     paintface_flush_flags(vc->C, vc->obact, SELECT);
2586   }
2587   return changed;
2588 }
2589
2590 static void do_nurbs_box_select__doSelect(void *userData,
2591                                           Nurb *UNUSED(nu),
2592                                           BPoint *bp,
2593                                           BezTriple *bezt,
2594                                           int beztindex,
2595                                           const float screen_co[2])
2596 {
2597   BoxSelectUserData *data = userData;
2598
2599   const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co);
2600   if (bp) {
2601     const bool is_select = bp->f1 & SELECT;
2602     const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
2603     if (sel_op_result != -1) {
2604       SET_FLAG_FROM_TEST(bp->f1, sel_op_result, SELECT);
2605       data->is_changed = true;
2606     }
2607   }
2608   else {
2609     if ((data->vc->v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_CU_HANDLES) == 0) {
2610       /* can only be (beztindex == 0) here since handles are hidden */
2611       const bool is_select = bezt->f2 & SELECT;
2612       const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
2613       if (sel_op_result != -1) {
2614         SET_FLAG_FROM_TEST(bezt->f2, sel_op_result, SELECT);
2615         data->is_changed = true;
2616       }
2617       bezt->f1 = bezt->f3 = bezt->f2;
2618     }
2619     else {
2620       char *flag_p = (&bezt->f1) + beztindex;
2621       const bool is_select = *flag_p & SELECT;
2622       const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
2623       if (sel_op_result != -1) {
2624         SET_FLAG_FROM_TEST(*flag_p, sel_op_result, SELECT);
2625         data->is_changed = true;
2626       }
2627     }
2628   }
2629 }
2630 static bool do_nurbs_box_select(ViewContext *vc, rcti *rect, const eSelectOp sel_op)
2631 {
2632   BoxSelectUserData data;
2633
2634   view3d_userdata_boxselect_init(&data, vc, rect, sel_op);
2635
2636   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
2637     Curve *curve = (Curve *)vc->obedit->data;
2638     data.is_changed |= ED_curve_deselect_all(curve->editnurb);
2639   }
2640
2641   ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
2642   nurbs_foreachScreenVert(vc, do_nurbs_box_select__doSelect, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
2643   BKE_curve_nurb_vert_active_validate(vc->obedit->data);
2644
2645   return data.is_changed;
2646 }
2647
2648 static void do_lattice_box_select__doSelect(void *userData, BPoint *bp, const float screen_co[2])
2649 {
2650   BoxSelectUserData *data = userData;
2651   const bool is_select = bp->f1 & SELECT;
2652   const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co);
2653   const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
2654   if (sel_op_result != -1) {
2655     SET_FLAG_FROM_TEST(bp->f1, sel_op_result, SELECT);
2656     data->is_changed = true;
2657   }
2658 }
2659 static bool do_lattice_box_select(ViewContext *vc, rcti *rect, const eSelectOp sel_op)
2660 {
2661   BoxSelectUserData data;
2662
2663   view3d_userdata_boxselect_init(&data, vc, rect, sel_op);
2664
2665   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
2666     data.is_changed |= ED_lattice_flags_set(vc->obedit, 0);
2667   }
2668
2669   ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
2670   lattice_foreachScreenVert(
2671       vc, do_lattice_box_select__doSelect, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
2672
2673   return data.is_changed;
2674 }
2675
2676 static void do_mesh_box_select__doSelectVert(void *userData,
2677                                              BMVert *eve,
2678                                              const float screen_co[2],
2679                                              int UNUSED(index))
2680 {
2681   BoxSelectUserData *data = userData;
2682   const bool is_select = BM_elem_flag_test(eve, BM_ELEM_SELECT);
2683   const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co);
2684   const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
2685   if (sel_op_result != -1) {
2686     BM_vert_select_set(data->vc->em->bm, eve, sel_op_result);
2687     data->is_changed = true;
2688   }
2689 }
2690 struct BoxSelectUserData_ForMeshEdge {
2691   BoxSelectUserData *data;
2692   struct EditSelectBuf_Cache *esel;
2693   uint backbuf_offset;
2694 };
2695 static void do_mesh_box_select__doSelectEdge_pass0(
2696     void *userData, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int index)
2697 {
2698   struct BoxSelectUserData_ForMeshEdge *data_for_edge = userData;
2699   BoxSelectUserData *data = data_for_edge->data;
2700   const bool is_visible = (data_for_edge->esel ?
2701                                BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap,
2702                                                     data_for_edge->backbuf_offset + index) :
2703                                true);
2704   const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT);
2705   const bool is_inside = (is_visible &&
2706                           edge_fully_inside_rect(data->rect_fl, screen_co_a, screen_co_b));
2707   const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
2708   if (sel_op_result != -1) {
2709     BM_edge_select_set(data->vc->em->bm, eed, sel_op_result);
2710     data->is_done = true;
2711     data->is_changed = true;
2712   }
2713 }
2714 static void do_mesh_box_select__doSelectEdge_pass1(
2715     void *userData, BMEdge *eed, const float screen_co_a[2], const float screen_co_b[2], int index)
2716 {
2717   struct BoxSelectUserData_ForMeshEdge *data_for_edge = userData;
2718   BoxSelectUserData *data = data_for_edge->data;
2719   const bool is_visible = (data_for_edge->esel ?
2720                                BLI_BITMAP_TEST_BOOL(data_for_edge->esel->select_bitmap,
2721                                                     data_for_edge->backbuf_offset + index) :
2722                                true);
2723   const bool is_select = BM_elem_flag_test(eed, BM_ELEM_SELECT);
2724   const bool is_inside = (is_visible && edge_inside_rect(data->rect_fl, screen_co_a, screen_co_b));
2725   const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
2726   if (sel_op_result != -1) {
2727     BM_edge_select_set(data->vc->em->bm, eed, sel_op_result);
2728     data->is_changed = true;
2729   }
2730 }
2731 static void do_mesh_box_select__doSelectFace(void *userData,
2732                                              BMFace *efa,
2733                                              const float screen_co[2],
2734                                              int UNUSED(index))
2735 {
2736   BoxSelectUserData *data = userData;
2737   const bool is_select = BM_elem_flag_test(efa, BM_ELEM_SELECT);
2738   const bool is_inside = BLI_rctf_isect_pt_v(data->rect_fl, screen_co);
2739   const int sel_op_result = ED_select_op_action_deselected(data->sel_op, is_select, is_inside);
2740   if (sel_op_result != -1) {
2741     BM_face_select_set(data->vc->em->bm, efa, sel_op_result);
2742     data->is_changed = true;
2743   }
2744 }
2745 static bool do_mesh_box_select(ViewContext *vc,
2746                                wmGenericUserData *wm_userdata,
2747                                const rcti *rect,
2748                                const eSelectOp sel_op)
2749 {
2750   BoxSelectUserData data;
2751   ToolSettings *ts = vc->scene->toolsettings;
2752
2753   view3d_userdata_boxselect_init(&data, vc, rect, sel_op);
2754
2755   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
2756     if (vc->em->bm->totvertsel) {
2757       EDBM_flag_disable_all(vc->em, BM_ELEM_SELECT);
2758       data.is_changed = true;
2759     }
2760   }
2761
2762   /* for non zbuf projections, don't change the GL state */
2763   ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
2764
2765   GPU_matrix_set(vc->rv3d->viewmat);
2766
2767   const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d);
2768
2769   struct EditSelectBuf_Cache *esel = wm_userdata->data;
2770   if (use_zbuf) {
2771     if (wm_userdata->data == NULL) {
2772       editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc);
2773       esel = wm_userdata->data;
2774       const uint buffer_len = EDBM_select_id_context_elem_len(esel->sel_id_ctx);
2775       esel->select_bitmap = ED_select_buffer_bitmap_from_rect(buffer_len, rect);
2776     }
2777   }
2778
2779   if (ts->selectmode & SCE_SELECT_VERTEX) {
2780     if (use_zbuf) {
2781       data.is_changed |= edbm_backbuf_check_and_select_verts(esel, vc->obedit, vc->em, sel_op);
2782     }
2783     else {
2784       mesh_foreachScreenVert(
2785           vc, do_mesh_box_select__doSelectVert, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
2786     }
2787   }
2788   if (ts->selectmode & SCE_SELECT_EDGE) {
2789     /* Does both use_zbuf and non-use_zbuf versions (need screen cos for both) */
2790     struct BoxSelectUserData_ForMeshEdge cb_data = {
2791         .data = &data,
2792         .esel = use_zbuf ? esel : NULL,
2793         .backbuf_offset = use_zbuf ?
2794                               EDBM_select_id_context_offset_for_object_elem(
2795                                   esel->sel_id_ctx, vc->obedit->runtime.select_id, BM_EDGE) :
2796                               0,
2797     };
2798     mesh_foreachScreenEdge(
2799         vc, do_mesh_box_select__doSelectEdge_pass0, &cb_data, V3D_PROJ_TEST_CLIP_NEAR);
2800     if (data.is_done == false) {
2801       mesh_foreachScreenEdge(
2802           vc, do_mesh_box_select__doSelectEdge_pass1, &cb_data, V3D_PROJ_TEST_CLIP_NEAR);
2803     }
2804   }
2805
2806   if (ts->selectmode & SCE_SELECT_FACE) {
2807     if (use_zbuf) {
2808       data.is_changed |= edbm_backbuf_check_and_select_faces(esel, vc->obedit, vc->em, sel_op);
2809     }
2810     else {
2811       mesh_foreachScreenFace(
2812           vc, do_mesh_box_select__doSelectFace, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
2813     }
2814   }
2815
2816   if (data.is_changed) {
2817     EDBM_selectmode_flush(vc->em);
2818   }
2819   return data.is_changed;
2820 }
2821
2822 static bool do_meta_box_select(ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
2823 {
2824   Object *ob = vc->obedit;
2825   MetaBall *mb = (MetaBall *)ob->data;
2826   MetaElem *ml;
2827   int a;
2828   bool changed = false;
2829
2830   uint buffer[MAXPICKBUF];
2831   int hits;
2832
2833   hits = view3d_opengl_select(
2834       vc, buffer, MAXPICKBUF, rect, VIEW3D_SELECT_ALL, VIEW3D_SELECT_FILTER_NOP);
2835
2836   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
2837     changed |= BKE_mball_deselect_all(mb);
2838   }
2839
2840   int metaelem_id = 0;
2841   for (ml = mb->editelems->first; ml; ml = ml->next, metaelem_id += 0x10000) {
2842     bool is_inside_radius = false;
2843     bool is_inside_stiff = false;
2844
2845     for (a = 0; a < hits; a++) {
2846       int hitresult = buffer[(4 * a) + 3];
2847
2848       if (hitresult == -1) {
2849         continue;
2850       }
2851       else if (hitresult & MBALL_NOSEL) {
2852         continue;
2853       }
2854
2855       const uint hit_object = hitresult & 0xFFFF;
2856       if (vc->obedit->runtime.select_id != hit_object) {
2857         continue;
2858       }
2859
2860       if (metaelem_id != (hitresult & 0xFFFF0000 & ~(MBALLSEL_ANY))) {
2861         continue;
2862       }
2863
2864       if (hitresult & MBALLSEL_RADIUS) {
2865         is_inside_radius = true;
2866         break;
2867       }
2868
2869       if (hitresult & MBALLSEL_STIFF) {
2870         is_inside_stiff = true;
2871         break;
2872       }
2873     }
2874     const int flag_prev = ml->flag;
2875     if (is_inside_radius) {
2876       ml->flag |= MB_SCALE_RAD;
2877     }
2878     if (is_inside_stiff) {
2879       ml->flag &= ~MB_SCALE_RAD;
2880     }
2881
2882     const bool is_select = (ml->flag & SELECT);
2883     const bool is_inside = is_inside_radius || is_inside_stiff;
2884
2885     const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
2886     if (sel_op_result != -1) {
2887       SET_FLAG_FROM_TEST(ml->flag, sel_op_result, SELECT);
2888     }
2889     changed |= (flag_prev != ml->flag);
2890   }
2891
2892   return changed;
2893 }
2894
2895 static bool do_armature_box_select(ViewContext *vc, const rcti *rect, const eSelectOp sel_op)
2896 {
2897   bool changed = false;
2898   int a;
2899
2900   uint buffer[MAXPICKBUF];
2901   int hits;
2902
2903   hits = view3d_opengl_select(
2904       vc, buffer, MAXPICKBUF, rect, VIEW3D_SELECT_ALL, VIEW3D_SELECT_FILTER_NOP);
2905
2906   uint bases_len = 0;
2907   Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(
2908       vc->view_layer, vc->v3d, &bases_len);
2909
2910   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
2911     changed |= ED_armature_edit_deselect_all_visible_multi_ex(bases, bases_len);
2912   }
2913
2914   for (uint base_index = 0; base_index < bases_len; base_index++) {
2915     Object *obedit = bases[base_index]->object;
2916     obedit->id.tag &= ~LIB_TAG_DOIT;
2917
2918     bArmature *arm = obedit->data;
2919     ED_armature_ebone_listbase_temp_clear(arm->edbo);
2920   }
2921
2922   /* first we only check points inside the border */
2923   for (a = 0; a < hits; a++) {
2924     int select_id = buffer[(4 * a) + 3];
2925     if (select_id != -1) {
2926       if ((select_id & 0xFFFF0000) == 0) {
2927         continue;
2928       }
2929
2930       EditBone *ebone;
2931       Base *base_edit = ED_armature_base_and_ebone_from_select_buffer(
2932           bases, bases_len, select_id, &ebone);
2933       ebone->temp.i |= select_id & BONESEL_ANY;
2934       base_edit->object->id.tag |= LIB_TAG_DOIT;
2935     }
2936   }
2937
2938   for (uint base_index = 0; base_index < bases_len; base_index++) {
2939     Object *obedit = bases[base_index]->object;
2940     if (obedit->id.tag & LIB_TAG_DOIT) {
2941       obedit->id.tag &= ~LIB_TAG_DOIT;
2942       changed |= ED_armature_edit_select_op_from_tagged(obedit->data, sel_op);
2943     }
2944   }
2945
2946   MEM_freeN(bases);
2947
2948   return changed;
2949 }
2950
2951 /**
2952  * Compare result of 'GPU_select': 'uint[4]',
2953  * needed for when we need to align with object draw-order.
2954  */
2955 static int opengl_bone_select_buffer_cmp(const void *sel_a_p, const void *sel_b_p)
2956 {
2957   /* 4th element is select id */
2958   uint sel_a = ((uint *)sel_a_p)[3];
2959   uint sel_b = ((uint *)sel_b_p)[3];
2960
2961 #ifdef __BIG_ENDIAN__
2962   BLI_endian_switch_uint32(&sel_a);
2963   BLI_endian_switch_uint32(&sel_b);
2964 #endif
2965
2966   if (sel_a < sel_b) {
2967     return -1;
2968   }
2969   else if (sel_a > sel_b) {
2970     return 1;
2971   }
2972   else {
2973     return 0;
2974   }
2975 }
2976
2977 static bool do_object_box_select(bContext *C, ViewContext *vc, rcti *rect, const eSelectOp sel_op)
2978 {
2979   View3D *v3d = vc->v3d;
2980   int totobj = MAXPICKBUF; /* XXX solve later */
2981
2982   /* selection buffer now has bones potentially too, so we add MAXPICKBUF */
2983   uint *vbuffer = MEM_mallocN(4 * (totobj + MAXPICKELEMS) * sizeof(uint[4]), "selection buffer");
2984   const eV3DSelectObjectFilter select_filter = ED_view3d_select_filter_from_mode(vc->scene,
2985                                                                                  vc->obact);
2986   const int hits = view3d_opengl_select(
2987       vc, vbuffer, 4 * (totobj + MAXPICKELEMS), rect, VIEW3D_SELECT_ALL, select_filter);
2988
2989   for (Base *base = vc->view_layer->object_bases.first; base; base = base->next) {
2990     base->object->id.tag &= ~LIB_TAG_DOIT;
2991   }
2992
2993   Base **bases = NULL;
2994   BLI_array_declare(bases);
2995
2996   bool changed = false;
2997   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
2998     changed |= object_deselect_all_visible(vc->view_layer, vc->v3d);
2999   }
3000
3001   if ((hits == -1) && !SEL_OP_USE_OUTSIDE(sel_op)) {
3002     goto finally;
3003   }
3004
3005   for (Base *base = vc->view_layer->object_bases.first; base; base = base->next) {
3006     if (BASE_SELECTABLE(v3d, base)) {
3007       if ((base->object->runtime.select_id & 0x0000FFFF) != 0) {
3008         BLI_array_append(bases, base);
3009       }
3010     }
3011   }
3012
3013   /* The draw order doesn't always match the order we populate the engine, see: T51695. */
3014   qsort(vbuffer, hits, sizeof(uint[4]), opengl_bone_select_buffer_cmp);
3015
3016   for (const uint *col = vbuffer + 3, *col_end = col + (hits * 4); col < col_end; col += 4) {
3017     Bone *bone;
3018     Base *base = ED_armature_base_and_bone_from_select_buffer(
3019         bases, BLI_array_len(bases), *col, &bone);
3020     if (base != NULL) {
3021       base->object->id.tag |= LIB_TAG_DOIT;
3022     }
3023   }
3024
3025   for (Base *base = vc->view_layer->object_bases.first; base && hits; base = base->next) {
3026     if (BASE_SELECTABLE(v3d, base)) {
3027       const bool is_select = base->flag & BASE_SELECTED;
3028       const bool is_inside = base->object->id.tag & LIB_TAG_DOIT;
3029       const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
3030       if (sel_op_result != -1) {
3031         ED_object_base_select(base, sel_op_result ? BA_SELECT : BA_DESELECT);
3032         changed = true;
3033       }
3034     }
3035   }
3036
3037 finally:
3038   if (bases != NULL) {
3039     MEM_freeN(bases);
3040   }
3041
3042   MEM_freeN(vbuffer);
3043
3044   if (changed) {
3045     DEG_id_tag_update(&vc->scene->id, ID_RECALC_SELECT);
3046     WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc->scene);
3047   }
3048   return changed;
3049 }
3050
3051 static bool do_pose_box_select(bContext *C, ViewContext *vc, rcti *rect, const eSelectOp sel_op)
3052 {
3053   uint bases_len;
3054   Base **bases = do_pose_tag_select_op_prepare(vc, &bases_len);
3055
3056   int totobj = MAXPICKBUF; /* XXX solve later */
3057
3058   /* selection buffer now has bones potentially too, so we add MAXPICKBUF */
3059   uint *vbuffer = MEM_mallocN((totobj + MAXPICKELEMS) * sizeof(uint[4]), "selection buffer");
3060   const eV3DSelectObjectFilter select_filter = ED_view3d_select_filter_from_mode(vc->scene,
3061                                                                                  vc->obact);
3062   const int hits = view3d_opengl_select(
3063       vc, vbuffer, 4 * (totobj + MAXPICKELEMS), rect, VIEW3D_SELECT_ALL, select_filter);
3064   /*
3065    * LOGIC NOTES (theeth):
3066    * The buffer and ListBase have the same relative order, which makes the selection
3067    * very simple. Loop through both data sets at the same time, if the color
3068    * is the same as the object, we have a hit and can move to the next color
3069    * and object pair, if not, just move to the next object,
3070    * keeping the same color until we have a hit.
3071    */
3072
3073   if (hits > 0) {
3074     /* no need to loop if there's no hit */
3075
3076     /* The draw order doesn't always match the order we populate the engine, see: T51695. */
3077     qsort(vbuffer, hits, sizeof(uint[4]), opengl_bone_select_buffer_cmp);
3078
3079     for (const uint *col = vbuffer + 3, *col_end = col + (hits * 4); col < col_end; col += 4) {
3080       Bone *bone;
3081       Base *base = ED_armature_base_and_bone_from_select_buffer(bases, bases_len, *col, &bone);
3082
3083       if (base == NULL) {
3084         continue;
3085       }
3086
3087       /* Loop over contiguous bone hits for 'base'. */
3088       for (; col != col_end; col += 4) {
3089         /* should never fail */
3090         if (bone != NULL) {
3091           base->object->id.tag |= LIB_TAG_DOIT;
3092           bone->flag |= BONE_DONE;
3093         }
3094
3095         /* Select the next bone if we're not switching bases. */
3096         if (col + 4 != col_end) {
3097           if ((base->object->runtime.select_id & 0x0000FFFF) != (col[4] & 0x0000FFFF)) {
3098             break;
3099           }
3100           if (base->object->pose != NULL) {
3101             const uint hit_bone = (col[4] & ~BONESEL_ANY) >> 16;
3102             bPoseChannel *pchan = BLI_findlink(&base->object->pose->chanbase, hit_bone);
3103             bone = pchan ? pchan->bone : NULL;
3104           }
3105           else {
3106             bone = NULL;
3107           }
3108         }
3109       }
3110     }
3111   }
3112
3113   const bool changed_multi = do_pose_tag_select_op_exec(bases, bases_len, sel_op);
3114   if (changed_multi) {
3115     DEG_id_tag_update(&vc->scene->id, ID_RECALC_SELECT);
3116     WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc->scene);
3117   }
3118
3119   if (bases != NULL) {
3120     MEM_freeN(bases);
3121   }
3122   MEM_freeN(vbuffer);
3123
3124   return changed_multi;
3125 }
3126
3127 static int view3d_box_select_exec(bContext *C, wmOperator *op)
3128 {
3129   ViewContext vc;
3130   rcti rect;
3131   bool changed_multi = false;
3132
3133   wmGenericUserData wm_userdata_buf = {0};
3134   wmGenericUserData *wm_userdata = &wm_userdata_buf;
3135
3136   view3d_operator_needs_opengl(C);
3137   BKE_object_update_select_id(CTX_data_main(C));
3138
3139   /* setup view context for argument to callbacks */
3140   ED_view3d_viewcontext_init(C, &vc);
3141
3142   eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
3143   WM_operator_properties_border_to_rcti(op, &rect);
3144
3145   if (vc.obedit) {
3146     FOREACH_OBJECT_IN_MODE_BEGIN (
3147         vc.view_layer, vc.v3d, vc.obedit->type, vc.obedit->mode, ob_iter) {
3148       ED_view3d_viewcontext_init_object(&vc, ob_iter);
3149       bool changed = false;
3150
3151       switch (vc.obedit->type) {
3152         case OB_MESH:
3153           vc.em = BKE_editmesh_from_object(vc.obedit);
3154           changed = do_mesh_box_select(&vc, wm_userdata, &rect, sel_op);
3155           if (changed) {
3156             DEG_id_tag_update(vc.obedit->data, ID_RECALC_SELECT);
3157             WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data);
3158           }
3159           break;
3160         case OB_CURVE:
3161         case OB_SURF:
3162           changed = do_nurbs_box_select(&vc, &rect, sel_op);
3163           if (changed) {
3164             DEG_id_tag_update(vc.obedit->data, ID_RECALC_SELECT);
3165             WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data);
3166           }
3167           break;
3168         case OB_MBALL:
3169           changed = do_meta_box_select(&vc, &rect, sel_op);
3170           if (changed) {
3171             DEG_id_tag_update(vc.obedit->data, ID_RECALC_SELECT);
3172             WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data);
3173           }
3174           break;
3175         case OB_ARMATURE:
3176           changed = do_armature_box_select(&vc, &rect, sel_op);
3177           if (changed) {
3178             DEG_id_tag_update(&vc.obedit->id, ID_RECALC_SELECT);
3179             WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, vc.obedit);
3180           }
3181           break;
3182         case OB_LATTICE:
3183           changed = do_lattice_box_select(&vc, &rect, sel_op);
3184           if (changed) {
3185             DEG_id_tag_update(vc.obedit->data, ID_RECALC_SELECT);
3186             WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data);
3187           }
3188           break;
3189         default:
3190           assert(!"box select on incorrect object type");
3191           break;
3192       }
3193       changed_multi |= changed;
3194     }
3195     FOREACH_OBJECT_IN_MODE_END;
3196   }
3197   else { /* no editmode, unified for bones and objects */
3198     if (vc.obact && vc.obact->mode & OB_MODE_SCULPT) {
3199       /* XXX, this is not selection, could be it's own operator. */
3200       changed_multi = ED_sculpt_mask_box_select(
3201           C, &vc, &rect, sel_op == SEL_OP_ADD ? true : false);
3202     }
3203     else if (vc.obact && BKE_paint_select_face_test(vc.obact)) {
3204       changed_multi = do_paintface_box_select(&vc, wm_userdata, &rect, sel_op);
3205     }
3206     else if (vc.obact && BKE_paint_select_vert_test(vc.obact)) {
3207       changed_multi = do_paintvert_box_select(&vc, wm_userdata, &rect, sel_op);
3208     }
3209     else if (vc.obact && vc.obact->mode & OB_MODE_PARTICLE_EDIT) {
3210       changed_multi = PE_box_select(C, &rect, sel_op);
3211     }
3212     else if (vc.obact && vc.obact->mode & OB_MODE_POSE) {
3213       changed_multi = do_pose_box_select(C, &vc, &rect, sel_op);
3214     }
3215     else { /* object mode with none active */
3216       changed_multi = do_object_box_select(C, &vc, &rect, sel_op);
3217     }
3218   }
3219
3220   WM_generic_user_data_free(wm_userdata);
3221
3222   if (changed_multi) {
3223     return OPERATOR_FINISHED;
3224   }
3225   else {
3226     return OPERATOR_CANCELLED;
3227   }
3228 }
3229
3230 void VIEW3D_OT_select_box(wmOperatorType *ot)
3231 {
3232   /* identifiers */
3233   ot->name = "Box Select";
3234   ot->description = "Select items using box selection";
3235   ot->idname = "VIEW3D_OT_select_box";
3236
3237   /* api callbacks */
3238   ot->invoke = WM_gesture_box_invoke;
3239   ot->exec = view3d_box_select_exec;
3240   ot->modal = WM_gesture_box_modal;
3241   ot->poll = view3d_selectable_data;
3242   ot->cancel = WM_gesture_box_cancel;
3243
3244   /* flags */
3245   ot->flag = OPTYPE_UNDO;
3246
3247   /* rna */
3248   WM_operator_properties_gesture_box(ot);
3249   WM_operator_properties_select_operation(ot);
3250 }
3251
3252 /** \} */
3253
3254 /* -------------------------------------------------------------------- */
3255 /** \name Circle Select
3256  * \{ */
3257
3258 typedef struct CircleSelectUserData {
3259   ViewContext *vc;
3260   bool select;
3261   int mval[2];
3262   float mval_fl[2];
3263   float radius;
3264   float radius_squared;
3265
3266   /* runtime */
3267   bool is_changed;
3268 } CircleSelectUserData;
3269
3270 static void view3d_userdata_circleselect_init(CircleSelectUserData *r_data,
3271                                               ViewContext *vc,
3272                                               const bool select,
3273                                               const int mval[2],
3274                                               const float rad)
3275 {
3276   r_data->vc = vc;
3277   r_data->select = select;
3278   copy_v2_v2_int(r_data->mval, mval);
3279   r_data->mval_fl[0] = mval[0];
3280   r_data->mval_fl[1] = mval[1];
3281
3282   r_data->radius = rad;
3283   r_data->radius_squared = rad * rad;
3284
3285   /* runtime */
3286   r_data->is_changed = false;
3287 }
3288
3289 static void mesh_circle_doSelectVert(void *userData,
3290                                      BMVert *eve,
3291                                      const float screen_co[2],
3292                                      int UNUSED(index))
3293 {
3294   CircleSelectUserData *data = userData;
3295
3296   if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
3297     BM_vert_select_set(data->vc->em->bm, eve, data->select);
3298     data->is_changed = true;
3299   }
3300 }
3301 static void mesh_circle_doSelectEdge(void *userData,
3302                                      BMEdge *eed,
3303                                      const float screen_co_a[2],
3304                                      const float screen_co_b[2],
3305                                      int UNUSED(index))
3306 {
3307   CircleSelectUserData *data = userData;
3308
3309   if (edge_inside_circle(data->mval_fl, data->radius, screen_co_a, screen_co_b)) {
3310     BM_edge_select_set(data->vc->em->bm, eed, data->select);
3311     data->is_changed = true;
3312   }
3313 }
3314 static void mesh_circle_doSelectFace(void *userData,
3315                                      BMFace *efa,
3316                                      const float screen_co[2],
3317                                      int UNUSED(index))
3318 {
3319   CircleSelectUserData *data = userData;
3320
3321   if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
3322     BM_face_select_set(data->vc->em->bm, efa, data->select);
3323     data->is_changed = true;
3324   }
3325 }
3326
3327 static bool mesh_circle_select(ViewContext *vc,
3328                                wmGenericUserData *wm_userdata,
3329                                eSelectOp sel_op,
3330                                const int mval[2],
3331                                float rad)
3332 {
3333   ToolSettings *ts = vc->scene->toolsettings;
3334   CircleSelectUserData data;
3335   vc->em = BKE_editmesh_from_object(vc->obedit);
3336
3337   bool changed = false;
3338   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
3339     if (vc->em->bm->totvertsel) {
3340       EDBM_flag_disable_all(vc->em, BM_ELEM_SELECT);
3341       changed = true;
3342     }
3343   }
3344   const bool select = (sel_op != SEL_OP_SUB);
3345
3346   ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
3347
3348   view3d_userdata_circleselect_init(&data, vc, select, mval, rad);
3349
3350   const bool use_zbuf = !XRAY_FLAG_ENABLED(vc->v3d);
3351
3352   if (use_zbuf) {
3353     if (wm_userdata->data == NULL) {
3354       editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc);
3355     }
3356   }
3357   struct EditSelectBuf_Cache *esel = wm_userdata->data;
3358
3359   if (use_zbuf) {
3360     const uint buffer_len = EDBM_select_id_context_elem_len(esel->sel_id_ctx);
3361     esel->select_bitmap = ED_select_buffer_bitmap_from_circle(buffer_len, mval, (int)(rad + 1.0f));
3362   }
3363
3364   if (ts->selectmode & SCE_SELECT_VERTEX) {
3365     if (use_zbuf) {
3366       if (esel->select_bitmap != NULL) {
3367         changed |= edbm_backbuf_check_and_select_verts(
3368             esel, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB);
3369       }
3370     }
3371     else {
3372       mesh_foreachScreenVert(vc, mesh_circle_doSelectVert, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
3373     }
3374   }
3375
3376   if (ts->selectmode & SCE_SELECT_EDGE) {
3377     if (use_zbuf) {
3378       if (esel->select_bitmap != NULL) {
3379         changed |= edbm_backbuf_check_and_select_edges(
3380             esel, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB);
3381       }
3382     }
3383     else {
3384       mesh_foreachScreenEdge(vc, mesh_circle_doSelectEdge, &data, V3D_PROJ_TEST_CLIP_NEAR);
3385     }
3386   }
3387
3388   if (ts->selectmode & SCE_SELECT_FACE) {
3389     if (use_zbuf) {
3390       if (esel->select_bitmap != NULL) {
3391         changed |= edbm_backbuf_check_and_select_faces(
3392             esel, vc->obedit, vc->em, select ? SEL_OP_ADD : SEL_OP_SUB);
3393       }
3394     }
3395     else {
3396       mesh_foreachScreenFace(vc, mesh_circle_doSelectFace, &data, V3D_PROJ_TEST_CLIP_DEFAULT);
3397     }
3398   }
3399
3400   if (use_zbuf) {
3401     if (esel->select_bitmap != NULL) {
3402       MEM_freeN(esel->select_bitmap);
3403       esel->select_bitmap = NULL;
3404     }
3405   }
3406
3407   changed |= data.is_changed;
3408
3409   if (changed) {
3410     EDBM_selectmode_flush(vc->em);
3411   }
3412   return changed;
3413 }
3414
3415 static bool paint_facesel_circle_select(ViewContext *vc,
3416                                         wmGenericUserData *wm_userdata,
3417                                         const eSelectOp sel_op,
3418                                         const int mval[2],
3419                                         float rad)
3420 {
3421   BLI_assert(ELEM(sel_op, SEL_OP_SET, SEL_OP_ADD, SEL_OP_SUB));
3422   Object *ob = vc->obact;
3423   Mesh *me = ob->data;
3424
3425   bool changed = false;
3426   if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
3427     /* flush selection at the end */
3428     changed |= paintface_deselect_all_visible(vc->C, ob, SEL_DESELECT, false);
3429   }
3430
3431   if (wm_userdata->data == NULL) {
3432     editselect_buf_cache_init_with_generic_userdata(wm_userdata, vc);
3433   }
3434
3435   {
3436     struct EditSelectBuf_Cache *esel = wm_userdata->data;
3437     const uint buffer_len = EDBM_select_id_context_elem_len(esel->sel_id_ctx);
3438     esel->select_bitmap = ED_select_buffer_bitmap_from_circle(buffer_len, mval, (int)(rad + 1.0f));
3439     if (esel->select_bitmap != NULL) {
3440       changed |= edbm_backbuf_check_and_select_faces_obmode(me, esel, sel_op);
3441       MEM_freeN(esel->select_bitmap);
3442       esel->select_bitmap = NULL;
3443     }
3444   }
3445
3446   if (changed) {
3447     paintface_flush_flags(vc->C, ob, SELECT);
3448   }
3449   return changed;
3450 }
3451
3452 static void paint_vertsel_circle_select_doSelectVert(void *userData,
3453                                                      MVert *mv,
3454                                                      const float screen_co[2],
3455                                                      int UNUSED(index))
3456 {
3457   CircleSelectUserData *data = userData;
3458
3459   if (len_squared_v2v2(data->mval_fl, screen_co) <= data->radius_squared) {
3460     SET_FLAG_FROM_TEST(mv->flag, data->select, SELECT);
3461     data->is_changed = true;
3462   }
3463 }
3464 static bool paint_vertsel_circle_select(ViewContext *vc,
3465                                         wmGenericUserData *wm_userdata,
3466                                         const eSelectOp sel_op,
3467                                         const int mval[2],
3468                                         float rad)
3469 {
3470   BLI_assert(ELEM(sel_op, SEL_OP_SET, SEL_OP_ADD, SEL_OP_SUB));
3471   const bool use_zbuf = !XRAY_ENABLED(vc->v3d);
3472   Object *ob = vc->obact;
3473   Mesh *me = ob->data;
3474   /* CircleSelectUserData data = {NULL}; */ /*