a4d67d1080682e8e44cd5632c5218e1d0cfbcee5
[blender.git] / source / blender / bmesh / operators / bmo_removedoubles.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.
19  *
20  * ***** END GPL LICENSE BLOCK *****
21  */
22
23 /** \file blender/bmesh/operators/bmo_removedoubles.c
24  *  \ingroup bmesh
25  *
26  * Welding and merging functionality.
27  */
28
29 #include "MEM_guardedalloc.h"
30
31 #include "BLI_math.h"
32 #include "BLI_alloca.h"
33 #include "BLI_kdtree.h"
34 #include "BLI_utildefines_stack.h"
35 #include "BLI_stack.h"
36
37 #include "BKE_customdata.h"
38
39 #include "bmesh.h"
40 #include "intern/bmesh_operators_private.h"
41
42
43 static void remdoubles_splitface(BMFace *f, BMesh *bm, BMOperator *op, BMOpSlot *slot_targetmap)
44 {
45         BMIter liter;
46         BMLoop *l, *l_tar, *l_double;
47         bool split = false;
48
49         BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
50                 BMVert *v_tar = BMO_slot_map_elem_get(slot_targetmap, l->v);
51                 /* ok: if v_tar is NULL (e.g. not in the map) then it's
52                  *     a target vert, otherwise it's a double */
53                 if (v_tar) {
54                         l_tar = BM_face_vert_share_loop(f, v_tar);
55
56                         if (l_tar && (l_tar != l) && !BM_loop_is_adjacent(l_tar, l)) {
57                                 l_double = l;
58                                 split = true;
59                                 break;
60                         }
61                 }
62         }
63
64         if (split) {
65                 BMLoop *l_new;
66                 BMFace *f_new;
67
68                 f_new = BM_face_split(bm, f, l_double, l_tar, &l_new, NULL, false);
69
70                 remdoubles_splitface(f,     bm, op, slot_targetmap);
71                 remdoubles_splitface(f_new, bm, op, slot_targetmap);
72         }
73 }
74
75 #define ELE_DEL         1
76 #define EDGE_COL        2
77 #define VERT_IN_FACE    4
78
79 /**
80  * helper function for bmo_weld_verts_exec so we can use stack memory
81  */
82 static BMFace *remdoubles_createface(BMesh *bm, BMFace *f, BMOpSlot *slot_targetmap)
83 {
84         BMEdge *e_new;
85
86         BMEdge **edges = BLI_array_alloca(edges, f->len);  /* new ordered edges */
87         BMVert **verts = BLI_array_alloca(verts, f->len);  /* new ordered verts */
88         BMLoop **loops = BLI_array_alloca(loops, f->len);  /* original ordered loops to copy attrs into the new face */
89
90         STACK_DECLARE(edges);
91         STACK_DECLARE(loops);
92         STACK_DECLARE(verts);
93
94         STACK_INIT(edges, f->len);
95         STACK_INIT(loops, f->len);
96         STACK_INIT(verts, f->len);
97
98
99         {
100 #define LOOP_MAP_VERT_INIT(l_init, v_map, is_del) \
101                 v_map = l_init->v; \
102                 is_del = BMO_vert_flag_test_bool(bm, v_map, ELE_DEL); \
103                 if (is_del) { \
104                         v_map = BMO_slot_map_elem_get(slot_targetmap, v_map); \
105                 } ((void)0)
106
107
108                 BMLoop *l_first, *l_curr, *l_next;
109                 BMVert *v_curr;
110                 bool is_del_v_curr;
111
112                 l_curr = l_first = BM_FACE_FIRST_LOOP(f);
113                 LOOP_MAP_VERT_INIT(l_curr, v_curr, is_del_v_curr);
114
115                 do {
116                         BMVert *v_next;
117                         bool is_del_v_next;
118
119                         l_next = l_curr->next;
120                         LOOP_MAP_VERT_INIT(l_next, v_next, is_del_v_next);
121
122                         /* only search for a new edge if one of the verts is mapped */
123                         if ((is_del_v_curr || is_del_v_next) == 0) {
124                                 e_new = l_curr->e;
125                         }
126                         else if (v_curr == v_next) {
127                                 e_new = NULL;  /* skip */
128                         }
129                         else {
130                                 e_new = BM_edge_exists(v_curr, v_next);
131                                 BLI_assert(e_new);  /* never fails */
132                         }
133
134                         if (e_new) {
135                                 if (UNLIKELY(BMO_vert_flag_test(bm, v_curr, VERT_IN_FACE))) {
136                                         /* we can't make the face, bail out */
137                                         STACK_CLEAR(edges);
138                                         goto finally;
139                                 }
140                                 BMO_vert_flag_enable(bm, v_curr, VERT_IN_FACE);
141
142                                 STACK_PUSH(edges, e_new);
143                                 STACK_PUSH(loops, l_curr);
144                                 STACK_PUSH(verts, v_curr);
145                         }
146
147                         v_curr = v_next;
148                         is_del_v_curr = is_del_v_next;
149                 } while ((l_curr = l_next) != l_first);
150
151 #undef LOOP_MAP_VERT_INIT
152
153         }
154
155 finally:
156         {
157                 uint i;
158                 for (i = 0; i < STACK_SIZE(verts); i++) {
159                         BMO_vert_flag_disable(bm, verts[i], VERT_IN_FACE);
160                 }
161         }
162
163         if (STACK_SIZE(edges) >= 3) {
164                 if (!BM_face_exists(verts, STACK_SIZE(edges))) {
165                         BMFace *f_new = BM_face_create(bm, verts, edges, STACK_SIZE(edges), f, BM_CREATE_NOP);
166                         BLI_assert(f_new != f);
167
168                         if (f_new) {
169                                 uint i = 0;
170                                 BMLoop *l_iter, *l_first;
171                                 l_iter = l_first = BM_FACE_FIRST_LOOP(f_new);
172                                 do {
173                                         BM_elem_attrs_copy(bm, bm, loops[i], l_iter);
174                                 } while ((void)i++, (l_iter = l_iter->next) != l_first);
175
176                                 return f_new;
177                         }
178                 }
179         }
180
181         return NULL;
182 }
183
184
185 /**
186  * \note with 'targetmap', multiple 'keys' are currently supported, though no callers should be using.
187  * (because slot maps currently use GHash without the GHASH_FLAG_ALLOW_DUPES flag set)
188  *
189  */
190 void bmo_weld_verts_exec(BMesh *bm, BMOperator *op)
191 {
192         BMIter iter, liter;
193         BMVert *v1, *v2;
194         BMEdge *e;
195         BMLoop *l;
196         BMFace *f;
197         BMOpSlot *slot_targetmap = BMO_slot_get(op->slots_in, "targetmap");
198
199         /* mark merge verts for deletion */
200         BM_ITER_MESH (v1, &iter, bm, BM_VERTS_OF_MESH) {
201                 if ((v2 = BMO_slot_map_elem_get(slot_targetmap, v1))) {
202                         BMO_vert_flag_enable(bm, v1, ELE_DEL);
203
204                         /* merge the vertex flags, else we get randomly selected/unselected verts */
205                         BM_elem_flag_merge_ex(v1, v2, BM_ELEM_HIDDEN);
206                 }
207         }
208
209         /* check if any faces are getting their own corners merged
210          * together, split face if so */
211         BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
212                 remdoubles_splitface(f, bm, op, slot_targetmap);
213         }
214
215         BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
216                 const bool is_del_v1 = BMO_vert_flag_test_bool(bm, (v1 = e->v1), ELE_DEL);
217                 const bool is_del_v2 = BMO_vert_flag_test_bool(bm, (v2 = e->v2), ELE_DEL);
218
219                 if (is_del_v1 || is_del_v2) {
220                         if (is_del_v1)
221                                 v1 = BMO_slot_map_elem_get(slot_targetmap, v1);
222                         if (is_del_v2)
223                                 v2 = BMO_slot_map_elem_get(slot_targetmap, v2);
224
225                         if (v1 == v2) {
226                                 BMO_edge_flag_enable(bm, e, EDGE_COL);
227                         }
228                         else {
229                                 /* always merge flags, even for edges we already created */
230                                 BMEdge *e_new = BM_edge_exists(v1, v2);
231                                 if (e_new == NULL) {
232                                         e_new = BM_edge_create(bm, v1, v2, e, BM_CREATE_NOP);
233                                 }
234                                 BM_elem_flag_merge_ex(e_new, e, BM_ELEM_HIDDEN);
235                         }
236
237                         BMO_edge_flag_enable(bm, e, ELE_DEL);
238                 }
239         }
240
241         /* faces get "modified" by creating new faces here, then at the
242          * end the old faces are deleted */
243         BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
244                 bool vert_delete = false;
245                 int  edge_collapse = 0;
246
247                 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
248                         if (BMO_vert_flag_test(bm, l->v, ELE_DEL)) {
249                                 vert_delete = true;
250                         }
251                         if (BMO_edge_flag_test(bm, l->e, EDGE_COL)) {
252                                 edge_collapse++;
253                         }
254                 }
255
256                 if (vert_delete) {
257                         BMO_face_flag_enable(bm, f, ELE_DEL);
258
259                         if (f->len - edge_collapse >= 3) {
260                                 BMFace *f_new = remdoubles_createface(bm, f, slot_targetmap);
261
262                                 /* do this so we don't need to return a list of created faces */
263                                 if (f_new) {
264                                         bmesh_face_swap_data(f_new, f);
265
266                                         if (bm->use_toolflags) {
267                                                 SWAP(BMFlagLayer *, ((BMFace_OFlag *)f)->oflags, ((BMFace_OFlag *)f_new)->oflags);
268                                         }
269
270                                         BMO_face_flag_disable(bm, f, ELE_DEL);
271
272                                         BM_face_kill(bm, f_new);
273                                 }
274                         }
275                 }
276         }
277
278         BMO_mesh_delete_oflag_context(bm, ELE_DEL, DEL_ONLYTAGGED);
279 }
280
281 #define VERT_KEEP       8
282 #define VERT_IN         32
283
284 #define EDGE_MARK       1
285
286 void bmo_pointmerge_facedata_exec(BMesh *bm, BMOperator *op)
287 {
288         BMOIter siter;
289         BMIter iter;
290         BMVert *v, *vert_snap;
291         BMLoop *l, *l_first = NULL;
292         float fac;
293         int i, tot;
294
295         vert_snap = BMO_slot_buffer_get_single(BMO_slot_get(op->slots_in, "vert_snap"));
296         tot = BM_vert_face_count(vert_snap);
297
298         if (!tot)
299                 return;
300
301         fac = 1.0f / tot;
302         BM_ITER_ELEM (l, &iter, vert_snap, BM_LOOPS_OF_VERT) {
303                 if (l_first == NULL) {
304                         l_first = l;
305                 }
306
307                 for (i = 0; i < bm->ldata.totlayer; i++) {
308                         if (CustomData_layer_has_math(&bm->ldata, i)) {
309                                 const int type = bm->ldata.layers[i].type;
310                                 const int offset = bm->ldata.layers[i].offset;
311                                 void *e1, *e2;
312
313                                 e1 = BM_ELEM_CD_GET_VOID_P(l_first, offset);
314                                 e2 = BM_ELEM_CD_GET_VOID_P(l,       offset);
315
316                                 CustomData_data_multiply(type, e2, fac);
317
318                                 if (l != l_first) {
319                                         CustomData_data_add(type, e1, e2);
320                                 }
321                         }
322                 }
323         }
324
325         BMO_ITER (v, &siter, op->slots_in, "verts", BM_VERT) {
326                 BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) {
327                         if (l == l_first) {
328                                 continue;
329                         }
330
331                         CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, l_first->head.data, &l->head.data);
332                 }
333         }
334 }
335
336 void bmo_average_vert_facedata_exec(BMesh *bm, BMOperator *op)
337 {
338         BMOIter siter;
339         BMIter iter;
340         BMVert *v;
341         BMLoop *l /* , *firstl = NULL */;
342         CDBlockBytes min, max;
343         int i;
344
345         for (i = 0; i < bm->ldata.totlayer; i++) {
346                 const int type = bm->ldata.layers[i].type;
347                 const int offset = bm->ldata.layers[i].offset;
348
349                 if (!CustomData_layer_has_math(&bm->ldata, i))
350                         continue;
351
352                 CustomData_data_initminmax(type, &min, &max);
353
354                 BMO_ITER (v, &siter, op->slots_in, "verts", BM_VERT) {
355                         BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) {
356                                 void *block = BM_ELEM_CD_GET_VOID_P(l, offset);
357                                 CustomData_data_dominmax(type, block, &min, &max);
358                         }
359                 }
360
361                 CustomData_data_multiply(type, &min, 0.5f);
362                 CustomData_data_multiply(type, &max, 0.5f);
363                 CustomData_data_add(type, &min, &max);
364
365                 BMO_ITER (v, &siter, op->slots_in, "verts", BM_VERT) {
366                         BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) {
367                                 void *block = BM_ELEM_CD_GET_VOID_P(l, offset);
368                                 CustomData_data_copy_value(type, &min, block);
369                         }
370                 }
371         }
372 }
373
374 void bmo_pointmerge_exec(BMesh *bm, BMOperator *op)
375 {
376         BMOperator weldop;
377         BMOIter siter;
378         BMVert *v, *vert_snap = NULL;
379         float vec[3];
380         BMOpSlot *slot_targetmap;
381
382         BMO_slot_vec_get(op->slots_in, "merge_co", vec);
383
384         //BMO_op_callf(bm, op->flag, "collapse_uvs edges=%s", op, "edges");
385         BMO_op_init(bm, &weldop, op->flag, "weld_verts");
386
387         slot_targetmap = BMO_slot_get(weldop.slots_in, "targetmap");
388
389         BMO_ITER (v, &siter, op->slots_in, "verts", BM_VERT) {
390                 if (!vert_snap) {
391                         vert_snap = v;
392                         copy_v3_v3(vert_snap->co, vec);
393                 }
394                 else {
395                         BMO_slot_map_elem_insert(&weldop, slot_targetmap, v, vert_snap);
396                 }
397         }
398
399         BMO_op_exec(bm, &weldop);
400         BMO_op_finish(bm, &weldop);
401 }
402
403 void bmo_collapse_exec(BMesh *bm, BMOperator *op)
404 {
405         BMOperator weldop;
406         BMWalker walker;
407         BMIter iter;
408         BMEdge *e;
409         BLI_Stack *edge_stack;
410         BMOpSlot *slot_targetmap;
411
412         if (BMO_slot_bool_get(op->slots_in, "uvs")) {
413                 BMO_op_callf(bm, op->flag, "collapse_uvs edges=%s", op, "edges");
414         }
415
416         BMO_op_init(bm, &weldop, op->flag, "weld_verts");
417         slot_targetmap = BMO_slot_get(weldop.slots_in, "targetmap");
418
419         BMO_slot_buffer_flag_enable(bm, op->slots_in, "edges", BM_EDGE, EDGE_MARK);
420
421         BMW_init(&walker, bm, BMW_VERT_SHELL,
422                  BMW_MASK_NOP, EDGE_MARK, BMW_MASK_NOP,
423                  BMW_FLAG_NOP, /* no need to use BMW_FLAG_TEST_HIDDEN, already marked data */
424                  BMW_NIL_LAY);
425
426         edge_stack = BLI_stack_new(sizeof(BMEdge *), __func__);
427
428         BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
429                 float center[3];
430                 int count = 0;
431                 BMVert *v_tar;
432
433                 zero_v3(center);
434
435                 if (!BMO_edge_flag_test(bm, e, EDGE_MARK))
436                         continue;
437
438                 BLI_assert(BLI_stack_is_empty(edge_stack));
439
440                 for (e = BMW_begin(&walker, e->v1); e; e = BMW_step(&walker)) {
441                         BLI_stack_push(edge_stack, &e);
442
443                         add_v3_v3(center, e->v1->co);
444                         add_v3_v3(center, e->v2->co);
445
446                         count += 2;
447
448                         /* prevent adding to slot_targetmap multiple times */
449                         BM_elem_flag_disable(e->v1, BM_ELEM_TAG);
450                         BM_elem_flag_disable(e->v2, BM_ELEM_TAG);
451                 }
452
453                 if (!BLI_stack_is_empty(edge_stack)) {
454                         mul_v3_fl(center, 1.0f / count);
455
456                         /* snap edges to a point.  for initial testing purposes anyway */
457                         e = *(BMEdge **)BLI_stack_peek(edge_stack);
458                         v_tar = e->v1;
459
460                         while (!BLI_stack_is_empty(edge_stack)) {
461                                 uint j;
462                                 BLI_stack_pop(edge_stack, &e);
463
464                                 for (j = 0; j < 2; j++) {
465                                         BMVert *v_src = *((&e->v1) + j);
466
467                                         copy_v3_v3(v_src->co, center);
468                                         if ((v_src != v_tar) && !BM_elem_flag_test(v_src, BM_ELEM_TAG)) {
469                                                 BM_elem_flag_enable(v_src, BM_ELEM_TAG);
470                                                 BMO_slot_map_elem_insert(&weldop, slot_targetmap, v_src, v_tar);
471                                         }
472                                 }
473                         }
474                 }
475         }
476
477         BLI_stack_free(edge_stack);
478
479         BMO_op_exec(bm, &weldop);
480         BMO_op_finish(bm, &weldop);
481
482         BMW_end(&walker);
483 }
484
485 /* uv collapse function */
486 static void bmo_collapsecon_do_layer(BMesh *bm, const int layer, const short oflag)
487 {
488         const int type = bm->ldata.layers[layer].type;
489         const int offset = bm->ldata.layers[layer].offset;
490         BMIter iter, liter;
491         BMFace *f;
492         BMLoop *l, *l2;
493         BMWalker walker;
494         BLI_Stack *block_stack;
495         CDBlockBytes min, max;
496
497         BMW_init(&walker, bm, BMW_LOOPDATA_ISLAND,
498                  BMW_MASK_NOP, oflag, BMW_MASK_NOP,
499                  BMW_FLAG_NOP, /* no need to use BMW_FLAG_TEST_HIDDEN, already marked data */
500                  layer);
501
502         block_stack = BLI_stack_new(sizeof(void *), __func__);
503
504         BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
505                 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
506                         if (BMO_edge_flag_test(bm, l->e, oflag)) {
507                                 /* walk */
508                                 BLI_assert(BLI_stack_is_empty(block_stack));
509
510                                 CustomData_data_initminmax(type, &min, &max);
511                                 for (l2 = BMW_begin(&walker, l); l2; l2 = BMW_step(&walker)) {
512                                         void *block = BM_ELEM_CD_GET_VOID_P(l2, offset);
513                                         CustomData_data_dominmax(type, block, &min, &max);
514                                         BLI_stack_push(block_stack, &block);
515                                 }
516
517                                 if (!BLI_stack_is_empty(block_stack)) {
518                                         CustomData_data_multiply(type, &min, 0.5f);
519                                         CustomData_data_multiply(type, &max, 0.5f);
520                                         CustomData_data_add(type, &min, &max);
521
522                                         /* snap CD (uv, vcol) points to their centroid */
523                                         while (!BLI_stack_is_empty(block_stack)) {
524                                                 void *block;
525                                                 BLI_stack_pop(block_stack, &block);
526                                                 CustomData_data_copy_value(type, &min, block);
527                                         }
528                                 }
529                         }
530                 }
531         }
532
533         BLI_stack_free(block_stack);
534
535         BMW_end(&walker);
536 }
537
538 void bmo_collapse_uvs_exec(BMesh *bm, BMOperator *op)
539 {
540         const short oflag = EDGE_MARK;
541         int i;
542
543         /* check flags dont change once set */
544 #ifndef NDEBUG
545         int tot_test;
546 #endif
547
548         if (!CustomData_has_math(&bm->ldata)) {
549                 return;
550         }
551
552         BMO_slot_buffer_flag_enable(bm, op->slots_in, "edges", BM_EDGE, oflag);
553
554 #ifndef NDEBUG
555         tot_test = BM_iter_mesh_count_flag(BM_EDGES_OF_MESH, bm, oflag, true);
556 #endif
557
558         for (i = 0; i < bm->ldata.totlayer; i++) {
559                 if (CustomData_layer_has_math(&bm->ldata, i))
560                         bmo_collapsecon_do_layer(bm, i, oflag);
561         }
562
563 #ifndef NDEBUG
564         BLI_assert(tot_test == BM_iter_mesh_count_flag(BM_EDGES_OF_MESH, bm, EDGE_MARK, true));
565 #endif
566
567 }
568
569 static void bmesh_find_doubles_common(
570         BMesh *bm, BMOperator *op,
571         BMOperator *optarget, BMOpSlot *optarget_slot)
572 {
573         const BMOpSlot *slot_verts = BMO_slot_get(op->slots_in, "verts");
574         BMVert * const *verts = (BMVert **)slot_verts->data.buf;
575         const int       verts_len = slot_verts->len;
576
577         bool has_keep_vert = false;
578         bool found_duplicates = false;
579
580         const float dist  = BMO_slot_float_get(op->slots_in, "dist");
581
582         /* Test whether keep_verts arg exists and is non-empty */
583         if (BMO_slot_exists(op->slots_in, "keep_verts")) {
584                 BMOIter oiter;
585                 has_keep_vert = BMO_iter_new(&oiter, op->slots_in, "keep_verts", BM_VERT) != NULL;
586         }
587
588         /* Flag keep_verts */
589         if (has_keep_vert) {
590                 BMO_slot_buffer_flag_enable(bm, op->slots_in, "keep_verts", BM_VERT, VERT_KEEP);
591         }
592
593         int *duplicates = MEM_mallocN(sizeof(int) * verts_len, __func__);
594         {
595                 KDTree *tree = BLI_kdtree_new(verts_len);
596                 for (int i = 0; i < verts_len; i++) {
597                         BLI_kdtree_insert(tree, i, verts[i]->co);
598                         if (has_keep_vert && BMO_vert_flag_test(bm, verts[i], VERT_KEEP)) {
599                                 duplicates[i] = i;
600                         }
601                         else {
602                                 duplicates[i] = -1;
603                         }
604                 }
605
606                 BLI_kdtree_balance(tree);
607                 found_duplicates = BLI_kdtree_calc_duplicates_fast(tree, dist, false, duplicates) != 0;
608                 BLI_kdtree_free(tree);
609         }
610
611         if (found_duplicates) {
612                 for (int i = 0; i < verts_len; i++) {
613                         BMVert *v_check = verts[i];
614                         if (duplicates[i] == -1) {
615                                 /* nop (others can use as target) */
616                         }
617                         else if (duplicates[i] == i) {
618                                 /* keep (others can use as target) */
619                         }
620                         else {
621                                 BMVert *v_other = verts[duplicates[i]];
622                                 BLI_assert(ELEM(duplicates[duplicates[i]], -1, duplicates[i]));
623                                 BMO_slot_map_elem_insert(optarget, optarget_slot, v_check, v_other);
624                         }
625                 }
626         }
627
628         MEM_freeN(duplicates);
629 }
630
631 void bmo_remove_doubles_exec(BMesh *bm, BMOperator *op)
632 {
633         BMOperator weldop;
634         BMOpSlot *slot_targetmap;
635
636         BMO_op_init(bm, &weldop, op->flag, "weld_verts");
637         slot_targetmap = BMO_slot_get(weldop.slots_in, "targetmap");
638         bmesh_find_doubles_common(bm, op,
639                                   &weldop, slot_targetmap);
640         BMO_op_exec(bm, &weldop);
641         BMO_op_finish(bm, &weldop);
642 }
643
644
645 void bmo_find_doubles_exec(BMesh *bm, BMOperator *op)
646 {
647         BMOpSlot *slot_targetmap_out;
648         slot_targetmap_out = BMO_slot_get(op->slots_out, "targetmap.out");
649         bmesh_find_doubles_common(bm, op,
650                                   op, slot_targetmap_out);
651 }
652
653 void bmo_automerge_exec(BMesh *bm, BMOperator *op)
654 {
655         BMOperator findop, weldop;
656         BMIter viter;
657         BMVert *v;
658
659         /* The "verts" input sent to this op is the set of verts that
660          * can be merged away into any other verts. Mark all other verts
661          * as VERT_KEEP. */
662         BMO_slot_buffer_flag_enable(bm, op->slots_in, "verts", BM_VERT, VERT_IN);
663         BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
664                 if (!BMO_vert_flag_test(bm, v, VERT_IN)) {
665                         BMO_vert_flag_enable(bm, v, VERT_KEEP);
666                 }
667         }
668
669         /* Search for doubles among all vertices, but only merge non-VERT_KEEP
670          * vertices into VERT_KEEP vertices. */
671         BMO_op_initf(bm, &findop, op->flag, "find_doubles verts=%av keep_verts=%fv", VERT_KEEP);
672         BMO_slot_copy(op,      slots_in, "dist",
673                       &findop, slots_in, "dist");
674         BMO_op_exec(bm, &findop);
675
676         /* weld the vertices */
677         BMO_op_init(bm, &weldop, op->flag, "weld_verts");
678         BMO_slot_copy(&findop, slots_out, "targetmap.out",
679                       &weldop, slots_in,  "targetmap");
680         BMO_op_exec(bm, &weldop);
681
682         BMO_op_finish(bm, &findop);
683         BMO_op_finish(bm, &weldop);
684 }