diff options
Diffstat (limited to 'source/blender/bmesh/intern/bmesh_core.c')
-rw-r--r-- | source/blender/bmesh/intern/bmesh_core.c | 756 |
1 files changed, 514 insertions, 242 deletions
diff --git a/source/blender/bmesh/intern/bmesh_core.c b/source/blender/bmesh/intern/bmesh_core.c index 9e0807710fc..20a88b0e17c 100644 --- a/source/blender/bmesh/intern/bmesh_core.c +++ b/source/blender/bmesh/intern/bmesh_core.c @@ -31,7 +31,7 @@ #include "BLI_math_vector.h" #include "BLI_array.h" #include "BLI_alloca.h" -#include "BLI_smallhash.h" +#include "BLI_linklist_stack.h" #include "BLI_stackdefines.h" #include "BLF_translation.h" @@ -57,8 +57,9 @@ /** * \brief Main function for creating a new vertex. */ -BMVert *BM_vert_create(BMesh *bm, const float co[3], - const BMVert *v_example, const eBMCreateFlag create_flag) +BMVert *BM_vert_create( + BMesh *bm, const float co[3], + const BMVert *v_example, const eBMCreateFlag create_flag) { BMVert *v = BLI_mempool_alloc(bm->vpool); @@ -88,7 +89,7 @@ BMVert *BM_vert_create(BMesh *bm, const float co[3], else { zero_v3(v->co); } - zero_v3(v->no); + /* 'v->no' set below */ v->e = NULL; /* --- done --- */ @@ -107,6 +108,7 @@ BMVert *BM_vert_create(BMesh *bm, const float co[3], if (v_example) { int *keyi; + /* handles 'v->no' too */ BM_elem_attrs_copy(bm, bm, v_example, v); /* exception: don't copy the original shapekey index */ @@ -117,6 +119,15 @@ BMVert *BM_vert_create(BMesh *bm, const float co[3], } else { CustomData_bmesh_set_default(&bm->vdata, &v->head.data); + zero_v3(v->no); + } + } + else { + if (v_example) { + copy_v3_v3(v->no, v_example->no); + } + else { + zero_v3(v->no); } } @@ -131,8 +142,9 @@ BMVert *BM_vert_create(BMesh *bm, const float co[3], * \note Duplicate edges are supported by the API however users should _never_ see them. * so unless you need a unique edge or know the edge won't exist, you should call with \a no_double = true */ -BMEdge *BM_edge_create(BMesh *bm, BMVert *v1, BMVert *v2, - const BMEdge *e_example, const eBMCreateFlag create_flag) +BMEdge *BM_edge_create( + BMesh *bm, BMVert *v1, BMVert *v2, + const BMEdge *e_example, const eBMCreateFlag create_flag) { BMEdge *e; @@ -194,8 +206,9 @@ BMEdge *BM_edge_create(BMesh *bm, BMVert *v1, BMVert *v2, return e; } -static BMLoop *bm_loop_create(BMesh *bm, BMVert *v, BMEdge *e, BMFace *f, - const BMLoop *l_example, const eBMCreateFlag create_flag) +static BMLoop *bm_loop_create( + BMesh *bm, BMVert *v, BMEdge *e, BMFace *f, + const BMLoop *l_example, const eBMCreateFlag create_flag) { BMLoop *l = NULL; @@ -213,8 +226,8 @@ static BMLoop *bm_loop_create(BMesh *bm, BMVert *v, BMEdge *e, BMFace *f, BM_elem_index_set(l, -1); /* set_ok_invalid */ #endif - l->head.hflag = 0; l->head.htype = BM_LOOP; + l->head.hflag = 0; l->head.api_flag = 0; l->v = v; @@ -244,8 +257,9 @@ static BMLoop *bm_loop_create(BMesh *bm, BMVert *v, BMEdge *e, BMFace *f, return l; } -static BMLoop *bm_face_boundary_add(BMesh *bm, BMFace *f, BMVert *startv, BMEdge *starte, - const eBMCreateFlag create_flag) +static BMLoop *bm_face_boundary_add( + BMesh *bm, BMFace *f, BMVert *startv, BMEdge *starte, + const eBMCreateFlag create_flag) { #ifdef USE_BMESH_HOLES BMLoopList *lst = BLI_mempool_calloc(bm->looplistpool); @@ -266,8 +280,9 @@ static BMLoop *bm_face_boundary_add(BMesh *bm, BMFace *f, BMVert *startv, BMEdge return l; } -BMFace *BM_face_copy(BMesh *bm_dst, BMesh *bm_src, BMFace *f, - const bool copy_verts, const bool copy_edges) +BMFace *BM_face_copy( + BMesh *bm_dst, BMesh *bm_src, BMFace *f, + const bool copy_verts, const bool copy_edges) { BMVert **verts = BLI_array_alloca(verts, f->len); BMEdge **edges = BLI_array_alloca(edges, f->len); @@ -362,7 +377,8 @@ BLI_INLINE BMFace *bm_face_create__internal(BMesh *bm) f->l_first = NULL; #endif f->len = 0; - zero_v3(f->no); + /* caller must initialize */ + // zero_v3(f->no); f->mat_nr = 0; /* --- done --- */ @@ -389,8 +405,9 @@ BLI_INLINE BMFace *bm_face_create__internal(BMesh *bm) * \param len Length of the face * \param create_flag Options for creating the face */ -BMFace *BM_face_create(BMesh *bm, BMVert **verts, BMEdge **edges, const int len, - const BMFace *f_example, const eBMCreateFlag create_flag) +BMFace *BM_face_create( + BMesh *bm, BMVert **verts, BMEdge **edges, const int len, + const BMFace *f_example, const eBMCreateFlag create_flag) { BMFace *f = NULL; BMLoop *l, *startl, *lastl; @@ -443,6 +460,15 @@ BMFace *BM_face_create(BMesh *bm, BMVert **verts, BMEdge **edges, const int len, } else { CustomData_bmesh_set_default(&bm->pdata, &f->head.data); + zero_v3(f->no); + } + } + else { + if (f_example) { + copy_v3_v3(f->no, f_example->no); + } + else { + zero_v3(f->no); } } @@ -454,25 +480,18 @@ BMFace *BM_face_create(BMesh *bm, BMVert **verts, BMEdge **edges, const int len, /** * Wrapper for #BM_face_create when you don't have an edge array */ -BMFace *BM_face_create_verts(BMesh *bm, BMVert **vert_arr, const int len, - const BMFace *f_example, const eBMCreateFlag create_flag, const bool create_edges) +BMFace *BM_face_create_verts( + BMesh *bm, BMVert **vert_arr, const int len, + const BMFace *f_example, const eBMCreateFlag create_flag, const bool create_edges) { BMEdge **edge_arr = BLI_array_alloca(edge_arr, len); - int i, i_prev = len - 1; if (create_edges) { - for (i = 0; i < len; i++) { - edge_arr[i_prev] = BM_edge_create(bm, vert_arr[i_prev], vert_arr[i], NULL, BM_CREATE_NO_DOUBLE); - i_prev = i; - } + BM_edges_from_verts_ensure(bm, edge_arr, vert_arr, len); } else { - for (i = 0; i < len; i++) { - edge_arr[i_prev] = BM_edge_exists(vert_arr[i_prev], vert_arr[i]); - if (edge_arr[i_prev] == NULL) { - return NULL; - } - i_prev = i; + if (BM_edges_from_verts(edge_arr, vert_arr, len) == false) { + return NULL; } } @@ -1026,8 +1045,9 @@ static bool disk_is_flagged(BMVert *v, const char api_flag) return false; } - if (bmesh_radial_length(l) == 1) + if (BM_edge_is_boundary(l->e)) { return false; + } do { if (!BM_ELEM_API_FLAG_TEST(l->f, api_flag)) @@ -1111,7 +1131,7 @@ BMFace *BM_faces_join(BMesh *bm, BMFace **faces, int totface, const bool do_del) if (!d1 && !d2 && !BM_ELEM_API_FLAG_TEST(l_iter->e, _FLAG_JF)) { /* don't remove an edge it makes up the side of another face * else this will remove the face as well - campbell */ - if (BM_edge_face_count(l_iter->e) <= 2) { + if (!BM_edge_face_count_is_over(l_iter->e, 3)) { if (do_del) { BLI_array_append(deledges, l_iter->e); } @@ -1307,14 +1327,14 @@ static BMFace *bm_face_create__sfme(BMesh *bm, BMFace *f_example) * * \return A BMFace pointer */ -BMFace *bmesh_sfme(BMesh *bm, BMFace *f, BMLoop *l_v1, BMLoop *l_v2, - BMLoop **r_l, +BMFace *bmesh_sfme( + BMesh *bm, BMFace *f, BMLoop *l_v1, BMLoop *l_v2, + BMLoop **r_l, #ifdef USE_BMESH_HOLES - ListBase *holes, + ListBase *holes, #endif - BMEdge *e_example, - const bool no_double - ) + BMEdge *e_example, + const bool no_double) { #ifdef USE_BMESH_HOLES BMLoopList *lst, *lst2; @@ -1481,14 +1501,7 @@ BMVert *bmesh_semv(BMesh *bm, BMVert *tv, BMEdge *e, BMEdge **r_e) bmesh_disk_edge_remove(e_new, tv); bmesh_disk_edge_remove(e_new, v_new); - /* remove e from tv's disk cycle */ - bmesh_disk_edge_remove(e, tv); - - /* swap out tv for v_new in e */ - bmesh_edge_swapverts(e, tv, v_new); - - /* add e to v_new's disk cycle */ - bmesh_disk_edge_append(e, v_new); + bmesh_disk_vert_replace(e, v_new, tv); /* add e_new to v_new's disk cycle */ bmesh_disk_edge_append(e_new, v_new); @@ -1653,13 +1666,14 @@ BMVert *bmesh_semv(BMesh *bm, BMVert *tv, BMEdge *e, BMEdge **r_e) * faces with just 2 edges. It is up to the caller to decide what to do with * these faces. */ -BMEdge *bmesh_jekv(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, - const bool do_del, const bool check_edge_double) +BMEdge *bmesh_jekv( + BMesh *bm, BMEdge *e_kill, BMVert *v_kill, + const bool do_del, const bool check_edge_double) { BMEdge *e_old; BMVert *v_old, *tv; BMLoop *l_kill; - int len, radlen = 0, i; + int radlen = 0, i; bool halt = false; #ifndef NDEBUG bool edok; @@ -1670,10 +1684,8 @@ BMEdge *bmesh_jekv(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, if (BM_vert_in_edge(e_kill, v_kill) == 0) { return NULL; } - - len = bmesh_disk_count(v_kill); - if (len == 2) { + if (bmesh_disk_count_ex(v_kill, 3) == 2) { #ifndef NDEBUG int valence1, valence2; BMLoop *l; @@ -1700,12 +1712,8 @@ BMEdge *bmesh_jekv(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, e_splice = BM_edge_exists(tv, v_old); } - /* remove e_old from v_kill's disk cycle */ - bmesh_disk_edge_remove(e_old, v_kill); - /* relink e_old->v_kill to be e_old->tv */ - bmesh_edge_swapverts(e_old, v_kill, tv); - /* append e_old to tv's disk cycle */ - bmesh_disk_edge_append(e_old, tv); + bmesh_disk_vert_replace(e_old, tv, v_kill); + /* remove e_kill from tv's disk cycle */ bmesh_disk_edge_remove(e_kill, tv); @@ -1789,7 +1797,7 @@ BMEdge *bmesh_jekv(BMesh *bm, BMEdge *e_kill, BMVert *v_kill, if (check_edge_double) { if (e_splice) { /* removes e_splice */ - BM_edge_splice(bm, e_splice, e_old); + BM_edge_splice(bm, e_old, e_splice); } } @@ -1963,27 +1971,38 @@ bool BM_vert_splice_check_double(BMVert *v_a, BMVert *v_b) BLI_assert(BM_edge_exists(v_a, v_b) == false); if (v_a->e && v_b->e) { - SmallHash visit; BMEdge *e, *e_first; - BLI_smallhash_init(&visit); +#define VERT_VISIT _FLAG_WALK + /* tag 'v_a' */ e = e_first = v_a->e; do { BMVert *v_other = BM_edge_other_vert(e, v_a); - BLI_smallhash_insert(&visit, (uintptr_t)v_other, NULL); + BLI_assert(!BM_ELEM_API_FLAG_TEST(v_other, VERT_VISIT)); + BM_ELEM_API_FLAG_ENABLE(v_other, VERT_VISIT); } while ((e = BM_DISK_EDGE_NEXT(e, v_a)) != e_first); + /* check 'v_b' connects to 'v_a' edges */ e = e_first = v_b->e; do { BMVert *v_other = BM_edge_other_vert(e, v_b); - if (BLI_smallhash_haskey(&visit, (uintptr_t)v_other)) { + if (BM_ELEM_API_FLAG_TEST(v_other, VERT_VISIT)) { is_double = true; break; } } while ((e = BM_DISK_EDGE_NEXT(e, v_b)) != e_first); - BLI_smallhash_release(&visit); + /* cleanup */ + e = e_first = v_a->e; + do { + BMVert *v_other = BM_edge_other_vert(e, v_a); + BLI_assert(BM_ELEM_API_FLAG_TEST(v_other, VERT_VISIT)); + BM_ELEM_API_FLAG_DISABLE(v_other, VERT_VISIT); + } while ((e = BM_DISK_EDGE_NEXT(e, v_a)) != e_first); + +#undef VERT_VISIT + } return is_double; @@ -1992,7 +2011,8 @@ bool BM_vert_splice_check_double(BMVert *v_a, BMVert *v_b) /** * \brief Splice Vert * - * Merges two verts into one (\a v into \a vtarget). + * Merges two verts into one + * (\a v_src into \a v_dst, removing \a v_src). * * \return Success * @@ -2000,49 +2020,42 @@ bool BM_vert_splice_check_double(BMVert *v_a, BMVert *v_b) * where \a v and \a vtarget are connected by an edge * (assert checks for this case). */ -bool BM_vert_splice(BMesh *bm, BMVert *v, BMVert *v_target) +bool BM_vert_splice(BMesh *bm, BMVert *v_dst, BMVert *v_src) { BMEdge *e; /* verts already spliced */ - if (v == v_target) { + if (v_src == v_dst) { return false; } - BLI_assert(BM_vert_pair_share_face_check(v, v_target) == false); - - /* move all the edges from v's disk to vtarget's disk */ - while ((e = v->e)) { + BLI_assert(BM_vert_pair_share_face_check(v_src, v_dst) == false); - /* loop */ - BMLoop *l_first; - if ((l_first = e->l)) { - BMLoop *l_iter = l_first; - do { - if (l_iter->v == v) { - l_iter->v = v_target; - } - /* else if (l_iter->prev->v == v) {...} - * (this case will be handled by a different edge) */ - } while ((l_iter = l_iter->radial_next) != l_first); - } - - /* disk */ - bmesh_disk_edge_remove(e, v); - bmesh_edge_swapverts(e, v, v_target); - bmesh_disk_edge_append(e, v_target); + /* move all the edges from 'v_src' disk to 'v_dst' */ + while ((e = v_src->e)) { + bmesh_edge_vert_swap(e, v_dst, v_src); BLI_assert(e->v1 != e->v2); } - BM_CHECK_ELEMENT(v); - BM_CHECK_ELEMENT(v_target); + BM_CHECK_ELEMENT(v_src); + BM_CHECK_ELEMENT(v_dst); - /* v is unused now, and can be killed */ - BM_vert_kill(bm, v); + /* 'v_src' is unused now, and can be killed */ + BM_vert_kill(bm, v_src); return true; } + +/** \name BM_vert_separate, bmesh_vert_separate and friends + * \{ */ + +/* BM_edge_face_count(e) >= 1 */ +BLI_INLINE bool bm_edge_supports_separate(const BMEdge *e) +{ + return (e->l && e->l->radial_next != e->l); +} + /** * \brief Separate Vert * @@ -2054,167 +2067,252 @@ bool BM_vert_splice(BMesh *bm, BMVert *v, BMVert *v_target) * * \return Success */ -void bmesh_vert_separate(BMesh *bm, BMVert *v, BMVert ***r_vout, int *r_vout_len, - const bool copy_select) +void bmesh_vert_separate( + BMesh *bm, BMVert *v, BMVert ***r_vout, int *r_vout_len, + const bool copy_select) { - const int v_edgetot = BM_vert_face_count(v); - BMEdge **stack = BLI_array_alloca(stack, v_edgetot); - STACK_DECLARE(stack); + int v_edges_num = 0; - SmallHash visithash; - BMVert **verts = NULL; - BMIter eiter, liter; - BMLoop *l; - BMEdge *e; - int i, maxindex; - BMLoop *l_new; + /* Detailed notes on array use since this is stack memory, we have to be careful */ - BLI_smallhash_init_ex(&visithash, v_edgetot); + /* newly created vertices, only use when 'r_vout' is set + * (total size will be number of fans) */ + BLI_SMALLSTACK_DECLARE(verts_new, BMVert *); + /* fill with edges from the face-fan, clearing on completion + * (total size will be max fan edge count) */ + BLI_SMALLSTACK_DECLARE(edges, BMEdge *); + /* temp store edges to walk over when filling 'edges', + * (total size will be max radial edges of any edge) */ + BLI_SMALLSTACK_DECLARE(edges_search, BMEdge *); - STACK_INIT(stack, v_edgetot); + /* number of resulting verts, include self */ + int verts_num = 1; + /* track the total number of edges handled, so we know when we've found the last fan */ + int edges_found = 0; - maxindex = 0; - BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { - if (BLI_smallhash_haskey(&visithash, (uintptr_t)e)) { - continue; - } +#define EDGE_VISIT _FLAG_WALK + + /* count and flag at once */ + if (v->e) { + BMEdge *e_first, *e_iter; + e_iter = e_first = v->e; + do { + v_edges_num += 1; + + BLI_assert(!BM_ELEM_API_FLAG_TEST(e_iter, EDGE_VISIT)); + BM_ELEM_API_FLAG_ENABLE(e_iter, EDGE_VISIT); + } while ((e_iter = bmesh_disk_edge_next(e_iter, v)) != e_first); + } + while (true) { /* Considering only edges and faces incident on vertex v, walk - * the edges & faces and assign an index to each connected set */ - BLI_smallhash_insert(&visithash, (uintptr_t)e, SET_INT_IN_POINTER(maxindex)); + * the edges & collect in the 'edges' list for splitting */ + + BMEdge *e = v->e; + BM_ELEM_API_FLAG_DISABLE(e, EDGE_VISIT); + do { + BLI_assert(!BM_ELEM_API_FLAG_TEST(e, EDGE_VISIT)); + BLI_SMALLSTACK_PUSH(edges, e); + edges_found += 1; + if (e->l) { BMLoop *l_iter, *l_first; l_iter = l_first = e->l; do { - l_new = (l_iter->v == v) ? l_iter->prev : l_iter->next; - BLI_assert(BM_vert_in_edge(l_new->e, v)); - if (!BLI_smallhash_haskey(&visithash, (uintptr_t)l_new->e)) { - BLI_smallhash_insert(&visithash, (uintptr_t)l_new->e, SET_INT_IN_POINTER(maxindex)); - STACK_PUSH(stack, l_new->e); + BMLoop *l_adjacent = (l_iter->v == v) ? l_iter->prev : l_iter->next; + BLI_assert(BM_vert_in_edge(l_adjacent->e, v)); + if (BM_ELEM_API_FLAG_TEST(l_adjacent->e, EDGE_VISIT)) { + BM_ELEM_API_FLAG_DISABLE(l_adjacent->e, EDGE_VISIT); + BLI_SMALLSTACK_PUSH(edges_search, l_adjacent->e); } } while ((l_iter = l_iter->radial_next) != l_first); } - } while ((e = STACK_POP(stack))); + } while ((e = BLI_SMALLSTACK_POP(edges_search))); - maxindex++; - } - - /* Make enough verts to split v for each group */ - if (r_vout != NULL) { - verts = MEM_callocN(sizeof(BMVert *) * maxindex, __func__); - } - else { - verts = BLI_array_alloca(verts, maxindex); - } + /* now we have all edges connected to 'v->e' */ - verts[0] = v; - for (i = 1; i < maxindex; i++) { - verts[i] = BM_vert_create(bm, v->co, v, BM_CREATE_NOP); - if (copy_select) { - BM_elem_select_copy(bm, bm, verts[i], v); - } - } + BLI_assert(edges_found <= v_edges_num); - /* Replace v with the new verts in each group */ -#if 0 - BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { - /* call first since its faster then a hash lookup */ - if (l->v != v) { - continue; - } - i = GET_INT_FROM_POINTER(BLI_ghash_lookup(visithash, l->e)); - if (i == 0) { - continue; + if (edges_found == v_edges_num) { + /* We're done! The remaining edges in 'edges' form the last fan, + * which can be left as is. + * if 'edges' were alloc'd it'd be freed here. */ + break; } + else { + BMVert *v_new; - /* Loops here should always refer to an edge that has v as an - * endpoint. For each appearance of this vert in a face, there - * will actually be two iterations: one for the loop heading - * towards vertex v, and another for the loop heading out from - * vertex v. Only need to swap the vertex on one of those times, - * on the outgoing loop. */ + v_new = BM_vert_create(bm, v->co, v, BM_CREATE_NOP); + if (copy_select) { + BM_elem_select_copy(bm, bm, v_new, v); + } - /* XXX - because this clobbers the iterator, this *whole* block is commented, see below */ - l->v = verts[i]; - } -#else - /* note: this is the same as the commented code above *except* that it doesn't break iterator - * by modifying data it loops over [#30632], this re-uses the 'stack' variable which is a bit - * bad practice but save alloc'ing a new array - note, the comment above is useful, keep it - * if you are tidying up code - campbell */ - STACK_INIT(stack, v_edgetot); - BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { - if (l->v == v) { - STACK_PUSH(stack, (BMEdge *)l); - } - } - while ((l = (BMLoop *)(STACK_POP(stack)))) { - if ((i = GET_INT_FROM_POINTER(BLI_smallhash_lookup(&visithash, (uintptr_t)l->e)))) { - l->v = verts[i]; - } - } -#endif + while ((e = BLI_SMALLSTACK_POP(edges))) { + bmesh_edge_vert_swap(e, v_new, v); + } - BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { - i = GET_INT_FROM_POINTER(BLI_smallhash_lookup(&visithash, (uintptr_t)e)); - if (i == 0) { - continue; + if (r_vout) { + BLI_SMALLSTACK_PUSH(verts_new, v_new); + } + verts_num += 1; } - - BLI_assert(e->v1 == v || e->v2 == v); - bmesh_disk_edge_remove(e, v); - bmesh_edge_swapverts(e, v, verts[i]); - bmesh_disk_edge_append(e, verts[i]); } - BLI_smallhash_release(&visithash); +#undef EDGE_VISIT - for (i = 0; i < maxindex; i++) { - BM_CHECK_ELEMENT(verts[i]); - } + /* flags are clean now, handle return values */ if (r_vout_len != NULL) { - *r_vout_len = maxindex; + *r_vout_len = verts_num; } if (r_vout != NULL) { + BMVert **verts; + + verts = MEM_mallocN(sizeof(BMVert *) * verts_num, __func__); *r_vout = verts; + + verts[0] = v; + BLI_SMALLSTACK_AS_TABLE(verts_new, &verts[1]); } } /** + * Utility function for #BM_vert_separate + * + * Takes a list of edges, which have been split from their original. + * + * Any edges which failed to split off in #bmesh_vert_separate will be merged back into the original edge. + * + * \param edges_separate + * A list-of-lists, each list is from a single original edge (the first edge is the original), + * Check for duplicates (not just with the first) but between all. + * This is O(n2) but radial edges are very rarely >2 and almost never >~10. + * + * \note typically its best to avoid creating the data in the first place, + * but inspecting all loops connectivity is quite involved. + * + * \note this function looks like it could become slow, + * but in common cases its only going to iterate a few times. + */ +static void bmesh_vert_separate__cleanup(BMesh *bm, LinkNode *edges_separate) +{ + do { + LinkNode *n_orig = edges_separate->link; + do { + BMEdge *e_orig = n_orig->link; + LinkNode *n_step = n_orig->next; + LinkNode *n_prev = n_orig; + do { + BMEdge *e = n_step->link; + BLI_assert(e != e_orig); + if ((e->v1 == e_orig->v1) && (e->v2 == e_orig->v2)) { + BM_edge_splice(bm, e_orig, e); + n_prev->next = n_step->next; + n_step = n_prev; + } + } while ((n_prev = n_step), + (n_step = n_step->next)); + + } while ((n_orig = n_orig->next) && n_orig->next); + } while ((edges_separate = edges_separate->next)); +} + +/** * High level function which wraps both #bmesh_vert_separate and #bmesh_edge_separate */ -void BM_vert_separate(BMesh *bm, BMVert *v, BMVert ***r_vout, int *r_vout_len, - BMEdge **e_in, int e_in_len) +void BM_vert_separate( + BMesh *bm, BMVert *v, + BMEdge **e_in, int e_in_len, + const bool copy_select, + BMVert ***r_vout, int *r_vout_len) { + LinkNode *edges_separate = NULL; int i; for (i = 0; i < e_in_len; i++) { BMEdge *e = e_in[i]; - if (e->l && BM_vert_in_edge(e, v)) { - bmesh_edge_separate(bm, e, e->l, false); + if (bm_edge_supports_separate(e)) { + LinkNode *edges_orig = NULL; + do { + BMLoop *l_sep = e->l; + bmesh_edge_separate(bm, e, l_sep, copy_select); + BLI_linklist_prepend_alloca(&edges_orig, l_sep->e); + BLI_assert(e != l_sep->e); + } while (bm_edge_supports_separate(e)); + BLI_linklist_prepend_alloca(&edges_orig, e); + BLI_linklist_prepend_alloca(&edges_separate, edges_orig); } } - bmesh_vert_separate(bm, v, r_vout, r_vout_len, false); + bmesh_vert_separate(bm, v, r_vout, r_vout_len, copy_select); + + if (edges_separate) { + bmesh_vert_separate__cleanup(bm, edges_separate); + } +} + + +/** + * A version of #BM_vert_separate which takes a flag. + */ +void BM_vert_separate_hflag( + BMesh *bm, BMVert *v, + const char hflag, + const bool copy_select, + BMVert ***r_vout, int *r_vout_len) +{ + LinkNode *edges_separate = NULL; + BMEdge *e_iter, *e_first; + + e_iter = e_first = v->e; + do { + if (BM_elem_flag_test(e_iter, hflag)) { + BMEdge *e = e_iter; + if (bm_edge_supports_separate(e)) { + LinkNode *edges_orig = NULL; + do { + BMLoop *l_sep = e->l; + bmesh_edge_separate(bm, e, l_sep, copy_select); + /* trick to avoid looping over seperated edges */ + if (edges_separate == NULL && edges_orig == NULL) { + e_first = l_sep->e; + } + BLI_linklist_prepend_alloca(&edges_orig, l_sep->e); + BLI_assert(e != l_sep->e); + } while (bm_edge_supports_separate(e)); + BLI_linklist_prepend_alloca(&edges_orig, e); + BLI_linklist_prepend_alloca(&edges_separate, edges_orig); + } + } + } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first); + + bmesh_vert_separate(bm, v, r_vout, r_vout_len, copy_select); + + if (edges_separate) { + bmesh_vert_separate__cleanup(bm, edges_separate); + } } +/** \} */ + + /** * \brief Splice Edge * * Splice two unique edges which share the same two vertices into one edge. + * (\a e_src into \a e_dst, removing e_src). * * \return Success * * \note Edges must already have the same vertices. */ -bool BM_edge_splice(BMesh *bm, BMEdge *e, BMEdge *e_target) +bool BM_edge_splice(BMesh *bm, BMEdge *e_dst, BMEdge *e_src) { BMLoop *l; - if (!BM_vert_in_edge(e, e_target->v1) || !BM_vert_in_edge(e, e_target->v2)) { + if (!BM_vert_in_edge(e_src, e_dst->v1) || !BM_vert_in_edge(e_src, e_dst->v2)) { /* not the same vertices can't splice */ /* the caller should really make sure this doesn't happen ever @@ -2224,21 +2322,21 @@ bool BM_edge_splice(BMesh *bm, BMEdge *e, BMEdge *e_target) return false; } - while (e->l) { - l = e->l; - BLI_assert(BM_vert_in_edge(e_target, l->v)); - BLI_assert(BM_vert_in_edge(e_target, l->next->v)); - bmesh_radial_loop_remove(l, e); - bmesh_radial_append(e_target, l); + while (e_src->l) { + l = e_src->l; + BLI_assert(BM_vert_in_edge(e_dst, l->v)); + BLI_assert(BM_vert_in_edge(e_dst, l->next->v)); + bmesh_radial_loop_remove(l, e_src); + bmesh_radial_append(e_dst, l); } - BLI_assert(bmesh_radial_length(e->l) == 0); + BLI_assert(bmesh_radial_length(e_src->l) == 0); - BM_CHECK_ELEMENT(e); - BM_CHECK_ELEMENT(e_target); + BM_CHECK_ELEMENT(e_src); + BM_CHECK_ELEMENT(e_dst); /* removes from disks too */ - BM_edge_kill(bm, e); + BM_edge_kill(bm, e_src); return true; } @@ -2254,8 +2352,9 @@ bool BM_edge_splice(BMesh *bm, BMEdge *e, BMEdge *e_target) * \note Does nothing if \a l_sep is already the only loop in the * edge radial. */ -void bmesh_edge_separate(BMesh *bm, BMEdge *e, BMLoop *l_sep, - const bool copy_select) +void bmesh_edge_separate( + BMesh *bm, BMEdge *e, BMLoop *l_sep, + const bool copy_select) { BMEdge *e_new; #ifndef NDEBUG @@ -2266,7 +2365,7 @@ void bmesh_edge_separate(BMesh *bm, BMEdge *e, BMLoop *l_sep, BLI_assert(e->l); if (BM_edge_is_boundary(e)) { - /* no cut required */ + BLI_assert(0); /* no cut required */ return; } @@ -2299,72 +2398,245 @@ void bmesh_edge_separate(BMesh *bm, BMEdge *e, BMLoop *l_sep, */ BMVert *bmesh_urmv_loop(BMesh *bm, BMLoop *l_sep) { - BMVert **vtar; - int len, i; BMVert *v_new = NULL; BMVert *v_sep = l_sep->v; + BMEdge *e_iter; + BMEdge *edges[2]; + int i; /* peel the face from the edge radials on both sides of the * loop vert, disconnecting the face from its fan */ bmesh_edge_separate(bm, l_sep->e, l_sep, false); bmesh_edge_separate(bm, l_sep->prev->e, l_sep->prev, false); - if (bmesh_disk_count(v_sep) == 2) { - /* If there are still only two edges out of v_sep, then - * this whole URMV was just a no-op, so exit now. */ + /* do inline, below */ +#if 0 + if (BM_vert_edge_count_is_equal(v_sep, 2)) { return v_sep; } +#endif - /* Update the disk start, so that v->e points to an edge - * not touching the split loop. This is so that BM_vert_split - * will leave the original v_sep on some *other* fan (not the - * one-face fan that holds the unglue face). */ - while (v_sep->e == l_sep->e || v_sep->e == l_sep->prev->e) { - v_sep->e = bmesh_disk_edge_next(v_sep->e, v_sep); + /* Search for an edge unattached to this loop */ + e_iter = v_sep->e; + while (!ELEM(e_iter, l_sep->e, l_sep->prev->e)) { + e_iter = bmesh_disk_edge_next(e_iter, v_sep); + + /* We've come back around to the initial edge, all touch this loop. + * If there are still only two edges out of v_sep, + * then this whole URMV was just a no-op, so exit now. */ + if (e_iter == v_sep->e) { + BLI_assert(BM_vert_edge_count_is_equal(v_sep, 2)); + return v_sep; + } } - /* Split all fans connected to the vert, duplicating it for - * each fans. */ - bmesh_vert_separate(bm, v_sep, &vtar, &len, false); + v_sep->e = l_sep->e; - /* There should have been at least two fans cut apart here, - * otherwise the early exit would have kicked in. */ - BLI_assert(len >= 2); + v_new = BM_vert_create(bm, v_sep->co, v_sep, BM_CREATE_NOP); - v_new = l_sep->v; + edges[0] = l_sep->e; + edges[1] = l_sep->prev->e; - /* Desired result here is that a new vert should always be - * created for the unglue face. This is so we can glue any - * extras back into the original vert. */ - BLI_assert(v_new != v_sep); - BLI_assert(v_sep == vtar[0]); + for (i = 0; i < ARRAY_SIZE(edges); i++) { + BMEdge *e = edges[i]; + bmesh_edge_vert_swap(e, v_new, v_sep); + } - /* If there are more than two verts as a result, glue together - * all the verts except the one this URMV intended to create */ - if (len > 2) { - for (i = 0; i < len; i++) { - if (vtar[i] == v_new) { - break; + BLI_assert(v_sep != l_sep->v); + BLI_assert(v_sep->e != l_sep->v->e); + + BM_CHECK_ELEMENT(l_sep); + BM_CHECK_ELEMENT(v_sep); + BM_CHECK_ELEMENT(edges[0]); + BM_CHECK_ELEMENT(edges[1]); + BM_CHECK_ELEMENT(v_new); + + return v_new; +} + +/** + * A version of #bmesh_urmv_loop that disconnects multiple loops at once. + * + * Handles the task of finding fans boundaris. + */ +BMVert *bmesh_urmv_loop_multi( + BMesh *bm, BMLoop **larr, int larr_len) +{ + BMVert *v_sep = larr[0]->v; + BMVert *v_new; + int i; + bool is_mixed_any = false; + + BLI_SMALLSTACK_DECLARE(edges, BMEdge *); + +#define LOOP_VISIT _FLAG_WALK +#define EDGE_VISIT _FLAG_WALK + + for (i = 0; i < larr_len; i++) { + BMLoop *l_sep = larr[i]; + + /* all must be from the same vert! */ + BLI_assert(v_sep == l_sep->v); + + BLI_assert(!BM_ELEM_API_FLAG_TEST(l_sep, LOOP_VISIT)); + BM_ELEM_API_FLAG_ENABLE(l_sep, LOOP_VISIT); + + /* weak! but it makes it simpler to check for edges to split + * while doing a radial loop (where loops may be adjacent) */ + BM_ELEM_API_FLAG_ENABLE(l_sep->next, LOOP_VISIT); + BM_ELEM_API_FLAG_ENABLE(l_sep->prev, LOOP_VISIT); + } + + for (i = 0; i < larr_len; i++) { + BMLoop *l_sep = larr[i]; + + BMLoop *loop_pair[2] = {l_sep, l_sep->prev}; + int j; + for (j = 0; j < ARRAY_SIZE(loop_pair); j++) { + BMEdge *e = loop_pair[j]->e; + if (!BM_ELEM_API_FLAG_TEST(e, EDGE_VISIT)) { + BMLoop *l_iter, *l_first; + bool is_mixed = false; + + BM_ELEM_API_FLAG_ENABLE(e, EDGE_VISIT); + + l_iter = l_first = e->l; + do { + if (!BM_ELEM_API_FLAG_TEST(l_iter, LOOP_VISIT)) { + is_mixed = true; + is_mixed_any = true; + break; + } + } while ((l_iter = l_iter->radial_next) != l_first); + + if (is_mixed) { + /* ensure the first loop is one we don't own so we can do a quick check below + * on the edge's loop-flag to see if the edge is mixed or not. */ + e->l = l_iter; + } + BLI_SMALLSTACK_PUSH(edges, e); } } + } + + if (is_mixed_any == false) { + /* all loops in 'larr' are the soul owners of their edges. + * nothing to split away from, this is a no-op */ + v_new = v_sep; + } + else { + BMEdge *e; + + BLI_assert(!BLI_SMALLSTACK_IS_EMPTY(edges)); + + v_new = BM_vert_create(bm, v_sep->co, v_sep, BM_CREATE_NOP); + while ((e = BLI_SMALLSTACK_POP(edges))) { + BMLoop *l_iter, *l_first, *l_next; + BMEdge *e_new; + + /* disable so copied edge isn't left dirty (loop edges are cleared last too) */ + BM_ELEM_API_FLAG_DISABLE(e, EDGE_VISIT); + + if (!BM_ELEM_API_FLAG_TEST(e->l, LOOP_VISIT)) { + /* edge has some loops owned by us, some owned by other loops */ + BMVert *e_new_v_pair[2]; - if (i != len) { - /* Swap the single vert that was needed for the - * unglue into the last array slot */ - SWAP(BMVert *, vtar[i], vtar[len - 1]); + if (e->v1 == v_sep) { + e_new_v_pair[0] = v_new; + e_new_v_pair[1] = e->v2; + } + else { + BLI_assert(v_sep == e->v2); + e_new_v_pair[0] = e->v1; + e_new_v_pair[1] = v_new; + } - /* And then glue the rest back together */ - for (i = 1; i < len - 1; i++) { - BM_vert_splice(bm, vtar[i], vtar[0]); + e_new = BM_edge_create(bm, UNPACK2(e_new_v_pair), e, BM_CREATE_NOP); + + /* now moved all loops from 'larr' to this newly created edge */ + l_iter = l_first = e->l; + do { + l_next = l_iter->radial_next; + if (BM_ELEM_API_FLAG_TEST(l_iter, LOOP_VISIT)) { + bmesh_radial_loop_remove(l_iter, e); + bmesh_radial_append(e_new, l_iter); + l_iter->e = e_new; + } + } while ((l_iter = l_next) != l_first); + } + else { + /* we own the edge entirely, replace the vert */ + bmesh_disk_vert_replace(e, v_new, v_sep); } + + /* loop vert is handled last! */ } } - MEM_freeN(vtar); + for (i = 0; i < larr_len; i++) { + BMLoop *l_sep = larr[i]; + + l_sep->v = v_new; + + BLI_assert(BM_ELEM_API_FLAG_TEST(l_sep, LOOP_VISIT)); + BLI_assert(BM_ELEM_API_FLAG_TEST(l_sep->prev, LOOP_VISIT)); + BLI_assert(BM_ELEM_API_FLAG_TEST(l_sep->next, LOOP_VISIT)); + BM_ELEM_API_FLAG_DISABLE(l_sep, LOOP_VISIT); + BM_ELEM_API_FLAG_DISABLE(l_sep->prev, LOOP_VISIT); + BM_ELEM_API_FLAG_DISABLE(l_sep->next, LOOP_VISIT); + + + BM_ELEM_API_FLAG_DISABLE(l_sep->prev->e, EDGE_VISIT); + BM_ELEM_API_FLAG_DISABLE(l_sep->e, EDGE_VISIT); + } + +#undef LOOP_VISIT +#undef EDGE_VISIT + + return v_new; +} + +static void bmesh_edge_vert_swap__recursive(BMEdge *e, BMVert *v_dst, BMVert *v_src) +{ + BMLoop *l_iter, *l_first; + + BLI_assert(ELEM(v_src, e->v1, e->v2)); + bmesh_disk_vert_replace(e, v_dst, v_src); + + l_iter = l_first = e->l; + do { + if (l_iter->v == v_src) { + l_iter->v = v_dst; + if (BM_vert_in_edge(l_iter->prev->e, v_src)) { + bmesh_edge_vert_swap__recursive(l_iter->prev->e, v_dst, v_src); + } + } + else if (l_iter->next->v == v_src) { + l_iter->next->v = v_dst; + if (BM_vert_in_edge(l_iter->next->e, v_src)) { + bmesh_edge_vert_swap__recursive(l_iter->next->e, v_dst, v_src); + } + } + else { + BLI_assert(l_iter->prev->v != v_src); + } + } while ((l_iter = l_iter->radial_next) != l_first); +} +/** + * This function assumes l_sep is apart of a larger fan which has already been + * isolated by calling bmesh_edge_separate to segregate it radially. + */ +BMVert *bmesh_urmv_loop_region(BMesh *bm, BMLoop *l_sep) +{ + BMVert *v_new = BM_vert_create(bm, l_sep->v->co, l_sep->v, BM_CREATE_NOP); + /* passing either 'l_sep->e', 'l_sep->prev->e' will work */ + bmesh_edge_vert_swap__recursive(l_sep->e, v_new, l_sep->v); + BLI_assert(l_sep->v == v_new); return v_new; } + /** * \brief Unglue Region Make Vert (URMV) * |