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