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