merge with/from trunk at r35190
[blender.git] / source / blender / editors / metaball / mball_edit.c
1 /*
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21  * All rights reserved.
22  *
23  * The Original Code is: all of this file.
24  
25  * Contributor(s): none yet.
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  */
29
30 #include <math.h>
31 #include <string.h>
32
33 #include "MEM_guardedalloc.h"
34
35 #include "BLI_blenlib.h"
36 #include "BLI_math.h"
37 #include "BLI_rand.h"
38 #include "BLI_utildefines.h"
39
40 #include "DNA_meta_types.h"
41 #include "DNA_object_types.h"
42 #include "DNA_scene_types.h"
43
44 #include "RNA_define.h"
45 #include "RNA_access.h"
46
47 #include "BKE_depsgraph.h"
48 #include "BKE_context.h"
49 #include "BKE_mball.h"
50
51 #include "ED_mball.h"
52 #include "ED_screen.h"
53 #include "ED_view3d.h"
54 #include "ED_transform.h"
55 #include "ED_util.h"
56
57 #include "WM_api.h"
58 #include "WM_types.h"
59
60 #include "mball_intern.h"
61
62 /* This function is used to free all MetaElems from MetaBall */
63 void free_editMball(Object *obedit)
64 {
65         MetaBall *mb = (MetaBall*)obedit->data;
66
67         mb->editelems= NULL;
68         mb->lastelem= NULL;
69 }
70
71 /* This function is called, when MetaBall Object is
72  * switched from object mode to edit mode */
73 void make_editMball(Object *obedit)
74 {
75         MetaBall *mb = (MetaBall*)obedit->data;
76         MetaElem *ml;/*, *newml;*/
77
78         ml= mb->elems.first;
79         
80         while(ml) {
81                 if(ml->flag & SELECT) mb->lastelem = ml;
82                 ml= ml->next;
83         }
84
85         mb->editelems = &mb->elems;
86 }
87
88 /* This function is called, when MetaBall Object switched from
89  * edit mode to object mode. List od MetaElements is copied
90  * from object->data->edit_elems to object->data->elems. */
91 void load_editMball(Object *UNUSED(obedit))
92 {
93 }
94
95 /* Add metaelem primitive to metaball object (which is in edit mode) */
96 MetaElem *add_metaball_primitive(bContext *C, float mat[4][4], int type, int UNUSED(newname))
97 {
98         Object *obedit= CTX_data_edit_object(C);
99         MetaBall *mball = (MetaBall*)obedit->data;
100         MetaElem *ml;
101
102         /* Deselect all existing metaelems */
103         ml= mball->editelems->first;
104         while(ml) {
105                 ml->flag &= ~SELECT;
106                 ml= ml->next;
107         }
108         
109         ml= add_metaball_element(mball, type);
110         copy_v3_v3(&ml->x, mat[3]);
111
112         ml->flag |= SELECT;
113         mball->lastelem= ml;
114         return ml;
115 }
116
117 /***************************** Select/Deselect operator *****************************/
118
119 /* Select or deselect all MetaElements */
120 static int select_all_exec(bContext *C, wmOperator *op)
121 {
122         //Scene *scene= CTX_data_scene(C);
123         Object *obedit= CTX_data_edit_object(C);
124         MetaBall *mb = (MetaBall*)obedit->data;
125         MetaElem *ml;
126         int action = RNA_enum_get(op->ptr, "action");
127
128         ml= mb->editelems->first;
129         if(ml) {
130                 if (action == SEL_TOGGLE) {
131                         action = SEL_SELECT;
132                         while(ml) {
133                                 if(ml->flag & SELECT) {
134                                         action = SEL_DESELECT;
135                                         break;
136                                 }
137                                 ml= ml->next;
138                         }
139                 }
140
141                 ml= mb->editelems->first;
142                 while(ml) {
143                         switch (action) {
144                         case SEL_SELECT:
145                                 ml->flag |= SELECT;
146                                 break;
147                         case SEL_DESELECT:
148                                 ml->flag &= ~SELECT;
149                                 break;
150                         case SEL_INVERT:
151                                 ml->flag ^= SELECT;
152                                 break;
153                         }
154                         ml= ml->next;
155                 }
156                 WM_event_add_notifier(C, NC_GEOM|ND_SELECT, mb);
157         }
158
159         return OPERATOR_FINISHED;
160 }
161
162 void MBALL_OT_select_all(wmOperatorType *ot)
163 {
164         /* identifiers */
165         ot->name= "Select or Deselect All";
166         ot->description= "Change selection of all meta elements";
167         ot->idname= "MBALL_OT_select_all";
168
169         /* callback functions */
170         ot->exec= 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 inverse operator *****************************/
180
181 /* Invert metaball selection */
182 static int select_inverse_metaelems_exec(bContext *C, wmOperator *UNUSED(op))
183 {
184         Object *obedit= CTX_data_edit_object(C);
185         MetaBall *mb = (MetaBall*)obedit->data;
186         MetaElem *ml;
187         
188         ml= mb->editelems->first;
189         if(ml) {
190                 while(ml) {
191                         if(ml->flag & SELECT)
192                                 ml->flag &= ~SELECT;
193                         else
194                                 ml->flag |= SELECT;
195                         ml= ml->next;
196                 }
197                 WM_event_add_notifier(C, NC_GEOM|ND_SELECT, mb);
198         }
199         
200         return OPERATOR_FINISHED;
201 }
202
203 void MBALL_OT_select_inverse_metaelems(wmOperatorType *ot)
204 {
205         /* identifiers */
206         ot->name= "Inverse";
207         ot->description= "Select inverse of (un)selected metaelements";
208         ot->idname= "MBALL_OT_select_inverse_metaelems";
209
210         /* callback functions */
211         ot->exec= select_inverse_metaelems_exec;
212         ot->poll= ED_operator_editmball;
213
214         /* flags */
215         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;  
216 }
217
218 /***************************** Select random operator *****************************/
219
220 /* Random metaball selection */
221 static int select_random_metaelems_exec(bContext *C, wmOperator *op)
222 {
223         Object *obedit= CTX_data_edit_object(C);
224         MetaBall *mb = (MetaBall*)obedit->data;
225         MetaElem *ml;
226         float percent= RNA_float_get(op->ptr, "percent");
227         
228         if(percent == 0.0)
229                 return OPERATOR_CANCELLED;
230         
231         ml= mb->editelems->first;
232         BLI_srand( BLI_rand() );        /* Random seed */
233         
234         /* Stupid version of random selection. Should be improved. */
235         while(ml) {
236                 if(BLI_frand() < percent)
237                         ml->flag |= SELECT;
238                 else
239                         ml->flag &= ~SELECT;
240                 ml= ml->next;
241         }
242         
243         WM_event_add_notifier(C, NC_GEOM|ND_SELECT, mb);
244         
245         return OPERATOR_FINISHED;
246 }
247
248
249 void MBALL_OT_select_random_metaelems(struct wmOperatorType *ot)
250 {
251         /* identifiers */
252         ot->name= "Random...";
253         ot->description= "Randomly select metaelements";
254         ot->idname= "MBALL_OT_select_random_metaelems";
255         
256         /* callback functions */
257         ot->exec= select_random_metaelems_exec;
258         ot->invoke= WM_operator_props_popup;
259         ot->poll= ED_operator_editmball;
260         
261         /* flags */
262         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
263         
264         /* properties */
265         RNA_def_float_percentage(ot->srna, "percent", 0.5f, 0.0f, 1.0f, "Percent", "Percentage of metaelems to select randomly.", 0.0001f, 1.0f);
266 }
267
268 /***************************** Duplicate operator *****************************/
269
270 /* Duplicate selected MetaElements */
271 static int duplicate_metaelems_exec(bContext *C, wmOperator *UNUSED(op))
272 {
273         Object *obedit= CTX_data_edit_object(C);
274         MetaBall *mb = (MetaBall*)obedit->data;
275         MetaElem *ml, *newml;
276         
277         ml= mb->editelems->last;
278         if(ml) {
279                 while(ml) {
280                         if(ml->flag & SELECT) {
281                                 newml= MEM_dupallocN(ml);
282                                 BLI_addtail(mb->editelems, newml);
283                                 mb->lastelem= newml;
284                                 ml->flag &= ~SELECT;
285                         }
286                         ml= ml->prev;
287                 }
288                 WM_event_add_notifier(C, NC_GEOM|ND_DATA, mb);
289                 DAG_id_tag_update(obedit->data, 0);
290         }
291
292         return OPERATOR_FINISHED;
293 }
294
295 static int duplicate_metaelems_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
296 {
297         int retv= duplicate_metaelems_exec(C, op);
298         
299         if (retv == OPERATOR_FINISHED) {
300                 RNA_int_set(op->ptr, "mode", TFM_TRANSLATION);
301                 WM_operator_name_call(C, "TRANSFORM_OT_transform", WM_OP_INVOKE_REGION_WIN, op->ptr);
302         }
303         
304         return retv;
305 }
306
307
308 void MBALL_OT_duplicate_metaelems(wmOperatorType *ot)
309 {
310         /* identifiers */
311         ot->name= "Duplicate";
312         ot->description= "Delete selected metaelement(s)";
313         ot->idname= "MBALL_OT_duplicate_metaelems";
314
315         /* callback functions */
316         ot->exec= duplicate_metaelems_exec;
317         ot->invoke= duplicate_metaelems_invoke;
318         ot->poll= ED_operator_editmball;
319
320         /* flags */
321         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
322         
323         /* to give to transform */
324         RNA_def_int(ot->srna, "mode", TFM_TRANSLATION, 0, INT_MAX, "Mode", "", 0, INT_MAX);
325 }
326
327 /***************************** Delete operator *****************************/
328
329 /* Delete all selected MetaElems (not MetaBall) */
330 static int delete_metaelems_exec(bContext *C, wmOperator *UNUSED(op))
331 {
332         Object *obedit= CTX_data_edit_object(C);
333         MetaBall *mb= (MetaBall*)obedit->data;
334         MetaElem *ml, *next;
335         
336         ml= mb->editelems->first;
337         if(ml) {
338                 while(ml) {
339                         next= ml->next;
340                         if(ml->flag & SELECT) {
341                                 if(mb->lastelem==ml) mb->lastelem= NULL;
342                                 BLI_remlink(mb->editelems, ml);
343                                 MEM_freeN(ml);
344                         }
345                         ml= next;
346                 }
347                 WM_event_add_notifier(C, NC_GEOM|ND_DATA, mb);
348                 DAG_id_tag_update(obedit->data, 0);
349         }
350
351         return OPERATOR_FINISHED;
352 }
353
354 void MBALL_OT_delete_metaelems(wmOperatorType *ot)
355 {
356         /* identifiers */
357         ot->name= "Delete";
358         ot->description= "Delete selected metaelement(s)";
359         ot->idname= "MBALL_OT_delete_metaelems";
360
361         /* callback functions */
362         ot->exec= delete_metaelems_exec;
363         ot->poll= ED_operator_editmball;
364
365         /* flags */
366         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;  
367 }
368
369 /***************************** Hide operator *****************************/
370
371 /* Hide selected MetaElems */
372 static int hide_metaelems_exec(bContext *C, wmOperator *op)
373 {
374         Object *obedit= CTX_data_edit_object(C);
375         MetaBall *mb= (MetaBall*)obedit->data;
376         MetaElem *ml;
377         const int invert= RNA_boolean_get(op->ptr, "unselected") ? SELECT : 0;
378
379         ml= mb->editelems->first;
380
381         if(ml) {
382                 while(ml){
383                         if((ml->flag & SELECT) != invert)
384                                 ml->flag |= MB_HIDE;
385                         ml= ml->next;
386                 }
387                 WM_event_add_notifier(C, NC_GEOM|ND_DATA, mb);
388                 DAG_id_tag_update(obedit->data, 0);
389         }
390
391         return OPERATOR_FINISHED;
392 }
393
394 void MBALL_OT_hide_metaelems(wmOperatorType *ot)
395 {
396         /* identifiers */
397         ot->name= "Hide";
398         ot->description= "Hide (un)selected metaelement(s)";
399         ot->idname= "MBALL_OT_hide_metaelems";
400
401         /* callback functions */
402         ot->exec= hide_metaelems_exec;
403         ot->poll= ED_operator_editmball;
404
405         /* flags */
406         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
407         
408         /* props */
409         RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected.");
410 }
411
412 /***************************** Unhide operator *****************************/
413
414 /* Unhide all edited MetaElems */
415 static int reveal_metaelems_exec(bContext *C, wmOperator *UNUSED(op))
416 {
417         Object *obedit= CTX_data_edit_object(C);
418         MetaBall *mb= (MetaBall*)obedit->data;
419         MetaElem *ml;
420
421         ml= mb->editelems->first;
422
423         if(ml) {
424                 while(ml) {
425                         ml->flag &= ~MB_HIDE;
426                         ml= ml->next;
427                 }
428                 WM_event_add_notifier(C, NC_GEOM|ND_DATA, mb);
429                 DAG_id_tag_update(obedit->data, 0);
430         }
431         
432         return OPERATOR_FINISHED;
433 }
434
435 void MBALL_OT_reveal_metaelems(wmOperatorType *ot)
436 {
437         /* identifiers */
438         ot->name= "Reveal";
439         ot->description= "Reveal all hidden metaelements";
440         ot->idname= "MBALL_OT_reveal_metaelems";
441         
442         /* callback functions */
443         ot->exec= reveal_metaelems_exec;
444         ot->poll= ED_operator_editmball;
445         
446         /* flags */
447         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;  
448 }
449
450 /* Select MetaElement with mouse click (user can select radius circle or
451  * stiffness circle) */
452 int mouse_mball(bContext *C, short mval[2], int extend)
453 {
454         static MetaElem *startelem=NULL;
455         Object *obedit= CTX_data_edit_object(C);
456         ViewContext vc;
457         MetaBall *mb = (MetaBall*)obedit->data;
458         MetaElem *ml, *act=NULL;
459         int a, hits;
460         unsigned int buffer[4*MAXPICKBUF];
461         rcti rect;
462
463         view3d_set_viewcontext(C, &vc);
464
465         rect.xmin= mval[0]-12;
466         rect.xmax= mval[0]+12;
467         rect.ymin= mval[1]-12;
468         rect.ymax= mval[1]+12;
469
470         hits= view3d_opengl_select(&vc, buffer, MAXPICKBUF, &rect);
471
472         /* does startelem exist? */
473         ml= mb->editelems->first;
474         while(ml) {
475                 if(ml==startelem) break;
476                 ml= ml->next;
477         }
478
479         if(ml==NULL) startelem= mb->editelems->first;
480         
481         if(hits>0) {
482                 ml= startelem;
483                 while(ml) {
484                         for(a=0; a<hits; a++) {
485                                 /* index converted for gl stuff */
486                                 if(ml->selcol1==buffer[ 4 * a + 3 ]){
487                                         ml->flag |= MB_SCALE_RAD;
488                                         act= ml;
489                                 }
490                                 if(ml->selcol2==buffer[ 4 * a + 3 ]){
491                                         ml->flag &= ~MB_SCALE_RAD;
492                                         act= ml;
493                                 }
494                         }
495                         if(act) break;
496                         ml= ml->next;
497                         if(ml==NULL) ml= mb->editelems->first;
498                         if(ml==startelem) break;
499                 }
500                 
501                 /* When some metaelem was found, then it is necessary to select or
502                  * deselet it. */
503                 if(act) {
504                         if(extend==0) {
505                                 /* Deselect all existing metaelems */
506                                 ml= mb->editelems->first;
507                                 while(ml) {
508                                         ml->flag &= ~SELECT;
509                                         ml= ml->next;
510                                 }
511                                 /* Select only metaelem clicked on */
512                                 act->flag |= SELECT;
513                         }
514                         else {
515                                 if(act->flag & SELECT)
516                                         act->flag &= ~SELECT;
517                                 else
518                                         act->flag |= SELECT;
519                         }
520                         mb->lastelem= act;
521                         
522                         WM_event_add_notifier(C, NC_GEOM|ND_SELECT, mb);
523
524                         return 1;
525                 }
526         }
527
528         return 0;
529 }
530
531
532 /*  ************* undo for MetaBalls ************* */
533
534 /* free all MetaElems from ListBase */
535 static void freeMetaElemlist(ListBase *lb)
536 {
537         MetaElem *ml, *next;
538
539         if(lb==NULL) return;
540
541         ml= lb->first;
542         while(ml){
543                 next= ml->next;
544                 BLI_remlink(lb, ml);
545                 MEM_freeN(ml);
546                 ml= next;
547         }
548
549         lb->first= lb->last= NULL;
550 }
551
552
553 static void undoMball_to_editMball(void *lbu, void *lbe, void *obdata)
554 {
555         ListBase *lb= lbu;
556         ListBase *editelems= lbe;
557         MetaElem *ml, *newml;
558         
559         freeMetaElemlist(editelems);
560
561         /* copy 'undo' MetaElems to 'edit' MetaElems */
562         ml= lb->first;
563         while(ml){
564                 newml= MEM_dupallocN(ml);
565                 BLI_addtail(editelems, newml);
566                 ml= ml->next;
567         }
568         
569 }
570
571 static void *editMball_to_undoMball(void *lbe, void *obdata)
572 {
573         ListBase *editelems= lbe;
574         ListBase *lb;
575         MetaElem *ml, *newml;
576
577         /* allocate memory for undo ListBase */
578         lb= MEM_callocN(sizeof(ListBase), "listbase undo");
579         lb->first= lb->last= NULL;
580         
581         /* copy contents of current ListBase to the undo ListBase */
582         ml= editelems->first;
583         while(ml){
584                 newml= MEM_dupallocN(ml);
585                 BLI_addtail(lb, newml);
586                 ml= ml->next;
587         }
588         
589         return lb;
590 }
591
592 /* free undo ListBase of MetaElems */
593 static void free_undoMball(void *lbv)
594 {
595         ListBase *lb= lbv;
596         
597         freeMetaElemlist(lb);
598         MEM_freeN(lb);
599 }
600
601 static ListBase *metaball_get_editelems(Object *ob)
602 {
603         if(ob && ob->type==OB_MBALL) {
604                 struct MetaBall *mb= (struct MetaBall*)ob->data;
605                 return mb->editelems;
606         }
607         return NULL;
608 }
609
610
611 static void *get_data(bContext *C)
612 {
613         Object *obedit= CTX_data_edit_object(C);
614         return metaball_get_editelems(obedit);
615 }
616
617 /* this is undo system for MetaBalls */
618 void undo_push_mball(bContext *C, const char *name)
619 {
620         undo_editmode_push(C, name, get_data, free_undoMball, undoMball_to_editMball, editMball_to_undoMball, NULL);
621 }
622