Fix T51900: Crash after pressing "F" multiple times.
[blender.git] / source / blender / bmesh / operators / bmo_create.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): Joseph Eagar, Campbell Barton.
19  *
20  * ***** END GPL LICENSE BLOCK *****
21  */
22
23 /** \file blender/bmesh/operators/bmo_create.c
24  *  \ingroup bmesh 
25  *
26  * Create faces or edges (Fkey by default).
27  */
28
29 #include "MEM_guardedalloc.h"
30
31 #include "BLI_listbase.h"
32
33 #include "bmesh.h"
34
35 #include "intern/bmesh_operators_private.h" /* own include */
36
37 #define ELE_NEW         1
38 #define ELE_OUT         2
39
40 /* This is what runs when pressing the F key
41  * doing the best thing here isn't always easy create vs dissolve, its nice to support
42  * but it _really_ gives issues we might have to not call dissolve. - campbell
43  */
44 void bmo_contextual_create_exec(BMesh *bm, BMOperator *op)
45 {
46         BMOIter oiter;
47         BMHeader *h;
48         int totv = 0, tote = 0, totf = 0;
49         const short mat_nr     = BMO_slot_int_get(op->slots_in,  "mat_nr");
50         const bool use_smooth  = BMO_slot_bool_get(op->slots_in, "use_smooth");
51
52         /* count number of each element type we were passe */
53         BMO_ITER (h, &oiter, op->slots_in, "geom", BM_VERT | BM_EDGE | BM_FACE) {
54                 switch (h->htype) {
55                         case BM_VERT:
56                                 BMO_vert_flag_enable(bm, (BMVert *)h, ELE_NEW);
57                                 totv++;
58                                 break;
59                         case BM_EDGE:
60                                 BMO_edge_flag_enable(bm, (BMEdge *)h, ELE_NEW);
61                                 tote++;
62                                 break;
63                         case BM_FACE:
64                                 BMO_face_flag_enable(bm, (BMFace *)h, ELE_NEW);
65                                 totf++;
66                                 break;
67                 }
68         }
69         
70         /* --- Support Edge Creation ---
71          * simple case when we only have 2 verts selected.
72          */
73         if (totv == 2 && tote == 0 && totf == 0) {
74                 BMVert *verts[2];
75                 BMEdge *e;
76
77                 if (BMO_iter_as_array(op->slots_in, "geom", BM_VERT, (void **)verts, 2) == 0) {
78                         /* create edge */
79                         e = BM_edge_create(bm, verts[0], verts[1], NULL, BM_CREATE_NO_DOUBLE);
80                         BMO_edge_flag_enable(bm, e, ELE_OUT);
81                         tote += 1;
82                         BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "edges.out", BM_EDGE, ELE_OUT);
83                 }
84                 return;
85         }
86
87
88         /* --- Support for Special Case ---
89          * where there is a contiguous edge ring with one isolated vertex.
90          *
91          * This example shows 2 edges created from 3 verts
92          * with 1 free standing vertex. Dotted lines denote the 2 edges that are created.
93          *
94          * note that this works for any sided shape.
95          *
96          * +--------+
97          * |        .
98          * |        .
99          * |        .
100          * |        .
101          * +........+ <-- starts out free standing.
102          *
103          */
104
105         /* Here we check for consistency and create 2 edges */
106         if (totf == 0 && totv >= 4 && totv == tote + 2) {
107                 /* find a free standing vertex and 2 endpoint verts */
108                 BMVert *v, *v_free = NULL, *v_a = NULL, *v_b = NULL;
109                 bool ok = true;
110
111
112                 BMO_ITER (v, &oiter, op->slots_in, "geom", BM_VERT) {
113                         /* count how many flagged edges this vertex uses */
114                         const int tot_edges = BMO_iter_elem_count_flag(bm, BM_EDGES_OF_VERT, v, ELE_NEW, true);
115                         if (tot_edges == 0) {
116                                 /* only accept 1 free vert */
117                                 if (v_free == NULL)  v_free = v;
118                                 else                 ok = false;  /* only ever want one of these */
119                         }
120                         else if (tot_edges == 1) {
121                                 if      (v_a == NULL)  v_a = v;
122                                 else if (v_b == NULL)  v_b = v;
123                                 else                   ok = false;  /* only ever want 2 of these */
124                         }
125                         else if (tot_edges == 2) {
126                                 /* do nothing, regular case */
127                         }
128                         else {
129                                 ok = false; /* if a vertex has 3+ edge users then cancel - this is only simple cases */
130                         }
131
132                         if (ok == false) {
133                                 break;
134                         }
135                 }
136
137                 if (ok == true && v_free && v_a && v_b) {
138                         BMEdge *e;
139
140                         e = BM_edge_create(bm, v_free, v_a, NULL, BM_CREATE_NO_DOUBLE);
141                         BMO_edge_flag_enable(bm, e, ELE_NEW);
142
143                         e = BM_edge_create(bm, v_free, v_b, NULL, BM_CREATE_NO_DOUBLE);
144                         BMO_edge_flag_enable(bm, e, ELE_NEW);
145                         tote += 2;
146                 }
147         }
148         /* --- end special case support, continue as normal --- */
149
150
151         /* -------------------------------------------------------------------- */
152         /* EdgeNet Create */
153         if (tote != 0) {
154                 /* call edgenet prepare op so additional face creation cases work */
155
156                 BMOperator op_sub;
157                 BMO_op_initf(bm, &op_sub, op->flag, "edgenet_prepare edges=%fe", ELE_NEW);
158                 BMO_op_exec(bm, &op_sub);
159                 BMO_slot_buffer_flag_enable(bm, op_sub.slots_out, "edges.out", BM_EDGE, ELE_NEW);
160                 BMO_op_finish(bm, &op_sub);
161
162                 BMO_op_initf(bm, &op_sub, op->flag,
163                              "edgenet_fill edges=%fe mat_nr=%i use_smooth=%b sides=%i",
164                              ELE_NEW, mat_nr, use_smooth, 10000);
165
166                 BMO_op_exec(bm, &op_sub);
167
168                 /* return if edge net create did something */
169                 if (BMO_slot_buffer_count(op_sub.slots_out, "faces.out")) {
170                         BMO_slot_copy(&op_sub, slots_out, "faces.out",
171                                       op,   slots_out, "faces.out");
172                         BMO_op_finish(bm, &op_sub);
173                         return;
174                 }
175
176                 BMO_op_finish(bm, &op_sub);
177         }
178
179
180         /* -------------------------------------------------------------------- */
181         /* Dissolve Face */
182         if (totf != 0) {  /* should be (totf > 1)... see below */
183                 /* note: allow this to run on single faces so running on a single face
184                  * won't go on to create a face, treating them as random */
185                 BMOperator op_sub;
186                 BMO_op_initf(bm, &op_sub, op->flag, "dissolve_faces faces=%ff", ELE_NEW);
187                 BMO_op_exec(bm, &op_sub);
188
189                 /* if we dissolved anything, then return */
190                 if (BMO_slot_buffer_count(op_sub.slots_out, "region.out")) {
191                         BMO_slot_copy(&op_sub, slots_out, "region.out",
192                                       op,      slots_out, "faces.out");
193                         BMO_op_finish(bm, &op_sub);
194                         return;
195                 }
196
197                 BMO_op_finish(bm, &op_sub);
198         }
199
200
201         /* -------------------------------------------------------------------- */
202         /* Fill EdgeLoop's - fills isolated loops, different from edgenet */
203         if (tote > 2) {
204                 BMOperator op_sub;
205                 /* note: in most cases 'edgenet_fill' will handle this case since in common cases
206                  * users fill in empty spaces, however its possible to have an edge selection around
207                  * existing geometry that makes 'edgenet_fill' fail. */
208                 BMO_op_initf(bm, &op_sub, op->flag, "edgeloop_fill edges=%fe", ELE_NEW);
209                 BMO_op_exec(bm, &op_sub);
210
211                 /* return if edge loop fill did something */
212                 if (BMO_slot_buffer_count(op_sub.slots_out, "faces.out")) {
213                         BMO_slot_copy(&op_sub, slots_out, "faces.out",
214                                       op,   slots_out, "faces.out");
215                         BMO_op_finish(bm, &op_sub);
216                         return;
217                 }
218
219                 BMO_op_finish(bm, &op_sub);
220         }
221
222
223
224         /* -------------------------------------------------------------------- */
225         /* Continue with ad-hoc fill methods since operators fail,
226          * edge, vcloud... may add more */
227
228         if (0) { /* nice feature but perhaps it should be a different tool? */
229
230                 /* tricky feature for making a line/edge from selection history...
231                  *
232                  * Rather then do nothing, when 5+ verts are selected, check if they are in our history,
233                  * when this is so, we can make edges from them, but _not_ a face,
234                  * if it is the intention to make a face the user can just hit F again since there will be edges next
235                  * time around.
236                  *
237                  * if all history verts have ELE_NEW flagged and the total number of history verts == totv,
238                  * then we know the history contains all verts here and we can continue...
239                  */
240
241                 BMEditSelection *ese;
242                 int tot_ese_v = 0;
243
244                 for (ese = bm->selected.first; ese; ese = ese->next) {
245                         if (ese->htype == BM_VERT) {
246                                 if (BMO_vert_flag_test(bm, (BMVert *)ese->ele, ELE_NEW)) {
247                                         tot_ese_v++;
248                                 }
249                                 else {
250                                         /* unflagged vert means we are not in sync */
251                                         tot_ese_v = -1;
252                                         break;
253                                 }
254                         }
255                 }
256
257                 if (tot_ese_v == totv) {
258                         BMVert *v_prev = NULL;
259                         /* yes, all select-history verts are accounted for, now make edges */
260
261                         for (ese = bm->selected.first; ese; ese = ese->next) {
262                                 if (ese->htype == BM_VERT) {
263                                         BMVert *v = (BMVert *)ese->ele;
264                                         if (v_prev) {
265                                                 BMEdge *e = BM_edge_create(bm, v, v_prev, NULL, BM_CREATE_NO_DOUBLE);
266                                                 BMO_edge_flag_enable(bm, e, ELE_OUT);
267                                         }
268                                         v_prev = v;
269                                 }
270                         }
271                 }
272                 BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "edges.out", BM_EDGE, ELE_OUT);
273                 /* done creating edges */
274
275                 return;
276         }
277
278
279         /* -------------------------------------------------------------------- */
280         /* Fill Vertex Cloud
281          *
282          * last resort when all else fails.
283          */
284         if (totv > 2) {
285                 /* TODO, some of these vertes may be connected by edges,
286                  * this connectivity could be used rather than treating
287                  * them as a bunch of isolated verts. */
288
289                 BMVert **vert_arr = MEM_mallocN(sizeof(BMVert *) * totv, __func__);
290                 BMFace *f;
291
292                 totv = BMO_iter_as_array(op->slots_in, "geom", BM_VERT, (void **)vert_arr, totv);
293
294                 BM_verts_sort_radial_plane(vert_arr, totv);
295
296                 /* create edges and find the winding (if faces are attached to any existing edges) */
297                 f = BM_face_create_ngon_verts(bm, vert_arr, totv, NULL, BM_CREATE_NO_DOUBLE, true, true);
298
299                 if (f) {
300                         BMO_face_flag_enable(bm, f, ELE_OUT);
301                         f->mat_nr = mat_nr;
302                         if (use_smooth) {
303                                 BM_elem_flag_enable(f, BM_ELEM_SMOOTH);
304                         }
305                         BM_face_copy_shared(bm, f, NULL, NULL);
306                         BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "faces.out", BM_FACE, ELE_OUT);
307                 }
308
309                 MEM_freeN(vert_arr);
310         }
311 }