Cleanup: remove redundant doxygen \file argument
[blender.git] / source / blender / bmesh / operators / bmo_dupe.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16
17 /** \file \ingroup bmesh
18  *
19  * Duplicate, Split, Split operators.
20  */
21
22 #include "MEM_guardedalloc.h"
23
24 #include "BLI_math.h"
25 #include "BLI_alloca.h"
26
27 #include "bmesh.h"
28
29 #include "intern/bmesh_operators_private.h" /* own include */
30
31 /* local flag define */
32 #define DUPE_INPUT      1 /* input from operator */
33 #define DUPE_NEW        2
34 #define DUPE_DONE       4
35 // #define DUPE_MAPPED     8 // UNUSED
36
37 /**
38  * COPY VERTEX
39  *
40  * Copy an existing vertex from one bmesh to another.
41  */
42 static BMVert *bmo_vert_copy(
43         BMOperator *op,
44         BMOpSlot *slot_vertmap_out,
45         BMesh *bm_dst, BMesh *bm_src, BMVert *v_src, GHash *vhash)
46 {
47         BMVert *v_dst;
48
49         /* Create a new vertex */
50         v_dst = BM_vert_create(bm_dst, v_src->co, NULL, BM_CREATE_SKIP_CD);
51         BMO_slot_map_elem_insert(op, slot_vertmap_out, v_src, v_dst);
52         BMO_slot_map_elem_insert(op, slot_vertmap_out, v_dst, v_src);
53
54         /* Insert new vertex into the vert hash */
55         BLI_ghash_insert(vhash, v_src, v_dst);
56
57         /* Copy attributes */
58         BM_elem_attrs_copy(bm_src, bm_dst, v_src, v_dst);
59
60         /* Mark the vert for output */
61         BMO_vert_flag_enable(bm_dst, v_dst, DUPE_NEW);
62
63         return v_dst;
64 }
65
66 /**
67  * COPY EDGE
68  *
69  * Copy an existing edge from one bmesh to another.
70  */
71 static BMEdge *bmo_edge_copy(
72         BMOperator *op,
73         BMOpSlot *slot_edgemap_out,
74         BMOpSlot *slot_boundarymap_out,
75         BMesh *bm_dst, BMesh *bm_src,
76         BMEdge *e_src,
77         GHash *vhash, GHash *ehash)
78 {
79         BMEdge *e_dst;
80         BMVert *e_dst_v1, *e_dst_v2;
81         uint rlen;
82
83         /* see if any of the neighboring faces are
84          * not being duplicated.  in that case,
85          * add it to the new/old map. */
86         /* lookup edge */
87         rlen = 0;
88         if (e_src->l) {
89                 BMLoop *l_iter_src, *l_first_src;
90                 l_iter_src = l_first_src = e_src->l;
91                 do {
92                         if (BMO_face_flag_test(bm_src, l_iter_src->f, DUPE_INPUT)) {
93                                 rlen++;
94                         }
95                 } while ((l_iter_src = l_iter_src->radial_next) != l_first_src);
96         }
97
98         /* Lookup v1 and v2 */
99         e_dst_v1 = BLI_ghash_lookup(vhash, e_src->v1);
100         e_dst_v2 = BLI_ghash_lookup(vhash, e_src->v2);
101
102         /* Create a new edge */
103         e_dst = BM_edge_create(bm_dst, e_dst_v1, e_dst_v2, NULL, BM_CREATE_SKIP_CD);
104         BMO_slot_map_elem_insert(op, slot_edgemap_out, e_src, e_dst);
105         BMO_slot_map_elem_insert(op, slot_edgemap_out, e_dst, e_src);
106
107         /* add to new/old edge map if necassary */
108         if (rlen < 2) {
109                 /* not sure what non-manifold cases of greater than three
110                  * radial should do. */
111                 BMO_slot_map_elem_insert(op, slot_boundarymap_out, e_src, e_dst);
112         }
113
114         /* Insert new edge into the edge hash */
115         BLI_ghash_insert(ehash, e_src, e_dst);
116
117         /* Copy attributes */
118         BM_elem_attrs_copy(bm_src, bm_dst, e_src, e_dst);
119
120         /* Mark the edge for output */
121         BMO_edge_flag_enable(bm_dst, e_dst, DUPE_NEW);
122
123         return e_dst;
124 }
125
126 /**
127  * COPY FACE
128  *
129  * Copy an existing face from one bmesh to another.
130  */
131 static BMFace *bmo_face_copy(
132         BMOperator *op,
133         BMOpSlot *slot_facemap_out,
134         BMesh *bm_dst, BMesh *bm_src,
135         BMFace *f_src,
136         GHash *vhash, GHash *ehash)
137 {
138         BMFace *f_dst;
139         BMVert **vtar = BLI_array_alloca(vtar, f_src->len);
140         BMEdge **edar = BLI_array_alloca(edar, f_src->len);
141         BMLoop *l_iter_src, *l_iter_dst, *l_first_src;
142         int i;
143
144         l_first_src = BM_FACE_FIRST_LOOP(f_src);
145
146         /* lookup edge */
147         l_iter_src = l_first_src;
148         i = 0;
149         do {
150                 vtar[i] = BLI_ghash_lookup(vhash, l_iter_src->v);
151                 edar[i] = BLI_ghash_lookup(ehash, l_iter_src->e);
152                 i++;
153         } while ((l_iter_src = l_iter_src->next) != l_first_src);
154
155         /* create new face */
156         f_dst = BM_face_create(bm_dst, vtar, edar, f_src->len, NULL, BM_CREATE_SKIP_CD);
157         BMO_slot_map_elem_insert(op, slot_facemap_out, f_src, f_dst);
158         BMO_slot_map_elem_insert(op, slot_facemap_out, f_dst, f_src);
159
160         /* Copy attributes */
161         BM_elem_attrs_copy(bm_src, bm_dst, f_src, f_dst);
162
163         /* copy per-loop custom data */
164         l_iter_src = l_first_src;
165         l_iter_dst = BM_FACE_FIRST_LOOP(f_dst);
166         do {
167                 BM_elem_attrs_copy(bm_src, bm_dst, l_iter_src, l_iter_dst);
168         } while ((void)
169                  (l_iter_dst = l_iter_dst->next),
170                  (l_iter_src = l_iter_src->next) != l_first_src);
171
172         /* Mark the face for output */
173         BMO_face_flag_enable(bm_dst, f_dst, DUPE_NEW);
174
175         return f_dst;
176 }
177
178 /**
179  * COPY MESH
180  *
181  * Internal Copy function.
182  */
183 static void bmo_mesh_copy(BMOperator *op, BMesh *bm_dst, BMesh *bm_src)
184 {
185         const bool use_select_history = BMO_slot_bool_get(op->slots_in, "use_select_history");
186
187         BMVert *v = NULL, *v2;
188         BMEdge *e = NULL;
189         BMFace *f = NULL;
190
191         BMIter viter, eiter, fiter;
192         GHash *vhash, *ehash;
193
194         BMOpSlot *slot_boundary_map_out = BMO_slot_get(op->slots_out, "boundary_map.out");
195         BMOpSlot *slot_isovert_map_out  = BMO_slot_get(op->slots_out, "isovert_map.out");
196
197         BMOpSlot *slot_vert_map_out = BMO_slot_get(op->slots_out, "vert_map.out");
198         BMOpSlot *slot_edge_map_out = BMO_slot_get(op->slots_out, "edge_map.out");
199         BMOpSlot *slot_face_map_out = BMO_slot_get(op->slots_out, "face_map.out");
200
201         /* initialize pointer hashes */
202         vhash = BLI_ghash_ptr_new("bmesh dupeops v");
203         ehash = BLI_ghash_ptr_new("bmesh dupeops e");
204
205         /* duplicate flagged vertices */
206         BM_ITER_MESH (v, &viter, bm_src, BM_VERTS_OF_MESH) {
207                 if (BMO_vert_flag_test(bm_src, v, DUPE_INPUT) &&
208                     BMO_vert_flag_test(bm_src, v, DUPE_DONE) == false)
209                 {
210                         BMIter iter;
211                         bool isolated = true;
212
213                         v2 = bmo_vert_copy(op, slot_vert_map_out, bm_dst, bm_src, v, vhash);
214
215                         BM_ITER_ELEM (f, &iter, v, BM_FACES_OF_VERT) {
216                                 if (BMO_face_flag_test(bm_src, f, DUPE_INPUT)) {
217                                         isolated = false;
218                                         break;
219                                 }
220                         }
221
222                         if (isolated) {
223                                 BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) {
224                                         if (BMO_edge_flag_test(bm_src, e, DUPE_INPUT)) {
225                                                 isolated = false;
226                                                 break;
227                                         }
228                                 }
229                         }
230
231                         if (isolated) {
232                                 BMO_slot_map_elem_insert(op, slot_isovert_map_out, v, v2);
233                         }
234
235                         BMO_vert_flag_enable(bm_src, v, DUPE_DONE);
236                 }
237         }
238
239         /* now we dupe all the edges */
240         BM_ITER_MESH (e, &eiter, bm_src, BM_EDGES_OF_MESH) {
241                 if (BMO_edge_flag_test(bm_src, e, DUPE_INPUT) &&
242                     BMO_edge_flag_test(bm_src, e, DUPE_DONE) == false)
243                 {
244                         /* make sure that verts are copied */
245                         if (!BMO_vert_flag_test(bm_src, e->v1, DUPE_DONE)) {
246                                 bmo_vert_copy(op, slot_vert_map_out, bm_dst, bm_src, e->v1, vhash);
247                                 BMO_vert_flag_enable(bm_src, e->v1, DUPE_DONE);
248                         }
249                         if (!BMO_vert_flag_test(bm_src, e->v2, DUPE_DONE)) {
250                                 bmo_vert_copy(op, slot_vert_map_out, bm_dst, bm_src, e->v2, vhash);
251                                 BMO_vert_flag_enable(bm_src, e->v2, DUPE_DONE);
252                         }
253                         /* now copy the actual edge */
254                         bmo_edge_copy(op, slot_edge_map_out, slot_boundary_map_out,
255                                       bm_dst, bm_src, e, vhash, ehash);
256                         BMO_edge_flag_enable(bm_src, e, DUPE_DONE);
257                 }
258         }
259
260         /* first we dupe all flagged faces and their elements from source */
261         BM_ITER_MESH (f, &fiter, bm_src, BM_FACES_OF_MESH) {
262                 if (BMO_face_flag_test(bm_src, f, DUPE_INPUT)) {
263                         /* vertex pass */
264                         BM_ITER_ELEM (v, &viter, f, BM_VERTS_OF_FACE) {
265                                 if (!BMO_vert_flag_test(bm_src, v, DUPE_DONE)) {
266                                         bmo_vert_copy(op, slot_vert_map_out, bm_dst, bm_src, v, vhash);
267                                         BMO_vert_flag_enable(bm_src, v, DUPE_DONE);
268                                 }
269                         }
270
271                         /* edge pass */
272                         BM_ITER_ELEM (e, &eiter, f, BM_EDGES_OF_FACE) {
273                                 if (!BMO_edge_flag_test(bm_src, e, DUPE_DONE)) {
274                                         bmo_edge_copy(op, slot_edge_map_out, slot_boundary_map_out,
275                                                       bm_dst, bm_src, e, vhash, ehash);
276                                         BMO_edge_flag_enable(bm_src, e, DUPE_DONE);
277                                 }
278                         }
279
280                         bmo_face_copy(op, slot_face_map_out, bm_dst, bm_src, f, vhash, ehash);
281                         BMO_face_flag_enable(bm_src, f, DUPE_DONE);
282                 }
283         }
284
285         /* free pointer hashes */
286         BLI_ghash_free(vhash, NULL, NULL);
287         BLI_ghash_free(ehash, NULL, NULL);
288
289         if (use_select_history) {
290                 BLI_assert(bm_src == bm_dst);
291                 BMO_mesh_selected_remap(
292                         bm_dst,
293                         slot_vert_map_out,
294                         slot_edge_map_out,
295                         slot_face_map_out,
296                         false);
297         }
298 }
299
300 /**
301  * Duplicate Operator
302  *
303  * Duplicates verts, edges and faces of a mesh.
304  *
305  * INPUT SLOTS:
306  *
307  * BMOP_DUPE_VINPUT: Buffer containing pointers to mesh vertices to be duplicated
308  * BMOP_DUPE_EINPUT: Buffer containing pointers to mesh edges to be duplicated
309  * BMOP_DUPE_FINPUT: Buffer containing pointers to mesh faces to be duplicated
310  *
311  * OUTPUT SLOTS:
312  *
313  * BMOP_DUPE_VORIGINAL: Buffer containing pointers to the original mesh vertices
314  * BMOP_DUPE_EORIGINAL: Buffer containing pointers to the original mesh edges
315  * BMOP_DUPE_FORIGINAL: Buffer containing pointers to the original mesh faces
316  * BMOP_DUPE_VNEW: Buffer containing pointers to the new mesh vertices
317  * BMOP_DUPE_ENEW: Buffer containing pointers to the new mesh edges
318  * BMOP_DUPE_FNEW: Buffer containing pointers to the new mesh faces
319  */
320 void bmo_duplicate_exec(BMesh *bm, BMOperator *op)
321 {
322         BMOperator *dupeop = op;
323         BMesh *bm_dst = BMO_slot_ptr_get(op->slots_in, "dest");
324
325         if (!bm_dst)
326                 bm_dst = bm;
327
328         /* flag input */
329         BMO_slot_buffer_flag_enable(bm, dupeop->slots_in, "geom", BM_ALL_NOLOOP, DUPE_INPUT);
330
331         /* use the internal copy function */
332         bmo_mesh_copy(dupeop, bm_dst, bm);
333
334         /* Output */
335         /* First copy the input buffers to output buffers - original data */
336         BMO_slot_copy(dupeop, slots_in,  "geom",
337                       dupeop, slots_out, "geom_orig.out");
338
339         /* Now alloc the new output buffers */
340         BMO_slot_buffer_from_enabled_flag(bm, dupeop, dupeop->slots_out, "geom.out", BM_ALL_NOLOOP, DUPE_NEW);
341 }
342
343 #if 0 /* UNUSED */
344 /* executes the duplicate operation, feeding elements of
345  * type flag etypeflag and header flag flag to it.  note,
346  * to get more useful information (such as the mapping from
347  * original to new elements) you should run the dupe op manually */
348 void BMO_dupe_from_flag(BMesh *bm, int htype, const char hflag)
349 {
350         BMOperator dupeop;
351
352         BMO_op_init(bm, &dupeop, "duplicate");
353         BMO_slot_buffer_from_enabled_hflag(bm, &dupeop, "geom", htype, hflag);
354
355         BMO_op_exec(bm, &dupeop);
356         BMO_op_finish(bm, &dupeop);
357 }
358 #endif
359
360 /**
361  * Split Operator
362  *
363  * Duplicates verts, edges and faces of a mesh but also deletes the originals.
364  *
365  * INPUT SLOTS:
366  *
367  * BMOP_DUPE_VINPUT: Buffer containing pointers to mesh vertices to be split
368  * BMOP_DUPE_EINPUT: Buffer containing pointers to mesh edges to be split
369  * BMOP_DUPE_FINPUT: Buffer containing pointers to mesh faces to be split
370  *
371  * OUTPUT SLOTS:
372  *
373  * BMOP_DUPE_VOUTPUT: Buffer containing pointers to the split mesh vertices
374  * BMOP_DUPE_EOUTPUT: Buffer containing pointers to the split mesh edges
375  * BMOP_DUPE_FOUTPUT: Buffer containing pointers to the split mesh faces
376  *
377  * \note Lower level uses of this operator may want to use #BM_mesh_separate_faces
378  * Since it's faster for the 'use_only_faces' case.
379  */
380 void bmo_split_exec(BMesh *bm, BMOperator *op)
381 {
382 #define SPLIT_INPUT 1
383
384         BMOperator *splitop = op;
385         BMOperator dupeop;
386         const bool use_only_faces = BMO_slot_bool_get(op->slots_in, "use_only_faces");
387
388         /* initialize our sub-operator */
389         BMO_op_init(bm, &dupeop, op->flag, "duplicate");
390
391         BMO_slot_copy(splitop, slots_in, "geom",
392                       &dupeop, slots_in, "geom");
393         BMO_op_exec(bm, &dupeop);
394
395         BMO_slot_buffer_flag_enable(bm, splitop->slots_in, "geom", BM_ALL_NOLOOP, SPLIT_INPUT);
396
397         if (use_only_faces) {
398                 BMVert *v;
399                 BMEdge *e;
400                 BMFace *f;
401                 BMIter iter, iter2;
402
403                 /* make sure to remove edges and verts we don't need */
404                 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
405                         bool found = false;
406                         BM_ITER_ELEM (f, &iter2, e, BM_FACES_OF_EDGE) {
407                                 if (!BMO_face_flag_test(bm, f, SPLIT_INPUT)) {
408                                         found = true;
409                                         break;
410                                 }
411                         }
412                         if (found == false) {
413                                 BMO_edge_flag_enable(bm, e, SPLIT_INPUT);
414                         }
415                 }
416
417                 BM_ITER_MESH (v, &iter, bm,  BM_VERTS_OF_MESH) {
418                         bool found = false;
419                         BM_ITER_ELEM (e, &iter2, v, BM_EDGES_OF_VERT) {
420                                 if (!BMO_edge_flag_test(bm, e, SPLIT_INPUT)) {
421                                         found = true;
422                                         break;
423                                 }
424                         }
425                         if (found == false) {
426                                 BMO_vert_flag_enable(bm, v, SPLIT_INPUT);
427                         }
428                 }
429         }
430
431         /* connect outputs of dupe to delete, exluding keep geometry */
432         BMO_mesh_delete_oflag_context(bm, SPLIT_INPUT, DEL_FACES);
433
434         /* now we make our outputs by copying the dupe output */
435         BMO_slot_copy(&dupeop, slots_out, "geom.out",
436                       splitop, slots_out, "geom.out");
437
438         /* cleanup */
439         BMO_op_finish(bm, &dupeop);
440
441 #undef SPLIT_INPUT
442 }
443
444
445 void bmo_delete_exec(BMesh *bm, BMOperator *op)
446 {
447 #define DEL_INPUT 1
448
449         BMOperator *delop = op;
450
451         /* Mark Buffer */
452         BMO_slot_buffer_flag_enable(bm, delop->slots_in, "geom", BM_ALL_NOLOOP, DEL_INPUT);
453
454         BMO_mesh_delete_oflag_context(bm, DEL_INPUT, BMO_slot_int_get(op->slots_in, "context"));
455
456 #undef DEL_INPUT
457 }
458
459 /**
460  * Spin Operator
461  *
462  * Extrude or duplicate geometry a number of times,
463  * rotating and possibly translating after each step
464  */
465 void bmo_spin_exec(BMesh *bm, BMOperator *op)
466 {
467         BMOperator dupop, extop;
468         float cent[3], dvec[3];
469         float axis[3];
470         float rmat[3][3];
471         float phi;
472         int steps, do_dupli, a;
473         bool use_dvec;
474
475         BMO_slot_vec_get(op->slots_in, "cent", cent);
476         BMO_slot_vec_get(op->slots_in, "axis", axis);
477         normalize_v3(axis);
478         BMO_slot_vec_get(op->slots_in, "dvec", dvec);
479         use_dvec = !is_zero_v3(dvec);
480         steps    = BMO_slot_int_get(op->slots_in,   "steps");
481         phi      = BMO_slot_float_get(op->slots_in, "angle") / steps;
482         do_dupli = BMO_slot_bool_get(op->slots_in,  "use_duplicate");
483         const bool use_normal_flip = BMO_slot_bool_get(op->slots_in, "use_normal_flip");
484         /* Caller needs to perform other sanity checks (such as the spin being 360d). */
485         const bool use_merge = BMO_slot_bool_get(op->slots_in, "use_merge") && steps >= 3;
486
487         axis_angle_normalized_to_mat3(rmat, axis, phi);
488
489         BMVert **vtable = NULL;
490         if (use_merge) {
491                 vtable = MEM_mallocN(sizeof(BMVert *) * bm->totvert, __func__);
492                 int i = 0;
493                 BMIter iter;
494                 BMVert *v;
495                 BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
496                         vtable[i] = v;
497                         /* Evil! store original index in normal,
498                          * this is duplicated into every other vertex.
499                          * So we can read the original from the final.
500                          *
501                          * The normals must be recalculated anyway. */
502                         *((int *)&v->no[0]) = i;
503                 }
504         }
505
506         BMO_slot_copy(op, slots_in,  "geom",
507                       op, slots_out, "geom_last.out");
508         for (a = 0; a < steps; a++) {
509                 if (do_dupli) {
510                         BMO_op_initf(bm, &dupop, op->flag, "duplicate geom=%S", op, "geom_last.out");
511                         BMO_op_exec(bm, &dupop);
512                         BMO_op_callf(bm, op->flag,
513                                      "rotate cent=%v matrix=%m3 space=%s verts=%S",
514                                      cent, rmat, op, "space", &dupop, "geom.out");
515                         BMO_slot_copy(&dupop, slots_out, "geom.out",
516                                       op,     slots_out, "geom_last.out");
517                         BMO_op_finish(bm, &dupop);
518                 }
519                 else {
520                         BMO_op_initf(bm, &extop, op->flag,
521                                      "extrude_face_region geom=%S use_normal_flip=%b use_normal_from_adjacent=%b",
522                                      op, "geom_last.out", use_normal_flip && (a == 0), (a != 0));
523                         BMO_op_exec(bm, &extop);
524                         if ((use_merge && (a == steps - 1)) == false) {
525                                 BMO_op_callf(bm, op->flag,
526                                              "rotate cent=%v matrix=%m3 space=%s verts=%S",
527                                              cent, rmat, op, "space", &extop, "geom.out");
528                                 BMO_slot_copy(&extop, slots_out, "geom.out",
529                                               op,     slots_out, "geom_last.out");
530                         }
531                         else {
532                                 /* Merge first/last vertices and edges (maintaining 'geom.out' state). */
533                                 BMOpSlot *slot_geom_out = BMO_slot_get(extop.slots_out, "geom.out");
534                                 BMElem  **elem_array = (BMElem **)slot_geom_out->data.buf;
535                                 int elem_array_len = slot_geom_out->len;
536                                 for (int i = 0; i < elem_array_len; ) {
537                                         if (elem_array[i]->head.htype == BM_VERT) {
538                                                 BMVert *v_src = (BMVert *)elem_array[i];
539                                                 BMVert *v_dst = vtable[*((const int *)&v_src->no[0])];
540                                                 BM_vert_splice(bm, v_dst, v_src);
541                                                 elem_array_len--;
542                                                 elem_array[i] = elem_array[elem_array_len];
543                                         }
544                                         else {
545                                                 i++;
546                                         }
547                                 }
548                                 for (int i = 0; i < elem_array_len; ) {
549                                         if (elem_array[i]->head.htype == BM_EDGE) {
550                                                 BMEdge *e_src = (BMEdge *)elem_array[i];
551                                                 BMEdge *e_dst = BM_edge_find_double(e_src);
552                                                 if (e_dst != NULL) {
553                                                         BM_edge_splice(bm, e_dst, e_src);
554                                                         elem_array_len--;
555                                                         elem_array[i] = elem_array[elem_array_len];
556                                                         continue;
557                                                 }
558                                         }
559                                         i++;
560                                 }
561                                 /* Full copies of faces may cause overlap. */
562                                 for (int i = 0; i < elem_array_len; ) {
563                                         if (elem_array[i]->head.htype == BM_FACE) {
564                                                 BMFace *f_src = (BMFace *)elem_array[i];
565                                                 BMFace *f_dst = BM_face_find_double(f_src);
566                                                 if (f_dst != NULL) {
567                                                         BM_face_kill(bm, f_src);
568                                                         elem_array_len--;
569                                                         elem_array[i] = elem_array[elem_array_len];
570                                                         continue;
571                                                 }
572                                         }
573                                         i++;
574                                 }
575                                 slot_geom_out->len = elem_array_len;
576                         }
577                         BMO_op_finish(bm, &extop);
578                 }
579
580                 if (use_dvec) {
581                         mul_m3_v3(rmat, dvec);
582                         BMO_op_callf(bm, op->flag,
583                                      "translate vec=%v space=%s verts=%S",
584                                      dvec, op, "space", op, "geom_last.out");
585                 }
586         }
587
588         if (vtable) {
589                 MEM_freeN(vtable);
590         }
591 }