04a39beec38d9aac4711fa9f75479d07138c7a50
[blender.git] / source / blender / bmesh / operators / bmo_inset.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 #include "MEM_guardedalloc.h"
24
25 #include "BLI_math.h"
26
27 #include "bmesh.h"
28
29 #include "intern/bmesh_operators_private.h" /* own include */
30
31 #define ELE_NEW         1
32
33 typedef struct SplitEdgeInfo {
34         float   no[3];
35         float   length;
36         BMEdge *e_old;
37         BMEdge *e_new;
38 } SplitEdgeInfo;
39
40 static void edge_loop_tangent(BMEdge *e, BMLoop *e_loop, float r_no[3])
41 {
42         float tvec[3];
43         BMVert *v1, *v2;
44         BM_edge_ordered_verts_ex(e, &v1, &v2, e_loop);
45
46         sub_v3_v3v3(tvec, v1->co, v2->co); /* use for temp storage */
47         cross_v3_v3v3(r_no, tvec, e_loop->f->no);
48         normalize_v3(r_no);
49 }
50
51 /**
52  * implementation is as follows...
53  *
54  * - set all faces as tagged/untagged based on selection.
55  * - find all edges that have 1 tagged, 1 untagged face.
56  * - separate these edges and tag vertices, set their index to point to the original edge.
57  * - build faces between old/new edges.
58  * - inset the new edges into their faces.
59  */
60
61 void bmo_inset_exec(BMesh *bm, BMOperator *op)
62 {
63         const int use_outset          = BMO_slot_bool_get(op, "use_outset");
64         const int use_boundary        = BMO_slot_bool_get(op, "use_boundary") && (use_outset == FALSE);
65         const int use_even_offset     = BMO_slot_bool_get(op, "use_even_offset");
66         const int use_even_boundry    = use_even_offset; /* could make own option */
67         const int use_relative_offset = BMO_slot_bool_get(op, "use_relative_offset");
68         const float thickness = BMO_slot_float_get(op, "thickness");
69
70         int edge_info_len = 0;
71
72         BMIter iter;
73         SplitEdgeInfo *edge_info;
74         SplitEdgeInfo *es;
75
76         BMVert *v;
77         BMEdge *e;
78         BMFace *f;
79         int i, j, k;
80
81         if (use_outset == FALSE) {
82                 BM_mesh_elem_flag_disable_all(bm, BM_FACE, BM_ELEM_TAG);
83                 BMO_slot_buffer_hflag_enable(bm, op, "faces", BM_FACE, BM_ELEM_TAG, FALSE);
84         }
85         else {
86                 BM_mesh_elem_flag_enable_all(bm, BM_FACE, BM_ELEM_TAG);
87                 BMO_slot_buffer_hflag_disable(bm, op, "faces", BM_FACE, BM_ELEM_TAG, FALSE);
88         }
89
90         /* first count all inset edges we will split */
91         /* fill in array and initialize tagging */
92         BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
93                 BMLoop *la, *lb;
94
95                 if (
96                     /* tag if boundary is enabled */
97                     (use_boundary && BM_edge_is_boundary(e) && BM_elem_flag_test(e->l->f, BM_ELEM_TAG)) ||
98
99                     /* tag if edge is an interior edge inbetween a tagged and untagged face */
100                     ((BM_edge_loop_pair(e, &la, &lb)) && (BM_elem_flag_test(la->f, BM_ELEM_TAG) != BM_elem_flag_test(lb->f, BM_ELEM_TAG))))
101                 {
102                         /* tag */
103                         BM_elem_flag_enable(e->v1, BM_ELEM_TAG);
104                         BM_elem_flag_enable(e->v2, BM_ELEM_TAG);
105                         BM_elem_flag_enable(e, BM_ELEM_TAG);
106
107                         BM_elem_index_set(e, edge_info_len); /* set_dirty! */
108                         edge_info_len++;
109                 }
110                 else {
111                         BM_elem_flag_disable(e->v1, BM_ELEM_TAG);
112                         BM_elem_flag_disable(e->v2, BM_ELEM_TAG);
113                         BM_elem_flag_disable(e, BM_ELEM_TAG);
114
115                         BM_elem_index_set(e, -1); /* set_dirty! */
116                 }
117         }
118         bm->elem_index_dirty |= BM_EDGE;
119
120         edge_info = MEM_mallocN(edge_info_len * sizeof(SplitEdgeInfo), __func__);
121
122         /* fill in array and initialize tagging */
123         es = edge_info;
124         BM_ITER(e, &iter, bm, BM_EDGES_OF_MESH, NULL) {
125                 i = BM_elem_index_get(e);
126                 if (i != -1) {
127                         /* calc edge-split info */
128                         es->length = BM_edge_length_calc(e);
129                         es->e_old = e;
130                         es++;
131                         /* initialize no and e_new after */
132                 }
133         }
134
135         for (i = 0, es = edge_info; i < edge_info_len; i++, es++) {
136                 BMLoop *l, *la, *lb;
137
138                 if (BM_edge_loop_pair(es->e_old, &la, &lb)) {
139                         l = BM_elem_flag_test(la->f, BM_ELEM_TAG) ? la : lb;
140                 }
141                 else {
142                         l = es->e_old->l; /* must be a boundary */
143                 }
144
145
146                 /* run the separate arg */
147                 bmesh_edge_separate(bm, es->e_old, l);
148
149                 /* calc edge-split info */
150                 es->e_new = l->e;
151                 edge_loop_tangent(es->e_new, l, es->no);
152
153
154                 if (es->e_new == es->e_old) { /* happens on boundary edges */
155                         es->e_old = BM_edge_create(bm, es->e_new->v1, es->e_new->v2, es->e_new, FALSE);
156                 }
157
158                 /* store index back to original in 'edge_info' */
159                 BM_elem_index_set(es->e_new, i);
160                 BM_elem_flag_enable(es->e_new, BM_ELEM_TAG);
161
162                 /* important to tag again here */
163                 BM_elem_flag_enable(es->e_new->v1, BM_ELEM_TAG);
164                 BM_elem_flag_enable(es->e_new->v2, BM_ELEM_TAG);
165         }
166
167
168         /* show edge normals for debugging */
169 #if 0
170         for (i = 0, es = edge_info; i < edge_info_len; i++, es++) {
171                 float tvec[3];
172                 BMVert *v1, *v2;
173
174                 mid_v3_v3v3(tvec, es->e_new->v1->co, es->e_new->v2->co);
175
176                 v1 = BM_vert_create(bm, tvec, NULL);
177                 v2 = BM_vert_create(bm, tvec, NULL);
178                 madd_v3_v3fl(v2->co, es->no, 0.1f);
179                 BM_edge_create(bm, v1, v2, NULL, FALSE);
180         }
181 #endif
182
183         /* execute the split and position verts, it would be most obvious to loop over verts
184          * here but don't do this since we will be splitting them off (iterating stuff you modify is bad juju)
185          * instead loop over edges then their verts */
186         for (i = 0, es = edge_info; i < edge_info_len; i++, es++) {
187                 for (j = 0; j < 2; j++) {
188                         v = (j == 0) ? es->e_new->v1 : es->e_new->v2;
189                         /* end confusing part - just pretend this is a typical loop on verts */
190
191
192                         /* only split of tagged verts - used by separated edges */
193
194                         /* comment the first part because we know this verts in a tagged face */
195                         if (/* v->e && */BM_elem_flag_test(v, BM_ELEM_TAG)) {
196                                 BMVert **vout;
197                                 int r_vout_len;
198                                 BMVert *v_glue = NULL;
199
200                                 /* disable touching twice, this _will_ happen if the flags not disabled */
201                                 BM_elem_flag_disable(v, BM_ELEM_TAG);
202
203                                 bmesh_vert_separate(bm, v, &vout, &r_vout_len);
204                                 v = NULL; /* don't use again */
205
206                                 for (k = 0; k < r_vout_len; k++) {
207                                         BMVert *v_split = vout[k]; /* only to avoid vout[k] all over */
208
209                                         /* need to check if this vertex is from a */
210                                         int vert_edge_tag_tot = 0;
211                                         int vecpair[2];
212
213                                         /* find adjacent */
214                                         BM_ITER(e, &iter, bm, BM_EDGES_OF_VERT, v_split) {
215                                                 if (BM_edge_is_boundary(e) && /* this will be true because bmesh_edge_separate() has run */
216                                                     BM_elem_flag_test(e, BM_ELEM_TAG) &&
217                                                     BM_elem_flag_test(e->l->f, BM_ELEM_TAG))
218                                                 {
219                                                         if (vert_edge_tag_tot < 2) {
220                                                                 vecpair[vert_edge_tag_tot] = BM_elem_index_get(e);
221                                                                 BLI_assert(vecpair[vert_edge_tag_tot] != -1);
222                                                         }
223
224                                                         vert_edge_tag_tot++;
225                                                 }
226                                         }
227
228                                         if (vert_edge_tag_tot != 0) {
229                                                 float tvec[3];
230
231                                                 if (vert_edge_tag_tot >= 2) { /* 2 edge users - common case */
232                                                         const float *e_no_a = edge_info[vecpair[0]].no;
233                                                         const float *e_no_b = edge_info[vecpair[1]].no;
234
235                                                         add_v3_v3v3(tvec, e_no_a, e_no_b);
236                                                         normalize_v3(tvec);
237
238                                                         /* scale by edge angle */
239                                                         if (use_even_offset) {
240                                                                 mul_v3_fl(tvec, shell_angle_to_dist(angle_normalized_v3v3(e_no_a, e_no_b) / 2.0f));
241                                                         }
242
243                                                         /* scale relative to edge lengths */
244                                                         if (use_relative_offset) {
245                                                                 mul_v3_fl(tvec, (edge_info[vecpair[0]].length + edge_info[vecpair[1]].length) / 2.0f);
246                                                         }
247                                                 }
248                                                 else if (vert_edge_tag_tot == 1) { /* 1 edge user - boundary vert, not so common */
249                                                         const float *e_no_a = edge_info[vecpair[0]].no;
250
251                                                         if (use_even_boundry) {
252
253                                                                 /* This case where only one edge attached to v_split
254                                                                  * is used - ei - the face to inset is on a boundary.
255                                                                  *
256                                                                  *                  We want the inset to align flush with the
257                                                                  *                  boundary edge, not the normal of the interior
258                                                                  *             <--- edge which would give an unsightly bump.
259                                                                  * --+-------------------------+---------------+--
260                                                                  *   |^v_other    ^e_other    /^v_split        |
261                                                                  *   |                       /                 |
262                                                                  *   |                      /                  |
263                                                                  *   |                     / <- tag split edge |
264                                                                  *   |                    /                    |
265                                                                  *   |                   /                     |
266                                                                  *   |                  /                      |
267                                                                  * --+-----------------+-----------------------+--
268                                                                  *   |                                         |
269                                                                  *   |                                         |
270                                                                  *
271                                                                  * note, the fact we are doing location comparisons on verts that are moved about
272                                                                  * doesn’t matter because the direction will remain the same in this case.
273                                                                  */
274
275                                                                 BMEdge *e_other;
276                                                                 BMVert *v_other;
277                                                                 /* loop will always be either next of prev */
278                                                                 BMLoop *l = v_split->e->l;
279                                                                 if (l->prev->v == v_split) {
280                                                                         l = l->prev;
281                                                                 }
282                                                                 else if (l->next->v == v_split) {
283                                                                         l = l->next;
284                                                                 }
285                                                                 else if (l->v == v_split) {
286                                                                         /* pass */
287                                                                 }
288                                                                 else {
289                                                                         /* should never happen */
290                                                                         BLI_assert(0);
291                                                                 }
292
293                                                                 /* find the edge which is _not_ being split here */
294                                                                 if (!BM_elem_flag_test(l->e, BM_ELEM_TAG)) {
295                                                                         e_other = l->e;
296                                                                 }
297                                                                 else if (!BM_elem_flag_test(l->prev->e, BM_ELEM_TAG)) {
298                                                                         e_other = l->prev->e;
299                                                                 }
300                                                                 else {
301                                                                         BLI_assert(0);
302                                                                         e_other = NULL;
303                                                                 }
304
305                                                                 v_other = BM_edge_other_vert(e_other, v_split);
306                                                                 sub_v3_v3v3(tvec, v_other->co, v_split->co);
307                                                                 normalize_v3(tvec);
308
309                                                                 if (use_even_offset) {
310                                                                         mul_v3_fl(tvec, shell_angle_to_dist(angle_normalized_v3v3(e_no_a, tvec)));
311                                                                 }
312                                                         }
313                                                         else {
314                                                                 copy_v3_v3(tvec, e_no_a);
315                                                         }
316
317                                                         /* use_even_offset - doesn't apply here */
318
319                                                         /* scale relative to edge length */
320                                                         if (use_relative_offset) {
321                                                                 mul_v3_fl(tvec, edge_info[vecpair[0]].length);
322                                                         }
323                                                 }
324                                                 else {
325                                                         /* should never happen */
326                                                         BLI_assert(0);
327                                                         zero_v3(tvec);
328                                                 }
329
330                                                 /* apply the offset */
331                                                 madd_v3_v3fl(v_split->co, tvec, thickness);
332                                         }
333
334                                         /* this saves expensive/slow glue check for common cases */
335                                         if (r_vout_len > 2) {
336                                                 int ok = TRUE;
337                                                 /* last step, NULL this vertex if has a tagged face */
338                                                 BM_ITER(f, &iter, bm, BM_FACES_OF_VERT, v_split) {
339                                                         if (BM_elem_flag_test(f, BM_ELEM_TAG)) {
340                                                                 ok = FALSE;
341                                                                 break;
342                                                         }
343                                                 }
344
345                                                 if (ok) {
346                                                         if (v_glue == NULL) {
347                                                                 v_glue = v_split;
348                                                         }
349                                                         else {
350                                                                 BM_vert_splice(bm, v_split, v_glue);
351                                                         }
352                                                 }
353                                         }
354                                         /* end glue */
355
356                                 }
357                                 MEM_freeN(vout);
358                         }
359                 }
360         }
361
362         /* create faces */
363         for (i = 0, es = edge_info; i < edge_info_len; i++, es++) {
364                 BMVert *v1, *v2, *v3, *v4;
365
366                 /* get the verts in the correct order */
367                 BM_edge_ordered_verts(es->e_new, &v1, &v2);
368                 if (v1 == es->e_new->v1) {
369                         v3 = es->e_old->v2;
370                         v4 = es->e_old->v1;
371                 }
372                 else {
373                         v3 = es->e_old->v1;
374                         v4 = es->e_old->v2;
375                 }
376
377                 /* no need to check doubles, we KNOW there won't be any */
378                 /* yes - reverse face is correct in this case */
379                 f = BM_face_create_quad_tri(bm, v4, v3, v2, v1, es->e_new->l->f, FALSE);
380                 BMO_elem_flag_enable(bm, f, ELE_NEW);
381         }
382
383         MEM_freeN(edge_info);
384
385         /* we could flag new edges/verts too, is it useful? */
386         BMO_slot_buffer_from_flag(bm, op, "faceout", BM_FACE, ELE_NEW);
387 }