Blender V4.3
uvedit_select.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include <cmath>
10#include <cstdlib>
11#include <cstring>
12
13#include "MEM_guardedalloc.h"
14
15#include "DNA_image_types.h"
16#include "DNA_material_types.h"
17#include "DNA_node_types.h"
18#include "DNA_object_types.h"
19#include "DNA_scene_types.h"
20#include "DNA_space_types.h"
21
22#include "BLI_alloca.h"
23#include "BLI_blenlib.h"
24#include "BLI_hash.h"
25#include "BLI_heap.h"
26#include "BLI_kdopbvh.h"
27#include "BLI_kdtree.h"
28#include "BLI_lasso_2d.hh"
29#include "BLI_math_geom.h"
30#include "BLI_math_matrix.h"
31#include "BLI_math_vector.h"
32#include "BLI_memarena.h"
33#include "BLI_polyfill_2d.h"
35#include "BLI_utildefines.h"
36
37#include "BLT_translation.hh"
38
39#include "BKE_context.hh"
40#include "BKE_customdata.hh"
41#include "BKE_editmesh.hh"
42#include "BKE_layer.hh"
43#include "BKE_material.h"
44#include "BKE_mesh.hh"
45#include "BKE_mesh_mapping.hh"
46#include "BKE_report.hh"
47
48#include "DEG_depsgraph.hh"
50
51#include "ED_image.hh"
52#include "ED_mesh.hh"
53#include "ED_screen.hh"
54#include "ED_select_utils.hh"
55#include "ED_uvedit.hh"
56
57#include "RNA_access.hh"
58#include "RNA_define.hh"
59#include "RNA_enum_types.hh"
60
61#include "WM_api.hh"
62#include "WM_types.hh"
63
64#include "UI_view2d.hh"
65
66#include "uvedit_intern.hh"
67
68using blender::Array;
69using blender::int2;
70using blender::Span;
71using blender::Vector;
72
73static void uv_select_all_perform(const Scene *scene, Object *obedit, int action);
74
75static void uv_select_all_perform_multi_ex(const Scene *scene,
76 Span<Object *> objects,
77 int action,
78 const Object *ob_exclude);
79static void uv_select_all_perform_multi(const Scene *scene, Span<Object *> objects, int action);
80
81static void uv_select_flush_from_tag_face(const Scene *scene, Object *obedit, const bool select);
82static void uv_select_flush_from_tag_loop(const Scene *scene, Object *obedit, const bool select);
83static void uv_select_flush_from_loop_edge_flag(const Scene *scene, BMEditMesh *em);
84
85static void uv_select_tag_update_for_object(Depsgraph *depsgraph,
86 const ToolSettings *ts,
87 Object *obedit);
88
101
102/* -------------------------------------------------------------------- */
117
119{
120 BMEditSelection *ese = static_cast<BMEditSelection *>(bm->selected.last);
121 if (ese && ese->prev) {
122 BMEditSelection *ese_prev = ese->prev;
123 if ((ese->htype == BM_VERT) && (ese_prev->htype == BM_FACE)) {
124 /* May be null. */
125 return BM_face_vert_share_loop((BMFace *)ese_prev->ele, (BMVert *)ese->ele);
126 }
127 }
128 return nullptr;
129}
130
139
141{
142 BMEditSelection *ese = static_cast<BMEditSelection *>(bm->selected.last);
143 if (ese && ese->prev) {
144 BMEditSelection *ese_prev = ese->prev;
145 if ((ese->htype == BM_EDGE) && (ese_prev->htype == BM_FACE)) {
146 /* May be null. */
147 return BM_face_edge_share_loop((BMFace *)ese_prev->ele, (BMEdge *)ese->ele);
148 }
149 }
150 return nullptr;
151}
152
155/* -------------------------------------------------------------------- */
160{
161 const ToolSettings *ts = scene->toolsettings;
162 char uv_selectmode = UV_SELECT_VERTEX;
163
164 if (ts->uv_flag & UV_SYNC_SELECTION) {
165 if (ts->selectmode & SCE_SELECT_VERTEX) {
166 uv_selectmode = UV_SELECT_VERTEX;
167 }
168 else if (ts->selectmode & SCE_SELECT_EDGE) {
169 uv_selectmode = UV_SELECT_EDGE;
170 }
171 else if (ts->selectmode & SCE_SELECT_FACE) {
172 uv_selectmode = UV_SELECT_FACE;
173 }
174 }
175 else {
177 uv_selectmode = UV_SELECT_VERTEX;
178 }
179 else if (ts->uv_selectmode & UV_SELECT_EDGE) {
180 uv_selectmode = UV_SELECT_EDGE;
181 }
182 else if (ts->uv_selectmode & UV_SELECT_FACE) {
183 uv_selectmode = UV_SELECT_FACE;
184 }
185 }
186 return uv_selectmode;
187}
188
190{
191 /* bmesh API handles flushing but not on de-select */
192 if (ts->uv_flag & UV_SYNC_SELECTION) {
193 if (ts->selectmode != SCE_SELECT_FACE) {
194 if (select == false) {
196 }
197 else {
198 if (ts->selectmode & SCE_SELECT_VERTEX) {
200 }
201 else {
202 /* Use instead of #EDBM_select_flush so selecting edges doesn't
203 * flush vertex to face selection, see: #117320. */
205 }
206 }
207 }
208
209 if (select == false) {
211 }
212 }
213}
214
216 Scene *scene,
217 bool select,
218 const BMUVOffsets offsets)
219{
220 BMFace *efa;
221 BMLoop *l;
222 BMIter iter, liter;
223
224 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
225 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
227 uvedit_uv_select_set(scene, em->bm, l, select, false, offsets);
228 }
229 }
230 }
231}
232
234{
235 if (ts->uv_flag & UV_SYNC_SELECTION) {
236 return (BM_elem_flag_test(efa, BM_ELEM_HIDDEN) == 0);
237 }
239}
240bool uvedit_face_visible_test(const Scene *scene, BMFace *efa)
241{
242 return uvedit_face_visible_test_ex(scene->toolsettings, efa);
243}
244
245bool uvedit_face_select_test_ex(const ToolSettings *ts, BMFace *efa, const BMUVOffsets offsets)
246{
247 BLI_assert(offsets.select_vert >= 0);
248 BLI_assert(offsets.select_edge >= 0);
249 if (ts->uv_flag & UV_SYNC_SELECTION) {
251 }
252
253 BMLoop *l;
254 BMIter liter;
255
256 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
258 if (!BM_ELEM_CD_GET_BOOL(l, offsets.select_vert)) {
259 return false;
260 }
261 }
262 else {
263 if (!BM_ELEM_CD_GET_BOOL(l, offsets.select_edge)) {
264 return false;
265 }
266 }
267 }
268 return true;
269}
270bool uvedit_face_select_test(const Scene *scene, BMFace *efa, const BMUVOffsets offsets)
271{
272 return uvedit_face_select_test_ex(scene->toolsettings, efa, offsets);
273}
274
276 BMEditMesh *em,
277 BMFace *efa,
278 const bool select,
279 const bool do_history,
280 const BMUVOffsets offsets)
281{
282 const ToolSettings *ts = scene->toolsettings;
283 const char sticky = ts->uv_sticky;
284 if (ts->uv_flag & UV_SYNC_SELECTION) {
285 uvedit_face_select_set(scene, em->bm, efa, select, do_history, offsets);
286 return;
287 }
288 if (!uvedit_face_visible_test(scene, efa)) {
289 return;
290 }
291 /* NOTE: Previously face selections done in sticky vertex mode selected stray UV vertices
292 * (not part of any face selections). This now uses the sticky location mode logic instead. */
293 switch (sticky) {
294 case SI_STICKY_DISABLE: {
295 uvedit_face_select_set(scene, em->bm, efa, select, do_history, offsets);
296 break;
297 }
298 default: {
299 /* SI_STICKY_LOC and SI_STICKY_VERTEX modes. */
300 uvedit_face_select_shared_vert(scene, em, efa, select, do_history, offsets);
301 }
302 }
303}
304
306 BMEditMesh *em,
307 BMFace *efa,
308 const bool select,
309 const bool do_history,
310 const BMUVOffsets offsets)
311{
312 BMLoop *l;
313 BMIter liter;
314
315 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
316 if (select) {
317 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, true);
319 }
320 else {
321 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, false);
322 if (!uvedit_vert_is_face_select_any_other(scene, l, offsets)) {
324 }
325 }
326 }
327}
328
330 BMesh *bm,
331 BMFace *efa,
332 const bool select,
333 const bool do_history,
334 const BMUVOffsets offsets)
335{
336 if (select) {
337 uvedit_face_select_enable(scene, bm, efa, do_history, offsets);
338 }
339 else {
340 uvedit_face_select_disable(scene, bm, efa, offsets);
341 }
342}
343
345 const Scene *scene, BMesh *bm, BMFace *efa, const bool do_history, const BMUVOffsets offsets)
346{
347 BLI_assert(offsets.select_vert >= 0);
348 BLI_assert(offsets.select_edge >= 0);
349 const ToolSettings *ts = scene->toolsettings;
350
351 if (ts->uv_flag & UV_SYNC_SELECTION) {
352 BM_face_select_set(bm, efa, true);
353 if (do_history) {
355 }
356 }
357 else {
358 BMLoop *l;
359 BMIter liter;
360
361 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
362 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, true);
363 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, true);
364 }
365 }
366}
367
369 BMesh *bm,
370 BMFace *efa,
371 const BMUVOffsets offsets)
372{
373 BLI_assert(offsets.select_vert >= 0);
374 BLI_assert(offsets.select_edge >= 0);
375 const ToolSettings *ts = scene->toolsettings;
376
377 if (ts->uv_flag & UV_SYNC_SELECTION) {
378 BM_face_select_set(bm, efa, false);
379 }
380 else {
381 BMLoop *l;
382 BMIter liter;
383
384 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
385 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, false);
386 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, false);
387 }
388 }
389}
390
391bool uvedit_edge_select_test_ex(const ToolSettings *ts, const BMLoop *l, const BMUVOffsets offsets)
392{
393 BLI_assert(offsets.select_vert >= 0);
394 BLI_assert(offsets.select_edge >= 0);
395 if (ts->uv_flag & UV_SYNC_SELECTION) {
396 if (ts->selectmode & SCE_SELECT_FACE) {
398 }
399 if (ts->selectmode == SCE_SELECT_EDGE) {
401 }
404 }
405
407 return BM_ELEM_CD_GET_BOOL(l, offsets.select_vert) &&
409 }
410 return BM_ELEM_CD_GET_BOOL(l, offsets.select_edge);
411}
412
413bool uvedit_edge_select_test(const Scene *scene, BMLoop *l, const BMUVOffsets offsets)
414{
415 return uvedit_edge_select_test_ex(scene->toolsettings, l, offsets);
416}
417
419 BMEditMesh *em,
420 BMLoop *l,
421 const bool select,
422 const bool do_history,
423 const BMUVOffsets offsets)
424{
425 const ToolSettings *ts = scene->toolsettings;
426 if (ts->uv_flag & UV_SYNC_SELECTION) {
427 uvedit_edge_select_set(scene, em->bm, l, select, do_history, offsets);
428 return;
429 }
430
431 const int sticky = ts->uv_sticky;
432 switch (sticky) {
433 case SI_STICKY_DISABLE: {
434 if (uvedit_face_visible_test(scene, l->f)) {
435 uvedit_edge_select_set(scene, em->bm, l, select, do_history, offsets);
436 }
437 break;
438 }
439 case SI_STICKY_VERTEX: {
441 break;
442 }
443 default: {
444 /* SI_STICKY_LOC (Fallback) */
446 break;
447 }
448 }
449}
450
452 BMEditMesh *em,
453 BMLoop *l,
454 const bool select,
455 const int sticky_flag,
456 const bool do_history,
457 const BMUVOffsets offsets)
458{
460 /* Set edge flags. Rely on this for face visibility checks */
461 uvedit_edge_select_set_noflush(scene, l, select, sticky_flag, offsets);
462
463 /* Vert selections. */
464 BMLoop *l_iter = l;
465 do {
466 if (select && BM_ELEM_CD_GET_BOOL(l_iter, offsets.select_edge)) {
467 uvedit_uv_select_shared_vert(scene, em, l_iter, true, SI_STICKY_LOC, do_history, offsets);
469 scene, em, l_iter->next, true, SI_STICKY_LOC, do_history, offsets);
470 }
471
472 else if (!select && !BM_ELEM_CD_GET_BOOL(l_iter, offsets.select_edge)) {
473 if (!uvedit_vert_is_edge_select_any_other(scene, l, offsets)) {
474 uvedit_uv_select_shared_vert(scene, em, l_iter, false, SI_STICKY_LOC, do_history, offsets);
475 }
476 if (!uvedit_vert_is_edge_select_any_other(scene, l->next, offsets)) {
478 scene, em, l_iter->next, false, SI_STICKY_LOC, do_history, offsets);
479 }
480 }
481 } while (((l_iter = l_iter->radial_next) != l) && (sticky_flag != SI_STICKY_LOC));
482}
483
485 BMLoop *l,
486 const bool select,
487 const int sticky_flag,
488 const BMUVOffsets offsets)
489{
490 BLI_assert(offsets.uv >= 0);
491 BLI_assert(offsets.select_edge >= 0);
492 BMLoop *l_iter = l;
493 do {
494 if (uvedit_face_visible_test(scene, l_iter->f)) {
495 if ((sticky_flag == SI_STICKY_VERTEX) || BM_loop_uv_share_edge_check(l, l_iter, offsets.uv))
496 {
497 BM_ELEM_CD_SET_BOOL(l_iter, offsets.select_edge, select);
498 }
499 }
500 } while (((l_iter = l_iter->radial_next) != l) && (sticky_flag != SI_STICKY_DISABLE));
501}
502
504 BMesh *bm,
505 BMLoop *l,
506 const bool select,
507 const bool do_history,
508 const BMUVOffsets offsets)
509{
510 if (select) {
511 uvedit_edge_select_enable(scene, bm, l, do_history, offsets);
512 }
513 else {
514 uvedit_edge_select_disable(scene, bm, l, offsets);
515 }
516}
517
519 const Scene *scene, BMesh *bm, BMLoop *l, const bool do_history, const BMUVOffsets offsets)
520
521{
522 const ToolSettings *ts = scene->toolsettings;
523 BLI_assert(offsets.select_vert >= 0);
524 BLI_assert(offsets.select_edge >= 0);
525
526 if (ts->uv_flag & UV_SYNC_SELECTION) {
527 if (ts->selectmode & SCE_SELECT_FACE) {
528 BM_face_select_set(bm, l->f, true);
529 }
530 else if (ts->selectmode & SCE_SELECT_EDGE) {
531 BM_edge_select_set(bm, l->e, true);
532 }
533 else {
534 BM_vert_select_set(bm, l->e->v1, true);
535 BM_vert_select_set(bm, l->e->v2, true);
536 }
537
538 if (do_history) {
540 }
541 }
542 else {
543 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, true);
544 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, true);
545 BM_ELEM_CD_SET_BOOL(l->next, offsets.select_vert, true);
546 }
547}
548
550 BMesh *bm,
551 BMLoop *l,
552 const BMUVOffsets offsets)
553{
554 const ToolSettings *ts = scene->toolsettings;
555 BLI_assert(offsets.select_vert >= 0);
556 BLI_assert(offsets.select_edge >= 0);
557
558 if (ts->uv_flag & UV_SYNC_SELECTION) {
559 if (ts->selectmode & SCE_SELECT_FACE) {
560 BM_face_select_set(bm, l->f, false);
561 }
562 else if (ts->selectmode & SCE_SELECT_EDGE) {
563 BM_edge_select_set(bm, l->e, false);
564 }
565 else {
566 BM_vert_select_set(bm, l->e->v1, false);
567 BM_vert_select_set(bm, l->e->v2, false);
568 }
569 }
570 else {
571 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, false);
572 if ((ts->uv_selectmode & UV_SELECT_VERTEX) == 0) {
573 /* Deselect UV vertex if not part of another edge selection */
574 if (!BM_ELEM_CD_GET_BOOL(l->next, offsets.select_edge)) {
575 BM_ELEM_CD_SET_BOOL(l->next, offsets.select_vert, false);
576 }
577 if (!BM_ELEM_CD_GET_BOOL(l->prev, offsets.select_edge)) {
578 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, false);
579 }
580 }
581 else {
582 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, false);
583 BM_ELEM_CD_SET_BOOL(l->next, offsets.select_vert, false);
584 }
585 }
586}
587
588bool uvedit_uv_select_test_ex(const ToolSettings *ts, const BMLoop *l, const BMUVOffsets offsets)
589{
590 BLI_assert(offsets.select_vert >= 0);
591 if (ts->uv_flag & UV_SYNC_SELECTION) {
592 if (ts->selectmode & SCE_SELECT_FACE) {
594 }
595 if (ts->selectmode & SCE_SELECT_EDGE) {
596 /* Are you looking for `uvedit_edge_select_test(...)` instead? */
597 }
599 }
600
601 if (ts->selectmode & SCE_SELECT_FACE) {
602 /* Are you looking for `uvedit_face_select_test(...)` instead? */
603 }
604
605 if (ts->selectmode & SCE_SELECT_EDGE) {
606 /* Are you looking for `uvedit_edge_select_test(...)` instead? */
607 }
608
609 return BM_ELEM_CD_GET_BOOL(l, offsets.select_vert);
610}
611
612bool uvedit_uv_select_test(const Scene *scene, BMLoop *l, const BMUVOffsets offsets)
613{
614 return uvedit_uv_select_test_ex(scene->toolsettings, l, offsets);
615}
616
618 BMEditMesh *em,
619 BMLoop *l,
620 const bool select,
621 const bool do_history,
622 const BMUVOffsets offsets)
623{
624 const ToolSettings *ts = scene->toolsettings;
625 if (ts->uv_flag & UV_SYNC_SELECTION) {
626 uvedit_uv_select_set(scene, em->bm, l, select, do_history, offsets);
627 return;
628 }
629
630 const int sticky = ts->uv_sticky;
631 switch (sticky) {
632 case SI_STICKY_DISABLE: {
633 if (uvedit_face_visible_test(scene, l->f)) {
634 uvedit_uv_select_set(scene, em->bm, l, select, do_history, offsets);
635 }
636 break;
637 }
638 case SI_STICKY_VERTEX: {
640 break;
641 }
642 default: {
643 /* SI_STICKY_LOC. */
645 break;
646 }
647 }
648}
649
651 BMEditMesh *em,
652 BMLoop *l,
653 const bool select,
654 const int sticky_flag,
655 const bool do_history,
656 const BMUVOffsets offsets)
657{
659 BLI_assert(offsets.uv >= 0);
660
661 BMEdge *e_first, *e_iter;
662 e_first = e_iter = l->e;
663 do {
664 BMLoop *l_radial_iter = e_iter->l;
665 if (!l_radial_iter) {
666 continue; /* Skip wire edges with no loops. */
667 }
668 do {
669 if (l_radial_iter->v == l->v) {
670 if (uvedit_face_visible_test(scene, l_radial_iter->f)) {
671 bool do_select = false;
672 if (sticky_flag == SI_STICKY_VERTEX) {
673 do_select = true;
674 }
675 else if (BM_loop_uv_share_vert_check(l, l_radial_iter, offsets.uv)) {
676 do_select = true;
677 }
678
679 if (do_select) {
680 uvedit_uv_select_set(scene, em->bm, l_radial_iter, select, do_history, offsets);
681 }
682 }
683 }
684 } while ((l_radial_iter = l_radial_iter->radial_next) != e_iter->l);
685 } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, l->v)) != e_first);
686}
687
688void uvedit_uv_select_set(const Scene *scene,
689 BMesh *bm,
690 BMLoop *l,
691 const bool select,
692 const bool do_history,
693 const BMUVOffsets offsets)
694{
695 if (select) {
696 uvedit_uv_select_enable(scene, bm, l, do_history, offsets);
697 }
698 else {
699 uvedit_uv_select_disable(scene, bm, l, offsets);
700 }
701}
702
704 const Scene *scene, BMesh *bm, BMLoop *l, const bool do_history, const BMUVOffsets offsets)
705{
706 const ToolSettings *ts = scene->toolsettings;
707 BLI_assert(offsets.select_vert >= 0);
708
709 if (ts->selectmode & SCE_SELECT_EDGE) {
710 /* Are you looking for `uvedit_edge_select_set(...)` instead? */
711 }
712
713 if (ts->uv_flag & UV_SYNC_SELECTION) {
714 if (ts->selectmode & SCE_SELECT_FACE) {
715 BM_face_select_set(bm, l->f, true);
716 }
717 else {
718 BM_vert_select_set(bm, l->v, true);
719 }
720
721 if (do_history) {
723 }
724 }
725 else {
726 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, true);
727 }
728}
729
730void uvedit_uv_select_disable(const Scene *scene, BMesh *bm, BMLoop *l, const BMUVOffsets offsets)
731{
732 const ToolSettings *ts = scene->toolsettings;
733 BLI_assert(offsets.select_vert >= 0);
734
735 if (ts->uv_flag & UV_SYNC_SELECTION) {
736 if (ts->selectmode & SCE_SELECT_FACE) {
737 BM_face_select_set(bm, l->f, false);
738 }
739 else {
740 BM_vert_select_set(bm, l->v, false);
741 }
742 }
743 else {
744 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, false);
745 }
746}
747
749 BMLoop *l_src,
750 const BMUVOffsets offsets)
751{
752 BLI_assert(offsets.uv >= 0);
753 BMLoop *l_other = nullptr;
754 BMLoop *l_iter = l_src->radial_next;
755 if (l_iter != l_src) {
756 do {
757 if (uvedit_face_visible_test(scene, l_iter->f) &&
758 BM_loop_uv_share_edge_check(l_src, l_iter, offsets.uv))
759 {
760 /* Check UVs are contiguous. */
761 if (l_other == nullptr) {
762 l_other = l_iter;
763 }
764 else {
765 /* Only use when there is a single alternative. */
766 l_other = nullptr;
767 break;
768 }
769 }
770 } while ((l_iter = l_iter->radial_next) != l_src);
771 }
772 return l_other;
773}
774
776 BMLoop *l_edge,
777 BMVert *v_pivot,
778 const BMUVOffsets offsets)
779{
781 nullptr);
782
783 BMLoop *l_step = l_edge;
784 l_step = (l_step->v == v_pivot) ? l_step->prev : l_step->next;
785 BMLoop *l_step_last = nullptr;
786 do {
787 BLI_assert(BM_vert_in_edge(l_step->e, v_pivot));
788 l_step_last = l_step;
789 l_step = uvedit_loop_find_other_radial_loop_with_visible_face(scene, l_step, offsets);
790 if (l_step) {
791 l_step = (l_step->v == v_pivot) ? l_step->prev : l_step->next;
792 }
793 } while (l_step != nullptr);
794
795 if (l_step_last != nullptr) {
797 nullptr);
798 }
799
800 return l_step_last;
801}
802
805/* -------------------------------------------------------------------- */
809UvNearestHit uv_nearest_hit_init_dist_px(const View2D *v2d, const float dist_px)
810{
811 UvNearestHit hit = {nullptr};
812 hit.dist_sq = square_f(U.pixelsize * dist_px);
813 hit.scale[0] = UI_view2d_scale_get_x(v2d);
814 hit.scale[1] = UI_view2d_scale_get_y(v2d);
815 return hit;
816}
817
819{
820 UvNearestHit hit = {nullptr};
821 hit.dist_sq = FLT_MAX;
822 hit.scale[0] = UI_view2d_scale_get_x(v2d);
823 hit.scale[1] = UI_view2d_scale_get_y(v2d);
824 return hit;
825}
826
828{
829 UvNearestHit hit = {nullptr};
830 hit.dist_sq = FLT_MAX;
831 hit.scale[0] = 1.0f;
832 hit.scale[1] = 1.0f;
833 return hit;
834}
835
837 Scene *scene, Object *obedit, const float co[2], const float penalty, UvNearestHit *hit)
838{
839 BLI_assert((hit->scale[0] > 0.0f) && (hit->scale[1] > 0.0f));
841 BMFace *efa;
842 BMLoop *l;
843 BMIter iter, liter;
844 float *luv, *luv_next;
845 int i;
846 bool found = false;
847
848 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
849 BLI_assert(offsets.uv >= 0);
850
852
853 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
854 if (!uvedit_face_visible_test(scene, efa)) {
855 continue;
856 }
857 BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
858 luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
859 luv_next = BM_ELEM_CD_GET_FLOAT_P(l->next, offsets.uv);
860
861 float delta[2];
862 closest_to_line_segment_v2(delta, co, luv, luv_next);
863
864 sub_v2_v2(delta, co);
865 mul_v2_v2(delta, hit->scale);
866
867 float dist_test_sq = len_squared_v2(delta);
868
869 /* Ensures that successive selection attempts will select other edges sharing the same
870 * UV coordinates as the previous selection. */
871 if ((penalty != 0.0f) && uvedit_edge_select_test(scene, l, offsets)) {
872 dist_test_sq = square_f(sqrtf(dist_test_sq) + penalty);
873 }
874 if (dist_test_sq < hit->dist_sq) {
875 hit->ob = obedit;
876 hit->efa = efa;
877
878 hit->l = l;
879
880 hit->dist_sq = dist_test_sq;
881 found = true;
882 }
883 }
884 }
885 return found;
886}
887
889 const Span<Object *> objects,
890 const float co[2],
891 const float penalty,
892 UvNearestHit *hit)
893{
894 bool found = false;
895 for (Object *obedit : objects) {
896 if (uv_find_nearest_edge(scene, obedit, co, penalty, hit)) {
897 found = true;
898 }
899 }
900 return found;
901}
902
904 Scene *scene, Object *obedit, const float co[2], UvNearestHit *hit, const bool only_in_face)
905{
906 BLI_assert((hit->scale[0] > 0.0f) && (hit->scale[1] > 0.0f));
908 bool found = false;
909
910 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_PROP_FLOAT2);
911
912 BMIter iter;
913 BMFace *efa;
914
915 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
916 if (!uvedit_face_visible_test(scene, efa)) {
917 continue;
918 }
919
920 float cent[2];
921 BM_face_uv_calc_center_median(efa, cd_loop_uv_offset, cent);
922
923 float delta[2];
924 sub_v2_v2v2(delta, co, cent);
925 mul_v2_v2(delta, hit->scale);
926
927 const float dist_test_sq = len_squared_v2(delta);
928
929 if (dist_test_sq < hit->dist_sq) {
930
931 if (only_in_face) {
932 if (!BM_face_uv_point_inside_test(efa, co, cd_loop_uv_offset)) {
933 continue;
934 }
935 }
936
937 hit->ob = obedit;
938 hit->efa = efa;
939 hit->dist_sq = dist_test_sq;
940 found = true;
941 }
942 }
943 return found;
944}
945
946bool uv_find_nearest_face(Scene *scene, Object *obedit, const float co[2], UvNearestHit *hit)
947{
948 return uv_find_nearest_face_ex(scene, obedit, co, hit, false);
949}
950
952 const Span<Object *> objects,
953 const float co[2],
954 UvNearestHit *hit,
955 const bool only_in_face)
956{
957 bool found = false;
958 for (Object *obedit : objects) {
959 if (uv_find_nearest_face_ex(scene, obedit, co, hit, only_in_face)) {
960 found = true;
961 }
962 }
963 return found;
964}
965
967 const Span<Object *> objects,
968 const float co[2],
969 UvNearestHit *hit)
970{
971 return uv_find_nearest_face_multi_ex(scene, objects, co, hit, false);
972}
973
974static bool uv_nearest_between(const BMLoop *l, const float co[2], const int cd_loop_uv_offset)
975{
976 const float *uv_prev = BM_ELEM_CD_GET_FLOAT_P(l->prev, cd_loop_uv_offset);
977 const float *uv_curr = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
978 const float *uv_next = BM_ELEM_CD_GET_FLOAT_P(l->next, cd_loop_uv_offset);
979
980 return ((line_point_side_v2(uv_prev, uv_curr, co) > 0.0f) &&
981 (line_point_side_v2(uv_next, uv_curr, co) <= 0.0f));
982}
983
985 Scene *scene, Object *obedit, float const co[2], const float penalty_dist, UvNearestHit *hit)
986{
987 BLI_assert((hit->scale[0] > 0.0f) && (hit->scale[1] > 0.0f));
988 bool found = false;
989
991 BMFace *efa;
992 BMIter iter;
993
995
996 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
997 BLI_assert(offsets.uv >= 0);
998
999 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1000 if (!uvedit_face_visible_test(scene, efa)) {
1001 continue;
1002 }
1003
1004 BMIter liter;
1005 BMLoop *l;
1006 int i;
1007 BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
1008 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
1009
1010 float delta[2];
1011
1012 sub_v2_v2v2(delta, co, luv);
1013 mul_v2_v2(delta, hit->scale);
1014
1015 float dist_test_sq = len_squared_v2(delta);
1016
1017 /* Ensures that successive selection attempts will select other vertices sharing the same
1018 * UV coordinates */
1019 if ((penalty_dist != 0.0f) && uvedit_uv_select_test(scene, l, offsets)) {
1020 dist_test_sq = square_f(sqrtf(dist_test_sq) + penalty_dist);
1021 }
1022
1023 if (dist_test_sq <= hit->dist_sq) {
1024 if (dist_test_sq == hit->dist_sq) {
1025 if (!uv_nearest_between(l, co, offsets.uv)) {
1026 continue;
1027 }
1028 }
1029
1030 hit->dist_sq = dist_test_sq;
1031
1032 hit->ob = obedit;
1033 hit->efa = efa;
1034 hit->l = l;
1035 found = true;
1036 }
1037 }
1038 }
1039
1040 return found;
1041}
1042
1044 const Span<Object *> objects,
1045 float const co[2],
1046 const float penalty_dist,
1047 UvNearestHit *hit)
1048{
1049 bool found = false;
1050 for (Object *obedit : objects) {
1051 if (uv_find_nearest_vert(scene, obedit, co, penalty_dist, hit)) {
1052 found = true;
1053 }
1054 }
1055 return found;
1056}
1057
1058static bool uvedit_nearest_uv(const Scene *scene,
1059 Object *obedit,
1060 const float co[2],
1061 const float scale[2],
1062 const bool ignore_selected,
1063 float *dist_sq,
1064 float r_uv[2])
1065{
1067 BMIter iter;
1068 BMFace *efa;
1069 const float *uv_best = nullptr;
1070 float dist_best = *dist_sq;
1071 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
1072 BLI_assert(offsets.uv >= 0);
1073 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1074 if (!uvedit_face_visible_test(scene, efa)) {
1075 continue;
1076 }
1077 BMLoop *l_iter, *l_first;
1078 l_iter = l_first = BM_FACE_FIRST_LOOP(efa);
1079 do {
1080 if (ignore_selected && uvedit_uv_select_test(scene, l_iter, offsets)) {
1081 continue;
1082 }
1083
1084 const float *uv = BM_ELEM_CD_GET_FLOAT_P(l_iter, offsets.uv);
1085 float co_tmp[2];
1086 mul_v2_v2v2(co_tmp, scale, uv);
1087 const float dist_test = len_squared_v2v2(co, co_tmp);
1088 if (dist_best > dist_test) {
1089 dist_best = dist_test;
1090 uv_best = uv;
1091 }
1092 } while ((l_iter = l_iter->next) != l_first);
1093 }
1094
1095 if (uv_best != nullptr) {
1096 copy_v2_v2(r_uv, uv_best);
1097 *dist_sq = dist_best;
1098 return true;
1099 }
1100 return false;
1101}
1102
1104 const Scene *scene,
1105 const Span<Object *> objects,
1106 const float mval_fl[2],
1107 const bool ignore_selected,
1108 float *dist_sq,
1109 float r_uv[2])
1110{
1111 bool found = false;
1112
1113 float scale[2], offset[2];
1114 UI_view2d_scale_get(v2d, &scale[0], &scale[1]);
1115 UI_view2d_view_to_region_fl(v2d, 0.0f, 0.0f, &offset[0], &offset[1]);
1116
1117 float co[2];
1118 sub_v2_v2v2(co, mval_fl, offset);
1119
1120 for (Object *obedit : objects) {
1121 if (uvedit_nearest_uv(scene, obedit, co, scale, ignore_selected, dist_sq, r_uv)) {
1122 found = true;
1123 }
1124 }
1125 return found;
1126}
1127
1130/* -------------------------------------------------------------------- */
1138BMLoop *uv_find_nearest_loop_from_vert(Scene *scene, Object *obedit, BMVert *v, const float co[2])
1139{
1141 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_PROP_FLOAT2);
1142
1143 BMIter liter;
1144 BMLoop *l;
1145 BMLoop *l_found = nullptr;
1146 float dist_best_sq = FLT_MAX;
1147
1148 BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
1149 if (!uvedit_face_visible_test(scene, l->f)) {
1150 continue;
1151 }
1152
1153 const float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
1154 const float dist_test_sq = len_squared_v2v2(co, luv);
1155 if (dist_test_sq < dist_best_sq) {
1156 dist_best_sq = dist_test_sq;
1157 l_found = l;
1158 }
1159 }
1160 return l_found;
1161}
1162
1163BMLoop *uv_find_nearest_loop_from_edge(Scene *scene, Object *obedit, BMEdge *e, const float co[2])
1164{
1166 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_PROP_FLOAT2);
1167
1168 BMIter eiter;
1169 BMLoop *l;
1170 BMLoop *l_found = nullptr;
1171 float dist_best_sq = FLT_MAX;
1172
1173 BM_ITER_ELEM (l, &eiter, e, BM_LOOPS_OF_EDGE) {
1174 if (!uvedit_face_visible_test(scene, l->f)) {
1175 continue;
1176 }
1177 const float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
1178 const float *luv_next = BM_ELEM_CD_GET_FLOAT_P(l->next, cd_loop_uv_offset);
1179 const float dist_test_sq = dist_squared_to_line_segment_v2(co, luv, luv_next);
1180 if (dist_test_sq < dist_best_sq) {
1181 dist_best_sq = dist_test_sq;
1182 l_found = l;
1183 }
1184 }
1185 return l_found;
1186}
1187
1190/* -------------------------------------------------------------------- */
1195{
1196 BLI_assert(offsets.uv >= 0);
1197 BMEdge *e_iter = l->e;
1198 do {
1199 BMLoop *l_radial_iter = e_iter->l, *l_other;
1200 do {
1201 if (uvedit_face_visible_test(scene, l_radial_iter->f)) {
1202 /* Use #l_other to check if the uvs are connected (share the same uv coordinates)
1203 * and #l_radial_iter for the actual edge selection test. */
1204 l_other = (l_radial_iter->v != l->v) ? l_radial_iter->next : l_radial_iter;
1205 if (BM_loop_uv_share_vert_check(l, l_other, offsets.uv) &&
1206 uvedit_edge_select_test(scene, l_radial_iter, offsets))
1207 {
1208 return true;
1209 }
1210 }
1211 } while ((l_radial_iter = l_radial_iter->radial_next) != e_iter->l);
1212 } while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, l->v)) != l->e);
1213
1214 return false;
1215}
1216
1218{
1219 BLI_assert(offsets.uv >= 0);
1220 BMIter liter;
1221 BMLoop *l_iter;
1222 BM_ITER_ELEM (l_iter, &liter, l->v, BM_LOOPS_OF_VERT) {
1223 if (!uvedit_face_visible_test(scene, l_iter->f) || (l_iter->f == l->f)) {
1224 continue;
1225 }
1226 if (BM_loop_uv_share_vert_check(l, l_iter, offsets.uv) &&
1227 uvedit_face_select_test(scene, l_iter->f, offsets))
1228 {
1229 return true;
1230 }
1231 }
1232 return false;
1233}
1234
1236 BMLoop *l,
1237 const BMUVOffsets offsets)
1238{
1239 BLI_assert(offsets.uv >= 0);
1240 BMIter liter;
1241 BMLoop *l_iter;
1242 BM_ITER_ELEM (l_iter, &liter, l->v, BM_LOOPS_OF_VERT) {
1243 if (!uvedit_face_visible_test(scene, l_iter->f) || (l_iter->f == l->f)) {
1244 continue;
1245 }
1246 if (BM_loop_uv_share_vert_check(l, l_iter, offsets.uv) &&
1247 !uvedit_face_select_test(scene, l_iter->f, offsets))
1248 {
1249 return false;
1250 }
1251 }
1252 return true;
1253}
1254
1255static void bm_clear_uv_vert_selection(const Scene *scene, BMesh *bm, const BMUVOffsets offsets)
1256{
1257 if (offsets.select_vert == -1) {
1258 return;
1259 }
1260 BMFace *efa;
1261 BMLoop *l;
1262 BMIter iter, liter;
1263 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
1264 if (!uvedit_face_visible_test(scene, efa)) {
1265 continue;
1266 }
1267 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1268 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, false);
1269 }
1270 }
1271}
1272
1275/* -------------------------------------------------------------------- */
1281{
1282 const ToolSettings *ts = scene->toolsettings;
1283 const char *active_uv_name = CustomData_get_active_layer_name(&em->bm->ldata, CD_PROP_FLOAT2);
1284 BM_uv_map_ensure_vert_select_attr(em->bm, active_uv_name);
1285 BM_uv_map_ensure_edge_select_attr(em->bm, active_uv_name);
1286 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
1287
1288 BLI_assert((ts->uv_flag & UV_SYNC_SELECTION) == 0);
1290
1291 /* Vertex Mode only. */
1292 if (ts->uv_selectmode & UV_SELECT_VERTEX) {
1293 BMFace *efa;
1294 BMLoop *l;
1295 BMIter iter, liter;
1296 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1297 if (!uvedit_face_visible_test(scene, efa)) {
1298 continue;
1299 }
1300 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1301 bool edge_selected = BM_ELEM_CD_GET_BOOL(l, offsets.select_vert) &&
1303 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, edge_selected);
1304 }
1305 }
1306 }
1307}
1308
1311/* -------------------------------------------------------------------- */
1315void uvedit_select_flush(const Scene *scene, BMEditMesh *em)
1316{
1317 /* Careful when using this in face select mode.
1318 * For face selections with sticky mode enabled, this can create invalid selection states. */
1319 const ToolSettings *ts = scene->toolsettings;
1320 const char *active_uv_name = CustomData_get_active_layer_name(&em->bm->ldata, CD_PROP_FLOAT2);
1321 BM_uv_map_ensure_edge_select_attr(em->bm, active_uv_name);
1322 BM_uv_map_ensure_vert_select_attr(em->bm, active_uv_name);
1323 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
1324
1325 BLI_assert((ts->uv_flag & UV_SYNC_SELECTION) == 0);
1327
1328 BMFace *efa;
1329 BMLoop *l;
1330 BMIter iter, liter;
1331 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1332 if (!uvedit_face_visible_test(scene, efa)) {
1333 continue;
1334 }
1335 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1336 if (BM_ELEM_CD_GET_BOOL(l, offsets.select_vert) &&
1338 {
1339 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, true);
1340 }
1341 }
1342 }
1343}
1344
1346{
1347 const ToolSettings *ts = scene->toolsettings;
1348 const char *active_uv_name = CustomData_get_active_layer_name(&em->bm->ldata, CD_PROP_FLOAT2);
1349 BM_uv_map_ensure_edge_select_attr(em->bm, active_uv_name);
1350 BM_uv_map_ensure_vert_select_attr(em->bm, active_uv_name);
1351 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
1352
1353 BLI_assert((ts->uv_flag & UV_SYNC_SELECTION) == 0);
1355
1356 BMFace *efa;
1357 BMLoop *l;
1358 BMIter iter, liter;
1359 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
1360 if (!uvedit_face_visible_test(scene, efa)) {
1361 continue;
1362 }
1363 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1364 if (!BM_ELEM_CD_GET_BOOL(l, offsets.select_vert) ||
1366 {
1367 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, false);
1368 }
1369 }
1370 }
1371}
1372
1375/* -------------------------------------------------------------------- */
1386
1388 BMLoop *l_step,
1389 BMVert *v_from,
1390 const BMUVOffsets offsets)
1391{
1392 if (l_step->f->len == 4) {
1393 BMVert *v_from_next = BM_edge_other_vert(l_step->e, v_from);
1394 BMLoop *l_step_over = (v_from == l_step->v) ? l_step->next : l_step->prev;
1396 scene, l_step_over, offsets);
1397 if (l_step_over) {
1398 return (l_step_over->v == v_from_next) ? l_step_over->prev : l_step_over->next;
1399 }
1400 }
1401 return nullptr;
1402}
1403
1405 BMLoop *l_step,
1406 BMVert *v_from,
1407 const BMUVOffsets offsets)
1408{
1409 BMVert *v_from_next = BM_edge_other_vert(l_step->e, v_from);
1411 scene, l_step, v_from_next, offsets);
1412}
1413
1414/* TODO(@ideasman42): support this in the BMesh API, as we have for clearing other types. */
1416{
1417 BMIter iter;
1418 BMFace *f;
1419 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
1420 BMIter liter;
1421 BMLoop *l_iter;
1422 BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
1424 }
1425 }
1426}
1427
1432 BMEditMesh *em,
1433 BMLoop *l_init_pair[2],
1434 const BMUVOffsets offsets)
1435{
1437
1438 for (int side = 0; side < 2; side++) {
1439 BMLoop *l_step_pair[2] = {l_init_pair[0], l_init_pair[1]};
1440 BMVert *v_from = side ? l_step_pair[0]->e->v1 : l_step_pair[0]->e->v2;
1441 /* Disable since we start from the same edge. */
1442 BM_elem_flag_disable(l_step_pair[0], BM_ELEM_TAG);
1443 BM_elem_flag_disable(l_step_pair[1], BM_ELEM_TAG);
1444 while ((l_step_pair[0] != nullptr) && (l_step_pair[1] != nullptr)) {
1445 if (!uvedit_face_visible_test(scene, l_step_pair[0]->f) ||
1446 !uvedit_face_visible_test(scene, l_step_pair[1]->f) ||
1447 /* Check loops have not diverged. */
1448 (uvedit_loop_find_other_radial_loop_with_visible_face(scene, l_step_pair[0], offsets) !=
1449 l_step_pair[1]))
1450 {
1451 break;
1452 }
1453
1454 BLI_assert(l_step_pair[0]->e == l_step_pair[1]->e);
1455
1456 BM_elem_flag_enable(l_step_pair[0], BM_ELEM_TAG);
1457 BM_elem_flag_enable(l_step_pair[1], BM_ELEM_TAG);
1458
1459 BMVert *v_from_next = BM_edge_other_vert(l_step_pair[0]->e, v_from);
1460 /* Walk over both sides, ensure they keep on the same edge. */
1461 for (int i = 0; i < ARRAY_SIZE(l_step_pair); i++) {
1462 l_step_pair[i] = bm_select_edgeloop_double_side_next(
1463 scene, l_step_pair[i], v_from, offsets);
1464 }
1465
1466 if ((l_step_pair[0] && BM_elem_flag_test(l_step_pair[0], BM_ELEM_TAG)) ||
1467 (l_step_pair[1] && BM_elem_flag_test(l_step_pair[1], BM_ELEM_TAG)))
1468 {
1469 break;
1470 }
1471 v_from = v_from_next;
1472 }
1473 }
1474}
1475
1483 BMEditMesh *em,
1484 BMLoop *l_init,
1485 const BMUVOffsets offsets,
1486 enum eUVEdgeLoopBoundaryMode boundary_mode,
1487 int r_count_by_select[2])
1488{
1489 if (r_count_by_select) {
1490 r_count_by_select[0] = r_count_by_select[1] = 0;
1491 }
1492
1494
1495 for (int side = 0; side < 2; side++) {
1496 BMLoop *l_step = l_init;
1497 BMVert *v_from = side ? l_step->e->v1 : l_step->e->v2;
1498 /* Disable since we start from the same edge. */
1500 while (l_step != nullptr) {
1501
1502 if (!uvedit_face_visible_test(scene, l_step->f) ||
1503 /* Check the boundary is still a boundary. */
1505 nullptr))
1506 {
1507 break;
1508 }
1509
1510 if (r_count_by_select != nullptr) {
1511 r_count_by_select[uvedit_edge_select_test(scene, l_step, offsets)] += 1;
1512 /* Early exit when mixed could be optional if needed. */
1513 if (r_count_by_select[0] && r_count_by_select[1]) {
1514 r_count_by_select[0] = r_count_by_select[1] = -1;
1515 break;
1516 }
1517 }
1518
1520
1521 BMVert *v_from_next = BM_edge_other_vert(l_step->e, v_from);
1522 BMFace *f_step_prev = l_step->f;
1523
1524 l_step = bm_select_edgeloop_single_side_next(scene, l_step, v_from, offsets);
1525
1526 if (l_step && BM_elem_flag_test(l_step, BM_ELEM_TAG)) {
1527 break;
1528 }
1529 if (boundary_mode == UV_EDGE_LOOP_BOUNDARY_LOOP) {
1530 /* Don't allow walking over the face. */
1531 if (f_step_prev == l_step->f) {
1532 break;
1533 }
1534 }
1535 v_from = v_from_next;
1536 }
1537 }
1538}
1539
1540static int uv_select_edgeloop(Scene *scene, Object *obedit, UvNearestHit *hit, const bool extend)
1541{
1542 const ToolSettings *ts = scene->toolsettings;
1544 bool select;
1545
1546 const char *active_uv_name = CustomData_get_active_layer_name(&em->bm->ldata, CD_PROP_FLOAT2);
1547 BM_uv_map_ensure_vert_select_attr(em->bm, active_uv_name);
1548 BM_uv_map_ensure_edge_select_attr(em->bm, active_uv_name);
1549 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
1550
1551 if (extend) {
1552 select = !uvedit_edge_select_test(scene, hit->l, offsets);
1553 }
1554 else {
1555 select = true;
1556 }
1557
1558 BMLoop *l_init_pair[2] = {
1559 hit->l,
1561 };
1562
1563 /* When selecting boundaries, support cycling between selection modes. */
1565
1566 /* Tag all loops that are part of the edge loop (select after).
1567 * This is done so we can */
1568 if (l_init_pair[1] == nullptr) {
1569 int count_by_select[2];
1570 /* If the loops selected toggle the boundaries. */
1572 scene, em, l_init_pair[0], offsets, boundary_mode, count_by_select);
1573 if (count_by_select[!select] == 0) {
1574 boundary_mode = UV_EDGE_LOOP_BOUNDARY_ALL;
1575
1576 /* If the boundary is selected, toggle back to the loop. */
1578 scene, em, l_init_pair[0], offsets, boundary_mode, count_by_select);
1579 if (count_by_select[!select] == 0) {
1580 boundary_mode = UV_EDGE_LOOP_BOUNDARY_LOOP;
1581 }
1582 }
1583 }
1584
1585 if (l_init_pair[1] == nullptr) {
1586 uv_select_edgeloop_single_side_tag(scene, em, l_init_pair[0], offsets, boundary_mode, nullptr);
1587 }
1588 else {
1589 uv_select_edgeloop_double_side_tag(scene, em, l_init_pair, offsets);
1590 }
1591
1592 /* Apply the selection. */
1593 if (!extend) {
1594 uv_select_all_perform(scene, obedit, SEL_DESELECT);
1595 }
1596
1597 /* Select all tagged loops. */
1598 {
1599 BMIter iter;
1600 BMFace *f;
1601 BM_ITER_MESH (f, &iter, em->bm, BM_FACES_OF_MESH) {
1602 BMIter liter;
1603 BMLoop *l_iter;
1604 BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
1605 if (BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
1606 if (ts->uv_selectmode == UV_SELECT_VERTEX) {
1607 uvedit_uv_select_set_with_sticky(scene, em, l_iter, select, false, offsets);
1608 uvedit_uv_select_set_with_sticky(scene, em, l_iter->next, select, false, offsets);
1609 }
1610 else {
1611 uvedit_edge_select_set_with_sticky(scene, em, l_iter, select, false, offsets);
1612 }
1613 }
1614 }
1615 }
1616 }
1617
1618 return select ? 1 : -1;
1619}
1620
1623/* -------------------------------------------------------------------- */
1627static int uv_select_faceloop(Scene *scene, Object *obedit, UvNearestHit *hit, const bool extend)
1628{
1630 bool select;
1631
1632 const char *active_uv_name = CustomData_get_active_layer_name(&em->bm->ldata, CD_PROP_FLOAT2);
1633 BM_uv_map_ensure_vert_select_attr(em->bm, active_uv_name);
1634 BM_uv_map_ensure_edge_select_attr(em->bm, active_uv_name);
1635 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
1636
1637 if (!extend) {
1638 uv_select_all_perform(scene, obedit, SEL_DESELECT);
1639 }
1640
1642
1643 if (extend) {
1644 select = !uvedit_face_select_test(scene, hit->l->f, offsets);
1645 }
1646 else {
1647 select = true;
1648 }
1649
1650 BMLoop *l_pair[2] = {
1651 hit->l,
1653 };
1654
1655 for (int side = 0; side < 2; side++) {
1656 BMLoop *l_step = l_pair[side];
1657 while (l_step) {
1658 if (!uvedit_face_visible_test(scene, l_step->f)) {
1659 break;
1660 }
1661
1662 uvedit_face_select_set_with_sticky(scene, em, l_step->f, select, false, offsets);
1663
1665 if (l_step->f->len == 4) {
1666 BMLoop *l_step_opposite = l_step->next->next;
1668 scene, l_step_opposite, offsets);
1669 }
1670 else {
1671 l_step = nullptr;
1672 }
1673
1674 /* Break iteration when `l_step`:
1675 * - is the first loop where we started from.
1676 * - tagged using #BM_ELEM_TAG (meaning this loop has been visited in this iteration). */
1677 if (l_step && BM_elem_flag_test(l_step->f, BM_ELEM_TAG)) {
1678 break;
1679 }
1680 }
1681 }
1682
1683 return (select) ? 1 : -1;
1684}
1685
1688/* -------------------------------------------------------------------- */
1692static int uv_select_edgering(Scene *scene, Object *obedit, UvNearestHit *hit, const bool extend)
1693{
1694 const ToolSettings *ts = scene->toolsettings;
1696 const bool use_face_select = (ts->uv_flag & UV_SYNC_SELECTION) ?
1697 (ts->selectmode & SCE_SELECT_FACE) :
1699 const bool use_vertex_select = (ts->uv_flag & UV_SYNC_SELECTION) ?
1702 bool select;
1703
1704 const char *active_uv_name = CustomData_get_active_layer_name(&em->bm->ldata, CD_PROP_FLOAT2);
1705 BM_uv_map_ensure_vert_select_attr(em->bm, active_uv_name);
1706 BM_uv_map_ensure_edge_select_attr(em->bm, active_uv_name);
1707 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
1708
1709 if (!extend) {
1710 uv_select_all_perform(scene, obedit, SEL_DESELECT);
1711 }
1712
1714
1715 if (extend) {
1716 select = !uvedit_edge_select_test(scene, hit->l, offsets);
1717 }
1718 else {
1719 select = true;
1720 }
1721
1722 BMLoop *l_pair[2] = {
1723 hit->l,
1725 };
1726
1727 for (int side = 0; side < 2; side++) {
1728 BMLoop *l_step = l_pair[side];
1729 /* Disable since we start from the same edge. */
1731 while (l_step) {
1732 if (!uvedit_face_visible_test(scene, l_step->f)) {
1733 break;
1734 }
1735
1736 if (use_face_select) {
1737 /* While selecting face loops is now done in a separate function #uv_select_faceloop(),
1738 * this check is still kept for edge ring selection, to keep it consistent with how edge
1739 * ring selection works in face mode in the 3D viewport. */
1740 uvedit_face_select_set_with_sticky(scene, em, l_step->f, select, false, offsets);
1741 }
1742 else if (use_vertex_select) {
1743 uvedit_uv_select_set_with_sticky(scene, em, l_step, select, false, offsets);
1744 uvedit_uv_select_set_with_sticky(scene, em, l_step->next, select, false, offsets);
1745 }
1746 else {
1747 /* Edge select mode */
1748 uvedit_edge_select_set_with_sticky(scene, em, l_step, select, false, offsets);
1749 }
1750
1752 if (l_step->f->len == 4) {
1753 BMLoop *l_step_opposite = l_step->next->next;
1755 scene, l_step_opposite, offsets);
1756 if (l_step == nullptr) {
1757 /* Ensure we touch the opposite edge if we can't walk over it. */
1758 l_step = l_step_opposite;
1759 }
1760 }
1761 else {
1762 l_step = nullptr;
1763 }
1764
1765 /* Break iteration when `l_step`:
1766 * - Is the first loop where we started from.
1767 * - Tagged using #BM_ELEM_TAG (meaning this loop has been visited in this iteration).
1768 * - Has its corresponding UV edge selected/unselected based on #select. */
1769 if (l_step && BM_elem_flag_test(l_step->e, BM_ELEM_TAG)) {
1770 /* Previously this check was not done and this resulted in the final edge in the edge ring
1771 * cycle to be skipped during selection (caused by old sticky selection behavior). */
1772 if (select && uvedit_edge_select_test(scene, l_step, offsets)) {
1773 break;
1774 }
1775 if (!select && !uvedit_edge_select_test(scene, l_step, offsets)) {
1776 break;
1777 }
1778 }
1779 }
1780 }
1781
1782 return (select) ? 1 : -1;
1783}
1784
1787/* -------------------------------------------------------------------- */
1792 const Span<Object *> objects,
1793 UvNearestHit *hit,
1794 const bool extend,
1795 bool deselect,
1796 const bool toggle,
1797 const bool select_faces)
1798{
1799 const bool uv_sync_select = (scene->toolsettings->uv_flag & UV_SYNC_SELECTION);
1800
1801 /* loop over objects, or just use hit->ob */
1802 for (const int ob_index : objects.index_range()) {
1803 if (hit && ob_index != 0) {
1804 break;
1805 }
1806 Object *obedit = hit ? hit->ob : objects[ob_index];
1807
1808 BMFace *efa;
1809 BMLoop *l;
1810 BMIter iter, liter;
1811 UvMapVert *vlist, *iterv, *startv;
1812 int i, stacksize = 0, *stack;
1813 uint a;
1814 char *flag;
1815
1817 const char *active_uv_name = CustomData_get_active_layer_name(&em->bm->ldata, CD_PROP_FLOAT2);
1818 BLI_assert(active_uv_name != nullptr);
1819 BM_uv_map_ensure_vert_select_attr(em->bm, active_uv_name);
1820 BM_uv_map_ensure_edge_select_attr(em->bm, active_uv_name);
1821 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
1822
1823 BM_mesh_elem_table_ensure(em->bm, BM_FACE); /* we can use this too */
1824
1825 /* NOTE: we had 'use winding' so we don't consider overlapping islands as connected, see #44320
1826 * this made *every* projection split the island into front/back islands.
1827 * Keep 'use_winding' to false, see: #50970.
1828 *
1829 * Better solve this by having a delimit option for select-linked operator,
1830 * keeping island-select working as is. */
1831 UvVertMap *vmap = BM_uv_vert_map_create(em->bm, !uv_sync_select);
1832 if (vmap == nullptr) {
1833 continue;
1834 }
1835
1836 stack = static_cast<int *>(MEM_mallocN(sizeof(*stack) * (em->bm->totface + 1), "UvLinkStack"));
1837 flag = static_cast<char *>(MEM_callocN(sizeof(*flag) * em->bm->totface, "UvLinkFlag"));
1838
1839 if (hit == nullptr) {
1840 /* Use existing selection */
1841 BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, a) {
1842 if (uvedit_face_visible_test(scene, efa)) {
1843 if (select_faces) {
1845 stack[stacksize] = a;
1846 stacksize++;
1847 flag[a] = 1;
1848 }
1849 }
1850 else {
1851 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1852 if (uvedit_uv_select_test(scene, l, offsets)) {
1853 bool add_to_stack = true;
1854 if (uv_sync_select) {
1855 /* Special case, vertex/edge & sync select being enabled.
1856 *
1857 * Without this, a second linked select will 'grow' each time as each new
1858 * selection reaches the boundaries of islands that share vertices but not UVs.
1859 *
1860 * Rules applied here:
1861 * - This loops face isn't selected.
1862 * - The only other fully selected face is connected or,
1863 * - There are no connected fully selected faces UV-connected to this loop.
1864 */
1865 BLI_assert(!select_faces);
1866 if (uvedit_face_select_test(scene, l->f, offsets)) {
1867 /* pass */
1868 }
1869 else {
1870 BMIter liter_other;
1871 BMLoop *l_other;
1872 BM_ITER_ELEM (l_other, &liter_other, l->v, BM_LOOPS_OF_VERT) {
1873 if ((l != l_other) && !BM_loop_uv_share_vert_check(l, l_other, offsets.uv) &&
1874 uvedit_face_select_test(scene, l_other->f, offsets))
1875 {
1876 add_to_stack = false;
1877 break;
1878 }
1879 }
1880 }
1881 }
1882
1883 if (add_to_stack) {
1884 stack[stacksize] = a;
1885 stacksize++;
1886 flag[a] = 1;
1887 break;
1888 }
1889 }
1890 }
1891 }
1892 }
1893 }
1894 }
1895 else {
1896 BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, a) {
1897 if (efa == hit->efa) {
1898 stack[stacksize] = a;
1899 stacksize++;
1900 flag[a] = 1;
1901 break;
1902 }
1903 }
1904 }
1905
1906 while (stacksize > 0) {
1907
1908 stacksize--;
1909 a = stack[stacksize];
1910
1911 efa = BM_face_at_index(em->bm, a);
1912
1913 BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) {
1914
1915 /* make_uv_vert_map_EM sets verts tmp.l to the indices */
1917
1918 startv = vlist;
1919
1920 for (iterv = vlist; iterv; iterv = iterv->next) {
1921 if (iterv->separate) {
1922 startv = iterv;
1923 }
1924 if (iterv->face_index == a) {
1925 break;
1926 }
1927 }
1928
1929 for (iterv = startv; iterv; iterv = iterv->next) {
1930 if ((startv != iterv) && (iterv->separate)) {
1931 break;
1932 }
1933 if (!flag[iterv->face_index]) {
1934 flag[iterv->face_index] = 1;
1935 stack[stacksize] = iterv->face_index;
1936 stacksize++;
1937 }
1938 }
1939 }
1940 }
1941
1942 /* Toggling - if any of the linked vertices is selected (and visible), we deselect. */
1943 if ((toggle == true) && (extend == false) && (deselect == false)) {
1944 BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, a) {
1945 bool found_selected = false;
1946 if (!flag[a]) {
1947 continue;
1948 }
1949
1950 if (select_faces) {
1952 found_selected = true;
1953 }
1954 }
1955 else {
1956 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
1957 if (uvedit_uv_select_test(scene, l, offsets)) {
1958 found_selected = true;
1959 break;
1960 }
1961 }
1962
1963 if (found_selected) {
1964 deselect = true;
1965 break;
1966 }
1967 }
1968 }
1969 }
1970
1971#define SET_SELECTION(value) \
1972 if (select_faces) { \
1973 BM_face_select_set(em->bm, efa, value); \
1974 } \
1975 else { \
1976 uvedit_face_select_set(scene, em->bm, efa, value, false, offsets); \
1977 } \
1978 (void)0
1979
1980 BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, a) {
1981 if (!flag[a]) {
1982 if (!extend && !deselect && !toggle) {
1983 SET_SELECTION(false);
1984 }
1985 continue;
1986 }
1987
1988 if (!deselect) {
1989 SET_SELECTION(true);
1990 }
1991 else {
1992 SET_SELECTION(false);
1993 }
1994 }
1995
1996#undef SET_SELECTION
1997
1998 MEM_freeN(stack);
1999 MEM_freeN(flag);
2000 BM_uv_vert_map_free(vmap);
2001
2002 if (uv_sync_select) {
2003 if (deselect) {
2005 }
2006 else {
2007 if (!select_faces) {
2009 }
2010 }
2011 }
2012 }
2013}
2014
2016 BMVert *eve,
2017 const BMUVOffsets offsets)
2018{
2019 BMIter liter;
2020 BMLoop *l;
2021
2022 BM_ITER_ELEM (l, &liter, eve, BM_LOOPS_OF_VERT) {
2023 if (!uvedit_face_visible_test(scene, l->f)) {
2024 continue;
2025 }
2026
2027 if (uvedit_uv_select_test(scene, l, offsets)) {
2028 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
2029 return luv;
2030 }
2031 }
2032
2033 return nullptr;
2034}
2035
2038/* -------------------------------------------------------------------- */
2042static int uv_select_more_less(bContext *C, const bool select)
2043{
2044 Scene *scene = CTX_data_scene(C);
2045 ViewLayer *view_layer = CTX_data_view_layer(C);
2046
2047 BMFace *efa;
2048 BMLoop *l;
2049 BMIter iter, liter;
2050 const ToolSettings *ts = scene->toolsettings;
2051
2053 scene, view_layer, nullptr);
2054
2055 const bool is_uv_face_selectmode = (ts->uv_selectmode == UV_SELECT_FACE);
2056
2057 for (Object *obedit : objects) {
2059
2060 bool changed = false;
2061
2062 const char *active_uv_name = CustomData_get_active_layer_name(&em->bm->ldata, CD_PROP_FLOAT2);
2063 BM_uv_map_ensure_vert_select_attr(em->bm, active_uv_name);
2064 BM_uv_map_ensure_edge_select_attr(em->bm, active_uv_name);
2065 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
2066
2067 if (ts->uv_flag & UV_SYNC_SELECTION) {
2068 if (select) {
2069 EDBM_select_more(em, true);
2070 }
2071 else {
2072 EDBM_select_less(em, true);
2073 }
2074
2075 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
2076 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2077 continue;
2078 }
2079
2080 if (is_uv_face_selectmode) {
2081
2082 /* clear tags */
2084
2085 /* mark loops to be selected */
2086 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2087 if (uvedit_face_visible_test(scene, efa)) {
2088
2089 if (select) {
2090#define NEIGHBORING_FACE_IS_SEL 1
2091#define CURR_FACE_IS_UNSEL 2
2092
2093 int sel_state = 0;
2094
2095 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2096 if (BM_ELEM_CD_GET_BOOL(l, offsets.select_vert)) {
2097 sel_state |= NEIGHBORING_FACE_IS_SEL;
2098 }
2099 else {
2100 sel_state |= CURR_FACE_IS_UNSEL;
2101 }
2102
2103 if (!BM_ELEM_CD_GET_BOOL(l, offsets.select_edge)) {
2104 sel_state |= CURR_FACE_IS_UNSEL;
2105 }
2106
2107 /* If the current face is not selected and at least one neighboring face is
2108 * selected, then tag the current face to grow selection. */
2109 if (sel_state == (NEIGHBORING_FACE_IS_SEL | CURR_FACE_IS_UNSEL)) {
2111 changed = true;
2112 break;
2113 }
2114 }
2115
2116#undef NEIGHBORING_FACE_IS_SEL
2117#undef CURR_FACE_IS_UNSEL
2118 }
2119 else {
2120 if (!uvedit_face_select_test(scene, efa, offsets)) {
2121 continue;
2122 }
2123 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2124 /* Deselect face when at least one of the surrounding faces is not selected */
2125 if (!uvedit_vert_is_all_other_faces_selected(scene, l, offsets)) {
2127 changed = true;
2128 break;
2129 }
2130 }
2131 }
2132 }
2133 }
2134 }
2135 else {
2136
2137 /* clear tags */
2138 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2139 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2141 }
2142 }
2143
2144 /* mark loops to be selected */
2145 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2146 if (uvedit_face_visible_test(scene, efa)) {
2147 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2148
2149 if (BM_ELEM_CD_GET_BOOL(l, offsets.select_vert) == select) {
2152 changed = true;
2153 }
2154 }
2155 }
2156 }
2157 }
2158
2159 if (changed) {
2160 if (is_uv_face_selectmode) {
2161 /* Select tagged faces. */
2162 uv_select_flush_from_tag_face(scene, obedit, select);
2163 }
2164 else {
2165 /* Select tagged loops. */
2166 uv_select_flush_from_tag_loop(scene, obedit, select);
2167 /* Set/unset edge flags based on selected verts. */
2168 if (select) {
2169 uvedit_select_flush(scene, em);
2170 }
2171 else {
2172 uvedit_deselect_flush(scene, em);
2173 }
2174 }
2175 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
2176 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
2177 }
2178 }
2179
2180 return OPERATOR_FINISHED;
2181}
2182
2184{
2185 return uv_select_more_less(C, true);
2186}
2187
2189{
2190 /* identifiers */
2191 ot->name = "Select More";
2192 ot->description = "Select more UV vertices connected to initial selection";
2193 ot->idname = "UV_OT_select_more";
2195
2196 /* api callbacks */
2199}
2200
2202{
2203 return uv_select_more_less(C, false);
2204}
2205
2207{
2208 /* identifiers */
2209 ot->name = "Select Less";
2210 ot->description = "Deselect UV vertices at the boundary of each selection region";
2211 ot->idname = "UV_OT_select_less";
2213
2214 /* api callbacks */
2217}
2218
2221/* -------------------------------------------------------------------- */
2225bool uvedit_select_is_any_selected(const Scene *scene, Object *obedit)
2226{
2227 const ToolSettings *ts = scene->toolsettings;
2229 BMFace *efa;
2230 BMLoop *l;
2231 BMIter iter, liter;
2232
2233 if (ts->uv_flag & UV_SYNC_SELECTION) {
2234 return (em->bm->totvertsel || em->bm->totedgesel || em->bm->totfacesel);
2235 }
2236
2237 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
2238
2239 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2240 if (!uvedit_face_visible_test(scene, efa)) {
2241 continue;
2242 }
2243 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2244 if (BM_ELEM_CD_GET_BOOL(l, offsets.select_vert)) {
2245 return true;
2246 }
2247 }
2248 }
2249 return false;
2250}
2251
2253{
2254 bool found = false;
2255 for (Object *obedit : objects) {
2256 if (uvedit_select_is_any_selected(scene, obedit)) {
2257 found = true;
2258 break;
2259 }
2260 }
2261 return found;
2262}
2263
2264static void uv_select_all(const Scene *scene, BMEditMesh *em, bool select_all)
2265{
2266 BMFace *efa;
2267 BMLoop *l;
2268 BMIter iter, liter;
2269 const char *active_uv_name = CustomData_get_active_layer_name(&em->bm->ldata, CD_PROP_FLOAT2);
2270 BM_uv_map_ensure_vert_select_attr(em->bm, active_uv_name);
2271 BM_uv_map_ensure_edge_select_attr(em->bm, active_uv_name);
2272 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
2273
2274 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2275 if (!uvedit_face_visible_test(scene, efa)) {
2276 continue;
2277 }
2278 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2279 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, select_all);
2280 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, select_all);
2281 }
2282 }
2283}
2284
2285static void uv_select_invert(const Scene *scene, BMEditMesh *em)
2286{
2287 const ToolSettings *ts = scene->toolsettings;
2288 BLI_assert((ts->uv_flag & UV_SYNC_SELECTION) == 0);
2289
2290 const char *active_uv_name = CustomData_get_active_layer_name(&em->bm->ldata, CD_PROP_FLOAT2);
2291 BM_uv_map_ensure_vert_select_attr(em->bm, active_uv_name);
2292 BM_uv_map_ensure_edge_select_attr(em->bm, active_uv_name);
2293 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
2294 BMFace *efa;
2295 BMLoop *l;
2296 BMIter iter, liter;
2297 char uv_selectmode = ts->uv_selectmode;
2298 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
2299 if (!uvedit_face_visible_test(scene, efa)) {
2300 continue;
2301 }
2302 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
2303 if (ELEM(uv_selectmode, UV_SELECT_EDGE, UV_SELECT_FACE)) {
2304 /* Use UV edge selection to find vertices and edges that must be selected. */
2305 bool es = BM_ELEM_CD_GET_BOOL(l, offsets.select_edge);
2306 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, !es);
2307 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, false);
2308 }
2309 /* Use UV vertex selection to find vertices and edges that must be selected. */
2310 else if (ELEM(uv_selectmode, UV_SELECT_VERTEX, UV_SELECT_ISLAND)) {
2311 bool vs = BM_ELEM_CD_GET_BOOL(l, offsets.select_vert);
2312 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, !vs);
2313 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, false);
2314 }
2315 }
2316 }
2317
2318 /* Flush based on uv vert/edge flags and current UV select mode */
2319 if (ELEM(uv_selectmode, UV_SELECT_EDGE, UV_SELECT_FACE)) {
2321 }
2322 else if (ELEM(uv_selectmode, UV_SELECT_VERTEX, UV_SELECT_ISLAND)) {
2323 uvedit_select_flush(scene, em);
2324 }
2325}
2326
2327static void uv_select_all_perform(const Scene *scene, Object *obedit, int action)
2328{
2329 const ToolSettings *ts = scene->toolsettings;
2331
2332 if (action == SEL_TOGGLE) {
2333 action = uvedit_select_is_any_selected(scene, obedit) ? SEL_DESELECT : SEL_SELECT;
2334 }
2335
2336 if (ts->uv_flag & UV_SYNC_SELECTION) {
2337 switch (action) {
2338 case SEL_TOGGLE:
2340 break;
2341 case SEL_SELECT:
2343 break;
2344 case SEL_DESELECT:
2346 break;
2347 case SEL_INVERT:
2348 EDBM_select_swap(em);
2350 break;
2351 }
2352 }
2353 else {
2354 switch (action) {
2355 case SEL_SELECT:
2356 uv_select_all(scene, em, true);
2357 break;
2358 case SEL_DESELECT:
2359 uv_select_all(scene, em, false);
2360 break;
2361 case SEL_INVERT:
2362 uv_select_invert(scene, em);
2363 break;
2364 }
2365 }
2366}
2367
2368static void uv_select_all_perform_multi_ex(const Scene *scene,
2369 const Span<Object *> objects,
2370 int action,
2371 const Object *ob_exclude)
2372{
2373 if (action == SEL_TOGGLE) {
2374 action = uvedit_select_is_any_selected_multi(scene, objects) ? SEL_DESELECT : SEL_SELECT;
2375 }
2376
2377 for (Object *obedit : objects) {
2378 if (ob_exclude && (obedit == ob_exclude)) {
2379 continue;
2380 }
2381 uv_select_all_perform(scene, obedit, action);
2382 }
2383}
2384
2385static void uv_select_all_perform_multi(const Scene *scene, Span<Object *> objects, int action)
2386{
2387 uv_select_all_perform_multi_ex(scene, objects, action, nullptr);
2388}
2389
2391{
2393 Scene *scene = CTX_data_scene(C);
2394 const ToolSettings *ts = scene->toolsettings;
2395 ViewLayer *view_layer = CTX_data_view_layer(C);
2396
2397 int action = RNA_enum_get(op->ptr, "action");
2398
2400 scene, view_layer, nullptr);
2401
2402 uv_select_all_perform_multi(scene, objects, action);
2403
2404 for (Object *obedit : objects) {
2406 }
2407
2408 return OPERATOR_FINISHED;
2409}
2410
2412{
2413 /* identifiers */
2414 ot->name = "(De)select All";
2415 ot->description = "Change selection of all UV vertices";
2416 ot->idname = "UV_OT_select_all";
2418
2419 /* api callbacks */
2422
2424}
2425
2428/* -------------------------------------------------------------------- */
2433 const Span<Object *> objects,
2434 const float co[2],
2436{
2438 const ARegion *region = CTX_wm_region(C);
2439 Scene *scene = CTX_data_scene(C);
2440 const ToolSettings *ts = scene->toolsettings;
2441 UvNearestHit hit = region ? uv_nearest_hit_init_dist_px(&region->v2d, 75.0f) :
2443 int selectmode, sticky;
2444 bool found_item = false;
2445 /* 0 == don't flush, 1 == sel, -1 == deselect; only use when selection sync is enabled. */
2446 int flush = 0;
2447
2448 /* Penalty (in pixels) applied to elements that are already selected
2449 * so elements that aren't already selected are prioritized. */
2450 const float penalty_dist = 3.0f * U.pixelsize;
2451
2452 /* retrieve operation mode */
2453 if (ts->uv_flag & UV_SYNC_SELECTION) {
2454 if (ts->selectmode & SCE_SELECT_FACE) {
2455 selectmode = UV_SELECT_FACE;
2456 }
2457 else if (ts->selectmode & SCE_SELECT_EDGE) {
2458 selectmode = UV_SELECT_EDGE;
2459 }
2460 else {
2461 selectmode = UV_SELECT_VERTEX;
2462 }
2463
2464 sticky = SI_STICKY_DISABLE;
2465 }
2466 else {
2467 selectmode = ts->uv_selectmode;
2468 sticky = ts->uv_sticky;
2469 }
2470
2471 /* find nearest element */
2472 if (selectmode == UV_SELECT_VERTEX) {
2473 /* find vertex */
2474 found_item = uv_find_nearest_vert_multi(scene, objects, co, penalty_dist, &hit);
2475 if (found_item) {
2476 if ((ts->uv_flag & UV_SYNC_SELECTION) == 0) {
2477 BMesh *bm = BKE_editmesh_from_object(hit.ob)->bm;
2479 }
2480 }
2481 }
2482 else if (selectmode == UV_SELECT_EDGE) {
2483 /* find edge */
2484 found_item = uv_find_nearest_edge_multi(scene, objects, co, penalty_dist, &hit);
2485 if (found_item) {
2486 if ((ts->uv_flag & UV_SYNC_SELECTION) == 0) {
2487 BMesh *bm = BKE_editmesh_from_object(hit.ob)->bm;
2489 }
2490 }
2491 }
2492 else if (selectmode == UV_SELECT_FACE) {
2493 /* find face */
2494 found_item = uv_find_nearest_face_multi(scene, objects, co, &hit);
2495
2496 if (!found_item) {
2497 /* Fallback, perform a second pass without a limited threshold,
2498 * which succeeds as long as the cursor is inside the UV face.
2499 * Useful when zoomed in, to select faces with distant screen-space face centers. */
2500 hit.dist_sq = FLT_MAX;
2501 found_item = uv_find_nearest_face_multi_ex(scene, objects, co, &hit, true);
2502 }
2503
2504 if (found_item) {
2505 BMesh *bm = BKE_editmesh_from_object(hit.ob)->bm;
2506 BM_mesh_active_face_set(bm, hit.efa);
2507 }
2508 }
2509 else if (selectmode == UV_SELECT_ISLAND) {
2510 found_item = uv_find_nearest_edge_multi(scene, objects, co, 0.0f, &hit);
2511
2512 if (!found_item) {
2513 /* Without this, we can be within the face of an island but too far from an edge,
2514 * see face selection comment for details. */
2515 hit.dist_sq = FLT_MAX;
2516 found_item = uv_find_nearest_face_multi_ex(scene, objects, co, &hit, true);
2517 }
2518 }
2519
2520 bool found = found_item;
2521 bool changed = false;
2522
2523 bool is_selected = false;
2524 if (found) {
2525 Object *obedit = hit.ob;
2527 const char *active_uv_name = CustomData_get_active_layer_name(&em->bm->ldata, CD_PROP_FLOAT2);
2528 BM_uv_map_ensure_vert_select_attr(em->bm, active_uv_name);
2529 BM_uv_map_ensure_edge_select_attr(em->bm, active_uv_name);
2530 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
2531 if (selectmode == UV_SELECT_FACE) {
2532 is_selected = uvedit_face_select_test(scene, hit.efa, offsets);
2533 }
2534 else if (selectmode == UV_SELECT_EDGE) {
2535 is_selected = uvedit_edge_select_test(scene, hit.l, offsets);
2536 }
2537 else {
2538 /* Vertex or island. For island (if we were using #uv_find_nearest_face_multi_ex, see above),
2539 * `hit.l` is null, use `hit.efa` instead. */
2540 if (hit.l != nullptr) {
2541 is_selected = uvedit_uv_select_test(scene, hit.l, offsets);
2542 }
2543 else {
2544 is_selected = uvedit_face_select_test(scene, hit.efa, offsets);
2545 }
2546 }
2547 }
2548
2549 if (params->sel_op == SEL_OP_SET) {
2550 if ((found && params->select_passthrough) && is_selected) {
2551 found = false;
2552 }
2553 else if (found || params->deselect_all) {
2554 /* Deselect everything. */
2556 for (Object *obedit : objects) {
2558 }
2559 changed = true;
2560 }
2561 }
2562
2563 if (found) {
2564 Object *obedit = hit.ob;
2566 const char *active_uv_name = CustomData_get_active_layer_name(&em->bm->ldata, CD_PROP_FLOAT2);
2567 BM_uv_map_ensure_vert_select_attr(em->bm, active_uv_name);
2568 BM_uv_map_ensure_edge_select_attr(em->bm, active_uv_name);
2569 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
2570
2571 if (selectmode == UV_SELECT_ISLAND) {
2572 const bool extend = params->sel_op == SEL_OP_ADD;
2573 const bool deselect = params->sel_op == SEL_OP_SUB;
2574 const bool toggle = params->sel_op == SEL_OP_XOR;
2575 /* Current behavior of 'extend'
2576 * is actually toggling, so pass extend flag as 'toggle' here */
2577 uv_select_linked_multi(scene, objects, &hit, extend, deselect, toggle, false);
2578 /* TODO: check if this actually changed. */
2579 changed = true;
2580 }
2581 else {
2583 bool select_value = false;
2584 switch (params->sel_op) {
2585 case SEL_OP_ADD: {
2586 select_value = true;
2587 break;
2588 }
2589 case SEL_OP_SUB: {
2590 select_value = false;
2591 break;
2592 }
2593 case SEL_OP_XOR: {
2594 select_value = !is_selected;
2595 break;
2596 }
2597 case SEL_OP_SET: {
2598 /* Deselect has already been performed. */
2599 select_value = true;
2600 break;
2601 }
2602 case SEL_OP_AND: {
2603 BLI_assert_unreachable(); /* Doesn't make sense for picking. */
2604 break;
2605 }
2606 }
2607
2608 if (selectmode == UV_SELECT_FACE) {
2609 uvedit_face_select_set_with_sticky(scene, em, hit.efa, select_value, true, offsets);
2610 flush = 1;
2611 }
2612 else if (selectmode == UV_SELECT_EDGE) {
2613 uvedit_edge_select_set_with_sticky(scene, em, hit.l, select_value, true, offsets);
2614 flush = 1;
2615 }
2616 else if (selectmode == UV_SELECT_VERTEX) {
2617 uvedit_uv_select_set_with_sticky(scene, em, hit.l, select_value, true, offsets);
2618 flush = 1;
2619 }
2620 else {
2622 }
2623
2624 /* De-selecting an edge may deselect a face too - validate. */
2625 if (ts->uv_flag & UV_SYNC_SELECTION) {
2626 if (select_value == false) {
2628 }
2629 }
2630
2631 /* (de)select sticky UV nodes. */
2632 if (sticky != SI_STICKY_DISABLE) {
2633 flush = select_value ? 1 : -1;
2634 }
2635
2636 changed = true;
2637 }
2638
2639 if (ts->uv_flag & UV_SYNC_SELECTION) {
2640 if (flush != 0) {
2642 }
2643 }
2644 else {
2645 /* Setting the selection implies a single element, which doesn't need to be flushed. */
2646 if (params->sel_op != SEL_OP_SET) {
2647 ED_uvedit_selectmode_flush(scene, em);
2648 }
2649 }
2650 }
2651
2652 if (changed && found) {
2653 /* Only update the `hit` object as de-selecting all will have refreshed the others. */
2654 Object *obedit = hit.ob;
2656 }
2657
2658 return changed || found;
2659}
2660static bool uv_mouse_select(bContext *C, const float co[2], const SelectPick_Params *params)
2661{
2662 const Scene *scene = CTX_data_scene(C);
2663 ViewLayer *view_layer = CTX_data_view_layer(C);
2665 scene, view_layer, nullptr);
2666 bool changed = uv_mouse_select_multi(C, objects, co, params);
2667 return changed;
2668}
2669
2671{
2672 float co[2];
2673
2674 RNA_float_get_array(op->ptr, "location", co);
2675
2678
2679 const bool changed = uv_mouse_select(C, co, &params);
2680
2681 if (changed) {
2683 }
2685}
2686
2687static int uv_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2688{
2689 const ARegion *region = CTX_wm_region(C);
2690 float co[2];
2691
2692 UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
2693 RNA_float_set_array(op->ptr, "location", co);
2694
2695 const int retval = uv_select_exec(C, op);
2696
2697 return WM_operator_flag_only_pass_through_on_press(retval, event);
2698}
2699
2701{
2702 /* identifiers */
2703 ot->name = "Select";
2704 ot->description = "Select UV vertices";
2705 ot->idname = "UV_OT_select";
2706 ot->flag = OPTYPE_UNDO;
2707
2708 /* api callbacks */
2711 ot->poll = ED_operator_uvedit; /* requires space image */
2713
2714 /* properties */
2715 PropertyRNA *prop;
2716
2718
2719 prop = RNA_def_float_vector(
2720 ot->srna,
2721 "location",
2722 2,
2723 nullptr,
2724 -FLT_MAX,
2725 FLT_MAX,
2726 "Location",
2727 "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds",
2728 -100.0f,
2729 100.0f);
2731}
2732
2735/* -------------------------------------------------------------------- */
2743
2745 const Span<Object *> objects,
2746 const float co[2],
2747 const bool extend,
2748 enum eUVLoopGenericType loop_type)
2749{
2750 const ARegion *region = CTX_wm_region(C);
2752 Scene *scene = CTX_data_scene(C);
2753 const ToolSettings *ts = scene->toolsettings;
2754 UvNearestHit hit = region ? uv_nearest_hit_init_max(&region->v2d) :
2756 bool found_item = false;
2757 /* 0 == don't flush, 1 == sel, -1 == deselect; only use when selection sync is enabled. */
2758 int flush = 0;
2759
2760 /* Find edge. */
2761 found_item = uv_find_nearest_edge_multi(scene, objects, co, 0.0f, &hit);
2762 if (!found_item) {
2763 return OPERATOR_CANCELLED;
2764 }
2765
2766 Object *obedit = hit.ob;
2768
2769 /* Do selection. */
2770 if (!extend) {
2771 uv_select_all_perform_multi_ex(scene, objects, SEL_DESELECT, obedit);
2772 }
2773
2774 if (loop_type == UV_LOOP_SELECT) {
2776 flush = uv_select_faceloop(scene, obedit, &hit, extend);
2777 }
2778 else {
2779 flush = uv_select_edgeloop(scene, obedit, &hit, extend);
2780 }
2781 }
2782 else if (loop_type == UV_RING_SELECT) {
2783 flush = uv_select_edgering(scene, obedit, &hit, extend);
2784 }
2785 else {
2787 }
2788
2789 if (ts->uv_flag & UV_SYNC_SELECTION) {
2790 if (flush == 1) {
2792 }
2793 else if (flush == -1) {
2795 }
2796 }
2797 else {
2798 ED_uvedit_selectmode_flush(scene, em);
2799 }
2800
2801 for (Object *ob : objects) {
2803 }
2804
2806}
2808 const float co[2],
2809 const bool extend,
2810 enum eUVLoopGenericType loop_type)
2811{
2812 const Scene *scene = CTX_data_scene(C);
2813 ViewLayer *view_layer = CTX_data_view_layer(C);
2815 scene, view_layer, nullptr);
2816 int ret = uv_mouse_select_loop_generic_multi(C, objects, co, extend, loop_type);
2817 return ret;
2818}
2819
2822/* -------------------------------------------------------------------- */
2827{
2828 float co[2];
2829
2830 RNA_float_get_array(op->ptr, "location", co);
2831 const bool extend = RNA_boolean_get(op->ptr, "extend");
2832
2833 return uv_mouse_select_loop_generic(C, co, extend, UV_LOOP_SELECT);
2834}
2835
2836static int uv_select_loop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2837{
2838 const ARegion *region = CTX_wm_region(C);
2839 float co[2];
2840
2841 UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
2842 RNA_float_set_array(op->ptr, "location", co);
2843
2844 const int retval = uv_select_loop_exec(C, op);
2845
2846 return WM_operator_flag_only_pass_through_on_press(retval, event);
2847}
2848
2850{
2851 /* identifiers */
2852 ot->name = "Loop Select";
2853 ot->description = "Select a loop of connected UV vertices";
2854 ot->idname = "UV_OT_select_loop";
2855 ot->flag = OPTYPE_UNDO;
2856
2857 /* api callbacks */
2860 ot->poll = ED_operator_uvedit; /* requires space image */
2861
2862 /* properties */
2863 PropertyRNA *prop;
2864 prop = RNA_def_boolean(ot->srna,
2865 "extend",
2866 false,
2867 "Extend",
2868 "Extend selection rather than clearing the existing selection");
2870 prop = RNA_def_float_vector(
2871 ot->srna,
2872 "location",
2873 2,
2874 nullptr,
2875 -FLT_MAX,
2876 FLT_MAX,
2877 "Location",
2878 "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds",
2879 -100.0f,
2880 100.0f);
2882}
2883
2886/* -------------------------------------------------------------------- */
2891{
2892 float co[2];
2893 RNA_float_get_array(op->ptr, "location", co);
2894 const bool extend = RNA_boolean_get(op->ptr, "extend");
2895 return uv_mouse_select_loop_generic(C, co, extend, UV_RING_SELECT);
2896}
2897
2899{
2900 const ARegion *region = CTX_wm_region(C);
2901 float co[2];
2902
2903 UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
2904 RNA_float_set_array(op->ptr, "location", co);
2905
2906 const int retval = uv_select_edge_ring_exec(C, op);
2907
2908 return WM_operator_flag_only_pass_through_on_press(retval, event);
2909}
2910
2912{
2913 /* identifiers */
2914 ot->name = "Edge Ring Select";
2915 ot->description = "Select an edge ring of connected UV vertices";
2916 ot->idname = "UV_OT_select_edge_ring";
2917 ot->flag = OPTYPE_UNDO;
2918
2919 /* api callbacks */
2922 ot->poll = ED_operator_uvedit; /* requires space image */
2923
2924 /* properties */
2925 PropertyRNA *prop;
2926 prop = RNA_def_boolean(ot->srna,
2927 "extend",
2928 false,
2929 "Extend",
2930 "Extend selection rather than clearing the existing selection");
2932 prop = RNA_def_float_vector(
2933 ot->srna,
2934 "location",
2935 2,
2936 nullptr,
2937 -FLT_MAX,
2938 FLT_MAX,
2939 "Location",
2940 "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds",
2941 -100.0f,
2942 100.0f);
2944}
2945
2948/* -------------------------------------------------------------------- */
2952static int uv_select_linked_internal(bContext *C, wmOperator *op, const wmEvent *event, bool pick)
2953{
2954 const ARegion *region = CTX_wm_region(C);
2955 Scene *scene = CTX_data_scene(C);
2956 const ToolSettings *ts = scene->toolsettings;
2957 ViewLayer *view_layer = CTX_data_view_layer(C);
2958 bool extend = true;
2959 bool deselect = false;
2960 bool select_faces = (ts->uv_flag & UV_SYNC_SELECTION) && (ts->selectmode & SCE_SELECT_FACE);
2961
2962 UvNearestHit hit = region ? uv_nearest_hit_init_max(&region->v2d) :
2964
2965 if (pick) {
2966 extend = RNA_boolean_get(op->ptr, "extend");
2967 deselect = RNA_boolean_get(op->ptr, "deselect");
2968 }
2969
2971 scene, view_layer, nullptr);
2972
2973 if (pick) {
2974 float co[2];
2975
2976 if (event) {
2977 /* invoke */
2978 UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &co[0], &co[1]);
2979 RNA_float_set_array(op->ptr, "location", co);
2980 }
2981 else {
2982 /* exec */
2983 RNA_float_get_array(op->ptr, "location", co);
2984 }
2985
2986 if (!uv_find_nearest_edge_multi(scene, objects, co, 0.0f, &hit)) {
2987 return OPERATOR_CANCELLED;
2988 }
2989 }
2990
2991 if (!extend && !deselect) {
2993 }
2994
2996 scene, objects, pick ? &hit : nullptr, extend, deselect, false, select_faces);
2997
2998 if (pick) {
2999 DEG_id_tag_update(static_cast<ID *>(hit.ob->data), ID_RECALC_SYNC_TO_EVAL | ID_RECALC_SELECT);
3000 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, hit.ob->data);
3001 }
3002 else {
3003 for (Object *obedit : objects) {
3004 DEG_id_tag_update(static_cast<ID *>(obedit->data),
3006 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
3007 }
3008 }
3009
3010 return OPERATOR_FINISHED;
3011}
3012
3014{
3015 return uv_select_linked_internal(C, op, nullptr, false);
3016}
3017
3019{
3020 /* identifiers */
3021 ot->name = "Select Linked";
3022 ot->description = "Select all UV vertices linked to the active UV map";
3023 ot->idname = "UV_OT_select_linked";
3024
3025 /* api callbacks */
3027 ot->poll = ED_operator_uvedit; /* requires space image */
3028
3029 /* flags */
3031}
3032
3035/* -------------------------------------------------------------------- */
3040{
3041 return uv_select_linked_internal(C, op, event, true);
3042}
3043
3045{
3046 return uv_select_linked_internal(C, op, nullptr, true);
3047}
3048
3050{
3051 /* identifiers */
3052 ot->name = "Select Linked Pick";
3053 ot->description = "Select all UV vertices linked under the mouse";
3054 ot->idname = "UV_OT_select_linked_pick";
3055
3056 /* flags */
3058
3059 /* api callbacks */
3062 ot->poll = ED_operator_uvedit; /* requires space image */
3063
3064 /* properties */
3065 PropertyRNA *prop;
3066 prop = RNA_def_boolean(ot->srna,
3067 "extend",
3068 false,
3069 "Extend",
3070 "Extend selection rather than clearing the existing selection");
3072 prop = RNA_def_boolean(ot->srna,
3073 "deselect",
3074 false,
3075 "Deselect",
3076 "Deselect linked UV vertices rather than selecting them");
3078 prop = RNA_def_float_vector(
3079 ot->srna,
3080 "location",
3081 2,
3082 nullptr,
3083 -FLT_MAX,
3084 FLT_MAX,
3085 "Location",
3086 "Mouse location in normalized coordinates, 0.0 to 1.0 is within the image bounds",
3087 -100.0f,
3088 100.0f);
3090}
3091
3094/* -------------------------------------------------------------------- */
3105{
3107 Scene *scene = CTX_data_scene(C);
3108 ViewLayer *view_layer = CTX_data_view_layer(C);
3109 const ToolSettings *ts = scene->toolsettings;
3110
3111 BMFace *efa;
3112 BMLoop *l;
3113 BMIter iter, liter;
3114
3115 if (ts->uv_flag & UV_SYNC_SELECTION) {
3116 BKE_report(op->reports, RPT_ERROR, "Cannot split selection when sync selection is enabled");
3117 return OPERATOR_CANCELLED;
3118 }
3119
3120 bool changed_multi = false;
3121
3123 scene, view_layer, nullptr);
3124
3125 for (Object *obedit : objects) {
3126 BMesh *bm = BKE_editmesh_from_object(obedit)->bm;
3127
3128 bool changed = false;
3129
3130 const char *active_uv_name = CustomData_get_active_layer_name(&bm->ldata, CD_PROP_FLOAT2);
3131 BM_uv_map_ensure_vert_select_attr(bm, active_uv_name);
3132 BM_uv_map_ensure_edge_select_attr(bm, active_uv_name);
3133 const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
3134
3135 BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) {
3136 bool is_sel = false;
3137 bool is_unsel = false;
3138
3139 if (!uvedit_face_visible_test(scene, efa)) {
3140 continue;
3141 }
3142
3143 /* are we all selected? */
3144 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3145
3146 if (BM_ELEM_CD_GET_BOOL(l, offsets.select_vert) ||
3148 {
3149 is_sel = true;
3150 }
3151 if (!BM_ELEM_CD_GET_BOOL(l, offsets.select_vert) ||
3153 {
3154 is_unsel = true;
3155 }
3156
3157 /* we have mixed selection, bail out */
3158 if (is_sel && is_unsel) {
3159 break;
3160 }
3161 }
3162
3163 if (is_sel && is_unsel) {
3164 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3165 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, false);
3166 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, false);
3167 }
3168
3169 changed = true;
3170 }
3171 }
3172
3173 if (changed) {
3174 changed_multi = true;
3177 }
3178 }
3179
3180 return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
3181}
3182
3184{
3185 /* identifiers */
3186 ot->name = "Select Split";
3187 ot->description = "Select only entirely selected faces";
3188 ot->idname = "UV_OT_select_split";
3190
3191 /* api callbacks */
3193 ot->poll = ED_operator_uvedit; /* requires space image */
3194}
3195
3197 const ToolSettings *ts,
3198 Object *obedit)
3199{
3200 if (ts->uv_flag & UV_SYNC_SELECTION) {
3201 DEG_id_tag_update(static_cast<ID *>(obedit->data), ID_RECALC_SELECT);
3203 }
3204 else {
3205 Object *obedit_eval = DEG_get_evaluated_object(depsgraph, obedit);
3206 BKE_mesh_batch_cache_dirty_tag(static_cast<Mesh *>(obedit_eval->data),
3208 /* Only for region redraw. */
3210 }
3211}
3212
3215/* -------------------------------------------------------------------- */
3225 BMEditMesh *em,
3226 UvVertMap *vmap,
3227 const uint efa_index,
3228 BMLoop *l,
3229 const bool select,
3230 const BMUVOffsets offsets)
3231{
3232 UvMapVert *start_vlist = nullptr, *vlist_iter;
3233 BMFace *efa_vlist;
3234
3235 uvedit_uv_select_set(scene, em->bm, l, select, false, offsets);
3236
3237 vlist_iter = BM_uv_vert_map_at_index(vmap, BM_elem_index_get(l->v));
3238
3239 while (vlist_iter) {
3240 if (vlist_iter->separate) {
3241 start_vlist = vlist_iter;
3242 }
3243
3244 if (efa_index == vlist_iter->face_index) {
3245 break;
3246 }
3247
3248 vlist_iter = vlist_iter->next;
3249 }
3250
3251 vlist_iter = start_vlist;
3252 while (vlist_iter) {
3253 if (vlist_iter != start_vlist && vlist_iter->separate) {
3254 break;
3255 }
3256
3257 if (efa_index != vlist_iter->face_index) {
3258 BMLoop *l_other;
3259 efa_vlist = BM_face_at_index(em->bm, vlist_iter->face_index);
3260 // tf_vlist = BM_ELEM_CD_GET_VOID_P(efa_vlist, cd_poly_tex_offset); /* UNUSED */
3261
3262 l_other = static_cast<BMLoop *>(
3263 BM_iter_at_index(em->bm, BM_LOOPS_OF_FACE, efa_vlist, vlist_iter->loop_of_face_index));
3264
3265 uvedit_uv_select_set(scene, em->bm, l_other, select, false, offsets);
3266 }
3267 vlist_iter = vlist_iter->next;
3268 }
3269}
3270
3281static void uv_select_flush_from_tag_face(const Scene *scene, Object *obedit, const bool select)
3282{
3283 /* Selecting UV Faces with some modes requires us to change
3284 * the selection in other faces (depending on the sticky mode).
3285 *
3286 * This only needs to be done when the Mesh is not used for
3287 * selection (so for sticky modes, vertex or location based). */
3288
3289 const ToolSettings *ts = scene->toolsettings;
3291 BMFace *efa;
3292 BMLoop *l;
3293 BMIter iter, liter;
3294 const char *active_uv_name = CustomData_get_active_layer_name(&em->bm->ldata, CD_PROP_FLOAT2);
3295 BM_uv_map_ensure_vert_select_attr(em->bm, active_uv_name);
3296 BM_uv_map_ensure_edge_select_attr(em->bm, active_uv_name);
3297 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
3298
3299 if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 &&
3301 {
3302
3303 uint efa_index;
3304
3306 UvVertMap *vmap = BM_uv_vert_map_create(em->bm, false);
3307 if (vmap == nullptr) {
3308 return;
3309 }
3310
3311 BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, efa_index) {
3312 if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
3313 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3314 if (select) {
3315 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, true);
3317 scene, em, vmap, efa_index, l, select, offsets);
3318 }
3319 else {
3320 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, false);
3321 if (!uvedit_vert_is_face_select_any_other(scene, l, offsets)) {
3323 scene, em, vmap, efa_index, l, select, offsets);
3324 }
3325 }
3326 }
3327 }
3328 }
3329 BM_uv_vert_map_free(vmap);
3330 }
3331 else { /* SI_STICKY_DISABLE or ts->uv_flag & UV_SYNC_SELECTION */
3332 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3333 if (BM_elem_flag_test(efa, BM_ELEM_TAG)) {
3334 uvedit_face_select_set(scene, em->bm, efa, select, false, offsets);
3335 }
3336 }
3337 }
3338}
3339
3350static void uv_select_flush_from_tag_loop(const Scene *scene, Object *obedit, const bool select)
3351{
3352 /* Selecting UV Loops with some modes requires us to change
3353 * the selection in other faces (depending on the sticky mode).
3354 *
3355 * This only needs to be done when the Mesh is not used for
3356 * selection (so for sticky modes, vertex or location based). */
3357
3358 const ToolSettings *ts = scene->toolsettings;
3360 BMFace *efa;
3361 BMLoop *l;
3362 BMIter iter, liter;
3363
3364 const char *active_uv_name = CustomData_get_active_layer_name(&em->bm->ldata, CD_PROP_FLOAT2);
3365 BM_uv_map_ensure_vert_select_attr(em->bm, active_uv_name);
3366 BM_uv_map_ensure_edge_select_attr(em->bm, active_uv_name);
3367 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
3368
3369 if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && ts->uv_sticky == SI_STICKY_VERTEX) {
3370 /* Tag all verts as untouched, then touch the ones that have a face center
3371 * in the loop and select all UVs that use a touched vert. */
3373
3374 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3375 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3378 }
3379 }
3380 }
3381
3382 /* now select tagged verts */
3383 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3384 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3386 uvedit_uv_select_set(scene, em->bm, l, select, false, offsets);
3387 }
3388 }
3389 }
3390 }
3391 else if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 && ts->uv_sticky == SI_STICKY_LOC) {
3392 uint efa_index;
3393
3395 UvVertMap *vmap = BM_uv_vert_map_create(em->bm, false);
3396 if (vmap == nullptr) {
3397 return;
3398 }
3399
3400 BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, efa_index) {
3401 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3404 scene, em, vmap, efa_index, l, select, offsets);
3405 }
3406 }
3407 }
3408 BM_uv_vert_map_free(vmap);
3409 }
3410 else { /* SI_STICKY_DISABLE or ts->uv_flag & UV_SYNC_SELECTION */
3411 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3412 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3414 uvedit_uv_select_set(scene, em->bm, l, select, false, offsets);
3415 }
3416 }
3417 }
3418 }
3419}
3420
3432{
3433 const ToolSettings *ts = scene->toolsettings;
3434 BMFace *efa;
3435 BMLoop *l;
3436 BMIter iter, liter;
3437
3438 const char *active_uv_name = CustomData_get_active_layer_name(&em->bm->ldata, CD_PROP_FLOAT2);
3439 BM_uv_map_ensure_vert_select_attr(em->bm, active_uv_name);
3440 BM_uv_map_ensure_edge_select_attr(em->bm, active_uv_name);
3441 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
3442
3443 if ((ts->uv_flag & UV_SYNC_SELECTION) == 0 &&
3445 {
3446 /* Use UV edge selection to identify which verts must to be selected */
3447 uint efa_index;
3448 /* Clear UV vert flags */
3449 bm_clear_uv_vert_selection(scene, em->bm, offsets);
3450
3452 UvVertMap *vmap = BM_uv_vert_map_create(em->bm, false);
3453 if (vmap == nullptr) {
3454 return;
3455 }
3456 BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, efa_index) {
3457 if (!uvedit_face_visible_test(scene, efa)) {
3458 /* This visibility check could be removed? Simply relying on edge flags to ensure
3459 * visibility might be sufficient. */
3460 continue;
3461 }
3462 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3463 /* Select verts based on UV edge flag. */
3464 if (BM_ELEM_CD_GET_BOOL(l, offsets.select_edge)) {
3466 scene, em, vmap, efa_index, l, true, offsets);
3468 scene, em, vmap, efa_index, l->next, true, offsets);
3469 }
3470 }
3471 }
3472 BM_uv_vert_map_free(vmap);
3473 }
3474 else {
3475 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3476 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3477
3478 if (BM_ELEM_CD_GET_BOOL(l, offsets.select_edge)) {
3479 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, true);
3480 BM_ELEM_CD_SET_BOOL(l->next, offsets.select_vert, true);
3481 }
3482 else if (!BM_ELEM_CD_GET_BOOL(l->prev, offsets.select_edge)) {
3483 BM_ELEM_CD_SET_BOOL(l->next, offsets.select_vert, false);
3484 }
3485 }
3486 }
3487 }
3488}
3489
3492/* -------------------------------------------------------------------- */
3497{
3499 Scene *scene = CTX_data_scene(C);
3500 const ToolSettings *ts = scene->toolsettings;
3501 ViewLayer *view_layer = CTX_data_view_layer(C);
3502 const ARegion *region = CTX_wm_region(C);
3503 BMFace *efa;
3504 BMLoop *l;
3505 BMIter iter, liter;
3506 float *luv;
3507 rctf rectf;
3508 bool pinned;
3509 const bool use_face_center = ((ts->uv_flag & UV_SYNC_SELECTION) ?
3510 (ts->selectmode == SCE_SELECT_FACE) :
3511 (ts->uv_selectmode == UV_SELECT_FACE));
3512 const bool use_edge = ((ts->uv_flag & UV_SYNC_SELECTION) ?
3513 (ts->selectmode == SCE_SELECT_EDGE) :
3514 (ts->uv_selectmode == UV_SELECT_EDGE));
3515 const bool use_select_linked = !(ts->uv_flag & UV_SYNC_SELECTION) &&
3517
3518 /* get rectangle from operator */
3520 UI_view2d_region_to_view_rctf(&region->v2d, &rectf, &rectf);
3521
3522 const eSelectOp sel_op = eSelectOp(RNA_enum_get(op->ptr, "mode"));
3523 const bool select = (sel_op != SEL_OP_SUB);
3524 const bool use_pre_deselect = SEL_OP_USE_PRE_DESELECT(sel_op);
3525
3526 pinned = RNA_boolean_get(op->ptr, "pinned");
3527
3528 bool changed_multi = false;
3529
3531 scene, view_layer, nullptr);
3532
3533 if (use_pre_deselect) {
3535 }
3536
3537 /* don't indent to avoid diff noise! */
3538 for (Object *obedit : objects) {
3540
3541 bool changed = false;
3542
3543 const char *active_uv_name = CustomData_get_active_layer_name(&em->bm->ldata, CD_PROP_FLOAT2);
3544 BM_uv_map_ensure_vert_select_attr(em->bm, active_uv_name);
3545 BM_uv_map_ensure_edge_select_attr(em->bm, active_uv_name);
3546 BM_uv_map_ensure_pin_attr(em->bm, active_uv_name);
3547 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
3548
3549 /* do actual selection */
3550 if (use_face_center && !pinned) {
3551 /* handle face selection mode */
3552 float cent[2];
3553
3554 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3555 /* assume not touched */
3557
3558 if (uvedit_face_visible_test(scene, efa)) {
3559 BM_face_uv_calc_center_median(efa, offsets.uv, cent);
3560 if (BLI_rctf_isect_pt_v(&rectf, cent)) {
3562 changed = true;
3563 }
3564 }
3565 }
3566
3567 /* (de)selects all tagged faces and deals with sticky modes */
3568 if (changed) {
3569 uv_select_flush_from_tag_face(scene, obedit, select);
3570 }
3571 }
3572 else if (use_edge && !pinned) {
3573 bool do_second_pass = true;
3574 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3575 if (!uvedit_face_visible_test(scene, efa)) {
3576 continue;
3577 }
3578
3579 BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev;
3580 float *luv_prev = BM_ELEM_CD_GET_FLOAT_P(l_prev, offsets.uv);
3581
3582 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3583 luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
3584 if (BLI_rctf_isect_pt_v(&rectf, luv) && BLI_rctf_isect_pt_v(&rectf, luv_prev)) {
3585 uvedit_edge_select_set_with_sticky(scene, em, l_prev, select, false, offsets);
3586 changed = true;
3587 do_second_pass = false;
3588 }
3589 l_prev = l;
3590 luv_prev = luv;
3591 }
3592 }
3593 /* Do a second pass if no complete edges could be selected.
3594 * This matches wire-frame edit-mesh selection in the 3D view. */
3595 if (do_second_pass) {
3596 /* Second pass to check if edges partially overlap with the selection area (box). */
3597 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3598 if (!uvedit_face_visible_test(scene, efa)) {
3599 continue;
3600 }
3601 BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev;
3602 float *luv_prev = BM_ELEM_CD_GET_FLOAT_P(l_prev, offsets.uv);
3603
3604 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3605 luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
3606 if (BLI_rctf_isect_segment(&rectf, luv_prev, luv)) {
3607 uvedit_edge_select_set_with_sticky(scene, em, l_prev, select, false, offsets);
3608 changed = true;
3609 }
3610 l_prev = l;
3611 luv_prev = luv;
3612 }
3613 }
3614 }
3615 }
3616 else {
3617 /* other selection modes */
3618 changed = true;
3620
3621 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3622 if (!uvedit_face_visible_test(scene, efa)) {
3623 continue;
3624 }
3625 bool has_selected = false;
3626 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3627 luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
3628 if (select != uvedit_uv_select_test(scene, l, offsets)) {
3629 if (!pinned || (ts->uv_flag & UV_SYNC_SELECTION)) {
3630 /* UV_SYNC_SELECTION - can't do pinned selection */
3631 if (BLI_rctf_isect_pt_v(&rectf, luv)) {
3632 uvedit_uv_select_set(scene, em->bm, l, select, false, offsets);
3634 has_selected = true;
3635 }
3636 }
3637 else if (pinned) {
3638 if (BM_ELEM_CD_GET_BOOL(l, offsets.pin) && BLI_rctf_isect_pt_v(&rectf, luv)) {
3639 uvedit_uv_select_set(scene, em->bm, l, select, false, offsets);
3641 }
3642 }
3643 }
3644 }
3645 if (has_selected && use_select_linked) {
3646 UvNearestHit hit = {};
3647 hit.ob = obedit;
3648 hit.efa = efa;
3649 uv_select_linked_multi(scene, objects, &hit, true, !select, false, false);
3650 }
3651 }
3652
3653 if (ts->uv_sticky == SI_STICKY_VERTEX) {
3654 uvedit_vertex_select_tagged(em, scene, select, offsets);
3655 }
3656 }
3657
3658 if (changed || use_pre_deselect) {
3659 changed_multi = true;
3660 if (ts->uv_flag & UV_SYNC_SELECTION) {
3662 }
3663 else {
3664 ED_uvedit_selectmode_flush(scene, em);
3665 }
3667 }
3668 }
3669
3670 return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
3671}
3672
3674{
3675 /* identifiers */
3676 ot->name = "Box Select";
3677 ot->description = "Select UV vertices using box selection";
3678 ot->idname = "UV_OT_select_box";
3679
3680 /* api callbacks */
3684 ot->poll = ED_operator_uvedit_space_image; /* requires space image */
3686
3687 /* flags */
3688 ot->flag = OPTYPE_UNDO;
3689
3690 /* properties */
3691 RNA_def_boolean(ot->srna, "pinned", false, "Pinned", "Border select pinned UVs only");
3692
3695}
3696
3699/* -------------------------------------------------------------------- */
3703static bool uv_circle_select_is_point_inside(const float uv[2],
3704 const float offset[2],
3705 const float ellipse[2])
3706{
3707 /* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */
3708 const float co[2] = {
3709 (uv[0] - offset[0]) * ellipse[0],
3710 (uv[1] - offset[1]) * ellipse[1],
3711 };
3712 return len_squared_v2(co) < 1.0f;
3713}
3714
3715static bool uv_circle_select_is_edge_inside(const float uv_a[2],
3716 const float uv_b[2],
3717 const float offset[2],
3718 const float ellipse[2])
3719{
3720 /* normalized ellipse: ell[0] = scaleX, ell[1] = scaleY */
3721 const float co_a[2] = {
3722 (uv_a[0] - offset[0]) * ellipse[0],
3723 (uv_a[1] - offset[1]) * ellipse[1],
3724 };
3725 const float co_b[2] = {
3726 (uv_b[0] - offset[0]) * ellipse[0],
3727 (uv_b[1] - offset[1]) * ellipse[1],
3728 };
3729 const float co_zero[2] = {0.0f, 0.0f};
3730 return dist_squared_to_line_segment_v2(co_zero, co_a, co_b) < 1.0f;
3731}
3732
3734{
3736 SpaceImage *sima = CTX_wm_space_image(C);
3737 Scene *scene = CTX_data_scene(C);
3738 ViewLayer *view_layer = CTX_data_view_layer(C);
3739 const ToolSettings *ts = scene->toolsettings;
3740 const ARegion *region = CTX_wm_region(C);
3741 BMFace *efa;
3742 BMLoop *l;
3743 BMIter iter, liter;
3744 float *luv;
3745 int x, y, radius, width, height;
3746 float zoomx, zoomy;
3747 float offset[2], ellipse[2];
3748
3749 const bool use_face_center = ((ts->uv_flag & UV_SYNC_SELECTION) ?
3750 (ts->selectmode == SCE_SELECT_FACE) :
3751 (ts->uv_selectmode == UV_SELECT_FACE));
3752 const bool use_edge = ((ts->uv_flag & UV_SYNC_SELECTION) ?
3753 (ts->selectmode == SCE_SELECT_EDGE) :
3754 (ts->uv_selectmode == UV_SELECT_EDGE));
3755 const bool use_select_linked = !(ts->uv_flag & UV_SYNC_SELECTION) &&
3757
3758 /* get operator properties */
3759 x = RNA_int_get(op->ptr, "x");
3760 y = RNA_int_get(op->ptr, "y");
3761 radius = RNA_int_get(op->ptr, "radius");
3762
3763 /* compute ellipse size and location, not a circle since we deal
3764 * with non square image. ellipse is normalized, r = 1.0. */
3765 ED_space_image_get_size(sima, &width, &height);
3766 ED_space_image_get_zoom(sima, region, &zoomx, &zoomy);
3767
3768 ellipse[0] = width * zoomx / radius;
3769 ellipse[1] = height * zoomy / radius;
3770
3771 UI_view2d_region_to_view(&region->v2d, x, y, &offset[0], &offset[1]);
3772
3773 bool changed_multi = false;
3774
3776 scene, view_layer, nullptr);
3777
3778 const eSelectOp sel_op = ED_select_op_modal(
3779 eSelectOp(RNA_enum_get(op->ptr, "mode")),
3780 WM_gesture_is_modal_first(static_cast<wmGesture *>(op->customdata)));
3781 const bool select = (sel_op != SEL_OP_SUB);
3782 const bool use_pre_deselect = SEL_OP_USE_PRE_DESELECT(sel_op);
3783
3784 if (use_pre_deselect) {
3786 }
3787
3788 for (Object *obedit : objects) {
3790
3791 bool changed = false;
3792
3793 const char *active_uv_name = CustomData_get_active_layer_name(&em->bm->ldata, CD_PROP_FLOAT2);
3794 BM_uv_map_ensure_vert_select_attr(em->bm, active_uv_name);
3795 BM_uv_map_ensure_edge_select_attr(em->bm, active_uv_name);
3796 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
3797
3798 /* do selection */
3799 if (use_face_center) {
3800 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3802 /* assume not touched */
3803 if (select != uvedit_face_select_test(scene, efa, offsets)) {
3804 float cent[2];
3805 BM_face_uv_calc_center_median(efa, offsets.uv, cent);
3806 if (uv_circle_select_is_point_inside(cent, offset, ellipse)) {
3808 changed = true;
3809 }
3810 }
3811 }
3812
3813 /* (de)selects all tagged faces and deals with sticky modes */
3814 if (changed) {
3815 uv_select_flush_from_tag_face(scene, obedit, select);
3816 }
3817 }
3818 else if (use_edge) {
3819 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3820 if (!uvedit_face_visible_test(scene, efa)) {
3821 continue;
3822 }
3823
3824 BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev;
3825 float *luv_prev = BM_ELEM_CD_GET_FLOAT_P(l_prev, offsets.uv);
3826
3827 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3828 luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
3829 if (uv_circle_select_is_edge_inside(luv, luv_prev, offset, ellipse)) {
3830 uvedit_edge_select_set_with_sticky(scene, em, l_prev, select, false, offsets);
3831 changed = true;
3832 }
3833 l_prev = l;
3834 luv_prev = luv;
3835 }
3836 }
3837 }
3838 else {
3840
3841 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3842 if (!uvedit_face_visible_test(scene, efa)) {
3843 continue;
3844 }
3845 bool has_selected = false;
3846 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
3847 if (select != uvedit_uv_select_test(scene, l, offsets)) {
3848 luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
3849 if (uv_circle_select_is_point_inside(luv, offset, ellipse)) {
3850 changed = true;
3851 uvedit_uv_select_set(scene, em->bm, l, select, false, offsets);
3853 has_selected = true;
3854 }
3855 }
3856 }
3857 if (has_selected && use_select_linked) {
3858 UvNearestHit hit = {};
3859 hit.ob = obedit;
3860 hit.efa = efa;
3861 uv_select_linked_multi(scene, objects, &hit, true, !select, false, false);
3862 }
3863 }
3864
3865 if (ts->uv_sticky == SI_STICKY_VERTEX) {
3866 uvedit_vertex_select_tagged(em, scene, select, offsets);
3867 }
3868 }
3869
3870 if (changed || use_pre_deselect) {
3871 changed_multi = true;
3872 if (ts->uv_flag & UV_SYNC_SELECTION) {
3874 }
3875 else {
3876 ED_uvedit_selectmode_flush(scene, em);
3877 }
3879 }
3880 }
3881
3882 return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
3883}
3884
3886{
3887 /* identifiers */
3888 ot->name = "Circle Select";
3889 ot->description = "Select UV vertices using circle selection";
3890 ot->idname = "UV_OT_select_circle";
3891
3892 /* api callbacks */
3896 ot->poll = ED_operator_uvedit_space_image; /* requires space image */
3899
3900 /* flags */
3901 ot->flag = OPTYPE_UNDO;
3902
3903 /* properties */
3906}
3907
3910/* -------------------------------------------------------------------- */
3915 const rcti *clip_rect,
3916 const Span<int2> mcoords,
3917 const float co_test[2])
3918{
3919 int co_screen[2];
3921 &region->v2d, co_test[0], co_test[1], &co_screen[0], &co_screen[1]) &&
3922 BLI_rcti_isect_pt_v(clip_rect, co_screen) &&
3923 BLI_lasso_is_point_inside(mcoords, co_screen[0], co_screen[1], V2D_IS_CLIPPED))
3924 {
3925 return true;
3926 }
3927 return false;
3928}
3929
3931 const rcti *clip_rect,
3932 const Span<int2> mcoords,
3933 const float co_test_a[2],
3934 const float co_test_b[2])
3935{
3936 int co_screen_a[2], co_screen_b[2];
3938 &region->v2d, co_test_a, co_test_b, co_screen_a, co_screen_b) &&
3939 BLI_rcti_isect_segment(clip_rect, co_screen_a, co_screen_b) &&
3941 mcoords, UNPACK2(co_screen_a), UNPACK2(co_screen_b), V2D_IS_CLIPPED))
3942 {
3943 return true;
3944 }
3945 return false;
3946}
3947
3948static bool do_lasso_select_mesh_uv(bContext *C, const Span<int2> mcoords, const eSelectOp sel_op)
3949{
3951 const ARegion *region = CTX_wm_region(C);
3952 Scene *scene = CTX_data_scene(C);
3953 const ToolSettings *ts = scene->toolsettings;
3954 ViewLayer *view_layer = CTX_data_view_layer(C);
3955 const bool use_face_center = ((ts->uv_flag & UV_SYNC_SELECTION) ?
3956 (ts->selectmode == SCE_SELECT_FACE) :
3957 (ts->uv_selectmode == UV_SELECT_FACE));
3958 const bool use_edge = ((ts->uv_flag & UV_SYNC_SELECTION) ?
3959 (ts->selectmode == SCE_SELECT_EDGE) :
3960 (ts->uv_selectmode == UV_SELECT_EDGE));
3961 const bool use_select_linked = !(ts->uv_flag & UV_SYNC_SELECTION) &&
3963
3964 const bool select = (sel_op != SEL_OP_SUB);
3965 const bool use_pre_deselect = SEL_OP_USE_PRE_DESELECT(sel_op);
3966
3967 BMIter iter, liter;
3968
3969 BMFace *efa;
3970 BMLoop *l;
3971 bool changed_multi = false;
3972 rcti rect;
3973
3974 BLI_lasso_boundbox(&rect, mcoords);
3975
3977 scene, view_layer, nullptr);
3978
3979 if (use_pre_deselect) {
3981 }
3982
3983 for (Object *obedit : objects) {
3984
3985 bool changed = false;
3986
3988
3989 const char *active_uv_name = CustomData_get_active_layer_name(&em->bm->ldata, CD_PROP_FLOAT2);
3990 BM_uv_map_ensure_vert_select_attr(em->bm, active_uv_name);
3991 BM_uv_map_ensure_edge_select_attr(em->bm, active_uv_name);
3992 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
3993
3994 if (use_face_center) { /* Face Center Select. */
3995 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
3997 /* assume not touched */
3998 if (select != uvedit_face_select_test(scene, efa, offsets)) {
3999 float cent[2];
4000 BM_face_uv_calc_center_median(efa, offsets.uv, cent);
4001 if (do_lasso_select_mesh_uv_is_point_inside(region, &rect, mcoords, cent)) {
4003 changed = true;
4004 }
4005 }
4006 }
4007
4008 /* (de)selects all tagged faces and deals with sticky modes */
4009 if (changed) {
4010 uv_select_flush_from_tag_face(scene, obedit, select);
4011 }
4012 }
4013 else if (use_edge) {
4014 bool do_second_pass = true;
4015 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
4016 if (!uvedit_face_visible_test(scene, efa)) {
4017 continue;
4018 }
4019
4020 BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev;
4021 float *luv_prev = BM_ELEM_CD_GET_FLOAT_P(l_prev, offsets.uv);
4022
4023 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
4024 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
4025 if (do_lasso_select_mesh_uv_is_point_inside(region, &rect, mcoords, luv) &&
4026 do_lasso_select_mesh_uv_is_point_inside(region, &rect, mcoords, luv_prev))
4027 {
4028 uvedit_edge_select_set_with_sticky(scene, em, l_prev, select, false, offsets);
4029 do_second_pass = false;
4030 changed = true;
4031 }
4032 l_prev = l;
4033 luv_prev = luv;
4034 }
4035 }
4036 /* Do a second pass if no complete edges could be selected.
4037 * This matches wire-frame edit-mesh selection in the 3D view. */
4038 if (do_second_pass) {
4039 /* Second pass to check if edges partially overlap with the selection area (lasso). */
4040 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
4041 if (!uvedit_face_visible_test(scene, efa)) {
4042 continue;
4043 }
4044 BMLoop *l_prev = BM_FACE_FIRST_LOOP(efa)->prev;
4045 float *luv_prev = BM_ELEM_CD_GET_FLOAT_P(l_prev, offsets.uv);
4046
4047 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
4048 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
4049 if (do_lasso_select_mesh_uv_is_edge_inside(region, &rect, mcoords, luv, luv_prev)) {
4050 uvedit_edge_select_set_with_sticky(scene, em, l_prev, select, false, offsets);
4051 changed = true;
4052 }
4053 l_prev = l;
4054 luv_prev = luv;
4055 }
4056 }
4057 }
4058 }
4059 else { /* Vert Selection. */
4061
4062 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
4063 if (!uvedit_face_visible_test(scene, efa)) {
4064 continue;
4065 }
4066 bool has_selected = false;
4067 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
4068 if (select != uvedit_uv_select_test(scene, l, offsets)) {
4069 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, offsets.uv);
4070 if (do_lasso_select_mesh_uv_is_point_inside(region, &rect, mcoords, luv)) {
4071 uvedit_uv_select_set(scene, em->bm, l, select, false, offsets);
4072 changed = true;
4074 has_selected = true;
4075 }
4076 }
4077 }
4078 if (has_selected && use_select_linked) {
4079 UvNearestHit hit = {};
4080 hit.ob = obedit;
4081 hit.efa = efa;
4082 uv_select_linked_multi(scene, objects, &hit, true, !select, false, false);
4083 }
4084 }
4085
4086 if (ts->uv_sticky == SI_STICKY_VERTEX) {
4087 uvedit_vertex_select_tagged(em, scene, select, offsets);
4088 }
4089 }
4090
4091 if (changed || use_pre_deselect) {
4092 changed_multi = true;
4093 if (ts->uv_flag & UV_SYNC_SELECTION) {
4095 }
4096 else {
4097 ED_uvedit_selectmode_flush(scene, em);
4098 }
4100 }
4101 }
4102
4103 return changed_multi;
4104}
4105
4107{
4109 if (mcoords.is_empty()) {
4110 return OPERATOR_PASS_THROUGH;
4111 }
4112
4113 const eSelectOp sel_op = eSelectOp(RNA_enum_get(op->ptr, "mode"));
4114 bool changed = do_lasso_select_mesh_uv(C, mcoords, sel_op);
4115
4116 return changed ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
4117}
4118
4120{
4121 ot->name = "Lasso Select UV";
4122 ot->description = "Select UVs using lasso selection";
4123 ot->idname = "UV_OT_select_lasso";
4124
4130
4131 /* flags */
4133
4134 /* properties */
4137}
4138
4141/* -------------------------------------------------------------------- */
4146{
4147 Scene *scene = CTX_data_scene(C);
4148 const ToolSettings *ts = scene->toolsettings;
4149
4150 /* Use this operator only in vertex mode, since it is not guaranteed that pinned vertices may
4151 * form higher selection states (like edges/faces/islands) in other modes. */
4152 if ((ts->uv_flag & UV_SYNC_SELECTION) == 0) {
4153 if (ts->uv_selectmode != UV_SELECT_VERTEX) {
4154 BKE_report(op->reports, RPT_ERROR, "Pinned vertices can be selected in Vertex Mode only");
4155 return OPERATOR_CANCELLED;
4156 }
4157 }
4158
4160 ViewLayer *view_layer = CTX_data_view_layer(C);
4161 BMFace *efa;
4162 BMLoop *l;
4163 BMIter iter, liter;
4164
4166 scene, view_layer, nullptr);
4167
4168 for (Object *obedit : objects) {
4170
4171 bool changed = false;
4172 const char *active_uv_name = CustomData_get_active_layer_name(&em->bm->ldata, CD_PROP_FLOAT2);
4173 BM_uv_map_ensure_vert_select_attr(em->bm, active_uv_name);
4174 BM_uv_map_ensure_edge_select_attr(em->bm, active_uv_name);
4175 BM_uv_map_ensure_pin_attr(em->bm, active_uv_name);
4176 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
4177
4178 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
4179 if (!uvedit_face_visible_test(scene, efa)) {
4180 continue;
4181 }
4182
4183 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
4184
4185 if (BM_ELEM_CD_GET_BOOL(l, offsets.pin)) {
4186 uvedit_uv_select_enable(scene, em->bm, l, false, offsets);
4187 changed = true;
4188 }
4189 }
4190 }
4191 if ((ts->uv_flag & UV_SYNC_SELECTION) == 0) {
4192 ED_uvedit_selectmode_flush(scene, em);
4193 }
4194
4195 if (changed) {
4197 }
4198 }
4199
4200 return OPERATOR_FINISHED;
4201}
4202
4204{
4205 /* identifiers */
4206 ot->name = "Selected Pinned";
4207 ot->description = "Select all pinned UV vertices";
4208 ot->idname = "UV_OT_select_pinned";
4210
4211 /* api callbacks */
4214}
4215
4218/* -------------------------------------------------------------------- */
4222BLI_INLINE uint overlap_hash(const void *overlap_v)
4223{
4224 const BVHTreeOverlap *overlap = static_cast<const BVHTreeOverlap *>(overlap_v);
4225
4226 /* Designed to treat (A,B) and (B,A) as the same. */
4227 int x = overlap->indexA;
4228 int y = overlap->indexB;
4229 if (x > y) {
4230 std::swap(x, y);
4231 }
4232 return BLI_hash_int_2d(x, y);
4233}
4234
4235BLI_INLINE bool overlap_cmp(const void *a_v, const void *b_v)
4236{
4237 const BVHTreeOverlap *a = static_cast<const BVHTreeOverlap *>(a_v);
4238 const BVHTreeOverlap *b = static_cast<const BVHTreeOverlap *>(b_v);
4239 return !((a->indexA == b->indexA && a->indexB == b->indexB) ||
4240 (a->indexA == b->indexB && a->indexB == b->indexA));
4241}
4242
4246 float tri[3][2];
4247};
4248
4256static bool overlap_tri_tri_uv_test(const float t1[3][2],
4257 const float t2[3][2],
4258 const float endpoint_bias)
4259{
4260 float vi[2];
4261
4262 /* Don't use 'isect_tri_tri_v2' here
4263 * because it's important to ignore overlap at end-points. */
4264 if (isect_seg_seg_v2_point_ex(t1[0], t1[1], t2[0], t2[1], endpoint_bias, vi) == 1 ||
4265 isect_seg_seg_v2_point_ex(t1[0], t1[1], t2[1], t2[2], endpoint_bias, vi) == 1 ||
4266 isect_seg_seg_v2_point_ex(t1[0], t1[1], t2[2], t2[0], endpoint_bias, vi) == 1 ||
4267 isect_seg_seg_v2_point_ex(t1[1], t1[2], t2[0], t2[1], endpoint_bias, vi) == 1 ||
4268 isect_seg_seg_v2_point_ex(t1[1], t1[2], t2[1], t2[2], endpoint_bias, vi) == 1 ||
4269 isect_seg_seg_v2_point_ex(t1[1], t1[2], t2[2], t2[0], endpoint_bias, vi) == 1 ||
4270 isect_seg_seg_v2_point_ex(t1[2], t1[0], t2[0], t2[1], endpoint_bias, vi) == 1 ||
4271 isect_seg_seg_v2_point_ex(t1[2], t1[0], t2[1], t2[2], endpoint_bias, vi) == 1)
4272 {
4273 return true;
4274 }
4275
4276 /* When none of the segments intersect, checking if either of the triangles corners
4277 * is inside the others is almost always sufficient to test if the two triangles intersect.
4278 *
4279 * However, the `endpoint_bias` on segment intersections causes _exact_ overlapping
4280 * triangles not to be detected.
4281 *
4282 * Resolve this problem at the small cost of calculating the triangle center, see #85508. */
4283 mid_v2_v2v2v2(vi, UNPACK3(t1));
4284 if (isect_point_tri_v2(vi, UNPACK3(t2)) != 0) {
4285 return true;
4286 }
4287 mid_v2_v2v2v2(vi, UNPACK3(t2));
4288 if (isect_point_tri_v2(vi, UNPACK3(t1)) != 0) {
4289 return true;
4290 }
4291
4292 return false;
4293}
4294
4295static int uv_select_overlap(bContext *C, const bool extend)
4296{
4298 Scene *scene = CTX_data_scene(C);
4299 ViewLayer *view_layer = CTX_data_view_layer(C);
4300
4302 scene, view_layer, nullptr);
4303
4304 /* Calculate maximum number of tree nodes and prepare initial selection. */
4305 uint uv_tri_len = 0;
4306 for (Object *obedit : objects) {
4308
4312 if (!extend) {
4313 uv_select_all_perform(scene, obedit, SEL_DESELECT);
4314 }
4315
4316 BMIter iter;
4317 BMFace *efa;
4318 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
4319 if (!uvedit_face_visible_test_ex(scene->toolsettings, efa)) {
4320 continue;
4321 }
4322 uv_tri_len += efa->len - 2;
4323 }
4324 }
4325
4326 UVOverlapData *overlap_data = static_cast<UVOverlapData *>(
4327 MEM_mallocN(sizeof(UVOverlapData) * uv_tri_len, "UvOverlapData"));
4328 BVHTree *uv_tree = BLI_bvhtree_new(uv_tri_len, 0.0f, 4, 6);
4329
4330 /* Use a global data index when inserting into the BVH. */
4331 int data_index = 0;
4332
4333 int face_len_alloc = 3;
4334 float(*uv_verts)[2] = static_cast<float(*)[2]>(
4335 MEM_mallocN(sizeof(*uv_verts) * face_len_alloc, "UvOverlapCoords"));
4336 uint(*indices)[3] = static_cast<uint(*)[3]>(
4337 MEM_mallocN(sizeof(*indices) * (face_len_alloc - 2), "UvOverlapTris"));
4338
4341
4342 for (const int ob_index : objects.index_range()) {
4343 Object *obedit = objects[ob_index];
4345 BMIter iter, liter;
4346 BMFace *efa;
4347 BMLoop *l;
4348
4349 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_PROP_FLOAT2);
4350
4351 /* Triangulate each UV face and store it inside the BVH. */
4352 int face_index;
4353 BM_ITER_MESH_INDEX (efa, &iter, em->bm, BM_FACES_OF_MESH, face_index) {
4354
4355 if (!uvedit_face_visible_test_ex(scene->toolsettings, efa)) {
4356 continue;
4357 }
4358
4359 const uint face_len = efa->len;
4360 const uint tri_len = face_len - 2;
4361
4362 if (face_len_alloc < face_len) {
4363 MEM_freeN(uv_verts);
4364 MEM_freeN(indices);
4365 uv_verts = static_cast<float(*)[2]>(
4366 MEM_mallocN(sizeof(*uv_verts) * face_len, "UvOverlapCoords"));
4367 indices = static_cast<uint(*)[3]>(
4368 MEM_mallocN(sizeof(*indices) * tri_len, "UvOverlapTris"));
4369 face_len_alloc = face_len;
4370 }
4371
4372 int vert_index;
4373 BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, vert_index) {
4374 float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
4375 copy_v2_v2(uv_verts[vert_index], luv);
4376 }
4377
4378 /* The UV coordinates winding could be positive of negative,
4379 * determine it automatically. */
4380 const int coords_sign = 0;
4381 BLI_polyfill_calc_arena(uv_verts, face_len, coords_sign, indices, arena);
4382
4383 /* A beauty fill is necessary to remove degenerate triangles that may be produced from the
4384 * above poly-fill (see #103913), otherwise the overlap tests can fail. */
4385 BLI_polyfill_beautify(uv_verts, face_len, indices, arena, heap);
4386
4387 for (int t = 0; t < tri_len; t++) {
4388 overlap_data[data_index].ob_index = ob_index;
4389 overlap_data[data_index].face_index = face_index;
4390
4391 /* BVH needs 3D, overlap data uses 2D. */
4392 const float tri[3][3] = {
4393 {UNPACK2(uv_verts[indices[t][0]]), 0.0f},
4394 {UNPACK2(uv_verts[indices[t][1]]), 0.0f},
4395 {UNPACK2(uv_verts[indices[t][2]]), 0.0f},
4396 };
4397
4398 copy_v2_v2(overlap_data[data_index].tri[0], tri[0]);
4399 copy_v2_v2(overlap_data[data_index].tri[1], tri[1]);
4400 copy_v2_v2(overlap_data[data_index].tri[2], tri[2]);
4401
4402 BLI_bvhtree_insert(uv_tree, data_index, &tri[0][0], 3);
4403 data_index++;
4404 }
4405
4406 BLI_memarena_clear(arena);
4407 BLI_heap_clear(heap, nullptr);
4408 }
4409 }
4410 BLI_assert(data_index == uv_tri_len);
4411
4412 BLI_memarena_free(arena);
4413 BLI_heap_free(heap, nullptr);
4414 MEM_freeN(uv_verts);
4415 MEM_freeN(indices);
4416
4417 BLI_bvhtree_balance(uv_tree);
4418
4419 uint tree_overlap_len;
4420 BVHTreeOverlap *overlap = BLI_bvhtree_overlap_self(uv_tree, &tree_overlap_len, nullptr, nullptr);
4421
4422 if (overlap != nullptr) {
4423 GSet *overlap_set = BLI_gset_new_ex(overlap_hash, overlap_cmp, __func__, tree_overlap_len);
4424
4425 for (int i = 0; i < tree_overlap_len; i++) {
4426 /* Skip overlaps against yourself. */
4427 if (overlap[i].indexA == overlap[i].indexB) {
4428 continue;
4429 }
4430
4431 /* Skip overlaps that have already been tested. */
4432 if (!BLI_gset_add(overlap_set, &overlap[i])) {
4433 continue;
4434 }
4435
4436 const UVOverlapData *o_a = &overlap_data[overlap[i].indexA];
4437 const UVOverlapData *o_b = &overlap_data[overlap[i].indexB];
4438 Object *obedit_a = objects[o_a->ob_index];
4439 Object *obedit_b = objects[o_b->ob_index];
4440 BMEditMesh *em_a = BKE_editmesh_from_object(obedit_a);
4441 BMEditMesh *em_b = BKE_editmesh_from_object(obedit_b);
4442 BMFace *face_a = em_a->bm->ftable[o_a->face_index];
4443 BMFace *face_b = em_b->bm->ftable[o_b->face_index];
4444 const char *uv_a_name = CustomData_get_active_layer_name(&em_a->bm->ldata, CD_PROP_FLOAT2);
4445 const char *uv_b_name = CustomData_get_active_layer_name(&em_b->bm->ldata, CD_PROP_FLOAT2);
4446 BM_uv_map_ensure_vert_select_attr(em_a->bm, uv_a_name);
4447 BM_uv_map_ensure_vert_select_attr(em_b->bm, uv_b_name);
4448 BM_uv_map_ensure_edge_select_attr(em_a->bm, uv_a_name);
4449 BM_uv_map_ensure_edge_select_attr(em_b->bm, uv_b_name);
4450 const BMUVOffsets offsets_a = BM_uv_map_get_offsets(em_a->bm);
4451 const BMUVOffsets offsets_b = BM_uv_map_get_offsets(em_b->bm);
4452
4453 /* Skip if both faces are already selected. */
4454 if (uvedit_face_select_test(scene, face_a, offsets_a) &&
4455 uvedit_face_select_test(scene, face_b, offsets_b))
4456 {
4457 continue;
4458 }
4459
4460 /* Main tri-tri overlap test. */
4461 const float endpoint_bias = -1e-4f;
4462 if (overlap_tri_tri_uv_test(o_a->tri, o_b->tri, endpoint_bias)) {
4463 uvedit_face_select_enable(scene, em_a->bm, face_a, false, offsets_a);
4464 uvedit_face_select_enable(scene, em_b->bm, face_b, false, offsets_b);
4465 }
4466 }
4467
4468 BLI_gset_free(overlap_set, nullptr);
4469 MEM_freeN(overlap);
4470 }
4471
4472 for (Object *object : objects) {
4473 uv_select_tag_update_for_object(depsgraph, scene->toolsettings, object);
4474 }
4475
4476 BLI_bvhtree_free(uv_tree);
4477
4478 MEM_freeN(overlap_data);
4479
4480 return OPERATOR_FINISHED;
4481}
4482
4484{
4485 bool extend = RNA_boolean_get(op->ptr, "extend");
4486 return uv_select_overlap(C, extend);
4487}
4488
4490{
4491 /* identifiers */
4492 ot->name = "Select Overlap";
4493 ot->description = "Select all UV faces which overlap each other";
4494 ot->idname = "UV_OT_select_overlap";
4496
4497 /* api callbacks */
4500
4501 /* properties */
4503 "extend",
4504 false,
4505 "Extend",
4506 "Extend selection rather than clearing the existing selection");
4507}
4508
4513static float get_uv_vert_needle(const eUVSelectSimilar type,
4514 BMVert *vert,
4515 const float ob_m3[3][3],
4516 BMLoop *loop,
4517 const BMUVOffsets offsets)
4518{
4519 BLI_assert(offsets.pin >= 0);
4520 BLI_assert(offsets.uv >= 0);
4521
4522 float result = 0.0f;
4523 switch (type) {
4524 case UV_SSIM_AREA_UV: {
4525 BMFace *f;
4526 BMIter iter;
4527 BM_ITER_ELEM (f, &iter, vert, BM_FACES_OF_VERT) {
4528 result += BM_face_calc_area_uv(f, offsets.uv);
4529 }
4530 break;
4531 }
4532 case UV_SSIM_AREA_3D: {
4533 BMFace *f;
4534 BMIter iter;
4535 BM_ITER_ELEM (f, &iter, vert, BM_FACES_OF_VERT) {
4536 result += BM_face_calc_area_with_mat3(f, ob_m3);
4537 }
4538 break;
4539 }
4540 case UV_SSIM_SIDES: {
4541 BMEdge *e;
4542 BMIter iter;
4543 BM_ITER_ELEM (e, &iter, vert, BM_EDGES_OF_VERT) {
4544 result += 1.0f;
4545 }
4546 break;
4547 }
4548 case UV_SSIM_PIN:
4549 return BM_ELEM_CD_GET_BOOL(loop, offsets.pin) ? 1.0f : 0.0f;
4550 default:
4552 return false;
4553 }
4554
4555 return result;
4556}
4557
4558static float get_uv_edge_needle(const eUVSelectSimilar type,
4559 BMEdge *edge,
4560 const float ob_m3[3][3],
4561 BMLoop *loop_a,
4562 BMLoop *loop_b,
4563 const BMUVOffsets offsets)
4564{
4565 BLI_assert(offsets.pin >= 0);
4566 BLI_assert(offsets.uv >= 0);
4567 float result = 0.0f;
4568 switch (type) {
4569 case UV_SSIM_AREA_UV: {
4570 BMFace *f;
4571 BMIter iter;
4572 BM_ITER_ELEM (f, &iter, edge, BM_FACES_OF_EDGE) {
4573 result += BM_face_calc_area_uv(f, offsets.uv);
4574 }
4575 break;
4576 }
4577 case UV_SSIM_AREA_3D: {
4578 BMFace *f;
4579 BMIter iter;
4580 BM_ITER_ELEM (f, &iter, edge, BM_FACES_OF_EDGE) {
4581 result += BM_face_calc_area_with_mat3(f, ob_m3);
4582 }
4583 break;
4584 }
4585 case UV_SSIM_LENGTH_UV: {
4586 float *luv_a = BM_ELEM_CD_GET_FLOAT_P(loop_a, offsets.uv);
4587 float *luv_b = BM_ELEM_CD_GET_FLOAT_P(loop_b, offsets.uv);
4588 return len_v2v2(luv_a, luv_b);
4589 }
4590 case UV_SSIM_LENGTH_3D:
4591 return len_v3v3(edge->v1->co, edge->v2->co);
4592 case UV_SSIM_SIDES: {
4593 BMEdge *e;
4594 BMIter iter;
4595 BM_ITER_ELEM (e, &iter, edge, BM_FACES_OF_EDGE) {
4596 result += 1.0f;
4597 }
4598 break;
4599 }
4600 case UV_SSIM_PIN: {
4601 if (BM_ELEM_CD_GET_BOOL(loop_a, offsets.pin)) {
4602 result += 1.0f;
4603 }
4604 if (BM_ELEM_CD_GET_BOOL(loop_b, offsets.pin)) {
4605 result += 1.0f;
4606 }
4607 break;
4608 }
4609 default:
4611 return false;
4612 }
4613
4614 return result;
4615}
4616
4617static float get_uv_face_needle(const eUVSelectSimilar type,
4618 BMFace *face,
4619 int ob_index,
4620 const float ob_m3[3][3],
4621 const BMUVOffsets offsets)
4622{
4623 BLI_assert(offsets.pin >= 0);
4624 BLI_assert(offsets.uv >= 0);
4625 float result = 0.0f;
4626 switch (type) {
4627 case UV_SSIM_AREA_UV:
4628 return BM_face_calc_area_uv(face, offsets.uv);
4629 case UV_SSIM_AREA_3D:
4630 return BM_face_calc_area_with_mat3(face, ob_m3);
4631 case UV_SSIM_SIDES:
4632 return face->len;
4633 case UV_SSIM_OBJECT:
4634 return ob_index;
4635 case UV_SSIM_PIN: {
4636 BMLoop *l;
4637 BMIter liter;
4638 BM_ITER_ELEM (l, &liter, face, BM_LOOPS_OF_FACE) {
4639 if (BM_ELEM_CD_GET_BOOL(l, offsets.pin)) {
4640 result += 1.0f;
4641 }
4642 }
4643 break;
4644 }
4645 case UV_SSIM_MATERIAL:
4646 return face->mat_nr;
4647 case UV_SSIM_WINDING:
4648 return signum_i(BM_face_calc_area_uv_signed(face, offsets.uv));
4649 default:
4651 return false;
4652 }
4653 return result;
4654}
4655
4657 const FaceIsland *island,
4658 const float ob_m3[3][3],
4659 const BMUVOffsets offsets)
4660
4661{
4662 BLI_assert(offsets.uv >= 0);
4663 float result = 0.0f;
4664 switch (type) {
4665 case UV_SSIM_AREA_UV:
4666 for (int i = 0; i < island->faces_len; i++) {
4667 result += BM_face_calc_area_uv(island->faces[i], offsets.uv);
4668 }
4669 break;
4670 case UV_SSIM_AREA_3D:
4671 for (int i = 0; i < island->faces_len; i++) {
4672 result += BM_face_calc_area_with_mat3(island->faces[i], ob_m3);
4673 }
4674 break;
4675 case UV_SSIM_FACE:
4676 return island->faces_len;
4677 default:
4679 return false;
4680 }
4681 return result;
4682}
4683
4685{
4686 Scene *scene = CTX_data_scene(C);
4687 ViewLayer *view_layer = CTX_data_view_layer(C);
4690
4691 const eUVSelectSimilar type = eUVSelectSimilar(RNA_enum_get(op->ptr, "type"));
4692 const float threshold = RNA_float_get(op->ptr, "threshold");
4693 const eSimilarCmp compare = eSimilarCmp(RNA_enum_get(op->ptr, "compare"));
4694
4696 scene, view_layer, nullptr);
4697
4698 int max_verts_selected_all = 0;
4699 for (Object *ob : objects) {
4701 BMFace *face;
4702 BMIter iter;
4703 BM_ITER_MESH (face, &iter, em->bm, BM_FACES_OF_MESH) {
4704 if (!uvedit_face_visible_test(scene, face)) {
4705 continue;
4706 }
4707 max_verts_selected_all += face->len;
4708 }
4709 /* TODO: Get a tighter bounds */
4710 }
4711
4712 int tree_index = 0;
4713 KDTree_1d *tree_1d = BLI_kdtree_1d_new(max_verts_selected_all);
4714
4715 for (Object *ob : objects) {
4717 BMesh *bm = em->bm;
4718 if (bm->totvertsel == 0) {
4719 continue;
4720 }
4721
4722 const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
4723 float ob_m3[3][3];
4724 copy_m3_m4(ob_m3, ob->object_to_world().ptr());
4725
4726 BMFace *face;
4727 BMIter iter;
4728 BM_ITER_MESH (face, &iter, em->bm, BM_FACES_OF_MESH) {
4729 if (!uvedit_face_visible_test(scene, face)) {
4730 continue;
4731 }
4732 BMLoop *l;
4733 BMIter liter;
4734 BM_ITER_ELEM (l, &liter, face, BM_LOOPS_OF_FACE) {
4735 if (!uvedit_uv_select_test(scene, l, offsets)) {
4736 continue;
4737 }
4738 float needle = get_uv_vert_needle(type, l->v, ob_m3, l, offsets);
4739 BLI_kdtree_1d_insert(tree_1d, tree_index++, &needle);
4740 }
4741 }
4742 }
4743
4744 if (tree_1d != nullptr) {
4745 BLI_kdtree_1d_deduplicate(tree_1d);
4746 BLI_kdtree_1d_balance(tree_1d);
4747 }
4748
4749 for (Object *ob : objects) {
4751 BMesh *bm = em->bm;
4752 if (bm->totvertsel == 0) {
4753 continue;
4754 }
4755
4756 bool changed = false;
4757
4758 const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
4759 float ob_m3[3][3];
4760 copy_m3_m4(ob_m3, ob->object_to_world().ptr());
4761
4762 BMFace *face;
4763 BMIter iter;
4764 BM_ITER_MESH (face, &iter, em->bm, BM_FACES_OF_MESH) {
4765 if (!uvedit_face_visible_test(scene, face)) {
4766 continue;
4767 }
4768 BMLoop *l;
4769 BMIter liter;
4770 BM_ITER_ELEM (l, &liter, face, BM_LOOPS_OF_FACE) {
4771 if (uvedit_uv_select_test(scene, l, offsets)) {
4772 continue; /* Already selected. */
4773 }
4774 const float needle = get_uv_vert_needle(type, l->v, ob_m3, l, offsets);
4775 bool select = ED_select_similar_compare_float_tree(tree_1d, needle, threshold, compare);
4776 if (select) {
4777 uvedit_uv_select_set(scene, em->bm, l, select, false, offsets);
4778 changed = true;
4779 }
4780 }
4781 if (changed) {
4783 }
4784 }
4785 }
4786
4787 BLI_kdtree_1d_free(tree_1d);
4788 return OPERATOR_FINISHED;
4789}
4790
4792{
4793 Scene *scene = CTX_data_scene(C);
4794 ViewLayer *view_layer = CTX_data_view_layer(C);
4797
4798 const eUVSelectSimilar type = eUVSelectSimilar(RNA_enum_get(op->ptr, "type"));
4799 const float threshold = RNA_float_get(op->ptr, "threshold");
4800 const eSimilarCmp compare = eSimilarCmp(RNA_enum_get(op->ptr, "compare"));
4801
4803 scene, view_layer, nullptr);
4804
4805 int max_edges_selected_all = 0;
4806 for (Object *ob : objects) {
4808 BMFace *face;
4809 BMIter iter;
4810 BM_ITER_MESH (face, &iter, em->bm, BM_FACES_OF_MESH) {
4811 if (!uvedit_face_visible_test(scene, face)) {
4812 continue;
4813 }
4814 max_edges_selected_all += face->len;
4815 }
4816 /* TODO: Get a tighter bounds. */
4817 }
4818
4819 int tree_index = 0;
4820 KDTree_1d *tree_1d = BLI_kdtree_1d_new(max_edges_selected_all);
4821
4822 for (Object *ob : objects) {
4824 BMesh *bm = em->bm;
4825 if (bm->totvertsel == 0) {
4826 continue;
4827 }
4828
4829 const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
4830 float ob_m3[3][3];
4831 copy_m3_m4(ob_m3, ob->object_to_world().ptr());
4832
4833 BMFace *face;
4834 BMIter iter;
4835 BM_ITER_MESH (face, &iter, em->bm, BM_FACES_OF_MESH) {
4836 if (!uvedit_face_visible_test(scene, face)) {
4837 continue;
4838 }
4839 BMLoop *l;
4840 BMIter liter;
4841 BM_ITER_ELEM (l, &liter, face, BM_LOOPS_OF_FACE) {
4842 if (!uvedit_edge_select_test(scene, l, offsets)) {
4843 continue;
4844 }
4845
4846 float needle = get_uv_edge_needle(type, l->e, ob_m3, l, l->next, offsets);
4847 if (tree_1d) {
4848 BLI_kdtree_1d_insert(tree_1d, tree_index++, &needle);
4849 }
4850 }
4851 }
4852 }
4853
4854 if (tree_1d != nullptr) {
4855 BLI_kdtree_1d_deduplicate(tree_1d);
4856 BLI_kdtree_1d_balance(tree_1d);
4857 }
4858
4859 for (Object *ob : objects) {
4861 BMesh *bm = em->bm;
4862 if (bm->totvertsel == 0) {
4863 continue;
4864 }
4865
4866 bool changed = false;
4867 const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
4868 float ob_m3[3][3];
4869 copy_m3_m4(ob_m3, ob->object_to_world().ptr());
4870
4871 BMFace *face;
4872 BMIter iter;
4873 BM_ITER_MESH (face, &iter, em->bm, BM_FACES_OF_MESH) {
4874 if (!uvedit_face_visible_test(scene, face)) {
4875 continue;
4876 }
4877 BMLoop *l;
4878 BMIter liter;
4879 BM_ITER_ELEM (l, &liter, face, BM_LOOPS_OF_FACE) {
4880 if (uvedit_edge_select_test(scene, l, offsets)) {
4881 continue; /* Already selected. */
4882 }
4883
4884 float needle = get_uv_edge_needle(type, l->e, ob_m3, l, l->next, offsets);
4885 bool select = ED_select_similar_compare_float_tree(tree_1d, needle, threshold, compare);
4886 if (select) {
4887 uvedit_edge_select_set(scene, em->bm, l, select, false, offsets);
4888 changed = true;
4889 }
4890 }
4891 if (changed) {
4893 }
4894 }
4895 }
4896
4897 BLI_kdtree_1d_free(tree_1d);
4898 return OPERATOR_FINISHED;
4899}
4900
4902{
4903 Scene *scene = CTX_data_scene(C);
4904 ViewLayer *view_layer = CTX_data_view_layer(C);
4907
4908 const eUVSelectSimilar type = eUVSelectSimilar(RNA_enum_get(op->ptr, "type"));
4909 const float threshold = RNA_float_get(op->ptr, "threshold");
4910 const eSimilarCmp compare = eSimilarCmp(RNA_enum_get(op->ptr, "compare"));
4911
4913 scene, view_layer, nullptr);
4914
4915 int max_faces_selected_all = 0;
4916 for (Object *ob : objects) {
4918 max_faces_selected_all += em->bm->totfacesel;
4919 /* TODO: Get a tighter bounds */
4920 }
4921
4922 int tree_index = 0;
4923 KDTree_1d *tree_1d = BLI_kdtree_1d_new(max_faces_selected_all);
4924
4925 for (const int ob_index : objects.index_range()) {
4926 Object *ob = objects[ob_index];
4928 BMesh *bm = em->bm;
4929
4930 float ob_m3[3][3];
4931 copy_m3_m4(ob_m3, ob->object_to_world().ptr());
4932
4933 const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
4934
4935 BMFace *face;
4936 BMIter iter;
4937 BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) {
4938 if (!uvedit_face_visible_test(scene, face)) {
4939 continue;
4940 }
4941 if (!uvedit_face_select_test(scene, face, offsets)) {
4942 continue;
4943 }
4944
4945 float needle = get_uv_face_needle(type, face, ob_index, ob_m3, offsets);
4946 if (tree_1d) {
4947 BLI_kdtree_1d_insert(tree_1d, tree_index++, &needle);
4948 }
4949 }
4950 }
4951
4952 if (tree_1d != nullptr) {
4953 BLI_kdtree_1d_deduplicate(tree_1d);
4954 BLI_kdtree_1d_balance(tree_1d);
4955 }
4956
4957 for (const int ob_index : objects.index_range()) {
4958 Object *ob = objects[ob_index];
4960 BMesh *bm = em->bm;
4961 bool changed = false;
4962 bool do_history = false;
4963 const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
4964
4965 float ob_m3[3][3];
4966 copy_m3_m4(ob_m3, ob->object_to_world().ptr());
4967
4968 BMFace *face;
4969 BMIter iter;
4970 BM_ITER_MESH (face, &iter, bm, BM_FACES_OF_MESH) {
4971 if (!uvedit_face_visible_test(scene, face)) {
4972 continue;
4973 }
4974 if (uvedit_face_select_test(scene, face, offsets)) {
4975 continue;
4976 }
4977
4978 float needle = get_uv_face_needle(type, face, ob_index, ob_m3, offsets);
4979
4980 bool select = ED_select_similar_compare_float_tree(tree_1d, needle, threshold, compare);
4981 if (select) {
4982 uvedit_face_select_set(scene, bm, face, select, do_history, offsets);
4983 changed = true;
4984 }
4985 }
4986 if (changed) {
4988 }
4989 }
4990
4991 BLI_kdtree_1d_free(tree_1d);
4992 return OPERATOR_FINISHED;
4993}
4994
4995static bool uv_island_selected(const Scene *scene, FaceIsland *island)
4996{
4997 BLI_assert(island && island->faces_len);
4998 return uvedit_face_select_test(scene, island->faces[0], island->offsets);
4999}
5000
5002{
5003 Scene *scene = CTX_data_scene(C);
5004 ViewLayer *view_layer = CTX_data_view_layer(C);
5007
5008 const eUVSelectSimilar type = eUVSelectSimilar(RNA_enum_get(op->ptr, "type"));
5009 const float threshold = RNA_float_get(op->ptr, "threshold");
5010 const eSimilarCmp compare = eSimilarCmp(RNA_enum_get(op->ptr, "compare"));
5011
5013 scene, view_layer, nullptr);
5014
5015 ListBase *island_list_ptr = static_cast<ListBase *>(
5016 MEM_callocN(sizeof(*island_list_ptr) * objects.size(), __func__));
5017 int island_list_len = 0;
5018
5019 const bool face_selected = !(scene->toolsettings->uv_flag & UV_SYNC_SELECTION);
5020
5021 for (const int ob_index : objects.index_range()) {
5022 Object *obedit = objects[ob_index];
5024 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
5025 if (offsets.uv == -1) {
5026 continue;
5027 }
5028
5029 float aspect_y = 1.0f; /* Placeholder value, aspect doesn't change connectivity. */
5030 island_list_len += bm_mesh_calc_uv_islands(
5031 scene, em->bm, &island_list_ptr[ob_index], face_selected, false, false, aspect_y, offsets);
5032 }
5033
5034 FaceIsland **island_array = static_cast<FaceIsland **>(
5035 MEM_callocN(sizeof(*island_array) * island_list_len, __func__));
5036
5037 int tree_index = 0;
5038 KDTree_1d *tree_1d = BLI_kdtree_1d_new(island_list_len);
5039
5040 for (const int ob_index : objects.index_range()) {
5041 Object *obedit = objects[ob_index];
5043 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_PROP_FLOAT2);
5044 if (cd_loop_uv_offset == -1) {
5045 continue;
5046 }
5047
5048 float ob_m3[3][3];
5049 copy_m3_m4(ob_m3, obedit->object_to_world().ptr());
5050
5051 int index;
5052 LISTBASE_FOREACH_INDEX (FaceIsland *, island, &island_list_ptr[ob_index], index) {
5053 island_array[index] = island;
5054 if (!uv_island_selected(scene, island)) {
5055 continue;
5056 }
5057 float needle = get_uv_island_needle(type, island, ob_m3, island->offsets);
5058 if (tree_1d) {
5059 BLI_kdtree_1d_insert(tree_1d, tree_index++, &needle);
5060 }
5061 }
5062 }
5063
5064 if (tree_1d != nullptr) {
5065 BLI_kdtree_1d_deduplicate(tree_1d);
5066 BLI_kdtree_1d_balance(tree_1d);
5067 }
5068
5069 int tot_island_index = 0;
5070 for (const int ob_index : objects.index_range()) {
5071 Object *obedit = objects[ob_index];
5073 const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_PROP_FLOAT2);
5074 if (cd_loop_uv_offset == -1) {
5075 continue;
5076 }
5077 float ob_m3[3][3];
5078 copy_m3_m4(ob_m3, obedit->object_to_world().ptr());
5079
5080 bool changed = false;
5081 int index;
5082 LISTBASE_FOREACH_INDEX (FaceIsland *, island, &island_list_ptr[ob_index], index) {
5083 island_array[tot_island_index++] = island; /* To deallocate later. */
5084 if (uv_island_selected(scene, island)) {
5085 continue;
5086 }
5087 float needle = get_uv_island_needle(type, island, ob_m3, island->offsets);
5088 bool select = ED_select_similar_compare_float_tree(tree_1d, needle, threshold, compare);
5089 if (!select) {
5090 continue;
5091 }
5092 bool do_history = false;
5093 for (int j = 0; j < island->faces_len; j++) {
5095 scene, em->bm, island->faces[j], select, do_history, island->offsets);
5096 }
5097 changed = true;
5098 }
5099
5100 if (changed) {
5102 }
5103 }
5104
5105 BLI_assert(tot_island_index == island_list_len);
5106 for (int i = 0; i < island_list_len; i++) {
5107 MEM_SAFE_FREE(island_array[i]->faces);
5108 MEM_SAFE_FREE(island_array[i]);
5109 }
5110
5111 MEM_SAFE_FREE(island_array);
5112 MEM_SAFE_FREE(island_list_ptr);
5113 BLI_kdtree_1d_free(tree_1d);
5114
5115 return OPERATOR_FINISHED;
5116}
5117
5118/* Select similar UV faces/edges/verts based on current selection. */
5120{
5122 PropertyRNA *prop = RNA_struct_find_property(op->ptr, "threshold");
5123
5124 if (!RNA_property_is_set(op->ptr, prop)) {
5126 }
5127 else {
5128 ts->select_thresh = RNA_property_float_get(op->ptr, prop);
5129 }
5130
5131 int selectmode = (ts->uv_flag & UV_SYNC_SELECTION) ? ts->selectmode : ts->uv_selectmode;
5132 if (selectmode & UV_SELECT_EDGE) {
5133 return uv_select_similar_edge_exec(C, op);
5134 }
5135 if (selectmode & UV_SELECT_FACE) {
5136 return uv_select_similar_face_exec(C, op);
5137 }
5138 if (selectmode & UV_SELECT_ISLAND) {
5139 return uv_select_similar_island_exec(C, op);
5140 }
5141
5142 return uv_select_similar_vert_exec(C, op);
5143}
5144
5146 {UV_SSIM_PIN, "PIN", 0, "Pinned", ""},
5147 {UV_SSIM_LENGTH_UV, "LENGTH", 0, "Length", ""},
5148 {UV_SSIM_LENGTH_3D, "LENGTH_3D", 0, "Length 3D", ""},
5149 {UV_SSIM_AREA_UV, "AREA", 0, "Area", ""},
5150 {UV_SSIM_AREA_3D, "AREA_3D", 0, "Area 3D", ""},
5151 {UV_SSIM_MATERIAL, "MATERIAL", 0, "Material", ""},
5152 {UV_SSIM_OBJECT, "OBJECT", 0, "Object", ""},
5153 {UV_SSIM_SIDES, "SIDES", 0, "Polygon Sides", ""},
5154 {UV_SSIM_WINDING, "WINDING", 0, "Winding", ""},
5155 {UV_SSIM_FACE, "FACE", 0, "Amount of Faces in Island", ""},
5156 {0}};
5157
5158static EnumPropertyItem prop_similar_compare_types[] = {{SIM_CMP_EQ, "EQUAL", 0, "Equal", ""},
5159 {SIM_CMP_GT, "GREATER", 0, "Greater", ""},
5160 {SIM_CMP_LT, "LESS", 0, "Less", ""},
5161 {0}};
5162
5164 PointerRNA * /*ptr*/,
5165 PropertyRNA * /*prop*/,
5166 bool *r_free)
5167{
5168 EnumPropertyItem *item = nullptr;
5169 int totitem = 0;
5170
5171 const ToolSettings *ts = CTX_data_tool_settings(C);
5172 if (ts) {
5173 int selectmode = (ts->uv_flag & UV_SYNC_SELECTION) ? ts->selectmode : ts->uv_selectmode;
5174 if (selectmode & UV_SELECT_VERTEX) {
5176 }
5177 else if (selectmode & UV_SELECT_EDGE) {
5181 }
5182 else if (selectmode & UV_SELECT_FACE) {
5189 }
5190 else if (selectmode & UV_SELECT_ISLAND) {
5194 }
5195 }
5196 else {
5198 }
5199
5200 RNA_enum_item_end(&item, &totitem);
5201 *r_free = true;
5202 return item;
5203}
5204
5206{
5207 /* identifiers */
5208 ot->name = "Select Similar";
5209 ot->description = "Select similar UVs by property types";
5210 ot->idname = "UV_OT_select_similar";
5211
5212 /* api callbacks */
5216
5217 /* flags */
5219
5220 /* properties */
5221 PropertyRNA *prop = ot->prop = RNA_def_enum(
5222 ot->srna, "type", uv_select_similar_type_items, SIMVERT_NORMAL, "Type", "");
5225 RNA_def_enum(ot->srna, "compare", prop_similar_compare_types, SIM_CMP_EQ, "Compare", "");
5226 RNA_def_float(ot->srna, "threshold", 0.0f, 0.0f, 1.0f, "Threshold", "", 0.0f, 1.0f);
5227}
5228
5231/* -------------------------------------------------------------------- */
5238BMFace **ED_uvedit_selected_faces(const Scene *scene, BMesh *bm, int len_max, int *r_faces_len)
5239{
5240 const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
5241
5242 CLAMP_MAX(len_max, bm->totface);
5243 int faces_len = 0;
5244 BMFace **faces = static_cast<BMFace **>(MEM_mallocN(sizeof(*faces) * len_max, __func__));
5245
5246 BMIter iter;
5247 BMFace *f;
5248 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
5249 if (uvedit_face_visible_test(scene, f)) {
5250 if (uvedit_face_select_test(scene, f, offsets)) {
5251 faces[faces_len++] = f;
5252 if (faces_len == len_max) {
5253 goto finally;
5254 }
5255 }
5256 }
5257 }
5258
5259finally:
5260 *r_faces_len = faces_len;
5261 if (faces_len != len_max) {
5262 faces = static_cast<BMFace **>(MEM_reallocN(faces, sizeof(*faces) * faces_len));
5263 }
5264 return faces;
5265}
5266
5267BMLoop **ED_uvedit_selected_edges(const Scene *scene, BMesh *bm, int len_max, int *r_edges_len)
5268{
5269 const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
5270 BLI_assert(offsets.uv >= 0);
5271
5272 CLAMP_MAX(len_max, bm->totloop);
5273 int edges_len = 0;
5274 BMLoop **edges = static_cast<BMLoop **>(MEM_mallocN(sizeof(*edges) * len_max, __func__));
5275
5276 BMIter iter;
5277 BMFace *f;
5278
5279 /* Clear tag. */
5280 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
5281 BMIter liter;
5282 BMLoop *l_iter;
5283 BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
5285 }
5286 }
5287
5288 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
5289 if (uvedit_face_visible_test(scene, f)) {
5290 BMIter liter;
5291 BMLoop *l_iter;
5292 BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
5293 if (!BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
5294 if (uvedit_edge_select_test(scene, l_iter, offsets)) {
5296
5297 edges[edges_len++] = l_iter;
5298 if (edges_len == len_max) {
5299 goto finally;
5300 }
5301
5302 /* Tag other connected loops so we don't consider them separate edges. */
5303 if (l_iter != l_iter->radial_next) {
5304 BMLoop *l_radial_iter = l_iter->radial_next;
5305 do {
5306 if (BM_loop_uv_share_edge_check(l_iter, l_radial_iter, offsets.uv)) {
5307 BM_elem_flag_enable(l_radial_iter, BM_ELEM_TAG);
5308 }
5309 } while ((l_radial_iter = l_radial_iter->radial_next) != l_iter);
5310 }
5311 }
5312 }
5313 }
5314 }
5315 }
5316
5317finally:
5318 *r_edges_len = edges_len;
5319 if (edges_len != len_max) {
5320 edges = static_cast<BMLoop **>(MEM_reallocN(edges, sizeof(*edges) * edges_len));
5321 }
5322 return edges;
5323}
5324
5325BMLoop **ED_uvedit_selected_verts(const Scene *scene, BMesh *bm, int len_max, int *r_verts_len)
5326{
5327 const BMUVOffsets offsets = BM_uv_map_get_offsets(bm);
5328 BLI_assert(offsets.select_vert >= 0);
5329 BLI_assert(offsets.uv >= 0);
5330
5331 CLAMP_MAX(len_max, bm->totloop);
5332 int verts_len = 0;
5333 BMLoop **verts = static_cast<BMLoop **>(MEM_mallocN(sizeof(*verts) * len_max, __func__));
5334
5335 BMIter iter;
5336 BMFace *f;
5337
5338 /* Clear tag. */
5339 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
5340 BMIter liter;
5341 BMLoop *l_iter;
5342 BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
5344 }
5345 }
5346
5347 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
5348 if (uvedit_face_visible_test(scene, f)) {
5349 BMIter liter;
5350 BMLoop *l_iter;
5351 BM_ITER_ELEM (l_iter, &liter, f, BM_LOOPS_OF_FACE) {
5352 if (!BM_elem_flag_test(l_iter, BM_ELEM_TAG)) {
5353 if (BM_ELEM_CD_GET_BOOL(l_iter, offsets.select_vert)) {
5355
5356 verts[verts_len++] = l_iter;
5357 if (verts_len == len_max) {
5358 goto finally;
5359 }
5360
5361 /* Tag other connected loops so we don't consider them separate vertices. */
5362 BMIter liter_disk;
5363 BMLoop *l_disk_iter;
5364 BM_ITER_ELEM (l_disk_iter, &liter_disk, l_iter->v, BM_LOOPS_OF_VERT) {
5365 if (BM_loop_uv_share_vert_check(l_iter, l_disk_iter, offsets.uv)) {
5366 BM_elem_flag_enable(l_disk_iter, BM_ELEM_TAG);
5367 }
5368 }
5369 }
5370 }
5371 }
5372 }
5373 }
5374
5375finally:
5376 *r_verts_len = verts_len;
5377 if (verts_len != len_max) {
5378 verts = static_cast<BMLoop **>(MEM_reallocN(verts, sizeof(*verts) * verts_len));
5379 }
5380 return verts;
5381}
5382
5385/* -------------------------------------------------------------------- */
5394static void uv_isolate_selected_islands(const Scene *scene,
5395 BMEditMesh *em,
5396 const BMUVOffsets offsets)
5397{
5398 BLI_assert((scene->toolsettings->uv_flag & UV_SYNC_SELECTION) == 0);
5399 BMFace *efa;
5400 BMIter iter, liter;
5401 UvElementMap *elementmap = BM_uv_element_map_create(em->bm, scene, false, false, true, true);
5402 if (elementmap == nullptr) {
5403 return;
5404 }
5405 BLI_assert(offsets.select_vert >= 0);
5406 BLI_assert(offsets.select_edge >= 0);
5407
5408 int num_islands = elementmap->total_islands;
5409 /* Boolean array that tells if island with index i is completely selected or not. */
5410 bool *is_island_not_selected = static_cast<bool *>(
5411 MEM_callocN(sizeof(bool) * (num_islands), __func__));
5412
5413 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
5414 BMLoop *l;
5415 if (!uvedit_face_visible_test(scene, efa)) {
5417 continue;
5418 }
5420 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
5421 if (!uvedit_edge_select_test(scene, l, offsets)) {
5422 UvElement *element = BM_uv_element_get(elementmap, l);
5423 if (element) {
5424 is_island_not_selected[element->island] = true;
5425 }
5426 }
5427 }
5428 }
5429
5430 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
5431 BMLoop *l;
5432 if (!BM_elem_flag_test(efa, BM_ELEM_TAG)) {
5433 continue;
5434 }
5435 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
5436 UvElement *element = BM_uv_element_get(elementmap, l);
5437 /* Deselect all elements of islands which are not completely selected. */
5438 if (element && is_island_not_selected[element->island]) {
5439 BM_ELEM_CD_SET_BOOL(l, offsets.select_vert, false);
5440 BM_ELEM_CD_SET_BOOL(l, offsets.select_edge, false);
5441 }
5442 }
5443 }
5444
5445 BM_uv_element_map_free(elementmap);
5446 MEM_freeN(is_island_not_selected);
5447}
5448
5449void ED_uvedit_selectmode_clean(const Scene *scene, Object *obedit)
5450{
5451 const ToolSettings *ts = scene->toolsettings;
5452 BLI_assert((ts->uv_flag & UV_SYNC_SELECTION) == 0);
5454 char sticky = ts->uv_sticky;
5455 const char *active_uv_name = CustomData_get_active_layer_name(&em->bm->ldata, CD_PROP_FLOAT2);
5456 BM_uv_map_ensure_vert_select_attr(em->bm, active_uv_name);
5457 BM_uv_map_ensure_edge_select_attr(em->bm, active_uv_name);
5458 const BMUVOffsets offsets = BM_uv_map_get_offsets(em->bm);
5459 BMFace *efa;
5460 BMLoop *l;
5461 BMIter iter, liter;
5462
5463 if (ts->uv_selectmode == UV_SELECT_VERTEX) {
5464 /* Vertex mode. */
5465 if (sticky != SI_STICKY_DISABLE) {
5467 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
5468 if (!uvedit_face_visible_test(scene, efa)) {
5469 continue;
5470 }
5471 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
5472 if (uvedit_uv_select_test(scene, l, offsets)) {
5474 }
5475 }
5476 }
5477 uv_select_flush_from_tag_loop(scene, obedit, true);
5478 }
5479 }
5480
5481 else if (ts->uv_selectmode == UV_SELECT_EDGE) {
5482 /* Edge mode. */
5483 if (sticky != SI_STICKY_DISABLE) {
5484 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
5485 if (!uvedit_face_visible_test(scene, efa)) {
5486 continue;
5487 }
5488 BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
5489 if (uvedit_edge_select_test(scene, l, offsets)) {
5490 uvedit_edge_select_set_noflush(scene, l, true, sticky, offsets);
5491 }
5492 }
5493 }
5494 }
5496 }
5497
5498 else if (ts->uv_selectmode == UV_SELECT_FACE) {
5499 /* Face mode. */
5500 BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
5502 if (uvedit_face_visible_test(scene, efa)) {
5503 if (uvedit_face_select_test(scene, efa, offsets)) {
5505 }
5506 uvedit_face_select_set(scene, em->bm, efa, false, false, offsets);
5507 }
5508 }
5509 uv_select_flush_from_tag_face(scene, obedit, true);
5510 }
5511
5512 else if (ts->uv_selectmode == UV_SELECT_ISLAND) {
5513 /* Island mode. */
5514 uv_isolate_selected_islands(scene, em, offsets);
5515 }
5516
5517 ED_uvedit_selectmode_flush(scene, em);
5518}
5519
5521{
5522 Scene *scene = CTX_data_scene(C);
5523 ViewLayer *view_layer = CTX_data_view_layer(C);
5525
5527 scene, view_layer, nullptr);
5528 for (Object *obedit : objects) {
5529 ED_uvedit_selectmode_clean(scene, obedit);
5530
5531 uv_select_tag_update_for_object(depsgraph, scene->toolsettings, obedit);
5532 }
5533}
5534
5536{
5537 Scene *scene = CTX_data_scene(C);
5538 ToolSettings *ts = scene->toolsettings;
5539 const char new_uv_selectmode = RNA_enum_get(op->ptr, "type");
5540
5541 /* Early exit if no change in current selection mode */
5542 if (new_uv_selectmode == ts->uv_selectmode) {
5543 return OPERATOR_CANCELLED;
5544 }
5545
5546 /* Set new UV select mode. */
5547 ts->uv_selectmode = new_uv_selectmode;
5548
5549 /* Handle UV selection states according to new select mode and sticky mode. */
5551
5554
5555 return OPERATOR_FINISHED;
5556}
5557
5558static int uv_select_mode_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
5559{
5560 const ToolSettings *ts = CTX_data_tool_settings(C);
5561 const SpaceImage *sima = CTX_wm_space_image(C);
5562
5563 /* Could be removed? - Already done in poll callback. */
5564 if ((!sima) || (sima->mode != SI_MODE_UV)) {
5565 return OPERATOR_CANCELLED;
5566 }
5567 /* Pass through when UV sync selection is enabled.
5568 * Allow for mesh select-mode key-map. */
5569 if (ts->uv_flag & UV_SYNC_SELECTION) {
5570 return OPERATOR_PASS_THROUGH;
5571 }
5572
5573 return uv_select_mode_exec(C, op);
5574}
5575
5577{
5578 /* identifiers */
5579 ot->name = "UV Select Mode";
5580 ot->description = "Change UV selection mode";
5581 ot->idname = "UV_OT_select_mode";
5582
5583 /* api callbacks */
5587
5588 /* flags */
5590
5591 /* RNA props */
5592 PropertyRNA *prop;
5593 ot->prop = prop = RNA_def_enum(
5594 ot->srna, "type", rna_enum_mesh_select_mode_uv_items, 0, "Type", "");
5596}
5597
SpaceImage * CTX_wm_space_image(const bContext *C)
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
ToolSettings * CTX_data_tool_settings(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
CustomData interface, see also DNA_customdata_types.h.
int CustomData_get_offset(const CustomData *data, eCustomDataType type)
const char * CustomData_get_active_layer_name(const CustomData *data, eCustomDataType type)
BMEditMesh * BKE_editmesh_from_object(Object *ob)
Return the BMEditMesh for a given object.
Definition editmesh.cc:63
blender::Vector< Object * > BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(const Scene *scene, ViewLayer *view_layer, const View3D *v3d)
General operations, lookup, etc. for materials.
void BKE_mesh_batch_cache_dirty_tag(Mesh *mesh, eMeshBatchDirtyMode mode)
@ BKE_MESH_BATCH_DIRTY_UVEDIT_SELECT
Definition BKE_mesh.h:43
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_INLINE
struct GSet GSet
Definition BLI_ghash.h:341
GSet * BLI_gset_new_ex(GSetHashFP hashfp, GSetCmpFP cmpfp, const char *info, unsigned int nentries_reserve) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
Definition BLI_ghash.c:936
void BLI_gset_free(GSet *gs, GSetKeyFreeFP keyfreefp)
Definition BLI_ghash.c:1034
bool BLI_gset_add(GSet *gs, void *key)
Definition BLI_ghash.c:966
BLI_INLINE unsigned int BLI_hash_int_2d(unsigned int kx, unsigned int ky)
Definition BLI_hash.h:55
A min-heap / priority queue ADT.
void BLI_heap_free(Heap *heap, HeapFreeFP ptrfreefp) ATTR_NONNULL(1)
Definition BLI_heap.c:192
Heap * BLI_heap_new_ex(unsigned int reserve_num) ATTR_WARN_UNUSED_RESULT
Definition BLI_heap.c:172
void BLI_heap_clear(Heap *heap, HeapFreeFP ptrfreefp) ATTR_NONNULL(1)
Definition BLI_heap.c:214
BVHTree * BLI_bvhtree_new(int maxsize, float epsilon, char tree_type, char axis)
void BLI_bvhtree_balance(BVHTree *tree)
void BLI_bvhtree_free(BVHTree *tree)
void BLI_bvhtree_insert(BVHTree *tree, int index, const float co[3], int numpoints)
BVHTreeOverlap * BLI_bvhtree_overlap_self(const BVHTree *tree, unsigned int *r_overlap_num, BVHTree_OverlapCallback callback, void *userdata)
A KD-tree for nearest neighbor search.
bool BLI_lasso_is_point_inside(blender::Span< blender::int2 > mcoords, int sx, int sy, int error_value)
void BLI_lasso_boundbox(rcti *rect, blender::Span< blender::int2 > mcoords)
bool BLI_lasso_is_edge_inside(blender::Span< blender::int2 > mcoords, int x0, int y0, int x1, int y1, int error_value)
#define LISTBASE_FOREACH_INDEX(type, var, list, index_var)
MINLINE float square_f(float a)
MINLINE int signum_i(float a)
int isect_seg_seg_v2_point_ex(const float v0[2], const float v1[2], const float v2[2], const float v3[2], float endpoint_bias, float r_vi[2])
float closest_to_line_segment_v2(float r_close[2], const float p[2], const float l1[2], const float l2[2])
Definition math_geom.cc:363
int isect_point_tri_v2(const float pt[2], const float v1[2], const float v2[2], const float v3[2])
float dist_squared_to_line_segment_v2(const float p[2], const float l1[2], const float l2[2])
Definition math_geom.cc:289
void copy_m3_m4(float m1[3][3], const float m2[4][4])
MINLINE float len_squared_v2(const float v[2]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE float len_squared_v2v2(const float a[2], const float b[2]) ATTR_WARN_UNUSED_RESULT
MINLINE void mul_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE void sub_v2_v2(float r[2], const float a[2])
MINLINE void mul_v2_v2(float r[2], const float a[2])
MINLINE float len_v2v2(const float v1[2], const float v2[2]) ATTR_WARN_UNUSED_RESULT
void mid_v2_v2v2v2(float v[2], const float v1[2], const float v2[2], const float v3[2])
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void sub_v2_v2v2(float r[2], const float a[2], const float b[2])
MINLINE float line_point_side_v2(const float l1[2], const float l2[2], const float pt[2]) ATTR_WARN_UNUSED_RESULT
void BLI_memarena_free(struct MemArena *ma) ATTR_NONNULL(1)
struct MemArena * BLI_memarena_new(size_t bufsize, const char *name) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(2) ATTR_MALLOC
#define BLI_MEMARENA_STD_BUFSIZE
void void BLI_memarena_clear(MemArena *ma) ATTR_NONNULL(1)
void BLI_polyfill_calc_arena(const float(*coords)[2], unsigned int coords_num, int coords_sign, unsigned int(*r_tris)[3], struct MemArena *arena)
#define BLI_POLYFILL_ALLOC_NGON_RESERVE
void BLI_polyfill_beautify(const float(*coords)[2], unsigned int coords_num, unsigned int(*tris)[3], struct MemArena *arena, struct Heap *eheap)
bool BLI_rcti_isect_pt_v(const struct rcti *rect, const int xy[2])
bool BLI_rctf_isect_pt_v(const struct rctf *rect, const float xy[2])
bool BLI_rcti_isect_segment(const struct rcti *rect, const int s1[2], const int s2[2])
bool BLI_rctf_isect_segment(const struct rctf *rect, const float s1[2], const float s2[2])
unsigned int uint
#define UNPACK2(a)
#define CLAMP_MAX(a, c)
#define ARRAY_SIZE(arr)
#define UNUSED_VARS_NDEBUG(...)
#define UNPACK3(a)
#define ELEM(...)
#define BLT_I18NCONTEXT_ID_MESH
void DEG_id_tag_update(ID *id, unsigned int flags)
Object * DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object)
@ ID_RECALC_SELECT
Definition DNA_ID.h:1068
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1085
@ CD_PROP_FLOAT2
Object is a sort of wrapper for general info.
@ SI_STICKY_VERTEX
@ SI_STICKY_LOC
@ SI_STICKY_DISABLE
@ UV_SELECT_VERTEX
@ UV_SELECT_FACE
@ UV_SELECT_EDGE
@ UV_SELECT_ISLAND
@ SCE_SELECT_FACE
@ SCE_SELECT_VERTEX
@ SCE_SELECT_EDGE
@ UV_SYNC_SELECTION
@ SI_MODE_UV
@ OPERATOR_PASS_THROUGH
void ED_space_image_get_size(SpaceImage *sima, int *r_width, int *r_height)
void ED_space_image_get_zoom(SpaceImage *sima, const ARegion *region, float *r_zoomx, float *r_zoomy)
UvVertMap * BM_uv_vert_map_create(BMesh *bm, bool use_select)
void EDBM_flag_disable_all(BMEditMesh *em, char hflag)
UvElement * BM_uv_element_get(const UvElementMap *element_map, const BMLoop *l)
void EDBM_select_toggle_all(BMEditMesh *em)
void EDBM_select_more(BMEditMesh *em, bool use_face_step)
void EDBM_deselect_flush(BMEditMesh *em)
void EDBM_select_swap(BMEditMesh *em)
void BM_uv_element_map_free(UvElementMap *element_map)
UvElementMap * BM_uv_element_map_create(BMesh *bm, const Scene *scene, bool uv_selected, bool use_winding, bool use_seams, bool do_islands)
void EDBM_selectmode_flush(BMEditMesh *em)
UvMapVert * BM_uv_vert_map_at_index(UvVertMap *vmap, unsigned int v)
void EDBM_flag_enable_all(BMEditMesh *em, char hflag)
void EDBM_select_less(BMEditMesh *em, bool use_face_step)
void EDBM_select_flush(BMEditMesh *em)
void BM_uv_vert_map_free(UvVertMap *vmap)
bool ED_operator_uvedit_space_image(bContext *C)
bool ED_operator_uvedit(bContext *C)
eSelectOp ED_select_op_modal(eSelectOp sel_op, bool is_first)
#define SEL_OP_USE_PRE_DESELECT(sel_op)
std::string ED_select_circle_get_name(wmOperatorType *ot, PointerRNA *ptr)
@ SEL_SELECT
@ SEL_INVERT
@ SEL_DESELECT
@ SEL_TOGGLE
void ED_select_pick_params_from_operator(PointerRNA *ptr, SelectPick_Params *params) ATTR_NONNULL(1
eSimilarCmp
@ SIM_CMP_LT
@ SIM_CMP_GT
@ SIM_CMP_EQ
bool ED_select_similar_compare_float_tree(const KDTree_1d *tree, float length, float thresh, eSimilarCmp compare)
void std::string ED_select_pick_get_name(wmOperatorType *ot, PointerRNA *ptr)
eSelectOp
@ SEL_OP_ADD
@ SEL_OP_SUB
@ SEL_OP_SET
@ SEL_OP_AND
@ SEL_OP_XOR
int bm_mesh_calc_uv_islands(const Scene *scene, BMesh *bm, ListBase *island_list, const bool only_selected_faces, const bool only_selected_uvs, const bool use_seams, const float aspect_y, BMUVOffsets offsets)
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
#define MEM_reallocN(vmemh, len)
PropertyFlag
Definition RNA_types.hh:201
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
@ PROP_HIDDEN
Definition RNA_types.hh:239
bool UI_view2d_view_to_region_segment_clip(const View2D *v2d, const float xy_a[2], const float xy_b[2], int r_region_a[2], int r_region_b[2]) ATTR_NONNULL()
Definition view2d.cc:1746
bool UI_view2d_view_to_region_clip(const View2D *v2d, float x, float y, int *r_region_x, int *r_region_y) ATTR_NONNULL()
Definition view2d.cc:1697
void UI_view2d_view_to_region_fl(const View2D *v2d, float x, float y, float *r_region_x, float *r_region_y) ATTR_NONNULL()
Definition view2d.cc:1734
float UI_view2d_scale_get_y(const View2D *v2d)
Definition view2d.cc:1920
void UI_view2d_scale_get(const View2D *v2d, float *r_x, float *r_y)
Definition view2d.cc:1907
void UI_view2d_region_to_view(const View2D *v2d, float x, float y, float *r_view_x, float *r_view_y) ATTR_NONNULL()
Definition view2d.cc:1663
#define V2D_IS_CLIPPED
Definition UI_view2d.hh:21
void UI_view2d_region_to_view_rctf(const View2D *v2d, const rctf *rect_src, rctf *rect_dst) ATTR_NONNULL()
Definition view2d.cc:1670
float UI_view2d_scale_get_x(const View2D *v2d)
Definition view2d.cc:1916
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:198
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_GEOM
Definition WM_types.hh:360
#define NC_SCENE
Definition WM_types.hh:345
#define ND_TOOLSETTINGS
Definition WM_types.hh:416
#define ND_SPACE_IMAGE
Definition WM_types.hh:488
#define ND_SELECT
Definition WM_types.hh:474
#define NC_SPACE
Definition WM_types.hh:359
#define BM_ELEM_CD_GET_BOOL(ele, offset)
#define BM_DISK_EDGE_NEXT(e, v)
@ BM_ELEM_HIDDEN
@ BM_ELEM_SELECT
@ BM_ELEM_TAG
#define BM_FACE_FIRST_LOOP(p)
#define BM_ELEM_CD_GET_FLOAT_P(ele, offset)
#define BM_ELEM_CD_SET_BOOL(ele, offset, f)
#define BM_elem_index_get(ele)
#define BM_elem_flag_disable(ele, hflag)
#define BM_elem_flag_test(ele, hflag)
#define BM_elem_flag_test_bool(ele, hflag)
#define BM_elem_flag_enable(ele, hflag)
void BM_uv_map_ensure_pin_attr(BMesh *bm, const char *uv_map_name)
void BM_uv_map_ensure_vert_select_attr(BMesh *bm, const char *uv_map_name)
void BM_uv_map_ensure_edge_select_attr(BMesh *bm, const char *uv_map_name)
void * BM_iter_at_index(BMesh *bm, const char itype, void *data, int index)
#define BM_ITER_ELEM(ele, iter, data, itype)
#define BM_ITER_MESH(ele, iter, bm, itype)
#define BM_ITER_MESH_INDEX(ele, iter, bm, itype, indexvar)
@ BM_FACES_OF_EDGE
@ BM_FACES_OF_VERT
@ BM_FACES_OF_MESH
@ BM_LOOPS_OF_VERT
@ BM_LOOPS_OF_EDGE
@ BM_EDGES_OF_VERT
@ BM_LOOPS_OF_FACE
#define BM_ITER_ELEM_INDEX(ele, iter, data, itype, indexvar)
ATTR_WARN_UNUSED_RESULT BMesh * bm
void BM_select_history_clear(BMesh *bm)
void BM_face_select_set(BMesh *bm, BMFace *f, const bool select)
Select Face.
void BM_vert_select_set(BMesh *bm, BMVert *v, const bool select)
Select Vert.
void BM_edge_select_set(BMesh *bm, BMEdge *e, const bool select)
Select Edge.
void BM_select_history_validate(BMesh *bm)
void BM_mesh_elem_hflag_disable_all(BMesh *bm, const char htype, const char hflag, const bool respecthide)
void BM_mesh_active_face_set(BMesh *bm, BMFace *f)
#define BM_select_history_store_notest(bm, ele)
#define BM_select_history_store(bm, ele)
#define BM_select_history_remove(bm, ele)
void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
void BM_mesh_elem_index_ensure(BMesh *bm, const char htype)
BLI_INLINE BMFace * BM_face_at_index(BMesh *bm, const int index)
#define BM_FACE
#define BM_EDGE
#define BM_VERT
@ SIMVERT_NORMAL
float BM_face_calc_area_uv(const BMFace *f, int cd_loop_uv_offset)
float BM_face_calc_area_with_mat3(const BMFace *f, const float mat3[3][3])
float BM_face_calc_area_uv_signed(const BMFace *f, int cd_loop_uv_offset)
BMLoop * BM_face_edge_share_loop(BMFace *f, BMEdge *e)
Return the Loop Shared by Face and Edge.
BMLoop * BM_face_vert_share_loop(BMFace *f, BMVert *v)
Return the Loop Shared by Face and Vertex.
BLI_INLINE BMVert * BM_edge_other_vert(BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
BLI_INLINE bool BM_vert_in_edge(const BMEdge *e, const BMVert *v) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
ATTR_WARN_UNUSED_RESULT const BMLoop * l
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
ATTR_WARN_UNUSED_RESULT const BMVert * v
BMUVOffsets BM_uv_map_get_offsets(const BMesh *bm)
bool BM_loop_uv_share_vert_check(BMLoop *l_a, BMLoop *l_b, const int cd_loop_uv_offset)
bool BM_loop_uv_share_edge_check(BMLoop *l_a, BMLoop *l_b, const int cd_loop_uv_offset)
void BM_face_uv_calc_center_median(const BMFace *f, const int cd_loop_uv_offset, float r_cent[2])
bool BM_face_uv_point_inside_test(const BMFace *f, const float co[2], const int cd_loop_uv_offset)
unsigned int U
Definition btGjkEpa3.h:78
bool is_empty() const
Definition BLI_array.hh:253
local_group_size(16, 16) .push_constant(Type b
const Depsgraph * depsgraph
#define sqrtf(x)
draw_view in_light_buf[] float
static float verts[][3]
uiWidgetBaseParameters params[MAX_WIDGET_BASE_BATCH]
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
void *(* MEM_callocN)(size_t len, const char *str)
Definition mallocn.cc:42
ccl_device_inline float4 select(const int4 mask, const float4 a, const float4 b)
static char faces[256]
return ret
float RNA_property_float_get(PointerRNA *ptr, PropertyRNA *prop)
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
void RNA_float_get_array(PointerRNA *ptr, const char *name, float *values)
int RNA_int_get(PointerRNA *ptr, const char *name)
float RNA_float_get(PointerRNA *ptr, const char *name)
void RNA_property_float_set(PointerRNA *ptr, PropertyRNA *prop, float value)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
void RNA_float_set_array(PointerRNA *ptr, const char *name, const float *values)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_float_vector(StructOrFunctionRNA *cont_, const char *identifier, const int len, const float *default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
void RNA_enum_item_end(EnumPropertyItem **items, int *totitem)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_translation_context(PropertyRNA *prop, const char *context)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
void RNA_def_enum_funcs(PropertyRNA *prop, EnumPropertyItemFunc itemfunc)
void RNA_enum_items_add_value(EnumPropertyItem **items, int *totitem, const EnumPropertyItem *item, int value)
const EnumPropertyItem rna_enum_mesh_select_mode_uv_items[]
Definition rna_scene.cc:133
#define FLT_MAX
Definition stdcycles.h:14
BMVert * v1
BMVert * v2
struct BMLoop * l
struct BMEditSelection * prev
struct BMVert * v
struct BMEdge * e
struct BMLoop * radial_next
struct BMLoop * prev
struct BMFace * f
struct BMLoop * next
int totfacesel
ListBase selected
int totvertsel
int totloop
int totedgesel
CustomData ldata
BMFace ** ftable
int totface
BMFace ** faces
Definition ED_uvedit.hh:290
BMUVOffsets offsets
Definition ED_uvedit.hh:296
Definition DNA_ID.h:413
void * last
float tri[3][2]
unsigned int face_index
UvMapVert * next
int mval[2]
Definition WM_types.hh:728
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1036
std::string(* get_name)(wmOperatorType *ot, PointerRNA *ptr)
Definition WM_types.hh:1068
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
PropertyRNA * prop
Definition WM_types.hh:1092
StructRNA * srna
Definition WM_types.hh:1080
void(* cancel)(bContext *C, wmOperator *op)
Definition WM_types.hh:1028
struct ReportList * reports
struct PointerRNA * ptr
static int uv_select_similar_island_exec(bContext *C, wmOperator *op)
void UV_OT_select_all(wmOperatorType *ot)
void uvedit_uv_select_set_with_sticky(const Scene *scene, BMEditMesh *em, BMLoop *l, const bool select, const bool do_history, const BMUVOffsets offsets)
static BMLoop * uvedit_loop_find_other_boundary_loop_with_visible_face(const Scene *scene, BMLoop *l_edge, BMVert *v_pivot, const BMUVOffsets offsets)
void UV_OT_select_edge_ring(wmOperatorType *ot)
static bool uv_circle_select_is_point_inside(const float uv[2], const float offset[2], const float ellipse[2])
void ED_uvedit_selectmode_clean(const Scene *scene, Object *obedit)
UV Select Mode set.
void uvedit_edge_select_enable(const Scene *scene, BMesh *bm, BMLoop *l, const bool do_history, const BMUVOffsets offsets)
static int uv_select_mode_invoke(bContext *C, wmOperator *op, const wmEvent *)
#define SET_SELECTION(value)
void ED_uvedit_selectmode_clean_multi(bContext *C)
static int uv_select_edge_ring_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void uv_select_tag_update_for_object(Depsgraph *depsgraph, const ToolSettings *ts, Object *obedit)
bool uvedit_face_select_test(const Scene *scene, BMFace *efa, const BMUVOffsets offsets)
void uvedit_edge_select_set(const Scene *scene, BMesh *bm, BMLoop *l, const bool select, const bool do_history, const BMUVOffsets offsets)
Select UV Edge.
static int uv_select_linked_internal(bContext *C, wmOperator *op, const wmEvent *event, bool pick)
bool uvedit_vert_is_edge_select_any_other(const Scene *scene, BMLoop *l, const BMUVOffsets offsets)
static int uv_circle_select_exec(bContext *C, wmOperator *op)
static float get_uv_face_needle(const eUVSelectSimilar type, BMFace *face, int ob_index, const float ob_m3[3][3], const BMUVOffsets offsets)
void uvedit_face_select_set(const Scene *scene, BMesh *bm, BMFace *efa, const bool select, const bool do_history, const BMUVOffsets offsets)
Select UV Face.
static int uv_select_loop_exec(bContext *C, wmOperator *op)
eUVSelectSimilar
@ UV_SSIM_AREA_UV
@ UV_SSIM_LENGTH_UV
@ UV_SSIM_MATERIAL
@ UV_SSIM_PIN
@ UV_SSIM_FACE
@ UV_SSIM_OBJECT
@ UV_SSIM_WINDING
@ UV_SSIM_LENGTH_3D
@ UV_SSIM_AREA_3D
@ UV_SSIM_SIDES
static void bm_loop_tags_clear(BMesh *bm)
void UV_OT_select(wmOperatorType *ot)
void ED_uvedit_active_edge_loop_set(BMesh *bm, BMLoop *l)
void UV_OT_select_split(wmOperatorType *ot)
static int uv_select_overlap(bContext *C, const bool extend)
static bool uv_nearest_between(const BMLoop *l, const float co[2], const int cd_loop_uv_offset)
static void bm_clear_uv_vert_selection(const Scene *scene, BMesh *bm, const BMUVOffsets offsets)
void UV_OT_select_linked(wmOperatorType *ot)
static int uv_select_linked_pick_exec(bContext *C, wmOperator *op)
static int uv_select_edgeloop(Scene *scene, Object *obedit, UvNearestHit *hit, const bool extend)
void uvedit_deselect_flush(const Scene *scene, BMEditMesh *em)
static void uv_select_flush_from_tag_sticky_loc_internal(const Scene *scene, BMEditMesh *em, UvVertMap *vmap, const uint efa_index, BMLoop *l, const bool select, const BMUVOffsets offsets)
static EnumPropertyItem uv_select_similar_type_items[]
static float get_uv_vert_needle(const eUVSelectSimilar type, BMVert *vert, const float ob_m3[3][3], BMLoop *loop, const BMUVOffsets offsets)
static float get_uv_edge_needle(const eUVSelectSimilar type, BMEdge *edge, const float ob_m3[3][3], BMLoop *loop_a, BMLoop *loop_b, const BMUVOffsets offsets)
eUVEdgeLoopBoundaryMode
@ UV_EDGE_LOOP_BOUNDARY_ALL
@ UV_EDGE_LOOP_BOUNDARY_LOOP
bool uv_find_nearest_face_multi_ex(Scene *scene, const Span< Object * > objects, const float co[2], UvNearestHit *hit, const bool only_in_face)
static int uv_select_mode_exec(bContext *C, wmOperator *op)
BMLoop ** ED_uvedit_selected_edges(const Scene *scene, BMesh *bm, int len_max, int *r_edges_len)
void uvedit_uv_select_shared_vert(const Scene *scene, BMEditMesh *em, BMLoop *l, const bool select, const int sticky_flag, const bool do_history, const BMUVOffsets offsets)
static void uv_select_all_perform(const Scene *scene, Object *obedit, int action)
BLI_INLINE bool overlap_cmp(const void *a_v, const void *b_v)
static int uv_box_select_exec(bContext *C, wmOperator *op)
void uvedit_face_select_shared_vert(const Scene *scene, BMEditMesh *em, BMFace *efa, const bool select, const bool do_history, const BMUVOffsets offsets)
void uvedit_edge_select_shared_vert(const Scene *scene, BMEditMesh *em, BMLoop *l, const bool select, const int sticky_flag, const bool do_history, const BMUVOffsets offsets)
static int uv_select_similar_exec(bContext *C, wmOperator *op)
char ED_uvedit_select_mode_get(const Scene *scene)
static int uv_select_loop_invoke(bContext *C, wmOperator *op, const wmEvent *event)
bool uv_find_nearest_edge(Scene *scene, Object *obedit, const float co[2], const float penalty, UvNearestHit *hit)
static int uv_select_less_exec(bContext *C, wmOperator *)
#define NEIGHBORING_FACE_IS_SEL
bool uvedit_edge_select_test_ex(const ToolSettings *ts, const BMLoop *l, const BMUVOffsets offsets)
void uvedit_edge_select_set_noflush(const Scene *scene, BMLoop *l, const bool select, const int sticky_flag, const BMUVOffsets offsets)
static int uv_select_similar_vert_exec(bContext *C, wmOperator *op)
static int uv_select_overlap_exec(bContext *C, wmOperator *op)
bool uvedit_select_is_any_selected_multi(const Scene *scene, const Span< Object * > objects)
void UV_OT_select_circle(wmOperatorType *ot)
void uvedit_select_flush(const Scene *scene, BMEditMesh *em)
static int uv_select_linked_exec(bContext *C, wmOperator *op)
UvNearestHit uv_nearest_hit_init_max_default()
bool uv_find_nearest_vert(Scene *scene, Object *obedit, float const co[2], const float penalty_dist, UvNearestHit *hit)
static int uv_select_all_exec(bContext *C, wmOperator *op)
void UV_OT_select_mode(wmOperatorType *ot)
UvNearestHit uv_nearest_hit_init_max(const View2D *v2d)
bool uvedit_face_visible_test_ex(const ToolSettings *ts, BMFace *efa)
void UV_OT_select_similar(wmOperatorType *ot)
static void uv_select_flush_from_tag_loop(const Scene *scene, Object *obedit, const bool select)
static bool uv_mouse_select(bContext *C, const float co[2], const SelectPick_Params *params)
void UV_OT_select_linked_pick(wmOperatorType *ot)
void UV_OT_select_more(wmOperatorType *ot)
BMLoop * uv_find_nearest_loop_from_edge(Scene *scene, Object *obedit, BMEdge *e, const float co[2])
bool uvedit_uv_select_test(const Scene *scene, BMLoop *l, const BMUVOffsets offsets)
static void uv_select_edgeloop_single_side_tag(const Scene *scene, BMEditMesh *em, BMLoop *l_init, const BMUVOffsets offsets, enum eUVEdgeLoopBoundaryMode boundary_mode, int r_count_by_select[2])
void UV_OT_select_pinned(wmOperatorType *ot)
static void uv_select_flush_from_tag_face(const Scene *scene, Object *obedit, const bool select)
void ED_uvedit_selectmode_flush(const Scene *scene, BMEditMesh *em)
UV Select Mode Flush.
bool uv_find_nearest_face_multi(Scene *scene, const Span< Object * > objects, const float co[2], UvNearestHit *hit)
static int uv_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static int uv_select_edgering(Scene *scene, Object *obedit, UvNearestHit *hit, const bool extend)
static const EnumPropertyItem * uv_select_similar_type_itemf(bContext *C, PointerRNA *, PropertyRNA *, bool *r_free)
BMLoop * ED_uvedit_active_edge_loop_get(BMesh *bm)
static bool do_lasso_select_mesh_uv(bContext *C, const Span< int2 > mcoords, const eSelectOp sel_op)
void UV_OT_select_loop(wmOperatorType *ot)
static void uv_select_all_perform_multi_ex(const Scene *scene, Span< Object * > objects, int action, const Object *ob_exclude)
BLI_INLINE uint overlap_hash(const void *overlap_v)
bool uvedit_uv_select_test_ex(const ToolSettings *ts, const BMLoop *l, const BMUVOffsets offsets)
static int uv_select_pinned_exec(bContext *C, wmOperator *op)
static bool uvedit_nearest_uv(const Scene *scene, Object *obedit, const float co[2], const float scale[2], const bool ignore_selected, float *dist_sq, float r_uv[2])
void uvedit_uv_select_disable(const Scene *scene, BMesh *bm, BMLoop *l, const BMUVOffsets offsets)
bool uvedit_face_visible_test(const Scene *scene, BMFace *efa)
bool uvedit_vert_is_face_select_any_other(const Scene *scene, BMLoop *l, const BMUVOffsets offsets)
bool uv_find_nearest_face(Scene *scene, Object *obedit, const float co[2], UvNearestHit *hit)
bool uvedit_vert_is_all_other_faces_selected(const Scene *scene, BMLoop *l, const BMUVOffsets offsets)
void uvedit_face_select_set_with_sticky(const Scene *scene, BMEditMesh *em, BMFace *efa, const bool select, const bool do_history, const BMUVOffsets offsets)
bool uvedit_face_select_test_ex(const ToolSettings *ts, BMFace *efa, const BMUVOffsets offsets)
static void uv_select_flush_from_loop_edge_flag(const Scene *scene, BMEditMesh *em)
static int uv_select_similar_edge_exec(bContext *C, wmOperator *op)
static int uv_select_more_exec(bContext *C, wmOperator *)
bool uv_find_nearest_vert_multi(Scene *scene, const Span< Object * > objects, float const co[2], const float penalty_dist, UvNearestHit *hit)
static int uv_select_similar_face_exec(bContext *C, wmOperator *op)
static BMLoop * bm_select_edgeloop_single_side_next(const Scene *scene, BMLoop *l_step, BMVert *v_from, const BMUVOffsets offsets)
eUVLoopGenericType
@ UV_RING_SELECT
@ UV_LOOP_SELECT
static bool overlap_tri_tri_uv_test(const float t1[3][2], const float t2[3][2], const float endpoint_bias)
static void uvedit_vertex_select_tagged(BMEditMesh *em, Scene *scene, bool select, const BMUVOffsets offsets)
void uvedit_edge_select_set_with_sticky(const Scene *scene, BMEditMesh *em, BMLoop *l, const bool select, const bool do_history, const BMUVOffsets offsets)
#define CURR_FACE_IS_UNSEL
static bool do_lasso_select_mesh_uv_is_edge_inside(const ARegion *region, const rcti *clip_rect, const Span< int2 > mcoords, const float co_test_a[2], const float co_test_b[2])
static int uv_select_edge_ring_exec(bContext *C, wmOperator *op)
bool uv_find_nearest_edge_multi(Scene *scene, const Span< Object * > objects, const float co[2], const float penalty, UvNearestHit *hit)
static int uv_select_split_exec(bContext *C, wmOperator *op)
static int uv_select_more_less(bContext *C, const bool select)
static int uv_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static BMLoop * uvedit_loop_find_other_radial_loop_with_visible_face(const Scene *scene, BMLoop *l_src, const BMUVOffsets offsets)
static bool uv_mouse_select_multi(bContext *C, const Span< Object * > objects, const float co[2], const SelectPick_Params *params)
static void uv_select_all(const Scene *scene, BMEditMesh *em, bool select_all)
BMLoop * ED_uvedit_active_vert_loop_get(BMesh *bm)
static int uv_mouse_select_loop_generic(bContext *C, const float co[2], const bool extend, enum eUVLoopGenericType loop_type)
static int uv_mouse_select_loop_generic_multi(bContext *C, const Span< Object * > objects, const float co[2], const bool extend, enum eUVLoopGenericType loop_type)
BMLoop * uv_find_nearest_loop_from_vert(Scene *scene, Object *obedit, BMVert *v, const float co[2])
static bool uv_circle_select_is_edge_inside(const float uv_a[2], const float uv_b[2], const float offset[2], const float ellipse[2])
bool uv_find_nearest_face_ex(Scene *scene, Object *obedit, const float co[2], UvNearestHit *hit, const bool only_in_face)
static int uv_select_exec(bContext *C, wmOperator *op)
void UV_OT_select_overlap(wmOperatorType *ot)
bool uvedit_edge_select_test(const Scene *scene, BMLoop *l, const BMUVOffsets offsets)
void UV_OT_select_lasso(wmOperatorType *ot)
static bool uv_island_selected(const Scene *scene, FaceIsland *island)
static bool do_lasso_select_mesh_uv_is_point_inside(const ARegion *region, const rcti *clip_rect, const Span< int2 > mcoords, const float co_test[2])
static EnumPropertyItem prop_similar_compare_types[]
static int uv_select_faceloop(Scene *scene, Object *obedit, UvNearestHit *hit, const bool extend)
static void uv_isolate_selected_islands(const Scene *scene, BMEditMesh *em, const BMUVOffsets offsets)
void uvedit_face_select_disable(const Scene *scene, BMesh *bm, BMFace *efa, const BMUVOffsets offsets)
void uvedit_edge_select_disable(const Scene *scene, BMesh *bm, BMLoop *l, const BMUVOffsets offsets)
UvNearestHit uv_nearest_hit_init_dist_px(const View2D *v2d, const float dist_px)
void uvedit_uv_select_set(const Scene *scene, BMesh *bm, BMLoop *l, const bool select, const bool do_history, const BMUVOffsets offsets)
Select UV Vertex.
static float get_uv_island_needle(const eUVSelectSimilar type, const FaceIsland *island, const float ob_m3[3][3], const BMUVOffsets offsets)
BMFace ** ED_uvedit_selected_faces(const Scene *scene, BMesh *bm, int len_max, int *r_faces_len)
void uvedit_uv_select_enable(const Scene *scene, BMesh *bm, BMLoop *l, const bool do_history, const BMUVOffsets offsets)
const float * uvedit_first_selected_uv_from_vertex(Scene *scene, BMVert *eve, const BMUVOffsets offsets)
static BMLoop * bm_select_edgeloop_double_side_next(const Scene *scene, BMLoop *l_step, BMVert *v_from, const BMUVOffsets offsets)
void uvedit_face_select_enable(const Scene *scene, BMesh *bm, BMFace *efa, const bool do_history, const BMUVOffsets offsets)
static int uv_lasso_select_exec(bContext *C, wmOperator *op)
static void uv_select_invert(const Scene *scene, BMEditMesh *em)
bool ED_uvedit_nearest_uv_multi(const View2D *v2d, const Scene *scene, const Span< Object * > objects, const float mval_fl[2], const bool ignore_selected, float *dist_sq, float r_uv[2])
bool uvedit_select_is_any_selected(const Scene *scene, Object *obedit)
void ED_uvedit_select_sync_flush(const ToolSettings *ts, BMEditMesh *em, const bool select)
static void uv_select_all_perform_multi(const Scene *scene, Span< Object * > objects, int action)
void UV_OT_select_less(wmOperatorType *ot)
BMLoop ** ED_uvedit_selected_verts(const Scene *scene, BMesh *bm, int len_max, int *r_verts_len)
static void uv_select_edgeloop_double_side_tag(const Scene *scene, BMEditMesh *em, BMLoop *l_init_pair[2], const BMUVOffsets offsets)
void ED_uvedit_active_vert_loop_set(BMesh *bm, BMLoop *l)
void UV_OT_select_box(wmOperatorType *ot)
static void uv_select_linked_multi(Scene *scene, const Span< Object * > objects, UvNearestHit *hit, const bool extend, bool deselect, const bool toggle, const bool select_faces)
void WM_main_add_notifier(uint type, void *reference)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4125
bool WM_gesture_is_modal_first(const wmGesture *gesture)
void WM_gesture_box_cancel(bContext *C, wmOperator *op)
int WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_circle_invoke(bContext *C, wmOperator *op, const wmEvent *event)
Array< int2 > WM_gesture_lasso_path_to_array(bContext *, wmOperator *op)
void WM_gesture_circle_cancel(bContext *C, wmOperator *op)
int WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_circle_modal(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event)
void WM_gesture_lasso_cancel(bContext *C, wmOperator *op)
int WM_gesture_lasso_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void WM_operator_properties_gesture_box(wmOperatorType *ot)
void WM_operator_properties_select_operation_simple(wmOperatorType *ot)
void WM_operator_properties_border_to_rctf(wmOperator *op, rctf *r_rect)
void WM_operator_properties_gesture_lasso(wmOperatorType *ot)
void WM_operator_properties_gesture_circle(wmOperatorType *ot)
void WM_operator_properties_select_all(wmOperatorType *ot)
void WM_operator_properties_mouse_select(wmOperatorType *ot)
int WM_operator_flag_only_pass_through_on_press(int retval, const wmEvent *event)
int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)
uint8_t flag
Definition wm_window.cc:138
static bool do_history(const char *filepath, ReportList *reports)