Blender V4.3
bmesh_mods.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
12#include "MEM_guardedalloc.h"
13
14#include "BLI_math_vector.h"
15#include "BLI_vector.hh"
16
17#include "BKE_customdata.hh"
18
19#include "bmesh.hh"
21
22using blender::Vector;
23
25{
26 /* logic for 3 or more is identical */
27 const int len = BM_vert_edge_count_at_most(v, 3);
28
29 if (len == 1) {
30 BM_vert_kill(bm, v); /* will kill edges too */
31 return true;
32 }
33 if (!BM_vert_is_manifold(v)) {
34 if (!v->e) {
36 return true;
37 }
38 if (!v->e->l) {
39 if (len == 2) {
40 return (BM_vert_collapse_edge(bm, v->e, v, true, true, true) != nullptr);
41 }
42 /* used to kill the vertex here, but it may be connected to faces.
43 * so better do nothing */
44 return false;
45 }
46 return false;
47 }
48 if (len == 2 && BM_vert_face_count_is_equal(v, 1)) {
49 /* boundary vertex on a face */
50 return (BM_vert_collapse_edge(bm, v->e, v, true, true, true) != nullptr);
51 }
52 return BM_disk_dissolve(bm, v);
53}
54
56{
57 BMEdge *e, *keepedge = nullptr, *baseedge = nullptr;
58 int len = 0;
59
60 if (!BM_vert_is_manifold(v)) {
61 return false;
62 }
63
64 if (v->e) {
65 /* v->e we keep, what else */
66 e = v->e;
67 do {
69 if (!BM_edge_share_face_check(e, v->e)) {
70 keepedge = e;
71 baseedge = v->e;
72 break;
73 }
74 len++;
75 } while (e != v->e);
76 }
77
78 /* this code for handling 2 and 3-valence verts
79 * may be totally bad */
80 if (keepedge == nullptr && len == 3) {
81#if 0
82 /* handle specific case for three-valence. solve it by
83 * increasing valence to four. this may be hackish. */
84 BMLoop *l_a = BM_face_vert_share_loop(e->l->f, v);
85 BMLoop *l_b = (e->l->v == v) ? e->l->next : e->l;
86
87 if (!BM_face_split(bm, e->l->f, l_a, l_b, nullptr, nullptr, false)) {
88 return false;
89 }
90
91 if (!BM_disk_dissolve(bm, v)) {
92 return false;
93 }
94#else
95 if (UNLIKELY(!BM_faces_join_pair(bm, e->l, e->l->radial_next, true))) {
96 return false;
97 }
98 if (UNLIKELY(!BM_vert_collapse_faces(bm, v->e, v, 1.0, true, false, true, true))) {
99 return false;
100 }
101#endif
102 return true;
103 }
104 if (keepedge == nullptr && len == 2) {
105 /* collapse the vertex */
106 e = BM_vert_collapse_faces(bm, v->e, v, 1.0, true, true, true, true);
107
108 if (!e) {
109 return false;
110 }
111
112 /* handle two-valence */
113 if (e->l != e->l->radial_next) {
114 if (!BM_faces_join_pair(bm, e->l, e->l->radial_next, true)) {
115 return false;
116 }
117 }
118
119 return true;
120 }
121
122 if (keepedge) {
123 bool done = false;
124
125 while (!done) {
126 done = true;
127 e = v->e;
128 do {
129 BMFace *f = nullptr;
130 if (BM_edge_is_manifold(e) && (e != baseedge) && (e != keepedge)) {
131 f = BM_faces_join_pair(bm, e->l, e->l->radial_next, true);
132 /* return if couldn't join faces in manifold
133 * conditions */
134 /* !disabled for testing why bad things happen */
135 if (!f) {
136 return false;
137 }
138 }
139
140 if (f) {
141 done = false;
142 break;
143 }
144 } while ((e = bmesh_disk_edge_next(e, v)) != v->e);
145 }
146
147 /* collapse the vertex */
148 /* NOTE: the baseedge can be a boundary of manifold, use this as join_faces arg. */
150 bm, baseedge, v, 1.0, true, !BM_edge_is_boundary(baseedge), true, true);
151
152 if (!e) {
153 return false;
154 }
155
156 if (e->l) {
157 /* get remaining two faces */
158 if (e->l != e->l->radial_next) {
159 /* join two remaining faces */
160 if (!BM_faces_join_pair(bm, e->l, e->l->radial_next, true)) {
161 return false;
162 }
163 }
164 }
165 }
166
167 return true;
168}
169
170BMFace *BM_faces_join_pair(BMesh *bm, BMLoop *l_a, BMLoop *l_b, const bool do_del)
171{
172 BLI_assert((l_a != l_b) && (l_a->e == l_b->e));
173
174 if (l_a->v == l_b->v) {
175 const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
176 bmesh_kernel_loop_reverse(bm, l_b->f, cd_loop_mdisp_offset, true);
177 }
178
179 BMFace *faces[2] = {l_a->f, l_b->f};
180 return BM_faces_join(bm, faces, 2, do_del);
181}
182
184 BMFace *f,
185 BMLoop *l_a,
186 BMLoop *l_b,
187 BMLoop **r_l,
188 BMEdge *example,
189 const bool no_double)
190{
191 const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
192 BMFace *f_new, *f_tmp;
193
194 BLI_assert(l_a != l_b);
195 BLI_assert(f == l_a->f && f == l_b->f);
197
198 /* could be an assert */
199 if (UNLIKELY(BM_loop_is_adjacent(l_a, l_b)) || UNLIKELY(f != l_a->f || f != l_b->f)) {
200 if (r_l) {
201 *r_l = nullptr;
202 }
203 return nullptr;
204 }
205
206 /* do we have a multires layer? */
207 if (cd_loop_mdisp_offset != -1) {
208 f_tmp = BM_face_copy(bm, f, false, false);
209 }
210
211#ifdef USE_BMESH_HOLES
212 f_new = bmesh_kernel_split_face_make_edge(bm, f, l_a, l_b, r_l, nullptr, example, no_double);
213#else
214 f_new = bmesh_kernel_split_face_make_edge(bm, f, l_a, l_b, r_l, example, no_double);
215#endif
216
217 if (f_new) {
218 /* handle multires update */
219 if (cd_loop_mdisp_offset != -1) {
220 float f_dst_center[3];
221 float f_src_center[3];
222
223 BM_face_calc_center_median(f_tmp, f_src_center);
224
225 BM_face_calc_center_median(f, f_dst_center);
226 BM_face_interp_multires_ex(bm, f, f_tmp, f_dst_center, f_src_center, cd_loop_mdisp_offset);
227
228 BM_face_calc_center_median(f_new, f_dst_center);
230 bm, f_new, f_tmp, f_dst_center, f_src_center, cd_loop_mdisp_offset);
231
232#if 0
233 /* BM_face_multires_bounds_smooth doesn't flip displacement correct */
236#endif
237 }
238 }
239
240 if (cd_loop_mdisp_offset != -1) {
241 BM_face_kill(bm, f_tmp);
242 }
243
244 return f_new;
245}
246
248 BMFace *f,
249 BMLoop *l_a,
250 BMLoop *l_b,
251 float cos[][3],
252 int n,
253 BMLoop **r_l,
254 BMEdge *example)
255{
256 BMFace *f_new, *f_tmp;
257 BMLoop *l_new;
258 BMEdge *e, *e_new;
259 BMVert *v_new;
260 // BMVert *v_a = l_a->v; /* UNUSED */
261 BMVert *v_b = l_b->v;
262 int i, j;
263
264 BLI_assert(l_a != l_b);
265 BLI_assert(f == l_a->f && f == l_b->f);
266 BLI_assert(!((n == 0) && BM_loop_is_adjacent(l_a, l_b)));
267
268 /* could be an assert */
269 if (UNLIKELY((n == 0) && BM_loop_is_adjacent(l_a, l_b)) || UNLIKELY(l_a->f != l_b->f)) {
270 if (r_l) {
271 *r_l = nullptr;
272 }
273 return nullptr;
274 }
275
276 f_tmp = BM_face_copy(bm, f, true, true);
277
278#ifdef USE_BMESH_HOLES
279 f_new = bmesh_kernel_split_face_make_edge(bm, f, l_a, l_b, &l_new, nullptr, example, false);
280#else
281 f_new = bmesh_kernel_split_face_make_edge(bm, f, l_a, l_b, &l_new, example, false);
282#endif
283 /* bmesh_kernel_split_face_make_edge returns in 'l_new'
284 * a Loop for f_new going from 'v_a' to 'v_b'.
285 * The radial_next is for 'f' and goes from 'v_b' to 'v_a'. */
286
287 if (f_new) {
288 e = l_new->e;
289 for (i = 0; i < n; i++) {
290 v_new = bmesh_kernel_split_edge_make_vert(bm, v_b, e, &e_new);
291 BLI_assert(v_new != nullptr);
292 /* bmesh_kernel_split_edge_make_vert returns in 'e_new'
293 * the edge going from 'v_new' to 'v_b'. */
294 copy_v3_v3(v_new->co, cos[i]);
295
296 /* interpolate the loop data for the loops with (v == v_new), using orig face */
297 for (j = 0; j < 2; j++) {
298 BMEdge *e_iter = (j == 0) ? e : e_new;
299 BMLoop *l_iter = e_iter->l;
300 do {
301 if (l_iter->v == v_new) {
302 /* this interpolates both loop and vertex data */
303 BM_loop_interp_from_face(bm, l_iter, f_tmp, true, true);
304 }
305 } while ((l_iter = l_iter->radial_next) != e_iter->l);
306 }
307 e = e_new;
308 }
309 }
310
311 BM_face_verts_kill(bm, f_tmp);
312
313 if (r_l) {
314 *r_l = l_new;
315 }
316
317 return f_new;
318}
319
321 BMEdge *e_kill,
322 BMVert *v_kill,
323 float fac,
324 const bool do_del,
325 const bool join_faces,
326 const bool kill_degenerate_faces,
327 const bool kill_duplicate_faces)
328{
329 BMEdge *e_new = nullptr;
330 BMVert *tv = BM_edge_other_vert(e_kill, v_kill);
331
332 BMEdge *e2;
333 BMVert *tv2;
334
335 /* Only intended to be called for 2-valence vertices */
336 BLI_assert(bmesh_disk_count(v_kill) <= 2);
337
338 /* first modify the face loop data */
339
340 if (e_kill->l) {
341 BMLoop *l_iter;
342 const float w[2] = {1.0f - fac, fac};
343
344 l_iter = e_kill->l;
345 do {
346 if (l_iter->v == tv && l_iter->next->v == v_kill) {
347 const void *src[2];
348 BMLoop *tvloop = l_iter;
349 BMLoop *kvloop = l_iter->next;
350
351 src[0] = kvloop->head.data;
352 src[1] = tvloop->head.data;
353 CustomData_bmesh_interp(&bm->ldata, src, w, nullptr, 2, kvloop->head.data);
354 }
355 } while ((l_iter = l_iter->radial_next) != e_kill->l);
356 }
357
358 /* now interpolate the vertex data */
359 BM_data_interp_from_verts(bm, v_kill, tv, v_kill, fac);
360
361 e2 = bmesh_disk_edge_next(e_kill, v_kill);
362 tv2 = BM_edge_other_vert(e2, v_kill);
363
364 if (join_faces) {
365 BMIter fiter;
366 BMFace *f;
367
369 BM_ITER_ELEM (f, &fiter, v_kill, BM_FACES_OF_VERT) {
370 faces.append(f);
371 }
372
373 if (faces.size() >= 2) {
374 BMFace *f2 = BM_faces_join(bm, faces.data(), faces.size(), true);
375 if (f2) {
376 BMLoop *l_a, *l_b;
377
378 if ((l_a = BM_face_vert_share_loop(f2, tv)) && (l_b = BM_face_vert_share_loop(f2, tv2))) {
379 BMLoop *l_new;
380
381 if (BM_face_split(bm, f2, l_a, l_b, &l_new, nullptr, false)) {
382 e_new = l_new->e;
383 }
384 }
385 }
386 }
387 }
388 else {
389 /* single face or no faces */
390 /* same as BM_vert_collapse_edge() however we already
391 * have vars to perform this operation so don't call. */
393 bm, e_kill, v_kill, do_del, true, kill_degenerate_faces, kill_duplicate_faces);
394
395 // e_new = BM_edge_exists(tv, tv2); /* Same as return above. */
396 }
397
398 return e_new;
399}
400
402 BMEdge *e_kill,
403 BMVert *v_kill,
404 const bool do_del,
405 const bool kill_degenerate_faces,
406 const bool kill_duplicate_faces)
407{
408/* nice example implementation but we want loops to have their customdata
409 * accounted for */
410#if 0
411 BMEdge *e_new = nullptr;
412
413 /* Collapse between 2 edges */
414
415 /* in this case we want to keep all faces and not join them,
416 * rather just get rid of the vertex - see bug #28645. */
417 BMVert *tv = BM_edge_other_vert(e_kill, v_kill);
418 if (tv) {
419 BMEdge *e2 = bmesh_disk_edge_next(e_kill, v_kill);
420 if (e2) {
421 BMVert *tv2 = BM_edge_other_vert(e2, v_kill);
422 if (tv2) {
423 /* only action, other calls here only get the edge to return */
425 bm, e_kill, v_kill, do_del, true, kill_degenerate_faces);
426 }
427 }
428 }
429
430 return e_new;
431#else
432 /* with these args faces are never joined, same as above
433 * but account for loop customdata */
435 bm, e_kill, v_kill, 1.0f, do_del, false, kill_degenerate_faces, kill_duplicate_faces);
436#endif
437}
438
439#undef DO_V_INTERP
440
442 BMesh *bm, BMEdge *e_kill, BMVert *v_kill, const bool do_del, const bool kill_degenerate_faces)
443{
444 return bmesh_kernel_join_vert_kill_edge(bm, e_kill, v_kill, do_del, true, kill_degenerate_faces);
445}
446
448{
449 BMVert *v_new, *v_other;
450 BMEdge *e_new;
451
452 Vector<BMFace *, 32> oldfaces;
453 const int cd_loop_mdisp_offset = BM_edge_is_wire(e) ?
454 -1 :
456
457 BLI_assert(BM_vert_in_edge(e, v) == true);
458
459 /* do we have a multi-res layer? */
460 if (cd_loop_mdisp_offset != -1) {
461 BMLoop *l = e->l;
462 do {
463 oldfaces.append(l->f);
464 l = l->radial_next;
465 } while (l != e->l);
466
467 /* flag existing faces so we can differentiate oldfaces from new faces */
468 for (int64_t i = 0; i < oldfaces.size(); i++) {
470 oldfaces[i] = BM_face_copy(bm, oldfaces[i], true, true);
472 }
473 }
474
475 v_other = BM_edge_other_vert(e, v);
476 v_new = bmesh_kernel_split_edge_make_vert(bm, v, e, &e_new);
477 if (r_e != nullptr) {
478 *r_e = e_new;
479 }
480
481 BLI_assert(v_new != nullptr);
482 BLI_assert(BM_vert_in_edge(e_new, v) && BM_vert_in_edge(e_new, v_new));
483 BLI_assert(BM_vert_in_edge(e, v_new) && BM_vert_in_edge(e, v_other));
484
485 sub_v3_v3v3(v_new->co, v_other->co, v->co);
486 madd_v3_v3v3fl(v_new->co, v->co, v_new->co, fac);
487
488 e_new->head.hflag = e->head.hflag;
489 BM_elem_attrs_copy(bm, e, e_new);
490
491 /* v->v_new->v2 */
492 BM_data_interp_face_vert_edge(bm, v_other, v, v_new, e, fac);
493 BM_data_interp_from_verts(bm, v, v_other, v_new, fac);
494
495 if (cd_loop_mdisp_offset != -1) {
496 /* interpolate new/changed loop data from copied old faces */
497 for (BMFace *oldface : oldfaces) {
498 float f_center_old[3];
499
500 BM_face_calc_center_median(oldface, f_center_old);
501
502 for (int j = 0; j < 2; j++) {
503 BMEdge *e1 = j ? e_new : e;
504 BMLoop *l = e1->l;
505
506 if (UNLIKELY(!l)) {
507 BMESH_ASSERT(0);
508 break;
509 }
510
511 do {
512 /* check this is an old face */
514 float f_center[3];
515
516 BM_face_calc_center_median(l->f, f_center);
518 bm, l->f, oldface, f_center, f_center_old, cd_loop_mdisp_offset);
519 }
520 l = l->radial_next;
521 } while (l != e1->l);
522 }
523 }
524
525 /* destroy the old faces */
526 for (BMFace *oldface : oldfaces) {
527 BM_face_verts_kill(bm, oldface);
528 }
529
530/* fix boundaries a bit, doesn't work too well quite yet */
531#if 0
532 for (int j = 0; j < 2; j++) {
533 BMEdge *e1 = j ? e_new : e;
534 BMLoop *l, *l2;
535
536 l = e1->l;
537 if (UNLIKELY(!l)) {
538 BMESH_ASSERT(0);
539 break;
540 }
541
542 do {
544 l = l->radial_next;
545 } while (l != e1->l);
546 }
547#endif
548 }
549
550 return v_new;
551}
552
553BMVert *BM_edge_split_n(BMesh *bm, BMEdge *e, int numcuts, BMVert **r_varr)
554{
555 int i;
556 float percent;
557 BMVert *v_new = nullptr;
558
559 for (i = 0; i < numcuts; i++) {
560 percent = 1.0f / float(numcuts + 1 - i);
561 v_new = BM_edge_split(bm, e, e->v2, nullptr, percent);
562 if (r_varr) {
563 /* fill in reverse order (v1 -> v2) */
564 r_varr[numcuts - i - 1] = v_new;
565 }
566 }
567 return v_new;
568}
569
571{
572 std::swap(e->v1, e->v2);
573 std::swap(e->v1_disk_link, e->v2_disk_link);
574}
575
576void BM_edge_calc_rotate(BMEdge *e, const bool ccw, BMLoop **r_l1, BMLoop **r_l2)
577{
578 BMVert *v1, *v2;
579 BMFace *fa, *fb;
580
581 /* this should have already run */
583
584 /* we know this will work */
585 BM_edge_face_pair(e, &fa, &fb);
586
587 /* so we can use `ccw` variable correctly,
588 * otherwise we could use the edges verts direct */
589 BM_edge_ordered_verts(e, &v1, &v2);
590
591 /* we could swap the verts _or_ the faces, swapping faces
592 * gives more predictable results since that way the next vert
593 * just stitches from face fa / fb */
594 if (!ccw) {
595 std::swap(fa, fb);
596 }
597
598 *r_l1 = BM_face_other_vert_loop(fb, v2, v1);
599 *r_l2 = BM_face_other_vert_loop(fa, v1, v2);
600}
601
603{
604 BMFace *fa, *fb;
605 if (BM_edge_face_pair(e, &fa, &fb)) {
606 BMLoop *la, *lb;
607
608 la = BM_face_other_vert_loop(fa, e->v2, e->v1);
609 lb = BM_face_other_vert_loop(fb, e->v2, e->v1);
610
611 /* check that the next vert in both faces isn't the same
612 * (ie - the next edge doesn't share the same faces).
613 * since we can't rotate usefully in this case. */
614 if (la->v == lb->v) {
615 return false;
616 }
617
618 /* mirror of the check above but in the opposite direction */
619 la = BM_face_other_vert_loop(fa, e->v1, e->v2);
620 lb = BM_face_other_vert_loop(fb, e->v1, e->v2);
621
622 if (la->v == lb->v) {
623 return false;
624 }
625
626 return true;
627 }
628 return false;
629}
630
632{
633 /* NOTE: for these vars 'old' just means initial edge state. */
634
635 float ed_dir_old[3]; /* edge vector */
636 float ed_dir_new[3]; /* edge vector */
637 float ed_dir_new_flip[3]; /* edge vector */
638
639 float ed_dir_v1_old[3];
640 float ed_dir_v2_old[3];
641
642 float ed_dir_v1_new[3];
643 float ed_dir_v2_new[3];
644
645 float cross_old[3];
646 float cross_new[3];
647
648 /* original verts - these will be in the edge 'e' */
649 BMVert *v1_old, *v2_old;
650
651 /* verts from the loops passed */
652
653 BMVert *v1, *v2;
654 /* These are the opposite verts - the verts that _would_ be used if `ccw` was inverted. */
655 BMVert *v1_alt, *v2_alt;
656
657 /* this should have already run */
659
660 BM_edge_ordered_verts(e, &v1_old, &v2_old);
661
662 v1 = l1->v;
663 v2 = l2->v;
664
665 /* get the next vert along */
666 v1_alt = BM_face_other_vert_loop(l1->f, v1_old, v1)->v;
667 v2_alt = BM_face_other_vert_loop(l2->f, v2_old, v2)->v;
668
669 /* normalize all so comparisons are scale independent */
670
671 BLI_assert(BM_edge_exists(v1_old, v1));
672 BLI_assert(BM_edge_exists(v1, v1_alt));
673
674 BLI_assert(BM_edge_exists(v2_old, v2));
675 BLI_assert(BM_edge_exists(v2, v2_alt));
676
677 /* old and new edge vecs */
678 sub_v3_v3v3(ed_dir_old, v1_old->co, v2_old->co);
679 sub_v3_v3v3(ed_dir_new, v1->co, v2->co);
680 normalize_v3(ed_dir_old);
681 normalize_v3(ed_dir_new);
682
683 /* old edge corner vecs */
684 sub_v3_v3v3(ed_dir_v1_old, v1_old->co, v1->co);
685 sub_v3_v3v3(ed_dir_v2_old, v2_old->co, v2->co);
686 normalize_v3(ed_dir_v1_old);
687 normalize_v3(ed_dir_v2_old);
688
689 /* old edge corner vecs */
690 sub_v3_v3v3(ed_dir_v1_new, v1->co, v1_alt->co);
691 sub_v3_v3v3(ed_dir_v2_new, v2->co, v2_alt->co);
692 normalize_v3(ed_dir_v1_new);
693 normalize_v3(ed_dir_v2_new);
694
695 /* compare */
696 cross_v3_v3v3(cross_old, ed_dir_old, ed_dir_v1_old);
697 cross_v3_v3v3(cross_new, ed_dir_new, ed_dir_v1_new);
698 if (dot_v3v3(cross_old, cross_new) < 0.0f) { /* does this flip? */
699 return false;
700 }
701 cross_v3_v3v3(cross_old, ed_dir_old, ed_dir_v2_old);
702 cross_v3_v3v3(cross_new, ed_dir_new, ed_dir_v2_new);
703 if (dot_v3v3(cross_old, cross_new) < 0.0f) { /* does this flip? */
704 return false;
705 }
706
707 negate_v3_v3(ed_dir_new_flip, ed_dir_new);
708
709 /* result is zero area corner */
710 if ((dot_v3v3(ed_dir_new, ed_dir_v1_new) > 0.999f) ||
711 (dot_v3v3(ed_dir_new_flip, ed_dir_v2_new) > 0.999f))
712 {
713 return false;
714 }
715
716 return true;
717}
718
720{
721 /* Stupid check for now:
722 * Could compare angles of surrounding edges
723 * before & after, but this is OK. */
724 return (len_squared_v3v3(e->v1->co, e->v2->co) > len_squared_v3v3(l1->v->co, l2->v->co));
725}
726
727BMEdge *BM_edge_rotate(BMesh *bm, BMEdge *e, const bool ccw, const short check_flag)
728{
729 BMVert *v1, *v2;
730 BMLoop *l1, *l2;
731 BMFace *f;
732 BMEdge *e_new = nullptr;
733 char f_active_prev = 0;
734 char f_hflag_prev_1;
735 char f_hflag_prev_2;
736
737 if (!BM_edge_rotate_check(e)) {
738 return nullptr;
739 }
740
741 BM_edge_calc_rotate(e, ccw, &l1, &l2);
742
743 /* the loops will be freed so assign verts */
744 v1 = l1->v;
745 v2 = l2->v;
746
747 /* --------------------------------------- */
748 /* Checking Code - make sure we can rotate */
749
750 if (check_flag & BM_EDGEROT_CHECK_BEAUTY) {
751 if (!BM_edge_rotate_check_beauty(e, l1, l2)) {
752 return nullptr;
753 }
754 }
755
756 /* check before applying */
757 if (check_flag & BM_EDGEROT_CHECK_EXISTS) {
758 if (BM_edge_exists(v1, v2)) {
759 return nullptr;
760 }
761 }
762
763 /* slowest, check last */
764 if (check_flag & BM_EDGEROT_CHECK_DEGENERATE) {
765 if (!BM_edge_rotate_check_degenerate(e, l1, l2)) {
766 return nullptr;
767 }
768 }
769 /* Done Checking */
770 /* ------------- */
771
772 /* --------------- */
773 /* Rotate The Edge */
774
775 /* first create the new edge, this is so we can copy the customdata from the old one
776 * if splice if disabled, always add in a new edge even if there's one there. */
777 e_new = BM_edge_create(
779
780 f_hflag_prev_1 = l1->f->head.hflag;
781 f_hflag_prev_2 = l2->f->head.hflag;
782
783 /* maintain active face */
784 if (bm->act_face == l1->f) {
785 f_active_prev = 1;
786 }
787 else if (bm->act_face == l2->f) {
788 f_active_prev = 2;
789 }
790
791 const bool is_flipped = !BM_edge_is_contiguous(e);
792
793 /* don't delete the edge, manually remove the edge after so we can copy its attributes */
796
797 if (f == nullptr) {
798 return nullptr;
799 }
800
801 /* NOTE: this assumes joining the faces _didnt_ also remove the verts.
802 * the #BM_edge_rotate_check will ensure this, but its possibly corrupt state or future edits
803 * break this */
804 if ((l1 = BM_face_vert_share_loop(f, v1)) && (l2 = BM_face_vert_share_loop(f, v2)) &&
805 BM_face_split(bm, f, l1, l2, nullptr, nullptr, true))
806 {
807 /* we should really be able to know the faces some other way,
808 * rather than fetching them back from the edge, but this is predictable
809 * where using the return values from face split isn't. - campbell */
810 BMFace *fa, *fb;
811 if (BM_edge_face_pair(e_new, &fa, &fb)) {
812 fa->head.hflag = f_hflag_prev_1;
813 fb->head.hflag = f_hflag_prev_2;
814
815 if (f_active_prev == 1) {
816 bm->act_face = fa;
817 }
818 else if (f_active_prev == 2) {
819 bm->act_face = fb;
820 }
821
822 if (is_flipped) {
824
825 if (ccw) {
826 /* Needed otherwise `ccw` toggles direction */
827 e_new->l = e_new->l->radial_next;
828 }
829 }
830 }
831 }
832 else {
833 return nullptr;
834 }
835
836 return e_new;
837}
838
843
848
850{
851 return bmesh_kernel_unglue_region_make_vert_multi(bm, larr, larr_len);
852}
CustomData interface, see also DNA_customdata_types.h.
int CustomData_get_offset(const CustomData *data, eCustomDataType type)
void CustomData_bmesh_interp(CustomData *data, const void **src_blocks, const float *weights, const float *sub_weights, int count, void *dst_block)
#define BLI_assert(a)
Definition BLI_assert.h:50
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
MINLINE void negate_v3_v3(float r[3], const float a[3])
MINLINE float dot_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void cross_v3_v3v3(float r[3], const float a[3], const float b[3])
MINLINE void madd_v3_v3v3fl(float r[3], const float a[3], const float b[3], float f)
MINLINE float normalize_v3(float n[3])
#define UNLIKELY(x)
Read Guarded memory(de)allocation.
void BM_elem_attrs_copy(BMesh *bm, const BMCustomDataCopyMap &map, const BMVert *src, BMVert *dst)
BMVert * bmesh_kernel_unglue_region_make_vert_multi_isolated(BMesh *bm, BMLoop *l_sep)
BMFace * BM_faces_join(BMesh *bm, BMFace **faces, int totface, const bool do_del)
Join Connected Faces.
BMVert * bmesh_kernel_split_edge_make_vert(BMesh *bm, BMVert *tv, BMEdge *e, BMEdge **r_e)
Split Edge Make Vert (SEMV)
void BM_face_verts_kill(BMesh *bm, BMFace *f)
void BM_vert_kill(BMesh *bm, BMVert *v)
void BM_face_kill(BMesh *bm, BMFace *f)
BMEdge * bmesh_kernel_join_edge_kill_vert(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, const bool do_del, const bool check_edge_exists, const bool kill_degenerate_faces, const bool kill_duplicate_faces)
Join Edge Kill Vert (JEKV)
BMVert * bmesh_kernel_unglue_region_make_vert_multi(BMesh *bm, BMLoop **larr, int larr_len)
BMVert * bmesh_kernel_unglue_region_make_vert(BMesh *bm, BMLoop *l_sep)
Un-glue Region Make Vert (URMV)
BMFace * BM_face_copy(BMesh *bm_dst, const BMCustomDataCopyMap &cd_face_map, const BMCustomDataCopyMap &cd_loop_map, BMFace *f, const bool copy_verts, const bool copy_edges)
BMVert * bmesh_kernel_join_vert_kill_edge(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, const bool do_del, const bool check_edge_exists, const bool kill_degenerate_faces)
Join Vert Kill Edge (JVKE)
void bmesh_kernel_loop_reverse(BMesh *bm, BMFace *f, const int cd_loop_mdisp_offset, const bool use_loop_mdisp_flip)
Loop Reverse.
BMFace * bmesh_kernel_split_face_make_edge(BMesh *bm, BMFace *f, BMLoop *l_v1, BMLoop *l_v2, BMLoop **r_l, BMEdge *e_example, const bool no_double)
Split Face Make Edge (SFME)
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:24
@ BM_CREATE_NO_DOUBLE
Definition bmesh_core.hh:26
#define BMESH_ASSERT(a)
void BM_face_interp_multires_ex(BMesh *bm, BMFace *f_dst, const BMFace *f_src, const float f_dst_center[3], const float f_src_center[3], const int cd_loop_mdisp_offset)
void BM_face_multires_bounds_smooth(BMesh *bm, BMFace *f)
void BM_loop_interp_from_face(BMesh *bm, BMLoop *l_dst, const BMFace *f_src, const bool do_vertex, const bool do_multires)
void BM_data_interp_face_vert_edge(BMesh *bm, const BMVert *v_src_1, const BMVert *, BMVert *v, BMEdge *e, const float fac)
Data Face-Vert Edge Interpolate.
void BM_data_interp_from_verts(BMesh *bm, const BMVert *v_src_1, const BMVert *v_src_2, BMVert *v_dst, const float fac)
Data, Interpolate From Verts.
#define BM_ITER_ELEM(ele, iter, data, itype)
@ BM_FACES_OF_VERT
ATTR_WARN_UNUSED_RESULT BMesh * bm
BMFace * BM_face_split_n(BMesh *bm, BMFace *f, BMLoop *l_a, BMLoop *l_b, float cos[][3], int n, BMLoop **r_l, BMEdge *example)
Face Split with intermediate points.
BMVert * BM_face_loop_separate(BMesh *bm, BMLoop *l_sep)
Rip a single face from a vertex fan.
void BM_edge_calc_rotate(BMEdge *e, const bool ccw, BMLoop **r_l1, BMLoop **r_l2)
BMVert * BM_face_loop_separate_multi(BMesh *bm, BMLoop **larr, int larr_len)
bool BM_disk_dissolve(BMesh *bm, BMVert *v)
Definition bmesh_mods.cc:55
BMEdge * BM_vert_collapse_faces(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, float fac, const bool do_del, const bool join_faces, const bool kill_degenerate_faces, const bool kill_duplicate_faces)
Vert Collapse Faces.
bool BM_edge_rotate_check_degenerate(BMEdge *e, BMLoop *l1, BMLoop *l2)
Check if Edge Rotate Gives Degenerate Faces.
bool BM_vert_dissolve(BMesh *bm, BMVert *v)
Dissolve Vert.
Definition bmesh_mods.cc:24
bool BM_edge_rotate_check_beauty(BMEdge *e, BMLoop *l1, BMLoop *l2)
BMVert * BM_edge_split(BMesh *bm, BMEdge *e, BMVert *v, BMEdge **r_e, float fac)
Edge Split.
BMEdge * BM_vert_collapse_edge(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, const bool do_del, const bool kill_degenerate_faces, const bool kill_duplicate_faces)
Vert Collapse Faces.
bool BM_edge_rotate_check(BMEdge *e)
Check if Rotate Edge is OK.
BMEdge * BM_edge_rotate(BMesh *bm, BMEdge *e, const bool ccw, const short check_flag)
Rotate Edge.
void BM_edge_verts_swap(BMEdge *e)
BMVert * BM_face_loop_separate_multi_isolated(BMesh *bm, BMLoop *l_sep)
BMFace * BM_faces_join_pair(BMesh *bm, BMLoop *l_a, BMLoop *l_b, const bool do_del)
Faces Join Pair.
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.
BMVert * BM_edge_collapse(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, const bool do_del, const bool kill_degenerate_faces)
BMVert * BM_edge_split_n(BMesh *bm, BMEdge *e, int numcuts, BMVert **r_varr)
Split an edge multiple times evenly.
@ BM_EDGEROT_CHECK_EXISTS
@ BM_EDGEROT_CHECK_BEAUTY
@ BM_EDGEROT_CHECK_SPLICE
@ BM_EDGEROT_CHECK_DEGENERATE
void BM_face_normal_flip(BMesh *bm, BMFace *f)
void BM_face_calc_center_median(const BMFace *f, float r_cent[3])
#define BM_ELEM_API_FLAG_DISABLE(element, f)
#define BM_ELEM_API_FLAG_TEST(element, f)
@ _FLAG_OVERLAP
#define BM_ELEM_API_FLAG_ENABLE(element, f)
int bmesh_disk_count(const BMVert *v)
void BM_edge_ordered_verts(const BMEdge *edge, BMVert **r_v1, BMVert **r_v2)
int BM_vert_edge_count_at_most(const BMVert *v, const int count_max)
bool BM_vert_is_manifold(const BMVert *v)
bool BM_edge_face_pair(BMEdge *e, BMFace **r_fa, BMFace **r_fb)
BMEdge * BM_edge_exists(BMVert *v_a, BMVert *v_b)
BMLoop * BM_face_other_vert_loop(BMFace *f, BMVert *v_prev, BMVert *v)
Other Loop in Face Sharing a Vertex.
bool BM_edge_share_face_check(BMEdge *e1, BMEdge *e2)
BMLoop * BM_face_edge_share_loop(BMFace *f, BMEdge *e)
Return the Loop Shared by Face and Edge.
BMLoop * BM_face_vert_share_loop(BMFace *f, BMVert *v)
Return the Loop Shared by Face and Vertex.
BLI_INLINE bool BM_edge_is_contiguous(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE bool BM_edge_is_manifold(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE bool BM_edge_is_boundary(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
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()
#define BM_vert_face_count_is_equal(v, n)
BLI_INLINE bool BM_edge_is_wire(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE bool BM_vert_in_edge(const 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 BMLoop * l_b
ATTR_WARN_UNUSED_RESULT const BMVert * v
BLI_INLINE BMEdge * bmesh_disk_edge_next(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition btQuadWord.h:119
int64_t size() const
void append(const T &value)
int len
draw_view in_light_buf[] float
BLI_INLINE float fb(float length, float L)
ccl_device_inline float3 cos(float3 v)
static char faces[256]
__int64 int64_t
Definition stdint.h:89
BMHeader head
struct BMLoop * l
BMHeader head
void * data
BMHeader head
struct BMVert * v
struct BMEdge * e
struct BMLoop * radial_next
struct BMFace * f
struct BMLoop * next
float co[3]
struct BMEdge * e
BMHeader head
BMFace * act_face
CustomData ldata