BMesh: select linked /w delimiters & wire edges
authorCampbell Barton <ideasman42@gmail.com>
Fri, 29 May 2015 04:27:39 +0000 (14:27 +1000)
committerCampbell Barton <ideasman42@gmail.com>
Fri, 29 May 2015 04:41:39 +0000 (14:41 +1000)
Add support for using edge delimiters mixed with wire edges.

Code isn't so elegant but users will expect this.

source/blender/bmesh/intern/bmesh_walkers.h
source/blender/bmesh/intern/bmesh_walkers_impl.c
source/blender/bmesh/intern/bmesh_walkers_private.h
source/blender/editors/mesh/editmesh_select.c

index 1877611..f5a801a 100644 (file)
@@ -115,6 +115,7 @@ void  BMW_reset(BMWalker *walker);
 enum {
        BMW_VERT_SHELL,
        BMW_LOOP_SHELL,
+       BMW_LOOP_SHELL_WIRE,
        BMW_FACE_SHELL,
        BMW_EDGELOOP,
        BMW_FACELOOP,
index 1fbfbba..0fb70b2 100644 (file)
@@ -300,17 +300,14 @@ static void *bmw_LoopShellWalker_yield(BMWalker *walker)
        return shellWalk->curloop;
 }
 
-static void *bmw_LoopShellWalker_step(BMWalker *walker)
+static void bmw_LoopShellWalker_step_impl(BMWalker *walker, BMLoop *l)
 {
-       BMwLoopShellWalker *swalk, owalk;
-       BMLoop *l;
-       int i;
        BMEdge *e_edj_pair[2];
+       int i;
 
-       BMW_state_remove_r(walker, &owalk);
-       swalk = &owalk;
+       /* seems paranoid, but one caller also walks edges */
+       BLI_assert(l->head.htype == BM_LOOP);
 
-       l = swalk->curloop;
        bmw_LoopShellWalker_visitLoop(walker, l->next);
        bmw_LoopShellWalker_visitLoop(walker, l->prev);
 
@@ -332,12 +329,160 @@ static void *bmw_LoopShellWalker_step(BMWalker *walker)
                        } while ((l_iter = l_iter->radial_next) != l_first);
                }
        }
+}
+
+static void *bmw_LoopShellWalker_step(BMWalker *walker)
+{
+       BMwLoopShellWalker *swalk, owalk;
+       BMLoop *l;
+
+       BMW_state_remove_r(walker, &owalk);
+       swalk = &owalk;
+
+       l = swalk->curloop;
+       bmw_LoopShellWalker_step_impl(walker, l);
 
        return l;
 }
 
 /** \} */
 
