From cb261c96f56b0b6d42caec77a6be2e7a94f75e6d Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 18 Jan 2021 18:24:56 +1100 Subject: UV: tweak face select behavior When the mouse cursor is inside the UV face, extend the selection threshold. This means when zoomed in, a face can always be selected when the cursor is inside it. In the case of multiple overlapping faces - the face with the closest center is used. --- source/blender/bmesh/intern/bmesh_query_uv.c | 19 ++++++++++++ source/blender/bmesh/intern/bmesh_query_uv.h | 5 ++++ source/blender/editors/uvedit/uvedit_intern.h | 11 +++++++ source/blender/editors/uvedit/uvedit_select.c | 43 +++++++++++++++++++++++---- 4 files changed, 73 insertions(+), 5 deletions(-) diff --git a/source/blender/bmesh/intern/bmesh_query_uv.c b/source/blender/bmesh/intern/bmesh_query_uv.c index d3067109d4e..f9b87e4e71c 100644 --- a/source/blender/bmesh/intern/bmesh_query_uv.c +++ b/source/blender/bmesh/intern/bmesh_query_uv.c @@ -203,3 +203,22 @@ bool BM_edge_uv_share_vert_check(BMEdge *e, BMLoop *l_a, BMLoop *l_b, const int return true; } + +/** + * Check if the point is inside the UV face. + */ +bool BM_face_uv_point_inside_test(const BMFace *f, const float co[2], const int cd_loop_uv_offset) +{ + float(*projverts)[2] = BLI_array_alloca(projverts, f->len); + + BMLoop *l_iter; + int i; + + BLI_assert(BM_face_is_normal_valid(f)); + + for (i = 0, l_iter = BM_FACE_FIRST_LOOP(f); i < f->len; i++, l_iter = l_iter->next) { + copy_v2_v2(projverts[i], BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset)); + } + + return isect_point_poly_v2(co, projverts, f->len, false); +} diff --git a/source/blender/bmesh/intern/bmesh_query_uv.h b/source/blender/bmesh/intern/bmesh_query_uv.h index fe62c5a8809..850b27d3894 100644 --- a/source/blender/bmesh/intern/bmesh_query_uv.h +++ b/source/blender/bmesh/intern/bmesh_query_uv.h @@ -58,3 +58,8 @@ bool BM_loop_uv_share_vert_check(BMLoop *l_a, BMLoop *l_b, const int cd_loop_uv_offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(); + +bool BM_face_uv_point_inside_test(const BMFace *f, + const float co[2], + const int cd_loop_uv_offset) ATTR_WARN_UNUSED_RESULT + ATTR_NONNULL(); diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h index 45d65e7070e..3785f7819e3 100644 --- a/source/blender/editors/uvedit/uvedit_intern.h +++ b/source/blender/editors/uvedit/uvedit_intern.h @@ -93,10 +93,21 @@ bool uv_find_nearest_edge_multi(struct Scene *scene, const float co[2], struct UvNearestHit *hit); +bool uv_find_nearest_face_ex(struct Scene *scene, + struct Object *obedit, + const float co[2], + struct UvNearestHit *hit, + struct UvNearestHit *hit_in_face); bool uv_find_nearest_face(struct Scene *scene, struct Object *obedit, const float co[2], struct UvNearestHit *hit); +bool uv_find_nearest_face_multi_ex(struct Scene *scene, + struct Object **objects, + const uint objects_len, + const float co[2], + struct UvNearestHit *hit, + struct UvNearestHit *hit_in_face); bool uv_find_nearest_face_multi(struct Scene *scene, struct Object **objects, const uint objects_len, diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index 1cbd2553879..e228011bc57 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -721,7 +721,8 @@ bool uv_find_nearest_edge_multi( return found; } -bool uv_find_nearest_face(Scene *scene, Object *obedit, const float co[2], UvNearestHit *hit) +bool uv_find_nearest_face_ex( + Scene *scene, Object *obedit, const float co[2], UvNearestHit *hit, UvNearestHit *hit_in_face) { BLI_assert((hit->scale[0] > 0.0f) && (hit->scale[1] > 0.0f)); BMEditMesh *em = BKE_editmesh_from_object(obedit); @@ -752,23 +753,49 @@ bool uv_find_nearest_face(Scene *scene, Object *obedit, const float co[2], UvNea hit->dist_sq = dist_test_sq; found = true; } + + if (hit_in_face != NULL) { + if (dist_test_sq < hit_in_face->dist_sq) { + if (BM_face_uv_point_inside_test(efa, co, cd_loop_uv_offset)) { + hit_in_face->ob = obedit; + hit_in_face->efa = efa; + hit_in_face->dist_sq = dist_test_sq; + found = true; + } + } + } } return found; } -bool uv_find_nearest_face_multi( - Scene *scene, Object **objects, const uint objects_len, const float co[2], UvNearestHit *hit) +bool uv_find_nearest_face(Scene *scene, Object *obedit, const float co[2], UvNearestHit *hit) +{ + return uv_find_nearest_face_ex(scene, obedit, co, hit, NULL); +} + +bool uv_find_nearest_face_multi_ex(Scene *scene, + Object **objects, + const uint objects_len, + const float co[2], + UvNearestHit *hit, + UvNearestHit *hit_in_face) { bool found = false; for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *obedit = objects[ob_index]; - if (uv_find_nearest_face(scene, obedit, co, hit)) { + if (uv_find_nearest_face_ex(scene, obedit, co, hit, hit_in_face)) { found = true; } } return found; } +bool uv_find_nearest_face_multi( + Scene *scene, Object **objects, const uint objects_len, const float co[2], UvNearestHit *hit) +{ + return uv_find_nearest_face_multi_ex(scene, objects, objects_len, co, hit, NULL); +} + static bool uv_nearest_between(const BMLoop *l, const float co[2], const int cd_loop_uv_offset) { const float *uv_prev = ((MLoopUV *)BM_ELEM_CD_GET_VOID_P(l->prev, cd_loop_uv_offset))->uv; @@ -1935,8 +1962,14 @@ static int uv_mouse_select_multi(bContext *C, } else if (selectmode == UV_SELECT_FACE) { /* find face */ - found_item = uv_find_nearest_face_multi(scene, objects, objects_len, co, &hit); + UvNearestHit hit_in_face = UV_NEAREST_HIT_INIT_MAX(®ion->v2d); + found_item = uv_find_nearest_face_multi_ex( + scene, objects, objects_len, co, &hit, &hit_in_face); if (found_item) { + if (hit.ob == NULL) { + hit = hit_in_face; + printf("Using hit in face\n"); + } BMesh *bm = BKE_editmesh_from_object(hit.ob)->bm; BM_mesh_active_face_set(bm, hit.efa); } -- cgit v1.2.3