From 22bbd1c51219aafb40adb3e9a206f660a621fd70 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 5 May 2015 07:11:31 +1000 Subject: BMesh: improve rip tool /w mon-manifold verts Can now rip from multiple fans (mixed single faces or larger regions) Also add BM_vert_is_manifold_region which only checks if a vert has disconnected fans. --- source/blender/bmesh/intern/bmesh_queries.c | 99 ++++++++++++++++++++++++++ source/blender/bmesh/intern/bmesh_queries.h | 3 + source/blender/editors/mesh/editmesh_rip.c | 106 +++++++++++++--------------- 3 files changed, 153 insertions(+), 55 deletions(-) (limited to 'source/blender') diff --git a/source/blender/bmesh/intern/bmesh_queries.c b/source/blender/bmesh/intern/bmesh_queries.c index 031fc68e66f..63e36178e0f 100644 --- a/source/blender/bmesh/intern/bmesh_queries.c +++ b/source/blender/bmesh/intern/bmesh_queries.c @@ -917,6 +917,105 @@ bool BM_vert_is_manifold(const BMVert *v) return (loop_num == loop_num_region); } +#define LOOP_VISIT _FLAG_WALK +#define EDGE_VISIT _FLAG_WALK + +static int bm_loop_region_count__recursive(BMEdge *e, BMVert *v) +{ + BMLoop *l_iter, *l_first; + int count = 0; + + BLI_assert(!BM_ELEM_API_FLAG_TEST(e, EDGE_VISIT)); + BM_ELEM_API_FLAG_ENABLE(e, EDGE_VISIT); + + l_iter = l_first = e->l; + do { + if (l_iter->v == v) { + BMEdge *e_other = l_iter->prev->e; + if (!BM_ELEM_API_FLAG_TEST(l_iter, LOOP_VISIT)) { + BM_ELEM_API_FLAG_ENABLE(l_iter, LOOP_VISIT); + count += 1; + } + if (!BM_ELEM_API_FLAG_TEST(e_other, EDGE_VISIT)) { + count += bm_loop_region_count__recursive(e_other, v); + } + } + else if (l_iter->next->v == v) { + BMEdge *e_other = l_iter->next->e; + if (!BM_ELEM_API_FLAG_TEST(l_iter->next, LOOP_VISIT)) { + BM_ELEM_API_FLAG_ENABLE(l_iter->next, LOOP_VISIT); + count += 1; + } + if (!BM_ELEM_API_FLAG_TEST(e_other, EDGE_VISIT)) { + count += bm_loop_region_count__recursive(e_other, v); + } + } + else { + BLI_assert(0); + } + } while ((l_iter = l_iter->radial_next) != l_first); + + return count; +} + +static int bm_loop_region_count__clear(BMLoop *l) +{ + int count = 0; + BMEdge *e_iter, *e_first; + + /* clear flags */ + e_iter = e_first = l->e; + do { + BM_ELEM_API_FLAG_DISABLE(e_iter, EDGE_VISIT); + if (e_iter->l) { + BMLoop *l_iter, *l_first; + l_iter = l_first = e_iter->l; + do { + if (l_iter->v == l->v) { + BM_ELEM_API_FLAG_DISABLE(l_iter, LOOP_VISIT); + count += 1; + } + } while ((l_iter = l_iter->radial_next) != l_first); + } + } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, l->v)) != e_first); + + return count; +} + +/** + * The number of loops connected to this loop (not including disconnected regions). + */ +int BM_loop_region_loops_count_ex(BMLoop *l, int *r_loop_total) +{ + const int count = bm_loop_region_count__recursive(l->e, l->v); + const int count_total = bm_loop_region_count__clear(l); + if (r_loop_total) { + *r_loop_total = count_total; + } + return count; +} + +#undef LOOP_VISIT +#undef EDGE_VISIT + +int BM_loop_region_loops_count(BMLoop *l) +{ + return BM_loop_region_loops_count_ex(l, NULL); +} + +/** + * A version of #BM_vert_is_manifold + * which only checks if we're connected to multiple isolated regions. + */ +bool BM_vert_is_manifold_region(const BMVert *v) +{ + BMLoop *l_first = BM_vert_find_first_loop((BMVert *)v); + int count, count_total; + + count = BM_loop_region_loops_count_ex(l_first, &count_total); + return (count == count_total); +} + /** * Check if the edge is convex or concave * (depends on face winding) diff --git a/source/blender/bmesh/intern/bmesh_queries.h b/source/blender/bmesh/intern/bmesh_queries.h index e4f7c78bdc1..f0a348db16a 100644 --- a/source/blender/bmesh/intern/bmesh_queries.h +++ b/source/blender/bmesh/intern/bmesh_queries.h @@ -85,12 +85,15 @@ bool BM_vert_is_wire(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); BLI_INLINE bool BM_edge_is_wire(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); bool BM_vert_is_manifold(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +bool BM_vert_is_manifold_region(const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); BLI_INLINE bool BM_edge_is_manifold(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); bool BM_vert_is_boundary(const BMVert *v) 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_edge_is_contiguous(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); bool BM_edge_is_convex(const BMEdge *e) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +int BM_loop_region_loops_count_ex(BMLoop *l, int *r_loop_total) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); +int BM_loop_region_loops_count(BMLoop *l) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); bool BM_loop_is_convex(const BMLoop *l) 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(); float BM_loop_point_side_of_loop_test(const BMLoop *l, const float co[3]) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); diff --git a/source/blender/editors/mesh/editmesh_rip.c b/source/blender/editors/mesh/editmesh_rip.c index abd88ae65aa..1e207a8edf8 100644 --- a/source/blender/editors/mesh/editmesh_rip.c +++ b/source/blender/editors/mesh/editmesh_rip.c @@ -532,14 +532,14 @@ static int edbm_rip_invoke__vert(bContext *C, wmOperator *op, const wmEvent *eve BMesh *bm = em->bm; BMIter iter, liter; BMLoop *l; - BMEdge *e, *e2; + BMEdge *e2; BMVert *v; const int totvert_orig = bm->totvert; int i; float projectMat[4][4], fmval[3] = {event->mval[0], event->mval[1]}; float dist_sq = FLT_MAX; float d; - bool is_wire; + bool is_wire, is_manifold_region; BMEditSelection ese; int totboundary_edge = 0; @@ -565,17 +565,19 @@ static int edbm_rip_invoke__vert(bContext *C, wmOperator *op, const wmEvent *eve } is_wire = BM_vert_is_wire(v); + is_manifold_region = BM_vert_is_manifold_region(v); e2 = NULL; - if (v->e) { + { + BMEdge *e; /* find closest edge to mouse cursor */ BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) { /* consider wire as boundary for this purpose, * otherwise we can't a face away from a wire edge */ totboundary_edge += (BM_edge_is_boundary(e) || BM_edge_is_wire(e)); if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { - if (BM_edge_is_manifold(e)) { + if ((is_manifold_region == false) || BM_edge_is_manifold(e)) { d = edbm_rip_edgedist_squared(ar, projectMat, e->v1->co, e->v2->co, fmval, INSET_DEFAULT); if ((e2 == NULL) || (d < dist_sq)) { dist_sq = d; @@ -584,67 +586,60 @@ static int edbm_rip_invoke__vert(bContext *C, wmOperator *op, const wmEvent *eve } } } - - /* if we are ripping a single vertex from 3 faces, - * then measure the distance to the face corner as well as the edge */ - if (BM_vert_face_count_is_equal(v, 3) && - BM_vert_edge_count_is_equal(v, 3)) - { - BMEdge *e_all[3]; - BMLoop *l_all[3]; - int i1, i2; - - BM_iter_as_array(bm, BM_EDGES_OF_VERT, v, (void **)e_all, 3); - BM_iter_as_array(bm, BM_LOOPS_OF_VERT, v, (void **)l_all, 3); - - /* not do a loop similar to the one above, but test against loops */ - for (i1 = 0; i1 < 3; i1++) { - /* consider wire as boundary for this purpose, - * otherwise we can't a face away from a wire edge */ - float l_mid_co[3]; - l = l_all[i1]; - edbm_calc_loop_co(l, l_mid_co); - d = edbm_rip_edgedist_squared(ar, projectMat, l->v->co, l_mid_co, fmval, INSET_DEFAULT); - if ((e2 == NULL) || (d < dist_sq)) { - dist_sq = d; - - /* find the edge that is not in this loop */ - e2 = NULL; - for (i2 = 0; i2 < 3; i2++) { - if (!BM_edge_in_loop(e_all[i2], l)) { - e2 = e_all[i2]; - break; - } - } - BLI_assert(e2 != NULL); - } - } - } } - if (e2) { + if (e2 && (is_manifold_region == false)) { /* Try to split off a non-manifold fan (when we have multiple disconnected fans) */ - - /* note: we're lazy here and first split then check there are any faces remaining, - * this isn't good practice, however its less hassle then checking for multiple-disconnected regions */ BMLoop *l_sep = e2->l->v == v ? e2->l : e2->l->next; BMVert *v_new; + BLI_assert(l_sep->v == v); v_new = bmesh_urmv_loop_region(bm, l_sep); - if (BM_vert_find_first_loop(v)) { - BM_vert_select_set(bm, v, false); - BM_select_history_remove(bm, v); + BLI_assert(BM_vert_find_first_loop(v)); - BM_vert_select_set(bm, v_new, true); - if (ese.ele) { - BM_select_history_store(bm, v_new); - } + BM_vert_select_set(bm, v, false); + BM_select_history_remove(bm, v); - return OPERATOR_FINISHED; + BM_vert_select_set(bm, v_new, true); + if (ese.ele) { + BM_select_history_store(bm, v_new); } - else { - /* rewind */ - BM_vert_splice(bm, v, v_new); + return OPERATOR_FINISHED; + } + + /* if we are ripping a single vertex from 3 faces, + * then measure the distance to the face corner as well as the edge */ + if (BM_vert_face_count_is_equal(v, 3) && + BM_vert_edge_count_is_equal(v, 3)) + { + BMEdge *e_all[3]; + BMLoop *l_all[3]; + int i1, i2; + + BM_iter_as_array(bm, BM_EDGES_OF_VERT, v, (void **)e_all, 3); + BM_iter_as_array(bm, BM_LOOPS_OF_VERT, v, (void **)l_all, 3); + + /* not do a loop similar to the one above, but test against loops */ + for (i1 = 0; i1 < 3; i1++) { + /* consider wire as boundary for this purpose, + * otherwise we can't a face away from a wire edge */ + float l_mid_co[3]; + l = l_all[i1]; + edbm_calc_loop_co(l, l_mid_co); + d = edbm_rip_edgedist_squared(ar, projectMat, l->v->co, l_mid_co, fmval, INSET_DEFAULT); + if ((e2 == NULL) || (d < dist_sq)) { + dist_sq = d; + + /* find the edge that is not in this loop */ + e2 = NULL; + for (i2 = 0; i2 < 3; i2++) { + if (!BM_edge_in_loop(e_all[i2], l)) { + e2 = e_all[i2]; + break; + } + } + BLI_assert(e2 != NULL); + } } } @@ -703,6 +698,7 @@ static int edbm_rip_invoke__vert(bContext *C, wmOperator *op, const wmEvent *eve } } else { + BMEdge *e; /* a wire vert, find the best edge */ BM_ITER_ELEM (e, &iter, vout[i], BM_EDGES_OF_VERT) { if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { -- cgit v1.2.3