Merged changes in the trunk up to revision 55357.
[blender-staging.git] / source / blender / editors / mesh / editmesh_tools.c
index c35f942cf33ddf0b9ed43c3ff05c2e9d6c6f5ab6..36a1d30c85e957d4766fb72f1089e35927b0043e 100644 (file)
 #include "DNA_object_types.h"
 #include "DNA_scene_types.h"
 
-#include "RNA_define.h"
-#include "RNA_access.h"
-#include "RNA_enum_types.h"
-
 #include "BLI_blenlib.h"
 #include "BLI_noise.h"
 #include "BLI_math.h"
 #include "BKE_main.h"
 #include "BKE_tessmesh.h"
 
+#include "RNA_define.h"
+#include "RNA_access.h"
+#include "RNA_enum_types.h"
+
 #include "WM_api.h"
 #include "WM_types.h"
 
@@ -77,6 +77,8 @@
 
 #include "mesh_intern.h"
 
+#define USE_FACE_CREATE_SEL_EXTEND
+
 #define MVAL_PIXEL_MARGIN  5.0f
 
 /* allow accumulated normals to form a new direction but don't
@@ -333,10 +335,7 @@ static short edbm_extrude_edge(Object *obedit, BMEditMesh *em, const char hflag,
                                        mult_m4_m4m4(mtx, imtx, obedit->obmat);
                                }
 
-                               for (edge = BM_iter_new(&iter, bm, BM_EDGES_OF_MESH, NULL);
-                                    edge;
-                                    edge = BM_iter_step(&iter))
-                               {
+                               BM_ITER_MESH (edge, &iter, bm, BM_EDGES_OF_MESH) {
                                        if (BM_elem_flag_test(edge, hflag) &&
                                            BM_edge_is_boundary(edge) &&
                                            BM_elem_flag_test(edge->l->f, hflag))
@@ -779,7 +778,7 @@ void MESH_OT_select_interior_faces(wmOperatorType *ot)
 }
 
 /* *************** add-click-mesh (extrude) operator ************** */
