Fix T102680: UV Pick shortest Path wrong for multi-object editing

Path selection support from [0] didn't account for multiple objects in
edit-mode. Now picking the UV also picks the object to operate on.

[0]: ea5fe7abc1
This commit is contained in:
Campbell Barton 2023-01-20 12:51:50 +11:00
parent ebb519652c
commit a0706d6cf0
Notes: blender-bot 2023-02-13 13:58:33 +01:00
Referenced by issue #102680, Pick shortest Path in UV Editor work incorrectly, if edit several objects
1 changed files with 136 additions and 102 deletions

View File

@ -38,6 +38,7 @@
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
#include "ED_object.h"
#include "ED_screen.h"
#include "ED_transform.h"
#include "ED_uvedit.h"
@ -559,136 +560,159 @@ static int uv_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmEve
op_params.track_active = true;
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
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(
scene, view_layer, CTX_wm_view3d(C), &objects_len);
float co[2];
const ARegion *region = CTX_wm_region(C);
Object *obedit = CTX_data_edit_object(C);
BMEditMesh *em = BKE_editmesh_from_object(obedit);
BMesh *bm = em->bm;
const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
float aspect_y;
{
float aspx, aspy;
ED_uvedit_get_aspect(obedit, &aspx, &aspy);
aspect_y = aspx / aspy;
}
UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
BMElem *ele_src = NULL, *ele_dst = NULL;
/* Detect the hit. */
UvNearestHit hit = UV_NEAREST_HIT_INIT_MAX(&region->v2d);
bool hit_found = false;
if (uv_selectmode == UV_SELECT_FACE) {
UvNearestHit hit = UV_NEAREST_HIT_INIT_MAX(&region->v2d);
if (!uv_find_nearest_face(scene, obedit, co, &hit)) {
return OPERATOR_CANCELLED;
if (uv_find_nearest_face_multi(scene, objects, objects_len, co, &hit)) {
hit_found = true;
}
BMFace *f_src = BM_mesh_active_face_get(bm, false, false);
/* Check selection? */
ele_src = (BMElem *)f_src;
ele_dst = (BMElem *)hit.efa;
}
else if (uv_selectmode & UV_SELECT_EDGE) {
UvNearestHit hit = UV_NEAREST_HIT_INIT_MAX(&region->v2d);
if (!uv_find_nearest_edge(scene, obedit, co, 0.0f, &hit)) {
return OPERATOR_CANCELLED;
}
BMLoop *l_src = NULL;
if (ts->uv_flag & UV_SYNC_SELECTION) {
BMEdge *e_src = BM_mesh_active_edge_get(bm);
if (e_src != NULL) {
l_src = uv_find_nearest_loop_from_edge(scene, obedit, e_src, co);
}
}
else {
l_src = ED_uvedit_active_edge_loop_get(bm);
if (l_src != NULL) {
if (!uvedit_uv_select_test(scene, l_src, offsets) &&
!uvedit_uv_select_test(scene, l_src->next, offsets)) {
l_src = NULL;
}
ele_src = (BMElem *)l_src;
}
}
ele_src = (BMElem *)l_src;
ele_dst = (BMElem *)hit.l;
}
else {
UvNearestHit hit = UV_NEAREST_HIT_INIT_MAX(&region->v2d);
if (!uv_find_nearest_vert(scene, obedit, co, 0.0f, &hit)) {
return OPERATOR_CANCELLED;
}
BMLoop *l_src = NULL;
if (ts->uv_flag & UV_SYNC_SELECTION) {
BMVert *v_src = BM_mesh_active_vert_get(bm);
if (v_src != NULL) {
l_src = uv_find_nearest_loop_from_vert(scene, obedit, v_src, co);
}
}
else {
l_src = ED_uvedit_active_vert_loop_get(bm);
if (l_src != NULL) {
if (!uvedit_uv_select_test(scene, l_src, offsets)) {
l_src = NULL;
}
}
}
ele_src = (BMElem *)l_src;
ele_dst = (BMElem *)hit.l;
}
if (ele_src == NULL || ele_dst == NULL) {
return OPERATOR_CANCELLED;
}
uv_shortest_path_pick_ex(
scene, depsgraph, obedit, &op_params, ele_src, ele_dst, aspect_y, offsets);
/* To support redo. */
int index;
if (uv_selectmode & UV_SELECT_FACE) {
BM_mesh_elem_index_ensure(bm, BM_FACE);
index = BM_elem_index_get(ele_dst);
}
else if (uv_selectmode & UV_SELECT_EDGE) {
BM_mesh_elem_index_ensure(bm, BM_LOOP);
index = BM_elem_index_get(ele_dst);
if (uv_find_nearest_edge_multi(scene, objects, objects_len, co, 0.0f, &hit)) {
hit_found = true;
}
}
else {
BM_mesh_elem_index_ensure(bm, BM_LOOP);
index = BM_elem_index_get(ele_dst);
if (uv_find_nearest_vert_multi(scene, objects, objects_len, co, 0.0f, &hit)) {
hit_found = true;
}
}
RNA_int_set(op->ptr, "index", index);
return OPERATOR_FINISHED;
bool changed = false;
if (hit_found) {
/* This may not be the active object. */
Object *obedit = hit.ob;
BMEditMesh *em = BKE_editmesh_from_object(obedit);
BMesh *bm = em->bm;
const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
/* Respond to the hit. */
if (uv_selectmode == UV_SELECT_FACE) {
/* Face selection. */
BMFace *f_src = BM_mesh_active_face_get(bm, false, false);
/* Check selection? */
ele_src = (BMElem *)f_src;
ele_dst = (BMElem *)hit.efa;
}
else if (uv_selectmode & UV_SELECT_EDGE) {
/* Edge selection. */
BMLoop *l_src = NULL;
if (ts->uv_flag & UV_SYNC_SELECTION) {
BMEdge *e_src = BM_mesh_active_edge_get(bm);
if (e_src != NULL) {
l_src = uv_find_nearest_loop_from_edge(scene, obedit, e_src, co);
}
}
else {
l_src = ED_uvedit_active_edge_loop_get(bm);
if (l_src != NULL) {
if (!uvedit_uv_select_test(scene, l_src, offsets) &&
!uvedit_uv_select_test(scene, l_src->next, offsets)) {
l_src = NULL;
}
ele_src = (BMElem *)l_src;
}
}
ele_src = (BMElem *)l_src;
ele_dst = (BMElem *)hit.l;
}
else {
/* Vertex selection. */
BMLoop *l_src = NULL;
if (ts->uv_flag & UV_SYNC_SELECTION) {
BMVert *v_src = BM_mesh_active_vert_get(bm);
if (v_src != NULL) {
l_src = uv_find_nearest_loop_from_vert(scene, obedit, v_src, co);
}
}
else {
l_src = ED_uvedit_active_vert_loop_get(bm);
if (l_src != NULL) {
if (!uvedit_uv_select_test(scene, l_src, offsets)) {
l_src = NULL;
}
}
}
ele_src = (BMElem *)l_src;
ele_dst = (BMElem *)hit.l;
}
if (ele_src && ele_dst) {
/* Always use the active object, not `obedit` as the active defines the UV display. */
float aspect_y;
{
float aspx, aspy;
ED_uvedit_get_aspect(CTX_data_edit_object(C), &aspx, &aspy);
aspect_y = aspx / aspy;
}
uv_shortest_path_pick_ex(
scene, depsgraph, obedit, &op_params, ele_src, ele_dst, aspect_y, offsets);
/* Store the object and it's index so redo is possible. */
int index;
if (uv_selectmode & UV_SELECT_FACE) {
BM_mesh_elem_index_ensure(bm, BM_FACE);
index = BM_elem_index_get(ele_dst);
}
else if (uv_selectmode & UV_SELECT_EDGE) {
BM_mesh_elem_index_ensure(bm, BM_LOOP);
index = BM_elem_index_get(ele_dst);
}
else {
BM_mesh_elem_index_ensure(bm, BM_LOOP);
index = BM_elem_index_get(ele_dst);
}
const int object_index = ED_object_in_mode_to_index(scene, view_layer, OB_MODE_EDIT, obedit);
BLI_assert(object_index != -1);
RNA_int_set(op->ptr, "object_index", object_index);
RNA_int_set(op->ptr, "index", index);
changed = true;
}
}
MEM_freeN(objects);
return changed ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
}
static int uv_shortest_path_pick_exec(bContext *C, wmOperator *op)
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
const char uv_selectmode = ED_uvedit_select_mode_get(scene);
Object *obedit = CTX_data_edit_object(C);
const int object_index = RNA_int_get(op->ptr, "object_index");
const int index = RNA_int_get(op->ptr, "index");
if (object_index == -1) {
return OPERATOR_CANCELLED;
}
Object *obedit = ED_object_in_mode_from_index(scene, view_layer, OB_MODE_EDIT, object_index);
if (obedit == NULL) {
return OPERATOR_CANCELLED;
}
BMEditMesh *em = BKE_editmesh_from_object(obedit);
BMesh *bm = em->bm;
const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
float aspect_y;
{
float aspx, aspy;
ED_uvedit_get_aspect(obedit, &aspx, &aspy);
aspect_y = aspx / aspy;
}
const int index = RNA_int_get(op->ptr, "index");
BMElem *ele_src, *ele_dst;
if (uv_selectmode & UV_SELECT_FACE) {
@ -719,6 +743,14 @@ static int uv_shortest_path_pick_exec(bContext *C, wmOperator *op)
}
}
/* Always use the active object, not `obedit` as the active defines the UV display. */
float aspect_y;
{
float aspx, aspy;
ED_uvedit_get_aspect(CTX_data_edit_object(C), &aspx, &aspy);
aspect_y = aspx / aspy;
}
struct PathSelectParams op_params;
path_select_params_from_op(op, &op_params);
op_params.track_active = true;
@ -752,6 +784,8 @@ void UV_OT_shortest_path_pick(wmOperatorType *ot)
path_select_properties(ot);
/* use for redo */
prop = RNA_def_int(ot->srna, "object_index", -1, -1, INT_MAX, "", "", 0, INT_MAX);
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
prop = RNA_def_int(ot->srna, "index", -1, -1, INT_MAX, "", "", 0, INT_MAX);
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}