Blender V5.0
bmo_removedoubles.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10
11#include "MEM_guardedalloc.h"
12
13#include "BLI_alloca.h"
14#include "BLI_kdtree.h"
15#include "BLI_listbase.h"
16#include "BLI_map.hh"
17#include "BLI_math_base.hh"
18#include "BLI_math_vector.h"
19#include "BLI_stack.h"
20#include "BLI_stack.hh"
22
23#include "BKE_customdata.hh"
24
25#include "bmesh.hh"
27
28static void remdoubles_splitface(BMFace *f, BMesh *bm, BMOperator *op, BMOpSlot *slot_targetmap)
29{
30 BMIter liter;
31 BMLoop *l, *l_tar, *l_double;
32 bool split = false;
33
34 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
35 BMVert *v_tar = static_cast<BMVert *>(BMO_slot_map_elem_get(slot_targetmap, l->v));
36 /* Ok: if `v_tar` is nullptr (e.g. not in the map) then it's
37 * a target vert, otherwise it's a double. */
38 if (v_tar) {
39 l_tar = BM_face_vert_share_loop(f, v_tar);
40
41 if (l_tar && (l_tar != l) && !BM_loop_is_adjacent(l_tar, l)) {
42 l_double = l;
43 split = true;
44 break;
45 }
46 }
47 }
48
49 if (split) {
50 BMLoop *l_new;
51 BMFace *f_new;
52
53 f_new = BM_face_split(bm, f, l_double, l_tar, &l_new, nullptr, false);
54
55 remdoubles_splitface(f, bm, op, slot_targetmap);
56 remdoubles_splitface(f_new, bm, op, slot_targetmap);
57 }
58}
59
60#define ELE_DEL 1
61#define EDGE_COL 2
62#define VERT_IN_FACE 4
63
68 BMFace *f,
69 BMOpSlot *slot_targetmap,
70 bool *r_created)
71{
72 BMEdge *e_new;
73
74 /* New ordered edges. */
75 BMEdge **edges = BLI_array_alloca(edges, f->len);
76 /* New ordered verts. */
78 /* Original ordered loops to copy attributes into the new face. */
79 BMLoop **loops = BLI_array_alloca(loops, f->len);
80
81 STACK_DECLARE(edges);
82 STACK_DECLARE(loops);
84
85 STACK_INIT(edges, f->len);
86 STACK_INIT(loops, f->len);
87 STACK_INIT(verts, f->len);
88
89 *r_created = false;
90
91 {
92#define LOOP_MAP_VERT_INIT(l_init, v_map, is_del) \
93 v_map = l_init->v; \
94 is_del = BMO_vert_flag_test_bool(bm, v_map, ELE_DEL); \
95 if (is_del) { \
96 v_map = static_cast<BMVert *>(BMO_slot_map_elem_get(slot_targetmap, v_map)); \
97 } \
98 ((void)0)
99
100 BMLoop *l_first, *l_curr, *l_next;
101 BMVert *v_curr;
102 bool is_del_v_curr;
103
104 l_curr = l_first = BM_FACE_FIRST_LOOP(f);
105 LOOP_MAP_VERT_INIT(l_curr, v_curr, is_del_v_curr);
106
107 do {
108 BMVert *v_next;
109 bool is_del_v_next;
110
111 l_next = l_curr->next;
112 LOOP_MAP_VERT_INIT(l_next, v_next, is_del_v_next);
113
114 /* Only search for a new edge if one of the verts is mapped. */
115 if ((is_del_v_curr || is_del_v_next) == 0) {
116 e_new = l_curr->e;
117 }
118 else if (v_curr == v_next) {
119 e_new = nullptr; /* Skip. */
120 }
121 else {
122 e_new = BM_edge_exists(v_curr, v_next);
123 BLI_assert(e_new); /* Never fails. */
124 }
125
126 if (e_new) {
128 /* We can't make the face, bail out. */
129 STACK_CLEAR(edges);
130 goto finally;
131 }
133
134 STACK_PUSH(edges, e_new);
135 STACK_PUSH(loops, l_curr);
136 STACK_PUSH(verts, v_curr);
137 }
138
139 v_curr = v_next;
140 is_del_v_curr = is_del_v_next;
141 } while ((l_curr = l_next) != l_first);
142
143#undef LOOP_MAP_VERT_INIT
144 }
145
146finally: {
147 uint i;
148 for (i = 0; i < STACK_SIZE(verts); i++) {
150 }
151}
152
153 if (STACK_SIZE(edges) >= 3) {
155 if (f_new) {
156 return f_new;
157 }
158 f_new = BM_face_create(bm, verts, edges, STACK_SIZE(edges), f, BM_CREATE_NOP);
159 BLI_assert(f_new != f);
160
161 if (f_new) {
162 uint i = 0;
163 BMLoop *l_iter, *l_first;
164 l_iter = l_first = BM_FACE_FIRST_LOOP(f_new);
165 do {
166 BM_elem_attrs_copy(bm, loops[i], l_iter);
167 } while ((void)i++, (l_iter = l_iter->next) != l_first);
168
169 *r_created = true;
170 return f_new;
171 }
172 }
173
174 return nullptr;
175}
176
183{
184 BMIter iter, liter;
185 BMVert *v;
186 BMEdge *e;
187 BMLoop *l;
188 BMFace *f;
189 BMOpSlot *slot_targetmap = BMO_slot_get(op->slots_in, "targetmap");
190 const bool use_centroid = BMO_slot_bool_get(op->slots_in, "use_centroid");
191
192 /* Maintain selection history. */
193 const bool has_selected = !BLI_listbase_is_empty(&bm->selected);
194 const bool use_targetmap_all = has_selected;
195 GHash *targetmap_all = nullptr;
196 if (use_targetmap_all) {
197 /* Map deleted to keep elem. */
198 targetmap_all = BLI_ghash_ptr_new(__func__);
199 }
200
201 GHash *clusters = use_centroid ? BLI_ghash_ptr_new(__func__) : nullptr;
202
203 /* Mark merge verts for deletion. */
204 BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
205 BMVert *v_dst = static_cast<BMVert *>(BMO_slot_map_elem_get(slot_targetmap, v));
206 if (v_dst == nullptr) {
207 continue;
208 }
209
211
212 /* Merge the vertex flags, else we get randomly selected/unselected verts. */
214
215 if (use_targetmap_all) {
216 BLI_assert(v != v_dst);
217 BLI_ghash_insert(targetmap_all, v, v_dst);
218 }
219
220 /* Group vertices by their survivor. */
221 if (use_centroid && LIKELY(v_dst != v)) {
222 void **cluster_p;
223 if (!BLI_ghash_ensure_p(clusters, v_dst, &cluster_p)) {
224 *cluster_p = MEM_new<blender::Vector<BMVert *>>(__func__);
225 }
226 blender::Vector<BMVert *> *cluster = static_cast<blender::Vector<BMVert *> *>(*cluster_p);
227 cluster->append(v);
228 }
229 }
230
231 if (use_centroid) {
232 /* Compute centroid for each survivor. */
233 GHashIterator gh_iter;
234 GHASH_ITER (gh_iter, clusters) {
235 BMVert *v_dst = static_cast<BMVert *>(BLI_ghashIterator_getKey(&gh_iter));
238
239 float centroid[3];
240 copy_v3_v3(centroid, v_dst->co);
241 int count = 1; /* Include `v_dst`. */
242
243 for (BMVert *v_duplicate : *cluster) {
244 add_v3_v3(centroid, v_duplicate->co);
245 count++;
246 }
247
248 mul_v3_fl(centroid, 1.0f / float(count));
249 copy_v3_v3(v_dst->co, centroid);
250
251 /* Free temporary cluster storage. */
252 MEM_delete(cluster);
253 }
254 BLI_ghash_free(clusters, nullptr, nullptr);
255 clusters = nullptr;
256 }
257
258 /* Check if any faces are getting their own corners merged
259 * together, split face if so. */
260 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
261 remdoubles_splitface(f, bm, op, slot_targetmap);
262 }
263
264 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
265 BMVert *v1, *v2;
266 const bool is_del_v1 = BMO_vert_flag_test_bool(bm, (v1 = e->v1), ELE_DEL);
267 const bool is_del_v2 = BMO_vert_flag_test_bool(bm, (v2 = e->v2), ELE_DEL);
268
269 if (is_del_v1 || is_del_v2) {
270 if (is_del_v1) {
271 v1 = static_cast<BMVert *>(BMO_slot_map_elem_get(slot_targetmap, v1));
272 }
273 if (is_del_v2) {
274 v2 = static_cast<BMVert *>(BMO_slot_map_elem_get(slot_targetmap, v2));
275 }
276
277 if (v1 == v2) {
279 }
280 else {
281 /* Always merge flags, even for edges we already created. */
282 BMEdge *e_new = BM_edge_exists(v1, v2);
283 if (e_new == nullptr) {
284 e_new = BM_edge_create(bm, v1, v2, e, BM_CREATE_NOP);
285 }
287 if (use_targetmap_all) {
288 BLI_assert(e != e_new);
289 BLI_ghash_insert(targetmap_all, e, e_new);
290 }
291 }
292
294 }
295 }
296
297 /* Faces get "modified" by creating new faces here, then at the
298 * end the old faces are deleted. */
299 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
300 bool vert_delete = false;
301 int edge_collapse = 0;
302
303 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
304 if (BMO_vert_flag_test(bm, l->v, ELE_DEL)) {
305 vert_delete = true;
306 }
307 if (BMO_edge_flag_test(bm, l->e, EDGE_COL)) {
308 edge_collapse++;
309 }
310 }
311
312 if (vert_delete) {
313 bool use_in_place = false;
314 BMFace *f_new = nullptr;
316
317 if (f->len - edge_collapse >= 3) {
318 bool created;
319 f_new = remdoubles_createface(bm, f, slot_targetmap, &created);
320 /* Do this so we don't need to return a list of created faces. */
321 if (f_new) {
322 if (created) {
323 bmesh_face_swap_data(f_new, f);
324
325 if (bm->use_toolflags) {
326 std::swap(((BMFace_OFlag *)f)->oflags, ((BMFace_OFlag *)f_new)->oflags);
327 }
328
330 BM_face_kill(bm, f_new);
331 use_in_place = true;
332 }
333 else {
335 }
336 }
337 }
338
339 if ((use_in_place == false) && (f_new != nullptr)) {
340 BLI_assert(f != f_new);
341 if (use_targetmap_all) {
342 BLI_ghash_insert(targetmap_all, f, f_new);
343 }
344 if (bm->act_face && (f == bm->act_face)) {
345 bm->act_face = f_new;
346 }
347 }
348 }
349 }
350
351 if (has_selected) {
352 BM_select_history_merge_from_targetmap(bm, targetmap_all, targetmap_all, targetmap_all, true);
353 }
354
355 if (use_targetmap_all) {
356 BLI_ghash_free(targetmap_all, nullptr, nullptr);
357 }
358
360}
361
362#define VERT_KEEP 8
363
364#define EDGE_MARK 1
365
367{
368 BMOIter siter;
369 BMIter iter;
370 BMVert *v, *vert_snap;
371 BMLoop *l, *l_first = nullptr;
372 float fac;
373 int i, tot;
374
375 vert_snap = static_cast<BMVert *>(
377 tot = BM_vert_face_count(vert_snap);
378
379 if (!tot) {
380 return;
381 }
382
383 fac = 1.0f / tot;
384 BM_ITER_ELEM (l, &iter, vert_snap, BM_LOOPS_OF_VERT) {
385 if (l_first == nullptr) {
386 l_first = l;
387 }
388
389 for (i = 0; i < bm->ldata.totlayer; i++) {
390 if (CustomData_layer_has_math(&bm->ldata, i)) {
391 const int type = bm->ldata.layers[i].type;
392 const int offset = bm->ldata.layers[i].offset;
393 void *e1, *e2;
394
395 e1 = BM_ELEM_CD_GET_VOID_P(l_first, offset);
396 e2 = BM_ELEM_CD_GET_VOID_P(l, offset);
397
399
400 if (l != l_first) {
402 }
403 }
404 }
405 }
406
407 BMO_ITER (v, &siter, op->slots_in, "verts", BM_VERT) {
408 BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) {
409 if (l == l_first) {
410 continue;
411 }
412
413 CustomData_bmesh_copy_block(bm->ldata, l_first->head.data, &l->head.data);
414 }
415 }
416}
417
419{
420 BMOIter siter;
421 BMIter iter;
422 BMVert *v;
423 BMLoop *l;
425 int i;
426
427 for (i = 0; i < bm->ldata.totlayer; i++) {
428 const int type = bm->ldata.layers[i].type;
429 const int offset = bm->ldata.layers[i].offset;
430
431 if (!CustomData_layer_has_math(&bm->ldata, i)) {
432 continue;
433 }
434
436
437 BMO_ITER (v, &siter, op->slots_in, "verts", BM_VERT) {
438 BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) {
439 void *block = BM_ELEM_CD_GET_VOID_P(l, offset);
441 }
442 }
443
447
448 BMO_ITER (v, &siter, op->slots_in, "verts", BM_VERT) {
449 BM_ITER_ELEM (l, &iter, v, BM_LOOPS_OF_VERT) {
450 void *block = BM_ELEM_CD_GET_VOID_P(l, offset);
452 }
453 }
454 }
455}
456
458{
459 BMOperator weldop;
460 BMOIter siter;
461 BMVert *v, *vert_snap = nullptr;
462 float vec[3];
463 BMOpSlot *slot_targetmap;
464
465 BMO_slot_vec_get(op->slots_in, "merge_co", vec);
466
467 // BMO_op_callf(bm, op->flag, "collapse_uvs edges=%s", op, "edges");
468 BMO_op_init(bm, &weldop, op->flag, "weld_verts");
469
470 slot_targetmap = BMO_slot_get(weldop.slots_in, "targetmap");
471
472 BMO_ITER (v, &siter, op->slots_in, "verts", BM_VERT) {
473 if (!vert_snap) {
474 vert_snap = v;
475 copy_v3_v3(vert_snap->co, vec);
476 }
477 else {
478 BMO_slot_map_elem_insert(&weldop, slot_targetmap, v, vert_snap);
479 }
480 }
481
482 BMO_op_exec(bm, &weldop);
483 BMO_op_finish(bm, &weldop);
484}
485
487{
488 BMOperator weldop;
489 BMWalker walker;
490 BMIter iter;
491 BMEdge *e;
492 BLI_Stack *edge_stack;
493 BMOpSlot *slot_targetmap;
494
495 if (BMO_slot_bool_get(op->slots_in, "uvs")) {
496 BMO_op_callf(bm, op->flag, "collapse_uvs edges=%s", op, "edges");
497 }
498
499 BMO_op_init(bm, &weldop, op->flag, "weld_verts");
500 slot_targetmap = BMO_slot_get(weldop.slots_in, "targetmap");
501
503
504 BMW_init(&walker,
505 bm,
508 EDGE_MARK,
510 BMW_FLAG_NOP, /* No need to use #BMW_FLAG_TEST_HIDDEN, already marked data. */
512
513 edge_stack = BLI_stack_new(sizeof(BMEdge *), __func__);
514
515 BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
516 float center[3];
517 int count = 0;
518 BMVert *v_tar;
519
520 zero_v3(center);
521
523 continue;
524 }
525
526 BLI_assert(BLI_stack_is_empty(edge_stack));
527
528 for (e = static_cast<BMEdge *>(BMW_begin(&walker, e->v1)); e;
529 e = static_cast<BMEdge *>(BMW_step(&walker)))
530 {
531 BLI_stack_push(edge_stack, &e);
532
533 add_v3_v3(center, e->v1->co);
534 add_v3_v3(center, e->v2->co);
535
536 count += 2;
537
538 /* Prevent adding to `slot_targetmap` multiple times. */
541 }
542
543 if (!BLI_stack_is_empty(edge_stack)) {
544 mul_v3_fl(center, 1.0f / count);
545
546 /* Snap edges to a point. for initial testing purposes anyway. */
547 e = *(BMEdge **)BLI_stack_peek(edge_stack);
548 v_tar = e->v1;
549
550 while (!BLI_stack_is_empty(edge_stack)) {
551 uint j;
552 BLI_stack_pop(edge_stack, &e);
553
554 for (j = 0; j < 2; j++) {
555 BMVert *v_src = *((&e->v1) + j);
556
557 copy_v3_v3(v_src->co, center);
558 if ((v_src != v_tar) && !BM_elem_flag_test(v_src, BM_ELEM_TAG)) {
560 BMO_slot_map_elem_insert(&weldop, slot_targetmap, v_src, v_tar);
561 }
562 }
563 }
564 }
565 }
566
567 BLI_stack_free(edge_stack);
568
569 BMO_op_exec(bm, &weldop);
570 BMO_op_finish(bm, &weldop);
571
572 BMW_end(&walker);
573}
574
576static void bmo_collapsecon_do_layer(BMesh *bm, const int layer, const short oflag)
577{
578 const int type = bm->ldata.layers[layer].type;
579 const int offset = bm->ldata.layers[layer].offset;
580 BMIter iter, liter;
581 BMFace *f;
582 BMLoop *l, *l2;
583 BMWalker walker;
584 BLI_Stack *block_stack;
586
587 BMW_init(&walker,
588 bm,
591 oflag,
593 BMW_FLAG_NOP, /* No need to use #BMW_FLAG_TEST_HIDDEN, already marked data. */
594 layer);
595
596 block_stack = BLI_stack_new(sizeof(void *), __func__);
597
598 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
599 BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
600 if (BMO_edge_flag_test(bm, l->e, oflag)) {
601 /* Walk. */
602 BLI_assert(BLI_stack_is_empty(block_stack));
603
605 for (l2 = static_cast<BMLoop *>(BMW_begin(&walker, l)); l2;
606 l2 = static_cast<BMLoop *>(BMW_step(&walker)))
607 {
608 void *block = BM_ELEM_CD_GET_VOID_P(l2, offset);
610 BLI_stack_push(block_stack, &block);
611 }
612
613 if (!BLI_stack_is_empty(block_stack)) {
617
618 /* Snap custom-data (UV, vertex-colors) points to their centroid. */
619 while (!BLI_stack_is_empty(block_stack)) {
620 void *block;
621 BLI_stack_pop(block_stack, &block);
623 }
624 }
625 }
626 }
627 }
628
629 BLI_stack_free(block_stack);
630
631 BMW_end(&walker);
632}
633
635{
636 const short oflag = EDGE_MARK;
637 int i;
638
639/* Check flags don't change once set. */
640#ifndef NDEBUG
641 int tot_test;
642#endif
643
644 if (!CustomData_has_math(&bm->ldata)) {
645 return;
646 }
647
649
650#ifndef NDEBUG
652#endif
653
654 for (i = 0; i < bm->ldata.totlayer; i++) {
655 if (CustomData_layer_has_math(&bm->ldata, i)) {
657 }
658 }
659
660#ifndef NDEBUG
662#endif
663}
664
673 BMVert *const *verts,
674 const int verts_len,
675 const float dist,
676 const bool has_keep_vert)
677{
678 int *duplicates = MEM_malloc_arrayN<int>(verts_len, __func__);
679 bool found_duplicates = false;
680 bool has_self_index = false;
681
682 KDTree_3d *tree = BLI_kdtree_3d_new(verts_len);
683 for (int i = 0; i < verts_len; i++) {
684 BLI_kdtree_3d_insert(tree, i, verts[i]->co);
685 if (has_keep_vert && BMO_vert_flag_test(bm, verts[i], VERT_KEEP)) {
686 duplicates[i] = i;
687 has_self_index = true;
688 }
689 else {
690 duplicates[i] = -1;
691 }
692 }
693
694 BLI_kdtree_3d_balance(tree);
695
696 /* Given a cluster of duplicates, pick the index to keep. */
697 auto deduplicate_target_calc_fn = [&verts](const int *cluster, const int cluster_num) -> int {
698 if (cluster_num == 2) {
699 /* Special case, no use in calculating centroid.
700 * Use the lowest index for stability. */
701 return (cluster[0] < cluster[1]) ? 0 : 1;
702 }
703 BLI_assert(cluster_num > 2);
704
705 blender::float3 centroid{0.0f};
706 for (int i = 0; i < cluster_num; i++) {
707 centroid += blender::float3(verts[cluster[i]]->co);
708 }
709 centroid /= float(cluster_num);
710
711 /* Now pick the most "central" index (with lowest index as a tie breaker). */
712 const int cluster_end = cluster_num - 1;
713 /* Assign `i_best` from the last index as this is the index where the search originated
714 * so it's most likely to be the best. */
715 int i_best = cluster_end;
716 float dist_sq_best = len_squared_v3v3(centroid, verts[cluster[i_best]]->co);
717 for (int i = 0; i < cluster_end; i++) {
718 const float dist_sq_test = len_squared_v3v3(centroid, verts[cluster[i]]->co);
719
720 if (dist_sq_test > dist_sq_best) {
721 continue;
722 }
723 if (dist_sq_test == dist_sq_best) {
724 if (cluster[i] > cluster[i_best]) {
725 continue;
726 }
727 }
728 i_best = i;
729 dist_sq_best = dist_sq_test;
730 }
731 return i_best;
732 };
733
734 found_duplicates = BLI_kdtree_3d_calc_duplicates_cb_cpp(
735 tree, dist, duplicates, has_self_index, deduplicate_target_calc_fn) != 0;
736
737 BLI_kdtree_3d_free(tree);
738
739 if (!found_duplicates) {
740 MEM_freeN(duplicates);
741 duplicates = nullptr;
742 }
743 return duplicates;
744}
745
748 BMVert *const *verts,
749 const int verts_len,
750 const float dist,
751 const bool has_keep_vert)
752{
753 int *duplicates = MEM_malloc_arrayN<int>(verts_len, __func__);
754 bool found_duplicates = false;
755
756 blender::Stack<int> vert_stack;
757 blender::Map<BMVert *, int> vert_to_index_map;
758
759 for (int i = 0; i < verts_len; i++) {
760 if (has_keep_vert && BMO_vert_flag_test(bm, verts[i], VERT_KEEP)) {
761 duplicates[i] = i;
762 }
763 else {
764 duplicates[i] = -1;
765 }
766 vert_to_index_map.add(verts[i], i);
767 }
768
769 const float dist_sq = blender::math::square(dist);
770
771 for (int i = 0; i < verts_len; i++) {
772 if (!ELEM(duplicates[i], -1, i)) {
773 continue;
774 }
775 const float *co_check = verts[i]->co;
776 BLI_assert(vert_stack.is_empty());
777 int i_check = i;
778 do {
779 BMVert *v_check = verts[i_check];
780 if (v_check->e) {
781 BMEdge *e_iter, *e_first;
782 e_first = e_iter = v_check->e;
783 do {
784 /* Edge stepping. */
785 BMVert *v_other = BM_edge_other_vert(e_iter, v_check);
786 if (len_squared_v3v3(v_other->co, co_check) < dist_sq) {
787 const int i_other = vert_to_index_map.lookup_default(v_other, -1);
788 if ((i_other != -1) && (duplicates[i_other] == -1)) {
789 duplicates[i_other] = i;
790 vert_stack.push(i_other);
791 found_duplicates = true;
792 }
793 }
794
795 /* Face stepping. */
796 if (e_iter->l) {
797 BMLoop *l_radial_iter;
798 l_radial_iter = e_iter->l;
799 do {
800 if (l_radial_iter->v != v_check) {
801 /* This face will be met from another edge. */
802 continue;
803 }
804 if (l_radial_iter->f->len <= 3) {
805 /* Edge iteration handles triangles. */
806 continue;
807 }
808
809 /* Loop over all vertices not connected to edges attached to `v_check`.
810 * For a 4 sided face, this will only check 1 vertex. */
811 BMLoop *l_iter = l_radial_iter->next->next;
812 BMLoop *l_end = l_radial_iter->prev;
813 do {
814 BMVert *v_other = l_iter->v;
815 if (len_squared_v3v3(v_other->co, co_check) < dist_sq) {
816 const int i_other = vert_to_index_map.lookup_default(v_other, -1);
817 if ((i_other != -1) && (duplicates[i_other] == -1)) {
818 duplicates[i_other] = i;
819 vert_stack.push(i_other);
820 found_duplicates = true;
821 }
822 }
823 } while ((l_iter = l_iter->next) != l_end);
824 } while ((l_radial_iter = l_radial_iter->radial_next) != e_iter->l);
825 }
826
827 } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v_check)) != e_first);
828 }
829 } while ((i_check = vert_stack.is_empty() ? -1 : vert_stack.pop()) != -1);
830 }
831
832 if (!found_duplicates) {
833 MEM_freeN(duplicates);
834 duplicates = nullptr;
835 }
836 return duplicates;
837}
838
840 BMOperator *op,
841 BMOperator *optarget,
842 BMOpSlot *optarget_slot)
843{
844 const bool use_connected = BMO_slot_bool_get(op->slots_in, "use_connected");
845
846 const BMOpSlot *slot_verts = BMO_slot_get(op->slots_in, "verts");
847 BMVert *const *verts = (BMVert **)slot_verts->data.buf;
848 const int verts_len = slot_verts->len;
849
850 bool has_keep_vert = false;
851
852 const float dist = BMO_slot_float_get(op->slots_in, "dist");
853
854 /* Test whether keep_verts arg exists and is non-empty. */
855 if (BMO_slot_exists(op->slots_in, "keep_verts")) {
856 BMOIter oiter;
857 has_keep_vert = BMO_iter_new(&oiter, op->slots_in, "keep_verts", BM_VERT) != nullptr;
858 }
859
860 /* Flag keep_verts. */
861 if (has_keep_vert) {
863 }
864
865 int *duplicates = nullptr; /* `verts_len` aligned index array. */
866 if (use_connected) {
868 bm, verts, verts_len, dist, has_keep_vert);
869 }
870 else {
871 duplicates = bmesh_find_doubles_by_distance_impl(bm, verts, verts_len, dist, has_keep_vert);
872 }
873
874 /* Null when no duplicates were found. */
875 if (duplicates) {
876 for (int i = 0; i < verts_len; i++) {
877 BMVert *v_check = verts[i];
878 if (duplicates[i] == -1) {
879 /* NOP (others can use as target). */
880 }
881 else if (duplicates[i] == i) {
882 /* Keep (others can use as target). */
883 }
884 else {
885 BMVert *v_other = verts[duplicates[i]];
886 BLI_assert(ELEM(duplicates[duplicates[i]], -1, duplicates[i]));
887 BMO_slot_map_elem_insert(optarget, optarget_slot, v_check, v_other);
888 }
889 }
890 MEM_freeN(duplicates);
891 }
892}
893
895{
896 BMOperator weldop;
897 BMOpSlot *slot_targetmap;
898
899 BMO_op_init(bm, &weldop, op->flag, "weld_verts");
900 slot_targetmap = BMO_slot_get(weldop.slots_in, "targetmap");
901 bmesh_find_doubles_common(bm, op, &weldop, slot_targetmap);
902 BMO_op_exec(bm, &weldop);
903 BMO_op_finish(bm, &weldop);
904}
905
907{
908 BMOpSlot *slot_targetmap_out;
909 slot_targetmap_out = BMO_slot_get(op->slots_out, "targetmap.out");
910 bmesh_find_doubles_common(bm, op, op, slot_targetmap_out);
911}
CustomData interface, see also DNA_customdata_types.h.
void CustomData_data_initminmax(eCustomDataType type, void *min, void *max)
bool CustomData_layer_has_math(const CustomData *data, int layer_n)
void CustomData_data_copy_value(eCustomDataType type, const void *source, void *dest)
void CustomData_bmesh_copy_block(CustomData &data, void *src_block, void **dst_block)
void CustomData_data_dominmax(eCustomDataType type, const void *data, void *min, void *max)
void CustomData_data_multiply(eCustomDataType type, void *data, float fac)
void CustomData_data_add(eCustomDataType type, void *data1, const void *data2)
bool CustomData_has_math(const CustomData *data)
#define BLI_array_alloca(arr, realsize)
Definition BLI_alloca.h:18
#define BLI_assert(a)
Definition BLI_assert.h:46
BLI_INLINE void * BLI_ghashIterator_getKey(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.h:295
BLI_INLINE void * BLI_ghashIterator_getValue(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.h:299
#define GHASH_ITER(gh_iter_, ghash_)
Definition BLI_ghash.h:318
GHash * BLI_ghash_ptr_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
void BLI_ghash_insert(GHash *gh, void *key, void *val)
Definition BLI_ghash.cc:707
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition BLI_ghash.cc:860
bool BLI_ghash_ensure_p(GHash *gh, void *key, void ***r_val) ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.cc:752
A KD-tree for nearest neighbor search.
BLI_INLINE bool BLI_listbase_is_empty(const ListBase *lb)
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void zero_v3(float r[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
void BLI_stack_pop(BLI_Stack *stack, void *dst) ATTR_NONNULL()
Definition stack.cc:138
void BLI_stack_push(BLI_Stack *stack, const void *src) ATTR_NONNULL()
Definition stack.cc:132
bool BLI_stack_is_empty(const BLI_Stack *stack) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition stack.cc:250
void BLI_stack_free(BLI_Stack *stack) ATTR_NONNULL()
Definition stack.cc:96
void * BLI_stack_peek(BLI_Stack *stack) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition stack.cc:169
#define BLI_stack_new(esize, descr)
unsigned int uint
#define UNLIKELY(x)
#define ELEM(...)
#define LIKELY(x)
#define STACK_CLEAR(stack)
#define STACK_PUSH(stack, val)
#define STACK_DECLARE(stack)
#define STACK_SIZE(stack)
#define STACK_INIT(stack, stack_num)
static void split(const char *text, const char *seps, char ***str, int *count)
Read Guarded memory(de)allocation.
#define BM_DISK_EDGE_NEXT(e, v)
#define BM_FACE_FIRST_LOOP(p)
@ BM_ELEM_HIDDEN
@ BM_ELEM_TAG
#define BM_ELEM_CD_GET_VOID_P(ele, offset)
void BM_elem_attrs_copy(BMesh *bm, const BMCustomDataCopyMap &map, const BMVert *src, BMVert *dst)
void bmesh_face_swap_data(BMFace *f_a, BMFace *f_b)
void BM_face_kill(BMesh *bm, BMFace *f)
BMFace * BM_face_create(BMesh *bm, BMVert *const *verts, BMEdge *const *edges, const int len, const BMFace *f_example, const eBMCreateFlag create_flag)
BMEdge * BM_edge_create(BMesh *bm, BMVert *v1, BMVert *v2, const BMEdge *e_example, const eBMCreateFlag create_flag)
Main function for creating a new edge.
@ BM_CREATE_NOP
Definition bmesh_core.hh:28
void BMO_mesh_delete_oflag_context(BMesh *bm, const short oflag, const int type, blender::FunctionRef< void()> prepare_fn)
#define BM_elem_flag_merge_ex(ele_a, ele_b, hflag_and)
#define BM_elem_flag_disable(ele, hflag)
#define BM_elem_flag_test(ele, hflag)
#define BM_elem_flag_enable(ele, hflag)
int BM_iter_mesh_count_flag(const char itype, BMesh *bm, const char hflag, const bool value)
Mesh Iter Flag Count.
#define BM_ITER_ELEM(ele, iter, data, itype)
#define BM_ITER_MESH(ele, iter, bm, itype)
@ BM_EDGES_OF_MESH
@ BM_VERTS_OF_MESH
@ BM_FACES_OF_MESH
@ BM_LOOPS_OF_VERT
@ BM_LOOPS_OF_FACE
BMesh * bm
void BM_select_history_merge_from_targetmap(BMesh *bm, GHash *vert_map, GHash *edge_map, GHash *face_map, const bool use_chain)
BMFace * BM_face_split(BMesh *bm, BMFace *f, BMLoop *l_a, BMLoop *l_b, BMLoop **r_l, BMEdge *example, const bool no_double)
Face Split.
#define BM_EDGE
#define BM_VERT
@ DEL_ONLYTAGGED
void * BMO_slot_buffer_get_single(BMOpSlot *slot)
void BMO_slot_buffer_flag_enable(BMesh *bm, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, char htype, short oflag)
BMO_FLAG_BUFFER.
#define BMO_vert_flag_disable(bm, e, oflag)
#define BMO_edge_flag_test(bm, e, oflag)
void BMO_slot_vec_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, float r_vec[3])
#define BMO_edge_flag_enable(bm, e, oflag)
float BMO_slot_float_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
#define BMO_vert_flag_enable(bm, e, oflag)
void * BMO_iter_new(BMOIter *iter, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, char restrictmask)
New Iterator.
BMOpSlot * BMO_slot_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *identifier)
BMESH OPSTACK GET SLOT.
void BMO_op_exec(BMesh *bm, BMOperator *op)
BMESH OPSTACK EXEC OP.
#define BMO_face_flag_enable(bm, e, oflag)
void BMO_op_init(BMesh *bm, BMOperator *op, int flag, const char *opname)
BMESH OPSTACK INIT OP.
#define BMO_ITER(ele, iter, slot_args, slot_name, restrict_flag)
#define BMO_vert_flag_test(bm, e, oflag)
void BMO_op_finish(BMesh *bm, BMOperator *op)
BMESH OPSTACK FINISH OP.
bool BMO_op_callf(BMesh *bm, int flag, const char *fmt,...)
#define BMO_vert_flag_test_bool(bm, e, oflag)
#define BMO_face_flag_disable(bm, e, oflag)
bool BMO_slot_exists(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *identifier)
BMESH OPSTACK HAS SLOT.
bool BMO_slot_bool_get(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
BLI_INLINE void BMO_slot_map_elem_insert(BMOperator *op, BMOpSlot *slot, const void *element, void *val)
ATTR_WARN_UNUSED_RESULT const BMFlagLayer const short oflag
ATTR_WARN_UNUSED_RESULT const BMFlagLayer * oflags
BMFace * BM_face_exists(BMVert *const *varr, int len)
BMEdge * BM_edge_exists(BMVert *v_a, BMVert *v_b)
int BM_vert_face_count(const BMVert *v)
BMLoop * BM_face_vert_share_loop(BMFace *f, BMVert *v)
Return the Loop Shared by Face and Vertex.
BLI_INLINE bool BM_loop_is_adjacent(const BMLoop *l_a, const BMLoop *l_b) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE BMVert * BM_edge_other_vert(BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMVert * v2
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
void * BMW_begin(BMWalker *walker, void *start)
void BMW_init(BMWalker *walker, BMesh *bm, int type, short mask_vert, short mask_edge, short mask_face, BMWFlag flag, int layer)
Initialize Walker.
void BMW_end(BMWalker *walker)
End Walker.
void * BMW_step(BMWalker *walker)
Step Walker.
#define BMW_NIL_LAY
@ BMW_FLAG_NOP
#define BMW_MASK_NOP
@ BMW_VERT_SHELL
@ BMW_LOOPDATA_ISLAND
#define EDGE_MARK
Definition bmo_bridge.cc:26
#define ELE_DEL
static void bmesh_find_doubles_common(BMesh *bm, BMOperator *op, BMOperator *optarget, BMOpSlot *optarget_slot)
#define VERT_KEEP
void bmo_collapse_exec(BMesh *bm, BMOperator *op)
void bmo_average_vert_facedata_exec(BMesh *bm, BMOperator *op)
void bmo_remove_doubles_exec(BMesh *bm, BMOperator *op)
#define LOOP_MAP_VERT_INIT(l_init, v_map, is_del)
static void remdoubles_splitface(BMFace *f, BMesh *bm, BMOperator *op, BMOpSlot *slot_targetmap)
static int * bmesh_find_doubles_by_distance_connected_impl(BMesh *bm, BMVert *const *verts, const int verts_len, const float dist, const bool has_keep_vert)
static BMFace * remdoubles_createface(BMesh *bm, BMFace *f, BMOpSlot *slot_targetmap, bool *r_created)
static int * bmesh_find_doubles_by_distance_impl(BMesh *bm, BMVert *const *verts, const int verts_len, const float dist, const bool has_keep_vert)
void bmo_pointmerge_exec(BMesh *bm, BMOperator *op)
void bmo_pointmerge_facedata_exec(BMesh *bm, BMOperator *op)
#define EDGE_COL
void bmo_find_doubles_exec(BMesh *bm, BMOperator *op)
static void bmo_collapsecon_do_layer(BMesh *bm, const int layer, const short oflag)
#define VERT_IN_FACE
void bmo_collapse_uvs_exec(BMesh *bm, BMOperator *op)
void bmo_weld_verts_exec(BMesh *bm, BMOperator *op)
bool add(const Key &key, const Value &value)
Definition BLI_map.hh:295
Value lookup_default(const Key &key, const Value &default_value) const
Definition BLI_map.hh:570
bool is_empty() const
Definition BLI_stack.hh:308
void push(const T &value)
Definition BLI_stack.hh:213
void append(const T &value)
nullptr float
KDTree_3d * tree
static float verts[][3]
int count
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
T square(const T &a)
VecBase< float, 3 > float3
#define min(a, b)
Definition sort.cc:36
struct BMLoop * l
void * data
BMHeader head
struct BMVert * v
struct BMEdge * e
struct BMLoop * radial_next
struct BMLoop * prev
struct BMFace * f
struct BMLoop * next
union BMOpSlot::@313121210037300127305053354046356312170117023254 data
struct BMOpSlot slots_out[BMO_OP_MAX_SLOTS]
struct BMOpSlot slots_in[BMO_OP_MAX_SLOTS]
float co[3]
struct BMEdge * e
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251