Commit: 58ae64b2bb58fe0042b73d0f3518bb9a5b957866
Author: Campbell Barton
Date:   Fri Jul 11 10:29:53 2014 +1000
https://developer.blender.org/rB58ae64b2bb58fe0042b73d0f3518bb9a5b957866

BMesh: new face splitting function BM_face_split_edgenet

This takes a face and an edge-net, splitting the face into regions
defined by the edge-net.

===================================================================

M       source/blender/bmesh/intern/bmesh_mods.c
M       source/blender/bmesh/intern/bmesh_mods.h
M       source/blender/bmesh/intern/bmesh_private.h

===================================================================

diff --git a/source/blender/bmesh/intern/bmesh_mods.c 
b/source/blender/bmesh/intern/bmesh_mods.c
index 2d7a2cf..4d3fac4 100644
--- a/source/blender/bmesh/intern/bmesh_mods.c
+++ b/source/blender/bmesh/intern/bmesh_mods.c
@@ -32,12 +32,19 @@
 
 #include "BLI_math.h"
 #include "BLI_array.h"
+#include "BLI_alloca.h"
+#include "BLI_stackdefines.h"
+#include "BLI_linklist_stack.h"
+#include "BLI_sort_utils.h"
 
 #include "BKE_customdata.h"
 
 #include "bmesh.h"
 #include "intern/bmesh_private.h"
 
