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.
This commit is contained in:
Campbell Barton 2015-05-05 07:11:31 +10:00
parent e59bd19fa7
commit 22bbd1c512
3 changed files with 153 additions and 55 deletions

View File

@ -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)

View File

@ -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();

View File

@ -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)) {