From 415d3ee05bfa51d7a71fa8417682e38186915776 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 11 Jul 2020 22:03:27 +1000 Subject: UV: add path select operator that uses the selection Instead of using the mouse cursor position, this selects between existing selected elements. Access this since picking a selection path doesn't work from the menu. --- source/blender/editors/include/ED_uvedit.h | 13 +++ source/blender/editors/uvedit/uvedit_intern.h | 1 + source/blender/editors/uvedit/uvedit_ops.c | 1 + source/blender/editors/uvedit/uvedit_path.c | 92 ++++++++++++++++ source/blender/editors/uvedit/uvedit_select.c | 151 ++++++++++++++++++++++++++ 5 files changed, 258 insertions(+) (limited to 'source/blender') diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h index cce788efa58..ec41e785714 100644 --- a/source/blender/editors/include/ED_uvedit.h +++ b/source/blender/editors/include/ED_uvedit.h @@ -175,6 +175,19 @@ bool ED_uvedit_nearest_uv_multi(const struct Scene *scene, float *dist_sq, float r_uv[2]); +struct BMFace **ED_uvedit_selected_faces(struct Scene *scene, + struct BMesh *bm, + int len_max, + int *r_faces_len); +struct BMLoop **ED_uvedit_selected_edges(struct Scene *scene, + struct BMesh *bm, + int len_max, + int *r_edges_len); +struct BMLoop **ED_uvedit_selected_verts(struct Scene *scene, + struct BMesh *bm, + int len_max, + int *r_verts_len); + void ED_uvedit_get_aspect(struct Object *obedit, float *r_aspx, float *r_aspy); void ED_uvedit_active_vert_loop_set(struct BMesh *bm, struct BMLoop *l); diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h index 8b99292029e..6a5f5162dff 100644 --- a/source/blender/editors/uvedit/uvedit_intern.h +++ b/source/blender/editors/uvedit/uvedit_intern.h @@ -111,6 +111,7 @@ void UV_OT_stitch(struct wmOperatorType *ot); /* uvedit_path.c */ void UV_OT_shortest_path_pick(struct wmOperatorType *ot); +void UV_OT_shortest_path_select(struct wmOperatorType *ot); /* uvedit_select.c */ diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 18278cf2541..6003d4d5d95 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -2090,6 +2090,7 @@ void ED_operatortypes_uvedit(void) WM_operatortype_append(UV_OT_rip); WM_operatortype_append(UV_OT_stitch); WM_operatortype_append(UV_OT_shortest_path_pick); + WM_operatortype_append(UV_OT_shortest_path_select); WM_operatortype_append(UV_OT_seams_from_islands); WM_operatortype_append(UV_OT_mark_seam); diff --git a/source/blender/editors/uvedit/uvedit_path.c b/source/blender/editors/uvedit/uvedit_path.c index 4f0c472158a..e9615b62523 100644 --- a/source/blender/editors/uvedit/uvedit_path.c +++ b/source/blender/editors/uvedit/uvedit_path.c @@ -677,3 +677,95 @@ void UV_OT_shortest_path_pick(wmOperatorType *ot) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Select Path Between Existing Selection + * \{ */ + +static int uv_shortest_path_select_exec(bContext *C, wmOperator *op) +{ + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Scene *scene = CTX_data_scene(C); + const ToolSettings *ts = scene->toolsettings; + bool found_valid_elements = false; + + float aspect_y; + { + Object *obedit = CTX_data_edit_object(C); + float aspx, aspy; + ED_uvedit_get_aspect(obedit, &aspx, &aspy); + aspect_y = aspx / aspy; + } + + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( + view_layer, CTX_wm_view3d(C), &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); + BMElem *ele_src = NULL, *ele_dst = NULL; + + /* Find 2x elements. */ + { + BMElem **ele_array = NULL; + int ele_array_len = 0; + if (ts->uv_selectmode & UV_SELECT_FACE) { + ele_array = (BMElem **)ED_uvedit_selected_faces(scene, bm, 3, &ele_array_len); + } + else if (ts->uv_selectmode & UV_SELECT_EDGE) { + ele_array = (BMElem **)ED_uvedit_selected_edges(scene, bm, 3, &ele_array_len); + } + else { + ele_array = (BMElem **)ED_uvedit_selected_verts(scene, bm, 3, &ele_array_len); + } + + if (ele_array_len == 2) { + ele_src = ele_array[0]; + ele_dst = ele_array[1]; + } + MEM_freeN(ele_array); + } + + if (ele_src && ele_dst) { + struct PathSelectParams op_params; + path_select_params_from_op(op, &op_params); + + uv_shortest_path_pick_ex( + scene, depsgraph, obedit, &op_params, ele_src, ele_dst, aspect_y, cd_loop_uv_offset); + + found_valid_elements = true; + } + } + MEM_freeN(objects); + + if (!found_valid_elements) { + BKE_report( + op->reports, RPT_WARNING, "Path selection requires two matching elements to be selected"); + return OPERATOR_CANCELLED; + } + + return OPERATOR_FINISHED; +} + +void UV_OT_shortest_path_select(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Shortest Path"; + ot->idname = "UV_OT_shortest_path_select"; + ot->description = "Selected shortest path between two vertices/edges/faces"; + + /* api callbacks */ + ot->exec = uv_shortest_path_select_exec; + ot->poll = ED_operator_editmesh; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + path_select_properties(ot); +} + +/** \} */ diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index a589114fd46..bae097a09bf 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -3419,3 +3419,154 @@ void UV_OT_select_overlap(wmOperatorType *ot) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Selected Elements as Arrays (Vertex, Edge & Faces) + * + * These functions return single elements per connected vertex/edge. + * So an edge that has two connected edge loops only assigns one loop in the array. + * \{ */ + +BMFace **ED_uvedit_selected_faces(Scene *scene, BMesh *bm, int len_max, int *r_faces_len) +{ + const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); + CLAMP_MAX(len_max, bm->totface); + int faces_len = 0; + BMFace **faces = MEM_mallocN(sizeof(*faces) * len_max, __func__); + + BMIter iter; + BMFace *f; + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + if (uvedit_face_visible_test(scene, f)) { + if (uvedit_face_select_test(scene, f, cd_loop_uv_offset)) { + faces[faces_len++] = f; + if (faces_len == len_max) { + goto finally; + } + } + } + } + +finally: + *r_faces_len = faces_len; + if (faces_len != len_max) { + faces = MEM_reallocN(faces, sizeof(*faces) * faces_len); + } + return faces; +} + +BMLoop **ED_uvedit_selected_edges(Scene *scene, BMesh *bm, int len_max, int *r_edges_len) +{ + const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); + CLAMP_MAX(len_max, bm->totloop); + int edges_len = 0; + BMLoop **edges = MEM_mallocN(sizeof(*edges) * len_max, __func__); + + BMIter iter; + BMFace *f; + + /* Clear tag. */ + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + BMIter liter; + BMLoop *l_iter; + BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) { + BM_elem_flag_disable(l_iter, BM_ELEM_TAG); + } + } + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + if (uvedit_face_visible_test(scene, f)) { + BMIter liter; + BMLoop *l_iter; + BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) { + if (!BM_elem_flag_test(l_iter, BM_ELEM_TAG)) { + const MLoopUV *luv_curr = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset); + const MLoopUV *luv_next = BM_ELEM_CD_GET_VOID_P(l_iter->next, cd_loop_uv_offset); + if ((luv_curr->flag & MLOOPUV_VERTSEL) && (luv_next->flag & MLOOPUV_VERTSEL)) { + BM_elem_flag_enable(l_iter, BM_ELEM_TAG); + + edges[edges_len++] = l_iter; + if (edges_len == len_max) { + goto finally; + } + + /* Tag other connected loops so we don't consider them separate edges. */ + if (l_iter != l_iter->radial_next) { + BMLoop *l_radial_iter = l_iter->radial_next; + do { + if (BM_loop_uv_share_edge_check(l_iter, l_radial_iter, cd_loop_uv_offset)) { + BM_elem_flag_enable(l_radial_iter, BM_ELEM_TAG); + } + } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter); + } + } + } + } + } + } + +finally: + *r_edges_len = edges_len; + if (edges_len != len_max) { + edges = MEM_reallocN(edges, sizeof(*edges) * edges_len); + } + return edges; +} + +BMLoop **ED_uvedit_selected_verts(Scene *scene, BMesh *bm, int len_max, int *r_verts_len) +{ + const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); + CLAMP_MAX(len_max, bm->totloop); + int verts_len = 0; + BMLoop **verts = MEM_mallocN(sizeof(*verts) * len_max, __func__); + + BMIter iter; + BMFace *f; + + /* Clear tag. */ + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + BMIter liter; + BMLoop *l_iter; + BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) { + BM_elem_flag_disable(l_iter, BM_ELEM_TAG); + } + } + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + if (uvedit_face_visible_test(scene, f)) { + BMIter liter; + BMLoop *l_iter; + BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) { + if (!BM_elem_flag_test(l_iter, BM_ELEM_TAG)) { + const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset); + if ((luv->flag & MLOOPUV_VERTSEL)) { + BM_elem_flag_enable(l_iter->v, BM_ELEM_TAG); + + verts[verts_len++] = l_iter; + if (verts_len == len_max) { + goto finally; + } + + /* Tag other connected loops so we don't consider them separate vertices. */ + BMIter liter_disk; + BMLoop *l_disk_iter; + BM_ITER_ELEM (l_disk_iter, &liter_disk, l_iter->v, BM_LOOPS_OF_VERT) { + if (BM_loop_uv_share_vert_check(l_iter, l_disk_iter, cd_loop_uv_offset)) { + BM_elem_flag_enable(l_disk_iter, BM_ELEM_TAG); + } + } + } + } + } + } + } + +finally: + *r_verts_len = verts_len; + if (verts_len != len_max) { + verts = MEM_reallocN(verts, sizeof(*verts) * verts_len); + } + return verts; +} + +/** \} */ -- cgit v1.2.3