+// #define DEBUG_PRINT
+
+
 /**
  * \brief Dissolve Vert
  *
@@ -416,6 +423,536 @@ BMFace *BM_face_split_n(BMesh *bm, BMFace *f,
        return f_new;
 }
 
+
+/* -------------------------------------------------------------------- */
+/* Face Split Edge-Net */
+
+/** \name BM_face_split_edgenet and helper functions.
+ *
+ * \note Don't use #BM_edge_is_wire or #BM_edge_is_boundary
+ * since we need to take flagged faces into account.
+ * Also take care accessing e->l directly.
+ *
+ * \{ */
+
+/* Note: All these flags _must_ be cleared on exit */
+
+/* face is apart of the edge-net (including the original face we're splitting) 
*/
+#define FACE_NET  _FLAG_WALK
+/* edge is apart of the edge-net we're filling */
+#define EDGE_NET   _FLAG_WALK
+/* tag verts we've visit */
+#define VERT_VISIT _FLAG_WALK
+
+struct VertOrder {
+       float   angle;
+       BMVert *v;
+};
+
+static unsigned int bm_edge_flagged_radial_count(BMEdge *e)
+{
+       unsigned int count = 0;
+       BMLoop *l;
+
+       if ((l = e->l)) {
+               do {
+                       if (BM_ELEM_API_FLAG_TEST(l->f, FACE_NET)) {
+                               count++;
+                       }
+               } while ((l = l->radial_next) != e->l);
+       }
+       return count;
+}
+
+static BMLoop *bm_edge_flagged_radial_first(BMEdge *e)
+{
+       BMLoop *l;
+
+       if ((l = e->l)) {
+               do {
+                       if (BM_ELEM_API_FLAG_TEST(l->f, FACE_NET)) {
+                               return l;
+                       }
+               } while ((l = l->radial_next) != e->l);
+       }
+       return NULL;
+}
+
+static bool bm_face_split_edgenet_find_loop_pair(
+        BMVert *v_init, const float face_normal[3],
+        BMEdge *e_pair[2])
+{
+       /* Always find one boundary edge (to determine winding)
+        * and one wire (if available), otherwise another boundary.
+        */
+       BMIter iter;
+       BMEdge *e;
+
+       /* detect winding */
+       BMLoop *l_walk;
+       bool swap;
+
+       BLI_SMALLSTACK_DECLARE(edges_boundary, BMEdge *);
+       BLI_SMALLSTACK_DECLARE(edges_wire,     BMEdge *);
+       int edges_boundary_len = 0;
+       int edges_wire_len = 0;
+
+       BM_ITER_ELEM (e, &iter, v_init, BM_EDGES_OF_VERT) {
+               if (BM_ELEM_API_FLAG_TEST(e, EDGE_NET)) {
+                       const unsigned int count = 
bm_edge_flagged_radial_count(e);
+                       if (count == 1) {
+                               BLI_SMALLSTACK_PUSH(edges_boundary, e);
+                               edges_boundary_len++;
+                       }
+                       else if (count == 0) {
+                               BLI_SMALLSTACK_PUSH(edges_wire, e);
+                               edges_wire_len++;
+                       }
+               }
+       }
+
+       /* first edge should always be boundary */
+       if (edges_boundary_len == 0) {
+               return false;
+       }
+       e_pair[0] = BLI_SMALLSTACK_POP(edges_boundary);
+
+       /* attempt one boundary and one wire, or 2 boundary */
+       if (edges_wire_len == 0) {
+               if (edges_boundary_len >= 2) {
+                       e_pair[1] = BLI_SMALLSTACK_POP(edges_boundary);
+               }
+               else {
+                       /* one boundary and no wire */
+                       return false;
+               }
+       }
+       else {
+               e_pair[1] = BLI_SMALLSTACK_POP(edges_wire);
+
+               if (edges_wire_len > 1) {
+                       BMVert *v_prev = BM_edge_other_vert(e_pair[0], v_init);
+                       BMVert *v_next;
+                       float angle_best;
+
+                       v_next = BM_edge_other_vert(e_pair[1], v_init);
+                       angle_best = angle_on_axis_v3v3v3_v3(v_prev->co, 
v_init->co, v_next->co, face_normal);
+
+                       while ((e = BLI_SMALLSTACK_POP(edges_wire))) {
+                               float angle_test;
+                               v_next = BM_edge_other_vert(e, v_init);
+                               angle_test = 
angle_on_axis_v3v3v3_v3(v_prev->co, v_init->co, v_next->co, face_normal);
+                               if (angle_test < angle_best) {
+                                       angle_best = angle_test;
+                                       e_pair[1] = e;
+                               }
+                       }
+               }
+       }
+
+
+       /* flip based on winding */
+       l_walk = bm_edge_flagged_radial_first(e_pair[0]);
+       swap = false;
+       if (face_normal == l_walk->f->no) {
+               swap = !swap;
+       }
+       if (l_walk->v != v_init) {
+               swap = !swap;
+       }
+       if (swap) {
+               SWAP(BMEdge *, e_pair[0], e_pair[1]);
+       }
+
+       return true;
+}
+
+static bool bm_face_split_edgenet_find_loop_walk(
+        BMVert *v_init, const float face_normal[3],
+        /* cache to avoid realloc every time */
+        struct VertOrder *edge_order, const unsigned int edge_order_len,
+        BMEdge *e_pair[2])
+{
+       /* fast-path for the common case (avoid push-pop).
+        * Also avoids tagging as visited since we know we
+        * can't reach these verts some other way */
+#define USE_FASTPATH_NOFORK
+
+       BMVert *v;
+       BMVert *v_dst;
+       bool found = false;
+
+       struct VertOrder *eo;
+       STACK_DECLARE(edge_order);
+
+       /* store visited verts so we can clear the visit flag after execution */
+       BLI_SMALLSTACK_DECLARE(vert_visit, BMVert *);
+
+       /* likely this will stay very small
+        * all verts pushed into this stack _must_ have their previous edges 
set! */
+       BLI_SMALLSTACK_DECLARE(vert_stack, BMVert *);
+       BLI_SMALLSTACK_DECLARE(vert_stack_next, BMVert *);
+
+       STACK_INIT(edge_order, edge_order_len);
+
+       /* start stepping */
+       v = BM_edge_other_vert(e_pair[0], v_init);
+       v->e = e_pair[0];
+       BLI_SMALLSTACK_PUSH(vert_stack, v);
+
+       v_dst = BM_edge_other_vert(e_pair[1], v_init);
+
+#ifdef DEBUG_PRINT
+       printf("%s: vert (search) %d\n", __func__, BM_elem_index_get(v_init));
+#endif
+
+       /* This loop will keep stepping over the best possible edge,
+        * in most cases it finds the direct route to close the face.
+        *
+        * In cases where paths can't be closed,
+        * alternatives are stored in the 'vert_stack'.
+        */
+       while ((v = BLI_SMALLSTACK_POP_EX(vert_stack, vert_stack_next))) {
+               BMIter eiter;
+               BMEdge *e_next;
+
+               BLI_SMALLSTACK_PUSH(vert_visit, v);
+               BM_ELEM_API_FLAG_ENABLE(v, VERT_VISIT);
+
+
+#ifdef USE_FASTPATH_NOFORK
+walk_nofork:
+#endif
+
+               BLI_assert(STACK_SIZE(edge_order) == 0);
+
+               /* check if we're done! */
+               if (v == v_dst) {
+                       found = true;
+                       goto finally;
+               }
+
+               BM_ITER_ELEM (e_next, &eiter, v, BM_EDGES_OF_VERT) {
+                       if ((v->e != e_next) &&
+                           (BM_ELEM_API_FLAG_TEST(e_next, EDGE_NET)) &&
+                           (bm_edge_flagged_radial_count(e_next) < 2))
+                       {
+                               BMVert *v_next;
+
+                               v_next = BM_edge_other_vert(e_next, v);
+
+#ifdef DEBUG_PRINT
+                               /* indent and print */
+                               {
+                                       BMVert *_v = v;
+                                       do {
+                                               printf("  ");
+                                       } while ((_v = 
BM_edge_other_vert(_v->e, _v)) != v_init);
+                                       printf("vert %d -> %d (add=%d)\n",
+                                              BM_elem_index_get(v), 
BM_elem_index_get(v_next),
+                                              BM_ELEM_API_FLAG_TEST(v_next, 
VERT_VISIT) == 0);
+                               }
+#endif
+
+                               if (!BM_ELEM_API_FLAG_TEST(v_next, VERT_VISIT)) 
{
+                                       eo = STACK_PUSH_RET_PTR(edge_order);
+                                       eo->v = v_next;
+
+                                       v_next->e = e_next;
+                               }
+                       }
+               }
+
+#ifdef USE_FASTPATH_NOFORK
+               if (STACK_SIZE(edge_order) == 1) {
+                       eo = STACK_POP_PTR(edge_order);
+                       v = eo->v;
+
+                       goto walk_nofork;
+               }
+#endif
+
+               /* sort by angle if needed */
+               if (STACK_SIZE(edge_order) > 1) {
+                       unsigned int j;
+                       BMVert *v_prev = BM_edge_other_vert(v->e, v);
+
+                       for (j = 0; j < STACK_SIZE(edge_order); j++) {
+                               edge_order[j].angle = 
angle_signed_on_axis_v3v3v3_v3(v_prev->co, v->co, edge_order[j].v->co, 
face_normal);
+                       }
+                       qsort(edge_order, STACK_SIZE(edge_order), sizeof(struct 
VertOrder), BLI_sortutil_cmp_float_reverse);
+               }
+
+               while ((eo = STACK_POP_PTR(edge_order))) {
+                       BLI_SMALLSTACK_PUSH(vert_stack_next, eo->v);
+               }
+
+               if (!BLI_SMALLSTACK_IS_EMPTY(vert_stack_next)) {
+                       BLI_SMALLSTACK_SWAP(vert_stack, vert_stack_next);
+               }
+       }
+
+
+finally:
+       /* clear flag for next execution */
+       while ((v = BLI_SMALLSTACK_POP(vert_visit))) {
+               BM_ELEM_API_FLAG_DISABLE(v, VERT_VISIT);
+       }
+
+       return found;
+
+#undef USE_FASTPATH_NOFORK
+}
+
+static bool bm_face_split_edgenet_find_loop(
+        BMVert *v_init, const float face_normal[3],
+        /* cache to avoid realloc every time */
+        struct VertOrder *edge_order, const unsigned int edge_order_len,
+        BMVert **r_face_verts, int *r_face_verts_len)
+{
+       BMEdge *e_pair[2];
+       BMVert *v;
+
+       if (!bm_face_split_edgenet_find_loop_pair(v_init, face_normal, e_pair)) 
{
+               return false;
+       }
+
+       BLI_assert((bm_edge_flagged_radial_count(e_pair[0]) == 1) ||
+                  (bm_edge_flagged_radial_count(e_pair[1]) == 1));
+
+       if (bm_face_split_edgenet_find_loop_walk(v_init, face_normal, 
edge_order, edge_order_len, e_pair)) {
+               unsigned int i = 0;
+
+               r_face_verts[i++] = v_init;
+               v = BM_edge_other_vert(e_pair[1], v_init);
+               do {
+                       r_face_verts[i++] = v;
+               } while ((v = BM_edge_other_vert(v->e, v)) != v_init);
+               *r_face_verts_len = i;
+               return (i > 2) ? true : false;
+       }
+       else {
+               return false;
+       }
+}
+
+/**
+ * Splits a face into many smaller faces defined by an edge-net.
+ * handle customdata and degenerate cases.
+ *
+ * - isolated holes or unsupported face configurations, will be ignored.
+ * - customdata calculations aren't efficient
+ *   (need to calculate weights for each vert).
+ */
+bool BM_face_split_edgenet(
+        BMesh *bm,
+        BMFace *f, BMEdge **edge_net, const int edge_net_len,
+        BMFace ***r_face_arr, int *r_face_arr_len)
+{
+       /* re-use for new face verts */
+       BMVert **face_verts;
+       int      face_verts_len;
+
+       BMFace **face_arr = NULL;
+       BLI_array_declare(face_arr);
+
+       BMVert **vert_queue;
+       STACK_DECLARE(vert_queue);
+       int i;
+
+       struct VertOrder *edge_order;
+       const unsigned int edge_order_len = edge_net_len + 2;
+
+       BMVert *v;
+
+       BMLoop *l_iter, *l_first;
+
+
+       if (!edge_net_len) {
+               if (r_face_arr) {
+                       *r_face_arr = NULL;
+                       *r_face_arr_len = 0;
+               }
+               return false;
+       }
+
+       /* over-alloc (probably 2-4 is only used in most cases), for the 
biggest-fan */
+       edge_order = BLI_array_alloca(edge_order, edge_order_len);
+
+       /* use later */
+       face_verts = BLI_array_alloca(face_verts, edge_net_len + f->len);
+
+       vert_queue = BLI_array_alloca(vert_queue, edge_net_len + f->len);
+       STACK_INIT(vert_queue, f->len + edge_net_len);
+
+       BLI_assert(BM_ELEM_API_FLAG_TEST(f, FACE_NET) == 0);
+       BM_ELEM_API_FLAG_ENABLE(f, FACE_NET);
+
+#ifdef DEBUG
+       for (i = 0; i < edge_net_len; i++) {
+               BLI_assert(BM_ELEM_API_FLAG_TEST(edge_net[i], EDGE_NET) == 0);
+       }
+       l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+       do {
+               BLI_assert(BM_ELEM_API_F

@@ Diff output truncated at 10240 characters. @@

_______________________________________________
Bf-blender-cvs mailing list
[email protected]
http://lists.blender.org/mailman/listinfo/bf-blender-cvs

Reply via email to