Pass EvaluationContext argument everywhere
[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
51 #include "BKE_context.h"
52 #include "BKE_mball.h"
53
54 #include "DEG_depsgraph.h"
55
56 #include "ED_mball.h"
57 #include "ED_screen.h"
58 #include "ED_view3d.h"
59 #include "ED_util.h"
60
61 #include "WM_api.h"
62 #include "WM_types.h"
63
64 #include "mball_intern.h"
65
66 /* This function is used to free all MetaElems from MetaBall */
67 void ED_mball_editmball_free(Object *obedit)
68 {
69         MetaBall *mb = (MetaBall *)obedit->data;
70
71         mb->editelems = NULL;
72         mb->lastelem = NULL;
73 }
74
75 /* This function is called, when MetaBall Object is
76  * switched from object mode to edit mode */
77 void ED_mball_editmball_make(Object *obedit)
78 {
79         MetaBall *mb = (MetaBall *)obedit->data;
80         MetaElem *ml; /*, *newml;*/
81
82         ml = mb->elems.first;
83         
84         while (ml) {
85                 if (ml->flag & SELECT) mb->lastelem = ml;
86                 ml = ml->next;
87         }
88
89         mb->editelems = &mb->elems;
90 }
91
92 /* This function is called, when MetaBall Object switched from
93  * edit mode to object mode. List of MetaElements is copied
94  * from object->data->edit_elems to object->data->elems. */
95 void ED_mball_editmball_load(Object *UNUSED(obedit))
96 {
97 }
98
99 /* Add metaelem primitive to metaball object (which is in edit mode) */
100 MetaElem *ED_mball_add_primitive(bContext *UNUSED(C), Object *obedit, float mat[4][4], float dia, int type)
101 {
102         MetaBall *mball = (MetaBall *)obedit->data;
103         MetaElem *ml;
104
105         /* Deselect all existing metaelems */
106         ml = mball->editelems->first;
107         while (ml) {
108                 ml->flag &= ~SELECT;
109                 ml = ml->next;
110         }
111         
112         ml = BKE_mball_element_add(mball, type);
113         ml->rad *= dia;
114         mball->wiresize *= dia;
115         mball->rendersize *= dia;
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 mball_select_all_exec(bContext *C, wmOperator *op)
127 {
128         Object *obedit = CTX_data_edit_object(C);
129         MetaBall *mb = (MetaBall *)obedit->data;
130         MetaElem *ml;
131         int action = RNA_enum_get(op->ptr, "action");
132
133         if (BLI_listbase_is_empty(mb->editelems))
134                 return OPERATOR_CANCELLED;
135
136         if (action == SEL_TOGGLE) {
137                 action = SEL_SELECT;
138                 for (ml = mb->editelems->first; ml; ml = ml->next) {
139                         if (ml->flag & SELECT) {
140                                 action = SEL_DESELECT;
141                                 break;
142                         }
143                 }
144         }
145
146         switch (action) {
147                 case SEL_SELECT:
148                         BKE_mball_select_all(mb);
149                         break;
150                 case SEL_DESELECT:
151                         BKE_mball_deselect_all(mb);
152                         break;
153                 case SEL_INVERT:
154                         BKE_mball_select_swap(mb);
155                         break;
156         }
157
158         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, mb);
159
160         return OPERATOR_FINISHED;
161 }
162
163 void MBALL_OT_select_all(wmOperatorType *ot)
164 {
165         /* identifiers */
166         ot->name = "(De)select All";
167         ot->description = "Change selection of all meta elements";
168         ot->idname = "MBALL_OT_select_all";
169
170         /* callback functions */
171         ot->exec = mball_select_all_exec;
172         ot->poll = ED_operator_editmball;
173
174         /* flags */
175         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
176
177         WM_operator_properties_select_all(ot);
178 }
179
180
181 /* -------------------------------------------------------------------- */
182 /* Select Similar */
183
184 enum {
185         SIMMBALL_TYPE = 1,
186         SIMMBALL_RADIUS,
187         SIMMBALL_STIFFNESS,
188         SIMMBALL_ROTATION
189 };
190
191 static EnumPropertyItem prop_similar_types[] = {
192         {SIMMBALL_TYPE, "TYPE", 0, "Type", ""},
193         {SIMMBALL_RADIUS, "RADIUS", 0, "Radius", ""},
194         {SIMMBALL_STIFFNESS, "STIFFNESS", 0, "Stiffness", ""},
195         {SIMMBALL_ROTATION, "ROTATION", 0, "Rotation", ""},
196         {0, NULL, 0, NULL, NULL}
197 };
198
199 static bool mball_select_similar_type(MetaBall *mb)
200 {
201         MetaElem *ml;
202         bool changed = false;
203
204         for (ml = mb->editelems->first; ml; ml = ml->next) {
205                 if (ml->flag & SELECT) {
206                         MetaElem *ml_iter;
207
208                         for (ml_iter = mb->editelems->first; ml_iter; ml_iter = ml_iter->next) {
209                                 if ((ml_iter->flag & SELECT) == 0) {
210                                         if (ml->type == ml_iter->type) {
211                                                 ml_iter->flag |= SELECT;
212                                                 changed = true;
213                                         }
214                                 }
215                         }
216                 }
217         }
218
219         return changed;
220 }
221
222 static bool mball_select_similar_radius(MetaBall *mb, const float thresh)
223 {
224         MetaElem *ml;
225         bool changed = false;
226
227         for (ml = mb->editelems->first; ml; ml = ml->next) {
228                 if (ml->flag & SELECT) {
229                         MetaElem *ml_iter;
230
231                         for (ml_iter = mb->editelems->first; ml_iter; ml_iter = ml_iter->next) {
232                                 if ((ml_iter->flag & SELECT) == 0) {
233                                         if (fabsf(ml_iter->rad - ml->rad) <= (thresh * ml->rad)) {
234                                                 ml_iter->flag |= SELECT;
235                                                 changed = true;
236                                         }
237                                 }
238                         }
239                 }
240         }
241
242         return changed;
243 }
244
245 static bool mball_select_similar_stiffness(MetaBall *mb, const float thresh)
246 {
247         MetaElem *ml;
248         bool changed = false;
249
250         for (ml = mb->editelems->first; ml; ml = ml->next) {
251                 if (ml->flag & SELECT) {
252                         MetaElem *ml_iter;
253
254                         for (ml_iter = mb->editelems->first; ml_iter; ml_iter = ml_iter->next) {
255                                 if ((ml_iter->flag & SELECT) == 0) {
256                                         if (fabsf(ml_iter->s - ml->s) <= thresh) {
257                                                 ml_iter->flag |= SELECT;
258                                                 changed = true;
259                                         }
260                                 }
261                         }
262                 }
263         }
264
265         return changed;
266 }
267
268 static bool mball_select_similar_rotation(MetaBall *mb, const float thresh)
269 {
270         const float thresh_rad = thresh * (float)M_PI_2;
271         MetaElem *ml;
272         bool changed = false;
273
274         for (ml = mb->editelems->first; ml; ml = ml->next) {
275                 if (ml->flag & SELECT) {
276                         MetaElem *ml_iter;
277
278                         float ml_mat[3][3];
279
280                         unit_m3(ml_mat);
281                         mul_qt_v3(ml->quat, ml_mat[0]);
282                         mul_qt_v3(ml->quat, ml_mat[1]);
283                         mul_qt_v3(ml->quat, ml_mat[2]);
284                         normalize_m3(ml_mat);
285
286                         for (ml_iter = mb->editelems->first; ml_iter; ml_iter = ml_iter->next) {
287                                 if ((ml_iter->flag & SELECT) == 0) {
288                                         float ml_iter_mat[3][3];
289
290                                         unit_m3(ml_iter_mat);
291                                         mul_qt_v3(ml_iter->quat, ml_iter_mat[0]);
292                                         mul_qt_v3(ml_iter->quat, ml_iter_mat[1]);
293                                         mul_qt_v3(ml_iter->quat, ml_iter_mat[2]);
294                                         normalize_m3(ml_iter_mat);
295
296                                         if ((angle_normalized_v3v3(ml_mat[0], ml_iter_mat[0]) +
297                                              angle_normalized_v3v3(ml_mat[1], ml_iter_mat[1]) +
298                                              angle_normalized_v3v3(ml_mat[2], ml_iter_mat[2])) < thresh_rad)
299                                         {
300                                                 ml_iter->flag |= SELECT;
301                                                 changed = true;
302                                         }
303                                 }
304                         }
305                 }
306         }
307
308         return changed;
309 }
310
311 static int mball_select_similar_exec(bContext *C, wmOperator *op)
312 {
313         Object *obedit = CTX_data_edit_object(C);
314         MetaBall *mb = (MetaBall *)obedit->data;
315
316         int type = RNA_enum_get(op->ptr, "type");
317         float thresh = RNA_float_get(op->ptr, "threshold");
318         bool changed = false;
319
320         switch (type) {
321                 case SIMMBALL_TYPE:
322                         changed = mball_select_similar_type(mb);
323                         break;
324                 case SIMMBALL_RADIUS:
325                         changed = mball_select_similar_radius(mb, thresh);
326                         break;
327                 case SIMMBALL_STIFFNESS:
328                         changed = mball_select_similar_stiffness(mb, thresh);
329                         break;
330                 case SIMMBALL_ROTATION:
331                         changed = mball_select_similar_rotation(mb, thresh);
332                         break;
333                 default:
334                         BLI_assert(0);
335                         break;
336         }
337
338         if (changed) {
339                 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, mb);
340         }
341
342         return OPERATOR_FINISHED;
343 }
344
345 void MBALL_OT_select_similar(wmOperatorType *ot)
346 {
347         /* identifiers */
348         ot->name = "Select Similar";
349         ot->idname = "MBALL_OT_select_similar";
350
351         /* callback functions */
352         ot->invoke = WM_menu_invoke;
353         ot->exec = mball_select_similar_exec;
354         ot->poll = ED_operator_editmball;
355         ot->description = "Select similar metaballs by property types";
356
357         /* flags */
358         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
359
360         /* properties */
361         ot->prop = RNA_def_enum(ot->srna, "type", prop_similar_types, 0, "Type", "");
362
363         RNA_def_float(ot->srna, "threshold", 0.1, 0.0, 1.0, "Threshold", "", 0.01, 1.0);
364 }
365
366
367 /***************************** Select random operator *****************************/
368
369 /* Random metaball selection */
370 static int select_random_metaelems_exec(bContext *C, wmOperator *op)
371 {
372         Object *obedit = CTX_data_edit_object(C);
373         MetaBall *mb = (MetaBall *)obedit->data;
374         MetaElem *ml;
375         const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT);
376         const float randfac = RNA_float_get(op->ptr, "percent") / 100.0f;
377         const int seed = WM_operator_properties_select_random_seed_increment_get(op);
378         
379         RNG *rng = BLI_rng_new_srandom(seed);
380
381         for (ml = mb->editelems->first; ml; ml = ml->next) {
382                 if (BLI_rng_get_float(rng) < randfac) {
383                         if (select)
384                                 ml->flag |= SELECT;
385                         else
386                                 ml->flag &= ~SELECT;
387                 }
388         }
389
390         BLI_rng_free(rng);
391
392         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, mb);
393         
394         return OPERATOR_FINISHED;
395 }
396
397
398 void MBALL_OT_select_random_metaelems(struct wmOperatorType *ot)
399 {
400         /* identifiers */
401         ot->name = "Select Random";
402         ot->description = "Randomly select metaelements";
403         ot->idname = "MBALL_OT_select_random_metaelems";
404         
405         /* callback functions */
406         ot->exec = select_random_metaelems_exec;
407         ot->poll = ED_operator_editmball;
408         
409         /* flags */
410         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
411         
412         /* properties */
413         WM_operator_properties_select_random(ot);
414 }
415
416 /***************************** Duplicate operator *****************************/
417
418 /* Duplicate selected MetaElements */
419 static int duplicate_metaelems_exec(bContext *C, wmOperator *UNUSED(op))
420 {
421         Object *obedit = CTX_data_edit_object(C);
422         MetaBall *mb = (MetaBall *)obedit->data;
423         MetaElem *ml, *newml;
424         
425         ml = mb->editelems->last;
426         if (ml) {
427                 while (ml) {
428                         if (ml->flag & SELECT) {
429                                 newml = MEM_dupallocN(ml);
430                                 BLI_addtail(mb->editelems, newml);
431                                 mb->lastelem = newml;
432                                 ml->flag &= ~SELECT;
433                         }
434                         ml = ml->prev;
435                 }
436                 WM_event_add_notifier(C, NC_GEOM | ND_DATA, mb);
437                 DEG_id_tag_update(obedit->data, 0);
438         }
439
440         return OPERATOR_FINISHED;
441 }
442
443 void MBALL_OT_duplicate_metaelems(wmOperatorType *ot)
444 {
445         /* identifiers */
446         ot->name = "Duplicate Metaelements";
447         ot->description = "Duplicate selected metaelement(s)";
448         ot->idname = "MBALL_OT_duplicate_metaelems";
449
450         /* callback functions */
451         ot->exec = duplicate_metaelems_exec;
452         ot->poll = ED_operator_editmball;
453
454         /* flags */
455         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
456 }
457
458 /***************************** Delete operator *****************************/
459
460 /* Delete all selected MetaElems (not MetaBall) */
461 static int delete_metaelems_exec(bContext *C, wmOperator *UNUSED(op))
462 {
463         Object *obedit = CTX_data_edit_object(C);
464         MetaBall *mb = (MetaBall *)obedit->data;
465         MetaElem *ml, *next;
466         
467         ml = mb->editelems->first;
468         if (ml) {
469                 while (ml) {
470                         next = ml->next;
471                         if (ml->flag & SELECT) {
472                                 if (mb->lastelem == ml) mb->lastelem = NULL;
473                                 BLI_remlink(mb->editelems, ml);
474                                 MEM_freeN(ml);
475                         }
476                         ml = next;
477                 }
478                 WM_event_add_notifier(C, NC_GEOM | ND_DATA, mb);
479                 DEG_id_tag_update(obedit->data, 0);
480         }
481
482         return OPERATOR_FINISHED;
483 }
484
485 void MBALL_OT_delete_metaelems(wmOperatorType *ot)
486 {
487         /* identifiers */
488         ot->name = "Delete";
489         ot->description = "Delete selected metaelement(s)";
490         ot->idname = "MBALL_OT_delete_metaelems";
491
492         /* callback functions */
493         ot->exec = delete_metaelems_exec;
494         ot->poll = ED_operator_editmball;
495
496         /* flags */
497         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
498 }
499
500 /***************************** Hide operator *****************************/
501
502 /* Hide selected MetaElems */
503 static int hide_metaelems_exec(bContext *C, wmOperator *op)
504 {
505         Object *obedit = CTX_data_edit_object(C);
506         MetaBall *mb = (MetaBall *)obedit->data;
507         MetaElem *ml;
508         const bool invert = RNA_boolean_get(op->ptr, "unselected") ? SELECT : 0;
509
510         ml = mb->editelems->first;
511
512         if (ml) {
513                 while (ml) {
514                         if ((ml->flag & SELECT) != invert)
515                                 ml->flag |= MB_HIDE;
516                         ml = ml->next;
517                 }
518                 WM_event_add_notifier(C, NC_GEOM | ND_DATA, mb);
519                 DEG_id_tag_update(obedit->data, 0);
520         }
521
522         return OPERATOR_FINISHED;
523 }
524
525 void MBALL_OT_hide_metaelems(wmOperatorType *ot)
526 {
527         /* identifiers */
528         ot->name = "Hide";
529         ot->description = "Hide (un)selected metaelement(s)";
530         ot->idname = "MBALL_OT_hide_metaelems";
531
532         /* callback functions */
533         ot->exec = hide_metaelems_exec;
534         ot->poll = ED_operator_editmball;
535
536         /* flags */
537         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
538         
539         /* props */
540         RNA_def_boolean(ot->srna, "unselected", false, "Unselected", "Hide unselected rather than selected");
541 }
542
543 /***************************** Unhide operator *****************************/
544
545 /* Unhide all edited MetaElems */
546 static int reveal_metaelems_exec(bContext *C, wmOperator *UNUSED(op))
547 {
548         Object *obedit = CTX_data_edit_object(C);
549         MetaBall *mb = (MetaBall *)obedit->data;
550         MetaElem *ml;
551
552         ml = mb->editelems->first;
553
554         if (ml) {
555                 while (ml) {
556                         ml->flag &= ~MB_HIDE;
557                         ml = ml->next;
558                 }
559                 WM_event_add_notifier(C, NC_GEOM | ND_DATA, mb);
560                 DEG_id_tag_update(obedit->data, 0);
561         }
562         
563         return OPERATOR_FINISHED;
564 }
565
566 void MBALL_OT_reveal_metaelems(wmOperatorType *ot)
567 {
568         /* identifiers */
569         ot->name = "Reveal";
570         ot->description = "Reveal all hidden metaelements";
571         ot->idname = "MBALL_OT_reveal_metaelems";
572         
573         /* callback functions */
574         ot->exec = reveal_metaelems_exec;
575         ot->poll = ED_operator_editmball;
576         
577         /* flags */
578         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
579 }
580
581 /* Select MetaElement with mouse click (user can select radius circle or
582  * stiffness circle) */
583 bool ED_mball_select_pick(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
584 {
585         static MetaElem *startelem = NULL;
586         Object *obedit = CTX_data_edit_object(C);
587         ViewContext vc;
588         MetaBall *mb = (MetaBall *)obedit->data;
589         MetaElem *ml, *ml_act = NULL;
590         int a, hits;
591         unsigned int buffer[MAXPICKBUF];
592         rcti rect;
593
594         view3d_set_viewcontext(C, &vc);
595
596         BLI_rcti_init_pt_radius(&rect, mval, 12);
597
598         hits = view3d_opengl_select(C, &vc, buffer, MAXPICKBUF, &rect, VIEW3D_SELECT_PICK_NEAREST);
599
600         /* does startelem exist? */
601         ml = mb->editelems->first;
602         while (ml) {
603                 if (ml == startelem) break;
604                 ml = ml->next;
605         }
606
607         if (ml == NULL) startelem = mb->editelems->first;
608         
609         if (hits > 0) {
610                 ml = startelem;
611                 while (ml) {
612                         for (a = 0; a < hits; a++) {
613                                 /* index converted for gl stuff */
614                                 if (ml->selcol1 == buffer[4 * a + 3]) {
615                                         ml->flag |= MB_SCALE_RAD;
616                                         ml_act = ml;
617                                 }
618                                 if (ml->selcol2 == buffer[4 * a + 3]) {
619                                         ml->flag &= ~MB_SCALE_RAD;
620                                         ml_act = ml;
621                                 }
622                         }
623                         if (ml_act) break;
624                         ml = ml->next;
625                         if (ml == NULL) ml = mb->editelems->first;
626                         if (ml == startelem) break;
627                 }
628                 
629                 /* When some metaelem was found, then it is necessary to select or
630                  * deselect it. */
631                 if (ml_act) {
632                         if (extend) {
633                                 ml_act->flag |= SELECT;
634                         }
635                         else if (deselect) {
636                                 ml_act->flag &= ~SELECT;
637                         }
638                         else if (toggle) {
639                                 if (ml_act->flag & SELECT)
640                                         ml_act->flag &= ~SELECT;
641                                 else
642                                         ml_act->flag |= SELECT;
643                         }
644                         else {
645                                 /* Deselect all existing metaelems */
646                                 BKE_mball_deselect_all(mb);
647
648                                 /* Select only metaelem clicked on */
649                                 ml_act->flag |= SELECT;
650                         }
651                         
652                         mb->lastelem = ml_act;
653                         
654                         WM_event_add_notifier(C, NC_GEOM | ND_SELECT, mb);
655
656                         return true;
657                 }
658         }
659
660         return false;
661 }
662
663
664 /*  ************* undo for MetaBalls ************* */
665
666 typedef struct UndoMBall {
667         ListBase editelems;
668         int lastelem_index;
669 } UndoMBall;
670
671 /* free all MetaElems from ListBase */
672 static void freeMetaElemlist(ListBase *lb)
673 {
674         MetaElem *ml;
675
676         if (lb == NULL) return;
677
678         while ((ml = BLI_pophead(lb))) {
679                 MEM_freeN(ml);
680         }
681 }
682
683
684 static void undoMball_to_editMball(void *umb_v, void *mb_v, void *UNUSED(obdata))
685 {
686         MetaBall *mb = mb_v;
687         UndoMBall *umb = umb_v;
688
689         freeMetaElemlist(mb->editelems);
690         mb->lastelem = NULL;
691
692         /* copy 'undo' MetaElems to 'edit' MetaElems */
693         int index = 0;
694         for (MetaElem *ml_undo = umb->editelems.first; ml_undo; ml_undo = ml_undo->next, index += 1) {
695                 MetaElem *ml_edit = MEM_dupallocN(ml_undo);
696                 BLI_addtail(mb->editelems, ml_edit);
697                 if (index == umb->lastelem_index) {
698                         mb->lastelem = ml_edit;
699                 }
700         }
701         
702 }
703
704 static void *editMball_to_undoMball(void *mb_v, void *UNUSED(obdata))
705 {
706         MetaBall *mb = mb_v;
707         UndoMBall *umb;
708
709         /* allocate memory for undo ListBase */
710         umb = MEM_callocN(sizeof(UndoMBall), __func__);
711         umb->lastelem_index = -1;
712         
713         /* copy contents of current ListBase to the undo ListBase */
714         int index = 0;
715         for (MetaElem *ml_edit = mb->editelems->first; ml_edit; ml_edit = ml_edit->next, index += 1) {
716                 MetaElem *ml_undo = MEM_dupallocN(ml_edit);
717                 BLI_addtail(&umb->editelems, ml_undo);
718                 if (ml_edit == mb->lastelem) {
719                         umb->lastelem_index = index;
720                 }
721         }
722         
723         return umb;
724 }
725
726 /* free undo ListBase of MetaElems */
727 static void free_undoMball(void *umb_v)
728 {
729         UndoMBall *umb = umb_v;
730         
731         freeMetaElemlist(&umb->editelems);
732         MEM_freeN(umb);
733 }
734
735 static MetaBall *metaball_get_obdata(Object *ob)
736 {
737         if (ob && ob->type == OB_MBALL) {
738                 return ob->data;
739         }
740         return NULL;
741 }
742
743
744 static void *get_data(bContext *C)
745 {
746         Object *obedit = CTX_data_edit_object(C);
747         return metaball_get_obdata(obedit);
748 }
749
750 /* this is undo system for MetaBalls */
751 void undo_push_mball(bContext *C, const char *name)
752 {
753         undo_editmode_push(C, name, get_data, free_undoMball, undoMball_to_editMball, editMball_to_undoMball, NULL);
754 }