+/** \name LoopShell & 'Wire' Walker
+ * \{
+ *
+ * Piggyback ontop of #BMwLoopShellWalker, but also walk over wire edges
+ * This isn't elegant but users expect it when selecting linked,
+ * so we can support delimiters _and_ walking over wire edges.
+ *
+ * Details:
+ * - can yield edges (as well as loops)
+ * - only step over wire edges.
+ * - verts and edges are stored in `visit_set_alt`.
+ */
+
+static void bmw_LoopShellWalker_visitEdgeWire(BMWalker *walker, BMEdge *e)
+{
+       BMwLoopShellWireWalker *shellWalk = NULL;
+
+       BLI_assert(BM_edge_is_wire(e));
+
+       if (BLI_gset_haskey(walker->visit_set_alt, e)) {
+               return;
+       }
+
+       shellWalk = BMW_state_add(walker);
+       shellWalk->curelem = (BMElem *)e;
+       BLI_gset_insert(walker->visit_set_alt, e);
+}
+
+static void bmw_LoopShellWireWalker_visitVert(BMWalker *walker, BMVert *v, const BMEdge *e_from)
+{
+       BMEdge *e;
+
+       BLI_assert(v->head.htype == BM_VERT);
+
+       if (BLI_gset_haskey(walker->visit_set_alt, v)) {
+               return;
+       }
+
+       e = v->e;
+       do {
+               if (BM_edge_is_wire(e) && (e != e_from)) {
+                       BMVert *v_other;
+                       BMIter iter;
+                       BMLoop *l;
+
+                       bmw_LoopShellWalker_visitEdgeWire(walker, e);
+
+                       /* check if we step onto a non-wire vertex */
+                       v_other = BM_edge_other_vert(e, v);
+                       BM_ITER_ELEM (l, &iter, v_other, BM_LOOPS_OF_VERT) {
+                               bmw_LoopShellWalker_visitLoop(walker, l);
+                       }
+               }
+       } while ((e = BM_DISK_EDGE_NEXT(e, v)) != v->e);
+
+       BLI_gset_insert(walker->visit_set_alt, v);
+}
+
+static void bmw_LoopShellWireWalker_begin(BMWalker *walker, void *data)
+{
+       BMHeader *h = data;
+
+       if (UNLIKELY(h == NULL)) {
+               return;
+       }
+
+       bmw_LoopShellWalker_begin(walker, data);
+
+       switch (h->htype) {
+               case BM_LOOP:
+               {
+                       BMLoop *l = (BMLoop *)h;
+                       bmw_LoopShellWireWalker_visitVert(walker, l->v, NULL);
+                       break;
+               }
+
+               case BM_VERT:
+               {
+                       BMVert *v = (BMVert *)h;
+                       bmw_LoopShellWireWalker_visitVert(walker, v, NULL);
+                       break;
+               }
+               case BM_EDGE:
+               {
+                       BMEdge *e = (BMEdge *)h;
+                       if (BM_edge_is_wire(e)) {
+                               bmw_LoopShellWalker_visitEdgeWire(walker, e);
+                       }
+                       break;
+               }
+               case BM_FACE:
+               {
+                       /* wire verts will be walked over */
+                       break;
+               }
+               default:
+                       BLI_assert(0);
+       }
+}
+
+static void *bmw_LoopShellWireWalker_yield(BMWalker *walker)
+{
+       BMwLoopShellWireWalker *shellWalk = BMW_current_state(walker);
+       return shellWalk->curelem;
+}
+
+static void *bmw_LoopShellWireWalker_step(BMWalker *walker)
+{
+       BMwLoopShellWireWalker *swalk, owalk;
+
+       BMW_state_remove_r(walker, &owalk);
+       swalk = &owalk;
+
+       if (swalk->curelem->head.htype == BM_LOOP) {
+               BMLoop *l = (BMLoop *)swalk->curelem;
+
+               bmw_LoopShellWalker_step_impl(walker, l);
+
+               bmw_LoopShellWireWalker_visitVert(walker, l->v, NULL);
+
+               return l;
+       }
+       else {
+               BMEdge *e = (BMEdge *)swalk->curelem;
+
+               BLI_assert(e->head.htype == BM_EDGE);
+
+               bmw_LoopShellWireWalker_visitVert(walker, e->v1, e);
+               bmw_LoopShellWireWalker_visitVert(walker, e->v2, e);
+
+               return e;
+       }
+}
+
+/** \} */
+
 
 /** \name FaceShell Walker
  * \{
@@ -1364,7 +1509,7 @@ static BMWalker bmw_VertShellWalker_Type = {
 };
 
 static BMWalker bmw_LoopShellWalker_Type = {
-       BM_LOOP | BM_VERT | BM_EDGE,
+       BM_FACE | BM_LOOP | BM_EDGE | BM_VERT,
        bmw_LoopShellWalker_begin,
        bmw_LoopShellWalker_step,
        bmw_LoopShellWalker_yield,
@@ -1373,6 +1518,16 @@ static BMWalker bmw_LoopShellWalker_Type = {
        BM_EDGE, /* valid restrict masks */
 };
 
