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