diff --git a/source/blender/bmesh/operators/bmo_offset_edgeloops.c b/source/blender/bmesh/operators/bmo_offset_edgeloops.c new file mode 100644 index 00000000000..d4d77f9a1cc --- /dev/null +++ b/source/blender/bmesh/operators/bmo_offset_edgeloops.c @@ -0,0 +1,223 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/operators/bmo_offset_edgeloops.c + * \ingroup bmesh + * + * Simple edge offset functionality. + * + * \note Actual offset is done by edge-slide. + * (this only changes topology) + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_math.h" +#include "BLI_alloca.h" +#include "BLI_stackdefines.h" + +#include "BKE_customdata.h" + +#include "bmesh.h" + +#include "intern/bmesh_operators_private.h" /* own include */ + +#define ELE_NEW 1 + +/* set for debugging */ +#define OFFSET 0.0f + +static BMFace *bm_face_split_walk_back( + BMesh *bm, BMLoop *l_src, + BMLoop **r_l) +{ + float (*cos)[3]; + BMLoop *l_dst; + BMFace *f; + int num, i; + + for (l_dst = l_src->prev, num = 0; BM_elem_index_get(l_dst->prev->v) != -1; l_dst = l_dst->prev, num++) { + /* pass */ + } + + BLI_assert(num != 0); + + cos = BLI_array_alloca( + cos, num); + + for (l_dst = l_src->prev, i = 0; BM_elem_index_get(l_dst->prev->v) != -1; l_dst = l_dst->prev, i++) { + copy_v3_v3(cos[num - (i + 1)], l_dst->v->co); + } + + f = BM_face_split_n( bm, l_src->f, l_dst->prev, l_src->next, cos, num, r_l, NULL); + + return f; +} + +void bmo_offset_edgeloops_exec(BMesh *bm, BMOperator *op) +{ + const int edges_num = BMO_slot_buffer_count(op->slots_in, "edges"); + BMVert **verts; + STACK_DECLARE(verts); + int i; + + BMOIter oiter; + + /* only so we can detect new verts (index == -1) */ + BM_mesh_elem_index_ensure(bm, BM_VERT); + + BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); + + /* over alloc */ + verts = MEM_mallocN(sizeof(*verts) * (edges_num * 2), __func__); + + STACK_INIT(verts, (edges_num * 2)); + + { + BMEdge *e; + BMO_ITER (e, &oiter, op->slots_in, "edges", BM_EDGE) { + int j; + + BM_elem_flag_enable(e, BM_ELEM_TAG); + + for (j = 0; j < 2; j++) { + BMVert *v_edge = *(&(e->v1) + j); + if (!BM_elem_flag_test(v_edge, BM_ELEM_TAG)) { + BM_elem_flag_enable(v_edge, BM_ELEM_TAG); + STACK_PUSH(verts, v_edge); + } + } + } + } + + + /* -------------------------------------------------------------------- */ + /* Remove verts only used by tagged edges */ + + for (i = 0; i < STACK_SIZE(verts); i++) { + BMIter iter; + int flag = 0; + BMEdge *e; + + BM_ITER_ELEM (e, &iter, verts[i], BM_EDGES_OF_VERT) { + flag |= BM_elem_flag_test(e, BM_ELEM_TAG) ? 1 : 2; + if (flag == (1 | 2)) { + break; + } + } + + /* only boundary verts are interesting */ + if (flag != (1 | 2)) { + STACK_REMOVE(verts, i); + } + } + + /* possible but unlikely we have no mixed vertices */ + if (UNLIKELY(STACK_SIZE(verts) == 0)) { + return; + } + + /* main loop */ + for (i = 0; i < STACK_SIZE(verts); i++) { + BMVert *v = verts[i]; + BMIter iter; + BMEdge *e; + + BM_ITER_ELEM (e, &iter, verts[i], BM_EDGES_OF_VERT) { + if (!BM_elem_flag_test(e, BM_ELEM_TAG)) { + BMVert *v_other; + BMIter liter; + BMLoop *l; + + BM_ITER_ELEM (l, &liter, e, BM_LOOPS_OF_EDGE) { + BM_elem_flag_enable(l->f, BM_ELEM_TAG); + } + + v_other = BM_edge_other_vert(e, v); + BM_edge_split(bm, e, v_other, NULL, 1.0f - OFFSET); + } + } + } + + + for (i = 0; i < STACK_SIZE(verts); i++) { + BMVert *v = verts[i]; + BMIter liter; + BMLoop *l; + + BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { + if (BM_elem_flag_test(l->f, BM_ELEM_TAG) && + (l->f->len != 3)) + { + BMFace *f_cmp = l->f; + if ((BM_elem_index_get(l->next->v) == -1) && + (BM_elem_index_get(l->prev->v) == -1)) + { + BMLoop *l_new; + BM_face_split(bm, l->f, l->prev, l->next, &l_new, NULL, true); + BLI_assert(f_cmp == l->f); + BLI_assert(f_cmp != l_new->f); + BMO_elem_flag_enable(bm, l_new->e, ELE_NEW); + } + else if (l->f->len > 4) { + if (BM_elem_flag_test(l->e, BM_ELEM_TAG) != + BM_elem_flag_test(l->prev->e, BM_ELEM_TAG)) + { + if (BM_elem_index_get(l->next->v) == -1) { + if (BM_elem_index_get(l->prev->prev->v) == -1) { + BMLoop *l_new; + BM_face_split(bm, l->f, l->prev->prev, l->next, &l_new, NULL, true); + BLI_assert(f_cmp == l->f); + BLI_assert(f_cmp != l_new->f); + BMO_elem_flag_enable(bm, l_new->e, ELE_NEW); + BM_elem_flag_disable(l->f, BM_ELEM_TAG); + } + else { + /* walk backwards */ + BMLoop *l_new; + bm_face_split_walk_back(bm, l, &l_new); + do { + BMO_elem_flag_enable(bm, l_new->e, ELE_NEW); + l_new = l_new->next; + } while (BM_vert_is_edge_pair(l_new->v)); + BM_elem_flag_disable(l->f, BM_ELEM_TAG); + } + } + + /* Note: instead of duplicate code in alternate direction, + * we can be sure to hit the other vertex, so the code above runs. */ +#if 0 + else if (BM_elem_index_get(l->prev->v) == -1) { + if (BM_elem_index_get(l->next->next->v) == -1) { + } + } +#endif + } + } + } + } + } + + MEM_freeN(verts); + + BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "edges.out", BM_EDGE, ELE_NEW); +}