1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  */
19 
20 /** \file
21  * \ingroup eduv
22  */
23 
24 #include <math.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include "MEM_guardedalloc.h"
29 
30 #include "DNA_image_types.h"
31 #include "DNA_meshdata_types.h"
32 #include "DNA_node_types.h"
33 #include "DNA_object_types.h"
34 #include "DNA_scene_types.h"
35 #include "DNA_space_types.h"
36 
37 #include "BLI_alloca.h"
38 #include "BLI_blenlib.h"
39 #include "BLI_hash.h"
40 #include "BLI_kdopbvh.h"
41 #include "BLI_lasso_2d.h"
42 #include "BLI_math.h"
43 #include "BLI_polyfill_2d.h"
44 #include "BLI_utildefines.h"
45 
46 #include "BKE_context.h"
47 #include "BKE_customdata.h"
48 #include "BKE_editmesh.h"
49 #include "BKE_layer.h"
50 #include "BKE_mesh.h"
51 #include "BKE_mesh_mapping.h"
52 #include "BKE_report.h"
53 
54 #include "DEG_depsgraph.h"
55 #include "DEG_depsgraph_query.h"
56 
57 #include "ED_image.h"
58 #include "ED_mesh.h"
59 #include "ED_screen.h"
60 #include "ED_select_utils.h"
61 #include "ED_uvedit.h"
62 
63 #include "RNA_access.h"
64 #include "RNA_define.h"
65 
66 #include "WM_api.h"
67 #include "WM_types.h"
68 
69 #include "UI_view2d.h"
70 
71 #include "uvedit_intern.h"
72 
73 static void uv_select_all_perform(Scene *scene, Object *obedit, int action);
74 
75 static void uv_select_all_perform_multi_ex(
76     Scene *scene, Object **objects, const uint objects_len, int action, const Object *ob_exclude);
77 static void uv_select_all_perform_multi(Scene *scene,
78                                         Object **objects,
79                                         const uint objects_len,
80                                         int action);
81 
82 static void uv_select_flush_from_tag_face(SpaceImage *sima,
83                                           Scene *scene,
84                                           Object *obedit,
85                                           const bool select);
86 static void uv_select_flush_from_tag_loop(SpaceImage *sima,
87                                           Scene *scene,
88                                           Object *obedit,
89                                           const bool select);
90 static void uv_select_tag_update_for_object(Depsgraph *depsgraph,
91                                             const ToolSettings *ts,
92                                             Object *obedit);
93 
94 /* -------------------------------------------------------------------- */
95 /** \name Active Selection Tracking
96  *
97  * Currently we don't store loops in the selection history,
98  * store face/edge/vert combinations (needed for UV path selection).
99  * \{ */
100 
ED_uvedit_active_vert_loop_set(BMesh * bm,BMLoop * l)101 void ED_uvedit_active_vert_loop_set(BMesh *bm, BMLoop *l)
102 {
103   BM_select_history_clear(bm);
104   BM_select_history_remove(bm, (BMElem *)l->f);
105   BM_select_history_remove(bm, (BMElem *)l->v);
106   BM_select_history_store_notest(bm, (BMElem *)l->f);
107   BM_select_history_store_notest(bm, (BMElem *)l->v);
108 }
109 
ED_uvedit_active_vert_loop_get(BMesh * bm)110 BMLoop *ED_uvedit_active_vert_loop_get(BMesh *bm)
111 {
112   BMEditSelection *ese = bm->selected.last;
113   if (ese && ese->prev) {
114     BMEditSelection *ese_prev = ese->prev;
115     if ((ese->htype == BM_VERT) && (ese_prev->htype == BM_FACE)) {
116       /* May be NULL. */
117       return BM_face_vert_share_loop((BMFace *)ese_prev->ele, (BMVert *)ese->ele);
118     }
119   }
120   return NULL;
121 }
122 
ED_uvedit_active_edge_loop_set(BMesh * bm,BMLoop * l)123 void ED_uvedit_active_edge_loop_set(BMesh *bm, BMLoop *l)
124 {
125   BM_select_history_clear(bm);
126   BM_select_history_remove(bm, (BMElem *)l->f);
127   BM_select_history_remove(bm, (BMElem *)l->e);
128   BM_select_history_store_notest(bm, (BMElem *)l->f);
129   BM_select_history_store_notest(bm, (BMElem *)l->e);
130 }
131 
ED_uvedit_active_edge_loop_get(BMesh * bm)132 BMLoop *ED_uvedit_active_edge_loop_get(BMesh *bm)
133 {
134   BMEditSelection *ese = bm->selected.last;
135   if (ese && ese->prev) {
136     BMEditSelection *ese_prev = ese->prev;
137     if ((ese->htype == BM_EDGE) && (ese_prev->htype == BM_FACE)) {
138       /* May be NULL. */
139       return BM_face_edge_share_loop((BMFace *)ese_prev->ele, (BMEdge *)ese->ele);
140     }
141   }
142   return NULL;
143 }
144 
145 /** \} */
146 
147 /* -------------------------------------------------------------------- */
148 /** \name Visibility and Selection Utilities
149  * \{ */
150 
151 /**
152  * Intentionally don't return #UV_SELECT_ISLAND as it's not an element type.
153  * In this case return #UV_SELECT_VERTEX as a fallback.
154  */
ED_uvedit_select_mode_get(const Scene * scene)155 char ED_uvedit_select_mode_get(const Scene *scene)
156 {
157   const ToolSettings *ts = scene->toolsettings;
158   char uv_selectmode = UV_SELECT_VERTEX;
159 
160   if (ts->uv_flag & UV_SYNC_SELECTION) {
161     if (ts->selectmode & SCE_SELECT_VERTEX) {
162       uv_selectmode = UV_SELECT_VERTEX;
163     }
164     else if (ts->selectmode & SCE_SELECT_EDGE) {
165       uv_selectmode = UV_SELECT_EDGE;
166     }
167     else if (ts->selectmode & SCE_SELECT_FACE) {
168       uv_selectmode = UV_SELECT_FACE;
169     }
170   }
171   else {
172     if (ts->uv_selectmode & UV_SELECT_VERTEX) {
173       uv_selectmode = UV_SELECT_VERTEX;
174     }
175     else if (ts->uv_selectmode & UV_SELECT_EDGE) {
176       uv_selectmode = UV_SELECT_EDGE;
177     }
178     else if (ts->uv_selectmode & UV_SELECT_FACE) {
179       uv_selectmode = UV_SELECT_FACE;
180     }
181   }
182   return uv_selectmode;
183 }
184 
ED_uvedit_select_sync_flush(const ToolSettings * ts,BMEditMesh * em,const bool select)185 void ED_uvedit_select_sync_flush(const ToolSettings *ts, BMEditMesh *em, const bool select)
186 {
187   /* bmesh API handles flushing but not on de-select */
188   if (ts->uv_flag & UV_SYNC_SELECTION) {
189     if (ts->selectmode != SCE_SELECT_FACE) {
190       if (select == false) {
191         EDBM_deselect_flush(em);
192       }
193       else {
194         EDBM_select_flush(em);
195       }
196     }
197 
198     if (select == false) {
199       BM_select_history_validate(em->bm);
200     }
201   }
202 }
203 
204 /**
205  * Apply a penalty to elements that are already selected
206  * so elements that aren't already selected are prioritized.
207  *
208  * \note This is calculated in screen-space otherwise zooming in on a uv-vert and
209  * shift-selecting can consider an adjacent point close enough to add to
210  * the selection rather than de-selecting the closest.
211  */
uv_select_penalty_default(SpaceImage * sima)212 static float uv_select_penalty_default(SpaceImage *sima)
213 {
214   float penalty[2];
215   uvedit_pixel_to_float(sima, 5.0f / (sima ? sima->zoom : 1.0f), penalty);
216   return len_v2(penalty);
217 }
218 
uvedit_vertex_select_tagged(BMEditMesh * em,Scene * scene,bool select,int cd_loop_uv_offset)219 static void uvedit_vertex_select_tagged(BMEditMesh *em,
220                                         Scene *scene,
221                                         bool select,
222                                         int cd_loop_uv_offset)
223 {
224   BMFace *efa;
225   BMLoop *l;
226   BMIter iter, liter;
227 
228   BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
229     BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
230       if (BM_elem_flag_test(l->v, BM_ELEM_TAG)) {
231         uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
232       }
233     }
234   }
235 }
236 
uvedit_face_visible_test_ex(const ToolSettings * ts,BMFace * efa)237 bool uvedit_face_visible_test_ex(const ToolSettings *ts, BMFace *efa)
238 {
239   if (ts->uv_flag & UV_SYNC_SELECTION) {
240     return (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) == 0);
241   }
242   return (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) == 0 && BM_elem_flag_test(efa, BM_ELEM_SELECT));
243 }
uvedit_face_visible_test(const Scene * scene,BMFace * efa)244 bool uvedit_face_visible_test(const Scene *scene, BMFace *efa)
245 {
246   return uvedit_face_visible_test_ex(scene->toolsettings, efa);
247 }
248 
uvedit_face_select_test_ex(const ToolSettings * ts,BMFace * efa,const int cd_loop_uv_offset)249 bool uvedit_face_select_test_ex(const ToolSettings *ts, BMFace *efa, const int cd_loop_uv_offset)
250 {
251   if (ts->uv_flag & UV_SYNC_SELECTION) {
252     return (BM_elem_flag_test(efa, BM_ELEM_SELECT));
253   }
254 
255   BMLoop *l;
256   MLoopUV *luv;
257   BMIter liter;
258 
259   BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
260     luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
261     if (!(luv->flag & MLOOPUV_VERTSEL)) {
262       return false;
263     }
264   }
265   return true;
266 }
uvedit_face_select_test(const Scene * scene,BMFace * efa,const int cd_loop_uv_offset)267 bool uvedit_face_select_test(const Scene *scene, BMFace *efa, const int cd_loop_uv_offset)
268 {
269   return uvedit_face_select_test_ex(scene->toolsettings, efa, cd_loop_uv_offset);
270 }
271 
uvedit_face_select_set_with_sticky(const SpaceImage * sima,const Scene * scene,BMEditMesh * em,BMFace * efa,const bool select,const bool do_history,const int cd_loop_uv_offset)272 void uvedit_face_select_set_with_sticky(const SpaceImage *sima,
273                                         const Scene *scene,
274                                         BMEditMesh *em,
275                                         BMFace *efa,
276                                         const bool select,
277                                         const bool do_history,
278                                         const int cd_loop_uv_offset)
279 {
280   const ToolSettings *ts = scene->toolsettings;
281   if (ts->uv_flag & UV_SYNC_SELECTION) {
282     uvedit_face_select_set(scene, em, efa, select, do_history, cd_loop_uv_offset);
283     return;
284   }
285 
286   BMLoop *l_iter, *l_first;
287   l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
288   do {
289     uvedit_uv_select_set_with_sticky(
290         sima, scene, em, l_iter, select, do_history, cd_loop_uv_offset);
291   } while ((l_iter = l_iter->next) != l_first);
292 }
293 
uvedit_face_select_set(const struct Scene * scene,struct BMEditMesh * em,struct BMFace * efa,const bool select,const bool do_history,const int cd_loop_uv_offset)294 void uvedit_face_select_set(const struct Scene *scene,
295                             struct BMEditMesh *em,
296                             struct BMFace *efa,
297                             const bool select,
298                             const bool do_history,
299                             const int cd_loop_uv_offset)
300 {
301   if (select) {
302     uvedit_face_select_enable(scene, em, efa, do_history, cd_loop_uv_offset);
303   }
304   else {
305     uvedit_face_select_disable(scene, em, efa, cd_loop_uv_offset);
306   }
307 }
308 
uvedit_face_select_enable(const Scene * scene,BMEditMesh * em,BMFace * efa,const bool do_history,const int cd_loop_uv_offset)309 void uvedit_face_select_enable(const Scene *scene,
310                                BMEditMesh *em,
311                                BMFace *efa,
312                                const bool do_history,
313                                const int cd_loop_uv_offset)
314 {
315   const ToolSettings *ts = scene->toolsettings;
316 
317   if (ts->uv_flag & UV_SYNC_SELECTION) {
318     BM_face_select_set(em->bm, efa, true);
319     if (do_history) {
320       BM_select_history_store(em->bm, (BMElem *)efa);
321     }
322   }
323   else {
324     BMLoop *l;
325     MLoopUV *luv;
326     BMIter liter;
327 
328     BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
329       luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
330       luv->flag |= MLOOPUV_VERTSEL;
331     }
332   }
333 }
334 
uvedit_face_select_disable(const Scene * scene,BMEditMesh * em,BMFace * efa,const int cd_loop_uv_offset)335 void uvedit_face_select_disable(const Scene *scene,
336                                 BMEditMesh *em,
337                                 BMFace *efa,
338                                 const int cd_loop_uv_offset)
339 {
340   const ToolSettings *ts = scene->toolsettings;
341 
342   if (ts->uv_flag & UV_SYNC_SELECTION) {
343     BM_face_select_set(em->bm, efa, false);
344   }
345   else {
346     BMLoop *l;
347     MLoopUV *luv;
348     BMIter liter;
349 
350     BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
351       luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
352       luv->flag &= ~MLOOPUV_VERTSEL;
353     }
354   }
355 }
356 
uvedit_edge_select_test_ex(const ToolSettings * ts,BMLoop * l,const int cd_loop_uv_offset)357 bool uvedit_edge_select_test_ex(const ToolSettings *ts, BMLoop *l, const int cd_loop_uv_offset)
358 {
359   if (ts->uv_flag & UV_SYNC_SELECTION) {
360     if (ts->selectmode & SCE_SELECT_FACE) {
361       return BM_elem_flag_test(l->f, BM_ELEM_SELECT);
362     }
363     if (ts->selectmode == SCE_SELECT_EDGE) {
364       return BM_elem_flag_test(l->e, BM_ELEM_SELECT);
365     }
366     return BM_elem_flag_test(l->v, BM_ELEM_SELECT) &&
367            BM_elem_flag_test(l->next->v, BM_ELEM_SELECT);
368   }
369 
370   MLoopUV *luv1, *luv2;
371 
372   luv1 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
373   luv2 = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
374 
375   return (luv1->flag & MLOOPUV_VERTSEL) && (luv2->flag & MLOOPUV_VERTSEL);
376 }
uvedit_edge_select_test(const Scene * scene,BMLoop * l,const int cd_loop_uv_offset)377 bool uvedit_edge_select_test(const Scene *scene, BMLoop *l, const int cd_loop_uv_offset)
378 {
379   return uvedit_edge_select_test_ex(scene->toolsettings, l, cd_loop_uv_offset);
380 }
381 
uvedit_edge_select_set_with_sticky(const struct SpaceImage * sima,const Scene * scene,BMEditMesh * em,BMLoop * l,const bool select,const bool do_history,const uint cd_loop_uv_offset)382 void uvedit_edge_select_set_with_sticky(const struct SpaceImage *sima,
383                                         const Scene *scene,
384                                         BMEditMesh *em,
385                                         BMLoop *l,
386                                         const bool select,
387                                         const bool do_history,
388                                         const uint cd_loop_uv_offset)
389 {
390   const ToolSettings *ts = scene->toolsettings;
391   if (ts->uv_flag & UV_SYNC_SELECTION) {
392     uvedit_edge_select_set(scene, em, l, select, do_history, cd_loop_uv_offset);
393     return;
394   }
395 
396   uvedit_uv_select_set_with_sticky(sima, scene, em, l, select, do_history, cd_loop_uv_offset);
397   uvedit_uv_select_set_with_sticky(
398       sima, scene, em, l->next, select, do_history, cd_loop_uv_offset);
399 }
400 
uvedit_edge_select_set(const Scene * scene,BMEditMesh * em,BMLoop * l,const bool select,const bool do_history,const int cd_loop_uv_offset)401 void uvedit_edge_select_set(const Scene *scene,
402                             BMEditMesh *em,
403                             BMLoop *l,
404                             const bool select,
405                             const bool do_history,
406                             const int cd_loop_uv_offset)
407 
408 {
409   if (select) {
410     uvedit_edge_select_enable(scene, em, l, do_history, cd_loop_uv_offset);
411   }
412   else {
413     uvedit_edge_select_disable(scene, em, l, cd_loop_uv_offset);
414   }
415 }
416 
uvedit_edge_select_enable(const Scene * scene,BMEditMesh * em,BMLoop * l,const bool do_history,const int cd_loop_uv_offset)417 void uvedit_edge_select_enable(const Scene *scene,
418                                BMEditMesh *em,
419                                BMLoop *l,
420                                const bool do_history,
421                                const int cd_loop_uv_offset)
422 
423 {
424   const ToolSettings *ts = scene->toolsettings;
425 
426   if (ts->uv_flag & UV_SYNC_SELECTION) {
427     if (ts->selectmode & SCE_SELECT_FACE) {
428       BM_face_select_set(em->bm, l->f, true);
429     }
430     else if (ts->selectmode & SCE_SELECT_EDGE) {
431       BM_edge_select_set(em->bm, l->e, true);
432     }
433     else {
434       BM_vert_select_set(em->bm, l->e->v1, true);
435       BM_vert_select_set(em->bm, l->e->v2, true);
436     }
437 
438     if (do_history) {
439       BM_select_history_store(em->bm, (BMElem *)l->e);
440     }
441   }
442   else {
443     MLoopUV *luv1, *luv2;
444 
445     luv1 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
446     luv2 = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
447 
448     luv1->flag |= MLOOPUV_VERTSEL;
449     luv2->flag |= MLOOPUV_VERTSEL;
450   }
451 }
452 
uvedit_edge_select_disable(const Scene * scene,BMEditMesh * em,BMLoop * l,const int cd_loop_uv_offset)453 void uvedit_edge_select_disable(const Scene *scene,
454                                 BMEditMesh *em,
455                                 BMLoop *l,
456                                 const int cd_loop_uv_offset)
457 
458 {
459   const ToolSettings *ts = scene->toolsettings;
460 
461   if (ts->uv_flag & UV_SYNC_SELECTION) {
462     if (ts->selectmode & SCE_SELECT_FACE) {
463       BM_face_select_set(em->bm, l->f, false);
464     }
465     else if (ts->selectmode & SCE_SELECT_EDGE) {
466       BM_edge_select_set(em->bm, l->e, false);
467     }
468     else {
469       BM_vert_select_set(em->bm, l->e->v1, false);
470       BM_vert_select_set(em->bm, l->e->v2, false);
471     }
472   }
473   else {
474     MLoopUV *luv1, *luv2;
475 
476     luv1 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
477     luv2 = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
478 
479     luv1->flag &= ~MLOOPUV_VERTSEL;
480     luv2->flag &= ~MLOOPUV_VERTSEL;
481   }
482 }
483 
uvedit_uv_select_test_ex(const ToolSettings * ts,BMLoop * l,const int cd_loop_uv_offset)484 bool uvedit_uv_select_test_ex(const ToolSettings *ts, BMLoop *l, const int cd_loop_uv_offset)
485 {
486   if (ts->uv_flag & UV_SYNC_SELECTION) {
487     if (ts->selectmode & SCE_SELECT_FACE) {
488       return BM_elem_flag_test_bool(l->f, BM_ELEM_SELECT);
489     }
490     return BM_elem_flag_test_bool(l->v, BM_ELEM_SELECT);
491   }
492 
493   MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
494   return (luv->flag & MLOOPUV_VERTSEL) != 0;
495 }
uvedit_uv_select_test(const Scene * scene,BMLoop * l,const int cd_loop_uv_offset)496 bool uvedit_uv_select_test(const Scene *scene, BMLoop *l, const int cd_loop_uv_offset)
497 {
498   return uvedit_uv_select_test_ex(scene->toolsettings, l, cd_loop_uv_offset);
499 }
500 
uvedit_uv_select_set_with_sticky(const struct SpaceImage * sima,const Scene * scene,BMEditMesh * em,BMLoop * l,const bool select,const bool do_history,const uint cd_loop_uv_offset)501 void uvedit_uv_select_set_with_sticky(const struct SpaceImage *sima,
502                                       const Scene *scene,
503                                       BMEditMesh *em,
504                                       BMLoop *l,
505                                       const bool select,
506                                       const bool do_history,
507                                       const uint cd_loop_uv_offset)
508 {
509   const ToolSettings *ts = scene->toolsettings;
510   if (ts->uv_flag & UV_SYNC_SELECTION) {
511     uvedit_uv_select_set(scene, em, l, select, do_history, cd_loop_uv_offset);
512     return;
513   }
514 
515   const int sticky = sima->sticky;
516   switch (sticky) {
517     case SI_STICKY_DISABLE: {
518       uvedit_uv_select_set(scene, em, l, select, do_history, cd_loop_uv_offset);
519       break;
520     }
521     default: {
522       /* #SI_STICKY_VERTEX or #SI_STICKY_LOC. */
523       const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
524       BMEdge *e_first, *e_iter;
525       e_first = e_iter = l->e;
526       do {
527         if (e_iter->l) {
528           BMLoop *l_radial_iter = e_iter->l;
529           do {
530             if (l_radial_iter->v == l->v) {
531               if (uvedit_face_visible_test(scene, l_radial_iter->f)) {
532                 bool do_select = false;
533                 if (sticky == SI_STICKY_VERTEX) {
534                   do_select = true;
535                 }
536                 else {
537                   const MLoopUV *luv_other = BM_ELEM_CD_GET_VOID_P(l_radial_iter,
538                                                                    cd_loop_uv_offset);
539                   if (equals_v2v2(luv_other->uv, luv->uv)) {
540                     do_select = true;
541                   }
542                 }
543 
544                 if (do_select) {
545                   uvedit_uv_select_set(
546                       scene, em, l_radial_iter, select, do_history, cd_loop_uv_offset);
547                 }
548               }
549             }
550           } while ((l_radial_iter = l_radial_iter->radial_next) != e_iter->l);
551         }
552       } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, l->v)) != e_first);
553     }
554   }
555 }
556 
uvedit_uv_select_set(const Scene * scene,BMEditMesh * em,BMLoop * l,const bool select,const bool do_history,const int cd_loop_uv_offset)557 void uvedit_uv_select_set(const Scene *scene,
558                           BMEditMesh *em,
559                           BMLoop *l,
560                           const bool select,
561                           const bool do_history,
562                           const int cd_loop_uv_offset)
563 {
564   if (select) {
565     uvedit_uv_select_enable(scene, em, l, do_history, cd_loop_uv_offset);
566   }
567   else {
568     uvedit_uv_select_disable(scene, em, l, cd_loop_uv_offset);
569   }
570 }
571 
uvedit_uv_select_enable(const Scene * scene,BMEditMesh * em,BMLoop * l,const bool do_history,const int cd_loop_uv_offset)572 void uvedit_uv_select_enable(const Scene *scene,
573                              BMEditMesh *em,
574                              BMLoop *l,
575                              const bool do_history,
576                              const int cd_loop_uv_offset)
577 {
578   const ToolSettings *ts = scene->toolsettings;
579 
580   if (ts->uv_flag & UV_SYNC_SELECTION) {
581     if (ts->selectmode & SCE_SELECT_FACE) {
582       BM_face_select_set(em->bm, l->f, true);
583     }
584     else {
585       BM_vert_select_set(em->bm, l->v, true);
586     }
587 
588     if (do_history) {
589       BM_select_history_store(em->bm, (BMElem *)l->v);
590     }
591   }
592   else {
593     MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
594     luv->flag |= MLOOPUV_VERTSEL;
595   }
596 }
597 
uvedit_uv_select_disable(const Scene * scene,BMEditMesh * em,BMLoop * l,const int cd_loop_uv_offset)598 void uvedit_uv_select_disable(const Scene *scene,
599                               BMEditMesh *em,
600                               BMLoop *l,
601                               const int cd_loop_uv_offset)
602 {
603   const ToolSettings *ts = scene->toolsettings;
604 
605   if (ts->uv_flag & UV_SYNC_SELECTION) {
606     if (ts->selectmode & SCE_SELECT_FACE) {
607       BM_face_select_set(em->bm, l->f, false);
608     }
609     else {
610       BM_vert_select_set(em->bm, l->v, false);
611     }
612   }
613   else {
614     MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
615     luv->flag &= ~MLOOPUV_VERTSEL;
616   }
617 }
618 
uvedit_loop_find_other_radial_loop_with_visible_face(const Scene * scene,BMLoop * l_src,const int cd_loop_uv_offset)619 static BMLoop *uvedit_loop_find_other_radial_loop_with_visible_face(const Scene *scene,
620                                                                     BMLoop *l_src,
621                                                                     const int cd_loop_uv_offset)
622 {
623   BMLoop *l_other = NULL;
624   BMLoop *l_iter = l_src->radial_next;
625   if (l_iter != l_src) {
626     do {
627       if (uvedit_face_visible_test(scene, l_iter->f) &&
628           BM_loop_uv_share_edge_check(l_src, l_iter, cd_loop_uv_offset)) {
629         /* Check UV's are contiguous. */
630         if (l_other == NULL) {
631           l_other = l_iter;
632         }
633         else {
634           /* Only use when there is a single alternative. */
635           l_other = NULL;
636           break;
637         }
638       }
639     } while ((l_iter = l_iter->radial_next) != l_src);
640   }
641   return l_other;
642 }
643 
uvedit_loop_find_other_boundary_loop_with_visible_face(const Scene * scene,BMLoop * l_edge,BMVert * v_pivot,const int cd_loop_uv_offset)644 static BMLoop *uvedit_loop_find_other_boundary_loop_with_visible_face(const Scene *scene,
645                                                                       BMLoop *l_edge,
646                                                                       BMVert *v_pivot,
647                                                                       const int cd_loop_uv_offset)
648 {
649   BLI_assert(uvedit_loop_find_other_radial_loop_with_visible_face(
650                  scene, l_edge, cd_loop_uv_offset) == NULL);
651 
652   BMLoop *l_step = l_edge;
653   l_step = (l_step->v == v_pivot) ? l_step->prev : l_step->next;
654   BMLoop *l_step_last = NULL;
655   do {
656     BLI_assert(BM_vert_in_edge(l_step->e, v_pivot));
657     l_step_last = l_step;
658     l_step = uvedit_loop_find_other_radial_loop_with_visible_face(
659         scene, l_step, cd_loop_uv_offset);
660     if (l_step) {
661       l_step = (l_step->v == v_pivot) ? l_step->prev : l_step->next;
662     }
663   } while (l_step != NULL);
664 
665   BM_elem_flag_set(l_step_last->e, BM_ELEM_SMOOTH, false);
666 
667   if (l_step_last != NULL) {
668     BLI_assert(uvedit_loop_find_other_radial_loop_with_visible_face(
669                    scene, l_step_last, cd_loop_uv_offset) == NULL);
670   }
671 
672   return l_step_last;
673 }
674 
675 /** \} */
676 
677 /* -------------------------------------------------------------------- */
678 /** \name Find Nearest Elements
679  * \{ */
680 
uv_find_nearest_edge(Scene * scene,Object * obedit,const float co[2],UvNearestHit * hit)681 bool uv_find_nearest_edge(Scene *scene, Object *obedit, const float co[2], UvNearestHit *hit)
682 {
683   BMEditMesh *em = BKE_editmesh_from_object(obedit);
684   BMFace *efa;
685   BMLoop *l;
686   BMIter iter, liter;
687   MLoopUV *luv, *luv_next;
688   int i;
689   bool found = false;
690 
691   const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
692 
693   BM_mesh_elem_index_ensure(em->bm, BM_VERT);
694 
695   BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
696     if (!uvedit_face_visible_test(scene, efa)) {
697       continue;
698     }
699     BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
700       luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
701       luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
702 
703       const float dist_test_sq = dist_squared_to_line_segment_v2(co, luv->uv, luv_next->uv);
704 
705       if (dist_test_sq < hit->dist_sq) {
706         hit->efa = efa;
707 
708         hit->l = l;
709 
710         hit->dist_sq = dist_test_sq;
711         found = true;
712       }
713     }
714   }
715   return found;
716 }
717 
uv_find_nearest_edge_multi(Scene * scene,Object ** objects,const uint objects_len,const float co[2],UvNearestHit * hit_final)718 bool uv_find_nearest_edge_multi(Scene *scene,
719                                 Object **objects,
720                                 const uint objects_len,
721                                 const float co[2],
722                                 UvNearestHit *hit_final)
723 {
724   bool found = false;
725   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
726     Object *obedit = objects[ob_index];
727     if (uv_find_nearest_edge(scene, obedit, co, hit_final)) {
728       hit_final->ob = obedit;
729       found = true;
730     }
731   }
732   return found;
733 }
734 
uv_find_nearest_face(Scene * scene,Object * obedit,const float co[2],UvNearestHit * hit_final)735 bool uv_find_nearest_face(Scene *scene, Object *obedit, const float co[2], UvNearestHit *hit_final)
736 {
737   BMEditMesh *em = BKE_editmesh_from_object(obedit);
738   bool found = false;
739 
740   const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
741 
742   /* this will fill in hit.vert1 and hit.vert2 */
743   float dist_sq_init = hit_final->dist_sq;
744   UvNearestHit hit = *hit_final;
745   if (uv_find_nearest_edge(scene, obedit, co, &hit)) {
746     hit.dist_sq = dist_sq_init;
747     hit.l = NULL;
748 
749     BMIter iter;
750     BMFace *efa;
751 
752     BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
753       if (!uvedit_face_visible_test(scene, efa)) {
754         continue;
755       }
756 
757       float cent[2];
758       BM_face_uv_calc_center_median(efa, cd_loop_uv_offset, cent);
759 
760       const float dist_test_sq = len_squared_v2v2(co, cent);
761 
762       if (dist_test_sq < hit.dist_sq) {
763         hit.efa = efa;
764         hit.dist_sq = dist_test_sq;
765         found = true;
766       }
767     }
768   }
769   if (found) {
770     *hit_final = hit;
771   }
772   return found;
773 }
774 
uv_find_nearest_face_multi(Scene * scene,Object ** objects,const uint objects_len,const float co[2],UvNearestHit * hit_final)775 bool uv_find_nearest_face_multi(Scene *scene,
776                                 Object **objects,
777                                 const uint objects_len,
778                                 const float co[2],
779                                 UvNearestHit *hit_final)
780 {
781   bool found = false;
782   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
783     Object *obedit = objects[ob_index];
784     if (uv_find_nearest_face(scene, obedit, co, hit_final)) {
785       hit_final->ob = obedit;
786       found = true;
787     }
788   }
789   return found;
790 }
791 
uv_nearest_between(const BMLoop * l,const float co[2],const int cd_loop_uv_offset)792 static bool uv_nearest_between(const BMLoop *l, const float co[2], const int cd_loop_uv_offset)
793 {
794   const float *uv_prev = ((MLoopUV *)BM_ELEM_CD_GET_VOID_P(l->prev, cd_loop_uv_offset))->uv;
795   const float *uv_curr = ((MLoopUV *)BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset))->uv;
796   const float *uv_next = ((MLoopUV *)BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset))->uv;
797 
798   return ((line_point_side_v2(uv_prev, uv_curr, co) > 0.0f) &&
799           (line_point_side_v2(uv_next, uv_curr, co) <= 0.0f));
800 }
801 
uv_find_nearest_vert(Scene * scene,Object * obedit,float const co[2],const float penalty_dist,UvNearestHit * hit_final)802 bool uv_find_nearest_vert(Scene *scene,
803                           Object *obedit,
804                           float const co[2],
805                           const float penalty_dist,
806                           UvNearestHit *hit_final)
807 {
808   bool found = false;
809 
810   /* this will fill in hit.vert1 and hit.vert2 */
811   float dist_sq_init = hit_final->dist_sq;
812   UvNearestHit hit = *hit_final;
813   if (uv_find_nearest_edge(scene, obedit, co, &hit)) {
814     hit.dist_sq = dist_sq_init;
815 
816     hit.l = NULL;
817 
818     BMEditMesh *em = BKE_editmesh_from_object(obedit);
819     BMFace *efa;
820     BMIter iter;
821 
822     BM_mesh_elem_index_ensure(em->bm, BM_VERT);
823 
824     const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
825 
826     BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
827       if (!uvedit_face_visible_test(scene, efa)) {
828         continue;
829       }
830 
831       BMIter liter;
832       BMLoop *l;
833       int i;
834       BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
835         float dist_test_sq;
836         MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
837         if (penalty_dist != 0.0f && uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
838           dist_test_sq = len_v2v2(co, luv->uv) + penalty_dist;
839           dist_test_sq = square_f(dist_test_sq);
840         }
841         else {
842           dist_test_sq = len_squared_v2v2(co, luv->uv);
843         }
844 
845         if (dist_test_sq <= hit.dist_sq) {
846           if (dist_test_sq == hit.dist_sq) {
847             if (!uv_nearest_between(l, co, cd_loop_uv_offset)) {
848               continue;
849             }
850           }
851 
852           hit.dist_sq = dist_test_sq;
853 
854           hit.l = l;
855           hit.efa = efa;
856           found = true;
857         }
858       }
859     }
860   }
861 
862   if (found) {
863     *hit_final = hit;
864   }
865 
866   return found;
867 }
868 
uv_find_nearest_vert_multi(Scene * scene,Object ** objects,const uint objects_len,float const co[2],const float penalty_dist,UvNearestHit * hit_final)869 bool uv_find_nearest_vert_multi(Scene *scene,
870                                 Object **objects,
871                                 const uint objects_len,
872                                 float const co[2],
873                                 const float penalty_dist,
874                                 UvNearestHit *hit_final)
875 {
876   bool found = false;
877   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
878     Object *obedit = objects[ob_index];
879     if (uv_find_nearest_vert(scene, obedit, co, penalty_dist, hit_final)) {
880       hit_final->ob = obedit;
881       found = true;
882     }
883   }
884   return found;
885 }
886 
ED_uvedit_nearest_uv(const Scene * scene,Object * obedit,const float co[2],float * dist_sq,float r_uv[2])887 bool ED_uvedit_nearest_uv(
888     const Scene *scene, Object *obedit, const float co[2], float *dist_sq, float r_uv[2])
889 {
890   BMEditMesh *em = BKE_editmesh_from_object(obedit);
891   BMIter iter;
892   BMFace *efa;
893   const float *uv_best = NULL;
894   float dist_best = *dist_sq;
895   const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
896   BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
897     if (!uvedit_face_visible_test(scene, efa)) {
898       continue;
899     }
900     BMLoop *l_iter, *l_first;
901     l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
902     do {
903       const float *uv = ((const MLoopUV *)BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset))->uv;
904       const float dist_test = len_squared_v2v2(co, uv);
905       if (dist_best > dist_test) {
906         dist_best = dist_test;
907         uv_best = uv;
908       }
909     } while ((l_iter = l_iter->next) != l_first);
910   }
911 
912   if (uv_best != NULL) {
913     copy_v2_v2(r_uv, uv_best);
914     *dist_sq = dist_best;
915     return true;
916   }
917   return false;
918 }
919 
ED_uvedit_nearest_uv_multi(const Scene * scene,Object ** objects,const uint objects_len,const float co[2],float * dist_sq,float r_uv[2])920 bool ED_uvedit_nearest_uv_multi(const Scene *scene,
921                                 Object **objects,
922                                 const uint objects_len,
923                                 const float co[2],
924                                 float *dist_sq,
925                                 float r_uv[2])
926 {
927   bool found = false;
928   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
929     Object *obedit = objects[ob_index];
930     if (ED_uvedit_nearest_uv(scene, obedit, co, dist_sq, r_uv)) {
931       found = true;
932     }
933   }
934   return found;
935 }
936 
937 /** \} */
938 
939 /* -------------------------------------------------------------------- */
940 /** \name Find Nearest to Element
941  *
942  * These functions are quite specialized, useful when sync select is enabled
943  * and we want to pick an active UV vertex/edge from the active element which may
944  * have multiple UV's split out.
945  * \{ */
946 
uv_find_nearest_loop_from_vert(struct Scene * scene,struct Object * obedit,struct BMVert * v,const float co[2])947 BMLoop *uv_find_nearest_loop_from_vert(struct Scene *scene,
948                                        struct Object *obedit,
949                                        struct BMVert *v,
950                                        const float co[2])
951 {
952   BMEditMesh *em = BKE_editmesh_from_object(obedit);
953   const uint cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
954 
955   BMIter liter;
956   BMLoop *l;
957   BMLoop *l_found = NULL;
958   float dist_best_sq = FLT_MAX;
959 
960   BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
961     if (!uvedit_face_visible_test(scene, l->f)) {
962       continue;
963     }
964 
965     const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
966     const float dist_test_sq = len_squared_v2v2(co, luv->uv);
967     if (dist_test_sq < dist_best_sq) {
968       dist_best_sq = dist_test_sq;
969       l_found = l;
970     }
971   }
972   return l_found;
973 }
974 
uv_find_nearest_loop_from_edge(struct Scene * scene,struct Object * obedit,struct BMEdge * e,const float co[2])975 BMLoop *uv_find_nearest_loop_from_edge(struct Scene *scene,
976                                        struct Object *obedit,
977                                        struct BMEdge *e,
978                                        const float co[2])
979 {
980   BMEditMesh *em = BKE_editmesh_from_object(obedit);
981   const uint cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
982 
983   BMIter eiter;
984   BMLoop *l;
985   BMLoop *l_found = NULL;
986   float dist_best_sq = FLT_MAX;
987 
988   BM_ITER_ELEM (l, &eiter, e, BM_LOOPS_OF_EDGE) {
989     if (!uvedit_face_visible_test(scene, l->f)) {
990       continue;
991     }
992     const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
993     const MLoopUV *luv_next = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset);
994     const float dist_test_sq = dist_squared_to_line_segment_v2(co, luv->uv, luv_next->uv);
995     if (dist_test_sq < dist_best_sq) {
996       dist_best_sq = dist_test_sq;
997       l_found = l;
998     }
999   }
1000   return l_found;
1001 }
1002 
1003 /** \} */
1004 
1005 /* -------------------------------------------------------------------- */
1006 /** \name Edge Loop Select
1007  * \{ */
1008 
1009 /** Mode for selecting edge loops at boundaries. */
1010 enum eUVEdgeLoopBoundaryMode {
1011   /** Delimit at face corners (don't walk over multiple edges in the same face). */
1012   UV_EDGE_LOOP_BOUNDARY_LOOP = 1,
1013   /** Don't delimit, walk over the all connected boundary loops. */
1014   UV_EDGE_LOOP_BOUNDARY_ALL = 2,
1015 };
1016 
bm_select_edgeloop_double_side_next(const Scene * scene,BMLoop * l_step,BMVert * v_from,const int cd_loop_uv_offset)1017 static BMLoop *bm_select_edgeloop_double_side_next(const Scene *scene,
1018                                                    BMLoop *l_step,
1019                                                    BMVert *v_from,
1020                                                    const int cd_loop_uv_offset)
1021 {
1022   if (l_step->f->len == 4) {
1023     BMVert *v_from_next = BM_edge_other_vert(l_step->e, v_from);
1024     BMLoop *l_step_over = (v_from == l_step->v) ? l_step->next : l_step->prev;
1025     l_step_over = uvedit_loop_find_other_radial_loop_with_visible_face(
1026         scene, l_step_over, cd_loop_uv_offset);
1027     if (l_step_over) {
1028       return (l_step_over->v == v_from_next) ? l_step_over->prev : l_step_over->next;
1029     }
1030   }
1031   return NULL;
1032 }
1033 
bm_select_edgeloop_single_side_next(const Scene * scene,BMLoop * l_step,BMVert * v_from,const int cd_loop_uv_offset)1034 static BMLoop *bm_select_edgeloop_single_side_next(const Scene *scene,
1035                                                    BMLoop *l_step,
1036                                                    BMVert *v_from,
1037                                                    const int cd_loop_uv_offset)
1038 {
1039   BMVert *v_from_next = BM_edge_other_vert(l_step->e, v_from);
1040   return uvedit_loop_find_other_boundary_loop_with_visible_face(
1041       scene, l_step, v_from_next, cd_loop_uv_offset);
1042 }
1043 
1044 /* TODO(campbell): support this in the BMesh API, as we have for clearing other types. */
bm_loop_tags_clear(BMesh * bm)1045 static void bm_loop_tags_clear(BMesh *bm)
1046 {
1047   BMIter iter;
1048   BMFace *f;
1049   BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
1050     BMIter liter;
1051     BMLoop *l_iter;
1052     BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
1053       BM_elem_flag_disable(l_iter, BM_ELEM_TAG);
1054     }
1055   }
1056 }
1057 
1058 /**
1059  * Tag all loops which should be selected, the caller must select.
1060  */
uv_select_edgeloop_double_side_tag(const Scene * scene,BMEditMesh * em,BMLoop * l_init_pair[2],const int cd_loop_uv_offset)1061 static void uv_select_edgeloop_double_side_tag(const Scene *scene,
1062                                                BMEditMesh *em,
1063                                                BMLoop *l_init_pair[2],
1064                                                const int cd_loop_uv_offset)
1065 {
1066   bm_loop_tags_clear(em->bm);
1067 
1068   for (int side = 0; side < 2; side++) {
1069     BMLoop *l_step_pair[2] = {l_init_pair[0], l_init_pair[1]};
1070     BMVert *v_from = side ? l_step_pair[0]->e->v1 : l_step_pair[0]->e->v2;
1071     /* Disable since we start from the same edge. */
1072     BM_elem_flag_disable(l_step_pair[0], BM_ELEM_TAG);
1073     BM_elem_flag_disable(l_step_pair[1], BM_ELEM_TAG);
1074     while ((l_step_pair[0] != NULL) && (l_step_pair[1] != NULL)) {
1075       if (!uvedit_face_visible_test(scene, l_step_pair[0]->f) ||
1076           !uvedit_face_visible_test(scene, l_step_pair[1]->f) ||
1077           /* Check loops have not diverged. */
1078           (uvedit_loop_find_other_radial_loop_with_visible_face(
1079                scene, l_step_pair[0], cd_loop_uv_offset) != l_step_pair[1])) {
1080         break;
1081       }
1082 
1083       BLI_assert(l_step_pair[0]->e == l_step_pair[1]->e);
1084 
1085       BM_elem_flag_enable(l_step_pair[0], BM_ELEM_TAG);
1086       BM_elem_flag_enable(l_step_pair[1], BM_ELEM_TAG);
1087 
1088       BMVert *v_from_next = BM_edge_other_vert(l_step_pair[0]->e, v_from);
1089       /* Walk over both sides, ensure they keep on the same edge. */
1090       for (int i = 0; i < ARRAY_SIZE(l_step_pair); i++) {
1091         l_step_pair[i] = bm_select_edgeloop_double_side_next(
1092             scene, l_step_pair[i], v_from, cd_loop_uv_offset);
1093       }
1094 
1095       if ((l_step_pair[0] && BM_elem_flag_test(l_step_pair[0], BM_ELEM_TAG)) ||
1096           (l_step_pair[1] && BM_elem_flag_test(l_step_pair[1], BM_ELEM_TAG))) {
1097         break;
1098       }
1099       v_from = v_from_next;
1100     }
1101   }
1102 }
1103 
1104 /**
1105  * Tag all loops which should be selected, the caller must select.
1106  *
1107  * \param r_count_by_select: Count the number of unselected and selected loops,
1108  * this is needed to implement cycling between #eUVEdgeLoopBoundaryMode.
1109  */
uv_select_edgeloop_single_side_tag(const Scene * scene,BMEditMesh * em,BMLoop * l_init,const int cd_loop_uv_offset,enum eUVEdgeLoopBoundaryMode boundary_mode,int r_count_by_select[2])1110 static void uv_select_edgeloop_single_side_tag(const Scene *scene,
1111                                                BMEditMesh *em,
1112                                                BMLoop *l_init,
1113                                                const int cd_loop_uv_offset,
1114                                                enum eUVEdgeLoopBoundaryMode boundary_mode,
1115                                                int r_count_by_select[2])
1116 {
1117   if (r_count_by_select) {
1118     r_count_by_select[0] = r_count_by_select[1] = 0;
1119   }
1120 
1121   bm_loop_tags_clear(em->bm);
1122 
1123   for (int side = 0; side < 2; side++) {
1124     BMLoop *l_step = l_init;
1125     BMVert *v_from = side ? l_step->e->v1 : l_step->e->v2;
1126     /* Disable since we start from the same edge. */
1127     BM_elem_flag_disable(l_step, BM_ELEM_TAG);
1128     while (l_step != NULL) {
1129 
1130       if (!uvedit_face_visible_test(scene, l_step->f) ||
1131           /* Check the boundary is still a  boundary. */
1132           (uvedit_loop_find_other_radial_loop_with_visible_face(
1133                scene, l_step, cd_loop_uv_offset) != NULL)) {
1134         break;
1135       }
1136 
1137       if (r_count_by_select != NULL) {
1138         r_count_by_select[uvedit_edge_select_test(scene, l_step, cd_loop_uv_offset)] += 1;
1139         /* Early exit when mixed could be optional if needed. */
1140         if (r_count_by_select[0] && r_count_by_select[1]) {
1141           r_count_by_select[0] = r_count_by_select[1] = -1;
1142           break;
1143         }
1144       }
1145 
1146       BM_elem_flag_enable(l_step, BM_ELEM_TAG);
1147 
1148       BMVert *v_from_next = BM_edge_other_vert(l_step->e, v_from);
1149       BMFace *f_step_prev = l_step->f;
1150 
1151       l_step = bm_select_edgeloop_single_side_next(scene, l_step, v_from, cd_loop_uv_offset);
1152 
1153       if (l_step && BM_elem_flag_test(l_step, BM_ELEM_TAG)) {
1154         break;
1155       }
1156       if (boundary_mode == UV_EDGE_LOOP_BOUNDARY_LOOP) {
1157         /* Don't allow walking over the the face. */
1158         if (f_step_prev == l_step->f) {
1159           break;
1160         }
1161       }
1162       v_from = v_from_next;
1163     }
1164   }
1165 }
1166 
uv_select_edgeloop(SpaceImage * sima,Scene * scene,Object * obedit,UvNearestHit * hit,const bool extend)1167 static int uv_select_edgeloop(
1168     SpaceImage *sima, Scene *scene, Object *obedit, UvNearestHit *hit, const bool extend)
1169 {
1170   BMEditMesh *em = BKE_editmesh_from_object(obedit);
1171   bool select;
1172 
1173   const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1174 
1175   if (extend) {
1176     select = !(uvedit_uv_select_test(scene, hit->l, cd_loop_uv_offset));
1177   }
1178   else {
1179     select = true;
1180   }
1181 
1182   BMLoop *l_init_pair[2] = {
1183       hit->l,
1184       uvedit_loop_find_other_radial_loop_with_visible_face(scene, hit->l, cd_loop_uv_offset),
1185   };
1186 
1187   /* When selecting boundaries, support cycling between selection modes. */
1188   enum eUVEdgeLoopBoundaryMode boundary_mode = UV_EDGE_LOOP_BOUNDARY_LOOP;
1189 
1190   /* Tag all loops that are part of the edge loop (select after).
1191    * This is done so we can */
1192   if (l_init_pair[1] == NULL) {
1193     int count_by_select[2];
1194     /* If the loops selected toggle the boundaries. */
1195     uv_select_edgeloop_single_side_tag(
1196         scene, em, l_init_pair[0], cd_loop_uv_offset, boundary_mode, count_by_select);
1197     if (count_by_select[!select] == 0) {
1198       boundary_mode = UV_EDGE_LOOP_BOUNDARY_ALL;
1199 
1200       /* If the boundary is selected, toggle back to the loop. */
1201       uv_select_edgeloop_single_side_tag(
1202           scene, em, l_init_pair[0], cd_loop_uv_offset, boundary_mode, count_by_select);
1203       if (count_by_select[!select] == 0) {
1204         boundary_mode = UV_EDGE_LOOP_BOUNDARY_LOOP;
1205       }
1206     }
1207   }
1208 
1209   if (l_init_pair[1] == NULL) {
1210     uv_select_edgeloop_single_side_tag(
1211         scene, em, l_init_pair[0], cd_loop_uv_offset, boundary_mode, NULL);
1212   }
1213   else {
1214     uv_select_edgeloop_double_side_tag(scene, em, l_init_pair, cd_loop_uv_offset);
1215   }
1216 
1217   /* Apply the selection. */
1218   if (!extend) {
1219     uv_select_all_perform(scene, obedit, SEL_DESELECT);
1220   }
1221 
1222   /* Select all tagged loops. */
1223   {
1224     BMIter iter;
1225     BMFace *f;
1226     BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
1227       BMIter liter;
1228       BMLoop *l_iter;
1229       BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
1230         if (BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
1231           uvedit_edge_select_set_with_sticky(
1232               sima, scene, em, l_iter, select, false, cd_loop_uv_offset);
1233         }
1234       }
1235     }
1236   }
1237 
1238   return (select) ? 1 : -1;
1239 }
1240 
1241 /** \} */
1242 
1243 /* -------------------------------------------------------------------- */
1244 /** \name Edge Ring Select
1245  * \{ */
1246 
uv_select_edgering(const SpaceImage * sima,Scene * scene,Object * obedit,UvNearestHit * hit,const bool extend)1247 static int uv_select_edgering(
1248     const SpaceImage *sima, Scene *scene, Object *obedit, UvNearestHit *hit, const bool extend)
1249 {
1250   const ToolSettings *ts = scene->toolsettings;
1251   BMEditMesh *em = BKE_editmesh_from_object(obedit);
1252   const bool use_face_select = (ts->uv_flag & UV_SYNC_SELECTION) ?
1253                                    (ts->selectmode & SCE_SELECT_FACE) :
1254                                    (ts->uv_selectmode & UV_SELECT_FACE);
1255   bool select;
1256 
1257   const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1258 
1259   if (!extend) {
1260     uv_select_all_perform(scene, obedit, SEL_DESELECT);
1261   }
1262 
1263   BM_mesh_elem_hflag_disable_all(em->bm, BM_EDGE, BM_ELEM_TAG, false);
1264 
1265   if (extend) {
1266     select = !(uvedit_uv_select_test(scene, hit->l, cd_loop_uv_offset));
1267   }
1268   else {
1269     select = true;
1270   }
1271 
1272   BMLoop *l_pair[2] = {
1273       hit->l,
1274       uvedit_loop_find_other_radial_loop_with_visible_face(scene, hit->l, cd_loop_uv_offset),
1275   };
1276 
1277   for (int side = 0; side < 2; side++) {
1278     BMLoop *l_step = l_pair[side];
1279     /* Disable since we start from the same edge. */
1280     BM_elem_flag_disable(hit->l->e, BM_ELEM_TAG);
1281     while (l_step) {
1282       if (!uvedit_face_visible_test(scene, l_step->f)) {
1283         break;
1284       }
1285 
1286       if (use_face_select) {
1287         uvedit_face_select_set_with_sticky(
1288             sima, scene, em, l_step->f, select, false, cd_loop_uv_offset);
1289       }
1290       else {
1291         uvedit_edge_select_set_with_sticky(
1292             sima, scene, em, l_step, select, false, cd_loop_uv_offset);
1293       }
1294 
1295       BM_elem_flag_enable(l_step->e, BM_ELEM_TAG);
1296       if (l_step->f->len == 4) {
1297         BMLoop *l_step_opposite = l_step->next->next;
1298         l_step = uvedit_loop_find_other_radial_loop_with_visible_face(
1299             scene, l_step_opposite, cd_loop_uv_offset);
1300         if (l_step == NULL) {
1301           /* Ensure we touch the opposite edge if we cant walk over it. */
1302           l_step = l_step_opposite;
1303         }
1304       }
1305       else {
1306         l_step = NULL;
1307       }
1308 
1309       if (l_step && BM_elem_flag_test(l_step->e, BM_ELEM_TAG)) {
1310         break;
1311       }
1312     }
1313   }
1314 
1315   return (select) ? 1 : -1;
1316 }
1317 
1318 /** \} */
1319 
1320 /* -------------------------------------------------------------------- */
1321 /** \name Select Linked
1322  * \{ */
1323 
uv_select_linked_multi(Scene * scene,Object ** objects,const uint objects_len,UvNearestHit * hit_final,const bool extend,bool deselect,const bool toggle,const bool select_faces)1324 static void uv_select_linked_multi(Scene *scene,
1325                                    Object **objects,
1326                                    const uint objects_len,
1327                                    UvNearestHit *hit_final,
1328                                    const bool extend,
1329                                    bool deselect,
1330                                    const bool toggle,
1331                                    const bool select_faces)
1332 {
1333   const bool uv_sync_select = (scene->toolsettings->uv_flag & UV_SYNC_SELECTION);
1334 
1335   /* loop over objects, or just use hit_final->ob */
1336   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1337     if (hit_final && ob_index != 0) {
1338       break;
1339     }
1340     Object *obedit = hit_final ? hit_final->ob : objects[ob_index];
1341 
1342     BMFace *efa;
1343     BMLoop *l;
1344     BMIter iter, liter;
1345     UvVertMap *vmap;
1346     UvMapVert *vlist, *iterv, *startv;
1347     int i, stacksize = 0, *stack;
1348     uint a;
1349     char *flag;
1350 
1351     BMEditMesh *em = BKE_editmesh_from_object(obedit);
1352     const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1353 
1354     BM_mesh_elem_table_ensure(em->bm, BM_FACE); /* we can use this too */
1355 
1356     /* Note, we had 'use winding' so we don't consider overlapping islands as connected, see T44320
1357      * this made *every* projection split the island into front/back islands.
1358      * Keep 'use_winding' to false, see: T50970.
1359      *
1360      * Better solve this by having a delimit option for select-linked operator,
1361      * keeping island-select working as is. */
1362     vmap = BM_uv_vert_map_create(em->bm, !uv_sync_select, false);
1363 
1364     if (vmap == NULL) {
1365       continue;
1366     }
1367 
1368     stack = MEM_mallocN(sizeof(*stack) * (em->bm->totface + 1), "UvLinkStack");
1369     flag = MEM_callocN(sizeof(*flag) * em->bm->totface, "UvLinkFlag");
1370 
1371     if (hit_final == NULL) {
1372       /* Use existing selection */
1373       BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, a) {
1374         if (uvedit_face_visible_test(scene, efa)) {
1375           if (select_faces) {
1376             if (BM_elem_flag_test(efa, BM_ELEM_SELECT)) {
1377               stack[stacksize] = a;
1378               stacksize++;
1379               flag[a] = 1;
1380             }
1381           }
1382           else {
1383             BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1384               if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1385                 bool add_to_stack = true;
1386                 if (uv_sync_select && !select_faces) {
1387                   /* Special case, vertex/edge & sync select being enabled.
1388                    *
1389                    * Without this, a second linked select will 'grow' each time as each new
1390                    * selection reaches the boundaries of islands that share vertices but not UV's.
1391                    *
1392                    * Rules applied here:
1393                    * - This loops face isn't selected.
1394                    * - The only other fully selected face is connected or,
1395                    * - There are no connected fully selected faces UV-connected to this loop.
1396                    */
1397                   if (uvedit_face_select_test(scene, l->f, cd_loop_uv_offset)) {
1398                     /* pass */
1399                   }
1400                   else {
1401                     BMIter liter_other;
1402                     BMLoop *l_other;
1403                     BM_ITER_ELEM (l_other, &liter_other, l->v, BM_LOOPS_OF_VERT) {
1404                       if ((l != l_other) &&
1405                           !BM_loop_uv_share_vert_check(l, l_other, cd_loop_uv_offset) &&
1406                           uvedit_face_select_test(scene, l_other->f, cd_loop_uv_offset)) {
1407                         add_to_stack = false;
1408                         break;
1409                       }
1410                     }
1411                   }
1412                 }
1413 
1414                 if (add_to_stack) {
1415                   stack[stacksize] = a;
1416                   stacksize++;
1417                   flag[a] = 1;
1418                   break;
1419                 }
1420               }
1421             }
1422           }
1423         }
1424       }
1425     }
1426     else {
1427       BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, a) {
1428         if (efa == hit_final->efa) {
1429           stack[stacksize] = a;
1430           stacksize++;
1431           flag[a] = 1;
1432           break;
1433         }
1434       }
1435     }
1436 
1437     while (stacksize > 0) {
1438 
1439       stacksize--;
1440       a = stack[stacksize];
1441 
1442       efa = BM_face_at_index(em->bm, a);
1443 
1444       BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
1445 
1446         /* make_uv_vert_map_EM sets verts tmp.l to the indices */
1447         vlist = BM_uv_vert_map_at_index(vmap, BM_elem_index_get(l->v));
1448 
1449         startv = vlist;
1450 
1451         for (iterv = vlist; iterv; iterv = iterv->next) {
1452           if (iterv->separate) {
1453             startv = iterv;
1454           }
1455           if (iterv->poly_index == a) {
1456             break;
1457           }
1458         }
1459 
1460         for (iterv = startv; iterv; iterv = iterv->next) {
1461           if ((startv != iterv) && (iterv->separate)) {
1462             break;
1463           }
1464           if (!flag[iterv->poly_index]) {
1465             flag[iterv->poly_index] = 1;
1466             stack[stacksize] = iterv->poly_index;
1467             stacksize++;
1468           }
1469         }
1470       }
1471     }
1472 
1473     /* Toggling - if any of the linked vertices is selected (and visible), we deselect. */
1474     if ((toggle == true) && (extend == false) && (deselect == false)) {
1475       BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, a) {
1476         bool found_selected = false;
1477         if (!flag[a]) {
1478           continue;
1479         }
1480 
1481         if (select_faces) {
1482           if (BM_elem_flag_test(efa, BM_ELEM_SELECT) && !BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
1483             found_selected = true;
1484           }
1485         }
1486         else {
1487           BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1488             if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1489               found_selected = true;
1490               break;
1491             }
1492           }
1493 
1494           if (found_selected) {
1495             deselect = true;
1496             break;
1497           }
1498         }
1499       }
1500     }
1501 
1502 #define SET_SELECTION(value) \
1503   if (select_faces) { \
1504     BM_face_select_set(em->bm, efa, value); \
1505   } \
1506   else { \
1507     uvedit_face_select_set(scene, em, efa, value, false, cd_loop_uv_offset); \
1508   } \
1509   (void)0
1510 
1511     BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, a) {
1512       if (!flag[a]) {
1513         if (!extend && !deselect && !toggle) {
1514           SET_SELECTION(false);
1515         }
1516         continue;
1517       }
1518 
1519       if (!deselect) {
1520         SET_SELECTION(true);
1521       }
1522       else {
1523         SET_SELECTION(false);
1524       }
1525     }
1526 
1527 #undef SET_SELECTION
1528 
1529     MEM_freeN(stack);
1530     MEM_freeN(flag);
1531     BM_uv_vert_map_free(vmap);
1532 
1533     if (uv_sync_select) {
1534       if (deselect) {
1535         EDBM_deselect_flush(em);
1536       }
1537       else {
1538         if (!select_faces) {
1539           EDBM_selectmode_flush(em);
1540         }
1541       }
1542     }
1543   }
1544 }
1545 
1546 /**
1547  * \warning This returns first selected UV,
1548  * not ideal in many cases since there could be multiple.
1549  */
uvedit_first_selected_uv_from_vertex(Scene * scene,BMVert * eve,const int cd_loop_uv_offset)1550 const float *uvedit_first_selected_uv_from_vertex(Scene *scene,
1551                                                   BMVert *eve,
1552                                                   const int cd_loop_uv_offset)
1553 {
1554   BMIter liter;
1555   BMLoop *l;
1556 
1557   BM_ITER_ELEM (l, &liter, eve, BM_LOOPS_OF_VERT) {
1558     if (!uvedit_face_visible_test(scene, l->f)) {
1559       continue;
1560     }
1561 
1562     if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
1563       MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1564       return luv->uv;
1565     }
1566   }
1567 
1568   return NULL;
1569 }
1570 
1571 /** \} */
1572 
1573 /* -------------------------------------------------------------------- */
1574 /** \name Select More/Less Operator
1575  * \{ */
1576 
uv_select_more_less(bContext * C,const bool select)1577 static int uv_select_more_less(bContext *C, const bool select)
1578 {
1579   Scene *scene = CTX_data_scene(C);
1580   ViewLayer *view_layer = CTX_data_view_layer(C);
1581   SpaceImage *sima = CTX_wm_space_image(C);
1582 
1583   BMFace *efa;
1584   BMLoop *l;
1585   BMIter iter, liter;
1586   const ToolSettings *ts = scene->toolsettings;
1587 
1588   uint objects_len = 0;
1589   Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
1590       view_layer, ((View3D *)NULL), &objects_len);
1591 
1592   const bool is_uv_face_selectmode = (ts->uv_selectmode == UV_SELECT_FACE);
1593 
1594   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1595     Object *obedit = objects[ob_index];
1596     BMEditMesh *em = BKE_editmesh_from_object(obedit);
1597 
1598     bool changed = false;
1599 
1600     const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1601 
1602     if (ts->uv_flag & UV_SYNC_SELECTION) {
1603       if (select) {
1604         EDBM_select_more(em, true);
1605       }
1606       else {
1607         EDBM_select_less(em, true);
1608       }
1609 
1610       DEG_id_tag_update(obedit->data, ID_RECALC_SELECT);
1611       WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
1612       continue;
1613     }
1614 
1615     if (is_uv_face_selectmode) {
1616 
1617       /* clear tags */
1618       BM_mesh_elem_hflag_disable_all(em->bm, BM_FACE, BM_ELEM_TAG, false);
1619 
1620       /* mark loops to be selected */
1621       BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1622         if (uvedit_face_visible_test(scene, efa)) {
1623 
1624 #define IS_SEL 1
1625 #define IS_UNSEL 2
1626 
1627           int sel_state = 0;
1628 
1629           BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1630             MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1631             if (luv->flag & MLOOPUV_VERTSEL) {
1632               sel_state |= IS_SEL;
1633             }
1634             else {
1635               sel_state |= IS_UNSEL;
1636             }
1637 
1638             /* if we have a mixed selection, tag to grow it */
1639             if (sel_state == (IS_SEL | IS_UNSEL)) {
1640               BM_elem_flag_enable(efa, BM_ELEM_TAG);
1641               changed = true;
1642               break;
1643             }
1644           }
1645 
1646 #undef IS_SEL
1647 #undef IS_UNSEL
1648         }
1649       }
1650     }
1651     else {
1652 
1653       /* clear tags */
1654       BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1655         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1656           BM_elem_flag_disable(l, BM_ELEM_TAG);
1657         }
1658       }
1659 
1660       /* mark loops to be selected */
1661       BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1662         if (uvedit_face_visible_test(scene, efa)) {
1663           BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1664 
1665             MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1666 
1667             if (((luv->flag & MLOOPUV_VERTSEL) != 0) == select) {
1668               BM_elem_flag_enable(l->next, BM_ELEM_TAG);
1669               BM_elem_flag_enable(l->prev, BM_ELEM_TAG);
1670               changed = true;
1671             }
1672           }
1673         }
1674       }
1675     }
1676 
1677     if (changed) {
1678       if (is_uv_face_selectmode) {
1679         /* Select tagged faces. */
1680         uv_select_flush_from_tag_face(sima, scene, obedit, select);
1681       }
1682       else {
1683         /* Select tagged loops. */
1684         uv_select_flush_from_tag_loop(sima, scene, obedit, select);
1685       }
1686       DEG_id_tag_update(obedit->data, ID_RECALC_SELECT);
1687       WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
1688     }
1689   }
1690   MEM_freeN(objects);
1691 
1692   return OPERATOR_FINISHED;
1693 }
1694 
uv_select_more_exec(bContext * C,wmOperator * UNUSED (op))1695 static int uv_select_more_exec(bContext *C, wmOperator *UNUSED(op))
1696 {
1697   return uv_select_more_less(C, true);
1698 }
1699 
UV_OT_select_more(wmOperatorType * ot)1700 void UV_OT_select_more(wmOperatorType *ot)
1701 {
1702   /* identifiers */
1703   ot->name = "Select More";
1704   ot->description = "Select more UV vertices connected to initial selection";
1705   ot->idname = "UV_OT_select_more";
1706   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1707 
1708   /* api callbacks */
1709   ot->exec = uv_select_more_exec;
1710   ot->poll = ED_operator_uvedit_space_image;
1711 }
1712 
uv_select_less_exec(bContext * C,wmOperator * UNUSED (op))1713 static int uv_select_less_exec(bContext *C, wmOperator *UNUSED(op))
1714 {
1715   return uv_select_more_less(C, false);
1716 }
1717 
UV_OT_select_less(wmOperatorType * ot)1718 void UV_OT_select_less(wmOperatorType *ot)
1719 {
1720   /* identifiers */
1721   ot->name = "Select Less";
1722   ot->description = "Deselect UV vertices at the boundary of each selection region";
1723   ot->idname = "UV_OT_select_less";
1724   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1725 
1726   /* api callbacks */
1727   ot->exec = uv_select_less_exec;
1728   ot->poll = ED_operator_uvedit_space_image;
1729 }
1730 
1731 /** \} */
1732 
1733 /* -------------------------------------------------------------------- */
1734 /** \name (De)Select All Operator
1735  * \{ */
1736 
uvedit_select_is_any_selected(Scene * scene,Object * obedit)1737 bool uvedit_select_is_any_selected(Scene *scene, Object *obedit)
1738 {
1739   const ToolSettings *ts = scene->toolsettings;
1740   BMEditMesh *em = BKE_editmesh_from_object(obedit);
1741   BMFace *efa;
1742   BMLoop *l;
1743   BMIter iter, liter;
1744   MLoopUV *luv;
1745 
1746   if (ts->uv_flag & UV_SYNC_SELECTION) {
1747     return (em->bm->totvertsel || em->bm->totedgesel || em->bm->totfacesel);
1748   }
1749 
1750   const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1751   BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1752     if (!uvedit_face_visible_test(scene, efa)) {
1753       continue;
1754     }
1755     BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1756       luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1757       if (luv->flag & MLOOPUV_VERTSEL) {
1758         return true;
1759       }
1760     }
1761   }
1762   return false;
1763 }
1764 
uvedit_select_is_any_selected_multi(Scene * scene,Object ** objects,const uint objects_len)1765 bool uvedit_select_is_any_selected_multi(Scene *scene, Object **objects, const uint objects_len)
1766 {
1767   bool found = false;
1768   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1769     Object *obedit = objects[ob_index];
1770     if (uvedit_select_is_any_selected(scene, obedit)) {
1771       found = true;
1772       break;
1773     }
1774   }
1775   return found;
1776 }
1777 
uv_select_all_perform(Scene * scene,Object * obedit,int action)1778 static void uv_select_all_perform(Scene *scene, Object *obedit, int action)
1779 {
1780   const ToolSettings *ts = scene->toolsettings;
1781   BMEditMesh *em = BKE_editmesh_from_object(obedit);
1782   BMFace *efa;
1783   BMLoop *l;
1784   BMIter iter, liter;
1785   MLoopUV *luv;
1786 
1787   const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
1788 
1789   if (action == SEL_TOGGLE) {
1790     action = uvedit_select_is_any_selected(scene, obedit) ? SEL_DESELECT : SEL_SELECT;
1791   }
1792 
1793   if (ts->uv_flag & UV_SYNC_SELECTION) {
1794     switch (action) {
1795       case SEL_TOGGLE:
1796         EDBM_select_toggle_all(em);
1797         break;
1798       case SEL_SELECT:
1799         EDBM_flag_enable_all(em, BM_ELEM_SELECT);
1800         break;
1801       case SEL_DESELECT:
1802         EDBM_flag_disable_all(em, BM_ELEM_SELECT);
1803         break;
1804       case SEL_INVERT:
1805         EDBM_select_swap(em);
1806         EDBM_selectmode_flush(em);
1807         break;
1808     }
1809   }
1810   else {
1811     BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1812       if (!uvedit_face_visible_test(scene, efa)) {
1813         continue;
1814       }
1815 
1816       BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1817         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
1818 
1819         switch (action) {
1820           case SEL_SELECT:
1821             luv->flag |= MLOOPUV_VERTSEL;
1822             break;
1823           case SEL_DESELECT:
1824             luv->flag &= ~MLOOPUV_VERTSEL;
1825             break;
1826           case SEL_INVERT:
1827             luv->flag ^= MLOOPUV_VERTSEL;
1828             break;
1829         }
1830       }
1831     }
1832   }
1833 }
1834 
uv_select_all_perform_multi_ex(Scene * scene,Object ** objects,const uint objects_len,int action,const Object * ob_exclude)1835 static void uv_select_all_perform_multi_ex(
1836     Scene *scene, Object **objects, const uint objects_len, int action, const Object *ob_exclude)
1837 {
1838   if (action == SEL_TOGGLE) {
1839     action = uvedit_select_is_any_selected_multi(scene, objects, objects_len) ? SEL_DESELECT :
1840                                                                                 SEL_SELECT;
1841   }
1842 
1843   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1844     Object *obedit = objects[ob_index];
1845     if (ob_exclude && (obedit == ob_exclude)) {
1846       continue;
1847     }
1848     uv_select_all_perform(scene, obedit, action);
1849   }
1850 }
1851 
uv_select_all_perform_multi(Scene * scene,Object ** objects,const uint objects_len,int action)1852 static void uv_select_all_perform_multi(Scene *scene,
1853                                         Object **objects,
1854                                         const uint objects_len,
1855                                         int action)
1856 {
1857   uv_select_all_perform_multi_ex(scene, objects, objects_len, action, NULL);
1858 }
1859 
uv_select_all_exec(bContext * C,wmOperator * op)1860 static int uv_select_all_exec(bContext *C, wmOperator *op)
1861 {
1862   Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
1863   Scene *scene = CTX_data_scene(C);
1864   const ToolSettings *ts = scene->toolsettings;
1865   ViewLayer *view_layer = CTX_data_view_layer(C);
1866 
1867   int action = RNA_enum_get(op->ptr, "action");
1868 
1869   uint objects_len = 0;
1870   Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
1871       view_layer, ((View3D *)NULL), &objects_len);
1872 
1873   uv_select_all_perform_multi(scene, objects, objects_len, action);
1874 
1875   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1876     Object *obedit = objects[ob_index];
1877     uv_select_tag_update_for_object(depsgraph, ts, obedit);
1878   }
1879 
1880   MEM_freeN(objects);
1881 
1882   return OPERATOR_FINISHED;
1883 }
1884 
UV_OT_select_all(wmOperatorType * ot)1885 void UV_OT_select_all(wmOperatorType *ot)
1886 {
1887   /* identifiers */
1888   ot->name = "(De)select All";
1889   ot->description = "Change selection of all UV vertices";
1890   ot->idname = "UV_OT_select_all";
1891   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1892 
1893   /* api callbacks */
1894   ot->exec = uv_select_all_exec;
1895   ot->poll = ED_operator_uvedit;
1896 
1897   WM_operator_properties_select_all(ot);
1898 }
1899 
1900 /** \} */
1901 
1902 /* -------------------------------------------------------------------- */
1903 /** \name Mouse Select Operator
1904  * \{ */
1905 
uv_mouse_select_multi(bContext * C,Object ** objects,uint objects_len,const float co[2],const bool extend,const bool deselect_all)1906 static int uv_mouse_select_multi(bContext *C,
1907                                  Object **objects,
1908                                  uint objects_len,
1909                                  const float co[2],
1910                                  const bool extend,
1911                                  const bool deselect_all)
1912 {
1913   Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
1914   SpaceImage *sima = CTX_wm_space_image(C);
1915   Scene *scene = CTX_data_scene(C);
1916   const ToolSettings *ts = scene->toolsettings;
1917   UvNearestHit hit = UV_NEAREST_HIT_INIT;
1918   int selectmode, sticky;
1919   bool found_item = false;
1920   /* 0 == don't flush, 1 == sel, -1 == desel;  only use when selection sync is enabled */
1921   int flush = 0;
1922 
1923   const float penalty_dist = uv_select_penalty_default(sima);
1924 
1925   /* retrieve operation mode */
1926   if (ts->uv_flag & UV_SYNC_SELECTION) {
1927     if (ts->selectmode & SCE_SELECT_FACE) {
1928       selectmode = UV_SELECT_FACE;
1929     }
1930     else if (ts->selectmode & SCE_SELECT_EDGE) {
1931       selectmode = UV_SELECT_EDGE;
1932     }
1933     else {
1934       selectmode = UV_SELECT_VERTEX;
1935     }
1936 
1937     sticky = SI_STICKY_DISABLE;
1938   }
1939   else {
1940     selectmode = ts->uv_selectmode;
1941     sticky = (sima) ? sima->sticky : SI_STICKY_DISABLE;
1942   }
1943 
1944   /* find nearest element */
1945   if (selectmode == UV_SELECT_VERTEX) {
1946     /* find vertex */
1947     found_item = uv_find_nearest_vert_multi(scene, objects, objects_len, co, penalty_dist, &hit);
1948     found_item = found_item && (!deselect_all || hit.dist_sq < penalty_dist);
1949 
1950     if (found_item) {
1951       if ((ts->uv_flag & UV_SYNC_SELECTION) == 0) {
1952         BMesh *bm = BKE_editmesh_from_object(hit.ob)->bm;
1953         ED_uvedit_active_vert_loop_set(bm, hit.l);
1954       }
1955     }
1956   }
1957   else if (selectmode == UV_SELECT_EDGE) {
1958     /* find edge */
1959     found_item = uv_find_nearest_edge_multi(scene, objects, objects_len, co, &hit);
1960     found_item = found_item && (!deselect_all || hit.dist_sq < penalty_dist);
1961 
1962     if (found_item) {
1963       if ((ts->uv_flag & UV_SYNC_SELECTION) == 0) {
1964         BMesh *bm = BKE_editmesh_from_object(hit.ob)->bm;
1965         ED_uvedit_active_edge_loop_set(bm, hit.l);
1966       }
1967     }
1968   }
1969   else if (selectmode == UV_SELECT_FACE) {
1970     /* find face */
1971     found_item = uv_find_nearest_face_multi(scene, objects, objects_len, co, &hit);
1972     found_item = found_item && (!deselect_all || hit.dist_sq < penalty_dist);
1973 
1974     if (found_item) {
1975       BMesh *bm = BKE_editmesh_from_object(hit.ob)->bm;
1976       BM_mesh_active_face_set(bm, hit.efa);
1977     }
1978   }
1979   else if (selectmode == UV_SELECT_ISLAND) {
1980     found_item = uv_find_nearest_edge_multi(scene, objects, objects_len, co, &hit);
1981     found_item = found_item && (!deselect_all || hit.dist_sq < penalty_dist);
1982   }
1983 
1984   if (!found_item) {
1985     if (deselect_all) {
1986       uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT);
1987 
1988       for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
1989         Object *obedit = objects[ob_index];
1990         uv_select_tag_update_for_object(depsgraph, ts, obedit);
1991       }
1992 
1993       return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED;
1994     }
1995     return OPERATOR_CANCELLED;
1996   }
1997 
1998   Object *obedit = hit.ob;
1999   BMEditMesh *em = BKE_editmesh_from_object(obedit);
2000   const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
2001 
2002   /* do selection */
2003   if (selectmode == UV_SELECT_ISLAND) {
2004     if (!extend) {
2005       uv_select_all_perform_multi_ex(scene, objects, objects_len, SEL_DESELECT, obedit);
2006     }
2007     /* Current behavior of 'extend'
2008      * is actually toggling, so pass extend flag as 'toggle' here */
2009     uv_select_linked_multi(scene, objects, objects_len, &hit, false, false, extend, false);
2010   }
2011   else if (extend) {
2012     bool select = true;
2013     if (selectmode == UV_SELECT_VERTEX) {
2014       /* (de)select uv vertex */
2015       select = !uvedit_uv_select_test(scene, hit.l, cd_loop_uv_offset);
2016       uvedit_uv_select_set_with_sticky(sima, scene, em, hit.l, select, true, cd_loop_uv_offset);
2017       flush = 1;
2018     }
2019     else if (selectmode == UV_SELECT_EDGE) {
2020       /* (de)select edge */
2021       select = !(uvedit_edge_select_test(scene, hit.l, cd_loop_uv_offset));
2022       uvedit_edge_select_set_with_sticky(sima, scene, em, hit.l, select, true, cd_loop_uv_offset);
2023       flush = 1;
2024     }
2025     else if (selectmode == UV_SELECT_FACE) {
2026       /* (de)select face */
2027       select = !(uvedit_face_select_test(scene, hit.efa, cd_loop_uv_offset));
2028       uvedit_face_select_set_with_sticky(
2029           sima, scene, em, hit.efa, select, true, cd_loop_uv_offset);
2030       flush = -1;
2031     }
2032 
2033     /* de-selecting an edge may deselect a face too - validate */
2034     if (ts->uv_flag & UV_SYNC_SELECTION) {
2035       if (select == false) {
2036         BM_select_history_validate(em->bm);
2037       }
2038     }
2039 
2040     /* (de)select sticky uv nodes */
2041     if (sticky != SI_STICKY_DISABLE) {
2042       flush = select ? 1 : -1;
2043     }
2044   }
2045   else {
2046     const bool select = true;
2047     /* deselect all */
2048     uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT);
2049 
2050     if (selectmode == UV_SELECT_VERTEX) {
2051       /* select vertex */
2052       uvedit_uv_select_set_with_sticky(sima, scene, em, hit.l, select, true, cd_loop_uv_offset);
2053       flush = 1;
2054     }
2055     else if (selectmode == UV_SELECT_EDGE) {
2056       /* select edge */
2057       uvedit_edge_select_set_with_sticky(sima, scene, em, hit.l, select, true, cd_loop_uv_offset);
2058       flush = 1;
2059     }
2060     else if (selectmode == UV_SELECT_FACE) {
2061       /* select face */
2062       uvedit_face_select_set_with_sticky(
2063           sima, scene, em, hit.efa, select, true, cd_loop_uv_offset);
2064       flush = 1;
2065     }
2066   }
2067 
2068   if (ts->uv_flag & UV_SYNC_SELECTION) {
2069     if (flush != 0) {
2070       EDBM_selectmode_flush(em);
2071     }
2072   }
2073 
2074   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
2075     Object *obiter = objects[ob_index];
2076     uv_select_tag_update_for_object(depsgraph, ts, obiter);
2077   }
2078 
2079   return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED;
2080 }
uv_mouse_select(bContext * C,const float co[2],const bool extend,const bool deselect_all)2081 static int uv_mouse_select(bContext *C,
2082                            const float co[2],
2083                            const bool extend,
2084                            const bool deselect_all)
2085 {
2086   ViewLayer *view_layer = CTX_data_view_layer(C);
2087   uint objects_len = 0;
2088   Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
2089       view_layer, ((View3D *)NULL), &objects_len);
2090   int ret = uv_mouse_select_multi(C, objects, objects_len, co, extend, deselect_all);
2091   MEM_freeN(objects);
2092   return ret;
2093 }
2094 
uv_select_exec(bContext * C,wmOperator * op)2095 static int uv_select_exec(bContext *C, wmOperator *op)
2096 {
2097   float co[2];
2098 
2099   RNA_float_get_array(op->ptr, "location", co);
2100   const bool extend = RNA_boolean_get(op->ptr, "extend");
2101   const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all");
2102 
2103   return uv_mouse_select(C, co, extend, deselect_all);
2104 }
2105 
uv_select_invoke(bContext * C,wmOperator * op,const wmEvent * event)2106 static int uv_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2107 {
2108   const ARegion *region = CTX_wm_region(C);
2109   float co[2];
2110 
2111   UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
2112   RNA_float_set_array(op->ptr, "location", co);
2113 
2114   return uv_select_exec(C, op);
2115 }
2116 
UV_OT_select(wmOperatorType * ot)2117 void UV_OT_select(wmOperatorType *ot)
2118 {
2119   /* identifiers */
2120   ot->name = "Select";
2121   ot->description = "Select UV vertices";
2122   ot->idname = "UV_OT_select";
2123   ot->flag = OPTYPE_UNDO;
2124 
2125   /* api callbacks */
2126   ot->exec = uv_select_exec;
2127   ot->invoke = uv_select_invoke;
2128   ot->poll = ED_operator_uvedit; /* requires space image */
2129 
2130   /* properties */
2131   PropertyRNA *prop;
2132   RNA_def_boolean(ot->srna,
2133                   "extend",
2134                   0,
2135                   "Extend",
2136                   "Extend selection rather than clearing the existing selection");
2137   prop = RNA_def_boolean(ot->srna,
2138                          "deselect_all",
2139                          false,
2140                          "Deselect On Nothing",
2141                          "Deselect all when nothing under the cursor");
2142   RNA_def_property_flag(prop, PROP_SKIP_SAVE);
2143 
2144   RNA_def_float_vector(
2145       ot->srna,
2146       "location",
2147       2,
2148       NULL,
2149       -FLT_MAX,
2150       FLT_MAX,
2151       "Location",
2152       "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds",
2153       -100.0f,
2154       100.0f);
2155 }
2156 
2157 /** \} */
2158 
2159 /* -------------------------------------------------------------------- */
2160 /** \name Shared Edge Loop/Ring Select Operator Functions
2161  * \{ */
2162 
2163 enum eUVLoopGenericType {
2164   UV_LOOP_SELECT = 1,
2165   UV_RING_SELECT = 2,
2166 };
2167 
uv_mouse_select_loop_generic_multi(bContext * C,Object ** objects,uint objects_len,const float co[2],const bool extend,enum eUVLoopGenericType loop_type)2168 static int uv_mouse_select_loop_generic_multi(bContext *C,
2169                                               Object **objects,
2170                                               uint objects_len,
2171                                               const float co[2],
2172                                               const bool extend,
2173                                               enum eUVLoopGenericType loop_type)
2174 {
2175   SpaceImage *sima = CTX_wm_space_image(C);
2176   Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
2177   Scene *scene = CTX_data_scene(C);
2178   const ToolSettings *ts = scene->toolsettings;
2179   UvNearestHit hit = UV_NEAREST_HIT_INIT;
2180   bool found_item = false;
2181   /* 0 == don't flush, 1 == sel, -1 == desel;  only use when selection sync is enabled */
2182   int flush = 0;
2183 
2184   /* Find edge. */
2185   found_item = uv_find_nearest_edge_multi(scene, objects, objects_len, co, &hit);
2186   if (!found_item) {
2187     return OPERATOR_CANCELLED;
2188   }
2189 
2190   Object *obedit = hit.ob;
2191   BMEditMesh *em = BKE_editmesh_from_object(obedit);
2192 
2193   /* Do selection. */
2194   if (!extend) {
2195     uv_select_all_perform_multi_ex(scene, objects, objects_len, SEL_DESELECT, obedit);
2196   }
2197 
2198   if (loop_type == UV_LOOP_SELECT) {
2199     flush = uv_select_edgeloop(sima, scene, obedit, &hit, extend);
2200   }
2201   else if (loop_type == UV_RING_SELECT) {
2202     flush = uv_select_edgering(sima, scene, obedit, &hit, extend);
2203   }
2204   else {
2205     BLI_assert(0);
2206   }
2207 
2208   if (ts->uv_flag & UV_SYNC_SELECTION) {
2209     if (flush == 1) {
2210       EDBM_select_flush(em);
2211     }
2212     else if (flush == -1) {
2213       EDBM_deselect_flush(em);
2214     }
2215   }
2216 
2217   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
2218     Object *obiter = objects[ob_index];
2219     uv_select_tag_update_for_object(depsgraph, ts, obiter);
2220   }
2221 
2222   return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED;
2223 }
uv_mouse_select_loop_generic(bContext * C,const float co[2],const bool extend,enum eUVLoopGenericType loop_type)2224 static int uv_mouse_select_loop_generic(bContext *C,
2225                                         const float co[2],
2226                                         const bool extend,
2227                                         enum eUVLoopGenericType loop_type)
2228 {
2229   ViewLayer *view_layer = CTX_data_view_layer(C);
2230   uint objects_len = 0;
2231   Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
2232       view_layer, ((View3D *)NULL), &objects_len);
2233   int ret = uv_mouse_select_loop_generic_multi(C, objects, objects_len, co, extend, loop_type);
2234   MEM_freeN(objects);
2235   return ret;
2236 }
2237 
2238 /** \} */
2239 
2240 /* -------------------------------------------------------------------- */
2241 /** \name Edge Loop Select Operator
2242  * \{ */
2243 
uv_select_loop_exec(bContext * C,wmOperator * op)2244 static int uv_select_loop_exec(bContext *C, wmOperator *op)
2245 {
2246   float co[2];
2247 
2248   RNA_float_get_array(op->ptr, "location", co);
2249   const bool extend = RNA_boolean_get(op->ptr, "extend");
2250 
2251   Scene *scene = CTX_data_scene(C);
2252   enum eUVLoopGenericType type = UV_LOOP_SELECT;
2253   if (ED_uvedit_select_mode_get(scene) == UV_SELECT_FACE) {
2254     /* For now ring-select and face-loop is the same thing,
2255      * if we support real edge selection this will no longer be the case. */
2256     type = UV_RING_SELECT;
2257   }
2258 
2259   return uv_mouse_select_loop_generic(C, co, extend, type);
2260 }
2261 
uv_select_loop_invoke(bContext * C,wmOperator * op,const wmEvent * event)2262 static int uv_select_loop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2263 {
2264   const ARegion *region = CTX_wm_region(C);
2265   float co[2];
2266 
2267   UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
2268   RNA_float_set_array(op->ptr, "location", co);
2269 
2270   return uv_select_loop_exec(C, op);
2271 }
2272 
UV_OT_select_loop(wmOperatorType * ot)2273 void UV_OT_select_loop(wmOperatorType *ot)
2274 {
2275   /* identifiers */
2276   ot->name = "Loop Select";
2277   ot->description = "Select a loop of connected UV vertices";
2278   ot->idname = "UV_OT_select_loop";
2279   ot->flag = OPTYPE_UNDO;
2280 
2281   /* api callbacks */
2282   ot->exec = uv_select_loop_exec;
2283   ot->invoke = uv_select_loop_invoke;
2284   ot->poll = ED_operator_uvedit; /* requires space image */
2285 
2286   /* properties */
2287   RNA_def_boolean(ot->srna,
2288                   "extend",
2289                   0,
2290                   "Extend",
2291                   "Extend selection rather than clearing the existing selection");
2292   RNA_def_float_vector(
2293       ot->srna,
2294       "location",
2295       2,
2296       NULL,
2297       -FLT_MAX,
2298       FLT_MAX,
2299       "Location",
2300       "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds",
2301       -100.0f,
2302       100.0f);
2303 }
2304 
2305 /** \} */
2306 
2307 /* -------------------------------------------------------------------- */
2308 /** \name Edge Ring Select Operator
2309  * \{ */
2310 
uv_select_edge_ring_exec(bContext * C,wmOperator * op)2311 static int uv_select_edge_ring_exec(bContext *C, wmOperator *op)
2312 {
2313   float co[2];
2314   RNA_float_get_array(op->ptr, "location", co);
2315   const bool extend = RNA_boolean_get(op->ptr, "extend");
2316   return uv_mouse_select_loop_generic(C, co, extend, UV_RING_SELECT);
2317 }
2318 
uv_select_edge_ring_invoke(bContext * C,wmOperator * op,const wmEvent * event)2319 static int uv_select_edge_ring_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2320 {
2321   const ARegion *region = CTX_wm_region(C);
2322   float co[2];
2323 
2324   UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
2325   RNA_float_set_array(op->ptr, "location", co);
2326 
2327   return uv_select_edge_ring_exec(C, op);
2328 }
2329 
UV_OT_select_edge_ring(wmOperatorType * ot)2330 void UV_OT_select_edge_ring(wmOperatorType *ot)
2331 {
2332   /* identifiers */
2333   ot->name = "Edge Ring Select";
2334   ot->description = "Select an edge ring of connected UV vertices";
2335   ot->idname = "UV_OT_select_edge_ring";
2336   ot->flag = OPTYPE_UNDO;
2337 
2338   /* api callbacks */
2339   ot->exec = uv_select_edge_ring_exec;
2340   ot->invoke = uv_select_edge_ring_invoke;
2341   ot->poll = ED_operator_uvedit; /* requires space image */
2342 
2343   /* properties */
2344   RNA_def_boolean(ot->srna,
2345                   "extend",
2346                   0,
2347                   "Extend",
2348                   "Extend selection rather than clearing the existing selection");
2349   RNA_def_float_vector(
2350       ot->srna,
2351       "location",
2352       2,
2353       NULL,
2354       -FLT_MAX,
2355       FLT_MAX,
2356       "Location",
2357       "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds",
2358       -100.0f,
2359       100.0f);
2360 }
2361 
2362 /** \} */
2363 
2364 /* -------------------------------------------------------------------- */
2365 /** \name Select Linked Operator
2366  * \{ */
2367 
uv_select_linked_internal(bContext * C,wmOperator * op,const wmEvent * event,bool pick)2368 static int uv_select_linked_internal(bContext *C, wmOperator *op, const wmEvent *event, bool pick)
2369 {
2370   Scene *scene = CTX_data_scene(C);
2371   const ToolSettings *ts = scene->toolsettings;
2372   ViewLayer *view_layer = CTX_data_view_layer(C);
2373   bool extend = true;
2374   bool deselect = false;
2375   bool select_faces = (ts->uv_flag & UV_SYNC_SELECTION) && (ts->selectmode & SCE_SELECT_FACE);
2376 
2377   UvNearestHit hit = UV_NEAREST_HIT_INIT;
2378 
2379   if (pick) {
2380     extend = RNA_boolean_get(op->ptr, "extend");
2381     deselect = RNA_boolean_get(op->ptr, "deselect");
2382   }
2383 
2384   uint objects_len = 0;
2385   Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
2386       view_layer, ((View3D *)NULL), &objects_len);
2387 
2388   if (pick) {
2389     float co[2];
2390 
2391     if (event) {
2392       /* invoke */
2393       const ARegion *region = CTX_wm_region(C);
2394 
2395       UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
2396       RNA_float_set_array(op->ptr, "location", co);
2397     }
2398     else {
2399       /* exec */
2400       RNA_float_get_array(op->ptr, "location", co);
2401     }
2402 
2403     if (!uv_find_nearest_edge_multi(scene, objects, objects_len, co, &hit)) {
2404       MEM_freeN(objects);
2405       return OPERATOR_CANCELLED;
2406     }
2407   }
2408 
2409   if (!extend && !deselect) {
2410     uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT);
2411   }
2412 
2413   uv_select_linked_multi(
2414       scene, objects, objects_len, pick ? &hit : NULL, extend, deselect, false, select_faces);
2415 
2416   /* weak!, but works */
2417   Object **objects_free = objects;
2418   if (pick) {
2419     objects = &hit.ob;
2420     objects_len = 1;
2421   }
2422 
2423   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
2424     Object *obedit = objects[ob_index];
2425     DEG_id_tag_update(obedit->data, ID_RECALC_COPY_ON_WRITE | ID_RECALC_SELECT);
2426     WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2427   }
2428 
2429   MEM_SAFE_FREE(objects_free);
2430 
2431   return OPERATOR_FINISHED;
2432 }
2433 
uv_select_linked_exec(bContext * C,wmOperator * op)2434 static int uv_select_linked_exec(bContext *C, wmOperator *op)
2435 {
2436   return uv_select_linked_internal(C, op, NULL, false);
2437 }
2438 
UV_OT_select_linked(wmOperatorType * ot)2439 void UV_OT_select_linked(wmOperatorType *ot)
2440 {
2441   /* identifiers */
2442   ot->name = "Select Linked";
2443   ot->description = "Select all UV vertices linked to the active UV map";
2444   ot->idname = "UV_OT_select_linked";
2445 
2446   /* api callbacks */
2447   ot->exec = uv_select_linked_exec;
2448   ot->poll = ED_operator_uvedit; /* requires space image */
2449 
2450   /* flags */
2451   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2452 }
2453 
2454 /** \} */
2455 
2456 /* -------------------------------------------------------------------- */
2457 /** \name Select Linked (Cursor Pick) Operator
2458  * \{ */
2459 
uv_select_linked_pick_invoke(bContext * C,wmOperator * op,const wmEvent * event)2460 static int uv_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2461 {
2462   return uv_select_linked_internal(C, op, event, true);
2463 }
2464 
uv_select_linked_pick_exec(bContext * C,wmOperator * op)2465 static int uv_select_linked_pick_exec(bContext *C, wmOperator *op)
2466 {
2467   return uv_select_linked_internal(C, op, NULL, true);
2468 }
2469 
UV_OT_select_linked_pick(wmOperatorType * ot)2470 void UV_OT_select_linked_pick(wmOperatorType *ot)
2471 {
2472   /* identifiers */
2473   ot->name = "Select Linked Pick";
2474   ot->description = "Select all UV vertices linked under the mouse";
2475   ot->idname = "UV_OT_select_linked_pick";
2476 
2477   /* flags */
2478   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2479 
2480   /* api callbacks */
2481   ot->invoke = uv_select_linked_pick_invoke;
2482   ot->exec = uv_select_linked_pick_exec;
2483   ot->poll = ED_operator_uvedit; /* requires space image */
2484 
2485   /* properties */
2486   RNA_def_boolean(ot->srna,
2487                   "extend",
2488                   0,
2489                   "Extend",
2490                   "Extend selection rather than clearing the existing selection");
2491   RNA_def_boolean(ot->srna,
2492                   "deselect",
2493                   0,
2494                   "Deselect",
2495                   "Deselect linked UV vertices rather than selecting them");
2496   RNA_def_float_vector(
2497       ot->srna,
2498       "location",
2499       2,
2500       NULL,
2501       -FLT_MAX,
2502       FLT_MAX,
2503       "Location",
2504       "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds",
2505       -100.0f,
2506       100.0f);
2507 }
2508 
2509 /** \} */
2510 
2511 /* -------------------------------------------------------------------- */
2512 /** \name Select Split Operator
2513  * \{ */
2514 
2515 /**
2516  * \note This is based on similar use case to #MESH_OT_split(), which has a similar effect
2517  * but in this case they are not joined to begin with (only having the behavior of being joined)
2518  * so its best to call this #uv_select_split() instead of just split(), but assigned to the same
2519  * key as #MESH_OT_split - Campbell.
2520  */
uv_select_split_exec(bContext * C,wmOperator * op)2521 static int uv_select_split_exec(bContext *C, wmOperator *op)
2522 {
2523   Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
2524   Scene *scene = CTX_data_scene(C);
2525   ViewLayer *view_layer = CTX_data_view_layer(C);
2526   const ToolSettings *ts = scene->toolsettings;
2527 
2528   BMFace *efa;
2529   BMLoop *l;
2530   BMIter iter, liter;
2531   MLoopUV *luv;
2532 
2533   if (ts->uv_flag & UV_SYNC_SELECTION) {
2534     BKE_report(op->reports, RPT_ERROR, "Cannot split selection when sync selection is enabled");
2535     return OPERATOR_CANCELLED;
2536   }
2537 
2538   bool changed_multi = false;
2539 
2540   uint objects_len = 0;
2541   Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
2542       view_layer, ((View3D *)NULL), &objects_len);
2543 
2544   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
2545     Object *obedit = objects[ob_index];
2546     BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
2547 
2548     bool changed = false;
2549 
2550     const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
2551 
2552     BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
2553       bool is_sel = false;
2554       bool is_unsel = false;
2555 
2556       if (!uvedit_face_visible_test(scene, efa)) {
2557         continue;
2558       }
2559 
2560       /* are we all selected? */
2561       BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2562         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2563 
2564         if (luv->flag & MLOOPUV_VERTSEL) {
2565           is_sel = true;
2566         }
2567         else {
2568           is_unsel = true;
2569         }
2570 
2571         /* we have mixed selection, bail out */
2572         if (is_sel && is_unsel) {
2573           break;
2574         }
2575       }
2576 
2577       if (is_sel && is_unsel) {
2578         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2579           luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2580           luv->flag &= ~MLOOPUV_VERTSEL;
2581         }
2582 
2583         changed = true;
2584       }
2585     }
2586 
2587     if (changed) {
2588       changed_multi = true;
2589       WM_event_add_notifier(C, NC_SPACE | ND_SPACE_IMAGE, NULL);
2590       uv_select_tag_update_for_object(depsgraph, ts, obedit);
2591     }
2592   }
2593   MEM_freeN(objects);
2594 
2595   return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
2596 }
2597 
UV_OT_select_split(wmOperatorType * ot)2598 void UV_OT_select_split(wmOperatorType *ot)
2599 {
2600   /* identifiers */
2601   ot->name = "Select Split";
2602   ot->description = "Select only entirely selected faces";
2603   ot->idname = "UV_OT_select_split";
2604   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
2605 
2606   /* api callbacks */
2607   ot->exec = uv_select_split_exec;
2608   ot->poll = ED_operator_uvedit; /* requires space image */
2609 }
2610 
uv_select_tag_update_for_object(Depsgraph * depsgraph,const ToolSettings * ts,Object * obedit)2611 static void uv_select_tag_update_for_object(Depsgraph *depsgraph,
2612                                             const ToolSettings *ts,
2613                                             Object *obedit)
2614 {
2615   if (ts->uv_flag & UV_SYNC_SELECTION) {
2616     DEG_id_tag_update(obedit->data, ID_RECALC_SELECT);
2617     WM_main_add_notifier(NC_GEOM | ND_SELECT, obedit->data);
2618   }
2619   else {
2620     Object *obedit_eval = DEG_get_evaluated_object(depsgraph, obedit);
2621     BKE_mesh_batch_cache_dirty_tag(obedit_eval->data, BKE_MESH_BATCH_DIRTY_UVEDIT_SELECT);
2622     /* Only for region redraw. */
2623     WM_main_add_notifier(NC_GEOM | ND_SELECT, obedit->data);
2624   }
2625 }
2626 
2627 /** \} */
2628 
2629 /* -------------------------------------------------------------------- */
2630 /** \name Select/Tag Flushing Utils
2631  *
2632  * Utility functions to flush the uv-selection from tags.
2633  * \{ */
2634 
2635 /**
2636  * helper function for #uv_select_flush_from_tag_loop and uv_select_flush_from_tag_face
2637  */
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)2638 static void uv_select_flush_from_tag_sticky_loc_internal(Scene *scene,
2639                                                          BMEditMesh *em,
2640                                                          UvVertMap *vmap,
2641                                                          const uint efa_index,
2642                                                          BMLoop *l,
2643                                                          const bool select,
2644                                                          const int cd_loop_uv_offset)
2645 {
2646   UvMapVert *start_vlist = NULL, *vlist_iter;
2647   BMFace *efa_vlist;
2648 
2649   uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
2650 
2651   vlist_iter = BM_uv_vert_map_at_index(vmap, BM_elem_index_get(l->v));
2652 
2653   while (vlist_iter) {
2654     if (vlist_iter->separate) {
2655       start_vlist = vlist_iter;
2656     }
2657 
2658     if (efa_index == vlist_iter->poly_index) {
2659       break;
2660     }
2661 
2662     vlist_iter = vlist_iter->next;
2663   }
2664 
2665   vlist_iter = start_vlist;
2666   while (vlist_iter) {
2667 
2668     if (vlist_iter != start_vlist && vlist_iter->separate) {
2669       break;
2670     }
2671 
2672     if (efa_index != vlist_iter->poly_index) {
2673       BMLoop *l_other;
2674       efa_vlist = BM_face_at_index(em->bm, vlist_iter->poly_index);
2675       /* tf_vlist = BM_ELEM_CD_GET_VOID_P(efa_vlist, cd_poly_tex_offset); */ /* UNUSED */
2676 
2677       l_other = BM_iter_at_index(
2678           em->bm, BM_LOOPS_OF_FACE, efa_vlist, vlist_iter->loop_of_poly_index);
2679 
2680       uvedit_uv_select_set(scene, em, l_other, select, false, cd_loop_uv_offset);
2681     }
2682     vlist_iter = vlist_iter->next;
2683   }
2684 }
2685 
2686 /**
2687  * Flush the selection from face tags based on sticky and selection modes.
2688  *
2689  * needed because settings the selection a face is done in a number of places but it also
2690  * needs to respect the sticky modes for the UV verts, so dealing with the sticky modes
2691  * is best done in a separate function.
2692  *
2693  * \note This function is very similar to #uv_select_flush_from_tag_loop,
2694  * be sure to update both upon changing.
2695  */
uv_select_flush_from_tag_face(SpaceImage * sima,Scene * scene,Object * obedit,const bool select)2696 static void uv_select_flush_from_tag_face(SpaceImage *sima,
2697                                           Scene *scene,
2698                                           Object *obedit,
2699                                           const bool select)
2700 {
2701   /* Selecting UV Faces with some modes requires us to change
2702    * the selection in other faces (depending on the sticky mode).
2703    *
2704    * This only needs to be done when the Mesh is not used for
2705    * selection (so for sticky modes, vertex or location based). */
2706 
2707   const ToolSettings *ts = scene->toolsettings;
2708   BMEditMesh *em = BKE_editmesh_from_object(obedit);
2709   BMFace *efa;
2710   BMLoop *l;
2711   BMIter iter, liter;
2712   const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
2713 
2714   if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_VERTEX) {
2715     /* Tag all verts as untouched, then touch the ones that have a face center
2716      * in the loop and select all MLoopUV's that use a touched vert. */
2717     BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false);
2718 
2719     BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2720       if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
2721         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2722           BM_elem_flag_enable(l->v, BM_ELEM_TAG);
2723         }
2724       }
2725     }
2726 
2727     /* now select tagged verts */
2728     BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2729       /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */
2730 
2731       BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2732         if (BM_elem_flag_test(l->v, BM_ELEM_TAG)) {
2733           uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
2734         }
2735       }
2736     }
2737   }
2738   else if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_LOC) {
2739     struct UvVertMap *vmap;
2740     uint efa_index;
2741 
2742     BM_mesh_elem_table_ensure(em->bm, BM_FACE);
2743     vmap = BM_uv_vert_map_create(em->bm, false, false);
2744     if (vmap == NULL) {
2745       return;
2746     }
2747 
2748     BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, efa_index) {
2749       if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
2750         /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */
2751 
2752         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2753           uv_select_flush_from_tag_sticky_loc_internal(
2754               scene, em, vmap, efa_index, l, select, cd_loop_uv_offset);
2755         }
2756       }
2757     }
2758     BM_uv_vert_map_free(vmap);
2759   }
2760   else { /* SI_STICKY_DISABLE or ts->uv_flag & UV_SYNC_SELECTION */
2761     BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2762       if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
2763         uvedit_face_select_set(scene, em, efa, select, false, cd_loop_uv_offset);
2764       }
2765     }
2766   }
2767 }
2768 
2769 /**
2770  * Flush the selection from loop tags based on sticky and selection modes.
2771  *
2772  * needed because settings the selection a face is done in a number of places but it also needs
2773  * to respect the sticky modes for the UV verts, so dealing with the sticky modes is best done
2774  * in a separate function.
2775  *
2776  * \note This function is very similar to #uv_select_flush_from_tag_loop,
2777  * be sure to update both upon changing.
2778  */
uv_select_flush_from_tag_loop(SpaceImage * sima,Scene * scene,Object * obedit,const bool select)2779 static void uv_select_flush_from_tag_loop(SpaceImage *sima,
2780                                           Scene *scene,
2781                                           Object *obedit,
2782                                           const bool select)
2783 {
2784   /* Selecting UV Loops with some modes requires us to change
2785    * the selection in other faces (depending on the sticky mode).
2786    *
2787    * This only needs to be done when the Mesh is not used for
2788    * selection (so for sticky modes, vertex or location based). */
2789 
2790   const ToolSettings *ts = scene->toolsettings;
2791   BMEditMesh *em = BKE_editmesh_from_object(obedit);
2792   BMFace *efa;
2793   BMLoop *l;
2794   BMIter iter, liter;
2795 
2796   const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
2797 
2798   if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_VERTEX) {
2799     /* Tag all verts as untouched, then touch the ones that have a face center
2800      * in the loop and select all MLoopUV's that use a touched vert. */
2801     BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false);
2802 
2803     BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2804       BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2805         if (BM_elem_flag_test(l, BM_ELEM_TAG)) {
2806           BM_elem_flag_enable(l->v, BM_ELEM_TAG);
2807         }
2808       }
2809     }
2810 
2811     /* now select tagged verts */
2812     BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2813       /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */
2814 
2815       BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2816         if (BM_elem_flag_test(l->v, BM_ELEM_TAG)) {
2817           uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
2818         }
2819       }
2820     }
2821   }
2822   else if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && sima->sticky == SI_STICKY_LOC) {
2823     struct UvVertMap *vmap;
2824     uint efa_index;
2825 
2826     BM_mesh_elem_table_ensure(em->bm, BM_FACE);
2827     vmap = BM_uv_vert_map_create(em->bm, false, false);
2828     if (vmap == NULL) {
2829       return;
2830     }
2831 
2832     BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, efa_index) {
2833       /* tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); */ /* UNUSED */
2834 
2835       BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2836         if (BM_elem_flag_test(l, BM_ELEM_TAG)) {
2837           uv_select_flush_from_tag_sticky_loc_internal(
2838               scene, em, vmap, efa_index, l, select, cd_loop_uv_offset);
2839         }
2840       }
2841     }
2842     BM_uv_vert_map_free(vmap);
2843   }
2844   else { /* SI_STICKY_DISABLE or ts->uv_flag & UV_SYNC_SELECTION */
2845     BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2846       BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2847         if (BM_elem_flag_test(l, BM_ELEM_TAG)) {
2848           uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
2849         }
2850       }
2851     }
2852   }
2853 }
2854 
2855 /** \} */
2856 
2857 /* -------------------------------------------------------------------- */
2858 /** \name Box Select Operator
2859  * \{ */
2860 
uv_box_select_exec(bContext * C,wmOperator * op)2861 static int uv_box_select_exec(bContext *C, wmOperator *op)
2862 {
2863   Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
2864   SpaceImage *sima = CTX_wm_space_image(C);
2865   Scene *scene = CTX_data_scene(C);
2866   const ToolSettings *ts = scene->toolsettings;
2867   ViewLayer *view_layer = CTX_data_view_layer(C);
2868   const ARegion *region = CTX_wm_region(C);
2869   BMFace *efa;
2870   BMLoop *l;
2871   BMIter iter, liter;
2872   MLoopUV *luv;
2873   rctf rectf;
2874   bool pinned;
2875   const bool use_face_center = ((ts->uv_flag & UV_SYNC_SELECTION) ?
2876                                     (ts->selectmode == SCE_SELECT_FACE) :
2877                                     (ts->uv_selectmode == UV_SELECT_FACE));
2878   const bool use_edge = ((ts->uv_flag & UV_SYNC_SELECTION) ?
2879                              (ts->selectmode == SCE_SELECT_EDGE) :
2880                              (ts->uv_selectmode == UV_SELECT_EDGE));
2881 
2882   /* get rectangle from operator */
2883   WM_operator_properties_border_to_rctf(op, &rectf);
2884   UI_view2d_region_to_view_rctf(&region->v2d, &rectf, &rectf);
2885 
2886   const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
2887   const bool select = (sel_op != SEL_OP_SUB);
2888   const bool use_pre_deselect = SEL_OP_USE_PRE_DESELECT(sel_op);
2889 
2890   pinned = RNA_boolean_get(op->ptr, "pinned");
2891 
2892   bool changed_multi = false;
2893 
2894   uint objects_len = 0;
2895   Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
2896       view_layer, ((View3D *)NULL), &objects_len);
2897 
2898   if (use_pre_deselect) {
2899     uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT);
2900   }
2901 
2902   /* don't indent to avoid diff noise! */
2903   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
2904     Object *obedit = objects[ob_index];
2905     BMEditMesh *em = BKE_editmesh_from_object(obedit);
2906 
2907     bool changed = false;
2908 
2909     const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
2910 
2911     /* do actual selection */
2912     if (use_face_center && !pinned) {
2913       /* handle face selection mode */
2914       float cent[2];
2915 
2916       BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2917         /* assume not touched */
2918         BM_elem_flag_disable(efa, BM_ELEM_TAG);
2919 
2920         if (uvedit_face_visible_test(scene, efa)) {
2921           BM_face_uv_calc_center_median(efa, cd_loop_uv_offset, cent);
2922           if (BLI_rctf_isect_pt_v(&rectf, cent)) {
2923             BM_elem_flag_enable(efa, BM_ELEM_TAG);
2924             changed = true;
2925           }
2926         }
2927       }
2928 
2929       /* (de)selects all tagged faces and deals with sticky modes */
2930       if (changed) {
2931         uv_select_flush_from_tag_face(sima, scene, obedit, select);
2932       }
2933     }
2934     else if (use_edge && !pinned) {
2935       BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2936         if (!uvedit_face_visible_test(scene, efa)) {
2937           continue;
2938         }
2939 
2940         BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev;
2941         MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l_prev, cd_loop_uv_offset);
2942 
2943         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2944           luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2945           if (BLI_rctf_isect_pt_v(&rectf, luv->uv) && BLI_rctf_isect_pt_v(&rectf, luv_prev->uv)) {
2946             uvedit_edge_select_set_with_sticky(
2947                 sima, scene, em, l_prev, select, false, cd_loop_uv_offset);
2948             changed = true;
2949           }
2950           l_prev = l;
2951           luv_prev = luv;
2952         }
2953       }
2954     }
2955     else {
2956       /* other selection modes */
2957       changed = true;
2958       BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false);
2959 
2960       BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2961         if (!uvedit_face_visible_test(scene, efa)) {
2962           continue;
2963         }
2964         bool has_selected = false;
2965         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2966           luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
2967           if ((select) != (uvedit_uv_select_test(scene, l, cd_loop_uv_offset))) {
2968             if (!pinned || (ts->uv_flag & UV_SYNC_SELECTION)) {
2969               /* UV_SYNC_SELECTION - can't do pinned selection */
2970               if (BLI_rctf_isect_pt_v(&rectf, luv->uv)) {
2971                 uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
2972                 BM_elem_flag_enable(l->v, BM_ELEM_TAG);
2973                 has_selected = true;
2974               }
2975             }
2976             else if (pinned) {
2977               if ((luv->flag & MLOOPUV_PINNED) && BLI_rctf_isect_pt_v(&rectf, luv->uv)) {
2978                 uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
2979                 BM_elem_flag_enable(l->v, BM_ELEM_TAG);
2980               }
2981             }
2982           }
2983         }
2984         if (has_selected && ts->uv_selectmode == UV_SELECT_ISLAND) {
2985           UvNearestHit hit = {
2986               .ob = obedit,
2987               .efa = efa,
2988           };
2989           uv_select_linked_multi(scene, objects, objects_len, &hit, true, !select, false, false);
2990         }
2991       }
2992 
2993       if (sima->sticky == SI_STICKY_VERTEX) {
2994         uvedit_vertex_select_tagged(em, scene, select, cd_loop_uv_offset);
2995       }
2996     }
2997 
2998     if (changed || use_pre_deselect) {
2999       changed_multi = true;
3000 
3001       ED_uvedit_select_sync_flush(ts, em, select);
3002       uv_select_tag_update_for_object(depsgraph, ts, obedit);
3003     }
3004   }
3005 
3006   MEM_freeN(objects);
3007 
3008   return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
3009 }
3010 
UV_OT_select_box(wmOperatorType * ot)3011 void UV_OT_select_box(wmOperatorType *ot)
3012 {
3013   /* identifiers */
3014   ot->name = "Box Select";
3015   ot->description = "Select UV vertices using box selection";
3016   ot->idname = "UV_OT_select_box";
3017 
3018   /* api callbacks */
3019   ot->invoke = WM_gesture_box_invoke;
3020   ot->exec = uv_box_select_exec;
3021   ot->modal = WM_gesture_box_modal;
3022   ot->poll = ED_operator_uvedit_space_image; /* requires space image */
3023   ot->cancel = WM_gesture_box_cancel;
3024 
3025   /* flags */
3026   ot->flag = OPTYPE_UNDO;
3027 
3028   /* properties */
3029   RNA_def_boolean(ot->srna, "pinned", 0, "Pinned", "Border select pinned UVs only");
3030 
3031   WM_operator_properties_gesture_box(ot);
3032   WM_operator_properties_select_operation_simple(ot);
3033 }
3034 
3035 /** \} */
3036 
3037 /* -------------------------------------------------------------------- */
3038 /** \name Circle Select Operator
3039  * \{ */
3040 
uv_circle_select_is_point_inside(const float uv[2],const float offset[2],const float ellipse[2])3041 static int uv_circle_select_is_point_inside(const float uv[2],
3042                                             const float offset[2],
3043                                             const float ellipse[2])
3044 {
3045   /* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */
3046   const float co[2] = {
3047       (uv[0] - offset[0]) * ellipse[0],
3048       (uv[1] - offset[1]) * ellipse[1],
3049   };
3050   return len_squared_v2(co) < 1.0f;
3051 }
3052 
uv_circle_select_is_edge_inside(const float uv_a[2],const float uv_b[2],const float offset[2],const float ellipse[2])3053 static int uv_circle_select_is_edge_inside(const float uv_a[2],
3054                                            const float uv_b[2],
3055                                            const float offset[2],
3056                                            const float ellipse[2])
3057 {
3058   /* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */
3059   const float co_a[2] = {
3060       (uv_a[0] - offset[0]) * ellipse[0],
3061       (uv_a[1] - offset[1]) * ellipse[1],
3062   };
3063   const float co_b[2] = {
3064       (uv_b[0] - offset[0]) * ellipse[0],
3065       (uv_b[1] - offset[1]) * ellipse[1],
3066   };
3067   return dist_squared_to_line_segment_v2((const float[2]){0.0f, 0.0f}, co_a, co_b) < 1.0f;
3068 }
3069 
uv_circle_select_exec(bContext * C,wmOperator * op)3070 static int uv_circle_select_exec(bContext *C, wmOperator *op)
3071 {
3072   Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
3073   SpaceImage *sima = CTX_wm_space_image(C);
3074   Scene *scene = CTX_data_scene(C);
3075   ViewLayer *view_layer = CTX_data_view_layer(C);
3076   const ToolSettings *ts = scene->toolsettings;
3077   const ARegion *region = CTX_wm_region(C);
3078   BMFace *efa;
3079   BMLoop *l;
3080   BMIter iter, liter;
3081   MLoopUV *luv;
3082   int x, y, radius, width, height;
3083   float zoomx, zoomy;
3084   float offset[2], ellipse[2];
3085 
3086   const bool use_face_center = ((ts->uv_flag & UV_SYNC_SELECTION) ?
3087                                     (ts->selectmode == SCE_SELECT_FACE) :
3088                                     (ts->uv_selectmode == UV_SELECT_FACE));
3089   const bool use_edge = ((ts->uv_flag & UV_SYNC_SELECTION) ?
3090                              (ts->selectmode == SCE_SELECT_EDGE) :
3091                              (ts->uv_selectmode == UV_SELECT_EDGE));
3092 
3093   /* get operator properties */
3094   x = RNA_int_get(op->ptr, "x");
3095   y = RNA_int_get(op->ptr, "y");
3096   radius = RNA_int_get(op->ptr, "radius");
3097 
3098   /* compute ellipse size and location, not a circle since we deal
3099    * with non square image. ellipse is normalized, r = 1.0. */
3100   ED_space_image_get_size(sima, &width, &height);
3101   ED_space_image_get_zoom(sima, region, &zoomx, &zoomy);
3102 
3103   ellipse[0] = width * zoomx / radius;
3104   ellipse[1] = height * zoomy / radius;
3105 
3106   UI_view2d_region_to_view(&region->v2d, x, y, &offset[0], &offset[1]);
3107 
3108   bool changed_multi = false;
3109 
3110   uint objects_len = 0;
3111   Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
3112       view_layer, ((View3D *)NULL), &objects_len);
3113 
3114   const eSelectOp sel_op = ED_select_op_modal(RNA_enum_get(op->ptr, "mode"),
3115                                               WM_gesture_is_modal_first(op->customdata));
3116   const bool select = (sel_op != SEL_OP_SUB);
3117   const bool use_pre_deselect = SEL_OP_USE_PRE_DESELECT(sel_op);
3118 
3119   if (use_pre_deselect) {
3120     uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT);
3121   }
3122 
3123   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
3124     Object *obedit = objects[ob_index];
3125     BMEditMesh *em = BKE_editmesh_from_object(obedit);
3126 
3127     bool changed = false;
3128 
3129     const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
3130 
3131     /* do selection */
3132     if (use_face_center) {
3133       BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3134         BM_elem_flag_disable(efa, BM_ELEM_TAG);
3135         /* assume not touched */
3136         if (select != uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) {
3137           float cent[2];
3138           BM_face_uv_calc_center_median(efa, cd_loop_uv_offset, cent);
3139           if (uv_circle_select_is_point_inside(cent, offset, ellipse)) {
3140             BM_elem_flag_enable(efa, BM_ELEM_TAG);
3141             changed = true;
3142           }
3143         }
3144       }
3145 
3146       /* (de)selects all tagged faces and deals with sticky modes */
3147       if (changed) {
3148         uv_select_flush_from_tag_face(sima, scene, obedit, select);
3149       }
3150     }
3151     else if (use_edge) {
3152       BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3153         if (!uvedit_face_visible_test(scene, efa)) {
3154           continue;
3155         }
3156 
3157         BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev;
3158         MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l_prev, cd_loop_uv_offset);
3159 
3160         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3161           luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
3162           if (uv_circle_select_is_edge_inside(luv->uv, luv_prev->uv, offset, ellipse)) {
3163             uvedit_edge_select_set_with_sticky(
3164                 sima, scene, em, l_prev, select, false, cd_loop_uv_offset);
3165             changed = true;
3166           }
3167           l_prev = l;
3168           luv_prev = luv;
3169         }
3170       }
3171     }
3172     else {
3173       BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false);
3174 
3175       BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3176         if (!uvedit_face_visible_test(scene, efa)) {
3177           continue;
3178         }
3179         bool has_selected = false;
3180         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3181           if ((select) != (uvedit_uv_select_test(scene, l, cd_loop_uv_offset))) {
3182             luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
3183             if (uv_circle_select_is_point_inside(luv->uv, offset, ellipse)) {
3184               changed = true;
3185               uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
3186               BM_elem_flag_enable(l->v, BM_ELEM_TAG);
3187               has_selected = true;
3188             }
3189           }
3190         }
3191         if (has_selected && ts->uv_selectmode == UV_SELECT_ISLAND) {
3192           UvNearestHit hit = {
3193               .ob = obedit,
3194               .efa = efa,
3195           };
3196           uv_select_linked_multi(scene, objects, objects_len, &hit, true, !select, false, false);
3197         }
3198       }
3199 
3200       if (sima->sticky == SI_STICKY_VERTEX) {
3201         uvedit_vertex_select_tagged(em, scene, select, cd_loop_uv_offset);
3202       }
3203     }
3204 
3205     if (changed || use_pre_deselect) {
3206       changed_multi = true;
3207 
3208       ED_uvedit_select_sync_flush(ts, em, select);
3209       uv_select_tag_update_for_object(depsgraph, ts, obedit);
3210     }
3211   }
3212   MEM_freeN(objects);
3213 
3214   return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
3215 }
3216 
UV_OT_select_circle(wmOperatorType * ot)3217 void UV_OT_select_circle(wmOperatorType *ot)
3218 {
3219   /* identifiers */
3220   ot->name = "Circle Select";
3221   ot->description = "Select UV vertices using circle selection";
3222   ot->idname = "UV_OT_select_circle";
3223 
3224   /* api callbacks */
3225   ot->invoke = WM_gesture_circle_invoke;
3226   ot->modal = WM_gesture_circle_modal;
3227   ot->exec = uv_circle_select_exec;
3228   ot->poll = ED_operator_uvedit_space_image; /* requires space image */
3229   ot->cancel = WM_gesture_circle_cancel;
3230 
3231   /* flags */
3232   ot->flag = OPTYPE_UNDO;
3233 
3234   /* properties */
3235   WM_operator_properties_gesture_circle(ot);
3236   WM_operator_properties_select_operation_simple(ot);
3237 }
3238 
3239 /** \} */
3240 
3241 /* -------------------------------------------------------------------- */
3242 /** \name Lasso Select Operator
3243  * \{ */
3244 
do_lasso_select_mesh_uv_is_point_inside(const ARegion * region,const rcti * clip_rect,const int mcoords[][2],const int mcoords_len,const float co_test[2])3245 static bool do_lasso_select_mesh_uv_is_point_inside(const ARegion *region,
3246                                                     const rcti *clip_rect,
3247                                                     const int mcoords[][2],
3248                                                     const int mcoords_len,
3249                                                     const float co_test[2])
3250 {
3251   int co_screen[2];
3252   if (UI_view2d_view_to_region_clip(
3253           &region->v2d, co_test[0], co_test[1], &co_screen[0], &co_screen[1]) &&
3254       BLI_rcti_isect_pt_v(clip_rect, co_screen) &&
3255       BLI_lasso_is_point_inside(
3256           mcoords, mcoords_len, co_screen[0], co_screen[1], V2D_IS_CLIPPED)) {
3257     return true;
3258   }
3259   return false;
3260 }
3261 
do_lasso_select_mesh_uv(bContext * C,const int mcoords[][2],const int mcoords_len,const eSelectOp sel_op)3262 static bool do_lasso_select_mesh_uv(bContext *C,
3263                                     const int mcoords[][2],
3264                                     const int mcoords_len,
3265                                     const eSelectOp sel_op)
3266 {
3267   Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
3268   SpaceImage *sima = CTX_wm_space_image(C);
3269   const ARegion *region = CTX_wm_region(C);
3270   Scene *scene = CTX_data_scene(C);
3271   const ToolSettings *ts = scene->toolsettings;
3272   ViewLayer *view_layer = CTX_data_view_layer(C);
3273   const bool use_face_center = ((ts->uv_flag & UV_SYNC_SELECTION) ?
3274                                     (ts->selectmode == SCE_SELECT_FACE) :
3275                                     (ts->uv_selectmode == UV_SELECT_FACE));
3276   const bool use_edge = ((ts->uv_flag & UV_SYNC_SELECTION) ?
3277                              (ts->selectmode == SCE_SELECT_EDGE) :
3278                              (ts->uv_selectmode == UV_SELECT_EDGE));
3279 
3280   const bool select = (sel_op != SEL_OP_SUB);
3281   const bool use_pre_deselect = SEL_OP_USE_PRE_DESELECT(sel_op);
3282 
3283   BMIter iter, liter;
3284 
3285   BMFace *efa;
3286   BMLoop *l;
3287   bool changed_multi = false;
3288   rcti rect;
3289 
3290   BLI_lasso_boundbox(&rect, mcoords, mcoords_len);
3291 
3292   uint objects_len = 0;
3293   Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
3294       view_layer, ((View3D *)NULL), &objects_len);
3295 
3296   if (use_pre_deselect) {
3297     uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT);
3298   }
3299 
3300   /* don't indent to avoid diff noise! */
3301   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
3302     Object *obedit = objects[ob_index];
3303 
3304     bool changed = false;
3305 
3306     BMEditMesh *em = BKE_editmesh_from_object(obedit);
3307 
3308     const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
3309 
3310     if (use_face_center) { /* Face Center Sel */
3311       BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3312         BM_elem_flag_disable(efa, BM_ELEM_TAG);
3313         /* assume not touched */
3314         if (select != uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) {
3315           float cent[2];
3316           BM_face_uv_calc_center_median(efa, cd_loop_uv_offset, cent);
3317           if (do_lasso_select_mesh_uv_is_point_inside(region, &rect, mcoords, mcoords_len, cent)) {
3318             BM_elem_flag_enable(efa, BM_ELEM_TAG);
3319             changed = true;
3320           }
3321         }
3322       }
3323 
3324       /* (de)selects all tagged faces and deals with sticky modes */
3325       if (changed) {
3326         uv_select_flush_from_tag_face(sima, scene, obedit, select);
3327       }
3328     }
3329     else if (use_edge) {
3330       BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3331         if (!uvedit_face_visible_test(scene, efa)) {
3332           continue;
3333         }
3334 
3335         BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev;
3336         MLoopUV *luv_prev = BM_ELEM_CD_GET_VOID_P(l_prev, cd_loop_uv_offset);
3337 
3338         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3339           MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
3340           if (do_lasso_select_mesh_uv_is_point_inside(
3341                   region, &rect, mcoords, mcoords_len, luv->uv) &&
3342               do_lasso_select_mesh_uv_is_point_inside(
3343                   region, &rect, mcoords, mcoords_len, luv_prev->uv)) {
3344             uvedit_edge_select_set_with_sticky(
3345                 sima, scene, em, l_prev, select, false, cd_loop_uv_offset);
3346             changed = true;
3347           }
3348           l_prev = l;
3349           luv_prev = luv;
3350         }
3351       }
3352     }
3353     else { /* Vert Sel */
3354       BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false);
3355 
3356       BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3357         if (!uvedit_face_visible_test(scene, efa)) {
3358           continue;
3359         }
3360         bool has_selected = false;
3361         BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3362           if ((select) != (uvedit_uv_select_test(scene, l, cd_loop_uv_offset))) {
3363             MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
3364             if (do_lasso_select_mesh_uv_is_point_inside(
3365                     region, &rect, mcoords, mcoords_len, luv->uv)) {
3366               uvedit_uv_select_set(scene, em, l, select, false, cd_loop_uv_offset);
3367               changed = true;
3368               BM_elem_flag_enable(l->v, BM_ELEM_TAG);
3369               has_selected = true;
3370             }
3371           }
3372         }
3373         if (has_selected && ts->uv_selectmode == UV_SELECT_ISLAND) {
3374           UvNearestHit hit = {
3375               .ob = obedit,
3376               .efa = efa,
3377           };
3378           uv_select_linked_multi(scene, objects, objects_len, &hit, true, !select, false, false);
3379         }
3380       }
3381 
3382       if (sima->sticky == SI_STICKY_VERTEX) {
3383         uvedit_vertex_select_tagged(em, scene, select, cd_loop_uv_offset);
3384       }
3385     }
3386 
3387     if (changed || use_pre_deselect) {
3388       changed_multi = true;
3389 
3390       ED_uvedit_select_sync_flush(ts, em, select);
3391       uv_select_tag_update_for_object(depsgraph, ts, obedit);
3392     }
3393   }
3394   MEM_freeN(objects);
3395 
3396   return changed_multi;
3397 }
3398 
uv_lasso_select_exec(bContext * C,wmOperator * op)3399 static int uv_lasso_select_exec(bContext *C, wmOperator *op)
3400 {
3401   int mcoords_len;
3402   const int(*mcoords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcoords_len);
3403 
3404   if (mcoords) {
3405     const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
3406     bool changed = do_lasso_select_mesh_uv(C, mcoords, mcoords_len, sel_op);
3407     MEM_freeN((void *)mcoords);
3408 
3409     return changed ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
3410   }
3411 
3412   return OPERATOR_PASS_THROUGH;
3413 }
3414 
UV_OT_select_lasso(wmOperatorType * ot)3415 void UV_OT_select_lasso(wmOperatorType *ot)
3416 {
3417   ot->name = "Lasso Select UV";
3418   ot->description = "Select UVs using lasso selection";
3419   ot->idname = "UV_OT_select_lasso";
3420 
3421   ot->invoke = WM_gesture_lasso_invoke;
3422   ot->modal = WM_gesture_lasso_modal;
3423   ot->exec = uv_lasso_select_exec;
3424   ot->poll = ED_operator_uvedit_space_image;
3425   ot->cancel = WM_gesture_lasso_cancel;
3426 
3427   /* flags */
3428   ot->flag = OPTYPE_UNDO;
3429 
3430   /* properties */
3431   WM_operator_properties_gesture_lasso(ot);
3432   WM_operator_properties_select_operation_simple(ot);
3433 }
3434 
3435 /** \} */
3436 
3437 /* -------------------------------------------------------------------- */
3438 /** \name Select Pinned UV's Operator
3439  * \{ */
3440 
uv_select_pinned_exec(bContext * C,wmOperator * UNUSED (op))3441 static int uv_select_pinned_exec(bContext *C, wmOperator *UNUSED(op))
3442 {
3443   Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
3444   Scene *scene = CTX_data_scene(C);
3445   const ToolSettings *ts = scene->toolsettings;
3446   ViewLayer *view_layer = CTX_data_view_layer(C);
3447   BMFace *efa;
3448   BMLoop *l;
3449   BMIter iter, liter;
3450   MLoopUV *luv;
3451 
3452   uint objects_len = 0;
3453   Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
3454       view_layer, ((View3D *)NULL), &objects_len);
3455 
3456   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
3457     Object *obedit = objects[ob_index];
3458     BMEditMesh *em = BKE_editmesh_from_object(obedit);
3459 
3460     const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
3461     bool changed = false;
3462 
3463     BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3464       if (!uvedit_face_visible_test(scene, efa)) {
3465         continue;
3466       }
3467 
3468       BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3469         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
3470 
3471         if (luv->flag & MLOOPUV_PINNED) {
3472           uvedit_uv_select_enable(scene, em, l, false, cd_loop_uv_offset);
3473           changed = true;
3474         }
3475       }
3476     }
3477 
3478     if (changed) {
3479       uv_select_tag_update_for_object(depsgraph, ts, obedit);
3480     }
3481   }
3482   MEM_freeN(objects);
3483 
3484   return OPERATOR_FINISHED;
3485 }
3486 
UV_OT_select_pinned(wmOperatorType * ot)3487 void UV_OT_select_pinned(wmOperatorType *ot)
3488 {
3489   /* identifiers */
3490   ot->name = "Selected Pinned";
3491   ot->description = "Select all pinned UV vertices";
3492   ot->idname = "UV_OT_select_pinned";
3493   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3494 
3495   /* api callbacks */
3496   ot->exec = uv_select_pinned_exec;
3497   ot->poll = ED_operator_uvedit;
3498 }
3499 
3500 /** \} */
3501 
3502 /* -------------------------------------------------------------------- */
3503 /** \name Select Overlap Operator
3504  * \{ */
3505 
overlap_hash(const void * overlap_v)3506 BLI_INLINE uint overlap_hash(const void *overlap_v)
3507 {
3508   const BVHTreeOverlap *overlap = overlap_v;
3509 
3510   /* Designed to treat (A,B) and (B,A) as the same. */
3511   int x = overlap->indexA;
3512   int y = overlap->indexB;
3513   if (x > y) {
3514     SWAP(int, x, y);
3515   }
3516   return BLI_hash_int_2d(x, y);
3517 }
3518 
overlap_cmp(const void * a_v,const void * b_v)3519 BLI_INLINE bool overlap_cmp(const void *a_v, const void *b_v)
3520 {
3521   const BVHTreeOverlap *a = a_v;
3522   const BVHTreeOverlap *b = b_v;
3523   return !((a->indexA == b->indexA && a->indexB == b->indexB) ||
3524            (a->indexA == b->indexB && a->indexB == b->indexA));
3525 }
3526 
3527 struct UVOverlapData {
3528   int ob_index;
3529   int face_index;
3530   float tri[3][2];
3531 };
3532 
uv_select_overlap(bContext * C,const bool extend)3533 static int uv_select_overlap(bContext *C, const bool extend)
3534 {
3535   Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
3536   Scene *scene = CTX_data_scene(C);
3537   ViewLayer *view_layer = CTX_data_view_layer(C);
3538 
3539   uint objects_len = 0;
3540   Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(
3541       view_layer, ((View3D *)NULL), &objects_len);
3542 
3543   /* Calculate maximum number of tree nodes and prepare initial selection. */
3544   uint uv_tri_len = 0;
3545   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
3546     Object *obedit = objects[ob_index];
3547     BMEditMesh *em = BKE_editmesh_from_object(obedit);
3548 
3549     BM_mesh_elem_table_ensure(em->bm, BM_FACE);
3550     BM_mesh_elem_index_ensure(em->bm, BM_VERT | BM_FACE);
3551     BM_mesh_elem_hflag_disable_all(em->bm, BM_FACE, BM_ELEM_TAG, false);
3552     if (!extend) {
3553       uv_select_all_perform(scene, obedit, SEL_DESELECT);
3554     }
3555 
3556     BMIter iter;
3557     BMFace *efa;
3558     BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3559       if (!uvedit_face_visible_test_ex(scene->toolsettings, efa)) {
3560         continue;
3561       }
3562       uv_tri_len += efa->len - 2;
3563     }
3564   }
3565 
3566   struct UVOverlapData *overlap_data = MEM_mallocN(sizeof(struct UVOverlapData) * uv_tri_len,
3567                                                    "UvOverlapData");
3568   BVHTree *uv_tree = BLI_bvhtree_new(uv_tri_len, 0.0f, 4, 6);
3569 
3570   /* Use a global data index when inserting into the BVH. */
3571   int data_index = 0;
3572 
3573   int face_len_alloc = 3;
3574   float(*uv_verts)[2] = MEM_mallocN(sizeof(*uv_verts) * face_len_alloc, "UvOverlapCoords");
3575   uint(*indices)[3] = MEM_mallocN(sizeof(*indices) * (face_len_alloc - 2), "UvOverlapTris");
3576 
3577   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
3578     Object *obedit = objects[ob_index];
3579     BMEditMesh *em = BKE_editmesh_from_object(obedit);
3580     BMIter iter, liter;
3581     BMFace *efa;
3582     BMLoop *l;
3583 
3584     const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
3585 
3586     /* Triangulate each UV face and store it inside the BVH. */
3587     int face_index;
3588     BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, face_index) {
3589 
3590       if (!uvedit_face_visible_test_ex(scene->toolsettings, efa)) {
3591         continue;
3592       }
3593 
3594       const uint face_len = efa->len;
3595       const uint tri_len = face_len - 2;
3596 
3597       if (face_len_alloc < face_len) {
3598         MEM_freeN(uv_verts);
3599         MEM_freeN(indices);
3600         uv_verts = MEM_mallocN(sizeof(*uv_verts) * face_len, "UvOverlapCoords");
3601         indices = MEM_mallocN(sizeof(*indices) * tri_len, "UvOverlapTris");
3602         face_len_alloc = face_len;
3603       }
3604 
3605       int vert_index;
3606       BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, vert_index) {
3607         MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
3608         copy_v2_v2(uv_verts[vert_index], luv->uv);
3609       }
3610 
3611       BLI_polyfill_calc(uv_verts, face_len, 0, indices);
3612 
3613       for (int t = 0; t < tri_len; t++) {
3614         overlap_data[data_index].ob_index = ob_index;
3615         overlap_data[data_index].face_index = face_index;
3616 
3617         /* BVH needs 3D, overlap data uses 2D. */
3618         const float tri[3][3] = {
3619             {UNPACK2(uv_verts[indices[t][0]]), 0.0f},
3620             {UNPACK2(uv_verts[indices[t][1]]), 0.0f},
3621             {UNPACK2(uv_verts[indices[t][2]]), 0.0f},
3622         };
3623 
3624         copy_v2_v2(overlap_data[data_index].tri[0], tri[0]);
3625         copy_v2_v2(overlap_data[data_index].tri[1], tri[1]);
3626         copy_v2_v2(overlap_data[data_index].tri[2], tri[2]);
3627 
3628         BLI_bvhtree_insert(uv_tree, data_index, &tri[0][0], 3);
3629         data_index++;
3630       }
3631     }
3632   }
3633   BLI_assert(data_index == uv_tri_len);
3634 
3635   MEM_freeN(uv_verts);
3636   MEM_freeN(indices);
3637 
3638   BLI_bvhtree_balance(uv_tree);
3639 
3640   uint tree_overlap_len;
3641   BVHTreeOverlap *overlap = BLI_bvhtree_overlap(uv_tree, uv_tree, &tree_overlap_len, NULL, NULL);
3642 
3643   if (overlap != NULL) {
3644     GSet *overlap_set = BLI_gset_new_ex(overlap_hash, overlap_cmp, __func__, tree_overlap_len);
3645 
3646     for (int i = 0; i < tree_overlap_len; i++) {
3647       /* Skip overlaps against yourself. */
3648       if (overlap[i].indexA == overlap[i].indexB) {
3649         continue;
3650       }
3651 
3652       /* Skip overlaps that have already been tested. */
3653       if (!BLI_gset_add(overlap_set, &overlap[i])) {
3654         continue;
3655       }
3656 
3657       const struct UVOverlapData *o_a = &overlap_data[overlap[i].indexA];
3658       const struct UVOverlapData *o_b = &overlap_data[overlap[i].indexB];
3659       Object *obedit_a = objects[o_a->ob_index];
3660       Object *obedit_b = objects[o_b->ob_index];
3661       BMEditMesh *em_a = BKE_editmesh_from_object(obedit_a);
3662       BMEditMesh *em_b = BKE_editmesh_from_object(obedit_b);
3663       BMFace *face_a = em_a->bm->ftable[o_a->face_index];
3664       BMFace *face_b = em_b->bm->ftable[o_b->face_index];
3665       const int cd_loop_uv_offset_a = CustomData_get_offset(&em_a->bm->ldata, CD_MLOOPUV);
3666       const int cd_loop_uv_offset_b = CustomData_get_offset(&em_b->bm->ldata, CD_MLOOPUV);
3667 
3668       /* Skip if both faces are already selected. */
3669       if (uvedit_face_select_test(scene, face_a, cd_loop_uv_offset_a) &&
3670           uvedit_face_select_test(scene, face_b, cd_loop_uv_offset_b)) {
3671         continue;
3672       }
3673 
3674       /* Main tri-tri overlap test. */
3675       const float endpoint_bias = -1e-4f;
3676       const float(*t1)[2] = o_a->tri;
3677       const float(*t2)[2] = o_b->tri;
3678       float vi[2];
3679       bool result = (
3680           /* Don't use 'isect_tri_tri_v2' here
3681            * because it's important to ignore overlap at end-points. */
3682           isect_seg_seg_v2_point_ex(t1[0], t1[1], t2[0], t2[1], endpoint_bias, vi) == 1 ||
3683           isect_seg_seg_v2_point_ex(t1[0], t1[1], t2[1], t2[2], endpoint_bias, vi) == 1 ||
3684           isect_seg_seg_v2_point_ex(t1[0], t1[1], t2[2], t2[0], endpoint_bias, vi) == 1 ||
3685           isect_seg_seg_v2_point_ex(t1[1], t1[2], t2[0], t2[1], endpoint_bias, vi) == 1 ||
3686           isect_seg_seg_v2_point_ex(t1[1], t1[2], t2[1], t2[2], endpoint_bias, vi) == 1 ||
3687           isect_seg_seg_v2_point_ex(t1[1], t1[2], t2[2], t2[0], endpoint_bias, vi) == 1 ||
3688           isect_seg_seg_v2_point_ex(t1[2], t1[0], t2[0], t2[1], endpoint_bias, vi) == 1 ||
3689           isect_seg_seg_v2_point_ex(t1[2], t1[0], t2[1], t2[2], endpoint_bias, vi) == 1 ||
3690           isect_point_tri_v2(t1[0], t2[0], t2[1], t2[2]) != 0 ||
3691           isect_point_tri_v2(t2[0], t1[0], t1[1], t1[2]) != 0);
3692 
3693       if (result) {
3694         uvedit_face_select_enable(scene, em_a, face_a, false, cd_loop_uv_offset_a);
3695         uvedit_face_select_enable(scene, em_b, face_b, false, cd_loop_uv_offset_b);
3696       }
3697     }
3698 
3699     BLI_gset_free(overlap_set, NULL);
3700     MEM_freeN(overlap);
3701   }
3702 
3703   for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
3704     uv_select_tag_update_for_object(depsgraph, scene->toolsettings, objects[ob_index]);
3705   }
3706 
3707   BLI_bvhtree_free(uv_tree);
3708 
3709   MEM_freeN(overlap_data);
3710   MEM_freeN(objects);
3711 
3712   return OPERATOR_FINISHED;
3713 }
3714 
uv_select_overlap_exec(bContext * C,wmOperator * op)3715 static int uv_select_overlap_exec(bContext *C, wmOperator *op)
3716 {
3717   bool extend = RNA_boolean_get(op->ptr, "extend");
3718   return uv_select_overlap(C, extend);
3719 }
3720 
UV_OT_select_overlap(wmOperatorType * ot)3721 void UV_OT_select_overlap(wmOperatorType *ot)
3722 {
3723   /* identifiers */
3724   ot->name = "Select Overlap";
3725   ot->description = "Select all UV faces which overlap each other";
3726   ot->idname = "UV_OT_select_overlap";
3727   ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
3728 
3729   /* api callbacks */
3730   ot->exec = uv_select_overlap_exec;
3731   ot->poll = ED_operator_uvedit;
3732 
3733   /* properties */
3734   RNA_def_boolean(ot->srna,
3735                   "extend",
3736                   0,
3737                   "Extend",
3738                   "Extend selection rather than clearing the existing selection");
3739 }
3740 
3741 /** \} */
3742 
3743 /* -------------------------------------------------------------------- */
3744 /** \name Selected Elements as Arrays (Vertex, Edge & Faces)
3745  *
3746  * These functions return single elements per connected vertex/edge.
3747  * So an edge that has two connected edge loops only assigns one loop in the array.
3748  * \{ */
3749 
ED_uvedit_selected_faces(Scene * scene,BMesh * bm,int len_max,int * r_faces_len)3750 BMFace **ED_uvedit_selected_faces(Scene *scene, BMesh *bm, int len_max, int *r_faces_len)
3751 {
3752   const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
3753   CLAMP_MAX(len_max, bm->totface);
3754   int faces_len = 0;
3755   BMFace **faces = MEM_mallocN(sizeof(*faces) * len_max, __func__);
3756 
3757   BMIter iter;
3758   BMFace *f;
3759   BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
3760     if (uvedit_face_visible_test(scene, f)) {
3761       if (uvedit_face_select_test(scene, f, cd_loop_uv_offset)) {
3762         faces[faces_len++] = f;
3763         if (faces_len == len_max) {
3764           goto finally;
3765         }
3766       }
3767     }
3768   }
3769 
3770 finally:
3771   *r_faces_len = faces_len;
3772   if (faces_len != len_max) {
3773     faces = MEM_reallocN(faces, sizeof(*faces) * faces_len);
3774   }
3775   return faces;
3776 }
3777 
ED_uvedit_selected_edges(Scene * scene,BMesh * bm,int len_max,int * r_edges_len)3778 BMLoop **ED_uvedit_selected_edges(Scene *scene, BMesh *bm, int len_max, int *r_edges_len)
3779 {
3780   const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
3781   CLAMP_MAX(len_max, bm->totloop);
3782   int edges_len = 0;
3783   BMLoop **edges = MEM_mallocN(sizeof(*edges) * len_max, __func__);
3784 
3785   BMIter iter;
3786   BMFace *f;
3787 
3788   /* Clear tag. */
3789   BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
3790     BMIter liter;
3791     BMLoop *l_iter;
3792     BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
3793       BM_elem_flag_disable(l_iter, BM_ELEM_TAG);
3794     }
3795   }
3796 
3797   BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
3798     if (uvedit_face_visible_test(scene, f)) {
3799       BMIter liter;
3800       BMLoop *l_iter;
3801       BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
3802         if (!BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
3803           const MLoopUV *luv_curr = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
3804           const MLoopUV *luv_next = BM_ELEM_CD_GET_VOID_P(l_iter->next, cd_loop_uv_offset);
3805           if ((luv_curr->flag & MLOOPUV_VERTSEL) && (luv_next->flag & MLOOPUV_VERTSEL)) {
3806             BM_elem_flag_enable(l_iter, BM_ELEM_TAG);
3807 
3808             edges[edges_len++] = l_iter;
3809             if (edges_len == len_max) {
3810               goto finally;
3811             }
3812 
3813             /* Tag other connected loops so we don't consider them separate edges. */
3814             if (l_iter != l_iter->radial_next) {
3815               BMLoop *l_radial_iter = l_iter->radial_next;
3816               do {
3817                 if (BM_loop_uv_share_edge_check(l_iter, l_radial_iter, cd_loop_uv_offset)) {
3818                   BM_elem_flag_enable(l_radial_iter, BM_ELEM_TAG);
3819                 }
3820               } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter);
3821             }
3822           }
3823         }
3824       }
3825     }
3826   }
3827 
3828 finally:
3829   *r_edges_len = edges_len;
3830   if (edges_len != len_max) {
3831     edges = MEM_reallocN(edges, sizeof(*edges) * edges_len);
3832   }
3833   return edges;
3834 }
3835 
ED_uvedit_selected_verts(Scene * scene,BMesh * bm,int len_max,int * r_verts_len)3836 BMLoop **ED_uvedit_selected_verts(Scene *scene, BMesh *bm, int len_max, int *r_verts_len)
3837 {
3838   const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
3839   CLAMP_MAX(len_max, bm->totloop);
3840   int verts_len = 0;
3841   BMLoop **verts = MEM_mallocN(sizeof(*verts) * len_max, __func__);
3842 
3843   BMIter iter;
3844   BMFace *f;
3845 
3846   /* Clear tag. */
3847   BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
3848     BMIter liter;
3849     BMLoop *l_iter;
3850     BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
3851       BM_elem_flag_disable(l_iter, BM_ELEM_TAG);
3852     }
3853   }
3854 
3855   BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
3856     if (uvedit_face_visible_test(scene, f)) {
3857       BMIter liter;
3858       BMLoop *l_iter;
3859       BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
3860         if (!BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
3861           const MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l_iter, cd_loop_uv_offset);
3862           if ((luv->flag & MLOOPUV_VERTSEL)) {
3863             BM_elem_flag_enable(l_iter->v, BM_ELEM_TAG);
3864 
3865             verts[verts_len++] = l_iter;
3866             if (verts_len == len_max) {
3867               goto finally;
3868             }
3869 
3870             /* Tag other connected loops so we don't consider them separate vertices. */
3871             BMIter liter_disk;
3872             BMLoop *l_disk_iter;
3873             BM_ITER_ELEM (l_disk_iter, &liter_disk, l_iter->v, BM_LOOPS_OF_VERT) {
3874               if (BM_loop_uv_share_vert_check(l_iter, l_disk_iter, cd_loop_uv_offset)) {
3875                 BM_elem_flag_enable(l_disk_iter, BM_ELEM_TAG);
3876               }
3877             }
3878           }
3879         }
3880       }
3881     }
3882   }
3883 
3884 finally:
3885   *r_verts_len = verts_len;
3886   if (verts_len != len_max) {
3887     verts = MEM_reallocN(verts, sizeof(*verts) * verts_len);
3888   }
3889   return verts;
3890 }
3891 
3892 /** \} */
3893