-static int edbm_dupli_extrude_cursor_invoke(bContext *C, wmOperator *op, wmEvent *event)
+static int edbm_dupli_extrude_cursor_invoke(bContext *C, wmOperator *op, const wmEvent *event)
 {
        ViewContext vc;
        BMVert *v1;
@@ -863,7 +862,7 @@ static int edbm_dupli_extrude_cursor_invoke(bContext *C, wmOperator *op, wmEvent
                copy_v3_v3(min, cent);
 
                mul_m4_v3(vc.obedit->obmat, min);  /* view space */
-               view3d_get_view_aligned_coordinate(&vc, min, event->mval, TRUE);
+               view3d_get_view_aligned_coordinate(vc.ar, min, event->mval, true);
                mul_m4_v3(vc.obedit->imat, min); // back in object space
 
                sub_v3_v3(min, cent);
@@ -912,7 +911,7 @@ static int edbm_dupli_extrude_cursor_invoke(bContext *C, wmOperator *op, wmEvent
                BMOIter oiter;
                
                copy_v3_v3(min, curs);
-               view3d_get_view_aligned_coordinate(&vc, min, event->mval, FALSE);
+               view3d_get_view_aligned_coordinate(vc.ar, min, event->mval, false);
 
                invert_m4_m4(vc.obedit->imat, vc.obedit->obmat);
                mul_m4_v3(vc.obedit->imat, min); // back in object space
@@ -1099,6 +1098,140 @@ static int edbm_add_edge_face__smooth_get(BMesh *bm)
        return (vote_on_smooth[0] < vote_on_smooth[1]);
 }
 
+#ifdef USE_FACE_CREATE_SEL_EXTEND
+/**
+ * Function used to get a fixed number of edges linked to a vertex that passes a test function.
+ * This is used so we can request all boundary edges connected to a vertex for eg.
+ */
+static int edbm_add_edge_face_exec__vert_edge_lookup(BMVert *v, BMEdge *e_used, BMEdge **e_arr, const int e_arr_len,
+                                                     bool (* func)(BMEdge *))
+{
+       BMIter iter;
+       BMEdge *e_iter;
+       int i = 0;
+       BM_ITER_ELEM (e_iter, &iter, v, BM_EDGES_OF_VERT) {
+               if (BM_elem_flag_test(e_iter, BM_ELEM_HIDDEN) == false) {
+                       if ((e_used == NULL) || (e_used != e_iter)) {
+                               if (func(e_iter)) {
+                                       e_arr[i++] = e_iter;
+                                       if (i >= e_arr_len) {
+                                               break;
+                                       }
+                               }
+                       }
+               }
+       }
+       return i;
+}
+
+static BMElem *edbm_add_edge_face_exec__tricky_extend_sel(BMesh *bm)
+{
+       BMIter iter;
+       bool found = false;
+
+       if (bm->totvertsel == 1 && bm->totedgesel == 0 && bm->totfacesel == 0) {
+               /* first look for 2 boundary edges */
+               BMVert *v;
+
+               BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) {
+                       if (BM_elem_flag_test(v, BM_ELEM_SELECT)) {
+                               found = true;
+                               break;
+                       }
+               }
+
+               if (found) {
+                       BMEdge *ed_pair[3];
+                       if (
+                           ((edbm_add_edge_face_exec__vert_edge_lookup(v, NULL, ed_pair, 3, BM_edge_is_wire) == 2) &&
+                            (BM_edge_share_face_check(ed_pair[0], ed_pair[1]) == false)) ||
+
+                           ((edbm_add_edge_face_exec__vert_edge_lookup(v, NULL, ed_pair, 3, BM_edge_is_boundary) == 2) &&
+                            (BM_edge_share_face_check(ed_pair[0], ed_pair[1]) == false))
+                           )
+                       {
+                               BMEdge *e_other = BM_edge_exists(BM_edge_other_vert(ed_pair[0], v),
+                                                                BM_edge_other_vert(ed_pair[1], v));
+                               BM_edge_select_set(bm, ed_pair[0], true);
+                               BM_edge_select_set(bm, ed_pair[1], true);
+                               if (e_other) {
+                                       BM_edge_select_set(bm, e_other, true);
+                               }
+                               return (BMElem *)v;
+                       }
+               }
+       }
+       else if (bm->totvertsel == 2 && bm->totedgesel == 1 && bm->totfacesel == 0) {
+               /* first look for 2 boundary edges */
+               BMEdge *e;
+
+               BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
+                       if (BM_elem_flag_test(e, BM_ELEM_SELECT)) {
+                               found = true;
+                               break;
+                       }
+               }
+               if (found) {
+                       BMEdge *ed_pair_v1[2];
+                       BMEdge *ed_pair_v2[2];
+                       if (
+                           ((edbm_add_edge_face_exec__vert_edge_lookup(e->v1, e, ed_pair_v1, 2, BM_edge_is_wire) == 1) &&
+                            (edbm_add_edge_face_exec__vert_edge_lookup(e->v2, e, ed_pair_v2, 2, BM_edge_is_wire) == 1) &&
+                            (BM_edge_share_face_check(e, ed_pair_v1[0]) == false) &&
+                            (BM_edge_share_face_check(e, ed_pair_v2[0]) == false)) ||
+
+                           ((edbm_add_edge_face_exec__vert_edge_lookup(e->v1, e, ed_pair_v1, 2, BM_edge_is_boundary) == 1) &&
+                            (edbm_add_edge_face_exec__vert_edge_lookup(e->v2, e, ed_pair_v2, 2, BM_edge_is_boundary) == 1) &&
+                            (BM_edge_share_face_check(e, ed_pair_v1[0]) == false) &&
+                            (BM_edge_share_face_check(e, ed_pair_v2[0]) == false))
+                           )
+                       {
+                               BMVert *v1_other = BM_edge_other_vert(ed_pair_v1[0], e->v1);
+                               BMVert *v2_other = BM_edge_other_vert(ed_pair_v2[0], e->v2);
+                               BMEdge *e_other = (v1_other != v2_other) ? BM_edge_exists(v1_other, v2_other) : NULL;
+                               BM_edge_select_set(bm, ed_pair_v1[0], true);
+                               BM_edge_select_set(bm, ed_pair_v2[0], true);
+                               if (e_other) {
+                                       BM_edge_select_set(bm, e_other, true);
+                               }
+                               return (BMElem *)e;
+                       }
+               }
+       }
+
+       return NULL;
+}
+static void edbm_add_edge_face_exec__tricky_finalize_sel(BMesh *bm, BMElem *ele_desel, BMFace *f)
+{
+       /* now we need to find the edge that isnt connected to this element */
+       BM_select_history_clear(bm);
+
+       if (ele_desel->head.htype == BM_VERT) {
+               BMLoop *l = BM_face_vert_share_loop(f, (BMVert *)ele_desel);
+               BLI_assert(f->len == 3);
+               BM_face_select_set(bm, f, false);
+               BM_vert_select_set(bm, (BMVert *)ele_desel, false);
+
+               BM_edge_select_set(bm, l->next->e, true);
+               BM_select_history_store(bm, l->next->e);
+       }
+       else {
+               BMLoop *l = BM_face_edge_share_loop(f, (BMEdge *)ele_desel);
+               BLI_assert(f->len == 4 || f->len == 3);
+               BM_face_select_set(bm, f, false);
+               BM_edge_select_set(bm, (BMEdge *)ele_desel, false);
+               if (f->len == 4) {
+                       BM_edge_select_set(bm, l->next->next->e, true);
+                       BM_select_history_store(bm, l->next->next->e);
+               }
+               else {
+                       BM_vert_select_set(bm, l->next->next->v, true);
+                       BM_select_history_store(bm, l->next->next->v);
+               }
+       }
+}
+#endif  /* USE_FACE_CREATE_SEL_EXTEND */
+
 static int edbm_add_edge_face_exec(bContext *C, wmOperator *op)
 {
        BMOperator bmop;
@@ -1107,6 +1240,15 @@ static int edbm_add_edge_face_exec(bContext *C, wmOperator *op)
        const short use_smooth = edbm_add_edge_face__smooth_get(em->bm);
        /* when this is used to dissolve we could avoid this, but checking isnt too slow */
 
+#ifdef USE_FACE_CREATE_SEL_EXTEND
+       BMElem *ele_desel;
+       BMFace *ele_desel_face;
+
+       /* be extra clever, figure out if a partial selection should be extended so we can create geometry
+        * with single vert or single edge selection */
+       ele_desel = edbm_add_edge_face_exec__tricky_extend_sel(em->bm);
+#endif
+
        if (!EDBM_op_init(em, &bmop, op,
                          "contextual_create geom=%hfev mat_nr=%i use_smooth=%b",
                          BM_ELEM_SELECT, em->mat_nr, use_smooth))
@@ -1115,8 +1257,22 @@ static int edbm_add_edge_face_exec(bContext *C, wmOperator *op)
        }
        
        BMO_op_exec(em->bm, &bmop);
