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