From 613d314251f32f32d9f634d073ce0d6993c22754 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 15 Jul 2020 17:35:57 +1000 Subject: UV: support region fill for path select This matches edit-mesh region selection (Ctrl-Shift-Select). --- .../keyconfig/keymap_data/blender_default.py | 5 +- source/blender/bmesh/CMakeLists.txt | 2 + source/blender/bmesh/bmesh_tools.h | 1 + source/blender/bmesh/tools/bmesh_path_region_uv.c | 502 +++++++++++++++++++++ source/blender/bmesh/tools/bmesh_path_region_uv.h | 48 ++ source/blender/editors/uvedit/uvedit_path.c | 52 ++- 6 files changed, 598 insertions(+), 12 deletions(-) create mode 100644 source/blender/bmesh/tools/bmesh_path_region_uv.c create mode 100644 source/blender/bmesh/tools/bmesh_path_region_uv.h diff --git a/release/scripts/presets/keyconfig/keymap_data/blender_default.py b/release/scripts/presets/keyconfig/keymap_data/blender_default.py index 3a885837444..fae247b30a5 100644 --- a/release/scripts/presets/keyconfig/keymap_data/blender_default.py +++ b/release/scripts/presets/keyconfig/keymap_data/blender_default.py @@ -850,7 +850,10 @@ def km_uv_editor(params): {"properties": [("extend", False)]}), ("uv.select_loop", {"type": params.select_mouse, "value": params.select_mouse_value, "shift": True, "alt": True}, {"properties": [("extend", True)]}), - ("uv.shortest_path_pick", {"type": params.select_mouse, "value": params.select_mouse_value, "ctrl": True}, None), + ("uv.shortest_path_pick", {"type": params.select_mouse, "value": params.select_mouse_value, "ctrl": True}, + {"properties": [("use_fill", False)]}), + ("uv.shortest_path_pick", {"type": params.select_mouse, "value": params.select_mouse_value, "ctrl": True, "shift": True}, + {"properties": [("use_fill", True)]}), ("uv.select_split", {"type": 'Y', "value": 'PRESS'}, None), ("uv.select_box", {"type": 'B', "value": 'PRESS'}, {"properties": [("pinned", False)]}), diff --git a/source/blender/bmesh/CMakeLists.txt b/source/blender/bmesh/CMakeLists.txt index 0943b4a9c2a..fca411e594f 100644 --- a/source/blender/bmesh/CMakeLists.txt +++ b/source/blender/bmesh/CMakeLists.txt @@ -155,6 +155,8 @@ set(SRC tools/bmesh_path_region.h tools/bmesh_path_uv.c tools/bmesh_path_uv.h + tools/bmesh_path_region_uv.c + tools/bmesh_path_region_uv.h tools/bmesh_region_match.c tools/bmesh_region_match.h tools/bmesh_separate.c diff --git a/source/blender/bmesh/bmesh_tools.h b/source/blender/bmesh/bmesh_tools.h index a0dc6abf009..4593f34792b 100644 --- a/source/blender/bmesh/bmesh_tools.h +++ b/source/blender/bmesh/bmesh_tools.h @@ -36,6 +36,7 @@ extern "C" { #include "tools/bmesh_edgesplit.h" #include "tools/bmesh_path.h" #include "tools/bmesh_path_region.h" +#include "tools/bmesh_path_region_uv.h" #include "tools/bmesh_path_uv.h" #include "tools/bmesh_region_match.h" #include "tools/bmesh_separate.h" diff --git a/source/blender/bmesh/tools/bmesh_path_region_uv.c b/source/blender/bmesh/tools/bmesh_path_region_uv.c new file mode 100644 index 00000000000..d036c20d0e4 --- /dev/null +++ b/source/blender/bmesh/tools/bmesh_path_region_uv.c @@ -0,0 +1,502 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup bmesh + * + * Find the region defined by the path(s) between 2 UV elements. + * (path isn't ordered). + * + * \note This uses the same behavior as bmesh_path_region.c + * however walking UV's causes enough differences that it's + * impractical to share the code. + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_alloca.h" +#include "BLI_linklist.h" +#include "BLI_math.h" +#include "BLI_utildefines_stack.h" + +#include "bmesh.h" +#include "bmesh_path_region_uv.h" /* own include */ + +/** + * Special handling of vertices with 2 edges + * (act as if the edge-chain is a single edge). + * + * \note Regarding manifold edge stepping: #BM_vert_is_edge_pair_manifold usage. + * Logic to skip a chain of vertices is not applied at boundaries because it gives + * strange behavior from a user perspective especially with boundary quads, see: T52701 + * + * Restrict walking over a vertex chain to cases where the edges share the same faces. + * This is more typical of what a user would consider a vertex chain. + */ +#define USE_EDGE_CHAIN + +#ifdef USE_EDGE_CHAIN +/** + * Takes a vertex with 2 edge users and assigns the vertices at each end-point, + * + * \return Success when \a l_end_pair values are set or false if the edges loop back on themselves. + */ +static bool bm_loop_pair_ends(BMLoop *l_pivot, BMLoop *l_end_pair[2]) +{ + int j; + for (j = 0; j < 2; j++) { + BMLoop *l_other = j ? l_pivot->next : l_pivot->prev; + while (BM_vert_is_edge_pair_manifold(l_other->v)) { + l_other = j ? l_other->next : l_other->prev; + if (l_other == l_pivot) { + return false; + } + } + l_end_pair[j] = l_other; + } + BLI_assert(j == 2); + return true; +} +#endif /* USE_EDGE_CHAIN */ + +/** \name Loop Vertex in Region Checks + * \{ */ + +static bool bm_loop_region_test(BMLoop *l, int *const depths[2], const int pass) +{ + const int index = BM_elem_index_get(l); + return (((depths[0][index] != -1) && (depths[1][index] != -1)) && + ((depths[0][index] + depths[1][index]) < pass)); +} + +#ifdef USE_EDGE_CHAIN +static bool bm_loop_region_test_chain(BMLoop *l, int *const depths[2], const int pass) +{ + BMLoop *l_end_pair[2]; + if (bm_loop_region_test(l, depths, pass)) { + return true; + } + if (BM_vert_is_edge_pair_manifold(l->v) && bm_loop_pair_ends(l, l_end_pair) && + bm_loop_region_test(l_end_pair[0], depths, pass) && + bm_loop_region_test(l_end_pair[1], depths, pass)) { + return true; + } + + return false; +} +#else +static bool bm_loop_region_test_chain(BMLoop *l, int *const depths[2], const int pass) +{ + return bm_loop_region_test(l, depths, pass); +} +#endif + +/** \} */ + +/** + * Main logic for calculating region between 2 elements. + * + * This method works walking (breadth first) over all vertices, + * keeping track of topological distance from the source. + * + * This is done in both directions, after that each vertices 'depth' is added to check + * if its less than the number of passes needed to complete the search. + * When it is, we know the path is one of possible paths + * that have the minimum topological distance. + * + * \note Only verts without BM_ELEM_TAG will be walked over. + */ +static LinkNode *mesh_calc_path_region_elem(BMesh *bm, + BMElem *ele_src, + BMElem *ele_dst, + const uint cd_loop_uv_offset, + const char path_htype) +{ + int ele_loops_len[2]; + BMLoop **ele_loops[2]; + + /* Get vertices from any `ele_src/ele_dst` elements. */ + for (int side = 0; side < 2; side++) { + BMElem *ele = side ? ele_dst : ele_src; + int j = 0; + + if (ele->head.htype == BM_FACE) { + BMFace *f = (BMFace *)ele; + ele_loops[side] = BLI_array_alloca(ele_loops[side], f->len); + + BMLoop *l_first, *l_iter; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + ele_loops[side][j++] = l_iter; + } while ((l_iter = l_iter->next) != l_first); + } + else if (ele->head.htype == BM_LOOP) { + if (path_htype == BM_EDGE) { + BMLoop *l = (BMLoop *)ele; + ele_loops[side] = BLI_array_alloca(ele_loops[side], 2); + ele_loops[side][j++] = l; + ele_loops[side][j++] = l->next; + } + else if (path_htype == BM_VERT) { + BMLoop *l = (BMLoop *)ele; + ele_loops[side] = BLI_array_alloca(ele_loops[side], 1); + + ele_loops[side][j++] = l; + } + else { + BLI_assert(0); + } + } + else { + BLI_assert(0); + } + ele_loops_len[side] = j; + } + + int *depths[2] = {NULL}; + int pass = 0; + + BMLoop **stack = MEM_mallocN(sizeof(*stack) * bm->totloop, __func__); + BMLoop **stack_other = MEM_mallocN(sizeof(*stack_other) * bm->totloop, __func__); + + STACK_DECLARE(stack); + STACK_INIT(stack, bm->totloop); + + STACK_DECLARE(stack_other); + STACK_INIT(stack_other, bm->totloop); + + BM_mesh_elem_index_ensure(bm, BM_LOOP); + + /* After exhausting all possible elements, we should have found all elements on the 'side_other'. + * otherwise, exit early. */ + bool found_all = false; + + for (int side = 0; side < 2; side++) { + const int side_other = !side; + + /* initialize depths to -1 (un-touched), fill in with the depth as we walk over the edges. */ + depths[side] = MEM_mallocN(sizeof(*depths[side]) * bm->totloop, __func__); + copy_vn_i(depths[side], bm->totloop, -1); + + /* needed for second side */ + STACK_CLEAR(stack); + STACK_CLEAR(stack_other); + + for (int i = 0; i < ele_loops_len[side]; i++) { + BMLoop *l = ele_loops[side][i]; + depths[side][BM_elem_index_get(l)] = 0; + if (!BM_elem_flag_test(l, BM_ELEM_TAG)) { + STACK_PUSH(stack, l); + } + } + +#ifdef USE_EDGE_CHAIN + /* Expand initial state to end-point vertices when they only have 2x edges, + * this prevents odd behavior when source or destination are in the middle + * of a long chain of edges. */ + if (ELEM(path_htype, BM_VERT, BM_EDGE)) { + for (int i = 0; i < ele_loops_len[side]; i++) { + BMLoop *l = ele_loops[side][i]; + BMLoop *l_end_pair[2]; + if (BM_vert_is_edge_pair_manifold(l->v) && bm_loop_pair_ends(l, l_end_pair)) { + for (int j = 0; j < 2; j++) { + const int l_end_index = BM_elem_index_get(l_end_pair[j]); + if (depths[side][l_end_index] == -1) { + depths[side][l_end_index] = 0; + if (!BM_elem_flag_test(l_end_pair[j], BM_ELEM_TAG)) { + STACK_PUSH(stack, l_end_pair[j]); + } + } + } + } + } + } +#endif /* USE_EDGE_CHAIN */ + + /* Keep walking over connected geometry until we find all the vertices in + * `ele_loops[side_other]`, or exit the loop when there's no connection. */ + found_all = false; + for (pass = 1; (STACK_SIZE(stack) != 0); pass++) { + while (STACK_SIZE(stack) != 0) { + BMLoop *l_a = STACK_POP(stack); + const int l_a_index = BM_elem_index_get(l_a); + + BMIter iter; + BMLoop *l_iter; + + BM_ITER_ELEM (l_iter, &iter, l_a->v, BM_LOOPS_OF_VERT) { + if (BM_elem_flag_test(l_iter, BM_ELEM_TAG)) { + continue; + } + if (!BM_loop_uv_share_vert_check(l_a, l_iter, cd_loop_uv_offset)) { + continue; + } + + /* Flush the depth to connected loops (only needed for UV's). */ + if (depths[side][BM_elem_index_get(l_iter)] == -1) { + depths[side][BM_elem_index_get(l_iter)] = depths[side][l_a_index]; + } + + for (int j = 0; j < 2; j++) { + BMLoop *l_b = j ? l_iter->next : l_iter->prev; + int l_b_index = BM_elem_index_get(l_b); + if (depths[side][l_b_index] == -1) { + +#ifdef USE_EDGE_CHAIN + /* Walk along the chain, fill in values until we reach a vertex with 3+ edges. */ + { + while (BM_vert_is_edge_pair_manifold(l_b->v) && + ((depths[side][l_b_index] == -1) && + /* Don't walk back to the beginning */ + (l_b != (j ? l_iter->prev : l_iter->next)))) { + depths[side][l_b_index] = pass; + + l_b = j ? l_b->next : l_b->prev; + l_b_index = BM_elem_index_get(l_b); + } + } +#endif /* USE_EDGE_CHAIN */ + + /* Add the other vertex to the stack, to be traversed in the next pass. */ + if (depths[side][l_b_index] == -1) { +#ifdef USE_EDGE_CHAIN + BLI_assert(!BM_vert_is_edge_pair_manifold(l_b->v)); +#endif + BLI_assert(pass == depths[side][BM_elem_index_get(l_a)] + 1); + depths[side][l_b_index] = pass; + if (!BM_elem_flag_test(l_b, BM_ELEM_TAG)) { + STACK_PUSH(stack_other, l_b); + } + } + } + } + } + } + + /* Stop searching once there's none left. + * Note that this looks in-efficient, however until the target elements reached, + * it will exit immediately. + * After that, it takes as many passes as the element has edges to finish off. */ + found_all = true; + for (int i = 0; i < ele_loops_len[side_other]; i++) { + if (depths[side][BM_elem_index_get(ele_loops[side_other][i])] == -1) { + found_all = false; + break; + } + } + if (found_all == true) { + pass++; + break; + } + + STACK_SWAP(stack, stack_other); + } + + /* if we have nothing left, and didn't find all elements on the other side, + * exit early and don't continue */ + if (found_all == false) { + break; + } + } + + MEM_freeN(stack); + MEM_freeN(stack_other); + + /* Now we have depths recorded from both sides, + * select elements that use tagged verts. */ + LinkNode *path = NULL; + + if (found_all == false) { + /* fail! (do nothing) */ + } + else if (path_htype == BM_FACE) { + BMIter fiter; + BMFace *f; + + BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) { + if (!BM_elem_flag_test(f, BM_ELEM_TAG)) { + /* check all verts in face are tagged */ + BMLoop *l_first, *l_iter; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + bool ok = true; +#if 0 + do { + if (!bm_loop_region_test_chain(l_iter->v, depths, pass)) { + ok = false; + break; + } + } while ((l_iter = l_iter->next) != l_first); +#else + /* Allowing a single failure on a face gives fewer 'gaps'. + * While correct, in practice they're often part of what + * a user would consider the 'region'. */ + int ok_tests = f->len > 3 ? 1 : 0; /* how many times we may fail */ + do { + if (!bm_loop_region_test_chain(l_iter, depths, pass)) { + if (ok_tests == 0) { + ok = false; + break; + } + ok_tests--; + } + } while ((l_iter = l_iter->next) != l_first); +#endif + + if (ok) { + BLI_linklist_prepend(&path, f); + } + } + } + } + else if (path_htype == BM_EDGE) { + BMIter fiter; + BMFace *f; + BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) { + BMIter liter; + BMLoop *l; + /* Check the current and next loop vertices are in the region. */ + bool l_in_chain_next = bm_loop_region_test_chain(BM_FACE_FIRST_LOOP(f), depths, pass); + BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { + const bool l_in_chain = l_in_chain_next; + l_in_chain_next = bm_loop_region_test_chain(l->next, depths, pass); + if (l_in_chain && l_in_chain_next) { + BLI_linklist_prepend(&path, l); + } + } + } + } + else if (path_htype == BM_VERT) { + BMIter fiter; + BMFace *f; + BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) { + BMIter liter; + BMLoop *l; + BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { + if (bm_loop_region_test_chain(l, depths, pass)) { + BLI_linklist_prepend(&path, l); + } + } + } + } + + for (int side = 0; side < 2; side++) { + if (depths[side]) { + MEM_freeN(depths[side]); + } + } + + return path; +} + +#undef USE_EDGE_CHAIN + +/** \name Main Functions (exposed externally). + * \{ */ + +LinkNode *BM_mesh_calc_path_uv_region_vert(BMesh *bm, + BMElem *ele_src, + BMElem *ele_dst, + const uint cd_loop_uv_offset, + bool (*filter_fn)(BMLoop *, void *user_data), + void *user_data) +{ + LinkNode *path = NULL; + /* BM_ELEM_TAG flag is used to store visited verts */ + BMFace *f; + BMIter fiter; + int i = 0; + + BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) { + BMIter liter; + BMLoop *l; + BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { + BM_elem_flag_set(l, BM_ELEM_TAG, !filter_fn(l, user_data)); + BM_elem_index_set(l, i); /* set_inline */ + i += 1; + } + } + bm->elem_index_dirty &= ~BM_LOOP; + + path = mesh_calc_path_region_elem(bm, ele_src, ele_dst, cd_loop_uv_offset, BM_VERT); + + return path; +} + +LinkNode *BM_mesh_calc_path_uv_region_edge(BMesh *bm, + BMElem *ele_src, + BMElem *ele_dst, + const uint cd_loop_uv_offset, + bool (*filter_fn)(BMLoop *, void *user_data), + void *user_data) +{ + LinkNode *path = NULL; + /* BM_ELEM_TAG flag is used to store visited verts */ + BMFace *f; + BMIter fiter; + int i = 0; + + BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) { + BMIter liter; + BMLoop *l; + BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) { + BM_elem_flag_set(l, BM_ELEM_TAG, !filter_fn(l, user_data)); + BM_elem_index_set(l, i); /* set_inline */ + i += 1; + } + } + bm->elem_index_dirty &= ~BM_LOOP; + + path = mesh_calc_path_region_elem(bm, ele_src, ele_dst, cd_loop_uv_offset, BM_EDGE); + + return path; +} + +LinkNode *BM_mesh_calc_path_uv_region_face(BMesh *bm, + BMElem *ele_src, + BMElem *ele_dst, + const uint cd_loop_uv_offset, + bool (*filter_fn)(BMFace *, void *user_data), + void *user_data) +{ + LinkNode *path = NULL; + /* BM_ELEM_TAG flag is used to store visited verts */ + BMFace *f; + BMIter fiter; + int i; + + /* flush flag to verts */ + BM_mesh_elem_hflag_enable_all(bm, BM_VERT, BM_ELEM_TAG, false); + + BM_ITER_MESH_INDEX (f, &fiter, bm, BM_FACES_OF_MESH, i) { + bool test; + BM_elem_flag_set(f, BM_ELEM_TAG, test = !filter_fn(f, user_data)); + + /* flush tag to verts */ + if (test == false) { + BMLoop *l_first, *l_iter; + l_iter = l_first = BM_FACE_FIRST_LOOP(f); + do { + BM_elem_flag_disable(l_iter->v, BM_ELEM_TAG); + } while ((l_iter = l_iter->next) != l_first); + } + } + + path = mesh_calc_path_region_elem(bm, ele_src, ele_dst, cd_loop_uv_offset, BM_FACE); + + return path; +} + +/** \} */ diff --git a/source/blender/bmesh/tools/bmesh_path_region_uv.h b/source/blender/bmesh/tools/bmesh_path_region_uv.h new file mode 100644 index 00000000000..16e627ab461 --- /dev/null +++ b/source/blender/bmesh/tools/bmesh_path_region_uv.h @@ -0,0 +1,48 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef __BMESH_PATH_REGION_UV_H__ +#define __BMESH_PATH_REGION_UV_H__ + +/** \file + * \ingroup bmesh + */ + +struct LinkNode *BM_mesh_calc_path_uv_region_vert(BMesh *bm, + BMElem *ele_src, + BMElem *ele_dst, + const uint cd_loop_uv_offset, + bool (*test_fn)(BMLoop *, void *user_data), + void *user_data) ATTR_WARN_UNUSED_RESULT + ATTR_NONNULL(1, 2, 3); + +struct LinkNode *BM_mesh_calc_path_uv_region_edge(BMesh *bm, + BMElem *ele_src, + BMElem *ele_dst, + const uint cd_loop_uv_offset, + bool (*test_fn)(BMLoop *, void *user_data), + void *user_data) ATTR_WARN_UNUSED_RESULT + ATTR_NONNULL(1, 2, 3); + +struct LinkNode *BM_mesh_calc_path_uv_region_face(BMesh *bm, + BMElem *ele_src, + BMElem *ele_dst, + const uint cd_loop_uv_offset, + bool (*test_fn)(BMFace *, void *user_data), + void *user_data) ATTR_WARN_UNUSED_RESULT + ATTR_NONNULL(1, 2, 3); + +#endif /* __BMESH_PATH_REGION_UV_H__ */ diff --git a/source/blender/editors/uvedit/uvedit_path.c b/source/blender/editors/uvedit/uvedit_path.c index e9615b62523..f72ddcdedd3 100644 --- a/source/blender/editors/uvedit/uvedit_path.c +++ b/source/blender/editors/uvedit/uvedit_path.c @@ -70,7 +70,7 @@ #include "bmesh_tools.h" /* TODO(campbell): region filling, matching mesh selection. */ -// #define USE_FILL +#define USE_FILL /* -------------------------------------------------------------------- */ /** \name Local Utilities @@ -258,10 +258,24 @@ static void mouse_mesh_uv_shortest_path_vert(Scene *scene, .aspect_y = aspect_y, .cd_loop_uv_offset = cd_loop_uv_offset, }; - LinkNode *path = BM_mesh_calc_path_uv_vert( - bm, l_src, l_dst, ¶ms, looptag_filter_cb, &user_data); - /* TODO: false when we support region selection. */ - bool is_path_ordered = true; + + 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_vert(bm, + (BMElem *)l_src, + (BMElem *)l_dst, + params.cd_loop_uv_offset, + looptag_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); + } + } BMLoop *l_dst_last = l_dst; @@ -374,10 +388,24 @@ static void mouse_mesh_uv_shortest_path_face(Scene *scene, .aspect_y = aspect_y, .cd_loop_uv_offset = cd_loop_uv_offset, }; - LinkNode *path = BM_mesh_calc_path_uv_face( - bm, f_src, f_dst, ¶ms, facetag_filter_cb, &user_data); - /* TODO: false when we support region selection. */ - bool is_path_ordered = true; + + LinkNode *path = NULL; + bool is_path_ordered = false; + + if (f_src != f_dst) { + if (op_params->use_fill) { + path = BM_mesh_calc_path_uv_region_face(bm, + (BMElem *)f_src, + (BMElem *)f_dst, + params.cd_loop_uv_offset, + facetag_filter_cb, + &user_data); + } + else { + is_path_ordered = true; + path = BM_mesh_calc_path_uv_face(bm, f_src, f_dst, ¶ms, facetag_filter_cb, &user_data); + } + } BMFace *f_dst_last = f_dst; @@ -453,8 +481,10 @@ static bool uv_shortest_path_pick_ex(Scene *scene, const ToolSettings *ts = scene->toolsettings; BMElem *ele_dst_final = NULL; if (ts->uv_selectmode & UV_SELECT_EDGE) { - bm_loop_calc_vert_pair_from_edge_pair( - cd_loop_uv_offset, aspect_y, &ele_src, &ele_dst, &ele_dst_final); + if (op_params->use_fill == false) { + bm_loop_calc_vert_pair_from_edge_pair( + cd_loop_uv_offset, aspect_y, &ele_src, &ele_dst, &ele_dst_final); + } } mouse_mesh_uv_shortest_path_vert(scene, obedit, -- cgit v1.2.3