Merge remote-tracking branch 'origin/master' into blender2.8
[blender.git] / source / blender / editors / metaball / mball_edit.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22
23  * Contributor(s): none yet.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/editors/metaball/mball_edit.c
29  *  \ingroup edmeta
30  */
31
32 #include <math.h>
33 #include <string.h>
34
35 #include "MEM_guardedalloc.h"
36
37 #include "BLI_blenlib.h"
38 #include "BLI_math.h"
39 #include "BLI_rand.h"
40 #include "BLI_utildefines.h"
41 #include "BLI_kdtree.h"
42
43 #include "DNA_defs.h"
44 #include "DNA_meta_types.h"
45 #include "DNA_object_types.h"
46 #include "DNA_scene_types.h"
47
48 #include "RNA_define.h"
49 #include "RNA_access.h"
50
51 #include "BKE_context.h"
52 #include "BKE_mball.h"
53 #include "BKE_layer.h"
54 #include "BKE_object.h"
55
56 #include "DEG_depsgraph.h"
57
58 #include "ED_mball.h"
59 #include "ED_object.h"
60 #include "ED_screen.h"
61 #include "ED_select_utils.h"
62 #include "ED_view3d.h"
63
64 #include "WM_api.h"
65 #include "WM_types.h"
66
67 #include "mball_intern.h"
68
69 /* This function is used to free all MetaElems from MetaBall */
70 void ED_mball_editmball_free(Object *obedit)
71 {
72         MetaBall *mb = (MetaBall *)obedit->data;
73
74         mb->editelems = NULL;
75         mb->lastelem = NULL;
76 }
77
78 /* This function is called, when MetaBall Object is
79  * switched from object mode to edit mode */
80 void ED_mball_editmball_make(Object *obedit)
81 {
82         MetaBall *mb = (MetaBall *)obedit->data;
83         MetaElem *ml; /*, *newml;*/
84
85         ml = mb->elems.first;
86
87         while (ml) {
88                 if (ml->flag & SELECT) mb->lastelem = ml;
89                 ml = ml->next;
90         }
91
92         mb->editelems = &mb->elems;
93 }
94
95 /* This function is called, when MetaBall Object switched from
96  * edit mode to object mode. List of MetaElements is copied
97  * from object->data->edit_elems to object->data->elems. */
98 void ED_mball_editmball_load(Object *UNUSED(obedit))
99 {
100 }
101
102 /* Add metaelem primitive to metaball object (which is in edit mode) */
103 MetaElem *ED_mball_add_primitive(bContext *UNUSED(C), Object *obedit, float mat[4][4], float dia, int type)
104 {
105         MetaBall *mball = (MetaBall *)obedit->data;
106         MetaElem *ml;
107
108         /* Deselect all existing metaelems */
109         ml = mball->editelems->first;
110         while (ml) {
111                 ml->flag &= ~SELECT;
112                 ml = ml->next;
113         }
114
115         ml = BKE_mball_element_add(mball, type);
116         ml->rad *= dia;
117         mball->wiresize *= dia;
118         mball->rendersize *= dia;
119         copy_v3_v3(&ml->x, mat[3]);
120
121         ml->flag |= SELECT;
122         mball->lastelem = ml;
123         return ml;
124 }
125
126 /***************************** Select/Deselect operator *****************************/
127
128 /* Select or deselect all MetaElements */
129 static int mball_select_all_exec(bContext *C, wmOperator *op)
130 {
131         int action = RNA_enum_get(op->ptr, "action");
132
133         ViewLayer *view_layer = CTX_data_view_layer(C);
134         uint objects_len = 0;
135         Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len);
136
137         if (action == SEL_TOGGLE) {
138                 action = BKE_mball_is_any_selected_multi(objects, objects_len) ?
139                         SEL_DESELECT :
140                         SEL_SELECT;
141         }
142
143         switch (action) {
144                 case SEL_SELECT:
145                         BKE_mball_select_all_multi(objects, objects_len);
146                         break;
147                 case SEL_DESELECT:
148                         BKE_mball_deselect_all_multi(objects, objects_len);
149                         break;
150                 case SEL_INVERT:
151                         BKE_mball_select_swap_multi(objects, objects_len);
152                         break;
153         }
154
155         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
156                 Object *obedit = objects[ob_index];
157                 MetaBall *mb = (MetaBall *)obedit->data;
158                 DEG_id_tag_update(&mb->id, ID_RECALC_SELECT);
159                 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, mb);
160         }
161
162         MEM_freeN(objects);
163
164         return OPERATOR_FINISHED;
165 }
166
167 void MBALL_OT_select_all(wmOperatorType *ot)
168 {
169         /* identifiers */
170         ot->name = "(De)select All";
171         ot->description = "Change selection of all meta elements";
172         ot->idname = "MBALL_OT_select_all";
173
174         /* callback functions */
175         ot->exec = mball_select_all_exec;
176         ot->poll = ED_operator_editmball;
177
178         /* flags */
179         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
180
181         WM_operator_properties_select_all(ot);
182 }
183
184
185 /* -------------------------------------------------------------------- */
186 /* Select Similar */
187
188 enum {
189         SIMMBALL_TYPE = 1,
190         SIMMBALL_RADIUS,
191         SIMMBALL_STIFFNESS,
192         SIMMBALL_ROTATION
193 };
194
195 static const EnumPropertyItem prop_similar_types[] = {
196         {SIMMBALL_TYPE, "TYPE", 0, "Type", ""},
197         {SIMMBALL_RADIUS, "RADIUS", 0, "Radius", ""},
198         {SIMMBALL_STIFFNESS, "STIFFNESS", 0, "Stiffness", ""},
199         {SIMMBALL_ROTATION, "ROTATION", 0, "Rotation", ""},
200         {0, NULL, 0, NULL, NULL}
201 };
202
203 static void mball_select_similar_type_get(Object *obedit, MetaBall *mb, int  type, KDTree *r_tree)
204 {
205         float tree_entry[3] = {0.0f, 0.0f, 0.0f};
206         MetaElem *ml;
207         int tree_index = 0;
208         for (ml = mb->editelems->first; ml; ml = ml->next) {
209                 if (ml->flag & SELECT) {
210                         switch (type) {
211                                 case SIMMBALL_RADIUS:
212                                 {
213                                         float radius = ml->rad;
214                                         /* Radius in world space. */
215                                         float smat[3][3];
216                                         float radius_vec[3] = {radius, radius, radius};
217                                         BKE_object_scale_to_mat3(obedit, smat);
218                                         mul_m3_v3(smat, radius_vec);
219                                         radius = (radius_vec[0] + radius_vec[1] + radius_vec[2]) / 3;
220                                         tree_entry[0] = radius;
221                                         break;
222                                 }
223                                 case SIMMBALL_STIFFNESS:
224                                 {
225                                         tree_entry[0] = ml->s;
226                                         break;
227                                 }
228                                         break;
229                                 case SIMMBALL_ROTATION:
230                                 {
231                                         float dir[3] = {1.0f, 0.0f, 0.0f};
232                                         float rmat[3][3];
233                                         mul_qt_v3(ml->quat, dir);
234                                         BKE_object_rot_to_mat3(obedit, rmat, true);
235                                         mul_m3_v3(rmat, dir);
236                                         copy_v3_v3(tree_entry, dir);
237                                         break;
238                                 }
239                         }
240                         BLI_kdtree_insert(r_tree, tree_index++, tree_entry);
241                 }
242         }
243 }
244
245 static bool mball_select_similar_type(Object *obedit, MetaBall *mb, int type, const KDTree *tree, const float thresh)
246 {
247         MetaElem *ml;
248         bool changed = false;
249         for (ml = mb->editelems->first; ml; ml = ml->next) {
250                 bool select = false;
251                 switch (type) {
252                         case SIMMBALL_RADIUS:
253                         {
254                                 float radius = ml->rad;
255                                 /* Radius in world space is the average of the
256                                  * scaled radius in x, y and z directions. */
257                                 float smat[3][3];
258                                 float radius_vec[3] = {radius, radius, radius};
259                                 BKE_object_scale_to_mat3(obedit, smat);
260                                 mul_m3_v3(smat, radius_vec);
261                                 radius = (radius_vec[0] + radius_vec[1] + radius_vec[2]) / 3;
262
263                                 if (ED_select_similar_compare_float_tree(tree, radius, thresh, SIM_CMP_EQ)) {
264                                         select = true;
265                                 }
266                                 break;
267                         }
268                         case SIMMBALL_STIFFNESS:
269                         {
270                                 float s = ml->s;
271                                 if (ED_select_similar_compare_float_tree(tree, s, thresh, SIM_CMP_EQ)) {
272                                         select = true;
273                                 }
274                                 break;
275                         }
276                         case SIMMBALL_ROTATION:
277                         {
278                                 float dir[3] = {1.0f, 0.0f, 0.0f};
279                                 float rmat[3][3];
280                                 mul_qt_v3(ml->quat, dir);
281                                 BKE_object_rot_to_mat3(obedit, rmat, true);
282                                 mul_m3_v3(rmat, dir);
283
284                                 float thresh_cos = cosf(thresh * (float)M_PI_2);
285
286                                 KDTreeNearest nearest;
287                                 if (BLI_kdtree_find_nearest(tree, dir, &nearest) != -1) {
288                                         float orient = angle_normalized_v3v3(dir, nearest.co);
289                                         /* Map to 0-1 to compare orientation. */
290                                         float delta = thresh_cos - fabsf(cosf(orient));
291                                         if (ED_select_similar_compare_float(delta, thresh, SIM_CMP_EQ)) {
292                                                 select = true;
293                                         }
294                                 }
295                                 break;
296                         }
297                 }
298
299                 if (select) {
300                         changed = true;
301                         ml->flag |= SELECT;
302                 }
303         }
304         return changed;
305 }
306
307 static int mball_select_similar_exec(bContext *C, wmOperator *op)
308 {
309         const int type = RNA_enum_get(op->ptr, "type");
310         const float thresh = RNA_float_get(op->ptr, "threshold");
311         int tot_mball_selected_all = 0;
312
313         ViewLayer *view_layer = CTX_data_view_layer(C);
314         uint objects_len = 0;
315         Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len);
316
317         tot_mball_selected_all = BKE_mball_select_count_multi(objects, objects_len);
318
319         short type_ref = 0;
320         KDTree *tree = NULL;
321
322         if (type != SIMMBALL_TYPE) {
323                 tree = BLI_kdtree_new(tot_mball_selected_all);
324         }
325
326         /* Get type of selected MetaBall */
327         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
328                 Object *obedit = objects[ob_index];
329                 MetaBall *mb = (MetaBall *)obedit->data;
330
331                 switch (type) {
332                         case SIMMBALL_TYPE:
333                         {
334                                 MetaElem *ml;
335                                 for (ml = mb->editelems->first; ml; ml = ml->next) {
336                                         if (ml->flag & SELECT) {
337                                                 short mball_type = 1 << (ml->type + 1);
338                                                 type_ref |= mball_type;
339                                         }
340                                 }
341                                 break;
342                         }
343                         case SIMMBALL_RADIUS:
344                         case SIMMBALL_STIFFNESS:
345                         case SIMMBALL_ROTATION:
346                                 mball_select_similar_type_get(obedit, mb, type, tree);
347                                 break;
348                         default:
349                                 BLI_assert(0);
350                                 break;
351                 }
352         }
353
354         if (tree != NULL) {
355                 BLI_kdtree_balance(tree);
356         }
357         /* Select MetaBalls with desired type. */
358         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
359                 Object *obedit = objects[ob_index];
360                 MetaBall *mb = (MetaBall *)obedit->data;
361                 bool changed = false;
362
363                 switch (type) {
364                         case SIMMBALL_TYPE:
365                         {
366                                 MetaElem *ml;
367                                 for (ml = mb->editelems->first; ml; ml = ml->next) {
368                                         short mball_type = 1 << (ml->type + 1);
369                                         if (mball_type & type_ref) {
370                                                 ml->flag |= SELECT;
371                                                 changed = true;
372                                         }
373                                 }
374                                 break;
375                         }
376                         case SIMMBALL_RADIUS:
377                         case SIMMBALL_STIFFNESS:
378                         case SIMMBALL_ROTATION:
379                                 changed = mball_select_similar_type(obedit, mb, type, tree, thresh);
380                                 break;
381                         default:
382                                 BLI_assert(0);
383                                 break;
384                 }
385
386                 if (changed) {
387                         DEG_id_tag_update(&mb->id, ID_RECALC_SELECT);
388                         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, mb);
389                 }
390         }
391
392         MEM_freeN(objects);
393         if (tree != NULL) {
394                 BLI_kdtree_free(tree);
395         }
396         return OPERATOR_FINISHED;
397 }
398
399 void MBALL_OT_select_similar(wmOperatorType *ot)
400 {
401         /* identifiers */
402         ot->name = "Select Similar";
403         ot->idname = "MBALL_OT_select_similar";
404
405         /* callback functions */
406         ot->invoke = WM_menu_invoke;
407         ot->exec = mball_select_similar_exec;
408         ot->poll = ED_operator_editmball;
409         ot->description = "Select similar metaballs by property types";
410
411         /* flags */
412         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
413
414         /* properties */
415         ot->prop = RNA_def_enum(ot->srna, "type", prop_similar_types, 0, "Type", "");
416
417         RNA_def_float(ot->srna, "threshold", 0.1, 0.0, FLT_MAX, "Threshold", "", 0.01, 1.0);
418 }
419
420
421 /***************************** Select random operator *****************************/
422
423 /* Random metaball selection */
424 static int select_random_metaelems_exec(bContext *C, wmOperator *op)
425 {
426         const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT);
427         const float randfac = RNA_float_get(op->ptr, "percent") / 100.0f;
428         const int seed = WM_operator_properties_select_random_seed_increment_get(op);
429
430         ViewLayer *view_layer = CTX_data_view_layer(C);
431         uint objects_len = 0;
432         Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len);
433         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
434                 Object *obedit = objects[ob_index];
435                 MetaBall *mb = (MetaBall *)obedit->data;
436                 if (!BKE_mball_is_any_unselected(mb)) {
437                         continue;
438                 }
439                 int seed_iter = seed;
440
441                 /* This gives a consistent result regardless of object order. */
442                 if (ob_index) {
443                         seed_iter += BLI_ghashutil_strhash_p(obedit->id.name);
444                 }
445
446                 RNG *rng = BLI_rng_new_srandom(seed_iter);
447
448                 for (MetaElem *ml = mb->editelems->first; ml; ml = ml->next) {
449                         if (BLI_rng_get_float(rng) < randfac) {
450                                 if (select) {
451                                         ml->flag |= SELECT;
452                                 }
453                                 else {
454                                         ml->flag &= ~SELECT;
455                                 }
456                         }
457                 }
458
459                 BLI_rng_free(rng);
460
461                 DEG_id_tag_update(&mb->id, ID_RECALC_SELECT);
462                 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, mb);
463         }
464         MEM_freeN(objects);
465         return OPERATOR_FINISHED;
466 }
467
468 void MBALL_OT_select_random_metaelems(struct wmOperatorType *ot)
469 {
470         /* identifiers */
471         ot->name = "Select Random";
472         ot->description = "Randomly select metaelements";
473         ot->idname = "MBALL_OT_select_random_metaelems";
474
475         /* callback functions */
476         ot->exec = select_random_metaelems_exec;
477         ot->poll = ED_operator_editmball;
478
479         /* flags */
480         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
481
482         /* properties */
483         WM_operator_properties_select_random(ot);
484 }
485
486 /***************************** Duplicate operator *****************************/
487
488 /* Duplicate selected MetaElements */
489 static int duplicate_metaelems_exec(bContext *C, wmOperator *UNUSED(op))
490 {
491         ViewLayer *view_layer = CTX_data_view_layer(C);
492         uint objects_len = 0;
493         Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len);
494         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
495                 Object *obedit = objects[ob_index];
496                 MetaBall *mb = (MetaBall *)obedit->data;
497                 MetaElem *ml, *newml;
498
499                 if (!BKE_mball_is_any_selected(mb)) {
500                         continue;
501                 }
502
503                 ml = mb->editelems->last;
504                 if (ml) {
505                         while (ml) {
506                                 if (ml->flag & SELECT) {
507                                         newml = MEM_dupallocN(ml);
508                                         BLI_addtail(mb->editelems, newml);
509                                         mb->lastelem = newml;
510                                         ml->flag &= ~SELECT;
511                                 }
512                                 ml = ml->prev;
513                         }
514                         WM_event_add_notifier(C, NC_GEOM | ND_DATA, mb);
515                         DEG_id_tag_update(obedit->data, 0);
516                 }
517         }
518         MEM_freeN(objects);
519         return OPERATOR_FINISHED;
520 }
521
522 void MBALL_OT_duplicate_metaelems(wmOperatorType *ot)
523 {
524         /* identifiers */
525         ot->name = "Duplicate Metaelements";
526         ot->description = "Duplicate selected metaelement(s)";
527         ot->idname = "MBALL_OT_duplicate_metaelems";
528
529         /* callback functions */
530         ot->exec = duplicate_metaelems_exec;
531         ot->poll = ED_operator_editmball;
532
533         /* flags */
534         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
535 }
536
537 /***************************** Delete operator *****************************/
538
539 /* Delete all selected MetaElems (not MetaBall) */
540 static int delete_metaelems_exec(bContext *C, wmOperator *UNUSED(op))
541 {
542         ViewLayer *view_layer = CTX_data_view_layer(C);
543         uint objects_len = 0;
544         Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len);
545         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
546                 Object *obedit = objects[ob_index];
547                 MetaBall *mb = (MetaBall *)obedit->data;
548                 MetaElem *ml, *next;
549
550                 if (!BKE_mball_is_any_selected(mb)) {
551                         continue;
552                 }
553
554                 ml = mb->editelems->first;
555                 if (ml) {
556                         while (ml) {
557                                 next = ml->next;
558                                 if (ml->flag & SELECT) {
559                                         if (mb->lastelem == ml) mb->lastelem = NULL;
560                                         BLI_remlink(mb->editelems, ml);
561                                         MEM_freeN(ml);
562                                 }
563                                 ml = next;
564                         }
565                         WM_event_add_notifier(C, NC_GEOM | ND_DATA, mb);
566                         DEG_id_tag_update(obedit->data, 0);
567                 }
568         }
569         MEM_freeN(objects);
570         return OPERATOR_FINISHED;
571 }
572
573 void MBALL_OT_delete_metaelems(wmOperatorType *ot)
574 {
575         /* identifiers */
576         ot->name = "Delete";
577         ot->description = "Delete selected metaelement(s)";
578         ot->idname = "MBALL_OT_delete_metaelems";
579
580         /* callback functions */
581         ot->invoke = WM_operator_confirm;
582         ot->exec = delete_metaelems_exec;
583         ot->poll = ED_operator_editmball;
584
585         /* flags */
586         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
587 }
588
589 /***************************** Hide operator *****************************/
590
591 /* Hide selected MetaElems */
592 static int hide_metaelems_exec(bContext *C, wmOperator *op)
593 {
594         Object *obedit = CTX_data_edit_object(C);
595         MetaBall *mb = (MetaBall *)obedit->data;
596         MetaElem *ml;
597         const bool invert = RNA_boolean_get(op->ptr, "unselected") ? SELECT : 0;
598
599         ml = mb->editelems->first;
600
601         if (ml) {
602                 while (ml) {
603                         if ((ml->flag & SELECT) != invert)
604                                 ml->flag |= MB_HIDE;
605                         ml = ml->next;
606                 }
607                 WM_event_add_notifier(C, NC_GEOM | ND_DATA, mb);
608                 DEG_id_tag_update(obedit->data, 0);
609         }
610
611         return OPERATOR_FINISHED;
612 }
613
614 void MBALL_OT_hide_metaelems(wmOperatorType *ot)
615 {
616         /* identifiers */
617         ot->name = "Hide";
618         ot->description = "Hide (un)selected metaelement(s)";
619         ot->idname = "MBALL_OT_hide_metaelems";
620
621         /* callback functions */
622         ot->exec = hide_metaelems_exec;
623         ot->poll = ED_operator_editmball;
624
625         /* flags */
626         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
627
628         /* props */
629         RNA_def_boolean(ot->srna, "unselected", false, "Unselected", "Hide unselected rather than selected");
630 }
631
632 /***************************** Unhide operator *****************************/
633
634 /* Unhide all edited MetaElems */
635 static int reveal_metaelems_exec(bContext *C, wmOperator *op)
636 {
637         Object *obedit = CTX_data_edit_object(C);
638         MetaBall *mb = (MetaBall *)obedit->data;
639         const bool select = RNA_boolean_get(op->ptr, "select");
640         bool changed = false;
641
642         for (MetaElem *ml = mb->editelems->first; ml; ml = ml->next) {
643                 if (ml->flag & MB_HIDE) {
644                         SET_FLAG_FROM_TEST(ml->flag, select, SELECT);
645                         ml->flag &= ~MB_HIDE;
646                         changed = true;
647                 }
648         }
649         if (changed) {
650                 WM_event_add_notifier(C, NC_GEOM | ND_DATA, mb);
651                 DEG_id_tag_update(obedit->data, 0);
652         }
653
654         return OPERATOR_FINISHED;
655 }
656
657 void MBALL_OT_reveal_metaelems(wmOperatorType *ot)
658 {
659         /* identifiers */
660         ot->name = "Reveal";
661         ot->description = "Reveal all hidden metaelements";
662         ot->idname = "MBALL_OT_reveal_metaelems";
663
664         /* callback functions */
665         ot->exec = reveal_metaelems_exec;
666         ot->poll = ED_operator_editmball;
667
668         /* flags */
669         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
670
671         /* props */
672         RNA_def_boolean(ot->srna, "select", true, "Select", "");
673 }
674
675 /* Select MetaElement with mouse click (user can select radius circle or
676  * stiffness circle) */
677 bool ED_mball_select_pick(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
678 {
679         static MetaElem *startelem = NULL;
680         ViewContext vc;
681         int a, hits;
682         unsigned int buffer[MAXPICKBUF];
683         rcti rect;
684
685         ED_view3d_viewcontext_init(C, &vc);
686
687         BLI_rcti_init_pt_radius(&rect, mval, 12);
688
689         hits = view3d_opengl_select(
690                 &vc, buffer, MAXPICKBUF, &rect,
691                 VIEW3D_SELECT_PICK_NEAREST, VIEW3D_SELECT_FILTER_NOP);
692
693         FOREACH_BASE_IN_EDIT_MODE_BEGIN (vc.view_layer, vc.v3d, base) {
694                 ED_view3d_viewcontext_init_object(&vc, base->object);
695                 MetaBall *mb = (MetaBall *)base->object->data;
696                 MetaElem *ml, *ml_act = NULL;
697
698                 /* does startelem exist? */
699                 ml = mb->editelems->first;
700                 while (ml) {
701                         if (ml == startelem) break;
702                         ml = ml->next;
703                 }
704
705                 if (ml == NULL) startelem = mb->editelems->first;
706
707                 if (hits > 0) {
708                         int metaelem_id = 0;
709                         ml = startelem;
710                         while (ml) {
711                                 for (a = 0; a < hits; a++) {
712                                         int hitresult = buffer[(4 * a) + 3];
713                                         if (hitresult == -1) {
714                                                 continue;
715                                         }
716                                         else if (hitresult & MBALL_NOSEL) {
717                                                 continue;
718                                         }
719
720                                         const uint hit_object = hitresult & 0xFFFF;
721                                         if (vc.obedit->select_color != hit_object) {
722                                                 continue;
723                                         }
724
725                                         if (metaelem_id != (hitresult & 0xFFFF0000 & ~(MBALLSEL_ANY))) {
726                                                 continue;
727                                         }
728
729                                         if (hitresult & MBALLSEL_RADIUS) {
730                                                 ml->flag |= MB_SCALE_RAD;
731                                                 ml_act = ml;
732                                                 break;
733                                         }
734
735                                         if (hitresult & MBALLSEL_STIFF) {
736                                                 ml->flag &= ~MB_SCALE_RAD;
737                                                 ml_act = ml;
738                                                 break;
739                                         }
740                                 }
741
742                                 if (ml_act) break;
743                                 ml = ml->next;
744                                 if (ml == NULL) ml = mb->editelems->first;
745                                 if (ml == startelem) break;
746
747                                 metaelem_id += 0x10000;
748                         }
749
750                         /* When some metaelem was found, then it is necessary to select or
751                         * deselect it. */
752                         if (ml_act) {
753                                 if (!extend && !deselect && !toggle) {
754                                         uint objects_len;
755                                         Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(vc.view_layer, vc.v3d, &objects_len);
756                                         for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
757                                                 Object *ob_iter = objects[ob_index];
758
759                                                 if (ob_iter == base->object) {
760                                                         continue;
761                                                 }
762
763                                                 BKE_mball_deselect_all((MetaBall *)ob_iter->data);
764                                                 DEG_id_tag_update(ob_iter->data, ID_RECALC_SELECT);
765                                                 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob_iter->data);
766                                         }
767                                         MEM_freeN(objects);
768                                 }
769
770                                 if (extend) {
771                                         ml_act->flag |= SELECT;
772                                 }
773                                 else if (deselect) {
774                                         ml_act->flag &= ~SELECT;
775                                 }
776                                 else if (toggle) {
777                                         if (ml_act->flag & SELECT)
778                                                 ml_act->flag &= ~SELECT;
779                                         else
780                                                 ml_act->flag |= SELECT;
781                                 }
782                                 else {
783                                         /* Deselect all existing metaelems */
784                                         BKE_mball_deselect_all(mb);
785
786                                         /* Select only metaelem clicked on */
787                                         ml_act->flag |= SELECT;
788                                 }
789
790                                 mb->lastelem = ml_act;
791
792                                 DEG_id_tag_update(&mb->id, ID_RECALC_SELECT);
793                                 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, mb);
794
795                                 if (vc.view_layer->basact != base) {
796                                         ED_object_base_activate(C, base);
797                                 }
798
799                                 return true;
800                         }
801                 }
802         } FOREACH_BASE_IN_EDIT_MODE_END;
803
804         return false;
805 }