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(®ion->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(®ion->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(®ion->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(®ion->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(®ion->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(®ion->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 ®ion->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