-       BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, TRUE);
-       BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, TRUE);
+
+#ifdef USE_FACE_CREATE_SEL_EXTEND
+       /* normally we would want to leave the new geometry selected,
+        * but being able to press F many times to add geometry is too useful! */
+       if (ele_desel &&
+           (BMO_slot_buffer_count(bmop.slots_out, "faces.out") == 1) &&
+           (ele_desel_face = BMO_slot_buffer_get_first(bmop.slots_out, "faces.out")))
+       {
+               edbm_add_edge_face_exec__tricky_finalize_sel(em->bm, ele_desel, ele_desel_face);
+       }
+       else
+#endif
+       {
+               BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "faces.out", BM_FACE, BM_ELEM_SELECT, true);
+               BMO_slot_buffer_hflag_enable(em->bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, true);
+       }
 
        if (!EDBM_op_finish(em, &bmop, op, TRUE)) {
                return OPERATOR_CANCELLED;
@@ -1355,7 +1511,7 @@ static int edbm_duplicate_exec(bContext *C, wmOperator *op)
        return OPERATOR_FINISHED;
 }
 
-static int edbm_duplicate_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
+static int edbm_duplicate_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
 {
        WM_cursor_wait(1);
        edbm_duplicate_exec(C, op);
@@ -2005,12 +2161,22 @@ static int merge_firstlast(BMEditMesh *em, int first, int uvmerge, wmOperator *w
        BMVert *mergevert;
        BMEditSelection *ese;
 
+       /* operator could be called directly from shortcut or python,
+        * so do extra check for data here
+        */
+
        /* do sanity check in mergemenu in edit.c ?*/
        if (first == 0) {
+               if (!em->bm->selected.last || ((BMEditSelection *)em->bm->selected.last)->htype != BM_VERT)
+                       return OPERATOR_CANCELLED;
+
                ese = em->bm->selected.last;
                mergevert = (BMVert *)ese->ele;
        }
        else {
+               if (!em->bm->selected.first || ((BMEditSelection *)em->bm->selected.first)->htype != BM_VERT)
+                       return OPERATOR_CANCELLED;
+
                ese = em->bm->selected.first;
                mergevert = (BMVert *)ese->ele;
        }
@@ -2293,8 +2459,12 @@ static int edbm_select_vertex_path_exec(bContext *C, wmOperator *op)
                        if (!BM_elem_flag_test(eve, BM_ELEM_SELECT) || BM_elem_flag_test(eve, BM_ELEM_HIDDEN))
                                continue;
 
-                       if (svert == NULL) svert = eve;
-                       else if (evert == NULL) evert = eve;
+                       if (svert == NULL) {
+                               svert = eve;
+                       }
+                       else if (evert == NULL) {
+                               evert = eve;
+                       }
                        else {
                                /* more than two vertices are selected,
                                 * show warning message and cancel operator */
@@ -3225,6 +3395,16 @@ static int edbm_separate_exec(bContext *C, wmOperator *op)
                Base *base = CTX_data_active_base(C);
                BMEditMesh *em = BMEdit_FromObject(base->object);
 
+               if (type == 0) {
+                       if ((em->bm->totvertsel == 0) &&
+                           (em->bm->totedgesel == 0) &&
+                           (em->bm->totfacesel == 0))
+                       {
+                               BKE_report(op->reports, RPT_ERROR, "Nothing selected");
+                               return OPERATOR_CANCELLED;
+                       }
+               }
+
                /* editmode separate */
                if      (type == 0) retval = mesh_separate_selected(bmain, scene, base, em->bm);
                else if (type == 1) retval = mesh_separate_material(bmain, scene, base, em->bm);
@@ -3689,7 +3869,7 @@ static int edbm_spin_exec(bContext *C, wmOperator *op)
 }
 
 /* get center and axis, in global coords */
-static int edbm_spin_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
+static int edbm_spin_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
 {
        Scene *scene = CTX_data_scene(C);
        View3D *v3d = CTX_wm_view3d(C);
@@ -3759,9 +3939,10 @@ static int edbm_screw_exec(bContext *C, wmOperator *op)
        /* find two vertices with valence count == 1, more or less is wrong */
        v1 = NULL;
        v2 = NULL;
-       for (eve = BM_iter_new(&iter, em->bm, BM_VERTS_OF_MESH, NULL); eve; eve = BM_iter_step(&iter)) {
+
+       BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
                valence = 0;
-               for (eed = BM_iter_new(&eiter, em->bm, BM_EDGES_OF_VERT, eve); eed; eed = BM_iter_step(&eiter)) {
+               BM_ITER_ELEM (eed, &eiter, eve, BM_EDGES_OF_VERT) {
                        if (BM_elem_flag_test(eed, BM_ELEM_SELECT)) {
                                valence++;
                        }
@@ -3812,7 +3993,7 @@ static int edbm_screw_exec(bContext *C, wmOperator *op)
 }
 
 /* get center and axis, in global coords */
-static int edbm_screw_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
+static int edbm_screw_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
 {
        Scene *scene = CTX_data_scene(C);
        View3D *v3d = CTX_wm_view3d(C);
@@ -4505,7 +4686,7 @@ static int edbm_sort_elements_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop
        int action = RNA_enum_get(ptr, "type");
 
        /* Only show seed for randomize action! */
-       if (strcmp(prop_id, "seed") == 0) {
+       if (STREQ(prop_id, "seed")) {
                if (action == SRT_RANDOMIZE)
                        return TRUE;
                else
@@ -4513,7 +4694,7 @@ static int edbm_sort_elements_draw_check_prop(PointerRNA *ptr, PropertyRNA *prop
        }
 
        /* Hide seed for reverse and randomize actions! */
-       if (strcmp(prop_id, "reverse") == 0) {
+       if (STREQ(prop_id, "reverse")) {
                if (ELEM(action, SRT_RANDOMIZE, SRT_REVERSE))
                        return FALSE;
                else
@@ -4804,7 +4985,7 @@ static int edbm_bevel_exec(bContext *C, wmOperator *op)
        return OPERATOR_FINISHED;
 }
 
-static int edbm_bevel_invoke(bContext *C, wmOperator *op, wmEvent *event)
+static int edbm_bevel_invoke(bContext *C, wmOperator *op, const wmEvent *event)
 {
        /* TODO make modal keymap (see fly mode) */
        RegionView3D *rv3d = CTX_wm_region_view3d(C);
@@ -4841,7 +5022,7 @@ static int edbm_bevel_invoke(bContext *C, wmOperator *op, wmEvent *event)
        return OPERATOR_RUNNING_MODAL;
 }
 
-static float edbm_bevel_mval_factor(wmOperator *op, wmEvent *event)
+static float edbm_bevel_mval_factor(wmOperator *op, const wmEvent *event)
 {
        BevelData *opdata = op->customdata;
        int use_dist = TRUE;
@@ -4880,7 +5061,7 @@ static float edbm_bevel_mval_factor(wmOperator *op, wmEvent *event)
        return factor;
 }
 
-static int edbm_bevel_modal(bContext *C, wmOperator *op, wmEvent *event)
+static int edbm_bevel_modal(bContext *C, wmOperator *op, const wmEvent *event)
 {
        BevelData *opdata = op->customdata;
        int segments = RNA_int_get(op->ptr, "segments");
@@ -5186,7 +5367,7 @@ static int edbm_inset_exec(bContext *C, wmOperator *op)
        return OPERATOR_FINISHED;
 }
 
-static int edbm_inset_invoke(bContext *C, wmOperator *op, wmEvent *event)
+static int edbm_inset_invoke(bContext *C, wmOperator *op, const wmEvent *event)
 {
        RegionView3D *rv3d = CTX_wm_region_view3d(C);
        InsetData *opdata;
@@ -5216,7 +5397,7 @@ static int edbm_inset_invoke(bContext *C, wmOperator *op, wmEvent *event)
        return OPERATOR_RUNNING_MODAL;
 }
 
-static int edbm_inset_modal(bContext *C, wmOperator *op, wmEvent *event)
+static int edbm_inset_modal(bContext *C, wmOperator *op, const wmEvent *event)
 {
        InsetData *opdata = op->customdata;