added boolean type for bmesh operators, will make python wrapping clearer and also...
[blender.git] / source / blender / bmesh / intern / bmesh_operators.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  * Contributor(s): Joseph Eagar, Geoffrey Bantle, Campbell Barton
19  *
20  * ***** END GPL LICENSE BLOCK *****
21  */
22
23 /** \file blender/bmesh/intern/bmesh_operators.c
24  *  \ingroup bmesh
25  *
26  * BMesh operator access.
27  */
28
29 #include "MEM_guardedalloc.h"
30
31 #include "BLI_utildefines.h"
32 #include "BLI_string.h"
33 #include "BLI_math.h"
34 #include "BLI_memarena.h"
35 #include "BLI_mempool.h"
36 #include "BLI_listbase.h"
37 #include "BLI_array.h"
38
39 #include "bmesh.h"
40 #include "bmesh_private.h"
41
42 /* forward declarations */
43 static void bmo_flag_layer_alloc(BMesh *bm);
44 static void bmo_flag_layer_free(BMesh *bm);
45 static void bmo_flag_layer_clear(BMesh *bm);
46 static int bmesh_name_to_slotcode(BMOpDefine *def, const char *name);
47 static int bmesh_name_to_slotcode_check(BMOpDefine *def, const char *name);
48 static int bmesh_opname_to_opcode(const char *opname);
49
50 static const char *bmo_error_messages[] = {
51         NULL,
52         "Self intersection error",
53         "Could not dissolve vert",
54         "Could not connect vertices",
55         "Could not traverse mesh",
56         "Could not dissolve faces",
57         "Could not dissolve vertices",
58         "Tesselation error",
59         "Can not deal with non-manifold geometry",
60         "Invalid selection",
61         "Internal mesh error",
62 };
63
64
65 /* operator slot type information - size of one element of the type given. */
66 const int BMO_OPSLOT_TYPEINFO[BMO_OP_SLOT_TOTAL_TYPES] = {
67         0,
68     sizeof(int),
69         sizeof(int),
70         sizeof(float),
71         sizeof(void *),
72         0, /* unused */
73         0, /* unused */
74         0, /* unused */
75         sizeof(void *), /* pointer buffer */
76         sizeof(BMOElemMapping)
77 };
78
79 /* Dummy slot so there is something to return when slot name lookup fails */
80 static BMOpSlot BMOpEmptySlot = {0};
81
82 void BMO_op_flag_enable(BMesh *UNUSED(bm), BMOperator *op, const int op_flag)
83 {
84         op->flag |= op_flag;
85 }
86
87 void BMO_op_flag_disable(BMesh *UNUSED(bm), BMOperator *op, const int op_flag)
88 {
89         op->flag &= ~op_flag;
90 }
91
92 /*
93  * BMESH OPSTACK PUSH
94  *
95  * Pushes the opstack down one level
96  * and allocates a new flag layer if
97  * appropriate.
98  */
99 void BMO_push(BMesh *bm, BMOperator *UNUSED(op))
100 {
101         bm->stackdepth++;
102
103         /* add flag layer, if appropriate */
104         if (bm->stackdepth > 1)
105                 bmo_flag_layer_alloc(bm);
106         else
107                 bmo_flag_layer_clear(bm);
108 }
109
110 /*
111  * BMESH OPSTACK POP
112  *
113  * Pops the opstack one level
114  * and frees a flag layer if appropriate
115  * BMESH_TODO: investigate NOT freeing flag
116  * layers.
117  */
118 void BMO_pop(BMesh *bm)
119 {
120         if (bm->stackdepth > 1)
121                 bmo_flag_layer_free(bm);
122
123         bm->stackdepth--;
124 }
125
126 /*
127  * BMESH OPSTACK INIT OP
128  *
129  * Initializes an operator structure
130  * to a certain type
131  */
132 void BMO_op_init(BMesh *bm, BMOperator *op, const char *opname)
133 {
134         int i, opcode = bmesh_opname_to_opcode(opname);
135
136 #ifdef DEBUG
137         BM_ELEM_INDEX_VALIDATE(bm, "pre bmo", opname);
138 #else
139         (void)bm;
140 #endif
141
142         if (opcode == -1) {
143                 opcode = 0; /* error!, already printed, have a better way to handle this? */
144         }
145
146         memset(op, 0, sizeof(BMOperator));
147         op->type = opcode;
148         op->flag = opdefines[opcode]->flag;
149         
150         /* initialize the operator slot types */
151         for (i = 0; opdefines[opcode]->slottypes[i].type; i++) {
152                 op->slots[i].slottype = opdefines[opcode]->slottypes[i].type;
153                 op->slots[i].index = i;
154         }
155
156         /* callback */
157         op->exec = opdefines[opcode]->exec;
158
159         /* memarena, used for operator's slot buffers */
160         op->arena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "bmesh operator");
161         BLI_memarena_use_calloc (op->arena);
162 }
163
164 /*
165  * BMESH OPSTACK EXEC OP
166  *
167  * Executes a passed in operator. This handles
168  * the allocation and freeing of temporary flag
169  * layers and starting/stopping the modelling
170  * loop. Can be called from other operators
171  * exec callbacks as well.
172  */
173 void BMO_op_exec(BMesh *bm, BMOperator *op)
174 {
175         
176         BMO_push(bm, op);
177
178         if (bm->stackdepth == 2)
179                 bmesh_begin_edit(bm, op->flag);
180         op->exec(bm, op);
181         
182         if (bm->stackdepth == 2)
183                 bmesh_end_edit(bm, op->flag);
184         
185         BMO_pop(bm);
186 }
187
188 /*
189  * BMESH OPSTACK FINISH OP
190  *
191  * Does housekeeping chores related to finishing
192  * up an operator.
193  */
194 void BMO_op_finish(BMesh *bm, BMOperator *op)
195 {
196         BMOpSlot *slot;
197         int i;
198
199         for (i = 0; opdefines[op->type]->slottypes[i].type; i++) {
200                 slot = &op->slots[i];
201                 if (slot->slottype == BMO_OP_SLOT_MAPPING) {
202                         if (slot->data.ghash)
203                                 BLI_ghash_free(slot->data.ghash, NULL, NULL);
204                 }
205         }
206
207         BLI_memarena_free(op->arena);
208
209 #ifdef DEBUG
210         BM_ELEM_INDEX_VALIDATE(bm, "post bmo", opdefines[op->type]->name);
211 #else
212         (void)bm;
213 #endif
214 }
215
216 /*
217  * BMESH OPSTACK HAS SLOT
218  *
219  * Returns 1 if the named slot exists on the given operator,
220  * otherwise returns 0.
221  */
222 int BMO_slot_exists(BMOperator *op, const char *slotname)
223 {
224         int slotcode = bmesh_name_to_slotcode(opdefines[op->type], slotname);
225         return (slotcode >= 0);
226 }
227
228 /*
229  * BMESH OPSTACK GET SLOT
230  *
231  * Returns a pointer to the slot of
232  * type 'slotcode'
233  */
234 BMOpSlot *BMO_slot_get(BMOperator *op, const char *slotname)
235 {
236         int slotcode = bmesh_name_to_slotcode_check(opdefines[op->type], slotname);
237
238         if (slotcode < 0) {
239                 return &BMOpEmptySlot;
240         }
241
242         return &(op->slots[slotcode]);
243 }
244
245 /*
246  * BMESH OPSTACK COPY SLOT
247  *
248  * Copies data from one slot to another
249  */
250 void BMO_slot_copy(BMOperator *source_op, BMOperator *dest_op, const char *src, const char *dst)
251 {
252         BMOpSlot *source_slot = BMO_slot_get(source_op, src);
253         BMOpSlot *dest_slot = BMO_slot_get(dest_op, dst);
254
255         if (source_slot == dest_slot)
256                 return;
257
258         if (source_slot->slottype != dest_slot->slottype)
259                 return;
260         
261         if (dest_slot->slottype > BMO_OP_SLOT_VEC) {
262                 if (dest_slot->slottype != BMO_OP_SLOT_MAPPING) {
263                         /* do buffer copy */
264                         dest_slot->data.buf = NULL;
265                         dest_slot->len = source_slot->len;
266                         if (dest_slot->len) {
267                                 const int slot_alloc_size = BMO_OPSLOT_TYPEINFO[dest_slot->slottype] * dest_slot->len;
268                                 dest_slot->data.buf = BLI_memarena_alloc(dest_op->arena, slot_alloc_size);
269                                 memcpy(dest_slot->data.buf, source_slot->data.buf, slot_alloc_size);
270                         }
271                 }
272                 else {
273                         GHashIterator it;
274                         BMOElemMapping *srcmap, *dstmap;
275
276                         /* sanity check */
277                         if (!source_slot->data.ghash) return;
278                         
279                         if (!dest_slot->data.ghash) {
280                                 dest_slot->data.ghash = BLI_ghash_new(BLI_ghashutil_ptrhash,
281                                                                       BLI_ghashutil_ptrcmp, "bmesh operator 2");
282                         }
283
284                         BLI_ghashIterator_init(&it, source_slot->data.ghash);
285                         for ( ; (srcmap = BLI_ghashIterator_getValue(&it));
286                               BLI_ghashIterator_step(&it))
287                         {
288                                 dstmap = BLI_memarena_alloc(dest_op->arena, sizeof(*dstmap) + srcmap->len);
289
290                                 dstmap->element = srcmap->element;
291                                 dstmap->len = srcmap->len;
292                                 memcpy(dstmap + 1, srcmap + 1, srcmap->len);
293
294                                 BLI_ghash_insert(dest_slot->data.ghash, dstmap->element, dstmap);
295                         }
296                 }
297         }
298         else {
299                 dest_slot->data = source_slot->data;
300         }
301 }
302
303 /*
304  * BMESH OPSTACK SET XXX
305  *
306  * Sets the value of a slot depending on it's type
307  *
308  */
309
310 void BMO_slot_float_set(BMOperator *op, const char *slotname, const float f)
311 {
312         BMOpSlot *slot = BMO_slot_get(op, slotname);
313         if (!(slot->slottype == BMO_OP_SLOT_FLT))
314                 return;
315
316         slot->data.f = f;
317 }
318
319 void BMO_slot_int_set(BMOperator *op, const char *slotname, const int i)
320 {
321         BMOpSlot *slot = BMO_slot_get(op, slotname);
322         if (!(slot->slottype == BMO_OP_SLOT_INT))
323                 return;
324
325         slot->data.i = i;
326 }
327
328 void BMO_slot_bool_set(BMOperator *op, const char *slotname, const int i)
329 {
330         BMOpSlot *slot = BMO_slot_get(op, slotname);
331         if (!(slot->slottype == BMO_OP_SLOT_BOOL))
332                 return;
333
334         slot->data.i = i;
335 }
336
337 /* only supports square mats */
338 void BMO_slot_mat_set(struct BMOperator *op, const char *slotname, const float *mat, int size)
339 {
340         BMOpSlot *slot = BMO_slot_get(op, slotname);
341         if (!(slot->slottype == BMO_OP_SLOT_MAT))
342                 return;
343
344         slot->len = 4;
345         slot->data.p = BLI_memarena_alloc(op->arena, sizeof(float) * 4 * 4);
346         
347         if (size == 4) {
348                 memcpy(slot->data.p, mat, sizeof(float) * 4 * 4);
349         }
350         else if (size == 3) {
351                 copy_m4_m3(slot->data.p, (float (*)[3])mat);
352         }
353         else {
354                 fprintf(stderr, "%s: invalid size argument %d (bmesh internal error)\n", __func__, size);
355
356                 memset(slot->data.p, 0, sizeof(float) * 4 * 4);
357         }
358 }
359
360 void BMO_slot_mat4_get(struct BMOperator *op, const char *slotname, float r_mat[4][4])
361 {
362         BMOpSlot *slot = BMO_slot_get(op, slotname);
363         if (!(slot->slottype == BMO_OP_SLOT_MAT))
364                 return;
365
366         copy_m4_m4(r_mat, (float (*)[4])slot->data.p);
367 }
368
369 void BMO_slot_mat3_set(struct BMOperator *op, const char *slotname, float r_mat[3][3])
370 {
371         BMOpSlot *slot = BMO_slot_get(op, slotname);
372         if (!(slot->slottype == BMO_OP_SLOT_MAT))
373                 return;
374
375         copy_m3_m4(r_mat, slot->data.p);
376 }
377
378 void BMO_slot_ptr_set(BMOperator *op, const char *slotname, void *p)
379 {
380         BMOpSlot *slot = BMO_slot_get(op, slotname);
381         if (!(slot->slottype == BMO_OP_SLOT_PNT))
382                 return;
383
384         slot->data.p = p;
385 }
386
387 void BMO_slot_vec_set(BMOperator *op, const char *slotname, const float vec[3])
388 {
389         BMOpSlot *slot = BMO_slot_get(op, slotname);
390         if (!(slot->slottype == BMO_OP_SLOT_VEC))
391                 return;
392
393         copy_v3_v3(slot->data.vec, vec);
394 }
395
396
397 float BMO_slot_float_get(BMOperator *op, const char *slotname)
398 {
399         BMOpSlot *slot = BMO_slot_get(op, slotname);
400         if (!(slot->slottype == BMO_OP_SLOT_FLT))
401                 return 0.0f;
402
403         return slot->data.f;
404 }
405
406 int BMO_slot_int_get(BMOperator *op, const char *slotname)
407 {
408         BMOpSlot *slot = BMO_slot_get(op, slotname);
409         if (!(slot->slottype == BMO_OP_SLOT_INT))
410                 return 0;
411
412         return slot->data.i;
413 }
414
415 int BMO_slot_bool_get(BMOperator *op, const char *slotname)
416 {
417         BMOpSlot *slot = BMO_slot_get(op, slotname);
418         if (!(slot->slottype == BMO_OP_SLOT_BOOL))
419                 return 0;
420
421         return slot->data.i;
422 }
423
424
425 void *BMO_slot_ptr_get(BMOperator *op, const char *slotname)
426 {
427         BMOpSlot *slot = BMO_slot_get(op, slotname);
428         if (!(slot->slottype == BMO_OP_SLOT_PNT))
429                 return NULL;
430
431         return slot->data.p;
432 }
433
434 void BMO_slot_vec_get(BMOperator *op, const char *slotname, float r_vec[3])
435 {
436         BMOpSlot *slot = BMO_slot_get(op, slotname);
437         if (!(slot->slottype == BMO_OP_SLOT_VEC))
438                 return;
439
440         copy_v3_v3(r_vec, slot->data.vec);
441 }
442
443 /*
444  * BMO_COUNTFLAG
445  *
446  * Counts the number of elements of a certain type that
447  * have a specific flag set.
448  *
449  */
450
451 int BMO_mesh_flag_count(BMesh *bm, const short oflag, const char htype)
452 {
453         BMIter elements;
454         int count = 0;
455         BMElemF *ele_f;
456
457         if (htype & BM_VERT) {
458                 for (ele_f = BM_iter_new(&elements, bm, BM_VERTS_OF_MESH, bm); ele_f; ele_f = BM_iter_step(&elements)) {
459                         if (BMO_elem_flag_test(bm, ele_f, oflag))
460                                 count++;
461                 }
462         }
463         if (htype & BM_EDGE) {
464                 for (ele_f = BM_iter_new(&elements, bm, BM_EDGES_OF_MESH, bm); ele_f; ele_f = BM_iter_step(&elements)) {
465                         if (BMO_elem_flag_test(bm, ele_f, oflag))
466                                 count++;
467                 }
468         }
469         if (htype & BM_FACE) {
470                 for (ele_f = BM_iter_new(&elements, bm, BM_FACES_OF_MESH, bm); ele_f; ele_f = BM_iter_step(&elements)) {
471                         if (BMO_elem_flag_test(bm, ele_f, oflag))
472                                 count++;
473                 }
474         }
475
476         return count;
477 }
478
479 void BMO_mesh_flag_disable_all(BMesh *bm, BMOperator *UNUSED(op), const char htype, const short oflag)
480 {
481         const char iter_types[3] = {BM_VERTS_OF_MESH,
482                                     BM_EDGES_OF_MESH,
483                                     BM_FACES_OF_MESH};
484
485         const char flag_types[3] = {BM_VERT, BM_EDGE, BM_FACE};
486
487         BMIter iter;
488         BMElemF *ele;
489         int i;
490
491         for (i = 0; i < 3; i++) {
492                 if (htype & flag_types[i]) {
493                         BM_ITER(ele, &iter, bm, iter_types[i], NULL) {
494                                 BMO_elem_flag_disable(bm, ele, oflag);
495                         }
496                 }
497         }
498 }
499
500 int BMO_slot_buf_count(struct BMesh *UNUSED(bm), struct BMOperator *op, const char *slotname)
501 {
502         BMOpSlot *slot = BMO_slot_get(op, slotname);
503         
504         /* check if its actually a buffer */
505         if (!(slot->slottype > BMO_OP_SLOT_VEC))
506                 return 0;
507
508         return slot->len;
509 }
510
511 int BMO_slot_map_count(BMesh *UNUSED(bm), BMOperator *op, const char *slotname)
512 {
513         BMOpSlot *slot = BMO_slot_get(op, slotname);
514         
515         /* check if its actually a buffer */
516         if (!(slot->slottype == BMO_OP_SLOT_MAPPING))
517                 return 0;
518
519         return slot->data.ghash ? BLI_ghash_size(slot->data.ghash) : 0;
520 }
521
522 #if 0
523 void *BMO_Grow_Array(BMesh *bm, BMOperator *op, int slotcode, int totadd)
524 {
525         BMOpSlot *slot = &op->slots[slotcode];
526         void *tmp;
527         ssize_t allocsize;
528         
529         /* check if its actually a buffer */
530         if (!(slot->slottype > BMO_OP_SLOT_VEC))
531                 return NULL;
532
533         if (slot->flag & BMOS_DYNAMIC_ARRAY) {
534                 if (slot->len >= slot->size) {
535                         slot->size = (slot->size + 1 + totadd) * 2;
536
537                         allocsize = BMO_OPSLOT_TYPEINFO[opdefines[op->type]->slottypes[slotcode].type] * slot->size;
538
539                         tmp = slot->data.buf;
540                         slot->data.buf = MEM_callocN(allocsize, "opslot dynamic array");
541                         memcpy(slot->data.buf, tmp, allocsize);
542                         MEM_freeN(tmp);
543                 }
544
545                 slot->len += totadd;
546         }
547         else {
548                 slot->flag |= BMOS_DYNAMIC_ARRAY;
549                 slot->len += totadd;
550                 slot->size = slot->len + 2;
551
552                 allocsize = BMO_OPSLOT_TYPEINFO[opdefines[op->type]->slottypes[slotcode].type] * slot->len;
553
554                 tmp = slot->data.buf;
555                 slot->data.buf = MEM_callocN(allocsize, "opslot dynamic array");
556                 memcpy(slot->data.buf, tmp, allocsize);
557         }
558
559         return slot->data.buf;
560 }
561 #endif
562
563 void BMO_slot_map_to_flag(struct BMesh *bm, struct BMOperator *op,
564                           const char *slotname, const short oflag)
565 {
566         GHashIterator it;
567         BMOpSlot *slot = BMO_slot_get(op, slotname);
568         BMElemF *ele_f;
569
570         /* sanity check */
571         if (slot->slottype != BMO_OP_SLOT_MAPPING) return;
572         if (!slot->data.ghash) return;
573
574         BLI_ghashIterator_init(&it, slot->data.ghash);
575         for ( ; (ele_f = BLI_ghashIterator_getKey(&it)); BLI_ghashIterator_step(&it)) {
576                 BMO_elem_flag_enable(bm, ele_f, oflag);
577         }
578 }
579
580 static void *bmo_slot_buffer_alloc(BMOperator *op, const char *slotname, int len)
581 {
582         BMOpSlot *slot = BMO_slot_get(op, slotname);
583
584         /* check if its actually a buffer */
585         if (!(slot->slottype > BMO_OP_SLOT_VEC))
586                 return NULL;
587         
588         slot->len = len;
589         if (len)
590                 slot->data.buf = BLI_memarena_alloc(op->arena, BMO_OPSLOT_TYPEINFO[slot->slottype] * len);
591         return slot->data.buf;
592 }
593
594 /*
595  * BMO_ALL_TO_SLOT
596  *
597  * Copies all elements of a certain type into an operator slot.
598  *
599  */
600
601 static void BMO_slot_from_all(BMesh *bm, BMOperator *op, const char *slotname, const char htype)
602 {
603         BMIter elements;
604         BMHeader *e;
605         BMOpSlot *output = BMO_slot_get(op, slotname);
606         int totelement = 0, i = 0;
607         
608         if (htype & BM_VERT) totelement += bm->totvert;
609         if (htype & BM_EDGE) totelement += bm->totedge;
610         if (htype & BM_FACE) totelement += bm->totface;
611
612         if (totelement) {
613                 bmo_slot_buffer_alloc(op, slotname, totelement);
614
615                 if (htype & BM_VERT) {
616                         for (e = BM_iter_new(&elements, bm, BM_VERTS_OF_MESH, bm); e; e = BM_iter_step(&elements)) {
617                                 ((BMHeader **)output->data.p)[i] = e;
618                                 i++;
619                         }
620                 }
621
622                 if (htype & BM_EDGE) {
623                         for (e = BM_iter_new(&elements, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&elements)) {
624                                 ((BMHeader **)output->data.p)[i] = e;
625                                 i++;
626                         }
627                 }
628
629                 if (htype & BM_FACE) {
630                         for (e = BM_iter_new(&elements, bm, BM_FACES_OF_MESH, bm); e; e = BM_iter_step(&elements)) {
631                                 ((BMHeader **)output->data.p)[i] = e;
632                                 i++;
633                         }
634                 }
635         }
636 }
637
638 /*
639  * BMO_HEADERFLAG_TO_SLOT
640  *
641  * Copies elements of a certain type, which have a certain header flag set
642  * into a slot for an operator.
643  */
644
645 void BMO_slot_from_hflag(BMesh *bm, BMOperator *op, const char *slotname,
646                          const char hflag, const char htype)
647 {
648         BMIter elements;
649         BMHeader *e;
650         BMOpSlot *output = BMO_slot_get(op, slotname);
651         int totelement = 0, i = 0;
652         
653         totelement = BM_mesh_count_flag(bm, htype, hflag, 1);
654
655         if (totelement) {
656                 bmo_slot_buffer_alloc(op, slotname, totelement);
657
658                 if (htype & BM_VERT) {
659                         for (e = BM_iter_new(&elements, bm, BM_VERTS_OF_MESH, bm); e; e = BM_iter_step(&elements)) {
660                                 if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN) && BM_elem_flag_test(e, hflag)) {
661                                         ((BMHeader **)output->data.p)[i] = e;
662                                         i++;
663                                 }
664                         }
665                 }
666
667                 if (htype & BM_EDGE) {
668                         for (e = BM_iter_new(&elements, bm, BM_EDGES_OF_MESH, bm); e; e = BM_iter_step(&elements)) {
669                                 if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN) && BM_elem_flag_test(e, hflag)) {
670                                         ((BMHeader **)output->data.p)[i] = e;
671                                         i++;
672                                 }
673                         }
674                 }
675
676                 if (htype & BM_FACE) {
677                         for (e = BM_iter_new(&elements, bm, BM_FACES_OF_MESH, bm); e; e = BM_iter_step(&elements)) {
678                                 if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN) && BM_elem_flag_test(e, hflag)) {
679                                         ((BMHeader **)output->data.p)[i] = e;
680                                         i++;
681                                 }
682                         }
683                 }
684         }
685         else {
686                 output->len = 0;
687         }
688 }
689
690 /*
691  * BMO_FLAG_TO_SLOT
692  *
693  * Copies elements of a certain type, which have a certain flag set
694  * into an output slot for an operator.
695  */
696 void BMO_slot_from_flag(BMesh *bm, BMOperator *op, const char *slotname,
697                         const short oflag, const char htype)
698 {
699         BMIter elements;
700         BMHeader *ele;
701         BMOpSlot *output = BMO_slot_get(op, slotname);
702         int totelement = BMO_mesh_flag_count(bm, oflag, htype), i = 0;
703
704         if (totelement) {
705                 bmo_slot_buffer_alloc(op, slotname, totelement);
706
707                 if (htype & BM_VERT) {
708                         for (ele = BM_iter_new(&elements, bm, BM_VERTS_OF_MESH, bm); ele; ele = BM_iter_step(&elements)) {
709                                 if (BMO_elem_flag_test(bm, (BMElemF *)ele, oflag)) {
710                                         ((BMHeader **)output->data.p)[i] = ele;
711                                         i++;
712                                 }
713                         }
714                 }
715
716                 if (htype & BM_EDGE) {
717                         for (ele = BM_iter_new(&elements, bm, BM_EDGES_OF_MESH, bm); ele; ele = BM_iter_step(&elements)) {
718                                 if (BMO_elem_flag_test(bm, (BMElemF *)ele, oflag)) {
719                                         ((BMHeader **)output->data.p)[i] = ele;
720                                         i++;
721                                 }
722                         }
723                 }
724
725                 if (htype & BM_FACE) {
726                         for (ele = BM_iter_new(&elements, bm, BM_FACES_OF_MESH, bm); ele; ele = BM_iter_step(&elements)) {
727                                 if (BMO_elem_flag_test(bm, (BMElemF *)ele, oflag)) {
728                                         ((BMHeader **)output->data.p)[i] = ele;
729                                         i++;
730                                 }
731                         }
732                 }
733         }
734         else {
735                 output->len = 0;
736         }
737 }
738
739 /*
740  *
741  * BMO_FLAG_BUFFER
742  *
743  * Header Flags elements in a slots buffer, automatically
744  * using the selection API where appropriate.
745  */
746 void BMO_slot_buffer_hflag_enable(BMesh *bm, BMOperator *op, const char *slotname,
747                                   const char hflag, const char htype)
748 {
749         BMOpSlot *slot = BMO_slot_get(op, slotname);
750         BMHeader **data =  slot->data.p;
751         int i;
752         
753         for (i = 0; i < slot->len; i++) {
754                 if (!(htype & data[i]->htype))
755                         continue;
756
757                 if (hflag & BM_ELEM_SELECT) {
758                         BM_elem_select_set(bm, data[i], TRUE);
759                 }
760                 BM_elem_flag_enable(data[i], hflag);
761         }
762 }
763
764 /*
765  *
766  * BMO_FLAG_BUFFER
767  *
768  * Removes flags from elements in a slots buffer, automatically
769  * using the selection API where appropriate.
770  */
771 void BMO_slot_buffer_hflag_disable(BMesh *bm, BMOperator *op, const char *slotname,
772                                    const char hflag, const char htype)
773 {
774         BMOpSlot *slot = BMO_slot_get(op, slotname);
775         BMHeader **data =  slot->data.p;
776         int i;
777         
778         for (i = 0; i < slot->len; i++) {
779                 if (!(htype & data[i]->htype))
780                         continue;
781
782                 if (hflag & BM_ELEM_SELECT) {
783                         BM_elem_select_set(bm, data[i], FALSE);
784                 }
785
786                 BM_elem_flag_disable(data[i], hflag);
787         }
788 }
789 int BMO_vert_edge_flags_count(BMesh *bm, BMVert *v, const short oflag)
790 {
791         int count = 0;
792
793         if (v->e) {
794                 BMEdge *curedge;
795                 const int len = bmesh_disk_count(v);
796                 int i;
797                 
798                 for (i = 0, curedge = v->e; i < len; i++) {
799                         if (BMO_elem_flag_test(bm, curedge, oflag))
800                                 count++;
801                         curedge = bmesh_disk_nextedge(curedge, v);
802                 }
803         }
804
805         return count;
806 }
807
808 /*
809  *
810  * BMO_FLAG_BUFFER
811  *
812  * Flags elements in a slots buffer
813  */
814 void BMO_slot_buffer_flag_enable(BMesh *bm, BMOperator *op, const char *slotname,
815                           const short oflag, const char htype)
816 {
817         BMOpSlot *slot = BMO_slot_get(op, slotname);
818         BMHeader **data =  slot->data.p;
819         int i;
820         
821         for (i = 0; i < slot->len; i++) {
822                 if (!(htype & data[i]->htype))
823                         continue;
824
825                 BMO_elem_flag_enable(bm, (BMElemF *)data[i], oflag);
826         }
827 }
828
829 /*
830  *
831  * BMO_FLAG_BUFFER
832  *
833  * Removes flags from elements in a slots buffer
834  */
835 void BMO_slot_buffer_flag_disable(BMesh *bm, BMOperator *op, const char *slotname,
836                                 const short oflag, const char htype)
837 {
838         BMOpSlot *slot = BMO_slot_get(op, slotname);
839         BMHeader **data =  slot->data.p;
840         int i;
841         
842         for (i = 0; i < slot->len; i++) {
843                 if (!(htype & data[i]->htype))
844                         continue;
845
846                 BMO_elem_flag_disable(bm, (BMElemF *)data[i], oflag);
847         }
848 }
849
850
851 /*
852  *
853  *      ALLOC/FREE FLAG LAYER
854  *
855  *  Used by operator stack to free/allocate
856  *  private flag data. This is allocated
857  *  using a mempool so the allocation/frees
858  *  should be quite fast.
859  *
860  *  BMESH_TODO:
861  *      Investigate not freeing flag layers until
862  *  all operators have been executed. This would
863  *  save a lot of realloc potentially.
864  */
865 static void bmo_flag_layer_alloc(BMesh *bm)
866 {
867         BMElemF *ele;
868         /* set the index values since we are looping over all data anyway,
869          * may save time later on */
870         int i;
871
872         BMIter iter;
873         BLI_mempool *oldpool = bm->toolflagpool;                /* old flag pool */
874         BLI_mempool *newpool;
875         void *oldflags;
876
877         /* store memcpy size for reuse */
878         const size_t old_totflags_size = (bm->totflags * sizeof(BMFlagLayer));
879         
880         bm->totflags++;
881
882         /* allocate new flag poo */
883         bm->toolflagpool = newpool = BLI_mempool_create(sizeof(BMFlagLayer)*bm->totflags, 512, 512, FALSE, FALSE);
884         
885         /* now go through and memcpy all the flags. Loops don't get a flag layer at this time.. */
886         for (ele = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) {
887                 oldflags = ele->oflags;
888                 ele->oflags = BLI_mempool_calloc(newpool);
889                 memcpy(ele->oflags, oldflags, old_totflags_size);
890                 BM_elem_index_set(ele, i); /* set_inline */
891         }
892         for (ele = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) {
893                 oldflags = ele->oflags;
894                 ele->oflags = BLI_mempool_calloc(newpool);
895                 memcpy(ele->oflags, oldflags, old_totflags_size);
896                 BM_elem_index_set(ele, i); /* set_inline */
897         }
898         for (ele = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) {
899                 oldflags = ele->oflags;
900                 ele->oflags = BLI_mempool_calloc(newpool);
901                 memcpy(ele->oflags, oldflags, old_totflags_size);
902                 BM_elem_index_set(ele, i); /* set_inline */
903         }
904
905         bm->elem_index_dirty &= ~(BM_VERT|BM_EDGE|BM_FACE);
906
907         BLI_mempool_destroy(oldpool);
908 }
909
910 static void bmo_flag_layer_free(BMesh *bm)
911 {
912         BMElemF *ele;
913         /* set the index values since we are looping over all data anyway,
914          * may save time later on */
915         int i;
916
917         BMIter iter;
918         BLI_mempool *oldpool = bm->toolflagpool;
919         BLI_mempool *newpool;
920         void *oldflags;
921         
922         /* store memcpy size for reuse */
923         const size_t new_totflags_size = ((bm->totflags - 1) * sizeof(BMFlagLayer));
924
925         /* de-increment the totflags first.. */
926         bm->totflags--;
927         /* allocate new flag poo */
928         bm->toolflagpool = newpool = BLI_mempool_create(new_totflags_size, 512, 512, TRUE, FALSE);
929         
930         /* now go through and memcpy all the flag */
931         for (ele = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) {
932                 oldflags = ele->oflags;
933                 ele->oflags = BLI_mempool_calloc(newpool);
934                 memcpy(ele->oflags, oldflags, new_totflags_size);
935                 BM_elem_index_set(ele, i); /* set_inline */
936         }
937         for (ele = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) {
938                 oldflags = ele->oflags;
939                 ele->oflags = BLI_mempool_calloc(newpool);
940                 memcpy(ele->oflags, oldflags, new_totflags_size);
941                 BM_elem_index_set(ele, i); /* set_inline */
942         }
943         for (ele = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) {
944                 oldflags = ele->oflags;
945                 ele->oflags = BLI_mempool_calloc(newpool);
946                 memcpy(ele->oflags, oldflags, new_totflags_size);
947                 BM_elem_index_set(ele, i); /* set_inline */
948         }
949
950         bm->elem_index_dirty &= ~(BM_VERT|BM_EDGE|BM_FACE);
951
952         BLI_mempool_destroy(oldpool);
953 }
954
955 static void bmo_flag_layer_clear(BMesh *bm)
956 {
957         BMElemF *ele;
958         /* set the index values since we are looping over all data anyway,
959          * may save time later on */
960         int i;
961
962         BMIter iter;
963         const int totflags_offset = bm->totflags - 1;
964
965         /* now go through and memcpy all the flag */
966         for (ele = BM_iter_new(&iter, bm, BM_VERTS_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) {
967                 memset(ele->oflags + totflags_offset, 0, sizeof(BMFlagLayer));
968                 BM_elem_index_set(ele, i); /* set_inline */
969         }
970         for (ele = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) {
971                 memset(ele->oflags + totflags_offset, 0, sizeof(BMFlagLayer));
972                 BM_elem_index_set(ele, i); /* set_inline */
973         }
974         for (ele = BM_iter_new(&iter, bm, BM_FACES_OF_MESH, bm), i = 0; ele; ele = BM_iter_step(&iter), i++) {
975                 memset(ele->oflags + totflags_offset, 0, sizeof(BMFlagLayer));
976                 BM_elem_index_set(ele, i); /* set_inline */
977         }
978
979         bm->elem_index_dirty &= ~(BM_VERT|BM_EDGE|BM_FACE);
980 }
981
982 void *BMO_slot_elem_first(BMOperator *op, const char *slotname)
983 {
984         BMOpSlot *slot = BMO_slot_get(op, slotname);
985         
986         if (slot->slottype != BMO_OP_SLOT_ELEMENT_BUF)
987                 return NULL;
988
989         return slot->data.buf ? *(void **)slot->data.buf : NULL;
990 }
991
992 void *BMO_iter_new(BMOIter *iter, BMesh *UNUSED(bm), BMOperator *op,
993                    const char *slotname, const char restrictmask)
994 {
995         BMOpSlot *slot = BMO_slot_get(op, slotname);
996
997         memset(iter, 0, sizeof(BMOIter));
998
999         iter->slot = slot;
1000         iter->cur = 0;
1001         iter->restrictmask = restrictmask;
1002
1003         if (iter->slot->slottype == BMO_OP_SLOT_MAPPING) {
1004                 if (iter->slot->data.ghash) {
1005                         BLI_ghashIterator_init(&iter->giter, slot->data.ghash);
1006                 }
1007                 else {
1008                         return NULL;
1009                 }
1010         }
1011
1012         return BMO_iter_step(iter);
1013 }
1014
1015 void *BMO_iter_step(BMOIter *iter)
1016 {
1017         if (iter->slot->slottype == BMO_OP_SLOT_ELEMENT_BUF) {
1018                 BMHeader *h;
1019
1020                 if (iter->cur >= iter->slot->len) {
1021                         return NULL;
1022                 }
1023
1024                 h = ((void **)iter->slot->data.buf)[iter->cur++];
1025                 while (!(iter->restrictmask & h->htype)) {
1026                         if (iter->cur >= iter->slot->len) {
1027                                 return NULL;
1028                         }
1029
1030                         h = ((void **)iter->slot->data.buf)[iter->cur++];
1031                 }
1032
1033                 return h;
1034         }
1035         else if (iter->slot->slottype == BMO_OP_SLOT_MAPPING) {
1036                 struct BMOElemMapping *map;
1037                 void *ret = BLI_ghashIterator_getKey(&iter->giter);
1038                 map = BLI_ghashIterator_getValue(&iter->giter);
1039                 
1040                 iter->val = map + 1;
1041
1042                 BLI_ghashIterator_step(&iter->giter);
1043
1044                 return ret;
1045         }
1046
1047         return NULL;
1048 }
1049
1050 /* used for iterating over mapping */
1051 void *BMO_iter_map_value(BMOIter *iter)
1052 {
1053         return iter->val;
1054 }
1055
1056 void *BMO_iter_map_value_p(BMOIter *iter)
1057 {
1058         return *((void **)iter->val);
1059 }
1060
1061 float BMO_iter_map_value_f(BMOIter *iter)
1062 {
1063         return *((float *)iter->val);
1064 }
1065
1066 /* error syste */
1067 typedef struct BMOpError {
1068         struct BMOpError *next, *prev;
1069         int errorcode;
1070         BMOperator *op;
1071         const char *msg;
1072 } BMOpError;
1073
1074 void BMO_error_clear(BMesh *bm)
1075 {
1076         while (BMO_error_pop(bm, NULL, NULL));
1077 }
1078
1079 void BMO_error_raise(BMesh *bm, BMOperator *owner, int errcode, const char *msg)
1080 {
1081         BMOpError *err = MEM_callocN(sizeof(BMOpError), "bmop_error");
1082         
1083         err->errorcode = errcode;
1084         if (!msg) msg = bmo_error_messages[errcode];
1085         err->msg = msg;
1086         err->op = owner;
1087         
1088         BLI_addhead(&bm->errorstack, err);
1089 }
1090
1091 int BMO_error_occurred(BMesh *bm)
1092 {
1093         return bm->errorstack.first != NULL;
1094 }
1095
1096 /* returns error code or 0 if no erro */
1097 int BMO_error_get(BMesh *bm, const char **msg, BMOperator **op)
1098 {
1099         BMOpError *err = bm->errorstack.first;
1100         if (!err) {
1101                 return 0;
1102         }
1103
1104         if (msg) *msg = err->msg;
1105         if (op) *op = err->op;
1106         
1107         return err->errorcode;
1108 }
1109
1110 int BMO_error_pop(BMesh *bm, const char **msg, BMOperator **op)
1111 {
1112         int errorcode = BMO_error_get(bm, msg, op);
1113         
1114         if (errorcode) {
1115                 BMOpError *err = bm->errorstack.first;
1116                 
1117                 BLI_remlink(&bm->errorstack, bm->errorstack.first);
1118                 MEM_freeN(err);
1119         }
1120
1121         return errorcode;
1122 }
1123
1124
1125 #define NEXT_CHAR(fmt) ((fmt)[0] != 0 ? (fmt)[1] : 0)
1126
1127 static int bmesh_name_to_slotcode(BMOpDefine *def, const char *name)
1128 {
1129         int i;
1130
1131         for (i = 0; def->slottypes[i].type; i++) {
1132                 if (!strncmp(name, def->slottypes[i].name, MAX_SLOTNAME)) {
1133                         return i;
1134                 }
1135         }
1136
1137         return -1;
1138 }
1139
1140 static int bmesh_name_to_slotcode_check(BMOpDefine *def, const char *name)
1141 {
1142         int i = bmesh_name_to_slotcode(def, name);
1143         if (i < 0) {
1144                 fprintf(stderr, "%s: ! could not find bmesh slot for name %s! (bmesh internal error)\n", __func__, name);
1145         }
1146
1147         return i;
1148 }
1149
1150 static int bmesh_opname_to_opcode(const char *opname)
1151 {
1152         int i;
1153
1154         for (i = 0; i < bmesh_total_ops; i++) {
1155                 if (!strcmp(opname, opdefines[i]->name)) {
1156                         return i;
1157                 }
1158         }
1159
1160         fprintf(stderr, "%s: ! could not find bmesh slot for name %s! (bmesh internal error)\n", __func__, opname);
1161         return -1;
1162 }
1163
1164 /* Example:
1165  * BMO_op_callf(bm, "del %i %hv", DEL_ONLYFACES, BM_ELEM_SELECT);
1166  *
1167  *  i - int
1168  *  b - boolean (same as int but 1/0 only)
1169  *  f - float
1170  *  hv - header flagged verts (hflag)
1171  *  he - header flagged edges (hflag)
1172  *  hf - header flagged faces (hflag)
1173  *  fv - flagged verts (oflag)
1174  *  fe - flagged edges (oflag)
1175  *  ff - flagged faces (oflag)
1176  */
1177
1178 int BMO_op_vinitf(BMesh *bm, BMOperator *op, const char *_fmt, va_list vlist)
1179 {
1180         BMOpDefine *def;
1181         char *opname, *ofmt, *fmt;
1182         char slotname[64] = {0};
1183         int i /*, n = strlen(fmt) */, stop /*, slotcode = -1 */, ret, type, state;
1184         int noslot = 0;
1185
1186
1187         /* basic useful info to help find where bmop formatting strings fail */
1188         int lineno = -1;
1189 #   define GOTO_ERROR { lineno = __LINE__; goto error; }
1190
1191
1192         /* we muck around in here, so dup i */
1193         fmt = ofmt = BLI_strdup(_fmt);
1194         
1195         /* find operator nam */
1196         i = strcspn(fmt, " \t");
1197
1198         opname = fmt;
1199         if (!opname[i]) noslot = 1;
1200         opname[i] = '\0';
1201
1202         fmt += i + (noslot ? 0 : 1);
1203         
1204         i = bmesh_opname_to_opcode(opname);
1205
1206         if (i == -1) {
1207                 MEM_freeN(ofmt);
1208                 return FALSE;
1209         }
1210
1211         BMO_op_init(bm, op, opname);
1212         def = opdefines[i];
1213         
1214         i = 0;
1215         state = 1; /* 0: not inside slotcode name, 1: inside slotcode name */
1216
1217         while (*fmt) {
1218                 if (state) {
1219                         /* jump past leading whitespac */
1220                         i = strspn(fmt, " \t");
1221                         fmt += i;
1222                         
1223                         /* ignore trailing whitespac */
1224                         if (!fmt[i])
1225                                 break;
1226
1227                         /* find end of slot name.  currently this is
1228                          * a little flexible, allowing "slot=%f",
1229                          * "slot %f", "slot%f", and "slot\t%f". */
1230                         i = strcspn(fmt, "= \t%");
1231                         if (!fmt[i]) GOTO_ERROR;
1232
1233                         fmt[i] = 0;
1234
1235                         if (bmesh_name_to_slotcode_check(def, fmt) < 0) GOTO_ERROR;
1236                         
1237                         BLI_strncpy(slotname, fmt, sizeof(slotname));
1238                         
1239                         state = 0;
1240                         fmt += i;
1241                 }
1242                 else {
1243                         switch (*fmt) {
1244                                 case ' ':
1245                                 case '\t':
1246                                 case '=':
1247                                 case '%':
1248                                         break;
1249                                 case 'm': {
1250                                         int size, c;
1251
1252                                         c = NEXT_CHAR(fmt);
1253                                         fmt++;
1254
1255                                         if (c == '3') size = 3;
1256                                         else if (c == '4') size = 4;
1257                                         else GOTO_ERROR;
1258
1259                                         BMO_slot_mat_set(op, slotname, va_arg(vlist, void *), size);
1260                                         state = 1;
1261                                         break;
1262                                 }
1263                                 case 'v': {
1264                                         BMO_slot_vec_set(op, slotname, va_arg(vlist, float *));
1265                                         state = 1;
1266                                         break;
1267                                 }
1268                                 case 'e': {
1269                                         BMHeader *ele = va_arg(vlist, void *);
1270                                         BMOpSlot *slot = BMO_slot_get(op, slotname);
1271
1272                                         slot->data.buf = BLI_memarena_alloc(op->arena, sizeof(void *) * 4);
1273                                         slot->len = 1;
1274                                         *((void **)slot->data.buf) = ele;
1275
1276                                         state = 1;
1277                                         break;
1278                                 }
1279                                 case 's': {
1280                                         BMOperator *op2 = va_arg(vlist, void *);
1281                                         const char *slotname2 = va_arg(vlist, char *);
1282
1283                                         BMO_slot_copy(op2, op, slotname2, slotname);
1284                                         state = 1;
1285                                         break;
1286                                 }
1287                                 case 'i':
1288                                         BMO_slot_int_set(op, slotname, va_arg(vlist, int));
1289                                         state = 1;
1290                                         break;
1291                                 case 'b':
1292                                         BMO_slot_bool_set(op, slotname, va_arg(vlist, int));
1293                                         state = 1;
1294                                         break;
1295                                 case 'p':
1296                                         BMO_slot_ptr_set(op, slotname, va_arg(vlist, void *));
1297                                         state = 1;
1298                                         break;
1299                                 case 'f':
1300                                 case 'h':
1301                                 case 'a':
1302                                         type = *fmt;
1303
1304                                         if (NEXT_CHAR(fmt) == ' ' || NEXT_CHAR(fmt) == '\t' || NEXT_CHAR(fmt) == '\0') {
1305                                                 BMO_slot_float_set(op, slotname, va_arg(vlist, double));
1306                                         }
1307                                         else {
1308                                                 ret = 0;
1309                                                 stop = 0;
1310                                                 while (1) {
1311                                                         switch (NEXT_CHAR(fmt)) {
1312                                                                 case 'f': ret |= BM_FACE; break;
1313                                                                 case 'e': ret |= BM_EDGE; break;
1314                                                                 case 'v': ret |= BM_VERT; break;
1315                                                                 default:
1316                                                                         stop = 1;
1317                                                                         break;
1318                                                         }
1319                                                         if (stop) {
1320                                                                 break;
1321                                                         }
1322
1323                                                         fmt++;
1324                                                 }
1325
1326                                                 if (type == 'h') {
1327                                                         BMO_slot_from_hflag(bm, op, slotname, va_arg(vlist, int), ret);
1328                                                 }
1329                                                 else if (type == 'a') {
1330                                                         BMO_slot_from_all(bm, op, slotname, ret);
1331                                                 }
1332                                                 else {
1333                                                         BMO_slot_from_flag(bm, op, slotname, va_arg(vlist, int), ret);
1334                                                 }
1335                                         }
1336
1337                                         state = 1;
1338                                         break;
1339                                 default:
1340                                         fprintf(stderr,
1341                                                 "%s: unrecognized bmop format char: %c, %d in '%s'\n",
1342                                                 __func__, *fmt, (int)(fmt - ofmt), ofmt);
1343                                         break;
1344                         }
1345                 }
1346                 fmt++;
1347         }
1348
1349         MEM_freeN(ofmt);
1350         return TRUE;
1351 error:
1352
1353         /* non urgent todo - explain exactly what is failing */
1354         fprintf(stderr,
1355                 "%s: error parsing formatting string, %d in '%s'\n    see - %s:%d\n",
1356                 __func__, (int)(fmt - ofmt), _fmt, __FILE__, lineno);
1357         MEM_freeN(ofmt);
1358
1359         BMO_op_finish(bm, op);
1360         return FALSE;
1361
1362 #undef GOTO_ERROR
1363
1364 }
1365
1366
1367 int BMO_op_initf(BMesh *bm, BMOperator *op, const char *fmt, ...)
1368 {
1369         va_list list;
1370
1371         va_start(list, fmt);
1372         if (!BMO_op_vinitf(bm, op, fmt, list)) {
1373                 printf("%s: failed\n", __func__);
1374                 va_end(list);
1375                 return FALSE;
1376         }
1377         va_end(list);
1378
1379         return TRUE;
1380 }
1381
1382 int BMO_op_callf(BMesh *bm, const char *fmt, ...)
1383 {
1384         va_list list;
1385         BMOperator op;
1386
1387         va_start(list, fmt);
1388         if (!BMO_op_vinitf(bm, &op, fmt, list)) {
1389                 printf("%s: failed, format is:\n    \"%s\"\n", __func__, fmt);
1390                 va_end(list);
1391                 return FALSE;
1392         }
1393
1394         BMO_op_exec(bm, &op);
1395         BMO_op_finish(bm, &op);
1396
1397         va_end(list);
1398         return TRUE;
1399 }