diff options
author | Siddhartha Jejurkar <f20180617@goa.bits-pilani.ac.in> | 2022-07-22 03:47:28 +0300 |
---|---|---|
committer | Campbell Barton <campbell@blender.org> | 2022-07-22 04:17:16 +0300 |
commit | 77257405437336dbd91a481926013f8c747cacae (patch) | |
tree | fc51a0b5668acb3259206444a9f8321d27f8f5f2 /source | |
parent | aa1ffc093c4711a40932c854670730944008118b (diff) |
UV: Edge support for select shortest path operator
Calculating shortest path selection in UV edge mode was done using vertex
path logic. Since the UV editor now supports proper edge selection [0],
this approach can sometimes give incorrect results.
This problem is now fixed by adding separate logic to calculate the
shortest path in UV edge mode.
Resolves T99344.
[0]: ffaaa0bcbf477c30cf3665b9330bbbb767397169
Reviewed By: campbellbarton
Ref D15511.
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/bmesh/tools/bmesh_path_uv.c | 203 | ||||
-rw-r--r-- | source/blender/bmesh/tools/bmesh_path_uv.h | 8 | ||||
-rw-r--r-- | source/blender/editors/uvedit/uvedit_path.c | 299 |
3 files changed, 351 insertions, 159 deletions
diff --git a/source/blender/bmesh/tools/bmesh_path_uv.c b/source/blender/bmesh/tools/bmesh_path_uv.c index 76697f51ac7..3d736cdc3b8 100644 --- a/source/blender/bmesh/tools/bmesh_path_uv.c +++ b/source/blender/bmesh/tools/bmesh_path_uv.c @@ -47,9 +47,7 @@ static float step_cost_3_v2_ex( return cost * (1.0f + 0.5f * (2.0f - sqrtf(fabsf(dot_v2v2(d1, d2))))); } -static float UNUSED_FUNCTION(step_cost_3_v2)(const float v1[2], - const float v2[2], - const float v3[2]) +static float step_cost_3_v2(const float v1[2], const float v2[2], const float v3[2]) { return step_cost_3_v2_ex(v1, v2, v3, false, false); } @@ -60,7 +58,7 @@ static float UNUSED_FUNCTION(step_cost_3_v2)(const float v1[2], /** \name BM_mesh_calc_path_uv_vert * \{ */ -static void looptag_add_adjacent_uv(HeapSimple *heap, +static void verttag_add_adjacent_uv(HeapSimple *heap, BMLoop *l_a, BMLoop **loops_prev, float *cost, @@ -162,7 +160,7 @@ struct LinkNode *BM_mesh_calc_path_uv_vert(BMesh *bm, if (!BM_elem_flag_test(l, BM_ELEM_TAG)) { /* Adjacent loops are tagged while stepping to avoid 2x loops. */ BM_elem_flag_enable(l, BM_ELEM_TAG); - looptag_add_adjacent_uv(heap, l, loops_prev, cost, params); + verttag_add_adjacent_uv(heap, l, loops_prev, cost, params); } } @@ -185,8 +183,199 @@ struct LinkNode *BM_mesh_calc_path_uv_vert(BMesh *bm, /** \name BM_mesh_calc_path_uv_edge * \{ */ -/* TODO(@sidd017): Setting this as todo, since we now support proper UV edge selection (D12028). - * Till then, continue using vertex path to fake shortest path calculation for edges. */ +static float edgetag_cut_cost_vert_uv( + BMLoop *l_e_a, BMLoop *l_e_b, BMLoop *l_v, const float aspect_y, const int cd_loop_uv_offset) +{ + BMLoop *l_v1 = (l_v->v == l_e_a->v) ? l_e_a->next : l_e_a; + BMLoop *l_v2 = (l_v->v == l_e_b->v) ? l_e_b->next : l_e_b; + + MLoopUV *luv_v1 = BM_ELEM_CD_GET_VOID_P(l_v1, cd_loop_uv_offset); + MLoopUV *luv_v2 = BM_ELEM_CD_GET_VOID_P(l_v2, cd_loop_uv_offset); + MLoopUV *luv_v = BM_ELEM_CD_GET_VOID_P(l_v, cd_loop_uv_offset); + + float uv_v1[2] = {luv_v1->uv[0], luv_v1->uv[1] / aspect_y}; + float uv_v2[2] = {luv_v2->uv[0], luv_v2->uv[1] / aspect_y}; + float uv_v[2] = {luv_v->uv[0], luv_v->uv[1] / aspect_y}; + + return step_cost_3_v2(uv_v1, uv_v, uv_v2); +} + +static float edgetag_cut_cost_face_uv( + BMLoop *l_e_a, BMLoop *l_e_b, BMFace *f, const float aspect_v2[2], const int cd_loop_uv_offset) +{ + float l_e_a_cent[2], l_e_b_cent[2], f_cent[2]; + MLoopUV *luv_e_a = BM_ELEM_CD_GET_VOID_P(l_e_a, cd_loop_uv_offset); + MLoopUV *luv_e_b = BM_ELEM_CD_GET_VOID_P(l_e_b, cd_loop_uv_offset); + + mid_v2_v2v2(l_e_a_cent, luv_e_a->uv, luv_e_a->uv); + mid_v2_v2v2(l_e_b_cent, luv_e_b->uv, luv_e_b->uv); + + mul_v2_v2(l_e_a_cent, aspect_v2); + mul_v2_v2(l_e_b_cent, aspect_v2); + + BM_face_uv_calc_center_median_weighted(f, aspect_v2, cd_loop_uv_offset, f_cent); + + return step_cost_3_v2(l_e_a_cent, l_e_b_cent, f_cent); +} + +static void edgetag_add_adjacent_uv(HeapSimple *heap, + BMLoop *l_a, + BMLoop **loops_prev, + float *cost, + const struct BMCalcPathUVParams *params) +{ + BLI_assert(params->aspect_y != 0.0f); + const uint cd_loop_uv_offset = params->cd_loop_uv_offset; + BMLoop *l_a_verts[2] = {l_a, l_a->next}; + const int l_a_index = BM_elem_index_get(l_a); + + if (params->use_step_face == false) { + for (int i = 0; i < ARRAY_SIZE(l_a_verts); i++) { + + /* Skip current UV vert if it is part of the previous UV edge in the path. */ + if (loops_prev[l_a_index]) { + BMLoop *l_prev = loops_prev[l_a_index]; + if (l_a_verts[i]->v != l_prev->v) { + l_prev = (l_a_verts[i]->v == l_prev->next->v) ? l_prev->next : NULL; + } + if (l_prev && BM_loop_uv_share_vert_check(l_a_verts[i], l_prev, cd_loop_uv_offset)) { + continue; + } + } + + BMEdge *e_b; + BMIter eiter; + BM_ITER_ELEM (e_b, &eiter, l_a_verts[i]->v, BM_EDGES_OF_VERT) { + BMLoop *l_first, *l_b; + l_first = l_b = e_b->l; + do { + if (!BM_elem_flag_test(l_b, BM_ELEM_TAG)) { + BMLoop *l_b_vert = (l_a_verts[i]->v == l_b->v) ? l_b : l_b->next; + if (BM_loop_uv_share_vert_check(l_a_verts[i], l_b_vert, cd_loop_uv_offset)) { + /* We know 'l_b' is not visited, check it out! */ + const int l_b_index = BM_elem_index_get(l_b); + const float cost_cut = params->use_topology_distance ? + 1.0f : + edgetag_cut_cost_vert_uv(l_a, + l_b, + l_a_verts[i], + params->aspect_y, + cd_loop_uv_offset); + const float cost_new = cost[l_a_index] + cost_cut; + + if (cost[l_b_index] > cost_new) { + cost[l_b_index] = cost_new; + loops_prev[l_b_index] = l_a; + BLI_heapsimple_insert(heap, cost_new, l_b); + } + } + } + } while ((l_b = l_b->radial_next) != l_first); + } + } + } + else { + const float aspect_v2[2] = {1.0f, 1.0f / params->aspect_y}; + BMLoop *l_first, *l_iter; + l_iter = l_first = l_a; + do { + /* Ensures connected UVs and that they lie on the same island. */ + if (!BM_loop_uv_share_edge_check(l_a, l_iter, cd_loop_uv_offset)) { + continue; + } + + BMLoop *l_cycle_iter, *l_cycle_end; + l_cycle_iter = l_iter->next; + l_cycle_end = l_iter; + do { + BMLoop *l_b = l_cycle_iter; + if (!BM_elem_flag_test(l_b, BM_ELEM_TAG)) { + /* We know 'l_b' is not visited, check it out! */ + const int l_b_index = BM_elem_index_get(l_b); + const float cost_cut = params->use_topology_distance ? + 1.0f : + edgetag_cut_cost_face_uv(l_a, + l_b, + l_iter->f, + aspect_v2, + params->cd_loop_uv_offset); + const float cost_new = cost[l_a_index] + cost_cut; + + if (cost[l_b_index] > cost_new) { + cost[l_b_index] = cost_new; + loops_prev[l_b_index] = l_a; + BLI_heapsimple_insert(heap, cost_new, l_b); + } + } + } while ((l_cycle_iter = l_cycle_iter->next) != l_cycle_end); + } while ((l_iter = l_iter->radial_next) != l_first); + } +} + +struct LinkNode *BM_mesh_calc_path_uv_edge(BMesh *bm, + BMLoop *l_src, + BMLoop *l_dst, + const struct BMCalcPathUVParams *params, + bool (*filter_fn)(BMLoop *, void *), + void *user_data) +{ + LinkNode *path = NULL; + + BMFace *f; + BMIter iter; + HeapSimple *heap; + float *cost; + BMLoop **loops_prev; + int i = 0, totloop; + + BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { + BMLoop *l_first = BM_FACE_FIRST_LOOP(f); + BMLoop *l_iter = l_first; + do { + BM_elem_flag_set(l_iter, BM_ELEM_TAG, !filter_fn(l_iter, user_data)); + BM_elem_index_set(l_iter, i); + i += 1; + } while ((l_iter = l_iter->next) != l_first); + } + bm->elem_index_dirty &= ~BM_LOOP; + + totloop = bm->totloop; + loops_prev = MEM_callocN(sizeof(*loops_prev) * totloop, __func__); + cost = MEM_mallocN(sizeof(*cost) * totloop, __func__); + + copy_vn_fl(cost, totloop, COST_INIT_MAX); + + /* Regular dijkstra shortest path, but over UV loops/edges instead of vertices. */ + heap = BLI_heapsimple_new(); + BLI_heapsimple_insert(heap, 0.0f, l_src); + cost[BM_elem_index_get(l_src)] = 0.0f; + + BMLoop *l = NULL; + while (!BLI_heapsimple_is_empty(heap)) { + l = BLI_heapsimple_pop_min(heap); + + if ((l->e == l_dst->e) && (BM_loop_uv_share_edge_check(l, l_dst, params->cd_loop_uv_offset))) { + break; + } + + if (!BM_elem_flag_test(l, BM_ELEM_TAG)) { + BM_elem_flag_enable(l, BM_ELEM_TAG); + edgetag_add_adjacent_uv(heap, l, loops_prev, cost, params); + } + } + + if ((l->e == l_dst->e) && (BM_loop_uv_share_edge_check(l, l_dst, params->cd_loop_uv_offset))) { + do { + BLI_linklist_prepend(&path, l); + } while ((l = loops_prev[BM_elem_index_get(l)])); + } + + MEM_freeN(loops_prev); + MEM_freeN(cost); + BLI_heapsimple_free(heap, NULL); + + return path; +} /** \} */ diff --git a/source/blender/bmesh/tools/bmesh_path_uv.h b/source/blender/bmesh/tools/bmesh_path_uv.h index af7341e2219..d7b5faa70e5 100644 --- a/source/blender/bmesh/tools/bmesh_path_uv.h +++ b/source/blender/bmesh/tools/bmesh_path_uv.h @@ -21,6 +21,14 @@ struct LinkNode *BM_mesh_calc_path_uv_vert(BMesh *bm, void *user_data) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 2, 3, 5); +struct LinkNode *BM_mesh_calc_path_uv_edge(BMesh *bm, + BMLoop *l_src, + BMLoop *l_dst, + const struct BMCalcPathUVParams *params, + bool (*filter_fn)(BMLoop *, void *), + void *user_data) ATTR_WARN_UNUSED_RESULT + ATTR_NONNULL(1, 2, 3, 5); + struct LinkNode *BM_mesh_calc_path_uv_face(BMesh *bm, BMFace *f_src, BMFace *f_dst, diff --git a/source/blender/editors/uvedit/uvedit_path.c b/source/blender/editors/uvedit/uvedit_path.c index 7c6960a634a..31a1b60167e 100644 --- a/source/blender/editors/uvedit/uvedit_path.c +++ b/source/blender/editors/uvedit/uvedit_path.c @@ -56,75 +56,6 @@ #include "bmesh_tools.h" /* -------------------------------------------------------------------- */ -/** \name Local Utilities - * \{ */ - -/** - * Support edge-path using vert-path calculation code. - * - * Cheat! Pick 2 closest loops and do vertex path, - * in practices only obscure/contrived cases will make give noticeably worse behavior. - * - * While the code below is a bit awkward, it's significantly less overhead than - * adding full edge selection which is nearly the same as vertex path in the case of UV's. - * - * \param use_nearest: When false use the post distant pair of loops, - * use when filling a region as we want both verts from each edge to be included in the region. - */ -static void bm_loop_calc_vert_pair_from_edge_pair(const bool use_nearest, - const int cd_loop_uv_offset, - const float aspect_y, - BMElem **ele_src_p, - BMElem **ele_dst_p, - BMElem **r_ele_dst_final) -{ - BMLoop *l_src = (BMLoop *)*ele_src_p; - BMLoop *l_dst = (BMLoop *)*ele_dst_p; - - const MLoopUV *luv_src_v1 = BM_ELEM_CD_GET_VOID_P(l_src, cd_loop_uv_offset); - const MLoopUV *luv_src_v2 = BM_ELEM_CD_GET_VOID_P(l_src->next, cd_loop_uv_offset); - const MLoopUV *luv_dst_v1 = BM_ELEM_CD_GET_VOID_P(l_dst, cd_loop_uv_offset); - const MLoopUV *luv_dst_v2 = BM_ELEM_CD_GET_VOID_P(l_dst->next, cd_loop_uv_offset); - - const float uv_src_v1[2] = {luv_src_v1->uv[0], luv_src_v1->uv[1] / aspect_y}; - const float uv_src_v2[2] = {luv_src_v2->uv[0], luv_src_v2->uv[1] / aspect_y}; - const float uv_dst_v1[2] = {luv_dst_v1->uv[0], luv_dst_v1->uv[1] / aspect_y}; - const float uv_dst_v2[2] = {luv_dst_v2->uv[0], luv_dst_v2->uv[1] / aspect_y}; - - struct { - int src_index; - int dst_index; - float len_sq; - } tests[4] = { - {0, 0, len_squared_v2v2(uv_src_v1, uv_dst_v1)}, - {0, 1, len_squared_v2v2(uv_src_v1, uv_dst_v2)}, - {1, 0, len_squared_v2v2(uv_src_v2, uv_dst_v1)}, - {1, 1, len_squared_v2v2(uv_src_v2, uv_dst_v2)}, - }; - int i_best = 0; - for (int i = 1; i < ARRAY_SIZE(tests); i++) { - if (use_nearest) { - if (tests[i].len_sq < tests[i_best].len_sq) { - i_best = i; - } - } - else { - if (tests[i].len_sq > tests[i_best].len_sq) { - i_best = i; - } - } - } - - *ele_src_p = (BMElem *)(tests[i_best].src_index ? l_src->next : l_src); - *ele_dst_p = (BMElem *)(tests[i_best].dst_index ? l_dst->next : l_dst); - - /* Ensure the edge is selected, not just the vertices up until we hit it. */ - *r_ele_dst_final = (BMElem *)(tests[i_best].dst_index ? l_dst : l_dst->next); -} - -/** \} */ - -/* -------------------------------------------------------------------- */ /** \name Path Select Struct & Properties * \{ */ @@ -180,12 +111,12 @@ static void path_select_params_from_op(wmOperator *op, struct PathSelectParams * * \{ */ /* callbacks */ -static bool looptag_filter_cb(BMLoop *l, void *user_data_v) +static bool verttag_filter_cb(BMLoop *l, void *user_data_v) { struct UserData_UV *user_data = user_data_v; return uvedit_face_visible_test(user_data->scene, l->f); } -static bool looptag_test_cb(BMLoop *l, void *user_data_v) +static bool verttag_test_cb(BMLoop *l, void *user_data_v) { /* All connected loops are selected or we return false. */ struct UserData_UV *user_data = user_data_v; @@ -195,7 +126,7 @@ static bool looptag_test_cb(BMLoop *l, void *user_data_v) BMIter iter; BMLoop *l_iter; BM_ITER_ELEM (l_iter, &iter, l->v, BM_LOOPS_OF_VERT) { - if (looptag_filter_cb(l_iter, user_data)) { + if (verttag_filter_cb(l_iter, user_data)) { const MLoopUV *luv_iter = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset); if (equals_v2v2(luv->uv, luv_iter->uv)) { if (!uvedit_uv_select_test(scene, l_iter, cd_loop_uv_offset)) { @@ -206,7 +137,7 @@ static bool looptag_test_cb(BMLoop *l, void *user_data_v) } return true; } -static void looptag_set_cb(BMLoop *l, bool val, void *user_data_v) +static void verttag_set_cb(BMLoop *l, bool val, void *user_data_v) { struct UserData_UV *user_data = user_data_v; const Scene *scene = user_data->scene; @@ -216,7 +147,7 @@ static void looptag_set_cb(BMLoop *l, bool val, void *user_data_v) BMIter iter; BMLoop *l_iter; BM_ITER_ELEM (l_iter, &iter, l->v, BM_LOOPS_OF_VERT) { - if (looptag_filter_cb(l_iter, user_data)) { + if (verttag_filter_cb(l_iter, user_data)) { MLoopUV *luv_iter = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset); if (equals_v2v2(luv->uv, luv_iter->uv)) { uvedit_uv_select_set(scene, em, l_iter, val, false, cd_loop_uv_offset); @@ -233,42 +164,10 @@ static int mouse_mesh_uv_shortest_path_vert(Scene *scene, const float aspect_y, const int cd_loop_uv_offset) { - const char uv_selectmode = ED_uvedit_select_mode_get(scene); - /* TODO(@sidd017): Implement logic to calculate shortest path for UV edges, since we now support - * proper edge selection for UVs (D12028). - * Till then continue using vertex path to fake shortest path calculation for edges. */ - const bool use_fake_edge_select = (uv_selectmode & UV_SELECT_EDGE); BMEditMesh *em = BKE_editmesh_from_object(obedit); BMesh *bm = em->bm; int flush = 0; - /* Variables to use when `use_fake_edge_select` is set. */ - struct { - BMLoop *l_dst_activate; - BMLoop *l_dst_add_to_path; - } fake_edge_select = {NULL}; - - if (use_fake_edge_select) { - fake_edge_select.l_dst_activate = l_dst; - - /* Use most distant when doing region selection. - * without this we get dangling edges outside the region. */ - bool use_neaerst = (op_params->use_fill == false); - BMElem *ele_src = (BMElem *)l_src; - BMElem *ele_dst = (BMElem *)l_dst; - BMElem *ele_dst_final = NULL; - bm_loop_calc_vert_pair_from_edge_pair( - use_neaerst, cd_loop_uv_offset, aspect_y, &ele_src, &ele_dst, &ele_dst_final); - - if (op_params->use_fill == false) { - /* Always activate the item under the cursor. */ - fake_edge_select.l_dst_add_to_path = (BMLoop *)ele_dst_final; - } - - l_src = (BMLoop *)ele_src; - l_dst = (BMLoop *)ele_dst; - } - struct UserData_UV user_data = { .scene = scene, .em = em, @@ -291,33 +190,23 @@ static int mouse_mesh_uv_shortest_path_vert(Scene *scene, (BMElem *)l_src, (BMElem *)l_dst, params.cd_loop_uv_offset, - looptag_filter_cb, + verttag_filter_cb, &user_data); } else { is_path_ordered = true; - path = BM_mesh_calc_path_uv_vert(bm, l_src, l_dst, ¶ms, looptag_filter_cb, &user_data); + path = BM_mesh_calc_path_uv_vert(bm, l_src, l_dst, ¶ms, verttag_filter_cb, &user_data); } } BMLoop *l_dst_last = l_dst; if (path) { - if (use_fake_edge_select) { - if ((fake_edge_select.l_dst_add_to_path != NULL) && - (BLI_linklist_index(path, fake_edge_select.l_dst_add_to_path) == -1)) { - /* Append, this isn't optimal compared to #BLI_linklist_append, it's a one-off lookup. */ - LinkNode *path_last = BLI_linklist_find_last(path); - BLI_linklist_insert_after(&path_last, fake_edge_select.l_dst_add_to_path); - BLI_assert(BLI_linklist_find_last(path)->link == fake_edge_select.l_dst_add_to_path); - } - } - /* toggle the flag */ bool all_set = true; LinkNode *node = path; do { - if (!looptag_test_cb((BMLoop *)node->link, &user_data)) { + if (!verttag_test_cb((BMLoop *)node->link, &user_data)) { all_set = false; break; } @@ -328,7 +217,7 @@ static int mouse_mesh_uv_shortest_path_vert(Scene *scene, do { if ((is_path_ordered == false) || WM_operator_properties_checker_interval_test(&op_params->interval_params, depth)) { - looptag_set_cb((BMLoop *)node->link, !all_set, &user_data); + verttag_set_cb((BMLoop *)node->link, !all_set, &user_data); if (is_path_ordered) { l_dst_last = node->link; } @@ -339,23 +228,133 @@ static int mouse_mesh_uv_shortest_path_vert(Scene *scene, flush = all_set ? -1 : 1; } else { - const bool is_act = !looptag_test_cb(l_dst, &user_data); - looptag_set_cb(l_dst, is_act, &user_data); /* switch the face option */ + const bool is_act = !verttag_test_cb(l_dst, &user_data); + verttag_set_cb(l_dst, is_act, &user_data); /* switch the face option */ } if (op_params->track_active) { - /* Fake edge selection. */ - if (use_fake_edge_select) { - BMLoop *l_dst_activate = fake_edge_select.l_dst_activate; - /* TODO(campbell): Search for an active loop attached to 'l_dst'. - * when `BLI_linklist_index(path, l_dst_activate) == -1` - * In practice this rarely happens though. */ - ED_uvedit_active_edge_loop_set(bm, l_dst_activate); + ED_uvedit_active_vert_loop_set(bm, l_dst_last); + } + return flush; +} + +/* -------------------------------------------------------------------- */ +/** \name UV Edge Path + * \{ */ + +/* callbacks */ +static bool edgetag_filter_cb(BMLoop *l, void *user_data_v) +{ + struct UserData_UV *user_data = user_data_v; + return uvedit_face_visible_test(user_data->scene, l->f); +} +static bool edgetag_test_cb(BMLoop *l, void *user_data_v) +{ + /* All connected loops (UV) are selected or we return false. */ + struct UserData_UV *user_data = user_data_v; + const Scene *scene = user_data->scene; + const uint cd_loop_uv_offset = user_data->cd_loop_uv_offset; + BMIter iter; + BMLoop *l_iter; + BM_ITER_ELEM (l_iter, &iter, l->e, BM_LOOPS_OF_EDGE) { + if (edgetag_filter_cb(l_iter, user_data)) { + if (BM_loop_uv_share_edge_check(l, l_iter, cd_loop_uv_offset)) { + if (!uvedit_edge_select_test(scene, l_iter, cd_loop_uv_offset)) { + return false; + } + } + } + } + return true; +} +static void edgetag_set_cb(BMLoop *l, bool val, void *user_data_v) +{ + struct UserData_UV *user_data = user_data_v; + const Scene *scene = user_data->scene; + BMEditMesh *em = user_data->em; + const uint cd_loop_uv_offset = user_data->cd_loop_uv_offset; + uvedit_edge_select_set_with_sticky(scene, em, l, val, false, cd_loop_uv_offset); +} + +static int mouse_mesh_uv_shortest_path_edge(Scene *scene, + Object *obedit, + const struct PathSelectParams *op_params, + BMLoop *l_src, + BMLoop *l_dst, + const float aspect_y, + const int cd_loop_uv_offset) +{ + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + int flush = 0; + + struct UserData_UV user_data = { + .scene = scene, + .em = em, + .cd_loop_uv_offset = cd_loop_uv_offset, + }; + + const struct BMCalcPathUVParams params = { + .use_topology_distance = op_params->use_topology_distance, + .use_step_face = op_params->use_face_step, + .aspect_y = aspect_y, + .cd_loop_uv_offset = cd_loop_uv_offset, + }; + + LinkNode *path = NULL; + bool is_path_ordered = false; + + if (l_src != l_dst) { + if (op_params->use_fill) { + path = BM_mesh_calc_path_uv_region_edge(bm, + (BMElem *)l_src, + (BMElem *)l_dst, + params.cd_loop_uv_offset, + edgetag_filter_cb, + &user_data); } else { - ED_uvedit_active_vert_loop_set(bm, l_dst_last); + is_path_ordered = true; + path = BM_mesh_calc_path_uv_edge(bm, l_src, l_dst, ¶ms, edgetag_filter_cb, &user_data); } } + + BMLoop *l_dst_last = l_dst; + + if (path) { + /* toggle the flag */ + bool all_set = true; + LinkNode *node = path; + do { + if (!edgetag_test_cb((BMLoop *)node->link, &user_data)) { + all_set = false; + break; + } + } while ((node = node->next)); + + int depth = -1; + node = path; + do { + if ((is_path_ordered == false) || + WM_operator_properties_checker_interval_test(&op_params->interval_params, depth)) { + edgetag_set_cb((BMLoop *)node->link, !all_set, &user_data); + if (is_path_ordered) { + l_dst_last = node->link; + } + } + } while ((void)depth++, (node = node->next)); + + BLI_linklist_free(path, NULL); + flush = all_set ? -1 : 1; + } + else { + const bool is_act = !edgetag_test_cb(l_dst, &user_data); + edgetag_set_cb(l_dst, is_act, &user_data); /* switch the face option */ + } + + if (op_params->track_active) { + ED_uvedit_active_edge_loop_set(bm, l_dst_last); + } return flush; } @@ -514,13 +513,24 @@ static bool uv_shortest_path_pick_ex(Scene *scene, ok = true; } else if (ele_src->head.htype == BM_LOOP) { - flush = mouse_mesh_uv_shortest_path_vert(scene, - obedit, - op_params, - (BMLoop *)ele_src, - (BMLoop *)ele_dst, - aspect_y, - cd_loop_uv_offset); + if (uv_selectmode & UV_SELECT_EDGE) { + flush = mouse_mesh_uv_shortest_path_edge(scene, + obedit, + op_params, + (BMLoop *)ele_src, + (BMLoop *)ele_dst, + aspect_y, + cd_loop_uv_offset); + } + else { + flush = mouse_mesh_uv_shortest_path_vert(scene, + obedit, + op_params, + (BMLoop *)ele_src, + (BMLoop *)ele_dst, + aspect_y, + cd_loop_uv_offset); + } ok = true; } @@ -529,24 +539,9 @@ static bool uv_shortest_path_pick_ex(Scene *scene, const bool select = (flush == 1); BMEditMesh *em = BKE_editmesh_from_object(obedit); if (ts->uv_flag & UV_SYNC_SELECTION) { - if (uv_selectmode & UV_SELECT_EDGE) { - /* Special case as we don't use true edge selection, - * flush the selection from the vertices. */ - BM_mesh_select_mode_flush_ex(em->bm, SCE_SELECT_VERTEX, BM_SELECT_LEN_FLUSH_RECALC_ALL); - } ED_uvedit_select_sync_flush(scene->toolsettings, em, select); } else { - if (uv_selectmode & UV_SELECT_EDGE) { - /* TODO(@sidd017): Remove this case when adding proper uv edge support for this operator. - * In the meantime, this case helps ensures proper UV selection states for edge mode. */ - if (select) { - uvedit_select_flush(scene, em); - } - else { - uvedit_deselect_flush(scene, em); - } - } ED_uvedit_selectmode_flush(scene, em); } } |