UV: internal changes to picking
authorCampbell Barton <ideasman42@gmail.com>
Mon, 12 Mar 2018 02:46:25 +0000 (13:46 +1100)
committerCampbell Barton <ideasman42@gmail.com>
Mon, 12 Mar 2018 02:57:38 +0000 (13:57 +1100)
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/sculpt_paint/sculpt_uv.c
source/blender/editors/uvedit/uvedit_intern.h
source/blender/editors/uvedit/uvedit_ops.c
source/blender/editors/uvedit/uvedit_smart_stitch.c

index 5ef9d4a..7b63653 100644 (file)
@@ -650,9 +650,9 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm
                /* we need to find the active island here */
                if (do_island_optimization) {
                        UvElement *element;
-                       NearestHit hit;
+                       UvNearestHit hit = UV_NEAREST_HIT_INIT;
                        Image *ima = CTX_data_edit_image(C);
-                       uv_find_nearest_vert(scene, ima, em, co, NULL, &hit);
+                       uv_find_nearest_vert(scene, ima, em, co, 0.0f, &hit);
 
                        element = BM_uv_element_get(data->elementMap, hit.efa, hit.l);
                        island_index = element->island;
index e028c08..b5ff46e 100644 (file)
@@ -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 */
 
index bf5a06f..1ee1026 100644 (file)
@@ -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);
index da36170..057417b 100644 (file)
@@ -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);
                }