code cleanup: use const events for modal and invoke operators.
[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
33 #include <math.h>
34 #include <string.h>
35
36 #include "MEM_guardedalloc.h"
37
38 #include "BLI_blenlib.h"
39 #include "BLI_math.h"
40 #include "BLI_rand.h"
41 #include "BLI_utildefines.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 #include "RNA_enum_types.h"
51
52 #include "BKE_depsgraph.h"
53 #include "BKE_context.h"
54 #include "BKE_mball.h"
55
56 #include "ED_mball.h"
57 #include "ED_screen.h"
58 #include "ED_view3d.h"
59 #include "ED_transform.h"
60 #include "ED_util.h"
61
62 #include "WM_api.h"
63 #include "WM_types.h"
64
65 #include "mball_intern.h"
66
67 /* This function is used to free all MetaElems from MetaBall */
68 void free_editMball(Object *obedit)
69 {
70         MetaBall *mb = (MetaBall *)obedit->data;
71
72         mb->editelems = NULL;
73         mb->lastelem = NULL;
74 }
75
76 /* This function is called, when MetaBall Object is
77  * switched from object mode to edit mode */
78 void make_editMball(Object *obedit)
79 {
80         MetaBall *mb = (MetaBall *)obedit->data;
81         MetaElem *ml; /*, *newml;*/
82
83         ml = mb->elems.first;
84         
85         while (ml) {
86                 if (ml->flag & SELECT) mb->lastelem = ml;
87                 ml = ml->next;
88         }
89
90         mb->editelems = &mb->elems;
91 }
92
93 /* This function is called, when MetaBall Object switched from
94  * edit mode to object mode. List od MetaElements is copied
95  * from object->data->edit_elems to object->data->elems. */
96 void load_editMball(Object *UNUSED(obedit))
97 {
98 }
99
100 /* Add metaelem primitive to metaball object (which is in edit mode) */
101 MetaElem *add_metaball_primitive(bContext *UNUSED(C), Object *obedit, float mat[4][4], float dia, int type, int UNUSED(newname))
102 {
103         MetaBall *mball = (MetaBall *)obedit->data;
104         MetaElem *ml;
105
106         /* Deselect all existing metaelems */
107         ml = mball->editelems->first;
108         while (ml) {
109                 ml->flag &= ~SELECT;
110                 ml = ml->next;
111         }
112         
113         ml = BKE_mball_element_add(mball, type);
114         ml->rad *= 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 (mb->editelems->first == NULL)
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 /***************************** Select random operator *****************************/
180
181 /* Random metaball selection */
182 static int select_random_metaelems_exec(bContext *C, wmOperator *op)
183 {
184         Object *obedit = CTX_data_edit_object(C);
185         MetaBall *mb = (MetaBall *)obedit->data;
186         MetaElem *ml;
187         float percent = RNA_float_get(op->ptr, "percent");
188         
189         if (percent == 0.0f)
190                 return OPERATOR_CANCELLED;
191         
192         ml = mb->editelems->first;
193         BLI_srand(BLI_rand());  /* Random seed */
194         
195         /* Stupid version of random selection. Should be improved. */
196         while (ml) {
197                 if (BLI_frand() < percent)
198                         ml->flag |= SELECT;
199                 else
200                         ml->flag &= ~SELECT;
201                 ml = ml->next;
202         }
203         
204         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, mb);
205         
206         return OPERATOR_FINISHED;
207 }
208
209
210 void MBALL_OT_select_random_metaelems(struct wmOperatorType *ot)
211 {
212         /* identifiers */
213         ot->name = "Random...";
214         ot->description = "Randomly select metaelements";
215         ot->idname = "MBALL_OT_select_random_metaelems";
216         
217         /* callback functions */
218         ot->exec = select_random_metaelems_exec;
219         ot->invoke = WM_operator_props_popup;
220         ot->poll = ED_operator_editmball;
221         
222         /* flags */
223         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
224         
225         /* properties */
226         RNA_def_float_percentage(ot->srna, "percent", 0.5f, 0.0f, 1.0f, "Percent",
227                                  "Percentage of metaelements to select randomly", 0.0001f, 1.0f);
228 }
229
230 /***************************** Duplicate operator *****************************/
231
232 /* Duplicate selected MetaElements */
233 static int duplicate_metaelems_exec(bContext *C, wmOperator *UNUSED(op))
234 {
235         Object *obedit = CTX_data_edit_object(C);
236         MetaBall *mb = (MetaBall *)obedit->data;
237         MetaElem *ml, *newml;
238         
239         ml = mb->editelems->last;
240         if (ml) {
241                 while (ml) {
242                         if (ml->flag & SELECT) {
243                                 newml = MEM_dupallocN(ml);
244                                 BLI_addtail(mb->editelems, newml);
245                                 mb->lastelem = newml;
246                                 ml->flag &= ~SELECT;
247                         }
248                         ml = ml->prev;
249                 }
250                 WM_event_add_notifier(C, NC_GEOM | ND_DATA, mb);
251                 DAG_id_tag_update(obedit->data, 0);
252         }
253
254         return OPERATOR_FINISHED;
255 }
256
257 static int duplicate_metaelems_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
258 {
259         int retv = duplicate_metaelems_exec(C, op);
260         
261         if (retv == OPERATOR_FINISHED) {
262                 RNA_enum_set(op->ptr, "mode", TFM_TRANSLATION);
263                 WM_operator_name_call(C, "TRANSFORM_OT_transform", WM_OP_INVOKE_REGION_WIN, op->ptr);
264         }
265         
266         return retv;
267 }
268
269
270 void MBALL_OT_duplicate_metaelems(wmOperatorType *ot)
271 {
272         /* identifiers */
273         ot->name = "Duplicate Metaelements";
274         ot->description = "Duplicate selected metaelement(s)";
275         ot->idname = "MBALL_OT_duplicate_metaelems";
276
277         /* callback functions */
278         ot->exec = duplicate_metaelems_exec;
279         ot->invoke = duplicate_metaelems_invoke;
280         ot->poll = ED_operator_editmball;
281
282         /* flags */
283         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
284         
285         /* to give to transform */
286         RNA_def_enum(ot->srna, "mode", transform_mode_types, TFM_TRANSLATION, "Mode", "");
287 }
288
289 /***************************** Delete operator *****************************/
290
291 /* Delete all selected MetaElems (not MetaBall) */
292 static int delete_metaelems_exec(bContext *C, wmOperator *UNUSED(op))
293 {
294         Object *obedit = CTX_data_edit_object(C);
295         MetaBall *mb = (MetaBall *)obedit->data;
296         MetaElem *ml, *next;
297         
298         ml = mb->editelems->first;
299         if (ml) {
300                 while (ml) {
301                         next = ml->next;
302                         if (ml->flag & SELECT) {
303                                 if (mb->lastelem == ml) mb->lastelem = NULL;
304                                 BLI_remlink(mb->editelems, ml);
305                                 MEM_freeN(ml);
306                         }
307                         ml = next;
308                 }
309                 WM_event_add_notifier(C, NC_GEOM | ND_DATA, mb);
310                 DAG_id_tag_update(obedit->data, 0);
311         }
312
313         return OPERATOR_FINISHED;
314 }
315
316 void MBALL_OT_delete_metaelems(wmOperatorType *ot)
317 {
318         /* identifiers */
319         ot->name = "Delete";
320         ot->description = "Delete selected metaelement(s)";
321         ot->idname = "MBALL_OT_delete_metaelems";
322
323         /* callback functions */
324         ot->exec = delete_metaelems_exec;
325         ot->poll = ED_operator_editmball;
326
327         /* flags */
328         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
329 }
330
331 /***************************** Hide operator *****************************/
332
333 /* Hide selected MetaElems */
334 static int hide_metaelems_exec(bContext *C, wmOperator *op)
335 {
336         Object *obedit = CTX_data_edit_object(C);
337         MetaBall *mb = (MetaBall *)obedit->data;
338         MetaElem *ml;
339         const int invert = RNA_boolean_get(op->ptr, "unselected") ? SELECT : 0;
340
341         ml = mb->editelems->first;
342
343         if (ml) {
344                 while (ml) {
345                         if ((ml->flag & SELECT) != invert)
346                                 ml->flag |= MB_HIDE;
347                         ml = ml->next;
348                 }
349                 WM_event_add_notifier(C, NC_GEOM | ND_DATA, mb);
350                 DAG_id_tag_update(obedit->data, 0);
351         }
352
353         return OPERATOR_FINISHED;
354 }
355
356 void MBALL_OT_hide_metaelems(wmOperatorType *ot)
357 {
358         /* identifiers */
359         ot->name = "Hide";
360         ot->description = "Hide (un)selected metaelement(s)";
361         ot->idname = "MBALL_OT_hide_metaelems";
362
363         /* callback functions */
364         ot->exec = hide_metaelems_exec;
365         ot->poll = ED_operator_editmball;
366
367         /* flags */
368         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
369         
370         /* props */
371         RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected");
372 }
373
374 /***************************** Unhide operator *****************************/
375
376 /* Unhide all edited MetaElems */
377 static int reveal_metaelems_exec(bContext *C, wmOperator *UNUSED(op))
378 {
379         Object *obedit = CTX_data_edit_object(C);
380         MetaBall *mb = (MetaBall *)obedit->data;
381         MetaElem *ml;
382
383         ml = mb->editelems->first;
384
385         if (ml) {
386                 while (ml) {
387                         ml->flag &= ~MB_HIDE;
388                         ml = ml->next;
389                 }
390                 WM_event_add_notifier(C, NC_GEOM | ND_DATA, mb);
391                 DAG_id_tag_update(obedit->data, 0);
392         }
393         
394         return OPERATOR_FINISHED;
395 }
396
397 void MBALL_OT_reveal_metaelems(wmOperatorType *ot)
398 {
399         /* identifiers */
400         ot->name = "Reveal";
401         ot->description = "Reveal all hidden metaelements";
402         ot->idname = "MBALL_OT_reveal_metaelems";
403         
404         /* callback functions */
405         ot->exec = reveal_metaelems_exec;
406         ot->poll = ED_operator_editmball;
407         
408         /* flags */
409         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
410 }
411
412 /* Select MetaElement with mouse click (user can select radius circle or
413  * stiffness circle) */
414 int mouse_mball(bContext *C, const int mval[2], int extend, int deselect, int toggle)
415 {
416         static MetaElem *startelem = NULL;
417         Object *obedit = CTX_data_edit_object(C);
418         ViewContext vc;
419         MetaBall *mb = (MetaBall *)obedit->data;
420         MetaElem *ml, *ml_act = NULL;
421         int a, hits;
422         unsigned int buffer[4 * MAXPICKBUF];
423         rcti rect;
424
425         view3d_set_viewcontext(C, &vc);
426
427         rect.xmin = mval[0] - 12;
428         rect.xmax = mval[0] + 12;
429         rect.ymin = mval[1] - 12;
430         rect.ymax = mval[1] + 12;
431
432         hits = view3d_opengl_select(&vc, buffer, MAXPICKBUF, &rect);
433
434         /* does startelem exist? */
435         ml = mb->editelems->first;
436         while (ml) {
437                 if (ml == startelem) break;
438                 ml = ml->next;
439         }
440
441         if (ml == NULL) startelem = mb->editelems->first;
442         
443         if (hits > 0) {
444                 ml = startelem;
445                 while (ml) {
446                         for (a = 0; a < hits; a++) {
447                                 /* index converted for gl stuff */
448                                 if (ml->selcol1 == buffer[4 * a + 3]) {
449                                         ml->flag |= MB_SCALE_RAD;
450                                         ml_act = ml;
451                                 }
452                                 if (ml->selcol2 == buffer[4 * a + 3]) {
453                                         ml->flag &= ~MB_SCALE_RAD;
454                                         ml_act = ml;
455                                 }
456                         }
457                         if (ml_act) break;
458                         ml = ml->next;
459                         if (ml == NULL) ml = mb->editelems->first;
460                         if (ml == startelem) break;
461                 }
462                 
463                 /* When some metaelem was found, then it is necessary to select or
464                  * deselect it. */
465                 if (ml_act) {
466                         if (extend) {
467                                 ml_act->flag |= SELECT;
468                         }
469                         else if (deselect) {
470                                 ml_act->flag &= ~SELECT;
471                         }
472                         else if (toggle) {
473                                 if (ml_act->flag & SELECT)
474                                         ml_act->flag &= ~SELECT;
475                                 else
476                                         ml_act->flag |= SELECT;
477                         }
478                         else {
479                                 /* Deselect all existing metaelems */
480                                 BKE_mball_deselect_all(mb);
481
482                                 /* Select only metaelem clicked on */
483                                 ml_act->flag |= SELECT;
484                         }
485                         
486                         mb->lastelem = ml_act;
487                         
488                         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, mb);
489
490                         return 1;
491                 }
492         }
493
494         return 0;
495 }
496
497
498 /*  ************* undo for MetaBalls ************* */
499
500 /* free all MetaElems from ListBase */
501 static void freeMetaElemlist(ListBase *lb)
502 {
503         MetaElem *ml, *next;
504
505         if (lb == NULL) return;
506
507         ml = lb->first;
508         while (ml) {
509                 next = ml->next;
510                 BLI_remlink(lb, ml);
511                 MEM_freeN(ml);
512                 ml = next;
513         }
514
515         lb->first = lb->last = NULL;
516 }
517
518
519 static void undoMball_to_editMball(void *lbu, void *lbe, void *UNUSED(obe))
520 {
521         ListBase *lb = lbu;
522         ListBase *editelems = lbe;
523         MetaElem *ml, *newml;
524         
525         freeMetaElemlist(editelems);
526
527         /* copy 'undo' MetaElems to 'edit' MetaElems */
528         ml = lb->first;
529         while (ml) {
530                 newml = MEM_dupallocN(ml);
531                 BLI_addtail(editelems, newml);
532                 ml = ml->next;
533         }
534         
535 }
536
537 static void *editMball_to_undoMball(void *lbe, void *UNUSED(obe))
538 {
539         ListBase *editelems = lbe;
540         ListBase *lb;
541         MetaElem *ml, *newml;
542
543         /* allocate memory for undo ListBase */
544         lb = MEM_callocN(sizeof(ListBase), "listbase undo");
545         lb->first = lb->last = NULL;
546         
547         /* copy contents of current ListBase to the undo ListBase */
548         ml = editelems->first;
549         while (ml) {
550                 newml = MEM_dupallocN(ml);
551                 BLI_addtail(lb, newml);
552                 ml = ml->next;
553         }
554         
555         return lb;
556 }
557
558 /* free undo ListBase of MetaElems */
559 static void free_undoMball(void *lbv)
560 {
561         ListBase *lb = lbv;
562         
563         freeMetaElemlist(lb);
564         MEM_freeN(lb);
565 }
566
567 static ListBase *metaball_get_editelems(Object *ob)
568 {
569         if (ob && ob->type == OB_MBALL) {
570                 struct MetaBall *mb = (struct MetaBall *)ob->data;
571                 return mb->editelems;
572         }
573         return NULL;
574 }
575
576
577 static void *get_data(bContext *C)
578 {
579         Object *obedit = CTX_data_edit_object(C);
580         return metaball_get_editelems(obedit);
581 }
582
583 /* this is undo system for MetaBalls */
584 void undo_push_mball(bContext *C, const char *name)
585 {
586         undo_editmode_push(C, name, get_data, free_undoMball, undoMball_to_editMball, editMball_to_undoMball, NULL);
587 }
588
589 /* matrix is 4x4 */
590 void ED_mball_transform(MetaBall *mb, float *mat)
591 {
592         MetaElem *me;
593         float quat[4];
594         const float scale = mat4_to_scale((float (*)[4])mat);
595         const float scale_sqrt = sqrtf(scale);
596
597         mat4_to_quat(quat, (float (*)[4])mat);
598
599         for (me = mb->elems.first; me; me = me->next) {
600                 mul_m4_v3((float (*)[4])mat, &me->x);
601                 mul_qt_qtqt(me->quat, quat, me->quat);
602                 me->rad *= scale;
603                 /* hrmf, probably elems shouldn't be
604                  * treating scale differently - campbell */
605                 if (!MB_TYPE_SIZE_SQUARED(me->type)) {
606                         mul_v3_fl(&me->expx, scale);
607                 }
608                 else {
609                         mul_v3_fl(&me->expx, scale_sqrt);
610                 }
611         }
612 }