Cleanup: remove redundant doxygen \file argument
[blender.git] / source / blender / bmesh / operators / bmo_offset_edgeloops.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16
17 /** \file \ingroup bmesh
18  *
19  * Simple edge offset functionality.
20  *
21  * \note Actual offset is done by edge-slide.
22  * (this only changes topology)
23  */
24
25 #include "MEM_guardedalloc.h"
26
27 #include "BLI_math.h"
28 #include "BLI_alloca.h"
29 #include "BLI_utildefines_stack.h"
30
31 #include "BKE_customdata.h"
32
33 #include "bmesh.h"
34
35 #include "intern/bmesh_operators_private.h" /* own include */
36
37 #define USE_CAP_OPTION
38
39 #define ELE_NEW                 (1 << 0)
40
41 #ifdef USE_CAP_OPTION
42 #define ELE_VERT_ENDPOINT       (1 << 1)
43 #endif
44
45 /* set for debugging */
46 #define OFFSET 0.0f
47
48 static BMFace *bm_face_split_walk_back(
49         BMesh *bm, BMLoop *l_src,
50         BMLoop **r_l)
51 {
52         float (*cos)[3];
53         BMLoop *l_dst;
54         BMFace *f;
55         int num, i;
56
57         for (l_dst = l_src->prev, num = 0; BM_elem_index_get(l_dst->prev->v) != -1; l_dst = l_dst->prev, num++) {
58                 /* pass */
59         }
60
61         BLI_assert(num != 0);
62
63         cos = BLI_array_alloca(
64                   cos, num);
65
66         for (l_dst = l_src->prev, i = 0; BM_elem_index_get(l_dst->prev->v) != -1; l_dst = l_dst->prev, i++) {
67                 copy_v3_v3(cos[num - (i + 1)], l_dst->v->co);
68         }
69
70         f = BM_face_split_n( bm, l_src->f, l_dst->prev, l_src->next, cos, num, r_l, NULL);
71
72         return f;
73 }
74
75 void bmo_offset_edgeloops_exec(BMesh *bm, BMOperator *op)
76 {
77         const int edges_num = BMO_slot_buffer_count(op->slots_in, "edges");
78         BMVert **verts;
79         STACK_DECLARE(verts);
80         int i;
81
82 #ifdef USE_CAP_OPTION
83         bool use_cap_endpoint = BMO_slot_bool_get(op->slots_in,  "use_cap_endpoint");
84         int v_edges_max = 0;
85 #endif
86
87         BMOIter oiter;
88
89         /* only so we can detect new verts (index == -1) */
90         BM_mesh_elem_index_ensure(bm, BM_VERT);
91
92         BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false);
93
94         /* over alloc */
95         verts = MEM_mallocN(sizeof(*verts) * (edges_num * 2), __func__);
96
97         STACK_INIT(verts, (edges_num * 2));
98
99         {
100                 BMEdge *e;
101                 BMO_ITER (e, &oiter, op->slots_in, "edges", BM_EDGE) {
102                         int j;
103
104                         BM_elem_flag_enable(e, BM_ELEM_TAG);
105
106                         for (j = 0; j < 2; j++) {
107                                 BMVert *v_edge = *(&(e->v1) + j);
108                                 if (!BM_elem_flag_test(v_edge, BM_ELEM_TAG)) {
109                                         BM_elem_flag_enable(v_edge, BM_ELEM_TAG);
110                                         STACK_PUSH(verts, v_edge);
111                                 }
112                         }
113                 }
114         }
115
116
117         /* -------------------------------------------------------------------- */
118         /* Remove verts only used by tagged edges */
119
120         for (i = 0; i < STACK_SIZE(verts); i++) {
121                 BMIter iter;
122                 int flag = 0;
123                 BMEdge *e;
124
125                 BM_ITER_ELEM (e, &iter, verts[i], BM_EDGES_OF_VERT) {
126                         flag |= BM_elem_flag_test(e, BM_ELEM_TAG) ? 1 : 2;
127                         if (flag == (1 | 2)) {
128                                 break;
129                         }
130                 }
131
132                 /* only boundary verts are interesting */
133                 if (flag != (1 | 2)) {
134                         STACK_REMOVE(verts, i);
135                 }
136         }
137
138         /* possible but unlikely we have no mixed vertices */
139         if (UNLIKELY(STACK_SIZE(verts) == 0)) {
140                 MEM_freeN(verts);
141                 return;
142         }
143
144         /* main loop */
145         for (i = 0; i < STACK_SIZE(verts); i++) {
146                 int v_edges_num = 0;
147                 int v_edges_num_untag = 0;
148                 BMVert *v = verts[i];
149                 BMIter iter;
150                 BMEdge *e;
151
152                 BM_ITER_ELEM (e, &iter, verts[i], BM_EDGES_OF_VERT) {
153                         if (!BM_elem_flag_test(e, BM_ELEM_TAG)) {
154                                 BMVert *v_other;
155                                 BMIter liter;
156                                 BMLoop *l;
157
158                                 BM_ITER_ELEM (l, &liter, e, BM_LOOPS_OF_EDGE) {
159                                         BM_elem_flag_enable(l->f, BM_ELEM_TAG);
160                                 }
161
162                                 v_other = BM_edge_other_vert(e, v);
163                                 BM_edge_split(bm, e, v_other, NULL, 1.0f - OFFSET);
164                         }
165                         else {
166                                 v_edges_num_untag += 1;
167                         }
168
169                         v_edges_num += 1;
170                 }
171
172 #ifdef USE_CAP_OPTION
173                 if (v_edges_num_untag == 1) {
174                         BMO_vert_flag_enable(bm, v, ELE_VERT_ENDPOINT);
175                 }
176
177                 CLAMP_MIN(v_edges_max, v_edges_num);
178 #endif
179
180         }
181
182
183         for (i = 0; i < STACK_SIZE(verts); i++) {
184                 BMVert *v = verts[i];
185                 BMIter liter;
186                 BMLoop *l;
187
188                 BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
189                         if (BM_elem_flag_test(l->f, BM_ELEM_TAG) &&
190                             (l->f->len != 3))
191                         {
192                                 BMFace *f_cmp = l->f;
193                                 if ((BM_elem_index_get(l->next->v) == -1) &&
194                                     (BM_elem_index_get(l->prev->v) == -1))
195                                 {
196 #ifdef USE_CAP_OPTION
197                                         if (use_cap_endpoint || (BMO_vert_flag_test(bm, v, ELE_VERT_ENDPOINT) == 0))
198 #endif
199                                         {
200                                                 BMLoop *l_new;
201                                                 BM_face_split(bm, l->f, l->prev, l->next, &l_new, NULL, true);
202                                                 BLI_assert(f_cmp == l->f);
203                                                 BLI_assert(f_cmp != l_new->f);
204                                                 UNUSED_VARS_NDEBUG(f_cmp);
205                                                 BMO_edge_flag_enable(bm, l_new->e, ELE_NEW);
206                                         }
207                                 }
208                                 else if (l->f->len > 4) {
209                                         if (BM_elem_flag_test(l->e,       BM_ELEM_TAG) !=
210                                             BM_elem_flag_test(l->prev->e, BM_ELEM_TAG))
211                                         {
212                                                 if (BM_elem_index_get(l->next->v) == -1) {
213                                                         if (BM_elem_index_get(l->prev->prev->v) == -1) {
214                                                                 BMLoop *l_new;
215                                                                 BM_face_split(bm, l->f, l->prev->prev, l->next, &l_new, NULL, true);
216                                                                 BLI_assert(f_cmp == l->f);
217                                                                 BLI_assert(f_cmp != l_new->f);
218                                                                 BMO_edge_flag_enable(bm, l_new->e, ELE_NEW);
219                                                                 BM_elem_flag_disable(l->f, BM_ELEM_TAG);
220                                                         }
221                                                         else {
222                                                                 /* walk backwards */
223                                                                 BMLoop *l_new;
224                                                                 bm_face_split_walk_back(bm, l, &l_new);
225                                                                 do {
226                                                                         BMO_edge_flag_enable(bm, l_new->e, ELE_NEW);
227                                                                         l_new = l_new->next;
228                                                                 } while (BM_vert_is_edge_pair(l_new->v));
229                                                                 BM_elem_flag_disable(l->f, BM_ELEM_TAG);
230                                                         }
231                                                 }
232
233                                                 /* Note: instead of duplicate code in alternate direction,
234                                                  * we can be sure to hit the other vertex, so the code above runs. */
235 #if 0
236                                                 else if (BM_elem_index_get(l->prev->v) == -1) {
237                                                         if (BM_elem_index_get(l->next->next->v) == -1) {
238                                                                 /* pass */
239                                                         }
240                                                 }
241 #endif
242                                         }
243                                 }
244                         }
245                 }
246         }
247
248 #ifdef USE_CAP_OPTION
249         if (use_cap_endpoint == false) {
250                 BMVert **varr = BLI_array_alloca(varr, v_edges_max);
251                 STACK_DECLARE(varr);
252                 BMVert *v;
253
254                 for (i = 0; i < STACK_SIZE(verts); i++) {
255                         BMIter iter;
256                         BMEdge *e;
257
258                         v = verts[i];
259
260                         STACK_INIT(varr, v_edges_max);
261
262                         BM_ITER_ELEM (e, &iter, v, BM_EDGES_OF_VERT) {
263                                 BMVert *v_other;
264                                 v_other = BM_edge_other_vert(e, v);
265                                 if (BM_elem_index_get(v_other) == -1) {
266                                         if (BM_vert_is_edge_pair(v_other)) {
267                                                 /* defer bmesh_kernel_join_edge_kill_vert to avoid looping over data we're removing */
268                                                 v_other->e = e;
269                                                 STACK_PUSH(varr, v_other);
270                                         }
271                                 }
272                         }
273
274                         while ((v = STACK_POP(varr))) {
275                                 bmesh_kernel_join_edge_kill_vert(bm, v->e, v, true, false, false);
276                         }
277                 }
278         }
279 #endif
280
281         MEM_freeN(verts);
282
283         BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "edges.out", BM_EDGE, ELE_NEW);
284 }