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