WIP: uv-simple-select (Version2) #2

Draft
Chris Blackbourn wants to merge 3 commits from uv-simple-select into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
4 changed files with 419 additions and 473 deletions

View File

@ -86,7 +86,6 @@ bool ED_uvedit_test(struct Object *obedit);
/* Visibility and selection tests. */ /* Visibility and selection tests. */
bool uvedit_face_visible_test_ex(const struct ToolSettings *ts, struct BMFace *efa);
bool uvedit_face_select_test_ex(const struct ToolSettings *ts, bool uvedit_face_select_test_ex(const struct ToolSettings *ts,
struct BMFace *efa, struct BMFace *efa,
BMUVOffsets offsets); BMUVOffsets offsets);
@ -194,30 +193,6 @@ void uvedit_uv_select_set_with_sticky(const struct Scene *scene,
bool do_history, bool do_history,
BMUVOffsets offsets); BMUVOffsets offsets);
/* Low level functions for sticky element selection (sticky mode independent). Type of sticky
* selection is specified explicitly (using sticky_flag, except for face selection). */
void uvedit_face_select_shared_vert(const struct Scene *scene,
struct BMEditMesh *em,
struct BMFace *efa,
const bool select,
const bool do_history,
BMUVOffsets offsets);
void uvedit_edge_select_shared_vert(const struct Scene *scene,
struct BMEditMesh *em,
struct BMLoop *l,
const bool select,
const int sticky_flag,
const bool do_history,
BMUVOffsets offsets);
void uvedit_uv_select_shared_vert(const struct Scene *scene,
struct BMEditMesh *em,
struct BMLoop *l,
const bool select,
const int sticky_flag,
const bool do_history,
BMUVOffsets offsets);
/* Sets required UV edge flags as specified by the sticky_flag. */ /* Sets required UV edge flags as specified by the sticky_flag. */
void uvedit_edge_select_set_noflush(const struct Scene *scene, void uvedit_edge_select_set_noflush(const struct Scene *scene,
struct BMLoop *l, struct BMLoop *l,
@ -259,19 +234,6 @@ bool ED_uvedit_nearest_uv_multi(const struct View2D *v2d,
float *dist_sq, float *dist_sq,
float r_uv[2]); float r_uv[2]);
struct BMFace **ED_uvedit_selected_faces(const struct Scene *scene,
struct BMesh *bm,
int len_max,
int *r_faces_len);
struct BMLoop **ED_uvedit_selected_edges(const struct Scene *scene,
struct BMesh *bm,
int len_max,
int *r_edges_len);
struct BMLoop **ED_uvedit_selected_verts(const struct Scene *scene,
struct BMesh *bm,
int len_max,
int *r_verts_len);
void ED_uvedit_get_aspect(struct Object *obedit, float *r_aspx, float *r_aspy); void ED_uvedit_get_aspect(struct Object *obedit, float *r_aspx, float *r_aspy);
/** /**

View File

@ -18,7 +18,7 @@ struct Scene;
struct SpaceImage; struct SpaceImage;
struct wmOperatorType; struct wmOperatorType;
/* find nearest */ /* UV Find Nearest */
typedef struct UvNearestHit { typedef struct UvNearestHit {
/** Only for `*_multi(..)` versions of functions. */ /** Only for `*_multi(..)` versions of functions. */
@ -30,9 +30,9 @@ typedef struct UvNearestHit {
* Needs to be set before calling nearest functions. * Needs to be set before calling nearest functions.
* *
* \note When #UV_NEAREST_HIT_INIT_DIST_PX or #UV_NEAREST_HIT_INIT_MAX are used, * \note When #UV_NEAREST_HIT_INIT_DIST_PX or #UV_NEAREST_HIT_INIT_MAX are used,
* this value is pixels squared. * Units are pixels.
*/ */
float dist_sq; float dist;
/** Scale the UVs to account for aspect ratio from the image view. */ /** Scale the UVs to account for aspect ratio from the image view. */
float scale[2]; float scale[2];
@ -40,7 +40,7 @@ typedef struct UvNearestHit {
#define UV_NEAREST_HIT_INIT_DIST_PX(v2d, dist_px) \ #define UV_NEAREST_HIT_INIT_DIST_PX(v2d, dist_px) \
{ \ { \
.dist_sq = square_f(U.pixelsize * dist_px), \ .dist = U.pixelsize * dist_px, \
.scale = { \ .scale = { \
UI_view2d_scale_get_x(v2d), \ UI_view2d_scale_get_x(v2d), \
UI_view2d_scale_get_y(v2d), \ UI_view2d_scale_get_y(v2d), \
@ -49,7 +49,7 @@ typedef struct UvNearestHit {
#define UV_NEAREST_HIT_INIT_MAX(v2d) \ #define UV_NEAREST_HIT_INIT_MAX(v2d) \
{ \ { \
.dist_sq = FLT_MAX, \ .dist = FLT_MAX, \
.scale = { \ .scale = { \
UI_view2d_scale_get_x(v2d), \ UI_view2d_scale_get_x(v2d), \
UI_view2d_scale_get_y(v2d), \ UI_view2d_scale_get_y(v2d), \
@ -80,34 +80,11 @@ bool uv_find_nearest_edge_multi(struct Scene *scene,
float penalty, float penalty,
struct UvNearestHit *hit); struct UvNearestHit *hit);
/**
* \param only_in_face: when true, only hit faces which `co` is inside.
* This gives users a result they might expect, especially when zoomed in.
*
* \note Concave faces can cause odd behavior, although in practice this isn't often an issue.
* The center can be outside the face, in this case the distance to the center
* could cause the face to be considered too far away.
* If this becomes an issue we could track the distance to the faces closest edge.
*/
bool uv_find_nearest_face_ex(struct Scene *scene,
struct Object *obedit,
const float co[2],
struct UvNearestHit *hit,
bool only_in_face);
bool uv_find_nearest_face(struct Scene *scene,
struct Object *obedit,
const float co[2],
struct UvNearestHit *hit);
bool uv_find_nearest_face_multi_ex(struct Scene *scene,
struct Object **objects,
uint objects_len,
const float co[2],
struct UvNearestHit *hit,
bool only_in_face);
bool uv_find_nearest_face_multi(struct Scene *scene, bool uv_find_nearest_face_multi(struct Scene *scene,
struct Object **objects, struct Object **objects,
uint objects_len, uint objects_len,
const float co[2], const float co[2],
float penalty,
struct UvNearestHit *hit); struct UvNearestHit *hit);
BMLoop *uv_find_nearest_loop_from_vert(struct Scene *scene, BMLoop *uv_find_nearest_loop_from_vert(struct Scene *scene,

View File

@ -577,7 +577,7 @@ static int uv_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmEve
UvNearestHit hit = UV_NEAREST_HIT_INIT_MAX(&region->v2d); UvNearestHit hit = UV_NEAREST_HIT_INIT_MAX(&region->v2d);
bool hit_found = false; bool hit_found = false;
if (uv_selectmode == UV_SELECT_FACE) { if (uv_selectmode == UV_SELECT_FACE) {
if (uv_find_nearest_face_multi(scene, objects, objects_len, co, &hit)) { if (uv_find_nearest_face_multi(scene, objects, objects_len, co, 0.0f, &hit)) {
hit_found = true; hit_found = true;
} }
} }
@ -787,6 +787,156 @@ void UV_OT_shortest_path_pick(wmOperatorType *ot)
/** \name Select Path Between Existing Selection /** \name Select Path Between Existing Selection
* \{ */ * \{ */
/* -------------------------------------------------------------------- */
/** \name Selected Elements as Arrays (Vertex, Edge & Faces)
*
* These functions return single elements per connected vertex/edge.
* So an edge that has two connected edge loops only assigns one loop in the array.
* \{ */
static BMFace **ED_uvedit_selected_faces(const Scene *scene,
BMesh *bm,
int len_max,
int *r_faces_len)
{
const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
int faces_len = 0;
BMFace **faces = MEM_mallocN(sizeof(*faces) * len_max, __func__);
BMIter iter;
BMFace *f;
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
if (uvedit_face_visible_test(scene, f) && uvedit_face_select_test(scene, f, offsets)) {
faces[faces_len++] = f;
if (faces_len == len_max) {
break;
}
}
}
*r_faces_len = faces_len;
return faces;
}
static BMLoop **ED_uvedit_selected_edges(const Scene *scene,
BMesh *bm,
int len_max,
int *r_edges_len)
{
const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
BLI_assert(offsets.uv >= 0);
CLAMP_MAX(len_max, bm->totloop);
int edges_len = 0;
BMLoop **edges = MEM_mallocN(sizeof(*edges) * len_max, __func__);
BMIter iter;
BMFace *f;
/* Clear tag. */
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
BMIter liter;
BMLoop *l_iter;
BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
BM_elem_flag_disable(l_iter, BM_ELEM_TAG);
}
}
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
if (uvedit_face_visible_test(scene, f)) {
BMIter liter;
BMLoop *l_iter;
BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
if (!BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
if (uvedit_edge_select_test(scene, l_iter, offsets)) {
BM_elem_flag_enable(l_iter, BM_ELEM_TAG);
edges[edges_len++] = l_iter;
if (edges_len == len_max) {
goto finally;
}
/* Tag other connected loops so we don't consider them separate edges. */
if (l_iter != l_iter->radial_next) {
BMLoop *l_radial_iter = l_iter->radial_next;
do {
if (BM_loop_uv_share_edge_check(l_iter, l_radial_iter, offsets.uv)) {
BM_elem_flag_enable(l_radial_iter, BM_ELEM_TAG);
}
} while ((l_radial_iter = l_radial_iter->radial_next) != l_iter);
}
}
}
}
}
}
finally:
*r_edges_len = edges_len;
return edges;
}
static BMLoop **ED_uvedit_selected_verts(const Scene *scene,
BMesh *bm,
int len_max,
int *r_verts_len)
{
const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
BLI_assert(offsets.select_vert >= 0);
BLI_assert(offsets.uv >= 0);
CLAMP_MAX(len_max, bm->totloop);
int verts_len = 0;
BMLoop **verts = MEM_mallocN(sizeof(*verts) * len_max, __func__);
BMIter iter;
BMFace *f;
/* Clear tag. */
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
BMIter liter;
BMLoop *l_iter;
BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
BM_elem_flag_disable(l_iter, BM_ELEM_TAG);
}
}
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
if (uvedit_face_visible_test(scene, f)) {
BMIter liter;
BMLoop *l_iter;
BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
if (!BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
if (BM_ELEM_CD_GET_BOOL(l_iter, offsets.select_vert)) {
BM_elem_flag_enable(l_iter->v, BM_ELEM_TAG);
verts[verts_len++] = l_iter;
if (verts_len == len_max) {
goto finally;
}
/* Tag other connected loops so we don't consider them separate vertices. */
BMIter liter_disk;
BMLoop *l_disk_iter;
BM_ITER_ELEM (l_disk_iter, &liter_disk, l_iter->v, BM_LOOPS_OF_VERT) {
if (BM_loop_uv_share_vert_check(l_iter, l_disk_iter, offsets.uv)) {
BM_elem_flag_enable(l_disk_iter, BM_ELEM_TAG);
}
}
}
}
}
}
}
finally:
*r_verts_len = verts_len;
return verts;
}
/** \} */
static int uv_shortest_path_select_exec(bContext *C, wmOperator *op) static int uv_shortest_path_select_exec(bContext *C, wmOperator *op)
{ {
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);

View File

@ -5,43 +5,22 @@
* \ingroup eduv * \ingroup eduv
*/ */
#include <math.h> #include "BKE_context.h"
#include <stdlib.h> #include "BKE_editmesh.h"
#include <string.h> #include "BKE_layer.h"
#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "BKE_report.h"
#include "MEM_guardedalloc.h"
#include "DNA_image_types.h"
#include "DNA_material_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_space_types.h"
#include "BLI_alloca.h"
#include "BLI_blenlib.h"
#include "BLI_hash.h" #include "BLI_hash.h"
#include "BLI_heap.h" #include "BLI_heap.h"
#include "BLI_kdopbvh.h" #include "BLI_kdopbvh.h"
#include "BLI_kdtree.h" #include "BLI_kdtree.h"
#include "BLI_lasso_2d.h" #include "BLI_lasso_2d.h"
#include "BLI_math.h"
#include "BLI_memarena.h" #include "BLI_memarena.h"
#include "BLI_polyfill_2d.h" #include "BLI_polyfill_2d.h"
#include "BLI_polyfill_2d_beautify.h" #include "BLI_polyfill_2d_beautify.h"
#include "BLI_utildefines.h"
#include "BKE_context.h"
#include "BKE_customdata.h"
#include "BKE_editmesh.h"
#include "BKE_layer.h"
#include "BKE_material.h"
#include "BKE_mesh.h"
#include "BKE_mesh_mapping.h"
#include "BKE_report.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h" #include "DEG_depsgraph_query.h"
#include "ED_image.h" #include "ED_image.h"
@ -50,28 +29,19 @@
#include "ED_select_utils.h" #include "ED_select_utils.h"
#include "ED_uvedit.h" #include "ED_uvedit.h"
#include "MEM_guardedalloc.h"
#include "RNA_access.h" #include "RNA_access.h"
#include "RNA_define.h" #include "RNA_define.h"
#include "RNA_enum_types.h" #include "RNA_enum_types.h"
#include "WM_api.h"
#include "WM_types.h"
#include "UI_view2d.h" #include "UI_view2d.h"
#include "uvedit_intern.h" #include "uvedit_intern.h"
static void uv_select_all_perform(const Scene *scene, Object *obedit, int action); #include "WM_api.h"
static void uv_select_all_perform_multi_ex(const Scene *scene, static void uv_select_all_perform(const Scene *scene, Object *obedit, int action);
Object **objects,
const uint objects_len,
int action,
const Object *ob_exclude);
static void uv_select_all_perform_multi(const Scene *scene,
Object **objects,
const uint objects_len,
int action);
static void uv_select_flush_from_tag_face(const Scene *scene, Object *obedit, const bool select); static void uv_select_flush_from_tag_face(const Scene *scene, Object *obedit, const bool select);
static void uv_select_flush_from_tag_loop(const Scene *scene, Object *obedit, const bool select); static void uv_select_flush_from_tag_loop(const Scene *scene, Object *obedit, const bool select);
@ -81,6 +51,30 @@ static void uv_select_tag_update_for_object(Depsgraph *depsgraph,
const ToolSettings *ts, const ToolSettings *ts,
Object *obedit); Object *obedit);
/* Low level functions for sticky element selection (sticky mode independent). Type of sticky
* selection is specified explicitly (using sticky_flag, except for face selection). */
static void uvedit_face_select_shared_vert(const struct Scene *scene,
struct BMEditMesh *em,
struct BMFace *efa,
const bool select,
const bool do_history,
BMUVOffsets offsets);
static void uvedit_edge_select_shared_vert(const struct Scene *scene,
struct BMEditMesh *em,
struct BMLoop *l,
const bool select,
const int sticky_flag,
const bool do_history,
BMUVOffsets offsets);
static void uvedit_uv_select_shared_vert(const struct Scene *scene,
struct BMEditMesh *em,
struct BMLoop *l,
const bool select,
const int sticky_flag,
const bool do_history,
BMUVOffsets offsets);
typedef enum { typedef enum {
UV_SSIM_AREA_UV = 1000, UV_SSIM_AREA_UV = 1000,
UV_SSIM_AREA_3D, UV_SSIM_AREA_3D,
@ -153,32 +147,47 @@ BMLoop *ED_uvedit_active_edge_loop_get(BMesh *bm)
char ED_uvedit_select_mode_get(const Scene *scene) char ED_uvedit_select_mode_get(const Scene *scene)
{ {
/* Returns a single element selection mode (Vertex, Edge or Face) based on
* Sync Selection, and potentially multiple selected modes in the UI. */
const ToolSettings *ts = scene->toolsettings; const ToolSettings *ts = scene->toolsettings;
char uv_selectmode = UV_SELECT_VERTEX; char uv_select_mode = ts->uv_selectmode; /* i.e. Default from UV Editor. */
if (ts->uv_flag & UV_SYNC_SELECTION) { if (ts->uv_flag & UV_SYNC_SELECTION) {
/* We're in Sync-Selection mode.
* Convert the selection mode options from the 3D Viewport
* into their closest UV Editor equivalents. */
uv_select_mode = 0;
if (ts->selectmode & SCE_SELECT_VERTEX) { if (ts->selectmode & SCE_SELECT_VERTEX) {
uv_selectmode = UV_SELECT_VERTEX; uv_select_mode |= UV_SELECT_VERTEX;
} }
else if (ts->selectmode & SCE_SELECT_EDGE) { if (ts->selectmode & SCE_SELECT_EDGE) {
uv_selectmode = UV_SELECT_EDGE; uv_select_mode |= UV_SELECT_EDGE;
} }
else if (ts->selectmode & SCE_SELECT_FACE) { if (ts->selectmode & SCE_SELECT_FACE) {
uv_selectmode = UV_SELECT_FACE; uv_select_mode |= UV_SELECT_FACE;
} }
} }
else {
if (ts->uv_selectmode & UV_SELECT_VERTEX) { /* Order is important here, as more than one type of selection element could be enabled. */
uv_selectmode = UV_SELECT_VERTEX; if (uv_select_mode & UV_SELECT_VERTEX) {
return UV_SELECT_VERTEX; /* Vertex selection is enabled, use Vertex mode. */
} }
else if (ts->uv_selectmode & UV_SELECT_EDGE) { if (uv_select_mode & UV_SELECT_EDGE) {
uv_selectmode = UV_SELECT_EDGE; return UV_SELECT_EDGE; /* Edge selection is enabled, use Edge mode. */
} }
else if (ts->uv_selectmode & UV_SELECT_FACE) { if (uv_select_mode & UV_SELECT_FACE) {
uv_selectmode = UV_SELECT_FACE; return UV_SELECT_FACE; /* Face selection is enabled, use Face mode. */
} }
if (uv_select_mode & UV_SELECT_ISLAND) {
/* Island selection is stored as Vertex selection internally. */
return UV_SELECT_VERTEX; /* Not UV_SELECT_ISLAND ! */
} }
return uv_selectmode;
BLI_assert_unreachable();
return UV_SELECT_VERTEX; /* Should never happen. */
} }
void ED_uvedit_select_sync_flush(const ToolSettings *ts, BMEditMesh *em, const bool select) void ED_uvedit_select_sync_flush(const ToolSettings *ts, BMEditMesh *em, const bool select)
@ -202,7 +211,7 @@ void ED_uvedit_select_sync_flush(const ToolSettings *ts, BMEditMesh *em, const b
static void uvedit_vertex_select_tagged(BMEditMesh *em, static void uvedit_vertex_select_tagged(BMEditMesh *em,
Scene *scene, Scene *scene,
bool select, const bool select,
const BMUVOffsets offsets) const BMUVOffsets offsets)
{ {
BMFace *efa; BMFace *efa;
@ -218,16 +227,18 @@ static void uvedit_vertex_select_tagged(BMEditMesh *em,
} }
} }
bool uvedit_face_visible_test_ex(const ToolSettings *ts, BMFace *efa)
{
if (ts->uv_flag & UV_SYNC_SELECTION) {
return (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) == 0);
}
return (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) == 0 && BM_elem_flag_test(efa, BM_ELEM_SELECT));
}
bool uvedit_face_visible_test(const Scene *scene, BMFace *efa) bool uvedit_face_visible_test(const Scene *scene, BMFace *efa)
{ {
return uvedit_face_visible_test_ex(scene->toolsettings, efa); if (BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
/* Face Hidden flag always applies to both 3D viewport *and* UV Editor. */
return false;
}
if (scene->toolsettings->uv_flag & UV_SYNC_SELECTION) {
/* UV Sync Selection mode, ignore 3D Viewport selection for visibility in UV Editor. */
return true;
}
/* No sync-selection, only *selected* faces in 3D Viewport are visible in UV Editor. */
return BM_elem_flag_test(efa, BM_ELEM_SELECT);
} }
bool uvedit_face_select_test_ex(const ToolSettings *ts, BMFace *efa, const BMUVOffsets offsets) bool uvedit_face_select_test_ex(const ToolSettings *ts, BMFace *efa, const BMUVOffsets offsets)
@ -807,6 +818,33 @@ static BMLoop *uvedit_loop_find_other_boundary_loop_with_visible_face(const Scen
/** \name Find Nearest Elements /** \name Find Nearest Elements
* \{ */ * \{ */
static bool uv_nearest_was_updated(const UvNearestHit *hit)
{
return hit->efa;
}
static void uv_nearest_hit_update(
UvNearestHit *hit, Object *ob, BMFace *efa, BMLoop *l, const float dist)
{
BLI_assert(ob != NULL);
BLI_assert(efa != NULL);
BLI_assert(dist >= 0.0f);
if (dist < hit->dist) {
hit->ob = ob;
hit->efa = efa;
hit->l = l; /* Can be NULL. */
hit->dist = dist;
}
}
static float uv_nearest_calc_dist(const UvNearestHit *hit, const float co[2], const float uv[2])
{
float delta[2];
sub_v2_v2v2(delta, co, uv);
mul_v2_v2(delta, hit->scale);
return len_v2(delta);
}
bool uv_find_nearest_edge( bool uv_find_nearest_edge(
Scene *scene, Object *obedit, const float co[2], const float penalty, UvNearestHit *hit) Scene *scene, Object *obedit, const float co[2], const float penalty, UvNearestHit *hit)
{ {
@ -817,7 +855,6 @@ bool uv_find_nearest_edge(
BMIter iter, liter; BMIter iter, liter;
float *luv, *luv_next; float *luv, *luv_next;
int i; int i;
bool found = false;
const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm); const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
BLI_assert(offsets.uv >= 0); BLI_assert(offsets.uv >= 0);
@ -834,29 +871,17 @@ bool uv_find_nearest_edge(
float delta[2]; float delta[2];
closest_to_line_segment_v2(delta, co, luv, luv_next); closest_to_line_segment_v2(delta, co, luv, luv_next);
float dist_test = uv_nearest_calc_dist(hit, co, delta);
sub_v2_v2(delta, co);
mul_v2_v2(delta, hit->scale);
float dist_test_sq = len_squared_v2(delta);
/* Ensures that successive selection attempts will select other edges sharing the same /* Ensures that successive selection attempts will select other edges sharing the same
* UV coordinates as the previous selection. */ * UV coordinates as the previous selection. */
if ((penalty != 0.0f) && uvedit_edge_select_test(scene, l, offsets)) { if ((penalty != 0.0f) && uvedit_edge_select_test(scene, l, offsets)) {
dist_test_sq = square_f(sqrtf(dist_test_sq) + penalty); dist_test += penalty;
} }
if (dist_test_sq < hit->dist_sq) { uv_nearest_hit_update(hit, obedit, efa, l, dist_test);
hit->ob = obedit;
hit->efa = efa;
hit->l = l;
hit->dist_sq = dist_test_sq;
found = true;
} }
} }
} return uv_nearest_was_updated(hit);
return found;
} }
bool uv_find_nearest_edge_multi(Scene *scene, bool uv_find_nearest_edge_multi(Scene *scene,
@ -866,85 +891,64 @@ bool uv_find_nearest_edge_multi(Scene *scene,
const float penalty, const float penalty,
UvNearestHit *hit) UvNearestHit *hit)
{ {
bool found = false;
for (uint ob_index = 0; ob_index < objects_len; ob_index++) { for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index]; uv_find_nearest_edge(scene, objects[ob_index], co, penalty, hit);
if (uv_find_nearest_edge(scene, obedit, co, penalty, hit)) {
found = true;
} }
} return uv_nearest_was_updated(hit);
return found;
} }
bool uv_find_nearest_face_ex( static bool uv_find_nearest_face(
Scene *scene, Object *obedit, const float co[2], UvNearestHit *hit, const bool only_in_face) Scene *scene, Object *obedit, const float co[2], const float penalty_dist, UvNearestHit *hit)
{ {
BLI_assert((hit->scale[0] > 0.0f) && (hit->scale[1] > 0.0f)); BLI_assert((hit->scale[0] > 0.0f) && (hit->scale[1] > 0.0f));
BMEditMesh *em = BKE_editmesh_from_object(obedit); BMEditMesh *em = BKE_editmesh_from_object(obedit);
bool found = false;
const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_PROP_FLOAT2); const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
BMIter iter; BMIter iter;
BMFace *efa; BMFace *efa;
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
if (!uvedit_face_visible_test(scene, efa)) { if (!uvedit_face_visible_test(scene, efa)) {
continue; continue;
} }
float cent[2]; /* Ensures that successive selection attempts will select other edges sharing the same
BM_face_uv_calc_center_median(efa, cd_loop_uv_offset, cent); * UV coordinates as the previous selection. */
const float face_penalty_dist = uvedit_face_select_test(scene, efa, offsets) ? penalty_dist :
0.0f;
float delta[2]; if (BM_face_uv_point_inside_test(efa, co, offsets.uv)) {
sub_v2_v2v2(delta, co, cent); uv_nearest_hit_update(hit, obedit, efa, NULL, face_penalty_dist);
mul_v2_v2(delta, hit->scale);
const float dist_test_sq = len_squared_v2(delta);
if (dist_test_sq < hit->dist_sq) {
if (only_in_face) {
if (!BM_face_uv_point_inside_test(efa, co, cd_loop_uv_offset)) {
continue; continue;
} }
}
hit->ob = obedit; BMLoop *l;
hit->efa = efa; BMIter liter;
hit->dist_sq = dist_test_sq; BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
found = true; float *luv = BM_ELEM_CD_GET_VOID_P(l, offsets.uv);
float *luv_next = BM_ELEM_CD_GET_VOID_P(l->next, offsets.uv);
float delta[2];
closest_to_line_segment_v2(delta, co, luv, luv_next);
sub_v2_v2(delta, co);
mul_v2_v2(delta, hit->scale);
uv_nearest_hit_update(hit, obedit, efa, NULL, len_v2(delta) + face_penalty_dist);
} }
} }
return found; return uv_nearest_was_updated(hit);
} }
bool uv_find_nearest_face(Scene *scene, Object *obedit, const float co[2], UvNearestHit *hit) bool uv_find_nearest_face_multi(Scene *scene,
{
return uv_find_nearest_face_ex(scene, obedit, co, hit, false);
}
bool uv_find_nearest_face_multi_ex(Scene *scene,
Object **objects, Object **objects,
const uint objects_len, uint objects_len,
const float co[2], const float co[2],
UvNearestHit *hit, const float penalty_dist,
const bool only_in_face) UvNearestHit *hit)
{ {
bool found = false; for (int ob_index = 0; ob_index < objects_len; ob_index++) {
for (uint ob_index = 0; ob_index < objects_len; ob_index++) { uv_find_nearest_face(scene, objects[ob_index], co, penalty_dist, hit);
Object *obedit = objects[ob_index];
if (uv_find_nearest_face_ex(scene, obedit, co, hit, only_in_face)) {
found = true;
} }
} return uv_nearest_was_updated(hit);
return found;
}
bool uv_find_nearest_face_multi(
Scene *scene, Object **objects, const uint objects_len, const float co[2], UvNearestHit *hit)
{
return uv_find_nearest_face_multi_ex(scene, objects, objects_len, co, hit, false);
} }
static bool uv_nearest_between(const BMLoop *l, const float co[2], const int cd_loop_uv_offset) static bool uv_nearest_between(const BMLoop *l, const float co[2], const int cd_loop_uv_offset)
@ -961,7 +965,6 @@ bool uv_find_nearest_vert(
Scene *scene, Object *obedit, float const co[2], const float penalty_dist, UvNearestHit *hit) Scene *scene, Object *obedit, float const co[2], const float penalty_dist, UvNearestHit *hit)
{ {
BLI_assert((hit->scale[0] > 0.0f) && (hit->scale[1] > 0.0f)); BLI_assert((hit->scale[0] > 0.0f) && (hit->scale[1] > 0.0f));
bool found = false;
BMEditMesh *em = BKE_editmesh_from_object(obedit); BMEditMesh *em = BKE_editmesh_from_object(obedit);
BMFace *efa; BMFace *efa;
@ -981,39 +984,30 @@ bool uv_find_nearest_vert(
BMLoop *l; BMLoop *l;
int i; int i;
BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) { BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv); const float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
float dist_test = uv_nearest_calc_dist(hit, co, luv);
float delta[2];
sub_v2_v2v2(delta, co, luv);
mul_v2_v2(delta, hit->scale);
float dist_test_sq = len_squared_v2(delta);
/* Ensures that successive selection attempts will select other vertices sharing the same /* Ensures that successive selection attempts will select other vertices sharing the same
* UV coordinates */ * UV coordinates */
if ((penalty_dist != 0.0f) && uvedit_uv_select_test(scene, l, offsets)) { if ((penalty_dist != 0.0f) && uvedit_uv_select_test(scene, l, offsets)) {
dist_test_sq = square_f(sqrtf(dist_test_sq) + penalty_dist); dist_test += penalty_dist;
} }
if (dist_test_sq <= hit->dist_sq) { if (dist_test == hit->dist) {
if (dist_test_sq == hit->dist_sq) { /* Special case to break ties. */
if (!uv_nearest_between(l, co, offsets.uv)) { if (uv_nearest_between(l, co, offsets.uv)) {
continue;
}
}
hit->dist_sq = dist_test_sq;
hit->ob = obedit; hit->ob = obedit;
hit->efa = efa; hit->efa = efa;
hit->l = l; hit->l = l;
found = true;
} }
continue;
}
uv_nearest_hit_update(hit, obedit, efa, l, dist_test);
} }
} }
return found; return uv_nearest_was_updated(hit);
} }
bool uv_find_nearest_vert_multi(Scene *scene, bool uv_find_nearest_vert_multi(Scene *scene,
@ -2433,8 +2427,8 @@ static bool uv_mouse_select_multi(bContext *C,
Scene *scene = CTX_data_scene(C); Scene *scene = CTX_data_scene(C);
const ToolSettings *ts = scene->toolsettings; const ToolSettings *ts = scene->toolsettings;
UvNearestHit hit = UV_NEAREST_HIT_INIT_DIST_PX(&region->v2d, 75.0f); UvNearestHit hit = UV_NEAREST_HIT_INIT_DIST_PX(&region->v2d, 75.0f);
int selectmode, sticky; int selectmode = ts->uv_selectmode;
bool found_item = false; int sticky = ts->uv_sticky;
/* 0 == don't flush, 1 == sel, -1 == deselect; only use when selection sync is enabled. */ /* 0 == don't flush, 1 == sel, -1 == deselect; only use when selection sync is enabled. */
int flush = 0; int flush = 0;
@ -2456,16 +2450,11 @@ static bool uv_mouse_select_multi(bContext *C,
sticky = SI_STICKY_DISABLE; sticky = SI_STICKY_DISABLE;
} }
else {
selectmode = ts->uv_selectmode;
sticky = ts->uv_sticky;
}
/* find nearest element */ /* find nearest element */
if (selectmode == UV_SELECT_VERTEX) { if (selectmode == UV_SELECT_VERTEX) {
/* find vertex */ /* find vertex */
found_item = uv_find_nearest_vert_multi(scene, objects, objects_len, co, penalty_dist, &hit); if (uv_find_nearest_vert_multi(scene, objects, objects_len, co, penalty_dist, &hit)) {
if (found_item) {
if ((ts->uv_flag & UV_SYNC_SELECTION) == 0) { if ((ts->uv_flag & UV_SYNC_SELECTION) == 0) {
BMesh *bm = BKE_editmesh_from_object(hit.ob)->bm; BMesh *bm = BKE_editmesh_from_object(hit.ob)->bm;
ED_uvedit_active_vert_loop_set(bm, hit.l); ED_uvedit_active_vert_loop_set(bm, hit.l);
@ -2474,8 +2463,7 @@ static bool uv_mouse_select_multi(bContext *C,
} }
else if (selectmode == UV_SELECT_EDGE) { else if (selectmode == UV_SELECT_EDGE) {
/* find edge */ /* find edge */
found_item = uv_find_nearest_edge_multi(scene, objects, objects_len, co, penalty_dist, &hit); if (uv_find_nearest_edge_multi(scene, objects, objects_len, co, penalty_dist, &hit)) {
if (found_item) {
if ((ts->uv_flag & UV_SYNC_SELECTION) == 0) { if ((ts->uv_flag & UV_SYNC_SELECTION) == 0) {
BMesh *bm = BKE_editmesh_from_object(hit.ob)->bm; BMesh *bm = BKE_editmesh_from_object(hit.ob)->bm;
ED_uvedit_active_edge_loop_set(bm, hit.l); ED_uvedit_active_edge_loop_set(bm, hit.l);
@ -2484,33 +2472,18 @@ static bool uv_mouse_select_multi(bContext *C,
} }
else if (selectmode == UV_SELECT_FACE) { else if (selectmode == UV_SELECT_FACE) {
/* find face */ /* find face */
found_item = uv_find_nearest_face_multi(scene, objects, objects_len, co, &hit); const bool found_item = uv_find_nearest_face_multi(
scene, objects, objects_len, co, penalty_dist, &hit);
if (!found_item) {
/* Fallback, perform a second pass without a limited threshold,
* which succeeds as long as the cursor is inside the UV face.
* Useful when zoomed in, to select faces with distant screen-space face centers. */
hit.dist_sq = FLT_MAX;
found_item = uv_find_nearest_face_multi_ex(scene, objects, objects_len, co, &hit, true);
}
if (found_item) { if (found_item) {
BMesh *bm = BKE_editmesh_from_object(hit.ob)->bm; BMesh *bm = BKE_editmesh_from_object(hit.ob)->bm;
BM_mesh_active_face_set(bm, hit.efa); BM_mesh_active_face_set(bm, hit.efa);
} }
} }
else if (selectmode == UV_SELECT_ISLAND) { else if (selectmode == UV_SELECT_ISLAND) {
found_item = uv_find_nearest_edge_multi(scene, objects, objects_len, co, 0.0f, &hit); uv_find_nearest_face_multi(scene, objects, objects_len, co, penalty_dist, &hit);
if (!found_item) {
/* Without this, we can be within the face of an island but too far from an edge,
* see face selection comment for details. */
hit.dist_sq = FLT_MAX;
found_item = uv_find_nearest_face_multi_ex(scene, objects, objects_len, co, &hit, true);
}
} }
bool found = found_item; bool found = uv_nearest_was_updated(&hit);
bool changed = false; bool changed = false;
bool is_selected = false; bool is_selected = false;
@ -2528,9 +2501,8 @@ static bool uv_mouse_select_multi(bContext *C,
is_selected = uvedit_edge_select_test(scene, hit.l, offsets); is_selected = uvedit_edge_select_test(scene, hit.l, offsets);
} }
else { else {
/* Vertex or island. For island (if we were using #uv_find_nearest_face_multi_ex, see above), /* Vertex or island. */
* `hit.l` is NULL, use `hit.efa` instead. */ if (hit.l) {
if (hit.l != NULL) {
is_selected = uvedit_uv_select_test(scene, hit.l, offsets); is_selected = uvedit_uv_select_test(scene, hit.l, offsets);
} }
else { else {
@ -3494,6 +3466,32 @@ static void uv_select_flush_from_loop_edge_flag(const Scene *scene, BMEditMesh *
/** \name Box Select Operator /** \name Box Select Operator
* \{ */ * \{ */
static bool uv_box_select_is_face_touching(Scene *scene,
BMFace *efa,
rctf rectf,
const int cd_loop_uv_offset)
{
/* First, are we visible? */
if (!uvedit_face_visible_test(scene, efa)) {
return false;
}
/* Next, are any of the edges inside the rectangle? */
BMLoop *l;
BMIter iter;
BM_ITER_ELEM (l, &iter, efa, BM_LOOPS_OF_FACE) {
float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
float *luv_next = BM_ELEM_CD_GET_FLOAT_P(l->next, cd_loop_uv_offset);
if (BLI_rctf_isect_segment(&rectf, luv_next, luv)) {
return true;
}
}
/* Last, is an arbitrary point of the rectangle in the interior of the face? */
float rectangle_corner[2] = {rectf.xmin, rectf.ymin};
return BM_face_uv_point_inside_test(efa, rectangle_corner, cd_loop_uv_offset);
}
static int uv_box_select_exec(bContext *C, wmOperator *op) static int uv_box_select_exec(bContext *C, wmOperator *op)
{ {
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
@ -3506,8 +3504,7 @@ static int uv_box_select_exec(bContext *C, wmOperator *op)
BMIter iter, liter; BMIter iter, liter;
float *luv; float *luv;
rctf rectf; rctf rectf;
bool pinned; const bool use_face_touch = ((ts->uv_flag & UV_SYNC_SELECTION) ?
const bool use_face_center = ((ts->uv_flag & UV_SYNC_SELECTION) ?
(ts->selectmode == SCE_SELECT_FACE) : (ts->selectmode == SCE_SELECT_FACE) :
(ts->uv_selectmode == UV_SELECT_FACE)); (ts->uv_selectmode == UV_SELECT_FACE));
const bool use_edge = ((ts->uv_flag & UV_SYNC_SELECTION) ? const bool use_edge = ((ts->uv_flag & UV_SYNC_SELECTION) ?
@ -3524,7 +3521,7 @@ static int uv_box_select_exec(bContext *C, wmOperator *op)
const bool select = (sel_op != SEL_OP_SUB); const bool select = (sel_op != SEL_OP_SUB);
const bool use_pre_deselect = SEL_OP_USE_PRE_DESELECT(sel_op); const bool use_pre_deselect = SEL_OP_USE_PRE_DESELECT(sel_op);
pinned = RNA_boolean_get(op->ptr, "pinned"); const bool pinned = RNA_boolean_get(op->ptr, "pinned");
bool changed_multi = false; bool changed_multi = false;
@ -3536,7 +3533,6 @@ static int uv_box_select_exec(bContext *C, wmOperator *op)
uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT); uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT);
} }
/* don't indent to avoid diff noise! */
for (uint ob_index = 0; ob_index < objects_len; ob_index++) { for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index]; Object *obedit = objects[ob_index];
BMEditMesh *em = BKE_editmesh_from_object(obedit); BMEditMesh *em = BKE_editmesh_from_object(obedit);
@ -3549,22 +3545,13 @@ static int uv_box_select_exec(bContext *C, wmOperator *op)
BM_uv_map_ensure_pin_attr(em->bm, active_uv_name); BM_uv_map_ensure_pin_attr(em->bm, active_uv_name);
const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm); const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
/* do actual selection */ /* Do actual selection. */
if (use_face_center && !pinned) { if (use_face_touch && !pinned) {
/* handle face selection mode */ /* Handle face selection mode. */
float cent[2];
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
/* assume not touched */ const bool touched = uv_box_select_is_face_touching(scene, efa, rectf, offsets.uv);
BM_elem_flag_disable(efa, BM_ELEM_TAG); BM_elem_flag_set(efa, BM_ELEM_TAG, touched);
changed |= touched;
if (uvedit_face_visible_test(scene, efa)) {
BM_face_uv_calc_center_median(efa, offsets.uv, cent);
if (BLI_rctf_isect_pt_v(&rectf, cent)) {
BM_elem_flag_enable(efa, BM_ELEM_TAG);
changed = true;
}
}
} }
/* (de)selects all tagged faces and deals with sticky modes */ /* (de)selects all tagged faces and deals with sticky modes */
@ -3735,6 +3722,26 @@ static bool uv_circle_select_is_edge_inside(const float uv_a[2],
return dist_squared_to_line_segment_v2((const float[2]){0.0f, 0.0f}, co_a, co_b) < 1.0f; return dist_squared_to_line_segment_v2((const float[2]){0.0f, 0.0f}, co_a, co_b) < 1.0f;
} }
static bool uv_circle_select_is_face_touching(BMFace *efa,
const float offset[2],
const float ellipse[2],
const int cd_loop_uv_offset)
{
/* First, are any of the edges inside the solid ellipse? */
BMLoop *l;
BMIter iter;
BM_ITER_ELEM (l, &iter, efa, BM_LOOPS_OF_FACE) {
float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
float *luv_next = BM_ELEM_CD_GET_FLOAT_P(l->next, cd_loop_uv_offset);
if (uv_circle_select_is_edge_inside(luv, luv_next, offset, ellipse)) {
return true;
}
}
/* Also, is the center of the ellipse in the interior of the face? */
return BM_face_uv_point_inside_test(efa, offset, cd_loop_uv_offset);
}
static int uv_circle_select_exec(bContext *C, wmOperator *op) static int uv_circle_select_exec(bContext *C, wmOperator *op)
{ {
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
@ -3751,9 +3758,6 @@ static int uv_circle_select_exec(bContext *C, wmOperator *op)
float zoomx, zoomy; float zoomx, zoomy;
float offset[2], ellipse[2]; float offset[2], ellipse[2];
const bool use_face_center = ((ts->uv_flag & UV_SYNC_SELECTION) ?
(ts->selectmode == SCE_SELECT_FACE) :
(ts->uv_selectmode == UV_SELECT_FACE));
const bool use_edge = ((ts->uv_flag & UV_SYNC_SELECTION) ? const bool use_edge = ((ts->uv_flag & UV_SYNC_SELECTION) ?
(ts->selectmode == SCE_SELECT_EDGE) : (ts->selectmode == SCE_SELECT_EDGE) :
(ts->uv_selectmode == UV_SELECT_EDGE)); (ts->uv_selectmode == UV_SELECT_EDGE));
@ -3801,19 +3805,13 @@ static int uv_circle_select_exec(bContext *C, wmOperator *op)
BM_uv_map_ensure_edge_select_attr(em->bm, active_uv_name); BM_uv_map_ensure_edge_select_attr(em->bm, active_uv_name);
const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm); const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
/* do selection */ /* Do selection. */
if (use_face_center) { if (ED_uvedit_select_mode_get(scene) == UV_SELECT_FACE) {
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
BM_elem_flag_disable(efa, BM_ELEM_TAG); const bool touched = select != uvedit_face_select_test(scene, efa, offsets) &&
/* assume not touched */ uv_circle_select_is_face_touching(efa, offset, ellipse, offsets.uv);
if (select != uvedit_face_select_test(scene, efa, offsets)) { BM_elem_flag_set(efa, BM_ELEM_TAG, touched);
float cent[2]; changed |= touched;
BM_face_uv_calc_center_median(efa, offsets.uv, cent);
if (uv_circle_select_is_point_inside(cent, offset, ellipse)) {
BM_elem_flag_enable(efa, BM_ELEM_TAG);
changed = true;
}
}
} }
/* (de)selects all tagged faces and deals with sticky modes */ /* (de)selects all tagged faces and deals with sticky modes */
@ -3944,14 +3942,35 @@ static bool do_lasso_select_mesh_uv_is_edge_inside(const ARegion *region,
const float co_test_b[2]) const float co_test_b[2])
{ {
int co_screen_a[2], co_screen_b[2]; int co_screen_a[2], co_screen_b[2];
if (UI_view2d_view_to_region_segment_clip( return UI_view2d_view_to_region_segment_clip(
&region->v2d, co_test_a, co_test_b, co_screen_a, co_screen_b) && &region->v2d, co_test_a, co_test_b, co_screen_a, co_screen_b) &&
BLI_rcti_isect_segment(clip_rect, co_screen_a, co_screen_b) && BLI_rcti_isect_segment(clip_rect, co_screen_a, co_screen_b) &&
BLI_lasso_is_edge_inside( BLI_lasso_is_edge_inside(
mcoords, mcoords_len, UNPACK2(co_screen_a), UNPACK2(co_screen_b), V2D_IS_CLIPPED)) { mcoords, mcoords_len, UNPACK2(co_screen_a), UNPACK2(co_screen_b), V2D_IS_CLIPPED);
}
static bool uv_lasso_select_is_face_touching(BMFace *efa,
const ARegion *region,
const rcti *clip_rect,
const int mcoords[][2],
const int mcoords_len,
const int cd_loop_uv_offset)
{
/* First, are any of the face edges inside the lasso? */
BMLoop *l;
BMIter iter;
BM_ITER_ELEM (l, &iter, efa, BM_LOOPS_OF_FACE) {
float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
float *luv_next = BM_ELEM_CD_GET_FLOAT_P(l->next, cd_loop_uv_offset);
if (do_lasso_select_mesh_uv_is_edge_inside(
region, clip_rect, mcoords, mcoords_len, luv, luv_next)) {
return true; return true;
} }
return false; }
/* Also, is an arbitrary point of the lasso in the interior of the face? */
float lasso_corner[2] = {mcoords[0][0], mcoords[0][1]};
return BM_face_uv_point_inside_test(efa, lasso_corner, cd_loop_uv_offset);
} }
static bool do_lasso_select_mesh_uv(bContext *C, static bool do_lasso_select_mesh_uv(bContext *C,
@ -3964,9 +3983,6 @@ static bool do_lasso_select_mesh_uv(bContext *C,
Scene *scene = CTX_data_scene(C); Scene *scene = CTX_data_scene(C);
const ToolSettings *ts = scene->toolsettings; const ToolSettings *ts = scene->toolsettings;
ViewLayer *view_layer = CTX_data_view_layer(C); ViewLayer *view_layer = CTX_data_view_layer(C);
const bool use_face_center = ((ts->uv_flag & UV_SYNC_SELECTION) ?
(ts->selectmode == SCE_SELECT_FACE) :
(ts->uv_selectmode == UV_SELECT_FACE));
const bool use_edge = ((ts->uv_flag & UV_SYNC_SELECTION) ? const bool use_edge = ((ts->uv_flag & UV_SYNC_SELECTION) ?
(ts->selectmode == SCE_SELECT_EDGE) : (ts->selectmode == SCE_SELECT_EDGE) :
(ts->uv_selectmode == UV_SELECT_EDGE)); (ts->uv_selectmode == UV_SELECT_EDGE));
@ -4005,18 +4021,13 @@ static bool do_lasso_select_mesh_uv(bContext *C,
BM_uv_map_ensure_edge_select_attr(em->bm, active_uv_name); BM_uv_map_ensure_edge_select_attr(em->bm, active_uv_name);
const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm); const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
if (use_face_center) { /* Face Center Select. */ if (ED_uvedit_select_mode_get(scene) == UV_SELECT_FACE) { /* Face Touch Select. */
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
BM_elem_flag_disable(efa, BM_ELEM_TAG); const bool touched = select != uvedit_face_select_test(scene, efa, offsets) &&
/* assume not touched */ uv_lasso_select_is_face_touching(
if (select != uvedit_face_select_test(scene, efa, offsets)) { efa, region, &rect, mcoords, mcoords_len, offsets.uv);
float cent[2]; BM_elem_flag_set(efa, BM_ELEM_TAG, touched);
BM_face_uv_calc_center_median(efa, offsets.uv, cent); changed |= touched;
if (do_lasso_select_mesh_uv_is_point_inside(region, &rect, mcoords, mcoords_len, cent)) {
BM_elem_flag_enable(efa, BM_ELEM_TAG);
changed = true;
}
}
} }
/* (de)selects all tagged faces and deals with sticky modes */ /* (de)selects all tagged faces and deals with sticky modes */
@ -4341,7 +4352,7 @@ static int uv_select_overlap(bContext *C, const bool extend)
BMIter iter; BMIter iter;
BMFace *efa; BMFace *efa;
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
if (!uvedit_face_visible_test_ex(scene->toolsettings, efa)) { if (!uvedit_face_visible_test(scene, efa)) {
continue; continue;
} }
uv_tri_len += efa->len - 2; uv_tri_len += efa->len - 2;
@ -4375,7 +4386,7 @@ static int uv_select_overlap(bContext *C, const bool extend)
int face_index; int face_index;
BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, face_index) { BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, face_index) {
if (!uvedit_face_visible_test_ex(scene->toolsettings, efa)) { if (!uvedit_face_visible_test(scene, efa)) {
continue; continue;
} }
@ -5245,160 +5256,6 @@ void UV_OT_select_similar(wmOperatorType *ot)
/** \} */ /** \} */
/* -------------------------------------------------------------------- */
/** \name Selected Elements as Arrays (Vertex, Edge & Faces)
*
* These functions return single elements per connected vertex/edge.
* So an edge that has two connected edge loops only assigns one loop in the array.
* \{ */
BMFace **ED_uvedit_selected_faces(const Scene *scene, BMesh *bm, int len_max, int *r_faces_len)
{
const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
CLAMP_MAX(len_max, bm->totface);
int faces_len = 0;
BMFace **faces = MEM_mallocN(sizeof(*faces) * len_max, __func__);
BMIter iter;
BMFace *f;
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
if (uvedit_face_visible_test(scene, f)) {
if (uvedit_face_select_test(scene, f, offsets)) {
faces[faces_len++] = f;
if (faces_len == len_max) {
goto finally;
}
}
}
}
finally:
*r_faces_len = faces_len;
if (faces_len != len_max) {
faces = MEM_reallocN(faces, sizeof(*faces) * faces_len);
}
return faces;
}
BMLoop **ED_uvedit_selected_edges(const Scene *scene, BMesh *bm, int len_max, int *r_edges_len)
{
const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
BLI_assert(offsets.uv >= 0);
CLAMP_MAX(len_max, bm->totloop);
int edges_len = 0;
BMLoop **edges = MEM_mallocN(sizeof(*edges) * len_max, __func__);
BMIter iter;
BMFace *f;
/* Clear tag. */
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
BMIter liter;
BMLoop *l_iter;
BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
BM_elem_flag_disable(l_iter, BM_ELEM_TAG);
}
}
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
if (uvedit_face_visible_test(scene, f)) {
BMIter liter;
BMLoop *l_iter;
BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
if (!BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
if (uvedit_edge_select_test(scene, l_iter, offsets)) {
BM_elem_flag_enable(l_iter, BM_ELEM_TAG);
edges[edges_len++] = l_iter;
if (edges_len == len_max) {
goto finally;
}
/* Tag other connected loops so we don't consider them separate edges. */
if (l_iter != l_iter->radial_next) {
BMLoop *l_radial_iter = l_iter->radial_next;
do {
if (BM_loop_uv_share_edge_check(l_iter, l_radial_iter, offsets.uv)) {
BM_elem_flag_enable(l_radial_iter, BM_ELEM_TAG);
}
} while ((l_radial_iter = l_radial_iter->radial_next) != l_iter);
}
}
}
}
}
}
finally:
*r_edges_len = edges_len;
if (edges_len != len_max) {
edges = MEM_reallocN(edges, sizeof(*edges) * edges_len);
}
return edges;
}
BMLoop **ED_uvedit_selected_verts(const Scene *scene, BMesh *bm, int len_max, int *r_verts_len)
{
const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
BLI_assert(offsets.select_vert >= 0);
BLI_assert(offsets.uv >= 0);
CLAMP_MAX(len_max, bm->totloop);
int verts_len = 0;
BMLoop **verts = MEM_mallocN(sizeof(*verts) * len_max, __func__);
BMIter iter;
BMFace *f;
/* Clear tag. */
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
BMIter liter;
BMLoop *l_iter;
BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
BM_elem_flag_disable(l_iter, BM_ELEM_TAG);
}
}
BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
if (uvedit_face_visible_test(scene, f)) {
BMIter liter;
BMLoop *l_iter;
BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
if (!BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
if (BM_ELEM_CD_GET_BOOL(l_iter, offsets.select_vert)) {
BM_elem_flag_enable(l_iter->v, BM_ELEM_TAG);
verts[verts_len++] = l_iter;
if (verts_len == len_max) {
goto finally;
}
/* Tag other connected loops so we don't consider them separate vertices. */
BMIter liter_disk;
BMLoop *l_disk_iter;
BM_ITER_ELEM (l_disk_iter, &liter_disk, l_iter->v, BM_LOOPS_OF_VERT) {
if (BM_loop_uv_share_vert_check(l_iter, l_disk_iter, offsets.uv)) {
BM_elem_flag_enable(l_disk_iter, BM_ELEM_TAG);
}
}
}
}
}
}
}
finally:
*r_verts_len = verts_len;
if (verts_len != len_max) {
verts = MEM_reallocN(verts, sizeof(*verts) * verts_len);
}
return verts;
}
/** \} */
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */
/** \name Select Mode UV Vert/Edge/Face/Island Operator /** \name Select Mode UV Vert/Edge/Face/Island Operator
* \{ */ * \{ */