bmesh edge rotate
authorCampbell Barton <ideasman42@gmail.com>
Sat, 3 Mar 2012 12:35:37 +0000 (12:35 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Sat, 3 Mar 2012 12:35:37 +0000 (12:35 +0000)
* improve check to see if edge rotate can be done,
  was checking if both edges verts have an edge count of 2, which is really a meaningless test since the verts can have stray edges connected and the result wont work right.
  instead check if the next verts in both faces share a vertex.

* add utility function BM_face_other_vert_loop() which gets the next loop in a face.
* add convenience function BM_edge_face_pair() which returns 2 faces for edges that have exactly 2 face users. (saves ugly e->l->radial_next ... in code) and is more readable.

source/blender/bmesh/intern/bmesh_mods.c
source/blender/bmesh/intern/bmesh_mods.h
source/blender/bmesh/intern/bmesh_queries.c
source/blender/bmesh/intern/bmesh_queries.h
source/blender/bmesh/operators/bmo_utils.c

index ae53d5f83e42354abfac84bdc0227152fe9f6409..29e6de5db0ef5127d889bd66200504373d28ac41 100644 (file)
@@ -708,6 +708,43 @@ int BM_face_validate(BMesh *bm, BMFace *face, FILE *err)
        return ret;
 }
 
+/**
+ * \brief Check if Rotate Edge is OK
+ *
+ * Quick check to see if we could rotate the edge,
+ * use this to avoid calling exceptions on common cases.
+ */
+int BM_edge_rotate_check(BMesh *UNUSED(bm), BMEdge *e)
+{
+       BMFace *fa, *fb;
+       if (BM_edge_face_pair(e, &fa, &fb)) {
+               BMLoop *la, *lb;
+
+               la = BM_face_other_vert_loop(e->v2, fa, e->v1);
+               lb = BM_face_other_vert_loop(e->v2, fb, e->v1);
+
+               /* check that the next vert in both faces isnt the same
+                * (ie - the next edge doesnt sharwe the same faces).
+                * since we can't rotate usefully in this case. */
+               if (la->v == lb->v) {
+                       return FALSE;
+               }
+
+               /* mirror of the check above but in the opposite direction */
+               la = BM_face_other_vert_loop(e->v1, fa, e->v2);
+               lb = BM_face_other_vert_loop(e->v1, fb, e->v2);
+
+               if (la->v == lb->v) {
+                       return FALSE;
+               }
+
+               return TRUE;
+       }
+       else {
+               return FALSE;
+       }
+}
+
 /**
  * \brief Rotate Edge
  *
@@ -727,16 +764,12 @@ BMEdge *BM_edge_rotate(BMesh *bm, BMEdge *e, int ccw)
        BMFace *f;
        BMIter liter;
 
-       v1 = e->v1;
-       v2 = e->v2;
-
-       if (BM_edge_face_count(e) != 2)
+       if (!BM_edge_rotate_check(bm, e)) {
                return NULL;
+       }
 
-       /* If either of e's vertices has valence 2, then
-        * dissolving the edge would leave a spur, so not allowed */
-       if (BM_vert_edge_count(e->v1) == 2 || BM_vert_edge_count(e->v2) == 2)
-               return NULL;
+       v1 = e->v1;
+       v2 = e->v2;
 
        f = BM_faces_join_pair(bm, e->l->f, e->l->radial_next->f, e);
 
index 6d4b1333549f9652a02d15bac14be60a1cc5087a..d530e9d8666010f5c812b536a08059488fd642f9 100644 (file)
@@ -53,6 +53,7 @@ BMVert *BM_edge_split_n(BMesh *bm, BMEdge *e, int numcuts);
 
 int     BM_face_validate(BMesh *bm, BMFace *face, FILE *err);
 
+int     BM_edge_rotate_check(BMesh *UNUSED(bm), BMEdge *e);
 BMEdge *BM_edge_rotate(BMesh *bm, BMEdge *e, int ccw);
 
 BMVert *BM_vert_rip(BMesh *bm, BMFace *sf, BMVert *sv);
index e597145a54c330fd69bcd91c585fe3efccf14fd3..ad7300e1c21e9e0b1fe4aa69a39d6dc87715b620 100644 (file)
@@ -65,7 +65,7 @@ int BM_vert_in_edge(BMEdge *e, BMVert *v)
 /**
  * \brief BMESH OTHER EDGE IN FACE SHARING A VERTEX
  *
- * Finds the other loop that shares 'v' with 'e's loop in 'f'.
+ * Finds the other loop that shares \a v with \a e loop in \a f.
  */
 BMLoop *BM_face_other_loop(BMEdge *e, BMFace *f, BMVert *v)
 {
@@ -83,6 +83,50 @@ BMLoop *BM_face_other_loop(BMEdge *e, BMFace *f, BMVert *v)
        return l_iter->v == v ? l_iter->prev : l_iter->next;
 }
 
