From 1966924467c901d541bfe3f510d21a20c29842e3 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 12 Mar 2018 13:46:25 +1100 Subject: UV: internal changes to picking Nothing user visible, only things needed for multi-object support, making picking functions more flexible too. - Support passing in an initialized hit-struct, so it's possible to do multiple nearest calls on the same hit data. - Replace manhattan distance w/ squared distance so they can be compared. - Return success to detect changes to a hit-data which might already be initialized (also more readable). --- source/blender/editors/uvedit/uvedit_intern.h | 27 ++- source/blender/editors/uvedit/uvedit_ops.c | 228 ++++++++++++--------- .../blender/editors/uvedit/uvedit_smart_stitch.c | 14 +- 3 files changed, 154 insertions(+), 115 deletions(-) (limited to 'source/blender/editors/uvedit') diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h index e028c08091c..b5ff46e9219 100644 --- a/source/blender/editors/uvedit/uvedit_intern.h +++ b/source/blender/editors/uvedit/uvedit_intern.h @@ -51,18 +51,31 @@ void uv_poly_center(struct BMFace *f, float r_cent[2], const int cd_loop_uv_off /* find nearest */ -typedef struct NearestHit { +typedef struct UvNearestHit { + /** Always set if we have a hit. */ struct BMFace *efa; struct MTexPoly *tf; struct BMLoop *l; struct MLoopUV *luv, *luv_next; - int lindex; /* index of loop within face */ -} NearestHit; + /** Index of loop within face. */ + int lindex; + /** Needs to be set before calling nearest functions. */ + float dist_sq; +} UvNearestHit; -void uv_find_nearest_vert(struct Scene *scene, struct Image *ima, struct BMEditMesh *em, - const float co[2], const float penalty[2], struct NearestHit *hit); -void uv_find_nearest_edge(struct Scene *scene, struct Image *ima, struct BMEditMesh *em, - const float co[2], struct NearestHit *hit); +#define UV_NEAREST_HIT_INIT { .dist_sq = FLT_MAX, } + +bool uv_find_nearest_vert( + struct Scene *scene, struct Image *ima, struct BMEditMesh *em, + const float co[2], const float penalty_dist, struct UvNearestHit *hit_final); + +bool uv_find_nearest_edge( + struct Scene *scene, struct Image *ima, struct BMEditMesh *em, + const float co[2], struct UvNearestHit *hit_final); + +bool uv_find_nearest_face( + struct Scene *scene, struct Image *ima, struct BMEditMesh *em, + const float co[2], struct UvNearestHit *hit_final); /* utility tool functions */ diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index bf5a06f0a1c..1ee10268be5 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -760,83 +760,93 @@ static bool uvedit_center(Scene *scene, Image *ima, Object *obedit, float cent[2 /************************** find nearest ****************************/ -void uv_find_nearest_edge(Scene *scene, Image *ima, BMEditMesh *em, const float co[2], NearestHit *hit) +bool uv_find_nearest_edge( + Scene *scene, Image *ima, BMEditMesh *em, const float co[2], + UvNearestHit *hit) { MTexPoly *tf; BMFace *efa; BMLoop *l; BMIter iter, liter; MLoopUV *luv, *luv_next; - float mindist_squared, dist_squared; int i; + bool found = false; const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY); - mindist_squared = 1e10f; - memset(hit, 0, sizeof(*hit)); - BM_mesh_elem_index_ensure(em->bm, BM_VERT); - + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); - if (!uvedit_face_visible_test(scene, ima, efa, tf)) + if (!uvedit_face_visible_test(scene, ima, efa, tf)) { continue; - + } BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) { luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset); - dist_squared = dist_squared_to_line_segment_v2(co, luv->uv, luv_next->uv); + const float dist_test_sq = dist_squared_to_line_segment_v2(co, luv->uv, luv_next->uv); - if (dist_squared < mindist_squared) { + if (dist_test_sq < hit->dist_sq) { hit->tf = tf; hit->efa = efa; - + hit->l = l; hit->luv = luv; hit->luv_next = luv_next; hit->lindex = i; - mindist_squared = dist_squared; + hit->dist_sq = dist_test_sq; + found = true; } } } + return found; } -static void uv_find_nearest_face(Scene *scene, Image *ima, BMEditMesh *em, const float co[2], NearestHit *hit) +bool uv_find_nearest_face( + Scene *scene, Image *ima, BMEditMesh *em, const float co[2], + UvNearestHit *hit_final) { - MTexPoly *tf; - BMFace *efa; - BMIter iter; - float mindist, dist, cent[2]; + bool found = false; const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY); - mindist = 1e10f; - memset(hit, 0, sizeof(*hit)); + /* this will fill in hit.vert1 and hit.vert2 */ + float dist_sq_init = hit_final->dist_sq; + UvNearestHit hit = *hit_final; + if (uv_find_nearest_edge(scene, ima, em, co, &hit)) { + hit.dist_sq = dist_sq_init; + hit.l = NULL; + hit.luv = hit.luv_next = NULL; - /*this will fill in hit.vert1 and hit.vert2*/ - uv_find_nearest_edge(scene, ima, em, co, hit); - hit->l = NULL; - hit->luv = hit->luv_next = NULL; + BMIter iter; + BMFace *efa; - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); - if (!uvedit_face_visible_test(scene, ima, efa, tf)) - continue; + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + MTexPoly *tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); + if (!uvedit_face_visible_test(scene, ima, efa, tf)) { + continue; + } - uv_poly_center(efa, cent, cd_loop_uv_offset); + float cent[2]; + uv_poly_center(efa, cent, cd_loop_uv_offset); - dist = len_manhattan_v2v2(co, cent); + const float dist_test_sq = len_squared_v2v2(co, cent); - if (dist < mindist) { - hit->tf = tf; - hit->efa = efa; - mindist = dist; + if (dist_test_sq < hit.dist_sq) { + hit.efa = efa; + hit.dist_sq = dist_test_sq; + found = true; + } } } + if (found) { + *hit_final = hit; + } + return found; } static bool uv_nearest_between(const BMLoop *l, const float co[2], @@ -850,60 +860,74 @@ static bool uv_nearest_between(const BMLoop *l, const float co[2], (line_point_side_v2(uv_next, uv_curr, co) <= 0.0f)); } -void uv_find_nearest_vert(Scene *scene, Image *ima, BMEditMesh *em, - float const co[2], const float penalty[2], NearestHit *hit) +bool uv_find_nearest_vert( + Scene *scene, Image *ima, BMEditMesh *em, + float const co[2], const float penalty_dist, UvNearestHit *hit_final) { - BMFace *efa; - BMLoop *l; - BMIter iter, liter; - MTexPoly *tf; - MLoopUV *luv; - float mindist, dist; - int i; + bool found = false; - const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY); + /* this will fill in hit.vert1 and hit.vert2 */ + float dist_sq_init = hit_final->dist_sq; + UvNearestHit hit = *hit_final; + if (uv_find_nearest_edge(scene, ima, em, co, &hit)) { + hit.dist_sq = dist_sq_init; - /*this will fill in hit.vert1 and hit.vert2*/ - uv_find_nearest_edge(scene, ima, em, co, hit); - hit->l = NULL; - hit->luv = hit->luv_next = NULL; + hit.l = NULL; + hit.luv = hit.luv_next = NULL; - mindist = 1e10f; - memset(hit, 0, sizeof(*hit)); - - BM_mesh_elem_index_ensure(em->bm, BM_VERT); + BMFace *efa; + BMIter iter; - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); - if (!uvedit_face_visible_test(scene, ima, efa, tf)) - continue; + BM_mesh_elem_index_ensure(em->bm, BM_VERT); - BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - if (penalty && uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) - dist = len_manhattan_v2v2(co, luv->uv) + len_manhattan_v2(penalty); - else - dist = len_manhattan_v2v2(co, luv->uv); + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY); - if (dist <= mindist) { - if (dist == mindist) { - if (!uv_nearest_between(l, co, cd_loop_uv_offset)) { - continue; - } + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + MTexPoly *tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); + if (!uvedit_face_visible_test(scene, ima, efa, tf)) { + continue; + } + + BMIter liter; + BMLoop *l; + int i; + BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) { + float dist_test_sq; + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (penalty_dist != 0.0f && uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { + dist_test_sq = len_v2v2(co, luv->uv) + penalty_dist; + dist_test_sq = SQUARE(dist_test_sq); + } + else { + dist_test_sq = len_squared_v2v2(co, luv->uv); } - mindist = dist; + if (dist_test_sq <= hit.dist_sq) { + if (dist_test_sq == hit.dist_sq) { + if (!uv_nearest_between(l, co, cd_loop_uv_offset)) { + continue; + } + } - hit->l = l; - hit->luv = luv; - hit->luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset); - hit->tf = tf; - hit->efa = efa; - hit->lindex = i; + hit.dist_sq = dist_test_sq; + + hit.l = l; + hit.luv = luv; + hit.luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset); + hit.efa = efa; + hit.lindex = i; + found = true; + } } } } + + if (found) { + *hit_final = hit; + } + + return found; } bool ED_uvedit_nearest_uv(Scene *scene, Object *obedit, Image *ima, const float co[2], float r_uv[2]) @@ -1029,8 +1053,9 @@ static bool uv_select_edgeloop_edge_tag_faces(BMEditMesh *em, UvMapVert *first1, return true; } -static int uv_select_edgeloop(Scene *scene, Image *ima, BMEditMesh *em, NearestHit *hit, - const float limit[2], const bool extend) +static int uv_select_edgeloop( + Scene *scene, Image *ima, BMEditMesh *em, UvNearestHit *hit, + const float limit[2], const bool extend) { BMFace *efa; BMIter iter, liter; @@ -1133,12 +1158,13 @@ static int uv_select_edgeloop(Scene *scene, Image *ima, BMEditMesh *em, NearestH /*********************** linked select ***********************/ -static void uv_select_linked(Scene *scene, Image *ima, BMEditMesh *em, const float limit[2], NearestHit *hit, bool extend, bool select_faces) +static void uv_select_linked( + Scene *scene, Image *ima, BMEditMesh *em, const float limit[2], UvNearestHit *hit_final, + bool extend, bool select_faces) { BMFace *efa; BMLoop *l; BMIter iter, liter; - MTexPoly *tf; MLoopUV *luv; UvVertMap *vmap; UvMapVert *vlist, *iterv, *startv; @@ -1165,9 +1191,10 @@ static void uv_select_linked(Scene *scene, Image *ima, BMEditMesh *em, const flo stack = MEM_mallocN(sizeof(*stack) * (em->bm->totface + 1), "UvLinkStack"); flag = MEM_callocN(sizeof(*flag) * em->bm->totface, "UvLinkFlag"); - if (!hit) { + if (hit_final == NULL) { + /* Use existing selection */ BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, a) { - tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); + MTexPoly *tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); if (uvedit_face_visible_test(scene, ima, efa, tf)) { if (select_faces) { @@ -1195,7 +1222,7 @@ static void uv_select_linked(Scene *scene, Image *ima, BMEditMesh *em, const flo } else { BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, a) { - if (efa == hit->efa) { + if (efa == hit_final->efa) { stack[stacksize] = a; stacksize++; flag[a] = 1; @@ -2084,12 +2111,11 @@ static int uv_mouse_select(bContext *C, const float co[2], bool extend, bool loo BMIter iter, liter; MTexPoly *tf; MLoopUV *luv; - NearestHit hit; + UvNearestHit hit = UV_NEAREST_HIT_INIT; int i, selectmode, sticky, sync, *hitv = NULL; bool select = true; int flush = 0, hitlen = 0; /* 0 == don't flush, 1 == sel, -1 == desel; only use when selection sync is enabled */ float limit[2], **hituv = NULL; - float penalty[2]; const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY); @@ -2100,8 +2126,13 @@ static int uv_mouse_select(bContext *C, const float co[2], bool extend, bool loo * shift-selecting can consider an adjacent point close enough to add to * the selection rather than de-selecting the closest. */ - uvedit_pixel_to_float(sima, limit, 0.05f); - uvedit_pixel_to_float(sima, penalty, 5.0f / (sima ? sima->zoom : 1.0f)); + float penalty_dist; + { + float penalty[2]; + uvedit_pixel_to_float(sima, limit, 0.05f); + uvedit_pixel_to_float(sima, penalty, 5.0f / (sima ? sima->zoom : 1.0f)); + penalty_dist = len_v2(penalty); + } /* retrieve operation mode */ if (ts->uv_flag & UV_SYNC_SELECTION) { @@ -2125,8 +2156,7 @@ static int uv_mouse_select(bContext *C, const float co[2], bool extend, bool loo /* find nearest element */ if (loop) { /* find edge */ - uv_find_nearest_edge(scene, ima, em, co, &hit); - if (hit.efa == NULL) { + if (!uv_find_nearest_edge(scene, ima, em, co, &hit)) { return OPERATOR_CANCELLED; } @@ -2134,8 +2164,7 @@ static int uv_mouse_select(bContext *C, const float co[2], bool extend, bool loo } else if (selectmode == UV_SELECT_VERTEX) { /* find vertex */ - uv_find_nearest_vert(scene, ima, em, co, penalty, &hit); - if (hit.efa == NULL) { + if (!uv_find_nearest_vert(scene, ima, em, co, penalty_dist, &hit)) { return OPERATOR_CANCELLED; } @@ -2151,8 +2180,7 @@ static int uv_mouse_select(bContext *C, const float co[2], bool extend, bool loo } else if (selectmode == UV_SELECT_EDGE) { /* find edge */ - uv_find_nearest_edge(scene, ima, em, co, &hit); - if (hit.efa == NULL) { + if (!uv_find_nearest_edge(scene, ima, em, co, &hit)) { return OPERATOR_CANCELLED; } @@ -2170,11 +2198,10 @@ static int uv_mouse_select(bContext *C, const float co[2], bool extend, bool loo } else if (selectmode == UV_SELECT_FACE) { /* find face */ - uv_find_nearest_face(scene, ima, em, co, &hit); - if (hit.efa == NULL) { + if (!uv_find_nearest_face(scene, ima, em, co, &hit)) { return OPERATOR_CANCELLED; } - + /* make active */ BM_mesh_active_face_set(em->bm, hit.efa); @@ -2191,9 +2218,7 @@ static int uv_mouse_select(bContext *C, const float co[2], bool extend, bool loo hitlen = hit.efa->len; } else if (selectmode == UV_SELECT_ISLAND) { - uv_find_nearest_edge(scene, ima, em, co, &hit); - - if (hit.efa == NULL) { + if (!uv_find_nearest_edge(scene, ima, em, co, &hit)) { return OPERATOR_CANCELLED; } @@ -2432,7 +2457,7 @@ static int uv_select_linked_internal(bContext *C, wmOperator *op, const wmEvent int extend; bool select_faces = (ts->uv_flag & UV_SYNC_SELECTION) && (ts->selectmode & SCE_SELECT_FACE); - NearestHit hit, *hit_p = NULL; + UvNearestHit hit = UV_NEAREST_HIT_INIT; if ((ts->uv_flag & UV_SYNC_SELECTION) && !(ts->selectmode & SCE_SELECT_FACE)) { BKE_report(op->reports, RPT_ERROR, "Select linked only works in face select mode when sync selection is enabled"); @@ -2457,11 +2482,12 @@ static int uv_select_linked_internal(bContext *C, wmOperator *op, const wmEvent RNA_float_get_array(op->ptr, "location", co); } - uv_find_nearest_edge(scene, ima, em, co, &hit); - hit_p = &hit; + if (!uv_find_nearest_edge(scene, ima, em, co, &hit)) { + return OPERATOR_CANCELLED; + } } - uv_select_linked(scene, ima, em, limit, hit_p, extend, select_faces); + uv_select_linked(scene, ima, em, limit, pick ? &hit : NULL, extend, select_faces); DAG_id_tag_update(obedit->data, 0); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c index da36170310a..057417b898f 100644 --- a/source/blender/editors/uvedit/uvedit_smart_stitch.c +++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c @@ -2058,16 +2058,16 @@ static void stitch_select(bContext *C, Scene *scene, const wmEvent *event, Stitc { /* add uv under mouse to processed uv's */ float co[2]; - NearestHit hit; + UvNearestHit hit = UV_NEAREST_HIT_INIT; ARegion *ar = CTX_wm_region(C); Image *ima = CTX_data_edit_image(C); UI_view2d_region_to_view(&ar->v2d, event->mval[0], event->mval[1], &co[0], &co[1]); if (state->mode == STITCH_VERT) { - uv_find_nearest_vert(scene, ima, state->em, co, NULL, &hit); - - if (hit.efa) { + if (uv_find_nearest_vert( + scene, ima, state->em, co, 0.0f, &hit)) + { /* Add vertex to selection, deselect all common uv's of vert other * than selected and update the preview. This behavior was decided so that * you can do stuff like deselect the opposite stitchable vertex and the initial still gets deselected */ @@ -2079,9 +2079,9 @@ static void stitch_select(bContext *C, Scene *scene, const wmEvent *event, Stitc } } else { - uv_find_nearest_edge(scene, ima, state->em, co, &hit); - - if (hit.efa) { + if (uv_find_nearest_edge( + scene, ima, state->em, co, &hit)) + { UvEdge *edge = uv_edge_get(hit.l, state); stitch_select_edge(edge, state, false); } -- cgit v1.2.3