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