bmesh: face creation from a single selected vertex/edge, now extends the selection...
authorCampbell Barton <ideasman42@gmail.com>
Sat, 9 Mar 2013 14:14:20 +0000 (14:14 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Sat, 9 Mar 2013 14:14:20 +0000 (14:14 +0000)
Selection is specifically so you can continuously fill in holes by tapping the Fkey.

Similar functionality to the F2 addon, however the mouse location isn't used.

source/blender/bmesh/intern/bmesh_operator_api.h
source/blender/bmesh/intern/bmesh_operators.c
source/blender/editors/mesh/editmesh_tools.c

index fd6571d..978aec0 100644 (file)
@@ -451,7 +451,7 @@ typedef struct BMOIter {
        char restrictmask; /* bitwise '&' with BMHeader.htype */
 } BMOIter;
 
-void *BMO_slot_buffer_elem_first(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name);
+void *BMO_slot_buffer_get_first(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name);
 
 void *BMO_iter_new(BMOIter *iter,
                    BMOpSlot slot_args[BMO_OP_MAX_SLOTS],  const char *slot_name,
index 00f8dbe..085b8b9 100644 (file)
@@ -1355,7 +1355,7 @@ static void bmo_flag_layer_clear(BMesh *bm)
        bm->elem_index_dirty &= ~(BM_VERT | BM_EDGE | BM_FACE);
 }
 
-void *BMO_slot_buffer_elem_first(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
+void *BMO_slot_buffer_get_first(BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name)
 {
        BMOpSlot *slot = BMO_slot_get(slot_args, slot_name);
        
index ffedef6..5da01da 100644 (file)
@@ -76,6 +76,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
@@ -1098,6 +1100,129 @@ 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] = {NULL};
+                       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))
+                           )
+                       {
+                               BM_edge_select_set(bm, ed_pair[0], true);
+                               BM_edge_select_set(bm, ed_pair[1], 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_a[2] = {NULL};
+                       BMEdge *ed_pair_b[2] = {NULL};
+                       if (
+                           ((edbm_add_edge_face_exec__vert_edge_lookup(e->v1, e, ed_pair_a, 2, BM_edge_is_wire) == 1) &&
+                            (edbm_add_edge_face_exec__vert_edge_lookup(e->v2, e, ed_pair_b, 2, BM_edge_is_wire) == 1) &&
+                            (BM_edge_share_face_check(e, ed_pair_a[0]) == false) &&
+                            (BM_edge_share_face_check(e, ed_pair_b[0]) == false)) ||
+
+                           ((edbm_add_edge_face_exec__vert_edge_lookup(e->v1, e, ed_pair_a, 2, BM_edge_is_boundary) == 1) &&
+                            (edbm_add_edge_face_exec__vert_edge_lookup(e->v2, e, ed_pair_b, 2, BM_edge_is_boundary) == 1) &&
+                            (BM_edge_share_face_check(e, ed_pair_a[0]) == false) &&
+                            (BM_edge_share_face_check(e, ed_pair_b[0]) == false))
+                           )
+                       {
+                               BM_edge_select_set(bm, ed_pair_a[0], true);
+                               BM_edge_select_set(bm, ed_pair_b[0], 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;
@@ -1106,6 +1231,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))
@@ -1114,8 +1248,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;