+/**
+ * \brief BMESH NEXT LOOP IN FACE SHARING A VERTEX
+ *
+ * Finds the other loop in a face.
+ *
+ * This function returns a loop in \a f that shares an edge with \v
+ * The direction is defined by \a v_prev, where the return value is
+ * the loop of what would be 'v_next'
+ *
+ * \note \a v_prev and \a v _implicitly_ define an edge.
+ */
+BMLoop *BM_face_other_vert_loop(BMVert *v_prev, BMFace *f, BMVert *v)
+{
+       BMIter liter;
+       BMLoop *l_iter;
+
+       BLI_assert(BM_edge_exists(v_prev, v) != NULL);
+
+       BM_ITER(l_iter, &liter, NULL, BM_LOOPS_OF_VERT, v) {
+               if (l_iter->f == f) {
+                       break;
+               }
+       }
+
+       if (l_iter) {
+               if (l_iter->prev->v == v_prev) {
+                       return l_iter->next;
+               }
+               else if (l_iter->next->v == v_prev) {
+                       return l_iter->prev;
+               }
+               else {
+                       /* invalid args */
+                       BLI_assert(0);
+                       return NULL;
+               }
+       }
+       else {
+               /* invalid args */
+               BLI_assert(0);
+               return NULL;
+       }
+}
+
 /**
  * Returns TRUE if the vertex is used in a given face.
  */
@@ -190,6 +234,31 @@ BMVert *BM_edge_other_vert(BMEdge *e, BMVert *v)
        return bmesh_edge_other_vert_get(e, v);
 }
 
+/**
+ * Utility function, since enough times we have an edge
+ * and want to access 2 connected faces.
+ *
+ * \return TRUE when only 2 faces are found.
+ */
+int BM_edge_face_pair(BMEdge *e, BMFace **r_fa, BMFace **r_fb)
+{
+       BMLoop *la, *lb;
+
+       if ((la = e->l) &&
+           (lb = la->radial_next) &&
+           (lb->radial_next == la))
+       {
+               *r_fa = la->f;
+               *r_fb = lb->f;
+               return TRUE;
+       }
+       else {
+               *r_fa = NULL;
+               *r_fb = NULL;
+               return FALSE;
+       }
+}
+
 /**
  *     Returns the number of edges around this vertex.
  */
index 1fb0df7e2af9bd71b6736faab307c96aa5eb14de..8c366d213d7dbf684ec08571ab9a40e17c125901 100644 (file)
@@ -37,8 +37,10 @@ int     BM_edge_in_face(BMFace *f, BMEdge *e);
 int     BM_vert_in_edge(BMEdge *e, BMVert *v);
 int     BM_verts_in_edge(BMVert *v1, BMVert *v2, BMEdge *e);
 
+int     BM_edge_face_pair(BMEdge *e, BMFace **r_fa, BMFace **r_fb);
 BMVert *BM_edge_other_vert(BMEdge *e, BMVert *v);
 BMLoop *BM_face_other_loop(BMEdge *e, BMFace *f, BMVert *v);
+BMLoop *BM_face_other_vert_loop(BMVert *v_prev, BMFace *f, BMVert *v);
 
 int     BM_vert_edge_count(BMVert *v);
 int     BM_edge_face_count(BMEdge *e);
index b7418e428ed5248c190fb4c4ec60b83cfd418ac1..64e530ca52a0bf87c61e918c1f213219b45d3049 100644 (file)
@@ -125,16 +125,39 @@ void bmo_edgerotate_exec(BMesh *bm, BMOperator *op)
        BMEdge *e, *e2;
        int ccw = BMO_slot_bool_get(op, "ccw");
 
+#define EDGE_OUT   1
+#define FACE_TAINT 1
+
        BMO_ITER(e, &siter, bm, op, "edges", BM_EDGE) {
-               if (!(e2 = BM_edge_rotate(bm, e, ccw))) {
-                       BMO_error_raise(bm, op, BMERR_INVALID_SELECTION, "Could not rotate edge");
-                       return;
-               }
+               if (BM_edge_rotate_check(bm, e)) {
+                       BMFace *fa, *fb;
+                       if (BM_edge_face_pair(e, &fa, &fb)) {
+
+                               /* check we're untouched */
+                               if (BMO_elem_flag_test(bm, fa, FACE_TAINT) == FALSE &&
+                                   BMO_elem_flag_test(bm, fb, FACE_TAINT) == FALSE)
+                               {
+
+                                       if (!(e2 = BM_edge_rotate(bm, e, ccw))) {
+                                               BMO_error_raise(bm, op, BMERR_INVALID_SELECTION, "Could not rotate edge");
+                                               return;
+                                       }
 
-               BMO_elem_flag_enable(bm, e2, 1);
+                                       BMO_elem_flag_enable(bm, e2, EDGE_OUT);
+
+                                       /* dont touch again */
+                                       BMO_elem_flag_enable(bm, fa, FACE_TAINT);
+                                       BMO_elem_flag_enable(bm, fb, FACE_TAINT);
+                               }
+                       }
+               }
        }
 
-       BMO_slot_buffer_from_flag(bm, op, "edgeout", 1, BM_EDGE);
+       BMO_slot_buffer_from_flag(bm, op, "edgeout", EDGE_OUT, BM_EDGE);
+
+#undef EDGE_OUT
+#undef FACE_TAINT
+
 }
 
 #define SEL_FLAG       1