remove BM_ITER, BM_ITER_INDEX macros, use ELEM or MESH variants only (the maceros...
[blender-staging.git] / source / blender / bmesh / operators / bmo_edgesplit.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * Contributor(s): Campbell Barton
19  *
20  * ***** END GPL LICENSE BLOCK *****
21  */
22
23 /** \file blender/bmesh/operators/bmo_edgesplit.c
24  *  \ingroup bmesh
25  */
26
27 #include "MEM_guardedalloc.h"
28
29 #include "BLI_utildefines.h"
30
31 #include "bmesh.h"
32
33 #include "intern/bmesh_operators_private.h" /* own include */
34
35 enum {
36         EDGE_SEAM  = 1
37 };
38
39 enum {
40         VERT_SEAM  = 2
41 };
42
43 /**
44  * Remove the EDGE_SEAM flag for edges we cant split
45  *
46  * un-tag edges not connected to other tagged edges,
47  * unless they are on a boundary
48  */
49 static void bm_edgesplit_validate_seams(BMesh *bm, BMOperator *op)
50 {
51         BMOIter siter;
52         BMIter iter;
53         BMEdge *e;
54
55         unsigned char *vtouch;
56         unsigned char *vt;
57
58         BM_mesh_elem_index_ensure(bm, BM_VERT);
59
60         vtouch = MEM_callocN(sizeof(char) * bm->totvert, __func__);
61
62         /* tag all boundary verts so as not to untag an edge which is inbetween only 2 faces [] */
63         BM_ITER_MESH (e, &iter, bm, BM_EDGES_OF_MESH) {
64
65                 /* unrelated to flag assignment in this function - since this is the
66                  * only place we loop over all edges, disable tag */
67                 BM_elem_flag_disable(e, BM_ELEM_INTERNAL_TAG);
68
69                 if (e->l == NULL) {
70                         BMO_elem_flag_disable(bm, e, EDGE_SEAM);
71                 }
72                 else if (BM_edge_is_boundary(e)) {
73                         vt = &vtouch[BM_elem_index_get(e->v1)]; if (*vt < 2) (*vt)++;
74                         vt = &vtouch[BM_elem_index_get(e->v2)]; if (*vt < 2) (*vt)++;
75
76                         /* while the boundary verts need to be tagged,
77                          * the edge its self can't be split */
78                         BMO_elem_flag_disable(bm, e, EDGE_SEAM);
79                 }
80         }
81
82         /* single marked edges unconnected to any other marked edges
83          * are illegal, go through and unmark them */
84         BMO_ITER (e, &siter, bm, op, "edges", BM_EDGE) {
85                 /* lame, but we don't want the count to exceed 255,
86                  * so just count to 2, its all we need */
87                 unsigned char *vt;
88                 vt = &vtouch[BM_elem_index_get(e->v1)]; if (*vt < 2) (*vt)++;
89                 vt = &vtouch[BM_elem_index_get(e->v2)]; if (*vt < 2) (*vt)++;
90         }
91         BMO_ITER (e, &siter, bm, op, "edges", BM_EDGE) {
92                 if (vtouch[BM_elem_index_get(e->v1)] == 1 &&
93                     vtouch[BM_elem_index_get(e->v2)] == 1)
94                 {
95                         BMO_elem_flag_disable(bm, e, EDGE_SEAM);
96                 }
97         }
98
99         MEM_freeN(vtouch);
100 }
101
102 /* keep this operator fast, its used in a modifier */
103 void bmo_edgesplit_exec(BMesh *bm, BMOperator *op)
104 {
105         BMOIter siter;
106         BMEdge *e;
107         const int use_verts = BMO_slot_bool_get(op, "use_verts");
108
109         BMO_slot_buffer_flag_enable(bm, op, "edges", BM_EDGE, EDGE_SEAM);
110
111         if (use_verts) {
112                 /* this slows down the operation but its ok because the modifier doesn't use */
113                 BMO_slot_buffer_flag_enable(bm, op, "verts", BM_VERT, VERT_SEAM);
114
115                 /* prevent one edge having both verts unflagged
116                  * we could alternately disable these edges, either way its a corner case.
117                  *
118                  * This is needed so we don't split off the edge but then none of its verts which
119                  * would leave a duplicate edge.
120                  */
121                 BMO_ITER (e, &siter, bm, op, "edges", BM_EDGE) {
122                         if (UNLIKELY((BMO_elem_flag_test(bm, e->v1, VERT_SEAM) == FALSE &&
123                                       (BMO_elem_flag_test(bm, e->v2, VERT_SEAM) == FALSE))))
124                         {
125                                 BMO_elem_flag_enable(bm, e->v1, VERT_SEAM);
126                                 BMO_elem_flag_enable(bm, e->v2, VERT_SEAM);
127                         }
128                 }
129         }
130
131         bm_edgesplit_validate_seams(bm, op);
132
133         BMO_ITER (e, &siter, bm, op, "edges", BM_EDGE) {
134                 if (BMO_elem_flag_test(bm, e, EDGE_SEAM)) {
135                         /* this flag gets copied so we can be sure duplicate edges get it too (important) */
136                         BM_elem_flag_enable(e, BM_ELEM_INTERNAL_TAG);
137
138                         /* keep splitting until each loop has its own edge */
139                         do {
140                                 bmesh_edge_separate(bm, e, e->l);
141                         } while (!BM_edge_is_boundary(e));
142
143                         BM_elem_flag_enable(e->v1, BM_ELEM_TAG);
144                         BM_elem_flag_enable(e->v2, BM_ELEM_TAG);
145                 }
146         }
147
148         if (use_verts) {
149                 BMO_ITER (e, &siter, bm, op, "edges", BM_EDGE) {
150                         if (BMO_elem_flag_test(bm, e->v1, VERT_SEAM) == FALSE) {
151                                 BM_elem_flag_disable(e->v1, BM_ELEM_TAG);
152                         }
153                         if (BMO_elem_flag_test(bm, e->v2, VERT_SEAM) == FALSE) {
154                                 BM_elem_flag_disable(e->v2, BM_ELEM_TAG);
155                         }
156                 }
157         }
158
159         BMO_ITER (e, &siter, bm, op, "edges", BM_EDGE) {
160                 if (BMO_elem_flag_test(bm, e, EDGE_SEAM)) {
161                         if (BM_elem_flag_test(e->v1, BM_ELEM_TAG)) {
162                                 BM_elem_flag_disable(e->v1, BM_ELEM_TAG);
163                                 bmesh_vert_separate(bm, e->v1, NULL, NULL);
164                         }
165                         if (BM_elem_flag_test(e->v2, BM_ELEM_TAG)) {
166                                 BM_elem_flag_disable(e->v2, BM_ELEM_TAG);
167                                 bmesh_vert_separate(bm, e->v2, NULL, NULL);
168                         }
169                 }
170         }
171
172         BMO_slot_buffer_from_enabled_hflag(bm, op, "edgeout", BM_EDGE, BM_ELEM_INTERNAL_TAG);
173 }