forked from bartvdbraak/blender
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:
parent
e59bd19fa7
commit
22bbd1c512
@ -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)
|
||||
|
@ -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();
|
||||
|
@ -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)) {
|
||||
|
Loading…
Reference in New Issue
Block a user