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