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:
committed by
Campbell Barton
parent
aa1ffc093c
commit
7725740543
@@ -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;
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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, ¶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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user