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]: ffaaa0bcbf

Reviewed By: campbellbarton

Ref D15511.
This commit is contained in:
Siddhartha Jejurkar
2022-07-22 10:47:28 +10:00
committed by Campbell Barton
parent aa1ffc093c
commit 7725740543
3 changed files with 351 additions and 159 deletions

View File

@@ -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;
}
/** \} */

View File

@@ -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,

View File

@@ -55,75 +55,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, &params, looptag_filter_cb, &user_data);
path = BM_mesh_calc_path_uv_vert(bm, l_src, l_dst, &params, 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, &params, 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);
}
}