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