Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/blender/draw/engines/overlay/shaders/edit_uv_edges_geom.glsl15
-rw-r--r--source/blender/draw/engines/overlay/shaders/edit_uv_edges_vert.glsl3
-rw-r--r--source/blender/editors/include/ED_uvedit.h14
-rw-r--r--source/blender/editors/uvedit/uvedit_intern.h13
-rw-r--r--source/blender/editors/uvedit/uvedit_path.c2
-rw-r--r--source/blender/editors/uvedit/uvedit_select.c971
-rw-r--r--source/blender/editors/uvedit/uvedit_smart_stitch.c2
-rw-r--r--source/blender/makesdna/DNA_meshdata_types.h2
-rw-r--r--source/blender/makesrna/intern/rna_mesh.c4
-rw-r--r--source/blender/python/bmesh/bmesh_py_types_meshdata.c6
10 files changed, 874 insertions, 158 deletions
diff --git a/source/blender/draw/engines/overlay/shaders/edit_uv_edges_geom.glsl b/source/blender/draw/engines/overlay/shaders/edit_uv_edges_geom.glsl
index 4f8d553a220..2b4ddaa52f3 100644
--- a/source/blender/draw/engines/overlay/shaders/edit_uv_edges_geom.glsl
+++ b/source/blender/draw/engines/overlay/shaders/edit_uv_edges_geom.glsl
@@ -2,9 +2,10 @@
#pragma BLENDER_REQUIRE(common_overlay_lib.glsl)
layout(lines) in;
-layout(triangle_strip, max_vertices = 4) out;
+layout(triangle_strip, max_vertices = 6) out;
in float selectionFac[2];
+in float edgeSelectionFac[2];
flat in vec2 stippleStart[2];
noperspective in vec2 stipplePos[2];
@@ -35,6 +36,12 @@ void main()
vec2 ss_pos[2];
vec4 pos0 = gl_in[0].gl_Position;
vec4 pos1 = gl_in[1].gl_Position;
+ /* TODO(verify): Calculating edge-center in clip space might cause errors due to precision loss?? */
+ vec4 edgeCenter = (pos0 + pos1) / 2.0;
+ bool is_edge_selected = edgeSelectionFac[0] == 1.0;
+ /* Depth value as specified in vertex shader */
+ edgeCenter.z = is_edge_selected ? 0.25 : 0.35;
+
ss_pos[0] = pos0.xy / pos0.w;
ss_pos[1] = pos1.xy / pos1.w;
@@ -54,8 +61,14 @@ void main()
vec2 line_perp = vec2(-line_dir.y, line_dir.x);
vec2 edge_ofs = line_perp * sizeViewportInv * ceil(half_size);
+ vec2 stippleStartCenter, stipplePosCenter;
+ /* TODO(stipple pattern): Probable incorrect implementation - used as specified in vertex shader */
+ stippleStartCenter = stipplePosCenter = 500.0 + 500.0 * (edgeCenter.xy / edgeCenter.w);
+
do_vertex(pos0, selectionFac[0], stippleStart[0], stipplePos[0], half_size, edge_ofs.xy);
do_vertex(pos0, selectionFac[0], stippleStart[0], stipplePos[0], -half_size, -edge_ofs.xy);
+ do_vertex(edgeCenter, edgeSelectionFac[0], stippleStartCenter, stipplePosCenter, half_size, edge_ofs.xy);
+ do_vertex(edgeCenter, edgeSelectionFac[0], stippleStartCenter, stipplePosCenter, -half_size, -edge_ofs.xy);
do_vertex(pos1, selectionFac[1], stippleStart[1], stipplePos[1], half_size, edge_ofs.xy);
do_vertex(pos1, selectionFac[1], stippleStart[1], stipplePos[1], -half_size, -edge_ofs.xy);
diff --git a/source/blender/draw/engines/overlay/shaders/edit_uv_edges_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_uv_edges_vert.glsl
index 7627a287a05..3b00fd7aafe 100644
--- a/source/blender/draw/engines/overlay/shaders/edit_uv_edges_vert.glsl
+++ b/source/blender/draw/engines/overlay/shaders/edit_uv_edges_vert.glsl
@@ -6,6 +6,7 @@ in vec2 au;
in int flag;
out float selectionFac;
+out float edgeSelectionFac;
noperspective out vec2 stipplePos;
flat out vec2 stippleStart;
@@ -20,7 +21,9 @@ void main()
half_pixel_offset;
bool is_select = (flag & VERT_UV_SELECT) != 0;
+ bool is_edge_selected = (flag & EDGE_UV_SELECT) != 0;
selectionFac = is_select ? 1.0 : 0.0;
+ edgeSelectionFac = is_edge_selected ? 1.0 : 0.0;
/* Move selected edges to the top
* Vertices are between 0.0 and 0.2, Edges between 0.2 and 0.4
* actual pixels are at 0.75, 1.0 is used for the background. */
diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h
index f3aba12a924..884124396b9 100644
--- a/source/blender/editors/include/ED_uvedit.h
+++ b/source/blender/editors/include/ED_uvedit.h
@@ -145,6 +145,13 @@ void uvedit_edge_select_set_with_sticky(const struct SpaceImage *sima,
const bool select,
const bool do_history,
const uint cd_loop_uv_offset);
+void uvedit_edge_select_shared_location(const struct Scene *scene,
+ struct BMEditMesh *em,
+ struct BMLoop *l,
+ const bool select,
+ const bool use_mesh_location,
+ const bool do_history,
+ const uint cd_loop_uv_offset);
void uvedit_edge_select_set(const struct Scene *scene,
struct BMEditMesh *em,
struct BMLoop *l,
@@ -168,6 +175,13 @@ void uvedit_uv_select_set_with_sticky(const struct SpaceImage *sima,
const bool select,
const bool do_history,
const uint cd_loop_uv_offset);
+void uvedit_uv_select_shared_location(const struct Scene *scene,
+ struct BMEditMesh *em,
+ struct BMLoop *l,
+ const bool select,
+ const bool use_mesh_location,
+ const bool do_history,
+ const uint cd_loop_uv_offset);
void uvedit_uv_select_set(const struct Scene *scene,
struct BMEditMesh *em,
struct BMLoop *l,
diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h
index cd8fbd00316..9bc8a165b1f 100644
--- a/source/blender/editors/uvedit/uvedit_intern.h
+++ b/source/blender/editors/uvedit/uvedit_intern.h
@@ -86,11 +86,13 @@ bool uv_find_nearest_vert_multi(struct Scene *scene,
bool uv_find_nearest_edge(struct Scene *scene,
struct Object *obedit,
const float co[2],
+ const float penalty,
struct UvNearestHit *hit);
bool uv_find_nearest_edge_multi(struct Scene *scene,
struct Object **objects,
const uint objects_len,
const float co[2],
+ const float penalty,
struct UvNearestHit *hit);
bool uv_find_nearest_face_ex(struct Scene *scene,
@@ -123,6 +125,17 @@ BMLoop *uv_find_nearest_loop_from_edge(struct Scene *scene,
struct BMEdge *e,
const float co[2]);
+/* flush uv selection */
+void uv_flush_vert_to_edge(struct Scene *scene,
+ struct BMEditMesh *em,
+ const int cd_loop_uv_offset);
+void uv_flush_edge_to_vert(struct Scene *scene,
+ struct BMEditMesh *em,
+ const int cd_loop_uv_offset);
+void uv_flush_edge_to_vert_with_sticky_loc(struct Scene *scene,
+ struct BMEditMesh *em,
+ const int cd_loop_uv_offset);
+
/* utility tool functions */
void uvedit_live_unwrap_update(struct SpaceImage *sima,
diff --git a/source/blender/editors/uvedit/uvedit_path.c b/source/blender/editors/uvedit/uvedit_path.c
index 2613c5b23a0..1f3ba206103 100644
--- a/source/blender/editors/uvedit/uvedit_path.c
+++ b/source/blender/editors/uvedit/uvedit_path.c
@@ -627,7 +627,7 @@ static int uv_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmEve
else if (uv_selectmode & UV_SELECT_EDGE) {
UvNearestHit hit = UV_NEAREST_HIT_INIT_MAX(&region->v2d);
- if (!uv_find_nearest_edge(scene, obedit, co, &hit)) {
+ if (!uv_find_nearest_edge(scene, obedit, co, 0.0f, &hit)) {
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c
index 86390882bed..83e8259f693 100644
--- a/source/blender/editors/uvedit/uvedit_select.c
+++ b/source/blender/editors/uvedit/uvedit_select.c
@@ -70,27 +70,45 @@
#include "uvedit_intern.h"
-static void uv_select_all_perform(Scene *scene, Object *obedit, int action);
+static void uv_select_all_perform(Scene *scene, SpaceImage *sima, Object *obedit, int action);
-static void uv_select_all_perform_multi_ex(
- Scene *scene, Object **objects, const uint objects_len, int action, const Object *ob_exclude);
-static void uv_select_all_perform_multi(Scene *scene,
- Object **objects,
- const uint objects_len,
- int action);
+static void uv_select_all_perform_multi_ex(Scene *scene,
+ SpaceImage *sima,
+ Object **objects,
+ const uint objects_len,
+ int action,
+ const Object *ob_exclude);
+static void uv_select_all_perform_multi(
+ Scene *scene, SpaceImage *sima, Object **objects, const uint objects_len, int action);
+
+static void uv_select_flush_from_tag_sticky_loc_internal(Scene *scene,
+ BMEditMesh *em,
+ UvVertMap *vmap,
+ const uint efa_index,
+ BMLoop *l,
+ const bool select,
+ const int cd_loop_uv_offset);
static void uv_select_flush_from_tag_face(SpaceImage *sima,
Scene *scene,
Object *obedit,
const bool select);
-static void uv_select_flush_from_tag_loop(SpaceImage *sima,
+static void uv_select_flush_from_tag_loop(const SpaceImage *sima,
Scene *scene,
- Object *obedit,
+ BMEditMesh *em,
const bool select);
static void uv_select_tag_update_for_object(Depsgraph *depsgraph,
const ToolSettings *ts,
Object *obedit);
+static bool uvedit_vert_is_any_other_edge_selected(const Scene *scene,
+ BMLoop *l,
+ const int cd_loop_uv_offset);
+static bool uvedit_vert_is_any_other_face_selected(const Scene *scene,
+ BMLoop *l,
+ BMVert *v,
+ const int cd_loop_uv_offset);
+
/* -------------------------------------------------------------------- */
/** \name Active Selection Tracking
*
@@ -246,6 +264,9 @@ bool uvedit_face_select_test_ex(const ToolSettings *ts, BMFace *efa, const int c
if (!(luv->flag & MLOOPUV_VERTSEL)) {
return false;
}
+ if (!(luv->flag & MLOOPUV_EDGESEL)) {
+ return false;
+ }
}
return true;
}
@@ -268,12 +289,43 @@ void uvedit_face_select_set_with_sticky(const SpaceImage *sima,
return;
}
- BMLoop *l_iter, *l_first;
- l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
- do {
- uvedit_uv_select_set_with_sticky(
- sima, scene, em, l_iter, select, do_history, cd_loop_uv_offset);
- } while ((l_iter = l_iter->next) != l_first);
+ const int sticky = sima->sticky;
+ switch (sticky) {
+ case SI_STICKY_DISABLE: {
+ if (uvedit_face_visible_test(scene, efa)) {
+ uvedit_face_select_set(scene, em, efa, select, do_history, cd_loop_uv_offset);
+ }
+ break;
+ }
+ case SI_STICKY_LOC:
+ case SI_STICKY_VERTEX:
+ default: {
+ if (uvedit_face_visible_test(scene, efa)) {
+ /* Sticky location and vertex modes. */
+ /* Fallback, in case sima->sticky is invalid */
+ BMLoop *l;
+ MLoopUV *luv;
+ BMIter liter;
+
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (select) {
+ luv->flag |= MLOOPUV_EDGESEL;
+ uvedit_uv_select_shared_location(
+ scene, em, l, select, false, do_history, cd_loop_uv_offset);
+ }
+ else {
+ luv->flag &= ~MLOOPUV_EDGESEL;
+ if (!uvedit_vert_is_any_other_face_selected(scene, l, l->v, cd_loop_uv_offset)) {
+ uvedit_uv_select_shared_location(
+ scene, em, l, select, false, do_history, cd_loop_uv_offset);
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
}
void uvedit_face_select_set(const struct Scene *scene,
@@ -313,6 +365,7 @@ void uvedit_face_select_enable(const Scene *scene,
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
luv->flag |= MLOOPUV_VERTSEL;
+ luv->flag |= MLOOPUV_EDGESEL;
}
}
}
@@ -335,6 +388,7 @@ void uvedit_face_select_disable(const Scene *scene,
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
luv->flag &= ~MLOOPUV_VERTSEL;
+ luv->flag &= ~MLOOPUV_EDGESEL;
}
}
}
@@ -357,7 +411,12 @@ bool uvedit_edge_select_test_ex(const ToolSettings *ts, BMLoop *l, const int cd_
luv1 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
luv2 = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
- return (luv1->flag & MLOOPUV_VERTSEL) && (luv2->flag & MLOOPUV_VERTSEL);
+ /* UV edge is selected only if :
+ * - first UV vertex in luv1 is selected
+ * - second UV vertex in luv2 is selected
+ * - MLOOPUV_EDGESEL flag is set for luv1 */
+ return (luv1->flag & MLOOPUV_VERTSEL) && (luv2->flag & MLOOPUV_VERTSEL) &&
+ (luv1->flag & MLOOPUV_EDGESEL);
}
bool uvedit_edge_select_test(const Scene *scene, BMLoop *l, const int cd_loop_uv_offset)
{
@@ -378,9 +437,101 @@ void uvedit_edge_select_set_with_sticky(const struct SpaceImage *sima,
return;
}
- uvedit_uv_select_set_with_sticky(sima, scene, em, l, select, do_history, cd_loop_uv_offset);
- uvedit_uv_select_set_with_sticky(
- sima, scene, em, l->next, select, do_history, cd_loop_uv_offset);
+ const int sticky = sima->sticky;
+ switch (sticky) {
+ case SI_STICKY_DISABLE: {
+ if (uvedit_face_visible_test(scene, l->f)) {
+ uvedit_edge_select_set(scene, em, l, select, do_history, cd_loop_uv_offset);
+ }
+ break;
+ }
+ case SI_STICKY_VERTEX: {
+ uvedit_edge_select_shared_location(
+ scene, em, l, select, true, do_history, cd_loop_uv_offset);
+ break;
+ }
+ default: {
+ /* SI_STICKY_LOC
+ * Fallback to SI_STICKY_LOC, in case sima->sticky is invalid */
+ uvedit_edge_select_shared_location(
+ scene, em, l, select, false, do_history, cd_loop_uv_offset);
+ break;
+ }
+ }
+}
+
+/* Selects UV edges sharing the same location as l->e
+ *
+ * If use_mesh_location is :
+ * - true :- selects all UV edges sharing the same location on the 3D mesh
+ * - false :- selects all UV edges sharing the same location in UV space */
+void uvedit_edge_select_shared_location(const Scene *scene,
+ BMEditMesh *em,
+ BMLoop *l,
+ const bool select,
+ const bool use_mesh_location,
+ const bool do_history,
+ const uint cd_loop_uv_offset)
+{
+ BMLoop *l_radial_iter = l;
+ do {
+ if (uvedit_face_visible_test(scene, l_radial_iter->f)) {
+
+ if (use_mesh_location) {
+ MLoopUV *luv_radial = BM_ELEM_CD_GET_VOID_P(l_radial_iter, cd_loop_uv_offset);
+ if (select) {
+ luv_radial->flag |= MLOOPUV_EDGESEL;
+ uvedit_uv_select_shared_location(
+ scene, em, l_radial_iter, select, false, do_history, cd_loop_uv_offset);
+ uvedit_uv_select_shared_location(
+ scene, em, l_radial_iter->next, select, false, do_history, cd_loop_uv_offset);
+ }
+ else {
+ luv_radial->flag &= ~MLOOPUV_EDGESEL;
+ if (!uvedit_vert_is_any_other_edge_selected(scene, l_radial_iter, cd_loop_uv_offset)) {
+ uvedit_uv_select_shared_location(
+ scene, em, l_radial_iter, select, false, do_history, cd_loop_uv_offset);
+ }
+ if (!uvedit_vert_is_any_other_edge_selected(
+ scene, l_radial_iter->next, cd_loop_uv_offset)) {
+ uvedit_uv_select_shared_location(
+ scene, em, l_radial_iter->next, select, false, do_history, cd_loop_uv_offset);
+ }
+ }
+ }
+ else {
+ if (BM_loop_uv_share_edge_check(l, l_radial_iter, cd_loop_uv_offset)) {
+ MLoopUV *luv_radial = BM_ELEM_CD_GET_VOID_P(l_radial_iter, cd_loop_uv_offset);
+ if (select) {
+ luv_radial->flag |= MLOOPUV_EDGESEL;
+ }
+ else {
+ luv_radial->flag &= ~MLOOPUV_EDGESEL;
+ }
+ /* Handle vertex selections outside radial cycle loop to avoid redundant calls to
+ * uvedit_uv_select_shared_location() */
+ }
+ }
+ }
+ } while ((l_radial_iter = l_radial_iter->radial_next) != l);
+
+ if (!use_mesh_location) {
+ if (select) {
+ uvedit_uv_select_shared_location(scene, em, l, select, false, do_history, cd_loop_uv_offset);
+ uvedit_uv_select_shared_location(
+ scene, em, l->next, select, false, do_history, cd_loop_uv_offset);
+ }
+ else {
+ if (!uvedit_vert_is_any_other_edge_selected(scene, l, cd_loop_uv_offset)) {
+ uvedit_uv_select_shared_location(
+ scene, em, l, select, false, do_history, cd_loop_uv_offset);
+ }
+ if (!uvedit_vert_is_any_other_edge_selected(scene, l->next, cd_loop_uv_offset)) {
+ uvedit_uv_select_shared_location(
+ scene, em, l->next, select, false, do_history, cd_loop_uv_offset);
+ }
+ }
+ }
}
void uvedit_edge_select_set(const Scene *scene,
@@ -425,13 +576,15 @@ void uvedit_edge_select_enable(const Scene *scene,
}
}
else {
- MLoopUV *luv1, *luv2;
+ MLoopUV *luv, *luv_next;
+
+ luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
- luv1 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- luv2 = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
+ luv->flag |= MLOOPUV_VERTSEL;
+ luv_next->flag |= MLOOPUV_VERTSEL;
- luv1->flag |= MLOOPUV_VERTSEL;
- luv2->flag |= MLOOPUV_VERTSEL;
+ luv->flag |= MLOOPUV_EDGESEL;
}
}
@@ -456,13 +609,21 @@ void uvedit_edge_select_disable(const Scene *scene,
}
}
else {
- MLoopUV *luv1, *luv2;
+ MLoopUV *luv_prev, *luv, *luv_next;
+
+ luv_prev = BM_ELEM_CD_GET_VOID_P(l->prev, cd_loop_uv_offset);
+ luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
- luv1 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- luv2 = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
+ luv->flag &= ~MLOOPUV_EDGESEL;
- luv1->flag &= ~MLOOPUV_VERTSEL;
- luv2->flag &= ~MLOOPUV_VERTSEL;
+ /* Deselect UV vertex only if it is not part of another edge selection */
+ if (!(luv_next->flag & MLOOPUV_EDGESEL)) {
+ luv_next->flag &= ~MLOOPUV_VERTSEL;
+ }
+ if (!(luv_prev->flag & MLOOPUV_EDGESEL)) {
+ luv->flag &= ~MLOOPUV_VERTSEL;
+ }
}
}
@@ -503,40 +664,60 @@ void uvedit_uv_select_set_with_sticky(const struct SpaceImage *sima,
uvedit_uv_select_set(scene, em, l, select, do_history, cd_loop_uv_offset);
break;
}
+ case SI_STICKY_VERTEX: {
+ uvedit_uv_select_shared_location(scene, em, l, select, true, do_history, cd_loop_uv_offset);
+ break;
+ }
default: {
- /* #SI_STICKY_VERTEX or #SI_STICKY_LOC. */
- const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- BMEdge *e_first, *e_iter;
- e_first = e_iter = l->e;
+ /* SI_STICKY_LOC
+ * Fallback to SI_STICKY_LOC, in case sima->sticky is invalid */
+ uvedit_uv_select_shared_location(scene, em, l, select, false, do_history, cd_loop_uv_offset);
+ break;
+ }
+ }
+}
+
+/* Selects UV vertices sharing the same location as l->v
+ *
+ * If use_mesh_location is :
+ * - true :- selects all UV vertices sharing the same location on the 3D mesh
+ * - false :- selects all UV vertices sharing the same location in UV space */
+void uvedit_uv_select_shared_location(const Scene *scene,
+ BMEditMesh *em,
+ BMLoop *l,
+ const bool select,
+ const bool use_mesh_location,
+ const bool do_history,
+ const uint cd_loop_uv_offset)
+{
+ const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ BMEdge *e_first, *e_iter;
+ e_first = e_iter = l->e;
+ do {
+ /* This check should be removed ?? */
+ if (e_iter->l) {
+ BMLoop *l_radial_iter = e_iter->l;
+
do {
- if (e_iter->l) {
- BMLoop *l_radial_iter = e_iter->l;
- do {
- if (l_radial_iter->v == l->v) {
- if (uvedit_face_visible_test(scene, l_radial_iter->f)) {
- bool do_select = false;
- if (sticky == SI_STICKY_VERTEX) {
- do_select = true;
- }
- else {
- const MLoopUV *luv_other = BM_ELEM_CD_GET_VOID_P(l_radial_iter,
- cd_loop_uv_offset);
- if (equals_v2v2(luv_other->uv, luv->uv)) {
- do_select = true;
- }
- }
+ if (l_radial_iter->v == l->v) {
+ if (uvedit_face_visible_test(scene, l_radial_iter->f)) {
- if (do_select) {
- uvedit_uv_select_set(
- scene, em, l_radial_iter, select, do_history, cd_loop_uv_offset);
- }
+ if (use_mesh_location) {
+ uvedit_uv_select_set(
+ scene, em, l_radial_iter, select, do_history, cd_loop_uv_offset);
+ }
+ else {
+ const MLoopUV *luv_other = BM_ELEM_CD_GET_VOID_P(l_radial_iter, cd_loop_uv_offset);
+ if (equals_v2v2(luv_other->uv, luv->uv)) {
+ uvedit_uv_select_set(
+ scene, em, l_radial_iter, select, do_history, cd_loop_uv_offset);
}
}
- } while ((l_radial_iter = l_radial_iter->radial_next) != e_iter->l);
+ }
}
- } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, l->v)) != e_first);
+ } while ((l_radial_iter = l_radial_iter->radial_next) != e_iter->l);
}
- }
+ } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, l->v)) != e_first);
}
void uvedit_uv_select_set(const Scene *scene,
@@ -577,6 +758,22 @@ void uvedit_uv_select_enable(const Scene *scene,
else {
MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
luv->flag |= MLOOPUV_VERTSEL;
+
+ /* Only allow the edges between two selected vertices to be selected when in vertex selection
+ * mode */
+ if (ts->uv_selectmode & UV_SELECT_VERTEX) {
+ MLoopUV *luv_next, *luv_prev;
+ luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
+ luv_prev = BM_ELEM_CD_GET_VOID_P(l->prev, cd_loop_uv_offset);
+ /* If previous UV vertex selected then mark corresponding edge as selected */
+ if (luv_prev->flag & MLOOPUV_VERTSEL) {
+ luv_prev->flag |= MLOOPUV_EDGESEL;
+ }
+ /* If next UV vertex selected then mark the edge for current MLoopUV as selected */
+ if (luv_next->flag & MLOOPUV_VERTSEL) {
+ luv->flag |= MLOOPUV_EDGESEL;
+ }
+ }
}
}
@@ -596,8 +793,19 @@ void uvedit_uv_select_disable(const Scene *scene,
}
}
else {
- MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ MLoopUV *luv, *luv_prev, *luv_next;
+ luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ luv_prev = BM_ELEM_CD_GET_VOID_P(l->prev, cd_loop_uv_offset);
+ luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
+
luv->flag &= ~MLOOPUV_VERTSEL;
+
+ if ((luv_prev->flag & MLOOPUV_VERTSEL) && (luv_prev->flag & MLOOPUV_EDGESEL)) {
+ luv_prev->flag &= ~MLOOPUV_EDGESEL;
+ }
+ if ((luv_next->flag & MLOOPUV_VERTSEL) && (luv->flag & MLOOPUV_EDGESEL)) {
+ luv->flag &= ~MLOOPUV_EDGESEL;
+ }
}
}
@@ -661,7 +869,8 @@ static BMLoop *uvedit_loop_find_other_boundary_loop_with_visible_face(const Scen
/** \name Find Nearest Elements
* \{ */
-bool uv_find_nearest_edge(Scene *scene, Object *obedit, const float co[2], UvNearestHit *hit)
+bool uv_find_nearest_edge(
+ Scene *scene, Object *obedit, const float co[2], const float penalty, UvNearestHit *hit)
{
BLI_assert((hit->scale[0] > 0.0f) && (hit->scale[1] > 0.0f));
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -690,8 +899,22 @@ bool uv_find_nearest_edge(Scene *scene, Object *obedit, const float co[2], UvNea
sub_v2_v2(delta, co);
mul_v2_v2(delta, hit->scale);
- const float dist_test_sq = len_squared_v2(delta);
+ float dist_test_sq = len_squared_v2(delta);
+ /* Sometimes an incorrect edge is chosen as the nearest edge and even in successive
+ * selection attempts, the same edge will be selected
+ * Proof: Disable sticky selection and use edge selection mode to select an UV edge that is
+ * shared between 2 UV faces. Sometimes the edge that is sharing the correct location but is
+ * part of a different face (not the face denoted by co[2]) is selected.
+ *
+ * The penalty used below ensures that the correct edge will be selected in the next
+ * attempts.
+ * TODO: Add a check to ensure the correct edge is returned in the first attempt itself */
+
+ /* Select the other edge sharing the same position as the previous selcted edge */
+ if ((penalty != 0.0f) && uvedit_edge_select_test(scene, l, cd_loop_uv_offset)) {
+ dist_test_sq = square_f(sqrtf(dist_test_sq) + penalty);
+ }
if (dist_test_sq < hit->dist_sq) {
hit->ob = obedit;
hit->efa = efa;
@@ -706,13 +929,17 @@ bool uv_find_nearest_edge(Scene *scene, Object *obedit, const float co[2], UvNea
return found;
}
-bool uv_find_nearest_edge_multi(
- Scene *scene, Object **objects, const uint objects_len, const float co[2], UvNearestHit *hit)
+bool uv_find_nearest_edge_multi(Scene *scene,
+ Object **objects,
+ const uint objects_len,
+ const float co[2],
+ const float penalty,
+ UvNearestHit *hit)
{
bool found = false;
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
- if (uv_find_nearest_edge(scene, obedit, co, hit)) {
+ if (uv_find_nearest_edge(scene, obedit, co, penalty, hit)) {
found = true;
}
}
@@ -841,6 +1068,8 @@ bool uv_find_nearest_vert(
float dist_test_sq = len_squared_v2(delta);
+ /* Ensures that successive selection attempts will select other vertices sharing the same
+ * UV coordinates */
if ((penalty_dist != 0.0f) && uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
dist_test_sq = square_f(sqrtf(dist_test_sq) + penalty_dist);
}
@@ -934,6 +1163,229 @@ bool ED_uvedit_nearest_uv_multi(const Scene *scene,
/** \} */
+/**
+ * For a specified UV vertex and edge, check if any edge connected to it is selected or not
+ */
+static bool uvedit_vert_is_any_other_edge_selected(const Scene *scene,
+ BMLoop *l,
+ const int cd_loop_uv_offset)
+{
+ BMEdge *e_first, *e_iter;
+ e_first = e_iter = l->e;
+ do {
+ BMLoop *l_radial_iter = e_iter->l;
+ do {
+ if (l_radial_iter->v == l->v) {
+ if (BM_loop_uv_share_vert_check(l, l_radial_iter, cd_loop_uv_offset) &&
+ uvedit_face_visible_test(scene, l_radial_iter->f)) {
+
+ if (uvedit_edge_select_test(scene, l_radial_iter, cd_loop_uv_offset)) {
+ return true;
+ }
+ }
+ }
+ else {
+ if (BM_loop_uv_share_vert_check(l, l_radial_iter->next, cd_loop_uv_offset) &&
+ uvedit_face_visible_test(scene, l_radial_iter->f)) {
+
+ if (uvedit_edge_select_test(scene, l_radial_iter, cd_loop_uv_offset)) {
+ return true;
+ }
+ }
+ }
+ } while ((l_radial_iter = l_radial_iter->radial_next) != e_iter->l);
+ } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, l->v)) != e_first);
+
+ return false;
+}
+
+/**
+ * For a specified UV vertexand face, check if any other face connected to it is selected or not */
+static bool uvedit_vert_is_any_other_face_selected(const Scene *scene,
+ BMLoop *l,
+ BMVert *v,
+ const int cd_loop_uv_offset)
+{
+ BMEdge *e_first, *e_iter;
+ e_first = e_iter = l->e;
+ do {
+ BMLoop *l_radial_iter = e_iter->l;
+ do {
+ if ((l_radial_iter->f != l->f) && uvedit_face_visible_test(scene, l_radial_iter->f) &&
+ (l->v == l_radial_iter->v)) {
+
+ if (BM_loop_uv_share_vert_check(l, l_radial_iter, cd_loop_uv_offset) &&
+ uvedit_face_select_test(scene, l_radial_iter->f, cd_loop_uv_offset)) {
+ return true;
+ }
+ }
+ } while ((l_radial_iter = l_radial_iter->radial_next) != e_iter->l);
+ } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first);
+
+ return false;
+}
+
+/* -------------------------------------------------------------------- */
+/** \name UV Flush selection
+ *
+ * Helper functions for flushing the UV selection state
+ * \{ */
+
+/**
+ * Clear all selection flags
+ */
+static void uv_clear_selection_flag(Scene *scene,
+ BMEditMesh *em,
+ const int selection_flag,
+ const int cd_loop_uv_offset)
+{
+ BMFace *efa;
+ BMLoop *l;
+ BMIter iter, liter;
+ MLoopUV *luv;
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (!uvedit_face_visible_test(scene, efa)) {
+ continue;
+ }
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ luv->flag &= ~selection_flag;
+ }
+ }
+}
+
+/**
+ * Flushes selection upwards (verts to edge)
+ */
+void uv_flush_vert_to_edge(Scene *scene, BMEditMesh *em, const int cd_loop_uv_offset)
+{
+ BMFace *efa;
+ BMLoop *l;
+ BMIter iter, liter;
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (!uvedit_face_visible_test(scene, efa)) {
+ continue;
+ }
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ MLoopUV *luv, *luv_next;
+ luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
+
+ if ((luv->flag & MLOOPUV_VERTSEL) && (luv_next->flag & MLOOPUV_VERTSEL)) {
+ luv->flag |= MLOOPUV_EDGESEL;
+ }
+ else {
+ luv->flag &= ~MLOOPUV_EDGESEL;
+ }
+ }
+ }
+}
+
+/**
+ * Flushes selection downwards (edge to verts)
+ * Could be renamed to uv_validate_selection_state()
+ *
+ * UNUSUAL CASE : Flushing downwards
+ * Useful when the vertices of the edges that need to be selected are marked using MLOOPUV_EDGESEL
+ * flag
+ */
+void uv_flush_edge_to_vert(Scene *scene, BMEditMesh *em, const int cd_loop_uv_offset)
+{
+ BMFace *efa;
+ BMLoop *l;
+ BMIter iter, liter;
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (!uvedit_face_visible_test(scene, efa)) {
+ continue;
+ }
+
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ MLoopUV *luv, *luv_next, *luv_prev;
+ luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
+ luv_prev = BM_ELEM_CD_GET_VOID_P(l->prev, cd_loop_uv_offset);
+
+ if (luv->flag & MLOOPUV_EDGESEL) {
+ luv->flag |= MLOOPUV_VERTSEL;
+ luv_next->flag |= MLOOPUV_VERTSEL;
+ }
+ else if (!(luv_prev->flag & MLOOPUV_EDGESEL)) {
+ luv->flag &= ~MLOOPUV_VERTSEL;
+ }
+ }
+ }
+}
+
+void uv_flush_edge_to_face(Scene *scene, BMEditMesh *em, const int cd_loop_uv_offset)
+{
+ BMFace *efa;
+ BMLoop *l;
+ BMIter iter, liter;
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (!uvedit_face_visible_test(scene, efa)) {
+ continue;
+ }
+
+ if (!uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) {
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ MLoopUV *luv;
+ luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ luv->flag &= ~MLOOPUV_VERTSEL;
+ luv->flag &= ~MLOOPUV_EDGESEL;
+ }
+ }
+ }
+}
+
+/**
+ * Select shared vertices (vertex selection with sticky location) for all edges that are marked
+ * using MLOOPUV_EDGESEL flag as a tag
+ */
+void uv_flush_edge_to_vert_with_sticky_loc(Scene *scene,
+ BMEditMesh *em,
+ const int cd_loop_uv_offset)
+{
+ BMFace *efa;
+ BMLoop *l;
+ BMIter iter, liter;
+ struct UvVertMap *vmap;
+ uint efa_index;
+ uv_clear_selection_flag(scene, em, MLOOPUV_VERTSEL, cd_loop_uv_offset);
+
+ vmap = BM_uv_vert_map_create(em->bm, false, false);
+ if (vmap == NULL) {
+ return;
+ }
+
+ BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, efa_index) {
+ if (!uvedit_face_visible_test(scene, efa)) {
+ continue;
+ }
+
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ MLoopUV *luv;
+ luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+
+ if (luv->flag & MLOOPUV_EDGESEL) {
+ uv_select_flush_from_tag_sticky_loc_internal(
+ scene, em, vmap, efa_index, l, true, cd_loop_uv_offset);
+ uv_select_flush_from_tag_sticky_loc_internal(
+ scene, em, vmap, efa_index, l->next, true, cd_loop_uv_offset);
+ }
+ }
+ }
+
+ BM_uv_vert_map_free(vmap);
+}
+
+/* TODO: (Siddhartha) Separate functions for flushing selections using sticky modes */
+
+/** \} */
+
+/** TODO: (Siddhartha) UV Select-Mode Flushing
+ *
+ * NOTE: An attempt at trying to implement UV selection mode flushing brought up a bunch of corner
+ * cases. These mostly revolved around how to handle sticky modes during flushing. */
/* -------------------------------------------------------------------- */
/** \name Find Nearest to Element
*
@@ -1171,7 +1623,7 @@ static int uv_select_edgeloop(
const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
if (extend) {
- select = !(uvedit_uv_select_test(scene, hit->l, cd_loop_uv_offset));
+ select = !(uvedit_edge_select_test(scene, hit->l, cd_loop_uv_offset));
}
else {
select = true;
@@ -1214,7 +1666,7 @@ static int uv_select_edgeloop(
/* Apply the selection. */
if (!extend) {
- uv_select_all_perform(scene, obedit, SEL_DESELECT);
+ uv_select_all_perform(scene, NULL, obedit, SEL_DESELECT);
}
/* Select all tagged loops. */
@@ -1239,6 +1691,70 @@ static int uv_select_edgeloop(
/** \} */
/* -------------------------------------------------------------------- */
+/** \name Face Loop Select
+ * \{ */
+
+static int uv_select_faceloop(
+ const SpaceImage *sima, Scene *scene, Object *obedit, UvNearestHit *hit, const bool extend)
+{
+ BMEditMesh *em = BKE_editmesh_from_object(obedit);
+ bool select;
+
+ const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+
+ if (!extend) {
+ uv_select_all_perform(scene, NULL, obedit, SEL_DESELECT);
+ }
+
+ BM_mesh_elem_hflag_disable_all(em->bm, BM_FACE, BM_ELEM_TAG, false);
+
+ if (extend) {
+ select = !(uvedit_face_select_test(scene, hit->l->f, cd_loop_uv_offset));
+ }
+ else {
+ select = true;
+ }
+
+ BMLoop *l_pair[2] = {
+ hit->l,
+ uvedit_loop_find_other_radial_loop_with_visible_face(scene, hit->l, cd_loop_uv_offset),
+ };
+
+ for (int side = 0; side < 2; side++) {
+ BMLoop *l_step = l_pair[side];
+ while (l_step) {
+ if (!uvedit_face_visible_test(scene, l_step->f)) {
+ break;
+ }
+
+ uvedit_face_select_set_with_sticky(
+ sima, scene, em, l_step->f, select, false, cd_loop_uv_offset);
+
+ BM_elem_flag_enable(l_step->f, BM_ELEM_TAG);
+ if (l_step->f->len == 4) {
+ BMLoop *l_step_opposite = l_step->next->next;
+ l_step = uvedit_loop_find_other_radial_loop_with_visible_face(
+ scene, l_step_opposite, cd_loop_uv_offset);
+ }
+ else {
+ l_step = NULL;
+ }
+
+ /* Break iteration when l_step :
+ * - is the first loop where we started from
+ * - tagged using BM_ELEM_TAG(meaning this loop has been visited in this iteration) */
+ if (l_step && BM_elem_flag_test(l_step->f, BM_ELEM_TAG)) {
+ break;
+ }
+ }
+ }
+
+ return (select) ? 1 : -1;
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
/** \name Edge Ring Select
* \{ */
@@ -1250,18 +1766,21 @@ static int uv_select_edgering(
const bool use_face_select = (ts->uv_flag & UV_SYNC_SELECTION) ?
(ts->selectmode & SCE_SELECT_FACE) :
(ts->uv_selectmode & UV_SELECT_FACE);
+ const bool use_vertex_select = (ts->uv_flag & UV_SYNC_SELECTION) ?
+ (ts->selectmode & SCE_SELECT_VERTEX) :
+ (ts->uv_selectmode & UV_SELECT_VERTEX);
bool select;
const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
if (!extend) {
- uv_select_all_perform(scene, obedit, SEL_DESELECT);
+ uv_select_all_perform(scene, NULL, obedit, SEL_DESELECT);
}
BM_mesh_elem_hflag_disable_all(em->bm, BM_EDGE, BM_ELEM_TAG, false);
if (extend) {
- select = !(uvedit_uv_select_test(scene, hit->l, cd_loop_uv_offset));
+ select = !(uvedit_edge_select_test(scene, hit->l, cd_loop_uv_offset));
}
else {
select = true;
@@ -1282,10 +1801,20 @@ static int uv_select_edgering(
}
if (use_face_select) {
+ /* While selecting face loops is now done in a separate function - uv_select_faceloop()
+ * ,this check is still kept for edge ring selection to keep it consistent with how edge
+ * ring selection works in the 3D viewport */
uvedit_face_select_set_with_sticky(
sima, scene, em, l_step->f, select, false, cd_loop_uv_offset);
}
+ else if (use_vertex_select) {
+ uvedit_uv_select_set_with_sticky(
+ sima, scene, em, l_step, select, false, cd_loop_uv_offset);
+ uvedit_uv_select_set_with_sticky(
+ sima, scene, em, l_step->next, select, false, cd_loop_uv_offset);
+ }
else {
+ /* Edge select mode */
uvedit_edge_select_set_with_sticky(
sima, scene, em, l_step, select, false, cd_loop_uv_offset);
}
@@ -1304,8 +1833,19 @@ static int uv_select_edgering(
l_step = NULL;
}
+ /* Break iteration when l_step :
+ * - is the first loop where we started from
+ * - tagged using BM_ELEM_TAG (meaning this loop has been visited in this iteration)
+ * - has its corresponding UV edge selected/unselected based on select */
if (l_step && BM_elem_flag_test(l_step->e, BM_ELEM_TAG)) {
- break;
+ /* Previously this check was not done and this resulted in the final edge in the edge ring
+ * cycle to be skipped during selection (casued by old sticky selection behavior) */
+ if (select && uvedit_edge_select_test(scene, l_step, cd_loop_uv_offset)) {
+ break;
+ }
+ else if (!select && !uvedit_edge_select_test(scene, l_step, cd_loop_uv_offset)) {
+ break;
+ }
}
}
}
@@ -1351,9 +1891,9 @@ static void uv_select_linked_multi(Scene *scene,
BM_mesh_elem_table_ensure(em->bm, BM_FACE); /* we can use this too */
- /* NOTE: we had 'use winding' so we don't consider overlapping islands as connected, see T44320
- * this made *every* projection split the island into front/back islands.
- * Keep 'use_winding' to false, see: T50970.
+ /* NOTE: we had 'use winding' so we don't consider overlapping islands as connected, see
+ * T44320 this made *every* projection split the island into front/back islands. Keep
+ * 'use_winding' to false, see: T50970.
*
* Better solve this by having a delimit option for select-linked operator,
* keeping island-select working as is. */
@@ -1620,30 +2160,66 @@ static int uv_select_more_less(bContext *C, const bool select)
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
if (uvedit_face_visible_test(scene, efa)) {
+ if (select) {
#define IS_SEL 1
#define IS_UNSEL 2
- int sel_state = 0;
+ int sel_state = 0;
- BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- if (luv->flag & MLOOPUV_VERTSEL) {
- sel_state |= IS_SEL;
- }
- else {
- sel_state |= IS_UNSEL;
- }
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (luv->flag & MLOOPUV_VERTSEL) {
+ sel_state |= IS_SEL;
+ }
+ else {
+ sel_state |= IS_UNSEL;
+ }
- /* if we have a mixed selection, tag to grow it */
- if (sel_state == (IS_SEL | IS_UNSEL)) {
- BM_elem_flag_enable(efa, BM_ELEM_TAG);
- changed = true;
- break;
+ /* if we have a mixed selection, tag to grow it */
+ if (sel_state == (IS_SEL | IS_UNSEL)) {
+ BM_elem_flag_enable(efa, BM_ELEM_TAG);
+ changed = true;
+ break;
+ }
}
- }
#undef IS_SEL
#undef IS_UNSEL
+ }
+ else {
+ if (uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) {
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ /* tag faces that must be deselected */
+
+ /* Deselect face only when any of the surrounding faces is not selected */
+ bool deselect_face = false;
+ BMEdge *e_first, *e_iter;
+ e_first = e_iter = l->e;
+ do {
+ BMLoop *l_radial_iter = e_iter->l;
+ do {
+ if ((l_radial_iter->f != l->f) &&
+ uvedit_face_visible_test(scene, l_radial_iter->f) &&
+ (l->v == l_radial_iter->v)) {
+
+ if (BM_loop_uv_share_vert_check(l, l_radial_iter, cd_loop_uv_offset) &&
+ !uvedit_face_select_test(scene, l_radial_iter->f, cd_loop_uv_offset)) {
+ BM_elem_flag_enable(efa, BM_ELEM_TAG);
+ changed = deselect_face = true;
+ break;
+ }
+ }
+ } while ((l_radial_iter = l_radial_iter->radial_next) != e_iter->l);
+ if (deselect_face)
+ break;
+ } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, l->v)) != e_first);
+
+ if (deselect_face) {
+ break;
+ }
+ }
+ }
+ }
}
}
}
@@ -1680,7 +2256,7 @@ static int uv_select_more_less(bContext *C, const bool select)
}
else {
/* Select tagged loops. */
- uv_select_flush_from_tag_loop(sima, scene, obedit, select);
+ uv_select_flush_from_tag_loop(sima, scene, em, select);
}
DEG_id_tag_update(obedit->data, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
@@ -1774,7 +2350,7 @@ bool uvedit_select_is_any_selected_multi(Scene *scene, Object **objects, const u
return found;
}
-static void uv_select_all_perform(Scene *scene, Object *obedit, int action)
+static void uv_select_all_perform(Scene *scene, SpaceImage *sima, Object *obedit, int action)
{
const ToolSettings *ts = scene->toolsettings;
BMEditMesh *em = BKE_editmesh_from_object(obedit);
@@ -1807,32 +2383,86 @@ static void uv_select_all_perform(Scene *scene, Object *obedit, int action)
}
}
else {
- BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
- if (!uvedit_face_visible_test(scene, efa)) {
- continue;
+ if (action != SEL_INVERT) {
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (!uvedit_face_visible_test(scene, efa)) {
+ continue;
+ }
+
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+
+ switch (action) {
+ case SEL_SELECT:
+ luv->flag |= MLOOPUV_VERTSEL;
+ luv->flag |= MLOOPUV_EDGESEL;
+ break;
+ case SEL_DESELECT:
+ luv->flag &= ~MLOOPUV_VERTSEL;
+ luv->flag &= ~MLOOPUV_EDGESEL;
+ break;
+ }
+ }
}
+ }
+ else {
+ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+ if (!uvedit_face_visible_test(scene, efa)) {
+ continue;
+ }
- BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+ luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- switch (action) {
- case SEL_SELECT:
- luv->flag |= MLOOPUV_VERTSEL;
- break;
- case SEL_DESELECT:
- luv->flag &= ~MLOOPUV_VERTSEL;
- break;
- case SEL_INVERT:
- luv->flag ^= MLOOPUV_VERTSEL;
- break;
+ switch (ts->uv_selectmode) {
+ /* Invert MLOOPUV_EDGESEL flag state for all uv elements if in edge/face selection
+ * mode. This will mark all edges that need to be selected */
+ case UV_SELECT_EDGE:
+ case UV_SELECT_FACE: {
+ luv->flag ^= MLOOPUV_EDGESEL;
+ break;
+ }
+ /* Invert MLOOPUV_VERTSEL flag state for all uv elements if in vertex/island selection
+ * mode This will mark all vertices that need to be selected */
+ case UV_SELECT_VERTEX:
+ case UV_SELECT_ISLAND: /* Fallback to vertex selection mode logic */
+ default: {
+ luv->flag ^= MLOOPUV_VERTSEL;
+ break;
+ }
+ }
+ }
+ }
+ /* Flush based on selection flags and current UV selection mode */
+ switch (ts->uv_selectmode) {
+ case UV_SELECT_EDGE:
+ case UV_SELECT_FACE: {
+ if (sima->sticky == SI_STICKY_DISABLE) {
+ uv_flush_edge_to_vert(scene, em, cd_loop_uv_offset);
+ }
+ else {
+ /* Special Handling for sticky modes. */
+ uv_flush_edge_to_vert_with_sticky_loc(scene, em, cd_loop_uv_offset);
+ }
+ break;
+ }
+ case UV_SELECT_VERTEX:
+ case UV_SELECT_ISLAND: /* Fallback to vertex selection mode logic */
+ default: {
+ uv_flush_vert_to_edge(scene, em, cd_loop_uv_offset);
+ break;
}
}
}
}
}
-static void uv_select_all_perform_multi_ex(
- Scene *scene, Object **objects, const uint objects_len, int action, const Object *ob_exclude)
+static void uv_select_all_perform_multi_ex(Scene *scene,
+ SpaceImage *sima,
+ Object **objects,
+ const uint objects_len,
+ int action,
+ const Object *ob_exclude)
{
if (action == SEL_TOGGLE) {
action = uvedit_select_is_any_selected_multi(scene, objects, objects_len) ? SEL_DESELECT :
@@ -1844,22 +2474,21 @@ static void uv_select_all_perform_multi_ex(
if (ob_exclude && (obedit == ob_exclude)) {
continue;
}
- uv_select_all_perform(scene, obedit, action);
+ uv_select_all_perform(scene, sima, obedit, action);
}
}
-static void uv_select_all_perform_multi(Scene *scene,
- Object **objects,
- const uint objects_len,
- int action)
+static void uv_select_all_perform_multi(
+ Scene *scene, SpaceImage *sima, Object **objects, const uint objects_len, int action)
{
- uv_select_all_perform_multi_ex(scene, objects, objects_len, action, NULL);
+ uv_select_all_perform_multi_ex(scene, sima, objects, objects_len, action, NULL);
}
static int uv_select_all_exec(bContext *C, wmOperator *op)
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Scene *scene = CTX_data_scene(C);
+ SpaceImage *sima = CTX_wm_space_image(C);
const ToolSettings *ts = scene->toolsettings;
ViewLayer *view_layer = CTX_data_view_layer(C);
@@ -1869,7 +2498,7 @@ static int uv_select_all_exec(bContext *C, wmOperator *op)
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
view_layer, ((View3D *)NULL), &objects_len);
- uv_select_all_perform_multi(scene, objects, objects_len, action);
+ uv_select_all_perform_multi(scene, sima, objects, objects_len, action);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -1956,7 +2585,7 @@ static int uv_mouse_select_multi(bContext *C,
}
else if (selectmode == UV_SELECT_EDGE) {
/* find edge */
- found_item = uv_find_nearest_edge_multi(scene, objects, objects_len, co, &hit);
+ found_item = uv_find_nearest_edge_multi(scene, objects, objects_len, co, penalty_dist, &hit);
if (found_item) {
if ((ts->uv_flag & UV_SYNC_SELECTION) == 0) {
BMesh *bm = BKE_editmesh_from_object(hit.ob)->bm;
@@ -1982,7 +2611,7 @@ static int uv_mouse_select_multi(bContext *C,
}
}
else if (selectmode == UV_SELECT_ISLAND) {
- found_item = uv_find_nearest_edge_multi(scene, objects, objects_len, co, &hit);
+ found_item = uv_find_nearest_edge_multi(scene, objects, objects_len, co, 0.0f, &hit);
if (!found_item) {
/* Without this, we can be within the face of an island but too far from an edge,
@@ -1994,7 +2623,7 @@ static int uv_mouse_select_multi(bContext *C,
if (!found_item) {
if (deselect_all) {
- uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT);
+ uv_select_all_perform_multi(scene, NULL, objects, objects_len, SEL_DESELECT);
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
Object *obedit = objects[ob_index];
@@ -2013,7 +2642,7 @@ static int uv_mouse_select_multi(bContext *C,
/* do selection */
if (selectmode == UV_SELECT_ISLAND) {
if (!extend) {
- uv_select_all_perform_multi_ex(scene, objects, objects_len, SEL_DESELECT, obedit);
+ uv_select_all_perform_multi_ex(scene, NULL, objects, objects_len, SEL_DESELECT, obedit);
}
/* Current behavior of 'extend'
* is actually toggling, so pass extend flag as 'toggle' here */
@@ -2056,7 +2685,7 @@ static int uv_mouse_select_multi(bContext *C,
else {
const bool select = true;
/* deselect all */
- uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT);
+ uv_select_all_perform_multi(scene, NULL, objects, objects_len, SEL_DESELECT);
if (selectmode == UV_SELECT_VERTEX) {
/* select vertex */
@@ -2198,7 +2827,7 @@ static int uv_mouse_select_loop_generic_multi(bContext *C,
int flush = 0;
/* Find edge. */
- found_item = uv_find_nearest_edge_multi(scene, objects, objects_len, co, &hit);
+ found_item = uv_find_nearest_edge_multi(scene, objects, objects_len, co, 0.0f, &hit);
if (!found_item) {
return OPERATOR_CANCELLED;
}
@@ -2208,11 +2837,16 @@ static int uv_mouse_select_loop_generic_multi(bContext *C,
/* Do selection. */
if (!extend) {
- uv_select_all_perform_multi_ex(scene, objects, objects_len, SEL_DESELECT, obedit);
+ uv_select_all_perform_multi_ex(scene, NULL, objects, objects_len, SEL_DESELECT, obedit);
}
if (loop_type == UV_LOOP_SELECT) {
- flush = uv_select_edgeloop(sima, scene, obedit, &hit, extend);
+ if (ED_uvedit_select_mode_get(scene) == UV_SELECT_FACE) {
+ flush = uv_select_faceloop(sima, scene, obedit, &hit, extend);
+ }
+ else {
+ flush = uv_select_edgeloop(sima, scene, obedit, &hit, extend);
+ }
}
else if (loop_type == UV_RING_SELECT) {
flush = uv_select_edgering(sima, scene, obedit, &hit, extend);
@@ -2264,15 +2898,7 @@ static int uv_select_loop_exec(bContext *C, wmOperator *op)
RNA_float_get_array(op->ptr, "location", co);
const bool extend = RNA_boolean_get(op->ptr, "extend");
- Scene *scene = CTX_data_scene(C);
- enum eUVLoopGenericType type = UV_LOOP_SELECT;
- if (ED_uvedit_select_mode_get(scene) == UV_SELECT_FACE) {
- /* For now ring-select and face-loop is the same thing,
- * if we support real edge selection this will no longer be the case. */
- type = UV_RING_SELECT;
- }
-
- return uv_mouse_select_loop_generic(C, co, extend, type);
+ return uv_mouse_select_loop_generic(C, co, extend, UV_LOOP_SELECT);
}
static int uv_select_loop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
@@ -2422,14 +3048,14 @@ static int uv_select_linked_internal(bContext *C, wmOperator *op, const wmEvent
RNA_float_get_array(op->ptr, "location", co);
}
- if (!uv_find_nearest_edge_multi(scene, objects, objects_len, co, &hit)) {
+ if (!uv_find_nearest_edge_multi(scene, objects, objects_len, co, 0.0f, &hit)) {
MEM_freeN(objects);
return OPERATOR_CANCELLED;
}
}
if (!extend && !deselect) {
- uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT);
+ uv_select_all_perform_multi(scene, NULL, objects, objects_len, SEL_DESELECT);
}
uv_select_linked_multi(
@@ -2576,8 +3202,8 @@ static int uv_select_split_exec(bContext *C, wmOperator *op)
const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
- bool is_sel = false;
- bool is_unsel = false;
+ /* Assume UV face is selected */
+ bool uv_face_is_sel = true;
if (!uvedit_face_visible_test(scene, efa)) {
continue;
@@ -2587,23 +3213,20 @@ static int uv_select_split_exec(bContext *C, wmOperator *op)
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
- if (luv->flag & MLOOPUV_VERTSEL) {
- is_sel = true;
+ if ((luv->flag & MLOOPUV_VERTSEL) && (luv->flag & MLOOPUV_EDGESEL)) {
+ continue;
}
else {
- is_unsel = true;
- }
-
- /* we have mixed selection, bail out */
- if (is_sel && is_unsel) {
+ uv_face_is_sel = false;
break;
}
}
- if (is_sel && is_unsel) {
+ if (!uv_face_is_sel) {
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
luv->flag &= ~MLOOPUV_VERTSEL;
+ luv->flag &= ~MLOOPUV_EDGESEL;
}
changed = true;
@@ -2712,7 +3335,7 @@ static void uv_select_flush_from_tag_sticky_loc_internal(Scene *scene,
/**
* Flush the selection from face tags based on sticky and selection modes.
*
- * needed because settings the selection a face is done in a number of places but it also
+ * needed because setting the selection of a face is done in a number of places but it also
* needs to respect the sticky modes for the UV verts, so dealing with the sticky modes
* is best done in a separate function.
*
@@ -2737,7 +3360,11 @@ static void uv_select_flush_from_tag_face(SpaceImage *sima,
BMIter iter, liter;
const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
- if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_VERTEX) {
+ if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 &&
+ ((sima->sticky == SI_STICKY_VERTEX) || (sima->sticky == SI_STICKY_LOC))) {
+ /* TEMPORARY : Commenting out old code that was used for sticky vertex mode, since sticky
+ * vertex for face selection now fallbacks to sticky location */
+#if 0
/* Tag all verts as untouched, then touch the ones that have a face center
* in the loop and select all MLoopUV's that use a touched vert. */
BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false);
@@ -2761,7 +3388,7 @@ static void uv_select_flush_from_tag_face(SpaceImage *sima,
}
}
}
- else if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_LOC) {
+#endif
struct UvVertMap *vmap;
uint efa_index;
@@ -2776,8 +3403,19 @@ static void uv_select_flush_from_tag_face(SpaceImage *sima,
/* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */
BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
- uv_select_flush_from_tag_sticky_loc_internal(
- scene, em, vmap, efa_index, l, select, cd_loop_uv_offset);
+ MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+ if (select) {
+ luv->flag |= MLOOPUV_EDGESEL;
+ uv_select_flush_from_tag_sticky_loc_internal(
+ scene, em, vmap, efa_index, l, select, cd_loop_uv_offset);
+ }
+ else {
+ luv->flag &= ~MLOOPUV_EDGESEL;
+ if (!uvedit_vert_is_any_other_face_selected(scene, l, l->v, cd_loop_uv_offset)) {
+ uv_select_flush_from_tag_sticky_loc_internal(
+ scene, em, vmap, efa_index, l, select, cd_loop_uv_offset);
+ }
+ }
}
}
}
@@ -2795,16 +3433,16 @@ static void uv_select_flush_from_tag_face(SpaceImage *sima,
/**
* Flush the selection from loop tags based on sticky and selection modes.
*
- * needed because settings the selection a face is done in a number of places but it also needs
+ * needed because setting the selection of a face is done in a number of places but it also needs
* to respect the sticky modes for the UV verts, so dealing with the sticky modes is best done
* in a separate function.
*
- * \note This function is very similar to #uv_select_flush_from_tag_loop,
+ * \note This function is very similar to #uv_select_flush_from_tag_face,
* be sure to update both upon changing.
*/
-static void uv_select_flush_from_tag_loop(SpaceImage *sima,
+static void uv_select_flush_from_tag_loop(const SpaceImage *sima,
Scene *scene,
- Object *obedit,
+ BMEditMesh *em,
const bool select)
{
/* Selecting UV Loops with some modes requires us to change
@@ -2814,7 +3452,6 @@ static void uv_select_flush_from_tag_loop(SpaceImage *sima,
* selection (so for sticky modes, vertex or location based). */
const ToolSettings *ts = scene->toolsettings;
- BMEditMesh *em = BKE_editmesh_from_object(obedit);
BMFace *efa;
BMLoop *l;
BMIter iter, liter;
@@ -2844,6 +3481,13 @@ static void uv_select_flush_from_tag_loop(SpaceImage *sima,
}
}
}
+
+ /* If UV edge selection mode then flush selection upwards. Not required for vertex selection
+ * mode since edge selections for vertex selection mode is handled within
+ * uvedit_uv_select_set() itself */
+ if (ts->uv_selectmode == UV_SELECT_EDGE) {
+ uv_flush_vert_to_edge(scene, em, cd_loop_uv_offset);
+ }
}
else if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_LOC) {
struct UvVertMap *vmap;
@@ -2866,6 +3510,13 @@ static void uv_select_flush_from_tag_loop(SpaceImage *sima,
}
}
BM_uv_vert_map_free(vmap);
+
+ /* If UV edge selection mode then flush selection upwards. Not required for vertex selection
+ * mode since edge selections for vertex selection mode is handled within
+ * uvedit_uv_select_set() itself */
+ if (ts->uv_selectmode == UV_SELECT_EDGE) {
+ uv_flush_vert_to_edge(scene, em, cd_loop_uv_offset);
+ }
}
else { /* SI_STICKY_DISABLE or ts->uv_flag & UV_SYNC_SELECTION */
BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
@@ -2875,6 +3526,12 @@ static void uv_select_flush_from_tag_loop(SpaceImage *sima,
}
}
}
+ /* If UV edge selection mode then flush selection upwards. Not required for vertex selection
+ * mode since edge selections for vertex selection mode is handled within
+ * uvedit_uv_select_set() itself */
+ if (ts->uv_selectmode == UV_SELECT_EDGE) {
+ uv_flush_vert_to_edge(scene, em, cd_loop_uv_offset);
+ }
}
}
@@ -2924,7 +3581,7 @@ static int uv_box_select_exec(bContext *C, wmOperator *op)
view_layer, ((View3D *)NULL), &objects_len);
if (use_pre_deselect) {
- uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT);
+ uv_select_all_perform_multi(scene, NULL, objects, objects_len, SEL_DESELECT);
}
/* don't indent to avoid diff noise! */
@@ -3147,7 +3804,7 @@ static int uv_circle_select_exec(bContext *C, wmOperator *op)
const bool use_pre_deselect = SEL_OP_USE_PRE_DESELECT(sel_op);
if (use_pre_deselect) {
- uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT);
+ uv_select_all_perform_multi(scene, NULL, objects, objects_len, SEL_DESELECT);
}
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
@@ -3326,7 +3983,7 @@ static bool do_lasso_select_mesh_uv(bContext *C,
view_layer, ((View3D *)NULL), &objects_len);
if (use_pre_deselect) {
- uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT);
+ uv_select_all_perform_multi(scene, NULL, objects, objects_len, SEL_DESELECT);
}
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
@@ -3506,6 +4163,12 @@ static int uv_select_pinned_exec(bContext *C, wmOperator *UNUSED(op))
}
}
+ /* Flush selection
+ * REASON : uvedit_uv_select_enable() allows edge selection only in vertex select mode */
+ if (ts->uv_selectmode != UV_SELECT_VERTEX) {
+ uv_flush_vert_to_edge(scene, em, cd_loop_uv_offset);
+ }
+
if (changed) {
uv_select_tag_update_for_object(depsgraph, ts, obedit);
}
@@ -3626,7 +4289,7 @@ static int uv_select_overlap(bContext *C, const bool extend)
BM_mesh_elem_index_ensure(em->bm, BM_VERT | BM_FACE);
BM_mesh_elem_hflag_disable_all(em->bm, BM_FACE, BM_ELEM_TAG, false);
if (!extend) {
- uv_select_all_perform(scene, obedit, SEL_DESELECT);
+ uv_select_all_perform(scene, NULL, obedit, SEL_DESELECT);
}
BMIter iter;
diff --git a/source/blender/editors/uvedit/uvedit_smart_stitch.c b/source/blender/editors/uvedit/uvedit_smart_stitch.c
index c5485cc1495..ef451d3fdaf 100644
--- a/source/blender/editors/uvedit/uvedit_smart_stitch.c
+++ b/source/blender/editors/uvedit/uvedit_smart_stitch.c
@@ -2561,7 +2561,7 @@ static StitchState *stitch_select(bContext *C,
return state;
}
}
- else if (uv_find_nearest_edge_multi(scene, ssc->objects, ssc->objects_len, co, &hit)) {
+ else if (uv_find_nearest_edge_multi(scene, ssc->objects, ssc->objects_len, co, 0.0f, &hit)) {
/* find StitchState from hit->ob */
StitchState *state = NULL;
for (uint ob_index = 0; ob_index < ssc->objects_len; ob_index++) {
diff --git a/source/blender/makesdna/DNA_meshdata_types.h b/source/blender/makesdna/DNA_meshdata_types.h
index bc6b35c8e43..4bc1de3b78e 100644
--- a/source/blender/makesdna/DNA_meshdata_types.h
+++ b/source/blender/makesdna/DNA_meshdata_types.h
@@ -337,7 +337,7 @@ typedef struct MLoopUV {
/** #MLoopUV.flag */
enum {
- /* MLOOPUV_DEPRECATED = (1 << 0), MLOOPUV_EDGESEL removed */
+ MLOOPUV_EDGESEL = (1 << 0),
MLOOPUV_VERTSEL = (1 << 1),
MLOOPUV_PINNED = (1 << 2),
};
diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c
index 5a937e6b06b..7a45cac94ee 100644
--- a/source/blender/makesrna/intern/rna_mesh.c
+++ b/source/blender/makesrna/intern/rna_mesh.c
@@ -2102,6 +2102,10 @@ static void rna_def_mloopuv(BlenderRNA *brna)
prop = RNA_def_property(srna, "select", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", MLOOPUV_VERTSEL);
RNA_def_property_ui_text(prop, "UV Select", "");
+
+ prop = RNA_def_property(srna, "select_edge", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", MLOOPUV_EDGESEL);
+ RNA_def_property_ui_text(prop, "UV Edge Select", "");
}
static void rna_def_mloopcol(BlenderRNA *brna)
diff --git a/source/blender/python/bmesh/bmesh_py_types_meshdata.c b/source/blender/python/bmesh/bmesh_py_types_meshdata.c
index d0c745e6a1d..ca5d408bfdb 100644
--- a/source/blender/python/bmesh/bmesh_py_types_meshdata.c
+++ b/source/blender/python/bmesh/bmesh_py_types_meshdata.c
@@ -72,6 +72,7 @@ static int bpy_bmloopuv_uv_set(BPy_BMLoopUV *self, PyObject *value, void *UNUSED
PyDoc_STRVAR(bpy_bmloopuv_flag__pin_uv_doc, "UV pin state.\n\n:type: boolean");
PyDoc_STRVAR(bpy_bmloopuv_flag__select_doc, "UV select state.\n\n:type: boolean");
+PyDoc_STRVAR(bpy_bmloopuv_flag__select_edge_doc, "UV edge select state.\n\n:type: boolean");
static PyObject *bpy_bmloopuv_flag_get(BPy_BMLoopUV *self, void *flag_p)
{
@@ -109,6 +110,11 @@ static PyGetSetDef bpy_bmloopuv_getseters[] = {
(setter)bpy_bmloopuv_flag_set,
bpy_bmloopuv_flag__select_doc,
(void *)MLOOPUV_VERTSEL},
+ {"select_edge",
+ (getter)bpy_bmloopuv_flag_get,
+ (setter)bpy_bmloopuv_flag_set,
+ bpy_bmloopuv_flag__select_edge_doc,
+ (void *)MLOOPUV_EDGESEL},
{NULL, NULL, NULL, NULL, NULL} /* Sentinel */
};