From b7174c9320c5e3446d8237059841d982279e32e9 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 1 Nov 2014 23:31:01 +0100 Subject: Fix connect-vertices failing for concave ngons Also add: - generic callback for bmesh elements. - ability to pass an existing array to a bmesh operator. --- source/blender/bmesh/CMakeLists.txt | 2 + source/blender/bmesh/bmesh.h | 1 + .../blender/bmesh/intern/bmesh_callback_generic.c | 55 ++++++++ .../blender/bmesh/intern/bmesh_callback_generic.h | 44 ++++++ source/blender/bmesh/intern/bmesh_operator_api.h | 2 + source/blender/bmesh/intern/bmesh_operators.c | 33 ++++- source/blender/bmesh/intern/bmesh_queries.c | 20 +++ source/blender/bmesh/intern/bmesh_queries.h | 3 + source/blender/bmesh/operators/bmo_connect_pair.c | 147 +++++++++++---------- source/blender/editors/mesh/editmesh_tools.c | 52 ++++++-- 10 files changed, 275 insertions(+), 84 deletions(-) create mode 100644 source/blender/bmesh/intern/bmesh_callback_generic.c create mode 100644 source/blender/bmesh/intern/bmesh_callback_generic.h (limited to 'source') diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index 50d3ac30ddc..23737866bf2 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -74,6 +74,8 @@ set(SRC operators/bmo_utils.c operators/bmo_wireframe.c + intern/bmesh_callback_generic.c + intern/bmesh_callback_generic.h intern/bmesh_construct.c intern/bmesh_construct.h intern/bmesh_core.c diff --git a/source/blender/bmesh/bmesh.h b/source/blender/bmesh/bmesh.h index 4efc6aa16f8..87b1818fa5d 100644 --- a/source/blender/bmesh/bmesh.h +++ b/source/blender/bmesh/bmesh.h @@ -243,6 +243,7 @@ extern "C" { #include "intern/bmesh_error.h" #include "intern/bmesh_core.h" +#include "intern/bmesh_callback_generic.h" #include "intern/bmesh_construct.h" #include "intern/bmesh_delete.h" #include "intern/bmesh_edgeloop.h" diff --git a/source/blender/bmesh/intern/bmesh_callback_generic.c b/source/blender/bmesh/intern/bmesh_callback_generic.c new file mode 100644 index 00000000000..91fec392552 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_callback_generic.c @@ -0,0 +1,55 @@ +/* + * ***** 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. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/bmesh/intern/bmesh_callback_generic.c + * \ingroup bmesh + * + * BM construction functions. + */ + +#include "BLI_utildefines.h" + +#include "bmesh.h" + +#include "intern/bmesh_callback_generic.h" + +bool BM_elem_cb_check_hflag_ex(BMElem *ele, void *user_data) +{ + const unsigned int hflag_pair = GET_INT_FROM_POINTER(user_data); + const char hflag_p = (hflag_pair & 0xff); + const char hflag_n = (hflag_pair >> 8); + + return ((BM_elem_flag_test(ele, hflag_p) != 0) && + (BM_elem_flag_test(ele, hflag_n) == 0)); +} + +bool BM_elem_cb_check_hflag_enabled(BMElem *ele, void *user_data) +{ + const char hflag = GET_INT_FROM_POINTER(user_data); + + return (BM_elem_flag_test(ele, hflag) != 0); +} + +bool BM_elem_cb_check_hflag_disabled(BMElem *ele, void *user_data) +{ + const char hflag = GET_INT_FROM_POINTER(user_data); + + return (BM_elem_flag_test(ele, hflag) == 0); +} diff --git a/source/blender/bmesh/intern/bmesh_callback_generic.h b/source/blender/bmesh/intern/bmesh_callback_generic.h new file mode 100644 index 00000000000..8c46128f3b0 --- /dev/null +++ b/source/blender/bmesh/intern/bmesh_callback_generic.h @@ -0,0 +1,44 @@ +/* + * ***** 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. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef __BMESH_CALLBACK_GENERIC_H__ +#define __BMESH_CALLBACK_GENERIC_H__ + +/** \file blender/bmesh/intern/bmesh_callback_generic.h + * \ingroup bmesh + */ + +bool BM_elem_cb_check_hflag_enabled(BMElem *, void *user_data); +bool BM_elem_cb_check_hflag_disabled(BMElem *, void *user_data); +bool BM_elem_cb_check_hflag_ex(BMElem *, void *user_data); + +#define BM_elem_cb_check_hflag_ex_simple(type, hflag_p, hflag_n) \ + (bool (*)(type, void *))BM_elem_cb_check_hflag_ex, \ + SET_UINT_IN_POINTER(((hflag_p) | (hflag_n << 8))) + +#define BM_elem_cb_check_hflag_enabled_simple(type, hflag_p) \ + (bool (*)(type, void *))BM_elem_cb_check_hflag_enabled, \ + SET_UINT_IN_POINTER((hflag_p)) + +#define BM_elem_cb_check_hflag_disabled_simple(type, hflag_n) \ + (bool (*)(type, void *))BM_elem_cb_check_hflag_disabled, \ + SET_UINT_IN_POINTER(hflag_n) + +#endif /* __BMESH_CALLBACK_GENERIC_H__ */ diff --git a/source/blender/bmesh/intern/bmesh_operator_api.h b/source/blender/bmesh/intern/bmesh_operator_api.h index 287aafc8f9f..825bbb136b1 100644 --- a/source/blender/bmesh/intern/bmesh_operator_api.h +++ b/source/blender/bmesh/intern/bmesh_operator_api.h @@ -395,6 +395,8 @@ void BMO_slot_buffer_from_disabled_hflag(BMesh *bm, BMOperator *op, BMOpSlot slot_args[BMO_OP_MAX_SLOTS], const char *slot_name, const char htype, const char hflag); +void BMO_slot_buffer_from_array(BMOperator *op, BMOpSlot *slot, BMHeader **ele_buffer, int ele_buffer_len); + void BMO_slot_buffer_from_single(BMOperator *op, BMOpSlot *slot, BMHeader *ele); void *BMO_slot_buffer_get_single(BMOpSlot *slot); diff --git a/source/blender/bmesh/intern/bmesh_operators.c b/source/blender/bmesh/intern/bmesh_operators.c index b041c010c22..ba154b04838 100644 --- a/source/blender/bmesh/intern/bmesh_operators.c +++ b/source/blender/bmesh/intern/bmesh_operators.c @@ -900,6 +900,21 @@ void BMO_slot_buffer_from_single(BMOperator *op, BMOpSlot *slot, BMHeader *ele) *slot->data.buf = ele; } +void BMO_slot_buffer_from_array(BMOperator *op, BMOpSlot *slot, BMHeader **ele_buffer, int ele_buffer_len) +{ + BMO_ASSERT_SLOT_IN_OP(slot, op); + BLI_assert(slot->slot_type == BMO_OP_SLOT_ELEMENT_BUF); + BLI_assert(slot->len == 0 || slot->len == ele_buffer_len); + + if (slot->data.buf == NULL) { + slot->data.buf = BLI_memarena_alloc(op->arena, sizeof(*slot->data.buf) * ele_buffer_len); + } + + slot->len = ele_buffer_len; + memcpy(slot->data.buf, ele_buffer, ele_buffer_len * sizeof(*slot->data.buf)); +} + + void *BMO_slot_buffer_get_single(BMOpSlot *slot) { BLI_assert(slot->slot_type == BMO_OP_SLOT_ELEMENT_BUF); @@ -1636,6 +1651,7 @@ static int BMO_opcode_from_opname_check(const char *opname) * * **Element Buffer** (#BMO_OP_SLOT_ELEMENT_BUF) * - `e` - single element vert/edge/face (use with #BMO_OP_SLOT_SUBTYPE_ELEM_IS_SINGLE). + * - `eb` - elem buffer, take an array and a length. * - `av` - all verts * - `ae` - all edges * - `af` - all faces @@ -1756,12 +1772,23 @@ bool BMO_op_vinitf(BMesh *bm, BMOperator *op, const int flag, const char *_fmt, state = true; break; } - case 'e': /* single vert/edge/face */ + case 'e': { - BMHeader *ele = va_arg(vlist, void *); BMOpSlot *slot = BMO_slot_get(op->slots_in, slot_name); - BMO_slot_buffer_from_single(op, slot, ele); + if (NEXT_CHAR(fmt) == 'b') { + BMHeader **ele_buffer = va_arg(vlist, void *); + int ele_buffer_len = va_arg(vlist, int); + + BMO_slot_buffer_from_array(op, slot, ele_buffer, ele_buffer_len); + fmt++; + } + else { + /* single vert/edge/face */ + BMHeader *ele = va_arg(vlist, void *); + + BMO_slot_buffer_from_single(op, slot, ele); + } state = true; break; diff --git a/source/blender/bmesh/intern/bmesh_queries.c b/source/blender/bmesh/intern/bmesh_queries.c index f301108a6ae..ca40cf9e6c8 100644 --- a/source/blender/bmesh/intern/bmesh_queries.c +++ b/source/blender/bmesh/intern/bmesh_queries.c @@ -202,6 +202,26 @@ bool BM_vert_pair_share_face_check( return false; } +bool BM_vert_pair_share_face_check_cb( + BMVert *v_a, BMVert *v_b, + bool (*test_fn)(BMFace *, void *user_data), void *user_data) +{ + if (v_a->e && v_b->e) { + BMIter iter; + BMFace *f; + + BM_ITER_ELEM (f, &iter, v_a, BM_FACES_OF_VERT) { + if (test_fn(f, user_data)) { + if (BM_vert_in_face(f, v_b)) { + return true; + } + } + } + } + + return false; +} + /** * Given 2 verts, find the smallest face they share and give back both loops. */ diff --git a/source/blender/bmesh/intern/bmesh_queries.h b/source/blender/bmesh/intern/bmesh_queries.h index 0d47633dc73..c8578a8b093 100644 --- a/source/blender/bmesh/intern/bmesh_queries.h +++ b/source/blender/bmesh/intern/bmesh_queries.h @@ -52,6 +52,9 @@ BMLoop *BM_vert_find_first_loop(BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL( bool BM_vert_pair_share_face_check( BMVert *v_a, BMVert *v_b) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); +bool BM_vert_pair_share_face_check_cb( + BMVert *v_a, BMVert *v_b, + bool (*test_fn)(BMFace *f, void *user_data), void *user_data) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2, 3); BMFace *BM_vert_pair_share_face_by_len( BMVert *v_a, BMVert *v_b, BMLoop **r_l_a, BMLoop **r_l_b, diff --git a/source/blender/bmesh/operators/bmo_connect_pair.c b/source/blender/bmesh/operators/bmo_connect_pair.c index cfffb6021cf..916ea3ebcd9 100644 --- a/source/blender/bmesh/operators/bmo_connect_pair.c +++ b/source/blender/bmesh/operators/bmo_connect_pair.c @@ -230,23 +230,45 @@ static PathLinkState *state_dupe_add( static PathLinkState *state_step__face_edges( PathContext *pc, PathLinkState *state, const PathLinkState *state_orig, - BMLoop *l_iter, BMLoop *l_last) + BMLoop *l_iter, BMLoop *l_last, + float *r_dist_best) { + BMLoop *l_iter_best = NULL; + float dist_best = *r_dist_best; + do { if (state_isect_co_pair(pc, l_iter->v->co, l_iter->next->v->co)) { - BMElem *ele_next = (BMElem *)l_iter->e; - BMElem *ele_next_from = (BMElem *)l_iter->f; + float dist_test; + float co_isect[3]; - if (FACE_WALK_TEST((BMFace *)ele_next_from) && - (state_link_find(state, ele_next) == false)) - { - if (state_orig->link_last != state->link_last) { - state = state_dupe_add(pc, state, state_orig); + state_calc_co_pair(pc, l_iter->v->co, l_iter->next->v->co, co_isect); + dist_test = len_squared_v3v3(state->co_prev, co_isect); + if (dist_test < dist_best) { + BMElem *ele_next = (BMElem *)l_iter->e; + BMElem *ele_next_from = (BMElem *)l_iter->f; + + if (FACE_WALK_TEST((BMFace *)ele_next_from) && + (state_link_find(state, ele_next) == false)) + { + dist_best = dist_test; + l_iter_best = l_iter; } - state_link_add(pc, state, ele_next, ele_next_from); } } } while ((l_iter = l_iter->next) != l_last); + + if ((l_iter = l_iter_best)) { + BMElem *ele_next = (BMElem *)l_iter->e; + BMElem *ele_next_from = (BMElem *)l_iter->f; + + if (state_orig->link_last != state->link_last) { + state = state_dupe_add(pc, state, state_orig); + } + state_link_add(pc, state, ele_next, ele_next_from); + } + + *r_dist_best = dist_best; + return state; } @@ -254,23 +276,40 @@ static PathLinkState *state_step__face_edges( static PathLinkState *state_step__face_verts( PathContext *pc, PathLinkState *state, const PathLinkState *state_orig, - BMLoop *l_iter, BMLoop *l_last) + BMLoop *l_iter, BMLoop *l_last, float *r_dist_best) { + BMLoop *l_iter_best = NULL; + float dist_best = *r_dist_best; + do { if (state_isect_co_exact(pc, l_iter->v->co)) { - BMElem *ele_next = (BMElem *)l_iter->v; - BMElem *ele_next_from = (BMElem *)l_iter->f; + const float dist_test = len_squared_v3v3(state->co_prev, l_iter->v->co); + if (dist_test < dist_best) { + BMElem *ele_next = (BMElem *)l_iter->v; + BMElem *ele_next_from = (BMElem *)l_iter->f; - if (FACE_WALK_TEST((BMFace *)ele_next_from) && - state_link_find(state, ele_next) == false) - { - if (state_orig->link_last != state->link_last) { - state = state_dupe_add(pc, state, state_orig); + if (FACE_WALK_TEST((BMFace *)ele_next_from) && + state_link_find(state, ele_next) == false) + { + dist_best = dist_test; + l_iter_best = l_iter; } - state_link_add(pc, state, ele_next, ele_next_from); } } } while ((l_iter = l_iter->next) != l_last); + + if ((l_iter = l_iter_best)) { + BMElem *ele_next = (BMElem *)l_iter->v; + BMElem *ele_next_from = (BMElem *)l_iter->f; + + if (state_orig->link_last != state->link_last) { + state = state_dupe_add(pc, state, state_orig); + } + state_link_add(pc, state, ele_next, ele_next_from); + } + + *r_dist_best = dist_best; + return state; } @@ -290,20 +329,12 @@ static bool state_step(PathContext *pc, PathLinkState *state) if ((l_start->f != ele_from) && FACE_WALK_TEST(l_start->f)) { + float dist_best = FLT_MAX; /* very similar to block below */ - if (BM_vert_in_face(l_start->f, pc->v_b)) { - if (state_orig.link_last != state->link_last) { - state = state_dupe_add(pc, state, &state_orig); - } - - state_link_add(pc, state, (BMElem *)pc->v_b, (BMElem *)l_start->f); - } - else { - state = state_step__face_edges(pc, state, &state_orig, - l_start->next, l_start); - state = state_step__face_verts(pc, state, &state_orig, - l_start->next->next, l_start); - } + state = state_step__face_edges(pc, state, &state_orig, + l_start->next, l_start, &dist_best); + state = state_step__face_verts(pc, state, &state_orig, + l_start->next->next, l_start, &dist_best); } } } @@ -319,24 +350,14 @@ static bool state_step(PathContext *pc, PathLinkState *state) if ((l_start->f != ele_from) && FACE_WALK_TEST(l_start->f)) { + float dist_best = FLT_MAX; /* very similar to block above */ - if (BM_vert_in_face(l_start->f, pc->v_b)) { - BMElem *ele_next = (BMElem *)pc->v_b; - BMElem *ele_next_from = (BMElem *)l_start->f; - - if (state_orig.link_last != state->link_last) { - state = state_dupe_add(pc, state, &state_orig); - } - state_link_add(pc, state, ele_next, ele_next_from); - } - else { - state = state_step__face_edges(pc, state, &state_orig, - l_start->next, l_start->prev); - if (l_start->f->len > 3) { - /* adjacent verts are handled in state_step__vert_edges */ - state = state_step__face_verts(pc, state, &state_orig, - l_start->next->next, l_start->prev); - } + state = state_step__face_edges(pc, state, &state_orig, + l_start->next, l_start->prev, &dist_best); + if (l_start->f->len > 3) { + /* adjacent verts are handled in state_step__vert_edges */ + state = state_step__face_verts(pc, state, &state_orig, + l_start->next->next, l_start->prev, &dist_best); } } } @@ -351,31 +372,19 @@ static bool state_step(PathContext *pc, PathLinkState *state) if (((BMElem *)e != ele_from) && VERT_WALK_TEST(v_other)) { - if (v_other == pc->v_b) { - BMElem *ele_next = (BMElem *)pc->v_b; + if (state_isect_co_exact(pc, v_other->co)) { + BMElem *ele_next = (BMElem *)v_other; BMElem *ele_next_from = (BMElem *)e; - - if (state_orig.link_last != state->link_last) { - state = state_dupe_add(pc, state, &state_orig); - } - state_link_add(pc, state, ele_next, ele_next_from); - } - else { - if (state_isect_co_exact(pc, v_other->co)) { - BMElem *ele_next = (BMElem *)v_other; - BMElem *ele_next_from = (BMElem *)e; - if (state_link_find(state, ele_next) == false) { - if (state_orig.link_last != state->link_last) { - state = state_dupe_add(pc, state, &state_orig); - } - state_link_add(pc, state, ele_next, ele_next_from); + if (state_link_find(state, ele_next) == false) { + if (state_orig.link_last != state->link_last) { + state = state_dupe_add(pc, state, &state_orig); } + state_link_add(pc, state, ele_next, ele_next_from); } } } } } - } else { BLI_assert(0); @@ -562,12 +571,10 @@ void bmo_connect_vert_pair_exec(BMesh *bm, BMOperator *op) #if 1 if (found_all) { - /* leave 'check_degenerate' off, if a user tries to cut with 2 verts, - * always connect even when resulting faces are degenerate [#39418] */ BMOperator op_sub; BMO_op_initf(bm, &op_sub, 0, - "connect_verts verts=%fv faces_exclude=%s", - VERT_OUT, op, "faces_exclude"); + "connect_verts verts=%fv faces_exclude=%s check_degenerate=%b", + VERT_OUT, op, "faces_exclude", true); BMO_op_exec(bm, &op_sub); BMO_slot_copy(&op_sub, slots_out, "edges.out", op, slots_out, "edges.out"); diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 20c7f4eb521..4f4a7998a87 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -877,23 +877,50 @@ static int edbm_vert_connect_exec(bContext *C, wmOperator *op) BMEditMesh *em = BKE_editmesh_from_object(obedit); BMesh *bm = em->bm; BMOperator bmop; - const bool is_pair = (bm->totvertsel == 2); + bool is_pair = (bm->totvertsel == 2); int len; - + bool check_degenerate = true; + const int verts_len = bm->totvertsel; + BMVert **verts; + + + verts = MEM_mallocN(sizeof(*verts) * verts_len, __func__); + { + BMIter iter; + BMVert *v; + int i = 0; + + BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { + if (BM_elem_flag_test(v, BM_ELEM_SELECT)) { + verts[i++] = v; + } + } + + if (is_pair) { + if (BM_vert_pair_share_face_check_cb( + verts[0], verts[1], + BM_elem_cb_check_hflag_disabled_simple(BMFace *, BM_ELEM_HIDDEN))) + { + check_degenerate = false; + is_pair = false; + } + } + } + if (is_pair) { if (!EDBM_op_init(em, &bmop, op, - "connect_vert_pair verts=%hv verts_exclude=%hv faces_exclude=%hf", - BM_ELEM_SELECT, BM_ELEM_HIDDEN, BM_ELEM_HIDDEN)) + "connect_vert_pair verts=%eb verts_exclude=%hv faces_exclude=%hf", + verts, verts_len, BM_ELEM_HIDDEN, BM_ELEM_HIDDEN)) { - return OPERATOR_CANCELLED; + goto finally; } } else { if (!EDBM_op_init(em, &bmop, op, - "connect_verts verts=%hv faces_exclude=%hf check_degenerate=%b", - BM_ELEM_SELECT, BM_ELEM_HIDDEN, true)) + "connect_verts verts=%eb faces_exclude=%hf check_degenerate=%b", + verts, verts_len, BM_ELEM_HIDDEN, check_degenerate)) { - return OPERATOR_CANCELLED; + goto finally; } } @@ -908,15 +935,18 @@ static int edbm_vert_connect_exec(bContext *C, wmOperator *op) } if (!EDBM_op_finish(em, &bmop, op, true)) { - return OPERATOR_CANCELLED; + len = 0; } else { EDBM_selectmode_flush(em); /* so newly created edges get the selection state from the vertex */ EDBM_update_generic(em, true, true); - - return len ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } + + +finally: + MEM_freeN(verts); + return len ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } void MESH_OT_vert_connect(wmOperatorType *ot) -- cgit v1.2.3