+static BMWalker bmw_LoopShellWireWalker_Type = {
+       BM_FACE | BM_LOOP | BM_EDGE | BM_VERT,
+       bmw_LoopShellWireWalker_begin,
+       bmw_LoopShellWireWalker_step,
+       bmw_LoopShellWireWalker_yield,
+       sizeof(BMwLoopShellWireWalker),
+       BMW_BREADTH_FIRST,
+       BM_EDGE, /* valid restrict masks */
+};
+
 static BMWalker bmw_FaceShellWalker_Type = {
        BM_EDGE,
        bmw_FaceShellWalker_begin,
@@ -1466,6 +1621,7 @@ static BMWalker bmw_ConnectedVertexWalker_Type = {
 BMWalker *bm_walker_types[] = {
        &bmw_VertShellWalker_Type,          /* BMW_VERT_SHELL */
        &bmw_LoopShellWalker_Type,          /* BMW_LOOP_SHELL */
+    &bmw_LoopShellWireWalker_Type,      /* BMW_LOOP_SHELL_WIRE */
        &bmw_FaceShellWalker_Type,          /* BMW_FACE_SHELL */
        &bmw_EdgeLoopWalker_Type,           /* BMW_EDGELOOP */
        &bmw_FaceLoopWalker_Type,           /* BMW_FACELOOP */
index 826d2ee..66d812b 100644 (file)
@@ -50,6 +50,11 @@ typedef struct BMwLoopShellWalker {
        BMLoop *curloop;
 } BMwLoopShellWalker;
 
+typedef struct BMwLoopShellWireWalker {
+       BMwGenericWalker header;
+       BMElem *curelem;
+} BMwLoopShellWireWalker;
+
 typedef struct BMwIslandboundWalker {
        BMwGenericWalker header;
        BMLoop *base;
index 30b3d46..81f7830 100644 (file)
@@ -2451,7 +2451,7 @@ static int edbm_select_linked_exec(bContext *C, wmOperator *op)
                        BM_elem_flag_set(v, BM_ELEM_TAG, BM_elem_flag_test(v, BM_ELEM_SELECT));
                }
 
-               BMW_init(&walker, em->bm, delimit ? BMW_LOOP_SHELL : BMW_VERT_SHELL,
+               BMW_init(&walker, em->bm, delimit ? BMW_LOOP_SHELL_WIRE : BMW_VERT_SHELL,
                         BMW_MASK_NOP, delimit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP,
                         BMW_FLAG_TEST_HIDDEN,
                         BMW_NIL_LAY);
@@ -2459,10 +2459,20 @@ static int edbm_select_linked_exec(bContext *C, wmOperator *op)
                if (delimit) {
                        BM_ITER_MESH (v, &iter, em->bm, BM_VERTS_OF_MESH) {
                                if (BM_elem_flag_test(v, BM_ELEM_TAG)) {
-                                       BMLoop *l_walk;
-                                       BMW_ITER (l_walk, &walker, v) {
-                                               BM_vert_select_set(em->bm, l_walk->v, true);
-                                               BM_elem_flag_disable(l_walk->v, BM_ELEM_TAG);
+                                       BMElem *ele_walk;
+                                       BMW_ITER (ele_walk, &walker, v) {
+                                               if (ele_walk->head.htype == BM_LOOP) {
+                                                       BMVert *v_step = ((BMLoop *)ele_walk)->v;
+                                                       BM_vert_select_set(em->bm, v_step, true);
+                                                       BM_elem_flag_disable(v_step, BM_ELEM_TAG);
+                                               }
+                                               else {
+                                                       BMEdge *e_step = (BMEdge *)ele_walk;
+                                                       BLI_assert(ele_walk->head.htype == BM_EDGE);
+                                                       BM_edge_select_set(em->bm, e_step, true);
+                                                       BM_elem_flag_disable(e_step->v1, BM_ELEM_TAG);
+                                                       BM_elem_flag_disable(e_step->v2, BM_ELEM_TAG);
+                                               }
                                        }
                                }
                        }
@@ -2490,7 +2500,7 @@ static int edbm_select_linked_exec(bContext *C, wmOperator *op)
                        BM_elem_flag_set(e, BM_ELEM_TAG, BM_elem_flag_test(e, BM_ELEM_SELECT));
                }
 
-               BMW_init(&walker, em->bm, delimit ? BMW_LOOP_SHELL : BMW_VERT_SHELL,
+               BMW_init(&walker, em->bm, delimit ? BMW_LOOP_SHELL_WIRE : BMW_VERT_SHELL,
                         BMW_MASK_NOP, delimit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP,
                         BMW_FLAG_TEST_HIDDEN,
                         BMW_NIL_LAY);
@@ -2498,11 +2508,20 @@ static int edbm_select_linked_exec(bContext *C, wmOperator *op)
                if (delimit) {
                        BM_ITER_MESH (e, &iter, em->bm, BM_EDGES_OF_MESH) {
                                if (BM_elem_flag_test(e, BM_ELEM_TAG)) {
-                                       BMLoop *l_walk;
-                                       BMW_ITER (l_walk, &walker, e) {
-                                               BM_edge_select_set(em->bm, l_walk->e, true);
-                                               BM_edge_select_set(em->bm, l_walk->prev->e, true);
-                                               BM_elem_flag_disable(l_walk->e, BM_ELEM_TAG);
+                                       BMElem *ele_walk;
+                                       BMW_ITER (ele_walk, &walker, e) {
+                                               if (ele_walk->head.htype == BM_LOOP) {
+                                                       BMLoop *l_step = (BMLoop *)ele_walk;
+                                                       BM_edge_select_set(em->bm, l_step->e, true);
+                                                       BM_edge_select_set(em->bm, l_step->prev->e, true);
+                                                       BM_elem_flag_disable(l_step->e, BM_ELEM_TAG);
+                                               }
+                                               else {
+                                                       BMEdge *e_step = (BMEdge *)ele_walk;
+                                                       BLI_assert(ele_walk->head.htype == BM_EDGE);
+                                                       BM_edge_select_set(em->bm, e_step, true);
+                                                       BM_elem_flag_disable(e_step, BM_ELEM_TAG);
+                                               }
                                        }
                                }
                        }
@@ -2593,15 +2612,23 @@ static void edbm_select_linked_pick_ex(
 
        if ((em->selectmode & SCE_SELECT_VERTEX) && eve) {
 
-               BMW_init(&walker, bm, delimit ? BMW_LOOP_SHELL : BMW_VERT_SHELL,
+               BMW_init(&walker, bm, delimit ? BMW_LOOP_SHELL_WIRE : BMW_VERT_SHELL,
                         BMW_MASK_NOP, delimit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP,
                         BMW_FLAG_TEST_HIDDEN,
                         BMW_NIL_LAY);
 
                if (delimit) {
-                       BMLoop *l_walk;
-                       BMW_ITER (l_walk, &walker, eve) {
-                               BM_vert_select_set(bm, l_walk->v, sel);
+                       BMElem *ele_walk;
+                       BMW_ITER (ele_walk, &walker, eve) {
+                               if (ele_walk->head.htype == BM_LOOP) {
+                                       BMVert *v_step = ((BMLoop *)ele_walk)->v;
+                                       BM_vert_select_set(bm, v_step, sel);
+                               }
+                               else {
+                                       BMEdge *e_step = (BMEdge *)ele_walk;
+                                       BLI_assert(ele_walk->head.htype == BM_EDGE);
+                                       BM_edge_select_set(bm, e_step, sel);
+                               }
                        }
                }
                else {
@@ -2617,16 +2644,23 @@ static void edbm_select_linked_pick_ex(
        }
        else if ((em->selectmode & SCE_SELECT_EDGE) && eed) {
 
-               BMW_init(&walker, bm, delimit ? BMW_LOOP_SHELL : BMW_VERT_SHELL,
+               BMW_init(&walker, bm, delimit ? BMW_LOOP_SHELL_WIRE : BMW_VERT_SHELL,
                         BMW_MASK_NOP, delimit ? BMO_ELE_TAG : BMW_MASK_NOP, BMW_MASK_NOP,
                         BMW_FLAG_TEST_HIDDEN,
                         BMW_NIL_LAY);
 
                if (delimit) {
-                       BMLoop *l_walk;
-                       BMW_ITER (l_walk, &walker, eed) {
-                               BM_edge_select_set(bm, l_walk->e, sel);
-                               BM_edge_select_set(bm, l_walk->prev->e, sel);
+                       BMElem *ele_walk;
+                       BMW_ITER (ele_walk, &walker, eed) {
+                               if (ele_walk->head.htype == BM_LOOP) {
+                                       BMEdge *e_step = ((BMLoop *)ele_walk)->e;
+                                       BM_edge_select_set(bm, e_step, sel);
+                               }
+                               else {
+                                       BMEdge *e_step = (BMEdge *)ele_walk;
+                                       BLI_assert(ele_walk->head.htype == BM_EDGE);
+                                       BM_edge_select_set(bm, e_step, sel);
+                               }
                        }
                }
                else {