diff options
author | Campbell Barton <ideasman42@gmail.com> | 2012-04-20 18:36:06 +0400 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2012-04-20 18:36:06 +0400 |
commit | 380e6c2a58ea1a6dc88ef1d633d1563115dce3a4 (patch) | |
tree | 80f73d421f7bc2825e2080e6b489f59a7176cb15 | |
parent | ed81982049987802b0215dc1b49439addbb9f0b3 (diff) |
refactor rip tool out into vert/edge functions, was getting too unweildy having them mixed in.
-rw-r--r-- | source/blender/editors/mesh/editmesh_rip.c | 519 |
1 files changed, 282 insertions, 237 deletions
diff --git a/source/blender/editors/mesh/editmesh_rip.c b/source/blender/editors/mesh/editmesh_rip.c index 8ae05e3b1b8..751f0296e2d 100644 --- a/source/blender/editors/mesh/editmesh_rip.c +++ b/source/blender/editors/mesh/editmesh_rip.c @@ -342,297 +342,201 @@ static void edbm_ripsel_deselect_helper(BMesh *bm, EdgeLoopPair *eloop_pairs, } /* --- end 'ripsel' selection handling code --- */ -/* based on mouse cursor position, it defines how is being ripped */ -static int edbm_rip_invoke(bContext *C, wmOperator *op, wmEvent *event) +static int edbm_rip_call_edgesplit(BMEditMesh *em, wmOperator *op) +{ + BMOperator bmop; + + if (!EDBM_op_init(em, &bmop, op, "edgesplit edges=%he verts=%hv use_verts=%b", + BM_ELEM_TAG, BM_ELEM_SELECT, TRUE)) { + return FALSE; + } + BMO_op_exec(em->bm, &bmop); + if (!EDBM_op_finish(em, &bmop, op, TRUE)) { + return FALSE; + } + + return TRUE; +} + +/** + * This is the main vert ripping function (rip when one vertex is selected) + */ +static int edbm_rip_invoke__vert(bContext *C, wmOperator *op, wmEvent *event) { Object *obedit = CTX_data_edit_object(C); ARegion *ar = CTX_wm_region(C); RegionView3D *rv3d = CTX_wm_region_view3d(C); BMEditMesh *em = BMEdit_FromObject(obedit); BMesh *bm = em->bm; - BMOperator bmop; - BMIter iter, eiter, liter; + BMIter iter, liter; BMLoop *l; BMEdge *e, *e2; BMVert *v, *ripvert = NULL; - int i, singlesel = FALSE; + int i; float projectMat[4][4], fmval[3] = {event->mval[0], event->mval[1]}; float dist = FLT_MAX; float d; - const int totedge_orig = bm->totedge; - - EdgeLoopPair *eloop_pairs = NULL; - - /* running in face mode hardly makes sense, so convert to region loop and rip */ - if (em->bm->totfacesel) { - WM_operator_name_call(C, "MESH_OT_region_to_loop", WM_OP_INVOKE_DEFAULT, NULL); - } - /* note on selection: - * When calling edge split we operate on tagged edges rather then selected - * this is important because the edges to operate on are extended by one, - * but the selection is left alone. - * - * After calling edge split - the duplicated edges have the same selection state as the - * original, so all we do is de-select the far side from the mouse and we have a - * useful selection for grabbing. - */ + BMEditSelection ese; + int totboundary_edge = 0; ED_view3d_ob_project_mat_get(rv3d, obedit, projectMat); - /* BM_ELEM_SELECT --> BM_ELEM_TAG */ - BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) { - BM_elem_flag_set(e, BM_ELEM_TAG, BM_elem_flag_test(e, BM_ELEM_SELECT)); + /* find selected vert - same some time and check history first */ + if (EDBM_editselection_active_get(em, &ese) && ese.htype == BM_VERT) { + v = (BMVert *)ese.ele; } + else { + ese.ele = NULL; - /* handle case of one vert selected. identify - * closest edge around that vert to mouse cursor, - * then rip two adjacent edges in the vert fan. */ - - if (bm->totvertsel == 1 && bm->totedgesel == 0 && bm->totfacesel == 0) { - /* --- Vert-Rip --- */ - - BMEditSelection ese; - int totboundary_edge = 0; - singlesel = TRUE; - - /* find selected vert - same some time and check history first */ - if (EDBM_editselection_active_get(em, &ese) && ese.htype == BM_VERT) { - v = (BMVert *)ese.ele; - } - else { - ese.ele = NULL; - - BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { - if (BM_elem_flag_test(v, BM_ELEM_SELECT)) - break; - } + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + if (BM_elem_flag_test(v, BM_ELEM_SELECT)) + break; } + } - /* this should be impossible, but sanity checks are a good thing */ - if (!v) - return OPERATOR_CANCELLED; - - e2 = NULL; + /* this should be impossible, but sanity checks are a good thing */ + if (!v) + return OPERATOR_CANCELLED; - if (v->e) { - /* find closest edge to mouse cursor */ - BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) { - int is_boundary = BM_edge_is_boundary(e); - /* consider wire as boundary for this purpose, - * otherwise we can't a face away from a wire edge */ - totboundary_edge += (is_boundary != 0 || BM_edge_is_wire(e)); - if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { - if (is_boundary == FALSE && BM_edge_is_manifold(e)) { - d = edbm_rip_rip_edgedist(ar, projectMat, e->v1->co, e->v2->co, fmval); - if (d < dist) { - dist = d; - e2 = e; - } + e2 = NULL; + + if (v->e) { + /* find closest edge to mouse cursor */ + BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) { + int is_boundary = BM_edge_is_boundary(e); + /* consider wire as boundary for this purpose, + * otherwise we can't a face away from a wire edge */ + totboundary_edge += (is_boundary != 0 || BM_edge_is_wire(e)); + if (!BM_elem_flag_test(e, BM_ELEM_HIDDEN)) { + if (is_boundary == FALSE && BM_edge_is_manifold(e)) { + d = edbm_rip_rip_edgedist(ar, projectMat, e->v1->co, e->v2->co, fmval); + if (d < dist) { + dist = d; + e2 = e; } } } + } + } + + /* should we go ahead with edge rip or do we need to do special case, split off vertex?: + * split off vertex if... + * - we cant find an edge - this means we are ripping a faces vert that is connected to other + * geometry only at the vertex. + * - the boundary edge total is greater then 2, + * in this case edge split _can_ work but we get far nicer results if we use this special case. */ + if (totboundary_edge > 2) { + BMVert **vout; + int vout_len; + + BM_elem_select_set(bm, v, FALSE); + bmesh_vert_separate(bm, v, &vout, &vout_len); + + if (vout_len < 2) { + /* set selection back to avoid active-unselected vertex */ + BM_elem_select_set(bm, v, TRUE); + /* should never happen */ + BKE_report(op->reports, RPT_ERROR, "Error ripping vertex from faces"); + return OPERATOR_CANCELLED; } + else { + int vi_best = 0; - /* should we go ahead with edge rip or do we need to do special case, split off vertex?: - * split off vertex if... - * - we cant find an edge - this means we are ripping a faces vert that is connected to other - * geometry only at the vertex. - * - the boundary edge total is greater then 2, - * in this case edge split _can_ work but we get far nicer results if we use this special case. */ - if (totboundary_edge > 2) { - BMVert **vout; - int vout_len; - - BM_elem_select_set(bm, v, FALSE); - bmesh_vert_separate(bm, v, &vout, &vout_len); - - if (vout_len < 2) { - /* set selection back to avoid active-unselected vertex */ - BM_elem_select_set(bm, v, TRUE); - /* should never happen */ - BKE_report(op->reports, RPT_ERROR, "Error ripping vertex from faces"); - return OPERATOR_CANCELLED; + if (ese.ele) { + EDBM_editselection_remove(em, &ese.ele->head); } - else { - int vi_best = 0; - - if (ese.ele) { - EDBM_editselection_remove(em, &ese.ele->head); - } - dist = FLT_MAX; + dist = FLT_MAX; - for (i = 0; i < vout_len; i++) { - BM_ITER_ELEM (l, &iter, vout[i], BM_LOOPS_OF_VERT) { - if (!BM_elem_flag_test(l->f, BM_ELEM_HIDDEN)) { - float l_mid_co[3]; - BM_loop_face_tangent(l, l_mid_co); + for (i = 0; i < vout_len; i++) { + BM_ITER_ELEM (l, &iter, vout[i], BM_LOOPS_OF_VERT) { + if (!BM_elem_flag_test(l->f, BM_ELEM_HIDDEN)) { + float l_mid_co[3]; + BM_loop_face_tangent(l, l_mid_co); - /* scale to average of surrounding edge size, only needs to be approx */ - mul_v3_fl(l_mid_co, (BM_edge_length_calc(l->e) + BM_edge_length_calc(l->prev->e)) / 2.0f); - add_v3_v3(l_mid_co, v->co); + /* scale to average of surrounding edge size, only needs to be approx */ + mul_v3_fl(l_mid_co, (BM_edge_length_calc(l->e) + BM_edge_length_calc(l->prev->e)) / 2.0f); + add_v3_v3(l_mid_co, v->co); - d = edbm_rip_rip_edgedist(ar, projectMat, v->co, l_mid_co, fmval); + d = edbm_rip_rip_edgedist(ar, projectMat, v->co, l_mid_co, fmval); - if (d < dist) { - dist = d; - vi_best = i; - } + if (d < dist) { + dist = d; + vi_best = i; } } } + } - /* select the vert from the best region */ - v = vout[vi_best]; - BM_elem_select_set(bm, v, TRUE); - - if (ese.ele) { - EDBM_editselection_store(em, &v->head); - } + /* select the vert from the best region */ + v = vout[vi_best]; + BM_elem_select_set(bm, v, TRUE); - /* splice all others back together */ - if (vout_len > 2) { + if (ese.ele) { + EDBM_editselection_store(em, &v->head); + } - /* vout[0] == best - * vout[1] == glue - * vout[2+] == splice with glue - */ - if (vi_best != 0) { - SWAP(BMVert *, vout[0], vout[vi_best]); - vi_best = 0; - } + /* splice all others back together */ + if (vout_len > 2) { - for (i = 2; i < vout_len; i++) { - BM_vert_splice(bm, vout[i], vout[1]); - } + /* vout[0] == best + * vout[1] == glue + * vout[2+] == splice with glue + */ + if (vi_best != 0) { + SWAP(BMVert *, vout[0], vout[vi_best]); + vi_best = 0; } - MEM_freeN(vout); - - return OPERATOR_FINISHED; + for (i = 2; i < vout_len; i++) { + BM_vert_splice(bm, vout[i], vout[1]); + } } - } - - if (!e2) { - BKE_report(op->reports, RPT_ERROR, "Selected vertex has no edge/face pairs attached"); - return OPERATOR_CANCELLED; - } - /* rip two adjacent edges */ - if (BM_edge_is_boundary(e2) || BM_vert_face_count(v) == 2) { - l = e2->l; - ripvert = BM_face_vert_separate(bm, l->f, v); + MEM_freeN(vout); - BLI_assert(ripvert); - if (!ripvert) { - return OPERATOR_CANCELLED; - } - } - else if (BM_edge_is_manifold(e2)) { - l = e2->l; - e = BM_face_other_edge_loop(l->f, e2, v)->e; - BM_elem_flag_enable(e, BM_ELEM_TAG); - - l = e2->l->radial_next; - e = BM_face_other_edge_loop(l->f, e2, v)->e; - BM_elem_flag_enable(e, BM_ELEM_TAG); + return OPERATOR_FINISHED; } - - dist = FLT_MAX; } - else { - /* --- Edge-Rip --- */ - int totedge; - int all_minifold; - /* important this runs on the original selection, before tempering with tagging */ - eloop_pairs = edbm_ripsel_looptag_helper(bm); - - /* expand edge selection */ - BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { - e2 = NULL; - i = 0; - totedge = 0; - all_minifold = TRUE; - BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { - - if (!BM_edge_is_wire(e) && - !BM_elem_flag_test(e, BM_ELEM_HIDDEN)) - { - /* important to check selection rather then tag here - * else we get feedback loop */ - if (BM_elem_flag_test(e, BM_ELEM_SELECT)) { - e2 = e; - i++; - } - totedge++; - } + if (!e2) { + BKE_report(op->reports, RPT_ERROR, "Selected vertex has no edge/face pairs attached"); + return OPERATOR_CANCELLED; + } - /** #BM_vert_other_disk_edge has no hidden checks so don't check hidden here */ - if ((all_minifold == TRUE) && (BM_edge_is_manifold(e) == FALSE)) { - all_minifold = FALSE; - } - } - - /* single edge, extend */ - if (i == 1 && e2->l) { - if ((totedge == 4) || (all_minifold == FALSE)) { - BMLoop *l_a = e2->l; - BMLoop *l_b = l_a->radial_next; - - /* find the best face to follow, this wat the edge won't point away from - * the mouse when there are more then 4 (takes the shortest face fan around) */ - l = (edbm_rip_edge_side_measure(e2, l_a, ar, projectMat, fmval) < - edbm_rip_edge_side_measure(e2, l_b, ar, projectMat, fmval)) ? l_a : l_b; - - l = BM_face_other_edge_loop(l->f, e2, v); - l = l->radial_next; - l = BM_face_other_edge_loop(l->f, l->e, v); - - if (l) { - BM_elem_flag_enable(l->e, BM_ELEM_TAG); - } - } - else { - e = BM_vert_other_disk_edge(v, e2); + /* rip two adjacent edges */ + if (BM_edge_is_boundary(e2) || BM_vert_face_count(v) == 2) { + l = e2->l; + ripvert = BM_face_vert_separate(bm, l->f, v); - if (e) { - BM_elem_flag_enable(e, BM_ELEM_TAG); - } - } - } + BLI_assert(ripvert); + if (!ripvert) { + return OPERATOR_CANCELLED; } } - - if (!EDBM_op_init(em, &bmop, op, "edgesplit edges=%he verts=%hv use_verts=%b", - BM_ELEM_TAG, BM_ELEM_SELECT, TRUE)) { - if (eloop_pairs) MEM_freeN(eloop_pairs); - return OPERATOR_CANCELLED; + else if (BM_edge_is_manifold(e2)) { + l = e2->l; + e = BM_face_other_edge_loop(l->f, e2, v)->e; + BM_elem_flag_enable(e, BM_ELEM_TAG); + + l = e2->l->radial_next; + e = BM_face_other_edge_loop(l->f, e2, v)->e; + BM_elem_flag_enable(e, BM_ELEM_TAG); } - - BMO_op_exec(bm, &bmop); - if (totedge_orig == bm->totedge) { - EDBM_op_finish(em, &bmop, op, TRUE); + dist = FLT_MAX; - BKE_report(op->reports, RPT_ERROR, "No edges could be ripped"); - if (eloop_pairs) MEM_freeN(eloop_pairs); + if (!edbm_rip_call_edgesplit(em, op)) { return OPERATOR_CANCELLED; } - - if (singlesel) { + else { + /* --- select which vert --- */ BMVert *v_best = NULL; float l_prev_co[3], l_next_co[3], l_corner_co[3]; float scale; - /* not good enough! - original vert may not be attached to the closest edge */ -#if 0 - EDBM_flag_disable_all(em, BM_ELEM_SELECT); - BM_elem_select_set(bm, ripvert, TRUE); -#else - dist = FLT_MAX; BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) { if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { @@ -664,22 +568,162 @@ static int edbm_rip_invoke(bContext *C, wmOperator *op, wmEvent *event) if (v_best) { BM_elem_select_set(bm, v_best, TRUE); } -#endif + } + + return OPERATOR_FINISHED; +} + +/** + * This is the main edge ripping function + */ +static int edbm_rip_invoke__edge(bContext *C, wmOperator *op, wmEvent *event) +{ + Object *obedit = CTX_data_edit_object(C); + ARegion *ar = CTX_wm_region(C); + RegionView3D *rv3d = CTX_wm_region_view3d(C); + BMEditMesh *em = BMEdit_FromObject(obedit); + BMesh *bm = em->bm; + BMIter iter, eiter; + BMLoop *l; + BMEdge *e, *e2; + BMVert *v; + int i; + float projectMat[4][4], fmval[3] = {event->mval[0], event->mval[1]}; + + int totedge; + int all_minifold; + + EdgeLoopPair *eloop_pairs; + + ED_view3d_ob_project_mat_get(rv3d, obedit, projectMat); + + /* important this runs on the original selection, before tempering with tagging */ + eloop_pairs = edbm_ripsel_looptag_helper(bm); + + /* expand edge selection */ + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + e2 = NULL; + i = 0; + totedge = 0; + all_minifold = TRUE; + BM_ITER_ELEM (e, &eiter, v, BM_EDGES_OF_VERT) { + + if (!BM_edge_is_wire(e) && + !BM_elem_flag_test(e, BM_ELEM_HIDDEN)) + { + /* important to check selection rather then tag here + * else we get feedback loop */ + if (BM_elem_flag_test(e, BM_ELEM_SELECT)) { + e2 = e; + i++; + } + totedge++; + } + + /** #BM_vert_other_disk_edge has no hidden checks so don't check hidden here */ + if ((all_minifold == TRUE) && (BM_edge_is_manifold(e) == FALSE)) { + all_minifold = FALSE; + } + } + + /* single edge, extend */ + if (i == 1 && e2->l) { + if ((totedge == 4) || (all_minifold == FALSE)) { + BMLoop *l_a = e2->l; + BMLoop *l_b = l_a->radial_next; + + /* find the best face to follow, this wat the edge won't point away from + * the mouse when there are more then 4 (takes the shortest face fan around) */ + l = (edbm_rip_edge_side_measure(e2, l_a, ar, projectMat, fmval) < + edbm_rip_edge_side_measure(e2, l_b, ar, projectMat, fmval)) ? l_a : l_b; + + l = BM_face_other_edge_loop(l->f, e2, v); + l = l->radial_next; + l = BM_face_other_edge_loop(l->f, l->e, v); + + if (l) { + BM_elem_flag_enable(l->e, BM_ELEM_TAG); + } + } + else { + e = BM_vert_other_disk_edge(v, e2); + + if (e) { + BM_elem_flag_enable(e, BM_ELEM_TAG); + } + } + } + } + + edbm_ripsel_deselect_helper(bm, eloop_pairs, + ar, projectMat, fmval); + MEM_freeN(eloop_pairs); + + if (!edbm_rip_call_edgesplit(em, op)) { + return OPERATOR_CANCELLED; + } + + return OPERATOR_FINISHED; +} + +/* based on mouse cursor position, it defines how is being ripped */ +static int edbm_rip_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BMEdit_FromObject(obedit); + BMesh *bm = em->bm; + BMIter iter; + BMEdge *e; + int singlesel = (bm->totvertsel == 1 && bm->totedgesel == 0 && bm->totfacesel == 0); + const int totedge_orig = bm->totedge; + int ret; + + /* running in face mode hardly makes sense, so convert to region loop and rip */ + if (em->bm->totfacesel) { + /* highly nifty but hard to sypport since the operator can fail and we're left + * with modified selection */ + // WM_operator_name_call(C, "MESH_OT_region_to_loop", WM_OP_INVOKE_DEFAULT, NULL); + + BKE_report(op->reports, RPT_ERROR, "Can't rip selected faces"); + return OPERATOR_CANCELLED; + } + + /* note on selection: + * When calling edge split we operate on tagged edges rather then selected + * this is important because the edges to operate on are extended by one, + * but the selection is left alone. + * + * After calling edge split - the duplicated edges have the same selection state as the + * original, so all we do is de-select the far side from the mouse and we have a + * useful selection for grabbing. + */ + + /* BM_ELEM_SELECT --> BM_ELEM_TAG */ + BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) { + BM_elem_flag_set(e, BM_ELEM_TAG, BM_elem_flag_test(e, BM_ELEM_SELECT)); + } + + /* split 2 main parts of this operator out into vertex and edge ripping */ + if (singlesel) { + ret = edbm_rip_invoke__vert(C, op, event); } else { - edbm_ripsel_deselect_helper(bm, eloop_pairs, - ar, projectMat, fmval); - MEM_freeN(eloop_pairs); + ret = edbm_rip_invoke__edge(C, op, event); } - EDBM_selectmode_flush(em); + if (ret == OPERATOR_CANCELLED) { + return OPERATOR_CANCELLED; + } - BLI_assert(singlesel ? (bm->totvertsel > 0) : (bm->totedgesel > 0)); + EDBM_selectmode_flush(em); - if (!EDBM_op_finish(em, &bmop, op, TRUE)) { + if (totedge_orig == bm->totedge) { + BKE_report(op->reports, RPT_ERROR, "No edges could be ripped"); return OPERATOR_CANCELLED; } + BLI_assert(singlesel ? (bm->totvertsel > 0) : (bm->totedgesel > 0)); + if (bm->totvertsel == 0) { return OPERATOR_CANCELLED; } @@ -689,6 +733,7 @@ static int edbm_rip_invoke(bContext *C, wmOperator *op, wmEvent *event) return OPERATOR_FINISHED; } + void MESH_OT_rip(wmOperatorType *ot) { /* identifiers */ |