diff options
Diffstat (limited to 'source/blender/bmesh/intern/bmesh_core.c')
-rw-r--r-- | source/blender/bmesh/intern/bmesh_core.c | 548 |
1 files changed, 420 insertions, 128 deletions
diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c index e67aa1da340..f7e709ce9cc 100644 --- a/source/blender/bmesh/intern/bmesh_core.c +++ b/source/blender/bmesh/intern/bmesh_core.c @@ -37,6 +37,7 @@ #include "BLT_translation.h" #include "BKE_DerivedMesh.h" +#include "BKE_mesh.h" #include "bmesh.h" #include "intern/bmesh_private.h" @@ -206,6 +207,11 @@ BMEdge *BM_edge_create( return e; } +/** + * \note In most cases a \a l_example should be NULL, + * since this is a low level API and we shouldn't attempt to be clever and guess whats intended. + * In cases where copying adjacent loop-data is useful, see #BM_face_copy_shared. + */ static BMLoop *bm_loop_create( BMesh *bm, BMVert *v, BMEdge *e, BMFace *f, const BMLoop *l_example, const eBMCreateFlag create_flag) @@ -217,6 +223,14 @@ static BMLoop *bm_loop_create( BLI_assert((l_example == NULL) || (l_example->head.htype == BM_LOOP)); BLI_assert(!(create_flag & 1)); +#ifndef NDEBUG + if (l_example) { + /* ensure passing a loop is either sharing the same vertex, or entirely disconnected + * use to catch mistake passing in loop offset-by-one. */ + BLI_assert((v == l_example->v) || !ELEM(v, l_example->prev->v, l_example->next->v)); + } +#endif + /* --- assign all members --- */ l->head.data = NULL; @@ -247,6 +261,9 @@ static BMLoop *bm_loop_create( if (!(create_flag & BM_CREATE_SKIP_CD)) { if (l_example) { + /* no need to copy attrs, just handle customdata */ + // BM_elem_attrs_copy(bm, bm, l_example, l); + CustomData_bmesh_free_block_data(&bm->ldata, l->head.data); CustomData_bmesh_copy_data(&bm->ldata, &bm->ldata, l_example->head.data, &l->head.data); } else { @@ -264,7 +281,7 @@ static BMLoop *bm_face_boundary_add( #ifdef USE_BMESH_HOLES BMLoopList *lst = BLI_mempool_calloc(bm->looplistpool); #endif - BMLoop *l = bm_loop_create(bm, startv, starte, f, starte->l, create_flag); + BMLoop *l = bm_loop_create(bm, startv, starte, f, NULL /* starte->l */, create_flag); bmesh_radial_append(starte, l); @@ -439,7 +456,7 @@ BMFace *BM_face_create( startl->v = verts[0]; startl->e = edges[0]; for (i = 1; i < len; i++) { - l = bm_loop_create(bm, verts[i], edges[i], f, edges[i]->l, create_flag); + l = bm_loop_create(bm, verts[i], edges[i], f, NULL /* edges[i]->l */, create_flag); l->f = f; bmesh_radial_append(edges[i], l); @@ -503,47 +520,84 @@ BMFace *BM_face_create_verts( /** * Check the element is valid. * - * BMESH_TODO, when this raises an error the output is incredible confusing. - * need to have some nice way to print/debug what the hecks going on. + * BMESH_TODO, when this raises an error the output is incredibly confusing. + * need to have some nice way to print/debug what the heck's going on. */ int bmesh_elem_check(void *element, const char htype) { BMHeader *head = element; - int err = 0; + enum { + IS_NULL = (1 << 0), + IS_WRONG_TYPE = (1 << 1), + + IS_VERT_WRONG_EDGE_TYPE = (1 << 2), + + IS_EDGE_NULL_DISK_LINK = (1 << 3), + IS_EDGE_WRONG_LOOP_TYPE = (1 << 4), + IS_EDGE_WRONG_FACE_TYPE = (1 << 5), + IS_EDGE_NULL_RADIAL_LINK = (1 << 6), + IS_EDGE_ZERO_FACE_LENGTH = (1 << 7), + + IS_LOOP_WRONG_FACE_TYPE = (1 << 8), + IS_LOOP_WRONG_EDGE_TYPE = (1 << 9), + IS_LOOP_WRONG_VERT_TYPE = (1 << 10), + IS_LOOP_VERT_NOT_IN_EDGE = (1 << 11), + IS_LOOP_NULL_CYCLE_LINK = (1 << 12), + IS_LOOP_ZERO_FACE_LENGTH = (1 << 13), + IS_LOOP_WRONG_FACE_LENGTH = (1 << 14), + IS_LOOP_WRONG_RADIAL_LENGTH = (1 << 15), + + IS_FACE_NULL_LOOP = (1 << 16), + IS_FACE_WRONG_LOOP_FACE = (1 << 17), + IS_FACE_NULL_EDGE = (1 << 18), + IS_FACE_NULL_VERT = (1 << 19), + IS_FACE_LOOP_VERT_NOT_IN_EDGE = (1 << 20), + IS_FACE_LOOP_WRONG_RADIAL_LENGTH = (1 << 21), + IS_FACE_LOOP_WRONG_DISK_LENGTH = (1 << 22), + IS_FACE_LOOP_DUPE_LOOP = (1 << 23), + IS_FACE_LOOP_DUPE_VERT = (1 << 24), + IS_FACE_LOOP_DUPE_EDGE = (1 << 25), + IS_FACE_WRONG_LENGTH = (1 << 26), + } err = 0; if (!element) - return (1 << 0); + return IS_NULL; if (head->htype != htype) - return (1 << 1); + return IS_WRONG_TYPE; switch (htype) { case BM_VERT: { BMVert *v = element; if (v->e && v->e->head.htype != BM_EDGE) { - err |= (1 << 2); + err |= IS_VERT_WRONG_EDGE_TYPE; } break; } case BM_EDGE: { BMEdge *e = element; - if (e->l && e->l->head.htype != BM_LOOP) - err |= (1 << 3); - if (e->l && e->l->f->head.htype != BM_FACE) - err |= (1 << 4); if (e->v1_disk_link.prev == NULL || e->v2_disk_link.prev == NULL || e->v1_disk_link.next == NULL || e->v2_disk_link.next == NULL) { - err |= (1 << 5); + err |= IS_EDGE_NULL_DISK_LINK; + } + + if (e->l && e->l->head.htype != BM_LOOP) { + err |= IS_EDGE_WRONG_LOOP_TYPE; + } + if (e->l && e->l->f->head.htype != BM_FACE) { + err |= IS_EDGE_WRONG_FACE_TYPE; + } + if (e->l && (e->l->radial_next == NULL || e->l->radial_prev == NULL)) { + err |= IS_EDGE_NULL_RADIAL_LINK; + } + if (e->l && e->l->f->len <= 0) { + err |= IS_EDGE_ZERO_FACE_LENGTH; } - if (e->l && (e->l->radial_next == NULL || e->l->radial_prev == NULL)) - err |= (1 << 6); - if (e->l && e->l->f->len <= 0) - err |= (1 << 7); break; } case BM_LOOP: @@ -551,21 +605,26 @@ int bmesh_elem_check(void *element, const char htype) BMLoop *l = element, *l2; int i; - if (l->f->head.htype != BM_FACE) - err |= (1 << 8); - if (l->e->head.htype != BM_EDGE) - err |= (1 << 9); - if (l->v->head.htype != BM_VERT) - err |= (1 << 10); + if (l->f->head.htype != BM_FACE) { + err |= IS_LOOP_WRONG_FACE_TYPE; + } + if (l->e->head.htype != BM_EDGE) { + err |= IS_LOOP_WRONG_EDGE_TYPE; + } + if (l->v->head.htype != BM_VERT) { + err |= IS_LOOP_WRONG_VERT_TYPE; + } if (!BM_vert_in_edge(l->e, l->v)) { fprintf(stderr, "%s: fatal bmesh error (vert not in edge)! (bmesh internal error)\n", __func__); - err |= (1 << 11); + err |= IS_LOOP_VERT_NOT_IN_EDGE; } - if (l->radial_next == NULL || l->radial_prev == NULL) - err |= (1 << 12); - if (l->f->len <= 0) - err |= (1 << 13); + if (l->radial_next == NULL || l->radial_prev == NULL) { + err |= IS_LOOP_NULL_CYCLE_LINK; + } + if (l->f->len <= 0) { + err |= IS_LOOP_ZERO_FACE_LENGTH; + } /* validate boundary loop -- invalid for hole loops, of course, * but we won't be allowing those for a while yet */ @@ -579,11 +638,13 @@ int bmesh_elem_check(void *element, const char htype) i++; } while ((l2 = l2->next) != l); - if (i != l->f->len || l2 != l) - err |= (1 << 14); + if (i != l->f->len || l2 != l) { + err |= IS_LOOP_WRONG_FACE_LENGTH; + } - if (!bmesh_radial_validate(bmesh_radial_length(l), l)) - err |= (1 << 15); + if (!bmesh_radial_validate(bmesh_radial_length(l), l)) { + err |= IS_LOOP_WRONG_RADIAL_LENGTH; + } break; } @@ -600,34 +661,73 @@ int bmesh_elem_check(void *element, const char htype) if (!f->l_first) #endif { - err |= (1 << 16); + err |= IS_FACE_NULL_LOOP; } l_iter = l_first = BM_FACE_FIRST_LOOP(f); do { if (l_iter->f != f) { fprintf(stderr, "%s: loop inside one face points to another! (bmesh internal error)\n", __func__); - err |= (1 << 17); + err |= IS_FACE_WRONG_LOOP_FACE; } - if (!l_iter->e) - err |= (1 << 18); - if (!l_iter->v) - err |= (1 << 19); - if (!BM_vert_in_edge(l_iter->e, l_iter->v) || !BM_vert_in_edge(l_iter->e, l_iter->next->v)) { - err |= (1 << 20); + if (!l_iter->e) { + err |= IS_FACE_NULL_EDGE; } + if (!l_iter->v) { + err |= IS_FACE_NULL_VERT; + } + if (l_iter->e && l_iter->v) { + if (!BM_vert_in_edge(l_iter->e, l_iter->v) || + !BM_vert_in_edge(l_iter->e, l_iter->next->v)) + { + err |= IS_FACE_LOOP_VERT_NOT_IN_EDGE; + } - if (!bmesh_radial_validate(bmesh_radial_length(l_iter), l_iter)) - err |= (1 << 21); + if (!bmesh_radial_validate(bmesh_radial_length(l_iter), l_iter)) { + err |= IS_FACE_LOOP_WRONG_RADIAL_LENGTH; + } - if (!bmesh_disk_count(l_iter->v) || !bmesh_disk_count(l_iter->next->v)) - err |= (1 << 22); + if (bmesh_disk_count_ex(l_iter->v, 2) < 2) { + err |= IS_FACE_LOOP_WRONG_DISK_LENGTH; + } + } + + /* check for duplicates */ + if (BM_ELEM_API_FLAG_TEST(l_iter, _FLAG_ELEM_CHECK)) { + err |= IS_FACE_LOOP_DUPE_LOOP; + } + BM_ELEM_API_FLAG_ENABLE(l_iter, _FLAG_ELEM_CHECK); + if (l_iter->v) { + if (BM_ELEM_API_FLAG_TEST(l_iter->v, _FLAG_ELEM_CHECK)) { + err |= IS_FACE_LOOP_DUPE_VERT; + } + BM_ELEM_API_FLAG_ENABLE(l_iter->v, _FLAG_ELEM_CHECK); + } + if (l_iter->e) { + if (BM_ELEM_API_FLAG_TEST(l_iter->e, _FLAG_ELEM_CHECK)) { + err |= IS_FACE_LOOP_DUPE_EDGE; + } + BM_ELEM_API_FLAG_ENABLE(l_iter->e, _FLAG_ELEM_CHECK); + } len++; } while ((l_iter = l_iter->next) != l_first); - if (len != f->len) - err |= (1 << 23); + /* cleanup duplicates flag */ + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + BM_ELEM_API_FLAG_DISABLE(l_iter, _FLAG_ELEM_CHECK); + if (l_iter->v) { + BM_ELEM_API_FLAG_DISABLE(l_iter->v, _FLAG_ELEM_CHECK); + } + if (l_iter->e) { + BM_ELEM_API_FLAG_DISABLE(l_iter->e, _FLAG_ELEM_CHECK); + } + } while ((l_iter = l_iter->next) != l_first); + + if (len != f->len) { + err |= IS_FACE_WRONG_LENGTH; + } break; } default: @@ -773,7 +873,12 @@ void BM_face_kill(BMesh *bm, BMFace *f) BMLoopList *ls, *ls_next; #endif - BM_CHECK_ELEMENT(f); +#ifdef NDEBUG + /* check length since we may be removing degenerate faces */ + if (f->len >= 3) { + BM_CHECK_ELEMENT(f); + } +#endif #ifdef USE_BMESH_HOLES for (ls = f->loops.first; ls; ls = ls_next) @@ -805,15 +910,72 @@ void BM_face_kill(BMesh *bm, BMFace *f) bm_kill_only_face(bm, f); } + +/** + * A version of #BM_face_kill which removes edges and verts + * which have no remaining connected geometry. + */ +void BM_face_kill_loose(BMesh *bm, BMFace *f) +{ +#ifdef USE_BMESH_HOLES + BMLoopList *ls, *ls_next; +#endif + + BM_CHECK_ELEMENT(f); + +#ifdef USE_BMESH_HOLES + for (ls = f->loops.first; ls; ls = ls_next) +#else + if (f->l_first) +#endif + { + BMLoop *l_iter, *l_next, *l_first; + +#ifdef USE_BMESH_HOLES + ls_next = ls->next; + l_iter = l_first = ls->first; +#else + l_iter = l_first = f->l_first; +#endif + + do { + BMEdge *e; + l_next = l_iter->next; + + e = l_iter->e; + bmesh_radial_loop_remove(l_iter, e); + bm_kill_only_loop(bm, l_iter); + + if (e->l == NULL) { + BMVert *v1 = e->v1, *v2 = e->v2; + + bmesh_disk_edge_remove(e, e->v1); + bmesh_disk_edge_remove(e, e->v2); + bm_kill_only_edge(bm, e); + + if (v1->e == NULL) { + bm_kill_only_vert(bm, v1); + } + if (v2->e == NULL) { + bm_kill_only_vert(bm, v2); + } + } + } while ((l_iter = l_next) != l_first); + +#ifdef USE_BMESH_HOLES + BLI_mempool_free(bm->looplistpool, ls); +#endif + } + + bm_kill_only_face(bm, f); +} + /** * kills \a e and all faces that use it. */ void BM_edge_kill(BMesh *bm, BMEdge *e) { - bmesh_disk_edge_remove(e, e->v1); - bmesh_disk_edge_remove(e, e->v2); - if (e->l) { BMLoop *l = e->l, *lnext, *startl = e->l; @@ -831,6 +993,9 @@ void BM_edge_kill(BMesh *bm, BMEdge *e) l = lnext; } while (l != startl); } + + bmesh_disk_edge_remove(e, e->v1); + bmesh_disk_edge_remove(e, e->v2); bm_kill_only_edge(bm, e); } @@ -880,13 +1045,18 @@ static int UNUSED_FUNCTION(bm_loop_length)(BMLoop *l) * * BMESH_TODO: reinsert validation code. * + * \param cd_loop_mdisp_offset: Cached result of `CustomData_get_offset(&bm->ldata, CD_MDISPS)`. + * \param use_loop_mdisp_flip: When set, flip the Z-depth of the mdisp, + * (use when flipping normals, disable when mirroring, eg: symmetrize). + * * \return Success */ -static bool bm_loop_reverse_loop(BMesh *bm, BMFace *f +static bool bm_loop_reverse_loop( + BMesh *bm, BMFace *f, #ifdef USE_BMESH_HOLES - , BMLoopList *lst + BMLoopList *lst, #endif - ) + const int cd_loop_mdisp_offset, const bool use_loop_mdisp_flip) { #ifdef USE_BMESH_HOLES @@ -896,7 +1066,6 @@ static bool bm_loop_reverse_loop(BMesh *bm, BMFace *f #endif const int len = f->len; - const bool do_disps = CustomData_has_layer(&bm->ldata, CD_MDISPS); BMLoop *l_iter, *oldprev, *oldnext; BMEdge **edar = BLI_array_alloca(edar, len); int i, j, edok; @@ -913,26 +1082,9 @@ static bool bm_loop_reverse_loop(BMesh *bm, BMFace *f l_iter->prev = oldnext; l_iter = oldnext; - if (do_disps) { - float (*co)[3]; - int x, y, sides; - MDisps *md; - - md = CustomData_bmesh_get(&bm->ldata, l_iter->head.data, CD_MDISPS); - if (!md->totdisp || !md->disps) - continue; - - sides = (int)sqrt(md->totdisp); - co = md->disps; - - for (x = 0; x < sides; x++) { - for (y = 0; y < x; y++) { - swap_v3_v3(co[y * sides + x], co[sides * x + y]); - SWAP(float, co[y * sides + x][0], co[y * sides + x][1]); - SWAP(float, co[x * sides + y][0], co[x * sides + y][1]); - } - SWAP(float, co[x * sides + x][0], co[x * sides + x][1]); - } + if (cd_loop_mdisp_offset != -1) { + MDisps *md = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_mdisp_offset); + BKE_mesh_mdisp_flip(md, use_loop_mdisp_flip); } } @@ -957,6 +1109,7 @@ static bool bm_loop_reverse_loop(BMesh *bm, BMFace *f for (i = 0, l_iter = l_first; i < len; i++, l_iter = l_iter->next) bmesh_radial_append(l_iter->e, l_iter); +#ifndef NDEBUG /* validate radial */ for (i = 0, l_iter = l_first; i < len; i++, l_iter = l_iter->next) { BM_CHECK_ELEMENT(l_iter); @@ -966,6 +1119,7 @@ static bool bm_loop_reverse_loop(BMesh *bm, BMFace *f } BM_CHECK_ELEMENT(f); +#endif /* Loop indices are no more valid! */ bm->elem_index_dirty |= BM_LOOP; @@ -976,12 +1130,14 @@ static bool bm_loop_reverse_loop(BMesh *bm, BMFace *f /** * \brief Flip the faces direction */ -bool bmesh_loop_reverse(BMesh *bm, BMFace *f) +bool bmesh_loop_reverse( + BMesh *bm, BMFace *f, + const int cd_loop_mdisp_offset, const bool use_loop_mdisp_flip) { #ifdef USE_BMESH_HOLES - return bm_loop_reverse_loop(bm, f, f->loops.first); + return bm_loop_reverse_loop(bm, f, f->loops.first, cd_loop_mdisp_offset, use_loop_mdisp_flip); #else - return bm_loop_reverse_loop(bm, f); + return bm_loop_reverse_loop(bm, f, cd_loop_mdisp_offset, use_loop_mdisp_flip); #endif } @@ -1092,6 +1248,7 @@ BMFace *BM_faces_join(BMesh *bm, BMFace **faces, int totface, const bool do_del) BMVert *v1 = NULL, *v2 = NULL; const char *err = NULL; int i, tote = 0; + const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS); if (UNLIKELY(!totface)) { BMESH_ASSERT(0); @@ -1222,11 +1379,19 @@ BMFace *BM_faces_join(BMesh *bm, BMFace **faces, int totface, const bool do_del) BM_ELEM_API_FLAG_DISABLE(f_new, _FLAG_JF); /* handle multi-res data */ - if (CustomData_has_layer(&bm->ldata, CD_MDISPS)) { + if (cd_loop_mdisp_offset != -1) { + float f_center[3]; + float (*faces_center)[3] = BLI_array_alloca(faces_center, totface); + + BM_face_calc_center_mean(f_new, f_center); + for (i = 0; i < totface; i++) { + BM_face_calc_center_mean(faces[i], faces_center[i]); + } + l_iter = l_first = BM_FACE_FIRST_LOOP(f_new); do { for (i = 0; i < totface; i++) { - BM_loop_interp_multires(bm, l_iter, faces[i]); + BM_loop_interp_multires_ex(bm, l_iter, faces[i], f_center, faces_center[i], cd_loop_mdisp_offset); } } while ((l_iter = l_iter->next) != l_first); } @@ -1644,16 +1809,15 @@ BMVert *bmesh_semv(BMesh *bm, BMVert *tv, BMEdge *e, BMEdge **r_e) * \par Examples: * * <pre> - * Before: OE KE - * ------- ------- - * | || | - * OV KV TV - * - * - * After: OE - * --------------- - * | | - * OV TV + * Before: e_old e_kill + * +-------+-------+ + * | | | + * v_old v_kill v_target + * + * After: e_old + * +---------------+ + * | | + * v_old v_target * </pre> * * \par Restrictions: @@ -1668,14 +1832,15 @@ BMVert *bmesh_semv(BMesh *bm, BMVert *tv, BMEdge *e, BMEdge **r_e) */ BMEdge *bmesh_jekv( BMesh *bm, BMEdge *e_kill, BMVert *v_kill, - const bool do_del, const bool check_edge_double) + const bool do_del, const bool check_edge_double, + const bool kill_degenerate_faces) { BMEdge *e_old; - BMVert *v_old, *tv; + BMVert *v_old, *v_target; BMLoop *l_kill; - int radlen = 0, i; bool halt = false; #ifndef NDEBUG + int radlen, i; bool edok; #endif @@ -1692,39 +1857,46 @@ BMEdge *bmesh_jekv( #endif e_old = bmesh_disk_edge_next(e_kill, v_kill); - tv = BM_edge_other_vert(e_kill, v_kill); + v_target = BM_edge_other_vert(e_kill, v_kill); v_old = BM_edge_other_vert(e_old, v_kill); - halt = BM_verts_in_edge(v_kill, tv, e_old); /* check for double edges */ + halt = BM_verts_in_edge(v_kill, v_target, e_old); /* check for double edges */ if (halt) { return NULL; } else { BMEdge *e_splice; + BLI_SMALLSTACK_DECLARE(faces_degenerate, BMFace *); + BMLoop *l_kill_next; #ifndef NDEBUG - /* For verification later, count valence of v_old and tv */ + /* For verification later, count valence of 'v_old' and 'v_target' */ valence1 = bmesh_disk_count(v_old); - valence2 = bmesh_disk_count(tv); + valence2 = bmesh_disk_count(v_target); #endif if (check_edge_double) { - e_splice = BM_edge_exists(tv, v_old); + e_splice = BM_edge_exists(v_target, v_old); } - bmesh_disk_vert_replace(e_old, tv, v_kill); + bmesh_disk_vert_replace(e_old, v_target, v_kill); - /* remove e_kill from tv's disk cycle */ - bmesh_disk_edge_remove(e_kill, tv); + /* remove e_kill from 'v_target's disk cycle */ + bmesh_disk_edge_remove(e_kill, v_target); +#ifndef NDEBUG /* deal with radial cycle of e_kill */ radlen = bmesh_radial_length(e_kill->l); +#endif if (e_kill->l) { - /* first step, fix the neighboring loops of all loops in e_kill's radial cycle */ - for (i = 0, l_kill = e_kill->l; i < radlen; i++, l_kill = l_kill->radial_next) { + + + /* fix the neighboring loops of all loops in e_kill's radial cycle */ + l_kill = e_kill->l; + do { /* relink loops and fix vertex pointer */ if (l_kill->next->v == v_kill) { - l_kill->next->v = tv; + l_kill->next->v = v_target; } l_kill->next->prev = l_kill->prev; @@ -1732,29 +1904,20 @@ BMEdge *bmesh_jekv( if (BM_FACE_FIRST_LOOP(l_kill->f) == l_kill) { BM_FACE_FIRST_LOOP(l_kill->f) = l_kill->next; } - l_kill->next = NULL; - l_kill->prev = NULL; /* fix len attribute of face */ l_kill->f->len--; - } - /* second step, remove all the hanging loops attached to e_kill */ - radlen = bmesh_radial_length(e_kill->l); - - if (LIKELY(radlen)) { - BMLoop **loops = BLI_array_alloca(loops, radlen); + if (kill_degenerate_faces) { + if (l_kill->f->len < 3) { + BLI_SMALLSTACK_PUSH(faces_degenerate, l_kill->f); + } + } + l_kill_next = l_kill->radial_next; - l_kill = e_kill->l; + bm_kill_only_loop(bm, l_kill); - /* this should be wrapped into a bme_free_radial function to be used by bmesh_KF as well... */ - for (i = 0; i < radlen; i++) { - loops[i] = l_kill; - l_kill = l_kill->radial_next; - } - for (i = 0; i < radlen; i++) { - bm_kill_only_loop(bm, loops[i]); - } - } + } while ((l_kill = l_kill_next) != e_kill->l); + /* `e_kill->l` is invalid but the edge is freed next. */ #ifndef NDEBUG /* Validate radial cycle of e_old */ edok = bmesh_radial_validate(radlen, e_old->l); @@ -1773,10 +1936,10 @@ BMEdge *bmesh_jekv( } #ifndef NDEBUG - /* Validate disk cycle lengths of v_old, tv are unchanged */ + /* Validate disk cycle lengths of 'v_old', 'v_target' are unchanged */ edok = bmesh_disk_validate(valence1, v_old->e, v_old); BMESH_ASSERT(edok != false); - edok = bmesh_disk_validate(valence2, tv->e, tv); + edok = bmesh_disk_validate(valence2, v_target->e, v_target); BMESH_ASSERT(edok != false); /* Validate loop cycle of all faces attached to 'e_old' */ @@ -1793,7 +1956,6 @@ BMEdge *bmesh_jekv( BM_CHECK_ELEMENT(l->f); } #endif - if (check_edge_double) { if (e_splice) { /* removes e_splice */ @@ -1801,8 +1963,15 @@ BMEdge *bmesh_jekv( } } + if (kill_degenerate_faces) { + BMFace *f_kill; + while ((f_kill = BLI_SMALLSTACK_POP(faces_degenerate))) { + BM_face_kill(bm, f_kill); + } + } + BM_CHECK_ELEMENT(v_old); - BM_CHECK_ELEMENT(tv); + BM_CHECK_ELEMENT(v_target); BM_CHECK_ELEMENT(e_old); return e_old; @@ -1812,6 +1981,104 @@ BMEdge *bmesh_jekv( } /** + * \brief Join Vert Kill Edge (JVKE) + * + * Collapse an edge, merging surrounding data. + * + * Unlike #BM_vert_collapse_edge & #bmesh_jekv which only handle 2 valence verts, + * this can handle any number of connected edges/faces. + * + * <pre> + * Before: -> After: + * +-+-+-+ +-+-+-+ + * | | | | | \ / | + * +-+-+-+ +--+--+ + * | | | | | / \ | + * +-+-+-+ +-+-+-+ + * </pre> + */ +BMVert *bmesh_jvke( + BMesh *bm, BMEdge *e_kill, BMVert *v_kill, + const bool do_del, const bool check_edge_double, + const bool kill_degenerate_faces) +{ + BLI_SMALLSTACK_DECLARE(faces_degenerate, BMFace *); + BMVert *v_target = BM_edge_other_vert(e_kill, v_kill); + + BLI_assert(BM_vert_in_edge(e_kill, v_kill)); + + if (e_kill->l) { + BMLoop *l_kill, *l_first, *l_kill_next; + l_kill = l_first = e_kill->l; + do { + /* relink loops and fix vertex pointer */ + if (l_kill->next->v == v_kill) { + l_kill->next->v = v_target; + } + + l_kill->next->prev = l_kill->prev; + l_kill->prev->next = l_kill->next; + if (BM_FACE_FIRST_LOOP(l_kill->f) == l_kill) { + BM_FACE_FIRST_LOOP(l_kill->f) = l_kill->next; + } + + /* fix len attribute of face */ + l_kill->f->len--; + if (kill_degenerate_faces) { + if (l_kill->f->len < 3) { + BLI_SMALLSTACK_PUSH(faces_degenerate, l_kill->f); + } + } + l_kill_next = l_kill->radial_next; + + bm_kill_only_loop(bm, l_kill); + + } while ((l_kill = l_kill_next) != l_first); + + e_kill->l = NULL; + } + + BM_edge_kill(bm, e_kill); + BM_CHECK_ELEMENT(v_kill); + BM_CHECK_ELEMENT(v_target); + + if (v_target->e && v_kill->e) { + /* inline BM_vert_splice(bm, v_target, v_kill); */ + BMEdge *e; + while ((e = v_kill->e)) { + BMEdge *e_target; + + if (check_edge_double) { + e_target = BM_edge_exists(v_target, BM_edge_other_vert(e, v_kill)); + } + + bmesh_edge_vert_swap(e, v_target, v_kill); + BLI_assert(e->v1 != e->v2); + + if (check_edge_double) { + if (e_target) { + BM_edge_splice(bm, e_target, e); + } + } + } + } + + if (kill_degenerate_faces) { + BMFace *f_kill; + while ((f_kill = BLI_SMALLSTACK_POP(faces_degenerate))) { + BM_face_kill(bm, f_kill); + } + } + + if (do_del) { + BLI_assert(v_kill->e == NULL); + bm_kill_only_vert(bm, v_kill); + } + + return v_target; +} + +/** * \brief Join Face Kill Edge (JFKE) * * Takes two faces joined by a single 2-manifold edge and fuses them together. @@ -2212,7 +2479,8 @@ static void bmesh_vert_separate__cleanup(BMesh *bm, LinkNode *edges_separate) n_prev->next = n_step->next; n_step = n_prev; } - } while ((n_prev = n_step), + } while ((void) + (n_prev = n_step), (n_step = n_step->next)); } while ((n_orig = n_orig->next) && n_orig->next); @@ -2275,7 +2543,7 @@ void BM_vert_separate_hflag( do { BMLoop *l_sep = e->l; bmesh_edge_separate(bm, e, l_sep, copy_select); - /* trick to avoid looping over seperated edges */ + /* trick to avoid looping over separated edges */ if (edges_separate == NULL && edges_orig == NULL) { e_first = l_sep->e; } @@ -2295,6 +2563,30 @@ void BM_vert_separate_hflag( } } +void BM_vert_separate_wire_hflag( + BMesh *UNUSED(bm), BMVert *v_dst, BMVert *v_src, + const char hflag) +{ + LinkNode *edges_hflag = NULL; + BMEdge *e_iter, *e_first; + + e_iter = e_first = v_src->e; + do { + if (BM_elem_flag_test(e_iter, hflag)) { + if (BM_edge_is_wire(e_iter)) { + BLI_linklist_prepend_alloca(&edges_hflag, e_iter); + } + } + } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v_src)) != e_first); + + if (edges_hflag) { + do { + e_iter = edges_hflag->link; + bmesh_disk_vert_replace(e_iter, v_dst, v_src); + } while ((edges_hflag = edges_hflag->next)); + } +} + /** \} */ @@ -2524,7 +2816,7 @@ BMVert *bmesh_urmv_loop_multi( } if (is_mixed_any == false) { - /* all loops in 'larr' are the soul owners of their edges. + /* all loops in 'larr' are the sole owners of their edges. * nothing to split away from, this is a no-op */ v_new = v_sep; } |